255 lines
8.3 KiB
Verilog
255 lines
8.3 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, // 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 |