`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