From 51650c047ff85773b079ba833079c189c12a1417 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Mon, 11 May 2020 17:19:14 +0100 Subject: [PATCH] Use vendored-in primitives from OpenTitan Instead of using copies of primitives from OpenTitan, vendor the files in directly from OpenTitan, and use them. Benefits: - Less potential for diverging code between OpenTitan and Ibex, causing problems when importing Ibex into OT. - Use of the abstract primitives instead of the generic ones. The abstract primitives are replaced during synthesis time with target-dependent implementations. For simulation, nothing changes. For synthesis for a given target technology (e.g. a specific ASIC or FPGA technology), the primitives system can be instructed to choose optimized versions (if available). This is most relevant for the icache, which hard-coded the generic SRAM primitive before. This primitive is always implemented as registers. By using the abstract primitive (prim_ram_1p) instead, the RAMs can be replaced with memory-compiler-generated ones if necessary. There are no real draw-backs, but a couple points to be aware of: - Our ram_1p and ram_2p implementations are kept as wrapper around the primitives, since their interface deviates slightly from the one in prim_ram*. This also includes a rather unfortunate naming confusion around rvalid, which means "read data valid" in the OpenTitan advanced RAM primitives (prim_ram_1p_adv for example), but means "ack" in PULP-derived IP and in our bus implementation. - The core_ibex UVM DV doesn't use FuseSoC to generate its file list, but uses a hard-coded list in `ibex_files.f` instead. Since the dynamic primitives system requires the use of FuseSoC we need to provide a stop-gap until this file is removed. Issue #893 tracks progress on that. - Dynamic primitives depend no a not-yet-merged feature of FuseSoC (https://github.com/olofk/fusesoc/pull/391). We depend on the same functionality in OpenTitan and have instructed users to use a patched branch of FuseSoC for a long time through `python-requirements.txt`, so no action is needed for users which are either successfully interacting with the OpenTitan source code, or have followed our instructions. All other users will see a reasonably descriptive error message during a FuseSoC run. - This commit is massive, but there are no good ways to split it into bisectable, yet small, chunks. I'm sorry. Reviewers can safely ignore all code in `vendor/lowrisc_ip`, it's an import from OpenTitan. - The check_tool_requirements tooling isn't easily vendor-able from OpenTitan at the moment. I've filed https://github.com/lowRISC/opentitan/issues/2309 to get that sorted. - The LFSR primitive doesn't have a own core file, forcing us to include the catch-all `lowrisc:prim:all` core. I've filed https://github.com/lowRISC/opentitan/issues/2310 to get that sorted. --- check_tool_requirements.core | 2 +- .../common/prim/prim_clock_gating.sv | 35 ++ dv/uvm/core_ibex/common/prim/prim_ram_1p.sv | 41 ++ dv/uvm/core_ibex/ibex_dv.f | 25 +- examples/simple_system/ibex_simple_system.cc | 2 +- ibex_core.core | 6 +- rtl/ibex_icache.sv | 8 +- shared/fpga_xilinx.core | 3 +- shared/rtl/prim_clock_gating.sv | 24 -- shared/rtl/prim_generic_ram_1p.sv | 109 ----- shared/rtl/ram_1p.sv | 16 +- shared/rtl/ram_2p.sv | 97 ++--- shared/sim_shared.core | 5 +- util/check_tool_requirements.py | 100 +---- util/ibex_config.py | 4 +- vendor/lowrisc_ip/lint/.gitignore | 3 + vendor/lowrisc_ip/lint/Makefile | 133 ++++++ vendor/lowrisc_ip/lint/common.core | 32 ++ vendor/lowrisc_ip/lint/comportable.core | 24 ++ vendor/lowrisc_ip/lint/data/ascentlint.hjson | 9 + .../lint/data/common_lint_cfg.hjson | 33 ++ vendor/lowrisc_ip/lint/data/lint.mk | 41 ++ vendor/lowrisc_ip/lint/data/veriblelint.hjson | 13 + vendor/lowrisc_ip/lint/doc/README.md | 121 ++++++ .../tools/ascentlint/ascentlint-config.tcl | 9 + .../lint/tools/ascentlint/common.waiver | 11 + .../lint/tools/ascentlint/comportable.waiver | 21 + .../tools/ascentlint/parse-lint-report.py | 125 ++++++ .../tools/veriblelint/parse-lint-report.py | 127 ++++++ .../lint/tools/veriblelint/rules.vbl | 19 + .../lint/tools/verilator/common.vlt | 6 + .../lint/tools/verilator/comportable.vlt | 6 + vendor/lowrisc_ip/lint/util/gen-report.sh | 54 +++ vendor/lowrisc_ip/prim/README.md | 199 +++++++++ vendor/lowrisc_ip/prim/doc/prim_keccak.md | 89 ++++ vendor/lowrisc_ip/prim/doc/prim_lfsr.md | 91 ++++ vendor/lowrisc_ip/prim/doc/prim_packer.md | 106 +++++ vendor/lowrisc_ip/prim/doc/prim_present.md | 70 +++ vendor/lowrisc_ip/prim/doc/prim_prince.md | 112 +++++ vendor/lowrisc_ip/prim/dv/Makefile | 46 ++ vendor/lowrisc_ip/prim/dv/prim_lfsr_sim.core | 28 ++ .../lowrisc_ip/prim/dv/tb/prim_lfsr_bind.sv | 9 + vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_tb.sv | 149 +++++++ .../prim/fpv/prim_alert_rxtx_async_fpv.core | 28 ++ .../prim/fpv/prim_alert_rxtx_fpv.core | 28 ++ .../prim/fpv/prim_arbiter_ppc_fpv.core | 28 ++ .../prim/fpv/prim_arbiter_tree_fpv.core | 28 ++ .../prim/fpv/prim_esc_rxtx_fpv.core | 28 ++ .../prim/fpv/prim_fifo_sync_fpv.core | 27 ++ .../lowrisc_ip/prim/fpv/prim_keccak_fpv.core | 23 + vendor/lowrisc_ip/prim/fpv/prim_lfsr_fpv.core | 26 ++ .../fpv/tb/prim_alert_rxtx_async_bind_fpv.sv | 28 ++ .../prim/fpv/tb/prim_alert_rxtx_async_fpv.sv | 109 +++++ .../prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv | 25 ++ .../prim/fpv/tb/prim_alert_rxtx_fpv.sv | 66 +++ .../prim/fpv/tb/prim_arbiter_ppc_bind_fpv.sv | 25 ++ .../prim/fpv/tb/prim_arbiter_ppc_fpv.sv | 40 ++ .../prim/fpv/tb/prim_arbiter_tree_bind_fpv.sv | 26 ++ .../prim/fpv/tb/prim_arbiter_tree_fpv.sv | 40 ++ .../prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv | 23 + .../prim/fpv/tb/prim_esc_rxtx_fpv.sv | 54 +++ .../prim/fpv/tb/prim_fifo_sync_bind_fpv.sv | 219 ++++++++++ .../prim/fpv/tb/prim_fifo_sync_fpv.sv | 236 +++++++++++ .../lowrisc_ip/prim/fpv/tb/prim_keccak_fpv.sv | 74 ++++ .../lowrisc_ip/prim/fpv/tb/prim_lfsr_fpv.sv | 75 ++++ .../fpv/vip/prim_alert_rxtx_assert_fpv.sv | 92 ++++ .../vip/prim_alert_rxtx_async_assert_fpv.sv | 121 ++++++ .../fpv/vip/prim_arbiter_ppc_assert_fpv.sv | 47 +++ .../fpv/vip/prim_arbiter_tree_assert_fpv.sv | 48 +++ .../prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv | 70 +++ .../prim/fpv/vip/prim_fifo_sync_assert_fpv.sv | 213 ++++++++++ vendor/lowrisc_ip/prim/lint/prim.vlt | 4 + vendor/lowrisc_ip/prim/lint/prim.waiver | 63 +++ .../prim/pre_dv/prim_sync_reqack/README.md | 34 ++ .../cpp/prim_sync_reqack_tb.cc | 62 +++ .../prim_sync_reqack/prim_sync_reqack_tb.core | 52 +++ .../rtl/prim_sync_reqack_tb.sv | 173 ++++++++ vendor/lowrisc_ip/prim/prim.core | 80 ++++ .../lowrisc_ip/prim}/prim_assert.core | 0 vendor/lowrisc_ip/prim/prim_clock_gating.core | 25 ++ vendor/lowrisc_ip/prim/prim_clock_mux2.core | 25 ++ .../lowrisc_ip/prim/prim_diff_decode.core | 8 +- vendor/lowrisc_ip/prim/prim_flash.core | 25 ++ .../lowrisc_ip/prim/prim_gf_mult.core | 9 +- vendor/lowrisc_ip/prim/prim_otp.core | 25 ++ vendor/lowrisc_ip/prim/prim_pad_wrapper.core | 25 ++ vendor/lowrisc_ip/prim/prim_pkg.core | 23 + vendor/lowrisc_ip/prim/prim_ram_1p.core | 25 ++ vendor/lowrisc_ip/prim/prim_ram_1p_adv.core | 19 + vendor/lowrisc_ip/prim/prim_ram_2p.core | 25 ++ vendor/lowrisc_ip/prim/prim_rom.core | 25 ++ .../lowrisc_ip/prim}/prim_secded.core | 2 + vendor/lowrisc_ip/prim/prim_util_memload.core | 17 + vendor/lowrisc_ip/prim/primgen.core | 10 + vendor/lowrisc_ip/prim/rtl/prim_alert_pkg.sv | 17 + .../prim/rtl/prim_alert_receiver.sv | 214 ++++++++++ .../lowrisc_ip/prim/rtl/prim_alert_sender.sv | 236 +++++++++++ .../lowrisc_ip/prim/rtl/prim_arbiter_ppc.sv | 171 ++++++++ .../lowrisc_ip/prim/rtl/prim_arbiter_tree.sv | 224 ++++++++++ .../lowrisc_ip/prim}/rtl/prim_assert.sv | 0 vendor/lowrisc_ip/prim/rtl/prim_cipher_pkg.sv | 363 ++++++++++++++++ .../prim/rtl/prim_clock_gating_sync.sv | 34 ++ .../prim/rtl/prim_clock_inverter.sv | 29 ++ .../lowrisc_ip/prim/rtl/prim_diff_decode.sv | 260 ++++++++++++ vendor/lowrisc_ip/prim/rtl/prim_esc_pkg.sv | 15 + .../lowrisc_ip/prim/rtl/prim_esc_receiver.sv | 180 ++++++++ vendor/lowrisc_ip/prim/rtl/prim_esc_sender.sv | 253 +++++++++++ vendor/lowrisc_ip/prim/rtl/prim_fifo_async.sv | 208 +++++++++ vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv | 155 +++++++ vendor/lowrisc_ip/prim/rtl/prim_filter.sv | 49 +++ vendor/lowrisc_ip/prim/rtl/prim_filter_ctr.sv | 63 +++ vendor/lowrisc_ip/prim/rtl/prim_flop_2sync.sv | 28 ++ vendor/lowrisc_ip/prim/rtl/prim_gate_gen.sv | 123 ++++++ vendor/lowrisc_ip/prim/rtl/prim_gf_mult.sv | 171 ++++++++ vendor/lowrisc_ip/prim/rtl/prim_intr_hw.sv | 36 ++ vendor/lowrisc_ip/prim/rtl/prim_keccak.sv | 295 +++++++++++++ .../lowrisc_ip/prim}/rtl/prim_lfsr.sv | 4 +- vendor/lowrisc_ip/prim/rtl/prim_packer.sv | 255 +++++++++++ vendor/lowrisc_ip/prim/rtl/prim_present.sv | 132 ++++++ vendor/lowrisc_ip/prim/rtl/prim_prince.sv | 181 ++++++++ vendor/lowrisc_ip/prim/rtl/prim_pulse_sync.sv | 66 +++ vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv | 65 +++ vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv | 272 ++++++++++++ .../prim/rtl/prim_ram_2p_async_adv.sv | 280 ++++++++++++ .../prim}/rtl/prim_secded_28_22_dec.sv | 0 .../prim}/rtl/prim_secded_28_22_enc.sv | 0 .../prim/rtl/prim_secded_39_32_dec.sv | 77 ++++ .../prim/rtl/prim_secded_39_32_enc.sv | 62 +++ .../prim}/rtl/prim_secded_72_64_dec.sv | 0 .../prim}/rtl/prim_secded_72_64_enc.sv | 0 .../lowrisc_ip/prim/rtl/prim_sram_arbiter.sv | 128 ++++++ vendor/lowrisc_ip/prim/rtl/prim_subreg.sv | 76 ++++ vendor/lowrisc_ip/prim/rtl/prim_subreg_ext.sv | 28 ++ .../lowrisc_ip/prim/rtl/prim_sync_reqack.sv | 158 +++++++ .../lowrisc_ip/prim/rtl/prim_util_memload.sv | 61 +++ .../lowrisc_ip/prim/util/get-lfsr-coeffs.py | 185 ++++++++ vendor/lowrisc_ip/prim/util/keccak_rc.py | 74 ++++ vendor/lowrisc_ip/prim/util/primgen.py | 399 ++++++++++++++++++ .../prim/util/primgen/abstract_prim.sv.tpl | 22 + .../prim/util/primgen/prim_pkg.core.tpl | 6 +- .../prim/util/primgen/prim_pkg.sv.tpl | 15 + vendor/lowrisc_ip/prim/util/secded_gen.py | 324 ++++++++++++++ .../lint/prim_generic_clock_gating.vlt | 4 + .../lint/prim_generic_clock_gating.waiver | 9 + .../lint/prim_generic_clock_mux2.vlt | 4 + .../lint/prim_generic_clock_mux2.waiver | 5 + .../prim_generic/lint/prim_generic_flash.vlt | 4 + .../lint/prim_generic_flash.waiver | 5 + .../prim_generic/lint/prim_generic_otp.vlt | 4 + .../prim_generic/lint/prim_generic_otp.waiver | 5 + .../lint/prim_generic_pad_wrapper.vlt | 4 + .../lint/prim_generic_pad_wrapper.waiver | 17 + .../prim_generic/lint/prim_generic_ram_1p.vlt | 4 + .../lint/prim_generic_ram_1p.waiver | 12 + .../prim_generic/lint/prim_generic_ram_2p.vlt | 4 + .../lint/prim_generic_ram_2p.waiver | 12 + .../prim_generic/lint/prim_generic_rom.vlt | 4 + .../prim_generic/lint/prim_generic_rom.waiver | 9 + .../prim_generic_clock_gating.core | 42 ++ .../prim_generic/prim_generic_clock_mux2.core | 42 ++ .../prim_generic/prim_generic_flash.core | 44 ++ .../prim_generic/prim_generic_otp.core | 38 ++ .../prim_generic_pad_wrapper.core | 42 ++ .../prim_generic/prim_generic_ram_1p.core | 44 ++ .../prim_generic/prim_generic_ram_2p.core | 42 ++ .../prim_generic/prim_generic_rom.core | 45 ++ .../rtl/prim_generic_clock_gating.sv | 23 + .../rtl/prim_generic_clock_mux2.sv | 22 + .../prim_generic/rtl/prim_generic_flash.sv | 287 +++++++++++++ .../prim_generic/rtl/prim_generic_otp.sv | 110 +++++ .../rtl/prim_generic_pad_wrapper.sv | 53 +++ .../prim_generic/rtl/prim_generic_ram_1p.sv | 58 +++ .../prim_generic/rtl/prim_generic_ram_2p.sv | 87 ++++ .../prim_generic/rtl/prim_generic_rom.sv | 46 ++ .../lint/prim_xilinx_clock_gating.vlt | 4 + .../lint/prim_xilinx_clock_gating.waiver | 4 + .../lint/prim_xilinx_clock_mux2.vlt | 4 + .../lint/prim_xilinx_clock_mux2.waiver | 4 + .../lint/prim_xilinx_pad_wrapper.vlt | 4 + .../lint/prim_xilinx_pad_wrapper.waiver | 12 + .../prim_xilinx/prim_xilinx_clock_gating.core | 42 ++ .../prim_xilinx/prim_xilinx_clock_mux2.core | 42 ++ .../prim_xilinx/prim_xilinx_pad_wrapper.core | 42 ++ .../rtl/prim_xilinx_clock_gating.sv | 12 +- .../prim_xilinx/rtl/prim_xilinx_clock_mux2.sv | 30 ++ .../rtl/prim_xilinx_pad_wrapper.sv | 43 ++ vendor/lowrisc_lint.lock.hjson | 15 + vendor/lowrisc_lint.vendor.hjson | 13 + vendor/lowrisc_prim.lock.hjson | 15 + vendor/lowrisc_prim.vendor.hjson | 13 + vendor/lowrisc_prim_generic.lock.hjson | 15 + vendor/lowrisc_prim_generic.vendor.hjson | 13 + vendor/lowrisc_prim_xilinx.lock.hjson | 15 + vendor/lowrisc_prim_xilinx.vendor.hjson | 13 + 194 files changed, 11904 insertions(+), 342 deletions(-) create mode 100644 dv/uvm/core_ibex/common/prim/prim_clock_gating.sv create mode 100644 dv/uvm/core_ibex/common/prim/prim_ram_1p.sv delete mode 100644 shared/rtl/prim_clock_gating.sv delete mode 100644 shared/rtl/prim_generic_ram_1p.sv create mode 100644 vendor/lowrisc_ip/lint/.gitignore create mode 100644 vendor/lowrisc_ip/lint/Makefile create mode 100644 vendor/lowrisc_ip/lint/common.core create mode 100644 vendor/lowrisc_ip/lint/comportable.core create mode 100644 vendor/lowrisc_ip/lint/data/ascentlint.hjson create mode 100644 vendor/lowrisc_ip/lint/data/common_lint_cfg.hjson create mode 100644 vendor/lowrisc_ip/lint/data/lint.mk create mode 100644 vendor/lowrisc_ip/lint/data/veriblelint.hjson create mode 100644 vendor/lowrisc_ip/lint/doc/README.md create mode 100644 vendor/lowrisc_ip/lint/tools/ascentlint/ascentlint-config.tcl create mode 100644 vendor/lowrisc_ip/lint/tools/ascentlint/common.waiver create mode 100644 vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver create mode 100755 vendor/lowrisc_ip/lint/tools/ascentlint/parse-lint-report.py create mode 100755 vendor/lowrisc_ip/lint/tools/veriblelint/parse-lint-report.py create mode 100644 vendor/lowrisc_ip/lint/tools/veriblelint/rules.vbl create mode 100644 vendor/lowrisc_ip/lint/tools/verilator/common.vlt create mode 100644 vendor/lowrisc_ip/lint/tools/verilator/comportable.vlt create mode 100755 vendor/lowrisc_ip/lint/util/gen-report.sh create mode 100644 vendor/lowrisc_ip/prim/README.md create mode 100644 vendor/lowrisc_ip/prim/doc/prim_keccak.md create mode 100644 vendor/lowrisc_ip/prim/doc/prim_lfsr.md create mode 100644 vendor/lowrisc_ip/prim/doc/prim_packer.md create mode 100644 vendor/lowrisc_ip/prim/doc/prim_present.md create mode 100644 vendor/lowrisc_ip/prim/doc/prim_prince.md create mode 100644 vendor/lowrisc_ip/prim/dv/Makefile create mode 100644 vendor/lowrisc_ip/prim/dv/prim_lfsr_sim.core create mode 100644 vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_bind.sv create mode 100644 vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_tb.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_async_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_arbiter_ppc_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_arbiter_tree_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_esc_rxtx_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_fifo_sync_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_keccak_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/prim_lfsr_fpv.core create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_keccak_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/tb/prim_lfsr_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_ppc_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_tree_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv create mode 100644 vendor/lowrisc_ip/prim/lint/prim.vlt create mode 100644 vendor/lowrisc_ip/prim/lint/prim.waiver create mode 100644 vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/README.md create mode 100644 vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc create mode 100644 vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core create mode 100644 vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv create mode 100644 vendor/lowrisc_ip/prim/prim.core rename {shared => vendor/lowrisc_ip/prim}/prim_assert.core (100%) create mode 100644 vendor/lowrisc_ip/prim/prim_clock_gating.core create mode 100644 vendor/lowrisc_ip/prim/prim_clock_mux2.core rename shared/prim_lfsr.core => vendor/lowrisc_ip/prim/prim_diff_decode.core (68%) create mode 100644 vendor/lowrisc_ip/prim/prim_flash.core rename shared/prim_ram_1p.core => vendor/lowrisc_ip/prim/prim_gf_mult.core (59%) create mode 100644 vendor/lowrisc_ip/prim/prim_otp.core create mode 100644 vendor/lowrisc_ip/prim/prim_pad_wrapper.core create mode 100644 vendor/lowrisc_ip/prim/prim_pkg.core create mode 100644 vendor/lowrisc_ip/prim/prim_ram_1p.core create mode 100644 vendor/lowrisc_ip/prim/prim_ram_1p_adv.core create mode 100644 vendor/lowrisc_ip/prim/prim_ram_2p.core create mode 100644 vendor/lowrisc_ip/prim/prim_rom.core rename {shared => vendor/lowrisc_ip/prim}/prim_secded.core (86%) create mode 100644 vendor/lowrisc_ip/prim/prim_util_memload.core create mode 100644 vendor/lowrisc_ip/prim/primgen.core create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_alert_pkg.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_alert_receiver.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_alert_sender.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_arbiter_ppc.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_arbiter_tree.sv rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_assert.sv (100%) create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_cipher_pkg.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_clock_gating_sync.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_clock_inverter.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_diff_decode.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_esc_pkg.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_esc_receiver.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_esc_sender.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_fifo_async.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_filter.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_filter_ctr.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_flop_2sync.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_gate_gen.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_gf_mult.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_intr_hw.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_keccak.sv rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_lfsr.sv (98%) create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_packer.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_present.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_prince.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_pulse_sync.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_secded_28_22_dec.sv (100%) rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_secded_28_22_enc.sv (100%) create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_dec.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_enc.sv rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_secded_72_64_dec.sv (100%) rename {shared => vendor/lowrisc_ip/prim}/rtl/prim_secded_72_64_enc.sv (100%) create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_sram_arbiter.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_subreg.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_subreg_ext.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_sync_reqack.sv create mode 100644 vendor/lowrisc_ip/prim/rtl/prim_util_memload.sv create mode 100755 vendor/lowrisc_ip/prim/util/get-lfsr-coeffs.py create mode 100755 vendor/lowrisc_ip/prim/util/keccak_rc.py create mode 100755 vendor/lowrisc_ip/prim/util/primgen.py create mode 100644 vendor/lowrisc_ip/prim/util/primgen/abstract_prim.sv.tpl rename shared/prim_generic_ram_1p.core => vendor/lowrisc_ip/prim/util/primgen/prim_pkg.core.tpl (71%) create mode 100644 vendor/lowrisc_ip/prim/util/primgen/prim_pkg.sv.tpl create mode 100755 vendor/lowrisc_ip/prim/util/secded_gen.py create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.vlt create mode 100644 vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.waiver create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_clock_gating.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_clock_mux2.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_flash.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_otp.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_pad_wrapper.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_ram_1p.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_ram_2p.core create mode 100644 vendor/lowrisc_ip/prim_generic/prim_generic_rom.core create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_mux2.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_otp.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_pad_wrapper.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_2p.sv create mode 100644 vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.vlt create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.waiver create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.vlt create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.waiver create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.vlt create mode 100644 vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.waiver create mode 100644 vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_gating.core create mode 100644 vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_mux2.core create mode 100644 vendor/lowrisc_ip/prim_xilinx/prim_xilinx_pad_wrapper.core rename shared/rtl/fpga/xilinx/prim_clock_gating.sv => vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv (61%) create mode 100644 vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_mux2.sv create mode 100644 vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv create mode 100644 vendor/lowrisc_lint.lock.hjson create mode 100644 vendor/lowrisc_lint.vendor.hjson create mode 100644 vendor/lowrisc_prim.lock.hjson create mode 100644 vendor/lowrisc_prim.vendor.hjson create mode 100644 vendor/lowrisc_prim_generic.lock.hjson create mode 100644 vendor/lowrisc_prim_generic.vendor.hjson create mode 100644 vendor/lowrisc_prim_xilinx.lock.hjson create mode 100644 vendor/lowrisc_prim_xilinx.vendor.hjson diff --git a/check_tool_requirements.core b/check_tool_requirements.core index 1276e3e8cb..375970cd8b 100644 --- a/check_tool_requirements.core +++ b/check_tool_requirements.core @@ -2,7 +2,7 @@ CAPI=2: # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -name: "lowrisc:ibex:check_tool_requirements:0.1" +name: "lowrisc:tool:check_tool_requirements:0.1" description: "Check tool requirements" filesets: diff --git a/dv/uvm/core_ibex/common/prim/prim_clock_gating.sv b/dv/uvm/core_ibex/common/prim/prim_clock_gating.sv new file mode 100644 index 0000000000..f60ae0a02c --- /dev/null +++ b/dv/uvm/core_ibex/common/prim/prim_clock_gating.sv @@ -0,0 +1,35 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Abstract primitives wrapper. +// +// This file is a stop-gap until the DV file list is generated by FuseSoC. +// Its contents are taken from the file which would be generated by FuseSoC. +// https://github.com/lowRISC/ibex/issues/893 + +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif + +module prim_clock_gating ( + input clk_i, + input en_i, + input test_en_i, + output logic clk_o +); + parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; + + if (Impl == prim_pkg::ImplGeneric) begin : gen_generic + prim_generic_clock_gating u_impl_generic ( + .* + ); + end else if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx + prim_xilinx_clock_gating u_impl_xilinx ( + .* + ); + end else begin : gen_failure + // TODO: Find code that works across tools and causes a compile failure + end + +endmodule diff --git a/dv/uvm/core_ibex/common/prim/prim_ram_1p.sv b/dv/uvm/core_ibex/common/prim/prim_ram_1p.sv new file mode 100644 index 0000000000..d169aa9ca4 --- /dev/null +++ b/dv/uvm/core_ibex/common/prim/prim_ram_1p.sv @@ -0,0 +1,41 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Abstract primitives wrapper. +// +// This file is a stop-gap until the DV file list is generated by FuseSoC. +// Its contents are taken from the file which would be generated by FuseSoC. +// https://github.com/lowRISC/ibex/issues/893 + +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif + +module prim_ram_1p #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input clk_i, + input rst_ni, // Memory content reset + + input req_i, + input write_i, + input [Aw-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o +); + parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; + + if (Impl == prim_pkg::ImplGeneric) begin : gen_generic + prim_generic_ram_1p u_impl_generic ( + .* + ); + end else begin : gen_failure + // TODO: Find code that works across tools and causes a compile failure + end + +endmodule diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index 1ebc257f19..ab8becd0c6 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -7,19 +7,26 @@ +define+BOOT_ADDR=2147483648 // 32'h8000_0000 +define+TRACE_EXECUTION +define+RVFI -+incdir+${PRJ_DIR}/ibex/shared/rtl -${PRJ_DIR}/ibex/shared/rtl/prim_clock_gating.sv +// Shared lowRISC code ++incdir${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_assert.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_enc.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_dec.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_enc.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_dec.sv + +// Until this list is generated by FuseSoC, we have to use manually generated +// wrappers around the prim_* modules to instantiate the prim_generic_* ones, +// see https://github.com/lowRISC/ibex/issues/893. +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv +${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/prim/prim_ram_1p.sv +${PRJ_DIR}/ibex/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv +${PRJ_DIR}/ibex/dv/uvm/core_ibex/common/prim/prim_clock_gating.sv // ibex CORE RTL files +incdir+${PRJ_DIR}/ibex/rtl -${PRJ_DIR}/ibex/shared/rtl/prim_assert.sv -${PRJ_DIR}/ibex/shared/rtl/prim_generic_ram_1p.sv -${PRJ_DIR}/ibex/shared/rtl/prim_lfsr.sv -${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_enc.sv -${PRJ_DIR}/ibex/shared/rtl/prim_secded_28_22_dec.sv -${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_enc.sv -${PRJ_DIR}/ibex/shared/rtl/prim_secded_72_64_dec.sv ${PRJ_DIR}/ibex/rtl/ibex_pkg.sv ${PRJ_DIR}/ibex/rtl/ibex_tracer_pkg.sv ${PRJ_DIR}/ibex/rtl/ibex_tracer.sv diff --git a/examples/simple_system/ibex_simple_system.cc b/examples/simple_system/ibex_simple_system.cc index 7dcc9ad654..2e1cdff68c 100644 --- a/examples/simple_system/ibex_simple_system.cc +++ b/examples/simple_system/ibex_simple_system.cc @@ -17,7 +17,7 @@ int main(int argc, char **argv) { simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N, VerilatorSimCtrlFlags::ResetPolarityNegative); - memutil.RegisterMemoryArea("ram", "TOP.ibex_simple_system.u_ram"); + memutil.RegisterMemoryArea("ram", "TOP.ibex_simple_system.u_ram.u_ram"); simctrl.RegisterExtension(&memutil); std::cout << "Simulation of Ibex" << std::endl diff --git a/ibex_core.core b/ibex_core.core index 7dc70487cf..346c67aa8f 100644 --- a/ibex_core.core +++ b/ibex_core.core @@ -9,7 +9,9 @@ filesets: files_rtl: depend: - lowrisc:prim:assert - - lowrisc:prim:lfsr + # TODO: Only lfsr is needed. Replace with a more specific dependency + # once available. + - lowrisc:prim:all - lowrisc:ibex:ibex_pkg - lowrisc:ibex:ibex_icache files: @@ -48,7 +50,7 @@ filesets: files_check_tool_requirements: depend: - - lowrisc:ibex:check_tool_requirements + - lowrisc:tool:check_tool_requirements parameters: RVFI: diff --git a/rtl/ibex_icache.sv b/rtl/ibex_icache.sv index e1d4bfc625..90b305e6e9 100644 --- a/rtl/ibex_icache.sv +++ b/rtl/ibex_icache.sv @@ -292,33 +292,29 @@ module ibex_icache #( for (genvar way = 0; way < NumWays; way++) begin : gen_rams // Tag RAM instantiation - prim_generic_ram_1p #( + prim_ram_1p #( .Width (TAG_SIZE_ECC), .Depth (NUM_LINES) ) tag_bank ( .clk_i (clk_i), - .rst_ni (rst_ni), .req_i (tag_req_ic0 & tag_banks_ic0[way]), .write_i (tag_write_ic0), .wmask_i ({TAG_SIZE_ECC{1'b1}}), .addr_i (tag_index_ic0), .wdata_i (tag_wdata_ic0), - .rvalid_o (), .rdata_o (tag_rdata_ic1[way]) ); // Data RAM instantiation - prim_generic_ram_1p #( + prim_ram_1p #( .Width (LINE_SIZE_ECC), .Depth (NUM_LINES) ) data_bank ( .clk_i (clk_i), - .rst_ni (rst_ni), .req_i (data_req_ic0 & data_banks_ic0[way]), .write_i (data_write_ic0), .wmask_i ({LINE_SIZE_ECC{1'b1}}), .addr_i (data_index_ic0), .wdata_i (data_wdata_ic0), - .rvalid_o (), .rdata_o (data_rdata_ic1[way]) ); end diff --git a/shared/fpga_xilinx.core b/shared/fpga_xilinx.core index a1a034ab95..127a768db3 100644 --- a/shared/fpga_xilinx.core +++ b/shared/fpga_xilinx.core @@ -6,8 +6,9 @@ name: "lowrisc:ibex:fpga_xilinx_shared" description: "Collection of useful RTL for Xilinx based examples" filesets: files_sv: + depend: + - lowrisc:prim:clock_gating files: - - rtl/fpga/xilinx/prim_clock_gating.sv - rtl/fpga/xilinx/clkgen_xil7series.sv - rtl/ram_1p.sv file_type: systemVerilogSource diff --git a/shared/rtl/prim_clock_gating.sv b/shared/rtl/prim_clock_gating.sv deleted file mode 100644 index a5b8138093..0000000000 --- a/shared/rtl/prim_clock_gating.sv +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// Dummy clock gating module compatible with latch-based register file - -module prim_clock_gating ( - input clk_i, - input en_i, - input test_en_i, - output logic clk_o -); - - logic clk_en; - - always_latch begin - if (clk_i == 1'b0) begin - clk_en <= en_i | test_en_i; - end - end - - assign clk_o = clk_i & clk_en; - -endmodule diff --git a/shared/rtl/prim_generic_ram_1p.sv b/shared/rtl/prim_generic_ram_1p.sv deleted file mode 100644 index 4f0080a181..0000000000 --- a/shared/rtl/prim_generic_ram_1p.sv +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 -// -// Synchronous single-port SRAM model - -`include "prim_assert.sv" - -module prim_generic_ram_1p #( - parameter int Width = 32, // bit - parameter int Depth = 128, - parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask - localparam int Aw = $clog2(Depth) // derived parameter -) ( - input logic clk_i, - input logic rst_ni, - - input logic req_i, - input logic write_i, - input logic [Aw-1:0] addr_i, - input logic [Width-1:0] wdata_i, - input logic [Width-1:0] wmask_i, - output logic rvalid_o, - output logic [Width-1:0] rdata_o -); - - // Width of internal write mask. Note wmask_i input into the module is always assumed - // to be the full bit mask - localparam int MaskWidth = Width / DataBitsPerMask; - - logic [Width-1:0] mem [Depth]; - logic [MaskWidth-1:0] wmask; - - always_comb begin - for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask - wmask[i] = &wmask_i[i*DataBitsPerMask +: DataBitsPerMask]; - end - end - - // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error - // thrown when using $readmemh system task to backdoor load an image - always @(posedge clk_i) begin - if (req_i) begin - if (write_i) begin - for (int i=0; i < MaskWidth; i = i + 1) begin - if (wmask[i]) begin - mem[addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= - wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; - end - end - end else begin - rdata_o <= mem[addr_i]; - end - end - end - - always_ff @(posedge clk_i, negedge rst_ni) begin - if (!rst_ni) begin - rvalid_o <= '0; - end else begin - rvalid_o <= req_i & ~write_i; - end - end - - `ifdef VERILATOR - // Task for loading 'mem' with SystemVerilog system task $readmemh() - export "DPI-C" task simutil_verilator_memload; - - task simutil_verilator_memload; - input string file; - $readmemh(file, mem); - endtask - - // Width must be a multiple of 32bit for this function to work - // Note that the DPI export and function definition must both be in the same generate - // context to get the correct name. - if ((Width % 32) == 0) begin : gen_set_mem - // Function for setting a specific element in |mem| - // Returns 1 (true) for success, 0 (false) for errors. - export "DPI-C" function simutil_verilator_set_mem; - - function int simutil_verilator_set_mem(input int index, - input bit [Width-1:0] val); - if (index >= Depth) begin - return 0; - end - - mem[index] = val; - return 1; - endfunction - end else begin : gen_other - // Function doesn't work unless Width % 32 so just return 0 - export "DPI-C" function simutil_verilator_set_mem; - - function int simutil_verilator_set_mem(input int index, - input bit [Width-1:0] val); - return 0; - endfunction - end - `endif - - `ifdef SRAM_INIT_FILE - localparam MEM_FILE = `PRIM_STRINGIFY(`SRAM_INIT_FILE); - initial begin - $display("Initializing SRAM from %s", MEM_FILE); - $readmemh(MEM_FILE, mem); - end - `endif -endmodule diff --git a/shared/rtl/ram_1p.sv b/shared/rtl/ram_1p.sv index 2eb8b36d78..ce310e5b75 100644 --- a/shared/rtl/ram_1p.sv +++ b/shared/rtl/ram_1p.sv @@ -39,32 +39,28 @@ module ram_1p #( end end - // |rvalid| in the bus module is an "ack", while prim_ram_1p associates the - // meaning "returned read data is valid" with this signal. - // Convert the RAM meaning to the meaning assumed by the bus module. - logic read_valid, we_q; + // |rvalid| in the bus module is an "ack" (which is a different meaning than + // in the prim_ram_*_adv modules). prim_ram_1p is guaranteed to return data + // in the next cycle. always_ff @(posedge clk_i, negedge rst_ni) begin if (!rst_ni) begin - we_q <= 1'b0; + rvalid_o <= 1'b0; end else begin - we_q <= we_i; + rvalid_o <= req_i; end end - assign rvalid_o = read_valid | we_q; - prim_generic_ram_1p #( + prim_ram_1p #( .Width(32), .DataBitsPerMask(8), .Depth(Depth) ) u_ram ( .clk_i (clk_i), - .rst_ni (rst_ni), .req_i (req_i), .write_i (we_i), .wmask_i (wmask), .addr_i (addr_idx), .wdata_i (wdata_i), - .rvalid_o (read_valid), .rdata_o (rdata_o) ); endmodule diff --git a/shared/rtl/ram_2p.sv b/shared/rtl/ram_2p.sv index 2eb77fa8e9..4cfea06e85 100644 --- a/shared/rtl/ram_2p.sv +++ b/shared/rtl/ram_2p.sv @@ -4,13 +4,6 @@ /** * Dual-port RAM with 1 cycle read/write delay, 32 bit words. - * - * The two ports are in Read-First Mode: when reading from and writing to the same address - * simultaneously, the old content is returned, before the new content is written. New content - * is made available to both ports with one cycle delay. - * - * Simultaneous write operations by both ports to the same address are to be avoided: The data - * written to memory is not determined. */ `include "prim_assert.sv" @@ -40,8 +33,6 @@ module ram_2p #( localparam int Aw = $clog2(Depth); - logic [31:0] mem [Depth]; - logic [Aw-1:0] a_addr_idx; assign a_addr_idx = a_addr_i[Aw-1+2:2]; logic [31-Aw:0] unused_a_addr_parts; @@ -52,77 +43,45 @@ module ram_2p #( logic [31-Aw:0] unused_b_addr_parts; assign unused_b_addr_parts = {b_addr_i[31:Aw+2], b_addr_i[1:0]}; - always @(posedge clk_i) begin - if (a_req_i) begin - if (a_we_i) begin - for (int i = 0; i < 4; i = i + 1) begin - if (a_be_i[i] == 1'b1) begin - mem[a_addr_idx][i*8 +: 8] <= a_wdata_i[i*8 +: 8]; - end - end - end - a_rdata_o <= mem[a_addr_idx]; - end - end - - always @(posedge clk_i) begin - if (b_req_i) begin - if (b_we_i) begin - for (int i = 0; i < 4; i = i + 1) begin - if (b_be_i[i] == 1'b1) begin - mem[b_addr_idx][i*8 +: 8] <= b_wdata_i[i*8 +: 8]; - end - end - end - b_rdata_o <= mem[b_addr_idx]; + // Convert byte mask to SRAM bit mask. + logic [31:0] a_wmask; + logic [31:0] b_wmask; + always_comb begin + for (int i = 0 ; i < 4 ; i++) begin + // mask for read data + a_wmask[8*i+:8] = {8{a_be_i[i]}}; + b_wmask[8*i+:8] = {8{b_be_i[i]}}; end end always_ff @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin a_rvalid_o <= '0; - end else begin - a_rvalid_o <= a_req_i; - end - end - - always_ff @(posedge clk_i or negedge rst_ni) begin - if (!rst_ni) begin b_rvalid_o <= '0; end else begin + a_rvalid_o <= a_req_i; b_rvalid_o <= b_req_i; end end - `ifdef VERILATOR - // Task for loading 'mem' with SystemVerilog system task $readmemh() - export "DPI-C" task simutil_verilator_memload; - // Function for setting a specific 32 bit element in |mem| - // Returns 1 (true) for success, 0 (false) for errors. - export "DPI-C" function simutil_verilator_set_mem; + prim_ram_2p #( + .Width(32), + .Depth(Depth) + ) u_ram ( + .clk_a_i (clk_i), + .clk_b_i (clk_i), + .a_req_i (a_req_i), + .a_write_i (a_we_i), + .a_addr_i (a_addr_idx), + .a_wdata_i (a_wdata_i), + .a_wmask_i (a_wmask), + .a_rdata_o (a_rdata_o), + .b_req_i (b_req_i), + .b_write_i (b_we_i), + .b_wmask_i (b_wmask), + .b_addr_i (b_addr_idx), + .b_wdata_i (b_wdata_i), + .b_rdata_o (b_rdata_o) + ); - task simutil_verilator_memload; - input string file; - $readmemh(file, mem); - endtask - - // TODO: Allow 'val' to have other widths than 32 bit - function int simutil_verilator_set_mem(input int index, - input logic[31:0] val); - if (index >= Depth) begin - return 0; - end - - mem[index] = val; - return 1; - endfunction - `endif - - `ifdef SRAM_INIT_FILE - localparam MEM_FILE = `PRIM_STRINGIFY(`SRAM_INIT_FILE); - initial begin - $display("Initializing SRAM from %s", MEM_FILE); - $readmemh(MEM_FILE, mem); - end - `endif endmodule diff --git a/shared/sim_shared.core b/shared/sim_shared.core index 64406759bf..5879f5ea19 100644 --- a/shared/sim_shared.core +++ b/shared/sim_shared.core @@ -8,9 +8,10 @@ filesets: files_sim_sv: depend: - lowrisc:prim:assert - - lowrisc:prim_generic:ram_1p + - lowrisc:prim:ram_1p + - lowrisc:prim:ram_2p + - lowrisc:prim:clock_gating files: - - ./rtl/prim_clock_gating.sv - ./rtl/ram_1p.sv - ./rtl/ram_2p.sv - ./rtl/bus.sv diff --git a/util/check_tool_requirements.py b/util/check_tool_requirements.py index 935ac9d7a4..5a59d4cd86 100755 --- a/util/check_tool_requirements.py +++ b/util/check_tool_requirements.py @@ -6,50 +6,15 @@ from distutils.version import StrictVersion import logging as log import os -import re import subprocess import sys # Display INFO log messages and up. log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s") - -def get_tool_requirements_path(): - '''Return the path to tool_requirements.py, at the top of the Ibex repo''' - # top_src_dir is the top of the repository - top_src_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), - '..')) - - return os.path.join(top_src_dir, 'tool_requirements.py') - - -def read_tool_requirements(path=None): - '''Read tool requirements from a Python file''' - if path is None: - path = get_tool_requirements_path() - - with open(path, 'r') as pyfile: - globs = {} - exec(pyfile.read(), globs) - - # We expect the exec call to have populated globs with a - # __TOOL_REQUIREMENTS__ dictionary. - reqs = globs.get('__TOOL_REQUIREMENTS__') - if reqs is None: - log.error('The Python file at {} did not define ' - '__TOOL_REQUIREMENTS__.' - .format(path)) - return None - - # reqs should be a dictionary (mapping tool name to minimum version) - if not isinstance(reqs, dict): - log.error('The Python file at {} defined ' - '__TOOL_REQUIREMENTS__, but it is not a dict.' - .format(path)) - return None - - return reqs - +# Populate __TOOL_REQUIREMENTS__ +topsrcdir = os.path.join(os.path.dirname(__file__), '..') +exec(open(os.path.join(topsrcdir, 'tool_requirements.py')).read()) def get_verilator_version(): try: @@ -67,44 +32,8 @@ def get_verilator_version(): log.error(e.stdout) return None - -def pip3_get_version(tool): - '''Run pip3 to find the version of an installed module''' - cmd = ['pip3', 'show', tool] - try: - proc = subprocess.run(cmd, - check=True, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - universal_newlines=True) - except subprocess.CalledProcessError as err: - log.error('pip3 command failed: {}'.format(err)) - log.error("Failed to get version of {} with pip3: is it installed?" - .format(tool)) - log.error(err.stdout) - return None - - version_re = 'Version: (.*)' - for line in proc.stdout.splitlines(): - match = re.match(version_re, line) - if match: - return match.group(1) - - # If we get here, we never saw a version line. - log.error('No output line from running {} started with "Version: ".' - .format(cmd)) - return None - - -def check_version(requirements, tool_name, getter): - required_version = requirements.get(tool_name) - if required_version is None: - log.error('Requirements file does not specify version for {}.' - .format(tool_name)) - return False - - actual_version = getter() - if actual_version is None: +def check_version(tool_name, required_version, actual_version): + if required_version is None or actual_version is None: return False if StrictVersion(actual_version) < StrictVersion(required_version): @@ -118,26 +47,17 @@ def check_version(requirements, tool_name, getter): def main(): - # Get tool requirements - tool_requirements = read_tool_requirements() - if tool_requirements is None: - return 1 + any_failed = False - all_good = True - all_good &= check_version(tool_requirements, - 'verilator', - get_verilator_version) - all_good &= check_version(tool_requirements, - 'edalize', - lambda: pip3_get_version('edalize')) + if not check_version('verilator', __TOOL_REQUIREMENTS__['verilator'], + get_verilator_version()): + any_failed = True - if not all_good: + if any_failed: log.error("Tool requirements not fulfilled. " "Please update the tools and retry.") return 1 - return 0 - if __name__ == "__main__": sys.exit(main()) diff --git a/util/ibex_config.py b/util/ibex_config.py index 58d236e142..7ad34d45d5 100755 --- a/util/ibex_config.py +++ b/util/ibex_config.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 import argparse -import collections +import collections.abc import os import shlex import sys @@ -39,7 +39,7 @@ def _verify_config(name, config_dict): ConfigException: An issue was found with config_dict """ - if not isinstance(config_dict, collections.Mapping): + if not isinstance(config_dict, collections.abc.Mapping): raise ConfigException('Config ' + name + ' must have dictionary giving parameters') diff --git a/vendor/lowrisc_ip/lint/.gitignore b/vendor/lowrisc_ip/lint/.gitignore new file mode 100644 index 0000000000..d4d090c02d --- /dev/null +++ b/vendor/lowrisc_ip/lint/.gitignore @@ -0,0 +1,3 @@ +build +reports +ascentlint.policy diff --git a/vendor/lowrisc_ip/lint/Makefile b/vendor/lowrisc_ip/lint/Makefile new file mode 100644 index 0000000000..c6c1e5c897 --- /dev/null +++ b/vendor/lowrisc_ip/lint/Makefile @@ -0,0 +1,133 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Makefile with ascentlint, verilator-lint and verible style lint +# targets for OpenTitan +# +# TODO: currently we cannot support parallel builds since some fusesoc cores +# define filesets with files outside the current folder (e.g. using relative +# path prefixes such as "../../"). this can cause collisions between parallel +# builds since they are not nicely contained within the same folder. This should +# be solved by reworking the fusesoc core files (especially the top-level one). + +CORE_ROOT ?= ../../ +REPORT_DIR ?= reports + +IPS ?= ip-aes \ + ip-alert_handler \ + ip-clkmgr \ + ip-flash_ctrl \ + ip-gpio \ + ip-hmac \ + ip-i2c \ + ip-otp_ctrl \ + ip-nmi_gen \ + ip-padctrl \ + ip-pinmux \ + ip-pwrmgr \ + ip-rv_core_ibex \ + ip-rv_dm \ + ip-rv_plic_example \ + ip-rv_timer \ + ip-spi_device \ + ip-uart \ + ip-usbdev \ + ip-usb_fs_nb_pe \ + ip-usbuart \ + ip-rstmgr \ + tlul-socket_1n \ + tlul-socket_m1 \ + tlul-adapter_reg \ + tlul-adapter_sram \ + tlul-sram2tlul \ + systems-top_earlgrey + +ips_lint = $(addsuffix _lint, $(IPS)) +ips_vlint = $(addsuffix _vlint, $(IPS)) +ips_slint = $(addsuffix _slint, $(IPS)) + +###################### +# ascentlint targets # +###################### + +# lint all discovered targets and make a report +all: lint + $(MAKE) report + +lint: clean + @echo Discovered lint targets: + @echo -e "\n $(patsubst %,%\\n,$(strip $(ips_lint)))" + $(MAKE) $(ips_lint) + +$(ips_lint): + rm -rf build + mkdir -p ${REPORT_DIR} + -fusesoc --cores-root ${CORE_ROOT} run --target=lint --tool=ascentlint lowrisc:$(subst -,:,$(patsubst %_lint,%,$@)) + cp build/lowrisc_*$(subst -,_,$(patsubst %_lint,%,$@))*/lint-ascentlint/ascentlint.log ${REPORT_DIR}/$(patsubst %_lint,%,$@).log + cp build/lowrisc_*$(subst -,_,$(patsubst %_lint,%,$@))*/lint-ascentlint/ascentlint.rpt ${REPORT_DIR}/$(patsubst %_lint,%,$@).rpt + +# creates a (filtered) summary report from all available ascentlint logs/rpts +# note that lint reports have to be filtered using this script before publishing +# any information from these reports publicly +# note, the filtering script is simplistic ATM and just looks for *.rpt files +# hence we have to temporarily write the output to a file with a different extension +# since otherwise the gen_report script will try to process the summary report as well. +report: + $(eval TMPFILE=$(shell mktemp)) + rm -f ${REPORT_DIR}/lint_summary.rpt + ./util/gen-report.sh | tee $(TMPFILE) + cp $(TMPFILE) ${REPORT_DIR}/lint_summary.rpt + rm -f $(TMPFILE) + +##################### +# verilator targets # +##################### + +vall: vlint + $(MAKE) vreport + +vlint: clean + @echo Discovered vlint targets: + @echo -e "\n $(patsubst %,%\\n,$(strip $(ips_vlint)))" + $(MAKE) $(ips_vlint) + +$(ips_vlint): + rm -rf build + mkdir -p ${REPORT_DIR} + -fusesoc --cores-root ${CORE_ROOT} run --target=lint lowrisc:$(subst -,:,$(patsubst %_vlint,%,$@)) + +# TODO: add summary reporting +vreport: + +############################## +# verible style lint targets # +############################## + +sall: slint + $(MAKE) sreport + +slint: clean + @echo Discovered vlint targets: + @echo -e "\n $(patsubst %,%\\n,$(strip $(ips_vlint)))" + $(MAKE) $(ips_vlint) + +# TODO(#1727): pass Verible config file to FuseSoC, once supported by Verible +$(ips_slint): + rm -rf build + mkdir -p ${REPORT_DIR} + -fusesoc --cores-root ${CORE_ROOT} run --target=lint --tool=veriblelint lowrisc:$(subst -,:,$(patsubst %_slint,%,$@)) + +# TODO: add summary reporting +sreport: + +################## +# common targets # +################## + +clean: + rm -rf build + rm -rf ${REPORT_DIR}/* + +.PHONY: all lint $(ips_lint) report vall vlint $(ips_vlint) \ + vreport sall slint $(ips_slint) clean sreport diff --git a/vendor/lowrisc_ip/lint/common.core b/vendor/lowrisc_ip/lint/common.core new file mode 100644 index 0000000000..77e87c7407 --- /dev/null +++ b/vendor/lowrisc_ip/lint/common.core @@ -0,0 +1,32 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:lint:common:0.1" +description: "Common waivers" +filesets: + files_verilator: + files: + - tools/verilator/common.vlt + file_type: vlt + + files_ascentlint: + files: + - tools/ascentlint/common.waiver: {file_type: waiver} + - tools/ascentlint/ascentlint-config.tcl: {file_type: tclSource} + + files_veriblelint: + files: + - tools/veriblelint/rules.vbl: {file_type: veribleLintRules} + + files_check_tool_requirements: + depend: + - lowrisc:tool:check_tool_requirements + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator) + - tool_ascentlint ? (files_ascentlint) + - tool_veriblelint ? (files_veriblelint) + - files_check_tool_requirements diff --git a/vendor/lowrisc_ip/lint/comportable.core b/vendor/lowrisc_ip/lint/comportable.core new file mode 100644 index 0000000000..f74083e80e --- /dev/null +++ b/vendor/lowrisc_ip/lint/comportable.core @@ -0,0 +1,24 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:lint:comportable:0.1" +description: "Waiver files for comportable IPs" +filesets: + files_verilator_waiver: + files: + - tools/verilator/comportable.vlt + file_type: vlt + + files_ascentlint_waiver: + files: + - tools/ascentlint/comportable.waiver + file_type: waiver + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + + diff --git a/vendor/lowrisc_ip/lint/data/ascentlint.hjson b/vendor/lowrisc_ip/lint/data/ascentlint.hjson new file mode 100644 index 0000000000..548b526da9 --- /dev/null +++ b/vendor/lowrisc_ip/lint/data/ascentlint.hjson @@ -0,0 +1,9 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Ascentlint-specific results parsing script that is called after running lint + report_cmd: "{proj_root}/hw/lint/tools/{tool}/parse-lint-report.py " + report_opts: ["--repdir={build_dir}/lint-{tool}", + "--outdir={build_dir}"] +} diff --git a/vendor/lowrisc_ip/lint/data/common_lint_cfg.hjson b/vendor/lowrisc_ip/lint/data/common_lint_cfg.hjson new file mode 100644 index 0000000000..5717b30f02 --- /dev/null +++ b/vendor/lowrisc_ip/lint/data/common_lint_cfg.hjson @@ -0,0 +1,33 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + flow: lint + flow_makefile: "{proj_root}/hw/lint/data/lint.mk" + + import_cfgs: [// common server configuration for results upload + "{proj_root}/hw/data/common_project_cfg.hjson" + // tool-specific configuration + "{proj_root}/hw/lint/data/{tool}.hjson"] + + // Name of the DUT / top-level to be run through lint + dut: "{name}" + + // Default directory structure for the output + build_dir: "{scratch_path}/{build_mode}" + build_log: "{build_dir}/lint.log" + // We rely on fusesoc to run lint for us + build_cmd: "fusesoc" + build_opts: ["--cores-root {proj_root}", + "run", + "--target={flow}", + "--tool={tool}", + "--build-root={build_dir}", + "{fusesoc_core}"] + + // these are not needed currently, but have to be defined + sv_flist_gen_cmd: "" + sv_flist_gen_opts: [] + sv_flist_gen_dir: "" + tool_srcs: [] +} diff --git a/vendor/lowrisc_ip/lint/data/lint.mk b/vendor/lowrisc_ip/lint/data/lint.mk new file mode 100644 index 0000000000..584541b6ed --- /dev/null +++ b/vendor/lowrisc_ip/lint/data/lint.mk @@ -0,0 +1,41 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +.DEFAULT_GOAL := all + +all: build + +################### +## build targets ## +################### +build: compile_result + +pre_compile: + @echo "[make]: pre_compile" + mkdir -p ${build_dir} && env | sort > ${build_dir}/env_vars + mkdir -p ${tool_srcs_dir} + -cp -Ru ${tool_srcs} ${tool_srcs_dir} + +compile: pre_compile + @echo "[make]: compile" + # we check the status in the parse script below + -cd ${build_dir} && ${build_cmd} ${build_opts} 2>&1 | tee ${build_log} + +post_compile: compile + @echo "[make]: post_compile" + +# Parse out result +compile_result: post_compile + @echo "[make]: compile_result" + ${report_cmd} ${report_opts} + +clean: + echo "[make]: clean" + rm -rf ${scratch_root}/${dut}/* + +.PHONY: build \ + run \ + pre_compile \ + compile \ + post_compile \ + compile_result diff --git a/vendor/lowrisc_ip/lint/data/veriblelint.hjson b/vendor/lowrisc_ip/lint/data/veriblelint.hjson new file mode 100644 index 0000000000..d7255d7701 --- /dev/null +++ b/vendor/lowrisc_ip/lint/data/veriblelint.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // TODO(#1342): switch over to native structured tool output, once supported by Verible + // Verible lint-specific results parsing script that is called after running lint + report_cmd: "{proj_root}/hw/lint/tools/{tool}/parse-lint-report.py " + report_opts: ["--repdir={build_dir}", + "--outdir={build_dir}"] + + // This customizes the report format for style lint + is_style_lint: True +} diff --git a/vendor/lowrisc_ip/lint/doc/README.md b/vendor/lowrisc_ip/lint/doc/README.md new file mode 100644 index 0000000000..9d5acc754f --- /dev/null +++ b/vendor/lowrisc_ip/lint/doc/README.md @@ -0,0 +1,121 @@ +# RTL Linting + +Linting is a productivity tool for designers to quickly find typos and bugs at the time when the RTL is written. +Running lint is important when using SystemVerilog, a weakly-typed language, unlike other hardware description languages. +We consider linting to be critical for conformance to our goals of high quality designs. + +We have standardized on the [AscentLint](https://www.realintent.com/rtl-linting-ascent-lint/) tool from RealIntent for this task due to its fast run-times and comprehensive set of rules that provide concise error and warning messages. + +The lint flow leverages a new lint rule policy named _"lowRISC Lint Rules"_ that has been tailored towards our [Verilog Style Guide](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md). +The lint flow run scripts and waiver files are available in the GitHub repository of this project, but due to the proprietary nature of the lint rules and their configuration, the _"lowRISC Lint Rules"_ lint policy file can not be publicly provided. +However, the _"lowRISC Lint Rules"_ are available as part of the default policies in AscentLint release 2019.A.p3 or newer (as `LRLR-v1.0.policy`). +This enables designers that have access to this tool to run the lint flow provided locally on their premises. + +Our linting flow leverages FuseSoC to resolve dependencies, build file lists and call the linting tools. See [here](https://github.com/olofk/fusesoc) for an introduction to this opensource package manager and [here](https://docs.opentitan.org/doc/ug/install_instructions/) for installation instructions. + +In order to run lint on a [comportable IP](https://docs.opentitan.org/doc/rm/comportability_specification/) block, the corresponding FuseSoC core file must have a lint target and include (optional) waiver files as shown in the following example taken from the FuseSoC core of the AES comportable IP: +``` +filesets: + + [...] + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/aes.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + files: + - lint/aes.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + [...] + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: aes + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" +``` +Note that the setup shown above also supports RTL style linting with the open source tool [Verible](https://github.com/google/verible/) and RTL linting with [Verilator](https://www.veripool.org/wiki/verilator) in order to complement the sign-off lint flow with AscentLint. +In particular, Verible lint focuses on different aspects of the code, and detects style elements that are in violation with our [Verilog Style Guide](https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md). + +The same lint target is reused for all three tools (we override the tool selection when invoking FuseSoC). +Lint waivers can be added to the flow by placing them in the corresponding waiver file. +In this example this would be `lint/aes.waiver` for AscentLint and `lint/aes.vlt` for Verilator. + +In order to manually run lint on a specific block, make sure AscentLint is properly installed and step into the `hw/lint` folder. +The makefile in that folder contains all targets that can be manually invoked. +For example, to run lint on AES, do: +``` +make ip-aes_lint +``` +This run will exit with PASSED status on the command line if there are no lint errors or warnings. +Otherwise it will exit with ERROR status, in which case you can get more information by running +``` +make clean +make ip-aes_lint +make report +``` +In order to build all lint targets and produce a summary report, the `make all` target can be invoked. +For more detailed information on a particular lint run you can inspect the tool output inside the build folder that is created by FuseSoC. + +Note that all AscentLint targets have a Verilator and Verible counterparts that are suffixed with `_vlint` and `_slint`, respectively. +This enables designers without access to AscentLint to iterate with open-source tools before making their first Pull Request. + +For batch regressions we have integrated this flow into the `dvsim` tool, which can be invoked as follows from the root of the project repository: +``` +util/dvsim.py hw/top_earlgrey/lint/ascentlint/top_earlgrey_lint_cfgs.hjson --tool "ascentlint" --purge -mp 1 +``` +where the `top_earlgrey_lint_cfgs.hjson` file contains all the lint targets to be run in that regression (currently all available comportable IPs and the top-level are run). +The `purge` option ensures that the scratch directory is fully erased before starting the build, and `mp 1` sets the number of parallel workers to one (should be set depending on your licensing situation). + +The batch regression is regularly run on the master branch at eight-hour intervals, and the results are published on a public dashboard such that everybody can inspect the current lint status of all IPs on the project website. +The dashboard can be found by following the appropriate link on the [hardware IP overview page](https://docs.opentitan.org/hw). + +# CDC Linting + +Logic designs that have signals that cross from one clock domain to +another unrelated clock domain are notorious for introducing hard to +debug problems. The reason is that design verification, with its constant +and idealized timing relationships on signals, does not represent the +variability and uncertainty of real world systems. For this reason, +maintaining a robust Clock Domain Crossing verification strategy ("CDC +methodology") is critical to the success of any multi-clock design. + +Currently, due to the proprietary nature of tool collateral, all CDC linting +activity is done offline and reported back to designers. The project will +standardize on a particular CDC linting tool, and results will be shared in +some form through continuous integration build results, published tool +outputs, pre-submit checks, and/or linting summaries of tool output +(TODO: publication details). At that time this README will be updated +with setup and run instructions. + +This holds for *Reset Domain Crossing* ("RDC") methodology as well. diff --git a/vendor/lowrisc_ip/lint/tools/ascentlint/ascentlint-config.tcl b/vendor/lowrisc_ip/lint/tools/ascentlint/ascentlint-config.tcl new file mode 100644 index 0000000000..3e01524a24 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/ascentlint/ascentlint-config.tcl @@ -0,0 +1,9 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# get installation path of ascentlint +set RI_INSTALL [file dirname [exec which ascentlint]] + +# source the policy file containing the lowrisc lint rules +source "$RI_INSTALL/../Ascent/Lint/lib/policies/lowRISC/LRLR-v1.0.policy" diff --git a/vendor/lowrisc_ip/lint/tools/ascentlint/common.waiver b/vendor/lowrisc_ip/lint/tools/ascentlint/common.waiver new file mode 100644 index 0000000000..9e694c6aa4 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/ascentlint/common.waiver @@ -0,0 +1,11 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# common waiver rules for ascentlint + +# waiver for unused_* signals for HIER_* rules (note that our policy file has a +# similar exception list for rule NOT_READ) +waive -rules {HIER_NET_NOT_READ HIER_BRANCH_NOT_READ} -pattern {unused_*} +waive -rules {HIER_NET_NOT_READ HIER_BRANCH_NOT_READ} -pattern {gen_*.unused_*} + diff --git a/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver b/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver new file mode 100644 index 0000000000..43ebcfc709 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/ascentlint/comportable.waiver @@ -0,0 +1,21 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# comportable IP waiver rules for ascentlint + +# auto-generated register files + +waive -rules CONST_FF -location {*_reg_top*} -regexp {rsp_opcode.*is driven by constant zeros} \ + -comment "makes the code more readable" +waive -rules CONST_OUTPUT -location {*_reg_top*} -regexp {Output 'tl_o.d_(param|size|sink|user)' is driven by constant} \ + -comment "makes the code more readable" +waive -rules INPUT_NOT_READ -location {*_reg_top*} -regexp {Input port.*a_(address|param|user).*not read from} \ + -comment "several TLUL signals are not used by register file" +waive -rules HIER_NET_NOT_READ -location {*_reg_top*} -regexp {Net 'tl_reg_h2d.a_(address|param|user).* is not read from} \ + -comment "several TLUL signals are not used by register file" +waive -rules CASE_SEL_CONST -location {*_reg_top*} \ + -comment "addr_hit is one hot encoded." +waive -rules LINE_LENGTH -location {*_reg_top*} -regexp {Line length of .* exceeds .* character limit} \ + -comment "These files are one-liners in order to comply with our SV style guide." + diff --git a/vendor/lowrisc_ip/lint/tools/ascentlint/parse-lint-report.py b/vendor/lowrisc_ip/lint/tools/ascentlint/parse-lint-report.py new file mode 100755 index 0000000000..c013e62522 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/ascentlint/parse-lint-report.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +r"""Parses lint report and dump filtered messages in hjson format. +""" +import argparse +import re +import sys +from pathlib import Path + +import hjson + + +def extract_messages(full_file, patterns, results): + """ + This extracts messages from the sting buffer full_file. + The argument patterns needs to be a list of tuples with + (, ). + """ + for severity, pattern in patterns: + results[severity] += re.findall(pattern, full_file, flags=re.MULTILINE) + + return results + + +def get_results(resdir): + """ + Parse report and corresponding logfiles and extract error, warning + and info messages for each IP present in the result folder + """ + results = { + "tool": "ascentlint", + "errors": [], + "warnings": [], + "lint_errors": [], + "lint_warnings": [], + "lint_infos": [] + } + try: + # check the log file for flow errors and warnings + with Path(resdir).joinpath('ascentlint.log').open() as f: + full_file = f.read() + err_warn_patterns = [("errors", r"^FlexNet Licensing error.*"), + ("errors", r"^Error: .*"), + ("errors", r"^ ERR .*"), + ("warnings", r"^Warning: .*"), + ("warnings", r"^ WARN .*")] + extract_messages(full_file, err_warn_patterns, results) + except IOError as err: + results["errors"] += ["IOError: %s" % err] + + try: + # check the report file for lint INFO, WARNING and ERRORs + with Path(resdir).joinpath('ascentlint.rpt').open() as f: + full_file = f.read() + err_warn_patterns = {("lint_errors", r"^E .*"), + ("lint_warnings", r"^W .*"), + ("lint_infos", r"^I .*")} + extract_messages(full_file, err_warn_patterns, results) + except IOError as err: + results["errors"] += ["IOError: %s" % err] + + return results + + +def main(): + + parser = argparse.ArgumentParser( + description="""This script parses AscentLint log and report files from + a lint run, filters the messages and creates an aggregated result + .hjson file with the following fields: + + {"tool": "ascentlint", + "errors" : [], + "warnings" : [], + "lint_errors" : [], + "lint_warnings" : [], + "lint_infos" : []} + + The fields 'errors' and 'warnings' contain file IO messages or + messages output by the tool itself, whereas the fields prefixed with + 'lint_' contain lint-related messages. + + The script returns nonzero status if any warnings or errors are present. + """) + parser.add_argument('--repdir', + type=str, + default="./", + help="""The script searches the 'ascentlint.log' and + 'ascentlint.rpt' files in this directory. + Defaults to './'""") + + parser.add_argument('--outdir', + type=str, + default="./", + help="""Output directory for the 'results.hjson' file. + Defaults to './'""") + + args = parser.parse_args() + results = get_results(args.repdir) + + with Path(args.outdir).joinpath("results.hjson").open("w") as results_file: + hjson.dump(results, + results_file, + ensure_ascii=False, + for_json=True, + use_decimal=True) + + # return nonzero status if any warnings or errors are present + # lint infos do not count as failures + nr_errors = len(results["errors"]) + len(results["lint_errors"]) + nr_warnings = len(results["warnings"]) + len(results["lint_warnings"]) + if nr_errors > 0 and nr_warnings > 0: + print("Lint not successful, got %d warnings and %d errors." % + (nr_warnings, nr_errors)) + sys.exit(1) + + print("Lint successful, got %d warnings and %d errors." % + (nr_warnings, nr_errors)) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/vendor/lowrisc_ip/lint/tools/veriblelint/parse-lint-report.py b/vendor/lowrisc_ip/lint/tools/veriblelint/parse-lint-report.py new file mode 100755 index 0000000000..6caeba5003 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/veriblelint/parse-lint-report.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +r"""Parses lint report and dump filtered messages in hjson format. +""" +import argparse +import re +import sys +from pathlib import Path + +import hjson + + +def extract_messages(full_file, patterns, results): + """ + This extracts messages from the sting buffer full_file. + The argument patterns needs to be a list of tuples with + (, ). + """ + for severity, pattern in patterns: + results[severity] += re.findall(pattern, full_file, flags=re.MULTILINE) + + return results + + +def get_results(resdir): + """ + Parse report and corresponding logfiles and extract error, warning + and info messages for each IP present in the result folder + """ + results = { + "tool": "veriblelint", + "errors": [], + "warnings": [], + "lint_errors": [], + "lint_warnings": [], + "lint_infos": [] + } + try: + # check the report file for lint INFO, WARNING and ERRORs + with Path(resdir).joinpath('lint.log').open() as f: + full_file = f.read() + err_warn_patterns = { + # The lint failed error can be ignored, since + # Fusesoc will always return this error if lint warnings have + # been found. We have a way of capturing the lint warnings + # explicitly in this parsing script, hence this error is redundant + # and we decided not to report it in the dashboard. + ("errors", + r"^(?!ERROR: Failed to run .* Lint failed)ERROR: .*"), + ("errors", r"^Error: .*"), + ("errors", r"^E .*"), + # TODO(https://github.com/olofk/edalize/issues/90): + # this is a workaround until we actually have native Edalize + # support for JasperGold and "formal" targets + ("warnings", + r"^(?!WARNING: Unknown item formal in section Target)WARNING: .*" + ), + ("warnings", r"^Warning: .* "), + ("warnings", r"^W .*"), + ("lint_warnings", r"^.*\[Style:.*") + } + extract_messages(full_file, err_warn_patterns, results) + except IOError as err: + results["errors"] += ["IOError: %s" % err] + + return results + + +def main(): + + parser = argparse.ArgumentParser( + description="""This script parses verible lint log files from + a lint run, filters the messages and creates an aggregated result + .hjson file with the following fields: + + {"tool": "veriblelint", + "errors" : [], + "warnings" : [], + "lint_errors" : [], + "lint_warnings" : [], + "lint_infos" : []} + + The fields 'errors' and 'warnings' contain file IO messages or + messages output by the tool itself, whereas the fields prefixed with + 'lint_' contain lint-related messages. + + The script returns nonzero status if any warnings or errors are present. + """) + parser.add_argument('--repdir', + type=str, + default="./", + help="""The script searches the 'lint.log' + files in this directory. + Defaults to './'""") + + parser.add_argument('--outdir', + type=str, + default="./", + help=""" + Output directory for the 'results.hjson' file. + Defaults to '%(default)s'""") + + args = parser.parse_args() + results = get_results(args.repdir) + + with Path(args.outdir).joinpath("results.hjson").open("w") as results_file: + hjson.dump(results, + results_file, + ensure_ascii=False, + for_json=True, + use_decimal=True) + + # return nonzero status if any warnings or errors are present + # lint infos do not count as failures + nr_errors = len(results["errors"]) + len(results["lint_errors"]) + nr_warnings = len(results["warnings"]) + len(results["lint_warnings"]) + print("Lint not successful, got %d warnings and %d errors." % + (nr_warnings, nr_errors)) + if nr_errors > 0 and nr_warnings > 0: + sys.exit(1) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/vendor/lowrisc_ip/lint/tools/veriblelint/rules.vbl b/vendor/lowrisc_ip/lint/tools/veriblelint/rules.vbl new file mode 100644 index 0000000000..3b77084ed5 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/veriblelint/rules.vbl @@ -0,0 +1,19 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# OpenTitan-specific style lint rule configurations + +# line length currently set to 150 to remove clutter in reports. +# set this back to 100 once we can waive this rule for generated +# files or once the file generators can respect this constraint +line-length=length:150 + +# we allow "classic" verilog string parameters without explicit type +explicit-parameter-storage-type=exempt_type:string + +# localparam can be both ALL_CAPS and CamelCase according to our style +parameter-name-style=localparam_style:CamelCase|ALL_CAPS + +# we allow nested struct definitions +-typedef-structs-unions diff --git a/vendor/lowrisc_ip/lint/tools/verilator/common.vlt b/vendor/lowrisc_ip/lint/tools/verilator/common.vlt new file mode 100644 index 0000000000..f0d4d73c08 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/verilator/common.vlt @@ -0,0 +1,6 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// common waiver rules for verilator + diff --git a/vendor/lowrisc_ip/lint/tools/verilator/comportable.vlt b/vendor/lowrisc_ip/lint/tools/verilator/comportable.vlt new file mode 100644 index 0000000000..5da936b4f0 --- /dev/null +++ b/vendor/lowrisc_ip/lint/tools/verilator/comportable.vlt @@ -0,0 +1,6 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// comportable IP waiver rules for verilator + diff --git a/vendor/lowrisc_ip/lint/util/gen-report.sh b/vendor/lowrisc_ip/lint/util/gen-report.sh new file mode 100755 index 0000000000..eba48a0865 --- /dev/null +++ b/vendor/lowrisc_ip/lint/util/gen-report.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Ascentlint report summary generation script. +# + +REPORT_DIR=reports + +#------------------------------------------------------------------------- +# print header +#------------------------------------------------------------------------- +printf "NUMBER OF LINT ERRORS PER BLOCK:\n\n" +format="%20s %10s %10s \n" +printf "${format}" "Block" "Errors" "Warnings" +echo "-------------------------------------------" + +#------------------------------------------------------------------------- +# run lint and summarize results +#------------------------------------------------------------------------- + +for report in ${REPORT_DIR}/*.rpt ; do + + # summarize results + crash=`grep "Exiting with error status" "${report%.*}.log"` + if [[ ! -z "$crash" ]]; then + error_cnt="CRASH" + warni_cnt="CRASH" + else + error_cnt=`grep "^E " "${report%.*}.rpt" | wc -l` + warni_cnt=`grep "^W " "${report%.*}.rpt" | wc -l` + fi + printf "${format}" `basename "${report%.*}"` $error_cnt $warni_cnt +done + +echo "-------------------------------------------" +echo "END SUMMARY" + +#------------------------------------------------------------------------- +# generate detailed reports +#------------------------------------------------------------------------- +printf "\n\nLIST OF ERRORS (E) AND WARNINGS (W) FOR EACH BLOCK:" +for report in ${REPORT_DIR}/*.rpt ; do + + printf "\n\n`basename "${report%.*}"`\n" + + # grep for lint crashes and lint errors, and limit line length + grep "^ ERR" -A 2 "${report%.*}.log" | cut -c -200 + grep "^E " "${report%.*}.rpt" | cut -c -200 + grep "^W " "${report%.*}.rpt" | cut -c -200 + +done diff --git a/vendor/lowrisc_ip/prim/README.md b/vendor/lowrisc_ip/prim/README.md new file mode 100644 index 0000000000..789f8c8d6f --- /dev/null +++ b/vendor/lowrisc_ip/prim/README.md @@ -0,0 +1,199 @@ +# lowRISC Hardware Primitives + +## Concepts + +This directory contains basic building blocks to create a hardware design, +called primitives. A primitive is described by its name, and has a well-defined +list of ports and parameters. + +Under the hood, primitives are slightly special, as they can have multiple +implementations. In contrast to many other modules in a hardware design, +primitives must often be implemented in technology-dependent ways. For example, +a clock multiplexer for a Xilinx FPGA is implemented differently than one for +a specific ASIC technology. + +Not all primitives need to have multiple implementations. + +* Primitives with a single, generic, implementation are normal SystemVerilog + modules inside the `hw/ip/prim/rtl` directory. We call these primitives + "technology-independent primitives". +* Primitives with multiple implementations have only a FuseSoC core file in the + `hw/ip/prim` directory. The actual implementations are in "technology + libraries". We call these primitives "technology-dependent primitives". + +### Abstract primitives + +Abstract primitives are wrappers around technology-dependent implementations of +primitives, with the ability to select a specific implementation if needed. + +In more technical terms, abstract primitives are SystemVerilog modules. The +example below shows one. + +```systemverilog +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif + +module prim_pad_wrapper +#( + parameter int unsigned AttrDw = 6 +) ( + inout wire inout_io, // bidirectional pad + output logic in_o, // input data + input out_i, // output data + input oe_i, // output enable + // additional attributes {drive strength, keeper, pull-up, pull-down, open-drain, invert} + input [AttrDw-1:0] attr_i +); + parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; + + if (Impl == prim_pkg::ImplGeneric) begin : gen_generic + prim_generic_pad_wrapper u_impl_generic ( + .* + ); + end else if (Impl == prim_pkg::ImplXilinx) begin : gen_xilinx + prim_xilinx_pad_wrapper u_impl_xilinx ( + .* + ); + end else begin : gen_failure + // TODO: Find code that works across tools and causes a compile failure + end + +endmodule +``` + +As seen from the source code snippet, abstract primitives have the following +properties: + +- They have an `Impl` parameter which can be set to choose a specific + implementation of the primitive. +- The `Impl` parameter is set to a system-wide default determined by the + `PRIM_DEFAULT_IMPL` define. +- All ports and parameters of the abstract primitive are forwarded to the + implementations. + +### Technology libraries + +Technology libraries collect implementations of primitives. + +At least one technology library must exist: the `generic` technology library, +which contains a pure-SystemVerilog implementation of the functionality. This +library is commonly used for simulations and as functional reference. The +`generic` technology library is contained in the `hw/ip/prim_generic` directory. + +In addition to the implementation in the `generic` library, primitives may be +implemented by as many other libraries as needed. + +Technology libraries are referenced by their name. + +### Technology library discovery + +In many cases, technology libraries contain vendor-specific code which cannot be +shared widely or openly. Therefore, a FuseSoC looks for available technology +libraries at build time, and makes all libraries it finds available. + +The discovery is performed based on the agreed-on naming scheme for primitives. + +- FuseSoC scans all libraries (e.g. as specified by its `--cores-root` command + line argument) for cores. +- All cores with a name matching `lowrisc:prim_TECHLIBNAME:PRIMNAME` + are considered. `TECHLIBNAME` is then added to the list of technology + libraries. + +After the discovery process has completed, a script (`primgen`) creates +- an abstract primitive (see above), and +- an entry in the `prim_pkg` package in the form of `prim_pkg::ImplTechlibname` + to identify the technology library by its name. + +## User Guide + +### Use primitives + +Primitives are normal SystemVerilog modules, and can be used as usual: +* instantiate it like a normal SystemVerilog module, and +* add a dependency in the FuseSoC core file. + +Technology-dependent primitives have an additional parameter called `Impl`. +Set this parameter to use a specific implementation of the primitive for this +specific instance. For example: + +```systemverilog +prim_ram_2p #( + .Width (TotalWidth), + .Depth (Depth), + // Force the use of the tsmc40lp technology library for this instance, instead + // of using the build-time default. + .Impl(prim_pkg::ImplTsmc40lp) +) u_mem ( + .clk_a_i (clk_i), + ... +) +``` + + +### Set the default technology library + +If no specific technology library is chosen for an instantiated primitive the +default library is used. The SystemVerilog define `PRIM_DEFAULT_IMPL` can be +used to set the default for the whole design. Set this define to one of the enum +values in `prim_pkg.sv` in the form `prim_pkg::ImplTechlibname`. `Techlibname` +is the capitalized name of the technology library. + +In the top-level FuseSoC core file the default technology library can be chosen +like this: + +```yaml +# my_toplevel.core + +# Declare filesets and other things (omitted) + +parameters: + # Make the parameter known to FuseSoC to enable overrides from the + # command line. If not overwritten, use the generic technology library. + PRIM_DEFAULT_IMPL: + datatype: str + paramtype: vlogdefine + description: Primitives implementation to use, e.g. "prim_pkg::ImplGeneric". + default: prim_pkg::ImplGeneric + +targets: + fpga_synthesis: + filesets: + - my_rtl_files + parameters: + # Use the xilinx technology library for this target by default. + - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx + toplevel: my_toplevel +``` + + +### Create a technology library + +To create a technology library follow these steps: + +- Choose a name for the new technology library. Names are all lower-case. + To ease sharing of technology libraries it is encouraged to pick a very + specific name, e.g. `tsmc40lp`, and not `asic`. +- Copy the `prim_generic` folder into an arbitrary location (can be outside + of this repository). Name the folder `prim_YOURLIBRARYNAME`. +- Replace the word `generic` everywhere with the name of your technology + library. This includes + - file and directory names (e.g. `prim_generic_ram1p.sv` becomes + `prim_tsmc40lp_ram1p.sv`), + - module names (e.g. `prim_generic_ram1p` becomes `prim_tsmc40lp_ram1p`), and + - all other references (grep for it!). +- Implement all primitives. Replace the module body of the generic + implementation with a technology-specific implementation as needed. Do *not* + modify the list of ports or parameters in any way! + +## Implementation details + +Technology-dependent primitives are implemented as a FuseSoC generator. The +core of the primitive (e.g. `lowrisc:prim:rom` in `prim/prim_rom.core`) calls +a FuseSoC generator. This generator is the script `util/primgen.py`. As input, +the script receives a list of all cores found by FuseSoC anywhere in its search +path. The script then looks through the cores FuseSoC discovered and extracts +a list of technology libraries out of it. It then goes on to create the +abstract primitive (copying over the list of parameters and ports from the +generic implementation), and an associated core file, which depends on all +technology-dependent libraries that were found. diff --git a/vendor/lowrisc_ip/prim/doc/prim_keccak.md b/vendor/lowrisc_ip/prim/doc/prim_keccak.md new file mode 100644 index 0000000000..8f0f65ac66 --- /dev/null +++ b/vendor/lowrisc_ip/prim/doc/prim_keccak.md @@ -0,0 +1,89 @@ +--- +title: "Primitive Component: Keccak permutation" +--- + +# Overview + +`prim_keccak` is a single round implementation of the permutation stage in [SHA3 algorithm][fibs-pub-202]. +Keccak primitive module assumes the number of rounds is less than or equal to 12 + 2L. +It supports all combinations of the data width described in the [spec][fibs-pub-202]. +This implementation is not currently hardened against side-channel or fault injection attacks. +It implements the Keccak_p function. + +[fibs-pub-202]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf + +## Parameters + +Name | Type | Description +------|------|---------------------------------------------------------------- +Width | int | state width in bits. can be 25, 50, 100, 200, 400, 800, or 1600 + +### Derived Parameters + +The parameters below are derived parameter from `Width` parameter. + +Name | Type | Description +---------|------|------------------------------------------------------- +W | int | number of slices in state. `Width/25` +L | int | log2 of `W` +MaxRound | int | maximum allowed round value. `12 + 2L` +RndW | int | bit-width to represent MaxRound. log2 of `MaxRound` + +## Signal Interfaces + +Signal | Type | Description +-------|---------------|------------------------------ +rnd_i | input [RndW] | current round number [0..(MaxRound-1)] +s_i | input [Width] | state input +s_o | output[Width] | permutated state output + +`s_i` and `s_o` are little-endian bitarrays. +The [SHA3 spec][fibs-pub-202] shows how to convert the bitstream into the 5x5xW state cube. +For instance, bit 0 of the stream maps to `A[0,0,0]`. +The bit 0 in the spec is the first bit of the bitstream. +In `prim_keccak`, `s_i[0]` is the first bit and `s_i[Width-1]` is the last bit. + +# Theory of Operations + +``` + | | +rnd_i | | +---/---->| -----------------------------------------\ | + [RndW] | | | + | | | +s_i | V | s_o +===/====>| bit2s() -> chi(pi(rho(theta))) -> iota( ,rnd) -> s2bit() |==/==> + [Width] | |-----------keccak_p--------------| |[Width] + | | +``` + +`prim_keccak` implements "Step Mappings" section in [SHA3 spec][fibs-pub-202]. +It is composed of five unique permutation functions, theta, rho, pi, chi, and iota. +Also it has functions that converts bitstream of `Width` into `5x5xW` state and vice versa. + +Three constant parameters are defined inside the keccak primitive module. +The rotate position described in phi function is hard-coded as below. +The value is described in the SHA3 specification. + +```systemverilog +localparam int PiRotate [5][5] = '{ + //y 0 1 2 3 4 x + '{ 0, 3, 1, 4, 2},// 0 + '{ 1, 4, 2, 0, 3},// 1 + '{ 2, 0, 3, 1, 4},// 2 + '{ 3, 1, 4, 2, 0},// 3 + '{ 4, 2, 0, 3, 1} // 4 +}; +``` + +The shift amount in rho function is defined as `RhoOffset` parameter. +The value is same as in the specification, but it is used as `RhoOffset % W`. +For instance, `RhoOffset[2][2]` is 171. +If `Width` is 1600, the value used in the design is `171%64`, which is `43`. + +The round constant is calculated by the tool `hw/ip/prim/util/keccak_rc.py`. +The recommended default value of 24 rounds is used in this design, +but an argument (changed with the `-r` flag) is provided for reference. +The `keccak_rc.py` script creates 64 bit of constants and the `prim_keccak` module uses only lower bits of the constants if the `Width` is less than 1600. +For instance, if `Width` is 800, lower 32bits of the round constant are used. + diff --git a/vendor/lowrisc_ip/prim/doc/prim_lfsr.md b/vendor/lowrisc_ip/prim/doc/prim_lfsr.md new file mode 100644 index 0000000000..a9c3af965e --- /dev/null +++ b/vendor/lowrisc_ip/prim/doc/prim_lfsr.md @@ -0,0 +1,91 @@ +--- +title: "Primitive Component: LFSR" +--- + +# Overview + +`prim_lfsr` is a parameterized linear feedback shift register (LFSR) +implementation that supports Galois (XOR form) and Fibonacci (XNOR form) +polynomials. The main difference between Galois and Fibonacci is that the +former has a shorter critical timing path since the XOR Gates are interleaved +with the shift register, whereas the latter combines several shift register taps +and reduces them with an XNOR tree. For more information, refer to +[this page](https://en.wikipedia.org/wiki/Linear-feedback_shift_register). Both +LFSR flavors have maximal period (`2^LfsrDw - 1`). The recommendation is to use +the Galois type and fall back to the Fibonacci type depending on the polynomial +width availability in the lookup table (see below). + + +## Parameters + +Name | type | Description +-------------|--------|---------------------------------------------------------- +LfsrType | string | LFSR form, can be `"GAL_XOR"` or `"FIB_XNOR"` +LfsrDw | int | Width of the LFSR +EntropyDw | int | Width of the entropy input +StateOutDw | int | Width of the LFSR state to be output (`lfsr_q[StateOutDw-1:0]`) +DefaultSeed | logic | Initial state of the LFSR, must be nonzero for XOR and non-all-ones for XNOR forms. +CustomCoeffs | logic | Custom polynomial coefficients of length LfsrDw. +MaxLenSVA | bit | Enables maximum length assertions, use only in sim and FPV. + +## Signal Interfaces + +Name | In/Out | Description +---------------------|--------|--------------------------------- +seed_en_i | input | External seed input enable +seed_i[LfsrDw] | input | External seed input +lfsr_en_i | input | Lfsr enable +entropy_i[EntropyDw] | input | Entropy input +state_o[StateOutDw] | output | LFSR state output. + +# Theory of Operations + +``` + /----------------\ +seed_en_i | | +------------>| lfsr | +seed_i | | +=====/======>| LfsrDw | + [LfsrDw] | LfsrType | +lfsr_en_i | EntropyDw | +------------>| StateOutDw | +entropy_i | DefaultSeed | state_o +=====/======>| CustomCoeffs |=====/=======> + [EntropyDw] | MaxLenSVA | [StateOutDw] + | | + \----------------/ +``` + +The LFSR module has an enable input and an additional entropy input that is +XOR'ed into the LFSR state (connect to zero if this feature is unused). The +state output contains the lower bits of the LFSR state from `StateOutDw-1` +downto `0`. As the entropy input may cause the LFSR to jump into its parasitic +state (all-zero for XOR, all-ones for XNOR), the LFSR state transition function +contains a lockup protection which re-seeds the state with `DefaultSeed` once +this condition is detected. + +The LFSR contains an external seed input `seed_i` which can be used to load a +custom seed into the LFSR by asserting `seed_en_i`. This operation takes +precedence over internal state updates. If the external seed happens to be a +parasitic state, the lockup protection feature explained above will reseed the +LFSR with the `DefaultSeed` in the next cycle. + +The LFSR coefficients are taken from an internal set of lookup tables with +precomputed coefficients. Alternatively, a custom polynomial can be provided +using the `Custom` parameter. The lookup tables contain polynomials for both +LFSR forms and range from 4bit to 64bit for the Galois form and 3bit to 168bit +for the Fibonacci form. The polynomial coefficients have been obtained from +[this page](https://users.ece.cmu.edu/~koopman/lfsr/) and +[Xilinx application note 52](https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf). +The script `./script/get-lfsr-coeffs.py` can be used to download, parse and dump +these coefficients in SV format as follows: +``` +$ script/get-lfsr-coeffs.py -o +``` +The default is to get the Galois coefficients. If the Fibonacci coefficients +are needed, add the `--fib` switch to the above command. + +The implementation of the state transition function of both polynomials have +been formally verified. Further, all polynomials up to 34bit in length have been +swept through in simulation in order to ensure that they are of +maximal-length. diff --git a/vendor/lowrisc_ip/prim/doc/prim_packer.md b/vendor/lowrisc_ip/prim/doc/prim_packer.md new file mode 100644 index 0000000000..57d57576e4 --- /dev/null +++ b/vendor/lowrisc_ip/prim/doc/prim_packer.md @@ -0,0 +1,106 @@ +--- +title: "Primitive Component: Packer" +--- + +# Overview + +`prim_packer` is a module that receives partial writes then packs and creates +full configurable width writes. It is one of a set of shared primitive modules +available for use within OpenTitan as referred to in the Comportability +Specification section on shared primitives. + +## Parameters + +Name | type | Description +-----|------|------------- +InW | int | Input data width +OutW | int | Output data width + +## Signal Interfaces + +Name | In/Out | Description +-------------|--------|------------- +valid_i | input | Input data available. +data_i[InW] | input | Input data. +mask_i[InW] | input | Input bit mask. Ones in the mask must be contiguous. +ready_o | output | Indicates if prim_packer is able to accept data. +valid_o | output | Indicates if output data is available. +data_o[OutW] | output | Output data. +mask_o[OutW] | output | Output bit mask. +ready_i | input | Output data can be drained. +flush_i | input | Send out stored data and clear state. +flush_done_o | output | Indicates flush operation is completed. + +# Theory of Opeations + +```code + /----------\ +valid_i | | valid_o +---------->| |---------------> +data_i | stacked | data_o +=====/====>| register |=======/=======> + [InW] | | [OutW] +mask_i | | mask_o +=====/====>| InW+OutW |=======/=======> +ready_o |----------| ready_i +<----------| |<--------------- + | | + \----------/ +``` + +`prim_packer` accepts `InW` bits of data and bitmask signals. On a `valid_i`/ +`ready_o` handshake, `data_i` is stored to internal registers and accumulated +until `OutW` data has been gathered. In the normal case, `mask_o` will be a +full width write (`{OutW{1'b1}}`). However, when `flush_i` is asserted, +`prim_packer` attempts to drain out all remaining data in the internal +storage. In this case, `mask_o` might be partial. + +The internal register size is `InW + OutW` bits to safely store the incoming +data and send outgoing data to the `data_o` port. + + +{{< wavejson >}} +{ signal: [ + { name: 'valid_i', wave: '01.01......0.'}, + { name: 'data_i[3:0]', wave: 'x==x===.===x.', data:'0h 1h 2h 3h 4h 5h 6h 7h'}, + { name: 'mask_i[3:0]', wave: 'x==x===.===x.', data:'Fh Fh Fh Fh Fh Fh Ch Ch'}, + { name: 'ready_o', wave: '1.....01.....'}, + { name: 'valid_o', wave: '0.10101..0.10'}, + { name: 'data_o[5:0]', wave: 'x.=x=x=.=x.=x', data:'10h 08h 03h 15h 05h'}, + { name: 'mask_o[5:0]', wave: 'x.=x=x=.=x.=x', data:'3Fh 3Fh 3Fh 3Fh 0Fh '}, + { name: 'ready_i', wave: '1.....01.....'}, + { name: 'flush_i', wave: '0..........10'}, + { name: 'flush_done_o', wave: '0..........10'}, + ], + + head:{ + text: 'prim_packer', + tick: ['0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 '] + } +} +{{< /wavejson >}} + +The above waveform shows the case of InW := 4 and OutW := 6. After the first +transaction, `prim_packer` has `0h` in the storage. When the second `valid_i` +is asserted, it combines `0h` and incoming data `1h` and creates output `10h` +(`6'b01_0000`). The remaining `2'b00` is put into the internal storage from +`data_i[3:2]`. The next transaction combines this and input data `2h` to create +`6'b00_1000`. + +`prim_packer` deasserts `ready_o` to indicate it cannot accept further data. +`ready_o` is deasserted when `ready_i` is deasserted and there is insufficient +internal storage available to store incoming data, as shown in cycle 6 above. + +At cycle 9 and 10, `mask_i` is used to only load 2 bits of data into the packer +each cycle. This is to show how the packer allows misaligned writes (smaller +than `InW`) to be packed together. + +At the end of the sequence, `flush_i` is asserted, and the remaining data is +drained. In this case, `mask_o` isn't full to indicate only partial data is +available (`6'b00_1111`). `flush_done_o` is asserted as soon as the remaining +data is drained. + +`prim_packer` only supports packing to the right. To use `prim_packer` in a +design requiring packing to the left (filling MSB first), the design needs to +reverse the bit order (and in some cases, the byte order) before pushing to the +packer, then reverse the data output. diff --git a/vendor/lowrisc_ip/prim/doc/prim_present.md b/vendor/lowrisc_ip/prim/doc/prim_present.md new file mode 100644 index 0000000000..da7856eed2 --- /dev/null +++ b/vendor/lowrisc_ip/prim/doc/prim_present.md @@ -0,0 +1,70 @@ +--- +title: "Primitive Component: PRESENT Scrambler" +--- + +# Overview + +`prim_present` is an (unhardened) implementation of the encryption pass of the [64bit PRESENT block cipher](https://en.wikipedia.org/wiki/PRESENT). +It is a fully unrolled combinational implementation that supports both key lengths specified in the paper (80bit and 128bit). +Further, the number of rounds is fully configurable, and the primitive supports a 32bit block cipher flavor which is not specified in the original paper. + +It should be noted, however, that reduced-round and/or 32bit versions **are not secure** and must not be used in a setting where cryptographic cipher strength is required. +I.e., this primitive is only intended to be used as a lightweight data scrambling device. + +## Parameters + +Name | type | Description +-------------|--------|---------------------------------------------------------- +DataWidth | int | Block size, can be 32 or 64 +KeyWidth | int | Key size, can be 64, 80 or 128 +NumRounds | int | Number of PRESENT rounds, has to be greater than 0 + +## Signal Interfaces + +Name | In/Out | Description +-------------|--------|--------------------------------- +data_i | input | Plaintext input +key_i | input | Key input +data_o | output | Output of the ciphertext + +# Theory of Operations + +``` + /---------------\ + | | + | PRESENT | +key_i | | +=====/======>| DataWidth | + [KeyWidth] | KeyWidth | + | NumRounds | +data_i | | data_o +=====/======>| |=====/=======> + [DataWidth] | | [DataWidth] + | | + \---------------/ +``` + +The PRESENT module is fully unrolled and combinational, meaning that it does not have any clock, reset or handshaking inputs. +The only inputs are the key and the plaintext, and the only output is the ciphertext. + +The internal construction follows the the algorithm described in the original [paper](http://www.lightweightcrypto.org/present/present_ches2007.pdf). +The block size is 64bit and the key size can be either 80bit or 128bit, depending on the security requirements. +In its original formulation, this cipher has 31 rounds comprised of an XOR operation with a round key, followed by the application of an s-box and a permutation layer: + +```c++ + +round_keys = key_derivation(key_i); + +state = data_i; + +for (int i=1; i < 32; i++) { + state = state ^ round_keys[i]; + state = sbox4_layer(state); + state = perm_layer(state); +} + +data_o = state ^ round_keys[32]; +``` + +The reduced 32bit block-size variant implemented is non-standard and should only be used for scrambling purposes, since it **is not secure**. +It leverages the same crypto primitives and key derivation functions as the 64bit variant, with the difference that the permutation layer is formulated for 32 instead of 64 elements. diff --git a/vendor/lowrisc_ip/prim/doc/prim_prince.md b/vendor/lowrisc_ip/prim/doc/prim_prince.md new file mode 100644 index 0000000000..8019b4f03a --- /dev/null +++ b/vendor/lowrisc_ip/prim/doc/prim_prince.md @@ -0,0 +1,112 @@ +--- +title: "Primitive Component: PRINCE Scrambler" +--- + +# Overview + +`prim_prince` is an (unhardened) implementation of the [64bit PRINCE block cipher](https://en.wikipedia.org/wiki/Prince_(cipher)). +It is a fully unrolled combinational implementation with a configurable number of rounds. +Due to the mirrored construction of this cipher, the same circuit can be used for encryption and decryption, as described below. +Further, the primitive supports a 32bit block cipher flavor which is not specified in the original paper. + +It should be noted, however, that reduced-round and/or 32bit versions **are not secure** and must not be used in a setting where cryptographic cipher strength is required. +I.e., this primitive is only intended to be used as a lightweight data scrambling device. + +This [paper](https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/documents/papers/session7-maene-paper.pdf) compares several lightweight ciphers, where PRINCE has been found to be the fastest candidate with the lowest circuit complexity among the algorithms compared. + +## Parameters + +Name | type | Description +---------------|--------|---------------------------------------------------------- +DataWidth | int | Block size, can be 32 or 64. +KeyWidth | int | Key size, can be 64 for block size 32, or 128 for block size 64 +NumRounds | int | Half the number of the reflected PRINCE rounds. Can range from 1 to 5. The effective number of non-linear layers is 2 + 2 * NumRounds. +UseOldKeySched | bit | If set to 1, fall back to the original keyschedule (not recommended). Defaults to 0. + +## Signal Interfaces + +Name | In/Out | Description +-------------|--------|--------------------------------- +data_i | input | Plaintext input +key_i | input | Key input +dec_i | input | Assert for decryption +data_o | output | Output of the ciphertext + +# Theory of Operations + +``` + /----------------\ +dec_i | | +------------>| PRINCE | +key_i | | +=====/======>| DataWidth | + [KeyWidth] | KeyWidth | + | NumRounds | +data_i | UseOldKeySched | data_o +=====/======>| |=====/=======> + [DataWidth] | | [DataWidth] + | | + \----------------/ +``` + +The PRINCE module is fully unrolled and combinational, meaning that it does not have any clock, reset or handshaking inputs. +The only inputs are the key and the plaintext, and the only output is the ciphertext. + +The internal construction follows the the algorithm described in the original [paper](https://eprint.iacr.org/2012/529.pdf). +The block size is 64bit and the key size is 128bit. +In its original formulation, this cipher has 11 rounds (but 12 non-linear layers), which are arranged in a mirrored structure, which allows the same circuit to be used for encryption and decryption with a lightweight tweak applied to the key: + +```c++ +k0, k0_prime, k1 = key_derivation(key_i, dec_i); + +// decryption mode +if (dec_i) { + swap(k0, k0_prime); + k1 ^= ALPHA_CONSTANT; +} + +state = data_i ^ k0; + +state ^= k1; +state ^= ROUND_CONSTANT[0]; + +// forward pass +for (int i=1; i < 6; i++) { + state = sbox4_layer(state); + state = mult_layer(state); + state = shiftrows_layer(state); + state ^= ROUND_CONSTANT[i] + data_state ^= (k & 0x1) ? k0 : k1; +} + +// middle part +state = sbox4_layer(state); +state = mult_layer(state); +state = sbox4_inverse_layer(state); + +// reverse pass +for (int i=6; i < 11; i++) { + data_state ^= (k & 0x1) ? k1 : k0; + state ^= ROUND_CONSTANT[i] + state = shiftrows_inverse_layer(state); + state = mult_layer(state); + state = sbox4_inverse_layer(state); +} + +state ^= ROUND_CONSTANT[11]; +state ^= k1; + +data_o = state ^ k0_prime; +``` +The multiplicative layer is an involution, meaning that it is its own inverse and it can hence be used in the reverse pass without inversion. + +It should be noted that the actual choice of the `ALPHA_CONSTANT` used in the key tweak can have security impacts as detailed in [this paper](https://eprint.iacr.org/2015/372.pdf). +The constant chosen by the designers of PRINCE does not have these issues - but proper care should be taken if it is decided to modify this constant. +Also, [this paper](https://eprint.iacr.org/2014/656.pdf) proposes an improved key schedule to fend against attacks on the FX structure of PRINCE (see Appendix C), and this improvement has been incorporated in this design. +The improvement involves alternating the keys `k0` and `k1` between rounds, as opposed to always using the same key `k1`. + + +The reduced 32bit variant mentioned above and all reduced round variants are non-standard and must only be used for scrambling purposes, since they **are not secure**. +The 32bit variant leverages the same crypto primitives and key derivation functions as the 64bit variant, with the difference that the multiplication matrix is only comprised of the first two block diagonal submatrices (^M0 and ^M1 in the paper), and the shiftrows operation does not operate on nibbles but pairs of 2 bits instead. + + diff --git a/vendor/lowrisc_ip/prim/dv/Makefile b/vendor/lowrisc_ip/prim/dv/Makefile new file mode 100644 index 0000000000..cb85b43be4 --- /dev/null +++ b/vendor/lowrisc_ip/prim/dv/Makefile @@ -0,0 +1,46 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# Entry point test Makefile for building and running tests. +# These are generic set of option groups that apply to all testbenches. +# This flow requires the following options to be set: +# DV_DIR - current dv directory that contains the test Makefile +# DUT_TOP - top level dut module name +# TB_TOP - top level tb module name +# DOTF - .f file used for compilation +# COMPILE_KEY - compile option set +# TEST_NAME - name of the test to run - this is supplied on the command line +DV_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +COMPILE_KEY ?= default + +########################################################## +# A D D I N D I V I D U A L T E S T S B E L O W # +########################################################## + +ifeq (${TEST_NAME},prim_lfsr_gal_xor) + export DUT_TOP := prim_lfsr + export TB_TOP := prim_lfsr_tb + FUSESOC_CORE := lowrisc:dv:prim_lfsr_sim:0.1 + COMPILE_KEY := gal_xor +endif + +ifeq (${TEST_NAME},prim_lfsr_fib_xnor) + export DUT_TOP := prim_lfsr + export TB_TOP := prim_lfsr_tb + FUSESOC_CORE := lowrisc:dv:prim_lfsr_sim:0.1 + COMPILE_KEY := fib_xnor +endif + +ifeq (${COMPILE_KEY},gal_xor) + BUILD_OPTS := +define+LFSR_TYPE="\"GAL_XOR\""+MAX_LFSR_DW=28+MIN_LFSR_DW=4 +endif + +ifeq (${COMPILE_KEY},fib_xnor) + BUILD_OPTS := +define+LFSR_TYPE="\"FIB_XNOR\""+MAX_LFSR_DW=28+MIN_LFSR_DW=3 +endif + +#################################### +# Include the tool Makefile below # +# Dont add anything else below it! # +#################################### +include ${DV_DIR}/../../../dv/tools/Makefile diff --git a/vendor/lowrisc_ip/prim/dv/prim_lfsr_sim.core b/vendor/lowrisc_ip/prim/dv/prim_lfsr_sim.core new file mode 100644 index 0000000000..03b28b22bb --- /dev/null +++ b/vendor/lowrisc_ip/prim/dv/prim_lfsr_sim.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv:prim_lfsr_sim:0.1" +description: "LFSR DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:common_ifs + files: + - tb/prim_lfsr_tb.sv + - tb/prim_lfsr_bind.sv + file_type: systemVerilogSource + +targets: + sim: + toplevel: prim_lfsr_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs diff --git a/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_bind.sv b/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_bind.sv new file mode 100644 index 0000000000..c90b3ed631 --- /dev/null +++ b/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_bind.sv @@ -0,0 +1,9 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module prim_lfsr_bind; + + // nothing to bind here yet + +endmodule : prim_lfsr_bind diff --git a/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_tb.sv b/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_tb.sv new file mode 100644 index 0000000000..e75a16c34f --- /dev/null +++ b/vendor/lowrisc_ip/prim/dv/tb/prim_lfsr_tb.sv @@ -0,0 +1,149 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_lfsr, sweeps through all implementations +// within a certain range to check whether they are max length. + +module prim_lfsr_tb; + +////////////////////////////////////////////////////// +// config +////////////////////////////////////////////////////// + +// this can be overriden on the command line +// supported types are GAL_XOR, FIB_XNOR +`ifdef LFSR_TYPE + localparam string LfsrType = `LFSR_TYPE; +`else + localparam string LfsrType = "GAL_XOR"; +`endif +`ifdef MIN_LFSR_DW + localparam int unsigned MinLfsrDw = `MIN_LFSR_DW; +`else + localparam int unsigned MinLfsrDw = 4; +`endif +`ifdef MAX_LFSR_DW + localparam int unsigned MaxLfsrDw = `MAX_LFSR_DW; +`else + localparam int unsigned MaxLfsrDw = 32; +`endif + + // leave this constant + localparam logic SEED = 1'b1; + + localparam time ClkPeriod = 10000; + +////////////////////////////////////////////////////// +// clock +////////////////////////////////////////////////////// + + wire clk, rst_n; + + clk_rst_if main_clk ( + .clk, + .rst_n + ); + +////////////////////////////////////////////////////// +// DUTs +////////////////////////////////////////////////////// + + logic [MaxLfsrDw:0] lfsr_en, err; + logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] state_out; + logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] lfsr_periods; + + for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts + prim_lfsr #( + .LfsrType ( LfsrType ), + .LfsrDw ( k ), + .EntropyDw ( 1 ), + .StateOutDw ( k ), + .DefaultSeed ( k'(SEED) ), + // enable internal max length check + .MaxLenSVA ( 1'b1 ) + ) i_prim_lfsr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .seed_en_i ( 1'b0 ), + .seed_i ( '0 ), + .lfsr_en_i ( lfsr_en[k] ), + .entropy_i ( 1'b0 ), + .state_o ( state_out[k][k-1:0] ) + ); + + if (k < MaxLfsrDw) begin : gen_tie_off + assign state_out[k][MaxLfsrDw-1:k] = '0; + end + + // calculate period of LFSR: + assign lfsr_periods[k] = MaxLfsrDw'({{(k-1){1'b1}}, 1'b0}); + end + +////////////////////////////////////////////////////// +// stimuli application / response checking +////////////////////////////////////////////////////// + + initial begin : p_stimuli + lfsr_en = '0; + err = '0; + + main_clk.set_period_ns(ClkPeriod); + main_clk.set_active(); + main_clk.apply_reset(); + + $display("LFSR maxlen test started for %s (%0d bit to %0d bit).", + LfsrType, MinLfsrDw, MaxLfsrDw); + + main_clk.wait_clks(10); + + // enable all LFSRs + lfsr_en = '1; + + $display("Running for 2**%0d-1 cycles...", MaxLfsrDw); + for (longint unsigned k = 0; k <= lfsr_periods[MaxLfsrDw]; k++ ) begin + + main_clk.wait_clks(1); + + for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin + // check if we reached the initial state again + if (state_out[j] == MaxLfsrDw'(SEED) && lfsr_en[j]) begin + // $display("cycle: %d -- lfsr: %d -- %x ?= %x, %x", + // k, j, state_out[j], SEED, lfsr_en); + lfsr_en[j] = 1'b0; + // we expect this to occur only after the maximum length period + if (lfsr_periods[j] == k) begin + $display("Maxlen check for LFSR %0d succeeded!", j); + end else begin + err[j] = 1'b1; + $error("Error LFSR %0d is not maximal length!", j); + end + end + end + end + + main_clk.wait_clks(10); + + for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin + if (lfsr_en[j]) begin + $error("Error LFSR %0d never got back to initial state!", j); + err[j] = 1'b1; + end + end + + if (!err) begin + $display("All LFSRs from %0d bit to %0d have maximum length!", + MinLfsrDw, MaxLfsrDw); + // signature for makefile + $display("TEST PASSED CHECKS"); + end else begin + $display("One or more checks have failed!"); + // signature for makefile + $display("TEST FAILED CHECKS"); + end + + $finish(); + end + + +endmodule : prim_lfsr_tb diff --git a/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_async_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_async_fpv.core new file mode 100644 index 0000000000..3189f81250 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_async_fpv.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_alert_rxtx_async_fpv:0.1" +description: "ALERT_HANDLER rxtx async FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - vip/prim_alert_rxtx_async_assert_fpv.sv + - tb/prim_alert_rxtx_async_fpv.sv + - tb/prim_alert_rxtx_async_bind_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: + - prim_alert_rxtx_async_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_fpv.core new file mode 100644 index 0000000000..492fa28ea8 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_alert_rxtx_fpv.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_alert_rxtx_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - vip/prim_alert_rxtx_assert_fpv.sv + - tb/prim_alert_rxtx_fpv.sv + - tb/prim_alert_rxtx_bind_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: + - prim_alert_rxtx_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_arbiter_ppc_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_arbiter_ppc_fpv.core new file mode 100644 index 0000000000..88e3f31618 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_arbiter_ppc_fpv.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_arbiter_ppc_fpv:0.1" +description: "prim_arbiter_ppc FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + # TODO: add more dependencies here if needed + files: + - vip/prim_arbiter_ppc_assert_fpv.sv + - tb/prim_arbiter_ppc_bind_fpv.sv + - tb/prim_arbiter_ppc_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: prim_arbiter_ppc_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_arbiter_tree_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_arbiter_tree_fpv.core new file mode 100644 index 0000000000..57dc41948c --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_arbiter_tree_fpv.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_arbiter_tree_fpv:0.1" +description: "prim_arbiter_tree FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + # TODO: add more dependencies here if needed + files: + - vip/prim_arbiter_tree_assert_fpv.sv + - tb/prim_arbiter_tree_bind_fpv.sv + - tb/prim_arbiter_tree_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: prim_arbiter_tree_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_esc_rxtx_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_esc_rxtx_fpv.core new file mode 100644 index 0000000000..d3f4bf7e93 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_esc_rxtx_fpv.core @@ -0,0 +1,28 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_esc_rxtx_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - vip/prim_esc_rxtx_assert_fpv.sv + - tb/prim_esc_rxtx_bind_fpv.sv + - tb/prim_esc_rxtx_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + formal: icarus + filesets: + - files_formal + toplevel: + - prim_esc_rxtx_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_fifo_sync_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_fifo_sync_fpv.core new file mode 100644 index 0000000000..00dd74f1a3 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_fifo_sync_fpv.core @@ -0,0 +1,27 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_fifo_sync_fpv:0.1" +description: "prim_fifo_sync FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - vip/prim_fifo_sync_assert_fpv.sv + - tb/prim_fifo_sync_bind_fpv.sv + - tb/prim_fifo_sync_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: prim_fifo_sync_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/prim_keccak_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_keccak_fpv.core new file mode 100644 index 0000000000..348342f5a0 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_keccak_fpv.core @@ -0,0 +1,23 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_keccak_fpv:0.1" +description: "Keccak_f FPV target" +filesets: + files_fpv: + depend: + - lowrisc:prim:all + files: + - tb/prim_keccak_fpv.sv + file_type: systemVerilogSource + +targets: + default: + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_fpv + toplevel: + - prim_keccak_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/prim_lfsr_fpv.core b/vendor/lowrisc_ip/prim/fpv/prim_lfsr_fpv.core new file mode 100644 index 0000000000..58441bcb7c --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/prim_lfsr_fpv.core @@ -0,0 +1,26 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:fpv:prim_lfsr_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - tb/prim_lfsr_fpv.sv + file_type: systemVerilogSource + +targets: + default: &default_target + # note, this setting is just used + # to generate a file list for jg + default_tool: icarus + filesets: + - files_formal + toplevel: + - prim_lfsr_fpv + + formal: + <<: *default_target diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv new file mode 100644 index 0000000000..295da5d610 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_alert_rxtx_async_bind_fpv; + + bind prim_alert_rxtx_async_fpv + prim_alert_rxtx_async_assert_fpv prim_alert_rxtx_async_assert_fpv ( + .clk_i, + .rst_ni, + .ping_err_pi, + .ping_err_ni, + .ping_skew_i, + .ack_err_pi, + .ack_err_ni, + .ack_skew_i, + .alert_err_pi, + .alert_err_ni, + .alert_skew_i, + .alert_i, + .ping_en_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_async_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.sv new file mode 100644 index 0000000000..f4bed9a96c --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_async_fpv.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 +// +// Testbench module for alert sender/receiver pair. Intended to use with +// a formal tool. + +module prim_alert_rxtx_async_fpv + import prim_alert_pkg::*; + import prim_esc_pkg::*; +( + input clk_i, + input rst_ni, + // for sigint error and skew injection only + input ping_err_pi, + input ping_err_ni, + input [1:0] ping_skew_i, + input ack_err_pi, + input ack_err_ni, + input [1:0] ack_skew_i, + input alert_err_pi, + input alert_err_ni, + input [1:0] alert_skew_i, + // normal I/Os + input alert_i, + input ping_en_i, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // asynchronous case + localparam bit AsyncOn = 1'b1; + + logic ping_pd; + logic ping_nd; + logic ack_pd; + logic ack_nd; + logic alert_pd; + logic alert_nd; + + alert_rx_t alert_rx_out, alert_rx_in; + alert_tx_t alert_tx_out, alert_tx_in; + + // for the purposes of FPV, we currently emulate the asynchronous transition + // only in terms of the skew it may introduce (which is limited to +- 1 cycle) + logic [1:0] ping_pq; + logic [1:0] ping_nq; + logic [1:0] ack_pq; + logic [1:0] ack_nq; + logic [1:0] alert_pq; + logic [1:0] alert_nq; + + assign ping_pd = alert_rx_out.ping_p; + assign ping_nd = alert_rx_out.ping_n; + assign ack_pd = alert_rx_out.ack_p; + assign ack_nd = alert_rx_out.ack_n; + assign alert_rx_in.ping_p = ping_pq[ping_skew_i[0]] ^ ping_err_pi; + assign alert_rx_in.ping_n = ping_nq[ping_skew_i[1]] ^ ping_err_ni; + assign alert_rx_in.ack_p = ack_pq[ack_skew_i[0]] ^ ack_err_pi; + assign alert_rx_in.ack_n = ack_nq[ack_skew_i[1]] ^ ack_err_ni; + + assign alert_pd = alert_tx_out.alert_p; + assign alert_nd = alert_tx_out.alert_n; + assign alert_tx_in.alert_p = alert_pq[alert_skew_i[0]] ^ alert_err_pi; + assign alert_tx_in.alert_n = alert_nq[alert_skew_i[1]] ^ alert_err_ni; + + prim_alert_sender #( + .AsyncOn ( AsyncOn ) + ) i_prim_alert_sender ( + .clk_i , + .rst_ni , + .alert_i , + .alert_rx_i ( alert_rx_in ), + .alert_tx_o ( alert_tx_out ) + ); + + prim_alert_receiver #( + .AsyncOn ( AsyncOn ) + ) i_prim_alert_receiver ( + .clk_i , + .rst_ni , + .ping_en_i , + .ping_ok_o , + .integ_fail_o , + .alert_o , + .alert_rx_o ( alert_rx_out ), + .alert_tx_i ( alert_tx_in ) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_skew_delay + if (!rst_ni) begin + ping_pq <= '0; + ping_nq <= '1; + ack_pq <= '0; + ack_nq <= '1; + alert_pq <= '0; + alert_nq <= '1; + end else begin + ping_pq <= {ping_pq [$high(ping_pq )-1:0], ping_pd}; + ping_nq <= {ping_nq [$high(ping_nq )-1:0], ping_nd}; + ack_pq <= {ack_pq [$high(ack_pq )-1:0], ack_pd}; + ack_nq <= {ack_nq [$high(ack_nq )-1:0], ack_nd}; + alert_pq <= {alert_pq[$high(alert_pq)-1:0], alert_pd}; + alert_nq <= {alert_nq[$high(alert_nq)-1:0], alert_nd}; + end + end + +endmodule : prim_alert_rxtx_async_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv new file mode 100644 index 0000000000..9158997007 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_alert_rxtx_bind_fpv; + + bind prim_alert_rxtx_fpv + prim_alert_rxtx_assert_fpv prim_alert_rxtx_assert_fpv ( + .clk_i, + .rst_ni, + .ping_err_pi, + .ping_err_ni, + .ack_err_pi, + .ack_err_ni, + .alert_err_pi, + .alert_err_ni, + .alert_i, + .ping_en_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv new file mode 100644 index 0000000000..98e9fe6c64 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_alert_rxtx_fpv.sv @@ -0,0 +1,66 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for alert sender/receiver pair. Intended to use with +// a formal tool. + +module prim_alert_rxtx_fpv + import prim_alert_pkg::*; + import prim_esc_pkg::*; +( + input clk_i, + input rst_ni, + // for sigint error injection only + input ping_err_pi, + input ping_err_ni, + input ack_err_pi, + input ack_err_ni, + input alert_err_pi, + input alert_err_ni, + // normal I/Os + input alert_i, + input ping_en_i, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // synchronous case + localparam bit AsyncOn = 1'b0; + + alert_rx_t alert_rx_out, alert_rx_in; + alert_tx_t alert_tx_out, alert_tx_in; + + assign alert_rx_in.ping_p = alert_rx_out.ping_p ^ ping_err_pi; + assign alert_rx_in.ping_n = alert_rx_out.ping_n ^ ping_err_ni; + assign alert_rx_in.ack_p = alert_rx_out.ack_p ^ ack_err_pi; + assign alert_rx_in.ack_n = alert_rx_out.ack_n ^ ack_err_ni; + + assign alert_tx_in.alert_p = alert_tx_out.alert_p ^ alert_err_pi; + assign alert_tx_in.alert_n = alert_tx_out.alert_n ^ alert_err_ni; + + prim_alert_sender #( + .AsyncOn ( AsyncOn ) + ) i_prim_alert_sender ( + .clk_i , + .rst_ni , + .alert_i , + .alert_rx_i ( alert_rx_in ), + .alert_tx_o ( alert_tx_out ) + ); + + prim_alert_receiver #( + .AsyncOn ( AsyncOn ) + ) i_prim_alert_receiver ( + .clk_i , + .rst_ni , + .ping_en_i , + .ping_ok_o , + .integ_fail_o , + .alert_o , + .alert_rx_o ( alert_rx_out ), + .alert_tx_i ( alert_tx_in ) + ); + +endmodule : prim_alert_rxtx_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_bind_fpv.sv new file mode 100644 index 0000000000..0dba4c1622 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_bind_fpv.sv @@ -0,0 +1,25 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_arbiter_ppc_bind_fpv; + + + bind prim_arbiter_ppc prim_arbiter_ppc_assert_fpv #( + .N(N), + .DW(DW) + ) i_prim_arbiter_ppc_assert_fpv ( + .clk_i, + .rst_ni, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_ppc_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv new file mode 100644 index 0000000000..08dbff9951 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_ppc_fpv.sv @@ -0,0 +1,40 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_arbiter_ppc. +// Intended to be used with a formal tool. + +module prim_arbiter_ppc_fpv #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32 +) ( + input clk_i, + input rst_ni, + input [N-1:0] req_i, + input [DW-1:0]data_i [N], + output logic[N-1:0] gnt_o, + output logic[$clog2(N)-1:0] idx_o, + output logic valid_o, + output logic[DW-1:0] data_o, + input ready_i +); + + + prim_arbiter_ppc #( + .N(N), + .DW(DW) + ) i_prim_arbiter_ppc ( + .clk_i, + .rst_ni, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_ppc_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_bind_fpv.sv new file mode 100644 index 0000000000..274d36fe91 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_bind_fpv.sv @@ -0,0 +1,26 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_arbiter_tree_bind_fpv; + + + bind prim_arbiter_tree prim_arbiter_tree_assert_fpv #( + .N(N), + .DW(DW), + .Lock(Lock) + ) i_prim_arbiter_tree_assert_fpv ( + .clk_i, + .rst_ni, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_tree_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv new file mode 100644 index 0000000000..c763eefd3a --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_arbiter_tree_fpv.sv @@ -0,0 +1,40 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_arbiter_tree. +// Intended to be used with a formal tool. + +module prim_arbiter_tree_fpv #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32, + parameter bit Lock = 1'b1 +) ( + input clk_i, + input rst_ni, + input [N-1:0] req_i, + input [DW-1:0]data_i [N], + output logic[N-1:0] gnt_o, + output logic[$clog2(N)-1:0] idx_o, + output logic valid_o, + output logic[DW-1:0] data_o, + input ready_i +); + + prim_arbiter_tree #( + .N(N), + .DW(DW), + .Lock(Lock) + ) i_prim_arbiter_tree ( + .clk_i, + .rst_ni, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + +endmodule : prim_arbiter_tree_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv new file mode 100644 index 0000000000..def9f1f0b6 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_esc_rxtx_bind_fpv; + + bind prim_esc_rxtx_fpv + prim_esc_rxtx_assert_fpv prim_esc_rxtx_assert_fpv ( + .clk_i , + .rst_ni , + .resp_err_pi , + .resp_err_ni , + .esc_err_pi , + .esc_err_ni , + .esc_en_i , + .ping_en_i , + .ping_ok_o , + .integ_fail_o, + .esc_en_o + ); + +endmodule : prim_esc_rxtx_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv new file mode 100644 index 0000000000..492bac3687 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_esc_rxtx_fpv.sv @@ -0,0 +1,54 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for escalation sender/receiver pair. Intended to use with +// a formal tool. + +module prim_esc_rxtx_fpv + import prim_alert_pkg::*; + import prim_esc_pkg::*; +( + input clk_i, + input rst_ni, + // for sigint error injection only + input resp_err_pi, + input resp_err_ni, + input esc_err_pi, + input esc_err_ni, + // normal I/Os + input esc_en_i, + input ping_en_i, + output logic ping_ok_o, + output logic integ_fail_o, + output logic esc_en_o +); + + esc_rx_t esc_rx_in, esc_rx_out; + esc_tx_t esc_tx_in, esc_tx_out; + + assign esc_rx_in.resp_p = esc_rx_out.resp_p ^ resp_err_pi; + assign esc_rx_in.resp_n = esc_rx_out.resp_n ^ resp_err_ni; + assign esc_tx_in.esc_p = esc_tx_out.esc_p ^ esc_err_pi; + assign esc_tx_in.esc_n = esc_tx_out.esc_n ^ esc_err_ni; + + prim_esc_sender i_prim_esc_sender ( + .clk_i , + .rst_ni , + .ping_en_i , + .ping_ok_o , + .integ_fail_o , + .esc_en_i , + .esc_rx_i ( esc_rx_in ), + .esc_tx_o ( esc_tx_out ) + ); + + prim_esc_receiver i_prim_esc_receiver ( + .clk_i , + .rst_ni , + .esc_en_o , + .esc_rx_o ( esc_rx_out ), + .esc_tx_i ( esc_tx_in ) + ); + +endmodule : prim_esc_rxtx_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv new file mode 100644 index 0000000000..1b70d74322 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv @@ -0,0 +1,219 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +module prim_fifo_sync_bind_fpv; + + localparam int unsigned Width = 4; + + // need to instantiate by hand since bind statements inside + // generate blocks are currently not supported + + //////////////////// + // non-pass FIFOs // + //////////////////// + + bind i_nopass1 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b0), + .Depth(1), + .EnableDataCheck(1'b1) + ) i_nopass1_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_nopass7 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b0), + .Depth(7), + .EnableDataCheck(1'b1) + ) i_nopass7_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_nopass8 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b0), + .Depth(8), + .EnableDataCheck(1'b1) + ) i_nopass8_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_nopass15 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b0), + .Depth(15), + .EnableDataCheck(1'b0) + ) i_nopass15_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_nopass16 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b0), + .Depth(16), + .EnableDataCheck(1'b0) + ) i_nopass16_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + //////////////// + // pass FIFOs // + //////////////// + + bind i_pass0 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(0), + .EnableDataCheck(1'b1) + ) i_pass0_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_pass1 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(1), + .EnableDataCheck(1'b1) + ) i_pass1_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_pass7 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(7), + .EnableDataCheck(1'b1) + ) i_pass7_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_pass8 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(8), + .EnableDataCheck(1'b1) + ) i_pass8_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_pass15 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(15), + .EnableDataCheck(1'b0) + ) i_pass15_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + + bind i_pass16 prim_fifo_sync_assert_fpv #( + .Width(Width), + .Pass(1'b1), + .Depth(16), + .EnableDataCheck(1'b0) + ) i_pass16_assert_fpv ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid, + .wready, + .wdata, + .rvalid, + .rready, + .rdata, + .depth + ); + +endmodule : prim_fifo_sync_bind_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_fpv.sv new file mode 100644 index 0000000000..4c0c1d1d49 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_fifo_sync_fpv.sv @@ -0,0 +1,236 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_fifo_sync. +// Intended to be used with a formal tool. +// +// This formal testbench instantiates a set of differently parameterized FIFOs: +// +// - a depth 0 pass through FIFO +// - depth 1, 7, 8, 15, 16 pass through FIFOs +// - depth 1, 7, 8, 15, 16 non-pass through FIFOs +// +// Data/depth value checks are enabled up to depth 8 in order to constrain the +// runtime. + +module prim_fifo_sync_fpv #( + // number of DUTs instantiated in this FPV testbench + parameter int unsigned NumDuts = 11, + // fifo params + parameter int unsigned Width = 4, + parameter int unsigned MaxDepth = 16, // max depth used in this destbench + localparam int unsigned DepthW = ($clog2(MaxDepth+1) == 0) ? 1 : $clog2(MaxDepth+1) +) ( + input clk_i, + input rst_ni, + input clr_i, + input wvalid [NumDuts], + output wready [NumDuts], + input [Width-1:0] wdata [NumDuts], + output rvalid [NumDuts], + input rready [NumDuts], + output [Width-1:0] rdata [NumDuts], + output [DepthW-1:0] depth [NumDuts] +); + + // need to instantiate by hand since bind statements inside + // generate blocks are currently not supported + + //////////////////// + // non-pass FIFOs // + //////////////////// + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(1) + ) i_nopass1 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[0]), + .wready(wready[0]), + .wdata(wdata[0]), + .rvalid(rvalid[0]), + .rready(rready[0]), + .rdata(rdata[0]), + .depth(depth[0][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(7) + ) i_nopass7 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[1]), + .wready(wready[1]), + .wdata(wdata[1]), + .rvalid(rvalid[1]), + .rready(rready[1]), + .rdata(rdata[1]), + .depth(depth[1][2:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(8) + ) i_nopass8 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[2]), + .wready(wready[2]), + .wdata(wdata[2]), + .rvalid(rvalid[2]), + .rready(rready[2]), + .rdata(rdata[2]), + .depth(depth[2][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(15) + ) i_nopass15 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[3]), + .wready(wready[3]), + .wdata(wdata[3]), + .rvalid(rvalid[3]), + .rready(rready[3]), + .rdata(rdata[3]), + .depth(depth[3][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(16) + ) i_nopass16 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[4]), + .wready(wready[4]), + .wdata(wdata[4]), + .rvalid(rvalid[4]), + .rready(rready[4]), + .rdata(rdata[4]), + .depth(depth[4][4:0]) + ); + + //////////////// + // pass FIFOs // + //////////////// + + // depth-zero is per definition a pass-through FIFO + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(0) + ) i_pass0 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[5]), + .wready(wready[5]), + .wdata(wdata[5]), + .rvalid(rvalid[5]), + .rready(rready[5]), + .rdata(rdata[5]), + .depth(depth[5][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(1) + ) i_pass1 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[6]), + .wready(wready[6]), + .wdata(wdata[6]), + .rvalid(rvalid[6]), + .rready(rready[6]), + .rdata(rdata[6]), + .depth(depth[6][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(7) + ) i_pass7 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[7]), + .wready(wready[7]), + .wdata(wdata[7]), + .rvalid(rvalid[7]), + .rready(rready[7]), + .rdata(rdata[7]), + .depth(depth[7][2:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(8) + ) i_pass8 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[8]), + .wready(wready[8]), + .wdata(wdata[8]), + .rvalid(rvalid[8]), + .rready(rready[8]), + .rdata(rdata[8]), + .depth(depth[8][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(15) + ) i_pass15 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[9]), + .wready(wready[9]), + .wdata(wdata[9]), + .rvalid(rvalid[9]), + .rready(rready[9]), + .rdata(rdata[9]), + .depth(depth[9][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(16) + ) i_pass16 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid(wvalid[10]), + .wready(wready[10]), + .wdata(wdata[10]), + .rvalid(rvalid[10]), + .rready(rready[10]), + .rdata(rdata[10]), + .depth(depth[10][4:0]) + ); + +endmodule : prim_fifo_sync_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_keccak_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_keccak_fpv.sv new file mode 100644 index 0000000000..78cb994cd2 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_keccak_fpv.sv @@ -0,0 +1,74 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_keccak. Intended to be used with a formal tool. + +module prim_keccak_fpv #( + parameter int Width = 1600 +) ( + input clk_i, + input rst_ni, + input valid_i, + input [Width-1:0] state_i, + output logic done_o, + output logic [Width-1:0] state_o +); + + localparam int W = Width/25; + localparam int L = $clog2(W); + localparam int NumRound = 12 + 2*L; // Keccak-f only + localparam int RndW = $clog2(NumRound+1); + logic [RndW-1:0] round; + logic active; + logic [Width-1:0] state, state_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) state <= '0; + else if (valid_i) state <= state_i; + else if (active) state <= state_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) round <= '0; + else if (valid_i) round <= '0; + else if (active) round <= round + 1'b 1; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) active <= 1'b 0; + else if (valid_i) active <= 1'b 1; + else if (round == (NumRound -1)) active <= 1'b 0; + end + + assign done_o = (round == NumRound); + assign state_o = state; + + prim_keccak #( + .Width (Width) + ) u_keccak ( + .rnd_i (round), + .s_i (state), + .s_o (state_d) + ); + + + `ASSUME_FPV(ValidSequence_A, ##1 valid_i |=> !valid_i) + `ASSUME_FPV(ValidValid_A, active |-> !valid_i) + + // Test with value 0 + logic [1599:0] data_0 ; + always_comb begin + data_0 = '0; + // SHA3-256 ==> r : 1088 + data_0[1087] = 1'b 1; + data_0[2:0] = 3'b 110; + end + logic [255:0] digest_0; + // Big-Endian a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a + assign digest_0 = 256'h 4a43_f880_4b0a_d882_fa49_3be4_4dff_80f5_62d6_61a0_5647_c151_66d7_1ebf_f8c6_ffa7; + `ASSUME_FPV(Data0TestSHA3_256_A, state_i == data_0) + `ASSERT(DigestForData0TestSHA3_256_A, done_o |-> state_o[255:0] == digest_0) + +endmodule + diff --git a/vendor/lowrisc_ip/prim/fpv/tb/prim_lfsr_fpv.sv b/vendor/lowrisc_ip/prim/fpv/tb/prim_lfsr_fpv.sv new file mode 100644 index 0000000000..b747eddc4c --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/tb/prim_lfsr_fpv.sv @@ -0,0 +1,75 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_lfsr. Intended to be used with a formal tool. + +module prim_lfsr_fpv #( + // LFSR entropy and output bitwidths (set to 1 here as they are unused) + parameter int unsigned EntropyDw = 1, + parameter int unsigned StateOutDw = 1, + // this specifies the range of differently + // parameterized LFSRs to instantiate and check + parameter int unsigned GalXorMinLfsrDw = 4, + parameter int unsigned GalXorMaxLfsrDw = 64, + parameter int unsigned FibXnorMinLfsrDw = 3, + parameter int unsigned FibXnorMaxLfsrDw = 168, + // LFSRs up to this bitwidth are checked for maximum length + parameter int unsigned MaxLenSVAThresh = 10, + // derived params + localparam int unsigned GalMaxGtFibMax = GalXorMaxLfsrDw > FibXnorMaxLfsrDw, + localparam int unsigned MaxLfsrDw = GalXorMaxLfsrDw * GalMaxGtFibMax + + FibXnorMaxLfsrDw * (1 - GalMaxGtFibMax), + localparam int unsigned NumDuts = FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1 + + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1 +) ( + input clk_i, + input rst_ni, + input [NumDuts-1:0] load_ext_en_i, + input [NumDuts-1:0][MaxLfsrDw-1:0] seed_ext_i, + input [NumDuts-1:0] lfsr_en_i, + input [NumDuts-1:0][EntropyDw-1:0] entropy_i, + output logic [NumDuts-1:0][StateOutDw-1:0] state_o +); + + for (genvar k = GalXorMinLfsrDw; k <= GalXorMaxLfsrDw; k++) begin : gen_gal_xor_duts + localparam int unsigned idx = k - GalXorMinLfsrDw; + prim_lfsr #(.LfsrType("GAL_XOR"), + .LfsrDw ( k ), + .EntropyDw ( EntropyDw ), + .StateOutDw ( StateOutDw ), + .DefaultSeed ( 1 ), + // disabled for large LFSRs since this becomes prohibitive in runtime + .MaxLenSVA ( k <= MaxLenSVAThresh ) + ) i_prim_lfsr ( + .clk_i, + .rst_ni, + .seed_en_i ( load_ext_en_i[idx] ), + .seed_i ( k'(seed_ext_i[idx]) ), + .lfsr_en_i ( lfsr_en_i[idx] ), + .entropy_i ( entropy_i[idx] ), + .state_o ( state_o[idx] ) + ); + end + + for (genvar k = FibXnorMinLfsrDw; k <= FibXnorMaxLfsrDw; k++) begin : gen_fib_xnor_duts + localparam int unsigned idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1; + prim_lfsr #(.LfsrType("FIB_XNOR"), + .LfsrDw ( k ), + .EntropyDw ( EntropyDw ), + .StateOutDw ( StateOutDw ), + .DefaultSeed ( 1 ), + // disabled for large LFSRs since this becomes prohibitive in runtime + .MaxLenSVA ( k <= MaxLenSVAThresh ) + ) i_prim_lfsr ( + .clk_i, + .rst_ni, + .seed_en_i ( load_ext_en_i[idx] ), + .seed_i ( k'(seed_ext_i[idx]) ), + .lfsr_en_i ( lfsr_en_i[idx] ), + .entropy_i ( entropy_i[idx] ), + .state_o ( state_o[idx] ) + ); + end + +endmodule : prim_lfsr_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv new file mode 100644 index 0000000000..f5684c305c --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv @@ -0,0 +1,92 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for alert sender/receiver pair. Intended to use with +// a formal tool. + +`include "prim_assert.sv" + +module prim_alert_rxtx_assert_fpv ( + input clk_i, + input rst_ni, + // for sigint error injection only + input ping_err_pi, + input ping_err_ni, + input ack_err_pi, + input ack_err_ni, + input alert_err_pi, + input alert_err_ni, + // normal I/Os + input alert_i, + input ping_en_i, + input ping_ok_o, + input integ_fail_o, + input alert_o +); + + logic error_present; + assign error_present = ping_err_pi | ping_err_ni | + ack_err_pi | ack_err_ni | + alert_err_pi | alert_err_ni; + + // note: we can only detect sigint errors where one wire is flipped. + `ASSUME_FPV(PingErrorsAreOH_M, $onehot0({ping_err_pi, ping_err_ni}), clk_i, !rst_ni) + `ASSUME_FPV(AckErrorsAreOH_M, $onehot0({ack_err_pi, ack_err_ni}), clk_i, !rst_ni) + `ASSUME_FPV(AlertErrorsAreOH_M, $onehot0({alert_err_pi, alert_err_ni}), clk_i, !rst_ni) + + // ping will stay high until ping ok received, then it must be deasserted + // TODO: this excludes the case where no ping ok will be returned due to an error + `ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> !ping_en_i, clk_i, !rst_ni) + `ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=> (ping_en_i && !ping_ok_o) or + (ping_en_i && ping_ok_o ##1 $fell(ping_en_i)), clk_i, !rst_ni || error_present) + + sequence FullHandshake_S; + $rose(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ##1 + $rose(prim_alert_rxtx_fpv.alert_rx_out.ack_p) && + $stable(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ##1 + $fell(prim_alert_rxtx_fpv.alert_tx_out.alert_p) && + $stable(prim_alert_rxtx_fpv.alert_rx_out.ack_p) ##1 + $fell(prim_alert_rxtx_fpv.alert_rx_out.ack_p) && + $stable(prim_alert_rxtx_fpv.alert_tx_out.alert_p) ; + endsequence + + // note: injected errors may lockup the FSMs, and hence the full HS can + // only take place if both FSMs are in a sane state + `ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_fpv.alert_rx_out.ping_p) && + (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle ) && + (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle )|=> FullHandshake_S, + clk_i, !rst_ni || error_present) + `ASSERT(AlertHs_A, alert_i && + (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) && + (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |=> + FullHandshake_S, clk_i, !rst_ni || error_present) + + // transmission of pings + `ASSERT(AlertPing_A, !error_present ##1 $rose(ping_en_i) |-> + ##[1:9] ping_ok_o, clk_i, !rst_ni || error_present) + // transmission of alerts in case of no collision with ping enable + `ASSERT(AlertCheck0_A, !ping_en_i [*3] ##0 $rose(alert_i) && + (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |=> + alert_o, clk_i, !rst_ni || error_present || ping_en_i) + // transmission of alerts in the general case which can include ping collisions + `ASSERT(AlertCheck1_A, alert_i |-> ##[1:9] alert_o, clk_i, !rst_ni || error_present) + + // basic liveness of FSMs in case no errors are present + `ASSERT(FsmLivenessSender_A, + (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q != + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) |-> + strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present) + `ASSERT(FsmLivenessReceiver_A, + (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q != + prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle) |-> + strong(##[1:$] (prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present) + +endmodule : prim_alert_rxtx_assert_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv new file mode 100644 index 0000000000..5f4b3df085 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv @@ -0,0 +1,121 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for alert sender/receiver pair for the asynchronous case. +// Intended to use with a formal tool. + +`include "prim_assert.sv" + +module prim_alert_rxtx_async_assert_fpv ( + input clk_i, + input rst_ni, + // for sigint error and skew injection only + input ping_err_pi, + input ping_err_ni, + input [1:0] ping_skew_i, + input ack_err_pi, + input ack_err_ni, + input [1:0] ack_skew_i, + input alert_err_pi, + input alert_err_ni, + input [1:0] alert_skew_i, + // normal I/Os + input alert_i, + input ping_en_i, + input ping_ok_o, + input integ_fail_o, + input alert_o +); + + logic error_present; + assign error_present = ping_err_pi | ping_err_ni | + ack_err_pi | ack_err_ni | + alert_err_pi | alert_err_ni; + + // used to check that an error has never occured so far + // this is used to check the handshake below. the handshake can lock up + // the protocol FSMs causing the handshake to never complete. + // note that this will block any ping messages and hence it can be + // eventually detected by the alert handler. + logic error_setreg_d, error_setreg_q; + assign error_setreg_d = error_present | error_setreg_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + error_setreg_q <= 1'b0; + end else begin + error_setreg_q <= error_setreg_d; + end + end + + // Note: we can only detect sigint errors where one wire is flipped. + `ASSUME_FPV(PingErrorsAreOH_M, $onehot0({ping_err_pi, ping_err_ni}) ) + `ASSUME_FPV(AckErrorsAreOH_M, $onehot0({ack_err_pi, ack_err_ni}) ) + `ASSUME_FPV(AlertErrorsAreOH_M, $onehot0({alert_err_pi, alert_err_ni})) + + // ping will stay high until ping ok received, then it must be deasserted + // TODO: this excludes the case where no ping ok will be returned due to an error + `ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> !ping_en_i, clk_i, !rst_ni) + `ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=> + (ping_en_i && !ping_ok_o) or + (ping_en_i && ping_ok_o ##1 $fell(ping_en_i)), + clk_i, !rst_ni || error_present) + + // Note: the sequence lengths of the handshake and the following properties needs to + // be parameterized accordingly if different clock ratios are to be used here. + // TODO: tighten bounds if possible + sequence FullHandshake_S; + $rose(prim_alert_rxtx_async_fpv.alert_pd) ##[3:5] + $rose(prim_alert_rxtx_async_fpv.ack_pd) && + $stable(prim_alert_rxtx_async_fpv.alert_pd) ##[3:5] + $fell(prim_alert_rxtx_async_fpv.alert_pd) && + $stable(prim_alert_rxtx_async_fpv.ack_pd) ##[3:5] + $fell(prim_alert_rxtx_async_fpv.ack_pd) && + $stable(prim_alert_rxtx_async_fpv.alert_pd); + endsequence + + // note: injected errors may lockup the FSMs, and hence the full HS can + // only take place if both FSMs are in a sane state + `ASSERT(PingHs_A, ##1 $changed(prim_alert_rxtx_async_fpv.ping_pd) && + (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) && + (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S, + clk_i, !rst_ni || error_setreg_q) + `ASSERT(AlertHs_A, alert_i && + (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) && + (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> ##[0:5] FullHandshake_S, + clk_i, !rst_ni || error_setreg_q) + + // transmission of pings + // this bound is relatively large as in the worst case, we need to resolve + // staggered differential signal patterns on all three differential channels + `ASSERT(AlertPing_A, $rose(ping_en_i) |-> ##[1:23] ping_ok_o, + clk_i, !rst_ni || error_setreg_q) + // transmission of first alert assertion (no ping collision) + `ASSERT(AlertCheck0_A, !ping_en_i [*10] ##1 $rose(alert_i) && + (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |-> + ##[3:5] alert_o, clk_i, !rst_ni || ping_en_i || error_setreg_q) + // eventual transmission of alerts in the general case which can include ping collisions + `ASSERT(AlertCheck1_A, alert_i |-> ##[1:25] alert_o, + clk_i, !rst_ni || error_setreg_q) + + // basic liveness of FSMs in case no errors are present + `ASSERT(FsmLivenessSender_A, !error_present [*2] ##1 !error_present && + (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q != + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle) |-> + strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle)), clk_i, !rst_ni || error_present) + `ASSERT(FsmLivenessReceiver_A, !error_present [*2] ##1 !error_present && + (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q != + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle) |-> + strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.Idle)),clk_i, !rst_ni || error_present) + + // TODO: add FSM liveness of 3x diff decoder instances + +endmodule : prim_alert_rxtx_async_assert_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_ppc_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_ppc_assert_fpv.sv new file mode 100644 index 0000000000..2ae1ae78f0 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_ppc_assert_fpv.sv @@ -0,0 +1,47 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for prim_arbiter_ppc. +// Intended to be used with a formal tool. + +`include "prim_assert.sv" + +module prim_arbiter_ppc_assert_fpv #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32 +) ( + input clk_i, + input rst_ni, + input [N-1:0] req_i, + input [DW-1:0]data_i [N], + input logic[N-1:0] gnt_o, + input logic[$clog2(N)-1:0] idx_o, + input logic valid_o, + input logic[DW-1:0] data_o, + input ready_i +); + + /////////////////////////////// + // Declarations & Parameters // + /////////////////////////////// + + ///////////////// + // Assumptions // + ///////////////// + + // `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni) + + //////////////////////// + // Forward Assertions // + //////////////////////// + + // `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni) + + ///////////////////////// + // Backward Assertions // + ///////////////////////// + + // `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni) + +endmodule : prim_arbiter_ppc_assert_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_tree_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_tree_assert_fpv.sv new file mode 100644 index 0000000000..70210075dd --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_arbiter_tree_assert_fpv.sv @@ -0,0 +1,48 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for prim_arbiter_tree. +// Intended to be used with a formal tool. + +`include "prim_assert.sv" + +module prim_arbiter_tree_assert_fpv #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32, + parameter bit Lock = 1'b1 +) ( + input clk_i, + input rst_ni, + input [N-1:0] req_i, + input [DW-1:0]data_i [N], + input logic[N-1:0] gnt_o, + input logic[$clog2(N)-1:0] idx_o, + input logic valid_o, + input logic[DW-1:0] data_o, + input ready_i +); + + /////////////////////////////// + // Declarations & Parameters // + /////////////////////////////// + + ///////////////// + // Assumptions // + ///////////////// + + // `ASSUME(MyAssumption_M, ..., clk_i, !rst_ni) + + //////////////////////// + // Forward Assertions // + //////////////////////// + + // `ASSERT(MyFwdAssertion_A, ..., clk_i, !rst_ni) + + ///////////////////////// + // Backward Assertions // + ///////////////////////// + + // `ASSERT(MyBkwdAssertion_A, ..., clk_i, !rst_ni) + +endmodule : prim_arbiter_tree_assert_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv new file mode 100644 index 0000000000..4f55520131 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv @@ -0,0 +1,70 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for escalation sender/receiver pair. Intended to use with +// a formal tool. + +`include "prim_assert.sv" + +module prim_esc_rxtx_assert_fpv ( + input clk_i, + input rst_ni, + // for sigint error injection only + input resp_err_pi, + input resp_err_ni, + input esc_err_pi, + input esc_err_ni, + // normal I/Os + input esc_en_i, + input ping_en_i, + input ping_ok_o, + input integ_fail_o, + input esc_en_o +); + + logic error_present; + assign error_present = resp_err_pi | resp_err_ni | + esc_err_pi | esc_err_ni; + + // ping will stay high until ping ok received, then it must be deasserted + // TODO: this escludes the case where no ping ok will be returned due to an error + `ASSUME_FPV(PingDeassert_M, ping_en_i && ping_ok_o |=> ping_en_i, clk_i, !rst_ni) + `ASSUME_FPV(PingEnStaysAsserted0_M, ping_en_i |=> + (ping_en_i && !ping_ok_o) or (ping_en_i && ping_ok_o ##1 $fell(ping_en_i)), + clk_i, !rst_ni || error_present) + + // assume that the ping enable and escalation enable signals will eventually be deasserted (and + // esc will stay low for more than 2 cycles) + `ASSUME_FPV(FiniteEsc_M, esc_en_i |-> strong(##[1:$] !esc_en_i [*2])) + `ASSUME_FPV(FinitePing_M, ping_en_i |-> strong(##[1:$] !ping_en_i)) + + // ping response mus occur within 4 cycles (given that no + // error occured within the previous cycles) + `ASSERT(PingRespCheck_A, !error_present [*4] ##1 $rose(ping_en_i) |-> + ##[0:4] ping_ok_o, clk_i, !rst_ni || error_present) + + // be more specific (i.e. use throughout) + `ASSERT(EscRespCheck_A, ##1 esc_en_i |-> ##[0:1] prim_esc_rxtx_fpv.esc_rx_out.resp_p ##1 + !prim_esc_rxtx_fpv.esc_rx_out.resp_p, clk_i, !rst_ni || error_present) + + // check correct transmission of escalation within 0-1 cycles + `ASSERT(EscCheck_A, ##1 esc_en_i |-> ##[0:1] esc_en_o, clk_i, !rst_ni || error_present) + + // check that a single error on the diffpairs is detected + `ASSERT(SingleSigIntDetected0_A, {esc_err_pi, esc_err_ni} == '0 ##1 + $onehot({resp_err_pi, resp_err_ni}) |-> integ_fail_o) + `ASSERT(SingleSigIntDetected1_A, $onehot({esc_err_pi, esc_err_ni}) ##1 + {resp_err_pi, resp_err_ni} == '0 |-> integ_fail_o) + + // basic liveness of FSMs in case no errors are present + `ASSERT(FsmLivenessSender_A, (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q != + prim_esc_rxtx_fpv.i_prim_esc_sender.Idle) |-> + strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_sender.state_q + == prim_esc_rxtx_fpv.i_prim_esc_sender.Idle)), clk_i, !rst_ni || error_present) + `ASSERT(FsmLivenessReceiver_A, (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q != + prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle) |-> + strong(##[1:$] (prim_esc_rxtx_fpv.i_prim_esc_receiver.state_q + == prim_esc_rxtx_fpv.i_prim_esc_receiver.Idle)), clk_i, !rst_ni || error_present) + +endmodule : prim_esc_rxtx_assert_fpv diff --git a/vendor/lowrisc_ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv b/vendor/lowrisc_ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv new file mode 100644 index 0000000000..9090e4c046 --- /dev/null +++ b/vendor/lowrisc_ip/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv @@ -0,0 +1,213 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Assertions for prim_fifo_sync. +// Intended to be used with a formal tool. + +`include "prim_assert.sv" + +module prim_fifo_sync_assert_fpv #( + // can be desabled for deeper FIFOs + parameter bit EnableDataCheck = 1'b1, + parameter int unsigned Width = 16, + parameter bit Pass = 1'b1, + parameter int unsigned Depth = 4, + localparam int unsigned DepthWNorm = $clog2(Depth+1), + localparam int unsigned DepthW = (DepthWNorm == 0) ? 1 : DepthWNorm +) ( + input clk_i, + input rst_ni, + input clr_i, + input wvalid, + input wready, + input [Width-1:0] wdata, + input rvalid, + input rready, + input [Width-1:0] rdata, + input [DepthW-1:0] depth +); + + ///////////////// + // Assumptions // + ///////////////// + + // no need to consider all possible input words + // 2-3 different values suffice + `ASSUME(WdataValues_M, wdata inside {Width'(1'b0), Width'(1'b1), {Width{1'b1}}}, clk_i, !rst_ni) + + //////////////////////////////// + // Data and Depth Value Check // + //////////////////////////////// + + if (EnableDataCheck && Depth > 0) begin : gen_data_check + + logic [DepthW+2:0] ref_depth; + logic [Width-1:0] ref_rdata; + + // no pointers needed in this case + if (Depth == 1) begin : gen_no_ptrs + + logic [Width-1:0] fifo; + logic [DepthW+2:0] wptr, rptr; + + // this only models the data flow, since the control logic is tested below + always_ff @(posedge clk_i or negedge rst_ni) begin : p_fifo_model + if (!rst_ni) begin + ref_depth <= 0; + end else begin + if (clr_i) begin + ref_depth <= 0; + end else begin + if (wvalid && wready && rvalid && rready) begin + fifo <= wdata; + end else if (wvalid && wready) begin + fifo <= wdata; + ref_depth <= ref_depth + 1; + end else if (rvalid && rready) begin + ref_depth <= ref_depth - 1; + end + end + end + end + + if (Pass) begin : gen_pass + assign ref_rdata = (ref_depth == 0) ? wdata : fifo; + end else begin : no_pass + assign ref_rdata = fifo; + end + + // general case + end else begin : gen_ptrs + + logic [Width-1:0] fifo [Depth]; + logic [DepthW+2:0] wptr, rptr; + + // implements (++val) mod Depth + function automatic logic [DepthW+2:0] modinc(logic [DepthW+2:0] val, int modval); + if (val == Depth-1) return 0; + else return val + 1; + endfunction + + // this only models the data flow, since the control logic is tested below + always_ff @(posedge clk_i or negedge rst_ni) begin : p_fifo_model + if (!rst_ni) begin + wptr <= 0; + rptr <= 0; + ref_depth <= 0; + end else begin + if (clr_i) begin + wptr <= 0; + rptr <= 0; + ref_depth <= 0; + end else begin + if (wvalid && wready && rvalid && rready) begin + fifo[wptr] <= wdata; + wptr <= modinc(wptr, Depth); + rptr <= modinc(rptr, Depth); + end else if (wvalid && wready) begin + fifo[wptr] <= wdata; + wptr <= modinc(wptr, Depth); + ref_depth <= ref_depth + 1; + end else if (rvalid && rready) begin + rptr <= modinc(rptr, Depth); + ref_depth <= ref_depth - 1; + end + end + end + end + + if (Pass) begin : gen_pass + assign ref_rdata = (ref_depth == 0) ? wdata : fifo[rptr]; + end else begin : no_pass + assign ref_rdata = fifo[rptr]; + end + + end + + // check the data + `ASSERT(DataCheck_A, rvalid |-> rdata == ref_rdata) + // check the depth + `ASSERT(DepthCheck_A, ref_depth == depth) + + end + + //////////////////////// + // Forward Assertions // + //////////////////////// + + // assert depth of FIFO + `ASSERT(Depth_A, depth <= Depth) + // if we clear the FIFO, it must be empty in the next cycle + `ASSERT(CheckClrDepth_A, clr_i |=> depth == 0) + // check write on full + `ASSERT(WriteFull_A, depth == Depth && wvalid && !rready |=> depth == $past(depth), + clk_i, !rst_ni || clr_i) + // read empty + `ASSERT(ReadEmpty_A, depth == 0 && rready && !wvalid |=> depth == 0, + clk_i, !rst_ni || clr_i) + + // this is unreachable in depth 1 no-pass through mode + if (Depth == 1 && Pass) begin : gen_d1_passthru + // check simultaneous write and read + `ASSERT(WriteAndRead_A, wready && wvalid && rvalid && rready |=> depth == $past(depth), + clk_i, !rst_ni || clr_i) + end + + if (Depth == 0) begin : gen_depth0 + // if there is no register, the FIFO is per definition pass-through + `ASSERT_INIT(ZeroDepthNeedsPass_A, Pass == 1) + // depth must remain zero + `ASSERT(DepthAlwaysZero_A, depth == 0) + // data is just passed through + `ASSERT(DataPassThru_A, wdata == rdata) + // FIFO is ready if downstream logic is ready + `ASSERT(Wready_A, rready == wready) + // valid input is valid output + `ASSERT(Rvalid_A, rvalid == wvalid) + // ensure full coverage + `ASSERT(UnusedClr_A, prim_fifo_sync.gen_passthru_fifo.unused_clr == clr_i) + end else begin : gen_depth_gt0 + // check wready + `ASSERT(Wready_A, depth < Depth |-> wready) + // check rvalid + `ASSERT(Rvalid_A, depth > 0 |-> rvalid) + // check write only + `ASSERT(WriteOnly_A, wvalid && wready && !rready && depth < Depth |=> + depth == $past(depth) + 1, clk_i, !rst_ni || clr_i) + // check read only + `ASSERT(ReadOnly_A, !wvalid && rready && rvalid && depth > 0 |=> + depth == $past(depth) - 1, clk_i, !rst_ni || clr_i) + end + + if (Pass) begin : gen_pass_fwd + // if we clear the FIFO, it must be empty in the next cycle + // but we may also get a pass through + `ASSERT(CheckClrValid_A, clr_i |=> wvalid == rvalid) + end else begin : gen_nopass_fwd + // if we clear the FIFO, it must be empty in the next cycle + `ASSERT(CheckClrValid_A, clr_i |=> !rvalid) + end + + ///////////////////////// + // Backward Assertions // + ///////////////////////// + + if (Pass) begin : gen_pass_bkwd + // there is still space in the FIFO or downstream logic is ready + `ASSERT(WreadySpacekBkwd_A, wready |-> depth < Depth || rready) + // elements ready to be read or upstream data is valid + `ASSERT(RvalidElemskBkwd_A, rvalid |-> depth > 0 || wvalid) + end else begin : gen_nopass_bkwd + // there is still space in the FIFO + `ASSERT(WreadySpacekBkwd_A, wready |-> depth < Depth) + // elements ready to be read + `ASSERT(RvalidElemskBkwd_A, rvalid |-> depth > 0) + end + + // no more space in the FIFO + `ASSERT(WreadyNoSpaceBkwd_A, !wready |-> depth == Depth) + // elements ready to be read + `ASSERT(RvalidNoElemskBkwd_A, !rvalid |-> depth == 0) + +endmodule : prim_fifo_sync_assert_fpv diff --git a/vendor/lowrisc_ip/prim/lint/prim.vlt b/vendor/lowrisc_ip/prim/lint/prim.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim/lint/prim.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim/lint/prim.waiver b/vendor/lowrisc_ip/prim/lint/prim.waiver new file mode 100644 index 0000000000..54fee4e30e --- /dev/null +++ b/vendor/lowrisc_ip/prim/lint/prim.waiver @@ -0,0 +1,63 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim + +# prim_fifo_sync +waive -rules {ONE_BIT_MEM_WIDTH} -location {prim_fifo_sync.sv} -msg {Memory 'gen_normal_fifo.storage' has word width which is single bit wide} \ + -comment "It is permissible that a FIFO has a wordwidth of 1bit" + +# prim_fifo_async +waive -rules {ASSIGN_SIGN} -location {prim_fifo_async.sv} -msg {Signed target 'i' assigned unsigned value 'PTR_WIDTH - 3'} \ + -comment "Parameter PTR_WIDTH is unsigned, but integer i is signed. This is fine. Changing the integer to unsigned might \ + cause issues with the for loop never exiting, because an unsigned integer can never become < 0." + +# prim_assert +waive -rules {UNDEF_MACRO_REF} -location {prim_assert.sv} -regexp {Macro definition for 'ASSERT_RPT' includes expansion of undefined macro '__(FILE|LINE)__'} \ + -comment "This is an UVM specific macro inside our assertion shortcuts" +# unfortunately most tools do not support line wrapping within the declaration of macro functions, hence we have to waive +# line length violations. +waive -rules {LINE_LENGTH} -location {prim_assert.sv} -msg {Line length of} \ + -comment "Some macros cannot be line-wrapped, as some tools do not support that." + +# prim_packer +waive -rules INTEGER -location {prim_packer.sv} -msg {'i' of type int used as a non-constant value} \ + -comment "This assigns int i (signed) to a multibit logic variable (unsigned), which is fine" + +# primitives: prim_subreg +waive -rules INPUT_NOT_READ -location {prim_subreg.sv} -regexp {Input port 'wd' is not read from} \ + -comment "for RO wd is not used" + +# primitives: prim_arbiter_* +waive -rules PARTIAL_CONST_ASSIGN -location {prim_arbiter_*.sv} -regexp {'mask.0.' is conditionally assigned a constant} \ + -comment "makes the code more readable" +waive -rules CONST_FF -location {prim_arbiter_*.sv} -regexp {Flip-flop 'mask.0.' is driven by constant} \ + -comment "makes the code more readable" + +# primitives: prim_sram_arbiter +waive -rules CONST_OUTPUT -location {prim_sram_arbiter.sv} -regexp {rsp_error.* is driven by constant} \ + -comment "SRAM protection is not yet implemented" + +# primitives: prim_fifos + +waive -rules VAR_INDEX_RANGE -location {prim_fifo_*sync.sv} -regexp {maximum value .* may be too large for 'storage'} \ + -comment "index is protected by control logic" +waive -rules EXPLICIT_BITLEN -location {prim_fifo_*sync.sv} -regexp {Bit length not specified for constant '1'} \ + -comment "index is protected by control logic" +waive -rules NOT_READ -location {prim_fifo_async.sv} -regexp {Signal 'nc_decval_msb' is not read} \ + -comment "Store temporary values. Not used intentionally" + +waive -rules {INPUT_NOT_READ} -location {prim_fifo_sync.sv} -regexp {Input port '(clk_i|rst_ni)' is not read from, instance.*Depth=0\)} \ + -comment "In passthrough mode, clk and reset are not read form within this module" + +# TL-UL fifo +waive -rules {HIER_BRANCH_NOT_READ} -location {tlul_fifo_sync.sv} -regexp {Connected net '(clk_i|rst_ni)' at prim_fifo_sync.sv:.* is not read from in module 'prim_fifo_sync'} \ + -comment "In passthrough mode, clk and reset are not read form within this module" + +# primitivies: prim_ram_2p_wrapper +# +#waive -rules INPUT_NOT_READ -location {prim_ram_*_wrapper*} -regexp {cfg_i} \ +# -comment "Register model doesn't need config port" +#waive -rules NOT_READ -location {prim_ram_*_wrapper*} -regexp {(a|b)_rdata_(q|d)\[38} \ +# -comment "Syndrome is not going out to the interface" diff --git a/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/README.md b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/README.md new file mode 100644 index 0000000000..77447b4cbd --- /dev/null +++ b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/README.md @@ -0,0 +1,34 @@ +REQ/ACK Syncronizer Verilator Testbench +======================================= + +This directory contains a basic, scratch Verilator testbench targeting +functional verification of the REQ/ACK synchronizer primitive during +development. + +How to build and run the testbench +---------------------------------- + +From the OpenTitan top level execute + + ```sh + fusesoc --cores-root=. run --setup --build \ + lowrisc:dv_verilator:prim_sync_reqack_tb + ``` +to build the testbench and afterwards + + ```sh + ./build/lowrisc_dv_verilator_prim_sync_reqack_tb_0/default-verilator/Vprim_sync_reqack_tb \ + --trace + ``` +to run it. + +Details of the testbench +------------------------ + +- `rtl/prim_sync_reqack_tb.sv`: SystemVerilog testbench, instantiates and + drives the DUT, counts handshakes in both domains, signals test end and + result (pass/fail) to C++ via output ports. Change this file to e.g. + for a different clock ratio or more transactions. +- `cpp/prim_sync_reqack_tb.cc`: Contains main function and instantiation of + SimCtrl, reads output ports of DUT and signals simulation termination to + Verilator. diff --git a/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc new file mode 100644 index 0000000000..4b93a4cc1b --- /dev/null +++ b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "Vprim_sync_reqack_tb.h" +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.h" + +#include +#include +#include + +#include "sim_ctrl_extension.h" + +class PrimSyncReqAckTB : public SimCtrlExtension { + using SimCtrlExtension::SimCtrlExtension; + + public: + PrimSyncReqAckTB(prim_sync_reqack_tb *top); + + void OnClock(unsigned long sim_time); + + private: + prim_sync_reqack_tb *top_; +}; + +// Constructor: +// - Set up top_ ptr +PrimSyncReqAckTB::PrimSyncReqAckTB(prim_sync_reqack_tb *top) + : SimCtrlExtension{}, top_(top) {} + +// Function called once every clock cycle from SimCtrl +void PrimSyncReqAckTB::OnClock(unsigned long sim_time) { + if (top_->test_done_o) { + VerilatorSimCtrl::GetInstance().RequestStop(top_->test_passed_o); + } +} + +int main(int argc, char **argv) { + int ret_code; + + // Init verilog instance + prim_sync_reqack_tb top; + + // Init sim + VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance(); + simctrl.SetTop(&top, &top.clk_i, &top.rst_ni, + VerilatorSimCtrlFlags::ResetPolarityNegative); + + // Create and register VerilatorSimCtrl extension + PrimSyncReqAckTB primsyncreqacktb(&top); + simctrl.RegisterExtension(&primsyncreqacktb); + + std::cout << "Simulation of REQ/ACK Synchronizer primitive" << std::endl + << "============================================" << std::endl + << std::endl; + + // Get pass / fail from Verilator + ret_code = simctrl.Exec(argc, argv); + + return ret_code; +} diff --git a/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core new file mode 100644 index 0000000000..bce88fb5f3 --- /dev/null +++ b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core @@ -0,0 +1,52 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:dv_verilator:prim_sync_reqack_tb" +description: "REQ/ACK Synchronizer Verilator TB" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + files: + - rtl/prim_sync_reqack_tb.sv + file_type: systemVerilogSource + + files_dv_verilator: + depend: + - lowrisc:dv_verilator:simutil_verilator + + files: + - cpp/prim_sync_reqack_tb.cc + file_type: cppSource + +targets: + default: + default_tool: verilator + filesets: + - files_rtl + - files_dv_verilator + toplevel: prim_sync_reqack_tb + tools: + verilator: + mode: cc + verilator_options: +# Disabling tracing reduces compile times by multiple times, but doesn't have a +# huge influence on runtime performance. (Based on early observations.) + - '--trace' + - '--trace-fst' # this requires -DVM_TRACE_FMT_FST in CFLAGS below! + - '--trace-structs' + - '--trace-params' + - '--trace-max-array 1024' +# compiler flags +# +# -O +# Optimization levels have a large impact on the runtime performance of the +# simulation model. -O2 and -O3 are pretty similar, -Os is slower than -O2/-O3 + - '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=prim_sync_reqack_tb -g -O0"' + - '-LDFLAGS "-pthread -lutil -lelf"' + - "-Wall" + - "-Wno-PINCONNECTEMPTY" + # XXX: Cleanup all warnings and remove this option + # (or make it more fine-grained at least) + - "-Wno-fatal" diff --git a/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv new file mode 100644 index 0000000000..760c62152d --- /dev/null +++ b/vendor/lowrisc_ip/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv @@ -0,0 +1,173 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Scratch verification testbench for REQ/ACK synchronizer primitive + +module prim_sync_reqack_tb #( +) ( + input logic clk_i, + input logic rst_ni, + + output logic test_done_o, + output logic test_passed_o +); + + // TB configuration + localparam int unsigned NumTransactions = 8; + localparam logic FastToSlow = 1'b1; // Select 1'b0 for SlowToFast + localparam int unsigned Ratio = 4; // must be even and greater equal 2 + + // Derivation of parameters + localparam int unsigned Ticks = Ratio/2; + localparam int unsigned WidthTicks = $clog2(Ticks)+1; + localparam int unsigned WidthTrans = $clog2(NumTransactions)+1; + + // Derive slow clock (using a counter) + logic [WidthTicks-1:0] count_clk_d, count_clk_q; + assign count_clk_d = count_clk_q == (Ticks[WidthTicks-1:0]-1) ? '0 : count_clk_q + {{WidthTicks-1{1'b0}},{1'b1}}; + always_ff @(posedge clk_i) begin : reg_count_clk + count_clk_q <= count_clk_d; + end + + logic clk_slow_d, clk_slow_q, clk_slow; + assign clk_slow_d = count_clk_q == (Ticks[WidthTicks-1:0]-1) ? !clk_slow_q : clk_slow_q; + always_ff @(posedge clk_i) begin : reg_clk_slow + clk_slow_q <= clk_slow_d; + end + assign clk_slow = clk_slow_q; + + // Sync reset to slow clock + logic [1:0] rst_slow_nq; + logic rst_slow_n; + always_ff @(posedge clk_slow) begin + rst_slow_nq <= {rst_slow_nq[0], rst_ni}; + end + assign rst_slow_n = rst_ni & rst_slow_nq[1]; + + // Connect clocks + logic clk_src, clk_dst; + assign clk_src = FastToSlow ? clk_i : clk_slow; + assign clk_dst = FastToSlow ? clk_slow : clk_i; + + logic src_req, dst_req; + logic src_ack, dst_ack; + logic rst_done; + + // Instantiate DUT + prim_sync_reqack prim_sync_reqack ( + .clk_src_i (clk_src), + .rst_src_ni (rst_slow_n), + .clk_dst_i (clk_dst), + .rst_dst_ni (rst_slow_n), + + .src_req_i (src_req), + .src_ack_o (src_ack), + .dst_req_o (dst_req), + .dst_ack_i (dst_ack) + ); + + // Make sure we do not apply stimuli before the reset. + always_ff @(posedge clk_slow or negedge rst_slow_n) begin + if (!rst_slow_n) begin + rst_done <= '1; + end else begin + rst_done <= rst_done; + end + end + + // Create randomized ACK delay + localparam int WIDTH_COUNT = 3; + logic [31:0] tmp; + logic [31-WIDTH_COUNT:0] unused_tmp; + assign unused_tmp = tmp[31:WIDTH_COUNT]; + logic [WIDTH_COUNT-1:0] dst_count_clk_d, dst_count_clk_q; + logic [WIDTH_COUNT-1:0] dst_count_clk_max_d, dst_count_clk_max_q; + logic count_exp; + assign count_exp = dst_count_clk_q == dst_count_clk_max_q; + always_comb begin + dst_count_clk_d = dst_count_clk_q; + dst_count_clk_max_d = dst_count_clk_max_q; + tmp = '0; + if (dst_req && count_exp) begin + // Clear counter + dst_count_clk_d = '0; + // Get new max + tmp = $random; + dst_count_clk_max_d = tmp[2:0]; + end else if (dst_req) begin + // Increment + dst_count_clk_d = dst_count_clk_q + {{WIDTH_COUNT-1{1'b0}},{1'b1}}; + end + end + always_ff @(posedge clk_dst or negedge rst_slow_n) begin : reg_dst_count_clk + if (!rst_slow_n) begin + dst_count_clk_q <= '0; + dst_count_clk_max_q <= '0; + end else begin + dst_count_clk_q <= dst_count_clk_d; + dst_count_clk_max_q <= dst_count_clk_max_d; + end + end + + // Apply stimuli + always_comb begin + + src_req = 1'b0; + dst_ack = 1'b0; + + if (rst_done && rst_slow_n) begin + // The source wants to perform handshakes at maximum rate. + src_req = 1'b1; + end + + if (dst_req && count_exp) begin + // The destination sends the ACK after a random delay. + dst_ack = 1'b1; + end + end + + // Count handshakes on both sides + logic [WidthTrans-1:0] src_count_d, src_count_q; + logic [WidthTrans-1:0] dst_count_d, dst_count_q; + assign src_count_d = (src_req && src_ack) ? src_count_q + 1'b1 : src_count_q; + always_ff @(posedge clk_src or negedge rst_slow_n) begin : reg_src_count + if (!rst_slow_n) begin + src_count_q <= '0; + end else begin + src_count_q <= src_count_d; + end + end + assign dst_count_d = (dst_req && dst_ack) ? dst_count_q + 1'b1 : dst_count_q; + always_ff @(posedge clk_dst or negedge rst_slow_n) begin : reg_dst_count + if (!rst_slow_n) begin + dst_count_q <= '0; + end else begin + dst_count_q <= dst_count_d; + end + end + + // Check responses, signal end of simulation + always_ff @(posedge clk_i) begin : tb_ctrl + test_done_o <= 1'b0; + test_passed_o <= 1'b1; + + if ((src_count_q == NumTransactions[WidthTrans-1:0]) && + (dst_count_q == NumTransactions[WidthTrans-1:0])) begin // Success + + $display("\nSUCCESS: Performed %0d handshakes in both source and destination domain.", + NumTransactions); + $display("Finishing simulation now.\n"); + test_passed_o <= 1'b1; + test_done_o <= 1'b1; + end else if (((src_count_q > dst_count_q) && ((src_count_q - dst_count_q) > 1)) || + ((dst_count_q > src_count_q) && ((dst_count_q - src_count_q) > 1))) begin // Failed + $display("\nERROR: Performed %0d handshakes in source domain, and %0d in destination domain.", + src_count_q, dst_count_q); + $display("Finishing simulation now.\n"); + test_passed_o <= 1'b0; + test_done_o <= 1'b1; + end + end + +endmodule diff --git a/vendor/lowrisc_ip/prim/prim.core b/vendor/lowrisc_ip/prim/prim.core new file mode 100644 index 0000000000..166b4cd540 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim.core @@ -0,0 +1,80 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# XXX: Split this core into multiple smaller ones. +name: "lowrisc:prim:all:0.1" +description: "Primitives" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_2p # for prim_ram_2p_adv + - lowrisc:prim:secded # for prim_ram_2p_adv + - lowrisc:prim:assert + - lowrisc:prim:diff_decode # for prim_alert_sender/receiver + - lowrisc:prim:pad_wrapper + - lowrisc:prim:prim_pkg + - lowrisc:prim:clock_mux2 + files: + - rtl/prim_clock_inverter.sv + - rtl/prim_clock_gating_sync.sv + - rtl/prim_alert_pkg.sv + - rtl/prim_alert_receiver.sv + - rtl/prim_alert_sender.sv + - rtl/prim_arbiter_ppc.sv + - rtl/prim_arbiter_tree.sv + - rtl/prim_esc_pkg.sv + - rtl/prim_esc_receiver.sv + - rtl/prim_esc_sender.sv + - rtl/prim_sram_arbiter.sv + - rtl/prim_fifo_async.sv + - rtl/prim_fifo_sync.sv + - rtl/prim_flop_2sync.sv + - rtl/prim_sync_reqack.sv + - rtl/prim_keccak.sv + - rtl/prim_lfsr.sv + - rtl/prim_packer.sv + - rtl/prim_cipher_pkg.sv + - rtl/prim_present.sv + - rtl/prim_prince.sv + - rtl/prim_gate_gen.sv + - rtl/prim_pulse_sync.sv + - rtl/prim_filter.sv + - rtl/prim_filter_ctr.sv + - rtl/prim_subreg.sv + - rtl/prim_subreg_ext.sv + - rtl/prim_intr_hw.sv + - rtl/prim_ram_2p_adv.sv + - rtl/prim_ram_2p_async_adv.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/shared/prim_assert.core b/vendor/lowrisc_ip/prim/prim_assert.core similarity index 100% rename from shared/prim_assert.core rename to vendor/lowrisc_ip/prim/prim_assert.core diff --git a/vendor/lowrisc_ip/prim/prim_clock_gating.core b/vendor/lowrisc_ip/prim/prim_clock_gating.core new file mode 100644 index 0000000000..1ed587c994 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_clock_gating.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:clock_gating" +description: "Clock gating primitives" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_gating + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_clock_mux2.core b/vendor/lowrisc_ip/prim/prim_clock_mux2.core new file mode 100644 index 0000000000..c925624593 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_clock_mux2.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:clock_mux2" +description: "2-input clock multiplexer" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_mux2 + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/shared/prim_lfsr.core b/vendor/lowrisc_ip/prim/prim_diff_decode.core similarity index 68% rename from shared/prim_lfsr.core rename to vendor/lowrisc_ip/prim/prim_diff_decode.core index c552f4aef3..eda0fe7c94 100644 --- a/shared/prim_lfsr.core +++ b/vendor/lowrisc_ip/prim/prim_diff_decode.core @@ -3,12 +3,14 @@ CAPI=2: # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -name: "lowrisc:prim:lfsr:0.1" -description: "LFSR primitive" +name: "lowrisc:prim:diff_decode" +description: "prim" filesets: files_rtl: + depend: + - lowrisc:prim:assert files: - - rtl/prim_lfsr.sv + - rtl/prim_diff_decode.sv file_type: systemVerilogSource targets: diff --git a/vendor/lowrisc_ip/prim/prim_flash.core b/vendor/lowrisc_ip/prim/prim_flash.core new file mode 100644 index 0000000000..9d7b41eab2 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_flash.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:flash" +description: "Flash memory" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: flash + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/shared/prim_ram_1p.core b/vendor/lowrisc_ip/prim/prim_gf_mult.core similarity index 59% rename from shared/prim_ram_1p.core rename to vendor/lowrisc_ip/prim/prim_gf_mult.core index 384c427878..361fb27e92 100644 --- a/shared/prim_ram_1p.core +++ b/vendor/lowrisc_ip/prim/prim_gf_mult.core @@ -3,12 +3,15 @@ CAPI=2: # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 -name: "lowrisc:prim:ram_1p:0.1" -description: "Single port RAM (technology independent)" +name: "lowrisc:prim:gf_mult" +description: "Galois multiplier" filesets: files_rtl: depend: - - lowrisc:prim_generic:ram_1p + - lowrisc:prim:assert + files: + - rtl/prim_gf_mult.sv + file_type: systemVerilogSource targets: default: diff --git a/vendor/lowrisc_ip/prim/prim_otp.core b/vendor/lowrisc_ip/prim/prim_otp.core new file mode 100644 index 0000000000..e1ff6f000e --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_otp.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:otp" +description: "One-Time Programmable (OTP) memory" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: otp + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_pad_wrapper.core b/vendor/lowrisc_ip/prim/prim_pad_wrapper.core new file mode 100644 index 0000000000..1454e74a1d --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_pad_wrapper.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:pad_wrapper" +description: "PAD wrapper" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: pad_wrapper + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_pkg.core b/vendor/lowrisc_ip/prim/prim_pkg.core new file mode 100644 index 0000000000..f054f66604 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_pkg.core @@ -0,0 +1,23 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:prim:prim_pkg:0.1" +description: "Constants used by the primitives" +filesets: + primgen_dep: + depend: + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + action: generate_prim_pkg + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_ram_1p.core b/vendor/lowrisc_ip/prim/prim_ram_1p.core new file mode 100644 index 0000000000..68f8452e5f --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_ram_1p.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:ram_1p" +description: "1 port random-access memory" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: ram_1p + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core b/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core new file mode 100644 index 0000000000..fe655a878c --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_ram_1p_adv.core @@ -0,0 +1,19 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:ram_1p_adv:0.1" +description: "Single-port RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_1p + files: + - rtl/prim_ram_1p_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/prim_ram_2p.core b/vendor/lowrisc_ip/prim/prim_ram_2p.core new file mode 100644 index 0000000000..af887f0744 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_ram_2p.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:ram_2p" +description: "2 port random-access memory" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: ram_2p + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/vendor/lowrisc_ip/prim/prim_rom.core b/vendor/lowrisc_ip/prim/prim_rom.core new file mode 100644 index 0000000000..a5a489e7ea --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_rom.core @@ -0,0 +1,25 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:rom" +description: "Read-only memory (ROM)" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + +generate: + impl: + generator: primgen + parameters: + prim_name: rom + +targets: + default: + filesets: + - primgen_dep + generate: + - impl diff --git a/shared/prim_secded.core b/vendor/lowrisc_ip/prim/prim_secded.core similarity index 86% rename from shared/prim_secded.core rename to vendor/lowrisc_ip/prim/prim_secded.core index fe075dfd04..8c22ecddd9 100644 --- a/shared/prim_secded.core +++ b/vendor/lowrisc_ip/prim/prim_secded.core @@ -10,6 +10,8 @@ filesets: files: - rtl/prim_secded_28_22_dec.sv - rtl/prim_secded_28_22_enc.sv + - rtl/prim_secded_39_32_dec.sv + - rtl/prim_secded_39_32_enc.sv - rtl/prim_secded_72_64_dec.sv - rtl/prim_secded_72_64_enc.sv file_type: systemVerilogSource diff --git a/vendor/lowrisc_ip/prim/prim_util_memload.core b/vendor/lowrisc_ip/prim/prim_util_memload.core new file mode 100644 index 0000000000..3b9ad55686 --- /dev/null +++ b/vendor/lowrisc_ip/prim/prim_util_memload.core @@ -0,0 +1,17 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim:util_memload" +description: "Memory loaders for simulation. To be included in a memory primitive." +filesets: + files_rtl: + files: + - rtl/prim_util_memload.sv: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/vendor/lowrisc_ip/prim/primgen.core b/vendor/lowrisc_ip/prim/primgen.core new file mode 100644 index 0000000000..3a143ee636 --- /dev/null +++ b/vendor/lowrisc_ip/prim/primgen.core @@ -0,0 +1,10 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:prim:primgen:0.1" + +generators: + primgen: + interpreter: python3 + command: util/primgen.py diff --git a/vendor/lowrisc_ip/prim/rtl/prim_alert_pkg.sv b/vendor/lowrisc_ip/prim/rtl/prim_alert_pkg.sv new file mode 100644 index 0000000000..a7c6be8f19 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_alert_pkg.sv @@ -0,0 +1,17 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_alert_pkg; + typedef struct packed { + logic alert_p; + logic alert_n; + } alert_tx_t; + + typedef struct packed { + logic ping_p; + logic ping_n; + logic ack_p; + logic ack_n; + } alert_rx_t; +endpackage diff --git a/vendor/lowrisc_ip/prim/rtl/prim_alert_receiver.sv b/vendor/lowrisc_ip/prim/rtl/prim_alert_receiver.sv new file mode 100644 index 0000000000..f8954b839e --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_alert_receiver.sv @@ -0,0 +1,214 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// The alert receiver primitive decodes alerts that have been differentially +// encoded and transmitted via a handshake protocol on alert_p/n and +// ack_p/n. In case an alert handshake is initiated, the output alert_o will +// immediately be asserted (even before completion of the handshake). +// +// In case the differential input is not correctly encoded, this module will +// raise an error by asserting integ_fail_o. +// +// Further, the module supports ping testing of the alert diff pair. In order to +// initiate a ping test, ping_en_i shall be set to 1'b1 until ping_ok_o is +// asserted for one cycle. The signal may be de-asserted (e.g. after a long) +// timeout period. However note that all ping responses that come in after +// deasserting ping_en_i will be treated as native alerts. +// +// The protocol works in both asynchronous and synchronous cases. In the +// asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to +// instantiate additional synchronization logic. Further, it must be ensured +// that the timing skew between all diff pairs is smaller than the shortest +// clock period of the involved clocks. +// +// Note that in case of synchronous operation, alerts on the diffpair are +// decoded combinationally and forwarded on alert_o within the same cycle. +// +// See also: prim_alert_sender, prim_diff_decode, alert_handler + +`include "prim_assert.sv" + +module prim_alert_receiver + import prim_alert_pkg::*; +#( + // enables additional synchronization logic + parameter bit AsyncOn = 1'b0 +) ( + input clk_i, + input rst_ni, + // this triggers a ping test. keep asserted + // until ping_ok_o is asserted. + input ping_en_i, + output logic ping_ok_o, + // asserted if signal integrity issue detected + output logic integ_fail_o, + // alert output (pulsed high) if a handshake is initiated + // on alert_p/n and no ping request is outstanding + output logic alert_o, + // ping input diff pair and ack diff pair + output alert_rx_t alert_rx_o, + // alert output diff pair + input alert_tx_t alert_tx_i +); + + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + logic alert_level, alert_sigint; + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) i_decode_alert ( + .clk_i, + .rst_ni, + .diff_pi ( alert_tx_i.alert_p ), + .diff_ni ( alert_tx_i.alert_n ), + .level_o ( alert_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( alert_sigint ) + ); + + ///////////////////////////////////////////////////// + // main protocol FSM that drives the diff outputs // + ///////////////////////////////////////////////////// + typedef enum logic [1:0] {Idle, HsAckWait, Pause0, Pause1} state_e; + state_e state_d, state_q; + logic ping_rise; + logic ping_tog_d, ping_tog_q, ack_d, ack_q; + logic ping_en_d, ping_en_q; + logic ping_pending_d, ping_pending_q; + + // signal ping request upon positive transition on ping_en_i + // signalling is performed by a level change event on the diff output + assign ping_en_d = ping_en_i; + assign ping_rise = ping_en_i && !ping_en_q; + assign ping_tog_d = (ping_rise) ? ~ping_tog_q : ping_tog_q; + + // the ping pending signal is used to in the FSM to distinguish whether the + // incoming handshake shall be treated as an alert or a ping response. + // it is important that this is only set on a rising ping_en level change, since + // otherwise the ping enable signal could be abused to "mask" all native alerts + // as ping responses by constantly tying it to 1. + assign ping_pending_d = ping_rise | ((~ping_ok_o) & ping_en_i & ping_pending_q); + + // diff pair outputs + assign alert_rx_o.ack_p = ack_q; + assign alert_rx_o.ack_n = ~ack_q; + assign alert_rx_o.ping_p = ping_tog_q; + assign alert_rx_o.ping_n = ~ping_tog_q; + + // this FSM receives the four phase handshakes from the alert receiver + // note that the latency of the alert_p/n input diff pair is at least one + // cycle until it enters the receiver FSM. the same holds for the ack_* diff + // pair outputs. + always_comb begin : p_fsm + // default + state_d = state_q; + ack_d = 1'b0; + ping_ok_o = 1'b0; + integ_fail_o = 1'b0; + alert_o = 1'b0; + + unique case (state_q) + Idle: begin + // wait for handshake to be initiated + if (alert_level) begin + state_d = HsAckWait; + ack_d = 1'b1; + // signal either an alert or ping received on the output + if (ping_pending_q) begin + ping_ok_o = 1'b1; + end else begin + alert_o = 1'b1; + end + end + end + // waiting for deassertion of alert to complete HS + HsAckWait: begin + if (!alert_level) begin + state_d = Pause0; + end else begin + ack_d = 1'b1; + end + end + // pause cycles between back-to-back handshakes + Pause0: state_d = Pause1; + Pause1: state_d = Idle; + default : ; // full case + endcase + + // override in case of sigint + if (alert_sigint) begin + state_d = Idle; + ack_d = 1'b0; + ping_ok_o = 1'b0; + integ_fail_o = 1'b1; + alert_o = 1'b0; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + state_q <= Idle; + ack_q <= 1'b0; + ping_tog_q <= 1'b0; + ping_en_q <= 1'b0; + ping_pending_q <= 1'b0; + end else begin + state_q <= state_d; + ack_q <= ack_d; + ping_tog_q <= ping_tog_d; + ping_en_q <= ping_en_d; + ping_pending_q <= ping_pending_d; + end + end + + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(PingOkKnownO_A, ping_ok_o) + `ASSERT_KNOWN(IntegFailKnownO_A, integ_fail_o) + `ASSERT_KNOWN(AlertKnownO_A, alert_o) + `ASSERT_KNOWN(PingPKnownO_A, alert_rx_o) + + // check encoding of outgoing diffpairs + `ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n) + `ASSERT(AckDiffOk_A, alert_rx_o.ack_p ^ alert_rx_o.ack_n) + // ping request at input -> need to see encoded ping request + `ASSERT(PingRequest0_A, ##1 $rose(ping_en_i) |=> $changed(alert_rx_o.ping_p)) + // ping response implies it has been requested + `ASSERT(PingResponse0_A, ping_ok_o |-> ping_pending_q) + // correctly latch ping request + `ASSERT(PingPending_A, ##1 $rose(ping_en_i) |=> ping_pending_q) + + if (AsyncOn) begin : gen_async_assert + // signal integrity check propagation + `ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n [*2] |-> + ##2 integ_fail_o) + // TODO: need to add skewed cases as well, the assertions below assume no skew at the moment + // ping response + `ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) && + (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2 state_q == Idle && ping_pending_q |-> + ping_ok_o, clk_i, !rst_ni || integ_fail_o) + // alert + `ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && (alert_tx_i.alert_p ^ alert_tx_i.alert_n) ##2 + state_q == Idle && !ping_pending_q |-> alert_o, clk_i, !rst_ni || integ_fail_o) + end else begin : gen_sync_assert + // signal integrity check propagation + `ASSERT(SigInt_A, alert_tx_i.alert_p == alert_tx_i.alert_n |-> integ_fail_o) + // ping response + `ASSERT(PingResponse1_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && ping_pending_q |-> + ping_ok_o, clk_i, !rst_ni || integ_fail_o) + // alert + `ASSERT(Alert_A, ##1 $rose(alert_tx_i.alert_p) && state_q == Idle && !ping_pending_q |-> + alert_o, clk_i, !rst_ni || integ_fail_o) + end + +endmodule : prim_alert_receiver diff --git a/vendor/lowrisc_ip/prim/rtl/prim_alert_sender.sv b/vendor/lowrisc_ip/prim/rtl/prim_alert_sender.sv new file mode 100644 index 0000000000..faa242a9c1 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_alert_sender.sv @@ -0,0 +1,236 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// The alert sender primitive module differentially encodes and transmits an +// alert signal to the prim_alert_receiver module. An alert will be signalled +// by a full handshake on alert_p/n and ack_p/n. The alert_i signal may +// be continuously asserted, in which case the alert signalling handshake +// will be repeatedly initiated. +// +// Further, this module supports in-band ping testing, which means that a level +// change on the ping_p/n diff pair will result in a full-handshake response +// on alert_p/n and ack_p/n. +// +// The protocol works in both asynchronous and synchronous cases. In the +// asynchronous case, the parameter AsyncOn must be set to 1'b1 in order to +// instantiate additional synchronization logic. Further, it must be ensured +// that the timing skew between all diff pairs is smaller than the shortest +// clock period of the involved clocks. +// +// Incorrectly encoded diff inputs can be detected and will be signalled +// to the receiver by placing an inconsistent diff value on the differential +// output (and continuously toggling it). +// +// See also: prim_alert_receiver, prim_diff_decode, alert_handler + +`include "prim_assert.sv" + +module prim_alert_sender + import prim_alert_pkg::*; +#( + // enables additional synchronization logic + parameter bit AsyncOn = 1'b1 +) ( + input clk_i, + input rst_ni, + // native alert from the peripheral + input alert_i, + // ping input diff pair and ack diff pair + input alert_rx_t alert_rx_i, + // alert output diff pair + output alert_tx_t alert_tx_o +); + + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + logic ping_sigint, ping_event; + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) i_decode_ping ( + .clk_i, + .rst_ni, + .diff_pi ( alert_rx_i.ping_p ), + .diff_ni ( alert_rx_i.ping_n ), + .level_o ( ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ping_event ), + .sigint_o ( ping_sigint ) + ); + + logic ack_sigint, ack_level; + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) i_decode_ack ( + .clk_i, + .rst_ni, + .diff_pi ( alert_rx_i.ack_p ), + .diff_ni ( alert_rx_i.ack_n ), + .level_o ( ack_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( ack_sigint ) + ); + + + /////////////////////////////////////////////////// + // main protocol FSM that drives the diff output // + /////////////////////////////////////////////////// + typedef enum logic [2:0] {Idle, HsPhase1, HsPhase2, SigInt, Pause0, Pause1} state_e; + state_e state_d, state_q; + logic alert_pq, alert_nq, alert_pd, alert_nd; + logic sigint_detected; + + assign sigint_detected = ack_sigint | ping_sigint; + + // diff pair output + assign alert_tx_o.alert_p = alert_pq; + assign alert_tx_o.alert_n = alert_nq; + + // alert and ping set regs + logic alert_set_d, alert_set_q, alert_clr; + logic ping_set_d, ping_set_q, ping_clr; + assign alert_set_d = (alert_clr) ? 1'b0 : (alert_set_q | alert_i); + assign ping_set_d = (ping_clr) ? 1'b0 : (ping_set_q | ping_event); + + // this FSM performs a full four phase handshake upon a ping or alert trigger. + // note that the latency of the alert_p/n diff pair is at least one cycle + // until it enters the receiver FSM. the same holds for the ack_* diff pair + // input. in case a signal integrity issue is detected, the FSM bails out, + // sets the alert_p/n diff pair to the same value and toggles it in order to + // signal that condition over to the receiver. + always_comb begin : p_fsm + // default + state_d = state_q; + alert_pd = 1'b0; + alert_nd = 1'b1; + ping_clr = 1'b0; + alert_clr = 1'b0; + + unique case (state_q) + Idle: begin + // alert always takes precedence + if (alert_i || alert_set_q || ping_event || ping_set_q) begin + state_d = HsPhase1; + alert_pd = 1'b1; + alert_nd = 1'b0; + if (ping_event || ping_set_q) begin + ping_clr = 1'b1; + end else begin + alert_clr = 1'b1; + end + end + end + // waiting for ack from receiver + HsPhase1: begin + if (ack_level) begin + state_d = HsPhase2; + end else begin + alert_pd = 1'b1; + alert_nd = 1'b0; + end + end + // wait for deassertion of ack + HsPhase2: begin + if (!ack_level) begin + state_d = Pause0; + end + end + // pause cycles between back-to-back handshakes + Pause0: state_d = Pause1; + Pause1: state_d = Idle; + // we have a signal integrity issue at one of + // the incoming diff pairs. this condition is + // signalled by setting the output diffpair + // to the same value and continuously toggling + // them. + SigInt: begin + state_d = Idle; + if (sigint_detected) begin + state_d = SigInt; + alert_pd = ~alert_pq; + alert_nd = ~alert_pq; + end + end + // catch parasitic states + default : state_d = Idle; + endcase + // bail out if a signal integrity issue has been detected + if (sigint_detected && (state_q != SigInt)) begin + state_d = SigInt; + alert_pd = 1'b0; + alert_nd = 1'b0; + ping_clr = 1'b0; + alert_clr = 1'b0; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + state_q <= Idle; + alert_pq <= 1'b0; + alert_nq <= 1'b1; + alert_set_q <= 1'b0; + ping_set_q <= 1'b0; + end else begin + state_q <= state_d; + alert_pq <= alert_pd; + alert_nq <= alert_nd; + alert_set_q <= alert_set_d; + ping_set_q <= ping_set_d; + end + end + + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(AlertPKnownO_A, alert_tx_o) + + if (AsyncOn) begin : gen_async_assert + // check propagation of sigint issues to output within three cycles + `ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n [*2] |-> + ##3 alert_tx_o.alert_p == alert_tx_o.alert_n) + `ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n [*2] |-> + ##3 alert_tx_o.alert_p == alert_tx_o.alert_n) + // output must be driven diff unless sigint issue detected + `ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) && + (alert_rx_i.ping_p ^ alert_rx_i.ping_n) |-> + ##3 alert_tx_o.alert_p ^ alert_tx_o.alert_n) + + // handshakes can take indefinite time if blocked due to sigint on outgoing + // lines (which is not visible here). thus, we only check whether the + // handshake is correctly initiated and defer the full handshake checking to the testbench. + // TODO: add the staggered cases as well + `ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) && + (alert_rx_i.ping_p ^ alert_rx_i.ping_n) ##2 state_q == Idle |=> + $rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n)) + end else begin : gen_sync_assert + // check propagation of sigint issues to output within one cycle + `ASSERT(SigIntPing_A, alert_rx_i.ping_p == alert_rx_i.ping_n |=> + alert_tx_o.alert_p == alert_tx_o.alert_n) + `ASSERT(SigIntAck_A, alert_rx_i.ack_p == alert_rx_i.ack_n |=> + alert_tx_o.alert_p == alert_tx_o.alert_n) + // output must be driven diff unless sigint issue detected + `ASSERT(DiffEncoding_A, (alert_rx_i.ack_p ^ alert_rx_i.ack_n) && + (alert_rx_i.ping_p ^ alert_rx_i.ping_n) |=> alert_tx_o.alert_p ^ alert_tx_o.alert_n) + // handshakes can take indefinite time if blocked due to sigint on outgoing + // lines (which is not visible here). thus, we only check whether the handshake + // is correctly initiated and defer the full handshake checking to the testbench. + `ASSERT(PingHs_A, ##1 $changed(alert_rx_i.ping_p) && state_q == Idle |=> + $rose(alert_tx_o.alert_p), clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n)) + end + + // if alert_i is true, handshakes should be continuously repeated + `ASSERT(AlertHs_A, alert_i && state_q == Idle |=> $rose(alert_tx_o.alert_p), + clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n)) + +endmodule : prim_alert_sender diff --git a/vendor/lowrisc_ip/prim/rtl/prim_arbiter_ppc.sv b/vendor/lowrisc_ip/prim/rtl/prim_arbiter_ppc.sv new file mode 100644 index 0000000000..d72083ae46 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_arbiter_ppc.sv @@ -0,0 +1,171 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 arbiter module +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// +// This is the original implementation of the arbiter which relies on parallel prefix +// computing optimization to optimize the request / arbiter tree. Not all synthesis tools +// may support this. +// +// Note that the currently winning request is held if the data sink is not ready. +// This behavior is required by some interconnect protocols (AXI, TL). Note that +// this implies that an asserted request must stay asserted +// until it has been granted. Note that for PPC, this option cannot +// be disabled. +// +// See also: prim_arbiter_tree + +`include "prim_assert.sv" + +module prim_arbiter_ppc #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter int EnDataPort = 1 +) ( + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [$clog2(N)-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + logic [N-1:0] masked_req; + logic [N-1:0] ppc_out; + logic [N-1:0] arb_req; + logic [N-1:0] mask, mask_next; + logic [N-1:0] winner; + + assign masked_req = mask & req_i; + assign arb_req = (|masked_req) ? masked_req : req_i; + + // PPC + // Even below code looks O(n) but DC optimizes it to O(log(N)) + // Using Parallel Prefix Computation + always_comb begin + ppc_out[0] = arb_req[0]; + for (int i = 1 ; i < N ; i++) begin + ppc_out[i] = ppc_out[i-1] | arb_req[i]; + end + end + + // Grant Generation: Leading-One detector + assign winner = ppc_out ^ {ppc_out[N-2:0], 1'b0}; + assign gnt_o = (ready_i) ? winner : '0; + + assign valid_o = |req_i; + // Mask Generation + assign mask_next = {ppc_out[N-2:0], 1'b0}; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mask <= '0; + end else if (valid_o && ready_i) begin + // Latch only when requests accepted + mask <= mask_next; + end else if (valid_o && !ready_i) begin + // Downstream isn't yet ready so, keep current request alive. (First come first serve) + mask <= ppc_out; + end + end + + if (EnDataPort == 1) begin: gen_datapath + always_comb begin + data_o = '0; + for (int i = 0 ; i < N ; i++) begin + if (winner[i]) begin + data_o = data_i[i]; + end + end + end + end else begin + assign data_o = '1; + // TODO: waive data_i from NOT_READ error + end + + always_comb begin + idx_o = '0; + for (int i = 0 ; i < N ; i++) begin + if (winner[i]) begin + idx_o = i; + end + end + end + + //////////////// + // assertions // + //////////////// + // grant shall be higher index than prev. unless no higher requests exist + `ASSERT(RoundRobin_A, valid_o && ready_i && $past(ready_i) && $past(valid_o) && + |(masked_req) |-> idx_o > $past(idx_o)) + + end + + //////////////// + // assertions // + //////////////// + + // we can only grant one requestor at a time + `ASSERT(CheckHotOne_A, $onehot0(gnt_o)) + // A grant implies that the sink is ready + `ASSERT(GntImpliesReady_A, |gnt_o |-> ready_i) + // A grant implies that the arbiter asserts valid as well + `ASSERT(GntImpliesValid_A, |gnt_o |-> valid_o) + // A request and a sink that is ready imply a grant + `ASSERT(ReqAndReadyImplyGrant_A, |req_i && ready_i |-> |gnt_o) + // A request and a sink that is ready imply a grant + `ASSERT(ReqImpliesValid_A, |req_i |-> valid_o) + // Both conditions above combined and reversed + `ASSERT(ReadyAndValidImplyGrant_A, ready_i && valid_o |-> |gnt_o) + // Both conditions above combined and reversed + `ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0) + // check index / grant correspond + `ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o]) + // data flow + `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `ASSERT_KNOWN(ValidKnown_A, valid_o) + `ASSERT_KNOWN(GrantKnown_A, gnt_o) + `ASSERT_KNOWN(IdxKnown_A, idx_o) + +`ifndef SYNTHESIS + // A grant implies a request + int unsigned k; // this is a symbolic variable + `ASSUME(KStable_M, ##1 $stable(k), clk_i, !rst_ni) + `ASSUME(KRange_M, k < N, clk_i, !rst_ni) + `ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k]) + + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted_M, (|req_i) && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i)) + // check that the arbitration decision is held if the sink is not ready + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o)) + +`endif + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_arbiter_tree.sv b/vendor/lowrisc_ip/prim/rtl/prim_arbiter_tree.sv new file mode 100644 index 0000000000..e0b4267947 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_arbiter_tree.sv @@ -0,0 +1,224 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 arbiter module +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// Lock: Lock arbiter decision when destination is not ready +// +// Hand optimized version which implements a binary tree to optimize +// timing. In particular, arbitration decisions and data mux steering happen +// simultaneously on the corresponding tree level, which leads to improved propagation +// delay compared to a solution that arbitrates first, followed by a data mux selection. +// +// If Lock is turned on, the currently winning request is held if the +// data sink is not ready. This behavior is required by some interconnect +// protocols (AXI, TL), and hence it is turned on by default. +// Note that this implies that an asserted request must stay asserted +// until it has been granted. +// +// See also: prim_arbiter_ppc + +`include "prim_assert.sv" + +module prim_arbiter_tree #( + parameter int unsigned N = 4, + parameter int unsigned DW = 32, + // holds the last arbiter decision in case the sink is not ready + // this should be enabled when used in AXI or TL protocols. + parameter bit Lock = 1'b1 +) ( + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [$clog2(N)-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + localparam int unsigned NumLevels = $clog2(N); + logic [N-1:0] req; + logic [2**(NumLevels+1)-2:0] req_tree; + logic [2**(NumLevels+1)-2:0] gnt_tree; + logic [2**(NumLevels+1)-2:0][NumLevels-1:0] idx_tree; + logic [2**(NumLevels+1)-2:0][DW-1:0] data_tree; + logic [NumLevels-1:0] rr_q; + + // req_locked + if (Lock) begin : gen_lock + logic [N-1:0] mask_d, mask_q; + // if the request cannot be served, we store the current request bits + // and apply it as a mask to the incoming requests in the next cycle. + assign mask_d = (valid_o && (!ready_i)) ? req : {N{1'b1}}; + assign req = mask_q & req_i; + + always_ff @(posedge clk_i) begin : p_lock_regs + if (!rst_ni) begin + mask_q <= {N{1'b1}}; + end else begin + mask_q <= mask_d; + end + end + end else begin : gen_no_lock + assign req = req_i; + end + + for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the Pa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int unsigned Base0 = (2**level)-1; + localparam int unsigned Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int unsigned Pa = Base0 + offset; + localparam int unsigned C0 = Base1 + 2*offset; + localparam int unsigned C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == NumLevels) begin : gen_leafs + if (offset < N) begin : gen_assign + // forward path + assign req_tree[Pa] = req[offset]; + assign idx_tree[Pa] = offset; + assign data_tree[Pa] = data_i[offset]; + // backward (grant) path + assign gnt_o[offset] = gnt_tree[Pa]; + end else begin : gen_tie_off + // forward path + assign req_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign data_tree[Pa] = '0; + end + // this creates the node assignments + end else begin : gen_nodes + // NOTE: the code below has been written in this way in order to work + // around a synthesis issue in Vivado 2018.3 and 2019.2 where the whole + // module would be optimized away if these assign statements contained + // ternary statements to implement the muxes. + // + // TODO: rewrite these lines with ternary statmements onec the problem + // has been fixed in the tool. + // + // See also originating issue: + // https://github.com/lowRISC/opentitan/issues/1355 + // Xilinx issue: + // https://forums.xilinx.com/t5/Synthesis/Simulation-Synthesis-Mismatch-with-Vivado-2018-3/m-p/1065923#M33849 + + // forward path + logic sel; // local helper variable + // this performs a (local) round robin arbitration using the associated rr counter bit + assign sel = ~req_tree[C0] | req_tree[C1] & rr_q[NumLevels-1-level]; + // propagate requests + assign req_tree[Pa] = req_tree[C0] | req_tree[C1]; + // muxes + assign idx_tree[Pa] = ({NumLevels{sel}} & idx_tree[C1]) | + ({NumLevels{~sel}} & idx_tree[C0]); + assign data_tree[Pa] = ({DW{sel}} & data_tree[C1]) | + ({DW{~sel}} & data_tree[C0]); + // backward (grant) path + assign gnt_tree[C0] = gnt_tree[Pa] & ~sel; + assign gnt_tree[C1] = gnt_tree[Pa] & sel; + end + end : gen_level + end : gen_tree + + // the results can be found at the tree root + assign idx_o = idx_tree[0]; + assign data_o = data_tree[0]; + assign valid_o = req_tree[0]; + // propagate the grant back to the requestors + assign gnt_tree[0] = valid_o & ready_i; + + // this is the round robin counter + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + rr_q <= '0; + end else begin + if (gnt_tree[0] && (rr_q == N-1)) begin + rr_q <= '0; + end else if (gnt_tree[0]) begin + rr_q <= rr_q + 1'b1; + end + end + end + + end + + //////////////// + // assertions // + //////////////// + + // we can only grant one requestor at a time + `ASSERT(CheckHotOne_A, $onehot0(gnt_o)) + // A grant implies that the sink is ready + `ASSERT(GntImpliesReady_A, |gnt_o |-> ready_i) + // A grant implies that the arbiter asserts valid as well + `ASSERT(GntImpliesValid_A, |gnt_o |-> valid_o) + // A request and a sink that is ready imply a grant + `ASSERT(ReqAndReadyImplyGrant_A, |req_i && ready_i |-> |gnt_o) + // A request and a sink that is ready imply a grant + `ASSERT(ReqImpliesValid_A, |req_i |-> valid_o) + // Both conditions above combined and reversed + `ASSERT(ReadyAndValidImplyGrant_A, ready_i && valid_o |-> |gnt_o) + // Both conditions above combined and reversed + `ASSERT(NoReadyValidNoGrant_A, !(ready_i || valid_o) |-> gnt_o == 0) + // check index / grant correspond + `ASSERT(IndexIsCorrect_A, ready_i && valid_o |-> gnt_o[idx_o] && req_i[idx_o]) + // data flow + `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `ASSERT_KNOWN(ValidKnown_A, valid_o) + `ASSERT_KNOWN(GrantKnown_A, gnt_o) + `ASSERT_KNOWN(IdxKnown_A, idx_o) + +`ifndef SYNTHESIS + // A grant implies a request + int unsigned k; // this is a symbolic variable + `ASSUME(KStable_M, ##1 $stable(k), clk_i, !rst_ni) + `ASSUME(KRange_M, k < N, clk_i, !rst_ni) + `ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k]) + + if (Lock) begin : gen_lock_assertion + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted_M, (|req_i) && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni) + // check that the arbitration decision is held if the sink is not ready + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o)) + end + +`endif + +endmodule diff --git a/shared/rtl/prim_assert.sv b/vendor/lowrisc_ip/prim/rtl/prim_assert.sv similarity index 100% rename from shared/rtl/prim_assert.sv rename to vendor/lowrisc_ip/prim/rtl/prim_assert.sv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_cipher_pkg.sv b/vendor/lowrisc_ip/prim/rtl/prim_cipher_pkg.sv new file mode 100644 index 0000000000..15df258b96 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_cipher_pkg.sv @@ -0,0 +1,363 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This package holds common constants and functions for PRESENT- and +// PRINCE-based scrambling devices. +// +// See also: prim_present, prim_prince +// +// References: - https://en.wikipedia.org/wiki/PRESENT +// - https://en.wikipedia.org/wiki/Prince_(cipher) +// - http://www.lightweightcrypto.org/present/present_ches2007.pdf +// - https://eprint.iacr.org/2012/529.pdf +// - https://eprint.iacr.org/2015/372.pdf +// - https://eprint.iacr.org/2014/656.pdf + +package prim_cipher_pkg; + + /////////////////// + // PRINCE Cipher // + /////////////////// + + parameter logic [15:0][3:0] PRINCE_SBOX4 = {4'h4, 4'hD, 4'h5, 4'hE, + 4'h0, 4'h8, 4'h7, 4'h6, + 4'h1, 4'h9, 4'hC, 4'hA, + 4'h2, 4'h3, 4'hF, 4'hB}; + + parameter logic [15:0][3:0] PRINCE_SBOX4_INV = {4'h1, 4'hC, 4'hE, 4'h5, + 4'h0, 4'h4, 4'h6, 4'hA, + 4'h9, 4'h8, 4'hD, 4'hF, + 4'h2, 4'h3, 4'h7, 4'hB}; + // nibble permutations + parameter logic [15:0][3:0] PRINCE_SHIFT_ROWS64 = '{4'hB, 4'h6, 4'h1, 4'hC, + 4'h7, 4'h2, 4'hD, 4'h8, + 4'h3, 4'hE, 4'h9, 4'h4, + 4'hF, 4'hA, 4'h5, 4'h0}; + + parameter logic [15:0][3:0] PRINCE_SHIFT_ROWS64_INV = '{4'h3, 4'h6, 4'h9, 4'hC, + 4'hF, 4'h2, 4'h5, 4'h8, + 4'hB, 4'hE, 4'h1, 4'h4, + 4'h7, 4'hA, 4'hD, 4'h0}; + + // these are the round constants + parameter logic [11:0][63:0] PRINCE_ROUND_CONST = {64'hC0AC29B7C97C50DD, + 64'hD3B5A399CA0C2399, + 64'h64A51195E0E3610D, + 64'hC882D32F25323C54, + 64'h85840851F1AC43AA, + 64'h7EF84F78FD955CB1, + 64'hBE5466CF34E90C6C, + 64'h452821E638D01377, + 64'h082EFA98EC4E6C89, + 64'hA4093822299F31D0, + 64'h13198A2E03707344, + 64'h0000000000000000}; + + // tweak constant for key modification between enc/dec modes + parameter logic [63:0] PRINCE_ALPHA_CONST = 64'hC0AC29B7C97C50DD; + + // masking constants for shift rows function below + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST0 = 16'hEDB7; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST1 = 16'h7EDB; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST2 = 16'hB7ED; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST3 = 16'hDB7E; + + // nibble shifts + function automatic logic [31:0] prince_shiftrows_32bit(logic [31:0] state_in, + logic [15:0][3:0] shifts ); + logic [31:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 32/2; k++) begin + // operate on pairs of 2bit instead of nibbles + state_out[k*2 +: 2] = state_in[shifts[k]*2 +: 2]; + end + return state_out; + endfunction : prince_shiftrows_32bit + + function automatic logic [63:0] prince_shiftrows_64bit(logic [63:0] state_in, + logic [15:0][3:0] shifts ); + logic [63:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 64/4; k++) begin + state_out[k*4 +: 4] = state_in[shifts[k]*4 +: 4]; + end + return state_out; + endfunction : prince_shiftrows_64bit + + // XOR reduction of four nibbles in a 16bit subvector + function automatic logic [3:0] prince_nibble_red16(logic [15:0] vect); + return vect[0 +: 4] ^ vect[4 +: 4] ^ vect[8 +: 4] ^ vect[12 +: 4]; + endfunction : prince_nibble_red16 + + // M prime multiplication + function automatic logic [31:0] prince_mult_prime_32bit(logic [31:0] state_in); + logic [31:0] state_out; + // M0 + state_out[0 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[4 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[8 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[12 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + // M1 + state_out[16 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[20 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[24 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[28 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + return state_out; + endfunction : prince_mult_prime_32bit + + // M prime multiplication + function automatic logic [63:0] prince_mult_prime_64bit(logic [63:0] state_in); + logic [63:0] state_out; + // M0 + state_out[0 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[4 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[8 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[12 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + // M1 + state_out[16 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[20 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[24 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[28 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + // M1 + state_out[32 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[36 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[40 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[44 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + // M0 + state_out[48 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[52 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[56 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[60 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + return state_out; + endfunction : prince_mult_prime_64bit + + + //////////////////// + // PRESENT Cipher // + //////////////////// + + // this is the sbox from the present cipher + parameter logic [15:0][3:0] PRESENT_SBOX4 = {4'h2, 4'h1, 4'h7, 4'h4, + 4'h8, 4'hF, 4'hE, 4'h3, + 4'hD, 4'hA, 4'h0, 4'h9, + 4'hB, 4'h6, 4'h5, 4'hC}; + + parameter logic [15:0][3:0] PRESENT_SBOX4_INV = {4'hA, 4'h9, 4'h7, 4'h0, + 4'h3, 4'h6, 4'h4, 4'hB, + 4'hD, 4'h2, 4'h1, 4'hC, + 4'h8, 4'hF, 4'hE, 4'h5}; + + // these are modified permutation indices for a 32bit version that + // follow the same pattern as for the 64bit version + parameter logic [31:0][4:0] PRESENT_PERM32 = {5'd31, 5'd23, 5'd15, 5'd07, + 5'd30, 5'd22, 5'd14, 5'd06, + 5'd29, 5'd21, 5'd13, 5'd05, + 5'd28, 5'd20, 5'd12, 5'd04, + 5'd27, 5'd19, 5'd11, 5'd03, + 5'd26, 5'd18, 5'd10, 5'd02, + 5'd25, 5'd17, 5'd09, 5'd01, + 5'd24, 5'd16, 5'd08, 5'd00}; + + parameter logic [31:0][4:0] PRESENT_PERM32_INV = {5'd31, 5'd27, 5'd23, 5'd19, + 5'd15, 5'd11, 5'd07, 5'd03, + 5'd30, 5'd26, 5'd22, 5'd18, + 5'd14, 5'd10, 5'd06, 5'd02, + 5'd29, 5'd25, 5'd21, 5'd17, + 5'd13, 5'd09, 5'd05, 5'd01, + 5'd28, 5'd24, 5'd20, 5'd16, + 5'd12, 5'd08, 5'd04, 5'd00}; + + // these are the permutation indices of the present cipher + parameter logic [63:0][5:0] PRESENT_PERM64 = {6'd63, 6'd47, 6'd31, 6'd15, + 6'd62, 6'd46, 6'd30, 6'd14, + 6'd61, 6'd45, 6'd29, 6'd13, + 6'd60, 6'd44, 6'd28, 6'd12, + 6'd59, 6'd43, 6'd27, 6'd11, + 6'd58, 6'd42, 6'd26, 6'd10, + 6'd57, 6'd41, 6'd25, 6'd09, + 6'd56, 6'd40, 6'd24, 6'd08, + 6'd55, 6'd39, 6'd23, 6'd07, + 6'd54, 6'd38, 6'd22, 6'd06, + 6'd53, 6'd37, 6'd21, 6'd05, + 6'd52, 6'd36, 6'd20, 6'd04, + 6'd51, 6'd35, 6'd19, 6'd03, + 6'd50, 6'd34, 6'd18, 6'd02, + 6'd49, 6'd33, 6'd17, 6'd01, + 6'd48, 6'd32, 6'd16, 6'd00}; + + parameter logic [63:0][5:0] PRESENT_PERM64_INV = {6'd63, 6'd59, 6'd55, 6'd51, + 6'd47, 6'd43, 6'd39, 6'd35, + 6'd31, 6'd27, 6'd23, 6'd19, + 6'd15, 6'd11, 6'd07, 6'd03, + 6'd62, 6'd58, 6'd54, 6'd50, + 6'd46, 6'd42, 6'd38, 6'd34, + 6'd30, 6'd26, 6'd22, 6'd18, + 6'd14, 6'd10, 6'd06, 6'd02, + 6'd61, 6'd57, 6'd53, 6'd49, + 6'd45, 6'd41, 6'd37, 6'd33, + 6'd29, 6'd25, 6'd21, 6'd17, + 6'd13, 6'd09, 6'd05, 6'd01, + 6'd60, 6'd56, 6'd52, 6'd48, + 6'd44, 6'd40, 6'd36, 6'd32, + 6'd28, 6'd24, 6'd20, 6'd16, + 6'd12, 6'd08, 6'd04, 6'd00}; + + // forward key schedule + function automatic logic [63:0] present_update_key64(logic [63:0] key_in, + logic [4:0] round_idx); + logic [63:0] key_out; + // rotate by 61 to the left + key_out = 64'(key_in << 61) | 64'(key_in >> (64-61)); + // sbox on uppermost 4 bits + key_out[63 -: 4] = PRESENT_SBOX4[key_out[63 -: 4]]; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= round_idx; + return key_out; + endfunction : present_update_key64 + + function automatic logic [79:0] present_update_key80(logic [79:0] key_in, + logic [4:0] round_idx); + logic [79:0] key_out; + // rotate by 61 to the left + key_out = 80'(key_in << 61) | 80'(key_in >> (80-61)); + // sbox on uppermost 4 bits + key_out[79 -: 4] = PRESENT_SBOX4[key_out[79 -: 4]]; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= round_idx; + return key_out; + endfunction : present_update_key80 + + function automatic logic [127:0] present_update_key128(logic [127:0] key_in, + logic [4:0] round_idx); + logic [127:0] key_out; + // rotate by 61 to the left + key_out = 128'(key_in << 61) | 128'(key_in >> (128-61)); + // sbox on uppermost 4 bits + key_out[127 -: 4] = PRESENT_SBOX4[key_out[127 -: 4]]; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= round_idx; + return key_out; + endfunction : present_update_key128 + + + // inverse key schedule + function automatic logic [63:0] present_inv_update_key64(logic [63:0] key_in, + logic [4:0] round_idx, + // total number of rounds employed + logic [4:0] round_cnt); + logic [63:0] key_out; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx; + // sbox on uppermost 4 bits + key_out[63 -: 4] = PRESENT_SBOX4_INV[key_out[63 -: 4]]; + // rotate by 61 to the right + key_out = 64'(key_in >> 61) | 64'(key_in << (64-61)); + return key_out; + endfunction : present_inv_update_key64 + + function automatic logic [79:0] present_inv_update_key80(logic [79:0] key_in, + logic [4:0] round_idx, + // total number of rounds employed + logic [4:0] round_cnt); + logic [79:0] key_out; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx; + // sbox on uppermost 4 bits + key_out[79 -: 4] = PRESENT_SBOX4_INV[key_out[79 -: 4]]; + // rotate by 61 to the right + key_out = 80'(key_in >> 61) | 80'(key_in << (80-61)); + return key_out; + endfunction : present_inv_update_key80 + + function automatic logic [127:0] present_inv_update_key128(logic [127:0] key_in, + logic [4:0] round_idx, + // total number of rounds employed + logic [4:0] round_cnt); + logic [127:0] key_out; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= 6'(round_cnt) + 1 - round_idx; + // sbox on uppermost 4 bits + key_out[127 -: 4] = PRESENT_SBOX4_INV[key_out[127 -: 4]]; + // rotate by 61 to the right + key_out = 128'(key_in >> 61) | 128'(key_in << (128-61)); + return key_out; + endfunction : present_inv_update_key128 + + + // these functions can be used to derive the DEC key from the ENC key by + // stepping the key by the correct number of rounds using the keyschedule functions above. + function automatic logic [63:0] present_get_dec_key64(logic [63:0] key_in, + // total number of rounds employed + logic [4:0] round_cnt); + logic [63:0] key_out; + key_out = key_in; + for (int k = 0; k < round_cnt; k++) begin + key_out = present_update_key64(key_out, 5'(k + 1)); + end + return key_out; + endfunction : present_get_dec_key64 + + function automatic logic [79:0] present_get_dec_key80(logic [79:0] key_in, + // total number of rounds employed + logic [4:0] round_cnt); + logic [79:0] key_out; + key_out = key_in; + for (int k = 0; k < round_cnt; k++) begin + key_out = present_update_key80(key_out, 5'(k + 1)); + end + return key_out; + endfunction : present_get_dec_key80 + + function automatic logic [127:0] present_get_dec_key128(logic [127:0] key_in, + // total number of rounds employed + logic [4:0] round_cnt); + logic [127:0] key_out; + key_out = key_in; + for (int k = 0; k < round_cnt; k++) begin + key_out = present_update_key128(key_out, 5'(k + 1)); + end + return key_out; + endfunction : present_get_dec_key128 + + ///////////////////////// + // Common Subfunctions // + ///////////////////////// + + function automatic logic [31:0] sbox4_32bit(logic [31:0] state_in, logic [15:0][3:0] sbox4); + logic [31:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 32/4; k++) begin + state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]]; + end + return state_out; + endfunction : sbox4_32bit + + function automatic logic [63:0] sbox4_64bit(logic [63:0] state_in, logic [15:0][3:0] sbox4); + logic [63:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 64/4; k++) begin + state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]]; + end + return state_out; + endfunction : sbox4_64bit + + function automatic logic [31:0] perm_32bit(logic [31:0] state_in, logic [31:0][4:0] perm); + logic [31:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 32; k++) begin + state_out[k] = state_in[perm[k]]; + end + return state_out; + endfunction : perm_32bit + + function automatic logic [63:0] perm_64bit(logic [63:0] state_in, logic [63:0][5:0] perm); + logic [63:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 64; k++) begin + state_out[k] = state_in[perm[k]]; + end + return state_out; + endfunction : perm_64bit + +endpackage : prim_cipher_pkg diff --git a/vendor/lowrisc_ip/prim/rtl/prim_clock_gating_sync.sv b/vendor/lowrisc_ip/prim/rtl/prim_clock_gating_sync.sv new file mode 100644 index 0000000000..aca0d5a9f5 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_clock_gating_sync.sv @@ -0,0 +1,34 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Common Library: Clock Gating cell with synchronizer + +module prim_clock_gating_sync ( + input clk_i, + input rst_ni, + input test_en_i, + input async_en_i, + output logic en_o, + output logic clk_o +); + + + prim_flop_2sync #( + .Width(1) + ) i_sync ( + .clk_i, + .rst_ni, + .d(async_en_i), + .q(en_o) + ); + + prim_clock_gating i_cg ( + .clk_i, + .en_i(en_o), + .test_en_i, + .clk_o + ); + + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_clock_inverter.sv b/vendor/lowrisc_ip/prim/rtl/prim_clock_inverter.sv new file mode 100644 index 0000000000..3113c55f82 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_clock_inverter.sv @@ -0,0 +1,29 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Clock inverter +// Varies on the process + +module prim_clock_inverter #( + parameter bit HasScanMode = 1'b1 +) ( + input clk_i, + input scanmode_i, + output logic clk_no // Inverted +); + + if (HasScanMode) begin : gen_scan + prim_clock_mux2 i_dft_tck_mux ( + .clk0_i ( ~clk_i ), + .clk1_i ( clk_i ), // bypass the inverted clock for testing + .sel_i ( scanmode_i ), + .clk_o ( clk_no ) + ); + end else begin : gen_noscan + logic unused_scanmode; + assign unused_scanmode = scanmode_i; + assign clk_no = ~clk_i; + end + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_diff_decode.sv b/vendor/lowrisc_ip/prim/rtl/prim_diff_decode.sv new file mode 100644 index 0000000000..3a1c27aef5 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_diff_decode.sv @@ -0,0 +1,260 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module decodes a differentially encoded signal and detects +// incorrectly encoded differential states. +// +// In case the differential pair crosses an asynchronous boundary, it has +// to be re-synchronized to the local clock. This can be achieved by +// setting the AsyncOn parameter to 1'b1. In that case, two additional +// input registers are added (to counteract metastability), and +// a pattern detector is instantiated that detects skewed level changes on +// the differential pair (i.e., when level changes on the diff pair are +// sampled one cycle apart due to a timing skew between the two wires). +// +// See also: prim_alert_sender, prim_alert_receiver, alert_handler + +`include "prim_assert.sv" + +module prim_diff_decode #( + // enables additional synchronization logic + parameter bit AsyncOn = 1'b0 +) ( + input clk_i, + input rst_ni, + // input diff pair + input diff_pi, + input diff_ni, + // logical level and + // detected edges + output logic level_o, + output logic rise_o, + output logic fall_o, + // either rise or fall + output logic event_o, + //signal integrity issue detected + output logic sigint_o +); + + logic level_d, level_q; + + /////////////////////////////////////////////////////////////// + // synchronization regs for incoming diff pair (if required) // + /////////////////////////////////////////////////////////////// + if (AsyncOn) begin : gen_async + + typedef enum logic [1:0] {IsStd, IsSkewed, SigInt} state_e; + state_e state_d, state_q; + logic diff_p_edge, diff_n_edge, diff_check_ok, level; + + // 2 sync regs, one reg for edge detection + logic diff_pq, diff_nq, diff_pd, diff_nd; + + prim_flop_2sync #( + .Width(1), + .ResetValue(0) + ) i_sync_p ( + .clk_i, + .rst_ni, + .d(diff_pi), + .q(diff_pd) + ); + + prim_flop_2sync #( + .Width(1), + .ResetValue(1) + ) i_sync_n ( + .clk_i, + .rst_ni, + .d(diff_ni), + .q(diff_nd) + ); + + // detect level transitions + assign diff_p_edge = diff_pq ^ diff_pd; + assign diff_n_edge = diff_nq ^ diff_nd; + + // detect sigint issue + assign diff_check_ok = diff_pd ^ diff_nd; + + // this is the current logical level + assign level = diff_pd; + + // outputs + assign level_o = level_d; + assign event_o = rise_o | fall_o; + + // sigint detection is a bit more involved in async case since + // we might have skew on the diff pair, which can result in a + // one cycle sampling delay between the two wires + // so we need a simple pattern matcher + // the following waves are legal + // clk | | | | | | | | + // _______ _______ + // p _______/ ... \________ + // _______ ________ + // n \_______ ... _______/ + // ____ ___ + // p __________/ ... \________ + // _______ ________ + // n \_______ ... _______/ + // + // i.e., level changes may be off by one cycle - which is permissible + // as long as this condition is only one cycle long. + + + always_comb begin : p_diff_fsm + // default + state_d = state_q; + level_d = level_q; + rise_o = 1'b0; + fall_o = 1'b0; + sigint_o = 1'b0; + + unique case (state_q) + // we remain here as long as + // the diff pair is correctly encoded + IsStd: begin + if (diff_check_ok) begin + level_d = level; + if (diff_p_edge && diff_n_edge) begin + if (level) begin + rise_o = 1'b1; + end else begin + fall_o = 1'b1; + end + end + end else begin + if (diff_p_edge || diff_n_edge) begin + state_d = IsSkewed; + end else begin + state_d = SigInt; + sigint_o = 1'b1; + end + end + end + // diff pair must be correctly encoded, otherwise we got a sigint + IsSkewed: begin + if (diff_check_ok) begin + state_d = IsStd; + level_d = level; + if (level) rise_o = 1'b1; + else fall_o = 1'b1; + end else begin + state_d = SigInt; + sigint_o = 1'b1; + end + end + // Signal integrity issue detected, remain here + // until resolved + SigInt: begin + sigint_o = 1'b1; + if (diff_check_ok) begin + state_d = IsStd; + sigint_o = 1'b0; + end + end + default : ; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_sync_reg + if (!rst_ni) begin + state_q <= IsStd; + diff_pq <= 1'b0; + diff_nq <= 1'b1; + level_q <= 1'b0; + end else begin + state_q <= state_d; + diff_pq <= diff_pd; + diff_nq <= diff_nd; + level_q <= level_d; + end + end + + ////////////////////////////////////////////////////////// + // fully synchronous case, no skew present in this case // + ////////////////////////////////////////////////////////// + end else begin : gen_no_async + logic diff_pq, diff_pd; + + // one reg for edge detection + assign diff_pd = diff_pi; + + // incorrect encoding -> signal integrity issue + assign sigint_o = ~(diff_pi ^ diff_ni); + + assign level_o = (sigint_o) ? level_q : diff_pi; + assign level_d = level_o; + + // detect level transitions + assign rise_o = (~diff_pq & diff_pi) & ~sigint_o; + assign fall_o = ( diff_pq & ~diff_pi) & ~sigint_o; + assign event_o = rise_o | fall_o; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_edge_reg + if (!rst_ni) begin + diff_pq <= 1'b0; + level_q <= 1'b0; + end else begin + diff_pq <= diff_pd; + level_q <= level_d; + end + end + end + + //////////////// + // assertions // + //////////////// + + // shared assertions + // sigint -> level stays the same during sigint + // $isunknown is needed to avoid false assertion in first clock cycle + `ASSERT(SigintLevelCheck_A, ##1 sigint_o |-> $stable(level_o)) + // sigint -> no additional events asserted at output + `ASSERT(SigintEventCheck_A, sigint_o |-> !event_o) + `ASSERT(SigintRiseCheck_A, sigint_o |-> !rise_o) + `ASSERT(SigintFallCheck_A, sigint_o |-> !fall_o) + + if (AsyncOn) begin : gen_async_assert + // assertions for asynchronous case + // correctly detect sigint issue (only one transition cycle of permissible due to skew) + `ASSERT(SigintCheck0_A, diff_pi == diff_ni [*2] |-> ##[1:2] sigint_o) + // the synchronizer adds 2 cycles of latency + `ASSERT(SigintCheck1_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1 + $rose(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $fell(diff_ni) |-> + ##2 rise_o) + `ASSERT(SigintCheck2_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1 + $fell(diff_pi) && $stable(diff_ni) ##1 $stable(diff_pi) && $rose(diff_ni) |-> + ##2 fall_o) + `ASSERT(SigintCheck3_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1 + $rose(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $fell(diff_pi) |-> + ##2 fall_o) + `ASSERT(SigintCheck4_A, ##1 (diff_pi ^ diff_ni) && $stable(diff_pi) && $stable(diff_ni) ##1 + $fell(diff_ni) && $stable(diff_pi) ##1 $stable(diff_ni) && $rose(diff_pi) |-> + ##2 rise_o) + // correctly detect edges + `ASSERT(RiseCheck_A, ##1 $rose(diff_pi) && (diff_pi ^ diff_ni) |-> + ##[2:3] rise_o, clk_i, !rst_ni || sigint_o) + `ASSERT(FallCheck_A, ##1 $fell(diff_pi) && (diff_pi ^ diff_ni) |-> + ##[2:3] fall_o, clk_i, !rst_ni || sigint_o) + `ASSERT(EventCheck_A, ##1 $changed(diff_pi) && (diff_pi ^ diff_ni) |-> + ##[2:3] event_o, clk_i, !rst_ni || sigint_o) + // correctly detect level + `ASSERT(LevelCheck0_A, !sigint_o && (diff_pi ^ diff_ni) [*3] |=> $past(diff_pi, 2) == level_o, + clk_i, !rst_ni || sigint_o) + + end else begin : gen_sync_assert + // assertions for synchronous case + // correctly detect sigint issue + `ASSERT(SigintCheck_A, diff_pi == diff_ni |-> sigint_o) + // correctly detect edges + `ASSERT(RiseCheck_A, ##1 $rose(diff_pi) && (diff_pi ^ diff_ni) |-> rise_o) + `ASSERT(FallCheck_A, ##1 $fell(diff_pi) && (diff_pi ^ diff_ni) |-> fall_o) + `ASSERT(EventCheck_A, ##1 $changed(diff_pi) && (diff_pi ^ diff_ni) |-> event_o) + // correctly detect level + `ASSERT(LevelCheck_A, (diff_pi ^ diff_ni) |-> diff_pi == level_o) + end + +endmodule : prim_diff_decode diff --git a/vendor/lowrisc_ip/prim/rtl/prim_esc_pkg.sv b/vendor/lowrisc_ip/prim/rtl/prim_esc_pkg.sv new file mode 100644 index 0000000000..6d300b85ae --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_esc_pkg.sv @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_esc_pkg; + typedef struct packed { + logic esc_p; + logic esc_n; + } esc_tx_t; + + typedef struct packed { + logic resp_p; + logic resp_n; + } esc_rx_t; +endpackage diff --git a/vendor/lowrisc_ip/prim/rtl/prim_esc_receiver.sv b/vendor/lowrisc_ip/prim/rtl/prim_esc_receiver.sv new file mode 100644 index 0000000000..fe146113c8 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_esc_receiver.sv @@ -0,0 +1,180 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module decodes escalation enable pulses that have been encoded using +// the prim_esc_sender module. +// +// The module supports in-band ping testing of the escalation +// wires. This is accomplished by the sender module that places a single-cycle, +// differentially encoded pulse on esc_p/n which will be interpreted as a ping +// request by the receiver module. The receiver module responds by sending back +// the response pattern "1010". +// +// Native escalation enable pulses are differentiated from ping +// requests by making sure that these pulses are always longer than 1 cycle. +// +// See also: prim_esc_sender, prim_diff_decode, alert_handler + +`include "prim_assert.sv" + +module prim_esc_receiver + import prim_esc_pkg::*; +( + input clk_i, + input rst_ni, + // escalation enable + output logic esc_en_o, + // escalation / ping response + output esc_rx_t esc_rx_o, + // escalation output diff pair + input esc_tx_t esc_tx_i +); + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + + logic esc_level, sigint_detected; + + prim_diff_decode #( + .AsyncOn(1'b0) + ) i_decode_esc ( + .clk_i, + .rst_ni, + .diff_pi ( esc_tx_i.esc_p ), + .diff_ni ( esc_tx_i.esc_n ), + .level_o ( esc_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( sigint_detected ) + ); + + ///////////////// + // RX/TX Logic // + ///////////////// + + typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e; + state_e state_d, state_q; + logic resp_pd, resp_pq, resp_nd, resp_nq; + + assign esc_rx_o.resp_p = resp_pq; + assign esc_rx_o.resp_n = resp_nq; + + + always_comb begin : p_fsm + // default + state_d = state_q; + resp_pd = 1'b0; + resp_nd = 1'b1; + esc_en_o = 1'b0; + + unique case (state_q) + // wait for the esc_p/n diff pair + Idle: begin + if (esc_level) begin + state_d = Check; + resp_pd = 1'b1; + resp_nd = 1'b0; + end + end + // we decide here whether this is only a ping request or + // whether this is an escalation enable + Check: begin + state_d = PingResp; + if (esc_level) begin + state_d = EscResp; + esc_en_o = 1'b1; + end + end + // finish ping response. in case esc_level is again asserted, + // we got an escalation signal (pings cannot occur back to back) + PingResp: begin + state_d = Idle; + resp_pd = 1'b1; + resp_nd = 1'b0; + if (esc_level) begin + state_d = EscResp; + esc_en_o = 1'b1; + end + end + // we have got an escalation enable pulse, + // keep on toggling the outputs + EscResp: begin + state_d = Idle; + if (esc_level) begin + state_d = EscResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + esc_en_o = 1'b1; + end + end + // we have a signal integrity issue at one of + // the incoming diff pairs. this condition is + // signalled to the sender by setting the resp + // diffpair to the same value and continuously + // toggling them. + SigInt: begin + state_d = Idle; + if (sigint_detected) begin + state_d = SigInt; + resp_pd = ~resp_pq; + resp_nd = ~resp_pq; + end + end + default : state_d = Idle; + endcase + + // bail out if a signal integrity issue has been detected + if (sigint_detected && (state_q != SigInt)) begin + state_d = SigInt; + resp_pd = 1'b0; + resp_nd = 1'b0; + end + end + + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + resp_pq <= 1'b0; + resp_nq <= 1'b1; + end else begin + state_q <= state_d; + resp_pq <= resp_pd; + resp_nq <= resp_nd; + end + end + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(EscEnKnownO_A, esc_en_o) + `ASSERT_KNOWN(RespPKnownO_A, esc_rx_o) + + `ASSERT(SigIntCheck0_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> + esc_rx_o.resp_p == esc_rx_o.resp_n, clk_i, !rst_ni) + `ASSERT(SigIntCheck1_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> state_q == SigInt) + // correct diff encoding + `ASSERT(DiffEncCheck_A, esc_tx_i.esc_p ^ esc_tx_i.esc_n |=> + esc_rx_o.resp_p ^ esc_rx_o.resp_n) + // disable in case of ping integrity issue + `ASSERT(PingRespCheck_A, $rose(esc_tx_i.esc_p) |=> $fell(esc_tx_i.esc_p) |-> + $rose(esc_rx_o.resp_p) |=> $fell(esc_rx_o.resp_p), + clk_i, !rst_ni || (esc_tx_i.esc_p == esc_tx_i.esc_n)) + // escalation response needs to continuously toggle + `ASSERT(EscRespCheck_A, esc_tx_i.esc_p && $past(esc_tx_i.esc_p) && + (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && $past(esc_tx_i.esc_p ^ esc_tx_i.esc_n) + |=> esc_rx_o.resp_p != $past(esc_rx_o.resp_p)) + // detect escalation pulse + `ASSERT(EscEnCheck_A, esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt + |=> esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_en_o) + +endmodule : prim_esc_receiver diff --git a/vendor/lowrisc_ip/prim/rtl/prim_esc_sender.sv b/vendor/lowrisc_ip/prim/rtl/prim_esc_sender.sv new file mode 100644 index 0000000000..0db7541122 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_esc_sender.sv @@ -0,0 +1,253 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module differentially encodes an escalation enable pulse +// of arbitrary width. +// +// The module supports in-band ping testing of the escalation +// wires. This is accomplished by sending out a single, differentially +// encoded pulse on esc_p/n which will be interpreted as a ping +// request by the escalation receiver. Note that ping_en_i shall +// be held high until either ping_ok_o or integ_fail_o is asserted. +// +// Native escalation enable pulses are differentiated from ping +// requests by making sure that these pulses are always longer than 1 cycle. +// +// If there is a differential encoding error, integ_fail_o +// will be asserted. +// +// See also: prim_esc_receiver, prim_diff_decode, alert_handler + +`include "prim_assert.sv" + +module prim_esc_sender + import prim_esc_pkg::*; +( + input clk_i, + input rst_ni, + // this triggers a ping test. keep asserted + // until either ping_ok_o or ping_fail_o is asserted. + input ping_en_i, + output logic ping_ok_o, + // asserted if signal integrity issue detected + output logic integ_fail_o, + // escalation enable signal + input esc_en_i, + // escalation / ping response + input esc_rx_t esc_rx_i, + // escalation output diff pair + output esc_tx_t esc_tx_o +); + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + + logic resp, sigint_detected; + + prim_diff_decode #( + .AsyncOn(1'b0) + ) i_decode_resp ( + .clk_i, + .rst_ni, + .diff_pi ( esc_rx_i.resp_p ), + .diff_ni ( esc_rx_i.resp_n ), + .level_o ( resp ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( sigint_detected ) + ); + + ////////////// + // TX Logic // + ////////////// + + logic ping_en_d, ping_en_q; + logic esc_en_d, esc_en_q, esc_en_q1; + + assign ping_en_d = ping_en_i; + assign esc_en_d = esc_en_i; + + // ping enable is 1 cycle pulse + // escalation pulse is always longer than 2 cycles + assign esc_tx_o.esc_p = esc_en_i | esc_en_q | (ping_en_d & ~ping_en_q); + assign esc_tx_o.esc_n = ~esc_tx_o.esc_p; + + ////////////// + // RX Logic // + ////////////// + + typedef enum logic [2:0] {Idle, CheckEscRespLo, CheckEscRespHi, + CheckPingResp0, CheckPingResp1, CheckPingResp2, CheckPingResp3} fsm_e; + + fsm_e state_d, state_q; + + always_comb begin : p_fsm + // default + state_d = state_q; + ping_ok_o = 1'b0; + integ_fail_o = sigint_detected; + + unique case (state_q) + // wait for ping or escalation enable + Idle: begin + if (esc_en_i) begin + state_d = CheckEscRespHi; + end else if (ping_en_i) begin + state_d = CheckPingResp0; + end + // any assertion of the response signal + // signal here will trigger a sigint error + if (resp) begin + integ_fail_o = 1'b1; + end + end + // check whether response is 0 + CheckEscRespLo: begin + state_d = CheckEscRespHi; + if (!esc_tx_o.esc_p || resp) begin + state_d = Idle; + integ_fail_o = sigint_detected | resp; + end + end + // check whether response is 1 + CheckEscRespHi: begin + state_d = CheckEscRespLo; + if (!esc_tx_o.esc_p || !resp) begin + state_d = Idle; + integ_fail_o = sigint_detected | ~resp; + end + end + // start of ping response sequence + // we expect the sequence "1010" + CheckPingResp0: begin + state_d = CheckPingResp1; + // abort sequence immediately if escalation is signalled, + // jump to escalation response checking (lo state) + if (esc_en_i) begin + state_d = CheckEscRespLo; + // abort if response is wrong + end else if (!resp) begin + state_d = Idle; + integ_fail_o = 1'b1; + end + end + CheckPingResp1: begin + state_d = CheckPingResp2; + // abort sequence immediately if escalation is signalled, + // jump to escalation response checking (hi state) + if (esc_en_i) begin + state_d = CheckEscRespHi; + // abort if response is wrong + end else if (resp) begin + state_d = Idle; + integ_fail_o = 1'b1; + end + end + CheckPingResp2: begin + state_d = CheckPingResp3; + // abort sequence immediately if escalation is signalled, + // jump to escalation response checking (lo state) + if (esc_en_i) begin + state_d = CheckEscRespLo; + // abort if response is wrong + end else if (!resp) begin + state_d = Idle; + integ_fail_o = 1'b1; + end + end + CheckPingResp3: begin + state_d = Idle; + // abort sequence immediately if escalation is signalled, + // jump to escalation response checking (hi state) + if (esc_en_i) begin + state_d = CheckEscRespHi; + // abort if response is wrong + end else if (resp) begin + integ_fail_o = 1'b1; + end else begin + ping_ok_o = ping_en_i; + end + end + default : state_d = Idle; + endcase + + // escalation takes precedence, + // immediately return ok in that case + if ((esc_en_i || esc_en_q || esc_en_q1) && ping_en_i) begin + ping_ok_o = 1'b1; + end + + // a sigint error will reset the state machine + // and have it pause for two cycles to let the + // receiver recover + if (sigint_detected) begin + ping_ok_o = 1'b0; + state_d = Idle; + end + end + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + esc_en_q <= 1'b0; + esc_en_q1 <= 1'b0; + ping_en_q <= 1'b0; + end else begin + state_q <= state_d; + esc_en_q <= esc_en_d; + esc_en_q1 <= esc_en_q; + ping_en_q <= ping_en_d; + end + end + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(PingOkKnownO_A, ping_ok_o) + `ASSERT_KNOWN(IntegFailKnownO_A, integ_fail_o) + `ASSERT_KNOWN(EscPKnownO_A, esc_tx_o) + + // diff encoding of output + `ASSERT(DiffEncCheck_A, esc_tx_o.esc_p ^ esc_tx_o.esc_n) + // signal integrity check propagation + `ASSERT(SigIntCheck0_A, esc_rx_i.resp_p == esc_rx_i.resp_n |-> integ_fail_o) + // this happens in case we did not get a correct escalation response + `ASSERT(SigIntCheck1_A, ##1 $rose(esc_en_i) && + state_q inside {Idle, CheckPingResp1, CheckPingResp3} ##1 !esc_rx_i.resp_p |-> + integ_fail_o, clk_i, !rst_ni || (esc_rx_i.resp_p == esc_rx_i.resp_n) || + (state_q == Idle && resp)) + `ASSERT(SigIntCheck2_A, ##1 $rose(esc_en_i) && + state_q inside {CheckPingResp0, CheckPingResp2} ##1 esc_rx_i.resp_p |-> + integ_fail_o, clk_i, !rst_ni || (esc_rx_i.resp_p == esc_rx_i.resp_n) || + (state_q == Idle && resp)) + // unexpected response + `ASSERT(SigIntCheck3_A, state_q == Idle && resp |-> integ_fail_o) + // signal_int_backward_check + `ASSERT(SigIntBackCheck_A, integ_fail_o |-> (esc_rx_i.resp_p == esc_rx_i.resp_n) || + (esc_rx_i.resp_p && !(state_q == CheckEscRespHi)) || + (!esc_rx_i.resp_p && !(state_q == CheckEscRespLo))) + // state machine CheckEscRespLo and Hi as they are ideal resp signals + `ASSERT(StateEscRespHiCheck_A, state_q == CheckEscRespLo && esc_tx_o.esc_p && !integ_fail_o |=> + state_q == CheckEscRespHi) + `ASSERT(StateEscRespLoCheck_A, state_q == CheckEscRespHi && esc_tx_o.esc_p && !integ_fail_o |=> + state_q == CheckEscRespLo) + `ASSERT(StateEscRespHiBackCheck_A, state_q == CheckEscRespHi |-> $past(esc_tx_o.esc_p)) + `ASSERT(StateEscRespLoBackCheck_A, state_q == CheckEscRespLo |-> $past(esc_tx_o.esc_p)) + // check that escalation signal is at least 2 cycles high + `ASSERT(EscCheck_A, esc_en_i |-> esc_tx_o.esc_p [*2] ) + // escalation / ping collision + `ASSERT(EscPingCheck_A, esc_en_i && ping_en_i |-> ping_ok_o, clk_i, !rst_ni || integ_fail_o) + // check that ping request results in only a single cycle pulse + `ASSERT(PingCheck_A, ##1 $rose(ping_en_i) |-> esc_tx_o.esc_p ##1 !esc_tx_o.esc_p , clk_i, + !rst_ni || esc_en_i || integ_fail_o) + +endmodule : prim_esc_sender diff --git a/vendor/lowrisc_ip/prim/rtl/prim_fifo_async.sv b/vendor/lowrisc_ip/prim/rtl/prim_fifo_async.sv new file mode 100644 index 0000000000..616cba9f89 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_fifo_async.sv @@ -0,0 +1,208 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic asynchronous fifo for use in a variety of devices. + +`include "prim_assert.sv" + +module prim_fifo_async #( + parameter int unsigned Width = 16, + parameter int unsigned Depth = 3, + localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth] +) ( + // write port + input clk_wr_i, + input rst_wr_ni, + input wvalid, + output wready, + input [Width-1:0] wdata, + output [DepthW-1:0] wdepth, + + // read port + input clk_rd_i, + input rst_rd_ni, + output rvalid, + input rready, + output [Width-1:0] rdata, + output [DepthW-1:0] rdepth +); + + `ASSERT_INIT(paramCheckDepth, Depth >= 3) + + localparam int unsigned PTRV_W = $clog2(Depth); + localparam logic [PTRV_W-1:0] DepthMinus1 = PTRV_W'(Depth - 1); + localparam int unsigned PTR_WIDTH = PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr; + logic [PTR_WIDTH-1:0] fifo_wptr_sync_combi, fifo_rptr_sync; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_sync, fifo_rptr_gray_sync; + logic [PTR_WIDTH-1:0] fifo_wptr_gray, fifo_rptr_gray; + logic fifo_incr_wptr, fifo_incr_rptr, empty; + + logic full_wclk, full_rclk; + + assign wready = !full_wclk; + assign rvalid = !empty; + + // create the write and read pointers + + assign fifo_incr_wptr = wvalid & wready; + assign fifo_incr_rptr = rvalid & rready; + + /////////////////// + // write pointer // + /////////////////// + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + + // gray-coded version + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_wptr_gray <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_wptr_gray <= dec2gray({~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}); + end else begin + fifo_wptr_gray <= dec2gray(fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}); + end + end + + prim_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d (fifo_wptr_gray), + .q (fifo_wptr_gray_sync)); + + assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync); + + ////////////////// + // read pointer // + ////////////////// + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) + if (!rst_rd_ni) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + + // gray-coded version + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) + if (!rst_rd_ni) begin + fifo_rptr_gray <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_rptr_gray <= dec2gray({~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}); + end else begin + fifo_rptr_gray <= dec2gray(fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}); + end + end + + prim_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d (fifo_rptr_gray), + .q (fifo_rptr_gray_sync)); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_rptr_sync <= {PTR_WIDTH{1'b0}}; + end else begin + fifo_rptr_sync <= gray2dec(fifo_rptr_gray_sync); + end + + ////////////////// + // empty / full // + ////////////////// + + assign full_wclk = (fifo_wptr == (fifo_rptr_sync ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + + // Current depth in the write clock side + logic wptr_msb; + logic rptr_sync_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_sync_value; + assign wptr_msb = fifo_wptr[PTR_WIDTH-1]; + assign rptr_sync_msb = fifo_rptr_sync[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr[0+:PTRV_W]; + assign rptr_sync_value = fifo_rptr_sync[0+:PTRV_W]; + assign wdepth = (full_wclk) ? DepthW'(Depth) : + (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) : + (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ; + + // Same again in the read clock side + assign empty = (fifo_wptr_sync_combi == fifo_rptr); + logic rptr_msb; + logic wptr_sync_msb; + logic [PTRV_W-1:0] rptr_value; + logic [PTRV_W-1:0] wptr_sync_value; + assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr[PTR_WIDTH-1]; + assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W]; + assign rptr_value = fifo_rptr[0+:PTRV_W]; + assign rdepth = (full_rclk) ? DepthW'(Depth) : + (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ; + + ///////////// + // storage // + ///////////// + + logic [Width-1:0] storage [Depth]; + + always_ff @(posedge clk_wr_i) + if (fifo_incr_wptr) begin + storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata; + end + + assign rdata = storage[fifo_rptr[PTR_WIDTH-2:0]]; + + // gray code conversion functions. algorithm walks up from 0..N-1 + // then flips the upper bit and walks down from N-1 to 0. + + function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval); + logic [PTR_WIDTH-1:0] decval_sub; + logic [PTR_WIDTH-2:0] decval_in; + logic unused_decval_msb; + + decval_sub = Depth - {1'b0,decval[PTR_WIDTH-2:0]} - 1'b1; + + {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval; + // Was done in two assigns for low bits and top bit + // but that generates a (bogus) verilator warning, so do in one assign + dec2gray = {decval[PTR_WIDTH-1], + {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]}; + endfunction + + function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval); + logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2]; + for (int i = PTR_WIDTH-3; i >= 0; i--) + dec_tmp[i] = dec_tmp[i+1]^grayval[i]; + {unused_decsub_msb, dec_tmp_sub} = Depth - {1'b0,dec_tmp} - 1'b1; + if (grayval[PTR_WIDTH-1]) + gray2dec = {1'b1,dec_tmp_sub}; + else + gray2dec = {1'b0,dec_tmp}; + endfunction + + // TODO: assertions on full, empty, gray transitions + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv b/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv new file mode 100644 index 0000000000..e2199db623 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_fifo_sync.sv @@ -0,0 +1,155 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic synchronous fifo for use in a variety of devices. + +`include "prim_assert.sv" + +module prim_fifo_sync #( + parameter int unsigned Width = 16, + parameter bit Pass = 1'b1, // if == 1 allow requests to pass through empty FIFO + parameter int unsigned Depth = 4, + // derived parameter + localparam int unsigned DepthWNorm = $clog2(Depth+1), + localparam int unsigned DepthW = (DepthWNorm == 0) ? 1 : DepthWNorm +) ( + input clk_i, + input rst_ni, + // synchronous clear / flush port + input clr_i, + // write port + input wvalid, + output wready, + input [Width-1:0] wdata, + // read port + output rvalid, + input rready, + output [Width-1:0] rdata, + // occupancy + output [DepthW-1:0] depth +); + + // FIFO is in complete passthrough mode + if (Depth == 0) begin : gen_passthru_fifo + `ASSERT_INIT(paramCheckPass, Pass == 1) + + assign depth = 1'b0; //output is meaningless + + // devie facing + assign rvalid = wvalid; + assign rdata = wdata; + + // host facing + assign wready = rready; + + // this avoids lint warnings + logic unused_clr; + assign unused_clr = clr_i; + + // Normal FIFO construction + end else begin : gen_normal_fifo + + // consider Depth == 1 case when $clog2(1) == 0 + localparam int unsigned PTRV_W = $clog2(Depth) + ~|$clog2(Depth); + localparam int unsigned PTR_WIDTH = PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr; + logic fifo_incr_wptr, fifo_incr_rptr, fifo_empty; + + // create the write and read pointers + logic full, empty; + logic wptr_msb; + logic rptr_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_value; + + assign wptr_msb = fifo_wptr[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr[0+:PTRV_W]; + assign rptr_value = fifo_rptr[0+:PTRV_W]; + assign depth = (full) ? DepthW'(Depth) : + (wptr_msb == rptr_msb) ? DepthW'(wptr_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_value)) ; + + assign fifo_incr_wptr = wvalid & wready; + assign fifo_incr_rptr = rvalid & rready; + + assign wready = ~full; + assign rvalid = ~empty; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (clr_i) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == (Depth-1)) begin + fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (clr_i) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == (Depth-1)) begin + fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + end + + assign full = (fifo_wptr == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + assign fifo_empty = (fifo_wptr == fifo_rptr); + + + // the generate blocks below are needed to avoid lint errors due to array indexing + // in the where the fifo only has one storage element + logic [Depth-1:0][Width-1:0] storage; + logic [Width-1:0] storage_rdata; + if (Depth == 1) begin : gen_depth_eq1 + assign storage_rdata = storage[0]; + + always_ff @(posedge clk_i) + if (fifo_incr_wptr) begin + storage[0] <= wdata; + end + // fifo with more than one storage element + end else begin : gen_depth_gt1 + assign storage_rdata = storage[fifo_rptr[PTR_WIDTH-2:0]]; + + always_ff @(posedge clk_i) + if (fifo_incr_wptr) begin + storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata; + end + end + + if (Pass == 1'b1) begin : gen_pass + assign rdata = (fifo_empty && wvalid) ? wdata : storage_rdata; + assign empty = fifo_empty & ~wvalid; + end else begin : gen_nopass + assign rdata = storage_rdata; + assign empty = fifo_empty; + end + + `ASSERT(depthShallNotExceedParamDepth, !empty |-> depth <= DepthW'(Depth)) + end // block: gen_normal_fifo + + + ////////////////////// + // Known Assertions // + ////////////////////// + + `ASSERT(DataKnown_A, rvalid |-> !$isunknown(rdata)) + `ASSERT_KNOWN(DepthKnown_A, depth) + `ASSERT_KNOWN(RvalidKnown_A, rvalid) + `ASSERT_KNOWN(WreadyKnown_A, wready) + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_filter.sv b/vendor/lowrisc_ip/prim/rtl/prim_filter.sv new file mode 100644 index 0000000000..e582cce1e1 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_filter.sv @@ -0,0 +1,49 @@ +// Copyright 2018 lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Indentifier: Apache-2.0 +// +// Primitive input filter, with enable. Configurable number of cycles. +// +// when in reset, stored vector is zero +// when enable is false, output is input +// when enable is true, output is stored value, +// new input must be opposite value from stored value for +// #Cycles before switching to new value. + +module prim_filter #(parameter int Cycles = 4) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + output filter_o +); + + logic [Cycles-1:0] stored_vector_q, stored_vector_d; + logic stored_value_q, update_stored_value; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_value_q <= 1'b0; + end else if (update_stored_value) begin + stored_value_q <= filter_i; + end + end + + assign stored_vector_d = {stored_vector_q[Cycles-2:0],filter_i}; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_vector_q <= {Cycles{1'b0}}; + end else begin + stored_vector_q <= stored_vector_d; + end + end + + assign update_stored_value = + (stored_vector_d == {Cycles{1'b0}}) | + (stored_vector_d == {Cycles{1'b1}}); + + assign filter_o = enable_i ? stored_value_q : filter_i; + +endmodule + diff --git a/vendor/lowrisc_ip/prim/rtl/prim_filter_ctr.sv b/vendor/lowrisc_ip/prim/rtl/prim_filter_ctr.sv new file mode 100644 index 0000000000..941ad2707f --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_filter_ctr.sv @@ -0,0 +1,63 @@ +// Copyright 2018 lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Indentifier: Apache-2.0 +// +// Primitive counter-based input filter, with enable. +// Configurable number of cycles. Cheaper version of filter for +// large values of #Cycles +// +// when in reset, stored value is zero +// when enable is false, output is input +// when enable is true, output is stored value, +// new input must be opposite value from stored value for +// #Cycles before switching to new value. + +module prim_filter_ctr #(parameter int unsigned Cycles = 4) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + output filter_o +); + + localparam int unsigned CTR_WIDTH = $clog2(Cycles); + localparam logic [CTR_WIDTH-1:0] CYCLESM1 = (CTR_WIDTH)'(Cycles-1); + + logic [CTR_WIDTH-1:0] diff_ctr_q, diff_ctr_d; + logic filter_q, stored_value_q, update_stored_value; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + filter_q <= 1'b0; + end else begin + filter_q <= filter_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_value_q <= 1'b0; + end else if (update_stored_value) begin + stored_value_q <= filter_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + diff_ctr_q <= {CTR_WIDTH{1'b0}}; + end else begin + diff_ctr_q <= diff_ctr_d; + end + end + + // always look for differences, even if not filter enabled + assign diff_ctr_d = + (filter_i != filter_q) ? '0 : // restart + (diff_ctr_q == CYCLESM1) ? CYCLESM1 : // saturate + (diff_ctr_q + 1'b1); // count up + assign update_stored_value = (diff_ctr_d == CYCLESM1); + + assign filter_o = enable_i ? stored_value_q : filter_i; + +endmodule + diff --git a/vendor/lowrisc_ip/prim/rtl/prim_flop_2sync.sv b/vendor/lowrisc_ip/prim/rtl/prim_flop_2sync.sv new file mode 100644 index 0000000000..519ce6bfa4 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_flop_2sync.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic double-synchronizer flop + +module prim_flop_2sync #( + parameter int Width = 16, + parameter bit ResetValue = 0 +) ( + input clk_i, // receive clock + input rst_ni, + input [Width-1:0] d, + output logic [Width-1:0] q +); + + logic [Width-1:0] intq; + + always_ff @(posedge clk_i or negedge rst_ni) + if (!rst_ni) begin + intq <= {Width{ResetValue}}; + q <= {Width{ResetValue}}; + end else begin + intq <= d; + q <= intq; + end + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_gate_gen.sv b/vendor/lowrisc_ip/prim/rtl/prim_gate_gen.sv new file mode 100644 index 0000000000..e97c7d50ad --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_gate_gen.sv @@ -0,0 +1,123 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Simple parameterizable gate generator. Used to fill up the netlist +// with gates that cannot be optimized away. +// +// The module leverages 4bit SBoxes from the PRINCE cipher, and interleaves +// them with registers, resulting in a split of around 50/50 between logic and +// sequential cells. +// +// This generator has been tested with 32bit wide data, and produces +// the following results: +// +// if valid_i constantly set to 1'b1: +// -------------+-----------+---------- +// requested GE | actual GE | GE error +// -------------+-----------+---------- +// 500 | 679 | 179 +// 1000 | 1018 | 18 +// 1500 | 1696 | 196 +// 2500 | 2714 | 214 +// 5000 | 5210 | 210 +// 7500 | 7456 | -44 +// 10000 | 10015 | 15 +// 15000 | 15191 | 191 +// 25000 | 25228 | 228 +// 50000 | 50485 | 485 +// +// otherwise, with clock gating enabled: +// -------------+-----------+---------- +// requested GE | actual GE | GE error +// -------------+-----------+---------- +// 500 | 696 | 196 +// 1000 | 1043 | 43 +// 1500 | 1737 | 237 +// 2500 | 2779 | 279 +// 5000 | 5340 | 340 +// 7500 | 7634 | 134 +// 10000 | 10284 | 284 +// 15000 | 15585 | 585 +// 25000 | 25855 | 855 +// 50000 | 51732 | 1732 +// +// Note that the generator is not very accurate for smaller gate counts due +// to the generate loop granularity. Hence, do not use for fever than 500 GE. + + +module prim_gate_gen #( + parameter int DataWidth = 32, + parameter int NumGates = 1000 +) ( + input clk_i, + input rst_ni, + + input valid_i, + input [DataWidth-1:0] data_i, + output logic [DataWidth-1:0] data_o, + output valid_o +); + + ///////////////////////////////////// + // Local parameters and assertions // + ///////////////////////////////////// + + // technology specific tuning, do not modify. + // an inner round is comprised of a 2bit rotation, followed by a 4bit SBox Layer. + localparam int NumInnerRounds = 2; + localparam int GatesPerRound = DataWidth * 10; + // an outer round consists of NumInnerRounds, followed by a register. + localparam int NumOuterRounds = (NumGates + GatesPerRound / 2) / GatesPerRound; + + // do not use for fewer than 500 GE + `ASSERT(MinimumNumGates_A, NumGates >= 500) + `ASSERT(DataMustBeMultipleOfFour_A, DataWidth % 4 == 0) + + ///////////////////// + // Generator Loops // + ///////////////////// + + logic [NumOuterRounds-1:0][DataWidth-1:0] regs_d, regs_q; + logic [NumOuterRounds-1:0] valid_d, valid_q; + + for (genvar k = 0; k < NumOuterRounds; k++) begin : gen_outer_round + + logic [NumInnerRounds:0][DataWidth-1:0] inner_data; + + if (k==0) begin : gen_first + assign inner_data[0] = data_i; + assign valid_d[0] = valid_i; + end else begin : gen_others + assign inner_data[0] = regs_q[k-1]; + assign valid_d[k] = valid_q[k-1]; + end + + for (genvar l = 0; l < NumInnerRounds; l++) begin : gen_inner + // 2bit rotation + sbox layer + assign inner_data[l+1] = prim_cipher_pkg::sbox4_32bit({inner_data[l][1:0], + inner_data[l][DataWidth-1:2]}, + prim_cipher_pkg::PRINCE_SBOX4); + end + + assign regs_d[k] = inner_data[NumInnerRounds]; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + regs_q <= '0; + valid_q <= '0; + end else begin + valid_q <= valid_d; + for (int k = 0; k < NumOuterRounds; k++) begin + if (valid_d[k]) begin + regs_q[k] <= regs_d[k]; + end + end + end + end + + assign data_o = regs_q[NumOuterRounds-1]; + assign valid_o = valid_q[NumOuterRounds-1]; + +endmodule : prim_gate_gen diff --git a/vendor/lowrisc_ip/prim/rtl/prim_gf_mult.sv b/vendor/lowrisc_ip/prim/rtl/prim_gf_mult.sv new file mode 100644 index 0000000000..61e75949c1 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_gf_mult.sv @@ -0,0 +1,171 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module performs a the multiplication of two operands in Galois field GF(2^Width) modulo the +// provided irreducible polynomial using a parallel Mastrovito multipler [3]. To cut long paths +// potentially occurring for large data widths, the implementation provides a parameter +// StagesPerCycle to decompose the multiplication into Width/StagesPerCycle iterative steps +// (Digit-Serial/Parallel Multiplier [4]). +// +// Note that this module is not pipelined and produces an output sample every Width/StagesPerCycle +// cycles. +// +// References: +// +// [1] Patel, "Parallel Multiplier Designs for the Galois/Counter Mode of Operation", +// https://pdfs.semanticscholar.org/1246/a9ad98dc0421ccfc945e6529c886f23e848d.pdf +// [2] Wagner, "The Laws of Cryptography: The Finite Field GF(2^8)", +// http://www.cs.utsa.edu/~wagner/laws/FFM.html +// +// [3]: Mastrovito, "VLSI Designs for Multiplication over Finite Fields GF(2^m)", +// https://link.springer.com/chapter/10.1007/3-540-51083-4_67 +// [4]: Song et al., "Efficient Finite Field Serial/Parallel Multiplication", +// https://ieeexplore.ieee.org/document/542803 + + +`include "prim_assert.sv" + +module prim_gf_mult #( + parameter int Width = 32, + parameter int StagesPerCycle = Width, + + // The field-generating, irreducible polynomial of degree Width. + // Can for example be a Conway polynomial, see + // http://www.math.rwth-aachen.de/~Frank.Luebeck/data/ConwayPol/CP2.html + // For Width = 33, the Conway polynomial hast bits 32, 15, 9, 7, 4, 3, 0 set to one. + parameter logic[Width-1:0] IPoly = 1'b1 << 15 | + 1'b1 << 9 | + 1'b1 << 7 | + 1'b1 << 4 | + 1'b1 << 3 | + 1'b1 << 0 +) ( + input clk_i, + input rst_ni, + input req_i, + input [Width-1:0] operand_a_i, + input [Width-1:0] operand_b_i, + output logic ack_o, + output logic [Width-1:0] prod_o +); + + `ASSERT_INIT(IntegerLoops_A, (Width % StagesPerCycle) == 0) + `ASSERT_INIT(StagePow2_A, $onehot(StagesPerCycle)) + + localparam int Loops = Width / StagesPerCycle; + localparam int CntWidth = (Loops == 1) ? 1 : $clog2(Loops); + + // reformat operand_b_i + logic [Loops-1:0][StagesPerCycle-1:0] reformat_data; + + // this slice of operand bits used during each loop + logic [StagesPerCycle-1:0] op_i_slice; + + // the matrix is made up of a series of GF(2^Width) * x + logic [StagesPerCycle-1:0][Width-1:0] matrix; + + // since the matrix generation is not done in one go, we must remember + // where it last left off + logic [Width-1:0] vector; + + // this variable tracks which loop we are currently operating + logic [CntWidth-1:0] cnt; + + // this variable tracks the first loop through the multiply + logic first; + + // intermediate prod held between loops + logic [Width-1:0] prod_q, prod_d; + + // select current slice + assign reformat_data = operand_b_i; + assign op_i_slice = reformat_data[cnt]; + + assign first = cnt == 0; + + if (StagesPerCycle == Width) begin : gen_all_combo + + assign ack_o = 1'b1; + assign cnt = '0; + assign prod_q = '0; + assign vector = '0; + + end else begin : gen_decomposed + + // multiply is done + assign ack_o = cnt == (Loops - 1); + + // advance the stage count and also advance the bit position count + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cnt <= '0; + end else if (req_i && ack_o) begin + cnt <= '0; + end else if (req_i && cnt < (Loops - 1)) begin + cnt <= cnt + 1'b1; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + prod_q <= '0; + vector <= '0; + end else if (ack_o) begin + prod_q <= '0; + vector <= '0; + end else if (req_i) begin + prod_q <= prod_d; + vector <= matrix[StagesPerCycle-1]; + end + end + end + + + assign matrix = first ? gen_matrix(operand_a_i, 1'b1) : gen_matrix(vector, 1'b0); + assign prod_d = prod_q ^ gf_mult(matrix, op_i_slice); + + // The output is not toggled until it is ready + assign prod_o = ack_o ? prod_d : operand_a_i; + + + // GF(2^Width) * x + function automatic logic [Width-1:0] gf_mult2( + logic [Width-1:0] operand + ); + logic [Width-1:0] mult_out; + mult_out = operand[Width-1] ? (operand << 1) ^ IPoly : (operand << 1); + return mult_out; + endfunction + + // Matrix generate step + function automatic logic [StagesPerCycle-1:0][Width-1:0] gen_matrix( + logic [Width-1:0] seed, + logic init + ); + logic [StagesPerCycle-1:0][Width-1:0] matrix_out; + + matrix_out[0] = init ? seed : gf_mult2(seed); + matrix_out[StagesPerCycle-1:1] = '0; + for (int i = 1; i < StagesPerCycle; i++) begin + matrix_out[i] = gf_mult2(matrix_out[i-1]); + end + return matrix_out; + endfunction + + // Galois multiply step + function automatic logic [Width-1:0] gf_mult( + logic [StagesPerCycle-1:0][Width-1:0] matrix, + logic [StagesPerCycle-1:0] operand + ); + logic [Width-1:0] mult_out; + logic [Width-1:0] add_vector; + mult_out = '0; + for (int i = 0; i < StagesPerCycle; i++) begin + add_vector = operand[i] ? matrix[i] : '0; + mult_out = mult_out ^ add_vector; + end + return mult_out; + endfunction // gf_mult + +endmodule // prim_gf_mult diff --git a/vendor/lowrisc_ip/prim/rtl/prim_intr_hw.sv b/vendor/lowrisc_ip/prim/rtl/prim_intr_hw.sv new file mode 100644 index 0000000000..950c337205 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_intr_hw.sv @@ -0,0 +1,36 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Indentifier: Apache-2.0 +// +// Primitive interrupt handler. This assumes the existence of three +// controller registers: INTR_ENABLE, INTR_STATE, INTR_TEST. +// This module can be instantiated once per interrupt field, or +// "bussified" with all fields of the interrupt vector. + +module prim_intr_hw #(parameter int unsigned Width = 1) ( + // event + input [Width-1:0] event_intr_i, + + // register interface + input [Width-1:0] reg2hw_intr_enable_q_i, + input [Width-1:0] reg2hw_intr_test_q_i, + input reg2hw_intr_test_qe_i, + input [Width-1:0] reg2hw_intr_state_q_i, + output hw2reg_intr_state_de_o, + output [Width-1:0] hw2reg_intr_state_d_o, + + // outgoing interrupt + output [Width-1:0] intr_o +); + + logic [Width-1:0] new_event; + assign new_event = + (({Width{reg2hw_intr_test_qe_i}} & reg2hw_intr_test_q_i) | event_intr_i); + assign hw2reg_intr_state_de_o = |new_event; + // for scalar interrupts, this resolves to '1' with new event + // for vector interrupts, new events are OR'd in to existing interrupt state + assign hw2reg_intr_state_d_o = new_event | reg2hw_intr_state_q_i; + assign intr_o = reg2hw_intr_state_q_i & reg2hw_intr_enable_q_i; + +endmodule + diff --git a/vendor/lowrisc_ip/prim/rtl/prim_keccak.sv b/vendor/lowrisc_ip/prim/rtl/prim_keccak.sv new file mode 100644 index 0000000000..7c3a526ea7 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_keccak.sv @@ -0,0 +1,295 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// prim_keccak is single round permutation module + +module prim_keccak #( + parameter int Width = 1600, // b= {25, 50, 100, 200, 400, 800, 1600} + + // Derived + localparam int W = Width/25, + localparam int L = $clog2(W), + localparam int MaxRound = 12 + 2*L, // Keccak-f only + localparam int RndW = $clog2(MaxRound) // Representing up to MaxRound-1 +) ( + input [RndW-1:0] rnd_i, // Current Round + input [Width-1:0] s_i, + output logic [Width-1:0] s_o +); + /////////// + // Types // + /////////// + // x y z + typedef logic [4:0][4:0][W-1:0] box_t; // (x,y,z) state + typedef logic [W-1:0] lane_t; // (z) + typedef logic [4:0] [W-1:0] plane_t; // (x,z) + typedef logic [4:0][4:0] slice_t; // (x,y) + typedef logic [4:0][W-1:0] sheet_t; // (y,z) identical to plane_t + typedef logic [4:0] row_t; // (x) + typedef logic [4:0] col_t; // (y) identical to row_t + + ////////////// + // Keccak_f // + ////////////// + box_t state_in, keccak_f; + box_t theta_data, rho_data, pi_data, chi_data, iota_data; + assign state_in = bitarray_to_box(s_i); + assign theta_data = theta(state_in); + // Commented out rho function as vcs complains z-Offset%W isn't constant + //assign rho_data = rho(theta_data); + assign pi_data = pi(rho_data); + assign chi_data = chi(pi_data); + assign iota_data = iota(chi_data, rnd_i); + assign keccak_f = iota_data; + assign s_o = box_to_bitarray(keccak_f); + + // Rho ====================================================================== + // As RhoOffset[x][y] is considered as variable int in VCS, + // it is replaced with generate statement. + localparam int RhoOffset [5][5] = '{ + //y 0 1 2 3 4 x + '{ 0, 36, 3, 105, 210},// 0 + '{ 1, 300, 10, 45, 66},// 1 + '{ 190, 6, 171, 15, 253},// 2 + '{ 28, 55, 153, 21, 120},// 3 + '{ 91, 276, 231, 136, 78} // 4 + }; + for (genvar x = 0 ; x < 5 ; x++) begin : gen_rho_x + for (genvar y = 0 ; y < 5 ; y++) begin : gen_rho_y + localparam int Offset = RhoOffset[x][y]%W; + localparam int ShiftAmt = W- Offset; + if (Offset == 0) begin : gen_offset0 + assign rho_data[x][y][W-1:0] = theta_data[x][y][W-1:0]; + end else begin : gen_others + assign rho_data[x][y][W-1:0] = {theta_data[x][y][0+:ShiftAmt], + theta_data[x][y][ShiftAmt+:Offset]}; + end + end + end + + //////////////// + // Assertions // + //////////////// + + `ASSERT_INIT(ValidWidth_A, Width inside {25, 50, 100, 200, 400, 800, 1600}) + `ASSERT_INIT(ValidW_A, W inside {1, 2, 4, 8, 16, 32, 64}) + `ASSERT_INIT(ValidL_A, L inside {0, 1, 2, 3, 4, 5, 6}) + `ASSERT_INIT(ValidRound_A, MaxRound <= 24) // Keccak-f only + + /////////////// + // Functions // + /////////////// + + // Convert bitarray to 3D box + // Please take a look at FIPS PUB 202 + // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf + // > For all triples (x,y,z) such that 0<=x<5, 0<=y<5, and 0<=z A[x,y,z]=S[w(5y+x)+z] + function automatic box_t bitarray_to_box(logic [Width-1:0] s_in); + automatic box_t box; + for (int y = 0 ; y < 5 ; y++) begin + for (int x = 0 ; x < 5 ; x++) begin + for (int z = 0 ; z < W ; z++) begin + box[x][y][z] = s_in[W*(5*y+x) + z]; + end + end + end + return box; + endfunction : bitarray_to_box + + // Convert 3D cube to bitarray + function automatic logic [Width-1:0] box_to_bitarray(box_t state); + automatic logic [Width-1:0] bitarray; + for (int y = 0 ; y < 5 ; y++) begin + for (int x = 0 ; x < 5 ; x++) begin + for (int z = 0 ; z < W ; z++) begin + bitarray[W*(5*y+x)+z] = state[x][y][z]; + end + end + end + return bitarray; + endfunction : box_to_bitarray + + // Step Mapping ============================================================= + // theta(θ) + // XOR each bit in the state with the parity of two columns + // C[x,z] = A[x,0,z] ^ A[x,1,z] ^ A[x,2,z] ^ A[x,3,z] ^ A[x,4,z] + // D[x,z] = C[x-1,z] ^ C[x+1,z-1] + // theta = A[x,y,z] ^ D[x,z] + function automatic box_t theta(box_t state); + plane_t c; + plane_t d; + box_t result; + for (int x = 0 ; x < 5 ; x++) begin + for (int z = 0 ; z < W ; z++) begin + c[x][z] = state[x][0][z] ^ state[x][1][z] + ^ state[x][2][z] ^ state[x][3][z] ^ state[x][4][z]; + end + end + for (int x = 0 ; x < 5 ; x++) begin + int index_x1, index_x2; + index_x1 = (x == 0) ? 4 : x-1; // (x-1)%5 + index_x2 = (x == 4) ? 0 : x+1; // (x+1)%5 + for (int z = 0 ; z < W ; z++) begin + int index_z; + index_z = (z == 0) ? W-1 : z-1; // (z+1)%W + d[x][z] = c[index_x1][z] ^ c[index_x2][index_z]; + end + end + for (int x = 0 ; x < 5 ; x++) begin + for (int y = 0 ; y < 5 ; y++) begin + for (int z = 0 ; z < W ; z++) begin + result[x][y][z] = state[x][y][z] ^ d[x][z]; + end + end + end + return result; + endfunction : theta + + // rho + + // Commented out entire rho function due to VCS elaboration error. + // (z-RhoOffset[x][y]%W) isn't considered as a constant in VCS. + // Even changing it to W-RhoOffset[x][y]%W and assign to ShiftAmt + // creates same error. + + // Offset : Look at Table 2 in FIPS PUB 202 + //localparam int RhoOffset [5][5] = '{ + // //y 0 1 2 3 4 x + // '{ 0, 36, 3, 105, 210},// 0 + // '{ 1, 300, 10, 45, 66},// 1 + // '{ 190, 6, 171, 15, 253},// 2 + // '{ 28, 55, 153, 21, 120},// 3 + // '{ 91, 276, 231, 136, 78} // 4 + //}; + + // rotate bits of each lane by offset + // 1. rho[0,0,z] = A[0,0,z] + // 2. Offset swap + // a. (x,y) := (1,0) + // b. for t [0..23] + // i. rho[x,y,z] = A[x,y,z-(t+1)(t+2)/2] + // ii. (x,y) = (y, (2x+3y)) + //function automatic box_t rho(box_t state); + // box_t result; + // for (int x = 0 ; x < 5 ; x++) begin + // for (int y = 0 ; y < 5 ; y++) begin + // for (int z = 0 ; z < W ; z++) begin + // automatic int index_z; + // index_z = (z-RhoOffset[x][y])%W; + // result[x][y][z] = state[x][y][(z-RhoOffset[x][y])%W]; + // end + // end + // end + // return result; + //endfunction : rho + + // pi + // rearrange the position of lanes + // pi[x,y,z] = state[(x+3y),x,z] + localparam int PiRotate [5][5] = '{ + //y 0 1 2 3 4 x + '{ 0, 3, 1, 4, 2},// 0 + '{ 1, 4, 2, 0, 3},// 1 + '{ 2, 0, 3, 1, 4},// 2 + '{ 3, 1, 4, 2, 0},// 3 + '{ 4, 2, 0, 3, 1} // 4 + }; + function automatic box_t pi(box_t state); + box_t result; + for (int x = 0 ; x < 5 ; x++) begin + for (int y = 0 ; y < 5 ; y++) begin + int index_x; + result[x][y][W-1:0] = state[PiRotate[x][y]][x][W-1:0]; + end + end + return result; + endfunction : pi + + // chi + // chi[x,y,z] = state[x,y,z] ^ ((state[x+1,y,z] ^ 1) & state[x+2,y,z]) + function automatic box_t chi(box_t state); + box_t result; + for (int x = 0 ; x < 5 ; x++) begin + int index_x1, index_x2; + index_x1 = (x == 4) ? 0 : x+1; + index_x2 = (x >= 3) ? x-3 : x+2; + for (int y = 0 ; y < 5 ; y++) begin + for (int z = 0 ; z < W ; z++) begin + result[x][y][z] = state[x][y][z] ^ + ((~state[index_x1][y][z]) + & state[index_x2][y][z]); + end + end + end + return result; + endfunction : chi + + // iota + // XOR (x,y) = (0,0) with round constant + + // RC parameter: Precomputed by util/keccak_rc.py. Only up-to 0..L-1 is used + // RC = '0 + // RC[2**j-1] = rc(j+7*rnd) + // rc(t) = + // 1. t%255 == 0 -> 1 + // 2. R[0:7] = 'b10000000 + // 3. for i = [1..t%255] + // a. R = 0 || R + // b. R[0] = R[0] ^ R[8] + // c. R[4] = R[4] ^ R[8] + // d. R[5] = R[5] ^ R[8] + // e. R[6] = R[6] ^ R[8] + // f. R = R[0:7] + // 4. return R[0] + // RC has L = [0..6] + // for lower L case, only chopping lower part of 64bit RC is sufficient. + localparam logic [63:0] RC [24] = '{ + 64'h 0000_0000_0000_0001, // Round 0 + 64'h 0000_0000_0000_8082, // Round 1 + 64'h 8000_0000_0000_808A, // Round 2 + 64'h 8000_0000_8000_8000, // Round 3 + 64'h 0000_0000_0000_808B, // Round 4 + 64'h 0000_0000_8000_0001, // Round 5 + 64'h 8000_0000_8000_8081, // Round 6 + 64'h 8000_0000_0000_8009, // Round 7 + 64'h 0000_0000_0000_008A, // Round 8 + 64'h 0000_0000_0000_0088, // Round 9 + 64'h 0000_0000_8000_8009, // Round 10 + 64'h 0000_0000_8000_000A, // Round 11 + 64'h 0000_0000_8000_808B, // Round 12 + 64'h 8000_0000_0000_008B, // Round 13 + 64'h 8000_0000_0000_8089, // Round 14 + 64'h 8000_0000_0000_8003, // Round 15 + 64'h 8000_0000_0000_8002, // Round 16 + 64'h 8000_0000_0000_0080, // Round 17 + 64'h 0000_0000_0000_800A, // Round 18 + 64'h 8000_0000_8000_000A, // Round 19 + 64'h 8000_0000_8000_8081, // Round 20 + 64'h 8000_0000_0000_8080, // Round 21 + 64'h 0000_0000_8000_0001, // Round 22 + 64'h 8000_0000_8000_8008 // Round 23 + }; + + // iota: XOR with RC for (x,y) = (0,0) + function automatic box_t iota(box_t state, logic [RndW-1:0] rnd); + box_t result; + result = state; + result[0][0][W-1:0] = state[0][0][W-1:0] ^ RC[rnd][W-1:0]; + + return result; + endfunction : iota + + // Round function : Rnd(A,i_r) + // Not used due to rho function issue described above. + + //function automatic box_t keccak_rnd(box_t state, logic [RndW-1:0] rnd); + // box_t keccak_state; + // keccak_state = iota(chi(pi(rho(theta(state)))), rnd); + // + // return keccak_state; + //endfunction : keccak_rnd + +endmodule + diff --git a/shared/rtl/prim_lfsr.sv b/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv similarity index 98% rename from shared/rtl/prim_lfsr.sv rename to vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv index 3c162bee4c..68481bc575 100644 --- a/shared/rtl/prim_lfsr.sv +++ b/vendor/lowrisc_ip/prim/rtl/prim_lfsr.sv @@ -437,7 +437,9 @@ module prim_lfsr #( // this can be disabled if unused in order to not distort coverage if (ExtSeedSVA) begin : gen_ext_seed_sva // check that external seed is correctly loaded into the state - `ASSERT(ExtDefaultSeedInputCheck_A, seed_en_i |=> lfsr_q == $past(seed_i)) + // rst_ni is used directly as part of the pre-condition since the usage of rst_ni + // in disable_iff is unsampled. See #1985 for more details + `ASSERT(ExtDefaultSeedInputCheck_A, (seed_en_i && rst_ni) |=> lfsr_q == $past(seed_i)) end // if the external seed mechanism is not used, diff --git a/vendor/lowrisc_ip/prim/rtl/prim_packer.sv b/vendor/lowrisc_ip/prim/rtl/prim_packer.sv new file mode 100644 index 0000000000..43afced08c --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_packer.sv @@ -0,0 +1,255 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Combine InW data and write to OutW data if packed to full word or stop signal + +`include "prim_assert.sv" + +module prim_packer #( + parameter int InW = 32, + parameter int OutW = 32 +) ( + input clk_i , + input rst_ni, + + input valid_i, + input [InW-1:0] data_i, + input [InW-1:0] mask_i, + output ready_o, + + output logic valid_o, + output logic [OutW-1:0] data_o, + output logic [OutW-1:0] mask_o, + input ready_i, + + input flush_i, // If 1, send out remnant and clear state + output logic flush_done_o +); + + localparam int Width = InW + OutW; + localparam int PtrW = $clog2(Width+1); + localparam int MaxW = (InW > OutW) ? InW : OutW; + localparam int IdxW = $clog2(InW) + ~|$clog2(InW); + + logic valid_next, ready_next; + logic [MaxW-1:0] stored_data, stored_mask; + logic [Width-1:0] concat_data, concat_mask; + logic [Width-1:0] shiftl_data, shiftl_mask; + + logic [PtrW-1:0] pos, pos_next; // Current write position + logic [IdxW-1:0] lod_idx; // result of Leading One Detector + logic [$clog2(InW+1)-1:0] inmask_ones; // Counting Ones for mask_i + + logic ack_in, ack_out; + + logic flush_ready; // flush_i is pulse, so only when the output is ready flush_ready assets + + // Computing next position + always_comb begin + // counting mask_i ones + inmask_ones = '0; + for (int i = 0 ; i < InW ; i++) begin + inmask_ones = inmask_ones + mask_i[i]; + end + end + + assign pos_next = (valid_i) ? pos + PtrW'(inmask_ones) : pos; // pos always stays (% OutW) + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pos <= '0; + end else if (flush_ready) begin + pos <= '0; + end else if (ack_out) begin + `ASSERT_I(pos_next_gte_outw_p, pos_next >= OutW) + pos <= pos_next - OutW; + end else if (ack_in) begin + pos <= pos_next; + end + end + + // Leading one detector for mask_i + always_comb begin + lod_idx = 0; + for (int i = InW-1; i >= 0 ; i--) begin + if (mask_i[i] == 1'b1) begin + lod_idx = i; + end + end + end + + assign ack_in = valid_i & ready_o; + assign ack_out = valid_o & ready_i; + + // Data process + assign shiftl_data = (valid_i) ? Width'(data_i >> lod_idx) << pos : '0; + assign shiftl_mask = (valid_i) ? Width'(mask_i >> lod_idx) << pos : '0; + assign concat_data = {{(Width-MaxW){1'b0}}, stored_data & stored_mask} | + (shiftl_data & shiftl_mask); + assign concat_mask = {{(Width-MaxW){1'b0}}, stored_mask} | shiftl_mask; + + logic [MaxW-1:0] stored_data_next, stored_mask_next; + + if (InW >= OutW) begin : gen_stored_in + assign stored_data_next = concat_data[OutW+:InW]; + assign stored_mask_next = concat_mask[OutW+:InW]; + end else begin : gen_stored_out + assign stored_data_next = {{(OutW-InW){1'b0}}, concat_data[OutW+:InW]}; + assign stored_mask_next = {{(OutW-InW){1'b0}}, concat_mask[OutW+:InW]}; + end + + // Store the data temporary if it doesn't exceed OutW + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_data <= '0; + stored_mask <= '0; + end else if (flush_ready) begin + stored_data <= '0; + stored_mask <= '0; + end else if (ack_out) begin + stored_data <= stored_data_next; + stored_mask <= stored_mask_next; + end else if (ack_in) begin + // When the requested size is smaller than OutW or output isn't ready + // Assume when output isn't ready, the module holds the input request + stored_data <= concat_data[MaxW-1:0]; + stored_mask <= concat_mask[MaxW-1:0]; + end + end + + // flush_ready handling + typedef enum logic { + FlushIdle, + FlushWait + } flush_st_e; + flush_st_e flush_st, flush_st_next; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + flush_st <= FlushIdle; + end else begin + flush_st <= flush_st_next; + end + end + + always_comb begin + flush_st_next = FlushIdle; + + flush_ready = 1'b0; + + unique case (flush_st) + FlushIdle: begin + if (flush_i && !ready_i) begin + // Wait until hold released + flush_st_next = FlushWait; + + flush_ready = 1'b0; + end else if (flush_i && ready_i) begin + // Can write right away! + flush_st_next = FlushIdle; + + flush_ready = 1'b1; + end else begin + flush_st_next = FlushIdle; + end + end + + FlushWait: begin + // TODO: Add timeout and force flush + if (ready_i) begin + // Ready to write + flush_st_next = FlushIdle; + + flush_ready = 1'b1; + end else begin + // Wait ... + flush_st_next = FlushWait; + + flush_ready = 1'b0; + end + end + + default: begin + flush_st_next = FlushIdle; + + flush_ready = 1'b0; + end + endcase + end + + assign flush_done_o = flush_ready; + + assign valid_next = (pos_next >= OutW) ? 1'b 1 : flush_ready & (pos != '0); + assign ready_next = ack_out ? 1'b1 : pos_next <= MaxW; // New `we` needs to be hold. + + // Output request + assign valid_o = valid_next; + assign data_o = concat_data[OutW-1:0]; + assign mask_o = concat_mask[OutW-1:0]; + + // ready_o + assign ready_o = ready_next; + + // TODO: Implement Pipelined logic + // Need to change pos logic, mask&data calculation logic too + + ////////////////////////////////////////////// + // Assertions, Assumptions, and Coverpoints // + ////////////////////////////////////////////// + // Assumption: mask_i should be contiguous ones + // e.g: 0011100 --> OK + // 0100011 --> Not OK + `ASSUME(ContiguousOnesMask_M, + valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2) + + // Assume data pattern to reduce FPV test time + //`ASSUME_FPV(FpvDataWithin_M, + // data_i inside {'0, '1, 32'hDEAD_BEEF}, + // clk_i, !rst_ni) + + // Flush and Write Enable cannot be asserted same time + `ASSUME(ExFlushValid_M, flush_i |-> !valid_i) + + // While in flush state, new request shouldn't come + `ASSUME(ValidIDeassertedOnFlush_M, + flush_st == FlushWait |-> $stable(valid_i)) + + // If not acked, input port keeps asserting valid and data + `ASSUME(DataIStable_M, + ##1 valid_i && $past(valid_i) && !$past(ready_o) + |-> $stable(data_i) && $stable(mask_i)) + `ASSUME(ValidIPairedWithReadyO_M, + valid_i && !ready_o |=> valid_i) + + `ASSERT(FlushFollowedByDone_A, + ##1 $rose(flush_i) && !flush_done_o |-> !flush_done_o [*0:$] ##1 flush_done_o) + + // If not acked, valid_o should keep asserting + `ASSERT(ValidOPairedWidthReadyI_A, + valid_o && !ready_i |=> valid_o) + + // If input mask + stored data is greater than output width, valid should be asserted + `ASSERT(ValidOAssertedForInputGTEOutW_A, + valid_i && (($countones(mask_i) + $countones(stored_mask)) >= OutW) |-> valid_o) + + // If output port doesn't accept the data, the data should be stable + `ASSERT(DataOStableWhenPending_A, + ##1 valid_o && $past(valid_o) + && !$past(ready_i) |-> $stable(data_o)) + + // If input data & stored data are greater than OutW, remained should be stored + // TODO: Find out how the FPV time can be reduced. + //`ASSERT(ExcessiveDataStored_A, + // ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=> + // (($past(data_i) & $past(mask_i)) >> + // ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + // == stored_data, + // clk_i, !rst_ni) + `ASSERT(ExcessiveMaskStored_A, + ack_in && (($countones(mask_i) + $countones(stored_mask)) > OutW) |=> + ($past(mask_i) >> + ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + == stored_mask) + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_present.sv b/vendor/lowrisc_ip/prim/rtl/prim_present.sv new file mode 100644 index 0000000000..bf3321c1e0 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_present.sv @@ -0,0 +1,132 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module is an implementation of the encryption pass of the 64bit PRESENT +// block cipher. It is a fully unrolled combinational implementation that +// supports both key sizes specified in the paper (80bit and 128bit). Further, +// the number of rounds is fully configurable, and the primitive supports a +// 32bit block cipher flavor which is not specified in the original paper. It +// should be noted, however, that the 32bit version is **not** secure and must +// not be used in a setting where cryptographic cipher strength is required. The +// 32bit variant is only intended to be used as a lightweight data scrambling +// device. +// +// See also: prim_prince, prim_cipher_pkg +// +// References: - https://en.wikipedia.org/wiki/PRESENT +// - https://en.wikipedia.org/wiki/Prince_(cipher) +// - http://www.lightweightcrypto.org/present/present_ches2007.pdf +// - https://eprint.iacr.org/2012/529.pdf +// - https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/documents/papers/session7-maene-paper.pdf + +// TODO: this module has not been verified yet, and has only been used in +// synthesis experiments. + +module prim_present #( + parameter int DataWidth = 64, // {32, 64} + parameter int KeyWidth = 128, // {64, 80, 128} + parameter int NumRounds = 31, // > 0 + // Note that the decryption pass needs a modified key, + // to be calculated by performing NumRounds key updates + parameter bit Decrypt = 0 // 0: encrypt, 1: decrypt +) ( + input [DataWidth-1:0] data_i, + input [KeyWidth-1:0] key_i, + output logic [DataWidth-1:0] data_o, + output logic [KeyWidth-1:0] key_o +); + + ////////////// + // datapath // + ////////////// + + logic [NumRounds:0][DataWidth-1:0] data_state; + logic [NumRounds:0][KeyWidth-1:0] round_key; + + // initialize + assign data_state[0] = data_i; + assign round_key[0] = key_i; + + for (genvar k = 0; k < NumRounds; k++) begin : gen_round + logic [DataWidth-1:0] data_state_xor, data_state_sbox; + // cipher layers + assign data_state_xor = data_state[k] ^ round_key[k][KeyWidth-1 : KeyWidth-DataWidth]; + + //////////////////////////////// + // decryption pass, performs inverse permutation, sbox and keyschedule + if (Decrypt) begin : gen_dec + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = prim_cipher_pkg::perm_64bit(data_state_xor, + prim_cipher_pkg::PRESENT_PERM64_INV); + assign data_state[k+1] = prim_cipher_pkg::sbox4_64bit(data_state_sbox, + prim_cipher_pkg::PRESENT_SBOX4_INV); + // reduced 32bit variant + end else begin : gen_d32 + assign data_state_sbox = prim_cipher_pkg::perm_32bit(data_state_xor, + prim_cipher_pkg::PRESENT_PERM32_INV); + assign data_state[k+1] = prim_cipher_pkg::sbox4_32bit(data_state_sbox, + prim_cipher_pkg::PRESENT_SBOX4_INV); + end + // update round key, count goes from 1 to 31 (max) + // original 128bit key variant + if (KeyWidth == 128) begin : gen_k128 + assign round_key[k+1] = prim_cipher_pkg::present_inv_update_key128(round_key[k], + 5'(k + 1), + 5'(NumRounds)); + // original 80bit key variant + end else if (KeyWidth == 80) begin : gen_k80 + assign round_key[k+1] = prim_cipher_pkg::present_inv_update_key80(round_key[k], + 5'(k + 1), + 5'(NumRounds)); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = prim_cipher_pkg::present_inv_update_key64(round_key[k], + 5'(k + 1), + 5'(NumRounds)); + end + //////////////////////////////// + // encryption pass + end else begin : gen_enc + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = prim_cipher_pkg::sbox4_64bit(data_state_xor, + prim_cipher_pkg::PRESENT_SBOX4); + assign data_state[k+1] = prim_cipher_pkg::perm_64bit(data_state_sbox, + prim_cipher_pkg::PRESENT_PERM64); + // reduced 32bit variant + end else begin : gen_d32 + assign data_state_sbox = prim_cipher_pkg::sbox4_32bit(data_state_xor, + prim_cipher_pkg::PRESENT_SBOX4); + assign data_state[k+1] = prim_cipher_pkg::perm_32bit(data_state_sbox, + prim_cipher_pkg::PRESENT_PERM32); + end + // update round key, count goes from 1 to 31 (max) + // original 128bit key variant + if (KeyWidth == 128) begin : gen_k128 + assign round_key[k+1] = prim_cipher_pkg::present_update_key128(round_key[k], 5'(k + 1)); + // original 80bit key variant + end else if (KeyWidth == 80) begin : gen_k80 + assign round_key[k+1] = prim_cipher_pkg::present_update_key80(round_key[k], 5'(k + 1)); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = prim_cipher_pkg::present_update_key64(round_key[k], 5'(k + 1)); + end + end // gen_enc + //////////////////////////////// + end // gen_round + + // finalize + assign data_o = data_state[NumRounds] ^ round_key[NumRounds][KeyWidth-1 : KeyWidth-DataWidth]; + assign key_o = round_key[NumRounds]; + + //////////////// + // assertions // + //////////////// + + `ASSERT_INIT(SupportedWidths_A, (DataWidth == 64 && KeyWidth inside {80, 128}) || + (DataWidth == 32 && KeyWidth == 64)) + `ASSERT_INIT(SupportedNumRounds_A, NumRounds > 0 && NumRounds <= 31) + +endmodule : prim_present diff --git a/vendor/lowrisc_ip/prim/rtl/prim_prince.sv b/vendor/lowrisc_ip/prim/rtl/prim_prince.sv new file mode 100644 index 0000000000..bd5e50d121 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_prince.sv @@ -0,0 +1,181 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module is an implementation of the 64bit PRINCE block cipher. It is a +// fully unrolled combinational implementation with configurable number of +// rounds. Due to the reflective construction of this cipher, the same circuit +// can be used for encryption and decryption, as described below. Further, the +// primitive supports a 32bit block cipher flavor which is not specified in the +// original paper. It should be noted, however, that the 32bit version is +// **not** secure and must not be used in a setting where cryptographic cipher +// strength is required. The 32bit variant is only intended to be used as a +// lightweight data scrambling device. +// +// See also: prim_present, prim_cipher_pkg +// +// References: - https://en.wikipedia.org/wiki/PRESENT +// - https://en.wikipedia.org/wiki/Prince_(cipher) +// - http://www.lightweightcrypto.org/present/present_ches2007.pdf +// - https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/documents/papers/session7-maene-paper.pdf +// - https://eprint.iacr.org/2012/529.pdf +// - https://eprint.iacr.org/2015/372.pdf +// - https://eprint.iacr.org/2014/656.pdf + + +// TODO: this module has not been verified yet, and has only been used in +// synthesis experiments. + +module prim_prince #( + parameter int DataWidth = 64, + parameter int KeyWidth = 128, + // The construction is reflective. Total number of rounds is 2*NumRoundsHalf + 2 + parameter int NumRoundsHalf = 5, + // This primitive uses the new key schedule proposed in https://eprint.iacr.org/2014/656.pdf + // Setting this parameter to 1 falls back to the original key schedule. + parameter bit UseOldKeySched = 1'b0 +) ( + input [DataWidth-1:0] data_i, + input [KeyWidth-1:0] key_i, + input dec_i, // set to 1 for decryption + output logic [DataWidth-1:0] data_o +); + + /////////////////// + // key expansion // + /////////////////// + + logic [DataWidth-1:0] k0, k0_prime, k1, k0_new; + + always_comb begin : p_key_expansion + k0 = key_i[DataWidth-1:0]; + k0_prime = {k0[0], k0[DataWidth-1:2], k0[DataWidth-1] ^ k0[1]}; + k1 = key_i[2*DataWidth-1 : DataWidth]; + + // modify key for decryption + if (dec_i) begin + k0 = k0_prime; + k0_prime = key_i[DataWidth-1:0]; + k1 ^= prim_cipher_pkg::PRINCE_ALPHA_CONST[DataWidth-1:0]; + end + end + + if (UseOldKeySched) begin : gen_legacy_keyschedule + assign k0_new = k1; + end else begin : gen_new_keyschedule + // improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf + assign k0_new = k0; + end + + ////////////// + // datapath // + ////////////// + + // state variable for holding the rounds + logic [NumRoundsHalf*2+1:0][DataWidth-1:0] data_state; + + // pre-round XOR + always_comb begin : p_pre_round_xor + data_state[0] = data_i ^ k0; + data_state[0] ^= k1; + data_state[0] ^= prim_cipher_pkg::PRINCE_ROUND_CONST[0][DataWidth-1:0]; + end + + // forward pass + for (genvar k = 1; k <= NumRoundsHalf; k++) begin : gen_fwd_pass + logic [DataWidth-1:0] data_state_round; + if (DataWidth == 64) begin : gen_fwd_d64 + always_comb begin : p_fwd_d64 + data_state_round = prim_cipher_pkg::sbox4_64bit(data_state[k-1], + prim_cipher_pkg::PRINCE_SBOX4); + data_state_round = prim_cipher_pkg::prince_mult_prime_64bit(data_state_round); + data_state_round = prim_cipher_pkg::prince_shiftrows_64bit(data_state_round, + prim_cipher_pkg::PRINCE_SHIFT_ROWS64); + end + end else begin : gen_fwd_d32 + always_comb begin : p_fwd_d32 + data_state_round = prim_cipher_pkg::sbox4_32bit(data_state[k-1], + prim_cipher_pkg::PRINCE_SBOX4); + data_state_round = prim_cipher_pkg::prince_mult_prime_32bit(data_state_round); + data_state_round = prim_cipher_pkg::prince_shiftrows_32bit(data_state_round, + prim_cipher_pkg::PRINCE_SHIFT_ROWS64); + end + end + logic [DataWidth-1:0] data_state_xor; + assign data_state_xor = data_state_round ^ + prim_cipher_pkg::PRINCE_ROUND_CONST[k][DataWidth-1:0]; + // improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf + if (k % 2 == 1) assign data_state[k] = data_state_xor ^ k0_new; + else assign data_state[k] = data_state_xor ^ k1; + end + + // middle part + logic [DataWidth-1:0] data_state_middle; + if (DataWidth == 64) begin : gen_middle_d64 + always_comb begin : p_middle_d64 + data_state_middle = prim_cipher_pkg::sbox4_64bit(data_state[NumRoundsHalf], + prim_cipher_pkg::PRINCE_SBOX4); + data_state_middle = prim_cipher_pkg::prince_mult_prime_64bit(data_state_middle); + data_state_middle = prim_cipher_pkg::sbox4_64bit(data_state_middle, + prim_cipher_pkg::PRINCE_SBOX4_INV); + end + end else begin : gen_middle_d32 + always_comb begin : p_middle_d32 + data_state_middle = prim_cipher_pkg::sbox4_32bit(data_state_middle[NumRoundsHalf], + prim_cipher_pkg::PRINCE_SBOX4); + data_state_middle = prim_cipher_pkg::prince_mult_prime_32bit(data_state_middle); + data_state_middle = prim_cipher_pkg::sbox4_32bit(data_state_middle, + prim_cipher_pkg::PRINCE_SBOX4_INV); + end + end + + assign data_state[NumRoundsHalf+1] = data_state_middle; + + // backward pass + for (genvar k = 1; k <= NumRoundsHalf; k++) begin : gen_bwd_pass + logic [DataWidth-1:0] data_state_xor0, data_state_xor1; + // improved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf + if (k % 2 == 1) assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k0_new; + else assign data_state_xor0 = data_state[NumRoundsHalf+k] ^ k1; + // the construction is reflective, hence the subtraction with NumRoundsHalf + assign data_state_xor1 = data_state_xor0 ^ + prim_cipher_pkg::PRINCE_ROUND_CONST[10-NumRoundsHalf+k][DataWidth-1:0]; + + logic [DataWidth-1:0] data_state_bwd; + if (DataWidth == 64) begin : gen_bwd_d64 + always_comb begin : p_bwd_d64 + data_state_bwd = prim_cipher_pkg::prince_shiftrows_64bit(data_state_xor1, + prim_cipher_pkg::PRINCE_SHIFT_ROWS64_INV); + data_state_bwd = prim_cipher_pkg::prince_mult_prime_64bit(data_state_bwd); + data_state[NumRoundsHalf+k+1] = prim_cipher_pkg::sbox4_64bit(data_state_bwd, + prim_cipher_pkg::PRINCE_SBOX4_INV); + end + end else begin : gen_bwd_d32 + always_comb begin : p_bwd_d32 + data_state_bwd = prim_cipher_pkg::prince_shiftrows_32bit(data_state_xor1, + prim_cipher_pkg::PRINCE_SHIFT_ROWS64_INV); + data_state_bwd = prim_cipher_pkg::prince_mult_prime_32bit(data_state_bwd); + data_state[NumRoundsHalf+k+1] = prim_cipher_pkg::sbox4_32bit(data_state_bwd, + prim_cipher_pkg::PRINCE_SBOX4_INV); + end + end + end + + // post-rounds + always_comb begin : p_post_round_xor + data_o = data_state[2*NumRoundsHalf+1] ^ + prim_cipher_pkg::PRINCE_ROUND_CONST[11][DataWidth-1:0]; + data_o ^= k1; + data_o ^= k0_prime; + end + + //////////////// + // assertions // + //////////////// + + `ASSERT_INIT(SupportedWidths_A, (DataWidth == 64 && KeyWidth == 128) || + (DataWidth == 32 && KeyWidth == 64)) + `ASSERT_INIT(SupportedNumRounds_A, NumRoundsHalf > 0 && NumRoundsHalf < 6) + + +endmodule : prim_prince diff --git a/vendor/lowrisc_ip/prim/rtl/prim_pulse_sync.sv b/vendor/lowrisc_ip/prim/rtl/prim_pulse_sync.sv new file mode 100644 index 0000000000..143b0884ae --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_pulse_sync.sv @@ -0,0 +1,66 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Pulse synchronizer: synchronizes a pulse from source clock domain (clk_src) +// to destination clock domain (clk_dst). Each pulse has the length of one clock +// cycle of its respective clock domain. Consecutive pulses need to be spaced +// appropriately apart from each other depending on the clock frequency ratio +// of the two clock domains. + +module prim_pulse_sync ( + // source clock domain + input logic clk_src_i, + input logic rst_src_ni, + input logic src_pulse_i, + // destination clock domain + input logic clk_dst_i, + input logic rst_dst_ni, + output logic dst_pulse_o +); + + //////////////////////////////////////////////////////////////////////////////// + // convert src_pulse to a level signal so we can use double-flop synchronizer // + //////////////////////////////////////////////////////////////////////////////// + logic src_level; + + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_level <= 1'b0; + end else begin + src_level <= src_level ^ src_pulse_i; + end + end + + ////////////////////////////////////////////////////////// + // synchronize level signal to destination clock domain // + ////////////////////////////////////////////////////////// + logic dst_level; + + prim_flop_2sync #(.Width(1)) prim_flop_2sync ( + // source clock domain + .d (src_level), + // destination clock domain + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .q (dst_level) + ); + + //////////////////////////////////////// + // convert level signal back to pulse // + //////////////////////////////////////// + logic dst_level_q; + + // delay dst_level by 1 cycle + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_level_q <= 1'b0; + end else begin + dst_level_q <= dst_level; + end + end + + // edge detection + assign dst_pulse_o = dst_level_q ^ dst_level; + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv new file mode 100644 index 0000000000..6d5467826c --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_1p_adv.sv @@ -0,0 +1,65 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Single-Port SRAM Wrapper + +`include "prim_assert.sv" + +module prim_ram_1p_adv #( + // Parameters passed on the the SRAM primitive. + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + parameter int CfgW = 8, // WTC, RTC, etc + + localparam int Aw = $clog2(Depth) +) ( + input clk_i, + input rst_ni, + + input req_i, + input write_i, + input [Aw-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, + output logic rvalid_o, // read response (rdata_o) is valid + output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + input [CfgW-1:0] cfg_i +); + + // We will eventually use cfg_i for RTC/WTC or other memory parameters. + logic [CfgW-1:0] unused_cfg; + assign unused_cfg = cfg_i; + + prim_ram_1p #( + .Width (Width), + .Depth (Depth), + .DataBitsPerMask (DataBitsPerMask), + .MemInitFile (MemInitFile) + ) u_mem ( + .clk_i, + + .req_i, + .write_i, + .addr_i, + .wdata_i, + .wmask_i, + .rdata_o + ); + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + rvalid_o <= '0; + end else begin + rvalid_o <= req_i & ~write_i; + end + end + + assign rerror_o = 2'b0; + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv new file mode 100644 index 0000000000..4fc1b729a4 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_adv.sv @@ -0,0 +1,272 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Dual-port SRAM Wrapper +// This module to connect SRAM interface to actual SRAM interface +// At this time, it doesn't utilize ECC or any pipeline. +// This module stays to include any additional calculation logic later on. +// Instantiating SRAM is up to the top design to remove process dependency. + +// Parameter +// EnableECC: +// EnableParity: +// EnableInputPipeline: +// EnableOutputPipeline: + +`include "prim_assert.sv" + +module prim_ram_2p_adv #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int CfgW = 8, // WTC, RTC, etc + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, + parameter bit EnableParity = 0, + parameter bit EnableInputPipeline = 0, + parameter bit EnableOutputPipeline = 0, + + parameter MemT = "REGISTER", // can be "REGISTER" or "SRAM" + + localparam int Aw = $clog2(Depth) +) ( + input clk_i, + input rst_ni, + + input a_req_i, + input a_write_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + output logic [Width-1:0] a_rdata_o, + output logic a_rvalid_o, // read response (a_rdata_o) is valid + output logic [1:0] a_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + input b_req_i, + input b_write_i, + input [Aw-1:0] b_addr_i, + input [Width-1:0] b_wdata_i, + output logic [Width-1:0] b_rdata_o, + output logic b_rvalid_o, // read response (b_rdata_o) is valid + output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + input [CfgW-1:0] cfg_i +); + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? 1 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + // We will eventually use cfg_i for RTC/WTC or other memory parameters. + logic [CfgW-1:0] unused_cfg; + assign unused_cfg = cfg_i; + + logic a_req_q, a_req_d ; + logic a_write_q, a_write_d ; + logic [Aw-1:0] a_addr_q, a_addr_d ; + logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; + logic a_rvalid_q, a_rvalid_d, a_rvalid_sram ; + logic [Width-1:0] a_rdata_q, a_rdata_d ; + logic [TotalWidth-1:0] a_rdata_sram ; + logic [1:0] a_rerror_q, a_rerror_d ; + + logic b_req_q, b_req_d ; + logic b_write_q, b_write_d ; + logic [Aw-1:0] b_addr_q, b_addr_d ; + logic [TotalWidth-1:0] b_wdata_q, b_wdata_d ; + logic b_rvalid_q, b_rvalid_d, b_rvalid_sram ; + logic [Width-1:0] b_rdata_q, b_rdata_d ; + logic [TotalWidth-1:0] b_rdata_sram ; + logic [1:0] b_rerror_q, b_rerror_d ; + + if (MemT == "REGISTER") begin : gen_regmem + prim_ram_2p #( + // force register implementation for all targets + .Impl (prim_pkg::ImplGeneric), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (TotalWidth), + .MemInitFile (MemInitFile) + ) u_mem ( + .clk_a_i (clk_i), + .clk_b_i (clk_i), + + .a_req_i (a_req_q), + .a_write_i (a_write_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i ({TotalWidth{1'b1}}), + .a_rdata_o (a_rdata_sram), + + .b_req_i (b_req_q), + .b_write_i (b_write_q), + .b_addr_i (b_addr_q), + .b_wdata_i (b_wdata_q), + .b_wmask_i ({TotalWidth{1'b1}}), + .b_rdata_o (b_rdata_sram) + ); + // end else if (TotalWidth == aa && Depth == yy) begin + end else if (MemT == "SRAM") begin : gen_srammem + prim_ram_2p #( + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (TotalWidth), + .MemInitFile (MemInitFile) + ) u_mem ( + .clk_a_i (clk_i), + .clk_b_i (clk_i), + + .a_req_i (a_req_q), + .a_write_i (a_write_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i ({TotalWidth{1'b1}}), + .a_rdata_o (a_rdata_sram), + + .b_req_i (b_req_q), + .b_write_i (b_write_q), + .b_addr_i (b_addr_q), + .b_wdata_i (b_wdata_q), + .b_wmask_i ({TotalWidth{1'b1}}), + .b_rdata_o (b_rdata_sram) + ); + end + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + a_rvalid_sram <= '0; + b_rvalid_sram <= '0; + end else begin + a_rvalid_sram <= a_req_q & ~a_write_q; + b_rvalid_sram <= b_req_q & ~b_write_q; + end + end + + assign a_req_d = a_req_i; + assign a_write_d = a_write_i; + assign a_addr_d = a_addr_i; + assign a_rvalid_o = a_rvalid_q; + assign a_rdata_o = a_rdata_q; + assign a_rerror_o = a_rerror_q; + + assign b_req_d = b_req_i; + assign b_write_d = b_write_i; + assign b_addr_d = b_addr_i; + assign b_rvalid_o = b_rvalid_q; + assign b_rdata_o = b_rdata_q; + assign b_rerror_o = b_rerror_q; + + // TODO: Parity Logic + `ASSERT_INIT(ParityNotYetSupported_A, EnableParity == 0) + + if (EnableParity == 0 && EnableECC) begin : gen_secded + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + + if (Width == 32) begin : gen_secded_39_32 + prim_secded_39_32_enc u_enc_a (.in(a_wdata_i), .out(a_wdata_d)); + prim_secded_39_32_dec u_dec_a ( + .in (a_rdata_sram), + .d_o (a_rdata_d), + .syndrome_o (), + .err_o (a_rerror_d) + ); + prim_secded_39_32_enc u_enc_b (.in(b_wdata_i), .out(b_wdata_d)); + prim_secded_39_32_dec u_dec_b ( + .in (b_rdata_sram), + .d_o (b_rdata_d), + .syndrome_o (), + .err_o (b_rerror_d) + ); + assign a_rvalid_d = a_rvalid_sram; + assign b_rvalid_d = b_rvalid_sram; + end + end else begin : gen_nosecded + assign a_wdata_d[0+:Width] = a_wdata_i; + assign b_wdata_d[0+:Width] = b_wdata_i; + assign a_rdata_d = a_rdata_sram; + assign b_rdata_d = b_rdata_sram; + assign a_rvalid_d = a_rvalid_sram; + assign b_rvalid_d = b_rvalid_sram; + assign a_rerror_d = 2'b00; + assign b_rerror_d = 2'b00; + end + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + a_req_q <= '0; + a_write_q <= '0; + a_addr_q <= '0; + a_wdata_q <= '0; + + b_req_q <= '0; + b_write_q <= '0; + b_addr_q <= '0; + b_wdata_q <= '0; + end else begin + a_req_q <= a_req_d; + a_write_q <= a_write_d; + a_addr_q <= a_addr_d; + a_wdata_q <= a_wdata_d; + + b_req_q <= b_req_d; + b_write_q <= b_write_d; + b_addr_q <= b_addr_d; + b_wdata_q <= b_wdata_d; + end + end + end else begin : gen_dirconnect_input + assign a_req_q = a_req_d; + assign a_write_q = a_write_d; + assign a_addr_q = a_addr_d; + assign a_wdata_q = a_wdata_d; + + assign b_req_q = b_req_d; + assign b_write_q = b_write_d; + assign b_addr_q = b_addr_d; + assign b_wdata_q = b_wdata_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + a_rvalid_q <= '0; + a_rdata_q <= '0; + a_rerror_q <= '0; + + b_rvalid_q <= '0; + b_rdata_q <= '0; + b_rerror_q <= '0; + end else begin + a_rvalid_q <= a_rvalid_d; + a_rdata_q <= a_rdata_d ; + a_rerror_q <= a_rerror_d; + + b_rvalid_q <= b_rvalid_d; + b_rdata_q <= b_rdata_d ; + b_rerror_q <= b_rerror_d; + end + end + end else begin : gen_dirconnect_output + assign a_rvalid_q = a_rvalid_d; + assign a_rdata_q = a_rdata_d; + assign a_rerror_q = a_rerror_d; + + assign b_rvalid_q = b_rvalid_d; + assign b_rdata_q = b_rdata_d; + assign b_rerror_q = b_rerror_d; + end + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv new file mode 100644 index 0000000000..a1c8474b19 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_ram_2p_async_adv.sv @@ -0,0 +1,280 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Dual-port SRAM Wrapper +// This module to connect SRAM interface to actual SRAM interface +// At this time, it doesn't utilize ECC or any pipeline. +// This module stays to include any additional calculation logic later on. +// Instantiating SRAM is up to the top design to remove process dependency. + +// Parameter +// EnableECC: +// EnableParity: +// EnableInputPipeline: +// EnableOutputPipeline: + +`include "prim_assert.sv" + +module prim_ram_2p_async_adv #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int CfgW = 8, // WTC, RTC, etc + + // Configurations + parameter bit EnableECC = 0, + parameter bit EnableParity = 0, + parameter bit EnableInputPipeline = 0, + parameter bit EnableOutputPipeline = 0, + + parameter MemT = "REGISTER", // can be "REGISTER" or "SRAM" + + // Do not touch + parameter int SramAw = $clog2(Depth) +) ( + input clk_a_i, + input clk_b_i, + input rst_a_ni, + input rst_b_ni, + + input a_req_i, + input a_write_i, + input [SramAw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + output logic a_rvalid_o, + output logic [Width-1:0] a_rdata_o, + output logic [1:0] a_rerror_o, + + input b_req_i, + input b_write_i, + input [SramAw-1:0] b_addr_i, + input [Width-1:0] b_wdata_i, + output logic b_rvalid_o, + output logic [Width-1:0] b_rdata_o, + output logic [1:0] b_rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + // config + input [CfgW-1:0] cfg_i +); + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? 1 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + logic a_req_q, a_req_d ; + logic a_write_q, a_write_d ; + logic [SramAw-1:0] a_addr_q, a_addr_d ; + logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; + logic a_rvalid_q, a_rvalid_d, a_rvalid_sram ; + logic [TotalWidth-1:0] a_rdata_d, a_rdata_sram ; + logic [Width-1:0] a_rdata_q ; + logic [1:0] a_rerror_q, a_rerror_d ; + + logic b_req_q, b_req_d ; + logic b_write_q, b_write_d ; + logic [SramAw-1:0] b_addr_q, b_addr_d ; + logic [TotalWidth-1:0] b_wdata_q, b_wdata_d ; + logic b_rvalid_q, b_rvalid_d, b_rvalid_sram ; + logic [TotalWidth-1:0] b_rdata_d, b_rdata_sram ; + logic [Width-1:0] b_rdata_q ; + logic [1:0] b_rerror_q, b_rerror_d ; + + if (MemT == "REGISTER") begin : gen_regmem + prim_ram_2p #( + // force register implementation for all targets + .Impl (prim_pkg::ImplGeneric), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (TotalWidth) + ) u_mem ( + .clk_a_i (clk_a_i), + .clk_b_i (clk_b_i), + + .a_req_i (a_req_q), + .a_write_i (a_write_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i ({TotalWidth{1'b1}}), + .a_rdata_o (a_rdata_sram), + + .b_req_i (b_req_q), + .b_write_i (b_write_q), + .b_addr_i (b_addr_q), + .b_wdata_i (b_wdata_q), + .b_wmask_i ({TotalWidth{1'b1}}), + .b_rdata_o (b_rdata_sram) + ); + // end else if (TotalWidth == aa && Depth == yy) begin + end else if (MemT == "SRAM") begin : gen_srammem + prim_ram_2p #( + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (TotalWidth) + ) u_mem ( + .clk_a_i (clk_a_i), + .clk_b_i (clk_b_i), + + .a_req_i (a_req_q), + .a_write_i (a_write_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i ({TotalWidth{1'b1}}), + .a_rdata_o (a_rdata_sram), + + .b_req_i (b_req_q), + .b_write_i (b_write_q), + .b_addr_i (b_addr_q), + .b_wdata_i (b_wdata_q), + .b_wmask_i ({TotalWidth{1'b1}}), + .b_rdata_o (b_rdata_sram) + ); + end + + always_ff @(posedge clk_a_i or negedge rst_a_ni) begin + if (!rst_a_ni) begin + a_rvalid_sram <= 1'b0; + end else begin + a_rvalid_sram <= a_req_q & ~a_write_q; + end + end + always_ff @(posedge clk_b_i or negedge rst_b_ni) begin + if (!rst_b_ni) begin + b_rvalid_sram <= 1'b0; + end else begin + b_rvalid_sram <= b_req_q & ~b_write_q; + end + end + + assign a_req_d = a_req_i; + assign a_write_d = a_write_i; + assign a_addr_d = a_addr_i; + assign a_rvalid_o = a_rvalid_q; + assign a_rdata_o = a_rdata_q; + assign a_rerror_o = a_rerror_q; + + assign b_req_d = b_req_i; + assign b_write_d = b_write_i; + assign b_addr_d = b_addr_i; + assign b_rvalid_o = b_rvalid_q; + assign b_rdata_o = b_rdata_q; + assign b_rerror_o = b_rerror_q; + + // TODO: Parity Logic + `ASSERT_INIT(ParityNotYetSupported_A, EnableParity == 0) + + if (EnableParity == 0 && EnableECC) begin : gen_secded + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + + if (Width == 32) begin : gen_secded_39_32 + prim_secded_39_32_enc u_enc_a (.in(a_wdata_i), .out(a_wdata_d)); + prim_secded_39_32_dec u_dec_a ( + .in (a_rdata_sram), + .d_o (a_rdata_d[0+:Width]), + .syndrome_o (a_rdata_d[Width+:ParWidth]), + .err_o (a_rerror_d) + ); + prim_secded_39_32_enc u_enc_b (.in(b_wdata_i), .out(b_wdata_d)); + prim_secded_39_32_dec u_dec_b ( + .in (b_rdata_sram), + .d_o (b_rdata_d[0+:Width]), + .syndrome_o (b_rdata_d[Width+:ParWidth]), + .err_o (b_rerror_d) + ); + assign a_rvalid_d = a_rvalid_sram; + assign b_rvalid_d = b_rvalid_sram; + end + end else begin : gen_nosecded + assign a_wdata_d[0+:Width] = a_wdata_i; + assign b_wdata_d[0+:Width] = b_wdata_i; + assign a_rdata_d[0+:Width] = a_rdata_sram; + assign b_rdata_d[0+:Width] = b_rdata_sram; + assign a_rvalid_d = a_rvalid_sram; + assign b_rvalid_d = b_rvalid_sram; + assign a_rerror_d = 2'b00; + assign b_rerror_d = 2'b00; + end + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_a_i or negedge rst_a_ni) begin + if (!rst_a_ni) begin + a_req_q <= '0; + a_write_q <= '0; + a_addr_q <= '0; + a_wdata_q <= '0; + end else begin + a_req_q <= a_req_d; + a_write_q <= a_write_d; + a_addr_q <= a_addr_d; + a_wdata_q <= a_wdata_d; + end + end + always_ff @(posedge clk_b_i or negedge rst_b_ni) begin + if (!rst_b_ni) begin + b_req_q <= '0; + b_write_q <= '0; + b_addr_q <= '0; + b_wdata_q <= '0; + end else begin + b_req_q <= b_req_d; + b_write_q <= b_write_d; + b_addr_q <= b_addr_d; + b_wdata_q <= b_wdata_d; + end + end + end else begin : gen_dirconnect_input + assign a_req_q = a_req_d; + assign a_write_q = a_write_d; + assign a_addr_q = a_addr_d; + assign a_wdata_q = a_wdata_d; + + assign b_req_q = b_req_d; + assign b_write_q = b_write_d; + assign b_addr_q = b_addr_d; + assign b_wdata_q = b_wdata_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + always_ff @(posedge clk_a_i or negedge rst_a_ni) begin + if (!rst_a_ni) begin + a_rvalid_q <= '0; + a_rdata_q <= '0; + a_rerror_q <= '0; + end else begin + a_rvalid_q <= a_rvalid_d; + a_rdata_q <= a_rdata_d[0+:Width] ; + a_rerror_q <= a_rerror_d; + end + end + always_ff @(posedge clk_b_i or negedge rst_b_ni) begin + if (!rst_b_ni) begin + b_rvalid_q <= '0; + b_rdata_q <= '0; + b_rerror_q <= '0; + end else begin + b_rvalid_q <= b_rvalid_d; + b_rdata_q <= b_rdata_d[0+:Width] ; + b_rerror_q <= b_rerror_d; + end + end + end else begin : gen_dirconnect_output + assign a_rvalid_q = a_rvalid_d; + assign a_rdata_q = a_rdata_d[0+:Width]; + assign a_rerror_q = a_rerror_d; + + assign b_rvalid_q = b_rvalid_d; + assign b_rdata_q = b_rdata_d[0+:Width]; + assign b_rerror_q = b_rerror_d; + end + +endmodule diff --git a/shared/rtl/prim_secded_28_22_dec.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_dec.sv similarity index 100% rename from shared/rtl/prim_secded_28_22_dec.sv rename to vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_dec.sv diff --git a/shared/rtl/prim_secded_28_22_enc.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_enc.sv similarity index 100% rename from shared/rtl/prim_secded_28_22_enc.sv rename to vendor/lowrisc_ip/prim/rtl/prim_secded_28_22_enc.sv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_dec.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_dec.sv new file mode 100644 index 0000000000..b6a89c6d75 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_dec.sv @@ -0,0 +1,77 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Decoder generated by secded_gen.py + +module prim_secded_39_32_dec ( + input [38:0] in, + output logic [31:0] d_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + logic single_error; + + // Syndrome calculation + assign syndrome_o[0] = in[32] ^ in[2] ^ in[3] ^ in[7] ^ in[8] ^ in[14] ^ in[15] + ^ in[16] ^ in[18] ^ in[19] ^ in[23] ^ in[24] ^ in[28] ^ in[29] + ; + assign syndrome_o[1] = in[33] ^ in[3] ^ in[6] ^ in[8] ^ in[12] ^ in[13] ^ in[15] + ^ in[17] ^ in[19] ^ in[21] ^ in[25] ^ in[27] ^ in[29] ^ in[30] + ^ in[31] ; + assign syndrome_o[2] = in[34] ^ in[0] ^ in[5] ^ in[7] ^ in[9] ^ in[10] ^ in[12] + ^ in[13] ^ in[15] ^ in[16] ^ in[22] ^ in[23] ^ in[26] ^ in[27] + ^ in[31] ; + assign syndrome_o[3] = in[35] ^ in[0] ^ in[1] ^ in[4] ^ in[6] ^ in[9] ^ in[11] + ^ in[12] ^ in[14] ^ in[22] ^ in[23] ^ in[25] ^ in[28] ^ in[29] + ^ in[30] ; + assign syndrome_o[4] = in[36] ^ in[0] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[11] + ^ in[17] ^ in[20] ^ in[24] ^ in[26] ^ in[27] ^ in[30] ; + assign syndrome_o[5] = in[37] ^ in[1] ^ in[2] ^ in[4] ^ in[6] ^ in[10] ^ in[13] + ^ in[14] ^ in[16] ^ in[18] ^ in[19] ^ in[20] ^ in[21] ^ in[22] + ^ in[26] ; + assign syndrome_o[6] = in[38] ^ in[1] ^ in[5] ^ in[7] ^ in[8] ^ in[9] ^ in[10] + ^ in[11] ^ in[17] ^ in[18] ^ in[20] ^ in[21] ^ in[24] ^ in[25] + ^ in[28] ^ in[31] ; + + // Corrected output calculation + assign d_o[0] = (syndrome_o == 7'h1c) ^ in[0]; + assign d_o[1] = (syndrome_o == 7'h68) ^ in[1]; + assign d_o[2] = (syndrome_o == 7'h31) ^ in[2]; + assign d_o[3] = (syndrome_o == 7'h13) ^ in[3]; + assign d_o[4] = (syndrome_o == 7'h38) ^ in[4]; + assign d_o[5] = (syndrome_o == 7'h54) ^ in[5]; + assign d_o[6] = (syndrome_o == 7'h2a) ^ in[6]; + assign d_o[7] = (syndrome_o == 7'h45) ^ in[7]; + assign d_o[8] = (syndrome_o == 7'h43) ^ in[8]; + assign d_o[9] = (syndrome_o == 7'h4c) ^ in[9]; + assign d_o[10] = (syndrome_o == 7'h64) ^ in[10]; + assign d_o[11] = (syndrome_o == 7'h58) ^ in[11]; + assign d_o[12] = (syndrome_o == 7'he) ^ in[12]; + assign d_o[13] = (syndrome_o == 7'h26) ^ in[13]; + assign d_o[14] = (syndrome_o == 7'h29) ^ in[14]; + assign d_o[15] = (syndrome_o == 7'h7) ^ in[15]; + assign d_o[16] = (syndrome_o == 7'h25) ^ in[16]; + assign d_o[17] = (syndrome_o == 7'h52) ^ in[17]; + assign d_o[18] = (syndrome_o == 7'h61) ^ in[18]; + assign d_o[19] = (syndrome_o == 7'h23) ^ in[19]; + assign d_o[20] = (syndrome_o == 7'h70) ^ in[20]; + assign d_o[21] = (syndrome_o == 7'h62) ^ in[21]; + assign d_o[22] = (syndrome_o == 7'h2c) ^ in[22]; + assign d_o[23] = (syndrome_o == 7'hd) ^ in[23]; + assign d_o[24] = (syndrome_o == 7'h51) ^ in[24]; + assign d_o[25] = (syndrome_o == 7'h4a) ^ in[25]; + assign d_o[26] = (syndrome_o == 7'h34) ^ in[26]; + assign d_o[27] = (syndrome_o == 7'h16) ^ in[27]; + assign d_o[28] = (syndrome_o == 7'h49) ^ in[28]; + assign d_o[29] = (syndrome_o == 7'hb) ^ in[29]; + assign d_o[30] = (syndrome_o == 7'h1a) ^ in[30]; + assign d_o[31] = (syndrome_o == 7'h46) ^ in[31]; + + // err_o calc. bit0: single error, bit1: double error + assign single_error = ^syndrome_o; + assign err_o[0] = single_error; + assign err_o[1] = ~single_error & (|syndrome_o); +endmodule + diff --git a/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_enc.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_enc.sv new file mode 100644 index 0000000000..ce4c26a06a --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_secded_39_32_enc.sv @@ -0,0 +1,62 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED Encoder generated by secded_gen.py + +module prim_secded_39_32_enc ( + input [31:0] in, + output logic [38:0] out +); + + assign out[0] = in[0] ; + assign out[1] = in[1] ; + assign out[2] = in[2] ; + assign out[3] = in[3] ; + assign out[4] = in[4] ; + assign out[5] = in[5] ; + assign out[6] = in[6] ; + assign out[7] = in[7] ; + assign out[8] = in[8] ; + assign out[9] = in[9] ; + assign out[10] = in[10] ; + assign out[11] = in[11] ; + assign out[12] = in[12] ; + assign out[13] = in[13] ; + assign out[14] = in[14] ; + assign out[15] = in[15] ; + assign out[16] = in[16] ; + assign out[17] = in[17] ; + assign out[18] = in[18] ; + assign out[19] = in[19] ; + assign out[20] = in[20] ; + assign out[21] = in[21] ; + assign out[22] = in[22] ; + assign out[23] = in[23] ; + assign out[24] = in[24] ; + assign out[25] = in[25] ; + assign out[26] = in[26] ; + assign out[27] = in[27] ; + assign out[28] = in[28] ; + assign out[29] = in[29] ; + assign out[30] = in[30] ; + assign out[31] = in[31] ; + assign out[32] = in[2] ^ in[3] ^ in[7] ^ in[8] ^ in[14] ^ in[15] ^ in[16] + ^ in[18] ^ in[19] ^ in[23] ^ in[24] ^ in[28] ^ in[29] ; + assign out[33] = in[3] ^ in[6] ^ in[8] ^ in[12] ^ in[13] ^ in[15] ^ in[17] + ^ in[19] ^ in[21] ^ in[25] ^ in[27] ^ in[29] ^ in[30] ^ in[31] ; + assign out[34] = in[0] ^ in[5] ^ in[7] ^ in[9] ^ in[10] ^ in[12] ^ in[13] + ^ in[15] ^ in[16] ^ in[22] ^ in[23] ^ in[26] ^ in[27] ^ in[31] ; + assign out[35] = in[0] ^ in[1] ^ in[4] ^ in[6] ^ in[9] ^ in[11] ^ in[12] + ^ in[14] ^ in[22] ^ in[23] ^ in[25] ^ in[28] ^ in[29] ^ in[30] + ; + assign out[36] = in[0] ^ in[2] ^ in[3] ^ in[4] ^ in[5] ^ in[11] ^ in[17] + ^ in[20] ^ in[24] ^ in[26] ^ in[27] ^ in[30] ; + assign out[37] = in[1] ^ in[2] ^ in[4] ^ in[6] ^ in[10] ^ in[13] ^ in[14] + ^ in[16] ^ in[18] ^ in[19] ^ in[20] ^ in[21] ^ in[22] ^ in[26] + ; + assign out[38] = in[1] ^ in[5] ^ in[7] ^ in[8] ^ in[9] ^ in[10] ^ in[11] + ^ in[17] ^ in[18] ^ in[20] ^ in[21] ^ in[24] ^ in[25] ^ in[28] + ^ in[31] ; +endmodule + diff --git a/shared/rtl/prim_secded_72_64_dec.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_dec.sv similarity index 100% rename from shared/rtl/prim_secded_72_64_dec.sv rename to vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_dec.sv diff --git a/shared/rtl/prim_secded_72_64_enc.sv b/vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_enc.sv similarity index 100% rename from shared/rtl/prim_secded_72_64_enc.sv rename to vendor/lowrisc_ip/prim/rtl/prim_secded_72_64_enc.sv diff --git a/vendor/lowrisc_ip/prim/rtl/prim_sram_arbiter.sv b/vendor/lowrisc_ip/prim/rtl/prim_sram_arbiter.sv new file mode 100644 index 0000000000..9a5d7948f5 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_sram_arbiter.sv @@ -0,0 +1,128 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 SRAM arbiter +// +// Parameter +// N: Number of requst port +// DW: Data width (SECDED is not included) +// Aw: Address width +// ArbiterImpl: can be either PPC or BINTREE. +`include "prim_assert.sv" + +module prim_sram_arbiter #( + parameter int N = 4, + parameter int SramDw = 32, + parameter int SramAw = 12, + parameter ArbiterImpl = "PPC" +) ( + input clk_i, + input rst_ni, + + input [ N-1:0] req, + input [SramAw-1:0] req_addr [N], + input req_write [N], + input [SramDw-1:0] req_wdata [N], + output logic [ N-1:0] gnt, + + output logic [ N-1:0] rsp_rvalid, // Pulse + output logic [SramDw-1:0] rsp_rdata [N], + output logic [ 1:0] rsp_error [N], + + // SRAM Interface + output logic sram_req, + output logic [SramAw-1:0] sram_addr, + output logic sram_write, + output logic [SramDw-1:0] sram_wdata, + input sram_rvalid, + input [SramDw-1:0] sram_rdata, + input [1:0] sram_rerror +); + + + typedef struct packed { + logic write; + logic [SramAw-1:0] addr; + logic [SramDw-1:0] wdata; + } req_t; + + localparam int ARB_DW = $bits(req_t); + + req_t req_packed [N]; + + for (genvar i = 0 ; i < N ; i++) begin : gen_reqs + assign req_packed[i] = {req_write[i], req_addr[i], req_wdata[i]}; + end + + req_t sram_packed; + assign sram_write = sram_packed.write; + assign sram_addr = sram_packed.addr; + assign sram_wdata = sram_packed.wdata; + + if (ArbiterImpl == "PPC") begin : gen_arb_ppc + prim_arbiter_ppc #( + .N (N), + .DW(ARB_DW) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_i ( req ), + .data_i ( req_packed ), + .gnt_o ( gnt ), + .idx_o ( ), + .valid_o ( sram_req ), + .data_o ( sram_packed ), + .ready_i ( 1'b1 ) + ); + end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb + prim_arbiter_arb #( + .N (N), + .DW(ARB_DW) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_i ( req ), + .data_i ( req_packed ), + .gnt_o ( gnt ), + .idx_o ( ), + .valid_o ( sram_req ), + .data_o ( sram_packed ), + .ready_i ( 1'b1 ) + ); + end else begin : gen_unknown + `ASSERT_INIT(UnknownArbImpl_A, 0) + end + + + logic [N-1:0] steer; // Steering sram_rvalid + logic sram_ack; // Ack for rvalid. |sram_rvalid + + assign sram_ack = sram_rvalid & (|steer); + + // Request FIFO + prim_fifo_sync #( + .Width (N), + .Pass (1'b0), + .Depth (4) // Assume at most 4 pipelined + ) u_req_fifo ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid (sram_req && !sram_write), // Push only for read + .wready (), // TODO: Generate Error + .wdata (gnt), + .depth (), // Not used + .rvalid (), // TODO; Generate error if sram_rvalid but rvalid==0 + .rready (sram_ack), + .rdata (steer) + ); + + assign rsp_rvalid = steer & {N{sram_rvalid}}; + + for (genvar i = 0 ; i < N ; i++) begin : gen_rsp + assign rsp_rdata[i] = sram_rdata; + assign rsp_error[i] = sram_rerror; // No SECDED yet + end + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_subreg.sv b/vendor/lowrisc_ip/prim/rtl/prim_subreg.sv new file mode 100644 index 0000000000..ce2b55b838 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_subreg.sv @@ -0,0 +1,76 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register slice conforming to Comportibility guide. + +module prim_subreg #( + parameter int DW = 32 , + parameter SWACCESS = "RW", // {RW, RO, WO, W1C, W1S, W0C, RC} + parameter logic [DW-1:0] RESVAL = '0 // Reset value +) ( + input clk_i, + input rst_ni, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC + // In case of RC, Top connects Read Pulse to we + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO + input de, + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + logic wr_en ; + logic [DW-1:0] wr_data; + + if ((SWACCESS == "RW") || (SWACCESS == "WO")) begin : gen_w + assign wr_en = we | de ; + assign wr_data = (we == 1'b1) ? wd : d ; // SW higher priority + end else if (SWACCESS == "RO") begin : gen_ro + // Unused we, wd + assign wr_en = de ; + assign wr_data = d ; + end else if (SWACCESS == "W1S") begin : gen_w1s + // If SWACCESS is W1S, then assume hw tries to clear. + // So, give a chance HW to clear when SW tries to set. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de ; + assign wr_data = (de ? d : q) | (we ? wd : '0); + end else if (SWACCESS == "W1C") begin : gen_w1c + // If SWACCESS is W1C, then assume hw tries to set. + // So, give a chance HW to set when SW tries to clear. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de ; + assign wr_data = (de ? d : q) & (we ? ~wd : '1); + end else if (SWACCESS == "W0C") begin : gen_w0c + assign wr_en = we | de ; + assign wr_data = (de ? d : q) & (we ? wd : '1); + end else if (SWACCESS == "RC") begin : gen_rc + // This swtype is not recommended but exists for compatibility. + // WARN: we signal is actually read signal not write enable. + assign wr_en = we | de ; + assign wr_data = (de ? d : q) & (we ? '0 : '1); + end else begin : gen_hw + assign wr_en = de ; + assign wr_data = d ; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) qe <= 1'b0; + else qe <= we ; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) q <= RESVAL ; + else if (wr_en) q <= wr_data; + end + assign qs = q; + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_subreg_ext.sv b/vendor/lowrisc_ip/prim/rtl/prim_subreg_ext.sv new file mode 100644 index 0000000000..6db975d8da --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_subreg_ext.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register slice conforming to Comportibility guide. + +module prim_subreg_ext #( + parameter int unsigned DW = 32 +) ( + input re, + input we, + input [DW-1:0] wd, + + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic qre, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + assign qs = d; + assign q = wd; + assign qe = we; + assign qre = re; + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_sync_reqack.sv b/vendor/lowrisc_ip/prim/rtl/prim_sync_reqack.sv new file mode 100644 index 0000000000..5484898b55 --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_sync_reqack.sv @@ -0,0 +1,158 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// REQ/ACK synchronizer +// +// This module synchronizes a REQ/ACK handshake across a clock domain crossing. +// Both domains will see a handshake with the duration of one clock cycle. +// +// Notes: +// - Once asserted, the source domain is not allowed to de-assert REQ without ACK. +// - The destination domain is not allowed to send an ACK without a REQ. +// - This module works both when syncing from a faster to a slower clock domain and vice versa. +// - Internally, this module uses a return-to-zero, four-phase handshake protocol. Assuming the +// destination side responds with an ACK immediately, the latency from asserting the REQ on the +// source side is: +// - 1 source + 2 destination clock cycles until the handshake is performed on the +// destination side, +// - 1 source + 2 destination + 1 destination + 2 source clock cycles until the handshake is +// performed on the source side. +// - It takes another round trip (3 source + 3 destination clock cycles) before the next +// REQ is starting to be propagated to the destination side. The module is thus not suitable +// for high-bandwidth communication. + +`include "prim_assert.sv" + +module prim_sync_reqack ( + input clk_src_i, // REQ side, SRC domain + input rst_src_ni, // REQ side, SRC domain + input clk_dst_i, // ACK side, DST domain + input rst_dst_ni, // ACK side, DST domain + + input logic src_req_i, // REQ side, SRC domain + output logic src_ack_o, // REQ side, SRC domain + output logic dst_req_o, // ACK side, DST domain + input logic dst_ack_i // ACK side, DST domain +); + + // Types + typedef enum logic { + HANDSHAKE, SYNC + } sync_reqack_fsm_e; + + // Signals + sync_reqack_fsm_e src_fsm_ns, src_fsm_cs; + sync_reqack_fsm_e dst_fsm_ns, dst_fsm_cs; + logic src_req_d, src_req_q, src_ack; + logic dst_ack_d, dst_ack_q, dst_req; + + // Move REQ over to ACK side. + prim_flop_2sync #( + .Width(1) + ) req_sync ( + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .d (src_req_q), + .q (dst_req) + ); + + // Move ACK over to REQ side. + prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i (clk_src_i), + .rst_ni (rst_src_ni), + .d (dst_ack_q), + .q (src_ack) + ); + + // REQ-side FSM (source domain) + always_comb begin : src_fsm + src_fsm_ns = src_fsm_cs; + + // By default, we forward the REQ and ACK. + src_req_d = src_req_i; + src_ack_o = src_ack; + + unique case (src_fsm_cs) + + HANDSHAKE: begin + // The handshake on the REQ side is done for exactly 1 clock cycle. + if (src_req_i && src_ack) begin + src_fsm_ns = SYNC; + // Tell ACK side that we are done. + src_req_d = 1'b0; + end + end + + SYNC: begin + // Make sure ACK side knows that we are done. + src_req_d = 1'b0; + src_ack_o = 1'b0; + if (!src_ack) begin + src_fsm_ns = HANDSHAKE; + end + end + + default: ; + endcase + end + + // ACK-side FSM (destination domain) + always_comb begin : dst_fsm + dst_fsm_ns = dst_fsm_cs; + + // By default, we forward the REQ and ACK. + dst_req_o = dst_req; + dst_ack_d = dst_ack_i; + + unique case (dst_fsm_cs) + + HANDSHAKE: begin + // The handshake on the ACK side is done for exactly 1 clock cycle. + if (dst_req && dst_ack_i) begin + dst_fsm_ns = SYNC; + end + end + + SYNC: begin + // Don't forward REQ, hold ACK, wait for REQ side. + dst_req_o = 1'b0; + dst_ack_d = 1'b1; + if (!dst_req) begin + dst_fsm_ns = HANDSHAKE; + end + end + + default: ; + endcase + end + + // Registers + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_fsm_cs <= HANDSHAKE; + src_req_q <= 1'b0; + end else begin + src_fsm_cs <= src_fsm_ns; + src_req_q <= src_req_d; + end + end + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_fsm_cs <= HANDSHAKE; + dst_ack_q <= 1'b0; + end else begin + dst_fsm_cs <= dst_fsm_ns; + dst_ack_q <= dst_ack_d; + end + end + + // Source domain cannot de-assert REQ while waiting for ACK. + `ASSERT(ReqAckSyncHoldReq, $fell(src_req_i) |-> (src_fsm_cs != HANDSHAKE), clk_src_i, rst_src_ni) + + // Destination domain cannot assert ACK without REQ. + `ASSERT(ReqAckSyncAckNeedsReq, dst_ack_i |-> dst_req_o, clk_dst_i, rst_dst_ni) + +endmodule diff --git a/vendor/lowrisc_ip/prim/rtl/prim_util_memload.sv b/vendor/lowrisc_ip/prim/rtl/prim_util_memload.sv new file mode 100644 index 0000000000..db7710288e --- /dev/null +++ b/vendor/lowrisc_ip/prim/rtl/prim_util_memload.sv @@ -0,0 +1,61 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Memory loader for simulation + * + * Include this file in a memory primitive to load a memory array from + * simulation. + * + * Requirements: + * - A memory array named `mem`. + * - A parameter `Width` giving the memory width (word size) in bit. + * - A parameter `Depth` giving the memory depth in words. + * - A parameter `MemInitFile` with a file path of a VMEM file to be loaded into +* the memory if not empty. + */ + +`ifdef VERILATOR + // Task for loading 'mem' with SystemVerilog system task $readmemh() + export "DPI-C" task simutil_verilator_memload; + + task simutil_verilator_memload; + input string file; + $readmemh(file, mem); + endtask + + // Width must be a multiple of 32bit for this function to work + // Note that the DPI export and function definition must both be in the same generate + // context to get the correct name. + if ((Width % 32) == 0) begin : gen_set_mem + // Function for setting a specific element in |mem| + // Returns 1 (true) for success, 0 (false) for errors. + export "DPI-C" function simutil_verilator_set_mem; + + function int simutil_verilator_set_mem(input int index, + input bit [Width-1:0] val); + if (index >= Depth) begin + return 0; + end + + mem[index] = val; + return 1; + endfunction + end else begin : gen_other + // Function doesn't work unless Width % 32 so just return 0 + export "DPI-C" function simutil_verilator_set_mem; + + function int simutil_verilator_set_mem(input int index, + input bit [Width-1:0] val); + return 0; + endfunction + end +`endif + +initial begin + if (MemInitFile != "") begin : gen_meminit + $display("Initializing memory %m from file '%s'.", MemInitFile); + $readmemh(MemInitFile, mem); + end +end diff --git a/vendor/lowrisc_ip/prim/util/get-lfsr-coeffs.py b/vendor/lowrisc_ip/prim/util/get-lfsr-coeffs.py new file mode 100755 index 0000000000..44c4152184 --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/get-lfsr-coeffs.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import glob +import os +import shutil +import subprocess +import sys + +import wget + +USAGE = """./get_lfsr_coeffs.py [-t ] [-o ] [-f] [--fib] + +Downloads LFSR constants from [1] and dumps them in SystemVerilog format +(for use in prim_lfsr.sv). These coeffs are for a Galois XOR type LFSR, and cover +implementations ranging from 4 to 64bits. + +Alternatively, the script can also extract the XNOR Fibonacci type LFSR coefficients +from the XILINX application note 52 [2] by specifying the --fib switch. Note that this +depends on the pdftotext utility for Linux. + +[1] https://users.ece.cmu.edu/~koopman/lfsr/ + +[2] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf +""" + +# configuration for Galois +MIN_LFSR_LEN = 4 +MAX_LFSR_LEN = 64 +BASE_URL = 'https://users.ece.cmu.edu/~koopman/lfsr/' + +# configuration for Fibonacci +FIB_URL = 'https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf' +PDF_NAME = 'xapp052' +LINE_FILTER = [ + 'Table 3: Taps for Maximum-Length LFSR Counters', + 'XAPP 052 July 7,1996 (Version 1.1)' +] + + +# helper function to write out coeffs +def dump_coeffs(lfsrType, widths, coeffs, outfile): + # widths consistency check + for k in range(widths[0], widths[-1] + 1): + # print("%d -- %d" % (k,widths[k-widths[0]])) + if k != widths[k - widths[0]]: + print("Error: widths is not consistently increasing") + sys.exit(1) + + # select first coefficient in each file and print to SV LUT + with outfile: + decl_str = "localparam int unsigned %s_LUT_OFF = %d;\n" \ + % (lfsrType, min(widths)) + outfile.write(decl_str) + decl_str = "localparam logic [%d:0] %s_COEFFS [%d] = '{ " \ + % (max(widths) - 1, lfsrType, max(widths)-min(widths)+1) + outfile.write(decl_str) + comma = ',\n' + spaces = '' + for k in widths: + if k == max(widths): + comma = "" + if k == min(widths) + 1: + for l in range(len(decl_str)): + spaces += ' ' + outfile.write("%s%d'h%s%s" % \ + (spaces, max(widths), coeffs[k-widths[0]], comma)) + outfile.write(' };\n') + + +# converts list with bit positions to a hex bit mask string +def to_bit_mask(bitPositions): + + bitMask = 0 + for b in bitPositions: + bitMask += 2**(b - 1) + + return "%X" % bitMask + + +def main(): + parser = argparse.ArgumentParser( + prog="get-lfsr-coeffs", + formatter_class=argparse.RawDescriptionHelpFormatter, + usage=USAGE, + description=__doc__, + epilog='defaults or the filename - can be used for stdin/stdout') + parser.add_argument( + '-t', + '--tempfolder', + help="""temporary folder to download the lfsr constant files +to (defaults to lfsr_tmp)""", + default='lfsr_tmp') + parser.add_argument('--fib', + help='download fibonacci coefficients', + action='store_true') + parser.add_argument('-f', + '--force', + help='overwrites tempfolder', + action='store_true') + parser.add_argument('-o', + '--output', + type=argparse.FileType('w'), + default=sys.stdout, + metavar='file', + help='Output file (default stdout)') + + args = parser.parse_args() + + if args.force and os.path.exists(args.tempfolder): + shutil.rmtree(args.tempfolder) + + if not os.path.exists(args.tempfolder): + # download coefficient files + os.makedirs(args.tempfolder, exist_ok=args.force) + os.chdir(args.tempfolder) + + if args.fib: + lfsrType = 'FIB_XNOR' + + wget.download(FIB_URL) + cmd = ['pdftotext %s.pdf' % PDF_NAME, '> %s.txt' % PDF_NAME] + subprocess.call(cmd, shell=True) + print("") + cmd = ['grep -A 350 "%s" %s.txt > table.txt' \ + % (LINE_FILTER[0], PDF_NAME)] + subprocess.call(cmd, shell=True) + + # parse the table + widths = [] + coeffs = [] + columnType = 0 + with open('table.txt') as infile: + for line in infile: + line = line.strip() + if line and line not in LINE_FILTER: + if line == 'n': + columnType = 0 + # yes, this is a typo in the PDF :) + elif line == 'XNOR from': + columnType = 1 + elif columnType: + tmpCoeffs = [int(c) for c in line.split(',')] + coeffs += [tmpCoeffs] + else: + widths += [int(line)] + + # # printout for checking + # for (w,c) in zip(widths,coeffs): + # print("width: %d > coeffs: %s" % (w, str(c))) + + # convert to bitmask + for k in range(len(coeffs)): + coeffs[k] = to_bit_mask(coeffs[k]) + + else: + lfsrType = 'GAL_XOR' + + for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1): + url = '%s%d.txt' % (BASE_URL, k) + print("\nDownloading %d bit LFSR coeffs from %s..." % (k, url)) + wget.download(url) + print("") + + widths = [] + coeffs = [] + for k in range(MIN_LFSR_LEN, MAX_LFSR_LEN + 1): + filename = '%d.txt' % k + with open(filename) as infile: + # read the first line + widths += [k] + coeffs += [infile.readline().strip()] + + # write to stdout or file + dump_coeffs(lfsrType, widths, coeffs, outfile=args.output) + else: + print("Temporary directory already exists, abort...") + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/vendor/lowrisc_ip/prim/util/keccak_rc.py b/vendor/lowrisc_ip/prim/util/keccak_rc.py new file mode 100755 index 0000000000..1ee97d0f41 --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/keccak_rc.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +r"""Calculate Round Constant +""" + +import argparse +import bitarray as ba +import logging as log + + +def main(): + parser = argparse.ArgumentParser( + prog="keccak round constant generator", + description= + '''This tool generates the round constants based on the given max round number''' + ) + parser.add_argument( + '-r', + type=int, + default=24, + help='''Max Round value. Default is SHA3 Keccak round %(default)''') + parser.add_argument('--verbose', '-v', action='store_true', help='Verbose') + + args = parser.parse_args() + + if (args.verbose): + log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) + else: + log.basicConfig(format="%(levelname)s: %(message)s") + + if args.r < 1: + log.error("Max Round value should be greater than 0") + + # Create 0..255 bit array + rc = ba.bitarray(256) + rc.setall(0) + + r = ba.bitarray('10000000') + rc[0] = True # t%255 == 0 -> 1 + for i in range(1, 256): + # Update from t=1 to t=255 + r_d = ba.bitarray('0') + r + if r_d[8]: + #Flip 0,4,5,6 + r = r_d[0:8] ^ ba.bitarray('10001110') + else: + r = r_d[0:8] + + rc[i] = r[0] + + ## Print rc + print(rc) + + ## Round + + rcs = [] # Each entry represent the round + for rnd in range(0, args.r): + # Let RC=0 + rndconst = ba.bitarray(64) + rndconst.setall(0) + # for j [0 .. L] RC[2**j-1] = rc(j+7*rnd) + for j in range(0, 7): #0 to 6 + rndconst[2**j - 1] = rc[(j + 7 * rnd) % 255] + print("64'h{}, // Round {}".format(rndhex(rndconst), rnd)) + + +def rndhex(bit) -> str: + return bit[::-1].tobytes().hex() + + +if __name__ == "__main__": + main() diff --git a/vendor/lowrisc_ip/prim/util/primgen.py b/vendor/lowrisc_ip/prim/util/primgen.py new file mode 100755 index 0000000000..1bed076180 --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/primgen.py @@ -0,0 +1,399 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +import os +import re +import shutil +import subprocess +import sys + +import yaml +from mako.template import Template + +try: + from yaml import CSafeLoader as YamlLoader, CSafeDumper as YamlDumper +except ImportError: + from yaml import SafeLoader as YamlLoader, SafeDumper as YamlDumper + + +def _split_vlnv(core_vlnv): + (vendor, library, name, version) = core_vlnv.split(':', 4) + return { + 'vendor': vendor, + 'library': library, + 'name': name, + 'version': version + } + + +def _prim_cores(cores, prim_name=None): + """ Get all cores of primitives found by fusesoc + + If prim_name is given, only primitives with the given name are returned. + Otherwise, all primitives are returned, independent of their name. + """ + def _filter_primitives(core): + """ Filter a list of cores to find the primitives we're interested in + + Matching cores follow the pattern + "lowrisc:prim_:", where "" and + "" are placeholders. + """ + + vlnv = _split_vlnv(core[0]) + if (vlnv['vendor'] == 'lowrisc' and + vlnv['library'].startswith('prim_') and + (prim_name is None or vlnv['name'] == prim_name)): + return core + return None + + return dict(filter(_filter_primitives, cores.items())) + + +def _techlibs(prim_cores): + techlibs = set() + for name, info in prim_cores.items(): + vlnv = _split_vlnv(name) + techlibs.add(_library_to_techlib_name(vlnv['library'])) + return techlibs + + +def _library_to_techlib_name(library): + return library[len("prim_"):] + + +def _core_info_for_techlib(prim_cores, techlib): + for name, info in prim_cores.items(): + vlnv = _split_vlnv(name) + if _library_to_techlib_name(vlnv['library']) == techlib: + return (name, info) + + +def _enum_name_for_techlib(techlib_name, qualified=True): + name = "Impl" + techlib_name.capitalize() + if qualified: + name = "prim_pkg::" + name + return name + + +def _top_module_file(core_files, module_name): + module_filename = module_name + '.sv' + for file in core_files: + if os.path.basename(file) == module_filename: + return file + + +def _parse_module_header(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header + + Return a dict with the following entries: + - module_header: the whole module header (including the 'module' keyword) + - package_import_declaration: import declarations + - parameter_port_list: parameter/localparam declarations in the header + - ports: the list of ports. The portlist can be ANSI or non-ANSI style (with + or without signal declarations; see the SV spec for details). + """ + + # TODO: Evaluate different Python SV parsers and use one instead of this + # beautiful regex. A more complete parser would ignore comments, for + # example, or properly handle matching parentheses ... Or write a minimal + # parser that can parse the module header. + # + # Initial evaluations: + # - https://github.com/PyHDI/Pyverilog: + # requires Icarus and Python 3.6 + # - https://kevinpt.github.io/hdlparse/: + # Didn't do anything in my tests. + # - https://github.com/sepandhaghighi/verilogparser: + # Doesn't support SystemVerilog + + # Grammar fragments from the SV2017 spec: + # + # module_nonansi_header ::= + # { attribute_instance } module_keyword [ lifetime ] module_identifier + # { package_import_declaration } [ parameter_port_list ] list_of_ports ; + # module_ansi_header ::= + # { attribute_instance } module_keyword [ lifetime ] module_identifier + # { package_import_declaration }1 [ parameter_port_list ] [ list_of_port_declarations ] + # package_import_declaration ::= + # import package_import_item { , package_import_item } ; + # package_import_item ::= + # package_identifier :: identifier + # | package_identifier :: * + + RE_MODULE_HEADER = ( + r'(?:\s|^)' + r'(?P' # start: capture the whole module header + r'module\s+' # module_keyword + r'(?:(?:static|automatic)\s+)?' # lifetime (optional) + + module_name + # module_identifier + r'\s*(?P(?:import\s+[^;]+;)+)?' # package_import_declaration (optional, skipped) + r'\s*(?:#\s*\((?P[^;]+)\))?' # parameter_port_list (optional) + r'\s*\(\s*(?P[^;]+)\s*\)' # list_of_port_declarations or list_of_ports + r'\s*;' # trailing semicolon + r')' # end: capture the whole module header + ) + + data = "" + with open(generic_impl_filepath, encoding="utf-8") as file: + data = file.read() + re_module_header = re.compile(RE_MODULE_HEADER, re.DOTALL) + matches = re_module_header.search(data) + if not matches: + raise ValueError("Unable to extract module header from %s." % + (generic_impl_filepath, )) + + parameter_port_list = matches.group('parameter_port_list') or '' + return { + 'module_header': + matches.group('module_header').strip(), + 'package_import_declaration': + matches.group('package_import_declaration') or '', + 'parameter_port_list': + parameter_port_list, + 'ports': + matches.group('ports').strip() or '', + 'parameters': + _parse_parameter_port_list(parameter_port_list) + } + + +def _parse_parameter_port_list(parameter_port_list): + """ Parse a list of ports in a module header into individual parameters """ + + # Grammar (SV2017): + # + # parameter_port_list ::= + # # ( list_of_param_assignments { , parameter_port_declaration } ) + # | # ( parameter_port_declaration { , parameter_port_declaration } ) + # | #( ) + # parameter_port_declaration ::= + # parameter_declaration + # | local_parameter_declaration + # | data_type list_of_param_assignments + # | type list_of_type_assignments + + # XXX: Not covering the complete grammar, e.g. `parameter x, y` + RE_PARAMS = ( + r'parameter\s+' + r'(?:[a-zA-Z0-9\]\[:\s\$]+\s+)?' # type + r'(?P\w+)' # name + r'(?:\s*=\s*[^,;]+)' # initial value + ) + re_params = re.compile(RE_PARAMS) + parameters = set() + for m in re_params.finditer(parameter_port_list): + parameters.add(m.group('name')) + return parameters + + +def _check_gapi(gapi): + if not 'cores' in gapi: + print("Key 'cores' not found in GAPI structure. " + "At least FuseSoC 1.11 is needed. " + "Install a compatible version with " + "'pip3 install --user -r python-requirements.txt'.") + return False + return True + + +def _generate_prim_pkg(gapi): + all_prim_cores = _prim_cores(gapi['cores']) + techlibs = _techlibs(all_prim_cores) + + techlib_enums = [] + + # Insert the required generic library first to ensure it gets enum value 0 + techlib_enums.append(_enum_name_for_techlib('generic', qualified=False)) + + for techlib in techlibs: + if techlib == 'generic': + # The generic implementation is required and handled separately. + continue + techlib_enums.append(_enum_name_for_techlib(techlib, qualified=False)) + + # Render prim_pkg.sv file + print("Creating prim_pkg.sv") + prim_pkg_sv_tpl_filepath = os.path.join(os.path.dirname(__file__), + 'primgen', 'prim_pkg.sv.tpl') + prim_pkg_sv_tpl = Template(filename=prim_pkg_sv_tpl_filepath) + + prim_pkg_sv = prim_pkg_sv_tpl.render(encoding="utf-8", + techlib_enums=techlib_enums) + with open('prim_pkg.sv', 'w') as f: + f.write(prim_pkg_sv) + + # Copy prim_pkg.core (no changes needed) + prim_pkg_core_src = os.path.join(os.path.dirname(__file__), 'primgen', + 'prim_pkg.core.tpl') + prim_pkg_core_dest = 'prim_pkg.core' + shutil.copyfile(prim_pkg_core_src, prim_pkg_core_dest) + print("Core file written to %s." % (prim_pkg_core_dest, )) + + +def _instance_sv(prim_name, techlib, parameters): + s = "prim_{techlib}_{prim_name} #(\n" + s += ", ".join(".{p}({p})".format(p=p) for p in parameters) + s += ") u_impl_{techlib} (\n" \ + " .*\n" \ + ");\n" + return s.format(prim_name=prim_name, techlib=techlib) + + +def _create_instances(prim_name, techlibs, parameters): + """ Build SystemVerilog code instantiating primitives from the techlib """ + techlibs_wo_generic = [ + techlib for techlib in techlibs if techlib != 'generic' + ] + techlibs_generic_last = techlibs_wo_generic + ['generic'] + + if not techlibs_wo_generic: + # Don't output the if/else blocks if there no alternatives exist. + # We still want the generate block to keep hierarchical path names + # stable, even if more than one techlib is found. + s = " if (1) begin : gen_generic\n" + s += _instance_sv(prim_name, "generic", parameters) + "\n" + s += "end" + return s + + nr_techlibs = len(techlibs_generic_last) + out = "" + for pos, techlib in enumerate(techlibs_generic_last): + is_first = pos == 0 + is_last = pos == nr_techlibs - 1 + + s = "" + if not is_first: + s += "else " + if not is_last: + s += "if (Impl == {techlib_enum}) " + + # TODO: wildcard port lists are against our style guide, but it's safer + # to let the synthesis tool figure out the connectivity than us trying + # to parse the port list into individual signals. + s += "begin : gen_{techlib}\n" + _instance_sv(prim_name, techlib, + parameters) + "end" + + if not is_last: + s += " " + + out += s.format(prim_name=prim_name, + techlib=techlib, + techlib_enum=_enum_name_for_techlib(techlib)) + return out + + +def _generate_abstract_impl(gapi): + prim_name = gapi['parameters']['prim_name'] + prim_cores = _prim_cores(gapi['cores'], prim_name) + + techlibs = _techlibs(prim_cores) + + if not 'generic' in techlibs: + raise ValueError("Techlib generic is required, but not found for " + "primitive %s." % prim_name) + print("Implementations for primitive %s: %s" % + (prim_name, ', '.join(techlibs))) + + # Extract port list out of generic implementation + generic_core = _core_info_for_techlib(prim_cores, 'generic')[1] + generic_module_name = 'prim_generic_' + prim_name + top_module_filename = _top_module_file(generic_core['files'], + generic_module_name) + top_module_file = os.path.join(generic_core['core_root'], + top_module_filename) + + print("Inspecting generic module %s" % (top_module_file, )) + generic_hdr = _parse_module_header(top_module_file, generic_module_name) + + # Render abstract primitive HDL from template + print("Creating SystemVerilog module for abstract primitive") + abstract_prim_sv_tpl_filepath = os.path.join(os.path.dirname(__file__), + 'primgen', + 'abstract_prim.sv.tpl') + abstract_prim_sv_tpl = Template(filename=abstract_prim_sv_tpl_filepath) + + abstract_prim_sv = abstract_prim_sv_tpl.render( + encoding="utf-8", + prim_name=prim_name, + module_header_imports=generic_hdr['package_import_declaration'], + module_header_params=generic_hdr['parameter_port_list'], + module_header_ports=generic_hdr['ports'], + # Creating the code to instantiate the primitives in the Mako templating + # language is tricky to do; do it in Python instead. + instances=_create_instances(prim_name, techlibs, + generic_hdr['parameters'])) + abstract_prim_sv_filepath = 'prim_%s.sv' % (prim_name) + with open(abstract_prim_sv_filepath, 'w') as f: + f.write(abstract_prim_sv) + print("Abstract primitive written to %s" % + (os.path.abspath(abstract_prim_sv_filepath), )) + + # Create core file depending on all primitive implementations we have in the + # techlibs. + print("Creating core file for primitive %s." % (prim_name, )) + abstract_prim_core_filepath = os.path.abspath('prim_%s.core' % (prim_name)) + dependencies = [] + dependencies.append('lowrisc:prim:prim_pkg') + dependencies += [ + _core_info_for_techlib(prim_cores, t)[0] for t in techlibs + ] + abstract_prim_core = { + 'name': "lowrisc:prim_abstract:%s" % (prim_name, ), + 'filesets': { + 'files_rtl': { + 'depend': dependencies, + 'files': [ + abstract_prim_sv_filepath, + ], + 'file_type': 'systemVerilogSource' + }, + }, + 'targets': { + 'default': { + 'filesets': [ + 'files_rtl', + ], + }, + }, + } + with open(abstract_prim_core_filepath, 'w') as f: + # FuseSoC requires this line to appear first in the YAML file. + # Inserting this line through the YAML serializer requires ordered dicts + # to be used everywhere, which is annoying syntax-wise on Python <3.7, + # where native dicts are not sorted. + f.write('CAPI=2:\n') + yaml.dump(abstract_prim_core, + f, + encoding="utf-8", + default_flow_style=False, + sort_keys=False) + print("Core file written to %s" % (abstract_prim_core_filepath, )) + + +def _get_action_from_gapi(gapi, default_action): + if 'parameters' in gapi and 'action' in gapi['parameters']: + return gapi['parameters']['action'] + return default_action + + +def main(): + gapi_filepath = sys.argv[1] + gapi = yaml.load(open(gapi_filepath), Loader=YamlLoader) + + if not _check_gapi(gapi): + sys.exit(1) + + action = _get_action_from_gapi(gapi, 'generate_abstract_impl') + + if action == 'generate_abstract_impl': + return _generate_abstract_impl(gapi) + elif action == 'generate_prim_pkg': + return _generate_prim_pkg(gapi) + else: + raise ValueError("Invalid action: %s" % (action, )) + + +if __name__ == '__main__': + main() diff --git a/vendor/lowrisc_ip/prim/util/primgen/abstract_prim.sv.tpl b/vendor/lowrisc_ip/prim/util/primgen/abstract_prim.sv.tpl new file mode 100644 index 0000000000..6a2bbd979d --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/primgen/abstract_prim.sv.tpl @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is auto-generated. + +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif + +module prim_${prim_name} +${module_header_imports} +#( +${module_header_params} +) ( + ${module_header_ports} +); + parameter prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; + +${instances} + +endmodule diff --git a/shared/prim_generic_ram_1p.core b/vendor/lowrisc_ip/prim/util/primgen/prim_pkg.core.tpl similarity index 71% rename from shared/prim_generic_ram_1p.core rename to vendor/lowrisc_ip/prim/util/primgen/prim_pkg.core.tpl index f43fe2ce17..4748315578 100644 --- a/shared/prim_generic_ram_1p.core +++ b/vendor/lowrisc_ip/prim/util/primgen/prim_pkg.core.tpl @@ -2,13 +2,13 @@ CAPI=2: # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 +name: "lowrisc:prim_abstract:prim_pkg:0.1" +description: "Constants used by the primitives" -name: "lowrisc:prim_generic:ram_1p" -description: "Single port RAM" filesets: files_rtl: files: - - rtl/prim_generic_ram_1p.sv + - prim_pkg.sv file_type: systemVerilogSource targets: diff --git a/vendor/lowrisc_ip/prim/util/primgen/prim_pkg.sv.tpl b/vendor/lowrisc_ip/prim/util/primgen/prim_pkg.sv.tpl new file mode 100644 index 0000000000..7974e5ad88 --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/primgen/prim_pkg.sv.tpl @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Constants for use in primitives + +// This file is auto-generated. + +package prim_pkg; + + // Implementation target specialization + typedef enum integer { + ${',\n '.join(techlib_enums)} + } impl_e; +endpackage : prim_pkg diff --git a/vendor/lowrisc_ip/prim/util/secded_gen.py b/vendor/lowrisc_ip/prim/util/secded_gen.py new file mode 100755 index 0000000000..f934216e1c --- /dev/null +++ b/vendor/lowrisc_ip/prim/util/secded_gen.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +r"""SECDED encoder/decoder generator + +Current version doesn't optimize Fan-In. It uses Hsiao code (modified version +of Hamming code + parity). Please refer https://arxiv.org/pdf/0803.1217.pdf +""" + +# TODO: Add FPV assertions in the encoder/decoder module + +import argparse +import itertools +import logging as log +import math +import os +import random +import sys +import time +from pathlib import PurePath + +COPYRIGHT = """// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +""" + + +def min_paritysize(k): + # SECDED --> Hamming distance 'd': 4 + # 2^(m-1) should cover (m+k) + for m in range(2, 10): + if 2**m >= (k + m + 1): + return m + 1 + return -1 + + +def ideal_fanin(k, m): + """Compute Ideal Max Fanin of any bit in the ecc codes.""" + fanin = 0 + needed = k + for select in range(3, m + 1, 2): + combinations = list(itertools.combinations(range(m), select)) + if len(combinations) <= needed: + fanin += int(math.ceil(float(len(combinations) * select) / m)) + needed -= len(combinations) + else: + fanin += int(math.ceil(float(needed * select) / m)) + needed = 0 + if not needed: + break + return fanin + + +def calc_fanin(width, codes): + """Sum the ones in a column""" + fanins = [0] * width + log.info("Calc Code: {}".format(codes)) + for i in codes: + for e in i: + fanins[e] += 1 + + return fanins + + +def print_comb(n, + k, + m, + cur_m, + codes, + start_cnt, + max_width=100, + prefix="", + first_indent=0): + """Print XOR comb. + + @param[max_width] Maximum Width of str + @param[prefix] The prepend string at the first line + @param[first_indent] The number of character that indented at the first line + e.g. first_indent := 2 + {prefix}in[nn] ... + ^ in[nn] ^ in[nn] + + result: + {prefix}in[nn] ^ ... in[nn] + ^ in[nn] ^ ... in[nn]; + """ + outstr = "" + line = prefix + prepend_len = len(prefix) + cnt = start_cnt + first = True + for j in range(k): + temp_str = "" + if cur_m in codes[j]: + if not first: + temp_str += " ^" + if first: + first = False + temp_str += " in[%d]" % (j) + temp_len = len(temp_str) + + if len(line) + temp_len > max_width: + outstr += line + "\n" + line = ' ' * (prepend_len - first_indent) + temp_str + else: + line += temp_str + outstr += line + ";\n" + return outstr + + +def print_enc(n, k, m, codes): + outstr = "" + for i in range(k): + outstr += " assign out[%d] = in[%d] ;\n" % (i, i) + + for i in range(m): + # Print parity computation + outstr += print_comb(n, k, m, i, codes, 0, 100, + " assign out[%d] =" % (i + k), 2) + return outstr + + +def calc_syndrome(code): + return sum(map((lambda x: 2**x), code)) + + +def print_dec(n, k, m, codes): + outstr = "" + outstr += " logic single_error;\n" + outstr += "\n" + outstr += " // Syndrome calculation\n" + for i in range(m): + # Print combination + outstr += print_comb(n, k, m, i, codes, 1, 100, + " assign syndrome_o[%d] = in[%d] ^" % (i, k + i), + len(" in[%d] ^" % (k + i)) + 2) + + outstr += "\n" + outstr += " // Corrected output calculation\n" + for i in range(k): + synd_v = calc_syndrome(codes[i]) + outstr += " assign d_o[%d] = (syndrome_o == %d'h%x) ^ in[%d];\n" % ( + i, m, calc_syndrome(codes[i]), i) + outstr += "\n" + outstr += " // err_o calc. bit0: single error, bit1: double error\n" + outstr += " assign single_error = ^syndrome_o;\n" + outstr += " assign err_o[0] = single_error;\n" + outstr += " assign err_o[1] = ~single_error & (|syndrome_o);\n" + return outstr + + +def main(): + parser = argparse.ArgumentParser( + prog="secded_gen", + description='''This tool generates Single Error Correction Double Error + Detection(SECDED) encoder and decoder modules in SystemVerilog. + ''') + parser.add_argument( + '-m', + type=int, + default=7, + help= + 'parity length. If fan-in is too big, increasing m helps. (default: %(default)s)' + ) + parser.add_argument( + '-k', + type=int, + default=32, + help= + 'code length. Minimum \'m\' is calculated by the tool (default: %(default)s)' + ) + parser.add_argument( + '--outdir', + default='../rtl', + help= + 'output directory. The output file will be named `prim_secded___enc/dec.sv` (default: %(default)s)' + ) + parser.add_argument('--verbose', '-v', action='store_true', help='Verbose') + + args = parser.parse_args() + + if (args.verbose): + log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) + else: + log.basicConfig(format="%(levelname)s: %(message)s") + + # Error checking + if (args.k <= 1 or args.k > 120): + log.error("Current tool doesn't support the value k (%d)", args.k) + k = args.k + + if (args.m <= 1 or args.m > 20): + log.error("Current tool doesn't support the value m (%d)", args.m) + + # Calculate 'm' (parity size) + min_m = min_paritysize(k) + if (args.m < min_m): + log.error("given \'m\' argument is smaller than minimum requirement") + m = min_m + else: + m = args.m + + n = m + k + log.info("n(%d), k(%d), m(%d)", n, k, m) + + random.seed(time.time()) + + # using itertools combinations, generate odd number of 1 in a row + + required_row = k # k rows are needed, decreasing everytime when it acquite + + fanin_ideal = ideal_fanin(k, m) + log.info("Ideal Fan-In value: %d" % fanin_ideal) + + # Each entry represents a row in below parity matrix + # Entry is tuple and the value inside is the position of ones + # e.g. (0,1,2) in m:=7 + # row -> [1 1 1 0 0 0 0] + codes = [] + + ## Find code matrix ======================================================= + # This is main part to find the parity matrix. + # For example, find SECDED for 4bit message is to find 4x4 matrix as below + # | 1 0 0 0 x x x x | + # | 0 1 0 0 x x x x | + # | 0 0 1 0 x x x x | + # | 0 0 0 1 x x x x | + # Then message _k_ X matrix_code ==> original message with parity + # + # Make a row to have even number of 1 including the I matrix. + # This helps to calculate the syndrom at the decoding stage. + # To reduce the max fan-in, Starting with smallest number 3. + # the number means the number of one in a row. + # Small number of ones means smaller fan-in overall. + + for step in range(3, m + 1, 2): + # starting from 3 as I matrix represents data + # Increased by 2 as number of 1 should be even in a row (odd excluding I) + + # get the list of combinations [0, .., m-1] with `step` + # e.g. step := 3 ==> [(0,1,2), (0,1,3), ... ] + candidate = list(itertools.combinations(range(m), step)) + + if len(candidate) <= required_row: + # we need more round use all of them + codes.extend(candidate) + required_row -= len(candidate) + else: + ## Find optimized fan-in ========================================== + + # Calculate each row fan-in with current + fanins = calc_fanin(m, codes) + while required_row != 0: + # Let's shuffle + # Shuffling makes the sequence randomized --> it reduces the + # fanin as the code takes randomly at the end of the round + + # TODO: There should be a clever way to find the subset without + # random retrying. + # Suggested this algorithm + # https://en.wikipedia.org/wiki/Assignment_problem + random.shuffle(candidate) + + # Take a subset + subset = candidate[0:required_row] + + subset_fanins = calc_fanin(m, subset) + # Check if it exceeds Ideal Fan-In + ideal = True + for i in range(m): + if fanins[i] + subset_fanins[i] > fanin_ideal: + # Exceeded. Retry + ideal = False + break + + if ideal: + required_row = 0 + + # Append to the code matrix + codes.extend(subset) + + if required_row == 0: + # Found everything! + break + + log.info(codes) + + # Print Encoder + enc_out = print_enc(n, k, m, codes) + #log.info(enc_out) + + module_name = "prim_secded_%d_%d" % (n, k) + + with open(args.outdir + "/" + module_name + "_enc.sv", "w") as f: + f.write(COPYRIGHT) + f.write("// SECDED Encoder generated by secded_gen.py\n\n") + + f.write("module " + module_name + "_enc (\n") + f.write(" input [%d:0] in,\n" % (k - 1)) + f.write(" output logic [%d:0] out\n" % (n - 1)) + f.write(");\n\n") + f.write(enc_out) + f.write("endmodule\n\n") + + dec_out = print_dec(n, k, m, codes) + + with open(args.outdir + "/" + module_name + "_dec.sv", "w") as f: + f.write(COPYRIGHT) + f.write("// SECDED Decoder generated by secded_gen.py\n\n") + + f.write("module " + module_name + "_dec (\n") + f.write(" input [%d:0] in,\n" % (n - 1)) + f.write(" output logic [%d:0] d_o,\n" % (k - 1)) + f.write(" output logic [%d:0] syndrome_o,\n" % (m - 1)) + f.write(" output logic [1:0] err_o\n") + f.write(");\n\n") + f.write(dec_out) + f.write("endmodule\n\n") + + +if __name__ == "__main__": + main() diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.waiver new file mode 100644 index 0000000000..8cc0fa5523 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_gating.waiver @@ -0,0 +1,9 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# primitives: prim_clock_gating +waive -rules LATCH -location {prim_generic_clock_gating.sv} -regexp {'en_latch' is a latch} \ + -comment "clock gating cell creates a latch" +waive -rules COMBO_NBA -location {prim_generic_clock_gating.sv} -regexp {Non-blocking assignment to 'en_latch'} \ + -comment "clock gating cell creates a latch" diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.waiver new file mode 100644 index 0000000000..0815756a74 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_clock_mux2.waiver @@ -0,0 +1,5 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_clock_mux2 diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.waiver new file mode 100644 index 0000000000..16bdd05d65 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_flash.waiver @@ -0,0 +1,5 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_generic_flash diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.waiver new file mode 100644 index 0000000000..08eeadef2d --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_otp.waiver @@ -0,0 +1,5 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# + diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.waiver new file mode 100644 index 0000000000..fb823f36f9 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_pad_wrapper.waiver @@ -0,0 +1,17 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_generic_pad_wrapper +# note that this code is NOT synthesizable and meant for sim only + +waive -rules TRI_DRIVER -regexp {'inout_io' is driven by a tristate driver} -location {prim_generic_pad_wrapper.sv} \ + -comment "This is a bidirectional pad inout." +waive -rules MULTI_DRIVEN -regexp {.* drivers on 'inout_io' here} -location {prim_generic_pad_wrapper.sv} \ + -comment "The pad simulation model has multiple drivers to emulate different IO terminations." +waive -rules SELF_ASSIGN -regexp {LHS signal 'inout_io' encountered on the RHS of a continuous assignment statement} -location {prim_generic_pad_wrapper.sv} \ + -comment "This implements a keeper termination (it's basically an explicit TRIREG)" +waive -rules DRIVE_STRENGTH -regexp {Drive strength .* encountered on assignment to 'inout_io'} -location {prim_generic_pad_wrapper.sv} \ + -comment "The pad simulation model uses driving strength attributes to emulate different IO terminations." +waive -rules INPUT_NOT_READ -regexp {Input port 'attr\_i\[.:6\]' is not read from} -location {prim_generic_pad_wrapper.sv}\ + -comment "Some IO attributes may not be implemented." diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.waiver new file mode 100644 index 0000000000..d800ceecdc --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_1p.waiver @@ -0,0 +1,12 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_generic_ram_[1,2]p + +waive -rules MULTI_PROC_ASSIGN -location {prim_generic_ram_2p.sv} -regexp {Assignment to 'storage' from more than one block} \ + -comment "That is the nature of a dual-port memory: both write ports can access the same storage simultaneously" +waive -rules ALWAYS_SPEC -location {prim_generic_ram_*p.sv} -regexp {Edge triggered block may be more accurately modeled as always_ff} \ + -comment "Vivado requires here an always instead of always_ff" +waive -rules HIER_NET_NOT_READ -regexp {Connected net '(addr|wdata)_i' at prim_generic_ram_1p.sv.* is not read from in module 'prim_generic_ram_1p'} \ + -comment "Ascentlint blackboxes very deep RAMs to speed up runtime. This blacboxing causes above lint errors." diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.waiver new file mode 100644 index 0000000000..d800ceecdc --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_ram_2p.waiver @@ -0,0 +1,12 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_generic_ram_[1,2]p + +waive -rules MULTI_PROC_ASSIGN -location {prim_generic_ram_2p.sv} -regexp {Assignment to 'storage' from more than one block} \ + -comment "That is the nature of a dual-port memory: both write ports can access the same storage simultaneously" +waive -rules ALWAYS_SPEC -location {prim_generic_ram_*p.sv} -regexp {Edge triggered block may be more accurately modeled as always_ff} \ + -comment "Vivado requires here an always instead of always_ff" +waive -rules HIER_NET_NOT_READ -regexp {Connected net '(addr|wdata)_i' at prim_generic_ram_1p.sv.* is not read from in module 'prim_generic_ram_1p'} \ + -comment "Ascentlint blackboxes very deep RAMs to speed up runtime. This blacboxing causes above lint errors." diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.vlt b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.waiver b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.waiver new file mode 100644 index 0000000000..9d0ded359c --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/lint/prim_generic_rom.waiver @@ -0,0 +1,9 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_generic_rom + +waive -rules NOT_DRIVEN -location {prim_generic_rom.sv} -regexp {Signal 'mem' has no driver in module 'prim_generic_rom'} \ + -comment "since this is a ROM, the signal mem has no driver, but it is populated using an initialization file" + diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_clock_gating.core b/vendor/lowrisc_ip/prim_generic/prim_generic_clock_gating.core new file mode 100644 index 0000000000..6a5423d571 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_clock_gating.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:clock_gating" +description: "prim" +filesets: + files_rtl: + files: + - rtl/prim_generic_clock_gating.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_gating.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_gating.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_clock_mux2.core b/vendor/lowrisc_ip/prim_generic/prim_generic_clock_mux2.core new file mode 100644 index 0000000000..3839290681 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_clock_mux2.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:clock_mux2" +description: "two-input clock multiplexer primitive" +filesets: + files_rtl: + files: + - rtl/prim_generic_clock_mux2.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_mux2.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_mux2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_flash.core b/vendor/lowrisc_ip/prim_generic/prim_generic_flash.core new file mode 100644 index 0000000000..9e9f74163b --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_flash.core @@ -0,0 +1,44 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:flash" +description: "prim" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_1p + files: + - rtl/prim_generic_flash.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_flash.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_flash.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_otp.core b/vendor/lowrisc_ip/prim_generic/prim_generic_otp.core new file mode 100644 index 0000000000..3d50166023 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_otp.core @@ -0,0 +1,38 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:otp" +description: "Technology-independent One-Time Programmable (OTP) memory emulation" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + files: + - rtl/prim_generic_otp.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_otp.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_otp.waiver + file_type: waiver + + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_pad_wrapper.core b/vendor/lowrisc_ip/prim_generic/prim_generic_pad_wrapper.core new file mode 100644 index 0000000000..3e2369977f --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_pad_wrapper.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:pad_wrapper" +description: "Technology-independent pad wrapper implementation (for sim only!)" +filesets: + files_rtl: + files: + - rtl/prim_generic_pad_wrapper.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_pad_wrapper.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_pad_wrapper.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_ram_1p.core b/vendor/lowrisc_ip/prim_generic/prim_generic_ram_1p.core new file mode 100644 index 0000000000..5863ab7e14 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_ram_1p.core @@ -0,0 +1,44 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:ram_1p" +description: "Single port RAM" +filesets: + files_rtl: + depend: + - lowrisc:prim:util_memload + files: + - rtl/prim_generic_ram_1p.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_1p.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_1p.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_ram_2p.core b/vendor/lowrisc_ip/prim_generic/prim_generic_ram_2p.core new file mode 100644 index 0000000000..9dae48832c --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_ram_2p.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:ram_2p" +description: "prim" +filesets: + files_rtl: + files: + - rtl/prim_generic_ram_2p.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_2p.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_2p.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/prim_generic_rom.core b/vendor/lowrisc_ip/prim_generic/prim_generic_rom.core new file mode 100644 index 0000000000..aee093ab07 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/prim_generic_rom.core @@ -0,0 +1,45 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_generic:rom" +description: "Technology-independent Read-Only Memory (ROM) implementation" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util_memload + files: + - rtl/prim_generic_rom.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_rom.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_rom.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv new file mode 100644 index 0000000000..376c38efcf --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_gating.sv @@ -0,0 +1,23 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Common Library: Clock Gating cell + +module prim_generic_clock_gating ( + input clk_i, + input en_i, + input test_en_i, + output logic clk_o +); + + // Assume en_i synchronized, if not put synchronizer prior to en_i + logic en_latch; + always_latch begin + if (!clk_i) begin + en_latch = en_i | test_en_i; + end + end + assign clk_o = en_latch & clk_i; + +endmodule diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_mux2.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_mux2.sv new file mode 100644 index 0000000000..668c832bb4 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_clock_mux2.sv @@ -0,0 +1,22 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module prim_generic_clock_mux2 ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + + assign clk_o = (sel_i) ? clk1_i : clk0_i; + + // make sure sel is never X (including during reset) + // need to use ##1 as this could break with inverted clocks that + // start with a rising edge at the beginning of the simulation. + `ASSERT(selKnown0, ##1 !$isunknown(sel_i), clk0_i, 0) + `ASSERT(selKnown1, ##1 !$isunknown(sel_i), clk1_i, 0) + +endmodule : prim_generic_clock_mux2 diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv new file mode 100644 index 0000000000..6a0655e0a3 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_flash.sv @@ -0,0 +1,287 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// prim flash module - Emulated using memory +// + +module prim_generic_flash #( + parameter int PagesPerBank = 256, // pages per bank + parameter int WordsPerPage = 256, // words per page + parameter int DataWidth = 32, // bits per word + parameter bit SkipInit = 1, // this is an option to reset flash to all F's at reset + + // Derived parameters + localparam int PageW = $clog2(PagesPerBank), + localparam int WordW = $clog2(WordsPerPage), + localparam int AddrW = PageW + WordW +) ( + input clk_i, + input rst_ni, + input rd_i, + input prog_i, + input pg_erase_i, + input bk_erase_i, + input [AddrW-1:0] addr_i, + input [DataWidth-1:0] prog_data_i, + output logic ack_o, + output logic [DataWidth-1:0] rd_data_o, + output logic init_busy_o +); + + // Emulated flash macro values + localparam int ReadCycles = 1; + localparam int ProgCycles = 50; + localparam int PgEraseCycles = 200; + localparam int BkEraseCycles = 2000; + + // Locally derived values + localparam int WordsPerBank = PagesPerBank * WordsPerPage; + + typedef enum logic [2:0] { + StReset = 'h0, + StInit = 'h1, + StIdle = 'h2, + StRead = 'h3, + StProg = 'h4, + StErase = 'h5 + } state_e; + + state_e st_q, st_d; + + logic [31:0] time_cnt; + logic [31:0] index_cnt; + logic time_cnt_inc ,time_cnt_clr, time_cnt_set1; + logic index_cnt_inc, index_cnt_clr; + logic [31:0] index_limit_q, index_limit_d; + logic [31:0] time_limit_q, time_limit_d; + logic prog_pend_q, prog_pend_d; + logic mem_req; + logic mem_wr; + logic [AddrW-1:0] mem_addr; + logic [DataWidth-1:0] held_rdata; + logic [DataWidth-1:0] held_wdata; + logic [DataWidth-1:0] mem_wdata; + logic hold_cmd; + logic [AddrW-1:0] held_addr; + + // insert a fifo here to break the large fanout from inputs to memories on reads + logic rd_q; + logic [AddrW-1:0] addr_q; + + prim_fifo_sync #( + .Width (AddrW), + .Pass (0), + .Depth (2) + ) i_slice ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid (rd_i), + .wready (), + .wdata (addr_i), + .depth (), + .rvalid (rd_q), + .rready (hold_cmd), //whenver command is held, pop + .rdata (addr_q) + ); + + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) st_q <= StReset; + else st_q <= st_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + held_addr <= '0; + held_wdata <= '0; + end else if (hold_cmd) begin + held_addr <= rd_q ? addr_q : addr_i; + held_wdata <= prog_data_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + time_limit_q <= 32'h0; + index_limit_q <= 32'h0; + prog_pend_q <= 1'h0; + end else begin + time_limit_q <= time_limit_d; + index_limit_q <= index_limit_d; + prog_pend_q <= prog_pend_d; + end + end + + // prog_pend_q is necessary to emulate flash behavior that a bit written to 0 cannot be written + // back to 1 without an erase + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + time_cnt <= 32'h0; + index_cnt <= 32'h0; + held_rdata <= 'h0; + end else begin + if (time_cnt_inc) time_cnt <= time_cnt + 1'b1; + else if (time_cnt_set1) time_cnt <= 32'h1; + else if (time_cnt_clr) time_cnt <= 32'h0; + + if (index_cnt_inc) index_cnt <= index_cnt + 1'b1; + else if (index_cnt_clr) index_cnt <= 32'h0; + + if (prog_pend_q) held_rdata <= rd_data_o; + + end + end + + + always_comb begin + // state + st_d = st_q; + + // internally consumed signals + index_limit_d = index_limit_q; + time_limit_d = time_limit_q; + prog_pend_d = prog_pend_q; + mem_req = 'h0; + mem_wr = 'h0; + mem_addr = 'h0; + mem_wdata = 'h0; + time_cnt_inc = 1'h0; + time_cnt_clr = 1'h0; + time_cnt_set1 = 1'h0; + index_cnt_inc = 1'h0; + index_cnt_clr = 1'h0; + hold_cmd = 1'h0; + + // i/o + init_busy_o = 1'h0; + ack_o = 1'h0; + + unique case (st_q) + StReset: begin + init_busy_o = 1'h1; + st_d = StInit; + end + // Emulate flash power up to all 1's + // This implies this flash will not survive a reset + // Might need a different RESET for FPGA purposes + StInit: begin + init_busy_o = 1'h1; + if (index_cnt < WordsPerBank && !SkipInit) begin + st_d = StInit; + index_cnt_inc = 1'b1; + mem_req = 1'h0; + mem_wr = 1'h0; + mem_addr = index_cnt[AddrW-1:0]; + mem_wdata = {DataWidth{1'b1}}; + end else begin + st_d = StIdle; + index_cnt_clr = 1'b1; + end + end + StIdle: begin + if (rd_q) begin + // reads begin immediately + hold_cmd = 1'b1; + mem_addr = addr_q; + mem_req = 1'b1; + time_cnt_inc = 1'b1; + st_d = StRead; + end else if (prog_i) begin + hold_cmd = 1'b1; + st_d = StRead; + prog_pend_d = 1'b1; + end else if (pg_erase_i) begin + hold_cmd = 1'b1; + st_d = StErase; + index_limit_d = WordsPerPage; + time_limit_d = PgEraseCycles; + end else if (bk_erase_i) begin + hold_cmd = 1'b1; + st_d = StErase; + index_limit_d = WordsPerBank; + time_limit_d = BkEraseCycles; + end + end + StRead: begin + mem_addr = held_addr; + if (time_cnt < ReadCycles) begin + mem_req = 1'b1; + time_cnt_inc = 1'b1; + end else if (!prog_pend_q) begin + ack_o = 1'b1; //finish up transaction + + // if another request already pending + if (rd_q) begin + hold_cmd = 1'b1; + mem_addr = addr_q; + mem_req = 1'b1; + time_cnt_set1 = 1'b1; + st_d = StRead; + end else begin + time_cnt_clr = 1'b1; + st_d = StIdle; + end + end else if (prog_pend_q) begin + // this is the read performed before a program operation + prog_pend_d = 1'b0; + time_cnt_clr = 1'b1; + st_d = StProg; + end + end + StProg: begin + mem_addr = held_addr; + + // if data is already 0, cannot program to 1 without erase + mem_wdata = held_wdata & held_rdata; + if (time_cnt < ProgCycles) begin + mem_req = 1'b1; + mem_wr = 1'b1; + time_cnt_inc = 1'b1; + end else begin + st_d = StIdle; + ack_o = 1'b1; + time_cnt_clr = 1'b1; + end + end + StErase: begin + // Actual erasing of the page + if (index_cnt < index_limit_q || time_cnt < time_limit_q) begin + mem_req = 1'b1; + mem_wr = 1'b1; + mem_wdata = {DataWidth{1'b1}}; + + mem_addr = held_addr + index_cnt[AddrW-1:0]; + time_cnt_inc = (time_cnt < time_limit_q); + index_cnt_inc = (index_cnt < index_limit_q); + end else begin + st_d = StIdle; + ack_o = 1'b1; + time_cnt_clr = 1'b1; + index_cnt_clr = 1'b1; + end + end + default: begin + st_d = StIdle; + end + endcase // unique case (st_q) + end // always_comb + + prim_ram_1p #( + .Width(DataWidth), + .Depth(WordsPerBank), + .DataBitsPerMask(DataWidth) + ) u_mem ( + .clk_i, + .req_i (mem_req), + .write_i (mem_wr), + .addr_i (mem_addr), + .wdata_i (mem_wdata), + .wmask_i ({DataWidth{1'b1}}), + .rdata_o (rd_data_o) + ); + + + +endmodule // prim_generic_flash diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_otp.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_otp.sv new file mode 100644 index 0000000000..4d3f3831c3 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_otp.sv @@ -0,0 +1,110 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module prim_generic_otp #( + parameter int Width = 8, + parameter int Depth = 1024, + parameter int ErrWidth = 8, + localparam int AddrWidth = $clog2(Depth) +) ( + input clk_i, + input rst_ni, + // TODO: power sequencing signals from/to AST + // Test interface + input tlul_pkg::tl_h2d_t test_tl_i, + output tlul_pkg::tl_d2h_t test_tl_o, + // Start Macro init sequence + // init shall be asserted until acknowledged with done + input init_req_i, + output logic init_done_o, + // Macro error output + // TODO: define error codes + output logic err_valid_o, + output logic [ErrWidth-1:0] err_code_o, + // Ready valid handshake for read/write command + output logic ready_o, + input valid_i, + input [AddrWidth-1:0] addr_i, + input [Width-1:0] wdata_i, + input wren_i, // 0: read command, 1: write command + // Read data output + output logic [Width-1:0] rdata_o, + output logic rvalid_o +); + + /////////////////// + // Control logic // + /////////////////// + + logic req, write_d, write_q, read_d, read_q; + logic [Width-1:0] wdata, wdata_d, wdata_q; + logic [AddrWidth-1:0] addr, waddr_d, waddr_q; + + // TODO: add support for these in emulation + assign test_tl_o = '0; + assign err_valid_o = '0; + assign err_type_o = '0; + assign init_done_o = 1'b1; + + // TODO: randomize / extend access times + assign read_d = ready_o & valid_i & ~wren_i; + assign write_d = ready_o & valid_i & wren_i; + assign wdata_d = (write_d) ? wdata_i : wdata_q; + assign waddr_d = (write_d) ? addr_i : waddr_q; + + assign ready_o = ~write_q; + assign rvalid_o = read_q; + + // perform read modify write and OR bits on top + // of existing data + assign wdata = rdata_o | wdata_q; + assign addr = (write_q) ? waddr_q : addr_i; + assign req = read_d | write_d | write_q; + + ////////////////////////////////// + // Emulate using memory process // + ////////////////////////////////// + + logic [Width-1:0] mem [Depth]; + + // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error + // thrown when using $readmemh system task to backdoor load an image + always @(posedge clk_i) begin + if (req) begin + if (write_q) begin + mem[addr] <= wdata; + end else begin + rdata_o <= mem[addr]; + end + end + end + + // TODO: add verilator init mechanism + `ifdef OTP_INIT_FILE + localparam MEM_FILE = `PRIM_STRINGIFY(`OTP_INIT_FILE); + initial begin + $display("Initializing OTP from %s", MEM_FILE); + $readmemh(MEM_FILE, mem); + end + `endif + + ////////// + // Regs // + ////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + read_q <= 1'b0; + write_q <= 1'b0; + wdata_q <= '0; + waddr_q <= '0; + end else begin + read_q <= read_d; + write_q <= write_d; + wdata_q <= wdata_d; + waddr_q <= waddr_d; + end + end + +endmodule : prim_generic_otp diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_pad_wrapper.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_pad_wrapper.sv new file mode 100644 index 0000000000..ecf3354c82 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_pad_wrapper.sv @@ -0,0 +1,53 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic, technology independent pad wrapper. This is NOT synthesizable! + + +`include "prim_assert.sv" + +module prim_generic_pad_wrapper #( + parameter int unsigned AttrDw = 6 +) ( + inout wire inout_io, // bidirectional pad + output logic in_o, // input data + input out_i, // output data + input oe_i, // output enable + // additional attributes {drive strength, keeper, pull-up, pull-down, open-drain, invert} + input [AttrDw-1:0] attr_i +); + + // get pad attributes + logic kp, pu, pd, od, inv; + typedef enum logic {STRONG_DRIVE = 1'b0, WEAK_DRIVE = 1'b1} drv_e; + drv_e drv; + assign {drv, kp, pu, pd, od, inv} = attr_i[5:0]; + + // input inversion + assign in_o = inv ^ inout_io; + + // virtual open drain emulation + logic oe, out; + assign out = out_i ^ inv; + assign oe = oe_i & ((od & ~out) | ~od); + +// driving strength attributes are not supported by verilator +`ifdef VERILATOR + assign inout_io = (oe) ? out : 1'bz; +`else + // different driver types + assign (strong0, strong1) inout_io = (oe && drv == STRONG_DRIVE) ? out : 1'bz; + assign (pull0, pull1) inout_io = (oe && drv == WEAK_DRIVE) ? out : 1'bz; + // pullup / pulldown termination + // default to high-Z in case both PU and PD are asserted (safety mechanism). + assign (highz0, weak1) inout_io = pu & ~pd; + assign (weak0, highz1) inout_io = pu | ~pd; + // fake trireg emulation + assign (weak0, weak1) inout_io = (kp) ? inout_io : 1'bz; +`endif + + // assertions + `ASSERT_INIT(AttrDwCheck_A, AttrDw >= 7) + +endmodule : prim_generic_pad_wrapper diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv new file mode 100644 index 0000000000..867a72d8f1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_1p.sv @@ -0,0 +1,58 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous single-port SRAM model + +`include "prim_assert.sv" + +module prim_generic_ram_1p #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input logic clk_i, + + input logic req_i, + input logic write_i, + input logic [Aw-1:0] addr_i, + input logic [Width-1:0] wdata_i, + input logic [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o // Read data. Data is returned one cycle after req_i is high. +); + + // Width of internal write mask. Note wmask_i input into the module is always assumed + // to be the full bit mask + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] wmask; + + always_comb begin + for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask + wmask[i] = &wmask_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + + // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error + // thrown when using $readmemh system task to backdoor load an image + always @(posedge clk_i) begin + if (req_i) begin + if (write_i) begin + for (int i=0; i < MaskWidth; i = i + 1) begin + if (wmask[i]) begin + mem[addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= + wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + end else begin + rdata_o <= mem[addr_i]; + end + end + end + + `include "prim_util_memload.sv" +endmodule diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_2p.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_2p.sv new file mode 100644 index 0000000000..103bbff6f3 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_ram_2p.sv @@ -0,0 +1,87 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous dual-port SRAM register model +// This module is for simulation and small size SRAM. +// Implementing ECC should be done inside wrapper not this model. + +module prim_generic_ram_2p #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input clk_a_i, + input clk_b_i, + + input a_req_i, + input a_write_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input logic [Width-1:0] a_wmask_i, + output logic [Width-1:0] a_rdata_o, + + + input b_req_i, + input b_write_i, + input [Aw-1:0] b_addr_i, + input [Width-1:0] b_wdata_i, + input logic [Width-1:0] b_wmask_i, + output logic [Width-1:0] b_rdata_o +); + // Width of internal write mask. Note *_wmask_i input into the module is always assumed + // to be the full bit mask. + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] a_wmask; + logic [MaskWidth-1:0] b_wmask; + + always_comb begin + for (int i=0; i < MaskWidth; i = i + 1) begin : create_wmask + a_wmask[i] = &a_wmask_i[i*DataBitsPerMask +: DataBitsPerMask]; + b_wmask[i] = &b_wmask_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + + + // Xilinx FPGA specific Dual-port RAM coding style + // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error + // thrown due to 'mem' being driven by two always processes below + always @(posedge clk_a_i) begin + if (a_req_i) begin + if (a_write_i) begin + for (int i=0; i < MaskWidth; i = i + 1) begin + if (a_wmask[i]) begin + mem[a_addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= + a_wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + end else begin + a_rdata_o <= mem[a_addr_i]; + end + end + end + + always @(posedge clk_b_i) begin + if (b_req_i) begin + if (b_write_i) begin + for (int i=0; i < MaskWidth; i = i + 1) begin + if (b_wmask[i]) begin + mem[b_addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= + b_wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + end else begin + b_rdata_o <= mem[b_addr_i]; + end + end + end + + // TODO: Enable once lowRISC/ibex#844 is fixed. + //`include "prim_util_memload.sv" + +endmodule diff --git a/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv new file mode 100644 index 0000000000..9bfe88b5a4 --- /dev/null +++ b/vendor/lowrisc_ip/prim_generic/rtl/prim_generic_rom.sv @@ -0,0 +1,46 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module prim_generic_rom #( + parameter int Width = 32, + parameter int Depth = 2048, // 8kB default + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) +) ( + input clk_i, + input rst_ni, + input [Aw-1:0] addr_i, + input cs_i, + output logic [Width-1:0] dout_o, + output logic dvalid_o +); + + logic [Width-1:0] mem [Depth]; + + always_ff @(posedge clk_i) begin + if (cs_i) begin + dout_o <= mem[addr_i]; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dvalid_o <= 1'b0; + end else begin + dvalid_o <= cs_i; + end + end + + `include "prim_util_memload.sv" + + //////////////// + // ASSERTIONS // + //////////////// + + // Control Signals should never be X + `ASSERT(noXOnCsI, !$isunknown(cs_i), clk_i, '0) +endmodule diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.vlt b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.waiver b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.waiver new file mode 100644 index 0000000000..af90009c05 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_gating.waiver @@ -0,0 +1,4 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.vlt b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.waiver b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.waiver new file mode 100644 index 0000000000..af90009c05 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_clock_mux2.waiver @@ -0,0 +1,4 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.vlt b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.vlt new file mode 100644 index 0000000000..544a7b79b1 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.vlt @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// diff --git a/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.waiver b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.waiver new file mode 100644 index 0000000000..0269d16604 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/lint/prim_xilinx_pad_wrapper.waiver @@ -0,0 +1,12 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for prim_xilinx_pad_wrapper +# note that this code is NOT synthesizable and meant for sim only +waive -rules TRI_DRIVER -regexp {'inout_io' is driven by a tristate driver} + -location {prim_xilinx_pad_wrapper.sv} + -comment "This is a bidirectional pad inout." +waive -rules INPUT_NOT_READ -regexp {Input port 'attr\_i\[.:2\]' is not read from} + -location {prim_xilinx_pad_wrapper.sv} + -comment "Some IO attributes may not be implemented." diff --git a/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_gating.core b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_gating.core new file mode 100644 index 0000000000..72058a63c6 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_gating.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:clock_gating" +description: "prim" +filesets: + files_rtl: + files: + - rtl/prim_xilinx_clock_gating.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_clock_gating.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_clock_gating.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_mux2.core b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_mux2.core new file mode 100644 index 0000000000..6f2dcb2a62 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_clock_mux2.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:clock_mux2" +description: "two-input clock multiplexer primitive" +filesets: + files_rtl: + files: + - rtl/prim_xilinx_clock_mux2.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_clock_mux2.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_clock_mux2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_pad_wrapper.core b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_pad_wrapper.core new file mode 100644 index 0000000000..ce2cd7a1c8 --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/prim_xilinx_pad_wrapper.core @@ -0,0 +1,42 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +name: "lowrisc:prim_xilinx:pad_wrapper" +description: "prim" +filesets: + files_rtl: + files: + - rtl/prim_xilinx_pad_wrapper.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_pad_wrapper.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xilinx_pad_wrapper.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + - lowrisc:lint:comportable + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/shared/rtl/fpga/xilinx/prim_clock_gating.sv b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv similarity index 61% rename from shared/rtl/fpga/xilinx/prim_clock_gating.sv rename to vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv index 19d08250c7..402af9043c 100644 --- a/shared/rtl/fpga/xilinx/prim_clock_gating.sv +++ b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_gating.sv @@ -2,14 +2,14 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 -module prim_clock_gating ( - input clk_i, - input en_i, - input test_en_i, - output logic clk_o +module prim_xilinx_clock_gating ( + input clk_i, + input en_i, + input test_en_i, + output logic clk_o ); - BUFGCE u_clock_gating ( + BUFGCE u_bufgce ( .I (clk_i), .CE (en_i | test_en_i), .O (clk_o) diff --git a/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_mux2.sv b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_mux2.sv new file mode 100644 index 0000000000..572c20ffed --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_clock_mux2.sv @@ -0,0 +1,30 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`include "prim_assert.sv" + +module prim_xilinx_clock_mux2 ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + + // for more info, refer to the Xilinx technology primitives userguide, e.g.: + // ug953-vivado-7series-libraries.pdf + // ug974-vivado-ultrascale-libraries.pdf + BUFGMUX bufgmux_i ( + .S ( sel_i ), + .I0 ( clk0_i ), + .I1 ( clk1_i ), + .O ( clk_o ) + ); + + // make sure sel is never X (including during reset) + // need to use ##1 as this could break with inverted clocks that + // start with a rising edge at the beginning of the simulation. + `ASSERT(selKnown0, ##1 !$isunknown(sel_i), clk0_i, 0) + `ASSERT(selKnown1, ##1 !$isunknown(sel_i), clk1_i, 0) + +endmodule : prim_xilinx_clock_mux2 diff --git a/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv new file mode 100644 index 0000000000..57e9bb49cb --- /dev/null +++ b/vendor/lowrisc_ip/prim_xilinx/rtl/prim_xilinx_pad_wrapper.sv @@ -0,0 +1,43 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Bidirectional IO buffer for Xilinx FPGAs. Implements inversion and +// virtual open drain feature. + + +module prim_xilinx_pad_wrapper #( + parameter int unsigned AttrDw = 2 +) ( + inout wire inout_io, // bidirectional pad + output logic in_o, // input data + input out_i, // output data + input oe_i, // output enable + // additional attributes + input [AttrDw-1:0] attr_i +); + + // get pad attributes + logic od, inv; + assign {od, inv} = attr_i[1:0]; + + // input inversion + logic in; + assign in_o = inv ^ in; + + // virtual open drain emulation + logic oe_n, out; + assign out = out_i ^ inv; + // oe_n = 0: enable driver + // oe_n = 1: disable driver + assign oe_n = ~oe_i | (out & od); + + // driver + IOBUF i_iobuf ( + .T(oe_n), + .I(out), + .O(in), + .IO(inout_io) + ); + +endmodule : prim_xilinx_pad_wrapper diff --git a/vendor/lowrisc_lint.lock.hjson b/vendor/lowrisc_lint.lock.hjson new file mode 100644 index 0000000000..8c61e73ed2 --- /dev/null +++ b/vendor/lowrisc_lint.lock.hjson @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/lowRISC/opentitan + rev: 5b098350c1099526eb7a3b2056432f306af76de1 + only_subdir: hw/lint + } +} diff --git a/vendor/lowrisc_lint.vendor.hjson b/vendor/lowrisc_lint.vendor.hjson new file mode 100644 index 0000000000..34ea382840 --- /dev/null +++ b/vendor/lowrisc_lint.vendor.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "lint", + target_dir: "lowrisc_ip/lint", + + upstream: { + url: "https://github.com/lowRISC/opentitan" + rev: "master" + only_subdir: "hw/lint" + } +} diff --git a/vendor/lowrisc_prim.lock.hjson b/vendor/lowrisc_prim.lock.hjson new file mode 100644 index 0000000000..f00acd824c --- /dev/null +++ b/vendor/lowrisc_prim.lock.hjson @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/lowRISC/opentitan + rev: 5b098350c1099526eb7a3b2056432f306af76de1 + only_subdir: hw/ip/prim + } +} diff --git a/vendor/lowrisc_prim.vendor.hjson b/vendor/lowrisc_prim.vendor.hjson new file mode 100644 index 0000000000..571585161e --- /dev/null +++ b/vendor/lowrisc_prim.vendor.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "prim", + target_dir: "lowrisc_ip/prim", + + upstream: { + url: "https://github.com/lowRISC/opentitan" + rev: "master" + only_subdir: "hw/ip/prim" + } +} diff --git a/vendor/lowrisc_prim_generic.lock.hjson b/vendor/lowrisc_prim_generic.lock.hjson new file mode 100644 index 0000000000..895090cd63 --- /dev/null +++ b/vendor/lowrisc_prim_generic.lock.hjson @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/lowRISC/opentitan + rev: 5b098350c1099526eb7a3b2056432f306af76de1 + only_subdir: hw/ip/prim_generic + } +} diff --git a/vendor/lowrisc_prim_generic.vendor.hjson b/vendor/lowrisc_prim_generic.vendor.hjson new file mode 100644 index 0000000000..0f3782efc4 --- /dev/null +++ b/vendor/lowrisc_prim_generic.vendor.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "prim_generic", + target_dir: "lowrisc_ip/prim_generic", + + upstream: { + url: "https://github.com/lowRISC/opentitan" + rev: "master" + only_subdir: "hw/ip/prim_generic" + } +} diff --git a/vendor/lowrisc_prim_xilinx.lock.hjson b/vendor/lowrisc_prim_xilinx.lock.hjson new file mode 100644 index 0000000000..ea70c87f66 --- /dev/null +++ b/vendor/lowrisc_prim_xilinx.lock.hjson @@ -0,0 +1,15 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This file is generated by the util/vendor script. Please do not modify it +// manually. + +{ + upstream: + { + url: https://github.com/lowRISC/opentitan + rev: 5b098350c1099526eb7a3b2056432f306af76de1 + only_subdir: hw/ip/prim_xilinx + } +} diff --git a/vendor/lowrisc_prim_xilinx.vendor.hjson b/vendor/lowrisc_prim_xilinx.vendor.hjson new file mode 100644 index 0000000000..9c9e84d0fb --- /dev/null +++ b/vendor/lowrisc_prim_xilinx.vendor.hjson @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "prim_xilinx", + target_dir: "lowrisc_ip/prim_xilinx", + + upstream: { + url: "https://github.com/lowRISC/opentitan" + rev: "master" + only_subdir: "hw/ip/prim_xilinx" + } +}