From 8a80d9bcccfa95f982537ca5b89c8d0b77214546 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 08:48:42 +0200 Subject: [PATCH 01/21] Added time-based redundancy modules - time-based TMR module and TB. - time-based DMR module and TB. - time-based Retry module and tb and combined DMR/Retry TB. - Lockable RR-Arbiter and combined TMR/Lock-RR TB and DMR/Retry/Lock-RR TB. --- Bender.yml | 20 +- rtl/time_redundancy/retry_end.sv | 44 +++ rtl/time_redundancy/retry_start.sv | 97 ++++++ rtl/time_redundancy/rr_arb_tree_lock.sv | 389 ++++++++++++++++++++++++ rtl/time_redundancy/time_DMR_end.sv | 269 ++++++++++++++++ rtl/time_redundancy/time_DMR_start.sv | 120 ++++++++ rtl/time_redundancy/time_TMR_end.sv | 294 ++++++++++++++++++ rtl/time_redundancy/time_TMR_start.sv | 115 +++++++ rtl/time_redundancy/voters.svh | 27 ++ run_tests.sh | 11 + src_files.yml | 8 + test/tb_retry.sv | 196 ++++++++++++ test/tb_rr_arb_tree_lock.sv | 281 +++++++++++++++++ test/tb_time_dmr.sv | 260 ++++++++++++++++ test/tb_time_dmr_retry.sv | 339 +++++++++++++++++++++ test/tb_time_dmr_retry_lock.sv | 266 ++++++++++++++++ test/tb_time_dmr_retry_lock_dut.sv | 276 +++++++++++++++++ test/tb_time_tmr.sv | 231 ++++++++++++++ test/tb_time_tmr_lock.sv | 219 +++++++++++++ test/tb_time_tmr_lock_dut.sv | 198 ++++++++++++ 20 files changed, 3659 insertions(+), 1 deletion(-) create mode 100644 rtl/time_redundancy/retry_end.sv create mode 100644 rtl/time_redundancy/retry_start.sv create mode 100644 rtl/time_redundancy/rr_arb_tree_lock.sv create mode 100644 rtl/time_redundancy/time_DMR_end.sv create mode 100644 rtl/time_redundancy/time_DMR_start.sv create mode 100644 rtl/time_redundancy/time_TMR_end.sv create mode 100644 rtl/time_redundancy/time_TMR_start.sv create mode 100644 rtl/time_redundancy/voters.svh create mode 100644 test/tb_retry.sv create mode 100644 test/tb_rr_arb_tree_lock.sv create mode 100644 test/tb_time_dmr.sv create mode 100644 test/tb_time_dmr_retry.sv create mode 100644 test/tb_time_dmr_retry_lock.sv create mode 100644 test/tb_time_dmr_retry_lock_dut.sv create mode 100644 test/tb_time_tmr.sv create mode 100644 test/tb_time_tmr_lock.sv create mode 100644 test/tb_time_tmr_lock_dut.sv diff --git a/Bender.yml b/Bender.yml index 94c17537..aa010a8b 100644 --- a/Bender.yml +++ b/Bender.yml @@ -25,14 +25,22 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_enc.sv - rtl/hsiao_ecc/hsiao_ecc_dec.sv - rtl/hsiao_ecc/hsiao_ecc_cor.sv + - rtl/time_redundancy/retry_end.sv + - rtl/time_redundancy/retry_start.sv + - rtl/time_redundancy/rr_arb_tree_lock.sv + - rtl/time_redundancy/voters.svh - rtl/TMR_voter.sv - rtl/TMR_voter_fail.sv - rtl/TMR_word_voter.sv # Level 1 - rtl/ODRG_unit/odrg_manager_reg_top.sv - rtl/ecc_wrap/ecc_manager_reg_top.sv - - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv + - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv + - rtl/time_redundancy/time_TMR_end.sv + - rtl/time_redundancy/time_TMR_start.sv + - rtl/time_redundancy/time_DMR_end.sv + - rtl/time_redundancy/time_DMR_start.sv - target: any(deprecated, axi_ecc, hci_ecc, pulp_ecc, test) files: @@ -99,6 +107,16 @@ sources: - test/tb_bitwise_tmr_voter.sv - test/tb_bitwise_tmr_voter_fail.sv - test/tb_voter_macros.sv + - test/tb_time_tmr.sv + - test/tb_time_dmr.sv + - test/tb_retry.sv + - test/tb_time_dmr_retry.sv + - test/tb_time_tmr_lock_dut.sv + - test/tb_time_tmr_lock.sv + - test/tb_time_dmr_retry_lock_dut.sv + - test/tb_time_dmr_retry_lock.sv + - test/tb_rr_arb_tree_lock.sv + vendor_package: - name: lowrisc_opentitan diff --git a/rtl/time_redundancy/retry_end.sv b/rtl/time_redundancy/retry_end.sv new file mode 100644 index 00000000..c5565ebd --- /dev/null +++ b/rtl/time_redundancy/retry_end.sv @@ -0,0 +1,44 @@ +module retry_end # ( + parameter type DataType = logic, + parameter IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input DataType data_i, + input logic [IDSize-1:0] id_i, + input logic needs_retry_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic valid_o, + input logic ready_i, + + // Retry Connection + output logic [IDSize-1:0] retry_id_o, + output logic retry_valid_o, + input logic retry_ready_i +); + + // Assign signals + assign retry_id_o = id_i; + assign data_o = data_i; + + always_comb begin + if (needs_retry_i) begin + retry_valid_o = valid_i; + ready_o = retry_ready_i; + valid_o = 0; + end else begin + valid_o = valid_i; + ready_o = ready_i; + retry_valid_o = 0; + end + end + +endmodule + + diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv new file mode 100644 index 00000000..3b43f64b --- /dev/null +++ b/rtl/time_redundancy/retry_start.sv @@ -0,0 +1,97 @@ +`include "common_cells/registers.svh" +`include "voters.svh" + +module retry_start # ( + parameter type DataType = logic, + parameter IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input DataType data_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic [IDSize-1:0] id_o, + output logic valid_o, + input logic ready_i, + + // Retry Connection + input logic [IDSize-1:0] retry_id_i, + input logic retry_valid_i, + output logic retry_ready_o +); + + ////////////////////////////////////////////////////////////////////// + // Register to store failed id for one cycle + logic [IDSize-1:0] failed_id_d, failed_id_q; + logic failed_valid_d, failed_valid_q; + + always_comb begin + if (ready_i | retry_valid_i) begin + failed_valid_d = retry_valid_i; + end else begin + failed_valid_d = failed_valid_q; + end + + if (retry_valid_i & retry_ready_o) begin + failed_id_d = retry_id_i; + end else begin + failed_id_d = failed_id_q; + end + end + + `FF(failed_id_q, failed_id_d, '0); + `FF(failed_valid_q, failed_valid_d, '0); + + assign retry_ready_o = ready_i | ~failed_valid_q; + + ////////////////////////////////////////////////////////////////////// + // ID Counter with parity bit + + logic [IDSize-1:0] counter_id_d, counter_id_q; + + always_comb begin + if ((failed_valid_q | valid_i) & ready_i) begin + `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); + end else begin + counter_id_d = counter_id_q; + end + end + + `FF(counter_id_q, counter_id_d, 0); + + ////////////////////////////////////////////////////////////////////// + // General Element storage + + logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; + + always_comb begin + // Keep data as is as abase + data_storage_d = data_storage_q; + + if ((failed_valid_q | valid_i) & ready_i) begin + data_storage_d[counter_id_q[IDSize-2:0]] = data_o; + end + end + + `FF(data_storage_q, data_storage_d, 0); + + always_comb begin + if (failed_valid_q & ready_i) begin + data_o = data_storage_q[failed_id_q[IDSize-2:0]]; + end else begin + data_o = data_i; + end + id_o = counter_id_q; + end + + ////////////////////////////////////////////////////////////////////// + // Handshake assignment + assign ready_o = ready_i & !failed_valid_q; + assign valid_o = valid_i | failed_valid_q; + +endmodule diff --git a/rtl/time_redundancy/rr_arb_tree_lock.sv b/rtl/time_redundancy/rr_arb_tree_lock.sv new file mode 100644 index 00000000..44bc510f --- /dev/null +++ b/rtl/time_redundancy/rr_arb_tree_lock.sv @@ -0,0 +1,389 @@ +// Copyright 2019 ETH Zurich and 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. +// +// Author: Michael Schaffner , ETH Zurich +// Wolfgang Roenninger , ETH Zurich +// Maurus Item , ETH Zurich +// Date: 10.04.2023 +// Description: logarithmic arbitration tree with round robin arbitration scheme +// that can be externally locked to not arbitrate. + +/// The rr_arb_tree employs non-starving round robin-arbitration - i.e., the priorities +/// rotate each cycle. +/// +/// ## Fair vs. unfair Arbitration +/// +/// This refers to fair throughput distribution when not all inputs have active requests. +/// This module has an internal state `rr_q` which defines the highest priority input. (When +/// `ExtPrio` is `1'b1` this state is provided from the outside.) The arbitration tree will +/// choose the input with the same index as currently defined by the state if it has an active +/// request. Otherwise a *random* other active input is selected. The parameter `FairArb` is used +/// to distinguish between two methods of calculating the next state. +/// * `1'b0`: The next state is calculated by advancing the current state by one. This leads to the +/// state being calculated without the context of the active request. Leading to an +/// unfair throughput distribution if not all inputs have active requests. +/// * `1'b1`: The next state jumps to the next unserved request with higher index. +/// This is achieved by using two trailing-zero-counters (`lzc`). The upper has the masked +/// `req_i` signal with all indices which will have a higher priority in the next state. +/// The trailing zero count defines the input index with the next highest priority after +/// the current one is served. When the upper is empty the lower `lzc` provides the +/// wrapped index if there are outstanding requests with lower or same priority. +/// The implication of throughput fairness on the module timing are: +/// * The trailing zero counter (`lzc`) has a loglog relation of input to output timing. This means +/// that in this module the input to register path scales with Log(Log(`NumIn`)). +/// * The `rr_arb_tree` data multiplexing scales with Log(`NumIn`). This means that the input to output +/// timing path of this module also scales scales with Log(`NumIn`). +/// This implies that in this module the input to output path is always longer than the input to +/// register path. As the output data usually also terminates in a register the parameter `FairArb` +/// only has implications on the area. When it is `1'b0` a static plus one adder is instantiated. +/// If it is `1'b1` two `lzc`, a masking logic stage and a two input multiplexer are instantiated. +/// However these are small in respect of the data multiplexers needed, as the width of the `req_i` +/// signal is usually less as than `DataWidth`. +module rr_arb_tree_lock #( + /// Number of inputs to be arbitrated. + parameter int unsigned NumIn = 64, + /// Data width of the payload in bits. Not needed if `DataType` is overwritten. + parameter int unsigned DataWidth = 32, + /// Data type of the payload, can be overwritten with custom type. Only use of `DataWidth`. + parameter type DataType = logic [DataWidth-1:0], + /// The `ExtPrio` option allows to override the internal round robin counter via the + /// `rr_i` signal. This can be useful in case multiple arbiters need to have + /// rotating priorities that are operating in lock-step. If static priority arbitration + /// is needed, just connect `rr_i` to '0. + /// + /// Set to 1'b1 to enable. + parameter bit ExtPrio = 1'b0, + /// If `AxiVldRdy` is set, the req/gnt signals are compliant with the AXI style vld/rdy + /// handshake. Namely, upstream vld (req) must not depend on rdy (gnt), as it can be deasserted + /// again even though vld is asserted. Enabling `AxiVldRdy` leads to a reduction of arbiter + /// delay and area. + /// + /// Set to `1'b1` to treat req/gnt as vld/rdy. + parameter bit AxiVldRdy = 1'b0, + /// The `LockIn` option prevents the arbiter from changing the arbitration + /// decision when the arbiter is disabled. I.e., the index of the first request + /// that wins the arbitration will be locked in case the destination is not + /// able to grant the request in the same cycle. + /// + /// Set to `1'b1` to enable. + parameter bit LockIn = 1'b0, + /// When set, ensures that throughput gets distributed evenly between all inputs. + /// + /// Set to `1'b0` to disable. + parameter bit FairArb = 1'b1, + /// Dependent parameter, do **not** overwrite. + /// Width of the arbitration priority signal and the arbitrated index. + parameter int unsigned IdxWidth = (NumIn > 32'd1) ? unsigned'($clog2(NumIn)) : 32'd1, + /// Dependent parameter, do **not** overwrite. + /// Type for defining the arbitration priority and arbitrated index signal. + parameter type idx_t = logic [IdxWidth-1:0] +) ( + /// Clock, positive edge triggered. + input logic clk_i, + /// Asynchronous reset, active low. + input logic rst_ni, + /// Clears the arbiter state. Only used if `ExtPrio` is `1'b0` or `LockIn` is `1'b1`. + input logic flush_i, + /// External round-robin priority. + input idx_t rr_i, + /// lock idx signal, only used if Only used if `ExtPrio` is `1'b0.` + input logic lock_rr_i, + /// Input requests arbitration. + input logic [NumIn-1:0] req_i, + /* verilator lint_off UNOPTFLAT */ + /// Input request is granted. + output logic [NumIn-1:0] gnt_o, + /* verilator lint_on UNOPTFLAT */ + /// Input data for arbitration. + input DataType [NumIn-1:0] data_i, + /// Output request is valid. + output logic req_o, + /// Output request is granted. + input logic gnt_i, + /// Output data. + output DataType data_o, + /// Index from which input the data came from. + output idx_t idx_o +); + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + // Default SVA reset + default disable iff (!rst_ni || flush_i); + `endif + `endif + // pragma translate_on + + // just pass through in this corner case + if (NumIn == unsigned'(1)) begin : gen_pass_through + assign req_o = req_i[0]; + assign gnt_o[0] = gnt_i; + assign data_o = data_i[0]; + assign idx_o = '0; + // non-degenerate cases + end else begin : gen_arbiter + localparam int unsigned NumLevels = unsigned'($clog2(NumIn)); + + /* verilator lint_off UNOPTFLAT */ + idx_t [2**NumLevels-2:0] index_nodes; // used to propagate the indices + DataType [2**NumLevels-2:0] data_nodes; // used to propagate the data + logic [2**NumLevels-2:0] gnt_nodes; // used to propagate the grant to masters + logic [2**NumLevels-2:0] req_nodes; // used to propagate the requests to slave + /* lint_off */ + idx_t rr_q; + logic [NumIn-1:0] req_d; + + // the final arbitration decision can be taken from the root of the tree + assign req_o = req_nodes[0]; + assign data_o = data_nodes[0]; + assign idx_o = index_nodes[0]; + + if (ExtPrio) begin : gen_ext_rr + assign rr_q = rr_i; + assign req_d = req_i; + end else begin : gen_int_rr + idx_t rr_d; + + // lock arbiter decision in case we got at least one req and no acknowledge + if (LockIn) begin : gen_lock + logic lock_d, lock_q; + logic [NumIn-1:0] req_q; + + assign lock_d = req_o & ~gnt_i; + assign req_d = (lock_q) ? req_q : req_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg + if (!rst_ni) begin + lock_q <= '0; + end else begin + if (flush_i) begin + lock_q <= '0; + end else begin + lock_q <= lock_d; + end + end + end + + // pragma translate_off + `ifndef VERILATOR + lock: assert property( + @(posedge clk_i) LockIn |-> req_o && !gnt_i |=> idx_o == $past(idx_o)) else + $fatal (1, "Lock implies same arbiter decision in next cycle if output is not \ + ready."); + + logic [NumIn-1:0] req_tmp; + assign req_tmp = req_q & req_i; + lock_req: assume property( + @(posedge clk_i) LockIn |-> lock_d |=> req_tmp == req_q) else + $fatal (1, "It is disallowed to deassert unserved request signals when LockIn is \ + enabled."); + `endif + // pragma translate_on + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_req_regs + if (!rst_ni) begin + req_q <= '0; + end else begin + if (flush_i) begin + req_q <= '0; + end else begin + req_q <= req_d; + end + end + end + end else begin : gen_no_lock + assign req_d = req_i; + end + + idx_t next_idx; + + if (FairArb) begin : gen_fair_arb + logic [NumIn-1:0] upper_mask, lower_mask; + idx_t upper_idx, lower_idx; + logic upper_empty, lower_empty; + + for (genvar i = 0; i < NumIn; i++) begin : gen_mask + assign upper_mask[i] = (i > rr_q) ? req_d[i] : 1'b0; + assign lower_mask[i] = (i <= rr_q) ? req_d[i] : 1'b0; + end + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_upper ( + .in_i ( upper_mask ), + .cnt_o ( upper_idx ), + .empty_o ( upper_empty ) + ); + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_lower ( + .in_i ( lower_mask ), + .cnt_o ( lower_idx ), + .empty_o ( /*unused*/ ) + ); + + assign next_idx = upper_empty ? lower_idx : upper_idx; + end else begin : gen_unfair_arb + assign next_idx = ((rr_q == idx_t'(NumIn-1)) ? '0 : rr_q + 1'b1); + end + + always_comb begin + if (lock_rr_i) begin + rr_d = idx_o; + end else begin + if (gnt_i && req_o) begin + rr_d = next_idx; + end else begin + rr_d = rr_q; + end + end + end + + // this holds the highest priority + always_ff @(posedge clk_i or negedge rst_ni) begin : p_rr_regs + if (!rst_ni) begin + rr_q <= '0; + end else begin + if (flush_i) begin + rr_q <= '0; + end else begin + rr_q <= rr_d; + end + end + end + + end + + logic lock_rr_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg + if (!rst_ni) begin + lock_rr_q <= '1; + end else begin + if (flush_i) begin + lock_rr_q <= '0; + end else begin + lock_rr_q <= lock_rr_i; + end + end + end + + assign gnt_nodes[0] = gnt_i; + + // arbiter tree + for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : gen_levels + for (genvar l = 0; l < 2**level; l++) begin : gen_level + // local select signal + logic sel; + // index calcs + localparam int unsigned Idx0 = 2**level-1+l;// current node + localparam int unsigned Idx1 = 2**(level+1)-1+l*2; + ////////////////////////////////////////////////////////////// + // uppermost level where data is fed in from the inputs + if (unsigned'(level) == NumLevels-1) begin : gen_first_level + // if two successive indices are still in the vector... + if (unsigned'(l) * 2 < NumIn-1) begin : gen_reduce + + // arbitration: round robin + always_comb begin + if (lock_rr_q) begin + sel = rr_q[NumLevels-1-level]; + end else begin + sel = ~req_d[l*2] | req_d[l*2+1] & rr_q[NumLevels-1-level]; + end + end + + assign index_nodes[Idx0] = idx_t'(sel); + assign req_nodes[Idx0] = (sel) ? req_d[l*2+1] : req_d[l*2] ; + assign data_nodes[Idx0] = (sel) ? data_i[l*2+1] : data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]) & ~sel; + assign gnt_o[l*2+1] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel; + end + // if only the first index is still in the vector... + if (unsigned'(l) * 2 == NumIn-1) begin : gen_first + assign req_nodes[Idx0] = req_d[l*2]; + assign index_nodes[Idx0] = '0;// always zero in this case + assign data_nodes[Idx0] = data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]); + end + // if index is out of range, fill up with zeros (will get pruned) + if (unsigned'(l) * 2 > NumIn-1) begin : gen_out_of_range + assign req_nodes[Idx0] = 1'b0; + assign index_nodes[Idx0] = idx_t'('0); + assign data_nodes[Idx0] = DataType'('0); + end + ////////////////////////////////////////////////////////////// + // general case for other levels within the tree + end else begin : gen_other_levels + + // arbitration: round robin + always_comb begin + if (lock_rr_q) begin + sel = rr_q[NumLevels-1-level]; + end else begin + sel = ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[NumLevels-1-level]; + end + end + + assign index_nodes[Idx0] = (sel) ? + idx_t'({1'b1, index_nodes[Idx1+1][NumLevels-unsigned'(level)-2:0]}) : + idx_t'({1'b0, index_nodes[Idx1][NumLevels-unsigned'(level)-2:0]}); + + assign req_nodes[Idx0] = (sel) ? req_nodes[Idx1+1] : req_nodes[Idx1]; + assign data_nodes[Idx0] = (sel) ? data_nodes[Idx1+1] : data_nodes[Idx1]; + assign gnt_nodes[Idx1] = gnt_nodes[Idx0] & ~sel; + assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel; + end + ////////////////////////////////////////////////////////////// + end + end + + // pragma translate_off + `ifndef VERILATOR + `ifndef XSIM + initial begin : p_assert + assert(NumIn) + else $fatal(1, "Input must be at least one element wide."); + assert(!(LockIn && ExtPrio)) + else $fatal(1,"Cannot use LockIn feature together with external ExtPrio."); + end + + hot_one : assert property( + @(posedge clk_i) $onehot0(gnt_o)) + else $fatal (1, "Grant signal must be hot1 or zero."); + + gnt0 : assert property( + @(posedge clk_i) |gnt_o |-> gnt_i) + else $fatal (1, "Grant out implies grant in."); + + gnt1 : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> |gnt_o) + else $fatal (1, "Req out and grant in implies grant out."); + + gnt_idx : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> gnt_o[idx_o]) + else $fatal (1, "Idx_o / gnt_o do not match."); + + req1 : assert property( + @(posedge clk_i) req_o |-> |req_i) + else $fatal (1, "Req out implies req in."); + + lock2 : assert property( + @(posedge clk_i) disable iff (!rst_ni) lock_rr_q |-> idx_o == $past(idx_o)) + else $error (1, "Lock means idx_o does not change."); + `endif + `endif + // pragma translate_on + end + +endmodule : rr_arb_tree_lock diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv new file mode 100644 index 00000000..2de4514d --- /dev/null +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -0,0 +1,269 @@ +`include "voters.svh" +`include "common_cells/registers.svh" + +module time_DMR_end # ( + // The data type you want to send through / replicate + parameter type DataType = logic, + // How long until the time_TMR module should abort listening for further copies + // of some data when they don't show up e.g. handshake failed + // For an in-order process you should set this to 4 + // For an out-of-order process (with RR Arbitrator) you should set it to 5 + // In case your combinatorial logic takes more than once cycle to compute an element + // multiply by the respective number of cycles + parameter int unsigned LockTimeout = 4, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1 + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with time_TMR_start! + parameter int unsigned IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + input logic enable_i, + + // Direct connection to corresponding time_DMR_start module + input logic [IDSize-1:0] next_id_i, + + // Upstream connection + input DataType data_i, + input logic [IDSize-1:0] id_i, + input logic valid_i, + output logic ready_o, + + // Signal for working with upstream Lockable RR Arbiter + output logic lock_o, + + // Downstream connection + output DataType data_o, + output logic [IDSize-1:0] id_o, + output logic needs_retry_o, + output logic valid_o, + input logic ready_i, + + // Output Flags for Error Counting + output logic fault_detected_o +); + ///////////////////////////////////////////////////////////////////////////////// + // Storage of incomming results and generating good output data + + DataType data_q; + logic [IDSize-1:0] id_q; + + // Next State Combinatorial Logic + logic load_enable; + assign load_enable = valid_i & ready_o & enable_i; + + // Storage Element + `FFL(data_q, data_i, load_enable, 'h1); + `FFL(id_q, id_i, load_enable, 'h1); + + ///////////////////////////////////////////////////////////////////////////////// + // Comparisons genereration for Handshake / State Machine + + logic [1:0] data_same, id_same, full_same, partial_same; + logic data_same_in, id_same_in; + logic data_same_q, id_same_q; + + always_comb begin: data_same_generation_comb + data_same_in = '0; + id_same_in = '0; + + // If disabled just send out input + if (!enable_i) begin + data_o = data_i; + id_o = id_i; + end else begin + data_o = data_q; + id_o = id_q; + end + + if (data_i == data_q) data_same_in = '1; + if (id_i == id_q) id_same_in = '1; + end + + + ///////////////////////////////////////////////////////////////////////////////// + // Storage of same / not same for one extra cycle + + `FFL(data_same_q, data_same_in, load_enable, 1'b1); + `FFL(id_same_q, id_same_in, load_enable, 1'b1); + + // Output (merged) signal assigment + assign data_same = {data_same_q, data_same_in}; + assign id_same = {id_same_q, id_same_in}; + + assign full_same = data_same & id_same; + assign partial_same = data_same | id_same; + + ///////////////////////////////////////////////////////////////////////////////// + // Logic to find out what we should do with our data based on same / not same + + logic new_element_arrived; + logic data_usable; + + // Flag Combinatorial Logic + always_comb begin : data_flags_generation_comb + // Some new element just showed up and we need to send data outwards again. + new_element_arrived = (id_same == 2'b01) || (id_same == 2'b00); + + // Data has at least two new things that are the same + data_usable = data_same[0]; + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // State machine to figure out handshake + + typedef enum logic [0:0] {BASE, WAIT_FOR_READY} state_t; + state_t state_d, state_q; + logic valid_internal, lock_internal; + + // Special State Description: + // Wait for Ready: We got some data that is usable, but downstream can't use it yet + // -> We keep shifting as far as our pipeline goes to collect all data samples if we haven't yet and then stop + // Wait for Valid: We got some data that is usable, and sent it downstream right away + // -> We try to collect one more piece of our data and then move on + + + // Next State Combinatorial Logic + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_d = state_q; + + case (state_q) + BASE: + if (valid_i) begin + if (new_element_arrived) begin + if (!ready_i) begin + state_d = WAIT_FOR_READY; + end + end + end + WAIT_FOR_READY: + if (ready_i) begin + state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state + end + endcase + end + + + // State Storage + `FF(state_q, state_d, BASE); + + // Output Combinatorial Logic + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q) + BASE: valid_internal = valid_i & new_element_arrived; + WAIT_FOR_READY: valid_internal = valid_i; + endcase + + case (state_q) + BASE: lock_internal = !ready_i | !full_same[0]; + WAIT_FOR_READY: lock_internal = !ready_i; + endcase + + case (state_q) + BASE: ready_o = ready_i | !new_element_arrived; + WAIT_FOR_READY: ready_o = ready_i; + endcase + end else begin + valid_internal = valid_i; + lock_internal = 0; + ready_o = ready_i; + end + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // State machine to lock / unlock Arbitrator with Watchdog timer + + logic lock_d, lock_q; + logic [$clog2(LockTimeout)-1:0] counter_d, counter_q; + logic lock_reset; + + // Next State Combinatorial Logic + always_comb begin : lock_comb + + if (counter_q > LockTimeout) begin + lock_d = 0; + counter_d = 0; + lock_reset = 1; + + end else begin + lock_reset = 0; + + if (lock_q & !valid_i) begin // TODO: Add dependency on valid + counter_d = counter_q + 1; + end else begin + counter_d = 0; + end + + // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything + if (valid_i | lock_q) begin + lock_d = lock_internal; + end else begin + lock_d = lock_q; + end + end + end + + assign lock_o = lock_d; + + // State Storage + `FF(lock_q, lock_d, '0); + `FF(counter_q, counter_d, '0); + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Output deduplication based on ID and ID Faults + + logic id_fault_q; + assign id_fault_q = ^id_q; + + logic [2 ** (IDSize-1)-1:0] recently_seen_d, recently_seen_q; + + always_comb begin + recently_seen_d = recently_seen_q; + + recently_seen_d[next_id_i[IDSize-2:0]] = 0; + + if (valid_internal & ready_i & !id_fault_q) begin + recently_seen_d[id_q[IDSize-2:0]] = 1; + end + end + + // State Storage + `FF(recently_seen_q, recently_seen_d, ~'0); + + always_comb begin + if (enable_i) begin + if (id_fault_q | recently_seen_q[id_q[IDSize-2:0]] & valid_internal) begin + valid_o = 0; + end else begin + valid_o = valid_internal; + end + needs_retry_o = id_same[0] & !data_usable; + end else begin + valid_o = valid_internal; + needs_retry_o = 0; + end + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Build error flag + + // Since we can sometimes output data that only showed up for one cycle e.g. when another id was faulty + // or handshake failed and are sure it does not need retry, but a fault occured anyway + // We can not us the need_retry_o signal to signify all faults. Instead we have a special signal + // In a non-error case, we should have a full same signal every other cycle + // So if that is not the case we had a fault. + + logic fault_detected_d, fault_detected_q; + + assign fault_detected_d = ~|full_same[1:0]; + + `FF(fault_detected_q, fault_detected_d, '0); + + assign fault_detected_o = fault_detected_d & !fault_detected_q; + +endmodule diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv new file mode 100644 index 00000000..50138ac3 --- /dev/null +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -0,0 +1,120 @@ +`include "voters.svh" +`include "common_cells/registers.svh" + +module time_DMR_start # ( + // The data type you want to send through / replicate + parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1 + // For an out of order process, it needs to be big enough so that the + // out-of-orderness can never rearange the elements with the same id + // next to each other and needs an extra bit for error detection. + // As an estimate you can use log2(longest_pipeline) + 2. + // Needs to match with time_TMR_end! + parameter int unsigned IDSize = 1, + // Set to 1 if the id_i port should be used + parameter bit UseExternalId = 0 +) ( + input logic clk_i, + input logic rst_ni, + input logic enable_i, + + // Direct connection + output logic [IDSize-1:0] next_id_o, + + // Upstream connection + input DataType data_i, + input logic [IDSize-1:0] id_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic [IDSize-1:0] id_o, + output logic valid_o, + input logic ready_i +); + + // State machine TLDR + // - counting the state from 0 to 2 if the handshake is good + // - counting the ID up whenever the state goes back to 0 + + // Next State Combinatorial Logic + typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE} state_t; + state_t state_d, state_q; + DataType data_d, data_q; + logic [IDSize-1:0] id_d, id_q; + logic [IDSize-2:0] next_id_o_noparity; + + always_comb begin : next_state_logic + // Default to staying in same state + state_d = state_q; + data_d = data_q; + id_d = id_q; + + next_id_o_noparity = id_q[IDSize-2:0] + 1; + if (UseExternalId == 1) begin + next_id_o = id_i; + end else begin + next_id_o = {^next_id_o_noparity, next_id_o_noparity}; + end + + case (state_q) + STORE_AND_SEND: + if (valid_i) begin + data_d = data_i; + id_d = next_id_o; + + if (ready_i) begin + if (enable_i) begin + state_d = REPLICATE; + end else begin + state_d = STORE_AND_SEND; + end + end else begin + state_d = SEND; + end + end + SEND: + if (ready_i) begin + if (enable_i) begin + state_d = REPLICATE; // Reset seqeuence + end else begin + state_d = STORE_AND_SEND; + end + end + REPLICATE: + if (ready_i) begin + state_d = STORE_AND_SEND; + end + endcase + end + + // State Storage + `FF(state_q, state_d, STORE_AND_SEND); + `FF(data_q, data_d, '0); + `FF(id_q, id_d, '0); + + // Output Combinatorial Logic + always_comb begin : output_logic + case (state_q) + STORE_AND_SEND: begin + valid_o = valid_i; + ready_o = 1; + end + SEND: begin + valid_o = '1; + ready_o = '0; + end + REPLICATE: begin + valid_o = '1; + ready_o = '0; + end + endcase + end + + // Output Voting Logic + assign data_o = data_d; + assign id_o = id_d; + +endmodule diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv new file mode 100644 index 00000000..7f85a2ed --- /dev/null +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -0,0 +1,294 @@ +`include "voters.svh" +`include "common_cells/registers.svh" + +module time_TMR_end # ( + // The data type you want to send through / replicate + parameter type DataType = logic, + // How long until the time_TMR module should abort listening for further copies + // of some data when they don't show up e.g. handshake failed + // For an in-order process you should set this to 4 + // For an out-of-order process (with RR Arbitrator) you should set it to 5 + // In case your combinatorial logic takes more than once cycle to compute an element + // multiply by the respective number of cycles + parameter int unsigned LockTimeout = 4, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1 + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with time_TMR_start! + parameter int unsigned IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + input logic enable_i, + + // Upstream connection + input DataType data_i, + input logic [IDSize-1:0] id_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic valid_o, + input logic ready_i, + + // Signal for working with Lockable RR Arbiter + output logic lock_o, + + // Flag for External Error Counter + output logic fault_detected_o +); + ///////////////////////////////////////////////////////////////////////////////// + // Storage of incomming results and generating good output data + + DataType data_q1, data_q2; + logic [IDSize-1:0] id_q1, id_q2; + logic load_enable; + assign load_enable = valid_i & ready_o & enable_i; + + // Storage Element + `FFL(data_q1, data_i, load_enable, 'h1); + `FFL(data_q2, data_q1, load_enable, 'h2); + `FFL(id_q1, id_i, load_enable, 'h1); + `FFL(id_q2, id_q1, load_enable, 'h2); + + ///////////////////////////////////////////////////////////////////////////////// + // Comparisons genereration for Handshake / State Machine + + logic [4:0] data_same, id_same, full_same, partial_same; + logic [2:0] data_same_in, id_same_in; + logic [1:0] data_same_d, data_same_q, id_same_d, id_same_q; + + always_comb begin: data_same_generation_comb + data_same_in = '0; + id_same_in = '0; + data_o = '0; + + // Slot 0 and 1 are filled with the same data (slot 2 might be different) + if (data_i == data_q1) begin + data_o = data_i; + data_same_in[0] = '1; + end + + // Slot 0 and 2 are filled with the same data (slot 1 might be different) + // This deals with cases where the second id got corrupted + if (data_i == data_q2) begin + data_o = data_i; + data_same_in[1] = '1; + end + + // Slot 1 and 2 are filled with the same data (slot 0 might be different) + if (data_q1 == data_q2) begin + data_o = data_q1; + data_same_in[2] = '1; + end + + // If disabled just send out input + if (!enable_i) begin + data_o = data_i; + end + + // Same kind of logic for id signal + if (id_i == id_q1) id_same_in[0] = '1; + if (id_i == id_q2) id_same_in[1] = '1; + if (id_q1 == id_q2) id_same_in[2] = '1; + end + + ///////////////////////////////////////////////////////////////////////////////// + // Storage of same / not same for one extra cycle + + // Next State Combinatorial Logic + always_comb begin : data_same_storage_comb + data_same_d = data_same_in[2:1]; + id_same_d = id_same_in[2:1]; + end + + // Storage Element + `FFL(data_same_q, data_same_d, load_enable, 2'b11); + `FFL(id_same_q, id_same_d, load_enable, 2'b11); + + // Output (merged) signal assigment + assign data_same = {data_same_q, data_same_in}; + assign id_same = {id_same_q, id_same_in}; + + assign full_same = data_same & id_same; + assign partial_same = data_same | id_same; + + ///////////////////////////////////////////////////////////////////////////////// + // Logic to find out what we should do with our data based on same / not same + + logic element_needs_shift; + logic new_element_arrived; + logic element_in_input; + logic element_relies_on_input; + logic data_usable; + + // Flag Combinatorial Logic + always_comb begin : data_flags_generation_comb + // Some new element just showed up and we need to send data outwards again. + new_element_arrived = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! + (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair + (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair + ); + + // Data or id is in the input -> We should consume the input for this element + // Same data or id count as matches since we can remove an inexact pair on error and be fine + // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) + element_in_input = |partial_same[1:0]; + + // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is + element_needs_shift = ~|full_same[2:1]; + + // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o + element_relies_on_input = |full_same[1:0] & ~full_same[2]; + + // Data has at least two new things that are the same + data_usable = |data_same[2:0]; + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // State machine to figure out handshake + + typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_DATA} state_t; + state_t state_d, state_q; + logic lock; + + // Special State Description: + // Wait for Ready: We got some data that is usable, but downstream can't use it yet + // -> We keep shifting as far as our pipeline goes to collect all data samples if we haven't yet and then stop + // Wait for Valid: We got some data that is usable, and sent it downstream right away + // -> We try to collect one more piece of our data and then move on + + // Next State Combinatorial Logic + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_d = state_q; + + case (state_q) + BASE: + if (valid_i) begin + if (new_element_arrived) begin + if (!data_usable) begin + state_d = WAIT_FOR_DATA; + end else begin + if (ready_i) begin + if (element_needs_shift) begin + // We can already send our data element, but it needs another shift to collect -> Go into special stat for this + state_d = WAIT_FOR_VALID; + end + end else begin + state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + end + end + WAIT_FOR_READY: + if (ready_i) begin + state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state + end + WAIT_FOR_VALID: begin + if (valid_i) begin + state_d = BASE; // We needed another shift to get back into base state + end + end + WAIT_FOR_DATA: begin + if (valid_i) begin + // We got another shift to get our redundant data completed + if (ready_i) begin + state_d = BASE; // And we send it on to the next stage immediately + end else begin + state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + end + endcase + end + + // State Storage + `FF(state_q, state_d, WAIT_FOR_VALID); + + // Output Combinatorial Logic + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q) + BASE: valid_o = (!element_relies_on_input | valid_i) & data_usable & new_element_arrived; + WAIT_FOR_DATA: valid_o = (!element_relies_on_input | valid_i) & data_usable; + WAIT_FOR_READY: valid_o = (!element_relies_on_input | valid_i); + WAIT_FOR_VALID: valid_o = 0; + endcase + + case (state_q) + BASE: lock = !ready_i | element_needs_shift | !new_element_arrived; + WAIT_FOR_DATA: lock = !ready_i | element_needs_shift; + WAIT_FOR_READY: lock = !ready_i; + WAIT_FOR_VALID: lock = !valid_i; + endcase + + case (state_q) + BASE: ready_o = ready_i & element_in_input | element_needs_shift | !new_element_arrived; + WAIT_FOR_DATA: ready_o = ready_i & element_in_input | element_needs_shift; + WAIT_FOR_READY: ready_o = ready_i & element_in_input | element_needs_shift; + WAIT_FOR_VALID: ready_o = element_in_input; + endcase + end else begin + valid_o = valid_i; + lock = 0; + ready_o = ready_i; + end + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // State machine to lock / unlock Arbitrator with Watchdog timer + + logic lock_d, lock_q; + logic [$clog2(LockTimeout)-1:0] counter_d, counter_q; + + // Next State Combinatorial Logic + always_comb begin : lock_comb + if (counter_q > LockTimeout) begin + lock_d = 0; + counter_d = 0; + end else begin + + if (lock_q & !valid_i) begin // TODO: Add dependency on valid + counter_d = counter_q + 1; + end else begin + counter_d = 0; + end + + // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything + if (valid_i | lock_q) begin + lock_d = lock; + end else begin + lock_d = lock_q; + end + end + end + + // Output Logic + assign lock_o = lock_d; + + // State Storage + `FF(lock_q, lock_d, '0); + `FF(counter_q, counter_d, '0); + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Build error flag + + // Since we can already output at two same elements we could have not seen an error yet, + // so we can't rely on valid / ready to be in sync with 3 same elements! + // Instead we use the following observation: If the data comes nicely in pairs of 3 we + // always have at least two data elements that are the same. + // Make output only 1 for a signly cycle even if internal pipeline is stopped + + logic fault_detected_d, fault_detected_q; + + assign fault_detected_d = ~|full_same[2:0]; + + `FF(fault_detected_q, fault_detected_d, '0); + + assign fault_detected_o = fault_detected_d & !fault_detected_q; + +endmodule diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv new file mode 100644 index 00000000..c4efd5af --- /dev/null +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -0,0 +1,115 @@ +`include "voters.svh" +`include "common_cells/registers.svh" + +module time_TMR_start # ( + // The data type you want to send through / replicate + parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1 + // For an out of order process, it needs to be big enough so that the + // out-of-orderness can never rearange the elements with the same id + // next to each other. + // As an estimate you can use log2(longest_pipeline) + 1. + // Needs to match with time_TMR_end! + parameter int unsigned IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + input logic enable_i, + + // Upstream connection + input DataType data_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic [IDSize-1:0] id_o, + output logic valid_o, + input logic ready_i +); + + // State machine TLDR + // - counting the state from 0 to 2 if the handshake is good + // - counting the ID up whenever the state goes back to 0 + + // Next State Combinatorial Logic + typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE_ONE, REPLICATE_TWO} state_t; + state_t state_d, state_q; + DataType data_d, data_q; + logic [IDSize-1:0] id_d, id_q; + + always_comb begin : next_state_logic + // Default to staying in same state + state_d = state_q; + data_d = data_q; + id_d = id_q; + + case (state_q) + STORE_AND_SEND: + if (valid_i) begin + data_d = data_i; + id_d = id_q + 1; + + if (ready_i) begin + if (enable_i) begin + state_d = REPLICATE_ONE; + end else begin + state_d = STORE_AND_SEND; + end + end else begin + state_d = SEND; + end + end + SEND: + if (ready_i) begin + if (enable_i) begin + state_d = REPLICATE_ONE; // Reset seqeuence + end else begin + state_d = STORE_AND_SEND; + end + end + REPLICATE_ONE: + if (ready_i) begin + state_d = REPLICATE_TWO; // Reset seqeuence + end + REPLICATE_TWO: begin + if (ready_i) begin + state_d = STORE_AND_SEND; + end + end + endcase + end + + // State Storage + `FF(state_q, state_d, STORE_AND_SEND); + `FF(data_q, data_d, '0); + `FF(id_q, id_d, '0); + + // Output Combinatorial Logic + always_comb begin : output_logic + case (state_q) + STORE_AND_SEND: begin + valid_o = valid_i; + ready_o = 1; + end + SEND: begin + valid_o = '1; + ready_o = '0; + end + REPLICATE_ONE: begin + valid_o = '1; + ready_o = '0; + end + REPLICATE_TWO: begin + valid_o = '1; + ready_o = '0; + end + endcase + end + + // Output Voting Logic + assign data_o = data_d; + assign id_o = id_d; + +endmodule diff --git a/rtl/time_redundancy/voters.svh b/rtl/time_redundancy/voters.svh new file mode 100644 index 00000000..56054965 --- /dev/null +++ b/rtl/time_redundancy/voters.svh @@ -0,0 +1,27 @@ + +`define MAJORITY(a, b, c, o) \ +begin \ + o = (a & b) | (a & c) | (b & c); \ +end + +`define BITWISE(a) \ + a[$bits(a)-1:0] + +`define VOTE3to3(input_signal, output_signal) \ +always_comb begin \ + `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[0]); \ + `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[1]); \ + `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[2]); \ +end + +`define VOTE3to3ENUM(input_signal, output_signal) \ +always_comb begin \ + `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[0])); \ + `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[1])); \ + `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[2])); \ +end + +`define VOTE3to1(input_signal, output_signal) \ +always_comb begin \ + `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal); \ +end diff --git a/run_tests.sh b/run_tests.sh index 6a04ad78..4060584f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -50,3 +50,14 @@ call_vsim -GDataWidth=32 tb_ecc_secded call_vsim -GDataWidth=64 tb_ecc_secded call_vsim tb_ecc_scrubber call_vsim tb_voter_macros + +call_vsim tb_time_tmr +call_vsim tb_time_tmr_lock +call_vsim tb_time_dmr +call_vsim tb_retry +call_vsim tb_time_dmr_retry +call_vsim tb_time_dmr_retry_lock -voptargs="+acc" + +for num in 1 4 7; do + call_vsim tb_rr_arb_tree_lock -GNumInp=$num -coverage -voptargs="+acc +cover=bcesfx" -suppress vsim-3009 +done diff --git a/src_files.yml b/src_files.yml index 3e44aeea..72c50d8e 100644 --- a/src_files.yml +++ b/src_files.yml @@ -9,6 +9,9 @@ redundancy_cells: lowrisc_ecc/prim_secded_39_32_enc.sv, lowrisc_ecc/prim_secded_72_64_dec.sv, lowrisc_ecc/prim_secded_72_64_enc.sv, + rtl/time_redundancy/retry_end.sv + rtl/time_redundancy/rr_arb_tree_lock.sv + rtl/time_redundancy/voters.svh rtl/TMR_voter.sv, rtl/TMR_voter_fail.sv, rtl/TMR_word_voter, @@ -28,6 +31,11 @@ redundancy_cells: rtl/BUS_enc_dec/TCDM_XBAR_bus_ecc_enc.sv, rlt/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv, rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv, + rtl/time_redundancy/retry_start.sv + rtl/time_redundancy/time_TMR_end.sv + rtl/time_redundancy/time_TMR_start.sv + rtl/time_redundancy/time_DMR_end.sv + rtl/time_redundancy/time_DMR_start.sv rtl/TMR_voter_detect.sv, # Level 2 rtl/bitwise_TMR_voter.sv, diff --git a/test/tb_retry.sv b/test/tb_retry.sv new file mode 100644 index 00000000..d24d1f9c --- /dev/null +++ b/test/tb_retry.sv @@ -0,0 +1,196 @@ +module tb_retry; + + // Clock Parameters + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + parameter IDSize = 2; + + // Testbench signals + data_t golden_queue [$]; + data_t data_golden, data_actual; + logic error; + int error_cnt; + + // Signals for DUTS + logic clk; + logic rst_n; + + data_t data_in, data_middle, data_out; + logic valid_in, valid_middle, valid_retry, valid_out; + logic ready_in, ready_middle, ready_retry, ready_out; + logic [IDSize-1:0] id_middle, id_retry; + logic needs_retry_middle; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // DUT Instances + retry_start #( + .DataType(data_t), + .IDSize(IDSize) + ) dut_start ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_in), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_middle), + .id_o(id_middle), + .valid_o(valid_middle), + .ready_i(ready_middle), + + // Retry Connection + .retry_id_i(id_retry), + .retry_valid_i(valid_retry), + .retry_ready_o(ready_retry) + ); + + + retry_end #( + .DataType(data_t), + .IDSize(IDSize) + ) dut_end ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_middle), + .id_i(id_middle), + .needs_retry_i(needs_retry_middle), + .valid_i(valid_middle), + .ready_o(ready_middle), + + // Downstream connection + .data_o(data_out), + .valid_o(valid_out), + .ready_i(ready_out), + + // Retry Connection + .retry_id_o(id_retry), + .retry_valid_o(valid_retry), + .retry_ready_i(ready_retry) + ); + + // Data Application + initial begin + // Initialize Handshake and Data + data_in = 8'h00; + valid_in = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + valid_in <= '0; + end + + valid_in <= '1; + + data_in = $random; + golden_queue.push_back(data_in); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Drive Fault signal + initial begin + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(15, 20)) begin + @(posedge clk); + # (APPLICATION_DELAY); + needs_retry_middle = '0; + end + + @(posedge clk); + # (APPLICATION_DELAY); + needs_retry_middle = '1; + end + + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(0); + end + + + // Aquisition & Validation + initial begin + $timeformat(-9, 0, " ns", 20); + + // Initialize error metrics + error = 0; // Signal so errors can easily be scrolled to in wave + error_cnt = 0; + + // Initialize Handshake + ready_out = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + ready_out <= '0; + end + + // Set ready + ready_out <= '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = data_out; + + if (golden_queue.size() > 0) begin + data_golden = golden_queue.pop_front(); + if (data_actual != data_golden) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + error = 0; + end + end else begin + $display("[T=%t] Data %h Output when nothing was in golden queue", $time, data_actual); + error = 1; + error_cnt += 1; + end + end + end + +endmodule diff --git a/test/tb_rr_arb_tree_lock.sv b/test/tb_rr_arb_tree_lock.sv new file mode 100644 index 00000000..7efa92cf --- /dev/null +++ b/test/tb_rr_arb_tree_lock.sv @@ -0,0 +1,281 @@ +// Copyright 2020 ETH Zurich and 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. + +// Author: Wolfgang Roennigner + +/// Testbench for the module `rr_arb_tree` +module tb_rr_arb_tree_lock #( + /// Number of input streams to the DUT + parameter int unsigned NumInp = 32'd7, + /// Number of requests per input + parameter int unsigned NumReqs = 32'd20000, + /// Handshaking as in AXI + parameter bit AxiVldRdy = 1'b1, + /// Do not deassert the input request + parameter bit LockIn = 1'b1, + /// Enable fair arbitration, when disabled, no assertion for fairness + parameter bit FairArb = 1'b1 +); + + localparam time CyclTime = 10ns; + localparam time ApplTime = 2ns; + localparam time TestTime = 8ns; + + // throw an error if the measured throughput and expected throughput differ + // more than this value + localparam real error_threshold = 0.1; + + localparam int unsigned IdxWidth = (NumInp > 32'd1) ? unsigned'($clog2(NumInp)) : 32'd1; + localparam int unsigned DataWidth = 32'd45; + typedef logic [IdxWidth-1:0] idx_t; + typedef logic [DataWidth-1:0] data_t; + + // clock signal + logic clk; + logic rst_n; + logic flush; + idx_t rr, idx; + logic [NumInp-1:0] req_inp, gnt_inp, end_of_sim; + data_t [NumInp-1:0] data_inp; + logic req_oup, gnt_oup; + logic lock; + data_t data_oup; + + // clock and rst gen + clk_rst_gen #( + .ClkPeriod ( CyclTime ), + .RstClkCycles ( 5 ) + ) i_clk_rst_gen ( + .clk_o ( clk ), + .rst_no ( rst_n ) + ); + + // drive input streams + // Can not use `stream_driver`, as this also tests for requests taken away. + for (genvar i = 0; i < NumInp; i++) begin : gen_stream_gen + initial begin : proc_stream_gen + automatic data_t stimuli; + automatic int unsigned rand_wait; + + end_of_sim[i] = 1'b0; + @(posedge rst_n); + data_inp[i] = '0; + req_inp[i] = 1'b0; + + // First have them continuously active for a number of time + for (int unsigned j = 0; j < (i+1) * NumReqs; j++) begin + data_inp[i] <= #ApplTime data_t'(i); + req_inp[i] <= #ApplTime 1'b1; + @(posedge clk); + end + data_inp[i] <= #ApplTime '0; + req_inp[i] <= #ApplTime 1'b0; + + // wait until all other processes have their fixed request finished. + repeat ((NumInp-i)*NumReqs) @(posedge clk); + repeat (1000) @(posedge clk); + + // First have them continuously active for a number of time + for (int unsigned j = 0; j < (NumInp-i) * NumReqs; j++) begin + data_inp[i] <= #ApplTime data_t'(i); + req_inp[i] <= #ApplTime 1'b1; + @(posedge clk); + end + data_inp[i] <= #ApplTime '0; + req_inp[i] <= #ApplTime 1'b0; + + repeat ((1+i)*NumReqs) @(posedge clk); + repeat (1000) @(posedge clk); + + // First have them continuously active for a number of time + for (int unsigned j = 0; j < (NumInp-i) * NumReqs; j++) begin + if ((i % 2) == 0) begin + data_inp[i] <= #ApplTime data_t'(i); + req_inp[i] <= #ApplTime 1'b1; + end + @(posedge clk); + end + data_inp[i] <= #ApplTime '0; + req_inp[i] <= #ApplTime 1'b0; + + repeat ((1+i)*NumReqs) @(posedge clk); + repeat (1000) @(posedge clk); + + + // have random stimuli + for (int unsigned j = 0; j < NumReqs; j++) begin + data_inp[i] <= #ApplTime new_stimuli(); + req_inp[i] <= #ApplTime 1'b1; + rand_wait = $urandom_range(1, 2*NumInp); + if (LockIn) begin + #TestTime; + while (!gnt_inp[i]) begin + @(posedge clk); + #TestTime; + end + end else begin + repeat (rand_wait) @(posedge clk); + end + @(posedge clk); + data_inp[i] <= #ApplTime '0; + req_inp[i] <= #ApplTime 1'b0; + + rand_wait = $urandom_range(0, NumInp); + repeat (rand_wait) @(posedge clk); + end + + end_of_sim[i] = 1'b1; + end + end + + function data_t new_stimuli(); + for (int unsigned i = 0; i < DataWidth; i++) begin + new_stimuli[i] = $urandom(); + end + endfunction : new_stimuli + + // consume streams + initial begin : proc_stream_consume + @(posedge rst_n); + gnt_oup = 1'b0; + forever begin + gnt_oup <= #ApplTime 1'b1; + @(posedge clk); + end + end + + // set external lock every few cycles + initial begin : proc_generate_lock + automatic int unsigned rand_wait; + @(posedge rst_n); + lock = 1'b0; + forever begin + rand_wait = $urandom_range(4, 10); + repeat (rand_wait) @(posedge clk); + lock <= #ApplTime 1'b1; + @(posedge clk); + lock <= #ApplTime 1'b0; + end + end + + // flush sometimes + initial begin : proc_flush + automatic int unsigned rand_wait; + flush = 1'b0; + @(posedge rst_n); + forever begin + rand_wait = $urandom_range(2000, 20000); + repeat (rand_wait) @(posedge clk); + flush <= #ApplTime 1'b1; + @(posedge clk); + flush <= #ApplTime 1'b0; + end + end + + // end simulation + initial begin : proc_sim_end + @(posedge rst_n); + wait (&end_of_sim); + repeat (10) @(posedge clk); + $stop(); + end + + // Check throughput + for (genvar i = 0; i < NumInp; i++) begin : gen_throughput_checker + initial begin : proc_throughput_checker + automatic longint unsigned tot_active [NumInp:1]; + automatic longint unsigned tot_served [NumInp:1]; + automatic int unsigned num_active; + automatic real throughput, exp_through, error; + for (int unsigned j = 0; j < NumInp; j++) begin + tot_active[j] = 0; + tot_served[j] = 0; + end + + @(posedge rst_n); + while (!(&end_of_sim)) begin + #TestTime; + + if (req_inp[i] && gnt_oup) begin + num_active = 0; + for (int unsigned j = 0; j < NumInp; j++) begin + if (req_inp[j]) begin + num_active++; + end + end + + tot_active[num_active] = tot_active[num_active] + 1; + if (gnt_inp[i]) begin + tot_served[num_active] = tot_served[num_active] + 1; + end + end + @(posedge clk); + end + + for (int unsigned j = 1; j <= NumInp; j++) begin + if (tot_active[j] > 0) begin + throughput = real'(tot_served[j])/real'(tot_active[j]); + exp_through = real'(1)/real'(j); + error = throughput - exp_through; + if (FairArb && LockIn) begin + assert(error < error_threshold && error > -error_threshold) else + $warning("Line: %0d is unfair!", i); + end + $display("Line: %0d, TotActice: %0d Throughput: %0f Ideal: %0f Diff: %0f", + i, j, throughput, exp_through, error); end + end + end + end + + // check data + initial begin : proc_check_data + automatic data_t data_queues[NumInp-1:0][$]; + automatic data_t exp_data; + forever begin + @(posedge clk); + #TestTime; + // Put exp data into the queues + for (int unsigned i = 0; i < NumInp; i++) begin + if (req_inp[i] && gnt_inp[i]) begin + data_queues[i].push_back(data_inp[i]); + end + end + + // check that the right data is observed + if (req_oup && gnt_oup) begin + exp_data = data_queues[idx].pop_front(); + assert(exp_data === data_oup); + end + end + end + + // DUT + rr_arb_tree_lock #( + .NumIn ( NumInp ), + .DataWidth ( DataWidth ), + .ExtPrio ( 1'b0 ), + .AxiVldRdy ( AxiVldRdy ), + .LockIn ( LockIn ), + .FairArb ( FairArb ) + ) i_rr_arb_tree_lock_dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .flush_i ( flush ), + .rr_i ( '0 ), + .lock_rr_i ( lock ), + .req_i ( req_inp ), + .gnt_o ( gnt_inp ), + .data_i ( data_inp ), + .gnt_i ( gnt_oup ), + .req_o ( req_oup ), + .data_o ( data_oup ), + .idx_o ( idx ) + ); +endmodule diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv new file mode 100644 index 00000000..4797f45a --- /dev/null +++ b/test/tb_time_dmr.sv @@ -0,0 +1,260 @@ +module tb_time_dmr; + + // Clock Parameters + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + parameter IDSize = 4; + localparam int LockTimeout = 4; + + // Testbench signals + data_t golden_queue [$]; + data_t data_golden, data_actual; + logic error; + int error_cnt; + int fault_budget; + + // Aux signals to show what faults are going on + enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + + // Signals for DUTS + logic clk; + logic rst_n; + logic enable; + + data_t data_in, data_redundant, data_error, data_redundant_faulty, data_out; + logic valid_in, valid_redundant, valid_error, valid_redundant_faulty, valid_out; + logic ready_in, ready_redundant, ready_error, ready_redundant_faulty, ready_out; + logic needs_retry_out; + logic [IDSize-1:0] id_redundant, id_error, id_redundant_faulty, id_next; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // DUT Instances + time_DMR_start #( + .DataType(data_t), + .IDSize(IDSize), + .UseExternalId(0) + ) dut_start ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_o(id_next), + + // Upstream connection + .data_i(data_in), + .id_i('0), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_redundant), + .id_o(id_redundant), + .valid_o(valid_redundant), + .ready_i(ready_redundant_faulty) + ); + + // Error XORs + assign data_redundant_faulty = data_redundant ^ data_error; + assign valid_redundant_faulty = valid_redundant ^ valid_error; + assign ready_redundant_faulty = ready_redundant ^ ready_error; + assign id_redundant_faulty = id_redundant ^ id_error; + + time_DMR_end #( + .DataType(data_t), + .LockTimeout(LockTimeout), + .IDSize(IDSize) + ) dut_end ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_i(id_next), + + + // Upstream connection + .data_i(data_redundant_faulty), + .id_i(id_redundant_faulty), + .valid_i(valid_redundant_faulty), + .ready_o(ready_redundant), + .lock_o(/*Unused*/), + + // Downstream connection + .data_o(data_out), + .id_o(/*Unused*/), + .needs_retry_o(needs_retry_out), + .valid_o(valid_out), + .ready_i(ready_out), + + .fault_detected_o(/*Unused*/) + ); + + // Data Application + initial begin + data_t data_new; + + // Initialize Handshake and Data + data_in = 8'h00; + valid_in = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + valid_in <= '0; + end + + valid_in <= '1; + + // do begin + data_new = $random; + // end while (data_new == data_in); + + data_in = data_new; + golden_queue.push_back(data_in); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Enable / Disable ECC + initial begin + enable = 1'b0; + $display("Disabled Redundancy"); + repeat (TESTS * 5) begin + @(posedge clk); + end + $display("Enabled Redundancy"); + enable = 1'b1; + end + + // Fault inject + initial begin + fault_budget = 0; + for (logic [2:0] ft = 0; ft < 5; ft++) begin + fault_type[2:0] = ft; + $display("Starting Test with fault type {%s}", fault_type.name()); + + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(15, 20)) begin + @(posedge clk); + # (APPLICATION_DELAY); + fault_current = NONE; + data_error = '0; + valid_error = '0; + ready_error = '0; + id_error = '0; + end + + // Send wrong data + @(posedge clk); + # (APPLICATION_DELAY); + fault_current <= fault_type; + fault_budget += 1; + data_error <= '0; + valid_error <= '0; + ready_error <= '0; + id_error <= '0; + case (fault_type) + // TODO: Write error to golden queue + DATA_ERROR: data_error <= $random; + VALID_ERROR: valid_error <= 1; + READY_ERROR: ready_error <= 1; + ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); + endcase + end + $display("Ending Test with fault type {%s}", fault_type.name()); + end + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(0); + end + + + // Aquisition & Validation + initial begin + $timeformat(-9, 0, " ns", 20); + + // Initialize error metrics + error = 0; // Signal so errors can easily be scrolled to in wave + error_cnt = 0; + // Initialize Handshake + ready_out = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + ready_out <= '0; + end + + // Set ready + ready_out <= '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = data_out; + + + if (golden_queue.size() > 0) begin + data_golden = golden_queue.pop_front(); + // Check output + if (needs_retry_out) begin + fault_budget -= 1; + if (fault_budget < 0) begin + $error("[T=%t] More faults detected than injected!", $time); + end + end else begin + if (data_actual != data_golden) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + fault_budget = 0; + error = 0; + end + end + end else begin + $display("[T=%t] Data %h Output when nothing was in golden queue", $time, data_actual); + error = 1; + error_cnt += 1; + end + end + end + +endmodule diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv new file mode 100644 index 00000000..5aae93d6 --- /dev/null +++ b/test/tb_time_dmr_retry.sv @@ -0,0 +1,339 @@ +module tb_time_dmr_retry; + + // Clock Parameters + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + typedef logic [7:0] tag_t; + + typedef struct packed { + data_t data; + tag_t tag; + } tagged_data_t; + + parameter IDSize = 4; + localparam int LockTimeout = 4; + + // Testbench signals + tagged_data_t golden_queue [$]; + tagged_data_t data_golden, data_actual; + logic error; + int error_cnt; + + // Aux signals to show what faults are going on + enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + + // Signals for DUTS + logic clk; + logic rst_n; + logic enable; + + // Downstream Connection + tagged_data_t data_in, data_detectable, data_redundant, data_error, data_redundant_faulty, data_detected, data_out; + logic valid_in, valid_detectable, valid_redundant, valid_error, valid_redundant_faulty, valid_detected, valid_out; + logic ready_in, ready_detectable, ready_redundant, ready_error, ready_redundant_faulty, ready_detected, ready_out; + logic [IDSize-1:0] id_detectable, id_redundant, id_error, id_redundant_faulty, id_detected, next_id; + logic needs_retry_detected; + + // Feedback connection + logic [IDSize-1:0] id_retry; + logic valid_retry; + logic ready_retry; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // DUT Instances + retry_start #( + .DataType(tagged_data_t), + .IDSize(IDSize) + ) dut_retry_start ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_in), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_detectable), + .id_o(id_detectable), + .valid_o(valid_detectable), + .ready_i(ready_detectable), + + // Retry Connection + .retry_id_i(id_retry), + .retry_valid_i(valid_retry), + .retry_ready_o(ready_retry) + ); + + + // DUT Instances + time_DMR_start #( + .DataType(tagged_data_t), + .IDSize(IDSize), + .UseExternalId(1) + ) dut_DMR_start ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_o(next_id), + + // Upstream connection + .data_i(data_detectable), + .id_i(id_detectable), + .valid_i(valid_detectable), + .ready_o(ready_detectable), + + // Downstream connection + .data_o(data_redundant), + .id_o(id_redundant), + .valid_o(valid_redundant), + .ready_i(ready_redundant_faulty) + ); + + // Error XORs + assign data_redundant_faulty = data_redundant ^ data_error; + assign valid_redundant_faulty = valid_redundant ^ valid_error; + assign ready_redundant_faulty = ready_redundant ^ ready_error; + assign id_redundant_faulty = id_redundant ^ id_error; + + // DUT Instances + time_DMR_end #( + .DataType(tagged_data_t), + .LockTimeout(LockTimeout), + .IDSize(IDSize) + ) dut_DMR_end ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_i(next_id), + + // Upstream connection + .data_i(data_redundant_faulty), + .id_i(id_redundant_faulty), + .valid_i(valid_redundant_faulty), + .ready_o(ready_redundant), + + // Downstream connection + .data_o(data_detected), + .id_o(id_detected), + .needs_retry_o(needs_retry_detected), + .valid_o(valid_detected), + .ready_i(ready_detected), + .lock_o(/*Unused*/), + + .fault_detected_o(/*Unused*/) + ); + + // DUT Instances + retry_end #( + .DataType(tagged_data_t), + .IDSize(IDSize) + ) dut_retry_end ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_detected), + .id_i(id_detected), + .needs_retry_i(needs_retry_detected), + .valid_i(valid_detected), + .ready_o(ready_detected), + + // Downstream connection + .data_o(data_out), + .valid_o(valid_out), + .ready_i(ready_out), + + // Retry Connection + .retry_id_o(id_retry), + .retry_valid_o(valid_retry), + .retry_ready_i(ready_retry) + ); + + // Data Application + initial begin + tag_t tag_new; + tagged_data_t data_new; + + tag_new = 0; + + // Initialize Handshake and Data + data_in = 8'h00; + valid_in = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + valid_in <= '0; + end + + valid_in <= '1; + + // Assign unique tag so we can + tag_new += 1; + data_new.data = $random; + data_new.tag = tag_new; + + data_in = data_new; + golden_queue.push_back(data_in); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Enable / Disable ECC + initial begin + enable = 1'b0; + $display("Disabled Redundancy"); + repeat (TESTS * 5) begin + @(posedge clk); + end + $display("Enabled Redundancy"); + enable = 1'b1; + end + + // Fault inject + initial begin + for (logic [2:0] ft = 0; ft < 5; ft++) begin + fault_type[2:0] = ft; + $display("Starting Test with fault type {%s}", fault_type.name()); + + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(15, 20)) begin + @(posedge clk); + # (APPLICATION_DELAY); + fault_current = NONE; + data_error = '0; + valid_error = '0; + ready_error = '0; + id_error = '0; + end + + // Send wrong data + @(posedge clk); + # (APPLICATION_DELAY); + fault_current <= fault_type; + data_error <= '0; + valid_error <= '0; + ready_error <= '0; + id_error <= '0; + case (fault_type) + DATA_ERROR: data_error <= $random; + VALID_ERROR: valid_error <= 1; + READY_ERROR: ready_error <= 1; + ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); + endcase + end + $display("Ending Test with fault type {%s}", fault_type.name()); + end + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(0); + end + + + // Aquisition & Validation + initial begin + logic found; + + $timeformat(-9, 0, " ns", 20); + + // Initialize error metrics + error = 0; // Signal so errors can easily be scrolled to in wave + error_cnt = 0; + found = 0; + + // Initialize Handshake + ready_out = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + ready_out <= '0; + end + + // Set ready + ready_out <= '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = data_out; + + if (golden_queue.size() > 0) begin + found = 0; + + repeat (golden_queue.size()) begin + data_golden = golden_queue.pop_front(); + if (data_golden.tag == data_actual.tag) begin + // Check output + if (data_actual.data != data_golden.data) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + error = 0; + found = 1; + break; + end + end else begin + golden_queue.push_back(data_golden); + end + end + if (found == 0) begin + $display("[T=%t] Tag %h Data %h Output but was not in golden queue ", $time, data_actual.tag, data_actual.data); + error = 1; + error_cnt += 1; + end + end else if (golden_queue.size() > 2 ** IDSize) begin + $display("[T=%t] Data does not get output in a timely manner!", $time); + error = 1; + error_cnt += 1; + end else begin + $display("[T=%t] Tag %h Data %h Output when nothing was in golden queue", $time, data_actual.tag, data_actual.data); + error = 1; + error_cnt += 1; + end + end + end + +endmodule diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv new file mode 100644 index 00000000..10e78f56 --- /dev/null +++ b/test/tb_time_dmr_retry_lock.sv @@ -0,0 +1,266 @@ +module tb_time_dmr_retry_lock; + // Clock Parameters + + timeunit 1ns; + timeprecision 10ps; + + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + parameter int NumOpgroups = 3; + parameter int OpgroupWidth = 2; + parameter int IDSize = 5; + localparam int LockTimeout = 5; + + // Data Type with Tag for testbench + typedef logic [7:0] data_t; + typedef logic [7:0] tag_t; + + typedef struct packed { + data_t data; + tag_t tag; + } tagged_data_t; + + // Testebench signals + tagged_data_t golden_queue [NumOpgroups-1:0][$]; + tagged_data_t data_golden, data_actual; + logic [OpgroupWidth-1:0] operation_actual; + + logic error; + int error_cnt; + + // Aux signals to show what faults are going on + enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + + // Signals for DUTS + logic clk; + logic rst_n; + + tagged_data_t in_data; + logic [OpgroupWidth-1:0] in_operation; + logic in_valid, in_ready; + + tagged_data_t data_error; + logic valid_error, ready_error; + logic [IDSize-1:0] id_error; + + tagged_data_t out_data; + logic [OpgroupWidth-1:0] out_operation; + logic out_valid, out_ready; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // Instantiation of full fpnew datapath dut + tb_time_dmr_retry_lock_dut #( + .DataType(tagged_data_t), + .NumOpgroups(NumOpgroups), + .OpgroupWidth(OpgroupWidth), + .IDSize(IDSize), + .LockTimeout(LockTimeout), + .OpgroupNumRegs({8'd4, 8'd3, 8'd3}) + ) dut ( + .clk_i(clk), + .rst_ni(rst_n), + + // Ustream + .operation_i(in_operation), + .data_i(in_data), + .valid_i(in_valid), + .ready_o(in_ready), + + .data_error_i (data_error), + .valid_error_i (valid_error), + .ready_error_i (ready_error), + .id_error_i (id_error), + + // Downstream + .operation_o(out_operation), + .data_o(out_data), + .valid_o(out_valid), + .ready_i(out_ready) + ); + + + // Data Application + initial begin + logic [OpgroupWidth-1:0] operation_new; + tag_t tag_new; + tagged_data_t data_new; + + tag_new = 0; + + // Initialize Handshake and Data + in_data = 8'h00; + in_operation = '0; + in_valid = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 10)) begin + @(posedge clk); + # APPLICATION_DELAY; + in_valid = '0; + end + + // Build next data element + operation_new = $urandom_range(0, NumOpgroups-1); + tag_new += 1; + data_new.data = $random; + data_new.tag = tag_new; + + // Apply Data + in_data = data_new; + in_operation = operation_new; + in_valid = '1; + + // Save data to Queue + golden_queue[operation_new].push_back(data_new); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!in_ready) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Fault inject + initial begin + for (logic [2:0] ft = 0; ft < 5; ft++) begin + fault_type[2:0] = ft; + $display("Starting Test with fault type {%s}", fault_type.name()); + + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(55, 65)) begin + @(posedge clk); + # (APPLICATION_DELAY); + fault_current = NONE; + data_error = '0; + valid_error = '0; + ready_error = '0; + id_error = '0; + end + + // Send wrong data + @(posedge clk); + # (APPLICATION_DELAY); + fault_current <= fault_type; + data_error <= '0; + valid_error <= '0; + ready_error <= '0; + case (fault_type) + DATA_ERROR: data_error <= $random; + VALID_ERROR: valid_error <= 1; + READY_ERROR: ready_error <= 1; + ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); + endcase + end + $display("Ending Test with fault type {%s}", fault_type.name()); + end + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(error_cnt); + end + + + // Aquisition & Validation + initial begin + logic found; + + $timeformat(-9, 0, " ns", 20); + + + // Initialize Handshake + out_ready = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 10)) begin + @(posedge clk); + # APPLICATION_DELAY; + out_ready = '0; + end + + // Set ready + out_ready = '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!out_valid) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = out_data; + operation_actual = out_operation; + if (golden_queue[out_operation].size() > 0) begin + + // Try to find an element with matching tag and consume it + found = 0; + repeat (golden_queue[out_operation].size()) begin + data_golden = golden_queue[out_operation].pop_front(); + if (data_golden.tag == data_actual.tag) begin + found = 1; + + // Check output + if (data_actual.data != data_golden.data) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + error = 0; + break; + end + + end else begin + golden_queue[out_operation].push_back(data_golden); + end + end + + // In case not tag matched error + if (found == 0) begin + $display("[T=%t] Tag %h Data %h Output but was not in golden queue ", $time, data_actual.tag, data_actual.data); + error = 1; + error_cnt += 1; + end + end else begin + $display("[T=%t] Operation %h -> Data %h Output when nothing was in golden queue", $time, operation_actual, data_actual); + error = 1; + error_cnt += 1; + end + + + // Check that no queue runs out of bounds + for (int operation = 0; operation < NumOpgroups; operation++) begin + if (golden_queue[operation].size() > 2 ** IDSize) begin + $display("[T=%t] Data does not get output in a timely manner!", $time); + error = 1; + error_cnt += 1; + end + end + end + end + +endmodule : tb_time_dmr_retry_lock \ No newline at end of file diff --git a/test/tb_time_dmr_retry_lock_dut.sv b/test/tb_time_dmr_retry_lock_dut.sv new file mode 100644 index 00000000..eca94e2a --- /dev/null +++ b/test/tb_time_dmr_retry_lock_dut.sv @@ -0,0 +1,276 @@ +`include "../../common_cells/include/common_cells/registers.svh" + + +module tb_time_dmr_retry_lock_dut # ( + // What kind of data signal to pass through the chain + parameter type DataType = logic, + parameter int LockTimeout = 5, + + // How many parallel instances to generate and how many registers they should each have + parameter int NumOpgroups = 3, + parameter int OpgroupWidth = 2, + parameter int IDSize = 4, + parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd10, 8'd10, 8'd10} +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input logic [OpgroupWidth-1:0] operation_i, + input DataType data_i, + input logic valid_i, + output logic ready_o, + + // Error Injection + input DataType data_error_i, + input logic [IDSize-1:0] id_error_i, + input logic valid_error_i, + input logic ready_error_i, + + // Downstream connection + output logic [OpgroupWidth-1:0] operation_o, + output DataType data_o, + output logic valid_o, + input logic ready_i +); + + // Typedef for stacked signal in TMR + typedef struct packed { + DataType data; + logic [$bits(operation_i)-1:0] operation; + } tmr_stacked_t; + + // Typedef for stacked signal in TMR + typedef struct packed { + logic [IDSize-1:0] id; + DataType data; + } rr_stacked_t; + + // Input connection + tmr_stacked_t in_tmr_stack; + assign in_tmr_stack.data = data_i; + assign in_tmr_stack.operation = operation_i; + + // Signals for after TMR + tmr_stacked_t in_tmr_stack_redundant; + logic in_valid_redundant, in_ready_redundant; + logic [IDSize-1:0] in_id_redundant; + + // Feedback connection + logic [IDSize-1:0] id_retry, next_id; + logic valid_retry; + logic ready_retry; + + // Connection between retry and DMR + tmr_stacked_t data_retry2dmr; + logic [IDSize-1:0] id_retry2dmr; + logic valid_retry2dmr; + logic ready_retry2dmr; + + // DUT Instances + retry_start #( + .DataType(tmr_stacked_t), + .IDSize(IDSize) + ) i_retry_start ( + .clk_i(clk_i), + .rst_ni(rst_ni), + + // Upstream connection + .data_i(in_tmr_stack), + .valid_i(valid_i), + .ready_o(ready_o), + + // Downstream connection + .data_o(data_retry2dmr), + .id_o(id_retry2dmr), + .valid_o(valid_retry2dmr), + .ready_i(ready_retry2dmr), + + // Retry Connection + .retry_id_i(id_retry), + .retry_valid_i(valid_retry), + .retry_ready_o(ready_retry) + ); + + + time_DMR_start #( + .DataType(tmr_stacked_t), + .IDSize (IDSize), + .UseExternalId(1) + ) i_time_DMR_start ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .enable_i(1'b1), + + .next_id_o(next_id), + + // Upstream connection + .data_i(data_retry2dmr), + .id_i(id_retry2dmr), + .valid_i(valid_retry2dmr), + .ready_o(ready_retry2dmr), + + // Downstream connection + .data_o(in_tmr_stack_redundant), + .id_o (in_id_redundant), + .valid_o(in_valid_redundant), + .ready_i(in_ready_redundant) + ); + + // Handshake signal array for opgroup block + logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; + rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; + rr_stacked_t out_rr_stack; + + // Pass ready up based on the current operation_i + assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; + + for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups + localparam NUM_REGS = OpgroupNumRegs[opgrp]; + + // Input pipeline signals, index i holds signal after i register stages + + DataType [0:NUM_REGS] pipe_data; + logic [0:NUM_REGS] pipe_valid; + logic [0:NUM_REGS] pipe_ready; + logic [0:NUM_REGS][IDSize-1:0] pipe_id; + + // Upstream Connection + // Error Injection + assign pipe_valid[0] = (in_valid_redundant ^ valid_error_i) && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_error_i; + assign pipe_id[0] = in_id_redundant ^ id_error_i; + assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_error_i; + + // Generate the register stages + for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline + // Internal register enable for this stage + logic reg_ena; + + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; + + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(pipe_data[i+1], pipe_data[i], reg_ena, DataType'('0)) + `FFL( pipe_id[i+1], pipe_id[i], reg_ena, IDSize'('0)) + end + + // Downstream connection + assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; + assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; + end + + // Signals for after RR + logic out_tmr_valid, out_tmr_ready; + tmr_stacked_t out_tmr_stack; + + // Backpropagating lock signal + logic lock; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree_lock #( + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .flush_i('0), + .rr_i ('0), + .lock_rr_i (lock), + + // Upstream connection + .req_i(out_opgrp_valid), + .gnt_o(out_opgrp_ready), + .data_i(out_opgrp_rr_stack), + + // Downstream connection + .gnt_i(out_tmr_ready), + .req_o(out_tmr_valid), + .data_o(out_rr_stack), + .idx_o(out_tmr_stack.operation) + ); + + + // Signals for after TMR + tmr_stacked_t out_stacked; + logic [IDSize-1:0] out_tmr_id; + + assign out_tmr_id = out_rr_stack.id; + assign out_tmr_stack.data = out_rr_stack.data; + + // Connection between retry and DMR + tmr_stacked_t data_dmr2retry; + logic [IDSize-1:0] id_dmr2retry; + logic needs_retry_dmr2retry; + logic valid_dmr2retry; + logic ready_dmr2retry; + + time_DMR_end #( + .DataType(tmr_stacked_t), + .LockTimeout(LockTimeout), + .IDSize (IDSize) + ) i_time_DMR_end ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .enable_i(1'b1), + + .next_id_i(next_id), + + // Upstream connection + .data_i(out_tmr_stack), + .id_i (out_tmr_id), + .valid_i(out_tmr_valid), + .ready_o(out_tmr_ready), + + // Lock connection to upstream + .lock_o(lock), + + // Downstream connection + .data_o(data_dmr2retry), + .id_o(id_dmr2retry), + .needs_retry_o(needs_retry_dmr2retry), + .valid_o(valid_dmr2retry), + .ready_i(ready_dmr2retry), + + // Flag output + .fault_detected_o(/*Unused*/) + ); + + retry_end #( + .DataType(tmr_stacked_t), + .IDSize(IDSize) + ) i_retry_end ( + .clk_i(clk_i), + .rst_ni(rst_ni), + + // Upstream connection + .data_i(data_dmr2retry), + .id_i(id_dmr2retry), + .needs_retry_i(needs_retry_dmr2retry), + .valid_i(valid_dmr2retry), + .ready_o(ready_dmr2retry), + + // Downstream connection + .data_o(out_stacked), + .valid_o(valid_o), + .ready_i(ready_i), + + // Retry Connection + .retry_id_o(id_retry), + .retry_valid_o(valid_retry), + .retry_ready_i(ready_retry) + ); + + assign data_o = out_stacked.data; + assign operation_o = out_stacked.operation; + +endmodule: tb_time_dmr_retry_lock_dut diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv new file mode 100644 index 00000000..9561934d --- /dev/null +++ b/test/tb_time_tmr.sv @@ -0,0 +1,231 @@ +module tb_time_tmr; + + // Clock Parameters + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + parameter IDSize = 4; + localparam int LockTimeout = 4; + + // Testbench signals + data_t golden_queue [$]; + data_t data_golden, data_actual; + logic error; + int error_cnt; + + // Aux signals to show what faults are going on + enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + + // Signals for DUTS + logic clk; + logic rst_n; + logic enable; + + data_t data_in, data_redundant, data_error, data_redundant_faulty, data_out; + logic valid_in, valid_redundant, valid_error, valid_redundant_faulty, valid_out; + logic ready_in, ready_redundant, ready_error, ready_redundant_faulty, ready_out; + logic [IDSize-1:0] id_redundant, id_error, id_redundant_faulty; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // DUT Instances + time_TMR_start #( + .DataType(data_t), + .IDSize(IDSize) + ) dut_start ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + // Upstream connection + .data_i(data_in), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_redundant), + .id_o(id_redundant), + .valid_o(valid_redundant), + .ready_i(ready_redundant_faulty) + ); + + // Error XORs + assign data_redundant_faulty = data_redundant ^ data_error; + assign valid_redundant_faulty = valid_redundant ^ valid_error; + assign ready_redundant_faulty = ready_redundant ^ ready_error; + assign id_redundant_faulty = id_redundant ^ id_error; + + time_TMR_end #( + .DataType(data_t), + .LockTimeout(LockTimeout), + .IDSize(IDSize) + ) dut_end ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + // Upstream connection + .data_i(data_redundant_faulty), + .id_i(id_redundant_faulty), + .valid_i(valid_redundant_faulty), + .ready_o(ready_redundant), + + // Downstream connection + .data_o(data_out), + .valid_o(valid_out), + .ready_i(ready_out), + .lock_o(/*Unused*/), + + // Flags + .fault_detected_o(/* Unused */) + ); + + // Data Application + initial begin + // Initialize Handshake and Data + data_in = 8'h00; + valid_in = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + valid_in <= '0; + end + + valid_in <= '1; + data_in = $random; + golden_queue.push_back(data_in); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Enable / Disable ECC + initial begin + enable = 1'b0; + $display("Disabled Redundancy"); + repeat (TESTS * 5) begin + @(posedge clk); + end + $display("Enabled Redundancy"); + enable = 1'b1; + end + + // Fault inject + initial begin + for (logic [2:0] ft = 0; ft < 5; ft++) begin + fault_type[2:0] = ft; + $display("Starting Test with fault type {%s}", fault_type.name()); + + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(15, 20)) begin + @(posedge clk); + # (APPLICATION_DELAY); + fault_current = NONE; + data_error = '0; + valid_error = '0; + ready_error = '0; + id_error = '0; + end + + // Send wrong data + @(posedge clk); + # (APPLICATION_DELAY); + fault_current <= fault_type; + data_error <= '0; + valid_error <= '0; + ready_error <= '0; + id_error <= '0; + case (fault_type) + DATA_ERROR: data_error <= $random; + VALID_ERROR: valid_error <= 1; + READY_ERROR: ready_error <= 1; + ID_ERROR: id_error <= $random; + endcase + end + $display("Ending Test with fault type {%s}", fault_type.name()); + end + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(error_cnt); + end + + + // Aquisition & Validation + initial begin + $timeformat(-9, 0, " ns", 20); + + // Initialize error metrics + error = 0; // Signal so errors can easily be scrolled to in wave + error_cnt = 0; + + // Initialize Handshake + ready_out = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + ready_out <= '0; + end + + // Set ready + ready_out <= '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = data_out; + if (golden_queue.size() > 0) begin + data_golden = golden_queue.pop_front(); + // Check output + if (data_actual != data_golden) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + error = 0; + end + end else begin + $display("[T=%t] Data %h Output when nothing was in golden queue", $time, data_actual); + error = 1; + error_cnt += 1; + end + end + end + +endmodule diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv new file mode 100644 index 00000000..00ca36d8 --- /dev/null +++ b/test/tb_time_tmr_lock.sv @@ -0,0 +1,219 @@ +module tb_time_tmr_lock; + // Clock Parameters + + timeunit 1ns; + timeprecision 10ps; + + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + parameter int NumOpgroups = 3; + parameter int OpgroupWidth = 2; + parameter int IDSize = 4; + localparam int LockTimeout = 5; + + // Testebench signals + data_t golden_queue [NumOpgroups-1:0][$]; + data_t data_golden, data_actual; + logic [OpgroupWidth-1:0] operation_actual; + logic error; + int error_cnt; + + // Aux signals to show what faults are going on + enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + + // Signals for DUTS + logic clk; + logic rst_n; + + data_t in_data; + logic [OpgroupWidth-1:0] in_operation; + logic in_valid, in_ready; + + data_t data_error; + logic valid_error, ready_error; + logic [IDSize-1:0] id_error; + + data_t out_data; + logic [OpgroupWidth-1:0] out_operation; + logic out_valid, out_ready; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // Instantiation of full fpnew datapath dut + tb_time_tmr_lock_dut #( + .DataType(data_t), + .NumOpgroups(NumOpgroups), + .OpgroupWidth(OpgroupWidth), + .IDSize(IDSize), + .LockTimeout(LockTimeout), + .OpgroupNumRegs({8'd4, 8'd3, 8'd3}) + ) dut ( + .clk_i(clk), + .rst_ni(rst_n), + + // Ustream + .operation_i(in_operation), + .data_i(in_data), + .valid_i(in_valid), + .ready_o(in_ready), + + .data_error_i (data_error), + .valid_error_i (valid_error), + .ready_error_i (ready_error), + .id_error_i (id_error), + + // Downstream + .operation_o(out_operation), + .data_o(out_data), + .valid_o(out_valid), + .ready_i(out_ready) + ); + + + // Data Application + initial begin + data_t new_data; + logic [OpgroupWidth-1:0] new_operation; + + // Initialize Handshake and Data + in_data = 8'h00; + in_operation = '0; + in_valid = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 10)) begin + @(posedge clk); + # APPLICATION_DELAY; + in_valid = '0; + end + + // Build next data element + new_operation = $urandom_range(0, NumOpgroups-1); + new_data = $random; + + // Apply Data + in_data = new_data; + in_operation = new_operation; + in_valid = '1; + + // Save data for future + golden_queue[new_operation].push_back(new_data); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!in_ready) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Fault inject + initial begin + for (logic [2:0] ft = 0; ft < 5; ft++) begin + fault_type[2:0] = ft; + $display("Starting Test with fault type {%s}", fault_type.name()); + + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(45, 55)) begin + @(posedge clk); + # (APPLICATION_DELAY); + fault_current = NONE; + data_error = '0; + valid_error = '0; + ready_error = '0; + id_error = '0; + end + + // Send wrong data + @(posedge clk); + # (APPLICATION_DELAY); + fault_current <= fault_type; + data_error <= '0; + valid_error <= '0; + ready_error <= '0; + case (fault_type) + DATA_ERROR: data_error <= $random; + VALID_ERROR: valid_error <= 1; + READY_ERROR: ready_error <= 1; + ID_ERROR: id_error <= $random; + endcase + end + $display("Ending Test with fault type {%s}", fault_type.name()); + end + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(error_cnt); + end + + + // Aquisition & Validation + initial begin + $timeformat(-9, 0, " ns", 20); + + + // Initialize Handshake + out_ready = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 10)) begin + @(posedge clk); + # APPLICATION_DELAY; + out_ready = '0; + end + + // Set ready + out_ready = '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!out_valid) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = out_data; + operation_actual = out_operation; + if (golden_queue[out_operation].size() > 0) begin + data_golden = golden_queue[out_operation].pop_front(); + if (data_actual != data_golden) begin + $display("[T=%t] Operation %h -> Data %h Mismatch, should be %h", $time, operation_actual, data_actual, data_golden); + error = 1; + error_cnt += 1; + end else begin + error = 0; + end + end else begin + $display("[T=%t] Operation %h -> Data %h Output when nothing was in golden queue", $time, operation_actual, data_actual); + error = 1; + error_cnt += 1; + end + end + end + +endmodule : tb_time_tmr_lock \ No newline at end of file diff --git a/test/tb_time_tmr_lock_dut.sv b/test/tb_time_tmr_lock_dut.sv new file mode 100644 index 00000000..c934d710 --- /dev/null +++ b/test/tb_time_tmr_lock_dut.sv @@ -0,0 +1,198 @@ +`include "../../common_cells/include/common_cells/registers.svh" + + +module tb_time_tmr_lock_dut # ( + // What kind of data signal to pass through the chain + parameter type DataType = logic, + parameter int LockTimeout = 5, + + // How many parallel instances to generate and how many registers they should each have + parameter int NumOpgroups = 3, + parameter int OpgroupWidth = 2, + parameter int IDSize = 4, + parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd10, 8'd10, 8'd10} +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input logic [OpgroupWidth-1:0] operation_i, + input DataType data_i, + input logic valid_i, + output logic ready_o, + + // Error Injection + input DataType data_error_i, + input logic [IDSize-1:0] id_error_i, + input logic valid_error_i, + input logic ready_error_i, + + // Downstream connection + output logic [OpgroupWidth-1:0] operation_o, + output DataType data_o, + output logic valid_o, + input logic ready_i +); + + // Typedef for stacked signal in TMR + typedef struct packed { + DataType data; + logic [$bits(operation_i)-1:0] operation; + } tmr_stacked_t; + + // Typedef for stacked signal in TMR + typedef struct packed { + logic [IDSize-1:0] id; + DataType data; + } rr_stacked_t; + + // Input connection + tmr_stacked_t in_tmr_stack; + assign in_tmr_stack.data = data_i; + assign in_tmr_stack.operation = operation_i; + + // Signals for after TMR + tmr_stacked_t in_tmr_stack_redundant; + logic in_valid_redundant, in_ready_redundant; + logic [IDSize-1:0] in_id_redundant; + + time_TMR_start #( + .DataType(tmr_stacked_t), + .IDSize (IDSize) + ) i_time_TMR_start ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .enable_i(1'b1), + + // Upstream connection + .data_i(in_tmr_stack), + .valid_i(valid_i), + .ready_o(ready_o), + + // Downstream connection + .data_o(in_tmr_stack_redundant), + .id_o (in_id_redundant), + .valid_o(in_valid_redundant), + .ready_i(in_ready_redundant) + ); + + // Handshake signal array for opgroup block + logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; + rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; + rr_stacked_t out_rr_stack; + + // Pass ready up based on the current operation_i + assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; + + for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups + localparam NUM_REGS = OpgroupNumRegs[opgrp]; + + // Input pipeline signals, index i holds signal after i register stages + + DataType [0:NUM_REGS] pipe_data; + logic [0:NUM_REGS] pipe_valid; + logic [0:NUM_REGS] pipe_ready; + logic [0:NUM_REGS][IDSize-1:0] pipe_id; + + // Upstream Connection + // Error Injection + assign pipe_valid[0] = in_valid_redundant ^ valid_error_i && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_error_i; + assign pipe_id[0] = in_id_redundant ^ id_error_i; + assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_error_i; + + // Generate the register stages + for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline + // Internal register enable for this stage + logic reg_ena; + + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; + + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(pipe_data[i+1], pipe_data[i], reg_ena, DataType'('0)) + `FFL( pipe_id[i+1], pipe_id[i], reg_ena, IDSize'('0)) + end + + // Downstream connection + assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; + assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; + end + + // Signals for after RR + logic out_tmr_valid, out_tmr_ready; + tmr_stacked_t out_tmr_stack; + + // Backpropagating lock signal + logic lock; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree_lock #( + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .flush_i('0), + .rr_i ('0), + .lock_rr_i (lock), + + // Upstream connection + .req_i(out_opgrp_valid), + .gnt_o(out_opgrp_ready), + .data_i(out_opgrp_rr_stack), + + // Downstream connection + .gnt_i(out_tmr_ready), + .req_o(out_tmr_valid), + .data_o(out_rr_stack), + .idx_o(out_tmr_stack.operation) + ); + + + // Signals for after TMR + tmr_stacked_t out_stacked; + logic [IDSize-1:0] out_tmr_id; + + assign out_tmr_id = out_rr_stack.id; + assign out_tmr_stack.data = out_rr_stack.data; + + + time_TMR_end #( + .DataType(tmr_stacked_t), + .LockTimeout(LockTimeout), + .IDSize (IDSize) + ) i_time_TMR_end ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .enable_i(1'b1), + + // Upstream connection + .data_i(out_tmr_stack), + .id_i (out_tmr_id), + .valid_i(out_tmr_valid), + .ready_o(out_tmr_ready), + + // Downstream connection + .data_o(out_stacked), + .valid_o(valid_o), + .ready_i(ready_i), + .lock_o(lock), + + // Flags + .fault_detected_o(/* Unused */) + ); + + assign data_o = out_stacked.data; + assign operation_o = out_stacked.operation; + +endmodule: tb_time_tmr_lock_dut \ No newline at end of file From 25312d3ea4c897688967590cf9b0dce7b92ebdf2 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 12 Apr 2024 19:51:40 +0200 Subject: [PATCH 02/21] Added TB throughput measurements - TBs test throughput - TB have an additional governing initial statement - Shared TB functionality moved to header file --- Bender.yml | 2 - test/tb_retry.sv | 7 + test/tb_time.svh | 100 +++++ test/tb_time_dmr.sv | 337 ++++++++-------- test/tb_time_dmr_retry_lock.sv | 607 ++++++++++++++++++++--------- test/tb_time_dmr_retry_lock_dut.sv | 276 ------------- test/tb_time_tmr.sv | 314 ++++++++------- test/tb_time_tmr_lock.sv | 515 +++++++++++++++--------- test/tb_time_tmr_lock_dut.sv | 198 ---------- 9 files changed, 1196 insertions(+), 1160 deletions(-) create mode 100644 test/tb_time.svh delete mode 100644 test/tb_time_dmr_retry_lock_dut.sv delete mode 100644 test/tb_time_tmr_lock_dut.sv diff --git a/Bender.yml b/Bender.yml index aa010a8b..ffce13a2 100644 --- a/Bender.yml +++ b/Bender.yml @@ -111,9 +111,7 @@ sources: - test/tb_time_dmr.sv - test/tb_retry.sv - test/tb_time_dmr_retry.sv - - test/tb_time_tmr_lock_dut.sv - test/tb_time_tmr_lock.sv - - test/tb_time_dmr_retry_lock_dut.sv - test/tb_time_dmr_retry_lock.sv - test/tb_rr_arb_tree_lock.sv diff --git a/test/tb_retry.sv b/test/tb_retry.sv index d24d1f9c..55e017db 100644 --- a/test/tb_retry.sv +++ b/test/tb_retry.sv @@ -134,6 +134,13 @@ module tb_retry; @(posedge clk); # (APPLICATION_DELAY); needs_retry_middle = '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!(ready_middle & valid_middle)) begin + @(posedge clk); + # AQUISITION_DELAY; + end end $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); diff --git a/test/tb_time.svh b/test/tb_time.svh new file mode 100644 index 00000000..8b2c3c0d --- /dev/null +++ b/test/tb_time.svh @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////////7 +// Signal Definitions +//////////////////////////////////////////////////////////////////////////////////7 + +logic clk, rst_n; +logic valid_in, ready_in; +logic valid_out, ready_out; +logic enable; + +//////////////////////////////////////////////////////////////////////////////////7 +// Initial State +//////////////////////////////////////////////////////////////////////////////////7 +initial clk = 1'b0; +initial rst_n = 1'b1; +initial valid_in = 1'b0; +initial ready_out = 1'b0; +initial enable = 1'b0; + +//////////////////////////////////////////////////////////////////////////////////7 +// Clock Setup +//////////////////////////////////////////////////////////////////////////////////7 +longint unsigned reset_length = 20; + +always #((CLK_PERIOD/2)) clk = ~clk; + +task reset(); + rst_n = 1'b0; + repeat (reset_length) @(negedge clk); + rst_n = 1'b1; +endtask + +//////////////////////////////////////////////////////////////////////////////////7 +// Handshake setup +//////////////////////////////////////////////////////////////////////////////////7 +longint unsigned in_hs_count = 0; +longint unsigned in_hs_max_starvation = 5; + +longint unsigned out_hs_count = 0; +longint unsigned out_hs_max_starvation = 5; + +task input_handshake_begin(); + // Wait random time (with no valid data) + repeat ($urandom_range(0, in_hs_max_starvation)) begin + @(posedge clk); + #APPLICATION_DELAY; + end + + // Wait for reset to pass + while (!rst_n) begin + @(posedge clk) ; + #APPLICATION_DELAY; + end +endtask + +task input_handshake_end(); + // Perform Handshake + valid_in = 1'b1; + + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end + @(posedge clk); + # APPLICATION_DELAY; + valid_in = 1'b0; // This might get overwritten by next handshake + + in_hs_count = in_hs_count + 1; +endtask + +task output_handshake_start(); + // Wait random time (with no valid data) + repeat ($urandom_range(0, out_hs_max_starvation)) begin + @(posedge clk); + # APPLICATION_DELAY; + end + + // Wait for reset to pass + while (!rst_n) begin + @(posedge clk); + # APPLICATION_DELAY; + end + + ready_out = 1'b1; + + // Wait for correct amount of time in cycle + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + #AQUISITION_DELAY; + end +endtask + +task output_handshake_end(); + @(posedge clk); + # APPLICATION_DELAY; + ready_out = 1'b0; // This might get overwritten by next handshake + + out_hs_count = out_hs_count + 1; +endtask \ No newline at end of file diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv index 4797f45a..129cb30d 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_time_dmr.sv @@ -1,47 +1,28 @@ -module tb_time_dmr; +module tb_time_dmr #( + // DUT Parameters + parameter IDSize = 4, + parameter int LockTimeout = 4, - // Clock Parameters - localparam time CLK_PERIOD = 10ns; - localparam time APPLICATION_DELAY = 2ns; - localparam time AQUISITION_DELAY = 8ns; - localparam unsigned RST_CLK_CYCLES = 10; - localparam unsigned TESTS = 10000; + // TB Parameters + parameter int unsigned TESTS = 10000, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns - // Parameters - typedef logic [7:0] data_t; - parameter IDSize = 4; - localparam int LockTimeout = 4; - - // Testbench signals - data_t golden_queue [$]; - data_t data_golden, data_actual; - logic error; - int error_cnt; - int fault_budget; +) ( /* no ports on TB */ ); - // Aux signals to show what faults are going on - enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + `include "tb_time.svh" - // Signals for DUTS - logic clk; - logic rst_n; - logic enable; + //////////////////////////////////////////////////////////////////////////////////7 + // DUT (s) + //////////////////////////////////////////////////////////////////////////////////7 - data_t data_in, data_redundant, data_error, data_redundant_faulty, data_out; - logic valid_in, valid_redundant, valid_error, valid_redundant_faulty, valid_out; - logic ready_in, ready_redundant, ready_error, ready_redundant_faulty, ready_out; - logic needs_retry_out; - logic [IDSize-1:0] id_redundant, id_error, id_redundant_faulty, id_next; - - // Clock Generation - initial begin - clk = '1; - rst_n = '0; - repeat (10) @(posedge clk); - rst_n = 1; - end + typedef logic [7:0] data_t; - always #((CLK_PERIOD/2)) clk = ~clk; + data_t data_in, data_redundant, data_fault, data_redundant_faulty, data_out; + logic valid_redundant, valid_fault, valid_redundant_faulty; + logic ready_redundant, ready_fault, ready_redundant_faulty; + logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty, id_next; // DUT Instances time_DMR_start #( @@ -68,12 +49,6 @@ module tb_time_dmr; .ready_i(ready_redundant_faulty) ); - // Error XORs - assign data_redundant_faulty = data_redundant ^ data_error; - assign valid_redundant_faulty = valid_redundant ^ valid_error; - assign ready_redundant_faulty = ready_redundant ^ ready_error; - assign id_redundant_faulty = id_redundant ^ id_error; - time_DMR_end #( .DataType(data_t), .LockTimeout(LockTimeout), @@ -103,141 +78,54 @@ module tb_time_dmr; .fault_detected_o(/*Unused*/) ); - // Data Application - initial begin - data_t data_new; - - // Initialize Handshake and Data - data_in = 8'h00; - valid_in = 1'b0; - - // Wait for reset to be lifted - @(posedge rst_n); + //////////////////////////////////////////////////////////////////////////////////7 + // Data Input + //////////////////////////////////////////////////////////////////////////////////7 + data_t golden_queue [$]; + initial begin forever begin - // Wait random time (with no valid data) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - valid_in <= '0; - end - - valid_in <= '1; - - // do begin - data_new = $random; - // end while (data_new == data_in); - - data_in = data_new; + input_handshake_begin(); + data_in = $random; golden_queue.push_back(data_in); - - // Wait for handshake and as soon as it happens invalidate data - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!ready_in) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - - end - end - - // Enable / Disable ECC - initial begin - enable = 1'b0; - $display("Disabled Redundancy"); - repeat (TESTS * 5) begin - @(posedge clk); + input_handshake_end(); end - $display("Enabled Redundancy"); - enable = 1'b1; end - // Fault inject - initial begin - fault_budget = 0; - for (logic [2:0] ft = 0; ft < 5; ft++) begin - fault_type[2:0] = ft; - $display("Starting Test with fault type {%s}", fault_type.name()); - - repeat (TESTS) begin - - // Send correct data for some cycles to space errors - repeat ($urandom_range(15, 20)) begin - @(posedge clk); - # (APPLICATION_DELAY); - fault_current = NONE; - data_error = '0; - valid_error = '0; - ready_error = '0; - id_error = '0; - end - - // Send wrong data - @(posedge clk); - # (APPLICATION_DELAY); - fault_current <= fault_type; - fault_budget += 1; - data_error <= '0; - valid_error <= '0; - ready_error <= '0; - id_error <= '0; - case (fault_type) - // TODO: Write error to golden queue - DATA_ERROR: data_error <= $random; - VALID_ERROR: valid_error <= 1; - READY_ERROR: ready_error <= 1; - ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); - endcase - end - $display("Ending Test with fault type {%s}", fault_type.name()); - end - $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); - $finish(0); - end + //////////////////////////////////////////////////////////////////////////////////7 + // Data Output + //////////////////////////////////////////////////////////////////////////////////7 + data_t data_golden, data_actual; + logic needs_retry_actual; + logic error; // Helper signal so one can quickly scroll to errors in questa + longint unsigned error_cnt = 0; + int fault_budget = 0; + + // Progress reporting + task reset_metrics(); + reset(); + error_cnt = 0; + in_hs_count = 0; + out_hs_count = 0; + golden_queue.delete(); + endtask - // Aquisition & Validation initial begin $timeformat(-9, 0, " ns", 20); - - // Initialize error metrics - error = 0; // Signal so errors can easily be scrolled to in wave - error_cnt = 0; - // Initialize Handshake - ready_out = '0; - - // Wait for reset to be lifted - @(posedge rst_n); - forever begin - // Wait random time (while not ready) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - ready_out <= '0; - end - - // Set ready - ready_out <= '1; - - // Wait for handshake - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!valid_out) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - - // Once it happened check if output was good and reset ready again + output_handshake_start(); data_actual = data_out; - - + needs_retry_actual = needs_retry_out; if (golden_queue.size() > 0) begin data_golden = golden_queue.pop_front(); - // Check output - if (needs_retry_out) begin + + if (needs_retry_actual) begin fault_budget -= 1; if (fault_budget < 0) begin $error("[T=%t] More faults detected than injected!", $time); + error = 1; + error_cnt += 1; end end else begin if (data_actual != data_golden) begin @@ -245,16 +133,139 @@ module tb_time_dmr; error = 1; error_cnt += 1; end else begin - fault_budget = 0; + fault_budget = 1; error = 0; end end + end else begin $display("[T=%t] Data %h Output when nothing was in golden queue", $time, data_actual); error = 1; error_cnt += 1; end + output_handshake_end(); + end + end + + //////////////////////////////////////////////////////////////////////////////////7 + // Fault Injection + //////////////////////////////////////////////////////////////////////////////////7 + + longint unsigned min_fault_delay = 15; + longint unsigned max_fault_delay = 20; + + // Signals to show what faults are going on + enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; + + assign data_redundant_faulty = data_redundant ^ data_fault; + assign valid_redundant_faulty = valid_redundant ^ valid_fault; + assign ready_redundant_faulty = ready_redundant ^ ready_fault; + assign id_redundant_faulty = id_redundant ^ id_fault; + + initial data_fault = '0; + initial valid_fault = '0; + initial ready_fault = '0; + initial id_fault = '0; + + task inject_fault(); + // Send correct data for some cycles to space errors + repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; end + + // Send wrong data + fault_current = fault_type; + case (fault_type) + DATA_FAULT: data_fault <= $random; + VALID_FAULT: valid_fault <= 1; + READY_FAULT: ready_fault <= 1; + ID_FAULT: id_fault <= (1 << $urandom_range(0,IDSize-1)); + endcase + + fault_budget += 1; + + // Send correct data again + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + endtask + + //////////////////////////////////////////////////////////////////////////////////7 + // Main Loop + //////////////////////////////////////////////////////////////////////////////////7 + longint unsigned total_error_cnt = 0; + + initial begin + reset_metrics(); + + // Check normal operation + fault_type = NONE; + enable = 0; + repeat (10 * TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + // Check fault tolerance + fault_type = DATA_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and data faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = VALID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and valid fault, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = READY_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = ID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + // Measure throughput + fault_type = NONE; + enable = 0; + in_hs_max_starvation = 0; + out_hs_max_starvation = 0; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); + $finish(error_cnt); end endmodule diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 10e78f56..0f644cbd 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -1,23 +1,29 @@ -module tb_time_dmr_retry_lock; - // Clock Parameters - - timeunit 1ns; - timeprecision 10ps; - - localparam time CLK_PERIOD = 10ns; - localparam time APPLICATION_DELAY = 2ns; - localparam time AQUISITION_DELAY = 8ns; - localparam unsigned RST_CLK_CYCLES = 10; - localparam unsigned TESTS = 10000; - - // Parameters - parameter int NumOpgroups = 3; - parameter int OpgroupWidth = 2; - parameter int IDSize = 5; - localparam int LockTimeout = 5; - - // Data Type with Tag for testbench - typedef logic [7:0] data_t; +`include "common_cells/registers.svh" + +module tb_time_dmr_retry_lock #( + // DUT Parameters + parameter int LockTimeout = 5, + parameter int NumOpgroups = 3, + parameter int OpgroupWidth = $clog2(NumOpgroups), + parameter int IDSize = 5, + parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, + + // TB Parameters + parameter int unsigned TESTS = 10000, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns +) ( /* no ports on TB */ ); + + `include "tb_time.svh" + + //////////////////////////////////////////////////////////////////////////////////7 + // DUT (s) + //////////////////////////////////////////////////////////////////////////////////7 + + typedef logic [7:0] data_t; + typedef logic [OpgroupWidth-1:0] operation_t; + typedef logic [IDSize-1:0] id_t; typedef logic [7:0] tag_t; typedef struct packed { @@ -25,202 +31,305 @@ module tb_time_dmr_retry_lock; tag_t tag; } tagged_data_t; - // Testebench signals - tagged_data_t golden_queue [NumOpgroups-1:0][$]; - tagged_data_t data_golden, data_actual; - logic [OpgroupWidth-1:0] operation_actual; - - logic error; - int error_cnt; - - // Aux signals to show what faults are going on - enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + // Typedef for stacked signal in TMR + typedef struct packed { + tagged_data_t data; + operation_t operation; + } tmr_stacked_t; - // Signals for DUTS - logic clk; - logic rst_n; + // Typedef for stacked signal in TMR + typedef struct packed { + id_t id; + tagged_data_t data; + } rr_stacked_t; + + // Input & Output + tagged_data_t data_in, data_out; + operation_t operation_in, operation_out; + + // Fault Connections for Injection + tagged_data_t data_fault; + logic valid_fault; + logic ready_fault; + id_t id_fault; + + // Signals for after TMR + tmr_stacked_t in_tmr_stack_redundant; + logic in_valid_redundant, in_ready_redundant; + id_t in_id_redundant; + + // Feedback connection + id_t id_retry, next_id; + logic valid_retry; + logic ready_retry; + + // Connection between retry and DMR + tmr_stacked_t data_retry2dmr; + id_t id_retry2dmr; + logic valid_retry2dmr; + logic ready_retry2dmr; + + // Input connection + tmr_stacked_t in_tmr_stack; + assign in_tmr_stack.data = data_in; + assign in_tmr_stack.operation = operation_in; + + // DUT Instances + retry_start #( + .DataType(tmr_stacked_t), + .IDSize(IDSize) + ) i_retry_start ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(in_tmr_stack), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_retry2dmr), + .id_o(id_retry2dmr), + .valid_o(valid_retry2dmr), + .ready_i(ready_retry2dmr), + + // Retry Connection + .retry_id_i(id_retry), + .retry_valid_i(valid_retry), + .retry_ready_o(ready_retry) + ); - tagged_data_t in_data; - logic [OpgroupWidth-1:0] in_operation; - logic in_valid, in_ready; - tagged_data_t data_error; - logic valid_error, ready_error; - logic [IDSize-1:0] id_error; + time_DMR_start #( + .DataType(tmr_stacked_t), + .IDSize (IDSize), + .UseExternalId(1) + ) i_time_DMR_start ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_o(next_id), + + // Upstream connection + .data_i(data_retry2dmr), + .id_i(id_retry2dmr), + .valid_i(valid_retry2dmr), + .ready_o(ready_retry2dmr), + + // Downstream connection + .data_o(in_tmr_stack_redundant), + .id_o (in_id_redundant), + .valid_o(in_valid_redundant), + .ready_i(in_ready_redundant) + ); - tagged_data_t out_data; - logic [OpgroupWidth-1:0] out_operation; - logic out_valid, out_ready; + // Handshake signal array for opgroup block + logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; + rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; + rr_stacked_t out_rr_stack; + + // Pass ready up based on the current operation_i + assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; + + for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups + localparam NUM_REGS = OpgroupNumRegs[opgrp]; + + // Input pipeline signals, index i holds signal after i register stages + tagged_data_t [0:NUM_REGS] pipe_data; + logic [0:NUM_REGS] pipe_valid; + logic [0:NUM_REGS] pipe_ready; + id_t [0:NUM_REGS] pipe_id; + + // Upstream Connection + // Error Injection + assign pipe_valid[0] = (in_valid_redundant ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_fault; + assign pipe_id[0] = in_id_redundant ^ id_fault; + assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_fault; + + // Generate the register stages + for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline + // Internal register enable for this stage + logic reg_ena; + + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; + + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk, rst_n) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFLARN(pipe_data[i+1], pipe_data[i], reg_ena, tagged_data_t'('0), clk, rst_n) + `FFLARN( pipe_id[i+1], pipe_id[i], reg_ena, id_t'('0), clk, rst_n) + end - // Clock Generation - initial begin - clk = '1; - rst_n = '0; - repeat (10) @(posedge clk); - rst_n = 1; + // Downstream connection + assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; + assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; end - always #((CLK_PERIOD/2)) clk = ~clk; - - // Instantiation of full fpnew datapath dut - tb_time_dmr_retry_lock_dut #( - .DataType(tagged_data_t), - .NumOpgroups(NumOpgroups), - .OpgroupWidth(OpgroupWidth), - .IDSize(IDSize), - .LockTimeout(LockTimeout), - .OpgroupNumRegs({8'd4, 8'd3, 8'd3}) - ) dut ( - .clk_i(clk), - .rst_ni(rst_n), - - // Ustream - .operation_i(in_operation), - .data_i(in_data), - .valid_i(in_valid), - .ready_o(in_ready), - - .data_error_i (data_error), - .valid_error_i (valid_error), - .ready_error_i (ready_error), - .id_error_i (id_error), - - // Downstream - .operation_o(out_operation), - .data_o(out_data), - .valid_o(out_valid), - .ready_i(out_ready) + // Signals for after RR + logic out_tmr_valid, out_tmr_ready; + tmr_stacked_t out_tmr_stack; + + // Backpropagating lock signal + logic lock; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree_lock #( + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i(clk), + .rst_ni(rst_n), + .flush_i('0), + .rr_i ('0), + .lock_rr_i (lock), + + // Upstream connection + .req_i(out_opgrp_valid), + .gnt_o(out_opgrp_ready), + .data_i(out_opgrp_rr_stack), + + // Downstream connection + .gnt_i(out_tmr_ready), + .req_o(out_tmr_valid), + .data_o(out_rr_stack), + .idx_o(out_tmr_stack.operation) ); - // Data Application - initial begin - logic [OpgroupWidth-1:0] operation_new; - tag_t tag_new; - tagged_data_t data_new; - - tag_new = 0; - - // Initialize Handshake and Data - in_data = 8'h00; - in_operation = '0; - in_valid = 1'b0; + // Signals for after TMR + tmr_stacked_t out_stacked; + logic [IDSize-1:0] out_tmr_id; - // Wait for reset to be lifted - @(posedge rst_n); + assign out_tmr_id = out_rr_stack.id; + assign out_tmr_stack.data = out_rr_stack.data; - forever begin - // Wait random time (with no valid data) - repeat ($urandom_range(1, 10)) begin - @(posedge clk); - # APPLICATION_DELAY; - in_valid = '0; - end - - // Build next data element - operation_new = $urandom_range(0, NumOpgroups-1); - tag_new += 1; - data_new.data = $random; - data_new.tag = tag_new; + // Connection between retry and DMR + tmr_stacked_t data_dmr2retry; + logic [IDSize-1:0] id_dmr2retry; + logic needs_retry_dmr2retry; + logic valid_dmr2retry; + logic ready_dmr2retry; - // Apply Data - in_data = data_new; - in_operation = operation_new; - in_valid = '1; + time_DMR_end #( + .DataType(tmr_stacked_t), + .LockTimeout(LockTimeout), + .IDSize (IDSize) + ) i_time_DMR_end ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + .next_id_i(next_id), + + // Upstream connection + .data_i(out_tmr_stack), + .id_i (out_tmr_id), + .valid_i(out_tmr_valid), + .ready_o(out_tmr_ready), + + // Lock connection to upstream + .lock_o(lock), + + // Downstream connection + .data_o(data_dmr2retry), + .id_o(id_dmr2retry), + .needs_retry_o(needs_retry_dmr2retry), + .valid_o(valid_dmr2retry), + .ready_i(ready_dmr2retry), + + // Flag output + .fault_detected_o(/*Unused*/) + ); - // Save data to Queue - golden_queue[operation_new].push_back(data_new); + retry_end #( + .DataType(tmr_stacked_t), + .IDSize(IDSize) + ) i_retry_end ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_dmr2retry), + .id_i(id_dmr2retry), + .needs_retry_i(needs_retry_dmr2retry), + .valid_i(valid_dmr2retry), + .ready_o(ready_dmr2retry), + + // Downstream connection + .data_o(out_stacked), + .valid_o(valid_out), + .ready_i(ready_out), + + // Retry Connection + .retry_id_o(id_retry), + .retry_valid_o(valid_retry), + .retry_ready_i(ready_retry) + ); - // Wait for handshake and as soon as it happens invalidate data - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!in_ready) begin - @(posedge clk); - # AQUISITION_DELAY; - end; + assign data_out = out_stacked.data; + assign operation_out = out_stacked.operation; - end - end + //////////////////////////////////////////////////////////////////////////////////7 + // Data Input + //////////////////////////////////////////////////////////////////////////////////7 + tagged_data_t golden_queue [NumOpgroups-1:0][$]; + tag_t tag_new; - // Fault inject initial begin - for (logic [2:0] ft = 0; ft < 5; ft++) begin - fault_type[2:0] = ft; - $display("Starting Test with fault type {%s}", fault_type.name()); - - repeat (TESTS) begin - - // Send correct data for some cycles to space errors - repeat ($urandom_range(55, 65)) begin - @(posedge clk); - # (APPLICATION_DELAY); - fault_current = NONE; - data_error = '0; - valid_error = '0; - ready_error = '0; - id_error = '0; - end - - // Send wrong data - @(posedge clk); - # (APPLICATION_DELAY); - fault_current <= fault_type; - data_error <= '0; - valid_error <= '0; - ready_error <= '0; - case (fault_type) - DATA_ERROR: data_error <= $random; - VALID_ERROR: valid_error <= 1; - READY_ERROR: ready_error <= 1; - ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); - endcase - end - $display("Ending Test with fault type {%s}", fault_type.name()); + tag_new = 0; + forever begin + input_handshake_begin(); + tag_new += 1; + operation_in = $urandom_range(0, NumOpgroups-1);; + data_in.data = $random; + data_in.tag = tag_new; + golden_queue[operation_in].push_back(data_in); + input_handshake_end(); end - $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); - $finish(error_cnt); end + //////////////////////////////////////////////////////////////////////////////////7 + // Data Output + //////////////////////////////////////////////////////////////////////////////////7 + tagged_data_t data_golden, data_actual; + operation_t operation_actual; + logic error; // Helper signal so one can quickly scroll to errors in questa + logic found; + longint unsigned error_cnt = 0; + + // Progress reporting + task reset_metrics(); + reset(); + error_cnt = 0; + in_hs_count = 0; + out_hs_count = 0; + for (int i = 0; i < NumOpgroups; i++) begin + golden_queue[i].delete(); + end + endtask - // Aquisition & Validation initial begin - logic found; - $timeformat(-9, 0, " ns", 20); - - - // Initialize Handshake - out_ready = '0; - - // Wait for reset to be lifted - @(posedge rst_n); - forever begin - // Wait random time (while not ready) - repeat ($urandom_range(1, 10)) begin - @(posedge clk); - # APPLICATION_DELAY; - out_ready = '0; - end - - // Set ready - out_ready = '1; - - // Wait for handshake - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!out_valid) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - - // Once it happened check if output was good and reset ready again - data_actual = out_data; - operation_actual = out_operation; - if (golden_queue[out_operation].size() > 0) begin + output_handshake_start(); + data_actual = data_out; + operation_actual = operation_out; + if (golden_queue[operation_actual].size() > 0) begin // Try to find an element with matching tag and consume it found = 0; - repeat (golden_queue[out_operation].size()) begin - data_golden = golden_queue[out_operation].pop_front(); + repeat (golden_queue[operation_actual].size()) begin + data_golden = golden_queue[operation_actual].pop_front(); if (data_golden.tag == data_actual.tag) begin found = 1; @@ -235,7 +344,7 @@ module tb_time_dmr_retry_lock; end end else begin - golden_queue[out_operation].push_back(data_golden); + golden_queue[operation_actual].push_back(data_golden); end end @@ -255,12 +364,128 @@ module tb_time_dmr_retry_lock; // Check that no queue runs out of bounds for (int operation = 0; operation < NumOpgroups; operation++) begin if (golden_queue[operation].size() > 2 ** IDSize) begin - $display("[T=%t] Data does not get output in a timely manner!", $time); + $display("[T=%t] Data does not get output in a timely manner for operation %d!", $time, operation); error = 1; error_cnt += 1; end end + output_handshake_end(); + end + end + + //////////////////////////////////////////////////////////////////////////////////7 + // Fault Injection + //////////////////////////////////////////////////////////////////////////////////7 + + longint unsigned min_fault_delay = 45; + longint unsigned max_fault_delay = 55; + + // Signals to show what faults are going on + enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; + + initial data_fault = '0; + initial valid_fault = '0; + initial ready_fault = '0; + initial id_fault = '0; + + task inject_fault(); + // Send correct data for some cycles to space errors + repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; end + + // Send wrong data + fault_current = fault_type; + case (fault_type) + DATA_FAULT: data_fault = $random; + VALID_FAULT: valid_fault = 1; + READY_FAULT: ready_fault = 1; + ID_FAULT: id_fault = (1 << $urandom_range(0,IDSize-1)); + endcase + + // Send correct data again + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + endtask + + //////////////////////////////////////////////////////////////////////////////////7 + // Main Loop + //////////////////////////////////////////////////////////////////////////////////7 + longint unsigned total_error_cnt = 0; + + initial begin + reset_metrics(); + + // Check normal operation + fault_type = NONE; + enable = 0; + repeat (10 * TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + // Check fault tolerance + fault_type = DATA_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and data faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = VALID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and valid fault, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = READY_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = ID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + // Measure throughput + fault_type = NONE; + enable = 0; + in_hs_max_starvation = 0; + out_hs_max_starvation = 0; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); + $finish(error_cnt); end -endmodule : tb_time_dmr_retry_lock \ No newline at end of file + +endmodule diff --git a/test/tb_time_dmr_retry_lock_dut.sv b/test/tb_time_dmr_retry_lock_dut.sv deleted file mode 100644 index eca94e2a..00000000 --- a/test/tb_time_dmr_retry_lock_dut.sv +++ /dev/null @@ -1,276 +0,0 @@ -`include "../../common_cells/include/common_cells/registers.svh" - - -module tb_time_dmr_retry_lock_dut # ( - // What kind of data signal to pass through the chain - parameter type DataType = logic, - parameter int LockTimeout = 5, - - // How many parallel instances to generate and how many registers they should each have - parameter int NumOpgroups = 3, - parameter int OpgroupWidth = 2, - parameter int IDSize = 4, - parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd10, 8'd10, 8'd10} -) ( - input logic clk_i, - input logic rst_ni, - - // Upstream connection - input logic [OpgroupWidth-1:0] operation_i, - input DataType data_i, - input logic valid_i, - output logic ready_o, - - // Error Injection - input DataType data_error_i, - input logic [IDSize-1:0] id_error_i, - input logic valid_error_i, - input logic ready_error_i, - - // Downstream connection - output logic [OpgroupWidth-1:0] operation_o, - output DataType data_o, - output logic valid_o, - input logic ready_i -); - - // Typedef for stacked signal in TMR - typedef struct packed { - DataType data; - logic [$bits(operation_i)-1:0] operation; - } tmr_stacked_t; - - // Typedef for stacked signal in TMR - typedef struct packed { - logic [IDSize-1:0] id; - DataType data; - } rr_stacked_t; - - // Input connection - tmr_stacked_t in_tmr_stack; - assign in_tmr_stack.data = data_i; - assign in_tmr_stack.operation = operation_i; - - // Signals for after TMR - tmr_stacked_t in_tmr_stack_redundant; - logic in_valid_redundant, in_ready_redundant; - logic [IDSize-1:0] in_id_redundant; - - // Feedback connection - logic [IDSize-1:0] id_retry, next_id; - logic valid_retry; - logic ready_retry; - - // Connection between retry and DMR - tmr_stacked_t data_retry2dmr; - logic [IDSize-1:0] id_retry2dmr; - logic valid_retry2dmr; - logic ready_retry2dmr; - - // DUT Instances - retry_start #( - .DataType(tmr_stacked_t), - .IDSize(IDSize) - ) i_retry_start ( - .clk_i(clk_i), - .rst_ni(rst_ni), - - // Upstream connection - .data_i(in_tmr_stack), - .valid_i(valid_i), - .ready_o(ready_o), - - // Downstream connection - .data_o(data_retry2dmr), - .id_o(id_retry2dmr), - .valid_o(valid_retry2dmr), - .ready_i(ready_retry2dmr), - - // Retry Connection - .retry_id_i(id_retry), - .retry_valid_i(valid_retry), - .retry_ready_o(ready_retry) - ); - - - time_DMR_start #( - .DataType(tmr_stacked_t), - .IDSize (IDSize), - .UseExternalId(1) - ) i_time_DMR_start ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .enable_i(1'b1), - - .next_id_o(next_id), - - // Upstream connection - .data_i(data_retry2dmr), - .id_i(id_retry2dmr), - .valid_i(valid_retry2dmr), - .ready_o(ready_retry2dmr), - - // Downstream connection - .data_o(in_tmr_stack_redundant), - .id_o (in_id_redundant), - .valid_o(in_valid_redundant), - .ready_i(in_ready_redundant) - ); - - // Handshake signal array for opgroup block - logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; - rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; - rr_stacked_t out_rr_stack; - - // Pass ready up based on the current operation_i - assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; - - for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups - localparam NUM_REGS = OpgroupNumRegs[opgrp]; - - // Input pipeline signals, index i holds signal after i register stages - - DataType [0:NUM_REGS] pipe_data; - logic [0:NUM_REGS] pipe_valid; - logic [0:NUM_REGS] pipe_ready; - logic [0:NUM_REGS][IDSize-1:0] pipe_id; - - // Upstream Connection - // Error Injection - assign pipe_valid[0] = (in_valid_redundant ^ valid_error_i) && (opgrp == in_tmr_stack_redundant.operation); - assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_error_i; - assign pipe_id[0] = in_id_redundant ^ id_error_i; - assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_error_i; - - // Generate the register stages - for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline - // Internal register enable for this stage - logic reg_ena; - - // Determine the ready signal of the current stage - advance the pipeline: - // 1. if the next stage is ready for our data - // 2. if the next stage only holds a bubble (not valid) -> we can pop it - assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; - - // Valid: enabled by ready signal, synchronous clear with the flush signal - `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk_i, rst_ni) - // Enable register if pipleine ready and a valid data item is present - assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; - // Generate the pipeline registers within the stages, use enable-registers - `FFL(pipe_data[i+1], pipe_data[i], reg_ena, DataType'('0)) - `FFL( pipe_id[i+1], pipe_id[i], reg_ena, IDSize'('0)) - end - - // Downstream connection - assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; - assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; - assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; - assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; - end - - // Signals for after RR - logic out_tmr_valid, out_tmr_ready; - tmr_stacked_t out_tmr_stack; - - // Backpropagating lock signal - logic lock; - - // Round-Robin arbiter to decide which result to use - rr_arb_tree_lock #( - .NumIn ( NumOpgroups ), - .DataType ( rr_stacked_t ), - .AxiVldRdy ( 1'b1 ) - ) i_arbiter ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .flush_i('0), - .rr_i ('0), - .lock_rr_i (lock), - - // Upstream connection - .req_i(out_opgrp_valid), - .gnt_o(out_opgrp_ready), - .data_i(out_opgrp_rr_stack), - - // Downstream connection - .gnt_i(out_tmr_ready), - .req_o(out_tmr_valid), - .data_o(out_rr_stack), - .idx_o(out_tmr_stack.operation) - ); - - - // Signals for after TMR - tmr_stacked_t out_stacked; - logic [IDSize-1:0] out_tmr_id; - - assign out_tmr_id = out_rr_stack.id; - assign out_tmr_stack.data = out_rr_stack.data; - - // Connection between retry and DMR - tmr_stacked_t data_dmr2retry; - logic [IDSize-1:0] id_dmr2retry; - logic needs_retry_dmr2retry; - logic valid_dmr2retry; - logic ready_dmr2retry; - - time_DMR_end #( - .DataType(tmr_stacked_t), - .LockTimeout(LockTimeout), - .IDSize (IDSize) - ) i_time_DMR_end ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .enable_i(1'b1), - - .next_id_i(next_id), - - // Upstream connection - .data_i(out_tmr_stack), - .id_i (out_tmr_id), - .valid_i(out_tmr_valid), - .ready_o(out_tmr_ready), - - // Lock connection to upstream - .lock_o(lock), - - // Downstream connection - .data_o(data_dmr2retry), - .id_o(id_dmr2retry), - .needs_retry_o(needs_retry_dmr2retry), - .valid_o(valid_dmr2retry), - .ready_i(ready_dmr2retry), - - // Flag output - .fault_detected_o(/*Unused*/) - ); - - retry_end #( - .DataType(tmr_stacked_t), - .IDSize(IDSize) - ) i_retry_end ( - .clk_i(clk_i), - .rst_ni(rst_ni), - - // Upstream connection - .data_i(data_dmr2retry), - .id_i(id_dmr2retry), - .needs_retry_i(needs_retry_dmr2retry), - .valid_i(valid_dmr2retry), - .ready_o(ready_dmr2retry), - - // Downstream connection - .data_o(out_stacked), - .valid_o(valid_o), - .ready_i(ready_i), - - // Retry Connection - .retry_id_o(id_retry), - .retry_valid_o(valid_retry), - .retry_ready_i(ready_retry) - ); - - assign data_o = out_stacked.data; - assign operation_o = out_stacked.operation; - -endmodule: tb_time_dmr_retry_lock_dut diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index 9561934d..7ac55e67 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -1,47 +1,33 @@ -module tb_time_tmr; +module tb_time_tmr #( + // DUT Parameters + parameter IDSize = 4, + parameter int LockTimeout = 4, - // Clock Parameters - localparam time CLK_PERIOD = 10ns; - localparam time APPLICATION_DELAY = 2ns; - localparam time AQUISITION_DELAY = 8ns; - localparam unsigned RST_CLK_CYCLES = 10; - localparam unsigned TESTS = 10000; + // TB Parameters + parameter int unsigned TESTS = 10000, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns - // Parameters - typedef logic [7:0] data_t; - parameter IDSize = 4; - localparam int LockTimeout = 4; - - // Testbench signals - data_t golden_queue [$]; - data_t data_golden, data_actual; - logic error; - int error_cnt; +) ( /* no ports on TB */ ); + + `include "tb_time.svh" - // Aux signals to show what faults are going on - enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; + //////////////////////////////////////////////////////////////////////////////////7 + // DUT (s) + //////////////////////////////////////////////////////////////////////////////////7 - // Signals for DUTS - logic clk; - logic rst_n; - logic enable; - - data_t data_in, data_redundant, data_error, data_redundant_faulty, data_out; - logic valid_in, valid_redundant, valid_error, valid_redundant_faulty, valid_out; - logic ready_in, ready_redundant, ready_error, ready_redundant_faulty, ready_out; - logic [IDSize-1:0] id_redundant, id_error, id_redundant_faulty; + typedef logic [7:0] data_t; - // Clock Generation - initial begin - clk = '1; - rst_n = '0; - repeat (10) @(posedge clk); - rst_n = 1; - end + // Input & Output + data_t data_in, data_out; - always #((CLK_PERIOD/2)) clk = ~clk; + // Internal Connections + data_t data_redundant, data_fault, data_redundant_faulty; + logic valid_redundant, valid_fault, valid_redundant_faulty; + logic ready_redundant, ready_fault, ready_redundant_faulty; + logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty; - // DUT Instances time_TMR_start #( .DataType(data_t), .IDSize(IDSize) @@ -62,12 +48,6 @@ module tb_time_tmr; .ready_i(ready_redundant_faulty) ); - // Error XORs - assign data_redundant_faulty = data_redundant ^ data_error; - assign valid_redundant_faulty = valid_redundant ^ valid_error; - assign ready_redundant_faulty = ready_redundant ^ ready_error; - assign id_redundant_faulty = id_redundant ^ id_error; - time_TMR_end #( .DataType(data_t), .LockTimeout(LockTimeout), @@ -93,126 +73,43 @@ module tb_time_tmr; .fault_detected_o(/* Unused */) ); - // Data Application - initial begin - // Initialize Handshake and Data - data_in = 8'h00; - valid_in = 1'b0; - - // Wait for reset to be lifted - @(posedge rst_n); + //////////////////////////////////////////////////////////////////////////////////7 + // Data Input + //////////////////////////////////////////////////////////////////////////////////7 + data_t golden_queue [$]; + initial begin forever begin - // Wait random time (with no valid data) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - valid_in <= '0; - end - - valid_in <= '1; + input_handshake_begin(); data_in = $random; golden_queue.push_back(data_in); - - // Wait for handshake and as soon as it happens invalidate data - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!ready_in) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - + input_handshake_end(); end end - // Enable / Disable ECC - initial begin - enable = 1'b0; - $display("Disabled Redundancy"); - repeat (TESTS * 5) begin - @(posedge clk); - end - $display("Enabled Redundancy"); - enable = 1'b1; - end - - // Fault inject - initial begin - for (logic [2:0] ft = 0; ft < 5; ft++) begin - fault_type[2:0] = ft; - $display("Starting Test with fault type {%s}", fault_type.name()); - - repeat (TESTS) begin - - // Send correct data for some cycles to space errors - repeat ($urandom_range(15, 20)) begin - @(posedge clk); - # (APPLICATION_DELAY); - fault_current = NONE; - data_error = '0; - valid_error = '0; - ready_error = '0; - id_error = '0; - end - - // Send wrong data - @(posedge clk); - # (APPLICATION_DELAY); - fault_current <= fault_type; - data_error <= '0; - valid_error <= '0; - ready_error <= '0; - id_error <= '0; - case (fault_type) - DATA_ERROR: data_error <= $random; - VALID_ERROR: valid_error <= 1; - READY_ERROR: ready_error <= 1; - ID_ERROR: id_error <= $random; - endcase - end - $display("Ending Test with fault type {%s}", fault_type.name()); - end - $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); - $finish(error_cnt); - end + //////////////////////////////////////////////////////////////////////////////////7 + // Data Output + //////////////////////////////////////////////////////////////////////////////////7 + data_t data_golden, data_actual; + logic error; // Helper signal so one can quickly scroll to errors in questa + longint unsigned error_cnt = 0; + // Progress reporting + task reset_metrics(); + reset(); + error_cnt = 0; + in_hs_count = 0; + out_hs_count = 0; + golden_queue.delete(); + endtask - // Aquisition & Validation initial begin $timeformat(-9, 0, " ns", 20); - - // Initialize error metrics - error = 0; // Signal so errors can easily be scrolled to in wave - error_cnt = 0; - - // Initialize Handshake - ready_out = '0; - - // Wait for reset to be lifted - @(posedge rst_n); - forever begin - // Wait random time (while not ready) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - ready_out <= '0; - end - - // Set ready - ready_out <= '1; - - // Wait for handshake - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!valid_out) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - - // Once it happened check if output was good and reset ready again + output_handshake_start(); data_actual = data_out; if (golden_queue.size() > 0) begin data_golden = golden_queue.pop_front(); - // Check output if (data_actual != data_golden) begin $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); error = 1; @@ -225,7 +122,128 @@ module tb_time_tmr; error = 1; error_cnt += 1; end + output_handshake_end(); end end + //////////////////////////////////////////////////////////////////////////////////7 + // Fault Injection + //////////////////////////////////////////////////////////////////////////////////7 + + longint unsigned min_fault_delay = 15; + longint unsigned max_fault_delay = 20; + + // Signals to show what faults are going on + enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; + + assign data_redundant_faulty = data_redundant ^ data_fault; + assign valid_redundant_faulty = valid_redundant ^ valid_fault; + assign ready_redundant_faulty = ready_redundant ^ ready_fault; + assign id_redundant_faulty = id_redundant ^ id_fault; + + initial data_fault = '0; + initial valid_fault = '0; + initial ready_fault = '0; + initial id_fault = '0; + + task inject_fault(); + // Send correct data for some cycles to space errors + repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + end + + // Send wrong data + fault_current = fault_type; + case (fault_type) + DATA_FAULT: data_fault = $random; + VALID_FAULT: valid_fault = 1; + READY_FAULT: ready_fault = 1; + ID_FAULT: id_fault = $random; + endcase + + // Send correct data again + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + endtask + + //////////////////////////////////////////////////////////////////////////////////7 + // Main Loop + //////////////////////////////////////////////////////////////////////////////////7 + longint unsigned total_error_cnt = 0; + + initial begin + reset_metrics(); + + // Check normal operation + fault_type = NONE; + enable = 0; + repeat (10 * TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + // Check fault tolerance + fault_type = DATA_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and data faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = VALID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and valid fault, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = READY_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = ID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + // Measure throughput + fault_type = NONE; + enable = 0; + in_hs_max_starvation = 0; + out_hs_max_starvation = 0; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); + $finish(error_cnt); + end + + endmodule diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index 00ca36d8..562cf159 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -1,206 +1,241 @@ -module tb_time_tmr_lock; - // Clock Parameters - - timeunit 1ns; - timeprecision 10ps; - - localparam time CLK_PERIOD = 10ns; - localparam time APPLICATION_DELAY = 2ns; - localparam time AQUISITION_DELAY = 8ns; - localparam unsigned RST_CLK_CYCLES = 10; - localparam unsigned TESTS = 10000; - - // Parameters - typedef logic [7:0] data_t; - parameter int NumOpgroups = 3; - parameter int OpgroupWidth = 2; - parameter int IDSize = 4; - localparam int LockTimeout = 5; - - // Testebench signals - data_t golden_queue [NumOpgroups-1:0][$]; - data_t data_golden, data_actual; - logic [OpgroupWidth-1:0] operation_actual; - logic error; - int error_cnt; - - // Aux signals to show what faults are going on - enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; +`include "common_cells/registers.svh" + +module tb_time_tmr_lock #( + // DUT Parameters + parameter int LockTimeout = 5, + parameter int NumOpgroups = 3, + parameter int OpgroupWidth = $clog2(NumOpgroups), + parameter int IDSize = 5, + parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, + + // TB Parameters + parameter int unsigned TESTS = 10000, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns +) ( /* no ports on TB */ ); + + `include "tb_time.svh" + + //////////////////////////////////////////////////////////////////////////////////7 + // DUT (s) + //////////////////////////////////////////////////////////////////////////////////7 + + typedef logic [7:0] data_t; + typedef logic [OpgroupWidth-1:0] operation_t; + typedef logic [IDSize-1:0] id_t; + + // Typedef for stacked signal in TMR + typedef struct packed { + data_t data; + operation_t operation; + } tmr_stacked_t; + + // Typedef for stacked signal in TMR + typedef struct packed { + id_t id; + data_t data; + } rr_stacked_t; + + // Input & Output + data_t data_in, data_out; + operation_t operation_in, operation_out; + + // Fault Connections for Injection + data_t data_fault; + logic valid_fault; + logic ready_fault; + id_t id_fault; + + tmr_stacked_t in_tmr_stack; + assign in_tmr_stack.data = data_in; + assign in_tmr_stack.operation = operation_in; + + // Signals for after TMR + tmr_stacked_t in_tmr_stack_redundant; + logic in_valid_redundant, in_ready_redundant; + id_t in_id_redundant; + + time_TMR_start #( + .DataType(tmr_stacked_t), + .IDSize (IDSize) + ) i_time_TMR_start ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + // Upstream connection + .data_i(in_tmr_stack), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(in_tmr_stack_redundant), + .id_o (in_id_redundant), + .valid_o(in_valid_redundant), + .ready_i(in_ready_redundant) + ); - // Signals for DUTS - logic clk; - logic rst_n; + // Handshake signal array for opgroup block + logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; + rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; + rr_stacked_t out_rr_stack; + + // Pass ready up based on the current operation_i + assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; + + for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups + localparam NUM_REGS = OpgroupNumRegs[opgrp]; + + // Input pipeline signals, index i holds signal after i register stages + data_t [0:NUM_REGS] pipe_data; + logic [0:NUM_REGS] pipe_valid; + logic [0:NUM_REGS] pipe_ready; + id_t [0:NUM_REGS] pipe_id; + + // Upstream Connection + // Error Injection + assign pipe_valid[0] = (in_valid_redundant ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_fault; + assign pipe_id[0] = in_id_redundant ^ id_fault; + assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_fault; + + // Generate the register stages + for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline + // Internal register enable for this stage + logic reg_ena; + + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; + + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk, rst_n) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFLARN(pipe_data[i+1], pipe_data[i], reg_ena, data_t'('0), clk, rst_n) + `FFLARN( pipe_id[i+1], pipe_id[i], reg_ena, id_t'('0), clk, rst_n) + end - data_t in_data; - logic [OpgroupWidth-1:0] in_operation; - logic in_valid, in_ready; + // Downstream connection + assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; + assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; + assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; + end - data_t data_error; - logic valid_error, ready_error; - logic [IDSize-1:0] id_error; + // Signals for after RR + logic out_tmr_valid, out_tmr_ready; + tmr_stacked_t out_tmr_stack; + + // Backpropagating lock signal + logic lock; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree_lock #( + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i(clk), + .rst_ni(rst_n), + .flush_i('0), + .rr_i ('0), + .lock_rr_i (lock), + + // Upstream connection + .req_i(out_opgrp_valid), + .gnt_o(out_opgrp_ready), + .data_i(out_opgrp_rr_stack), + + // Downstream connection + .gnt_i(out_tmr_ready), + .req_o(out_tmr_valid), + .data_o(out_rr_stack), + .idx_o(out_tmr_stack.operation) + ); - data_t out_data; - logic [OpgroupWidth-1:0] out_operation; - logic out_valid, out_ready; - // Clock Generation - initial begin - clk = '1; - rst_n = '0; - repeat (10) @(posedge clk); - rst_n = 1; - end + // Signals for after TMR + tmr_stacked_t out_stacked; + id_t out_tmr_id; - always #((CLK_PERIOD/2)) clk = ~clk; + assign out_tmr_id = out_rr_stack.id; + assign out_tmr_stack.data = out_rr_stack.data; - // Instantiation of full fpnew datapath dut - tb_time_tmr_lock_dut #( - .DataType(data_t), - .NumOpgroups(NumOpgroups), - .OpgroupWidth(OpgroupWidth), - .IDSize(IDSize), + time_TMR_end #( + .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), - .OpgroupNumRegs({8'd4, 8'd3, 8'd3}) - ) dut ( - .clk_i(clk), - .rst_ni(rst_n), - - // Ustream - .operation_i(in_operation), - .data_i(in_data), - .valid_i(in_valid), - .ready_o(in_ready), - - .data_error_i (data_error), - .valid_error_i (valid_error), - .ready_error_i (ready_error), - .id_error_i (id_error), - - // Downstream - .operation_o(out_operation), - .data_o(out_data), - .valid_o(out_valid), - .ready_i(out_ready) + .IDSize (IDSize) + ) i_time_TMR_end ( + .clk_i(clk), + .rst_ni(rst_n), + .enable_i(enable), + + // Upstream connection + .data_i(out_tmr_stack), + .id_i (out_tmr_id), + .valid_i(out_tmr_valid), + .ready_o(out_tmr_ready), + + // Downstream connection + .data_o(out_stacked), + .valid_o(valid_out), + .ready_i(ready_out), + .lock_o(lock), + + // Flags + .fault_detected_o(/* Unused */) ); + assign data_out = out_stacked.data; + assign operation_out = out_stacked.operation; - // Data Application - initial begin - data_t new_data; - logic [OpgroupWidth-1:0] new_operation; - - // Initialize Handshake and Data - in_data = 8'h00; - in_operation = '0; - in_valid = 1'b0; - - // Wait for reset to be lifted - @(posedge rst_n); + //////////////////////////////////////////////////////////////////////////////////7 + // Data Input + //////////////////////////////////////////////////////////////////////////////////7 + data_t golden_queue [NumOpgroups-1:0][$]; + initial begin forever begin - // Wait random time (with no valid data) - repeat ($urandom_range(1, 10)) begin - @(posedge clk); - # APPLICATION_DELAY; - in_valid = '0; - end - - // Build next data element - new_operation = $urandom_range(0, NumOpgroups-1); - new_data = $random; - - // Apply Data - in_data = new_data; - in_operation = new_operation; - in_valid = '1; - - // Save data for future - golden_queue[new_operation].push_back(new_data); - - // Wait for handshake and as soon as it happens invalidate data - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!in_ready) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - + input_handshake_begin(); + operation_in = $urandom_range(0, NumOpgroups-1);; + data_in = $random; + golden_queue[operation_in].push_back(data_in); + input_handshake_end(); end end - // Fault inject - initial begin - for (logic [2:0] ft = 0; ft < 5; ft++) begin - fault_type[2:0] = ft; - $display("Starting Test with fault type {%s}", fault_type.name()); - - repeat (TESTS) begin - - // Send correct data for some cycles to space errors - repeat ($urandom_range(45, 55)) begin - @(posedge clk); - # (APPLICATION_DELAY); - fault_current = NONE; - data_error = '0; - valid_error = '0; - ready_error = '0; - id_error = '0; - end - - // Send wrong data - @(posedge clk); - # (APPLICATION_DELAY); - fault_current <= fault_type; - data_error <= '0; - valid_error <= '0; - ready_error <= '0; - case (fault_type) - DATA_ERROR: data_error <= $random; - VALID_ERROR: valid_error <= 1; - READY_ERROR: ready_error <= 1; - ID_ERROR: id_error <= $random; - endcase - end - $display("Ending Test with fault type {%s}", fault_type.name()); + //////////////////////////////////////////////////////////////////////////////////7 + // Data Output + //////////////////////////////////////////////////////////////////////////////////7 + data_t data_golden, data_actual; + logic [OpgroupWidth-1:0] operation_actual; + logic error; // Helper signal so one can quickly scroll to errors in questa + longint unsigned error_cnt = 0; + + // Progress reporting + task reset_metrics(); + reset(); + error_cnt = 0; + in_hs_count = 0; + out_hs_count = 0; + for (int i = 0; i < NumOpgroups; i++) begin + golden_queue[i].delete(); end - $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); - $finish(error_cnt); - end + endtask - - // Aquisition & Validation initial begin $timeformat(-9, 0, " ns", 20); - - - // Initialize Handshake - out_ready = '0; - - // Wait for reset to be lifted - @(posedge rst_n); - forever begin - // Wait random time (while not ready) - repeat ($urandom_range(1, 10)) begin - @(posedge clk); - # APPLICATION_DELAY; - out_ready = '0; - end - - // Set ready - out_ready = '1; - - // Wait for handshake - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!out_valid) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - + output_handshake_start(); // Once it happened check if output was good and reset ready again - data_actual = out_data; - operation_actual = out_operation; - if (golden_queue[out_operation].size() > 0) begin - data_golden = golden_queue[out_operation].pop_front(); + data_actual = data_out; + operation_actual = operation_out; + if (golden_queue[operation_actual].size() > 0) begin + data_golden = golden_queue[operation_actual].pop_front(); if (data_actual != data_golden) begin $display("[T=%t] Operation %h -> Data %h Mismatch, should be %h", $time, operation_actual, data_actual, data_golden); error = 1; @@ -213,7 +248,123 @@ module tb_time_tmr_lock; error = 1; error_cnt += 1; end + output_handshake_end(); end end -endmodule : tb_time_tmr_lock \ No newline at end of file + //////////////////////////////////////////////////////////////////////////////////7 + // Fault Injection + //////////////////////////////////////////////////////////////////////////////////7 + + longint unsigned min_fault_delay = 45; + longint unsigned max_fault_delay = 55; + + // Signals to show what faults are going on + enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; + + initial data_fault = '0; + initial valid_fault = '0; + initial ready_fault = '0; + initial id_fault = '0; + + task inject_fault(); + // Send correct data for some cycles to space errors + repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + end + + // Send wrong data + fault_current = fault_type; + case (fault_type) + DATA_FAULT: data_fault = $random; + VALID_FAULT: valid_fault = 1; + READY_FAULT: ready_fault = 1; + ID_FAULT: id_fault = $random; + endcase + + // Send correct data again + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + endtask + + //////////////////////////////////////////////////////////////////////////////////7 + // Main Loop + //////////////////////////////////////////////////////////////////////////////////7 + longint unsigned total_error_cnt = 0; + + initial begin + reset_metrics(); + + // Check normal operation + fault_type = NONE; + enable = 0; + repeat (10 * TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + // Check fault tolerance + fault_type = DATA_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and data faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = VALID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and valid fault, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = READY_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = ID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + // Measure throughput + fault_type = NONE; + enable = 0; + in_hs_max_starvation = 0; + out_hs_max_starvation = 0; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_error_cnt += error_cnt; + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + reset_metrics(); + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); + $finish(error_cnt); + end + + +endmodule diff --git a/test/tb_time_tmr_lock_dut.sv b/test/tb_time_tmr_lock_dut.sv deleted file mode 100644 index c934d710..00000000 --- a/test/tb_time_tmr_lock_dut.sv +++ /dev/null @@ -1,198 +0,0 @@ -`include "../../common_cells/include/common_cells/registers.svh" - - -module tb_time_tmr_lock_dut # ( - // What kind of data signal to pass through the chain - parameter type DataType = logic, - parameter int LockTimeout = 5, - - // How many parallel instances to generate and how many registers they should each have - parameter int NumOpgroups = 3, - parameter int OpgroupWidth = 2, - parameter int IDSize = 4, - parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd10, 8'd10, 8'd10} -) ( - input logic clk_i, - input logic rst_ni, - - // Upstream connection - input logic [OpgroupWidth-1:0] operation_i, - input DataType data_i, - input logic valid_i, - output logic ready_o, - - // Error Injection - input DataType data_error_i, - input logic [IDSize-1:0] id_error_i, - input logic valid_error_i, - input logic ready_error_i, - - // Downstream connection - output logic [OpgroupWidth-1:0] operation_o, - output DataType data_o, - output logic valid_o, - input logic ready_i -); - - // Typedef for stacked signal in TMR - typedef struct packed { - DataType data; - logic [$bits(operation_i)-1:0] operation; - } tmr_stacked_t; - - // Typedef for stacked signal in TMR - typedef struct packed { - logic [IDSize-1:0] id; - DataType data; - } rr_stacked_t; - - // Input connection - tmr_stacked_t in_tmr_stack; - assign in_tmr_stack.data = data_i; - assign in_tmr_stack.operation = operation_i; - - // Signals for after TMR - tmr_stacked_t in_tmr_stack_redundant; - logic in_valid_redundant, in_ready_redundant; - logic [IDSize-1:0] in_id_redundant; - - time_TMR_start #( - .DataType(tmr_stacked_t), - .IDSize (IDSize) - ) i_time_TMR_start ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .enable_i(1'b1), - - // Upstream connection - .data_i(in_tmr_stack), - .valid_i(valid_i), - .ready_o(ready_o), - - // Downstream connection - .data_o(in_tmr_stack_redundant), - .id_o (in_id_redundant), - .valid_o(in_valid_redundant), - .ready_i(in_ready_redundant) - ); - - // Handshake signal array for opgroup block - logic [NumOpgroups-1:0] in_opgrp_ready, out_opgrp_valid, out_opgrp_ready; - rr_stacked_t [NumOpgroups-1:0] out_opgrp_rr_stack; - rr_stacked_t out_rr_stack; - - // Pass ready up based on the current operation_i - assign in_ready_redundant = in_valid_redundant & in_opgrp_ready[in_tmr_stack_redundant.operation]; - - for (genvar opgrp = 0; opgrp < int'(NumOpgroups); opgrp++) begin : gen_operation_groups - localparam NUM_REGS = OpgroupNumRegs[opgrp]; - - // Input pipeline signals, index i holds signal after i register stages - - DataType [0:NUM_REGS] pipe_data; - logic [0:NUM_REGS] pipe_valid; - logic [0:NUM_REGS] pipe_ready; - logic [0:NUM_REGS][IDSize-1:0] pipe_id; - - // Upstream Connection - // Error Injection - assign pipe_valid[0] = in_valid_redundant ^ valid_error_i && (opgrp == in_tmr_stack_redundant.operation); - assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_error_i; - assign pipe_id[0] = in_id_redundant ^ id_error_i; - assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_error_i; - - // Generate the register stages - for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline - // Internal register enable for this stage - logic reg_ena; - - // Determine the ready signal of the current stage - advance the pipeline: - // 1. if the next stage is ready for our data - // 2. if the next stage only holds a bubble (not valid) -> we can pop it - assign pipe_ready[i] = pipe_ready[i+1] | ~pipe_valid[i+1]; - - // Valid: enabled by ready signal, synchronous clear with the flush signal - `FFLARNC(pipe_valid[i+1], pipe_valid[i], pipe_ready[i], 1'b0, 1'b0, clk_i, rst_ni) - // Enable register if pipleine ready and a valid data item is present - assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; - // Generate the pipeline registers within the stages, use enable-registers - `FFL(pipe_data[i+1], pipe_data[i], reg_ena, DataType'('0)) - `FFL( pipe_id[i+1], pipe_id[i], reg_ena, IDSize'('0)) - end - - // Downstream connection - assign out_opgrp_valid[opgrp] = pipe_valid[NUM_REGS]; - assign out_opgrp_rr_stack[opgrp].data = pipe_data[NUM_REGS]; - assign out_opgrp_rr_stack[opgrp].id = pipe_id[NUM_REGS]; - assign pipe_ready[NUM_REGS] = out_opgrp_ready[opgrp]; - end - - // Signals for after RR - logic out_tmr_valid, out_tmr_ready; - tmr_stacked_t out_tmr_stack; - - // Backpropagating lock signal - logic lock; - - // Round-Robin arbiter to decide which result to use - rr_arb_tree_lock #( - .NumIn ( NumOpgroups ), - .DataType ( rr_stacked_t ), - .AxiVldRdy ( 1'b1 ) - ) i_arbiter ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .flush_i('0), - .rr_i ('0), - .lock_rr_i (lock), - - // Upstream connection - .req_i(out_opgrp_valid), - .gnt_o(out_opgrp_ready), - .data_i(out_opgrp_rr_stack), - - // Downstream connection - .gnt_i(out_tmr_ready), - .req_o(out_tmr_valid), - .data_o(out_rr_stack), - .idx_o(out_tmr_stack.operation) - ); - - - // Signals for after TMR - tmr_stacked_t out_stacked; - logic [IDSize-1:0] out_tmr_id; - - assign out_tmr_id = out_rr_stack.id; - assign out_tmr_stack.data = out_rr_stack.data; - - - time_TMR_end #( - .DataType(tmr_stacked_t), - .LockTimeout(LockTimeout), - .IDSize (IDSize) - ) i_time_TMR_end ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .enable_i(1'b1), - - // Upstream connection - .data_i(out_tmr_stack), - .id_i (out_tmr_id), - .valid_i(out_tmr_valid), - .ready_o(out_tmr_ready), - - // Downstream connection - .data_o(out_stacked), - .valid_o(valid_o), - .ready_i(ready_i), - .lock_o(lock), - - // Flags - .fault_detected_o(/* Unused */) - ); - - assign data_o = out_stacked.data; - assign operation_o = out_stacked.operation; - -endmodule: tb_time_tmr_lock_dut \ No newline at end of file From 56afc3c96533be90b694c805af2b5f733de05b7e Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 09:04:24 +0200 Subject: [PATCH 03/21] Added in-order time-based retry modules. --- Bender.yml | 5 +- rtl/time_redundancy/retry_inorder_end.sv | 69 ++++++ rtl/time_redundancy/retry_inorder_start.sv | 103 +++++++++ run_tests.sh | 1 + src_files.yml | 2 + test/tb_retry_inorder.sv | 234 +++++++++++++++++++++ 6 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 rtl/time_redundancy/retry_inorder_end.sv create mode 100644 rtl/time_redundancy/retry_inorder_start.sv create mode 100644 test/tb_retry_inorder.sv diff --git a/Bender.yml b/Bender.yml index ffce13a2..103aa136 100644 --- a/Bender.yml +++ b/Bender.yml @@ -26,7 +26,7 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_dec.sv - rtl/hsiao_ecc/hsiao_ecc_cor.sv - rtl/time_redundancy/retry_end.sv - - rtl/time_redundancy/retry_start.sv + - rtl/time_redundancy/retry_inorder_end.sv - rtl/time_redundancy/rr_arb_tree_lock.sv - rtl/time_redundancy/voters.svh - rtl/TMR_voter.sv @@ -37,6 +37,8 @@ sources: - rtl/ecc_wrap/ecc_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv + - rtl/time_redundancy/retry_inorder_start.sv + - rtl/time_redundancy/retry_start.sv - rtl/time_redundancy/time_TMR_end.sv - rtl/time_redundancy/time_TMR_start.sv - rtl/time_redundancy/time_DMR_end.sv @@ -110,6 +112,7 @@ sources: - test/tb_time_tmr.sv - test/tb_time_dmr.sv - test/tb_retry.sv + - test/tb_retry_inorder.sv - test/tb_time_dmr_retry.sv - test/tb_time_tmr_lock.sv - test/tb_time_dmr_retry_lock.sv diff --git a/rtl/time_redundancy/retry_inorder_end.sv b/rtl/time_redundancy/retry_inorder_end.sv new file mode 100644 index 00000000..f70a5595 --- /dev/null +++ b/rtl/time_redundancy/retry_inorder_end.sv @@ -0,0 +1,69 @@ +`include "common_cells/registers.svh" + +module retry_inorder_end # ( + parameter type DataType = logic, + parameter int IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input DataType data_i, + input logic [IDSize-1:0] id_i, + input logic needs_retry_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic valid_o, + input logic ready_i, + + // Retry Connection + output logic [IDSize-1:0] retry_id_o, + input logic [IDSize-1:0] retry_id_i, + output logic retry_valid_o, + output logic retry_lock_o, + input logic retry_ready_i +); + + // Signals do not change, only validity changes + assign retry_id_o = id_i; + assign data_o = data_i; + + logic [IDSize-1:0] failed_id_d, failed_id_q; + logic retry_d, retry_q; + + always_comb begin + if (valid_i & retry_q) begin + failed_id_d = failed_id_q; + retry_d = ~(failed_id_q == id_i); + end else if (valid_i & needs_retry_i) begin + failed_id_d = retry_id_i; + retry_d = 1; + end else begin + failed_id_d = failed_id_q; + retry_d = retry_q; + end + end + + assign retry_lock_o = retry_d; + + `FF(retry_q, retry_d, '0); + `FF(failed_id_q, failed_id_d, '0); + + always_comb begin + if (retry_d) begin + retry_valid_o = valid_i; + ready_o = retry_ready_i; + valid_o = 0; + end else begin + valid_o = valid_i; + ready_o = ready_i; + retry_valid_o = 0; + end + end + +endmodule + + diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv new file mode 100644 index 00000000..defe68bf --- /dev/null +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -0,0 +1,103 @@ +`include "common_cells/registers.svh" +`include "voters.svh" + +module retry_inorder_start # ( + parameter type DataType = logic, + parameter int IDSize = 1 +) ( + input logic clk_i, + input logic rst_ni, + + // Upstream connection + input DataType data_i, + input logic valid_i, + output logic ready_o, + + // Downstream connection + output DataType data_o, + output logic [IDSize-1:0] id_o, + output logic valid_o, + input logic ready_i, + + // Retry Connection + input logic [IDSize-1:0] retry_id_i, + output logic [IDSize-1:0] retry_id_o, + input logic retry_valid_i, + input logic retry_lock_i, + output logic retry_ready_o +); + + ////////////////////////////////////////////////////////////////////// + // Register to store failed id for one cycle + logic [IDSize-1:0] failed_id_d, failed_id_q; + logic failed_valid_d, failed_valid_q; + logic retry_lock_d, retry_lock_q; + + always_comb begin + if (ready_i | retry_valid_i) begin + failed_valid_d = retry_valid_i; + end else begin + failed_valid_d = failed_valid_q; + end + + if (retry_valid_i & retry_ready_o) begin + failed_id_d = retry_id_i; + end else begin + failed_id_d = failed_id_q; + end + end + + assign retry_lock_d = retry_lock_i; + `FF(failed_id_q, failed_id_d, '0); + `FF(failed_valid_q, failed_valid_d, '0); + `FF(retry_lock_q, retry_lock_d, '0); + + assign retry_ready_o = ready_i | ~failed_valid_q; + + ////////////////////////////////////////////////////////////////////// + // ID Counter with parity bit + + logic [IDSize-1:0] counter_id_d, counter_id_q; + + always_comb begin + if ((failed_valid_q | valid_i) & ready_i) begin + `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); + end else begin + counter_id_d = counter_id_q; + end + end + + `FF(counter_id_q, counter_id_d, 0); + + ////////////////////////////////////////////////////////////////////// + // General Element storage + + logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; + + always_comb begin + // Keep data as is as abase + data_storage_d = data_storage_q; + + if ((failed_valid_q | valid_i) & ready_i) begin + data_storage_d[counter_id_q[IDSize-2:0]] = data_o; + end + end + + `FF(data_storage_q, data_storage_d, 0); + + always_comb begin + if (failed_valid_q & ready_i) begin + data_o = data_storage_q[failed_id_q[IDSize-2:0]]; + end else begin + data_o = data_i; + end + id_o = counter_id_q; + retry_id_o = counter_id_d; + end + + ////////////////////////////////////////////////////////////////////// + // Handshake assignment + assign ready_o = ready_i & !failed_valid_q & !retry_lock_q; + assign valid_o = valid_i & !retry_lock_q | failed_valid_q; + +endmodule diff --git a/run_tests.sh b/run_tests.sh index 4060584f..b9c79388 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -55,6 +55,7 @@ call_vsim tb_time_tmr call_vsim tb_time_tmr_lock call_vsim tb_time_dmr call_vsim tb_retry +call_vsim tb_retry_inorder call_vsim tb_time_dmr_retry call_vsim tb_time_dmr_retry_lock -voptargs="+acc" diff --git a/src_files.yml b/src_files.yml index 72c50d8e..4c6b895f 100644 --- a/src_files.yml +++ b/src_files.yml @@ -10,6 +10,7 @@ redundancy_cells: lowrisc_ecc/prim_secded_72_64_dec.sv, lowrisc_ecc/prim_secded_72_64_enc.sv, rtl/time_redundancy/retry_end.sv + rtl/time_redundancy/retry_inorder_end.sv rtl/time_redundancy/rr_arb_tree_lock.sv rtl/time_redundancy/voters.svh rtl/TMR_voter.sv, @@ -31,6 +32,7 @@ redundancy_cells: rtl/BUS_enc_dec/TCDM_XBAR_bus_ecc_enc.sv, rlt/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv, rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv, + rtl/time_redundancy/retry_inorder_start.sv rtl/time_redundancy/retry_start.sv rtl/time_redundancy/time_TMR_end.sv rtl/time_redundancy/time_TMR_start.sv diff --git a/test/tb_retry_inorder.sv b/test/tb_retry_inorder.sv new file mode 100644 index 00000000..176f0ef6 --- /dev/null +++ b/test/tb_retry_inorder.sv @@ -0,0 +1,234 @@ +`include "common_cells/registers.svh" + +module tb_retry_inorder; + + // Clock Parameters + localparam time CLK_PERIOD = 10ns; + localparam time APPLICATION_DELAY = 2ns; + localparam time AQUISITION_DELAY = 8ns; + localparam unsigned RST_CLK_CYCLES = 10; + localparam unsigned TESTS = 10000; + + // Parameters + typedef logic [7:0] data_t; + parameter IDSize = 4; + localparam int unsigned NUM_REGS = 4; + + // Testbench signals + data_t golden_queue [$]; + data_t data_golden, data_actual; + logic error; + int error_cnt; + + // Signals for DUTS + logic clk; + logic rst_n; + + data_t data_in, data_out; + logic valid_in, valid_retry, valid_out; + logic ready_in, ready_retry, ready_out; + logic [IDSize-1:0] id_retry, id_retry_reverse; + logic needs_retry_middle; + logic lock_retry; + + data_t [0:NUM_REGS] data_middle; + logic [0:NUM_REGS] valid_middle; + logic [0:NUM_REGS] ready_middle; + logic [0:NUM_REGS][IDSize-1:0] id_middle; + + // Clock Generation + initial begin + clk = '1; + rst_n = '0; + repeat (10) @(posedge clk); + rst_n = 1; + end + + always #((CLK_PERIOD/2)) clk = ~clk; + + // DUT Instances + retry_inorder_start #( + .DataType(data_t), + .IDSize(IDSize) + ) dut_start ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_in), + .valid_i(valid_in), + .ready_o(ready_in), + + // Downstream connection + .data_o(data_middle[0]), + .id_o(id_middle[0]), + .valid_o(valid_middle[0]), + .ready_i(ready_middle[0]), + + // Retry Connection + .retry_id_i(id_retry), + .retry_id_o(id_retry_reverse), + .retry_valid_i(valid_retry), + .retry_lock_i(lock_retry), + .retry_ready_o(ready_retry) + ); + + // Generate the register stages + for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline + // Internal register enable for this stage + logic reg_ena; + + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign ready_middle[i] = ready_middle[i+1] | ~valid_middle[i+1]; + + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(valid_middle[i+1], valid_middle[i], ready_middle[i], 1'b0, 1'b0, clk, rst_n) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = (ready_middle[i] & valid_middle[i]); // | reg_ena_i[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFLARN(data_middle[i+1], data_middle[i], reg_ena, '0, clk, rst_n) + `FFLARN( id_middle[i+1], id_middle[i], reg_ena, '0, clk, rst_n) + end + + retry_inorder_end #( + .DataType(data_t), + .IDSize(IDSize) + ) dut_end ( + .clk_i(clk), + .rst_ni(rst_n), + + // Upstream connection + .data_i(data_middle[NUM_REGS]), + .id_i(id_middle[NUM_REGS]), + .needs_retry_i(needs_retry_middle), + .valid_i(valid_middle[NUM_REGS]), + .ready_o(ready_middle[NUM_REGS]), + + // Downstream connection + .data_o(data_out), + .valid_o(valid_out), + .ready_i(ready_out), + + // Retry Connection + .retry_id_o(id_retry), + .retry_id_i(id_retry_reverse), + .retry_valid_o(valid_retry), + .retry_lock_o(lock_retry), + .retry_ready_i(ready_retry) + ); + + // Data Application + initial begin + // Initialize Handshake and Data + data_in = 8'h00; + valid_in = 1'b0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (with no valid data) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + valid_in <= '0; + end + + valid_in <= '1; + + data_in = $random; + golden_queue.push_back(data_in); + + // Wait for handshake and as soon as it happens invalidate data + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!ready_in) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + end + end + + // Drive Fault signal + initial begin + repeat (TESTS) begin + + // Send correct data for some cycles to space errors + repeat ($urandom_range(15, 20)) begin + @(posedge clk); + # (APPLICATION_DELAY); + needs_retry_middle = '0; + end + + @(posedge clk); + # (APPLICATION_DELAY); + needs_retry_middle = '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!(ready_middle[NUM_REGS] & valid_middle[NUM_REGS])) begin + @(posedge clk); + # AQUISITION_DELAY; + end + end + + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); + $finish(0); + end + + + // Aquisition & Validation + initial begin + $timeformat(-9, 0, " ns", 20); + + // Initialize error metrics + error = 0; // Signal so errors can easily be scrolled to in wave + error_cnt = 0; + + // Initialize Handshake + ready_out = '0; + + // Wait for reset to be lifted + @(posedge rst_n); + + forever begin + // Wait random time (while not ready) + repeat ($urandom_range(1, 5)) begin + @(posedge clk); + # APPLICATION_DELAY; + ready_out <= '0; + end + + // Set ready + ready_out <= '1; + + // Wait for handshake + # (AQUISITION_DELAY - APPLICATION_DELAY); + while (!valid_out) begin + @(posedge clk); + # AQUISITION_DELAY; + end; + + // Once it happened check if output was good and reset ready again + data_actual = data_out; + + if (golden_queue.size() > 0) begin + data_golden = golden_queue.pop_front(); + if (data_actual != data_golden) begin + $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); + error = 1; + error_cnt += 1; + end else begin + error = 0; + end + end else begin + $display("[T=%t] Data %h Output when nothing was in golden queue", $time, data_actual); + error = 1; + error_cnt += 1; + end + end + end + +endmodule From 0efce1214bc76a10ac9bfbc980023e1e9b70bafe Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 23 Apr 2024 14:07:52 +0200 Subject: [PATCH 04/21] Added time-based TMR FSM choice time-based TMR can now send data early if correct (2 cycles if good 3 if bad) or be smaller / have better critical path (3 cycles always). --- rtl/time_redundancy/time_TMR_end.sv | 323 +++++++++++++++++++--------- run_tests.sh | 6 +- test/tb_time_tmr.sv | 4 +- test/tb_time_tmr_lock.sv | 4 +- 4 files changed, 230 insertions(+), 107 deletions(-) diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 7f85a2ed..8c962df1 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -17,7 +17,16 @@ module time_TMR_end # ( // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 // Needs to match with time_TMR_start! - parameter int unsigned IDSize = 1 + parameter int unsigned IDSize = 1, + // This parameter chooses the implementation of the internal state machine. + // EarlyValidEnable = 1: + // The state machine will return a valid output after it recieved two out of three + // data elements belonging together if possible e.g. no faults occur (otherwise valid in third cycle) + // This option slightly increases area and critical path. + // EarlyValidEnable = 0: + // The sate machine will always return a valid data element after collecting all 3 parts. + // Internal area and critical path are reduced. + parameter bit EarlyValidEnable = 0 ) ( input logic clk_i, input logic rst_ni, @@ -116,126 +125,234 @@ module time_TMR_end # ( assign full_same = data_same & id_same; assign partial_same = data_same | id_same; - ///////////////////////////////////////////////////////////////////////////////// - // Logic to find out what we should do with our data based on same / not same - - logic element_needs_shift; - logic new_element_arrived; - logic element_in_input; - logic element_relies_on_input; - logic data_usable; - - // Flag Combinatorial Logic - always_comb begin : data_flags_generation_comb - // Some new element just showed up and we need to send data outwards again. - new_element_arrived = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! - (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair - (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair - ); - - // Data or id is in the input -> We should consume the input for this element - // Same data or id count as matches since we can remove an inexact pair on error and be fine - // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) - element_in_input = |partial_same[1:0]; - - // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is - element_needs_shift = ~|full_same[2:1]; - - // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o - element_relies_on_input = |full_same[1:0] & ~full_same[2]; - - // Data has at least two new things that are the same - data_usable = |data_same[2:0]; - end - /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake + logic lock_internal; - typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_DATA} state_t; - state_t state_d, state_q; - logic lock; + if (EarlyValidEnable) begin: gen_early_valid_statemachine - // Special State Description: - // Wait for Ready: We got some data that is usable, but downstream can't use it yet - // -> We keep shifting as far as our pipeline goes to collect all data samples if we haven't yet and then stop - // Wait for Valid: We got some data that is usable, and sent it downstream right away - // -> We try to collect one more piece of our data and then move on + // Input signal reassignment to make state machine more readable + logic element_needs_shift; + logic new_element_arrived; + logic element_in_input; + logic element_relies_on_input; + logic data_usable; - // Next State Combinatorial Logic - always_comb begin : next_state_generation_comb - // Default to staying in the same state - state_d = state_q; - - case (state_q) - BASE: - if (valid_i) begin - if (new_element_arrived) begin - if (!data_usable) begin - state_d = WAIT_FOR_DATA; - end else begin - if (ready_i) begin - if (element_needs_shift) begin - // We can already send our data element, but it needs another shift to collect -> Go into special stat for this - state_d = WAIT_FOR_VALID; - end + always_comb begin : input_reassignment_comb + // Some new element just showed up and we need to send data outwards again. + new_element_arrived = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! + (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair + (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair + ); + + // Data or id is in the input -> We should consume the input for this element + // Same data or id count as matches since we can remove an inexact pair on error and be fine + // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) + element_in_input = |partial_same[1:0]; + + // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is + element_needs_shift = ~|full_same[2:1]; + + // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o + element_relies_on_input = |full_same[1:0] & ~full_same[2]; + + // Data has at least two new things that are the same + data_usable = |data_same[2:0]; + end + + // State Definition + // Special State Description: + // WAIT_FOR_READY: We got some data that is usable, but downstream can't use it yet + // -> We keep shifting as far as our pipeline goes to collect all data samples if we haven't yet and then stop + // WAIT_FOR_VALID: We got some data that is usable, and sent it downstream right away + // -> We try to collect one more piece of our data and then move on + // WAIT_FOR_DATA: We got some pieces of data that should belong together but they are not the same + // -> We try to collect one more piece of the same data and then send it downstream + typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_DATA} state_t; + state_t state_d, state_q; + + + // Next State Combinatorial Logic + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_d = state_q; + + case (state_q) + BASE: + if (valid_i) begin + if (new_element_arrived) begin + if (!data_usable) begin + state_d = WAIT_FOR_DATA; end else begin - state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + if (ready_i) begin + if (element_needs_shift) begin + // We can already send our data element, but it needs another shift to collect -> Go into special stat for this + state_d = WAIT_FOR_VALID; + end + end else begin + state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end end end end - end - WAIT_FOR_READY: - if (ready_i) begin - state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state - end - WAIT_FOR_VALID: begin - if (valid_i) begin - state_d = BASE; // We needed another shift to get back into base state - end - end - WAIT_FOR_DATA: begin - if (valid_i) begin - // We got another shift to get our redundant data completed + WAIT_FOR_READY: if (ready_i) begin - state_d = BASE; // And we send it on to the next stage immediately - end else begin - state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state + end + WAIT_FOR_VALID: begin + if (valid_i) begin + state_d = BASE; // We needed another shift to get back into base state end end + WAIT_FOR_DATA: begin + if (valid_i) begin + // We got another shift to get our redundant data completed + if (ready_i) begin + state_d = BASE; // And we send it on to the next stage immediately + end else begin + state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + end + endcase + end + + // State Storage + `FF(state_q, state_d, WAIT_FOR_VALID); + + // Output Combinatorial Logic + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q) + BASE: valid_o = (!element_relies_on_input | valid_i) & data_usable & new_element_arrived; + WAIT_FOR_DATA: valid_o = (!element_relies_on_input | valid_i) & data_usable; + WAIT_FOR_READY: valid_o = (!element_relies_on_input | valid_i); + WAIT_FOR_VALID: valid_o = 0; + endcase + + case (state_q) + BASE: lock_internal = !ready_i | element_needs_shift | !new_element_arrived; + WAIT_FOR_DATA: lock_internal = !ready_i | element_needs_shift; + WAIT_FOR_READY: lock_internal = !ready_i; + WAIT_FOR_VALID: lock_internal = !valid_i; + endcase + + case (state_q) + BASE: ready_o = ready_i & element_in_input | element_needs_shift | !new_element_arrived; + WAIT_FOR_DATA: ready_o = ready_i & element_in_input | element_needs_shift; + WAIT_FOR_READY: ready_o = ready_i & element_in_input | element_needs_shift; + WAIT_FOR_VALID: ready_o = element_in_input; + endcase + end else begin + valid_o = valid_i; + lock_internal = 0; + ready_o = ready_i; end - endcase - end + end + end else begin : gen_late_valid_statemachine - // State Storage - `FF(state_q, state_d, WAIT_FOR_VALID); + // Input signal reassignment to make state machine more readable + logic new_id_arrived; + logic id_in_input; + logic id_all_same; + logic data_usable; - // Output Combinatorial Logic - always_comb begin: output_generation_comb - if (enable_i) begin - case (state_q) - BASE: valid_o = (!element_relies_on_input | valid_i) & data_usable & new_element_arrived; - WAIT_FOR_DATA: valid_o = (!element_relies_on_input | valid_i) & data_usable; - WAIT_FOR_READY: valid_o = (!element_relies_on_input | valid_i); - WAIT_FOR_VALID: valid_o = 0; - endcase + always_comb begin : data_flags_generation_comb + new_id_arrived = ( + (id_same == 5'b00111) || + (id_same == 5'b00100) || + (id_same == 5'b00010) || + (id_same == 5'b01010) + ); - case (state_q) - BASE: lock = !ready_i | element_needs_shift | !new_element_arrived; - WAIT_FOR_DATA: lock = !ready_i | element_needs_shift; - WAIT_FOR_READY: lock = !ready_i; - WAIT_FOR_VALID: lock = !valid_i; - endcase + id_in_input = |id_same[1:0]; + + id_all_same = &id_same[2:0]; + + data_usable = |data_same[2:0]; + end + + // State Definition + // Special State Description: + // WAIT_FOR_READY: We got some data that is usable, but downstream can't use it yet + // -> We keep shifting as far as our pipeline goes to collect all data samples if we haven't yet and then stop + // WAIT_FOR_VALID: We got two usable pieces of data in the last and another register + // -> We need to wait for at least one new data element before data can be valid again + // WAIT_FOR_VALID_X2: We have recieved three fully usable pieces of data + // -> We need to wait for at least two new data elements before data can be valid again + typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_VALID_X2} state_t; + state_t state_d, state_q; + + // Next State Combinatorial Logic + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_d = state_q; case (state_q) - BASE: ready_o = ready_i & element_in_input | element_needs_shift | !new_element_arrived; - WAIT_FOR_DATA: ready_o = ready_i & element_in_input | element_needs_shift; - WAIT_FOR_READY: ready_o = ready_i & element_in_input | element_needs_shift; - WAIT_FOR_VALID: ready_o = element_in_input; + BASE: + if (new_id_arrived) begin + if (ready_i) begin + if (id_all_same) begin + state_d = WAIT_FOR_VALID_X2; + end else begin + state_d = WAIT_FOR_VALID; + end + end else begin + state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + WAIT_FOR_READY: + if (ready_i) begin + if (id_all_same) begin + state_d = WAIT_FOR_VALID_X2; + end else begin + state_d = WAIT_FOR_VALID; + end + end + WAIT_FOR_VALID: begin + if (valid_i) begin + state_d = BASE; + end + end + WAIT_FOR_VALID_X2: begin + if (valid_i) begin + state_d = WAIT_FOR_VALID; + end + end endcase - end else begin - valid_o = valid_i; - lock = 0; - ready_o = ready_i; + end + + // State Storage + `FF(state_q, state_d, WAIT_FOR_VALID); + + // Output Combinatorial Logic + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q) + BASE: valid_o = new_id_arrived & data_usable; + WAIT_FOR_READY: valid_o = new_id_arrived & data_usable; + WAIT_FOR_VALID: valid_o = 0; + WAIT_FOR_VALID_X2: valid_o = 0; + endcase + + case (state_q) + BASE: lock_internal = !ready_i | !new_id_arrived; + WAIT_FOR_READY: lock_internal = !ready_i; + WAIT_FOR_VALID: lock_internal = 1; + WAIT_FOR_VALID_X2: lock_internal = 1; + endcase + + case (state_q) + BASE: ready_o = ready_i & id_in_input | !new_id_arrived; + WAIT_FOR_READY: ready_o = ready_i & id_in_input; + WAIT_FOR_VALID: ready_o = 1; + WAIT_FOR_VALID_X2: ready_o = 1; + endcase + end else begin + valid_o = valid_i; + lock_internal = 0; + ready_o = ready_i; + end end end @@ -260,7 +377,7 @@ module time_TMR_end # ( // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything if (valid_i | lock_q) begin - lock_d = lock; + lock_d = lock_internal; end else begin lock_d = lock_q; end diff --git a/run_tests.sh b/run_tests.sh index b9c79388..9bf5efb8 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -51,8 +51,10 @@ call_vsim -GDataWidth=64 tb_ecc_secded call_vsim tb_ecc_scrubber call_vsim tb_voter_macros -call_vsim tb_time_tmr -call_vsim tb_time_tmr_lock +call_vsim tb_time_tmr -GEarlyValidEnable=0 +call_vsim tb_time_tmr -GEarlyValidEnable=1 +call_vsim tb_time_tmr_lock -GEarlyValidEnable=0 +call_vsim tb_time_tmr_lock -GEarlyValidEnable=1 call_vsim tb_time_dmr call_vsim tb_retry call_vsim tb_retry_inorder diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index 7ac55e67..1eef3e42 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -2,6 +2,7 @@ module tb_time_tmr #( // DUT Parameters parameter IDSize = 4, parameter int LockTimeout = 4, + parameter bit EarlyValidEnable = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -51,7 +52,8 @@ module tb_time_tmr #( time_TMR_end #( .DataType(data_t), .LockTimeout(LockTimeout), - .IDSize(IDSize) + .IDSize(IDSize), + .EarlyValidEnable(EarlyValidEnable) ) dut_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index 562cf159..52146607 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -7,6 +7,7 @@ module tb_time_tmr_lock #( parameter int OpgroupWidth = $clog2(NumOpgroups), parameter int IDSize = 5, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, + parameter bit EarlyValidEnable, // TB Parameters parameter int unsigned TESTS = 10000, @@ -168,7 +169,8 @@ module tb_time_tmr_lock #( time_TMR_end #( .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), - .IDSize (IDSize) + .IDSize (IDSize), + .EarlyValidEnable(EarlyValidEnable) ) i_time_TMR_end ( .clk_i(clk), .rst_ni(rst_n), From c8d20d1c30b51039368fcab52ffd0da62d9e8995 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Wed, 24 Apr 2024 11:03:01 +0200 Subject: [PATCH 05/21] Added configurable (parallel) internal Redundancy to time-based TMR and DMR modules. --- rtl/time_redundancy/time_DMR_end.sv | 276 +++++++++------ rtl/time_redundancy/time_DMR_start.sv | 169 ++++++---- rtl/time_redundancy/time_TMR_end.sv | 464 +++++++++++++++----------- rtl/time_redundancy/time_TMR_start.sv | 168 ++++++---- rtl/time_redundancy/voters.svh | 6 + run_tests.sh | 19 +- test/tb_time_dmr.sv | 7 +- test/tb_time_dmr_retry.sv | 27 +- test/tb_time_dmr_retry_lock.sv | 7 +- test/tb_time_tmr.sv | 7 +- test/tb_time_tmr_lock.sv | 9 +- 11 files changed, 728 insertions(+), 431 deletions(-) diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index 2de4514d..bbb08f36 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -17,7 +17,14 @@ module time_DMR_end # ( // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 // Needs to match with time_TMR_start! - parameter int unsigned IDSize = 1 + parameter int unsigned IDSize = 1, + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 ) ( input logic clk_i, input logic rst_ni, @@ -44,7 +51,14 @@ module time_DMR_end # ( // Output Flags for Error Counting output logic fault_detected_o -); +); + + // Redundant Output signals + logic ready_ov[REP]; + logic valid_ov[REP]; + logic needs_retry_ov[REP]; + logic fault_detected_ov[REP]; + ///////////////////////////////////////////////////////////////////////////////// // Storage of incomming results and generating good output data @@ -100,24 +114,26 @@ module time_DMR_end # ( ///////////////////////////////////////////////////////////////////////////////// // Logic to find out what we should do with our data based on same / not same - logic new_element_arrived; - logic data_usable; + logic new_element_arrived[REP]; + logic data_usable[REP]; // Flag Combinatorial Logic - always_comb begin : data_flags_generation_comb - // Some new element just showed up and we need to send data outwards again. - new_element_arrived = (id_same == 2'b01) || (id_same == 2'b00); + for (genvar r = 0; r < REP; r++) begin + always_comb begin : data_flags_generation_comb + // Some new element just showed up and we need to send data outwards again. + new_element_arrived[r] = (id_same == 2'b01) || (id_same == 2'b00); - // Data has at least two new things that are the same - data_usable = data_same[0]; + // Data has at least two new things that are the same + data_usable[r] = data_same[0]; + end end /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake typedef enum logic [0:0] {BASE, WAIT_FOR_READY} state_t; - state_t state_d, state_q; - logic valid_internal, lock_internal; + state_t state_v[REP], state_d[REP], state_q[REP]; + logic valid_internal[REP], lock_internal[REP]; // Special State Description: // Wait for Ready: We got some data that is usable, but downstream can't use it yet @@ -127,92 +143,122 @@ module time_DMR_end # ( // Next State Combinatorial Logic - always_comb begin : next_state_generation_comb - // Default to staying in the same state - state_d = state_q; - - case (state_q) - BASE: - if (valid_i) begin - if (new_element_arrived) begin - if (!ready_i) begin - state_d = WAIT_FOR_READY; + for (genvar r = 0; r < REP; r++) begin + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_v[r] = state_q[r]; + + case (state_q[r]) + BASE: + if (valid_i) begin + if (new_element_arrived[r]) begin + if (!ready_i) begin + state_v[r] = WAIT_FOR_READY; + end end end - end - WAIT_FOR_READY: - if (ready_i) begin - state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state - end - endcase + WAIT_FOR_READY: + if (ready_i) begin + state_v[r] = BASE; // Downstream takes the data that we are holding and we can go back to the base state + end + endcase + end + end + + // Generate default cases + state_t state_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign state_base[r] = BASE; end + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3ENUM(state_v, state_d); + end else begin + assign state_d = state_v; + end // State Storage - `FF(state_q, state_d, BASE); + `FF(state_q, state_d, state_base); // Output Combinatorial Logic - always_comb begin: output_generation_comb - if (enable_i) begin - case (state_q) - BASE: valid_internal = valid_i & new_element_arrived; - WAIT_FOR_READY: valid_internal = valid_i; - endcase - - case (state_q) - BASE: lock_internal = !ready_i | !full_same[0]; - WAIT_FOR_READY: lock_internal = !ready_i; - endcase - - case (state_q) - BASE: ready_o = ready_i | !new_element_arrived; - WAIT_FOR_READY: ready_o = ready_i; - endcase - end else begin - valid_internal = valid_i; - lock_internal = 0; - ready_o = ready_i; + for (genvar r = 0; r < REP; r++) begin + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q[r]) + BASE: valid_internal[r] = valid_i & new_element_arrived[r]; + WAIT_FOR_READY: valid_internal[r] = valid_i; + endcase + + case (state_q[r]) + BASE: lock_internal[r] = !ready_i | !full_same[0]; + WAIT_FOR_READY: lock_internal[r] = !ready_i; + endcase + + case (state_q[r]) + BASE: ready_ov[r] = ready_i | !new_element_arrived[r]; + WAIT_FOR_READY: ready_ov[r] = ready_i; + endcase + end else begin + valid_internal[r] = valid_i; + lock_internal[r] = 0; + ready_ov[r] = ready_i; + end end end /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to lock / unlock Arbitrator with Watchdog timer - logic lock_d, lock_q; - logic [$clog2(LockTimeout)-1:0] counter_d, counter_q; - logic lock_reset; + logic lock_v[REP], lock_d[REP], lock_q[REP]; + logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; // Next State Combinatorial Logic - always_comb begin : lock_comb - - if (counter_q > LockTimeout) begin - lock_d = 0; - counter_d = 0; - lock_reset = 1; + for (genvar r = 0; r < REP; r++) begin + always_comb begin : lock_comb + if (counter_q[r] > LockTimeout) begin + lock_v[r] = 0; + counter_v[r] = 0; - end else begin - lock_reset = 0; - - if (lock_q & !valid_i) begin // TODO: Add dependency on valid - counter_d = counter_q + 1; end else begin - counter_d = 0; - end + if (lock_q[r] & !valid_i) begin + counter_v[r] = counter_q[r] + 1; + end else begin + counter_v[r] = 0; + end - // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything - if (valid_i | lock_q) begin - lock_d = lock_internal; - end else begin - lock_d = lock_q; + // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything + if (valid_i | lock_q[r]) begin + lock_v[r] = lock_internal[r]; + end else begin + lock_v[r] = lock_q[r]; + end end end end - assign lock_o = lock_d; + // State Voting Logic + if (InternalRedundancy) begin : gen_lock_voters + `VOTE3to3(lock_v, lock_d); + `VOTE3to3(counter_v, counter_d); + end else begin + assign counter_d = counter_v; + assign lock_d = lock_v; + end + + assign lock_o = lock_d[0]; + + // Default state + logic lock_base[REP]; + logic [$clog2(LockTimeout)-1:0] counter_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign lock_base[r] = '0; + assign counter_base[r] = '0; + end // State Storage - `FF(lock_q, lock_d, '0); - `FF(counter_q, counter_d, '0); + `FF(lock_q, lock_d, lock_base); + `FF(counter_q, counter_d, counter_base); /////////////////////////////////////////////////////////////////////////////////////////////////// // Output deduplication based on ID and ID Faults @@ -220,32 +266,49 @@ module time_DMR_end # ( logic id_fault_q; assign id_fault_q = ^id_q; - logic [2 ** (IDSize-1)-1:0] recently_seen_d, recently_seen_q; + logic [2 ** (IDSize-1)-1:0] recently_seen_v[REP], recently_seen_d[REP], recently_seen_q[REP]; - always_comb begin - recently_seen_d = recently_seen_q; + for (genvar r = 0; r < REP; r++) begin + always_comb begin + recently_seen_v[r] = recently_seen_q[r]; - recently_seen_d[next_id_i[IDSize-2:0]] = 0; + recently_seen_v[r][next_id_i[IDSize-2:0]] = 0; - if (valid_internal & ready_i & !id_fault_q) begin - recently_seen_d[id_q[IDSize-2:0]] = 1; + if (valid_internal[r] & ready_i & !id_fault_q) begin + recently_seen_v[r][id_q[IDSize-2:0]] = 1; + end end end - // State Storage - `FF(recently_seen_q, recently_seen_d, ~'0); + // State Voting Logic + if (InternalRedundancy) begin : gen_deduplication_voters + `VOTE3to3(recently_seen_v, recently_seen_d); + end else begin + assign recently_seen_d = recently_seen_v; + end + + // Default state + logic [2 ** (IDSize-1)-1:0] recently_seen_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign recently_seen_base[r] = ~'0; // All 1s! + end - always_comb begin - if (enable_i) begin - if (id_fault_q | recently_seen_q[id_q[IDSize-2:0]] & valid_internal) begin - valid_o = 0; + // State Storage + `FF(recently_seen_q, recently_seen_d, recently_seen_base); + + for (genvar r = 0; r < REP; r++) begin + always_comb begin + if (enable_i) begin + if (id_fault_q | recently_seen_q[r][id_q[IDSize-2:0]] & valid_internal[r]) begin + valid_ov[r] = 0; + end else begin + valid_ov[r] = valid_internal[r]; + end + needs_retry_ov[r] = id_same[0] & !data_usable[r]; end else begin - valid_o = valid_internal; - end - needs_retry_o = id_same[0] & !data_usable; - end else begin - valid_o = valid_internal; - needs_retry_o = 0; + valid_ov[r] = valid_internal[r]; + needs_retry_ov[r] = 0; + end end end @@ -258,12 +321,37 @@ module time_DMR_end # ( // In a non-error case, we should have a full same signal every other cycle // So if that is not the case we had a fault. - logic fault_detected_d, fault_detected_q; + logic fault_detected_d[REP], fault_detected_q[REP]; - assign fault_detected_d = ~|full_same[1:0]; + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_d[r] = ~|full_same[1:0]; + end - `FF(fault_detected_q, fault_detected_d, '0); + // Default state + logic fault_detected_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_base[r] = '0; + end - assign fault_detected_o = fault_detected_d & !fault_detected_q; + `FF(fault_detected_q, fault_detected_d, fault_detected_base); + + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Output Voting + + if (InternalRedundancy) begin : gen_output_voters + `VOTE3to1(ready_ov, ready_o); + `VOTE3to1(valid_ov, valid_o); + `VOTE3to1(needs_retry_ov, needs_retry_o); + `VOTE3to1(fault_detected_ov, fault_detected_o); + end else begin + assign ready_o = ready_ov[0]; + assign valid_o = valid_ov[0]; + assign needs_retry_o = needs_retry_ov[0]; + assign fault_detected_o = fault_detected_ov[0]; + end endmodule diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 50138ac3..03acfa31 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -13,7 +13,14 @@ module time_DMR_start # ( // Needs to match with time_TMR_end! parameter int unsigned IDSize = 1, // Set to 1 if the id_i port should be used - parameter bit UseExternalId = 0 + parameter bit UseExternalId = 0, + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 ) ( input logic clk_i, input logic rst_ni, @@ -33,7 +40,11 @@ module time_DMR_start # ( output logic [IDSize-1:0] id_o, output logic valid_o, input logic ready_i -); +); + // Redundant Output signals + logic [IDSize-1:0] next_id_ov[REP]; + logic ready_ov[REP]; + logic valid_ov[REP]; // State machine TLDR // - counting the state from 0 to 2 if the handshake is good @@ -41,80 +52,114 @@ module time_DMR_start # ( // Next State Combinatorial Logic typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE} state_t; - state_t state_d, state_q; - DataType data_d, data_q; - logic [IDSize-1:0] id_d, id_q; - logic [IDSize-2:0] next_id_o_noparity; - - always_comb begin : next_state_logic - // Default to staying in same state - state_d = state_q; - data_d = data_q; - id_d = id_q; - - next_id_o_noparity = id_q[IDSize-2:0] + 1; - if (UseExternalId == 1) begin - next_id_o = id_i; - end else begin - next_id_o = {^next_id_o_noparity, next_id_o_noparity}; - end + state_t state_v[REP], state_d[REP], state_q[REP]; + DataType data_v[REP], data_d[REP], data_q[REP]; + logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; + + for (genvar r = 0; r < REP; r++) begin + always_comb begin : next_state_logic + // Default to staying in same state + state_v[r] = state_q[r]; + data_v[r] = data_q[r]; + id_v[r] = id_q[r]; + + if (UseExternalId == 1) begin + next_id_ov[r] = id_i; + end else begin + `INCREMENT_WITH_PARITY(id_q[r], next_id_ov[r]); + end - case (state_q) - STORE_AND_SEND: - if (valid_i) begin - data_d = data_i; - id_d = next_id_o; - + case (state_q[r]) + STORE_AND_SEND: + if (valid_i) begin + data_v[r] = data_i; + id_v[r] = next_id_ov[r]; + + if (ready_i) begin + if (enable_i) begin + state_v[r] = REPLICATE; + end else begin + state_v[r] = STORE_AND_SEND; + end + end else begin + state_v[r] = SEND; + end + end + SEND: if (ready_i) begin if (enable_i) begin - state_d = REPLICATE; + state_v[r] = REPLICATE; // Reset seqeuence end else begin - state_d = STORE_AND_SEND; + state_v[r] = STORE_AND_SEND; end - end else begin - state_d = SEND; end - end - SEND: - if (ready_i) begin - if (enable_i) begin - state_d = REPLICATE; // Reset seqeuence - end else begin - state_d = STORE_AND_SEND; + REPLICATE: + if (ready_i) begin + state_v[r] = STORE_AND_SEND; end - end - REPLICATE: - if (ready_i) begin - state_d = STORE_AND_SEND; - end - endcase + endcase + end + end + + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3ENUM(state_v, state_d); + `VOTE3to3(id_v, id_d); + `VOTE3to3(data_v, data_d); + end else begin + assign state_d = state_v; + assign data_d = data_v; + assign id_d = id_v; + end + + // Generate default cases + state_t state_base[REP]; + DataType data_base[REP]; + logic [IDSize-1:0] id_base[REP]; + + for (genvar r = 0; r < REP; r++) begin + assign state_base[r] = STORE_AND_SEND; + assign data_base[r] = 0; + assign id_base[r] = 0; end // State Storage - `FF(state_q, state_d, STORE_AND_SEND); - `FF(data_q, data_d, '0); - `FF(id_q, id_d, '0); + `FF(state_q, state_d, state_base); + `FF(data_q, data_d, data_base); + `FF(id_q, id_d, id_base); // Output Combinatorial Logic - always_comb begin : output_logic - case (state_q) - STORE_AND_SEND: begin - valid_o = valid_i; - ready_o = 1; - end - SEND: begin - valid_o = '1; - ready_o = '0; - end - REPLICATE: begin - valid_o = '1; - ready_o = '0; - end - endcase + for (genvar r = 0; r < REP; r++) begin + always_comb begin : output_logic + case (state_q[r]) + STORE_AND_SEND: begin + valid_ov[r] = valid_i; + ready_ov[r] = 1; + end + SEND: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + REPLICATE: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + endcase + end end // Output Voting Logic - assign data_o = data_d; - assign id_o = id_d; + assign data_o = data_d[0]; + assign id_o = id_d[0]; + + if (InternalRedundancy) begin : gen_output_voters + `VOTE3to1(next_id_ov, next_id_o); + `VOTE3to1(ready_ov, ready_o); + `VOTE3to1(valid_ov, valid_o); + end else begin + assign next_id_o = next_id_ov[0]; + assign ready_o = ready_ov[0]; + assign valid_o = valid_ov[0]; + end endmodule diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 8c962df1..4cbeb82c 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -26,7 +26,14 @@ module time_TMR_end # ( // EarlyValidEnable = 0: // The sate machine will always return a valid data element after collecting all 3 parts. // Internal area and critical path are reduced. - parameter bit EarlyValidEnable = 0 + parameter bit EarlyValidEnable = 0, + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 ) ( input logic clk_i, input logic rst_ni, @@ -49,6 +56,12 @@ module time_TMR_end # ( // Flag for External Error Counter output logic fault_detected_o ); + + // Redundant Output signals + logic ready_ov[REP]; + logic valid_ov[REP]; + logic fault_detected_ov[REP]; + ///////////////////////////////////////////////////////////////////////////////// // Storage of incomming results and generating good output data @@ -127,37 +140,39 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake - logic lock_internal; + logic lock_internal[REP]; if (EarlyValidEnable) begin: gen_early_valid_statemachine // Input signal reassignment to make state machine more readable - logic element_needs_shift; - logic new_element_arrived; - logic element_in_input; - logic element_relies_on_input; - logic data_usable; - - always_comb begin : input_reassignment_comb - // Some new element just showed up and we need to send data outwards again. - new_element_arrived = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! - (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair - (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair - ); - - // Data or id is in the input -> We should consume the input for this element - // Same data or id count as matches since we can remove an inexact pair on error and be fine - // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) - element_in_input = |partial_same[1:0]; - - // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is - element_needs_shift = ~|full_same[2:1]; - - // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o - element_relies_on_input = |full_same[1:0] & ~full_same[2]; - - // Data has at least two new things that are the same - data_usable = |data_same[2:0]; + logic element_needs_shift[REP]; + logic new_element_arrived[REP]; + logic element_in_input[REP]; + logic element_relies_on_input[REP]; + logic data_usable[REP]; + + for (genvar r = 0; r < REP; r++) begin + always_comb begin : input_reassignment_comb + // Some new element just showed up and we need to send data outwards again. + new_element_arrived[r] = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! + (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair + (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair + ); + + // Data or id is in the input -> We should consume the input for this element + // Same data or id count as matches since we can remove an inexact pair on error and be fine + // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) + element_in_input[r] = |partial_same[1:0]; + + // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is + element_needs_shift[r] = ~|full_same[2:1]; + + // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o + element_relies_on_input[r] = |full_same[1:0] & ~full_same[2]; + + // Data has at least two new things that are the same + data_usable[r] = |data_same[2:0]; + end end // State Definition @@ -169,107 +184,126 @@ module time_TMR_end # ( // WAIT_FOR_DATA: We got some pieces of data that should belong together but they are not the same // -> We try to collect one more piece of the same data and then send it downstream typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_DATA} state_t; - state_t state_d, state_q; + state_t state_v[REP], state_d[REP], state_q[REP]; // Next State Combinatorial Logic - always_comb begin : next_state_generation_comb - // Default to staying in the same state - state_d = state_q; - - case (state_q) - BASE: - if (valid_i) begin - if (new_element_arrived) begin - if (!data_usable) begin - state_d = WAIT_FOR_DATA; - end else begin - if (ready_i) begin - if (element_needs_shift) begin - // We can already send our data element, but it needs another shift to collect -> Go into special stat for this - state_d = WAIT_FOR_VALID; - end + for (genvar r = 0; r < REP; r++) begin + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_v[r] = state_q[r]; + + case (state_q[r]) + BASE: + if (valid_i) begin + if (new_element_arrived[r]) begin + if (!data_usable[r]) begin + state_v[r] = WAIT_FOR_DATA; end else begin - state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + if (ready_i) begin + if (element_needs_shift[r]) begin + // We can already send our data element, but it needs another shift to collect -> Go into special stat for this + state_v[r] = WAIT_FOR_VALID; + end + end else begin + state_v[r] = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end end end end - end - WAIT_FOR_READY: - if (ready_i) begin - state_d = BASE; // Downstream takes the data that we are holding and we can go back to the base state - end - WAIT_FOR_VALID: begin - if (valid_i) begin - state_d = BASE; // We needed another shift to get back into base state - end - end - WAIT_FOR_DATA: begin - if (valid_i) begin - // We got another shift to get our redundant data completed + WAIT_FOR_READY: if (ready_i) begin - state_d = BASE; // And we send it on to the next stage immediately - end else begin - state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + state_v[r] = BASE; // Downstream takes the data that we are holding and we can go back to the base state + end + WAIT_FOR_VALID: begin + if (valid_i) begin + state_v[r] = BASE; // We needed another shift to get back into base state end end - end - endcase + WAIT_FOR_DATA: begin + if (valid_i) begin + // We got another shift to get our redundant data completed + if (ready_i) begin + state_v[r] = BASE; // And we send it on to the next stage immediately + end else begin + state_v[r] = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + end + endcase + end end - // State Storage - `FF(state_q, state_d, WAIT_FOR_VALID); + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3ENUM(state_v, state_d); + end else begin + assign state_d = state_v; + end - // Output Combinatorial Logic - always_comb begin: output_generation_comb - if (enable_i) begin - case (state_q) - BASE: valid_o = (!element_relies_on_input | valid_i) & data_usable & new_element_arrived; - WAIT_FOR_DATA: valid_o = (!element_relies_on_input | valid_i) & data_usable; - WAIT_FOR_READY: valid_o = (!element_relies_on_input | valid_i); - WAIT_FOR_VALID: valid_o = 0; - endcase + // Generate default cases + state_t state_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign state_base[r] = WAIT_FOR_VALID; + end - case (state_q) - BASE: lock_internal = !ready_i | element_needs_shift | !new_element_arrived; - WAIT_FOR_DATA: lock_internal = !ready_i | element_needs_shift; - WAIT_FOR_READY: lock_internal = !ready_i; - WAIT_FOR_VALID: lock_internal = !valid_i; - endcase + // State Storage + `FF(state_q, state_d, state_base); - case (state_q) - BASE: ready_o = ready_i & element_in_input | element_needs_shift | !new_element_arrived; - WAIT_FOR_DATA: ready_o = ready_i & element_in_input | element_needs_shift; - WAIT_FOR_READY: ready_o = ready_i & element_in_input | element_needs_shift; - WAIT_FOR_VALID: ready_o = element_in_input; - endcase - end else begin - valid_o = valid_i; - lock_internal = 0; - ready_o = ready_i; + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q[r]) + BASE: valid_ov[r] = (!element_relies_on_input[r] | valid_i) & data_usable[r] & new_element_arrived[r]; + WAIT_FOR_DATA: valid_ov[r] = (!element_relies_on_input[r] | valid_i) & data_usable[r]; + WAIT_FOR_READY: valid_ov[r] = (!element_relies_on_input[r] | valid_i); + WAIT_FOR_VALID: valid_ov[r] = 0; + endcase + + case (state_q[r]) + BASE: lock_internal[r] = !ready_i | element_needs_shift[r] | !new_element_arrived[r]; + WAIT_FOR_DATA: lock_internal[r] = !ready_i | element_needs_shift[r]; + WAIT_FOR_READY: lock_internal[r] = !ready_i; + WAIT_FOR_VALID: lock_internal[r] = !valid_i; + endcase + + case (state_q[r]) + BASE: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r] | !new_element_arrived[r]; + WAIT_FOR_DATA: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r]; + WAIT_FOR_READY: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r]; + WAIT_FOR_VALID: ready_ov[r] = element_in_input[r]; + endcase + end else begin + valid_ov[r] = valid_i; + lock_internal[r] = 0; + ready_ov[r] = ready_i; + end end end end else begin : gen_late_valid_statemachine // Input signal reassignment to make state machine more readable - logic new_id_arrived; - logic id_in_input; - logic id_all_same; - logic data_usable; + logic new_id_arrived[REP]; + logic id_in_input[REP]; + logic id_all_same[REP]; + logic data_usable[REP]; - always_comb begin : data_flags_generation_comb - new_id_arrived = ( - (id_same == 5'b00111) || - (id_same == 5'b00100) || - (id_same == 5'b00010) || - (id_same == 5'b01010) - ); + for (genvar r = 0; r < REP; r++) begin + always_comb begin : data_flags_generation_comb + new_id_arrived[r] = ( + (id_same == 5'b00111) || + (id_same == 5'b00100) || + (id_same == 5'b00010) || + (id_same == 5'b01010) + ); - id_in_input = |id_same[1:0]; + id_in_input[r] = |id_same[1:0]; - id_all_same = &id_same[2:0]; + id_all_same[r] = &id_same[2:0]; - data_usable = |data_same[2:0]; + data_usable[r] = |data_same[2:0]; + end end // State Definition @@ -281,77 +315,94 @@ module time_TMR_end # ( // WAIT_FOR_VALID_X2: We have recieved three fully usable pieces of data // -> We need to wait for at least two new data elements before data can be valid again typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_VALID_X2} state_t; - state_t state_d, state_q; + state_t state_v[REP], state_d[REP], state_q[REP]; // Next State Combinatorial Logic - always_comb begin : next_state_generation_comb - // Default to staying in the same state - state_d = state_q; - - case (state_q) - BASE: - if (new_id_arrived) begin + for (genvar r = 0; r < REP; r++) begin + always_comb begin : next_state_generation_comb + // Default to staying in the same state + state_v[r] = state_q[r]; + + case (state_q[r]) + BASE: + if (new_id_arrived[r]) begin + if (ready_i) begin + if (id_all_same[r]) begin + state_v[r] = WAIT_FOR_VALID_X2; + end else begin + state_v[r] = WAIT_FOR_VALID; + end + end else begin + state_v[r] = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go + end + end + WAIT_FOR_READY: if (ready_i) begin - if (id_all_same) begin - state_d = WAIT_FOR_VALID_X2; + if (id_all_same[r]) begin + state_v[r] = WAIT_FOR_VALID_X2; end else begin - state_d = WAIT_FOR_VALID; + state_v[r] = WAIT_FOR_VALID; end - end else begin - state_d = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go end - end - WAIT_FOR_READY: - if (ready_i) begin - if (id_all_same) begin - state_d = WAIT_FOR_VALID_X2; - end else begin - state_d = WAIT_FOR_VALID; + WAIT_FOR_VALID: begin + if (valid_i) begin + state_v[r] = BASE; end end - WAIT_FOR_VALID: begin - if (valid_i) begin - state_d = BASE; - end - end - WAIT_FOR_VALID_X2: begin - if (valid_i) begin - state_d = WAIT_FOR_VALID; + WAIT_FOR_VALID_X2: begin + if (valid_i) begin + state_v[r] = WAIT_FOR_VALID; + end end - end - endcase + endcase + end end - // State Storage - `FF(state_q, state_d, WAIT_FOR_VALID); + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3ENUM(state_v, state_d); + end else begin + assign state_d = state_v; + end - // Output Combinatorial Logic - always_comb begin: output_generation_comb - if (enable_i) begin - case (state_q) - BASE: valid_o = new_id_arrived & data_usable; - WAIT_FOR_READY: valid_o = new_id_arrived & data_usable; - WAIT_FOR_VALID: valid_o = 0; - WAIT_FOR_VALID_X2: valid_o = 0; - endcase + // Generate default cases + state_t state_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign state_base[r] = WAIT_FOR_VALID; + end - case (state_q) - BASE: lock_internal = !ready_i | !new_id_arrived; - WAIT_FOR_READY: lock_internal = !ready_i; - WAIT_FOR_VALID: lock_internal = 1; - WAIT_FOR_VALID_X2: lock_internal = 1; - endcase + // State Storage + `FF(state_q, state_d, state_base); - case (state_q) - BASE: ready_o = ready_i & id_in_input | !new_id_arrived; - WAIT_FOR_READY: ready_o = ready_i & id_in_input; - WAIT_FOR_VALID: ready_o = 1; - WAIT_FOR_VALID_X2: ready_o = 1; - endcase - end else begin - valid_o = valid_i; - lock_internal = 0; - ready_o = ready_i; + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin + always_comb begin: output_generation_comb + if (enable_i) begin + case (state_q[r]) + BASE: valid_ov[r] = new_id_arrived[r] & data_usable[r]; + WAIT_FOR_READY: valid_ov[r] = new_id_arrived[r] & data_usable[r]; + WAIT_FOR_VALID: valid_ov[r] = 0; + WAIT_FOR_VALID_X2: valid_ov[r] = 0; + endcase + + case (state_q[r]) + BASE: lock_internal[r] = !ready_i | !new_id_arrived[r]; + WAIT_FOR_READY: lock_internal[r] = !ready_i; + WAIT_FOR_VALID: lock_internal[r] = 1; + WAIT_FOR_VALID_X2: lock_internal[r] = 1; + endcase + + case (state_q[r]) + BASE: ready_ov[r] = ready_i & id_in_input[r] | !new_id_arrived[r]; + WAIT_FOR_READY: ready_ov[r] = ready_i & id_in_input[r]; + WAIT_FOR_VALID: ready_ov[r] = 1; + WAIT_FOR_VALID_X2: ready_ov[r] = 1; + endcase + end else begin + valid_ov[r] = valid_i; + lock_internal[r] = 0; + ready_ov[r] = ready_i; + end end end end @@ -359,37 +410,55 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to lock / unlock Arbitrator with Watchdog timer - logic lock_d, lock_q; - logic [$clog2(LockTimeout)-1:0] counter_d, counter_q; + logic lock_v[REP], lock_d[REP], lock_q[REP]; + logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; // Next State Combinatorial Logic - always_comb begin : lock_comb - if (counter_q > LockTimeout) begin - lock_d = 0; - counter_d = 0; - end else begin + for (genvar r = 0; r < REP; r++) begin + always_comb begin : lock_comb + if (counter_q[r] > LockTimeout) begin + lock_v[r] = 0; + counter_v[r] = 0; - if (lock_q & !valid_i) begin // TODO: Add dependency on valid - counter_d = counter_q + 1; end else begin - counter_d = 0; - end + if (lock_q[r] & !valid_i) begin + counter_v[r] = counter_q[r] + 1; + end else begin + counter_v[r] = 0; + end - // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything - if (valid_i | lock_q) begin - lock_d = lock_internal; - end else begin - lock_d = lock_q; + // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything + if (valid_i | lock_q[r]) begin + lock_v[r] = lock_internal[r]; + end else begin + lock_v[r] = lock_q[r]; + end end end end - // Output Logic - assign lock_o = lock_d; + // State Voting Logic + if (InternalRedundancy) begin : gen_lock_voters + `VOTE3to3(lock_v, lock_d); + `VOTE3to3(counter_v, counter_d); + end else begin + assign counter_d = counter_v; + assign lock_d = lock_v; + end + + assign lock_o = lock_d[0]; + + // Default state + logic lock_base[REP]; + logic [$clog2(LockTimeout)-1:0] counter_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign lock_base[r] = '0; + assign counter_base[r] = '0; + end // State Storage - `FF(lock_q, lock_d, '0); - `FF(counter_q, counter_d, '0); + `FF(lock_q, lock_d, lock_base); + `FF(counter_q, counter_d, counter_base); /////////////////////////////////////////////////////////////////////////////////////////////////// // Build error flag @@ -400,12 +469,35 @@ module time_TMR_end # ( // always have at least two data elements that are the same. // Make output only 1 for a signly cycle even if internal pipeline is stopped - logic fault_detected_d, fault_detected_q; + logic fault_detected_d[REP], fault_detected_q[REP]; - assign fault_detected_d = ~|full_same[2:0]; + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_d[r] = ~|full_same[1:0]; + end + + // Default state + logic fault_detected_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_base[r] = '0; + end - `FF(fault_detected_q, fault_detected_d, '0); + `FF(fault_detected_q, fault_detected_d, fault_detected_base); - assign fault_detected_o = fault_detected_d & !fault_detected_q; + for (genvar r = 0; r < REP; r++) begin + assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; + end + + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Output Voting + + if (InternalRedundancy) begin : gen_output_voters + `VOTE3to1(ready_ov, ready_o); + `VOTE3to1(valid_ov, valid_o); + `VOTE3to1(fault_detected_ov, fault_detected_o); + end else begin + assign ready_o = ready_ov[0]; + assign valid_o = valid_ov[0]; + assign fault_detected_o = fault_detected_ov[0]; + end endmodule diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index c4efd5af..a66d0049 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -11,7 +11,14 @@ module time_TMR_start # ( // next to each other. // As an estimate you can use log2(longest_pipeline) + 1. // Needs to match with time_TMR_end! - parameter int unsigned IDSize = 1 + parameter int unsigned IDSize = 1, + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 ) ( input logic clk_i, input logic rst_ni, @@ -27,7 +34,10 @@ module time_TMR_start # ( output logic [IDSize-1:0] id_o, output logic valid_o, input logic ready_i -); +); + // Redundant Output signals + logic ready_ov[REP]; + logic valid_ov[REP]; // State machine TLDR // - counting the state from 0 to 2 if the handshake is good @@ -35,81 +45,115 @@ module time_TMR_start # ( // Next State Combinatorial Logic typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE_ONE, REPLICATE_TWO} state_t; - state_t state_d, state_q; - DataType data_d, data_q; - logic [IDSize-1:0] id_d, id_q; - - always_comb begin : next_state_logic - // Default to staying in same state - state_d = state_q; - data_d = data_q; - id_d = id_q; - - case (state_q) - STORE_AND_SEND: - if (valid_i) begin - data_d = data_i; - id_d = id_q + 1; + state_t state_v[REP], state_d[REP], state_q[REP]; + DataType data_v[REP], data_d[REP], data_q[REP]; + logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; + for (genvar r = 0; r < REP; r++) begin + always_comb begin : next_state_logic + // Default to staying in same state + state_v[r] = state_q[r]; + data_v[r] = data_q[r]; + id_v[r] = id_q[r]; + + case (state_q[r]) + STORE_AND_SEND: + if (valid_i) begin + data_v[r] = data_i; + id_v[r] = id_q[r] + 1; + + if (ready_i) begin + if (enable_i) begin + state_v[r] = REPLICATE_ONE; + end else begin + state_v[r] = STORE_AND_SEND; + end + end else begin + state_v[r] = SEND; + end + end + SEND: if (ready_i) begin if (enable_i) begin - state_d = REPLICATE_ONE; + state_v[r] = REPLICATE_ONE; // Reset seqeuence end else begin - state_d = STORE_AND_SEND; + state_v[r] = STORE_AND_SEND; end - end else begin - state_d = SEND; end - end - SEND: - if (ready_i) begin - if (enable_i) begin - state_d = REPLICATE_ONE; // Reset seqeuence - end else begin - state_d = STORE_AND_SEND; + REPLICATE_ONE: + if (ready_i) begin + state_v[r] = REPLICATE_TWO; // Reset seqeuence + end + REPLICATE_TWO: begin + if (ready_i) begin + state_v[r] = STORE_AND_SEND; end end - REPLICATE_ONE: - if (ready_i) begin - state_d = REPLICATE_TWO; // Reset seqeuence - end - REPLICATE_TWO: begin - if (ready_i) begin - state_d = STORE_AND_SEND; - end - end - endcase + endcase + end + end + + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3ENUM(state_v, state_d); + `VOTE3to3(id_v, id_d); + `VOTE3to3(data_v, data_d); + end else begin + assign state_d = state_v; + assign data_d = data_v; + assign id_d = id_v; + end + + // Generate default cases + state_t state_base[REP]; + DataType data_base[REP]; + logic [IDSize-1:0] id_base[REP]; + + for (genvar r = 0; r < REP; r++) begin + assign state_base[r] = STORE_AND_SEND; + assign data_base[r] = 0; + assign id_base[r] = 0; end // State Storage - `FF(state_q, state_d, STORE_AND_SEND); - `FF(data_q, data_d, '0); - `FF(id_q, id_d, '0); + `FF(state_q, state_d, state_base); + `FF(data_q, data_d, data_base); + `FF(id_q, id_d, id_base); // Output Combinatorial Logic - always_comb begin : output_logic - case (state_q) - STORE_AND_SEND: begin - valid_o = valid_i; - ready_o = 1; - end - SEND: begin - valid_o = '1; - ready_o = '0; - end - REPLICATE_ONE: begin - valid_o = '1; - ready_o = '0; - end - REPLICATE_TWO: begin - valid_o = '1; - ready_o = '0; - end - endcase + for (genvar r = 0; r < REP; r++) begin + always_comb begin : output_logic + case (state_q[r]) + STORE_AND_SEND: begin + valid_ov[r] = valid_i; + ready_ov[r] = 1; + end + SEND: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + REPLICATE_ONE: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + REPLICATE_TWO: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + endcase + end end // Output Voting Logic - assign data_o = data_d; - assign id_o = id_d; + assign data_o = data_d[0]; + assign id_o = id_d[0]; + + if (InternalRedundancy) begin : gen_output_voters + `VOTE3to1(ready_ov, ready_o); + `VOTE3to1(valid_ov, valid_o); + end else begin + assign ready_o = ready_ov[0]; + assign valid_o = valid_ov[0]; + end endmodule diff --git a/rtl/time_redundancy/voters.svh b/rtl/time_redundancy/voters.svh index 56054965..2d2d7b41 100644 --- a/rtl/time_redundancy/voters.svh +++ b/rtl/time_redundancy/voters.svh @@ -25,3 +25,9 @@ end always_comb begin \ `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal); \ end + +`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ +begin \ + output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ + output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ +end \ No newline at end of file diff --git a/run_tests.sh b/run_tests.sh index 9bf5efb8..8e618f13 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -51,15 +51,20 @@ call_vsim -GDataWidth=64 tb_ecc_secded call_vsim tb_ecc_scrubber call_vsim tb_voter_macros -call_vsim tb_time_tmr -GEarlyValidEnable=0 -call_vsim tb_time_tmr -GEarlyValidEnable=1 -call_vsim tb_time_tmr_lock -GEarlyValidEnable=0 -call_vsim tb_time_tmr_lock -GEarlyValidEnable=1 -call_vsim tb_time_dmr call_vsim tb_retry call_vsim tb_retry_inorder -call_vsim tb_time_dmr_retry -call_vsim tb_time_dmr_retry_lock -voptargs="+acc" + +for redundancy in 0 1; do + + for early_valid in 0 1; do + call_vsim tb_time_tmr -GEarlyValidEnable=$early_valid -GInternalRedundancy=$redundancy + call_vsim tb_time_tmr_lock -GEarlyValidEnable=$early_valid -GInternalRedundancy=$redundancy + done + + call_vsim tb_time_dmr -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr_retry -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GInternalRedundancy=$redundancy +done for num in 1 4 7; do call_vsim tb_rr_arb_tree_lock -GNumInp=$num -coverage -voptargs="+acc +cover=bcesfx" -suppress vsim-3009 diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv index 129cb30d..66082e28 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_time_dmr.sv @@ -2,6 +2,7 @@ module tb_time_dmr #( // DUT Parameters parameter IDSize = 4, parameter int LockTimeout = 4, + parameter bit InternalRedundancy = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -28,7 +29,8 @@ module tb_time_dmr #( time_DMR_start #( .DataType(data_t), .IDSize(IDSize), - .UseExternalId(0) + .UseExternalId(0), + .InternalRedundancy(InternalRedundancy) ) dut_start ( .clk_i(clk), .rst_ni(rst_n), @@ -52,7 +54,8 @@ module tb_time_dmr #( time_DMR_end #( .DataType(data_t), .LockTimeout(LockTimeout), - .IDSize(IDSize) + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) ) dut_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv index 5aae93d6..27ee9221 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_time_dmr_retry.sv @@ -1,11 +1,17 @@ -module tb_time_dmr_retry; +module tb_time_dmr_retry #( + // DUT Parameters + parameter int IDSize = 4, + parameter int LockTimeout = 4, + parameter bit InternalRedundancy = 0, + + // TB Parameters + parameter int unsigned TESTS = 10000, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns +) ( /* no ports on TB */ ); - // Clock Parameters - localparam time CLK_PERIOD = 10ns; - localparam time APPLICATION_DELAY = 2ns; - localparam time AQUISITION_DELAY = 8ns; localparam unsigned RST_CLK_CYCLES = 10; - localparam unsigned TESTS = 10000; // Parameters typedef logic [7:0] data_t; @@ -16,9 +22,6 @@ module tb_time_dmr_retry; tag_t tag; } tagged_data_t; - parameter IDSize = 4; - localparam int LockTimeout = 4; - // Testbench signals tagged_data_t golden_queue [$]; tagged_data_t data_golden, data_actual; @@ -85,7 +88,8 @@ module tb_time_dmr_retry; time_DMR_start #( .DataType(tagged_data_t), .IDSize(IDSize), - .UseExternalId(1) + .UseExternalId(1), + .InternalRedundancy(InternalRedundancy) ) dut_DMR_start ( .clk_i(clk), .rst_ni(rst_n), @@ -116,7 +120,8 @@ module tb_time_dmr_retry; time_DMR_end #( .DataType(tagged_data_t), .LockTimeout(LockTimeout), - .IDSize(IDSize) + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) ) dut_DMR_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 0f644cbd..baf51658 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -7,6 +7,7 @@ module tb_time_dmr_retry_lock #( parameter int OpgroupWidth = $clog2(NumOpgroups), parameter int IDSize = 5, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, + parameter bit InternalRedundancy = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -103,7 +104,8 @@ module tb_time_dmr_retry_lock #( time_DMR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), - .UseExternalId(1) + .UseExternalId(1), + .InternalRedundancy(InternalRedundancy) ) i_time_DMR_start ( .clk_i(clk), .rst_ni(rst_n), @@ -223,7 +225,8 @@ module tb_time_dmr_retry_lock #( time_DMR_end #( .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), - .IDSize (IDSize) + .IDSize (IDSize), + .InternalRedundancy(InternalRedundancy) ) i_time_DMR_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index 1eef3e42..df223824 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -3,6 +3,7 @@ module tb_time_tmr #( parameter IDSize = 4, parameter int LockTimeout = 4, parameter bit EarlyValidEnable = 0, + parameter bit InternalRedundancy = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -31,7 +32,8 @@ module tb_time_tmr #( time_TMR_start #( .DataType(data_t), - .IDSize(IDSize) + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) ) dut_start ( .clk_i(clk), .rst_ni(rst_n), @@ -53,7 +55,8 @@ module tb_time_tmr #( .DataType(data_t), .LockTimeout(LockTimeout), .IDSize(IDSize), - .EarlyValidEnable(EarlyValidEnable) + .EarlyValidEnable(EarlyValidEnable), + .InternalRedundancy(InternalRedundancy) ) dut_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index 52146607..9f659a30 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -7,7 +7,8 @@ module tb_time_tmr_lock #( parameter int OpgroupWidth = $clog2(NumOpgroups), parameter int IDSize = 5, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, - parameter bit EarlyValidEnable, + parameter bit EarlyValidEnable = 0, + parameter bit InternalRedundancy = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -59,7 +60,8 @@ module tb_time_tmr_lock #( time_TMR_start #( .DataType(tmr_stacked_t), - .IDSize (IDSize) + .IDSize (IDSize), + .InternalRedundancy(InternalRedundancy) ) i_time_TMR_start ( .clk_i(clk), .rst_ni(rst_n), @@ -170,7 +172,8 @@ module tb_time_tmr_lock #( .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), .IDSize (IDSize), - .EarlyValidEnable(EarlyValidEnable) + .EarlyValidEnable(EarlyValidEnable), + .InternalRedundancy(InternalRedundancy) ) i_time_TMR_end ( .clk_i(clk), .rst_ni(rst_n), From 5aa12c6fbfaa80a04b3f4bb95a8770dbe826d95f Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Wed, 24 Apr 2024 15:43:54 +0200 Subject: [PATCH 06/21] Added redundancy controller module and TB. --- Bender.yml | 2 + rtl/time_redundancy/redundancy_controller.sv | 97 ++++++++++++++++++++ run_tests.sh | 1 + src_files.yml | 1 + test/tb_redundancy_controller.sv | 95 +++++++++++++++++++ 5 files changed, 196 insertions(+) create mode 100644 rtl/time_redundancy/redundancy_controller.sv create mode 100644 test/tb_redundancy_controller.sv diff --git a/Bender.yml b/Bender.yml index 103aa136..f10f89b6 100644 --- a/Bender.yml +++ b/Bender.yml @@ -37,6 +37,7 @@ sources: - rtl/ecc_wrap/ecc_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv + - rtl/time_redundancy/redundancy_controller.sv - rtl/time_redundancy/retry_inorder_start.sv - rtl/time_redundancy/retry_start.sv - rtl/time_redundancy/time_TMR_end.sv @@ -111,6 +112,7 @@ sources: - test/tb_voter_macros.sv - test/tb_time_tmr.sv - test/tb_time_dmr.sv + - test/tb_redundancy_controller.sv - test/tb_retry.sv - test/tb_retry_inorder.sv - test/tb_time_dmr_retry.sv diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv new file mode 100644 index 00000000..0577d863 --- /dev/null +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -0,0 +1,97 @@ +`include "voters.svh" +`include "common_cells/registers.svh" + +module redundancy_controller # ( + // TODO: Add flush mechanism with timeout + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 +) ( + input logic clk_i, + input logic rst_ni, + + // Connection to outside + input logic enable_i, + output logic busy_o, + + // Connection to redundancy modules + input logic busy_i, + output logic enable_o, + + // Upstream Handshake + input logic valid_i, + output logic ready_o, + + // Downstream Handshake + output logic valid_o, + input logic ready_i +); + + // Redundant versions of output signals + logic valid_ov[REP]; + logic ready_ov[REP]; + logic busy_ov[REP]; + logic enable_ov[REP]; + + logic enable_v[REP], enable_d[REP], enable_q[REP]; + + for (genvar r = 0; r < REP; r++) begin + always_comb begin + if (busy_i) begin + enable_v[r] = enable_q[r]; + end else begin + enable_v[r] = enable_i; + end + end + end + + // State Voting Logic + if (InternalRedundancy) begin : gen_state_voters + `VOTE3to3(enable_v, enable_d); + end else begin + assign enable_d = enable_v; + end + + // Generate default case + logic enable_base[REP]; + for (genvar r = 0; r < REP; r++) begin + assign enable_base[r] = '0; + end + + `FFARN(enable_q, enable_d, enable_base, clk_i, rst_ni); + + // Output combinatorial logic + for (genvar r = 0; r < REP; r++) begin + always_comb begin + enable_ov[r] = enable_q[r]; + + if (enable_q[r] == enable_i) begin + valid_ov[r] = valid_i; + ready_ov[r] = ready_i; + busy_ov[r] = busy_i; + end else begin + valid_ov[r] = 1'b0; + ready_ov[r] = 1'b0; + busy_ov[r] = 1'b1; + end + end + end + + // Output voting logic + if (InternalRedundancy) begin : gen_output_voters + `VOTE3to1(enable_ov, enable_o); + `VOTE3to1(valid_ov, valid_o); + `VOTE3to1(ready_ov, ready_o); + `VOTE3to1(busy_ov, busy_o); + end else begin + assign enable_o = enable_ov[0]; + assign valid_o = valid_ov[0]; + assign ready_o = ready_ov[0]; + assign busy_o = busy_ov[0]; + end + +endmodule diff --git a/run_tests.sh b/run_tests.sh index 8e618f13..3e39f2f0 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -55,6 +55,7 @@ call_vsim tb_retry call_vsim tb_retry_inorder for redundancy in 0 1; do + call_vsim tb_redundancy_controller -GInternalRedundancy=$redundancy for early_valid in 0 1; do call_vsim tb_time_tmr -GEarlyValidEnable=$early_valid -GInternalRedundancy=$redundancy diff --git a/src_files.yml b/src_files.yml index 4c6b895f..6dffab0b 100644 --- a/src_files.yml +++ b/src_files.yml @@ -32,6 +32,7 @@ redundancy_cells: rtl/BUS_enc_dec/TCDM_XBAR_bus_ecc_enc.sv, rlt/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv, rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv, + rtl/time_redundancy/redundancy_controller.sv rtl/time_redundancy/retry_inorder_start.sv rtl/time_redundancy/retry_start.sv rtl/time_redundancy/time_TMR_end.sv diff --git a/test/tb_redundancy_controller.sv b/test/tb_redundancy_controller.sv new file mode 100644 index 00000000..7da21448 --- /dev/null +++ b/test/tb_redundancy_controller.sv @@ -0,0 +1,95 @@ +module tb_redundancy_controller #( + // DUT Parameters + parameter bit InternalRedundancy = 0, + + // TB Parameters + parameter int unsigned TESTS = 10, + parameter time CLK_PERIOD = 10ns, + parameter time APPLICATION_DELAY = 2ns, + parameter time AQUISITION_DELAY = 8ns + +) ( /* no ports on TB */ ); + + // Signals for dut + logic clk_i; + logic rst_ni; + + // Connection to outside + logic enable_i; + logic busy_o; + + // Connection to redundancy modules + logic busy_i; + logic enable_o; + + // Upstream Handshake + logic valid_i; + logic ready_o; + + // Downstream Handshake + logic valid_o; + logic ready_i; + + // Dut + redundancy_controller # ( + .InternalRedundancy(InternalRedundancy) + ) i_dut ( + .* + ); + + initial clk_i = 0; + initial rst_ni = 1; + always #((CLK_PERIOD/2)) clk_i = ~clk_i; + + task check_handshake_passthrough(); + valid_i = $random; + ready_i = $random; + # (AQUISITION_DELAY - APPLICATION_DELAY); + assert(valid_o == valid_i); + assert(ready_o == ready_o); + assert(enable_o == enable_i); + endtask + + task check_handshake_blocked(); + valid_i = $random; + ready_i = $random; + # (AQUISITION_DELAY - APPLICATION_DELAY); + assert(valid_o == 0); + assert(ready_o == 0); + assert(enable_o != enable_i); + endtask + + + initial begin + // Start state + enable_i = 0; + busy_i = 0; + + repeat (TESTS) begin + @(posedge clk_i); + # APPLICATION_DELAY; + busy_i = 1; + enable_i = ~enable_i; + + repeat ($urandom_range(0, 5)) begin + check_handshake_blocked(); + @(posedge clk_i); + #APPLICATION_DELAY; + end + + @(posedge clk_i); + # APPLICATION_DELAY; + busy_i = 0; + check_handshake_blocked(); + + repeat (5) begin + @(posedge clk_i); + #APPLICATION_DELAY; + check_handshake_passthrough(); + end + + end + $finish; + end + +endmodule \ No newline at end of file From 26831caf849409c4b0b4ef31405173f58175b9a5 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 08:46:24 +0200 Subject: [PATCH 07/21] Added module Documentation - Added module headers explaining how to use them and who to contact for questions. - Named all generate blocks. --- rtl/time_redundancy/redundancy_controller.sv | 31 ++++--- rtl/time_redundancy/retry_end.sv | 24 ++++- rtl/time_redundancy/retry_inorder_end.sv | 26 +++++- rtl/time_redundancy/retry_inorder_start.sv | 26 +++++- rtl/time_redundancy/retry_start.sv | 28 +++++- rtl/time_redundancy/rr_arb_tree_lock.sv | 4 +- rtl/time_redundancy/time_DMR_end.sv | 92 ++++++++++++++------ rtl/time_redundancy/time_DMR_start.sv | 56 +++++++++--- rtl/time_redundancy/time_TMR_end.sv | 84 +++++++++++------- rtl/time_redundancy/time_TMR_start.sv | 46 +++++++--- 10 files changed, 310 insertions(+), 107 deletions(-) diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv index 0577d863..26b269bc 100644 --- a/rtl/time_redundancy/redundancy_controller.sv +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -1,10 +1,21 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Module that can correctly enable / disable time_TMR or time_DMR modules +// when they do not hold valid data. It does so by stalling the handshake +// upstream of the redundancy modules until they are empty, +// then enables / disables them. +// +// Caveat: In case a fault occurs while this module is trying to switch to a non-redundant mode +// the module might currently stall since not all data in the combinatorial process can propperly +// be consumed and the condition for switching can not be reached. +// Switching back to the redundant mode will unstall the setup. + `include "voters.svh" `include "common_cells/registers.svh" module redundancy_controller # ( - // TODO: Add flush mechanism with timeout // Determines if the internal state machines should - // be parallely redundant, meaning errors inside this module + // be parallely redundant, meaning errors inside this module // can also not cause errors in the output // The external output is never protected! parameter bit InternalRedundancy = 0, @@ -29,8 +40,8 @@ module redundancy_controller # ( // Downstream Handshake output logic valid_o, input logic ready_i -); - +); + // Redundant versions of output signals logic valid_ov[REP]; logic ready_ov[REP]; @@ -39,7 +50,7 @@ module redundancy_controller # ( logic enable_v[REP], enable_d[REP], enable_q[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin if (busy_i) begin enable_v[r] = enable_q[r]; @@ -58,14 +69,14 @@ module redundancy_controller # ( // Generate default case logic enable_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign enable_base[r] = '0; end `FFARN(enable_q, enable_d, enable_base, clk_i, rst_ni); // Output combinatorial logic - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin enable_ov[r] = enable_q[r]; @@ -75,19 +86,19 @@ module redundancy_controller # ( busy_ov[r] = busy_i; end else begin valid_ov[r] = 1'b0; - ready_ov[r] = 1'b0; + ready_ov[r] = 1'b0; busy_ov[r] = 1'b1; end end end // Output voting logic - if (InternalRedundancy) begin : gen_output_voters + if (InternalRedundancy) begin: gen_output_voters `VOTE3to1(enable_ov, enable_o); `VOTE3to1(valid_ov, valid_o); `VOTE3to1(ready_ov, ready_o); `VOTE3to1(busy_ov, busy_o); - end else begin + end else begin: gen_output_passthrough assign enable_o = enable_ov[0]; assign valid_o = valid_ov[0]; assign ready_o = ready_ov[0]; diff --git a/rtl/time_redundancy/retry_end.sv b/rtl/time_redundancy/retry_end.sv index c5565ebd..428c0571 100644 --- a/rtl/time_redundancy/retry_end.sv +++ b/rtl/time_redundancy/retry_end.sv @@ -1,3 +1,23 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: retry is a pair of modules that can be used to run an operation +// passing through a (pipelined) combinatorial process. +// +// In order to propperly function: +// - id_o of retry_start needs to be passed paralelly along the combinatorial logic, +// using the same handshake and arrive at id_i of retry_end +// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end +// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end +// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - All elements in processing have a unique ID +// +// This modules might cause operations to reach the output of retry_end in a different +// order than they have entered retry_start e.g. can be out-of-order in case of a retry +// so make sure you can deal with out-of-order results. +// For the special case that the process is purely combinatorial, the same operaton is tried again +// in the very next cycle and thus the order is preserved. +// If you need in-order for pipelined processes have a look at retry_inorder instead. + module retry_end # ( parameter type DataType = logic, parameter IDSize = 1 @@ -22,12 +42,12 @@ module retry_end # ( output logic retry_valid_o, input logic retry_ready_i ); - + // Assign signals assign retry_id_o = id_i; assign data_o = data_i; - always_comb begin + always_comb begin: gen_output if (needs_retry_i) begin retry_valid_o = valid_i; ready_o = retry_ready_i; diff --git a/rtl/time_redundancy/retry_inorder_end.sv b/rtl/time_redundancy/retry_inorder_end.sv index f70a5595..b286a012 100644 --- a/rtl/time_redundancy/retry_inorder_end.sv +++ b/rtl/time_redundancy/retry_inorder_end.sv @@ -1,3 +1,21 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: retry is a pair of modules that can be used to run an operation +// passing through a (pipelined) combinatorial process. +// +// In order to propperly function: +// - id_o of retry_start needs to be passed paralelly along the combinatorial logic, +// using the same handshake and arrive at id_i of retry_end +// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end +// - retry_id_o of retry_start needs to be directly connected to retry_id_i of retry_end +// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end +// - retry_lock_i of retry_start needs to be directly connected to retry_lock_o of retry_end +// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - All elements in processing have a unique ID +// +// This module always keeps results in order by also retrying results that have been correct +// but at the wronge place or time. + `include "common_cells/registers.svh" module retry_inorder_end # ( @@ -26,7 +44,7 @@ module retry_inorder_end # ( output logic retry_lock_o, input logic retry_ready_i ); - + // Signals do not change, only validity changes assign retry_id_o = id_i; assign data_o = data_i; @@ -34,10 +52,10 @@ module retry_inorder_end # ( logic [IDSize-1:0] failed_id_d, failed_id_q; logic retry_d, retry_q; - always_comb begin + always_comb begin: gen_next_state if (valid_i & retry_q) begin failed_id_d = failed_id_q; - retry_d = ~(failed_id_q == id_i); + retry_d = ~(failed_id_q == id_i); end else if (valid_i & needs_retry_i) begin failed_id_d = retry_id_i; retry_d = 1; @@ -52,7 +70,7 @@ module retry_inorder_end # ( `FF(retry_q, retry_d, '0); `FF(failed_id_q, failed_id_d, '0); - always_comb begin + always_comb begin: gen_output if (retry_d) begin retry_valid_o = valid_i; ready_o = retry_ready_i; diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index defe68bf..c2656a95 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -1,3 +1,21 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: retry is a pair of modules that can be used to run an operation +// passing through a (pipelined) combinatorial process. +// +// In order to propperly function: +// - id_o of retry_start needs to be passed paralelly along the combinatorial logic, +// using the same handshake and arrive at id_i of retry_end +// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end +// - retry_id_o of retry_start needs to be directly connected to retry_id_i of retry_end +// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end +// - retry_lock_i of retry_start needs to be directly connected to retry_lock_o of retry_end +// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - All elements in processing have a unique ID +// +// This module always keeps results in order by also retrying results that have been correct +// but at the wronge place or time. + `include "common_cells/registers.svh" `include "voters.svh" @@ -33,7 +51,7 @@ module retry_inorder_start # ( logic failed_valid_d, failed_valid_q; logic retry_lock_d, retry_lock_q; - always_comb begin + always_comb begin: gen_next_cycle_decision if (ready_i | retry_valid_i) begin failed_valid_d = retry_valid_i; end else begin @@ -59,7 +77,7 @@ module retry_inorder_start # ( logic [IDSize-1:0] counter_id_d, counter_id_q; - always_comb begin + always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); end else begin @@ -74,7 +92,7 @@ module retry_inorder_start # ( logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; - always_comb begin + always_comb begin: gen_failed_state // Keep data as is as abase data_storage_d = data_storage_q; @@ -85,7 +103,7 @@ module retry_inorder_start # ( `FF(data_storage_q, data_storage_d, 0); - always_comb begin + always_comb begin: gen_output if (failed_valid_q & ready_i) begin data_o = data_storage_q[failed_id_q[IDSize-2:0]]; end else begin diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index 3b43f64b..36ee8cf5 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -1,3 +1,23 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: retry is a pair of modules that can be used to run an operation +// passing through a (pipelined) combinatorial process. +// +// In order to propperly function: +// - id_o of retry_start needs to be passed paralelly along the combinatorial logic, +// using the same handshake and arrive at id_i of retry_end +// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end +// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end +// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - All elements in processing have a unique ID +// +// This modules might cause operations to reach the output of retry_end in a different +// order than they have entered retry_start e.g. can be out-of-order in case of a retry +// so make sure you can deal with out-of-order results. +// For the special case that the process is purely combinatorial, the same operaton is tried again +// in the very next cycle and thus the order is preserved. +// If you need in-order for pipelined processes have a look at retry_inorder instead. + `include "common_cells/registers.svh" `include "voters.svh" @@ -30,7 +50,7 @@ module retry_start # ( logic [IDSize-1:0] failed_id_d, failed_id_q; logic failed_valid_d, failed_valid_q; - always_comb begin + always_comb begin: gen_next_cycle_decision if (ready_i | retry_valid_i) begin failed_valid_d = retry_valid_i; end else begin @@ -54,7 +74,7 @@ module retry_start # ( logic [IDSize-1:0] counter_id_d, counter_id_q; - always_comb begin + always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); end else begin @@ -69,7 +89,7 @@ module retry_start # ( logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; - always_comb begin + always_comb begin: gen_failed_state // Keep data as is as abase data_storage_d = data_storage_q; @@ -80,7 +100,7 @@ module retry_start # ( `FF(data_storage_q, data_storage_d, 0); - always_comb begin + always_comb begin: gen_output if (failed_valid_q & ready_i) begin data_o = data_storage_q[failed_id_q[IDSize-2:0]]; end else begin diff --git a/rtl/time_redundancy/rr_arb_tree_lock.sv b/rtl/time_redundancy/rr_arb_tree_lock.sv index 44bc510f..26ff9c74 100644 --- a/rtl/time_redundancy/rr_arb_tree_lock.sv +++ b/rtl/time_redundancy/rr_arb_tree_lock.sv @@ -13,7 +13,7 @@ // Maurus Item , ETH Zurich // Date: 10.04.2023 // Description: logarithmic arbitration tree with round robin arbitration scheme -// that can be externally locked to not arbitrate. +// that can be externally locked to not arbitrate and just send the same output. /// The rr_arb_tree employs non-starving round robin-arbitration - i.e., the priorities /// rotate each cycle. @@ -93,7 +93,7 @@ module rr_arb_tree_lock #( input logic flush_i, /// External round-robin priority. input idx_t rr_i, - /// lock idx signal, only used if Only used if `ExtPrio` is `1'b0.` + /// lock idx signal, only used if `ExtPrio` is `1'b0.` input logic lock_rr_i, /// Input requests arbitration. input logic [NumIn-1:0] req_i, diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index bbb08f36..ec7e55f5 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -1,3 +1,37 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: time_DMR is a pair of modules that can be used to +// detect faults in any (pipelined) combinatorial process. This is +// done by repeating each input twice and comparing the outputs. +// +// Faults that can be handled: +// - Any number of fault in the datapath during one cycle e.g. wrong result. +// - A single fault in the handshake (valid or ready) e.g. dropped or injected results. +// - A single fault in the accompanying ID. +// +// In case a fault occurs and the result needs to be recalculated, the needs_retry_o signal +// is asserted for one cycle and the ID that needs to be tried again is given. +// Not all faults nessesarily need a retry, in case you just want to know if +// faults are happening you can use fault_detected_o for it. +// +// This needs_retry_o signal should be used to trigger some kind of mitigating action: +// For example, one could use the "retry" modules or "retry_inorder" modules +// to recalculate in hardware, or invoke some recalculation or error on the software side. +// +// In order to propperly function: +// - next_id_o of time_DMR_start needs to be directly connected to next_id_i of time_DMR_end. +// - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake +// and arrive at id_i of time_DMR_end. +// - All operation pairs in contact with each other have a unique ID. +// - The module can only be enabled / disabled when the combinatorially process holds no valid data. +// +// This module can deal with out-of-order combinatorial processes under the conditions that +// the two operations belonging together are not separated. +// To facilitate this on the output side the module has the lock_o signal which is asserted if +// the module wants another element of the same kind in the next cycle. +// This can for example be used with the rr_arb_tree_lock module, but other implementations are +// permissible. + `include "voters.svh" `include "common_cells/registers.svh" @@ -19,7 +53,7 @@ module time_DMR_end # ( // Needs to match with time_TMR_start! parameter int unsigned IDSize = 1, // Determines if the internal state machines should - // be parallely redundant, meaning errors inside this module + // be parallely redundant, meaning errors inside this module // can also not cause errors in the output // The external output is never protected! parameter bit InternalRedundancy = 0, @@ -51,7 +85,7 @@ module time_DMR_end # ( // Output Flags for Error Counting output logic fault_detected_o -); +); // Redundant Output signals logic ready_ov[REP]; @@ -80,7 +114,7 @@ module time_DMR_end # ( logic data_same_in, id_same_in; logic data_same_q, id_same_q; - always_comb begin: data_same_generation_comb + always_comb begin: gen_data_same data_same_in = '0; id_same_in = '0; @@ -118,8 +152,8 @@ module time_DMR_end # ( logic data_usable[REP]; // Flag Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : data_flags_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_data_flags + always_comb begin: gen_data_flags_comb // Some new element just showed up and we need to send data outwards again. new_element_arrived[r] = (id_same == 2'b01) || (id_same == 2'b00); @@ -143,8 +177,8 @@ module time_DMR_end # ( // Next State Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : next_state_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin: gen_next_state_comb // Default to staying in the same state state_v[r] = state_q[r]; @@ -167,14 +201,14 @@ module time_DMR_end # ( // Generate default cases state_t state_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_base[r] = BASE; end // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters + if (InternalRedundancy) begin: gen_state_voters `VOTE3to3ENUM(state_v, state_d); - end else begin + end else begin: gen_state_passthrough assign state_d = state_v; end @@ -182,8 +216,8 @@ module time_DMR_end # ( `FF(state_q, state_d, state_base); // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin: output_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) BASE: valid_internal[r] = valid_i & new_element_arrived[r]; @@ -214,8 +248,8 @@ module time_DMR_end # ( logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; // Next State Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : lock_comb + for (genvar r = 0; r < REP; r++) begin: gen_lock_next_state + always_comb begin: gen_lock_next_state_comb if (counter_q[r] > LockTimeout) begin lock_v[r] = 0; counter_v[r] = 0; @@ -241,7 +275,7 @@ module time_DMR_end # ( if (InternalRedundancy) begin : gen_lock_voters `VOTE3to3(lock_v, lock_d); `VOTE3to3(counter_v, counter_d); - end else begin + end else begin: gen_lock_passthrough assign counter_d = counter_v; assign lock_d = lock_v; end @@ -251,7 +285,7 @@ module time_DMR_end # ( // Default state logic lock_base[REP]; logic [$clog2(LockTimeout)-1:0] counter_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state assign lock_base[r] = '0; assign counter_base[r] = '0; end @@ -268,8 +302,8 @@ module time_DMR_end # ( logic [2 ** (IDSize-1)-1:0] recently_seen_v[REP], recently_seen_d[REP], recently_seen_q[REP]; - for (genvar r = 0; r < REP; r++) begin - always_comb begin + for (genvar r = 0; r < REP; r++) begin: gen_deduplication_next_state + always_comb begin: gen_deduplication_next_state_comb recently_seen_v[r] = recently_seen_q[r]; recently_seen_v[r][next_id_i[IDSize-2:0]] = 0; @@ -281,29 +315,29 @@ module time_DMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_deduplication_voters + if (InternalRedundancy) begin: gen_deduplication_voters `VOTE3to3(recently_seen_v, recently_seen_d); - end else begin + end else begin: gen_deduplication_passthrough assign recently_seen_d = recently_seen_v; end // Default state logic [2 ** (IDSize-1)-1:0] recently_seen_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_deduplication_default_state assign recently_seen_base[r] = ~'0; // All 1s! end // State Storage `FF(recently_seen_q, recently_seen_d, recently_seen_base); - for (genvar r = 0; r < REP; r++) begin - always_comb begin + for (genvar r = 0; r < REP; r++) begin: gen_deduplication_output + always_comb begin: gen_deduplication_output_comb if (enable_i) begin if (id_fault_q | recently_seen_q[r][id_q[IDSize-2:0]] & valid_internal[r]) begin valid_ov[r] = 0; end else begin valid_ov[r] = valid_internal[r]; - end + end needs_retry_ov[r] = id_same[0] & !data_usable[r]; end else begin valid_ov[r] = valid_internal[r]; @@ -323,31 +357,31 @@ module time_DMR_end # ( logic fault_detected_d[REP], fault_detected_q[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state assign fault_detected_d[r] = ~|full_same[1:0]; end // Default state logic fault_detected_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_default_state assign fault_detected_base[r] = '0; end `FF(fault_detected_q, fault_detected_d, fault_detected_base); - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_output assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; end /////////////////////////////////////////////////////////////////////////////////////////////////// // Output Voting - if (InternalRedundancy) begin : gen_output_voters + if (InternalRedundancy) begin: gen_output_voters `VOTE3to1(ready_ov, ready_o); `VOTE3to1(valid_ov, valid_o); `VOTE3to1(needs_retry_ov, needs_retry_o); `VOTE3to1(fault_detected_ov, fault_detected_o); - end else begin + end else begin: gen_output_passthrough assign ready_o = ready_ov[0]; assign valid_o = valid_ov[0]; assign needs_retry_o = needs_retry_ov[0]; diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 03acfa31..4c95091b 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -1,3 +1,37 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: time_DMR is a pair of modules that can be used to +// detect faults in any (pipelined) combinatorial process. This is +// done by repeating each input twice and comparing the outputs. +// +// Faults that can be handled: +// - Any number of fault in the datapath during one cycle e.g. wrong result. +// - A single fault in the handshake (valid or ready) e.g. dropped or injected results. +// - A single fault in the accompanying ID. +// +// In case a fault occurs and the result needs to be recalculated, the needs_retry_o signal +// is asserted for one cycle and the ID that needs to be tried again is given. +// Not all faults nessesarily need a retry, in case you just want to know if +// faults are happening you can use fault_detected_o for it. +// +// This needs_retry_o signal should be used to trigger some kind of mitigating action: +// For example, one could use the "retry" modules or "retry_inorder" modules +// to recalculate in hardware, or invoke some recalculation or error on the software side. +// +// In order to propperly function: +// - next_id_o of time_DMR_start needs to be directly connected to next_id_i of time_DMR_end. +// - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake +// and arrive at id_i of time_DMR_end. +// - All operation pairs in contact with each other have a unique ID. +// - The module can only be enabled / disabled when the combinatorially process holds no valid data. +// +// This module can deal with out-of-order combinatorial processes under the conditions that +// the two operations belonging together are not separated. +// To facilitate this on the output side the module has the lock_o signal which is asserted if +// the module wants another element of the same kind in the next cycle. +// This can for example be used with the rr_arb_tree_lock module, but other implementations are +// permissible. + `include "voters.svh" `include "common_cells/registers.svh" @@ -15,7 +49,7 @@ module time_DMR_start # ( // Set to 1 if the id_i port should be used parameter bit UseExternalId = 0, // Determines if the internal state machines should - // be parallely redundant, meaning errors inside this module + // be parallely redundant, meaning errors inside this module // can also not cause errors in the output // The external output is never protected! parameter bit InternalRedundancy = 0, @@ -40,7 +74,7 @@ module time_DMR_start # ( output logic [IDSize-1:0] id_o, output logic valid_o, input logic ready_i -); +); // Redundant Output signals logic [IDSize-1:0] next_id_ov[REP]; logic ready_ov[REP]; @@ -56,8 +90,8 @@ module time_DMR_start # ( DataType data_v[REP], data_d[REP], data_q[REP]; logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; - for (genvar r = 0; r < REP; r++) begin - always_comb begin : next_state_logic + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin: gen_next_state_comb // Default to staying in same state state_v[r] = state_q[r]; data_v[r] = data_q[r]; @@ -74,7 +108,7 @@ module time_DMR_start # ( if (valid_i) begin data_v[r] = data_i; id_v[r] = next_id_ov[r]; - + if (ready_i) begin if (enable_i) begin state_v[r] = REPLICATE; @@ -106,7 +140,7 @@ module time_DMR_start # ( `VOTE3to3ENUM(state_v, state_d); `VOTE3to3(id_v, id_d); `VOTE3to3(data_v, data_d); - end else begin + end else begin: gen_state_passthrough assign state_d = state_v; assign data_d = data_v; assign id_d = id_v; @@ -117,7 +151,7 @@ module time_DMR_start # ( DataType data_base[REP]; logic [IDSize-1:0] id_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_base[r] = STORE_AND_SEND; assign data_base[r] = 0; assign id_base[r] = 0; @@ -129,8 +163,8 @@ module time_DMR_start # ( `FF(id_q, id_d, id_base); // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : output_logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin : gen_output_comb case (state_q[r]) STORE_AND_SEND: begin valid_ov[r] = valid_i; @@ -152,11 +186,11 @@ module time_DMR_start # ( assign data_o = data_d[0]; assign id_o = id_d[0]; - if (InternalRedundancy) begin : gen_output_voters + if (InternalRedundancy) begin: gen_output_voters `VOTE3to1(next_id_ov, next_id_o); `VOTE3to1(ready_ov, ready_o); `VOTE3to1(valid_ov, valid_o); - end else begin + end else begin: gen_output_passthrough assign next_id_o = next_id_ov[0]; assign ready_o = ready_ov[0]; assign valid_o = valid_ov[0]; diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 4cbeb82c..a8b41593 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -1,3 +1,27 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: time_TMR is a pair of modules that can be used to error correct +// any transient fault in a (pipelined) combinatorial process. This is done by +// repeating the element three times and comparing the results +// +// Faults that can be handled: +// - Any number of fault in the datapath during one cycle e.g. wrong result. +// - A single fault in the handshake (valid or ready) e.g. dropped or injected results. +// - A single fault in the accompanying ID. +// +// In order to propperly function: +// - id_o of time_TMR_start needs to be passed paralelly to the combinatorial logic, +// using the same handshake and arrive at id_i of time_TMR_end. +// - All operation tripplets in contact which each other have a unique ID. +// - The module can only be enabled / disabled when the combinatorially process holds no valid data. +// +// This module can deal with out-of-order combinatorial processes under the conditions that +// the three operations belonging together are not separated. +// To facilitate that the tripples stay together, the module has the lock_o signal +// Which defines if the same pipeline should output again in the next cycle. +// This can for example be used with the rr_arb_tree_lock module, but other implementations are +// permissible. + `include "voters.svh" `include "common_cells/registers.svh" @@ -20,7 +44,7 @@ module time_TMR_end # ( parameter int unsigned IDSize = 1, // This parameter chooses the implementation of the internal state machine. // EarlyValidEnable = 1: - // The state machine will return a valid output after it recieved two out of three + // The state machine will return a valid output after it recieved two out of three // data elements belonging together if possible e.g. no faults occur (otherwise valid in third cycle) // This option slightly increases area and critical path. // EarlyValidEnable = 0: @@ -28,7 +52,7 @@ module time_TMR_end # ( // Internal area and critical path are reduced. parameter bit EarlyValidEnable = 0, // Determines if the internal state machines should - // be parallely redundant, meaning errors inside this module + // be parallely redundant, meaning errors inside this module // can also not cause errors in the output // The external output is never protected! parameter bit InternalRedundancy = 0, @@ -83,7 +107,7 @@ module time_TMR_end # ( logic [2:0] data_same_in, id_same_in; logic [1:0] data_same_d, data_same_q, id_same_d, id_same_q; - always_comb begin: data_same_generation_comb + always_comb begin: gen_data_same data_same_in = '0; id_same_in = '0; data_o = '0; @@ -151,8 +175,8 @@ module time_TMR_end # ( logic element_relies_on_input[REP]; logic data_usable[REP]; - for (genvar r = 0; r < REP; r++) begin - always_comb begin : input_reassignment_comb + for (genvar r = 0; r < REP; r++) begin: gen_data_flags + always_comb begin: gen_data_flags_comb // Some new element just showed up and we need to send data outwards again. new_element_arrived[r] = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair @@ -188,8 +212,8 @@ module time_TMR_end # ( // Next State Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : next_state_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin: gen_next_state_comb // Default to staying in the same state state_v[r] = state_q[r]; @@ -237,13 +261,13 @@ module time_TMR_end # ( // State Voting Logic if (InternalRedundancy) begin : gen_state_voters `VOTE3to3ENUM(state_v, state_d); - end else begin + end else begin: gen_state_passthrough assign state_d = state_v; end // Generate default cases state_t state_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_base[r] = WAIT_FOR_VALID; end @@ -251,8 +275,8 @@ module time_TMR_end # ( `FF(state_q, state_d, state_base); // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin: output_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) BASE: valid_ov[r] = (!element_relies_on_input[r] | valid_i) & data_usable[r] & new_element_arrived[r]; @@ -289,8 +313,8 @@ module time_TMR_end # ( logic id_all_same[REP]; logic data_usable[REP]; - for (genvar r = 0; r < REP; r++) begin - always_comb begin : data_flags_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_data_flags + always_comb begin: gen_data_flags_comb new_id_arrived[r] = ( (id_same == 5'b00111) || (id_same == 5'b00100) || @@ -318,8 +342,8 @@ module time_TMR_end # ( state_t state_v[REP], state_d[REP], state_q[REP]; // Next State Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : next_state_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin: gen_next_state_comb // Default to staying in the same state state_v[r] = state_q[r]; @@ -361,13 +385,13 @@ module time_TMR_end # ( // State Voting Logic if (InternalRedundancy) begin : gen_state_voters `VOTE3to3ENUM(state_v, state_d); - end else begin + end else begin: gen_state_passthrough assign state_d = state_v; end // Generate default cases state_t state_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_base[r] = WAIT_FOR_VALID; end @@ -375,8 +399,8 @@ module time_TMR_end # ( `FF(state_q, state_d, state_base); // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin: output_generation_comb + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) BASE: valid_ov[r] = new_id_arrived[r] & data_usable[r]; @@ -414,8 +438,8 @@ module time_TMR_end # ( logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; // Next State Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : lock_comb + for (genvar r = 0; r < REP; r++) begin: gen_lock_next_state + always_comb begin : gen_lock_next_state_comb if (counter_q[r] > LockTimeout) begin lock_v[r] = 0; counter_v[r] = 0; @@ -438,10 +462,10 @@ module time_TMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_lock_voters + if (InternalRedundancy) begin: gen_lock_voters `VOTE3to3(lock_v, lock_d); `VOTE3to3(counter_v, counter_d); - end else begin + end else begin: gen_lock_passthrough assign counter_d = counter_v; assign lock_d = lock_v; end @@ -451,7 +475,7 @@ module time_TMR_end # ( // Default state logic lock_base[REP]; logic [$clog2(LockTimeout)-1:0] counter_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state assign lock_base[r] = '0; assign counter_base[r] = '0; end @@ -463,27 +487,27 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // Build error flag - // Since we can already output at two same elements we could have not seen an error yet, + // Since we can already output at two same elements we could have not seen an error yet, // so we can't rely on valid / ready to be in sync with 3 same elements! - // Instead we use the following observation: If the data comes nicely in pairs of 3 we + // Instead we use the following observation: If the data comes nicely in pairs of 3 we // always have at least two data elements that are the same. // Make output only 1 for a signly cycle even if internal pipeline is stopped logic fault_detected_d[REP], fault_detected_q[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state assign fault_detected_d[r] = ~|full_same[1:0]; end // Default state logic fault_detected_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_default_state assign fault_detected_base[r] = '0; end `FF(fault_detected_q, fault_detected_d, fault_detected_base); - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_flag_output assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; end @@ -494,7 +518,7 @@ module time_TMR_end # ( `VOTE3to1(ready_ov, ready_o); `VOTE3to1(valid_ov, valid_o); `VOTE3to1(fault_detected_ov, fault_detected_o); - end else begin + end else begin: gen_output_passthrough assign ready_o = ready_ov[0]; assign valid_o = valid_ov[0]; assign fault_detected_o = fault_detected_ov[0]; diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index a66d0049..0107d42e 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -1,3 +1,27 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: time_TMR is a pair of modules that can be used to error correct +// any transient fault in a (pipelined) combinatorial process. This is done by +// repeating the element three times and comparing the results +// +// Faults that can be handled: +// - Any number of fault in the datapath during one cycle e.g. wrong result. +// - A single fault in the handshake (valid or ready) e.g. dropped or injected results. +// - A single fault in the accompanying ID. +// +// In order to propperly function: +// - id_o of time_TMR_start needs to be passed paralelly to the combinatorial logic, +// using the same handshake and arrive at id_i of time_TMR_end. +// - All operation tripplets in contact which each other have a unique ID. +// - The module can only be enabled / disabled when the combinatorially process holds no valid data. +// +// This module can deal with out-of-order combinatorial processes under the conditions that +// the three operations belonging together are not separated. +// To facilitate that the tripples stay together, the module has the lock_o signal +// Which defines if the same pipeline should output again in the next cycle. +// This can for example be used with the rr_arb_tree_lock module, but other implementations are +// permissible. + `include "voters.svh" `include "common_cells/registers.svh" @@ -13,7 +37,7 @@ module time_TMR_start # ( // Needs to match with time_TMR_end! parameter int unsigned IDSize = 1, // Determines if the internal state machines should - // be parallely redundant, meaning errors inside this module + // be parallely redundant, meaning errors inside this module // can also not cause errors in the output // The external output is never protected! parameter bit InternalRedundancy = 0, @@ -34,7 +58,7 @@ module time_TMR_start # ( output logic [IDSize-1:0] id_o, output logic valid_o, input logic ready_i -); +); // Redundant Output signals logic ready_ov[REP]; logic valid_ov[REP]; @@ -49,8 +73,8 @@ module time_TMR_start # ( DataType data_v[REP], data_d[REP], data_q[REP]; logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; - for (genvar r = 0; r < REP; r++) begin - always_comb begin : next_state_logic + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin : gen_next_state_comb // Default to staying in same state state_v[r] = state_q[r]; data_v[r] = data_q[r]; @@ -94,11 +118,11 @@ module time_TMR_start # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters + if (InternalRedundancy) begin: gen_state_voters `VOTE3to3ENUM(state_v, state_d); `VOTE3to3(id_v, id_d); `VOTE3to3(data_v, data_d); - end else begin + end else begin: gen_state_passthrough assign state_d = state_v; assign data_d = data_v; assign id_d = id_v; @@ -109,7 +133,7 @@ module time_TMR_start # ( DataType data_base[REP]; logic [IDSize-1:0] id_base[REP]; - for (genvar r = 0; r < REP; r++) begin + for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_base[r] = STORE_AND_SEND; assign data_base[r] = 0; assign id_base[r] = 0; @@ -121,8 +145,8 @@ module time_TMR_start # ( `FF(id_q, id_d, id_base); // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin - always_comb begin : output_logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb case (state_q[r]) STORE_AND_SEND: begin valid_ov[r] = valid_i; @@ -148,10 +172,10 @@ module time_TMR_start # ( assign data_o = data_d[0]; assign id_o = id_d[0]; - if (InternalRedundancy) begin : gen_output_voters + if (InternalRedundancy) begin: gen_output_voters `VOTE3to1(ready_ov, ready_o); `VOTE3to1(valid_ov, valid_o); - end else begin + end else begin: gen_output_voters assign ready_o = ready_ov[0]; assign valid_o = valid_ov[0]; end From 59df08857c00efd30fb8f8b3776b6ea414911f59 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 09:01:37 +0200 Subject: [PATCH 08/21] Bugfixes - Testbench Typos - Fixed a bug where result could be output twice on error. - Fixed a bug that causes fault_detected_o to trigger to often. - Made fault detected properly depend on valid_i. --- rtl/time_redundancy/time_TMR_end.sv | 10 ++++++---- test/tb_time_dmr.sv | 2 +- test/tb_time_dmr_retry_lock.sv | 2 +- test/tb_time_tmr.sv | 18 +++++++++--------- test/tb_time_tmr_lock.sv | 22 +++++++++++----------- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index a8b41593..7ec9b6ee 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -311,6 +311,7 @@ module time_TMR_end # ( logic new_id_arrived[REP]; logic id_in_input[REP]; logic id_all_same[REP]; + logic id_all_cover[REP]; logic data_usable[REP]; for (genvar r = 0; r < REP; r++) begin: gen_data_flags @@ -325,6 +326,7 @@ module time_TMR_end # ( id_in_input[r] = |id_same[1:0]; id_all_same[r] = &id_same[2:0]; + id_all_cover[r] = id_same[1]; data_usable[r] = |data_same[2:0]; end @@ -351,7 +353,7 @@ module time_TMR_end # ( BASE: if (new_id_arrived[r]) begin if (ready_i) begin - if (id_all_same[r]) begin + if (id_all_cover[r]) begin state_v[r] = WAIT_FOR_VALID_X2; end else begin state_v[r] = WAIT_FOR_VALID; @@ -362,7 +364,7 @@ module time_TMR_end # ( end WAIT_FOR_READY: if (ready_i) begin - if (id_all_same[r]) begin + if (id_all_cover[r]) begin state_v[r] = WAIT_FOR_VALID_X2; end else begin state_v[r] = WAIT_FOR_VALID; @@ -490,13 +492,13 @@ module time_TMR_end # ( // Since we can already output at two same elements we could have not seen an error yet, // so we can't rely on valid / ready to be in sync with 3 same elements! // Instead we use the following observation: If the data comes nicely in pairs of 3 we - // always have at least two data elements that are the same. + // always have at least two data elements that are the same in the first 3 elements. // Make output only 1 for a signly cycle even if internal pipeline is stopped logic fault_detected_d[REP], fault_detected_q[REP]; for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state - assign fault_detected_d[r] = ~|full_same[1:0]; + assign fault_detected_d[r] = ~|full_same[2:0] & valid_i; end // Default state diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv index 66082e28..366733fa 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_time_dmr.sv @@ -249,7 +249,7 @@ module tb_time_dmr #( enable = 1; repeat (TESTS) inject_fault(); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + $display("Ending Test with ecc enabled and ID faults, got %d errors.", error_cnt); reset_metrics(); // Measure throughput diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index baf51658..28b55688 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -468,7 +468,7 @@ module tb_time_dmr_retry_lock #( enable = 1; repeat (TESTS) inject_fault(); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + $display("Ending Test with ecc enabled and ID faults, got %d errors.", error_cnt); reset_metrics(); // Measure throughput diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index df223824..ef08a1e2 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -12,7 +12,7 @@ module tb_time_tmr #( parameter time AQUISITION_DELAY = 8ns ) ( /* no ports on TB */ ); - + `include "tb_time.svh" //////////////////////////////////////////////////////////////////////////////////7 @@ -146,7 +146,7 @@ module tb_time_tmr #( assign ready_redundant_faulty = ready_redundant ^ ready_fault; assign id_redundant_faulty = id_redundant ^ id_fault; - initial data_fault = '0; + initial data_fault = '0; initial valid_fault = '0; initial ready_fault = '0; initial id_fault = '0; @@ -155,13 +155,13 @@ module tb_time_tmr #( // Send correct data for some cycles to space errors repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; end - + // Send wrong data fault_current = fault_type; case (fault_type) @@ -169,12 +169,12 @@ module tb_time_tmr #( VALID_FAULT: valid_fault = 1; READY_FAULT: ready_fault = 1; ID_FAULT: id_fault = $random; - endcase + endcase // Send correct data again @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; @@ -228,7 +228,7 @@ module tb_time_tmr #( enable = 1; repeat (TESTS) inject_fault(); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + $display("Ending Test with ecc enabled and ID faults, got %d errors.", error_cnt); reset_metrics(); // Measure throughput diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index 9f659a30..f958e087 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -16,7 +16,7 @@ module tb_time_tmr_lock #( parameter time APPLICATION_DELAY = 2ns, parameter time AQUISITION_DELAY = 8ns ) ( /* no ports on TB */ ); - + `include "tb_time.svh" //////////////////////////////////////////////////////////////////////////////////7 @@ -57,7 +57,7 @@ module tb_time_tmr_lock #( tmr_stacked_t in_tmr_stack_redundant; logic in_valid_redundant, in_ready_redundant; id_t in_id_redundant; - + time_TMR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), @@ -151,7 +151,7 @@ module tb_time_tmr_lock #( // Upstream connection .req_i(out_opgrp_valid), .gnt_o(out_opgrp_ready), - .data_i(out_opgrp_rr_stack), + .data_i(out_opgrp_rr_stack), // Downstream connection .gnt_i(out_tmr_ready), @@ -267,7 +267,7 @@ module tb_time_tmr_lock #( // Signals to show what faults are going on enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; - initial data_fault = '0; + initial data_fault = '0; initial valid_fault = '0; initial ready_fault = '0; initial id_fault = '0; @@ -276,13 +276,13 @@ module tb_time_tmr_lock #( // Send correct data for some cycles to space errors repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; end - + // Send wrong data fault_current = fault_type; case (fault_type) @@ -290,12 +290,12 @@ module tb_time_tmr_lock #( VALID_FAULT: valid_fault = 1; READY_FAULT: ready_fault = 1; ID_FAULT: id_fault = $random; - endcase + endcase // Send correct data again @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; @@ -349,7 +349,7 @@ module tb_time_tmr_lock #( enable = 1; repeat (TESTS) inject_fault(); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + $display("Ending Test with ecc enabled and ID faults, got %d errors.", error_cnt); reset_metrics(); // Measure throughput From 1e01654359b7dc2aa51177ba790c96637a692c3f Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Mon, 29 Apr 2024 11:20:54 +0200 Subject: [PATCH 09/21] Added stall testing to TBs & fixed related bugs --- rtl/time_redundancy/time_DMR_end.sv | 2 +- rtl/time_redundancy/time_TMR_end.sv | 20 +- test/tb_time.svh | 36 +++- test/tb_time_dmr.sv | 33 +-- test/tb_time_dmr_retry.sv | 317 +++++++++++++++------------- test/tb_time_dmr_retry_lock.sv | 49 +++-- test/tb_time_tmr.sv | 19 +- test/tb_time_tmr_lock.sv | 21 +- 8 files changed, 282 insertions(+), 215 deletions(-) diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index ec7e55f5..7a464a1b 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -358,7 +358,7 @@ module time_DMR_end # ( logic fault_detected_d[REP], fault_detected_q[REP]; for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state - assign fault_detected_d[r] = ~|full_same[1:0]; + assign fault_detected_d[r] = ~|full_same[1:0] & valid_i;; end // Default state diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 7ec9b6ee..91dd1a7b 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -351,15 +351,17 @@ module time_TMR_end # ( case (state_q[r]) BASE: - if (new_id_arrived[r]) begin - if (ready_i) begin - if (id_all_cover[r]) begin - state_v[r] = WAIT_FOR_VALID_X2; + if (valid_i) begin + if (new_id_arrived[r]) begin + if (ready_i) begin + if (id_all_cover[r]) begin + state_v[r] = WAIT_FOR_VALID_X2; + end else begin + state_v[r] = WAIT_FOR_VALID; + end end else begin - state_v[r] = WAIT_FOR_VALID; + state_v[r] = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go end - end else begin - state_v[r] = WAIT_FOR_READY; // We keep the data until downstream is ready, only shifting as far as our registers go end end WAIT_FOR_READY: @@ -405,14 +407,14 @@ module time_TMR_end # ( always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) - BASE: valid_ov[r] = new_id_arrived[r] & data_usable[r]; + BASE: valid_ov[r] = valid_i & new_id_arrived[r] & data_usable[r]; WAIT_FOR_READY: valid_ov[r] = new_id_arrived[r] & data_usable[r]; WAIT_FOR_VALID: valid_ov[r] = 0; WAIT_FOR_VALID_X2: valid_ov[r] = 0; endcase case (state_q[r]) - BASE: lock_internal[r] = !ready_i | !new_id_arrived[r]; + BASE: lock_internal[r] = !(ready_i & valid_i) | !new_id_arrived[r] ; WAIT_FOR_READY: lock_internal[r] = !ready_i; WAIT_FOR_VALID: lock_internal[r] = 1; WAIT_FOR_VALID_X2: lock_internal[r] = 1; diff --git a/test/tb_time.svh b/test/tb_time.svh index 8b2c3c0d..2043343f 100644 --- a/test/tb_time.svh +++ b/test/tb_time.svh @@ -6,6 +6,7 @@ logic clk, rst_n; logic valid_in, ready_in; logic valid_out, ready_out; logic enable; +logic stall; //////////////////////////////////////////////////////////////////////////////////7 // Initial State @@ -54,14 +55,14 @@ endtask task input_handshake_end(); // Perform Handshake - valid_in = 1'b1; + valid_in = 1'b1; # (AQUISITION_DELAY - APPLICATION_DELAY); while (!ready_in) begin @(posedge clk); # AQUISITION_DELAY; end - @(posedge clk); + @(posedge clk); # APPLICATION_DELAY; valid_in = 1'b0; // This might get overwritten by next handshake @@ -71,13 +72,13 @@ endtask task output_handshake_start(); // Wait random time (with no valid data) repeat ($urandom_range(0, out_hs_max_starvation)) begin - @(posedge clk); + @(posedge clk); # APPLICATION_DELAY; end // Wait for reset to pass while (!rst_n) begin - @(posedge clk); + @(posedge clk); # APPLICATION_DELAY; end @@ -85,8 +86,8 @@ task output_handshake_start(); // Wait for correct amount of time in cycle # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!valid_out) begin - @(posedge clk); + while (!valid_out | !rst_n) begin + @(posedge clk); #AQUISITION_DELAY; end endtask @@ -97,4 +98,25 @@ task output_handshake_end(); ready_out = 1'b0; // This might get overwritten by next handshake out_hs_count = out_hs_count + 1; -endtask \ No newline at end of file +endtask + +//////////////////////////////////////////////////////////////////////////////////7 +// Internal Stall +//////////////////////////////////////////////////////////////////////////////////7 + +longint unsigned internal_hs_max_starvation = 12; + +initial begin + forever begin + // Wait random time (with pipeline stalled) + repeat ($urandom_range(0, internal_hs_max_starvation)) begin + @(posedge clk); + #APPLICATION_DELAY; + stall = 0; + end + + @(posedge clk) ; + #APPLICATION_DELAY; + stall = 1; + end +end diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv index 366733fa..b1027ff4 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_time_dmr.sv @@ -1,7 +1,7 @@ module tb_time_dmr #( // DUT Parameters parameter IDSize = 4, - parameter int LockTimeout = 4, + parameter int LockTimeout = 4 * 12, parameter bit InternalRedundancy = 0, // TB Parameters @@ -129,7 +129,7 @@ module tb_time_dmr #( $error("[T=%t] More faults detected than injected!", $time); error = 1; error_cnt += 1; - end + end end else begin if (data_actual != data_golden) begin $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); @@ -154,18 +154,18 @@ module tb_time_dmr #( // Fault Injection //////////////////////////////////////////////////////////////////////////////////7 - longint unsigned min_fault_delay = 15; - longint unsigned max_fault_delay = 20; + longint unsigned min_fault_delay = 12 * 4; + longint unsigned max_fault_delay = 12 * 4 + 20; // Signals to show what faults are going on enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; assign data_redundant_faulty = data_redundant ^ data_fault; - assign valid_redundant_faulty = valid_redundant ^ valid_fault; - assign ready_redundant_faulty = ready_redundant ^ ready_fault; + assign valid_redundant_faulty = (valid_redundant & stall) ^ valid_fault; + assign ready_redundant_faulty = (ready_redundant & stall) ^ ready_fault; assign id_redundant_faulty = id_redundant ^ id_fault; - initial data_fault = '0; + initial data_fault = '0; initial valid_fault = '0; initial ready_fault = '0; initial id_fault = '0; @@ -174,13 +174,13 @@ module tb_time_dmr #( // Send correct data for some cycles to space errors repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; end - + // Send wrong data fault_current = fault_type; case (fault_type) @@ -194,8 +194,8 @@ module tb_time_dmr #( // Send correct data again @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; @@ -257,15 +257,22 @@ module tb_time_dmr #( enable = 0; in_hs_max_starvation = 0; out_hs_max_starvation = 0; + internal_hs_max_starvation = 0; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + if (TESTS - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc disabled!"); + end reset_metrics(); enable = 1; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS /2, error_cnt); + if (TESTS /2 - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc enabled!"); + end reset_metrics(); $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); $finish(error_cnt); diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv index 27ee9221..7c6b4fb6 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_time_dmr_retry.sv @@ -1,7 +1,7 @@ module tb_time_dmr_retry #( // DUT Parameters parameter int IDSize = 4, - parameter int LockTimeout = 4, + parameter int LockTimeout = 4 * 12, parameter bit InternalRedundancy = 0, // TB Parameters @@ -9,11 +9,16 @@ module tb_time_dmr_retry #( parameter time CLK_PERIOD = 10ns, parameter time APPLICATION_DELAY = 2ns, parameter time AQUISITION_DELAY = 8ns + ) ( /* no ports on TB */ ); - localparam unsigned RST_CLK_CYCLES = 10; + `include "tb_time.svh" + + //////////////////////////////////////////////////////////////////////////////////7 + // DUT (s) + //////////////////////////////////////////////////////////////////////////////////7 - // Parameters + // Signal Types typedef logic [7:0] data_t; typedef logic [7:0] tag_t; @@ -22,25 +27,11 @@ module tb_time_dmr_retry #( tag_t tag; } tagged_data_t; - // Testbench signals - tagged_data_t golden_queue [$]; - tagged_data_t data_golden, data_actual; - logic error; - int error_cnt; - - // Aux signals to show what faults are going on - enum {NONE, DATA_ERROR, VALID_ERROR, READY_ERROR, ID_ERROR} fault_type, fault_current; - - // Signals for DUTS - logic clk; - logic rst_n; - logic enable; - // Downstream Connection - tagged_data_t data_in, data_detectable, data_redundant, data_error, data_redundant_faulty, data_detected, data_out; - logic valid_in, valid_detectable, valid_redundant, valid_error, valid_redundant_faulty, valid_detected, valid_out; - logic ready_in, ready_detectable, ready_redundant, ready_error, ready_redundant_faulty, ready_detected, ready_out; - logic [IDSize-1:0] id_detectable, id_redundant, id_error, id_redundant_faulty, id_detected, next_id; + tagged_data_t data_in, data_detectable, data_redundant, data_fault, data_redundant_faulty, data_detected, data_out; + logic valid_detectable, valid_redundant, valid_fault, valid_redundant_faulty, valid_detected; + logic ready_detectable, ready_redundant, ready_fault, ready_redundant_faulty, ready_detected; + logic [IDSize-1:0] id_detectable, id_redundant, id_fault, id_redundant_faulty, id_detected, next_id; logic needs_retry_detected; // Feedback connection @@ -48,16 +39,6 @@ module tb_time_dmr_retry #( logic valid_retry; logic ready_retry; - // Clock Generation - initial begin - clk = '1; - rst_n = '0; - repeat (10) @(posedge clk); - rst_n = 1; - end - - always #((CLK_PERIOD/2)) clk = ~clk; - // DUT Instances retry_start #( .DataType(tagged_data_t), @@ -110,12 +91,6 @@ module tb_time_dmr_retry #( .ready_i(ready_redundant_faulty) ); - // Error XORs - assign data_redundant_faulty = data_redundant ^ data_error; - assign valid_redundant_faulty = valid_redundant ^ valid_error; - assign ready_redundant_faulty = ready_redundant ^ ready_error; - assign id_redundant_faulty = id_redundant ^ id_error; - // DUT Instances time_DMR_end #( .DataType(tagged_data_t), @@ -151,8 +126,8 @@ module tb_time_dmr_retry #( .DataType(tagged_data_t), .IDSize(IDSize) ) dut_retry_end ( - .clk_i(clk), - .rst_ni(rst_n), + .clk_i(clk), + .rst_ni(rst_n), // Upstream connection .data_i(data_detected), @@ -172,136 +147,48 @@ module tb_time_dmr_retry #( .retry_ready_i(ready_retry) ); - // Data Application + //////////////////////////////////////////////////////////////////////////////////7 + // Data Input + //////////////////////////////////////////////////////////////////////////////////7 + tagged_data_t golden_queue[$]; + initial begin tag_t tag_new; tagged_data_t data_new; tag_new = 0; - - // Initialize Handshake and Data - data_in = 8'h00; - valid_in = 1'b0; - - // Wait for reset to be lifted - @(posedge rst_n); forever begin - // Wait random time (with no valid data) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - valid_in <= '0; - end - - valid_in <= '1; - - // Assign unique tag so we can + input_handshake_begin(); tag_new += 1; data_new.data = $random; data_new.tag = tag_new; - data_in = data_new; - golden_queue.push_back(data_in); - - // Wait for handshake and as soon as it happens invalidate data - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!ready_in) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - + golden_queue.push_back(data_new); + input_handshake_end(); end end - // Enable / Disable ECC - initial begin - enable = 1'b0; - $display("Disabled Redundancy"); - repeat (TESTS * 5) begin - @(posedge clk); - end - $display("Enabled Redundancy"); - enable = 1'b1; - end - - // Fault inject - initial begin - for (logic [2:0] ft = 0; ft < 5; ft++) begin - fault_type[2:0] = ft; - $display("Starting Test with fault type {%s}", fault_type.name()); - - repeat (TESTS) begin - - // Send correct data for some cycles to space errors - repeat ($urandom_range(15, 20)) begin - @(posedge clk); - # (APPLICATION_DELAY); - fault_current = NONE; - data_error = '0; - valid_error = '0; - ready_error = '0; - id_error = '0; - end - - // Send wrong data - @(posedge clk); - # (APPLICATION_DELAY); - fault_current <= fault_type; - data_error <= '0; - valid_error <= '0; - ready_error <= '0; - id_error <= '0; - case (fault_type) - DATA_ERROR: data_error <= $random; - VALID_ERROR: valid_error <= 1; - READY_ERROR: ready_error <= 1; - ID_ERROR: id_error <= (1 << $urandom_range(0,IDSize-1)); - endcase - end - $display("Ending Test with fault type {%s}", fault_type.name()); - end - $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, error_cnt); - $finish(0); - end + //////////////////////////////////////////////////////////////////////////////////7 + // Data Output + //////////////////////////////////////////////////////////////////////////////////7 + tagged_data_t data_golden, data_actual; + logic error, found; // Helper signal so one can quickly scroll to errors in questa + longint unsigned error_cnt = 0; + // Progress reporting + task reset_metrics(); + reset(); + error_cnt = 0; + in_hs_count = 0; + out_hs_count = 0; + golden_queue.delete(); + endtask - // Aquisition & Validation initial begin - logic found; - $timeformat(-9, 0, " ns", 20); - - // Initialize error metrics - error = 0; // Signal so errors can easily be scrolled to in wave - error_cnt = 0; - found = 0; - - // Initialize Handshake - ready_out = '0; - - // Wait for reset to be lifted - @(posedge rst_n); - forever begin - // Wait random time (while not ready) - repeat ($urandom_range(1, 5)) begin - @(posedge clk); - # APPLICATION_DELAY; - ready_out <= '0; - end - - // Set ready - ready_out <= '1; - - // Wait for handshake - # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!valid_out) begin - @(posedge clk); - # AQUISITION_DELAY; - end; - - // Once it happened check if output was good and reset ready again + output_handshake_start(); data_actual = data_out; if (golden_queue.size() > 0) begin @@ -315,6 +202,7 @@ module tb_time_dmr_retry #( $error("[T=%t] Mismatch: Golden: %h, Actual: %h", $time, data_golden, data_actual); error = 1; error_cnt += 1; + break; end else begin error = 0; found = 1; @@ -332,13 +220,140 @@ module tb_time_dmr_retry #( end else if (golden_queue.size() > 2 ** IDSize) begin $display("[T=%t] Data does not get output in a timely manner!", $time); error = 1; - error_cnt += 1; + error_cnt += 1; end else begin $display("[T=%t] Tag %h Data %h Output when nothing was in golden queue", $time, data_actual.tag, data_actual.data); error = 1; error_cnt += 1; end + output_handshake_end(); + end + end + + //////////////////////////////////////////////////////////////////////////////////7 + // Fault Injection + //////////////////////////////////////////////////////////////////////////////////7 + + longint unsigned min_fault_delay = 12 * 4; + longint unsigned max_fault_delay = 12 * 4 + 20; + + // Signals to show what faults are going on + enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; + + assign data_redundant_faulty = data_redundant ^ data_fault; + assign valid_redundant_faulty = (valid_redundant & stall) ^ valid_fault; + assign ready_redundant_faulty = (ready_redundant & stall) ^ ready_fault; + assign id_redundant_faulty = id_redundant ^ id_fault; + + initial data_fault = '0; + initial valid_fault = '0; + initial ready_fault = '0; + initial id_fault = '0; + + task inject_fault(); + // Send correct data for some cycles to space errors + repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + end + + // Send wrong data + fault_current = fault_type; + case (fault_type) + DATA_FAULT: data_fault <= $random; + VALID_FAULT: valid_fault <= 1; + READY_FAULT: ready_fault <= 1; + ID_FAULT: id_fault <= (1 << $urandom_range(0,IDSize-1)); + endcase + + // Send correct data again + @(posedge clk); + fault_current = NONE; + data_fault = '0; + valid_fault = '0; + ready_fault = '0; + id_fault = '0; + endtask + + //////////////////////////////////////////////////////////////////////////////////7 + // Main Loop + //////////////////////////////////////////////////////////////////////////////////7 + longint unsigned total_fault_cnt = 0; + + initial begin + reset_metrics(); + + // Check normal operation + fault_type = NONE; + enable = 0; + repeat (10 * TESTS) @(posedge clk); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc disabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled and no faults, got %d errors.", error_cnt); + reset_metrics(); + + // Check fault tolerance + fault_type = DATA_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled and data faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = VALID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled and valid fault, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = READY_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled and ready faults, got %d errors.", error_cnt); + reset_metrics(); + + fault_type = ID_FAULT; + enable = 1; + repeat (TESTS) inject_fault(); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled and ID faults, got %d errors.", error_cnt); + reset_metrics(); + + // Measure throughput + fault_type = NONE; + enable = 0; + in_hs_max_starvation = 0; + out_hs_max_starvation = 0; + internal_hs_max_starvation = 0; + repeat (TESTS) @(posedge clk); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + if (TESTS - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc disabled!"); + end + reset_metrics(); + + enable = 1; + repeat (TESTS) @(posedge clk); + total_fault_cnt += error_cnt; + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS/2, error_cnt); + if (TESTS/2 - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc enabled!"); end + reset_metrics(); + $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_fault_cnt); + $finish(error_cnt); end endmodule diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 28b55688..49a0f77a 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -2,10 +2,10 @@ module tb_time_dmr_retry_lock #( // DUT Parameters - parameter int LockTimeout = 5, + parameter int LockTimeout = 5 * 12, parameter int NumOpgroups = 3, parameter int OpgroupWidth = $clog2(NumOpgroups), - parameter int IDSize = 5, + parameter int IDSize = 9, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, parameter bit InternalRedundancy = 0, @@ -15,7 +15,7 @@ module tb_time_dmr_retry_lock #( parameter time APPLICATION_DELAY = 2ns, parameter time AQUISITION_DELAY = 8ns ) ( /* no ports on TB */ ); - + `include "tb_time.svh" //////////////////////////////////////////////////////////////////////////////////7 @@ -58,7 +58,7 @@ module tb_time_dmr_retry_lock #( tmr_stacked_t in_tmr_stack_redundant; logic in_valid_redundant, in_ready_redundant; id_t in_id_redundant; - + // Feedback connection id_t id_retry, next_id; logic valid_retry; @@ -145,10 +145,10 @@ module tb_time_dmr_retry_lock #( // Upstream Connection // Error Injection - assign pipe_valid[0] = (in_valid_redundant ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_valid[0] = ((in_valid_redundant & stall) ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_fault; assign pipe_id[0] = in_id_redundant ^ id_fault; - assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_fault; + assign in_opgrp_ready[opgrp] = (pipe_ready[0] & stall) ^ ready_fault; // Generate the register stages for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline @@ -198,7 +198,7 @@ module tb_time_dmr_retry_lock #( // Upstream connection .req_i(out_opgrp_valid), .gnt_o(out_opgrp_ready), - .data_i(out_opgrp_rr_stack), + .data_i(out_opgrp_rr_stack), // Downstream connection .gnt_i(out_tmr_ready), @@ -356,7 +356,7 @@ module tb_time_dmr_retry_lock #( $display("[T=%t] Tag %h Data %h Output but was not in golden queue ", $time, data_actual.tag, data_actual.data); error = 1; error_cnt += 1; - end + end end else begin $display("[T=%t] Operation %h -> Data %h Output when nothing was in golden queue", $time, operation_actual, data_actual); error = 1; @@ -367,10 +367,10 @@ module tb_time_dmr_retry_lock #( // Check that no queue runs out of bounds for (int operation = 0; operation < NumOpgroups; operation++) begin if (golden_queue[operation].size() > 2 ** IDSize) begin - $display("[T=%t] Data does not get output in a timely manner for operation %d!", $time, operation); + $display("[T=%t] Data does not get output in a timely manner for operation %d! (Q %d)", $time, operation, golden_queue[operation].size()); error = 1; - error_cnt += 1; - end + error_cnt += 1; + end end output_handshake_end(); end @@ -380,13 +380,13 @@ module tb_time_dmr_retry_lock #( // Fault Injection //////////////////////////////////////////////////////////////////////////////////7 - longint unsigned min_fault_delay = 45; - longint unsigned max_fault_delay = 55; + longint unsigned min_fault_delay = (12 + 5) * 5; + longint unsigned max_fault_delay = (12 + 5) * 5 + 20; // Signals to show what faults are going on enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; - initial data_fault = '0; + initial data_fault = '0; initial valid_fault = '0; initial ready_fault = '0; initial id_fault = '0; @@ -395,13 +395,13 @@ module tb_time_dmr_retry_lock #( // Send correct data for some cycles to space errors repeat ($urandom_range(min_fault_delay, max_fault_delay)) begin @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; end - + // Send wrong data fault_current = fault_type; case (fault_type) @@ -409,12 +409,12 @@ module tb_time_dmr_retry_lock #( VALID_FAULT: valid_fault = 1; READY_FAULT: ready_fault = 1; ID_FAULT: id_fault = (1 << $urandom_range(0,IDSize-1)); - endcase + endcase // Send correct data again @(posedge clk); - fault_current = NONE; - data_fault = '0; + fault_current = NONE; + data_fault = '0; valid_fault = '0; ready_fault = '0; id_fault = '0; @@ -476,15 +476,22 @@ module tb_time_dmr_retry_lock #( enable = 0; in_hs_max_starvation = 0; out_hs_max_starvation = 0; + internal_hs_max_starvation = 0; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + if (TESTS - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc disabled!"); + end reset_metrics(); enable = 1; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS/2, error_cnt); + if (TESTS/2 - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc enabled!"); + end reset_metrics(); $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); $finish(error_cnt); diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index ef08a1e2..c7c3d390 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -1,7 +1,7 @@ module tb_time_tmr #( // DUT Parameters parameter IDSize = 4, - parameter int LockTimeout = 4, + parameter int LockTimeout = 4 * 12, parameter bit EarlyValidEnable = 0, parameter bit InternalRedundancy = 0, @@ -135,15 +135,15 @@ module tb_time_tmr #( // Fault Injection //////////////////////////////////////////////////////////////////////////////////7 - longint unsigned min_fault_delay = 15; - longint unsigned max_fault_delay = 20; + longint unsigned min_fault_delay = 12 * 3; + longint unsigned max_fault_delay = 12 * 3 + 20; // Signals to show what faults are going on enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; assign data_redundant_faulty = data_redundant ^ data_fault; - assign valid_redundant_faulty = valid_redundant ^ valid_fault; - assign ready_redundant_faulty = ready_redundant ^ ready_fault; + assign valid_redundant_faulty = (valid_redundant & stall) ^ valid_fault; + assign ready_redundant_faulty = (ready_redundant & stall) ^ ready_fault; assign id_redundant_faulty = id_redundant ^ id_fault; initial data_fault = '0; @@ -236,15 +236,22 @@ module tb_time_tmr #( enable = 0; in_hs_max_starvation = 0; out_hs_max_starvation = 0; + internal_hs_max_starvation = 0; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + if (TESTS - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc disabled!"); + end reset_metrics(); enable = 1; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS/3, error_cnt); + if (TESTS / 3 - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc enabled!"); + end reset_metrics(); $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); $finish(error_cnt); diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index f958e087..e21ff5ff 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -2,10 +2,10 @@ module tb_time_tmr_lock #( // DUT Parameters - parameter int LockTimeout = 5, + parameter int LockTimeout = 5 * 12, parameter int NumOpgroups = 3, parameter int OpgroupWidth = $clog2(NumOpgroups), - parameter int IDSize = 5, + parameter int IDSize = 5 + $clog2(12), parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, parameter bit EarlyValidEnable = 0, parameter bit InternalRedundancy = 0, @@ -98,10 +98,10 @@ module tb_time_tmr_lock #( // Upstream Connection // Error Injection - assign pipe_valid[0] = (in_valid_redundant ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); + assign pipe_valid[0] = ((in_valid_redundant & stall) ^ valid_fault) && (opgrp == in_tmr_stack_redundant.operation); assign pipe_data[0] = in_tmr_stack_redundant.data ^ data_fault; assign pipe_id[0] = in_id_redundant ^ id_fault; - assign in_opgrp_ready[opgrp] = pipe_ready[0] ^ ready_fault; + assign in_opgrp_ready[opgrp] = (pipe_ready[0] & stall) ^ ready_fault; // Generate the register stages for (genvar i = 0; i < NUM_REGS; i++) begin : gen_pipeline @@ -261,8 +261,8 @@ module tb_time_tmr_lock #( // Fault Injection //////////////////////////////////////////////////////////////////////////////////7 - longint unsigned min_fault_delay = 45; - longint unsigned max_fault_delay = 55; + longint unsigned min_fault_delay = (12 + 5) * 5; + longint unsigned max_fault_delay = (12 + 5) * 5 + 20; // Signals to show what faults are going on enum {NONE, DATA_FAULT, VALID_FAULT, READY_FAULT, ID_FAULT} fault_type, fault_current; @@ -357,15 +357,22 @@ module tb_time_tmr_lock #( enable = 0; in_hs_max_starvation = 0; out_hs_max_starvation = 0; + internal_hs_max_starvation = 0; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; $display("Ending Test with ecc disabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + if (TESTS - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc disabled!"); + end reset_metrics(); enable = 1; repeat (TESTS) @(posedge clk); total_error_cnt += error_cnt; - $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS, error_cnt); + $display("Ending Test with ecc enabled got a max throughtput of %d/%d and %d errors.", out_hs_count, TESTS/3, error_cnt); + if (TESTS / 3 - out_hs_count > TESTS / 20) begin + $error("Stall detected with ecc enabled!"); + end reset_metrics(); $display("Checked %0d tests of each type, found %0d mismatches.", TESTS, total_error_cnt); $finish(error_cnt); From b77b1c3486df46a10d3d221caa7ff3ed904a4035 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Mon, 13 May 2024 13:19:51 +0200 Subject: [PATCH 10/21] Converted retry connection to interface. --- Bender.yml | 5 ++-- rtl/time_redundancy/retry_end.sv | 17 ++++++-------- rtl/time_redundancy/retry_inorder_end.sv | 24 +++++++------------ rtl/time_redundancy/retry_inorder_start.sv | 26 ++++++++------------- rtl/time_redundancy/retry_interface.sv | 27 ++++++++++++++++++++++ rtl/time_redundancy/retry_start.sv | 18 ++++++--------- src_files.yml | 5 ++-- test/tb_retry.sv | 16 ++++++------- test/tb_retry_inorder.sv | 20 +++++----------- test/tb_time_dmr_retry.sv | 12 +++------- test/tb_time_dmr_retry_lock.sv | 15 ++++-------- 11 files changed, 85 insertions(+), 100 deletions(-) create mode 100644 rtl/time_redundancy/retry_interface.sv diff --git a/Bender.yml b/Bender.yml index f10f89b6..dd43df34 100644 --- a/Bender.yml +++ b/Bender.yml @@ -25,8 +25,7 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_enc.sv - rtl/hsiao_ecc/hsiao_ecc_dec.sv - rtl/hsiao_ecc/hsiao_ecc_cor.sv - - rtl/time_redundancy/retry_end.sv - - rtl/time_redundancy/retry_inorder_end.sv + - rtl/time_redundancy/retry_interface.sv - rtl/time_redundancy/rr_arb_tree_lock.sv - rtl/time_redundancy/voters.svh - rtl/TMR_voter.sv @@ -38,7 +37,9 @@ sources: - rtl/ecc_wrap/ecc_scrubber.sv - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv - rtl/time_redundancy/redundancy_controller.sv + - rtl/time_redundancy/retry_end.sv - rtl/time_redundancy/retry_inorder_start.sv + - rtl/time_redundancy/retry_inorder_end.sv - rtl/time_redundancy/retry_start.sv - rtl/time_redundancy/time_TMR_end.sv - rtl/time_redundancy/time_TMR_start.sv diff --git a/rtl/time_redundancy/retry_end.sv b/rtl/time_redundancy/retry_end.sv index 428c0571..0bcaac89 100644 --- a/rtl/time_redundancy/retry_end.sv +++ b/rtl/time_redundancy/retry_end.sv @@ -6,9 +6,8 @@ // In order to propperly function: // - id_o of retry_start needs to be passed paralelly along the combinatorial logic, // using the same handshake and arrive at id_i of retry_end -// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end -// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end -// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - interface retry of retry_start needs to be directly connected to retry of retry_end + // - All elements in processing have a unique ID // // This modules might cause operations to reach the output of retry_end in a different @@ -38,24 +37,22 @@ module retry_end # ( input logic ready_i, // Retry Connection - output logic [IDSize-1:0] retry_id_o, - output logic retry_valid_o, - input logic retry_ready_i + retry_interface.ende retry ); // Assign signals - assign retry_id_o = id_i; + assign retry.id = id_i; assign data_o = data_i; always_comb begin: gen_output if (needs_retry_i) begin - retry_valid_o = valid_i; - ready_o = retry_ready_i; + retry.valid = valid_i; + ready_o = retry.ready; valid_o = 0; end else begin valid_o = valid_i; ready_o = ready_i; - retry_valid_o = 0; + retry.valid = 0; end end diff --git a/rtl/time_redundancy/retry_inorder_end.sv b/rtl/time_redundancy/retry_inorder_end.sv index b286a012..72f432ff 100644 --- a/rtl/time_redundancy/retry_inorder_end.sv +++ b/rtl/time_redundancy/retry_inorder_end.sv @@ -6,11 +6,7 @@ // In order to propperly function: // - id_o of retry_start needs to be passed paralelly along the combinatorial logic, // using the same handshake and arrive at id_i of retry_end -// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end -// - retry_id_o of retry_start needs to be directly connected to retry_id_i of retry_end -// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end -// - retry_lock_i of retry_start needs to be directly connected to retry_lock_o of retry_end -// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - interface retry of retry_start needs to be directly connected to retry of retry_end // - All elements in processing have a unique ID // // This module always keeps results in order by also retrying results that have been correct @@ -38,15 +34,11 @@ module retry_inorder_end # ( input logic ready_i, // Retry Connection - output logic [IDSize-1:0] retry_id_o, - input logic [IDSize-1:0] retry_id_i, - output logic retry_valid_o, - output logic retry_lock_o, - input logic retry_ready_i + retry_interface.ende retry ); // Signals do not change, only validity changes - assign retry_id_o = id_i; + assign retry.id = id_i; assign data_o = data_i; logic [IDSize-1:0] failed_id_d, failed_id_q; @@ -57,7 +49,7 @@ module retry_inorder_end # ( failed_id_d = failed_id_q; retry_d = ~(failed_id_q == id_i); end else if (valid_i & needs_retry_i) begin - failed_id_d = retry_id_i; + failed_id_d = retry.id_feedback; retry_d = 1; end else begin failed_id_d = failed_id_q; @@ -65,20 +57,20 @@ module retry_inorder_end # ( end end - assign retry_lock_o = retry_d; + assign retry.lock = retry_d; `FF(retry_q, retry_d, '0); `FF(failed_id_q, failed_id_d, '0); always_comb begin: gen_output if (retry_d) begin - retry_valid_o = valid_i; - ready_o = retry_ready_i; + retry.valid = valid_i; + ready_o = retry.ready; valid_o = 0; end else begin valid_o = valid_i; ready_o = ready_i; - retry_valid_o = 0; + retry.valid = 0; end end diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index c2656a95..f461364b 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -6,11 +6,7 @@ // In order to propperly function: // - id_o of retry_start needs to be passed paralelly along the combinatorial logic, // using the same handshake and arrive at id_i of retry_end -// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end -// - retry_id_o of retry_start needs to be directly connected to retry_id_i of retry_end -// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end -// - retry_lock_i of retry_start needs to be directly connected to retry_lock_o of retry_end -// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - interface retry of retry_start needs to be directly connected to retry of retry_end // - All elements in processing have a unique ID // // This module always keeps results in order by also retrying results that have been correct @@ -38,11 +34,7 @@ module retry_inorder_start # ( input logic ready_i, // Retry Connection - input logic [IDSize-1:0] retry_id_i, - output logic [IDSize-1:0] retry_id_o, - input logic retry_valid_i, - input logic retry_lock_i, - output logic retry_ready_o + retry_interface.start retry ); ////////////////////////////////////////////////////////////////////// @@ -52,25 +44,25 @@ module retry_inorder_start # ( logic retry_lock_d, retry_lock_q; always_comb begin: gen_next_cycle_decision - if (ready_i | retry_valid_i) begin - failed_valid_d = retry_valid_i; + if (ready_i | retry.valid) begin + failed_valid_d = retry.valid; end else begin failed_valid_d = failed_valid_q; end - if (retry_valid_i & retry_ready_o) begin - failed_id_d = retry_id_i; + if (retry.valid & retry.ready) begin + failed_id_d = retry.id; end else begin failed_id_d = failed_id_q; end end - assign retry_lock_d = retry_lock_i; + assign retry_lock_d = retry.lock; `FF(failed_id_q, failed_id_d, '0); `FF(failed_valid_q, failed_valid_d, '0); `FF(retry_lock_q, retry_lock_d, '0); - assign retry_ready_o = ready_i | ~failed_valid_q; + assign retry.ready = ready_i | ~failed_valid_q; ////////////////////////////////////////////////////////////////////// // ID Counter with parity bit @@ -110,7 +102,7 @@ module retry_inorder_start # ( data_o = data_i; end id_o = counter_id_q; - retry_id_o = counter_id_d; + retry.id_feedback = counter_id_d; end ////////////////////////////////////////////////////////////////////// diff --git a/rtl/time_redundancy/retry_interface.sv b/rtl/time_redundancy/retry_interface.sv new file mode 100644 index 00000000..43543e0f --- /dev/null +++ b/rtl/time_redundancy/retry_interface.sv @@ -0,0 +1,27 @@ +// Author: Maurus Item , ETH Zurich +// Date: 25.04.2024 +// Description: Interface in between retry modules to transmit which elements need to be tried again + +interface retry_interface #(parameter IDSize = 0) ( /* No ports on this interface */ ); + logic [IDSize-1:0] id; + logic valid; + logic ready; + logic [IDSize-1:0] id_feedback; + logic lock; + + modport start ( + input id, + input valid, + output ready, + output id_feedback, + input lock + ); + + modport ende ( + output id, + output valid, + input ready, + input id_feedback, + output lock + ); +endinterface \ No newline at end of file diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index 36ee8cf5..d00dbb6d 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -6,9 +6,7 @@ // In order to propperly function: // - id_o of retry_start needs to be passed paralelly along the combinatorial logic, // using the same handshake and arrive at id_i of retry_end -// - retry_id_i of retry_start needs to be directly connected to retry_id_o of retry_end -// - retry_valid_i of retry_start needs to be directly connected to retry_valid_o of retry_end -// - retry_ready_o of retry_start needs to be directly connected to retry_ready_i of retry_end +// - interface retry of retry_start needs to be directly connected to retry of retry_end // - All elements in processing have a unique ID // // This modules might cause operations to reach the output of retry_end in a different @@ -40,9 +38,7 @@ module retry_start # ( input logic ready_i, // Retry Connection - input logic [IDSize-1:0] retry_id_i, - input logic retry_valid_i, - output logic retry_ready_o + retry_interface.start retry ); ////////////////////////////////////////////////////////////////////// @@ -51,14 +47,14 @@ module retry_start # ( logic failed_valid_d, failed_valid_q; always_comb begin: gen_next_cycle_decision - if (ready_i | retry_valid_i) begin - failed_valid_d = retry_valid_i; + if (ready_i | retry.valid) begin + failed_valid_d = retry.valid; end else begin failed_valid_d = failed_valid_q; end - if (retry_valid_i & retry_ready_o) begin - failed_id_d = retry_id_i; + if (retry.valid & retry.ready) begin + failed_id_d = retry.id; end else begin failed_id_d = failed_id_q; end @@ -67,7 +63,7 @@ module retry_start # ( `FF(failed_id_q, failed_id_d, '0); `FF(failed_valid_q, failed_valid_d, '0); - assign retry_ready_o = ready_i | ~failed_valid_q; + assign retry.ready = ready_i | ~failed_valid_q; ////////////////////////////////////////////////////////////////////// // ID Counter with parity bit diff --git a/src_files.yml b/src_files.yml index 6dffab0b..1dd0997b 100644 --- a/src_files.yml +++ b/src_files.yml @@ -9,8 +9,7 @@ redundancy_cells: lowrisc_ecc/prim_secded_39_32_enc.sv, lowrisc_ecc/prim_secded_72_64_dec.sv, lowrisc_ecc/prim_secded_72_64_enc.sv, - rtl/time_redundancy/retry_end.sv - rtl/time_redundancy/retry_inorder_end.sv + rtl/time_redundancy/retry_interface.sv rtl/time_redundancy/rr_arb_tree_lock.sv rtl/time_redundancy/voters.svh rtl/TMR_voter.sv, @@ -33,7 +32,9 @@ redundancy_cells: rlt/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv, rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv, rtl/time_redundancy/redundancy_controller.sv + rtl/time_redundancy/retry_end.sv rtl/time_redundancy/retry_inorder_start.sv + rtl/time_redundancy/retry_inorder_end.sv rtl/time_redundancy/retry_start.sv rtl/time_redundancy/time_TMR_end.sv rtl/time_redundancy/time_TMR_start.sv diff --git a/test/tb_retry.sv b/test/tb_retry.sv index 55e017db..7a642c7d 100644 --- a/test/tb_retry.sv +++ b/test/tb_retry.sv @@ -22,11 +22,13 @@ module tb_retry; logic rst_n; data_t data_in, data_middle, data_out; - logic valid_in, valid_middle, valid_retry, valid_out; - logic ready_in, ready_middle, ready_retry, ready_out; - logic [IDSize-1:0] id_middle, id_retry; + logic valid_in, valid_middle, valid_out; + logic ready_in, ready_middle, ready_out; + logic [IDSize-1:0] id_middle; logic needs_retry_middle; + retry_interface #(.IDSize(IDSize)) retry_connection (); + // Clock Generation initial begin clk = '1; @@ -57,9 +59,7 @@ module tb_retry; .ready_i(ready_middle), // Retry Connection - .retry_id_i(id_retry), - .retry_valid_i(valid_retry), - .retry_ready_o(ready_retry) + .retry (retry_connection) ); @@ -83,9 +83,7 @@ module tb_retry; .ready_i(ready_out), // Retry Connection - .retry_id_o(id_retry), - .retry_valid_o(valid_retry), - .retry_ready_i(ready_retry) + .retry (retry_connection) ); // Data Application diff --git a/test/tb_retry_inorder.sv b/test/tb_retry_inorder.sv index 176f0ef6..ca93ffce 100644 --- a/test/tb_retry_inorder.sv +++ b/test/tb_retry_inorder.sv @@ -25,11 +25,11 @@ module tb_retry_inorder; logic rst_n; data_t data_in, data_out; - logic valid_in, valid_retry, valid_out; - logic ready_in, ready_retry, ready_out; - logic [IDSize-1:0] id_retry, id_retry_reverse; + logic valid_in, valid_out; + logic ready_in, ready_out; logic needs_retry_middle; - logic lock_retry; + + retry_interface #(.IDSize(IDSize)) retry_connection (); data_t [0:NUM_REGS] data_middle; logic [0:NUM_REGS] valid_middle; @@ -66,11 +66,7 @@ module tb_retry_inorder; .ready_i(ready_middle[0]), // Retry Connection - .retry_id_i(id_retry), - .retry_id_o(id_retry_reverse), - .retry_valid_i(valid_retry), - .retry_lock_i(lock_retry), - .retry_ready_o(ready_retry) + .retry(retry_connection) ); // Generate the register stages @@ -112,11 +108,7 @@ module tb_retry_inorder; .ready_i(ready_out), // Retry Connection - .retry_id_o(id_retry), - .retry_id_i(id_retry_reverse), - .retry_valid_o(valid_retry), - .retry_lock_o(lock_retry), - .retry_ready_i(ready_retry) + .retry(retry_connection) ); // Data Application diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv index 7c6b4fb6..bbd2ff05 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_time_dmr_retry.sv @@ -35,9 +35,7 @@ module tb_time_dmr_retry #( logic needs_retry_detected; // Feedback connection - logic [IDSize-1:0] id_retry; - logic valid_retry; - logic ready_retry; + retry_interface #(.IDSize(IDSize)) retry_connection (); // DUT Instances retry_start #( @@ -59,9 +57,7 @@ module tb_time_dmr_retry #( .ready_i(ready_detectable), // Retry Connection - .retry_id_i(id_retry), - .retry_valid_i(valid_retry), - .retry_ready_o(ready_retry) + .retry(retry_connection) ); @@ -142,9 +138,7 @@ module tb_time_dmr_retry #( .ready_i(ready_out), // Retry Connection - .retry_id_o(id_retry), - .retry_valid_o(valid_retry), - .retry_ready_i(ready_retry) + .retry(retry_connection) ); //////////////////////////////////////////////////////////////////////////////////7 diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 49a0f77a..19f2b677 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -60,10 +60,9 @@ module tb_time_dmr_retry_lock #( id_t in_id_redundant; // Feedback connection - id_t id_retry, next_id; - logic valid_retry; - logic ready_retry; - + id_t next_id; + retry_interface #(.IDSize(IDSize)) retry_connection (); + // Connection between retry and DMR tmr_stacked_t data_retry2dmr; id_t id_retry2dmr; @@ -95,9 +94,7 @@ module tb_time_dmr_retry_lock #( .ready_i(ready_retry2dmr), // Retry Connection - .retry_id_i(id_retry), - .retry_valid_i(valid_retry), - .retry_ready_o(ready_retry) + .retry(retry_connection) ); @@ -274,9 +271,7 @@ module tb_time_dmr_retry_lock #( .ready_i(ready_out), // Retry Connection - .retry_id_o(id_retry), - .retry_valid_o(valid_retry), - .retry_ready_i(ready_retry) + .retry(retry_connection) ); assign data_out = out_stacked.data; From 97fb9984f47723923a2fb54fde29587844e341e3 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 08:59:47 +0200 Subject: [PATCH 11/21] Added trickle feed bypass to redundancy controller to fix stall on last redundant operation error. --- rtl/time_redundancy/redundancy_controller.sv | 28 +++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv index 26b269bc..8ea05856 100644 --- a/rtl/time_redundancy/redundancy_controller.sv +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -14,6 +14,10 @@ `include "common_cells/registers.svh" module redundancy_controller # ( + // How long the redundancy controller should keep trying to + // Resolve unfinished transactions before switching modes + // Needs to be equal or bigger than timeout in underlying TMR / DMR modules + parameter int unsigned LockTimeout = 4, // Determines if the internal state machines should // be parallely redundant, meaning errors inside this module // can also not cause errors in the output @@ -47,40 +51,62 @@ module redundancy_controller # ( logic ready_ov[REP]; logic busy_ov[REP]; logic enable_ov[REP]; + logic flush_ov[REP]; logic enable_v[REP], enable_d[REP], enable_q[REP]; + logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; + + logic timout[REP]; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin + // As long as the unit is busy, do not change the enable state if (busy_i) begin enable_v[r] = enable_q[r]; end else begin enable_v[r] = enable_i; end + + // If the unit is stalled e.g. nothing gets out during for the timeout, then trickle new operations in to unstall it + if (counter_q[r] > LockTimeout) begin + counter_v[r] = 0; + timout[r] = 1; + end else if (valid_i && enable_q[r] && !enable_i) begin + counter_v[r] = counter_q[r] + 1; + timout[r] = 0; + end else begin + counter_v[r] = 0; + timout[r] = 0; + end end end // State Voting Logic if (InternalRedundancy) begin : gen_state_voters `VOTE3to3(enable_v, enable_d); + `VOTE3to3(counter_v, counter_d); end else begin assign enable_d = enable_v; + assign counter_d = counter_v; end // Generate default case logic enable_base[REP]; + logic [$clog2(LockTimeout)-1:0] counter_base[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state assign enable_base[r] = '0; + assign counter_base[r] = '0; end `FFARN(enable_q, enable_d, enable_base, clk_i, rst_ni); + `FFARN(counter_q, counter_d, counter_base, clk_i, rst_ni); // Output combinatorial logic for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin enable_ov[r] = enable_q[r]; - if (enable_q[r] == enable_i) begin + if ((enable_q[r] == enable_i) || timout[r]) begin valid_ov[r] = valid_i; ready_ov[r] = ready_i; busy_ov[r] = busy_i; From 62656d91ed037b5460c67efcf53db5d48cc95b52 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 21 May 2024 09:02:12 +0200 Subject: [PATCH 12/21] Renamed signals for easy don't touch in synthesis. - Made all redundant signals end on _v or _ov for don't touch in synthesis - Made all default signals end on _b --- rtl/time_redundancy/redundancy_controller.sv | 22 ++-- rtl/time_redundancy/time_DMR_end.sv | 68 ++++++------ rtl/time_redundancy/time_DMR_start.sv | 18 +-- rtl/time_redundancy/time_TMR_end.sv | 110 +++++++++---------- rtl/time_redundancy/time_TMR_start.sv | 18 +-- 5 files changed, 118 insertions(+), 118 deletions(-) diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv index 8ea05856..14671df6 100644 --- a/rtl/time_redundancy/redundancy_controller.sv +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -56,7 +56,7 @@ module redundancy_controller # ( logic enable_v[REP], enable_d[REP], enable_q[REP]; logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; - logic timout[REP]; + logic timout_v[REP]; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin @@ -70,13 +70,13 @@ module redundancy_controller # ( // If the unit is stalled e.g. nothing gets out during for the timeout, then trickle new operations in to unstall it if (counter_q[r] > LockTimeout) begin counter_v[r] = 0; - timout[r] = 1; + timout_v[r] = 1; end else if (valid_i && enable_q[r] && !enable_i) begin counter_v[r] = counter_q[r] + 1; - timout[r] = 0; + timout_v[r] = 0; end else begin counter_v[r] = 0; - timout[r] = 0; + timout_v[r] = 0; end end end @@ -91,22 +91,22 @@ module redundancy_controller # ( end // Generate default case - logic enable_base[REP]; - logic [$clog2(LockTimeout)-1:0] counter_base[REP]; + logic enable_b[REP]; + logic [$clog2(LockTimeout)-1:0] counter_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign enable_base[r] = '0; - assign counter_base[r] = '0; + assign enable_b[r] = '0; + assign counter_b[r] = '0; end - `FFARN(enable_q, enable_d, enable_base, clk_i, rst_ni); - `FFARN(counter_q, counter_d, counter_base, clk_i, rst_ni); + `FFARN(enable_q, enable_d, enable_b, clk_i, rst_ni); + `FFARN(counter_q, counter_d, counter_b, clk_i, rst_ni); // Output combinatorial logic for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin enable_ov[r] = enable_q[r]; - if ((enable_q[r] == enable_i) || timout[r]) begin + if ((enable_q[r] == enable_i) || timout_v[r]) begin valid_ov[r] = valid_i; ready_ov[r] = ready_i; busy_ov[r] = busy_i; diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index 7a464a1b..740ac969 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -148,17 +148,17 @@ module time_DMR_end # ( ///////////////////////////////////////////////////////////////////////////////// // Logic to find out what we should do with our data based on same / not same - logic new_element_arrived[REP]; - logic data_usable[REP]; + logic new_element_arrived_v[REP]; + logic data_usable_v[REP]; // Flag Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_data_flags always_comb begin: gen_data_flags_comb // Some new element just showed up and we need to send data outwards again. - new_element_arrived[r] = (id_same == 2'b01) || (id_same == 2'b00); + new_element_arrived_v[r] = (id_same == 2'b01) || (id_same == 2'b00); // Data has at least two new things that are the same - data_usable[r] = data_same[0]; + data_usable_v[r] = data_same[0]; end end @@ -167,7 +167,7 @@ module time_DMR_end # ( typedef enum logic [0:0] {BASE, WAIT_FOR_READY} state_t; state_t state_v[REP], state_d[REP], state_q[REP]; - logic valid_internal[REP], lock_internal[REP]; + logic valid_internal_v[REP], lock_internal_v[REP]; // Special State Description: // Wait for Ready: We got some data that is usable, but downstream can't use it yet @@ -185,7 +185,7 @@ module time_DMR_end # ( case (state_q[r]) BASE: if (valid_i) begin - if (new_element_arrived[r]) begin + if (new_element_arrived_v[r]) begin if (!ready_i) begin state_v[r] = WAIT_FOR_READY; end @@ -200,9 +200,9 @@ module time_DMR_end # ( end // Generate default cases - state_t state_base[REP]; + state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_base[r] = BASE; + assign state_b[r] = BASE; end // State Voting Logic @@ -213,29 +213,29 @@ module time_DMR_end # ( end // State Storage - `FF(state_q, state_d, state_base); + `FF(state_q, state_d, state_b); // Output Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) - BASE: valid_internal[r] = valid_i & new_element_arrived[r]; - WAIT_FOR_READY: valid_internal[r] = valid_i; + BASE: valid_internal_v[r] = valid_i & new_element_arrived_v[r]; + WAIT_FOR_READY: valid_internal_v[r] = valid_i; endcase case (state_q[r]) - BASE: lock_internal[r] = !ready_i | !full_same[0]; - WAIT_FOR_READY: lock_internal[r] = !ready_i; + BASE: lock_internal_v[r] = !ready_i | !full_same[0]; + WAIT_FOR_READY: lock_internal_v[r] = !ready_i; endcase case (state_q[r]) - BASE: ready_ov[r] = ready_i | !new_element_arrived[r]; + BASE: ready_ov[r] = ready_i | !new_element_arrived_v[r]; WAIT_FOR_READY: ready_ov[r] = ready_i; endcase end else begin - valid_internal[r] = valid_i; - lock_internal[r] = 0; + valid_internal_v[r] = valid_i; + lock_internal_v[r] = 0; ready_ov[r] = ready_i; end end @@ -263,7 +263,7 @@ module time_DMR_end # ( // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything if (valid_i | lock_q[r]) begin - lock_v[r] = lock_internal[r]; + lock_v[r] = lock_internal_v[r]; end else begin lock_v[r] = lock_q[r]; end @@ -283,16 +283,16 @@ module time_DMR_end # ( assign lock_o = lock_d[0]; // Default state - logic lock_base[REP]; - logic [$clog2(LockTimeout)-1:0] counter_base[REP]; + logic lock_b[REP]; + logic [$clog2(LockTimeout)-1:0] counter_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state - assign lock_base[r] = '0; - assign counter_base[r] = '0; + assign lock_b[r] = '0; + assign counter_b[r] = '0; end // State Storage - `FF(lock_q, lock_d, lock_base); - `FF(counter_q, counter_d, counter_base); + `FF(lock_q, lock_d, lock_b); + `FF(counter_q, counter_d, counter_b); /////////////////////////////////////////////////////////////////////////////////////////////////// // Output deduplication based on ID and ID Faults @@ -308,7 +308,7 @@ module time_DMR_end # ( recently_seen_v[r][next_id_i[IDSize-2:0]] = 0; - if (valid_internal[r] & ready_i & !id_fault_q) begin + if (valid_internal_v[r] & ready_i & !id_fault_q) begin recently_seen_v[r][id_q[IDSize-2:0]] = 1; end end @@ -322,25 +322,25 @@ module time_DMR_end # ( end // Default state - logic [2 ** (IDSize-1)-1:0] recently_seen_base[REP]; + logic [2 ** (IDSize-1)-1:0] recently_seen_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_deduplication_default_state - assign recently_seen_base[r] = ~'0; // All 1s! + assign recently_seen_b[r] = ~'0; // All 1s! end // State Storage - `FF(recently_seen_q, recently_seen_d, recently_seen_base); + `FF(recently_seen_q, recently_seen_d, recently_seen_b); for (genvar r = 0; r < REP; r++) begin: gen_deduplication_output always_comb begin: gen_deduplication_output_comb if (enable_i) begin - if (id_fault_q | recently_seen_q[r][id_q[IDSize-2:0]] & valid_internal[r]) begin + if (id_fault_q | recently_seen_q[r][id_q[IDSize-2:0]] & valid_internal_v[r]) begin valid_ov[r] = 0; end else begin - valid_ov[r] = valid_internal[r]; + valid_ov[r] = valid_internal_v[r]; end - needs_retry_ov[r] = id_same[0] & !data_usable[r]; + needs_retry_ov[r] = id_same[0] & !data_usable_v[r]; end else begin - valid_ov[r] = valid_internal[r]; + valid_ov[r] = valid_internal_v[r]; needs_retry_ov[r] = 0; end end @@ -362,12 +362,12 @@ module time_DMR_end # ( end // Default state - logic fault_detected_base[REP]; + logic fault_detected_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_flag_default_state - assign fault_detected_base[r] = '0; + assign fault_detected_b[r] = '0; end - `FF(fault_detected_q, fault_detected_d, fault_detected_base); + `FF(fault_detected_q, fault_detected_d, fault_detected_b); for (genvar r = 0; r < REP; r++) begin: gen_flag_output assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 4c95091b..c2c09432 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -147,20 +147,20 @@ module time_DMR_start # ( end // Generate default cases - state_t state_base[REP]; - DataType data_base[REP]; - logic [IDSize-1:0] id_base[REP]; + state_t state_b[REP]; + DataType data_b[REP]; + logic [IDSize-1:0] id_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_base[r] = STORE_AND_SEND; - assign data_base[r] = 0; - assign id_base[r] = 0; + assign state_b[r] = STORE_AND_SEND; + assign data_b[r] = 0; + assign id_b[r] = 0; end // State Storage - `FF(state_q, state_d, state_base); - `FF(data_q, data_d, data_base); - `FF(id_q, id_d, id_base); + `FF(state_q, state_d, state_b); + `FF(data_q, data_d, data_b); + `FF(id_q, id_d, id_b); // Output Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_output diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 91dd1a7b..b91a5d93 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -164,21 +164,21 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake - logic lock_internal[REP]; + logic lock_internal_v[REP]; if (EarlyValidEnable) begin: gen_early_valid_statemachine // Input signal reassignment to make state machine more readable - logic element_needs_shift[REP]; - logic new_element_arrived[REP]; - logic element_in_input[REP]; - logic element_relies_on_input[REP]; - logic data_usable[REP]; + logic element_needs_shift_v[REP]; + logic new_element_arrived_v[REP]; + logic element_in_input_v[REP]; + logic element_relies_on_input_v[REP]; + logic data_usable_v[REP]; for (genvar r = 0; r < REP; r++) begin: gen_data_flags always_comb begin: gen_data_flags_comb // Some new element just showed up and we need to send data outwards again. - new_element_arrived[r] = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! + new_element_arrived_v[r] = (id_same != 5'b11111) && ( // ID All same -> No element change counts. ID always needs to change! (full_same & 5'b01111) == 5'b00001 || // 1st and 2nd element the same, other two each different from pair (full_same & 5'b10111) == 5'b00010 // 1st and 3rd element the same, other two each different from pair ); @@ -186,16 +186,16 @@ module time_TMR_end # ( // Data or id is in the input -> We should consume the input for this element // Same data or id count as matches since we can remove an inexact pair on error and be fine // (Pairs where one thing matches and the other doesn't which are from a different elements can only happen with two errors) - element_in_input[r] = |partial_same[1:0]; + element_in_input_v[r] = |partial_same[1:0]; // Second register does not contain something that is completely the same elsewhere -> We should keep shifting until it is - element_needs_shift[r] = ~|full_same[2:1]; + element_needs_shift_v[r] = ~|full_same[2:1]; // Data is in input and only one of the registers -> We need to take valid_i into account for valid_o - element_relies_on_input[r] = |full_same[1:0] & ~full_same[2]; + element_relies_on_input_v[r] = |full_same[1:0] & ~full_same[2]; // Data has at least two new things that are the same - data_usable[r] = |data_same[2:0]; + data_usable_v[r] = |data_same[2:0]; end end @@ -220,12 +220,12 @@ module time_TMR_end # ( case (state_q[r]) BASE: if (valid_i) begin - if (new_element_arrived[r]) begin - if (!data_usable[r]) begin + if (new_element_arrived_v[r]) begin + if (!data_usable_v[r]) begin state_v[r] = WAIT_FOR_DATA; end else begin if (ready_i) begin - if (element_needs_shift[r]) begin + if (element_needs_shift_v[r]) begin // We can already send our data element, but it needs another shift to collect -> Go into special stat for this state_v[r] = WAIT_FOR_VALID; end @@ -266,41 +266,41 @@ module time_TMR_end # ( end // Generate default cases - state_t state_base[REP]; + state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_base[r] = WAIT_FOR_VALID; + assign state_b[r] = WAIT_FOR_VALID; end // State Storage - `FF(state_q, state_d, state_base); + `FF(state_q, state_d, state_b); // Output Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) - BASE: valid_ov[r] = (!element_relies_on_input[r] | valid_i) & data_usable[r] & new_element_arrived[r]; - WAIT_FOR_DATA: valid_ov[r] = (!element_relies_on_input[r] | valid_i) & data_usable[r]; - WAIT_FOR_READY: valid_ov[r] = (!element_relies_on_input[r] | valid_i); + BASE: valid_ov[r] = (!element_relies_on_input_v[r] | valid_i) & data_usable_v[r] & new_element_arrived_v[r]; + WAIT_FOR_DATA: valid_ov[r] = (!element_relies_on_input_v[r] | valid_i) & data_usable_v[r]; + WAIT_FOR_READY: valid_ov[r] = (!element_relies_on_input_v[r] | valid_i); WAIT_FOR_VALID: valid_ov[r] = 0; endcase case (state_q[r]) - BASE: lock_internal[r] = !ready_i | element_needs_shift[r] | !new_element_arrived[r]; - WAIT_FOR_DATA: lock_internal[r] = !ready_i | element_needs_shift[r]; - WAIT_FOR_READY: lock_internal[r] = !ready_i; - WAIT_FOR_VALID: lock_internal[r] = !valid_i; + BASE: lock_internal_v[r] = !ready_i | element_needs_shift_v[r] | !new_element_arrived_v[r]; + WAIT_FOR_DATA: lock_internal_v[r] = !ready_i | element_needs_shift_v[r]; + WAIT_FOR_READY: lock_internal_v[r] = !ready_i; + WAIT_FOR_VALID: lock_internal_v[r] = !valid_i; endcase case (state_q[r]) - BASE: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r] | !new_element_arrived[r]; - WAIT_FOR_DATA: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r]; - WAIT_FOR_READY: ready_ov[r] = ready_i & element_in_input[r] | element_needs_shift[r]; - WAIT_FOR_VALID: ready_ov[r] = element_in_input[r]; + BASE: ready_ov[r] = ready_i & element_in_input_v[r] | element_needs_shift_v[r] | !new_element_arrived_v[r]; + WAIT_FOR_DATA: ready_ov[r] = ready_i & element_in_input_v[r] | element_needs_shift_v[r]; + WAIT_FOR_READY: ready_ov[r] = ready_i & element_in_input_v[r] | element_needs_shift_v[r]; + WAIT_FOR_VALID: ready_ov[r] = element_in_input_v[r]; endcase end else begin valid_ov[r] = valid_i; - lock_internal[r] = 0; + lock_internal_v[r] = 0; ready_ov[r] = ready_i; end end @@ -308,27 +308,27 @@ module time_TMR_end # ( end else begin : gen_late_valid_statemachine // Input signal reassignment to make state machine more readable - logic new_id_arrived[REP]; - logic id_in_input[REP]; - logic id_all_same[REP]; - logic id_all_cover[REP]; - logic data_usable[REP]; + logic new_id_arrived_v[REP]; + logic id_in_input_v[REP]; + logic id_all_same_v[REP]; + logic id_all_cover_v[REP]; + logic data_usable_v[REP]; for (genvar r = 0; r < REP; r++) begin: gen_data_flags always_comb begin: gen_data_flags_comb - new_id_arrived[r] = ( + new_id_arrived_v[r] = ( (id_same == 5'b00111) || (id_same == 5'b00100) || (id_same == 5'b00010) || (id_same == 5'b01010) ); - id_in_input[r] = |id_same[1:0]; + id_in_input_v[r] = |id_same[1:0]; - id_all_same[r] = &id_same[2:0]; - id_all_cover[r] = id_same[1]; + id_all_same_v[r] = &id_same[2:0]; + id_all_cover_v[r] = id_same[1]; - data_usable[r] = |data_same[2:0]; + data_usable_v[r] = |data_same[2:0]; end end @@ -352,9 +352,9 @@ module time_TMR_end # ( case (state_q[r]) BASE: if (valid_i) begin - if (new_id_arrived[r]) begin + if (new_id_arrived_v[r]) begin if (ready_i) begin - if (id_all_cover[r]) begin + if (id_all_cover_v[r]) begin state_v[r] = WAIT_FOR_VALID_X2; end else begin state_v[r] = WAIT_FOR_VALID; @@ -366,7 +366,7 @@ module time_TMR_end # ( end WAIT_FOR_READY: if (ready_i) begin - if (id_all_cover[r]) begin + if (id_all_cover_v[r]) begin state_v[r] = WAIT_FOR_VALID_X2; end else begin state_v[r] = WAIT_FOR_VALID; @@ -394,41 +394,41 @@ module time_TMR_end # ( end // Generate default cases - state_t state_base[REP]; + state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_base[r] = WAIT_FOR_VALID; + assign state_b[r] = WAIT_FOR_VALID; end // State Storage - `FF(state_q, state_d, state_base); + `FF(state_q, state_d, state_b); // Output Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_output always_comb begin: gen_output_comb if (enable_i) begin case (state_q[r]) - BASE: valid_ov[r] = valid_i & new_id_arrived[r] & data_usable[r]; - WAIT_FOR_READY: valid_ov[r] = new_id_arrived[r] & data_usable[r]; + BASE: valid_ov[r] = valid_i & new_id_arrived_v[r] & data_usable_v[r]; + WAIT_FOR_READY: valid_ov[r] = new_id_arrived_v[r] & data_usable_v[r]; WAIT_FOR_VALID: valid_ov[r] = 0; WAIT_FOR_VALID_X2: valid_ov[r] = 0; endcase case (state_q[r]) - BASE: lock_internal[r] = !(ready_i & valid_i) | !new_id_arrived[r] ; - WAIT_FOR_READY: lock_internal[r] = !ready_i; - WAIT_FOR_VALID: lock_internal[r] = 1; - WAIT_FOR_VALID_X2: lock_internal[r] = 1; + BASE: lock_internal_v[r] = !(ready_i & valid_i) | !new_id_arrived_v[r] ; + WAIT_FOR_READY: lock_internal_v[r] = !ready_i; + WAIT_FOR_VALID: lock_internal_v[r] = 1; + WAIT_FOR_VALID_X2: lock_internal_v[r] = 1; endcase case (state_q[r]) - BASE: ready_ov[r] = ready_i & id_in_input[r] | !new_id_arrived[r]; - WAIT_FOR_READY: ready_ov[r] = ready_i & id_in_input[r]; + BASE: ready_ov[r] = ready_i & id_in_input_v[r] | !new_id_arrived_v[r]; + WAIT_FOR_READY: ready_ov[r] = ready_i & id_in_input_v[r]; WAIT_FOR_VALID: ready_ov[r] = 1; WAIT_FOR_VALID_X2: ready_ov[r] = 1; endcase end else begin valid_ov[r] = valid_i; - lock_internal[r] = 0; + lock_internal_v[r] = 0; ready_ov[r] = ready_i; end end @@ -457,7 +457,7 @@ module time_TMR_end # ( // To set Lock -> 1 require previous imput to be valid, to set Lock -> 0 lock don't require anything if (valid_i | lock_q[r]) begin - lock_v[r] = lock_internal[r]; + lock_v[r] = lock_internal_v[r]; end else begin lock_v[r] = lock_q[r]; end diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index 0107d42e..368e1a3f 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -129,20 +129,20 @@ module time_TMR_start # ( end // Generate default cases - state_t state_base[REP]; - DataType data_base[REP]; - logic [IDSize-1:0] id_base[REP]; + state_t state_b[REP]; + DataType data_b[REP]; + logic [IDSize-1:0] id_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_base[r] = STORE_AND_SEND; - assign data_base[r] = 0; - assign id_base[r] = 0; + assign state_b[r] = STORE_AND_SEND; + assign data_b[r] = 0; + assign id_b[r] = 0; end // State Storage - `FF(state_q, state_d, state_base); - `FF(data_q, data_d, data_base); - `FF(id_q, id_d, id_base); + `FF(state_q, state_d, state_b); + `FF(data_q, data_d, data_b); + `FF(id_q, id_d, id_b); // Output Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_output From 08b2179303f18fbb10161498ad7b2c98ef278614 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Wed, 29 May 2024 15:44:47 +0200 Subject: [PATCH 13/21] Switched unpacked arrays used for redundancy to packed arrays. --- rtl/time_redundancy/redundancy_controller.sv | 27 +++++----- rtl/time_redundancy/time_DMR_end.sv | 29 +++++------ rtl/time_redundancy/time_DMR_start.sv | 16 +++--- rtl/time_redundancy/time_TMR_end.sv | 55 +++++++++----------- rtl/time_redundancy/time_TMR_start.sv | 14 ++--- 5 files changed, 60 insertions(+), 81 deletions(-) diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv index 14671df6..6557bd58 100644 --- a/rtl/time_redundancy/redundancy_controller.sv +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -47,16 +47,15 @@ module redundancy_controller # ( ); // Redundant versions of output signals - logic valid_ov[REP]; - logic ready_ov[REP]; - logic busy_ov[REP]; - logic enable_ov[REP]; - logic flush_ov[REP]; + logic [REP-1:0] valid_ov; + logic [REP-1:0] ready_ov; + logic [REP-1:0] busy_ov; + logic [REP-1:0] enable_ov; + logic [REP-1:0] flush_ov; + logic [REP-1:0] timeout_v; - logic enable_v[REP], enable_d[REP], enable_q[REP]; - logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; - - logic timout_v[REP]; + logic [REP-1:0] enable_b, enable_v, enable_d, enable_q; + logic [REP-1:0][$clog2(LockTimeout)-1:0] counter_b, counter_v, counter_d, counter_q; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin @@ -70,13 +69,13 @@ module redundancy_controller # ( // If the unit is stalled e.g. nothing gets out during for the timeout, then trickle new operations in to unstall it if (counter_q[r] > LockTimeout) begin counter_v[r] = 0; - timout_v[r] = 1; + timeout_v[r] = 1; end else if (valid_i && enable_q[r] && !enable_i) begin counter_v[r] = counter_q[r] + 1; - timout_v[r] = 0; + timeout_v[r] = 0; end else begin counter_v[r] = 0; - timout_v[r] = 0; + timeout_v[r] = 0; end end end @@ -91,8 +90,6 @@ module redundancy_controller # ( end // Generate default case - logic enable_b[REP]; - logic [$clog2(LockTimeout)-1:0] counter_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state assign enable_b[r] = '0; assign counter_b[r] = '0; @@ -106,7 +103,7 @@ module redundancy_controller # ( always_comb begin enable_ov[r] = enable_q[r]; - if ((enable_q[r] == enable_i) || timout_v[r]) begin + if ((enable_q[r] == enable_i) || timeout_v[r]) begin valid_ov[r] = valid_i; ready_ov[r] = ready_i; busy_ov[r] = busy_i; diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index 740ac969..a13bcd76 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -88,10 +88,10 @@ module time_DMR_end # ( ); // Redundant Output signals - logic ready_ov[REP]; - logic valid_ov[REP]; - logic needs_retry_ov[REP]; - logic fault_detected_ov[REP]; + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; + logic [REP-1:0] needs_retry_ov; + logic [REP-1:0] fault_detected_ov; ///////////////////////////////////////////////////////////////////////////////// // Storage of incomming results and generating good output data @@ -148,8 +148,8 @@ module time_DMR_end # ( ///////////////////////////////////////////////////////////////////////////////// // Logic to find out what we should do with our data based on same / not same - logic new_element_arrived_v[REP]; - logic data_usable_v[REP]; + logic [REP-1:0] new_element_arrived_v; + logic [REP-1:0] data_usable_v; // Flag Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_data_flags @@ -166,8 +166,8 @@ module time_DMR_end # ( // State machine to figure out handshake typedef enum logic [0:0] {BASE, WAIT_FOR_READY} state_t; - state_t state_v[REP], state_d[REP], state_q[REP]; - logic valid_internal_v[REP], lock_internal_v[REP]; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + logic [REP-1:0] valid_internal_v, lock_internal_v; // Special State Description: // Wait for Ready: We got some data that is usable, but downstream can't use it yet @@ -200,7 +200,6 @@ module time_DMR_end # ( end // Generate default cases - state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_b[r] = BASE; end @@ -244,8 +243,8 @@ module time_DMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to lock / unlock Arbitrator with Watchdog timer - logic lock_v[REP], lock_d[REP], lock_q[REP]; - logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; + logic [REP-1:0] lock_b, lock_v, lock_d, lock_q; + logic [REP-1:0][$clog2(LockTimeout)-1:0] counter_b, counter_v, counter_d, counter_q; // Next State Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_lock_next_state @@ -283,8 +282,6 @@ module time_DMR_end # ( assign lock_o = lock_d[0]; // Default state - logic lock_b[REP]; - logic [$clog2(LockTimeout)-1:0] counter_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state assign lock_b[r] = '0; assign counter_b[r] = '0; @@ -300,7 +297,7 @@ module time_DMR_end # ( logic id_fault_q; assign id_fault_q = ^id_q; - logic [2 ** (IDSize-1)-1:0] recently_seen_v[REP], recently_seen_d[REP], recently_seen_q[REP]; + logic [REP-1:0][2 ** (IDSize-1)-1:0] recently_seen_b, recently_seen_v, recently_seen_d, recently_seen_q; for (genvar r = 0; r < REP; r++) begin: gen_deduplication_next_state always_comb begin: gen_deduplication_next_state_comb @@ -322,7 +319,6 @@ module time_DMR_end # ( end // Default state - logic [2 ** (IDSize-1)-1:0] recently_seen_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_deduplication_default_state assign recently_seen_b[r] = ~'0; // All 1s! end @@ -355,14 +351,13 @@ module time_DMR_end # ( // In a non-error case, we should have a full same signal every other cycle // So if that is not the case we had a fault. - logic fault_detected_d[REP], fault_detected_q[REP]; + logic [REP-1:0] fault_detected_b, fault_detected_d, fault_detected_q; for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state assign fault_detected_d[r] = ~|full_same[1:0] & valid_i;; end // Default state - logic fault_detected_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_flag_default_state assign fault_detected_b[r] = '0; end diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index c2c09432..463072bd 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -76,9 +76,9 @@ module time_DMR_start # ( input logic ready_i ); // Redundant Output signals - logic [IDSize-1:0] next_id_ov[REP]; - logic ready_ov[REP]; - logic valid_ov[REP]; + logic [REP-1:0][IDSize-1:0] next_id_ov; + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; // State machine TLDR // - counting the state from 0 to 2 if the handshake is good @@ -86,9 +86,9 @@ module time_DMR_start # ( // Next State Combinatorial Logic typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE} state_t; - state_t state_v[REP], state_d[REP], state_q[REP]; - DataType data_v[REP], data_d[REP], data_q[REP]; - logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + DataType [REP-1:0] data_b, data_v, data_d, data_q; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin: gen_next_state_comb @@ -147,10 +147,6 @@ module time_DMR_start # ( end // Generate default cases - state_t state_b[REP]; - DataType data_b[REP]; - logic [IDSize-1:0] id_b[REP]; - for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_b[r] = STORE_AND_SEND; assign data_b[r] = 0; diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index b91a5d93..5ccaa8ca 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -82,9 +82,9 @@ module time_TMR_end # ( ); // Redundant Output signals - logic ready_ov[REP]; - logic valid_ov[REP]; - logic fault_detected_ov[REP]; + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; + logic [REP-1:0] fault_detected_ov; ///////////////////////////////////////////////////////////////////////////////// // Storage of incomming results and generating good output data @@ -164,16 +164,16 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake - logic lock_internal_v[REP]; + logic [REP-1:0] lock_internal_v; if (EarlyValidEnable) begin: gen_early_valid_statemachine // Input signal reassignment to make state machine more readable - logic element_needs_shift_v[REP]; - logic new_element_arrived_v[REP]; - logic element_in_input_v[REP]; - logic element_relies_on_input_v[REP]; - logic data_usable_v[REP]; + logic [REP-1:0] element_needs_shift_v; + logic [REP-1:0] new_element_arrived_v; + logic [REP-1:0] element_in_input_v; + logic [REP-1:0] element_relies_on_input_v; + logic [REP-1:0] data_usable_v; for (genvar r = 0; r < REP; r++) begin: gen_data_flags always_comb begin: gen_data_flags_comb @@ -208,7 +208,7 @@ module time_TMR_end # ( // WAIT_FOR_DATA: We got some pieces of data that should belong together but they are not the same // -> We try to collect one more piece of the same data and then send it downstream typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_DATA} state_t; - state_t state_v[REP], state_d[REP], state_q[REP]; + state_t [REP-1:0] state_b, state_v, state_d, state_q; // Next State Combinatorial Logic @@ -266,7 +266,6 @@ module time_TMR_end # ( end // Generate default cases - state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_b[r] = WAIT_FOR_VALID; end @@ -308,11 +307,11 @@ module time_TMR_end # ( end else begin : gen_late_valid_statemachine // Input signal reassignment to make state machine more readable - logic new_id_arrived_v[REP]; - logic id_in_input_v[REP]; - logic id_all_same_v[REP]; - logic id_all_cover_v[REP]; - logic data_usable_v[REP]; + logic [REP-1:0] new_id_arrived_v; + logic [REP-1:0] id_in_input_v; + logic [REP-1:0] id_all_same_v; + logic [REP-1:0] id_all_cover_v; + logic [REP-1:0] data_usable_v; for (genvar r = 0; r < REP; r++) begin: gen_data_flags always_comb begin: gen_data_flags_comb @@ -341,7 +340,7 @@ module time_TMR_end # ( // WAIT_FOR_VALID_X2: We have recieved three fully usable pieces of data // -> We need to wait for at least two new data elements before data can be valid again typedef enum logic [1:0] {BASE, WAIT_FOR_READY, WAIT_FOR_VALID, WAIT_FOR_VALID_X2} state_t; - state_t state_v[REP], state_d[REP], state_q[REP]; + state_t [REP-1:0] state_b, state_v, state_d, state_q; // Next State Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_next_state @@ -394,7 +393,6 @@ module time_TMR_end # ( end // Generate default cases - state_t state_b[REP]; for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_b[r] = WAIT_FOR_VALID; end @@ -438,8 +436,8 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to lock / unlock Arbitrator with Watchdog timer - logic lock_v[REP], lock_d[REP], lock_q[REP]; - logic [$clog2(LockTimeout)-1:0] counter_v[REP], counter_d[REP], counter_q[REP]; + logic [REP-1:0] lock_b, lock_v, lock_d, lock_q; + logic [REP-1:0][$clog2(LockTimeout)-1:0] counter_b, counter_v, counter_d, counter_q; // Next State Combinatorial Logic for (genvar r = 0; r < REP; r++) begin: gen_lock_next_state @@ -477,16 +475,14 @@ module time_TMR_end # ( assign lock_o = lock_d[0]; // Default state - logic lock_base[REP]; - logic [$clog2(LockTimeout)-1:0] counter_base[REP]; for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state - assign lock_base[r] = '0; - assign counter_base[r] = '0; + assign lock_b[r] = '0; + assign counter_b[r] = '0; end // State Storage - `FF(lock_q, lock_d, lock_base); - `FF(counter_q, counter_d, counter_base); + `FF(lock_q, lock_d, lock_b); + `FF(counter_q, counter_d, counter_b); /////////////////////////////////////////////////////////////////////////////////////////////////// // Build error flag @@ -497,19 +493,18 @@ module time_TMR_end # ( // always have at least two data elements that are the same in the first 3 elements. // Make output only 1 for a signly cycle even if internal pipeline is stopped - logic fault_detected_d[REP], fault_detected_q[REP]; + logic [REP-1:0] fault_detected_b, fault_detected_d, fault_detected_q; for (genvar r = 0; r < REP; r++) begin: gen_flag_next_state assign fault_detected_d[r] = ~|full_same[2:0] & valid_i; end // Default state - logic fault_detected_base[REP]; for (genvar r = 0; r < REP; r++) begin: gen_flag_default_state - assign fault_detected_base[r] = '0; + assign fault_detected_b[r] = '0; end - `FF(fault_detected_q, fault_detected_d, fault_detected_base); + `FF(fault_detected_q, fault_detected_d, fault_detected_b); for (genvar r = 0; r < REP; r++) begin: gen_flag_output assign fault_detected_ov[r] = fault_detected_d[r] & !fault_detected_q[r]; diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index 368e1a3f..5cd1d819 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -60,8 +60,8 @@ module time_TMR_start # ( input logic ready_i ); // Redundant Output signals - logic ready_ov[REP]; - logic valid_ov[REP]; + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; // State machine TLDR // - counting the state from 0 to 2 if the handshake is good @@ -69,9 +69,9 @@ module time_TMR_start # ( // Next State Combinatorial Logic typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE_ONE, REPLICATE_TWO} state_t; - state_t state_v[REP], state_d[REP], state_q[REP]; - DataType data_v[REP], data_d[REP], data_q[REP]; - logic [IDSize-1:0] id_v[REP], id_d[REP], id_q[REP]; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + DataType [REP-1:0] data_b, data_v, data_d, data_q; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin : gen_next_state_comb @@ -129,10 +129,6 @@ module time_TMR_start # ( end // Generate default cases - state_t state_b[REP]; - DataType data_b[REP]; - logic [IDSize-1:0] id_b[REP]; - for (genvar r = 0; r < REP; r++) begin: gen_default_state assign state_b[r] = STORE_AND_SEND; assign data_b[r] = 0; From adcabe9ed5016e836d1873b4775ddbc74bdd93be Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 14 Jun 2024 09:24:16 +0200 Subject: [PATCH 14/21] Switched voting macros to use version in /include and removed local header file. --- Bender.yml | 1 - rtl/time_redundancy/redundancy_controller.sv | 27 ++++---------- rtl/time_redundancy/retry_inorder_start.sv | 7 +++- rtl/time_redundancy/retry_start.sv | 7 +++- rtl/time_redundancy/time_DMR_end.sv | 38 +++++--------------- rtl/time_redundancy/time_DMR_start.sv | 32 +++++++---------- rtl/time_redundancy/time_TMR_end.sv | 35 +++++------------- rtl/time_redundancy/time_TMR_start.sv | 23 ++++-------- rtl/time_redundancy/voters.svh | 33 ----------------- src_files.yml | 1 - 10 files changed, 55 insertions(+), 149 deletions(-) delete mode 100644 rtl/time_redundancy/voters.svh diff --git a/Bender.yml b/Bender.yml index dd43df34..b5b253a2 100644 --- a/Bender.yml +++ b/Bender.yml @@ -27,7 +27,6 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_cor.sv - rtl/time_redundancy/retry_interface.sv - rtl/time_redundancy/rr_arb_tree_lock.sv - - rtl/time_redundancy/voters.svh - rtl/TMR_voter.sv - rtl/TMR_voter_fail.sv - rtl/TMR_word_voter.sv diff --git a/rtl/time_redundancy/redundancy_controller.sv b/rtl/time_redundancy/redundancy_controller.sv index 6557bd58..b85ec792 100644 --- a/rtl/time_redundancy/redundancy_controller.sv +++ b/rtl/time_redundancy/redundancy_controller.sv @@ -10,7 +10,7 @@ // be consumed and the condition for switching can not be reached. // Switching back to the redundant mode will unstall the setup. -`include "voters.svh" +`include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" module redundancy_controller # ( @@ -80,14 +80,8 @@ module redundancy_controller # ( end end - // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters - `VOTE3to3(enable_v, enable_d); - `VOTE3to3(counter_v, counter_d); - end else begin - assign enable_d = enable_v; - assign counter_d = counter_v; - end + `VOTEXX(REP, enable_v, enable_d); + `VOTEXX(REP, counter_v, counter_d); // Generate default case for (genvar r = 0; r < REP; r++) begin: gen_default_state @@ -116,16 +110,9 @@ module redundancy_controller # ( end // Output voting logic - if (InternalRedundancy) begin: gen_output_voters - `VOTE3to1(enable_ov, enable_o); - `VOTE3to1(valid_ov, valid_o); - `VOTE3to1(ready_ov, ready_o); - `VOTE3to1(busy_ov, busy_o); - end else begin: gen_output_passthrough - assign enable_o = enable_ov[0]; - assign valid_o = valid_ov[0]; - assign ready_o = ready_ov[0]; - assign busy_o = busy_ov[0]; - end + `VOTEX1(REP, enable_ov, enable_o); + `VOTEX1(REP, valid_ov, valid_o); + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, busy_ov, busy_o); endmodule diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index f461364b..fe822c58 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -13,7 +13,12 @@ // but at the wronge place or time. `include "common_cells/registers.svh" -`include "voters.svh" + +`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ +begin \ + output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ + output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ +end module retry_inorder_start # ( parameter type DataType = logic, diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index d00dbb6d..6f7edab7 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -17,7 +17,12 @@ // If you need in-order for pipelined processes have a look at retry_inorder instead. `include "common_cells/registers.svh" -`include "voters.svh" + +`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ +begin \ + output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ + output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ +end module retry_start # ( parameter type DataType = logic, diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index a13bcd76..b398e3d1 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -32,7 +32,7 @@ // This can for example be used with the rr_arb_tree_lock module, but other implementations are // permissible. -`include "voters.svh" +`include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" module time_DMR_end # ( @@ -205,11 +205,7 @@ module time_DMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin: gen_state_voters - `VOTE3to3ENUM(state_v, state_d); - end else begin: gen_state_passthrough - assign state_d = state_v; - end + `VOTEXX(REP, state_v, state_d); // State Storage `FF(state_q, state_d, state_b); @@ -271,13 +267,8 @@ module time_DMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_lock_voters - `VOTE3to3(lock_v, lock_d); - `VOTE3to3(counter_v, counter_d); - end else begin: gen_lock_passthrough - assign counter_d = counter_v; - assign lock_d = lock_v; - end + `VOTEXX(REP, lock_v, lock_d); + `VOTEXX(REP, counter_v, counter_d); assign lock_o = lock_d[0]; @@ -312,11 +303,7 @@ module time_DMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin: gen_deduplication_voters - `VOTE3to3(recently_seen_v, recently_seen_d); - end else begin: gen_deduplication_passthrough - assign recently_seen_d = recently_seen_v; - end + `VOTEXX(REP, recently_seen_v, recently_seen_d); // Default state for (genvar r = 0; r < REP; r++) begin: gen_deduplication_default_state @@ -371,16 +358,9 @@ module time_DMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // Output Voting - if (InternalRedundancy) begin: gen_output_voters - `VOTE3to1(ready_ov, ready_o); - `VOTE3to1(valid_ov, valid_o); - `VOTE3to1(needs_retry_ov, needs_retry_o); - `VOTE3to1(fault_detected_ov, fault_detected_o); - end else begin: gen_output_passthrough - assign ready_o = ready_ov[0]; - assign valid_o = valid_ov[0]; - assign needs_retry_o = needs_retry_ov[0]; - assign fault_detected_o = fault_detected_ov[0]; - end + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); + `VOTEX1(REP, needs_retry_ov, needs_retry_o); + `VOTEX1(REP, fault_detected_ov, fault_detected_o); endmodule diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 463072bd..701d5cd5 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -32,9 +32,15 @@ // This can for example be used with the rr_arb_tree_lock module, but other implementations are // permissible. -`include "voters.svh" +`include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" +`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ +begin \ + output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ + output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ +end + module time_DMR_start # ( // The data type you want to send through / replicate parameter type DataType = logic, @@ -136,15 +142,9 @@ module time_DMR_start # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters - `VOTE3to3ENUM(state_v, state_d); - `VOTE3to3(id_v, id_d); - `VOTE3to3(data_v, data_d); - end else begin: gen_state_passthrough - assign state_d = state_v; - assign data_d = data_v; - assign id_d = id_v; - end + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + `VOTEXX(REP, data_v, data_d); // Generate default cases for (genvar r = 0; r < REP; r++) begin: gen_default_state @@ -182,14 +182,8 @@ module time_DMR_start # ( assign data_o = data_d[0]; assign id_o = id_d[0]; - if (InternalRedundancy) begin: gen_output_voters - `VOTE3to1(next_id_ov, next_id_o); - `VOTE3to1(ready_ov, ready_o); - `VOTE3to1(valid_ov, valid_o); - end else begin: gen_output_passthrough - assign next_id_o = next_id_ov[0]; - assign ready_o = ready_ov[0]; - assign valid_o = valid_ov[0]; - end + `VOTEX1(REP, next_id_ov, next_id_o); + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); endmodule diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index 5ccaa8ca..e6c01311 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -22,7 +22,7 @@ // This can for example be used with the rr_arb_tree_lock module, but other implementations are // permissible. -`include "voters.svh" +`include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" module time_TMR_end # ( @@ -259,11 +259,7 @@ module time_TMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters - `VOTE3to3ENUM(state_v, state_d); - end else begin: gen_state_passthrough - assign state_d = state_v; - end + `VOTEXX(REP, state_v, state_d); // Generate default cases for (genvar r = 0; r < REP; r++) begin: gen_default_state @@ -386,11 +382,7 @@ module time_TMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin : gen_state_voters - `VOTE3to3ENUM(state_v, state_d); - end else begin: gen_state_passthrough - assign state_d = state_v; - end + `VOTEXX(REP, state_v, state_d); // Generate default cases for (genvar r = 0; r < REP; r++) begin: gen_default_state @@ -464,13 +456,8 @@ module time_TMR_end # ( end // State Voting Logic - if (InternalRedundancy) begin: gen_lock_voters - `VOTE3to3(lock_v, lock_d); - `VOTE3to3(counter_v, counter_d); - end else begin: gen_lock_passthrough - assign counter_d = counter_v; - assign lock_d = lock_v; - end + `VOTEXX(REP, lock_v, lock_d); + `VOTEXX(REP, counter_v, counter_d); assign lock_o = lock_d[0]; @@ -513,14 +500,8 @@ module time_TMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // Output Voting - if (InternalRedundancy) begin : gen_output_voters - `VOTE3to1(ready_ov, ready_o); - `VOTE3to1(valid_ov, valid_o); - `VOTE3to1(fault_detected_ov, fault_detected_o); - end else begin: gen_output_passthrough - assign ready_o = ready_ov[0]; - assign valid_o = valid_ov[0]; - assign fault_detected_o = fault_detected_ov[0]; - end + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); + `VOTEX1(REP, fault_detected_ov, fault_detected_o); endmodule diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index 5cd1d819..5d9ba107 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -22,7 +22,7 @@ // This can for example be used with the rr_arb_tree_lock module, but other implementations are // permissible. -`include "voters.svh" +`include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" module time_TMR_start # ( @@ -118,15 +118,9 @@ module time_TMR_start # ( end // State Voting Logic - if (InternalRedundancy) begin: gen_state_voters - `VOTE3to3ENUM(state_v, state_d); - `VOTE3to3(id_v, id_d); - `VOTE3to3(data_v, data_d); - end else begin: gen_state_passthrough - assign state_d = state_v; - assign data_d = data_v; - assign id_d = id_v; - end + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + `VOTEXX(REP, data_v, data_d); // Generate default cases for (genvar r = 0; r < REP; r++) begin: gen_default_state @@ -168,12 +162,7 @@ module time_TMR_start # ( assign data_o = data_d[0]; assign id_o = id_d[0]; - if (InternalRedundancy) begin: gen_output_voters - `VOTE3to1(ready_ov, ready_o); - `VOTE3to1(valid_ov, valid_o); - end else begin: gen_output_voters - assign ready_o = ready_ov[0]; - assign valid_o = valid_ov[0]; - end + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); endmodule diff --git a/rtl/time_redundancy/voters.svh b/rtl/time_redundancy/voters.svh deleted file mode 100644 index 2d2d7b41..00000000 --- a/rtl/time_redundancy/voters.svh +++ /dev/null @@ -1,33 +0,0 @@ - -`define MAJORITY(a, b, c, o) \ -begin \ - o = (a & b) | (a & c) | (b & c); \ -end - -`define BITWISE(a) \ - a[$bits(a)-1:0] - -`define VOTE3to3(input_signal, output_signal) \ -always_comb begin \ - `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[0]); \ - `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[1]); \ - `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal[2]); \ -end - -`define VOTE3to3ENUM(input_signal, output_signal) \ -always_comb begin \ - `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[0])); \ - `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[1])); \ - `MAJORITY(`BITWISE(input_signal[0]), `BITWISE(input_signal[1]), `BITWISE(input_signal[2]), `BITWISE(output_signal[2])); \ -end - -`define VOTE3to1(input_signal, output_signal) \ -always_comb begin \ - `MAJORITY(input_signal[0], input_signal[1], input_signal[2], output_signal); \ -end - -`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ -begin \ - output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ - output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ -end \ No newline at end of file diff --git a/src_files.yml b/src_files.yml index 1dd0997b..d1baa79d 100644 --- a/src_files.yml +++ b/src_files.yml @@ -11,7 +11,6 @@ redundancy_cells: lowrisc_ecc/prim_secded_72_64_enc.sv, rtl/time_redundancy/retry_interface.sv rtl/time_redundancy/rr_arb_tree_lock.sv - rtl/time_redundancy/voters.svh rtl/TMR_voter.sv, rtl/TMR_voter_fail.sv, rtl/TMR_word_voter, From 81954fdfdb26633419a5855e77643f71f60dd9a1 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 2 Jul 2024 09:33:10 +0200 Subject: [PATCH 15/21] Switched asserts in lockable RR-Arbitrator to $error so they do not stop fault injection when violated. --- rtl/time_redundancy/rr_arb_tree_lock.sv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rtl/time_redundancy/rr_arb_tree_lock.sv b/rtl/time_redundancy/rr_arb_tree_lock.sv index 26ff9c74..c04d058e 100644 --- a/rtl/time_redundancy/rr_arb_tree_lock.sv +++ b/rtl/time_redundancy/rr_arb_tree_lock.sv @@ -176,14 +176,14 @@ module rr_arb_tree_lock #( `ifndef VERILATOR lock: assert property( @(posedge clk_i) LockIn |-> req_o && !gnt_i |=> idx_o == $past(idx_o)) else - $fatal (1, "Lock implies same arbiter decision in next cycle if output is not \ + $error (1, "Lock implies same arbiter decision in next cycle if output is not \ ready."); logic [NumIn-1:0] req_tmp; assign req_tmp = req_q & req_i; lock_req: assume property( @(posedge clk_i) LockIn |-> lock_d |=> req_tmp == req_q) else - $fatal (1, "It is disallowed to deassert unserved request signals when LockIn is \ + $error (1, "It is disallowed to deassert unserved request signals when LockIn is \ enabled."); `endif // pragma translate_on @@ -353,30 +353,30 @@ module rr_arb_tree_lock #( `ifndef XSIM initial begin : p_assert assert(NumIn) - else $fatal(1, "Input must be at least one element wide."); + else $error(1, "Input must be at least one element wide."); assert(!(LockIn && ExtPrio)) - else $fatal(1,"Cannot use LockIn feature together with external ExtPrio."); + else $error(1,"Cannot use LockIn feature together with external ExtPrio."); end hot_one : assert property( @(posedge clk_i) $onehot0(gnt_o)) - else $fatal (1, "Grant signal must be hot1 or zero."); + else $error (1, "Grant signal must be hot1 or zero."); gnt0 : assert property( @(posedge clk_i) |gnt_o |-> gnt_i) - else $fatal (1, "Grant out implies grant in."); + else $error (1, "Grant out implies grant in."); gnt1 : assert property( @(posedge clk_i) req_o |-> gnt_i |-> |gnt_o) - else $fatal (1, "Req out and grant in implies grant out."); + else $error (1, "Req out and grant in implies grant out."); gnt_idx : assert property( @(posedge clk_i) req_o |-> gnt_i |-> gnt_o[idx_o]) - else $fatal (1, "Idx_o / gnt_o do not match."); + else $error (1, "Idx_o / gnt_o do not match."); req1 : assert property( @(posedge clk_i) req_o |-> |req_i) - else $fatal (1, "Req out implies req in."); + else $error (1, "Req out implies req in."); lock2 : assert property( @(posedge clk_i) disable iff (!rst_ni) lock_rr_q |-> idx_o == $past(idx_o)) From d1bc37491a9ca17383ded5c17eb2f40a6e42674a Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Mon, 15 Jul 2024 09:06:38 +0200 Subject: [PATCH 16/21] Added internal redundancy option to lockable RR-Arbiter --- rtl/time_redundancy/rr_arb_tree_lock.sv | 215 ++++++++++-------------- rtl/time_redundancy/time_DMR_end.sv | 4 +- rtl/time_redundancy/time_TMR_end.sv | 4 +- test/tb_time_dmr_retry_lock.sv | 11 +- test/tb_time_tmr_lock.sv | 11 +- 5 files changed, 109 insertions(+), 136 deletions(-) diff --git a/rtl/time_redundancy/rr_arb_tree_lock.sv b/rtl/time_redundancy/rr_arb_tree_lock.sv index c04d058e..778c08ef 100644 --- a/rtl/time_redundancy/rr_arb_tree_lock.sv +++ b/rtl/time_redundancy/rr_arb_tree_lock.sv @@ -46,6 +46,9 @@ /// If it is `1'b1` two `lzc`, a masking logic stage and a two input multiplexer are instantiated. /// However these are small in respect of the data multiplexers needed, as the width of the `req_i` /// signal is usually less as than `DataWidth`. + +`include "redundancy_cells/voters.svh" + module rr_arb_tree_lock #( /// Number of inputs to be arbitrated. parameter int unsigned NumIn = 64, @@ -67,13 +70,6 @@ module rr_arb_tree_lock #( /// /// Set to `1'b1` to treat req/gnt as vld/rdy. parameter bit AxiVldRdy = 1'b0, - /// The `LockIn` option prevents the arbiter from changing the arbitration - /// decision when the arbiter is disabled. I.e., the index of the first request - /// that wins the arbitration will be locked in case the destination is not - /// able to grant the request in the same cycle. - /// - /// Set to `1'b1` to enable. - parameter bit LockIn = 1'b0, /// When set, ensures that throughput gets distributed evenly between all inputs. /// /// Set to `1'b0` to disable. @@ -83,18 +79,25 @@ module rr_arb_tree_lock #( parameter int unsigned IdxWidth = (NumIn > 32'd1) ? unsigned'($clog2(NumIn)) : 32'd1, /// Dependent parameter, do **not** overwrite. /// Type for defining the arbitration priority and arbitrated index signal. - parameter type idx_t = logic [IdxWidth-1:0] + parameter type idx_t = logic [IdxWidth-1:0], + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 ) ( /// Clock, positive edge triggered. input logic clk_i, /// Asynchronous reset, active low. input logic rst_ni, /// Clears the arbiter state. Only used if `ExtPrio` is `1'b0` or `LockIn` is `1'b1`. - input logic flush_i, + input logic [REP-1:0] flush_i, /// External round-robin priority. input idx_t rr_i, /// lock idx signal, only used if `ExtPrio` is `1'b0.` - input logic lock_rr_i, + input logic [REP-1:0] lock_rr_i, /// Input requests arbitration. input logic [NumIn-1:0] req_i, /* verilator lint_off UNOPTFLAT */ @@ -117,7 +120,7 @@ module rr_arb_tree_lock #( `ifndef VERILATOR `ifndef XSIM // Default SVA reset - default disable iff (!rst_ni || flush_i); + default disable iff (!rst_ni || flush_i[0]); `endif `endif // pragma translate_on @@ -133,86 +136,39 @@ module rr_arb_tree_lock #( localparam int unsigned NumLevels = unsigned'($clog2(NumIn)); /* verilator lint_off UNOPTFLAT */ - idx_t [2**NumLevels-2:0] index_nodes; // used to propagate the indices + idx_t [2**NumLevels-2:0][REP-1:0] index_nodes; // used to propagate the indices DataType [2**NumLevels-2:0] data_nodes; // used to propagate the data logic [2**NumLevels-2:0] gnt_nodes; // used to propagate the grant to masters logic [2**NumLevels-2:0] req_nodes; // used to propagate the requests to slave /* lint_off */ - idx_t rr_q; + idx_t [REP-1:0] rr_q; logic [NumIn-1:0] req_d; // the final arbitration decision can be taken from the root of the tree assign req_o = req_nodes[0]; assign data_o = data_nodes[0]; - assign idx_o = index_nodes[0]; + assign idx_o = index_nodes[0][0]; + if (ExtPrio) begin : gen_ext_rr - assign rr_q = rr_i; + assign rr_q[0] = rr_i; assign req_d = req_i; end else begin : gen_int_rr - idx_t rr_d; - - // lock arbiter decision in case we got at least one req and no acknowledge - if (LockIn) begin : gen_lock - logic lock_d, lock_q; - logic [NumIn-1:0] req_q; - - assign lock_d = req_o & ~gnt_i; - assign req_d = (lock_q) ? req_q : req_i; - - always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg - if (!rst_ni) begin - lock_q <= '0; - end else begin - if (flush_i) begin - lock_q <= '0; - end else begin - lock_q <= lock_d; - end - end - end + idx_t [REP-1:0] rr_d; - // pragma translate_off - `ifndef VERILATOR - lock: assert property( - @(posedge clk_i) LockIn |-> req_o && !gnt_i |=> idx_o == $past(idx_o)) else - $error (1, "Lock implies same arbiter decision in next cycle if output is not \ - ready."); - - logic [NumIn-1:0] req_tmp; - assign req_tmp = req_q & req_i; - lock_req: assume property( - @(posedge clk_i) LockIn |-> lock_d |=> req_tmp == req_q) else - $error (1, "It is disallowed to deassert unserved request signals when LockIn is \ - enabled."); - `endif - // pragma translate_on - - always_ff @(posedge clk_i or negedge rst_ni) begin : p_req_regs - if (!rst_ni) begin - req_q <= '0; - end else begin - if (flush_i) begin - req_q <= '0; - end else begin - req_q <= req_d; - end - end - end - end else begin : gen_no_lock - assign req_d = req_i; - end + assign req_d = req_i; idx_t next_idx; if (FairArb) begin : gen_fair_arb + logic [NumIn-1:0] upper_mask, lower_mask; idx_t upper_idx, lower_idx; logic upper_empty, lower_empty; for (genvar i = 0; i < NumIn; i++) begin : gen_mask - assign upper_mask[i] = (i > rr_q) ? req_d[i] : 1'b0; - assign lower_mask[i] = (i <= rr_q) ? req_d[i] : 1'b0; + assign upper_mask[i] = (i > rr_q[0]) ? req_d[i] : 1'b0; + assign lower_mask[i] = (i <= rr_q[0]) ? req_d[i] : 1'b0; end lzc #( @@ -235,45 +191,52 @@ module rr_arb_tree_lock #( assign next_idx = upper_empty ? lower_idx : upper_idx; end else begin : gen_unfair_arb - assign next_idx = ((rr_q == idx_t'(NumIn-1)) ? '0 : rr_q + 1'b1); + assign next_idx = ((rr_q[0] == idx_t'(NumIn-1)) ? '0 : rr_q[0] + 1'b1); end - always_comb begin - if (lock_rr_i) begin - rr_d = idx_o; - end else begin - if (gnt_i && req_o) begin - rr_d = next_idx; + idx_t [REP-1:0] voted_idx, voted_rr_q; + `VOTEXX(REP, index_nodes[0], voted_idx); + `VOTEXX(REP, rr_q, voted_rr_q); + + for (genvar r = 0; r < REP; r++) begin: gen_rr + always_comb begin + if (lock_rr_i[r]) begin + rr_d[r] = voted_idx[r]; end else begin - rr_d = rr_q; + if (gnt_i && req_o) begin + rr_d[r] = next_idx; + end else begin + rr_d[r] = voted_rr_q[r]; + end end end - end - // this holds the highest priority - always_ff @(posedge clk_i or negedge rst_ni) begin : p_rr_regs - if (!rst_ni) begin - rr_q <= '0; - end else begin - if (flush_i) begin - rr_q <= '0; + // this holds the highest priority + always_ff @(posedge clk_i or negedge rst_ni) begin : p_rr_regs + if (!rst_ni) begin + rr_q[r] <= '0; end else begin - rr_q <= rr_d; + if (flush_i[r]) begin + rr_q[r] <= '0; + end else begin + rr_q[r] <= rr_d[r]; + end end end end - end - logic lock_rr_q; - always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg - if (!rst_ni) begin - lock_rr_q <= '1; - end else begin - if (flush_i) begin - lock_rr_q <= '0; + logic [REP-1:0] lock_rr_q; + for (genvar r = 0; r < REP; r++) begin: gen_lock_q + always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg + if (!rst_ni) begin + lock_rr_q[r] <= '1; end else begin - lock_rr_q <= lock_rr_i; + if (flush_i[r]) begin + lock_rr_q[r] <= '0; + end else begin + lock_rr_q[r] <= lock_rr_i[r]; + end end end end @@ -284,7 +247,7 @@ module rr_arb_tree_lock #( for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : gen_levels for (genvar l = 0; l < 2**level; l++) begin : gen_level // local select signal - logic sel; + logic [REP-1:0] sel; // index calcs localparam int unsigned Idx0 = 2**level-1+l;// current node localparam int unsigned Idx1 = 2**(level+1)-1+l*2; @@ -294,32 +257,34 @@ module rr_arb_tree_lock #( // if two successive indices are still in the vector... if (unsigned'(l) * 2 < NumIn-1) begin : gen_reduce - // arbitration: round robin - always_comb begin - if (lock_rr_q) begin - sel = rr_q[NumLevels-1-level]; - end else begin - sel = ~req_d[l*2] | req_d[l*2+1] & rr_q[NumLevels-1-level]; + for (genvar r = 0; r < REP; r++) begin: gen_select + // arbitration: round robin + always_comb begin + if (lock_rr_q[r]) begin + sel[r] = rr_q[r][NumLevels-1-level]; + end else begin + sel[r] = ~req_d[l*2] | req_d[l*2+1] & rr_q[0][NumLevels-1-level]; + end end + assign index_nodes[Idx0][r] = idx_t'(sel[r]); end - assign index_nodes[Idx0] = idx_t'(sel); - assign req_nodes[Idx0] = (sel) ? req_d[l*2+1] : req_d[l*2] ; - assign data_nodes[Idx0] = (sel) ? data_i[l*2+1] : data_i[l*2]; - assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]) & ~sel; - assign gnt_o[l*2+1] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel; + assign req_nodes[Idx0] = (sel[0]) ? req_d[l*2+1] : req_d[l*2] ; + assign data_nodes[Idx0] = (sel[0]) ? data_i[l*2+1] : data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]) & ~sel[0]; + assign gnt_o[l*2+1] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel[0]; end // if only the first index is still in the vector... if (unsigned'(l) * 2 == NumIn-1) begin : gen_first assign req_nodes[Idx0] = req_d[l*2]; - assign index_nodes[Idx0] = '0;// always zero in this case + assign index_nodes[Idx0] = '0; assign data_nodes[Idx0] = data_i[l*2]; assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]); end // if index is out of range, fill up with zeros (will get pruned) if (unsigned'(l) * 2 > NumIn-1) begin : gen_out_of_range assign req_nodes[Idx0] = 1'b0; - assign index_nodes[Idx0] = idx_t'('0); + assign index_nodes[Idx0] = '0; assign data_nodes[Idx0] = DataType'('0); end ////////////////////////////////////////////////////////////// @@ -327,22 +292,24 @@ module rr_arb_tree_lock #( end else begin : gen_other_levels // arbitration: round robin - always_comb begin - if (lock_rr_q) begin - sel = rr_q[NumLevels-1-level]; - end else begin - sel = ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[NumLevels-1-level]; + for (genvar r = 0; r < REP; r++) begin: gen_select + always_comb begin + if (lock_rr_q[r]) begin + sel[r] = rr_q[r][NumLevels-1-level]; + end else begin + sel[r] = ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[0][NumLevels-1-level]; + end end - end - assign index_nodes[Idx0] = (sel) ? - idx_t'({1'b1, index_nodes[Idx1+1][NumLevels-unsigned'(level)-2:0]}) : - idx_t'({1'b0, index_nodes[Idx1][NumLevels-unsigned'(level)-2:0]}); + assign index_nodes[Idx0][r] = (sel[r]) ? + idx_t'({1'b1, index_nodes[Idx1+1][r][NumLevels-unsigned'(level)-2:0]}) : + idx_t'({1'b0, index_nodes[Idx1][r][NumLevels-unsigned'(level)-2:0]}); + end - assign req_nodes[Idx0] = (sel) ? req_nodes[Idx1+1] : req_nodes[Idx1]; - assign data_nodes[Idx0] = (sel) ? data_nodes[Idx1+1] : data_nodes[Idx1]; - assign gnt_nodes[Idx1] = gnt_nodes[Idx0] & ~sel; - assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel; + assign req_nodes[Idx0] = (sel[0]) ? req_nodes[Idx1+1] : req_nodes[Idx1]; + assign data_nodes[Idx0] = (sel[0]) ? data_nodes[Idx1+1] : data_nodes[Idx1]; + assign gnt_nodes[Idx1] = gnt_nodes[Idx0] & ~sel[0]; + assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel[0]; end ////////////////////////////////////////////////////////////// end @@ -354,8 +321,8 @@ module rr_arb_tree_lock #( initial begin : p_assert assert(NumIn) else $error(1, "Input must be at least one element wide."); - assert(!(LockIn && ExtPrio)) - else $error(1,"Cannot use LockIn feature together with external ExtPrio."); + assert(!(ExtPrio && (REP > 1))) + else $error(1, "There is no reason to use REP > 1 with ExtPrio!"); end hot_one : assert property( @@ -379,7 +346,7 @@ module rr_arb_tree_lock #( else $error (1, "Req out implies req in."); lock2 : assert property( - @(posedge clk_i) disable iff (!rst_ni) lock_rr_q |-> idx_o == $past(idx_o)) + @(posedge clk_i) disable iff (!rst_ni) lock_rr_q[0] |-> idx_o == $past(idx_o)) else $error (1, "Lock means idx_o does not change."); `endif `endif diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index b398e3d1..11236a38 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -74,7 +74,7 @@ module time_DMR_end # ( output logic ready_o, // Signal for working with upstream Lockable RR Arbiter - output logic lock_o, + output logic [REP-1:0] lock_o, // Downstream connection output DataType data_o, @@ -270,7 +270,7 @@ module time_DMR_end # ( `VOTEXX(REP, lock_v, lock_d); `VOTEXX(REP, counter_v, counter_d); - assign lock_o = lock_d[0]; + assign lock_o = lock_d; // Default state for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/time_TMR_end.sv index e6c01311..89ea70e2 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/time_TMR_end.sv @@ -75,7 +75,7 @@ module time_TMR_end # ( input logic ready_i, // Signal for working with Lockable RR Arbiter - output logic lock_o, + output logic [REP-1:0] lock_o, // Flag for External Error Counter output logic fault_detected_o @@ -459,7 +459,7 @@ module time_TMR_end # ( `VOTEXX(REP, lock_v, lock_d); `VOTEXX(REP, counter_v, counter_d); - assign lock_o = lock_d[0]; + assign lock_o = lock_d; // Default state for (genvar r = 0; r < REP; r++) begin: gen_lock_default_state diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 19f2b677..7dd16301 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -8,6 +8,8 @@ module tb_time_dmr_retry_lock #( parameter int IDSize = 9, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1, // TB Parameters parameter int unsigned TESTS = 10000, @@ -178,13 +180,14 @@ module tb_time_dmr_retry_lock #( tmr_stacked_t out_tmr_stack; // Backpropagating lock signal - logic lock; + logic [REP-1:0] lock; // Round-Robin arbiter to decide which result to use rr_arb_tree_lock #( - .NumIn ( NumOpgroups ), - .DataType ( rr_stacked_t ), - .AxiVldRdy ( 1'b1 ) + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ), + .InternalRedundancy ( InternalRedundancy ) ) i_arbiter ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index e21ff5ff..0f14c1d9 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -9,6 +9,8 @@ module tb_time_tmr_lock #( parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, parameter bit EarlyValidEnable = 0, parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1, // TB Parameters parameter int unsigned TESTS = 10000, @@ -134,13 +136,14 @@ module tb_time_tmr_lock #( tmr_stacked_t out_tmr_stack; // Backpropagating lock signal - logic lock; + logic [REP-1:0] lock; // Round-Robin arbiter to decide which result to use rr_arb_tree_lock #( - .NumIn ( NumOpgroups ), - .DataType ( rr_stacked_t ), - .AxiVldRdy ( 1'b1 ) + .NumIn ( NumOpgroups ), + .DataType ( rr_stacked_t ), + .AxiVldRdy ( 1'b1 ), + .InternalRedundancy ( InternalRedundancy ) ) i_arbiter ( .clk_i(clk), .rst_ni(rst_n), From 68e741c2628db25825d0545de7f13fc0366e3854 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Fri, 26 Jul 2024 12:19:51 +0200 Subject: [PATCH 17/21] Added option to make replication with only handshake injection. --- rtl/time_redundancy/time_DMR_end.sv | 2 +- rtl/time_redundancy/time_DMR_start.sv | 102 ++++++++++ rtl/time_redundancy/time_TMR_start.sv | 261 +++++++++++++++++--------- run_tests.sh | 16 +- test/tb_time.svh | 2 +- test/tb_time_dmr_retry.sv | 2 + test/tb_time_dmr_retry_lock.sv | 2 + test/tb_time_tmr.sv | 2 + test/tb_time_tmr_lock.sv | 2 + 9 files changed, 296 insertions(+), 95 deletions(-) diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index 11236a38..6bdf8840 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -165,7 +165,7 @@ module time_DMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // State machine to figure out handshake - typedef enum logic [0:0] {BASE, WAIT_FOR_READY} state_t; + typedef enum logic [1:0] {BASE, WAIT_FOR_READY} state_t; state_t [REP-1:0] state_b, state_v, state_d, state_q; logic [REP-1:0] valid_internal_v, lock_internal_v; diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 701d5cd5..5a80af4c 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -52,6 +52,10 @@ module time_DMR_start # ( // As an estimate you can use log2(longest_pipeline) + 2. // Needs to match with time_TMR_end! parameter int unsigned IDSize = 1, + // If you want Ready to be set as early as possible, and store elements + // internally in redundant mode. Increases area but potentially stops + // upstream stalls. + parameter bit EarlyReadyEnable = 1, // Set to 1 if the id_i port should be used parameter bit UseExternalId = 0, // Determines if the internal state machines should @@ -81,6 +85,8 @@ module time_DMR_start # ( output logic valid_o, input logic ready_i ); + + if (EarlyReadyEnable) begin : gen_store_fsm // Redundant Output signals logic [REP-1:0][IDSize-1:0] next_id_ov; logic [REP-1:0] ready_ov; @@ -186,4 +192,100 @@ module time_DMR_start # ( `VOTEX1(REP, ready_ov, ready_o); `VOTEX1(REP, valid_ov, valid_o); + end else begin: gen_no_store_fsm + // Redundant Output signals + logic [REP-1:0][IDSize-1:0] next_id_ov; + logic [REP-1:0] ready_ov; + + // State machine TLDR + // - Wait for valid and count number of ready cycles after valid is sent + + // Next State Combinatorial Logic + typedef enum logic [1:0] {SEND, SEND_AND_CONSUME, SEND_NO_INCREMENT} state_t; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; + + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin: gen_next_state_comb + // Default to staying in same state + state_v[r] = state_q[r]; + id_v[r] = id_q[r]; + + if (UseExternalId == 1) begin + next_id_ov[r] = id_i; + end else begin + `INCREMENT_WITH_PARITY(id_q[r], next_id_ov[r]); + end + + case (state_q[r]) + SEND: + if (valid_i) begin + id_v[r] = next_id_ov[r]; + + if (ready_i) begin + if (enable_i) begin + state_v[r] = SEND_AND_CONSUME; + end else begin + state_v[r] = SEND; + end + end else begin + state_v[r] = SEND_NO_INCREMENT; + end + end + SEND_NO_INCREMENT: + if (ready_i) begin + if (enable_i) begin + state_v[r] = SEND_AND_CONSUME; + end else begin + state_v[r] = SEND; + end + end + SEND_AND_CONSUME: + if (ready_i) begin + state_v[r] = SEND; + end + endcase + end + end + + // State Voting Logic + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + + // Generate default cases + for (genvar r = 0; r < REP; r++) begin: gen_default_state + assign state_b[r] = SEND; + assign id_b[r] = 0; + end + + // State Storage + `FF(state_q, state_d, state_b); + `FF(id_q, id_d, id_b); + + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin : gen_output_comb + case (state_q[r]) + SEND: begin + ready_ov[r] = ~enable_i & ready_i; + end + SEND_NO_INCREMENT: begin + ready_ov[r] = ~enable_i & ready_i; + end + SEND_AND_CONSUME: begin + ready_ov[r] = enable_i & ready_i; + end + endcase + end + end + + // Output Voting Logic + assign data_o = data_i; + assign valid_o = valid_i; + assign id_o = id_d[0]; + + `VOTEX1(REP, next_id_ov, next_id_o); + `VOTEX1(REP, ready_ov, ready_o); + + end endmodule diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/time_TMR_start.sv index 5d9ba107..47f7caf0 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/time_TMR_start.sv @@ -36,6 +36,10 @@ module time_TMR_start # ( // As an estimate you can use log2(longest_pipeline) + 1. // Needs to match with time_TMR_end! parameter int unsigned IDSize = 1, + // If you want Ready to be set as early as possible, and store elements + // internally in redundant mode. Increases area but potentially stops + // upstream stalls. + parameter bit EarlyReadyEnable = 1, // Determines if the internal state machines should // be parallely redundant, meaning errors inside this module // can also not cause errors in the output @@ -59,110 +63,195 @@ module time_TMR_start # ( output logic valid_o, input logic ready_i ); - // Redundant Output signals - logic [REP-1:0] ready_ov; - logic [REP-1:0] valid_ov; - - // State machine TLDR - // - counting the state from 0 to 2 if the handshake is good - // - counting the ID up whenever the state goes back to 0 - - // Next State Combinatorial Logic - typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE_ONE, REPLICATE_TWO} state_t; - state_t [REP-1:0] state_b, state_v, state_d, state_q; - DataType [REP-1:0] data_b, data_v, data_d, data_q; - logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; - - for (genvar r = 0; r < REP; r++) begin: gen_next_state - always_comb begin : gen_next_state_comb - // Default to staying in same state - state_v[r] = state_q[r]; - data_v[r] = data_q[r]; - id_v[r] = id_q[r]; - - case (state_q[r]) - STORE_AND_SEND: - if (valid_i) begin - data_v[r] = data_i; - id_v[r] = id_q[r] + 1; + if (EarlyReadyEnable) begin: gen_store_fsm + + // Redundant Output signals + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; + + // State machine TLDR + // - counting the state from 0 to 2 if the handshake is good + // - counting the ID up whenever the state goes back to 0 + // - stay in state 0 whenever there is no redundancy + + typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE_ONE, REPLICATE_TWO} state_t; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + DataType [REP-1:0] data_b, data_v, data_d, data_q; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; + + // Next State Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin : gen_next_state_comb + // Default to staying in same state + state_v[r] = state_q[r]; + data_v[r] = data_q[r]; + id_v[r] = id_q[r]; + + case (state_q[r]) + STORE_AND_SEND: + if (valid_i) begin + data_v[r] = data_i; + + if (ready_i) begin + if (enable_i) begin + state_v[r] = REPLICATE_ONE; + end else begin + state_v[r] = STORE_AND_SEND; + end + end else begin + state_v[r] = SEND; + end + end + SEND: if (ready_i) begin if (enable_i) begin - state_v[r] = REPLICATE_ONE; + state_v[r] = REPLICATE_ONE; // Reset seqeuence end else begin state_v[r] = STORE_AND_SEND; end - end else begin - state_v[r] = SEND; end - end - SEND: - if (ready_i) begin - if (enable_i) begin - state_v[r] = REPLICATE_ONE; // Reset seqeuence - end else begin + REPLICATE_ONE: + if (ready_i) begin + state_v[r] = REPLICATE_TWO; // Reset seqeuence + end + REPLICATE_TWO: begin + if (ready_i) begin state_v[r] = STORE_AND_SEND; + id_v[r] = id_q[r] + 1; end end - REPLICATE_ONE: - if (ready_i) begin - state_v[r] = REPLICATE_TWO; // Reset seqeuence + endcase + end + end + + // State Voting Logic + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + `VOTEXX(REP, data_v, data_d); + + // Generate default cases + for (genvar r = 0; r < REP; r++) begin: gen_default_state + assign state_b[r] = STORE_AND_SEND; + assign data_b[r] = 0; + assign id_b[r] = 0; + end + + // State Storage + `FF(state_q, state_d, state_b); + `FF(data_q, data_d, data_b); + `FF(id_q, id_d, id_b); + + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb + case (state_q[r]) + STORE_AND_SEND: begin + valid_ov[r] = valid_i; + ready_ov[r] = 1; + end + SEND: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + end + REPLICATE_ONE: begin + valid_ov[r] = '1; + ready_ov[r] = '0; end - REPLICATE_TWO: begin - if (ready_i) begin - state_v[r] = STORE_AND_SEND; + REPLICATE_TWO: begin + valid_ov[r] = '1; + ready_ov[r] = '0; end - end - endcase + endcase + end end - end - // State Voting Logic - `VOTEXX(REP, state_v, state_d); - `VOTEXX(REP, id_v, id_d); - `VOTEXX(REP, data_v, data_d); + // Output Voting Logic + assign data_o = data_d[0]; + assign id_o = id_q[0]; - // Generate default cases - for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_b[r] = STORE_AND_SEND; - assign data_b[r] = 0; - assign id_b[r] = 0; - end + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); - // State Storage - `FF(state_q, state_d, state_b); - `FF(data_q, data_d, data_b); - `FF(id_q, id_d, id_b); - - // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin: gen_output - always_comb begin: gen_output_comb - case (state_q[r]) - STORE_AND_SEND: begin - valid_ov[r] = valid_i; - ready_ov[r] = 1; - end - SEND: begin - valid_ov[r] = '1; - ready_ov[r] = '0; - end - REPLICATE_ONE: begin - valid_ov[r] = '1; - ready_ov[r] = '0; - end - REPLICATE_TWO: begin - valid_ov[r] = '1; - ready_ov[r] = '0; - end - endcase + end else begin: gen_no_store_fsm + + // Redundant Output signals + logic [REP-1:0] ready_ov; + + // State machine TLDR + // - counting the state from 0 to 2 if the handshake is good + // - counting the ID up whenever the state goes back to 0 + // - stay in state 2 whenever there is no redundancy + + typedef enum logic [1:0] {SEND_ONE, SEND_TWO, SEND_AND_CONSUME} state_t; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; + + // Next State Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_next_state + always_comb begin : gen_next_state_comb + // Default to staying in same state + state_v[r] = state_q[r]; + id_v[r] = id_q[r]; + + case (state_q[r]) + SEND_ONE: + if (valid_i & ready_i) begin + if (enable_i) begin + state_v[r] = SEND_TWO; + end else begin + state_v[r] = SEND_ONE; + end + end + SEND_TWO: + if (ready_i) begin + state_v[r] = SEND_AND_CONSUME; + end + SEND_AND_CONSUME: + if (ready_i) begin + id_v[r] = id_q[r] + 1; + state_v[r] = SEND_ONE; + end + endcase + end end - end - // Output Voting Logic - assign data_o = data_d[0]; - assign id_o = id_d[0]; + // State Voting Logic + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + + // Generate default cases + for (genvar r = 0; r < REP; r++) begin: gen_default_state + assign state_b[r] = SEND_ONE; + assign id_b[r] = 0; + end - `VOTEX1(REP, ready_ov, ready_o); - `VOTEX1(REP, valid_ov, valid_o); + // State Storage + `FF(state_q, state_d, state_b); + `FF(id_q, id_d, id_b); + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin: gen_output_comb + case (state_q[r]) + SEND_ONE: begin + ready_ov[r] = ~enable_i & ready_i; + end + SEND_TWO: begin + ready_ov[r] = '0; + end + SEND_AND_CONSUME: begin + ready_ov[r] = ready_i & enable_i; + end + endcase + end + end + + // Output Voting Logic + assign data_o = data_i; + assign valid_o = valid_i; + assign id_o = id_q[0]; + + `VOTEX1(REP, ready_ov, ready_o); + end endmodule diff --git a/run_tests.sh b/run_tests.sh index 3e39f2f0..412cf825 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -57,14 +57,16 @@ call_vsim tb_retry_inorder for redundancy in 0 1; do call_vsim tb_redundancy_controller -GInternalRedundancy=$redundancy - for early_valid in 0 1; do - call_vsim tb_time_tmr -GEarlyValidEnable=$early_valid -GInternalRedundancy=$redundancy - call_vsim tb_time_tmr_lock -GEarlyValidEnable=$early_valid -GInternalRedundancy=$redundancy - done + for early_ready in 0 1; do + for early_valid in 0 1; do + call_vsim tb_time_tmr -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_time_tmr_lock -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + done - call_vsim tb_time_dmr -GInternalRedundancy=$redundancy - call_vsim tb_time_dmr_retry -GInternalRedundancy=$redundancy - call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr_retry -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + done done for num in 1 4 7; do diff --git a/test/tb_time.svh b/test/tb_time.svh index 2043343f..2ca93212 100644 --- a/test/tb_time.svh +++ b/test/tb_time.svh @@ -58,7 +58,7 @@ task input_handshake_end(); valid_in = 1'b1; # (AQUISITION_DELAY - APPLICATION_DELAY); - while (!ready_in) begin + while (!ready_in & rst_n) begin @(posedge clk); # AQUISITION_DELAY; end diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv index bbd2ff05..e112e415 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_time_dmr_retry.sv @@ -3,6 +3,7 @@ module tb_time_dmr_retry #( parameter int IDSize = 4, parameter int LockTimeout = 4 * 12, parameter bit InternalRedundancy = 0, + parameter bit EarlyReadyEnable = 0, // TB Parameters parameter int unsigned TESTS = 10000, @@ -65,6 +66,7 @@ module tb_time_dmr_retry #( time_DMR_start #( .DataType(tagged_data_t), .IDSize(IDSize), + .EarlyReadyEnable(EarlyReadyEnable), .UseExternalId(1), .InternalRedundancy(InternalRedundancy) ) dut_DMR_start ( diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index 7dd16301..e4be95ac 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -7,6 +7,7 @@ module tb_time_dmr_retry_lock #( parameter int OpgroupWidth = $clog2(NumOpgroups), parameter int IDSize = 9, parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, + parameter bit EarlyReadyEnable = 0, parameter bit InternalRedundancy = 0, // Do not modify localparam int REP = InternalRedundancy ? 3 : 1, @@ -103,6 +104,7 @@ module tb_time_dmr_retry_lock #( time_DMR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), + .EarlyReadyEnable(EarlyReadyEnable), .UseExternalId(1), .InternalRedundancy(InternalRedundancy) ) i_time_DMR_start ( diff --git a/test/tb_time_tmr.sv b/test/tb_time_tmr.sv index c7c3d390..8ff44d2c 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_time_tmr.sv @@ -3,6 +3,7 @@ module tb_time_tmr #( parameter IDSize = 4, parameter int LockTimeout = 4 * 12, parameter bit EarlyValidEnable = 0, + parameter bit EarlyReadyEnable = 0, parameter bit InternalRedundancy = 0, // TB Parameters @@ -33,6 +34,7 @@ module tb_time_tmr #( time_TMR_start #( .DataType(data_t), .IDSize(IDSize), + .EarlyReadyEnable(EarlyReadyEnable), .InternalRedundancy(InternalRedundancy) ) dut_start ( .clk_i(clk), diff --git a/test/tb_time_tmr_lock.sv b/test/tb_time_tmr_lock.sv index 0f14c1d9..53e57d24 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_time_tmr_lock.sv @@ -8,6 +8,7 @@ module tb_time_tmr_lock #( parameter int IDSize = 5 + $clog2(12), parameter [NumOpgroups-1:0][7:0] OpgroupNumRegs = {8'd4, 8'd3, 8'd3}, parameter bit EarlyValidEnable = 0, + parameter bit EarlyReadyEnable = 0, parameter bit InternalRedundancy = 0, // Do not modify localparam int REP = InternalRedundancy ? 3 : 1, @@ -63,6 +64,7 @@ module tb_time_tmr_lock #( time_TMR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), + .EarlyReadyEnable(EarlyReadyEnable), .InternalRedundancy(InternalRedundancy) ) i_time_TMR_start ( .clk_i(clk), From d25946d33b3a2605ba80c9292f33a0b6170d73e3 Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 3 Sep 2024 18:05:18 +0200 Subject: [PATCH 18/21] Fixed valid bug for DMR methods, converted connection to interface. --- Bender.yml | 5 +- rtl/time_redundancy/retry_end.sv | 6 + rtl/time_redundancy/retry_inorder_end.sv | 6 + rtl/time_redundancy/retry_inorder_start.sv | 19 +- rtl/time_redundancy/retry_start.sv | 21 ++- rtl/time_redundancy/time_DMR_end.sv | 34 ++-- rtl/time_redundancy/time_DMR_interface.sv | 29 +++ rtl/time_redundancy/time_DMR_start.sv | 210 +++++++++++---------- run_tests.sh | 5 +- src_files.yml | 5 +- test/tb_time.svh | 8 +- test/tb_time_dmr.sv | 11 +- test/tb_time_dmr_retry.sv | 21 ++- test/tb_time_dmr_retry_lock.sv | 34 ++-- 14 files changed, 245 insertions(+), 169 deletions(-) create mode 100644 rtl/time_redundancy/time_DMR_interface.sv diff --git a/Bender.yml b/Bender.yml index b5b253a2..a03fe549 100644 --- a/Bender.yml +++ b/Bender.yml @@ -27,6 +27,7 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_cor.sv - rtl/time_redundancy/retry_interface.sv - rtl/time_redundancy/rr_arb_tree_lock.sv + - rtl/time_redundancy/time_DMR_interface.sv - rtl/TMR_voter.sv - rtl/TMR_voter_fail.sv - rtl/TMR_word_voter.sv @@ -40,10 +41,10 @@ sources: - rtl/time_redundancy/retry_inorder_start.sv - rtl/time_redundancy/retry_inorder_end.sv - rtl/time_redundancy/retry_start.sv - - rtl/time_redundancy/time_TMR_end.sv - - rtl/time_redundancy/time_TMR_start.sv - rtl/time_redundancy/time_DMR_end.sv - rtl/time_redundancy/time_DMR_start.sv + - rtl/time_redundancy/time_TMR_end.sv + - rtl/time_redundancy/time_TMR_start.sv - target: any(deprecated, axi_ecc, hci_ecc, pulp_ecc, test) files: diff --git a/rtl/time_redundancy/retry_end.sv b/rtl/time_redundancy/retry_end.sv index 0bcaac89..67a76cc7 100644 --- a/rtl/time_redundancy/retry_end.sv +++ b/rtl/time_redundancy/retry_end.sv @@ -19,6 +19,12 @@ module retry_end # ( parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1. + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with retry_start! parameter IDSize = 1 ) ( input logic clk_i, diff --git a/rtl/time_redundancy/retry_inorder_end.sv b/rtl/time_redundancy/retry_inorder_end.sv index 72f432ff..ccc4d8c4 100644 --- a/rtl/time_redundancy/retry_inorder_end.sv +++ b/rtl/time_redundancy/retry_inorder_end.sv @@ -16,6 +16,12 @@ module retry_inorder_end # ( parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1. + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with retry_inorder_start! parameter int IDSize = 1 ) ( input logic clk_i, diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index fe822c58..9a0b7283 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -14,14 +14,15 @@ `include "common_cells/registers.svh" -`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ -begin \ - output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ - output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ -end module retry_inorder_start # ( parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1. + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with retry_inorder_end! parameter int IDSize = 1 ) ( input logic clk_i, @@ -76,7 +77,7 @@ module retry_inorder_start # ( always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin - `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); + counter_id_d = counter_id_q + 1; end else begin counter_id_d = counter_id_q; end @@ -87,14 +88,14 @@ module retry_inorder_start # ( ////////////////////////////////////////////////////////////////////// // General Element storage - logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; + logic [2 ** IDSize -1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; always_comb begin: gen_failed_state // Keep data as is as abase data_storage_d = data_storage_q; if ((failed_valid_q | valid_i) & ready_i) begin - data_storage_d[counter_id_q[IDSize-2:0]] = data_o; + data_storage_d[counter_id_q] = data_o; end end @@ -102,7 +103,7 @@ module retry_inorder_start # ( always_comb begin: gen_output if (failed_valid_q & ready_i) begin - data_o = data_storage_q[failed_id_q[IDSize-2:0]]; + data_o = data_storage_q[failed_id_q]; end else begin data_o = data_i; end diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index 6f7edab7..1fe99508 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -18,14 +18,15 @@ `include "common_cells/registers.svh" -`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ -begin \ - output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ - output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ -end module retry_start # ( parameter type DataType = logic, + // The size of the ID to use as an auxilliary signal + // For an in-order process, this can be set to 1. + // For an out of order process, it needs to be big enough so that the out-of-orderness can never + // rearange the elements with the same id next to each other + // As an estimate you can use log2(longest_pipeline) + 1 + // Needs to match with retry_end! parameter IDSize = 1 ) ( input logic clk_i, @@ -71,13 +72,13 @@ module retry_start # ( assign retry.ready = ready_i | ~failed_valid_q; ////////////////////////////////////////////////////////////////////// - // ID Counter with parity bit + // ID Counter logic [IDSize-1:0] counter_id_d, counter_id_q; always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin - `INCREMENT_WITH_PARITY(counter_id_q, counter_id_d); + counter_id_d = counter_id_q + 1; end else begin counter_id_d = counter_id_q; end @@ -88,14 +89,14 @@ module retry_start # ( ////////////////////////////////////////////////////////////////////// // General Element storage - logic [2 ** (IDSize-1)-1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; + logic [2 ** IDSize -1:0][$bits(DataType)-1:0] data_storage_d, data_storage_q; always_comb begin: gen_failed_state // Keep data as is as abase data_storage_d = data_storage_q; if ((failed_valid_q | valid_i) & ready_i) begin - data_storage_d[counter_id_q[IDSize-2:0]] = data_o; + data_storage_d[counter_id_q] = data_o; end end @@ -103,7 +104,7 @@ module retry_start # ( always_comb begin: gen_output if (failed_valid_q & ready_i) begin - data_o = data_storage_q[failed_id_q[IDSize-2:0]]; + data_o = data_storage_q[failed_id_q]; end else begin data_o = data_i; end diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/time_DMR_end.sv index 6bdf8840..8b24ad15 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/time_DMR_end.sv @@ -19,7 +19,7 @@ // to recalculate in hardware, or invoke some recalculation or error on the software side. // // In order to propperly function: -// - next_id_o of time_DMR_start needs to be directly connected to next_id_i of time_DMR_end. +// - time_DMR_interface of time_DMR_start needs to be connected to time_DMR_interface of time_DMR_end. // - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake // and arrive at id_i of time_DMR_end. // - All operation pairs in contact with each other have a unique ID. @@ -46,7 +46,7 @@ module time_DMR_end # ( // multiply by the respective number of cycles parameter int unsigned LockTimeout = 4, // The size of the ID to use as an auxilliary signal - // For an in-order process, this can be set to 1 + // For an in-order process, this can be set to 2. // For an out of order process, it needs to be big enough so that the out-of-orderness can never // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 @@ -64,8 +64,8 @@ module time_DMR_end # ( input logic rst_ni, input logic enable_i, - // Direct connection to corresponding time_DMR_start module - input logic [IDSize-1:0] next_id_i, + // Direct connection + time_DMR_interface.reciever dmr_interface, // Upstream connection input DataType data_i, @@ -78,7 +78,7 @@ module time_DMR_end # ( // Downstream connection output DataType data_o, - output logic [IDSize-1:0] id_o, + output logic [IDSize-2:0] id_o, output logic needs_retry_o, output logic valid_o, input logic ready_i, @@ -121,10 +121,10 @@ module time_DMR_end # ( // If disabled just send out input if (!enable_i) begin data_o = data_i; - id_o = id_i; + id_o = id_i[IDSize-2:0]; end else begin data_o = data_q; - id_o = id_q; + id_o = id_q[IDSize-2:0]; end if (data_i == data_q) data_same_in = '1; @@ -220,7 +220,7 @@ module time_DMR_end # ( endcase case (state_q[r]) - BASE: lock_internal_v[r] = !ready_i | !full_same[0]; + BASE: lock_internal_v[r] = !valid_i | !full_same[0]; WAIT_FOR_READY: lock_internal_v[r] = !ready_i; endcase @@ -285,8 +285,12 @@ module time_DMR_end # ( /////////////////////////////////////////////////////////////////////////////////////////////////// // Output deduplication based on ID and ID Faults - logic id_fault_q; - assign id_fault_q = ^id_q; + // Split ID signal into parts + logic id_q_fault; + logic [IDSize-2:0] id_q_noparity; + + assign id_q_fault = ^id_q; + assign id_q_noparity = id_q[IDSize-2:0]; logic [REP-1:0][2 ** (IDSize-1)-1:0] recently_seen_b, recently_seen_v, recently_seen_d, recently_seen_q; @@ -294,10 +298,12 @@ module time_DMR_end # ( always_comb begin: gen_deduplication_next_state_comb recently_seen_v[r] = recently_seen_q[r]; - recently_seen_v[r][next_id_i[IDSize-2:0]] = 0; + if (dmr_interface.sent[r]) begin + recently_seen_v[r][dmr_interface.id[r][IDSize-2:0]] = 0; + end - if (valid_internal_v[r] & ready_i & !id_fault_q) begin - recently_seen_v[r][id_q[IDSize-2:0]] = 1; + if (valid_internal_v[r] & ready_i & !id_q_fault) begin + recently_seen_v[r][id_q_noparity] = 1; end end end @@ -316,7 +322,7 @@ module time_DMR_end # ( for (genvar r = 0; r < REP; r++) begin: gen_deduplication_output always_comb begin: gen_deduplication_output_comb if (enable_i) begin - if (id_fault_q | recently_seen_q[r][id_q[IDSize-2:0]] & valid_internal_v[r]) begin + if (id_q_fault | recently_seen_q[r][id_q_noparity] & valid_internal_v[r]) begin valid_ov[r] = 0; end else begin valid_ov[r] = valid_internal_v[r]; diff --git a/rtl/time_redundancy/time_DMR_interface.sv b/rtl/time_redundancy/time_DMR_interface.sv new file mode 100644 index 00000000..8ed71dfa --- /dev/null +++ b/rtl/time_redundancy/time_DMR_interface.sv @@ -0,0 +1,29 @@ +// Author: Maurus Item , ETH Zurich +// Date: 09.09.2024 +// Description: Interface in between time_DMR modules to transmit which elements are valid + +interface time_DMR_interface #( + parameter IDSize = 0, + // Determines if the internal state machines should + // be parallely redundant, meaning errors inside this module + // can also not cause errors in the output + // The external output is never protected! + parameter bit InternalRedundancy = 0, + // Do not modify + localparam int REP = InternalRedundancy ? 3 : 1 +) ( + /* No ports on this interface */ +); + logic [REP-1:0][IDSize-1:0] id; + logic [REP-1:0] sent; + + modport sender ( + input id, + input sent + ); + + modport reciever ( + output id, + output sent + ); +endinterface \ No newline at end of file diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/time_DMR_start.sv index 5a80af4c..636af545 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/time_DMR_start.sv @@ -19,7 +19,7 @@ // to recalculate in hardware, or invoke some recalculation or error on the software side. // // In order to propperly function: -// - next_id_o of time_DMR_start needs to be directly connected to next_id_i of time_DMR_end. +// - time_DMR_interface of time_DMR_start needs to be connected to time_DMR_interface of time_DMR_end. // - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake // and arrive at id_i of time_DMR_end. // - All operation pairs in contact with each other have a unique ID. @@ -35,17 +35,11 @@ `include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" -`define INCREMENT_WITH_PARITY(input_signal, output_signal) \ -begin \ - output_signal[$bits(input_signal)-2:0] = input_signal[$bits(input_signal)-2:0] + 1; \ - output_signal[$bits(input_signal)-1] = ^output_signal[$bits(input_signal)-2:0]; \ -end - module time_DMR_start # ( // The data type you want to send through / replicate parameter type DataType = logic, // The size of the ID to use as an auxilliary signal - // For an in-order process, this can be set to 1 + // For an in-order process, this can be set to 2. // For an out of order process, it needs to be big enough so that the // out-of-orderness can never rearange the elements with the same id // next to each other and needs an extra bit for error detection. @@ -56,7 +50,8 @@ module time_DMR_start # ( // internally in redundant mode. Increases area but potentially stops // upstream stalls. parameter bit EarlyReadyEnable = 1, - // Set to 1 if the id_i port should be used + // Set to 1 if the id_i port should be used. Since the internal ID representation + // has a parity bit but id_i does not, the size will be IDSize-1!. parameter bit UseExternalId = 0, // Determines if the internal state machines should // be parallely redundant, meaning errors inside this module @@ -71,11 +66,11 @@ module time_DMR_start # ( input logic enable_i, // Direct connection - output logic [IDSize-1:0] next_id_o, + time_DMR_interface.sender dmr_interface, // Upstream connection input DataType data_i, - input logic [IDSize-1:0] id_i, + input logic [IDSize-2:0] id_i, input logic valid_i, output logic ready_o, @@ -86,115 +81,127 @@ module time_DMR_start # ( input logic ready_i ); - if (EarlyReadyEnable) begin : gen_store_fsm - // Redundant Output signals + // ID Generation Logic logic [REP-1:0][IDSize-1:0] next_id_ov; - logic [REP-1:0] ready_ov; - logic [REP-1:0] valid_ov; + logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; - // State machine TLDR - // - counting the state from 0 to 2 if the handshake is good - // - counting the ID up whenever the state goes back to 0 + for (genvar r = 0; r < REP; r++) begin: gen_id + if (UseExternalId == 1) begin + // Add a parity bit + assign next_id_ov[r][IDSize-2:0] = id_i; + assign next_id_ov[r][IDSize-1] = ^id_i; + end else begin + // Increment and add parity bit + assign next_id_ov[r][IDSize-2:0] = id_q[r][IDSize-2:0] + 1; + assign next_id_ov[r][IDSize-1] = ^next_id_ov[r][IDSize-2:0]; + end - // Next State Combinatorial Logic - typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE} state_t; - state_t [REP-1:0] state_b, state_v, state_d, state_q; - DataType [REP-1:0] data_b, data_v, data_d, data_q; - logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; + assign dmr_interface.id[r] = next_id_ov[r]; + end - for (genvar r = 0; r < REP; r++) begin: gen_next_state - always_comb begin: gen_next_state_comb - // Default to staying in same state - state_v[r] = state_q[r]; - data_v[r] = data_q[r]; - id_v[r] = id_q[r]; - - if (UseExternalId == 1) begin - next_id_ov[r] = id_i; - end else begin - `INCREMENT_WITH_PARITY(id_q[r], next_id_ov[r]); - end + if (EarlyReadyEnable) begin : gen_store_fsm + // Redundant Output signals + logic [REP-1:0] ready_ov; + logic [REP-1:0] valid_ov; + + // State machine TLDR + // - counting the state from 0 to 2 if the handshake is good + // - counting the ID up whenever the state goes back to 0 - case (state_q[r]) - STORE_AND_SEND: - if (valid_i) begin - data_v[r] = data_i; - id_v[r] = next_id_ov[r]; + // Next State Combinatorial Logic + typedef enum logic [1:0] {STORE_AND_SEND, SEND, REPLICATE} state_t; + state_t [REP-1:0] state_b, state_v, state_d, state_q; + DataType [REP-1:0] data_b, data_v, data_d, data_q; + for (genvar r = 0; r < REP; r++) begin: gen_next_state + + always_comb begin: gen_next_state_comb + // Default to staying in same state + state_v[r] = state_q[r]; + data_v[r] = data_q[r]; + id_v[r] = id_q[r]; + + case (state_q[r]) + STORE_AND_SEND: + if (valid_i) begin + data_v[r] = data_i; + id_v[r] = next_id_ov[r]; + + if (ready_i) begin + if (enable_i) begin + state_v[r] = REPLICATE; + end else begin + state_v[r] = STORE_AND_SEND; + end + end else begin + state_v[r] = SEND; + end + end + SEND: if (ready_i) begin if (enable_i) begin - state_v[r] = REPLICATE; + state_v[r] = REPLICATE; // Reset seqeuence end else begin state_v[r] = STORE_AND_SEND; end - end else begin - state_v[r] = SEND; end - end - SEND: - if (ready_i) begin - if (enable_i) begin - state_v[r] = REPLICATE; // Reset seqeuence - end else begin + REPLICATE: + if (ready_i) begin state_v[r] = STORE_AND_SEND; end - end - REPLICATE: - if (ready_i) begin - state_v[r] = STORE_AND_SEND; - end - endcase + endcase + end end - end - // State Voting Logic - `VOTEXX(REP, state_v, state_d); - `VOTEXX(REP, id_v, id_d); - `VOTEXX(REP, data_v, data_d); + // State Voting Logic + `VOTEXX(REP, state_v, state_d); + `VOTEXX(REP, id_v, id_d); + `VOTEXX(REP, data_v, data_d); - // Generate default cases - for (genvar r = 0; r < REP; r++) begin: gen_default_state - assign state_b[r] = STORE_AND_SEND; - assign data_b[r] = 0; - assign id_b[r] = 0; - end + // Generate default cases + for (genvar r = 0; r < REP; r++) begin: gen_default_state + assign state_b[r] = STORE_AND_SEND; + assign data_b[r] = 0; + assign id_b[r] = 0; + end + + // State Storage + `FF(state_q, state_d, state_b); + `FF(data_q, data_d, data_b); + `FF(id_q, id_d, id_b); - // State Storage - `FF(state_q, state_d, state_b); - `FF(data_q, data_d, data_b); - `FF(id_q, id_d, id_b); - - // Output Combinatorial Logic - for (genvar r = 0; r < REP; r++) begin: gen_output - always_comb begin : gen_output_comb - case (state_q[r]) - STORE_AND_SEND: begin - valid_ov[r] = valid_i; - ready_ov[r] = 1; - end - SEND: begin - valid_ov[r] = '1; - ready_ov[r] = '0; - end - REPLICATE: begin - valid_ov[r] = '1; - ready_ov[r] = '0; - end - endcase + // Output Combinatorial Logic + for (genvar r = 0; r < REP; r++) begin: gen_output + always_comb begin : gen_output_comb + case (state_q[r]) + STORE_AND_SEND: begin + valid_ov[r] = valid_i; + ready_ov[r] = 1; + dmr_interface.sent[r] = 1; + end + SEND: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + dmr_interface.sent[r] = 0; + end + REPLICATE: begin + valid_ov[r] = '1; + ready_ov[r] = '0; + dmr_interface.sent[r] = 1; + end + endcase + end end - end - // Output Voting Logic - assign data_o = data_d[0]; - assign id_o = id_d[0]; + // Output Voting Logic + assign data_o = data_d[0]; + assign id_o = id_d[0]; - `VOTEX1(REP, next_id_ov, next_id_o); - `VOTEX1(REP, ready_ov, ready_o); - `VOTEX1(REP, valid_ov, valid_o); + `VOTEX1(REP, ready_ov, ready_o); + `VOTEX1(REP, valid_ov, valid_o); end else begin: gen_no_store_fsm // Redundant Output signals - logic [REP-1:0][IDSize-1:0] next_id_ov; logic [REP-1:0] ready_ov; // State machine TLDR @@ -203,7 +210,6 @@ module time_DMR_start # ( // Next State Combinatorial Logic typedef enum logic [1:0] {SEND, SEND_AND_CONSUME, SEND_NO_INCREMENT} state_t; state_t [REP-1:0] state_b, state_v, state_d, state_q; - logic [REP-1:0][IDSize-1:0] id_b, id_v, id_d, id_q; for (genvar r = 0; r < REP; r++) begin: gen_next_state always_comb begin: gen_next_state_comb @@ -211,12 +217,6 @@ module time_DMR_start # ( state_v[r] = state_q[r]; id_v[r] = id_q[r]; - if (UseExternalId == 1) begin - next_id_ov[r] = id_i; - end else begin - `INCREMENT_WITH_PARITY(id_q[r], next_id_ov[r]); - end - case (state_q[r]) SEND: if (valid_i) begin @@ -268,12 +268,15 @@ module time_DMR_start # ( case (state_q[r]) SEND: begin ready_ov[r] = ~enable_i & ready_i; + dmr_interface.sent[r] = valid_i & enable_i; end SEND_NO_INCREMENT: begin ready_ov[r] = ~enable_i & ready_i; + dmr_interface.sent[r] = 0; end SEND_AND_CONSUME: begin ready_ov[r] = enable_i & ready_i; + dmr_interface.sent[r] = 0; end endcase end @@ -284,7 +287,6 @@ module time_DMR_start # ( assign valid_o = valid_i; assign id_o = id_d[0]; - `VOTEX1(REP, next_id_ov, next_id_o); `VOTEX1(REP, ready_ov, ready_o); end diff --git a/run_tests.sh b/run_tests.sh index 412cf825..0b47a925 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -64,9 +64,10 @@ for redundancy in 0 1; do done call_vsim tb_time_dmr -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy - call_vsim tb_time_dmr_retry -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy - call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy done + + call_vsim tb_time_dmr_retry -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy done for num in 1 4 7; do diff --git a/src_files.yml b/src_files.yml index d1baa79d..453ca29e 100644 --- a/src_files.yml +++ b/src_files.yml @@ -11,6 +11,7 @@ redundancy_cells: lowrisc_ecc/prim_secded_72_64_enc.sv, rtl/time_redundancy/retry_interface.sv rtl/time_redundancy/rr_arb_tree_lock.sv + rtl/time_redundancy/time_DMR_interface.sv rtl/TMR_voter.sv, rtl/TMR_voter_fail.sv, rtl/TMR_word_voter, @@ -35,10 +36,10 @@ redundancy_cells: rtl/time_redundancy/retry_inorder_start.sv rtl/time_redundancy/retry_inorder_end.sv rtl/time_redundancy/retry_start.sv - rtl/time_redundancy/time_TMR_end.sv - rtl/time_redundancy/time_TMR_start.sv rtl/time_redundancy/time_DMR_end.sv rtl/time_redundancy/time_DMR_start.sv + rtl/time_redundancy/time_TMR_end.sv + rtl/time_redundancy/time_TMR_start.sv rtl/TMR_voter_detect.sv, # Level 2 rtl/bitwise_TMR_voter.sv, diff --git a/test/tb_time.svh b/test/tb_time.svh index 2ca93212..d6d2e035 100644 --- a/test/tb_time.svh +++ b/test/tb_time.svh @@ -63,10 +63,10 @@ task input_handshake_end(); # AQUISITION_DELAY; end @(posedge clk); + in_hs_count = in_hs_count + 1; + # APPLICATION_DELAY; valid_in = 1'b0; // This might get overwritten by next handshake - - in_hs_count = in_hs_count + 1; endtask task output_handshake_start(); @@ -94,10 +94,10 @@ endtask task output_handshake_end(); @(posedge clk); + out_hs_count = out_hs_count + 1; + # APPLICATION_DELAY; ready_out = 1'b0; // This might get overwritten by next handshake - - out_hs_count = out_hs_count + 1; endtask //////////////////////////////////////////////////////////////////////////////////7 diff --git a/test/tb_time_dmr.sv b/test/tb_time_dmr.sv index b1027ff4..ec8f4c75 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_time_dmr.sv @@ -25,6 +25,12 @@ module tb_time_dmr #( logic ready_redundant, ready_fault, ready_redundant_faulty; logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty, id_next; + // Forward connection + time_DMR_interface #( + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) + ) dmr_interface (); + // DUT Instances time_DMR_start #( .DataType(data_t), @@ -36,7 +42,7 @@ module tb_time_dmr #( .rst_ni(rst_n), .enable_i(enable), - .next_id_o(id_next), + .dmr_interface(dmr_interface), // Upstream connection .data_i(data_in), @@ -61,8 +67,7 @@ module tb_time_dmr #( .rst_ni(rst_n), .enable_i(enable), - .next_id_i(id_next), - + .dmr_interface(dmr_interface), // Upstream connection .data_i(data_redundant_faulty), diff --git a/test/tb_time_dmr_retry.sv b/test/tb_time_dmr_retry.sv index e112e415..cd9c7f2f 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_time_dmr_retry.sv @@ -32,16 +32,25 @@ module tb_time_dmr_retry #( tagged_data_t data_in, data_detectable, data_redundant, data_fault, data_redundant_faulty, data_detected, data_out; logic valid_detectable, valid_redundant, valid_fault, valid_redundant_faulty, valid_detected; logic ready_detectable, ready_redundant, ready_fault, ready_redundant_faulty, ready_detected; - logic [IDSize-1:0] id_detectable, id_redundant, id_fault, id_redundant_faulty, id_detected, next_id; + logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty; + logic [IDSize-2:0] id_detectable, id_detected; logic needs_retry_detected; + // Forward connection + time_DMR_interface #( + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) + ) dmr_connection (); + // Feedback connection - retry_interface #(.IDSize(IDSize)) retry_connection (); + retry_interface #( + .IDSize(IDSize-1) + ) retry_connection (); // DUT Instances retry_start #( .DataType(tagged_data_t), - .IDSize(IDSize) + .IDSize(IDSize-1) ) dut_retry_start ( .clk_i(clk), .rst_ni(rst_n), @@ -74,7 +83,7 @@ module tb_time_dmr_retry #( .rst_ni(rst_n), .enable_i(enable), - .next_id_o(next_id), + .dmr_interface(dmr_connection), // Upstream connection .data_i(data_detectable), @@ -100,7 +109,7 @@ module tb_time_dmr_retry #( .rst_ni(rst_n), .enable_i(enable), - .next_id_i(next_id), + .dmr_interface(dmr_connection), // Upstream connection .data_i(data_redundant_faulty), @@ -122,7 +131,7 @@ module tb_time_dmr_retry #( // DUT Instances retry_end #( .DataType(tagged_data_t), - .IDSize(IDSize) + .IDSize(IDSize-1) ) dut_retry_end ( .clk_i(clk), .rst_ni(rst_n), diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_time_dmr_retry_lock.sv index e4be95ac..ac5d73b8 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_time_dmr_retry_lock.sv @@ -27,7 +27,8 @@ module tb_time_dmr_retry_lock #( typedef logic [7:0] data_t; typedef logic [OpgroupWidth-1:0] operation_t; - typedef logic [IDSize-1:0] id_t; + typedef logic [IDSize-1:0] id_parity_t; + typedef logic [IDSize-2:0] id_t; typedef logic [7:0] tag_t; typedef struct packed { @@ -43,7 +44,7 @@ module tb_time_dmr_retry_lock #( // Typedef for stacked signal in TMR typedef struct packed { - id_t id; + id_parity_t id; tagged_data_t data; } rr_stacked_t; @@ -55,16 +56,23 @@ module tb_time_dmr_retry_lock #( tagged_data_t data_fault; logic valid_fault; logic ready_fault; - id_t id_fault; + id_parity_t id_fault; // Signals for after TMR tmr_stacked_t in_tmr_stack_redundant; logic in_valid_redundant, in_ready_redundant; - id_t in_id_redundant; + id_parity_t in_id_redundant; + + // Forward connection + time_DMR_interface #( + .IDSize(IDSize), + .InternalRedundancy(InternalRedundancy) + ) dmr_connection (); // Feedback connection - id_t next_id; - retry_interface #(.IDSize(IDSize)) retry_connection (); + retry_interface #( + .IDSize(IDSize-1) + ) retry_connection (); // Connection between retry and DMR tmr_stacked_t data_retry2dmr; @@ -80,7 +88,7 @@ module tb_time_dmr_retry_lock #( // DUT Instances retry_start #( .DataType(tmr_stacked_t), - .IDSize(IDSize) + .IDSize(IDSize-1) ) i_retry_start ( .clk_i(clk), .rst_ni(rst_n), @@ -112,7 +120,7 @@ module tb_time_dmr_retry_lock #( .rst_ni(rst_n), .enable_i(enable), - .next_id_o(next_id), + .dmr_interface(dmr_connection), // Upstream connection .data_i(data_retry2dmr), @@ -142,7 +150,7 @@ module tb_time_dmr_retry_lock #( tagged_data_t [0:NUM_REGS] pipe_data; logic [0:NUM_REGS] pipe_valid; logic [0:NUM_REGS] pipe_ready; - id_t [0:NUM_REGS] pipe_id; + id_parity_t [0:NUM_REGS] pipe_id; // Upstream Connection // Error Injection @@ -167,7 +175,7 @@ module tb_time_dmr_retry_lock #( assign reg_ena = (pipe_ready[i] & pipe_valid[i]); // | reg_ena_i[i]; // Generate the pipeline registers within the stages, use enable-registers `FFLARN(pipe_data[i+1], pipe_data[i], reg_ena, tagged_data_t'('0), clk, rst_n) - `FFLARN( pipe_id[i+1], pipe_id[i], reg_ena, id_t'('0), clk, rst_n) + `FFLARN( pipe_id[i+1], pipe_id[i], reg_ena, id_parity_t'('0), clk, rst_n) end // Downstream connection @@ -219,7 +227,7 @@ module tb_time_dmr_retry_lock #( // Connection between retry and DMR tmr_stacked_t data_dmr2retry; - logic [IDSize-1:0] id_dmr2retry; + id_t id_dmr2retry; logic needs_retry_dmr2retry; logic valid_dmr2retry; logic ready_dmr2retry; @@ -234,7 +242,7 @@ module tb_time_dmr_retry_lock #( .rst_ni(rst_n), .enable_i(enable), - .next_id_i(next_id), + .dmr_interface(dmr_connection), // Upstream connection .data_i(out_tmr_stack), @@ -258,7 +266,7 @@ module tb_time_dmr_retry_lock #( retry_end #( .DataType(tmr_stacked_t), - .IDSize(IDSize) + .IDSize(IDSize-1) ) i_retry_end ( .clk_i(clk), .rst_ni(rst_n), From e564453051e40949ac6d01c095017f867749db2a Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 3 Sep 2024 18:29:44 +0200 Subject: [PATCH 19/21] Added parameters to retry modules that allow for split storage utilization. --- rtl/time_redundancy/retry_inorder_start.sv | 29 +++++++++++++++++++-- rtl/time_redundancy/retry_start.sv | 30 ++++++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index 9a0b7283..abb0d92c 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -23,7 +23,19 @@ module retry_inorder_start # ( // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 // Needs to match with retry_inorder_end! - parameter int IDSize = 1 + parameter int IDSize = 1, + // Amount of bits from the ID which are defined externally and should not be incremented. + // This allows for seperating the ID spaces into multiple sections, which will behave and overwrite + // each other indefinitely. For example, if you set IDSize=3 and ExternalIDBits=1, then you will get + // two sets of IDs which each cycle through the ID. + // Set 1: 000, 001, 010, 011 + // Set 2: 100, 101, 110, 111 + // You can use this to reduce storage space required if some operations take significantly longer in + // the pipelines than others. E.g. use Set 1 with operations done in 1 cycle, and Set 2 with operations + // that take 10 cycles. Each subset must satisfy the required ID Size of log2(longest_pipeline) + 1, + // excluding the bits used to distinguish the sets. + parameter ExternalIDBits = 0, + localparam NormalIDSize = IDSize - ExternalIDBits ) ( input logic clk_i, input logic rst_ni, @@ -32,6 +44,7 @@ module retry_inorder_start # ( input DataType data_i, input logic valid_i, output logic ready_o, + input logic [ExternalIDBits-1:0] ext_id_bits_i, // Downstream connection output DataType data_o, @@ -77,7 +90,19 @@ module retry_inorder_start # ( always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin - counter_id_d = counter_id_q + 1; + + // The topmost ID bits are not incremented but are controlled externally if + // required to split the storage area into sections. In this case get if fron external + // or take it from the element to retry. + counter_id_d[NormalIDSize-1:0] = counter_id_q[NormalIDSize-1:0] + 1; + if (ExternalIDBits > 0) begin + if (failed_valid_q) begin + counter_id_d[IDSize: NormalIDSize] = failed_id_q[IDSize: NormalIDSize]; + end else begin + counter_id_d[IDSize: NormalIDSize] = ext_id_bits_i; + end + end + end else begin counter_id_d = counter_id_q; end diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index 1fe99508..625cc3c7 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -27,7 +27,19 @@ module retry_start # ( // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 // Needs to match with retry_end! - parameter IDSize = 1 + parameter IDSize = 1, + // Amount of bits from the ID which are defined externally and should not be incremented. + // This allows for seperating the ID spaces into multiple sections, which will behave and overwrite + // each other indefinitely. For example, if you set IDSize=3 and ExternalIDBits=1, then you will get + // two sets of IDs which each cycle through the ID. + // Set 1: 000, 001, 010, 011 + // Set 2: 100, 101, 110, 111 + // You can use this to reduce storage space required if some operations take significantly longer in + // the pipelines than others. E.g. use Set 1 with operations done in 1 cycle, and Set 2 with operations + // that take 10 cycles. Each subset must satisfy the required ID Size of log2(longest_pipeline) + 1, + // excluding the bits used to distinguish the sets. + parameter ExternalIDBits = 0, + localparam NormalIDSize = IDSize - ExternalIDBits ) ( input logic clk_i, input logic rst_ni, @@ -36,6 +48,7 @@ module retry_start # ( input DataType data_i, input logic valid_i, output logic ready_o, + input logic [ExternalIDBits-1:0] ext_id_bits_i, // Downstream connection output DataType data_o, @@ -46,6 +59,7 @@ module retry_start # ( // Retry Connection retry_interface.start retry ); + ////////////////////////////////////////////////////////////////////// // Register to store failed id for one cycle @@ -78,7 +92,19 @@ module retry_start # ( always_comb begin: gen_id_counter if ((failed_valid_q | valid_i) & ready_i) begin - counter_id_d = counter_id_q + 1; + + // The topmost ID bits are not incremented but are controlled externally if + // required to split the storage area into sections. In this case get if fron external + // or take it from the element to retry. + counter_id_d[NormalIDSize-1:0] = counter_id_q[NormalIDSize-1:0] + 1; + if (ExternalIDBits > 0) begin + if (failed_valid_q) begin + counter_id_d[IDSize: NormalIDSize] = failed_id_q[IDSize: NormalIDSize]; + end else begin + counter_id_d[IDSize: NormalIDSize] = ext_id_bits_i; + end + end + end else begin counter_id_d = counter_id_q; end From c594afeb384bc36547ca89dd17c41887cf15beac Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Tue, 3 Sep 2024 21:10:34 +0200 Subject: [PATCH 20/21] Renamed modules to use Triple / Double Temporal Redundancy as their names. --- Bender.yml | 27 +++++++++--------- .../{time_DMR_end.sv => DTR_end.sv} | 18 ++++++------ ...time_DMR_interface.sv => DTR_interface.sv} | 4 +-- .../{time_DMR_start.sv => DTR_start.sv} | 28 +++++++++---------- .../{time_TMR_end.sv => TTR_end.sv} | 12 ++++---- .../{time_TMR_start.sv => TTR_start.sv} | 10 +++---- run_tests.sh | 10 +++---- src_files.yml | 10 +++---- test/{tb_time_dmr.sv => tb_dtr.sv} | 14 +++++----- .../{tb_time_dmr_retry.sv => tb_dtr_retry.sv} | 14 +++++----- ...dmr_retry_lock.sv => tb_dtr_retry_lock.sv} | 18 ++++++------ test/{tb_time_tmr.sv => tb_ttr.sv} | 6 ++-- test/{tb_time_tmr_lock.sv => tb_ttr_lock.sv} | 10 +++---- 13 files changed, 90 insertions(+), 91 deletions(-) rename rtl/time_redundancy/{time_DMR_end.sv => DTR_end.sv} (96%) rename rtl/time_redundancy/{time_DMR_interface.sv => DTR_interface.sv} (84%) rename rtl/time_redundancy/{time_DMR_start.sv => DTR_start.sv} (92%) rename rtl/time_redundancy/{time_TMR_end.sv => TTR_end.sv} (98%) rename rtl/time_redundancy/{time_TMR_start.sv => TTR_start.sv} (96%) rename test/{tb_time_dmr.sv => tb_dtr.sv} (97%) rename test/{tb_time_dmr_retry.sv => tb_dtr_retry.sv} (98%) rename test/{tb_time_dmr_retry_lock.sv => tb_dtr_retry_lock.sv} (98%) rename test/{tb_time_tmr.sv => tb_ttr.sv} (99%) rename test/{tb_time_tmr_lock.sv => tb_ttr_lock.sv} (99%) diff --git a/Bender.yml b/Bender.yml index a03fe549..9ada50fe 100644 --- a/Bender.yml +++ b/Bender.yml @@ -25,9 +25,9 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_enc.sv - rtl/hsiao_ecc/hsiao_ecc_dec.sv - rtl/hsiao_ecc/hsiao_ecc_cor.sv + - rtl/time_redundancy/DTR_interface.sv - rtl/time_redundancy/retry_interface.sv - rtl/time_redundancy/rr_arb_tree_lock.sv - - rtl/time_redundancy/time_DMR_interface.sv - rtl/TMR_voter.sv - rtl/TMR_voter_fail.sv - rtl/TMR_word_voter.sv @@ -36,15 +36,15 @@ sources: - rtl/ecc_wrap/ecc_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv + - rtl/time_redundancy/DTR_end.sv + - rtl/time_redundancy/DTR_start.sv - rtl/time_redundancy/redundancy_controller.sv - rtl/time_redundancy/retry_end.sv - rtl/time_redundancy/retry_inorder_start.sv - rtl/time_redundancy/retry_inorder_end.sv - rtl/time_redundancy/retry_start.sv - - rtl/time_redundancy/time_DMR_end.sv - - rtl/time_redundancy/time_DMR_start.sv - - rtl/time_redundancy/time_TMR_end.sv - - rtl/time_redundancy/time_TMR_start.sv + - rtl/time_redundancy/TTR_end.sv + - rtl/time_redundancy/TTR_start.sv - target: any(deprecated, axi_ecc, hci_ecc, pulp_ecc, test) files: @@ -101,6 +101,11 @@ sources: - rtl/pulpissimo_tcls/TCLS_unit.sv - target: test files: + - test/tb_bitwise_tmr_voter.sv + - test/tb_bitwise_tmr_voter_fail.sv + - test/tb_dtr.sv + - test/tb_dtr_retry.sv + - test/tb_dtr_retry_lock.sv - test/tb_ecc_scrubber.sv - test/tb_ecc_secded.sv - test/tb_ecc_sram.sv @@ -108,19 +113,13 @@ sources: - test/tb_tmr_voter_fail.sv - test/tb_tmr_voter_detect.sv - test/tb_tmr_word_voter.sv - - test/tb_bitwise_tmr_voter.sv - - test/tb_bitwise_tmr_voter_fail.sv - - test/tb_voter_macros.sv - - test/tb_time_tmr.sv - - test/tb_time_dmr.sv + - test/tb_ttr.sv + - test/tb_ttr_lock.sv - test/tb_redundancy_controller.sv - test/tb_retry.sv - test/tb_retry_inorder.sv - - test/tb_time_dmr_retry.sv - - test/tb_time_tmr_lock.sv - - test/tb_time_dmr_retry_lock.sv - test/tb_rr_arb_tree_lock.sv - + - test/tb_voter_macros.sv vendor_package: - name: lowrisc_opentitan diff --git a/rtl/time_redundancy/time_DMR_end.sv b/rtl/time_redundancy/DTR_end.sv similarity index 96% rename from rtl/time_redundancy/time_DMR_end.sv rename to rtl/time_redundancy/DTR_end.sv index 8b24ad15..d50d7023 100644 --- a/rtl/time_redundancy/time_DMR_end.sv +++ b/rtl/time_redundancy/DTR_end.sv @@ -1,6 +1,6 @@ // Author: Maurus Item , ETH Zurich // Date: 25.04.2024 -// Description: time_DMR is a pair of modules that can be used to +// Description: DTR is a pair of modules that can be used to // detect faults in any (pipelined) combinatorial process. This is // done by repeating each input twice and comparing the outputs. // @@ -19,9 +19,9 @@ // to recalculate in hardware, or invoke some recalculation or error on the software side. // // In order to propperly function: -// - time_DMR_interface of time_DMR_start needs to be connected to time_DMR_interface of time_DMR_end. -// - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake -// and arrive at id_i of time_DMR_end. +// - DTR_interface of DTR_start needs to be connected to DTR_interface of DTR_end. +// - id_o of DTR_start needs to be passed paralelly to the combinatorial logic, using the same handshake +// and arrive at id_i of DTR_end. // - All operation pairs in contact with each other have a unique ID. // - The module can only be enabled / disabled when the combinatorially process holds no valid data. // @@ -35,7 +35,7 @@ `include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" -module time_DMR_end # ( +module DTR_end # ( // The data type you want to send through / replicate parameter type DataType = logic, // How long until the time_TMR module should abort listening for further copies @@ -50,7 +50,7 @@ module time_DMR_end # ( // For an out of order process, it needs to be big enough so that the out-of-orderness can never // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 - // Needs to match with time_TMR_start! + // Needs to match with DTR_start! parameter int unsigned IDSize = 1, // Determines if the internal state machines should // be parallely redundant, meaning errors inside this module @@ -65,7 +65,7 @@ module time_DMR_end # ( input logic enable_i, // Direct connection - time_DMR_interface.reciever dmr_interface, + DTR_interface.reciever dtr_interface, // Upstream connection input DataType data_i, @@ -298,8 +298,8 @@ module time_DMR_end # ( always_comb begin: gen_deduplication_next_state_comb recently_seen_v[r] = recently_seen_q[r]; - if (dmr_interface.sent[r]) begin - recently_seen_v[r][dmr_interface.id[r][IDSize-2:0]] = 0; + if (dtr_interface.sent[r]) begin + recently_seen_v[r][dtr_interface.id[r][IDSize-2:0]] = 0; end if (valid_internal_v[r] & ready_i & !id_q_fault) begin diff --git a/rtl/time_redundancy/time_DMR_interface.sv b/rtl/time_redundancy/DTR_interface.sv similarity index 84% rename from rtl/time_redundancy/time_DMR_interface.sv rename to rtl/time_redundancy/DTR_interface.sv index 8ed71dfa..6f7ea614 100644 --- a/rtl/time_redundancy/time_DMR_interface.sv +++ b/rtl/time_redundancy/DTR_interface.sv @@ -1,8 +1,8 @@ // Author: Maurus Item , ETH Zurich // Date: 09.09.2024 -// Description: Interface in between time_DMR modules to transmit which elements are valid +// Description: Interface in between DTR modules to transmit which elements are valid -interface time_DMR_interface #( +interface DTR_interface #( parameter IDSize = 0, // Determines if the internal state machines should // be parallely redundant, meaning errors inside this module diff --git a/rtl/time_redundancy/time_DMR_start.sv b/rtl/time_redundancy/DTR_start.sv similarity index 92% rename from rtl/time_redundancy/time_DMR_start.sv rename to rtl/time_redundancy/DTR_start.sv index 636af545..0d70998b 100644 --- a/rtl/time_redundancy/time_DMR_start.sv +++ b/rtl/time_redundancy/DTR_start.sv @@ -1,6 +1,6 @@ // Author: Maurus Item , ETH Zurich // Date: 25.04.2024 -// Description: time_DMR is a pair of modules that can be used to +// Description: DTR is a pair of modules that can be used to // detect faults in any (pipelined) combinatorial process. This is // done by repeating each input twice and comparing the outputs. // @@ -19,9 +19,9 @@ // to recalculate in hardware, or invoke some recalculation or error on the software side. // // In order to propperly function: -// - time_DMR_interface of time_DMR_start needs to be connected to time_DMR_interface of time_DMR_end. -// - id_o of time_DMR_start needs to be passed paralelly to the combinatorial logic, using the same handshake -// and arrive at id_i of time_DMR_end. +// - DTR_interface of DTR_start needs to be connected to DTR_interface of DTR_end. +// - id_o of DTR_start needs to be passed paralelly to the combinatorial logic, using the same handshake +// and arrive at id_i of DTR_end. // - All operation pairs in contact with each other have a unique ID. // - The module can only be enabled / disabled when the combinatorially process holds no valid data. // @@ -35,7 +35,7 @@ `include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" -module time_DMR_start # ( +module DTR_start # ( // The data type you want to send through / replicate parameter type DataType = logic, // The size of the ID to use as an auxilliary signal @@ -44,7 +44,7 @@ module time_DMR_start # ( // out-of-orderness can never rearange the elements with the same id // next to each other and needs an extra bit for error detection. // As an estimate you can use log2(longest_pipeline) + 2. - // Needs to match with time_TMR_end! + // Needs to match with DTR_end! parameter int unsigned IDSize = 1, // If you want Ready to be set as early as possible, and store elements // internally in redundant mode. Increases area but potentially stops @@ -66,7 +66,7 @@ module time_DMR_start # ( input logic enable_i, // Direct connection - time_DMR_interface.sender dmr_interface, + DTR_interface.sender dtr_interface, // Upstream connection input DataType data_i, @@ -96,7 +96,7 @@ module time_DMR_start # ( assign next_id_ov[r][IDSize-1] = ^next_id_ov[r][IDSize-2:0]; end - assign dmr_interface.id[r] = next_id_ov[r]; + assign dtr_interface.id[r] = next_id_ov[r]; end if (EarlyReadyEnable) begin : gen_store_fsm @@ -177,17 +177,17 @@ module time_DMR_start # ( STORE_AND_SEND: begin valid_ov[r] = valid_i; ready_ov[r] = 1; - dmr_interface.sent[r] = 1; + dtr_interface.sent[r] = 1; end SEND: begin valid_ov[r] = '1; ready_ov[r] = '0; - dmr_interface.sent[r] = 0; + dtr_interface.sent[r] = 0; end REPLICATE: begin valid_ov[r] = '1; ready_ov[r] = '0; - dmr_interface.sent[r] = 1; + dtr_interface.sent[r] = 1; end endcase end @@ -268,15 +268,15 @@ module time_DMR_start # ( case (state_q[r]) SEND: begin ready_ov[r] = ~enable_i & ready_i; - dmr_interface.sent[r] = valid_i & enable_i; + dtr_interface.sent[r] = valid_i & enable_i; end SEND_NO_INCREMENT: begin ready_ov[r] = ~enable_i & ready_i; - dmr_interface.sent[r] = 0; + dtr_interface.sent[r] = 0; end SEND_AND_CONSUME: begin ready_ov[r] = enable_i & ready_i; - dmr_interface.sent[r] = 0; + dtr_interface.sent[r] = 0; end endcase end diff --git a/rtl/time_redundancy/time_TMR_end.sv b/rtl/time_redundancy/TTR_end.sv similarity index 98% rename from rtl/time_redundancy/time_TMR_end.sv rename to rtl/time_redundancy/TTR_end.sv index 89ea70e2..6b90e4f3 100644 --- a/rtl/time_redundancy/time_TMR_end.sv +++ b/rtl/time_redundancy/TTR_end.sv @@ -1,6 +1,6 @@ // Author: Maurus Item , ETH Zurich // Date: 25.04.2024 -// Description: time_TMR is a pair of modules that can be used to error correct +// Description: TTR is a pair of modules that can be used to error correct // any transient fault in a (pipelined) combinatorial process. This is done by // repeating the element three times and comparing the results // @@ -10,8 +10,8 @@ // - A single fault in the accompanying ID. // // In order to propperly function: -// - id_o of time_TMR_start needs to be passed paralelly to the combinatorial logic, -// using the same handshake and arrive at id_i of time_TMR_end. +// - id_o of TTR_start needs to be passed paralelly to the combinatorial logic, +// using the same handshake and arrive at id_i of TTR_end. // - All operation tripplets in contact which each other have a unique ID. // - The module can only be enabled / disabled when the combinatorially process holds no valid data. // @@ -25,10 +25,10 @@ `include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" -module time_TMR_end # ( +module TTR_end # ( // The data type you want to send through / replicate parameter type DataType = logic, - // How long until the time_TMR module should abort listening for further copies + // How long until the TTR module should abort listening for further copies // of some data when they don't show up e.g. handshake failed // For an in-order process you should set this to 4 // For an out-of-order process (with RR Arbitrator) you should set it to 5 @@ -40,7 +40,7 @@ module time_TMR_end # ( // For an out of order process, it needs to be big enough so that the out-of-orderness can never // rearange the elements with the same id next to each other // As an estimate you can use log2(longest_pipeline) + 1 - // Needs to match with time_TMR_start! + // Needs to match with TTR_start! parameter int unsigned IDSize = 1, // This parameter chooses the implementation of the internal state machine. // EarlyValidEnable = 1: diff --git a/rtl/time_redundancy/time_TMR_start.sv b/rtl/time_redundancy/TTR_start.sv similarity index 96% rename from rtl/time_redundancy/time_TMR_start.sv rename to rtl/time_redundancy/TTR_start.sv index 47f7caf0..3da1adc5 100644 --- a/rtl/time_redundancy/time_TMR_start.sv +++ b/rtl/time_redundancy/TTR_start.sv @@ -1,6 +1,6 @@ // Author: Maurus Item , ETH Zurich // Date: 25.04.2024 -// Description: time_TMR is a pair of modules that can be used to error correct +// Description: TTR is a pair of modules that can be used to error correct // any transient fault in a (pipelined) combinatorial process. This is done by // repeating the element three times and comparing the results // @@ -10,8 +10,8 @@ // - A single fault in the accompanying ID. // // In order to propperly function: -// - id_o of time_TMR_start needs to be passed paralelly to the combinatorial logic, -// using the same handshake and arrive at id_i of time_TMR_end. +// - id_o of TTR_start needs to be passed paralelly to the combinatorial logic, +// using the same handshake and arrive at id_i of TTR_end. // - All operation tripplets in contact which each other have a unique ID. // - The module can only be enabled / disabled when the combinatorially process holds no valid data. // @@ -25,7 +25,7 @@ `include "redundancy_cells/voters.svh" `include "common_cells/registers.svh" -module time_TMR_start # ( +module TTR_start # ( // The data type you want to send through / replicate parameter type DataType = logic, // The size of the ID to use as an auxilliary signal @@ -34,7 +34,7 @@ module time_TMR_start # ( // out-of-orderness can never rearange the elements with the same id // next to each other. // As an estimate you can use log2(longest_pipeline) + 1. - // Needs to match with time_TMR_end! + // Needs to match with TTR_end! parameter int unsigned IDSize = 1, // If you want Ready to be set as early as possible, and store elements // internally in redundant mode. Increases area but potentially stops diff --git a/run_tests.sh b/run_tests.sh index 0b47a925..3205c0f5 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -59,15 +59,15 @@ for redundancy in 0 1; do for early_ready in 0 1; do for early_valid in 0 1; do - call_vsim tb_time_tmr -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy - call_vsim tb_time_tmr_lock -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_ttr -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_ttr_lock -GEarlyValidEnable=$early_valid -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy done - call_vsim tb_time_dmr -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_dtr -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy done - call_vsim tb_time_dmr_retry -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy - call_vsim tb_time_dmr_retry_lock -voptargs="+acc" -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_dtr_retry -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy + call_vsim tb_dtr_retry_lock -voptargs="+acc" -GEarlyReadyEnable=$early_ready -GInternalRedundancy=$redundancy done for num in 1 4 7; do diff --git a/src_files.yml b/src_files.yml index 453ca29e..719134ce 100644 --- a/src_files.yml +++ b/src_files.yml @@ -9,9 +9,9 @@ redundancy_cells: lowrisc_ecc/prim_secded_39_32_enc.sv, lowrisc_ecc/prim_secded_72_64_dec.sv, lowrisc_ecc/prim_secded_72_64_enc.sv, + rtl/time_redundancy/DTR_interface.sv rtl/time_redundancy/retry_interface.sv rtl/time_redundancy/rr_arb_tree_lock.sv - rtl/time_redundancy/time_DMR_interface.sv rtl/TMR_voter.sv, rtl/TMR_voter_fail.sv, rtl/TMR_word_voter, @@ -31,15 +31,15 @@ redundancy_cells: rtl/BUS_enc_dec/TCDM_XBAR_bus_ecc_enc.sv, rlt/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv, rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv, + rtl/time_redundancy/DTR_end.sv + rtl/time_redundancy/DTR_start.sv rtl/time_redundancy/redundancy_controller.sv rtl/time_redundancy/retry_end.sv rtl/time_redundancy/retry_inorder_start.sv rtl/time_redundancy/retry_inorder_end.sv rtl/time_redundancy/retry_start.sv - rtl/time_redundancy/time_DMR_end.sv - rtl/time_redundancy/time_DMR_start.sv - rtl/time_redundancy/time_TMR_end.sv - rtl/time_redundancy/time_TMR_start.sv + rtl/time_redundancy/TTR_end.sv + rtl/time_redundancy/TTR_start.sv rtl/TMR_voter_detect.sv, # Level 2 rtl/bitwise_TMR_voter.sv, diff --git a/test/tb_time_dmr.sv b/test/tb_dtr.sv similarity index 97% rename from test/tb_time_dmr.sv rename to test/tb_dtr.sv index ec8f4c75..acbda5b3 100644 --- a/test/tb_time_dmr.sv +++ b/test/tb_dtr.sv @@ -1,4 +1,4 @@ -module tb_time_dmr #( +module tb_dtr #( // DUT Parameters parameter IDSize = 4, parameter int LockTimeout = 4 * 12, @@ -26,13 +26,13 @@ module tb_time_dmr #( logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty, id_next; // Forward connection - time_DMR_interface #( + DTR_interface #( .IDSize(IDSize), .InternalRedundancy(InternalRedundancy) - ) dmr_interface (); + ) dtr_interface (); // DUT Instances - time_DMR_start #( + DTR_start #( .DataType(data_t), .IDSize(IDSize), .UseExternalId(0), @@ -42,7 +42,7 @@ module tb_time_dmr #( .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_interface), + .dtr_interface(dtr_interface), // Upstream connection .data_i(data_in), @@ -57,7 +57,7 @@ module tb_time_dmr #( .ready_i(ready_redundant_faulty) ); - time_DMR_end #( + DTR_end #( .DataType(data_t), .LockTimeout(LockTimeout), .IDSize(IDSize), @@ -67,7 +67,7 @@ module tb_time_dmr #( .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_interface), + .dtr_interface(dtr_interface), // Upstream connection .data_i(data_redundant_faulty), diff --git a/test/tb_time_dmr_retry.sv b/test/tb_dtr_retry.sv similarity index 98% rename from test/tb_time_dmr_retry.sv rename to test/tb_dtr_retry.sv index cd9c7f2f..d08023ca 100644 --- a/test/tb_time_dmr_retry.sv +++ b/test/tb_dtr_retry.sv @@ -1,4 +1,4 @@ -module tb_time_dmr_retry #( +module tb_dtr_retry #( // DUT Parameters parameter int IDSize = 4, parameter int LockTimeout = 4 * 12, @@ -37,10 +37,10 @@ module tb_time_dmr_retry #( logic needs_retry_detected; // Forward connection - time_DMR_interface #( + DTR_interface #( .IDSize(IDSize), .InternalRedundancy(InternalRedundancy) - ) dmr_connection (); + ) dtr_connection (); // Feedback connection retry_interface #( @@ -72,7 +72,7 @@ module tb_time_dmr_retry #( // DUT Instances - time_DMR_start #( + DTR_start #( .DataType(tagged_data_t), .IDSize(IDSize), .EarlyReadyEnable(EarlyReadyEnable), @@ -83,7 +83,7 @@ module tb_time_dmr_retry #( .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_connection), + .dtr_interface(dtr_connection), // Upstream connection .data_i(data_detectable), @@ -99,7 +99,7 @@ module tb_time_dmr_retry #( ); // DUT Instances - time_DMR_end #( + DTR_end #( .DataType(tagged_data_t), .LockTimeout(LockTimeout), .IDSize(IDSize), @@ -109,7 +109,7 @@ module tb_time_dmr_retry #( .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_connection), + .dtr_interface(dtr_connection), // Upstream connection .data_i(data_redundant_faulty), diff --git a/test/tb_time_dmr_retry_lock.sv b/test/tb_dtr_retry_lock.sv similarity index 98% rename from test/tb_time_dmr_retry_lock.sv rename to test/tb_dtr_retry_lock.sv index ac5d73b8..e20451c8 100644 --- a/test/tb_time_dmr_retry_lock.sv +++ b/test/tb_dtr_retry_lock.sv @@ -1,6 +1,6 @@ `include "common_cells/registers.svh" -module tb_time_dmr_retry_lock #( +module tb_dtr_retry_lock #( // DUT Parameters parameter int LockTimeout = 5 * 12, parameter int NumOpgroups = 3, @@ -64,10 +64,10 @@ module tb_time_dmr_retry_lock #( id_parity_t in_id_redundant; // Forward connection - time_DMR_interface #( + DTR_interface #( .IDSize(IDSize), .InternalRedundancy(InternalRedundancy) - ) dmr_connection (); + ) dtr_connection (); // Feedback connection retry_interface #( @@ -109,18 +109,18 @@ module tb_time_dmr_retry_lock #( ); - time_DMR_start #( + DTR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), .EarlyReadyEnable(EarlyReadyEnable), .UseExternalId(1), .InternalRedundancy(InternalRedundancy) - ) i_time_DMR_start ( + ) i_DTR_start ( .clk_i(clk), .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_connection), + .dtr_interface(dtr_connection), // Upstream connection .data_i(data_retry2dmr), @@ -232,17 +232,17 @@ module tb_time_dmr_retry_lock #( logic valid_dmr2retry; logic ready_dmr2retry; - time_DMR_end #( + DTR_end #( .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), .IDSize (IDSize), .InternalRedundancy(InternalRedundancy) - ) i_time_DMR_end ( + ) i_DTR_end ( .clk_i(clk), .rst_ni(rst_n), .enable_i(enable), - .dmr_interface(dmr_connection), + .dtr_interface(dtr_connection), // Upstream connection .data_i(out_tmr_stack), diff --git a/test/tb_time_tmr.sv b/test/tb_ttr.sv similarity index 99% rename from test/tb_time_tmr.sv rename to test/tb_ttr.sv index 8ff44d2c..fc2bc610 100644 --- a/test/tb_time_tmr.sv +++ b/test/tb_ttr.sv @@ -1,4 +1,4 @@ -module tb_time_tmr #( +module tb_ttr #( // DUT Parameters parameter IDSize = 4, parameter int LockTimeout = 4 * 12, @@ -31,7 +31,7 @@ module tb_time_tmr #( logic ready_redundant, ready_fault, ready_redundant_faulty; logic [IDSize-1:0] id_redundant, id_fault, id_redundant_faulty; - time_TMR_start #( + TTR_start #( .DataType(data_t), .IDSize(IDSize), .EarlyReadyEnable(EarlyReadyEnable), @@ -53,7 +53,7 @@ module tb_time_tmr #( .ready_i(ready_redundant_faulty) ); - time_TMR_end #( + TTR_end #( .DataType(data_t), .LockTimeout(LockTimeout), .IDSize(IDSize), diff --git a/test/tb_time_tmr_lock.sv b/test/tb_ttr_lock.sv similarity index 99% rename from test/tb_time_tmr_lock.sv rename to test/tb_ttr_lock.sv index 53e57d24..1362c940 100644 --- a/test/tb_time_tmr_lock.sv +++ b/test/tb_ttr_lock.sv @@ -1,6 +1,6 @@ `include "common_cells/registers.svh" -module tb_time_tmr_lock #( +module tb_ttr_lock #( // DUT Parameters parameter int LockTimeout = 5 * 12, parameter int NumOpgroups = 3, @@ -61,12 +61,12 @@ module tb_time_tmr_lock #( logic in_valid_redundant, in_ready_redundant; id_t in_id_redundant; - time_TMR_start #( + TTR_start #( .DataType(tmr_stacked_t), .IDSize (IDSize), .EarlyReadyEnable(EarlyReadyEnable), .InternalRedundancy(InternalRedundancy) - ) i_time_TMR_start ( + ) i_TTR_start ( .clk_i(clk), .rst_ni(rst_n), .enable_i(enable), @@ -173,13 +173,13 @@ module tb_time_tmr_lock #( assign out_tmr_id = out_rr_stack.id; assign out_tmr_stack.data = out_rr_stack.data; - time_TMR_end #( + TTR_end #( .DataType(tmr_stacked_t), .LockTimeout(LockTimeout), .IDSize (IDSize), .EarlyValidEnable(EarlyValidEnable), .InternalRedundancy(InternalRedundancy) - ) i_time_TMR_end ( + ) i_TTR_end ( .clk_i(clk), .rst_ni(rst_n), .enable_i(enable), From 4df64e39c4e70ff03880cae6b79739c2853f8e7e Mon Sep 17 00:00:00 2001 From: Maurus Item Date: Wed, 27 Nov 2024 09:25:24 +0100 Subject: [PATCH 21/21] Added separate parameter for external id width so that different physical sizes can be accommodated. --- rtl/time_redundancy/retry_inorder_start.sv | 14 +++++++++++--- rtl/time_redundancy/retry_start.sv | 9 ++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/rtl/time_redundancy/retry_inorder_start.sv b/rtl/time_redundancy/retry_inorder_start.sv index abb0d92c..fee88669 100644 --- a/rtl/time_redundancy/retry_inorder_start.sv +++ b/rtl/time_redundancy/retry_inorder_start.sv @@ -35,6 +35,9 @@ module retry_inorder_start # ( // that take 10 cycles. Each subset must satisfy the required ID Size of log2(longest_pipeline) + 1, // excluding the bits used to distinguish the sets. parameter ExternalIDBits = 0, + // Physical width of the ID bits input so that the case of 0 is well defined + // Must be equal or greater than the ExternalIDBits that are actually used + parameter ExternalIDWidth = (ExternalIDBits == 0) ? 1 : ExternalIDBits, localparam NormalIDSize = IDSize - ExternalIDBits ) ( input logic clk_i, @@ -44,7 +47,7 @@ module retry_inorder_start # ( input DataType data_i, input logic valid_i, output logic ready_o, - input logic [ExternalIDBits-1:0] ext_id_bits_i, + input logic [ExternalIDWidth-1:0] ext_id_bits_i, // Downstream connection output DataType data_o, @@ -56,6 +59,11 @@ module retry_inorder_start # ( retry_interface.start retry ); + initial begin + assert (ExternalIDWidth >= ExternalIDBits) + else $fatal("ExternalIDWidth must be at least as large as ExternalIDBits."); + end + ////////////////////////////////////////////////////////////////////// // Register to store failed id for one cycle logic [IDSize-1:0] failed_id_d, failed_id_q; @@ -97,9 +105,9 @@ module retry_inorder_start # ( counter_id_d[NormalIDSize-1:0] = counter_id_q[NormalIDSize-1:0] + 1; if (ExternalIDBits > 0) begin if (failed_valid_q) begin - counter_id_d[IDSize: NormalIDSize] = failed_id_q[IDSize: NormalIDSize]; + counter_id_d[IDSize-1: NormalIDSize] = failed_id_q[IDSize-1: NormalIDSize]; end else begin - counter_id_d[IDSize: NormalIDSize] = ext_id_bits_i; + counter_id_d[IDSize-1: NormalIDSize] = ext_id_bits_i[ExternalIDBits-1 :0]; end end diff --git a/rtl/time_redundancy/retry_start.sv b/rtl/time_redundancy/retry_start.sv index 625cc3c7..f5920f0e 100644 --- a/rtl/time_redundancy/retry_start.sv +++ b/rtl/time_redundancy/retry_start.sv @@ -39,6 +39,9 @@ module retry_start # ( // that take 10 cycles. Each subset must satisfy the required ID Size of log2(longest_pipeline) + 1, // excluding the bits used to distinguish the sets. parameter ExternalIDBits = 0, + // Physical width of the ID bits input so that the case of 0 is well defined + // Must be equal or greater than the ExternalIDBits that are actually used + parameter ExternalIDWidth = (ExternalIDBits == 0) ? 1 : ExternalIDBits, localparam NormalIDSize = IDSize - ExternalIDBits ) ( input logic clk_i, @@ -48,7 +51,7 @@ module retry_start # ( input DataType data_i, input logic valid_i, output logic ready_o, - input logic [ExternalIDBits-1:0] ext_id_bits_i, + input logic [ExternalIDWidth-1:0] ext_id_bits_i, // Downstream connection output DataType data_o, @@ -99,9 +102,9 @@ module retry_start # ( counter_id_d[NormalIDSize-1:0] = counter_id_q[NormalIDSize-1:0] + 1; if (ExternalIDBits > 0) begin if (failed_valid_q) begin - counter_id_d[IDSize: NormalIDSize] = failed_id_q[IDSize: NormalIDSize]; + counter_id_d[IDSize-1: NormalIDSize] = failed_id_q[IDSize-1: NormalIDSize]; end else begin - counter_id_d[IDSize: NormalIDSize] = ext_id_bits_i; + counter_id_d[IDSize-1: NormalIDSize] = ext_id_bits_i[ExternalIDBits-1 :0]; end end