diff --git a/src/axi/rtl/axi_pkg.sv b/src/axi/rtl/axi_pkg.sv index 50d996b4d..5b087b8c9 100644 --- a/src/axi/rtl/axi_pkg.sv +++ b/src/axi/rtl/axi_pkg.sv @@ -14,6 +14,12 @@ // package axi_pkg; + localparam int AXI_DW = 32; + localparam int AXI_AW = 32; + localparam int AXI_UW = 32; + localparam int AXI_IW = 1; + localparam int AXI_BC = AXI_DW/8; + localparam int AXI_BW = $clog2(AXI_BC); localparam AXI_LEN_MAX_VALUE = 256; // 8-bit LEN signal = 256 beats max localparam AXI_LEN_WIDTH = $clog2(AXI_LEN_MAX_VALUE); diff --git a/src/axi/rtl/axi_sub.sv b/src/axi/rtl/axi_sub.sv index c11eec6a1..e4fffa2d7 100644 --- a/src/axi/rtl/axi_sub.sv +++ b/src/axi/rtl/axi_sub.sv @@ -57,6 +57,7 @@ module axi_sub import axi_pkg::*; #( output logic [IW-1:0] id, output logic [DW-1:0] wdata, // Requires: Component dwidth == AXI dwidth output logic [BC-1:0] wstrb, // Requires: Component dwidth == AXI dwidth + output logic [2:0] size, input logic [DW-1:0] rdata, // Requires: Component dwidth == AXI dwidth output logic last, // Asserted with final 'dv' of a burst input logic hld, @@ -80,6 +81,7 @@ module axi_sub import axi_pkg::*; #( logic [AW-1:0] r_addr; // Byte address logic [UW-1:0] r_user; logic [IW-1:0] r_id; + logic [2:0] r_size; logic r_last; // Asserted with final 'dv' of a burst logic r_hld; logic r_err; @@ -93,6 +95,7 @@ module axi_sub import axi_pkg::*; #( logic [IW-1:0] w_id; logic [DW-1:0] w_wdata; // Requires: Component dwidth == AXI dwidth logic [BC-1:0] w_wstrb; // Requires: Component dwidth == AXI dwidth + logic [2:0] w_size; logic w_last; // Asserted with final 'dv' of a burst logic w_hld; logic w_err; @@ -125,6 +128,7 @@ module axi_sub import axi_pkg::*; #( .id (w_id ), .wdata(w_wdata), .wstrb(w_wstrb), + .wsize(w_size ), .last (w_last ), .hld (w_hld ), .err (w_err ) @@ -157,6 +161,7 @@ module axi_sub import axi_pkg::*; #( .addr (r_addr ), .user (r_user ), .id (r_id ), + .size (r_size ), .last (r_last ), .hld (r_hld ), .err (r_err ), @@ -181,6 +186,7 @@ module axi_sub import axi_pkg::*; #( .r_user (r_user ), .r_id (r_id ), .r_last (r_last ), + .r_size (r_size ), .r_hld (r_hld ), .r_err (r_err ), .r_rdata(r_rdata), @@ -192,6 +198,7 @@ module axi_sub import axi_pkg::*; #( .w_id (w_id ), .w_wdata(w_wdata), .w_wstrb(w_wstrb), + .w_size (w_size ), .w_last (w_last ), .w_hld (w_hld ), .w_err (w_err ), @@ -204,6 +211,7 @@ module axi_sub import axi_pkg::*; #( .id (id ), .wdata (wdata ), .wstrb (wstrb ), + .size (size ), .last (last ), .hld (hld ), .rd_err (rd_err ), diff --git a/src/axi/rtl/axi_sub_arb.sv b/src/axi/rtl/axi_sub_arb.sv index 8459ca2e6..a5e8d52c1 100644 --- a/src/axi/rtl/axi_sub_arb.sv +++ b/src/axi/rtl/axi_sub_arb.sv @@ -44,6 +44,7 @@ module axi_sub_arb import axi_pkg::*; #( input logic [AW-1:0] r_addr, // Byte address input logic [UW-1:0] r_user, input logic [IW-1:0] r_id, + input logic [2:0] r_size, input logic r_last, // Asserted with final 'dv' of a burst output logic r_hld, output logic r_err, @@ -57,6 +58,7 @@ module axi_sub_arb import axi_pkg::*; #( input logic [IW-1:0] w_id, input logic [DW-1:0] w_wdata, // Requires: Component dwidth == AXI dwidth input logic [BC-1:0] w_wstrb, // Requires: Component dwidth == AXI dwidth + input logic [2:0] w_size, input logic w_last, // Asserted with final 'dv' of a burst output logic w_hld, output logic w_err, @@ -69,6 +71,7 @@ module axi_sub_arb import axi_pkg::*; #( output logic [IW-1:0] id, output logic [DW-1:0] wdata, // Requires: Component dwidth == AXI dwidth output logic [BC-1:0] wstrb, // Requires: Component dwidth == AXI dwidth + output logic [2:0] size, output logic last, // Asserted with final 'dv' of a burst input logic hld, input logic rd_err, // Asserts with rdata for reads (when C_LAT > 0) @@ -125,6 +128,7 @@ module axi_sub_arb import axi_pkg::*; #( user = r_win ? r_user : w_user; id = r_win ? r_id : w_id ; last = r_win ? r_last : w_last; + size = r_win ? r_size : w_size; r_hld = hld || !r_win; w_hld = hld || r_win; r_err = rd_err; diff --git a/src/axi/rtl/axi_sub_rd.sv b/src/axi/rtl/axi_sub_rd.sv index 566d54527..0c7c6d995 100644 --- a/src/axi/rtl/axi_sub_rd.sv +++ b/src/axi/rtl/axi_sub_rd.sv @@ -61,6 +61,7 @@ module axi_sub_rd import axi_pkg::*; #( output logic [AW-1:0] addr, // Byte address output logic [UW-1:0] user, output logic [IW-1:0] id, + output logic [2:0] size, output logic last, // Asserted with final 'dv' of a burst input logic hld, input logic err, @@ -192,9 +193,10 @@ module axi_sub_rd import axi_pkg::*; #( // Address Calculations // // --------------------------------------- // // Force aligned address to component - always_comb addr = {txn_ctx.addr[AW-1:BW],BW'(0)}; + always_comb addr = {txn_ctx.addr[AW-1:0]};//,BW'(0)}; always_comb user = txn_ctx.user; always_comb id = txn_ctx.id; + always_comb size = txn_ctx.size; always_comb last = txn_cnt == 0; // Use full address to calculate next address (in case of arsize < data width) diff --git a/src/axi/rtl/axi_sub_wr.sv b/src/axi/rtl/axi_sub_wr.sv index dd873ffdb..909a77877 100644 --- a/src/axi/rtl/axi_sub_wr.sv +++ b/src/axi/rtl/axi_sub_wr.sv @@ -59,6 +59,7 @@ module axi_sub_wr import axi_pkg::*; #( output logic [IW-1:0] id, output logic [DW-1:0] wdata, // Requires: Component dwidth == AXI dwidth output logic [BC-1:0] wstrb, // Requires: Component dwidth == AXI dwidth + output logic [2:0] wsize, output logic last, // Asserted with final 'dv' of a burst input logic hld, input logic err @@ -233,7 +234,7 @@ module axi_sub_wr import axi_pkg::*; #( // Address Calculations // // --------------------------------------- // // Force aligned address to component - always_comb addr = {txn_ctx.addr[AW-1:BW],BW'(0)}; + always_comb addr = {txn_ctx.addr[AW-1:0]};//,BW'(0)}; always_comb user = txn_ctx.user; always_comb id = txn_ctx.id; @@ -296,7 +297,7 @@ module axi_sub_wr import axi_pkg::*; #( .OPT_OUTREG (0 ), // .OPT_PASSTHROUGH(0 ), - .DW (DW + BC + 1) + .DW (DW + BC + 1 + 3) ) i_dp_skd ( .i_clk (clk ), .i_reset(rst_n ), @@ -304,11 +305,13 @@ module axi_sub_wr import axi_pkg::*; #( .o_ready(txn_wready ), .i_data ({s_axi_if.wdata, s_axi_if.wstrb, + txn_ctx.size, s_axi_if.wlast}), .o_valid(dv_pre ), .i_ready(!hld ), .o_data ({wdata, wstrb, + wsize, last } ) ); diff --git a/src/axi/rtl/skidbuffer.v b/src/axi/rtl/skidbuffer.v new file mode 100644 index 000000000..ceb8587da --- /dev/null +++ b/src/axi/rtl/skidbuffer.v @@ -0,0 +1,499 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: skidbuffer.v +// {{{ +// Project: WB2AXIPSP: bus bridges and other odds and ends +// +// Purpose: A basic SKID buffer. +// {{{ +// Skid buffers are required for high throughput AXI code, since the AXI +// specification requires that all outputs be registered. This means +// that, if there are any stall conditions calculated, it will take a clock +// cycle before the stall can be propagated up stream. This means that +// the data will need to be buffered for a cycle until the stall signal +// can make it to the output. +// +// Handling that buffer is the purpose of this core. +// +// On one end of this core, you have the i_valid and i_data inputs to +// connect to your bus interface. There's also a registered o_ready +// signal to signal stalls for the bus interface. +// +// The other end of the core has the same basic interface, but it isn't +// registered. This allows you to interact with the bus interfaces +// as though they were combinatorial logic, by interacting with this half +// of the core. +// +// If at any time the incoming !stall signal, i_ready, signals a stall, +// the incoming data is placed into a buffer. Internally, that buffer +// is held in r_data with the r_valid flag used to indicate that valid +// data is within it. +// }}} +// Parameters: +// {{{ +// DW or data width +// In order to make this core generic, the width of the data in the +// skid buffer is parameterized +// +// OPT_LOWPOWER +// Forces both o_data and r_data to zero if the respective *VALID +// signal is also low. While this costs extra logic, it can also +// be used to guarantee that any unused values aren't toggling and +// therefore unnecessarily using power. +// +// This excess toggling can be particularly problematic if the +// bus signals have a high fanout rate, or a long signal path +// across an FPGA. +// +// OPT_OUTREG +// Causes the outputs to be registered +// +// OPT_PASSTHROUGH +// Turns the skid buffer into a passthrough. Used for formal +// verification only. +// }}} +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +// Caliptra Modifications: +// * Revert the default_nettype assignment at file end +// * Convert i_reset from active-high synchronous reset to +// active-low asynchronous reset +// * Enable OPT_INITIAL behavior by default using async reset instead of +// initial block +// +//////////////////////////////////////////////////////////////////////////////// +// }}} +// Copyright (C) 2019-2024, Gisselquist Technology, LLC +// {{{ +// This file is part of the WB2AXIP project. +// +// The WB2AXIP project contains free software and gateware, licensed under the +// Apache License, Version 2.0 (the "License"). You may not use this project, +// or this file, except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// }}} +module skidbuffer #( + // {{{ + parameter [0:0] OPT_LOWPOWER = 0, + parameter [0:0] OPT_OUTREG = 1, + // + parameter [0:0] OPT_PASSTHROUGH = 0, + parameter DW = 8 + // }}} + ) ( + // {{{ + input wire i_clk, i_reset, // Changed to resetn, async + input wire i_valid, + output wire o_ready, + input wire [DW-1:0] i_data, + output wire o_valid, + input wire i_ready, + output reg [DW-1:0] o_data + // }}} + ); + + wire [DW-1:0] w_data; + + generate if (OPT_PASSTHROUGH) + begin : PASSTHROUGH + // {{{ + assign { o_valid, o_ready } = { i_valid, i_ready }; + + always @(*) + if (!i_valid && OPT_LOWPOWER) + o_data = 0; + else + o_data = i_data; + + assign w_data = 0; + + // Keep Verilator happy + // Verilator lint_off UNUSED + // {{{ + wire unused_passthrough; + assign unused_passthrough = &{ 1'b0, i_clk, i_reset }; + // }}} + // Verilator lint_on UNUSED + // }}} + end else begin : LOGIC + // We'll start with skid buffer itself + // {{{ + reg r_valid; + reg [DW-1:0] r_data; + + // r_valid + // {{{ + always @(posedge i_clk or negedge i_reset) + if (!i_reset) + r_valid <= 0; + else if ((i_valid && o_ready) && (o_valid && !i_ready)) + // We have incoming data, but the output is stalled + r_valid <= 1; + else if (i_ready) + r_valid <= 0; + // }}} + + // r_data + // {{{ + always @(posedge i_clk or negedge i_reset) + if (!i_reset) + r_data <= 0; + else if (OPT_LOWPOWER && (!o_valid || i_ready)) + r_data <= 0; + else if ((!OPT_LOWPOWER || !OPT_OUTREG || i_valid) && o_ready) + r_data <= i_data; + + assign w_data = r_data; + // }}} + + // o_ready + // {{{ + assign o_ready = !r_valid; + // }}} + + // + // And then move on to the output port + // + if (!OPT_OUTREG) + begin : NET_OUTPUT + // Outputs are combinatorially determined from inputs + // {{{ + // o_valid + // {{{ + // NOTE: As i_reset is now asynchronous, omit from the equation + assign o_valid = /*i_reset && */(i_valid || r_valid); + // }}} + + // o_data + // {{{ + always @(*) + if (r_valid) + o_data = r_data; + else if (!OPT_LOWPOWER || i_valid) + o_data = i_data; + else + o_data = 0; + // }}} + // }}} + end else begin : REG_OUTPUT + // Register our outputs + // {{{ + // o_valid + // {{{ + reg ro_valid; + + always @(posedge i_clk or negedge i_reset) + if (!i_reset) + ro_valid <= 0; + else if (!o_valid || i_ready) + ro_valid <= (i_valid || r_valid); + + assign o_valid = ro_valid; + // }}} + + // o_data + // {{{ + always @(posedge i_clk or negedge i_reset) + if (!i_reset) + o_data <= 0; + else if (!o_valid || i_ready) + begin + + if (r_valid) + o_data <= r_data; + else if (!OPT_LOWPOWER || i_valid) + o_data <= i_data; + else + o_data <= 0; + end + // }}} + + // }}} + end + // }}} + end endgenerate + + // Keep Verilator happy + // {{{ + // Verilator lint_off UNUSED + wire unused; + assign unused = &{ 1'b0, w_data }; + // Verilator lint_on UNUSED + // }}} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// Formal properties +// {{{ +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +`ifdef FORMAL +`ifdef SKIDBUFFER +`define ASSUME assume +`else +`define ASSUME assert +`endif + + reg f_past_valid; + + initial f_past_valid = 0; + always @(posedge i_clk) + f_past_valid <= 1; + + always @(*) + if (!f_past_valid) + assume(!i_reset); + + //////////////////////////////////////////////////////////////////////// + // + // Incoming stream properties / assumptions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + always @(posedge i_clk) + if (!f_past_valid) + begin + `ASSUME(!i_valid); + end else if ($past(i_valid && !o_ready && i_reset) && i_reset) + `ASSUME(i_valid && $stable(i_data)); + +`ifdef VERIFIC +`define FORMAL_VERIFIC + // Reset properties + property RESET_CLEARS_IVALID; + @(posedge i_clk) !i_reset |=> !i_valid; + endproperty + + property IDATA_HELD_WHEN_NOT_READY; + @(posedge i_clk) disable iff (!i_reset) + i_valid && !o_ready |=> i_valid && $stable(i_data); + endproperty + +`ifdef SKIDBUFFER + assume property (IDATA_HELD_WHEN_NOT_READY); +`else + assert property (IDATA_HELD_WHEN_NOT_READY); +`endif +`endif + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Outgoing stream properties / assumptions + // {{{ + //////////////////////////////////////////////////////////////////////// + // + + generate if (!OPT_PASSTHROUGH) + begin + + always @(posedge i_clk) + if (!f_past_valid) // || $past(!i_reset)) + begin + // Following any reset, valid must be deasserted + assert(!o_valid); + end else if ($past(o_valid && !i_ready && i_reset) && i_reset) + // Following any stall, valid must remain high and + // data must be preserved + assert(o_valid && $stable(o_data)); + + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Other properties + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // + generate if (!OPT_PASSTHROUGH) + begin + // Rule #1: + // If registered, then following any reset we should be + // ready for a new request + // {{{ + always @(posedge i_clk) + if (f_past_valid && $past(OPT_OUTREG && !i_reset)) + assert(o_ready); + // }}} + + // Rule #2: + // All incoming data must either go directly to the + // output port, or into the skid buffer + // {{{ +`ifndef VERIFIC + always @(posedge i_clk) + if (f_past_valid && !$past(!i_reset) && $past(i_valid && o_ready + && (!OPT_OUTREG || o_valid) && !i_ready)) + assert(!o_ready && w_data == $past(i_data)); +`else + assert property (@(posedge i_clk) + disable iff (!i_reset) + (i_valid && o_ready + && (!OPT_OUTREG || o_valid) && !i_ready) + |=> (!o_ready && w_data == $past(i_data))); +`endif + // }}} + + // Rule #3: + // After the last transaction, o_valid should become idle + // {{{ + if (!OPT_OUTREG) + begin + // {{{ + always @(posedge i_clk) + if (f_past_valid && !$past(!i_reset) && i_reset + && $past(i_ready)) + begin + assert(o_valid == i_valid); + assert(!i_valid || (o_data == i_data)); + end + // }}} + end else begin + // {{{ + always @(posedge i_clk) + if (f_past_valid && !$past(!i_reset)) + begin + if ($past(i_valid && o_ready)) + assert(o_valid); + + if ($past(!i_valid && o_ready && i_ready)) + assert(!o_valid); + end + // }}} + end + // }}} + + // Rule #4 + // Same thing, but this time for o_ready + // {{{ + always @(posedge i_clk) + if (f_past_valid && $past(!o_ready && i_ready)) + assert(o_ready); + // }}} + + // If OPT_LOWPOWER is set, o_data and w_data both need to be + // zero any time !o_valid or !r_valid respectively + // {{{ + if (OPT_LOWPOWER) + begin + always @(*) + if ((OPT_OUTREG || i_reset) && !o_valid) + assert(o_data == 0); + + always @(*) + if (o_ready) + assert(w_data == 0); + + end + // }}} + end endgenerate + // }}} + //////////////////////////////////////////////////////////////////////// + // + // Cover checks + // {{{ + //////////////////////////////////////////////////////////////////////// + // + // +`ifdef SKIDBUFFER + generate if (!OPT_PASSTHROUGH) + begin + reg f_changed_data; + + initial f_changed_data = 0; + always @(posedge i_clk) + if (!i_reset) + f_changed_data <= 1; + else if (i_valid && $past(!i_valid || o_ready)) + begin + if (i_data != $past(i_data + 1)) + f_changed_data <= 0; + end else if (!i_valid && i_data != 0) + f_changed_data <= 0; + + +`ifndef VERIFIC + reg [3:0] cvr_steps, cvr_hold; + + always @(posedge i_clk) + if (!i_reset) + begin + cvr_steps <= 0; + cvr_hold <= 0; + end else begin + cvr_steps <= cvr_steps + 1; + cvr_hold <= cvr_hold + 1; + case(cvr_steps) + 0: if (o_valid || i_valid) + cvr_steps <= 0; + 1: if (!i_valid || !i_ready) + cvr_steps <= 0; + 2: if (!i_valid || !i_ready) + cvr_steps <= 0; + 3: if (!i_valid || !i_ready) + cvr_steps <= 0; + 4: if (!i_valid || i_ready) + cvr_steps <= 0; + 5: if (!i_valid || !i_ready) + cvr_steps <= 0; + 6: if (!i_valid || !i_ready) + cvr_steps <= 0; + 7: if (!i_valid || i_ready) + cvr_steps <= 0; + 8: if (!i_valid || i_ready) + cvr_steps <= 0; + 9: if (!i_valid || !i_ready) + cvr_steps <= 0; + 10: if (!i_valid || !i_ready) + cvr_steps <= 0; + 11: if (!i_valid || !i_ready) + cvr_steps <= 0; + 12: begin + cvr_steps <= cvr_steps; + cover(!o_valid && !i_valid && f_changed_data); + if (!o_valid || !i_ready) + cvr_steps <= 0; + else + cvr_hold <= cvr_hold + 1; + end + default: assert(0); + endcase + end + +`else + // Cover test + cover property (@(posedge i_clk) + disable iff (!i_reset) + (!o_valid && !i_valid) + ##1 i_valid && i_ready [*3] + ##1 i_valid && !i_ready + ##1 i_valid && i_ready [*2] + ##1 i_valid && !i_ready [*2] + ##1 i_valid && i_ready [*3] + // Wait for the design to clear + ##1 o_valid && i_ready [*0:5] + ##1 (!o_valid && !i_valid && f_changed_data)); +`endif + end endgenerate +`endif // SKIDBUFFER + // }}} +`endif +// }}} +endmodule +`default_nettype wire \ No newline at end of file diff --git a/src/caliptra_prim/config/compile.yml b/src/caliptra_prim/config/compile.yml index 70fac1887..2dd9a2d76 100644 --- a/src/caliptra_prim/config/compile.yml +++ b/src/caliptra_prim/config/compile.yml @@ -1,6 +1,8 @@ --- provides: [caliptra_prim_pkg] schema_version: 2.4.0 +requires: + - libs targets: rtl: directories: [$COMPILE_ROOT/rtl] @@ -12,6 +14,11 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_cipher_pkg.sv - $COMPILE_ROOT/rtl/caliptra_prim_pkg.sv - $COMPILE_ROOT/rtl/caliptra_prim_sparse_fsm_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_otp_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_ram_1p_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_esc_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_count_pkg.sv tb: directories: [$COMPILE_ROOT/rtl] files: @@ -22,12 +29,18 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_cipher_pkg.sv - $COMPILE_ROOT/rtl/caliptra_prim_pkg.sv - $COMPILE_ROOT/rtl/caliptra_prim_sparse_fsm_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_otp_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_ram_1p_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_esc_pkg.sv + - $COMPILE_ROOT/rtl/caliptra_prim_count_pkg.sv --- provides: [caliptra_prim] schema_version: 2.4.0 requires: - libs - lc_ctrl_pkg + - edn_pkg - caliptra_prim_pkg - caliptra_prim_generic targets: @@ -39,6 +52,14 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_cdc_rand_delay.sv - $COMPILE_ROOT/rtl/caliptra_prim_flop_2sync.sv - $COMPILE_ROOT/rtl/caliptra_prim_lfsr.sv + - $COMPILE_ROOT/rtl/caliptra_prim_double_lfsr.sv + - $COMPILE_ROOT/rtl/caliptra_prim_arbiter_fixed.sv + - $COMPILE_ROOT/rtl/caliptra_prim_arbiter_tree.sv + - $COMPILE_ROOT/rtl/caliptra_prim_edn_req.sv + - $COMPILE_ROOT/rtl/caliptra_prim_present.sv + - $COMPILE_ROOT/rtl/caliptra_prim_lc_sender.sv + - $COMPILE_ROOT/rtl/caliptra_prim_sync_reqack.sv + - $COMPILE_ROOT/rtl/caliptra_prim_sync_reqack_data.sv - $COMPILE_ROOT/rtl/caliptra_prim_mubi4_sync.sv - $COMPILE_ROOT/rtl/caliptra_prim_diff_decode.sv - $COMPILE_ROOT/rtl/caliptra_prim_sec_anchor_buf.sv @@ -57,6 +78,7 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_intr_hw.sv - $COMPILE_ROOT/rtl/caliptra_prim_onehot_check.sv - $COMPILE_ROOT/rtl/caliptra_prim_mubi8_sync.sv + - $COMPILE_ROOT/rtl/caliptra_prim_mubi8_sender.sv - $COMPILE_ROOT/rtl/caliptra_prim_fifo_sync_cnt.sv - $COMPILE_ROOT/rtl/caliptra_prim_buf.sv - $COMPILE_ROOT/rtl/caliptra_prim_lc_sync.sv @@ -71,4 +93,61 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_sum_tree.sv - $COMPILE_ROOT/rtl/caliptra_prim_subreg_ext.sv - $COMPILE_ROOT/rtl/caliptra_prim_edge_detector.sv - tops: [caliptra_prim] + - $COMPILE_ROOT/rtl/caliptra_prim_blanker.sv + - $COMPILE_ROOT/rtl/caliptra_prim_ram_1p_adv.sv + - $COMPILE_ROOT/rtl/caliptra_prim_mubi4_sender.sv + - $COMPILE_ROOT/rtl/caliptra_prim_packer.sv + - $COMPILE_ROOT/rtl/caliptra_prim_subreg_shadow.sv + - $COMPILE_ROOT/rtl/caliptra_prim_esc_receiver.sv + - $COMPILE_ROOT/rtl/caliptra_prim_mubi4_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_clock_mux2.sv + - $COMPILE_ROOT/rtl/caliptra_prim_fifo_async.sv + - $COMPILE_ROOT/rtl/caliptra_prim_fifo_async_simple.sv + tops: [calitpra_prim] +--- +provides: [caliptra_prim_secded] +schema_version: 2.4.0 +requires: + - caliptra_prim_pkg +targets: + rtl: + directories: [$COMPILE_ROOT/rtl] + files: + - $COMPILE_ROOT/rtl/caliptra_prim_secded_22_16_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_22_16_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_28_22_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_28_22_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_39_32_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_39_32_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_64_57_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_64_57_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_72_64_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_72_64_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_22_16_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_22_16_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_39_32_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_39_32_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_72_64_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_72_64_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_76_68_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_hamming_76_68_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_22_16_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_22_16_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_28_22_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_28_22_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_39_32_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_39_32_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_64_57_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_64_57_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_72_64_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_72_64_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_22_16_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_22_16_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_39_32_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_39_32_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_72_64_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_72_64_enc.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_76_68_dec.sv + - $COMPILE_ROOT/rtl/caliptra_prim_secded_inv_hamming_76_68_enc.sv + # - $COMPILE_ROOT/rtl/caliptra_prim_secded_pkg.sv + tops: [caliptra_prim_secded] diff --git a/src/caliptra_prim/rtl/caliptra_prim_arbiter_fixed.sv b/src/caliptra_prim/rtl/caliptra_prim_arbiter_fixed.sv new file mode 100644 index 000000000..5b41cdf87 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_arbiter_fixed.sv @@ -0,0 +1,170 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 fixed priority arbiter module (index 0 has highest prio) +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// +// See also: prim_arbiter_ppc, prim_arbiter_tree + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_arbiter_fixed #( + parameter int N = 8, + parameter int DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + // used for assertions only + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + `CALIPTRA_ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + logic [2**(IdxW+1)-2:0] req_tree; + logic [2**(IdxW+1)-2:0] gnt_tree; + logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree; + logic [2**(IdxW+1)-2:0][DW-1:0] data_tree; + + for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the Pa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == IdxW) begin : gen_leafs + if (offset < N) begin : gen_assign + // forward path + assign req_tree[Pa] = req_i[offset]; + assign idx_tree[Pa] = offset; + assign data_tree[Pa] = data_i[offset]; + // backward (grant) path + assign gnt_o[offset] = gnt_tree[Pa]; + + end else begin : gen_tie_off + // forward path + assign req_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign data_tree[Pa] = '0; + logic unused_sigs; + assign unused_sigs = gnt_tree[Pa]; + end + // this creates the node assignments + end else begin : gen_nodes + // forward path + logic sel; // local helper variable + always_comb begin : p_node + // this always gives priority to the left child + sel = ~req_tree[C0]; + // propagate requests + req_tree[Pa] = req_tree[C0] | req_tree[C1]; + // data and index muxes + idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; + // propagate the grants back to the input + gnt_tree[C0] = gnt_tree[Pa] & ~sel; + gnt_tree[C1] = gnt_tree[Pa] & sel; + end + end + end : gen_level + end : gen_tree + + // the results can be found at the tree root + if (EnDataPort) begin : gen_data_port + assign data_o = data_tree[0]; + end else begin : gen_no_dataport + logic [DW-1:0] unused_data; + assign unused_data = data_tree[0]; + assign data_o = '1; + end + + assign idx_o = idx_tree[0]; + assign valid_o = req_tree[0]; + + // this propagates a grant back to the input + assign gnt_tree[0] = valid_o & ready_i; + end + + //////////////// + // assertions // + //////////////// + + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `CALIPTRA_ASSERT_KNOWN(ValidKnown_A, valid_o) + `CALIPTRA_ASSERT_KNOWN(GrantKnown_A, gnt_o) + `CALIPTRA_ASSERT_KNOWN(IdxKnown_A, idx_o) + + // Make sure no higher prio req is asserted + `CALIPTRA_ASSERT(Priority_A, |req_i |-> req_i[idx_o] && (((N'(1'b1) << idx_o) - 1'b1) & req_i) == '0) + + // we can only grant one requestor at a time + `CALIPTRA_ASSERT(CheckHotOne_A, $onehot0(gnt_o)) + // A grant implies that the sink is ready + `CALIPTRA_ASSERT(GntImpliesReady_A, |gnt_o |-> ready_i) + // A grant implies that the arbiter asserts valid as well + `CALIPTRA_ASSERT(GntImpliesValid_A, |gnt_o |-> valid_o) + // A request and a sink that is ready imply a grant + `CALIPTRA_ASSERT(ReqAndReadyImplyGrant_A, |req_i && ready_i |-> |gnt_o) + // A request and a sink that is ready imply a grant + `CALIPTRA_ASSERT(ReqImpliesValid_A, |req_i |-> valid_o) + // Both conditions above combined and reversed + `CALIPTRA_ASSERT(ReadyAndValidImplyGrant_A, ready_i && valid_o |-> |gnt_o) + // Both conditions above combined and reversed + `CALIPTRA_ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0) + // check index / grant correspond + `CALIPTRA_ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o]) + +if (EnDataPort) begin: gen_data_port_assertion + // data flow + `CALIPTRA_ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) +end + +endmodule : caliptra_prim_arbiter_fixed diff --git a/src/caliptra_prim/rtl/caliptra_prim_arbiter_tree.sv b/src/caliptra_prim/rtl/caliptra_prim_arbiter_tree.sv new file mode 100644 index 000000000..012f19c67 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_arbiter_tree.sv @@ -0,0 +1,291 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 arbiter module +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// +// This is a tree implementation of a round robin arbiter. It has the same behavior as the PPC +// implementation in prim_arbiter_ppc, and also uses a prefix summing approach to determine the next +// request to be granted. The main difference with respect to the PPC arbiter is that the leading 1 +// detection and the prefix summation are performed with a binary tree instead of a sequential loop. +// Also, if the data port is enabled, the data is muxed based on the local arbitration decisions at +// each node of the arbiter tree. This means that the data can propagate through the tree +// simultaneously with the requests, instead of waiting for the arbitration to determine the winner +// index first. As a result, this design has a shorter critical path than other implementations, +// leading to better ovberall timing. +// +// Note that the currently winning request is held if the data sink is not ready. This behavior is +// required by some interconnect protocols (AXI, TL). The module contains an assertion that checks +// this behavior. +// +// Also, this module contains a request stability assertion that checks that requests stay asserted +// until they have been served. This assertion can be gated by driving the req_chk_i low. This is +// a non-functional input and does not affect the designs behavior. +// +// See also: prim_arbiter_ppc + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_arbiter_tree #( + parameter int N = 8, + parameter int DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + `CALIPTRA_ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + logic [2**(IdxW+1)-2:0] req_tree; + logic [2**(IdxW+1)-2:0] prio_tree; + logic [2**(IdxW+1)-2:0] sel_tree; + logic [2**(IdxW+1)-2:0] mask_tree; + logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree; + logic [2**(IdxW+1)-2:0][DW-1:0] data_tree; + logic [N-1:0] prio_mask_d, prio_mask_q; + + for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the Pa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == IdxW) begin : gen_leafs + if (offset < N) begin : gen_assign + // forward path (requests and data) + // all requests inputs are assigned to the request tree + assign req_tree[Pa] = req_i[offset]; + // we basically split the incoming request vector into two halves with the following + // priority assignment. the prio_mask_q register contains a prefix sum that has been + // computed using the last winning index, and hence masks out all requests at offsets + // lower or equal the previously granted index. hence, all higher indices are considered + // first in the arbitration tree nodes below, before considering the lower indices. + assign prio_tree[Pa] = req_i[offset] & prio_mask_q[offset]; + // input for the index muxes (used to compute the winner index) + assign idx_tree[Pa] = offset; + // input for the data muxes + assign data_tree[Pa] = data_i[offset]; + + // backward path (grants and prefix sum) + // grant if selected, ready and request asserted + assign gnt_o[offset] = req_i[offset] & sel_tree[Pa] & ready_i; + // only update mask if there is a valid request + assign prio_mask_d[offset] = (|req_i) ? + mask_tree[Pa] | sel_tree[Pa] & ~ready_i : + prio_mask_q[offset]; + end else begin : gen_tie_off + // forward path + assign req_tree[Pa] = '0; + assign prio_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign data_tree[Pa] = '0; + logic unused_sigs; + assign unused_sigs = ^{mask_tree[Pa], + sel_tree[Pa]}; + end + // this creates the node assignments + end else begin : gen_nodes + // local helper variable + logic sel; + + // forward path (requests and data) + // each node looks at its two children, and selects the one with higher priority + assign sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1]; + // propagate requests + assign req_tree[Pa] = req_tree[C0] | req_tree[C1]; + assign prio_tree[Pa] = prio_tree[C1] | prio_tree[C0]; + // data and index muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. + assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + assign data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; + + // backward path (grants and prefix sum) + // this propagates the selction index back and computes a hot one mask + assign sel_tree[C0] = sel_tree[Pa] & ~sel; + assign sel_tree[C1] = sel_tree[Pa] & sel; + // this performs a prefix sum for masking the input requests in the next cycle + assign mask_tree[C0] = mask_tree[Pa]; + assign mask_tree[C1] = mask_tree[Pa] | sel_tree[C0]; + end + end : gen_level + end : gen_tree + + // the results can be found at the tree root + if (EnDataPort) begin : gen_data_port + assign data_o = data_tree[0]; + end else begin : gen_no_dataport + logic [DW-1:0] unused_data; + assign unused_data = data_tree[0]; + assign data_o = '1; + end + + // This index is unused. + logic unused_prio_tree; + assign unused_prio_tree = prio_tree[0]; + + assign idx_o = idx_tree[0]; + assign valid_o = req_tree[0]; + + // the select tree computes a hot one signal that indicates which request is currently selected + assign sel_tree[0] = 1'b1; + // the mask tree is basically a prefix sum of the hot one select signal computed above + assign mask_tree[0] = 1'b0; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_mask_reg + if (!rst_ni) begin + prio_mask_q <= '0; + end else begin + prio_mask_q <= prio_mask_d; + end + end + end + + //////////////// + // assertions // + //////////////// + + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `CALIPTRA_ASSERT_KNOWN(ValidKnown_A, valid_o) + `CALIPTRA_ASSERT_KNOWN(GrantKnown_A, gnt_o) + `CALIPTRA_ASSERT_KNOWN(IdxKnown_A, idx_o) + + // grant index shall be higher index than previous index, unless no higher requests exist. + `CALIPTRA_ASSERT(RoundRobin_A, + ##1 valid_o && ready_i && $past(ready_i) && $past(valid_o) && + |(req_i & ~((N'(1) << $past(idx_o)+1) - 1)) |-> + idx_o > $past(idx_o)) + // we can only grant one requestor at a time + `CALIPTRA_ASSERT(CheckHotOne_A, $onehot0(gnt_o)) + // A grant implies that the sink is ready + `CALIPTRA_ASSERT(GntImpliesReady_A, |gnt_o |-> ready_i) + // A grant implies that the arbiter asserts valid as well + `CALIPTRA_ASSERT(GntImpliesValid_A, |gnt_o |-> valid_o) + // A request and a sink that is ready imply a grant + `CALIPTRA_ASSERT(ReqAndReadyImplyGrant_A, |req_i && ready_i |-> |gnt_o) + // A request and a sink that is ready imply a grant + `CALIPTRA_ASSERT(ReqImpliesValid_A, |req_i |-> valid_o) + // Both conditions above combined and reversed + `CALIPTRA_ASSERT(ReadyAndValidImplyGrant_A, ready_i && valid_o |-> |gnt_o) + // Both conditions above combined and reversed + `CALIPTRA_ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0) + // check index / grant correspond + `CALIPTRA_ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o]) + +if (EnDataPort) begin: gen_data_port_assertion + // data flow + `CALIPTRA_ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) +end + + // requests must stay asserted until they have been granted + `CALIPTRA_ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i) + // check that the arbitration decision is held if the sink is not ready + `CALIPTRA_ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o), + clk_i, !rst_ni || !req_chk_i) + +// FPV-only assertions with symbolic variables +`ifdef FPV_ON + // symbolic variables + int unsigned k; + bit ReadyIsStable; + bit ReqsAreStable; + + // constraints for symbolic variables + `CALIPTRA_ASSUME(KStable_M, ##1 $stable(k)) + `CALIPTRA_ASSUME(KRange_M, k < N) + // this is used enable checking for stable and unstable ready_i and req_i signals in the same run. + // the symbolic variables act like a switch that the solver can trun on and off. + `CALIPTRA_ASSUME(ReadyIsStable_M, ##1 $stable(ReadyIsStable)) + `CALIPTRA_ASSUME(ReqsAreStable_M, ##1 $stable(ReqsAreStable)) + `CALIPTRA_ASSUME(ReadyStable_M, ##1 !ReadyIsStable || $stable(ready_i)) + `CALIPTRA_ASSUME(ReqsStable_M, ##1 !ReqsAreStable || $stable(req_i)) + + // A grant implies a request + `CALIPTRA_ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k]) + + // if request and ready are constantly held at 1, we should eventually get a grant + `CALIPTRA_ASSERT(NoStarvation_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] |-> + strong(##[0:$] gnt_o[k])) + + // if N requests are constantly asserted and ready is constant 1, each request must + // be granted exactly once over a time window of N cycles for the arbiter to be fair. + for (genvar n = 1; n <= N; n++) begin : gen_fairness + integer gnt_cnt; + `CALIPTRA_ASSERT(Fairness_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] && + $countones(req_i) == n |-> + ##n gnt_cnt == $past(gnt_cnt, n) + 1) + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_cnt + if (!rst_ni) begin + gnt_cnt <= 0; + end else begin + gnt_cnt <= gnt_cnt + gnt_o[k]; + end + end + end + + // requests must stay asserted until they have been granted + `CALIPTRA_ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=> + req_i[k], clk_i, !rst_ni || !req_chk_i) +`endif + +endmodule : caliptra_prim_arbiter_tree diff --git a/src/caliptra_prim/rtl/caliptra_prim_blanker.sv b/src/caliptra_prim/rtl/caliptra_prim_blanker.sv new file mode 100644 index 000000000..92756d27c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_blanker.sv @@ -0,0 +1,21 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Convenience module for wrapping prim_and2 for use in blanking. +// When en_i == 1 the input is fed through to the output. +// When en_i == 0 the output is 0. +module caliptra_prim_blanker #( + parameter int Width = 1 +) ( + input logic [Width-1:0] in_i, + input logic en_i, + output logic [Width-1:0] out_o +); + //caliptra_prim_and2 #(.Width(Width)) u_blank_and ( + caliptra_prim_generic_and2 #(.Width(Width)) u_blank_and ( + .in0_i(in_i), + .in1_i({Width{en_i}}), + .out_o + ); +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_clock_mux2.sv b/src/caliptra_prim/rtl/caliptra_prim_clock_mux2.sv new file mode 100644 index 000000000..7ed453773 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_clock_mux2.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Abstract primitives wrapper. +// +// This file is a stop-gap until the DV file list is generated by FuseSoC. +// Its contents are taken from the file which would be generated by FuseSoC. +// https://github.com/lowRISC/ibex/issues/893 + +module caliptra_prim_clock_mux2 #( + parameter bit NoFpgaBufG = 1'b0 +) ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + +if (1) begin : gen_generic + caliptra_prim_generic_clock_mux2 #( + .NoFpgaBufG(NoFpgaBufG) + ) u_impl_generic ( + .* + ); +end + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_count.sv b/src/caliptra_prim/rtl/caliptra_prim_count.sv index 1e8dc7465..1dca705f0 100644 --- a/src/caliptra_prim/rtl/caliptra_prim_count.sv +++ b/src/caliptra_prim/rtl/caliptra_prim_count.sv @@ -16,6 +16,9 @@ // (i.e. 2**Width-1-set_cnt_i). Set has priority over increment and decrement. // incr_en_i: Increments the primary counter by step_i, and decrements the secondary by step_i. // decr_en_i: Decrements the primary counter by step_i, and increments the secondary by step_i. +// commit_i: Counter changes only take effect when `commit_i` is set. This does not effect the +// `cnt_after_commit_o` output which gives the next counter state if the change is +// committed. // // Note that if both incr_en_i and decr_en_i are asserted at the same time, the counter remains // unchanged. The counter is also protected against under- and overflows. @@ -35,12 +38,13 @@ module caliptra_prim_count #( input rst_ni, input clr_i, input set_i, - input [Width-1:0] set_cnt_i, // Set value for the counter. + input [Width-1:0] set_cnt_i, // Set value for the counter. input incr_en_i, input decr_en_i, - input [Width-1:0] step_i, // Increment/decrement step when enabled. - output logic [Width-1:0] cnt_o, // Current counter state - output logic [Width-1:0] cnt_next_o, // Next counter state + input [Width-1:0] step_i, // Increment/decrement step when enabled. + input commit_i, + output logic [Width-1:0] cnt_o, // Current counter state + output logic [Width-1:0] cnt_after_commit_o, // Next counter state if committed output logic err_o ); @@ -53,10 +57,14 @@ module caliptra_prim_count #( localparam logic [NumCnt-1:0][Width-1:0] ResetValues = {{Width{1'b1}} - ResetValue, // secondary ResetValue}; // primary - logic [NumCnt-1:0][Width-1:0] cnt_d, cnt_q, fpv_force; + logic [NumCnt-1:0][Width-1:0] cnt_d, cnt_d_committed, cnt_q; -`ifndef FPV_SEC_CM_ON - // This becomes a free variable in FPV. + // The fpv_force signal can be used in FPV runs to make the internal counters (cnt_q) jump + // unexpectedly. We only want to use this mechanism when we're doing FPV on prim_count itself. In + // that situation, we will have the PrimCountFpv define and wish to leave fpv_force undriven so + // that it becomes a free variable in FPV. In any other situation, we drive the signal with zero. + logic [NumCnt-1:0][Width-1:0] fpv_force; +`ifndef PrimCountFpv assign fpv_force = '0; `endif @@ -100,6 +108,8 @@ module caliptra_prim_count #( (set_i) ? set_val : (cnt_en) ? cnt_sat : cnt_q[k]; + assign cnt_d_committed[k] = commit_i ? cnt_d[k] : cnt_q[k]; + logic [Width-1:0] cnt_unforced_q; caliptra_prim_flop #( .Width(Width), @@ -107,7 +117,7 @@ module caliptra_prim_count #( ) u_cnt_flop ( .clk_i, .rst_ni, - .d_i(cnt_d[k]), + .d_i(cnt_d_committed[k]), .q_o(cnt_unforced_q) ); @@ -121,8 +131,8 @@ module caliptra_prim_count #( assign err_o = (sum != {1'b0, {Width{1'b1}}}); // Output count values - assign cnt_o = cnt_q[0]; - assign cnt_next_o = cnt_d[0]; + assign cnt_o = cnt_q[0]; + assign cnt_after_commit_o = cnt_d[0]; //////////////// // Assertions // @@ -154,7 +164,7 @@ module caliptra_prim_count #( `CALIPTRA_ASSERT(CntNext_A, rst_ni |=> - cnt_o == $past(cnt_next_o), + $past(!commit_i) || (cnt_o == $past(cnt_after_commit_o)), clk_i, err_o || fpv_err_present || !rst_ni) // Clear @@ -194,12 +204,12 @@ module caliptra_prim_count #( // Up counter `CALIPTRA_ASSERT(IncrUpCnt_A, - rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) + rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) && commit_i |=> cnt_o == min($past(cnt_o) + $past({2'b0, step_i}), {2'b0, {Width{1'b1}}}), clk_i, err_o || fpv_err_present || !rst_ni) `CALIPTRA_ASSERT(IncrDnCnt_A, - rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) + rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) && commit_i |=> cnt_q[1] == max($past(signed'({2'b0, cnt_q[1]})) - $past({2'b0, step_i}), '0), clk_i, err_o || fpv_err_present || !rst_ni) @@ -218,12 +228,12 @@ module caliptra_prim_count #( // Down counter `CALIPTRA_ASSERT(DecrUpCnt_A, - rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) + rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) && commit_i |=> cnt_o == max($past(signed'({2'b0, cnt_o})) - $past({2'b0, step_i}), '0), clk_i, err_o || fpv_err_present || !rst_ni) `CALIPTRA_ASSERT(DecrDnCnt_A, - rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) + rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) && commit_i |=> cnt_q[1] == min($past(cnt_q[1]) + $past({2'b0, step_i}), {2'b0, {Width{1'b1}}}), clk_i, err_o || fpv_err_present || !rst_ni) @@ -251,7 +261,7 @@ module caliptra_prim_count #( (cnt_q[1] + cnt_q[0]) != {Width{1'b1}}) // This logic that will be assign to one, when user adds macro - // CALIPTRA_ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT to check the error with alert, in case that caliptra_prim_count + // ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT to check the error with alert, in case that prim_count // is used in design without adding this assertion check. logic unused_assert_connected; diff --git a/src/caliptra_prim/rtl/caliptra_prim_count_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_count_pkg.sv new file mode 100644 index 000000000..bcc99316c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_count_pkg.sv @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package caliptra_prim_count_pkg; + + // An enum that names the possible actions that the inputs might ask for. See the PossibleActions + // parameter in prim_count for how this is used. + typedef logic [3:0] action_mask_t; + typedef enum action_mask_t {Clr = 4'h1, + Set = 4'h2, + Incr = 4'h4, + Decr = 4'h8} action_e; + +endpackage : caliptra_prim_count_pkg diff --git a/src/caliptra_prim/rtl/caliptra_prim_double_lfsr.sv b/src/caliptra_prim/rtl/caliptra_prim_double_lfsr.sv new file mode 100644 index 000000000..e9b8303bf --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_double_lfsr.sv @@ -0,0 +1,113 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Hardened LFSR module that instantiates two LFSRs of the same type. +// The state vector of both LFSRs is constantly checked and an error is asserted if the +// two states are inconsistent. + +module caliptra_prim_double_lfsr #( + // prim_lfsr parameters - refer to prim_lfsr for their meaning/ + parameter LfsrType = "GAL_XOR", + parameter int unsigned LfsrDw = 32, + localparam int unsigned LfsrIdxDw = $clog2(LfsrDw), + parameter int unsigned EntropyDw = 8, + parameter int unsigned StateOutDw = 8, + parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1), + parameter logic [LfsrDw-1:0] CustomCoeffs = '0, + parameter bit StatePermEn = 1'b0, + parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0, + parameter bit MaxLenSVA = 1'b1, + parameter bit LockupSVA = 1'b1, + parameter bit ExtSeedSVA = 1'b1, + parameter bit NonLinearOut = 1'b0, + // This should only be disabled in special circumstances, for example + // in non-comportable IPs where an error does not trigger an alert. + parameter bit EnableAlertTriggerSVA = 1 +) ( + input clk_i, + input rst_ni, + input seed_en_i, + input [LfsrDw-1:0] seed_i, + input lfsr_en_i, + input [EntropyDw-1:0] entropy_i, + output logic [StateOutDw-1:0] state_o, + // Asserted if the parallel LFSR states are inconsistent. + output logic err_o +); + + + logic [1:0][LfsrDw-1:0] lfsr_state; + // We employ redundant LFSRs to guard against FI attacks. + for (genvar k = 0; k < 2; k++) begin : gen_double_lfsr + // Instantiate size_only buffers to prevent + // optimization / merging of redundant logic. + logic lfsr_en_buf, seed_en_buf; + logic [EntropyDw-1:0] entropy_buf; + logic [LfsrDw-1:0] seed_buf, lfsr_state_unbuf; + caliptra_prim_buf #( + .Width(EntropyDw + LfsrDw + 2) + ) u_prim_buf_input ( + .in_i({seed_en_i, seed_i, lfsr_en_i, entropy_i}), + .out_o({seed_en_buf, seed_buf, lfsr_en_buf, entropy_buf}) + ); + + caliptra_prim_lfsr #( + .LfsrType(LfsrType), + .LfsrDw(LfsrDw), + .EntropyDw(EntropyDw), + // output the full width so that the states can be cross checked. + .StateOutDw(LfsrDw), + .DefaultSeed(DefaultSeed), + .CustomCoeffs(CustomCoeffs), + .StatePermEn(StatePermEn), + .StatePerm(StatePerm), + .MaxLenSVA(MaxLenSVA), + .LockupSVA(LockupSVA), + .ExtSeedSVA(ExtSeedSVA), + .NonLinearOut(NonLinearOut) + ) u_prim_lfsr ( + .clk_i, + .rst_ni, + .seed_en_i ( seed_en_buf ), + .seed_i ( seed_buf ), + .lfsr_en_i ( lfsr_en_buf ), + .entropy_i ( entropy_buf ), + .state_o ( lfsr_state_unbuf ) + ); + + caliptra_prim_buf #( + .Width(LfsrDw) + ) u_prim_buf_output ( + .in_i(lfsr_state_unbuf), + .out_o(lfsr_state[k]) + ); + end + +`ifdef SIMULATION +`ifndef VERILATOR + // Ensure both LFSRs start off with the same default seed. if randomized in simulations. + initial begin : p_sync_lfsr_default_seed + wait (!$isunknown(gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal)); + wait (!$isunknown(gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal)); + gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal = + gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal; + $display("%m: Updated gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal = 0x%0h", + gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal); + end +`endif +`endif + + // Output the state from the first LFSR + assign state_o = lfsr_state[0][StateOutDw-1:0]; + assign err_o = lfsr_state[0] != lfsr_state[1]; + + // This logic that will be assign to one, when user adds macro + // ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT to check the error with alert, in case that + // prim_double_lfsr is used in design without adding this assertion check. + `ifdef CALIPTRA_INC_ASSERT + logic unused_assert_connected; + + `CALIPTRA_ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA) + `endif +endmodule : caliptra_prim_double_lfsr diff --git a/src/caliptra_prim/rtl/caliptra_prim_edn_req.sv b/src/caliptra_prim/rtl/caliptra_prim_edn_req.sv new file mode 100644 index 000000000..e5528d732 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_edn_req.sv @@ -0,0 +1,215 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module can be used as a "gadget" to adapt the native 32bit width of the EDN network +// locally to the width needed by the consuming logic. For example, if the local consumer +// needs 128bit, this module would request four 32 bit words from EDN and stack them accordingly. +// +// The module also uses a req/ack synchronizer to synchronize the EDN data over to the local +// clock domain. Note that this assumes that the EDN data bus remains stable between subsequent +// requests. +// + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_edn_req + import caliptra_prim_alert_pkg::*; +#( + parameter int OutWidth = 32, + // Repetition check for incoming edn data + parameter bit RepCheck = 0, + // Disable reset-related assertion checks inside prim_sync_reqack primitives. + parameter bit EnRstChks = 0, + + // EDN Request latency checker + // + // Each consumer IP may have the maximum expected latency. MaxLatency + // parameter describes the expected latency in terms of the consumer clock + // cycles. If the edn request comes later than that, the assertion will be + // fired. + // + // The default value is 0, which disables the assertion. + parameter int unsigned MaxLatency = 0 +) ( + // Design side + input clk_i, + input rst_ni, + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input req_i, + output logic ack_o, + output logic [OutWidth-1:0] data_o, + output logic fips_o, + output logic err_o, // current data_o failed repetition check + // EDN side + input clk_edn_i, + input rst_edn_ni, + output edn_pkg::edn_req_t edn_o, + input edn_pkg::edn_rsp_t edn_i +); + + // Stop requesting words from EDN once desired amount of data is available. + logic word_req, word_ack; + assign word_req = req_i & ~ack_o; + + logic [edn_pkg::ENDPOINT_BUS_WIDTH-1:0] word_data; + logic word_fips; + localparam int SyncWidth = $bits({edn_i.edn_fips, edn_i.edn_bus}); + caliptra_prim_sync_reqack_data #( + .Width(SyncWidth), + .EnRstChks(EnRstChks), + .DataSrc2Dst(1'b0), + .DataReg(1'b0) + ) u_prim_sync_reqack_data ( + .clk_src_i ( clk_i ), + .rst_src_ni ( rst_ni ), + .clk_dst_i ( clk_edn_i ), + .rst_dst_ni ( rst_edn_ni ), + .req_chk_i ( req_chk_i ), + .src_req_i ( word_req ), + .src_ack_o ( word_ack ), + .dst_req_o ( edn_o.edn_req ), + .dst_ack_i ( edn_i.edn_ack ), + .data_i ( {edn_i.edn_fips, edn_i.edn_bus} ), + .data_o ( {word_fips, word_data} ) + ); + + if (RepCheck) begin : gen_rep_chk + logic [edn_pkg::ENDPOINT_BUS_WIDTH-1:0] word_data_q; + always_ff @(posedge clk_i) begin + if (word_ack) begin + word_data_q <= word_data; + end + end + + // do not check until we have received at least the first entry + logic chk_rep; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + chk_rep <= '0; + end else if (word_ack) begin + chk_rep <= 1'b1; + end + end + + // Need to track if any of the packed words has failed the repetition check, i.e., is identical + // to the last packed word. + logic err_d, err_q; + assign err_d = (req_i && ack_o) ? 1'b0 : // clear + (chk_rep && word_ack && word_data == word_data_q) ? 1'b1 : // set + err_q; // keep + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_q <= 1'b0; + end else begin + err_q <= err_d; + end + end + assign err_o = err_q; + + end else begin : gen_no_rep_chk // block: gen_rep_chk + assign err_o = '0; + end + + caliptra_prim_packer_fifo #( + .InW(edn_pkg::ENDPOINT_BUS_WIDTH), + .OutW(OutWidth), + .ClearOnRead(1'b0) + ) u_prim_packer_fifo ( + .clk_i, + .rst_ni, + .clr_i ( 1'b0 ), // not needed + .wvalid_i ( word_ack ), + .wdata_i ( word_data ), + // no need for backpressure since we're always ready to + // sink data at this point. + .wready_o ( ), + .rvalid_o ( ack_o ), + .rdata_o ( data_o ), + // we're always ready to receive the packed output word + // at this point. + .rready_i ( 1'b1 ), + .depth_o ( ) + ); + + // Need to track if any of the packed words has been generated with a pre-FIPS seed, i.e., has + // fips == 1'b0. + logic fips_d, fips_q; + assign fips_d = (req_i && ack_o) ? 1'b1 : // clear + (word_ack) ? fips_q & word_fips : // accumulate + fips_q; // keep + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fips_q <= 1'b1; + end else begin + fips_q <= fips_d; + end + end + assign fips_o = fips_q; + + //////////////// + // Assertions // + //////////////// + + // Check EDN data is valid: Not all zeros, all ones, or not the same as previous data. +`ifdef CALIPTRA_INC_ASSERT + //VCS coverage off + // pragma coverage off + + logic [OutWidth-1:0] data_prev, data_curr; + + always_ff @(posedge ack_o or negedge rst_ni) begin + if (!rst_ni) begin + data_prev <= '0; + data_curr <= '0; + end else if (ack_o) begin + data_curr <= data_o; + data_prev <= data_curr; + end + end + //VCS coverage on + // pragma coverage on + + `CALIPTRA_ASSERT(DataOutputValid_A, ack_o |-> (data_o != 0) && (data_o != '1)) + `CALIPTRA_ASSERT(DataOutputDiffFromPrev_A, data_prev != 0 |-> data_prev != data_o) +`endif + + // EDN Max Latency Checker +`ifndef SYNTHESIS + if (MaxLatency != 0) begin: g_maxlatency_assertion + //VCS coverage off + // pragma coverage off + localparam int unsigned LatencyW = $clog2(MaxLatency+1); + logic [LatencyW-1:0] latency_counter; + logic reset_counter; + logic enable_counter; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) latency_counter <= '0; + else if (reset_counter) latency_counter <= '0; + else if (enable_counter) latency_counter <= latency_counter + 1'b1; + end + + assign reset_counter = ack_o; + assign enable_counter = req_i; + //VCS coverage on + // pragma coverage on + + `CALIPTRA_ASSERT(MaxLatency_A, latency_counter <= MaxLatency) + + // TODO: Is it worth to check req & ack pair? + // _________________________________ + // req __/ \______ + // ____ + // ack ____________________________________/ \_ + // + // | error + + end // g_maxlatency_assertion +`else // SYNTHESIS + logic unused_param_maxlatency; + assign unused_param_maxlatency = ^MaxLatency; +`endif // SYNTHESIS + +endmodule : caliptra_prim_edn_req diff --git a/src/caliptra_prim/rtl/caliptra_prim_esc_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_esc_pkg.sv new file mode 100644 index 000000000..0fdb8ad53 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_esc_pkg.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package caliptra_prim_esc_pkg; + + typedef struct packed { + logic esc_p; + logic esc_n; + } esc_tx_t; + + typedef struct packed { + logic resp_p; + logic resp_n; + } esc_rx_t; + + parameter esc_tx_t ESC_TX_DEFAULT = '{esc_p: 1'b0, + esc_n: 1'b1}; + + parameter esc_rx_t ESC_RX_DEFAULT = '{resp_p: 1'b0, + resp_n: 1'b1}; + +endpackage : caliptra_prim_esc_pkg diff --git a/src/caliptra_prim/rtl/caliptra_prim_esc_receiver.sv b/src/caliptra_prim/rtl/caliptra_prim_esc_receiver.sv new file mode 100644 index 000000000..1da9628bc --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_esc_receiver.sv @@ -0,0 +1,290 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module decodes escalation enable pulses that have been encoded using +// the caliptra_prim_esc_sender module. +// +// The module supports in-band ping testing of the escalation +// wires. This is accomplished by the sender module that places a single-cycle, +// differentially encoded pulse on esc_p/n which will be interpreted as a ping +// request by the receiver module. The receiver module responds by sending back +// the response pattern "1010". +// +// Native escalation enable pulses are differentiated from ping +// requests by making sure that these pulses are always longer than 1 cycle. +// +// See also: caliptra_prim_esc_sender, caliptra_prim_diff_decode, alert_handler + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_esc_receiver + import caliptra_prim_esc_pkg::*; +#( + // The number of escalation severities. Should be set to the Alert Handler's N_ESC_SEV when this + // primitive is instantiated. + parameter int N_ESC_SEV = 4, + + // The width of the Alert Handler's ping counter. Should be set to the Alert Handler's PING_CNT_DW + // when this primitive is instantiated. + parameter int PING_CNT_DW = 16, + + // This counter monitors incoming ping requests and auto-escalates if the alert handler + // ceases to send them regularly. The maximum number of cycles between subsequent ping requests + // is N_ESC_SEV x (2 x 2 x 2**PING_CNT_DW), see also implementation of the ping timer + // (alert_handler_ping_timer.sv). The timeout counter below uses a timeout that is 4x larger than + // that in order to incorporate some margin. + // + // Do NOT modify this counter value, when instantiating it in the design. It is only exposed to + // reduce the state space in the FPV testbench. + localparam int MarginFactor = 4, + localparam int NumWaitCounts = 2, + localparam int NumTimeoutCounts = 2, + parameter int TimeoutCntDw = $clog2(MarginFactor) + + $clog2(N_ESC_SEV) + + $clog2(NumWaitCounts) + + $clog2(NumTimeoutCounts) + + PING_CNT_DW +) ( + input clk_i, + input rst_ni, + // escalation enable + output logic esc_req_o, + // escalation / ping response + output esc_rx_t esc_rx_o, + // escalation output diff pair + input esc_tx_t esc_tx_i +); + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + + logic esc_level, esc_p, esc_n, sigint_detected; + + // This prevents further tool optimizations of the differential signal. + caliptra_prim_buf #( + .Width(2) + ) u_caliptra_prim_buf_esc ( + .in_i({esc_tx_i.esc_n, + esc_tx_i.esc_p}), + .out_o({esc_n, + esc_p}) + ); + + caliptra_prim_diff_decode #( + .AsyncOn(1'b0) + ) u_decode_esc ( + .clk_i, + .rst_ni, + .diff_pi ( esc_p ), + .diff_ni ( esc_n ), + .level_o ( esc_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( sigint_detected ) + ); + + //////////////////////////////////////////// + // Ping Monitor Counter / Auto Escalation // + //////////////////////////////////////////// + + // The timeout counter is kicked off when the first ping occurs, and subsequent pings reset + // the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it + // has reached its maximum (this state is terminal). + logic ping_en, timeout_cnt_error; + logic timeout_cnt_set, timeout_cnt_en; + logic [TimeoutCntDw-1:0] timeout_cnt; + assign timeout_cnt_set = (ping_en && !(&timeout_cnt)); + assign timeout_cnt_en = (timeout_cnt > '0); + + caliptra_prim_count #( + .Width(TimeoutCntDw), + // The escalation receiver behaves differently than other comportable IP. I.e., instead of + // sending out an alert signal, this condition is handled internally in the alert handler. + .EnableAlertTriggerSVA(0), + // Pass a parameter to disable coverage for some assertions that are unreachable because + // clr_i and decr_en_i are tied to zero. + .PossibleActions(caliptra_prim_count_pkg::Set | caliptra_prim_count_pkg::Incr) + ) u_caliptra_prim_count ( + .clk_i, + .rst_ni, + .clr_i(1'b0), + .set_i(timeout_cnt_set), + .set_cnt_i(TimeoutCntDw'(1)), + .incr_en_i(timeout_cnt_en), + .decr_en_i(1'b0), + .step_i(TimeoutCntDw'(1)), + .commit_i(1'b1), + .cnt_o(timeout_cnt), + .cnt_after_commit_o(), + .err_o(timeout_cnt_error) + ); + + // Escalation is asserted if + // - requested via the escalation sender/receiver path, + // - the ping monitor timeout is reached, + // - the two ping monitor counters are in an inconsistent state. + // Register the escalation request to avoid potential CDC issues downstream. + logic esc_req, esc_req_d, esc_req_q; + assign esc_req_d = esc_req || (&timeout_cnt) || timeout_cnt_error; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + esc_req_q <= 1'b0; + end else begin + esc_req_q <= esc_req_d; + end + end + + caliptra_prim_sec_anchor_buf #( + .Width(1) + ) u_caliptra_prim_buf_esc_req ( + .in_i (esc_req_q), + .out_o(esc_req_o) + ); + + ///////////////// + // RX/TX Logic // + ///////////////// + + typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e; + state_e state_d, state_q; + logic resp_pd, resp_pq; + logic resp_nd, resp_nq; + + // This prevents further tool optimizations of the differential signal. + caliptra_prim_sec_anchor_flop #( + .Width(2), + .ResetValue(2'b10) + ) u_caliptra_prim_flop_esc ( + .clk_i, + .rst_ni, + .d_i({resp_nd, resp_pd}), + .q_o({resp_nq, resp_pq}) + ); + + assign esc_rx_o.resp_p = resp_pq; + assign esc_rx_o.resp_n = resp_nq; + + always_comb begin : p_fsm + // default + state_d = state_q; + resp_pd = 1'b0; + resp_nd = 1'b1; + esc_req = 1'b0; + ping_en = 1'b0; + + unique case (state_q) + // wait for the esc_p/n diff pair + Idle: begin + if (esc_level) begin + state_d = Check; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + end + end + // we decide here whether this is only a ping request or + // whether this is an escalation enable + Check: begin + state_d = PingResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + if (esc_level) begin + state_d = EscResp; + esc_req = 1'b1; + end + end + // finish ping response. in case esc_level is again asserted, + // we got an escalation signal (pings cannot occur back to back) + PingResp: begin + state_d = Idle; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + ping_en = 1'b1; + if (esc_level) begin + state_d = EscResp; + esc_req = 1'b1; + end + end + // we have got an escalation enable pulse, + // keep on toggling the outputs + EscResp: begin + state_d = Idle; + if (esc_level) begin + state_d = EscResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + esc_req = 1'b1; + end + end + // we have a signal integrity issue at one of + // the incoming diff pairs. this condition is + // signalled to the sender by setting the resp + // diffpair to the same value and continuously + // toggling them. + SigInt: begin + state_d = Idle; + esc_req = 1'b1; + if (sigint_detected) begin + state_d = SigInt; + resp_pd = ~resp_pq; + resp_nd = ~resp_pq; + end + end + default: state_d = Idle; + endcase + + // bail out if a signal integrity issue has been detected + if (sigint_detected && (state_q != SigInt)) begin + state_d = SigInt; + resp_pd = 1'b0; + resp_nd = 1'b0; + end + end + + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + end else begin + state_q <= state_d; + end + end + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `CALIPTRA_ASSERT_KNOWN(EscEnKnownO_A, esc_req_o) + `CALIPTRA_ASSERT_KNOWN(RespPKnownO_A, esc_rx_o) + + `CALIPTRA_ASSERT(SigIntCheck0_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> esc_rx_o.resp_p == esc_rx_o.resp_n) + `CALIPTRA_ASSERT(SigIntCheck1_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> state_q == SigInt) + // auto-escalate in case of signal integrity issue + `CALIPTRA_ASSERT(SigIntCheck2_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> esc_req_d) + // correct diff encoding + `CALIPTRA_ASSERT(DiffEncCheck_A, esc_tx_i.esc_p ^ esc_tx_i.esc_n |=> esc_rx_o.resp_p ^ esc_rx_o.resp_n) + // disable in case of signal integrity issue + `CALIPTRA_ASSERT(PingRespCheck_A, state_q == Idle ##1 $rose(esc_tx_i.esc_p) ##1 $fell(esc_tx_i.esc_p) |-> + $rose(esc_rx_o.resp_p) ##1 $fell(esc_rx_o.resp_p), + clk_i, !rst_ni || (esc_tx_i.esc_p == esc_tx_i.esc_n)) + // escalation response needs to continuously toggle + `CALIPTRA_ASSERT(EscRespCheck_A, ##1 esc_tx_i.esc_p && $past(esc_tx_i.esc_p) && + (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && $past(esc_tx_i.esc_p ^ esc_tx_i.esc_n) + |=> esc_rx_o.resp_p != $past(esc_rx_o.resp_p)) + // detect escalation pulse + `CALIPTRA_ASSERT(EscEnCheck_A, + esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt + ##1 esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_req_d) + // make sure the counter does not wrap around + `CALIPTRA_ASSERT(EscCntWrap_A, &timeout_cnt |=> timeout_cnt != 0) + // if the counter expires, escalation should be asserted + `CALIPTRA_ASSERT(EscCntEsc_A, &timeout_cnt |-> esc_req_d) + +endmodule : caliptra_prim_esc_receiver diff --git a/src/caliptra_prim/rtl/caliptra_prim_fifo_async.sv b/src/caliptra_prim/rtl/caliptra_prim_fifo_async.sv new file mode 100644 index 000000000..80b57f7d6 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_fifo_async.sv @@ -0,0 +1,298 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic asynchronous fifo for use in a variety of devices. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_fifo_async #( + parameter int unsigned Width = 16, + parameter int unsigned Depth = 4, + parameter bit OutputZeroIfEmpty = 1'b0, // if == 1 always output 0 when FIFO is empty + parameter bit OutputZeroIfInvalid = 1'b0, // if == 1 always output 0 when rvalid_o is low + localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth] +) ( + // write port + input logic clk_wr_i, + input logic rst_wr_ni, + input logic wvalid_i, + output logic wready_o, + input logic [Width-1:0] wdata_i, + output logic [DepthW-1:0] wdepth_o, + + // read port + input logic clk_rd_i, + input logic rst_rd_ni, + output logic rvalid_o, + input logic rready_i, + output logic [Width-1:0] rdata_o, + output logic [DepthW-1:0] rdepth_o +); + + // Depth must be a power of 2 for the gray code pointers to work + `CALIPTRA_ASSERT_INIT(ParamCheckDepth_A, (Depth == 2**$clog2(Depth))) + + localparam int unsigned PTRV_W = (Depth == 1) ? 1 : $clog2(Depth); + localparam int unsigned PTR_WIDTH = (Depth == 1) ? 1 : PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr_q, fifo_wptr_d; + logic [PTR_WIDTH-1:0] fifo_rptr_q, fifo_rptr_d; + logic [PTR_WIDTH-1:0] fifo_wptr_sync_combi, fifo_rptr_sync_combi; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_sync, fifo_rptr_gray_sync, fifo_rptr_sync_q; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_q, fifo_wptr_gray_d; + logic [PTR_WIDTH-1:0] fifo_rptr_gray_q, fifo_rptr_gray_d; + logic fifo_incr_wptr, fifo_incr_rptr; + logic full_wclk, full_rclk, empty_rclk; + logic [Width-1:0] storage [Depth]; + + /////////////////// + // Write Pointer // + /////////////////// + + assign fifo_incr_wptr = wvalid_i & wready_o; + + // decimal version + assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1'b1); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_wptr_q <= '0; + end else if (fifo_incr_wptr) begin + fifo_wptr_q <= fifo_wptr_d; + end + end + + // gray-coded version + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_wptr_gray_q <= '0; + end else if (fifo_incr_wptr) begin + fifo_wptr_gray_q <= fifo_wptr_gray_d; + end + end + + // sync gray-coded pointer to read clk + caliptra_prim_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d_i (fifo_wptr_gray_q), + .q_o (fifo_wptr_gray_sync)); + + ////////////////// + // Read Pointer // + ////////////////// + + assign fifo_incr_rptr = rvalid_o & rready_i; + + // decimal version + assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1'b1); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + fifo_rptr_q <= '0; + end else if (fifo_incr_rptr) begin + fifo_rptr_q <= fifo_rptr_d; + end + end + + // gray-coded version + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + fifo_rptr_gray_q <= '0; + end else if (fifo_incr_rptr) begin + fifo_rptr_gray_q <= fifo_rptr_gray_d; + end + end + + // sync gray-coded pointer to write clk + caliptra_prim_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d_i (fifo_rptr_gray_q), + .q_o (fifo_rptr_gray_sync)); + + // Registered version of synced read pointer + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_rptr_sync_q <= '0; + end else begin + fifo_rptr_sync_q <= fifo_rptr_sync_combi; + end + end + + ////////////////// + // Empty / Full // + ////////////////// + + logic [PTR_WIDTH-1:0] xor_mask; + assign xor_mask = PTR_WIDTH'(1'b1) << (PTR_WIDTH-1); + assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ xor_mask)); + assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ xor_mask)); + assign empty_rclk = (fifo_wptr_sync_combi == fifo_rptr_q); + + if (Depth > 1) begin : g_depth_calc + + // Current depth in the write clock side + logic wptr_msb; + logic rptr_sync_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_sync_value; + + assign wptr_msb = fifo_wptr_q[PTR_WIDTH-1]; + assign rptr_sync_msb = fifo_rptr_sync_q[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr_q[0+:PTRV_W]; + assign rptr_sync_value = fifo_rptr_sync_q[0+:PTRV_W]; + assign wdepth_o = (full_wclk) ? DepthW'(Depth) : + (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) : + (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ; + + // Current depth in the read clock side + logic rptr_msb; + logic wptr_sync_msb; + logic [PTRV_W-1:0] rptr_value; + logic [PTRV_W-1:0] wptr_sync_value; + + assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr_q[PTR_WIDTH-1]; + assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W]; + assign rptr_value = fifo_rptr_q[0+:PTRV_W]; + assign rdepth_o = (full_rclk) ? DepthW'(Depth) : + (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ; + + end else begin : g_no_depth_calc + + assign rdepth_o = full_rclk; + assign wdepth_o = full_wclk; + + end + + assign wready_o = ~full_wclk; + assign rvalid_o = ~empty_rclk; + + ///////////// + // Storage // + ///////////// + + logic [Width-1:0] rdata_int; + if (Depth > 1) begin : g_storage_mux + + always_ff @(posedge clk_wr_i) begin + if (fifo_incr_wptr) begin + storage[fifo_wptr_q[PTRV_W-1:0]] <= wdata_i; + end + end + + assign rdata_int = storage[fifo_rptr_q[PTRV_W-1:0]]; + + end else begin : g_storage_simple + + always_ff @(posedge clk_wr_i) begin + if (fifo_incr_wptr) begin + storage[0] <= wdata_i; + end + end + + assign rdata_int = storage[0]; + + end + + // rdata_o is qualified with rvalid_o to avoid CDC error + if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero + if (OutputZeroIfInvalid == 1'b1) begin : gen_invalid_zero + assign rdata_o = empty_rclk ? '0 : (rvalid_o ? rdata_int : '0); + end + else begin : gen_invalid_non_zero + assign rdata_o = empty_rclk ? '0 : rdata_int; + end + end else begin : gen_no_output_zero + if (OutputZeroIfInvalid == 1'b1) begin : gen_invalid_zero + assign rdata_o = rvalid_o ? rdata_int : '0; + end + else begin : gen_invalid_non_zero + assign rdata_o = rdata_int; + end + end + + ////////////////////////////////////// + // Decimal <-> Gray-code Conversion // + ////////////////////////////////////// + + // This code is all in a generate context to avoid lint errors when Depth <= 2 + if (Depth > 2) begin : g_full_gray_conversion + + function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval); + logic [PTR_WIDTH-1:0] decval_sub; + logic [PTR_WIDTH-1:0] decval_in; + logic unused_decval_msb; + + decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1; + + decval_in = decval[PTR_WIDTH-1] ? decval_sub : decval; + + // We do not care about the MSB, hence we mask it out + unused_decval_msb = decval_in[PTR_WIDTH-1]; + decval_in[PTR_WIDTH-1] = 1'b0; + + // Perform the XOR conversion + dec2gray = decval_in; + dec2gray ^= (decval_in >> 1); + + // Override the MSB + dec2gray[PTR_WIDTH-1] = decval[PTR_WIDTH-1]; + endfunction + + // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0. + function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval); + logic [PTR_WIDTH-1:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp = '0; + for (int i = PTR_WIDTH-2; i >= 0; i--) begin + dec_tmp[i] = dec_tmp[i+1] ^ grayval[i]; + end + dec_tmp_sub = (PTR_WIDTH)'(Depth) - dec_tmp - 1'b1; + if (grayval[PTR_WIDTH-1]) begin + gray2dec = dec_tmp_sub; + // Override MSB + gray2dec[PTR_WIDTH-1] = 1'b1; + unused_decsub_msb = dec_tmp_sub[PTR_WIDTH-1]; + end else begin + gray2dec = dec_tmp; + end + endfunction + + // decimal version of read pointer in write domain + assign fifo_rptr_sync_combi = gray2dec(fifo_rptr_gray_sync); + // decimal version of write pointer in read domain + assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync); + + assign fifo_rptr_gray_d = dec2gray(fifo_rptr_d); + assign fifo_wptr_gray_d = dec2gray(fifo_wptr_d); + + end else if (Depth == 2) begin : g_simple_gray_conversion + + assign fifo_rptr_sync_combi = {fifo_rptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync}; + assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_wptr_gray_sync}; + + assign fifo_rptr_gray_d = {fifo_rptr_d[PTR_WIDTH-1], ^fifo_rptr_d}; + assign fifo_wptr_gray_d = {fifo_wptr_d[PTR_WIDTH-1], ^fifo_wptr_d}; + + end else begin : g_no_gray_conversion + + assign fifo_rptr_sync_combi = fifo_rptr_gray_sync; + assign fifo_wptr_sync_combi = fifo_wptr_gray_sync; + + assign fifo_rptr_gray_d = fifo_rptr_d; + assign fifo_wptr_gray_d = fifo_wptr_d; + + end + + // TODO: assertions on full, empty + `CALIPTRA_ASSERT(GrayWptr_A, ##1 $countones(fifo_wptr_gray_q ^ $past(fifo_wptr_gray_q)) <= 1, + clk_wr_i, !rst_wr_ni) + `CALIPTRA_ASSERT(GrayRptr_A, ##1 $countones(fifo_rptr_gray_q ^ $past(fifo_rptr_gray_q)) <= 1, + clk_rd_i, !rst_rd_ni) + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_fifo_async_simple.sv b/src/caliptra_prim/rtl/caliptra_prim_fifo_async_simple.sv new file mode 100644 index 000000000..9a75e5384 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_fifo_async_simple.sv @@ -0,0 +1,91 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_fifo_async_simple #( + parameter int unsigned Width = 16, + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by + // default. + parameter bit EnRzHs = 1'b0 // By Default, the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake protocol + // if the FSMs need to be partial-reset-safe. +) ( + // write port + input logic clk_wr_i, + input logic rst_wr_ni, + input logic wvalid_i, + output logic wready_o, + input logic [Width-1:0] wdata_i, + + // read port + input logic clk_rd_i, + input logic rst_rd_ni, + output logic rvalid_o, + input logic rready_i, + output logic [Width-1:0] rdata_o +); + + //////////////// + // FIFO logic // + //////////////// + + // Convert ready/valid to req/ack + logic wr_en; + logic src_req, src_ack; + logic pending_d, pending_q, not_in_reset_q; + assign wready_o = !pending_q && not_in_reset_q; + assign wr_en = wvalid_i && wready_o; + assign src_req = pending_q || wvalid_i; + + assign pending_d = (src_ack) ? 1'b0 : + (wr_en) ? 1'b1 : pending_q; + + logic dst_req, dst_ack; + assign rvalid_o = dst_req; + assign dst_ack = dst_req && rready_i; + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + pending_q <= 1'b0; + not_in_reset_q <= 1'b0; + end else begin + pending_q <= pending_d; + not_in_reset_q <= 1'b1; + end + end + + //////////////////////////////////// + // REQ/ACK synchronizer primitive // + //////////////////////////////////// + + caliptra_prim_sync_reqack #( + .EnRstChks(EnRstChks), + .EnRzHs(EnRzHs) + ) u_caliptra_prim_sync_reqack ( + .clk_src_i(clk_wr_i), + .rst_src_ni(rst_wr_ni), + .clk_dst_i(clk_rd_i), + .rst_dst_ni(rst_rd_ni), + .req_chk_i(1'b0), + .src_req_i(src_req), + .src_ack_o(src_ack), + .dst_req_o(dst_req), + .dst_ack_i(dst_ack) + ); + + ////////////////////// + // Data holding reg // + ////////////////////// + + logic [Width-1:0] data_q; + always_ff @(posedge clk_wr_i) begin + if (wr_en) begin + data_q <= wdata_i; + end + end + assign rdata_o = data_q; + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_lc_sender.sv b/src/caliptra_prim/rtl/caliptra_prim_lc_sender.sv new file mode 100644 index 000000000..c7552afd1 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_lc_sender.sv @@ -0,0 +1,68 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Multibit life cycle signal sender module. +// +// This module is instantiates a hand-picked flop cell +// for each bit in the life cycle control signal such that tools do not +// optimize the multibit encoding. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_lc_sender #( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // 0: reset value is lc_ctrl_pkg::Off + // 1: reset value is lc_ctrl_pkg::On + parameter bit ResetValueIsOn = 0 +) ( + input clk_i, + input rst_ni, + input lc_ctrl_pkg::lc_tx_t lc_en_i, + output lc_ctrl_pkg::lc_tx_t lc_en_o +); + + localparam lc_ctrl_pkg::lc_tx_t ResetValue = (ResetValueIsOn) ? lc_ctrl_pkg::On : + lc_ctrl_pkg::Off; + + logic [lc_ctrl_pkg::TxWidth-1:0] lc_en, lc_en_out; + assign lc_en = lc_ctrl_pkg::TxWidth'(lc_en_i); + + if (AsyncOn) begin : gen_flops + caliptra_prim_sec_anchor_flop #( + .Width(lc_ctrl_pkg::TxWidth), + .ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( lc_en ), + .q_o ( lc_en_out ) + ); + end else begin : gen_no_flops + for (genvar k = 0; k < lc_ctrl_pkg::TxWidth; k++) begin : gen_bits + caliptra_prim_sec_anchor_buf u_prim_buf ( + .in_i(lc_en[k]), + .out_o(lc_en_out[k]) + ); + end + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // or nothing at all. + // This logic will be removed for sythesis since it is unloaded. + lc_ctrl_pkg::lc_tx_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= lc_ctrl_pkg::Off; + end else begin + unused_logic <= lc_en_i; + end + end + end + + assign lc_en_o = lc_ctrl_pkg::lc_tx_t'(lc_en_out); + +endmodule : caliptra_prim_lc_sender diff --git a/src/caliptra_prim/rtl/caliptra_prim_mubi4_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_mubi4_dec.sv new file mode 100644 index 000000000..bf7370d13 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_mubi4_dec.sv @@ -0,0 +1,48 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Decoder for multibit control signals with additional input buffers. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_mubi4_dec + import caliptra_prim_mubi_pkg::*; +#( + parameter bit TestTrue = 1, + parameter bit TestStrict = 1 +) ( + input mubi4_t mubi_i, + output logic mubi_dec_o +); + +logic [MuBi4Width-1:0] mubi, mubi_out; +assign mubi = MuBi4Width'(mubi_i); + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < MuBi4Width; k++) begin : gen_bits + caliptra_prim_buf u_caliptra_prim_buf ( + .in_i ( mubi[k] ), + .out_o ( mubi_out[k] ) + ); +end + +if (TestTrue && TestStrict) begin : gen_test_true_strict + assign mubi_dec_o = mubi4_test_true_strict(mubi4_t'(mubi_out)); +end else if (TestTrue && !TestStrict) begin : gen_test_true_loose + assign mubi_dec_o = mubi4_test_true_loose(mubi4_t'(mubi_out)); +end else if (!TestTrue && TestStrict) begin : gen_test_false_strict + assign mubi_dec_o = mubi4_test_false_strict(mubi4_t'(mubi_out)); +end else if (!TestTrue && !TestStrict) begin : gen_test_false_loose + assign mubi_dec_o = mubi4_test_false_loose(mubi4_t'(mubi_out)); +end else begin : gen_unknown_config + `CALIPTRA_ASSERT_INIT(UnknownConfig_A, 0) +end + +endmodule : caliptra_prim_mubi4_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_mubi4_sender.sv b/src/caliptra_prim/rtl/caliptra_prim_mubi4_sender.sv new file mode 100644 index 000000000..4b383ccc9 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_mubi4_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_mubi4_sender + import caliptra_prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi4_t ResetValue = MuBi4False +) ( + input clk_i, + input rst_ni, + input mubi4_t mubi_i, + output mubi4_t mubi_o +); + + logic [MuBi4Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi4Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + caliptra_prim_flop #( + .Width(MuBi4Width), + .ResetValue(MuBi4Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi4_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi4False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + caliptra_prim_sec_anchor_buf #( + .Width(4) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + caliptra_prim_buf #( + .Width(4) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi4_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `CALIPTRA_ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : caliptra_prim_mubi4_sender diff --git a/src/caliptra_prim/rtl/caliptra_prim_mubi8_sender.sv b/src/caliptra_prim/rtl/caliptra_prim_mubi8_sender.sv new file mode 100644 index 000000000..28e94453f --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_mubi8_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_mubi8_sender + import caliptra_prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi8_t ResetValue = MuBi8False +) ( + input clk_i, + input rst_ni, + input mubi8_t mubi_i, + output mubi8_t mubi_o +); + + logic [MuBi8Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi8Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + caliptra_prim_flop #( + .Width(MuBi8Width), + .ResetValue(MuBi8Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi8_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi8False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + caliptra_prim_sec_anchor_buf #( + .Width(8) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + caliptra_prim_buf #( + .Width(8) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi8_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `CALIPTRA_ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : caliptra_prim_mubi8_sender diff --git a/src/caliptra_prim/rtl/caliptra_prim_mubi_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_mubi_pkg.sv index cc474c97d..cd7786a16 100644 --- a/src/caliptra_prim/rtl/caliptra_prim_mubi_pkg.sv +++ b/src/caliptra_prim/rtl/caliptra_prim_mubi_pkg.sv @@ -19,10 +19,15 @@ package caliptra_prim_mubi_pkg; ////////////////////////////////////////////// parameter int MuBi4Width = 4; + /* typedef enum logic [MuBi4Width-1:0] { MuBi4True = 4'h6, // enabled MuBi4False = 4'h9 // disabled } mubi4_t; + */ + typedef logic [MuBi4Width-1:0] mubi4_t; + localparam mubi4_t MuBi4True = 4'h6; + localparam mubi4_t MuBi4False = 4'h9; // This is a prerequisite for the multibit functions below to work. `CALIPTRA_ASSERT_STATIC_IN_PACKAGE(CheckMuBi4ValsComplementary_A, MuBi4True == ~MuBi4False) diff --git a/src/caliptra_prim/rtl/caliptra_prim_otp_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_otp_pkg.sv new file mode 100644 index 000000000..ca056b93c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_otp_pkg.sv @@ -0,0 +1,50 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Common interface definitions for OTP primitives. + +package caliptra_prim_otp_pkg; + + // The command is sparsely encoded to make it more difficult to tamper with. + parameter int CmdWidth = 7; + parameter int ErrWidth = 3; + + // Encoding generated with: + // $ ./util/design/sparse-fsm-encode.py -d 4 -m 5 -n 7 \ + // -s 696743973 --language=sv + // + // Hamming distance histogram: + // + // 0: -- + // 1: -- + // 2: -- + // 3: -- + // 4: |||||||||||||||||||| (100.00%) + // 5: -- + // 6: -- + // 7: -- + // + // Minimum Hamming distance: 4 + // Maximum Hamming distance: 4 + // Minimum Hamming weight: 3 + // Maximum Hamming weight: 5 + // + typedef enum logic [CmdWidth-1:0] { + Read = 7'b1000101, + Write = 7'b0110111, + // Raw commands ignore integrity + ReadRaw = 7'b1111001, + WriteRaw = 7'b1100010, + Init = 7'b0101100 + } cmd_e; + + typedef enum logic [ErrWidth-1:0] { + NoError = 3'h0, + MacroError = 3'h1, + MacroEccCorrError = 3'h2, + MacroEccUncorrError = 3'h3, + MacroWriteBlankError = 3'h4 + } err_e; + +endpackage : caliptra_prim_otp_pkg diff --git a/src/caliptra_prim/rtl/caliptra_prim_packer.sv b/src/caliptra_prim/rtl/caliptra_prim_packer.sv new file mode 100644 index 000000000..559484c2d --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_packer.sv @@ -0,0 +1,371 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Combine InW data and write to OutW data if packed to full word or stop signal + +`include "caliptra_prim_assert.sv" + +// ICEBOX(#12958): Revise to send out the empty status. +module caliptra_prim_packer #( + parameter int unsigned InW = 32, + parameter int unsigned OutW = 32, + + // If 1, The input/output are byte granularity + parameter int HintByteData = 0, + + // Turn on protect against FI for the pos variable + parameter bit EnProtection = 1'b 0 +) ( + input clk_i , + input rst_ni, + + input valid_i, + input [InW-1:0] data_i, + input [InW-1:0] mask_i, + output ready_o, + + output logic valid_o, + output logic [OutW-1:0] data_o, + output logic [OutW-1:0] mask_o, + input ready_i, + + input flush_i, // If 1, send out remnant and clear state + output logic flush_done_o, + + // When EnProtection is set, err_o raises an error case (position variable + // mismatch) + output logic err_o +); + + localparam int unsigned Width = InW + OutW; // storage width + localparam int unsigned ConcatW = Width + InW; // Input concatenated width + localparam int unsigned PtrW = $clog2(ConcatW+1); + localparam int unsigned IdxW = caliptra_prim_util_pkg::vbits(InW); + localparam int unsigned OnesCntW = $clog2(InW+1); + + logic valid_next, ready_next; + logic [Width-1:0] stored_data, stored_mask; + logic [ConcatW-1:0] concat_data, concat_mask; + logic [ConcatW-1:0] shiftl_data, shiftl_mask; + logic [InW-1:0] shiftr_data, shiftr_mask; + + logic [PtrW-1:0] pos_q; // Current write position + logic [IdxW-1:0] lod_idx; // result of Leading One Detector + logic [OnesCntW-1:0] inmask_ones; // Counting Ones for mask_i + + logic ack_in, ack_out; + + logic flush_valid; // flush data out request + logic flush_done; + + // Computing next position ================================================== + always_comb begin + // counting mask_i ones + inmask_ones = '0; + for (int i = 0 ; i < InW ; i++) begin + inmask_ones = inmask_ones + OnesCntW'(mask_i[i]); + end + end + + logic [PtrW-1:0] pos_with_input; + assign pos_with_input = pos_q + PtrW'(inmask_ones); + + if (EnProtection == 1'b 0) begin : g_pos_nodup + logic [PtrW-1:0] pos_d; + + always_comb begin + pos_d = pos_q; + + unique case ({ack_in, ack_out}) + 2'b00: pos_d = pos_q; + 2'b01: pos_d = (int'(pos_q) <= OutW) ? '0 : pos_q - PtrW'(OutW); + 2'b10: pos_d = pos_with_input; + 2'b11: pos_d = (int'(pos_with_input) <= OutW) ? '0 : pos_with_input - PtrW'(OutW); + default: pos_d = pos_q; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pos_q <= '0; + end else if (flush_done) begin + pos_q <= '0; + end else begin + pos_q <= pos_d; + end + end + + assign err_o = 1'b 0; // No checker logic + + end else begin : g_pos_dupcnt // EnProtection == 1'b 1 + // incr_en: Increase the pos by cnt_step. ack_in && !ack_out + // decr_en: Decrease the pos by cnt_step. !ack_in && ack_out + // set_en: Set to specific value in case of ack_in && ack_out. + // This case, the value could be increased or descreased based on + // the input size (inmask_ones) + logic cnt_incr_en, cnt_decr_en, cnt_set_en; + logic [PtrW-1:0] cnt_step, cnt_set; + + assign cnt_incr_en = ack_in && !ack_out; + assign cnt_decr_en = !ack_in && ack_out; + assign cnt_set_en = ack_in && ack_out; + + // counter has underflow protection. + assign cnt_step = (cnt_incr_en) ? PtrW'(inmask_ones) : PtrW'(OutW); + + always_comb begin : cnt_set_logic + + // default, consuming all data + cnt_set = '0; + + if (pos_with_input > PtrW'(OutW)) begin + // pos_q + inmask_ones is bigger than Output width. Still data remained. + cnt_set = pos_with_input - PtrW'(OutW); + end + end : cnt_set_logic + + + caliptra_prim_count #( + .Width (PtrW), + .ResetValue ('0 ) + ) u_pos ( + .clk_i, + .rst_ni, + + .clr_i (flush_done), + + .set_i (cnt_set_en), + .set_cnt_i (cnt_set ), + + .incr_en_i (cnt_incr_en), + .decr_en_i (cnt_decr_en), + .step_i (cnt_step ), + .commit_i (1'b1 ), + + .cnt_o (pos_q ), // Current counter state + .cnt_after_commit_o ( ), // Next counter state + + .err_o + ); + end // g_pos_dupcnt + + //--------------------------------------------------------------------------- + + // Leading one detector for mask_i + always_comb begin + lod_idx = 0; + for (int i = InW-1; i >= 0 ; i--) begin + if (mask_i[i] == 1'b1) begin + lod_idx = IdxW'(unsigned'(i)); + end + end + end + + assign ack_in = valid_i & ready_o; + assign ack_out = valid_o & ready_i; + + // Data process ============================================================= + // shiftr : Input data shifted right to put the leading one at bit zero + assign shiftr_data = (valid_i) ? data_i >> lod_idx : '0; + assign shiftr_mask = (valid_i) ? mask_i >> lod_idx : '0; + + // shiftl : Input data shifted into the current stored position + assign shiftl_data = ConcatW'(shiftr_data) << pos_q; + assign shiftl_mask = ConcatW'(shiftr_mask) << pos_q; + + // concat : Merging stored and shiftl + assign concat_data = {{(InW){1'b0}}, stored_data & stored_mask} | + (shiftl_data & shiftl_mask); + assign concat_mask = {{(InW){1'b0}}, stored_mask} | shiftl_mask; + + logic [Width-1:0] stored_data_next, stored_mask_next; + + always_comb begin + unique case ({ack_in, ack_out}) + 2'b 00: begin + stored_data_next = stored_data; + stored_mask_next = stored_mask; + end + 2'b 01: begin + // ack_out : shift the amount of OutW + stored_data_next = {{OutW{1'b0}}, stored_data[Width-1:OutW]}; + stored_mask_next = {{OutW{1'b0}}, stored_mask[Width-1:OutW]}; + end + 2'b 10: begin + // ack_in : Store concat data + stored_data_next = concat_data[0+:Width]; + stored_mask_next = concat_mask[0+:Width]; + end + 2'b 11: begin + // both : shift the concat_data + stored_data_next = concat_data[ConcatW-1:OutW]; + stored_mask_next = concat_mask[ConcatW-1:OutW]; + end + default: begin + stored_data_next = stored_data; + stored_mask_next = stored_mask; + end + endcase + end + + // Store the data temporary if it doesn't exceed OutW + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_data <= '0; + stored_mask <= '0; + end else if (flush_done) begin + stored_data <= '0; + stored_mask <= '0; + end else begin + stored_data <= stored_data_next; + stored_mask <= stored_mask_next; + end + end + //--------------------------------------------------------------------------- + + // flush handling + typedef enum logic { + FlushIdle, + FlushSend + } flush_st_e; + flush_st_e flush_st, flush_st_next; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + flush_st <= FlushIdle; + end else begin + flush_st <= flush_st_next; + end + end + + always_comb begin + flush_st_next = FlushIdle; + + flush_valid = 1'b0; + flush_done = 1'b0; + + unique case (flush_st) + FlushIdle: begin + if (flush_i) begin + flush_st_next = FlushSend; + end else begin + flush_st_next = FlushIdle; + end + end + + FlushSend: begin + if (pos_q == '0) begin + flush_st_next = FlushIdle; + + flush_valid = 1'b 0; + flush_done = 1'b 1; + end else begin + flush_st_next = FlushSend; + + flush_valid = 1'b 1; + flush_done = 1'b 0; + end + end + default: begin + flush_st_next = FlushIdle; + + flush_valid = 1'b 0; + flush_done = 1'b 0; + end + endcase + end + + assign flush_done_o = flush_done; + + + // Output signals =========================================================== + assign valid_next = (int'(pos_q) >= OutW) ? 1'b 1 : flush_valid; + + // storage space is InW + OutW. So technically, ready_o can be asserted even + // if `pos_q` is greater than OutW. But in order to do that, the logic should + // use `inmask_ones` value whether pos_q+inmask_ones is less than (InW+OutW) + // with `valid_i`. It creates a path from `valid_i` --> `ready_o`. + // It may create a timing loop in some modules that use `ready_o` to + // `valid_i` (which is not a good practice though) + assign ready_next = int'(pos_q) <= OutW; + + // Output request + assign valid_o = valid_next; + assign data_o = stored_data[OutW-1:0]; + assign mask_o = stored_mask[OutW-1:0]; + + // ready_o + assign ready_o = ready_next; + //--------------------------------------------------------------------------- + + ////////////////////////////////////////////// + // Assertions, Assumptions, and Coverpoints // + ////////////////////////////////////////////// + // Assumption: mask_i should be contiguous ones + // e.g: 0011100 --> OK + // 0100011 --> Not OK + if (InW > 1) begin : gen_mask_assert + `CALIPTRA_ASSUME(ContiguousOnesMask_M, + valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2) + end + + // Flush and Write Enable cannot be asserted same time + `CALIPTRA_ASSUME(ExFlushValid_M, flush_i |-> !valid_i) + + // While in flush state, new request shouldn't come + `CALIPTRA_ASSUME(ValidIDeassertedOnFlush_M, + flush_st == FlushSend |-> $stable(valid_i)) + + // If not acked, input port keeps asserting valid and data + `CALIPTRA_ASSUME(DataIStable_M, + ##1 valid_i && $past(valid_i) && !$past(ready_o) + |-> $stable(data_i) && $stable(mask_i)) + + `CALIPTRA_ASSERT(FlushFollowedByDone_A, + ##1 $rose(flush_i) && !flush_done_o |-> !flush_done_o [*0:$] ##1 flush_done_o) + + // If not acked, valid_o should keep asserting + `CALIPTRA_ASSERT(ValidOPairedWidthReadyI_A, + valid_o && !ready_i |=> valid_o) + + // If stored data is greater than the output width, valid should be asserted + `CALIPTRA_ASSERT(ValidOAssertedForStoredDataGTEOutW_A, + ($countones(stored_mask) >= OutW) |-> valid_o) + + // If output port doesn't accept the data, the data should be stable + `CALIPTRA_ASSERT(DataOStableWhenPending_A, + ##1 valid_o && $past(valid_o) + && !$past(ready_i) |-> $stable(data_o)) + + // If input data & stored data are greater than OutW, remained should be stored + `CALIPTRA_ASSERT(ExcessiveDataStored_A, + ack_in && ack_out && (($countones(mask_i) + $countones(stored_mask)) > OutW) + |=> (($past(data_i) & $past(mask_i)) >> + ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + == stored_data) + + `CALIPTRA_ASSERT(ExcessiveMaskStored_A, + ack_in && ack_out && (($countones(mask_i) + $countones(stored_mask)) > OutW) + |=> ($past(mask_i) >> + ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + == stored_mask) + + // Assertions for byte hint enabled + if (HintByteData != 0) begin : g_byte_assert + `CALIPTRA_ASSERT_INIT(InputDividedBy8_A, InW % 8 == 0) + `CALIPTRA_ASSERT_INIT(OutputDividedBy8_A, OutW % 8 == 0) + + // Masking[8*i+:8] should be all zero or all one + for (genvar i = 0 ; i < InW/8 ; i++) begin : g_byte_input_masking + `CALIPTRA_ASSERT(InputMaskContiguous_A, + valid_i |-> (|mask_i[8*i+:8] == 1'b 0) + || (&mask_i[8*i+:8] == 1'b 1)) + end + for (genvar i = 0 ; i < OutW/8 ; i++) begin : g_byte_output_masking + `CALIPTRA_ASSERT(OutputMaskContiguous_A, + valid_o |-> (|mask_o[8*i+:8] == 1'b 0) + || (&mask_o[8*i+:8] == 1'b 1)) + end + end +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_present.sv b/src/caliptra_prim/rtl/caliptra_prim_present.sv new file mode 100644 index 000000000..884bd9510 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_present.sv @@ -0,0 +1,158 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module is an implementation of the encryption pass of the 64bit PRESENT +// block cipher. It is a fully unrolled combinational implementation that +// supports both key sizes specified in the paper (80bit and 128bit). Further, +// the number of rounds is fully configurable, and the primitive supports a +// 32bit block cipher flavor which is not specified in the original paper. It +// should be noted, however, that the 32bit version is **not** secure and must +// not be used in a setting where cryptographic cipher strength is required. The +// 32bit variant is only intended to be used as a lightweight data scrambling +// device. +// +// See also: prim_prince, prim_cipher_pkg +// +// References: - https://en.wikipedia.org/wiki/PRESENT +// - https://en.wikipedia.org/wiki/Prince_(cipher) +// - http://www.lightweightcrypto.org/present/present_ches2007.pdf +// - https://eprint.iacr.org/2012/529.pdf +// - https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/ +// documents/papers/session7-maene-paper.pdf + +`include "caliptra_prim_assert.sv" +module caliptra_prim_present #( + parameter int DataWidth = 64, // {32, 64} + parameter int KeyWidth = 128, // {64, 80, 128} + // Number of rounds to perform in total (>0) + parameter int NumRounds = 31, + // Number of physically instantiated PRESENT rounds. + // This can be used to construct e.g. an iterative + // full-round implementation that only has one physical + // round instance by setting NumRounds = 31 and NumPhysRounds = 1. + // Note that NumPhysRounds needs to divide NumRounds. + parameter int NumPhysRounds = NumRounds, + // Note that the decryption pass needs a modified key, + // to be calculated by performing NumRounds key updates + parameter bit Decrypt = 0 // 0: encrypt, 1: decrypt +) ( + input [DataWidth-1:0] data_i, + input [KeyWidth-1:0] key_i, + // Starting round index for keyschedule [1 ... 31]. + // Set this to 5'd1 for a fully unrolled encryption, and 5'd31 for a fully unrolled decryption. + input [4:0] idx_i, + output logic [DataWidth-1:0] data_o, + output logic [KeyWidth-1:0] key_o, + // Next round index for keyschedule + // (Enc: idx_i + NumPhysRounds, Dec: idx_i - NumPhysRounds) + // Can be ignored for a fully unrolled implementation. + output logic [4:0] idx_o +); + + ////////////// + // datapath // + ////////////// + + logic [NumPhysRounds:0][DataWidth-1:0] data_state; + logic [NumPhysRounds:0][KeyWidth-1:0] round_key; + logic [NumPhysRounds:0][4:0] round_idx; + + // initialize + assign data_state[0] = data_i; + assign round_key[0] = key_i; + assign round_idx[0] = idx_i; + + for (genvar k = 0; k < NumPhysRounds; k++) begin : gen_round + logic [DataWidth-1:0] data_state_xor, data_state_sbox; + // cipher layers + assign data_state_xor = data_state[k] ^ round_key[k][KeyWidth-1 : KeyWidth-DataWidth]; + //////////////////////////////// + // decryption pass, performs inverse permutation, sbox and keyschedule + if (Decrypt) begin : gen_dec + // Decrement round count. + assign round_idx[k+1] = round_idx[k] - 1'b1; + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = caliptra_prim_cipher_pkg::perm_64bit(data_state_xor, + caliptra_prim_cipher_pkg::PRESENT_PERM64_INV); + assign data_state[k+1] = caliptra_prim_cipher_pkg::sbox4_64bit(data_state_sbox, + caliptra_prim_cipher_pkg::PRESENT_SBOX4_INV); + // reduced 32bit variant + end else begin : gen_d32 + assign data_state_sbox = caliptra_prim_cipher_pkg::perm_32bit(data_state_xor, + caliptra_prim_cipher_pkg::PRESENT_PERM32_INV); + assign data_state[k+1] = caliptra_prim_cipher_pkg::sbox4_32bit(data_state_sbox, + caliptra_prim_cipher_pkg::PRESENT_SBOX4_INV); + end + // update round key, count goes from 1 to 31 (max) + // original 128bit key variant + if (KeyWidth == 128) begin : gen_k128 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_inv_update_key128(round_key[k], + round_idx[k]); + // original 80bit key variant + end else if (KeyWidth == 80) begin : gen_k80 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_inv_update_key80(round_key[k], + round_idx[k]); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_inv_update_key64(round_key[k], + round_idx[k]); + end + //////////////////////////////// + // encryption pass + end else begin : gen_enc + // Increment round count. + assign round_idx[k+1] = round_idx[k] + 1'b1; + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = caliptra_prim_cipher_pkg::sbox4_64bit(data_state_xor, + caliptra_prim_cipher_pkg::PRESENT_SBOX4); + assign data_state[k+1] = caliptra_prim_cipher_pkg::perm_64bit(data_state_sbox, + caliptra_prim_cipher_pkg::PRESENT_PERM64); + // reduced 32bit variant + end else begin : gen_d32 + assign data_state_sbox = caliptra_prim_cipher_pkg::sbox4_32bit(data_state_xor, + caliptra_prim_cipher_pkg::PRESENT_SBOX4); + assign data_state[k+1] = caliptra_prim_cipher_pkg::perm_32bit(data_state_sbox, + caliptra_prim_cipher_pkg::PRESENT_PERM32); + end + // update round key, count goes from 1 to 31 (max) + // original 128bit key variant + if (KeyWidth == 128) begin : gen_k128 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_update_key128(round_key[k], round_idx[k]); + // original 80bit key variant + end else if (KeyWidth == 80) begin : gen_k80 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_update_key80(round_key[k], round_idx[k]); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = caliptra_prim_cipher_pkg::present_update_key64(round_key[k], round_idx[k]); + end + end // gen_enc + //////////////////////////////// + end // gen_round + + // This only needs to be applied after the last round. + // Note that for a full-round implementation the output index + // will be 0 for enc/dec for the last round (either due to wraparound or subtraction). + localparam int LastRoundIdx = (Decrypt != 0 || NumRounds == 31) ? 0 : NumRounds+1; + assign data_o = (int'(idx_o) == LastRoundIdx) ? + data_state[NumPhysRounds] ^ + round_key[NumPhysRounds][KeyWidth-1 : KeyWidth-DataWidth] : + data_state[NumPhysRounds]; + + assign key_o = round_key[NumPhysRounds]; + assign idx_o = round_idx[NumPhysRounds]; + + //////////////// + // assertions // + //////////////// + + `CALIPTRA_ASSERT_INIT(SupportedWidths_A, (DataWidth == 64 && KeyWidth inside {80, 128}) || + (DataWidth == 32 && KeyWidth == 64)) + `CALIPTRA_ASSERT_INIT(SupportedNumRounds_A, NumRounds > 0 && NumRounds <= 31) + `CALIPTRA_ASSERT_INIT(SupportedNumPhysRounds0_A, NumPhysRounds > 0 && NumPhysRounds <= NumRounds) + // Currently we do not support other arrangements + `CALIPTRA_ASSERT_INIT(SupportedNumPhysRounds1_A, (NumRounds % NumPhysRounds) == 0) + +endmodule : caliptra_prim_present diff --git a/src/caliptra_prim/rtl/caliptra_prim_ram_1p_adv.sv b/src/caliptra_prim/rtl/caliptra_prim_ram_1p_adv.sv new file mode 100644 index 000000000..0949a0e7c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_ram_1p_adv.sv @@ -0,0 +1,269 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Single-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_ram_1p_adv import caliptra_prim_ram_1p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = caliptra_prim_util_pkg::vbits(Depth) +) ( + input clk_i, + input rst_ni, + + input req_i, + input write_i, + input [Aw-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, + output logic rvalid_o, // read response (rdata_o) is valid + output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + // config + input ram_1p_cfg_t cfg_i +); + + + `CALIPTRA_ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? Width/8 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + // If byte parity is enabled, the write enable bits are used to write memory colums + // with 8 + 1 = 9 bit width (data plus corresponding parity bit). + // If ECC is enabled, the DataBitsPerMask is ignored. + localparam int LocalDataBitsPerMask = (EnableParity) ? 9 : + (EnableECC) ? TotalWidth : + DataBitsPerMask; + + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// + + logic req_q, req_d ; + logic write_q, write_d ; + logic [Aw-1:0] addr_q, addr_d ; + logic [TotalWidth-1:0] wdata_q, wdata_d ; + logic [TotalWidth-1:0] wmask_q, wmask_d ; + logic rvalid_q, rvalid_d, rvalid_sram_q ; + logic [Width-1:0] rdata_q, rdata_d ; + logic [TotalWidth-1:0] rdata_sram ; + logic [1:0] rerror_q, rerror_d ; + + caliptra_prim_generic_ram_1p #( + .MemInitFile (MemInitFile), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (LocalDataBitsPerMask) + ) u_mem ( + .clk_i, + + .req_i (req_q), + .write_i (write_q), + .addr_i (addr_q), + .wdata_i (wdata_q), + .wmask_i (wmask_q), + .rdata_o (rdata_sram), + .cfg_i + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_sram_q <= 1'b0; + end else begin + rvalid_sram_q <= req_q & ~write_q; + end + end + + assign req_d = req_i; + assign write_d = write_i; + assign addr_d = addr_i; + assign rvalid_o = rvalid_q; + assign rdata_o = rdata_q; + assign rerror_o = rerror_q; + + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// + + if (EnableParity == 0 && EnableECC) begin : gen_secded + logic unused_wmask; + assign unused_wmask = ^wmask_i; + + // check supported widths + `CALIPTRA_ASSERT_INIT(SecDecWidth_A, Width inside {16, 32}) + + // the wmask is constantly set to 1 in this case + `CALIPTRA_ASSERT(OnlyWordWritePossibleWithEccPortA_A, req_i |-> + wmask_i == {Width{1'b1}}) + + assign wmask_d = {TotalWidth{1'b1}}; + + if (Width == 16) begin : gen_secded_22_16 + if (HammingECC) begin : gen_hamming + caliptra_prim_secded_inv_hamming_22_16_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + caliptra_prim_secded_inv_hamming_22_16_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end else begin : gen_hsiao + caliptra_prim_secded_inv_22_16_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + caliptra_prim_secded_inv_22_16_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end + end else if (Width == 32) begin : gen_secded_39_32 + if (HammingECC) begin : gen_hamming + caliptra_prim_secded_inv_hamming_39_32_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + caliptra_prim_secded_inv_hamming_39_32_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end else begin : gen_hsiao + caliptra_prim_secded_inv_39_32_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + caliptra_prim_secded_inv_39_32_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end + end + + end else if (EnableParity) begin : gen_byte_parity + + `CALIPTRA_ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + `CALIPTRA_ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + + always_comb begin : p_parity + rerror_d = '0; + for (int i = 0; i < Width/8; i ++) begin + // Data mapping. We have to make 8+1 = 9 bit groups + // that have the same write enable such that FPGA tools + // can map this correctly to BRAM resources. + wmask_d[i*9 +: 8] = wmask_i[i*8 +: 8]; + wdata_d[i*9 +: 8] = wdata_i[i*8 +: 8]; + rdata_d[i*8 +: 8] = rdata_sram[i*9 +: 8]; + + // parity generation (odd parity) + wdata_d[i*9 + 8] = ~(^wdata_i[i*8 +: 8]); + wmask_d[i*9 + 8] = &wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + rerror_d[1] |= ~(^{rdata_sram[i*9 +: 8], rdata_sram[i*9 + 8]}); + end + end + end else begin : gen_nosecded_noparity + assign wmask_d = wmask_i; + assign wdata_d = wdata_i; + + assign rdata_d = rdata_sram[0+:Width]; + assign rerror_d = '0; + end + + assign rvalid_d = rvalid_sram_q; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_q <= '0; + write_q <= '0; + addr_q <= '0; + wdata_q <= '0; + wmask_q <= '0; + end else begin + req_q <= req_d; + write_q <= write_d; + addr_q <= addr_d; + wdata_q <= wdata_d; + wmask_q <= wmask_d; + end + end + end else begin : gen_dirconnect_input + assign req_q = req_d; + assign write_q = write_d; + assign addr_q = addr_d; + assign wdata_q = wdata_d; + assign wmask_q = wmask_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_q <= '0; + rdata_q <= '0; + rerror_q <= '0; + end else begin + rvalid_q <= rvalid_d; + rdata_q <= rdata_d; + // tie to zero if the read data is not valid + rerror_q <= rerror_d & {2{rvalid_d}}; + end + end + end else begin : gen_dirconnect_output + assign rvalid_q = rvalid_d; + assign rdata_q = rdata_d; + // tie to zero if the read data is not valid + assign rerror_q = rerror_d & {2{rvalid_d}}; + end + +endmodule : caliptra_prim_ram_1p_adv diff --git a/src/caliptra_prim/rtl/caliptra_prim_ram_1p_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_ram_1p_pkg.sv new file mode 100644 index 000000000..12c19b77b --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_ram_1p_pkg.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +package caliptra_prim_ram_1p_pkg; + + typedef struct packed { + logic cfg_en; + logic [3:0] cfg; + } cfg_t; + + typedef struct packed { + cfg_t ram_cfg; // configuration for ram + cfg_t rf_cfg; // configuration for regfile + } ram_1p_cfg_t; + + parameter ram_1p_cfg_t RAM_1P_CFG_DEFAULT = '0; + +endpackage // caliptra_prim_ram_1p_pkg diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_dec.sv new file mode 100644 index 000000000..3b01c692a --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_dec.sv @@ -0,0 +1,45 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01496E); + syndrome_o[1] = ^(data_i & 22'h02F20B); + syndrome_o[2] = ^(data_i & 22'h048ED8); + syndrome_o[3] = ^(data_i & 22'h087714); + syndrome_o[4] = ^(data_i & 22'h10ACA5); + syndrome_o[5] = ^(data_i & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_22_16_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_enc.sv new file mode 100644 index 000000000..cd4dc1b4b --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_22_16_enc.sv @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + end + +endmodule : caliptra_prim_secded_22_16_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_dec.sv new file mode 100644 index 000000000..e0a5b3e3d --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_dec.sv @@ -0,0 +1,51 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_28_22_dec ( + input [27:0] data_i, + output logic [21:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 28'h07003FF); + syndrome_o[1] = ^(data_i & 28'h090FC0F); + syndrome_o[2] = ^(data_i & 28'h1271C71); + syndrome_o[3] = ^(data_i & 28'h23B6592); + syndrome_o[4] = ^(data_i & 28'h43DAAA4); + syndrome_o[5] = ^(data_i & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_28_22_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_enc.sv new file mode 100644 index 000000000..c440a0300 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_28_22_enc.sv @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_28_22_enc ( + input [21:0] data_i, + output logic [27:0] data_o +); + + always_comb begin : p_encode + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + end + +endmodule : caliptra_prim_secded_28_22_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_dec.sv new file mode 100644 index 000000000..5fbf53e8c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_dec.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h012606BD25); + syndrome_o[1] = ^(data_i & 39'h02DEBA8050); + syndrome_o[2] = ^(data_i & 39'h04413D89AA); + syndrome_o[3] = ^(data_i & 39'h0831234ED1); + syndrome_o[4] = ^(data_i & 39'h10C2C1323B); + syndrome_o[5] = ^(data_i & 39'h202DCC624C); + syndrome_o[6] = ^(data_i & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_39_32_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_enc.sv new file mode 100644 index 000000000..522183423 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_39_32_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + end + +endmodule : caliptra_prim_secded_39_32_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_dec.sv new file mode 100644 index 000000000..34f17d7b2 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_dec.sv @@ -0,0 +1,87 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_64_57_dec ( + input [63:0] data_i, + output logic [56:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 64'h0303FFF800007FFF); + syndrome_o[1] = ^(data_i & 64'h057C1FF801FF801F); + syndrome_o[2] = ^(data_i & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^(data_i & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^(data_i & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^(data_i & 64'h41F7BB56D5525488); + syndrome_o[6] = ^(data_i & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_64_57_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_enc.sv new file mode 100644 index 000000000..4d068b32c --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_64_57_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_64_57_enc ( + input [56:0] data_i, + output logic [63:0] data_o +); + + always_comb begin : p_encode + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + end + +endmodule : caliptra_prim_secded_64_57_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_dec.sv new file mode 100644 index 000000000..736a4139f --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_dec.sv @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^(data_i & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^(data_i & 72'h0467003FF003E007C1); + syndrome_o[3] = ^(data_i & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^(data_i & 72'h10B671C711C4438884); + syndrome_o[5] = ^(data_i & 72'h20B5B65926488C9108); + syndrome_o[6] = ^(data_i & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^(data_i & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_72_64_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_enc.sv new file mode 100644 index 000000000..8f0252edd --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_72_64_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + end + +endmodule : caliptra_prim_secded_72_64_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_dec.sv new file mode 100644 index 000000000..020346319 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_dec.sv @@ -0,0 +1,45 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01AD5B); + syndrome_o[1] = ^(data_i & 22'h02366D); + syndrome_o[2] = ^(data_i & 22'h04C78E); + syndrome_o[3] = ^(data_i & 22'h0807F0); + syndrome_o[4] = ^(data_i & 22'h10F800); + syndrome_o[5] = ^(data_i & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + end +endmodule : caliptra_prim_secded_hamming_22_16_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_enc.sv new file mode 100644 index 000000000..cdb2e3242 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_22_16_enc.sv @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + end + +endmodule : caliptra_prim_secded_hamming_22_16_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_dec.sv new file mode 100644 index 000000000..770ccd2dc --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_dec.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h0156AAAD5B); + syndrome_o[1] = ^(data_i & 39'h029B33366D); + syndrome_o[2] = ^(data_i & 39'h04E3C3C78E); + syndrome_o[3] = ^(data_i & 39'h0803FC07F0); + syndrome_o[4] = ^(data_i & 39'h1003FFF800); + syndrome_o[5] = ^(data_i & 39'h20FC000000); + syndrome_o[6] = ^(data_i & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + end +endmodule : caliptra_prim_secded_hamming_39_32_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_enc.sv new file mode 100644 index 000000000..e80ccfbf7 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_39_32_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + end + +endmodule : caliptra_prim_secded_hamming_39_32_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_dec.sv new file mode 100644 index 000000000..5809cfc34 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_dec.sv @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 72'h02CD9999999B33366D); + syndrome_o[2] = ^(data_i & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 72'h40FE00000000000000); + syndrome_o[7] = ^(data_i & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : caliptra_prim_secded_hamming_72_64_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_enc.sv new file mode 100644 index 000000000..6289dd369 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_72_64_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + end + +endmodule : caliptra_prim_secded_hamming_72_64_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_dec.sv new file mode 100644 index 000000000..3178eedc2 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_dec.sv @@ -0,0 +1,99 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_76_68_dec ( + input [75:0] data_i, + output logic [67:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^(data_i & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 76'h40FFE00000000000000); + syndrome_o[7] = ^(data_i & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : caliptra_prim_secded_hamming_76_68_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_enc.sv new file mode 100644 index 000000000..13643a457 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_hamming_76_68_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_hamming_76_68_enc ( + input [67:0] data_i, + output logic [75:0] data_o +); + + always_comb begin : p_encode + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + end + +endmodule : caliptra_prim_secded_hamming_76_68_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_dec.sv new file mode 100644 index 000000000..f89aa8109 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_dec.sv @@ -0,0 +1,45 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01496E); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02F20B); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h048ED8); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h087714); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10ACA5); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_inv_22_16_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_enc.sv new file mode 100644 index 000000000..c8726f793 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_22_16_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + data_o ^= 22'h2A0000; + end + +endmodule : caliptra_prim_secded_inv_22_16_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_dec.sv new file mode 100644 index 000000000..57b2c1742 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_dec.sv @@ -0,0 +1,51 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_28_22_dec ( + input [27:0] data_i, + output logic [21:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 28'hA800000) & 28'h07003FF); + syndrome_o[1] = ^((data_i ^ 28'hA800000) & 28'h090FC0F); + syndrome_o[2] = ^((data_i ^ 28'hA800000) & 28'h1271C71); + syndrome_o[3] = ^((data_i ^ 28'hA800000) & 28'h23B6592); + syndrome_o[4] = ^((data_i ^ 28'hA800000) & 28'h43DAAA4); + syndrome_o[5] = ^((data_i ^ 28'hA800000) & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_inv_28_22_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_enc.sv new file mode 100644 index 000000000..a847398b4 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_28_22_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_28_22_enc ( + input [21:0] data_i, + output logic [27:0] data_o +); + + always_comb begin : p_encode + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + data_o ^= 28'hA800000; + end + +endmodule : caliptra_prim_secded_inv_28_22_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_dec.sv new file mode 100644 index 000000000..677c17452 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_dec.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h012606BD25); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h02DEBA8050); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04413D89AA); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0831234ED1); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h10C2C1323B); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h202DCC624C); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_inv_39_32_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_enc.sv new file mode 100644 index 000000000..1cbd66a7e --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_39_32_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + data_o ^= 39'h2A00000000; + end + +endmodule : caliptra_prim_secded_inv_39_32_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_dec.sv new file mode 100644 index 000000000..28fad1dc4 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_dec.sv @@ -0,0 +1,87 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_64_57_dec ( + input [63:0] data_i, + output logic [56:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 64'h5400000000000000) & 64'h0303FFF800007FFF); + syndrome_o[1] = ^((data_i ^ 64'h5400000000000000) & 64'h057C1FF801FF801F); + syndrome_o[2] = ^((data_i ^ 64'h5400000000000000) & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^((data_i ^ 64'h5400000000000000) & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^((data_i ^ 64'h5400000000000000) & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^((data_i ^ 64'h5400000000000000) & 64'h41F7BB56D5525488); + syndrome_o[6] = ^((data_i ^ 64'h5400000000000000) & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_inv_64_57_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_enc.sv new file mode 100644 index 000000000..b491a4204 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_64_57_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_64_57_enc ( + input [56:0] data_i, + output logic [63:0] data_o +); + + always_comb begin : p_encode + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + data_o ^= 64'h5400000000000000; + end + +endmodule : caliptra_prim_secded_inv_64_57_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_dec.sv new file mode 100644 index 000000000..13fa06a40 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_dec.sv @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0467003FF003E007C1); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h10B671C711C4438884); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h20B5B65926488C9108); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : caliptra_prim_secded_inv_72_64_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_enc.sv new file mode 100644 index 000000000..ba2d458cb --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_72_64_enc.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + data_o ^= 72'hAA0000000000000000; + end + +endmodule : caliptra_prim_secded_inv_72_64_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_dec.sv new file mode 100644 index 000000000..7a76ccc2a --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_dec.sv @@ -0,0 +1,45 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01AD5B); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02366D); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h04C78E); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h0807F0); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10F800); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + end +endmodule : caliptra_prim_secded_inv_hamming_22_16_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_enc.sv new file mode 100644 index 000000000..2190d1aa4 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_22_16_enc.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + data_o ^= 22'h2A0000; + end + +endmodule : caliptra_prim_secded_inv_hamming_22_16_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_dec.sv new file mode 100644 index 000000000..e63f57725 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_dec.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h0156AAAD5B); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h029B33366D); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04E3C3C78E); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0803FC07F0); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h1003FFF800); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h20FC000000); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + end +endmodule : caliptra_prim_secded_inv_hamming_39_32_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_enc.sv new file mode 100644 index 000000000..5ca158096 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_39_32_enc.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + data_o ^= 39'h2A00000000; + end + +endmodule : caliptra_prim_secded_inv_hamming_39_32_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_dec.sv new file mode 100644 index 000000000..d53e17cad --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_dec.sv @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h02CD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40FE00000000000000); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : caliptra_prim_secded_inv_hamming_72_64_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_enc.sv new file mode 100644 index 000000000..933078ef6 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_72_64_enc.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + data_o ^= 72'hAA0000000000000000; + end + +endmodule : caliptra_prim_secded_inv_hamming_72_64_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_dec.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_dec.sv new file mode 100644 index 000000000..583848ad8 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_dec.sv @@ -0,0 +1,99 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED decoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_76_68_dec ( + input [75:0] data_i, + output logic [67:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 76'hAA00000000000000000) & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 76'hAA00000000000000000) & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 76'hAA00000000000000000) & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 76'hAA00000000000000000) & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 76'hAA00000000000000000) & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 76'hAA00000000000000000) & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 76'hAA00000000000000000) & 76'h40FFE00000000000000); + syndrome_o[7] = ^((data_i ^ 76'hAA00000000000000000) & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : caliptra_prim_secded_inv_hamming_76_68_dec diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_enc.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_enc.sv new file mode 100644 index 000000000..ebb89bed4 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_inv_hamming_76_68_enc.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encoder generated by util/design/secded_gen.py + +module caliptra_prim_secded_inv_hamming_76_68_enc ( + input [67:0] data_i, + output logic [75:0] data_o +); + + always_comb begin : p_encode + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + data_o ^= 76'hAA00000000000000000; + end + +endmodule : caliptra_prim_secded_inv_hamming_76_68_enc diff --git a/src/caliptra_prim/rtl/caliptra_prim_secded_pkg.sv b/src/caliptra_prim/rtl/caliptra_prim_secded_pkg.sv new file mode 100644 index 000000000..7088e5804 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_secded_pkg.sv @@ -0,0 +1,1787 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED package generated by +// util/design/secded_gen.py from util/design/data/secded_cfg.hjson + +package caliptra_prim_secded_pkg; + + typedef enum int { + SecdedNone, + Secded_22_16, + Secded_28_22, + Secded_39_32, + Secded_64_57, + Secded_72_64, + SecdedHamming_22_16, + SecdedHamming_39_32, + SecdedHamming_72_64, + SecdedHamming_76_68, + SecdedInv_22_16, + SecdedInv_28_22, + SecdedInv_39_32, + SecdedInv_64_57, + SecdedInv_72_64, + SecdedInvHamming_22_16, + SecdedInvHamming_39_32, + SecdedInvHamming_72_64, + SecdedInvHamming_76_68 + } prim_secded_e; + + function automatic int get_ecc_data_width(prim_secded_e ecc_type); + case (ecc_type) + Secded_22_16: return 16; + Secded_28_22: return 22; + Secded_39_32: return 32; + Secded_64_57: return 57; + Secded_72_64: return 64; + SecdedHamming_22_16: return 16; + SecdedHamming_39_32: return 32; + SecdedHamming_72_64: return 64; + SecdedHamming_76_68: return 68; + SecdedInv_22_16: return 16; + SecdedInv_28_22: return 22; + SecdedInv_39_32: return 32; + SecdedInv_64_57: return 57; + SecdedInv_72_64: return 64; + SecdedInvHamming_22_16: return 16; + SecdedInvHamming_39_32: return 32; + SecdedInvHamming_72_64: return 64; + SecdedInvHamming_76_68: return 68; + // Return a non-zero width to avoid VCS compile issues + default: return 32; + endcase + endfunction + + function automatic int get_ecc_parity_width(prim_secded_e ecc_type); + case (ecc_type) + Secded_22_16: return 6; + Secded_28_22: return 6; + Secded_39_32: return 7; + Secded_64_57: return 7; + Secded_72_64: return 8; + SecdedHamming_22_16: return 6; + SecdedHamming_39_32: return 7; + SecdedHamming_72_64: return 8; + SecdedHamming_76_68: return 8; + SecdedInv_22_16: return 6; + SecdedInv_28_22: return 6; + SecdedInv_39_32: return 7; + SecdedInv_64_57: return 7; + SecdedInv_72_64: return 8; + SecdedInvHamming_22_16: return 6; + SecdedInvHamming_39_32: return 7; + SecdedInvHamming_72_64: return 8; + SecdedInvHamming_76_68: return 8; + default: return 0; + endcase + endfunction + + parameter logic [5:0] Secded2216ZeroEcc = 6'h0; + parameter logic [21:0] Secded2216ZeroWord = 22'h0; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_22_16_t; + + parameter logic [5:0] Secded2822ZeroEcc = 6'h0; + parameter logic [27:0] Secded2822ZeroWord = 28'h0; + + typedef struct packed { + logic [21:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_28_22_t; + + parameter logic [6:0] Secded3932ZeroEcc = 7'h0; + parameter logic [38:0] Secded3932ZeroWord = 39'h0; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_39_32_t; + + parameter logic [6:0] Secded6457ZeroEcc = 7'h0; + parameter logic [63:0] Secded6457ZeroWord = 64'h0; + + typedef struct packed { + logic [56:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_64_57_t; + + parameter logic [7:0] Secded7264ZeroEcc = 8'h0; + parameter logic [71:0] Secded7264ZeroWord = 72'h0; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_72_64_t; + + parameter logic [5:0] SecdedHamming2216ZeroEcc = 6'h0; + parameter logic [21:0] SecdedHamming2216ZeroWord = 22'h0; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_hamming_22_16_t; + + parameter logic [6:0] SecdedHamming3932ZeroEcc = 7'h0; + parameter logic [38:0] SecdedHamming3932ZeroWord = 39'h0; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_hamming_39_32_t; + + parameter logic [7:0] SecdedHamming7264ZeroEcc = 8'h0; + parameter logic [71:0] SecdedHamming7264ZeroWord = 72'h0; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_hamming_72_64_t; + + parameter logic [7:0] SecdedHamming7668ZeroEcc = 8'h0; + parameter logic [75:0] SecdedHamming7668ZeroWord = 76'h0; + + typedef struct packed { + logic [67:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_hamming_76_68_t; + + parameter logic [5:0] SecdedInv2216ZeroEcc = 6'h2A; + parameter logic [21:0] SecdedInv2216ZeroWord = 22'h2A0000; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_22_16_t; + + parameter logic [5:0] SecdedInv2822ZeroEcc = 6'h2A; + parameter logic [27:0] SecdedInv2822ZeroWord = 28'hA800000; + + typedef struct packed { + logic [21:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_28_22_t; + + parameter logic [6:0] SecdedInv3932ZeroEcc = 7'h2A; + parameter logic [38:0] SecdedInv3932ZeroWord = 39'h2A00000000; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_39_32_t; + + parameter logic [6:0] SecdedInv6457ZeroEcc = 7'h2A; + parameter logic [63:0] SecdedInv6457ZeroWord = 64'h5400000000000000; + + typedef struct packed { + logic [56:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_64_57_t; + + parameter logic [7:0] SecdedInv7264ZeroEcc = 8'hAA; + parameter logic [71:0] SecdedInv7264ZeroWord = 72'hAA0000000000000000; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_72_64_t; + + parameter logic [5:0] SecdedInvHamming2216ZeroEcc = 6'h2A; + parameter logic [21:0] SecdedInvHamming2216ZeroWord = 22'h2A0000; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_22_16_t; + + parameter logic [6:0] SecdedInvHamming3932ZeroEcc = 7'h2A; + parameter logic [38:0] SecdedInvHamming3932ZeroWord = 39'h2A00000000; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_39_32_t; + + parameter logic [7:0] SecdedInvHamming7264ZeroEcc = 8'hAA; + parameter logic [71:0] SecdedInvHamming7264ZeroWord = 72'hAA0000000000000000; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_72_64_t; + + parameter logic [7:0] SecdedInvHamming7668ZeroEcc = 8'hAA; + parameter logic [75:0] SecdedInvHamming7668ZeroWord = 76'hAA00000000000000000; + + typedef struct packed { + logic [67:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_76_68_t; + + function automatic logic [21:0] + prim_secded_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + return data_o; + endfunction + + function automatic secded_22_16_t + prim_secded_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01496E); + syndrome_o[1] = ^(data_i & 22'h02F20B); + syndrome_o[2] = ^(data_i & 22'h048ED8); + syndrome_o[3] = ^(data_i & 22'h087714); + syndrome_o[4] = ^(data_i & 22'h10ACA5); + syndrome_o[5] = ^(data_i & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [27:0] + prim_secded_28_22_enc (logic [21:0] data_i); + logic [27:0] data_o; + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + return data_o; + endfunction + + function automatic secded_28_22_t + prim_secded_28_22_dec (logic [27:0] data_i); + logic [21:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_28_22_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 28'h07003FF); + syndrome_o[1] = ^(data_i & 28'h090FC0F); + syndrome_o[2] = ^(data_i & 28'h1271C71); + syndrome_o[3] = ^(data_i & 28'h23B6592); + syndrome_o[4] = ^(data_i & 28'h43DAAA4); + syndrome_o[5] = ^(data_i & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + return data_o; + endfunction + + function automatic secded_39_32_t + prim_secded_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h012606BD25); + syndrome_o[1] = ^(data_i & 39'h02DEBA8050); + syndrome_o[2] = ^(data_i & 39'h04413D89AA); + syndrome_o[3] = ^(data_i & 39'h0831234ED1); + syndrome_o[4] = ^(data_i & 39'h10C2C1323B); + syndrome_o[5] = ^(data_i & 39'h202DCC624C); + syndrome_o[6] = ^(data_i & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [63:0] + prim_secded_64_57_enc (logic [56:0] data_i); + logic [63:0] data_o; + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + return data_o; + endfunction + + function automatic secded_64_57_t + prim_secded_64_57_dec (logic [63:0] data_i); + logic [56:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_64_57_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 64'h0303FFF800007FFF); + syndrome_o[1] = ^(data_i & 64'h057C1FF801FF801F); + syndrome_o[2] = ^(data_i & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^(data_i & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^(data_i & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^(data_i & 64'h41F7BB56D5525488); + syndrome_o[6] = ^(data_i & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + return data_o; + endfunction + + function automatic secded_72_64_t + prim_secded_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^(data_i & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^(data_i & 72'h0467003FF003E007C1); + syndrome_o[3] = ^(data_i & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^(data_i & 72'h10B671C711C4438884); + syndrome_o[5] = ^(data_i & 72'h20B5B65926488C9108); + syndrome_o[6] = ^(data_i & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^(data_i & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_hamming_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + return data_o; + endfunction + + function automatic secded_hamming_22_16_t + prim_secded_hamming_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01AD5B); + syndrome_o[1] = ^(data_i & 22'h02366D); + syndrome_o[2] = ^(data_i & 22'h04C78E); + syndrome_o[3] = ^(data_i & 22'h0807F0); + syndrome_o[4] = ^(data_i & 22'h10F800); + syndrome_o[5] = ^(data_i & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_hamming_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_39_32_t + prim_secded_hamming_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h0156AAAD5B); + syndrome_o[1] = ^(data_i & 39'h029B33366D); + syndrome_o[2] = ^(data_i & 39'h04E3C3C78E); + syndrome_o[3] = ^(data_i & 39'h0803FC07F0); + syndrome_o[4] = ^(data_i & 39'h1003FFF800); + syndrome_o[5] = ^(data_i & 39'h20FC000000); + syndrome_o[6] = ^(data_i & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_hamming_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_72_64_t + prim_secded_hamming_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 72'h02CD9999999B33366D); + syndrome_o[2] = ^(data_i & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 72'h40FE00000000000000); + syndrome_o[7] = ^(data_i & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [75:0] + prim_secded_hamming_76_68_enc (logic [67:0] data_i); + logic [75:0] data_o; + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_76_68_t + prim_secded_hamming_76_68_dec (logic [75:0] data_i); + logic [67:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_76_68_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^(data_i & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 76'h40FFE00000000000000); + syndrome_o[7] = ^(data_i & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_inv_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + data_o ^= 22'h2A0000; + return data_o; + endfunction + + function automatic secded_inv_22_16_t + prim_secded_inv_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01496E); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02F20B); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h048ED8); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h087714); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10ACA5); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [27:0] + prim_secded_inv_28_22_enc (logic [21:0] data_i); + logic [27:0] data_o; + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + data_o ^= 28'hA800000; + return data_o; + endfunction + + function automatic secded_inv_28_22_t + prim_secded_inv_28_22_dec (logic [27:0] data_i); + logic [21:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_28_22_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 28'hA800000) & 28'h07003FF); + syndrome_o[1] = ^((data_i ^ 28'hA800000) & 28'h090FC0F); + syndrome_o[2] = ^((data_i ^ 28'hA800000) & 28'h1271C71); + syndrome_o[3] = ^((data_i ^ 28'hA800000) & 28'h23B6592); + syndrome_o[4] = ^((data_i ^ 28'hA800000) & 28'h43DAAA4); + syndrome_o[5] = ^((data_i ^ 28'hA800000) & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_inv_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + data_o ^= 39'h2A00000000; + return data_o; + endfunction + + function automatic secded_inv_39_32_t + prim_secded_inv_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h012606BD25); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h02DEBA8050); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04413D89AA); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0831234ED1); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h10C2C1323B); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h202DCC624C); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [63:0] + prim_secded_inv_64_57_enc (logic [56:0] data_i); + logic [63:0] data_o; + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + data_o ^= 64'h5400000000000000; + return data_o; + endfunction + + function automatic secded_inv_64_57_t + prim_secded_inv_64_57_dec (logic [63:0] data_i); + logic [56:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_64_57_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 64'h5400000000000000) & 64'h0303FFF800007FFF); + syndrome_o[1] = ^((data_i ^ 64'h5400000000000000) & 64'h057C1FF801FF801F); + syndrome_o[2] = ^((data_i ^ 64'h5400000000000000) & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^((data_i ^ 64'h5400000000000000) & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^((data_i ^ 64'h5400000000000000) & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^((data_i ^ 64'h5400000000000000) & 64'h41F7BB56D5525488); + syndrome_o[6] = ^((data_i ^ 64'h5400000000000000) & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_inv_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + data_o ^= 72'hAA0000000000000000; + return data_o; + endfunction + + function automatic secded_inv_72_64_t + prim_secded_inv_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0467003FF003E007C1); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h10B671C711C4438884); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h20B5B65926488C9108); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_inv_hamming_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + data_o ^= 22'h2A0000; + return data_o; + endfunction + + function automatic secded_inv_hamming_22_16_t + prim_secded_inv_hamming_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01AD5B); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02366D); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h04C78E); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h0807F0); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10F800); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_inv_hamming_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + data_o ^= 39'h2A00000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_39_32_t + prim_secded_inv_hamming_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h0156AAAD5B); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h029B33366D); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04E3C3C78E); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0803FC07F0); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h1003FFF800); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h20FC000000); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_inv_hamming_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + data_o ^= 72'hAA0000000000000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_72_64_t + prim_secded_inv_hamming_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h02CD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40FE00000000000000); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [75:0] + prim_secded_inv_hamming_76_68_enc (logic [67:0] data_i); + logic [75:0] data_o; + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + data_o ^= 76'hAA00000000000000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_76_68_t + prim_secded_inv_hamming_76_68_dec (logic [75:0] data_i); + logic [67:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_76_68_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 76'hAA00000000000000000) & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 76'hAA00000000000000000) & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 76'hAA00000000000000000) & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 76'hAA00000000000000000) & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 76'hAA00000000000000000) & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 76'hAA00000000000000000) & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 76'hAA00000000000000000) & 76'h40FFE00000000000000); + syndrome_o[7] = ^((data_i ^ 76'hAA00000000000000000) & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + +endpackage diff --git a/src/caliptra_prim/rtl/caliptra_prim_subreg.sv b/src/caliptra_prim/rtl/caliptra_prim_subreg.sv index 8bb91d955..53511f1a1 100644 --- a/src/caliptra_prim/rtl/caliptra_prim_subreg.sv +++ b/src/caliptra_prim/rtl/caliptra_prim_subreg.sv @@ -9,7 +9,8 @@ module caliptra_prim_subreg #( parameter int DW = 32, parameter sw_access_e SwAccess = SwAccessRW, - parameter logic [DW-1:0] RESVAL = '0 // reset value + parameter logic [DW-1:0] RESVAL = '0 , // reset value + parameter bit Mubi = 1'b0 ) ( input clk_i, input rst_ni, @@ -39,7 +40,8 @@ module caliptra_prim_subreg caliptra_prim_subreg_arb #( .DW ( DW ), - .SwAccess ( SwAccess ) + .SwAccess ( SwAccess ), + .Mubi ( Mubi ) ) wr_en_data_arb ( .we, .wd, @@ -61,6 +63,13 @@ module caliptra_prim_subreg // feed back out for consolidation assign ds = wr_en ? wr_data : qs; assign qe = wr_en; - assign qs = q; + + if (SwAccess == SwAccessRC) begin : gen_rc + // In case of a SW RC colliding with a HW write, SW gets the value written by HW + // but the register is cleared to 0. See #5416 for a discussion. + assign qs = de && we ? d : q; + end else begin : gen_no_rc + assign qs = q; + end endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_subreg_arb.sv b/src/caliptra_prim/rtl/caliptra_prim_subreg_arb.sv index d660a5465..be3f5b978 100644 --- a/src/caliptra_prim/rtl/caliptra_prim_subreg_arb.sv +++ b/src/caliptra_prim/rtl/caliptra_prim_subreg_arb.sv @@ -8,7 +8,8 @@ module caliptra_prim_subreg_arb import caliptra_prim_subreg_pkg::*; #( parameter int DW = 32, - parameter sw_access_e SwAccess = SwAccessRW + parameter sw_access_e SwAccess = SwAccessRW, + parameter bit Mubi = 1'b0 ) ( // From SW: valid for RW, WO, W1C, W1S, W0C, RC. // In case of RC, top connects read pulse to we. @@ -26,13 +27,18 @@ module caliptra_prim_subreg_arb output logic wr_en, output logic [DW-1:0] wr_data ); + import caliptra_prim_mubi_pkg::*; if (SwAccess inside {SwAccessRW, SwAccessWO}) begin : gen_w assign wr_en = we | de; assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority // Unused q - Prevent lint errors. logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off assign unused_q = q; + //VCS coverage on + // pragma coverage on end else if (SwAccess == SwAccessRO) begin : gen_ro assign wr_en = de; assign wr_data = d; @@ -40,32 +46,128 @@ module caliptra_prim_subreg_arb logic unused_we; logic [DW-1:0] unused_wd; logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off assign unused_we = we; assign unused_wd = wd; assign unused_q = q; + //VCS coverage on + // pragma coverage on end else if (SwAccess == SwAccessW1S) begin : gen_w1s // If SwAccess is W1S, then assume hw tries to clear. // So, give a chance HW to clear when SW tries to set. // If both try to set/clr at the same bit pos, SW wins. assign wr_en = we | de; - assign wr_data = (de ? d : q) | (we ? wd : '0); + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = caliptra_prim_mubi_pkg::mubi4_or_hi(caliptra_prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi4_t'(wd) : + caliptra_prim_mubi_pkg::MuBi4False)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = caliptra_prim_mubi_pkg::mubi8_or_hi(caliptra_prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi8_t'(wd) : + caliptra_prim_mubi_pkg::MuBi8False)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = caliptra_prim_mubi_pkg::mubi12_or_hi(caliptra_prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi12_t'(wd) : + caliptra_prim_mubi_pkg::MuBi12False)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = caliptra_prim_mubi_pkg::mubi16_or_hi(caliptra_prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi16_t'(wd) : + caliptra_prim_mubi_pkg::MuBi16False)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) | (we ? wd : '0); + end end else if (SwAccess == SwAccessW1C) begin : gen_w1c // If SwAccess is W1C, then assume hw tries to set. // So, give a chance HW to set when SW tries to clear. // If both try to set/clr at the same bit pos, SW wins. assign wr_en = we | de; - assign wr_data = (de ? d : q) & (we ? ~wd : '1); + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = caliptra_prim_mubi_pkg::mubi4_and_hi(caliptra_prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi4_t'(~wd) : + caliptra_prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = caliptra_prim_mubi_pkg::mubi8_and_hi(caliptra_prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi8_t'(~wd) : + caliptra_prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = caliptra_prim_mubi_pkg::mubi12_and_hi(caliptra_prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi12_t'(~wd) : + caliptra_prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = caliptra_prim_mubi_pkg::mubi16_and_hi(caliptra_prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi16_t'(~wd) : + caliptra_prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? ~wd : '1); + end end else if (SwAccess == SwAccessW0C) begin : gen_w0c assign wr_en = we | de; - assign wr_data = (de ? d : q) & (we ? wd : '1); + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = caliptra_prim_mubi_pkg::mubi4_and_hi(caliptra_prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi4_t'(wd) : + caliptra_prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = caliptra_prim_mubi_pkg::mubi8_and_hi(caliptra_prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi8_t'(wd) : + caliptra_prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = caliptra_prim_mubi_pkg::mubi12_and_hi(caliptra_prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi12_t'(wd) : + caliptra_prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = caliptra_prim_mubi_pkg::mubi16_and_hi(caliptra_prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi16_t'(wd) : + caliptra_prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? wd : '1); + end end else if (SwAccess == SwAccessRC) begin : gen_rc // This swtype is not recommended but exists for compatibility. // WARN: we signal is actually read signal not write enable. assign wr_en = we | de; - assign wr_data = (de ? d : q) & (we ? '0 : '1); + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = caliptra_prim_mubi_pkg::mubi4_and_hi(caliptra_prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::MuBi4False : + caliptra_prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = caliptra_prim_mubi_pkg::mubi8_and_hi(caliptra_prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::MuBi8False : + caliptra_prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = caliptra_prim_mubi_pkg::mubi12_and_hi(caliptra_prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::MuBi12False : + caliptra_prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = caliptra_prim_mubi_pkg::mubi16_and_hi(caliptra_prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? caliptra_prim_mubi_pkg::mubi16_t'(wd) : + caliptra_prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? '0 : '1); + end // Unused wd - Prevent lint errors. logic [DW-1:0] unused_wd; + //VCS coverage off + // pragma coverage off assign unused_wd = wd; + //VCS coverage on + // pragma coverage on end else begin : gen_hw assign wr_en = de; assign wr_data = d; @@ -73,9 +175,13 @@ module caliptra_prim_subreg_arb logic unused_we; logic [DW-1:0] unused_wd; logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off assign unused_we = we; assign unused_wd = wd; assign unused_q = q; + //VCS coverage on + // pragma coverage on end endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_subreg_shadow.sv b/src/caliptra_prim/rtl/caliptra_prim_subreg_shadow.sv new file mode 100644 index 000000000..7ecce9376 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_subreg_shadow.sv @@ -0,0 +1,196 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Shadowed register slice conforming to Comportibility guide. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_subreg_shadow + import caliptra_prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, + parameter logic [DW-1:0] RESVAL = '0, // reset value + parameter bit Mubi = 1'b0 +) ( + input clk_i, + input rst_ni, + input rst_shadowed_ni, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC. + // SW reads clear phase unless SwAccess is RO. + input re, + // In case of RC, top connects read pulse to we. + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO. + input de, + input [DW-1:0] d, + + // Output to HW and Reg Read + output logic qe, + output logic [DW-1:0] q, + output logic [DW-1:0] ds, + output logic [DW-1:0] qs, + + // Phase output to HW + output logic phase, + + // Error conditions + output logic err_update, + output logic err_storage +); + + // Since the shadow register works with the 1's complement value, + // we need to invert the polarity of the SW access if it is either "W1S" or "W0C". + // W1C is forbidden since the W0S complement is not implemented. + `CALIPTRA_ASSERT_INIT(CheckSwAccessIsLegal_A, + SwAccess inside {SwAccessRW, SwAccessRO, SwAccessWO, SwAccessW1S, SwAccessW0C}) + localparam sw_access_e InvertedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessW0C : + (SwAccess == SwAccessW0C) ? SwAccessW1S : SwAccess; + + // For the staging register, we set the SwAccess to RW in case of W1S and W0C in + // order to always capture the data value on the first write operation - no matter + // whether the data value will actually have an effect. That way, we can still capture + // inconsistent double writes which would otherwise be ignored due to the data value filtering + // effect that W1S and W0C can have. + localparam sw_access_e StagedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessRW : + (SwAccess == SwAccessW0C) ? SwAccessRW : SwAccess; + + // Subreg control signals + logic phase_clear; + logic phase_q; + logic staged_we, shadow_we, committed_we; + logic staged_de, shadow_de, committed_de; + + // Subreg status and data signals + logic committed_qe; + logic [DW-1:0] staged_q, shadow_q, committed_q; + logic [DW-1:0] committed_qs; + + // Effective write enable and write data signals. + // These depend on we, de and wd, d, q as well as SwAccess. + logic wr_en; + logic [DW-1:0] wr_data; + + caliptra_prim_subreg_arb #( + .DW ( DW ), + .SwAccess ( SwAccess ) + ) wr_en_data_arb ( + .we ( we ), + .wd ( wd ), + .de ( de ), + .d ( d ), + .q ( q ), + .wr_en ( wr_en ), + .wr_data ( wr_data ) + ); + + // Phase clearing: + // - SW reads clear phase unless SwAccess is RO. + // - In case of RO, SW should not interfere with update process. + assign phase_clear = (SwAccess == SwAccessRO) ? 1'b0 : re; + + // Phase tracker: + // - Reads from SW clear the phase back to 0. + // - Writes have priority (can come from SW or HW). + always_ff @(posedge clk_i or negedge rst_ni) begin : phase_reg + if (!rst_ni) begin + phase_q <= 1'b0; + end else if (wr_en && !err_storage) begin + phase_q <= ~phase_q; + end else if (phase_clear || err_storage) begin + phase_q <= 1'b0; + end + end + + // The staged register: + // - Holds the 1's complement value. + // - Written in Phase 0. + // - Once storage error occurs, do not allow any further update until reset + assign staged_we = we & ~phase_q & ~err_storage; + assign staged_de = de & ~phase_q & ~err_storage; + caliptra_prim_subreg #( + .DW ( DW ), + .SwAccess ( StagedSwAccess ), + .RESVAL ( ~RESVAL ) + ) staged_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .we ( staged_we ), + .wd ( ~wr_data ), + .de ( staged_de ), + .d ( ~d ), + .qe ( ), + .q ( staged_q ), + .ds ( ), + .qs ( ) + ); + + // The shadow register: + // - Holds the 1's complement value. + // - Written in Phase 1. + // - Writes are ignored in case of update errors. + // - Gets the value from the staged register. + // - Once storage error occurs, do not allow any further update until reset + assign shadow_we = we & phase_q & ~err_update & ~err_storage; + assign shadow_de = de & phase_q & ~err_update & ~err_storage; + caliptra_prim_subreg #( + .DW ( DW ), + .SwAccess ( InvertedSwAccess ), + .RESVAL ( ~RESVAL ) + ) shadow_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_shadowed_ni ), + .we ( shadow_we ), + .wd ( staged_q ), + .de ( shadow_de ), + .d ( staged_q ), + .qe ( ), + .q ( shadow_q ), + .ds ( ), + .qs ( ) + ); + + // The committed register: + // - Written in Phase 1. + // - Writes are ignored in case of update errors. + assign committed_we = shadow_we; + assign committed_de = shadow_de; + caliptra_prim_subreg #( + .DW ( DW ), + .SwAccess ( SwAccess ), + .RESVAL ( RESVAL ) + ) committed_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .we ( committed_we ), + .wd ( wr_data ), + .de ( committed_de ), + .d ( d ), + .qe ( committed_qe ), + .q ( committed_q ), + .ds ( ds ), + .qs ( committed_qs ) + ); + + // Output phase for hwext. + assign phase = phase_q; + + // Error detection - all bits must match. + assign err_update = (~staged_q != wr_data) ? phase_q & wr_en : 1'b0; + assign err_storage = (~shadow_q != committed_q); + + // Remaining output assignments + assign qe = committed_qe; + assign q = committed_q; + assign qs = committed_qs; + + // prim_subreg_shadow does not support multi-bit software access yet + `CALIPTRA_ASSERT_NEVER(MubiIsNotYetSupported_A, Mubi) + logic unused_mubi; + assign unused_mubi = Mubi; + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_sync_reqack.sv b/src/caliptra_prim/rtl/caliptra_prim_sync_reqack.sv new file mode 100644 index 000000000..d5958d68d --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_sync_reqack.sv @@ -0,0 +1,404 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// REQ/ACK synchronizer +// +// This module synchronizes a REQ/ACK handshake across a clock domain crossing. +// Both domains will see a handshake with the duration of one clock cycle. +// +// Notes: +// - Once asserted, the source (SRC) domain is not allowed to de-assert REQ without ACK. +// - The destination (DST) domain is not allowed to send an ACK without a REQ. +// - When resetting one domain, also the other domain needs to be reset with both resets being +// active at the same time. +// - This module works both when syncing from a faster to a slower clock domain and vice versa. +// - Internally, this module uses a non-return-to-zero (NRZ), two-phase handshake protocol by +// default. Assuming the DST domain responds with an ACK immediately, the latency from asserting +// the REQ in the SRC domain is: +// - 1 source + 2 destination clock cycles until the handshake is performed in the DST domain, +// - 1 source + 2 destination + 1 destination + 2 source clock cycles until the handshake is +// performed in the SRC domain. +// - Optionally, the module can also use a return-to-zero (RZ), four-phase handshake protocol. +// That one has lower throughput, but it is safe to reset either domain in isolation, since the +// two FSMs cannot get out of sync due to persistent EVEN/ODD states. The handshake latencies +// are the same as for the NRZ protocol, but the throughput is half that of the NRZ protocol +// since the signals neet to return to zero first, causing two round-trips through the +// synchronizers instead of just one. +// +// For further information, see Section 8.2.4 in H. Kaeslin, "Top-Down Digital VLSI Design: From +// Architecture to Gate-Level Circuits and FPGAs", 2015. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_sync_reqack #( + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by default. + parameter bit EnRzHs = 1'b0 // By Default, the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake protocol + // if the FSMs need to be partial-reset-safe. +) ( + input clk_src_i, // REQ side, SRC domain + input rst_src_ni, // REQ side, SRC domain + input clk_dst_i, // ACK side, DST domain + input rst_dst_ni, // ACK side, DST domain + + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + + input logic src_req_i, // REQ side, SRC domain + output logic src_ack_o, // REQ side, SRC domain + output logic dst_req_o, // ACK side, DST domain + input logic dst_ack_i // ACK side, DST domain +); + + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + if (EnRzHs) begin : gen_rz_hs_protocol + ////////////////// + // RZ protocol // + ////////////////// + + // Types + typedef enum logic { + LoSt, HiSt + } rz_fsm_e; + + // Signals + rz_fsm_e src_fsm_d, src_fsm_q; + rz_fsm_e dst_fsm_d, dst_fsm_q; + logic src_ack, dst_ack; + logic src_req, dst_req; + + // REQ-side FSM (SRC domain) + always_comb begin : src_fsm + src_fsm_d = src_fsm_q; + src_ack_o = 1'b0; + src_req = 1'b0; + + unique case (src_fsm_q) + LoSt: begin + // Wait for the ack to go back to zero before starting + // a new transaction. + if (!src_ack && src_req_i) begin + src_fsm_d = HiSt; + end + end + HiSt: begin + src_req = 1'b1; + // Forward the acknowledgement. + src_ack_o = src_ack; + // If request drops out, we go back to LoSt. + // If DST side asserts ack, we also go back to LoSt. + if (!src_req_i || src_ack) begin + src_fsm_d = LoSt; + end + end + //VCS coverage off + // pragma coverage off + default: ; + //VCS coverage on + // pragma coverage on + endcase + end + + // Move ACK over to SRC domain. + caliptra_prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i (clk_src_i), + .rst_ni (rst_src_ni), + .d_i (dst_ack), + .q_o (src_ack) + ); + + // Registers + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_fsm_q <= LoSt; + end else begin + src_fsm_q <= src_fsm_d; + end + end + + // ACK-side FSM (DST domain) + always_comb begin : dst_fsm + dst_fsm_d = dst_fsm_q; + dst_req_o = 1'b0; + dst_ack = 1'b0; + + unique case (dst_fsm_q) + LoSt: begin + if (dst_req) begin + // Forward the request. + dst_req_o = 1'b1; + // Wait for the request and acknowledge to be asserted + // before responding to the SRC side. + if (dst_ack_i) begin + dst_fsm_d = HiSt; + end + end + end + HiSt: begin + dst_ack = 1'b1; + // Wait for the request to drop back to zero. + if (!dst_req) begin + dst_fsm_d = LoSt; + end + end + //VCS coverage off + // pragma coverage off + default: ; + //VCS coverage on + // pragma coverage on + endcase + end + + // Move REQ over to DST domain. + caliptra_prim_flop_2sync #( + .Width(1) + ) req_sync ( + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .d_i (src_req), + .q_o (dst_req) + ); + + // Registers + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_fsm_q <= LoSt; + end else begin + dst_fsm_q <= dst_fsm_d; + end + end + + end else begin : gen_nrz_hs_protocol + ////////////////// + // NRZ protocol // + ////////////////// + + // Types + typedef enum logic { + EVEN, ODD + } sync_reqack_fsm_e; + + // Signals + sync_reqack_fsm_e src_fsm_ns, src_fsm_cs; + sync_reqack_fsm_e dst_fsm_ns, dst_fsm_cs; + + logic src_req_d, src_req_q, src_ack; + logic dst_ack_d, dst_ack_q, dst_req; + logic src_handshake, dst_handshake; + + assign src_handshake = src_req_i & src_ack_o; + assign dst_handshake = dst_req_o & dst_ack_i; + + // Move REQ over to DST domain. + caliptra_prim_flop_2sync #( + .Width(1) + ) req_sync ( + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .d_i (src_req_q), + .q_o (dst_req) + ); + + // Move ACK over to SRC domain. + caliptra_prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i (clk_src_i), + .rst_ni (rst_src_ni), + .d_i (dst_ack_q), + .q_o (src_ack) + ); + + // REQ-side FSM (SRC domain) + always_comb begin : src_fsm + src_fsm_ns = src_fsm_cs; + + // By default, we keep the internal REQ value and don't ACK. + src_req_d = src_req_q; + src_ack_o = 1'b0; + + unique case (src_fsm_cs) + + EVEN: begin + // Simply forward REQ and ACK. + src_req_d = src_req_i; + src_ack_o = src_ack; + + // The handshake is done for exactly 1 clock cycle. + if (src_handshake) begin + src_fsm_ns = ODD; + end + end + + ODD: begin + // Internal REQ and ACK have inverted meaning now. If src_req_i is high again, this + // signals a new transaction. + src_req_d = ~src_req_i; + src_ack_o = ~src_ack; + + // The handshake is done for exactly 1 clock cycle. + if (src_handshake) begin + src_fsm_ns = EVEN; + end + end + + //VCS coverage off + // pragma coverage off + + default: ; + + //VCS coverage on + // pragma coverage on + + endcase + end + + // ACK-side FSM (DST domain) + always_comb begin : dst_fsm + dst_fsm_ns = dst_fsm_cs; + + // By default, we don't REQ and keep the internal ACK. + dst_req_o = 1'b0; + dst_ack_d = dst_ack_q; + + unique case (dst_fsm_cs) + + EVEN: begin + // Simply forward REQ and ACK. + dst_req_o = dst_req; + dst_ack_d = dst_ack_i; + + // The handshake is done for exactly 1 clock cycle. + if (dst_handshake) begin + dst_fsm_ns = ODD; + end + end + + ODD: begin + // Internal REQ and ACK have inverted meaning now. If dst_req goes low, this signals a new + // transaction. + dst_req_o = ~dst_req; + dst_ack_d = ~dst_ack_i; + + // The handshake is done for exactly 1 clock cycle. + if (dst_handshake) begin + dst_fsm_ns = EVEN; + end + end + + //VCS coverage off + // pragma coverage off + + default: ; + + //VCS coverage on + // pragma coverage on + + endcase + end + + // Registers + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_fsm_cs <= EVEN; + src_req_q <= 1'b0; + end else begin + src_fsm_cs <= src_fsm_ns; + src_req_q <= src_req_d; + end + end + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_fsm_cs <= EVEN; + dst_ack_q <= 1'b0; + end else begin + dst_fsm_cs <= dst_fsm_ns; + dst_ack_q <= dst_ack_d; + end + end + end + + //////////////// + // Assertions // + //////////////// + + `ifdef CALIPTRA_INC_ASSERT + //VCS coverage off + // pragma coverage off + + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag; + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag <= '0; + end else if (src_req_i && !chk_flag) begin + chk_flag <= 1'b1; + end + end + //VCS coverage on + // pragma coverage on + + // SRC domain can only de-assert REQ after receiving ACK. + `CALIPTRA_ASSERT(SyncReqAckHoldReq, $fell(src_req_i) && req_chk_i && chk_flag |-> + $fell(src_ack_o), clk_src_i, !rst_src_ni || !rst_dst_ni || !req_chk_i || !chk_flag) + `endif + + // DST domain cannot assert ACK without REQ. + `CALIPTRA_ASSERT(SyncReqAckAckNeedsReq, dst_ack_i |-> + dst_req_o, clk_dst_i, !rst_src_ni || !rst_dst_ni) + + if (EnRstChks) begin : gen_assert_en_rst_chks + `ifdef CALIPTRA_INC_ASSERT + + //VCS coverage off + // pragma coverage off + // This assertion is written very oddly because it is difficult to reliably catch + // when rst drops. + // The problem is that reset assertion in the design is often associated with clocks + // stopping, this means things like rise / fell don't work correctly since there are + // no clocks. + // As a result of this, we end up detecting way past the interest point (whenever + // clocks are restored) and falsely assert an error. + // The code below instead tries to use asynchronous flags to determine when and if + // the two domains are properly reset. + logic src_reset_flag; + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_reset_flag <= '0; + end else if(src_req_i) begin + src_reset_flag <= 1'b1; + end + end + + logic dst_reset_flag; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_reset_flag <= '0; + end else if (dst_req_o) begin + dst_reset_flag <= 1'b1; + end + end + //VCS coverage on + // pragma coverage on + + // Always reset both domains. Both resets need to be active at the same time. + `CALIPTRA_ASSERT(SyncReqAckRstSrc, $fell(rst_src_ni) |-> + (!src_reset_flag throughout !dst_reset_flag[->1]), + clk_src_i, 0) + `CALIPTRA_ASSERT(SyncReqAckRstDst, $fell(rst_dst_ni) |-> + (!dst_reset_flag throughout !src_reset_flag[->1]), + clk_dst_i, 0) + + `endif + + + end + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_sync_reqack_data.sv b/src/caliptra_prim/rtl/caliptra_prim_sync_reqack_data.sv new file mode 100644 index 000000000..dc0a3f0b9 --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_sync_reqack_data.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// REQ/ACK synchronizer with associated data. +// +// This module synchronizes a REQ/ACK handshake with associated data across a clock domain +// crossing (CDC). Both domains will see a handshake with the duration of one clock cycle. By +// default, the data itself is not registered. The main purpose of feeding the data through this +// module to have an anchor point for waiving CDC violations. If the data is configured to flow +// from the destination (DST) to the source (SRC) domain, an additional register stage can be +// inserted for data buffering. +// +// Under the hood, this module uses a prim_sync_reqack primitive for synchronizing the +// REQ/ACK handshake. See prim_sync_reqack.sv for more details. + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_sync_reqack_data #( + parameter int unsigned Width = 1, + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by + // default. + parameter bit DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST, + // 1'b0 = DST to SRC + parameter bit DataReg = 1'b0, // Enable optional register stage for data, + // only usable with DataSrc2Dst == 1'b0. + parameter bit EnRzHs = 1'b0 // By Default, we the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake + // protocol if the FSMs need to be partial-reset-safe. +) ( + input clk_src_i, // REQ side, SRC domain + input rst_src_ni, // REQ side, SRC domain + input clk_dst_i, // ACK side, DST domain + input rst_dst_ni, // ACK side, DST domain + + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + + input logic src_req_i, // REQ side, SRC domain + output logic src_ack_o, // REQ side, SRC domain + output logic dst_req_o, // ACK side, DST domain + input logic dst_ack_i, // ACK side, DST domain + + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + //////////////////////////////////// + // REQ/ACK synchronizer primitive // + //////////////////////////////////// + caliptra_prim_sync_reqack #( + .EnRstChks(EnRstChks), + .EnRzHs(EnRzHs) + ) u_prim_sync_reqack ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + + .req_chk_i, + + .src_req_i, + .src_ack_o, + .dst_req_o, + .dst_ack_i + ); + + ///////////////////////// + // Data register stage // + ///////////////////////// + // Optional - Only relevant if the data flows from DST to SRC. In this case, it must be ensured + // that the data remains stable until the ACK becomes visible in the SRC domain. + // + // Note that for larger data widths, it is recommended to adjust the data sender to hold the data + // stable until the next REQ in order to save the cost of this register stage. + if (DataSrc2Dst == 1'b0 && DataReg == 1'b1) begin : gen_data_reg + logic data_we; + logic [Width-1:0] data_d, data_q; + + // Sample the data when seing the REQ/ACK handshake in the DST domain. + assign data_we = dst_req_o & dst_ack_i; + assign data_d = data_i; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + data_q <= '0; + end else if (data_we) begin + data_q <= data_d; + end + end + assign data_o = data_q; + + end else begin : gen_no_data_reg + // Just feed through the data. + assign data_o = data_i; + end + + //////////////// + // Assertions // + //////////////// + if (DataSrc2Dst == 1'b1) begin : gen_assert_data_src2dst +`ifdef CALIPTRA_INC_ASSERT + //VCS coverage off + // pragma coverage off + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag_d, chk_flag_q; + assign chk_flag_d = src_req_i && !chk_flag_q ? 1'b1 : chk_flag_q; + + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag_q <= '0; + end else begin + chk_flag_q <= chk_flag_d; + end + end + //VCS coverage on + // pragma coverage on + + // SRC domain cannot change data while waiting for ACK. + `CALIPTRA_ASSERT(SyncReqAckDataHoldSrc2Dst, !$stable(data_i) && chk_flag_q |-> + (!src_req_i || (src_req_i && src_ack_o)), + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) + + // Register stage cannot be used. + `CALIPTRA_ASSERT_INIT(SyncReqAckDataReg, DataSrc2Dst && !DataReg) +`endif + end else if (DataSrc2Dst == 1'b0 && DataReg == 1'b0) begin : gen_assert_data_dst2src + // DST domain shall not change data while waiting for SRC domain to latch it (together with + // receiving ACK). It takes 2 SRC cycles for ACK to cross over from DST to SRC, and 1 SRC cycle + // for the next REQ to cross over from SRC to DST. + // + // Denote the src clock where REQ & ACK as time zero. The data flowing through the CDC could be + // corrupted if data_o was not stable over the previous 2 clock cycles (so we want to check time + // points -2, -1 and 0). Moreover, the DST domain cannot know that it is allowed to change value + // until at least one more SRC cycle (the time taken for REQ to cross back from SRC to DST). + // + // To make this assertion, we will sample at each of 4 time points (-2, -1, 0 and +1), asserting + // that data_o is equal at each of these times. Note this won't detect glitches at intermediate + // timepoints. + // + // The SVAs below are designed not to consume time, which means that they can be disabled with + // an $assertoff(..) and won't linger to fail later. This wouldn't work properly if we used + // something like |=> instead of the $past(...) function. That means that we have to trigger at + // the "end" of the window. To make sure we don't miss a situation where the value changed at + // time -1 (causing corruption), but reset was asserted between time 0 and 1, we split the + // assertion into two pieces. The first (SyncReqAckDataHoldDst2SrcA) checks that data doesn't + // change in a way that could cause data corruption. The second (SyncReqAckDataHoldDst2SrcB) + // checks that the DST side doesn't do anything that it shouldn't know is safe. +`ifdef CALIPTRA_INC_ASSERT + //VCS coverage off + // pragma coverage off + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag_d, chk_flag_q; + assign chk_flag_d = src_req_i && !chk_flag_q ? 1'b1 : chk_flag_q; + + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag_q <= '0; + end else begin + chk_flag_q <= chk_flag_d; + end + end + //VCS coverage on + // pragma coverage on + + `CALIPTRA_ASSERT(SyncReqAckDataHoldDst2SrcA, + chk_flag_q && src_req_i && src_ack_o |-> + $past(data_o, 2) == data_o && $past(data_o) == data_o, + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) + `CALIPTRA_ASSERT(SyncReqAckDataHoldDst2SrcB, + chk_flag_q && $past(src_req_i && src_ack_o) |-> $past(data_o) == data_o, + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) +`endif + end + +endmodule diff --git a/src/caliptra_prim/rtl/caliptra_prim_util_memload.svh b/src/caliptra_prim/rtl/caliptra_prim_util_memload.svh new file mode 100644 index 000000000..81600a8de --- /dev/null +++ b/src/caliptra_prim/rtl/caliptra_prim_util_memload.svh @@ -0,0 +1,68 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Memory loader for simulation + * + * Include this file in a memory primitive to load a memory array from + * simulation. + * + * Requirements: + * - A memory array named `mem`. + * - A parameter `Width` giving the memory width (word size) in bit. + * - A parameter `Depth` giving the memory depth in words. + * - A parameter `MemInitFile` with a file path of a VMEM file to be loaded into + * the memory if not empty. + * + * Note this works with memories up to a maximum width of 312 bits. Should this maximum width be + * increased all of the `simutil_set_mem` and `simutil_get_mem` call sites must be found (e.g. using + * git grep) and adjusted appropriately. + */ + +`ifndef SYNTHESIS + // Task for loading 'mem' with SystemVerilog system task $readmemh() + export "DPI-C" task simutil_memload; + + task simutil_memload; + input string file; + $readmemh(file, mem); + endtask + + // Function for setting a specific element in |mem| + // Returns 1 (true) for success, 0 (false) for errors. + export "DPI-C" function simutil_set_mem; + + function int simutil_set_mem(input int index, input bit [311:0] val); + int valid; + valid = Width > 312 || index >= Depth ? 0 : 1; + if (valid == 1) mem[index] = val[Width-1:0]; + return valid; + endfunction + + // Function for getting a specific element in |mem| + export "DPI-C" function simutil_get_mem; + + function int simutil_get_mem(input int index, output bit [311:0] val); + int valid; + valid = Width > 312 || index >= Depth ? 0 : 1; + if (valid == 1) begin + val = 0; + val[Width-1:0] = mem[index]; + end + return valid; + endfunction +`endif + +initial begin + logic show_mem_paths; + + // Print the hierarchical path to the memory to help make formal connectivity checks easy. + void'($value$plusargs("show_mem_paths=%0b", show_mem_paths)); + if (show_mem_paths) $display("%m"); + + if (MemInitFile != "") begin : gen_meminit + $display("Initializing memory %m from file '%s'.", MemInitFile); + $readmemh(MemInitFile, mem); + end +end diff --git a/src/caliptra_prim_generic/config/compile.yml b/src/caliptra_prim_generic/config/compile.yml index f18389093..0263e24c2 100644 --- a/src/caliptra_prim_generic/config/compile.yml +++ b/src/caliptra_prim_generic/config/compile.yml @@ -8,3 +8,7 @@ targets: - $COMPILE_ROOT/rtl/caliptra_prim_generic_flop_en.sv - $COMPILE_ROOT/rtl/caliptra_prim_generic_flop.sv - $COMPILE_ROOT/rtl/caliptra_prim_generic_buf.sv + - $COMPILE_ROOT/rtl/caliptra_prim_generic_ram_1p.sv + - $COMPILE_ROOT/rtl/caliptra_prim_generic_and2.sv + - $COMPILE_ROOT/rtl/caliptra_prim_generic_clock_mux2.sv + - $COMPILE_ROOT/rtl/caliptra_prim_clock_inv.sv \ No newline at end of file diff --git a/src/caliptra_prim_generic/rtl/caliptra_prim_clock_inv.sv b/src/caliptra_prim_generic/rtl/caliptra_prim_clock_inv.sv new file mode 100644 index 000000000..6d080344a --- /dev/null +++ b/src/caliptra_prim_generic/rtl/caliptra_prim_clock_inv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Clock inverter +// Varies on the process + +module caliptra_prim_clock_inv #( + parameter bit HasScanMode = 1'b1, + parameter bit NoFpgaBufG = 1'b0 // only used in FPGA case +) ( + input clk_i, + input scanmode_i, + output logic clk_no // Inverted +); + + if (HasScanMode) begin : gen_scan + caliptra_prim_clock_mux2 #( + .NoFpgaBufG(NoFpgaBufG) + ) i_dft_tck_mux ( + .clk0_i ( ~clk_i ), + .clk1_i ( clk_i ), // bypass the inverted clock for testing + .sel_i ( scanmode_i ), + .clk_o ( clk_no ) + ); + end else begin : gen_noscan + logic unused_scanmode; + assign unused_scanmode = scanmode_i; + assign clk_no = ~clk_i; + end + +endmodule : caliptra_prim_clock_inv diff --git a/src/caliptra_prim_generic/rtl/caliptra_prim_generic_and2.sv b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_and2.sv new file mode 100644 index 000000000..a692e7d1a --- /dev/null +++ b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_and2.sv @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_generic_and2 #( + parameter int Width = 1 +) ( + input [Width-1:0] in0_i, + input [Width-1:0] in1_i, + output logic [Width-1:0] out_o +); + + assign out_o = in0_i & in1_i; + +endmodule diff --git a/src/caliptra_prim_generic/rtl/caliptra_prim_generic_clock_mux2.sv b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_clock_mux2.sv new file mode 100644 index 000000000..387acb784 --- /dev/null +++ b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_clock_mux2.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_generic_clock_mux2 #( + parameter bit NoFpgaBufG = 1'b0 // this parameter serves no function in the generic model +) ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + + // We model the mux with logic operations for GTECH runs. + assign clk_o = (sel_i & clk1_i) | (~sel_i & clk0_i); + + // make sure sel is never X (including during reset) + // need to use ##1 as this could break with inverted clocks that + // start with a rising edge at the beginning of the simulation. + `CALIPTRA_ASSERT(selKnown0, ##1 !$isunknown(sel_i), clk0_i, 0) + `CALIPTRA_ASSERT(selKnown1, ##1 !$isunknown(sel_i), clk1_i, 0) + +endmodule : caliptra_prim_generic_clock_mux2 diff --git a/src/caliptra_prim_generic/rtl/caliptra_prim_generic_ram_1p.sv b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_ram_1p.sv new file mode 100644 index 000000000..1c9698f92 --- /dev/null +++ b/src/caliptra_prim_generic/rtl/caliptra_prim_generic_ram_1p.sv @@ -0,0 +1,79 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous single-port SRAM model + +`include "caliptra_prim_assert.sv" + +module caliptra_prim_generic_ram_1p import caliptra_prim_ram_1p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input logic clk_i, + + input logic req_i, + input logic write_i, + input logic [Aw-1:0] addr_i, + input logic [Width-1:0] wdata_i, + input logic [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, // Read data. Data is returned one cycle after req_i is high. + input ram_1p_cfg_t cfg_i +); + +// For certain synthesis experiments we compile the design with generic models to get an unmapped +// netlist (GTECH). In these synthesis experiments, we typically black-box the memory models since +// these are going to be simulated using plain RTL models in netlist simulations. This can be done +// by analyzing and elaborating the design, and then removing the memory submodules before writing +// out the verilog netlist. However, memory arrays can take a long time to elaborate, and in case +// of dual port rams they can even trigger elab errors due to multiple processes writing to the +// same memory variable concurrently. To this end, we exclude the entire logic in this module in +// these runs with the following macro. +`ifndef SYNTHESIS_MEMORY_BLACK_BOXING + + // Width must be fully divisible by DataBitsPerMask + `CALIPTRA_ASSERT_INIT(DataBitsPerMaskCheck_A, (Width % DataBitsPerMask) == 0) + + logic unused_cfg; + assign unused_cfg = ^cfg_i; + + // Width of internal write mask. Note wmask_i input into the module is always assumed + // to be the full bit mask + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] wmask; + + for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask + assign wmask[k] = &wmask_i[k*DataBitsPerMask +: DataBitsPerMask]; + + // Ensure that all mask bits within a group have the same value for a write + `CALIPTRA_ASSERT(MaskCheck_A, req_i && write_i |-> + wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0}, + clk_i, '0) + end + + // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error + // thrown when using $readmemh system task to backdoor load an image + always @(posedge clk_i) begin + if (req_i) begin + if (write_i) begin + for (int i=0; i < MaskWidth; i = i + 1) begin + if (wmask[i]) begin + mem[addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= + wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + end else begin + rdata_o <= mem[addr_i]; + end + end + end + + `include "caliptra_prim_util_memload.svh" +`endif +endmodule diff --git a/src/integration/config/compile.yml b/src/integration/config/compile.yml index 3d9b55942..baa4c5e73 100644 --- a/src/integration/config/compile.yml +++ b/src/integration/config/compile.yml @@ -99,6 +99,7 @@ targets: - $COMPILE_ROOT/tb - $COMPILE_ROOT/coverage files: + - $COMPILE_ROOT/tb/global_fuse_ctrl_init_done_event_pkg.sv - $COMPILE_ROOT/tb/caliptra_top_tb_pkg.sv - $COMPILE_ROOT/tb/caliptra_veer_sram_export.sv - $COMPILE_ROOT/tb/caliptra_top_tb_services.sv diff --git a/src/integration/tb/caliptra_top_tb_soc_bfm.sv b/src/integration/tb/caliptra_top_tb_soc_bfm.sv index 2b9edd627..c563075fb 100644 --- a/src/integration/tb/caliptra_top_tb_soc_bfm.sv +++ b/src/integration/tb/caliptra_top_tb_soc_bfm.sv @@ -21,8 +21,9 @@ module caliptra_top_tb_soc_bfm import axi_pkg::*; import soc_ifc_pkg::*; -import caliptra_top_tb_pkg::*; #( - parameter SKIP_BRINGUP = 0 +import caliptra_top_tb_pkg::*; #( + parameter SKIP_BRINGUP = 0, + parameter SKIP_FUSE_CTRL = 1 ) ( input logic core_clk, output logic cptra_pwrgood, diff --git a/src/integration/tb/global_fuse_ctrl_init_done_event_pkg.sv b/src/integration/tb/global_fuse_ctrl_init_done_event_pkg.sv new file mode 100644 index 000000000..9bf58d6b2 --- /dev/null +++ b/src/integration/tb/global_fuse_ctrl_init_done_event_pkg.sv @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package global_fuse_ctrl_init_done_event; + event fuse_ctrl_init_done; +endpackage diff --git a/src/lc_ctrl/rtl/lc_ctrl_pkg.sv b/src/lc_ctrl/rtl/lc_ctrl_pkg.sv index 8793c5dbe..604500465 100644 --- a/src/lc_ctrl/rtl/lc_ctrl_pkg.sv +++ b/src/lc_ctrl/rtl/lc_ctrl_pkg.sv @@ -40,10 +40,16 @@ package lc_ctrl_pkg; // Note that changing this encoding has implications on isolation cell // values in RTL. Do not change this unless absolutely needed. + /* typedef enum logic [TxWidth-1:0] { On = 4'b0101, Off = 4'b1010 } lc_tx_t; + */ + typedef logic [TxWidth-1:0] lc_tx_t; + parameter lc_tx_t On = 4'b0101; + parameter lc_tx_t Off = 4'b1010; + parameter lc_tx_t LC_TX_DEFAULT = lc_tx_t'(Off); parameter int RmaSeedWidth = 32; diff --git a/src/lc_ctrl/rtl/lc_ctrl_state_pkg.sv b/src/lc_ctrl/rtl/lc_ctrl_state_pkg.sv index 100e62077..af4eff310 100644 --- a/src/lc_ctrl/rtl/lc_ctrl_state_pkg.sv +++ b/src/lc_ctrl/rtl/lc_ctrl_state_pkg.sv @@ -338,7 +338,9 @@ package lc_ctrl_state_pkg; parameter lc_token_t AllZeroToken = { 128'h0 }; - parameter lc_token_t RndCnstRawUnlockToken = { + // [anjpar]: Renaming to *Default as we are not running topgen + //parameter lc_token_t RndCnstRawUnlockToken = { + parameter lc_token_t RndCnstRawUnlockTokenDefault = { 128'h1C8BE2FF12790AE2E6D6A68151CBD084 }; parameter lc_token_t AllZeroTokenHashed = { diff --git a/src/soc_ifc/rtl/soc_ifc_top.sv b/src/soc_ifc/rtl/soc_ifc_top.sv index 3f23654ce..ccf518feb 100644 --- a/src/soc_ifc/rtl/soc_ifc_top.sv +++ b/src/soc_ifc/rtl/soc_ifc_top.sv @@ -344,6 +344,7 @@ axi_sub #( .rdata (soc_req_rdata ), // Requires: Component dwidth == AXI dwidth .last ( ), // Asserted with final 'dv' of a burst .hld (soc_req_hold ), + .size ( ), .rd_err(soc_req_error ), .wr_err(soc_req_error ) );