diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a957cad4..99d2db4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: run: pip install -r requirements.txt - name: Check clean make targets run: | - make -B gen_ODRG gen_TCLS gen_ecc_registers gen_ECC + make -B gen_HMR gen_ecc_registers gen_ECC git status && test -z "$(git status --porcelain)" lint-verilog: runs-on: ubuntu-latest @@ -41,18 +41,11 @@ jobs: ./rtl exclude_paths: | ./test - ./rtl/*/*_reg_pkg.sv - ./rtl/*/*_reg_top.sv - ./rtl/ecc_wrap/ecc_manager_2_reg_pkg.sv - ./rtl/ecc_wrap/ecc_manager_2_reg_top.sv - ./rtl/ecc_wrap/ecc_manager_8_reg_pkg.sv - ./rtl/ecc_wrap/ecc_manager_8_reg_top.sv + ./rtl/HMR/regs + ./rtl/HMR/regs ./rtl/ecc_wrap/ecc_manager_reg_pkg.sv ./rtl/ecc_wrap/ecc_manager_reg_top.sv - ./rtl/ODRG_unit/odrg_manager_reg_pkg.sv - ./rtl/ODRG_unit/odrg_manager_reg_top.sv - ./rtl/pulpissimo_tcls/tcls_manager_reg_pkg.sv - ./rtl/pulpissimo_tcls/tcls_manager_reg_top.sv + ./rtl/deprecated extra_args: "--rules=-interface-name-style --lint_fatal --parse_fatal --waiver_files .github/waiver.verible" github_token: ${{ secrets.GITHUB_TOKEN }} reviewdog_reporter: github-check diff --git a/Bender.yml b/Bender.yml index 14bfe737..71c5f2d0 100644 --- a/Bender.yml +++ b/Bender.yml @@ -14,10 +14,7 @@ sources: # package. Files in level 1 only depend on files in level 0, files in level 2 on files in # levels 1 and 0, etc. Files within a level are ordered alphabetically. # Level 0 - - 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/ODRG_unit/triple_core_barrier.sv - rtl/hsiao_ecc/hsiao_ecc_pkg.sv - rtl/hsiao_ecc/hsiao_ecc_enc.sv - rtl/hsiao_ecc/hsiao_ecc_dec.sv @@ -25,39 +22,73 @@ sources: - rtl/TMR_voter.sv - rtl/TMR_word_voter.sv # Level 1 - - rtl/ODRG_unit/odrg_manager_reg_top.sv - rtl/ecc_wrap/ecc_manager_reg_top.sv - - rtl/pulpissimo_tcls/tcls_manager_reg_top.sv - rtl/ecc_wrap/ecc_scrubber.sv + - rtl/TMR_voter_detect.sv + # Level 2 + - rtl/bitwise_TMR_voter.sv + - rtl/ecc_wrap/ecc_manager.sv + - rtl/ecc_wrap/ecc_sram.sv + # Level 3 + + - files: + - rtl/HMR/hmr_pkg.sv + - rtl/HMR/resp_suppress.sv + - rtl/HMR/rapid_recovery/rapid_recovery_pkg.sv + - rtl/HMR/rapid_recovery/recovery_csr.sv + - rtl/HMR/rapid_recovery/recovery_pc.sv + - rtl/HMR/rapid_recovery/recovery_rf.sv + - rtl/HMR/rapid_recovery/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/rapid_recovery/hmr_rapid_recovery_ctrl.sv + - rtl/HMR/regs/hmr_registers_reg_pkg.sv + - rtl/HMR/regs/hmr_core_regs_reg_pkg.sv + - rtl/HMR/regs/hmr_dmr_regs_reg_pkg.sv + - rtl/HMR/regs/hmr_tmr_regs_reg_pkg.sv + - rtl/HMR/regs/hmr_registers_reg_top.sv + - rtl/HMR/regs/hmr_core_regs_reg_top.sv + - rtl/HMR/regs/hmr_dmr_regs_reg_top.sv + - rtl/HMR/hmr_dmr_ctrl.sv + - rtl/HMR/regs/hmr_tmr_regs_reg_top.sv + - rtl/HMR/hmr_tmr_ctrl.sv + - rtl/HMR/hmr_unit.sv + - rtl/HMR/hmr_rr_wrapper.sv - target: any(deprecated, axi_ecc, hci_ecc, pulp_ecc, test) files: - - rtl/ecc_concat_32_64.sv - - rtl/lowrisc_ecc/prim_secded_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/deprecated/ecc_concat_32_64.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_pkg.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_13_8_cor.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_13_8_dec.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_13_8_enc.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_22_16_cor.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_22_16_dec.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_22_16_enc.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_39_32_cor.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_39_32_dec.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_39_32_enc.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_72_64_cor.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_72_64_dec.sv + - rtl/deprecated/lowrisc_ecc/prim_secded_72_64_enc.sv - - target: axi_ecc # custom ECC for PULP AXI IPs, make sure to include interface IPs when adding this target + # custom ECC for PULP AXI IPs, make sure to include interface IPs when adding this target + - target: axi_ecc files: - rtl/BUS_enc_dec/AXI_bus_ecc_dec.sv - rtl/BUS_enc_dec/AXI_bus_ecc_enc.sv - - target: hci_ecc # custom ECC for PULP HCI IPs, make sure to include interface IPs when adding this target + # custom ECC for PULP HCI IPs, make sure to include interface IPs when adding this target + - target: hci_ecc files: - rtl/BUS_enc_dec/hci_core_intf_ecc_dec.sv - rtl/BUS_enc_dec/hci_core_intf_ecc_enc.sv - rtl/BUS_enc_dec/hci_mem_intf_ecc_dec.sv - rtl/BUS_enc_dec/hci_mem_intf_ecc_enc.sv - - target: pulp_ecc # custom ECC for PULP (pulp_soc) interface IPs, make sure to include interface IPs when adding this target + + # custom ECC for PULP (pulp_soc) interface IPs, make sure to include interface IPs when adding this target + - target: pulp_ecc files: - rtl/BUS_enc_dec/PE_XBAR_bus_ecc_dec.sv - rtl/BUS_enc_dec/PE_XBAR_bus_ecc_enc.sv @@ -65,23 +96,33 @@ sources: - rtl/BUS_enc_dec/TCDM_XBAR_bus_ecc_enc.sv - rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_dec.sv - rtl/BUS_enc_dec/XBAR_DEMUX_BUS_ecc_enc.sv - - rtl/TMR_voter_detect.sv - # Level 2 - - rtl/bitwise_TMR_voter.sv - - rtl/ecc_wrap/ecc_manager.sv + - target: deprecated files: - - rtl/ecc_wrap/ecc_sram_wrap.sv - - rtl/ecc_wrap/ecc_sram.sv - # Level 3 - - include_dirs: - - rtl/ODRG_unit + - rtl/deprecated/ecc_sram_wrap.sv + + - target: any(deprecated, ODRG) + include_dirs: + - rtl/deprecated/ODRG_unit files: - - rtl/ODRG_unit/ODRG_unit.sv - - include_dirs: - - rtl/pulpissimo_tcls + - rtl/deprecated/ODRG_unit/odrg_manager_reg_pkg.sv + - rtl/deprecated/ODRG_unit/triple_core_barrier.sv + - rtl/deprecated/ODRG_unit/odrg_manager_reg_top.sv + - rtl/deprecated/ODRG_unit/ODRG_unit.sv + + - target: any(deprecated, pulpissimo_tcls) + include_dirs: + - rtl/deprecated/pulpissimo_tcls files: - - rtl/pulpissimo_tcls/TCLS_unit.sv + - rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_pkg.sv + - rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_top.sv + - rtl/deprecated/pulpissimo_tcls/TCLS_unit.sv + + - target: any(deprecated, HMR_wrap) + files: + - rtl/HMR/rapid_recovery/DMR_address_generator.sv + - rtl/deprecated/HMR_wrap.sv + - target: test files: - test/tb_ecc_scrubber.sv @@ -94,9 +135,9 @@ sources: vendor_package: - name: lowrisc_opentitan - target_dir: "util/lowrisc_opentitan" + target_dir: "rtl/deprecated/util/lowrisc_opentitan" upstream: { git: "https://github.com/lowRISC/opentitan.git", rev: "cfcfbce85e182c127b8c4be5cd8bf531e0a4d927" } - patch_dir: "util/patches" + patch_dir: "rtl/deprecated/util/patches" mapping: - {from: 'util/design/secded_gen.py', to: 'util/design/secded_gen.py', patch_dir: 'lowrisc_secded_gen'} - {from: 'util/design/data/', to: 'util/design/data/', patch_dir: 'lowrisc_data_dir'} diff --git a/CHANGELOG.md b/CHANGELOG.md index 79f119b7..6d12ccf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added - Add ECC correctors +- Add `HMR` unit ### 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 +- Deprecate `ODRG`, superceeded by HMR +- Deprecate `pulpissimo_tcls`, superceeded by HMR ## 0.5.1 - 2023-04-12 ### Added diff --git a/Makefile b/Makefile index 64af21f9..7280720b 100644 --- a/Makefile +++ b/Makefile @@ -20,41 +20,111 @@ REG_PATH = $(shell $(BENDER) path register_interface) # REG_PATH = ../register_interface 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/regs/HMR_regs.hjson +HJSON_HMR_core = rtl/HMR/regs/HMR_core_regs.hjson +HJSON_HMR_dmr = rtl/HMR/regs/HMR_dmr_regs.hjson +HJSON_HMR_tmr = rtl/HMR/regs/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/regs TARGET_DIR_ECC = rtl/ecc_wrap -.PHONY: gen_ODRG gen_TCLS gen_ecc_registers gen_ECC -gen_ODRG: - python $(REG_TOOL) $(HJSON_ODRG) -t $(TARGET_DIR_ODRG) -r - python $(REG_TOOL) $(HJSON_ODRG) -d > $(TARGET_DIR_ODRG)/doc.md - python $(REG_TOOL) $(HJSON_ODRG) -D > $(TARGET_DIR_ODRG)/ODRG.h +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. + */ -gen_TCLS: - python $(REG_TOOL) $(HJSON_TCLS) -t $(TARGET_DIR_TCLS) -r - python $(REG_TOOL) $(HJSON_TCLS) -d > $(TARGET_DIR_TCLS)/doc.md - python $(REG_TOOL) $(HJSON_TCLS) -D > $(TARGET_DIR_TCLS)/TCLS.h +#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_HMR gen_ecc_registers gen_ECC + +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 python $(REG_TOOL) $(HJSON_ECC) -D > $(TARGET_DIR_ECC)/ECC.h -gen_ECC: - $(BENDER) vendor init - cd util/lowrisc_opentitan && ./util/design/secded_gen.py --no_fpv --outdir ../../rtl/lowrisc_ecc - bender: ifeq (,$(wildcard ./bender)) curl --proto '=https' --tlsv1.2 -sSf https://pulp-platform.github.io/bender/init \ - | bash -s -- 0.27.1 + | bash -s -- 0.27.3 touch bender endif .PHONY: bender-rm bender-rm: rm -f bender + +# deprecated +.PHONY: gen_ODRG gen_TCLS +HJSON_ODRG = rtl/deprecated/ODRG_unit/ODRG_unit.hjson +HJSON_TCLS = rtl/deprecated/pulpissimo_tcls/TCLS_unit.hjson +TARGET_DIR_ODRG = rtl/deprecated/ODRG_unit +TARGET_DIR_TCLS = rtl/deprecated/pulpissimo_tcls + +gen_ODRG: + python $(REG_TOOL) $(HJSON_ODRG) -t $(TARGET_DIR_ODRG) -r + python $(REG_TOOL) $(HJSON_ODRG) -d > $(TARGET_DIR_ODRG)/doc.md + python $(REG_TOOL) $(HJSON_ODRG) -D > $(TARGET_DIR_ODRG)/ODRG.h + +gen_TCLS: + python $(REG_TOOL) $(HJSON_TCLS) -t $(TARGET_DIR_TCLS) -r + python $(REG_TOOL) $(HJSON_TCLS) -d > $(TARGET_DIR_TCLS)/doc.md + python $(REG_TOOL) $(HJSON_TCLS) -D > $(TARGET_DIR_TCLS)/TCLS.h + +gen_ECC: + $(BENDER) vendor init + cd rtl/deprecated/util/lowrisc_opentitan && ./util/design/secded_gen.py --no_fpv --outdir ../../lowrisc_ecc diff --git a/README.md b/README.md index 47369bed..4e57efc9 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,35 @@ This repository contains various modules used to add redundancy. +## Hybrid-Modular Redundancy (HMR) +The `hmr_unit` is designed as a configurable bridge between multiple cores and the system, allowing for independent or lock-step operation, either in DMR or TMR mode. Further recovery mechanisms are implemented, including a SW-based recovery mechanism when in TMR mode with an interrupt being triggered on an error, a rapid recovery mechanism to automatically backup and refill the state with correct values, and in the future a checkpoint-based recovery mechanism. + +### Testing +The HMR unit has been integrated in the [PULP cluster](https://github.com/pulp-platform/pulp_cluster/tree/michaero/hmr_merge) and the [Safety Island](https://github.com/pulp-platform/safety_island) in different configurations. + +### Citing +If you are using HMR in your academic work you can cite us: +```BibTeX +@article{10.1145/3635161, +author = {Rogenmoser, Michael and Tortorella, Yvan and Rossi, Davide and Conti, Francesco and Benini, Luca}, +title = {Hybrid Modular Redundancy: Exploring Modular Redundancy Approaches in RISC-V Multi-Core Computing Clusters for Reliable Processing in Space}, +year = {2023}, +publisher = {Association for Computing Machinery}, +address = {New York, NY, USA}, +issn = {2378-962X}, +url = {https://doi.org/10.1145/3635161}, +doi = {10.1145/3635161}, +abstract = {Space Cyber-Physical Systems (S-CPS) such as spacecraft and satellites strongly rely on the reliability of onboard computers to guarantee the success of their missions. Relying solely on radiation-hardened technologies is extremely expensive, and developing inflexible architectural and microarchitectural modifications to introduce modular redundancy within a system leads to significant area increase and performance degradation. To mitigate the overheads of traditional radiation hardening and modular redundancy approaches, we present a novel Hybrid Modular Redundancy (HMR) approach, a redundancy scheme that features a cluster of RISC-V processors with a flexible on-demand dual-core and triple-core lockstep grouping of computing cores with runtime split-lock capabilities. Further, we propose two recovery approaches, software-based and hardware-based, trading off performance and area overhead. Running at 430MHz, our fault-tolerant cluster achieves up to 1160MOPS on a matrix multiplication benchmark when configured in non-redundant mode and 617 and 414 MOPS in dual and triple mode, respectively. A software-based recovery in triple mode requires 363 clock cycles and occupies 0.612 mm2, representing a 1.3\% area overhead over a non-redundant 12-core RISC-V cluster. As a high-performance alternative, a new hardware-based method provides rapid fault recovery in just 24 clock cycles and occupies 0.660 mm2, namely ∼ 9.4\% area overhead over the baseline non-redundant RISC-V cluster. The cluster is also enhanced with split-lock capabilities to enter one of the available redundant modes with minimum performance loss, allowing execution of a mission-critical portion of code when in independent mode, or a performance section when in a reliability mode, with <400 clock cycles overhead for entry and exit. The proposed system is the first to integrate these functionalities on an open-source RISC-V-based compute device, enabling finely tunable reliability vs. performance trade-offs.}, +note = {Just Accepted}, +journal = {ACM Trans. Cyber-Phys. Syst.}, +month = {nov} +} +``` + ## On-Demand Redundancy Grouping (ODRG_unit) The `ODRG_unit` is designed as a configurable bridge between three ibex cores, allowing for independent operation or lock-step operation with majority voting, triggering an interrupt in case a mismatch is detected. It uses lowrisc's reggen tool to generate the required configuration registers. -### Testing -ODRG is integrated in the [PULP cluster](https://github.com/pulp-platform/pulp_cluster/tree/space_pulp) and the [PULP](https://github.com/pulp-platform/pulp/tree/space_pulp) system. To test, please use the `space_pulp` branch. +ODRG has been superceeded by HMR, as HMR integrates all ODRG features. To simplify maintenance, only one is included. If you would like to inspect the code, please check out the tag `v0.5.1`. ### Citing If you are using ODRG in your academic work you can cite us: @@ -27,7 +51,7 @@ If you are using ODRG in your academic work you can cite us: To re-generate regfile, run following command in the root directory of this repo. ```bash -make gen_ODRG +make gen_HMR ``` This will generate the register file SV-code, its corresponding C-code and documentation using lowrisc's reggen tool via the pulp register-interface repository. @@ -59,4 +83,4 @@ To run tests, execute the following command: ./run_tests.sh ``` -A bender installation >=v0.27 is required. +A [bender](https://github.com/pulp-platform/bender) installation >=v0.27 is required. diff --git a/rtl/HMR/DMR_CSR_checker.sv b/rtl/HMR/DMR_CSR_checker.sv new file mode 100644 index 00000000..b801dd77 --- /dev/null +++ b/rtl/HMR/DMR_CSR_checker.sv @@ -0,0 +1,49 @@ +// 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. +// +// 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_checker.sv b/rtl/HMR/DMR_checker.sv new file mode 100644 index 00000000..ff881c1a --- /dev/null +++ b/rtl/HMR/DMR_checker.sv @@ -0,0 +1,49 @@ +// 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. +// +// Dual Modular Redundancy Checker +// Compares the outputs generated by two IPs and returns error signals +// in case of mismatch + +module DMR_checker #( + parameter int unsigned DataWidth = 41, + parameter int unsigned Pipeline = 0 +)( + input logic clk_i, + input logic rst_ni, + input logic [DataWidth-1:0] inp_a_i, + input logic [DataWidth-1:0] inp_b_i, + output logic [DataWidth-1:0] check_o, + output logic error_o +); + +logic error; +logic [DataWidth-1:0] compare; +logic [DataWidth-1:0] inp_q; + +if (Pipeline) begin : gen_pipeline_reg + 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 : gen_no_pipeline + assign compare = inp_a_i ^ inp_b_i; + assign inp_q = inp_a_i; +end +assign error = |compare; +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..e2d208c5 --- /dev/null +++ b/rtl/HMR/DMR_controller.sv @@ -0,0 +1,366 @@ +// 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. +// +// 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 : gen_dmr_connection + 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 : gen_instr_locks + 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 : gen_core_clk_enable + 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 : gen_recover_regs + 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 : gen_PC_we + 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, default: 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_dmr_ctrl.sv b/rtl/HMR/hmr_dmr_ctrl.sv new file mode 100644 index 00000000..5b344225 --- /dev/null +++ b/rtl/HMR/hmr_dmr_ctrl.sv @@ -0,0 +1,206 @@ +// 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, + input logic setback_q_i, + input logic setback_qe_i, + input logic synch_req_q_i, + input logic synch_req_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, + input logic bus_resp_ok_i, + output logic bus_hold_o +); + + 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.setback.de = setback_qe_i; + assign dmr_hw2reg.dmr_config.setback.d = setback_q_i; + assign dmr_hw2reg.dmr_config.synch_req.de = synch_req_qe_i; + assign dmr_hw2reg.dmr_config.synch_req.d = synch_req_q_i; + 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; + bus_hold_o = 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: begin end // 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 = dmr_reg2hw.dmr_config.synch_req.q; + 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 + bus_hold_o = 1'b1; + if (bus_resp_ok_i) begin + if (dmr_reg2hw.dmr_config.setback.q) begin + setback_o = 2'b10; + end + dmr_red_mode_d = NON_DMR; + end + 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_pkg.sv b/rtl/HMR/hmr_pkg.sv new file mode 100644 index 00000000..17a01158 --- /dev/null +++ b/rtl/HMR/hmr_pkg.sv @@ -0,0 +1,29 @@ +// Copyright 2024 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 support package + +package hmr_pkg; + function automatic int max(int a, int b); + return (a > b) ? a : b; + endfunction + + typedef struct packed { + logic recovery_start; + logic rr_enable; + } rr_ctrl_t; + + typedef struct packed { + logic recovery_finished; + logic rr_error; + } rr_status_t; + + +endpackage diff --git a/rtl/HMR/hmr_rr_wrapper.sv b/rtl/HMR/hmr_rr_wrapper.sv new file mode 100644 index 00000000..3fe05f5a --- /dev/null +++ b/rtl/HMR/hmr_rr_wrapper.sv @@ -0,0 +1,410 @@ +// 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 with rapid recovery + +module hmr_rr_wrapper import hmr_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, + /// 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, + /// 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, + parameter type reg_req_t = logic, + parameter type reg_rsp_t = logic, + /// Rapid recovery structure + parameter type rapid_recovery_t = logic, + 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, + // 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, + /// Number of RapidRecover Units + localparam int unsigned NumRRUnits = + max(DMRSupported || DMRFixed ? NumDMRGroups : 0, TMRSupported || TMRFixed ? NumTMRGroups : 0) +) ( + 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 [ NumCores-1:0] tmr_core_en_o, + output logic [NumTMRGroups-1:0] tmr_failure_o , + output logic [ NumCores-1:0] tmr_error_o , + 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 [ NumCores-1:0] dmr_core_en_o, + output logic [NumDMRGroups-1:0] dmr_failure_o , + 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, + + // 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, + 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 +); + + function automatic int tmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumTMRGroups; + else return (core_id/3); + endfunction + + function automatic 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 automatic int tmr_shared_id (int group_id); + if (InterleaveGrps || !(DMRSupported || DMRFixed)) return group_id; + else return group_id + group_id/2; + endfunction + + if (DMRSupported && TMRSupported && RapidRecovery && !InterleaveGrps) + $warning("TMR and DMR with RR without interleaving groups may lead to malfunctioning RR ", + "synchronization."); + + function automatic int tmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumTMRGroups; + else return core_id % 3; + endfunction + + function automatic int dmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumDMRGroups; + else return (core_id/2); + endfunction + + function automatic 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 automatic int dmr_shared_id (int group_id); + return group_id; + endfunction + + function automatic int dmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumDMRGroups; + else return core_id % 2; + endfunction + + logic [NumDMRGroups-1:0] dmr_failure_hmr, dmr_failure_rr; + logic [NumTMRGroups-1:0] tmr_failure_hmr, tmr_failure_rr; + logic [ NumCores-1:0] tmr_error_hmr; + logic [NumTMRGroups-1:0][2:0] tmr_error_rr; + logic [NumCores-1:0] hmr_setback; + + rr_ctrl_t [NumRRUnits-1:0] rr_ctrl; + rr_status_t [NumRRUnits-1:0] rr_status; + + hmr_unit #( + .NumCores ( NumCores ), + .DMRSupported ( DMRSupported ), + .DMRFixed ( DMRFixed ), + .TMRSupported ( TMRSupported ), + .TMRFixed ( TMRFixed ), + .InterleaveGrps ( InterleaveGrps ), + .SeparateData ( SeparateData ), + .NumBusVoters ( NumBusVoters ), + .all_inputs_t ( all_inputs_t ), + .nominal_outputs_t( nominal_outputs_t ), + .bus_outputs_t ( bus_outputs_t ), + .reg_req_t ( reg_req_t ), + .reg_rsp_t ( reg_rsp_t ) + ) i_hmr ( + .clk_i, + .rst_ni, + + .reg_request_i (), + .reg_response_o (), + + .tmr_core_en_o, + .tmr_failure_o ( tmr_failure_hmr ), + .tmr_error_o ( tmr_error_hmr ), + .tmr_resynch_req_o, + .tmr_sw_synch_req_o, + .tmr_cores_synch_i, + + .dmr_core_en_o, + .dmr_failure_o ( dmr_failure_hmr ), + .dmr_resynch_req_o, + .dmr_sw_synch_req_o, + .dmr_cores_synch_i, + + .rr_ctrl_o ( rr_ctrl ), + .rr_status_i ( rr_status ), + + .sys_inputs_i, + .sys_nominal_outputs_o, + .sys_bus_outputs_o, + .sys_fetch_en_i, + .enable_bus_vote_i, + + .core_setback_o ( hmr_setback ), + .core_inputs_o, + .core_nominal_outputs_i, + .core_bus_outputs_i + ); + + core_backup_t [ NumDMRGroups-1:0] dmr_backup_outputs; + core_backup_t [ NumTMRGroups-1:0] tmr_backup_outputs; + // logic [ NumDMRGroups-1:0][SysDataWidth-1:0] checkpoint_reg_q; + + logic [ NumDMRGroups-1:0] dmr_recovery_start, dmr_recovery_finished; + logic [ NumTMRGroups-1:0] tmr_recovery_start, tmr_recovery_finished; + logic [NumRRUnits-1:0] rapid_recovery_start, rapid_recovery_finished; + logic [NumRRUnits-1:0] rapid_recovery_backup_en_inp, rapid_recovery_backup_en_oup; + logic [NumRRUnits-1:0] rapid_recovery_setback; + rapid_recovery_t [NumRRUnits-1:0] rapid_recovery_bus; + core_backup_t [NumRRUnits-1:0] rapid_recovery_backup_bus; + nominal_outputs_t [NumRRUnits-1:0] rapid_recovery_nominal; + + if (RapidRecovery) begin : gen_rapid_recovery_hw + if (DMRSupported) begin : gen_dmr_rr + for (genvar i = 0; i < NumDMRGroups; i++) begin : gen_dmr_rr + DMR_checker #( + .DataWidth( $bits(core_backup_t) ), + .Pipeline ( 1 ) + ) i_dmr_core_checker_backup ( + .clk_i, + .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_rr [ i ] ) + ); + assign dmr_failure_o[i] = (dmr_core_en_o[dmr_core_id(i, 0)] && + rr_ctrl[dmr_shared_id(i)].rr_enable) ? + dmr_failure_hmr[i] | dmr_failure_rr[i] : + dmr_failure_hmr[i]; + end + end else begin : gen_no_dmr_rr + assign dmr_backup_outputs = '0; + assign dmr_failure_rr = '0; + assign dmr_failure_o = dmr_failure_hmr; + end + if (TMRSupported) begin : gen_tmr_rr + for (genvar i = 0; i < NumTMRGroups; i++) begin : gen_tmr_rr + bitwise_TMR_voter #( + .DataWidth( $bits(core_backup_t) ) + ) i_tmr_core_checker_backup ( + .a_i ( core_backup_i [tmr_core_id(i, 0)] ), + .b_i ( core_backup_i [tmr_core_id(i, 1)] ), + .c_i ( core_backup_i [tmr_core_id(i, 2)] ), + .majority_o ( tmr_backup_outputs [ i ] ), + .error_o ( tmr_failure_rr [ i ] ), + .error_cba_o( tmr_error_rr [ i ] ) + ); + assign tmr_failure_o[i] = (tmr_core_en_o[tmr_core_id(i, 0)] && + rr_ctrl[tmr_shared_id(i)].rr_enable) ? + tmr_failure_hmr[i] | tmr_failure_rr[i] : + tmr_failure_hmr[i]; + for (genvar j = 0; j < 3; j++) begin : gen_tmr_error + assign tmr_error_o[tmr_core_id(i,j)] = (tmr_core_en_o[tmr_core_id(i, j)] && + rr_ctrl[tmr_shared_id(i)].rr_enable) ? + tmr_error_hmr[tmr_core_id(i,j)] | tmr_error_rr[i][j] : + tmr_error_hmr[tmr_core_id(i,j)]; + end + end + for (genvar i = NumTMRCores; i < NumCores; i++) begin : gen_remaining_core_err + assign tmr_error_o[i] = '0; + end + end else begin : gen_no_tmr_rr + assign tmr_backup_outputs = '0; + assign tmr_failure_rr = '0; + assign tmr_error_rr = '0; + assign tmr_failure_o = tmr_failure_hmr; + assign tmr_error_o = tmr_error_hmr; + end + for (genvar i = 0; i < NumRRUnits; i++) begin : gen_rapid_recovery + assign rapid_recovery_backup_en_inp[i] = tmr_core_en_o[tmr_core_id(i, 0)] ? + (i < NumTMRGroups ? rapid_recovery_backup_en_oup[i] : 1'b0) : // TMR mode + dmr_core_en_o[dmr_core_id(i, 0)] ? + (rapid_recovery_backup_en_oup[i] & ~dmr_failure_o[i] ) : // DMR mode + 1'b1; // Disabled + + assign rr_status[i].rr_error = tmr_core_en_o[tmr_core_id(i, 0)] ? |tmr_error_rr[i] : + dmr_core_en_o[dmr_core_id(i, 0)] ? dmr_failure_rr[i] : + '0; + + rapid_recovery_unit #( + .RfAddrWidth ( RfAddrWidth ), + .DataWidth ( SysDataWidth ), + .EccEnabled ( EccEnabled ), + .regfile_write_t( regfile_write_t ), + .regfile_raddr_t( regfile_raddr_t ), + .regfile_rdata_t( regfile_rdata_t ), + .csr_intf_t ( csr_intf_t ), + .pc_intf_t ( pc_intf_t ) + ) i_rr_unit ( + .clk_i, + .rst_ni, + + .core_in_independent_i ( ~dmr_core_en_o[dmr_core_id(i, 0)] & + ~tmr_core_en_o[tmr_core_id(i, 0)] ), + .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 ( rr_ctrl[i].recovery_start ), + .backup_enable_o ( rapid_recovery_backup_en_oup[i] ), + .recovery_finished_o ( rr_status[i].recovery_finished ), + .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 ) + ); + end + + always_comb begin : proc_rr_connection + rapid_recovery_nominal = '0; + rapid_recovery_backup_bus = '0; + for (int i = 0; i < NumDMRGroups; i++) begin + if ((DMRFixed || (DMRSupported && dmr_core_en_o[dmr_core_id(i, 0)])) && + rr_ctrl[dmr_shared_id(i)].rr_enable) begin + rapid_recovery_nominal [dmr_shared_id(i)] = sys_nominal_outputs_o[dmr_core_id(i,0)]; + rapid_recovery_backup_bus[dmr_shared_id(i)] = dmr_backup_outputs [ i ]; + end + end + for (int i = 0; i < NumTMRGroups; i++) begin + if ((TMRFixed || (TMRSupported && tmr_core_id[tmr_core_id(i, 0)])) && + rr_ctrl[tmr_shared_id(i)].rr_enable) begin + rapid_recovery_nominal [tmr_shared_id(i)] = sys_nominal_outputs_o[tmr_core_id(i,0)]; + rapid_recovery_backup_bus[tmr_shared_id(i)] = tmr_backup_outputs [ i ]; + end + end + end + + if (DMRSupported && TMRSupported) begin : gen_full_HMR + /***************** + *** TMR & DMR *** + *****************/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + always_comb begin + rapid_recovery_o[i] = + dmr_core_en_o [i] ? rapid_recovery_bus[dmr_shared_id(dmr_group_id(i))] : + (tmr_core_en_o[i] ? rapid_recovery_bus[tmr_shared_id(tmr_group_id(i))] : '0); + core_setback_o [i] = hmr_setback [i] | + (dmr_core_en_o[i] ? rapid_recovery_setback[dmr_shared_id(dmr_group_id(i))] : + (tmr_core_en_o[i] ? rapid_recovery_setback[tmr_shared_id(tmr_group_id(i))] : '0)); + end + end + end else if (TMRSupported || TMRFixed) begin : gen_TMR_only + /*********** + *** TMR *** + ***********/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + always_comb begin + rapid_recovery_o[i] = tmr_core_en_o [i] ? + rapid_recovery_bus[tmr_shared_id(tmr_group_id(i))] : '0; + core_setback_o [i] = hmr_setback [i] | + (tmr_core_en_o[i] ? + rapid_recovery_setback [tmr_shared_id(tmr_group_id(i))] : '0); + end + end + + end else if (DMRSupported || DMRFixed) begin : gen_DMR_only + /***************** + *** DMR *** + *****************/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + always_comb begin + rapid_recovery_o[i] = dmr_core_en_o [i] ? + rapid_recovery_bus[dmr_shared_id(dmr_group_id(i))] : '0; + core_setback_o [i] = hmr_setback [i] | + (dmr_core_en_o[i] ? + rapid_recovery_setback [dmr_shared_id(dmr_group_id(i))] : '0); + end + end + end else begin : gen_no_redundancy + assign rapid_recovery_o = '0; + assign core_setback_o = hmr_setback; + end + end else begin : gen_no_rr + assign dmr_failure_o = dmr_failure_hmr; + assign tmr_failure_o = tmr_failure_hmr; + assign tmr_error_o = tmr_error_hmr; + assign core_setback_o = hmr_setback; + assign rapid_recovery_nominal = '0; + assign rapid_recovery_backup_bus = '0; + assign rapid_recovery_start = '0; + assign dmr_recovery_finished = '0; + assign tmr_recovery_finished = '0; + end + +endmodule diff --git a/rtl/HMR/hmr_tmr_ctrl.sv b/rtl/HMR/hmr_tmr_ctrl.sv new file mode 100644 index 00000000..33faaa88 --- /dev/null +++ b/rtl/HMR/hmr_tmr_ctrl.sv @@ -0,0 +1,248 @@ +// 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, + input logic synch_req_q_i, + input logic synch_req_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, + input logic bus_resp_ok_i, + output logic bus_hold_o +); + + 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 || ~RapidRecovery; + assign tmr_hw2reg.tmr_config.rapid_recovery.d = rapid_recovery_q_i && RapidRecovery; + assign tmr_hw2reg.tmr_config.synch_req.d = synch_req_q_i; + assign tmr_hw2reg.tmr_config.synch_req.de = synch_req_qe_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; + bus_hold_o = 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: begin end // 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 = tmr_reg2hw.tmr_config.synch_req.q; + 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 + bus_hold_o = 1'b1; + if (bus_resp_ok_i) begin + if (tmr_reg2hw.tmr_config.setback.q) begin + setback_o = 3'b110; + end + tmr_red_mode_d = NON_TMR; + end + 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_unit.sv b/rtl/HMR/hmr_unit.sv new file mode 100644 index 00000000..245e0da4 --- /dev/null +++ b/rtl/HMR/hmr_unit.sv @@ -0,0 +1,835 @@ +// 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 import hmr_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, + /// Interleave DMR/TMR cores, alternatively with sequential grouping + parameter bit InterleaveGrps = 1'b1, + /// Rapid Recovery support - requires external RR wrapper + 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, + /// Number of separate voters/checkers for individual buses + parameter int unsigned NumBusVoters = 1, + /// General core inputs wrapping struct + parameter type all_inputs_t = logic, + /// General core outputs wrapping struct + parameter type nominal_outputs_t = logic, + /// Bus outputs wrapping struct + parameter type bus_outputs_t = logic, + parameter type reg_req_t = logic, + parameter type reg_rsp_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, + /// Number of RapidRecover Units + localparam int unsigned NumRRUnits = + max(DMRSupported || DMRFixed ? NumDMRGroups : 0, TMRSupported || TMRFixed ? NumTMRGroups : 0) +) ( + 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 [ NumCores-1:0] tmr_core_en_o, + output logic [NumTMRGroups-1:0] tmr_failure_o, + output logic [ NumCores-1:0] tmr_error_o, + 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 [ NumCores-1:0] dmr_core_en_o, + output logic [NumDMRGroups-1:0] dmr_failure_o, + 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, + + // RR connections - don't connect / tie '0 if not used + output rr_ctrl_t [NumRRUnits-1:0] rr_ctrl_o, + input rr_status_t [NumRRUnits-1:0] rr_status_i, + + // Boot address is handled apart from other signals + 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, + 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] 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 +); + + function automatic int tmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumTMRGroups; + else return (core_id/3); + endfunction + + function automatic 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 automatic int tmr_shared_id (int group_id); + if (InterleaveGrps || !(DMRSupported || DMRFixed)) return group_id; + else return group_id + group_id/2; + endfunction + + function automatic int tmr_offset_id (int core_id); + if (InterleaveGrps) return core_id / NumTMRGroups; + else return core_id % 3; + endfunction + + function automatic int dmr_group_id (int core_id); + if (InterleaveGrps) return core_id % NumDMRGroups; + else return (core_id/2); + endfunction + + function automatic 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 automatic int dmr_shared_id (int group_id); + return group_id; + endfunction + + function automatic 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; + + 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_backup; + logic [NumDMRGroups-1:0][NumBusVoters-1:0] dmr_failure_data; + + /*************************** + * 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 [NumDMRGroups-1:0] dmr_recovery_start; + logic [NumDMRGroups-1:0] dmr_recovery_finished; + + 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 [NumTMRGroups-1:0] tmr_recovery_start; + logic [NumTMRGroups-1:0] tmr_recovery_finished; + + 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; + + assign dmr_core_en_o[i] = core_in_dmr[i]; + assign tmr_core_en_o[i] = core_in_tmr[i]; + end + + if (RapidRecovery) begin : gen_rr_connection + always_comb begin : proc_rr_connection + rr_ctrl_o = '0; + dmr_recovery_finished = '0; + tmr_recovery_finished = '0; + for (int i = 0; i < NumDMRGroups; i++) begin + if (DMRFixed || (DMRSupported && core_in_dmr[dmr_core_id(i, 0)])) begin + rr_ctrl_o[dmr_shared_id(i)].rr_enable = dmr_core_rapid_recovery_en[dmr_core_id(i, 0)]; + if (dmr_core_rapid_recovery_en[dmr_core_id(i, 0)]) begin + rr_ctrl_o[dmr_shared_id(i)].recovery_start = dmr_recovery_start[i]; + dmr_recovery_finished[i] = rr_status_i[dmr_shared_id(i)].recovery_finished; + end + end + end + for (int i = 0; i < NumTMRGroups; i++) begin + if (TMRFixed || (TMRSupported && core_in_tmr[tmr_core_id(i, 0)])) begin + rr_ctrl_o[tmr_shared_id(i)].rr_enable = tmr_core_rapid_recovery_en[tmr_core_id(i, 0)]; + if (tmr_core_rapid_recovery_en[tmr_core_id(i, 0)]) begin + rr_ctrl_o[tmr_shared_id(i)].recovery_start = tmr_recovery_start[i]; + tmr_recovery_finished[i] = rr_status_i[tmr_shared_id(i)].recovery_finished; + end + end + end + end + 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 int unsigned TMRSelWidth = $clog2(NumTMRGroups); + + /*************** + * Registers * + ***************/ + if (NumTMRGroups == 1) begin : gen_one_tmr_reg + assign tmr_register_reqs[0] = top_register_reqs[3]; + assign top_register_resps[3] = tmr_register_resps[0]; + end else begin : gen_tmr_reg_demux + 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] ), + .bus_resp_ok_i ('0), + .bus_hold_o () + ); + + 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 : gen_data_voters + 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 = '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_rsp_t [NumDMRGroups-1:0] dmr_register_resps; + logic [NumDMRGroups-1:0] dmr_sw_synch_req; + + localparam int unsigned DMRSelWidth = $clog2(NumDMRGroups); + + /*************** + * Registers * + ***************/ + if (NumDMRGroups == 1) begin : gen_one_dmr_reg + assign dmr_register_reqs[0] = top_register_reqs[2]; + assign top_register_resps[2] = dmr_register_resps[0]; + end else begin : gen_dmr_reg_demux + 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 ( '0),//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] ), + .bus_resp_ok_i ('0), + .bus_hold_o () + ); + + 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 ( $bits(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 (SeparateData) begin : gen_data_checker + for (genvar j = 0; j < NumBusVoters; j++) begin : gen_data_checkers + DMR_checker # ( + .DataWidth ( $bits(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 + end + end else begin: gen_no_dmr_checkers + assign dmr_failure_main = '0; + assign dmr_failure_data = '0; + assign dmr_failure = '0; + assign dmr_incr_mismatches = '0; + assign dmr_nominal_outputs = '0; + assign dmr_bus_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 + + for (genvar i = 0; i < NumDMRGroups; i++) begin : gen_dmr_error_o + assign dmr_failure_o[i] = core_in_dmr[dmr_core_id(i, 0)] ? dmr_failure[i] : '0; + end + for (genvar i = 0; i < NumTMRGroups; i++) begin : gen_tmr_error_o + assign tmr_failure_o[i] = core_in_tmr[tmr_core_id(i, 0)] ? tmr_failure[i] : '0; + assign tmr_error_o[3*i ] = core_in_tmr[tmr_core_id(i, 0)] ? tmr_error [i][0] : '0; + assign tmr_error_o[3*i+1] = core_in_tmr[tmr_core_id(i, 1)] ? tmr_error [i][1] : '0; + assign tmr_error_o[3*i+2] = core_in_tmr[tmr_core_id(i, 2)] ? tmr_error [i][2] : '0; + end + if (NumCores > NumTMRCores) begin : gen_extra_error_o + assign tmr_error_o[NumCores-1:NumTMRCores] = '0; + 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 int unsigned TMRCoreIndex = tmr_core_id(tmr_group_id(i), 0); + localparam int unsigned 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 (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)]; + end else if (i >= NumTMRCores && i < NumDMRCores) begin + core_setback_o [i] = dmr_setback_q [dmr_group_id(i)][dmr_offset_id(i)]; + 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 && 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 int unsigned TMRCoreIndex = tmr_group_id(i); + localparam int unsigned 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]; + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o[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 + sys_nominal_outputs_o[i] = dmr_nominal_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; + 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 else if (TMRSupported || TMRFixed) begin : gen_TMR_only + /***************** + *** TMR only *** + *****************/ + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam int unsigned 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 (i >= NumTMRCores) begin + core_setback_o [i] = '0; + end else begin + core_setback_o [i] = tmr_setback_q [tmr_group_id(i)]; + 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 int unsigned CoreCoreIndex = TMRFixed ? i : tmr_group_id(i); + if (TMRFixed && i < NumTMRGroups) begin : gen_fixed_tmr + assign sys_nominal_outputs_o[i] = tmr_nominal_outputs[CoreCoreIndex]; + assign sys_bus_outputs_o [i] = tmr_bus_outputs [CoreCoreIndex]; + end else begin : gen_switchable_tmr + if (i >= NumTMRCores) begin : gen_independent_stragglers + assign sys_nominal_outputs_o[i] = TMRFixed ? + core_nominal_outputs_i[i-NumTMRGroups+NumTMRCores] : + core_nominal_outputs_i[i]; + assign sys_bus_outputs_o [i] = TMRFixed ? + core_bus_outputs_i [i-NumTMRGroups+NumTMRCores] : + core_bus_outputs_i [i]; + end else begin : gen_tmr_core + 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 + 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; + 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 core added not properly handled! :)"); + // Binding DMR outputs to zero for now + // assign dmr_resynch_req_o = '0; + + for (genvar i = 0; i < NumCores; i++) begin : gen_core_inputs + localparam int unsigned 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 (i >= NumDMRCores) begin + core_setback_o [i] = '0; + end else begin + core_setback_o [i] = dmr_setback_q[dmr_group_id(i)][dmr_offset_id(i)]; + 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 int unsigned CoreCoreIndex = DMRFixed ? i : dmr_group_id(i); + if (DMRFixed && i < NumDMRGroups) begin : gen_fixed_dmr + assign sys_nominal_outputs_o[i] = dmr_nominal_outputs[CoreCoreIndex]; + assign sys_bus_outputs_o [i] = dmr_bus_outputs [CoreCoreIndex]; + end else begin : gen_switchable_dmr + if (i >= NumDMRCores) begin : gen_independent_stragglers + assign sys_nominal_outputs_o[i] = DMRFixed ? + core_nominal_outputs_i[i-NumDMRGroups+NumDMRCores] : + core_nominal_outputs_i[i]; + assign sys_bus_outputs_o [i] = DMRFixed ? + core_bus_outputs_i [i-NumDMRGroups+NumDMRCores] : + core_bus_outputs_i [i]; + end else begin : gen_dmr_core + 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]; + end else begin : disable_core // Assign disable + sys_nominal_outputs_o[i] = '0; + sys_bus_outputs_o [i] = '0; + 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 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; + end + +endmodule diff --git a/rtl/HMR/rapid_recovery/DMR_address_generator.sv b/rtl/HMR/rapid_recovery/DMR_address_generator.sv new file mode 100644 index 00000000..29e42c0e --- /dev/null +++ b/rtl/HMR/rapid_recovery/DMR_address_generator.sv @@ -0,0 +1,67 @@ +// 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. +// +// Dual Modular Address Generator +// Generates addresses for RF refill + +module DMR_address_generator #( + parameter int unsigned 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; + +for (genvar i = 0; i < NumVotingSignals; i++) begin : gen_addr_counters + 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 + +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/rapid_recovery/hmr_rapid_recovery_ctrl.sv b/rtl/HMR/rapid_recovery/hmr_rapid_recovery_ctrl.sv new file mode 100644 index 00000000..4efa3a06 --- /dev/null +++ b/rtl/HMR/rapid_recovery/hmr_rapid_recovery_ctrl.sv @@ -0,0 +1,144 @@ +// 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 + default: begin end // Nothing + 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/rapid_recovery/rapid_recovery_pkg.sv b/rtl/HMR/rapid_recovery/rapid_recovery_pkg.sv new file mode 100644 index 00000000..4d27622d --- /dev/null +++ b/rtl/HMR/rapid_recovery/rapid_recovery_pkg.sv @@ -0,0 +1,84 @@ +// 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. +// +// 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/rapid_recovery_unit.sv b/rtl/HMR/rapid_recovery/rapid_recovery_unit.sv new file mode 100644 index 00000000..8290e572 --- /dev/null +++ b/rtl/HMR/rapid_recovery/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/rapid_recovery/recovery_csr.sv b/rtl/HMR/rapid_recovery/recovery_csr.sv new file mode 100644 index 00000000..f77dce6f --- /dev/null +++ b/rtl/HMR/rapid_recovery/recovery_csr.sv @@ -0,0 +1,150 @@ +// 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. +// +// 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/rapid_recovery/recovery_pc.sv b/rtl/HMR/rapid_recovery/recovery_pc.sv new file mode 100644 index 00000000..0d424176 --- /dev/null +++ b/rtl/HMR/rapid_recovery/recovery_pc.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. +// +// 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/rapid_recovery/recovery_rf.sv b/rtl/HMR/rapid_recovery/recovery_rf.sv new file mode 100644 index 00000000..8f06c7a2 --- /dev/null +++ b/rtl/HMR/rapid_recovery/recovery_rf.sv @@ -0,0 +1,201 @@ +// 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. +// +// Register File for RR with ECC + +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 int unsigned NumWords = 2 ** (ADDR_WIDTH - 1); + // number of floating point registers + localparam int unsigned NumFPWords = 2 ** (ADDR_WIDTH - 1); + localparam int unsigned NumTotWords = FPU ? + (PULP_ZFINX ? NumWords : NumWords + NumFPWords) : + NumWords; + + // integer register file + logic [NumWords-1:0][NonProtectedWidth-1:0] mem; + logic [NumWords-1:0][ DataWidth-1:0] ecc_mem; + // fp register file + logic [NumFPWords-1:0][NonProtectedWidth-1:0] mem_fp; + logic [NumFPWords-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 [NumTotWords-1:0] we_a_dec; + logic [NumTotWords-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 < NumWords; index++) begin : gen_secded + 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 : gen_fp + for (genvar index = 0; index < NumFPWords; index++) begin : gen_fp_secded + 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 : gen_no_ecc_region + assign wdata_a = wdata_a_i; + assign wdata_b = wdata_b_i; + + for (genvar index = 0; index < NumWords; index++) + assign mem [index] = ecc_mem [index]; + + for (genvar index = 0; index < NumFPWords; 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 < NumTotWords; 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 NumWords-1 as R0 is nil + for (i = 1; i < NumWords; 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 < NumFPWords; l++) begin : gen_fp_rf + 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+NumWords] == 1'b1) ecc_mem_fp[l] <= wdata_b; + else if (we_a_dec[l+NumWords] == 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/rapid_recovery/recovery_rf_latch.sv b/rtl/HMR/rapid_recovery/recovery_rf_latch.sv new file mode 100644 index 00000000..5db59761 --- /dev/null +++ b/rtl/HMR/rapid_recovery/recovery_rf_latch.sv @@ -0,0 +1,243 @@ +// 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. +// +// Latch-based Register File for RR with ECC + +module recovery_rf_latch #( + 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 int unsigned NumWords = 2 ** (ADDR_WIDTH - 1); + // number of floating point registers + localparam int unsigned NumFPWords = 2 ** (ADDR_WIDTH - 1); + localparam int unsigned NumTotWords = FPU ? + (PULP_ZFINX ? NumWords : NumWords + NumFPWords) : + NumWords; + + // integer register file + logic [NonProtectedWidth-1:0] mem [NumWords]; + logic [ DataWidth-1:0] ecc_mem [NumWords]; + logic [NumTotWords-1:1] waddr_onehot_a; + logic [NumTotWords-1:1] waddr_onehot_b , + waddr_onehot_b_q; + logic [NumTotWords-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 [NumFPWords]; + logic [ DataWidth-1:0] ecc_mem_fp [NumFPWords]; + + 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 < NumWords; index++) begin : gen_secded + 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 : gen_fp_secded + for (genvar index = 0; index < NumFPWords; index++) begin : gen_fp_secded + 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 : gen_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 < NumWords; index++) + assign mem [index] = ecc_mem [index]; + + for (genvar index = 0; index < NumFPWords; index++) + assign mem_fp [index] = ecc_mem_fp [index]; + end + endgenerate + + //----------------------------------------------------------------------------- + //-- READ : Read address decoder RAD + //----------------------------------------------------------------------------- + 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 + + //----------------------------------------------------------------------------- + // 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 < NumTotWords; 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 < NumTotWords; 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 < NumWords; 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 : gen_fp_rf + // Floating point registers + always_latch begin : latch_wdata_fp + if (FPU == 1) begin + for (l = 0; l < NumFPWords; l++) begin : w_WordIter + if (~rst_ni) ecc_mem_fp[l] = '0; + else if (mem_clocks[l+NumWords] == 1'b1) + ecc_mem_fp[l] = waddr_onehot_b_q[l+NumWords] ? wdata_b_q : wdata_a_q; + end + end + end + end +endmodule diff --git a/rtl/HMR/regs/HMR_core_regs.hjson b/rtl/HMR/regs/HMR_core_regs.hjson new file mode 100644 index 00000000..c55f5ede --- /dev/null +++ b/rtl/HMR/regs/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/regs/HMR_dmr_regs.hjson b/rtl/HMR/regs/HMR_dmr_regs.hjson new file mode 100644 index 00000000..7bee4f61 --- /dev/null +++ b/rtl/HMR/regs/HMR_dmr_regs.hjson @@ -0,0 +1,66 @@ +{ + 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)." + }, + { bits: "2", + name: "setback", + desc: "Enable setback (synchronous reset) during re-synch. REQUIRED 1!", + resval: "1" + }, + { bits: "3", + name: "synch_req", + resval: "1", + desc: "Sends synchronization IRQ if core is requesting synch" + } + ] + }, + { 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/regs/HMR_regs.hjson b/rtl/HMR/regs/HMR_regs.hjson new file mode 100644 index 00000000..e0118c68 --- /dev/null +++ b/rtl/HMR/regs/HMR_regs.hjson @@ -0,0 +1,156 @@ +{ + 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)." + }, + { bits: "2", + name: "setback", + resval: "1", + desc: "Enable setback (synchronous reset) during re-synch. REQUIRED 1!" + }, + { bits: "3", + name: "synch_req", + resval: "1", + desc: "Sends synchronization IRQ if core is requesting synch" + } + ] + }, + { 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" + }, + { bits: "5", + name: "synch_req", + resval: "1", + desc: "Sends synchronization IRQ if core is requesting synch" + } + ] + } + ] +} diff --git a/rtl/HMR/regs/HMR_tmr_regs.hjson b/rtl/HMR/regs/HMR_tmr_regs.hjson new file mode 100644 index 00000000..299c9904 --- /dev/null +++ b/rtl/HMR/regs/HMR_tmr_regs.hjson @@ -0,0 +1,65 @@ +{ + 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" + }, + { bits: "5", + name: "synch_req", + resval: "1", + desc: "Sends synchronization IRQ if core is requesting synch" + } + ] + } + ] +} diff --git a/rtl/HMR/regs/doc.html b/rtl/HMR/regs/doc.html new file mode 100644 index 00000000..92cd4bf3 --- /dev/null +++ b/rtl/HMR/regs/doc.html @@ -0,0 +1,125 @@ +## Summary + +| Name | Offset | Length | Description | +|:----------------------------------------------|:---------|---------:|:---------------------------------------------------------------------| +| HMR_registers.[`avail_config`](#avail_config) | 0x0 | 4 | Available Configurations from implemented hardware. | +| HMR_registers.[`cores_en`](#cores_en) | 0x4 | 4 | Enabled cores, based on the configuration. Can be used for barriers. | +| HMR_registers.[`DMR_enable`](#dmr_enable) | 0x8 | 4 | DMR configuration enable, on bit per DMR group. | +| HMR_registers.[`TMR_enable`](#tmr_enable) | 0xc | 4 | TMR configuration enable, one bit per TMR group. | +| HMR_registers.[`DMR_config`](#dmr_config) | 0x10 | 4 | DMR configuration bits. | +| HMR_registers.[`TMR_config`](#tmr_config) | 0x14 | 4 | TMR configuration bits. | + +## avail_config +Available Configurations from implemented hardware. +- Offset: `0x0` +- Reset default: `0x0` +- Reset mask: `0x107` + +### Fields + +```wavejson +{"reg": [{"name": "independent", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "dual", "bits": 1, "attr": ["ro"], "rotate": -90}, {"name": "triple", "bits": 1, "attr": ["ro"], "rotate": -90}, {"bits": 5}, {"name": "rapid_recovery", "bits": 1, "attr": ["ro"], "rotate": -90}, {"bits": 23}], "config": {"lanes": 1, "fontsize": 10, "vspace": 160}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:---------------|:----------------------------------------------| +| 31:9 | | | | Reserved | +| 8 | ro | x | rapid_recovery | Rapid Recovery is available. | +| 7:3 | | | | Reserved | +| 2 | ro | x | triple | Triple Modular Redundancy (TMR) is available. | +| 1 | ro | x | dual | Dual Modular Redundancy (DMR) is available. | +| 0 | ro | x | independent | Independent mode is available. | + +## cores_en +Enabled cores, based on the configuration. Can be used for barriers. +- Offset: `0x4` +- Reset default: `0x0` +- Reset mask: `0xfff` + +### Fields + +```wavejson +{"reg": [{"name": "cores_en", "bits": 12, "attr": ["ro"], "rotate": 0}, {"bits": 20}], "config": {"lanes": 1, "fontsize": 10, "vspace": 80}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:---------|:---------------| +| 31:12 | | | | Reserved | +| 11:0 | ro | x | cores_en | Enabled cores. | + +## DMR_enable +DMR configuration enable, on bit per DMR group. +- Offset: `0x8` +- Reset default: `0x0` +- Reset mask: `0x3f` + +### Fields + +```wavejson +{"reg": [{"name": "DMR_enable", "bits": 6, "attr": ["rw"], "rotate": 0}, {"bits": 26}], "config": {"lanes": 1, "fontsize": 10, "vspace": 80}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:-----------|:--------------------------| +| 31:6 | | | | Reserved | +| 5:0 | rw | 0x0 | DMR_enable | DMR configuration enable. | + +## TMR_enable +TMR configuration enable, one bit per TMR group. +- Offset: `0xc` +- Reset default: `0x0` +- Reset mask: `0xf` + +### Fields + +```wavejson +{"reg": [{"name": "TMR_enable", "bits": 4, "attr": ["rw"], "rotate": -90}, {"bits": 28}], "config": {"lanes": 1, "fontsize": 10, "vspace": 120}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:-----------|:--------------------------| +| 31:4 | | | | Reserved | +| 3:0 | rw | 0x0 | TMR_enable | TMR configuration enable. | + +## DMR_config +DMR configuration bits. +- Offset: `0x10` +- Reset default: `0xc` +- Reset mask: `0xf` + +### Fields + +```wavejson +{"reg": [{"name": "rapid_recovery", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "force_recovery", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "setback", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "synch_req", "bits": 1, "attr": ["rw"], "rotate": -90}, {"bits": 28}], "config": {"lanes": 1, "fontsize": 10, "vspace": 160}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:---------------|:----------------------------------------------------------------| +| 31:4 | | | | Reserved | +| 3 | rw | 0x1 | synch_req | Sends synchronization IRQ if core is requesting synch | +| 2 | rw | 0x1 | setback | Enable setback (synchronous reset) during re-synch. REQUIRED 1! | +| 1 | rw | x | force_recovery | Forces recovery routine (if rapid_recovery is available). | +| 0 | rw | x | rapid_recovery | Enable rapid recovery using an additional register file. | + +## TMR_config +TMR configuration bits. +- Offset: `0x14` +- Reset default: `0x26` +- Reset mask: `0x3f` + +### Fields + +```wavejson +{"reg": [{"name": "delay_resynch", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "setback", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "reload_setback", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "rapid_recovery", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "force_resynch", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "synch_req", "bits": 1, "attr": ["rw"], "rotate": -90}, {"bits": 26}], "config": {"lanes": 1, "fontsize": 10, "vspace": 160}} +``` + +| Bits | Type | Reset | Name | Description | +|:------:|:------:|:-------:|:---------------|:--------------------------------------------------------------------------------------------| +| 31:6 | | | | Reserved | +| 5 | rw | 0x1 | synch_req | Sends synchronization IRQ if core is requesting synch | +| 4 | rw | 0x0 | force_resynch | Forces a resynchronization routine | +| 3 | rw | x | rapid_recovery | Enable rapid recovery using additional register file. | +| 2 | rw | 0x1 | reload_setback | Enable setback on mismatch during reload section of re-synch (only possible with `setback`) | +| 1 | rw | 0x1 | setback | Enable setback (synchronous reset) during re-synch. | +| 0 | rw | 0x0 | delay_resynch | Enable wait-for-restoration | + diff --git a/rtl/HMR/regs/doc.md b/rtl/HMR/regs/doc.md new file mode 100644 index 00000000..892ab665 --- /dev/null +++ b/rtl/HMR/regs/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/regs/hmr_core_regs_reg_pkg.sv b/rtl/HMR/regs/hmr_core_regs_reg_pkg.sv new file mode 100644 index 00000000..602bd91a --- /dev/null +++ b/rtl/HMR/regs/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/regs/hmr_core_regs_reg_top.sv b/rtl/HMR/regs/hmr_core_regs_reg_top.sv new file mode 100644 index 00000000..3dc8d1ed --- /dev/null +++ b/rtl/HMR/regs/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 [BlockAw-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[BlockAw-1:0]; + 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/regs/hmr_dmr_regs_reg_pkg.sv b/rtl/HMR/regs/hmr_dmr_regs_reg_pkg.sv new file mode 100644 index 00000000..31aa1b7a --- /dev/null +++ b/rtl/HMR/regs/hmr_dmr_regs_reg_pkg.sv @@ -0,0 +1,108 @@ +// 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; + struct packed { + logic q; + logic qe; + } setback; + struct packed { + logic q; + logic qe; + } synch_req; + } 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; + struct packed { + logic d; + logic de; + } setback; + struct packed { + logic d; + logic de; + } synch_req; + } 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; // [42:41] + hmr_dmr_regs_reg2hw_dmr_config_reg_t dmr_config; // [40: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; // [42:41] + hmr_dmr_regs_hw2reg_dmr_config_reg_t dmr_config; // [40: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/regs/hmr_dmr_regs_reg_top.sv b/rtl/HMR/regs/hmr_dmr_regs_reg_top.sv new file mode 100644 index 00000000..4be54d5d --- /dev/null +++ b/rtl/HMR/regs/hmr_dmr_regs_reg_top.sv @@ -0,0 +1,378 @@ +// 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 [BlockAw-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[BlockAw-1:0]; + 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 dmr_config_setback_qs; + logic dmr_config_setback_wd; + logic dmr_config_setback_we; + logic dmr_config_synch_req_qs; + logic dmr_config_synch_req_wd; + logic dmr_config_synch_req_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) + ); + + + // F[setback]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h1) + ) u_dmr_config_setback ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (dmr_config_setback_we), + .wd (dmr_config_setback_wd), + + // from internal hardware + .de (hw2reg.dmr_config.setback.de), + .d (hw2reg.dmr_config.setback.d ), + + // to internal hardware + .qe (reg2hw.dmr_config.setback.qe), + .q (reg2hw.dmr_config.setback.q ), + + // to register interface (read) + .qs (dmr_config_setback_qs) + ); + + + // F[synch_req]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h1) + ) u_dmr_config_synch_req ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (dmr_config_synch_req_we), + .wd (dmr_config_synch_req_wd), + + // from internal hardware + .de (hw2reg.dmr_config.synch_req.de), + .d (hw2reg.dmr_config.synch_req.d ), + + // to internal hardware + .qe (reg2hw.dmr_config.synch_req.qe), + .q (reg2hw.dmr_config.synch_req.q ), + + // to register interface (read) + .qs (dmr_config_synch_req_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 dmr_config_setback_we = addr_hit[1] & reg_we & !reg_error; + assign dmr_config_setback_wd = reg_wdata[2]; + + assign dmr_config_synch_req_we = addr_hit[1] & reg_we & !reg_error; + assign dmr_config_synch_req_wd = reg_wdata[3]; + + 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; + reg_rdata_next[2] = dmr_config_setback_qs; + reg_rdata_next[3] = dmr_config_synch_req_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/regs/hmr_registers_reg_pkg.sv b/rtl/HMR/regs/hmr_registers_reg_pkg.sv new file mode 100644 index 00000000..9483f08e --- /dev/null +++ b/rtl/HMR/regs/hmr_registers_reg_pkg.sv @@ -0,0 +1,204 @@ +// 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; + struct packed { + logic q; + logic qe; + } setback; + struct packed { + logic q; + logic qe; + } synch_req; + } 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; + struct packed { + logic q; + logic qe; + } synch_req; + } 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; + struct packed { + logic d; + } setback; + struct packed { + logic d; + } synch_req; + } 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; + struct packed { + logic d; + } synch_req; + } hmr_registers_hw2reg_tmr_config_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_registers_reg2hw_dmr_enable_reg_t dmr_enable; // [31:25] + hmr_registers_reg2hw_tmr_enable_reg_t tmr_enable; // [24:20] + hmr_registers_reg2hw_dmr_config_reg_t dmr_config; // [19:12] + hmr_registers_reg2hw_tmr_config_reg_t tmr_config; // [11:0] + } hmr_registers_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_registers_hw2reg_avail_config_reg_t avail_config; // [35:32] + hmr_registers_hw2reg_cores_en_reg_t cores_en; // [31:20] + hmr_registers_hw2reg_dmr_enable_reg_t dmr_enable; // [19:14] + hmr_registers_hw2reg_tmr_enable_reg_t tmr_enable; // [13:10] + hmr_registers_hw2reg_dmr_config_reg_t dmr_config; // [9:6] + hmr_registers_hw2reg_tmr_config_reg_t tmr_config; // [5: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 [3:0] HMR_REGISTERS_DMR_CONFIG_RESVAL = 4'h c; + parameter logic [0:0] HMR_REGISTERS_DMR_CONFIG_SETBACK_RESVAL = 1'h 1; + parameter logic [0:0] HMR_REGISTERS_DMR_CONFIG_SYNCH_REQ_RESVAL = 1'h 1; + parameter logic [5:0] HMR_REGISTERS_TMR_CONFIG_RESVAL = 6'h 26; + 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; + parameter logic [0:0] HMR_REGISTERS_TMR_CONFIG_SYNCH_REQ_RESVAL = 1'h 1; + + // 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/regs/hmr_registers_reg_top.sv b/rtl/HMR/regs/hmr_registers_reg_top.sv new file mode 100644 index 00000000..307faffb --- /dev/null +++ b/rtl/HMR/regs/hmr_registers_reg_top.sv @@ -0,0 +1,588 @@ +// 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 [BlockAw-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[BlockAw-1:0]; + 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 dmr_config_setback_qs; + logic dmr_config_setback_wd; + logic dmr_config_setback_we; + logic dmr_config_setback_re; + logic dmr_config_synch_req_qs; + logic dmr_config_synch_req_wd; + logic dmr_config_synch_req_we; + logic dmr_config_synch_req_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; + logic tmr_config_synch_req_qs; + logic tmr_config_synch_req_wd; + logic tmr_config_synch_req_we; + logic tmr_config_synch_req_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) + ); + + + // F[setback]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_dmr_config_setback ( + .re (dmr_config_setback_re), + .we (dmr_config_setback_we), + .wd (dmr_config_setback_wd), + .d (hw2reg.dmr_config.setback.d), + .qre (), + .qe (reg2hw.dmr_config.setback.qe), + .q (reg2hw.dmr_config.setback.q ), + .qs (dmr_config_setback_qs) + ); + + + // F[synch_req]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_dmr_config_synch_req ( + .re (dmr_config_synch_req_re), + .we (dmr_config_synch_req_we), + .wd (dmr_config_synch_req_wd), + .d (hw2reg.dmr_config.synch_req.d), + .qre (), + .qe (reg2hw.dmr_config.synch_req.qe), + .q (reg2hw.dmr_config.synch_req.q ), + .qs (dmr_config_synch_req_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) + ); + + + // F[synch_req]: 5:5 + prim_subreg_ext #( + .DW (1) + ) u_tmr_config_synch_req ( + .re (tmr_config_synch_req_re), + .we (tmr_config_synch_req_we), + .wd (tmr_config_synch_req_wd), + .d (hw2reg.tmr_config.synch_req.d), + .qre (), + .qe (reg2hw.tmr_config.synch_req.qe), + .q (reg2hw.tmr_config.synch_req.q ), + .qs (tmr_config_synch_req_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 dmr_config_setback_we = addr_hit[4] & reg_we & !reg_error; + assign dmr_config_setback_wd = reg_wdata[2]; + assign dmr_config_setback_re = addr_hit[4] & reg_re & !reg_error; + + assign dmr_config_synch_req_we = addr_hit[4] & reg_we & !reg_error; + assign dmr_config_synch_req_wd = reg_wdata[3]; + assign dmr_config_synch_req_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; + + assign tmr_config_synch_req_we = addr_hit[5] & reg_we & !reg_error; + assign tmr_config_synch_req_wd = reg_wdata[5]; + assign tmr_config_synch_req_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; + reg_rdata_next[2] = dmr_config_setback_qs; + reg_rdata_next[3] = dmr_config_synch_req_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; + reg_rdata_next[5] = tmr_config_synch_req_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/regs/hmr_tmr_regs_reg_pkg.sv b/rtl/HMR/regs/hmr_tmr_regs_reg_pkg.sv new file mode 100644 index 00000000..ce854ff5 --- /dev/null +++ b/rtl/HMR/regs/hmr_tmr_regs_reg_pkg.sv @@ -0,0 +1,109 @@ +// 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; + struct packed { + logic q; + logic qe; + } synch_req; + } 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; + struct packed { + logic d; + logic de; + } synch_req; + } hmr_tmr_regs_hw2reg_tmr_config_reg_t; + + // Register -> HW type + typedef struct packed { + hmr_tmr_regs_reg2hw_tmr_enable_reg_t tmr_enable; // [13:12] + hmr_tmr_regs_reg2hw_tmr_config_reg_t tmr_config; // [11:0] + } hmr_tmr_regs_reg2hw_t; + + // HW -> register type + typedef struct packed { + hmr_tmr_regs_hw2reg_tmr_enable_reg_t tmr_enable; // [13:12] + hmr_tmr_regs_hw2reg_tmr_config_reg_t tmr_config; // [11: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/regs/hmr_tmr_regs_reg_top.sv b/rtl/HMR/regs/hmr_tmr_regs_reg_top.sv new file mode 100644 index 00000000..8c6c37af --- /dev/null +++ b/rtl/HMR/regs/hmr_tmr_regs_reg_top.sv @@ -0,0 +1,405 @@ +// 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 [BlockAw-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[BlockAw-1:0]; + 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; + logic tmr_config_synch_req_qs; + logic tmr_config_synch_req_wd; + logic tmr_config_synch_req_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) + ); + + + // F[synch_req]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h1) + ) u_tmr_config_synch_req ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (tmr_config_synch_req_we), + .wd (tmr_config_synch_req_wd), + + // from internal hardware + .de (hw2reg.tmr_config.synch_req.de), + .d (hw2reg.tmr_config.synch_req.d ), + + // to internal hardware + .qe (reg2hw.tmr_config.synch_req.qe), + .q (reg2hw.tmr_config.synch_req.q ), + + // to register interface (read) + .qs (tmr_config_synch_req_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]; + + assign tmr_config_synch_req_we = addr_hit[1] & reg_we & !reg_error; + assign tmr_config_synch_req_wd = reg_wdata[5]; + + // 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; + reg_rdata_next[5] = tmr_config_synch_req_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/regs/hmr_v1.h b/rtl/HMR/regs/hmr_v1.h new file mode 100644 index 00000000..fc5f8917 --- /dev/null +++ b/rtl/HMR/regs/hmr_v1.h @@ -0,0 +1,192 @@ +/* + * 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 +#define HMR_REGISTERS_DMR_CONFIG_SETBACK_BIT 2 +#define HMR_REGISTERS_DMR_CONFIG_SYNCH_REQ_BIT 3 + +// 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 +#define HMR_REGISTERS_TMR_CONFIG_SYNCH_REQ_BIT 5 + +#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 +#define HMR_DMR_REGS_DMR_CONFIG_SETBACK_BIT 2 +#define HMR_DMR_REGS_DMR_CONFIG_SYNCH_REQ_BIT 3 + +// 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 +#define HMR_TMR_REGS_TMR_CONFIG_SYNCH_REQ_BIT 5 + +#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/resp_suppress.sv b/rtl/HMR/resp_suppress.sv new file mode 100644 index 00000000..d8e46c84 --- /dev/null +++ b/rtl/HMR/resp_suppress.sv @@ -0,0 +1,122 @@ +// 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 bus_hold_i, + output logic resp_ok_o, + + 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 latent_req_enforced_d, latent_req_enforced_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; + + // Number of outstanding is current + additional accepted - completed + assign outstanding_d = outstanding_q + (req_o & gnt_i ? 1 : 0) - (r_valid_i ? 1 : 0); + + // The responses are in OK state if there is no outstanding transmission + assign resp_ok_o = outstanding_q == '0 & ~latent_req_q; + + // Suppress r_valid if there is a latent request signal or we are in block state + assign r_valid_o = block_q || latent_req_enforced_q ? 1'b0 : r_valid_i; + + // Suppress gnt if there is a latent request signal or we are holding the bus + assign gnt_o = latent_req_enforced_q || (bus_hold_i & ~latent_req_q) ? 1'b0 : gnt_i; + + // Request is req_i or latent request (protocol compliance), unless bus is being held + assign req_o = (req_i & ~bus_hold_i) | latent_req_q; + assign we_o = latent_req_enforced_q ? we_q : we_i; + assign addr_o = latent_req_enforced_q ? addr_q : addr_i; + assign data_o = latent_req_enforced_q ? data_q : data_i; + assign be_o = latent_req_enforced_q ? be_q : be_i; + + assign latent_req_d = req_o & ~gnt_i; // clear latent request if we got the grant + + always_comb begin + block_d = block_q; + latent_req_enforced_d = latent_req_enforced_q & ~gnt_i; + if (latent_req_q && !req_i) begin + we_d = we_q; + addr_d = addr_q; + data_d = data_q; + be_d = be_q; + end else begin + we_d = we_i; + addr_d = addr_i; + data_d = data_i; + be_d = be_i; + end + if (ctrl_setback_i) begin + // block if we got a setback + block_d = 1'b1; + // create latent request if requesting on setback call (protocol compliance) + latent_req_enforced_d = (req_i & ~bus_hold_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; // Clear block if there are no more outstanding requests + 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; + latent_req_enforced_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; + latent_req_enforced_q <= latent_req_enforced_d; + we_q <= we_d; + addr_q <= addr_d; + data_q <= data_d; + be_q <= be_d; + end + end + +endmodule diff --git a/rtl/deprecated/HMR_wrap.sv b/rtl/deprecated/HMR_wrap.sv new file mode 100644 index 00000000..22f7e7a5 --- /dev/null +++ b/rtl/deprecated/HMR_wrap.sv @@ -0,0 +1,1978 @@ +// 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 [NumTMRGroups-1:0] tmr_bus_hold; + logic [NumTMRGroups-1:0] tmr_resp_ok; + + 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] dmr_bus_hold; + logic [NumDMRGroups-1:0] dmr_resp_ok; + + + 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; + + logic [NumSysCores-1:0] filt_bus_hold; + logic [NumSysCores-1:0][1:0] filt_resp_ok; + + 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]), + .bus_hold_i (filt_bus_hold[i]), + .resp_ok_o (filt_resp_ok[i][0]), + + .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]), + .resp_ok_o (filt_resp_ok[i][1]), + .bus_hold_i (filt_bus_hold[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 ), + .synch_req_q_i ( hmr_reg2hw.tmr_config.synch_req.q ), + .synch_req_qe_i ( hmr_reg2hw.tmr_config.synch_req.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] ), + .bus_resp_ok_i ( tmr_resp_ok[i] ), + .bus_hold_o ( tmr_bus_hold[i] ) + ); + + assign tmr_resp_ok[i] = &filt_resp_ok[tmr_core_id(i, 0)] & &filt_resp_ok[tmr_core_id(i, 1)] & &filt_resp_ok[tmr_core_id(i, 2)]; + + 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_q_i ( hmr_reg2hw.dmr_config.setback.q ), + .setback_qe_i ( hmr_reg2hw.dmr_config.setback.qe ), + .synch_req_q_i ( hmr_reg2hw.dmr_config.synch_req.q ), + .synch_req_qe_i ( hmr_reg2hw.dmr_config.synch_req.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] ), + .bus_hold_o ( dmr_bus_hold[i] ), + .bus_resp_ok_i ( dmr_resp_ok[i] ) + ); + + assign dmr_resp_ok[i] = &filt_resp_ok[dmr_core_id(i, 0)] & &filt_resp_ok[dmr_core_id(i, 1)]; + + 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 + filt_bus_hold [i] = tmr_bus_hold[tmr_group_id(i)] | dmr_bus_hold[dmr_group_id(i)]; + if (i >= NumTMRCores && i >= NumDMRCores) begin + core_setback_o [i] = '0; + filt_bus_hold [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); + filt_bus_hold [i] = tmr_bus_hold [tmr_group_id(i)]; + 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); + filt_bus_hold [i] = dmr_bus_hold [dmr_group_id(i)]; + 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 + filt_bus_hold[i] = tmr_bus_hold[tmr_group_id(i)]; + if (i >= NumTMRCores) begin + core_setback_o [i] = '0; + tmr_bus_hold[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 + filt_bus_hold[i] = dmr_bus_hold[dmr_group_id(i)]; + if (i >= NumDMRCores) begin + core_setback_o [i] = '0; + filt_bus_hold[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; + assign filt_bus_hold = '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/ODRG_unit/ODRG.h b/rtl/deprecated/ODRG_unit/ODRG.h similarity index 100% rename from rtl/ODRG_unit/ODRG.h rename to rtl/deprecated/ODRG_unit/ODRG.h diff --git a/rtl/ODRG_unit/ODRG_unit.hjson b/rtl/deprecated/ODRG_unit/ODRG_unit.hjson similarity index 100% rename from rtl/ODRG_unit/ODRG_unit.hjson rename to rtl/deprecated/ODRG_unit/ODRG_unit.hjson diff --git a/rtl/ODRG_unit/ODRG_unit.sv b/rtl/deprecated/ODRG_unit/ODRG_unit.sv similarity index 100% rename from rtl/ODRG_unit/ODRG_unit.sv rename to rtl/deprecated/ODRG_unit/ODRG_unit.sv diff --git a/rtl/ODRG_unit/doc.md b/rtl/deprecated/ODRG_unit/doc.md similarity index 100% rename from rtl/ODRG_unit/doc.md rename to rtl/deprecated/ODRG_unit/doc.md diff --git a/rtl/ODRG_unit/odrg_manager_reg_pkg.sv b/rtl/deprecated/ODRG_unit/odrg_manager_reg_pkg.sv similarity index 100% rename from rtl/ODRG_unit/odrg_manager_reg_pkg.sv rename to rtl/deprecated/ODRG_unit/odrg_manager_reg_pkg.sv diff --git a/rtl/ODRG_unit/odrg_manager_reg_top.sv b/rtl/deprecated/ODRG_unit/odrg_manager_reg_top.sv similarity index 100% rename from rtl/ODRG_unit/odrg_manager_reg_top.sv rename to rtl/deprecated/ODRG_unit/odrg_manager_reg_top.sv diff --git a/rtl/ODRG_unit/triple_core_barrier.sv b/rtl/deprecated/ODRG_unit/triple_core_barrier.sv similarity index 100% rename from rtl/ODRG_unit/triple_core_barrier.sv rename to rtl/deprecated/ODRG_unit/triple_core_barrier.sv diff --git a/rtl/ecc_concat_32_64.sv b/rtl/deprecated/ecc_concat_32_64.sv similarity index 100% rename from rtl/ecc_concat_32_64.sv rename to rtl/deprecated/ecc_concat_32_64.sv diff --git a/rtl/ecc_wrap/ecc_sram_wrap.sv b/rtl/deprecated/ecc_sram_wrap.sv similarity index 100% rename from rtl/ecc_wrap/ecc_sram_wrap.sv rename to rtl/deprecated/ecc_sram_wrap.sv diff --git a/rtl/lowrisc_ecc/prim_secded_13_8_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_13_8_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_13_8_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_13_8_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_13_8_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_13_8_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_13_8_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_13_8_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_13_8_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_13_8_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_13_8_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_13_8_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_22_16_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_22_16_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_22_16_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_22_16_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_22_16_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_22_16_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_22_16_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_22_16_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_22_16_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_22_16_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_22_16_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_22_16_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_28_22_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_28_22_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_28_22_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_28_22_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_28_22_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_28_22_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_28_22_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_28_22_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_28_22_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_28_22_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_28_22_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_28_22_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_39_32_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_39_32_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_39_32_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_39_32_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_39_32_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_39_32_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_39_32_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_39_32_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_39_32_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_39_32_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_39_32_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_39_32_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_64_57_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_64_57_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_64_57_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_64_57_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_64_57_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_64_57_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_64_57_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_64_57_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_64_57_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_64_57_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_64_57_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_64_57_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_72_64_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_72_64_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_72_64_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_72_64_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_72_64_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_72_64_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_72_64_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_72_64_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_72_64_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_72_64_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_72_64_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_72_64_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_22_16_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_22_16_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_22_16_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_22_16_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_22_16_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_39_32_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_39_32_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_39_32_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_39_32_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_39_32_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_cor.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_72_64_dec.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_dec.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_72_64_dec.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_dec.sv diff --git a/rtl/lowrisc_ecc/prim_secded_hamming_72_64_enc.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_enc.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_hamming_72_64_enc.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_hamming_72_64_enc.sv diff --git a/rtl/lowrisc_ecc/prim_secded_pkg.sv b/rtl/deprecated/lowrisc_ecc/prim_secded_pkg.sv similarity index 100% rename from rtl/lowrisc_ecc/prim_secded_pkg.sv rename to rtl/deprecated/lowrisc_ecc/prim_secded_pkg.sv diff --git a/rtl/pulpissimo_tcls/TCLS.h b/rtl/deprecated/pulpissimo_tcls/TCLS.h similarity index 100% rename from rtl/pulpissimo_tcls/TCLS.h rename to rtl/deprecated/pulpissimo_tcls/TCLS.h diff --git a/rtl/pulpissimo_tcls/TCLS_unit.hjson b/rtl/deprecated/pulpissimo_tcls/TCLS_unit.hjson similarity index 100% rename from rtl/pulpissimo_tcls/TCLS_unit.hjson rename to rtl/deprecated/pulpissimo_tcls/TCLS_unit.hjson diff --git a/rtl/pulpissimo_tcls/TCLS_unit.sv b/rtl/deprecated/pulpissimo_tcls/TCLS_unit.sv similarity index 100% rename from rtl/pulpissimo_tcls/TCLS_unit.sv rename to rtl/deprecated/pulpissimo_tcls/TCLS_unit.sv diff --git a/rtl/pulpissimo_tcls/doc.md b/rtl/deprecated/pulpissimo_tcls/doc.md similarity index 100% rename from rtl/pulpissimo_tcls/doc.md rename to rtl/deprecated/pulpissimo_tcls/doc.md diff --git a/rtl/pulpissimo_tcls/tcls_manager_reg_pkg.sv b/rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_pkg.sv similarity index 100% rename from rtl/pulpissimo_tcls/tcls_manager_reg_pkg.sv rename to rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_pkg.sv diff --git a/rtl/pulpissimo_tcls/tcls_manager_reg_top.sv b/rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_top.sv similarity index 100% rename from rtl/pulpissimo_tcls/tcls_manager_reg_top.sv rename to rtl/deprecated/pulpissimo_tcls/tcls_manager_reg_top.sv diff --git a/util/lowrisc_opentitan/util/design/data/secded_cfg.hjson b/rtl/deprecated/util/lowrisc_opentitan/util/design/data/secded_cfg.hjson similarity index 100% rename from util/lowrisc_opentitan/util/design/data/secded_cfg.hjson rename to rtl/deprecated/util/lowrisc_opentitan/util/design/data/secded_cfg.hjson diff --git a/util/lowrisc_opentitan/util/design/secded_gen.py b/rtl/deprecated/util/lowrisc_opentitan/util/design/secded_gen.py similarity index 100% rename from util/lowrisc_opentitan/util/design/secded_gen.py rename to rtl/deprecated/util/lowrisc_opentitan/util/design/secded_gen.py diff --git a/util/patches/lowrisc_data_dir/0001-edit-cfg.patch b/rtl/deprecated/util/patches/lowrisc_data_dir/0001-edit-cfg.patch similarity index 100% rename from util/patches/lowrisc_data_dir/0001-edit-cfg.patch rename to rtl/deprecated/util/patches/lowrisc_data_dir/0001-edit-cfg.patch diff --git a/util/patches/lowrisc_secded_gen/0001-Add-Corrector-to-generation-script-and-update-testbe.patch b/rtl/deprecated/util/patches/lowrisc_secded_gen/0001-Add-Corrector-to-generation-script-and-update-testbe.patch similarity index 100% rename from util/patches/lowrisc_secded_gen/0001-Add-Corrector-to-generation-script-and-update-testbe.patch rename to rtl/deprecated/util/patches/lowrisc_secded_gen/0001-Add-Corrector-to-generation-script-and-update-testbe.patch diff --git a/util/patches/lowrisc_secded_gen/0002-Update-SECDED-Encoder-modules-to-prevent-circular-de.patch b/rtl/deprecated/util/patches/lowrisc_secded_gen/0002-Update-SECDED-Encoder-modules-to-prevent-circular-de.patch similarity index 100% rename from util/patches/lowrisc_secded_gen/0002-Update-SECDED-Encoder-modules-to-prevent-circular-de.patch rename to rtl/deprecated/util/patches/lowrisc_secded_gen/0002-Update-SECDED-Encoder-modules-to-prevent-circular-de.patch diff --git a/util/patches/lowrisc_secded_gen/0003-secded-add-iterative-algorithm-for-secded-Hsiao-matr.patch b/rtl/deprecated/util/patches/lowrisc_secded_gen/0003-secded-add-iterative-algorithm-for-secded-Hsiao-matr.patch similarity index 100% rename from util/patches/lowrisc_secded_gen/0003-secded-add-iterative-algorithm-for-secded-Hsiao-matr.patch rename to rtl/deprecated/util/patches/lowrisc_secded_gen/0003-secded-add-iterative-algorithm-for-secded-Hsiao-matr.patch