Skip to content

Commit

Permalink
Stream FIFO: Add a passthrough variant
Browse files Browse the repository at this point in the history
  • Loading branch information
thommythomaso committed Mar 6, 2024
1 parent 69d40a5 commit 18a6833
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions common_cells.core
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
122 changes: 122 additions & 0 deletions src/passthrough_stream_fifo.sv
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
// - Tobias Senti <[email protected]>

`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
1 change: 1 addition & 0 deletions src_files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
135 changes: 135 additions & 0 deletions test/passthrough_stream_fifo_tb.sv
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>

`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

0 comments on commit 18a6833

Please sign in to comment.