minor change: revised directory structure, added MIT license
This commit is contained in:
1
src/fabric/.gitignore
vendored
Normal file
1
src/fabric/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
obj_dir/
|
212
src/fabric/hub.sv
Normal file
212
src/fabric/hub.sv
Normal 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
146
src/fabric/interface.sv
Normal 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
23
src/fabric/params.svh
Normal 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
21
src/fabric/routing.svh
Normal 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
58
src/fabric/tb.sv
Normal 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
|
Reference in New Issue
Block a user