implemented a simple bootloader for initializing the free_queue
This commit is contained in:
49
devlog/2025-07-02-Bootloader.md
Normal file
49
devlog/2025-07-02-Bootloader.md
Normal 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!
|
@ -15,7 +15,8 @@ module hub(input logic rst,
|
||||
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 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;
|
||||
timeprecision 1ps;
|
||||
|
||||
@ -36,7 +37,8 @@ module hub(input logic rst,
|
||||
.empty_slot_addr(empty_slot_addr),
|
||||
.empty_slot_enqueue(empty_slot_enqueue),
|
||||
.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 [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 empty_slot_enqueue,
|
||||
output logic [QUEUE_ADDR_LEN - 1:0] new_slot_addr,
|
||||
output logic queue_empty);
|
||||
output logic queue_empty,
|
||||
output logic queue_ready);
|
||||
timeunit 1ns;
|
||||
timeprecision 1ps;
|
||||
|
||||
@ -157,32 +160,39 @@ module free_queue(input logic sys_clk,
|
||||
logic [QUEUE_ADDR_LEN - 1:0] tail;
|
||||
shortint queue_size;
|
||||
|
||||
enum logic [1:0] {VOID, INIT, READY} state = VOID;
|
||||
|
||||
assign queue_empty = queue_size == 0;
|
||||
|
||||
initial begin
|
||||
// TODO: pre-load the free queue with every slot possible
|
||||
end
|
||||
|
||||
// IMPORTANT: rst must be held high for at least 2 sys_clk cycles
|
||||
always_ff @ (posedge sys_clk or rst) begin
|
||||
if (rst) begin
|
||||
always_ff @ (posedge sys_clk) begin
|
||||
new_slot_addr <= fqueue[head];
|
||||
|
||||
if (state == VOID) begin
|
||||
queue_ready <= '0;
|
||||
head <= '0;
|
||||
tail <= {QUEUE_ADDR_LEN{1'd1}};
|
||||
queue_size <= QUEUE_SIZE;
|
||||
new_slot_addr <= '0;
|
||||
tail <= '0;
|
||||
queue_size <= 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
|
||||
if (request_new_slot) begin
|
||||
head <= head + 1;
|
||||
queue_size <= queue_size - 1;
|
||||
end
|
||||
new_slot_addr <= fqueue[head];
|
||||
|
||||
if (empty_slot_enqueue) begin
|
||||
fqueue[tail] <= empty_slot_addr;
|
||||
tail <= tail + 1;
|
||||
queue_size <= queue_size + 1;
|
||||
end
|
||||
end
|
||||
end // else: !if(fqueue_state_t == INIT)
|
||||
end
|
||||
|
||||
endmodule // free_queue
|
||||
|
@ -9,16 +9,20 @@ module fabric(input logic rst_raw,
|
||||
input logic [INTERFACE_CNT - 1:0] cs_n,
|
||||
input logic [INTERFACE_CNT - 1:0] mosi,
|
||||
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;
|
||||
timeprecision 1ps;
|
||||
|
||||
logic sys_clk;
|
||||
logic rst;
|
||||
|
||||
// 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),
|
||||
.rst_raw(rst_raw),
|
||||
.rst(rst));
|
||||
@ -39,6 +43,8 @@ module fabric(input logic rst_raw,
|
||||
genvar i;
|
||||
generate
|
||||
for (i = 0; i < INTERFACE_CNT; i = i + 1) begin : gen_interfaces
|
||||
assign sig_fabric_ready[i] = fabric_ready;
|
||||
|
||||
spi_interface spi(.rst(rst),
|
||||
.sys_clk(sys_clk),
|
||||
.sclk(sclk[i]),
|
||||
@ -74,7 +80,8 @@ module fabric(input logic rst_raw,
|
||||
.tx_queue_addr(tx_queue_addr),
|
||||
.tx_new_queue(tx_new_queue),
|
||||
.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
|
||||
|
||||
@ -86,7 +93,8 @@ module reset_sync (input logic sys_clk,
|
||||
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
|
||||
if (rst_raw) begin
|
||||
|
Reference in New Issue
Block a user