Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ tests:
- stream_to_mem_tb
- stream_xbar_tb
- sub_per_hash_tb
- lossy_valid_to_stream_tb
- TOPLEVEL: graycode_tb
PARAM1: [-GN=1, -GN=2, -GN=3, -GN=4, -GN=8, -GN=16]
- TOPLEVEL: fifo_tb
Expand Down
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ sources:
- src/stream_join.sv
- src/stream_mux.sv
- src/stream_throttle.sv
- src/lossy_valid_to_stream.sv
- src/sub_per_hash.sv
- src/sync.sv
- src/sync_wedge.sv
Expand Down Expand Up @@ -128,6 +129,7 @@ sources:
- test/stream_xbar_tb.sv
- test/clk_int_div_tb.sv
- test/clk_mux_glitch_free_tb.sv
- test/lossy_valid_to_stream_tb.sv


- target: synth_test
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 `lossy_valid_to_stream`: A converter between valid-only protocols and ready-valid where the latest transaction overwrites the most recently queue one.

## 1.29.0 - 2023-04-14
### Added
- Add `shift_reg_gated`: Shift register with ICG for arbitrary types.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Please note that cells with status *deprecated* are not to be used for new desig
### Data Path Elements

| Name | Description | Status | Superseded By |
| -------------------------- | --------------------------------------------------------------------------------------------------------- | ------------ | ------------- |
|----------------------------|-----------------------------------------------------------------------------------------------------------|--------------|---------------|
| `addr_decode` | Address map decoder | active | |
| `addr_decode_napot` | Address map decoder using naturally-aligned power of two (NAPOT) regions | active | |
| `ecc_decode` | SECDED Decoder (Single Error Correction, Double Error Detection) | active | |
Expand All @@ -86,6 +86,7 @@ Please note that cells with status *deprecated* are not to be used for new desig
| `stream_arbiter` | Round-robin arbiter for ready/valid stream interface | active | |
| `stream_arbiter_flushable` | Round-robin arbiter for ready/valid stream interface and flush functionality | active | |
| `stream_demux` | Ready/valid interface demultiplexer | active | |
| `lossy_valid_to_stream` | Convert Valid-only to ready/valid by updating in-flight transaction | active | |
| `stream_join` | Ready/valid handshake join multiple to one common | active | |
| `stream_mux` | Ready/valid interface multiplexer | active | |
| `stream_register` | Register with ready/valid interface | active | |
Expand Down
139 changes: 139 additions & 0 deletions src/lossy_valid_to_stream.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//-----------------------------------------------------------------------------
// Title : lossy_valid_to_stream
// -----------------------------------------------------------------------------
// File : lossy_valid_to_stream.sv Author : Manuel Eggimann
// <[email protected]> Created : 17.05.2023
// -----------------------------------------------------------------------------
// Description :
//
// This module helps to deal with sources that use a valid-only interface, that
// is they do not support backpressure i.e. cannot handle the case where the
// sink is not ready to accept a value. The module is implemented as FIFO with 2
// elements. In contrast to a regular FIFO that stops accepting new transactions
// once the FIFO is full, this IP overwrites the last element entered into the
// FIFO. This means the input is always ready to accept new transactions,
// however, intermediate transactions might be overwritten by the latest one. On
// the output side, the module behaves like a regular ready-valid source i.e.
// once valid is asserted, data_o remains stable until the sink consumes them
// (by asserting ready_i).

// IMPORTANT: As the name implies, this module might drop intermediate
// transactions if the input generates transactions faster than the sink can
// consume them. The input side can check if the last transaction was
// successfully comitted to the output by checking the
// busy_o signal. If it is de-asserted (low), there are no
// more outstanding transactions and the most recent value presented at the
// input side has been comitted to the output.
//
//
// The lossy_valid_to_stream module is helpful to connect configuration registers with
// IPs that could cause back pressure. In this case we might not care how long
// it takes for the config value to be sent to the IP but if we change the
// config value we want the latest value to be used regardless whether the
// previous value has already been forwarded or not.
//
//
//-----------------------------------------------------------------------------
// Copyright (C) 2023 ETH Zurich, University of Bologna Copyright and related
// rights are licensed under the Solderpad Hardware License, Version 0.51 (the
// "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law or
// agreed to in writing, software, hardware and materials distributed under this
// 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.
// SPDX-License-Identifier: SHL-0.51
// -----------------------------------------------------------------------------

module lossy_valid_to_stream #(
/// Default data width if the fifo is of type logic
parameter int unsigned DATA_WIDTH = 32,
parameter type T = logic [DATA_WIDTH-1:0]
) (
input logic clk_i,
input logic rst_ni,
// Input Interface (the input is always ready so there is no ready_o signal)
input logic valid_i,
input T data_i,
// Output Interface
output logic valid_o,
input logic ready_i,
output T data_o,
// Status port
output busy_o
);

// Implement a FIFO with depth == 2 where the write logic can overwrite the
// last element.

logic read_ptr_d, read_ptr_q;
logic write_ptr_d, write_ptr_q;
logic [1:0] pending_tx_counter_d, pending_tx_counter_q;
T[1:0] mem_d, mem_q;

assign valid_o = pending_tx_counter_q != 0 || valid_i;

always_comb begin : write_logic
write_ptr_d = write_ptr_q;
mem_d = mem_q;
if (valid_i) begin
// If the FIFO is empty and the output is currently ready, fall through
// the FIFO and don't update anything
if (pending_tx_counter_q != 0 || !ready_i) begin
// If the FIFO is full and the output is still stalling, update the
// previous element instead of writing a new one and do not update the
// write pointer
if (pending_tx_counter_q == 2 && !ready_i) begin
mem_d[write_ptr_q - 1'b1] = data_i;
end else begin
mem_d[write_ptr_q] = data_i;
write_ptr_d = write_ptr_q + 1'b1;
end
end
end
end

always_comb begin : read_logic
read_ptr_d = read_ptr_q;
data_o = mem_q[read_ptr_q];
// Handle the fall-through case
if (pending_tx_counter_q == 0 && valid_i) begin
data_o = data_i;
end else if (valid_o && ready_i) begin
read_ptr_d = read_ptr_q + 1'b1;
end
end

always_comb begin: count_transactions
pending_tx_counter_d = pending_tx_counter_q;
if (valid_i && valid_o && ready_i) begin
pending_tx_counter_d = pending_tx_counter_q;
Comment on lines +110 to +111
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it is. That was a voluntarily decision to make the three cases (push, pop and simultaneous push and pop) a bit more clear in the code.

end else if (valid_i && !(valid_o && ready_i)) begin
// If the FIFO is already full, do not update the counter
if (pending_tx_counter_q != 2) begin
pending_tx_counter_d = pending_tx_counter_q + 1'b1;
end
end else if (!valid_i && (valid_o && ready_i)) begin
pending_tx_counter_d = pending_tx_counter_q - 1'b1;
end
end

// Registers
always_ff @(posedge clk_i, negedge rst_ni) begin
if (!rst_ni) begin
read_ptr_q <= '0;
write_ptr_q <= '0;
pending_tx_counter_q <= '0;
mem_q <= '0;
end else begin
read_ptr_q <= read_ptr_d;
write_ptr_q <= write_ptr_d;
pending_tx_counter_q <= pending_tx_counter_d;
mem_q <= mem_d;
end
end

assign busy_o = pending_tx_counter_q != 0;

endmodule
109 changes: 109 additions & 0 deletions test/lossy_valid_to_stream_tb.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2023 ETH Zurich, University of Bologna
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.
// SPDX-License-Identifier: SHL-0.51
//-----------------------------------------------------------------------------

// Author: Manuel Eggimann <[email protected]>

module lossy_valid_to_stream_tb #(
/// Theu number of requests to simulate
parameter int unsigned NumReq = 32'd10000,
/// Clock cycle time.
parameter time CyclTime = 20ns
);

logic clk;
logic rst_n;

localparam type payload_t = logic [$clog2(NumReq)-1:0];



// clock generator
clk_rst_gen #(
.ClkPeriod (CyclTime),
.RstClkCycles(5)
) i_clk_rst_gen (
.clk_o (clk),
.rst_no(rst_n)
);

typedef stream_test::stream_driver#(
.payload_t(payload_t),
.TA(CyclTime * 0.2),
.TT(CyclTime * 0.8)
) stream_driver_in_t;

STREAM_DV #(.payload_t(payload_t)) dut_in (.clk_i(clk));
stream_driver_in_t stream_source = new(dut_in);

typedef stream_test::stream_driver#(
.payload_t(payload_t),
.TA(CyclTime * 0.2),
.TT(CyclTime * 0.8)
) stream_driver_out_t;
STREAM_DV #(.payload_t(payload_t)) dut_out (.clk_i(clk));
stream_driver_out_t stream_sink = new(dut_out);


payload_t payload_queue[$];
logic is_busy;

assign dut_in.ready = 1'b1;
lossy_valid_to_stream #(
.T(payload_t)
) i_lossy_valid_to_stream (
.clk_i (clk),
.rst_ni (rst_n),
.valid_i(dut_in.valid),
.data_i (dut_in.data),
.data_o (dut_out.data),
.valid_o(dut_out.valid),
.ready_i(dut_out.ready),
.busy_o(is_busy)
);

initial begin : apply_stimuli
automatic int unsigned wait_cycl;

@(posedge rst_n);
stream_source.reset_in();
for (int i = 0; i < NumReq; i++) begin
wait_cycl = $urandom_range(0, 5);
repeat (wait_cycl) @(posedge clk);
stream_source.send(i);
if (payload_queue.size() == 2 && !dut_out.ready)
payload_queue[0] = i;
else
payload_queue.push_front(i);
end
$stop();
end

initial begin : receive_responses
automatic payload_t data;
automatic payload_t expected_data;
automatic int unsigned wait_cycl;
@(posedge rst_n);
stream_sink.reset_out();
forever begin
wait_cycl = $urandom_range(0, 5);
repeat (wait_cycl) @(posedge clk);
stream_sink.recv(data);
assert (payload_queue.size() > 0) else
$error("Receieved transaction at output even though the input side did not send any new data.");
expected_data = payload_queue.pop_back();
assert (data == expected_data) else
$error("Received the wrong data.x Was %d instead of %d", data, expected_data);
end
end

endmodule