`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: nope // Engineer: Jose // // Create Date: 03/03/2026 02:40:56 AM // Design Name: Bootloader // Module Name: uart_bootloader // Project Name: riscv-ac // Target Devices: Artix 7 // Tool Versions: 2025.2 // Description: Allows dynamic program burning // // Dependencies: // // Revision: 1.0 // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module uart_bootloader ( input wire clk, // Reloj de la placa (12 MHz) input wire rx, // Cable de entrada de datos desde el USB output reg [31:0] prog_data, // Instrucción completa de 32 bits output reg [31:0] prog_addr, // Dirección donde vamos a guardarla output reg prog_we, // Señal para escribir en la imem output wire cpu_rst // Mantiene al RISC-V en reset mientras programamos ); // Parámetros para 12 MHz y 115200 baudios // 12.000.000 / 115200 = 104 ciclos por bit localparam CLKS_PER_BIT = 104; // 1 segundo de timeout a 12 MHz para soltar el reset localparam TIMEOUT_LIMIT = 12_000_000; // Estados de la máquina UART RX localparam S_IDLE = 2'b00; localparam S_START = 2'b01; localparam S_DATA = 2'b10; localparam S_STOP = 2'b11; reg [1:0] state = S_IDLE; reg [7:0] clk_count = 0; reg [2:0] bit_index = 0; reg [7:0] rx_byte = 0; reg rx_done = 0; // --- 1. MÁQUINA DE ESTADOS UART RX --- always @(posedge clk) begin rx_done <= 0; case (state) S_IDLE: begin clk_count <= 0; bit_index <= 0; if (rx == 1'b0) // Detectamos el Start Bit (baja a 0) state <= S_START; end S_START: begin if (clk_count == (CLKS_PER_BIT / 2)) begin if (rx == 1'b0) begin // Confirmamos que es un Start bit válido clk_count <= 0; state <= S_DATA; end else state <= S_IDLE; end else clk_count <= clk_count + 1; end S_DATA: begin if (clk_count == CLKS_PER_BIT - 1) begin clk_count <= 0; rx_byte[bit_index] <= rx; // Guardamos el bit if (bit_index == 7) begin bit_index <= 0; state <= S_STOP; end else bit_index <= bit_index + 1; end else clk_count <= clk_count + 1; end S_STOP: begin if (clk_count == CLKS_PER_BIT - 1) begin rx_done <= 1; // ¡Byte recibido de lujo! state <= S_IDLE; end else clk_count <= clk_count + 1; end endcase end // --- 2. ENSAMBLADOR DE 32 BITS Y CONTROL DE TIMEOUT --- reg [1:0] byte_count = 0; reg [23:0] timeout_counter = 0; reg is_programming = 1; // Empezamos en modo programación por defecto // El procesador está en reset (apagado) mientras is_programming sea 1 assign cpu_rst = is_programming; always @(posedge clk) begin prog_we <= 0; // Por defecto no escribimos if (rx_done) begin // Ha llegado un byte nuevo, reseteamos el timeout timeout_counter <= 0; is_programming <= 1; // RISC-V usa Little Endian (el byte menos significativo llega primero) prog_data <= {rx_byte, prog_data[31:8]}; if (byte_count == 3) begin byte_count <= 0; prog_we <= 1; // ¡Tenemos los 32 bits! Damos la orden de escribir // Avanzamos la dirección para la siguiente instrucción (después de escribir) end else begin byte_count <= byte_count + 1; end end else begin // Si acabamos de escribir, subimos la dirección if (prog_we) begin prog_addr <= prog_addr + 4; end // Control de timeout (perro guardián) if (is_programming) begin if (timeout_counter == TIMEOUT_LIMIT) begin is_programming <= 0; // ¡Se acabó el tiempo! Soltamos al procesador prog_addr <= 0; // Reseteamos el puntero para la próxima vez byte_count <= 0; end else begin timeout_counter <= timeout_counter + 1; end end end end endmodule`timescale 1ns / 1ps `default_nettype none module uart_bootloader ( input wire clk, // reloj de la placa (12 MHz) input wire rx, // cable de entrada de datos desde el USB output reg [31:0] prog_data = 0, // instrucción completa de 32 bits output reg [31:0] prog_addr = 0, // dirección donde vamos a guardarla output reg prog_we, // señal para escribir en la imem output wire cpu_rst // mantiene el CPU en reset mientras programamos ); // parámetros para 12 MHz y 115200 de baudrate // 12e6 / 115200 = 104 ciclos por bit // 1 segundo de timeout a 12 MHz para soltar el reset localparam CLKS_PER_BIT = 104; // 868; localparam TIMEOUT_LIMIT = 12_000_000; // 50000; localparam S_IDLE = 2'b00; localparam S_START = 2'b01; localparam S_DATA = 2'b10; localparam S_STOP = 2'b11; reg [1:0] state = S_IDLE; reg [15:0] clk_count = 0; reg [2:0] bit_index = 0; reg [7:0] rx_byte = 0; reg rx_done = 0; reg [1:0] byte_count = 0; reg [23:0] timeout_counter = 0; reg is_programming = 1; // FSM de la UART always @(posedge clk) begin rx_done <= 0; case (state) S_IDLE: begin clk_count <= 0; bit_index <= 0; if (rx == 1'b0) state <= S_START; end S_START: begin if (clk_count == (CLKS_PER_BIT / 2)) begin if (rx == 1'b0) begin clk_count <= 0; state <= S_DATA; end else state <= S_IDLE; end else clk_count <= clk_count + 1; end S_DATA: begin if (clk_count == CLKS_PER_BIT - 1) begin clk_count <= 0; rx_byte[bit_index] <= rx; if (bit_index == 7) begin bit_index <= 0; state <= S_STOP; end else bit_index <= bit_index + 1; end else clk_count <= clk_count + 1; end S_STOP: begin if (clk_count == CLKS_PER_BIT - 1) begin rx_done <= 1; // byte recibido state <= S_IDLE; end else clk_count <= clk_count + 1; end endcase end assign cpu_rst = is_programming; always @(posedge clk) begin prog_we <= 0; if (rx_done) begin // byte nuevo timeout_counter <= 0; is_programming <= 1; // little-endian prog_data <= {rx_byte, prog_data[31:8]}; if (byte_count == 3) begin byte_count <= 0; prog_we <= 1; // 32b han llegado, escribimos end else begin byte_count <= byte_count + 1; end end else begin if (prog_we) begin prog_addr <= prog_addr + 4; end if (is_programming) begin if (timeout_counter == TIMEOUT_LIMIT) begin is_programming <= 0; // timeout excedido, soltamos CPU prog_addr <= 0; // reset al puntero byte_count <= 0; end else begin timeout_counter <= timeout_counter + 1; end end end end endmodule