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