Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions verilog/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@


.PHONY: compile sim clean test
.SILENT: compile sim clean test

compile:
iverilog -g2005-sv -o sim -c modules.txt -s rr_scheduling_kernel_tb

sim:
echo "finish" | vvp sim -lxt2
/Applications/gtkwave.app/Contents/Resources/bin/gtkwave test.vcd

test:
iverilog -g2005-sv -o test_sim -c modules.txt -s memory_tb
vvp test_sim -lxt2
rm test_sim

iverilog -g2005-sv -o test_sim -c modules.txt -s test_pivots_rr_scheduling_kernel_tb
vvp test_sim -lxt2
rm test_sim

iverilog -g2005-sv -o test_sim -c modules.txt -s test_request_selection_rr_scheduling_kernel_tb
vvp test_sim -lxt2
rm test_sim

iverilog -g2005-sv -o test_sim -c modules.txt -s test_request_mux_tb
vvp test_sim -lxt2
rm test_sim

clean:
rm -f sim
rm -f test.vcd
rm -f memory_sim
rm -f memory_tb.vcd
46 changes: 46 additions & 0 deletions verilog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Optimized Arbiter Verilog Implementation

## Quickstart

### Install Icarus Verilog
For MacOS:
```bash
brew install icarus-verilog
```

### Install gtkwave

For MacOS:
```bash

# Install gtkwave
brew tap homebrew/cask
brew cask install gtkwave

# Perl Switch
cpan install Switch
perl -V:'installsitelib'
sudo cp /usr/local/Cellar/perl/5.*/lib/perl5/site_perl/5.*/Switch.pm /Library/Perl/5.*/
```

### Run Testbench

Run unit tests:
```bash
make test
```

Compile SystemVerilog:
```bash
iverilog -g2005-sv -o sim -c modules.txt -s <module_name>
```

Run the simulation:
```bash
vvp sim -lxt2
```

See the waveforms:
```bash
gtkwave test.vcd
```
13 changes: 13 additions & 0 deletions verilog/modules.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
utils/utils.sv
tb/memory_tb.sv
tb/test_request_mux_tb.sv
tb/rr_scheduling_kernel_tb.sv
tb/test_request_selection_rr_scheduling_kernel_tb.sv
tb/test_pivots_rr_scheduling_kernel_tb.sv
modules/memory.sv
modules/request_mux.sv
modules/response_mux.sv
modules/rr_scheduling_kernel.sv


//
39 changes: 39 additions & 0 deletions verilog/modules/memory.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module memory #(
parameter DATA = 8,
parameter ADDR = 4
) (
input wire clk,
input wire rst_n,

// Port A
input wire a_wr,
input wire [ADDR-1:0] a_addr,
input wire [DATA-1:0] a_din,
output reg [DATA-1:0] a_dout,

input wire b_wr,
input wire [ADDR-1:0] b_addr,
input wire [DATA-1:0] b_din,
output reg [DATA-1:0] b_dout
);

// Shared memory
reg [DATA-1:0] mem [(2**ADDR)-1:0];

always @(posedge clk) begin
a_dout <= mem[a_addr];
b_dout <= mem[b_addr];
if(a_wr) begin
a_dout <= a_din;
mem[a_addr] <= a_din;
end
if(b_wr) begin
b_dout <= b_din;
mem[b_addr] <= b_din;
end
if (b_wr == 1 && a_wr == 1 && a_addr == b_addr) begin
$display("conflicting write on addr: %h", a_addr);
$finish;
end
end
endmodule
17 changes: 17 additions & 0 deletions verilog/modules/request_mux.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

module request_mux #(
parameter REQ_WIDTH = 10,
parameter REQ_NUMBER = 16)
(
input [REQ_WIDTH-1:0] requests [REQ_NUMBER],
input [$clog2(REQ_NUMBER)-1: 0] select,
output [REQ_WIDTH-1:0] selected_request
);
/*
I know muxes are tricky for FPGAs, so factored this out.
https://www.doulos.com/knowhow/fpga/multiplexer-variations/

*/

assign selected_request = requests[select];
endmodule
35 changes: 35 additions & 0 deletions verilog/modules/response_mux.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module response_mux #(
parameter RES_WIDTH = 20,
parameter NKERNELS = 4,
parameter NCONSUMERS = 8,
parameter C_ID = 0)
(
input [RES_WIDTH-1:0] augumented_plm_outputs [NKERNELS],
input [$clog2(NCONSUMERS)-1:0] response_pivots [NKERNELS],
output reg [RES_WIDTH-1:0] selected_response);

/*
Produce a 1-hot where the asserted bit represents the k_id of the port where the respone is for this C_ID.
*/

wire [NKERNELS-1:0] one_hot_selector;
genvar k_id;
generate
for(k_id = 0; k_id < NKERNELS; k_id = k_id + 1) begin
/* A request can only be handled by one port, so RHS will only be 1 for a single k_id if any */
assign one_hot_selector[k_id] = (response_pivots[k_id] == C_ID);
end
endgenerate

/*
One-hot mux
https://stackoverflow.com/questions/19875899/how-to-define-a-parameterized-multiplexer-using-systemverilog
*/
always @(*) begin
selected_response = 0;
for(int i = 0; i < NKERNELS; i++) begin
if (one_hot_selector == (1 << i))
selected_response = augumented_plm_outputs[i];
end
end
endmodule
131 changes: 131 additions & 0 deletions verilog/modules/rr_scheduling_kernel.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@

module rr_scheduling_kernel #(
parameter ADDR_WIDTH = 4,
parameter VALUE_WIDTH = 8,
parameter NCONSUMERS = 2,
parameter NBANKS = 1,
parameter NPORTS = 1)
(
input [REQ_WIDTH-1:0] requests [NCONSUMERS],
output [PLM_INPUT_WIDTH-1:0] plm_inputs [NKERNELS],
input [PLM_OUTPUT_WIDTH-1:0] plm_outputs [NKERNELS],
output reg [PLM_INPUT_WIDTH-1:0] out [NKERNELS],
output [RES_WIDTH-1:0] responses [NCONSUMERS],

input clk,
input reset);

localparam REQ_WIDTH = ADDR_WIDTH + VALUE_WIDTH + 1 + 1; /* addr, value, wr, valid */
localparam RES_WIDTH = PLM_OUTPUT_WIDTH + 1; /* value, valid */
localparam NUM_BANK_BITS = $clog2(NBANKS);
localparam PLM_INPUT_WIDTH = (ADDR_WIDTH - NUM_BANK_BITS) + VALUE_WIDTH + 1;
localparam PLM_OUTPUT_WIDTH = VALUE_WIDTH;

initial begin
`assert_true((NPORTS == 1 || NPORTS == 2), "unspuported number of PLM ports")
end

// NOTE: Must be > 1. Otherwise barfs up "failed assertion prts[0]->unpacked_dimensions()==0"
localparam NKERNELS = NBANKS * NPORTS;

localparam PIVOT_DIFF = NCONSUMERS / NPORTS;

/* Registers that remember the RR pivots.
Structure:
bank_0_port_0
bank_0_port_1
...
bank_1_port_0
bank_1_port_1
*/
reg [$clog2(NCONSUMERS)-1:0] rr_pivots [NKERNELS];
reg rr_response_valid_bits [NKERNELS];

reg [$clog2(NCONSUMERS)-1:0] rr_response_pivots [NKERNELS];

wire [PLM_OUTPUT_WIDTH-1+1:0] augumented_plm_outputs[NKERNELS];

/* rr_pivots control */
genvar g_port_i;
genvar g_bank_i;
generate

/* There is a rr_pivot register for each port of each bank */
for (g_bank_i = 0; g_bank_i < NBANKS; g_bank_i = g_bank_i + 1) begin
for (g_port_i = 0; g_port_i < NPORTS; g_port_i = g_port_i + 1) begin
localparam K_ID = g_bank_i * NPORTS + g_port_i; /* ID of the scheduling kernel */

/****************************** REQUEST ROUTING ******************************/

/* Determine validity of the candidate that the pivot is pointing to */
wire [REQ_WIDTH-1:0] lead_candidate; /* Bits of the request that pivot is pointing to */
wire [$clog2(NCONSUMERS)-1:0] sel_candidate; /* Candidate select */
wire [NUM_BANK_BITS-1:0] bank_address; /* Address bit of the said candidate */
wire is_candidate_addr_in_range; /* Is the address in range of the bank? */
wire is_valid_bit; /* Is the candidate valid bit set? */
wire is_eligible_request; /* Is this a request eligible for scheduling? */

assign sel_candidate = rr_pivots[K_ID];
request_mux #( .REQ_WIDTH(REQ_WIDTH), .REQ_NUMBER(NCONSUMERS)) req_mux (
.requests(requests), .select(sel_candidate), .selected_request(lead_candidate));

assign bank_address = lead_candidate[REQ_WIDTH-1:REQ_WIDTH-NUM_BANK_BITS];

assign is_candidate_addr_in_range = (bank_address == g_bank_i);
assign is_valid_bit = lead_candidate[0];
assign is_eligible_request = is_candidate_addr_in_range && is_valid_bit;

assign plm_inputs[K_ID] = (is_eligible_request) ?
lead_candidate[REQ_WIDTH-1:1] /* not including request valid bit */
: 0; /* All 0s is wr=0, this its a resource read */

/****************************** PLM OUTPUT AUGUMENTATION ******************************/

/* Need to augument the outputs with the validity bits */
assign augumented_plm_outputs[K_ID] = {plm_outputs[K_ID], rr_response_valid_bits[K_ID]};

/****************************** PIVOT UPDATE ******************************/
always @(posedge clk or posedge reset) begin

/* Progress the pivot up, rely on wrapping */
rr_pivots[K_ID] <= rr_pivots[K_ID] + 1;

/* Tell the response logic for what consumer is the request being served */
rr_response_valid_bits[K_ID] <= is_eligible_request;
rr_response_pivots[K_ID] <= rr_pivots[K_ID];

if (reset) begin
/* Maximize the spread of pivots */
rr_pivots[K_ID] <= g_bank_i + g_port_i * PIVOT_DIFF;
rr_response_valid_bits[K_ID] <= 1'b0;
end
end
end
end
endgenerate

/****************************** RESPONSE ROUTING ******************************/

genvar consumer_id;
generate
for(consumer_id = 0; consumer_id < NCONSUMERS; consumer_id = consumer_id + 1) begin

wire [RES_WIDTH-1:0] selected_response;

response_mux #(
.RES_WIDTH(RES_WIDTH),
.NKERNELS(NKERNELS),
.NCONSUMERS(NCONSUMERS),
.C_ID(consumer_id)
) res_mux (
.augumented_plm_outputs(augumented_plm_outputs),
.response_pivots(rr_response_pivots),
.selected_response(selected_response)
);

assign responses[consumer_id] = selected_response;

end
endgenerate

endmodule
Loading