Add MultiFifo MultiFifo is a Fifo implementation that can enqueue/dequeue multiple elements in a cycle. Change-Id: I36087f2a3bb5f4e7517eb59ce3a429f6201e9fd7
diff --git a/hdl/verilog/rvv/Makefile b/hdl/verilog/rvv/Makefile index 0814e2a..e95da0e 100644 --- a/hdl/verilog/rvv/Makefile +++ b/hdl/verilog/rvv/Makefile
@@ -4,7 +4,13 @@ Aligner_tb: Aligner_tb.sv Aligner.sv @vcs -full64 -sverilog Aligner.sv Aligner_tb.sv -o Aligner_tb +MultiFifo: MultiFifo.sv + @vcs -full64 -sverilog MultiFifo.sv -o MultiFifo + +MultiFifo_tb: MultiFifo_tb.sv MultiFifo.sv + @vcs -full64 -sverilog MultiFifo.sv MultiFifo_tb.sv -o MultiFifo_tb + .PHONY : clean clean: - @rm -f Aligner Aligner_tb + @rm -f Aligner Aligner_tb MultiFifo MultiFifo_tb
diff --git a/hdl/verilog/rvv/MultiFifo.sv b/hdl/verilog/rvv/MultiFifo.sv new file mode 100644 index 0000000..27821cd --- /dev/null +++ b/hdl/verilog/rvv/MultiFifo.sv
@@ -0,0 +1,99 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A FIFO that can enqueue and dequeue multiple elements at a time. +module MultiFifo#(type T=logic [7:0], + parameter N = 4, + parameter MAX_CAPACITY=16, + parameter INTERFACE_BITS=$clog2(N+1), + parameter CAPACITYBITS=$clog2(MAX_CAPACITY+1)) +( + input clk, + input rstn, + + // Command input. + input logic [INTERFACE_BITS-1:0] valid_in, + input T [N-1:0] data_in, + + // fill_level is used to determine if elements can be enqueued and dequeued. + // valid_in must be <= MAX_CAPACITY - fill_level + // ready_out must be <= fill_level + output logic [CAPACITYBITS-1:0] fill_level, + + // Command output. + output T [N-1:0] data_out, + input logic [INTERFACE_BITS-1:0] ready_out +); + typedef logic [CAPACITYBITS-1:0] buffer_ptr_t; + typedef logic [CAPACITYBITS-1:0] buffer_size_t; + + // Module state + buffer_ptr_t head; // Elements to enqueue + buffer_ptr_t tail; // Elements to dequeue + buffer_size_t m_fill_level; + T [MAX_CAPACITY-1:0] buffer; + + function automatic buffer_ptr_t WrapAroundSum(buffer_ptr_t ptr, + buffer_size_t sz); + logic [CAPACITYBITS:0] sum; + sum = ptr + sz; + return (sum >= MAX_CAPACITY) ? (sum - MAX_CAPACITY) : sum; + endfunction + + // Output fill_level + always_comb begin + fill_level = m_fill_level; + end + + always_ff @(posedge clk or negedge rstn) begin + if (!rstn) begin + head <= 0; + tail <= 0; + m_fill_level <= 0; + end else begin + head <= WrapAroundSum(head, valid_in); + tail <= WrapAroundSum(tail, ready_out); + + // Update buffer + for (int i = 0; i < N; i++) begin + if (i < valid_in) begin + buffer[WrapAroundSum(head, i)] <= data_in[i]; + end + end + m_fill_level <= m_fill_level + valid_in - ready_out; + end + end + + always_comb begin + for (int i = 0; i < N; i++) begin + data_out[i] = buffer[WrapAroundSum(tail, i)]; + end + end + + // Assertions +`ifndef SYNTHESIS + always @(posedge clk) begin + // Producer should enqueue less than what's empty + assert (valid_in <= (MAX_CAPACITY - m_fill_level)) else + $error("Trying to enqueue ", valid_in, " elements ", + (MAX_CAPACITY - m_fill_level), " free"); + + // Consumer should dequeue less than what's full + assert (ready_out <= m_fill_level) else + $error("Trying to dequeue ", ready_out, " elements ", m_fill_level, + " free"); + end +`endif // not def SYNTHESIS + +endmodule \ No newline at end of file
diff --git a/hdl/verilog/rvv/MultiFifo_tb.sv b/hdl/verilog/rvv/MultiFifo_tb.sv new file mode 100644 index 0000000..bcb7ee7 --- /dev/null +++ b/hdl/verilog/rvv/MultiFifo_tb.sv
@@ -0,0 +1,258 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +class MultiFifoTransaction; + rand logic [3:0][31:0] data; + rand logic [2:0] valid; // Number of elements that producer is enqueuing + rand logic [2:0] ready; // Number of elements that consumer is dequeuing. + + constraint valid_limit { valid < 4; }; + constraint ready_limit { ready < 4; }; +endclass + +class MultiFifoTransactionGenerator; + rand MultiFifoTransaction transaction; + mailbox gen2driv; + int n; + event finished; + + function new(mailbox gen2driv, int n, event finished); + this.gen2driv = gen2driv; + this.n = n; + this.finished = finished; + endfunction + + task generate_transactions(); + repeat(n) begin + transaction = new(); + if( !transaction.randomize() ) $fatal("Gen:: trans randomization failed"); + gen2driv.put(transaction); + end + -> finished; + endtask +endclass + +interface MultiFifoInterface(input logic clk, rstn); + logic [2:0] valid_in; + logic [3:0][31:0] data_in; + logic [4:0] fill_level; + logic [3:0][31:0] data_out; + logic [2:0] ready_out; + + clocking driver_cb @(posedge clk); + default input negedge output negedge; + input fill_level, data_out; + output valid_in, data_in, ready_out; + endclocking + + modport DRIVER (clocking driver_cb, input clk, rstn); + +endinterface + +class MultiFifoDriver; + virtual MultiFifoInterface multififo_iface; + mailbox gen2driv; + mailbox enqueued; + mailbox dequeued; + + MultiFifoTransaction transaction; + int n_transactions_processed; + + logic [2:0] enqueued_fired; // Number of elements to enqueue + logic [2:0] dequeued_fired; // Number of elements to enqueue + logic [4:0] fill_level; + logic [3:0][31:0] data_out; + + function new(virtual MultiFifoInterface multififo_iface, + mailbox gen2driv, + mailbox enqueued, + mailbox dequeued); + this.multififo_iface = multififo_iface; + this.gen2driv = gen2driv; + this.enqueued = enqueued; + this.dequeued = dequeued; + endfunction + + task reset; + wait(!multififo_iface.rstn); + $display("Reset started."); + multififo_iface.DRIVER.driver_cb.valid_in <= 0; + multififo_iface.DRIVER.driver_cb.data_in <= 0; + multififo_iface.DRIVER.driver_cb.ready_out <= 0; + wait(multififo_iface.rstn); + $display("Finished resetting."); + endtask + + task record_enqueue_dequeue; + endtask + + task drive; + forever begin + $display("Running iteration ", n_transactions_processed); + gen2driv.get(transaction); + + @(negedge multififo_iface.DRIVER.clk); + + fill_level = multififo_iface.DRIVER.driver_cb.fill_level; + data_out = multififo_iface.DRIVER.driver_cb.data_out; + + enqueued_fired = (transaction.valid < (16 - fill_level)) ? + transaction.valid : 16 - fill_level; + dequeued_fired = (transaction.ready > fill_level) ? + fill_level : transaction.ready; + + multififo_iface.DRIVER.driver_cb.valid_in <= enqueued_fired; + multififo_iface.DRIVER.driver_cb.data_in <= transaction.data; + multififo_iface.DRIVER.driver_cb.ready_out <= dequeued_fired; + + // Put enqueued and dequeued elements into checker mailboxes + for (int i = 0; i < 4; i++) begin + if (i < transaction.valid && i < enqueued_fired) begin + // $display("Enqueuing ", transaction.data[i]); + enqueued.put(transaction.data[i]); + end + end + + for (int o = 0; o < 4; o++) begin + if (o < dequeued_fired && o < transaction.ready) begin + // $display("Dequeuing ", data_out[o]); + dequeued.put(data_out[o]); + end + end + + n_transactions_processed++; + + @(posedge multififo_iface.DRIVER.clk); + end + endtask + +endclass + +class MultiFifoComparator; + mailbox enqueued; + mailbox dequeued; + int n_transactions_processed = 0; + + logic [31:0] enqueued_elem; + logic [31:0] dequeued_elem; + + function new(mailbox enqueued, + mailbox dequeued); + this.enqueued = enqueued; + this.dequeued = dequeued; + endfunction + + task check; + forever begin + enqueued.get(enqueued_elem); + dequeued.get(dequeued_elem); + if (enqueued_elem != dequeued_elem) begin + $display("Error: expected ", enqueued_elem, " got ", dequeued_elem); + end + n_transactions_processed++; + end + endtask +endclass + +class MultiFifoEnvironment; + localparam ITERATIONS = 512; + + MultiFifoTransactionGenerator gen; + MultiFifoDriver driv; + MultiFifoComparator comparator; + mailbox gen2driv; + mailbox enqueued; + mailbox dequeued; + event gen_ended; + + virtual MultiFifoInterface multififo_iface; + + function new(virtual MultiFifoInterface multififo_iface); + this.multififo_iface = multififo_iface; + + gen2driv = new(); + enqueued = new(); + dequeued = new(); + gen = new(gen2driv, ITERATIONS, gen_ended); + driv = new(multififo_iface, gen2driv, enqueued, dequeued); + comparator = new(enqueued, dequeued); + endfunction + + task setup(); + driv.reset(); + endtask + + task test(); + fork + gen.generate_transactions(); + driv.drive(); + comparator.check(); + join_any + endtask + + task teardown(); + wait(gen_ended.triggered); + wait(driv.n_transactions_processed == ITERATIONS); + endtask + + task run; + setup(); + test(); + teardown(); + $finish; + endtask +endclass; + +program MultiFifoTest(MultiFifoInterface multififo_iface); + MultiFifoEnvironment env; + initial begin + env = new(multififo_iface); + env.run(); + end +endprogram + +module MultiFifo_tb(); + logic clk; + logic rstn; + + MultiFifoInterface multififo_iface(clk, rstn); + + typedef logic[31:0] MyInt; + MultiFifo#(.T (MyInt), .N (4), .MAX_CAPACITY (16)) + dut( + .clk(multififo_iface.clk), + .rstn(multififo_iface.rstn), + .valid_in(multififo_iface.valid_in), + .data_in(multififo_iface.data_in), + .fill_level(multififo_iface.fill_level), + .data_out(multififo_iface.data_out), + .ready_out(multififo_iface.ready_out) + ); + + // Clock and reset + initial begin + rstn = 0; + clk = 0; + #5 + clk = 1; + #5 + clk = 0; + rstn = 1; + + forever + #5 clk = ~clk; + end + + MultiFifoTest test(multififo_iface); +endmodule