From dc771a6458155989928847e5e069733b6ab7229d Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Wed, 6 Mar 2024 12:34:09 +0100 Subject: [PATCH] Add passthrough_stream_fifo (#215) --- .gitlab-ci.yml | 1 + Bender.yml | 2 + CHANGELOG.md | 4 + README.md | 29 ++++--- common_cells.core | 1 + src/passthrough_stream_fifo.sv | 122 ++++++++++++++++++++++++++ src_files.yml | 1 + test/passthrough_stream_fifo_tb.sv | 135 +++++++++++++++++++++++++++++ 8 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 src/passthrough_stream_fifo.sv create mode 100644 test/passthrough_stream_fifo_tb.sv diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2cadb0b9..21b7a01d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -42,6 +42,7 @@ tests: - clk_mux_glitch_free_tb - id_queue_tb - isochronous_crossing_tb + - passthrough_stream_fifo_tb - popcount_tb - rr_arb_tree_tb - stream_omega_net_tb diff --git a/Bender.yml b/Bender.yml index 1f93df61..05231665 100644 --- a/Bender.yml +++ b/Bender.yml @@ -47,6 +47,7 @@ sources: - src/mv_filter.sv - src/onehot_to_bin.sv - src/plru_tree.sv + - src/passthrough_stream_fifo.sv - src/popcount.sv - src/rr_arb_tree.sv - src/rstgen_bypass.sv @@ -127,6 +128,7 @@ sources: - test/fifo_tb.sv - test/graycode_tb.sv - test/id_queue_tb.sv + - test/passthrough_stream_fifo_tb.sv - test/popcount_tb.sv - test/rr_arb_tree_tb.sv - test/stream_test.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f53b40..d393a342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased +### Added +- Add `passthrough_stream_fifo`: stream FIFO which does not cut the timing path, this allows it to do a simultaneous push and pop when full. + ## 1.32.0 - 2023-09-26 ### Added - Add `stream_join_dynamic`: `stream_join` with a dynamically configurable subset selection. diff --git a/README.md b/README.md index d22e2ca2..8c2aa59b 100644 --- a/README.md +++ b/README.md @@ -109,20 +109,21 @@ Please note that cells with status *deprecated* are not to be used for new desig ### Data Structures -| Name | Description | Status | Superseded By | -| -------------------------- | ----------------------------------------------------------------------- | ------------ | ------------- | -| `cb_filter` | Counting-Bloom-Filter with combinational lookup | active | | -| `fifo` | FIFO register with upper threshold | *deprecated* | `fifo_v3` | -| `fifo_v2` | FIFO register with upper and lower threshold | *deprecated* | `fifo_v3` | -| `fifo_v3` | FIFO register with generic fill counts | active | | -| `stream_fifo` | FIFO register with ready/valid interface | active | | -| `stream_fifo_optimal_wrap` | Wrapper that optimally selects either a spill register or a FIFO | active | | -| `generic_fifo` | FIFO register without thresholds | *deprecated* | `fifo_v3` | -| `generic_fifo_adv` | FIFO register without thresholds | *deprecated* | `fifo_v3` | -| `sram` | SRAM behavioral model | active | | -| `plru_tree` | Pseudo least recently used tree | active | | -| `unread` | Empty module to sink unconnected outputs into | active | | -| `read` | Dummy module that prevents a signal from being removed during synthesis | active | | +| Name | Description | Status | Superseded By | +| -------------------------- | --------------------------------------------------------------------------- | ------------ | ------------- | +| `cb_filter` | Counting-Bloom-Filter with combinational lookup | active | | +| `fifo` | FIFO register with upper threshold | *deprecated* | `fifo_v3` | +| `fifo_v2` | FIFO register with upper and lower threshold | *deprecated* | `fifo_v3` | +| `fifo_v3` | FIFO register with generic fill counts | active | | +| `passthrough_stream_fifo` | FIFO register with ready/valid interface and same-cycle push/pop when full | active | | +| `stream_fifo` | FIFO register with ready/valid interface | active | | +| `stream_fifo_optimal_wrap` | Wrapper that optimally selects either a spill register or a FIFO | active | | +| `generic_fifo` | FIFO register without thresholds | *deprecated* | `fifo_v3` | +| `generic_fifo_adv` | FIFO register without thresholds | *deprecated* | `fifo_v3` | +| `sram` | SRAM behavioral model | active | | +| `plru_tree` | Pseudo least recently used tree | active | | +| `unread` | Empty module to sink unconnected outputs into | active | | +| `read` | Dummy module that prevents a signal from being removed during synthesis | active | | ## Header Contents diff --git a/common_cells.core b/common_cells.core index 89fd483c..654f639d 100644 --- a/common_cells.core +++ b/common_cells.core @@ -31,6 +31,7 @@ filesets: - src/mv_filter.sv - src/onehot_to_bin.sv - src/plru_tree.sv + - src/passthrough_stream_fifo.sv - src/popcount.sv - src/rr_arb_tree.sv - src/rstgen_bypass.sv diff --git a/src/passthrough_stream_fifo.sv b/src/passthrough_stream_fifo.sv new file mode 100644 index 00000000..b7a01b4d --- /dev/null +++ b/src/passthrough_stream_fifo.sv @@ -0,0 +1,122 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Thomas Benz +// - Tobias Senti + +`include "common_cells/assertions.svh" +`include "common_cells/registers.svh" + +/// Stream FIFO that does not cut the timing path. When full; pushing data is allowed if in +/// the same cycle data is popped. Creates longer timing paths but can use buffer space more +/// efficiently. +module passthrough_stream_fifo #( + /// Depth can be arbitrary from 2 to 2**32 + parameter int unsigned Depth = 32'd8, + /// Print information when the simulation launches + parameter bit PrintInfo = 1'b0, + /// If the FIFO is full, allow reading and writing in the same cycle + parameter bit SameCycleRW = 1'b1, + /// Type of the FIFO + parameter type type_t = logic +) ( + /// Clock + input logic clk_i, + /// Asynchronous reset active low + input logic rst_ni, + /// Fifo flush + input logic flush_i, + /// Bypass clock gate + input logic testmode_i, + /// data to push into the FIFO + input type_t data_i, + /// input data valid + input logic valid_i, + /// FIFO is not full + output logic ready_o, + /// output data + output type_t data_o, + /// FIFO is not empty + output logic valid_o, + /// pop head from FIFO + input logic ready_i +); + /// Bit Width of the read and write pointers + /// One additional bit to detect overflows + localparam int unsigned PointerWidth = $clog2(Depth) + 1; + + // Read and write pointers + logic [PointerWidth-1:0] read_ptr_d, read_ptr_q; + logic [PointerWidth-1:0] write_ptr_d, write_ptr_q; + + // Data + type_t [Depth-1 :0] data_d, data_q; + + // Enable storage + logic load_data; + + assign data_o = data_q[read_ptr_q[PointerWidth-2:0]]; + + // Logic + always_comb begin + // Default + load_data = 1'b0; + read_ptr_d = read_ptr_q; + write_ptr_d = write_ptr_q; + data_d = data_q; + + if (flush_i) begin // Flush + read_ptr_d = '0; + write_ptr_d = '0; + valid_o = 1'b0; + ready_o = 1'b0; + end else begin + // Read + valid_o = read_ptr_q[PointerWidth-1] == write_ptr_q[PointerWidth-1] + ? read_ptr_q[PointerWidth-2:0] != write_ptr_q[PointerWidth-2:0] : 1'b1; + if (ready_i) begin + if (read_ptr_q[PointerWidth-2:0] == (Depth-1)) begin + // On overflow reset pointer to zero and flip imaginary bit + read_ptr_d[PointerWidth-2:0] = '0; + read_ptr_d[PointerWidth-1] = !read_ptr_q[PointerWidth-1]; + end else begin + // Increment counter + read_ptr_d = read_ptr_q + 'd1; + end + end + + // Write -> Also able to write if we read in the same cycle + ready_o = (read_ptr_q[PointerWidth-1] == write_ptr_q[PointerWidth-1] + ? 1'b1 : write_ptr_q[PointerWidth-2:0] != read_ptr_q[PointerWidth-2:0]) + || (SameCycleRW && ready_i && valid_o); + + if (valid_i) begin + load_data = 1'b1; + data_d[write_ptr_q[PointerWidth-2:0]] = data_i; + + if (write_ptr_q[PointerWidth-2:0] == (Depth-1)) begin + // On overflow reset pointer to zero and flip imaginary bit + write_ptr_d[PointerWidth-2:0] = '0; + write_ptr_d[PointerWidth-1] = !write_ptr_q[PointerWidth-1]; + end else begin + // Increment pointer + write_ptr_d = write_ptr_q + 'd1; + end + end + end + end + + // Flip Flops + `FF( read_ptr_q, read_ptr_d, '0, clk_i, rst_ni) + `FF(write_ptr_q, write_ptr_d, '0, clk_i, rst_ni) + + `FFL(data_q, data_d, load_data, '0, clk_i, rst_ni) + + // no full push + `ASSERT_NEVER(CheckFullPush, (!ready_o & valid_i), clk_i, !rst_ni) + // empty pop + `ASSERT_NEVER(CheckEmptyPop, (!valid_o & ready_i), clk_i, !rst_ni) + +endmodule diff --git a/src_files.yml b/src_files.yml index 41b45410..0449fabe 100644 --- a/src_files.yml +++ b/src_files.yml @@ -26,6 +26,7 @@ common_cells_all: - src/mv_filter.sv - src/onehot_to_bin.sv - src/plru_tree.sv + - src/passthrough_stream_fifo.sv - src/popcount.sv - src/rr_arb_tree.sv - src/rstgen_bypass.sv diff --git a/test/passthrough_stream_fifo_tb.sv b/test/passthrough_stream_fifo_tb.sv new file mode 100644 index 00000000..d0939d61 --- /dev/null +++ b/test/passthrough_stream_fifo_tb.sv @@ -0,0 +1,135 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 + +// Authors: +// - Tobias Senti + +`timescale 1ns/1ns + +/// This modules verifies basic operation of the passthrough_stream_fifo_tb. +module passthrough_stream_fifo_tb #( + parameter int unsigned TCK = 10, + parameter int unsigned DataWidth = 8, + parameter int unsigned Depth = 10, + parameter int unsigned NumStims = 1000, + parameter int unsigned WriteProbability = 10, + parameter int unsigned ReadProbability = 10, + parameter bit SameCycleRW = 1'b1 +) (); + typedef logic [DataWidth-1:0] data_t; + + int unsigned applied_stims, acquired_stims; + + logic clk, rst_n; + + data_t in_data, out_data; + logic in_valid, in_ready, out_valid, out_ready; + + // Data queues + data_t app_queue[$], acq_queue[$]; + + //Clock generator + clk_rst_gen #( + .ClkPeriod ( TCK ), + .RstClkCycles ( 1 ) + ) i_clk_rst_gen ( + .clk_o ( clk ), + .rst_no ( rst_n ) + ); + + // DUT + passthrough_stream_fifo #( + .Depth ( Depth ), + .type_t ( data_t ), + .PrintInfo ( 1'b1 ), + .SameCycleRW ( SameCycleRW ) + ) i_passthrough_stream_fifo ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .flush_i ( 1'b0 ), + .testmode_i ( 1'b0 ), + + .data_i ( (in_valid && in_ready) ? in_data : 'x ), + .valid_i ( in_valid && in_ready ), + .ready_o ( in_ready ), + + .data_o ( out_data ), + .valid_o ( out_valid ), + .ready_i ( out_ready && out_valid ) + ); + + // Application + initial begin + applied_stims = 0; + in_data = '0; + in_valid = 1'b0; + + // Wait for reset + wait(rst_n); + + $display("Started application!"); + + while(applied_stims < NumStims) begin + @(negedge clk); + in_valid = $urandom_range(0, WriteProbability) == 0; + in_data = $urandom(); + @(posedge clk); + if (in_valid && in_ready) begin + $display("%d Applied: %d", applied_stims, in_data); + app_queue.push_back(in_data); + applied_stims++; + end + end + in_valid = 1'b0; + + $display("Applied %d stimuli", applied_stims); + end + + // Acquisition + initial begin + acquired_stims = 0; + out_ready = 1'b0; + + // Wait for reset + wait(rst_n); + + $display("Started acquisition!"); + + forever begin + @(negedge clk); + out_ready = $urandom_range(0, ReadProbability) == 0; + @(posedge clk); + if (out_valid && out_ready) begin + $display("%d Acquired: %d", acquired_stims, out_data); + acq_queue.push_back(out_data); + acquired_stims++; + end + end + end + + // Response Checking + initial begin + int unsigned num_errors; + data_t acq_data, app_data; + + num_errors = 0; + + while((acquired_stims < NumStims) || (applied_stims < NumStims)) begin + wait((app_queue.size() != 0) && (acq_queue.size() != 0)); + + acq_data = acq_queue.pop_front(); + app_data = app_queue.pop_front(); + + if (app_data != acq_data) begin + $display("Missmatch! Applied: %d Acquired: %d", app_data, acq_data); + num_errors++; + end else begin + $display("Match! Applied: %d Acquired: %d", app_data, acq_data); + end + end + $display("Applied %d stimuli and acquired %d responses", applied_stims, acquired_stims); + $display("Errors: %d", num_errors); + $stop(); + end +endmodule