diff --git a/doc/icache.rst b/doc/icache.rst index 11e8357e..37dce429 100644 --- a/doc/icache.rst +++ b/doc/icache.rst @@ -43,6 +43,9 @@ The following table describes the available configuration parameters. +-------------------------+-----------+-----------------------------------------------+ | ``CacheSizeBytes`` | ``4kB`` | Size of cache in bytes. | +-------------------------+-----------+-----------------------------------------------+ +| ``CacheECC`` | ``1'b0`` | Enable SECDED ECC protection in tag and data | +| | | RAMs. | ++-------------------------+-----------+-----------------------------------------------+ | ``LineSize`` | ``64`` | The width of one cache line in bits. | | | | Line sizes smaller than 64 bits may give | | | | compilation errors. | @@ -82,18 +85,21 @@ RAM Arrangement --------------- The data RAMs are arranged as ``NumWays`` banks of ``LineSize`` width. +If ECC is configured, the tag and data banks will be wider to accomodate the extra checkbits. Indicative RAM sizes for common configurations are given in the table below: -+-------------------------+-----------------+-----------------+ -| Cache config | Tag RAMs | Data RAMs | -+=========================+=================+=================+ -| 4kB, 2 way, 64bit line | 2 x 256 x 21bit | 4 x 256 x 32bit | -+-------------------------+-----------------+-----------------+ -| 4kB, 2 way, 128bit line | 2 x 128 x 21bit | 8 x 128 x 32bit | -+-------------------------+-----------------+-----------------+ -| 4kB, 4 way, 64bit line | 4 x 128 x 21bit | 8 x 128 x 32bit | -+-------------------------+-----------------+-----------------+ ++------------------------------+-----------------+------------------+ +| Cache config | Tag RAMs | Data RAMs | ++==============================+=================+==================+ +| 4kB, 2 way, 64bit line | 2 x 256 x 22bit | 2 x 256 x 64bit | ++------------------------------+-----------------+------------------+ +| 4kB, 2 way, 64bit line w/ECC | 2 x 256 x 28bit | 2 x 256 x 72bit | ++------------------------------+-----------------+------------------+ +| 4kB, 2 way, 128bit line | 2 x 128 x 22bit | 2 x 128 x 128bit | ++------------------------------+-----------------+------------------+ +| 4kB, 4 way, 64bit line | 4 x 128 x 22bit | 4 x 128 x 64bit | ++------------------------------+-----------------+------------------+ Sub Unit Description -------------------- @@ -160,6 +166,36 @@ The remaining data from hits is buffered in the fill buffer data storage and sup To deal with misalignment caused by compressed instructions, there is a 16bit skid buffer to store the upper halfword. +Cache ECC protection +^^^^^^^^^^^^^^^^^^^^ + +When ECC protection is enabled, extra checkbits are appended to the top of the tag and data RAM write data as follows: + +For the Tag RAMs (4kB cache): + ++---------------+-----------+--------+ +| ECC checkbits | Valid bit | Tag | ++---------------+-----------+--------+ +| [27:22] | [21] | [20:0] | ++---------------+-----------+--------+ + +For the Data RAMs (64bit line): + ++---------------+--------+ +| ECC checkbits | Data | ++---------------+--------+ +| [71:64] | [63:0] | ++---------------+--------+ + +The checkbits are generated by dedicated modules in IC0 before the RAMs are written. +In IC1, the RAM read data and checkbits are fed into dedicated modules which output whether there was an error. +Although the modules used have the required outputs to allow inline correction of single bit errors, the I$ does not make use of them since it never performs corrections. + +Any error (single or double bit) in any RAM will effectively cancel a cache hit in IC1. +The request which observed an error will fetch it's data from the main instruction memory as normal for a cache miss. +The cache index and way (or ways) with errors are stored in IC1, and a cache write is forced the next cycle to invalidate that line. +Lookup requests will be blocked in IC0 while the invalidation write is performed. + Cache invalidation ^^^^^^^^^^^^^^^^^^ diff --git a/rtl/ibex_icache.sv b/rtl/ibex_icache.sv index 3c88ea2b..b5127e50 100644 --- a/rtl/ibex_icache.sv +++ b/rtl/ibex_icache.sv @@ -14,12 +14,13 @@ module ibex_icache #( // Cache arrangement parameters parameter int unsigned BusWidth = 32, parameter int unsigned CacheSizeBytes = 4*1024, + parameter bit CacheECC = 1'b0, parameter int unsigned LineSize = 64, parameter int unsigned NumWays = 2, // Always make speculative bus requests in parallel with lookups - parameter bit SpecRequest = 1'b0, + parameter bit SpecRequest = 1'b0, // Only cache branch targets - parameter bit BranchCache = 1'b0 + parameter bit BranchCache = 1'b0 ) ( // Clock and reset input logic clk_i, @@ -55,7 +56,6 @@ module ibex_icache #( ); // NOTE RTL IS DRAFT - // TODO different RAM primitives? // Local constants localparam int unsigned ADDR_W = 32; @@ -64,6 +64,7 @@ module ibex_icache #( // Request throttling threshold localparam int unsigned FB_THRESHOLD = NUM_FB - 2; // Derived parameters + localparam int unsigned LINE_SIZE_ECC = CacheECC ? (LineSize + 8) : LineSize; localparam int unsigned LINE_SIZE_BYTES = LineSize/8; localparam int unsigned LINE_W = $clog2(LINE_SIZE_BYTES); localparam int unsigned BUS_BYTES = BusWidth/8; @@ -74,6 +75,7 @@ module ibex_icache #( localparam int unsigned INDEX_W = $clog2(NUM_LINES); localparam int unsigned INDEX_HI = INDEX_W + LINE_W - 1; localparam int unsigned TAG_SIZE = ADDR_W - INDEX_W - LINE_W + 1; // 1 valid bit + localparam int unsigned TAG_SIZE_ECC = CacheECC ? (TAG_SIZE + 6) : TAG_SIZE; localparam int unsigned OUTPUT_BEATS = (BUS_BYTES / 2); // number of halfwords // Prefetch signals @@ -87,7 +89,7 @@ module ibex_icache #( logic [INDEX_W-1:0] lookup_index_ic0; logic fill_req_ic0; logic [INDEX_W-1:0] fill_index_ic0; - logic [31:INDEX_HI+1] fill_tag_ic0; + logic [TAG_SIZE-1:0] fill_tag_ic0; logic [LineSize-1:0] fill_wdata_ic0; logic lookup_grant_ic0; logic lookup_actual_ic0; @@ -96,16 +98,16 @@ module ibex_icache #( logic [INDEX_W-1:0] tag_index_ic0; logic [NumWays-1:0] tag_banks_ic0; logic tag_write_ic0; - logic [TAG_SIZE-1:0] tag_wdata_ic0; + logic [TAG_SIZE_ECC-1:0] tag_wdata_ic0; logic data_req_ic0; logic [INDEX_W-1:0] data_index_ic0; logic [NumWays-1:0] data_banks_ic0; logic data_write_ic0; - logic [LineSize-1:0] data_wdata_ic0; + logic [LINE_SIZE_ECC-1:0] data_wdata_ic0; // Cache pipelipe IC1 signals - logic [TAG_SIZE-1:0] tag_rdata_ic1 [NumWays]; - logic [LineSize-1:0] data_rdata_ic1 [NumWays]; - logic [LineSize-1:0] hit_data_ic1; + logic [TAG_SIZE_ECC-1:0] tag_rdata_ic1 [NumWays]; + logic [LINE_SIZE_ECC-1:0] data_rdata_ic1 [NumWays]; + logic [LINE_SIZE_ECC-1:0] hit_data_ic1; logic lookup_valid_ic1; logic [ADDR_W-1:INDEX_HI+1] lookup_addr_ic1; logic [NumWays-1:0] tag_match_ic1; @@ -114,6 +116,10 @@ module ibex_icache #( logic [NumWays-1:0] lowest_invalid_way_ic1; logic [NumWays-1:0] round_robin_way_ic1, round_robin_way_q; logic [NumWays-1:0] sel_way_ic1; + logic ecc_err_ic1; + logic ecc_write_req; + logic [NumWays-1:0] ecc_write_ways; + logic [INDEX_W-1:0] ecc_write_index; // Fill buffer signals logic gnt_or_pmp_err; logic [$clog2(NUM_FB)-1:0] fb_fill_level; @@ -133,6 +139,7 @@ module ibex_icache #( logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_rvd_cnt_d, fill_rvd_cnt_q; logic [NUM_FB-1:0] fill_rvd_done; logic [NUM_FB-1:0] fill_ram_done_d, fill_ram_done_q; + logic [NUM_FB-1:0] fill_out_grant; logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_out_cnt_d, fill_out_cnt_q; logic [NUM_FB-1:0] fill_out_done; logic [NUM_FB-1:0] fill_ext_req, fill_rvd_exp, fill_ram_req, fill_out_req; @@ -214,7 +221,7 @@ module ibex_icache #( // Cache lookup assign lookup_throttle = (fb_fill_level > FB_THRESHOLD[$clog2(NUM_FB)-1:0]); - assign lookup_req_ic0 = req_i & ~&fill_busy_q & (branch_i | ~lookup_throttle); + assign lookup_req_ic0 = req_i & ~&fill_busy_q & (branch_i | ~lookup_throttle) & ~ecc_write_req; assign lookup_addr_ic0 = branch_i ? addr_i : prefetch_addr_q; assign lookup_index_ic0 = lookup_addr_ic0[INDEX_HI:LINE_W]; @@ -222,30 +229,61 @@ module ibex_icache #( // Cache write assign fill_req_ic0 = (|fill_ram_req); assign fill_index_ic0 = fill_ram_req_addr[INDEX_HI:LINE_W]; - assign fill_tag_ic0 = fill_ram_req_addr[ADDR_W-1:INDEX_HI+1]; + assign fill_tag_ic0 = {(~inval_prog_q & ~ecc_write_req),fill_ram_req_addr[ADDR_W-1:INDEX_HI+1]}; assign fill_wdata_ic0 = fill_ram_req_data; // Arbitrated signals - lookups have highest priority assign lookup_grant_ic0 = lookup_req_ic0; - assign fill_grant_ic0 = fill_req_ic0 & ~lookup_req_ic0 & ~inval_prog_q; + assign fill_grant_ic0 = fill_req_ic0 & ~lookup_req_ic0 & ~inval_prog_q & ~ecc_write_req; // Qualified lookup grant to mask ram signals in IC1 if access was not made assign lookup_actual_ic0 = lookup_grant_ic0 & icache_enable_i & ~inval_prog_q; // Tagram - assign tag_req_ic0 = lookup_req_ic0 | fill_req_ic0 | inval_prog_q; + assign tag_req_ic0 = lookup_req_ic0 | fill_req_ic0 | inval_prog_q | ecc_write_req; assign tag_index_ic0 = inval_prog_q ? inval_index_q : + ecc_write_req ? ecc_write_index : fill_grant_ic0 ? fill_index_ic0 : lookup_index_ic0; - assign tag_banks_ic0 = fill_grant_ic0 ? fill_ram_req_way : {NumWays{1'b1}}; - assign tag_write_ic0 = fill_grant_ic0 | inval_prog_q; - assign tag_wdata_ic0 = {~inval_prog_q,fill_tag_ic0}; + assign tag_banks_ic0 = ecc_write_req ? ecc_write_ways : + fill_grant_ic0 ? fill_ram_req_way : + {NumWays{1'b1}}; + assign tag_write_ic0 = fill_grant_ic0 | inval_prog_q | ecc_write_req; // Dataram assign data_req_ic0 = lookup_req_ic0 | fill_req_ic0; assign data_index_ic0 = tag_index_ic0; assign data_banks_ic0 = tag_banks_ic0; assign data_write_ic0 = tag_write_ic0; - assign data_wdata_ic0 = fill_wdata_ic0; + + // Append ECC checkbits to write data if required + if (CacheECC) begin : gen_ecc_wdata + + // Tagram ECC + // Reuse the same ecc encoding module for larger cache sizes by padding with zeros + logic [21:0] tag_ecc_input_padded; + logic [27:0] tag_ecc_output_padded; + logic [22-TAG_SIZE:0] tag_ecc_output_unused; + + assign tag_ecc_input_padded = {{22-TAG_SIZE{1'b0}},fill_tag_ic0}; + assign tag_ecc_output_unused = tag_ecc_output_padded[21:TAG_SIZE-1]; + + prim_secded_28_22_enc tag_ecc_enc ( + .in (tag_ecc_input_padded), + .out (tag_ecc_output_padded) + ); + + assign tag_wdata_ic0 = {tag_ecc_output_padded[27:22],tag_ecc_output_padded[TAG_SIZE-1:0]}; + + // Dataram ECC + prim_secded_72_64_enc data_ecc_enc ( + .in (fill_wdata_ic0), + .out (data_wdata_ic0) + ); + + end else begin : gen_noecc_wdata + assign tag_wdata_ic0 = fill_tag_ic0; + assign data_wdata_ic0 = fill_wdata_ic0; + end //////////////// // IC0 -> IC1 // @@ -254,14 +292,14 @@ module ibex_icache #( for (genvar way = 0; way < NumWays; way++) begin : gen_rams // Tag RAM instantiation prim_generic_ram_1p #( - .Width (TAG_SIZE), + .Width (TAG_SIZE_ECC), .Depth (NUM_LINES) ) tag_bank ( .clk_i (clk_i), .rst_ni (rst_ni), .req_i (tag_req_ic0 & tag_banks_ic0[way]), .write_i (tag_write_ic0), - .wmask_i ({TAG_SIZE{1'b1}}), + .wmask_i ({TAG_SIZE_ECC{1'b1}}), .addr_i (tag_index_ic0), .wdata_i (tag_wdata_ic0), .rvalid_o (), @@ -269,14 +307,14 @@ module ibex_icache #( ); // Data RAM instantiation prim_generic_ram_1p #( - .Width (LineSize), + .Width (LINE_SIZE_ECC), .Depth (NUM_LINES) ) data_bank ( .clk_i (clk_i), .rst_ni (rst_ni), .req_i (data_req_ic0 & data_banks_ic0[way]), .write_i (data_write_ic0), - .wmask_i ({LineSize{1'b1}}), + .wmask_i ({LINE_SIZE_ECC{1'b1}}), .addr_i (data_index_ic0), .wdata_i (data_wdata_ic0), .rvalid_o (), @@ -305,10 +343,11 @@ module ibex_icache #( // Tag matching for (genvar way = 0; way < NumWays; way++) begin : gen_tag_match - assign tag_match_ic1[way] = (tag_rdata_ic1[way] == + assign tag_match_ic1[way] = (tag_rdata_ic1[way][TAG_SIZE-1:0] == {1'b1,lookup_addr_ic1[ADDR_W-1:INDEX_HI+1]}); assign tag_invalid_ic1[way] = ~tag_rdata_ic1[way][TAG_SIZE-1]; end + assign tag_hit_ic1 = |tag_match_ic1; // Hit data mux @@ -342,6 +381,83 @@ module ibex_icache #( assign sel_way_ic1 = |tag_invalid_ic1 ? lowest_invalid_way_ic1 : round_robin_way_q; + // ECC checking logic + if (CacheECC) begin : gen_data_ecc_checking + logic [NumWays-1:0] tag_err_ic1; + logic [1:0] data_err_ic1; + logic ecc_correction_write_d, ecc_correction_write_q; + logic [NumWays-1:0] ecc_correction_ways_d, ecc_correction_ways_q; + logic [INDEX_W-1:0] lookup_index_ic1, ecc_correction_index_q; + + // Tag ECC checking + for (genvar way = 0; way < NumWays; way++) begin : gen_tag_ecc + logic [1:0] tag_err_bank_ic1; + logic [27:0] tag_rdata_padded_ic1; + + // Expand the tag rdata with extra padding if the tag size is less than the maximum + assign tag_rdata_padded_ic1 = {tag_rdata_ic1[way][TAG_SIZE_ECC-1-:6], + {22-TAG_SIZE{1'b0}}, + tag_rdata_ic1[way][TAG_SIZE-1:0]}; + + prim_secded_28_22_dec data_ecc_dec ( + .in (tag_rdata_padded_ic1), + .d_o (), + .syndrome_o (), + .err_o (tag_err_bank_ic1) + ); + assign tag_err_ic1[way] = |tag_err_bank_ic1; + end + + // Data ECC checking + // Note - could generate for all ways and mux after + prim_secded_72_64_dec data_ecc_dec ( + .in (hit_data_ic1), + .d_o (), + .syndrome_o (), + .err_o (data_err_ic1) + ); + + assign ecc_err_ic1 = lookup_valid_ic1 & ((|data_err_ic1) | (|tag_err_ic1)); + + // Error correction + // The way(s) producing the error will be invalidated in the next cycle. + assign ecc_correction_ways_d = tag_err_ic1 | (tag_match_ic1 & {NumWays{|data_err_ic1}}); + assign ecc_correction_write_d = ecc_err_ic1; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ecc_correction_write_q <= 1'b0; + end else begin + ecc_correction_write_q <= ecc_correction_write_d; + end + end + + // The index is required in IC1 only when ECC is configured so is registered here + always_ff @(posedge clk_i) begin + if (lookup_grant_ic0) begin + lookup_index_ic1 <= lookup_addr_ic0[INDEX_HI-:INDEX_W]; + end + end + + // Store the ways with errors to be invalidated + always_ff @(posedge clk_i) begin + if (ecc_err_ic1) begin + ecc_correction_ways_q <= ecc_correction_ways_d; + ecc_correction_index_q <= lookup_index_ic1; + end + end + + assign ecc_write_req = ecc_correction_write_q; + assign ecc_write_ways = ecc_correction_ways_q; + assign ecc_write_index = ecc_correction_index_q; + + end else begin : gen_no_data_ecc + assign ecc_err_ic1 = 1'b0; + assign ecc_write_req = 1'b0; + assign ecc_write_ways = '0; + assign ecc_write_index = '0; + end + /////////////////////////////// // Cache allocation decision // /////////////////////////////// @@ -436,7 +552,7 @@ module ibex_icache #( (fill_cache_q[fb] & fill_busy_q[fb]); // Record whether the request hit in the cache assign fill_hit_ic1[fb] = lookup_valid_ic1 & fill_in_ic1[fb] & tag_hit_ic1; - assign fill_hit_d[fb] = fill_hit_ic1[fb] | + assign fill_hit_d[fb] = (fill_hit_ic1[fb] & ~ecc_err_ic1) | (fill_hit_q[fb] & fill_busy_q[fb]); /////////////////////////////////////////// @@ -454,10 +570,10 @@ module ibex_icache #( // External request must be held until granted assign fill_ext_hold_d[fb] = (fill_alloc[fb] & fill_spec_hold) | (fill_ext_arb[fb] & ~gnt_or_pmp_err); - // Extneral requests are completed when the counter is filled or when the request is cancelled + // External requests are completed when the counter is filled or when the request is cancelled assign fill_ext_done[fb] = (fill_ext_cnt_q[fb][LINE_BEATS_W] | // external requests are considered complete if the request hit - fill_hit_ic1[fb] | fill_hit_q[fb] | + (fill_hit_ic1[fb] & ~ecc_err_ic1) | fill_hit_q[fb] | // cancel if the line is stale and won't be cached (~fill_cache_q[fb] & (branch_i | fill_stale_q[fb]))) & // can't cancel while we are waiting for a grant on the bus @@ -485,11 +601,13 @@ module ibex_icache #( (fill_hit_ic1[fb] | fill_hit_q[fb] | (fill_rvd_cnt_q[fb] > fill_out_cnt_q[fb]) | fill_rvd_arb[fb]); + // Calculate when a beat of data is output. Any ECC error squashes the output that cycle. + assign fill_out_grant[fb] = fill_out_arb[fb] & output_ready & ~ecc_err_ic1; + // Count the beats of data output to the IF stage assign fill_out_cnt_d[fb] = fill_alloc[fb] ? {1'b0,lookup_addr_ic0[LINE_W-1:BUS_W]} : (fill_out_cnt_q[fb] + - {{LINE_BEATS_W{1'b0}},(fill_out_arb[fb] & - output_ready)}); + {{LINE_BEATS_W{1'b0}},fill_out_grant[fb]}); // Data output complete when the counter fills assign fill_out_done[fb] = fill_out_cnt_q[fb][LINE_BEATS_W]; @@ -531,6 +649,7 @@ module ibex_icache #( fill_older_q[fb]); // Arbitrate the request which has data available to send, and is the oldest outstanding assign fill_out_arb[fb] = fill_out_req[fb] & fill_data_sel[fb]; + // Assign incoming rvalid data to the oldest fill buffer expecting it assign fill_rvd_arb[fb] = instr_rvalid_i & fill_rvd_exp[fb] & ~|(fill_rvd_exp & fill_older_q[fb]); ///////////////////////////// @@ -604,9 +723,10 @@ module ibex_icache #( end end - // Data either comes from the cache or the bus - assign fill_data_d[fb] = fill_hit_ic1[fb] ? hit_data_ic1 : - {LINE_BEATS{instr_rdata_i}}; + // Data either comes from the cache or the bus. If there was an ECC error, we must take + // the incoming bus data since the cache hit data is corrupted. + assign fill_data_d[fb] = (fill_hit_ic1[fb] & ~ecc_err_ic1) ? hit_data_ic1[LineSize-1:0] : + {LINE_BEATS{instr_rdata_i}}; for (genvar b = 0; b < LINE_BEATS; b++) begin : gen_data_buf // Error tracking (per beat) @@ -698,8 +818,8 @@ module ibex_icache #( //////////////////////// // Mux between line-width data sources - assign line_data = |fill_data_hit ? hit_data_ic1 : fill_out_data; - assign line_err = |fill_data_hit ? '0 : fill_out_err; + assign line_data = |fill_data_hit ? hit_data_ic1[LineSize-1:0] : fill_out_data; + assign line_err = |fill_data_hit ? {LINE_BEATS{1'b0}} : fill_out_err; // Mux the relevant beat of line data, based on the output address always_comb begin @@ -722,7 +842,8 @@ module ibex_icache #( // Output data is valid (from any of the three possible sources). Note that fill_out_arb // must be used here rather than fill_out_req because data can become valid out of order // (e.g. cache hit data can become available ahead of an older outstanding miss). - assign data_valid = |fill_out_arb; + // Any ECC error suppresses the output that cycle. + assign data_valid = |fill_out_arb & ~ecc_err_ic1; // Skid buffer data assign skid_data_d = output_data[31:16]; @@ -855,6 +976,10 @@ module ibex_icache #( // Assertions // //////////////// - `ASSERT_INIT(param_legal, (LineSize > 32)) + `ASSERT_INIT(size_param_legal, (LineSize > 32)) + + // ECC primitives will need to be changed for different sizes + `ASSERT_INIT(ecc_tag_param_legal, (TAG_SIZE <= 27)) + `ASSERT_INIT(ecc_data_param_legal, (LineSize <= 121)) endmodule diff --git a/shared/rtl/prim_secded_28_22_dec.sv b/shared/rtl/prim_secded_28_22_dec.sv new file mode 100644 index 00000000..6452c512 --- /dev/null +++ b/shared/rtl/prim_secded_28_22_dec.sv @@ -0,0 +1,59 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Decoder generated by secded_gen.py + +module prim_secded_28_22_dec ( + input [27:0] in, + output logic [21:0] d_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + logic single_error; + + // Syndrome calculation + assign syndrome_o[0] = in[22] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7] + ^ in[8] ^ in[9] ^ in[20] ^ in[21]; + assign syndrome_o[1] = in[23] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[10] ^ in[11] ^ in[12] ^ in[13] + ^ in[14] ^ in[15] ^ in[20] ^ in[21]; + assign syndrome_o[2] = in[24] ^ in[0] ^ in[4] ^ in[5] ^ in[6] ^ in[10] ^ in[11] ^ in[12] ^ in[16] + ^ in[17] ^ in[18] ^ in[20]; + assign syndrome_o[3] = in[25] ^ in[1] ^ in[4] ^ in[7] ^ in[8] ^ in[10] ^ in[13] ^ in[14] ^ in[16] + ^ in[17] ^ in[19] ^ in[21]; + assign syndrome_o[4] = in[26] ^ in[2] ^ in[5] ^ in[7] ^ in[9] ^ in[11] ^ in[13] ^ in[15] ^ in[16] + ^ in[18] ^ in[19] ^ in[20] ^ in[21]; + assign syndrome_o[5] = in[27] ^ in[3] ^ in[6] ^ in[8] ^ in[9] ^ in[12] ^ in[14] ^ in[15] ^ in[17] + ^ in[18] ^ in[19] ^ in[20] ^ in[21]; + + // Corrected output calculation + assign d_o[0] = (syndrome_o == 6'h7) ^ in[0]; + assign d_o[1] = (syndrome_o == 6'hb) ^ in[1]; + assign d_o[2] = (syndrome_o == 6'h13) ^ in[2]; + assign d_o[3] = (syndrome_o == 6'h23) ^ in[3]; + assign d_o[4] = (syndrome_o == 6'hd) ^ in[4]; + assign d_o[5] = (syndrome_o == 6'h15) ^ in[5]; + assign d_o[6] = (syndrome_o == 6'h25) ^ in[6]; + assign d_o[7] = (syndrome_o == 6'h19) ^ in[7]; + assign d_o[8] = (syndrome_o == 6'h29) ^ in[8]; + assign d_o[9] = (syndrome_o == 6'h31) ^ in[9]; + assign d_o[10] = (syndrome_o == 6'he) ^ in[10]; + assign d_o[11] = (syndrome_o == 6'h16) ^ in[11]; + assign d_o[12] = (syndrome_o == 6'h26) ^ in[12]; + assign d_o[13] = (syndrome_o == 6'h1a) ^ in[13]; + assign d_o[14] = (syndrome_o == 6'h2a) ^ in[14]; + assign d_o[15] = (syndrome_o == 6'h32) ^ in[15]; + assign d_o[16] = (syndrome_o == 6'h1c) ^ in[16]; + assign d_o[17] = (syndrome_o == 6'h2c) ^ in[17]; + assign d_o[18] = (syndrome_o == 6'h34) ^ in[18]; + assign d_o[19] = (syndrome_o == 6'h38) ^ in[19]; + assign d_o[20] = (syndrome_o == 6'h37) ^ in[20]; + assign d_o[21] = (syndrome_o == 6'h3b) ^ in[21]; + + // err_o calc. bit0: single error, bit1: double error + assign single_error = ^syndrome_o; + assign err_o[0] = single_error; + assign err_o[1] = ~single_error & (|syndrome_o); +endmodule + diff --git a/shared/rtl/prim_secded_28_22_enc.sv b/shared/rtl/prim_secded_28_22_enc.sv new file mode 100644 index 00000000..0c1f023d --- /dev/null +++ b/shared/rtl/prim_secded_28_22_enc.sv @@ -0,0 +1,47 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Encoder generated by secded_gen.py + +module prim_secded_28_22_enc ( + input [21:0] in, + output logic [27:0] out +); + + assign out[0] = in[0] ; + assign out[1] = in[1] ; + assign out[2] = in[2] ; + assign out[3] = in[3] ; + assign out[4] = in[4] ; + assign out[5] = in[5] ; + assign out[6] = in[6] ; + assign out[7] = in[7] ; + assign out[8] = in[8] ; + assign out[9] = in[9] ; + assign out[10] = in[10] ; + assign out[11] = in[11] ; + assign out[12] = in[12] ; + assign out[13] = in[13] ; + assign out[14] = in[14] ; + assign out[15] = in[15] ; + assign out[16] = in[16] ; + assign out[17] = in[17] ; + assign out[18] = in[18] ; + assign out[19] = in[19] ; + assign out[20] = in[20] ; + assign out[21] = in[21] ; + assign out[22] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7] ^ in[8] ^ in[9] + ^ in[20] ^ in[21]; + assign out[23] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[10] ^ in[11] ^ in[12] ^ in[13] ^ in[14] + ^ in[15] ^ in[20] ^ in[21]; + assign out[24] = in[0] ^ in[4] ^ in[5] ^ in[6] ^ in[10] ^ in[11] ^ in[12] ^ in[16] ^ in[17] + ^ in[18] ^ in[20]; + assign out[25] = in[1] ^ in[4] ^ in[7] ^ in[8] ^ in[10] ^ in[13] ^ in[14] ^ in[16] ^ in[17] + ^ in[19] ^ in[21]; + assign out[26] = in[2] ^ in[5] ^ in[7] ^ in[9] ^ in[11] ^ in[13] ^ in[15] ^ in[16] ^ in[18] + ^ in[19] ^ in[20] ^ in[21]; + assign out[27] = in[3] ^ in[6] ^ in[8] ^ in[9] ^ in[12] ^ in[14] ^ in[15] ^ in[17] ^ in[18] + ^ in[19] ^ in[20] ^ in[21]; +endmodule + diff --git a/shared/rtl/prim_secded_72_64_dec.sv b/shared/rtl/prim_secded_72_64_dec.sv new file mode 100644 index 00000000..02b18224 --- /dev/null +++ b/shared/rtl/prim_secded_72_64_dec.sv @@ -0,0 +1,121 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Decoder generated by secded_gen.py + +module prim_secded_72_64_dec ( + input [71:0] in, + output logic [63:0] d_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + logic single_error; + + // Syndrome calculation + assign syndrome_o[0] = in[64] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7] + ^ in[8] ^ in[9] ^ in[10] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[15] + ^ in[16] ^ in[17] ^ in[18] ^ in[19] ^ in[20] ^ in[57] ^ in[58] ^ in[61] + ^ in[62] ^ in[63]; + assign syndrome_o[1] = in[65] ^ in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[21] ^ in[22] + ^ in[23] ^ in[24] ^ in[25] ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[30] + ^ in[31] ^ in[32] ^ in[33] ^ in[34] ^ in[35] ^ in[58] ^ in[59] ^ in[60] + ^ in[62] ^ in[63]; + assign syndrome_o[2] = in[66] ^ in[0] ^ in[6] ^ in[7] ^ in[8] ^ in[9] ^ in[10] ^ in[21] ^ in[22] + ^ in[23] ^ in[24] ^ in[25] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[40] + ^ in[41] ^ in[42] ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[59] + ^ in[60] ^ in[63]; + assign syndrome_o[3] = in[67] ^ in[1] ^ in[6] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[21] + ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[36] ^ in[37] ^ in[38] ^ in[39] + ^ in[46] ^ in[47] ^ in[48] ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[57] + ^ in[58] ^ in[61] ^ in[63]; + assign syndrome_o[4] = in[68] ^ in[2] ^ in[7] ^ in[11] ^ in[15] ^ in[16] ^ in[17] ^ in[22] + ^ in[26] ^ in[30] ^ in[31] ^ in[32] ^ in[36] ^ in[40] ^ in[41] ^ in[42] + ^ in[46] ^ in[47] ^ in[48] ^ in[52] ^ in[53] ^ in[54] ^ in[56] ^ in[58] + ^ in[59] ^ in[61] ^ in[62]; + assign syndrome_o[5] = in[69] ^ in[3] ^ in[8] ^ in[12] ^ in[15] ^ in[18] ^ in[19] ^ in[23] + ^ in[27] ^ in[30] ^ in[33] ^ in[34] ^ in[37] ^ in[40] ^ in[43] ^ in[44] + ^ in[46] ^ in[49] ^ in[50] ^ in[52] ^ in[53] ^ in[55] ^ in[56] ^ in[57] + ^ in[59] ^ in[60] ^ in[61]; + assign syndrome_o[6] = in[70] ^ in[4] ^ in[9] ^ in[13] ^ in[16] ^ in[18] ^ in[20] ^ in[24] + ^ in[28] ^ in[31] ^ in[33] ^ in[35] ^ in[38] ^ in[41] ^ in[43] ^ in[45] + ^ in[47] ^ in[49] ^ in[51] ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[59] + ^ in[60] ^ in[61] ^ in[62]; + assign syndrome_o[7] = in[71] ^ in[5] ^ in[10] ^ in[14] ^ in[17] ^ in[19] ^ in[20] ^ in[25] + ^ in[29] ^ in[32] ^ in[34] ^ in[35] ^ in[39] ^ in[42] ^ in[44] ^ in[45] + ^ in[48] ^ in[50] ^ in[51] ^ in[53] ^ in[54] ^ in[55] ^ in[57] ^ in[58] + ^ in[60] ^ in[62] ^ in[63]; + + // Corrected output calculation + assign d_o[0] = (syndrome_o == 8'h7) ^ in[0]; + assign d_o[1] = (syndrome_o == 8'hb) ^ in[1]; + assign d_o[2] = (syndrome_o == 8'h13) ^ in[2]; + assign d_o[3] = (syndrome_o == 8'h23) ^ in[3]; + assign d_o[4] = (syndrome_o == 8'h43) ^ in[4]; + assign d_o[5] = (syndrome_o == 8'h83) ^ in[5]; + assign d_o[6] = (syndrome_o == 8'hd) ^ in[6]; + assign d_o[7] = (syndrome_o == 8'h15) ^ in[7]; + assign d_o[8] = (syndrome_o == 8'h25) ^ in[8]; + assign d_o[9] = (syndrome_o == 8'h45) ^ in[9]; + assign d_o[10] = (syndrome_o == 8'h85) ^ in[10]; + assign d_o[11] = (syndrome_o == 8'h19) ^ in[11]; + assign d_o[12] = (syndrome_o == 8'h29) ^ in[12]; + assign d_o[13] = (syndrome_o == 8'h49) ^ in[13]; + assign d_o[14] = (syndrome_o == 8'h89) ^ in[14]; + assign d_o[15] = (syndrome_o == 8'h31) ^ in[15]; + assign d_o[16] = (syndrome_o == 8'h51) ^ in[16]; + assign d_o[17] = (syndrome_o == 8'h91) ^ in[17]; + assign d_o[18] = (syndrome_o == 8'h61) ^ in[18]; + assign d_o[19] = (syndrome_o == 8'ha1) ^ in[19]; + assign d_o[20] = (syndrome_o == 8'hc1) ^ in[20]; + assign d_o[21] = (syndrome_o == 8'he) ^ in[21]; + assign d_o[22] = (syndrome_o == 8'h16) ^ in[22]; + assign d_o[23] = (syndrome_o == 8'h26) ^ in[23]; + assign d_o[24] = (syndrome_o == 8'h46) ^ in[24]; + assign d_o[25] = (syndrome_o == 8'h86) ^ in[25]; + assign d_o[26] = (syndrome_o == 8'h1a) ^ in[26]; + assign d_o[27] = (syndrome_o == 8'h2a) ^ in[27]; + assign d_o[28] = (syndrome_o == 8'h4a) ^ in[28]; + assign d_o[29] = (syndrome_o == 8'h8a) ^ in[29]; + assign d_o[30] = (syndrome_o == 8'h32) ^ in[30]; + assign d_o[31] = (syndrome_o == 8'h52) ^ in[31]; + assign d_o[32] = (syndrome_o == 8'h92) ^ in[32]; + assign d_o[33] = (syndrome_o == 8'h62) ^ in[33]; + assign d_o[34] = (syndrome_o == 8'ha2) ^ in[34]; + assign d_o[35] = (syndrome_o == 8'hc2) ^ in[35]; + assign d_o[36] = (syndrome_o == 8'h1c) ^ in[36]; + assign d_o[37] = (syndrome_o == 8'h2c) ^ in[37]; + assign d_o[38] = (syndrome_o == 8'h4c) ^ in[38]; + assign d_o[39] = (syndrome_o == 8'h8c) ^ in[39]; + assign d_o[40] = (syndrome_o == 8'h34) ^ in[40]; + assign d_o[41] = (syndrome_o == 8'h54) ^ in[41]; + assign d_o[42] = (syndrome_o == 8'h94) ^ in[42]; + assign d_o[43] = (syndrome_o == 8'h64) ^ in[43]; + assign d_o[44] = (syndrome_o == 8'ha4) ^ in[44]; + assign d_o[45] = (syndrome_o == 8'hc4) ^ in[45]; + assign d_o[46] = (syndrome_o == 8'h38) ^ in[46]; + assign d_o[47] = (syndrome_o == 8'h58) ^ in[47]; + assign d_o[48] = (syndrome_o == 8'h98) ^ in[48]; + assign d_o[49] = (syndrome_o == 8'h68) ^ in[49]; + assign d_o[50] = (syndrome_o == 8'ha8) ^ in[50]; + assign d_o[51] = (syndrome_o == 8'hc8) ^ in[51]; + assign d_o[52] = (syndrome_o == 8'h70) ^ in[52]; + assign d_o[53] = (syndrome_o == 8'hb0) ^ in[53]; + assign d_o[54] = (syndrome_o == 8'hd0) ^ in[54]; + assign d_o[55] = (syndrome_o == 8'he0) ^ in[55]; + assign d_o[56] = (syndrome_o == 8'h7c) ^ in[56]; + assign d_o[57] = (syndrome_o == 8'had) ^ in[57]; + assign d_o[58] = (syndrome_o == 8'h9b) ^ in[58]; + assign d_o[59] = (syndrome_o == 8'h76) ^ in[59]; + assign d_o[60] = (syndrome_o == 8'he6) ^ in[60]; + assign d_o[61] = (syndrome_o == 8'h79) ^ in[61]; + assign d_o[62] = (syndrome_o == 8'hd3) ^ in[62]; + assign d_o[63] = (syndrome_o == 8'h8f) ^ in[63]; + + // err_o calc. bit0: single error, bit1: double error + assign single_error = ^syndrome_o; + assign err_o[0] = single_error; + assign err_o[1] = ~single_error & (|syndrome_o); +endmodule + diff --git a/shared/rtl/prim_secded_72_64_enc.sv b/shared/rtl/prim_secded_72_64_enc.sv new file mode 100644 index 00000000..207c45ca --- /dev/null +++ b/shared/rtl/prim_secded_72_64_enc.sv @@ -0,0 +1,101 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Encoder generated by secded_gen.py + +module prim_secded_72_64_enc ( + input [63:0] in, + output logic [71:0] out +); + + assign out[0] = in[0] ; + assign out[1] = in[1] ; + assign out[2] = in[2] ; + assign out[3] = in[3] ; + assign out[4] = in[4] ; + assign out[5] = in[5] ; + assign out[6] = in[6] ; + assign out[7] = in[7] ; + assign out[8] = in[8] ; + assign out[9] = in[9] ; + assign out[10] = in[10] ; + assign out[11] = in[11] ; + assign out[12] = in[12] ; + assign out[13] = in[13] ; + assign out[14] = in[14] ; + assign out[15] = in[15] ; + assign out[16] = in[16] ; + assign out[17] = in[17] ; + assign out[18] = in[18] ; + assign out[19] = in[19] ; + assign out[20] = in[20] ; + assign out[21] = in[21] ; + assign out[22] = in[22] ; + assign out[23] = in[23] ; + assign out[24] = in[24] ; + assign out[25] = in[25] ; + assign out[26] = in[26] ; + assign out[27] = in[27] ; + assign out[28] = in[28] ; + assign out[29] = in[29] ; + assign out[30] = in[30] ; + assign out[31] = in[31] ; + assign out[32] = in[32] ; + assign out[33] = in[33] ; + assign out[34] = in[34] ; + assign out[35] = in[35] ; + assign out[36] = in[36] ; + assign out[37] = in[37] ; + assign out[38] = in[38] ; + assign out[39] = in[39] ; + assign out[40] = in[40] ; + assign out[41] = in[41] ; + assign out[42] = in[42] ; + assign out[43] = in[43] ; + assign out[44] = in[44] ; + assign out[45] = in[45] ; + assign out[46] = in[46] ; + assign out[47] = in[47] ; + assign out[48] = in[48] ; + assign out[49] = in[49] ; + assign out[50] = in[50] ; + assign out[51] = in[51] ; + assign out[52] = in[52] ; + assign out[53] = in[53] ; + assign out[54] = in[54] ; + assign out[55] = in[55] ; + assign out[56] = in[56] ; + assign out[57] = in[57] ; + assign out[58] = in[58] ; + assign out[59] = in[59] ; + assign out[60] = in[60] ; + assign out[61] = in[61] ; + assign out[62] = in[62] ; + assign out[63] = in[63] ; + assign out[64] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[6] ^ in[7] ^ in[8] ^ in[9] + ^ in[10] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[15] ^ in[16] ^ in[17] ^ in[18] + ^ in[19] ^ in[20] ^ in[57] ^ in[58] ^ in[61] ^ in[62] ^ in[63]; + assign out[65] = in[0] ^ in[1] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[21] ^ in[22] ^ in[23] ^ in[24] + ^ in[25] ^ in[26] ^ in[27] ^ in[28] ^ in[29] ^ in[30] ^ in[31] ^ in[32] ^ in[33] + ^ in[34] ^ in[35] ^ in[58] ^ in[59] ^ in[60] ^ in[62] ^ in[63]; + assign out[66] = in[0] ^ in[6] ^ in[7] ^ in[8] ^ in[9] ^ in[10] ^ in[21] ^ in[22] ^ in[23] + ^ in[24] ^ in[25] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[40] ^ in[41] ^ in[42] + ^ in[43] ^ in[44] ^ in[45] ^ in[56] ^ in[57] ^ in[59] ^ in[60] ^ in[63]; + assign out[67] = in[1] ^ in[6] ^ in[11] ^ in[12] ^ in[13] ^ in[14] ^ in[21] ^ in[26] ^ in[27] + ^ in[28] ^ in[29] ^ in[36] ^ in[37] ^ in[38] ^ in[39] ^ in[46] ^ in[47] ^ in[48] + ^ in[49] ^ in[50] ^ in[51] ^ in[56] ^ in[57] ^ in[58] ^ in[61] ^ in[63]; + assign out[68] = in[2] ^ in[7] ^ in[11] ^ in[15] ^ in[16] ^ in[17] ^ in[22] ^ in[26] ^ in[30] + ^ in[31] ^ in[32] ^ in[36] ^ in[40] ^ in[41] ^ in[42] ^ in[46] ^ in[47] ^ in[48] + ^ in[52] ^ in[53] ^ in[54] ^ in[56] ^ in[58] ^ in[59] ^ in[61] ^ in[62]; + assign out[69] = in[3] ^ in[8] ^ in[12] ^ in[15] ^ in[18] ^ in[19] ^ in[23] ^ in[27] ^ in[30] + ^ in[33] ^ in[34] ^ in[37] ^ in[40] ^ in[43] ^ in[44] ^ in[46] ^ in[49] ^ in[50] + ^ in[52] ^ in[53] ^ in[55] ^ in[56] ^ in[57] ^ in[59] ^ in[60] ^ in[61]; + assign out[70] = in[4] ^ in[9] ^ in[13] ^ in[16] ^ in[18] ^ in[20] ^ in[24] ^ in[28] ^ in[31] + ^ in[33] ^ in[35] ^ in[38] ^ in[41] ^ in[43] ^ in[45] ^ in[47] ^ in[49] ^ in[51] + ^ in[52] ^ in[54] ^ in[55] ^ in[56] ^ in[59] ^ in[60] ^ in[61] ^ in[62]; + assign out[71] = in[5] ^ in[10] ^ in[14] ^ in[17] ^ in[19] ^ in[20] ^ in[25] ^ in[29] ^ in[32] + ^ in[34] ^ in[35] ^ in[39] ^ in[42] ^ in[44] ^ in[45] ^ in[48] ^ in[50] ^ in[51] + ^ in[53] ^ in[54] ^ in[55] ^ in[57] ^ in[58] ^ in[60] ^ in[62] ^ in[63]; +endmodule + diff --git a/shared/sim_shared.core b/shared/sim_shared.core index 1c6a6516..286c6107 100644 --- a/shared/sim_shared.core +++ b/shared/sim_shared.core @@ -11,6 +11,10 @@ filesets: files: - ./rtl/prim_clock_gating.sv - ./rtl/prim_generic_ram_1p.sv + - ./rtl/prim_secded_28_22_enc.sv + - ./rtl/prim_secded_28_22_dec.sv + - ./rtl/prim_secded_72_64_enc.sv + - ./rtl/prim_secded_72_64_dec.sv - ./rtl/ram_1p.sv - ./rtl/ram_2p.sv - ./rtl/bus.sv