work in progress, major overhaul for design, see devlogs for details. Also added the first version of the style guide
This commit is contained in:
@ -1,96 +1,167 @@
|
||||
module hub (
|
||||
input logic rst,
|
||||
input logic sys_clk,
|
||||
input logic [31:0] rx_cmd, // for routing-related commands
|
||||
input logic [3:0] rx_cmd_valid,
|
||||
input logic [31:0] rx_byte,
|
||||
input logic [3:0] rx_valid,
|
||||
input logic [31:0] rx2tx_dest, // rx byte's destination
|
||||
input logic [3:0] tx_ready, // if tx_byte is ready to be read
|
||||
output logic [3:0] rx_ready, // if rx_byte is ready to be read
|
||||
output logic [7:0] tx_src, // tell the tx where the stream is comming from
|
||||
output logic [31:0] tx_byte,
|
||||
output logic [3:0] tx_valid,
|
||||
output logic [1:0] packet_size); // 4 states for 4 fixed packet sizes
|
||||
`include <params.sv>
|
||||
|
||||
// IMPORTANT: interfaces are supposed to keep track of their own packet states
|
||||
module hub(
|
||||
input logic sys_clk,
|
||||
input logic rst,
|
||||
input logic [INTERFACE_CNT - 1][PACKET_ADDR_LEN - 1:0] rx_pkt_addr,
|
||||
input logic [INTERFACE_CNT - 1:0][7:0] rx_byte,
|
||||
input logic [INTERFACE_CNT - 1:0] rx_valid,
|
||||
input logic [INTERFACE_CNT - 1:0] tx_ready,
|
||||
input logic [INTERFACE_CNT - 1:0] tx_full,
|
||||
input logic [INTERFACE_CNT - 1:0][PACKET_ADDR_LEN - 1:0] tx_pkt_addr,
|
||||
input logic [INTERFACE_CNT - 1:0] rx_new_packet,
|
||||
output logic [INTERFACE_CNT - 1:0] rx_ready,
|
||||
output logic [INTERFACE_CNT - 1:0][PACKET_ADDR_LEN - 1:0] tx_queue_addr,
|
||||
output logic [INTERFACE_CNT - 1:0] tx_queue_addr_valid,
|
||||
output logic [INTERFACE_CNT - 1:0][7:0] tx_byte,
|
||||
output logic [INTERFACE_CNT - 1:0] tx_valid);
|
||||
timeunit 1ns;
|
||||
timeprecision 1ps;
|
||||
|
||||
logic [INTERFACE_CNT - 1:0] curr_service;
|
||||
logic request_new_slot;
|
||||
logic [QUEUE_ADDR_LEN - 1:0] new_slot_addr;
|
||||
logic free_queue_empty;
|
||||
logic [QUEUE_ADDR_LEN - 1:0] empty_slot_addr;
|
||||
logic [QUEUE_ADDR_LEN - 1:0] 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));
|
||||
|
||||
// TBD: pre-agree on packet size
|
||||
logic [INTERFACE_CNT - 1:0][MEMORY_ADDR_LEN - 1:0] rx_mem_addr;
|
||||
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;
|
||||
|
||||
// use the round-robin strat to poll since the routing is much faster
|
||||
// NOTE: To expand to more connected_devices, use a hierarchical design
|
||||
logic [1:0] curr_service = 0;
|
||||
logic [1:0] last_dest = 0;
|
||||
|
||||
// src dest byte
|
||||
typedef struct {
|
||||
logic [1:0] dest;
|
||||
logic [7:0] payload;
|
||||
} svc_buffer;
|
||||
svc_buffer service_buffer [3:0];
|
||||
svc_buffer curr_buffer;
|
||||
assign curr_buffer = service_buffer[curr_service];
|
||||
logic [3:0] in_buffer;
|
||||
assign rx_ready = ~in_buffer;
|
||||
|
||||
always_ff @ (posedge sys_clk) begin
|
||||
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
|
||||
in_buffer <= '0;
|
||||
tx_src <= '0;
|
||||
tx_queue_addr <= '0;
|
||||
tx_queue_addr_valid <= '0;
|
||||
tx_byte <= '0;
|
||||
tx_valid <= '0;
|
||||
packet_size <= '0;
|
||||
curr_service <= '0;
|
||||
last_dest <= '0;
|
||||
for (int i = 0; i < 4; i++) begin
|
||||
service_buffer[i] <= '0;
|
||||
end
|
||||
end else begin // if (rst)
|
||||
// Handle RX side logic
|
||||
for (int i = 0; i < 4; i++) begin
|
||||
if (rx_valid[i]) begin
|
||||
if (!in_buffer[i]) begin
|
||||
service_buffer[i].dest <= get_hop(rx2tx_dest, i[1:0]);
|
||||
service_buffer[i].payload <= get_byte(rx_byte, i[1:0]);
|
||||
in_buffer[i] <= 1;
|
||||
rx_ready <= '0;
|
||||
rx_mem_addr <= '0;
|
||||
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_service] <= 0;
|
||||
rx_ready[curr_service + 1] <= 1;
|
||||
|
||||
// IMPORTANT: interfaces should send the byte no matter what, rx_ready is to prevent sending a new byte
|
||||
if (rx_valid[curr_service]) begin
|
||||
// IMPORTANT: memory_write_addr is ready on the next cycle
|
||||
if (rx_new_packet[curr_service]) begin
|
||||
if (free_queue_empty) begin
|
||||
// TODO: handle the drop logic
|
||||
end else begin
|
||||
request_new_slot <= 1;
|
||||
rx_mem_addr[{curr_service,
|
||||
MEMORY_POOL_ADDR_SHIFT'd0}
|
||||
+:MEMORY_POOL_ADDR_LEN
|
||||
] <= {new_slot_addr, PACKET_ADDR_LEN'd0};
|
||||
mem_write_addr <= {new_slot_addr, PACKET_ADDR_LEN'd0};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Handle TX side logic
|
||||
if (in_buffer[curr_service] && tx_ready[curr_buffer.dest]) begin
|
||||
tx_byte[{curr_buffer.dest, 3'b000} +: 8]
|
||||
<= curr_buffer.payload;
|
||||
tx_src[{curr_buffer.dest, 1'b0} +: 2]
|
||||
<= curr_service;
|
||||
in_buffer[curr_service] <= 0;
|
||||
tx_valid[curr_buffer.dest] <= 1;
|
||||
end
|
||||
tx_valid[last_dest] <= 0;
|
||||
last_dest <= service_buffer[curr_service].dest;
|
||||
curr_service <= curr_service + 1;
|
||||
end // else: !if(rst)
|
||||
end // always_ff @ (posedge sys_clk)
|
||||
|
||||
end else begin // if (rx_new_packet[curr_service])
|
||||
// NOTE: if memory
|
||||
mem_write_addr <= mem_write_addr + 1;
|
||||
request_new_slot <= 0;
|
||||
end // else: !if(rx_new_packet[curr_service])
|
||||
mem_write_byte <= rx_byte[{curr_service, 3'd0}+:8];
|
||||
mem_write_enable <= 1;
|
||||
end else // if (rx_valid[curr_service])
|
||||
mem_write_enable <= 0;
|
||||
end
|
||||
end
|
||||
endmodule // hub
|
||||
|
||||
function automatic logic [7:0] get_byte(input logic [31:0] byte_arr,
|
||||
input logic [1:0] idx);
|
||||
return byte_arr[{idx, 3'b000} +: 8];
|
||||
endfunction // get_byte
|
||||
// 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;
|
||||
|
||||
// NOTE: addr 0 is alway mapped to the fabric itself and caught before this
|
||||
function automatic logic [1:0] get_hop(input logic [31:0] dest_map,
|
||||
input logic [1:0] idx);
|
||||
case (dest_map[{idx, 3'b000} +: 8])
|
||||
8'b00000001:
|
||||
return 2'b00;
|
||||
8'b00000010:
|
||||
return 2'b01;
|
||||
8'b00000011:
|
||||
return 2'b10;
|
||||
8'b00000100:
|
||||
return 2'b11;
|
||||
default:
|
||||
return 0;
|
||||
endcase // case (dest_map[{idx, 3'b000} +: 8])
|
||||
endfunction // get_hop
|
||||
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'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
|
||||
|
11
fabric/src/params.sv
Normal file
11
fabric/src/params.sv
Normal file
@ -0,0 +1,11 @@
|
||||
parameter int PACKET_SIZE = 64;
|
||||
parameter int PACKET_ADDR_LEN = 6;
|
||||
parameter int 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 CRC_BITS = 8;
|
Reference in New Issue
Block a user