implemented a simple bootloader for initializing the free_queue

This commit is contained in:
2025-07-02 21:30:24 -04:00
parent 32ea59b7db
commit ce1949adc0
3 changed files with 88 additions and 21 deletions

View File

@ -0,0 +1,49 @@
# The Bootloader
Date: 2025-07-02
## Goals and expectations
Writing the bootloader logic for the fabric. This is for easy scaling
of the buffer size.
And also add indicators (output logic) to signal the user and
interfaces that the fabric is ready.
## Results
Done. A simple bootloader, much simpler than I expected, just a
simple FSM with some initializing logic.
Just some logic inside the `free_queue` module to initialize it when
the FPGA boots.
And in the meanwhile, I spawned a new server (one specifically for
running long term simulations), and also changed my shell to `fish`
from `zsh` for some more modern feel.
## Reflections
1. Once again, I am bemused by the simplicity of combinational logic.
FSMs translate much better to SystemVerilog than to some
sequentially executed logic like C.
2. Keep it simple. A simple bootloader is much easier for future
implementations compared to initializing through other means, and
once complete, it's very scalable.
3. The plan isn't everything. This wasn't part of the plan, the plan
was to burn the initial values inside a hex file or some other
methods that would allow instant initialization. However, due to
the elements of the `free_queue` being not byte-aligned, this would
become much more trouble in the end. The plan was modified
accordingly.
4. Make the best out of the existing tools on hand. This is a
follow-up to the previous point. Don't go into coding with only
the plan and goals in mind, reassess when you've set foot on its
grounds, weigh the options, and something else might just come up.
## Final thoughts
Find the right tools to do the right things. Trying to simulate
clocked behaviors in C++ is as awkward as trying to write sequential
logic in SystemVerilog.
The first choice isn't always the best one, but the final choice
should the better one after weighing the options.
## Next steps
THORN!

View File

@ -15,7 +15,8 @@ module hub(input logic rst,
input logic [QUEUE_ADDR_LEN - 1:0] tx_queue_addr [INTERFACE_CNT], input logic [QUEUE_ADDR_LEN - 1:0] tx_queue_addr [INTERFACE_CNT],
output logic [QUEUE_ADDR_LEN - 1:0] tx_new_queue[INTERFACE_CNT], output logic [QUEUE_ADDR_LEN - 1:0] tx_new_queue[INTERFACE_CNT],
output logic tx_new_queue_valid [INTERFACE_CNT], output logic tx_new_queue_valid [INTERFACE_CNT],
input logic tx_new_queue_ready [INTERFACE_CNT]); input logic tx_new_queue_ready [INTERFACE_CNT],
output logic hub_ready);
timeunit 1ns; timeunit 1ns;
timeprecision 1ps; timeprecision 1ps;
@ -36,7 +37,8 @@ module hub(input logic rst,
.empty_slot_addr(empty_slot_addr), .empty_slot_addr(empty_slot_addr),
.empty_slot_enqueue(empty_slot_enqueue), .empty_slot_enqueue(empty_slot_enqueue),
.new_slot_addr(new_slot_addr), .new_slot_addr(new_slot_addr),
.queue_empty(free_queue_empty)); .queue_empty(free_queue_empty),
.queue_ready(hub_ready));
logic [QUEUE_ADDR_LEN - 1:0] rx_queue_addr [INTERFACE_CNT]; logic [QUEUE_ADDR_LEN - 1:0] rx_queue_addr [INTERFACE_CNT];
logic [MEMORY_POOL_ADDR_LEN - 1:0] mem_read_addr; logic [MEMORY_POOL_ADDR_LEN - 1:0] mem_read_addr;
@ -148,7 +150,8 @@ module free_queue(input logic sys_clk,
input logic [QUEUE_ADDR_LEN - 1:0] empty_slot_addr, input logic [QUEUE_ADDR_LEN - 1:0] empty_slot_addr,
input logic empty_slot_enqueue, input logic empty_slot_enqueue,
output logic [QUEUE_ADDR_LEN - 1:0] new_slot_addr, output logic [QUEUE_ADDR_LEN - 1:0] new_slot_addr,
output logic queue_empty); output logic queue_empty,
output logic queue_ready);
timeunit 1ns; timeunit 1ns;
timeprecision 1ps; timeprecision 1ps;
@ -157,32 +160,39 @@ module free_queue(input logic sys_clk,
logic [QUEUE_ADDR_LEN - 1:0] tail; logic [QUEUE_ADDR_LEN - 1:0] tail;
shortint queue_size; shortint queue_size;
enum logic [1:0] {VOID, INIT, READY} state = VOID;
assign queue_empty = queue_size == 0; assign queue_empty = queue_size == 0;
initial begin always_ff @ (posedge sys_clk) begin
// TODO: pre-load the free queue with every slot possible new_slot_addr <= fqueue[head];
end
if (state == VOID) begin
// IMPORTANT: rst must be held high for at least 2 sys_clk cycles queue_ready <= '0;
always_ff @ (posedge sys_clk or rst) begin
if (rst) begin
head <= '0; head <= '0;
tail <= {QUEUE_ADDR_LEN{1'd1}}; tail <= '0;
queue_size <= QUEUE_SIZE; queue_size <= 0;
new_slot_addr <= '0; state <= INIT;
end else if (state == INIT) begin
if (tail != {QUEUE_ADDR_LEN{1'b1}}) begin
fqueue[tail] <= queue_size[QUEUE_ADDR_LEN - 1:0];
tail <= tail + 1;
queue_size <= queue_size + 1;
end else begin
state <= READY;
queue_ready <= 1;
end
end else begin end else begin
if (request_new_slot) begin if (request_new_slot) begin
head <= head + 1; head <= head + 1;
queue_size <= queue_size - 1; queue_size <= queue_size - 1;
end end
new_slot_addr <= fqueue[head];
if (empty_slot_enqueue) begin if (empty_slot_enqueue) begin
fqueue[tail] <= empty_slot_addr; fqueue[tail] <= empty_slot_addr;
tail <= tail + 1; tail <= tail + 1;
queue_size <= queue_size + 1; queue_size <= queue_size + 1;
end end
end end // else: !if(fqueue_state_t == INIT)
end end
endmodule // free_queue endmodule // free_queue

View File

@ -9,16 +9,20 @@ module fabric(input logic rst_raw,
input logic [INTERFACE_CNT - 1:0] cs_n, input logic [INTERFACE_CNT - 1:0] cs_n,
input logic [INTERFACE_CNT - 1:0] mosi, input logic [INTERFACE_CNT - 1:0] mosi,
output logic [INTERFACE_CNT - 1:0] miso, output logic [INTERFACE_CNT - 1:0] miso,
output logic [INTERFACE_CNT - 1:0] tx_active); output logic [INTERFACE_CNT - 1:0] tx_active,
output logic [INTERFACE_CNT - 1:0] sig_fabric_ready,
output logic fabric_ready,
output logic hub_ready);
timeunit 1ns; timeunit 1ns;
timeprecision 1ps; timeprecision 1ps;
logic sys_clk; logic sys_clk;
logic rst;
// TODO: add PPL module for a boosted system clock // TODO: add PPL module for a boosted system clock
logic rst;
assign fabric_ready = hub_ready & (~rst);
reset_sync rst_sync(.sys_clk(sys_clk), reset_sync rst_sync(.sys_clk(sys_clk),
.rst_raw(rst_raw), .rst_raw(rst_raw),
.rst(rst)); .rst(rst));
@ -39,6 +43,8 @@ module fabric(input logic rst_raw,
genvar i; genvar i;
generate generate
for (i = 0; i < INTERFACE_CNT; i = i + 1) begin : gen_interfaces for (i = 0; i < INTERFACE_CNT; i = i + 1) begin : gen_interfaces
assign sig_fabric_ready[i] = fabric_ready;
spi_interface spi(.rst(rst), spi_interface spi(.rst(rst),
.sys_clk(sys_clk), .sys_clk(sys_clk),
.sclk(sclk[i]), .sclk(sclk[i]),
@ -74,7 +80,8 @@ module fabric(input logic rst_raw,
.tx_queue_addr(tx_queue_addr), .tx_queue_addr(tx_queue_addr),
.tx_new_queue(tx_new_queue), .tx_new_queue(tx_new_queue),
.tx_new_queue_valid(tx_new_queue_valid), .tx_new_queue_valid(tx_new_queue_valid),
.tx_new_queue_ready(tx_new_queue_ready)); .tx_new_queue_ready(tx_new_queue_ready),
.hub_ready(hub_ready));
endmodule // fabric endmodule // fabric
@ -86,7 +93,8 @@ module reset_sync (input logic sys_clk,
timeprecision 1ps; timeprecision 1ps;
logic rst_sync_0, rst_sync_1; logic rst_sync_0 = 0;
logic rst_sync_1 = 0;
always_ff @(posedge sys_clk or posedge rst_raw) begin always_ff @(posedge sys_clk or posedge rst_raw) begin
if (rst_raw) begin if (rst_raw) begin