diff --git a/Bender.yml b/Bender.yml index 14bfe737..53568b8a 100644 --- a/Bender.yml +++ b/Bender.yml @@ -17,6 +17,19 @@ sources: - rtl/ODRG_unit/odrg_manager_reg_pkg.sv - rtl/ecc_wrap/ecc_manager_reg_pkg.sv - rtl/pulpissimo_tcls/tcls_manager_reg_pkg.sv + - rtl/lowrisc_ecc/prim_secded_13_8_cor.sv + - rtl/lowrisc_ecc/prim_secded_13_8_dec.sv + - rtl/lowrisc_ecc/prim_secded_13_8_enc.sv + - rtl/lowrisc_ecc/prim_secded_22_16_cor.sv + - rtl/lowrisc_ecc/prim_secded_22_16_dec.sv + - rtl/lowrisc_ecc/prim_secded_22_16_enc.sv + - rtl/lowrisc_ecc/prim_secded_39_32_cor.sv + - rtl/lowrisc_ecc/prim_secded_39_32_dec.sv + - rtl/lowrisc_ecc/prim_secded_39_32_enc.sv + - rtl/lowrisc_ecc/prim_secded_72_64_cor.sv + - rtl/lowrisc_ecc/prim_secded_72_64_dec.sv + - rtl/lowrisc_ecc/prim_secded_72_64_enc.sv + - rtl/lowrisc_ecc/prim_secded_pkg.sv - rtl/ODRG_unit/triple_core_barrier.sv - rtl/hsiao_ecc/hsiao_ecc_pkg.sv - rtl/hsiao_ecc/hsiao_ecc_enc.sv @@ -24,11 +37,13 @@ sources: - rtl/hsiao_ecc/hsiao_ecc_cor.sv - rtl/TMR_voter.sv - rtl/TMR_word_voter.sv + - rtl/HMR/resp_suppress.sv # Level 1 - rtl/ODRG_unit/odrg_manager_reg_top.sv - rtl/ecc_wrap/ecc_manager_reg_top.sv - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv + - rtl/ecc_wrap/ecc_scrubber_out.sv - target: any(deprecated, axi_ecc, hci_ecc, pulp_ecc, test) files: @@ -92,6 +107,30 @@ sources: - test/tb_tmr_word_voter.sv - test/tb_bitwise_tmr_voter.sv + - files: + - rtl/HMR/rapid_recovery_pkg.sv + - rtl/HMR/recovery_csr.sv + - rtl/HMR/recovery_pc.sv + - rtl/HMR/recovery_rf.sv + - rtl/HMR/rapid_recovery_unit.sv + - rtl/HMR/DMR_checker.sv + - rtl/HMR/DMR_CSR_checker.sv + - rtl/HMR/DMR_address_generator.sv + # - rtl/HMR/DMR_controller.sv + - rtl/HMR/hmr_rapid_recovery_ctrl.sv + - rtl/HMR/hmr_registers_reg_pkg.sv + - rtl/HMR/hmr_core_regs_reg_pkg.sv + - rtl/HMR/hmr_dmr_regs_reg_pkg.sv + - rtl/HMR/hmr_tmr_regs_reg_pkg.sv + - rtl/HMR/hmr_registers_reg_top.sv + - rtl/HMR/hmr_core_regs_reg_top.sv + - rtl/HMR/hmr_dmr_regs_reg_top.sv + - rtl/HMR/hmr_dmr_ctrl.sv + - rtl/HMR/hmr_tmr_regs_reg_top.sv + - rtl/HMR/hmr_tmr_ctrl.sv + - rtl/HMR/HMR_wrap.sv + - rtl/HMR/hmr_unit.sv + vendor_package: - name: lowrisc_opentitan target_dir: "util/lowrisc_opentitan" diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f119b7..074d5acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Replace vendor.py script with bender vendor for ECC modules - Update `ecc_manager` for configurability -- Update secded testbench to use correctors and fix error injection ## 0.5.1 - 2023-04-12 ### Added @@ -29,11 +28,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Expose additional error logging signals - Add scrubber to ECC SRAM wrap - Add testing signals for tapeout +- Add secded ECC corrector ### Changed - Expose `ecc_sram` ecc error signals - Rename cTCLS to ODRG - Hide bus ecc behind bender targets, remove related dependencies +- Update secded testbench to use correctors and fix error injection ## 0.4.0 - 2022-03-31 diff --git a/Makefile b/Makefile index 64af21f9..afeed9ac 100644 --- a/Makefile +++ b/Makefile @@ -22,12 +22,60 @@ REG_TOOL = $(REG_PATH)/vendor/lowrisc_opentitan/util/regtool.py HJSON_ODRG = rtl/ODRG_unit/ODRG_unit.hjson HJSON_TCLS = rtl/pulpissimo_tcls/TCLS_unit.hjson +HJSON_HMR = rtl/HMR/HMR_regs.hjson +HJSON_HMR_core = rtl/HMR/HMR_core_regs.hjson +HJSON_HMR_dmr = rtl/HMR/HMR_dmr_regs.hjson +HJSON_HMR_tmr = rtl/HMR/HMR_tmr_regs.hjson HJSON_ECC = rtl/ecc_wrap/ecc_sram_wrapper.hjson TARGET_DIR_ODRG = rtl/ODRG_unit TARGET_DIR_TCLS = rtl/pulpissimo_tcls +TARGET_DIR_HMR = rtl/HMR TARGET_DIR_ECC = rtl/ecc_wrap +define HMR_H_HEADER_STRING +/* + * Copyright (C) 2023 ETH Zurich and University of Bologna + * + * 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. + */ + +#ifndef __ARCHI_HMR_HMR_V1_H__ +#define __ARCHI_HMR_HMR_V1_H__ + +#define HMR_IN_INTERLEAVED 1 + +#define HMR_TOP_OFFSET 0x000 +#define HMR_CORE_OFFSET 0x100 +#define HMR_DMR_OFFSET 0x200 +#define HMR_TMR_OFFSET 0x300 + +#define HMR_CORE_INCREMENT 0x010 +#define HMR_CORE_SLL 0x004 +#define HMR_DMR_INCREMENT 0x010 +#define HMR_DMR_SLL 0x004 +#define HMR_TMR_INCREMENT 0x010 +#define HMR_TMR_SLL 0x004 +\n +endef +define HMR_H_FINAL_STRING +\n\n +#endif // __ARCHI_HMR_HMR_V1_H__ + +endef +export HMR_H_HEADER_STRING +export HMR_H_FINAL_STRING + .PHONY: gen_ODRG gen_TCLS gen_ecc_registers gen_ECC gen_ODRG: python $(REG_TOOL) $(HJSON_ODRG) -t $(TARGET_DIR_ODRG) -r @@ -39,6 +87,24 @@ gen_TCLS: python $(REG_TOOL) $(HJSON_TCLS) -d > $(TARGET_DIR_TCLS)/doc.md python $(REG_TOOL) $(HJSON_TCLS) -D > $(TARGET_DIR_TCLS)/TCLS.h +gen_HMR: + python $(REG_TOOL) $(HJSON_HMR) -t $(TARGET_DIR_HMR) -r + python $(REG_TOOL) $(HJSON_HMR) -d > $(TARGET_DIR_HMR)/doc.html + python $(REG_TOOL) $(HJSON_HMR) --doc > $(TARGET_DIR_HMR)/doc.md + python $(REG_TOOL) $(HJSON_HMR_core) -t $(TARGET_DIR_HMR) -r + python $(REG_TOOL) $(HJSON_HMR_dmr) -t $(TARGET_DIR_HMR) -r + python $(REG_TOOL) $(HJSON_HMR_tmr) -t $(TARGET_DIR_HMR) -r + + @printf "$$HMR_H_HEADER_STRING" > $(TARGET_DIR_HMR)/hmr_v1.h + python $(REG_TOOL) $(HJSON_HMR) -D >> $(TARGET_DIR_HMR)/hmr_v1.h + @printf "\n\n" >> $(TARGET_DIR_HMR)/hmr_v1.h + python $(REG_TOOL) $(HJSON_HMR_core) -D >> $(TARGET_DIR_HMR)/hmr_v1.h + @printf "\n\n" >> $(TARGET_DIR_HMR)/hmr_v1.h + python $(REG_TOOL) $(HJSON_HMR_dmr) -D >> $(TARGET_DIR_HMR)/hmr_v1.h + @printf "\n\n" >> $(TARGET_DIR_HMR)/hmr_v1.h + python $(REG_TOOL) $(HJSON_HMR_tmr) -D >> $(TARGET_DIR_HMR)/hmr_v1.h + @printf "$$HMR_H_FINAL_STRING" >> $(TARGET_DIR_HMR)/hmr_v1.h + gen_ecc_registers: python $(REG_TOOL) $(HJSON_ECC) -t $(TARGET_DIR_ECC) -r python $(REG_TOOL) $(HJSON_ECC) -d > $(TARGET_DIR_ECC)/doc.md diff --git a/rtl/HMR/DMR_CSR_checker.sv b/rtl/HMR/DMR_CSR_checker.sv new file mode 100644 index 00000000..c883b18d --- /dev/null +++ b/rtl/HMR/DMR_CSR_checker.sv @@ -0,0 +1,51 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * CS Registers Checker + * + */ + +module DMR_CSR_checker + import rapid_recovery_pkg::*; +( + input csrs_intf_t csr_a_i, + input csrs_intf_t csr_b_i, + output csrs_intf_t check_o, + output logic error_o +); + +logic compare_mstatus; +logic compare_mie; +logic compare_mtvec; +logic compare_mscratch; +logic compare_mip; +logic compare_mepc; +logic compare_mcause; +logic error; + +assign compare_mstatus = |(csr_a_i.csr_mstatus ^ csr_b_i.csr_mstatus); +assign compare_mie = |(csr_a_i.csr_mie ^ csr_b_i.csr_mie); +assign compare_mtvec = |(csr_a_i.csr_mtvec ^ csr_b_i.csr_mtvec); +assign compare_mscratch = |(csr_a_i.csr_mscratch ^ csr_b_i.csr_mscratch); +assign compare_mip = |(csr_a_i.csr_mip ^ csr_b_i.csr_mip); +assign compare_mepc = |(csr_a_i.csr_mepc ^ csr_b_i.csr_mepc); +assign compare_mcause = |(csr_a_i.csr_mcause ^ csr_b_i.csr_mcause); + +assign error = compare_mstatus | + compare_mie | + compare_mtvec | + compare_mscratch | + compare_mip | + compare_mepc | + compare_mcause; +assign check_o = (error) ? csr_a_i : '0; +assign error_o = error; + +endmodule : DMR_CSR_checker diff --git a/rtl/HMR/DMR_address_generator.sv b/rtl/HMR/DMR_address_generator.sv new file mode 100644 index 00000000..e832de28 --- /dev/null +++ b/rtl/HMR/DMR_address_generator.sv @@ -0,0 +1,71 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Dual Modular Address Generator + * Generates addresses for RF refill + * + */ + +module DMR_address_generator #( + parameter AddrWidth = 5 +)( + input logic clk_i , + input logic rst_ni , + input logic clear_i , + input logic enable_i , + output logic done_o , + output logic fatal_o , + output logic [AddrWidth-1:0] address_o +); + +localparam int unsigned NumAddr = 2 ** (AddrWidth - 1); +localparam int unsigned NumVotingSignals = 3; +localparam int unsigned NumTMRResults = 1; +localparam int unsigned ArrayWidth = NumVotingSignals + NumTMRResults; + +logic addr_count_err; +logic [NumVotingSignals-1:0] addr_count_rst; +logic [ArrayWidth-1:0][AddrWidth-1:0] addr_count; + +generate + for (genvar i = 0; i < NumVotingSignals; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : address_generator_counter + if (~rst_ni) + addr_count [i] <= '1; + else begin + if (clear_i || addr_count_rst [i]) + addr_count [i] <= '1; + else if (enable_i) + addr_count [i] <= addr_count [i] + 1; + else + addr_count [i] <= addr_count [i]; + end + end + assign addr_count_rst [i] = ( addr_count [i] == NumAddr/2 - 1) ? 1'b1 : 1'b0; + end +endgenerate + +bitwise_TMR_voter #( + .DataWidth ( AddrWidth ), + .VoterType ( 0 ) +) address_counter_voter ( + .a_i ( addr_count [0] ), + .b_i ( addr_count [1] ), + .c_i ( addr_count [2] ), + .majority_o ( addr_count [3] ), + .error_o ( addr_count_err ), + .error_cba_o ( /* ... */ ) +); + +assign address_o = addr_count [3]; // Result of TMR address voter +assign fatal_o = addr_count_err; // Error from one of the two TMR voters +assign done_o = |addr_count_rst; + +endmodule : DMR_address_generator diff --git a/rtl/HMR/DMR_checker.sv b/rtl/HMR/DMR_checker.sv new file mode 100644 index 00000000..ec4d9ceb --- /dev/null +++ b/rtl/HMR/DMR_checker.sv @@ -0,0 +1,114 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Dual Modular Redundancy Checker + * Compares the outputs generated by two IPs and returns error signals + * in case of mismatch + * + */ + +module DMR_checker #( + parameter type check_bus_t = logic, + parameter int unsigned Pipeline = 0, + parameter bit AxiBus = 1'b0, + parameter bit AxiHasAce = 1'b0 +)( + input logic clk_i, + input logic rst_ni, + input check_bus_t inp_a_i, + input check_bus_t inp_b_i, + output check_bus_t check_o, + output logic error_o +); + +check_bus_t compare; +check_bus_t inp_q; +logic error; + +if (AxiBus == 1) begin: gen_axi_checker + logic error_aw, error_w, error_ar, error_r, error_b, error_ac, error_cr, error_cd; + if (Pipeline) begin + always_ff @(posedge clk_i, negedge rst_ni) begin + if (~rst_ni) begin + compare <= '0; + inp_q <= '0; + end else begin + compare.aw <= inp_a_i.aw ^ inp_b_i.aw; + compare.aw_valid <= inp_a_i.aw_valid ^ inp_b_i.aw_valid; + compare.w <= inp_a_i.w ^ inp_b_i.w; + compare.w_valid <= inp_a_i.w_valid ^ inp_b_i.w_valid; + compare.ar <= inp_a_i.ar ^ inp_b_i.ar; + compare.ar_valid <= inp_a_i.ar_valid ^ inp_b_i.ar_valid; + compare.r_ready <= inp_a_i.r_ready ^ inp_b_i.r_ready; + compare.b_ready <= inp_a_i.b_ready ^ inp_b_i.b_ready; + if (AxiHasAce) begin + compare.ac_ready <= inp_a_i.ac_ready ^ inp_b_i.ac_ready; + compare.cr_valid <= inp_a_i.cr_valid ^ inp_b_i.cr_valid; + compare.cr_resp <= inp_a_i.cr_resp ^ inp_b_i.cr_resp; + compare.cd_valid <= inp_a_i.cd_valid ^ inp_b_i.cd_valid; + compare.cd <= inp_a_i.cd ^ inp_b_i.cd; + end + inp_q <= inp_a_i; + end + end + end else begin + assign compare.aw = inp_a_i.aw ^ inp_b_i.aw; + assign compare.aw_valid = inp_a_i.aw_valid ^ inp_b_i.aw_valid; + assign compare.w = inp_a_i.w ^ inp_b_i.w; + assign compare.w_valid = inp_a_i.w_valid ^ inp_b_i.w_valid; + assign compare.ar = inp_a_i.ar ^ inp_b_i.ar; + assign compare.ar_valid = inp_a_i.ar_valid ^ inp_b_i.ar_valid; + assign compare.r_ready = inp_a_i.r_ready ^ inp_b_i.r_ready; + assign compare.b_ready = inp_a_i.b_ready ^ inp_b_i.b_ready; + if (AxiHasAce) begin + assign compare.ac_ready = inp_a_i.ac_ready ^ inp_b_i.ac_ready; + assign compare.cr_valid = inp_a_i.cr_valid ^ inp_b_i.cr_valid; + assign compare.cr_resp = inp_a_i.cr_resp ^ inp_b_i.cr_resp; + assign compare.cd_valid = inp_a_i.cd_valid ^ inp_b_i.cd_valid; + assign compare.cd = inp_a_i.cd ^ inp_b_i.cd; + end + assign inp_q = inp_a_i; + end + assign error_aw = (|compare.aw) | compare.aw_valid; + assign error_w = (|compare.w) | compare.w_valid; + assign error_ar = (|compare.ar) | compare.ar_valid; + assign error_r = compare.r_ready; + assign error_b = compare.b_ready; + if (AxiHasAce) begin + assign error_ac = compare.ac_ready; + assign error_cr = (|compare.cr_resp) | compare.cr_valid; + assign error_cd = (|compare.cd) | compare.cd_valid; + end else begin + assign error_ac = '0; + assign error_cr = '0; + assign error_cd = '0; + end + assign error = error_aw | error_w | error_ar | error_r | error_b | error_ac | error_cr | error_cd; +end else begin: gen_generic_checker + if (Pipeline) begin + always_ff @(posedge clk_i, negedge rst_ni) begin + if (~rst_ni) begin + compare <= '0; + inp_q <= '0; + end else begin + compare <= inp_a_i ^ inp_b_i; + inp_q <= inp_a_i; + end + end + end else begin + assign compare = inp_a_i ^ inp_b_i; + assign inp_q = inp_a_i; + end + assign error = |compare; +end +assign check_o = (error) ? '0 : inp_q; +assign error_o = error; + +endmodule : DMR_checker diff --git a/rtl/HMR/DMR_controller.sv b/rtl/HMR/DMR_controller.sv new file mode 100644 index 00000000..9272a769 --- /dev/null +++ b/rtl/HMR/DMR_controller.sv @@ -0,0 +1,368 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Dual Modular Redundancy Controller + * Handles the occurrence of errors and starts recovery routine + * + */ + +module DMR_controller + import rapid_recovery_pkg::*; +#( + parameter int unsigned NumCores = 0, + parameter bit DMRFixed = 1'b0, + parameter bit RapidRecovery = 1'b0, + parameter int unsigned RFAddrWidth = 6, + localparam int unsigned NumDMRGroups = NumCores/2, + localparam int unsigned NumDMRCores = NumDMRGroups * 2, + localparam int unsigned NumDMRLeftover = NumCores - NumDMRCores, + localparam int unsigned NumSysCores = DMRFixed ? NumDMRCores : NumCores +)( + input logic clk_i , + input logic rst_ni, + input logic [NumDMRGroups-1:0] dmr_rf_checker_error_port_a_i, + input logic [NumDMRGroups-1:0] dmr_rf_checker_error_port_b_i, + input logic [NumDMRGroups-1:0] dmr_core_checker_error_main_i, + input logic [NumDMRGroups-1:0] dmr_core_checker_error_data_i, + input regfile_write_t [NumDMRGroups-1:0] backup_regfile_write_i, + output regfile_write_t [NumDMRGroups-1:0] core_recovery_regfile_wport_o, + output logic [NumDMRGroups-1:0] regfile_readback_o, + output regfile_raddr_t [NumDMRGroups-1:0] regfile_raddr_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_pc_read_enable_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_pc_write_enable_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_debug_req_o, + input logic [NumDMRGroups-1:0] dmr_ctrl_core_debug_rsp_i, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_instr_lock_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_setback_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_recover_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_debug_resume_o, + output logic [NumDMRGroups-1:0] dmr_ctrl_core_clk_en_o +); + +/******************************************************** +******************** Recovery Routine ******************* +*********************************************************/ +/************************ + * Signals Declarations * + ************************/ +logic clear, + routine_start; +logic core_instr_lock_rst, + core_recover_rst, + pc_write_enable_rst; +logic addr_gen_start, + addr_gen_error, + addr_gen_done; + +logic restore_pc_cycles_d, + restore_pc_cycles_q, + restore_pc_cycles_rst; + +logic [RFAddrWidth-1:0] addr_gen_res; + +logic [NumDMRGroups-1:0] dmr_ctrl_core_setback_out , + dmr_ctrl_core_debug_rsp_in , + dmr_ctrl_core_clk_en_out, + dmr_ctrl_core_debug_req_out, + dmr_ctrl_pc_read_enable_out, + dmr_ctrl_pc_write_enable_d, + dmr_ctrl_pc_write_enable_q, + dmr_ctrl_core_recover_d, + dmr_ctrl_core_recover_q, + dmr_ctrl_core_instr_lock_d, + dmr_ctrl_core_instr_lock_q, + dmr_ctrl_core_clk_en_d, + dmr_ctrl_core_clk_en_q; + +recovery_routine_state_e current, next; +logic [$clog2(NumDMRGroups)-1:0] error_index_d, + error_index_q; +/****************** + * Output Assigns * + ******************/ +for (genvar i = 0; i < NumDMRGroups; i++) begin + assign dmr_ctrl_core_setback_o [i] = dmr_ctrl_core_setback_out [i]; + assign dmr_ctrl_core_clk_en_o [i] = dmr_ctrl_core_clk_en_out [i]; + assign dmr_ctrl_pc_read_enable_o [i] = dmr_ctrl_pc_read_enable_out [i]; + assign dmr_ctrl_pc_write_enable_o [i] = dmr_ctrl_pc_write_enable_q [i]; + assign dmr_ctrl_core_instr_lock_o [i] = dmr_ctrl_core_instr_lock_q [i]; + assign dmr_ctrl_core_debug_req_o [i] = dmr_ctrl_core_debug_req_out [i]; + assign dmr_ctrl_core_recover_o [i] = dmr_ctrl_core_recover_q [i]; + assign dmr_ctrl_core_debug_rsp_in [i] = dmr_ctrl_core_debug_rsp_i [i]; + assign regfile_readback_o [i] = '0; + assign regfile_raddr_o [i] = '0; +end + +/************** + * Comb logic * + **************/ + +/* + * Error index identifier. + * Identifies the index of the group that is faulty. + */ +always_comb begin + error_index_d = error_index_q; + for (int i = 0; i < NumDMRGroups; i++) begin + if (dmr_rf_checker_error_port_a_i [i] || + dmr_rf_checker_error_port_b_i [i] || + dmr_core_checker_error_main_i [i] || + dmr_core_checker_error_data_i [i] ) + error_index_d = i; + end +end + +/* + * Routine start signal. + * Checks if there are any errors from external checkers to start the FSM Recovery Routine. + */ +assign routine_start = (|dmr_rf_checker_error_port_a_i) | + (|dmr_rf_checker_error_port_a_i) | + (|dmr_core_checker_error_main_i) | + (|dmr_core_checker_error_data_i) ; +/************ +* Registers * +*************/ + +/* + * Error index register. + * If the controller receives an error from one of the input NumDMRGroups, + * this register saves the index of the faulty input group. + */ +always_ff @(posedge clk_i, negedge rst_ni) begin : error_index_register + if (~rst_ni) + error_index_q <= '0; + else begin + if (clear) + error_index_q <= '0; + else + error_index_q <= error_index_d; + end +end + +/* + * Instruction lock registers. + * These registers prevent PULP obi adapter to propagate + * inexistent instruction requests towards iCache while the cores are in debug mode (halted). + */ +generate + for (genvar i = 0; i < NumDMRGroups; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : instruction_lock_registers + if (~rst_ni) begin + dmr_ctrl_core_instr_lock_q [i] <= 1'b0; + end else begin + if (clear || core_instr_lock_rst) begin + dmr_ctrl_core_instr_lock_q [i] <= 1'b0; + end else + dmr_ctrl_core_instr_lock_q [i] <= dmr_ctrl_core_instr_lock_d [i]; + end + end + end +endgenerate + +/* + * Core clock enable. + * Clock gate the cores that do not need to recover during the recovery routine. + */ +generate + for (genvar i = 0; i < NumDMRGroups; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : core_clock_enable + if (~rst_ni) begin + dmr_ctrl_core_clk_en_q [i] <= 1'b1; + end else begin + if (clear) begin + dmr_ctrl_core_clk_en_q [i] <= 1'b1; + end else + dmr_ctrl_core_clk_en_q [i] <= dmr_ctrl_core_clk_en_d [i]; + end + end + end +endgenerate + +/* + * Core Recover Registers. + * These registers raise the recover signal towards the cores to + * allow their register files to be reloaded with the RRF content. + */ +generate + for (genvar i = 0; i < NumDMRGroups; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : core_recover_registers + if (~rst_ni) begin + dmr_ctrl_core_recover_q [i] <= 1'b0; + end else begin + if (clear || core_recover_rst) begin + dmr_ctrl_core_recover_q [i] <= 1'b0; + end else + dmr_ctrl_core_recover_q [i] <= dmr_ctrl_core_recover_d [i]; + end + end + end +endgenerate + +/* + * Program Counter Write Enable Register. + * During a recovery routine, this register blocks the Recovery PC + * from sampling new values from the cores. + */ +generate + for (genvar i = 0; i < NumDMRGroups; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : program_counter_write_enable + if (~rst_ni) begin + dmr_ctrl_pc_write_enable_q [i] <= 1'b1; + end else begin + if (clear || pc_write_enable_rst) begin + dmr_ctrl_pc_write_enable_q [i] <= 1'b1; + end else + dmr_ctrl_pc_write_enable_q [i] <= dmr_ctrl_pc_write_enable_d [i]; + end + end + end +endgenerate + +/* + * Program Counter Restore Counter. + * Counter that keeps the Recovery Routine FSM in the RECOVERY_PC state + * for two cycles to make sure that the PC state is safely restored. + */ +always_ff @(posedge clk_i, negedge rst_ni) begin : pc_restore_counter + if (~rst_ni) + restore_pc_cycles_q <= '0; + else begin + if (clear || restore_pc_cycles_rst) + restore_pc_cycles_q <= '0; + else + restore_pc_cycles_q <= restore_pc_cycles_d; + end +end + +/*********************** +* RF Address Generator * +************************/ +DMR_address_generator #( + .AddrWidth ( RFAddrWidth ) +) RF_address_generator ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .clear_i ( clear ), + .enable_i ( addr_gen_start ), + .done_o ( addr_gen_done ), + .fatal_o ( addr_gen_error ), + .address_o ( addr_gen_res ) +); + +/* Binding recovery signals towards RRF and cores */ +always_comb begin : RF_ports_binding + core_recovery_regfile_wport_o = '0; + for (int i = 0; i < NumDMRGroups; i++) begin + if (i == error_index_q) begin + core_recovery_regfile_wport_o[i].we_a = (addr_gen_start) ? 1'b1 : 1'b0; + core_recovery_regfile_wport_o[i].waddr_a = addr_gen_res; + core_recovery_regfile_wport_o[i].we_b = (addr_gen_start) ? 1'b1 : 1'b0; + core_recovery_regfile_wport_o[i].waddr_b = 5'd16 + addr_gen_res; + end else + core_recovery_regfile_wport_o = '0; + end +end + +/******************************** +* Recovery Routine State Update * +*********************************/ +always_ff @(posedge clk_i, negedge rst_ni) begin : recovery_routine_register + if (~rst_ni) + current <= IDLE; + else begin + current <= next; + end +end + +/*********************** +* Recovery Routine FSM * +************************/ +always_comb begin : recovery_routine_fsm + next = current; + clear = 1'b0; + addr_gen_start = 1'b0; + core_recover_rst = '0; + pc_write_enable_rst = 1'b0; + core_instr_lock_rst = 1'b0; + restore_pc_cycles_rst = 1'b0; + dmr_ctrl_core_setback_out = '0; + dmr_ctrl_core_clk_en_out = '1; + dmr_ctrl_core_recover_d = dmr_ctrl_core_recover_q; + dmr_ctrl_core_instr_lock_d = dmr_ctrl_core_instr_lock_q; + dmr_ctrl_core_clk_en_d = dmr_ctrl_core_clk_en_q; + dmr_ctrl_core_debug_req_out = '0; + dmr_ctrl_core_debug_resume_o = '0; + dmr_ctrl_pc_read_enable_out = '0; + dmr_ctrl_pc_write_enable_d = dmr_ctrl_pc_write_enable_q; + restore_pc_cycles_d = restore_pc_cycles_q; + case (current) + IDLE: begin + if (routine_start) begin + next = RESET; + end else + next = current; + end + + RESET: begin + dmr_ctrl_core_setback_out [error_index_q] = 1'b1; + dmr_ctrl_core_instr_lock_d [error_index_q] = 1'b1; + dmr_ctrl_pc_write_enable_d [error_index_q] = 1'b0; + for (int i = 0; i < NumDMRGroups; i++) begin + if (i != error_index_q) + dmr_ctrl_core_clk_en_d [i] = 1'b0; + end + next = HALT_REQ; + end + + HALT_REQ: begin + dmr_ctrl_core_debug_req_out [error_index_q] = 1'b1; + next = HALT_WAIT; + end + + HALT_WAIT: begin + if (dmr_ctrl_core_debug_rsp_in [error_index_q]) begin + next = RESTORE_PC; + end else + next = current; + end + + RESTORE_PC: begin + dmr_ctrl_pc_read_enable_out [error_index_q] = 1'b1; + restore_pc_cycles_d = restore_pc_cycles_q + 1'd1; + if (restore_pc_cycles_q == 1'd1) begin + restore_pc_cycles_d = 1'd0; + next = RESTORE_RF; + end else + next = current; + end + + RESTORE_RF: begin + dmr_ctrl_core_recover_d [error_index_q] = 1'b1; + addr_gen_start = 1'b1; + if (addr_gen_done) begin + dmr_ctrl_core_instr_lock_d [error_index_q] = 1'b0; + dmr_ctrl_core_debug_resume_o [error_index_q] = 1'b1; + next = EXIT; + end else + next = current; + end + + RESTORE_CSR: begin + end + + EXIT: begin + clear = 1'b1; + next = IDLE; + end + endcase +end : recovery_routine_fsm + +endmodule : DMR_controller diff --git a/rtl/HMR/HMR_core_regs.hjson b/rtl/HMR/HMR_core_regs.hjson new file mode 100644 index 00000000..c55f5ede --- /dev/null +++ b/rtl/HMR/HMR_core_regs.hjson @@ -0,0 +1,60 @@ +{ name: "HMR_core_regs", + clock_primary: "clk_i", + reset_primary: "rst_ni", + bus_interfaces: [ + { protocol: "reg_iface", + direction: "device" + } + ], + + regwidth: "32", + registers: [ + { name: "Current_mode", + desc: "Value to determine wich redundancy mode the core with that ID is in.", + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "0", + name: "independent", + resval: "1", + desc: "" + }, + { bits: "1", + name: "dual", + resval: "0", + desc: "" + }, + { bits: "2", + name: "triple", + resval: "0", + desc: "" + } + ] + + }, + { name: "mismatches", + desc: "Mismatches of the core", + swaccess: "rw0c", + hwaccess: "hrw", + fields: [ + { bits: "31:0", + name: "mismatches", + desc: "mismatch counter of the core" + } + ] + }, + { name: "sp_store", + desc: "Stack Pointer storage register", + swaccess: "rw", + hwaccess: "hro", + hwqe: "true", + fields: [ + { bits: "31:0", + name: "SP", + desc: "Stack Pointer" + } + ] + } + ] +} diff --git a/rtl/HMR/HMR_dmr_regs.hjson b/rtl/HMR/HMR_dmr_regs.hjson new file mode 100644 index 00000000..12e16d44 --- /dev/null +++ b/rtl/HMR/HMR_dmr_regs.hjson @@ -0,0 +1,56 @@ +{ + name: "HMR_dmr_regs", + clock_primary: "clk_i", + reset_primary: "rst_ni", + bus_interfaces: [ + { protocol: "reg_iface", + direction: "device" + } + ], + + regwidth: "32", + + registers: [ + { name: "DMR_enable", + desc: "DMR configuration enable.", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + resval: "0", + fields: [ + { bits: "0", + name: "DMR_enable", + desc: "DMR configuration enable." + } + ] + }, + { name: "DMR_config", + desc: "DMR configuration bits." + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + fields: [ + { bits: "0", + name: "rapid_recovery", + desc: "Enable rapid recovery using an additional register file." + }, + { bits: "1" + name: "force_recovery", + desc: "Forces recovery routine (if rapid_recovery is available)." + } + ] + }, + { name: "checkpoint_addr", + desc: "Address for the last checkpoint.", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + fields: [ + { bits: "31:0", + name: "checkpoint_addr", + desc: "Address for the last checkpoint." + } + ] + } + ] +} diff --git a/rtl/HMR/HMR_regs.hjson b/rtl/HMR/HMR_regs.hjson new file mode 100644 index 00000000..c6a10cbc --- /dev/null +++ b/rtl/HMR/HMR_regs.hjson @@ -0,0 +1,141 @@ +{ + name: "HMR_registers", + clock_primary: "clk_i", + reset_primary: "rst_ni", + bus_interfaces: [ + { protocol: "reg_iface", + direction: "device" + } + ], + + regwidth: "32", + + param_list: [ + { name: "NumCores", + default: "12" # Supports up to 16 cores + }, + { name: "NumDMRGroups", + default: "6" + }, + { name: "NumTMRGroups", + default: "4" + } + ], + + registers: [ + { name: "avail_config", + desc: "Available Configurations from implemented hardware.", + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "0", + name: "independent", + desc: "Independent mode is available." + }, + { bits: "1", + name: "dual", + desc: "Dual Modular Redundancy (DMR) is available." + }, + { bits: "2", + name: "triple", + desc: "Triple Modular Redundancy (TMR) is available." + }, + { bits: "8", + name: "rapid_recovery", + desc: "Rapid Recovery is available." + } + ] + }, + { name: "cores_en", + desc: "Enabled cores, based on the configuration. Can be used for barriers.", + swaccess: "ro", + hwaccess: "hwo", + hwext: "true", + fields: [ + { bits: "NumCores-1:0" + name: "cores_en", + desc: "Enabled cores." + } + ] + }, + { name: "DMR_enable", + desc: "DMR configuration enable, on bit per DMR group.", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + hwext: "true", + resval: "0", + fields: [ + { bits: "NumDMRGroups-1:0", + name: "DMR_enable", + desc: "DMR configuration enable." + } + ] + }, + { name: "TMR_enable", + desc: "TMR configuration enable, one bit per TMR group.", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + hwext: "true", + resval: "0", + fields: [ + { bits: "NumTMRGroups-1:0", + name: "TMR_enable", + desc: "TMR configuration enable." + } + ] + }, + { name: "DMR_config", + desc: "DMR configuration bits." + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + hwext: "true", + fields: [ + { bits: "0", + name: "rapid_recovery", + desc: "Enable rapid recovery using an additional register file." + }, + { bits: "1" + name: "force_recovery", + desc: "Forces recovery routine (if rapid_recovery is available)." + } + ] + }, + { name: "TMR_config", + desc: "TMR configuration bits." + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + hwext: "true", + fields: [ + { bits: "0", + name: "delay_resynch", + resval: "0", + desc: "Enable wait-for-restoration" + }, + { bits: "1", + name: "setback", + resval: "1", + desc: "Enable setback (synchronous reset) during re-synch." + }, + { bits: "2", + name: "reload_setback", + resval: "1", + desc: "Enable setback on mismatch during reload section of re-synch (only possible with `setback`)" + }, + { bits: "3", + name: "rapid_recovery", + desc: "Enable rapid recovery using additional register file." + }, + { bits: "4", + name: "force_resynch", + resval: "0", + desc: "Forces a resynchronization routine" + } + ] + } + ] +} diff --git a/rtl/HMR/HMR_tmr_regs.hjson b/rtl/HMR/HMR_tmr_regs.hjson new file mode 100644 index 00000000..8d6dab90 --- /dev/null +++ b/rtl/HMR/HMR_tmr_regs.hjson @@ -0,0 +1,60 @@ +{ + name: "HMR_tmr_regs", + clock_primary: "clk_i", + reset_primary: "rst_ni", + bus_interfaces: [ + { protocol: "reg_iface", + direction: "device" + } + ], + + regwidth: "32", + + registers: [ + { name: "TMR_enable", + desc: "TMR configuration enable.", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + resval: "0", + fields: [ + { bits: "0", + name: "TMR_enable", + desc: "TMR configuration enable." + } + ] + }, + { name: "TMR_config", + desc: "TMR configuration bits." + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + fields: [ + { bits: "0", + name: "delay_resynch", + resval: "0", + desc: "Enable wait-for-restoration" + }, + { bits: "1", + name: "setback", + resval: "1", + desc: "Enable setback (synchronous reset) during re-synch." + }, + { bits: "2", + name: "reload_setback", + resval: "1", + desc: "Enable setback on mismatch during reload section of re-synch (only possible with `setback`)" + }, + { bits: "3", + name: "rapid_recovery", + desc: "Enable rapid recovery using additional register file." + }, + { bits: "4", + name: "force_resynch", + resval: "0", + desc: "Forces a resynchronization routine" + } + ] + } + ] +} diff --git a/rtl/HMR/HMR_wrap.sv b/rtl/HMR/HMR_wrap.sv new file mode 100644 index 00000000..ac19fb4a --- /dev/null +++ b/rtl/HMR/HMR_wrap.sv @@ -0,0 +1,1944 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Hybrid modular redundancy wrapping unit + +module HMR_wrap + import rapid_recovery_pkg::*; +#( + // Wrapper parameters + /// Number of physical cores + parameter int unsigned NumCores = 0, + /// Enables support for Dual Modular Redundancy + parameter bit DMRSupported = 1'b1, + /// Locks HMR into permanent DMR mode + parameter bit DMRFixed = 1'b0, + /// Enables support for Triple Modular Redundancy + parameter bit TMRSupported = 1'b1, + /// Locks HMR into permanent TMR mode + parameter bit TMRFixed = 1'b0, + /// Enables rapid recovery with a backup register file, PC, ... + parameter bit RapidRecovery = 1'b0, + /// Separates voters and checkers for data, which are then only checked if data request is valid + parameter bit SeparateData = 1'b1, + /// Interleave DMR/TMR cores, alternatively with sequential grouping + parameter bit InterleaveGrps = 1'b1, + parameter int unsigned InstrDataWidth = 32, + parameter int unsigned DataWidth = 32, + parameter int unsigned BeWidth = 4, + parameter int unsigned UserWidth = 0, + parameter int unsigned NumExtPerf = 5, + parameter type reg_req_t = logic, + parameter type reg_resp_t = logic, + // Local parameters depending on the above ones + /// Number of TMR groups (virtual TMR cores) + localparam int unsigned NumTMRGroups = NumCores/3, + /// Number of physical cores used for TMR + localparam int unsigned NumTMRCores = NumTMRGroups * 3, + /// Number of physical cores NOT used for TMR + localparam int unsigned NumTMRLeftover = NumCores - NumTMRCores, + /// Number of DMR groups (virtual DMR cores) + localparam int unsigned NumDMRGroups = NumCores/2, + /// Nubmer of physical cores used for DMR + localparam int unsigned NumDMRCores = NumDMRGroups * 2, + /// Number of physical cores NOT used for DMR + localparam int unsigned NumDMRLeftover = NumCores - NumDMRCores, + /// Number of cores visible to the system (Fixed mode removes unneeded system ports) + localparam int unsigned NumSysCores = DMRFixed ? NumDMRCores : TMRFixed ? NumTMRCores : NumCores +) ( + input logic clk_i , + input logic rst_ni, + + // Port to configuration unit + input reg_req_t reg_request_i , + output reg_resp_t reg_response_o, + + // TMR signals + output logic [NumTMRGroups-1:0] tmr_failure_o , + output logic [ NumSysCores-1:0] tmr_error_o , // Should this not be NumTMRCores? or NumCores? + output logic [NumTMRGroups-1:0] tmr_resynch_req_o, + output logic [ NumCores-1:0] tmr_sw_synch_req_o, + input logic [NumTMRGroups-1:0] tmr_cores_synch_i, + + // DMR signals + output logic [NumDMRGroups-1:0] dmr_failure_o , + output logic [ NumSysCores-1:0] dmr_error_o , // Should this not be NumDMRCores? or NumCores? + output logic [NumDMRGroups-1:0] dmr_resynch_req_o, + output logic [ NumCores-1:0] dmr_sw_synch_req_o, + input logic [NumDMRGroups-1:0] dmr_cores_synch_i, + + // Backup Port from Cores' CSRs + input csrs_intf_t [NumCores-1:0] backup_csr_i, + // Recovery Port to Cores' CSRs + output csrs_intf_t [NumCores-1:0] recovery_csr_o, + // Backup Port from Cores'Program Counter + input logic [ NumCores-1:0][DataWidth-1:0] backup_program_counter_i, + output logic [ NumCores-1:0] pc_recover_o, + output logic [ NumCores-1:0][DataWidth-1:0] recovery_program_counter_o, + input logic [ NumCores-1:0] backup_branch_i, + input logic [ NumCores-1:0][DataWidth-1:0] backup_branch_addr_i, + output logic [ NumCores-1:0] recovery_branch_o, + output logic [ NumCores-1:0][DataWidth-1:0] recovery_branch_addr_o, + // Backup ports from Cores' RFs + input regfile_write_t [NumCores-1:0] backup_regfile_wport_i, + output regfile_write_t [NumCores-1:0] core_recovery_regfile_wport_o, + // Nonstandard core control signals + output logic [ NumCores-1:0] core_setback_o , + output logic [ NumCores-1:0] core_instr_lock_o , + output logic [ NumCores-1:0] core_recover_o , + output logic [ NumCores-1:0] core_debug_resume_o , + + // Ports connecting to System + input logic [NumSysCores-1:0][ 3:0] sys_core_id_i , + input logic [NumSysCores-1:0][ 5:0] sys_cluster_id_i , + + input logic [NumSysCores-1:0] sys_clock_en_i , + input logic [NumSysCores-1:0] sys_fetch_en_i , + input logic [NumSysCores-1:0][ 31:0] sys_boot_addr_i , + output logic [NumSysCores-1:0] sys_core_busy_o , + + input logic [NumSysCores-1:0] sys_irq_req_i , + output logic [NumSysCores-1:0] sys_irq_ack_o , + input logic [NumSysCores-1:0][ 4:0] sys_irq_id_i , + output logic [NumSysCores-1:0][ 4:0] sys_irq_ack_id_o , + + output logic [NumSysCores-1:0] sys_instr_req_o , + input logic [NumSysCores-1:0] sys_instr_gnt_i , + output logic [NumSysCores-1:0][ 31:0] sys_instr_addr_o , + input logic [NumSysCores-1:0][InstrDataWidth-1:0] sys_instr_r_rdata_i, + input logic [NumSysCores-1:0] sys_instr_r_valid_i, + input logic [NumSysCores-1:0] sys_instr_err_i , + + input logic [NumSysCores-1:0] sys_debug_req_i , + + output logic [NumSysCores-1:0] sys_data_req_o , + output logic [NumSysCores-1:0][ 31:0] sys_data_add_o , + output logic [NumSysCores-1:0] sys_data_wen_o , + output logic [NumSysCores-1:0][ DataWidth-1:0] sys_data_wdata_o , + output logic [NumSysCores-1:0][ UserWidth-1:0] sys_data_user_o , + output logic [NumSysCores-1:0][ BeWidth-1:0] sys_data_be_o , + input logic [NumSysCores-1:0] sys_data_gnt_i , + input logic [NumSysCores-1:0] sys_data_r_opc_i , + input logic [NumSysCores-1:0][ DataWidth-1:0] sys_data_r_rdata_i , + input logic [NumSysCores-1:0][ UserWidth-1:0] sys_data_r_user_i , + input logic [NumSysCores-1:0] sys_data_r_valid_i , + input logic [NumSysCores-1:0] sys_data_err_i , + + input logic [NumSysCores-1:0][NumExtPerf-1:0] sys_perf_counters_i, + + // Ports connecting to the cores + output logic [ NumCores-1:0][ 3:0] core_core_id_o , + output logic [ NumCores-1:0][ 5:0] core_cluster_id_o , + + output logic [ NumCores-1:0] core_clock_en_o , + output logic [ NumCores-1:0] core_fetch_en_o , + output logic [ NumCores-1:0][ 31:0] core_boot_addr_o , + input logic [ NumCores-1:0] core_core_busy_i , + + output logic [ NumCores-1:0] core_irq_req_o , + input logic [ NumCores-1:0] core_irq_ack_i , + output logic [ NumCores-1:0][ 4:0] core_irq_id_o , + input logic [ NumCores-1:0][ 4:0] core_irq_ack_id_i , + + input logic [ NumCores-1:0] core_instr_req_i , + output logic [ NumCores-1:0] core_instr_gnt_o , + input logic [ NumCores-1:0][ 31:0] core_instr_addr_i , + output logic [ NumCores-1:0][InstrDataWidth-1:0] core_instr_r_rdata_o, + output logic [ NumCores-1:0] core_instr_r_valid_o, + output logic [ NumCores-1:0] core_instr_err_o , + + output logic [ NumCores-1:0] core_debug_req_o , + input logic [ NumCores-1:0] core_debug_halted_i , + + input logic [ NumCores-1:0] core_data_req_i , + input logic [ NumCores-1:0][ 31:0] core_data_add_i , + input logic [ NumCores-1:0] core_data_wen_i , + input logic [ NumCores-1:0][ DataWidth-1:0] core_data_wdata_i , + input logic [ NumCores-1:0][ UserWidth-1:0] core_data_user_i , + input logic [ NumCores-1:0][ BeWidth-1:0] core_data_be_i , + output logic [ NumCores-1:0] core_data_gnt_o , + output logic [ NumCores-1:0] core_data_r_opc_o , + output logic [ NumCores-1:0][ DataWidth-1:0] core_data_r_rdata_o , + output logic [ NumCores-1:0][ UserWidth-1:0] core_data_r_user_o , + output logic [ NumCores-1:0] core_data_r_valid_o , + output logic [ NumCores-1:0] core_data_err_o , + + output logic [ NumCores-1:0][NumExtPerf-1:0] core_perf_counters_o + + // APU/SHARED_FPU not implemented +); + function int max(int a, int b); + return (a > b) ? a : b; + endfunction + + localparam int unsigned NumBackupRegfiles = max(DMRSupported || DMRFixed ? NumDMRGroups : 0, TMRSupported || TMRFixed ? NumTMRGroups : 0); + + function int tmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumTMRGroups; + else return (core_id/3); + endfunction + + function int tmr_core_id (int group_id, int core_offset); + if (InterleaveGrps) return group_id + core_offset * NumTMRGroups; + else return (group_id * 3) + core_offset; + endfunction + + function int tmr_shared_id (int group_id); + if (InterleaveGrps || !(DMRSupported || DMRFixed)) return group_id; + else return group_id + group_id/2; + endfunction + + function int tmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumTMRGroups; + else return core_id % 3; + endfunction + + function int dmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumDMRGroups; + else return (core_id/2); + endfunction + + function int dmr_core_id (int group_id, int core_offset); + if (InterleaveGrps) return group_id + core_offset * NumDMRGroups; + else return (group_id * 2) + core_offset; + endfunction + + function int dmr_shared_id (int group_id); + return group_id; + endfunction + + function int dmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumDMRGroups; + else return core_id % 2; + endfunction + + if (TMRFixed && DMRFixed) $fatal(1, "Cannot fix both TMR and DMR!"); + + localparam int unsigned CtrlConcatWidth = 1 + 1 + 5 + 1 + 32 + 1; + // busy irq_ack irq_ack_id i_req i_addr d_req + localparam int unsigned RapidRecoveryConcatWidth = RapidRecovery ? 165 + 65 + 76 : 0; + // csr pc rf + localparam int unsigned DataConcatWidth = 32 + 1 + DataWidth + BeWidth + UserWidth; + // data_add data_wen data_wdata data_be data_user + localparam int unsigned MainConcatWidth = RapidRecoveryConcatWidth + + (SeparateData ? CtrlConcatWidth + : CtrlConcatWidth + DataConcatWidth); + + localparam int unsigned RFAddrWidth = 6; + + logic [ NumCores-1:0][MainConcatWidth-1:0] main_concat_in; + logic [NumTMRGroups-1:0][MainConcatWidth-1:0] main_tmr_out; + logic [NumDMRGroups-1:0][MainConcatWidth-1:0] main_dmr_out; + + logic [ NumCores-1:0][DataConcatWidth-1:0] data_concat_in; + logic [NumTMRGroups-1:0][DataConcatWidth-1:0] data_tmr_out; + logic [NumDMRGroups-1:0][DataConcatWidth-1:0] data_dmr_out; + + logic [NumTMRGroups-1:0] tmr_failure, tmr_failure_main, tmr_failure_data; + logic [NumTMRGroups-1:0][2:0] tmr_error, tmr_error_main, tmr_error_data; + logic [NumTMRGroups-1:0] tmr_single_mismatch; + + logic [NumDMRGroups-1:0] dmr_failure, dmr_failure_main, dmr_failure_data; + // logic [NumDMRGroups-1:0][2:0] dmr_error, dmr_error_main, dmr_error_data; + // logic [NumDMRGroups-1:0] dmr_single_mismatch; + + logic [NumTMRGroups-1:0] tmr_core_busy_out; + logic [NumTMRGroups-1:0] tmr_irq_ack_out; + logic [NumTMRGroups-1:0][ 4:0] tmr_irq_ack_id_out; + logic [NumTMRGroups-1:0] tmr_instr_req_out; + logic [NumTMRGroups-1:0][ 31:0] tmr_instr_addr_out; + logic [NumTMRGroups-1:0] tmr_data_req_out; + logic [NumTMRGroups-1:0][ 31:0] tmr_data_add_out; + logic [NumTMRGroups-1:0] tmr_data_wen_out; + logic [NumTMRGroups-1:0][ DataWidth-1:0] tmr_data_wdata_out; + logic [NumTMRGroups-1:0][ UserWidth-1:0] tmr_data_user_out; + logic [NumTMRGroups-1:0][ BeWidth-1:0] tmr_data_be_out; + + logic [NumDMRGroups-1:0] dmr_core_busy_out; + logic [NumDMRGroups-1:0] dmr_irq_ack_out; + logic [NumDMRGroups-1:0][ 4:0] dmr_irq_ack_id_out; + logic [NumDMRGroups-1:0] dmr_instr_req_out; + logic [NumDMRGroups-1:0][ 31:0] dmr_instr_addr_out; + logic [NumDMRGroups-1:0] dmr_data_req_out; + logic [NumDMRGroups-1:0][ 31:0] dmr_data_add_out; + logic [NumDMRGroups-1:0] dmr_data_wen_out; + logic [NumDMRGroups-1:0][ DataWidth-1:0] dmr_data_wdata_out; + logic [NumDMRGroups-1:0][ UserWidth-1:0] dmr_data_user_out; + logic [NumDMRGroups-1:0][ BeWidth-1:0] dmr_data_be_out; + + + logic [ NumDMRGroups-1:0][RFAddrWidth-1:0] dmr_backup_regfile_waddr_a, + dmr_backup_regfile_waddr_b; + logic [ NumDMRGroups-1:0][ DataWidth-1:0] dmr_backup_program_counter, + dmr_backup_regfile_wdata_a, + dmr_backup_regfile_wdata_b, + dmr_backup_branch_addr_int; + logic [ NumDMRGroups-1:0] dmr_backup_branch_int, + dmr_start_recovery, + dmr_backup_regfile_we_a, + dmr_backup_regfile_we_b, + dmr_recovery_finished; + logic [ NumTMRGroups-1:0][RFAddrWidth-1:0] tmr_backup_regfile_waddr_a, + tmr_backup_regfile_waddr_b; + logic [ NumTMRGroups-1:0][ DataWidth-1:0] tmr_backup_program_counter, + tmr_backup_regfile_wdata_a, + tmr_backup_regfile_wdata_b, + tmr_backup_branch_addr_int; + logic [ NumTMRGroups-1:0] tmr_backup_branch_int, + tmr_start_recovery, + tmr_backup_regfile_we_a, + tmr_backup_regfile_we_b, + tmr_recovery_finished; + + logic [NumBackupRegfiles-1:0][RFAddrWidth-1:0] backup_regfile_waddr_a, + backup_regfile_waddr_b; + logic [NumBackupRegfiles-1:0][ DataWidth-1:0] backup_branch_addr_int, + recovery_branch_addr_out, + backup_program_counter_int, + recovery_program_counter_out, + backup_regfile_wdata_a, + backup_regfile_wdata_b; + logic [NumBackupRegfiles-1:0] backup_branch_int, + backup_regfile_we_a, + backup_regfile_we_b, + backup_program_counter_error, + recovery_branch_out, + backup_enable, + recovery_csr_enable_out, + recovery_pc_enable_out, + recovery_debug_req_out, + recovery_debug_halted_in, + recovery_instr_lock_out, + recovery_setback_out, + recovery_trigger_out, + recovery_debug_resume_out, + start_recovery, + recovery_finished; + + logic [NumBackupRegfiles-1:0] rapid_recovery_backup_enable; + regfile_raddr_t [NumBackupRegfiles-1:0] core_regfile_raddr_out; + regfile_rdata_t [NumBackupRegfiles-1:0] core_recovery_regfile_rdata_out; + regfile_write_t [NumBackupRegfiles-1:0] core_recovery_regfile_wport_out; + csrs_intf_t [NumBackupRegfiles-1:0] backup_csr_int, dmr_backup_csr, tmr_backup_csr, recovery_csr_out; + + for (genvar i = 0; i < NumCores; i++) begin : gen_concat + if (SeparateData) begin : gen_separate_data + assign main_concat_in[i] = {core_core_busy_i[i], core_irq_ack_i[i], core_irq_ack_id_i[i], + core_instr_req_i[i], core_instr_addr_i[i], core_data_req_i[i], + // CSRs signals + backup_csr_i[i].csr_mstatus , // 7-bits + backup_csr_i[i].csr_mie , // 32-bits + backup_csr_i[i].csr_mtvec , // 24-bits + backup_csr_i[i].csr_mscratch, // 32-bits + backup_csr_i[i].csr_mip , // 32-bits + backup_csr_i[i].csr_mepc , // 32-bits + backup_csr_i[i].csr_mcause , // 6-bits + // PC signals + backup_program_counter_i[i], // 32-bits + backup_branch_i[i], backup_branch_addr_i[i], // 1-bits + 32-bits + // RF signals + backup_regfile_wport_i[i].wdata_a, // 32-bits + backup_regfile_wport_i[i].waddr_a, // 6-bits + backup_regfile_wport_i[i].wdata_b, // 32-bits + backup_regfile_wport_i[i].waddr_b};// 6-bits + assign data_concat_in[i] = {core_data_add_i[i], core_data_wen_i[i], core_data_wdata_i[i], + core_data_be_i[i], core_data_user_i[i]}; + end else begin : gen_single_group + assign main_concat_in[i] = {core_core_busy_i[i], core_irq_ack_i[i], core_irq_ack_id_i[i], + core_instr_req_i[i], core_instr_addr_i[i], core_data_req_i[i], core_data_add_i[i], + core_data_wen_i[i], core_data_wdata_i[i], core_data_be_i[i], core_data_user_i[i], + // CSRs signals + backup_csr_i[i].csr_mstatus , // 7-bits + backup_csr_i[i].csr_mie , // 32-bits + backup_csr_i[i].csr_mtvec , // 24-bits + backup_csr_i[i].csr_mscratch, // 32-bits + backup_csr_i[i].csr_mip , // 32-bits + backup_csr_i[i].csr_mepc , // 32-bits + backup_csr_i[i].csr_mcause , // 6-bits + // PC signals + backup_program_counter_i[i], // 32-bits + backup_branch_i[i], backup_branch_addr_i[i], // 1-bits + 32-bits + // RF signals + backup_regfile_wport_i[i].wdata_a, // 32-bits + backup_regfile_wport_i[i].waddr_a, // 6-bits + backup_regfile_wport_i[i].wdata_b, // 32-bits + backup_regfile_wport_i[i].waddr_b};// 6-bits + assign data_concat_in = '0; + end + end + + logic [NumSysCores-1:0] filt_instr_req, filt_data_req; + logic [NumSysCores-1:0] filt_instr_r_valid, filt_data_r_valid; + logic [NumSysCores-1:0] filt_instr_gnt, filt_data_gnt; + logic [NumSysCores-1:0] filt_data_we; + logic [NumSysCores-1:0][31:0] filt_instr_addr, filt_data_addr; + logic [NumSysCores-1:0][DataWidth-1:0] filt_data_data; + logic [NumSysCores-1:0][BeWidth-1:0] filt_data_be; + + for (genvar i = 0; i < NumSysCores; i++) begin + resp_suppress #( + .AW (32), + .DW (DataWidth) + ) i_instr_suppress ( + .clk_i, + .rst_ni, + + .ctrl_setback_i (core_setback_o[i]), + + .req_i (filt_instr_req [i]), + .gnt_o (filt_instr_gnt [i]), + .r_valid_o (filt_instr_r_valid[i]), + .we_i ('0), + .addr_i (filt_instr_addr [i]), + .data_i ('0), + .be_i ('0), + + .req_o (sys_instr_req_o[i]), + .gnt_i (sys_instr_gnt_i[i]), + .r_valid_i (sys_instr_r_valid_i[i]), + .we_o (), + .addr_o (sys_instr_addr_o[i]), + .data_o (), + .be_o () + ); + resp_suppress #( + .AW (32), + .DW (DataWidth) + ) i_data_suppress ( + .clk_i, + .rst_ni, + + .ctrl_setback_i (core_setback_o[i]), + + .req_i (filt_data_req [i]), + .gnt_o (filt_data_gnt [i]), + .r_valid_o (filt_data_r_valid[i]), + .we_i (filt_data_we [i]), + .addr_i (filt_data_addr [i]), + .data_i (filt_data_data [i]), + .be_i (filt_data_be [i]), + + .req_o (sys_data_req_o [i]), + .gnt_i (sys_data_gnt_i [i]), + .r_valid_i (sys_data_r_valid_i[i]), + .we_o (sys_data_wen_o [i]), + .addr_o (sys_data_add_o [i]), + .data_o (sys_data_wdata_o [i]), + .be_o (sys_data_be_o [i]) + ); + end + + /*************************** + * HMR Control Registers * + ***************************/ + + logic [NumCores-1:0] core_en_as_master; + logic [NumCores-1:0] core_in_independent; + logic [NumCores-1:0] core_in_dmr; + logic [NumCores-1:0] core_in_tmr; + logic [NumCores-1:0] dmr_core_rapid_recovery_en; + logic [NumCores-1:0] tmr_core_rapid_recovery_en; + + logic [NumDMRGroups-1:0][1:0] dmr_setback_q; + logic [NumDMRGroups-1:0] dmr_grp_in_independent; + logic [NumDMRGroups-1:0] dmr_rapid_recovery_en; + + logic [NumTMRGroups-1:0][2:0] tmr_setback_q; + logic [NumTMRGroups-1:0] tmr_grp_in_independent; + logic [NumTMRGroups-1:0] tmr_rapid_recovery_en; + + logic [NumCores-1:0] sp_store_is_zero; + logic [NumCores-1:0] sp_store_will_be_zero; + + for (genvar i = 0; i < NumCores; i++) begin : gen_global_status + assign core_in_independent[i] = ~core_in_dmr[i] & ~core_in_tmr[i]; + assign core_in_dmr[i] = (DMRSupported || DMRFixed) && i < NumDMRCores ? ~dmr_grp_in_independent[dmr_group_id(i)] : '0; + assign core_in_tmr[i] = (TMRSupported || TMRFixed) && i < NumTMRCores ? ~tmr_grp_in_independent[tmr_group_id(i)] : '0; + assign core_en_as_master[i] = ((tmr_core_id(tmr_group_id(i), 0) == i || i>=NumTMRCores) ? 1'b1 : ~core_in_tmr[i]) & + ((dmr_core_id(dmr_group_id(i), 0) == i || i>=NumDMRCores) ? 1'b1 : ~core_in_dmr[i]); + assign dmr_core_rapid_recovery_en[i] = (DMRSupported || DMRFixed) && i < NumDMRCores && RapidRecovery ? dmr_rapid_recovery_en[dmr_group_id(i)] : '0; + assign tmr_core_rapid_recovery_en[i] = (TMRSupported || TMRFixed) && i < NumTMRCores && RapidRecovery ? tmr_rapid_recovery_en[tmr_group_id(i)] : '0; + end + + reg_req_t [3:0] top_register_reqs; + reg_resp_t [3:0] top_register_resps; + + // 0x000-0x100 -> Top config + // 0x100-0x200 -> Core configs + // 0x200-0x300 -> DMR configs + // 0x300-0x400 -> TMR configs + + reg_demux #( + .NoPorts ( 4 ), + .req_t ( reg_req_t ), + .rsp_t ( reg_resp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( reg_request_i.addr[9:8] ), + .in_req_i ( reg_request_i ), + .in_rsp_o ( reg_response_o ), + .out_req_o ( top_register_reqs ), + .out_rsp_i ( top_register_resps ) + ); + + // Global config registers + + hmr_registers_reg_pkg::hmr_registers_hw2reg_t hmr_hw2reg; + hmr_registers_reg_pkg::hmr_registers_reg2hw_t hmr_reg2hw; + + hmr_registers_reg_top #( + .reg_req_t( reg_req_t ), + .reg_rsp_t( reg_resp_t ) + ) i_hmr_registers ( + .clk_i, + .rst_ni, + .reg_req_i(top_register_reqs[0] ), + .reg_rsp_o(top_register_resps[0]), + .reg2hw (hmr_reg2hw), + .hw2reg (hmr_hw2reg), + .devmode_i('0) + ); + + assign hmr_hw2reg.avail_config.independent.d = ~(TMRFixed | DMRFixed); + assign hmr_hw2reg.avail_config.dual.d = DMRFixed | DMRSupported; + assign hmr_hw2reg.avail_config.triple.d = TMRFixed | TMRSupported; + assign hmr_hw2reg.avail_config.rapid_recovery.d = RapidRecovery; + + always_comb begin : proc_reg_status + hmr_hw2reg.cores_en.d = '0; + hmr_hw2reg.cores_en.d = core_en_as_master; + + hmr_hw2reg.dmr_enable.d = '0; + hmr_hw2reg.dmr_enable.d[NumDMRGroups-1:0] = ~dmr_grp_in_independent; + hmr_hw2reg.tmr_enable.d = '0; + hmr_hw2reg.tmr_enable.d[NumTMRGroups-1:0] = ~tmr_grp_in_independent; + end + + assign hmr_hw2reg.tmr_config.delay_resynch.d = '0; + assign hmr_hw2reg.tmr_config.setback.d = '0; + assign hmr_hw2reg.tmr_config.reload_setback.d = '0; + assign hmr_hw2reg.tmr_config.force_resynch.d = '0; + assign hmr_hw2reg.tmr_config.rapid_recovery.d = '0; + + assign hmr_hw2reg.dmr_config.rapid_recovery.d = '0; + assign hmr_hw2reg.dmr_config.force_recovery.d = '0; + + // Core Config Registers + + reg_req_t [NumCores-1:0] core_register_reqs; + reg_resp_t [NumCores-1:0] core_register_resps; + + // 4 words per core + + reg_demux #( + .NoPorts ( NumCores ), + .req_t ( reg_req_t ), + .rsp_t ( reg_resp_t ) + ) i_core_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs [1].addr[4+$clog2(NumCores)-1:4] ), + .in_req_i ( top_register_reqs [1] ), + .in_rsp_o ( top_register_resps[1] ), + .out_req_o ( core_register_reqs ), + .out_rsp_i ( core_register_resps ) + ); + + hmr_core_regs_reg_pkg::hmr_core_regs_reg2hw_t [NumCores-1:0] core_config_reg2hw; + hmr_core_regs_reg_pkg::hmr_core_regs_hw2reg_t [NumCores-1:0] core_config_hw2reg; + + logic [NumCores-1:0] tmr_incr_mismatches; + logic [NumCores-1:0] dmr_incr_mismatches; + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_registers + hmr_core_regs_reg_top #( + .reg_req_t(reg_req_t), + .reg_rsp_t(reg_resp_t) + ) icore_registers ( + .clk_i, + .rst_ni, + .reg_req_i( core_register_reqs [i] ), + .reg_rsp_o( core_register_resps[i] ), + .reg2hw ( core_config_reg2hw [i] ), + .hw2reg ( core_config_hw2reg [i] ), + .devmode_i('0) + ); + + assign core_config_hw2reg[i].mismatches.d = core_config_reg2hw[i].mismatches.q + 1; + assign core_config_hw2reg[i].mismatches.de = tmr_incr_mismatches[i] | dmr_incr_mismatches[i]; + assign core_config_hw2reg[i].current_mode.independent.d = core_in_independent[i]; + assign core_config_hw2reg[i].current_mode.dual.d = core_in_dmr[i]; + assign core_config_hw2reg[i].current_mode.triple.d = core_in_tmr[i]; + assign sp_store_is_zero[i] = core_config_reg2hw[i].sp_store.q == '0; + assign sp_store_will_be_zero[i] = core_config_reg2hw[i].sp_store.qe && core_register_reqs[i].wdata == '0; + end + + + /********************************************************** + ******************** TMR Voters & Regs ******************* + **********************************************************/ + + if (TMRSupported || TMRFixed) begin : gen_tmr_logic + if (TMRFixed && NumCores % 3 != 0) $warning("Extra cores added not properly handled!"); + + reg_req_t [NumTMRGroups-1:0] tmr_register_reqs; + reg_resp_t [NumTMRGroups-1:0] tmr_register_resps; + logic [NumTMRGroups-1:0] tmr_sw_synch_req; + + localparam TMRSelWidth = $clog2(NumTMRGroups); + + /*************** + * Registers * + ***************/ + reg_demux #( + .NoPorts ( NumTMRGroups ), + .req_t ( reg_req_t ), + .rsp_t ( reg_resp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs[3].addr[4+$clog2(NumTMRGroups)-1:4] ), + .in_req_i ( top_register_reqs[3] ), + .in_rsp_o ( top_register_resps[3] ), + .out_req_o ( tmr_register_reqs ), + .out_rsp_i ( tmr_register_resps ) + ); + + for (genvar i = NumTMRCores; i < NumCores; i++) begin : gen_extra_core_assigns + assign tmr_incr_mismatches[i] = '0; + assign tmr_sw_synch_req_o[i] = '0; + end + + for (genvar i = 0; i < NumTMRGroups; i++) begin : gen_tmr_groups + + hmr_tmr_ctrl #( + .reg_req_t ( reg_req_t ), + .reg_resp_t ( reg_resp_t ), + .TMRFixed ( TMRFixed ), + .InterleaveGrps ( InterleaveGrps ), + .DefaultInTMR ( 1'b0 ), + .RapidRecovery ( RapidRecovery ) + ) i_tmr_ctrl ( + .clk_i, + .rst_ni, + + .reg_req_i ( tmr_register_reqs[i] ), + .reg_resp_o ( tmr_register_resps[i] ), + + .tmr_enable_q_i ( hmr_reg2hw.tmr_enable.q[i] ), + .tmr_enable_qe_i ( hmr_reg2hw.tmr_enable.qe ), + .delay_resynch_q_i ( hmr_reg2hw.tmr_config.delay_resynch.q ), + .delay_resynch_qe_i ( hmr_reg2hw.tmr_config.delay_resynch.qe ), + .setback_q_i ( hmr_reg2hw.tmr_config.setback.q ), + .setback_qe_i ( hmr_reg2hw.tmr_config.setback.qe ), + .reload_setback_q_i ( hmr_reg2hw.tmr_config.reload_setback.q ), + .reload_setback_qe_i ( hmr_reg2hw.tmr_config.reload_setback.qe ), + .rapid_recovery_q_i ( hmr_reg2hw.tmr_config.rapid_recovery.q ), + .rapid_recovery_qe_i ( hmr_reg2hw.tmr_config.rapid_recovery.qe ), + .force_resynch_q_i ( hmr_reg2hw.tmr_config.force_resynch.q ), + .force_resynch_qe_i ( hmr_reg2hw.tmr_config.force_resynch.qe ), + + .setback_o ( tmr_setback_q[i] ), + .sw_resynch_req_o ( tmr_resynch_req_o[i] ), + .sw_synch_req_o ( tmr_sw_synch_req[i] ), + .grp_in_independent_o ( tmr_grp_in_independent[i] ), + .rapid_recovery_en_o ( tmr_rapid_recovery_en[i] ), + .tmr_incr_mismatches_o( {tmr_incr_mismatches[tmr_core_id(i,0)], tmr_incr_mismatches[tmr_core_id(i,1)], tmr_incr_mismatches[tmr_core_id(i,2)]} ), + .tmr_single_mismatch_i( tmr_single_mismatch[i] ), + .tmr_error_i ( tmr_error[i] ), + .tmr_failure_i ( tmr_failure[i] ), + .sp_store_is_zero ( sp_store_is_zero[tmr_core_id(i, 0)] ), + .sp_store_will_be_zero( sp_store_will_be_zero[tmr_core_id(i, 0)] ), + + .fetch_en_i ( sys_fetch_en_i[tmr_core_id(i, 0)] ), + .cores_synch_i ( tmr_cores_synch_i[i] ), + + .recovery_request_o ( tmr_start_recovery [i] ), + .recovery_finished_i ( tmr_recovery_finished [i] ) + ); + + assign tmr_sw_synch_req_o[tmr_core_id(i, 0)] = tmr_sw_synch_req[i]; + assign tmr_sw_synch_req_o[tmr_core_id(i, 1)] = tmr_sw_synch_req[i]; + assign tmr_sw_synch_req_o[tmr_core_id(i, 2)] = tmr_sw_synch_req[i]; + + assign tmr_failure[i] = tmr_data_req_out[i] ? + tmr_failure_main[i] | tmr_failure_data[i] : + tmr_failure_main[i]; + assign tmr_error[i] = tmr_data_req_out[i] ? + tmr_error_main[i] | tmr_error_data[i] : + tmr_error_main[i]; + assign tmr_single_mismatch[i] = tmr_error[i] != 3'b000; + + bitwise_TMR_voter #( + .DataWidth( MainConcatWidth ), + .VoterType( 0 ) + ) i_main_voter ( + .a_i ( main_concat_in[tmr_core_id(i, 0)] ), + .b_i ( main_concat_in[tmr_core_id(i, 1)] ), + .c_i ( main_concat_in[tmr_core_id(i, 2)] ), + .majority_o ( main_tmr_out [i ] ), + .error_o ( tmr_failure_main[i] ), + .error_cba_o( tmr_error_main[i ] ) + ); + if (SeparateData) begin : gen_data_voter + bitwise_TMR_voter #( + .DataWidth( DataConcatWidth ), + .VoterType( 0 ) + ) i_data_voter ( + .a_i ( data_concat_in[tmr_core_id(i, 0)] ), + .b_i ( data_concat_in[tmr_core_id(i, 1)] ), + .c_i ( data_concat_in[tmr_core_id(i, 2)] ), + .majority_o ( data_tmr_out [i ] ), + .error_o ( tmr_failure_data[i] ), + .error_cba_o( tmr_error_data[i ] ) + ); + + assign {tmr_core_busy_out[i], tmr_irq_ack_out[i], tmr_irq_ack_id_out[i], + tmr_instr_req_out[i], tmr_instr_addr_out[i], tmr_data_req_out[i], + // CSRs signals + tmr_backup_csr[i].csr_mstatus , // 7-bits + tmr_backup_csr[i].csr_mie , // 32-bits + tmr_backup_csr[i].csr_mtvec , // 24-bits + tmr_backup_csr[i].csr_mscratch, // 32-bits + tmr_backup_csr[i].csr_mip , // 32-bits + tmr_backup_csr[i].csr_mepc , // 32-bits + tmr_backup_csr[i].csr_mcause , // 6-bits + // PC signals + tmr_backup_program_counter[i], // 32-bits + tmr_backup_branch_int[i], tmr_backup_branch_addr_int[i], // 1-bits + 32-bits + // RF signals + tmr_backup_regfile_wdata_a[i], // 32-bits + tmr_backup_regfile_waddr_a[i], // 6-bits + tmr_backup_regfile_wdata_b[i], // 32-bits + tmr_backup_regfile_waddr_b[i]} + = main_tmr_out[i]; + assign {tmr_data_add_out[i], tmr_data_wen_out[i], tmr_data_wdata_out[i], + tmr_data_be_out[i], tmr_data_user_out[i]} = data_tmr_out[i]; + end else begin : gen_data_in_main + assign tmr_failure_data[i] = 1'b0; + assign tmr_error_data[i] = 3'b000; + assign {tmr_core_busy_out[i], tmr_irq_ack_out[i], tmr_irq_ack_id_out[i], + tmr_instr_req_out[i], tmr_instr_addr_out[i], tmr_data_req_out[i], + tmr_data_add_out[i], tmr_data_wen_out[i], tmr_data_wdata_out[i], + tmr_data_be_out[i], tmr_data_user_out[i], + // CSRs signals + tmr_backup_csr[i].csr_mstatus , // 7-bits + tmr_backup_csr[i].csr_mie , // 32-bits + tmr_backup_csr[i].csr_mtvec , // 24-bits + tmr_backup_csr[i].csr_mscratch, // 32-bits + tmr_backup_csr[i].csr_mip , // 32-bits + tmr_backup_csr[i].csr_mepc , // 32-bits + tmr_backup_csr[i].csr_mcause , // 6-bits + // PC signals + tmr_backup_program_counter[i], // 32-bits + tmr_backup_branch_int[i], tmr_backup_branch_addr_int[i], // 1-bits + 32-bits + // RF signals + tmr_backup_regfile_wdata_a[i], // 32-bits + tmr_backup_regfile_waddr_a[i], // 6-bits + tmr_backup_regfile_wdata_b[i], // 32-bits + tmr_backup_regfile_waddr_b[i]} = main_tmr_out[i]; + end + + if (RapidRecovery) begin : gen_rapid_recovery_connection + + bitwise_TMR_voter #( + .DataWidth( 1 ), + .VoterType( 0 ) + ) i_voter_regfile_we_a ( + .a_i ( backup_regfile_wport_i[tmr_core_id(i, 0)].we_a ), + .b_i ( backup_regfile_wport_i[tmr_core_id(i, 1)].we_a ), + .c_i ( backup_regfile_wport_i[tmr_core_id(i, 2)].we_a ), + .majority_o ( tmr_backup_regfile_we_a [i] ), + .error_o ( ), + .error_cba_o( ) + ); + + bitwise_TMR_voter #( + .DataWidth( 1 ), + .VoterType( 0 ) + ) i_voter_regfile_we_b ( + .a_i ( backup_regfile_wport_i[tmr_core_id(i, 0)].we_b ), + .b_i ( backup_regfile_wport_i[tmr_core_id(i, 1)].we_b ), + .c_i ( backup_regfile_wport_i[tmr_core_id(i, 2)].we_b ), + .majority_o ( tmr_backup_regfile_we_b [i] ), + .error_o ( ), + .error_cba_o( ) + ); + + end else begin : gen_standard_failure + assign tmr_failure [i] = tmr_data_req_out [i] ? (tmr_failure_main[i] | tmr_failure_data[i]) + : tmr_failure_main[i]; + end + end + end else begin : gen_no_tmr_voted + assign tmr_error_main = '0; + assign tmr_error_data = '0; + assign tmr_error = '0; + assign tmr_failure_main = '0; + assign tmr_failure_data = '0; + assign tmr_failure = '0; + assign main_tmr_out = '0; + assign data_tmr_out = '0; + assign {tmr_core_busy_out, tmr_irq_ack_out, tmr_irq_ack_id_out, + tmr_instr_req_out, tmr_instr_addr_out, tmr_data_req_out, + tmr_data_add_out, tmr_data_wen_out, tmr_data_wdata_out, + tmr_data_be_out, tmr_data_user_out} = '0; + assign top_register_resps[3].rdata = '0; + assign top_register_resps[3].error = 1'b1; + assign top_register_resps[3].ready = 1'b1; + assign tmr_incr_mismatches = '0; + assign tmr_grp_in_independent = '0; + assign tmr_setback_q = '0; + assign tmr_resynch_req_o = '0; + assign tmr_sw_synch_req_o = '0; + end + + /************************************************************ + ******************** DMR Voters and Regs ******************* + ************************************************************/ + + if (DMRSupported || DMRFixed) begin: gen_dmr_logic + + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_reg2hw_t [NumDMRGroups-1:0] dmr_reg2hw; + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_hw2reg_t [NumDMRGroups-1:0] dmr_hw2reg; + + reg_req_t [NumDMRGroups-1:0] dmr_register_reqs; + reg_resp_t [NumDMRGroups-1:0] dmr_register_resps; + logic [NumDMRGroups-1:0] dmr_sw_synch_req; + + localparam DMRSelWidth = $clog2(NumDMRGroups); + + /*************** + * Registers * + ***************/ + reg_demux #( + .NoPorts ( NumDMRGroups ), + .req_t ( reg_req_t ), + .rsp_t ( reg_resp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs[2].addr[4+$clog2(NumDMRGroups)-1:4] ), + .in_req_i ( top_register_reqs[2] ), + .in_rsp_o ( top_register_resps[2] ), + .out_req_o ( dmr_register_reqs ), + .out_rsp_i ( dmr_register_resps ) + ); + + for (genvar i = NumDMRCores; i < NumCores; i++) begin : gen_extra_core_assigns + assign dmr_incr_mismatches[i] = '0; + assign dmr_sw_synch_req_o[i] = '0; + end + + for (genvar i = 0; i < NumDMRGroups; i++) begin : gen_dmr_groups + + hmr_dmr_ctrl #( + .reg_req_t ( reg_req_t ), + .reg_resp_t ( reg_resp_t ), + .InterleaveGrps( InterleaveGrps ), + .DMRFixed ( DMRFixed ), + .RapidRecovery ( RapidRecovery ), + .DefaultInDMR ( 1'b0 ) + ) i_dmr_ctrl ( + .clk_i, + .rst_ni, + + .reg_req_i ( dmr_register_reqs [i] ), + .reg_resp_o ( dmr_register_resps[i] ), + + .dmr_enable_q_i ( hmr_reg2hw.dmr_enable.q[i] ), + .dmr_enable_qe_i ( hmr_reg2hw.dmr_enable.qe ), + .rapid_recovery_q_i ( hmr_reg2hw.dmr_config.rapid_recovery.q ), + .rapid_recovery_qe_i ( hmr_reg2hw.dmr_config.rapid_recovery.qe ), + .force_recovery_q_i ( hmr_reg2hw.dmr_config.force_recovery.q ), + .force_recovery_qe_i ( hmr_reg2hw.dmr_config.force_recovery.qe ), + + .setback_o ( dmr_setback_q [i] ), + .sw_resynch_req_o ( dmr_resynch_req_o [i] ), + .sw_synch_req_o ( dmr_sw_synch_req [i] ), + .grp_in_independent_o ( dmr_grp_in_independent[i] ), + .rapid_recovery_en_o ( dmr_rapid_recovery_en [i] ), + .dmr_incr_mismatches_o ( {dmr_incr_mismatches[dmr_core_id(i, 0)], dmr_incr_mismatches[dmr_core_id(i, 1)]} ), + .dmr_error_i ( dmr_failure [i] ), + + .fetch_en_i ( sys_fetch_en_i[dmr_core_id(i, 0)] ), + .cores_synch_i ( dmr_cores_synch_i[i] ), + + .recovery_request_o ( dmr_start_recovery [i] ), + .recovery_finished_i ( dmr_recovery_finished[i] ) + ); + + assign dmr_sw_synch_req_o[dmr_core_id(i, 0)] = dmr_sw_synch_req[i]; + assign dmr_sw_synch_req_o[dmr_core_id(i, 1)] = dmr_sw_synch_req[i]; + + /********************* + * DMR Core Checkers * + *********************/ + DMR_checker #( + .DataWidth ( MainConcatWidth ) + ) dmr_core_checker_main ( + .inp_a_i ( main_concat_in [dmr_core_id(i, 0)] ), + .inp_b_i ( main_concat_in [dmr_core_id(i, 1)] ), + .check_o ( main_dmr_out [i] ), + .error_o ( dmr_failure_main [i] ) + ); + if (SeparateData) begin : gen_data_checker + DMR_checker # ( + .DataWidth ( DataConcatWidth ) + ) dmr_core_checker_data ( + .inp_a_i ( data_concat_in [dmr_core_id(i, 0)] ), + .inp_b_i ( data_concat_in [dmr_core_id(i, 1)] ), + .check_o ( data_dmr_out [i] ), + .error_o ( dmr_failure_data [i] ) + ); + assign {dmr_core_busy_out[i], dmr_irq_ack_out[i] , dmr_irq_ack_id_out[i], + dmr_instr_req_out[i], dmr_instr_addr_out[i], dmr_data_req_out[i], + // CSRs signals + dmr_backup_csr[i].csr_mstatus , // 7-bits + dmr_backup_csr[i].csr_mie , // 32-bits + dmr_backup_csr[i].csr_mtvec , // 24-bits + dmr_backup_csr[i].csr_mscratch, // 32-bits + dmr_backup_csr[i].csr_mip , // 32-bits + dmr_backup_csr[i].csr_mepc , // 32-bits + dmr_backup_csr[i].csr_mcause , // 6-bits + // PC signals + dmr_backup_program_counter[i], // 32-bits + dmr_backup_branch_int[i], dmr_backup_branch_addr_int[i], // 1-bits + 32-bits + // RF signals + dmr_backup_regfile_wdata_a[i], // 32-bits + dmr_backup_regfile_waddr_a[i], // 6-bits + dmr_backup_regfile_wdata_b[i], // 32-bits + dmr_backup_regfile_waddr_b[i]} // 6-bits + = main_dmr_out[i]; + assign {dmr_data_add_out[i], dmr_data_wen_out[i] , dmr_data_wdata_out[i], + dmr_data_be_out[i] , dmr_data_user_out[i] } + = data_dmr_out[i]; + end else begin : gen_data_in_main + assign dmr_failure_data[i] = 1'b0; + assign {dmr_core_busy_out[i], dmr_irq_ack_out[i] , dmr_irq_ack_id_out[i], + dmr_instr_req_out[i], dmr_instr_addr_out[i], dmr_data_req_out[i] , + dmr_data_add_out[i] , dmr_data_wen_out[i] , dmr_data_wdata_out[i], + dmr_data_be_out[i] , dmr_data_user_out[i], + // CSRs signals + dmr_backup_csr[i].csr_mstatus , // 7-bits + dmr_backup_csr[i].csr_mie , // 32-bits + dmr_backup_csr[i].csr_mtvec , // 24-bits + dmr_backup_csr[i].csr_mscratch, // 32-bits + dmr_backup_csr[i].csr_mip , // 32-bits + dmr_backup_csr[i].csr_mepc , // 32-bits + dmr_backup_csr[i].csr_mcause , // 6-bits + // PC signals + dmr_backup_program_counter[i], // 32-bits + dmr_backup_branch_int[i], dmr_backup_branch_addr_int[i], // 1-bits + 32-bits + // RF signals + dmr_backup_regfile_wdata_a[i], // 32-bits + dmr_backup_regfile_waddr_a[i], // 6-bits + dmr_backup_regfile_wdata_b[i], // 32-bits + dmr_backup_regfile_waddr_b[i]} // 6-bits + = main_dmr_out[i]; + end + + if (RapidRecovery) begin : gen_rapid_recovery_connection + + assign dmr_failure [i] = (dmr_data_req_out [i] ? (dmr_failure_main[i] | dmr_failure_data[i]) + : dmr_failure_main[i]) ; + + assign dmr_backup_regfile_we_a [i] = backup_regfile_wport_i[dmr_core_id(i, 0)].we_a + & backup_regfile_wport_i[dmr_core_id(i, 1)].we_a + & ~dmr_failure [i]; + + assign dmr_backup_regfile_we_b [i] = backup_regfile_wport_i[dmr_core_id(i, 0)].we_b + & backup_regfile_wport_i[dmr_core_id(i, 1)].we_b + & ~dmr_failure [i]; + + end else begin : gen_standard_failure + assign dmr_failure [i] = dmr_data_req_out [i] ? (dmr_failure_main[i] | dmr_failure_data[i]) + : dmr_failure_main[i]; + end + end + end else begin: no_dmr_checkers + assign dmr_failure_main = '0; + assign dmr_failure_data = '0; + assign dmr_failure = '0; + assign dmr_incr_mismatches = '0; + assign main_dmr_out = '0; + assign data_dmr_out = '0; + assign {dmr_core_busy_out, dmr_irq_ack_out , dmr_irq_ack_id_out, + dmr_instr_req_out, dmr_instr_addr_out, dmr_data_req_out , + dmr_data_add_out , dmr_data_wen_out , dmr_data_wdata_out, + dmr_data_be_out , dmr_data_user_out} + = '0; + assign top_register_resps[2].rdata = '0; + assign top_register_resps[2].error = 1'b1; + assign top_register_resps[2].ready = 1'b1; + assign dmr_sw_synch_req_o = '0; + end + + // RapidRecovery output signals + if (RapidRecovery) begin : gen_rapid_recovery + for (genvar i = 0; i < NumBackupRegfiles; i++) begin : gen_groups + // Write Enable signal for backup registers + assign rapid_recovery_backup_enable[i] = core_in_tmr[i] ? (i < NumTMRGroups ? backup_enable[i] : 1'b0) // TMR mode + : core_in_dmr[i] ? (backup_enable[i] & ~dmr_failure[i] ) // DMR mode + : 1'b1; // Independent mode + // TODO: Only supports interleaved mode!!! + + + + hmr_rapid_recovery_ctrl #( + .RFAddrWidth( RFAddrWidth ) + ) i_rapid_recovery_ctrl ( + .clk_i, + .rst_ni, + .start_recovery_i ( start_recovery [i] ), + .recovery_finished_o ( recovery_finished [i] ), + .setback_o ( recovery_setback_out [i] ), + .instr_lock_o ( recovery_instr_lock_out [i] ), + .debug_req_o ( recovery_debug_req_out [i] ), + .debug_halt_i ( recovery_debug_halted_in [i] ), + .debug_resume_o ( recovery_debug_resume_out [i] ), + .recovery_regfile_waddr_o ( core_recovery_regfile_wport_out[i] ), + .backup_enable_o ( backup_enable [i] ), + .recover_csr_enable_o ( recovery_csr_enable_out [i] ), + .recover_pc_enable_o ( recovery_pc_enable_out [i] ), + .recover_rf_enable_o ( recovery_trigger_out [i] ) + ); + + /************************* + * Recovery CS Registers * + *************************/ + recovery_csr #( + .ECCEnabled ( 1 ) + ) RCSR ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .read_enable_i ( recovery_csr_enable_out [i] ), + .write_enable_i ( rapid_recovery_backup_enable [i] ), + .backup_csr_i ( backup_csr_int [i] ), + .recovery_csr_o ( recovery_csr_out [i] ) + ); + + /**************************** + * Recovery Program Counter * + ****************************/ + recovery_pc #( + .ECCEnabled ( 1 ) + ) RPC ( + // Control Ports + .clk_i, + .rst_ni, + .clear_i ( '0 ), + .read_enable_i ( recovery_pc_enable_out [i] ), + .write_enable_i ( rapid_recovery_backup_enable [i] ), + // Backup Ports + .backup_program_counter_i ( backup_program_counter_int [i] ), + .backup_branch_i ( backup_branch_int [i] ), + .backup_branch_addr_i ( backup_branch_addr_int [i] ), + // Recovery Pors + .recovery_program_counter_o ( recovery_program_counter_out [i] ), + .recovery_branch_o ( recovery_branch_out [i] ), + .recovery_branch_addr_o ( recovery_branch_addr_out [i] ) + ); + + /*************************** + * Recovery Register Files * + ***************************/ + recovery_rf #( + .ECCEnabled ( 1 ), + .ADDR_WIDTH ( RFAddrWidth ) + ) RRF ( + .clk_i, + .rst_ni, + .test_en_i ( '0 ), + //Read port A + .raddr_a_i ( core_recovery_regfile_wport_out[i].waddr_a ), + .rdata_a_o ( core_recovery_regfile_rdata_out[i].rdata_a ), + //Read port B + .raddr_b_i ( core_recovery_regfile_wport_out[i].waddr_b ), + .rdata_b_o ( core_recovery_regfile_rdata_out[i].rdata_b ), + //Read port C + .raddr_c_i ( '0 ), + .rdata_c_o ( ), + // Write Port A + .waddr_a_i ( backup_regfile_waddr_a [i] ), + .wdata_a_i ( backup_regfile_wdata_a [i] ), + .we_a_i ( backup_regfile_we_a[i] & + rapid_recovery_backup_enable [i] ), + // Write Port B + .waddr_b_i ( backup_regfile_waddr_b [i] ), + .wdata_b_i ( backup_regfile_wdata_b [i] ), + .we_b_i ( backup_regfile_we_b[i] & + rapid_recovery_backup_enable [i] ) + ); + + end + + always_comb begin : proc_dmr_tmr_assignments + backup_csr_int = '0; + backup_program_counter_int = '0; + backup_program_counter_error = '0; + backup_branch_int = '0; + backup_branch_addr_int = '0; + backup_regfile_wdata_a = '0; + backup_regfile_wdata_b = '0; + backup_regfile_we_a = '0; + backup_regfile_we_b = '0; + backup_regfile_waddr_a = '0; + backup_regfile_waddr_b = '0; + start_recovery = '0; + dmr_recovery_finished = '0; + tmr_recovery_finished = '0; + recovery_debug_halted_in = '0; + + // Continually backup master cores in interleaved mode for fast entry + if (InterleaveGrps) begin + for (int i = 0; i < NumBackupRegfiles; i++) begin + backup_csr_int [i] = backup_csr_i [i]; + backup_program_counter_int [i] = backup_program_counter_i [i]; + backup_branch_int [i] = backup_branch_i [i]; + backup_branch_addr_int [i] = backup_branch_addr_i [i]; + backup_regfile_wdata_a [i] = backup_regfile_wport_i[i].wdata_a; + backup_regfile_wdata_b [i] = backup_regfile_wport_i[i].wdata_b; + backup_regfile_we_a [i] = backup_regfile_wport_i[i].we_a; + backup_regfile_we_b [i] = backup_regfile_wport_i[i].we_b; + backup_regfile_waddr_a [i] = backup_regfile_wport_i[i].waddr_a; + backup_regfile_waddr_b [i] = backup_regfile_wport_i[i].waddr_b; + end + end + + for (int i = 0; i < NumDMRGroups; i++) begin + if ((DMRFixed || (DMRSupported && ~dmr_grp_in_independent[i])) && dmr_core_rapid_recovery_en[dmr_core_id(i, 0)]) begin + backup_csr_int [dmr_shared_id(i)] = dmr_backup_csr [i]; + backup_program_counter_int [dmr_shared_id(i)] = dmr_backup_program_counter [i]; + // backup_program_counter_error[dmr_shared_id(i)] = dmr_backup_program_counter_error[i]; + backup_branch_int [dmr_shared_id(i)] = dmr_backup_branch_int [i]; + backup_branch_addr_int [dmr_shared_id(i)] = dmr_backup_branch_addr_int [i]; + backup_regfile_wdata_a [dmr_shared_id(i)] = dmr_backup_regfile_wdata_a [i]; + backup_regfile_wdata_b [dmr_shared_id(i)] = dmr_backup_regfile_wdata_b [i]; + backup_regfile_we_a [dmr_shared_id(i)] = dmr_backup_regfile_we_a [i]; + backup_regfile_we_b [dmr_shared_id(i)] = dmr_backup_regfile_we_b [i]; + backup_regfile_waddr_a [dmr_shared_id(i)] = dmr_backup_regfile_waddr_a [i]; + backup_regfile_waddr_b [dmr_shared_id(i)] = dmr_backup_regfile_waddr_b [i]; + start_recovery [dmr_shared_id(i)] = dmr_start_recovery [i]; + dmr_recovery_finished[i] = recovery_finished[dmr_shared_id(i)]; + recovery_debug_halted_in [dmr_shared_id(i)] = core_debug_halted_i [dmr_core_id(dmr_group_id(i), 0)] + & core_debug_halted_i [dmr_core_id(dmr_group_id(i), 1)]; + end + end + + for (int i = 0; i < NumTMRGroups; i++) begin + if ((TMRFixed || (TMRSupported && ~tmr_grp_in_independent[i])) && tmr_core_rapid_recovery_en[tmr_core_id(i, 0)]) begin + backup_csr_int [tmr_shared_id(i)] = tmr_backup_csr [i]; + backup_program_counter_int [tmr_shared_id(i)] = tmr_backup_program_counter [i]; + backup_branch_int [tmr_shared_id(i)] = tmr_backup_branch_int [i]; + backup_branch_addr_int [tmr_shared_id(i)] = tmr_backup_branch_addr_int [i]; + backup_regfile_wdata_a [tmr_shared_id(i)] = tmr_backup_regfile_wdata_a [i]; + backup_regfile_wdata_b [tmr_shared_id(i)] = tmr_backup_regfile_wdata_b [i]; + backup_regfile_we_a [tmr_shared_id(i)] = tmr_backup_regfile_we_a [i]; + backup_regfile_we_b [tmr_shared_id(i)] = tmr_backup_regfile_we_b [i]; + backup_regfile_waddr_a [tmr_shared_id(i)] = tmr_backup_regfile_waddr_a [i]; + backup_regfile_waddr_b [tmr_shared_id(i)] = tmr_backup_regfile_waddr_b [i]; + start_recovery [tmr_shared_id(i)] = tmr_start_recovery [i]; + tmr_recovery_finished[i] = recovery_finished[tmr_shared_id(i)]; + recovery_debug_halted_in [tmr_shared_id(i)] = core_debug_halted_i [tmr_core_id(tmr_group_id(i), 0)] + & core_debug_halted_i [tmr_core_id(tmr_group_id(i), 1)] + & core_debug_halted_i [tmr_core_id(tmr_group_id(i), 2)]; + end + end + end + + for (genvar i = 0; i < NumCores; i++) begin : gen_cores + always_comb begin + if ((DMRFixed || (DMRSupported && core_in_dmr[i])) && dmr_core_rapid_recovery_en[i]) begin + + core_debug_resume_o [i] = recovery_debug_resume_out [dmr_shared_id(dmr_group_id(i))]; + + // Setback + core_recover_o [i] = recovery_trigger_out [dmr_shared_id(dmr_group_id(i))]; + core_instr_lock_o [i] = recovery_instr_lock_out [dmr_shared_id(dmr_group_id(i))]; + + // CSRs + recovery_csr_o [i] = recovery_csr_out [dmr_shared_id(dmr_group_id(i))]; + + // PC + pc_recover_o [i] = recovery_pc_enable_out [dmr_shared_id(dmr_group_id(i))]; + recovery_program_counter_o [i] = recovery_program_counter_out [dmr_shared_id(dmr_group_id(i))]; + recovery_branch_o [i] = recovery_branch_out [dmr_shared_id(dmr_group_id(i))]; + recovery_branch_addr_o [i] = recovery_branch_addr_out [dmr_shared_id(dmr_group_id(i))]; + + // RF + core_recovery_regfile_wport_o[i].we_a = core_recovery_regfile_wport_out[dmr_shared_id(dmr_group_id(i))].we_a; + core_recovery_regfile_wport_o[i].waddr_a = core_recovery_regfile_wport_out[dmr_shared_id(dmr_group_id(i))].waddr_a; + core_recovery_regfile_wport_o[i].wdata_a = core_recovery_regfile_rdata_out[dmr_shared_id(dmr_group_id(i))].rdata_a; + core_recovery_regfile_wport_o[i].we_b = core_recovery_regfile_wport_out[dmr_shared_id(dmr_group_id(i))].we_b; + core_recovery_regfile_wport_o[i].waddr_b = core_recovery_regfile_wport_out[dmr_shared_id(dmr_group_id(i))].waddr_b; + core_recovery_regfile_wport_o[i].wdata_b = core_recovery_regfile_rdata_out[dmr_shared_id(dmr_group_id(i))].rdata_b; + + end else if ((TMRFixed || (TMRSupported && core_in_tmr[i])) && tmr_core_rapid_recovery_en[i]) begin + core_debug_resume_o [i] = recovery_debug_resume_out [tmr_shared_id(tmr_group_id(i))]; + + // Setback + core_recover_o [i] = recovery_trigger_out [tmr_shared_id(tmr_group_id(i))]; + core_instr_lock_o [i] = recovery_instr_lock_out [tmr_shared_id(tmr_group_id(i))]; + + // CSRs + recovery_csr_o [i] = recovery_csr_out [tmr_shared_id(tmr_group_id(i))]; + + // PC + pc_recover_o [i] = recovery_pc_enable_out [tmr_shared_id(tmr_group_id(i))]; + recovery_program_counter_o [i] = recovery_program_counter_out [tmr_shared_id(tmr_group_id(i))]; + recovery_branch_o [i] = recovery_branch_out [tmr_shared_id(tmr_group_id(i))]; + recovery_branch_addr_o [i] = recovery_branch_addr_out [tmr_shared_id(tmr_group_id(i))]; + + // RF + // core_regfile_raddr_o [i] = core_regfile_raddr_out [tmr_shared_id(tmr_group_id(i))]; + core_recovery_regfile_wport_o[i].we_a = core_recovery_regfile_wport_out[tmr_shared_id(tmr_group_id(i))].we_a; + core_recovery_regfile_wport_o[i].waddr_a = core_recovery_regfile_wport_out[tmr_shared_id(tmr_group_id(i))].waddr_a; + core_recovery_regfile_wport_o[i].wdata_a = core_recovery_regfile_rdata_out[tmr_shared_id(tmr_group_id(i))].rdata_a; + core_recovery_regfile_wport_o[i].we_b = core_recovery_regfile_wport_out[tmr_shared_id(tmr_group_id(i))].we_b; + core_recovery_regfile_wport_o[i].waddr_b = core_recovery_regfile_wport_out[tmr_shared_id(tmr_group_id(i))].waddr_b; + core_recovery_regfile_wport_o[i].wdata_b = core_recovery_regfile_rdata_out[tmr_shared_id(tmr_group_id(i))].rdata_b; + + end else begin + // Disable RapidRecovery + core_debug_resume_o [i] = '0; + + // Setback + core_recover_o [i] = '0; + core_instr_lock_o [i] = '0; + + // CSRs + recovery_csr_o [i] = '0; + + // PC + pc_recover_o [i] = '0; + recovery_program_counter_o [i] = '0; + recovery_branch_o [i] = '0; + recovery_branch_addr_o [i] = '0; + + // RF + core_recovery_regfile_wport_o[i].we_a = '0; + core_recovery_regfile_wport_o[i].waddr_a = '0; + core_recovery_regfile_wport_o[i].wdata_a = '0; + core_recovery_regfile_wport_o[i].we_b = '0; + core_recovery_regfile_wport_o[i].waddr_b = '0; + core_recovery_regfile_wport_o[i].wdata_b = '0; + end + end + end + + end else begin : gen_sw_recovery + for (genvar i = 0; i < NumCores; i++) begin : gen_cores + // Disable RapidRecovery + assign core_debug_resume_o [i] = '0; + + // Setback + assign core_recover_o [i] = '0; + assign core_instr_lock_o [i] = '0; + + // CSRs + assign recovery_csr_o [i] = '0; + + // PC + assign pc_recover_o [i] = '0; + assign recovery_program_counter_o [i] = '0; + assign recovery_branch_o [i] = '0; + assign recovery_branch_addr_o [i] = '0; + + // RF + assign core_recovery_regfile_wport_o[i].we_a = '0; + assign core_recovery_regfile_wport_o[i].waddr_a = '0; + assign core_recovery_regfile_wport_o[i].wdata_a = '0; + assign core_recovery_regfile_wport_o[i].we_b = '0; + assign core_recovery_regfile_wport_o[i].waddr_b = '0; + assign core_recovery_regfile_wport_o[i].wdata_b = '0; + end + end + + // Assign output signals + if (DMRSupported && TMRSupported) begin : gen_full_HMR + /***************** + *** TMR & DMR *** + *****************/ + if (TMRFixed || DMRFixed) $fatal(1, "Cannot support both TMR and DMR and fix one!"); + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam TMRCoreIndex = tmr_core_id(tmr_group_id(i), 0); + localparam DMRCoreIndex = dmr_core_id(dmr_group_id(i), 0); + + always_comb begin + // Special signals + if (RapidRecovery) begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)] + | (core_in_dmr[i] ? recovery_setback_out [dmr_shared_id(dmr_group_id(i))] : + (core_in_tmr[i] ? recovery_setback_out [tmr_shared_id(tmr_group_id(i))] : '0)); + end else begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)]; + end + if (i >= NumTMRCores && i >= NumDMRCores) begin + core_setback_o [i] = '0; + end else if (i < NumTMRCores && i >= NumDMRCores) begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | (RapidRecovery ? (core_in_tmr[i] ? recovery_setback_out [tmr_shared_id(tmr_group_id(i))] : '0) : '0); + end else if (i >= NumTMRCores && i < NumDMRCores) begin + core_setback_o [i] = dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)] + | (RapidRecovery ? (core_in_dmr[i] ? recovery_setback_out [dmr_shared_id(dmr_group_id(i))] : '0) : '0); + end + if (i < NumTMRCores && core_in_tmr[i]) begin : tmr_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [TMRCoreIndex]; + core_cluster_id_o [i] = sys_cluster_id_i [TMRCoreIndex]; + + core_clock_en_o [i] = sys_clock_en_i [TMRCoreIndex]; + core_fetch_en_o [i] = sys_fetch_en_i [TMRCoreIndex]; + core_boot_addr_o [i] = sys_boot_addr_i [TMRCoreIndex]; + + if (RapidRecovery) begin + core_debug_req_o [i] = sys_debug_req_i [TMRCoreIndex] + | recovery_debug_req_out [tmr_shared_id(tmr_group_id(i))]; + end else begin + core_debug_req_o [i] = sys_debug_req_i [TMRCoreIndex]; + end + core_perf_counters_o[i] = sys_perf_counters_i[TMRCoreIndex]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [TMRCoreIndex]; + core_irq_id_o [i] = sys_irq_id_i [TMRCoreIndex]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [TMRCoreIndex]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[TMRCoreIndex]; + core_instr_r_valid_o[i] = filt_instr_r_valid[TMRCoreIndex]; + core_instr_err_o [i] = sys_instr_err_i [TMRCoreIndex]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [TMRCoreIndex]; + core_data_r_opc_o [i] = sys_data_r_opc_i [TMRCoreIndex]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [TMRCoreIndex]; + core_data_r_user_o [i] = sys_data_r_user_i [TMRCoreIndex]; + core_data_r_valid_o [i] = filt_data_r_valid [TMRCoreIndex]; + core_data_err_o [i] = sys_data_err_i [TMRCoreIndex]; + end else if (i < NumDMRCores && core_in_dmr[i]) begin : dmr_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [DMRCoreIndex]; + core_cluster_id_o [i] = sys_cluster_id_i [DMRCoreIndex]; + + core_clock_en_o [i] = sys_clock_en_i [DMRCoreIndex]; + core_fetch_en_o [i] = sys_fetch_en_i [DMRCoreIndex]; + core_boot_addr_o [i] = sys_boot_addr_i [DMRCoreIndex]; + + if (RapidRecovery) begin + core_debug_req_o [i] = sys_debug_req_i [DMRCoreIndex] + | recovery_debug_req_out [dmr_shared_id(dmr_group_id(i))]; + end else begin + core_debug_req_o [i] = sys_debug_req_i [DMRCoreIndex]; + end + core_perf_counters_o[i] = sys_perf_counters_i[DMRCoreIndex]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [DMRCoreIndex]; + core_irq_id_o [i] = sys_irq_id_i [DMRCoreIndex]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [DMRCoreIndex]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[DMRCoreIndex]; + core_instr_r_valid_o[i] = filt_instr_r_valid[DMRCoreIndex]; + core_instr_err_o [i] = sys_instr_err_i [DMRCoreIndex]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [DMRCoreIndex]; + core_data_r_opc_o [i] = sys_data_r_opc_i [DMRCoreIndex]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [DMRCoreIndex]; + core_data_r_user_o [i] = sys_data_r_user_i [DMRCoreIndex]; + core_data_r_valid_o [i] = filt_data_r_valid [DMRCoreIndex]; + core_data_err_o [i] = sys_data_err_i [DMRCoreIndex]; + end else begin : independent_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [i]; + core_cluster_id_o [i] = sys_cluster_id_i [i]; + + core_clock_en_o [i] = sys_clock_en_i [i]; + core_fetch_en_o [i] = sys_fetch_en_i [i]; + core_boot_addr_o [i] = sys_boot_addr_i [i]; + + core_debug_req_o [i] = sys_debug_req_i [i]; + core_perf_counters_o[i] = sys_perf_counters_i[i]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [i]; + core_irq_id_o [i] = sys_irq_id_i [i]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [i]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[i]; + core_instr_r_valid_o[i] = filt_instr_r_valid[i]; + core_instr_err_o [i] = sys_instr_err_i [i]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [i]; + core_data_r_opc_o [i] = sys_data_r_opc_i [i]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [i]; + core_data_r_user_o [i] = sys_data_r_user_i [i]; + core_data_r_valid_o [i] = filt_data_r_valid [i]; + core_data_err_o [i] = sys_data_err_i [i]; + end + end + end + + for (genvar i = 0; i < NumSysCores/*==NumCores*/; i++) begin : gen_core_outputs + localparam TMRCoreIndex = tmr_group_id(i); + localparam DMRCoreIndex = dmr_group_id(i); + always_comb begin + if (i < NumTMRCores && core_in_tmr[i]) begin : tmr_mode + if (tmr_core_id(tmr_group_id(i), 0) == i) begin : is_tmr_main_core + // CTRL + sys_core_busy_o [i] = tmr_core_busy_out [TMRCoreIndex]; + + // IRQ + sys_irq_ack_o [i] = tmr_irq_ack_out [TMRCoreIndex]; + sys_irq_ack_id_o [i] = tmr_irq_ack_id_out[TMRCoreIndex]; + + // INSTR + filt_instr_req [i] = tmr_instr_req_out [TMRCoreIndex]; + filt_instr_addr [i] = tmr_instr_addr_out[TMRCoreIndex]; + + // DATA + filt_data_req [i] = tmr_data_req_out [TMRCoreIndex]; + filt_data_addr [i] = tmr_data_add_out [TMRCoreIndex]; + filt_data_we [i] = tmr_data_wen_out [TMRCoreIndex]; + filt_data_data [i] = tmr_data_wdata_out[TMRCoreIndex]; + sys_data_user_o [i] = tmr_data_user_out [TMRCoreIndex]; + filt_data_be [i] = tmr_data_be_out [TMRCoreIndex]; + end else begin : disable_core // Assign disable + // CTLR + sys_core_busy_o [i] = '0; + + // IRQ + sys_irq_ack_o [i] = '0; + sys_irq_ack_id_o [i] = '0; + + // INSTR + filt_instr_req [i] = '0; + filt_instr_addr [i] = '0; + + // DATA + filt_data_req [i] = '0; + filt_data_addr [i] = '0; + filt_data_we [i] = '0; + filt_data_data [i] = '0; + sys_data_user_o [i] = '0; + filt_data_be [i] = '0; + end + end else if (i < NumDMRCores && core_in_dmr[i]) begin : dmr_mode + if (dmr_core_id(dmr_group_id(i), 0) == i) begin : is_dmr_main_core + // CTRL + sys_core_busy_o [i] = dmr_core_busy_out [DMRCoreIndex]; + + // IRQ + sys_irq_ack_o [i] = dmr_irq_ack_out [DMRCoreIndex]; + sys_irq_ack_id_o [i] = dmr_irq_ack_id_out[DMRCoreIndex]; + + // INSTR + filt_instr_req [i] = dmr_instr_req_out [DMRCoreIndex]; + filt_instr_addr [i] = dmr_instr_addr_out[DMRCoreIndex]; + + // DATA + filt_data_req [i] = dmr_data_req_out [DMRCoreIndex]; + filt_data_addr [i] = dmr_data_add_out [DMRCoreIndex]; + filt_data_we [i] = dmr_data_wen_out [DMRCoreIndex]; + filt_data_data [i] = dmr_data_wdata_out[DMRCoreIndex]; + sys_data_user_o [i] = dmr_data_user_out [DMRCoreIndex]; + filt_data_be [i] = dmr_data_be_out [DMRCoreIndex]; + end else begin : disable_core // Assign disable + // CTLR + sys_core_busy_o [i] = '0; + + // IRQ + sys_irq_ack_o [i] = '0; + sys_irq_ack_id_o [i] = '0; + + // INSTR + filt_instr_req [i] = '0; + filt_instr_addr [i] = '0; + + // DATA + filt_data_req [i] = '0; + filt_data_addr [i] = '0; + filt_data_we [i] = '0; + filt_data_data [i] = '0; + sys_data_user_o [i] = '0; + filt_data_be [i] = '0; + end + end else begin : independent_mode + // CTRL + sys_core_busy_o [i] = core_core_busy_i [i]; + + // IRQ + sys_irq_ack_o [i] = core_irq_ack_i [i]; + sys_irq_ack_id_o [i] = core_irq_ack_id_i[i]; + + // INSTR + filt_instr_req [i] = core_instr_req_i [i]; + filt_instr_addr [i] = core_instr_addr_i[i]; + + // DATA + filt_data_req [i] = core_data_req_i [i]; + filt_data_addr [i] = core_data_add_i [i]; + filt_data_we [i] = core_data_wen_i [i]; + filt_data_data [i] = core_data_wdata_i[i]; + sys_data_user_o [i] = core_data_user_i [i]; + filt_data_be [i] = core_data_be_i [i]; + end + end + end + + end else if (TMRSupported || TMRFixed) begin : gen_TMR_only + /***************** + *** TMR only *** + *****************/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam SysCoreIndex = TMRFixed ? i/3 : tmr_core_id(tmr_group_id(i), 0); + always_comb begin + // Special signals + // Setback + if (RapidRecovery) begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)] + | recovery_setback_out [tmr_shared_id(tmr_group_id(i))]; + end else begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)]; + end + if (i >= NumTMRCores) begin + core_setback_o [i] = '0; + end + if (i < NumTMRCores && (TMRFixed || core_in_tmr[i])) begin : tmr_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [SysCoreIndex]; + core_cluster_id_o [i] = sys_cluster_id_i [SysCoreIndex]; + + core_clock_en_o [i] = sys_clock_en_i [SysCoreIndex]; + core_fetch_en_o [i] = sys_fetch_en_i [SysCoreIndex]; + core_boot_addr_o [i] = sys_boot_addr_i [SysCoreIndex]; + + if (RapidRecovery) begin + core_debug_req_o [i] = sys_debug_req_i [SysCoreIndex] + | recovery_debug_req_out [tmr_shared_id(tmr_group_id(i))]; + end else begin + core_debug_req_o [i] = sys_debug_req_i [SysCoreIndex]; + end + core_perf_counters_o[i] = sys_perf_counters_i[SysCoreIndex]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [SysCoreIndex]; + core_irq_id_o [i] = sys_irq_id_i [SysCoreIndex]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [SysCoreIndex]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[SysCoreIndex]; + core_instr_r_valid_o[i] = filt_instr_r_valid[SysCoreIndex]; + core_instr_err_o [i] = sys_instr_err_i [SysCoreIndex]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [SysCoreIndex]; + core_data_r_opc_o [i] = sys_data_r_opc_i [SysCoreIndex]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [SysCoreIndex]; + core_data_r_user_o [i] = sys_data_r_user_i [SysCoreIndex]; + core_data_r_valid_o [i] = filt_data_r_valid [SysCoreIndex]; + core_data_err_o [i] = sys_data_err_i [SysCoreIndex]; + end else begin : independent_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [i]; + core_cluster_id_o [i] = sys_cluster_id_i [i]; + + core_clock_en_o [i] = sys_clock_en_i [i]; + core_fetch_en_o [i] = sys_fetch_en_i [i]; + core_boot_addr_o [i] = sys_boot_addr_i [i]; + + core_debug_req_o [i] = sys_debug_req_i [i]; + core_perf_counters_o[i] = sys_perf_counters_i[i]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [i]; + core_irq_id_o [i] = sys_irq_id_i [i]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [i]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[i]; + core_instr_r_valid_o[i] = filt_instr_r_valid[i]; + core_instr_err_o [i] = sys_instr_err_i [i]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [i]; + core_data_r_opc_o [i] = sys_data_r_opc_i [i]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [i]; + core_data_r_user_o [i] = sys_data_r_user_i [i]; + core_data_r_valid_o [i] = filt_data_r_valid [i]; + core_data_err_o [i] = sys_data_err_i [i]; + end + end + end + + for (genvar i = 0; i < NumSysCores; i++) begin : gen_core_outputs + localparam CoreCoreIndex = TMRFixed ? i : tmr_group_id(i); + if (TMRFixed && i < NumTMRGroups) begin : fixed_tmr + // CTRL + assign sys_core_busy_o [i] = tmr_core_busy_out[CoreCoreIndex]; + + // IRQ + assign sys_irq_ack_o [i] = tmr_irq_ack_out [CoreCoreIndex]; + assign sys_irq_ack_id_o [i] = tmr_irq_ack_id_out[CoreCoreIndex]; + + // INSTR + assign filt_instr_req [i] = tmr_instr_req_out [CoreCoreIndex]; + assign filt_instr_addr [i] = tmr_instr_addr_out[CoreCoreIndex]; + + // DATA + assign filt_data_req [i] = tmr_data_req_out [CoreCoreIndex]; + assign filt_data_addr [i] = tmr_data_add_out [CoreCoreIndex]; + assign filt_data_we [i] = tmr_data_wen_out [CoreCoreIndex]; + assign filt_data_data [i] = tmr_data_wdata_out[CoreCoreIndex]; + assign sys_data_user_o [i] = tmr_data_user_out [CoreCoreIndex]; + assign filt_data_be [i] = tmr_data_be_out [CoreCoreIndex]; + end else begin + if (i >= NumTMRCores) begin : independent_stragglers + // CTRL + assign sys_core_busy_o [i] = core_core_busy_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // IRQ + assign sys_irq_ack_o [i] = core_irq_ack_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_irq_ack_id_o [i] = core_irq_ack_id_i[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // INSTR + assign filt_instr_req [i] = core_instr_req_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_instr_addr [i] = core_instr_addr_i[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // DATA + assign filt_data_req [i] = core_data_req_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_addr [i] = core_data_add_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_we [i] = core_data_wen_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_data [i] = core_data_wdata_i[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_data_user_o [i] = core_data_user_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_be [i] = core_data_be_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + end else begin + always_comb begin + if (core_in_tmr[i]) begin : tmr_mode + if (tmr_core_id(tmr_group_id(i), 0) == i) begin : is_tmr_main_core + // CTRL + sys_core_busy_o [i] = tmr_core_busy_out[CoreCoreIndex]; + + // IRQ + sys_irq_ack_o [i] = tmr_irq_ack_out [CoreCoreIndex]; + sys_irq_ack_id_o [i] = tmr_irq_ack_id_out[CoreCoreIndex]; + + // INSTR + filt_instr_req [i] = tmr_instr_req_out [CoreCoreIndex]; + filt_instr_addr [i] = tmr_instr_addr_out[CoreCoreIndex]; + + // DATA + filt_data_req [i] = tmr_data_req_out [CoreCoreIndex]; + filt_data_addr [i] = tmr_data_add_out [CoreCoreIndex]; + filt_data_we [i] = tmr_data_wen_out [CoreCoreIndex]; + filt_data_data [i] = tmr_data_wdata_out[CoreCoreIndex]; + sys_data_user_o [i] = tmr_data_user_out [CoreCoreIndex]; + filt_data_be [i] = tmr_data_be_out [CoreCoreIndex]; + end else begin : disable_core // Assign disable + // CTLR + sys_core_busy_o [i] = '0; + + // IRQ + sys_irq_ack_o [i] = '0; + sys_irq_ack_id_o [i] = '0; + + // INSTR + filt_instr_req [i] = '0; + filt_instr_addr [i] = '0; + + // DATA + filt_data_req [i] = '0; + filt_data_addr [i] = '0; + filt_data_we [i] = '0; + filt_data_data [i] = '0; + sys_data_user_o [i] = '0; + filt_data_be [i] = '0; + end + end else begin : independent_mode + // CTRL + sys_core_busy_o [i] = core_core_busy_i [i]; + + // IRQ + sys_irq_ack_o [i] = core_irq_ack_i [i]; + sys_irq_ack_id_o [i] = core_irq_ack_id_i[i]; + + // INSTR + filt_instr_req [i] = core_instr_req_i [i]; + filt_instr_addr [i] = core_instr_addr_i[i]; + + // DATA + filt_data_req [i] = core_data_req_i [i]; + filt_data_addr [i] = core_data_add_i [i]; + filt_data_we [i] = core_data_wen_i [i]; + filt_data_data [i] = core_data_wdata_i[i]; + sys_data_user_o [i] = core_data_user_i [i]; + filt_data_be [i] = core_data_be_i [i]; + end + end + end + end + end + + end else if (DMRSupported || DMRFixed) begin : gen_DMR_only + /***************** + *** DMR only *** + *****************/ + if (DMRFixed && NumCores % 2 != 0) $warning("Extra cores added not properly handled! :)"); + // Binding DMR outputs to zero for now + assign dmr_failure_o = '0; + assign dmr_error_o = '0; + // assign dmr_resynch_req_o = '0; + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam SysCoreIndex = DMRFixed ? i/2 : dmr_core_id(dmr_group_id(i), 0); + always_comb begin + // Setback + if (RapidRecovery) begin + core_setback_o [i] = dmr_setback_q[dmr_group_id(i)][dmr_offset_id(i)] + | recovery_setback_out [dmr_shared_id(dmr_group_id(i))]; + end else begin + core_setback_o [i] = '0; + end + if (i >= NumDMRCores) begin + core_setback_o [i] = '0; + end + if (i < NumDMRCores && (DMRFixed || core_in_dmr[i])) begin : dmr_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [SysCoreIndex]; + core_cluster_id_o [i] = sys_cluster_id_i [SysCoreIndex]; + + core_clock_en_o [i] = sys_clock_en_i [SysCoreIndex]; + core_fetch_en_o [i] = sys_fetch_en_i [SysCoreIndex]; + core_boot_addr_o [i] = sys_boot_addr_i [SysCoreIndex]; + + if (RapidRecovery) begin + core_debug_req_o [i] = sys_debug_req_i [SysCoreIndex] + | recovery_debug_req_out [dmr_shared_id(dmr_group_id(i))]; + end else begin + core_debug_req_o [i] = sys_debug_req_i [SysCoreIndex]; + end + core_perf_counters_o[i] = sys_perf_counters_i [SysCoreIndex]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [SysCoreIndex]; + core_irq_id_o [i] = sys_irq_id_i [SysCoreIndex]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [SysCoreIndex]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i [SysCoreIndex]; + core_instr_r_valid_o[i] = filt_instr_r_valid [SysCoreIndex]; + core_instr_err_o [i] = sys_instr_err_i [SysCoreIndex]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [SysCoreIndex]; + core_data_r_opc_o [i] = sys_data_r_opc_i [SysCoreIndex]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [SysCoreIndex]; + core_data_r_user_o [i] = sys_data_r_user_i [SysCoreIndex]; + core_data_r_valid_o [i] = filt_data_r_valid [SysCoreIndex]; + core_data_err_o [i] = sys_data_err_i [SysCoreIndex]; + end else begin : gen_independent_mode + // CTRL + core_core_id_o [i] = sys_core_id_i [i]; + core_cluster_id_o [i] = sys_cluster_id_i [i]; + + core_clock_en_o [i] = sys_clock_en_i [i]; + core_fetch_en_o [i] = sys_fetch_en_i [i]; + core_boot_addr_o [i] = sys_boot_addr_i [i]; + + core_debug_req_o [i] = sys_debug_req_i [i]; + core_perf_counters_o[i] = sys_perf_counters_i[i]; + + // IRQ + core_irq_req_o [i] = sys_irq_req_i [i]; + core_irq_id_o [i] = sys_irq_id_i [i]; + + // INSTR + core_instr_gnt_o [i] = filt_instr_gnt [i]; + core_instr_r_rdata_o[i] = sys_instr_r_rdata_i[i]; + core_instr_r_valid_o[i] = filt_instr_r_valid[i]; + core_instr_err_o [i] = sys_instr_err_i [i]; + + // DATA + core_data_gnt_o [i] = filt_data_gnt [i]; + core_data_r_opc_o [i] = sys_data_r_opc_i [i]; + core_data_r_rdata_o [i] = sys_data_r_rdata_i [i]; + core_data_r_user_o [i] = sys_data_r_user_i [i]; + core_data_r_valid_o [i] = filt_data_r_valid [i]; + core_data_err_o [i] = sys_data_err_i [i]; + end + end + end // gen_core_inputs + + for (genvar i = 0; i < NumSysCores; i++) begin : gen_core_outputs + localparam CoreCoreIndex = DMRFixed ? i : dmr_group_id(i); + if (DMRFixed && i < NumDMRGroups) begin : fixed_dmr + // CTRL + assign sys_core_busy_o [i] = dmr_core_busy_out[CoreCoreIndex]; + + // IRQ + assign sys_irq_ack_o [i] = dmr_irq_ack_out [CoreCoreIndex]; + assign sys_irq_ack_id_o [i] = dmr_irq_ack_id_out[CoreCoreIndex]; + + // INSTR + assign filt_instr_req [i] = dmr_instr_req_out [CoreCoreIndex]; + assign filt_instr_addr [i] = dmr_instr_addr_out[CoreCoreIndex]; + + // DATA + assign filt_data_req [i] = dmr_data_req_out [CoreCoreIndex]; + assign filt_data_addr [i] = dmr_data_add_out [CoreCoreIndex]; + assign filt_data_we [i] = dmr_data_wen_out [CoreCoreIndex]; + assign filt_data_data [i] = dmr_data_wdata_out[CoreCoreIndex]; + assign sys_data_user_o [i] = dmr_data_user_out [CoreCoreIndex]; + assign filt_data_be [i] = dmr_data_be_out [CoreCoreIndex]; + end else begin + if (i >= NumDMRCores) begin : independent_stragglers + // CTRL + assign sys_core_busy_o [i] = dmr_core_busy_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // IRQ + assign sys_irq_ack_o [i] = dmr_irq_ack_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_irq_ack_id_o [i] = dmr_irq_ack_id_out[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // INSTR + assign filt_instr_req [i] = dmr_instr_req_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_instr_addr [i] = dmr_instr_addr_out[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + + // DATA + assign filt_data_req [i] = dmr_data_req_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_addr [i] = dmr_data_add_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_we [i] = dmr_data_wen_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_data [i] = dmr_data_wdata_out[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_data_user_o [i] = dmr_data_user_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign filt_data_be [i] = dmr_data_be_out [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + end else begin + always_comb begin + if (core_in_dmr[i]) begin : dmr_mode + if (dmr_core_id(dmr_group_id(i), 0) == i) begin : is_dmr_main_core + // CTRL + sys_core_busy_o [i] = dmr_core_busy_out[CoreCoreIndex]; + + // IRQ + sys_irq_ack_o [i] = dmr_irq_ack_out [CoreCoreIndex]; + sys_irq_ack_id_o [i] = dmr_irq_ack_id_out[CoreCoreIndex]; + + // INSTR + filt_instr_req [i] = dmr_instr_req_out [CoreCoreIndex]; + filt_instr_addr [i] = dmr_instr_addr_out[CoreCoreIndex]; + + // DATA + filt_data_req [i] = dmr_data_req_out [CoreCoreIndex]; + filt_data_addr [i] = dmr_data_add_out [CoreCoreIndex]; + filt_data_we [i] = dmr_data_wen_out [CoreCoreIndex]; + filt_data_data [i] = dmr_data_wdata_out[CoreCoreIndex]; + sys_data_user_o [i] = dmr_data_user_out [CoreCoreIndex]; + filt_data_be [i] = dmr_data_be_out [CoreCoreIndex]; + end else begin : disable_core // Assign disable + // CTLR + sys_core_busy_o [i] = '0; + + // IRQ + sys_irq_ack_o [i] = '0; + sys_irq_ack_id_o [i] = '0; + + // INSTR + filt_instr_req [i] = '0; + filt_instr_addr [i] = '0; + + // DATA + filt_data_req [i] = '0; + filt_data_addr [i] = '0; + filt_data_we [i] = '0; + filt_data_data [i] = '0; + sys_data_user_o [i] = '0; + filt_data_be [i] = '0; + end + end else begin : independent_mode + // CTRL + sys_core_busy_o [i] = core_core_busy_i [i]; + + // IRQ + sys_irq_ack_o [i] = core_irq_ack_i [i]; + sys_irq_ack_id_o [i] = core_irq_ack_id_i[i]; + + // INSTR + filt_instr_req [i] = core_instr_req_i [i]; + filt_instr_addr [i] = core_instr_addr_i[i]; + + // DATA + filt_data_req [i] = core_data_req_i [i]; + filt_data_addr [i] = core_data_add_i [i]; + filt_data_we [i] = core_data_wen_i [i]; + filt_data_data [i] = core_data_wdata_i[i]; + sys_data_user_o [i] = core_data_user_i [i]; + filt_data_be [i] = core_data_be_i [i]; + end + end + end + end + end + + end else begin : gen_no_redundancy + /***************** + *** none *** + *****************/ + // Direct assignment, disable all + assign core_setback_o = '0; + + // CTRL + assign core_core_id_o = sys_core_id_i; + assign core_cluster_id_o = sys_cluster_id_i; + + assign core_clock_en_o = sys_clock_en_i; + assign core_fetch_en_o = sys_fetch_en_i; + assign core_boot_addr_o = sys_boot_addr_i; + assign sys_core_busy_o = core_core_busy_i; + + assign core_debug_req_o = sys_debug_req_i; + assign core_perf_counters_o = sys_perf_counters_i; + + // IRQ + assign core_irq_req_o = sys_irq_req_i; + assign sys_irq_ack_o = core_irq_ack_i; + assign core_irq_id_o = sys_irq_id_i; + assign sys_irq_ack_id_o = core_irq_ack_id_i; + + // INSTR + assign filt_instr_req = core_instr_req_i; + assign core_instr_gnt_o = filt_instr_gnt; + assign filt_instr_addr = core_instr_addr_i; + assign core_instr_r_rdata_o = sys_instr_r_rdata_i; + assign core_instr_r_valid_o = filt_instr_r_valid; + assign core_instr_err_o = sys_instr_err_i; + + // DATA + assign filt_data_req = core_data_req_i; + assign filt_data_addr = core_data_add_i; + assign filt_data_we = core_data_wen_i; + assign filt_data_data = core_data_wdata_i; + assign sys_data_user_o = core_data_user_i; + assign filt_data_be = core_data_be_i; + assign core_data_gnt_o = filt_data_gnt; + assign core_data_r_opc_o = sys_data_r_opc_i; + assign core_data_r_rdata_o = sys_data_r_rdata_i; + assign core_data_r_user_o = sys_data_r_user_i; + assign core_data_r_valid_o = filt_data_r_valid; + assign core_data_err_o = sys_data_err_i; + end + +endmodule diff --git a/rtl/HMR/doc.html b/rtl/HMR/doc.html new file mode 100644 index 00000000..ab3898ef --- /dev/null +++ b/rtl/HMR/doc.html @@ -0,0 +1,99 @@ + + + + + +
+
HMR_registers.avail_config @ 0x0
+

Available Configurations from implemented hardware.

+
Reset default = 0x0, mask 0x107
+
+ + + + + + + +
31302928272625242322212019181716
 
1514131211109876543210
 rapid_recovery tripledualindependent
BitsTypeResetNameDescription
0roxindependent

Independent mode is available.

1roxdual

Dual Modular Redundancy (DMR) is available.

2roxtriple

Triple Modular Redundancy (TMR) is available.

7:3Reserved
8roxrapid_recovery

Rapid Recovery is available.

+
+ + + + + +
+
HMR_registers.cores_en @ 0x4
+

Enabled cores, based on the configuration. Can be used for barriers.

+
Reset default = 0x0, mask 0xfff
+
+ + + +
31302928272625242322212019181716
 
1514131211109876543210
 cores_en
BitsTypeResetNameDescription
11:0roxcores_en

Enabled cores.

+
+ + + + + +
+
HMR_registers.DMR_enable @ 0x8
+

DMR configuration enable, on bit per DMR group.

+
Reset default = 0x0, mask 0x3f
+
+ + + +
31302928272625242322212019181716
 
1514131211109876543210
 DMR_enable
BitsTypeResetNameDescription
5:0rw0x0DMR_enable

DMR configuration enable.

+
+ + + + + +
+
HMR_registers.TMR_enable @ 0xc
+

TMR configuration enable, one bit per TMR group.

+
Reset default = 0x0, mask 0xf
+
+ + + +
31302928272625242322212019181716
 
1514131211109876543210
 TMR_enable
BitsTypeResetNameDescription
3:0rw0x0TMR_enable

TMR configuration enable.

+
+ + + + + +
+
HMR_registers.DMR_config @ 0x10
+

DMR configuration bits.

+
Reset default = 0x0, mask 0x3
+
+ + + + +
31302928272625242322212019181716
 
1514131211109876543210
 force_recoveryrapid_recovery
BitsTypeResetNameDescription
0rwxrapid_recovery

Enable rapid recovery using an additional register file.

1rwxforce_recovery

Forces recovery routine (if rapid_recovery is available).

+
+ + + + + +
+
HMR_registers.TMR_config @ 0x14
+

TMR configuration bits.

+
Reset default = 0x6, mask 0x1f
+
+ + + + + + + +
31302928272625242322212019181716
 
1514131211109876543210
 force_resynchrapid_recoveryreload_setbacksetbackdelay_resynch
BitsTypeResetNameDescription
0rw0x0delay_resynch

Enable wait-for-restoration

1rw0x1setback

Enable setback (synchronous reset) during re-synch.

2rw0x1reload_setback

Enable setback on mismatch during reload section of re-synch (only possible with setback)

3rwxrapid_recovery

Enable rapid recovery using additional register file.

4rw0x0force_resynch

Forces a resynchronization routine

+
diff --git a/rtl/HMR/doc.md b/rtl/HMR/doc.md new file mode 100644 index 00000000..892ab665 --- /dev/null +++ b/rtl/HMR/doc.md @@ -0,0 +1,312 @@ + + + + +The tables describe each key and the type of the value. The following +types are used: + +Type | Description +---- | ----------- +int | integer (binary 0b, octal 0o, decimal, hex 0x) +xint | x for undefined otherwise int +bitrange | bit number as decimal integer, or bit-range as decimal integers msb:lsb +list | comma separated list enclosed in `[]` +name list | comma separated list enclosed in `[]` of one or more groups that have just name and dscr keys. e.g. `{ name: "name", desc: "description"}` +name list+ | name list that optionally contains a width +parameter list | parameter list having default value optionally +group | comma separated group of key:value enclosed in `{}` +list of group | comma separated group of key:value enclosed in `{}` the second entry of the list is the sub group format +string | string, typically short +text | string, may be multi-line enclosed in `'''` may use `**bold**`, `*italic*` or `!!Reg` markup +tuple | tuple enclosed in () +python int | Native Python type int (generated) +python Bool | Native Python type Bool (generated) +python list | Native Python type list (generated) +python enum | Native Python type enum (generated) + + +Register fields are tagged using the swaccess key to describe the +permitted access and side-effects. This key must have one of these +values: + + +Key | Description +--- | ----------- +none | No access +ro | Read Only +rc | Read Only, reading clears +rw | Read/Write +r0w1c | Read zero, Write with 1 clears +rw1s | Read, Write with 1 sets +rw1c | Read, Write with 1 clears +rw0c | Read, Write with 0 clears +wo | Write Only + + +Register fields are tagged using the hwaccess key to describe the +permitted access from hardware logic and side-effects. This key must +have one of these values: + + +Key | Description +--- | ----------- +hro | Read Only +hrw | Read/Write +hwo | Write Only +none | No Access Needed + + +The top level of the JSON is a group containing the following keys: + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +name | required | string | name of the component +clock_primary | required | string | name of the primary clock +bus_interfaces | required | list | bus interfaces for the device +registers | required | list | list of register definition groups and offset control groups +alert_list | optional | name list+ | list of peripheral alerts +available_inout_list | optional | name list+ | list of available peripheral inouts +available_input_list | optional | name list+ | list of available peripheral inputs +available_output_list | optional | name list+ | list of available peripheral outputs +hier_path | additional hierarchy path before the reg block instance +interrupt_list | optional | name list+ | list of peripheral interrupts +inter_signal_list | optional | list | list of inter-module signals +no_auto_alert_regs | optional | string | Set to true to suppress automatic generation of alert test registers. Defaults to true if no alert_list is present. Otherwise this defaults to false. +no_auto_intr_regs | optional | string | Set to true to suppress automatic generation of interrupt registers. Defaults to true if no interrupt_list is present. Otherwise this defaults to false. +other_clock_list | optional | list | list of other chip clocks needed +other_reset_list | optional | list | list of other resets +param_list | optional | parameter list | list of parameters of the IP +regwidth | optional | int | width of registers in bits (default 32) +reset_primary | optional | string | primary reset used by the module +reset_request_list | optional | list | list of signals requesting reset +scan | optional | python Bool | Indicates the module have `scanmode_i` +scan_reset | optional | python Bool | Indicates the module have `scan_rst_ni` +scan_en | optional | python Bool | Indicates the module has `scan_en_i` +SPDX-License-Identifier | optional | string | License ientifier (if using pure json) Only use this if unable to put this information in a comment at the top of the file. +wakeup_list | optional | name list+ | list of peripheral wakeups + +The basic structure of a register definition file is thus: + +```hjson +{ + name: "GP", + regwidth: "32", + registers: [ + // register definitions... + ] +} + +``` + + + +The list of registers includes register definition groups containing the following keys: + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +name | required | string | name of the register +desc | required | text | description of the register +fields | required | list | list of register field description groups +swaccess | optional | string | software access permission to use for fields that don't specify swaccess +hwaccess | optional | string | hardware access permission to use for fields that don't specify hwaccess +hwext | optional | string | 'true' if the register is stored outside of the register module +hwqe | optional | string | 'true' if hardware uses 'q' enable signal, which is latched signal of software write pulse. +hwre | optional | string | 'true' if hardware uses 're' signal, which is latched signal of software read pulse. +regwen | optional | string | if register is write-protected by another register, that register name should be given here. empty-string for no register write protection +resval | optional | int | reset value of full register (default 0) +tags | optional | string | tags for the register, following the format 'tag_name:item1:item2...' +shadowed | optional | string | 'true' if the register is shadowed +update_err_alert | optional | string | alert that will be triggered if this shadowed register has update error +storage_err_alert | optional | string | alert that will be triggered if this shadowed register has storage error + + +The basic register definition group will follow this pattern: + +```hjson + { name: "REGA", + desc: "Description of register", + swaccess: "rw", + resval: "42", + fields: [ + // bit field definitions... + ] + } +``` + +The name and brief description are required. If the swaccess key is +provided it describes the access pattern that will be used by all +bitfields in the register that do not override with their own swaccess +key. This is a useful shortcut because in most cases a register will +have the same access restrictions for all fields. The reset value of +the register may also be provided here or in the individual fields. If +it is provided in both places then they must match, if it is provided +in neither place then the reset value defaults to zero for all except +write-only fields when it defaults to x. + + + +In the fields list each field definition is a group itself containing the following keys: + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +bits | required | bitrange | bit or bit range (msb:lsb) +name | optional | string | name of the field +desc | optional | text | description of field (required if the field has a name) +swaccess | optional | string | software access permission, copied from register if not provided in field. (Tool adds if not provided.) +hwaccess | optional | string | hardware access permission, copied from register if not prvided in field. (Tool adds if not provided.) +resval | optional | xint | reset value, comes from register resval if not provided in field. Zero if neither are provided and the field is readable, x if neither are provided and the field is wo. Must match if both are provided. +enum | optional | list | list of permitted enumeration groups +tags | optional | string | tags for the field, followed by the format 'tag_name:item1:item2...' + + +Field names should be relatively short because they will be used +frequently (and need to fit in the register layout picture!) The field +description is expected to be longer and will most likely make use of +the Hjson ability to include multi-line strings. An example with three +fields: + +```hjson + fields: [ + { bits: "15:0", + name: "RXS", + desc: ''' + Last 16 oversampled values of RX. These are captured at 16x the baud + rate clock. This is a shift register with the most recent bit in + bit 0 and the oldest in bit 15. Only valid when ENRXS is set. + ''' + } + { bits: "16", + name: "ENRXS", + desc: ''' + If this bit is set the receive oversampled data is collected + in the RXS field. + ''' + } + {bits: "20:19", name: "TXILVL", + desc: "Trigger level for TX interrupts", + resval: "2", + enum: [ + { value: "0", name: "txlvl1", desc: "1 character" }, + { value: "1", name: "txlvl4", desc: "4 characters" }, + { value: "2", name: "txlvl8", desc: "8 characters" }, + { value: "3", name: "txlvl16", desc: "16 characters" } + ] + } + ] +``` + +In all of these the swaccess parameter is inherited from the register +level, and will be added so this key is always available to the +backend. The RXS and ENRXS will default to zero reset value (unless +something different is provided for the register) and will have the +key added, but TXILVL expicitly sets its reset value as 2. + +The missing bits 17 and 18 will be treated as reserved by the tool, as +will any bits between 21 and the maximum in the register. + +The TXILVL is an example using an enumeration to specify all valid +values for the field. In this case all possible values are described, +if the list is incomplete then the field is marked with the rsvdenum +key so the backend can take appropriate action. (If the enum field is +more than 7 bits then the checking is not done.) + + + +Definitions in an enumeration group contain: + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +name | required | string | name of the member of the enum +desc | required | text | description when field has this value +value | required | int | value of this member of the enum + + +The list of registers may include single entry groups to control the offset, open a window or generate registers: + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +reserved | optional | int | number of registers to reserve space for +skipto | optional | int | set next register offset to value +window | optional | group | group defining an address range for something other than standard registers +multireg | optional | group | group defining registers generated from a base instance. + + + + +Registers can protect themselves from software writes by using the +register attribute regwen. When not an emptry string (the default +value), regwen indicates that another register must be true in order +to allow writes to this register. This is useful for the prevention +of software modification. The register-enable register (call it +REGWEN) must be one bit in width, and should default to 1 and be rw1c +for preferred security control. This allows all writes to proceed +until at some point software disables future modifications by clearing +REGWEN. An error is reported if REGWEN does not exist, contains more +than one bit, is not `rw1c` or does not default to 1. One REGWEN can +protect multiple registers. The REGWEN register must precede those +registers that refer to it in the .hjson register list. An example: + +```hjson + { name: "REGWEN", + desc: "Register write enable for a bank of registers", + swaccess: "rw1c", + fields: [ { bits: "0", resval: "1" } ] + } + { name: "REGA", + swaccess: "rw", + regwen: "REGWEN", + ... + } + { name: "REGB", + swaccess: "rw", + regwen: "REGWEN", + ... + } +``` + + +A window defines an open region of the register space that can be used +for things that are not registers (for example access to a buffer ram). + + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +name | required | string | name of the window +desc | required | text | description of the window +items | required | int | size in fieldaccess width words of the window +swaccess | required | string | software access permitted +data-intg-passthru | optional | string | True if the window has data integrity pass through. Defaults to false if not present. +byte-write | optional | string | True if byte writes are supported. Defaults to false if not present. +validbits | optional | int | Number of valid data bits within regwidth sized word. Defaults to regwidth. If smaller than the regwidth then in each word of the window bits [regwidth-1:validbits] are unused and bits [validbits-1:0] are valid. +unusual | optional | string | True if window has unusual parameters (set to prevent Unusual: errors).Defaults to false if not present. + + +The multireg expands on the register required fields and will generate +a list of the generated registers (that contain all required and +generated keys for an actual register). + + +Key | Kind | Type | Description of Value +--- | ---- | ---- | -------------------- +name | required | string | base name of the registers +desc | required | text | description of the registers +count | required | string | number of instances to generate. This field can be integer or string matching from param_list. +cname | required | string | base name for each instance, mostly useful for referring to instance in messages. +fields | required | list | list of register field description groups. Describes bit positions used for base instance. +swaccess | optional | string | software access permission to use for fields that don't specify swaccess +hwaccess | optional | string | hardware access permission to use for fields that don't specify hwaccess +hwext | optional | string | 'true' if the register is stored outside of the register module +hwqe | optional | string | 'true' if hardware uses 'q' enable signal, which is latched signal of software write pulse. +hwre | optional | string | 'true' if hardware uses 're' signal, which is latched signal of software read pulse. +regwen | optional | string | if register is write-protected by another register, that register name should be given here. empty-string for no register write protection +resval | optional | int | reset value of full register (default 0) +tags | optional | string | tags for the register, following the format 'tag_name:item1:item2...' +shadowed | optional | string | 'true' if the register is shadowed +update_err_alert | optional | string | alert that will be triggered if this shadowed register has update error +storage_err_alert | optional | string | alert that will be triggered if this shadowed register has storage error +regwen_multi | optional | python Bool | If true, regwen term increments along with current multireg count. +compact | optional | python Bool | If true, allow multireg compacting.If false, do not compact. + + +(end of output generated by `regtool.py --doc`) + diff --git a/rtl/HMR/hmr_core_regs_reg_pkg.sv b/rtl/HMR/hmr_core_regs_reg_pkg.sv new file mode 100644 index 00000000..602bd91a --- /dev/null +++ b/rtl/HMR/hmr_core_regs_reg_pkg.sv @@ -0,0 +1,80 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package hmr_core_regs_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + logic [31:0] q; + } hmr_core_regs_reg2hw_mismatches_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } hmr_core_regs_reg2hw_sp_store_reg_t; + + typedef struct packed { + struct packed { + logic d; + } independent; + struct packed { + logic d; + } dual; + struct packed { + logic d; + } triple; + } hmr_core_regs_hw2reg_current_mode_reg_t; + + typedef struct packed { + logic [31:0] d; + logic de; + } hmr_core_regs_hw2reg_mismatches_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_core_regs_reg2hw_mismatches_reg_t mismatches; // [64:33] + hmr_core_regs_reg2hw_sp_store_reg_t sp_store; // [32:0] + } hmr_core_regs_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_core_regs_hw2reg_current_mode_reg_t current_mode; // [35:33] + hmr_core_regs_hw2reg_mismatches_reg_t mismatches; // [32:0] + } hmr_core_regs_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] HMR_CORE_REGS_CURRENT_MODE_OFFSET = 4'h 0; + parameter logic [BlockAw-1:0] HMR_CORE_REGS_MISMATCHES_OFFSET = 4'h 4; + parameter logic [BlockAw-1:0] HMR_CORE_REGS_SP_STORE_OFFSET = 4'h 8; + + // Reset values for hwext registers and their fields + parameter logic [2:0] HMR_CORE_REGS_CURRENT_MODE_RESVAL = 3'h 1; + parameter logic [0:0] HMR_CORE_REGS_CURRENT_MODE_INDEPENDENT_RESVAL = 1'h 1; + parameter logic [0:0] HMR_CORE_REGS_CURRENT_MODE_DUAL_RESVAL = 1'h 0; + parameter logic [0:0] HMR_CORE_REGS_CURRENT_MODE_TRIPLE_RESVAL = 1'h 0; + + // Register index + typedef enum int { + HMR_CORE_REGS_CURRENT_MODE, + HMR_CORE_REGS_MISMATCHES, + HMR_CORE_REGS_SP_STORE + } hmr_core_regs_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] HMR_CORE_REGS_PERMIT [3] = '{ + 4'b 0001, // index[0] HMR_CORE_REGS_CURRENT_MODE + 4'b 1111, // index[1] HMR_CORE_REGS_MISMATCHES + 4'b 1111 // index[2] HMR_CORE_REGS_SP_STORE + }; + +endpackage + diff --git a/rtl/HMR/hmr_core_regs_reg_top.sv b/rtl/HMR/hmr_core_regs_reg_top.sv new file mode 100644 index 00000000..d9cf8ceb --- /dev/null +++ b/rtl/HMR/hmr_core_regs_reg_top.sv @@ -0,0 +1,306 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module hmr_core_regs_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 4 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output hmr_core_regs_reg_pkg::hmr_core_regs_reg2hw_t reg2hw, // Write + input hmr_core_regs_reg_pkg::hmr_core_regs_hw2reg_t hw2reg, // Read + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import hmr_core_regs_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic current_mode_independent_qs; + logic current_mode_independent_re; + logic current_mode_dual_qs; + logic current_mode_dual_re; + logic current_mode_triple_qs; + logic current_mode_triple_re; + logic [31:0] mismatches_qs; + logic [31:0] mismatches_wd; + logic mismatches_we; + logic [31:0] sp_store_qs; + logic [31:0] sp_store_wd; + logic sp_store_we; + + // Register instances + // R[current_mode]: V(True) + + // F[independent]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_current_mode_independent ( + .re (current_mode_independent_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.current_mode.independent.d), + .qre (), + .qe (), + .q (), + .qs (current_mode_independent_qs) + ); + + + // F[dual]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_current_mode_dual ( + .re (current_mode_dual_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.current_mode.dual.d), + .qre (), + .qe (), + .q (), + .qs (current_mode_dual_qs) + ); + + + // F[triple]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_current_mode_triple ( + .re (current_mode_triple_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.current_mode.triple.d), + .qre (), + .qe (), + .q (), + .qs (current_mode_triple_qs) + ); + + + // R[mismatches]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("W0C"), + .RESVAL (32'h0) + ) u_mismatches ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (mismatches_we), + .wd (mismatches_wd), + + // from internal hardware + .de (hw2reg.mismatches.de), + .d (hw2reg.mismatches.d ), + + // to internal hardware + .qe (), + .q (reg2hw.mismatches.q ), + + // to register interface (read) + .qs (mismatches_qs) + ); + + + // R[sp_store]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_sp_store ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (sp_store_we), + .wd (sp_store_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.sp_store.qe), + .q (reg2hw.sp_store.q ), + + // to register interface (read) + .qs (sp_store_qs) + ); + + + + + logic [2:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == HMR_CORE_REGS_CURRENT_MODE_OFFSET); + addr_hit[1] = (reg_addr == HMR_CORE_REGS_MISMATCHES_OFFSET); + addr_hit[2] = (reg_addr == HMR_CORE_REGS_SP_STORE_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(HMR_CORE_REGS_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(HMR_CORE_REGS_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(HMR_CORE_REGS_PERMIT[2] & ~reg_be))))); + end + + assign current_mode_independent_re = addr_hit[0] & reg_re & !reg_error; + + assign current_mode_dual_re = addr_hit[0] & reg_re & !reg_error; + + assign current_mode_triple_re = addr_hit[0] & reg_re & !reg_error; + + assign mismatches_we = addr_hit[1] & reg_we & !reg_error; + assign mismatches_wd = reg_wdata[31:0]; + + assign sp_store_we = addr_hit[2] & reg_we & !reg_error; + assign sp_store_wd = reg_wdata[31:0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = current_mode_independent_qs; + reg_rdata_next[1] = current_mode_dual_qs; + reg_rdata_next[2] = current_mode_triple_qs; + end + + addr_hit[1]: begin + reg_rdata_next[31:0] = mismatches_qs; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = sp_store_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module hmr_core_regs_reg_top_intf +#( + parameter int AW = 4, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output hmr_core_regs_reg_pkg::hmr_core_regs_reg2hw_t reg2hw, // Write + input hmr_core_regs_reg_pkg::hmr_core_regs_hw2reg_t hw2reg, // Read + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + hmr_core_regs_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .hw2reg, // Read + .devmode_i + ); + +endmodule + + diff --git a/rtl/HMR/hmr_dmr_ctrl.sv b/rtl/HMR/hmr_dmr_ctrl.sv new file mode 100644 index 00000000..35b638ba --- /dev/null +++ b/rtl/HMR/hmr_dmr_ctrl.sv @@ -0,0 +1,189 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Hybrid modular redundancy DMR control unit + +module hmr_dmr_ctrl + import rapid_recovery_pkg::*; +#( + parameter bit InterleaveGrps = 1'b0, + parameter int unsigned DataWidth = 32, + parameter bit DMRFixed = 1'b0, + parameter bit DefaultInDMR = DMRFixed ? 1'b1 : 1'b0, + parameter bit RapidRecovery = 1'b0, + parameter type reg_req_t = logic, + parameter type reg_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // input logic test_enable_i, + + // Register interface + input reg_req_t reg_req_i, + output reg_resp_t reg_resp_o, + + // CTRL from external (e.g. HMR ctrl regs) + input logic dmr_enable_q_i, + input logic dmr_enable_qe_i, + input logic rapid_recovery_q_i, + input logic rapid_recovery_qe_i, + input logic force_recovery_q_i, + input logic force_recovery_qe_i, + + // DMR control signals + output logic [1:0] setback_o, + output logic sw_resynch_req_o, + output logic sw_synch_req_o, + output logic [DataWidth-1:0] checkpoint_o, + output logic grp_in_independent_o, + output logic rapid_recovery_en_o, + output logic [1:0] dmr_incr_mismatches_o, + input logic dmr_error_i, + output logic recovery_request_o, + input logic recovery_finished_i, + + input logic fetch_en_i, + input logic cores_synch_i +); + + logic synch_req, synch_req_sent_d, synch_req_sent_q; + logic resynch_req, resynch_req_sent_d, resynch_req_sent_q; + logic cores_synch_q; + + typedef enum logic [2:0] {NON_DMR, DMR_RUN, DMR_RESTORE} dmr_mode_e; + localparam dmr_mode_e DefaultDMRMode = DefaultInDMR || DMRFixed ? DMR_RUN : NON_DMR; + + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_reg2hw_t dmr_reg2hw; + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_hw2reg_t dmr_hw2reg; + + dmr_mode_e dmr_red_mode_d, dmr_red_mode_q; + + assign grp_in_independent_o = dmr_red_mode_q == NON_DMR; + assign rapid_recovery_en_o = dmr_reg2hw.dmr_config.rapid_recovery.q && RapidRecovery; + + assign sw_synch_req_o = synch_req & ~synch_req_sent_q; + assign synch_req_sent_d = synch_req; + assign sw_resynch_req_o = resynch_req & ~resynch_req_sent_q; + assign resynch_req_sent_d = resynch_req; + assign checkpoint_o = dmr_reg2hw.checkpoint_addr.q; + + hmr_dmr_regs_reg_top #( + .reg_req_t(reg_req_t), + .reg_rsp_t(reg_resp_t) + ) i_dmr_regs ( + .clk_i, + .rst_ni, + .reg_req_i(reg_req_i), + .reg_rsp_o(reg_resp_o), + .reg2hw (dmr_reg2hw), + .hw2reg (dmr_hw2reg), + .devmode_i('0) + ); + + // Global config update + assign dmr_hw2reg.dmr_enable.de = dmr_enable_qe_i; + assign dmr_hw2reg.dmr_enable.d = dmr_enable_q_i; + assign dmr_hw2reg.dmr_config.rapid_recovery.de = rapid_recovery_qe_i || ~RapidRecovery; + assign dmr_hw2reg.dmr_config.rapid_recovery.d = rapid_recovery_q_i && RapidRecovery; + assign dmr_hw2reg.dmr_config.force_recovery.d = force_recovery_qe_i ? force_recovery_q_i : 1'b0; + + /************************** + * FSM for DMR lockstep * + **************************/ + + always_comb begin : proc_fsm + setback_o = 2'b00; + dmr_red_mode_d = dmr_red_mode_q; + dmr_incr_mismatches_o = '0; + recovery_request_o = 1'b0; + resynch_req = 1'b0; + synch_req = 1'b0; + + dmr_hw2reg.dmr_config.force_recovery.de = force_recovery_qe_i; + + case (dmr_red_mode_q) + DMR_RUN: begin + // If forced execute recovery + if (dmr_reg2hw.dmr_config.force_recovery.q && RapidRecovery && dmr_reg2hw.dmr_config.rapid_recovery.q) begin + dmr_hw2reg.dmr_config.force_recovery.de = 1'b1; + dmr_red_mode_d = DMR_RESTORE; + end + + // If error detected, restore + if (dmr_error_i && RapidRecovery && dmr_reg2hw.dmr_config.rapid_recovery.q) begin + $display("[HMR-dual] %t - mismatch detected, rapid recovery starting", $realtime); + dmr_red_mode_d = DMR_RESTORE; + end + + if (dmr_error_i && (!RapidRecovery || !dmr_reg2hw.dmr_config.rapid_recovery.q)) begin + $display("[HMR-dual] %t - mismatch detected, SW trigger", $realtime); + resynch_req = 1'b1; + end + end + + DMR_RESTORE: begin + recovery_request_o = 1'b1; + if (recovery_finished_i) begin + $display("[HMR-dual] %t - mismatch restored", $realtime); + dmr_red_mode_d = DMR_RUN; + end + end + + // Default: do nothing + endcase + + // Logic to switch in and out of DMR + if (!DMRFixed) begin + // Set DMR mode on external signal that cores are synchronized + if (dmr_red_mode_q == NON_DMR && dmr_reg2hw.dmr_enable.q == 1'b1) begin + synch_req = 1'b1; + if (cores_synch_q == 1'b1) begin + if (dmr_reg2hw.dmr_config.rapid_recovery.q == 1'b1) begin + dmr_red_mode_d = DMR_RESTORE; + end else begin + dmr_red_mode_d = DMR_RUN; + setback_o = 2'b11; + end + end + end + // Before core startup: set DMR mode from reg2hw.dmr_enable + if (fetch_en_i == 0) begin + if (dmr_reg2hw.dmr_enable.q == 1'b0) begin + dmr_red_mode_d = NON_DMR; + end else begin + synch_req = 1'b0; + dmr_red_mode_d = DMR_RUN; + end + end + // split tolerant mode to performance mode anytime (but require correct core state) + if (dmr_red_mode_q == DMR_RUN) begin + if (dmr_reg2hw.dmr_enable.q == 1'b0) begin + dmr_red_mode_d = NON_DMR; + setback_o = 2'b10; + end + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_red_mode + if(!rst_ni) begin + dmr_red_mode_q <= DefaultDMRMode; + synch_req_sent_q <= '0; + resynch_req_sent_q <= '0; + cores_synch_q <= '0; + end else begin + dmr_red_mode_q <= dmr_red_mode_d; + synch_req_sent_q <= synch_req_sent_d; + resynch_req_sent_q <= resynch_req_sent_d; + cores_synch_q <= cores_synch_i; + end + end + +endmodule diff --git a/rtl/HMR/hmr_dmr_regs_reg_pkg.sv b/rtl/HMR/hmr_dmr_regs_reg_pkg.sv new file mode 100644 index 00000000..95199d4c --- /dev/null +++ b/rtl/HMR/hmr_dmr_regs_reg_pkg.sv @@ -0,0 +1,92 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package hmr_dmr_regs_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + logic q; + logic qe; + } hmr_dmr_regs_reg2hw_dmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } rapid_recovery; + struct packed { + logic q; + logic qe; + } force_recovery; + } hmr_dmr_regs_reg2hw_dmr_config_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } hmr_dmr_regs_reg2hw_checkpoint_addr_reg_t; + + typedef struct packed { + logic d; + logic de; + } hmr_dmr_regs_hw2reg_dmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } rapid_recovery; + struct packed { + logic d; + logic de; + } force_recovery; + } hmr_dmr_regs_hw2reg_dmr_config_reg_t; + + typedef struct packed { + logic [31:0] d; + logic de; + } hmr_dmr_regs_hw2reg_checkpoint_addr_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_dmr_regs_reg2hw_dmr_enable_reg_t dmr_enable; // [38:37] + hmr_dmr_regs_reg2hw_dmr_config_reg_t dmr_config; // [36:33] + hmr_dmr_regs_reg2hw_checkpoint_addr_reg_t checkpoint_addr; // [32:0] + } hmr_dmr_regs_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_dmr_regs_hw2reg_dmr_enable_reg_t dmr_enable; // [38:37] + hmr_dmr_regs_hw2reg_dmr_config_reg_t dmr_config; // [36:33] + hmr_dmr_regs_hw2reg_checkpoint_addr_reg_t checkpoint_addr; // [32:0] + } hmr_dmr_regs_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] HMR_DMR_REGS_DMR_ENABLE_OFFSET = 4'h 0; + parameter logic [BlockAw-1:0] HMR_DMR_REGS_DMR_CONFIG_OFFSET = 4'h 4; + parameter logic [BlockAw-1:0] HMR_DMR_REGS_CHECKPOINT_ADDR_OFFSET = 4'h 8; + + // Register index + typedef enum int { + HMR_DMR_REGS_DMR_ENABLE, + HMR_DMR_REGS_DMR_CONFIG, + HMR_DMR_REGS_CHECKPOINT_ADDR + } hmr_dmr_regs_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] HMR_DMR_REGS_PERMIT [3] = '{ + 4'b 0001, // index[0] HMR_DMR_REGS_DMR_ENABLE + 4'b 0001, // index[1] HMR_DMR_REGS_DMR_CONFIG + 4'b 1111 // index[2] HMR_DMR_REGS_CHECKPOINT_ADDR + }; + +endpackage + diff --git a/rtl/HMR/hmr_dmr_regs_reg_top.sv b/rtl/HMR/hmr_dmr_regs_reg_top.sv new file mode 100644 index 00000000..209d9348 --- /dev/null +++ b/rtl/HMR/hmr_dmr_regs_reg_top.sv @@ -0,0 +1,312 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module hmr_dmr_regs_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 4 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output hmr_dmr_regs_reg_pkg::hmr_dmr_regs_reg2hw_t reg2hw, // Write + input hmr_dmr_regs_reg_pkg::hmr_dmr_regs_hw2reg_t hw2reg, // Read + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import hmr_dmr_regs_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic dmr_enable_qs; + logic dmr_enable_wd; + logic dmr_enable_we; + logic dmr_config_rapid_recovery_qs; + logic dmr_config_rapid_recovery_wd; + logic dmr_config_rapid_recovery_we; + logic dmr_config_force_recovery_qs; + logic dmr_config_force_recovery_wd; + logic dmr_config_force_recovery_we; + logic [31:0] checkpoint_addr_qs; + logic [31:0] checkpoint_addr_wd; + logic checkpoint_addr_we; + + // Register instances + // R[dmr_enable]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_dmr_enable ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (dmr_enable_we), + .wd (dmr_enable_wd), + + // from internal hardware + .de (hw2reg.dmr_enable.de), + .d (hw2reg.dmr_enable.d ), + + // to internal hardware + .qe (reg2hw.dmr_enable.qe), + .q (reg2hw.dmr_enable.q ), + + // to register interface (read) + .qs (dmr_enable_qs) + ); + + + // R[dmr_config]: V(False) + + // F[rapid_recovery]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_dmr_config_rapid_recovery ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (dmr_config_rapid_recovery_we), + .wd (dmr_config_rapid_recovery_wd), + + // from internal hardware + .de (hw2reg.dmr_config.rapid_recovery.de), + .d (hw2reg.dmr_config.rapid_recovery.d ), + + // to internal hardware + .qe (reg2hw.dmr_config.rapid_recovery.qe), + .q (reg2hw.dmr_config.rapid_recovery.q ), + + // to register interface (read) + .qs (dmr_config_rapid_recovery_qs) + ); + + + // F[force_recovery]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_dmr_config_force_recovery ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (dmr_config_force_recovery_we), + .wd (dmr_config_force_recovery_wd), + + // from internal hardware + .de (hw2reg.dmr_config.force_recovery.de), + .d (hw2reg.dmr_config.force_recovery.d ), + + // to internal hardware + .qe (reg2hw.dmr_config.force_recovery.qe), + .q (reg2hw.dmr_config.force_recovery.q ), + + // to register interface (read) + .qs (dmr_config_force_recovery_qs) + ); + + + // R[checkpoint_addr]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_checkpoint_addr ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (checkpoint_addr_we), + .wd (checkpoint_addr_wd), + + // from internal hardware + .de (hw2reg.checkpoint_addr.de), + .d (hw2reg.checkpoint_addr.d ), + + // to internal hardware + .qe (reg2hw.checkpoint_addr.qe), + .q (reg2hw.checkpoint_addr.q ), + + // to register interface (read) + .qs (checkpoint_addr_qs) + ); + + + + + logic [2:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == HMR_DMR_REGS_DMR_ENABLE_OFFSET); + addr_hit[1] = (reg_addr == HMR_DMR_REGS_DMR_CONFIG_OFFSET); + addr_hit[2] = (reg_addr == HMR_DMR_REGS_CHECKPOINT_ADDR_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(HMR_DMR_REGS_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(HMR_DMR_REGS_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(HMR_DMR_REGS_PERMIT[2] & ~reg_be))))); + end + + assign dmr_enable_we = addr_hit[0] & reg_we & !reg_error; + assign dmr_enable_wd = reg_wdata[0]; + + assign dmr_config_rapid_recovery_we = addr_hit[1] & reg_we & !reg_error; + assign dmr_config_rapid_recovery_wd = reg_wdata[0]; + + assign dmr_config_force_recovery_we = addr_hit[1] & reg_we & !reg_error; + assign dmr_config_force_recovery_wd = reg_wdata[1]; + + assign checkpoint_addr_we = addr_hit[2] & reg_we & !reg_error; + assign checkpoint_addr_wd = reg_wdata[31:0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = dmr_enable_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = dmr_config_rapid_recovery_qs; + reg_rdata_next[1] = dmr_config_force_recovery_qs; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = checkpoint_addr_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module hmr_dmr_regs_reg_top_intf +#( + parameter int AW = 4, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output hmr_dmr_regs_reg_pkg::hmr_dmr_regs_reg2hw_t reg2hw, // Write + input hmr_dmr_regs_reg_pkg::hmr_dmr_regs_hw2reg_t hw2reg, // Read + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + hmr_dmr_regs_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .hw2reg, // Read + .devmode_i + ); + +endmodule + + diff --git a/rtl/HMR/hmr_rapid_recovery_ctrl.sv b/rtl/HMR/hmr_rapid_recovery_ctrl.sv new file mode 100644 index 00000000..4f5b9f2f --- /dev/null +++ b/rtl/HMR/hmr_rapid_recovery_ctrl.sv @@ -0,0 +1,143 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Hybrid modular redundancy Rapid Recovery control unit + +module hmr_rapid_recovery_ctrl + import rapid_recovery_pkg::*; +#( + parameter int unsigned RFAddrWidth = 6, + parameter type regfile_write_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // input logic test_en_i, + + input logic start_recovery_i, + output logic recovery_finished_o, + + // Signals to core + output logic setback_o, + output logic instr_lock_o, + output logic debug_req_o, + input logic debug_halt_i, + output logic debug_resume_o, + output regfile_write_t recovery_regfile_waddr_o, + + // Signals to backup state + output logic backup_enable_o, + output logic recover_csr_enable_o, + output logic recover_pc_enable_o, + output logic recover_rf_enable_o +); + + typedef enum logic [1:0] {IDLE, RESET, HALT, RESTORE} recovery_mode_e; + recovery_mode_e rec_mode_d, rec_mode_q; + + logic instr_lock_d, instr_lock_q; + logic setback_d, setback_q; + logic addr_gen_done; + logic [RFAddrWidth-1:0] addr_gen_result; + + DMR_address_generator #( + .AddrWidth ( RFAddrWidth ) + ) i_rf_address_generator ( + .clk_i, + .rst_ni, + .clear_i ('0), + .enable_i ( recover_rf_enable_o ), + .done_o ( addr_gen_done ), + .fatal_o (), + .address_o (addr_gen_result) + ); + + assign recovery_regfile_waddr_o.we_a = recover_rf_enable_o; + assign recovery_regfile_waddr_o.waddr_a = addr_gen_result; + assign recovery_regfile_waddr_o.wdata_a = '0; + assign recovery_regfile_waddr_o.we_b = recover_rf_enable_o; + assign recovery_regfile_waddr_o.waddr_b = 16 + addr_gen_result; + assign recovery_regfile_waddr_o.wdata_b = '0; + + + assign instr_lock_o = instr_lock_q; + assign setback_o = setback_q; + + always_comb begin + rec_mode_d = rec_mode_q; + instr_lock_d = instr_lock_q; + backup_enable_o = 1'b0; + setback_d = 1'b0; + debug_req_o = 1'b0; + recover_csr_enable_o = 1'b0; + recover_pc_enable_o = 1'b0; + recover_rf_enable_o = 1'b0; + debug_resume_o = 1'b0; + recovery_finished_o = 1'b0; + + case (rec_mode_q) + IDLE: begin + backup_enable_o = 1'b1; + // If requested start the routine in the reset state + if (start_recovery_i) begin + // Disable all backups + backup_enable_o = 1'b0; + rec_mode_d = RESET; + end + end + + RESET: begin + // Clear the core state + setback_d = 1'b1; + // Lock the instruction requests + instr_lock_d = 1'b1; + // Go to request halt of the core + rec_mode_d = HALT; + end + + HALT: begin + // Requesst a debug halt + debug_req_o = 1'b1; + // Wait until the core has halted + if (debug_halt_i) begin + rec_mode_d = RESTORE; + end + end + + RESTORE: begin + // Enable CSR recovery routine + recover_csr_enable_o = 1'b1; + // Enable the PC recovery routine + recover_pc_enable_o = 1'b1; + // Enable the RF recovery routine + recover_rf_enable_o = 1'b1; + // If recovery routine complete, continue + if (addr_gen_done) begin + instr_lock_d = 1'b0; + rec_mode_d = IDLE; + debug_resume_o = 1'b1; + recovery_finished_o = 1'b1; + end + end + endcase + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + instr_lock_q <= 1'b0; + rec_mode_q <= IDLE; + setback_q <= 1'b0; + end else begin + instr_lock_q <= instr_lock_d; + rec_mode_q <= rec_mode_d; + setback_q <= setback_d; + end + end + +endmodule diff --git a/rtl/HMR/hmr_registers_reg_pkg.sv b/rtl/HMR/hmr_registers_reg_pkg.sv new file mode 100644 index 00000000..1c590cce --- /dev/null +++ b/rtl/HMR/hmr_registers_reg_pkg.sv @@ -0,0 +1,180 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package hmr_registers_reg_pkg; + + // Param list + parameter int NumCores = 12; + parameter int NumDMRGroups = 6; + parameter int NumTMRGroups = 4; + + // Address widths within the block + parameter int BlockAw = 5; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + logic [5:0] q; + logic qe; + } hmr_registers_reg2hw_dmr_enable_reg_t; + + typedef struct packed { + logic [3:0] q; + logic qe; + } hmr_registers_reg2hw_tmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } rapid_recovery; + struct packed { + logic q; + logic qe; + } force_recovery; + } hmr_registers_reg2hw_dmr_config_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } delay_resynch; + struct packed { + logic q; + logic qe; + } setback; + struct packed { + logic q; + logic qe; + } reload_setback; + struct packed { + logic q; + logic qe; + } rapid_recovery; + struct packed { + logic q; + logic qe; + } force_resynch; + } hmr_registers_reg2hw_tmr_config_reg_t; + + typedef struct packed { + struct packed { + logic d; + } independent; + struct packed { + logic d; + } dual; + struct packed { + logic d; + } triple; + struct packed { + logic d; + } rapid_recovery; + } hmr_registers_hw2reg_avail_config_reg_t; + + typedef struct packed { + logic [11:0] d; + } hmr_registers_hw2reg_cores_en_reg_t; + + typedef struct packed { + logic [5:0] d; + } hmr_registers_hw2reg_dmr_enable_reg_t; + + typedef struct packed { + logic [3:0] d; + } hmr_registers_hw2reg_tmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic d; + } rapid_recovery; + struct packed { + logic d; + } force_recovery; + } hmr_registers_hw2reg_dmr_config_reg_t; + + typedef struct packed { + struct packed { + logic d; + } delay_resynch; + struct packed { + logic d; + } setback; + struct packed { + logic d; + } reload_setback; + struct packed { + logic d; + } rapid_recovery; + struct packed { + logic d; + } force_resynch; + } hmr_registers_hw2reg_tmr_config_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_registers_reg2hw_dmr_enable_reg_t dmr_enable; // [25:19] + hmr_registers_reg2hw_tmr_enable_reg_t tmr_enable; // [18:14] + hmr_registers_reg2hw_dmr_config_reg_t dmr_config; // [13:10] + hmr_registers_reg2hw_tmr_config_reg_t tmr_config; // [9:0] + } hmr_registers_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_registers_hw2reg_avail_config_reg_t avail_config; // [32:29] + hmr_registers_hw2reg_cores_en_reg_t cores_en; // [28:17] + hmr_registers_hw2reg_dmr_enable_reg_t dmr_enable; // [16:11] + hmr_registers_hw2reg_tmr_enable_reg_t tmr_enable; // [10:7] + hmr_registers_hw2reg_dmr_config_reg_t dmr_config; // [6:5] + hmr_registers_hw2reg_tmr_config_reg_t tmr_config; // [4:0] + } hmr_registers_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] HMR_REGISTERS_AVAIL_CONFIG_OFFSET = 5'h 0; + parameter logic [BlockAw-1:0] HMR_REGISTERS_CORES_EN_OFFSET = 5'h 4; + parameter logic [BlockAw-1:0] HMR_REGISTERS_DMR_ENABLE_OFFSET = 5'h 8; + parameter logic [BlockAw-1:0] HMR_REGISTERS_TMR_ENABLE_OFFSET = 5'h c; + parameter logic [BlockAw-1:0] HMR_REGISTERS_DMR_CONFIG_OFFSET = 5'h 10; + parameter logic [BlockAw-1:0] HMR_REGISTERS_TMR_CONFIG_OFFSET = 5'h 14; + + // Reset values for hwext registers and their fields + parameter logic [8:0] HMR_REGISTERS_AVAIL_CONFIG_RESVAL = 9'h 0; + parameter logic [11:0] HMR_REGISTERS_CORES_EN_RESVAL = 12'h 0; + parameter logic [5:0] HMR_REGISTERS_DMR_ENABLE_RESVAL = 6'h 0; + parameter logic [5:0] HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_RESVAL = 6'h 0; + parameter logic [3:0] HMR_REGISTERS_TMR_ENABLE_RESVAL = 4'h 0; + parameter logic [3:0] HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_RESVAL = 4'h 0; + parameter logic [1:0] HMR_REGISTERS_DMR_CONFIG_RESVAL = 2'h 0; + parameter logic [4:0] HMR_REGISTERS_TMR_CONFIG_RESVAL = 5'h 6; + parameter logic [0:0] HMR_REGISTERS_TMR_CONFIG_DELAY_RESYNCH_RESVAL = 1'h 0; + parameter logic [0:0] HMR_REGISTERS_TMR_CONFIG_SETBACK_RESVAL = 1'h 1; + parameter logic [0:0] HMR_REGISTERS_TMR_CONFIG_RELOAD_SETBACK_RESVAL = 1'h 1; + parameter logic [0:0] HMR_REGISTERS_TMR_CONFIG_FORCE_RESYNCH_RESVAL = 1'h 0; + + // Register index + typedef enum int { + HMR_REGISTERS_AVAIL_CONFIG, + HMR_REGISTERS_CORES_EN, + HMR_REGISTERS_DMR_ENABLE, + HMR_REGISTERS_TMR_ENABLE, + HMR_REGISTERS_DMR_CONFIG, + HMR_REGISTERS_TMR_CONFIG + } hmr_registers_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] HMR_REGISTERS_PERMIT [6] = '{ + 4'b 0011, // index[0] HMR_REGISTERS_AVAIL_CONFIG + 4'b 0011, // index[1] HMR_REGISTERS_CORES_EN + 4'b 0001, // index[2] HMR_REGISTERS_DMR_ENABLE + 4'b 0001, // index[3] HMR_REGISTERS_TMR_ENABLE + 4'b 0001, // index[4] HMR_REGISTERS_DMR_CONFIG + 4'b 0001 // index[5] HMR_REGISTERS_TMR_CONFIG + }; + +endpackage + diff --git a/rtl/HMR/hmr_registers_reg_top.sv b/rtl/HMR/hmr_registers_reg_top.sv new file mode 100644 index 00000000..9769fed7 --- /dev/null +++ b/rtl/HMR/hmr_registers_reg_top.sv @@ -0,0 +1,516 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module hmr_registers_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 5 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output hmr_registers_reg_pkg::hmr_registers_reg2hw_t reg2hw, // Write + input hmr_registers_reg_pkg::hmr_registers_hw2reg_t hw2reg, // Read + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import hmr_registers_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic avail_config_independent_qs; + logic avail_config_independent_re; + logic avail_config_dual_qs; + logic avail_config_dual_re; + logic avail_config_triple_qs; + logic avail_config_triple_re; + logic avail_config_rapid_recovery_qs; + logic avail_config_rapid_recovery_re; + logic [11:0] cores_en_qs; + logic cores_en_re; + logic [5:0] dmr_enable_qs; + logic [5:0] dmr_enable_wd; + logic dmr_enable_we; + logic dmr_enable_re; + logic [3:0] tmr_enable_qs; + logic [3:0] tmr_enable_wd; + logic tmr_enable_we; + logic tmr_enable_re; + logic dmr_config_rapid_recovery_qs; + logic dmr_config_rapid_recovery_wd; + logic dmr_config_rapid_recovery_we; + logic dmr_config_rapid_recovery_re; + logic dmr_config_force_recovery_qs; + logic dmr_config_force_recovery_wd; + logic dmr_config_force_recovery_we; + logic dmr_config_force_recovery_re; + logic tmr_config_delay_resynch_qs; + logic tmr_config_delay_resynch_wd; + logic tmr_config_delay_resynch_we; + logic tmr_config_delay_resynch_re; + logic tmr_config_setback_qs; + logic tmr_config_setback_wd; + logic tmr_config_setback_we; + logic tmr_config_setback_re; + logic tmr_config_reload_setback_qs; + logic tmr_config_reload_setback_wd; + logic tmr_config_reload_setback_we; + logic tmr_config_reload_setback_re; + logic tmr_config_rapid_recovery_qs; + logic tmr_config_rapid_recovery_wd; + logic tmr_config_rapid_recovery_we; + logic tmr_config_rapid_recovery_re; + logic tmr_config_force_resynch_qs; + logic tmr_config_force_resynch_wd; + logic tmr_config_force_resynch_we; + logic tmr_config_force_resynch_re; + + // Register instances + // R[avail_config]: V(True) + + // F[independent]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_avail_config_independent ( + .re (avail_config_independent_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.avail_config.independent.d), + .qre (), + .qe (), + .q (), + .qs (avail_config_independent_qs) + ); + + + // F[dual]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_avail_config_dual ( + .re (avail_config_dual_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.avail_config.dual.d), + .qre (), + .qe (), + .q (), + .qs (avail_config_dual_qs) + ); + + + // F[triple]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_avail_config_triple ( + .re (avail_config_triple_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.avail_config.triple.d), + .qre (), + .qe (), + .q (), + .qs (avail_config_triple_qs) + ); + + + // F[rapid_recovery]: 8:8 + prim_subreg_ext #( + .DW (1) + ) u_avail_config_rapid_recovery ( + .re (avail_config_rapid_recovery_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.avail_config.rapid_recovery.d), + .qre (), + .qe (), + .q (), + .qs (avail_config_rapid_recovery_qs) + ); + + + // R[cores_en]: V(True) + + prim_subreg_ext #( + .DW (12) + ) u_cores_en ( + .re (cores_en_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.cores_en.d), + .qre (), + .qe (), + .q (), + .qs (cores_en_qs) + ); + + + // R[dmr_enable]: V(True) + + prim_subreg_ext #( + .DW (6) + ) u_dmr_enable ( + .re (dmr_enable_re), + .we (dmr_enable_we), + .wd (dmr_enable_wd), + .d (hw2reg.dmr_enable.d), + .qre (), + .qe (reg2hw.dmr_enable.qe), + .q (reg2hw.dmr_enable.q ), + .qs (dmr_enable_qs) + ); + + + // R[tmr_enable]: V(True) + + prim_subreg_ext #( + .DW (4) + ) u_tmr_enable ( + .re (tmr_enable_re), + .we (tmr_enable_we), + .wd (tmr_enable_wd), + .d (hw2reg.tmr_enable.d), + .qre (), + .qe (reg2hw.tmr_enable.qe), + .q (reg2hw.tmr_enable.q ), + .qs (tmr_enable_qs) + ); + + + // R[dmr_config]: V(True) + + // F[rapid_recovery]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_dmr_config_rapid_recovery ( + .re (dmr_config_rapid_recovery_re), + .we (dmr_config_rapid_recovery_we), + .wd (dmr_config_rapid_recovery_wd), + .d (hw2reg.dmr_config.rapid_recovery.d), + .qre (), + .qe (reg2hw.dmr_config.rapid_recovery.qe), + .q (reg2hw.dmr_config.rapid_recovery.q ), + .qs (dmr_config_rapid_recovery_qs) + ); + + + // F[force_recovery]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_dmr_config_force_recovery ( + .re (dmr_config_force_recovery_re), + .we (dmr_config_force_recovery_we), + .wd (dmr_config_force_recovery_wd), + .d (hw2reg.dmr_config.force_recovery.d), + .qre (), + .qe (reg2hw.dmr_config.force_recovery.qe), + .q (reg2hw.dmr_config.force_recovery.q ), + .qs (dmr_config_force_recovery_qs) + ); + + + // R[tmr_config]: V(True) + + // F[delay_resynch]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_delay_resynch ( + .re (tmr_config_delay_resynch_re), + .we (tmr_config_delay_resynch_we), + .wd (tmr_config_delay_resynch_wd), + .d (hw2reg.tmr_config.delay_resynch.d), + .qre (), + .qe (reg2hw.tmr_config.delay_resynch.qe), + .q (reg2hw.tmr_config.delay_resynch.q ), + .qs (tmr_config_delay_resynch_qs) + ); + + + // F[setback]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_setback ( + .re (tmr_config_setback_re), + .we (tmr_config_setback_we), + .wd (tmr_config_setback_wd), + .d (hw2reg.tmr_config.setback.d), + .qre (), + .qe (reg2hw.tmr_config.setback.qe), + .q (reg2hw.tmr_config.setback.q ), + .qs (tmr_config_setback_qs) + ); + + + // F[reload_setback]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_reload_setback ( + .re (tmr_config_reload_setback_re), + .we (tmr_config_reload_setback_we), + .wd (tmr_config_reload_setback_wd), + .d (hw2reg.tmr_config.reload_setback.d), + .qre (), + .qe (reg2hw.tmr_config.reload_setback.qe), + .q (reg2hw.tmr_config.reload_setback.q ), + .qs (tmr_config_reload_setback_qs) + ); + + + // F[rapid_recovery]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_rapid_recovery ( + .re (tmr_config_rapid_recovery_re), + .we (tmr_config_rapid_recovery_we), + .wd (tmr_config_rapid_recovery_wd), + .d (hw2reg.tmr_config.rapid_recovery.d), + .qre (), + .qe (reg2hw.tmr_config.rapid_recovery.qe), + .q (reg2hw.tmr_config.rapid_recovery.q ), + .qs (tmr_config_rapid_recovery_qs) + ); + + + // F[force_resynch]: 4:4 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_force_resynch ( + .re (tmr_config_force_resynch_re), + .we (tmr_config_force_resynch_we), + .wd (tmr_config_force_resynch_wd), + .d (hw2reg.tmr_config.force_resynch.d), + .qre (), + .qe (reg2hw.tmr_config.force_resynch.qe), + .q (reg2hw.tmr_config.force_resynch.q ), + .qs (tmr_config_force_resynch_qs) + ); + + + + + logic [5:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == HMR_REGISTERS_AVAIL_CONFIG_OFFSET); + addr_hit[1] = (reg_addr == HMR_REGISTERS_CORES_EN_OFFSET); + addr_hit[2] = (reg_addr == HMR_REGISTERS_DMR_ENABLE_OFFSET); + addr_hit[3] = (reg_addr == HMR_REGISTERS_TMR_ENABLE_OFFSET); + addr_hit[4] = (reg_addr == HMR_REGISTERS_DMR_CONFIG_OFFSET); + addr_hit[5] = (reg_addr == HMR_REGISTERS_TMR_CONFIG_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(HMR_REGISTERS_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(HMR_REGISTERS_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(HMR_REGISTERS_PERMIT[2] & ~reg_be))) | + (addr_hit[3] & (|(HMR_REGISTERS_PERMIT[3] & ~reg_be))) | + (addr_hit[4] & (|(HMR_REGISTERS_PERMIT[4] & ~reg_be))) | + (addr_hit[5] & (|(HMR_REGISTERS_PERMIT[5] & ~reg_be))))); + end + + assign avail_config_independent_re = addr_hit[0] & reg_re & !reg_error; + + assign avail_config_dual_re = addr_hit[0] & reg_re & !reg_error; + + assign avail_config_triple_re = addr_hit[0] & reg_re & !reg_error; + + assign avail_config_rapid_recovery_re = addr_hit[0] & reg_re & !reg_error; + + assign cores_en_re = addr_hit[1] & reg_re & !reg_error; + + assign dmr_enable_we = addr_hit[2] & reg_we & !reg_error; + assign dmr_enable_wd = reg_wdata[5:0]; + assign dmr_enable_re = addr_hit[2] & reg_re & !reg_error; + + assign tmr_enable_we = addr_hit[3] & reg_we & !reg_error; + assign tmr_enable_wd = reg_wdata[3:0]; + assign tmr_enable_re = addr_hit[3] & reg_re & !reg_error; + + assign dmr_config_rapid_recovery_we = addr_hit[4] & reg_we & !reg_error; + assign dmr_config_rapid_recovery_wd = reg_wdata[0]; + assign dmr_config_rapid_recovery_re = addr_hit[4] & reg_re & !reg_error; + + assign dmr_config_force_recovery_we = addr_hit[4] & reg_we & !reg_error; + assign dmr_config_force_recovery_wd = reg_wdata[1]; + assign dmr_config_force_recovery_re = addr_hit[4] & reg_re & !reg_error; + + assign tmr_config_delay_resynch_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_delay_resynch_wd = reg_wdata[0]; + assign tmr_config_delay_resynch_re = addr_hit[5] & reg_re & !reg_error; + + assign tmr_config_setback_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_setback_wd = reg_wdata[1]; + assign tmr_config_setback_re = addr_hit[5] & reg_re & !reg_error; + + assign tmr_config_reload_setback_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_reload_setback_wd = reg_wdata[2]; + assign tmr_config_reload_setback_re = addr_hit[5] & reg_re & !reg_error; + + assign tmr_config_rapid_recovery_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_rapid_recovery_wd = reg_wdata[3]; + assign tmr_config_rapid_recovery_re = addr_hit[5] & reg_re & !reg_error; + + assign tmr_config_force_resynch_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_force_resynch_wd = reg_wdata[4]; + assign tmr_config_force_resynch_re = addr_hit[5] & reg_re & !reg_error; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = avail_config_independent_qs; + reg_rdata_next[1] = avail_config_dual_qs; + reg_rdata_next[2] = avail_config_triple_qs; + reg_rdata_next[8] = avail_config_rapid_recovery_qs; + end + + addr_hit[1]: begin + reg_rdata_next[11:0] = cores_en_qs; + end + + addr_hit[2]: begin + reg_rdata_next[5:0] = dmr_enable_qs; + end + + addr_hit[3]: begin + reg_rdata_next[3:0] = tmr_enable_qs; + end + + addr_hit[4]: begin + reg_rdata_next[0] = dmr_config_rapid_recovery_qs; + reg_rdata_next[1] = dmr_config_force_recovery_qs; + end + + addr_hit[5]: begin + reg_rdata_next[0] = tmr_config_delay_resynch_qs; + reg_rdata_next[1] = tmr_config_setback_qs; + reg_rdata_next[2] = tmr_config_reload_setback_qs; + reg_rdata_next[3] = tmr_config_rapid_recovery_qs; + reg_rdata_next[4] = tmr_config_force_resynch_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module hmr_registers_reg_top_intf +#( + parameter int AW = 5, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output hmr_registers_reg_pkg::hmr_registers_reg2hw_t reg2hw, // Write + input hmr_registers_reg_pkg::hmr_registers_hw2reg_t hw2reg, // Read + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + hmr_registers_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .hw2reg, // Read + .devmode_i + ); + +endmodule + + diff --git a/rtl/HMR/hmr_tmr_ctrl.sv b/rtl/HMR/hmr_tmr_ctrl.sv new file mode 100644 index 00000000..6f05db62 --- /dev/null +++ b/rtl/HMR/hmr_tmr_ctrl.sv @@ -0,0 +1,238 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Hybrid modular redundancy TMR control unit + +module hmr_tmr_ctrl #( + parameter bit InterleaveGrps = 1'b0, + parameter bit TMRFixed = 1'b0, + parameter bit DefaultInTMR = TMRFixed ? 1'b1 : 1'b0, + parameter bit RapidRecovery = 1'b0, + parameter type reg_req_t = logic, + parameter type reg_resp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + // input logic test_enable_i, + + // Register interface + input reg_req_t reg_req_i, + output reg_resp_t reg_resp_o, + + // CTRL from external (e.g. HMR ctrl regs) + input logic tmr_enable_q_i, + input logic tmr_enable_qe_i, + input logic delay_resynch_q_i, + input logic delay_resynch_qe_i, + input logic setback_q_i, + input logic setback_qe_i, + input logic reload_setback_q_i, + input logic reload_setback_qe_i, + input logic rapid_recovery_q_i, + input logic rapid_recovery_qe_i, + input logic force_resynch_q_i, + input logic force_resynch_qe_i, + + // TMR control signals + output logic [2:0] setback_o, + output logic sw_resynch_req_o, + output logic sw_synch_req_o, + output logic grp_in_independent_o, + output logic rapid_recovery_en_o, + output logic [2:0] tmr_incr_mismatches_o, + input logic tmr_single_mismatch_i, + input logic [2:0] tmr_error_i, + input logic tmr_failure_i, + input logic sp_store_is_zero, + input logic sp_store_will_be_zero, + input logic fetch_en_i, + input logic cores_synch_i, + output logic recovery_request_o, + input logic recovery_finished_i +); + + logic synch_req, synch_req_sent_d, synch_req_sent_q; + logic resynch_req, resynch_req_sent_d, resynch_req_sent_q; + logic cores_synch_q; + + typedef enum logic [2:0] {NON_TMR, TMR_RUN, TMR_UNLOAD, TMR_RELOAD, TMR_RAPID} tmr_mode_e; + localparam tmr_mode_e DefaultTMRMode = DefaultInTMR || TMRFixed ? TMR_RUN : NON_TMR; + + hmr_tmr_regs_reg_pkg::hmr_tmr_regs_reg2hw_t tmr_reg2hw; + hmr_tmr_regs_reg_pkg::hmr_tmr_regs_hw2reg_t tmr_hw2reg; + + tmr_mode_e tmr_red_mode_d, tmr_red_mode_q; + + assign grp_in_independent_o = tmr_red_mode_q == NON_TMR; + assign tmr_resynch_req_o = tmr_red_mode_q == TMR_UNLOAD; + assign rapid_recovery_en_o = tmr_reg2hw.tmr_config.rapid_recovery.q & RapidRecovery; + + assign sw_synch_req_o = synch_req & ~synch_req_sent_q; + assign synch_req_sent_d = synch_req; + assign sw_resynch_req_o = resynch_req & ~resynch_req_sent_q; + assign resynch_req_sent_d = resynch_req; + + hmr_tmr_regs_reg_top #( + .reg_req_t(reg_req_t), + .reg_rsp_t(reg_resp_t) + ) i_tmr_regs ( + .clk_i, + .rst_ni, + .reg_req_i(reg_req_i), + .reg_rsp_o(reg_resp_o), + .reg2hw (tmr_reg2hw), + .hw2reg (tmr_hw2reg), + .devmode_i('0) + ); + + // Global config update + assign tmr_hw2reg.tmr_enable.de = tmr_enable_qe_i; + assign tmr_hw2reg.tmr_enable.d = tmr_enable_q_i; + assign tmr_hw2reg.tmr_config.delay_resynch.de = delay_resynch_qe_i; + assign tmr_hw2reg.tmr_config.delay_resynch.d = delay_resynch_q_i; + assign tmr_hw2reg.tmr_config.setback.de = setback_qe_i; + assign tmr_hw2reg.tmr_config.setback.d = setback_q_i; + assign tmr_hw2reg.tmr_config.reload_setback.de = reload_setback_qe_i; + assign tmr_hw2reg.tmr_config.reload_setback.d = reload_setback_q_i; + assign tmr_hw2reg.tmr_config.rapid_recovery.de = rapid_recovery_qe_i; + assign tmr_hw2reg.tmr_config.rapid_recovery.d = rapid_recovery_q_i; + assign tmr_hw2reg.tmr_config.force_resynch.d = force_resynch_qe_i ? force_resynch_q_i : 1'b0; + + /************************** + * FSM for TMR lockstep * + **************************/ + always_comb begin : proc_fsm + setback_o = 3'b000; + tmr_red_mode_d = tmr_red_mode_q; + tmr_incr_mismatches_o = '0; + recovery_request_o = 1'b0; + resynch_req = 1'b0; + synch_req = 1'b0; + + tmr_hw2reg.tmr_config.force_resynch.de = force_resynch_qe_i; + + case (tmr_red_mode_q) + TMR_RUN: begin + // If forced execute resynchronization + if (tmr_reg2hw.tmr_config.force_resynch.q) begin + tmr_hw2reg.tmr_config.force_resynch.de = 1'b1; + if (tmr_reg2hw.tmr_config.rapid_recovery.q == 1'b1 && RapidRecovery) begin + tmr_red_mode_d = TMR_RAPID; + end else if (tmr_reg2hw.tmr_config.delay_resynch.q == '0) begin + tmr_red_mode_d = TMR_UNLOAD; + // TODO: buffer the restoration until delay_resynch is disabled + end + end + + // If error detected, do resynchronization + if (tmr_single_mismatch_i) begin + $display("[HMR-triple] %t - mismatch detected", $realtime); + if (tmr_error_i[0]) tmr_incr_mismatches_o[0] = 1'b1; + if (tmr_error_i[1]) tmr_incr_mismatches_o[1] = 1'b1; + if (tmr_error_i[2]) tmr_incr_mismatches_o[2] = 1'b1; + + if (tmr_reg2hw.tmr_config.rapid_recovery.q == 1'b1 && RapidRecovery) begin + tmr_red_mode_d = TMR_RAPID; + end else if (tmr_reg2hw.tmr_config.delay_resynch.q == '0) begin + tmr_red_mode_d = TMR_UNLOAD; + // TODO: buffer the restoration until delay_resynch is disabled + end + end + end + + TMR_UNLOAD: begin + resynch_req = 1'b1; + // If unload complete, go to reload (and reset) + if (!sp_store_is_zero) begin + tmr_red_mode_d = TMR_RELOAD; + if (tmr_reg2hw.tmr_config.setback.q) begin + setback_o = 3'b111; + end + end + end + + TMR_RELOAD: begin + // If reload complete, finish (or reset if error happens during reload) + if (sp_store_is_zero) begin + $display("[HMR-triple] %t - mismatch restored", $realtime); + tmr_red_mode_d = TMR_RUN; + end else begin + if ((tmr_single_mismatch_i || tmr_failure_i) && tmr_reg2hw.tmr_config.setback.q && + tmr_reg2hw.tmr_config.reload_setback.q && + !sp_store_will_be_zero) begin + setback_o = 3'b111; + end + end + end + + TMR_RAPID: begin + recovery_request_o = 1'b1; + if (recovery_finished_i) begin + $display("[HMR-triple] %t - mismatch restored", $realtime); + tmr_red_mode_d = TMR_RUN; + end + end + + // Default: do nothing + + endcase + + // Logic to switch in and out of TMR + if (!TMRFixed) begin + // Set TMR mode on external signal that cores are synchronized + if (tmr_red_mode_q == NON_TMR && tmr_reg2hw.tmr_enable.q == 1'b1) begin + synch_req = 1'b1; + if (cores_synch_q == 1'b1) begin + if (tmr_reg2hw.tmr_config.rapid_recovery.q == 1'b1 && RapidRecovery) begin + tmr_red_mode_d = TMR_RAPID; + end else begin + tmr_red_mode_d = TMR_RELOAD; + if (tmr_reg2hw.tmr_config.setback.q == 1'b1) begin + setback_o = 3'b111; + end + end + end + end + // Before core startup: set TMR mode from reg2hw.tmr_enable + if (fetch_en_i == 0) begin + if (tmr_reg2hw.tmr_enable.q == 1'b0) begin + tmr_red_mode_d = NON_TMR; + end else begin + tmr_red_mode_d = TMR_RUN; + synch_req = 1'b0; + end + end + // split tolerant mode to performance mode anytime (but require correct core state) + if (tmr_red_mode_q == TMR_RUN) begin + if (tmr_reg2hw.tmr_enable.q == 1'b0) begin + if (tmr_reg2hw.tmr_config.setback.q) begin + setback_o = 3'b110; + end + tmr_red_mode_d = NON_TMR; + end + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_red_mode + if(!rst_ni) begin + tmr_red_mode_q <= DefaultTMRMode; + synch_req_sent_q <= '0; + resynch_req_sent_q <= '0; + cores_synch_q <= '0; + end else begin + tmr_red_mode_q <= tmr_red_mode_d; + synch_req_sent_q <= synch_req_sent_d; + resynch_req_sent_q <= resynch_req_sent_d; + cores_synch_q <= cores_synch_i; + end + end + +endmodule diff --git a/rtl/HMR/hmr_tmr_regs_reg_pkg.sv b/rtl/HMR/hmr_tmr_regs_reg_pkg.sv new file mode 100644 index 00000000..89eeca99 --- /dev/null +++ b/rtl/HMR/hmr_tmr_regs_reg_pkg.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 +// +// Register Package auto-generated by `reggen` containing data structure + +package hmr_tmr_regs_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 3; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + logic q; + logic qe; + } hmr_tmr_regs_reg2hw_tmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } delay_resynch; + struct packed { + logic q; + logic qe; + } setback; + struct packed { + logic q; + logic qe; + } reload_setback; + struct packed { + logic q; + logic qe; + } rapid_recovery; + struct packed { + logic q; + logic qe; + } force_resynch; + } hmr_tmr_regs_reg2hw_tmr_config_reg_t; + + typedef struct packed { + logic d; + logic de; + } hmr_tmr_regs_hw2reg_tmr_enable_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } delay_resynch; + struct packed { + logic d; + logic de; + } setback; + struct packed { + logic d; + logic de; + } reload_setback; + struct packed { + logic d; + logic de; + } rapid_recovery; + struct packed { + logic d; + logic de; + } force_resynch; + } hmr_tmr_regs_hw2reg_tmr_config_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_tmr_regs_reg2hw_tmr_enable_reg_t tmr_enable; // [11:10] + hmr_tmr_regs_reg2hw_tmr_config_reg_t tmr_config; // [9:0] + } hmr_tmr_regs_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_tmr_regs_hw2reg_tmr_enable_reg_t tmr_enable; // [11:10] + hmr_tmr_regs_hw2reg_tmr_config_reg_t tmr_config; // [9:0] + } hmr_tmr_regs_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] HMR_TMR_REGS_TMR_ENABLE_OFFSET = 3'h 0; + parameter logic [BlockAw-1:0] HMR_TMR_REGS_TMR_CONFIG_OFFSET = 3'h 4; + + // Register index + typedef enum int { + HMR_TMR_REGS_TMR_ENABLE, + HMR_TMR_REGS_TMR_CONFIG + } hmr_tmr_regs_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] HMR_TMR_REGS_PERMIT [2] = '{ + 4'b 0001, // index[0] HMR_TMR_REGS_TMR_ENABLE + 4'b 0001 // index[1] HMR_TMR_REGS_TMR_CONFIG + }; + +endpackage + diff --git a/rtl/HMR/hmr_tmr_regs_reg_top.sv b/rtl/HMR/hmr_tmr_regs_reg_top.sv new file mode 100644 index 00000000..1249580e --- /dev/null +++ b/rtl/HMR/hmr_tmr_regs_reg_top.sv @@ -0,0 +1,372 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +`include "common_cells/assertions.svh" + +module hmr_tmr_regs_reg_top #( + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 3 +) ( + input logic clk_i, + input logic rst_ni, + input reg_req_t reg_req_i, + output reg_rsp_t reg_rsp_o, + // To HW + output hmr_tmr_regs_reg_pkg::hmr_tmr_regs_reg2hw_t reg2hw, // Write + input hmr_tmr_regs_reg_pkg::hmr_tmr_regs_hw2reg_t hw2reg, // Read + + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import hmr_tmr_regs_reg_pkg::* ; + + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + // Below register interface can be changed + reg_req_t reg_intf_req; + reg_rsp_t reg_intf_rsp; + + + assign reg_intf_req = reg_req_i; + assign reg_rsp_o = reg_intf_rsp; + + + assign reg_we = reg_intf_req.valid & reg_intf_req.write; + assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; + assign reg_addr = reg_intf_req.addr; + assign reg_wdata = reg_intf_req.wdata; + assign reg_be = reg_intf_req.wstrb; + assign reg_intf_rsp.rdata = reg_rdata; + assign reg_intf_rsp.error = reg_error; + assign reg_intf_rsp.ready = 1'b1; + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err; + + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic tmr_enable_qs; + logic tmr_enable_wd; + logic tmr_enable_we; + logic tmr_config_delay_resynch_qs; + logic tmr_config_delay_resynch_wd; + logic tmr_config_delay_resynch_we; + logic tmr_config_setback_qs; + logic tmr_config_setback_wd; + logic tmr_config_setback_we; + logic tmr_config_reload_setback_qs; + logic tmr_config_reload_setback_wd; + logic tmr_config_reload_setback_we; + logic tmr_config_rapid_recovery_qs; + logic tmr_config_rapid_recovery_wd; + logic tmr_config_rapid_recovery_we; + logic tmr_config_force_resynch_qs; + logic tmr_config_force_resynch_wd; + logic tmr_config_force_resynch_we; + + // Register instances + // R[tmr_enable]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_tmr_enable ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_enable_we), + .wd (tmr_enable_wd), + + // from internal hardware + .de (hw2reg.tmr_enable.de), + .d (hw2reg.tmr_enable.d ), + + // to internal hardware + .qe (reg2hw.tmr_enable.qe), + .q (reg2hw.tmr_enable.q ), + + // to register interface (read) + .qs (tmr_enable_qs) + ); + + + // R[tmr_config]: V(False) + + // F[delay_resynch]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_tmr_config_delay_resynch ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_delay_resynch_we), + .wd (tmr_config_delay_resynch_wd), + + // from internal hardware + .de (hw2reg.tmr_config.delay_resynch.de), + .d (hw2reg.tmr_config.delay_resynch.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.delay_resynch.qe), + .q (reg2hw.tmr_config.delay_resynch.q ), + + // to register interface (read) + .qs (tmr_config_delay_resynch_qs) + ); + + + // F[setback]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h1) + ) u_tmr_config_setback ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_setback_we), + .wd (tmr_config_setback_wd), + + // from internal hardware + .de (hw2reg.tmr_config.setback.de), + .d (hw2reg.tmr_config.setback.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.setback.qe), + .q (reg2hw.tmr_config.setback.q ), + + // to register interface (read) + .qs (tmr_config_setback_qs) + ); + + + // F[reload_setback]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h1) + ) u_tmr_config_reload_setback ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_reload_setback_we), + .wd (tmr_config_reload_setback_wd), + + // from internal hardware + .de (hw2reg.tmr_config.reload_setback.de), + .d (hw2reg.tmr_config.reload_setback.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.reload_setback.qe), + .q (reg2hw.tmr_config.reload_setback.q ), + + // to register interface (read) + .qs (tmr_config_reload_setback_qs) + ); + + + // F[rapid_recovery]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_tmr_config_rapid_recovery ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_rapid_recovery_we), + .wd (tmr_config_rapid_recovery_wd), + + // from internal hardware + .de (hw2reg.tmr_config.rapid_recovery.de), + .d (hw2reg.tmr_config.rapid_recovery.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.rapid_recovery.qe), + .q (reg2hw.tmr_config.rapid_recovery.q ), + + // to register interface (read) + .qs (tmr_config_rapid_recovery_qs) + ); + + + // F[force_resynch]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_tmr_config_force_resynch ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_force_resynch_we), + .wd (tmr_config_force_resynch_wd), + + // from internal hardware + .de (hw2reg.tmr_config.force_resynch.de), + .d (hw2reg.tmr_config.force_resynch.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.force_resynch.qe), + .q (reg2hw.tmr_config.force_resynch.q ), + + // to register interface (read) + .qs (tmr_config_force_resynch_qs) + ); + + + + + logic [1:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == HMR_TMR_REGS_TMR_ENABLE_OFFSET); + addr_hit[1] = (reg_addr == HMR_TMR_REGS_TMR_CONFIG_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(HMR_TMR_REGS_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(HMR_TMR_REGS_PERMIT[1] & ~reg_be))))); + end + + assign tmr_enable_we = addr_hit[0] & reg_we & !reg_error; + assign tmr_enable_wd = reg_wdata[0]; + + assign tmr_config_delay_resynch_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_delay_resynch_wd = reg_wdata[0]; + + assign tmr_config_setback_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_setback_wd = reg_wdata[1]; + + assign tmr_config_reload_setback_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_reload_setback_wd = reg_wdata[2]; + + assign tmr_config_rapid_recovery_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_rapid_recovery_wd = reg_wdata[3]; + + assign tmr_config_force_resynch_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_force_resynch_wd = reg_wdata[4]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = tmr_enable_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = tmr_config_delay_resynch_qs; + reg_rdata_next[1] = tmr_config_setback_qs; + reg_rdata_next[2] = tmr_config_reload_setback_qs; + reg_rdata_next[3] = tmr_config_rapid_recovery_qs; + reg_rdata_next[4] = tmr_config_force_resynch_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + + // Assertions for Register Interface + `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + +endmodule + +module hmr_tmr_regs_reg_top_intf +#( + parameter int AW = 3, + localparam int DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + REG_BUS.in regbus_slave, + // To HW + output hmr_tmr_regs_reg_pkg::hmr_tmr_regs_reg2hw_t reg2hw, // Write + input hmr_tmr_regs_reg_pkg::hmr_tmr_regs_hw2reg_t hw2reg, // Read + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + localparam int unsigned STRB_WIDTH = DW/8; + +`include "register_interface/typedef.svh" +`include "register_interface/assign.svh" + + // Define structs for reg_bus + typedef logic [AW-1:0] addr_t; + typedef logic [DW-1:0] data_t; + typedef logic [STRB_WIDTH-1:0] strb_t; + `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) + + reg_bus_req_t s_reg_req; + reg_bus_rsp_t s_reg_rsp; + + // Assign SV interface to structs + `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) + `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) + + + + hmr_tmr_regs_reg_top #( + .reg_req_t(reg_bus_req_t), + .reg_rsp_t(reg_bus_rsp_t), + .AW(AW) + ) i_regs ( + .clk_i, + .rst_ni, + .reg_req_i(s_reg_req), + .reg_rsp_o(s_reg_rsp), + .reg2hw, // Write + .hw2reg, // Read + .devmode_i + ); + +endmodule + + diff --git a/rtl/HMR/hmr_unit.sv b/rtl/HMR/hmr_unit.sv new file mode 100644 index 00000000..a03d997f --- /dev/null +++ b/rtl/HMR/hmr_unit.sv @@ -0,0 +1,987 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Hybrid modular redundancy wrapping unit + +module hmr_unit #( + // Wrapper parameters + /// Number of physical cores + parameter int unsigned NumCores = 0, + /// Enables support for Dual Modular Redundancy + parameter bit DMRSupported = 1'b1, + /// Locks HMR into permanent DMR mode + parameter bit DMRFixed = 1'b0, + /// Enables support for Triple Modular Redundancy + parameter bit TMRSupported = 1'b1, + /// Locks HMR into permanent TMR mode + parameter bit TMRFixed = 1'b0, + /// Interleave DMR/TMR cores, alternatively with sequential grouping + parameter bit InterleaveGrps = 1'b1, + /// rapid recovery - tbd + parameter bit RapidRecovery = 1'b0, + /// Separates voters and checkers for data, which are then only checked if data request is valid + parameter bit SeparateData = 1'b1, + /// Separates voters and checkers for AXI buses + parameter bit SeparateAxiBus = 1'b0, + parameter bit AxiHasAce = 1'b0, + /// Number of separate voters/checkers for individual buses + parameter int unsigned NumBusVoters = 1, + /// Address width of the core register file (in RISC-V it should be always 6) + parameter int unsigned RfAddrWidth = 6, + parameter int unsigned SysDataWidth = 32, + /// General core inputs wrapping struct + parameter type all_inputs_t = logic, + /// General core outputs wrapping struct + parameter type nominal_outputs_t = logic, + /// Cores' backup output bus + parameter type core_backup_t = logic, + /// Bus outputs wrapping struct + parameter type bus_outputs_t = logic, + /// AXI output wrapping struct + parameter type axi_req_t = logic, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + /// Rapid recovery structure + parameter type rapid_recovery_t = logic, + // Local parameters depending on the above ones + /// Number of TMR groups (virtual TMR cores) + localparam int unsigned NumTMRGroups = (TMRFixed || TMRSupported) ? NumCores/3 : 1, + /// Number of physical cores used for TMR + localparam int unsigned NumTMRCores = NumTMRGroups * 3, + /// Number of physical cores NOT used for TMR + localparam int unsigned NumTMRLeftover = NumCores - NumTMRCores, + /// Number of DMR groups (virtual DMR cores) + localparam int unsigned NumDMRGroups = (DMRFixed || DMRSupported) ? NumCores/2 : 1, + /// Nubmer of physical cores used for DMR + localparam int unsigned NumDMRCores = NumDMRGroups * 2, + /// Number of physical cores NOT used for DMR + localparam int unsigned NumDMRLeftover = NumCores - NumDMRCores, + /// Number of cores visible to the system (Fixed mode removes unneeded system ports) + localparam int unsigned NumSysCores = DMRFixed ? NumDMRGroups : TMRFixed ? NumTMRGroups : NumCores +) ( + input logic clk_i , + input logic rst_ni, + + // Port to configuration unit + input reg_req_t reg_request_i , + output reg_rsp_t reg_response_o, + + // TMR signals + output logic [NumTMRGroups-1:0] tmr_failure_o , + output logic [ NumSysCores-1:0] tmr_error_o , // Should this not be NumTMRCores? or NumCores? + output logic [NumTMRGroups-1:0] tmr_resynch_req_o, + output logic [ NumCores-1:0] tmr_sw_synch_req_o, + input logic [NumTMRGroups-1:0] tmr_cores_synch_i, + + // DMR signals + output logic [NumDMRGroups-1:0] dmr_failure_o , + output logic [ NumSysCores-1:0] dmr_error_o , // Should this not be NumDMRCores? or NumCores? + output logic [NumDMRGroups-1:0] dmr_resynch_req_o, + output logic [ NumCores-1:0] dmr_sw_synch_req_o, + input logic [NumDMRGroups-1:0] dmr_cores_synch_i, + output logic redundancy_enable_o, + + // Rapid recovery buses + output rapid_recovery_t [NumSysCores-1:0] rapid_recovery_o, + input core_backup_t [NumCores-1:0] core_backup_i, + + // Boot address is handled apart from other signals + input logic [SysDataWidth-1:0] sys_bootaddress_i, + input all_inputs_t [NumSysCores-1:0] sys_inputs_i, + output nominal_outputs_t [NumSysCores-1:0] sys_nominal_outputs_o, + output bus_outputs_t [NumSysCores-1:0][NumBusVoters-1:0] sys_bus_outputs_o, + output axi_req_t [NumSysCores-1:0] sys_axi_outputs_o, + input logic [NumSysCores-1:0] sys_fetch_en_i, + input logic [NumSysCores-1:0][NumBusVoters-1:0] enable_bus_vote_i, + + // Boot address is handled apart from other signals + output logic [NumCores-1:0][SysDataWidth-1:0] core_bootaddress_o, + output logic [NumCores-1:0] core_setback_o, + output all_inputs_t [NumCores-1:0] core_inputs_o, + input nominal_outputs_t [NumCores-1:0] core_nominal_outputs_i, + input bus_outputs_t [NumCores-1:0][NumBusVoters-1:0] core_bus_outputs_i, + input axi_req_t [NumCores-1:0] core_axi_outputs_i +); + function int max(int a, int b); + return (a > b) ? a : b; + endfunction + + localparam int unsigned NumBackupRegs = max(DMRSupported || DMRFixed ? NumDMRGroups : 0, TMRSupported || TMRFixed ? NumTMRGroups : 0); + + function int tmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumTMRGroups; + else return (core_id/3); + endfunction + + function int tmr_core_id (int group_id, int core_offset); + if (InterleaveGrps) return group_id + core_offset * NumTMRGroups; + else return (group_id * 3) + core_offset; + endfunction + + function int tmr_shared_id (int group_id); + if (InterleaveGrps || !(DMRSupported || DMRFixed)) return group_id; + else return group_id + group_id/2; + endfunction + + function int tmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumTMRGroups; + else return core_id % 3; + endfunction + + function int dmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumDMRGroups; + else return (core_id/2); + endfunction + + function int dmr_core_id (int group_id, int core_offset); + if (InterleaveGrps) return group_id + core_offset * NumDMRGroups; + else return (group_id * 2) + core_offset; + endfunction + + function int dmr_shared_id (int group_id); + return group_id; + endfunction + + function int dmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumDMRGroups; + else return core_id % 2; + endfunction + + if (TMRFixed && DMRFixed) $fatal(1, "Cannot fix both TMR and DMR!"); + + nominal_outputs_t [NumTMRGroups-1:0] tmr_nominal_outputs; + bus_outputs_t [NumTMRGroups-1:0][NumBusVoters-1:0] tmr_bus_outputs; + + nominal_outputs_t [NumDMRGroups-1:0] dmr_nominal_outputs; + bus_outputs_t [NumDMRGroups-1:0][NumBusVoters-1:0] dmr_bus_outputs; + axi_req_t [NumDMRGroups-1:0] dmr_axi_outputs; + core_backup_t [NumDMRGroups-1:0] dmr_backup_outputs; + + logic [NumTMRGroups-1:0] tmr_failure, tmr_failure_main; + logic [NumTMRGroups-1:0][NumBusVoters-1:0] tmr_failure_data; + logic [NumTMRGroups-1:0][2:0] tmr_error, tmr_error_main; + logic [NumTMRGroups-1:0][NumBusVoters-1:0][2:0] tmr_error_data; + logic [NumTMRGroups-1:0] tmr_single_mismatch; + + logic [NumDMRGroups-1:0] dmr_failure, dmr_failure_main, dmr_failure_axi, dmr_failure_backup; + logic [NumDMRGroups-1:0][NumBusVoters-1:0] dmr_failure_data; + logic [NumDMRGroups-1:0][SysDataWidth-1:0] checkpoint_reg_q; + + /************************** + * Rapid Recovery Signals * + **************************/ + logic [ NumDMRGroups-1:0] dmr_recovery_start, dmr_recovery_finished; + logic [ NumTMRGroups-1:0] tmr_recovery_start, tmr_recovery_finished; + logic [NumBackupRegs-1:0] rapid_recovery_start, rapid_recovery_finished; + logic [NumBackupRegs-1:0] rapid_recovery_backup_en_inp, rapid_recovery_backup_en_oup; + logic [NumBackupRegs-1:0] rapid_recovery_setback; + rapid_recovery_t [NumBackupRegs-1:0] rapid_recovery_bus; + core_backup_t [NumBackupRegs-1:0] rapid_recovery_backup_bus, core_backup_q; + nominal_outputs_t [NumBackupRegs-1:0] rapid_recovery_nominal; + + /*************************** + * HMR Control Registers * + ***************************/ + + logic [NumCores-1:0] core_en_as_master; + logic [NumCores-1:0] core_in_independent; + logic [NumCores-1:0] core_in_dmr; + logic [NumCores-1:0] core_in_tmr; + logic [NumCores-1:0] dmr_core_rapid_recovery_en; + logic [NumCores-1:0] tmr_core_rapid_recovery_en; + + logic [NumDMRGroups-1:0][1:0] dmr_setback_q; + logic [NumDMRGroups-1:0] dmr_grp_in_independent; + logic [NumDMRGroups-1:0] dmr_rapid_recovery_en; + + logic [NumTMRGroups-1:0][2:0] tmr_setback_q; + logic [NumTMRGroups-1:0] tmr_grp_in_independent; + logic [NumTMRGroups-1:0] tmr_rapid_recovery_en; + + logic [NumCores-1:0] sp_store_is_zero; + logic [NumCores-1:0] sp_store_will_be_zero; + + assign redundancy_enable_o = (|core_in_dmr) | (|core_in_tmr); + + for (genvar i = 0; i < NumCores; i++) begin : gen_global_status + assign core_in_independent[i] = ~core_in_dmr[i] & ~core_in_tmr[i]; + assign core_in_dmr[i] = (DMRSupported || DMRFixed) && i < NumDMRCores ? ~dmr_grp_in_independent[dmr_group_id(i)] : '0; + assign core_in_tmr[i] = (TMRSupported || TMRFixed) && i < NumTMRCores ? ~tmr_grp_in_independent[tmr_group_id(i)] : '0; + assign core_en_as_master[i] = ((tmr_core_id(tmr_group_id(i), 0) == i || i>=NumTMRCores) ? 1'b1 : ~core_in_tmr[i]) & + ((dmr_core_id(dmr_group_id(i), 0) == i || i>=NumDMRCores) ? 1'b1 : ~core_in_dmr[i]); + assign dmr_core_rapid_recovery_en[i] = (DMRSupported || DMRFixed) && i < NumDMRCores && RapidRecovery ? dmr_rapid_recovery_en[dmr_group_id(i)] : '0; + assign tmr_core_rapid_recovery_en[i] = (TMRSupported || TMRFixed) && i < NumTMRCores && RapidRecovery ? tmr_rapid_recovery_en[tmr_group_id(i)] : '0; + end + + reg_req_t [3:0] top_register_reqs; + reg_rsp_t [3:0] top_register_resps; + + // 0x000-0x100 -> Top config + // 0x100-0x200 -> Core configs + // 0x200-0x300 -> DMR configs + // 0x300-0x400 -> TMR configs + + reg_demux #( + .NoPorts ( 4 ), + .req_t ( reg_req_t ), + .rsp_t ( reg_rsp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( reg_request_i.addr[9:8] ), + .in_req_i ( reg_request_i ), + .in_rsp_o ( reg_response_o ), + .out_req_o ( top_register_reqs ), + .out_rsp_i ( top_register_resps ) + ); + + // Global config registers + + hmr_registers_reg_pkg::hmr_registers_hw2reg_t hmr_hw2reg; + hmr_registers_reg_pkg::hmr_registers_reg2hw_t hmr_reg2hw; + + hmr_registers_reg_top #( + .reg_req_t( reg_req_t ), + .reg_rsp_t( reg_rsp_t ) + ) i_hmr_registers ( + .clk_i, + .rst_ni, + .reg_req_i(top_register_reqs[0] ), + .reg_rsp_o(top_register_resps[0]), + .reg2hw (hmr_reg2hw), + .hw2reg (hmr_hw2reg), + .devmode_i('0) + ); + + assign hmr_hw2reg.avail_config.independent.d = ~(TMRFixed | DMRFixed); + assign hmr_hw2reg.avail_config.dual.d = DMRFixed | DMRSupported; + assign hmr_hw2reg.avail_config.triple.d = TMRFixed | TMRSupported; + assign hmr_hw2reg.avail_config.rapid_recovery.d = RapidRecovery; + + always_comb begin : proc_reg_status + hmr_hw2reg.cores_en.d = '0; + hmr_hw2reg.cores_en.d = core_en_as_master; + + hmr_hw2reg.dmr_enable.d = '0; + hmr_hw2reg.dmr_enable.d[NumDMRGroups-1:0] = ~dmr_grp_in_independent; + hmr_hw2reg.tmr_enable.d = '0; + hmr_hw2reg.tmr_enable.d[NumTMRGroups-1:0] = ~tmr_grp_in_independent; + end + + assign hmr_hw2reg.tmr_config.delay_resynch.d = '0; + assign hmr_hw2reg.tmr_config.setback.d = '0; + assign hmr_hw2reg.tmr_config.reload_setback.d = '0; + assign hmr_hw2reg.tmr_config.force_resynch.d = '0; + assign hmr_hw2reg.tmr_config.rapid_recovery.d = '0; + + assign hmr_hw2reg.dmr_config.rapid_recovery.d = '0; + assign hmr_hw2reg.dmr_config.force_recovery.d = '0; + + // Core Config Registers + + reg_req_t [NumCores-1:0] core_register_reqs; + reg_rsp_t [NumCores-1:0] core_register_resps; + + // 4 words per core + + reg_demux #( + .NoPorts ( NumCores ), + .req_t ( reg_req_t ), + .rsp_t ( reg_rsp_t ) + ) i_core_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs [1].addr[4+$clog2(NumCores)-1:4] ), + .in_req_i ( top_register_reqs [1] ), + .in_rsp_o ( top_register_resps[1] ), + .out_req_o ( core_register_reqs ), + .out_rsp_i ( core_register_resps ) + ); + + hmr_core_regs_reg_pkg::hmr_core_regs_reg2hw_t [NumCores-1:0] core_config_reg2hw; + hmr_core_regs_reg_pkg::hmr_core_regs_hw2reg_t [NumCores-1:0] core_config_hw2reg; + + logic [NumCores-1:0] tmr_incr_mismatches; + logic [NumCores-1:0] dmr_incr_mismatches; + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_registers + hmr_core_regs_reg_top #( + .reg_req_t(reg_req_t), + .reg_rsp_t(reg_rsp_t) + ) icore_registers ( + .clk_i, + .rst_ni, + .reg_req_i( core_register_reqs [i] ), + .reg_rsp_o( core_register_resps[i] ), + .reg2hw ( core_config_reg2hw [i] ), + .hw2reg ( core_config_hw2reg [i] ), + .devmode_i('0) + ); + + assign core_config_hw2reg[i].mismatches.d = core_config_reg2hw[i].mismatches.q + 1; + assign core_config_hw2reg[i].mismatches.de = tmr_incr_mismatches[i] | dmr_incr_mismatches[i]; + assign core_config_hw2reg[i].current_mode.independent.d = core_in_independent[i]; + assign core_config_hw2reg[i].current_mode.dual.d = core_in_dmr[i]; + assign core_config_hw2reg[i].current_mode.triple.d = core_in_tmr[i]; + assign sp_store_is_zero[i] = core_config_reg2hw[i].sp_store.q == '0; + assign sp_store_will_be_zero[i] = core_config_reg2hw[i].sp_store.qe && core_register_reqs[i].wdata == '0; + end + + /********************************************************** + ******************** TMR Voters & Regs ******************* + **********************************************************/ + + if (TMRSupported || TMRFixed) begin : gen_tmr_logic + if (TMRFixed && NumCores % 3 != 0) $warning("Extra cores added not properly handled!"); + + reg_req_t [NumTMRGroups-1:0] tmr_register_reqs; + reg_rsp_t [NumTMRGroups-1:0] tmr_register_resps; + logic [NumTMRGroups-1:0] tmr_sw_synch_req; + + localparam TMRSelWidth = $clog2(NumTMRGroups); + + /*************** + * Registers * + ***************/ + if (NumTMRGroups == 1) begin + assign tmr_register_reqs[0] = top_register_reqs[3]; + assign top_register_resps[3] = tmr_register_resps[0]; + end else begin + reg_demux #( + .NoPorts ( NumTMRGroups ), + .req_t ( reg_req_t ), + .rsp_t ( reg_rsp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs[3].addr[4+$clog2(NumTMRGroups)-1:4] ), + .in_req_i ( top_register_reqs[3] ), + .in_rsp_o ( top_register_resps[3] ), + .out_req_o ( tmr_register_reqs ), + .out_rsp_i ( tmr_register_resps ) + ); + end + + for (genvar i = NumTMRCores; i < NumCores; i++) begin : gen_extra_core_assigns + assign tmr_incr_mismatches[i] = '0; + assign tmr_sw_synch_req_o[i] = '0; + end + + for (genvar i = 0; i < NumTMRGroups; i++) begin : gen_tmr_groups + + hmr_tmr_ctrl #( + .reg_req_t ( reg_req_t ), + .reg_resp_t ( reg_rsp_t ), + .TMRFixed ( TMRFixed ), + .InterleaveGrps ( InterleaveGrps ), + .DefaultInTMR ( 1'b0 ), + .RapidRecovery ( RapidRecovery ) + ) i_tmr_ctrl ( + .clk_i, + .rst_ni, + + .reg_req_i ( tmr_register_reqs[i] ), + .reg_resp_o ( tmr_register_resps[i] ), + + .tmr_enable_q_i ( hmr_reg2hw.tmr_enable.q[i] ), + .tmr_enable_qe_i ( hmr_reg2hw.tmr_enable.qe ), + .delay_resynch_q_i ( hmr_reg2hw.tmr_config.delay_resynch.q ), + .delay_resynch_qe_i ( hmr_reg2hw.tmr_config.delay_resynch.qe ), + .setback_q_i ( hmr_reg2hw.tmr_config.setback.q ), + .setback_qe_i ( hmr_reg2hw.tmr_config.setback.qe ), + .reload_setback_q_i ( hmr_reg2hw.tmr_config.reload_setback.q ), + .reload_setback_qe_i ( hmr_reg2hw.tmr_config.reload_setback.qe ), + .rapid_recovery_q_i ( hmr_reg2hw.tmr_config.rapid_recovery.q ), + .rapid_recovery_qe_i ( hmr_reg2hw.tmr_config.rapid_recovery.qe ), + .force_resynch_q_i ( hmr_reg2hw.tmr_config.force_resynch.q ), + .force_resynch_qe_i ( hmr_reg2hw.tmr_config.force_resynch.qe ), + + .setback_o ( tmr_setback_q[i] ), + .sw_resynch_req_o ( tmr_resynch_req_o[i] ), + .sw_synch_req_o ( tmr_sw_synch_req[i] ), + .grp_in_independent_o ( tmr_grp_in_independent[i] ), + .rapid_recovery_en_o ( tmr_rapid_recovery_en[i] ), + .tmr_incr_mismatches_o( {tmr_incr_mismatches[tmr_core_id(i,2)], tmr_incr_mismatches[tmr_core_id(i,1)], tmr_incr_mismatches[tmr_core_id(i,0)]} ), + .tmr_single_mismatch_i( tmr_single_mismatch[i] ), + .tmr_error_i ( tmr_error[i] ), + .tmr_failure_i ( tmr_failure[i] ), + .sp_store_is_zero ( sp_store_is_zero[tmr_core_id(i, 0)] ), + .sp_store_will_be_zero( sp_store_will_be_zero[tmr_core_id(i, 0)] ), + + .fetch_en_i ( sys_fetch_en_i[tmr_core_id(i, 0)] ), + .cores_synch_i ( tmr_cores_synch_i[i] ), + + .recovery_request_o ( tmr_recovery_start [i] ), + .recovery_finished_i ( tmr_recovery_finished [i] ) + ); + + assign tmr_sw_synch_req_o[tmr_core_id(i, 0)] = tmr_sw_synch_req[i]; + assign tmr_sw_synch_req_o[tmr_core_id(i, 1)] = tmr_sw_synch_req[i]; + assign tmr_sw_synch_req_o[tmr_core_id(i, 2)] = tmr_sw_synch_req[i]; + + always_comb begin + tmr_failure[i] = tmr_failure_main[i]; + tmr_error [i] = tmr_error_main [i]; + for (int j = 0; j < NumBusVoters; j++) begin + if (enable_bus_vote_i[tmr_core_id(i, 0)][j]) begin + tmr_failure[i] = tmr_failure[i] | tmr_failure_data[i][j]; + tmr_error [i] = tmr_error [i] | tmr_error_data [i][j]; + end + end + end + assign tmr_single_mismatch[i] = tmr_error[i] != 3'b000; + + bitwise_TMR_voter #( + .DataWidth( $bits(nominal_outputs_t) ), + .VoterType( 0 ) + ) i_main_voter ( + .a_i ( core_nominal_outputs_i[tmr_core_id(i, 0)] ), + .b_i ( core_nominal_outputs_i[tmr_core_id(i, 1)] ), + .c_i ( core_nominal_outputs_i[tmr_core_id(i, 2)] ), + .majority_o ( tmr_nominal_outputs [ i ] ), + .error_o ( tmr_failure_main [ i ] ), + .error_cba_o( tmr_error_main [ i ] ) + ); + if (SeparateData) begin : gen_data_voter + for (genvar j = 0; j < NumBusVoters; j++) begin + bitwise_TMR_voter #( + .DataWidth( $bits(bus_outputs_t) ), + .VoterType( 0 ) + ) i_data_voter ( + .a_i ( core_bus_outputs_i[tmr_core_id(i, 0)][j] ), + .b_i ( core_bus_outputs_i[tmr_core_id(i, 1)][j] ), + .c_i ( core_bus_outputs_i[tmr_core_id(i, 2)][j] ), + .majority_o ( tmr_bus_outputs [ i ][j] ), + .error_o ( tmr_failure_data [ i ][j] ), + .error_cba_o( tmr_error_data [ i ][j] ) + ); + end + end + end + end else begin : gen_no_tmr_voted + assign tmr_error_main = '0; + assign tmr_error_data = '0; + assign tmr_error = '0; + assign tmr_failure_main = '0; + assign tmr_failure_data = '0; + assign tmr_failure = '0; + assign tmr_nominal_outputs = '0; + assign tmr_bus_outputs = '0; + assign top_register_resps[3].rdata = '0; + assign top_register_resps[3].error = 1'b1; + assign top_register_resps[3].ready = 1'b1; + assign tmr_incr_mismatches = '0; + assign tmr_grp_in_independent = '1; + assign tmr_setback_q = '0; + assign tmr_resynch_req_o = '0; + assign tmr_sw_synch_req_o = '0; + end + + /************************************************************ + ******************** DMR Voters and Regs ******************* + ************************************************************/ + + if (DMRSupported || DMRFixed) begin: gen_dmr_logic + + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_reg2hw_t [NumDMRGroups-1:0] dmr_reg2hw; + hmr_dmr_regs_reg_pkg::hmr_dmr_regs_hw2reg_t [NumDMRGroups-1:0] dmr_hw2reg; + + reg_req_t [NumDMRGroups-1:0] dmr_register_reqs; + reg_rsp_t [NumDMRGroups-1:0] dmr_register_resps; + logic [NumDMRGroups-1:0] dmr_sw_synch_req; + + localparam DMRSelWidth = $clog2(NumDMRGroups); + + /*************** + * Registers * + ***************/ + if (NumDMRGroups == 1) begin + assign dmr_register_reqs[0] = top_register_reqs[2]; + assign top_register_resps[2] = dmr_register_resps[0]; + end else begin + reg_demux #( + .NoPorts ( NumDMRGroups ), + .req_t ( reg_req_t ), + .rsp_t ( reg_rsp_t ) + ) i_reg_demux ( + .clk_i, + .rst_ni, + .in_select_i( top_register_reqs[2].addr[4+$clog2(NumDMRGroups)-1:4] ), + .in_req_i ( top_register_reqs[2] ), + .in_rsp_o ( top_register_resps[2] ), + .out_req_o ( dmr_register_reqs ), + .out_rsp_i ( dmr_register_resps ) + ); + end + + for (genvar i = NumDMRCores; i < NumCores; i++) begin : gen_extra_core_assigns + assign dmr_incr_mismatches[i] = '0; + assign dmr_sw_synch_req_o[i] = '0; + end + + for (genvar i = 0; i < NumDMRGroups; i++) begin : gen_dmr_groups + + hmr_dmr_ctrl #( + .reg_req_t ( reg_req_t ), + .reg_resp_t ( reg_rsp_t ), + .DataWidth ( SysDataWidth ), + .InterleaveGrps( InterleaveGrps ), + .DMRFixed ( DMRFixed ), + .RapidRecovery ( RapidRecovery ), + .DefaultInDMR ( 1'b0 ) + ) i_dmr_ctrl ( + .clk_i, + .rst_ni, + + .reg_req_i ( dmr_register_reqs [i] ), + .reg_resp_o ( dmr_register_resps[i] ), + + .dmr_enable_q_i ( hmr_reg2hw.dmr_enable.q[i] ), + .dmr_enable_qe_i ( hmr_reg2hw.dmr_enable.qe ), + .rapid_recovery_q_i ( hmr_reg2hw.dmr_config.rapid_recovery.q ), + .rapid_recovery_qe_i ( hmr_reg2hw.dmr_config.rapid_recovery.qe ), + .force_recovery_q_i ( hmr_reg2hw.dmr_config.force_recovery.q ), + .force_recovery_qe_i ( hmr_reg2hw.dmr_config.force_recovery.qe ), + + .setback_o ( dmr_setback_q [i] ), + .sw_resynch_req_o ( dmr_resynch_req_o [i] ), + .sw_synch_req_o ( dmr_sw_synch_req [i] ), + .checkpoint_o ( checkpoint_reg_q [i] ), + .grp_in_independent_o ( dmr_grp_in_independent[i] ), + .rapid_recovery_en_o ( dmr_rapid_recovery_en [i] ), + .dmr_incr_mismatches_o ( {dmr_incr_mismatches[dmr_core_id(i, 1)], dmr_incr_mismatches[dmr_core_id(i, 0)]} ), + .dmr_error_i ( dmr_failure [i] ), + + .fetch_en_i ( sys_fetch_en_i[dmr_core_id(i, 0)] ), + .cores_synch_i ( dmr_cores_synch_i[i] ), + + .recovery_request_o ( dmr_recovery_start [i] ), + .recovery_finished_i ( dmr_recovery_finished [i] ) + ); + + assign dmr_sw_synch_req_o[dmr_core_id(i, 0)] = dmr_sw_synch_req[i]; + assign dmr_sw_synch_req_o[dmr_core_id(i, 1)] = dmr_sw_synch_req[i]; + + /********************* + * DMR Core Checkers * + *********************/ + DMR_checker #( + .check_bus_t ( nominal_outputs_t ) + ) dmr_core_checker_main ( + .clk_i ( ), + .rst_ni ( ), + .inp_a_i ( core_nominal_outputs_i[dmr_core_id(i, 0)] ), + .inp_b_i ( core_nominal_outputs_i[dmr_core_id(i, 1)] ), + .check_o ( dmr_nominal_outputs [ i ] ), + .error_o ( dmr_failure_main [ i ] ) + ); + if (SeparateAxiBus) begin: gen_axi_checker + DMR_checker #( + .AxiBus ( SeparateAxiBus ), + .AxiHasAce ( AxiHasAce ), + .check_bus_t ( axi_req_t ) + ) dmr_core_checker_axi ( + .clk_i ( ), + .rst_ni ( ), + .inp_a_i ( core_axi_outputs_i[dmr_core_id(i, 0)] ), + .inp_b_i ( core_axi_outputs_i[dmr_core_id(i, 1)] ), + .check_o ( dmr_axi_outputs [ i ] ), + .error_o ( dmr_failure_axi [ i ] ) + ); + end else begin: gen_no_axi_checker + assign dmr_axi_outputs[i] = '0; + assign dmr_failure_axi[i] = '0; + end + if (SeparateData) begin : gen_data_checker + for (genvar j = 0; j < NumBusVoters; j++) begin + DMR_checker # ( + .check_bus_t ( bus_outputs_t ) + ) dmr_core_checker_data ( + .clk_i ( ), + .rst_ni ( ), + .inp_a_i ( core_bus_outputs_i[dmr_core_id(i, 0)][j] ), + .inp_b_i ( core_bus_outputs_i[dmr_core_id(i, 1)][j] ), + .check_o ( dmr_bus_outputs [ i ][j] ), + .error_o ( dmr_failure_data [ i ][j] ) + ); + end + end else begin: gen_no_data_checker + assign dmr_bus_outputs [i] = '0; + assign dmr_failure_data [i] = '0; + end + + if (RapidRecovery) begin : gen_rapid_recovery_unit + + DMR_checker #( + .check_bus_t ( core_backup_t ), + .Pipeline ( 1 ) + ) dmr_core_checker_backup ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .inp_a_i ( core_backup_i [dmr_core_id(i, 0)] ), + .inp_b_i ( core_backup_i [dmr_core_id(i, 1)] ), + .check_o ( dmr_backup_outputs [ i ] ), + .error_o ( dmr_failure_backup [ i ] ) + ); + + assign rapid_recovery_backup_en_inp[i] = core_in_tmr[i] ? (i < NumTMRGroups ? rapid_recovery_backup_en_oup[i] : 1'b0) // TMR mode + : core_in_dmr[i] ? (rapid_recovery_backup_en_oup[i] & ~dmr_failure[i] ) // DMR mode + : 1'b1; // Independent + rapid_recovery_unit #( + .RfAddrWidth ( RfAddrWidth ), + .DataWidth ( SysDataWidth ), + .regfile_write_t ( rapid_recovery_pkg::regfile_write_t ), + .regfile_raddr_t ( rapid_recovery_pkg::regfile_raddr_t ), + .regfile_rdata_t ( rapid_recovery_pkg::regfile_rdata_t ), + .csr_intf_t ( rapid_recovery_pkg::csrs_intf_t ), + .pc_intf_t ( rapid_recovery_pkg::pc_intf_t ) + ) i_rapid_recovery_unit ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .core_in_independent_i ( core_in_independent[i] ), + .regfile_write_i ( rapid_recovery_backup_bus[i].regfile_backup ), + .backup_csr_i ( rapid_recovery_backup_bus[i].csr_backup ), + .recovery_csr_o ( rapid_recovery_bus[i].csr_recovery ), + .backup_pc_i ( rapid_recovery_backup_bus[i].pc_backup ), + .recovery_pc_o ( rapid_recovery_bus[i].pc_recovery ), + .backup_enable_i ( rapid_recovery_backup_en_inp[i] ), + .start_recovery_i ( rapid_recovery_start[i] ), + .backup_enable_o ( rapid_recovery_backup_en_oup[i] ), + .recovery_finished_o ( rapid_recovery_finished[i] ), + .setback_o ( rapid_recovery_setback[i] ), + .instr_lock_o ( rapid_recovery_bus[i].instr_lock ), + .enable_pc_recovery_o ( rapid_recovery_bus[i].pc_recovery_en ), + .enable_rf_recovery_o ( rapid_recovery_bus[i].rf_recovery_en ), + .regfile_recovery_wdata_o ( rapid_recovery_bus[i].rf_recovery_wdata ), + .regfile_recovery_rdata_o ( rapid_recovery_bus[i].rf_recovery_rdata ), + .debug_halt_i ( rapid_recovery_nominal[i].debug_halted ), + .debug_req_o ( rapid_recovery_bus[i].debug_req ), + .debug_resume_o ( rapid_recovery_bus[i].debug_resume ) + ); + + always_comb begin + dmr_failure[i] = dmr_failure_main[i] | dmr_failure_backup[i] | dmr_failure_axi[i]; + for (int j = 0; j < NumBusVoters; j++) begin + if (enable_bus_vote_i[dmr_core_id(i, 0)][j]) begin + dmr_failure[i] = dmr_failure[i] | dmr_failure_backup[i] | dmr_failure_data[i][j]; + end + end + end + end else begin : gen_standard_failure + always_comb begin + dmr_failure[i] = dmr_failure_main[i] | dmr_failure_axi[i]; + for (int j = 0; j < NumBusVoters; j++) begin + if (enable_bus_vote_i[dmr_core_id(i, 0)][j]) begin + dmr_failure[i] = dmr_failure[i] | dmr_failure_data[i][j]; + end + end + end + end + end + end else begin: no_dmr_checkers + assign dmr_failure_main = '0; + assign dmr_failure_data = '0; + assign dmr_failure_axi = '0; + assign dmr_failure = '0; + assign dmr_incr_mismatches = '0; + assign dmr_nominal_outputs = '0; + assign dmr_bus_outputs = '0; + assign dmr_axi_outputs = '0; + assign top_register_resps[2].rdata = '0; + assign top_register_resps[2].error = 1'b1; + assign top_register_resps[2].ready = 1'b1; + assign dmr_sw_synch_req_o = '0; + assign dmr_grp_in_independent = '1; + end + + if (RapidRecovery) begin: gen_rapid_recovery_connection + + for (genvar i = 0; i < NumBackupRegs; i++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin + if (~rst_ni) begin + core_backup_q[i] <= '0; + end else begin + core_backup_q[i] <= core_backup_i[i]; + end + end + end + + always_comb begin + rapid_recovery_nominal = '0; + rapid_recovery_backup_bus = '0; + rapid_recovery_start = '0; + dmr_recovery_finished = '0; + tmr_recovery_finished = '0; + if (InterleaveGrps) begin + for (int i = 0; i < NumBackupRegs; i++) begin + rapid_recovery_nominal[i] = dmr_nominal_outputs[i]; + rapid_recovery_backup_bus[i] = core_backup_q[i]; + rapid_recovery_start[i] = dmr_recovery_start[i]; + dmr_recovery_finished[i] = rapid_recovery_finished[i]; + end + end + for (int i = 0; i < NumDMRGroups; i++) begin + if ((DMRFixed || (DMRSupported && ~dmr_grp_in_independent[i])) && dmr_core_rapid_recovery_en[dmr_core_id(i, 0)]) begin + rapid_recovery_nominal[dmr_shared_id(i)] = dmr_nominal_outputs[i]; + rapid_recovery_backup_bus[dmr_shared_id(i)] = dmr_backup_outputs[i]; + rapid_recovery_start[dmr_shared_id(i)] = dmr_recovery_start[i]; + dmr_recovery_finished[i] = rapid_recovery_finished[dmr_shared_id(i)]; + end + end + for (int i = 0; i < NumTMRGroups; i++) begin + if ((TMRFixed || (TMRSupported && ~tmr_grp_in_independent[i])) && tmr_core_rapid_recovery_en[tmr_core_id(i, 0)]) begin + rapid_recovery_nominal[tmr_shared_id(i)] = tmr_nominal_outputs[i]; + rapid_recovery_start[tmr_shared_id(i)] = tmr_recovery_start[i]; + tmr_recovery_finished[i] = rapid_recovery_finished[tmr_shared_id(i)]; + end + end + end + end else begin + assign core_backup_q = '0; + assign rapid_recovery_nominal = '0; + assign rapid_recovery_start = '0; + assign tmr_recovery_finished = '1; + assign dmr_recovery_finished = '1; + end + + // Assign output signals + if (DMRSupported && TMRSupported) begin : gen_full_HMR + /***************** + *** TMR & DMR *** + *****************/ + if (TMRFixed || DMRFixed) $fatal(1, "Cannot support both TMR and DMR and fix one!"); + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam TMRCoreIndex = tmr_core_id(tmr_group_id(i), 0); + localparam DMRCoreIndex = dmr_core_id(dmr_group_id(i), 0); + + always_comb begin + // Special signals + core_bootaddress_o[i] = (checkpoint_reg_q[dmr_shared_id(dmr_group_id(i))] != '0) ? + checkpoint_reg_q[dmr_shared_id(dmr_group_id(i))] : sys_bootaddress_i; + if (RapidRecovery) begin + // $error("UNIMPLEMENTED"); + rapid_recovery_o [i] = (core_in_dmr[i] ? rapid_recovery_bus [dmr_shared_id(dmr_group_id(i))] : + (core_in_tmr[i] ? rapid_recovery_bus [tmr_shared_id(tmr_group_id(i))] : '0)); + + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)] + | (core_in_dmr[i] ? rapid_recovery_setback [dmr_shared_id(dmr_group_id(i))] : + (core_in_tmr[i] ? rapid_recovery_setback [tmr_shared_id(tmr_group_id(i))] : '0)); + end else begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)]; + end + if (i >= NumTMRCores && i >= NumDMRCores) begin + core_setback_o [i] = '0; + end else if (i < NumTMRCores && i >= NumDMRCores) begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)][tmr_offset_id(i)] + | (RapidRecovery ? (core_in_tmr[i] ? rapid_recovery_setback [tmr_shared_id(tmr_group_id(i))] : '0) : '0); + end else if (i >= NumTMRCores && i < NumDMRCores) begin + core_setback_o [i] = dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)] + | (RapidRecovery ? (core_in_dmr[i] ? rapid_recovery_setback [dmr_shared_id(dmr_group_id(i))] : '0) : '0); + end + if (i < NumTMRCores && core_in_tmr[i]) begin : tmr_mode + core_inputs_o[i] = sys_inputs_i[TMRCoreIndex]; + end else if (i < NumDMRCores && core_in_dmr[i]) begin : dmr_mode + core_inputs_o[i] = sys_inputs_i[DMRCoreIndex]; + end else begin : independent_mode + core_inputs_o[i] = sys_inputs_i[i]; + end + end + end + + for (genvar i = 0; i < NumSysCores/*==NumCores*/; i++) begin : gen_core_outputs + localparam TMRCoreIndex = tmr_group_id(i); + localparam DMRCoreIndex = dmr_group_id(i); + always_comb begin + if (i < NumTMRCores && core_in_tmr[i]) begin : tmr_mode + if (tmr_core_id(tmr_group_id(i), 0) == i) begin : is_tmr_main_core + sys_nominal_outputs_o[i] = tmr_nominal_outputs[TMRCoreIndex]; + sys_bus_outputs_o[i] = tmr_bus_outputs[TMRCoreIndex]; + sys_axi_outputs_o[i] = '0; + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o[i] = '0; + sys_axi_outputs_o[i] = '0; + sys_axi_outputs_o[i].r_ready = 1'b1; + sys_axi_outputs_o[i].b_ready = 1'b1; + end + end else if (i < NumDMRCores && core_in_dmr[i]) begin : dmr_mode + if (dmr_core_id(dmr_group_id(i), 0) == i) begin : is_dmr_main_core + sys_nominal_outputs_o[i] = dmr_nominal_outputs[DMRCoreIndex]; + sys_axi_outputs_o[i] = dmr_axi_outputs[DMRCoreIndex]; + for (int j = 0; j < NumBusVoters; j++) begin + sys_bus_outputs_o[i][j] = dmr_bus_outputs[DMRCoreIndex][j]; + end + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o[i] = '0; + sys_axi_outputs_o[i] = '0; + sys_axi_outputs_o[i].r_ready = 1'b1; + sys_axi_outputs_o[i].b_ready = 1'b1; + end + end else begin : independent_mode + sys_nominal_outputs_o[i] = core_nominal_outputs_i[i]; + sys_bus_outputs_o[i] = core_bus_outputs_i[i]; + sys_axi_outputs_o[i] = core_axi_outputs_i[i]; + end + end + end + + end else if (TMRSupported || TMRFixed) begin : gen_TMR_only + /***************** + *** TMR only *** + *****************/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam SysCoreIndex = TMRFixed ? i/3 : tmr_core_id(tmr_group_id(i), 0); + always_comb begin + // Special signals + core_bootaddress_o[i] = (checkpoint_reg_q[dmr_shared_id(dmr_group_id(i))] != '0) ? + checkpoint_reg_q[dmr_shared_id(dmr_group_id(i))] : sys_bootaddress_i; + // Setback + if (RapidRecovery) begin + // $error("UNIMPLEMENTED"); + rapid_recovery_o [i] = core_in_tmr[i] ? rapid_recovery_bus [tmr_shared_id(tmr_group_id(i))] : '0; + + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)] + | rapid_recovery_setback [tmr_shared_id(tmr_group_id(i))]; + end else begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)]; + end + if (i >= NumTMRCores) begin + core_setback_o [i] = '0; + end + if (i < NumTMRCores && (TMRFixed || core_in_tmr[i])) begin : tmr_mode + core_inputs_o[i] = sys_inputs_i[SysCoreIndex]; + end else begin : independent_mode + core_inputs_o[i] = sys_inputs_i[i]; + end + end + end + + for (genvar i = 0; i < NumSysCores; i++) begin : gen_core_outputs + localparam CoreCoreIndex = TMRFixed ? i : tmr_group_id(i); + if (TMRFixed && i < NumTMRGroups) begin : fixed_tmr + assign sys_nominal_outputs_o[i] = tmr_nominal_outputs[CoreCoreIndex]; + assign sys_bus_outputs_o [i] = tmr_bus_outputs [CoreCoreIndex]; + assign sys_axi_outputs_o [i] = '0; + end else begin + if (i >= NumTMRCores) begin : independent_stragglers + assign sys_nominal_outputs_o[i] = core_nominal_outputs_i[TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_bus_outputs_o [i] = core_bus_outputs_i [TMRFixed ? i-NumTMRGroups+NumTMRCores : i]; + assign sys_axi_outputs_o [i] = '0; + end else begin + always_comb begin + sys_axi_outputs_o [i] = '0; + if (core_in_tmr[i]) begin : tmr_mode + if (tmr_core_id(tmr_group_id(i), 0) == i) begin : is_tmr_main_core + sys_nominal_outputs_o[i] = tmr_nominal_outputs[CoreCoreIndex]; + sys_bus_outputs_o [i] = tmr_bus_outputs [CoreCoreIndex]; + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o [i] = '0; + sys_axi_outputs_o[i].r_ready = 1'b1; + sys_axi_outputs_o[i].b_ready = 1'b1; + end + end else begin : independent_mode + sys_nominal_outputs_o[i] = core_nominal_outputs_i[i]; + sys_bus_outputs_o [i] = core_bus_outputs_i [i]; + end + end + end + end + end + + end else if (DMRSupported || DMRFixed) begin : gen_DMR_only + /***************** + *** DMR only *** + *****************/ + if (DMRFixed && NumCores % 2 != 0) $warning("Extra cores added not properly handled! :)"); + // Binding DMR outputs to zero for now + assign dmr_failure_o = '0; + assign dmr_error_o = '0; + // assign dmr_resynch_req_o = '0; + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam SysCoreIndex = DMRFixed ? i/2 : dmr_core_id(dmr_group_id(i), 0); + always_comb begin + core_bootaddress_o[i] = (checkpoint_reg_q[SysCoreIndex] != '0) ? + checkpoint_reg_q[SysCoreIndex] : sys_bootaddress_i; + // Setback + if (RapidRecovery) begin + // $error("UNIMPLEMENTED"); + rapid_recovery_o [i] = core_in_dmr[i] ? rapid_recovery_bus [dmr_shared_id(dmr_group_id(i))] : '0; + + core_setback_o [i] = dmr_setback_q[dmr_group_id(i)][dmr_offset_id(i)] + | rapid_recovery_setback [dmr_shared_id(dmr_group_id(i))]; + end else begin + core_setback_o [i] = dmr_setback_q[dmr_group_id(i)][dmr_offset_id(i)]; + end + if (i >= NumDMRCores) begin + core_setback_o [i] = '0; + end + if (i < NumDMRCores && (DMRFixed || core_in_dmr[i])) begin : dmr_mode + core_inputs_o[i] = sys_inputs_i[SysCoreIndex]; + end else begin : gen_independent_mode + core_inputs_o[i] = sys_inputs_i[i]; + end + end + end // gen_core_inputs + + for (genvar i = 0; i < NumSysCores; i++) begin : gen_core_outputs + localparam CoreCoreIndex = DMRFixed ? i : dmr_group_id(i); + if (DMRFixed && i < NumDMRGroups) begin : fixed_dmr + assign sys_nominal_outputs_o[i] = dmr_nominal_outputs[CoreCoreIndex]; + assign sys_bus_outputs_o [i] = dmr_bus_outputs [CoreCoreIndex]; + assign sys_axi_outputs_o [i] = dmr_axi_outputs [CoreCoreIndex]; + end else begin + if (i >= NumDMRCores) begin : independent_stragglers + assign sys_nominal_outputs_o[i] = core_nominal_outputs_i[DMRFixed ? i-NumDMRGroups+NumDMRCores : i]; + assign sys_bus_outputs_o [i] = core_bus_outputs_i [DMRFixed ? i-NumDMRGroups+NumDMRCores : i]; + assign sys_axi_outputs_o [i] = core_axi_outputs_i [DMRFixed ? i-NumDMRGroups+NumDMRCores : i]; + end else begin + always_comb begin + if (core_in_dmr[i]) begin : dmr_mode + if (dmr_core_id(dmr_group_id(i), 0) == i) begin : is_dmr_main_core + sys_nominal_outputs_o[i] = dmr_nominal_outputs[CoreCoreIndex]; + sys_bus_outputs_o [i] = dmr_bus_outputs [CoreCoreIndex]; + sys_axi_outputs_o [i] = dmr_axi_outputs [CoreCoreIndex]; + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o [i] = '0; + sys_axi_outputs_o [i] = '0; + sys_axi_outputs_o[i].r_ready = 1'b1; + sys_axi_outputs_o[i].b_ready = 1'b1; + end + end else begin : independent_mode + sys_nominal_outputs_o[i] = core_nominal_outputs_i[i]; + sys_bus_outputs_o [i] = core_bus_outputs_i [i]; + sys_axi_outputs_o [i] = core_axi_outputs_i [i]; + end + end + end + end + end + + end else begin : gen_no_redundancy + /***************** + *** none *** + *****************/ + // Direct assignment, disable all + assign core_setback_o = '0; + assign core_bootaddress_o = sys_bootaddress_i; + assign core_inputs_o = sys_inputs_i; + assign sys_nominal_outputs_o = core_nominal_outputs_i; + assign sys_bus_outputs_o = core_bus_outputs_i; + assign sys_axi_outputs_o = core_axi_outputs_i; + end + +endmodule diff --git a/rtl/HMR/hmr_v1.h b/rtl/HMR/hmr_v1.h new file mode 100644 index 00000000..7a4e0bfa --- /dev/null +++ b/rtl/HMR/hmr_v1.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 ETH Zurich and University of Bologna + * + * 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. + */ + +#ifndef __ARCHI_HMR_HMR_V1_H__ +#define __ARCHI_HMR_HMR_V1_H__ + +#define HMR_IN_INTERLEAVED 1 + +#define HMR_TOP_OFFSET 0x000 +#define HMR_CORE_OFFSET 0x100 +#define HMR_DMR_OFFSET 0x200 +#define HMR_TMR_OFFSET 0x300 + +#define HMR_CORE_INCREMENT 0x010 +#define HMR_CORE_SLL 0x004 +#define HMR_DMR_INCREMENT 0x010 +#define HMR_DMR_SLL 0x004 +#define HMR_TMR_INCREMENT 0x010 +#define HMR_TMR_SLL 0x004 + +// Generated register defines for HMR_registers + +#ifndef _HMR_REGISTERS_REG_DEFS_ +#define _HMR_REGISTERS_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +#define HMR_REGISTERS_PARAM_NUM_CORES 12 + +#define HMR_REGISTERS_PARAM_NUM_D_M_R_GROUPS 6 + +#define HMR_REGISTERS_PARAM_NUM_T_M_R_GROUPS 4 + +// Register width +#define HMR_REGISTERS_PARAM_REG_WIDTH 32 + +// Available Configurations from implemented hardware. +#define HMR_REGISTERS_AVAIL_CONFIG_REG_OFFSET 0x0 +#define HMR_REGISTERS_AVAIL_CONFIG_INDEPENDENT_BIT 0 +#define HMR_REGISTERS_AVAIL_CONFIG_DUAL_BIT 1 +#define HMR_REGISTERS_AVAIL_CONFIG_TRIPLE_BIT 2 +#define HMR_REGISTERS_AVAIL_CONFIG_RAPID_RECOVERY_BIT 8 + +// Enabled cores, based on the configuration. Can be used for barriers. +#define HMR_REGISTERS_CORES_EN_REG_OFFSET 0x4 +#define HMR_REGISTERS_CORES_EN_CORES_EN_MASK 0xfff +#define HMR_REGISTERS_CORES_EN_CORES_EN_OFFSET 0 +#define HMR_REGISTERS_CORES_EN_CORES_EN_FIELD \ + ((bitfield_field32_t) { .mask = HMR_REGISTERS_CORES_EN_CORES_EN_MASK, .index = HMR_REGISTERS_CORES_EN_CORES_EN_OFFSET }) + +// DMR configuration enable, on bit per DMR group. +#define HMR_REGISTERS_DMR_ENABLE_REG_OFFSET 0x8 +#define HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_MASK 0x3f +#define HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_OFFSET 0 +#define HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_FIELD \ + ((bitfield_field32_t) { .mask = HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_MASK, .index = HMR_REGISTERS_DMR_ENABLE_DMR_ENABLE_OFFSET }) + +// TMR configuration enable, one bit per TMR group. +#define HMR_REGISTERS_TMR_ENABLE_REG_OFFSET 0xc +#define HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_MASK 0xf +#define HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_OFFSET 0 +#define HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_FIELD \ + ((bitfield_field32_t) { .mask = HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_MASK, .index = HMR_REGISTERS_TMR_ENABLE_TMR_ENABLE_OFFSET }) + +// DMR configuration bits. +#define HMR_REGISTERS_DMR_CONFIG_REG_OFFSET 0x10 +#define HMR_REGISTERS_DMR_CONFIG_RAPID_RECOVERY_BIT 0 +#define HMR_REGISTERS_DMR_CONFIG_FORCE_RECOVERY_BIT 1 + +// TMR configuration bits. +#define HMR_REGISTERS_TMR_CONFIG_REG_OFFSET 0x14 +#define HMR_REGISTERS_TMR_CONFIG_DELAY_RESYNCH_BIT 0 +#define HMR_REGISTERS_TMR_CONFIG_SETBACK_BIT 1 +#define HMR_REGISTERS_TMR_CONFIG_RELOAD_SETBACK_BIT 2 +#define HMR_REGISTERS_TMR_CONFIG_RAPID_RECOVERY_BIT 3 +#define HMR_REGISTERS_TMR_CONFIG_FORCE_RESYNCH_BIT 4 + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _HMR_REGISTERS_REG_DEFS_ +// End generated register defines for HMR_registers + +// Generated register defines for HMR_core_regs + +#ifndef _HMR_CORE_REGS_REG_DEFS_ +#define _HMR_CORE_REGS_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +// Register width +#define HMR_CORE_REGS_PARAM_REG_WIDTH 32 + +// Value to determine wich redundancy mode the core with that ID is in. +#define HMR_CORE_REGS_CURRENT_MODE_REG_OFFSET 0x0 +#define HMR_CORE_REGS_CURRENT_MODE_INDEPENDENT_BIT 0 +#define HMR_CORE_REGS_CURRENT_MODE_DUAL_BIT 1 +#define HMR_CORE_REGS_CURRENT_MODE_TRIPLE_BIT 2 + +// Mismatches of the core +#define HMR_CORE_REGS_MISMATCHES_REG_OFFSET 0x4 + +// Stack Pointer storage register +#define HMR_CORE_REGS_SP_STORE_REG_OFFSET 0x8 + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _HMR_CORE_REGS_REG_DEFS_ +// End generated register defines for HMR_core_regs + +// Generated register defines for HMR_dmr_regs + +#ifndef _HMR_DMR_REGS_REG_DEFS_ +#define _HMR_DMR_REGS_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +// Register width +#define HMR_DMR_REGS_PARAM_REG_WIDTH 32 + +// DMR configuration enable. +#define HMR_DMR_REGS_DMR_ENABLE_REG_OFFSET 0x0 +#define HMR_DMR_REGS_DMR_ENABLE_DMR_ENABLE_BIT 0 + +// DMR configuration bits. +#define HMR_DMR_REGS_DMR_CONFIG_REG_OFFSET 0x4 +#define HMR_DMR_REGS_DMR_CONFIG_RAPID_RECOVERY_BIT 0 +#define HMR_DMR_REGS_DMR_CONFIG_FORCE_RECOVERY_BIT 1 + +// Address for the last checkpoint. +#define HMR_DMR_REGS_CHECKPOINT_ADDR_REG_OFFSET 0x8 + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _HMR_DMR_REGS_REG_DEFS_ +// End generated register defines for HMR_dmr_regs + +// Generated register defines for HMR_tmr_regs + +#ifndef _HMR_TMR_REGS_REG_DEFS_ +#define _HMR_TMR_REGS_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +// Register width +#define HMR_TMR_REGS_PARAM_REG_WIDTH 32 + +// TMR configuration enable. +#define HMR_TMR_REGS_TMR_ENABLE_REG_OFFSET 0x0 +#define HMR_TMR_REGS_TMR_ENABLE_TMR_ENABLE_BIT 0 + +// TMR configuration bits. +#define HMR_TMR_REGS_TMR_CONFIG_REG_OFFSET 0x4 +#define HMR_TMR_REGS_TMR_CONFIG_DELAY_RESYNCH_BIT 0 +#define HMR_TMR_REGS_TMR_CONFIG_SETBACK_BIT 1 +#define HMR_TMR_REGS_TMR_CONFIG_RELOAD_SETBACK_BIT 2 +#define HMR_TMR_REGS_TMR_CONFIG_RAPID_RECOVERY_BIT 3 +#define HMR_TMR_REGS_TMR_CONFIG_FORCE_RESYNCH_BIT 4 + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _HMR_TMR_REGS_REG_DEFS_ +// End generated register defines for HMR_tmr_regs + + +#endif // __ARCHI_HMR_HMR_V1_H__ diff --git a/rtl/HMR/rapid_recovery_pkg.sv b/rtl/HMR/rapid_recovery_pkg.sv new file mode 100644 index 00000000..6a1e1044 --- /dev/null +++ b/rtl/HMR/rapid_recovery_pkg.sv @@ -0,0 +1,86 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Recovery Region Package + * + */ + +package rapid_recovery_pkg; + +localparam int unsigned DataWidth = 32; +localparam int unsigned ProtectedWidth = 39; +localparam int unsigned RegfileAddr = 6; +localparam int unsigned RecoveryStateBits = 3; +/* CSRs */ +localparam int unsigned MstatusWidth = 7; +localparam int unsigned MtvecWidth = 24; +localparam int unsigned McauseWidth = 6; + +typedef struct packed { + // Write Port A + logic we_a; + logic [RegfileAddr-1:0] waddr_a; + logic [ DataWidth-1:0] wdata_a; + // Write Port B + logic we_b; + logic [RegfileAddr-1:0] waddr_b; + logic [ DataWidth-1:0] wdata_b; +} regfile_write_t; + +typedef struct packed { + logic [RegfileAddr-1:0] raddr_a; + logic [RegfileAddr-1:0] raddr_b; +} regfile_raddr_t; + +typedef struct packed { + logic [DataWidth-1:0] rdata_a; + logic [DataWidth-1:0] rdata_b; +} regfile_rdata_t; + +typedef struct packed { + logic [MstatusWidth-1:0] csr_mstatus; + logic [ DataWidth-1:0] csr_mie; + logic [ MtvecWidth-1:0] csr_mtvec; + logic [ DataWidth-1:0] csr_mscratch; + logic [ DataWidth-1:0] csr_mip; + logic [ DataWidth-1:0] csr_mepc; + logic [ McauseWidth-1:0] csr_mcause; +} csrs_intf_t; + +typedef struct packed { + logic [ProtectedWidth-1:0] csr_mstatus; + logic [ProtectedWidth-1:0] csr_mie; + logic [ProtectedWidth-1:0] csr_mtvec; + logic [ProtectedWidth-1:0] csr_mscratch; + logic [ProtectedWidth-1:0] csr_mip; + logic [ProtectedWidth-1:0] csr_mepc; + logic [ProtectedWidth-1:0] csr_mcause; +} ecc_csrs_intf_t; + +typedef struct packed { + logic [DataWidth-1:0] program_counter_if; + logic [DataWidth-1:0] program_counter; + logic is_branch; + logic [DataWidth-1:0] branch_addr; +} pc_intf_t; + +typedef struct packed { + logic instr_lock; + logic pc_recovery_en; + logic rf_recovery_en; + logic debug_req; + logic debug_resume; + rapid_recovery_pkg::regfile_write_t rf_recovery_wdata; + rapid_recovery_pkg::regfile_rdata_t rf_recovery_rdata; + rapid_recovery_pkg::csrs_intf_t csr_recovery; + rapid_recovery_pkg::pc_intf_t pc_recovery; +} rapid_recovery_t; + +endpackage diff --git a/rtl/HMR/rapid_recovery_unit.sv b/rtl/HMR/rapid_recovery_unit.sv new file mode 100644 index 00000000..77bcf453 --- /dev/null +++ b/rtl/HMR/rapid_recovery_unit.sv @@ -0,0 +1,165 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Rapid Recovery unit with backup status registers + +module rapid_recovery_unit + import rapid_recovery_pkg::*; +#( + parameter int unsigned RfAddrWidth = 5, + parameter int unsigned DataWidth = 32, + parameter int unsigned EccEnabled = 1, + parameter type regfile_write_t = logic, + parameter type regfile_raddr_t = logic, + parameter type regfile_rdata_t = logic, + parameter type csr_intf_t = logic, + parameter type pc_intf_t = logic +)( + input logic clk_i, + input logic rst_ni, + /* Signals that cores are not grouped */ + input logic core_in_independent_i, + /* Recovery Register File interface */ + input regfile_write_t regfile_write_i, + /* Recovery Control and Status Registers interface */ + input csr_intf_t backup_csr_i, + output csr_intf_t recovery_csr_o, + /* Recovery Program Counter interface */ + input pc_intf_t backup_pc_i, + output pc_intf_t recovery_pc_o, + /* Rapid Recovery Controller interface */ + /* backup_enable_i: result of comparison between backup_enable_o + (generated by the rapid_recovery_ctrl) and + the DMR/TMR selection mode */ + input logic backup_enable_i, + /* start_recovery_i: software-requested recovery */ + input logic start_recovery_i, + /* backup_enable_o: generated by rapid_recovery_ctrl, asserts that + the core can do the backup of its state */ + output logic backup_enable_o, + /* recovery_finished_o: recovery routine completion */ + output logic recovery_finished_o, + /* setback_o: synchronous clear for the core */ + output logic setback_o, + /* instr_lock_o: blocks the requests toward the instruction cache + during the recovery routine */ + output logic instr_lock_o, + /* enable_pc_recovery_o: allows the program counter to be reloaded + into the core */ + output logic enable_pc_recovery_o, + /* enable_rf_recovery_o: allows the register file to be reloaded + into the core */ + output logic enable_rf_recovery_o, + /* regfile_recovery_wdata_o: used by the address generator in the + rapid_recovery_ctrl to propagate the RF + addresses to the core during the recovery + routine */ + output regfile_write_t regfile_recovery_wdata_o, // To cores RF interface + /* regfile_recovery_rdata_o: propagates the content from the backup RF to + the core RF during the recovery routine */ + output regfile_rdata_t regfile_recovery_rdata_o, + /* debug_halt_i: signals that the cores in recovery are halted */ + input logic debug_halt_i, + /* debug_req_o: sends the cores in debug mode during the recovery routine */ + output logic debug_req_o, + /* debug_resume_o: resumes the cores in recovery from the debug mode */ + output logic debug_resume_o +); + +logic csr_renable; + +hmr_rapid_recovery_ctrl #( + .RFAddrWidth ( RfAddrWidth ), + .regfile_write_t ( regfile_write_t ) +) i_rapid_recovery_ctrl ( + .clk_i, + .rst_ni, + .start_recovery_i, + .recovery_finished_o, + .setback_o, + .instr_lock_o, + .debug_req_o, + .debug_halt_i, + .debug_resume_o, + .recovery_regfile_waddr_o ( regfile_recovery_wdata_o ), + .backup_enable_o ( backup_enable_o ), + .recover_csr_enable_o ( csr_renable ), + .recover_pc_enable_o ( enable_pc_recovery_o ), + .recover_rf_enable_o ( enable_rf_recovery_o ) +); + +recovery_csr #( + .ECCEnabled ( EccEnabled ), + .csr_intf_t ( csr_intf_t ) +) i_recovery_csr ( + .clk_i, + .rst_ni, + .read_enable_i ( csr_renable ), + .write_enable_i ( backup_enable_i ), + .backup_csr_i ( backup_csr_i ), + .recovery_csr_o ( recovery_csr_o ) +); + +/* When cores are not grouped, we store as a recovery program counter the instruction + that just eneterd the fetch stage. The reason is that if we switch from independent + to redundant, we do it after a barrier, and if we restart from a barrier instruction + without setting it up properly first, we never continue the execution. */ +logic [DataWidth-1:0] backup_program_counter; +assign backup_program_counter = core_in_independent_i ? backup_pc_i.program_counter_if + : backup_pc_i.program_counter; +recovery_pc #( + .ECCEnabled ( EccEnabled ), + .pc_intf_t ( pc_intf_t ) +) i_recovery_pc ( + // Control Ports + .clk_i, + .rst_ni, + .clear_i ( '0 ), + .read_enable_i ( enable_pc_recovery_o ), + .write_enable_i ( backup_enable_i ), + // Backup Ports + .backup_program_counter_i ( backup_program_counter ), + .backup_branch_i ( backup_pc_i.is_branch ), + .backup_branch_addr_i ( backup_pc_i.branch_addr ), + // Recovery Pors + .recovery_program_counter_o ( recovery_pc_o.program_counter ), + .recovery_branch_o ( recovery_pc_o.is_branch ), + .recovery_branch_addr_o ( recovery_pc_o.branch_addr ) +); + +recovery_rf #( + .ECCEnabled ( EccEnabled ), + .ADDR_WIDTH ( RfAddrWidth ), + .regfile_write_t ( regfile_write_t ), + .regfile_raddr_t ( regfile_raddr_t ), + .regfile_rdata_t ( regfile_rdata_t ) +) i_recovery_rf ( + .clk_i, + .rst_ni, + //Read port A + .raddr_a_i ( regfile_recovery_wdata_o.waddr_a ), + .rdata_a_o ( regfile_recovery_rdata_o.rdata_a ), + //Read port B + .raddr_b_i ( regfile_recovery_wdata_o.waddr_b ), + .rdata_b_o ( regfile_recovery_rdata_o.rdata_b ), + //Read port C + .raddr_c_i ( '0 ), + .rdata_c_o ( ), + // Write Port A + .waddr_a_i ( regfile_write_i.waddr_a ), + .wdata_a_i ( regfile_write_i.wdata_a ), + .we_a_i ( regfile_write_i.we_a & backup_enable_i ), + // Write Port B + .waddr_b_i ( regfile_write_i.waddr_b ), + .wdata_b_i ( regfile_write_i.wdata_b ), + .we_b_i ( regfile_write_i.we_b & backup_enable_i ) +); + +endmodule: rapid_recovery_unit diff --git a/rtl/HMR/recovery_csr.sv b/rtl/HMR/recovery_csr.sv new file mode 100644 index 00000000..6cf78126 --- /dev/null +++ b/rtl/HMR/recovery_csr.sv @@ -0,0 +1,152 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Recovery Control Status Registers + * ECC-protected register that stores the CSRs values from the cores + * + */ + +module recovery_csr + import rapid_recovery_pkg::*; +#( + parameter int unsigned ECCEnabled = 0, + parameter int unsigned NonProtectedWidth = 32, + parameter int unsigned ProtectedWidth = 39, + parameter type csr_intf_t = logic, + localparam int unsigned DataWidth = ( ECCEnabled ) ? ProtectedWidth + : NonProtectedWidth +) ( + input logic clk_i , + input logic rst_ni, + input logic read_enable_i, + input logic write_enable_i, + input csrs_intf_t backup_csr_i, + output csrs_intf_t recovery_csr_o +); + +csrs_intf_t csr_inp, + csr_out; + +logic [31:0] csr_dec_mstatus, + csr_dec_mie, + csr_dec_mtvec, + csr_dec_mscratch, + csr_dec_mip, + csr_dec_mepc, + csr_dec_mcause; + +assign csr_inp = backup_csr_i; +assign recovery_csr_o = (read_enable_i) ? csr_out : '0; + +if (ECCEnabled) begin : gen_ecc_csrs + + ecc_csrs_intf_t csr_d, csr_q; + + prim_secded_39_32_enc csr_mstatus_ecc_encoder ( + .in ( {25'd0,csr_inp.csr_mstatus} ), // mtvec is a 7-bit value + .out ( csr_d.csr_mstatus ) + ); + prim_secded_39_32_enc csr_mie_ecc_encoder ( + .in ( csr_inp.csr_mie ), + .out ( csr_d.csr_mie ) + ); + prim_secded_39_32_enc csr_mtvec_ecc_encoder ( + .in ( {8'd0, csr_inp.csr_mtvec} ), // mtvec is a 24-bit value + .out ( csr_d.csr_mtvec ) + ); + prim_secded_39_32_enc csr_mscratch_ecc_encoder ( + .in ( csr_inp.csr_mscratch ), + .out ( csr_d.csr_mscratch ) + ); + prim_secded_39_32_enc csr_mip_ecc_encoder ( + .in ( csr_inp.csr_mip ), + .out ( csr_d.csr_mip ) + ); + prim_secded_39_32_enc csr_mepc_ecc_encoder ( + .in ( csr_inp.csr_mepc), + .out ( csr_d.csr_mepc ) + ); + prim_secded_39_32_enc csr_mcause_ecc_encoder ( + .in ( {26'd0, csr_inp.csr_mcause} ), // mcause is a 6-bit value + .out ( csr_d.csr_mcause ) + ); + + always_ff @(posedge clk_i, negedge rst_ni) begin : ecc_csr_backup + if (~rst_ni) + csr_q <= '0; + else if (write_enable_i) + csr_q <= csr_d; + end + + prim_secded_39_32_dec csr_mstatus_ecc_decoder ( + .in ( csr_q.csr_mstatus ), + .d_o ( csr_dec_mstatus ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mie_ecc_decoder ( + .in ( csr_q.csr_mie ), + .d_o ( csr_dec_mie ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mtvec_ecc_decoder ( + .in ( csr_q.csr_mtvec ), + .d_o ( csr_dec_mtvec ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mscratch_ecc_decoder ( + .in ( csr_q.csr_mscratch ), + .d_o ( csr_dec_mscratch ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mip_ecc_decoder ( + .in ( csr_q.csr_mip ), + .d_o ( csr_dec_mip ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mepc_ecc_decoder ( + .in ( csr_q.csr_mepc ), + .d_o ( csr_dec_mepc ), + .syndrome_o ( ), + .err_o ( ) + ); + prim_secded_39_32_dec csr_mcause_ecc_decoder ( + .in ( csr_q.csr_mcause ), + .d_o ( csr_dec_mcause ), + .syndrome_o ( ), + .err_o ( ) + ); + assign csr_out.csr_mstatus = csr_dec_mstatus[6:0]; + assign csr_out.csr_mie = csr_dec_mie; + assign csr_out.csr_mtvec = csr_dec_mtvec[23:0]; + assign csr_out.csr_mscratch = csr_dec_mscratch; + assign csr_out.csr_mip = csr_dec_mip; + assign csr_out.csr_mepc = csr_dec_mepc; + assign csr_out.csr_mcause = csr_dec_mcause[5:0]; +end else begin : gen_no_ecc_csrs + csrs_intf_t csr_d, csr_q; + + assign csr_d = csr_inp; + + always_ff @(posedge clk_i, negedge rst_ni) begin : csr_backup + if (~rst_ni) + csr_q <= '0; + else if (write_enable_i) + csr_q <= csr_d; + end + assign csr_out = csr_q; + +end + +endmodule : recovery_csr diff --git a/rtl/HMR/recovery_pc.sv b/rtl/HMR/recovery_pc.sv new file mode 100644 index 00000000..51f62347 --- /dev/null +++ b/rtl/HMR/recovery_pc.sv @@ -0,0 +1,191 @@ +/* Copyright 2020 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Recovery Program Counter + * ECC-protected register that stores the Program Counter value from the cores + * + */ + +module recovery_pc #( + parameter int unsigned ECCEnabled = 0, + parameter int unsigned NonProtectedWidth = 32, + parameter int unsigned ProtectedWidth = 39, + parameter type pc_intf_t = logic, + localparam int unsigned DataWidth = ( ECCEnabled ) ? ProtectedWidth + : NonProtectedWidth +) ( + // Control Ports + input logic clk_i, + input logic rst_ni, + input logic clear_i, + input logic read_enable_i, + input logic write_enable_i, + // Backup Ports + input logic [NonProtectedWidth-1:0] backup_program_counter_i, + input logic backup_branch_i, + input logic [NonProtectedWidth-1:0] backup_branch_addr_i, + // Recovery Pors + output logic [NonProtectedWidth-1:0] recovery_program_counter_o, + output logic recovery_branch_o, + output logic [NonProtectedWidth-1:0] recovery_branch_addr_o +); + +logic branch_q; + +logic [DataWidth-1:0] pc_d, + pc_q, + branch_addr_d, + branch_addr_q; +logic [NonProtectedWidth-1:0] pc_out, + branch_addr_out; + +generate + if (ECCEnabled) begin : gen_ecc_region + /************************** + * Program Counter Backup * + **************************/ + prim_secded_39_32_enc pc_ecc_encoder ( + .in ( backup_program_counter_i ), + .out ( pc_d ) + ); + + always_ff @(posedge clk_i, negedge rst_ni) begin : pc_value_sampler + if (~rst_ni) + pc_q <= '0; + else begin + if (clear_i) + pc_q <= '0; + else if (write_enable_i && pc_d != '0) + pc_q <= pc_d; + else + pc_q <= pc_q; + end + end + + prim_secded_39_32_dec pc_ecc_decoder ( + .in ( pc_q ), + .d_o ( pc_out ), + .syndrome_o ( ), + .err_o ( ) + ); + + /********************** + * Branch Addr Backup * + **********************/ + prim_secded_39_32_enc branch_addr_ecc_encoder ( + .in ( backup_branch_addr_i ), + .out ( branch_addr_d ) + ); + + always_ff @(posedge clk_i, negedge rst_ni) begin : branch_addr_sampler + if (~rst_ni) + branch_addr_q <= '0; + else begin + if (clear_i) + branch_addr_q <= '0; + else if (backup_branch_i && write_enable_i) + branch_addr_q <= branch_addr_d; + else + branch_addr_q <= branch_addr_q; + end + end + + prim_secded_39_32_dec branch_addr_ecc_decoder ( + .in ( branch_addr_q ), + .d_o ( branch_addr_out ), + .syndrome_o ( ), + .err_o ( ) + ); + + /************************ + * Branch Signal Backup * + ************************/ + always_ff @(posedge clk_i, negedge rst_ni) begin : branch_sampler + if (~rst_ni) + branch_q <= '0; + else begin + if (clear_i) + branch_q <= '0; + else if (write_enable_i) + branch_q <= backup_branch_i; + else + branch_q <= branch_q; + end + end + + /***************** + * Output Assign * + *****************/ + assign recovery_program_counter_o = (read_enable_i) ? pc_out : '0; + assign recovery_branch_addr_o = (read_enable_i && branch_q) ? branch_addr_out : '0; + assign recovery_branch_o = (read_enable_i) ? branch_q : '0; + end else begin : gen_no_ecc_region + /************************** + * Program Counter Backup * + **************************/ + assign pc_d = backup_program_counter_i; + always_ff @(posedge clk_i, negedge rst_ni) begin : pc_value_sampler + if (~rst_ni) + pc_q <= '0; + else begin + if (clear_i) + pc_q <= '0; + else if (write_enable_i) + pc_q <= pc_d; + else + pc_q <= pc_d; + end + end + assign pc_out = pc_q; + + /********************** + * Branch Addr Backup * + **********************/ + assign branch_addr_d = backup_branch_addr_i; + always_ff @(posedge clk_i, negedge rst_ni) begin : branch_addr_sampler + if (~rst_ni) + branch_addr_q <= '0; + else begin + if (clear_i) + branch_addr_q <= '0; + else if (backup_branch_i && write_enable_i) + branch_addr_q <= branch_addr_d; + else + branch_addr_q <= branch_addr_q; + end + end + assign branch_addr_out = branch_addr_q; + + /************************ + * Branch Signal Backup * + ************************/ + always_ff @(posedge clk_i, negedge rst_ni) begin : branch_sampler + if (~rst_ni) + branch_q <= '0; + else begin + if (clear_i) + branch_q <= '0; + else if (write_enable_i) + branch_q <= backup_branch_i; + else + branch_q <= branch_q; + end + end + + /***************** + * Output Assign * + *****************/ + assign recovery_program_counter_o = (read_enable_i) ? pc_out : '0; + assign recovery_branch_addr_o = (read_enable_i) ? branch_addr_out : '0; + assign recovery_branch_o = (read_enable_i) ? branch_q : '0; + end +endgenerate + +endmodule : recovery_pc diff --git a/rtl/HMR/recovery_rf.sv b/rtl/HMR/recovery_rf.sv new file mode 100644 index 00000000..b546087c --- /dev/null +++ b/rtl/HMR/recovery_rf.sv @@ -0,0 +1,216 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// Engineer: Francesco Conti - f.conti@unibo.it // +// // +// Additional contributions by: // +// Michael Gautschi - gautschi@iis.ee.ethz.ch // +// Davide Schiavone - pschiavo@iis.ee.ethz.ch // +// // +// Design Name: RISC-V register file // +// Project Name: RI5CY // +// Language: SystemVerilog // +// // +// Description: Register file with 31x 32 bit wide registers. Register 0 // +// is fixed to 0. This register file is based on flip-flops. // +// Also supports the fp-register file now if FPU=1 // +// If PULP_ZFINX is 1, floating point operations take values // +// from the X register file // +// // +//////////////////////////////////////////////////////////////////////////////// + +module recovery_rf #( + parameter int unsigned ECCEnabled = 0, + parameter int unsigned ADDR_WIDTH = 5, + parameter int unsigned NonProtectedWidth = 32, + parameter int unsigned ProtectedWidth = 39, + parameter int unsigned FPU = 0, + parameter int unsigned PULP_ZFINX = 0, + parameter type regfile_write_t = logic, + parameter type regfile_raddr_t = logic, + parameter type regfile_rdata_t = logic, + localparam int unsigned DataWidth = ( ECCEnabled ) ? ProtectedWidth + : NonProtectedWidth +)( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + //Read port R1 + input logic [ADDR_WIDTH-1:0] raddr_a_i, + output logic [NonProtectedWidth-1:0] rdata_a_o, + + //Read port R2 + input logic [ADDR_WIDTH-1:0] raddr_b_i, + output logic [NonProtectedWidth-1:0] rdata_b_o, + + //Read port R3 + input logic [ADDR_WIDTH-1:0] raddr_c_i, + output logic [NonProtectedWidth:0] rdata_c_o, + + // Write port W1 + input logic [ADDR_WIDTH-1:0] waddr_a_i, + input logic [NonProtectedWidth-1:0] wdata_a_i, + input logic we_a_i, + + // Write port W2 + input logic [ADDR_WIDTH-1:0] waddr_b_i, + input logic [NonProtectedWidth-1:0] wdata_b_i, + input logic we_b_i +); + + // number of integer registers + localparam NUM_WORDS = 2 ** (ADDR_WIDTH - 1); + // number of floating point registers + localparam NUM_FP_WORDS = 2 ** (ADDR_WIDTH - 1); + localparam NUM_TOT_WORDS = FPU ? (PULP_ZFINX ? NUM_WORDS : NUM_WORDS + NUM_FP_WORDS) : NUM_WORDS; + + // integer register file + logic [NUM_WORDS-1:0][NonProtectedWidth-1:0] mem; + logic [NUM_WORDS-1:0][ DataWidth-1:0] ecc_mem; + // fp register file + logic [NUM_FP_WORDS-1:0][NonProtectedWidth-1:0] mem_fp; + logic [NUM_FP_WORDS-1:0][ DataWidth-1:0] ecc_mem_fp; + + logic [DataWidth-1:0] wdata_a, + wdata_b; + + // masked write addresses + logic [ADDR_WIDTH-1:0] waddr_a; + logic [ADDR_WIDTH-1:0] waddr_b; + + // write enable signals for all registers + logic [NUM_TOT_WORDS-1:0] we_a_dec; + logic [NUM_TOT_WORDS-1:0] we_b_dec; + + generate + if (ECCEnabled) begin : gen_ecc_region + + prim_secded_39_32_enc a_port_ecc_encoder ( + .in ( wdata_a_i ), + .out ( wdata_a ) + ); + + prim_secded_39_32_enc b_port_ecc_encoder ( + .in ( wdata_b_i ), + .out ( wdata_b ) + ); + + for (genvar index = 0; index < NUM_WORDS; index++) begin + prim_secded_39_32_dec internal_memory_decoder ( + .in ( ecc_mem [index] ), + .d_o ( mem [index] ), + .syndrome_o ( ), + .err_o ( ) + ); + end + + if (FPU == 1 && PULP_ZFINX == 0) begin + for (genvar index = 0; index < NUM_FP_WORDS; index++) begin + prim_secded_39_32_dec internal_fp_memory_decoder ( + .in ( ecc_mem_fp [index] ), + .d_o ( mem_fp [index] ), + .syndrome_o ( ), + .err_o ( ) + ); + end + end + end else begin : no_ecc_region + assign wdata_a = wdata_a_i; + assign wdata_b = wdata_b_i; + + for (genvar index = 0; index < NUM_WORDS; index++) + assign mem [index] = ecc_mem [index]; + + for (genvar index = 0; index < NUM_FP_WORDS; index++) + assign mem_fp [index] = ecc_mem_fp [index]; + end + endgenerate + + //----------------------------------------------------------------------------- + //-- READ : Read address decoder RAD + //----------------------------------------------------------------------------- + generate + if (FPU == 1 && PULP_ZFINX == 0) begin : gen_mem_fp_read + assign rdata_a_o = raddr_a_i[5] ? mem_fp[raddr_a_i[4:0]] : mem[raddr_a_i[4:0]]; + assign rdata_b_o = raddr_b_i[5] ? mem_fp[raddr_b_i[4:0]] : mem[raddr_b_i[4:0]]; + assign rdata_c_o = raddr_c_i[5] ? mem_fp[raddr_c_i[4:0]] : mem[raddr_c_i[4:0]]; + end else begin : gen_mem_read + assign rdata_a_o = mem[raddr_a_i[4:0]]; + assign rdata_b_o = mem[raddr_b_i[4:0]]; + assign rdata_c_o = mem[raddr_c_i[4:0]]; + end + endgenerate + + //----------------------------------------------------------------------------- + //-- WRITE : Write Address Decoder (WAD), combinatorial process + //----------------------------------------------------------------------------- + + // Mask top bit of write address to disable fp regfile + assign waddr_a = waddr_a_i; + assign waddr_b = waddr_b_i; + + genvar gidx; + generate + for (gidx = 0; gidx < NUM_TOT_WORDS; gidx++) begin : gen_we_decoder + assign we_a_dec[gidx] = (waddr_a == gidx) ? we_a_i : 1'b0; + assign we_b_dec[gidx] = (waddr_b == gidx) ? we_b_i : 1'b0; + end + endgenerate + + genvar i, l; + generate + + //----------------------------------------------------------------------------- + //-- WRITE : Write operation + //----------------------------------------------------------------------------- + // R0 is nil + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + // R0 is nil + ecc_mem[0] <= 32'b0; + end else begin + // R0 is nil + ecc_mem[0] <= 32'b0; + end + end + + // loop from 1 to NUM_WORDS-1 as R0 is nil + for (i = 1; i < NUM_WORDS; i++) begin : gen_rf + always_ff @(posedge clk_i, negedge rst_ni) begin : register_write_behavioral + if (rst_ni == 1'b0) begin + ecc_mem[i] <= 32'b0; + end else begin + if (we_b_dec[i] == 1'b1) ecc_mem[i] <= wdata_b; + else if (we_a_dec[i] == 1'b1) ecc_mem[i] <= wdata_a; + end + end + end + + if (FPU == 1 && PULP_ZFINX == 0) begin : gen_mem_fp_write + // Floating point registers + for (l = 0; l < NUM_FP_WORDS; l++) begin + always_ff @(posedge clk_i, negedge rst_ni) begin : fp_regs + if (rst_ni == 1'b0) begin + ecc_mem_fp[l] <= '0; + end else begin + if (we_b_dec[l+NUM_WORDS] == 1'b1) ecc_mem_fp[l] <= wdata_b; + else if (we_a_dec[l+NUM_WORDS] == 1'b1) ecc_mem_fp[l] <= wdata_a; + end + end + end + end else begin : gen_no_mem_fp_write + assign ecc_mem_fp = 'b0; + end + + endgenerate + +endmodule diff --git a/rtl/HMR/recovery_rf_latch.sv b/rtl/HMR/recovery_rf_latch.sv new file mode 100644 index 00000000..3bc572a7 --- /dev/null +++ b/rtl/HMR/recovery_rf_latch.sv @@ -0,0 +1,260 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// Engineer: Antonio Pullini - pullinia@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// Sven Stucki - svstucki@student.ethz.ch // +// Michael Gautschi - gautschi@iis.ee.ethz.ch // +// Davide Schiavone - pschiavo@iis.ee.ethz.ch // +// // +// Design Name: RISC-V register file // +// Project Name: RI5CY // +// Language: SystemVerilog // +// // +// Description: Register file with 31x 32 bit wide registers. Register 0 // +// is fixed to 0. This register file is based on latches and // +// is thus smaller than the flip-flop based register file. // +// Also supports the fp-register file now if FPU=1 // +// If PULP_ZFINX is 1, floating point operations take values // +// from the X register file // +// // +//////////////////////////////////////////////////////////////////////////////// + +module recovery_rf #( + parameter int unsigned ECCEnabled = 0, + parameter int unsigned ADDR_WIDTH = 5, + parameter int unsigned NonProtectedWidth = 32, + parameter int unsigned ProtectedWidth = 39, + parameter int unsigned FPU = 0, + parameter int unsigned PULP_ZFINX = 0, + parameter type regfile_write_t = logic, + parameter type regfile_raddr_t = logic, + parameter type regfile_rdata_t = logic, + localparam int unsigned DataWidth = ( ECCEnabled ) ? ProtectedWidth + : NonProtectedWidth +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + input logic test_en_i, + + //Read port R1 + input logic [ADDR_WIDTH-1:0] raddr_a_i, + output logic [NonProtectedWidth-1:0] rdata_a_o, + + //Read port R2 + input logic [ADDR_WIDTH-1:0] raddr_b_i, + output logic [NonProtectedWidth-1:0] rdata_b_o, + + //Read port R3 + input logic [ADDR_WIDTH-1:0] raddr_c_i, + output logic [NonProtectedWidth-1:0] rdata_c_o, + + // Write port W1 + input logic [ADDR_WIDTH-1:0] waddr_a_i, + input logic [NonProtectedWidth-1:0] wdata_a_i, + input logic we_a_i, + + // Write port W2 + input logic [ADDR_WIDTH-1:0] waddr_b_i, + input logic [NonProtectedWidth-1:0] wdata_b_i, + input logic we_b_i +); + + // number of integer registers + localparam NUM_WORDS = 2 ** (ADDR_WIDTH - 1); + // number of floating point registers + localparam NUM_FP_WORDS = 2 ** (ADDR_WIDTH - 1); + localparam NUM_TOT_WORDS = FPU ? (PULP_ZFINX ? NUM_WORDS : NUM_WORDS + NUM_FP_WORDS) : NUM_WORDS; + + // integer register file + logic [NonProtectedWidth-1:0] mem [NUM_WORDS]; + logic [ DataWidth-1:0] ecc_mem [NUM_WORDS]; + logic [NUM_TOT_WORDS-1:1] waddr_onehot_a; + logic [NUM_TOT_WORDS-1:1] waddr_onehot_b , + waddr_onehot_b_q; + logic [NUM_TOT_WORDS-1:1] mem_clocks; + logic [DataWidth-1:0] wdata_a , + wdata_a_q , + wdata_a_ecc; + logic [DataWidth-1:0] wdata_b , + wdata_b_q , + wdata_b_ecc; + + // masked write addresses + logic [ADDR_WIDTH-1:0] waddr_a; + logic [ADDR_WIDTH-1:0] waddr_b; + + logic clk_int; + + // fp register file + logic [NonProtectedWidth-1:0] mem_fp [NUM_FP_WORDS]; + logic [ DataWidth-1:0] ecc_mem_fp [NUM_FP_WORDS]; + + int unsigned i; + int unsigned j; + int unsigned k; + int unsigned l; + + genvar x; + genvar y; + + generate + if (ECCEnabled) begin : gen_ecc_region + + prim_secded_39_32_enc a_port_ecc_encoder ( + .in ( wdata_a_i ), + .out ( wdata_a_ecc) + ); + assign wdata_a = wdata_a_ecc; + + prim_secded_39_32_enc b_port_ecc_encoder ( + .in ( wdata_b_i ), + .out ( wdata_b_ecc) + ); + assign wdata_b = wdata_b_ecc; + + for (genvar index = 0; index < NUM_WORDS; index++) begin + prim_secded_39_32_dec internal_memory_decoder ( + .in ( ecc_mem [index] ), + .d_o ( mem [index] ), + .syndrome_o ( ), + .err_o ( ) + ); + end + + if (FPU == 1 && PULP_ZFINX == 0) begin + for (genvar index = 0; index < NUM_FP_WORDS; index++) begin + prim_secded_39_32_dec internal_fp_memory_decoder ( + .in ( ecc_mem_fp [index] ), + .d_o ( mem_fp [index] ), + .syndrome_o ( ), + .err_o ( ) + ); + end + end + end else begin : no_ecc_region + assign wdata_a = wdata_a_i; + assign wdata_a_ecc = '0; + assign wdata_b = wdata_b_i; + assign wdata_b_ecc = '0; + + for (genvar index = 0; index < NUM_WORDS; index++) + assign mem [index] = ecc_mem [index]; + + for (genvar index = 0; index < NUM_FP_WORDS; index++) + assign mem_fp [index] = ecc_mem_fp [index]; + end + endgenerate + + //----------------------------------------------------------------------------- + //-- READ : Read address decoder RAD + //----------------------------------------------------------------------------- + if (FPU == 1 && PULP_ZFINX == 0) begin + assign rdata_a_o = raddr_a_i[5] ? mem_fp[raddr_a_i[4:0]] : mem[raddr_a_i[4:0]]; + assign rdata_b_o = raddr_b_i[5] ? mem_fp[raddr_b_i[4:0]] : mem[raddr_b_i[4:0]]; + assign rdata_c_o = raddr_c_i[5] ? mem_fp[raddr_c_i[4:0]] : mem[raddr_c_i[4:0]]; + end else begin + assign rdata_a_o = mem[raddr_a_i[4:0]]; + assign rdata_b_o = mem[raddr_b_i[4:0]]; + assign rdata_c_o = mem[raddr_c_i[4:0]]; + end + + //----------------------------------------------------------------------------- + // WRITE : SAMPLE INPUT DATA + //--------------------------------------------------------------------------- + + tc_clk_gating CG_WE_GLOBAL ( + .clk_i ( clk_i ), + .en_i ( we_a_i | we_b_i ), + .test_en_i ( test_en_i ), + .clk_o ( clk_int ) + ); + + // use clk_int here, since otherwise we don't want to write anything anyway + always_ff @(posedge clk_int, negedge rst_ni) begin : sample_waddr + if (~rst_ni) begin + wdata_a_q <= '0; + wdata_b_q <= '0; + waddr_onehot_b_q <= '0; + end else begin + if (we_a_i) wdata_a_q <= wdata_a; + + if (we_b_i) wdata_b_q <= wdata_b; + + waddr_onehot_b_q <= waddr_onehot_b; + end + end + + //----------------------------------------------------------------------------- + //-- WRITE : Write Address Decoder (WAD), combinatorial process + //----------------------------------------------------------------------------- + + assign waddr_a = waddr_a_i; + assign waddr_b = waddr_b_i; + + genvar gidx; + generate + for (gidx = 1; gidx < NUM_TOT_WORDS; gidx++) begin : gen_we_decoder + assign waddr_onehot_a[gidx] = (we_a_i == 1'b1) && (waddr_a == gidx); + assign waddr_onehot_b[gidx] = (we_b_i == 1'b1) && (waddr_b == gidx); + end + endgenerate + + //----------------------------------------------------------------------------- + //-- WRITE : Clock gating (if integrated clock-gating cells are available) + //----------------------------------------------------------------------------- + generate + for (x = 1; x < NUM_TOT_WORDS; x++) begin : gen_clock_gate + tc_clk_gating clock_gate_i ( + .clk_i ( clk_int ), + .en_i ( waddr_onehot_a[x] | waddr_onehot_b[x] ), + .test_en_i ( test_en_i ), + .clk_o ( mem_clocks[x] ) + ); + end + endgenerate + + //----------------------------------------------------------------------------- + //-- WRITE : Write operation + //----------------------------------------------------------------------------- + //-- Generate M = WORDS sequential processes, each of which describes one + //-- word of the memory. The processes are synchronized with the clocks + //-- ClocksxC(i), i = 0, 1, ..., M-1 + //-- Use active low, i.e. transparent on low latches as storage elements + //-- Data is sampled on rising clock edge + + // Integer registers + always_latch begin : latch_wdata + // Note: The assignment has to be done inside this process or Modelsim complains about it + ecc_mem[0] = '0; + + for (k = 1; k < NUM_WORDS; k++) begin : w_WordIter + if (~rst_ni) ecc_mem[k] = '0; + else if (mem_clocks[k] == 1'b1) ecc_mem[k] = waddr_onehot_b_q[k] ? wdata_b_q : wdata_a_q; + end + end + + if (FPU == 1 && PULP_ZFINX == 0) begin + // Floating point registers + always_latch begin : latch_wdata_fp + if (FPU == 1) begin + for (l = 0; l < NUM_FP_WORDS; l++) begin : w_WordIter + if (~rst_ni) ecc_mem_fp[l] = '0; + else if (mem_clocks[l+NUM_WORDS] == 1'b1) + ecc_mem_fp[l] = waddr_onehot_b_q[l+NUM_WORDS] ? wdata_b_q : wdata_a_q; + end + end + end + end +endmodule diff --git a/rtl/HMR/resp_suppress.sv b/rtl/HMR/resp_suppress.sv new file mode 100644 index 00000000..fa1b8f28 --- /dev/null +++ b/rtl/HMR/resp_suppress.sv @@ -0,0 +1,98 @@ +// Copyright 2023 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Suppress the r_valid if set back + +module resp_suppress #( + parameter int unsigned NumOutstanding = 2, + parameter int unsigned AW = 32, + parameter int unsigned DW = 32 +) ( + input logic clk_i, + input logic rst_ni, + + input logic ctrl_setback_i, + + input logic req_i, + output logic gnt_o, + output logic r_valid_o, + input logic we_i, + input logic [AW -1:0] addr_i, + input logic [DW -1:0] data_i, + input logic [DW/8-1:0] be_i, + + output logic req_o, + input logic gnt_i, + input logic r_valid_i, + output logic we_o, + output logic [AW -1:0] addr_o, + output logic [DW -1:0] data_o, + output logic [DW/8-1:0] be_o +); + + logic [$clog2(NumOutstanding)-1:0] outstanding_d, outstanding_q; + logic block_d, block_q; + logic latent_req_d, latent_req_q; + logic we_d, we_q; + logic [AW -1:0] addr_d, addr_q; + logic [DW -1:0] data_d, data_q; + logic [DW/8-1:0] be_d, be_q; + + assign outstanding_d = outstanding_q + (req_o & gnt_i ? 1 : 0) - (r_valid_i ? 1 : 0); + + assign r_valid_o = block_q || latent_req_q ? 1'b0 : r_valid_i; + assign gnt_o = latent_req_q ? 1'b0 : gnt_i; + + assign req_o = req_i | latent_req_q; + assign we_o = latent_req_q ? we_q : we_i; + assign addr_o = latent_req_q ? addr_q : addr_i; + assign data_o = latent_req_q ? data_q : data_i; + assign be_o = latent_req_q ? be_q : be_i; + + always_comb begin + block_d = block_q; + latent_req_d = latent_req_q & ~gnt_i; + we_d = we_q; + addr_d = addr_q; + data_d = data_q; + be_d = be_q; + if (ctrl_setback_i) begin + block_d = 1'b1; + latent_req_d = req_i & ~gnt_i; + we_d = we_i; + addr_d = addr_i; + data_d = data_i; + be_d = be_i; + end else if (outstanding_q == 0 && !latent_req_q) begin + block_d = 1'b0; + end + end + + always_ff @( posedge clk_i or negedge rst_ni ) begin : proc_ff + if (!rst_ni) begin + outstanding_q <= '0; + block_q <= '0; + latent_req_q <= '0; + we_q <= '0; + addr_q <= '0; + data_q <= '0; + be_q <= '0; + end else begin + outstanding_q <= outstanding_d; + block_q <= block_d; + latent_req_q <= latent_req_d; + we_q <= we_d; + addr_q <= addr_d; + data_q <= data_d; + be_q <= be_d; + end + end + +endmodule diff --git a/rtl/ecc_wrap/ecc_manager_reg_pkg.sv b/rtl/ecc_wrap/ecc_manager_reg_pkg.sv index 6c6b88df..cb741759 100644 --- a/rtl/ecc_wrap/ecc_manager_reg_pkg.sv +++ b/rtl/ecc_wrap/ecc_manager_reg_pkg.sv @@ -6,6 +6,9 @@ package ecc_manager_reg_pkg; + // Param list + parameter OffsetStart = 'h100; + // Address widths within the block parameter int BlockAw = 5; diff --git a/rtl/ecc_wrap/ecc_manager_reg_top.sv b/rtl/ecc_wrap/ecc_manager_reg_top.sv index eea5ba55..2caf7204 100644 --- a/rtl/ecc_wrap/ecc_manager_reg_top.sv +++ b/rtl/ecc_wrap/ecc_manager_reg_top.sv @@ -8,12 +8,12 @@ `include "common_cells/assertions.svh" module ecc_manager_reg_top #( - parameter type reg_req_t = logic, - parameter type reg_rsp_t = logic, - parameter int AW = 5 + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + parameter int AW = 5 ) ( - input logic clk_i, - input logic rst_ni, + input clk_i, + input rst_ni, input reg_req_t reg_req_i, output reg_rsp_t reg_rsp_o, // To HW @@ -33,7 +33,7 @@ module ecc_manager_reg_top #( // register signals logic reg_we; logic reg_re; - logic [BlockAw-1:0] reg_addr; + logic [AW-1:0] reg_addr; logic [DW-1:0] reg_wdata; logic [DBW-1:0] reg_be; logic [DW-1:0] reg_rdata; @@ -54,7 +54,7 @@ module ecc_manager_reg_top #( assign reg_we = reg_intf_req.valid & reg_intf_req.write; assign reg_re = reg_intf_req.valid & ~reg_intf_req.write; - assign reg_addr = reg_intf_req.addr[BlockAw-1:0]; + assign reg_addr = reg_intf_req.addr; assign reg_wdata = reg_intf_req.wdata; assign reg_be = reg_intf_req.wstrb; assign reg_intf_rsp.rdata = reg_rdata; @@ -341,55 +341,3 @@ module ecc_manager_reg_top #( `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) endmodule - -module ecc_manager_reg_top_intf -#( - parameter int AW = 5, - localparam int DW = 32 -) ( - input logic clk_i, - input logic rst_ni, - REG_BUS.in regbus_slave, - // To HW - output ecc_manager_reg_pkg::ecc_manager_reg2hw_t reg2hw, // Write - input ecc_manager_reg_pkg::ecc_manager_hw2reg_t hw2reg, // Read - // Config - input devmode_i // If 1, explicit error return for unmapped register access -); - localparam int unsigned STRB_WIDTH = DW/8; - -`include "register_interface/typedef.svh" -`include "register_interface/assign.svh" - - // Define structs for reg_bus - typedef logic [AW-1:0] addr_t; - typedef logic [DW-1:0] data_t; - typedef logic [STRB_WIDTH-1:0] strb_t; - `REG_BUS_TYPEDEF_ALL(reg_bus, addr_t, data_t, strb_t) - - reg_bus_req_t s_reg_req; - reg_bus_rsp_t s_reg_rsp; - - // Assign SV interface to structs - `REG_BUS_ASSIGN_TO_REQ(s_reg_req, regbus_slave) - `REG_BUS_ASSIGN_FROM_RSP(regbus_slave, s_reg_rsp) - - - - ecc_manager_reg_top #( - .reg_req_t(reg_bus_req_t), - .reg_rsp_t(reg_bus_rsp_t), - .AW(AW) - ) i_regs ( - .clk_i, - .rst_ni, - .reg_req_i(s_reg_req), - .reg_rsp_o(s_reg_rsp), - .reg2hw, // Write - .hw2reg, // Read - .devmode_i - ); - -endmodule - - diff --git a/rtl/ecc_wrap/ecc_scrubber_out.sv b/rtl/ecc_wrap/ecc_scrubber_out.sv new file mode 100644 index 00000000..513d378d --- /dev/null +++ b/rtl/ecc_wrap/ecc_scrubber_out.sv @@ -0,0 +1,382 @@ +// Copyright 2021 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Scrubber for ecc +// - iteratively steps through memory bank +// - the out scrubber itself does't correct data, the ecc_sram_wrap itself will write the corrected read data back to sram + +module ecc_scrubber_out #( + parameter type data_be_t = logic, + parameter int unsigned TagSramWidth = 32, + parameter int unsigned TagDataWidth = 32, + parameter int unsigned DataWidth = 128, + parameter int unsigned TagDepth = 256, + parameter int unsigned DataDepth = 2048, + parameter int unsigned TagReadLatency = 1, + parameter int unsigned DataReadLatency= 1, + parameter type error_info_per_way_t = logic, + // Dependency parameters: + parameter int unsigned DataTagDepthFactor = DataDepth/TagDepth // should equal to the block number for each index + + +) ( + input logic clk_i, + input logic rst_ni, + + input logic scrub_trigger_i, // Set to 1'b0 to disable + output logic scrub_tag_bit_corrected_o, + output logic scrub_tag_uncorrectable_o, + output logic scrub_data_bit_corrected_o, + output logic scrub_data_uncorrectable_o, + + output logic tag_single_error_o, + output logic tag_multi_error_o, + output logic data_single_error_o, + output logic data_multi_error_o, + + output logic tag_valid_bit_o, + output logic tag_dirty_bit_o, + + // Input signals from others accessing tag memory bank + input logic tag_intc_req_i, + output logic tag_intc_gnt_o, + input logic tag_intc_we_i, + input logic tag_intc_be_i, + input logic [$clog2(TagDepth)-1:0] tag_intc_add_i, + input logic [ TagSramWidth-1:0] tag_intc_wdata_i, + output logic [ TagSramWidth-1:0] tag_intc_rdata_o, + output logic tag_intc_multi_err_o, + + // Input signals from others accessing data memory bank + input logic data_intc_req_i, + output logic data_intc_gnt_o, + input logic data_intc_we_i, + input data_be_t data_intc_be_i, + input logic [$clog2(DataDepth)-1:0]data_intc_add_i, + input logic [ DataWidth-1:0] data_intc_wdata_i, + output logic [ DataWidth-1:0] data_intc_rdata_o, + output logic data_intc_multi_err_o, + + // Output directly to tag bank + output logic tag_bank_req_o, + input logic tag_bank_gnt_i, + output logic tag_bank_we_o, + output logic tag_bank_be_o, + output logic [$clog2(TagDepth)-1:0] tag_bank_add_o, + output logic [$clog2(TagDepth)-1:0] tag_bank_add_q_o, + output logic [ TagSramWidth-1:0] tag_bank_wdata_o, + input logic [ TagSramWidth-1:0] tag_bank_rdata_i, + + // Output directly to data bank + output logic data_bank_req_o, + input logic data_bank_gnt_i, + output logic data_bank_we_o, + output data_be_t data_bank_be_o, + output logic [$clog2(DataDepth)-1:0]data_bank_add_o, + output logic [$clog2(DataDepth)-1:0]data_bank_add_q_o, + output logic [ DataWidth-1:0] data_bank_wdata_o, + input logic [ DataWidth-1:0] data_bank_rdata_i, + + // Input external ECC result + input error_info_per_way_t ecc_err_i +); + + logic scrub_req; + logic scrub_taginv; + logic scrub_we; + logic [$clog2(DataDepth)-1:0]scrub_add; + // logic [ DataWidth-1:0] scrub_wdata; + logic [ TagSramWidth-1:0] scrub_tag_rdata; + logic [ DataWidth-1:0] scrub_data_rdata; + + typedef enum logic [1:0] {Idle, Read, Check, TagInv} scrub_state_e; + + scrub_state_e state_d, state_q; + + logic [$clog2(DataDepth)-1:0] working_add_d, working_add_q, working_add_plus1; // use data addr as it should be >= tag addr size, because the block number per index + + logic tag_rwdata_en, data_rwdata_en; + logic tag_rdata_en, tag_rdata_en_q; + logic data_rdata_en, data_rdata_en_q; + logic data_wdata_en, data_wdata_en_q; + logic data_en_q; + logic [TagSramWidth-1:0] tag_rdata_q; + logic [DataWidth-1:0] data_rdata_q; + logic tag_intc_multi_err_q; + logic data_intc_multi_err_q; + logic [$clog2(TagDepth)-1:0] tag_bank_add_q; + logic [$clog2(DataDepth)-1:0] data_bank_add_q; + + assign tag_rwdata_en = tag_intc_req_i & tag_bank_gnt_i; + assign data_rwdata_en = data_intc_req_i & data_bank_gnt_i; + + assign tag_rdata_en = tag_rwdata_en & ~tag_intc_we_i; + assign data_rdata_en = data_rwdata_en & ~data_intc_we_i; + assign data_wdata_en = data_rwdata_en & data_intc_we_i; + assign data_en_q = data_rdata_en_q | data_wdata_en_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + tag_rdata_q <= '0; + tag_intc_multi_err_q <= '0; + end else if (tag_rdata_en_q) begin + tag_rdata_q <= tag_bank_rdata_i; + tag_intc_multi_err_q <= ecc_err_i.tag_sram_multi_error; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + data_rdata_q <= '0; + end else if (data_rdata_en_q) begin + data_rdata_q <= data_bank_rdata_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + data_intc_multi_err_q <= '0; + end else if (data_en_q) begin + data_intc_multi_err_q <= ecc_err_i.data_sram_multi_error; + end + end + + + // always_ff @(posedge clk_i or negedge rst_ni) begin + // if(~rst_ni) begin + // tag_rdata_en_q <= 1'b0; + // data_rdata_en_q <= 1'b0; + // data_wdata_en_q <= 1'b0; + // end else begin + // tag_rdata_en_q <= tag_rdata_en; + // data_rdata_en_q <= data_rdata_en; + // data_wdata_en_q <= data_wdata_en; + // end + // end + + shift_reg #( + .dtype ( logic ), + .Depth ( TagReadLatency ) + ) i_shift_reg_tag_rdata_en_q ( + .clk_i, + .rst_ni, + .d_i ( tag_rdata_en ), + .d_o ( tag_rdata_en_q ) + ); + + shift_reg #( + .dtype ( logic ), + .Depth ( DataReadLatency) + ) i_shift_reg_data_rdata_en_q ( + .clk_i, + .rst_ni, + .d_i ( data_rdata_en ), + .d_o ( data_rdata_en_q ) + ); + + shift_reg #( + .dtype ( logic ), + .Depth ( DataReadLatency) + ) i_shift_reg_data_wdata_en_q ( + .clk_i, + .rst_ni, + .d_i ( data_wdata_en ), + .d_o ( data_wdata_en_q ) + ); + + assign scrub_add = working_add_q; + + assign tag_bank_req_o = tag_intc_req_i || scrub_req || scrub_taginv; + assign tag_intc_gnt_o = tag_bank_gnt_i & ~scrub_taginv; // if scrubbr finds a valid clean data with uncorrectable error, need to invalidate the cache line, block tag for a while + assign tag_intc_rdata_o = tag_rdata_en_q ? tag_bank_rdata_i : tag_rdata_q; + assign tag_intc_multi_err_o = tag_rdata_en_q ? ecc_err_i.tag_sram_multi_error : tag_intc_multi_err_q; + + assign data_bank_req_o = data_intc_req_i || scrub_req; + assign data_intc_gnt_o = data_bank_gnt_i; + assign data_intc_rdata_o = data_rdata_en_q ? data_bank_rdata_i : data_rdata_q; + // for data sram write, because the read modify wrtie for partial block write exist, may have error respones the next cycle of the write gnt + assign data_intc_multi_err_o = data_en_q ? ecc_err_i.data_sram_multi_error : data_intc_multi_err_q; + + assign scrub_tag_rdata = tag_bank_rdata_i; + assign scrub_data_rdata = data_bank_rdata_i; + + always_comb begin : proc_tag_bank_assign + // By default, bank is connected to outside + tag_bank_we_o = tag_intc_we_i; + tag_bank_be_o = tag_intc_be_i; + tag_bank_add_o = tag_intc_add_i; + tag_bank_wdata_o = tag_intc_wdata_i; + + // If scrubber active and outside is not, do scrub + if(scrub_taginv || (state_q == TagInv)) begin + tag_bank_we_o = 1'b1; + tag_bank_be_o = '1; + tag_bank_add_o = scrub_add[$clog2(DataDepth)-1:$clog2(DataTagDepthFactor)]; + tag_bank_wdata_o = '0; + end else if ( (state_q == Read || state_q == Check) && (tag_intc_req_i == 1'b0) && (data_intc_req_i == 1'b0)) begin + tag_bank_we_o = 1'b0; + tag_bank_be_o = '0; + tag_bank_add_o = scrub_add[$clog2(DataDepth)-1:$clog2(DataTagDepthFactor)]; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + tag_bank_add_q <= '0; + end else begin + if(tag_rwdata_en) begin + tag_bank_add_q <= tag_bank_add_o; + end + end + end + + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + data_bank_add_q <= '0; + end else begin + if(data_rwdata_en) begin + data_bank_add_q <= data_bank_add_o; + end + end + end + + assign tag_bank_add_q_o = tag_bank_add_q; + assign data_bank_add_q_o = data_bank_add_q; + + always_comb begin : proc_data_bank_assign + // By default, bank is connected to outside + data_bank_we_o = data_intc_we_i; + data_bank_be_o = data_intc_be_i; + data_bank_add_o = data_intc_add_i; + data_bank_wdata_o = data_intc_wdata_i; + + // If scrubber active and outside is not, do scrub + if ( (state_q == Read || state_q == Check) && (tag_intc_req_i == 1'b0) && (data_intc_req_i == 1'b0)) begin + data_bank_we_o = 1'b0; + data_bank_be_o = '0; + data_bank_add_o = scrub_add; + end + end + + always_comb begin : proc_FSM_logic + state_d = state_q; + scrub_req = 1'b0; + scrub_taginv = 1'b0; + working_add_d = working_add_q; + scrub_tag_bit_corrected_o = 1'b0; + scrub_tag_uncorrectable_o = 1'b0; + scrub_data_bit_corrected_o = 1'b0; + scrub_data_uncorrectable_o = 1'b0; + + case (state_q) + Idle: begin + // Switch to read state if triggered to scrub + if (scrub_trigger_i) begin + state_d = Read; + end + end + Read: begin + // Request only active if outside is inactive, and the ecc_sram is ready + if ((tag_intc_req_i == 1'b0) && + (tag_bank_gnt_i == 1'b1) && + (data_intc_req_i == 1'b0) && + (data_bank_gnt_i == 1'b1) + ) begin + // Request read to scrub + scrub_req = 1'b1; + state_d = Check; + end + end + Check: begin + // Find uncorrectable error in a valid clean data line, invalidate the line + if(data_multi_error_o) begin + if(~tag_multi_error_o) begin + if(tag_valid_bit_o & ~tag_dirty_bit_o) begin + scrub_taginv = 1'b1; + if(tag_bank_gnt_i) begin + state_d = Idle; + working_add_d = working_add_plus1; // increment address + end else begin + state_d = TagInv; + working_add_d = working_add_q; // don't increment address + end + end + end + end else begin + // Return to idle state + state_d = Idle; + working_add_d = working_add_plus1; // increment address + end + + scrub_tag_bit_corrected_o = tag_single_error_o; + scrub_tag_uncorrectable_o = tag_multi_error_o; + scrub_data_bit_corrected_o = data_single_error_o; + scrub_data_uncorrectable_o = data_multi_error_o; + end + TagInv: begin + scrub_taginv = 1'b1; + if(tag_bank_gnt_i) begin + state_d = Idle; + working_add_d = working_add_plus1; // increment address + end else begin + state_d = TagInv; + working_add_d = working_add_q; // don't increment address + end + end + default: /* do nothing */; + endcase + end + + // TODO: tag sram has uncorrectable error, interrupt + // TODO: data sram has uncorrectable error, interrupt, has addr info + + + assign tag_single_error_o = ecc_err_i.tag_sram_single_error; + assign tag_multi_error_o = ecc_err_i.tag_sram_multi_error; + assign data_single_error_o = ecc_err_i.data_sram_single_error; + assign data_multi_error_o = ecc_err_i.data_sram_multi_error; + + // typedef to have consistent tag data (that what gets written into the sram) + assign tag_valid_bit_o = tag_bank_rdata_i[TagDataWidth-1]; + assign tag_dirty_bit_o = tag_bank_rdata_i[TagDataWidth-2]; + + assign working_add_plus1 = (working_add_q + 1) % DataDepth; // increment address + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_bank_add + if(!rst_ni) begin + working_add_q <= '0; + end else begin + working_add_q <= working_add_d; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_FSM + if(!rst_ni) begin + state_q <= Idle; + end else begin + state_q <= state_d; + end + end + + +`ifndef TARGET_SYNTHESIS + always @(posedge clk_i) begin + if ((scrub_tag_bit_corrected_o) == 1) + $display("[ECC SCRUB] %t - tag single error detected", $realtime); + if ((scrub_data_bit_corrected_o) == 1) + $display("[ECC SCRUB] %t - data single error detected", $realtime); + if ((scrub_tag_uncorrectable_o) == 1) + $display("[ECC SCRUB] %t - tag multi error detected", $realtime); + if ((scrub_data_uncorrectable_o) == 1) + $display("[ECC SCRUB] %t - data multi error detected", $realtime); + end +`endif + +endmodule diff --git a/rtl/ecc_wrap/ecc_sram.sv b/rtl/ecc_wrap/ecc_sram.sv index 15df7b02..836eca63 100644 --- a/rtl/ecc_wrap/ecc_sram.sv +++ b/rtl/ecc_wrap/ecc_sram.sv @@ -17,6 +17,7 @@ module ecc_sram #( parameter bit InputECC = 0, // 0: no ECC on input // 1: SECDED on input parameter int unsigned NumRMWCuts = 0, // Number of cuts in the read-modify-write path + parameter int unsigned NumOutputCuts = 0, // Number of cuts in the data output path parameter SimInit = "random", // ("zeros", "ones", "random", "none") parameter int unsigned ByteWidth = 8, // Set params @@ -43,19 +44,55 @@ module ecc_sram #( output logic multi_error_o ); + typedef struct packed { + logic [ DataInWidth-1:0] corrected_data; + logic [BankAddrWidth-1:0] bank_addr; + } correct_info_t; + logic [1:0] ecc_error; logic valid_read_d, valid_read_q; + logic valid_load_d, valid_load_q; + correct_info_t corrected_data_raw_d, corrected_data_raw_q; + logic corrected_data_raw_en; + logic corrected_data_raw_valid_d, corrected_data_raw_valid_q; + logic corrected_data_raw_valid_en; + logic corrected_data_raw_valid_set, corrected_data_raw_valid_clr; + logic in_update_corrected_data_mode; + + logic [ DataInWidth-1:0] rdata; + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_valid_read if(~rst_ni) begin valid_read_q <= '0; + valid_load_q <= '0; end else begin valid_read_q <= valid_read_d; + valid_load_q <= valid_load_d; + end + end + + always_ff @(posedge clk_i) begin : proc_corrected_data_update + if(corrected_data_raw_valid_set) begin + corrected_data_raw_q <= corrected_data_raw_d; + end + end + + assign corrected_data_raw_valid_set = corrected_data_raw_en; + assign corrected_data_raw_valid_clr = in_update_corrected_data_mode; + assign corrected_data_raw_valid_en = corrected_data_raw_valid_set | corrected_data_raw_valid_clr; + assign corrected_data_raw_valid_d = corrected_data_raw_valid_set | (~corrected_data_raw_valid_clr); + always_ff @(posedge clk_i or negedge rst_ni) begin : proc_corrected_data_raw_valid + if(~rst_ni) begin + corrected_data_raw_valid_q <= '0; + end else if(corrected_data_raw_valid_en) begin + corrected_data_raw_valid_q <= corrected_data_raw_valid_d; end end assign valid_read_d = req_i && gnt_o && (~we_i || (be_i != {ByteEnWidth{1'b1}})); + assign valid_load_d = req_i && gnt_o && ~we_i; assign single_error_o = ecc_error[0] && valid_read_q; assign multi_error_o = ecc_error[1] && valid_read_q; @@ -74,13 +111,13 @@ module ecc_sram #( logic [ProtectedWidth-1:0] bank_wdata; logic [ProtectedWidth-1:0] bank_rdata; - logic bank_scrub_req; - logic bank_scrub_we; + logic bank_scrub_req, bank_scrub_req_q; + logic bank_scrub_we, bank_scrub_we_q; logic [ BankAddrWidth-1:0] bank_scrub_addr; logic [ProtectedWidth-1:0] bank_scrub_wdata; logic [ProtectedWidth-1:0] bank_scrub_rdata; - typedef enum logic { NORMAL, READ_MODIFY_WRITE } store_state_e; + typedef enum logic[1:0] { NORMAL, READ_MODIFY_WRITE, UPDATE_CORRECTED_DATA } store_state_e; store_state_e store_state_d, store_state_q; typedef logic [cf_math_pkg::idx_width(NumRMWCuts)-1:0] rmw_count_t; @@ -140,11 +177,21 @@ module ecc_sram #( .out ( bank_wdata ) ); - assign rdata_o = loaded; + assign rdata = loaded; assign to_store = store_state_q == NORMAL ? wdata_i : - (be_selector & input_buffer_q) | (~be_selector & loaded); + store_state_q == READ_MODIFY_WRITE ? + (be_selector & input_buffer_q) | (~be_selector & loaded) : + corrected_data_raw_q.corrected_data; + + // for read transaction, update the sram content after a single-error was corrected + assign corrected_data_raw_en = valid_load_q && single_error_o; + assign corrected_data_raw_d = correct_info_t'{ + corrected_data: rdata, + bank_addr : addr_buffer_q + }; + end else begin : gen_ecc_input @@ -152,7 +199,7 @@ module ecc_sram #( logic [UnprotectedWidth-1:0] intermediate_data_ld, intermediate_data_st; assign bank_wdata = store_state_q == NORMAL ? wdata_i : lns_wdata; - assign rdata_o = bank_rdata; + assign rdata = bank_rdata; hsiao_ecc_dec #( .DataWidth (UnprotectedWidth), @@ -194,27 +241,43 @@ module ecc_sram #( be_buffer_d = be_i; bank_req = req_i; rmw_count_d = rmw_count_q; - if (store_state_q == NORMAL) begin - if (req_i & (be_i != {ByteEnWidth{1'b1}}) & we_i) begin - store_state_d = READ_MODIFY_WRITE; - bank_we = 1'b0; - rmw_count_d = rmw_count_t'(NumRMWCuts); + in_update_corrected_data_mode = 1'b0; + case (store_state_q) + NORMAL: begin + if (req_i & (be_i != {ByteEnWidth{1'b1}}) & we_i) begin + store_state_d = READ_MODIFY_WRITE; + bank_we = 1'b0; + rmw_count_d = rmw_count_t'(NumRMWCuts); + end + if(~req_i & corrected_data_raw_valid_q) begin + store_state_d = UPDATE_CORRECTED_DATA; + end end - end else begin - gnt_o = 1'b0; - bank_addr = addr_buffer_q; - bank_we = 1'b1; - input_buffer_d = input_buffer_q; - addr_buffer_d = addr_buffer_q; - be_buffer_d = be_buffer_q; - if (rmw_count_q == '0) begin - bank_req = 1'b1; - end else begin - bank_req = 1'b0; - rmw_count_d = rmw_count_q - 1; - store_state_d = READ_MODIFY_WRITE; + READ_MODIFY_WRITE: begin + gnt_o = 1'b0; + bank_addr = addr_buffer_q; + bank_we = 1'b1; + input_buffer_d = input_buffer_q; + addr_buffer_d = addr_buffer_q; + be_buffer_d = be_buffer_q; + if (rmw_count_q == '0) begin + bank_req = 1'b1; + end else begin + bank_req = 1'b0; + rmw_count_d = rmw_count_q - 1; + store_state_d = READ_MODIFY_WRITE; + end end - end + UPDATE_CORRECTED_DATA: begin + gnt_o = 1'b0; + bank_addr = corrected_data_raw_q.bank_addr; + bank_we = 1'b1; + bank_req = 1'b1; + in_update_corrected_data_mode = 1'b1; + store_state_d = NORMAL; + end + default: /* do nothing */; + endcase end always_ff @(posedge clk_i or negedge rst_ni) begin : proc_read_modify_write_ff @@ -284,4 +347,15 @@ module ecc_sram #( .rdata_o ( bank_scrub_rdata ) // read data ); + + // Output spill register for breaking timing path + shift_reg #( + .dtype(logic[DataInWidth-1:0]), + .Depth(NumOutputCuts) + ) i_output_buffer ( + .clk_i, + .rst_ni, + .d_i (rdata), + .d_o (rdata_o) + ); endmodule diff --git a/rtl/ecc_wrap/ecc_sram_wrap.sv b/rtl/ecc_wrap/ecc_sram_wrap.sv index d053c11c..f33e3d7d 100644 --- a/rtl/ecc_wrap/ecc_sram_wrap.sv +++ b/rtl/ecc_wrap/ecc_sram_wrap.sv @@ -20,8 +20,8 @@ module ecc_sram_wrap #( // Requries bitwise byte enable in SRAM parameter int unsigned NumRMWCuts = 0, // Number of cuts in the read-modify-write path // Set params - parameter int unsigned UnprotectedWidth = 32, // This currently only works for 32bit - parameter int unsigned ProtectedWidth = 39, // This currently only works for 39bit + parameter int unsigned UnprotectedWidth = 32, + parameter int unsigned ProtectedWidth = 39, localparam int unsigned DataInWidth = InputECC ? ProtectedWidth : UnprotectedWidth, localparam int unsigned BEInWidth = UnprotectedWidth/8, localparam int unsigned BankAddWidth = $clog2(BankSize) @@ -51,17 +51,29 @@ module ecc_sram_wrap #( logic [1:0] ecc_error; logic valid_read_d, valid_read_q; + logic valid_load_d, valid_load_q; + logic [DataInWidth-1:0] corrected_data_raw_d, corrected_data_raw_q; + logic corrected_data_raw_en; + logic in_update_corrected_data_mode; always_ff @(posedge clk_i or negedge rst_ni) begin : proc_valid_read if(~rst_ni) begin valid_read_q <= '0; end else begin valid_read_q <= valid_read_d; + valid_load_q <= valid_load_d; + end + end + + always_ff @(posedge clk_i) begin : proc_corrected_data_update + if(corrected_data_raw_en) begin + corrected_data_raw_q <= corrected_data_raw_d; end end assign valid_read_d = tcdm_req_i && tcdm_gnt_o && (tcdm_wen_i || (tcdm_be_i != {BEInWidth{1'b1}})); + assign valid_load_d = tcdm_req_i && tcdm_gnt_o && tcdm_wen_i; assign single_error_o = ecc_error[0] && valid_read_q; assign multi_error_o = ecc_error[1] && valid_read_q; @@ -93,7 +105,7 @@ module ecc_sram_wrap #( logic bank_final_we; logic [ BankAddWidth-1:0] bank_final_add; - typedef enum logic { NORMAL, LOAD_AND_STORE } store_state_e; + typedef enum logic[1:0] { NORMAL, LOAD_AND_STORE, UPDATE_CORRECTED_DATA } store_state_e; store_state_e store_state_d, store_state_q; logic [cf_math_pkg::idx_width(NumRMWCuts)-1:0] rmw_count_d, rmw_count_q; @@ -102,8 +114,11 @@ module ecc_sram_wrap #( logic [ BEInWidth-1:0] be_buffer_d, be_buffer_q; logic [UnprotectedWidth-1:0] be_selector; - assign be_selector = {{8{be_buffer_q[3]}},{8{be_buffer_q[2]}}, - {8{be_buffer_q[1]}},{8{be_buffer_q[0]}}}; + generate + for(genvar i = 0; i < UnprotectedWidth / 8; i++) begin: gen_be_selector + assign be_selector[i*8 +: 8] = {8{be_buffer_q[i]}}; + end + endgenerate logic [ProtectedWidth-1:0] rmw_buffer_end; logic [ProtectedWidth-1:0] rmw_buffer_0; @@ -132,14 +147,11 @@ module ecc_sram_wrap #( assign decoder_in = store_state_q == NORMAL ? bank_rdata : rmw_buffer_end; - hsiao_ecc_dec #( - .DataWidth (UnprotectedWidth), - .ProtWidth (ProtectedWidth - UnprotectedWidth) - ) ecc_decode ( - .in ( decoder_in ), - .out ( loaded ), - .syndrome_o(), - .err_o ( ecc_error ) + prim_secded_39_32_dec ecc_decode ( + .in ( decoder_in ), + .d_o ( loaded ), + .syndrome_o (), + .err_o (ecc_error) ); hsiao_ecc_enc #( @@ -154,8 +166,15 @@ module ecc_sram_wrap #( assign to_store = store_state_q == NORMAL ? tcdm_wdata_i : + in_update_corrected_data_mode ? + corrected_data_raw_q : (be_selector & input_buffer_q) | (~be_selector & loaded); + // for read transaction, update the sram content after a single-error was corrected + assign corrected_data_raw_en = valid_load_q && single_error_o; + assign corrected_data_raw_d = tcdm_rdata_o; + + end else begin : gen_ecc_input logic [ ProtectedWidth-1:0] lns_wdata; @@ -164,12 +183,9 @@ module ecc_sram_wrap #( assign bank_wdata = store_state_q == NORMAL ? tcdm_wdata_i : lns_wdata; assign tcdm_rdata_o = bank_rdata; - hsiao_ecc_dec #( - .DataWidth (UnprotectedWidth), - .ProtWidth (ProtectedWidth - UnprotectedWidth) - ) ld_decode ( - .in ( rmw_buffer_end ), - .out ( intermediate_data_ld ), + prim_secded_39_32_dec ld_decode ( + .in (rmw_buffer_end), + .d_o (intermediate_data_ld), .syndrome_o(), .err_o () ); @@ -191,7 +207,6 @@ module ecc_sram_wrap #( .in ( (be_selector & intermediate_data_st) | (~be_selector & intermediate_data_ld) ), .out ( lns_wdata ) ); - end always_comb begin @@ -204,27 +219,46 @@ module ecc_sram_wrap #( be_buffer_d = tcdm_be_i; bank_req = tcdm_req_i; rmw_count_d = rmw_count_q; - if (store_state_q == NORMAL) begin - if (tcdm_req_i & (tcdm_be_i != 4'b1111) & ~tcdm_wen_i) begin - store_state_d = LOAD_AND_STORE; - bank_we = 1'b0; - rmw_count_d = NumRMWCuts; + in_update_corrected_data_mode = 1'b0; + case (store_state_q) + NORMAL: begin + if (tcdm_req_i & (&tcdm_be_i != 1'b1) & ~tcdm_wen_i) begin + store_state_d = LOAD_AND_STORE; + bank_we = 1'b0; + rmw_count_d = NumRMWCuts; + end + if(valid_load_q && single_error_o) begin + store_state_d = UPDATE_CORRECTED_DATA; + tcdm_gnt_o = 1'b0; + add_buffer_d = add_buffer_q; + bank_req = 1'b0; + end end - end else begin - tcdm_gnt_o = 1'b0; - bank_add = add_buffer_q; - bank_we = 1'b1; - input_buffer_d = input_buffer_q; - add_buffer_d = add_buffer_q; - be_buffer_d = be_buffer_q; - if (rmw_count_q == '0) begin - bank_req = 1'b1; - end else begin - bank_req = 1'b0; - rmw_count_d = rmw_count_q - 1; - store_state_d = LOAD_AND_STORE; + LOAD_AND_STORE: begin + tcdm_gnt_o = 1'b0; + bank_add = add_buffer_q; + bank_we = 1'b1; + input_buffer_d = input_buffer_q; + add_buffer_d = add_buffer_q; + be_buffer_d = be_buffer_q; + if (rmw_count_q == '0) begin + bank_req = 1'b1; + end else begin + bank_req = 1'b0; + rmw_count_d = rmw_count_q - 1; + store_state_d = LOAD_AND_STORE; + end end - end + UPDATE_CORRECTED_DATA: begin + tcdm_gnt_o = 1'b0; + bank_req = 1'b1; + bank_we = 1'b1; + bank_add = add_buffer_q; + in_update_corrected_data_mode = 1'b1; + store_state_d = NORMAL; + end + default: /* do nothing */; + endcase end always_ff @(posedge clk_i or negedge rst_ni) begin : proc_load_and_store_ff @@ -246,7 +280,8 @@ module ecc_sram_wrap #( ecc_scrubber #( .BankSize ( BankSize ), .UseExternalECC ( 0 ), - .DataWidth ( ProtectedWidth ) + .DataWidth ( ProtectedWidth ), + .ProtWidth (ProtectedWidth - UnprotectedWidth) ) i_scrubber ( .clk_i, .rst_ni, diff --git a/rtl/ecc_wrap/ecc_sram_wrapper.hjson b/rtl/ecc_wrap/ecc_sram_wrapper.hjson index b635caf2..b7026c45 100644 --- a/rtl/ecc_wrap/ecc_sram_wrapper.hjson +++ b/rtl/ecc_wrap/ecc_sram_wrapper.hjson @@ -9,6 +9,14 @@ ], regwidth: "32", + param_list: [ + { name: "OffsetStart", + desc: "Starting offset of this reg set", + type: "", + default: "'h100", + local: "true" + }, + ], registers: [ { name: "mismatch_count", diff --git a/rtl/lowrisc_ecc/prim_secded_13_8_cor.sv b/rtl/lowrisc_ecc/prim_secded_13_8_cor.sv index 644fa91e..b1ba2ff0 100644 --- a/rtl/lowrisc_ecc/prim_secded_13_8_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_13_8_cor.sv @@ -16,20 +16,20 @@ module prim_secded_13_8_cor ( // Syndrome calculation assign syndrome_o[0] = ^(d_i & 13'h016B); - assign syndrome_o[1] = ^(d_i & 13'h02AD); + assign syndrome_o[1] = ^(d_i & 13'h02F8); assign syndrome_o[2] = ^(d_i & 13'h04D5); - assign syndrome_o[3] = ^(d_i & 13'h0836); - assign syndrome_o[4] = ^(d_i & 13'h10DA); + assign syndrome_o[3] = ^(d_i & 13'h08A7); + assign syndrome_o[4] = ^(d_i & 13'h101E); // Corrected output calculation - assign d_o[0] = (syndrome_o == 5'h7) ^ d_i[0]; + assign d_o[0] = (syndrome_o == 5'hd) ^ d_i[0]; assign d_o[1] = (syndrome_o == 5'h19) ^ d_i[1]; - assign d_o[2] = (syndrome_o == 5'he) ^ d_i[2]; + assign d_o[2] = (syndrome_o == 5'h1c) ^ d_i[2]; assign d_o[3] = (syndrome_o == 5'h13) ^ d_i[3]; - assign d_o[4] = (syndrome_o == 5'h1c) ^ d_i[4]; + assign d_o[4] = (syndrome_o == 5'h16) ^ d_i[4]; assign d_o[5] = (syndrome_o == 5'hb) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 5'h15) ^ d_i[6]; - assign d_o[7] = (syndrome_o == 5'h16) ^ d_i[7]; + assign d_o[6] = (syndrome_o == 5'h7) ^ d_i[6]; + assign d_o[7] = (syndrome_o == 5'he) ^ d_i[7]; assign d_o[8] = (syndrome_o == 5'h1) ^ d_i[8]; assign d_o[9] = (syndrome_o == 5'h2) ^ d_i[9]; assign d_o[10] = (syndrome_o == 5'h4) ^ d_i[10]; diff --git a/rtl/lowrisc_ecc/prim_secded_22_16_cor.sv b/rtl/lowrisc_ecc/prim_secded_22_16_cor.sv index 96851a5e..b3fd2e4e 100644 --- a/rtl/lowrisc_ecc/prim_secded_22_16_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_22_16_cor.sv @@ -15,30 +15,30 @@ module prim_secded_22_16_cor ( logic single_error; // Syndrome calculation - assign syndrome_o[0] = ^(d_i & 22'h015555); - assign syndrome_o[1] = ^(d_i & 22'h02AA55); - assign syndrome_o[2] = ^(d_i & 22'h0495A9); - assign syndrome_o[3] = ^(d_i & 22'h0869A6); - assign syndrome_o[4] = ^(d_i & 22'h10669A); - assign syndrome_o[5] = ^(d_i & 22'h209A6A); + assign syndrome_o[0] = ^(d_i & 22'h017B48); + assign syndrome_o[1] = ^(d_i & 22'h0291AB); + assign syndrome_o[2] = ^(d_i & 22'h040E3D); + assign syndrome_o[3] = ^(d_i & 22'h087692); + assign syndrome_o[4] = ^(d_i & 22'h10A547); + assign syndrome_o[5] = ^(d_i & 22'h20C8F4); // Corrected output calculation - assign d_o[0] = (syndrome_o == 6'h7) ^ d_i[0]; - assign d_o[1] = (syndrome_o == 6'h38) ^ d_i[1]; - assign d_o[2] = (syndrome_o == 6'hb) ^ d_i[2]; - assign d_o[3] = (syndrome_o == 6'h34) ^ d_i[3]; - assign d_o[4] = (syndrome_o == 6'h13) ^ d_i[4]; - assign d_o[5] = (syndrome_o == 6'h2c) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 6'h23) ^ d_i[6]; - assign d_o[7] = (syndrome_o == 6'h1c) ^ d_i[7]; - assign d_o[8] = (syndrome_o == 6'hd) ^ d_i[8]; - assign d_o[9] = (syndrome_o == 6'h32) ^ d_i[9]; - assign d_o[10] = (syndrome_o == 6'h15) ^ d_i[10]; - assign d_o[11] = (syndrome_o == 6'h2a) ^ d_i[11]; - assign d_o[12] = (syndrome_o == 6'h25) ^ d_i[12]; - assign d_o[13] = (syndrome_o == 6'h1a) ^ d_i[13]; - assign d_o[14] = (syndrome_o == 6'h19) ^ d_i[14]; - assign d_o[15] = (syndrome_o == 6'h26) ^ d_i[15]; + assign d_o[0] = (syndrome_o == 6'h16) ^ d_i[0]; + assign d_o[1] = (syndrome_o == 6'h1a) ^ d_i[1]; + assign d_o[2] = (syndrome_o == 6'h34) ^ d_i[2]; + assign d_o[3] = (syndrome_o == 6'h7) ^ d_i[3]; + assign d_o[4] = (syndrome_o == 6'h2c) ^ d_i[4]; + assign d_o[5] = (syndrome_o == 6'h26) ^ d_i[5]; + assign d_o[6] = (syndrome_o == 6'h31) ^ d_i[6]; + assign d_o[7] = (syndrome_o == 6'h2a) ^ d_i[7]; + assign d_o[8] = (syndrome_o == 6'h13) ^ d_i[8]; + assign d_o[9] = (syndrome_o == 6'hd) ^ d_i[9]; + assign d_o[10] = (syndrome_o == 6'h1c) ^ d_i[10]; + assign d_o[11] = (syndrome_o == 6'h25) ^ d_i[11]; + assign d_o[12] = (syndrome_o == 6'hb) ^ d_i[12]; + assign d_o[13] = (syndrome_o == 6'h19) ^ d_i[13]; + assign d_o[14] = (syndrome_o == 6'h29) ^ d_i[14]; + assign d_o[15] = (syndrome_o == 6'h32) ^ d_i[15]; assign d_o[16] = (syndrome_o == 6'h1) ^ d_i[16]; assign d_o[17] = (syndrome_o == 6'h2) ^ d_i[17]; assign d_o[18] = (syndrome_o == 6'h4) ^ d_i[18]; diff --git a/rtl/lowrisc_ecc/prim_secded_28_22_cor.sv b/rtl/lowrisc_ecc/prim_secded_28_22_cor.sv index d279c716..12908105 100644 --- a/rtl/lowrisc_ecc/prim_secded_28_22_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_28_22_cor.sv @@ -16,11 +16,11 @@ module prim_secded_28_22_cor ( // Syndrome calculation assign syndrome_o[0] = ^(d_i & 28'h07003FF); - assign syndrome_o[1] = ^(d_i & 28'h0B0FC0F); - assign syndrome_o[2] = ^(d_i & 28'h1371C71); + assign syndrome_o[1] = ^(d_i & 28'h0A0FC0F); + assign syndrome_o[2] = ^(d_i & 28'h1171C71); assign syndrome_o[3] = ^(d_i & 28'h23B6592); - assign syndrome_o[4] = ^(d_i & 28'h41DAAA4); - assign syndrome_o[5] = ^(d_i & 28'h82ED348); + assign syndrome_o[4] = ^(d_i & 28'h43DAAA4); + assign syndrome_o[5] = ^(d_i & 28'h83ED348); // Corrected output calculation assign d_o[0] = (syndrome_o == 6'h7) ^ d_i[0]; @@ -43,8 +43,8 @@ module prim_secded_28_22_cor ( assign d_o[17] = (syndrome_o == 6'h2c) ^ d_i[17]; assign d_o[18] = (syndrome_o == 6'h34) ^ d_i[18]; assign d_o[19] = (syndrome_o == 6'h38) ^ d_i[19]; - assign d_o[20] = (syndrome_o == 6'h1f) ^ d_i[20]; - assign d_o[21] = (syndrome_o == 6'h2f) ^ d_i[21]; + assign d_o[20] = (syndrome_o == 6'h3d) ^ d_i[20]; + assign d_o[21] = (syndrome_o == 6'h3b) ^ d_i[21]; assign d_o[22] = (syndrome_o == 6'h1) ^ d_i[22]; assign d_o[23] = (syndrome_o == 6'h2) ^ d_i[23]; assign d_o[24] = (syndrome_o == 6'h4) ^ d_i[24]; diff --git a/rtl/lowrisc_ecc/prim_secded_39_32_cor.sv b/rtl/lowrisc_ecc/prim_secded_39_32_cor.sv index 40e6b005..725b8b61 100644 --- a/rtl/lowrisc_ecc/prim_secded_39_32_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_39_32_cor.sv @@ -15,47 +15,47 @@ module prim_secded_39_32_cor ( logic single_error; // Syndrome calculation - assign syndrome_o[0] = ^(d_i & 39'h012CA53295); - assign syndrome_o[1] = ^(d_i & 39'h0293492CA5); - assign syndrome_o[2] = ^(d_i & 39'h04552A5329); - assign syndrome_o[3] = ^(d_i & 39'h08A8D294AA); - assign syndrome_o[4] = ^(d_i & 39'h104A2CA54A); - assign syndrome_o[5] = ^(d_i & 39'h2025534952); - assign syndrome_o[6] = ^(d_i & 39'h40D294CA54); + assign syndrome_o[0] = ^(d_i & 39'h013800CDBC); + assign syndrome_o[1] = ^(d_i & 39'h02C439C325); + assign syndrome_o[2] = ^(d_i & 39'h0452D82C63); + assign syndrome_o[3] = ^(d_i & 39'h08A4363856); + assign syndrome_o[4] = ^(d_i & 39'h109B833109); + assign syndrome_o[5] = ^(d_i & 39'h202DCF42C0); + assign syndrome_o[6] = ^(d_i & 39'h404364969A); // Corrected output calculation - assign d_o[0] = (syndrome_o == 7'h7) ^ d_i[0]; - assign d_o[1] = (syndrome_o == 7'h38) ^ d_i[1]; - assign d_o[2] = (syndrome_o == 7'h43) ^ d_i[2]; - assign d_o[3] = (syndrome_o == 7'h1c) ^ d_i[3]; - assign d_o[4] = (syndrome_o == 7'h61) ^ d_i[4]; - assign d_o[5] = (syndrome_o == 7'he) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 7'h70) ^ d_i[6]; - assign d_o[7] = (syndrome_o == 7'hb) ^ d_i[7]; - assign d_o[8] = (syndrome_o == 7'h34) ^ d_i[8]; - assign d_o[9] = (syndrome_o == 7'h45) ^ d_i[9]; - assign d_o[10] = (syndrome_o == 7'h1a) ^ d_i[10]; - assign d_o[11] = (syndrome_o == 7'h62) ^ d_i[11]; - assign d_o[12] = (syndrome_o == 7'hd) ^ d_i[12]; - assign d_o[13] = (syndrome_o == 7'h13) ^ d_i[13]; - assign d_o[14] = (syndrome_o == 7'h64) ^ d_i[14]; - assign d_o[15] = (syndrome_o == 7'h58) ^ d_i[15]; - assign d_o[16] = (syndrome_o == 7'h23) ^ d_i[16]; - assign d_o[17] = (syndrome_o == 7'h2c) ^ d_i[17]; - assign d_o[18] = (syndrome_o == 7'h51) ^ d_i[18]; - assign d_o[19] = (syndrome_o == 7'h16) ^ d_i[19]; - assign d_o[20] = (syndrome_o == 7'h68) ^ d_i[20]; - assign d_o[21] = (syndrome_o == 7'h15) ^ d_i[21]; - assign d_o[22] = (syndrome_o == 7'h2a) ^ d_i[22]; - assign d_o[23] = (syndrome_o == 7'h49) ^ d_i[23]; - assign d_o[24] = (syndrome_o == 7'h26) ^ d_i[24]; - assign d_o[25] = (syndrome_o == 7'h52) ^ d_i[25]; - assign d_o[26] = (syndrome_o == 7'h25) ^ d_i[26]; - assign d_o[27] = (syndrome_o == 7'h19) ^ d_i[27]; - assign d_o[28] = (syndrome_o == 7'h46) ^ d_i[28]; + assign d_o[0] = (syndrome_o == 7'h16) ^ d_i[0]; + assign d_o[1] = (syndrome_o == 7'h4c) ^ d_i[1]; + assign d_o[2] = (syndrome_o == 7'hb) ^ d_i[2]; + assign d_o[3] = (syndrome_o == 7'h51) ^ d_i[3]; + assign d_o[4] = (syndrome_o == 7'h49) ^ d_i[4]; + assign d_o[5] = (syndrome_o == 7'h7) ^ d_i[5]; + assign d_o[6] = (syndrome_o == 7'h2c) ^ d_i[6]; + assign d_o[7] = (syndrome_o == 7'h61) ^ d_i[7]; + assign d_o[8] = (syndrome_o == 7'h13) ^ d_i[8]; + assign d_o[9] = (syndrome_o == 7'h62) ^ d_i[9]; + assign d_o[10] = (syndrome_o == 7'h45) ^ d_i[10]; + assign d_o[11] = (syndrome_o == 7'hd) ^ d_i[11]; + assign d_o[12] = (syndrome_o == 7'h58) ^ d_i[12]; + assign d_o[13] = (syndrome_o == 7'h1c) ^ d_i[13]; + assign d_o[14] = (syndrome_o == 7'h23) ^ d_i[14]; + assign d_o[15] = (syndrome_o == 7'h43) ^ d_i[15]; + assign d_o[16] = (syndrome_o == 7'h32) ^ d_i[16]; + assign d_o[17] = (syndrome_o == 7'h38) ^ d_i[17]; + assign d_o[18] = (syndrome_o == 7'h68) ^ d_i[18]; + assign d_o[19] = (syndrome_o == 7'h26) ^ d_i[19]; + assign d_o[20] = (syndrome_o == 7'he) ^ d_i[20]; + assign d_o[21] = (syndrome_o == 7'h4a) ^ d_i[21]; + assign d_o[22] = (syndrome_o == 7'h64) ^ d_i[22]; + assign d_o[23] = (syndrome_o == 7'h34) ^ d_i[23]; + assign d_o[24] = (syndrome_o == 7'h70) ^ d_i[24]; + assign d_o[25] = (syndrome_o == 7'h54) ^ d_i[25]; + assign d_o[26] = (syndrome_o == 7'h2a) ^ d_i[26]; + assign d_o[27] = (syndrome_o == 7'h31) ^ d_i[27]; + assign d_o[28] = (syndrome_o == 7'h15) ^ d_i[28]; assign d_o[29] = (syndrome_o == 7'h29) ^ d_i[29]; - assign d_o[30] = (syndrome_o == 7'h54) ^ d_i[30]; - assign d_o[31] = (syndrome_o == 7'h4a) ^ d_i[31]; + assign d_o[30] = (syndrome_o == 7'h46) ^ d_i[30]; + assign d_o[31] = (syndrome_o == 7'h1a) ^ d_i[31]; assign d_o[32] = (syndrome_o == 7'h1) ^ d_i[32]; assign d_o[33] = (syndrome_o == 7'h2) ^ d_i[33]; assign d_o[34] = (syndrome_o == 7'h4) ^ d_i[34]; diff --git a/rtl/lowrisc_ecc/prim_secded_72_64_cor.sv b/rtl/lowrisc_ecc/prim_secded_72_64_cor.sv index c1a8d3ea..72c85cf4 100644 --- a/rtl/lowrisc_ecc/prim_secded_72_64_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_72_64_cor.sv @@ -15,14 +15,14 @@ module prim_secded_72_64_cor ( logic single_error; // Syndrome calculation - assign syndrome_o[0] = ^(d_i & 72'h015B000000001FFFFF); - assign syndrome_o[1] = ^(d_i & 72'h026B00000FFFE0003F); - assign syndrome_o[2] = ^(d_i & 72'h046D003FF003E007C1); - assign syndrome_o[3] = ^(d_i & 72'h08AD0FC0F03C207842); - assign syndrome_o[4] = ^(d_i & 72'h10B571C711C4438884); - assign syndrome_o[5] = ^(d_i & 72'h20B6B65926488C9108); - assign syndrome_o[6] = ^(d_i & 72'h40D6DAAA4A91152210); - assign syndrome_o[7] = ^(d_i & 72'h80DAED348D221A4420); + assign syndrome_o[0] = ^(d_i & 72'h01F8000000001FFFFF); + assign syndrome_o[1] = ^(d_i & 72'h029D00000FFFE0003F); + assign syndrome_o[2] = ^(d_i & 72'h048F003FF003E007C1); + assign syndrome_o[3] = ^(d_i & 72'h08F10FC0F03C207842); + assign syndrome_o[4] = ^(d_i & 72'h106E71C711C4438884); + assign syndrome_o[5] = ^(d_i & 72'h203EB65926488C9108); + assign syndrome_o[6] = ^(d_i & 72'h40D3DAAA4A91152210); + assign syndrome_o[7] = ^(d_i & 72'h8067ED348D221A4420); // Corrected output calculation assign d_o[0] = (syndrome_o == 8'h7) ^ d_i[0]; @@ -81,14 +81,14 @@ module prim_secded_72_64_cor ( assign d_o[53] = (syndrome_o == 8'hb0) ^ d_i[53]; assign d_o[54] = (syndrome_o == 8'hd0) ^ d_i[54]; assign d_o[55] = (syndrome_o == 8'he0) ^ d_i[55]; - assign d_o[56] = (syndrome_o == 8'h1f) ^ d_i[56]; - assign d_o[57] = (syndrome_o == 8'he3) ^ d_i[57]; - assign d_o[58] = (syndrome_o == 8'h7c) ^ d_i[58]; - assign d_o[59] = (syndrome_o == 8'h8f) ^ d_i[59]; - assign d_o[60] = (syndrome_o == 8'hf1) ^ d_i[60]; - assign d_o[61] = (syndrome_o == 8'h3e) ^ d_i[61]; - assign d_o[62] = (syndrome_o == 8'hc7) ^ d_i[62]; - assign d_o[63] = (syndrome_o == 8'hf8) ^ d_i[63]; + assign d_o[56] = (syndrome_o == 8'hce) ^ d_i[56]; + assign d_o[57] = (syndrome_o == 8'hf4) ^ d_i[57]; + assign d_o[58] = (syndrome_o == 8'hb6) ^ d_i[58]; + assign d_o[59] = (syndrome_o == 8'h37) ^ d_i[59]; + assign d_o[60] = (syndrome_o == 8'h6b) ^ d_i[60]; + assign d_o[61] = (syndrome_o == 8'hb9) ^ d_i[61]; + assign d_o[62] = (syndrome_o == 8'hd9) ^ d_i[62]; + assign d_o[63] = (syndrome_o == 8'h4f) ^ d_i[63]; assign d_o[64] = (syndrome_o == 8'h1) ^ d_i[64]; assign d_o[65] = (syndrome_o == 8'h2) ^ d_i[65]; assign d_o[66] = (syndrome_o == 8'h4) ^ d_i[66]; diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv b/rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv index 249a9e7f..e9f47371 100644 --- a/rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv @@ -19,25 +19,25 @@ module prim_secded_hamming_22_16_cor ( assign syndrome_o[2] = ^(d_i & 22'h04C78E); assign syndrome_o[3] = ^(d_i & 22'h0807F0); assign syndrome_o[4] = ^(d_i & 22'h10F800); - assign syndrome_o[5] = ^(d_i & 22'h205CB7); + assign syndrome_o[5] = ^(d_i & 22'h3FFFFF); // Corrected output calculation assign d_o[0] = (syndrome_o == 6'h23) ^ d_i[0]; assign d_o[1] = (syndrome_o == 6'h25) ^ d_i[1]; assign d_o[2] = (syndrome_o == 6'h26) ^ d_i[2]; - assign d_o[3] = (syndrome_o == 6'h7) ^ d_i[3]; + assign d_o[3] = (syndrome_o == 6'h27) ^ d_i[3]; assign d_o[4] = (syndrome_o == 6'h29) ^ d_i[4]; assign d_o[5] = (syndrome_o == 6'h2a) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 6'hb) ^ d_i[6]; + assign d_o[6] = (syndrome_o == 6'h2b) ^ d_i[6]; assign d_o[7] = (syndrome_o == 6'h2c) ^ d_i[7]; - assign d_o[8] = (syndrome_o == 6'hd) ^ d_i[8]; - assign d_o[9] = (syndrome_o == 6'he) ^ d_i[9]; + assign d_o[8] = (syndrome_o == 6'h2d) ^ d_i[8]; + assign d_o[9] = (syndrome_o == 6'h2e) ^ d_i[9]; assign d_o[10] = (syndrome_o == 6'h2f) ^ d_i[10]; assign d_o[11] = (syndrome_o == 6'h31) ^ d_i[11]; assign d_o[12] = (syndrome_o == 6'h32) ^ d_i[12]; - assign d_o[13] = (syndrome_o == 6'h13) ^ d_i[13]; + assign d_o[13] = (syndrome_o == 6'h33) ^ d_i[13]; assign d_o[14] = (syndrome_o == 6'h34) ^ d_i[14]; - assign d_o[15] = (syndrome_o == 6'h15) ^ d_i[15]; + assign d_o[15] = (syndrome_o == 6'h35) ^ d_i[15]; assign d_o[16] = (syndrome_o == 6'h1) ^ d_i[16]; assign d_o[17] = (syndrome_o == 6'h2) ^ d_i[17]; assign d_o[18] = (syndrome_o == 6'h4) ^ d_i[18]; diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv b/rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv index c861663c..2918c058 100644 --- a/rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv @@ -20,41 +20,41 @@ module prim_secded_hamming_39_32_cor ( assign syndrome_o[3] = ^(d_i & 39'h0803FC07F0); assign syndrome_o[4] = ^(d_i & 39'h1003FFF800); assign syndrome_o[5] = ^(d_i & 39'h20FC000000); - assign syndrome_o[6] = ^(d_i & 39'h402DA65CB7); + assign syndrome_o[6] = ^(d_i & 39'h7FFFFFFFFF); // Corrected output calculation assign d_o[0] = (syndrome_o == 7'h43) ^ d_i[0]; assign d_o[1] = (syndrome_o == 7'h45) ^ d_i[1]; assign d_o[2] = (syndrome_o == 7'h46) ^ d_i[2]; - assign d_o[3] = (syndrome_o == 7'h7) ^ d_i[3]; + assign d_o[3] = (syndrome_o == 7'h47) ^ d_i[3]; assign d_o[4] = (syndrome_o == 7'h49) ^ d_i[4]; assign d_o[5] = (syndrome_o == 7'h4a) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 7'hb) ^ d_i[6]; + assign d_o[6] = (syndrome_o == 7'h4b) ^ d_i[6]; assign d_o[7] = (syndrome_o == 7'h4c) ^ d_i[7]; - assign d_o[8] = (syndrome_o == 7'hd) ^ d_i[8]; - assign d_o[9] = (syndrome_o == 7'he) ^ d_i[9]; + assign d_o[8] = (syndrome_o == 7'h4d) ^ d_i[8]; + assign d_o[9] = (syndrome_o == 7'h4e) ^ d_i[9]; assign d_o[10] = (syndrome_o == 7'h4f) ^ d_i[10]; assign d_o[11] = (syndrome_o == 7'h51) ^ d_i[11]; assign d_o[12] = (syndrome_o == 7'h52) ^ d_i[12]; - assign d_o[13] = (syndrome_o == 7'h13) ^ d_i[13]; + assign d_o[13] = (syndrome_o == 7'h53) ^ d_i[13]; assign d_o[14] = (syndrome_o == 7'h54) ^ d_i[14]; - assign d_o[15] = (syndrome_o == 7'h15) ^ d_i[15]; - assign d_o[16] = (syndrome_o == 7'h16) ^ d_i[16]; + assign d_o[15] = (syndrome_o == 7'h55) ^ d_i[15]; + assign d_o[16] = (syndrome_o == 7'h56) ^ d_i[16]; assign d_o[17] = (syndrome_o == 7'h57) ^ d_i[17]; assign d_o[18] = (syndrome_o == 7'h58) ^ d_i[18]; - assign d_o[19] = (syndrome_o == 7'h19) ^ d_i[19]; - assign d_o[20] = (syndrome_o == 7'h1a) ^ d_i[20]; + assign d_o[19] = (syndrome_o == 7'h59) ^ d_i[19]; + assign d_o[20] = (syndrome_o == 7'h5a) ^ d_i[20]; assign d_o[21] = (syndrome_o == 7'h5b) ^ d_i[21]; - assign d_o[22] = (syndrome_o == 7'h1c) ^ d_i[22]; + assign d_o[22] = (syndrome_o == 7'h5c) ^ d_i[22]; assign d_o[23] = (syndrome_o == 7'h5d) ^ d_i[23]; assign d_o[24] = (syndrome_o == 7'h5e) ^ d_i[24]; - assign d_o[25] = (syndrome_o == 7'h1f) ^ d_i[25]; + assign d_o[25] = (syndrome_o == 7'h5f) ^ d_i[25]; assign d_o[26] = (syndrome_o == 7'h61) ^ d_i[26]; assign d_o[27] = (syndrome_o == 7'h62) ^ d_i[27]; - assign d_o[28] = (syndrome_o == 7'h23) ^ d_i[28]; + assign d_o[28] = (syndrome_o == 7'h63) ^ d_i[28]; assign d_o[29] = (syndrome_o == 7'h64) ^ d_i[29]; - assign d_o[30] = (syndrome_o == 7'h25) ^ d_i[30]; - assign d_o[31] = (syndrome_o == 7'h26) ^ d_i[31]; + assign d_o[30] = (syndrome_o == 7'h65) ^ d_i[30]; + assign d_o[31] = (syndrome_o == 7'h66) ^ d_i[31]; assign d_o[32] = (syndrome_o == 7'h1) ^ d_i[32]; assign d_o[33] = (syndrome_o == 7'h2) ^ d_i[33]; assign d_o[34] = (syndrome_o == 7'h4) ^ d_i[34]; diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv b/rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv index 896f97ac..e8f2e854 100644 --- a/rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv +++ b/rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv @@ -21,72 +21,72 @@ module prim_secded_hamming_72_64_cor ( assign syndrome_o[4] = ^(d_i & 72'h1001FFFE0003FFF800); assign syndrome_o[5] = ^(d_i & 72'h2001FFFFFFFC000000); assign syndrome_o[6] = ^(d_i & 72'h40FE00000000000000); - assign syndrome_o[7] = ^(d_i & 72'h80972CD2D32DA65CB7); + assign syndrome_o[7] = ^(d_i & 72'hFFFFFFFFFFFFFFFFFF); // Corrected output calculation assign d_o[0] = (syndrome_o == 8'h83) ^ d_i[0]; assign d_o[1] = (syndrome_o == 8'h85) ^ d_i[1]; assign d_o[2] = (syndrome_o == 8'h86) ^ d_i[2]; - assign d_o[3] = (syndrome_o == 8'h7) ^ d_i[3]; + assign d_o[3] = (syndrome_o == 8'h87) ^ d_i[3]; assign d_o[4] = (syndrome_o == 8'h89) ^ d_i[4]; assign d_o[5] = (syndrome_o == 8'h8a) ^ d_i[5]; - assign d_o[6] = (syndrome_o == 8'hb) ^ d_i[6]; + assign d_o[6] = (syndrome_o == 8'h8b) ^ d_i[6]; assign d_o[7] = (syndrome_o == 8'h8c) ^ d_i[7]; - assign d_o[8] = (syndrome_o == 8'hd) ^ d_i[8]; - assign d_o[9] = (syndrome_o == 8'he) ^ d_i[9]; + assign d_o[8] = (syndrome_o == 8'h8d) ^ d_i[8]; + assign d_o[9] = (syndrome_o == 8'h8e) ^ d_i[9]; assign d_o[10] = (syndrome_o == 8'h8f) ^ d_i[10]; assign d_o[11] = (syndrome_o == 8'h91) ^ d_i[11]; assign d_o[12] = (syndrome_o == 8'h92) ^ d_i[12]; - assign d_o[13] = (syndrome_o == 8'h13) ^ d_i[13]; + assign d_o[13] = (syndrome_o == 8'h93) ^ d_i[13]; assign d_o[14] = (syndrome_o == 8'h94) ^ d_i[14]; - assign d_o[15] = (syndrome_o == 8'h15) ^ d_i[15]; - assign d_o[16] = (syndrome_o == 8'h16) ^ d_i[16]; + assign d_o[15] = (syndrome_o == 8'h95) ^ d_i[15]; + assign d_o[16] = (syndrome_o == 8'h96) ^ d_i[16]; assign d_o[17] = (syndrome_o == 8'h97) ^ d_i[17]; assign d_o[18] = (syndrome_o == 8'h98) ^ d_i[18]; - assign d_o[19] = (syndrome_o == 8'h19) ^ d_i[19]; - assign d_o[20] = (syndrome_o == 8'h1a) ^ d_i[20]; + assign d_o[19] = (syndrome_o == 8'h99) ^ d_i[19]; + assign d_o[20] = (syndrome_o == 8'h9a) ^ d_i[20]; assign d_o[21] = (syndrome_o == 8'h9b) ^ d_i[21]; - assign d_o[22] = (syndrome_o == 8'h1c) ^ d_i[22]; + assign d_o[22] = (syndrome_o == 8'h9c) ^ d_i[22]; assign d_o[23] = (syndrome_o == 8'h9d) ^ d_i[23]; assign d_o[24] = (syndrome_o == 8'h9e) ^ d_i[24]; - assign d_o[25] = (syndrome_o == 8'h1f) ^ d_i[25]; + assign d_o[25] = (syndrome_o == 8'h9f) ^ d_i[25]; assign d_o[26] = (syndrome_o == 8'ha1) ^ d_i[26]; assign d_o[27] = (syndrome_o == 8'ha2) ^ d_i[27]; - assign d_o[28] = (syndrome_o == 8'h23) ^ d_i[28]; + assign d_o[28] = (syndrome_o == 8'ha3) ^ d_i[28]; assign d_o[29] = (syndrome_o == 8'ha4) ^ d_i[29]; - assign d_o[30] = (syndrome_o == 8'h25) ^ d_i[30]; - assign d_o[31] = (syndrome_o == 8'h26) ^ d_i[31]; + assign d_o[30] = (syndrome_o == 8'ha5) ^ d_i[30]; + assign d_o[31] = (syndrome_o == 8'ha6) ^ d_i[31]; assign d_o[32] = (syndrome_o == 8'ha7) ^ d_i[32]; assign d_o[33] = (syndrome_o == 8'ha8) ^ d_i[33]; - assign d_o[34] = (syndrome_o == 8'h29) ^ d_i[34]; - assign d_o[35] = (syndrome_o == 8'h2a) ^ d_i[35]; + assign d_o[34] = (syndrome_o == 8'ha9) ^ d_i[34]; + assign d_o[35] = (syndrome_o == 8'haa) ^ d_i[35]; assign d_o[36] = (syndrome_o == 8'hab) ^ d_i[36]; - assign d_o[37] = (syndrome_o == 8'h2c) ^ d_i[37]; + assign d_o[37] = (syndrome_o == 8'hac) ^ d_i[37]; assign d_o[38] = (syndrome_o == 8'had) ^ d_i[38]; assign d_o[39] = (syndrome_o == 8'hae) ^ d_i[39]; - assign d_o[40] = (syndrome_o == 8'h2f) ^ d_i[40]; + assign d_o[40] = (syndrome_o == 8'haf) ^ d_i[40]; assign d_o[41] = (syndrome_o == 8'hb0) ^ d_i[41]; - assign d_o[42] = (syndrome_o == 8'h31) ^ d_i[42]; - assign d_o[43] = (syndrome_o == 8'h32) ^ d_i[43]; + assign d_o[42] = (syndrome_o == 8'hb1) ^ d_i[42]; + assign d_o[43] = (syndrome_o == 8'hb2) ^ d_i[43]; assign d_o[44] = (syndrome_o == 8'hb3) ^ d_i[44]; - assign d_o[45] = (syndrome_o == 8'h34) ^ d_i[45]; + assign d_o[45] = (syndrome_o == 8'hb4) ^ d_i[45]; assign d_o[46] = (syndrome_o == 8'hb5) ^ d_i[46]; assign d_o[47] = (syndrome_o == 8'hb6) ^ d_i[47]; - assign d_o[48] = (syndrome_o == 8'h37) ^ d_i[48]; - assign d_o[49] = (syndrome_o == 8'h38) ^ d_i[49]; + assign d_o[48] = (syndrome_o == 8'hb7) ^ d_i[48]; + assign d_o[49] = (syndrome_o == 8'hb8) ^ d_i[49]; assign d_o[50] = (syndrome_o == 8'hb9) ^ d_i[50]; assign d_o[51] = (syndrome_o == 8'hba) ^ d_i[51]; - assign d_o[52] = (syndrome_o == 8'h3b) ^ d_i[52]; + assign d_o[52] = (syndrome_o == 8'hbb) ^ d_i[52]; assign d_o[53] = (syndrome_o == 8'hbc) ^ d_i[53]; - assign d_o[54] = (syndrome_o == 8'h3d) ^ d_i[54]; - assign d_o[55] = (syndrome_o == 8'h3e) ^ d_i[55]; + assign d_o[54] = (syndrome_o == 8'hbd) ^ d_i[54]; + assign d_o[55] = (syndrome_o == 8'hbe) ^ d_i[55]; assign d_o[56] = (syndrome_o == 8'hbf) ^ d_i[56]; assign d_o[57] = (syndrome_o == 8'hc1) ^ d_i[57]; assign d_o[58] = (syndrome_o == 8'hc2) ^ d_i[58]; - assign d_o[59] = (syndrome_o == 8'h43) ^ d_i[59]; + assign d_o[59] = (syndrome_o == 8'hc3) ^ d_i[59]; assign d_o[60] = (syndrome_o == 8'hc4) ^ d_i[60]; - assign d_o[61] = (syndrome_o == 8'h45) ^ d_i[61]; - assign d_o[62] = (syndrome_o == 8'h46) ^ d_i[62]; + assign d_o[61] = (syndrome_o == 8'hc5) ^ d_i[61]; + assign d_o[62] = (syndrome_o == 8'hc6) ^ d_i[62]; assign d_o[63] = (syndrome_o == 8'hc7) ^ d_i[63]; assign d_o[64] = (syndrome_o == 8'h1) ^ d_i[64]; assign d_o[65] = (syndrome_o == 8'h2) ^ d_i[65]; diff --git a/test/tb_ecc_secded.sv b/test/tb_ecc_secded.sv index 30793d65..a463b54e 100644 --- a/test/tb_ecc_secded.sv +++ b/test/tb_ecc_secded.sv @@ -193,49 +193,103 @@ module tb_ecc_secded #( prot_t prot_out, prot_in, prot_corrected; logic [1:0] error, error_corrector, error_correcter_decoder; - hsiao_ecc_enc #( - .DataWidth(DataWidth), - .ProtWidth (ProtectBits), - .PrintHsiao(1'b1) - ) i_dut_encode ( - .in (in), - .out(prot_out) - ); - - assign prot_in = prot_out ^ inject; - - hsiao_ecc_dec #( - .DataWidth(DataWidth), - .ProtWidth (ProtectBits), - .PrintHsiao(1'b1) - ) i_dut_decode ( - .in (prot_in), - .out (out), - .syndrome_o(syndrome), - .err_o (error) - ); - - hsiao_ecc_cor #( - .DataWidth(DataWidth), - .ProtWidth (ProtectBits), - .PrintHsiao(1'b1) - ) i_dut_correct ( - .in (prot_in), - .out (prot_corrected), - .syndrome_o(syndrome_corrector), - .err_o (error_corrector) - ); - - hsiao_ecc_dec #( - .DataWidth(DataWidth), - .ProtWidth (ProtectBits), - .PrintHsiao(1'b1) - ) i_dut_correct_decode ( - .in (prot_corrected), - .out (out_corrected), - .syndrome_o(syndrome_corrector_decoder), - .err_o (error_correcter_decoder) - ); + if (DataWidth == 8) begin + prim_secded_13_8_enc i_dut_encode ( + .in (in), + .out(prot_out) + ); + assign prot_in = prot_out ^ inject; + prim_secded_13_8_dec i_dut_decode ( + .in (prot_in), + .d_o (out), + .syndrome_o(syndrome), + .err_o (error) + ); + prim_secded_13_8_cor i_dut_correct ( + .d_i (prot_in), + .d_o (prot_corrected), + .syndrome_o(syndrome_corrector), + .err_o (error_corrector) + ); + prim_secded_13_8_dec i_dut_correct_decode ( + .in (prot_corrected), + .d_o (out_corrected), + .syndrome_o(syndrome_corrector_decoder), + .err_o (error_correcter_decoder) + ); + end else if (DataWidth == 16) begin + prim_secded_22_16_enc i_dut_encode ( + .in (in), + .out(prot_out) + ); + assign prot_in = prot_out ^ inject; + prim_secded_22_16_dec i_dut_decode ( + .in (prot_in), + .d_o (out), + .syndrome_o(syndrome), + .err_o (error) + ); + prim_secded_22_16_cor i_dut_correct ( + .d_i (prot_in), + .d_o (prot_corrected), + .syndrome_o(syndrome_corrector), + .err_o (error_corrector) + ); + prim_secded_22_16_dec i_dut_correct_decode ( + .in (prot_corrected), + .d_o (out_corrected), + .syndrome_o(syndrome_corrector_decoder), + .err_o (error_correcter_decoder) + ); + end else if (DataWidth == 32) begin + prim_secded_39_32_enc i_dut_encode ( + .in (in), + .out(prot_out) + ); + assign prot_in = prot_out ^ inject; + prim_secded_39_32_dec i_dut_decode ( + .in (prot_in), + .d_o (out), + .syndrome_o(syndrome), + .err_o (error) + ); + prim_secded_39_32_cor i_dut_correct ( + .d_i (prot_in), + .d_o (prot_corrected), + .syndrome_o(syndrome_corrector), + .err_o (error_corrector) + ); + prim_secded_39_32_dec i_dut_correct_decode ( + .in (prot_corrected), + .d_o (out_corrected), + .syndrome_o(syndrome_corrector_decoder), + .err_o (error_correcter_decoder) + ); + end else if (DataWidth == 64) begin + prim_secded_72_64_enc i_dut_encode ( + .in (in), + .out(prot_out) + ); + assign prot_in = prot_out ^ inject; + prim_secded_72_64_dec i_dut_decode ( + .in (prot_in), + .d_o (out), + .syndrome_o(syndrome), + .err_o (error) + ); + prim_secded_72_64_cor i_dut_correct ( + .d_i (prot_in), + .d_o (prot_corrected), + .syndrome_o(syndrome_corrector), + .err_o (error_corrector) + ); + prim_secded_72_64_dec i_dut_correct_decode ( + .in (prot_corrected), + .d_o (out_corrected), + .syndrome_o(syndrome_corrector_decoder), + .err_o (error_correcter_decoder) + ); + end /*********************** * Output collection *