Files
riscv-ac/riscv-ac.srcs/sources_1/new/uart_bootloader.v

133 lines
4.0 KiB
Verilog

`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 = 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 = 868; // 104;
localparam TIMEOUT_LIMIT = 50000; // 12_000_000;
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