minor change: revised directory structure, added MIT license

This commit is contained in:
2025-06-06 16:37:26 -04:00
parent a94823b44a
commit 018b7a3fcf
7 changed files with 7 additions and 0 deletions

1
src/fabric/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
obj_dir/

212
src/fabric/hub.sv Normal file
View File

@ -0,0 +1,212 @@
`include <params.svh>
`include <routing.svh>
// IMPORTANT: interfaces are supposed to keep track of their own packet states
module hub(
input logic rst,
input logic sys_clk,
input logic [7:0] rx_byte [INTERFACE_CNT],
input logic rx_valid [INTERFACE_CNT],
output logic rx_ready [INTERFACE_CNT],
input logic [PACKET_ADDR_LEN - 1:0] rx_pkt_addr [INTERFACE_CNT],
output logic [7:0] tx_byte [INTERFACE_CNT],
output logic tx_valid [INTERFACE_CNT],
input logic tx_ready [INTERFACE_CNT],
input logic [PACKET_ADDR_LEN - 1:0] tx_pkt_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 tx_new_queue_valid [INTERFACE_CNT],
input logic tx_new_queue_ready [INTERFACE_CNT],
output logic free_queue_empty);
timeunit 1ns;
timeprecision 1ps;
logic [INTERFACE_ADDR_LEN - 1:0] curr;
logic [INTERFACE_ADDR_LEN - 1:0] dest_buff [INTERFACE_CNT];
logic reuse_queue_slot [INTERFACE_CNT];
logic last_served_read;
logic request_new_slot;
logic [QUEUE_ADDR_LEN - 1:0] new_slot_addr;
logic [QUEUE_ADDR_LEN - 1:0] empty_slot_addr;
logic empty_slot_enqueue;
free_queue fqueue(.sys_clk(sys_clk),
.rst(rst),
.request_new_slot(request_new_slot),
.empty_slot_addr(empty_slot_addr),
.empty_slot_enqueue(empty_slot_enqueue),
.new_slot_addr(new_slot_addr),
.queue_empty(free_queue_empty));
logic [QUEUE_ADDR_LEN - 1:0] rx_queue_addr [INTERFACE_CNT];
logic [MEMORY_POOL_ADDR_LEN - 1:0] mem_read_addr;
logic [7:0] mem_read_byte;
logic [MEMORY_POOL_ADDR_LEN - 1:0] mem_write_addr;
logic mem_write_enable;
logic [7:0] mem_write_byte;
shortint new_slot_cooldown [INTERFACE_CNT];
memory_pool mpool(.sys_clk(sys_clk),
.rst(rst),
.read_addr(mem_read_addr),
.write_addr(mem_write_addr),
.write_byte(mem_write_byte),
.write_enable(mem_write_enable),
.read_byte(mem_read_byte));
always_ff @ (posedge sys_clk or rst) begin
if (rst) begin
curr <= '0;
for (int i = 0; i < INTERFACE_CNT; i++) begin
tx_new_queue[i] <= '0;
tx_new_queue_valid[i] <= 0;
tx_byte[i] <= '0;
tx_valid[i] <= 0;
rx_ready[i] <= 0;
rx_queue_addr[i] <= '0;
dest_buff[i] <= '0;
reuse_queue_slot[i] <= 0;
new_slot_cooldown[i] <= 0;
end
mem_read_addr <= '0;
mem_write_addr <= '0;
mem_write_enable <= 0;
mem_write_byte <= '0;
end else begin
// NOTE: signaled the servicing interface in the last cycle
rx_ready[curr] <= 0;
rx_ready[curr + 1] <= 1;
tx_new_queue_valid[dest_buff[curr - 1]] <= 0;
// IMPORTANT: interfaces should send the byte no matter what, rx_ready is to prevent sending a new byte
if (rx_valid[curr]) begin
// IMPORTANT: memory_write_addr is ready on the next cycle
if (rx_pkt_addr[curr] == 0 && !reuse_queue_slot[curr] &&
!(|new_slot_cooldown[curr])) begin
if (free_queue_empty) begin
// TODO: handle the drop logic
end else begin
request_new_slot <= 1;
rx_queue_addr[curr] <= new_slot_addr;
mem_write_addr <= {new_slot_addr, rx_pkt_addr[curr]};
new_slot_cooldown[curr] <= NEW_SLOT_COOLDOWN;
end
end else begin // if (rx_new_packet[curr])
reuse_queue_slot[curr] <= 0;
mem_write_addr <= {rx_queue_addr[curr], rx_pkt_addr[curr]};
request_new_slot <= 0;
if (rx_pkt_addr[curr] == ROSE_DEST_INDEX) begin
dest_buff[curr] <= next_hop(rx_byte[curr]);
end
end // else: !if(rx_new_packet[curr])
if (|new_slot_cooldown[curr]) begin
new_slot_cooldown[curr] <= new_slot_cooldown[curr] - 1;
end
mem_write_byte <= rx_byte[curr];
mem_write_enable <= 1;
if (&rx_pkt_addr[curr]) begin // packet complete
if (tx_new_queue_ready[dest_buff[curr]]) begin
tx_new_queue[dest_buff[curr]] <= rx_queue_addr[curr];
tx_new_queue_valid[dest_buff[curr]] <= 1;
end else begin
reuse_queue_slot[curr] <= 1;
end
end
end else begin // if (rx_valid[curr])
mem_write_enable <= 0;
end // else: !if(rx_valid[curr])
// IMPORTANT: tx_ready is only signaled when tx_pkt_addr is valid
if (tx_ready[curr]) begin
last_served_read <= 1;
mem_read_addr <= {tx_queue_addr[curr], tx_pkt_addr[curr]};
if (&tx_pkt_addr[curr]) begin
empty_slot_addr <= tx_queue_addr[curr];
empty_slot_enqueue <= 1;
end else begin
empty_slot_enqueue <= 0;
end
end else begin
empty_slot_enqueue <= 0;
last_served_read <= 0;
end // else: !if(tx_ready[curr])
if (last_served_read) begin
tx_byte[curr - 1] <= mem_read_byte;
tx_valid[curr - 1] <= 1;
end else begin
tx_valid[curr - 1] <= 0;
end
end
end
endmodule // hub
// IMPORTANT: the current queue_addr is always valid unless queue_empty
// REQUIRES: hub does not request a new slot when the queue is empty
module free_queue(input logic sys_clk,
input logic rst,
input logic request_new_slot,
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);
timeunit 1ns;
timeprecision 1ps;
logic [QUEUE_ADDR_LEN - 1:0] fqueue [QUEUE_SIZE - 1:0];
logic [QUEUE_ADDR_LEN - 1:0] head;
logic [QUEUE_ADDR_LEN - 1:0] tail;
shortint queue_size;
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
head <= '0;
tail <= {QUEUE_ADDR_LEN{1'd1}};
queue_size <= QUEUE_SIZE;
new_slot_addr <= '0;
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
endmodule // free_queue
module memory_pool(input logic sys_clk,
input logic rst,
input logic [MEMORY_POOL_ADDR_LEN - 1:0] read_addr,
input logic [MEMORY_POOL_ADDR_LEN - 1:0] write_addr,
input logic [7:0] write_byte,
input logic write_enable,
output logic [7:0] read_byte);
timeunit 1ns;
timeprecision 1ps;
logic [7:0] mem_pool[MEMORY_POOL_SIZE - 1:0];
always_ff @ (posedge sys_clk or rst) begin
if (rst) begin
read_byte <= 8'hFF;
end else begin
if (write_enable)
mem_pool[write_addr] <= write_byte;
read_byte <= mem_pool[read_addr];
end
end
endmodule // memory_pool

146
src/fabric/interface.sv Normal file
View File

@ -0,0 +1,146 @@
// NOTE: The first byte is used for syncing due to using different clock domains
`include <params.svh>
module spi_interface(
input logic rst,
input logic sys_clk,
input logic mosi,
input logic cs,
input logic sclk,
output logic miso,
output logic [7:0] rx_byte,
output logic rx_valid,
input logic rx_ready,
output logic [PACKET_ADDR_LEN - 1:0] rx_pkt_addr,
input logic [7:0] tx_byte,
input logic tx_valid,
output logic tx_ready,
output logic [PACKET_ADDR_LEN - 1:0] tx_pkt_addr,
output logic [QUEUE_ADDR_LEN - 1:0] tx_queue_addr,
input logic [QUEUE_ADDR_LEN - 1:0] tx_new_queue,
input logic tx_new_queue_valid,
output logic tx_new_queue_ready,
input logic free_queue_empty);
timeunit 1ns;
timeprecision 1ps;
// SPI logic
logic sclk_rising_edge;
logic sclk_falling_edge;
async_get_clk_edges sync (.rst(rst),
.ext_clk(sclk),
.sys_clk(sys_clk),
.clk_rising_edge(sclk_rising_edge),
.clk_falling_edge(sclk_falling_edge));
shortint bit_cnt = 0;
logic [7:0] rx_shift;
logic [7:0] tx_shift = 8'b00101010;
logic [7:0] rx_buff = '0;
logic byte_ready = 0;
always_ff @ (posedge sclk_rising_edge or posedge rst) begin
if (rst) begin
rx_shift <= '0;
rx_buff <= '0;
bit_cnt <= '0;
byte_ready <= 0;
end
else begin
if (cs) begin
rx_shift <= 0;
rx_buff <= 0;
bit_cnt <= 0;
end else begin
rx_shift <= {rx_shift[6:0], mosi};
bit_cnt <= bit_cnt + 1;
if (bit_cnt == 7) begin
bit_cnt <= 0;
rx_buff <= {rx_shift[6:0], mosi};
byte_ready <= 1;
end else begin
byte_ready <= 0;
end
end // else: !if(cs)
end // else: !if(rst)
end // always_ff @ (posedge sclk)
shortint idle_cntdn;
logic rx_drained;
always_ff @ (posedge sys_clk or rst) begin
if (rst) begin
rx_drained <= 0;
rx_pkt_addr <= '1;
rx_byte <= '0;
rx_valid <= 0;
idle_cntdn <= 0;
end else begin
if (!rx_drained && byte_ready) begin
rx_byte <= rx_buff;
rx_valid <= 1;
idle_cntdn <= INTERFACE_IDLE_COUNTDOWN;
rx_drained <= 1;
rx_pkt_addr <= rx_pkt_addr + 1;
end else if (!byte_ready) begin
rx_drained <= 0;
if (!(|idle_cntdn)) begin
rx_valid <= 0;
end else begin
idle_cntdn <= idle_cntdn - 1;
end
end
end
end
always_ff @ (posedge sclk_falling_edge or rst) begin
if (rst) begin
tx_shift <= 8'b00101010;
end else begin
if (cs) begin
tx_shift <= 0;
end else begin
if (bit_cnt == 0) begin
tx_shift <= rx_buff[7:0];
end else begin
tx_shift <= {tx_shift[6:0], 1'b0};
end
end
end // else: !if(rst)
$display("last bit sent: %b", miso);
$display("[%0d] current tx_shift: %b", $time, tx_shift);
$display("-----------------------------------------");
end // always_ff @ (negedge sclk)
assign miso = tx_shift[7];
endmodule // spi_interface
module async_get_clk_edges(
input logic rst,
input logic ext_clk,
input logic sys_clk,
output logic clk_rising_edge,
output logic clk_falling_edge);
timeunit 1ns;
timeprecision 1ps;
logic sync_0 = 0;
logic sync_1 = 0;
always_ff @ (posedge sys_clk) begin
if (rst) begin
sync_0 <= 0;
sync_1 <= 0;
end else begin
sync_0 <= ext_clk;
sync_1 <= sync_0;
end
end
assign clk_rising_edge = sync_0 & ~sync_1;
assign clk_falling_edge = ~sync_0 & sync_1;
endmodule // async_get_clk_edges

23
src/fabric/params.svh Normal file
View File

@ -0,0 +1,23 @@
`ifndef __PARAMS_SVH__
`define __PARAMS_SVH__
parameter int PACKET_SIZE = 64;
parameter int PACKET_ADDR_LEN = 6;
parameter int ROSE_ADDR_LEN = 8;
parameter logic [PACKET_ADDR_LEN - 1:0] ROSE_DEST_INDEX = 1;
parameter shortint QUEUE_SIZE = 1024;
parameter int QUEUE_ADDR_LEN = 10;
parameter int MEMORY_POOL_SIZE = QUEUE_SIZE * PACKET_SIZE;
parameter int MEMORY_POOL_ADDR_LEN = QUEUE_ADDR_LEN + PACKET_ADDR_LEN;
parameter int MEMORY_POOL_ADDR_SHIFT = 4;
parameter int INTERFACE_QUEUE_SIZE = 512;
parameter int INTERFACE_QUEUE_ADDR_LEN = 9;
parameter int INTERFACE_CNT = 4;
parameter int INTERFACE_ADDR_LEN = 2;
parameter int CRC_BITS = 8;
parameter shortint NEW_SLOT_COOLDOWN = 500;
parameter shortint INTERFACE_IDLE_COUNTDOWN = 4;
`endif

21
src/fabric/routing.svh Normal file
View File

@ -0,0 +1,21 @@
`ifndef __ROUTING__SVH__
`define __ROUTING__SVH__
`include <params.svh>
function automatic logic [INTERFACE_ADDR_LEN - 1:0] next_hop(input logic [ROSE_ADDR_LEN - 1:0] interface_addr);
case(interface_addr)
0:
return 0;
1:
return 1;
2:
return 2;
3:
return 3;
default:
return 0;
endcase // case (interface_addr)
endfunction // next_hop
`endif

58
src/fabric/tb.sv Normal file
View File

@ -0,0 +1,58 @@
module tb;
timeunit 1ns;
timeprecision 1ps;
logic sys_clk = 0;
always #3.703 sys_clk = ~sys_clk;
logic sclk = 0;
logic cs = 1;
logic mosi = 0;
logic miso;
logic rst_n = 0;
string hello = {'b1111_0000_1111_0000, "_ABCD"};
spi_slave dut (.sys_clk(sys_clk),
.mosi(mosi),
.cs(cs),
.sclk(sclk),
.miso(miso));
initial begin
#20 rst_n = 1;
end
task spi_test (input logic [7:0] tx_byte,
output logic [7:0] rx_byte);
begin
cs = 0;
#30;
for(int idx = 1; idx < hello.len(); idx = idx + 1) begin
tx_byte = hello[idx];
$display("%0d=================================================", $time);
for (int i = 7; i >= 0; i = i - 1) begin
mosi = tx_byte[i];
sclk = 1;
#10;
sclk = 0;
rx_byte[i] = miso;
#10;
$display("[%0d] received bit: %b", $time, miso);
end // for (int i = 7; i >= 0; i = i - 1)
$display("[%0d] Sent: %c | %b", $time, tx_byte, tx_byte);
$display("[%0d] Received: %c (expected %c)", $time, rx_byte, hello[idx - 1] + 8'd1);
$display("[%0d] Received: %b (expected %b)", $time, rx_byte, hello[idx - 1] + 8'd1);
$display("%0d=================================================", $time);
end
cs = 1;
#40;
end
endtask // spi_test
logic [7:0] tx_data = 8'd65;
logic [7:0] rx_data;
initial begin
spi_test(tx_data, rx_data);
end
endmodule // tb