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],
|
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
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user