From cf7d2c51eaf3f0407ce58fbd8ac7edc9508e18ad Mon Sep 17 00:00:00 2001 From: Anderson Ignacio Date: Wed, 24 Apr 2024 12:23:44 +0100 Subject: [PATCH] Added synthesis / yosys setup Signed-off-by: Anderson Ignacio --- .gitignore | 1 + .gitmodules | 3 + Dockerfile.nox | 2 +- Dockerfile.synlig | 21 + Dockerfile.yosys | 21 + README.md | 19 + synth/README.md | 145 ++ synth/lec_sv2v.do | 55 + synth/lec_sv2v.sh | 82 + synth/nox.nangate.sdc | 6 + synth/nox_abc.nangate.sdc | 6 + synth/nox_lr_synth_conf.tcl | 43 + synth/pdk_45nm | 1 + synth/prim/BUILD | 11 + synth/prim/README.md | 229 +++ synth/prim/doc/prim_clock_gp_mux2.md | 26 + synth/prim/doc/prim_flash.md | 167 ++ synth/prim/doc/prim_keccak.md | 86 + synth/prim/doc/prim_lfsr.md | 85 + synth/prim/doc/prim_packer.md | 106 + synth/prim/doc/prim_packer_fifo.md | 64 + synth/prim/doc/prim_present.md | 84 + synth/prim/doc/prim_prince.md | 120 ++ synth/prim/doc/prim_ram_1p_scr.md | 123 ++ synth/prim/doc/prim_xoshiro256pp.md | 73 + .../dv/prim_alert/data/prim_alert_cover.cfg | 10 + .../prim_alert/data/prim_alert_testplan.hjson | 108 + synth/prim/dv/prim_alert/prim_alert_sim.core | 32 + .../dv/prim_alert/prim_alert_sim_cfg.hjson | 78 + synth/prim/dv/prim_alert/tb/prim_alert_tb.sv | 253 +++ .../prim/dv/prim_esc/data/prim_esc_cover.cfg | 10 + .../dv/prim_esc/data/prim_esc_testplan.hjson | 91 + synth/prim/dv/prim_esc/prim_esc_sim.core | 31 + synth/prim/dv/prim_esc/prim_esc_sim_cfg.hjson | 53 + synth/prim/dv/prim_esc/tb/prim_esc_tb.sv | 248 +++ .../dv/prim_lfsr/data/prim_lfsr_cov_excl.el | 3 + .../dv/prim_lfsr/data/prim_lfsr_cover.cfg | 16 + synth/prim/dv/prim_lfsr/prim_lfsr_sim.core | 31 + .../prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson | 114 ++ synth/prim/dv/prim_lfsr/prim_lfsr_tb.sv | 313 +++ .../crypto_dpi_present/crypto_dpi_present.cc | 257 +++ .../crypto_dpi_present.core | 17 + .../crypto_dpi_present_pkg.sv | 79 + .../prim_present/data/prim_present_cover.cfg | 9 + .../dv/prim_present/prim_present_sim.core | 32 + .../prim_present/prim_present_sim_cfg.hjson | 50 + .../dv/prim_present/tb/prim_present_tb.sv | 234 +++ .../crypto_dpi_prince/crypto_dpi_prince.c | 33 + .../crypto_dpi_prince/crypto_dpi_prince.core | 18 + .../crypto_dpi_prince_pkg.sv | 64 + .../crypto_dpi_prince_sim_opts.hjson | 22 + .../crypto_dpi_prince/crypto_prince_ref.core | 19 + .../crypto_dpi_prince/prince_ref.h | 342 ++++ .../dv/prim_prince/data/prim_prince_cover.cfg | 11 + .../prim/dv/prim_prince/prim_prince_sim.core | 33 + .../dv/prim_prince/prim_prince_sim_cfg.hjson | 61 + .../prim/dv/prim_prince/tb/prim_prince_tb.sv | 322 +++ .../dv/prim_ram_scr/cpp/scramble_model.cc | 366 ++++ .../dv/prim_ram_scr/cpp/scramble_model.core | 24 + .../prim/dv/prim_ram_scr/cpp/scramble_model.h | 79 + synth/prim/dv/prim_secded/secded_enc.c | 160 ++ synth/prim/dv/prim_secded/secded_enc.core | 22 + synth/prim/dv/prim_secded/secded_enc.h | 36 + .../fpv/prim_alert_rxtx_async_fatal_fpv.core | 32 + synth/prim/fpv/prim_alert_rxtx_async_fpv.core | 32 + synth/prim/fpv/prim_alert_rxtx_fatal_fpv.core | 32 + synth/prim/fpv/prim_alert_rxtx_fpv.core | 32 + synth/prim/fpv/prim_arbiter_fixed_fpv.core | 28 + synth/prim/fpv/prim_arbiter_ppc_fpv.core | 28 + synth/prim/fpv/prim_arbiter_tree_fpv.core | 28 + .../fpv/prim_count_expected_failure.hjson | 13 + synth/prim/fpv/prim_count_fpv.core | 29 + synth/prim/fpv/prim_esc_rxtx_fpv.core | 32 + .../fpv/prim_fifo_async_sram_adapter_fpv.core | 27 + synth/prim/fpv/prim_fifo_sync_fpv.core | 30 + synth/prim/fpv/prim_keccak_fpv.core | 26 + synth/prim/fpv/prim_lfsr_fpv.core | 29 + synth/prim/fpv/prim_packer_fpv.core | 29 + synth/prim/fpv/prim_secded_22_16_fpv.core | 33 + synth/prim/fpv/prim_secded_28_22_fpv.core | 33 + synth/prim/fpv/prim_secded_39_32_fpv.core | 33 + synth/prim/fpv/prim_secded_64_57_fpv.core | 33 + synth/prim/fpv/prim_secded_72_64_fpv.core | 33 + .../fpv/prim_secded_hamming_22_16_fpv.core | 33 + .../fpv/prim_secded_hamming_39_32_fpv.core | 33 + .../fpv/prim_secded_hamming_72_64_fpv.core | 33 + .../fpv/prim_secded_hamming_76_68_fpv.core | 33 + synth/prim/fpv/prim_secded_inv_22_16_fpv.core | 33 + synth/prim/fpv/prim_secded_inv_28_22_fpv.core | 33 + synth/prim/fpv/prim_secded_inv_39_32_fpv.core | 33 + synth/prim/fpv/prim_secded_inv_64_57_fpv.core | 33 + synth/prim/fpv/prim_secded_inv_72_64_fpv.core | 33 + .../prim_secded_inv_hamming_22_16_fpv.core | 33 + .../prim_secded_inv_hamming_39_32_fpv.core | 33 + .../prim_secded_inv_hamming_72_64_fpv.core | 33 + .../prim_secded_inv_hamming_76_68_fpv.core | 33 + .../fpv/tb/prim_alert_rxtx_async_bind_fpv.sv | 32 + .../prim_alert_rxtx_async_fatal_bind_fpv.sv | 32 + .../fpv/tb/prim_alert_rxtx_async_fatal_tb.sv | 119 ++ synth/prim/fpv/tb/prim_alert_rxtx_async_tb.sv | 120 ++ synth/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv | 29 + .../fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv | 30 + synth/prim/fpv/tb/prim_alert_rxtx_fatal_tb.sv | 76 + synth/prim/fpv/tb/prim_alert_rxtx_tb.sv | 76 + synth/prim/fpv/tb/prim_arbiter_fixed_tb.sv | 43 + synth/prim/fpv/tb/prim_arbiter_ppc_tb.sv | 46 + synth/prim/fpv/tb/prim_arbiter_tree_tb.sv | 46 + synth/prim/fpv/tb/prim_count_tb.sv | 44 + synth/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv | 23 + synth/prim/fpv/tb/prim_esc_rxtx_tb.sv | 57 + .../fpv/tb/prim_fifo_async_sram_adapter_tb.sv | 204 ++ synth/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv | 219 ++ synth/prim/fpv/tb/prim_fifo_sync_tb.sv | 236 +++ synth/prim/fpv/tb/prim_keccak_tb.sv | 74 + synth/prim/fpv/tb/prim_lfsr_tb.sv | 123 ++ synth/prim/fpv/tb/prim_packer_tb.sv | 66 + .../prim/fpv/tb/prim_secded_22_16_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_22_16_tb.sv | 30 + .../prim/fpv/tb/prim_secded_28_22_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_28_22_tb.sv | 30 + .../prim/fpv/tb/prim_secded_39_32_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_39_32_tb.sv | 30 + .../prim/fpv/tb/prim_secded_64_57_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_64_57_tb.sv | 30 + .../prim/fpv/tb/prim_secded_72_64_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_72_64_tb.sv | 30 + .../tb/prim_secded_hamming_22_16_bind_fpv.sv | 21 + .../fpv/tb/prim_secded_hamming_22_16_tb.sv | 30 + .../tb/prim_secded_hamming_39_32_bind_fpv.sv | 21 + .../fpv/tb/prim_secded_hamming_39_32_tb.sv | 30 + .../tb/prim_secded_hamming_72_64_bind_fpv.sv | 21 + .../fpv/tb/prim_secded_hamming_72_64_tb.sv | 30 + .../tb/prim_secded_hamming_76_68_bind_fpv.sv | 21 + .../fpv/tb/prim_secded_hamming_76_68_tb.sv | 30 + .../fpv/tb/prim_secded_inv_22_16_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_inv_22_16_tb.sv | 30 + .../fpv/tb/prim_secded_inv_28_22_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_inv_28_22_tb.sv | 30 + .../fpv/tb/prim_secded_inv_39_32_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_inv_39_32_tb.sv | 30 + .../fpv/tb/prim_secded_inv_64_57_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_inv_64_57_tb.sv | 30 + .../fpv/tb/prim_secded_inv_72_64_bind_fpv.sv | 21 + synth/prim/fpv/tb/prim_secded_inv_72_64_tb.sv | 30 + .../prim_secded_inv_hamming_22_16_bind_fpv.sv | 21 + .../tb/prim_secded_inv_hamming_22_16_tb.sv | 30 + .../prim_secded_inv_hamming_39_32_bind_fpv.sv | 21 + .../tb/prim_secded_inv_hamming_39_32_tb.sv | 30 + .../prim_secded_inv_hamming_72_64_bind_fpv.sv | 21 + .../tb/prim_secded_inv_hamming_72_64_tb.sv | 30 + .../prim_secded_inv_hamming_76_68_bind_fpv.sv | 21 + .../tb/prim_secded_inv_hamming_76_68_tb.sv | 30 + .../fpv/vip/prim_alert_rxtx_assert_fpv.sv | 146 ++ .../vip/prim_alert_rxtx_async_assert_fpv.sv | 168 ++ .../prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv | 170 ++ .../prim/fpv/vip/prim_fifo_sync_assert_fpv.sv | 214 ++ .../fpv/vip/prim_secded_22_16_assert_fpv.sv | 32 + .../fpv/vip/prim_secded_28_22_assert_fpv.sv | 32 + .../fpv/vip/prim_secded_39_32_assert_fpv.sv | 32 + .../fpv/vip/prim_secded_64_57_assert_fpv.sv | 32 + .../fpv/vip/prim_secded_72_64_assert_fpv.sv | 32 + .../prim_secded_hamming_22_16_assert_fpv.sv | 32 + .../prim_secded_hamming_39_32_assert_fpv.sv | 32 + .../prim_secded_hamming_72_64_assert_fpv.sv | 32 + .../prim_secded_hamming_76_68_assert_fpv.sv | 32 + .../vip/prim_secded_inv_22_16_assert_fpv.sv | 37 + .../vip/prim_secded_inv_28_22_assert_fpv.sv | 37 + .../vip/prim_secded_inv_39_32_assert_fpv.sv | 37 + .../vip/prim_secded_inv_64_57_assert_fpv.sv | 37 + .../vip/prim_secded_inv_72_64_assert_fpv.sv | 37 + ...rim_secded_inv_hamming_22_16_assert_fpv.sv | 37 + ...rim_secded_inv_hamming_39_32_assert_fpv.sv | 37 + ...rim_secded_inv_hamming_72_64_assert_fpv.sv | 37 + ...rim_secded_inv_hamming_76_68_assert_fpv.sv | 37 + synth/prim/lint/prim.vlt | 13 + synth/prim/lint/prim.waiver | 21 + synth/prim/lint/prim_and2.waiver | 8 + synth/prim/lint/prim_arbiter.vlt | 45 + synth/prim/lint/prim_arbiter.waiver | 13 + synth/prim/lint/prim_assert.vlt | 5 + synth/prim/lint/prim_assert.waiver | 16 + synth/prim/lint/prim_buf.waiver | 8 + synth/prim/lint/prim_cdc_rand_delay.vlt | 10 + synth/prim/lint/prim_cdc_rand_delay.waiver | 17 + synth/prim/lint/prim_cipher.vlt | 16 + synth/prim/lint/prim_cipher_pkg.waiver | 8 + synth/prim/lint/prim_clock_buf.waiver | 8 + synth/prim/lint/prim_clock_div.waiver | 14 + synth/prim/lint/prim_clock_gating.waiver | 8 + synth/prim/lint/prim_clock_inv.waiver | 8 + synth/prim/lint/prim_clock_mux2.waiver | 8 + synth/prim/lint/prim_count.vlt | 8 + synth/prim/lint/prim_count.waiver | 11 + synth/prim/lint/prim_crc32.vlt | 7 + synth/prim/lint/prim_double_lfsr.vlt | 10 + synth/prim/lint/prim_double_lfsr.waiver | 8 + synth/prim/lint/prim_fifo.vlt | 10 + synth/prim/lint/prim_fifo.waiver | 31 + synth/prim/lint/prim_flash.waiver | 8 + synth/prim/lint/prim_flop.waiver | 8 + synth/prim/lint/prim_flop_2sync.waiver | 14 + synth/prim/lint/prim_flop_en.waiver | 8 + synth/prim/lint/prim_lc_sender.waiver | 11 + synth/prim/lint/prim_lfsr.waiver | 9 + synth/prim/lint/prim_max_tree.vlt | 18 + synth/prim/lint/prim_max_tree.waiver | 8 + synth/prim/lint/prim_mubi.waiver | 11 + synth/prim/lint/prim_onehot_check.vlt | 22 + synth/prim/lint/prim_onehot_check.waiver | 11 + synth/prim/lint/prim_onehot_mux.waiver | 11 + synth/prim/lint/prim_otp.waiver | 8 + synth/prim/lint/prim_pad_attr.waiver | 8 + synth/prim/lint/prim_pad_wrapper.waiver | 8 + synth/prim/lint/prim_ram_1p.waiver | 8 + synth/prim/lint/prim_ram_1p_adv.waiver | 8 + synth/prim/lint/prim_ram_1p_scr.vlt | 9 + synth/prim/lint/prim_ram_1r1w.waiver | 8 + synth/prim/lint/prim_ram_2p.waiver | 8 + synth/prim/lint/prim_reg_we_check.waiver | 8 + synth/prim/lint/prim_rom.waiver | 8 + synth/prim/lint/prim_rst_sync.waiver | 5 + synth/prim/lint/prim_secded.waiver | 8 + synth/prim/lint/prim_sha2.vbl | 9 + synth/prim/lint/prim_sha2.vlt | 12 + synth/prim/lint/prim_sha2.waiver | 45 + synth/prim/lint/prim_sparse_fsm_flop.vlt | 9 + synth/prim/lint/prim_sparse_fsm_flop.waiver | 17 + synth/prim/lint/prim_subreg.vlt | 9 + synth/prim/lint/prim_subreg.waiver | 9 + synth/prim/lint/prim_sum_tree.vlt | 17 + synth/prim/lint/prim_sum_tree.waiver | 8 + synth/prim/lint/prim_trivium.vlt | 7 + synth/prim/lint/prim_trivium.waiver | 5 + synth/prim/lint/prim_usb_diff_rx.waiver | 8 + synth/prim/lint/prim_xnor2.waiver | 8 + synth/prim/lint/prim_xor2.waiver | 8 + synth/prim/lint/prim_xoshiro256pp.vlt | 10 + synth/prim/pre_dv/prim_crc32/README.md | 20 + synth/prim/pre_dv/prim_crc32/expected_out.py | 18 + .../prim/pre_dv/prim_crc32/prim_crc32_sim.cc | 34 + .../pre_dv/prim_crc32/prim_crc32_sim.core | 61 + .../prim/pre_dv/prim_crc32/prim_crc32_sim.sv | 50 + synth/prim/pre_dv/prim_crc32/run_predv.sh | 51 + .../prim_flop_2sync/prim_flop_2sync_sim.core | 31 + .../prim_flop_2sync_sim_cfg.hjson | 41 + synth/prim/pre_dv/prim_flop_2sync/tb.sv | 85 + synth/prim/pre_dv/prim_sync_reqack/README.md | 34 + .../cpp/prim_sync_reqack_tb.cc | 61 + .../prim_sync_reqack/prim_sync_reqack_tb.core | 51 + .../rtl/prim_sync_reqack_tb.sv | 188 ++ synth/prim/pre_dv/prim_trivium/README.md | 35 + .../prim_trivium/cpp/prim_trivium_tb.cc | 61 + .../pre_dv/prim_trivium/prim_trivium_tb.core | 51 + .../prim_trivium/rtl/prim_trivium_tb.sv | 428 ++++ synth/prim/prim.core | 79 + synth/prim/prim_alert.core | 45 + synth/prim/prim_and2.core | 49 + synth/prim/prim_arbiter.core | 46 + synth/prim/prim_assert.core | 46 + synth/prim/prim_blanker.core | 41 + synth/prim/prim_buf.core | 49 + synth/prim/prim_cdc_rand_delay.core | 48 + synth/prim/prim_cipher.core | 43 + synth/prim/prim_cipher_pkg.core | 38 + synth/prim/prim_clock_buf.core | 49 + synth/prim/prim_clock_div.core | 48 + synth/prim/prim_clock_gating.core | 49 + synth/prim/prim_clock_gp_mux2.core | 19 + synth/prim/prim_clock_inv.core | 49 + synth/prim/prim_clock_meas.core | 22 + synth/prim/prim_clock_mux2.core | 49 + synth/prim/prim_count.core | 43 + synth/prim/prim_crc32.core | 38 + synth/prim/prim_diff_decode.core | 20 + synth/prim/prim_dom_and_2share.core | 51 + synth/prim/prim_double_lfsr.core | 45 + synth/prim/prim_edge_detector.core | 44 + synth/prim/prim_edn_req.core | 21 + synth/prim/prim_esc.core | 44 + synth/prim/prim_fifo.core | 49 + synth/prim/prim_flash.core | 54 + synth/prim/prim_flop.core | 49 + synth/prim/prim_flop_2sync.core | 46 + synth/prim/prim_flop_en.core | 48 + synth/prim/prim_gf_mult.core | 19 + synth/prim/prim_lc_and_hardened.core | 55 + synth/prim/prim_lc_combine.core | 53 + synth/prim/prim_lc_dec.core | 54 + synth/prim/prim_lc_or_hardened.core | 55 + synth/prim/prim_lc_sender.core | 55 + synth/prim/prim_lc_sync.core | 56 + synth/prim/prim_lfsr.core | 54 + synth/prim/prim_macros.core | 17 + synth/prim/prim_max_tree.core | 49 + synth/prim/prim_msb_extend.core | 39 + synth/prim/prim_mubi.core | 72 + synth/prim/prim_multibit_sync.core | 20 + synth/prim/prim_onehot.core | 44 + synth/prim/prim_onehot_check.core | 51 + synth/prim/prim_otp.core | 52 + synth/prim/prim_otp_pkg.core | 50 + synth/prim/prim_pad_attr.core | 50 + synth/prim/prim_pad_wrapper.core | 50 + synth/prim/prim_pad_wrapper_pkg.core | 39 + synth/prim/prim_pkg.core | 23 + synth/prim/prim_ram_1p.core | 50 + synth/prim/prim_ram_1p_adv.core | 44 + synth/prim/prim_ram_1p_pkg.core | 18 + synth/prim/prim_ram_1p_scr.core | 35 + synth/prim/prim_ram_1r1w.core | 50 + synth/prim/prim_ram_1r1w_adv.core | 19 + synth/prim/prim_ram_1r1w_async_adv.core | 22 + synth/prim/prim_ram_2p.core | 50 + synth/prim/prim_ram_2p_adv.core | 19 + synth/prim/prim_ram_2p_async_adv.core | 22 + synth/prim/prim_ram_2p_pkg.core | 18 + synth/prim/prim_reg_we_check.core | 45 + synth/prim/prim_rom.core | 50 + synth/prim/prim_rom_adv.core | 20 + synth/prim/prim_rom_pkg.core | 18 + synth/prim/prim_rst_sync.core | 47 + synth/prim/prim_sec_anchor.core | 20 + synth/prim/prim_secded.core | 78 + synth/prim/prim_sha2.core | 46 + synth/prim/prim_sha2_pkg.core | 38 + synth/prim/prim_sparse_fsm.core | 44 + synth/prim/prim_subreg.core | 50 + synth/prim/prim_sum_tree.core | 48 + synth/prim/prim_trivium.core | 44 + synth/prim/prim_usb_diff_rx.core | 49 + synth/prim/prim_util.core | 19 + synth/prim/prim_util_get_scramble_params.core | 17 + synth/prim/prim_util_memload.core | 17 + synth/prim/prim_xnor2.core | 49 + synth/prim/prim_xor2.core | 49 + synth/prim/prim_xoshiro256pp.core | 53 + synth/prim/primgen.core | 10 + synth/prim/rtl/prim_alert_pkg.sv | 27 + synth/prim/rtl/prim_alert_receiver.sv | 384 ++++ synth/prim/rtl/prim_alert_sender.sv | 392 ++++ synth/prim/rtl/prim_arbiter_fixed.sv | 170 ++ synth/prim/rtl/prim_arbiter_ppc.sv | 225 +++ synth/prim/rtl/prim_arbiter_tree.sv | 291 +++ synth/prim/rtl/prim_arbiter_tree_dup.sv | 148 ++ synth/prim/rtl/prim_assert.sv | 188 ++ synth/prim/rtl/prim_assert_dummy_macros.svh | 19 + synth/prim/rtl/prim_assert_sec_cm.svh | 65 + .../prim/rtl/prim_assert_standard_macros.svh | 95 + synth/prim/rtl/prim_assert_yosys_macros.svh | 60 + synth/prim/rtl/prim_blanker.sv | 20 + synth/prim/rtl/prim_cdc_rand_delay.sv | 67 + synth/prim/rtl/prim_cipher_pkg.sv | 397 ++++ synth/prim/rtl/prim_clock_gating_sync.sv | 34 + synth/prim/rtl/prim_clock_gp_mux2.sv | 62 + synth/prim/rtl/prim_clock_meas.sv | 279 +++ synth/prim/rtl/prim_clock_timeout.sv | 65 + synth/prim/rtl/prim_count.sv | 267 +++ synth/prim/rtl/prim_crc32.sv | 324 +++ synth/prim/rtl/prim_diff_decode.sv | 285 +++ synth/prim/rtl/prim_dom_and_2share.sv | 173 ++ synth/prim/rtl/prim_double_lfsr.sv | 113 ++ synth/prim/rtl/prim_edge_detector.sv | 55 + synth/prim/rtl/prim_edn_req.sv | 215 ++ synth/prim/rtl/prim_esc_pkg.sv | 23 + synth/prim/rtl/prim_esc_receiver.sv | 277 +++ synth/prim/rtl/prim_esc_sender.sv | 272 +++ synth/prim/rtl/prim_fifo_async.sv | 298 +++ synth/prim/rtl/prim_fifo_async_simple.sv | 91 + .../prim/rtl/prim_fifo_async_sram_adapter.sv | 439 ++++ synth/prim/rtl/prim_fifo_sync.sv | 198 ++ synth/prim/rtl/prim_fifo_sync_cnt.sv | 104 + synth/prim/rtl/prim_filter.sv | 73 + synth/prim/rtl/prim_filter_ctr.sv | 82 + synth/prim/rtl/prim_flop_2sync.sv | 61 + synth/prim/rtl/prim_flop_macros.sv | 74 + synth/prim/rtl/prim_gate_gen.sv | 109 + synth/prim/rtl/prim_gf_mult.sv | 171 ++ synth/prim/rtl/prim_intr_hw.sv | 112 ++ synth/prim/rtl/prim_keccak.sv | 295 +++ synth/prim/rtl/prim_lc_and_hardened.sv | 63 + synth/prim/rtl/prim_lc_combine.sv | 73 + synth/prim/rtl/prim_lc_dec.sv | 30 + synth/prim/rtl/prim_lc_or_hardened.sv | 70 + synth/prim/rtl/prim_lc_sender.sv | 68 + synth/prim/rtl/prim_lc_sync.sv | 116 ++ synth/prim/rtl/prim_lfsr.sv | 634 ++++++ synth/prim/rtl/prim_macros.svh | 47 + synth/prim/rtl/prim_max_tree.sv | 152 ++ synth/prim/rtl/prim_msb_extend.sv | 27 + synth/prim/rtl/prim_mubi12_dec.sv | 48 + synth/prim/rtl/prim_mubi12_sender.sv | 94 + synth/prim/rtl/prim_mubi12_sync.sv | 178 ++ synth/prim/rtl/prim_mubi16_dec.sv | 48 + synth/prim/rtl/prim_mubi16_sender.sv | 94 + synth/prim/rtl/prim_mubi16_sync.sv | 178 ++ synth/prim/rtl/prim_mubi4_dec.sv | 48 + synth/prim/rtl/prim_mubi4_sender.sv | 94 + synth/prim/rtl/prim_mubi4_sync.sv | 178 ++ synth/prim/rtl/prim_mubi8_dec.sv | 48 + synth/prim/rtl/prim_mubi8_sender.sv | 94 + synth/prim/rtl/prim_mubi8_sync.sv | 178 ++ synth/prim/rtl/prim_mubi_pkg.sv | 545 +++++ synth/prim/rtl/prim_multibit_sync.sv | 103 + synth/prim/rtl/prim_onehot_check.sv | 156 ++ synth/prim/rtl/prim_onehot_enc.sv | 21 + synth/prim/rtl/prim_onehot_mux.sv | 48 + synth/prim/rtl/prim_otp_pkg.sv | 29 + synth/prim/rtl/prim_packer.sv | 371 ++++ synth/prim/rtl/prim_packer_fifo.sv | 180 ++ synth/prim/rtl/prim_pad_wrapper_pkg.sv | 46 + synth/prim/rtl/prim_present.sv | 158 ++ synth/prim/rtl/prim_prince.sv | 242 +++ synth/prim/rtl/prim_pulse_sync.sv | 98 + synth/prim/rtl/prim_ram_1p_adv.sv | 269 +++ synth/prim/rtl/prim_ram_1p_pkg.sv | 20 + synth/prim/rtl/prim_ram_1p_scr.sv | 405 ++++ synth/prim/rtl/prim_ram_1r1w_adv.sv | 83 + synth/prim/rtl/prim_ram_1r1w_async_adv.sv | 264 +++ synth/prim/rtl/prim_ram_2p_adv.sv | 95 + synth/prim/rtl/prim_ram_2p_async_adv.sv | 358 ++++ synth/prim/rtl/prim_ram_2p_pkg.sv | 22 + synth/prim/rtl/prim_reg_cdc.sv | 197 ++ synth/prim/rtl/prim_reg_cdc_arb.sv | 305 +++ synth/prim/rtl/prim_reg_we_check.sv | 54 + synth/prim/rtl/prim_rom_adv.sv | 53 + synth/prim/rtl/prim_rom_pkg.sv | 15 + synth/prim/rtl/prim_rst_sync.sv | 65 + synth/prim/rtl/prim_sec_anchor_buf.sv | 21 + synth/prim/rtl/prim_sec_anchor_flop.sv | 27 + synth/prim/rtl/prim_secded_22_16_dec.sv | 45 + synth/prim/rtl/prim_secded_22_16_enc.sv | 22 + synth/prim/rtl/prim_secded_28_22_dec.sv | 51 + synth/prim/rtl/prim_secded_28_22_enc.sv | 22 + synth/prim/rtl/prim_secded_39_32_dec.sv | 62 + synth/prim/rtl/prim_secded_39_32_enc.sv | 23 + synth/prim/rtl/prim_secded_64_57_dec.sv | 87 + synth/prim/rtl/prim_secded_64_57_enc.sv | 23 + synth/prim/rtl/prim_secded_72_64_dec.sv | 95 + synth/prim/rtl/prim_secded_72_64_enc.sv | 24 + .../prim/rtl/prim_secded_hamming_22_16_dec.sv | 45 + .../prim/rtl/prim_secded_hamming_22_16_enc.sv | 22 + .../prim/rtl/prim_secded_hamming_39_32_dec.sv | 62 + .../prim/rtl/prim_secded_hamming_39_32_enc.sv | 23 + .../prim/rtl/prim_secded_hamming_72_64_dec.sv | 95 + .../prim/rtl/prim_secded_hamming_72_64_enc.sv | 24 + .../prim/rtl/prim_secded_hamming_76_68_dec.sv | 99 + .../prim/rtl/prim_secded_hamming_76_68_enc.sv | 24 + synth/prim/rtl/prim_secded_inv_22_16_dec.sv | 45 + synth/prim/rtl/prim_secded_inv_22_16_enc.sv | 23 + synth/prim/rtl/prim_secded_inv_28_22_dec.sv | 51 + synth/prim/rtl/prim_secded_inv_28_22_enc.sv | 23 + synth/prim/rtl/prim_secded_inv_39_32_dec.sv | 62 + synth/prim/rtl/prim_secded_inv_39_32_enc.sv | 24 + synth/prim/rtl/prim_secded_inv_64_57_dec.sv | 87 + synth/prim/rtl/prim_secded_inv_64_57_enc.sv | 24 + synth/prim/rtl/prim_secded_inv_72_64_dec.sv | 95 + synth/prim/rtl/prim_secded_inv_72_64_enc.sv | 25 + .../rtl/prim_secded_inv_hamming_22_16_dec.sv | 45 + .../rtl/prim_secded_inv_hamming_22_16_enc.sv | 23 + .../rtl/prim_secded_inv_hamming_39_32_dec.sv | 62 + .../rtl/prim_secded_inv_hamming_39_32_enc.sv | 24 + .../rtl/prim_secded_inv_hamming_72_64_dec.sv | 95 + .../rtl/prim_secded_inv_hamming_72_64_enc.sv | 25 + .../rtl/prim_secded_inv_hamming_76_68_dec.sv | 99 + .../rtl/prim_secded_inv_hamming_76_68_enc.sv | 25 + synth/prim/rtl/prim_secded_pkg.sv | 1787 +++++++++++++++++ synth/prim/rtl/prim_sha2.sv | 494 +++++ synth/prim/rtl/prim_sha2_32.sv | 276 +++ synth/prim/rtl/prim_sha2_pad.sv | 356 ++++ synth/prim/rtl/prim_sha2_pkg.sv | 238 +++ synth/prim/rtl/prim_slicer.sv | 32 + synth/prim/rtl/prim_sparse_fsm_flop.sv | 67 + synth/prim/rtl/prim_sram_arbiter.sv | 154 ++ synth/prim/rtl/prim_subreg.sv | 75 + synth/prim/rtl/prim_subreg_arb.sv | 187 ++ synth/prim/rtl/prim_subreg_ext.sv | 32 + synth/prim/rtl/prim_subreg_pkg.sv | 17 + synth/prim/rtl/prim_subreg_shadow.sv | 196 ++ synth/prim/rtl/prim_subst_perm.sv | 92 + synth/prim/rtl/prim_sum_tree.sv | 124 ++ synth/prim/rtl/prim_sync_reqack.sv | 404 ++++ synth/prim/rtl/prim_sync_reqack_data.sv | 178 ++ synth/prim/rtl/prim_sync_slow_fast.sv | 59 + synth/prim/rtl/prim_trivium.sv | 317 +++ synth/prim/rtl/prim_trivium_pkg.sv | 159 ++ .../rtl/prim_util_get_scramble_params.svh | 26 + synth/prim/rtl/prim_util_memload.svh | 68 + synth/prim/rtl/prim_util_pkg.sv | 98 + synth/prim/rtl/prim_xoshiro256pp.sv | 87 + synth/prim/util/prim_crc32_table_gen.py | 42 + synth/prim/util/primgen.py | 497 +++++ synth/prim/util/primgen/abstract_prim.sv.tpl | 33 + synth/prim/util/primgen/prim_pkg.core.tpl | 17 + synth/prim/util/primgen/prim_pkg.sv.tpl | 15 + ...oogle_verible_verilog_syntax_py.lock.hjson | 15 + ...gle_verible_verilog_syntax_py.vendor.hjson | 13 + .../google_verible_verilog_syntax_py/BUILD | 55 + .../print_modules.py | 134 ++ .../print_tree.py | 69 + .../verible_verilog_syntax.py | 531 +++++ .../verible_verilog_syntax_test.py | 272 +++ synth/prim_generic/BUILD | 11 + .../lint/prim_generic_clock_buf.vlt | 8 + .../lint/prim_generic_clock_buf.waiver | 7 + .../lint/prim_generic_clock_div.waiver | 20 + .../lint/prim_generic_clock_gating.vlt | 9 + .../lint/prim_generic_clock_gating.waiver | 13 + .../lint/prim_generic_clock_mux2.vlt | 8 + .../lint/prim_generic_clock_mux2.waiver | 8 + .../prim_generic/lint/prim_generic_flash.vlt | 4 + .../lint/prim_generic_flash.waiver | 9 + synth/prim_generic/lint/prim_generic_otp.vlt | 10 + .../prim_generic/lint/prim_generic_otp.waiver | 16 + .../lint/prim_generic_pad_wrapper.vlt | 4 + .../lint/prim_generic_pad_wrapper.waiver | 27 + .../prim_generic/lint/prim_generic_ram_1p.vlt | 4 + .../lint/prim_generic_ram_1p.waiver | 12 + .../lint/prim_generic_ram_1r1w.vlt | 6 + .../lint/prim_generic_ram_1r1w.waiver | 12 + .../prim_generic/lint/prim_generic_ram_2p.vlt | 9 + .../lint/prim_generic_ram_2p.waiver | 14 + synth/prim_generic/lint/prim_generic_rom.vlt | 4 + .../prim_generic/lint/prim_generic_rom.waiver | 9 + .../lint/prim_generic_usb_diff_rx.waiver | 13 + synth/prim_generic/prim_generic_and2.core | 39 + synth/prim_generic/prim_generic_buf.core | 39 + .../prim_generic/prim_generic_clock_buf.core | 41 + .../prim_generic/prim_generic_clock_div.core | 31 + .../prim_generic_clock_gating.core | 41 + .../prim_generic/prim_generic_clock_inv.core | 44 + .../prim_generic/prim_generic_clock_mux2.core | 43 + synth/prim_generic/prim_generic_flash.core | 49 + synth/prim_generic/prim_generic_flop.core | 39 + synth/prim_generic/prim_generic_flop_en.core | 41 + synth/prim_generic/prim_generic_otp.core | 43 + synth/prim_generic/prim_generic_pad_attr.core | 44 + .../prim_generic_pad_wrapper.core | 44 + synth/prim_generic/prim_generic_ram_1p.core | 45 + synth/prim_generic/prim_generic_ram_1r1w.core | 45 + synth/prim_generic/prim_generic_ram_2p.core | 45 + synth/prim_generic/prim_generic_rom.core | 45 + .../prim_generic_usb_diff_rx.core | 41 + synth/prim_generic/prim_generic_xnor2.core | 39 + synth/prim_generic/prim_generic_xor2.core | 39 + synth/prim_generic/rtl/prim_generic_and2.sv | 17 + synth/prim_generic/rtl/prim_generic_buf.sv | 18 + .../rtl/prim_generic_clock_buf.sv | 23 + .../rtl/prim_generic_clock_div.sv | 121 ++ .../rtl/prim_generic_clock_gating.sv | 28 + .../rtl/prim_generic_clock_inv.sv | 32 + .../rtl/prim_generic_clock_mux2.sv | 25 + synth/prim_generic/rtl/prim_generic_flash.sv | 146 ++ .../rtl/prim_generic_flash_bank.sv | 455 +++++ synth/prim_generic/rtl/prim_generic_flop.sv | 25 + .../prim_generic/rtl/prim_generic_flop_en.sv | 39 + synth/prim_generic/rtl/prim_generic_otp.sv | 433 ++++ .../prim_generic/rtl/prim_generic_pad_attr.sv | 66 + .../rtl/prim_generic_pad_wrapper.sv | 112 ++ synth/prim_generic/rtl/prim_generic_ram_1p.sv | 79 + .../prim_generic/rtl/prim_generic_ram_1r1w.sv | 84 + synth/prim_generic/rtl/prim_generic_ram_2p.sv | 107 + synth/prim_generic/rtl/prim_generic_rom.sv | 40 + .../rtl/prim_generic_usb_diff_rx.sv | 50 + synth/prim_generic/rtl/prim_generic_xnor2.sv | 17 + synth/prim_generic/rtl/prim_generic_xor2.sv | 17 + synth/python/build_translated_names.py | 47 + synth/python/flow_utils.py | 82 + synth/python/get_kge.py | 80 + synth/python/translate_timing_csv.py | 41 + synth/rtl/latch_map.v | 12 + synth/rtl/prim_clock_gating.v | 23 + synth/syn_setup.example.sh | 24 + synth/syn_setup_nox.sh | 24 + synth/syn_yosys.sh | 114 ++ synth/tcl/flow_utils.tcl | 124 ++ synth/tcl/lr_synth_flow_var_setup.tcl | 47 + synth/tcl/sta_common.tcl | 18 + synth/tcl/sta_open_design.tcl | 6 + synth/tcl/sta_run_reports.tcl | 21 + synth/tcl/sta_utils.tcl | 51 + synth/tcl/yosys_common.tcl | 12 + synth/tcl/yosys_post_synth.tcl | 10 + synth/tcl/yosys_pre_map.tcl | 9 + synth/tcl/yosys_run_synth.tcl | 67 + synth/translate_timing_rpts.sh | 12 + 585 files changed, 42335 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.synlig create mode 100644 Dockerfile.yosys create mode 100755 synth/README.md create mode 100755 synth/lec_sv2v.do create mode 100755 synth/lec_sv2v.sh create mode 100755 synth/nox.nangate.sdc create mode 100755 synth/nox_abc.nangate.sdc create mode 100755 synth/nox_lr_synth_conf.tcl create mode 160000 synth/pdk_45nm create mode 100755 synth/prim/BUILD create mode 100755 synth/prim/README.md create mode 100755 synth/prim/doc/prim_clock_gp_mux2.md create mode 100755 synth/prim/doc/prim_flash.md create mode 100755 synth/prim/doc/prim_keccak.md create mode 100755 synth/prim/doc/prim_lfsr.md create mode 100755 synth/prim/doc/prim_packer.md create mode 100755 synth/prim/doc/prim_packer_fifo.md create mode 100755 synth/prim/doc/prim_present.md create mode 100755 synth/prim/doc/prim_prince.md create mode 100755 synth/prim/doc/prim_ram_1p_scr.md create mode 100755 synth/prim/doc/prim_xoshiro256pp.md create mode 100755 synth/prim/dv/prim_alert/data/prim_alert_cover.cfg create mode 100755 synth/prim/dv/prim_alert/data/prim_alert_testplan.hjson create mode 100755 synth/prim/dv/prim_alert/prim_alert_sim.core create mode 100755 synth/prim/dv/prim_alert/prim_alert_sim_cfg.hjson create mode 100755 synth/prim/dv/prim_alert/tb/prim_alert_tb.sv create mode 100755 synth/prim/dv/prim_esc/data/prim_esc_cover.cfg create mode 100755 synth/prim/dv/prim_esc/data/prim_esc_testplan.hjson create mode 100755 synth/prim/dv/prim_esc/prim_esc_sim.core create mode 100755 synth/prim/dv/prim_esc/prim_esc_sim_cfg.hjson create mode 100755 synth/prim/dv/prim_esc/tb/prim_esc_tb.sv create mode 100755 synth/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el create mode 100755 synth/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg create mode 100755 synth/prim/dv/prim_lfsr/prim_lfsr_sim.core create mode 100755 synth/prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson create mode 100755 synth/prim/dv/prim_lfsr/prim_lfsr_tb.sv create mode 100755 synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.cc create mode 100755 synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.core create mode 100755 synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present_pkg.sv create mode 100755 synth/prim/dv/prim_present/data/prim_present_cover.cfg create mode 100755 synth/prim/dv/prim_present/prim_present_sim.core create mode 100755 synth/prim/dv/prim_present/prim_present_sim_cfg.hjson create mode 100755 synth/prim/dv/prim_present/tb/prim_present_tb.sv create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core create mode 100755 synth/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h create mode 100755 synth/prim/dv/prim_prince/data/prim_prince_cover.cfg create mode 100755 synth/prim/dv/prim_prince/prim_prince_sim.core create mode 100755 synth/prim/dv/prim_prince/prim_prince_sim_cfg.hjson create mode 100755 synth/prim/dv/prim_prince/tb/prim_prince_tb.sv create mode 100755 synth/prim/dv/prim_ram_scr/cpp/scramble_model.cc create mode 100755 synth/prim/dv/prim_ram_scr/cpp/scramble_model.core create mode 100755 synth/prim/dv/prim_ram_scr/cpp/scramble_model.h create mode 100755 synth/prim/dv/prim_secded/secded_enc.c create mode 100755 synth/prim/dv/prim_secded/secded_enc.core create mode 100755 synth/prim/dv/prim_secded/secded_enc.h create mode 100755 synth/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core create mode 100755 synth/prim/fpv/prim_alert_rxtx_async_fpv.core create mode 100755 synth/prim/fpv/prim_alert_rxtx_fatal_fpv.core create mode 100755 synth/prim/fpv/prim_alert_rxtx_fpv.core create mode 100755 synth/prim/fpv/prim_arbiter_fixed_fpv.core create mode 100755 synth/prim/fpv/prim_arbiter_ppc_fpv.core create mode 100755 synth/prim/fpv/prim_arbiter_tree_fpv.core create mode 100755 synth/prim/fpv/prim_count_expected_failure.hjson create mode 100755 synth/prim/fpv/prim_count_fpv.core create mode 100755 synth/prim/fpv/prim_esc_rxtx_fpv.core create mode 100755 synth/prim/fpv/prim_fifo_async_sram_adapter_fpv.core create mode 100755 synth/prim/fpv/prim_fifo_sync_fpv.core create mode 100755 synth/prim/fpv/prim_keccak_fpv.core create mode 100755 synth/prim/fpv/prim_lfsr_fpv.core create mode 100755 synth/prim/fpv/prim_packer_fpv.core create mode 100755 synth/prim/fpv/prim_secded_22_16_fpv.core create mode 100755 synth/prim/fpv/prim_secded_28_22_fpv.core create mode 100755 synth/prim/fpv/prim_secded_39_32_fpv.core create mode 100755 synth/prim/fpv/prim_secded_64_57_fpv.core create mode 100755 synth/prim/fpv/prim_secded_72_64_fpv.core create mode 100755 synth/prim/fpv/prim_secded_hamming_22_16_fpv.core create mode 100755 synth/prim/fpv/prim_secded_hamming_39_32_fpv.core create mode 100755 synth/prim/fpv/prim_secded_hamming_72_64_fpv.core create mode 100755 synth/prim/fpv/prim_secded_hamming_76_68_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_22_16_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_28_22_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_39_32_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_64_57_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_72_64_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_hamming_22_16_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_hamming_39_32_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_hamming_72_64_fpv.core create mode 100755 synth/prim/fpv/prim_secded_inv_hamming_76_68_fpv.core create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_tb.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_async_tb.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_fatal_tb.sv create mode 100755 synth/prim/fpv/tb/prim_alert_rxtx_tb.sv create mode 100755 synth/prim/fpv/tb/prim_arbiter_fixed_tb.sv create mode 100755 synth/prim/fpv/tb/prim_arbiter_ppc_tb.sv create mode 100755 synth/prim/fpv/tb/prim_arbiter_tree_tb.sv create mode 100755 synth/prim/fpv/tb/prim_count_tb.sv create mode 100755 synth/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_esc_rxtx_tb.sv create mode 100755 synth/prim/fpv/tb/prim_fifo_async_sram_adapter_tb.sv create mode 100755 synth/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_fifo_sync_tb.sv create mode 100755 synth/prim/fpv/tb/prim_keccak_tb.sv create mode 100755 synth/prim/fpv/tb/prim_lfsr_tb.sv create mode 100755 synth/prim/fpv/tb/prim_packer_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_22_16_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_28_22_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_39_32_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_64_57_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_72_64_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_22_16_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_39_32_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_72_64_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_76_68_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_hamming_76_68_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_22_16_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_22_16_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_28_22_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_28_22_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_39_32_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_39_32_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_64_57_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_64_57_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_72_64_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_72_64_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_tb.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_bind_fpv.sv create mode 100755 synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_tb.sv create mode 100755 synth/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_hamming_76_68_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_22_16_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_28_22_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_39_32_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_64_57_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_72_64_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_hamming_22_16_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_hamming_39_32_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_hamming_72_64_assert_fpv.sv create mode 100755 synth/prim/fpv/vip/prim_secded_inv_hamming_76_68_assert_fpv.sv create mode 100755 synth/prim/lint/prim.vlt create mode 100755 synth/prim/lint/prim.waiver create mode 100755 synth/prim/lint/prim_and2.waiver create mode 100755 synth/prim/lint/prim_arbiter.vlt create mode 100755 synth/prim/lint/prim_arbiter.waiver create mode 100755 synth/prim/lint/prim_assert.vlt create mode 100755 synth/prim/lint/prim_assert.waiver create mode 100755 synth/prim/lint/prim_buf.waiver create mode 100755 synth/prim/lint/prim_cdc_rand_delay.vlt create mode 100755 synth/prim/lint/prim_cdc_rand_delay.waiver create mode 100755 synth/prim/lint/prim_cipher.vlt create mode 100755 synth/prim/lint/prim_cipher_pkg.waiver create mode 100755 synth/prim/lint/prim_clock_buf.waiver create mode 100755 synth/prim/lint/prim_clock_div.waiver create mode 100755 synth/prim/lint/prim_clock_gating.waiver create mode 100755 synth/prim/lint/prim_clock_inv.waiver create mode 100755 synth/prim/lint/prim_clock_mux2.waiver create mode 100755 synth/prim/lint/prim_count.vlt create mode 100755 synth/prim/lint/prim_count.waiver create mode 100755 synth/prim/lint/prim_crc32.vlt create mode 100755 synth/prim/lint/prim_double_lfsr.vlt create mode 100755 synth/prim/lint/prim_double_lfsr.waiver create mode 100755 synth/prim/lint/prim_fifo.vlt create mode 100755 synth/prim/lint/prim_fifo.waiver create mode 100755 synth/prim/lint/prim_flash.waiver create mode 100755 synth/prim/lint/prim_flop.waiver create mode 100755 synth/prim/lint/prim_flop_2sync.waiver create mode 100755 synth/prim/lint/prim_flop_en.waiver create mode 100755 synth/prim/lint/prim_lc_sender.waiver create mode 100755 synth/prim/lint/prim_lfsr.waiver create mode 100755 synth/prim/lint/prim_max_tree.vlt create mode 100755 synth/prim/lint/prim_max_tree.waiver create mode 100755 synth/prim/lint/prim_mubi.waiver create mode 100755 synth/prim/lint/prim_onehot_check.vlt create mode 100755 synth/prim/lint/prim_onehot_check.waiver create mode 100755 synth/prim/lint/prim_onehot_mux.waiver create mode 100755 synth/prim/lint/prim_otp.waiver create mode 100755 synth/prim/lint/prim_pad_attr.waiver create mode 100755 synth/prim/lint/prim_pad_wrapper.waiver create mode 100755 synth/prim/lint/prim_ram_1p.waiver create mode 100755 synth/prim/lint/prim_ram_1p_adv.waiver create mode 100755 synth/prim/lint/prim_ram_1p_scr.vlt create mode 100755 synth/prim/lint/prim_ram_1r1w.waiver create mode 100755 synth/prim/lint/prim_ram_2p.waiver create mode 100755 synth/prim/lint/prim_reg_we_check.waiver create mode 100755 synth/prim/lint/prim_rom.waiver create mode 100755 synth/prim/lint/prim_rst_sync.waiver create mode 100755 synth/prim/lint/prim_secded.waiver create mode 100755 synth/prim/lint/prim_sha2.vbl create mode 100755 synth/prim/lint/prim_sha2.vlt create mode 100755 synth/prim/lint/prim_sha2.waiver create mode 100755 synth/prim/lint/prim_sparse_fsm_flop.vlt create mode 100755 synth/prim/lint/prim_sparse_fsm_flop.waiver create mode 100755 synth/prim/lint/prim_subreg.vlt create mode 100755 synth/prim/lint/prim_subreg.waiver create mode 100755 synth/prim/lint/prim_sum_tree.vlt create mode 100755 synth/prim/lint/prim_sum_tree.waiver create mode 100755 synth/prim/lint/prim_trivium.vlt create mode 100755 synth/prim/lint/prim_trivium.waiver create mode 100755 synth/prim/lint/prim_usb_diff_rx.waiver create mode 100755 synth/prim/lint/prim_xnor2.waiver create mode 100755 synth/prim/lint/prim_xor2.waiver create mode 100755 synth/prim/lint/prim_xoshiro256pp.vlt create mode 100755 synth/prim/pre_dv/prim_crc32/README.md create mode 100755 synth/prim/pre_dv/prim_crc32/expected_out.py create mode 100755 synth/prim/pre_dv/prim_crc32/prim_crc32_sim.cc create mode 100755 synth/prim/pre_dv/prim_crc32/prim_crc32_sim.core create mode 100755 synth/prim/pre_dv/prim_crc32/prim_crc32_sim.sv create mode 100755 synth/prim/pre_dv/prim_crc32/run_predv.sh create mode 100755 synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim.core create mode 100755 synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim_cfg.hjson create mode 100755 synth/prim/pre_dv/prim_flop_2sync/tb.sv create mode 100755 synth/prim/pre_dv/prim_sync_reqack/README.md create mode 100755 synth/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc create mode 100755 synth/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core create mode 100755 synth/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv create mode 100755 synth/prim/pre_dv/prim_trivium/README.md create mode 100755 synth/prim/pre_dv/prim_trivium/cpp/prim_trivium_tb.cc create mode 100755 synth/prim/pre_dv/prim_trivium/prim_trivium_tb.core create mode 100755 synth/prim/pre_dv/prim_trivium/rtl/prim_trivium_tb.sv create mode 100755 synth/prim/prim.core create mode 100755 synth/prim/prim_alert.core create mode 100755 synth/prim/prim_and2.core create mode 100755 synth/prim/prim_arbiter.core create mode 100755 synth/prim/prim_assert.core create mode 100755 synth/prim/prim_blanker.core create mode 100755 synth/prim/prim_buf.core create mode 100755 synth/prim/prim_cdc_rand_delay.core create mode 100755 synth/prim/prim_cipher.core create mode 100755 synth/prim/prim_cipher_pkg.core create mode 100755 synth/prim/prim_clock_buf.core create mode 100755 synth/prim/prim_clock_div.core create mode 100755 synth/prim/prim_clock_gating.core create mode 100755 synth/prim/prim_clock_gp_mux2.core create mode 100755 synth/prim/prim_clock_inv.core create mode 100755 synth/prim/prim_clock_meas.core create mode 100755 synth/prim/prim_clock_mux2.core create mode 100755 synth/prim/prim_count.core create mode 100755 synth/prim/prim_crc32.core create mode 100755 synth/prim/prim_diff_decode.core create mode 100755 synth/prim/prim_dom_and_2share.core create mode 100755 synth/prim/prim_double_lfsr.core create mode 100755 synth/prim/prim_edge_detector.core create mode 100755 synth/prim/prim_edn_req.core create mode 100755 synth/prim/prim_esc.core create mode 100755 synth/prim/prim_fifo.core create mode 100755 synth/prim/prim_flash.core create mode 100755 synth/prim/prim_flop.core create mode 100755 synth/prim/prim_flop_2sync.core create mode 100755 synth/prim/prim_flop_en.core create mode 100755 synth/prim/prim_gf_mult.core create mode 100755 synth/prim/prim_lc_and_hardened.core create mode 100755 synth/prim/prim_lc_combine.core create mode 100755 synth/prim/prim_lc_dec.core create mode 100755 synth/prim/prim_lc_or_hardened.core create mode 100755 synth/prim/prim_lc_sender.core create mode 100755 synth/prim/prim_lc_sync.core create mode 100755 synth/prim/prim_lfsr.core create mode 100755 synth/prim/prim_macros.core create mode 100755 synth/prim/prim_max_tree.core create mode 100755 synth/prim/prim_msb_extend.core create mode 100755 synth/prim/prim_mubi.core create mode 100755 synth/prim/prim_multibit_sync.core create mode 100755 synth/prim/prim_onehot.core create mode 100755 synth/prim/prim_onehot_check.core create mode 100755 synth/prim/prim_otp.core create mode 100755 synth/prim/prim_otp_pkg.core create mode 100755 synth/prim/prim_pad_attr.core create mode 100755 synth/prim/prim_pad_wrapper.core create mode 100755 synth/prim/prim_pad_wrapper_pkg.core create mode 100755 synth/prim/prim_pkg.core create mode 100755 synth/prim/prim_ram_1p.core create mode 100755 synth/prim/prim_ram_1p_adv.core create mode 100755 synth/prim/prim_ram_1p_pkg.core create mode 100755 synth/prim/prim_ram_1p_scr.core create mode 100755 synth/prim/prim_ram_1r1w.core create mode 100755 synth/prim/prim_ram_1r1w_adv.core create mode 100755 synth/prim/prim_ram_1r1w_async_adv.core create mode 100755 synth/prim/prim_ram_2p.core create mode 100755 synth/prim/prim_ram_2p_adv.core create mode 100755 synth/prim/prim_ram_2p_async_adv.core create mode 100755 synth/prim/prim_ram_2p_pkg.core create mode 100755 synth/prim/prim_reg_we_check.core create mode 100755 synth/prim/prim_rom.core create mode 100755 synth/prim/prim_rom_adv.core create mode 100755 synth/prim/prim_rom_pkg.core create mode 100755 synth/prim/prim_rst_sync.core create mode 100755 synth/prim/prim_sec_anchor.core create mode 100755 synth/prim/prim_secded.core create mode 100755 synth/prim/prim_sha2.core create mode 100755 synth/prim/prim_sha2_pkg.core create mode 100755 synth/prim/prim_sparse_fsm.core create mode 100755 synth/prim/prim_subreg.core create mode 100755 synth/prim/prim_sum_tree.core create mode 100755 synth/prim/prim_trivium.core create mode 100755 synth/prim/prim_usb_diff_rx.core create mode 100755 synth/prim/prim_util.core create mode 100755 synth/prim/prim_util_get_scramble_params.core create mode 100755 synth/prim/prim_util_memload.core create mode 100755 synth/prim/prim_xnor2.core create mode 100755 synth/prim/prim_xor2.core create mode 100755 synth/prim/prim_xoshiro256pp.core create mode 100755 synth/prim/primgen.core create mode 100755 synth/prim/rtl/prim_alert_pkg.sv create mode 100755 synth/prim/rtl/prim_alert_receiver.sv create mode 100755 synth/prim/rtl/prim_alert_sender.sv create mode 100755 synth/prim/rtl/prim_arbiter_fixed.sv create mode 100755 synth/prim/rtl/prim_arbiter_ppc.sv create mode 100755 synth/prim/rtl/prim_arbiter_tree.sv create mode 100755 synth/prim/rtl/prim_arbiter_tree_dup.sv create mode 100755 synth/prim/rtl/prim_assert.sv create mode 100755 synth/prim/rtl/prim_assert_dummy_macros.svh create mode 100755 synth/prim/rtl/prim_assert_sec_cm.svh create mode 100755 synth/prim/rtl/prim_assert_standard_macros.svh create mode 100755 synth/prim/rtl/prim_assert_yosys_macros.svh create mode 100755 synth/prim/rtl/prim_blanker.sv create mode 100755 synth/prim/rtl/prim_cdc_rand_delay.sv create mode 100755 synth/prim/rtl/prim_cipher_pkg.sv create mode 100755 synth/prim/rtl/prim_clock_gating_sync.sv create mode 100755 synth/prim/rtl/prim_clock_gp_mux2.sv create mode 100755 synth/prim/rtl/prim_clock_meas.sv create mode 100755 synth/prim/rtl/prim_clock_timeout.sv create mode 100755 synth/prim/rtl/prim_count.sv create mode 100755 synth/prim/rtl/prim_crc32.sv create mode 100755 synth/prim/rtl/prim_diff_decode.sv create mode 100755 synth/prim/rtl/prim_dom_and_2share.sv create mode 100755 synth/prim/rtl/prim_double_lfsr.sv create mode 100755 synth/prim/rtl/prim_edge_detector.sv create mode 100755 synth/prim/rtl/prim_edn_req.sv create mode 100755 synth/prim/rtl/prim_esc_pkg.sv create mode 100755 synth/prim/rtl/prim_esc_receiver.sv create mode 100755 synth/prim/rtl/prim_esc_sender.sv create mode 100755 synth/prim/rtl/prim_fifo_async.sv create mode 100755 synth/prim/rtl/prim_fifo_async_simple.sv create mode 100755 synth/prim/rtl/prim_fifo_async_sram_adapter.sv create mode 100755 synth/prim/rtl/prim_fifo_sync.sv create mode 100755 synth/prim/rtl/prim_fifo_sync_cnt.sv create mode 100755 synth/prim/rtl/prim_filter.sv create mode 100755 synth/prim/rtl/prim_filter_ctr.sv create mode 100755 synth/prim/rtl/prim_flop_2sync.sv create mode 100755 synth/prim/rtl/prim_flop_macros.sv create mode 100755 synth/prim/rtl/prim_gate_gen.sv create mode 100755 synth/prim/rtl/prim_gf_mult.sv create mode 100755 synth/prim/rtl/prim_intr_hw.sv create mode 100755 synth/prim/rtl/prim_keccak.sv create mode 100755 synth/prim/rtl/prim_lc_and_hardened.sv create mode 100755 synth/prim/rtl/prim_lc_combine.sv create mode 100755 synth/prim/rtl/prim_lc_dec.sv create mode 100755 synth/prim/rtl/prim_lc_or_hardened.sv create mode 100755 synth/prim/rtl/prim_lc_sender.sv create mode 100755 synth/prim/rtl/prim_lc_sync.sv create mode 100755 synth/prim/rtl/prim_lfsr.sv create mode 100755 synth/prim/rtl/prim_macros.svh create mode 100755 synth/prim/rtl/prim_max_tree.sv create mode 100755 synth/prim/rtl/prim_msb_extend.sv create mode 100755 synth/prim/rtl/prim_mubi12_dec.sv create mode 100755 synth/prim/rtl/prim_mubi12_sender.sv create mode 100755 synth/prim/rtl/prim_mubi12_sync.sv create mode 100755 synth/prim/rtl/prim_mubi16_dec.sv create mode 100755 synth/prim/rtl/prim_mubi16_sender.sv create mode 100755 synth/prim/rtl/prim_mubi16_sync.sv create mode 100755 synth/prim/rtl/prim_mubi4_dec.sv create mode 100755 synth/prim/rtl/prim_mubi4_sender.sv create mode 100755 synth/prim/rtl/prim_mubi4_sync.sv create mode 100755 synth/prim/rtl/prim_mubi8_dec.sv create mode 100755 synth/prim/rtl/prim_mubi8_sender.sv create mode 100755 synth/prim/rtl/prim_mubi8_sync.sv create mode 100755 synth/prim/rtl/prim_mubi_pkg.sv create mode 100755 synth/prim/rtl/prim_multibit_sync.sv create mode 100755 synth/prim/rtl/prim_onehot_check.sv create mode 100755 synth/prim/rtl/prim_onehot_enc.sv create mode 100755 synth/prim/rtl/prim_onehot_mux.sv create mode 100755 synth/prim/rtl/prim_otp_pkg.sv create mode 100755 synth/prim/rtl/prim_packer.sv create mode 100755 synth/prim/rtl/prim_packer_fifo.sv create mode 100755 synth/prim/rtl/prim_pad_wrapper_pkg.sv create mode 100755 synth/prim/rtl/prim_present.sv create mode 100755 synth/prim/rtl/prim_prince.sv create mode 100755 synth/prim/rtl/prim_pulse_sync.sv create mode 100755 synth/prim/rtl/prim_ram_1p_adv.sv create mode 100755 synth/prim/rtl/prim_ram_1p_pkg.sv create mode 100755 synth/prim/rtl/prim_ram_1p_scr.sv create mode 100755 synth/prim/rtl/prim_ram_1r1w_adv.sv create mode 100755 synth/prim/rtl/prim_ram_1r1w_async_adv.sv create mode 100755 synth/prim/rtl/prim_ram_2p_adv.sv create mode 100755 synth/prim/rtl/prim_ram_2p_async_adv.sv create mode 100755 synth/prim/rtl/prim_ram_2p_pkg.sv create mode 100755 synth/prim/rtl/prim_reg_cdc.sv create mode 100755 synth/prim/rtl/prim_reg_cdc_arb.sv create mode 100755 synth/prim/rtl/prim_reg_we_check.sv create mode 100755 synth/prim/rtl/prim_rom_adv.sv create mode 100755 synth/prim/rtl/prim_rom_pkg.sv create mode 100755 synth/prim/rtl/prim_rst_sync.sv create mode 100755 synth/prim/rtl/prim_sec_anchor_buf.sv create mode 100755 synth/prim/rtl/prim_sec_anchor_flop.sv create mode 100755 synth/prim/rtl/prim_secded_22_16_dec.sv create mode 100755 synth/prim/rtl/prim_secded_22_16_enc.sv create mode 100755 synth/prim/rtl/prim_secded_28_22_dec.sv create mode 100755 synth/prim/rtl/prim_secded_28_22_enc.sv create mode 100755 synth/prim/rtl/prim_secded_39_32_dec.sv create mode 100755 synth/prim/rtl/prim_secded_39_32_enc.sv create mode 100755 synth/prim/rtl/prim_secded_64_57_dec.sv create mode 100755 synth/prim/rtl/prim_secded_64_57_enc.sv create mode 100755 synth/prim/rtl/prim_secded_72_64_dec.sv create mode 100755 synth/prim/rtl/prim_secded_72_64_enc.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_22_16_dec.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_22_16_enc.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_39_32_dec.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_39_32_enc.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_72_64_dec.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_72_64_enc.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_76_68_dec.sv create mode 100755 synth/prim/rtl/prim_secded_hamming_76_68_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_22_16_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_22_16_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_28_22_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_28_22_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_39_32_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_39_32_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_64_57_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_64_57_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_72_64_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_72_64_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_22_16_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_22_16_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_39_32_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_39_32_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_72_64_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_72_64_enc.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_76_68_dec.sv create mode 100755 synth/prim/rtl/prim_secded_inv_hamming_76_68_enc.sv create mode 100755 synth/prim/rtl/prim_secded_pkg.sv create mode 100755 synth/prim/rtl/prim_sha2.sv create mode 100755 synth/prim/rtl/prim_sha2_32.sv create mode 100755 synth/prim/rtl/prim_sha2_pad.sv create mode 100755 synth/prim/rtl/prim_sha2_pkg.sv create mode 100755 synth/prim/rtl/prim_slicer.sv create mode 100755 synth/prim/rtl/prim_sparse_fsm_flop.sv create mode 100755 synth/prim/rtl/prim_sram_arbiter.sv create mode 100755 synth/prim/rtl/prim_subreg.sv create mode 100755 synth/prim/rtl/prim_subreg_arb.sv create mode 100755 synth/prim/rtl/prim_subreg_ext.sv create mode 100755 synth/prim/rtl/prim_subreg_pkg.sv create mode 100755 synth/prim/rtl/prim_subreg_shadow.sv create mode 100755 synth/prim/rtl/prim_subst_perm.sv create mode 100755 synth/prim/rtl/prim_sum_tree.sv create mode 100755 synth/prim/rtl/prim_sync_reqack.sv create mode 100755 synth/prim/rtl/prim_sync_reqack_data.sv create mode 100755 synth/prim/rtl/prim_sync_slow_fast.sv create mode 100755 synth/prim/rtl/prim_trivium.sv create mode 100755 synth/prim/rtl/prim_trivium_pkg.sv create mode 100755 synth/prim/rtl/prim_util_get_scramble_params.svh create mode 100755 synth/prim/rtl/prim_util_memload.svh create mode 100755 synth/prim/rtl/prim_util_pkg.sv create mode 100755 synth/prim/rtl/prim_xoshiro256pp.sv create mode 100755 synth/prim/util/prim_crc32_table_gen.py create mode 100755 synth/prim/util/primgen.py create mode 100755 synth/prim/util/primgen/abstract_prim.sv.tpl create mode 100755 synth/prim/util/primgen/prim_pkg.core.tpl create mode 100755 synth/prim/util/primgen/prim_pkg.sv.tpl create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py/BUILD create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py create mode 100755 synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py create mode 100755 synth/prim_generic/BUILD create mode 100755 synth/prim_generic/lint/prim_generic_clock_buf.vlt create mode 100755 synth/prim_generic/lint/prim_generic_clock_buf.waiver create mode 100755 synth/prim_generic/lint/prim_generic_clock_div.waiver create mode 100755 synth/prim_generic/lint/prim_generic_clock_gating.vlt create mode 100755 synth/prim_generic/lint/prim_generic_clock_gating.waiver create mode 100755 synth/prim_generic/lint/prim_generic_clock_mux2.vlt create mode 100755 synth/prim_generic/lint/prim_generic_clock_mux2.waiver create mode 100755 synth/prim_generic/lint/prim_generic_flash.vlt create mode 100755 synth/prim_generic/lint/prim_generic_flash.waiver create mode 100755 synth/prim_generic/lint/prim_generic_otp.vlt create mode 100755 synth/prim_generic/lint/prim_generic_otp.waiver create mode 100755 synth/prim_generic/lint/prim_generic_pad_wrapper.vlt create mode 100755 synth/prim_generic/lint/prim_generic_pad_wrapper.waiver create mode 100755 synth/prim_generic/lint/prim_generic_ram_1p.vlt create mode 100755 synth/prim_generic/lint/prim_generic_ram_1p.waiver create mode 100755 synth/prim_generic/lint/prim_generic_ram_1r1w.vlt create mode 100755 synth/prim_generic/lint/prim_generic_ram_1r1w.waiver create mode 100755 synth/prim_generic/lint/prim_generic_ram_2p.vlt create mode 100755 synth/prim_generic/lint/prim_generic_ram_2p.waiver create mode 100755 synth/prim_generic/lint/prim_generic_rom.vlt create mode 100755 synth/prim_generic/lint/prim_generic_rom.waiver create mode 100755 synth/prim_generic/lint/prim_generic_usb_diff_rx.waiver create mode 100755 synth/prim_generic/prim_generic_and2.core create mode 100755 synth/prim_generic/prim_generic_buf.core create mode 100755 synth/prim_generic/prim_generic_clock_buf.core create mode 100755 synth/prim_generic/prim_generic_clock_div.core create mode 100755 synth/prim_generic/prim_generic_clock_gating.core create mode 100755 synth/prim_generic/prim_generic_clock_inv.core create mode 100755 synth/prim_generic/prim_generic_clock_mux2.core create mode 100755 synth/prim_generic/prim_generic_flash.core create mode 100755 synth/prim_generic/prim_generic_flop.core create mode 100755 synth/prim_generic/prim_generic_flop_en.core create mode 100755 synth/prim_generic/prim_generic_otp.core create mode 100755 synth/prim_generic/prim_generic_pad_attr.core create mode 100755 synth/prim_generic/prim_generic_pad_wrapper.core create mode 100755 synth/prim_generic/prim_generic_ram_1p.core create mode 100755 synth/prim_generic/prim_generic_ram_1r1w.core create mode 100755 synth/prim_generic/prim_generic_ram_2p.core create mode 100755 synth/prim_generic/prim_generic_rom.core create mode 100755 synth/prim_generic/prim_generic_usb_diff_rx.core create mode 100755 synth/prim_generic/prim_generic_xnor2.core create mode 100755 synth/prim_generic/prim_generic_xor2.core create mode 100755 synth/prim_generic/rtl/prim_generic_and2.sv create mode 100755 synth/prim_generic/rtl/prim_generic_buf.sv create mode 100755 synth/prim_generic/rtl/prim_generic_clock_buf.sv create mode 100755 synth/prim_generic/rtl/prim_generic_clock_div.sv create mode 100755 synth/prim_generic/rtl/prim_generic_clock_gating.sv create mode 100755 synth/prim_generic/rtl/prim_generic_clock_inv.sv create mode 100755 synth/prim_generic/rtl/prim_generic_clock_mux2.sv create mode 100755 synth/prim_generic/rtl/prim_generic_flash.sv create mode 100755 synth/prim_generic/rtl/prim_generic_flash_bank.sv create mode 100755 synth/prim_generic/rtl/prim_generic_flop.sv create mode 100755 synth/prim_generic/rtl/prim_generic_flop_en.sv create mode 100755 synth/prim_generic/rtl/prim_generic_otp.sv create mode 100755 synth/prim_generic/rtl/prim_generic_pad_attr.sv create mode 100755 synth/prim_generic/rtl/prim_generic_pad_wrapper.sv create mode 100755 synth/prim_generic/rtl/prim_generic_ram_1p.sv create mode 100755 synth/prim_generic/rtl/prim_generic_ram_1r1w.sv create mode 100755 synth/prim_generic/rtl/prim_generic_ram_2p.sv create mode 100755 synth/prim_generic/rtl/prim_generic_rom.sv create mode 100755 synth/prim_generic/rtl/prim_generic_usb_diff_rx.sv create mode 100755 synth/prim_generic/rtl/prim_generic_xnor2.sv create mode 100755 synth/prim_generic/rtl/prim_generic_xor2.sv create mode 100755 synth/python/build_translated_names.py create mode 100755 synth/python/flow_utils.py create mode 100755 synth/python/get_kge.py create mode 100755 synth/python/translate_timing_csv.py create mode 100755 synth/rtl/latch_map.v create mode 100755 synth/rtl/prim_clock_gating.v create mode 100755 synth/syn_setup.example.sh create mode 100755 synth/syn_setup_nox.sh create mode 100755 synth/syn_yosys.sh create mode 100755 synth/tcl/flow_utils.tcl create mode 100755 synth/tcl/lr_synth_flow_var_setup.tcl create mode 100755 synth/tcl/sta_common.tcl create mode 100755 synth/tcl/sta_open_design.tcl create mode 100755 synth/tcl/sta_run_reports.tcl create mode 100755 synth/tcl/sta_utils.tcl create mode 100755 synth/tcl/yosys_common.tcl create mode 100755 synth/tcl/yosys_post_synth.tcl create mode 100755 synth/tcl/yosys_pre_map.tcl create mode 100755 synth/tcl/yosys_run_synth.tcl create mode 100755 synth/translate_timing_rpts.sh diff --git a/.gitignore b/.gitignore index 1230d0c..8aeeb43 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ output_temp/ riscof_work/ riscv-arch-test/ *.txt +synth/syn_out diff --git a/.gitmodules b/.gitmodules index 9891824..d07a141 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "bus_arch_sv_pkg"] path = bus_arch_sv_pkg url = https://github.com/aignacio/bus_arch_sv_pkg.git +[submodule "synth/pdk_45nm"] + path = synth/pdk_45nm + url = https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts diff --git a/Dockerfile.nox b/Dockerfile.nox index b95017d..9529dcc 100644 --- a/Dockerfile.nox +++ b/Dockerfile.nox @@ -6,7 +6,7 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN apt-get update && apt-get upgrade -y RUN apt-get install git file gcc make time wget zip -y #[sv2v] -RUN wget https://github.com/zachjs/sv2v/releases/download/v0.0.9/sv2v-Linux.zip +RUN wget https://github.com/zachjs/sv2v/releases/download/v0.0.11/sv2v-Linux.zip RUN unzip sv2v-Linux.zip && rm sv2v-Linux.zip RUN ln -s /sv2v-Linux/sv2v /usr/bin/sv2v && chmod +x /sv2v-Linux/sv2v # [Verilator] diff --git a/Dockerfile.synlig b/Dockerfile.synlig new file mode 100644 index 0000000..5888386 --- /dev/null +++ b/Dockerfile.synlig @@ -0,0 +1,21 @@ +FROM ubuntu:latest +LABEL author="Anderson Ignacio da Silva" +LABEL maintainer="anderson@aignacio.com" +ENV TZ=Europe/Dublin +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && apt-get upgrade -y +RUN apt-get install git file gcc make time wget zip -y +# Install synlig / Yosys / surelog +RUN apt install -y gcc-11 g++-11 build-essential cmake tclsh ant default-jre swig google-perftools libgoogle-perftools-dev python3 python3-dev python3-pip uuid uuid-dev tcl-dev flex libfl-dev git pkg-config libreadline-dev bison libffi-dev wget python3-orderedmultidict +RUN git clone https://github.com/chipsalliance/synlig.git +WORKDIR /synlig +RUN git submodule sync +RUN git submodule init +ENV GIT_TRACE=1 +RUN git submodule update --init --recursive third_party/surelog +RUN git submodule update --init --recursive third_party/yosys +RUN make install +ENV PATH=/synlig/out/release/bin:$PATH + + + diff --git a/Dockerfile.yosys b/Dockerfile.yosys new file mode 100644 index 0000000..55f16b1 --- /dev/null +++ b/Dockerfile.yosys @@ -0,0 +1,21 @@ +FROM ubuntu:latest +LABEL author="Anderson Ignacio da Silva" +LABEL maintainer="anderson@aignacio.com" +ENV TZ=Europe/Dublin +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && apt-get upgrade -y +RUN apt-get install git file gcc make time wget zip -y +# OSS CAD suite +RUN wget -c http://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-04-23/oss-cad-suite-linux-x64-20240423.tgz -O - | tar -xz +ENV PATH="/oss-cad-suite/bin:$PATH" +#[sv2v] +RUN wget https://github.com/zachjs/sv2v/releases/download/v0.0.11/sv2v-Linux.zip +RUN unzip sv2v-Linux.zip && rm sv2v-Linux.zip +RUN ln -s /sv2v-Linux/sv2v /usr/bin/sv2v && chmod +x /sv2v-Linux/sv2v +RUN git clone https://github.com/parallaxsw/OpenSTA.git /opensta +RUN apt-get install cmake build-essential flex bison swig clang expect tcl-dev libeigen3-dev -y +WORKDIR /opensta +RUN mkdir build +RUN cd build && cmake ../ +RUN cd build && make -j`nproc` +RUN cd build && make install diff --git a/README.md b/README.md index bbd9e35..b3acc42 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ * [FreeRTOS](#freertos) * [Compliance Tests](#compliance) * [CoreMark](#coremark) +* [Synthesis](#synth) * [License](#lic) ## Introduction @@ -244,7 +245,25 @@ seedcrc : 0x18f2 [0]crcfinal : 0x0cac Correct operation validated. See README.md for run and reporting rules. ``` +## Synthesis + +Adapting the setup to [Ibex Core - low risc](https://github.com/lowRISC/ibex/tree/master/syn), attached is the command to perform synthesis on the 45nm nangate PDK. +```bash +docker run -v .:/test -w /test --rm aignacio/oss_cad_suite:latest bash -c "cd /test/synth && ./syn_yosys.sh" +``` + +### Area results: +* 27.04 kGE @ 250MHz in 45nm + +```bash +... +End of script. Logfile hash: 39230763f8, CPU: user 15.51s system 0.15s, MEM: 175.05 MB peak +Yosys 0.40+25 (git sha1 171577f90, clang++ 14.0.0-1ubuntu1.1 -fPIC -Os) +Time spent: 72% 2416x select (5 sec), 22% 2x read_verilog (1 sec), ... +Area in kGE = 27.04 +``` ## License + NoX is licensed under the permissive MIT license. Please refer to the [LICENSE](LICENSE) file for details. diff --git a/synth/README.md b/synth/README.md new file mode 100755 index 0000000..4f2a37b --- /dev/null +++ b/synth/README.md @@ -0,0 +1,145 @@ +**This synthesis flow is experimental and under development, it does not produce +tape-out quality netlists and area/timing numbers it generates are not +representative of what would be achievable with a tape-out quality flow** + +# Yosys/OpenSTA Ibex Synthesis Flow + +This is a synthesis-only implementation flow using Yosys for Synthesis and +OpenSTA to produce timing reports. Its outputs are: + +* A pre-mapping netlist - Gate-level verilog using generic gates that hasn't + been mapped to a standard-cell library yet +* A post synthesis netlist - Gate-level verilog after optimisation mapped to a + standard-cell library +* An STA netlist - Logically equivilent to the netlist above but with changes to + allow processing by OpenSTA +* Area/Cell Usage report - Total area consumed by utilised cells and counts of + each cell instance used +* Timing reports - Overal timing report and reports broken down into various + path groups (register to register paths and per IO reports) + +Yosys doesn't yet support the full subset of SystemVerilog used by Ibex so the +sv2v tool is used to first convert the Ibex RTL into the SystemVerilog subset +Yosys can process. + +# Synthesis flow requirements + +The following must be installed: + +* Python 3 (version >= 3.5) +* sv2v - https://github.com/zachjs/sv2v +* Yosys - https://github.com/YosysHQ/yosys +* OpenSTA - https://github.com/The-OpenROAD-Project/OpenSTA + +The flow was tested with yosys 0.9 and OpenSTA 2.2 but may work with other +versions. A standard cell library is also required in the liberty (.lib) +format. The following Open Libraries can be used: + +* Nangate45 - https://github.com/The-OpenROAD-Project/OpenROAD-flow/tree/master/flow/platforms/nangate45 + +# Synthesis flow setup + +The synthesis flow is configured via environment variables. The `syn_setup.sh` +file is used to set the environment variables for the flow and any changes made +should be placed there. An example `syn_setup.example.sh` is included. A copy +of this named `syn_setup.sh` must be made and the values in it set appropriately +for the flow to work. + +The environment variables that must be set in `syn_setup.sh` are + +* `LR_SYNTH_CELL_LIBRARY_PATH` - The path to the standard cell library, this + should point to the absolute path of the Nangate45 library + (`NangateOpenCellLibrary_typical.lib`). +* `LR_SYNTH_CELL_LIBRARY_NAME` - The name of the standard cell library, this is + used to alter the flow configuration for the library, currently 'nangate' is + the only supported value + +# Running the synthesis flow + +Once `syn_setup.sh` has been created, call `syn_yosys.sh` to run the entire +flow. All outputs are placed under the `syn/syn_out` directory with the prefix +`ibex_` with the current date/time forming the rest of the name, e.g. +`syn/syn_out/ibex_06_01_2020_11_19_15` + +- `syn/syn_out/ibex_date` + - `reports` - All of the generated reports + - area.rpt - Total area used and per cell instance counts + - `timing` + - *.rpt - Raw reports from OpenSTA, gives full paths + - *.csv.rpt - CSV reports gives start and end point and slack + - `log` + - syn.log - Log of the Yosys run + - sta.log - Log of the OpenSTA run + - `generated` + - *.v - Ibex RTL after sv2v processing + - ibex_top.pre_map.v - Pre-mapping synthesis netlists + - ibex_top_netlist.v - Post-synthesis netlist + - ibex_top_netlist.sta.v - Post-synthesis netlist usable by OpenSTA + - ibex_top.[library-name].out.sdc - Generated .sdc timing constraints + file + +If you wish to change the results directory naming or location edit +`syn_setup.sh` appropriately. + +# Timing constraints + +Two files specify the timing constraints and timing related settings for the +flow. These are used to generate a single .sdc file + +* `ibex_top_lr_synth_core.tcl` - This specifies the constraints on all inputs + and outputs as a fraction of a clock cycle, the names of the clock and reset + inputs and the desired clock period in ps +* `ibex.[library-name].sdc` - Header to include in generated .sdc file. Settings + can be library dependent so the `LR_SYNTH_CELL_LIBRARY_NAME` environment + varible is used to supply the `[library-name]` part of the name + +# Timing reports + +Timing reports are produced for the following path groups +* Overall - Every path in the design, WNS (worst negative slack) from this report is the design WNS + that limits the frequency +* reg2reg - Paths from register to register +* in2reg - Paths from any input to any register +* reg2out - Paths from any register to any output +* in2out - Paths from any input to any output + +They are available in two formats .rpt and .csv.rpt. The .rpt is the full output +from OpenSTA and gives the full path between the start and end points. The CSV +version contains the start-point, end-point and WNS (one path per line). CSV +reports have had their start and end points translated to human readable names +(though this isn't 100% reliable). The raw OpenSTA reports generally contain +only generated cell names so will require further netlist inspection (via Yosys +or simply looking at the netlist .v) to make sense of. + +# Post-synthesis inspection + +Both Yosys and OpenSTA can be run to perform further inspection on the generated +synthesis. TCL is provided to setup the tools appropriately. + +First the environment variables must be setup for the flow and the directory +containing the synthesis output set. This can be done with `syn_setup.sh` + +``` +$ source syn_setup.sh syn_out_06_01_2020_11_19_15/ +``` + +Where `syn_out_06_01_2020_11_19_15/` is directory containing the synthesis +outputs. Then start Yosys or OpenSTA and run one of the provided TCL files + +* `./tcl/yosys_pre_map.tcl` - Loads the pre-mapping netlist +* `./tcl/yosys_post_synth.tcl` - Load the post-synthesis netlist + +So to load the post-synthesis netlist in Yosys: + +``` +$ yosys +yosys> tcl ./tcl/yosys_post_synth.tcl +``` + +To open the design in OpenSTA + +``` +$ sta +% source ./tcl/sta_open_design.tcl +``` + diff --git a/synth/lec_sv2v.do b/synth/lec_sv2v.do new file mode 100755 index 0000000..7dba52c --- /dev/null +++ b/synth/lec_sv2v.do @@ -0,0 +1,55 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// LEC dofile for script lec_sv2v.sh. A similar script is used in +// OpenTitan, any updates or fixes here may need to be reflected in the +// OpenTitan script as well: +// https://github.com/lowRISC/opentitan/blob/master/hw/formal/lec_sv2v.do + +//------------------------------------------------------------------------- +// read in golden (SystemVerilog) and revised (Verilog) +//------------------------------------------------------------------------- + +// map all multi-dimensional ports (including structs) onto 1-dim. ports +set naming rule -mdportflatten + +read design -golden -sv09 -f flist_gold -rootonly -root $LEC_TOP +read design -revised -sys -f flist_rev -rootonly -root $LEC_TOP +// TODO: instead of using switch -sys (for old SystemVerilog, +// older than sv2009) we should use -ve (for Verilog). But +// this currently doesn't work because sv2v doesn't translate +// .* port connections. Is that an sv2v bug? + +//------------------------------------------------------------------------- +// pre-LEC reports +//------------------------------------------------------------------------- +report rule check -verbose +report design data +report black box +report module + +//------------------------------------------------------------------------- +// compare +//------------------------------------------------------------------------- +set system mode lec +set parallel option -threads 8 + +// map unreachable points +set mapping method -nets -mem -unreach +map key points +report unmapped points + +add compare point -all +compare -threads 8 -noneq_stop 1 +analyze abort -compare + +//------------------------------------------------------------------------- +// reports +//------------------------------------------------------------------------- +report compare data -class nonequivalent -class abort -class notcompared +report verification -verbose +report statistics +usage + +exit -force diff --git a/synth/lec_sv2v.sh b/synth/lec_sv2v.sh new file mode 100755 index 0000000..10f2eaa --- /dev/null +++ b/synth/lec_sv2v.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# This script converts all SystemVerilog RTL files to Verilog +# using sv2v and then runs LEC (Cadence Conformal) to check if +# the generated Verilog is logically equivalent to the original +# SystemVerilog. A similar script is used in OpenTitan, any updates +# or fixes here may need to be reflected in the OpenTitan script as well +# https://github.com/lowRISC/opentitan/blob/master/util/syn_yosys.sh +# +# The following tools are required: +# - sv2v: SystemVerilog-to-Verilog converter from github.com/zachjs/sv2v +# - Cadence Conformal +# +# Usage: +# ./lec_sv2v.sh |& tee lec.log + +#------------------------------------------------------------------------- +# use fusesoc to generate files and file list +#------------------------------------------------------------------------- +rm -Rf build lec_out +fusesoc --cores-root .. run --tool=icarus --target=lint \ + --setup "lowrisc:ibex:ibex_top" > /dev/null 2>&1 + +# copy all files to lec_out +mkdir lec_out +cp build/*/src/*/*.sv build/*/src/*/*/*.sv lec_out +cd lec_out || exit + +# copy file list and remove incdir, RVFI define, and sim-file +grep -E -v 'incdir|RVFI|simulator_ctrl' ../build/*/*/*.scr > flist_gold + +# remove all hierarchical paths +sed -i 's!.*/!!' flist_gold + +# generate revised flist by replacing '.sv' by '.v' and removing packages +sed 's/.sv/.v/' flist_gold | grep -v "_pkg.v" > flist_rev + +#------------------------------------------------------------------------- +# convert all RTL files to Verilog using sv2v +#------------------------------------------------------------------------- +printf "\nSV2V ERRORS:\n" + +for file in *.sv; do + module=$(basename -s .sv "$file") + sv2v --define=SYNTHESIS ./*_pkg.sv prim_assert.sv "$file" > "${module}".v +done + +# remove *pkg.v files (they are empty files and not needed) +rm -f ./*_pkg.v prim_assert.v prim_util_memload.v + +# overwrite the prim_clock_gating modules with the module from ../rtl +cp ../rtl/prim_clock_gating.v . +cp ../rtl/prim_clock_gating.v prim_clock_gating.sv + +#------------------------------------------------------------------------- +# run LEC (generated Verilog vs. original SystemVerilog) +#------------------------------------------------------------------------- +printf "\n\nLEC RESULTS:\n" + +for file in *.v; do + LEC_TOP=$(basename -s .v "$file") + export LEC_TOP + + # run Conformal LEC + lec -xl -nogui -nobanner \ + -dofile ../lec_sv2v.do \ + -logfile lec_"${LEC_TOP}".log \ + <<< "exit -force" > /dev/null 2>&1 + + # summarize results + check=$(grep "Compare Results" lec_"${LEC_TOP}".log) + if [ $? -ne 0 ]; then + result="CRASH" + else + result=$(echo "$check" | awk '{ print $4 }') + fi + printf "%-25s %s\n" "$LEC_TOP" "$result" +done diff --git a/synth/nox.nangate.sdc b/synth/nox.nangate.sdc new file mode 100755 index 0000000..f5fae74 --- /dev/null +++ b/synth/nox.nangate.sdc @@ -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 + +set_driving_cell [all_inputs] -lib_cell BUF_X2 +set_load 10.0 [all_outputs] diff --git a/synth/nox_abc.nangate.sdc b/synth/nox_abc.nangate.sdc new file mode 100755 index 0000000..f887018 --- /dev/null +++ b/synth/nox_abc.nangate.sdc @@ -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 + +set_driving_cell BUF_X2 +set_load 10.0 [all_outputs] diff --git a/synth/nox_lr_synth_conf.tcl b/synth/nox_lr_synth_conf.tcl new file mode 100755 index 0000000..5b90d3a --- /dev/null +++ b/synth/nox_lr_synth_conf.tcl @@ -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 + +# List of inputs and outputs. Number is timing constraint expressed as a % of +# clock cycle, e.g. +# +# {instr_req_o 70.0} +# +# as an output means the instr_req_o output must be stable by 60% of total clock +# cycle +# +# {instr_gnt_i 30.0} +# +# as an input means the instr_gnt_i input will be stable by 30% of the total +# clock cycle + +# These IO constraints are an educated guess, they effectively assume there's a +# bit of external logic on the inputs and outputs but not much before they reach +# a flop. +set lr_synth_outputs [list {instr_axi_mosi_o 80.0} \ + {lsu_axi_mosi_o 80.0}] + +set lr_synth_inputs [list {start_fetch_i 30} \ + {start_addr_i 30} \ + {irq_i 30} \ + {instr_axi_miso_i 30} \ + {lsu_axi_miso_i 30}] + +# clock and reset IO names (at top-level) +set lr_synth_clk_input clk +set lr_synth_rst_input arst + +# clock period in ps, this gives a 250 MHz clock. using the nangate45 library +# Ibex can happily meet this on all paths with the lr_synth_abc_clk_uprate +# setting below. With a lower uprate timing may not be met. +set lr_synth_clk_period 4000.0 + +# Amount to subtract from clk period to give the clock period passed to ABC in +# the synth flow. ABC maps the design to the standard cell library and +# optimises paths for timing, better results are obtained by giving it a faster +# clock period so it optimises more. +set lr_synth_abc_clk_uprate 2000.0 diff --git a/synth/pdk_45nm b/synth/pdk_45nm new file mode 160000 index 0000000..1bc06f3 --- /dev/null +++ b/synth/pdk_45nm @@ -0,0 +1 @@ +Subproject commit 1bc06f3235cb2a30491b64cf059a90fb4add6817 diff --git a/synth/prim/BUILD b/synth/prim/BUILD new file mode 100755 index 0000000..c87fb86 --- /dev/null +++ b/synth/prim/BUILD @@ -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 + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]) + [ + ], +) diff --git a/synth/prim/README.md b/synth/prim/README.md new file mode 100755 index 0000000..62dfd43 --- /dev/null +++ b/synth/prim/README.md @@ -0,0 +1,229 @@ +# lowRISC Hardware Primitives + +[`prim_alert`](https://reports.opentitan.org/hw/ip/prim/dv/prim_alert/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/prim_alert/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_alert/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_alert/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_alert/code.svg) + +[`prim_esc`](https://reports.opentitan.org/hw/ip/prim/dv/prim_esc/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/prim_esc/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_esc/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_esc/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_esc/code.svg) + +[`prim_lfsr`](https://reports.opentitan.org/hw/ip/prim/dv/prim_lfsr/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/prim_lfsr/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_lfsr/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_lfsr/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_lfsr/code.svg) + +[`prim_present`](https://reports.opentitan.org/hw/ip/prim/dv/prim_lfsr/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/prim_present/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_present/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_present/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_present/code.svg) + +[`prim_prince`](https://reports.opentitan.org/hw/ip/prim/dv/prim_lfsr/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/prim_prince/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_prince/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_prince/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/prim_prince/code.svg) + +## 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/synth/prim/doc/prim_clock_gp_mux2.md b/synth/prim/doc/prim_clock_gp_mux2.md new file mode 100755 index 0000000..b73a288 --- /dev/null +++ b/synth/prim/doc/prim_clock_gp_mux2.md @@ -0,0 +1,26 @@ +# Primitive Component: Two input clock Mux with glitch protection + +# Overview +`prim_clock_gp_mux2` is a two input clock mux that protects a glitch. When a current clock source is switched to the next clock source where two clocks are totally unrelated, a glitch can be generated as follows. + +### Glitch +```wavejson +{signal: [ + {name: 'clk0_i', wave: 'L.H....L....H....L....H....'}, + {name: 'clk1_i', wave: 'L.H.L.H.L.H.L.H.L.H.L.H.L.H'}, + {name: 'sel_i', wave: '0............1.............'}, + {name: 'clk_o', wave: 'L.H....L....HLH.L.H.L.H.L.H'}, + ]} +``` + +This glitch free clock mux can avoid glitch by placing two parallel synchronizers connected to each other. 1st flop and 2nd flop are triggered by positive edge and negative edge respectively to protect metastability on the sel_i signal. The following waveform shows the result. + +### Glitch Free +```wavejson +{signal: [ + {name: 'clk0_i', wave: 'L.H....L....H....L....H....'}, + {name: 'clk1_i', wave: 'L.H.L.H.L.H.L.H.L.H.L.H.L.H'}, + {name: 'sel_i', wave: '0............1.............'}, + {name: 'clk_o', wave: 'L.H....L....H....LH.L.H.L.H'}, + ]} +``` diff --git a/synth/prim/doc/prim_flash.md b/synth/prim/doc/prim_flash.md new file mode 100755 index 0000000..b5073e3 --- /dev/null +++ b/synth/prim/doc/prim_flash.md @@ -0,0 +1,167 @@ +# Primitive Component: Flash Wrapper + +# Overview +`prim_flash` is a wrapper interface for technology specific flash modules. + +As the exact details of each technology can be different, this document mainly describes the interface requirements and their functions. +The wrapper however does assume that all page sizes are the same (they cannot be different between data and info partitions, or different types of info partitions). + +## Parameters + +Name | type | Description +---------------|--------|---------------------------------------------------------- +NumBanks | int | Number of flash banks. Flash banks are assumed to be identical, asymmetric flash banks are not supported +InfosPerBank | int | Maximum number of info pages in the info partition. Since info partitions can have multiple types, this is max among all types. +InfoTypes | int | The number of info partition types, this number can be 1~N. +InfoTypesWidth | int | The number of bits needed to represent the info types. +PagesPerBank | int | The number of pages per bank for data partition. +WordsPerPage | int | The number of words per page per bank for both information and data partition. +DataWidth | int | The full data width of a flash word (inclusive of metadata) +MetaDataWidth | int | The metadata width of a flash word +TestModeWidth | int | The number of test modes for a bank of flash + + +## Signal Interfaces + +### Overall Interface Signals +Name | In/Out | Description +------------------------|--------|--------------------------------- +clk_i | input | Clock input +rst_ni | input | Reset input +flash_req_i | input | Inputs from flash protocol and physical controllers +flash_rsp_o | output | Outputs to flash protocol and physical controllers +prog_type_avail_o | output | Available program types in this flash wrapper: Currently there are only two types, program normal and program repair +init_busy_o | output | The flash wrapper is undergoing initialization +tck_i | input | jtag tck +tdi_i | input | jtag tdi +tms_i | input | jtag tms +tdo_o | output | jtag tdo +bist_enable_i | input | lc_ctrl_pkg :: On for bist_enable input +scanmode_i | input | dft scanmode input +scan_en_i | input | dft scan shift input +scan_rst_ni | input | dft scanmode reset +flash_power_ready_h_i | input | flash power is ready (high voltage connection) +flash_power_down_h_i | input | flash wrapper is powering down (high voltage connection) +flash_test_mode_a_i | input | flash test mode values (analog connection) +flash_test_voltage_h_i | input | flash test mode voltage (high voltage connection) +flash_err_o | output | flash level error interrupt indication, cleared on write 1 to status register +flash_alert_po | output | flash positive detector alert +flash_alert_no | output | flash negative detector alert +flash_alert_ack | input | single pulse ack +flash_alert_trig | input | alert force trig by SW +tl_i | input | TL_UL interface for rd/wr registers access +tl_o | output | TL_UL interface for rd/wr registers access +### Flash Request/Response Signals + +Name | In/Out | Description +-------------------|--------|--------------------------------- +rd | input | read request +prog | input | program request +prog_last | input | last program beat +prog_type | input | type of program requested: currently there are only two types, program normal and program repair +pg_erase | input | page erase request +bk_erase | output | bank erase request +erase_suspend | input | erase suspend request +addr | input | requested transaction address +part | input | requested transaction partition +info_sel | input | if requested transaction is information partition, the type of information partition accessed +he | input | high endurance enable for requested address +prog_data | input | program data +ack | output | transaction acknowledge +rd_data | output | transaction read data +done | output | transaction done + + + +# Theory of Operations + +## Transactions + +Transactions into the flash wrapper follow a req / ack / done format. +A request is issued by raising one of `rd`, `prog`, `pg_erase` or `bk_erase` to 1. +When the flash wrapper accepts the transaction, `ack` is returned. +When the transaction fully completes, a `done` is returned as well. + +Depending on the type of transaction, there may be a significant gap between `ack` and `done`. +For example, a read may have only 1 or 2 cycles between transaction acknowledgement and transaction complete. +Whereas a program or erase may have a gap extending up to uS or even mS. + +It is the flash wrapper decision on how many outstanding transaction to accept. +The following are examples for read, program and erase transactions. + +### Read +```wavejson +{signal: [ + {name: 'clk_i', wave: 'p.................'}, + {name: 'rd_i', wave: '011..0.1..0.......'}, + {name: 'addr_i', wave: 'x22..x.2..x.......'}, + {name: 'ack_o', wave: '010.10...10.......'}, + {name: 'done_o', wave: '0....10...10....10'}, + {name: 'rd_data_o', wave: 'x....2x...2x....2x'}, +]} +``` + +### Program +```wavejson +{signal: [ + {name: 'clk_i', wave: 'p................'}, + {name: 'prog_i', wave: '011...0.1....0...'}, + {name: 'prog_type_i', wave: 'x22...x.2....x...'}, + {name: 'prog_data_i', wave: 'x22...x.2....x...'}, + {name: 'prog_last_i', wave: '0.......1....0...'}, + {name: 'ack_o', wave: '010..10.....10...'}, + {name: 'done_o', wave: '0..............10'}, +]} +``` + +### Erase +```wavejson +{signal: [ + {name: 'clk_i', wave: 'p................'}, + {name: '*_erase_i', wave: '01.0.........1.0.'}, + {name: 'ack_o', wave: '0.10..........10.'}, + {name: 'done_o', wave: '0.....10.........'}, +]} +``` + +## Initialization + +The flash wrapper may undergo technology specific initializations when it is first powered up. +During this state, it asserts the `init_busy` to inform the outside world that it is not ready for transactions. +During this time, if a transaction is issued towards the flash wrapper, the transaction is not acknowledged until the initialization is complete. + +## Program Beats + +Since flash programs can take a significant amount of time, certain flash wrappers employ methods to optimize the program operation. +This optimization may place an upper limit on how many flash words can be handled at a time. +The purpose of the `prog_last` is thus to indicate when a program burst has completed. + +Assume the flash wrapper can handle 16 words per program operation. +Assume a program burst has only 15 words to program and thus will not fill up the full program resolution. +On the 15th word, the `prog_last` signal asserts and informs the flash wrapper that it should not expect a 16th word and should proceed to complete the program operation. + +## Program Type +The `prog_type` input informs the flash wrapper what type of program operation it should perform. +A program type not supported by the wrapper, indicated through `prog_type_avail` shall never be issued to the flash wrapper. + +## Erase Suspend +Since erase operations can take a significant amount of time, sometimes it is necessary for software or other components to suspend the operation. +The suspend operation input request starts with `erase_suspend_req` assertion. Flash wrapper circuit acks when wrapper starts suspend. +When the erase suspend completes, the flash wrapper circuitry also asserts `done` for the ongoing erase transaction to ensure all hardware gracefully completes. + +The following is an example diagram +```wavejson +{signal: [ + {name: 'clk_i', wave: 'p................'}, + {name: 'pg_erase_i', wave: '01.0..............'}, + {name: 'ack_o', wave: '0.10...10........'}, + {name: 'erase_suspend_i', wave: '0.....1.0........'}, + {name: 'done_o', wave: '0............10..'}, + ] + } +``` + +## Error Interrupt +The `flash_err_o` is a level interrupt indication, that is asserted whenever an error event occurs in one of the Flash banks. +An Error status register is used to hold the error source of both banks, and it is cleared on writing 1 to the relevant bit. +Clearing the status register trigs deassertion of the interrupt. diff --git a/synth/prim/doc/prim_keccak.md b/synth/prim/doc/prim_keccak.md new file mode 100755 index 0000000..122489e --- /dev/null +++ b/synth/prim/doc/prim_keccak.md @@ -0,0 +1,86 @@ +# 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] | permuted 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/synth/prim/doc/prim_lfsr.md b/synth/prim/doc/prim_lfsr.md new file mode 100755 index 0000000..0531fdd --- /dev/null +++ b/synth/prim/doc/prim_lfsr.md @@ -0,0 +1,85 @@ +# 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. + + +## 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 table contains polynomials for both +LFSR forms and range from 3bit to 168bit. +The polynomial coefficients have been obtained from +[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 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/synth/prim/doc/prim_packer.md b/synth/prim/doc/prim_packer.md new file mode 100755 index 0000000..97297ac --- /dev/null +++ b/synth/prim/doc/prim_packer.md @@ -0,0 +1,106 @@ +# 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 +EnProtection | bit | Check FI attack on position counter + +## 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. +err_o | output | When EnProtection is set, the error is reported through this port. This signal is asynchronous to the datapath. + +# Theory of Operations + +```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 '] + } +} +``` + +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/synth/prim/doc/prim_packer_fifo.md b/synth/prim/doc/prim_packer_fifo.md new file mode 100755 index 0000000..b79f565 --- /dev/null +++ b/synth/prim/doc/prim_packer_fifo.md @@ -0,0 +1,64 @@ +# Primitive Component: Packer FIFO + +# Overview + +`prim_packer_fifo` is a module that supports three modes of operation: packing, +unpacking, and single depth FIFO modes. Packing mode is where the input +data width is less than the output data width. Unpacking mode is where the input +data width is greater than the output data width. Single depth FIFO is where +the input and output data widths are the same. Because masking options are not +supported, the larger data size must be an even multiple of the smaller size. +The controls for this module are modeled after the `prim_fifo_sync` module, +both in name and functional behavior. +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 +-------------|--------|------------- +clk_i | input | Input clock +rst_ni | input | Input reset, negative active +clr_i | input | Input clear, clears all internal flops. +wvalid_i | input | Writes data into the first available position. +wdata_i[InW] | input | Input data. +wready_o | output | Indicates if prim_packer_fifo is able to accept data. +rvalid_o | output | Indicates if output data is valid. +rdata_o[OutW]| output | Output data. +rready_i | input | Output data is popped from the FIFO. +depth_o | output | Indicates the fullness of the FIFO. + +# Theory of Operations + +```code + /----------\ +wvalid_i | | rvalid_o +---------->| |---------------> +wdata_i | Flop | rdata_o +=====/====>| FIFO |=======/=======> + [InW] | | [OutW] + | | depth_o + | |---------------> +wready_o | | rready_i +<----------| |<--------------- + | | + \----------/ +``` + +In pack mode, `prim_packer_fifo` accepts `InW` bits of data. On a `wvalid_i`/ +`wready_o` handshake, `wdata_i` is stored to internal registers and accumulated +until `OutW` data has been gathered. Once the FIFO is full, a single pop (when +rvalid_o and rready_i are coincident), will clear the data and depth values on +the next clock cycle. The complimentary flow occurs when the`prim_packer_fifo` +module is in unpack mode. + +The internal register size is the greater of `InW` and `OutW` bits. +Timing diagrams are shown in the header of the `prim_packer_fifo` module. diff --git a/synth/prim/doc/prim_present.md b/synth/prim/doc/prim_present.md new file mode 100755 index 0000000..c4c51f9 --- /dev/null +++ b/synth/prim/doc/prim_present.md @@ -0,0 +1,84 @@ +# 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 +NumPhysRounds | int | Number of physically instantiated PRESENT rounds (defaults to NumRounds) + +Note that by setting `NumRounds = 31` and `NumPhysRounds = 1` we can construct a PRESENT primitive that is suitable for iterative evaluation of a full-round PRESENT pass, where each iteration evaluates one of the 31 rounds. + +## Signal Interfaces + +Name | In/Out | Description +-------------|--------|--------------------------------- +data_i | input | Plaintext input +key_i | input | Key input +idx_i | input | Round index input +data_o | output | Output of the ciphertext +key_o | output | Key output after keyschedule update +idx_o | output | Round index output after keyschedule update + +The `key_o` and `idx_o` are useful for iterative implementations where the state of the scheduled key, as well as the current round index have to be registered in between rounds. +Note that `idx_i` should be initialized to 1 for encryption mode, and to `NumRounds` for decryption mode. + +# Theory of Operations + +``` + /---------------\ + | | +idx_i | | idx_o +=====/======>| |=====/======> + [5] | | [5] + | PRESENT | +key_i | | key_o +=====/======>| DataWidth |=====/======> + [KeyWidth] | KeyWidth | [KeyWidth] + | NumRounds | +data_i | NumPhysRounds | 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 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, as illustrated for the encryption pass below: + +```c++ +NumRounds = 32; +idx_i = 1; + +round_keys = key_derivation(key_i, idx_i); + +state = data_i; + +for (int i=0; i < NumRounds; i++) { + state = state ^ round_keys[i]; + state = sbox4_layer(state); + state = perm_layer(state); +} + +data_o = state ^ round_keys[NumRounds-1]; +key_o = round_keys[NumRounds-1]; +idx_o = idx_i + NumRounds; +``` + +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/synth/prim/doc/prim_prince.md b/synth/prim/doc/prim_prince.md new file mode 100755 index 0000000..eb4576a --- /dev/null +++ b/synth/prim/doc/prim_prince.md @@ -0,0 +1,120 @@ +# 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 (data and key state registers placed half-way in the design can optionally be enabled). +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. +HalfwayDataReg | bit | If set to 1, instantiates a data register half-way in the data path +HalfwayKeyReg | bit | If set to 1, instantiates a key register half-way in the data path. This is only required if the key is not static and changes with every new data input. + +## Signal Interfaces + +Name | In/Out | Description +-------------|--------|--------------------------------- +clk_i | input | Clock input +rst_ni | input | Reset input +valid_i | input | Data valid input +data_i | input | Plaintext input +data_i | input | Plaintext input +key_i | input | Key input +dec_i | input | Assert for decryption +valid_o | output | Data valid output +data_o | output | Output of the ciphertext + +# Theory of Operations + +``` + /-----------------\ +clk_i / rst_ni | | +-------------->| | +dec_i | | +-------------->| PRINCE | +valid_i | | valid_o +-------------->| DataWidth |---------------> +key_i | KeyWidth | +=====/========>| NumRounds | + [KeyWidth] | UseOldKeySched | data_o + | HalfwayDataReg |=======/=======> +data_i | HalfwayKeyReg | [DataWidth] +=====/========>| | + [DataWidth] | | + | | + \-----------------/ +``` + +The PRINCE module is fully unrolled and combinational by default. +But since data and key state registers can optionally be enabled, the primitive also has a clock, reset and valid input besides the key and plaintext inputs. +On the output side it exposes the ciphertext with its corresponding valid signal. + +The internal construction follows 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/synth/prim/doc/prim_ram_1p_scr.md b/synth/prim/doc/prim_ram_1p_scr.md new file mode 100755 index 0000000..ffdaebc --- /dev/null +++ b/synth/prim/doc/prim_ram_1p_scr.md @@ -0,0 +1,123 @@ +# Primitive Component: SRAM Scrambler + +# Overview + +The scrambling primitive `prim_ram_1p_scr` employs a reduced-round (5 instead of 11) PRINCE block cipher in CTR mode to scramble the data. +The PRINCE lightweight block cipher has been selected due to its low latency and low area characteristics, see also [prim_prince](./prim_prince.md) for more information on PRINCE. +The number of rounds is reduced to 5 in order to ease timing pressure and ensure single cycle operation (the number of rounds can always be increased if it turns out that there is enough timing slack). + +In [CTR mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)), the block cipher is used to encrypt a 64bit IV with the scrambling key in order to create a 64bit keystream block that is bitwise XOR'ed with the data in order to transform plaintext into ciphertext and vice versa. +The IV is assembled by concatenating a nonce with the word address. + +If the word width of the scrambled memory is smaller than 64bit, the keystream block is truncated to fit the data width. +If the word width is wider than 64bit, the scrambling primitive by default instantiates multiple PRINCE primitives in order to create a unique keystream for the full datawidth. +For area constrained settings, the parameter `ReplicateKeyStream` in `prim_ram_1p_scr` can be set to 1 in order to replicate the keystream block generated by one single primitive instead of using multiple parallel PRINCE instances (but it should be understood that this lowers the level of security). + +Since plain CTR mode does not diffuse the data bits due to the bitwise XOR, the scheme is augmented by passing each individual word through a two-layer substitution-permutation (S&P) network implemented with the `prim_subst_perm` primitive (the diffusion chunk width can be parameterized via the `DiffWidth` parameter). +The S&P network employed is similar to the one employed in PRESENT and will be explained in more detail [further below](#custom-substitution-permutation-network). +Note that if individual bytes need to be writable without having to perform a read-modify-write operation, the diffusion chunk width should be set to 8. + +Another CTR mode augmentation that is aimed at breaking the linear address space is SRAM address scrambling. +The same two-layer S&P network that is used for byte diffusion is leveraged to non-linearly remap the SRAM address as shown in the block diagram above. +As opposed to the byte diffusion S&P networks, this particular address scrambling network additionally XOR's in a nonce that has the same width as the address. + +## Parameters + +The following table lists the instantiation parameters of the `prim_ram_1p_scr` primitive. +These are not exposed in the `sram_ctrl` IP, but have to be set directly when instantiating `prim_ram_1p_scr` in the top. + +Parameter | Default (Max) | Top Earlgrey | Description +----------------------------|-----------------------|--------------|--------------- +`Depth` | 512 | multiple | SRAM depth, needs to be a power of 2 if `NumAddrScrRounds` > 0. +`Width` | 32 | 32 | Effective SRAM width without redundancy. +`DataBitsPerMask` | 8 | 8 | Number of data bits per write mask. +`EnableParity` | 1 | 1 | This parameter enables byte parity. +`CfgWidth` | 8 | 8 | Width of SRAM attributes field. +`NumPrinceRoundsHalf` | 2 (5) | 2 | Number of PRINCE half-rounds. +`NumDiffRounds` | 2 | 2 | Number of additional diffusion rounds, set to 0 to disable. +`DiffWidth` | 8 | 8 | Width of additional diffusion rounds, set to 8 for intra-byte diffusion. +`NumAddrScrRounds` | 2 | 2 | Number of address scrambling rounds, set to 0 to disable. +`ReplicateKeyStream` | 0 (1) | 0 | If set to 1, the same 64bit key stream is replicated if the data port is wider than 64bit. Otherwise, multiple PRINCE primitives are employed to generate a unique keystream for the full data width. + +## Signal Interfaces + +Signal | Direction | Type | Description +---------------------------|------------------|------------------------------------|--------------- +`key_valid_i` | `input` | `logic` | Indicates whether the key and nonce are considered valid. New memory requests are blocked if this is set to 0. +`key_i` | `input` | `logic [127:0]` | Scrambling key. +`nonce_i` | `input` | `logic [NonceWidth-1:0]` | Scrambling nonce. +`req_i` | `input` | `logic` | Memory request indication signal (from TL-UL SRAM adapter). +`gnt_o` | `output` | `logic` | Grant signal for memory request (to TL-UL SRAM adapter) +`write_i` | `input` | `logic` | Indicates that this is a write operation (from TL-UL SRAM adapter). +`addr_i` | `input` | `logic [AddrWidth-1:0]` | Address for memory op (from TL-UL SRAM adapter). +`wdata_i` | `input` | `logic [Width-1:0]` | Write data (from TL-UL SRAM adapter). +`wmask_i` | `input` | `logic [Width-1:0]` | Write mask (from TL-UL SRAM adapter). +`intg_error_i` | `input` | `logic` | Indicates whether the incoming transaction has an integrity error +`rdata_o` | `output` | `logic [Width-1:0]` | Read data output (to TL-UL SRAM adapter). +`rvalid_o` | `output` | `logic` | Read data valid indication (to TL-UL SRAM adapter). +`rerror_o` | `output` | `logic [1:0]` | Error indication (to TL-UL SRAM adapter). Bit 0 indicates a correctable and bit 1 an uncorrectable error. Note that at this time, only uncorrectable errors are reported, since the scrambling device only supports byte parity. +`raddr_o` | `output` | `logic [31:0]` | Address of the faulty read operation. +`cfg_i` | `input` | `logic [CfgWidth-1:0]` | Attributes for physical memory macro. + +## Custom Substitution Permutation Network + +In addition to the PRINCE primitive, `prim_ram_1p_scr` employs a custom S&P network for byte diffusion and address scrambling. +The structure of that S&P network is similar to the one used in PRESENT, but it uses a modified permutation function that makes it possible to parameterize the network to arbitrary data widths as shown in the pseudo code below. + +```c++ + +NUM_ROUNDS = 2; +DATA_WIDTH = 8; // bitwidth of the data + +// Apply PRESENT Sbox4 on all nibbles, leave uppermost bits unchanged +// if the width is not divisible by 4. +state_t sbox4_layer(state) { + for (int i = 0; i < DATA_WIDTH/4; i ++) { + nibble_t nibble = get_nibble(state, i); + nibble = present_sbox4(nibble) + set_nibble(state, i, nibble); + } + return state; +} + +// Reverses the bit order. +state_t flip_vector(state) { + state_t state_flipped; + for (int i = 0; i < DATA_WIDTH; i ++) { + state_flipped[i] = state[width-1-i]; + } + return state_flipped; +} + +// Gather all even bits and put them into the lower half. +// Gather all odd bits and put them into the upper half. +state_t perm_layer(state) { + // Initialize with input state. + // If the number of bits is odd, the uppermost bit + // will stay in position, as intended. + state_t state_perm = state; + for (int i = 0; i < DATA_WIDTH/2; i++) { + state_perm[i] = state[i * 2]; + state_perm[i + DATA_WIDTH/2] = state[i * 2 + 1]; + } + return state_perm; +} + +state_t prim_subst_perm(data_i, key_i) { + + state_t state = data_i; + for (int i = 0; i < NUM_ROUNDS; i++) { + state ^= key_i; + state = sbox4_layer(state); + // The vector flip and permutation operations have the + // combined effect that all bits will be passed through an + // Sbox4 eventually, even if the number of bits in data_i + // is not aligned with 4. + state = flip_vector(state); + state = perm_layer(state); + } + + return state ^ key_i; +} + +``` diff --git a/synth/prim/doc/prim_xoshiro256pp.md b/synth/prim/doc/prim_xoshiro256pp.md new file mode 100755 index 0000000..267af60 --- /dev/null +++ b/synth/prim/doc/prim_xoshiro256pp.md @@ -0,0 +1,73 @@ +# Primitive Component: XoShiRo256++ + + +`prim_xoshiro256pp` is a PRNG with 256-bit state. +For more information refer to [this page](https://arxiv.org/pdf/1805.01407.pdf). + +## Parameters + +Name | type | Description +-------------|--------|---------------------------------------------------------- +OutputDw | int | Width of the PRNG output. Must be a multiple of 64. +DefaultSeed | logic | Initial 256-bit state of the PRNG, must be nonzero. + +## Signal Interfaces + +Name | In/Out | Description +---------------------|--------|--------------------------------- +seed_en_i | input | External seed input enable +seed_i[256] | input | External seed input +xoshiro_en_i | input | PRNG enable +entropy_i[256] | input | Entropy input +data_o[OutputDw] | output | PRNG output +all_zero_o | output | Impossible all-zero state + +# Theory of Operations + +``` + /----------------\ +seed_en_i | | +------------>| xoshiro256++ | +seed_i | | data_o +=====/======>| |=====/=======> + [256] | | [OutputDw] +xoshiro_en_i | | +------------>| OutputDw | +entropy_i | DefaultSeed | all_zero_o +=====/======>| |-------------> + [256] | | + | | + \----------------/ +``` + +Xoshiro256++ PRNG consists of: + * A 256-bit state + * A single-cycle state-update function. + * A state output function. + +The basic xoshiro256++ PRNG has a 64-bit output. +This implementation supports an output size of any multiple of 64 bits. +The output size is controlled using the `OutputDW` parameter. + +The xoshiro256++ module has an enable input and an additional entropy input that is +XORed into the PRNG state (connect to zero if this feature is unused). +As the PRNG may jump into the all-zero state (e.g. due to an active attack), the PRNG +state-update function contains a lockup protection which re-seeds the state with +`DefaultSeed` and raises the alert signal `all_zero_o`, once this condition is detected. + +When the seed enable signal `seed_en_i` is raised, the internal state of xoshiro256++ is updated +with the value provided at the 256-bit input 'seed_i'. +The state is internally updated in every clock cycle whenever the enable signal `xoshiro_en_i` is raised. +The timing diagram below visualizes this process. + +```wavejson +{ + signal: [ + {name: 'clk', wave: 'p......|....'}, + {name: 'xoshiro_en_i', wave: '0...1..|..0.'}, + {name: 'seed_en_i', wave: '010....|....'}, + {name:'seed_i', wave: '3..x...|....', data: 'Seed' }, + {name: 'state', wave: 'x.3..45|678.', data: 'Seed'} + ] +} +``` diff --git a/synth/prim/dv/prim_alert/data/prim_alert_cover.cfg b/synth/prim/dv/prim_alert/data/prim_alert_cover.cfg new file mode 100755 index 0000000..597ed64 --- /dev/null +++ b/synth/prim/dv/prim_alert/data/prim_alert_cover.cfg @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + ++module prim_alert_sender ++module prim_alert_receiver +begin tgl(portsonly) + +module prim_alert_sender + +module prim_alert_receiver +end diff --git a/synth/prim/dv/prim_alert/data/prim_alert_testplan.hjson b/synth/prim/dv/prim_alert/data/prim_alert_testplan.hjson new file mode 100755 index 0000000..c044171 --- /dev/null +++ b/synth/prim/dv/prim_alert/data/prim_alert_testplan.hjson @@ -0,0 +1,108 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "prim_alert" + testpoints: [ + { + name: prim_alert_request_test + desc: '''Verify alert request from prim_alert_sender. + + - Send an alert request by driving `alert_req` pin to 1. + - Verify that `alert_ack` signal is set and the alert handshake completes. + - If the alert is fatal, verify if the alert continuous fires until a reset is + issued. + ''' + stage: V1 + tests: ["prim_async_alert", + "prim_async_fatal_alert", + "prim_sync_alert", + "prim_sync_fatal_alert"] + } + + { + name: prim_alert_test + desc: '''Verify alert test request from prim_alert_sender. + + - Send an alert test request by driving `alert_test` pin to 1. + - Verify that alert handshake completes and `alert_ack` signal stays low. + ''' + stage: V1 + tests: ["prim_async_alert", + "prim_async_fatal_alert", + "prim_sync_alert", + "prim_sync_fatal_alert"] + } + + { + name: prim_alert_ping_request_test + desc: '''Verify ping request from prim_alert_sender. + + - Send a ping request by driving `ping_req` pin to 1. + - Verify that `ping_ok` signal is set and ping handshake completes. + ''' + stage: V1 + tests: ["prim_async_alert", + "prim_async_fatal_alert", + "prim_sync_alert", + "prim_sync_fatal_alert"] + } + + { + name: prim_alert_init_trigger_test + desc: '''Verify init_trigger input from prim_alert_receiver. + + This test is based on previous tests: + - Based on the prim_alert_request_test, this test adds a parallel sequence to randomly + drive `init_trigger_i` in prim_alert_receiver. + Check if `alert_ack_o` returns 1 and check if alert sender/receiver pairs can resume + normal handshake after alert init finishes. + For fatal alert, check if fatal alerts keep firing until reset is issued. + - Based on prim_alert_ping_request_test, this test adds a parallel sequence to randomly + drive `init_trigger_i` in prim_alert_receiver. + Check `ping_ok` returns 1. + ''' + stage: V2 + tests: ["prim_async_alert", + "prim_async_fatal_alert", + "prim_sync_alert", + "prim_sync_fatal_alert"] + } + + { + name: prim_alert_integrity_errors_test + desc: '''Verify the integrity errors from the prim_alert_sender and prim_alert_receiver pair. + + 1). `Ack_p/n` integrity error: + - Send an alert reqeust by driving `alert_req` pin to 1. + - Force `ack_p` signal to stay low to trigger an integrity error. + - Verify that prim_alert_receiver can identify the integrity error by setting + `integ_fail_o` output to 1. + 2). `Ping_p/n` integrity error: + - Send a ping request by driving `ping_req` to 1. + - Force `ping_n` signal to 1 to trigger an integrity error. + - Verify that prim_alert_receiver can identify the integrity error by setting + `integ_fail_o` output to 1. + ''' + stage: V1 + tests: ["prim_async_alert", + "prim_async_fatal_alert", + "prim_sync_alert", + "prim_sync_fatal_alert"] + } + + { + name: prim_alert_gate_sender_clk_rst_test + desc: '''Gate prim_alert_sender clk or reset during alert_handshake. + + - During alert handshake, gate prim_alert_sender's clock or issue reset. + - Verify alert handshake can resume after prim_alert_sender is re-initialized. + - If clock was gated, verify alert handshake will resume when clock is active, + and verify we will never miss or drop an alert handshake by expecting `alert_ack_o` + to return 1 after `alert_req` is sent. + ''' + stage: V3 + tests: [] + } + ] +} diff --git a/synth/prim/dv/prim_alert/prim_alert_sim.core b/synth/prim/dv/prim_alert/prim_alert_sim.core new file mode 100755 index 0000000..1967637 --- /dev/null +++ b/synth/prim/dv/prim_alert/prim_alert_sim.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:dv:prim_alert_sim:0.1" +description: "ALERT DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:alert + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_test_status + - lowrisc:dv:common_ifs + files: + - tb/prim_alert_tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: prim_alert_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target + diff --git a/synth/prim/dv/prim_alert/prim_alert_sim_cfg.hjson b/synth/prim/dv/prim_alert/prim_alert_sim_cfg.hjson new file mode 100755 index 0000000..b36d23b --- /dev/null +++ b/synth/prim/dv/prim_alert/prim_alert_sim_cfg.hjson @@ -0,0 +1,78 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_alert + + // Top level dut name (sv module). + dut: prim_alert + + // Top level testbench name (sv module). + tb: prim_alert_tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_alert_sim:0.1 + + // Testplan hjson file. + testplan: "{proj_root}/hw/ip/prim/dv/prim_alert/data/prim_alert_testplan.hjson" + + // Import additional common sim cfg files. + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + + // Default iterations for all tests - each test entry can override this. + reseed: 20 + + // Enable cdc instrumentation + run_opts: ["+cdc_instrumentation_enabled=1"] + + build_modes: [ + { + name: sync_alert + build_opts: ["+define+IS_SYNC"] + } + { + name: fatal_alert + build_opts: ["+define+IS_FATAL"] + } + { + name: sync_fatal_alert + build_opts: ["+define+IS_FATAL", "+define+IS_SYNC"] + } + ] + + // List of test specifications. + tests: [ + { + name: prim_async_alert + } + { + name: prim_async_fatal_alert + build_mode: fatal_alert + } + { + name: prim_sync_alert + build_mode: sync_alert + } + { + name: prim_sync_fatal_alert + build_mode: sync_fatal_alert + } + ] + // List of regressions. + regressions: [ + { + name: smoke + tests: ["prim_async_alert"] + } + ] + overrides: [ + { + name: vcs_cov_cfg_file + value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_alert/data/prim_alert_cover.cfg" + } + ] +} diff --git a/synth/prim/dv/prim_alert/tb/prim_alert_tb.sv b/synth/prim/dv/prim_alert/tb/prim_alert_tb.sv new file mode 100755 index 0000000..228ecd2 --- /dev/null +++ b/synth/prim/dv/prim_alert/tb/prim_alert_tb.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 +// +// Testbench module for prim_alert_sender and prim_alert_receiver pair. +// +// This direct test has five test sequences: +// 1). Alert request sequence. +// 2). Alert test sequence. +// 3). Ping request sequence. +// 4). `Ack_p/n` integrity check sequence. +// 5). `Ping_p/n` integrity check sequence. +// +// This direct sequence did not drive `ack_p/n` to trigger integrity error because this sequence is +// covered in alert_handler IP level test. + +module prim_alert_tb; + + import dv_utils_pkg::*; + + ////////////////////////////////////////////////////// + // config + ////////////////////////////////////////////////////// + + // this can be overriden on the command line + `ifdef IS_SYNC + localparam bit IsAsync = 0; + `else + localparam bit IsAsync = 1; + `endif + `ifdef IS_FATAL + localparam bit IsFatal = 1; + `else + localparam bit IsFatal = 0; + `endif + + localparam time ClkPeriod = 10_000; + localparam int WaitCycle = IsAsync ? 3 : 1; + + // Minimal cycles to wait between each sequence. + // The main concern here is the minimal wait cycles between each handshake. + localparam int MinHandshakeWait = 2 + WaitCycle; + + // Clock cycles for alert init handshake to finish. + localparam int WaitAlertInitDone = 30; + + // Clock cycles for alert or ping handshake to finish. + // Wait enough cycles to ensure assertions from design are checked. + localparam int WaitAlertHandshakeDone = 20; + + uint default_spinwait_timeout_ns = 100_000; + + typedef enum bit [3:0]{ + AlertSet, + AlertAckSet, + AlertReset, + AlertAckReset + } alert_handshake_e; + + typedef enum bit[1:0] { + PingPair, + AlertPair, + AckPair + } alert_signal_pair_e; + + ////////////////////////////////////////////////////// + // Clock and Reset + ////////////////////////////////////////////////////// + + wire clk, rst_n; + + clk_rst_if main_clk ( + .clk, + .rst_n + ); + + ////////////////////////////////////////////////////// + // DUTs + ////////////////////////////////////////////////////// + + logic alert_test, alert_req, alert_ack, alert_state; + logic ping_req, ping_ok, integ_fail, alert_o; + prim_alert_pkg::alert_rx_t alert_rx; + prim_alert_pkg::alert_tx_t alert_tx; + prim_mubi_pkg::mubi4_t init_trig = prim_mubi_pkg::MuBi4False; + prim_alert_sender #( + .AsyncOn(IsAsync), + .IsFatal(IsFatal) + ) i_alert_sender ( + .clk_i(clk), + .rst_ni(rst_n), + .alert_test_i(alert_test), + .alert_req_i(alert_req), + .alert_ack_o(alert_ack), + .alert_state_o(alert_state), + .alert_rx_i(alert_rx), + .alert_tx_o(alert_tx) + ); + + prim_alert_receiver #( + .AsyncOn(IsAsync) + ) i_alert_receiver ( + .clk_i(clk), + .rst_ni(rst_n), + .init_trig_i(init_trig), + .ping_req_i(ping_req), + .ping_ok_o(ping_ok), + .integ_fail_o(integ_fail), + .alert_o(alert_o), + .alert_rx_o(alert_rx), + .alert_tx_i(alert_tx) + ); + + ////////////////////////////////////////////////////// + // Helper Functions/Tasks and Variables + ////////////////////////////////////////////////////// + logic error = 0; + + ////////////////////////////////////////////////////// + // Stimuli Application / Response Checking + ////////////////////////////////////////////////////// + + initial begin: p_stimuli + alert_test = 0; + alert_req = 0; + ping_req = 0; + main_clk.set_period_ps(ClkPeriod); + main_clk.set_active(); + main_clk.apply_reset(); + + // Wait for initialization sequence to end + if ($urandom_range(0, 1)) begin + main_clk.wait_clks($urandom_range(0, WaitAlertInitDone)); + if ($urandom_range(0, 1)) begin + init_trig = prim_mubi_pkg::MuBi4True; + main_clk.wait_clks(1); + init_trig = prim_mubi_pkg::MuBi4False; + end else begin + main_clk.apply_reset(); + end + end + main_clk.wait_clks(WaitAlertInitDone); + $display("Init sequence finish"); + + // Sequence 1). Alert request sequence. + for (int num_trans = 1; num_trans <= 10; num_trans++) begin + fork + begin + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + alert_req = 1; + `DV_SPINWAIT(wait (alert_ack == 1);, , , "Wait for alert_ack timeout"); + alert_req = 0; + main_clk.wait_clks(WaitAlertHandshakeDone); + end + begin + if ($urandom_range(0, 1)) begin + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + init_trig = prim_mubi_pkg::MuBi4True; + main_clk.wait_clks($urandom_range(1, 10)); + init_trig = prim_mubi_pkg::MuBi4False; + main_clk.wait_clks(WaitAlertInitDone); + end + end + join + + if (IsFatal) begin + // For fatal alert, ensure alert keeps firing until reset. + // If only alert_init is triggered, alert_sender side still expect fatal alert to fire. + // This check is valid if the alert is fatal, and alert is requested before init request. + main_clk.wait_clks($urandom_range(10, 100)); + `DV_SPINWAIT(wait (alert_tx.alert_p == 0);, , , "Wait for alert_p goes low"); + `DV_SPINWAIT(wait (alert_tx.alert_p == 1);, , , "Wait for alert_p goes high"); + main_clk.wait_clks(WaitAlertHandshakeDone); + main_clk.apply_reset(); + main_clk.wait_clks(WaitAlertInitDone); + end + $display("[prim_alert_seq] Alert request sequence %0d/10 finished!", num_trans); + end + + // Sequence 2). Alert test sequence. + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + alert_test = 1; + main_clk.wait_clks(1); + alert_test = 0; + repeat ($urandom_range(10, 20)) begin + if (alert_ack == 1) begin + $error("Alert ack should not set high during alert_test sequence!"); + error = 1; + end + main_clk.wait_clks(1); + end + $display("[prim_alert_seq] Alert test sequence finished!"); + + // Sequence 3) Ping request sequence. + for (int num_trans = 0; num_trans < 10; num_trans++) begin + fork + begin + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + ping_req = 1; + `DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout"); + ping_req = 0; + main_clk.wait_clks(WaitCycle + WaitAlertHandshakeDone); + end + begin + if ($urandom_range(0, 1)) begin + main_clk.wait_clks($urandom_range(1, WaitAlertHandshakeDone + 10)); + init_trig = prim_mubi_pkg::MuBi4True; + main_clk.wait_clks($urandom_range(0, 10)); + init_trig = prim_mubi_pkg::MuBi4False; + main_clk.wait_clks(WaitAlertInitDone); + end + end + join + $display($sformatf("[prim_alert_seq] Ping request sequence[%0d] finished!", num_trans)); + end + + // Sequence 4) `Ack_p/n` integrity check sequence. + // Note that alert_tx signal interigy errors are verified in alert_handler testbench. + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + alert_req = 1; + + $assertoff(0, prim_alert_tb.i_alert_receiver.AckDiffOk_A); + force i_alert_receiver.alert_rx_o.ack_p = 0; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for integrity error timeout"); + alert_req = 0; + release i_alert_receiver.alert_rx_o.ack_p; + + // Wait until async or sync signal propogate from alert to ack. + main_clk.wait_clks(WaitCycle); + $asserton(0, prim_alert_tb.i_alert_receiver.AckDiffOk_A); + $display("[prim_alert_seq] Ack signal integrity error sequence finished!"); + + // Sequence 5) `Ping_p/n` integrity check sequence. + // Disable the assertion at least two clock cycles before sending the ping request, because the + // `PingDiffOk_A` assertion has ##2 delay. + $assertoff(0, prim_alert_tb.i_alert_receiver.PingDiffOk_A); + main_clk.wait_clks($urandom_range(MinHandshakeWait, 10)); + force i_alert_receiver.alert_rx_o.ping_n = i_alert_receiver.alert_rx_o.ping_n; + ping_req = 1; + + wait (integ_fail == 1); + ping_req = 0; + release i_alert_receiver.alert_rx_o.ping_n; + + // Ping is the first signal of the handshake, so we can directly turn on the assertion once the + // forced ping signal is released. + $asserton(0, prim_alert_tb.i_alert_receiver.PingDiffOk_A); + $display("[prim_alert_seq] Ping signal integrity error sequence finished!"); + + dv_test_status_pkg::dv_test_status(.passed(!error)); + $finish(); + end +endmodule diff --git a/synth/prim/dv/prim_esc/data/prim_esc_cover.cfg b/synth/prim/dv/prim_esc/data/prim_esc_cover.cfg new file mode 100755 index 0000000..3846386 --- /dev/null +++ b/synth/prim/dv/prim_esc/data/prim_esc_cover.cfg @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + ++module prim_esc_sender ++module prim_esc_receiver +begin tgl(portsonly) + +module prim_esc_sender + +module prim_esc_receiver +end diff --git a/synth/prim/dv/prim_esc/data/prim_esc_testplan.hjson b/synth/prim/dv/prim_esc/data/prim_esc_testplan.hjson new file mode 100755 index 0000000..4a2a5eb --- /dev/null +++ b/synth/prim/dv/prim_esc/data/prim_esc_testplan.hjson @@ -0,0 +1,91 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + name: "prim_esc" + testpoints: [ + { + name: prim_esc_request_test + desc: '''Verify escalation request from prim_esc_sender. + + - Send an escalation request by driving `esc_req` pin to 1. + - Wait random length of cycles and verify `esc_en` output is set and `integ_fail` + output remains 0. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + { + name: prim_ping_req_interrupted_by_esc_req_test + desc: '''Verify prim_esc will process the esc_req when ping handshake is in progress. + + - Send a ping request by driving `ping_req` pin to 1. + - Randomly wait a few cycles before the ping handshake is completed. + - Send an escalation request by driving `esc_req` pin to 1. + - Wait for `ping_ok` to set and `esc_req_out` to set. + - Check the sequence completes without any signal integrity error. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + { + name: prim_esc_tx_integrity_errors_test + desc: '''Verify `esc_tx` signal integrity error. + + - Send an escalation request by driving `esc_req` pin to 1. + - Force `esc_n` signal to stay high to trigger an integrity error. + - Verify that prim_esc_sender identifies the error by setting `integ_fail` signal. + - Release the `esc_n` signal. + - Send a ping request and repeat the above sequence and checkings. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + { + name: prim_esc_reverse_ping_timeout_test + desc: '''Verify prim_esc_receiver detects ping timeout. + + - Send a ping request by driving `ping_req` pin to 1. + - Wait for ping handshake to finish and `ping_ok` signal is set. + - Verify that `esc_en` output remains 0 and `integ_fail` output remains 0. + - Drive `ping_req` signal back to 0 and wait until ping counter timeout. + - Verify that `prim_esc_receiver` detects the ping timeout by setting `esc_en` output + to 1. + - Reset the DUT to clear `esc_en` output. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + { + name: prim_esc_receiver_counter_fail_test + desc: '''Verify prim_esc_receiver detects counter mismatch. + + - Send a ping request by driving `ping_req` pin to 1. + - Wait until `ping_ok` output sets to 1, which means the two counters start. + - Force one of the two identical counters to 0. + - Verify that prim_esc_receiver detects the counter mismatch and set `esc_en` signal to + 1. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + { + name: prim_esc_handshake_with_rand_reset_test + desc: '''Verify alert request from prim_alert_sender. + + - Send a ping request by driving `ping_req` pin to 1. + - Randomly issue reset during the escalation handshake. + - Verify that after reset, the prim_esc_sender and prim_esc_receiver pair functions + correctly by issuing the tests above. + ''' + stage: V1 + tests: ["prim_esc_test"] + } + + ] +} diff --git a/synth/prim/dv/prim_esc/prim_esc_sim.core b/synth/prim/dv/prim_esc/prim_esc_sim.core new file mode 100755 index 0000000..ef00ab6 --- /dev/null +++ b/synth/prim/dv/prim_esc/prim_esc_sim.core @@ -0,0 +1,31 @@ +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_esc_sim:0.1" +description: "ESCALATOR DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:esc + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_test_status + - lowrisc:dv:common_ifs + files: + - tb/prim_esc_tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: prim_esc_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/synth/prim/dv/prim_esc/prim_esc_sim_cfg.hjson b/synth/prim/dv/prim_esc/prim_esc_sim_cfg.hjson new file mode 100755 index 0000000..6658a55 --- /dev/null +++ b/synth/prim/dv/prim_esc/prim_esc_sim_cfg.hjson @@ -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 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_esc + + // Top level dut name (sv module). + dut: prim_esc + + // Top level testbench name (sv module). + tb: prim_esc_tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_esc_sim:0.1 + + // Testplan hjson file. + testplan: "{proj_root}/hw/ip/prim/dv/prim_esc/data/prim_esc_testplan.hjson" + + // Import additional common sim cfg files. + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + + // Default iterations for all tests - each test entry can override this. + reseed: 20 + + // Enable cdc instrumentation + run_opts: ["+cdc_instrumentation_enabled=1"] + + // List of test specifications. + tests: [ + { + name: prim_esc_test + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["prim_esc_test"] + } + ] + + overrides: [ + { + name: vcs_cov_cfg_file + value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_esc/data/prim_esc_cover.cfg" + } + ] +} diff --git a/synth/prim/dv/prim_esc/tb/prim_esc_tb.sv b/synth/prim/dv/prim_esc/tb/prim_esc_tb.sv new file mode 100755 index 0000000..592abef --- /dev/null +++ b/synth/prim/dv/prim_esc/tb/prim_esc_tb.sv @@ -0,0 +1,248 @@ +// 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_esc_sender and prim_esc_receiver_pair. +// +// This test has five sequnces: +// 1). Random reset during escalation handshake sequence. +// 2). Escalation request sequence. +// 3). Ping request interrupted by escalation request sequence. +// 4). Integrity error sequence. +// 5). Escalation reverse ping timeout sequence. +// 6). Escalation receiver counter fail sequence. +// 7). Random esc_rx/tx signal without request error sequence. + +module prim_esc_tb; + + import dv_utils_pkg::*; + `include "dv_macros.svh" + + ////////////////////////////////////////////////////// + // config + ////////////////////////////////////////////////////// + + localparam time ClkPeriod = 10000; + localparam int PING_CNT_DW = 1; + localparam int TIMEOUT_CYCLES = 1 << (PING_CNT_DW + 6); + localparam string MSG_ID = $sformatf("%m"); + + uint default_spinwait_timeout_ns = 100_000; + + ////////////////////////////////////////////////////// + // Clock and Reset + ////////////////////////////////////////////////////// + + wire clk, rst_n; + + clk_rst_if main_clk ( + .clk, + .rst_n + ); + + ////////////////////////////////////////////////////// + // DUTs + ////////////////////////////////////////////////////// + + logic ping_req, ping_ok, integ_fail, esc_req, esc_req_out; + prim_esc_pkg::esc_tx_t esc_tx; + prim_esc_pkg::esc_rx_t esc_rx; + + prim_esc_sender i_esc_sender ( + .clk_i(clk), + .rst_ni(rst_n), + .ping_req_i(ping_req), + .ping_ok_o(ping_ok), + .integ_fail_o(integ_fail), + .esc_req_i(esc_req), + .esc_rx_i(esc_rx), + .esc_tx_o(esc_tx) + ); + + prim_esc_receiver #( + .N_ESC_SEV(4), + // Set to 1 to avoid long wait period to check ping request reverse timeout. + .PING_CNT_DW(PING_CNT_DW) + ) i_esc_receiver ( + .clk_i(clk), + .rst_ni(rst_n), + .esc_req_o(esc_req_out), + .esc_rx_o(esc_rx), + .esc_tx_i(esc_tx) + ); + + ////////////////////////////////////////////////////// + // Helper Functions/Tasks and Variables + ////////////////////////////////////////////////////// + + logic error = 0; + + function automatic void test_error(string msg); + $error($sformatf("%time: %0s", $realtime, msg)); + error = 1; + endfunction + + ////////////////////////////////////////////////////// + // Stimuli Application / Response Checking + ////////////////////////////////////////////////////// + + initial begin: p_stimuli + time delay_ps; + + ping_req = 0; + esc_req = 0; + main_clk.set_period_ps(ClkPeriod); + main_clk.set_active(); + main_clk.apply_reset(); + + // Sequence 1. Random reset during escalation handshake sequence. + ping_req = 1; + // Issue async reset between first and fifth clock cycle to reach FSM coverages. + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(delay_ps, delay_ps inside {[1:ClkPeriod]};, , MSG_ID) + #((delay_ps + $urandom_range(1, 5) * ClkPeriod) * 1ps); + main_clk.apply_reset(); + ping_req = 0; + + $display("[prim_esc_seq] Random reset during escalation handshake sequence finished!"); + + // Sequence 2. Escalation request sequence. + esc_req = 1; + // Drive random length of esc_req and check `esc_req_out` and `integ_fail` outputs. + main_clk.wait_clks($urandom_range(1, 20)); + if (integ_fail) test_error("Esc_req unexpected signal integrity error!"); + if (!esc_req_out) test_error("Esc_req did not set esc_req!"); + esc_req = 0; + + $display("[prim_esc_seq] Escalation request sequence finished!"); + + // Sequence 3. Ping request interrupted by escalation request. + main_clk.wait_clks($urandom_range(2, 20)); + #1ns; + ping_req = 1; + `DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout"); + if (integ_fail) test_error("Expect no errors when trigger ping_req"); + main_clk.wait_clks($urandom_range(2, 20)); + ping_req = 0; + + main_clk.wait_clks($urandom_range(2, 20)); + ping_req = 1; + // Wait a max of 5 clock cycle to ensure esc_req is send during ping handshake. + main_clk.wait_clks($urandom_range(0, 5)); + esc_req = 1; + `DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout"); + `DV_SPINWAIT(wait (esc_req_out == 1);, , , "Wait for esc_req_out timeout"); + main_clk.wait_clks(1); + esc_req = 0; + ping_req = 0; + if (integ_fail) test_error("Expect no errors when esc_req interrupts ping_req"); + + $display("[prim_esc_seq] Ping request interrupted by escalation request sequence finished!"); + + // Sequence 4.1 Integrity error sequence during escalation request. + main_clk.wait_clks($urandom_range(1, 20)); + esc_req = 1; + // Randomly wait a few clock cycles then inject integrity error. + main_clk.wait_clks($urandom_range(0, 5)); + // Force esc_tx signal to create a integrity fail error case. + force esc_tx.esc_n = 1; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_tx.esc_n integ_fail timeout"); + main_clk.wait_clks(1); + release esc_tx.esc_n; + // Wait #1ps to avoid a race condition on clock edge. + #1ps; + if (!esc_req_out) test_error("Signal integrity error should set esc_req!"); + esc_req = 0; + $display("[prim_esc_seq] Integrity esc_tx error sequence finished [1/2]!"); + + main_clk.wait_clks($urandom_range(1, 20)); + // Force esc_tx signal to create a integrity fail error case. + force esc_rx.resp_n = 1; + esc_req = 1; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_n integ_fail timeout"); + main_clk.wait_clks(1); + release esc_rx.resp_n; + // Wait #1ps to avoid a race condition on clock edge. + #1ps; + if (!esc_req_out) test_error("Signal integrity error should set esc_req!"); + esc_req = 0; + $display("[prim_esc_seq] Integrity esc_rx error sequence finished [2/2]!"); + + // Sequence 4.1 `Esc_tx` integrity error sequence during ping request. + main_clk.wait_clks($urandom_range(1, 20)); + ping_req = 1; + // Force esc_tx signal to create a integrity fail error case. + force esc_tx.esc_n = 1; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for ping request integ_fail timeout"); + release esc_tx.esc_n; + // Wait #1ps to avoid a race condition on clock edge. + #1ps; + if (!esc_req_out) test_error("Signal integrity error should set esc_req!"); + if (ping_ok) test_error("Ping error!"); + ping_req = 0; + + $display("[prim_esc_seq] Escalation esc_p/n integrity sequence during ping request finished!"); + + // Sequence 5. Escalation reverse ping timeout sequence. + // Wait at least two clock cycles for the previous sequence to finish its escalation request. + main_clk.wait_clks($urandom_range(2, 20)); + ping_req = 1; + fork + begin + // After one ping_req, esc_receiver will start a counter to expect next ping_req. If the + // counter reaches its max value but no ping_req has been received, design will set + // `esc_req_out` signal. + main_clk.wait_clks(TIMEOUT_CYCLES + 1); + if (!esc_req_out) test_error("Design failed to detect ping request timeout!"); + end + begin + // Wait for a ping handshake to complete. + `DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout"); + main_clk.wait_clks(2); + ping_req = 0; + if (integ_fail) test_error("Ping_req unexpected signal integrity error!"); + if (esc_req_out) test_error("Ping request should not set esc_req_out!"); + end + join + main_clk.apply_reset(); + + $display("[prim_esc_seq] Escalation ping request timeout sequence finished!"); + + // Sequence 6. Escalation receiver counter fail sequence. + ping_req = 1; + // Wait until ping request is acknowledged and counter starts to increment. + `DV_SPINWAIT(wait (ping_ok == 1);, , , "Wait for ping_ok timeout"); + main_clk.wait_clks(2); + ping_req = 0; + // If duplicate counter values are inconsistent, the design will set the `esc_req_out` signal. + force prim_esc_tb.i_esc_receiver.u_prim_count.cnt_q[1] = '0; + `DV_SPINWAIT(wait (esc_req_out == 1);, , , "Wait for esc_req_out timeout"); + if (integ_fail) test_error("Escalation receiver counter unexpected signal integrity error!"); + release prim_esc_tb.i_esc_receiver.u_prim_count.cnt_q[1]; + + $display("[prim_esc_seq] Escalation couter error sequence finished!"); + + // 7. Random esc_rx/tx signal without request error sequence. + main_clk.wait_clks($urandom_range(1, 20)); + // Force esc_tx signals to create a integrity fail error case. + force esc_rx.resp_p = 1; + force esc_rx.resp_n = 0; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_p/n integ_fail timeout"); + release esc_rx.resp_p; + release esc_rx.resp_n; + $display("[prim_esc_seq] Random esc_rx error sequence finished [1/2]!"); + + main_clk.wait_clks($urandom_range(1, 20)); + // Force esc_tx signals to create a integrity fail error case. + force esc_tx.esc_p = 1; + force esc_tx.esc_n = 0; + `DV_SPINWAIT(wait (integ_fail == 1);, , , "Wait for esc_rx.resp_p/n integ_fail timeout"); + release esc_tx.esc_p; + release esc_tx.esc_n; + $display("[prim_esc_seq] Random esc_tx error sequence finished [1/2]!"); + main_clk.wait_clks(2); + + dv_test_status_pkg::dv_test_status(.passed(!error)); + $finish(); + end + +endmodule diff --git a/synth/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el b/synth/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el new file mode 100755 index 0000000..45f15b4 --- /dev/null +++ b/synth/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el @@ -0,0 +1,3 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 diff --git a/synth/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg b/synth/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg new file mode 100755 index 0000000..533abbc --- /dev/null +++ b/synth/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg @@ -0,0 +1,16 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// We only collect coverage for the LFSRs with width 8 and 24 as +// 8 is the largest used in the smoke test and 24 is the largest +// used in the nightly DV regression. +// Note that FPV fully covers a large range of additional width +// configuration, so this is permissible. + ++tree prim_lfsr_tb.gen_duts[8].i_prim_lfsr ++tree prim_lfsr_tb.gen_duts[24].i_prim_lfsr +begin tgl(portsonly) + +tree prim_lfsr_tb.gen_duts[8].i_prim_lfsr + +tree prim_lfsr_tb.gen_duts[24].i_prim_lfsr +end diff --git a/synth/prim/dv/prim_lfsr/prim_lfsr_sim.core b/synth/prim/dv/prim_lfsr/prim_lfsr_sim.core new file mode 100755 index 0000000..ec82c16 --- /dev/null +++ b/synth/prim/dv/prim_lfsr/prim_lfsr_sim.core @@ -0,0 +1,31 @@ +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:lfsr:0.1 + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_test_status + - lowrisc:dv:common_ifs + files: + - prim_lfsr_tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: prim_lfsr_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/synth/prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson b/synth/prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson new file mode 100755 index 0000000..4fb68ce --- /dev/null +++ b/synth/prim/dv/prim_lfsr/prim_lfsr_sim_cfg.hjson @@ -0,0 +1,114 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_lfsr + + // Top level dut name (sv module). + dut: prim_lfsr + + // Top level testbench name (sv module). + tb: prim_lfsr_tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_lfsr_sim:0.1 + + // Import additional common sim cfg files. + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + + // Default iterations for all tests - each test entry can override this. + reseed: 50 + + // Enable cdc instrumentation + run_opts: ["+cdc_instrumentation_enabled=1"] + + // Add PRIM_LSFR specific exclusion files. + vcs_cov_excl_files: ["{proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cov_excl.el"] + + build_modes: [ + { + name: prim_lfsr_dw_8_gal + build_opts: ["+define+MAX_LFSR_DW=8"] + } + { + name: prim_lfsr_dw_8_fib + build_opts: ["+define+MAX_LFSR_DW=8", + "+define+LFSR_FIB_TYPE"] + } + { + name: prim_lfsr_dw_24_gal + build_opts: ["+define+MAX_LFSR_DW=24"] + } + { + name: prim_lfsr_dw_24_fib + build_opts: ["+define+MAX_LFSR_DW=24", + "+define+LFSR_FIB_TYPE"] + } + ] + + // Since none of the tests use the "default" build mode, we need to indicate which build is the + // main build mode. + primary_build_mode: prim_lfsr_dw_8_gal + + // Always randomize the initial seed of the LFSR. + // + // For this block level, the DefaultSeed is ignored and a random value is picked instead. This is + // facilitated by the plusarg below. At the chip level, this plusarg is not set, so prim_lfsr + // randomly picks between the DefaultSeed value and a random value. + run_opts: ["+prim_lfsr_use_default_seed=0"] + + // dw_8 is only used for "smoke" sims, so coverage collection is not needed. + prim_lfsr_dw_8_gal_vcs_cov_cfg_file: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg" + prim_lfsr_dw_8_fib_vcs_cov_cfg_file: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg" + prim_lfsr_dw_24_gal_vcs_cov_cfg_file: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg" + prim_lfsr_dw_24_fib_vcs_cov_cfg_file: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_lfsr/data/prim_lfsr_cover.cfg" + + prim_lfsr_dw_8_gal_xcelium_cov_cfg_file: "" + prim_lfsr_dw_8_fib_xcelium_cov_cfg_file: "" + prim_lfsr_dw_24_gal_xcelium_cov_cfg_file: "" + prim_lfsr_dw_24_fib_xcelium_cov_cfg_file: "" + + // List of test specifications. + tests: [ + { + name: prim_lfsr_gal_smoke + // limit max lfsr length to reduce private CI runtime. + build_mode: prim_lfsr_dw_8_gal + } + { + name: prim_lfsr_fib_smoke + // limit max lfsr length to reduce private CI runtime. + build_mode: prim_lfsr_dw_8_fib + } + { + name: prim_lfsr_gal_test + build_mode: prim_lfsr_dw_24_gal + run_timeout_mins: 120 + } + { + name: prim_lfsr_fib_test + build_mode: prim_lfsr_dw_24_fib + run_timeout_mins: 120 + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["prim_lfsr_gal_smoke", + "prim_lfsr_fib_smoke"] + } + { + name: nightly + tests:["prim_lfsr_gal_smoke", + "prim_lfsr_fib_smoke", + "prim_lfsr_gal_test", + "prim_lfsr_fib_test"] + } + ] +} diff --git a/synth/prim/dv/prim_lfsr/prim_lfsr_tb.sv b/synth/prim/dv/prim_lfsr/prim_lfsr_tb.sv new file mode 100755 index 0000000..9855615 --- /dev/null +++ b/synth/prim/dv/prim_lfsr/prim_lfsr_tb.sv @@ -0,0 +1,313 @@ +// 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; + + import dv_utils_pkg::*; + `include "dv_macros.svh" + + ////////////////////////////////////////////////////// + // Build configurations: + // LFSR_FIB_TYPE; If defined, "FIB_XNOR" is used. + // If not defined, this defaults to "GAL_XOR". + // MIN_LFSR_DW: Minimum LFSR width tested. + // MAX_LFSR_DW: Maximum LFSR width tested. + ////////////////////////////////////////////////////// +`ifdef LFSR_FIB_TYPE + localparam string LfsrType = "FIB_XNOR"; +`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 + + // The default seed of the LFSR. + // + // This is fixed to 1. It is unused in simulations. The `prim_lfsr` instead, randomizes the + // default seed value (DefaultSeedLocal) at runtime. This is enforced with + // +prim_lfsr_use_default_seed=0 plusarg. + localparam logic SEED = 1'b1; + + // The StatePerm below is only defined for LFSRs up to 256bit wide. + `ASSERT_INIT(MaxStateSizeCheck_A, MaxLfsrDw < 256) + + logic [MaxLfsrDw:MinLfsrDw] err, test_done; + logic lfsr_en [MaxLfsrDw+1]; + logic seed_en [MaxLfsrDw+1]; + logic [MaxLfsrDw-1:0] state_out [MaxLfsrDw+1]; + logic [MaxLfsrDw-1:0] lfsr_periods [MaxLfsrDw+1]; + logic [MaxLfsrDw-1:0] entropy [MaxLfsrDw+1]; + logic [MaxLfsrDw-1:0] seed [MaxLfsrDw+1]; + logic [MaxLfsrDw-1:0] rand_entropy; + logic [MaxLfsrDw-1:0] rand_seed; + + + for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts + // This is used to specify an identity permutation via the custom state output + // permutation parameter for all LFSRs up to 256bit wide. + localparam int Dw = $clog2(k); + localparam logic [255:0][Dw-1:0] StatePerm = '{ + Dw'(32'd255), Dw'(32'd254), Dw'(32'd253), Dw'(32'd252), + Dw'(32'd251), Dw'(32'd250), Dw'(32'd249), Dw'(32'd248), + Dw'(32'd247), Dw'(32'd246), Dw'(32'd245), Dw'(32'd244), + Dw'(32'd243), Dw'(32'd242), Dw'(32'd241), Dw'(32'd240), + Dw'(32'd239), Dw'(32'd238), Dw'(32'd237), Dw'(32'd236), + Dw'(32'd235), Dw'(32'd234), Dw'(32'd233), Dw'(32'd232), + Dw'(32'd231), Dw'(32'd230), Dw'(32'd229), Dw'(32'd228), + Dw'(32'd227), Dw'(32'd226), Dw'(32'd225), Dw'(32'd224), + Dw'(32'd223), Dw'(32'd222), Dw'(32'd221), Dw'(32'd220), + Dw'(32'd219), Dw'(32'd218), Dw'(32'd217), Dw'(32'd216), + Dw'(32'd215), Dw'(32'd214), Dw'(32'd213), Dw'(32'd212), + Dw'(32'd211), Dw'(32'd210), Dw'(32'd209), Dw'(32'd208), + Dw'(32'd207), Dw'(32'd206), Dw'(32'd205), Dw'(32'd204), + Dw'(32'd203), Dw'(32'd202), Dw'(32'd201), Dw'(32'd200), + Dw'(32'd199), Dw'(32'd198), Dw'(32'd197), Dw'(32'd196), + Dw'(32'd195), Dw'(32'd194), Dw'(32'd193), Dw'(32'd192), + Dw'(32'd191), Dw'(32'd190), Dw'(32'd189), Dw'(32'd188), + Dw'(32'd187), Dw'(32'd186), Dw'(32'd185), Dw'(32'd184), + Dw'(32'd183), Dw'(32'd182), Dw'(32'd181), Dw'(32'd180), + Dw'(32'd179), Dw'(32'd178), Dw'(32'd177), Dw'(32'd176), + Dw'(32'd175), Dw'(32'd174), Dw'(32'd173), Dw'(32'd172), + Dw'(32'd171), Dw'(32'd170), Dw'(32'd169), Dw'(32'd168), + Dw'(32'd167), Dw'(32'd166), Dw'(32'd165), Dw'(32'd164), + Dw'(32'd163), Dw'(32'd162), Dw'(32'd161), Dw'(32'd160), + Dw'(32'd159), Dw'(32'd158), Dw'(32'd157), Dw'(32'd156), + Dw'(32'd155), Dw'(32'd154), Dw'(32'd153), Dw'(32'd152), + Dw'(32'd151), Dw'(32'd150), Dw'(32'd149), Dw'(32'd148), + Dw'(32'd147), Dw'(32'd146), Dw'(32'd145), Dw'(32'd144), + Dw'(32'd143), Dw'(32'd142), Dw'(32'd141), Dw'(32'd140), + Dw'(32'd139), Dw'(32'd138), Dw'(32'd137), Dw'(32'd136), + Dw'(32'd135), Dw'(32'd134), Dw'(32'd133), Dw'(32'd132), + Dw'(32'd131), Dw'(32'd130), Dw'(32'd129), Dw'(32'd128), + Dw'(32'd127), Dw'(32'd126), Dw'(32'd125), Dw'(32'd124), + Dw'(32'd123), Dw'(32'd122), Dw'(32'd121), Dw'(32'd120), + Dw'(32'd119), Dw'(32'd118), Dw'(32'd117), Dw'(32'd116), + Dw'(32'd115), Dw'(32'd114), Dw'(32'd113), Dw'(32'd112), + Dw'(32'd111), Dw'(32'd110), Dw'(32'd109), Dw'(32'd108), + Dw'(32'd107), Dw'(32'd106), Dw'(32'd105), Dw'(32'd104), + Dw'(32'd103), Dw'(32'd102), Dw'(32'd101), Dw'(32'd100), + Dw'(32'd099), Dw'(32'd098), Dw'(32'd097), Dw'(32'd096), + Dw'(32'd095), Dw'(32'd094), Dw'(32'd093), Dw'(32'd092), + Dw'(32'd091), Dw'(32'd090), Dw'(32'd089), Dw'(32'd088), + Dw'(32'd087), Dw'(32'd086), Dw'(32'd085), Dw'(32'd084), + Dw'(32'd083), Dw'(32'd082), Dw'(32'd081), Dw'(32'd080), + Dw'(32'd079), Dw'(32'd078), Dw'(32'd077), Dw'(32'd076), + Dw'(32'd075), Dw'(32'd074), Dw'(32'd073), Dw'(32'd072), + Dw'(32'd071), Dw'(32'd070), Dw'(32'd069), Dw'(32'd068), + Dw'(32'd067), Dw'(32'd066), Dw'(32'd065), Dw'(32'd064), + Dw'(32'd063), Dw'(32'd062), Dw'(32'd061), Dw'(32'd060), + Dw'(32'd059), Dw'(32'd058), Dw'(32'd057), Dw'(32'd056), + Dw'(32'd055), Dw'(32'd054), Dw'(32'd053), Dw'(32'd052), + Dw'(32'd051), Dw'(32'd050), Dw'(32'd049), Dw'(32'd048), + Dw'(32'd047), Dw'(32'd046), Dw'(32'd045), Dw'(32'd044), + Dw'(32'd043), Dw'(32'd042), Dw'(32'd041), Dw'(32'd040), + Dw'(32'd039), Dw'(32'd038), Dw'(32'd037), Dw'(32'd036), + Dw'(32'd035), Dw'(32'd034), Dw'(32'd033), Dw'(32'd032), + Dw'(32'd031), Dw'(32'd030), Dw'(32'd029), Dw'(32'd028), + Dw'(32'd027), Dw'(32'd026), Dw'(32'd025), Dw'(32'd024), + Dw'(32'd023), Dw'(32'd022), Dw'(32'd021), Dw'(32'd020), + Dw'(32'd019), Dw'(32'd018), Dw'(32'd017), Dw'(32'd016), + Dw'(32'd015), Dw'(32'd014), Dw'(32'd013), Dw'(32'd012), + Dw'(32'd011), Dw'(32'd010), Dw'(32'd009), Dw'(32'd008), + Dw'(32'd007), Dw'(32'd006), Dw'(32'd005), Dw'(32'd004), + Dw'(32'd003), Dw'(32'd002), Dw'(32'd001), Dw'(32'd000) + }; + + ////////////////////////////////////////////////////// + // clock & reset + ////////////////////////////////////////////////////// + wire clk, rst_n; + clk_rst_if main_clk(.clk, .rst_n); + + ////////////////////////////////////////////////////// + // DUTs + ////////////////////////////////////////////////////// + prim_lfsr #( + .LfsrType ( LfsrType ), + .LfsrDw ( k ), + .EntropyDw ( k ), + .StateOutDw ( k ), + .DefaultSeed ( k'(SEED) ), + // The case where this is disabled is already tested with FPV. + // Hence we cover the enabled case with custom permutations + // in this testbench. + .StatePermEn ( 1'b1 ), + .StatePerm ( StatePerm[MaxLfsrDw-1:0] ), + // Enable internal max length check. + .MaxLenSVA ( 1'b1 ) + ) i_prim_lfsr ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .seed_en_i ( seed_en[k] ), + .seed_i ( seed[k][k-1:0] ), + .lfsr_en_i ( lfsr_en[k] ), + .entropy_i ( entropy[k][k-1:0] ), + .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}); + + ////////////////////////////////////////////////////// + // stimuli application / response checking + ////////////////////////////////////////////////////// + initial begin : p_stimuli + bit [k-1:0] actual_default_seed; + + lfsr_en[k] = 0; + seed_en[k] = 0; + seed[k] = 0; + entropy[k] = 0; + err[k] = 0; + test_done[k] = 0; + + main_clk.set_sole_clock(1); + main_clk.set_active(); + main_clk.apply_reset(); + main_clk.wait_clks($urandom_range(2, 20)); + + //////////////////////////////// + // Smoke Check + //////////////////////////////// + + // For simulations, we modify prim_lfsr to pick a random default seed for every + // invocation, instead of going with the DefaultSeed parameter. + actual_default_seed = MaxLfsrDw'(i_prim_lfsr.DefaultSeedLocal); + + // enable this LFSR + lfsr_en[k] = 1; + + $display("Starting LFSR maxlen test for width %0d: running %0d cycles", k, 2 ** k - 1); + for (longint unsigned i = 0; i <= lfsr_periods[MaxLfsrDw] && lfsr_en[k]; i++) begin + main_clk.wait_clks(1); + // Check if we reached the initial state again. + if (state_out[k] == actual_default_seed && lfsr_en[k]) begin + lfsr_en[k] = 1'b0; + // We expect this to occur only after the maximum length period. + if (i == lfsr_periods[k]) begin + $display("LFSR maxlen test for width %0d passed!", k); + end else begin + $error("LFSR maxlen test for width %0d failed at period %0d!", k, i); + err[k] = 1'b1; + end + end + end + + main_clk.wait_clks(10); + if (lfsr_en[k]) begin + $error("LFSR with width %0d never got back to the initial state!", k); + err[k] = 1'b1; + end + + //////////////////////////////// + // Random Vectors + //////////////////////////////// + + // Load an invalid seed externally to trigger the lockup condition. + lfsr_en[k] = 1; + + // Add some additional toggles for coverage - checking is done within the module using SVAs. + repeat ($urandom_range(5000, 10000)) begin + // Do random reset sometimes + if ($urandom_range(0, 10) == 0) main_clk.apply_reset(); + randomize(rand_seed); + randomize(rand_entropy); + seed[k] = rand_seed; + entropy[k] = rand_entropy; + lfsr_en[k] = $urandom_range(0, 1); + seed_en[k] = $urandom_range(0, 1); + main_clk.wait_clks(1); + end + + //////////////////////////////// + // Lockup Check + //////////////////////////////// + + lfsr_en[k] = 0; + seed_en[k] = 0; + seed[k] = 0; + entropy[k] = 0; + + // Wait a few cycles + main_clk.wait_clks(10); + + // Load an invalid seed externally to trigger the lockup condition. + lfsr_en[k] = 1; + seed_en[k] = 1; + + if (LfsrType == "GAL_XOR") begin + seed[k] = {k{1'b0}}; + end else begin // "FIB_XNOR" + seed[k] = {k{1'b1}}; + end + + main_clk.wait_clks(1); + + // cover the case where the LFSR is disabled for a cycle. + lfsr_en[k] = 0; + seed_en[k] = 0; + seed[k] = 0; + + if (gen_duts[k].i_prim_lfsr.lockup) begin + $display("LFSR with width %0d detected lockup condition!", k); + end else begin + $error("LFSR with width %0d does not detect lockup condition", k); + err[k] = 1'b1; + end + + main_clk.wait_clks(1); + lfsr_en[k] = 1; + main_clk.wait_clks(1); + + // we expect that the LFSR returns to its initial state + if (gen_duts[k].i_prim_lfsr.lfsr_q == actual_default_seed) begin + $display("LFSR lockup test for width %0d passed!", k); + end else begin + $error("LFSR with width %0d does not reset to DefaultSeedLocal when lockup is detected", k); + err[k] = 1'b1; + end + + lfsr_en[k] = 0; + main_clk.wait_clks(10); + + main_clk.stop_clk(); + test_done[k] = 1; + end + end + + initial begin + $display("Testing LFSR of type %0s for widths {[%0d:%0d]}", LfsrType, MinLfsrDw, MaxLfsrDw); + `DV_WAIT(test_done === '1, , 1_000_000_000 /*1ms*/, "prim_lfsr_tb") + dv_test_status_pkg::dv_test_status(.passed(err === '0 && test_done === '1)); + $finish(); + end + + // TODO: perhaps wrap this in a macro? + initial begin + bit poll_for_stop; + int unsigned poll_for_stop_interval_ns; + + poll_for_stop = 1'b1; + void'($value$plusargs("poll_for_stop=%0b", poll_for_stop)); + poll_for_stop_interval_ns = 1000; + void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns)); + if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns)); + end + +endmodule : prim_lfsr_tb diff --git a/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.cc b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.cc new file mode 100755 index 0000000..782d0e1 --- /dev/null +++ b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.cc @@ -0,0 +1,257 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Simple unhardened reference implementation of the PRESENT cipher, following +// the description in +// +// [1] Bognadov et al, PRESENT: An Ultra-Lightweight Block Cipher. LNCS 4727: +// 450–466. doi:10.1007/978-3-540-74735-2_31. + +#include +#include +#include +#include + +static const uint8_t sbox4[16] = {0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd, + 0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2}; + +static const uint8_t sbox4_inv[16] = {0x5, 0xe, 0xf, 0x8, 0xc, 0x1, 0x2, 0xd, + 0xb, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xa}; + +static const uint8_t bit_perm[64] = { + 0, 16, 32, 48, 1, 17, 33, 49, 2, 18, 34, 50, 3, 19, 35, 51, + 4, 20, 36, 52, 5, 21, 37, 53, 6, 22, 38, 54, 7, 23, 39, 55, + 8, 24, 40, 56, 9, 25, 41, 57, 10, 26, 42, 58, 11, 27, 43, 59, + 12, 28, 44, 60, 13, 29, 45, 61, 14, 30, 46, 62, 15, 31, 47, 63}; + +static const uint8_t bit_perm_inv[64] = { + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, + 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, + 2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, + 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63}; + +static uint64_t mask64(int bits) { return ((uint64_t)1 << bits) - 1; } + +namespace { +struct key128_t { + uint64_t hi, lo; +}; + +class PresentState { + public: + PresentState(unsigned key_size, key128_t key); + + // This is the body of the for loop in Fig. 1 of the paper ([1], above). If + // is_last_round, then it also includes the call to addRoundKey that follows. + uint64_t enc_round(uint64_t input, unsigned round, bool is_last_round) const; + + // The inverse of enc_round. Note that a decryption should start with a high + // round and with is_last_round set, then count down. + uint64_t dec_round(uint64_t input, unsigned round, bool is_last_round) const; + + private: + static key128_t next_round_key(const key128_t &k, unsigned key_size, + unsigned round_count); + + static uint64_t add_round_key(uint64_t data, const key128_t &k, + unsigned key_size); + static uint64_t sbox_layer(bool inverse, uint64_t data); + static uint64_t perm_layer(bool inverse, uint64_t data); + + unsigned key_size; + std::vector key_schedule; +}; +} // namespace + +PresentState::PresentState(unsigned key_size, key128_t key) + : key_size(key_size) { + assert(key_size == 80 || key_size == 128); + key_schedule.reserve(32); + key_schedule.push_back(key); + for (int i = 1; i <= 31; ++i) { + key = next_round_key(key, key_size, i); + key_schedule.push_back(key); + } +} + +uint64_t PresentState::enc_round(uint64_t input, unsigned round, + bool is_last_round) const { + assert(1 <= round && round < key_schedule.size()); + const key128_t &key = key_schedule[round - 1]; + + // addRoundKey + uint64_t w1 = add_round_key(input, key, key_size); + + // sBoxLayer + uint64_t w2 = sbox_layer(false, w1); + + // pLayer + uint64_t w3 = perm_layer(false, w2); + + // On the final round, call addRoundKey with the following key. + uint64_t w4 = + is_last_round ? add_round_key(w3, key_schedule[round], key_size) : w3; + + return w4; +} + +uint64_t PresentState::dec_round(uint64_t input, unsigned round, + bool is_last_round) const { + assert(1 <= round && round < key_schedule.size()); + const key128_t &key = key_schedule[round - 1]; + + // If we're undoing the last round, start by calling addRoundKey with the + // following key. + uint64_t w1 = is_last_round + ? add_round_key(input, key_schedule[round], key_size) + : input; + + // pLayer^{-1} + uint64_t w2 = perm_layer(true, w1); + + // sBoxLayer^{-1} + uint64_t w3 = sbox_layer(true, w2); + + // addRoundKey + uint64_t w4 = add_round_key(w3, key, key_size); + + return w4; +} + +key128_t PresentState::next_round_key(const key128_t &k, unsigned key_size, + unsigned round_count) { + assert((round_count >> 5) == 0); + assert(key_size == 80 || key_size == 128); + + if (key_size == 80) { + assert((k.hi >> 16) == 0); + + // Rotate the key left by 61 bits + // + // The top word (bits 79:64) will come from bits (18:3). The + // bottom word (bits 63:0) will have bits 2:0 at the top, then + // bits 79:64 (from the top word) and finally bits 63:19. + uint64_t rot_hi = (k.lo >> 3) & mask64(16); + uint64_t rot_lo = + ((((k.lo >> 0) & mask64(3)) << 61) | (((k.hi >> 0)) << 45) | + (((k.lo >> 19) & mask64(45)) << 0)); + assert((rot_hi >> 16) == 0); + + // Pass the top 4 bits through sbox4 + uint64_t subst_hi = + (((uint64_t)sbox4[rot_hi >> 12] << 12) | (rot_hi & mask64(12))); + uint64_t subst_lo = rot_lo; + + // XOR bits 19:15 with the round counter + uint64_t xored_hi = subst_hi; + uint64_t xored_lo = + ((subst_lo & ~mask64(20)) | + ((((subst_lo >> 15) & mask64(5)) ^ round_count) << 15) | + (subst_lo >> 0 & mask64(15))); + + key128_t next = {.hi = xored_hi, .lo = xored_lo}; + return next; + } else { + // Rotate the key left by 61 bits + // + // The top word (bits 127:64) will come from bits 66:64 (from the + // top word) and then bits 63:3 (from the bottom word). The bottom + // word (bits 63:0) will have bits 2:0 at the top and then bits + // 127:67. + uint64_t rot_hi = (((k.hi & mask64(3)) << 61) | (k.lo >> 3)); + uint64_t rot_lo = (((k.lo & mask64(3)) << 61) | (k.hi >> 3)); + + // Pass top 8 bits through a pair of sbox4's + uint64_t rot_nib124 = (rot_hi >> 60) & mask64(4); + uint64_t rot_nib120 = (rot_hi >> 56) & mask64(4); + + uint64_t subst_hi = + (((uint64_t)sbox4[rot_nib124] << 60) | + ((uint64_t)sbox4[rot_nib120] << 56) | (rot_hi & mask64(56))); + uint64_t subst_lo = rot_lo; + + // XOR bits 66:62 + uint64_t xored_hi = subst_hi ^ ((uint64_t)round_count >> 2); + uint64_t xored_lo = subst_lo ^ ((uint64_t)round_count << 62); + + key128_t next = {.hi = xored_hi, .lo = xored_lo}; + return next; + } +} + +uint64_t PresentState::add_round_key(uint64_t data, const key128_t &k, + unsigned key_size) { + assert(key_size == 80 || key_size == 128); + uint64_t k64 = (key_size == 80) ? ((k.hi << 48) | (k.lo >> 16)) : k.hi; + return data ^ k64; +} + +uint64_t PresentState::sbox_layer(bool inverse, uint64_t data) { + uint64_t ret = 0; + for (int i = 0; i < 64 / 4; ++i) { + unsigned nibble = (data >> (4 * i)) & 0xf; + uint64_t subst = inverse ? sbox4_inv[nibble] : sbox4[nibble]; + ret |= subst << (4 * i); + } + return ret; +} + +uint64_t PresentState::perm_layer(bool inverse, uint64_t data) { + uint64_t ret = 0; + for (int i = 0; i < 64; ++i) { + uint64_t bit = (data >> i) & 1; + ret |= bit << (inverse ? bit_perm_inv[i] : bit_perm[i]); + } + return ret; +} + +extern "C" { + +PresentState *c_dpi_present_mk(unsigned key_size, const svBitVecVal *key) { + assert(key_size == 80 || key_size == 128); + + // Each element of key represents 32 bits. Unpack into a key128_t, zeroing + // the top bits if key size was 80. + uint32_t w32s[4]; + for (int i = 0; i < 4; ++i) { + unsigned lsb = 32 * i; + unsigned bits_left = (lsb < key_size) ? key_size - lsb : 0; + unsigned mask = + (bits_left < 32) ? ((uint32_t)1 << bits_left) - 1 : ~(uint32_t)0; + w32s[i] = key[i] & mask; + } + key128_t k128 = {.hi = ((uint64_t)w32s[3] << 32) | w32s[2], + .lo = ((uint64_t)w32s[1] << 32) | w32s[0]}; + + return new PresentState(key_size, k128); +} + +void c_dpi_present_free(PresentState *ps) { delete ps; } + +void c_dpi_present_enc_round(const PresentState *ps, unsigned round, + unsigned char is_last_round, + const svBitVecVal *src, svBitVecVal *dst) { + assert(ps); + assert(is_last_round == 0 || is_last_round == 1); + + uint64_t in64 = ((uint64_t)src[1] << 32) | src[0]; + uint64_t out64 = ps->enc_round(in64, round, is_last_round != 0); + + dst[1] = out64 >> 32; + dst[0] = (uint32_t)out64; +} + +void c_dpi_present_dec_round(const PresentState *ps, unsigned round, + unsigned char is_last_round, + const svBitVecVal *src, svBitVecVal *dst) { + assert(ps); + assert(is_last_round == 0 || is_last_round == 1); + + uint64_t in64 = ((uint64_t)src[1] << 32) | src[0]; + uint64_t out64 = ps->dec_round(in64, round, is_last_round != 0); + + dst[1] = out64 >> 32; + dst[0] = (uint32_t)out64; +} +} diff --git a/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.core b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.core new file mode 100755 index 0000000..ed00855 --- /dev/null +++ b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present.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:dv:crypto_dpi_present:0.1" +description: "PRESENT block cipher reference implementation in C from Ruhr-University Bochum" +filesets: + files_dv: + files: + - crypto_dpi_present.cc: {file_type: cppSource} + - crypto_dpi_present_pkg.sv: {file_type: systemVerilogSource} + file_type: cSource + +targets: + default: + filesets: + - files_dv diff --git a/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present_pkg.sv b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present_pkg.sv new file mode 100755 index 0000000..2565512 --- /dev/null +++ b/synth/prim/dv/prim_present/crypto_dpi_present/crypto_dpi_present_pkg.sv @@ -0,0 +1,79 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package crypto_dpi_present_pkg; + // PRESENT expects up to 31 rounds in total + localparam int unsigned MaxRounds = 31; + localparam int unsigned MaxKeyWidth = 128; + localparam int unsigned DataWidth = 64; + + // DPI-C imports + import "DPI-C" function chandle c_dpi_present_mk(int unsigned key_size, + bit [MaxKeyWidth-1:0] key); + import "DPI-C" function void c_dpi_present_free(chandle h); + + import "DPI-C" function void c_dpi_present_enc_round(chandle h, + int unsigned round, + bit is_last_round, + bit [DataWidth-1:0] in, + output bit [DataWidth-1:0] out); + import "DPI-C" function void c_dpi_present_dec_round(chandle h, + int unsigned round, + bit is_last_round, + bit [DataWidth-1:0] in, + output bit [DataWidth-1:0] out); + + // This function encrypts the input plaintext with the PRESENT encryption algorithm. + // + // This produces a list of all intermediate values produced after each round of the algorithm, + // including the final encrypted ciphertext value. + function automatic void sv_dpi_present_encrypt( + input bit [DataWidth-1:0] plaintext, + input bit [MaxKeyWidth-1:0] key, + input int unsigned key_size, + input int unsigned num_rounds, + output bit [DataWidth-1:0] ciphertext + ); + + bit [DataWidth-1:0] round_in, round_out; + chandle h = c_dpi_present_mk(key_size, key); + + round_out = plaintext; + for (int i = 1; i <= num_rounds; i++) begin + round_in = round_out; + c_dpi_present_enc_round(h, i, i == num_rounds, round_in, round_out); + end + ciphertext = round_out; + + c_dpi_present_free(h); + + endfunction + + // This function decrypts the input ciphertext with the PRESENT decryption algorithm. + // + // This produces a list of all intermediate values produced after each round of the algorithm, + // including the final decrypted plaintext value. + function automatic void sv_dpi_present_decrypt( + input bit [DataWidth-1:0] ciphertext, + input bit [MaxKeyWidth-1:0] key, + input int unsigned key_size, + input int unsigned num_rounds, + output bit [DataWidth-1:0] plaintext + ); + + bit [DataWidth-1:0] round_in, round_out; + chandle h = c_dpi_present_mk(key_size, key); + + round_in = ciphertext; + for (int i = num_rounds; i > 0; i--) begin + c_dpi_present_dec_round(h, i, i == num_rounds, round_in, round_out); + round_in = round_out; + end + plaintext = round_out; + + c_dpi_present_free(h); + + endfunction + +endpackage diff --git a/synth/prim/dv/prim_present/data/prim_present_cover.cfg b/synth/prim/dv/prim_present/data/prim_present_cover.cfg new file mode 100755 index 0000000..8789a92 --- /dev/null +++ b/synth/prim/dv/prim_present/data/prim_present_cover.cfg @@ -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_present +begin tgl(portsonly) + -tree prim_present_tb + +module prim_present +end diff --git a/synth/prim/dv/prim_present/prim_present_sim.core b/synth/prim/dv/prim_present/prim_present_sim.core new file mode 100755 index 0000000..5fcb3d4 --- /dev/null +++ b/synth/prim/dv/prim_present/prim_present_sim.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:dv:prim_present_sim:0.1" +description: "PRESENT block cipher DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_macros + - lowrisc:dv:dv_test_status + - lowrisc:dv:crypto_dpi_present:0.1 + files: + - tb/prim_present_tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: prim_present_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/synth/prim/dv/prim_present/prim_present_sim_cfg.hjson b/synth/prim/dv/prim_present/prim_present_sim_cfg.hjson new file mode 100755 index 0000000..9b4b828 --- /dev/null +++ b/synth/prim/dv/prim_present/prim_present_sim_cfg.hjson @@ -0,0 +1,50 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_present + + // Top level dut name (sv module). + dut: prim_present + + // Top level testbench name (sv module). + tb: prim_present_tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_present_sim:0.1 + + // Import additional common sim cfg files. + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + + // Default iterations for all tests - each test entry can override this. + reseed: 50 + + // Enable cdc instrumentation + run_opts: ["+cdc_instrumentation_enabled=1"] + + overrides: [ + { + name: vcs_cov_cfg_file + value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_present/data/prim_present_cover.cfg" + } + ] + + // List of test specifications. + tests: [ + { + name: prim_present_test + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["prim_present_test"] + } + ] +} diff --git a/synth/prim/dv/prim_present/tb/prim_present_tb.sv b/synth/prim/dv/prim_present/tb/prim_present_tb.sv new file mode 100755 index 0000000..4a80b18 --- /dev/null +++ b/synth/prim/dv/prim_present/tb/prim_present_tb.sv @@ -0,0 +1,234 @@ +// 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_present, drives various test vectors into all +// implementations and compares intermediate and final output against +// C-reference model, for both encryption and decryption. +// +// This testbench only tests the PRESENT block cipher in its 64-bit data +// and 128-bit key configuration, other configurations with different sets of +// widths remain untested. + +module prim_present_tb; + `include "dv_macros.svh" + + import crypto_dpi_present_pkg::MaxRounds; + import crypto_dpi_present_pkg::MaxKeyWidth; + import crypto_dpi_present_pkg::DataWidth; + +////////////////////////////////////////////////////// +// config +////////////////////////////////////////////////////// + +`ifndef KEY_WIDTH + `define KEY_WIDTH 128 +`endif + + // Key width can be set with a define, but must be 80 or 128 + localparam int unsigned KeyWidth = `KEY_WIDTH; + + // used to index the data arrays + localparam bit Encrypt = 1'b0; + localparam bit Decrypt = 1'b1; + localparam bit SingleRound = 1'b0; + localparam bit FullRound = 1'b1; + + localparam int NumDuts = 4; + + // This bit can be set from the command line to indicate that we are running a smoke regression, + // and to run just a single iteration of the test. + // This helps drastically reduce runtimes in the CI flows. + bit smoke_test; + +////////////////////////////////////////////////////// +// DUTs for both encryption and decryption +////////////////////////////////////////////////////// + + // data_in[0]: encryption, data_in[1]: decryption. + // Same scheme used for key_in, data_out, key_out. + logic [1:0][NumDuts-1:0][DataWidth-1:0] data_in; + logic [1:0][NumDuts-1:0][KeyWidth-1 :0] key_in; + logic [1:0][NumDuts-1:0][4:0] idx_in; + logic [1:0][NumDuts-1:0][DataWidth-1:0] data_out; + logic [1:0][NumDuts-1:0][KeyWidth-1 :0] key_out; + logic [1:0][NumDuts-1:0][4:0] idx_out; + + for (genvar j = 0; j < 2; j++) begin : gen_encrypt_decrypt + for (genvar k = 0; k < 2; k++) begin : gen_duts + localparam int NumRounds = (k > 0) ? MaxRounds : 1; + prim_present #( + .DataWidth ( DataWidth ), + .KeyWidth ( KeyWidth ), + .NumRounds ( MaxRounds ), + .NumPhysRounds ( NumRounds ), + .Decrypt ( j ) + ) dut ( + .data_i ( data_in[j][k] ), + .key_i ( key_in[j][k] ), + .idx_i ( idx_in[j][k] ), + .data_o ( data_out[j][k] ), + .key_o ( key_out[j][k] ), + .idx_o ( idx_out[j][k] ) + ); + end + end + +////////////////////////////////////////////////////// +// API called by the testbench to drive/check stimulus +////////////////////////////////////////////////////// + + // Top level API task that should be called to run a full pass + // of encryption and decryption on some input data and key. + task automatic test_present(bit [DataWidth-1:0] plaintext, + bit [KeyWidth-1:0] key); + + bit [DataWidth-1:0] ciphertext; + check_encryption(plaintext, key, ciphertext); + check_decryption(ciphertext, key, key_out[Encrypt]); + endtask + + + // Helper task to drive plaintext and key into each encryption instance. + // Calls a subroutine to perform checks on the outputs (once they are available). + task automatic check_encryption(input bit [DataWidth-1:0] plaintext, + input bit [KeyWidth-1:0] key, + output bit [DataWidth-1:0] expected_ciphertext); + + // Drive input into encryption instances. + data_in[Encrypt] <= '{default: plaintext}; + key_in[Encrypt] <= '{default: key}; + idx_in[Encrypt] <= '{default: 5'd1}; + + // Iterate MaxRounds times for the single-round instance. + for (int unsigned i = 0; i < MaxRounds-1; i++) begin + #10ns; + data_in[Encrypt][SingleRound] <= data_out[Encrypt][SingleRound]; + key_in[Encrypt][SingleRound] <= key_out[Encrypt][SingleRound]; + idx_in[Encrypt][SingleRound] <= idx_out[Encrypt][SingleRound]; + end + + // Wait a bit for the DUTs to finish calculations. + #10ns; + + crypto_dpi_present_pkg::sv_dpi_present_encrypt(plaintext, MaxKeyWidth'(key), + KeyWidth, MaxRounds, expected_ciphertext); + check_output(data_out[Encrypt][SingleRound], + expected_ciphertext, + $sformatf("Single Round Encryption; %0d rounds, key width %0d", + MaxRounds, KeyWidth)); + check_output(data_out[Encrypt][FullRound], + expected_ciphertext, + $sformatf("Full Round Encryption; %0d rounds, key width %0d", + MaxRounds, KeyWidth)); + endtask + + + // Helper task to drive ciphertext and key into each decryption instance. + // Calls a subroutine to perform checks on the outputs (once they are available). + task automatic check_decryption(input bit [DataWidth-1:0] ciphertext, + input bit [KeyWidth-1:0] key, + input bit [NumDuts-1:0][KeyWidth-1:0] decryption_keys); + bit [DataWidth-1:0] expected_plaintext; + + // Drive input into decryption instances. + data_in[Decrypt] <= '{default: ciphertext}; + key_in[Decrypt] <= decryption_keys; + idx_in[Decrypt] <= '{default: 5'(MaxRounds)}; + + // Iterate MaxRounds times for the single-round instance. + for (int unsigned i = 0; i < MaxRounds-1; i++) begin + #10ns; + data_in[Decrypt][SingleRound] <= data_out[Decrypt][SingleRound]; + key_in[Decrypt][SingleRound] <= key_out[Decrypt][SingleRound]; + idx_in[Decrypt][SingleRound] <= idx_out[Decrypt][SingleRound]; + end + + // Wait a bit for the DUTs to finish calculations. + #10ns; + + crypto_dpi_present_pkg::sv_dpi_present_decrypt(ciphertext, MaxKeyWidth'(key), + KeyWidth, MaxRounds, expected_plaintext); + check_output(data_out[Decrypt][SingleRound], + expected_plaintext, + $sformatf("Single Round Decryption; %0d rounds, key width %0d", + MaxRounds, KeyWidth)); + check_output(data_out[Decrypt][FullRound], + expected_plaintext, + $sformatf("Full Round Decryption; %0d rounds, key width %0d", + MaxRounds, KeyWidth)); + endtask + + + // Helper subroutine to compare key and data output values from + // the C-reference model and the DUTs. + function automatic void check_output(bit [DataWidth-1:0] dut_value, + bit [DataWidth-1:0] exp_value, + string desc); + if (dut_value != exp_value) begin + $error("%s: MISMATCH. Expected[0x%0x] - Actual[0x%0x]", desc, exp_value, dut_value); + dv_test_status_pkg::dv_test_status(.passed(1'b0)); + end + endfunction + + +////////////////////////////////////////////////////// +// main testbench body +////////////////////////////////////////////////////// + + initial begin : p_stimuli + int num_trans; + + // The key and plaintext/ciphertext to be fed into PRESENT instances. + bit [KeyWidth-1:0] key; + bit [DataWidth-1:0] plaintext; + + $timeformat(-12, 0, " ps", 12); + + ///////////////////////////// + // Test the 4 golden vectors. + ///////////////////////////// + + plaintext = '0; + key = '0; + test_present(plaintext, key); + + plaintext = '0; + key = '1; + test_present(plaintext, key); + + plaintext = '1; + key = '0; + test_present(plaintext, key); + + plaintext = '1; + key = '1; + test_present(plaintext, key); + + // Test random vectors + void'($value$plusargs("smoke_test=%0b", smoke_test)); + num_trans = smoke_test ? 1 : $urandom_range(5000, 25000); + for (int i = 0; i < num_trans; i++) begin + randomize(plaintext); + randomize(key); + test_present(plaintext, key); + end + + // Final error checking and print out the test signature (expected by simulation flow). + $display("All encryption and decryption passes were successful!"); + dv_test_status_pkg::dv_test_status(.passed(1'b1)); + $finish(); + end + + initial begin + bit poll_for_stop; + int unsigned poll_for_stop_interval_ns; + + poll_for_stop = 1'b1; + void'($value$plusargs("poll_for_stop=%0b", poll_for_stop)); + poll_for_stop_interval_ns = 1000; + void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns)); + if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns)); + end + +endmodule : prim_present_tb diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c new file mode 100755 index 0000000..fba3a5b --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.c @@ -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 + +#ifdef _cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "prince_ref.h" +#include "svdpi.h" + +extern uint64_t c_dpi_prince_encrypt(uint64_t plaintext, uint64_t key0, + uint64_t key1, int num_half_rounds, + int old_key_schedule) { + return prince_enc_dec_uint64(plaintext, key0, key1, 0, num_half_rounds, + old_key_schedule); +} + +extern uint64_t c_dpi_prince_decrypt(const uint64_t ciphertext, + const uint64_t key0, const uint64_t key1, + int num_half_rounds, + int old_key_schedule) { + return prince_enc_dec_uint64(ciphertext, key0, key1, 1, num_half_rounds, + old_key_schedule); +} + +#ifdef _cplusplus +} +#endif diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core new file mode 100755 index 0000000..1ce8133 --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince.core @@ -0,0 +1,18 @@ +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:crypto_dpi_prince:0.1" +description: "PRINCE block cipher reference C implementation DPI interface" +filesets: + files_dv: + depend: + - lowrisc:dv:crypto_prince_ref + files: + - crypto_dpi_prince.c: {file_type: cSource} + - crypto_dpi_prince_pkg.sv: {file_type: systemVerilogSource} + +targets: + default: + filesets: + - files_dv diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv new file mode 100755 index 0000000..8535e98 --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_pkg.sv @@ -0,0 +1,64 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package crypto_dpi_prince_pkg; + + // dep packages + + // parameters + // Max number of half-rounds. + parameter int unsigned NumRoundsHalf = 5; + + // DPI-C imports + import "DPI-C" context function longint c_dpi_prince_encrypt( + input longint unsigned data, + input longint unsigned key0, + input longint unsigned key1, + input int unsigned num_half_rounds, + input int unsigned new_key_schedule + ); + + import "DPI-C" context function longint c_dpi_prince_decrypt( + input longint unsigned data, + input longint unsigned key0, + input longint unsigned key1, + input int unsigned num_half_rounds, + input int unsigned new_key_schedule + ); + + ////////////////////////////////////////////////////// + // SV wrapper functions to be used by the testbench // + ////////////////////////////////////////////////////// + + function automatic void sv_dpi_prince_encrypt( + input bit [63:0] plaintext, + input bit [127:0] key, + input bit old_key_schedule, + output bit [NumRoundsHalf-1:0][63:0] ciphertext + ); + for (int i = 0; i < NumRoundsHalf; i++) begin + ciphertext[i] = c_dpi_prince_encrypt(plaintext, + key[127:64], // k0 gets assigned the MSB halve + key[63:0], // k1 gets assigned the LSB halve + i+1, + old_key_schedule); + end + endfunction + + function automatic void sv_dpi_prince_decrypt( + input bit [NumRoundsHalf-1:0][63:0] ciphertext, + input bit [127:0] key, + input bit old_key_schedule, + output bit [NumRoundsHalf-1:0][63:0] plaintext + ); + for (int i = 0; i < NumRoundsHalf; i++) begin + plaintext[i] = c_dpi_prince_decrypt(ciphertext[i], + key[127:64], // k0 gets assigned the MSB halve + key[63:0], // k1 gets assigned the LSB halve + i+1, + old_key_schedule); + end + endfunction + +endpackage diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson new file mode 100755 index 0000000..8c5b0f7 --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson @@ -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 +{ + // Additional build-time options needed to compile C++ sources in + // simulators such as VCS and Xcelium for anything that uses + // crypto_prince_ref. + crypto_prince_ref_core: "lowrisc:dv:crypto_prince_ref:0.1" + crypto_prince_ref_src_dir: "{eval_cmd} echo \"{crypto_prince_ref_core}\" | tr ':' '_'" + + build_modes: [ + { + name: vcs_crypto_dpi_prince_build_opts + build_opts: ["-CFLAGS -I{build_dir}/src/{crypto_prince_ref_src_dir}"] + } + + { + name: xcelium_crypto_dpi_prince_build_opts + build_opts: ["-I{build_dir}/src/{crypto_prince_ref_src_dir}"] + } + ] +} diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.core new file mode 100755 index 0000000..20868bc --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/crypto_prince_ref.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:dv:crypto_prince_ref:0.1" +description: "PRINCE block cipher reference C implementation from Sebastien Riou" +filesets: + files_dv: + files: + - prince_ref.h: {file_type: cSource, is_include_file: true} + +targets: + default: + filesets: + - files_dv + tools: + vcs: + vcs_options: + - '-CFLAGS -I../../src/lowrisc_dv_crypto_prince_ref_0.1' diff --git a/synth/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h b/synth/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h new file mode 100755 index 0000000..a807869 --- /dev/null +++ b/synth/prim/dv/prim_prince/crypto_dpi_prince/prince_ref.h @@ -0,0 +1,342 @@ +// Copyright lowRISC contributors. +// Copyright 2016 Sebastien Riou +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_ +#define OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_ + +/*! \file prince_ref.h + \brief Reference implementation of the Prince block cipher, complient to + C99. 'Reference' here means straightforward, unoptimized, and checked against + the few test vectors provided in the original paper + (http://eprint.iacr.org/2012/529.pdf). By defining the macro PRINCE_PRINT, + one can print out all successive internal states. +*/ + +/* + * This C-implementation of the PRINCE block cipher is taken from Sebastien + * Riou's open-sourced 'prince-c-ref' GitHub repository, found here: + * https://github.com/sebastien-riou/prince-c-ref/blob/master/include/prince_ref.h. + * + * Several modifications of varying severity have been made to this C model to + * enable extra external parameterizations for more thorough verification of the + * OpenTitan PRINCE hardware implementation. + * These modifications, in no particular order, are: + * + * - Conversion of `prince_enc_dec_uint64_t(...)` to a non-static function to + * allow usage by an external DPI-C model. + * - Additional arguments added to `prince_enc_dec_uint64_t(...)`, + * `prince_enc_dec(...)`, `prince_encrypt(...)` `prince_decrypt(...)`, and + * `prince_core(...)` to allow an external DPI-C model to control the + * number of cipher half-rounds and to toggle between the "legacy" key + * schedule detailed in the original PRINCE paper and a newer key schedule. + * - Modification of `prince_core(...)` to handle the new key schedule and + * user-specified number of half-rounds. + */ + +#include +#include + +#ifndef PRINCE_PRINT +#define PRINCE_PRINT(a) \ + do { \ + } while (0) +#endif + +/** + * Converts a byte array into a 64-bit integer. + * + * The byte at index 0 is placed as the most significant byte. + */ +static uint64_t bytes_to_uint64(const uint8_t in[8]) { + uint64_t out = 0; + unsigned int i; + for (i = 0; i < 8; i++) + out = (out << 8) | in[i]; + return out; +} + +/** + * Converts a 64-bit integer into a byte array. + * + * The most significant byte is placed at index 0. + */ +static void uint64_to_bytes(const uint64_t in, uint8_t out[8]) { + unsigned int i; + for (i = 0; i < 8; i++) + out[i] = in >> ((7 - i) * 8); +} + +/** + * Computes K0' from K0. + */ +static uint64_t prince_k0_to_k0_prime(const uint64_t k0) { + uint64_t k0_ror1 = (k0 >> 1) | (k0 << 63); + uint64_t k0_prime = k0_ror1 ^ (k0 >> 63); + return k0_prime; +} + +static uint64_t prince_round_constant(const unsigned int round) { + uint64_t rc[] = {0x0000000000000000, 0x13198a2e03707344, 0xa4093822299f31d0, + 0x082efa98ec4e6c89, 0x452821e638d01377, 0xbe5466cf34e90c6c, + 0x7ef84f78fd955cb1, 0x85840851f1ac43aa, 0xc882d32f25323c54, + 0x64a51195e0e3610d, 0xd3b5a399ca0c2399, 0xc0ac29b7c97c50dd}; + return rc[round]; +} + +/** + * The 4 bit Prince sbox. + * + * Only the 4 lsb are taken into account to preserve nibble granularity. + */ +static unsigned int prince_sbox(unsigned int nibble) { + const unsigned int sbox[] = {0xb, 0xf, 0x3, 0x2, 0xa, 0xc, 0x9, 0x1, + 0x6, 0x7, 0x8, 0x0, 0xe, 0x5, 0xd, 0x4}; + return sbox[nibble & 0xF]; +} + +/** + * The 4 bit Prince inverse sbox. + * + * Only the 4 lsb are taken into account to preserve nibble granularity. + */ +static unsigned int prince_sbox_inv(unsigned int nibble) { + const unsigned int sbox[] = {0xb, 0x7, 0x3, 0x2, 0xf, 0xd, 0x8, 0x9, + 0xa, 0x6, 0x4, 0x0, 0x5, 0xe, 0xc, 0x1}; + return sbox[nibble & 0xF]; +} + +/** + * The S step of the Prince cipher. + */ +static uint64_t prince_s_layer(const uint64_t s_in) { + uint64_t s_out = 0; + for (unsigned int i = 0; i < 16; i++) { + const unsigned int shift = i * 4; + const unsigned int sbox_in = s_in >> shift; + const uint64_t sbox_out = prince_sbox(sbox_in); + s_out |= sbox_out << shift; + } + return s_out; +} + +/** + * The S^-1 step of the Prince cipher. + */ +static uint64_t prince_s_inv_layer(const uint64_t s_inv_in) { + uint64_t s_inv_out = 0; + for (unsigned int i = 0; i < 16; i++) { + const unsigned int shift = i * 4; + const unsigned int sbox_in = s_inv_in >> shift; + const uint64_t sbox_out = prince_sbox_inv(sbox_in); + s_inv_out |= sbox_out << shift; + } + return s_inv_out; +} + +static uint64_t gf2_mat_mult16_1(const uint64_t in, const uint64_t mat[16]) { + uint64_t out = 0; + for (unsigned int i = 0; i < 16; i++) { + if ((in >> i) & 1) + out ^= mat[i]; + } + return out; +} + +/** + * Build Prince's 16 bit matrices M0 and M1. + */ +static inline void prince_m16_matrices(uint64_t m16[2][16]) { + // 4 bits matrices m0 to m3 are stored in array m4 + const uint64_t m4[4][4] = {// m0 + {0x0, 0x2, 0x4, 0x8}, + // m1 + {0x1, 0x0, 0x4, 0x8}, + // m2 + {0x1, 0x2, 0x0, 0x8}, + // m3 + {0x1, 0x2, 0x4, 0x0}}; + // build 16 bits matrices + for (unsigned int i = 0; i < 16; i++) { + const unsigned int base = i / 4; + m16[0][i] = + (m4[(base + 3) % 4][i % 4] << 8) | (m4[(base + 2) % 4][i % 4] << 4) | + (m4[(base + 1) % 4][i % 4] << 0) | (m4[(base + 0) % 4][i % 4] << 12); + m16[1][i] = + (m16[0][i] >> 12) | + (0xFFFF & (m16[0][i] << 4)); // m1 is just a rotated version of m0 + } +} + +/** + * The M' step of the Prince cipher. + */ +static uint64_t prince_m_prime_layer(const uint64_t m_prime_in) { + // 16 bits matrices M0 and M1, generated using the code below + // uint64_t m16[2][16];prince_m16_matrices(m16); + // for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[0][i]); + // for(unsigned int i=0;i<16;i++) PRINCE_PRINT(m16[1][i]); + static const uint64_t m16[2][16] = { + {0x0111, 0x2220, 0x4404, 0x8088, 0x1011, 0x0222, 0x4440, 0x8808, 0x1101, + 0x2022, 0x0444, 0x8880, 0x1110, 0x2202, 0x4044, 0x0888}, + + {0x1110, 0x2202, 0x4044, 0x0888, 0x0111, 0x2220, 0x4404, 0x8088, 0x1011, + 0x0222, 0x4440, 0x8808, 0x1101, 0x2022, 0x0444, 0x8880}}; + const uint64_t chunk0 = gf2_mat_mult16_1(m_prime_in >> (0 * 16), m16[0]); + const uint64_t chunk1 = gf2_mat_mult16_1(m_prime_in >> (1 * 16), m16[1]); + const uint64_t chunk2 = gf2_mat_mult16_1(m_prime_in >> (2 * 16), m16[1]); + const uint64_t chunk3 = gf2_mat_mult16_1(m_prime_in >> (3 * 16), m16[0]); + const uint64_t m_prime_out = (chunk3 << (3 * 16)) | (chunk2 << (2 * 16)) | + (chunk1 << (1 * 16)) | (chunk0 << (0 * 16)); + return m_prime_out; +} + +/** + * The shift row and inverse shift row of the Prince cipher. + */ +static uint64_t prince_shift_rows(const uint64_t in, int inverse) { + const uint64_t row_mask = 0xF000F000F000F000; + uint64_t shift_rows_out = 0; + for (unsigned int i = 0; i < 4; i++) { + const uint64_t row = in & (row_mask >> (4 * i)); + const unsigned int shift = inverse ? i * 16 : 64 - i * 16; + shift_rows_out |= (row >> shift) | (row << (64 - shift)); + } + return shift_rows_out; +} + +/** + * The M step of the Prince cipher. + */ +static uint64_t prince_m_layer(const uint64_t m_in) { + const uint64_t m_prime_out = prince_m_prime_layer(m_in); + const uint64_t shift_rows_out = prince_shift_rows(m_prime_out, 0); + return shift_rows_out; +} + +/** + * The M^-1 step of the Prince cipher. + */ +static uint64_t prince_m_inv_layer(const uint64_t m_inv_in) { + const uint64_t shift_rows_out = prince_shift_rows(m_inv_in, 1); + const uint64_t m_prime_out = prince_m_prime_layer(shift_rows_out); + return m_prime_out; +} + +/** + * The core function of the Prince cipher. + */ +static uint64_t prince_core(const uint64_t core_input, const uint64_t k0_new, + const uint64_t k1, int num_half_rounds) { + PRINCE_PRINT(core_input); + PRINCE_PRINT(k1); + uint64_t round_input = core_input ^ k1 ^ prince_round_constant(0); + for (int round = 1; round <= num_half_rounds; round++) { + PRINCE_PRINT(round_input); + const uint64_t s_out = prince_s_layer(round_input); + PRINCE_PRINT(s_out); + const uint64_t m_out = prince_m_layer(s_out); + PRINCE_PRINT(m_out); + round_input = (round % 2 == 1) + ? m_out ^ k0_new ^ prince_round_constant(round) + : m_out ^ k1 ^ prince_round_constant(round); + } + const uint64_t middle_round_s_out = prince_s_layer(round_input); + PRINCE_PRINT(middle_round_s_out); + const uint64_t m_prime_out = prince_m_prime_layer(middle_round_s_out); + PRINCE_PRINT(m_prime_out); + const uint64_t middle_round_s_inv_out = prince_s_inv_layer(m_prime_out); + round_input = middle_round_s_inv_out; + for (int round = 1; round <= num_half_rounds; round++) { + PRINCE_PRINT(round_input); + const uint64_t constant_idx = 10 - num_half_rounds + round; + const uint64_t m_inv_in = + ((num_half_rounds + round + 1) % 2 == 1) + ? round_input ^ k0_new ^ prince_round_constant(constant_idx) + : round_input ^ k1 ^ prince_round_constant(constant_idx); + PRINCE_PRINT(m_inv_in); + const uint64_t s_inv_in = prince_m_inv_layer(m_inv_in); + PRINCE_PRINT(s_inv_in); + const uint64_t s_inv_out = prince_s_inv_layer(s_inv_in); + round_input = s_inv_out; + } + const uint64_t core_output = round_input ^ k1 ^ prince_round_constant(11); + PRINCE_PRINT(core_output); + return core_output; +} + +/** + * Top level function for Prince encryption/decryption. + * + * enc_k0 and enc_k1 must be the same for encryption and decryption. + * The handling of decryption is done internally. + */ +uint64_t prince_enc_dec_uint64(const uint64_t input, const uint64_t enc_k0, + const uint64_t enc_k1, int decrypt, + int num_half_rounds, int old_key_schedule) { + const uint64_t prince_alpha = 0xc0ac29b7c97c50dd; + const uint64_t k1 = enc_k1 ^ (decrypt ? prince_alpha : 0); + const uint64_t k0_new = + (old_key_schedule) ? k1 : enc_k0 ^ (decrypt ? prince_alpha : 0); + const uint64_t enc_k0_prime = prince_k0_to_k0_prime(enc_k0); + const uint64_t k0 = decrypt ? enc_k0_prime : enc_k0; + const uint64_t k0_prime = decrypt ? enc_k0 : enc_k0_prime; + PRINCE_PRINT(k0); + PRINCE_PRINT(input); + const uint64_t core_input = input ^ k0; + const uint64_t core_output = + prince_core(core_input, k0_new, k1, num_half_rounds); + const uint64_t output = core_output ^ k0_prime; + PRINCE_PRINT(k0_prime); + PRINCE_PRINT(output); + return output; +} + +/** + * Byte oriented top level function for Prince encryption/decryption. + * + * key_bytes 0 to 7 must contain K0. + * key_bytes 8 to 15 must contain K1. + */ +static void prince_enc_dec(const uint8_t in_bytes[8], + const uint8_t key_bytes[16], uint8_t out_bytes[8], + int decrypt, int num_half_rounds, + int old_key_schedule) { + const uint64_t input = bytes_to_uint64(in_bytes); + const uint64_t enc_k0 = bytes_to_uint64(key_bytes); + const uint64_t enc_k1 = bytes_to_uint64(key_bytes + 8); + const uint64_t output = prince_enc_dec_uint64( + input, enc_k0, enc_k1, decrypt, num_half_rounds, old_key_schedule); + uint64_to_bytes(output, out_bytes); +} + +/** + * Byte oriented top level function for Prince encryption. + * + * key_bytes 0 to 7 must contain K0. + * key_bytes 8 to 15 must contain K1. + */ +static inline void prince_encrypt(const uint8_t in_bytes[8], + const uint8_t key_bytes[16], + uint8_t out_bytes[8], int num_half_rounds, + int old_key_schedule) { + prince_enc_dec(in_bytes, key_bytes, out_bytes, 0, num_half_rounds, + old_key_schedule); +} + +/** + * Byte oriented top level function for Prince decryption. + * + * key_bytes 0 to 7 must contain K0. + * key_bytes 8 to 15 must contain K1. + */ +static inline void prince_decrypt(const uint8_t in_bytes[8], + const uint8_t key_bytes[16], + uint8_t out_bytes[8], int num_half_rounds, + int old_key_schedule) { + prince_enc_dec(in_bytes, key_bytes, out_bytes, 1, num_half_rounds, + old_key_schedule); +} + +#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_PRINCE_CRYPTO_DPI_PRINCE_PRINCE_REF_H_ diff --git a/synth/prim/dv/prim_prince/data/prim_prince_cover.cfg b/synth/prim/dv/prim_prince/data/prim_prince_cover.cfg new file mode 100755 index 0000000..574da3b --- /dev/null +++ b/synth/prim/dv/prim_prince/data/prim_prince_cover.cfg @@ -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 + +// Enables coverage just only each of the prim_prince instances. +// Limits toggle coverage to DUT IOs. + ++module prim_prince +begin tgl(portsonly) + +module prim_prince +end diff --git a/synth/prim/dv/prim_prince/prim_prince_sim.core b/synth/prim/dv/prim_prince/prim_prince_sim.core new file mode 100755 index 0000000..38007c0 --- /dev/null +++ b/synth/prim/dv/prim_prince/prim_prince_sim.core @@ -0,0 +1,33 @@ +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_prince_sim:0.1" +description: "PRINCE block cipher DV sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + file_type: systemVerilogSource + + files_dv: + depend: + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_macros + - lowrisc:dv:common_ifs + - lowrisc:dv:dv_test_status + - lowrisc:dv:crypto_dpi_prince + files: + - tb/prim_prince_tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: prim_prince_tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/synth/prim/dv/prim_prince/prim_prince_sim_cfg.hjson b/synth/prim/dv/prim_prince/prim_prince_sim_cfg.hjson new file mode 100755 index 0000000..c1253ab --- /dev/null +++ b/synth/prim/dv/prim_prince/prim_prince_sim_cfg.hjson @@ -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 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_prince + + // Top level dut name (sv module). + dut: prim_prince + + // Top level testbench name (sv module). + tb: prim_prince_tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_prince_sim:0.1 + + // Import additional common sim cfg files. + import_cfgs: ["{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson", + // Config files to get the correct flags for crypto_dpi_prince + "{proj_root}/hw/ip/prim/dv/prim_prince/crypto_dpi_prince/crypto_dpi_prince_sim_opts.hjson"] + + en_build_modes: ["{tool}_crypto_dpi_prince_build_opts"] + + // Default iterations for all tests - each test entry can override this. + reseed: 50 + + // Enable cdc instrumentation + run_opts: ["+cdc_instrumentation_enabled=1"] + + overrides: [ + { + name: vcs_cov_cfg_file + value: "-cm_hier {proj_root}/hw/ip/prim/dv/prim_prince/data/prim_prince_cover.cfg" + } + ] + + // List of test specifications. + tests: [ + { + name: prim_prince_test + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["prim_prince_test"] + } + { + name: nightly + // Run the same test as the "smoke" regression, just with a higher reseed value. + // This higher reseed value is due to the rather large state space created by + // the 128-bit key and 64-bit data values. + reseed: 500 + } + ] +} diff --git a/synth/prim/dv/prim_prince/tb/prim_prince_tb.sv b/synth/prim/dv/prim_prince/tb/prim_prince_tb.sv new file mode 100755 index 0000000..4d54ba8 --- /dev/null +++ b/synth/prim/dv/prim_prince/tb/prim_prince_tb.sv @@ -0,0 +1,322 @@ +// 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_prince, drives various test vectors into all +// implementations and compares intermediate and final output against +// C-reference model, for both encryption and decryption. +// +// This testbench only tests the PRINCE block cipher in its 64-bit data +// and 128-bit key configuration, other configurations with different sets of +// widths remain untested. + +module prim_prince_tb; + `include "dv_macros.svh" + +////////////////////////////////////////////////////// +// config +////////////////////////////////////////////////////// + +// Default to {data_width:64, key_width:128} configuration. +// Data width and key width can be overriden from command-line if desired. + +`ifdef DATA_WIDTH + localparam int unsigned DataWidth = `DATA_WIDTH; +`else + localparam int unsigned DataWidth = 64; +`endif + +`ifdef KEY_WIDTH + localparam int unsigned KeyWidth = `KEY_WIDTH; +`else + localparam int unsigned KeyWidth = 128; +`endif + + localparam string MSG_ID = $sformatf("%m"); + + // Max number of half-rounds according to spec. + // Duplicate parameter definition here to avoid clutter due to long identifier. + localparam int unsigned NumRoundsHalf = crypto_dpi_prince_pkg::NumRoundsHalf; + + // Use these to index the data/key arrays. + localparam bit Unregistered = 1'b0; + localparam bit Registered = 1'b1; + + localparam bit NewKeySched = 1'b0; + localparam bit OldKeySched = 1'b1; + + localparam time ClkPeriod = 10000; + + // This bit can be set from the command line to indicate that we are running a smoke regression, + // and to run just a single iteration of the test. + // This helps drastically reduce runtimes in the CI flows. + bit smoke_test; + +////////////////////////////////////////////////////// +// Clock interface +////////////////////////////////////////////////////// + + wire clk, rst_n; + + clk_rst_if clk_if ( + .clk, + .rst_n + ); + +////////////////////////////////////////////////////// +// DUTs for both encryption and decryption +////////////////////////////////////////////////////// + + logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] data_in; + logic [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] data_out; + logic [1:0][1:0][NumRoundsHalf-1:0] valid_out; + logic valid_in; + logic [KeyWidth-1:0] key_in; + logic dec_in; + + for (genvar i = 0; i < 2; i++) begin : gen_new_key_schedule + for (genvar j = 0; j < 2; j++) begin : gen_registered_variant + for (genvar k = 0; k < NumRoundsHalf; k++) begin : gen_duts + prim_prince #( + .DataWidth ( DataWidth ), + .KeyWidth ( KeyWidth ), + .NumRoundsHalf ( k+1 ), + .UseOldKeySched ( i ), + .HalfwayDataReg ( j ), + .HalfwayKeyReg ( j ) + ) dut ( + .clk_i ( clk ), + .rst_ni ( rst_n ), + .valid_i ( valid_in ), + .data_i ( data_in[i][j][k] ), + .key_i ( key_in ), + .dec_i ( dec_in ), + .valid_o ( valid_out[i][j][k] ), + .data_o ( data_out[i][j][k] ) + ); + end : gen_duts + end : gen_registered_variant + end : gen_new_key_schedule + +////////////////////////////////////////////////////// +// API called by the testbench to drive/check stimulus +////////////////////////////////////////////////////// + + // Top level API task that should be called to run a full pass + // of encryption and decryption on some input data and key. + task automatic test_prince(bit [DataWidth-1:0] plaintext, + bit [KeyWidth-1:0] key); + + bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] encrypted_text; + + check_encryption(plaintext, key, encrypted_text); + + check_decryption(encrypted_text, key); + + endtask + + + // Helper task to drive plaintext and key into each encryption instance. + // Calls a subroutine to perform checks on the outputs (once they are available). + task automatic check_encryption( + input bit [DataWidth-1:0] plaintext, + input bit [KeyWidth-1:0] key, + output bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_cipher); + + // Drive input into encryption instances. + key_in = key; + dec_in = 0; + valid_in = 0; + for (int unsigned i = 0; i < 2; i++) begin + for (int unsigned j = 0; j < 2; j++) begin + for (int unsigned k = 0; k < NumRoundsHalf; k++) begin + data_in[i][j][k] = plaintext; + end + end + end + // wait some time before signaling that the inputs are valid + clk_if.wait_clks($urandom_range(0, 10)); + valid_in = 1; + // Wait for the DUTs to finish calculations. + clk_if.wait_clks(2); + wait(&valid_out == 1); + valid_in = 0; + clk_if.wait_clks(1); + // query DPI model for expected encrypted output. + for (int i = 0; i < 2; i++) begin + for (int j = 0; j < 2; j++) begin + crypto_dpi_prince_pkg::sv_dpi_prince_encrypt(plaintext, key, i, + expected_cipher[i][j]); + end + end + check_output(expected_cipher[OldKeySched], + expected_cipher[NewKeySched], + data_out[OldKeySched], + data_out[NewKeySched], + "Encryption"); + endtask + + + // Helper task to drive ciphertext and key into each decryption instance. + // Calls a subroutine to perform checks on the outputs (once they are available). + task automatic check_decryption( + input bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] ciphertext, + input bit [KeyWidth-1:0] key); + + // the expected plaintext after decryption will be provided by the C model. + bit [1:0][1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_plaintext; + // Drive input into decryption instances. + key_in = key; + dec_in = 1; + valid_in = 0; + for (int unsigned i = 0; i < 2; i++) begin + for (int unsigned j = 0; j < 2; j++) begin + for (int unsigned k = 0; k < NumRoundsHalf; k++) begin + data_in[i][j][k] = ciphertext[i][j][k]; + end + end + end + // wait some time before signaling that the inputs are valid + clk_if.wait_clks($urandom_range(0, 10)); + valid_in = 1; + // Wait for the DUTs to finish calculations. + clk_if.wait_clks(2); + wait(&valid_out == 1); + valid_in = 0; + // query DPI model for expected decrypted output. + for (int unsigned i = 0; i < 2; i++) begin + for (int unsigned j = 0; j < 2; j++) begin + crypto_dpi_prince_pkg::sv_dpi_prince_decrypt(ciphertext[i][j], key, i, + expected_plaintext[i][j]); + end + end + check_output(expected_plaintext[OldKeySched], + expected_plaintext[NewKeySched], + data_out[OldKeySched], + data_out[NewKeySched], + "Decryption"); + endtask + + + // Helper subroutine to compare key and data output values from + // the C-reference model and the DUTs. + // + // For each instance of PRINCE (whether using old or new key schedule), + // we need to check that the output data matches the output of the reference model. + // + // If any comparison error is seen, this task short-circuits immediately, + // printing out some debug information and the correct failure signature. + task automatic check_output( + input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_old_sched, + input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] expected_text_new_sched, + input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_old_sched, + input bit [1:0][NumRoundsHalf-1:0][DataWidth-1:0] actual_text_new_sched, + input string msg); + + string reg_msg; + string err_msg; + for (int unsigned i = 0; i < 2; i++) begin + reg_msg = i ? "registered" : "unregistered"; + for (int unsigned j = 0; j < NumRoundsHalf; j++) begin + // compare outputs generated using old key schedule. + if (expected_text_old_sched[i][j] != actual_text_old_sched[i][j]) begin + err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n", + msg, reg_msg, i+1), + $sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_old_sched[i][j], + actual_text_old_sched[i][j])}; + $error(err_msg); + dv_test_status_pkg::dv_test_status(.passed(1'b0)); + end + // compare outputs generated using new key schedule. + if (expected_text_new_sched[i][j] != actual_text_new_sched[i][j]) begin + err_msg = {$sformatf("%s mismatch in %s design with %0d rounds and old key schedule!\n", + msg, reg_msg, i+1), + $sformatf("Expected[0x%0x] - Actual[0x%0x]\n", expected_text_new_sched[i][j], + actual_text_new_sched[i][j])}; + $error(err_msg); + dv_test_status_pkg::dv_test_status(.passed(1'b0)); + end + end + end + endtask + +////////////////////////////////////////////////////// +// main testbench body +////////////////////////////////////////////////////// + + initial begin : p_stimuli + int num_trans; + + // The key and plaintext/ciphertext to be fed into PRINCE instances. + bit [KeyWidth/2-1:0] k0, k1; + bit [DataWidth-1:0] plaintext; + + $timeformat(-12, 0, " ps", 12); + clk_if.set_period_ps(ClkPeriod); + clk_if.set_active(); + // Toggle reset twice at start of the test to hit a small toggle coverage point: + // - rst_ni: 1 -> 0 + // No additional functional impact + clk_if.apply_reset(); + clk_if.wait_clks(5); + clk_if.apply_reset(); + clk_if.wait_clks(5); + + ///////////////////////////// + // Test the 5 golden vectors. + ///////////////////////////// + + plaintext = '0; + k0 = '1; + k1 = '0; + test_prince(plaintext, {k1, k0}); + + plaintext = '0; + k0 = '0; + k1 = '0; + test_prince(plaintext, {k1, k0}); + + plaintext = '1; + k0 = '0; + k1 = '0; + test_prince(plaintext, {k1, k0}); + + plaintext = '0; + k0 = '0; + k1 = '1; + test_prince(plaintext, {k1, k0}); + + plaintext = 'h0123456789ABCDEF; + k0 = '0; + k1 = 'hFEDC_BA98_7654_3210; + test_prince(plaintext, {k1, k0}); + + // Test random vectors + void'($value$plusargs("smoke_test=%0b", smoke_test)); + num_trans = smoke_test ? 1 : $urandom_range(5000, 25000); + for (int i = 0; i < num_trans; i++) begin + `DV_CHECK_STD_RANDOMIZE_FATAL(plaintext, "", MSG_ID) + `DV_CHECK_STD_RANDOMIZE_FATAL(k0, "", MSG_ID) + `DV_CHECK_STD_RANDOMIZE_FATAL(k1, "", MSG_ID) + test_prince(plaintext, {k1, k0}); + end + + // Final error checking and print out the test signature (expected by simulation flow). + $display("All encryption and decryption passes were successful!"); + dv_test_status_pkg::dv_test_status(.passed(1'b1)); + $finish(); + end + + // TODO: perhaps wrap this in a macro? + initial begin + bit poll_for_stop; + int unsigned poll_for_stop_interval_ns; + + poll_for_stop = 1'b1; + void'($value$plusargs("poll_for_stop=%0b", poll_for_stop)); + poll_for_stop_interval_ns = 1000; + void'($value$plusargs("poll_for_stop_interval_ns=%0d", poll_for_stop_interval_ns)); + if (poll_for_stop) dv_utils_pkg::poll_for_stop(.interval_ns(poll_for_stop_interval_ns)); + end + +endmodule : prim_prince_tb diff --git a/synth/prim/dv/prim_ram_scr/cpp/scramble_model.cc b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.cc new file mode 100755 index 0000000..14a6790 --- /dev/null +++ b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.cc @@ -0,0 +1,366 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "scramble_model.h" + +#include +#include +#include +#include +#include +#include + +#include "prince_ref.h" + +uint8_t PRESENT_SBOX4[] = {0xc, 0x5, 0x6, 0xb, 0x9, 0x0, 0xa, 0xd, + 0x3, 0xe, 0xf, 0x8, 0x4, 0x7, 0x1, 0x2}; + +uint8_t PRESENT_SBOX4_INV[] = {0x5, 0xe, 0xf, 0x8, 0xc, 0x1, 0x2, 0xd, + 0xb, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xa}; + +static const uint32_t kNumAddrSubstPermRounds = 2; +static const uint32_t kNumDataSubstPermRounds = 2; +static const uint32_t kNumPrinceHalfRounds = 2; + +static std::vector byte_reverse_vector( + const std::vector &vec_in) { + std::vector vec_out(vec_in.size()); + + std::reverse_copy(std::begin(vec_in), std::end(vec_in), std::begin(vec_out)); + + return vec_out; +} + +static uint8_t read_vector_bit(const std::vector &vec, + uint32_t bit_pos) { + assert(bit_pos / 8 < vec.size()); + + return (vec[bit_pos / 8] >> (bit_pos % 8)) & 1; +} + +static void or_vector_bit(std::vector &vec, uint32_t bit_pos, + uint8_t bit) { + assert(bit_pos / 8 < vec.size()); + + vec[bit_pos / 8] |= bit << (bit_pos % 8); +} + +static std::vector xor_vectors(const std::vector &vec_a, + const std::vector &vec_b) { + assert(vec_a.size() == vec_b.size()); + + std::vector vec_out(vec_a.size()); + + std::transform(vec_a.begin(), vec_a.end(), vec_b.begin(), vec_out.begin(), + std::bit_xor{}); + + return vec_out; +} + +// Run each 4-bit chunk of bytes from `in` through the SBOX. Where `bit_width` +// isn't a multiple of 4 the remaining bits are just copied straight through. +// `invert` choose whether to use the inverted SBOX or not. +static std::vector scramble_sbox_layer(const std::vector &in, + uint32_t bit_width, + uint8_t sbox[16]) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + // Iterate through each 4 bit chunk of the data and apply the appropriate SBOX + for (uint32_t i = 0; i < bit_width / 4; ++i) { + uint8_t sbox_in, sbox_out; + + sbox_in = in[i / 2]; + + int shift = (i % 2) ? 4 : 0; + sbox_in = (sbox_in >> shift) & 0xf; + + sbox_out = sbox[sbox_in]; + + out[i / 2] |= sbox_out << shift; + } + + // Where bit_width is not a multiple of 4 copy over the remaining bits + if (bit_width % 4) { + int shift = ((bit_width % 8) >= 4) ? 4 : 0; + uint8_t nibble = (in[bit_width / 8] >> shift) & 0xf; + out[bit_width / 8] |= nibble << shift; + } + + return out; +} + +// Reverse bits from incoming byte vector +static std::vector scramble_flip_layer(const std::vector &in, + uint32_t bit_width) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + for (uint32_t i = 0; i < bit_width; ++i) { + or_vector_bit(out, bit_width - i - 1, read_vector_bit(in, i)); + } + + return out; +} + +// Apply butterfly to incoming byte vector. Even bits are placed in the lower +// half of the output, odd bits are placed in the upper half of the output. +static std::vector scramble_perm_layer(const std::vector &in, + uint32_t bit_width, + bool invert) { + assert(in.size() == ((bit_width + 7) / 8)); + std::vector out(in.size(), 0); + + for (uint32_t i = 0; i < bit_width / 2; ++i) { + if (invert) { + or_vector_bit(out, i * 2, read_vector_bit(in, i)); + or_vector_bit(out, i * 2 + 1, read_vector_bit(in, i + (bit_width / 2))); + } else { + or_vector_bit(out, i, read_vector_bit(in, i * 2)); + or_vector_bit(out, i + (bit_width / 2), read_vector_bit(in, i * 2 + 1)); + } + } + + if (bit_width % 2) { + // Where bit_width isn't even, the final bit is copied across to the same + // position + or_vector_bit(out, bit_width - 1, read_vector_bit(in, bit_width - 1)); + } + + return out; +} + +// Apply a full set of subsitution/permutation rounds for encrypt to the +// incoming byte vector +static std::vector scramble_subst_perm_enc( + const std::vector &in, const std::vector &key, + uint32_t bit_width, uint32_t num_rounds) { + assert(in.size() == ((bit_width + 7) / 8)); + assert(key.size() == ((bit_width + 7) / 8)); + + std::vector state(in); + + for (uint32_t i = 0; i < num_rounds; ++i) { + state = xor_vectors(state, key); + + state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4); + state = scramble_flip_layer(state, bit_width); + state = scramble_perm_layer(state, bit_width, false); + } + + state = xor_vectors(state, key); + + return state; +} + +// Apply a full set of substitution/permutation rounds for decrypt to the +// incoming byte vector +static std::vector scramble_subst_perm_dec( + const std::vector &in, const std::vector &key, + uint32_t bit_width, uint32_t num_rounds) { + assert(in.size() == ((bit_width + 7) / 8)); + assert(key.size() == ((bit_width + 7) / 8)); + + std::vector state(in); + + for (uint32_t i = 0; i < num_rounds; ++i) { + state = xor_vectors(state, key); + + state = scramble_perm_layer(state, bit_width, true); + state = scramble_flip_layer(state, bit_width); + state = scramble_sbox_layer(state, bit_width, PRESENT_SBOX4_INV); + } + + state = xor_vectors(state, key); + + return state; +} + +// Generate a keystream for XORing with data using PRINCE. +// If repeat_keystream is set to true, the output from one PRINCE instance is +// repeated when the keystream is greater than a single PRINCE width (64bit). +// Otherwise, multiple PRINCEs are instantiated to form the keystream. +static std::vector scramble_gen_keystream( + const std::vector &addr, uint32_t addr_width, + const std::vector &nonce, const std::vector &key, + uint32_t keystream_width, uint32_t num_half_rounds, bool repeat_keystream) { + assert(key.size() == (kPrinceWidthByte * 2)); + + // Determine how many PRINCE replications are required + uint32_t num_princes, num_repetitions; + if (repeat_keystream) { + num_princes = 1; + num_repetitions = (keystream_width + kPrinceWidth - 1) / kPrinceWidth; + } else { + num_princes = (keystream_width + kPrinceWidth - 1) / kPrinceWidth; + num_repetitions = 1; + } + + std::vector keystream; + + for (uint32_t i = 0; i < num_princes; ++i) { + // Initial vector is data for PRINCE to encrypt. Formed from nonce and data + // address + std::vector iv(8, 0); + + for (uint32_t j = 0; j < kPrinceWidth; ++j) { + if (j < addr_width) { + // Bottom addr_width bits of IV are address + or_vector_bit(iv, j, read_vector_bit(addr, j)); + } else { + // Other bits are taken from nonce. Each PRINCE instantiation will use + // different nonce bits. + int nonce_bit = (j - addr_width) + i * (kPrinceWidth - addr_width); + or_vector_bit(iv, j, read_vector_bit(nonce, nonce_bit)); + } + } + + // PRINCE C reference model works on big-endian byte order + iv = byte_reverse_vector(iv); + auto key_be = byte_reverse_vector(key); + + // Apply PRINCE to IV to produce keystream + std::vector keystream_block(kPrinceWidthByte); + prince_enc_dec(&iv[0], &key_be[0], &keystream_block[0], 0, num_half_rounds, + 0); + + // Flip keystream into little endian order and add to keystream vector + keystream_block = byte_reverse_vector(keystream_block); + // Repeat the output of a single PRINCE instance if needed + for (uint32_t k = 0; k < num_repetitions; ++k) { + keystream.insert(keystream.end(), keystream_block.begin(), + keystream_block.end()); + } + } + + // Total keystream bits generated are some multiple of kPrinceWidth. This can + // result in unused keystream bits. Remove the unused bytes from the keystream + // vector and zero out top unused bits in the final byte if required. + uint32_t keystream_bytes = (keystream_width + 7) / 8; + uint32_t keystream_bytes_to_erase = keystream.size() - keystream_bytes; + if (keystream_bytes_to_erase) { + keystream.erase(keystream.end() - keystream_bytes_to_erase, + keystream.end()); + } + + if (keystream_width % 8) { + keystream[keystream.size() - 1] &= (1 << (keystream_width % 8)) - 1; + } + + return keystream; +} + +// Split incoming data into subst_perm_width chunks and individually apply the +// substitution/permutation layer to each +static std::vector scramble_subst_perm_full_width( + const std::vector &in, uint32_t bit_width, + uint32_t subst_perm_width, bool enc) { + assert(in.size() == ((bit_width + 7) / 8)); + + // Determine how many bytes each subst_perm_width chunk is and how many + // chunks are needed to cover the full bit_width. + uint32_t subst_perm_bytes = (subst_perm_width + 7) / 8; + uint32_t subst_perm_blocks = + (bit_width + subst_perm_width - 1) / subst_perm_width; + + std::vector out(in.size(), 0); + std::vector zero_key(subst_perm_bytes, 0); + + auto sp_scrambler = enc ? scramble_subst_perm_enc : scramble_subst_perm_dec; + + for (uint32_t i = 0; i < subst_perm_blocks; ++i) { + // Where bit_width does not evenly divide into subst_perm_width the + // final block is smaller. + uint32_t bits_so_far = subst_perm_width * i; + uint32_t block_width = std::min(subst_perm_width, bit_width - bits_so_far); + + std::vector subst_perm_data(subst_perm_bytes, 0); + + // Extract bits from in for this chunk + for (uint32_t j = 0; j < block_width; ++j) { + or_vector_bit(subst_perm_data, j, + read_vector_bit(in, j + i * subst_perm_width)); + } + + // Apply the substitution/permutation layer to the chunk + auto subst_perm_out = sp_scrambler(subst_perm_data, zero_key, block_width, + kNumDataSubstPermRounds); + + // Write the result to the `out` vector + for (uint32_t j = 0; j < block_width; ++j) { + or_vector_bit(out, j + i * subst_perm_width, + read_vector_bit(subst_perm_out, j)); + } + } + + return out; +} + +std::vector scramble_addr(const std::vector &addr_in, + uint32_t addr_width, + const std::vector &nonce, + uint32_t nonce_width) { + assert(addr_in.size() == ((addr_width + 7) / 8)); + + std::vector addr_enc_nonce(addr_in.size(), 0); + + // Address is scrambled by using substitution/permutation layer with the nonce + // used as a key. + // Extract relevant nonce bits for key + for (uint32_t i = 0; i < addr_width; ++i) { + or_vector_bit(addr_enc_nonce, i, + read_vector_bit(nonce, nonce_width - addr_width + i)); + } + + // Apply substitution/permutation layer + return scramble_subst_perm_enc(addr_in, addr_enc_nonce, addr_width, + kNumAddrSubstPermRounds); +} + +std::vector scramble_encrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream, bool use_sp_layer) { + assert(data_in.size() == ((data_width + 7) / 8)); + assert(addr.size() == ((addr_width + 7) / 8)); + + // Data is encrypted by XORing with keystream then applying + // substitution/permutation layer + + auto keystream = + scramble_gen_keystream(addr, addr_width, nonce, key, data_width, + kNumPrinceHalfRounds, repeat_keystream); + + auto data_enc = xor_vectors(data_in, keystream); + + if (use_sp_layer) { + return scramble_subst_perm_full_width(data_enc, data_width, + subst_perm_width, true); + } else { + return data_enc; + } +} + +std::vector scramble_decrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream, bool use_sp_layer) { + assert(data_in.size() == ((data_width + 7) / 8)); + assert(addr.size() == ((addr_width + 7) / 8)); + + auto keystream = + scramble_gen_keystream(addr, addr_width, nonce, key, data_width, + kNumPrinceHalfRounds, repeat_keystream); + if (use_sp_layer) { + // Data is decrypted by reversing substitution/permutation layer then XORing + // with keystream + auto data_sp_out = scramble_subst_perm_full_width(data_in, data_width, + subst_perm_width, false); + return xor_vectors(data_sp_out, keystream); + } else { + return xor_vectors(data_in, keystream); + } +} diff --git a/synth/prim/dv/prim_ram_scr/cpp/scramble_model.core b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.core new file mode 100755 index 0000000..2ab96b8 --- /dev/null +++ b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.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:dv:scramble_model" +description: "Memory scrambling C++ model" +filesets: + files_cpp: + depend: + - lowrisc:dv:crypto_prince_ref + files: + - scramble_model.cc + - scramble_model.h: { is_include_file: true } + file_type: cppSource + +targets: + default: + filesets: + - files_cpp + tools: + vcs: + vcs_options: + - '-CFLAGS -I../../src/lowrisc_dv_scramble_model_0' diff --git a/synth/prim/dv/prim_ram_scr/cpp/scramble_model.h b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.h new file mode 100755 index 0000000..0092cfa --- /dev/null +++ b/synth/prim/dv/prim_ram_scr/cpp/scramble_model.h @@ -0,0 +1,79 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ +#define OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ + +#include +#include + +const uint32_t kPrinceWidth = 64; +const uint32_t kPrinceWidthByte = kPrinceWidth / 8; + +// C++ model of memory scrambling. All byte vectors are in little endian byte +// order (least significant byte at index 0). + +/** Scramble an address to give the physical address used to access the + * scrambled memory. Return vector of scrambled address bytes + * + * @param addr_in Byte vector of address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param nonce_width Width of scramble nonce in bits + * @return Byte vector with scrambled address + */ +std::vector scramble_addr(const std::vector &addr_in, + uint32_t addr_width, + const std::vector &nonce, + uint32_t nonce_width); + +/** Decrypt scrambled data + * @param data_in Byte vector of data to decrypt + * @param data_width Width of data in bits + * @param subst_perm_width Width over which the substitution/permutation network + * is applied (DiffWidth parameter on prim_ram_1p_scr) + * @param addr Byte vector of data address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param key Byte vector of scrambling key + * @param repeat_keystream Repeat the keystream of one single PRINCE instance if + * set to true. Otherwise multiple PRINCE instances are + * used. + * @param use_sp_layer Use the S&P layer for data diffusion. In HW this is + * disabled by default since it interacts adversely with + * the end-to-end integrity scheme. See #20788 for + * context. + * @return Byte vector with decrypted data + */ +std::vector scramble_decrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream, bool use_sp_layer); + +/** Encrypt scrambled data + * @param data_in Byte vector of data to encrypt + * @param data_width Width of data in bits + * @param subst_perm_width Width over which the substitution/permutation network + * is applied (DiffWidth parameter on prim_ram_1p_scr) + * @param addr Byte vector of data address + * @param addr_width Width of the address in bits + * @param nonce Byte vector of scrambling nonce + * @param key Byte vector of scrambling key + * @param repeat_keystream Repeat the keystream of one single PRINCE instance if + * set to true. Otherwise multiple PRINCE instances are + * used. + * @param use_sp_layer Use the S&P layer for data diffusion. In HW this is + * disabled by default since it interacts adversely with + * the end-to-end integrity scheme. See #20788 for + * context. + * @return Byte vector with encrypted data + */ +std::vector scramble_encrypt_data( + const std::vector &data_in, uint32_t data_width, + uint32_t subst_perm_width, const std::vector &addr, + uint32_t addr_width, const std::vector &nonce, + const std::vector &key, bool repeat_keystream, bool use_sp_layer); + +#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_RAM_SCR_CPP_SCRAMBLE_MODEL_H_ diff --git a/synth/prim/dv/prim_secded/secded_enc.c b/synth/prim/dv/prim_secded/secded_enc.c new file mode 100755 index 0000000..e61909f --- /dev/null +++ b/synth/prim/dv/prim_secded/secded_enc.c @@ -0,0 +1,160 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encode code generated by +// util/design/secded_gen.py from util/design/data/secded_cfg.hjson + +#include "secded_enc.h" + +#include +#include + +// Calculates even parity for a 64-bit word +static uint8_t calc_parity(uint64_t word, bool invert) { + bool parity = false; + + while (word) { + if (word & 1) { + parity = !parity; + } + + word >>= 1; + } + + return parity ^ invert; +} + +uint8_t enc_secded_22_16(const uint8_t bytes[2]) { + uint16_t word = ((uint16_t)bytes[0] << 0) | ((uint16_t)bytes[1] << 8); + + return (calc_parity(word & 0x496e, false) << 0) | + (calc_parity(word & 0xf20b, false) << 1) | + (calc_parity(word & 0x8ed8, false) << 2) | + (calc_parity(word & 0x7714, false) << 3) | + (calc_parity(word & 0xaca5, false) << 4) | + (calc_parity(word & 0x11f3, false) << 5); +} + +uint8_t enc_secded_28_22(const uint8_t bytes[3]) { + uint32_t word = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16); + + return (calc_parity(word & 0x3003ff, false) << 0) | + (calc_parity(word & 0x10fc0f, false) << 1) | + (calc_parity(word & 0x271c71, false) << 2) | + (calc_parity(word & 0x3b6592, false) << 3) | + (calc_parity(word & 0x3daaa4, false) << 4) | + (calc_parity(word & 0x3ed348, false) << 5); +} + +uint8_t enc_secded_39_32(const uint8_t bytes[4]) { + uint32_t word = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); + + return (calc_parity(word & 0x2606bd25, false) << 0) | + (calc_parity(word & 0xdeba8050, false) << 1) | + (calc_parity(word & 0x413d89aa, false) << 2) | + (calc_parity(word & 0x31234ed1, false) << 3) | + (calc_parity(word & 0xc2c1323b, false) << 4) | + (calc_parity(word & 0x2dcc624c, false) << 5) | + (calc_parity(word & 0x98505586, false) << 6); +} + +uint8_t enc_secded_64_57(const uint8_t bytes[8]) { + uint64_t word = ((uint64_t)bytes[0] << 0) | ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); + + return (calc_parity(word & 0x103fff800007fff, false) << 0) | + (calc_parity(word & 0x17c1ff801ff801f, false) << 1) | + (calc_parity(word & 0x1bde1f87e0781e1, false) << 2) | + (calc_parity(word & 0x1deee3b8e388e22, false) << 3) | + (calc_parity(word & 0x1ef76cdb2c93244, false) << 4) | + (calc_parity(word & 0x1f7bb56d5525488, false) << 5) | + (calc_parity(word & 0x1fbdda769a46910, false) << 6); +} + +uint8_t enc_secded_72_64(const uint8_t bytes[8]) { + uint64_t word = ((uint64_t)bytes[0] << 0) | ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); + + return (calc_parity(word & 0xb9000000001fffff, false) << 0) | + (calc_parity(word & 0x5e00000fffe0003f, false) << 1) | + (calc_parity(word & 0x67003ff003e007c1, false) << 2) | + (calc_parity(word & 0xcd0fc0f03c207842, false) << 3) | + (calc_parity(word & 0xb671c711c4438884, false) << 4) | + (calc_parity(word & 0xb5b65926488c9108, false) << 5) | + (calc_parity(word & 0xcbdaaa4a91152210, false) << 6) | + (calc_parity(word & 0x7aed348d221a4420, false) << 7); +} + +uint8_t enc_secded_inv_22_16(const uint8_t bytes[2]) { + uint16_t word = ((uint16_t)bytes[0] << 0) | ((uint16_t)bytes[1] << 8); + + return (calc_parity(word & 0x496e, false) << 0) | + (calc_parity(word & 0xf20b, true) << 1) | + (calc_parity(word & 0x8ed8, false) << 2) | + (calc_parity(word & 0x7714, true) << 3) | + (calc_parity(word & 0xaca5, false) << 4) | + (calc_parity(word & 0x11f3, true) << 5); +} + +uint8_t enc_secded_inv_28_22(const uint8_t bytes[3]) { + uint32_t word = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16); + + return (calc_parity(word & 0x3003ff, false) << 0) | + (calc_parity(word & 0x10fc0f, true) << 1) | + (calc_parity(word & 0x271c71, false) << 2) | + (calc_parity(word & 0x3b6592, true) << 3) | + (calc_parity(word & 0x3daaa4, false) << 4) | + (calc_parity(word & 0x3ed348, true) << 5); +} + +uint8_t enc_secded_inv_39_32(const uint8_t bytes[4]) { + uint32_t word = ((uint32_t)bytes[0] << 0) | ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | ((uint32_t)bytes[3] << 24); + + return (calc_parity(word & 0x2606bd25, false) << 0) | + (calc_parity(word & 0xdeba8050, true) << 1) | + (calc_parity(word & 0x413d89aa, false) << 2) | + (calc_parity(word & 0x31234ed1, true) << 3) | + (calc_parity(word & 0xc2c1323b, false) << 4) | + (calc_parity(word & 0x2dcc624c, true) << 5) | + (calc_parity(word & 0x98505586, false) << 6); +} + +uint8_t enc_secded_inv_64_57(const uint8_t bytes[8]) { + uint64_t word = ((uint64_t)bytes[0] << 0) | ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); + + return (calc_parity(word & 0x103fff800007fff, false) << 0) | + (calc_parity(word & 0x17c1ff801ff801f, true) << 1) | + (calc_parity(word & 0x1bde1f87e0781e1, false) << 2) | + (calc_parity(word & 0x1deee3b8e388e22, true) << 3) | + (calc_parity(word & 0x1ef76cdb2c93244, false) << 4) | + (calc_parity(word & 0x1f7bb56d5525488, true) << 5) | + (calc_parity(word & 0x1fbdda769a46910, false) << 6); +} + +uint8_t enc_secded_inv_72_64(const uint8_t bytes[8]) { + uint64_t word = ((uint64_t)bytes[0] << 0) | ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | ((uint64_t)bytes[7] << 56); + + return (calc_parity(word & 0xb9000000001fffff, false) << 0) | + (calc_parity(word & 0x5e00000fffe0003f, true) << 1) | + (calc_parity(word & 0x67003ff003e007c1, false) << 2) | + (calc_parity(word & 0xcd0fc0f03c207842, true) << 3) | + (calc_parity(word & 0xb671c711c4438884, false) << 4) | + (calc_parity(word & 0xb5b65926488c9108, true) << 5) | + (calc_parity(word & 0xcbdaaa4a91152210, false) << 6) | + (calc_parity(word & 0x7aed348d221a4420, true) << 7); +} diff --git a/synth/prim/dv/prim_secded/secded_enc.core b/synth/prim/dv/prim_secded/secded_enc.core new file mode 100755 index 0000000..702da67 --- /dev/null +++ b/synth/prim/dv/prim_secded/secded_enc.core @@ -0,0 +1,22 @@ +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:secded_enc" +description: "Hsiao SECDED encode reference C implementation" +filesets: + files_dv: + files: + - secded_enc.h: {is_include_file: true} + - secded_enc.c + file_type: cSource + +targets: + default: + filesets: + - files_dv + tools: + vcs: + vcs_options: + - '-CFLAGS -I../../src/lowrisc_dv_secded_enc_0' diff --git a/synth/prim/dv/prim_secded/secded_enc.h b/synth/prim/dv/prim_secded/secded_enc.h new file mode 100755 index 0000000..776dbc8 --- /dev/null +++ b/synth/prim/dv/prim_secded/secded_enc.h @@ -0,0 +1,36 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED encode code generated by +// util/design/secded_gen.py from util/design/data/secded_cfg.hjson + +#ifndef OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ +#define OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Integrity encode functions for varying bit widths matching the functionality +// of the RTL modules of the same name. Each takes an array of bytes in +// little-endian order and returns the calculated integrity bits. + +uint8_t enc_secded_22_16(const uint8_t bytes[2]); +uint8_t enc_secded_28_22(const uint8_t bytes[3]); +uint8_t enc_secded_39_32(const uint8_t bytes[4]); +uint8_t enc_secded_64_57(const uint8_t bytes[8]); +uint8_t enc_secded_72_64(const uint8_t bytes[8]); +uint8_t enc_secded_inv_22_16(const uint8_t bytes[2]); +uint8_t enc_secded_inv_28_22(const uint8_t bytes[3]); +uint8_t enc_secded_inv_39_32(const uint8_t bytes[4]); +uint8_t enc_secded_inv_64_57(const uint8_t bytes[8]); +uint8_t enc_secded_inv_72_64(const uint8_t bytes[8]); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // OPENTITAN_HW_IP_PRIM_DV_PRIM_SECDED_SECDED_ENC_H_ diff --git a/synth/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core b/synth/prim/fpv/prim_alert_rxtx_async_fatal_fpv.core new file mode 100755 index 0000000..677a3fe --- /dev/null +++ b/synth/prim/fpv/prim_alert_rxtx_async_fatal_fpv.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:fpv:prim_alert_rxtx_async_fatal_fpv:0.1" +description: "ALERT_HANDLER rxtx async fatal FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:alert + files: + - vip/prim_alert_rxtx_async_assert_fpv.sv + - tb/prim_alert_rxtx_async_fatal_tb.sv + - tb/prim_alert_rxtx_async_fatal_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_fatal_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_alert_rxtx_async_fpv.core b/synth/prim/fpv/prim_alert_rxtx_async_fpv.core new file mode 100755 index 0000000..c1907fe --- /dev/null +++ b/synth/prim/fpv/prim_alert_rxtx_async_fpv.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:fpv:prim_alert_rxtx_async_fpv:0.1" +description: "ALERT_HANDLER rxtx async FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:alert + files: + - vip/prim_alert_rxtx_async_assert_fpv.sv + - tb/prim_alert_rxtx_async_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_alert_rxtx_fatal_fpv.core b/synth/prim/fpv/prim_alert_rxtx_fatal_fpv.core new file mode 100755 index 0000000..28a286c --- /dev/null +++ b/synth/prim/fpv/prim_alert_rxtx_fatal_fpv.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:fpv:prim_alert_rxtx_fatal_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:alert + files: + - vip/prim_alert_rxtx_assert_fpv.sv + - tb/prim_alert_rxtx_fatal_tb.sv + - tb/prim_alert_rxtx_fatal_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_fatal_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_alert_rxtx_fpv.core b/synth/prim/fpv/prim_alert_rxtx_fpv.core new file mode 100755 index 0000000..8c87903 --- /dev/null +++ b/synth/prim/fpv/prim_alert_rxtx_fpv.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:fpv:prim_alert_rxtx_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:alert + files: + - vip/prim_alert_rxtx_assert_fpv.sv + - tb/prim_alert_rxtx_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_arbiter_fixed_fpv.core b/synth/prim/fpv/prim_arbiter_fixed_fpv.core new file mode 100755 index 0000000..e133422 --- /dev/null +++ b/synth/prim/fpv/prim_arbiter_fixed_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_fixed_fpv:0.1" +description: "prim_arbiter_fixed FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - tb/prim_arbiter_fixed_tb.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_fixed_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_arbiter_ppc_fpv.core b/synth/prim/fpv/prim_arbiter_ppc_fpv.core new file mode 100755 index 0000000..48c8835 --- /dev/null +++ b/synth/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 + files: + - tb/prim_arbiter_ppc_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_arbiter_tree_fpv.core b/synth/prim/fpv/prim_arbiter_tree_fpv.core new file mode 100755 index 0000000..684d5c3 --- /dev/null +++ b/synth/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 + files: + - tb/prim_arbiter_tree_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_count_expected_failure.hjson b/synth/prim/fpv/prim_count_expected_failure.hjson new file mode 100755 index 0000000..30df389 --- /dev/null +++ b/synth/prim/fpv/prim_count_expected_failure.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 +// +// These two assertions are unreachable in the prim_count environment unless the counters are +// forced to have different output. +{ + unreachable: + [ + prim_count_tb.u_counter.CntErrForward_A:precondition1 + prim_count_tb.u_counter.CntErrBackward_A:precondition1 + ] +} diff --git a/synth/prim/fpv/prim_count_fpv.core b/synth/prim/fpv/prim_count_fpv.core new file mode 100755 index 0000000..dcd2bde --- /dev/null +++ b/synth/prim/fpv/prim_count_fpv.core @@ -0,0 +1,29 @@ +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_count_fpv:0.1" +description: "prim_count FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:count + files: + - tb/prim_count_tb.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_count_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_esc_rxtx_fpv.core b/synth/prim/fpv/prim_esc_rxtx_fpv.core new file mode 100755 index 0000000..7538ca3 --- /dev/null +++ b/synth/prim/fpv/prim_esc_rxtx_fpv.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:fpv:prim_esc_rxtx_fpv:0.1" +description: "ALERT_HANDLER FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:esc + files: + - vip/prim_esc_rxtx_assert_fpv.sv + - tb/prim_esc_rxtx_bind_fpv.sv + - tb/prim_esc_rxtx_tb.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_esc_rxtx_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_fifo_async_sram_adapter_fpv.core b/synth/prim/fpv/prim_fifo_async_sram_adapter_fpv.core new file mode 100755 index 0000000..4c8c924 --- /dev/null +++ b/synth/prim/fpv/prim_fifo_async_sram_adapter_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_async_sram_adapter_fpv:0.1" +description: "prim_fifo_async_sram_adapter FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:ram_2p_async_adv + files: + - tb/prim_fifo_async_sram_adapter_tb.sv + file_type: systemVerilogSource + +targets: + default: &default_target + default_tool: icarus + filesets: + - files_formal + toplevel: prim_fifo_async_sram_adapter_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_fifo_sync_fpv.core b/synth/prim/fpv/prim_fifo_sync_fpv.core new file mode 100755 index 0000000..d3f1baa --- /dev/null +++ b/synth/prim/fpv/prim_fifo_sync_fpv.core @@ -0,0 +1,30 @@ +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_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_keccak_fpv.core b/synth/prim/fpv/prim_keccak_fpv.core new file mode 100755 index 0000000..b60df13 --- /dev/null +++ b/synth/prim/fpv/prim_keccak_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_keccak_fpv:0.1" +description: "Keccak_f FPV target" +filesets: + files_fpv: + depend: + - lowrisc:prim:all + files: + - tb/prim_keccak_tb.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_fpv + toplevel: + - prim_keccak_tb + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_lfsr_fpv.core b/synth/prim/fpv/prim_lfsr_fpv.core new file mode 100755 index 0000000..824f212 --- /dev/null +++ b/synth/prim/fpv/prim_lfsr_fpv.core @@ -0,0 +1,29 @@ +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: "LFSR primitive FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:lfsr + files: + - tb/prim_lfsr_tb.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_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_packer_fpv.core b/synth/prim/fpv/prim_packer_fpv.core new file mode 100755 index 0000000..3e147f7 --- /dev/null +++ b/synth/prim/fpv/prim_packer_fpv.core @@ -0,0 +1,29 @@ +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_packer_fpv" +description: "formal Testbench for prim_packer" +filesets: + files_formal: + depend: + - lowrisc:prim:all + files: + - tb/prim_packer_tb.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_packer_tb + + formal: + <<: *default_target + + lint: + <<: *default_target diff --git a/synth/prim/fpv/prim_secded_22_16_fpv.core b/synth/prim/fpv/prim_secded_22_16_fpv.core new file mode 100755 index 0000000..5d9ab39 --- /dev/null +++ b/synth/prim/fpv/prim_secded_22_16_fpv.core @@ -0,0 +1,33 @@ +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_secded_22_16_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_22_16_assert_fpv.sv + - tb/prim_secded_22_16_tb.sv + - tb/prim_secded_22_16_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_secded_22_16_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_28_22_fpv.core b/synth/prim/fpv/prim_secded_28_22_fpv.core new file mode 100755 index 0000000..97f4f4e --- /dev/null +++ b/synth/prim/fpv/prim_secded_28_22_fpv.core @@ -0,0 +1,33 @@ +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_secded_28_22_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_28_22_assert_fpv.sv + - tb/prim_secded_28_22_tb.sv + - tb/prim_secded_28_22_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_secded_28_22_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_39_32_fpv.core b/synth/prim/fpv/prim_secded_39_32_fpv.core new file mode 100755 index 0000000..c239151 --- /dev/null +++ b/synth/prim/fpv/prim_secded_39_32_fpv.core @@ -0,0 +1,33 @@ +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_secded_39_32_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_39_32_assert_fpv.sv + - tb/prim_secded_39_32_tb.sv + - tb/prim_secded_39_32_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_secded_39_32_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_64_57_fpv.core b/synth/prim/fpv/prim_secded_64_57_fpv.core new file mode 100755 index 0000000..2ab02bc --- /dev/null +++ b/synth/prim/fpv/prim_secded_64_57_fpv.core @@ -0,0 +1,33 @@ +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_secded_64_57_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_64_57_assert_fpv.sv + - tb/prim_secded_64_57_tb.sv + - tb/prim_secded_64_57_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_secded_64_57_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_72_64_fpv.core b/synth/prim/fpv/prim_secded_72_64_fpv.core new file mode 100755 index 0000000..cc5aeb8 --- /dev/null +++ b/synth/prim/fpv/prim_secded_72_64_fpv.core @@ -0,0 +1,33 @@ +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_secded_72_64_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_72_64_assert_fpv.sv + - tb/prim_secded_72_64_tb.sv + - tb/prim_secded_72_64_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_secded_72_64_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_hamming_22_16_fpv.core b/synth/prim/fpv/prim_secded_hamming_22_16_fpv.core new file mode 100755 index 0000000..4910804 --- /dev/null +++ b/synth/prim/fpv/prim_secded_hamming_22_16_fpv.core @@ -0,0 +1,33 @@ +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_secded_hamming_22_16_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_hamming_22_16_assert_fpv.sv + - tb/prim_secded_hamming_22_16_tb.sv + - tb/prim_secded_hamming_22_16_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_secded_hamming_22_16_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_hamming_39_32_fpv.core b/synth/prim/fpv/prim_secded_hamming_39_32_fpv.core new file mode 100755 index 0000000..b73f22b --- /dev/null +++ b/synth/prim/fpv/prim_secded_hamming_39_32_fpv.core @@ -0,0 +1,33 @@ +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_secded_hamming_39_32_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_hamming_39_32_assert_fpv.sv + - tb/prim_secded_hamming_39_32_tb.sv + - tb/prim_secded_hamming_39_32_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_secded_hamming_39_32_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_hamming_72_64_fpv.core b/synth/prim/fpv/prim_secded_hamming_72_64_fpv.core new file mode 100755 index 0000000..5f9f102 --- /dev/null +++ b/synth/prim/fpv/prim_secded_hamming_72_64_fpv.core @@ -0,0 +1,33 @@ +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_secded_hamming_72_64_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_hamming_72_64_assert_fpv.sv + - tb/prim_secded_hamming_72_64_tb.sv + - tb/prim_secded_hamming_72_64_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_secded_hamming_72_64_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_hamming_76_68_fpv.core b/synth/prim/fpv/prim_secded_hamming_76_68_fpv.core new file mode 100755 index 0000000..0ea18cb --- /dev/null +++ b/synth/prim/fpv/prim_secded_hamming_76_68_fpv.core @@ -0,0 +1,33 @@ +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_secded_hamming_76_68_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_hamming_76_68_assert_fpv.sv + - tb/prim_secded_hamming_76_68_tb.sv + - tb/prim_secded_hamming_76_68_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_secded_hamming_76_68_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_22_16_fpv.core b/synth/prim/fpv/prim_secded_inv_22_16_fpv.core new file mode 100755 index 0000000..4094c00 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_22_16_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_22_16_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_22_16_assert_fpv.sv + - tb/prim_secded_inv_22_16_tb.sv + - tb/prim_secded_inv_22_16_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_secded_inv_22_16_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_28_22_fpv.core b/synth/prim/fpv/prim_secded_inv_28_22_fpv.core new file mode 100755 index 0000000..d68bb35 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_28_22_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_28_22_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_28_22_assert_fpv.sv + - tb/prim_secded_inv_28_22_tb.sv + - tb/prim_secded_inv_28_22_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_secded_inv_28_22_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_39_32_fpv.core b/synth/prim/fpv/prim_secded_inv_39_32_fpv.core new file mode 100755 index 0000000..07df9f1 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_39_32_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_39_32_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_39_32_assert_fpv.sv + - tb/prim_secded_inv_39_32_tb.sv + - tb/prim_secded_inv_39_32_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_secded_inv_39_32_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_64_57_fpv.core b/synth/prim/fpv/prim_secded_inv_64_57_fpv.core new file mode 100755 index 0000000..d5d7f57 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_64_57_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_64_57_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_64_57_assert_fpv.sv + - tb/prim_secded_inv_64_57_tb.sv + - tb/prim_secded_inv_64_57_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_secded_inv_64_57_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_72_64_fpv.core b/synth/prim/fpv/prim_secded_inv_72_64_fpv.core new file mode 100755 index 0000000..755eaf0 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_72_64_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_72_64_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_72_64_assert_fpv.sv + - tb/prim_secded_inv_72_64_tb.sv + - tb/prim_secded_inv_72_64_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_secded_inv_72_64_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_hamming_22_16_fpv.core b/synth/prim/fpv/prim_secded_inv_hamming_22_16_fpv.core new file mode 100755 index 0000000..9ab87ff --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_hamming_22_16_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_hamming_22_16_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_hamming_22_16_assert_fpv.sv + - tb/prim_secded_inv_hamming_22_16_tb.sv + - tb/prim_secded_inv_hamming_22_16_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_secded_inv_hamming_22_16_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_hamming_39_32_fpv.core b/synth/prim/fpv/prim_secded_inv_hamming_39_32_fpv.core new file mode 100755 index 0000000..786a289 --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_hamming_39_32_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_hamming_39_32_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_hamming_39_32_assert_fpv.sv + - tb/prim_secded_inv_hamming_39_32_tb.sv + - tb/prim_secded_inv_hamming_39_32_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_secded_inv_hamming_39_32_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_hamming_72_64_fpv.core b/synth/prim/fpv/prim_secded_inv_hamming_72_64_fpv.core new file mode 100755 index 0000000..08ef86a --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_hamming_72_64_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_hamming_72_64_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_hamming_72_64_assert_fpv.sv + - tb/prim_secded_inv_hamming_72_64_tb.sv + - tb/prim_secded_inv_hamming_72_64_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_secded_inv_hamming_72_64_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/prim_secded_inv_hamming_76_68_fpv.core b/synth/prim/fpv/prim_secded_inv_hamming_76_68_fpv.core new file mode 100755 index 0000000..750f67a --- /dev/null +++ b/synth/prim/fpv/prim_secded_inv_hamming_76_68_fpv.core @@ -0,0 +1,33 @@ +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_secded_inv_hamming_76_68_fpv:0.1" +description: "SECDED FPV target" +filesets: + files_formal: + depend: + - lowrisc:prim:all + - lowrisc:prim:secded + files: + - vip/prim_secded_inv_hamming_76_68_assert_fpv.sv + - tb/prim_secded_inv_hamming_76_68_tb.sv + - tb/prim_secded_inv_hamming_76_68_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_secded_inv_hamming_76_68_tb + + formal: + <<: *default_target + + lint: + <<: *default_target + diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv b/synth/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv new file mode 100755 index 0000000..db33bf2 --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_async_bind_fpv.sv @@ -0,0 +1,32 @@ +// 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_test_i, + .init_trig_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .ping_req_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_async_bind_fpv diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv b/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv new file mode 100755 index 0000000..3a66f27 --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_bind_fpv.sv @@ -0,0 +1,32 @@ +// 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_fatal_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_test_i, + .init_trig_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .ping_req_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_async_fatal_bind_fpv diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_tb.sv b/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_tb.sv new file mode 100755 index 0000000..267a0ab --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_async_fatal_tb.sv @@ -0,0 +1,119 @@ +// 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_fatal_tb + import prim_alert_pkg::*; + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input alert_req_i, + input mubi4_t init_trig_i, + input ping_req_i, + output logic alert_ack_o, + output logic alert_state_o, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // asynchronous case + localparam bit AsyncOn = 1'b1; + localparam bit IsFatal = 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 ), + .IsFatal ( IsFatal ) + ) i_prim_alert_sender ( + .clk_i, + .rst_ni, + .alert_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .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, + .init_trig_i, + .ping_req_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_fatal_tb diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_async_tb.sv b/synth/prim/fpv/tb/prim_alert_rxtx_async_tb.sv new file mode 100755 index 0000000..0327985 --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_async_tb.sv @@ -0,0 +1,120 @@ +// 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_tb + import prim_alert_pkg::*; + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input alert_req_i, + input mubi4_t init_trig_i, + input ping_req_i, + output logic alert_ack_o, + output logic alert_state_o, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // asynchronous case + localparam bit AsyncOn = 1'b1; + localparam bit IsFatal = 1'b0; + + 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 ), + .IsFatal ( IsFatal ) + ) i_prim_alert_sender ( + .clk_i, + .rst_ni, + .alert_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .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, + .init_trig_i, + .ping_req_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_tb diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv b/synth/prim/fpv/tb/prim_alert_rxtx_bind_fpv.sv new file mode 100755 index 0000000..f3ac2ef --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_bind_fpv.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 +// + +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_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .init_trig_i, + .ping_req_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_bind_fpv diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv b/synth/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.sv new file mode 100755 index 0000000..c3f33b6 --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_fatal_bind_fpv.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 +// + +module prim_alert_rxtx_fatal_bind_fpv; + + // this reuses the synchronous VIP. + 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_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .init_trig_i, + .ping_req_i, + .ping_ok_o, + .integ_fail_o, + .alert_o + ); + +endmodule : prim_alert_rxtx_fatal_bind_fpv diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_fatal_tb.sv b/synth/prim/fpv/tb/prim_alert_rxtx_fatal_tb.sv new file mode 100755 index 0000000..86376d2 --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_fatal_tb.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 +// +// Testbench module for alert sender/receiver pair. Intended to use with +// a formal tool. + +module prim_alert_rxtx_fatal_tb + import prim_alert_pkg::*; + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input alert_req_i, + input mubi4_t init_trig_i, + input ping_req_i, + output logic alert_ack_o, + output logic alert_state_o, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // synchronous case + localparam bit AsyncOn = 1'b0; + localparam bit IsFatal = 1'b1; + + 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 ), + .IsFatal ( IsFatal ) + ) i_prim_alert_sender ( + .clk_i, + .rst_ni, + .alert_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .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, + .init_trig_i, + .ping_req_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_fatal_tb diff --git a/synth/prim/fpv/tb/prim_alert_rxtx_tb.sv b/synth/prim/fpv/tb/prim_alert_rxtx_tb.sv new file mode 100755 index 0000000..a7575af --- /dev/null +++ b/synth/prim/fpv/tb/prim_alert_rxtx_tb.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 +// +// Testbench module for alert sender/receiver pair. Intended to use with +// a formal tool. + +module prim_alert_rxtx_tb + import prim_alert_pkg::*; + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input alert_req_i, + input mubi4_t init_trig_i, + input ping_req_i, + output logic alert_ack_o, + output logic alert_state_o, + output logic ping_ok_o, + output logic integ_fail_o, + output logic alert_o +); + + // synchronous case + localparam bit AsyncOn = 1'b0; + localparam bit IsFatal = 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 ), + .IsFatal ( IsFatal ) + ) i_prim_alert_sender ( + .clk_i, + .rst_ni, + .alert_test_i, + .alert_req_i, + .alert_ack_o, + .alert_state_o, + .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, + .init_trig_i, + .ping_req_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_tb diff --git a/synth/prim/fpv/tb/prim_arbiter_fixed_tb.sv b/synth/prim/fpv/tb/prim_arbiter_fixed_tb.sv new file mode 100755 index 0000000..be7a572 --- /dev/null +++ b/synth/prim/fpv/tb/prim_arbiter_fixed_tb.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 +// +// Testbench module for prim_arbiter_fixed. +// Intended to be used with a formal tool. + +module prim_arbiter_fixed_tb #( + parameter int N = 8, + parameter int DW = 32, + parameter bit EnDataPort = 1, + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + input [N-1:0] req_i, + input [DW-1:0]data_i [N], + output logic[N-1:0] gnt_o, + output logic[IdxW-1:0] idx_o, + output logic valid_o, + output logic[DW-1:0] data_o, + input ready_i +); + + + prim_arbiter_fixed #( + .N(N), + .DW(DW), + .EnDataPort(EnDataPort) + ) i_prim_arbiter_fixed ( + .clk_i, + .rst_ni, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_fixed_tb diff --git a/synth/prim/fpv/tb/prim_arbiter_ppc_tb.sv b/synth/prim/fpv/tb/prim_arbiter_ppc_tb.sv new file mode 100755 index 0000000..087b2dd --- /dev/null +++ b/synth/prim/fpv/tb/prim_arbiter_ppc_tb.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 +// +// Testbench module for prim_arbiter_ppc. +// Intended to be used with a formal tool. + +module prim_arbiter_ppc_tb #( + parameter int unsigned N = 8, + parameter int unsigned DW = 32, + parameter bit EnDataPort = 1, + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + + prim_arbiter_ppc #( + .N(N), + .DW(DW), + .EnDataPort(EnDataPort) + ) i_prim_arbiter_ppc ( + .clk_i, + .rst_ni, + .req_chk_i, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_ppc_tb diff --git a/synth/prim/fpv/tb/prim_arbiter_tree_tb.sv b/synth/prim/fpv/tb/prim_arbiter_tree_tb.sv new file mode 100755 index 0000000..d37ea88 --- /dev/null +++ b/synth/prim/fpv/tb/prim_arbiter_tree_tb.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 +// +// Testbench module for prim_arbiter_tree. +// Intended to be used with a formal tool. + +module prim_arbiter_tree_tb #( + parameter int N = 8, + parameter int DW = 32, + parameter bit EnDataPort = 1, + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + + prim_arbiter_tree #( + .N(N), + .DW(DW), + .EnDataPort(EnDataPort) + ) i_prim_arbiter_tree ( + .clk_i, + .rst_ni, + .req_chk_i, + .req_i, + .data_i, + .gnt_o, + .idx_o, + .valid_o, + .data_o, + .ready_i + ); + + +endmodule : prim_arbiter_tree_tb diff --git a/synth/prim/fpv/tb/prim_count_tb.sv b/synth/prim/fpv/tb/prim_count_tb.sv new file mode 100755 index 0000000..a3cb8da --- /dev/null +++ b/synth/prim/fpv/tb/prim_count_tb.sv @@ -0,0 +1,44 @@ +// 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_count. +// Intended to be used with a formal tool. + +`include "prim_assert.sv" + +module prim_count_tb #( + parameter int Width = 2, + parameter logic [Width-1:0] ResetValue = '0 +) ( + input clk_i, + input rst_ni, + input clr_i, + input set_i, + input [Width-1:0] set_cnt_i, + input incr_en_i, + input decr_en_i, + input [Width-1:0] step_i, + output logic [Width-1:0] cnt_o, + output logic [Width-1:0] cnt_next_o, + output logic err_o +); + + prim_count #( + .Width(Width), + .ResetValue(ResetValue) + ) u_counter ( + .clk_i, + .rst_ni, + .clr_i, + .set_i, + .set_cnt_i, + .incr_en_i, + .decr_en_i, + .step_i, + .cnt_o, + .cnt_next_o, + .err_o + ); + +endmodule : prim_count_tb diff --git a/synth/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv b/synth/prim/fpv/tb/prim_esc_rxtx_bind_fpv.sv new file mode 100755 index 0000000..80c47bb --- /dev/null +++ b/synth/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_req_i , + .ping_req_i , + .ping_ok_o , + .integ_fail_o, + .esc_req_o + ); + +endmodule : prim_esc_rxtx_bind_fpv diff --git a/synth/prim/fpv/tb/prim_esc_rxtx_tb.sv b/synth/prim/fpv/tb/prim_esc_rxtx_tb.sv new file mode 100755 index 0000000..7e27902 --- /dev/null +++ b/synth/prim/fpv/tb/prim_esc_rxtx_tb.sv @@ -0,0 +1,57 @@ +// 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_tb + 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_req_i, + input ping_req_i, + output logic ping_ok_o, + output logic integ_fail_o, + output logic esc_req_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 u_prim_esc_sender ( + .clk_i , + .rst_ni , + .ping_req_i , + .ping_ok_o , + .integ_fail_o , + .esc_req_i , + .esc_rx_i ( esc_rx_in ), + .esc_tx_o ( esc_tx_out ) + ); + + prim_esc_receiver #( + // This reduces the state space for this counter + // from 2**24 to 2**6 to speed up convergence. + .TimeoutCntDw(6) + ) u_prim_esc_receiver ( + .clk_i , + .rst_ni , + .esc_req_o , + .esc_rx_o ( esc_rx_out ), + .esc_tx_i ( esc_tx_in ) + ); + +endmodule : prim_esc_rxtx_tb diff --git a/synth/prim/fpv/tb/prim_fifo_async_sram_adapter_tb.sv b/synth/prim/fpv/tb/prim_fifo_async_sram_adapter_tb.sv new file mode 100755 index 0000000..889796e --- /dev/null +++ b/synth/prim/fpv/tb/prim_fifo_async_sram_adapter_tb.sv @@ -0,0 +1,204 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Testbench module for prim_fifo_sram_async + +module prim_fifo_async_sram_adapter_tb #( + parameter int unsigned Width = 32, + parameter int unsigned Depth = 16, + + parameter int unsigned FpgaSram = 0 // Use FF based +) ( + input clk_wr_i, + input clk_rd_i, + + input rst_ni, + + input wvalid_i, + output logic wready_o, + input [Width-1:0] wdata_i, + + output logic rvalid_o, + input rready_i, + output logic [Width-1:0] rdata_o +); + + localparam int unsigned SramAw = 7; + localparam int unsigned SramDw = 32; + localparam logic [SramAw-1:0] SramBaseAddr = 'h 30; + + logic w_sram_req; + logic w_sram_gnt; + logic w_sram_write; + logic [SramAw-1:0] w_sram_addr; + logic [SramDw-1:0] w_sram_wdata; + logic [SramDw-1:0] w_sram_wmask; + logic w_sram_rvalid; // not used + logic [SramDw-1:0] w_sram_rdata; // not used + logic [1:0] w_sram_rerror; // not used + + logic r_sram_req; + logic r_sram_gnt; + logic r_sram_write; + logic [SramAw-1:0] r_sram_addr; + logic [SramDw-1:0] r_sram_wdata; // not used + logic [SramDw-1:0] r_sram_wmask; // not used + logic r_sram_rvalid; + logic [SramDw-1:0] r_sram_rdata; + logic [1:0] r_sram_rerror; + + prim_fifo_async_sram_adapter #( + .Width (Width), + .Depth (Depth), + + .SramAw (SramAw), // TODO: random with constraint + .SramDw (SramDw), // TODO: random with constraint + + .SramBaseAddr(SramBaseAddr) // TODO: random? + ) dut ( + .clk_wr_i, + .rst_wr_ni (rst_ni), + .clk_rd_i, + .rst_rd_ni (rst_ni), + + .wvalid_i, + .wready_o, + .wdata_i, + + .wdepth_o (), + + .rvalid_o, + .rready_i, + .rdata_o, + + .rdepth_o (), + + // Sram Interface + .w_sram_req_o (w_sram_req ), + .w_sram_gnt_i (w_sram_gnt ), + .w_sram_write_o (w_sram_write ), + .w_sram_addr_o (w_sram_addr ), + .w_sram_wdata_o (w_sram_wdata ), + .w_sram_wmask_o (w_sram_wmask ), + .w_sram_rvalid_i(w_sram_rvalid), // not used + .w_sram_rdata_i (w_sram_rdata ), // not used + .w_sram_rerror_i(w_sram_rerror), // not used + + // Read SRAM port + .r_sram_req_o (r_sram_req ), + .r_sram_gnt_i (r_sram_gnt ), + .r_sram_write_o (r_sram_write ), + .r_sram_addr_o (r_sram_addr ), + .r_sram_wdata_o (r_sram_wdata ), // not used + .r_sram_wmask_o (r_sram_wmask ), // not used + .r_sram_rvalid_i(r_sram_rvalid), + .r_sram_rdata_i (r_sram_rdata ), + .r_sram_rerror_i(r_sram_rerror), + + .r_full_o(), + + .r_notempty_o(), + + .w_full_o() + ); + +if (FpgaSram == 1) begin : g_sram_fpga + // FPV looks like does not like the style of ram_2p. + prim_ram_2p #( + .Depth (2**SramAw), + .Width (SramDw), + .DataBitsPerMask (1) + ) u_sram ( + .clk_a_i (clk_wr_i), + .clk_b_i (clk_rd_i), + + .a_req_i (w_sram_req ), + .a_write_i (w_sram_write ), + .a_addr_i (w_sram_addr ), + .a_wdata_i (w_sram_wdata ), + .a_wmask_i (w_sram_wmask ), + .a_rdata_o (w_sram_rdata ), + + .b_req_i (r_sram_req ), + .b_write_i (r_sram_write ), + .b_addr_i (r_sram_addr ), + .b_wdata_i (r_sram_wdata ), + .b_wmask_i (r_sram_wmask ), + .b_rdata_o (r_sram_rdata ), + + .cfg_i ('0) + ); +end else begin : g_sram_ff + logic [SramDw-1:0] mem [2**SramAw]; + + always_ff @(posedge clk_wr_i) begin + if (w_sram_req && w_sram_write) begin + for (int unsigned i = 0; i < SramDw ; i++) begin + if (w_sram_wmask[i]) mem[w_sram_addr][i] <= w_sram_wdata[i]; + end // for + end // if + end + + always_ff @(posedge clk_rd_i) begin + if (r_sram_req && !r_sram_write) begin + r_sram_rdata <= mem[r_sram_addr]; + end + end +end // !FpgaSram + + assign w_sram_gnt = w_sram_req; + + always_ff @(posedge clk_wr_i) begin + w_sram_rvalid <= w_sram_req && !w_sram_write; + end + + assign w_sram_rerror = '0; + + assign r_sram_gnt = r_sram_req; + + always_ff @(posedge clk_rd_i) begin + r_sram_rvalid <= r_sram_req && !r_sram_write; + end + + assign r_sram_rerror = '0; + + `ASSUME_FPV(WdataBins_M, + wvalid_i |-> wdata_i inside { + 32'h DEAD_BEEF, 32'h5A5A_A5A5, 32'h 1234_5678 + }, + clk_wr_i, !rst_ni) + +`ifdef FPV_ON + logic [Width-1:0] storage [Depth]; + logic [Width-1:0] rdata; + logic [$clog2(Depth)-1:0] wptr, rptr; + logic wack, rack; + + assign wack = wvalid_i && wready_o; + assign rack = rvalid_o && rready_i; + + always_ff @(posedge clk_wr_i or negedge rst_ni) begin + if (!rst_ni) wptr <= '0; + else if (wack) wptr <= wptr+1; + end + always_ff @(posedge clk_rd_i or negedge rst_ni) begin + if (!rst_ni) rptr <= '0; + else if (rack) rptr <= rptr+1; + end + + always_ff @(posedge clk_wr_i or negedge rst_ni) begin + if (!rst_ni) storage <= '{default:'0}; + else if (wack) begin + storage[wptr] <= wdata_i; + end + end + + assign rdata = storage[rptr]; + + `ASSERT(DataIntegrityCheck_A, + rack |-> rdata_o == rdata, + clk_rd_i, !rst_ni) +`endif // FPV_ON + +endmodule : prim_fifo_async_sram_adapter_tb diff --git a/synth/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv b/synth/prim/fpv/tb/prim_fifo_sync_bind_fpv.sv new file mode 100755 index 0000000..e734d98 --- /dev/null +++ b/synth/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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + //////////////// + // 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + + 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_i, + .wready_o, + .wdata_i, + .rvalid_o, + .rready_i, + .rdata_o, + .depth_o + ); + +endmodule : prim_fifo_sync_bind_fpv diff --git a/synth/prim/fpv/tb/prim_fifo_sync_tb.sv b/synth/prim/fpv/tb/prim_fifo_sync_tb.sv new file mode 100755 index 0000000..5dfa5bc --- /dev/null +++ b/synth/prim/fpv/tb/prim_fifo_sync_tb.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_tb #( + // 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_i[NumDuts], + output wready_o[NumDuts], + input [Width-1:0] wdata_i [NumDuts], + output rvalid_o[NumDuts], + input rready_i[NumDuts], + output [Width-1:0] rdata_o [NumDuts], + output [DepthW-1:0] depth_o [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_i(wvalid_i[0]), + .wready_o(wready_o[0]), + .wdata_i(wdata_i[0]), + .rvalid_o(rvalid_o[0]), + .rready_i(rready_i[0]), + .rdata_o(rdata_o[0]), + .depth_o(depth_o[0][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(7) + ) i_nopass7 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[1]), + .wready_o(wready_o[1]), + .wdata_i(wdata_i[1]), + .rvalid_o(rvalid_o[1]), + .rready_i(rready_i[1]), + .rdata_o(rdata_o[1]), + .depth_o(depth_o[1][2:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(8) + ) i_nopass8 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[2]), + .wready_o(wready_o[2]), + .wdata_i(wdata_i[2]), + .rvalid_o(rvalid_o[2]), + .rready_i(rready_i[2]), + .rdata_o(rdata_o[2]), + .depth_o(depth_o[2][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(15) + ) i_nopass15 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[3]), + .wready_o(wready_o[3]), + .wdata_i(wdata_i[3]), + .rvalid_o(rvalid_o[3]), + .rready_i(rready_i[3]), + .rdata_o(rdata_o[3]), + .depth_o(depth_o[3][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b0), + .Depth(16) + ) i_nopass16 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[4]), + .wready_o(wready_o[4]), + .wdata_i(wdata_i[4]), + .rvalid_o(rvalid_o[4]), + .rready_i(rready_i[4]), + .rdata_o(rdata_o[4]), + .depth_o(depth_o[4][4:0]) + ); + + //////////////// + // pass FIFOs // + //////////////// + + // depth_o-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_i(wvalid_i[5]), + .wready_o(wready_o[5]), + .wdata_i(wdata_i[5]), + .rvalid_o(rvalid_o[5]), + .rready_i(rready_i[5]), + .rdata_o(rdata_o[5]), + .depth_o(depth_o[5][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(1) + ) i_pass1 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[6]), + .wready_o(wready_o[6]), + .wdata_i(wdata_i[6]), + .rvalid_o(rvalid_o[6]), + .rready_i(rready_i[6]), + .rdata_o(rdata_o[6]), + .depth_o(depth_o[6][0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(7) + ) i_pass7 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[7]), + .wready_o(wready_o[7]), + .wdata_i(wdata_i[7]), + .rvalid_o(rvalid_o[7]), + .rready_i(rready_i[7]), + .rdata_o(rdata_o[7]), + .depth_o(depth_o[7][2:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(8) + ) i_pass8 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[8]), + .wready_o(wready_o[8]), + .wdata_i(wdata_i[8]), + .rvalid_o(rvalid_o[8]), + .rready_i(rready_i[8]), + .rdata_o(rdata_o[8]), + .depth_o(depth_o[8][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(15) + ) i_pass15 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[9]), + .wready_o(wready_o[9]), + .wdata_i(wdata_i[9]), + .rvalid_o(rvalid_o[9]), + .rready_i(rready_i[9]), + .rdata_o(rdata_o[9]), + .depth_o(depth_o[9][3:0]) + ); + + prim_fifo_sync #( + .Width(Width), + .Pass(1'b1), + .Depth(16) + ) i_pass16 ( + .clk_i, + .rst_ni, + .clr_i, + .wvalid_i(wvalid_i[10]), + .wready_o(wready_o[10]), + .wdata_i(wdata_i[10]), + .rvalid_o(rvalid_o[10]), + .rready_i(rready_i[10]), + .rdata_o(rdata_o[10]), + .depth_o(depth_o[10][4:0]) + ); + +endmodule : prim_fifo_sync_tb diff --git a/synth/prim/fpv/tb/prim_keccak_tb.sv b/synth/prim/fpv/tb/prim_keccak_tb.sv new file mode 100755 index 0000000..f5d0c3d --- /dev/null +++ b/synth/prim/fpv/tb/prim_keccak_tb.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_tb #( + 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/synth/prim/fpv/tb/prim_lfsr_tb.sv b/synth/prim/fpv/tb/prim_lfsr_tb.sv new file mode 100755 index 0000000..dd2c9eb --- /dev/null +++ b/synth/prim/fpv/tb/prim_lfsr_tb.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 +// +// Testbench module for prim_lfsr. Intended to be used with a formal tool. + +module prim_lfsr_tb #( + // 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 = 2*(FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1) + + 2*(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 ) + ) u_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 + + // same as above but with non-linear output enabled. + // only power-of-two widths are allowed. + for (genvar k = 16; k <= GalXorMaxLfsrDw; k = k*2) + begin : gen_gal_xor_duts_nonlinear + 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 ), + .NonLinearOut (1) + ) u_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 ) + ) u_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 + + // same as above but with non-linear output enabled. + // only power-of-two widths are allowed. + for (genvar k = 16; k <= FibXnorMaxLfsrDw; k = k*2) + begin : gen_fib_xnor_duts_nonlinear + 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 ), + .NonLinearOut (1) + ) u_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_tb diff --git a/synth/prim/fpv/tb/prim_packer_tb.sv b/synth/prim/fpv/tb/prim_packer_tb.sv new file mode 100755 index 0000000..6146963 --- /dev/null +++ b/synth/prim/fpv/tb/prim_packer_tb.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 prim_packer. Intended to be used with a formal tool. +// To reduce the runtime for prim_packer, we limited the width parameter. + +module prim_packer_tb #( + parameter int unsigned MaxInW = 64, + parameter int unsigned MaxOutW = 64 +) ( + input clk_i , + input rst_ni, + + input valid_i, + input [MaxInW-1:0] data_i, + input [MaxInW-1:0] mask_i, + output ready_o, + + output logic valid_o, + output logic [MaxOutW-1:0] data_o, + output logic [MaxOutW-1:0] mask_o, + input ready_i, + + input flush_i, + output logic flush_done_o, + output logic err_o +); + + for (genvar k = 1; k <= 16; k++) begin : gen_prim_packer + prim_packer #(.InW(k), .OutW(17-k) + ) i_prim_packer ( + .clk_i, + .rst_ni, + .valid_i, + .data_i (data_i[k-1:0]), + .mask_i (mask_i[k-1:0]), + .ready_o, + .valid_o, + .data_o (data_o[16-k:0]), + .mask_o (mask_o[16-k:0]), + .ready_i, + .flush_i, + .flush_done_o, + .err_o + ); + end + + prim_packer #(.InW(MaxInW), .OutW(MaxOutW) + ) i_prim_packer_max ( + .clk_i, + .rst_ni, + .valid_i, + .data_i (data_i), + .mask_i (mask_i), + .ready_o, + .valid_o, + .data_o (data_o), + .mask_o (mask_o), + .ready_i, + .flush_i, + .flush_done_o, + .err_o + ); + +endmodule : prim_packer_tb diff --git a/synth/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv new file mode 100755 index 0000000..1102d95 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_22_16_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_22_16_bind_fpv; + + bind prim_secded_22_16_tb + prim_secded_22_16_assert_fpv prim_secded_22_16_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_22_16_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_22_16_tb.sv b/synth/prim/fpv/tb/prim_secded_22_16_tb.sv new file mode 100755 index 0000000..e7da60e --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_22_16_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_22_16_tb ( + input clk_i, + input rst_ni, + input [15:0] data_i, + output logic [15:0] data_o, + output logic [21:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [21:0] error_inject_i +); + + prim_secded_22_16_enc prim_secded_22_16_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_22_16_dec prim_secded_22_16_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_22_16_tb diff --git a/synth/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv new file mode 100755 index 0000000..06a0ab7 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_28_22_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_28_22_bind_fpv; + + bind prim_secded_28_22_tb + prim_secded_28_22_assert_fpv prim_secded_28_22_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_28_22_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_28_22_tb.sv b/synth/prim/fpv/tb/prim_secded_28_22_tb.sv new file mode 100755 index 0000000..6a6ff79 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_28_22_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_28_22_tb ( + input clk_i, + input rst_ni, + input [21:0] data_i, + output logic [21:0] data_o, + output logic [27:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [27:0] error_inject_i +); + + prim_secded_28_22_enc prim_secded_28_22_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_28_22_dec prim_secded_28_22_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_28_22_tb diff --git a/synth/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv new file mode 100755 index 0000000..1215d79 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_39_32_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_39_32_bind_fpv; + + bind prim_secded_39_32_tb + prim_secded_39_32_assert_fpv prim_secded_39_32_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_39_32_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_39_32_tb.sv b/synth/prim/fpv/tb/prim_secded_39_32_tb.sv new file mode 100755 index 0000000..a497af9 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_39_32_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_39_32_tb ( + input clk_i, + input rst_ni, + input [31:0] data_i, + output logic [31:0] data_o, + output logic [38:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [38:0] error_inject_i +); + + prim_secded_39_32_enc prim_secded_39_32_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_39_32_dec prim_secded_39_32_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_39_32_tb diff --git a/synth/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv new file mode 100755 index 0000000..0acfbe8 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_64_57_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_64_57_bind_fpv; + + bind prim_secded_64_57_tb + prim_secded_64_57_assert_fpv prim_secded_64_57_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_64_57_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_64_57_tb.sv b/synth/prim/fpv/tb/prim_secded_64_57_tb.sv new file mode 100755 index 0000000..9cc9ef3 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_64_57_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_64_57_tb ( + input clk_i, + input rst_ni, + input [56:0] data_i, + output logic [56:0] data_o, + output logic [63:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [63:0] error_inject_i +); + + prim_secded_64_57_enc prim_secded_64_57_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_64_57_dec prim_secded_64_57_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_64_57_tb diff --git a/synth/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv new file mode 100755 index 0000000..31a1657 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_72_64_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_72_64_bind_fpv; + + bind prim_secded_72_64_tb + prim_secded_72_64_assert_fpv prim_secded_72_64_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_72_64_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_72_64_tb.sv b/synth/prim/fpv/tb/prim_secded_72_64_tb.sv new file mode 100755 index 0000000..f653524 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_72_64_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_72_64_tb ( + input clk_i, + input rst_ni, + input [63:0] data_i, + output logic [63:0] data_o, + output logic [71:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [71:0] error_inject_i +); + + prim_secded_72_64_enc prim_secded_72_64_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_72_64_dec prim_secded_72_64_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_72_64_tb diff --git a/synth/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv new file mode 100755 index 0000000..8ecfc23 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_22_16_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_hamming_22_16_bind_fpv; + + bind prim_secded_hamming_22_16_tb + prim_secded_hamming_22_16_assert_fpv prim_secded_hamming_22_16_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_hamming_22_16_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_hamming_22_16_tb.sv b/synth/prim/fpv/tb/prim_secded_hamming_22_16_tb.sv new file mode 100755 index 0000000..cffae6b --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_22_16_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_hamming_22_16_tb ( + input clk_i, + input rst_ni, + input [15:0] data_i, + output logic [15:0] data_o, + output logic [21:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [21:0] error_inject_i +); + + prim_secded_hamming_22_16_enc prim_secded_hamming_22_16_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_hamming_22_16_dec prim_secded_hamming_22_16_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_hamming_22_16_tb diff --git a/synth/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv new file mode 100755 index 0000000..35eb0bf --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_39_32_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_hamming_39_32_bind_fpv; + + bind prim_secded_hamming_39_32_tb + prim_secded_hamming_39_32_assert_fpv prim_secded_hamming_39_32_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_hamming_39_32_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_hamming_39_32_tb.sv b/synth/prim/fpv/tb/prim_secded_hamming_39_32_tb.sv new file mode 100755 index 0000000..5a59e15 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_39_32_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_hamming_39_32_tb ( + input clk_i, + input rst_ni, + input [31:0] data_i, + output logic [31:0] data_o, + output logic [38:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [38:0] error_inject_i +); + + prim_secded_hamming_39_32_enc prim_secded_hamming_39_32_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_hamming_39_32_dec prim_secded_hamming_39_32_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_hamming_39_32_tb diff --git a/synth/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv new file mode 100755 index 0000000..bf09d37 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_72_64_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_hamming_72_64_bind_fpv; + + bind prim_secded_hamming_72_64_tb + prim_secded_hamming_72_64_assert_fpv prim_secded_hamming_72_64_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_hamming_72_64_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_hamming_72_64_tb.sv b/synth/prim/fpv/tb/prim_secded_hamming_72_64_tb.sv new file mode 100755 index 0000000..5f4c8e3 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_72_64_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_hamming_72_64_tb ( + input clk_i, + input rst_ni, + input [63:0] data_i, + output logic [63:0] data_o, + output logic [71:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [71:0] error_inject_i +); + + prim_secded_hamming_72_64_enc prim_secded_hamming_72_64_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_hamming_72_64_dec prim_secded_hamming_72_64_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_hamming_72_64_tb diff --git a/synth/prim/fpv/tb/prim_secded_hamming_76_68_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_hamming_76_68_bind_fpv.sv new file mode 100755 index 0000000..4962eb1 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_76_68_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_hamming_76_68_bind_fpv; + + bind prim_secded_hamming_76_68_tb + prim_secded_hamming_76_68_assert_fpv prim_secded_hamming_76_68_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_hamming_76_68_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_hamming_76_68_tb.sv b/synth/prim/fpv/tb/prim_secded_hamming_76_68_tb.sv new file mode 100755 index 0000000..a599ce1 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_hamming_76_68_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_hamming_76_68_tb ( + input clk_i, + input rst_ni, + input [67:0] data_i, + output logic [67:0] data_o, + output logic [75:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [75:0] error_inject_i +); + + prim_secded_hamming_76_68_enc prim_secded_hamming_76_68_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_hamming_76_68_dec prim_secded_hamming_76_68_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_hamming_76_68_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_22_16_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_22_16_bind_fpv.sv new file mode 100755 index 0000000..96d0c95 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_22_16_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_22_16_bind_fpv; + + bind prim_secded_inv_22_16_tb + prim_secded_inv_22_16_assert_fpv prim_secded_inv_22_16_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_22_16_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_22_16_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_22_16_tb.sv new file mode 100755 index 0000000..aaa5123 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_22_16_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_22_16_tb ( + input clk_i, + input rst_ni, + input [15:0] data_i, + output logic [15:0] data_o, + output logic [21:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [21:0] error_inject_i +); + + prim_secded_inv_22_16_enc prim_secded_inv_22_16_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_22_16_dec prim_secded_inv_22_16_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_22_16_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_28_22_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_28_22_bind_fpv.sv new file mode 100755 index 0000000..5b114b2 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_28_22_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_28_22_bind_fpv; + + bind prim_secded_inv_28_22_tb + prim_secded_inv_28_22_assert_fpv prim_secded_inv_28_22_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_28_22_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_28_22_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_28_22_tb.sv new file mode 100755 index 0000000..ff4caf4 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_28_22_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_28_22_tb ( + input clk_i, + input rst_ni, + input [21:0] data_i, + output logic [21:0] data_o, + output logic [27:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [27:0] error_inject_i +); + + prim_secded_inv_28_22_enc prim_secded_inv_28_22_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_28_22_dec prim_secded_inv_28_22_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_28_22_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_39_32_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_39_32_bind_fpv.sv new file mode 100755 index 0000000..a115964 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_39_32_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_39_32_bind_fpv; + + bind prim_secded_inv_39_32_tb + prim_secded_inv_39_32_assert_fpv prim_secded_inv_39_32_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_39_32_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_39_32_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_39_32_tb.sv new file mode 100755 index 0000000..b1c5af6 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_39_32_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_39_32_tb ( + input clk_i, + input rst_ni, + input [31:0] data_i, + output logic [31:0] data_o, + output logic [38:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [38:0] error_inject_i +); + + prim_secded_inv_39_32_enc prim_secded_inv_39_32_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_39_32_dec prim_secded_inv_39_32_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_39_32_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_64_57_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_64_57_bind_fpv.sv new file mode 100755 index 0000000..81bb663 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_64_57_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_64_57_bind_fpv; + + bind prim_secded_inv_64_57_tb + prim_secded_inv_64_57_assert_fpv prim_secded_inv_64_57_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_64_57_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_64_57_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_64_57_tb.sv new file mode 100755 index 0000000..fd0751d --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_64_57_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_64_57_tb ( + input clk_i, + input rst_ni, + input [56:0] data_i, + output logic [56:0] data_o, + output logic [63:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [63:0] error_inject_i +); + + prim_secded_inv_64_57_enc prim_secded_inv_64_57_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_64_57_dec prim_secded_inv_64_57_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_64_57_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_72_64_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_72_64_bind_fpv.sv new file mode 100755 index 0000000..3a37a45 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_72_64_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_72_64_bind_fpv; + + bind prim_secded_inv_72_64_tb + prim_secded_inv_72_64_assert_fpv prim_secded_inv_72_64_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_72_64_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_72_64_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_72_64_tb.sv new file mode 100755 index 0000000..1d2c6d8 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_72_64_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_72_64_tb ( + input clk_i, + input rst_ni, + input [63:0] data_i, + output logic [63:0] data_o, + output logic [71:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [71:0] error_inject_i +); + + prim_secded_inv_72_64_enc prim_secded_inv_72_64_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_72_64_dec prim_secded_inv_72_64_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_72_64_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_bind_fpv.sv new file mode 100755 index 0000000..07e95af --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_22_16_bind_fpv; + + bind prim_secded_inv_hamming_22_16_tb + prim_secded_inv_hamming_22_16_assert_fpv prim_secded_inv_hamming_22_16_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_hamming_22_16_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_tb.sv new file mode 100755 index 0000000..6397d77 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_22_16_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_22_16_tb ( + input clk_i, + input rst_ni, + input [15:0] data_i, + output logic [15:0] data_o, + output logic [21:0] encoded_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o, + input [21:0] error_inject_i +); + + prim_secded_inv_hamming_22_16_enc prim_secded_inv_hamming_22_16_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_hamming_22_16_dec prim_secded_inv_hamming_22_16_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_hamming_22_16_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_bind_fpv.sv new file mode 100755 index 0000000..a364b15 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_39_32_bind_fpv; + + bind prim_secded_inv_hamming_39_32_tb + prim_secded_inv_hamming_39_32_assert_fpv prim_secded_inv_hamming_39_32_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_hamming_39_32_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_tb.sv new file mode 100755 index 0000000..7d1fcdf --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_39_32_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_39_32_tb ( + input clk_i, + input rst_ni, + input [31:0] data_i, + output logic [31:0] data_o, + output logic [38:0] encoded_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o, + input [38:0] error_inject_i +); + + prim_secded_inv_hamming_39_32_enc prim_secded_inv_hamming_39_32_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_hamming_39_32_dec prim_secded_inv_hamming_39_32_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_hamming_39_32_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_bind_fpv.sv new file mode 100755 index 0000000..4c4bc3a --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_72_64_bind_fpv; + + bind prim_secded_inv_hamming_72_64_tb + prim_secded_inv_hamming_72_64_assert_fpv prim_secded_inv_hamming_72_64_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_hamming_72_64_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_tb.sv new file mode 100755 index 0000000..52acadd --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_72_64_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_72_64_tb ( + input clk_i, + input rst_ni, + input [63:0] data_i, + output logic [63:0] data_o, + output logic [71:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [71:0] error_inject_i +); + + prim_secded_inv_hamming_72_64_enc prim_secded_inv_hamming_72_64_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_hamming_72_64_dec prim_secded_inv_hamming_72_64_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_hamming_72_64_tb diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_bind_fpv.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_bind_fpv.sv new file mode 100755 index 0000000..8c2bac9 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_bind_fpv.sv @@ -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 +// +// SECDED FPV bind file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_76_68_bind_fpv; + + bind prim_secded_inv_hamming_76_68_tb + prim_secded_inv_hamming_76_68_assert_fpv prim_secded_inv_hamming_76_68_assert_fpv ( + .clk_i, + .rst_ni, + .data_i, + .data_o, + .encoded_o, + .syndrome_o, + .err_o, + .error_inject_i + ); + +endmodule : prim_secded_inv_hamming_76_68_bind_fpv diff --git a/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_tb.sv b/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_tb.sv new file mode 100755 index 0000000..c635bb1 --- /dev/null +++ b/synth/prim/fpv/tb/prim_secded_inv_hamming_76_68_tb.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 +// +// SECDED FPV testbench generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_76_68_tb ( + input clk_i, + input rst_ni, + input [67:0] data_i, + output logic [67:0] data_o, + output logic [75:0] encoded_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o, + input [75:0] error_inject_i +); + + prim_secded_inv_hamming_76_68_enc prim_secded_inv_hamming_76_68_enc ( + .data_i, + .data_o(encoded_o) + ); + + prim_secded_inv_hamming_76_68_dec prim_secded_inv_hamming_76_68_dec ( + .data_i(encoded_o ^ error_inject_i), + .data_o, + .syndrome_o, + .err_o + ); + +endmodule : prim_secded_inv_hamming_76_68_tb diff --git a/synth/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv b/synth/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv new file mode 100755 index 0000000..ebdaf70 --- /dev/null +++ b/synth/prim/fpv/vip/prim_alert_rxtx_assert_fpv.sv @@ -0,0 +1,146 @@ +// 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 + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input alert_req_i, + input alert_ack_o, + input alert_state_o, + input mubi4_t init_trig_i, + input ping_req_i, + input ping_ok_o, + input integ_fail_o, + input alert_o +); + + import prim_mubi_pkg::mubi4_test_true_strict; + + logic error_present; + assign error_present = ping_err_pi | ping_err_ni | + ack_err_pi | ack_err_ni | + alert_err_pi | alert_err_ni; + + logic init_pending; + assign init_pending = mubi4_test_true_strict(init_trig_i) || + prim_alert_rxtx_fpv.i_prim_alert_receiver.state_q inside { + prim_alert_rxtx_fpv.i_prim_alert_receiver.InitReq, + prim_alert_rxtx_fpv.i_prim_alert_receiver.InitAckWait}; + + // 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_req_i && ping_ok_o |=> !ping_req_i, clk_i, !rst_ni) + `ASSUME_FPV(PingEn_M, $rose(ping_req_i) |-> ping_req_i throughout + (ping_ok_o || error_present)[->1] ##1 $fell(ping_req_i), + clk_i, !rst_ni) + 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 good 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 || mubi4_test_true_strict(init_trig_i)) + `ASSERT(AlertHs_A, alert_req_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 |-> alert_ack_o, + clk_i, !rst_ni || error_present || mubi4_test_true_strict(init_trig_i)) + `ASSERT(AlertTestHs_A, alert_test_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 || mubi4_test_true_strict(init_trig_i)) + // Make sure we eventually get an ACK + `ASSERT(AlertReqAck_A, alert_req_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) |-> strong(##[1:$] alert_ack_o), + clk_i, !rst_ni || error_present || mubi4_test_true_strict(init_trig_i)) + + // transmission of pings + // note: the complete transmission of pings only happen when no ping handshake is in progress + `ASSERT(AlertPingOk_A, !(prim_alert_rxtx_fpv.i_prim_alert_sender.state_q inside { + prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase1, + prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |-> + ##[1:9] ping_ok_o, + clk_i, !rst_ni || error_present || init_pending) + `ASSERT(AlertPingIgnored_A, (prim_alert_rxtx_fpv.i_prim_alert_sender.state_q inside { + prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase1, + prim_alert_rxtx_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |-> + ping_ok_o == 0 throughout ping_req_i [->1], + clk_i, !rst_ni || error_present || mubi4_test_true_strict(init_trig_i)) + // transmission of alerts in case of no collision with ping enable + `ASSERT(AlertCheck0_A, !ping_req_i [*3] ##0 ($rose(alert_req_i) || $rose(alert_test_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_req_i || init_pending) + // transmission of alerts in the general case which can include continous ping collisions + `ASSERT(AlertCheck1_A, alert_req_i || alert_test_i |=> + strong(##[1:$] ((prim_alert_rxtx_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_fpv.i_prim_alert_sender.Idle) && !ping_req_i) ##1 alert_o), + clk_i, + !rst_ni || error_present || prim_alert_rxtx_fpv.i_prim_alert_sender.alert_clr || + mubi4_test_true_strict(init_trig_i)) + + // 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 || mubi4_test_true_strict(init_trig_i)) + `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 || mubi4_test_true_strict(init_trig_i)) + + // check that the in-band reset moves sender FSM into Idle state. + `ASSERT(InBandInitFromReceiverToSender_A, + mubi4_test_true_strict(init_trig_i) + |-> + ##[1:20] (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) + +endmodule : prim_alert_rxtx_assert_fpv diff --git a/synth/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv b/synth/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv new file mode 100755 index 0000000..eaa8c03 --- /dev/null +++ b/synth/prim/fpv/vip/prim_alert_rxtx_async_assert_fpv.sv @@ -0,0 +1,168 @@ +// 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 + import prim_mubi_pkg::mubi4_t; +( + 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_test_i, + input mubi4_t init_trig_i, + input alert_req_i, + input alert_ack_o, + input alert_state_o, + input ping_req_i, + input ping_ok_o, + input integ_fail_o, + input alert_o +); + + import prim_mubi_pkg::mubi4_test_true_strict; + + logic error_present; + assign error_present = ping_err_pi | ping_err_ni | + ack_err_pi | ack_err_ni | + alert_err_pi | alert_err_ni; + + logic init_pending; + assign init_pending = mubi4_test_true_strict(init_trig_i) || + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.state_q inside { + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.InitReq, + prim_alert_rxtx_async_fpv.i_prim_alert_receiver.InitAckWait}; + + // 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_req_i && ping_ok_o |=> !ping_req_i) + `ASSUME_FPV(PingEn_M, $rose(ping_req_i) |-> ping_req_i throughout + (ping_ok_o || error_present)[->1] ##1 $fell(ping_req_i)) + + // 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 good 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 || init_pending) + `ASSERT(AlertHs_A, alert_req_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 || init_pending) + `ASSERT(AlertTestHs_A, alert_test_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 || init_pending) + // Make sure we eventually get an ACK + `ASSERT(AlertReqAck_A, alert_req_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) |-> strong(##[1:$] alert_ack_o), + clk_i, !rst_ni || error_setreg_q || init_pending) + + // 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 + // note: the complete transmission of pings only happen when no ping handshake is in progress + `ASSERT(AlertPingOk_A, !(prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q inside { + prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase1, + prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |-> + ##[1:23] ping_ok_o, + clk_i, !rst_ni || error_setreg_q || init_pending) + `ASSERT(AlertPingIgnored_A, (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q inside { + prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase1, + prim_alert_rxtx_async_fpv.i_prim_alert_sender.PingHsPhase2}) && $rose(ping_req_i) |-> + ping_ok_o == 0 throughout ping_req_i[->1], + clk_i, !rst_ni || error_setreg_q) + // transmission of first alert assertion (no ping collision) + `ASSERT(AlertCheck0_A, !ping_req_i [*10] ##1 ($rose(alert_req_i) || $rose(alert_test_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_req_i || error_setreg_q || init_pending) + // eventual transmission of alerts in the general case which can include continous ping + // collisions + `ASSERT(AlertCheck1_A, alert_req_i || alert_test_i |-> + strong(##[1:$] (prim_alert_rxtx_async_fpv.i_prim_alert_sender.state_q == + prim_alert_rxtx_async_fpv.i_prim_alert_sender.Idle && !ping_req_i) ##[3:5] alert_o), + clk_i, !rst_ni || error_setreg_q || + prim_alert_rxtx_async_fpv.i_prim_alert_sender.alert_clr || init_pending) + + // 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 || init_pending) + `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 || init_pending) + + // check that the in-band reset moves sender FSM into Idle state. + `ASSERT(InBandInitFromReceiverToSender_A, + mubi4_test_true_strict(init_trig_i) + |-> + ##[1:30] (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) + +endmodule : prim_alert_rxtx_async_assert_fpv diff --git a/synth/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv b/synth/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv new file mode 100755 index 0000000..165f85e --- /dev/null +++ b/synth/prim/fpv/vip/prim_esc_rxtx_assert_fpv.sv @@ -0,0 +1,170 @@ +// 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_req_i, + input ping_req_i, + input ping_ok_o, + input integ_fail_o, + input esc_req_o +); + + logic error_present; + assign error_present = resp_err_pi || + resp_err_ni || + esc_err_pi || + esc_err_ni; + + // tracks whether any error has been injected so far + logic error_d, error_q; + assign error_d = error_q || error_present; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_error_reg + if (!rst_ni) begin + error_q <= 1'b0; + end else begin + error_q <= error_d; + end + end + + // tracks whether escalation has been triggered so far + logic esc_d, esc_q; + assign esc_d = esc_q || esc_req_i; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_esc_reg + if (!rst_ni) begin + esc_q <= 1'b0; + end else begin + esc_q <= esc_d; + end + end + + // ping will stay high until ping ok received, then it must be deasserted + `ASSUME_FPV(PingReqDeassert_M, + ping_req_i && + ping_ok_o + |=> + !ping_req_i) + `ASSUME_FPV(PingReqStaysAsserted0_M, + ping_req_i && + !ping_ok_o + |=> + ping_req_i) + // this timing is guaranteed by the lfsr ping timer. + `ASSUME_FPV(PingReqStaysLowFor3Cycles_M, + $fell(ping_req_i) + |-> + !ping_req_i [*3]) + + + // assume that escalation enable signal will eventually be deasserted + // for more than 3 cycles (this assumption is needed such that the FSM liveness + // assertion below can be proven). + `ASSUME_FPV(FiniteEsc_M, + esc_req_i + |-> + strong(##[1:$] !esc_req_i [*3])) + + // check that ping response time is bounded if no error has occurred so far, and + // no escalation is being requested. + `ASSERT(PingRespCheck_A, + $rose(ping_req_i) && + !esc_req_i + |-> + ##[0:4] ping_ok_o, + clk_i, + !rst_ni || + error_d || + esc_req_i) + + // check escalation response toggles. + `ASSERT(EscRespCheck_A, + ##1 esc_req_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_req_i + |-> + ##[0:1] esc_req_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 sender FSM + `ASSERT(FsmLivenessSender_A, + (prim_esc_rxtx_fpv.u_prim_esc_sender.state_q != + prim_esc_rxtx_fpv.u_prim_esc_sender.Idle) + |-> + strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_sender.state_q + == prim_esc_rxtx_fpv.u_prim_esc_sender.Idle))) + // basic liveness of sender FSM (can only be guaranteed if no error is present) + `ASSERT(FsmLivenessReceiver_A, + (prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q != + prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle) + |-> + strong(##[1:$] (prim_esc_rxtx_fpv.u_prim_esc_receiver.state_q + == prim_esc_rxtx_fpv.u_prim_esc_receiver.Idle)), + clk_i, + rst_ni || + error_present) + + // check that auto escalation timeout does not trigger prematurely. + // this requires that no errors have been present so far. + `ASSERT(AutoEscalation0_A, + ping_req_i && + ping_ok_o && + !esc_req_o ##1 + !ping_req_i [*0 : 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 4] + |-> + !esc_req_o, + clk_i, + !rst_ni || + error_d || + esc_d) + + // check that auto escalation timeout kicks in if pings are absent for too long. + // this requires that no errors have been present so far. + `ASSERT(AutoEscalation1_A, + ping_req_i && + ping_ok_o && + !esc_req_o ##1 + !ping_req_i [* 2**prim_esc_rxtx_fpv.u_prim_esc_receiver.TimeoutCntDw - 3 : $] + |-> + esc_req_o, + clk_i, + !rst_ni || + error_d || + esc_d) + + +endmodule : prim_esc_rxtx_assert_fpv diff --git a/synth/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv b/synth/prim/fpv/vip/prim_fifo_sync_assert_fpv.sv new file mode 100755 index 0000000..8cd7a3f --- /dev/null +++ b/synth/prim/fpv/vip/prim_fifo_sync_assert_fpv.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 +// +// 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_i, + input wready_o, + input [Width-1:0] wdata_i, + input rvalid_o, + input rready_i, + input [Width-1:0] rdata_o, + input [DepthW-1:0] depth_o +); + + ///////////////// + // Assumptions // + ///////////////// + + // no need to consider all possible input words + // 2-3 different values suffice + `ASSUME(WdataValues_M, wdata_i 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_i && wready_o && rvalid_o && rready_i) begin + fifo <= wdata_i; + end else if (wvalid_i && wready_o) begin + fifo <= wdata_i; + ref_depth <= ref_depth + 1; + end else if (rvalid_o && rready_i) begin + ref_depth <= ref_depth - 1; + end + end + end + end + + if (Pass) begin : gen_pass + assign ref_rdata = (ref_depth == 0) ? wdata_i : fifo; + end else begin : gen_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_i && wready_o && rvalid_o && rready_i) begin + fifo[wptr] <= wdata_i; + wptr <= modinc(wptr, Depth); + rptr <= modinc(rptr, Depth); + end else if (wvalid_i && wready_o) begin + fifo[wptr] <= wdata_i; + wptr <= modinc(wptr, Depth); + ref_depth <= ref_depth + 1; + end else if (rvalid_o && rready_i) 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_i : fifo[rptr]; + end else begin : gen_no_pass + assign ref_rdata = fifo[rptr]; + end + + end + + // check the data + `ASSERT(DataCheck_A, rvalid_o |-> rdata_o == ref_rdata) + // check the depth + `ASSERT(DepthCheck_A, ref_depth == depth_o) + + end + + //////////////////////// + // Forward Assertions // + //////////////////////// + + // assert depth of FIFO + `ASSERT(Depth_A, depth_o <= Depth) + // if we clear the FIFO, it must be empty in the next cycle + `ASSERT(CheckClrDepth_A, clr_i |=> depth_o == 0) + // check write on full + `ASSERT(WriteFull_A, depth_o == Depth && wvalid_i && !rready_i |=> depth_o == $past(depth_o), + clk_i, !rst_ni || clr_i) + // read empty + `ASSERT(ReadEmpty_A, depth_o == 0 && rready_i && !wvalid_i |=> depth_o == 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_o && wvalid_i && rvalid_o && rready_i |=> depth_o == $past(depth_o), + 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_o == 0) + // data is just passed through + `ASSERT(DataPassThru_A, wdata_i == rdata_o) + // FIFO is ready if downstream logic is ready + `ASSERT(Wready_A, rready_i == wready_o) + // valid input is valid output + `ASSERT(Rvalid_A, rvalid_o == wvalid_i) + // 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_o < Depth |-> wready_o) + // check rvalid + `ASSERT(Rvalid_A, depth_o > 0 |-> rvalid_o) + // check write only + `ASSERT(WriteOnly_A, wvalid_i && wready_o && !rready_i && depth_o < Depth |=> + depth_o == $past(depth_o) + 1, clk_i, !rst_ni || clr_i) + // check read only + `ASSERT(ReadOnly_A, !wvalid_i && rready_i && rvalid_o && depth_o > 0 |=> + depth_o == $past(depth_o) - 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_i == rvalid_o) + 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_o) + 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_o |-> depth_o < Depth || rready_i) + // elements ready to be read or upstream data is valid + `ASSERT(RvalidElemskBkwd_A, rvalid_o |-> depth_o > 0 || wvalid_i) + end else begin : gen_nopass_bkwd + // there is still space in the FIFO + `ASSERT(WreadySpacekBkwd_A, wready_o |-> depth_o < Depth) + // elements ready to be read + `ASSERT(RvalidElemskBkwd_A, rvalid_o |-> depth_o > 0) + end + + // no more space in the FIFO + `ASSERT(WreadyNoSpaceBkwd_A, !wready_o |-> depth_o == Depth) + // elements ready to be read + `ASSERT(RvalidNoElemskBkwd_A, !rvalid_o |-> depth_o == 0) + +endmodule : prim_fifo_sync_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv new file mode 100755 index 0000000..8e65474 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_22_16_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_22_16_assert_fpv ( + input clk_i, + input rst_ni, + input [15:0] data_i, + input [15:0] data_o, + input [21:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [21:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_22_16_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv new file mode 100755 index 0000000..31da73c --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_28_22_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_28_22_assert_fpv ( + input clk_i, + input rst_ni, + input [21:0] data_i, + input [21:0] data_o, + input [27:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [27:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_28_22_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv new file mode 100755 index 0000000..ba927d9 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_39_32_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_39_32_assert_fpv ( + input clk_i, + input rst_ni, + input [31:0] data_i, + input [31:0] data_o, + input [38:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [38:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_39_32_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv new file mode 100755 index 0000000..746d181 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_64_57_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_64_57_assert_fpv ( + input clk_i, + input rst_ni, + input [56:0] data_i, + input [56:0] data_o, + input [63:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [63:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_64_57_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv new file mode 100755 index 0000000..5313f5d --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_72_64_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_72_64_assert_fpv ( + input clk_i, + input rst_ni, + input [63:0] data_i, + input [63:0] data_o, + input [71:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [71:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_72_64_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv new file mode 100755 index 0000000..6e0a079 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_hamming_22_16_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_hamming_22_16_assert_fpv ( + input clk_i, + input rst_ni, + input [15:0] data_i, + input [15:0] data_o, + input [21:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [21:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_hamming_22_16_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv new file mode 100755 index 0000000..9bd5e30 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_hamming_39_32_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_hamming_39_32_assert_fpv ( + input clk_i, + input rst_ni, + input [31:0] data_i, + input [31:0] data_o, + input [38:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [38:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_hamming_39_32_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv new file mode 100755 index 0000000..c3eadf4 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_hamming_72_64_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_hamming_72_64_assert_fpv ( + input clk_i, + input rst_ni, + input [63:0] data_i, + input [63:0] data_o, + input [71:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [71:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_hamming_72_64_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_hamming_76_68_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_hamming_76_68_assert_fpv.sv new file mode 100755 index 0000000..748a091 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_hamming_76_68_assert_fpv.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_hamming_76_68_assert_fpv ( + input clk_i, + input rst_ni, + input [67:0] data_i, + input [67:0] data_o, + input [75:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [75:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + +endmodule : prim_secded_hamming_76_68_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_22_16_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_22_16_assert_fpv.sv new file mode 100755 index 0000000..2e9cb54 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_22_16_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_22_16_assert_fpv ( + input clk_i, + input rst_ni, + input [15:0] data_i, + input [15:0] data_o, + input [21:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [21:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_22_16_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_28_22_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_28_22_assert_fpv.sv new file mode 100755 index 0000000..42632ed --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_28_22_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_28_22_assert_fpv ( + input clk_i, + input rst_ni, + input [21:0] data_i, + input [21:0] data_o, + input [27:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [27:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_28_22_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_39_32_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_39_32_assert_fpv.sv new file mode 100755 index 0000000..0d0b7d7 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_39_32_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_39_32_assert_fpv ( + input clk_i, + input rst_ni, + input [31:0] data_i, + input [31:0] data_o, + input [38:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [38:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_39_32_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_64_57_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_64_57_assert_fpv.sv new file mode 100755 index 0000000..6c7e8ac --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_64_57_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_64_57_assert_fpv ( + input clk_i, + input rst_ni, + input [56:0] data_i, + input [56:0] data_o, + input [63:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [63:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_64_57_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_72_64_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_72_64_assert_fpv.sv new file mode 100755 index 0000000..625af52 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_72_64_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_72_64_assert_fpv ( + input clk_i, + input rst_ni, + input [63:0] data_i, + input [63:0] data_o, + input [71:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [71:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_72_64_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_hamming_22_16_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_hamming_22_16_assert_fpv.sv new file mode 100755 index 0000000..30b9bfe --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_hamming_22_16_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_22_16_assert_fpv ( + input clk_i, + input rst_ni, + input [15:0] data_i, + input [15:0] data_o, + input [21:0] encoded_o, + input [5:0] syndrome_o, + input [1:0] err_o, + input [21:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_hamming_22_16_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_hamming_39_32_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_hamming_39_32_assert_fpv.sv new file mode 100755 index 0000000..05a414a --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_hamming_39_32_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_39_32_assert_fpv ( + input clk_i, + input rst_ni, + input [31:0] data_i, + input [31:0] data_o, + input [38:0] encoded_o, + input [6:0] syndrome_o, + input [1:0] err_o, + input [38:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_hamming_39_32_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_hamming_72_64_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_hamming_72_64_assert_fpv.sv new file mode 100755 index 0000000..229ae7c --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_hamming_72_64_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_72_64_assert_fpv ( + input clk_i, + input rst_ni, + input [63:0] data_i, + input [63:0] data_o, + input [71:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [71:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_hamming_72_64_assert_fpv diff --git a/synth/prim/fpv/vip/prim_secded_inv_hamming_76_68_assert_fpv.sv b/synth/prim/fpv/vip/prim_secded_inv_hamming_76_68_assert_fpv.sv new file mode 100755 index 0000000..41024b9 --- /dev/null +++ b/synth/prim/fpv/vip/prim_secded_inv_hamming_76_68_assert_fpv.sv @@ -0,0 +1,37 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED FPV assertion file generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_76_68_assert_fpv ( + input clk_i, + input rst_ni, + input [67:0] data_i, + input [67:0] data_o, + input [75:0] encoded_o, + input [7:0] syndrome_o, + input [1:0] err_o, + input [75:0] error_inject_i +); + + // Inject a maximum of two errors simultaneously. + `ASSUME_FPV(MaxTwoErrors_M, $countones(error_inject_i) <= 2) + // Single bit error detection + `ASSERT(SingleErrorDetect_A, $countones(error_inject_i) == 1 |-> err_o[0]) + `ASSERT(SingleErrorDetectReverse_A, err_o[0] |-> $countones(error_inject_i) == 1) + // Double bit error detection + `ASSERT(DoubleErrorDetect_A, $countones(error_inject_i) == 2 |-> err_o[1]) + `ASSERT(DoubleErrorDetectReverse_A, err_o[1] |-> $countones(error_inject_i) == 2) + // Single bit error correction (implicitly tests the syndrome output) + `ASSERT(SingleErrorCorrect_A, $countones(error_inject_i) < 2 |-> data_i == data_o) + // Basic syndrome check + `ASSERT(SyndromeCheck_A, |syndrome_o |-> $countones(error_inject_i) > 0) + `ASSERT(SyndromeCheckReverse_A, $countones(error_inject_i) > 0 |-> |syndrome_o) + + // Check that all-one and all-zero data does not result in all-one or all-zero codewords + `ASSERT(AllZerosCheck_A, data_i == '0 |-> encoded_o != '0) + `ASSERT(AllOnesCheck_A, data_i == '1 |-> encoded_o != '1) + + +endmodule : prim_secded_inv_hamming_76_68_assert_fpv diff --git a/synth/prim/lint/prim.vlt b/synth/prim/lint/prim.vlt new file mode 100755 index 0000000..bff544e --- /dev/null +++ b/synth/prim/lint/prim.vlt @@ -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 + +`verilator_config + +// prim_arbiter_ppc +// ppc_out[i] depends on ppc_out[i-1] +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_ppc.sv" -match "*ppc_out*" + +// prim_lfsr +// lfsr_perm_test is just used for an SVA +lint_off -rule UNUSED -file "*/rtl/prim_lfsr.sv" -match "*lfsr_perm_test*" diff --git a/synth/prim/lint/prim.waiver b/synth/prim/lint/prim.waiver new file mode 100755 index 0000000..1a495fe --- /dev/null +++ b/synth/prim/lint/prim.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 +# +# waiver file for prim + +# 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" + +# prim_packer_fifo +waive -rules {ZERO_REP} -location {prim_packer_fifo.sv} -regexp {Replication count is zero in .*DepthW.*} \ + -comment "If InW equals OutW, DepthW is zero" + +# 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" + +# prim_fifo_async_simple +waive -rules CONST_FF -location {prim_fifo_async_simple.sv} -msg {Flip-flop 'not_in_reset_q' is driven by constant one in module 'prim_fifo_async_simple'} \ + -comment "The flop input and reset values are constants." diff --git a/synth/prim/lint/prim_and2.waiver b/synth/prim/lint/prim_and2.waiver new file mode 100755 index 0000000..25e90e7 --- /dev/null +++ b/synth/prim/lint/prim_and2.waiver @@ -0,0 +1,8 @@ +# 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_and2 + +waive -rules {STAR_PORT_CONN_USE} -location {prim_and2.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_arbiter.vlt b/synth/prim/lint/prim_arbiter.vlt new file mode 100755 index 0000000..42e44b2 --- /dev/null +++ b/synth/prim/lint/prim_arbiter.vlt @@ -0,0 +1,45 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// Tell the Verilator scheduler to split up these variables into +// separate pieces when it's figuring out process scheduling. This +// avoids spurious UNOPTFLAT warnings caused by the fact that the +// arrays feed into themselves (with different bits for different +// positions in the tree). +split_var -module "prim_arbiter_fixed" -var "data_tree" +split_var -module "prim_arbiter_fixed" -var "gnt_tree" +split_var -module "prim_arbiter_fixed" -var "idx_tree" +split_var -module "prim_arbiter_fixed" -var "req_tree" + +split_var -module "prim_arbiter_tree" -var "req_tree" +split_var -module "prim_arbiter_tree" -var "prio_tree" +split_var -module "prim_arbiter_tree" -var "sel_tree" +split_var -module "prim_arbiter_tree" -var "mask_tree" +split_var -module "prim_arbiter_tree" -var "idx_tree" +split_var -module "prim_arbiter_tree" -var "data_tree" + +// Waive ALWCOMBORDER warnings about the tree variables. We've got +// lines like "req_tree[Pa] = req_tree[C0] | req_tree[C1];" and it +// seems that the ALWCOMBORDER warning isn't affected by the split_var +// rules above. +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_fixed.sv" -match "*data_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_fixed.sv" -match "*gnt_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_fixed.sv" -match "*idx_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_fixed.sv" -match "*req_tree*" + +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*req_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*prio_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*sel_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*mask_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*idx_tree*" +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_tree.sv" -match "*data_tree*" + +lint_off -rule ALWCOMBORDER -file "*/rtl/prim_arbiter_ppc.sv" -match "*ppc_out*" + +// Waive unused clk and reset signals: they're just used for +// assertions (which Verilator doesn't see) +lint_off -rule UNUSED -file "*/rtl/prim_arbiter_fixed.sv" -match "*clk_i*" +lint_off -rule UNUSED -file "*/rtl/prim_arbiter_fixed.sv" -match "*rst_ni*" diff --git a/synth/prim/lint/prim_arbiter.waiver b/synth/prim/lint/prim_arbiter.waiver new file mode 100755 index 0000000..278740c --- /dev/null +++ b/synth/prim/lint/prim_arbiter.waiver @@ -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 +# +# waiver file for 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" + +waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_arbiter_fixed.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_arbiter_fixed'.*} \ + -comment "clk_ and rst_ni are only used for assertions in this module." diff --git a/synth/prim/lint/prim_assert.vlt b/synth/prim/lint/prim_assert.vlt new file mode 100755 index 0000000..affe750 --- /dev/null +++ b/synth/prim/lint/prim_assert.vlt @@ -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 + +`verilator_config diff --git a/synth/prim/lint/prim_assert.waiver b/synth/prim/lint/prim_assert.waiver new file mode 100755 index 0000000..83286a7 --- /dev/null +++ b/synth/prim/lint/prim_assert.waiver @@ -0,0 +1,16 @@ +# 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_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 prim_assert_sec_cm.svh} \ + -msg {Line length of} \ + -comment "Some macros cannot be line-wrapped, as some tools do not support that." +waive -rules {LINE_LENGTH} -location {prim_flop_macros.sv} -msg {Line length of} \ + -comment "Some macros cannot be line-wrapped, as some tools do not support that." diff --git a/synth/prim/lint/prim_buf.waiver b/synth/prim/lint/prim_buf.waiver new file mode 100755 index 0000000..ea04aab --- /dev/null +++ b/synth/prim/lint/prim_buf.waiver @@ -0,0 +1,8 @@ +# 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_buf + +waive -rules {STAR_PORT_CONN_USE} -location {prim_buf.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_cdc_rand_delay.vlt b/synth/prim/lint/prim_cdc_rand_delay.vlt new file mode 100755 index 0000000..6416525 --- /dev/null +++ b/synth/prim/lint/prim_cdc_rand_delay.vlt @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// When running this prim through Verilator, we stub out all of its contents. +// This, in turn, generates UNUSED warnings which we can waive here. No need to +// be fine-grained: this is DV code anyway. +lint_off -rule UNUSED -file "*/rtl/prim_cdc_rand_delay.sv" -match "*" diff --git a/synth/prim/lint/prim_cdc_rand_delay.waiver b/synth/prim/lint/prim_cdc_rand_delay.waiver new file mode 100755 index 0000000..db67e54 --- /dev/null +++ b/synth/prim/lint/prim_cdc_rand_delay.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_cdc_rand_delay + +waive -rules {IFDEF_CODE} -location {prim_cdc_rand_delay.sv} -regexp {.*contained within \`else block.*} \ + -comment "Ifdefs are required for prim_rand_cdc_delay since it is turned on only for simulation." + +waive -rules {HIER_BRANCH_NOT_READ} -location {prim_cdc_rand_delay.sv} -regexp {.*dst_clk.*} \ + -comment "Destination clock is only used when simulating random delays." + +waive -rules {INPUT_NOT_READ} -location {prim_cdc_rand_delay.sv} -regexp {clk_i|rst_ni|prev_data_i} \ + -comment "Clock, reset, and previous data are only used when simulating random delays." + +waive -rules {PARAM_NOT_USED} -location {prim_cdc_rand_delay.sv} -regexp {Enable} \ + -comment "Enable parameter is only used when simulating random delays." diff --git a/synth/prim/lint/prim_cipher.vlt b/synth/prim/lint/prim_cipher.vlt new file mode 100755 index 0000000..533b1cd --- /dev/null +++ b/synth/prim/lint/prim_cipher.vlt @@ -0,0 +1,16 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// Tell the Verilator scheduler to split up these variables into +// separate pieces when it's figuring out process scheduling. This +// avoids spurious UNOPTFLAT warnings caused by the fact that (if you +// don't track the indices carefully) it looks like the arrays feed +// into themselves. +split_var -module "prim_present" -var "data_state" +split_var -module "prim_present" -var "round_idx" +split_var -module "prim_present" -var "round_key" +split_var -module "prim_prince" -var "data_state_lo" +split_var -module "prim_prince" -var "data_state_hi" diff --git a/synth/prim/lint/prim_cipher_pkg.waiver b/synth/prim/lint/prim_cipher_pkg.waiver new file mode 100755 index 0000000..faf69ad --- /dev/null +++ b/synth/prim/lint/prim_cipher_pkg.waiver @@ -0,0 +1,8 @@ +# 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_cipher_pkg + +waive -rules {INTEGER} -location {prim_cipher_pkg.sv} -msg {'k' of type int used as a non-constant} \ + -comment "We need to use the iterator value in the keyschedule function, hence this is ok." diff --git a/synth/prim/lint/prim_clock_buf.waiver b/synth/prim/lint/prim_clock_buf.waiver new file mode 100755 index 0000000..9e030ad --- /dev/null +++ b/synth/prim/lint/prim_clock_buf.waiver @@ -0,0 +1,8 @@ +# 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_buf + +waive -rules {STAR_PORT_CONN_USE} -location {prim_clock_buf.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_clock_div.waiver b/synth/prim/lint/prim_clock_div.waiver new file mode 100755 index 0000000..c62bfac --- /dev/null +++ b/synth/prim/lint/prim_clock_div.waiver @@ -0,0 +1,14 @@ +# 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_div + +waive -rules {STAR_PORT_CONN_USE} -location {prim_clock_div.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." + +waive -rules CLOCK_MUX -location {prim_clock_div.sv} -regexp {.*reaches a multiplexer here, used as a clock.*} \ + -comment "A mux is used during scan bypass, and for switching between div by 2 and div by 1 clocks" + +waive -rules {SAME_NAME_TYPE} -location {prim_clock_div.sv} -regexp {'ResetValue' is used as a parameter here, and as an enumeration value at prim.*} \ + -comment "Parameter name reuse." diff --git a/synth/prim/lint/prim_clock_gating.waiver b/synth/prim/lint/prim_clock_gating.waiver new file mode 100755 index 0000000..0e10939 --- /dev/null +++ b/synth/prim/lint/prim_clock_gating.waiver @@ -0,0 +1,8 @@ +# 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_gating + +waive -rules {STAR_PORT_CONN_USE} -location {prim_clock_gating.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_clock_inv.waiver b/synth/prim/lint/prim_clock_inv.waiver new file mode 100755 index 0000000..0893e78 --- /dev/null +++ b/synth/prim/lint/prim_clock_inv.waiver @@ -0,0 +1,8 @@ +# 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_inv + +waive -rules {STAR_PORT_CONN_USE} -location {prim_clock_inv.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_clock_mux2.waiver b/synth/prim/lint/prim_clock_mux2.waiver new file mode 100755 index 0000000..6a82aaa --- /dev/null +++ b/synth/prim/lint/prim_clock_mux2.waiver @@ -0,0 +1,8 @@ +# 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 + +waive -rules {STAR_PORT_CONN_USE} -location {prim_clock_mux2.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_count.vlt b/synth/prim/lint/prim_count.vlt new file mode 100755 index 0000000..b47f59e --- /dev/null +++ b/synth/prim/lint/prim_count.vlt @@ -0,0 +1,8 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// This parameter is only used in DV/FPV. +lint_off -rule UNUSED -file "*/rtl/prim_count.sv" -match "*EnableAlertTriggerSVA*" diff --git a/synth/prim/lint/prim_count.waiver b/synth/prim/lint/prim_count.waiver new file mode 100755 index 0000000..c0b59c9 --- /dev/null +++ b/synth/prim/lint/prim_count.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 +# +# waiver file for prim_count + +waive -rules {PARAM_NOT_USED} -location {prim_count.sv} -regexp {.*EnableAlertTriggerSVA.*} \ + -comment "The disable parameter is used only during DV / FPV." + +waive -rules {IFDEF_CODE} -location {prim_count.sv} -msg {Assignment to 'fpv_force' contained within `ifndef 'FPV_SEC_CM_ON' block at} \ + -comment "This ifdef segment is ok, since it is used to provide the tool with a symbolic variable for error injection during FPV." diff --git a/synth/prim/lint/prim_crc32.vlt b/synth/prim/lint/prim_crc32.vlt new file mode 100755 index 0000000..85805e6 --- /dev/null +++ b/synth/prim/lint/prim_crc32.vlt @@ -0,0 +1,7 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +split_var -module "prim_crc32" -var "crc_stages" diff --git a/synth/prim/lint/prim_double_lfsr.vlt b/synth/prim/lint/prim_double_lfsr.vlt new file mode 100755 index 0000000..fe7645d --- /dev/null +++ b/synth/prim/lint/prim_double_lfsr.vlt @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// The EnableAlertTriggerSVA parameter is only used by modules bound +// in for DV testing. Waive the warning that we don't read it. +lint_off -rule UNUSED -file "*/rtl/prim_double_lfsr.sv" -match "*EnableAlertTriggerSVA*" + diff --git a/synth/prim/lint/prim_double_lfsr.waiver b/synth/prim/lint/prim_double_lfsr.waiver new file mode 100755 index 0000000..5cbfa7e --- /dev/null +++ b/synth/prim/lint/prim_double_lfsr.waiver @@ -0,0 +1,8 @@ +# 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_double_lfsr + +waive -rules {PARAM_NOT_USED} -location {prim_double_lfsr.sv} -regexp {.*EnableAlertTriggerSVA.*} \ + -comment "The disable parameter is used only during DV / FPV." diff --git a/synth/prim/lint/prim_fifo.vlt b/synth/prim/lint/prim_fifo.vlt new file mode 100755 index 0000000..a05e937 --- /dev/null +++ b/synth/prim/lint/prim_fifo.vlt @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// prim_fifo_sync +// In passthrough mode, clk and reset are not read form within this module +lint_off -rule UNUSED -file "*/rtl/prim_fifo_sync.sv" -match "*clk_i*" +lint_off -rule UNUSED -file "*/rtl/prim_fifo_sync.sv" -match "*rst_ni*" diff --git a/synth/prim/lint/prim_fifo.waiver b/synth/prim/lint/prim_fifo.waiver new file mode 100755 index 0000000..bf1bb22 --- /dev/null +++ b/synth/prim/lint/prim_fifo.waiver @@ -0,0 +1,31 @@ +# 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_fifo + +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" + +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" + + +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." + +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 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" + +## prim_fifo_async_sram_adapter +waive -rules ARITH_CONTEXT -location {prim_fifo_async_sram_adapter.sv} \ + -regexp {(r|w)_wptr_v.*_rptr_v} \ + -comment "The pointer value width is determined. Remove the casting for readability" diff --git a/synth/prim/lint/prim_flash.waiver b/synth/prim/lint/prim_flash.waiver new file mode 100755 index 0000000..3833a52 --- /dev/null +++ b/synth/prim/lint/prim_flash.waiver @@ -0,0 +1,8 @@ +# 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_flash + +waive -rules {STAR_PORT_CONN_USE} -location {prim_flash.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_flop.waiver b/synth/prim/lint/prim_flop.waiver new file mode 100755 index 0000000..8659ddd --- /dev/null +++ b/synth/prim/lint/prim_flop.waiver @@ -0,0 +1,8 @@ +# 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_flop + +waive -rules {STAR_PORT_CONN_USE} -location {prim_flop.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_flop_2sync.waiver b/synth/prim/lint/prim_flop_2sync.waiver new file mode 100755 index 0000000..ea071eb --- /dev/null +++ b/synth/prim/lint/prim_flop_2sync.waiver @@ -0,0 +1,14 @@ +# 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_flop_2sync + +waive -rules {IFDEF_CODE} -location {prim_flop_2sync.sv} -regexp {.*contained within \`else block.*} \ + -comment "Ifdefs are required for prim_flop_2sync since it is turned on only for simulation." + +waive -rules {PARAM_NOT_USED} -location {prim_flop_2sync.sv} -regexp {Parameter 'EnablePrimCdcRand' not used in module.*} \ + -comment "This parameter is used when cdc instrumentation is enabled." + +waive -rules {SAME_NAME_TYPE} -location {prim_flop_2sync.sv} -regexp {'ResetValue' is used as a parameter here, and as an enumeration value at prim.*} \ + -comment "Parameter name reuse." diff --git a/synth/prim/lint/prim_flop_en.waiver b/synth/prim/lint/prim_flop_en.waiver new file mode 100755 index 0000000..3ccaaa7 --- /dev/null +++ b/synth/prim/lint/prim_flop_en.waiver @@ -0,0 +1,8 @@ +# 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_flop_en + +waive -rules {STAR_PORT_CONN_USE} -location {prim_flop_en.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_lc_sender.waiver b/synth/prim/lint/prim_lc_sender.waiver new file mode 100755 index 0000000..e1b921e --- /dev/null +++ b/synth/prim/lint/prim_lc_sender.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 +# +# waiver file for prim_mubi modules + +waive -rules {SAME_NAME_TYPE} -location {prim_lc_sender.sv} -regexp {'ResetValue' is used as an enumeration value here, and as a parameter at prim.*} \ + -comment "Parameter name reuse" + + + diff --git a/synth/prim/lint/prim_lfsr.waiver b/synth/prim/lint/prim_lfsr.waiver new file mode 100755 index 0000000..0ce77e4 --- /dev/null +++ b/synth/prim/lint/prim_lfsr.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_lfsr + +# TODO: this lint waiver may not be necessary in future AscentLint versions. check and remove if possible. +waive -rules {LOOP_VAR_OP} -location {prim_lfsr.sv} -msg {Loop variable 'k' is in arithmetic expression '(k + shift) % gen_out_non_linear.NumSboxes' with non-constant terms} \ + -comment "This message is a false positive in this context, since the function inputs are constant." diff --git a/synth/prim/lint/prim_max_tree.vlt b/synth/prim/lint/prim_max_tree.vlt new file mode 100755 index 0000000..fe2485c --- /dev/null +++ b/synth/prim/lint/prim_max_tree.vlt @@ -0,0 +1,18 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +`verilator_config + +// Tell the Verilator scheduler to split up these variables into +// separate pieces when it's figuring out process scheduling. This +// avoids spurious UNOPTFLAT warnings caused by the fact that the +// arrays feed into themselves (with different bits for different +// positions in the tree). +split_var -module "prim_max_tree" -var "max_tree" +split_var -module "prim_max_tree" -var "vld_tree" +split_var -module "prim_max_tree" -var "idx_tree" + +// The clock and reset are only used for assertions in this module. +lint_off -rule UNUSED -file "*/rtl/prim_max_tree.sv" -match "Signal is not used: 'clk_i'" +lint_off -rule UNUSED -file "*/rtl/prim_max_tree.sv" -match "Signal is not used: 'rst_ni'" diff --git a/synth/prim/lint/prim_max_tree.waiver b/synth/prim/lint/prim_max_tree.waiver new file mode 100755 index 0000000..8708020 --- /dev/null +++ b/synth/prim/lint/prim_max_tree.waiver @@ -0,0 +1,8 @@ +# 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_arbiter + +waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_max_tree.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_max_tree'.*} \ + -comment "clk_ and rst_ni are only used for assertions in this module." diff --git a/synth/prim/lint/prim_mubi.waiver b/synth/prim/lint/prim_mubi.waiver new file mode 100755 index 0000000..c7527fb --- /dev/null +++ b/synth/prim/lint/prim_mubi.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 +# +# waiver file for prim_mubi modules + +waive -rules {SAME_NAME_TYPE} -location {prim_mubi*.sv} -regexp {'ResetValue' is used as an enumeration value here, and as a parameter at prim.*} \ + -comment "Parameter name reuse" + + + diff --git a/synth/prim/lint/prim_onehot_check.vlt b/synth/prim/lint/prim_onehot_check.vlt new file mode 100755 index 0000000..d7d927b --- /dev/null +++ b/synth/prim/lint/prim_onehot_check.vlt @@ -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 + +`verilator_config + +// Tell the Verilator scheduler to split up these variables into +// separate pieces when it's figuring out process scheduling. This +// avoids spurious UNOPTFLAT warnings caused by the fact that the +// arrays feed into themselves (with different bits for different +// positions in the tree). +split_var -module "prim_onehot_check" -var "or_tree" +split_var -module "prim_onehot_check" -var "and_tree" +split_var -module "prim_onehot_check" -var "err_tree" + +// The clock and reset are only used for assertions in this module. +lint_off -rule UNUSED -file "*/rtl/prim_onehot_check.sv" -match "Signal is not used: 'clk_i'" +lint_off -rule UNUSED -file "*/rtl/prim_onehot_check.sv" -match "Signal is not used: 'rst_ni'" + +// The EnableAlertTriggerSVA parameter is only used by modules bound +// in for DV testing. Waive the warning that we don't read it. +lint_off -rule UNUSED -file "*/rtl/prim_onehot_check.sv" -match "*EnableAlertTriggerSVA*" diff --git a/synth/prim/lint/prim_onehot_check.waiver b/synth/prim/lint/prim_onehot_check.waiver new file mode 100755 index 0000000..65d5b0b --- /dev/null +++ b/synth/prim/lint/prim_onehot_check.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 +# +# waiver file for prim_onehot_check + +waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_onehot_check.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_onehot_check'.*} \ + -comment "clk_ and rst_ni are only used for assertions in this module." + +waive -rules {PARAM_NOT_USED} -location {prim_onehot_check.sv} -regexp {.*EnableAlertTriggerSVA.*} \ + -comment "The disable parameter is used only during DV / FPV." diff --git a/synth/prim/lint/prim_onehot_mux.waiver b/synth/prim/lint/prim_onehot_mux.waiver new file mode 100755 index 0000000..f4b38e2 --- /dev/null +++ b/synth/prim/lint/prim_onehot_mux.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 +# +# waiver file for prim_onehot_mux + +waive -rules {CLOCK_USE} -location {prim_onehot_mux.sv} -msg {'clk_i' is connected to 'prim_onehot_mux' port 'clk_i', and used as a clock at} \ + -comment "clk_i and rst_ni are only used for assertions in this module." + +waive -rules {RESET_USE} -location {prim_onehot_mux.sv} -msg {'rst_ni' is connected to 'prim_onehot_mux' port 'rst_ni', and used as an asynchronous reset or set at } \ + -comment "clk_i and rst_ni are only used for assertions in this module." diff --git a/synth/prim/lint/prim_otp.waiver b/synth/prim/lint/prim_otp.waiver new file mode 100755 index 0000000..593bbc5 --- /dev/null +++ b/synth/prim/lint/prim_otp.waiver @@ -0,0 +1,8 @@ +# 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_otp + +waive -rules {STAR_PORT_CONN_USE} -location {prim_otp.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_pad_attr.waiver b/synth/prim/lint/prim_pad_attr.waiver new file mode 100755 index 0000000..e7bbc69 --- /dev/null +++ b/synth/prim/lint/prim_pad_attr.waiver @@ -0,0 +1,8 @@ +# 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_pad_attr + +waive -rules {STAR_PORT_CONN_USE} -location {prim_pad_attr.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_pad_wrapper.waiver b/synth/prim/lint/prim_pad_wrapper.waiver new file mode 100755 index 0000000..c03e039 --- /dev/null +++ b/synth/prim/lint/prim_pad_wrapper.waiver @@ -0,0 +1,8 @@ +# 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_pad_wrapper + +waive -rules {STAR_PORT_CONN_USE} -location {prim_pad_wrapper.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_ram_1p.waiver b/synth/prim/lint/prim_ram_1p.waiver new file mode 100755 index 0000000..5e3de12 --- /dev/null +++ b/synth/prim/lint/prim_ram_1p.waiver @@ -0,0 +1,8 @@ +# 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_ram_1p + +waive -rules {STAR_PORT_CONN_USE} -location {prim_ram_1p.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_ram_1p_adv.waiver b/synth/prim/lint/prim_ram_1p_adv.waiver new file mode 100755 index 0000000..6e49dff --- /dev/null +++ b/synth/prim/lint/prim_ram_1p_adv.waiver @@ -0,0 +1,8 @@ +# 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_ram_1p_adv + +waive -rules {CONST_FF} -location {prim_ram_1p_adv.sv} -regexp {.*Flip-flop 'wmask_q' is driven by constant ones.*EnableECC=1'h1.*} \ + -comment "This particular instance is ok since we do not use the wmask when ECC is enabled." diff --git a/synth/prim/lint/prim_ram_1p_scr.vlt b/synth/prim/lint/prim_ram_1p_scr.vlt new file mode 100755 index 0000000..3397d48 --- /dev/null +++ b/synth/prim/lint/prim_ram_1p_scr.vlt @@ -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 + +`verilator_config + +// prim_ram_1p_scr +// waive warnings on comparing 'addr_cnt_q' with 32 bit parameters +lint_off -rule WIDTH -file "*/rtl/prim_ram_1p_scr.sv" -match "Operator * expects 32 bits on the LHS, but LHS's VARREF 'addr_cnt_q' generates * bits." diff --git a/synth/prim/lint/prim_ram_1r1w.waiver b/synth/prim/lint/prim_ram_1r1w.waiver new file mode 100755 index 0000000..b247f1f --- /dev/null +++ b/synth/prim/lint/prim_ram_1r1w.waiver @@ -0,0 +1,8 @@ +# 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_ram_1r1w + +waive -rules {STAR_PORT_CONN_USE} -location {prim_ram_1r1w.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_ram_2p.waiver b/synth/prim/lint/prim_ram_2p.waiver new file mode 100755 index 0000000..474975e --- /dev/null +++ b/synth/prim/lint/prim_ram_2p.waiver @@ -0,0 +1,8 @@ +# 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_ram_2p + +waive -rules {STAR_PORT_CONN_USE} -location {prim_ram_2p.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_reg_we_check.waiver b/synth/prim/lint/prim_reg_we_check.waiver new file mode 100755 index 0000000..0c13a94 --- /dev/null +++ b/synth/prim/lint/prim_reg_we_check.waiver @@ -0,0 +1,8 @@ +# 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_reg_we_check + +waive -rules {HIER_BRANCH_NOT_READ} -location {prim_reg_we_check.sv} -regexp {.*'(clk_i|rst_ni)'.*} \ + -comment "clk_i and rst_ni are only used for assertions in this module." diff --git a/synth/prim/lint/prim_rom.waiver b/synth/prim/lint/prim_rom.waiver new file mode 100755 index 0000000..5b588b4 --- /dev/null +++ b/synth/prim/lint/prim_rom.waiver @@ -0,0 +1,8 @@ +# 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_rom + +waive -rules {STAR_PORT_CONN_USE} -location {prim_rom.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_rst_sync.waiver b/synth/prim/lint/prim_rst_sync.waiver new file mode 100755 index 0000000..e05268b --- /dev/null +++ b/synth/prim/lint/prim_rst_sync.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_rst_sync diff --git a/synth/prim/lint/prim_secded.waiver b/synth/prim/lint/prim_secded.waiver new file mode 100755 index 0000000..ace5295 --- /dev/null +++ b/synth/prim/lint/prim_secded.waiver @@ -0,0 +1,8 @@ +# 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_secded + +waive -rules {SAME_NAME_TYPE} -location {*} -regexp {'prim_secded_.*' is used as a module here, and as a function at prim_secded_pkg.sv.*} \ + -comment "The secded functions and primitives may have the same name." diff --git a/synth/prim/lint/prim_sha2.vbl b/synth/prim/lint/prim_sha2.vbl new file mode 100755 index 0000000..909ffbd --- /dev/null +++ b/synth/prim/lint/prim_sha2.vbl @@ -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_sha2 pre_dv Verilator testbenches + +# waive long line violations in test vectors feeding +waive --rule=line-length --location="prim_sha_multimode32_tb.sv" +waive --rule=line-length --location="prim_sha_tb.sv" diff --git a/synth/prim/lint/prim_sha2.vlt b/synth/prim/lint/prim_sha2.vlt new file mode 100755 index 0000000..0fdd4a5 --- /dev/null +++ b/synth/prim/lint/prim_sha2.vlt @@ -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 sha-2 + +`verilator_config + +// The wipe_secret and wipe_v inputs to hmac_core and sha2_pad are not +// currently used, but we're keeping them attached for future use. +lint_off -rule UNUSED -file "*/rtl/prim_sha2_pad.sv" -match "Signal is not used: 'wipe_secret'" +lint_off -rule UNUSED -file "*/rtl/prim_sha2_pad.sv" -match "Signal is not used: 'wipe_v'" diff --git a/synth/prim/lint/prim_sha2.waiver b/synth/prim/lint/prim_sha2.waiver new file mode 100755 index 0000000..16df310 --- /dev/null +++ b/synth/prim/lint/prim_sha2.waiver @@ -0,0 +1,45 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# waiver file for SHA-2: prim_sha2, prim_sha2_pad, prim_sha2_32, prim_sha2_pkg + +waive -rules {CONST_FF RESET_ONLY PARTIAL_CONST_ASSIGN} -location {prim_sha2.sv} -regexp {processed_length\[8:0\]} \ + -comment "lower 512bits of message are aligned. So ignoring txcount for lower 9 bits" +waive -rules {CONST_FF RESET_ONLY PARTIAL_CONST_ASSIGN} -location {prim_sha2_pad.sv} -regexp {tx_count\[4:0\]} \ + -comment "lower 32bits of message are aligned. So ignoring txcount for lower 5 bits" +waive -rules {NOT_READ HIER_NET_NOT_READ CONST_OUTPUT} -location {prim_sha2_pad.sv prim_sha2.sv} \ + -regexp {padded_length\[8:0\]} \ + -comment "lower 512bits of padded message are 0 (always aligned message)" + +waive -rules {EXPLICIT_BITLEN} -location {prim_sha2.sv} -regexp {.*(0|1)} \ + -comment "Added or subtracted by 1" + +waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_sha2_pad.sv} -regexp {wipe_(secret|v)} \ + -comment "Not used but remained for future use" + +waive -rules {NOT_READ} -location {*_reg_top.sv} -regexp {(address|param|user)} \ + -comment "Register module waiver" + +# ARITH_CONTEXT +waive -rules {ARITH_CONTEXT} -location {prim_sha2.sv} -regexp {Bitlength of arithmetic operation '.processed_length.63:9. \+ 1'b1.'} \ + -comment "Bitwidth overflow is intended" +waive -rules {ARITH_CONTEXT} -location {prim_sha2_pad.sv} -regexp {Bitlength of arithmetic operation 'tx_count.63:5. \+ 2'd1'} \ + -comment "Bitwidth overflow is intended" +waive -rules {ARITH_CONTEXT} -location {prim_sha2_pad.sv} -regexp {Bitlength of arithmetic operation 'message_length.63:9. \+ (1'b1|2'b10)'} \ + -comment "Bitwidth overflow is intended" +waive -rules {ARITH_CONTEXT} -location {prim_sha2_pkg.sv} -regexp {Bitlength of arithmetic operation 'h_i\[3\]\[31:0\] \+ temp1' is self-determined in this context} \ + -comment "Bitwidth overflow is intended" +waive -rules {ARITH_CONTEXT} -location {prim_sha2_pkg.sv} -regexp {Bitlength of arithmetic operation '\(temp1 \+ temp2\)' is self-determined in this context} \ + -comment "Bitwidth overflow is intended" + +# INPUT_NOT_READ +waive -rules {INPUT_NOT_READ} -location {prim_sha2_pkg.sv} -regexp {Input port 'h_i\[0:7\]\[63:32\]' is not read from in function 'compress_multi_256'} + -comment "Upper bits are only used in SHA2-384/512" + +# INTEGER +waive -rules {INTEGER} -location {prim_sha2_pkg.sv} -regexp {'amt' of type int used as a} +waive -rules {TWO_STATE_TYPE} -location {prim_sha2_pkg.sv} -regexp {'amt' is of two state type 'int'} \ + -comment "shift function behaves as static, it is called with constant in the design" +waive -rules {INTEGER} -location {prim_sha2_pkg.sv} -regexp {'amt' of type integer used as a non-constant value} \ + -comment "rotate function behaves as static function - it is called with a constant value in the design" diff --git a/synth/prim/lint/prim_sparse_fsm_flop.vlt b/synth/prim/lint/prim_sparse_fsm_flop.vlt new file mode 100755 index 0000000..1ae4299 --- /dev/null +++ b/synth/prim/lint/prim_sparse_fsm_flop.vlt @@ -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 + +`verilator_config + +// This parameter is only used in DV/FPV. +lint_off -rule UNUSED -file "*/rtl/prim_sparse_fsm_flop.sv" -match "*EnableAlertTriggerSVA*" +lint_off -rule UNUSED -file "*/rtl/prim_sparse_fsm_flop.sv" -match "*CustomForceName*" diff --git a/synth/prim/lint/prim_sparse_fsm_flop.waiver b/synth/prim/lint/prim_sparse_fsm_flop.waiver new file mode 100755 index 0000000..03a5ee8 --- /dev/null +++ b/synth/prim/lint/prim_sparse_fsm_flop.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_sparse_fsm_flop + +waive -rules {IFDEF_CODE} -location {prim_sparse_fsm_flop.sv} -regexp {.*(unused_valid_st|unused_err_o).*} \ + -comment "The unused_valid_st / unused_err_o signals are used purely for DV only and is switched to a constant during lint / synth." + +waive -rules {PARAM_NOT_USED} -location {prim_sparse_fsm_flop.sv} -regexp {.*StateEnumT.*} \ + -comment "The state enum is used only during DV / FPV." + +waive -rules {PARAM_NOT_USED} -location {prim_sparse_fsm_flop.sv} -regexp {.*EnableAlertTriggerSVA.*} \ + -comment "The disable parameter is used only during DV / FPV." + +waive -rules {SAME_NAME_TYPE} -location {prim_sparse_fsm_flop.sv} -regexp {.*ResetValue.*} \ + -comment "The ResetValue parameter is a common name used by many prim types" diff --git a/synth/prim/lint/prim_subreg.vlt b/synth/prim/lint/prim_subreg.vlt new file mode 100755 index 0000000..620f1d2 --- /dev/null +++ b/synth/prim/lint/prim_subreg.vlt @@ -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 + +`verilator_config + +// prim_subreg +// for RO wd is not used +lint_off -rule UNUSED -file "*/rtl/prim_subreg.sv" -match "Signal is not used: 'wd'" diff --git a/synth/prim/lint/prim_subreg.waiver b/synth/prim/lint/prim_subreg.waiver new file mode 100755 index 0000000..fe37834 --- /dev/null +++ b/synth/prim/lint/prim_subreg.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 + +waive -rules INPUT_NOT_READ -location {prim_subreg.sv} -regexp {Input port 'wd' is not read from} \ + -comment "for RO wd is not used" + +waive -rules {PARAM_NOT_USED} -location {prim_subreg_shadow.sv} -regexp {Mubi} \ + -comment "Mubi is not yet supported in prim_subreg_shadow." diff --git a/synth/prim/lint/prim_sum_tree.vlt b/synth/prim/lint/prim_sum_tree.vlt new file mode 100755 index 0000000..bb990bd --- /dev/null +++ b/synth/prim/lint/prim_sum_tree.vlt @@ -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 + +`verilator_config + +// Tell the Verilator scheduler to split up these variables into +// separate pieces when it's figuring out process scheduling. This +// avoids spurious UNOPTFLAT warnings caused by the fact that the +// arrays feed into themselves (with different bits for different +// positions in the tree). +split_var -module "prim_sum_tree" -var "sum_tree" +split_var -module "prim_sum_tree" -var "vld_tree" + +// The clock and reset are only used for assertions in this module. +lint_off -rule UNUSED -file "*/rtl/prim_sum_tree.sv" -match "Signal is not used: 'clk_i'" +lint_off -rule UNUSED -file "*/rtl/prim_sum_tree.sv" -match "Signal is not used: 'rst_ni'" diff --git a/synth/prim/lint/prim_sum_tree.waiver b/synth/prim/lint/prim_sum_tree.waiver new file mode 100755 index 0000000..7f2fa7c --- /dev/null +++ b/synth/prim/lint/prim_sum_tree.waiver @@ -0,0 +1,8 @@ +# 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_arbiter + +waive -rules {HIER_BRANCH_NOT_READ INPUT_NOT_READ} -location {prim_sum_tree.sv} -regexp {.*'(clk_i|rst_ni)' is not read from in module 'prim_sum_tree'.*} \ + -comment "clk_ and rst_ni are only used for assertions in this module." diff --git a/synth/prim/lint/prim_trivium.vlt b/synth/prim/lint/prim_trivium.vlt new file mode 100755 index 0000000..d6149e3 --- /dev/null +++ b/synth/prim/lint/prim_trivium.vlt @@ -0,0 +1,7 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// waiver file for Trivium/Bivium stream cipher primitives + +`verilator_config diff --git a/synth/prim/lint/prim_trivium.waiver b/synth/prim/lint/prim_trivium.waiver new file mode 100755 index 0000000..5f6dec1 --- /dev/null +++ b/synth/prim/lint/prim_trivium.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 Trivium/Bivium stream cipher primitives diff --git a/synth/prim/lint/prim_usb_diff_rx.waiver b/synth/prim/lint/prim_usb_diff_rx.waiver new file mode 100755 index 0000000..30c0bdb --- /dev/null +++ b/synth/prim/lint/prim_usb_diff_rx.waiver @@ -0,0 +1,8 @@ +# 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_usb_diff_rx + +waive -rules {STAR_PORT_CONN_USE} -location {prim_usb_diff_rx.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_xnor2.waiver b/synth/prim/lint/prim_xnor2.waiver new file mode 100755 index 0000000..77f1c6a --- /dev/null +++ b/synth/prim/lint/prim_xnor2.waiver @@ -0,0 +1,8 @@ +# 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_xnor2 + +waive -rules {STAR_PORT_CONN_USE} -location {prim_xnor2.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_xor2.waiver b/synth/prim/lint/prim_xor2.waiver new file mode 100755 index 0000000..c2625ae --- /dev/null +++ b/synth/prim/lint/prim_xor2.waiver @@ -0,0 +1,8 @@ +# 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_xor2 + +waive -rules {STAR_PORT_CONN_USE} -location {prim_xor2.sv} -regexp {.*wild card port connection encountered on instance.*} \ + -comment "Generated prims may have wildcard connections." diff --git a/synth/prim/lint/prim_xoshiro256pp.vlt b/synth/prim/lint/prim_xoshiro256pp.vlt new file mode 100755 index 0000000..4fa210f --- /dev/null +++ b/synth/prim/lint/prim_xoshiro256pp.vlt @@ -0,0 +1,10 @@ +// 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_xoshiro256pp + +`verilator_config + +// In the following array some members may depend on others. +split_var -module "prim_xoshiro256pp" -var "unrolled_state" diff --git a/synth/prim/pre_dv/prim_crc32/README.md b/synth/prim/pre_dv/prim_crc32/README.md new file mode 100755 index 0000000..3bc3ff0 --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/README.md @@ -0,0 +1,20 @@ +CRC32 Calculator Testbench +========================== + +This is a primitive testbench to check the basic functionality of the CRC32 +primitive. It is not intended as a full verification environment. + +It is built via fusesoc (from repository root) + + ```sh + fusesoc --cores-root=. run --target=sim --setup --build lowrisc:prim:crc32_sim + ./build/lowrisc_prim_crc32_sim_0/sim-verilator/Vprim_crc32_sim + ``` + +`predv_expected.txt` contains the expected output which can be generated by the +`expected_out.py` python script. This is simply a dump of expected CRC values +as test data is fed in. + +The `run_predv.sh` script will build and run the simulator and diff the output +against the expected output, producing an error if this results in a mismatch or +any other part of the process fails. diff --git a/synth/prim/pre_dv/prim_crc32/expected_out.py b/synth/prim/pre_dv/prim_crc32/expected_out.py new file mode 100755 index 0000000..62a27ee --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/expected_out.py @@ -0,0 +1,18 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Generates expected outputs for simple prim_crc32 testbench + +import binascii + +test_data = 0xdeadbeef +crc = 0x0 +for i in range(2, 100): + print(f'{crc:08x}') + test_data_bytes = test_data.to_bytes(4, byteorder='little') + crc = binascii.crc32(test_data_bytes, crc) + test_count_bytes = i.to_bytes(1, byteorder='little') + crc = binascii.crc32(test_count_bytes, crc) + crc = binascii.crc32(test_count_bytes, crc) + test_data += 1 diff --git a/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.cc b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.cc new file mode 100755 index 0000000..9403703 --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.cc @@ -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 + +#include +#include + +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.h" + +int main(int argc, char **argv) { + prim_crc32_sim top; + VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance(); + simctrl.SetTop(&top, &top.IO_CLK, &top.IO_RST_N, + VerilatorSimCtrlFlags::ResetPolarityNegative); + + bool exit_app = false; + int ret_code = simctrl.ParseCommandArgs(argc, argv, exit_app); + if (exit_app) { + return ret_code; + } + + std::cout << "Simulation" << std::endl + << "==================" << std::endl + << std::endl; + + simctrl.RunSimulation(); + + if (!simctrl.WasSimulationSuccessful()) { + return 1; + } + + return 0; +} diff --git a/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.core b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.core new file mode 100755 index 0000000..7c6e9d4 --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.core @@ -0,0 +1,61 @@ +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:crc32_sim" +description: "Verilator simulation of CRC32 calculation block" + +filesets: + files_crc32: + depend: + - lowrisc:prim:crc32 + files_verilator: + depend: + - lowrisc:dv_verilator:simutil_verilator + files: + - prim_crc32_sim.cc: { file_type: cppSource } + - prim_crc32_sim.sv: { file_type: systemVerilogSource } + +targets: + default: &default_target + filesets: + - files_crc32 + - files_verilator + toplevel: prim_crc32_sim + + lint: + <<: *default_target + default_tool: verilator + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" + # RAM primitives wider than 64bit (required for ECC) fail to build in + # Verilator without increasing the unroll count (see Verilator#1266) + - "--unroll-count 72" + + sim: + <<: *default_target + default_tool: verilator + tools: + vcs: + vcs_options: + - '-xlrm uniq_prior_final' + - '-debug_access+r' + verilator: + mode: cc + verilator_options: + # Disabling tracing reduces compile times but doesn't have a + # huge influence on runtime performance. + - '--trace' + - '--trace-fst' # this requires -DVM_TRACE_FMT_FST in CFLAGS below! + - '--trace-structs' + - '--trace-params' + - '--trace-max-array 1024' + - '-CFLAGS "-std=c++11 -Wall -DVM_TRACE_FMT_FST -DTOPLEVEL_NAME=prim_crc32_sim"' + - '-LDFLAGS "-pthread -lutil -lelf"' + - "-Wall" + # RAM primitives wider than 64bit (required for ECC) fail to build in + # Verilator without increasing the unroll count (see Verilator#1266) + - "--unroll-count 72" diff --git a/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.sv b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.sv new file mode 100755 index 0000000..041da16 --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/prim_crc32_sim.sv @@ -0,0 +1,50 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module prim_crc32_sim ( + input IO_CLK, + input IO_RST_N +); + logic [47:0] test_crc_in; + logic [31:0] test_data; + logic [31:0] crc_out; + logic [31:0] cnt; + logic set_crc; + + assign test_crc_in = {cnt[7:0], cnt[7:0], test_data}; + + always @(posedge IO_CLK or negedge IO_RST_N) begin + if (!IO_RST_N) begin + test_data <= 32'hdeadbeee; + cnt <= '0; + set_crc <= 1'b0; + end else begin + if (cnt == 0) begin + set_crc <= 1'b1; + cnt <= cnt + 32'd1; + end else if (cnt < 100) begin + set_crc <= 1'b0; + test_data <= test_data + 32'd1; + cnt <= cnt + 32'd1; + + $display("%08x", crc_out); + end else begin + $finish(); + end + end + end + + prim_crc32 #(.BytesPerWord(6)) dut ( + .clk_i(IO_CLK), + .rst_ni(IO_RST_N), + + .set_crc_i(set_crc), + .crc_in_i(32'h00000000), + + .data_valid_i(~set_crc), + .data_i(test_crc_in), + .crc_out_o(crc_out) + ); + +endmodule diff --git a/synth/prim/pre_dv/prim_crc32/run_predv.sh b/synth/prim/pre_dv/prim_crc32/run_predv.sh new file mode 100755 index 0000000..d10fe46 --- /dev/null +++ b/synth/prim/pre_dv/prim_crc32/run_predv.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Runs the CRC32 pre-dv testbench (build simulation, +# runs simulation and checks expected output) + +fail() { + echo >&2 "PRE-DV FAILURE: $*" + exit 1 +} + +set -o pipefail + +SCRIPT_DIR="$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")" +UTIL_DIR="$(readlink -e "$SCRIPT_DIR/../../../../../util")" || \ + fail "Can't find OpenTitan util dir" + +source "$UTIL_DIR/build_consts.sh" + +PREDV_DIR=$REPO_TOP/hw/ip/prim/pre_dv/prim_crc32 + +(cd $REPO_TOP || exit; + fusesoc --cores-root=. run --target=sim --setup --build \ + lowrisc:prim:crc32_sim || fail "HW Sim build failed") + +RUN_LOG=`mktemp` +readonly RUN_LOG +# shellcheck disable=SC2064 # The RUN_LOG tempfile path should not change +trap "rm -rf $RUN_LOG" EXIT + +timeout 5s \ + $REPO_TOP/build/lowrisc_prim_crc32_sim_0/sim-verilator/Vprim_crc32_sim | \ + tee $RUN_LOG + +if [ $? -eq 124 ]; then + fail "Simulation timeout" +fi + +if [ $? -ne 0 ]; then + fail "Simulator run failed" +fi + +grep -A 97 "00000000" $RUN_LOG | diff $PREDV_DIR/predv_expected.txt - + +if [ $? -eq 0 ]; then + echo "PRE-DV PASS" +else + fail "Simulator output does not match expected output" +fi diff --git a/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim.core b/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim.core new file mode 100755 index 0000000..e2b9c59 --- /dev/null +++ b/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim.core @@ -0,0 +1,31 @@ +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_flop_2sync_sim:0.1" +description: "prim_flop_2sync_sim sim target" +filesets: + files_rtl: + depend: + - lowrisc:prim:flop_2sync + + files_dv: + depend: + - lowrisc:dv:common_ifs + - lowrisc:dv:dv_macros + - lowrisc:dv:dv_utils + - lowrisc:dv:dv_test_status + files: + - tb.sv + file_type: systemVerilogSource + +targets: + sim: &sim_target + toplevel: tb + filesets: + - files_rtl + - files_dv + default_tool: vcs + + lint: + <<: *sim_target diff --git a/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim_cfg.hjson b/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim_cfg.hjson new file mode 100755 index 0000000..660a6e3 --- /dev/null +++ b/synth/prim/pre_dv/prim_flop_2sync/prim_flop_2sync_sim_cfg.hjson @@ -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 +{ + // Name of the sim cfg - typically same as the name of the DUT. + name: prim_flop_2sync + + // Top level dut name (sv module). + dut: prim_flop_2sync + + // Top level testbench name (sv module). + tb: tb + + // Simulator used to sign off this block + tool: vcs + + // Fusesoc core file used for building the file list. + fusesoc_core: lowrisc:dv:prim_flop_2sync_sim:0.1 + + // Import additional common sim cfg files. + import_cfgs: [// Project wide common sim cfg file + "{proj_root}/hw/dv/tools/dvsim/common_sim_cfg.hjson"] + + // Default iterations for all tests - each test entry can override this. + reseed: 5 + + // List of test specifications. + tests: [ + { + name: unit_test + } + ] + + // List of regressions. + regressions: [ + { + name: smoke + tests: ["unit_test"] + } + ] +} diff --git a/synth/prim/pre_dv/prim_flop_2sync/tb.sv b/synth/prim/pre_dv/prim_flop_2sync/tb.sv new file mode 100755 index 0000000..4d91ce5 --- /dev/null +++ b/synth/prim/pre_dv/prim_flop_2sync/tb.sv @@ -0,0 +1,85 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Basic testbench for prim_flop_2sync with CDC random delay enabled. +module tb; + `include "dv_macros.svh" + + localparam string MsgId = $sformatf("%m"); + + logic [31:0] src_d, src_q; + wire clk, rst_n; + + clk_rst_if clk_rst_if(.clk, .rst_n); + + prim_flop_2sync #(.Width(32)) dut ( + // source clock domain + .d_i (src_d), + // destination clock domain + .clk_i (clk), + .rst_ni (rst_n), + .q_o (src_q) + ); + + initial begin + clk_rst_if.set_active(); + clk_rst_if.apply_reset(.reset_width_clks(10)); + + $display("Using prim_cdc_rand_delay_mode slow"); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_mode(1); + repeat (100) begin + src_d <= $urandom(); + clk_rst_if.wait_clks($urandom_range(1, 20)); + end + clk_rst_if.wait_clks(200); + + $display("Using prim_cdc_rand_delay_mode once"); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_mode(2); + repeat (100) begin + src_d <= $urandom(); + clk_rst_if.wait_clks($urandom_range(1, 20)); + end + clk_rst_if.wait_clks(200); + + $display("Using prim_cdc_rand_delay_mode interval = 10"); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_mode(3); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_interval(10); + repeat (100) begin + src_d <= $urandom(); + clk_rst_if.wait_clks($urandom_range(1, 20)); + end + clk_rst_if.wait_clks(200); + + $display("Using prim_cdc_rand_delay_mode interval = 1"); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_interval(1); + repeat (100) begin + src_d <= $urandom(); + clk_rst_if.wait_clks($urandom_range(1, 20)); + end + clk_rst_if.wait_clks(200); + + $display("Using prim_cdc_rand_delay_mode interval = 0"); + dut.u_prim_cdc_rand_delay.set_prim_cdc_rand_delay_interval(0); + repeat (100) begin + src_d <= $urandom(); + clk_rst_if.wait_clks($urandom_range(1, 20)); + end + clk_rst_if.wait_clks(200); + + // TODO: Add more checks. + dv_test_status_pkg::dv_test_status(.passed(1)); + `DV_CHECK(!src_d_q.size(), , , MsgId) + $finish; + end + + // Verify src_d to src_q consistency. + logic [31:0] src_d_q[$]; + initial begin + fork + forever @src_d if (rst_n) src_d_q.push_back(src_d); + forever @src_q `DV_CHECK_EQ(src_q, src_d_q.pop_front(), , , MsgId) + join_none + end + +endmodule diff --git a/synth/prim/pre_dv/prim_sync_reqack/README.md b/synth/prim/pre_dv/prim_sync_reqack/README.md new file mode 100755 index 0000000..cd149ef --- /dev/null +++ b/synth/prim/pre_dv/prim_sync_reqack/README.md @@ -0,0 +1,34 @@ +REQ/ACK Synchronizer 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/synth/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc b/synth/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc new file mode 100755 index 0000000..1b8adf2 --- /dev/null +++ b/synth/prim/pre_dv/prim_sync_reqack/cpp/prim_sync_reqack_tb.cc @@ -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 + +#include +#include +#include + +#include "Vprim_sync_reqack_tb.h" +#include "sim_ctrl_extension.h" +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.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).first; + + return ret_code; +} diff --git a/synth/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core b/synth/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core new file mode 100755 index 0000000..99f272e --- /dev/null +++ b/synth/prim/pre_dv/prim_sync_reqack/prim_sync_reqack_tb.core @@ -0,0 +1,51 @@ +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" + # XXX: Cleanup all warnings and remove this option + # (or make it more fine-grained at least) + - "-Wno-fatal" diff --git a/synth/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv b/synth/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv new file mode 100755 index 0000000..36b881c --- /dev/null +++ b/synth/prim/pre_dv/prim_sync_reqack/rtl/prim_sync_reqack_tb.sv @@ -0,0 +1,188 @@ +// 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 + localparam bit DataSrc2Dst = 1'b1; // Select 1'b0 for Dst2Src + localparam bit DataReg = 1'b0; // Select 1'b1 if data flows from Dst2Src + localparam bit EnRzHs = 1'b0; // RZ vs. NRZ protocol + + // 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 + logic [WidthTrans-1:0] in_data, out_data, unused_out_data; + assign in_data = DataSrc2Dst ? src_count_q : dst_count_q; + prim_sync_reqack_data #( + .Width ( WidthTrans ), + .DataSrc2Dst ( DataSrc2Dst ), + .DataReg ( DataReg ), + .EnRzHs ( EnRzHs ) + ) u_prim_sync_reqack_data ( + .clk_src_i (clk_src), + .rst_src_ni (rst_slow_n), + .clk_dst_i (clk_dst), + .rst_dst_ni (rst_slow_n), + + .req_chk_i (1'b1), + + .src_req_i (src_req), + .src_ack_o (src_ack), + .dst_req_o (dst_req), + .dst_ack_i (dst_ack), + + .data_i (in_data), + .data_o (out_data) + ); + assign unused_out_data = out_data; + + // 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/synth/prim/pre_dv/prim_trivium/README.md b/synth/prim/pre_dv/prim_trivium/README.md new file mode 100755 index 0000000..c739483 --- /dev/null +++ b/synth/prim/pre_dv/prim_trivium/README.md @@ -0,0 +1,35 @@ +Trivium/Bivium Stream Cipher Verilator Testbench +================================================ + +This directory contains a basic, scratch Verilator testbench targeting +functional verification of the Trivium/Bivium stream cipher primitives +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_trivium_tb + ``` +to build the testbench and afterwards + + ```sh + ./build/lowrisc_dv_verilator_prim_trivium_tb_0/default-verilator/Vprim_trivium_tb \ + --trace + ``` +to run it. + +Details of the testbench +------------------------ + +- `rtl/prim_trivium_tb.sv`: SystemVerilog testbench, instantiates and + drives multiple, differently parameterized instances of the primitives, + checks key streams of the Trivium instances against a known good key + stream, signals test end and result (pass/fail) to C++ via output ports. + Change this file to e.g. configure different output widths. +- `cpp/prim_trivium_tb.cc`: Contains main function and instantiation of + SimCtrl, reads output ports of the testbench and signals simulation + termination to Verilator. diff --git a/synth/prim/pre_dv/prim_trivium/cpp/prim_trivium_tb.cc b/synth/prim/pre_dv/prim_trivium/cpp/prim_trivium_tb.cc new file mode 100755 index 0000000..df0a8c6 --- /dev/null +++ b/synth/prim/pre_dv/prim_trivium/cpp/prim_trivium_tb.cc @@ -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 + +#include +#include +#include + +#include "Vprim_trivium_tb.h" +#include "sim_ctrl_extension.h" +#include "verilated_toplevel.h" +#include "verilator_sim_ctrl.h" + +class PrimTriviumTB : public SimCtrlExtension { + using SimCtrlExtension::SimCtrlExtension; + + public: + PrimTriviumTB(prim_trivium_tb *top); + + void OnClock(unsigned long sim_time); + + private: + prim_trivium_tb *top_; +}; + +// Constructor: +// - Set up top_ ptr +PrimTriviumTB::PrimTriviumTB(prim_trivium_tb *top) + : SimCtrlExtension{}, top_(top) {} + +// Function called once every clock cycle from SimCtrl +void PrimTriviumTB::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_trivium_tb top; + + // Init sim + VerilatorSimCtrl &simctrl = VerilatorSimCtrl::GetInstance(); + simctrl.SetTop(&top, &top.clk_i, &top.rst_ni, + VerilatorSimCtrlFlags::ResetPolarityNegative); + + // Create and register VerilatorSimCtrl extension + PrimTriviumTB primtriviumtb(&top); + simctrl.RegisterExtension(&primtriviumtb); + + std::cout << "Simulation of Trivium/Bivium PRNG primitives" << std::endl + << "============================================" << std::endl + << std::endl; + + // Get pass / fail from Verilator + ret_code = simctrl.Exec(argc, argv).first; + + return ret_code; +} diff --git a/synth/prim/pre_dv/prim_trivium/prim_trivium_tb.core b/synth/prim/pre_dv/prim_trivium/prim_trivium_tb.core new file mode 100755 index 0000000..6c942c5 --- /dev/null +++ b/synth/prim/pre_dv/prim_trivium/prim_trivium_tb.core @@ -0,0 +1,51 @@ +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_trivium_tb" +description: "Trivium/Bivium Stream Cipher Verilator TB" +filesets: + files_rtl: + depend: + - lowrisc:prim:trivium + files: + - rtl/prim_trivium_tb.sv + file_type: systemVerilogSource + + files_dv_verilator: + depend: + - lowrisc:dv_verilator:simutil_verilator + + files: + - cpp/prim_trivium_tb.cc + file_type: cppSource + +targets: + default: + default_tool: verilator + filesets: + - files_rtl + - files_dv_verilator + toplevel: prim_trivium_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_trivium_tb -g -O0"' + - '-LDFLAGS "-pthread -lutil -lelf"' + - "-Wall" + # XXX: Cleanup all warnings and remove this option + # (or make it more fine-grained at least) + - "-Wno-fatal" diff --git a/synth/prim/pre_dv/prim_trivium/rtl/prim_trivium_tb.sv b/synth/prim/pre_dv/prim_trivium/rtl/prim_trivium_tb.sv new file mode 100755 index 0000000..cbc06a7 --- /dev/null +++ b/synth/prim/pre_dv/prim_trivium/rtl/prim_trivium_tb.sv @@ -0,0 +1,428 @@ +// 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 Trivium/Bivium stream cipher primitives + +module prim_trivium_tb ( + input logic clk_i, + input logic rst_ni, + + output logic test_done_o, + output logic test_passed_o +); + + import prim_trivium_pkg::*; + + // TB configuration + localparam int unsigned OutputWidth = 128; + localparam int unsigned PartialSeedWidth = 32; + + localparam int unsigned TriviumLastStatePartFractional = + TriviumStateWidth % PartialSeedWidth != 0 ? 1 : 0; + localparam int unsigned TriviumNumStateParts = + TriviumStateWidth / PartialSeedWidth + TriviumLastStatePartFractional; + localparam int unsigned BiviumLastStatePartFractional = + BiviumStateWidth % PartialSeedWidth != 0 ? 1 : 0; + localparam int unsigned BiviumNumStateParts = + BiviumStateWidth / PartialSeedWidth + BiviumLastStatePartFractional; + + // Counter to control the simulation. + localparam int unsigned CountWidth = 10; + logic [CountWidth-1:0] count_d, count_q; + assign count_d = count_q + 10'd1; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_count + if (!rst_ni) begin + count_q <= '0; + end else begin + count_q <= count_d; + end + end + + ///////////// + // Trivium // + ///////////// + // Test vectors seem to be available for Trivium only. Thus only the Trivium instances are really + // verified against a known good key stream. + + // In case the OutputWidth is not a divisor of 1152, the key stream is offset by the remainder of + // this division. Thus, we skip the checking of the instance seeded with key and IV. + localparam bit SkipSeedKeyIvInstance = 1152 % OutputWidth != '0; + localparam int unsigned TestVectorOutLen = 512; + localparam int unsigned NumOutputsToCheck = TestVectorOutLen / OutputWidth; + localparam int unsigned NumOutputBitsToCheck = NumOutputsToCheck * OutputWidth; + localparam int unsigned OutCtrWidth = prim_util_pkg::vbits(NumOutputsToCheck) + 1; + localparam int NumInstances = 3; + + logic trivium_en, trivium_seed_en; + logic trivium_seed_ack_seed_state_partial; + logic trivium_seed_done_seed_key_iv; + logic trivium_seed_done_seed_state_full; + logic trivium_seed_done_seed_state_partial; + logic [KeyIvWidth-1:0] seed_key, seed_iv; + logic [TriviumStateWidth-1:0] seed_state_full; + logic [PartialSeedWidth-1:0] trivium_seed_state_partial; + logic [OutputWidth-1:0] key_seed_key_iv, key_seed_state_full, key_seed_state_partial; + + // Trivium test vectors generated using + // + // https://asecuritysite.com/encryption/trivium + // + // Notes on the test vectors from the above site: + // - key and IV are 'byte' vectors starting with Byte n (most left) and ending with Byte 0 (most + // right). Within each byte, the LSB is the most left bit and the MSB being the + // most right bit. + // - The key stream is a 'byte' vector starting with Byte 0 (most left) and ending with + // Byte n (most right). Within each byte, the LSB is the most right bit and the MSB being the + // most left bit. + // + // In this testbench, we use bit vectors only with LSB being most left and the MSB being most + // right. + logic [TestVectorOutLen-1:0] key_stream_expected; + + // // key = 0 + // // iv = 0 + // localparam logic [KeyIvWidth-1:0] VECTOR_0_KEY = 80'h0000_0000_0000_0000_0000; + // localparam logic [KeyIvWidth-1:0] VECTOR_0_IV = 80'h0000_0000_0000_0000_0000; + // localparam logic [TestVectorOutLen-1:0] VECTOR_0_KEY_STREAM = + // {128'hF2D4_A5B5_4C1A_8003_5980_746E_9849_5528, + // 128'h9A59_A29A_751A_4C2B_38B7_6802_0392_52F7, + // 128'hCDE9_B2A1_0F79_A8E7_2DCF_0719_1603_3256, + // 128'h7FC9_9F23_4E2E_7A51_1B05_5958_26BF_E0FB}; + // localparam logic [TriviumStateWidth-1:0] VECTOR_0_STATE = + // {96'h92AC_487E_267F_5871_D3A1_896D, + // 96'hF514_AD2F_EA84_1320_40AE_4058, + // 96'hA21B_38EC_692A_E61D_7493_1A85}; + + // // key = 8000_0000_0000_0000_0000 + // // iv = 0 + // localparam logic [KeyIvWidth-1:0] VECTOR_1_KEY = 80'h0100_0000_0000_0000_0000; + // localparam logic [KeyIvWidth-1:0] VECTOR_1_IV = 80'h0000_0000_0000_0000_0000; + // localparam logic [TestVectorOutLen-1:0] VECTOR_1_KEY_STREAM = + // {128'hF980_FC54_74EF_E87B_B962_6ACC_CC20_FF98, + // 128'h807F_CFCE_928F_6CE0_EB21_0961_15F5_FBD2, + // 128'h649A_F249_C241_2055_0175_C864_1465_7BBB, + // 128'h0D54_2044_3AF1_8DAF_9C7A_0D73_FF86_EB38}; + // localparam logic [TriviumStateWidth-1:0] VECTOR_1_STATE = + // {96'hC7D7_C89B_CC06_725B_3D94_7181, + // 96'h06F2_A065_6422_AF1F_A457_B81F, + // 96'h0D25_16A9_D565_893A_64C1_E50E}; + + // key = 0A5D_B003_56A9_FC4F_A2F5 + // iv = 1F86_ED54_BB22_89F0_57BE + localparam logic [KeyIvWidth-1:0] VECTOR_2_KEY = 80'h50BA_0DC0_6A95_3FF2_45AF; + localparam logic [KeyIvWidth-1:0] VECTOR_2_IV = 80'hF861_B72A_DD44_910F_EA7D; + localparam logic [TestVectorOutLen-1:0] VECTOR_2_KEY_STREAM = + {128'h7258_8CB7_89E3_8615_0DFC_DA03_BDA3_A5AE, + 128'h2F98_426C_4C75_C0F1_3BFB_6B2D_E2DD_6E54, + 128'hB8F0_AB03_51B7_F538_3C17_FAC1_E8B0_913B, + 128'h1838_E884_56D9_D2D0_ADCB_4B13_C510_94DE}; + localparam logic [TriviumStateWidth-1:0] VECTOR_2_STATE = + {96'hE843_DB60_EB48_14D1_C198_620D, + 96'hF21B_0322_0BAD_DAD5_A15A_3958, + 96'hBC97_5171_D8C0_4C75_B395_11C4}; + + assign seed_key = VECTOR_2_KEY; + assign seed_iv = VECTOR_2_IV; + assign seed_state_full = VECTOR_2_STATE; + assign key_stream_expected = VECTOR_2_KEY_STREAM; + + // Instantiate DUTs + prim_trivium #( + .BiviumVariant(0), + .OutputWidth (OutputWidth), + .SeedType (SeedTypeKeyIv) + ) u_prim_trivium_seed_key_iv ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (trivium_en), + .allow_lockup_i (1'b0), + .seed_en_i (trivium_seed_en), + .seed_done_o (trivium_seed_done_seed_key_iv), + .seed_req_o (), + .seed_ack_i (trivium_seed_en), + .seed_key_i (seed_key), + .seed_iv_i (seed_iv), + .seed_state_full_i ('0), // unused + .seed_state_partial_i('0), // unused + + .key_o(key_seed_key_iv), + .err_o() + ); + + prim_trivium #( + .BiviumVariant(0), + .OutputWidth (OutputWidth), + .SeedType (SeedTypeStateFull) + ) u_prim_trivium_seed_state_full ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (trivium_en), + .allow_lockup_i (1'b0), + .seed_en_i (trivium_seed_en), + .seed_done_o (trivium_seed_done_seed_state_full), + .seed_req_o (), + .seed_ack_i (trivium_seed_en), + .seed_key_i ('0), // unused + .seed_iv_i ('0), // unused + .seed_state_full_i (seed_state_full), + .seed_state_partial_i('0), // unused + + .key_o(key_seed_state_full), + .err_o() + ); + + prim_trivium #( + .BiviumVariant (0), + .OutputWidth (OutputWidth), + .SeedType (SeedTypeStatePartial), + .PartialSeedWidth(PartialSeedWidth) + ) u_prim_trivium_seed_state_partial ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (trivium_en), + .allow_lockup_i (1'b0), + .seed_en_i (trivium_seed_en), + .seed_done_o (trivium_seed_done_seed_state_partial), + .seed_req_o (), + .seed_ack_i (trivium_seed_ack_seed_state_partial), + .seed_key_i ('0), // unused + .seed_iv_i ('0), // unused + .seed_state_full_i ('0), // unused + .seed_state_partial_i(trivium_seed_state_partial), + + .key_o(key_seed_state_partial), + .err_o() + ); + + // Tracking of seed_done + logic [NumInstances-1:0] trivium_seed_done_d, trivium_seed_done_q; + assign trivium_seed_done_d = (trivium_seed_done_q & + // Clear back to zero upon staring a new reseed operation. + {NumInstances{~trivium_seed_en}}) | + // Register finishing of reseed operation. + {trivium_seed_done_seed_state_partial, + trivium_seed_done_seed_state_full, + trivium_seed_done_seed_key_iv}; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_trivium_seed_done + if (!rst_ni) begin + trivium_seed_done_q <= '0; + end else begin + trivium_seed_done_q <= trivium_seed_done_d; + end + end + + // Start the reseed operation right after coming out of reset. + assign trivium_seed_en = count_q == 10'd1; + // Start running once all instances have finished the reseed operation. + assign trivium_en = &trivium_seed_done_q; + + // Provide the 9 partial seed parts. + assign trivium_seed_ack_seed_state_partial = + (count_q >= 10'd1) && (count_q < 10'd1 + TriviumNumStateParts[CountWidth-1:0]); + assign trivium_seed_state_partial = + seed_state_full[({22'h0, count_q} - 32'd1) * PartialSeedWidth +: PartialSeedWidth]; + + ///////////////////////////////// + // Record generated key streams// + ///////////////////////////////// + logic record; + assign record = trivium_en & (out_ctr_q < NumOutputsToCheck[OutCtrWidth-1:0]); + + logic [OutCtrWidth-1:0] out_ctr_d, out_ctr_q; + assign out_ctr_d = record ? out_ctr_q + 1'b1 : out_ctr_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_out_ctr + if (!rst_ni) begin + out_ctr_q <= '0; + end else begin + out_ctr_q <= out_ctr_d; + end + end + + logic [TestVectorOutLen-1:0] key_stream_actual_d [NumInstances]; + logic [TestVectorOutLen-1:0] key_stream_actual_q [NumInstances]; + always_comb begin + key_stream_actual_d = key_stream_actual_q; + if (record) begin + key_stream_actual_d[0][out_ctr_q * OutputWidth +: OutputWidth] = key_seed_key_iv; + key_stream_actual_d[1][out_ctr_q * OutputWidth +: OutputWidth] = key_seed_state_full; + key_stream_actual_d[2][out_ctr_q * OutputWidth +: OutputWidth] = key_seed_state_partial; + end + end + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_key_stream_actual + if (!rst_ni) begin + key_stream_actual_q <= '{default: '0}; + end else begin + key_stream_actual_q <= key_stream_actual_d; + end + end + + /////////////////////////////////////////////// + // Check responses, signal end of simulation // + /////////////////////////////////////////////// + logic mismatch; + always_ff @(posedge clk_i) begin : tb_ctrl + test_done_o <= 1'b0; + test_passed_o <= 1'b1; + + if (out_ctr_q == NumOutputsToCheck[OutCtrWidth-1:0]) begin + mismatch <= 1'b0; + for (int i = 0; i < NumInstances; i++) begin + if (!SkipSeedKeyIvInstance || i > 0) begin + if (key_stream_expected[NumOutputBitsToCheck-1:0] + == key_stream_actual_q[i][NumOutputBitsToCheck-1:0]) begin + $display("\nSUCCESS: Observed key stream of instance %1d matches expected data.", i); + end else begin + $display("\nERROR: Obeserved key stream of instance %1d doesn't match expected data.", + i); + mismatch <= 1'b1; + end + end + end + $display("Finishing simulation now.\n"); + test_passed_o <= ~mismatch; + test_done_o <= 1'b1; + end + + if (count_q == 10'd1000) begin + $display("\nDONE"); + test_done_o <= 1'b1; + end + end + + //////////// + // Bivium // + //////////// + // No test vectors seem to be available for Bivium. The 3 instances below are not checked against + // a known good key stream. It's still useful to instantiate the 3 different Bivium variants for + // visual inspection, for checking Verilator lint of all three variants, and for inspecting the + // partial reseed behavior while the primitive is running. + + logic bivium_en, bivium_seed_en; + logic bivium_seed_done_seed_key_iv; + logic bivium_seed_done_seed_state_full; + logic bivium_seed_done_seed_state_partial; + logic bivium_seed_en_seed_state_partial, bivium_seed_ack_seed_state_partial; + logic [PartialSeedWidth-1:0] bivium_seed_state_partial; + + // Instantiate DUTs + prim_trivium #( + .BiviumVariant(1), + .OutputWidth (OutputWidth), + .SeedType (SeedTypeKeyIv) + ) u_prim_bivium_seed_key_iv ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (bivium_en), + .allow_lockup_i (1'b0), + .seed_en_i (bivium_seed_en), + .seed_done_o (bivium_seed_done_seed_key_iv), + .seed_req_o (), + .seed_ack_i (bivium_seed_en), + .seed_key_i (seed_key), + .seed_iv_i (seed_iv), + .seed_state_full_i ('0), // unused + .seed_state_partial_i('0), // unused + + .key_o(key_seed_key_iv), + .err_o() + ); + + prim_trivium #( + .BiviumVariant(1), + .OutputWidth (OutputWidth), + .SeedType (SeedTypeStateFull) + ) u_prim_bivium_seed_state_full ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (bivium_en), + .allow_lockup_i (1'b0), + .seed_en_i (bivium_seed_en), + .seed_done_o (bivium_seed_done_seed_state_full), + .seed_req_o (), + .seed_ack_i (bivium_seed_en), + .seed_key_i ('0), // unused + .seed_iv_i ('0), // unused + .seed_state_full_i (seed_state_full[BiviumStateWidth-1:0]), + .seed_state_partial_i('0), // unused + + .key_o(key_seed_state_full), + .err_o() + ); + + prim_trivium #( + .BiviumVariant (1), + .OutputWidth (OutputWidth), + .StrictLockupProtection(0), + .SeedType (SeedTypeStatePartial), + .PartialSeedWidth (PartialSeedWidth) + ) u_prim_bivium_seed_state_partial ( + .clk_i (clk_i), + .rst_ni(rst_ni), + + .en_i (bivium_en), + .allow_lockup_i (1'b1), + .seed_en_i (bivium_seed_en_seed_state_partial), + .seed_done_o (bivium_seed_done_seed_state_partial), + .seed_req_o (), + .seed_ack_i (bivium_seed_ack_seed_state_partial), + .seed_key_i ('0), // unused + .seed_iv_i ('0), // unused + .seed_state_full_i ('0), // unused + .seed_state_partial_i(bivium_seed_state_partial), + + .key_o(), + .err_o() + ); + + // Tracking of seed_done + logic [NumInstances-1:0] bivium_seed_done_d, bivium_seed_done_q; + assign bivium_seed_done_d = (bivium_seed_done_q & + // Clear back to zero upon staring a new reseed operation. + {NumInstances{~bivium_seed_en}}) | + // Register finishing of reseed operation. + {bivium_seed_done_seed_state_partial, + bivium_seed_done_seed_state_full, + bivium_seed_done_seed_key_iv}; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_bivium_seed_done + if (!rst_ni) begin + bivium_seed_done_q <= '0; + end else begin + bivium_seed_done_q <= bivium_seed_done_d; + end + end + + // Start the reseed operation right after coming out of reset. + assign bivium_seed_en = count_q == 10'd1; + // Start running once all instances have finished the reseed operation. + assign bivium_en = &bivium_seed_done_q; + + // The last instance is handled separately: + // First, we put the Bivium primitive into an all-zero state. Then, a single PartialSeedWidth + // seed is provided while the primitive is running. This allows inpsecting the diffusion pattern + // of the primitive (depending on the OutputWidth). + + // Register to latch counter value when initial reseed operation finishes. This depends on + // PartialSeedWidth and OutputWidth. + logic [CountWidth-1:0] count_seed_done_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : reg_count_seed_done + if (!rst_ni) begin + count_seed_done_q <= '0; + end else if (&bivium_seed_done_q && count_seed_done_q == '0) begin + count_seed_done_q <= count_q; + end + end + + assign bivium_seed_en_seed_state_partial = bivium_seed_en || + ((count_seed_done_q != 0) && (count_q >= count_seed_done_q + 10'd1)); + assign bivium_seed_ack_seed_state_partial = + ((count_q >= 10'd1) && (count_q < 10'd1 + BiviumNumStateParts[CountWidth-1:0])) || + bivium_seed_en_seed_state_partial; + assign bivium_seed_state_partial = &bivium_seed_done_q ? '1 : '0; + +endmodule diff --git a/synth/prim/prim.core b/synth/prim/prim.core new file mode 100755 index 0000000..d96d61c --- /dev/null +++ b/synth/prim/prim.core @@ -0,0 +1,79 @@ +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:assert + - lowrisc:prim:util + - lowrisc:prim:diff_decode # for prim_alert_sender/receiver + - lowrisc:prim:pad_wrapper + - lowrisc:prim:prim_pkg + - lowrisc:prim:clock_mux2 + - lowrisc:prim:clock_inv + - lowrisc:prim:buf + - lowrisc:prim:flop + - lowrisc:prim:flop_en + - lowrisc:prim:flop_2sync + - lowrisc:prim:rst_sync + - lowrisc:prim:arbiter + - lowrisc:prim:fifo + - lowrisc:prim:alert + - lowrisc:prim:esc + - lowrisc:prim:subreg + - lowrisc:prim:cipher + - lowrisc:prim:xor2 + - lowrisc:prim:xnor2 + - lowrisc:prim:and2 + - lowrisc:prim:reg_we_check + - lowrisc:prim:sha2 + files: + - rtl/prim_clock_gating_sync.sv + - rtl/prim_sram_arbiter.sv + - rtl/prim_slicer.sv + - rtl/prim_sync_reqack.sv + - rtl/prim_sync_reqack_data.sv + - rtl/prim_sync_slow_fast.sv + - rtl/prim_keccak.sv + - rtl/prim_packer.sv + - rtl/prim_packer_fifo.sv + - rtl/prim_gate_gen.sv + - rtl/prim_pulse_sync.sv + - rtl/prim_filter.sv + - rtl/prim_filter_ctr.sv + - rtl/prim_intr_hw.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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_alert.core b/synth/prim/prim_alert.core new file mode 100755 index 0000000..c92db58 --- /dev/null +++ b/synth/prim/prim_alert.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:alert" +description: "Alert send and receive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:diff_decode + - lowrisc:prim:buf + - lowrisc:prim:flop + - lowrisc:prim:sec_anchor + - lowrisc:prim:mubi + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_alert_pkg.sv + - rtl/prim_alert_receiver.sv + - rtl/prim_alert_sender.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_and2.core b/synth/prim/prim_and2.core new file mode 100755 index 0000000..9265443 --- /dev/null +++ b/synth/prim/prim_and2.core @@ -0,0 +1,49 @@ +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:and2" +description: "Generic 2-input and" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_and2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: and2 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_arbiter.core b/synth/prim/prim_arbiter.core new file mode 100755 index 0000000..a15386d --- /dev/null +++ b/synth/prim/prim_arbiter.core @@ -0,0 +1,46 @@ +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:arbiter" +description: "Generic arbiter" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_arbiter_fixed.sv + - rtl/prim_arbiter_ppc.sv + - rtl/prim_arbiter_tree.sv + - rtl/prim_arbiter_tree_dup.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_arbiter.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_arbiter.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_assert.core b/synth/prim/prim_assert.core new file mode 100755 index 0000000..26e88e4 --- /dev/null +++ b/synth/prim/prim_assert.core @@ -0,0 +1,46 @@ +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:assert:0.1" +description: "Assertion primitives" +filesets: + files_rtl: + files: + - rtl/prim_assert.sv : {is_include_file : true} + - rtl/prim_assert_dummy_macros.svh : {is_include_file : true} + - rtl/prim_assert_yosys_macros.svh : {is_include_file : true} + - rtl/prim_assert_standard_macros.svh : {is_include_file : true} + - rtl/prim_assert_sec_cm.svh : {is_include_file : true} + - rtl/prim_flop_macros.sv : {is_include_file : true} + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_assert.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_assert.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_blanker.core b/synth/prim/prim_blanker.core new file mode 100755 index 0000000..c8b7a9a --- /dev/null +++ b/synth/prim/prim_blanker.core @@ -0,0 +1,41 @@ +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:blanker" +description: "Primitive for blanking signals" +filesets: + files_rtl: + depend: + - lowrisc:prim:and2 + files: + - rtl/prim_blanker.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_buf.core b/synth/prim/prim_buf.core new file mode 100755 index 0000000..648fcec --- /dev/null +++ b/synth/prim/prim_buf.core @@ -0,0 +1,49 @@ +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:buf" +description: "Generic buffer" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_buf.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: buf + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_cdc_rand_delay.core b/synth/prim/prim_cdc_rand_delay.core new file mode 100755 index 0000000..38fb538 --- /dev/null +++ b/synth/prim/prim_cdc_rand_delay.core @@ -0,0 +1,48 @@ +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:cdc_rand_delay" +description: "CDC random delay module for DV" + +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:dv:dv_macros + files: + - rtl/prim_cdc_rand_delay.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_cdc_rand_delay.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_cdc_rand_delay.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + tools: + verilator: + verilator_options: + - '-DDISABLE_PRIM_CDC_RAND_DELAY' + filesets: + - files_rtl + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_verilator_waiver) diff --git a/synth/prim/prim_cipher.core b/synth/prim/prim_cipher.core new file mode 100755 index 0000000..a32c1d8 --- /dev/null +++ b/synth/prim/prim_cipher.core @@ -0,0 +1,43 @@ +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:cipher" +description: "Lightweight block ciphers (PRINCE and PRESENT)" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:cipher_pkg:0.1 + files: + - rtl/prim_subst_perm.sv + - rtl/prim_present.sv + - rtl/prim_prince.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_cipher.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_cipher_pkg.core b/synth/prim/prim_cipher_pkg.core new file mode 100755 index 0000000..cdc3a29 --- /dev/null +++ b/synth/prim/prim_cipher_pkg.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:cipher_pkg:0.1" +description: "PRINCE and PRESENT block cipher package" +filesets: + files_rtl: + files: + - rtl/prim_cipher_pkg.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_cipher_pkg.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_clock_buf.core b/synth/prim/prim_clock_buf.core new file mode 100755 index 0000000..4f571fb --- /dev/null +++ b/synth/prim/prim_clock_buf.core @@ -0,0 +1,49 @@ +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_buf" +description: "Generic clock buffer" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_clock_buf.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_buf + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_clock_div.core b/synth/prim/prim_clock_div.core new file mode 100755 index 0000000..755f17d --- /dev/null +++ b/synth/prim/prim_clock_div.core @@ -0,0 +1,48 @@ +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_div" +description: "Generic clock divide" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_clock_div.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_div + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_clock_gating.core b/synth/prim/prim_clock_gating.core new file mode 100755 index 0000000..20be120 --- /dev/null +++ b/synth/prim/prim_clock_gating.core @@ -0,0 +1,49 @@ +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 + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_clock_gating.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_gating + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_clock_gp_mux2.core b/synth/prim/prim_clock_gp_mux2.core new file mode 100755 index 0000000..3918b32 --- /dev/null +++ b/synth/prim/prim_clock_gp_mux2.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:clock_gp_mux2" +description: "2-input glitch free clock multiplexer" +filesets: + files_rtl: + depend: + - lowrisc:prim:prim_pkg + files: + - rtl/prim_clock_gp_mux2.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_clock_inv.core b/synth/prim/prim_clock_inv.core new file mode 100755 index 0000000..9711919 --- /dev/null +++ b/synth/prim/prim_clock_inv.core @@ -0,0 +1,49 @@ +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_inv" +description: "Clock inverter with scanmode bypass mux" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_clock_inv.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_inv + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_clock_meas.core b/synth/prim/prim_clock_meas.core new file mode 100755 index 0000000..4510675 --- /dev/null +++ b/synth/prim/prim_clock_meas.core @@ -0,0 +1,22 @@ +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:measure" +description: "Clock measurement" +filesets: + files_rtl: + depend: + - lowrisc:prim:all + - lowrisc:prim:flop_2sync + - lowrisc:prim:assert + files: + - rtl/prim_clock_meas.sv + - rtl/prim_clock_timeout.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_clock_mux2.core b/synth/prim/prim_clock_mux2.core new file mode 100755 index 0000000..68566ac --- /dev/null +++ b/synth/prim/prim_clock_mux2.core @@ -0,0 +1,49 @@ +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 + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_clock_mux2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: clock_mux2 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_count.core b/synth/prim/prim_count.core new file mode 100755 index 0000000..f65bc6a --- /dev/null +++ b/synth/prim/prim_count.core @@ -0,0 +1,43 @@ +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:count" +description: "" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_count.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_count.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_count.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_crc32.core b/synth/prim/prim_crc32.core new file mode 100755 index 0000000..178b68c --- /dev/null +++ b/synth/prim/prim_crc32.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:crc32" +description: "CRC32 Checksum Calculator" +filesets: + files_rtl: + files: + - rtl/prim_crc32.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_crc32.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_diff_decode.core b/synth/prim/prim_diff_decode.core new file mode 100755 index 0000000..a91367f --- /dev/null +++ b/synth/prim/prim_diff_decode.core @@ -0,0 +1,20 @@ +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:diff_decode" +description: "prim" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:flop_2sync + files: + - rtl/prim_diff_decode.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_dom_and_2share.core b/synth/prim/prim_dom_and_2share.core new file mode 100755 index 0000000..9376d66 --- /dev/null +++ b/synth/prim/prim_dom_and_2share.core @@ -0,0 +1,51 @@ +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_dom_and_2share:0.1" +description: "Domain-Oriented Masking AND" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_dom_and_2share.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 + + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + toplevel: prim_dom_and_2share + formal: + filesets: + - files_rtl + toplevel: prim_dom_and_2share + + diff --git a/synth/prim/prim_double_lfsr.core b/synth/prim/prim_double_lfsr.core new file mode 100755 index 0000000..5edcb08 --- /dev/null +++ b/synth/prim/prim_double_lfsr.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:double_lfsr" +description: "" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:lfsr + - lowrisc:prim:buf + files: + - rtl/prim_double_lfsr.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_double_lfsr.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_double_lfsr.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_edge_detector.core b/synth/prim/prim_edge_detector.core new file mode 100755 index 0000000..1d6d52f --- /dev/null +++ b/synth/prim/prim_edge_detector.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:edge_detector:0.1" +description: "Positive/ Negative Edge detector" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:flop_2sync + files: + - rtl/prim_edge_detector.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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_edn_req.core b/synth/prim/prim_edn_req.core new file mode 100755 index 0000000..8c07aab --- /dev/null +++ b/synth/prim/prim_edn_req.core @@ -0,0 +1,21 @@ +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:edn_req:0.1" +description: "EDN synchronization and word packing IP." +filesets: + files_rtl: + depend: + - lowrisc:prim:all + - lowrisc:prim:assert + - lowrisc:ip:edn_pkg + files: + - rtl/prim_edn_req.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_esc.core b/synth/prim/prim_esc.core new file mode 100755 index 0000000..d796bbb --- /dev/null +++ b/synth/prim/prim_esc.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:esc" +description: "Escalation send and receive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:diff_decode + - lowrisc:prim:buf + - lowrisc:prim:count + - lowrisc:prim:flop + - lowrisc:prim:sec_anchor + files: + - rtl/prim_esc_pkg.sv + - rtl/prim_esc_receiver.sv + - rtl/prim_esc_sender.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_fifo.core b/synth/prim/prim_fifo.core new file mode 100755 index 0000000..002f96c --- /dev/null +++ b/synth/prim/prim_fifo.core @@ -0,0 +1,49 @@ +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:fifo" +description: "Synchronous and asynchronous fifos" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:flop_2sync + files: + - rtl/prim_fifo_async_sram_adapter.sv + - rtl/prim_fifo_async_simple.sv + - rtl/prim_fifo_async.sv + - rtl/prim_fifo_sync.sv + - rtl/prim_fifo_sync_cnt.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_fifo.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_fifo.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_flash.core b/synth/prim/prim_flash.core new file mode 100755 index 0000000..849506b --- /dev/null +++ b/synth/prim/prim_flash.core @@ -0,0 +1,54 @@ +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 + # TODO olofk/fusesoc#404: The below dependency is already added to prim_generic_flash.core. + # However, the generator for the prim:ram1p does not kick in, causing compile errors. + - lowrisc:prim:ram_1p + - lowrisc:prim:clock_inv + - lowrisc:prim:clock_gating + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_flash.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: flash + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_flop.core b/synth/prim/prim_flop.core new file mode 100755 index 0000000..ced2c18 --- /dev/null +++ b/synth/prim/prim_flop.core @@ -0,0 +1,49 @@ +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:flop" +description: "Generic flop" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_flop.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: flop + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_flop_2sync.core b/synth/prim/prim_flop_2sync.core new file mode 100755 index 0000000..82f99fe --- /dev/null +++ b/synth/prim/prim_flop_2sync.core @@ -0,0 +1,46 @@ +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:flop_2sync" +description: "Primitive synchronizer" +filesets: + files_rtl: + depend: + - lowrisc:prim:prim_pkg + # Needed because the generic prim_flop_2sync has a + # dependency on prim:flop. + - lowrisc:prim:flop + - lowrisc:prim:cdc_rand_delay + files: + - rtl/prim_flop_2sync.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_flop_2sync.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_flop_en.core b/synth/prim/prim_flop_en.core new file mode 100755 index 0000000..3dc1c88 --- /dev/null +++ b/synth/prim/prim_flop_en.core @@ -0,0 +1,48 @@ +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:flop_en" +description: "Generic enable flop" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_flop_en.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: flop_en + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_gf_mult.core b/synth/prim/prim_gf_mult.core new file mode 100755 index 0000000..361fb27 --- /dev/null +++ b/synth/prim/prim_gf_mult.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:gf_mult" +description: "Galois multiplier" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_gf_mult.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_lc_and_hardened.core b/synth/prim/prim_lc_and_hardened.core new file mode 100755 index 0000000..a44be20 --- /dev/null +++ b/synth/prim/prim_lc_and_hardened.core @@ -0,0 +1,55 @@ +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:lc_and_hardened:0.1" +description: "Hardened OR function for life cycle signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:lc_sync + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_and_hardened.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_lc_and_hardened.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lc_combine.core b/synth/prim/prim_lc_combine.core new file mode 100755 index 0000000..1437edc --- /dev/null +++ b/synth/prim/prim_lc_combine.core @@ -0,0 +1,53 @@ +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:lc_combine:0.1" +description: "Logic combination of life cycle control signals." +filesets: + files_rtl: + depend: + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_combine.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_lc_combine.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lc_dec.core b/synth/prim/prim_lc_dec.core new file mode 100755 index 0000000..c22e4b6 --- /dev/null +++ b/synth/prim/prim_lc_dec.core @@ -0,0 +1,54 @@ +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:lc_dec:0.1" +description: "Decoder for life cycle control signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:buf + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_dec.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_lc_sync.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lc_or_hardened.core b/synth/prim/prim_lc_or_hardened.core new file mode 100755 index 0000000..47bfb43 --- /dev/null +++ b/synth/prim/prim_lc_or_hardened.core @@ -0,0 +1,55 @@ +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:lc_or_hardened:0.1" +description: "Hardened OR function for life cycle signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:lc_sync + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_or_hardened.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_lc_or_hardened.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lc_sender.core b/synth/prim/prim_lc_sender.core new file mode 100755 index 0000000..378b9ab --- /dev/null +++ b/synth/prim/prim_lc_sender.core @@ -0,0 +1,55 @@ +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:lc_sender:0.1" +description: "Sender primitive for life cycle control signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:flop + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_sender.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_lc_sender.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lc_sync.core b/synth/prim/prim_lc_sync.core new file mode 100755 index 0000000..0a8d449 --- /dev/null +++ b/synth/prim/prim_lc_sync.core @@ -0,0 +1,56 @@ +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:lc_sync:0.1" +description: "Two-stage synchronizer for life cycle control signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:flop_2sync + - lowrisc:prim:buf + - lowrisc:ip:lc_ctrl_pkg + files: + - rtl/prim_lc_sync.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_lc_sync.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_lfsr.core b/synth/prim/prim_lfsr.core new file mode 100755 index 0000000..417e818 --- /dev/null +++ b/synth/prim/prim_lfsr.core @@ -0,0 +1,54 @@ +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:lfsr:0.1" +description: "A Linear-Feedback Shift Register (LFSR) primitive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:cipher_pkg + files: + - rtl/prim_lfsr.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_lfsr.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_macros.core b/synth/prim/prim_macros.core new file mode 100755 index 0000000..70b9deb --- /dev/null +++ b/synth/prim/prim_macros.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:macros:0.1" +description: "Common support macros" +filesets: + files_rtl: + files: + - rtl/prim_macros.svh : {is_include_file : true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_max_tree.core b/synth/prim/prim_max_tree.core new file mode 100755 index 0000000..891da17 --- /dev/null +++ b/synth/prim/prim_max_tree.core @@ -0,0 +1,49 @@ +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:max_tree" +description: "Maximum computation primitive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_max_tree.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_max_tree.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_max_tree.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + formal: + filesets: + - files_rtl + toplevel: prim_max_tree + \ No newline at end of file diff --git a/synth/prim/prim_msb_extend.core b/synth/prim/prim_msb_extend.core new file mode 100755 index 0000000..5738854 --- /dev/null +++ b/synth/prim/prim_msb_extend.core @@ -0,0 +1,39 @@ +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:msb_extend" +description: "Extend msb" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_msb_extend.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_mubi.core b/synth/prim/prim_mubi.core new file mode 100755 index 0000000..f51e455 --- /dev/null +++ b/synth/prim/prim_mubi.core @@ -0,0 +1,72 @@ +CAPI=2: +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +# PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +# +# util/design/gen-mubi.py +# +name: "lowrisc:prim:mubi:0.1" +description: "Multibit types and functions" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:buf + - lowrisc:prim:flop + files: + - rtl/prim_mubi_pkg.sv + - rtl/prim_mubi4_sender.sv + - rtl/prim_mubi4_sync.sv + - rtl/prim_mubi4_dec.sv + - rtl/prim_mubi8_sender.sv + - rtl/prim_mubi8_sync.sv + - rtl/prim_mubi8_dec.sv + - rtl/prim_mubi12_sender.sv + - rtl/prim_mubi12_sync.sv + - rtl/prim_mubi12_dec.sv + - rtl/prim_mubi16_sender.sv + - rtl/prim_mubi16_sync.sv + - rtl/prim_mubi16_dec.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_mubi.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_multibit_sync.core b/synth/prim/prim_multibit_sync.core new file mode 100755 index 0000000..9ce56ee --- /dev/null +++ b/synth/prim/prim_multibit_sync.core @@ -0,0 +1,20 @@ +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:multibit_sync:0.1" +description: "Synchronizer with consistency check for multi-bit signals." +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:flop_2sync + files: + - rtl/prim_multibit_sync.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_onehot.core b/synth/prim/prim_onehot.core new file mode 100755 index 0000000..2e5a52d --- /dev/null +++ b/synth/prim/prim_onehot.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:onehot" +description: "Utilities for working with one-hot encoding" +filesets: + files_rtl: + depend: + - lowrisc:prim:util + files: + - rtl/prim_onehot_enc.sv + - rtl/prim_onehot_mux.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + #files: + # - lint/prim_onehot.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_onehot_mux.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_onehot_check.core b/synth/prim/prim_onehot_check.core new file mode 100755 index 0000000..f62943d --- /dev/null +++ b/synth/prim/prim_onehot_check.core @@ -0,0 +1,51 @@ +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:onehot_check" +description: "One-hot encoding checker" +filesets: + files_rtl: + depend: + - lowrisc:prim:util + - lowrisc:prim:assert + # TODO: remove then #13337 is resolved. + - lowrisc:prim:prim_pkg + files: + - rtl/prim_onehot_check.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_onehot_check.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_onehot_check.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + formal: + filesets: + - files_rtl + toplevel: prim_onehot_check diff --git a/synth/prim/prim_otp.core b/synth/prim/prim_otp.core new file mode 100755 index 0000000..87cb491 --- /dev/null +++ b/synth/prim/prim_otp.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:prim:otp" +description: "One-Time Programmable (OTP) memory" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + # TODO(#6604): these two dependencies are needed to + # make sure the corresponding prims are generated by primgen. + - lowrisc:prim:clock_gating + - lowrisc:prim:clock_inv + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_otp.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: otp + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_otp_pkg.core b/synth/prim/prim_otp_pkg.core new file mode 100755 index 0000000..9b603ec --- /dev/null +++ b/synth/prim/prim_otp_pkg.core @@ -0,0 +1,50 @@ +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_pkg:0.1" +description: "Package with common interface definitions for OTP primitives." +filesets: + files_rtl: + files: + - rtl/prim_otp_pkg.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/prim_pad_attr.core b/synth/prim/prim_pad_attr.core new file mode 100755 index 0000000..6ac83a1 --- /dev/null +++ b/synth/prim/prim_pad_attr.core @@ -0,0 +1,50 @@ +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_attr" +description: "PAD wrapper attributes" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:pad_wrapper_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_pad_attr.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: pad_attr + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_pad_wrapper.core b/synth/prim/prim_pad_wrapper.core new file mode 100755 index 0000000..05a3593 --- /dev/null +++ b/synth/prim/prim_pad_wrapper.core @@ -0,0 +1,50 @@ +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:pad_wrapper_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_pad_wrapper.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: pad_wrapper + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_pad_wrapper_pkg.core b/synth/prim/prim_pad_wrapper_pkg.core new file mode 100755 index 0000000..76816d8 --- /dev/null +++ b/synth/prim/prim_pad_wrapper_pkg.core @@ -0,0 +1,39 @@ +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_pkg" +description: "Package for pad wrapper" +filesets: + files_rtl: + files: + - rtl/prim_pad_wrapper_pkg.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_pkg.core b/synth/prim/prim_pkg.core new file mode 100755 index 0000000..f054f66 --- /dev/null +++ b/synth/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/synth/prim/prim_ram_1p.core b/synth/prim/prim_ram_1p.core new file mode 100755 index 0000000..cedb5e1 --- /dev/null +++ b/synth/prim/prim_ram_1p.core @@ -0,0 +1,50 @@ +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:ram_1p_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_1p.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: ram_1p + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_ram_1p_adv.core b/synth/prim/prim_ram_1p_adv.core new file mode 100755 index 0000000..e0bed14 --- /dev/null +++ b/synth/prim/prim_ram_1p_adv.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:ram_1p_adv:0.1" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:secded + - lowrisc:prim:ram_1p + files: + - rtl/prim_ram_1p_adv.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_1p_adv.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - files_rtl + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) diff --git a/synth/prim/prim_ram_1p_pkg.core b/synth/prim/prim_ram_1p_pkg.core new file mode 100755 index 0000000..c1d9324 --- /dev/null +++ b/synth/prim/prim_ram_1p_pkg.core @@ -0,0 +1,18 @@ +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_pkg" +description: "Ram 1p package" +filesets: + files_rtl: + depend: + files: + - rtl/prim_ram_1p_pkg.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_ram_1p_scr.core b/synth/prim/prim_ram_1p_scr.core new file mode 100755 index 0000000..fa96c76 --- /dev/null +++ b/synth/prim/prim_ram_1p_scr.core @@ -0,0 +1,35 @@ +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_scr:0.1" +description: "Single-port RAM primitive with data and address scrambling" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:ram_1p_adv + - lowrisc:prim:lfsr + - lowrisc:prim:buf + - lowrisc:prim:cipher + - lowrisc:prim:util_get_scramble_params + - lowrisc:dv_verilator:memutil_dpi_scrambled + files: + - rtl/prim_ram_1p_scr.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_1p_scr.vlt + file_type: vlt + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - files_rtl diff --git a/synth/prim/prim_ram_1r1w.core b/synth/prim/prim_ram_1r1w.core new file mode 100755 index 0000000..3730ff2 --- /dev/null +++ b/synth/prim/prim_ram_1r1w.core @@ -0,0 +1,50 @@ +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_1r1w" +description: "Random-access memory with 1 read-only port and 1 write-only port" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:ram_2p_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_1r1w.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: ram_1r1w + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_ram_1r1w_adv.core b/synth/prim/prim_ram_1r1w_adv.core new file mode 100755 index 0000000..218bea9 --- /dev/null +++ b/synth/prim/prim_ram_1r1w_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_1r1w_adv:0.1" +description: "Two-port (1 read-only port, 1 write-only port) RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_1r1w_async_adv + files: + - rtl/prim_ram_1r1w_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_ram_1r1w_async_adv.core b/synth/prim/prim_ram_1r1w_async_adv.core new file mode 100755 index 0000000..a1caaa6 --- /dev/null +++ b/synth/prim/prim_ram_1r1w_async_adv.core @@ -0,0 +1,22 @@ +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_1r1w_async_adv:0.1" +description: "Asynchronous two-port (1 read-only port, 1 write-only port) RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:secded + - lowrisc:prim:ram_1r1w + files: + - rtl/prim_ram_1r1w_async_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_ram_2p.core b/synth/prim/prim_ram_2p.core new file mode 100755 index 0000000..e265b83 --- /dev/null +++ b/synth/prim/prim_ram_2p.core @@ -0,0 +1,50 @@ +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:ram_2p_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_ram_2p.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: ram_2p + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_ram_2p_adv.core b/synth/prim/prim_ram_2p_adv.core new file mode 100755 index 0000000..3ff2dcd --- /dev/null +++ b/synth/prim/prim_ram_2p_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_2p_adv:0.1" +description: "Dual-port RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:ram_2p_async_adv + files: + - rtl/prim_ram_2p_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_ram_2p_async_adv.core b/synth/prim/prim_ram_2p_async_adv.core new file mode 100755 index 0000000..e5eda9d --- /dev/null +++ b/synth/prim/prim_ram_2p_async_adv.core @@ -0,0 +1,22 @@ +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_async_adv:0.1" +description: "Asynchronous dual-port RAM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + - lowrisc:prim:secded + - lowrisc:prim:ram_2p + files: + - rtl/prim_ram_2p_async_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_ram_2p_pkg.core b/synth/prim/prim_ram_2p_pkg.core new file mode 100755 index 0000000..34b3ad2 --- /dev/null +++ b/synth/prim/prim_ram_2p_pkg.core @@ -0,0 +1,18 @@ +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_pkg" +description: "Ram 2p package" +filesets: + files_rtl: + depend: + files: + - rtl/prim_ram_2p_pkg.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_reg_we_check.core b/synth/prim/prim_reg_we_check.core new file mode 100755 index 0000000..4761664 --- /dev/null +++ b/synth/prim/prim_reg_we_check.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:reg_we_check" +description: "Spurious WE strobe detector for autogenerated CSR node" +filesets: + files_rtl: + depend: + - lowrisc:prim:util + - lowrisc:prim:buf + - lowrisc:prim:onehot_check + files: + - rtl/prim_reg_we_check.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + #files: + # - lint/prim_reg_we_check.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_reg_we_check.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_rom.core b/synth/prim/prim_rom.core new file mode 100755 index 0000000..86f188c --- /dev/null +++ b/synth/prim/prim_rom.core @@ -0,0 +1,50 @@ +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:rom_pkg + - lowrisc:prim:primgen + + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_rom.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: rom + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_rom_adv.core b/synth/prim/prim_rom_adv.core new file mode 100755 index 0000000..249d6dd --- /dev/null +++ b/synth/prim/prim_rom_adv.core @@ -0,0 +1,20 @@ +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_adv:0.1" +description: "ROM primitive with advanced features" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:rom + files: + - rtl/prim_rom_adv.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_rom_pkg.core b/synth/prim/prim_rom_pkg.core new file mode 100755 index 0000000..73f4c6c --- /dev/null +++ b/synth/prim/prim_rom_pkg.core @@ -0,0 +1,18 @@ +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_pkg" +description: "Rom package" +filesets: + files_rtl: + depend: + files: + - rtl/prim_rom_pkg.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_rst_sync.core b/synth/prim/prim_rst_sync.core new file mode 100755 index 0000000..7d20700 --- /dev/null +++ b/synth/prim/prim_rst_sync.core @@ -0,0 +1,47 @@ +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:rst_sync" +description: "Primitive Reset synchronizer" +filesets: + files_rtl: + depend: + - lowrisc:prim:prim_pkg + # Needed because the generic prim_flop_2sync has a + # dependency on prim:flop. + - lowrisc:prim:flop_2sync + - lowrisc:prim:mubi + - lowrisc:prim:cdc_rand_delay + files: + - rtl/prim_rst_sync.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_rst_sync.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_sec_anchor.core b/synth/prim/prim_sec_anchor.core new file mode 100755 index 0000000..2825284 --- /dev/null +++ b/synth/prim/prim_sec_anchor.core @@ -0,0 +1,20 @@ +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:sec_anchor:0.1" +description: "Secure anchor primitives. These are nothing more than wrappers with special names" +filesets: + files_rtl: + files: + - rtl/prim_sec_anchor_buf.sv + - rtl/prim_sec_anchor_flop.sv + file_type: systemVerilogSource + depend: + - lowrisc:prim:assert + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_secded.core b/synth/prim/prim_secded.core new file mode 100755 index 0000000..13f89c2 --- /dev/null +++ b/synth/prim/prim_secded.core @@ -0,0 +1,78 @@ +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:secded:0.1" +description: "SECDED ECC primitives" +filesets: + files_rtl: + files: + - rtl/prim_secded_pkg.sv + - rtl/prim_secded_22_16_dec.sv + - rtl/prim_secded_22_16_enc.sv + - 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_64_57_dec.sv + - rtl/prim_secded_64_57_enc.sv + - rtl/prim_secded_72_64_dec.sv + - rtl/prim_secded_72_64_enc.sv + - rtl/prim_secded_hamming_22_16_dec.sv + - rtl/prim_secded_hamming_22_16_enc.sv + - rtl/prim_secded_hamming_39_32_dec.sv + - rtl/prim_secded_hamming_39_32_enc.sv + - rtl/prim_secded_hamming_72_64_dec.sv + - rtl/prim_secded_hamming_72_64_enc.sv + - rtl/prim_secded_hamming_76_68_dec.sv + - rtl/prim_secded_hamming_76_68_enc.sv + - rtl/prim_secded_inv_22_16_dec.sv + - rtl/prim_secded_inv_22_16_enc.sv + - rtl/prim_secded_inv_28_22_dec.sv + - rtl/prim_secded_inv_28_22_enc.sv + - rtl/prim_secded_inv_39_32_dec.sv + - rtl/prim_secded_inv_39_32_enc.sv + - rtl/prim_secded_inv_64_57_dec.sv + - rtl/prim_secded_inv_64_57_enc.sv + - rtl/prim_secded_inv_72_64_dec.sv + - rtl/prim_secded_inv_72_64_enc.sv + - rtl/prim_secded_inv_hamming_22_16_dec.sv + - rtl/prim_secded_inv_hamming_22_16_enc.sv + - rtl/prim_secded_inv_hamming_39_32_dec.sv + - rtl/prim_secded_inv_hamming_39_32_enc.sv + - rtl/prim_secded_inv_hamming_72_64_dec.sv + - rtl/prim_secded_inv_hamming_72_64_enc.sv + - rtl/prim_secded_inv_hamming_76_68_dec.sv + - rtl/prim_secded_inv_hamming_76_68_enc.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_secded.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_secded.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + diff --git a/synth/prim/prim_sha2.core b/synth/prim/prim_sha2.core new file mode 100755 index 0000000..85ca06a --- /dev/null +++ b/synth/prim/prim_sha2.core @@ -0,0 +1,46 @@ +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:sha2" +description: "SHA-2 (configurable multi-mode and 256) engine" +filesets: + files_rtl: + depend: + - lowrisc:prim:sha2_pkg + files: + - rtl/prim_sha2_pad.sv + - rtl/prim_sha2.sv + - rtl/prim_sha2_32.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sha2.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sha2.waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sha2.vbl + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_sha2_pkg.core b/synth/prim/prim_sha2_pkg.core new file mode 100755 index 0000000..cf89e79 --- /dev/null +++ b/synth/prim/prim_sha2_pkg.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:sha2_pkg" +description: "HMAC and SHA-2 package" +filesets: + files_rtl: + files: + - rtl/prim_sha2_pkg.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sha2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_sparse_fsm.core b/synth/prim/prim_sparse_fsm.core new file mode 100755 index 0000000..385a9f5 --- /dev/null +++ b/synth/prim/prim_sparse_fsm.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:sparse_fsm" +description: "" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_sparse_fsm_flop.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sparse_fsm_flop.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sparse_fsm_flop.waiver + file_type: waiver + + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_subreg.core b/synth/prim/prim_subreg.core new file mode 100755 index 0000000..9f326c2 --- /dev/null +++ b/synth/prim/prim_subreg.core @@ -0,0 +1,50 @@ +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:subreg" +description: "Register slices" +filesets: + files_rtl: + files: + - rtl/prim_subreg_pkg.sv + - rtl/prim_reg_cdc.sv + - rtl/prim_reg_cdc_arb.sv + - rtl/prim_subreg.sv + - rtl/prim_subreg_arb.sv + - rtl/prim_subreg_ext.sv + - rtl/prim_subreg_shadow.sv + file_type: systemVerilogSource + depend: + - lowrisc:prim:assert + - lowrisc:prim:mubi + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_subreg.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_subreg.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_sum_tree.core b/synth/prim/prim_sum_tree.core new file mode 100755 index 0000000..d984bd1 --- /dev/null +++ b/synth/prim/prim_sum_tree.core @@ -0,0 +1,48 @@ +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:sum_tree" +description: "Summation primitive for arbitrary numbers of inputs" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_sum_tree.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sum_tree.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_sum_tree.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + formal: + filesets: + - files_rtl + toplevel: prim_sum_tree diff --git a/synth/prim/prim_trivium.core b/synth/prim/prim_trivium.core new file mode 100755 index 0000000..0c19150 --- /dev/null +++ b/synth/prim/prim_trivium.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:trivium:0.1" +description: "Trivium/Bivium Stream Cipher Primitives" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:util + files: + - rtl/prim_trivium_pkg.sv + - rtl/prim_trivium.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_trivium.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_trivium.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim/prim_usb_diff_rx.core b/synth/prim/prim_usb_diff_rx.core new file mode 100755 index 0000000..f62fc02 --- /dev/null +++ b/synth/prim/prim_usb_diff_rx.core @@ -0,0 +1,49 @@ +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:usb_diff_rx" +description: "Differential receiver for USB." +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_usb_diff_rx.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: usb_diff_rx + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_util.core b/synth/prim/prim_util.core new file mode 100755 index 0000000..5f22411 --- /dev/null +++ b/synth/prim/prim_util.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:util:0.1" +description: "Utilities" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_util_pkg.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_util_get_scramble_params.core b/synth/prim/prim_util_get_scramble_params.core new file mode 100755 index 0000000..57dc1ec --- /dev/null +++ b/synth/prim/prim_util_get_scramble_params.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_get_scramble_params" +description: "DPI functions to snoop scramble key and nonce for simulation" +filesets: + files_rtl: + files: + - rtl/prim_util_get_scramble_params.svh: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_util_memload.core b/synth/prim/prim_util_memload.core new file mode 100755 index 0000000..0371bd8 --- /dev/null +++ b/synth/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.svh: {is_include_file: true} + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/prim_xnor2.core b/synth/prim/prim_xnor2.core new file mode 100755 index 0000000..360cb2c --- /dev/null +++ b/synth/prim/prim_xnor2.core @@ -0,0 +1,49 @@ +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:xnor2" +description: "Generic 2-input xnor" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xnor2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: xnor2 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_xor2.core b/synth/prim/prim_xor2.core new file mode 100755 index 0000000..07f486d --- /dev/null +++ b/synth/prim/prim_xor2.core @@ -0,0 +1,49 @@ +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:xor2" +description: "Generic 2-input xor" +filesets: + primgen_dep: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:primgen + - lowrisc:prim:assert + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xor2.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +generate: + impl: + generator: primgen + parameters: + prim_name: xor2 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - primgen_dep + generate: + - impl diff --git a/synth/prim/prim_xoshiro256pp.core b/synth/prim/prim_xoshiro256pp.core new file mode 100755 index 0000000..258a474 --- /dev/null +++ b/synth/prim/prim_xoshiro256pp.core @@ -0,0 +1,53 @@ +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:xoshiro256pp" +description: "A xoshiro256pp PRNG primitive" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + files: + - rtl/prim_xoshiro256pp.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_xoshiro256pp.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: &default_target + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl + + lint: + <<: *default_target + default_tool: verilator + parameters: + - SYNTHESIS=true + tools: + verilator: + mode: lint-only + verilator_options: + - "-Wall" diff --git a/synth/prim/primgen.core b/synth/prim/primgen.core new file mode 100755 index 0000000..3a143ee --- /dev/null +++ b/synth/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/synth/prim/rtl/prim_alert_pkg.sv b/synth/prim/rtl/prim_alert_pkg.sv new file mode 100755 index 0000000..a3594b6 --- /dev/null +++ b/synth/prim/rtl/prim_alert_pkg.sv @@ -0,0 +1,27 @@ +// 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; + + parameter alert_tx_t ALERT_TX_DEFAULT = '{alert_p: 1'b0, + alert_n: 1'b1}; + + parameter alert_rx_t ALERT_RX_DEFAULT = '{ping_p: 1'b0, + ping_n: 1'b1, + ack_p: 1'b0, + ack_n: 1'b1}; + +endpackage : prim_alert_pkg diff --git a/synth/prim/rtl/prim_alert_receiver.sv b/synth/prim/rtl/prim_alert_receiver.sv new file mode 100755 index 0000000..2463117 --- /dev/null +++ b/synth/prim/rtl/prim_alert_receiver.sv @@ -0,0 +1,384 @@ +// 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_req_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_req_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::*; + import prim_mubi_pkg::mubi4_t; +#( + // enables additional synchronization logic + parameter bit AsyncOn = 1'b0 +) ( + input clk_i, + input rst_ni, + // if set to lc_ctrl_pkg::On, this triggers the in-band alert channel + // reset, which resets both the sender and receiver FSMs into IDLE. + input mubi4_t init_trig_i, + // this triggers a ping test. keep asserted + // until ping_ok_o is asserted. + input ping_req_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 +); + + import prim_mubi_pkg::mubi4_test_true_strict; + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + logic alert_level, alert_sigint, alert_p, alert_n; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_buf #( + .Width(2) + ) u_prim_buf_in ( + .in_i({alert_tx_i.alert_n, + alert_tx_i.alert_p}), + .out_o({alert_n, + alert_p}) + ); + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) u_decode_alert ( + .clk_i, + .rst_ni, + .diff_pi ( alert_p ), + .diff_ni ( 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 [2:0] {Idle, HsAckWait, Pause0, Pause1, InitReq, InitAckWait} state_e; + state_e state_d, state_q; + logic ping_rise; + logic ping_tog_pd, ping_tog_pq, ping_tog_dn, ping_tog_nq; + logic ack_pd, ack_pq, ack_dn, ack_nq; + logic ping_req_d, ping_req_q; + logic ping_pending_d, ping_pending_q; + logic send_init; + logic send_ping; + + // signal ping request upon positive transition on ping_req_i + // signalling is performed by a level change event on the diff output + assign ping_req_d = ping_req_i; + assign ping_rise = ping_req_d && !ping_req_q; + assign ping_tog_pd = (send_init) ? 1'b0 : + (send_ping) ? ~ping_tog_pq : ping_tog_pq; + + // in-band reset is performed by sending out an integrity error on purpose. + assign ack_dn = (send_init) ? ack_pd : ~ack_pd; + assign ping_tog_dn = ~ping_tog_pd; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_generic_flop_ack ( + .clk_i, + .rst_ni, + .d_i({ack_dn, + ack_pd}), + .q_o({ack_nq, + ack_pq}) + ); + + prim_sec_anchor_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_generic_flop_ping ( + .clk_i, + .rst_ni, + .d_i({ping_tog_dn, + ping_tog_pd}), + .q_o({ping_tog_nq, + ping_tog_pq}) + ); + + // the ping pending signal is used 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_req_i & ping_pending_q); + + // diff pair outputs + assign alert_rx_o.ack_p = ack_pq; + assign alert_rx_o.ack_n = ack_nq; + + assign alert_rx_o.ping_p = ping_tog_pq; + assign alert_rx_o.ping_n = ping_tog_nq; + + // 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_pd = 1'b0; + ping_ok_o = 1'b0; + integ_fail_o = 1'b0; + alert_o = 1'b0; + send_init = 1'b0; + // by default, a ping request leads to a toogle on the differential ping pair + send_ping = ping_rise; + + unique case (state_q) + Idle: begin + // wait for handshake to be initiated + if (alert_level) begin + state_d = HsAckWait; + ack_pd = 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_pd = 1'b1; + end + end + // pause cycles between back-to-back handshakes + Pause0: state_d = Pause1; + Pause1: state_d = Idle; + // this state is only reached if an in-band reset is + // requested via the low-power logic. + InitReq: begin + // we deliberately place a sigint error on the ack and ping lines in this case. + send_init = 1'b1; + // suppress any toggles on the ping line while we are in the init phase. + send_ping = 1'b0; + // As long as init req is asserted, we remain in this state and acknowledge all incoming + // ping requests. As soon as the init request is dropped however, ping requests are not + // acked anymore such that the ping mechanism can also flag alert channels that got stuck + // in the initialization sequence. + if (mubi4_test_true_strict(init_trig_i)) begin + ping_ok_o = ping_pending_q; + // the sender will respond to the sigint error above with a sigint error on the alert lines. + // hence we treat the alert_sigint like an acknowledgement in this case. + end else if (alert_sigint) begin + state_d = InitAckWait; + end + end + // We get here if the sender has responded with alert_sigint, and init_trig_i==lc_ctrl_pkg::On + // has been deasserted. At this point, we need to wait for the alert_sigint to drop again + // before resuming normal operation. + InitAckWait: begin + // suppress any toggles on the ping line while we are in the init phase. + send_ping = 1'b0; + if (!alert_sigint) begin + state_d = Pause0; + // If we get a ping request in this cycle, or if we realize that there is an unhandled + // ping request that came in during initialization (but after init_trig_i has been + // deasserted), we signal this to the alert sender by toggling the request line. + send_ping = ping_rise || ping_pending_q; + end + end + default: state_d = Idle; + endcase + + // once the initialization sequence has been triggered, + // overrides are not allowed anymore until the initialization has been completed. + if (!(state_q inside {InitReq, InitAckWait})) begin + // in this case, abort and jump into the initialization sequence + if (mubi4_test_true_strict(init_trig_i)) begin + state_d = InitReq; + ack_pd = 1'b0; + ping_ok_o = 1'b0; + integ_fail_o = 1'b0; + alert_o = 1'b0; + send_init = 1'b1; + // if we're not busy with an init request, we clamp down all outputs + // and indicate an integrity failure. + end else if (alert_sigint) begin + state_d = Idle; + ack_pd = 1'b0; + ping_ok_o = 1'b0; + integ_fail_o = 1'b1; + alert_o = 1'b0; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + // Reset into the init request so that an alert handler reset implicitly + // triggers an in-band reset of all alert channels. + state_q <= InitReq; + ping_req_q <= 1'b0; + ping_pending_q <= 1'b0; + end else begin + state_q <= state_d; + ping_req_q <= ping_req_d; + ping_pending_q <= ping_pending_d; + end + end + + + //////////////// + // assertions // + //////////////// + +`ifdef INC_ASSERT + import prim_mubi_pkg::mubi4_test_false_loose; +`endif + + // 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. note that during init, the outgoing diffpairs are + // supposed to be incorrectly encoded on purpose. + // shift sequence two cycles to the right to avoid reset effects. + `ASSERT(PingDiffOk_A, alert_rx_o.ping_p ^ alert_rx_o.ping_n) + `ASSERT(AckDiffOk_A, ##2 $past(send_init) ^ alert_rx_o.ack_p ^ alert_rx_o.ack_n) + `ASSERT(InitReq_A, mubi4_test_true_strict(init_trig_i) && + !(state_q inside {InitReq, InitAckWait}) |=> send_init) + + // ping request at input -> need to see encoded ping request + `ASSERT(PingRequest0_A, ##1 $rose(ping_req_i) && !state_q inside {InitReq, InitAckWait} + |=> $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_req_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 + !(state_q inside {InitReq, InitAckWait}) && + mubi4_test_false_loose(init_trig_i) + |-> + ##[0:1] integ_fail_o) + `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 + |-> + ##[0:1] ping_ok_o, + clk_i, !rst_ni || integ_fail_o || mubi4_test_true_strict(init_trig_i)) + // 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 + |-> + ##[0:1] alert_o, + clk_i, !rst_ni || integ_fail_o || mubi4_test_true_strict(init_trig_i)) + end else begin : gen_sync_assert + // signal integrity check propagation + `ASSERT(SigInt_A, + alert_tx_i.alert_p == alert_tx_i.alert_n && + !(state_q inside {InitReq, InitAckWait}) && + mubi4_test_false_loose(init_trig_i) + |-> + 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 || mubi4_test_true_strict(init_trig_i)) + // 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 || mubi4_test_true_strict(init_trig_i)) + end + + // check in-band init request is always accepted + `ASSERT(InBandInitRequest_A, + mubi4_test_true_strict(init_trig_i) && + state_q != InitAckWait + |=> + state_q == InitReq) + // check in-band init sequence moves FSM into IDLE state + `ASSERT(InBandInitSequence_A, + (state_q == InitReq && + mubi4_test_true_strict(init_trig_i)) ##1 + (alert_sigint && + mubi4_test_false_loose(init_trig_i)) [*1:$] ##1 + (!alert_sigint && + mubi4_test_false_loose(init_trig_i)) [*3] + |=> + state_q == Idle) + // check there are no spurious alerts during init + `ASSERT(NoSpuriousAlertsDuringInit_A, + mubi4_test_true_strict(init_trig_i) || + (state_q inside {InitReq, InitAckWait}) + |-> + !alert_o) + // check that there are no spurious ping OKs + `ASSERT(NoSpuriousPingOksDuringInit_A, + (mubi4_test_true_strict(init_trig_i) || + (state_q inside {InitReq, InitAckWait})) && + !ping_pending_q + |-> + !ping_ok_o) + // check ping request is bypassed when in init state + `ASSERT(PingOkBypassDuringInit_A, + $rose(ping_req_i) ##1 + state_q == InitReq && + mubi4_test_true_strict(init_trig_i) + |-> + ping_ok_o) + +endmodule : prim_alert_receiver diff --git a/synth/prim/rtl/prim_alert_sender.sv b/synth/prim/rtl/prim_alert_sender.sv new file mode 100755 index 0000000..88249b5 --- /dev/null +++ b/synth/prim/rtl/prim_alert_sender.sv @@ -0,0 +1,392 @@ +// 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_req_i signal may +// be continuously asserted, in which case the alert signalling handshake +// will be repeatedly initiated. +// +// The alert_req_i signal may also be used as part of req/ack. The parent module +// can keep alert_req_i asserted until it has been ack'd (transferred to the alert +// receiver). The parent module is not required to use this. +// +// In case the alert sender parameter IsFatal is set to 1, an incoming alert +// alert_req_i is latched in a local register until the next reset, causing the +// alert sender to behave as if alert_req_i were continously asserted. +// The alert_state_o output reflects the state of this internal latching register. +// +// The alert sender also exposes an alert test input, which can be used to trigger +// single alert handshakes. This input behaves exactly the same way as the +// alert_req_i input with IsFatal set to 0. Test alerts do not cause alert_ack_o +// to be asserted, nor are they latched until reset (regardless of the value of the +// IsFatal parameter). +// +// 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, + // alert sender will latch the incoming alert event permanently and + // keep on sending alert events until the next reset. + parameter bit IsFatal = 1'b0 +) ( + input clk_i, + input rst_ni, + // alert test trigger (this will never be latched, even if IsFatal == 1) + input alert_test_i, + // native alert from the peripheral + input alert_req_i, + output logic alert_ack_o, + // state of the alert latching register + output logic alert_state_o, + // 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, ping_n, ping_p; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_buf #( + .Width(2) + ) u_prim_buf_ping ( + .in_i({alert_rx_i.ping_n, + alert_rx_i.ping_p}), + .out_o({ping_n, + ping_p}) + ); + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) u_decode_ping ( + .clk_i, + .rst_ni, + .diff_pi ( ping_p ), + .diff_ni ( ping_n ), + .level_o ( ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ping_event ), + .sigint_o ( ping_sigint ) + ); + + logic ack_sigint, ack_level, ack_n, ack_p; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_buf #( + .Width(2) + ) u_prim_buf_ack ( + .in_i({alert_rx_i.ack_n, + alert_rx_i.ack_p}), + .out_o({ack_n, + ack_p}) + ); + + prim_diff_decode #( + .AsyncOn(AsyncOn) + ) u_decode_ack ( + .clk_i, + .rst_ni, + .diff_pi ( ack_p ), + .diff_ni ( 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, + AlertHsPhase1, + AlertHsPhase2, + PingHsPhase1, + PingHsPhase2, + 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 alert_test_set_d, alert_test_set_q; + logic ping_set_d, ping_set_q, ping_clr; + logic alert_req_trigger, alert_test_trigger, ping_trigger; + + // if handshake is ongoing, capture additional alert requests. + logic alert_req; + prim_sec_anchor_buf #( + .Width(1) + ) u_prim_buf_in_req ( + .in_i(alert_req_i), + .out_o(alert_req) + ); + + assign alert_req_trigger = alert_req | alert_set_q; + if (IsFatal) begin : gen_fatal + assign alert_set_d = alert_req_trigger; + end else begin : gen_recov + assign alert_set_d = (alert_clr) ? 1'b0 : alert_req_trigger; + end + + // the alert test request is always cleared. + assign alert_test_trigger = alert_test_i | alert_test_set_q; + assign alert_test_set_d = (alert_clr) ? 1'b0 : alert_test_trigger; + + logic alert_trigger; + assign alert_trigger = alert_req_trigger | alert_test_trigger; + + assign ping_trigger = ping_set_q | ping_event; + assign ping_set_d = (ping_clr) ? 1'b0 : ping_trigger; + + + // alert event acknowledge and state (not affected by alert_test_i) + assign alert_ack_o = alert_clr & alert_set_q; + assign alert_state_o = alert_set_q; + + // 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_trigger || ping_trigger) begin + state_d = (alert_trigger) ? AlertHsPhase1 : PingHsPhase1; + alert_pd = 1'b1; + alert_nd = 1'b0; + end + end + // waiting for ack from receiver + AlertHsPhase1: begin + if (ack_level) begin + state_d = AlertHsPhase2; + end else begin + alert_pd = 1'b1; + alert_nd = 1'b0; + end + end + // wait for deassertion of ack + AlertHsPhase2: begin + if (!ack_level) begin + state_d = Pause0; + alert_clr = 1'b1; + end + end + // waiting for ack from receiver + PingHsPhase1: begin + if (ack_level) begin + state_d = PingHsPhase2; + end else begin + alert_pd = 1'b1; + alert_nd = 1'b0; + end + end + // wait for deassertion of ack + PingHsPhase2: begin + if (!ack_level) begin + ping_clr = 1'b1; + state_d = Pause0; + end + end + // pause cycles between back-to-back handshakes + Pause0: begin + state_d = Pause1; + end + // clear and ack alert request if it was set + Pause1: begin + state_d = Idle; + end + // catch parasitic states + default : state_d = Idle; + endcase + + // we have a signal integrity issue at one of the incoming diff pairs. this condition is + // signalled by setting the output diffpair to zero. If the sigint has disappeared, we clear + // the ping request state of this sender and go back to idle. + if (sigint_detected) begin + state_d = Idle; + alert_pd = 1'b0; + alert_nd = 1'b0; + ping_clr = 1'b1; + alert_clr = 1'b0; + end + end + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_flop #( + .Width (2), + .ResetValue(2'b10) + ) u_prim_flop_alert ( + .clk_i, + .rst_ni, + .d_i({alert_nd, alert_pd}), + .q_o({alert_nq, alert_pq}) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + state_q <= Idle; + alert_set_q <= 1'b0; + alert_test_set_q <= 1'b0; + ping_set_q <= 1'b0; + end else begin + state_q <= state_d; + alert_set_q <= alert_set_d; + alert_test_set_q <= alert_test_set_d; + ping_set_q <= ping_set_d; + end + end + + + //////////////// + // assertions // + //////////////// + +// however, since we use sequence constructs below, we need to wrap the entire block again. +// typically, the ASSERT macros already contain this INC_ASSERT macro. +`ifdef INC_ASSERT + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(AlertPKnownO_A, alert_tx_o) + + if (AsyncOn) begin : gen_async_assert + sequence PingSigInt_S; + alert_rx_i.ping_p == alert_rx_i.ping_n [*2]; + endsequence + sequence AckSigInt_S; + alert_rx_i.ping_p == alert_rx_i.ping_n [*2]; + endsequence + + `ifndef FPV_ALERT_NO_SIGINT_ERR + // check propagation of sigint issues to output within three cycles, or four due to CDC + // shift sequence to the right to avoid reset effects. + `ASSERT(SigIntPing_A, ##1 PingSigInt_S |-> + ##[3:4] alert_tx_o.alert_p == alert_tx_o.alert_n) + `ASSERT(SigIntAck_A, ##1 AckSigInt_S |-> + ##[3:4] alert_tx_o.alert_p == alert_tx_o.alert_n) + `endif + + // Test in-band FSM reset request (via signal integrity error) + `ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |-> ##[3:4] state_q == Idle) + `ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |-> ##[3:4] !ping_set_q) + // 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:5] 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) && + (alert_rx_i.ping_p ^ alert_rx_i.ping_n) ##2 state_q == Idle |=> + ##[0:1] $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 + sequence PingSigInt_S; + alert_rx_i.ping_p == alert_rx_i.ping_n; + endsequence + sequence AckSigInt_S; + alert_rx_i.ping_p == alert_rx_i.ping_n; + endsequence + + `ifndef FPV_ALERT_NO_SIGINT_ERR + // check propagation of sigint issues to output within one cycle + `ASSERT(SigIntPing_A, PingSigInt_S |=> + alert_tx_o.alert_p == alert_tx_o.alert_n) + `ASSERT(SigIntAck_A, AckSigInt_S |=> + alert_tx_o.alert_p == alert_tx_o.alert_n) + `endif + + // Test in-band FSM reset request (via signal integrity error) + `ASSERT(InBandInitFsm_A, PingSigInt_S or AckSigInt_S |=> state_q == Idle) + `ASSERT(InBandInitPing_A, PingSigInt_S or AckSigInt_S |=> !ping_set_q) + // 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 + + // Test the alert state output. + `ASSERT(AlertState0_A, alert_set_q === alert_state_o) + + if (IsFatal) begin : gen_fatal_assert + `ASSERT(AlertState1_A, alert_req_i |=> alert_state_o) + `ASSERT(AlertState2_A, alert_state_o |=> $stable(alert_state_o)) + `ASSERT(AlertState3_A, alert_ack_o |=> alert_state_o) + end else begin : gen_recov_assert + `ASSERT(AlertState1_A, alert_req_i && !alert_clr |=> alert_state_o) + `ASSERT(AlertState2_A, alert_req_i && alert_ack_o |=> !alert_state_o) + end + + // The alert test input should not set the alert state register. + `ASSERT(AlertTest1_A, alert_test_i && !alert_req_i && !alert_state_o |=> $stable(alert_state_o)) + + // if alert_req_i is true, handshakes should be continuously repeated + `ASSERT(AlertHs_A, alert_req_i && state_q == Idle |=> $rose(alert_tx_o.alert_p), + clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n)) + + // if alert_test_i is true, handshakes should be continuously repeated + `ASSERT(AlertTestHs_A, alert_test_i && state_q == Idle |=> $rose(alert_tx_o.alert_p), + clk_i, !rst_ni || (alert_tx_o.alert_p == alert_tx_o.alert_n)) +`endif + +`ifdef FPV_ALERT_NO_SIGINT_ERR + // Assumptions for FPV security countermeasures to ensure the alert protocol functions collectly. + `ASSUME_FPV(AckPFollowsAlertP_S, alert_rx_i.ack_p == $past(alert_tx_o.alert_p)) + `ASSUME_FPV(AckNFollowsAlertN_S, alert_rx_i.ack_n == $past(alert_tx_o.alert_n)) + `ASSUME_FPV(TriggerAlertInit_S, $stable(rst_ni) == 0 |=> alert_rx_i.ping_p == alert_rx_i.ping_n) + `ASSUME_FPV(PingDiffPair_S, ##2 alert_rx_i.ping_p != alert_rx_i.ping_n) +`endif +endmodule : prim_alert_sender diff --git a/synth/prim/rtl/prim_arbiter_fixed.sv b/synth/prim/rtl/prim_arbiter_fixed.sv new file mode 100755 index 0000000..d9677dd --- /dev/null +++ b/synth/prim/rtl/prim_arbiter_fixed.sv @@ -0,0 +1,170 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// N:1 fixed priority arbiter module (index 0 has highest prio) +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// +// See also: prim_arbiter_ppc, prim_arbiter_tree + +`include "prim_assert.sv" + +module prim_arbiter_fixed #( + parameter int N = 8, + parameter int DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + // used for assertions only + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + logic [2**(IdxW+1)-2:0] req_tree; + logic [2**(IdxW+1)-2:0] gnt_tree; + logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree; + logic [2**(IdxW+1)-2:0][DW-1:0] data_tree; + + for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the Pa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == IdxW) begin : gen_leafs + if (offset < N) begin : gen_assign + // forward path + assign req_tree[Pa] = req_i[offset]; + assign idx_tree[Pa] = offset; + assign data_tree[Pa] = data_i[offset]; + // backward (grant) path + assign gnt_o[offset] = gnt_tree[Pa]; + + end else begin : gen_tie_off + // forward path + assign req_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign data_tree[Pa] = '0; + logic unused_sigs; + assign unused_sigs = gnt_tree[Pa]; + end + // this creates the node assignments + end else begin : gen_nodes + // forward path + logic sel; // local helper variable + always_comb begin : p_node + // this always gives priority to the left child + sel = ~req_tree[C0]; + // propagate requests + req_tree[Pa] = req_tree[C0] | req_tree[C1]; + // data and index muxes + idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; + // propagate the grants back to the input + gnt_tree[C0] = gnt_tree[Pa] & ~sel; + gnt_tree[C1] = gnt_tree[Pa] & sel; + end + end + end : gen_level + end : gen_tree + + // the results can be found at the tree root + if (EnDataPort) begin : gen_data_port + assign data_o = data_tree[0]; + end else begin : gen_no_dataport + logic [DW-1:0] unused_data; + assign unused_data = data_tree[0]; + assign data_o = '1; + end + + assign idx_o = idx_tree[0]; + assign valid_o = req_tree[0]; + + // this propagates a grant back to the input + assign gnt_tree[0] = valid_o & ready_i; + end + + //////////////// + // assertions // + //////////////// + + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `ASSERT_KNOWN(ValidKnown_A, valid_o) + `ASSERT_KNOWN(GrantKnown_A, gnt_o) + `ASSERT_KNOWN(IdxKnown_A, idx_o) + + // Make sure no higher prio req is asserted + `ASSERT(Priority_A, |req_i |-> req_i[idx_o] && (((N'(1'b1) << idx_o) - 1'b1) & req_i) == '0) + + // we can only grant one requestor at a time + `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]) + +if (EnDataPort) begin: gen_data_port_assertion + // data flow + `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) +end + +endmodule : prim_arbiter_fixed diff --git a/synth/prim/rtl/prim_arbiter_ppc.sv b/synth/prim/rtl/prim_arbiter_ppc.sv new file mode 100755 index 0000000..17f3e19 --- /dev/null +++ b/synth/prim/rtl/prim_arbiter_ppc.sv @@ -0,0 +1,225 @@ +// 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 +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// +// 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). The module contains an assertion that checks +// this behavior. +// +// Also, this module contains a request stability assertion that checks that requests stay asserted +// until they have been served. This assertion can be gated by driving the req_chk_i low. This is +// a non-functional input and does not affect the designs behavior. +// +// See also: prim_arbiter_tree + +`include "prim_assert.sv" + +module prim_arbiter_ppc #( + parameter int unsigned N = 8, + parameter int unsigned DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + `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: gen_nodatapath + assign data_o = '1; + // The following signal is used to avoid possible lint errors. + logic [DW-1:0] unused_data [N]; + assign unused_data = data_i; + end + + always_comb begin + idx_o = '0; + for (int unsigned i = 0 ; i < N ; i++) begin + if (winner[i]) begin + idx_o = i[IdxW-1:0]; + end + end + end + end + + //////////////// + // assertions // + //////////////// + + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `ASSERT_KNOWN(ValidKnown_A, valid_o) + `ASSERT_KNOWN(GrantKnown_A, gnt_o) + `ASSERT_KNOWN(IdxKnown_A, idx_o) + + // grant index shall be higher index than previous index, unless no higher requests exist. + `ASSERT(RoundRobin_A, + ##1 valid_o && ready_i && $past(ready_i) && $past(valid_o) && + |(req_i & ~((N'(1) << $past(idx_o)+1) - 1)) |-> + idx_o > $past(idx_o)) + // we can only grant one requestor at a time + `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]) + +if (EnDataPort) begin: gen_data_port_assertion + // data flow + `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) +end + + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i) + // check that the arbitration decision is held if the sink is not ready + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o), + clk_i, !rst_ni || !req_chk_i) + +// FPV-only assertions with symbolic variables +`ifdef FPV_ON + // symbolic variables + int unsigned k; + bit ReadyIsStable; + bit ReqsAreStable; + + // constraints for symbolic variables + `ASSUME(KStable_M, ##1 $stable(k)) + `ASSUME(KRange_M, k < N) + // this is used enable checking for stable and unstable ready_i and req_i signals in the same run. + // the symbolic variables act like a switch that the solver can trun on and off. + `ASSUME(ReadyIsStable_M, ##1 $stable(ReadyIsStable)) + `ASSUME(ReqsAreStable_M, ##1 $stable(ReqsAreStable)) + `ASSUME(ReadyStable_M, ##1 !ReadyIsStable || $stable(ready_i)) + `ASSUME(ReqsStable_M, ##1 !ReqsAreStable || $stable(req_i)) + + // A grant implies a request + `ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k]) + + // if request and ready are constantly held at 1, we should eventually get a grant + `ASSERT(NoStarvation_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] |-> + strong(##[0:$] gnt_o[k])) + + // if N requests are constantly asserted and ready is constant 1, each request must + // be granted exactly once over a time window of N cycles for the arbiter to be fair. + for (genvar n = 1; n <= N; n++) begin : gen_fairness + integer gnt_cnt; + `ASSERT(Fairness_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] && + $countones(req_i) == n |-> + ##n gnt_cnt == $past(gnt_cnt, n) + 1) + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_cnt + if (!rst_ni) begin + gnt_cnt <= 0; + end else begin + gnt_cnt <= gnt_cnt + gnt_o[k]; + end + end + end + + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=> + req_i[k], clk_i, !rst_ni || !req_chk_i) +`endif + +endmodule : prim_arbiter_ppc diff --git a/synth/prim/rtl/prim_arbiter_tree.sv b/synth/prim/rtl/prim_arbiter_tree.sv new file mode 100755 index 0000000..93d809e --- /dev/null +++ b/synth/prim/rtl/prim_arbiter_tree.sv @@ -0,0 +1,291 @@ +// 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 +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// +// This is a tree implementation of a round robin arbiter. It has the same behavior as the PPC +// implementation in prim_arbiter_ppc, and also uses a prefix summing approach to determine the next +// request to be granted. The main difference with respect to the PPC arbiter is that the leading 1 +// detection and the prefix summation are performed with a binary tree instead of a sequential loop. +// Also, if the data port is enabled, the data is muxed based on the local arbitration decisions at +// each node of the arbiter tree. This means that the data can propagate through the tree +// simultaneously with the requests, instead of waiting for the arbitration to determine the winner +// index first. As a result, this design has a shorter critical path than other implementations, +// leading to better ovberall timing. +// +// Note that the currently winning request is held if the data sink is not ready. This behavior is +// required by some interconnect protocols (AXI, TL). The module contains an assertion that checks +// this behavior. +// +// Also, this module contains a request stability assertion that checks that requests stay asserted +// until they have been served. This assertion can be gated by driving the req_chk_i low. This is +// a non-functional input and does not affect the designs behavior. +// +// See also: prim_arbiter_ppc + +`include "prim_assert.sv" + +module prim_arbiter_tree #( + parameter int N = 8, + parameter int DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + `ASSERT_INIT(CheckNGreaterZero_A, N > 0) + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + logic [2**(IdxW+1)-2:0] req_tree; + logic [2**(IdxW+1)-2:0] prio_tree; + logic [2**(IdxW+1)-2:0] sel_tree; + logic [2**(IdxW+1)-2:0] mask_tree; + logic [2**(IdxW+1)-2:0][IdxW-1:0] idx_tree; + logic [2**(IdxW+1)-2:0][DW-1:0] data_tree; + logic [N-1:0] prio_mask_d, prio_mask_q; + + for (genvar level = 0; level < IdxW+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the Pa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == IdxW) begin : gen_leafs + if (offset < N) begin : gen_assign + // forward path (requests and data) + // all requests inputs are assigned to the request tree + assign req_tree[Pa] = req_i[offset]; + // we basically split the incoming request vector into two halves with the following + // priority assignment. the prio_mask_q register contains a prefix sum that has been + // computed using the last winning index, and hence masks out all requests at offsets + // lower or equal the previously granted index. hence, all higher indices are considered + // first in the arbitration tree nodes below, before considering the lower indices. + assign prio_tree[Pa] = req_i[offset] & prio_mask_q[offset]; + // input for the index muxes (used to compute the winner index) + assign idx_tree[Pa] = offset; + // input for the data muxes + assign data_tree[Pa] = data_i[offset]; + + // backward path (grants and prefix sum) + // grant if selected, ready and request asserted + assign gnt_o[offset] = req_i[offset] & sel_tree[Pa] & ready_i; + // only update mask if there is a valid request + assign prio_mask_d[offset] = (|req_i) ? + mask_tree[Pa] | sel_tree[Pa] & ~ready_i : + prio_mask_q[offset]; + end else begin : gen_tie_off + // forward path + assign req_tree[Pa] = '0; + assign prio_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign data_tree[Pa] = '0; + logic unused_sigs; + assign unused_sigs = ^{mask_tree[Pa], + sel_tree[Pa]}; + end + // this creates the node assignments + end else begin : gen_nodes + // local helper variable + logic sel; + + // forward path (requests and data) + // each node looks at its two children, and selects the one with higher priority + assign sel = ~req_tree[C0] | ~prio_tree[C0] & prio_tree[C1]; + // propagate requests + assign req_tree[Pa] = req_tree[C0] | req_tree[C1]; + assign prio_tree[Pa] = prio_tree[C1] | prio_tree[C0]; + // data and index muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. + assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + assign data_tree[Pa] = (sel) ? data_tree[C1] : data_tree[C0]; + + // backward path (grants and prefix sum) + // this propagates the selction index back and computes a hot one mask + assign sel_tree[C0] = sel_tree[Pa] & ~sel; + assign sel_tree[C1] = sel_tree[Pa] & sel; + // this performs a prefix sum for masking the input requests in the next cycle + assign mask_tree[C0] = mask_tree[Pa]; + assign mask_tree[C1] = mask_tree[Pa] | sel_tree[C0]; + end + end : gen_level + end : gen_tree + + // the results can be found at the tree root + if (EnDataPort) begin : gen_data_port + assign data_o = data_tree[0]; + end else begin : gen_no_dataport + logic [DW-1:0] unused_data; + assign unused_data = data_tree[0]; + assign data_o = '1; + end + + // This index is unused. + logic unused_prio_tree; + assign unused_prio_tree = prio_tree[0]; + + assign idx_o = idx_tree[0]; + assign valid_o = req_tree[0]; + + // the select tree computes a hot one signal that indicates which request is currently selected + assign sel_tree[0] = 1'b1; + // the mask tree is basically a prefix sum of the hot one select signal computed above + assign mask_tree[0] = 1'b0; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_mask_reg + if (!rst_ni) begin + prio_mask_q <= '0; + end else begin + prio_mask_q <= prio_mask_d; + end + end + end + + //////////////// + // assertions // + //////////////// + + // KNOWN assertions on outputs, except for data as that may be partially X in simulation + // e.g. when used on a BUS + `ASSERT_KNOWN(ValidKnown_A, valid_o) + `ASSERT_KNOWN(GrantKnown_A, gnt_o) + `ASSERT_KNOWN(IdxKnown_A, idx_o) + + // grant index shall be higher index than previous index, unless no higher requests exist. + `ASSERT(RoundRobin_A, + ##1 valid_o && ready_i && $past(ready_i) && $past(valid_o) && + |(req_i & ~((N'(1) << $past(idx_o)+1) - 1)) |-> + idx_o > $past(idx_o)) + // we can only grant one requestor at a time + `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]) + +if (EnDataPort) begin: gen_data_port_assertion + // data flow + `ASSERT(DataFlow_A, ready_i && valid_o |-> data_o == data_i[idx_o]) +end + + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted0_M, |req_i && !ready_i |=> + (req_i & $past(req_i)) == $past(req_i), clk_i, !rst_ni || !req_chk_i) + // check that the arbitration decision is held if the sink is not ready + `ASSERT(LockArbDecision_A, |req_i && !ready_i |=> idx_o == $past(idx_o), + clk_i, !rst_ni || !req_chk_i) + +// FPV-only assertions with symbolic variables +`ifdef FPV_ON + // symbolic variables + int unsigned k; + bit ReadyIsStable; + bit ReqsAreStable; + + // constraints for symbolic variables + `ASSUME(KStable_M, ##1 $stable(k)) + `ASSUME(KRange_M, k < N) + // this is used enable checking for stable and unstable ready_i and req_i signals in the same run. + // the symbolic variables act like a switch that the solver can trun on and off. + `ASSUME(ReadyIsStable_M, ##1 $stable(ReadyIsStable)) + `ASSUME(ReqsAreStable_M, ##1 $stable(ReqsAreStable)) + `ASSUME(ReadyStable_M, ##1 !ReadyIsStable || $stable(ready_i)) + `ASSUME(ReqsStable_M, ##1 !ReqsAreStable || $stable(req_i)) + + // A grant implies a request + `ASSERT(GntImpliesReq_A, gnt_o[k] |-> req_i[k]) + + // if request and ready are constantly held at 1, we should eventually get a grant + `ASSERT(NoStarvation_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] |-> + strong(##[0:$] gnt_o[k])) + + // if N requests are constantly asserted and ready is constant 1, each request must + // be granted exactly once over a time window of N cycles for the arbiter to be fair. + for (genvar n = 1; n <= N; n++) begin : gen_fairness + integer gnt_cnt; + `ASSERT(Fairness_A, + ReqsAreStable && ReadyIsStable && ready_i && req_i[k] && + $countones(req_i) == n |-> + ##n gnt_cnt == $past(gnt_cnt, n) + 1) + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_cnt + if (!rst_ni) begin + gnt_cnt <= 0; + end else begin + gnt_cnt <= gnt_cnt + gnt_o[k]; + end + end + end + + // requests must stay asserted until they have been granted + `ASSUME(ReqStaysHighUntilGranted1_M, req_i[k] && !gnt_o[k] |=> + req_i[k], clk_i, !rst_ni || !req_chk_i) +`endif + +endmodule : prim_arbiter_tree diff --git a/synth/prim/rtl/prim_arbiter_tree_dup.sv b/synth/prim/rtl/prim_arbiter_tree_dup.sv new file mode 100755 index 0000000..8ed2a7c --- /dev/null +++ b/synth/prim/rtl/prim_arbiter_tree_dup.sv @@ -0,0 +1,148 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This is a wrapper module that instantiates two prim_aribter_tree modules. +// The reason for two is similar to modules such as prim_count/prim_lfsr where +// we use spatial redundancy to ensure the arbitration results are not altered. +// +// Note there are limits to this check, as the inputs to the duplicated arbiter +// is still a single lane. So an upstream attack can defeat this module. The +// duplication merely protects against attacks directly on the arbiter. + +`include "prim_assert.sv" + +module prim_arbiter_tree_dup #( + parameter int N = 8, + parameter int DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // if arbiter has fixed priority + parameter bit FixedArb = 0, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i, + output logic err_o +); + + localparam int ArbInstances = 2; + + //typedef struct packed { + // logic [N-1:0] req; + // logic [N-1:0][DW-1:0] data; + //} arb_inputs_t; + + typedef struct packed { + logic valid; + logic [N-1:0] gnt; + logic [IdxW-1:0] idx; + logic [DW-1:0] data; + } arb_outputs_t; + + // buffer up the inputs separately for each instance + //arb_inputs_t arb_in; + //arb_inputs_t [ArbInstances-1:0] arb_input_buf; + arb_outputs_t [ArbInstances-1:0] arb_output_buf; + + for (genvar i = 0; i < ArbInstances; i++) begin : gen_input_bufs + logic [N-1:0] req_buf; + prim_buf #( + .Width(N) + ) u_req_buf ( + .in_i(req_i), + .out_o(req_buf) + ); + + logic [DW-1:0] data_buf [N]; + for (genvar j = 0; j < N; j++) begin : gen_data_bufs + prim_buf #( + .Width(DW) + ) u_dat_buf ( + .in_i(data_i[j]), + .out_o(data_buf[j]) + ); + end + + if (FixedArb) begin : gen_fixed_arbiter + prim_arbiter_fixed #( + .N(N), + .DW(DW), + .EnDataPort(EnDataPort) + ) u_arb ( + .clk_i, + .rst_ni, + .req_i(req_buf), + .data_i(data_buf), + .gnt_o(arb_output_buf[i].gnt), + .idx_o(arb_output_buf[i].idx), + .valid_o(arb_output_buf[i].valid), + .data_o(arb_output_buf[i].data), + .ready_i + ); + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + end else begin : gen_rr_arbiter + prim_arbiter_tree #( + .N(N), + .DW(DW), + .EnDataPort(EnDataPort) + ) u_arb ( + .clk_i, + .rst_ni, + .req_chk_i, + .req_i(req_buf), + .data_i(data_buf), + .gnt_o(arb_output_buf[i].gnt), + .idx_o(arb_output_buf[i].idx), + .valid_o(arb_output_buf[i].valid), + .data_o(arb_output_buf[i].data), + .ready_i + ); + end + end + + // the last buffered position is sent out + assign gnt_o = arb_output_buf[ArbInstances-1].gnt; + assign idx_o = arb_output_buf[ArbInstances-1].idx; + assign valid_o = arb_output_buf[ArbInstances-1].valid; + assign data_o = arb_output_buf[ArbInstances-1].data; + + // Check the last buffer index against all other instances + logic [ArbInstances-2:0] output_delta; + + for (genvar i = 0; i < ArbInstances-1; i++) begin : gen_checks + assign output_delta[i] = arb_output_buf[ArbInstances-1] != arb_output_buf[i]; + end + + logic err_d, err_q; + // There is an error if anything ever disagrees + assign err_d = |output_delta; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_q <= '0; + end else begin + err_q <= err_d | err_q; + end + end + + assign err_o = err_q; + +endmodule // prim_arbiter_tree diff --git a/synth/prim/rtl/prim_assert.sv b/synth/prim/rtl/prim_assert.sv new file mode 100755 index 0000000..540808b --- /dev/null +++ b/synth/prim/rtl/prim_assert.sv @@ -0,0 +1,188 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Macros and helper code for using assertions. +// - Provides default clk and rst options to simplify code +// - Provides boiler plate template for common assertions + +`ifndef PRIM_ASSERT_SV +`define PRIM_ASSERT_SV + +/////////////////// +// Helper macros // +/////////////////// + +// Default clk and reset signals used by assertion macros below. +`define ASSERT_DEFAULT_CLK clk_i +`define ASSERT_DEFAULT_RST !rst_ni + +// Converts an arbitrary block of code into a Verilog string +`define PRIM_STRINGIFY(__x) `"__x`" + +// ASSERT_ERROR logs an error message with either `uvm_error or with $error. +// +// This somewhat duplicates `DV_ERROR macro defined in hw/dv/sv/dv_utils/dv_macros.svh. The reason +// for redefining it here is to avoid creating a dependency. +`define ASSERT_ERROR(__name) \ +`ifdef UVM \ + uvm_pkg::uvm_report_error("ASSERT FAILED", `PRIM_STRINGIFY(__name), uvm_pkg::UVM_NONE, \ + `__FILE__, `__LINE__, "", 1); \ +`else \ + $error("%0t: (%0s:%0d) [%m] [ASSERT FAILED] %0s", $time, `__FILE__, `__LINE__, \ + `PRIM_STRINGIFY(__name)); \ +`endif + +// This macro is suitable for conditionally triggering lint errors, e.g., if a Sec parameter takes +// on a non-default value. This may be required for pre-silicon/FPGA evaluation but we don't want +// to allow this for tapeout. +`define ASSERT_STATIC_LINT_ERROR(__name, __prop) \ + localparam int __name = (__prop) ? 1 : 2; \ + always_comb begin \ + logic unused_assert_static_lint_error; \ + unused_assert_static_lint_error = __name'(1'b1); \ + end + +// Static assertions for checks inside SV packages. If the conditions is not true, this will +// trigger an error during elaboration. +`define ASSERT_STATIC_IN_PACKAGE(__name, __prop) \ + function automatic bit assert_static_in_package_``__name(); \ + bit unused_bit [((__prop) ? 1 : -1)]; \ + unused_bit = '{default: 1'b0}; \ + return unused_bit[0]; \ + endfunction + +// The basic helper macros are actually defined in "implementation headers". The macros should do +// the same thing in each case (except for the dummy flavour), but in a way that the respective +// tools support. +// +// If the tool supports assertions in some form, we also define INC_ASSERT (which can be used to +// hide signal definitions that are only used for assertions). +// +// The list of basic macros supported is: +// +// ASSERT_I: Immediate assertion. Note that immediate assertions are sensitive to simulation +// glitches. +// +// ASSERT_INIT: Assertion in initial block. Can be used for things like parameter checking. +// +// ASSERT_INIT_NET: Assertion in initial block. Can be used for initial value of a net. +// +// ASSERT_FINAL: Assertion in final block. Can be used for things like queues being empty at end of +// sim, all credits returned at end of sim, state machines in idle at end of sim. +// +// ASSERT_AT_RESET: Assertion just before reset. Can be used to check sum-like properties that get +// cleared at reset. +// Note that unless your simulation ends with a reset, the property does not get +// checked at end of simulation; use ASSERT_AT_RESET_AND_FINAL if the property +// should also get checked at end of simulation. +// +// ASSERT_AT_RESET_AND_FINAL: Assertion just before reset and in final block. Can be used to check +// sum-like properties before every reset and at the end of simulation. +// +// ASSERT: Assert a concurrent property directly. It can be called as a module (or +// interface) body item. +// +// Note: We use (__rst !== '0) in the disable iff statements instead of (__rst == +// '1). This properly disables the assertion in cases when reset is X at the +// beginning of a simulation. For that case, (reset == '1) does not disable the +// assertion. +// +// ASSERT_NEVER: Assert a concurrent property NEVER happens +// +// ASSERT_KNOWN: Assert that signal has a known value (each bit is either '0' or '1') after reset. +// It can be called as a module (or interface) body item. +// +// COVER: Cover a concurrent property +// +// ASSUME: Assume a concurrent property +// +// ASSUME_I: Assume an immediate property + +`ifdef VERILATOR + `include "prim_assert_dummy_macros.svh" +`elsif SYNTHESIS + `include "prim_assert_dummy_macros.svh" +`elsif YOSYS + `include "prim_assert_yosys_macros.svh" + `define INC_ASSERT +`else + `include "prim_assert_standard_macros.svh" + `define INC_ASSERT +`endif + +////////////////////////////// +// Complex assertion macros // +////////////////////////////// + +// Assert that signal is an active-high pulse with pulse length of 1 clock cycle +`define ASSERT_PULSE(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT(__name, $rose(__sig) |=> !(__sig), __clk, __rst) + +// Assert that a property is true only when an enable signal is set. It can be called as a module +// (or interface) body item. +`define ASSERT_IF(__name, __prop, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT(__name, (__enable) |-> (__prop), __clk, __rst) + +// Assert that signal has a known value (each bit is either '0' or '1') after reset if enable is +// set. It can be called as a module (or interface) body item. +`define ASSERT_KNOWN_IF(__name, __sig, __enable, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT_KNOWN(__name``KnownEnable, __enable, __clk, __rst) \ + `ASSERT_IF(__name, !$isunknown(__sig), __enable, __clk, __rst) + +////////////////////////////////// +// For formal verification only // +////////////////////////////////// + +// Note that the existing set of ASSERT macros specified above shall be used for FPV, +// thereby ensuring that the assertions are evaluated during DV simulations as well. + +// ASSUME_FPV +// Assume a concurrent property during formal verification only. +`define ASSUME_FPV(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef FPV_ON \ + `ASSUME(__name, __prop, __clk, __rst) \ +`endif + +// ASSUME_I_FPV +// Assume a concurrent property during formal verification only. +`define ASSUME_I_FPV(__name, __prop) \ +`ifdef FPV_ON \ + `ASSUME_I(__name, __prop) \ +`endif + +// COVER_FPV +// Cover a concurrent property during formal verification +`define COVER_FPV(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ +`ifdef FPV_ON \ + `COVER(__name, __prop, __clk, __rst) \ +`endif + +// FPV assertion that proves that the FSM control flow is linear (no loops) +// The sequence triggers whenever the state changes and stores the current state as "initial_state". +// Then thereafter we must never see that state again until reset. +// It is possible for the reset to release ahead of the clock. +// Create a small "gray" window beyond the usual rst time to avoid +// checking. +`define ASSERT_FPV_LINEAR_FSM(__name, __state, __type, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + `ifdef INC_ASSERT \ + bit __name``_cond; \ + always_ff @(posedge __clk or posedge __rst) begin \ + if (__rst) begin \ + __name``_cond <= 0; \ + end else begin \ + __name``_cond <= 1; \ + end \ + end \ + property __name``_p; \ + __type initial_state; \ + (!$stable(__state) & __name``_cond, initial_state = $past(__state)) |-> \ + (__state != initial_state) until (__rst == 1'b1); \ + endproperty \ + `ASSERT(__name, __name``_p, __clk, __rst) \ + `endif + +`include "prim_assert_sec_cm.svh" +`include "prim_flop_macros.sv" + +`endif // PRIM_ASSERT_SV diff --git a/synth/prim/rtl/prim_assert_dummy_macros.svh b/synth/prim/rtl/prim_assert_dummy_macros.svh new file mode 100755 index 0000000..c2a5313 --- /dev/null +++ b/synth/prim/rtl/prim_assert_dummy_macros.svh @@ -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 + +// Macro bodies included by prim_assert.sv for tools that don't support assertions. See +// prim_assert.sv for documentation for each of the macros. + +`define ASSERT_I(__name, __prop) +`define ASSERT_INIT(__name, __prop) +`define ASSERT_INIT_NET(__name, __prop) +`define ASSERT_FINAL(__name, __prop) +`define ASSERT_AT_RESET(__name, __prop, __rst = `ASSERT_DEFAULT_RST) +`define ASSERT_AT_RESET_AND_FINAL(__name, __prop, __rst = `ASSERT_DEFAULT_RST) +`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) +`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) +`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) +`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) +`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) +`define ASSUME_I(__name, __prop) diff --git a/synth/prim/rtl/prim_assert_sec_cm.svh b/synth/prim/rtl/prim_assert_sec_cm.svh new file mode 100755 index 0000000..b9f077f --- /dev/null +++ b/synth/prim/rtl/prim_assert_sec_cm.svh @@ -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 + +// // Macros and helper code for security countermeasures. + +`ifndef PRIM_ASSERT_SEC_CM_SVH +`define PRIM_ASSERT_SEC_CM_SVH + +`define _SEC_CM_ALERT_MAX_CYC 30 + +// Helper macros +`define ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_, MAX_CYCLES_, ERR_NAME_) \ + `ASSERT(FpvSecCm``NAME_``, \ + $rose(PRIM_HIER_.ERR_NAME_) && !(GATE_) \ + |-> ##[0:MAX_CYCLES_] (ALERT_.alert_p)) \ + `ifdef INC_ASSERT \ + assign PRIM_HIER_.unused_assert_connected = 1'b1; \ + `endif \ + `ASSUME_FPV(``NAME_``TriggerAfterAlertInit_S, $stable(rst_ni) == 0 |-> \ + PRIM_HIER_.ERR_NAME_ == 0 [*10]) + +`define ASSERT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_, MAX_CYCLES_, ERR_NAME_, CLK_, RST_) \ + `ASSERT(FpvSecCm``NAME_``, \ + $rose(PRIM_HIER_.ERR_NAME_) && !(GATE_) \ + |-> ##[0:MAX_CYCLES_] (ERR_), CLK_, RST_) \ + `ifdef INC_ASSERT \ + assign PRIM_HIER_.unused_assert_connected = 1'b1; \ + `endif + +// macros for security countermeasures that will trigger alert +`define ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC) \ + `ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_, MAX_CYCLES_, err_o) + +`define ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC) \ + `ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_, MAX_CYCLES_, err_o) + +`define ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC) \ + `ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_, MAX_CYCLES_, unused_err_o) + +`define ASSERT_PRIM_ONEHOT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC) \ + `ASSERT_ERROR_TRIGGER_ALERT(NAME_, PRIM_HIER_, ALERT_, GATE_, MAX_CYCLES_, err_o) + +`define ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(NAME_, REG_TOP_HIER_, ALERT_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC) \ + `ASSERT_PRIM_ONEHOT_ERROR_TRIGGER_ALERT(NAME_, \ + REG_TOP_HIER_.u_prim_reg_we_check.u_prim_onehot_check, ALERT_, GATE_, MAX_CYCLES_) + +// macros for security countermeasures that will trigger other errors +`define ASSERT_PRIM_FSM_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_ = 0, MAX_CYCLES_ = 2, CLK_ = clk_i, RST_ = !rst_ni) \ + `ASSERT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_, MAX_CYCLES_, unused_err_o, CLK_, RST_) + +`define ASSERT_PRIM_COUNT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_ = 0, MAX_CYCLES_ = 2, CLK_ = clk_i, RST_ = !rst_ni) \ + `ASSERT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_, MAX_CYCLES_, err_o, CLK_, RST_) + +`define ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_ = 0, MAX_CYCLES_ = 2, CLK_ = clk_i, RST_ = !rst_ni) \ + `ASSERT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_, MAX_CYCLES_, err_o, CLK_, RST_) + +`define ASSERT_PRIM_ONEHOT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC, CLK_ = clk_i, RST_ = !rst_ni) \ + `ASSERT_ERROR_TRIGGER_ERR(NAME_, PRIM_HIER_, ERR_, GATE_, MAX_CYCLES_, err_o, CLK_, RST_) + +`define ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ERR(NAME_, REG_TOP_HIER_, ERR_, GATE_ = 0, MAX_CYCLES_ = `_SEC_CM_ALERT_MAX_CYC, CLK_ = clk_i, RST_ = !rst_ni) \ + `ASSERT_PRIM_ONEHOT_ERROR_TRIGGER_ERR(NAME_, \ + REG_TOP_HIER_.u_prim_reg_we_check.u_prim_onehot_check, ERR_, GATE_, MAX_CYCLES_, CLK_, RST_) + +`endif // PRIM_ASSERT_SEC_CM_SVH diff --git a/synth/prim/rtl/prim_assert_standard_macros.svh b/synth/prim/rtl/prim_assert_standard_macros.svh new file mode 100755 index 0000000..bfa0738 --- /dev/null +++ b/synth/prim/rtl/prim_assert_standard_macros.svh @@ -0,0 +1,95 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Macro bodies included by prim_assert.sv for tools that support full SystemVerilog and SVA syntax. +// See prim_assert.sv for documentation for each of the macros. + +`define ASSERT_I(__name, __prop) \ + __name: assert (__prop) \ + else begin \ + `ASSERT_ERROR(__name) \ + end + +// Formal tools will ignore the initial construct, so use static assertion as a workaround. +// This workaround terminates design elaboration if the __prop predict is false. +// It calls $fatal() with the first argument equal to 2, it outputs the statistics about the memory +// and CPU time. +`define ASSERT_INIT(__name, __prop) \ +`ifdef FPV_ON \ + if (!(__prop)) $fatal(2, "Fatal static assertion [%s]: (%s) is not true.", \ + (__name), (__prop)); \ +`else \ + initial begin \ + __name: assert (__prop) \ + else begin \ + `ASSERT_ERROR(__name) \ + end \ + end \ +`endif + +`define ASSERT_INIT_NET(__name, __prop) \ + initial begin \ + // When a net is assigned with a value, the assignment is evaluated after \ + // initial in Xcelium. Add 1ps delay to check value after the assignment is \ + // completed. \ + #1ps; \ + __name: assert (__prop) \ + else begin \ + `ASSERT_ERROR(__name) \ + end \ + end \ + +`define ASSERT_FINAL(__name, __prop) \ + final begin \ + __name: assert (__prop || $test$plusargs("disable_assert_final_checks")) \ + else begin \ + `ASSERT_ERROR(__name) \ + end \ + end + +`define ASSERT_AT_RESET(__name, __prop, __rst = `ASSERT_DEFAULT_RST) \ + // `__rst` is active-high for these macros, so trigger on its posedge. \ + // The values inside the property are sampled just before the trigger, \ + // which is necessary to make the evaluation of `__prop` on a reset edge \ + // meaningful. On any reset posedge at the start of time, `__rst` itself \ + // is unknown, and at that time `__prop` is likely not initialized either, \ + // so this assertion does not evaluate `__prop` when `__rst` is unknown. \ + __name: assert property (@(posedge __rst) $isunknown(__rst) || (__prop)) \ + else begin \ + `ASSERT_ERROR(__name) \ + end + +`define ASSERT_AT_RESET_AND_FINAL(__name, __prop, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT_AT_RESET(AtReset_``__name``, __prop, __rst) \ + `ASSERT_FINAL(Final_``__name``, __prop) + +`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + __name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \ + else begin \ + `ASSERT_ERROR(__name) \ + end + +`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + __name: assert property (@(posedge __clk) disable iff ((__rst) !== '0) not (__prop)) \ + else begin \ + `ASSERT_ERROR(__name) \ + end + +`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT(__name, !$isunknown(__sig), __clk, __rst) + +`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + __name: cover property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)); + +`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + __name: assume property (@(posedge __clk) disable iff ((__rst) !== '0) (__prop)) \ + else begin \ + `ASSERT_ERROR(__name) \ + end + +`define ASSUME_I(__name, __prop) \ + __name: assume (__prop) \ + else begin \ + `ASSERT_ERROR(__name) \ + end diff --git a/synth/prim/rtl/prim_assert_yosys_macros.svh b/synth/prim/rtl/prim_assert_yosys_macros.svh new file mode 100755 index 0000000..81102ec --- /dev/null +++ b/synth/prim/rtl/prim_assert_yosys_macros.svh @@ -0,0 +1,60 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Macro bodies included by prim_assert.sv for formal verification with Yosys. See prim_assert.sv +// for documentation for each of the macros. + +`define ASSERT_I(__name, __prop) \ + always_comb begin : __name \ + assert (__prop); \ + end + +`define ASSERT_INIT(__name, __prop) \ + initial begin : __name \ + assert (__prop); \ + end + +`define ASSERT_INIT_NET(__name, __prop) \ + initial begin : __name \ + #1ps assert (__prop); \ + end + +// This doesn't make much sense for a formal tool (we never get to the final block!) +`define ASSERT_FINAL(__name, __prop) + +// This needs sampling just before reset assertion and thus requires an event scheduler, which Yosys +// may or may not implement, so we leave it blank for the time being. +`define ASSERT_AT_RESET(__name, __prop, __rst = `ASSERT_DEFAULT_RST) + +`define ASSERT_AT_RESET_AND_FINAL(__name, __prop, __rst = `ASSERT_DEFAULT_RST) \ + `ASSERT_AT_RESET(AtReset_``__name``, __prop, __rst) \ + `ASSERT_FINAL(Final_``__name``, __prop) + +`define ASSERT(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + always_ff @(posedge __clk) begin \ + if (! (__rst !== '0)) __name: assert (__prop); \ + end + +`define ASSERT_NEVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + always_ff @(posedge __clk) begin \ + if (! (__rst !== '0)) __name: assert (! (__prop)); \ + end + +// Yosys uses 2-state logic, so this doesn't make sense here +`define ASSERT_KNOWN(__name, __sig, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) + +`define COVER(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + always_ff @(posedge __clk) begin : __name \ + cover ((! (__rst !== '0)) && (__prop)); \ + end + +`define ASSUME(__name, __prop, __clk = `ASSERT_DEFAULT_CLK, __rst = `ASSERT_DEFAULT_RST) \ + always_ff @(posedge __clk) begin \ + if (! (__rst !== '0)) __name: assume (__prop); \ + end + +`define ASSUME_I(__name, __prop) \ + always_comb begin : __name \ + assume (__prop); \ + end diff --git a/synth/prim/rtl/prim_blanker.sv b/synth/prim/rtl/prim_blanker.sv new file mode 100755 index 0000000..2253da3 --- /dev/null +++ b/synth/prim/rtl/prim_blanker.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Convenience module for wrapping prim_and2 for use in blanking. +// When en_i == 1 the input is fed through to the output. +// When en_i == 0 the output is 0. +module prim_blanker #( + parameter int Width = 1 +) ( + input logic [Width-1:0] in_i, + input logic en_i, + output logic [Width-1:0] out_o +); + prim_and2 #(.Width(Width)) u_blank_and ( + .in0_i(in_i), + .in1_i({Width{en_i}}), + .out_o + ); +endmodule diff --git a/synth/prim/rtl/prim_cdc_rand_delay.sv b/synth/prim/rtl/prim_cdc_rand_delay.sv new file mode 100755 index 0000000..0318e2c --- /dev/null +++ b/synth/prim/rtl/prim_cdc_rand_delay.sv @@ -0,0 +1,67 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// This module should be instantiated within flops of CDC synchronization primitives, +// and allows DV to model real CDC delays within simulations, especially useful at the chip level +// or in IPs that communicate across clock domains. +// +// The instrumentation is very simple: when this is enabled the input into the first +// synchronizer flop has a mux where the select is randomly set. One of the mux inputs is the input +// of this module, and the other is the output of the first flop: selecting the latter models the +// effect of the first flop missing the input transition. +// +// Notice the delay should cause the input to be skipped by at most a single cycle. As a perhaps +// unnecessary precaution, the select will only be allowed to be random when the input changes. + +module prim_cdc_rand_delay #( + parameter int DataWidth = 1, + parameter bit Enable = 1 +) ( + input logic clk_i, + input logic rst_ni, + input logic [DataWidth-1:0] prev_data_i, + input logic [DataWidth-1:0] src_data_i, + output logic [DataWidth-1:0] dst_data_o +); +`ifdef SIMULATION + if (Enable) begin : gen_enable + + // This controls dst_data_o: any bit with its data_sel set uses prev_data_i, others use + // src_data_i. + bit [DataWidth-1:0] data_sel; + bit cdc_instrumentation_enabled; + + function automatic bit [DataWidth-1:0] fast_randomize(); + bit [DataWidth-1:0] data; + if (DataWidth <= 32) begin + data = $urandom(); + end else begin + if (!std::randomize(data)) $fatal(1, "%t: [%m] Failed to randomize data", $time); + end + return data; + endfunction + + initial begin + void'($value$plusargs("cdc_instrumentation_enabled=%d", cdc_instrumentation_enabled)); + data_sel = '0; + end + + // Set data_sel at random combinationally when the input changes. + always @(src_data_i) begin + data_sel = cdc_instrumentation_enabled ? fast_randomize() : 0; + end + + // Clear data_del on any cycle start. + always @(posedge clk_i or negedge rst_ni) begin + data_sel <= 0; + end + + always_comb dst_data_o = (prev_data_i & data_sel) | (src_data_i & ~data_sel); + end else begin : gen_no_enable + assign dst_data_o = src_data_i; + end +`else // SIMULATION + assign dst_data_o = src_data_i; +`endif // SIMULATION +endmodule diff --git a/synth/prim/rtl/prim_cipher_pkg.sv b/synth/prim/rtl/prim_cipher_pkg.sv new file mode 100755 index 0000000..742c925 --- /dev/null +++ b/synth/prim/rtl/prim_cipher_pkg.sv @@ -0,0 +1,397 @@ +// 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'hF, 4'hA, 4'h5, 4'h0, + 4'hB, 4'h6, 4'h1, 4'hC, + 4'h7, 4'h2, 4'hD, 4'h8, + 4'h3, 4'hE, 4'h9, 4'h4}; + + parameter logic [15:0][3:0] PRINCE_SHIFT_ROWS64_INV = '{4'hF, 4'h2, 4'h5, 4'h8, + 4'hB, 4'hE, 4'h1, 4'h4, + 4'h7, 4'hA, 4'hD, 4'h0, + 4'h3, 4'h6, 4'h9, 4'hC}; + + // 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'h7BDE; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST1 = 16'hBDE7; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST2 = 16'hDE7B; + parameter logic [15:0] PRINCE_SHIFT_ROWS_CONST3 = 16'hE7BD; + + // 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_CONST3); + state_out[4 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[8 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[12 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + // M1 + state_out[16 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[20 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[24 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[28 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + 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_CONST3); + state_out[4 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[8 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[12 +: 4] = prince_nibble_red16(state_in[ 0 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + // M1 + state_out[16 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[20 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[24 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[28 +: 4] = prince_nibble_red16(state_in[16 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + // M1 + state_out[32 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + state_out[36 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[40 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[44 +: 4] = prince_nibble_red16(state_in[32 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + // M0 + state_out[48 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST3); + state_out[52 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST2); + state_out[56 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST1); + state_out[60 +: 4] = prince_nibble_red16(state_in[48 +: 16] & PRINCE_SHIFT_ROWS_CONST0); + 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 = {key_in[63-61:0], key_in[63: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 = {key_in[79-61:0], key_in[79: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 = {key_in[127-61:0], key_in[127:128-61]}; + // sbox on uppermost 4 bits + key_out[127 -: 4] = PRESENT_SBOX4[key_out[127 -: 4]]; + // sbox on second nibble from top + key_out[123 -: 4] = PRESENT_SBOX4[key_out[123 -: 4]]; + // xor in round counter on bits 66 to 62 + key_out[66:62] ^= 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); + logic [63:0] key_out = key_in; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= 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 = {key_out[60:0], key_out[63: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); + logic [79:0] key_out = key_in; + // xor in round counter on bits 19 to 15 + key_out[19:15] ^= 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 = {key_out[60:0], key_out[79: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); + logic [127:0] key_out = key_in; + // xor in round counter on bits 66 to 62 + key_out[66:62] ^= round_idx; + // sbox on second highest nibble + key_out[123 -: 4] = PRESENT_SBOX4_INV[key_out[123 -: 4]]; + // sbox on uppermost 4 bits + key_out[127 -: 4] = PRESENT_SBOX4_INV[key_out[127 -: 4]]; + // rotate by 61 to the right + key_out = {key_out[60:0], key_out[127: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 unsigned 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 unsigned 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 unsigned 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 [7:0] sbox4_8bit(logic [7:0] state_in, logic [15:0][3:0] sbox4); + logic [7:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 8/4; k++) begin + state_out[k*4 +: 4] = sbox4[state_in[k*4 +: 4]]; + end + return state_out; + endfunction : sbox4_8bit + + function automatic logic [15:0] sbox4_16bit(logic [15:0] state_in, logic [15:0][3:0] sbox4); + logic [15:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 2; k++) begin + state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4); + end + return state_out; + endfunction : sbox4_16bit + + 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 < 4; k++) begin + state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4); + 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 < 8; k++) begin + state_out[k*8 +: 8] = sbox4_8bit(state_in[k*8 +: 8], sbox4); + end + return state_out; + endfunction : sbox4_64bit + + function automatic logic [7:0] perm_8bit(logic [7:0] state_in, logic [7:0][2:0] perm); + logic [7:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 8; k++) begin + state_out[perm[k]] = state_in[k]; + end + return state_out; + endfunction : perm_8bit + + function automatic logic [15:0] perm_16bit(logic [15:0] state_in, logic [15:0][3:0] perm); + logic [15:0] state_out; + // note that if simulation performance becomes an issue, this loop can be unrolled + for (int k = 0; k < 16; k++) begin + state_out[perm[k]] = state_in[k]; + end + return state_out; + endfunction : perm_16bit + + 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[perm[k]] = state_in[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[perm[k]] = state_in[k]; + end + return state_out; + endfunction : perm_64bit + +endpackage : prim_cipher_pkg diff --git a/synth/prim/rtl/prim_clock_gating_sync.sv b/synth/prim/rtl/prim_clock_gating_sync.sv new file mode 100755 index 0000000..bcc8f75 --- /dev/null +++ b/synth/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_i(async_en_i), + .q_o(en_o) + ); + + prim_clock_gating i_cg ( + .clk_i, + .en_i(en_o), + .test_en_i, + .clk_o + ); + + +endmodule diff --git a/synth/prim/rtl/prim_clock_gp_mux2.sv b/synth/prim/rtl/prim_clock_gp_mux2.sv new file mode 100755 index 0000000..f4d7860 --- /dev/null +++ b/synth/prim/rtl/prim_clock_gp_mux2.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 + +// Glitch free clock mux using parallel two flop synchronizers + +module prim_clock_gp_mux2 #( + parameter bit NoFpgaBufG = 1'b0, + parameter bit FpgaBufGlobal = 1, + parameter bit GlitchProtect = 1 +) ( + input clk0_i, + input clk1_i, + input sel_i, + input rst_ni, + input test_en_i, + output logic clk_o +); + +logic [1:0] clk_gp; +logic [1:0] intq; +logic [1:0] stage_d; +logic [1:0] stage_q; +logic [1:0] clk_glitch_off; + +assign clk_gp = {clk1_i, clk0_i}; +assign stage_d = {sel_i & !stage_q[0], !sel_i & !stage_q[1]}; + +generate + genvar i; + for (i = 0; i < 2; i = i++) begin: gen_two_flops + always_ff @(posedge clk_gp[i] or negedge rst_ni) begin: stage1 + if (!rst_ni) begin + intq[i] <= 1'b0; + end else begin + intq[i] <= stage_d[i]; + end + end + + always_ff @(negedge clk_gp[i] or negedge rst_ni) begin: stage2 + if (!rst_ni) begin + stage_q[i] <= 1'b0; + end else begin + stage_q[i] <= intq[i]; + end + end + + prim_clock_gating #( + .FpgaBufGlobal(FpgaBufGlobal) + ) u_cg ( + .clk_i(clk_gp[i]), + .en_i(stage_q[i]), + .test_en_i(1'b0), + .clk_o(clk_glitch_off[i]) + ); + + end +endgenerate + +assign clk_o = |clk_glitch_off; + +endmodule diff --git a/synth/prim/rtl/prim_clock_meas.sv b/synth/prim/rtl/prim_clock_meas.sv new file mode 100755 index 0000000..e2c2b7f --- /dev/null +++ b/synth/prim/rtl/prim_clock_meas.sv @@ -0,0 +1,279 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// prim_clk_meas is a generic module that measures two clocks against each other. +// One clock is the reference, and another is the input. +// If the input clocks becomes too fast or too slow, an error condition is created. + +// The default parameters assume the reference clock is significantly slower than +// the input clock + + +`include "prim_assert.sv" + +module prim_clock_meas #( + // Maximum value of input clock counts over measurement period + parameter int Cnt = 16, + // Maximum value of reference clock counts over measurement period + parameter int RefCnt = 1, + parameter bit ClkTimeOutChkEn = 1, + parameter bit RefTimeOutChkEn = 1, + localparam int CntWidth = prim_util_pkg::vbits(Cnt), + localparam int RefCntWidth = prim_util_pkg::vbits(RefCnt) +) ( + input clk_i, + input rst_ni, + input clk_ref_i, + input rst_ref_ni, + input en_i, + input [CntWidth-1:0] max_cnt, + input [CntWidth-1:0] min_cnt, + // the output valid and fast/slow indications are all on the + // input clock domain + output logic valid_o, + output logic fast_o, + output logic slow_o, + + // signal on clk_i domain that indicates clk_ref has timed out + output logic timeout_clk_ref_o, + + // signal on clk_ref_i domain that indicates clk has timed out + output logic ref_timeout_clk_o +); + + + ////////////////////////// + // Reference clock logic + ////////////////////////// + + logic ref_en; + prim_flop_2sync #( + .Width(1) + ) u_ref_meas_en_sync ( + .d_i(en_i), + .clk_i(clk_ref_i), + .rst_ni(rst_ref_ni), + .q_o(ref_en) + ); + + ////////////////////////// + // Domain sequencing + ////////////////////////// + + // the enable comes in on the clk_i domain, however, to ensure the + // clk and clk_ref domains remain in sync, the following sequencing is + // done to enable/disable the measurement feature + + typedef enum logic [1:0] { + StDisable, + StEnabling, + StEnable, + StDisabling + } meas_chk_fsm_e; + + // sync reference clock view of enable back into the source domain + logic en_ref_sync; + prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i, + .rst_ni, + .d_i(ref_en), + .q_o(en_ref_sync) + ); + + meas_chk_fsm_e state_d, state_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= StDisable; + end else begin + state_q <= state_d; + end + end + + // The following fsm sequence ensures that even if the source + // side changes the enable too quickly, the measurement controls + // remain consistent. + logic cnt_en; + always_comb begin + state_d = state_q; + cnt_en = '0; + + unique case (state_q) + + StDisable: begin + if (en_i) begin + state_d = StEnabling; + end + end + + StEnabling: begin + if (en_ref_sync) begin + state_d = StEnable; + end + end + + StEnable: begin + cnt_en = 1'b1; + if (!en_i) begin + state_d = StDisabling; + end + end + + StDisabling: begin + if (!en_ref_sync) begin + state_d = StDisable; + end + end + + //VCS coverage off + // pragma coverage off + default:; + //VCS coverage on + // pragma coverage on + + endcase // unique case (state_q) + end + + ////////////////////////// + // Input Clock Logic + ////////////////////////// + + logic valid_ref; + logic valid; + // The valid pulse causes the count to reset and start counting again + // for each reference cycle. + // The count obtained during the last reference cycle is used + // to measure how fast/slow the input clock is. + prim_pulse_sync u_sync_ref ( + .clk_src_i(clk_ref_i), + .rst_src_ni(rst_ref_ni), + .src_pulse_i(ref_en), + .clk_dst_i(clk_i), + .rst_dst_ni(rst_ni), + .dst_pulse_o(valid_ref) + ); + + + if (RefCnt == 1) begin : gen_degenerate_case + // if reference count is one, cnt_ref is always 0. + // So there is no need to maintain a counter, and + // valid just becomes valid_ref + assign valid = valid_ref; + end else begin : gen_normal_case + logic [RefCntWidth-1:0] cnt_ref; + assign valid = valid_ref & (int'(cnt_ref) == RefCnt - 1); + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cnt_ref <= '0; + end else if (!cnt_en && |cnt_ref) begin + cnt_ref <= '0; + end else if (cnt_en && valid) begin + cnt_ref <= '0; + end else if (cnt_en && valid_ref) begin + cnt_ref <= cnt_ref + 1'b1; + end + end + end + + logic cnt_ovfl; + logic [CntWidth-1:0] cnt; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cnt <= '0; + cnt_ovfl <= '0; + end else if (!cnt_en && |cnt) begin + cnt <= '0; + cnt_ovfl <= '0; + end else if (valid_o) begin + cnt <= '0; + cnt_ovfl <= '0; + end else if (cnt_ovfl) begin + cnt <= '{default: '1}; + end else if (cnt_en) begin + {cnt_ovfl, cnt} <= cnt + 1'b1; + end + end + + assign valid_o = valid & |cnt; + assign fast_o = valid_o & ((cnt > max_cnt) | cnt_ovfl); + assign slow_o = valid_o & (cnt < min_cnt); + + ////////////////////////// + // Timeout Handling + ////////////////////////// + + localparam bit TimeOutChkEn = ClkTimeOutChkEn | RefTimeOutChkEn; + + // determine ratio between + localparam int ClkRatio = Cnt / RefCnt; + + // maximum cdc latency from the perspective of the measured clock + // 1 cycle to output request + // 2 ref cycles for synchronization + // 1 ref cycle to send ack + // 2 cycles to sync ack + // Double for margin + localparam int MaxClkCdcLatency = (1 + 2*ClkRatio + 1*ClkRatio + 2)*2; + + // maximum cdc latency from the perspective of the reference clock + // 1 ref cycle to output request + // 2 cycles to sync + 1 cycle to ack are less than 1 cycle of ref based on assertion requirement + // 2 ref cycles to sync ack + // 2 extra ref cycles for margin + localparam int MaxRefCdcLatency = 1 + 1 + 2 + 2; + + if (RefTimeOutChkEn) begin : gen_ref_timeout_chk + // check whether reference clock has timed out + prim_clock_timeout #( + .TimeOutCnt(MaxClkCdcLatency) + ) u_timeout_clk_to_ref ( + .clk_chk_i(clk_ref_i), + .rst_chk_ni(rst_ref_ni), + .clk_i, + .rst_ni, + .en_i, + .timeout_o(timeout_clk_ref_o) + ); + end else begin : gen_unused_ref_timeout + assign timeout_clk_ref_o = 1'b0; + end + + if (ClkTimeOutChkEn) begin : gen_clk_timeout_chk + // check whether clock has timed out + prim_clock_timeout #( + .TimeOutCnt(MaxRefCdcLatency) + ) u_timeout_ref_to_clk ( + .clk_chk_i(clk_i), + .rst_chk_ni(rst_ni), + .clk_i(clk_ref_i), + .rst_ni(rst_ref_ni), + .en_i(ref_en), + .timeout_o(ref_timeout_clk_o) + ); + end else begin : gen_unused_clk_timeout + assign ref_timeout_clk_o = 1'b0; + end + + + ////////////////////////// + // Assertions + ////////////////////////// + + if (TimeOutChkEn) begin : gen_timeout_assert + // the measured clock must be faster than the reference clock + `ASSERT_INIT(ClkRatios_A, ClkRatio > 2) + end + + // reference count has to be at least 1 + `ASSERT_INIT(RefCntVal_A, RefCnt >= 1) + + // if we've reached the max count, enable must be 0 next. + // Otherwise the width of the counter is too small to accommodate the usecase + `ASSERT(MaxWidth_A, (cnt == Cnt-1) |=> !cnt_en ) + + + + +endmodule // prim_clk_meas diff --git a/synth/prim/rtl/prim_clock_timeout.sv b/synth/prim/rtl/prim_clock_timeout.sv new file mode 100755 index 0000000..4b02ee3 --- /dev/null +++ b/synth/prim/rtl/prim_clock_timeout.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 +// +// prim_clk_timeout is a simple module that assesses whether the input clock +// has stopped ticking as measured by the reference clock. +// +// If both clocks are stopped for whatever reason, this module is effectively dead. + +`include "prim_assert.sv" + +module prim_clock_timeout #( + parameter int TimeOutCnt = 16, + localparam int CntWidth = prim_util_pkg::vbits(TimeOutCnt+1) +) ( + // clock to be checked + input clk_chk_i, + input rst_chk_ni, + + // clock used to measure whether clk_chk has stopped ticking + input clk_i, + input rst_ni, + input en_i, + output logic timeout_o +); + + logic [CntWidth-1:0] cnt; + logic ack; + logic timeout; + assign timeout = int'(cnt) >= TimeOutCnt; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cnt <= '0; + end else if (ack || !en_i) begin + cnt <= '0; + end else if (timeout) begin + cnt <= '{default: '1}; + end else if (en_i) begin + cnt <= cnt + 1'b1; + end + end + + logic chk_req; + prim_sync_reqack u_ref_timeout ( + .clk_src_i(clk_i), + .rst_src_ni(rst_ni), + .clk_dst_i(clk_chk_i), + .rst_dst_ni(rst_chk_ni), + .req_chk_i('0), + .src_req_i(1'b1), + .src_ack_o(ack), + .dst_req_o(chk_req), + .dst_ack_i(chk_req) + ); + + prim_flop #( + .ResetValue('0) + ) u_out ( + .clk_i, + .rst_ni, + .d_i(timeout), + .q_o(timeout_o) + ); + +endmodule // prim_clk_timeout diff --git a/synth/prim/rtl/prim_count.sv b/synth/prim/rtl/prim_count.sv new file mode 100755 index 0000000..d5b5aff --- /dev/null +++ b/synth/prim/rtl/prim_count.sv @@ -0,0 +1,267 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Hardened counter primitive: +// +// This internally uses a cross counter scheme with primary and a secondary counter, where the +// secondary counter counts in reverse direction. The sum of both counters must remain constant and +// equal to 2**Width-1 or otherwise an err_o will be asserted. +// +// This counter supports a generic clear / set / increment / decrement interface: +// +// clr_i: This clears the primary counter to ResetValue and adjusts the secondary counter to match +// (i.e. 2**Width-1-ResetValue). Clear has priority over set, increment and decrement. +// set_i: This sets the primary counter to set_cnt_i and adjusts the secondary counter to match +// (i.e. 2**Width-1-set_cnt_i). Set has priority over increment and decrement. +// incr_en_i: Increments the primary counter by step_i, and decrements the secondary by step_i. +// decr_en_i: Decrements the primary counter by step_i, and increments the secondary by step_i. +// commit_i: Counter changes only take effect when `commit_i` is set. This does not effect the +// `cnt_after_commit_o` output which gives the next counter state if the change is +// committed. +// +// Note that if both incr_en_i and decr_en_i are asserted at the same time, the counter remains +// unchanged. The counter is also protected against under- and overflows. + +`include "prim_assert.sv" + +module prim_count #( + parameter int Width = 2, + // Can be used to reset the counter to a different value than 0, for example when + // the counter is used as a down-counter. + parameter logic [Width-1:0] ResetValue = '0, + // This should only be disabled in special circumstances, for example + // in non-comportable IPs where an error does not trigger an alert. + parameter bit EnableAlertTriggerSVA = 1 +) ( + input clk_i, + input rst_ni, + input clr_i, + input set_i, + input [Width-1:0] set_cnt_i, // Set value for the counter. + input incr_en_i, + input decr_en_i, + input [Width-1:0] step_i, // Increment/decrement step when enabled. + input commit_i, + output logic [Width-1:0] cnt_o, // Current counter state + output logic [Width-1:0] cnt_after_commit_o, // Next counter state if committed + output logic err_o +); + + /////////////////// + // Counter logic // + /////////////////// + + // Reset Values for primary and secondary counters. + localparam int NumCnt = 2; + localparam logic [NumCnt-1:0][Width-1:0] ResetValues = {{Width{1'b1}} - ResetValue, // secondary + ResetValue}; // primary + + logic [NumCnt-1:0][Width-1:0] cnt_d, cnt_d_committed, cnt_q, fpv_force; + +`ifndef FPV_SEC_CM_ON + // This becomes a free variable in FPV. + assign fpv_force = '0; +`endif + + for (genvar k = 0; k < NumCnt; k++) begin : gen_cnts + // Note that increments / decrements are reversed for the secondary counter. + logic incr_en, decr_en; + logic [Width-1:0] set_val; + if (k == 0) begin : gen_up_cnt + assign incr_en = incr_en_i; + assign decr_en = decr_en_i; + assign set_val = set_cnt_i; + end else begin : gen_dn_cnt + assign incr_en = decr_en_i; + assign decr_en = incr_en_i; + // The secondary value needs to be adjusted accordingly. + assign set_val = {Width{1'b1}} - set_cnt_i; + end + + // Main counter logic + logic [Width:0] ext_cnt; + assign ext_cnt = (decr_en) ? {1'b0, cnt_q[k]} - {1'b0, step_i} : + (incr_en) ? {1'b0, cnt_q[k]} + {1'b0, step_i} : {1'b0, cnt_q[k]}; + + // Saturation logic + logic uflow, oflow; + assign oflow = incr_en && ext_cnt[Width]; + assign uflow = decr_en && ext_cnt[Width]; + logic [Width-1:0] cnt_sat; + assign cnt_sat = (uflow) ? '0 : + (oflow) ? {Width{1'b1}} : ext_cnt[Width-1:0]; + + // Clock gate flops when in saturation, and do not + // count if both incr_en and decr_en are asserted. + logic cnt_en; + assign cnt_en = (incr_en ^ decr_en) && + ((incr_en && !(&cnt_q[k])) || + (decr_en && !(cnt_q[k] == '0))); + + // Counter muxes + assign cnt_d[k] = (clr_i) ? ResetValues[k] : + (set_i) ? set_val : + (cnt_en) ? cnt_sat : cnt_q[k]; + + assign cnt_d_committed[k] = commit_i ? cnt_d[k] : cnt_q[k]; + + logic [Width-1:0] cnt_unforced_q; + prim_flop #( + .Width(Width), + .ResetValue(ResetValues[k]) + ) u_cnt_flop ( + .clk_i, + .rst_ni, + .d_i(cnt_d_committed[k]), + .q_o(cnt_unforced_q) + ); + + // fpv_force is only used during FPV. + assign cnt_q[k] = fpv_force[k] + cnt_unforced_q; + end + + // The sum of both counters must always equal the counter maximum. + logic [Width:0] sum; + assign sum = (cnt_q[0] + cnt_q[1]); + assign err_o = (sum != {1'b0, {Width{1'b1}}}); + + // Output count values + assign cnt_o = cnt_q[0]; + assign cnt_after_commit_o = cnt_d[0]; + + //////////////// + // Assertions // + //////////////// +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + // We need to disable most assertions in that case using a helper signal. + // We can't rely on err_o since some error patterns cannot be detected (e.g. all error + // patterns that still fullfill the sum constraint). + logic fpv_err_present; + assign fpv_err_present = |fpv_force; + + // Helper functions for assertions. + function automatic logic signed [Width+1:0] max(logic signed [Width+1:0] a, + logic signed [Width+1:0] b); + return (a > b) ? a : b; + endfunction + + function automatic logic signed [Width+1:0] min(logic signed [Width+1:0] a, + logic signed [Width+1:0] b); + return (a < b) ? a : b; + endfunction + //VCS coverage on + // pragma coverage on + + // Cnt next + `ASSERT(CntNext_A, + rst_ni + |=> + $past(!commit_i) || (cnt_o == $past(cnt_after_commit_o)), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Clear + `ASSERT(ClrFwd_A, + rst_ni && clr_i + |=> + (cnt_o == ResetValue) && + (cnt_q[1] == ({Width{1'b1}} - ResetValue)), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(ClrBkwd_A, + rst_ni && !(incr_en_i || decr_en_i || set_i) ##1 + $changed(cnt_o) && $changed(cnt_q[1]) + |-> + $past(clr_i), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Set + `ASSERT(SetFwd_A, + rst_ni && set_i && !clr_i + |=> + (cnt_o == $past(set_cnt_i)) && + (cnt_q[1] == ({Width{1'b1}} - $past(set_cnt_i))), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(SetBkwd_A, + rst_ni && !(incr_en_i || decr_en_i || clr_i) ##1 + $changed(cnt_o) && $changed(cnt_q[1]) + |-> + $past(set_i), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Do not count if both increment and decrement are asserted. + `ASSERT(IncrDecrUpDnCnt_A, + rst_ni && incr_en_i && decr_en_i && !(clr_i || set_i) + |=> + $stable(cnt_o) && $stable(cnt_q[1]), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Up counter + `ASSERT(IncrUpCnt_A, + rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) && commit_i + |=> + cnt_o == min($past(cnt_o) + $past({2'b0, step_i}), {2'b0, {Width{1'b1}}}), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(IncrDnCnt_A, + rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) && commit_i + |=> + cnt_q[1] == max($past(signed'({2'b0, cnt_q[1]})) - $past({2'b0, step_i}), '0), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(UpCntIncrStable_A, + incr_en_i && !(clr_i || set_i || decr_en_i) && + cnt_o == {Width{1'b1}} + |=> + $stable(cnt_o), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(UpCntDecrStable_A, + decr_en_i && !(clr_i || set_i || incr_en_i) && + cnt_o == '0 + |=> + $stable(cnt_o), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Down counter + `ASSERT(DecrUpCnt_A, + rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) && commit_i + |=> + cnt_o == max($past(signed'({2'b0, cnt_o})) - $past({2'b0, step_i}), '0), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(DecrDnCnt_A, + rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) && commit_i + |=> + cnt_q[1] == min($past(cnt_q[1]) + $past({2'b0, step_i}), {2'b0, {Width{1'b1}}}), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(DnCntIncrStable_A, + rst_ni && incr_en_i && !(clr_i || set_i || decr_en_i) && + cnt_q[1] == '0 + |=> + $stable(cnt_q[1]), + clk_i, err_o || fpv_err_present || !rst_ni) + `ASSERT(DnCntDecrStable_A, + rst_ni && decr_en_i && !(clr_i || set_i || incr_en_i) && + cnt_q[1] == {Width{1'b1}} + |=> + $stable(cnt_q[1]), + clk_i, err_o || fpv_err_present || !rst_ni) + + // Error + `ASSERT(CntErrForward_A, + (cnt_q[1] + cnt_q[0]) != {Width{1'b1}} + |-> + err_o) + `ASSERT(CntErrBackward_A, + err_o + |-> + (cnt_q[1] + cnt_q[0]) != {Width{1'b1}}) + + // This logic that will be assign to one, when user adds macro + // ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT to check the error with alert, in case that prim_count + // is used in design without adding this assertion check. + logic unused_assert_connected; + + `ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA) +`endif + +endmodule // prim_count diff --git a/synth/prim/rtl/prim_crc32.sv b/synth/prim/rtl/prim_crc32.sv new file mode 100755 index 0000000..762fd48 --- /dev/null +++ b/synth/prim/rtl/prim_crc32.sv @@ -0,0 +1,324 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// CRC32 calculator +// +// This module takes in n-bits data words (n defined by BytePerWord parameter) and updates an +// internally stored CRC with each valid data word. The polynomial used is the standard CRC32 IEEE +// one. An interface is provided to set the internal CRC to an arbitrary value. The output CRC is an +// inverted version of the internally stored CRC and the input CRC is inverted before being stored. +// This is done so results match existing widely used software libraries (e.g. the crc32 +// functionality available in Python). Note that a initial CRC of 0x0 (corresponding to an internal +// CRC of 0xffffffff) must be used to match results generated elsewhere. + + +module prim_crc32 #( + parameter int unsigned BytesPerWord = 4 +) ( + input logic clk_i, + input logic rst_ni, + + input logic set_crc_i, + input logic [31:0] crc_in_i, + + input logic data_valid_i, + input logic [BytesPerWord*8-1:0] data_i, + + output logic [31:0] crc_out_o +); + // Generated using hw/ip/prim/util/prim_crc32_table_gen.py + function automatic logic [31:0] crc32_byte_calc(logic [7:0] b); + unique case (b) + 8'hff: crc32_byte_calc = 32'h2d02ef8d; + 8'hfe: crc32_byte_calc = 32'h5a05df1b; + 8'hfd: crc32_byte_calc = 32'hc30c8ea1; + 8'hfc: crc32_byte_calc = 32'hb40bbe37; + 8'hfb: crc32_byte_calc = 32'h2a6f2b94; + 8'hfa: crc32_byte_calc = 32'h5d681b02; + 8'hf9: crc32_byte_calc = 32'hc4614ab8; + 8'hf8: crc32_byte_calc = 32'hb3667a2e; + 8'hf7: crc32_byte_calc = 32'h23d967bf; + 8'hf6: crc32_byte_calc = 32'h54de5729; + 8'hf5: crc32_byte_calc = 32'hcdd70693; + 8'hf4: crc32_byte_calc = 32'hbad03605; + 8'hf3: crc32_byte_calc = 32'h24b4a3a6; + 8'hf2: crc32_byte_calc = 32'h53b39330; + 8'hf1: crc32_byte_calc = 32'hcabac28a; + 8'hf0: crc32_byte_calc = 32'hbdbdf21c; + 8'hef: crc32_byte_calc = 32'h30b5ffe9; + 8'hee: crc32_byte_calc = 32'h47b2cf7f; + 8'hed: crc32_byte_calc = 32'hdebb9ec5; + 8'hec: crc32_byte_calc = 32'ha9bcae53; + 8'heb: crc32_byte_calc = 32'h37d83bf0; + 8'hea: crc32_byte_calc = 32'h40df0b66; + 8'he9: crc32_byte_calc = 32'hd9d65adc; + 8'he8: crc32_byte_calc = 32'haed16a4a; + 8'he7: crc32_byte_calc = 32'h3e6e77db; + 8'he6: crc32_byte_calc = 32'h4969474d; + 8'he5: crc32_byte_calc = 32'hd06016f7; + 8'he4: crc32_byte_calc = 32'ha7672661; + 8'he3: crc32_byte_calc = 32'h3903b3c2; + 8'he2: crc32_byte_calc = 32'h4e048354; + 8'he1: crc32_byte_calc = 32'hd70dd2ee; + 8'he0: crc32_byte_calc = 32'ha00ae278; + 8'hdf: crc32_byte_calc = 32'h166ccf45; + 8'hde: crc32_byte_calc = 32'h616bffd3; + 8'hdd: crc32_byte_calc = 32'hf862ae69; + 8'hdc: crc32_byte_calc = 32'h8f659eff; + 8'hdb: crc32_byte_calc = 32'h11010b5c; + 8'hda: crc32_byte_calc = 32'h66063bca; + 8'hd9: crc32_byte_calc = 32'hff0f6a70; + 8'hd8: crc32_byte_calc = 32'h88085ae6; + 8'hd7: crc32_byte_calc = 32'h18b74777; + 8'hd6: crc32_byte_calc = 32'h6fb077e1; + 8'hd5: crc32_byte_calc = 32'hf6b9265b; + 8'hd4: crc32_byte_calc = 32'h81be16cd; + 8'hd3: crc32_byte_calc = 32'h1fda836e; + 8'hd2: crc32_byte_calc = 32'h68ddb3f8; + 8'hd1: crc32_byte_calc = 32'hf1d4e242; + 8'hd0: crc32_byte_calc = 32'h86d3d2d4; + 8'hcf: crc32_byte_calc = 32'h0bdbdf21; + 8'hce: crc32_byte_calc = 32'h7cdcefb7; + 8'hcd: crc32_byte_calc = 32'he5d5be0d; + 8'hcc: crc32_byte_calc = 32'h92d28e9b; + 8'hcb: crc32_byte_calc = 32'h0cb61b38; + 8'hca: crc32_byte_calc = 32'h7bb12bae; + 8'hc9: crc32_byte_calc = 32'he2b87a14; + 8'hc8: crc32_byte_calc = 32'h95bf4a82; + 8'hc7: crc32_byte_calc = 32'h05005713; + 8'hc6: crc32_byte_calc = 32'h72076785; + 8'hc5: crc32_byte_calc = 32'heb0e363f; + 8'hc4: crc32_byte_calc = 32'h9c0906a9; + 8'hc3: crc32_byte_calc = 32'h026d930a; + 8'hc2: crc32_byte_calc = 32'h756aa39c; + 8'hc1: crc32_byte_calc = 32'hec63f226; + 8'hc0: crc32_byte_calc = 32'h9b64c2b0; + 8'hbf: crc32_byte_calc = 32'h5bdeae1d; + 8'hbe: crc32_byte_calc = 32'h2cd99e8b; + 8'hbd: crc32_byte_calc = 32'hb5d0cf31; + 8'hbc: crc32_byte_calc = 32'hc2d7ffa7; + 8'hbb: crc32_byte_calc = 32'h5cb36a04; + 8'hba: crc32_byte_calc = 32'h2bb45a92; + 8'hb9: crc32_byte_calc = 32'hb2bd0b28; + 8'hb8: crc32_byte_calc = 32'hc5ba3bbe; + 8'hb7: crc32_byte_calc = 32'h5505262f; + 8'hb6: crc32_byte_calc = 32'h220216b9; + 8'hb5: crc32_byte_calc = 32'hbb0b4703; + 8'hb4: crc32_byte_calc = 32'hcc0c7795; + 8'hb3: crc32_byte_calc = 32'h5268e236; + 8'hb2: crc32_byte_calc = 32'h256fd2a0; + 8'hb1: crc32_byte_calc = 32'hbc66831a; + 8'hb0: crc32_byte_calc = 32'hcb61b38c; + 8'haf: crc32_byte_calc = 32'h4669be79; + 8'hae: crc32_byte_calc = 32'h316e8eef; + 8'had: crc32_byte_calc = 32'ha867df55; + 8'hac: crc32_byte_calc = 32'hdf60efc3; + 8'hab: crc32_byte_calc = 32'h41047a60; + 8'haa: crc32_byte_calc = 32'h36034af6; + 8'ha9: crc32_byte_calc = 32'haf0a1b4c; + 8'ha8: crc32_byte_calc = 32'hd80d2bda; + 8'ha7: crc32_byte_calc = 32'h48b2364b; + 8'ha6: crc32_byte_calc = 32'h3fb506dd; + 8'ha5: crc32_byte_calc = 32'ha6bc5767; + 8'ha4: crc32_byte_calc = 32'hd1bb67f1; + 8'ha3: crc32_byte_calc = 32'h4fdff252; + 8'ha2: crc32_byte_calc = 32'h38d8c2c4; + 8'ha1: crc32_byte_calc = 32'ha1d1937e; + 8'ha0: crc32_byte_calc = 32'hd6d6a3e8; + 8'h9f: crc32_byte_calc = 32'h60b08ed5; + 8'h9e: crc32_byte_calc = 32'h17b7be43; + 8'h9d: crc32_byte_calc = 32'h8ebeeff9; + 8'h9c: crc32_byte_calc = 32'hf9b9df6f; + 8'h9b: crc32_byte_calc = 32'h67dd4acc; + 8'h9a: crc32_byte_calc = 32'h10da7a5a; + 8'h99: crc32_byte_calc = 32'h89d32be0; + 8'h98: crc32_byte_calc = 32'hfed41b76; + 8'h97: crc32_byte_calc = 32'h6e6b06e7; + 8'h96: crc32_byte_calc = 32'h196c3671; + 8'h95: crc32_byte_calc = 32'h806567cb; + 8'h94: crc32_byte_calc = 32'hf762575d; + 8'h93: crc32_byte_calc = 32'h6906c2fe; + 8'h92: crc32_byte_calc = 32'h1e01f268; + 8'h91: crc32_byte_calc = 32'h8708a3d2; + 8'h90: crc32_byte_calc = 32'hf00f9344; + 8'h8f: crc32_byte_calc = 32'h7d079eb1; + 8'h8e: crc32_byte_calc = 32'h0a00ae27; + 8'h8d: crc32_byte_calc = 32'h9309ff9d; + 8'h8c: crc32_byte_calc = 32'he40ecf0b; + 8'h8b: crc32_byte_calc = 32'h7a6a5aa8; + 8'h8a: crc32_byte_calc = 32'h0d6d6a3e; + 8'h89: crc32_byte_calc = 32'h94643b84; + 8'h88: crc32_byte_calc = 32'he3630b12; + 8'h87: crc32_byte_calc = 32'h73dc1683; + 8'h86: crc32_byte_calc = 32'h04db2615; + 8'h85: crc32_byte_calc = 32'h9dd277af; + 8'h84: crc32_byte_calc = 32'head54739; + 8'h83: crc32_byte_calc = 32'h74b1d29a; + 8'h82: crc32_byte_calc = 32'h03b6e20c; + 8'h81: crc32_byte_calc = 32'h9abfb3b6; + 8'h80: crc32_byte_calc = 32'hedb88320; + 8'h7f: crc32_byte_calc = 32'hc0ba6cad; + 8'h7e: crc32_byte_calc = 32'hb7bd5c3b; + 8'h7d: crc32_byte_calc = 32'h2eb40d81; + 8'h7c: crc32_byte_calc = 32'h59b33d17; + 8'h7b: crc32_byte_calc = 32'hc7d7a8b4; + 8'h7a: crc32_byte_calc = 32'hb0d09822; + 8'h79: crc32_byte_calc = 32'h29d9c998; + 8'h78: crc32_byte_calc = 32'h5edef90e; + 8'h77: crc32_byte_calc = 32'hce61e49f; + 8'h76: crc32_byte_calc = 32'hb966d409; + 8'h75: crc32_byte_calc = 32'h206f85b3; + 8'h74: crc32_byte_calc = 32'h5768b525; + 8'h73: crc32_byte_calc = 32'hc90c2086; + 8'h72: crc32_byte_calc = 32'hbe0b1010; + 8'h71: crc32_byte_calc = 32'h270241aa; + 8'h70: crc32_byte_calc = 32'h5005713c; + 8'h6f: crc32_byte_calc = 32'hdd0d7cc9; + 8'h6e: crc32_byte_calc = 32'haa0a4c5f; + 8'h6d: crc32_byte_calc = 32'h33031de5; + 8'h6c: crc32_byte_calc = 32'h44042d73; + 8'h6b: crc32_byte_calc = 32'hda60b8d0; + 8'h6a: crc32_byte_calc = 32'had678846; + 8'h69: crc32_byte_calc = 32'h346ed9fc; + 8'h68: crc32_byte_calc = 32'h4369e96a; + 8'h67: crc32_byte_calc = 32'hd3d6f4fb; + 8'h66: crc32_byte_calc = 32'ha4d1c46d; + 8'h65: crc32_byte_calc = 32'h3dd895d7; + 8'h64: crc32_byte_calc = 32'h4adfa541; + 8'h63: crc32_byte_calc = 32'hd4bb30e2; + 8'h62: crc32_byte_calc = 32'ha3bc0074; + 8'h61: crc32_byte_calc = 32'h3ab551ce; + 8'h60: crc32_byte_calc = 32'h4db26158; + 8'h5f: crc32_byte_calc = 32'hfbd44c65; + 8'h5e: crc32_byte_calc = 32'h8cd37cf3; + 8'h5d: crc32_byte_calc = 32'h15da2d49; + 8'h5c: crc32_byte_calc = 32'h62dd1ddf; + 8'h5b: crc32_byte_calc = 32'hfcb9887c; + 8'h5a: crc32_byte_calc = 32'h8bbeb8ea; + 8'h59: crc32_byte_calc = 32'h12b7e950; + 8'h58: crc32_byte_calc = 32'h65b0d9c6; + 8'h57: crc32_byte_calc = 32'hf50fc457; + 8'h56: crc32_byte_calc = 32'h8208f4c1; + 8'h55: crc32_byte_calc = 32'h1b01a57b; + 8'h54: crc32_byte_calc = 32'h6c0695ed; + 8'h53: crc32_byte_calc = 32'hf262004e; + 8'h52: crc32_byte_calc = 32'h856530d8; + 8'h51: crc32_byte_calc = 32'h1c6c6162; + 8'h50: crc32_byte_calc = 32'h6b6b51f4; + 8'h4f: crc32_byte_calc = 32'he6635c01; + 8'h4e: crc32_byte_calc = 32'h91646c97; + 8'h4d: crc32_byte_calc = 32'h086d3d2d; + 8'h4c: crc32_byte_calc = 32'h7f6a0dbb; + 8'h4b: crc32_byte_calc = 32'he10e9818; + 8'h4a: crc32_byte_calc = 32'h9609a88e; + 8'h49: crc32_byte_calc = 32'h0f00f934; + 8'h48: crc32_byte_calc = 32'h7807c9a2; + 8'h47: crc32_byte_calc = 32'he8b8d433; + 8'h46: crc32_byte_calc = 32'h9fbfe4a5; + 8'h45: crc32_byte_calc = 32'h06b6b51f; + 8'h44: crc32_byte_calc = 32'h71b18589; + 8'h43: crc32_byte_calc = 32'hefd5102a; + 8'h42: crc32_byte_calc = 32'h98d220bc; + 8'h41: crc32_byte_calc = 32'h01db7106; + 8'h40: crc32_byte_calc = 32'h76dc4190; + 8'h3f: crc32_byte_calc = 32'hb6662d3d; + 8'h3e: crc32_byte_calc = 32'hc1611dab; + 8'h3d: crc32_byte_calc = 32'h58684c11; + 8'h3c: crc32_byte_calc = 32'h2f6f7c87; + 8'h3b: crc32_byte_calc = 32'hb10be924; + 8'h3a: crc32_byte_calc = 32'hc60cd9b2; + 8'h39: crc32_byte_calc = 32'h5f058808; + 8'h38: crc32_byte_calc = 32'h2802b89e; + 8'h37: crc32_byte_calc = 32'hb8bda50f; + 8'h36: crc32_byte_calc = 32'hcfba9599; + 8'h35: crc32_byte_calc = 32'h56b3c423; + 8'h34: crc32_byte_calc = 32'h21b4f4b5; + 8'h33: crc32_byte_calc = 32'hbfd06116; + 8'h32: crc32_byte_calc = 32'hc8d75180; + 8'h31: crc32_byte_calc = 32'h51de003a; + 8'h30: crc32_byte_calc = 32'h26d930ac; + 8'h2f: crc32_byte_calc = 32'habd13d59; + 8'h2e: crc32_byte_calc = 32'hdcd60dcf; + 8'h2d: crc32_byte_calc = 32'h45df5c75; + 8'h2c: crc32_byte_calc = 32'h32d86ce3; + 8'h2b: crc32_byte_calc = 32'hacbcf940; + 8'h2a: crc32_byte_calc = 32'hdbbbc9d6; + 8'h29: crc32_byte_calc = 32'h42b2986c; + 8'h28: crc32_byte_calc = 32'h35b5a8fa; + 8'h27: crc32_byte_calc = 32'ha50ab56b; + 8'h26: crc32_byte_calc = 32'hd20d85fd; + 8'h25: crc32_byte_calc = 32'h4b04d447; + 8'h24: crc32_byte_calc = 32'h3c03e4d1; + 8'h23: crc32_byte_calc = 32'ha2677172; + 8'h22: crc32_byte_calc = 32'hd56041e4; + 8'h21: crc32_byte_calc = 32'h4c69105e; + 8'h20: crc32_byte_calc = 32'h3b6e20c8; + 8'h1f: crc32_byte_calc = 32'h8d080df5; + 8'h1e: crc32_byte_calc = 32'hfa0f3d63; + 8'h1d: crc32_byte_calc = 32'h63066cd9; + 8'h1c: crc32_byte_calc = 32'h14015c4f; + 8'h1b: crc32_byte_calc = 32'h8a65c9ec; + 8'h1a: crc32_byte_calc = 32'hfd62f97a; + 8'h19: crc32_byte_calc = 32'h646ba8c0; + 8'h18: crc32_byte_calc = 32'h136c9856; + 8'h17: crc32_byte_calc = 32'h83d385c7; + 8'h16: crc32_byte_calc = 32'hf4d4b551; + 8'h15: crc32_byte_calc = 32'h6ddde4eb; + 8'h14: crc32_byte_calc = 32'h1adad47d; + 8'h13: crc32_byte_calc = 32'h84be41de; + 8'h12: crc32_byte_calc = 32'hf3b97148; + 8'h11: crc32_byte_calc = 32'h6ab020f2; + 8'h10: crc32_byte_calc = 32'h1db71064; + 8'h0f: crc32_byte_calc = 32'h90bf1d91; + 8'h0e: crc32_byte_calc = 32'he7b82d07; + 8'h0d: crc32_byte_calc = 32'h7eb17cbd; + 8'h0c: crc32_byte_calc = 32'h09b64c2b; + 8'h0b: crc32_byte_calc = 32'h97d2d988; + 8'h0a: crc32_byte_calc = 32'he0d5e91e; + 8'h09: crc32_byte_calc = 32'h79dcb8a4; + 8'h08: crc32_byte_calc = 32'h0edb8832; + 8'h07: crc32_byte_calc = 32'h9e6495a3; + 8'h06: crc32_byte_calc = 32'he963a535; + 8'h05: crc32_byte_calc = 32'h706af48f; + 8'h04: crc32_byte_calc = 32'h076dc419; + 8'h03: crc32_byte_calc = 32'h990951ba; + 8'h02: crc32_byte_calc = 32'hee0e612c; + 8'h01: crc32_byte_calc = 32'h77073096; + 8'h00: crc32_byte_calc = 32'h00000000; + default: crc32_byte_calc = '0; + endcase + endfunction + + logic [31:0] crc_d, crc_q; + logic crc_en; + logic [31:0] crc_stages[BytesPerWord + 1]; + + assign crc_en = set_crc_i | data_valid_i; + + assign crc_stages[0] = crc_q; + + for (genvar i = 0;i < BytesPerWord; ++i) begin : g_crc_stages + assign crc_stages[i + 1] = + {8'h00, crc_stages[i][31:8]} ^ + crc32_byte_calc(crc_stages[i][7:0] ^ data_i[i * 8 +: 8]); + end + + always_comb begin + if (set_crc_i) begin + crc_d = ~crc_in_i; + end else begin + crc_d = crc_stages[BytesPerWord]; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + crc_q <= 32'hFFFFFFFF; + end else if (crc_en) begin + crc_q <= crc_d; + end + end + + assign crc_out_o = ~crc_q; +endmodule diff --git a/synth/prim/rtl/prim_diff_decode.sv b/synth/prim/rtl/prim_diff_decode.sv new file mode 100755 index 0000000..caf3219 --- /dev/null +++ b/synth/prim/rtl/prim_diff_decode.sv @@ -0,0 +1,285 @@ +// 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_i(diff_pi), + .q_o(diff_pd) + ); + + prim_flop_2sync #( + .Width(1), + .ResetValue(1'b1) + ) i_sync_n ( + .clk_i, + .rst_ni, + .d_i(diff_ni), + .q_o(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 +`ifdef INC_ASSERT + `ifndef FPV_ALERT_NO_SIGINT_ERR + // correctly detect sigint issue (only one transition cycle of permissible due to skew) + `ASSERT(SigintCheck0_A, gen_async.diff_pd == gen_async.diff_nd [*2] |-> sigint_o) + // the synchronizer adds 2 cycles of latency with respect to input signals. + `ASSERT(SigintCheck1_A, + ##1 (gen_async.diff_pd ^ gen_async.diff_nd) && + $stable(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $rose(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $stable(gen_async.diff_pd) && $fell(gen_async.diff_nd) + |-> rise_o) + `ASSERT(SigintCheck2_A, + ##1 (gen_async.diff_pd ^ gen_async.diff_nd) && + $stable(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $fell(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $stable(gen_async.diff_pd) && $rose(gen_async.diff_nd) + |-> fall_o) + `ASSERT(SigintCheck3_A, + ##1 (gen_async.diff_pd ^ gen_async.diff_nd) && + $stable(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $rose(gen_async.diff_nd) && $stable(gen_async.diff_pd) ##1 + $stable(gen_async.diff_nd) && $fell(gen_async.diff_pd) + |-> fall_o) + `ASSERT(SigintCheck4_A, + ##1 (gen_async.diff_pd ^ gen_async.diff_nd) && + $stable(gen_async.diff_pd) && $stable(gen_async.diff_nd) ##1 + $fell(gen_async.diff_nd) && $stable(gen_async.diff_pd) ##1 + $stable(gen_async.diff_nd) && $rose(gen_async.diff_pd) + |-> rise_o) + `endif + + // correctly detect edges + `ASSERT(RiseCheck_A, + !sigint_o ##1 $rose(gen_async.diff_pd) && (gen_async.diff_pd ^ gen_async.diff_nd) |-> + ##[0:1] rise_o, clk_i, !rst_ni || sigint_o) + `ASSERT(FallCheck_A, + !sigint_o ##1 $fell(gen_async.diff_pd) && (gen_async.diff_pd ^ gen_async.diff_nd) |-> + ##[0:1] fall_o, clk_i, !rst_ni || sigint_o) + `ASSERT(EventCheck_A, + !sigint_o ##1 $changed(gen_async.diff_pd) && (gen_async.diff_pd ^ gen_async.diff_nd) |-> + ##[0:1] event_o, clk_i, !rst_ni || sigint_o) + // correctly detect level + `ASSERT(LevelCheck0_A, + !sigint_o && (gen_async.diff_pd ^ gen_async.diff_nd) [*2] |-> + gen_async.diff_pd == level_o, + clk_i, !rst_ni || sigint_o) +`endif + end else begin : gen_sync_assert + // assertions for synchronous case + + `ifndef FPV_ALERT_NO_SIGINT_ERR + // correctly detect sigint issue + `ASSERT(SigintCheck_A, diff_pi == diff_ni |-> sigint_o) + `endif + + // 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/synth/prim/rtl/prim_dom_and_2share.sv b/synth/prim/rtl/prim_dom_and_2share.sv new file mode 100755 index 0000000..b45db9a --- /dev/null +++ b/synth/prim/rtl/prim_dom_and_2share.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 +// +// Domain-Oriented Masking GF(2) Multiplier with 2-shares +// ref: Higher-Order Side-Channel Protected Implementations of Keccak +// https://eprint.iacr.org/2017/395.pdf +// +// q0 = a0 & b0 + (a0 & b1 + z) +// q1 = a1 & b1 + (a1 & b0 + z) +// () ==> registered +// +// all input should be stable for two clocks +// as the output is valid after a clock +// For z, it can use other slice from the state +// as it is fairly random w.r.t the current inputs. + +// General formula of Q in the paper +// Qi = t{i,i} + Sig(j>i,d)(t{i,j}+Z{i+j*(j-1)/2}) + Sig(j0,1)(t{0,j}+Z{j(j-1)/2}) + Sig(j<0,d)(..) +// = a0&b0 + (a0&b1 + z0 + 0) +// Q1 = t{1,1} + sig(j>1,1)(...) + sig(j<1,1)(t{1,j} + Z{j}) +// = a1&b1 + (0 + a1&b0 + z0) + +`include "prim_assert.sv" + +module prim_dom_and_2share #( + parameter int DW = 64, // Input width + parameter bit Pipeline = 1'b0 // Enable full pipelining +) ( + input clk_i, + input rst_ni, + + input [DW-1:0] a0_i, // share0 of a + input [DW-1:0] a1_i, // share1 of a + input [DW-1:0] b0_i, // share0 of b + input [DW-1:0] b1_i, // share1 of b + input z_valid_i, // random number input validity + input [DW-1:0] z_i, // random number + + output logic [DW-1:0] q0_o, // share0 of q + output logic [DW-1:0] q1_o, // share1 of q + output logic [DW-1:0] prd_o // pseudo-random data for other instances +); + + logic [DW-1:0] t0_d, t0_q, t1_d, t1_q; + logic [DW-1:0] t_a0b0, t_a1b1; + logic [DW-1:0] t_a0b0_d, t_a1b1_d; + logic [DW-1:0] t_a0b1, t_a1b0; + + ///////////////// + // Calculation // + ///////////////// + // Inner-domain terms + assign t_a0b0_d = a0_i & b0_i; + assign t_a1b1_d = a1_i & b1_i; + + // Cross-domain terms + assign t_a0b1 = a0_i & b1_i; + assign t_a1b0 = a1_i & b0_i; + + /////////////// + // Resharing // + /////////////// + // Resharing of cross-domain terms + + // Preserve the logic sequence for XOR not to proceed cross-domain AND. + prim_xor2 #( + .Width ( DW*2 ) + ) u_prim_xor_t01 ( + .in0_i ( {t_a0b1, t_a1b0} ), + .in1_i ( {z_i, z_i} ), + .out_o ( {t0_d, t1_d} ) + ); + + // Register stage + prim_flop_en #( + .Width ( DW*2 ), + .ResetValue ( '0 ) + ) u_prim_flop_t01 ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .en_i ( z_valid_i ), + .d_i ( {t0_d, t1_d} ), + .q_o ( {t0_q, t1_q} ) + ); + + ///////////////////////// + // Optional Pipelining // + ///////////////////////// + + if (Pipeline == 1'b1) begin : gen_inner_domain_regs + // Add pipeline registers on inner-domain terms prior to integration. This allows accepting new + // input data every clock cycle and prevents SCA leakage occurring due to the integration of + // reshared cross-domain terms with inner-domain terms derived from different input data. + + logic [DW-1:0] t_a0b0_q, t_a1b1_q; + prim_flop_en #( + .Width ( DW*2 ), + .ResetValue ( '0 ) + ) u_prim_flop_tab01 ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .en_i ( z_valid_i ), + .d_i ( {t_a0b0_d, t_a1b1_d} ), + .q_o ( {t_a0b0_q, t_a1b1_q} ) + ); + + assign t_a0b0 = t_a0b0_q; + assign t_a1b1 = t_a1b1_q; + + end else begin : gen_no_inner_domain_regs + // Do not add the optional pipeline registers on the inner-domain terms. This allows to save + // some area in case the multiplier does not need to accept new data in every cycle. However, + // this can cause SCA leakage as during the clock cycle in which new data arrives, the new + // inner-domain terms are integrated with the previous, reshared cross-domain terms. + + assign t_a0b0 = t_a0b0_d; + assign t_a1b1 = t_a1b1_d; + end + + ///////////////// + // Integration // + ///////////////// + + // Preserve the logic sequence for XOR not to proceed the inner-domain AND. + prim_xor2 #( + .Width ( DW*2 ) + ) u_prim_xor_q01 ( + .in0_i ( {t_a0b0, t_a1b1} ), + .in1_i ( {t0_q, t1_q} ), + .out_o ( {q0_o, q1_o} ) + ); + + // Use intermediate results for remasking computations in another instance in the following + // clock cycle. Use one share only. Directly use output of flops updating with z_valid_i. + // t1_q is obtained by remasking t_a1b0 with z_i. Since z_i is uniformly distributed and + // independent of a1/b0_i, t1_q is also uniformly distributed and independent of a1/b0_i. + // For details, see Lemma 1 in Canright, "A very compact 'perfectly masked' S-box for AES + // (corrected)" available at https://eprint.iacr.org/2009/011.pdf + assign prd_o = t1_q; + + // DOM AND should be same as unmasked computation + // The correct test sequence will be: + // 1. inputs are changed + // 2. check if z_valid_i, + // 3. at the next cycle, inputs are still stable (assumption) - only in case Pipeline = 0 + // 4. and results Q == A & B (assertion) + + // To speed up the FPV process, random value is ready in less than or + // equal to two cycles. + `ASSUME_FPV(RandomReadyInShortTime_A, + $changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i) + |-> ##[0:2] z_valid_i, + clk_i, !rst_ni) + + if (Pipeline == 0) begin: g_assert_stable + // If Pipeline is not set, the computation takes two cycles without flop + // crossing the domain. In this case, the signal should be stable for at + // least two cycles. + `ASSUME(StableTwoCycles_M, + ($changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i)) + ##[0:$] z_valid_i |=> + $stable(a0_i) && $stable(a1_i) && $stable(b0_i) && $stable(b1_i)) + end + + `ASSERT(UnmaskedAndMatched_A, + z_valid_i |=> (q0_o ^ q1_o) == + (($past(a0_i) ^ $past(a1_i)) & ($past(b0_i) ^ $past(b1_i))), + clk_i, !rst_ni) + +endmodule diff --git a/synth/prim/rtl/prim_double_lfsr.sv b/synth/prim/rtl/prim_double_lfsr.sv new file mode 100755 index 0000000..8fce7b7 --- /dev/null +++ b/synth/prim/rtl/prim_double_lfsr.sv @@ -0,0 +1,113 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Hardened LFSR module that instantiates two LFSRs of the same type. +// The state vector of both LFSRs is constantly checked and an error is asserted if the +// two states are inconsistent. + +module prim_double_lfsr #( + // prim_lfsr parameters - refer to prim_lfsr for their meaning/ + parameter LfsrType = "GAL_XOR", + parameter int unsigned LfsrDw = 32, + localparam int unsigned LfsrIdxDw = $clog2(LfsrDw), + parameter int unsigned EntropyDw = 8, + parameter int unsigned StateOutDw = 8, + parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1), + parameter logic [LfsrDw-1:0] CustomCoeffs = '0, + parameter bit StatePermEn = 1'b0, + parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0, + parameter bit MaxLenSVA = 1'b1, + parameter bit LockupSVA = 1'b1, + parameter bit ExtSeedSVA = 1'b1, + parameter bit NonLinearOut = 1'b0, + // This should only be disabled in special circumstances, for example + // in non-comportable IPs where an error does not trigger an alert. + parameter bit EnableAlertTriggerSVA = 1 +) ( + input clk_i, + input rst_ni, + input seed_en_i, + input [LfsrDw-1:0] seed_i, + input lfsr_en_i, + input [EntropyDw-1:0] entropy_i, + output logic [StateOutDw-1:0] state_o, + // Asserted if the parallel LFSR states are inconsistent. + output logic err_o +); + + + logic [1:0][LfsrDw-1:0] lfsr_state; + // We employ redundant LFSRs to guard against FI attacks. + for (genvar k = 0; k < 2; k++) begin : gen_double_lfsr + // Instantiate size_only buffers to prevent + // optimization / merging of redundant logic. + logic lfsr_en_buf, seed_en_buf; + logic [EntropyDw-1:0] entropy_buf; + logic [LfsrDw-1:0] seed_buf, lfsr_state_unbuf; + prim_buf #( + .Width(EntropyDw + LfsrDw + 2) + ) u_prim_buf_input ( + .in_i({seed_en_i, seed_i, lfsr_en_i, entropy_i}), + .out_o({seed_en_buf, seed_buf, lfsr_en_buf, entropy_buf}) + ); + + prim_lfsr #( + .LfsrType(LfsrType), + .LfsrDw(LfsrDw), + .EntropyDw(EntropyDw), + // output the full width so that the states can be cross checked. + .StateOutDw(LfsrDw), + .DefaultSeed(DefaultSeed), + .CustomCoeffs(CustomCoeffs), + .StatePermEn(StatePermEn), + .StatePerm(StatePerm), + .MaxLenSVA(MaxLenSVA), + .LockupSVA(LockupSVA), + .ExtSeedSVA(ExtSeedSVA), + .NonLinearOut(NonLinearOut) + ) u_prim_lfsr ( + .clk_i, + .rst_ni, + .seed_en_i ( seed_en_buf ), + .seed_i ( seed_buf ), + .lfsr_en_i ( lfsr_en_buf ), + .entropy_i ( entropy_buf ), + .state_o ( lfsr_state_unbuf ) + ); + + prim_buf #( + .Width(LfsrDw) + ) u_prim_buf_output ( + .in_i(lfsr_state_unbuf), + .out_o(lfsr_state[k]) + ); + end + +`ifdef SIMULATION +`ifndef VERILATOR + // Ensure both LFSRs start off with the same default seed. if randomized in simulations. + initial begin : p_sync_lfsr_default_seed + wait (!$isunknown(gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal)); + wait (!$isunknown(gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal)); + gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal = + gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal; + $display("%m: Updated gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal = 0x%0h", + gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal); + end +`endif +`endif + + // Output the state from the first LFSR + assign state_o = lfsr_state[0][StateOutDw-1:0]; + assign err_o = lfsr_state[0] != lfsr_state[1]; + + // This logic that will be assign to one, when user adds macro + // ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT to check the error with alert, in case that + // prim_double_lfsr is used in design without adding this assertion check. + `ifdef INC_ASSERT + logic unused_assert_connected; + + `ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA) + `endif +endmodule : prim_double_lfsr diff --git a/synth/prim/rtl/prim_edge_detector.sv b/synth/prim/rtl/prim_edge_detector.sv new file mode 100755 index 0000000..2b0a92f --- /dev/null +++ b/synth/prim/rtl/prim_edge_detector.sv @@ -0,0 +1,55 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Edge Detector + +module prim_edge_detector #( + parameter int unsigned Width = 1, + + parameter logic [Width-1:0] ResetValue = '0, + + // EnSync + // + // Enable Synchronizer to the input signal. + // It is assumed that the input signal is glitch free (registered input). + parameter bit EnSync = 1'b 1 +) ( + input clk_i, + input rst_ni, + + input [Width-1:0] d_i, + output logic [Width-1:0] q_sync_o, + + output logic [Width-1:0] q_posedge_pulse_o, + output logic [Width-1:0] q_negedge_pulse_o +); + + logic [Width-1:0] q_sync_d, q_sync_q; + + if (EnSync) begin : g_sync + prim_flop_2sync #( + .Width (Width), + .ResetValue (ResetValue) + ) u_sync ( + .clk_i, + .rst_ni, + .d_i, + .q_o (q_sync_d) + ); + end : g_sync + else begin : g_nosync + assign q_sync_d = d_i; + end : g_nosync + + assign q_sync_o = q_sync_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) q_sync_q <= ResetValue; + else q_sync_q <= q_sync_d; + end + + assign q_posedge_pulse_o = q_sync_d & ~q_sync_q; + assign q_negedge_pulse_o = ~q_sync_d & q_sync_q; + +endmodule : prim_edge_detector diff --git a/synth/prim/rtl/prim_edn_req.sv b/synth/prim/rtl/prim_edn_req.sv new file mode 100755 index 0000000..70efb2e --- /dev/null +++ b/synth/prim/rtl/prim_edn_req.sv @@ -0,0 +1,215 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module can be used as a "gadget" to adapt the native 32bit width of the EDN network +// locally to the width needed by the consuming logic. For example, if the local consumer +// needs 128bit, this module would request four 32 bit words from EDN and stack them accordingly. +// +// The module also uses a req/ack synchronizer to synchronize the EDN data over to the local +// clock domain. Note that this assumes that the EDN data bus remains stable between subsequent +// requests. +// + +`include "prim_assert.sv" + +module prim_edn_req + import prim_alert_pkg::*; +#( + parameter int OutWidth = 32, + // Repetition check for incoming edn data + parameter bit RepCheck = 0, + // Disable reset-related assertion checks inside prim_sync_reqack primitives. + parameter bit EnRstChks = 0, + + // EDN Request latency checker + // + // Each consumer IP may have the maximum expected latency. MaxLatency + // parameter describes the expected latency in terms of the consumer clock + // cycles. If the edn request comes later than that, the assertion will be + // fired. + // + // The default value is 0, which disables the assertion. + parameter int unsigned MaxLatency = 0 +) ( + // Design side + input clk_i, + input rst_ni, + input req_chk_i, // Used for gating assertions. Drive to 1 during normal + // operation. + input req_i, + output logic ack_o, + output logic [OutWidth-1:0] data_o, + output logic fips_o, + output logic err_o, // current data_o failed repetition check + // EDN side + input clk_edn_i, + input rst_edn_ni, + output edn_pkg::edn_req_t edn_o, + input edn_pkg::edn_rsp_t edn_i +); + + // Stop requesting words from EDN once desired amount of data is available. + logic word_req, word_ack; + assign word_req = req_i & ~ack_o; + + logic [edn_pkg::ENDPOINT_BUS_WIDTH-1:0] word_data; + logic word_fips; + localparam int SyncWidth = $bits({edn_i.edn_fips, edn_i.edn_bus}); + prim_sync_reqack_data #( + .Width(SyncWidth), + .EnRstChks(EnRstChks), + .DataSrc2Dst(1'b0), + .DataReg(1'b0) + ) u_prim_sync_reqack_data ( + .clk_src_i ( clk_i ), + .rst_src_ni ( rst_ni ), + .clk_dst_i ( clk_edn_i ), + .rst_dst_ni ( rst_edn_ni ), + .req_chk_i ( req_chk_i ), + .src_req_i ( word_req ), + .src_ack_o ( word_ack ), + .dst_req_o ( edn_o.edn_req ), + .dst_ack_i ( edn_i.edn_ack ), + .data_i ( {edn_i.edn_fips, edn_i.edn_bus} ), + .data_o ( {word_fips, word_data} ) + ); + + if (RepCheck) begin : gen_rep_chk + logic [edn_pkg::ENDPOINT_BUS_WIDTH-1:0] word_data_q; + always_ff @(posedge clk_i) begin + if (word_ack) begin + word_data_q <= word_data; + end + end + + // do not check until we have received at least the first entry + logic chk_rep; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + chk_rep <= '0; + end else if (word_ack) begin + chk_rep <= 1'b1; + end + end + + // Need to track if any of the packed words has failed the repetition check, i.e., is identical + // to the last packed word. + logic err_d, err_q; + assign err_d = (req_i && ack_o) ? 1'b0 : // clear + (chk_rep && word_ack && word_data == word_data_q) ? 1'b1 : // set + err_q; // keep + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_q <= 1'b0; + end else begin + err_q <= err_d; + end + end + assign err_o = err_q; + + end else begin : gen_no_rep_chk // block: gen_rep_chk + assign err_o = '0; + end + + prim_packer_fifo #( + .InW(edn_pkg::ENDPOINT_BUS_WIDTH), + .OutW(OutWidth), + .ClearOnRead(1'b0) + ) u_prim_packer_fifo ( + .clk_i, + .rst_ni, + .clr_i ( 1'b0 ), // not needed + .wvalid_i ( word_ack ), + .wdata_i ( word_data ), + // no need for backpressure since we're always ready to + // sink data at this point. + .wready_o ( ), + .rvalid_o ( ack_o ), + .rdata_o ( data_o ), + // we're always ready to receive the packed output word + // at this point. + .rready_i ( 1'b1 ), + .depth_o ( ) + ); + + // Need to track if any of the packed words has been generated with a pre-FIPS seed, i.e., has + // fips == 1'b0. + logic fips_d, fips_q; + assign fips_d = (req_i && ack_o) ? 1'b1 : // clear + (word_ack) ? fips_q & word_fips : // accumulate + fips_q; // keep + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fips_q <= 1'b1; + end else begin + fips_q <= fips_d; + end + end + assign fips_o = fips_q; + + //////////////// + // Assertions // + //////////////// + + // Check EDN data is valid: Not all zeros, all ones, or not the same as previous data. +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + logic [OutWidth-1:0] data_prev, data_curr; + + always_ff @(posedge ack_o or negedge rst_ni) begin + if (!rst_ni) begin + data_prev <= '0; + data_curr <= '0; + end else if (ack_o) begin + data_curr <= data_o; + data_prev <= data_curr; + end + end + //VCS coverage on + // pragma coverage on + + `ASSERT(DataOutputValid_A, ack_o |-> (data_o != 0) && (data_o != '1)) + `ASSERT(DataOutputDiffFromPrev_A, data_prev != 0 |-> data_prev != data_o) +`endif + + // EDN Max Latency Checker +`ifndef SYNTHESIS + if (MaxLatency != 0) begin: g_maxlatency_assertion + //VCS coverage off + // pragma coverage off + localparam int unsigned LatencyW = $clog2(MaxLatency+1); + logic [LatencyW-1:0] latency_counter; + logic reset_counter; + logic enable_counter; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) latency_counter <= '0; + else if (reset_counter) latency_counter <= '0; + else if (enable_counter) latency_counter <= latency_counter + 1'b1; + end + + assign reset_counter = ack_o; + assign enable_counter = req_i; + //VCS coverage on + // pragma coverage on + + `ASSERT(MaxLatency_A, latency_counter <= MaxLatency) + + // TODO: Is it worth to check req & ack pair? + // _________________________________ + // req __/ \______ + // ____ + // ack ____________________________________/ \_ + // + // | error + + end // g_maxlatency_assertion +`else // SYNTHESIS + logic unused_param_maxlatency; + assign unused_param_maxlatency = ^MaxLatency; +`endif // SYNTHESIS + +endmodule : prim_edn_req diff --git a/synth/prim/rtl/prim_esc_pkg.sv b/synth/prim/rtl/prim_esc_pkg.sv new file mode 100755 index 0000000..8cd9c4e --- /dev/null +++ b/synth/prim/rtl/prim_esc_pkg.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 + +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; + + parameter esc_tx_t ESC_TX_DEFAULT = '{esc_p: 1'b0, + esc_n: 1'b1}; + + parameter esc_rx_t ESC_RX_DEFAULT = '{resp_p: 1'b0, + resp_n: 1'b1}; + +endpackage : prim_esc_pkg diff --git a/synth/prim/rtl/prim_esc_receiver.sv b/synth/prim/rtl/prim_esc_receiver.sv new file mode 100755 index 0000000..269e390 --- /dev/null +++ b/synth/prim/rtl/prim_esc_receiver.sv @@ -0,0 +1,277 @@ +// 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::*; +#( + // The number of escalation severities. Should be set to the Alert Handler's N_ESC_SEV when this + // primitive is instantiated. + parameter int N_ESC_SEV = 4, + + // The width of the Alert Handler's ping counter. Should be set to the Alert Handler's PING_CNT_DW + // when this primitive is instantiated. + parameter int PING_CNT_DW = 16, + + // This counter monitors incoming ping requests and auto-escalates if the alert handler + // ceases to send them regularly. The maximum number of cycles between subsequent ping requests + // is N_ESC_SEV x (2 x 2 x 2**PING_CNT_DW), see also implementation of the ping timer + // (alert_handler_ping_timer.sv). The timeout counter below uses a timeout that is 4x larger than + // that in order to incorporate some margin. + // + // Do NOT modify this counter value, when instantiating it in the design. It is only exposed to + // reduce the state space in the FPV testbench. + localparam int MarginFactor = 4, + localparam int NumWaitCounts = 2, + localparam int NumTimeoutCounts = 2, + parameter int TimeoutCntDw = $clog2(MarginFactor) + + $clog2(N_ESC_SEV) + + $clog2(NumWaitCounts) + + $clog2(NumTimeoutCounts) + + PING_CNT_DW +) ( + input clk_i, + input rst_ni, + // escalation enable + output logic esc_req_o, + // escalation / ping response + output esc_rx_t esc_rx_o, + // escalation output diff pair + input esc_tx_t esc_tx_i +); + + ///////////////////////////////// + // decode differential signals // + ///////////////////////////////// + + logic esc_level, esc_p, esc_n, sigint_detected; + + // This prevents further tool optimizations of the differential signal. + prim_buf #( + .Width(2) + ) u_prim_buf_esc ( + .in_i({esc_tx_i.esc_n, + esc_tx_i.esc_p}), + .out_o({esc_n, + esc_p}) + ); + + prim_diff_decode #( + .AsyncOn(1'b0) + ) u_decode_esc ( + .clk_i, + .rst_ni, + .diff_pi ( esc_p ), + .diff_ni ( esc_n ), + .level_o ( esc_level ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( sigint_detected ) + ); + + //////////////////////////////////////////// + // Ping Monitor Counter / Auto Escalation // + //////////////////////////////////////////// + + // The timeout counter is kicked off when the first ping occurs, and subsequent pings reset + // the counter to 1. The counter keeps on counting when it is nonzero, and saturates when it + // has reached its maximum (this state is terminal). + logic ping_en, timeout_cnt_error; + logic timeout_cnt_set, timeout_cnt_en; + logic [TimeoutCntDw-1:0] timeout_cnt; + assign timeout_cnt_set = (ping_en && !(&timeout_cnt)); + assign timeout_cnt_en = ((timeout_cnt > '0) && !(&timeout_cnt)); + + prim_count #( + .Width(TimeoutCntDw), + // The escalation receiver behaves differently than other comportable IP. I.e., instead of + // sending out an alert signal, this condition is handled internally in the alert handler. + .EnableAlertTriggerSVA(0) + ) u_prim_count ( + .clk_i, + .rst_ni, + .clr_i(1'b0), + .set_i(timeout_cnt_set), + .set_cnt_i(TimeoutCntDw'(1)), + .incr_en_i(timeout_cnt_en), + .decr_en_i(1'b0), + .step_i(TimeoutCntDw'(1)), + .commit_i(1'b1), + .cnt_o(timeout_cnt), + .cnt_after_commit_o(), + .err_o(timeout_cnt_error) + ); + + // Escalation is asserted if + // - requested via the escalation sender/receiver path, + // - the ping monitor timeout is reached, + // - the two ping monitor counters are in an inconsistent state. + logic esc_req; + prim_sec_anchor_buf #( + .Width(1) + ) u_prim_buf_esc_req ( + .in_i(esc_req || (&timeout_cnt) || timeout_cnt_error), + .out_o(esc_req_o) + ); + + ///////////////// + // RX/TX Logic // + ///////////////// + + typedef enum logic [2:0] {Idle, Check, PingResp, EscResp, SigInt} state_e; + state_e state_d, state_q; + logic resp_pd, resp_pq; + logic resp_nd, resp_nq; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_flop #( + .Width(2), + .ResetValue(2'b10) + ) u_prim_flop_esc ( + .clk_i, + .rst_ni, + .d_i({resp_nd, resp_pd}), + .q_o({resp_nq, resp_pq}) + ); + + assign esc_rx_o.resp_p = resp_pq; + assign esc_rx_o.resp_n = resp_nq; + + always_comb begin : p_fsm + // default + state_d = state_q; + resp_pd = 1'b0; + resp_nd = 1'b1; + esc_req = 1'b0; + ping_en = 1'b0; + + unique case (state_q) + // wait for the esc_p/n diff pair + Idle: begin + if (esc_level) begin + state_d = Check; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + end + end + // we decide here whether this is only a ping request or + // whether this is an escalation enable + Check: begin + state_d = PingResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + if (esc_level) begin + state_d = EscResp; + esc_req = 1'b1; + end + end + // finish ping response. in case esc_level is again asserted, + // we got an escalation signal (pings cannot occur back to back) + PingResp: begin + state_d = Idle; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + ping_en = 1'b1; + if (esc_level) begin + state_d = EscResp; + esc_req = 1'b1; + end + end + // we have got an escalation enable pulse, + // keep on toggling the outputs + EscResp: begin + state_d = Idle; + if (esc_level) begin + state_d = EscResp; + resp_pd = ~resp_pq; + resp_nd = resp_pq; + esc_req = 1'b1; + end + end + // we have a signal integrity issue at one of + // the incoming diff pairs. this condition is + // signalled to the sender by setting the resp + // diffpair to the same value and continuously + // toggling them. + SigInt: begin + state_d = Idle; + esc_req = 1'b1; + if (sigint_detected) begin + state_d = SigInt; + resp_pd = ~resp_pq; + resp_nd = ~resp_pq; + end + end + default: state_d = Idle; + endcase + + // bail out if a signal integrity issue has been detected + if (sigint_detected && (state_q != SigInt)) begin + state_d = SigInt; + resp_pd = 1'b0; + resp_nd = 1'b0; + end + end + + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + end else begin + state_q <= state_d; + end + end + + //////////////// + // assertions // + //////////////// + + // check whether all outputs have a good known state after reset + `ASSERT_KNOWN(EscEnKnownO_A, esc_req_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) + `ASSERT(SigIntCheck1_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> state_q == SigInt) + // auto-escalate in case of signal integrity issue + `ASSERT(SigIntCheck2_A, esc_tx_i.esc_p == esc_tx_i.esc_n |=> esc_req_o) + // 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 signal integrity issue + `ASSERT(PingRespCheck_A, state_q == Idle ##1 $rose(esc_tx_i.esc_p) ##1 $fell(esc_tx_i.esc_p) |-> + $rose(esc_rx_o.resp_p) ##1 $fell(esc_rx_o.resp_p), + clk_i, !rst_ni || (esc_tx_i.esc_p == esc_tx_i.esc_n)) + // escalation response needs to continuously toggle + `ASSERT(EscRespCheck_A, ##1 esc_tx_i.esc_p && $past(esc_tx_i.esc_p) && + (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && $past(esc_tx_i.esc_p ^ esc_tx_i.esc_n) + |=> esc_rx_o.resp_p != $past(esc_rx_o.resp_p)) + // detect escalation pulse + `ASSERT(EscEnCheck_A, + esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) && state_q != SigInt + ##1 esc_tx_i.esc_p && (esc_tx_i.esc_p ^ esc_tx_i.esc_n) |-> esc_req_o) + // make sure the counter does not wrap around + `ASSERT(EscCntWrap_A, &timeout_cnt |=> timeout_cnt != 0) + // if the counter expires, escalation should be asserted + `ASSERT(EscCntEsc_A, &timeout_cnt |-> esc_req_o) + +endmodule : prim_esc_receiver diff --git a/synth/prim/rtl/prim_esc_sender.sv b/synth/prim/rtl/prim_esc_sender.sv new file mode 100755 index 0000000..7987e6c --- /dev/null +++ b/synth/prim/rtl/prim_esc_sender.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 +// +// 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_req_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 ping_ok_o is pulsed high. + input ping_req_i, + output logic ping_ok_o, + // asserted if signal integrity issue detected + output logic integ_fail_o, + // escalation request signal + input esc_req_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, resp_n, resp_p, sigint_detected; + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_buf #( + .Width(2) + ) u_prim_buf_resp ( + .in_i({esc_rx_i.resp_n, + esc_rx_i.resp_p}), + .out_o({resp_n, + resp_p}) + ); + + prim_diff_decode #( + .AsyncOn(1'b0) + ) u_decode_resp ( + .clk_i, + .rst_ni, + .diff_pi ( resp_p ), + .diff_ni ( resp_n ), + .level_o ( resp ), + .rise_o ( ), + .fall_o ( ), + .event_o ( ), + .sigint_o ( sigint_detected ) + ); + + ////////////// + // TX Logic // + ////////////// + + logic ping_req_d, ping_req_q; + logic esc_req_d, esc_req_q, esc_req_q1; + + assign ping_req_d = ping_req_i; + assign esc_req_d = esc_req_i; + + // ping enable is 1 cycle pulse + // escalation pulse is always longer than 2 cycles + logic esc_p; + assign esc_p = esc_req_i | esc_req_q | (ping_req_d & ~ping_req_q); + + // This prevents further tool optimizations of the differential signal. + prim_sec_anchor_buf #( + .Width(2) + ) u_prim_buf_esc ( + .in_i({~esc_p, + esc_p}), + .out_o({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_req_i) begin + state_d = CheckEscRespHi; + end else if (ping_req_d & ~ping_req_q) 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_req_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_req_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_req_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_req_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_req_i; + end + end + default : state_d = Idle; + endcase + + // 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 + + // escalation takes precedence, + // immediately return ok in that case + if ((esc_req_i || esc_req_q || esc_req_q1) && ping_req_i) begin + ping_ok_o = 1'b1; + end + end + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + esc_req_q <= 1'b0; + esc_req_q1 <= 1'b0; + ping_req_q <= 1'b0; + end else begin + state_q <= state_d; + esc_req_q <= esc_req_d; + esc_req_q1 <= esc_req_q; + ping_req_q <= ping_req_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_req_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_req_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_req_i |-> esc_tx_o.esc_p [*2] ) + // escalation / ping collision + `ASSERT(EscPingCheck_A, esc_req_i && ping_req_i |-> ping_ok_o) + // check that ping request results in only a single cycle pulse + `ASSERT(PingCheck_A, ##1 $rose(ping_req_i) |-> esc_tx_o.esc_p ##1 !esc_tx_o.esc_p , clk_i, + !rst_ni || esc_req_i || integ_fail_o) + +endmodule : prim_esc_sender diff --git a/synth/prim/rtl/prim_fifo_async.sv b/synth/prim/rtl/prim_fifo_async.sv new file mode 100755 index 0000000..0edebc7 --- /dev/null +++ b/synth/prim/rtl/prim_fifo_async.sv @@ -0,0 +1,298 @@ +// 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 = 4, + parameter bit OutputZeroIfEmpty = 1'b0, // if == 1 always output 0 when FIFO is empty + parameter bit OutputZeroIfInvalid = 1'b0, // if == 1 always output 0 when rvalid_o is low + localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth] +) ( + // write port + input logic clk_wr_i, + input logic rst_wr_ni, + input logic wvalid_i, + output logic wready_o, + input logic [Width-1:0] wdata_i, + output logic [DepthW-1:0] wdepth_o, + + // read port + input logic clk_rd_i, + input logic rst_rd_ni, + output logic rvalid_o, + input logic rready_i, + output logic [Width-1:0] rdata_o, + output logic [DepthW-1:0] rdepth_o +); + + // Depth must be a power of 2 for the gray code pointers to work + `ASSERT_INIT(ParamCheckDepth_A, (Depth == 2**$clog2(Depth))) + + localparam int unsigned PTRV_W = (Depth == 1) ? 1 : $clog2(Depth); + localparam int unsigned PTR_WIDTH = (Depth == 1) ? 1 : PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr_q, fifo_wptr_d; + logic [PTR_WIDTH-1:0] fifo_rptr_q, fifo_rptr_d; + logic [PTR_WIDTH-1:0] fifo_wptr_sync_combi, fifo_rptr_sync_combi; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_sync, fifo_rptr_gray_sync, fifo_rptr_sync_q; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_q, fifo_wptr_gray_d; + logic [PTR_WIDTH-1:0] fifo_rptr_gray_q, fifo_rptr_gray_d; + logic fifo_incr_wptr, fifo_incr_rptr; + logic full_wclk, full_rclk, empty_rclk; + logic [Width-1:0] storage [Depth]; + + /////////////////// + // Write Pointer // + /////////////////// + + assign fifo_incr_wptr = wvalid_i & wready_o; + + // decimal version + assign fifo_wptr_d = fifo_wptr_q + PTR_WIDTH'(1'b1); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_wptr_q <= '0; + end else if (fifo_incr_wptr) begin + fifo_wptr_q <= fifo_wptr_d; + end + end + + // gray-coded version + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_wptr_gray_q <= '0; + end else if (fifo_incr_wptr) begin + fifo_wptr_gray_q <= fifo_wptr_gray_d; + end + end + + // sync gray-coded pointer to read clk + prim_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d_i (fifo_wptr_gray_q), + .q_o (fifo_wptr_gray_sync)); + + ////////////////// + // Read Pointer // + ////////////////// + + assign fifo_incr_rptr = rvalid_o & rready_i; + + // decimal version + assign fifo_rptr_d = fifo_rptr_q + PTR_WIDTH'(1'b1); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + fifo_rptr_q <= '0; + end else if (fifo_incr_rptr) begin + fifo_rptr_q <= fifo_rptr_d; + end + end + + // gray-coded version + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + fifo_rptr_gray_q <= '0; + end else if (fifo_incr_rptr) begin + fifo_rptr_gray_q <= fifo_rptr_gray_d; + end + end + + // sync gray-coded pointer to write clk + prim_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d_i (fifo_rptr_gray_q), + .q_o (fifo_rptr_gray_sync)); + + // Registered version of synced read pointer + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + fifo_rptr_sync_q <= '0; + end else begin + fifo_rptr_sync_q <= fifo_rptr_sync_combi; + end + end + + ////////////////// + // Empty / Full // + ////////////////// + + logic [PTR_WIDTH-1:0] xor_mask; + assign xor_mask = PTR_WIDTH'(1'b1) << (PTR_WIDTH-1); + assign full_wclk = (fifo_wptr_q == (fifo_rptr_sync_q ^ xor_mask)); + assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr_q ^ xor_mask)); + assign empty_rclk = (fifo_wptr_sync_combi == fifo_rptr_q); + + if (Depth > 1) begin : g_depth_calc + + // Current depth in the write clock side + logic wptr_msb; + logic rptr_sync_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_sync_value; + + assign wptr_msb = fifo_wptr_q[PTR_WIDTH-1]; + assign rptr_sync_msb = fifo_rptr_sync_q[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr_q[0+:PTRV_W]; + assign rptr_sync_value = fifo_rptr_sync_q[0+:PTRV_W]; + assign wdepth_o = (full_wclk) ? DepthW'(Depth) : + (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) : + (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ; + + // Current depth in the read clock side + logic rptr_msb; + logic wptr_sync_msb; + logic [PTRV_W-1:0] rptr_value; + logic [PTRV_W-1:0] wptr_sync_value; + + assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr_q[PTR_WIDTH-1]; + assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W]; + assign rptr_value = fifo_rptr_q[0+:PTRV_W]; + assign rdepth_o = (full_rclk) ? DepthW'(Depth) : + (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ; + + end else begin : g_no_depth_calc + + assign rdepth_o = full_rclk; + assign wdepth_o = full_wclk; + + end + + assign wready_o = ~full_wclk; + assign rvalid_o = ~empty_rclk; + + ///////////// + // Storage // + ///////////// + + logic [Width-1:0] rdata_int; + if (Depth > 1) begin : g_storage_mux + + always_ff @(posedge clk_wr_i) begin + if (fifo_incr_wptr) begin + storage[fifo_wptr_q[PTRV_W-1:0]] <= wdata_i; + end + end + + assign rdata_int = storage[fifo_rptr_q[PTRV_W-1:0]]; + + end else begin : g_storage_simple + + always_ff @(posedge clk_wr_i) begin + if (fifo_incr_wptr) begin + storage[0] <= wdata_i; + end + end + + assign rdata_int = storage[0]; + + end + + // rdata_o is qualified with rvalid_o to avoid CDC error + if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero + if (OutputZeroIfInvalid == 1'b1) begin : gen_invalid_zero + assign rdata_o = empty_rclk ? '0 : (rvalid_o ? rdata_int : '0); + end + else begin : gen_invalid_non_zero + assign rdata_o = empty_rclk ? '0 : rdata_int; + end + end else begin : gen_no_output_zero + if (OutputZeroIfInvalid == 1'b1) begin : gen_invalid_zero + assign rdata_o = rvalid_o ? rdata_int : '0; + end + else begin : gen_invalid_non_zero + assign rdata_o = rdata_int; + end + end + + ////////////////////////////////////// + // Decimal <-> Gray-code Conversion // + ////////////////////////////////////// + + // This code is all in a generate context to avoid lint errors when Depth <= 2 + if (Depth > 2) begin : g_full_gray_conversion + + function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval); + logic [PTR_WIDTH-1:0] decval_sub; + logic [PTR_WIDTH-1:0] decval_in; + logic unused_decval_msb; + + decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1; + + decval_in = decval[PTR_WIDTH-1] ? decval_sub : decval; + + // We do not care about the MSB, hence we mask it out + unused_decval_msb = decval_in[PTR_WIDTH-1]; + decval_in[PTR_WIDTH-1] = 1'b0; + + // Perform the XOR conversion + dec2gray = decval_in; + dec2gray ^= (decval_in >> 1); + + // Override the MSB + dec2gray[PTR_WIDTH-1] = decval[PTR_WIDTH-1]; + endfunction + + // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0. + function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval); + logic [PTR_WIDTH-1:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp = '0; + for (int i = PTR_WIDTH-2; i >= 0; i--) begin + dec_tmp[i] = dec_tmp[i+1] ^ grayval[i]; + end + dec_tmp_sub = (PTR_WIDTH)'(Depth) - dec_tmp - 1'b1; + if (grayval[PTR_WIDTH-1]) begin + gray2dec = dec_tmp_sub; + // Override MSB + gray2dec[PTR_WIDTH-1] = 1'b1; + unused_decsub_msb = dec_tmp_sub[PTR_WIDTH-1]; + end else begin + gray2dec = dec_tmp; + end + endfunction + + // decimal version of read pointer in write domain + assign fifo_rptr_sync_combi = gray2dec(fifo_rptr_gray_sync); + // decimal version of write pointer in read domain + assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync); + + assign fifo_rptr_gray_d = dec2gray(fifo_rptr_d); + assign fifo_wptr_gray_d = dec2gray(fifo_wptr_d); + + end else if (Depth == 2) begin : g_simple_gray_conversion + + assign fifo_rptr_sync_combi = {fifo_rptr_gray_sync[PTR_WIDTH-1], ^fifo_rptr_gray_sync}; + assign fifo_wptr_sync_combi = {fifo_wptr_gray_sync[PTR_WIDTH-1], ^fifo_wptr_gray_sync}; + + assign fifo_rptr_gray_d = {fifo_rptr_d[PTR_WIDTH-1], ^fifo_rptr_d}; + assign fifo_wptr_gray_d = {fifo_wptr_d[PTR_WIDTH-1], ^fifo_wptr_d}; + + end else begin : g_no_gray_conversion + + assign fifo_rptr_sync_combi = fifo_rptr_gray_sync; + assign fifo_wptr_sync_combi = fifo_wptr_gray_sync; + + assign fifo_rptr_gray_d = fifo_rptr_d; + assign fifo_wptr_gray_d = fifo_wptr_d; + + end + + // TODO: assertions on full, empty + `ASSERT(GrayWptr_A, ##1 $countones(fifo_wptr_gray_q ^ $past(fifo_wptr_gray_q)) <= 1, + clk_wr_i, !rst_wr_ni) + `ASSERT(GrayRptr_A, ##1 $countones(fifo_rptr_gray_q ^ $past(fifo_rptr_gray_q)) <= 1, + clk_rd_i, !rst_rd_ni) + +endmodule diff --git a/synth/prim/rtl/prim_fifo_async_simple.sv b/synth/prim/rtl/prim_fifo_async_simple.sv new file mode 100755 index 0000000..5dbeed0 --- /dev/null +++ b/synth/prim/rtl/prim_fifo_async_simple.sv @@ -0,0 +1,91 @@ +// 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_fifo_async_simple #( + parameter int unsigned Width = 16, + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by + // default. + parameter bit EnRzHs = 1'b0 // By Default, the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake protocol + // if the FSMs need to be partial-reset-safe. +) ( + // write port + input logic clk_wr_i, + input logic rst_wr_ni, + input logic wvalid_i, + output logic wready_o, + input logic [Width-1:0] wdata_i, + + // read port + input logic clk_rd_i, + input logic rst_rd_ni, + output logic rvalid_o, + input logic rready_i, + output logic [Width-1:0] rdata_o +); + + //////////////// + // FIFO logic // + //////////////// + + // Convert ready/valid to req/ack + logic wr_en; + logic src_req, src_ack; + logic pending_d, pending_q, not_in_reset_q; + assign wready_o = !pending_q && not_in_reset_q; + assign wr_en = wvalid_i && wready_o; + assign src_req = pending_q || wvalid_i; + + assign pending_d = (src_ack) ? 1'b0 : + (wr_en) ? 1'b1 : pending_q; + + logic dst_req, dst_ack; + assign rvalid_o = dst_req; + assign dst_ack = dst_req && rready_i; + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + pending_q <= 1'b0; + not_in_reset_q <= 1'b0; + end else begin + pending_q <= pending_d; + not_in_reset_q <= 1'b1; + end + end + + //////////////////////////////////// + // REQ/ACK synchronizer primitive // + //////////////////////////////////// + + prim_sync_reqack #( + .EnRstChks(EnRstChks), + .EnRzHs(EnRzHs) + ) u_prim_sync_reqack ( + .clk_src_i(clk_wr_i), + .rst_src_ni(rst_wr_ni), + .clk_dst_i(clk_rd_i), + .rst_dst_ni(rst_rd_ni), + .req_chk_i(1'b0), + .src_req_i(src_req), + .src_ack_o(src_ack), + .dst_req_o(dst_req), + .dst_ack_i(dst_ack) + ); + + ////////////////////// + // Data holding reg // + ////////////////////// + + logic [Width-1:0] data_q; + always_ff @(posedge clk_wr_i) begin + if (wr_en) begin + data_q <= wdata_i; + end + end + assign rdata_o = data_q; + +endmodule diff --git a/synth/prim/rtl/prim_fifo_async_sram_adapter.sv b/synth/prim/rtl/prim_fifo_async_sram_adapter.sv new file mode 100755 index 0000000..4d4911c --- /dev/null +++ b/synth/prim/rtl/prim_fifo_async_sram_adapter.sv @@ -0,0 +1,439 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic Asynchronous SRAM FIFO (Dual port SRAM) + +`include "prim_assert.sv" + +module prim_fifo_async_sram_adapter #( + parameter int unsigned Width = 32, + parameter int unsigned Depth = 16, + + // SRAM parameters + parameter int unsigned SramAw = 16, + + // If SramDw > Width, upper data bits are 0. + parameter int unsigned SramDw = 32, + parameter logic [SramAw-1:0] SramBaseAddr = 'h 0, + + // derived + localparam int unsigned DepthW = $clog2(Depth+1) +) ( + // Write port + input clk_wr_i, + input rst_wr_ni, + input wvalid_i, + output logic wready_o, + input [Width-1:0] wdata_i, + output logic [DepthW-1:0] wdepth_o, + + // Read port + input clk_rd_i, + input rst_rd_ni, + output logic rvalid_o, + input rready_i, + output logic [Width-1:0] rdata_o, + output logic [DepthW-1:0] rdepth_o, + + output logic r_full_o, + output logic r_notempty_o, + + output logic w_full_o, + + // TODO: watermark(threshold) ? + + // SRAM interface + // Write SRAM port + output logic w_sram_req_o, + input w_sram_gnt_i, + output logic w_sram_write_o, + output logic [SramAw-1:0] w_sram_addr_o, + output logic [SramDw-1:0] w_sram_wdata_o, + output logic [SramDw-1:0] w_sram_wmask_o, + input w_sram_rvalid_i, // not used + input [SramDw-1:0] w_sram_rdata_i, // not used + input [1:0] w_sram_rerror_i, // not used + + // Read SRAM port + output logic r_sram_req_o, + input r_sram_gnt_i, + output logic r_sram_write_o, + output logic [SramAw-1:0] r_sram_addr_o, + output logic [SramDw-1:0] r_sram_wdata_o, // not used + output logic [SramDw-1:0] r_sram_wmask_o, // not used + input r_sram_rvalid_i, + input [SramDw-1:0] r_sram_rdata_i, + input [1:0] r_sram_rerror_i +); + + //////////////// + // Definition // + //////////////// + // w_: write clock domain signals + // r_: read clock domain signals + + // PtrVW: Pointer Value (without msb, flip) width + localparam int unsigned PtrVW = $clog2(Depth); + // PtrW: Read/Write pointer with flip bit + localparam int unsigned PtrW = PtrVW+1; + + //////////// + // Signal // + //////////// + + logic [PtrW-1:0] w_wptr_q, w_wptr_d, w_wptr_gray_d, w_wptr_gray_q; + logic [PtrW-1:0] r_wptr_gray, r_wptr; + logic [PtrVW-1:0] w_wptr_v, r_wptr_v; + logic w_wptr_p, r_wptr_p; // phase + + logic [PtrW-1:0] r_rptr_q, r_rptr_d, r_rptr_gray_d, r_rptr_gray_q; + logic [PtrW-1:0] w_rptr_gray, w_rptr; + logic [PtrVW-1:0] r_rptr_v, w_rptr_v; + logic r_rptr_p, w_rptr_p; // phase + + logic w_wptr_inc, r_rptr_inc; + + logic w_full, r_full, r_empty; + + // SRAM response one clock delayed. So store the value into read clock + // domain + logic stored; + logic [Width-1:0] rdata_q, rdata_d; + + // SRAM has another read pointer (for address of SRAM req) + // It is -1 of r_rptr if stored, else same to r_rptr + logic r_sram_rptr_inc; + logic [PtrW-1:0] r_sram_rptr; + + // r_sram_rptr == r_wptr + // Used to determine r_sram_req + logic r_sramrptr_empty; + + logic rfifo_ack; // Used to check if FIFO read interface consumes a data + logic rsram_ack; + + ////////////// + // Datapath // + ////////////// + + // Begin: Write pointer sync to read clock ======================== + assign w_wptr_inc = wvalid_i & wready_o; + + assign w_wptr_d = w_wptr_q + PtrW'(1); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) begin + if (!rst_wr_ni) begin + w_wptr_q <= PtrW'(0); + w_wptr_gray_q <= PtrW'(0); + end else if (w_wptr_inc) begin + w_wptr_q <= w_wptr_d; + w_wptr_gray_q <= w_wptr_gray_d; + end + end + + assign w_wptr_v = w_wptr_q[0+:PtrVW]; + assign w_wptr_p = w_wptr_q[PtrW-1]; + + assign w_wptr_gray_d = dec2gray(w_wptr_d); + + prim_flop_2sync #( + .Width (PtrW) + ) u_sync_wptr_gray ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d_i (w_wptr_gray_q), + .q_o (r_wptr_gray) + ); + + assign r_wptr = gray2dec(r_wptr_gray); + assign r_wptr_p = r_wptr[PtrW-1]; + assign r_wptr_v = r_wptr[0+:PtrVW]; + + assign wdepth_o = (w_wptr_p == w_rptr_p) + ? DepthW'(w_wptr_v - w_rptr_v) + : DepthW'({1'b1, w_wptr_v} - {1'b 0, w_rptr_v}); + // End: Write pointer sync to read clock ------------------------ + + // Begin: Read pointer sync to write clock ======================== + //assign r_rptr_inc = rvalid_o & rready_i; + //assign r_rptr_inc = r_sram_req_o && r_sram_gnt_i; + // Increase the read pointer (crossing the clock domain) only when the + // reader acked. + assign r_rptr_inc = rfifo_ack; + + assign r_rptr_d = r_rptr_q + PtrW'(1); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + r_rptr_q <= PtrW'(0); + r_rptr_gray_q <= PtrW'(0); + end else if (r_rptr_inc) begin + r_rptr_q <= r_rptr_d; + r_rptr_gray_q <= r_rptr_gray_d; + end + end + + assign r_rptr_v = r_rptr_q[0+:PtrVW]; + assign r_rptr_p = r_rptr_q[PtrW-1]; + + assign r_rptr_gray_d = dec2gray(r_rptr_d); + + prim_flop_2sync #( + .Width (PtrW) + ) u_sync_rptr_gray ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d_i (r_rptr_gray_q), + .q_o (w_rptr_gray) + ); + + assign w_rptr = gray2dec(w_rptr_gray); + assign w_rptr_p = w_rptr[PtrW-1]; + assign w_rptr_v = w_rptr[0+:PtrVW]; + + assign rdepth_o = (r_wptr_p == r_rptr_p) + ? DepthW'(r_wptr_v - r_rptr_v) + : DepthW'({1'b1, r_wptr_v} - {1'b 0, r_rptr_v}); + // End: Read pointer sync to write clock ------------------------ + + // Begin: SRAM Read pointer + assign r_sram_rptr_inc = rsram_ack; + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + r_sram_rptr <= PtrW'(0); + end else if (r_sram_rptr_inc) begin + r_sram_rptr <= r_sram_rptr + PtrW'(1); + end + end + + assign r_sramrptr_empty = (r_wptr == r_sram_rptr); + // End: SRAM Read pointer + + // Full/ Empty + // Lint complains PtrW'(1) << (PtrW-1). So changed as below + localparam logic [PtrW-1:0] XorMask = {1'b 1, {PtrW-1{1'b0}}}; + assign w_full = (w_wptr_q == (w_rptr ^ XorMask)); + assign r_full = (r_wptr == (r_rptr_q ^ XorMask)); + assign r_empty = (r_wptr == r_rptr_q); + + logic unused_r_empty; + assign unused_r_empty = r_empty; + + assign r_full_o = r_full; + assign w_full_o = w_full; + + // The notempty status !(wptr == rptr) assert one clock earlier than the + // actual `rvalid` signals. + // + // The reason is due to the SRAM read latency. The module uses SRAM FIFO + // interface. When the logic in producer domain pushes entries, the pointer + // is increased. This triggers the FIFO logic in the consumer clock domain + // fetches data from SRAM. + // + // The pointer crosses the clock boundary. It takes usually two cycles (in + // the consumer side). Then, as the read and write pointer in the read clock + // domain has a gap by 1, the FIFO not empty status is raised. + // + // At this time, the logic just sent the read request to the SRAM. The data + // is not yet read. The `rvalid` asserts when it receives data from the + // SRAM. + // + // So, if the consumer reads data at the same cycle when notempty status is + // raised, it reads incorrect data. + assign r_notempty_o = rvalid_o; + + assign rsram_ack = r_sram_req_o && r_sram_gnt_i; + assign rfifo_ack = rvalid_o && rready_i; + + // SRAM Write Request + assign w_sram_req_o = wvalid_i && !w_full; + assign wready_o = !w_full && w_sram_gnt_i; + assign w_sram_write_o = 1'b 1; // Always write + assign w_sram_addr_o = SramBaseAddr + SramAw'(w_wptr_v); + + assign w_sram_wdata_o = SramDw'(wdata_i); + assign w_sram_wmask_o = SramDw'({Width{1'b1}}); + + logic unused_w_sram; + assign unused_w_sram = ^{w_sram_rvalid_i, w_sram_rdata_i, w_sram_rerror_i}; + + // SRAM Read Request + // Request Scenario (!r_empty): + // - storage empty: Send request if + // !r_sram_rvalid_i || (rfifo_ack && r_sram_rvalid_i); + // - storage !empty: depends on the rfifo_ack: + // - r_rptr_inc: Can request more + // - !r_rptr_inc: Can't request + always_comb begin : r_sram_req + r_sram_req_o = 1'b 0; + // Karnough Map (!empty): sram_req + // {sram_rv, rfifo_ack} | 00 | 01 | 11 | 10 + // ---------------------------------------------------------- + // stored | 0 | 1 | impossible | 1 | 0 + // | 1 | 0 | 1 | X | impossible + // + // req_o = r_ptr_inc || (!stored && !r_sram_rvalid_i) + + if (stored) begin + // storage has data. depends on rfifo_ack + // rfifo_ack can be replaced to rready_i as `rvalid_o` is 1 + r_sram_req_o = !r_sramrptr_empty && rfifo_ack; + end else begin + // storage has no data. + // Can send request only when the reader accept the request or no + // previous request sent out. + r_sram_req_o = !r_sramrptr_empty && !(r_sram_rvalid_i ^ rfifo_ack); + end + end : r_sram_req + + assign rvalid_o = stored || r_sram_rvalid_i; + assign r_sram_write_o = 1'b 0; // always read + assign r_sram_wdata_o = '0; + assign r_sram_wmask_o = '0; + + // Send SRAM request with sram read pointer. + assign r_sram_addr_o = SramBaseAddr + SramAw'(r_sram_rptr[0+:PtrVW]); + + assign rdata_d = (r_sram_rvalid_i) ? r_sram_rdata_i[0+:Width] : Width'(0); + + assign rdata_o = (stored) ? rdata_q : rdata_d; + + logic unused_rsram; + assign unused_rsram = ^{r_sram_rerror_i}; + + if (Width < SramDw) begin : g_unused_rdata + logic unused_rdata; + assign unused_rdata = ^r_sram_rdata_i[SramDw-1:Width]; + end : g_unused_rdata + + // read clock domain rdata storage + logic store_en; + + // Karnough Map (r_sram_rvalid_i): + // rfifo_ack | 0 | 1 | + // --------------------- + // stored 0 | 1 | 0 | + // 1 | 0 | 1 | + // + // stored = s.r.v && XNOR(stored, rptr_inc) + assign store_en = r_sram_rvalid_i && !(stored ^ rfifo_ack); + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) begin + if (!rst_rd_ni) begin + stored <= 1'b 0; + rdata_q <= Width'(0); + end else if (store_en) begin + stored <= 1'b 1; + rdata_q <= rdata_d; + end else if (!r_sram_rvalid_i && rfifo_ack) begin + // No request sent, host reads the data + stored <= 1'b 0; + rdata_q <= Width'(0); + end + end + + ////////////// + // Function // + ////////////// + + // dec2gray / gray2dec copied from prim_fifo_async.sv + function automatic [PtrW-1:0] dec2gray(input logic [PtrW-1:0] decval); + logic [PtrW-1:0] decval_sub; + logic [PtrW-1:0] decval_in; + logic unused_decval_msb; + + decval_sub = (PtrW)'(Depth) - {1'b0, decval[PtrW-2:0]} - 1'b1; + + decval_in = decval[PtrW-1] ? decval_sub : decval; + + // We do not care about the MSB, hence we mask it out + unused_decval_msb = decval_in[PtrW-1]; + decval_in[PtrW-1] = 1'b0; + + // Perform the XOR conversion + dec2gray = decval_in; + dec2gray ^= (decval_in >> 1); + + // Override the MSB + dec2gray[PtrW-1] = decval[PtrW-1]; + endfunction + + // Algorithm walks up from 0..N-1 then flips the upper bit and walks down from N-1 to 0. + function automatic [PtrW-1:0] gray2dec(input logic [PtrW-1:0] grayval); + logic [PtrW-1:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp = '0; + for (int i = PtrW-2; i >= 0; i--) begin + dec_tmp[i] = dec_tmp[i+1] ^ grayval[i]; + end + dec_tmp_sub = (PtrW)'(Depth) - dec_tmp - 1'b1; + if (grayval[PtrW-1]) begin + gray2dec = dec_tmp_sub; + // Override MSB + gray2dec[PtrW-1] = 1'b1; + unused_decsub_msb = dec_tmp_sub[PtrW-1]; + end else begin + gray2dec = dec_tmp; + end + endfunction + + /////////////// + // Assertion // + /////////////// + + `ASSERT_INIT(ParamCheckDepth_A, (Depth == 2**$clog2(Depth))) + + // Use FF if less than 4. + `ASSERT_INIT(MinDepth_A, Depth >= 4) + + // SramDw greather than or equal to Width + `ASSERT_INIT(WidthMatch_A, SramDw >= Width) + + // Not stored, Not read valid, but rptr_inc case is impossible + `ASSERT(RptrIncDataValid_A, + r_rptr_inc |-> stored || r_sram_rvalid_i, + clk_rd_i, !rst_rd_ni) + `ASSERT(SramRvalid_A, + r_sram_rvalid_i |-> !stored || r_rptr_inc, + clk_rd_i, !rst_rd_ni) + + // FIFO interface + `ASSERT(NoWAckInFull_A, w_wptr_inc |-> !w_full, + clk_wr_i, !rst_wr_ni) + + `ASSERT(WptrIncrease_A, + w_wptr_inc |=> w_wptr_v == PtrVW'($past(w_wptr_v,2) + 1), + clk_wr_i, !rst_wr_ni) + `ASSERT(WptrGrayOneBitAtATime_A, + w_wptr_inc |=> $countones(w_wptr_gray_q ^ $past(w_wptr_gray_q,2)) == 1, + clk_wr_i, !rst_wr_ni) + + `ASSERT(NoRAckInEmpty_A, r_rptr_inc |-> !r_empty, + clk_rd_i, !rst_rd_ni) + + `ASSERT(RptrIncrease_A, + r_rptr_inc |=> PtrVW'($past(r_rptr_v) + 1) == r_rptr_v, + clk_rd_i, !rst_rd_ni) + `ASSERT(RptrGrayOneBitAtATime_A, + r_rptr_inc |=> $countones(r_rptr_gray_q ^ $past(r_rptr_gray_q)) == 1, + clk_rd_i, !rst_rd_ni) + + // SRAM interface + `ASSERT(WSramRvalid_A, !w_sram_rvalid_i, clk_wr_i, !rst_wr_ni) + `ASSUME_FPV(WSramRdataError_M, w_sram_rdata_i == '0 && w_sram_rerror_i == '0, + clk_wr_i, !rst_wr_ni) + + `ASSUME(RSramRvalidOneCycle_M, + r_sram_req_o && r_sram_gnt_i |=> r_sram_rvalid_i, + clk_rd_i, !rst_rd_ni) + `ASSUME_FPV(RErrorTied_M, r_sram_rerror_i == '0, + clk_rd_i, !rst_rd_ni) + + + // FPV coverage + `COVER_FPV(WFull_C, w_full, clk_wr_i, !rst_wr_ni) + +endmodule : prim_fifo_async_sram_adapter diff --git a/synth/prim/rtl/prim_fifo_sync.sv b/synth/prim/rtl/prim_fifo_sync.sv new file mode 100755 index 0000000..5e9ee9c --- /dev/null +++ b/synth/prim/rtl/prim_fifo_sync.sv @@ -0,0 +1,198 @@ +// 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, + parameter bit OutputZeroIfEmpty = 1'b1, // if == 1 always output 0 when FIFO is empty + parameter bit Secure = 1'b0, // use prim count for pointers + // derived parameter + localparam int DepthW = prim_util_pkg::vbits(Depth+1) +) ( + input clk_i, + input rst_ni, + // synchronous clear / flush port + input clr_i, + // write port + input wvalid_i, + output wready_o, + input [Width-1:0] wdata_i, + // read port + output rvalid_o, + input rready_i, + output [Width-1:0] rdata_o, + // occupancy + output full_o, + output [DepthW-1:0] depth_o, + output err_o +); + + + // FIFO is in complete passthrough mode + if (Depth == 0) begin : gen_passthru_fifo + `ASSERT_INIT(paramCheckPass, Pass == 1) + + assign depth_o = 1'b0; //output is meaningless + + // devie facing + assign rvalid_o = wvalid_i; + assign rdata_o = wdata_i; + + // host facing + assign wready_o = rready_i; + assign full_o = rready_i; + + // this avoids lint warnings + logic unused_clr; + assign unused_clr = clr_i; + + // No error + assign err_o = 1'b 0; + + // Normal FIFO construction + end else begin : gen_normal_fifo + + localparam int unsigned PTRV_W = prim_util_pkg::vbits(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; + + // module under reset flag + logic under_rst; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + under_rst <= 1'b1; + end else if (under_rst) begin + under_rst <= ~under_rst; + end + end + + // 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_o = (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_i & wready_o & ~under_rst; + assign fifo_incr_rptr = rvalid_o & rready_i & ~under_rst; + + // full and not ready for write are two different concepts. + // The latter can be '0' when under reset, while the former is an indication that no more + // entries can be written. + assign wready_o = ~full & ~under_rst; + assign full_o = full; + assign rvalid_o = ~empty & ~under_rst; + + prim_fifo_sync_cnt #( + .Width(PTR_WIDTH), + .Depth(Depth), + .Secure(Secure) + ) u_fifo_cnt ( + .clk_i, + .rst_ni, + .clr_i, + .incr_wptr_i(fifo_incr_wptr), + .incr_rptr_i(fifo_incr_rptr), + .wptr_o(fifo_wptr), + .rptr_o(fifo_rptr), + .err_o + ); + + //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] == (PTR_WIDTH-1)'(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] == (PTR_WIDTH-1)'(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_i; + 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_i; + end + end + + logic [Width-1:0] rdata_int; + if (Pass == 1'b1) begin : gen_pass + assign rdata_int = (fifo_empty && wvalid_i) ? wdata_i : storage_rdata; + assign empty = fifo_empty & ~wvalid_i; + end else begin : gen_nopass + assign rdata_int = storage_rdata; + assign empty = fifo_empty; + end + + if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero + assign rdata_o = empty ? 'b0 : rdata_int; + end else begin : gen_no_output_zero + assign rdata_o = rdata_int; + end + + `ASSERT(depthShallNotExceedParamDepth, !empty |-> depth_o <= DepthW'(Depth)) + end // block: gen_normal_fifo + + + ////////////////////// + // Known Assertions // + ////////////////////// + + `ASSERT(DataKnown_A, rvalid_o |-> !$isunknown(rdata_o)) + `ASSERT_KNOWN(DepthKnown_A, depth_o) + `ASSERT_KNOWN(RvalidKnown_A, rvalid_o) + `ASSERT_KNOWN(WreadyKnown_A, wready_o) + +endmodule diff --git a/synth/prim/rtl/prim_fifo_sync_cnt.sv b/synth/prim/rtl/prim_fifo_sync_cnt.sv new file mode 100755 index 0000000..d54977c --- /dev/null +++ b/synth/prim/rtl/prim_fifo_sync_cnt.sv @@ -0,0 +1,104 @@ +// 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_cnt #( + parameter int Depth = 4, + parameter int Width = 16, + parameter bit Secure = 1'b0 +) ( + input clk_i, + input rst_ni, + input clr_i, + input incr_wptr_i, + input incr_rptr_i, + output logic [Width-1:0] wptr_o, + output logic [Width-1:0] rptr_o, + output logic err_o +); + + logic wptr_wrap; + logic [Width-1:0] wptr_wrap_cnt; + logic rptr_wrap; + logic [Width-1:0] rptr_wrap_cnt; + + assign wptr_wrap = incr_wptr_i & (wptr_o[Width-2:0] == unsigned'((Width-1)'(Depth-1))); + assign rptr_wrap = incr_rptr_i & (rptr_o[Width-2:0] == unsigned'((Width-1)'(Depth-1))); + + assign wptr_wrap_cnt = {~wptr_o[Width-1],{(Width-1){1'b0}}}; + assign rptr_wrap_cnt = {~rptr_o[Width-1],{(Width-1){1'b0}}}; + + if (Secure) begin : gen_secure_ptrs + logic wptr_err; + prim_count #( + .Width(Width) + ) u_wptr ( + .clk_i, + .rst_ni, + .clr_i, + .set_i(wptr_wrap), + .set_cnt_i(wptr_wrap_cnt), + .incr_en_i(incr_wptr_i), + .decr_en_i(1'b0), + .step_i(Width'(1'b1)), + .commit_i(1'b1), + .cnt_o(wptr_o), + .cnt_after_commit_o(), + .err_o(wptr_err) + ); + + logic rptr_err; + prim_count #( + .Width(Width) + ) u_rptr ( + .clk_i, + .rst_ni, + .clr_i, + .set_i(rptr_wrap), + .set_cnt_i(rptr_wrap_cnt), + .incr_en_i(incr_rptr_i), + .decr_en_i(1'b0), + .step_i(Width'(1'b1)), + .commit_i(1'b1), + .cnt_o(rptr_o), + .cnt_after_commit_o(), + .err_o(rptr_err) + ); + + assign err_o = wptr_err | rptr_err; + + end else begin : gen_normal_ptrs + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + wptr_o <= {(Width){1'b0}}; + end else if (clr_i) begin + wptr_o <= {(Width){1'b0}}; + end else if (wptr_wrap) begin + wptr_o <= wptr_wrap_cnt; + end else if (incr_wptr_i) begin + wptr_o <= wptr_o + {{(Width-1){1'b0}},1'b1}; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rptr_o <= {(Width){1'b0}}; + end else if (clr_i) begin + rptr_o <= {(Width){1'b0}}; + end else if (rptr_wrap) begin + rptr_o <= rptr_wrap_cnt; + end else if (incr_rptr_i) begin + rptr_o <= rptr_o + {{(Width-1){1'b0}},1'b1}; + end + end + + assign err_o = '0; + end + + + +endmodule // prim_fifo_sync_cnt diff --git a/synth/prim/rtl/prim_filter.sv b/synth/prim/rtl/prim_filter.sv new file mode 100755 index 0000000..a9d5dcd --- /dev/null +++ b/synth/prim/rtl/prim_filter.sv @@ -0,0 +1,73 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: 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 #( + // If this parameter is set, an additional 2-stage synchronizer will be + // added at the input. + parameter bit AsyncOn = 0, + parameter int unsigned Cycles = 4 +) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + output logic filter_o +); + + logic [Cycles-1:0] stored_vector_q, stored_vector_d; + logic stored_value_q, update_stored_value; + logic unused_stored_vector_q_msb; + + logic filter_synced; + + if (AsyncOn) begin : gen_async + // Run this through a 2 stage synchronizer to + // prevent metastability. + prim_flop_2sync #( + .Width(1) + ) prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(filter_i), + .q_o(filter_synced) + ); + end else begin : gen_sync + assign filter_synced = filter_i; + 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_synced; + end + end + + assign stored_vector_d = {stored_vector_q[Cycles-2:0],filter_synced}; + assign unused_stored_vector_q_msb = stored_vector_q[Cycles-1]; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_vector_q <= '0; + 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_synced; + +endmodule + diff --git a/synth/prim/rtl/prim_filter_ctr.sv b/synth/prim/rtl/prim_filter_ctr.sv new file mode 100755 index 0000000..435b161 --- /dev/null +++ b/synth/prim/rtl/prim_filter_ctr.sv @@ -0,0 +1,82 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: 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 #( + // If this parameter is set, an additional 2-stage synchronizer will be + // added at the input. + parameter bit AsyncOn = 0, + parameter int unsigned CntWidth = 2 +) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + input [CntWidth-1:0] thresh_i, + output logic filter_o +); + + logic [CntWidth-1:0] diff_ctr_q, diff_ctr_d; + logic filter_q, stored_value_q, update_stored_value; + + logic filter_synced; + + if (AsyncOn) begin : gen_async + // Run this through a 2 stage synchronizer to + // prevent metastability. + prim_flop_2sync #( + .Width(1) + ) prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(filter_i), + .q_o(filter_synced) + ); + end else begin : gen_sync + assign filter_synced = filter_i; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + filter_q <= 1'b0; + end else begin + filter_q <= filter_synced; + 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_synced; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + diff_ctr_q <= '0; + end else begin + diff_ctr_q <= diff_ctr_d; + end + end + + // always look for differences, even if not filter enabled + assign update_stored_value = (diff_ctr_d == thresh_i); + assign diff_ctr_d = (filter_synced != filter_q) ? '0 : // restart + (diff_ctr_q >= thresh_i) ? thresh_i : // saturate + (diff_ctr_q + 1'b1); // count up + + assign filter_o = enable_i ? stored_value_q : filter_synced; + +endmodule + diff --git a/synth/prim/rtl/prim_flop_2sync.sv b/synth/prim/rtl/prim_flop_2sync.sv new file mode 100755 index 0000000..7ff3c76 --- /dev/null +++ b/synth/prim/rtl/prim_flop_2sync.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 +// +// Generic double-synchronizer flop +// This may need to be moved to prim_generic if libraries have a specific cell +// for synchronization + +module prim_flop_2sync #( + parameter int Width = 16, + parameter logic [Width-1:0] ResetValue = '0, + parameter bit EnablePrimCdcRand = 1 +) ( + input clk_i, + input rst_ni, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + logic [Width-1:0] d_o; + logic [Width-1:0] intq; + +`ifdef SIMULATION + + prim_cdc_rand_delay #( + .DataWidth(Width), + .Enable(EnablePrimCdcRand) + ) u_prim_cdc_rand_delay ( + .clk_i, + .rst_ni, + .src_data_i(d_i), + .prev_data_i(intq), + .dst_data_o(d_o) + ); +`else // !`ifdef SIMULATION + logic unused_sig; + assign unused_sig = EnablePrimCdcRand; + always_comb d_o = d_i; +`endif // !`ifdef SIMULATION + + prim_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_sync_1 ( + .clk_i, + .rst_ni, + .d_i(d_o), + .q_o(intq) + ); + + prim_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_sync_2 ( + .clk_i, + .rst_ni, + .d_i(intq), + .q_o + ); + +endmodule : prim_flop_2sync diff --git a/synth/prim/rtl/prim_flop_macros.sv b/synth/prim/rtl/prim_flop_macros.sv new file mode 100755 index 0000000..edae459 --- /dev/null +++ b/synth/prim/rtl/prim_flop_macros.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 + +`ifndef PRIM_FLOP_MACROS_SV +`define PRIM_FLOP_MACROS_SV + +///////////////////////////////////// +// Default Values for Macros below // +///////////////////////////////////// + +`define PRIM_FLOP_CLK clk_i +`define PRIM_FLOP_RST rst_ni +`define PRIM_FLOP_RESVAL '0 + +///////////////////// +// Register Macros // +///////////////////// + +// TODO: define other variations of register macros so that they can be used throughout all designs +// to make the code more concise. + +// Register with asynchronous reset. +`define PRIM_FLOP_A(__d, __q, __resval = `PRIM_FLOP_RESVAL, __clk = `PRIM_FLOP_CLK, __rst_n = `PRIM_FLOP_RST) \ + always_ff @(posedge __clk or negedge __rst_n) begin \ + if (!__rst_n) begin \ + __q <= __resval; \ + end else begin \ + __q <= __d; \ + end \ + end + +/////////////////////////// +// Macro for Sparse FSMs // +/////////////////////////// + +// Simulation tools typically infer FSMs and report coverage for these separately. However, tools +// like Xcelium and VCS seem to have problems inferring FSMs if the state register is not coded in +// a behavioral always_ff block in the same hierarchy. To that end, this uses a modified variant +// with a second behavioral register definition for RTL simulations so that FSMs can be inferred. +// Note that in this variant, the __q output is disconnected from prim_sparse_fsm_flop and attached +// to the behavioral flop. An assertion is added to ensure equivalence between the +// prim_sparse_fsm_flop output and the behavioral flop output in that case. +`define PRIM_FLOP_SPARSE_FSM(__name, __d, __q, __type, __resval = `PRIM_FLOP_RESVAL, __clk = `PRIM_FLOP_CLK, __rst_n = `PRIM_FLOP_RST, __alert_trigger_sva_en = 1) \ + `ifdef SIMULATION \ + prim_sparse_fsm_flop #( \ + .StateEnumT(__type), \ + .Width($bits(__type)), \ + .ResetValue($bits(__type)'(__resval)), \ + .EnableAlertTriggerSVA(__alert_trigger_sva_en), \ + .CustomForceName(`PRIM_STRINGIFY(__q)) \ + ) __name ( \ + .clk_i ( __clk ), \ + .rst_ni ( __rst_n ), \ + .state_i ( __d ), \ + .state_o ( ) \ + ); \ + `PRIM_FLOP_A(__d, __q, __resval, __clk, __rst_n) \ + `ASSERT(``__name``_A, __q === ``__name``.state_o) \ + `else \ + prim_sparse_fsm_flop #( \ + .StateEnumT(__type), \ + .Width($bits(__type)), \ + .ResetValue($bits(__type)'(__resval)), \ + .EnableAlertTriggerSVA(__alert_trigger_sva_en) \ + ) __name ( \ + .clk_i ( __clk ), \ + .rst_ni ( __rst_n ), \ + .state_i ( __d ), \ + .state_o ( __q ) \ + ); \ + `endif + +`endif // PRIM_FLOP_MACROS_SV diff --git a/synth/prim/rtl/prim_gate_gen.sv b/synth/prim/rtl/prim_gate_gen.sv new file mode 100755 index 0000000..dac761f --- /dev/null +++ b/synth/prim/rtl/prim_gate_gen.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 +// +// 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: +// +// -------------+-----------+---------- +// requested GE | actual GE | GE error +// -------------+-----------+---------- +// 500 | 483 | -17 +// 1000 | 964 | -36 +// 1500 | 1447 | -53 +// 2500 | 2892 | 392 +// 5000 | 5299 | 299 +// 7500 | 8030 | 530 +// 10000 | 10393 | 393 +// 15000 | 15575 | 575 +// 25000 | 26422 | 1422 +// 50000 | 52859 | 2859 +// 100000 | 105270 | 5270 +// +// 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. +// +// If valid_i constantly set to 1'b1, the gate generator produces around 2.5% smaller designs for +// the configurations listed in the table above. + +`include "prim_assert.sv" +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 * 14; + // 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/synth/prim/rtl/prim_gf_mult.sv b/synth/prim/rtl/prim_gf_mult.sv new file mode 100755 index 0000000..aa47c22 --- /dev/null +++ b/synth/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 has bits 32, 15, 9, 7, 4, 3, 0 set to one. + parameter logic[Width-1:0] IPoly = Width'(1'b1) << 15 | + Width'(1'b1) << 9 | + Width'(1'b1) << 7 | + Width'(1'b1) << 4 | + Width'(1'b1) << 3 | + Width'(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 = int'(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 && int'(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/synth/prim/rtl/prim_intr_hw.sv b/synth/prim/rtl/prim_intr_hw.sv new file mode 100755 index 0000000..941b995 --- /dev/null +++ b/synth/prim/rtl/prim_intr_hw.sv @@ -0,0 +1,112 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Primitive block for generating CIP interrupts in peripherals. +// +// This block generates both the level-triggered wired interrupt signal intr_o and also updates +// the value of the INTR_STATE register. Together, the signal and register make up the two +// externally-visible indications of of the interrupt state. +// +// This assumes the existence of three external controller registers, which +// interface with this module via the standard reggen reg2hw/hw2reg signals. The 3 registers are: +// - INTR_ENABLE : enables/masks the output of INTR_STATE as the intr_o signal +// - INTR_STATE : the current state of the interrupt (may be RO or W1C depending on "IntrT") +// - INTR_TEST : sw-access-only register which asserts the interrupt for testing purposes + +module prim_intr_hw # ( + // This module can be instantiated once per interrupt field (Width == 1), or + // "bussified" with all fields of the interrupt vector (Width == $width(vec)). + parameter int unsigned Width = 1, + parameter bit FlopOutput = 1, + + // As the wired interrupt signal intr_o is a level-triggered interrupt, the upstream consumer sw + // has two options to make forward progress when this signal is asserted: + // - Mask the interrupt, by setting INTR_ENABLE = 1'b0 or masking/enabling at an upstream + // consumer. + // - Interact with the peripheral in some user-defined way that clears the signal. + // To make this user-defined interaction ergonomic from a SW-perspective, we have defined + // two common patterns for typical interrupt-triggering events, *Status* and *Event*. + // - *Event* is useful for capturing a momentary assertion of the input signal. + // - INTR_STATE/intr_o is set to '1 upon the event occurring. + // - INTR_STATE/intr_o remain set until software writes-1-to-clear to INTR_STATE. + // - *Status* captures a persistent conditional assertion that requires intervention to de-assert. + // - Until the root cause is alleviated, the interrupt output (while enabled) is continuously + // asserted. + // - INTR_STATE for *status* interrupts is RO (it simply presents the raw HW input signal). + // - If the root_cause is cleared, INTR_STATE/intr_o also clears automatically. + // More details about the interrupt type distinctions can be found in the comportability docs. + parameter IntrT = "Event" // Event or Status +) ( + // event + input clk_i, + input rst_ni, + 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 logic [Width-1:0] intr_o +); + + logic [Width-1:0] status; // incl. test + + if (IntrT == "Event") begin : g_intr_event + 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 status = reg2hw_intr_state_q_i ; + end : g_intr_event + else if (IntrT == "Status") begin : g_intr_status + logic [Width-1:0] test_q; // Storing test. Cleared by SW + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) test_q <= '0; + else if (reg2hw_intr_test_qe_i) test_q <= reg2hw_intr_test_q_i; + end + + // TODO: In Status type, INTR_STATE is better to be external type and RO. + assign hw2reg_intr_state_de_o = 1'b 1; // always represent the status + assign hw2reg_intr_state_d_o = event_intr_i | test_q; + + assign status = event_intr_i | test_q; + + // To make the timing same to event type, status signal does not use CSR.q, + // rather the input of the CSR. + logic unused_reg2hw; + assign unused_reg2hw = ^reg2hw_intr_state_q_i; + end : g_intr_status + + + if (FlopOutput == 1) begin : gen_flop_intr_output + // flop the interrupt output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + intr_o <= '0; + end else begin + intr_o <= status & reg2hw_intr_enable_q_i; + end + end + + end else begin : gen_intr_passthrough_output + logic unused_clk; + logic unused_rst_n; + assign unused_clk = clk_i; + assign unused_rst_n = rst_ni; + assign intr_o = reg2hw_intr_state_q_i & reg2hw_intr_enable_q_i; + end + + `ASSERT_INIT(IntrTKind_A, IntrT inside {"Event", "Status"}) + +endmodule diff --git a/synth/prim/rtl/prim_keccak.sv b/synth/prim/rtl/prim_keccak.sv new file mode 100755 index 0000000..16b3178 --- /dev/null +++ b/synth/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 +`include "prim_assert.sv" +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+1) // Representing up to MaxRound +) ( + 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/synth/prim/rtl/prim_lc_and_hardened.sv b/synth/prim/rtl/prim_lc_and_hardened.sv new file mode 100755 index 0000000..ae10f6a --- /dev/null +++ b/synth/prim/rtl/prim_lc_and_hardened.sv @@ -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 +// +// Hardened AND operation for life cycle values. To be used instead of lc_tx_and_*() if the output +// signal must always be rectified (i.e. either ActVal or ~ActVal)/ +// + +`include "prim_assert.sv" + +module prim_lc_and_hardened + import lc_ctrl_pkg::*; +#( + lc_tx_t ActVal = On +) ( + // Clock is only used for SVAs in this module. + input clk_i, + input rst_ni, + input lc_tx_t lc_en_a_i, + input lc_tx_t lc_en_b_i, + output lc_tx_t lc_en_o +); + // We perform strict comparisons and make sure we only output ActVal if both of the two inputs are + // strictly ActVal. The comparisons are done redundantly so that the multibit aspect is preserved + // throughout instead of creating a single point of failure due to the reduction. + lc_tx_t [TxWidth-1:0] lc_en_a_copies; + prim_lc_sync #( + .NumCopies(TxWidth), + .AsyncOn(0) // no sync needed + ) u_prim_lc_sync_a ( + .clk_i, + .rst_ni, + .lc_en_i(lc_en_a_i), + .lc_en_o(lc_en_a_copies) + ); + lc_tx_t [TxWidth-1:0] lc_en_b_copies; + prim_lc_sync #( + .NumCopies(TxWidth), + .AsyncOn(0) // no sync needed + ) u_prim_lc_sync_b ( + .clk_i, + .rst_ni, + .lc_en_i(lc_en_b_i), + .lc_en_o(lc_en_b_copies) + ); + + logic [TxWidth-1:0] lc_en_logic; + for (genvar k = 0; k < TxWidth; k++) begin : gen_hardened_or + assign lc_en_logic[k] = (lc_en_a_copies[k] == ActVal) && (lc_en_b_copies[k] == ActVal); + end + // So far all comparisons above produce the same value in lc_en_logic. + // X'oring with the inverse active value will flip the bits that need to be inverted. + assign lc_en_o = lc_tx_t'(lc_en_logic ^ lc_tx_inv(ActVal)); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, lc_en_o) + `ASSERT(FunctionCheck_A, ((lc_en_a_i == ActVal) && (lc_en_b_i == ActVal)) == (lc_en_o == ActVal)) + +endmodule : prim_lc_and_hardened diff --git a/synth/prim/rtl/prim_lc_combine.sv b/synth/prim/rtl/prim_lc_combine.sv new file mode 100755 index 0000000..2b3cce9 --- /dev/null +++ b/synth/prim/rtl/prim_lc_combine.sv @@ -0,0 +1,73 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Perform logical OR or AND between two life cycle multibit signals. + +module prim_lc_combine #( + // 0: use the ON value as active value for the logical combination + // 1: use the OFF value as active value for the logical combination + parameter bit ActiveLow = 0, + // 0: logical combination is an OR function + // 1: logical combination is an AND function + parameter bit CombineMode = 0 +) ( + input lc_ctrl_pkg::lc_tx_t lc_en_a_i, + input lc_ctrl_pkg::lc_tx_t lc_en_b_i, + output lc_ctrl_pkg::lc_tx_t lc_en_o +); + + // Determine whether which multibit value is considered "active" for the + // purpose of the logical function below. + localparam lc_ctrl_pkg::lc_tx_t ActiveValue = (ActiveLow) ? lc_ctrl_pkg::Off : lc_ctrl_pkg::On; + // Truth tables: + // + // ActiveLow: 0, CombineMode: 0 (active-high "OR") + // + // A | B | OUT + //------+------+----- + // !On | !On | !On + // On | !On | On + // !On | On | On + // On | On | On + // + // ActiveLow: 0, CombineMode: 1 (active-high "AND") + // + // A | B | OUT + //------+------+----- + // !On | !On | !On + // On | !On | !On + // !On | On | !On + // On | On | On + // + // ActiveLow: 1, CombineMode: 0 (active-low "OR") + // + // A | B | OUT + //------+------+----- + // !Off | !Off | !Off + // Off | !Off | Off + // !Off | Off | Off + // Off | Off | Off + // + // ActiveLow: 1, CombineMode: 1 (active-low "AND") + // + // A | B | OUT + //------+------+----- + // !Off | !Off | !Off + // Off | !Off | !Off + // !Off | Off | !Off + // Off | Off | Off + // + // Note: the inactive value (e.g. !On) can be any multibit value + // different from the active value. + // + for (genvar k = 0; k < $bits(ActiveValue); k++) begin : gen_loop + if (CombineMode && ActiveValue[k] || + (!CombineMode && !ActiveValue[k])) begin : gen_and_gate + assign lc_en_o[k] = lc_en_a_i[k] && lc_en_b_i[k]; + end else begin : gen_or_gate + assign lc_en_o[k] = lc_en_a_i[k] || lc_en_b_i[k]; + end + end + +endmodule : prim_lc_combine diff --git a/synth/prim/rtl/prim_lc_dec.sv b/synth/prim/rtl/prim_lc_dec.sv new file mode 100755 index 0000000..d10e0ce --- /dev/null +++ b/synth/prim/rtl/prim_lc_dec.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 +// +// Decoder for life cycle control signals with additional +// input buffers. + +module prim_lc_dec + import lc_ctrl_pkg::*; +( + input lc_tx_t lc_en_i, + output logic lc_en_dec_o +); + +logic [lc_ctrl_pkg::TxWidth-1:0] lc_en; +logic [lc_ctrl_pkg::TxWidth-1:0] lc_en_out; +assign lc_en = lc_en_i; + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < TxWidth; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i ( lc_en[k] ), + .out_o ( lc_en_out[k] ) + ); +end + +assign lc_en_dec_o = lc_tx_test_true_strict(lc_tx_t'(lc_en_out)); + +endmodule : prim_lc_dec diff --git a/synth/prim/rtl/prim_lc_or_hardened.sv b/synth/prim/rtl/prim_lc_or_hardened.sv new file mode 100755 index 0000000..a1c1df1 --- /dev/null +++ b/synth/prim/rtl/prim_lc_or_hardened.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 +// +// Hardened OR operation for life cycle values. To be used instead of lc_tx_or_*() if the output +// signal must be ActVal iff one of the two input signals is strictly ActVal. +// + +`include "prim_assert.sv" + +module prim_lc_or_hardened + import lc_ctrl_pkg::*; +#( + lc_tx_t ActVal = On +) ( + // Clock is only used for SVAs in this module. + input clk_i, + input rst_ni, + input lc_tx_t lc_en_a_i, + input lc_tx_t lc_en_b_i, + output lc_tx_t lc_en_o +); + + // For multibit or'ing we need to be careful if the resulting signal is consumed with + // lc_tx_test_*_strict(). I.e., due to the nature of the bitwise lc_tx_or_*() operation, two + // non-strictly ActVal values can produce a strictly ActVal value (this is not possible with the + // lc_tx_and_*() operation since there is only one strict ActVal output value in the truth table). + // + // To this end, we perform strict comparisons and make sure we only output ActVal if one of the + // two inputs is strictly ActVal. The comparisons are done redundantly so that the multibit + // aspect is preserved throughout instead of creating a single point of failure due to the + // reduction. + lc_tx_t [TxWidth-1:0] lc_en_a_copies; + prim_lc_sync #( + .NumCopies(TxWidth), + .AsyncOn(0) // no sync needed + ) u_prim_lc_sync_a ( + .clk_i, + .rst_ni, + .lc_en_i(lc_en_a_i), + .lc_en_o(lc_en_a_copies) + ); + lc_tx_t [TxWidth-1:0] lc_en_b_copies; + prim_lc_sync #( + .NumCopies(TxWidth), + .AsyncOn(0) // no sync needed + ) u_prim_lc_sync_b ( + .clk_i, + .rst_ni, + .lc_en_i(lc_en_b_i), + .lc_en_o(lc_en_b_copies) + ); + + logic [TxWidth-1:0] lc_en_logic; + for (genvar k = 0; k < TxWidth; k++) begin : gen_hardened_or + assign lc_en_logic[k] = (lc_en_a_copies[k] == ActVal) || (lc_en_b_copies[k] == ActVal); + end + // So far all comparisons above produce the same value in lc_en_logic. + // X'oring with the inverse active value will flip the bits that need to be inverted. + assign lc_en_o = lc_tx_t'(lc_en_logic ^ lc_tx_inv(ActVal)); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, lc_en_o) + `ASSERT(FunctionCheck_A, ((lc_en_a_i == ActVal) || (lc_en_b_i == ActVal)) == (lc_en_o == ActVal)) + +endmodule : prim_lc_or_hardened diff --git a/synth/prim/rtl/prim_lc_sender.sv b/synth/prim/rtl/prim_lc_sender.sv new file mode 100755 index 0000000..ae95a2c --- /dev/null +++ b/synth/prim/rtl/prim_lc_sender.sv @@ -0,0 +1,68 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Multibit life cycle signal sender module. +// +// This module is instantiates a hand-picked flop cell +// for each bit in the life cycle control signal such that tools do not +// optimize the multibit encoding. + +`include "prim_assert.sv" + +module prim_lc_sender #( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // 0: reset value is lc_ctrl_pkg::Off + // 1: reset value is lc_ctrl_pkg::On + parameter bit ResetValueIsOn = 0 +) ( + input clk_i, + input rst_ni, + input lc_ctrl_pkg::lc_tx_t lc_en_i, + output lc_ctrl_pkg::lc_tx_t lc_en_o +); + + localparam lc_ctrl_pkg::lc_tx_t ResetValue = (ResetValueIsOn) ? lc_ctrl_pkg::On : + lc_ctrl_pkg::Off; + + logic [lc_ctrl_pkg::TxWidth-1:0] lc_en, lc_en_out; + assign lc_en = lc_ctrl_pkg::TxWidth'(lc_en_i); + + if (AsyncOn) begin : gen_flops + prim_sec_anchor_flop #( + .Width(lc_ctrl_pkg::TxWidth), + .ResetValue(lc_ctrl_pkg::TxWidth'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( lc_en ), + .q_o ( lc_en_out ) + ); + end else begin : gen_no_flops + for (genvar k = 0; k < lc_ctrl_pkg::TxWidth; k++) begin : gen_bits + prim_sec_anchor_buf u_prim_buf ( + .in_i(lc_en[k]), + .out_o(lc_en_out[k]) + ); + end + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // or nothing at all. + // This logic will be removed for sythesis since it is unloaded. + lc_ctrl_pkg::lc_tx_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= lc_ctrl_pkg::Off; + end else begin + unused_logic <= lc_en_i; + end + end + end + + assign lc_en_o = lc_ctrl_pkg::lc_tx_t'(lc_en_out); + +endmodule : prim_lc_sender diff --git a/synth/prim/rtl/prim_lc_sync.sv b/synth/prim/rtl/prim_lc_sync.sv new file mode 100755 index 0000000..d1536c6 --- /dev/null +++ b/synth/prim/rtl/prim_lc_sync.sv @@ -0,0 +1,116 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Double-synchronizer flop for life cycle control signals with additional +// output buffers and life-cycle specific assertions. +// +// Should be used exactly as recommended in the life cycle controller spec: +// https://docs.opentitan.org/hw/ip/lc_ctrl/doc/index.html#control-signal-propagation + +`include "prim_assert.sv" + +module prim_lc_sync #( + // Number of separately buffered output signals. + // The buffer cells have a don't touch constraint + // on them such that synthesis tools won't collapse + // all copies into one signal. + parameter int NumCopies = 1, + // This instantiates the synchronizer flops if set to 1. + // In special cases where the receiver is in the same clock domain as the sender, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // 0: reset value is lc_ctrl_pkg::Off + // 1: reset value is lc_ctrl_pkg::On + parameter bit ResetValueIsOn = 0 +) ( + input clk_i, + input rst_ni, + input lc_ctrl_pkg::lc_tx_t lc_en_i, + output lc_ctrl_pkg::lc_tx_t [NumCopies-1:0] lc_en_o +); + + localparam lc_ctrl_pkg::lc_tx_t LcResetValue = (ResetValueIsOn) ? lc_ctrl_pkg::On : + lc_ctrl_pkg::Off; + + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) + + logic [lc_ctrl_pkg::TxWidth-1:0] lc_en; + if (AsyncOn) begin : gen_flops + prim_flop_2sync #( + .Width(lc_ctrl_pkg::TxWidth), + .ResetValue(lc_ctrl_pkg::TxWidth'(LcResetValue)) + ) u_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(lc_en_i), + .q_o(lc_en) + ); + +// Note regarding SVA below: +// +// 1) Without the sampled rst_ni pre-condition, this may cause false assertion failures right after +// a reset release, since the "disable iff" condition with the rst_ni is sampled in the "observed" +// SV scheduler region after all assignments have been evaluated (see also LRM section 16.12, page +// 423). This is a simulation artifact due to reset synchronization in RTL, which releases rst_ni +// on the active clock edge. This causes the assertion to evaluate although the reset was actually +// 0 when entering this simulation cycle. +// +// 2) Similarly to 1) there can be sampling mismatches of the lc_en_i signal since that signal may +// originate from a different clock domain. I.e., in cases where the lc_en_i signal changes exactly +// at the same time that the clk_i signal rises, the SVA will not pick up that change in that clock +// cycle, whereas RTL will because SVAs sample values in the "preponed" region. To that end we make +// use of an RTL helper variable to sample the lc_en_i signal, hence ensuring that there are no +// sampling mismatches. +`ifdef INC_ASSERT + lc_ctrl_pkg::lc_tx_t lc_en_in_sva_q; + always_ff @(posedge clk_i) begin + lc_en_in_sva_q <= lc_en_i; + end + `ASSERT(OutputDelay_A, + rst_ni |-> ##3 lc_en_o == {NumCopies{$past(lc_en_in_sva_q, 2)}} || + ($past(lc_en_in_sva_q, 2) != $past(lc_en_in_sva_q, 1))) +`endif + end else begin : gen_no_flops + //VCS coverage off + // pragma coverage off + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // or nothing at all. + // This logic will be removed for sythesis since it is unloaded. + lc_ctrl_pkg::lc_tx_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= lc_ctrl_pkg::Off; + end else begin + unused_logic <= lc_en_i; + end + end + //VCS coverage on + // pragma coverage on + + assign lc_en = lc_en_i; + + `ASSERT(OutputDelay_A, lc_en_o == {NumCopies{lc_en_i}}) + end + + for (genvar j = 0; j < NumCopies; j++) begin : gen_buffs + logic [lc_ctrl_pkg::TxWidth-1:0] lc_en_out; + for (genvar k = 0; k < lc_ctrl_pkg::TxWidth; k++) begin : gen_bits + prim_sec_anchor_buf u_prim_buf ( + .in_i(lc_en[k]), + .out_o(lc_en_out[k]) + ); + end + assign lc_en_o[j] = lc_ctrl_pkg::lc_tx_t'(lc_en_out); + end + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, lc_en_o) + +endmodule : prim_lc_sync diff --git a/synth/prim/rtl/prim_lfsr.sv b/synth/prim/rtl/prim_lfsr.sv new file mode 100755 index 0000000..d6d1244 --- /dev/null +++ b/synth/prim/rtl/prim_lfsr.sv @@ -0,0 +1,634 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This module implements different LFSR types: +// +// 0) Galois XOR type LFSR ([1], internal XOR gates, very fast). +// Parameterizable width from 3 to 168 bits. +// Coefficients obtained from [3]. +// +// 1) Fibonacci XNOR type LFSR, parameterizable from 3 to 168 bits. +// Coefficients obtained from [3]. +// +// All flavors have an additional entropy input and lockup protection, which +// reseeds the state once it has accidentally fallen into the all-zero (XOR) or +// all-one (XNOR) state. Further, an external seed can be loaded into the LFSR +// state at runtime. If that seed is all-zero (XOR case) or all-one (XNOR case), +// the state will be reseeded in the next cycle using the lockup protection mechanism. +// Note that the external seed input takes precedence over internal state updates. +// +// All polynomials up to 34 bit in length have been verified in simulation. +// +// Refs: [1] https://en.wikipedia.org/wiki/Linear-feedback_shift_register +// [2] https://users.ece.cmu.edu/~koopman/lfsr/ +// [3] https://www.xilinx.com/support/documentation/application_notes/xapp052.pdf + +`include "prim_assert.sv" + +module prim_lfsr #( + // Lfsr Type, can be FIB_XNOR or GAL_XOR + parameter LfsrType = "GAL_XOR", + // Lfsr width + parameter int unsigned LfsrDw = 32, + // Derived parameter, do not override + localparam int unsigned LfsrIdxDw = $clog2(LfsrDw), + // Width of the entropy input to be XOR'd into state (lfsr_q[EntropyDw-1:0]) + parameter int unsigned EntropyDw = 8, + // Width of output tap (from lfsr_q[StateOutDw-1:0]) + parameter int unsigned StateOutDw = 8, + // Lfsr reset state, must be nonzero! + parameter logic [LfsrDw-1:0] DefaultSeed = LfsrDw'(1), + // Custom polynomial coeffs + parameter logic [LfsrDw-1:0] CustomCoeffs = '0, + // If StatePermEn is set to 1, the custom permutation specified via StatePerm is applied to the + // state output, in order to break linear shifting patterns of the LFSR. Note that this + // permutation represents a way of customizing the LFSR via a random netlist constant. This is + // different from the NonLinearOut feature below which just transforms the output non-linearly + // with a fixed function. In most cases, designers should consider enabling StatePermEn as it + // comes basically "for free" in terms of area and timing impact. NonLinearOut on the other hand + // has area and timing implications and designers should consider whether the use of that feature + // is justified. + parameter bit StatePermEn = 1'b0, + parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0, + // Enable this for DV, disable this for long LFSRs in FPV + parameter bit MaxLenSVA = 1'b1, + // Can be disabled in cases where seed and entropy + // inputs are unused in order to not distort coverage + // (the SVA will be unreachable in such cases) + parameter bit LockupSVA = 1'b1, + parameter bit ExtSeedSVA = 1'b1, + // Introduce non-linearity to lfsr output. Note, unlike StatePermEn, this feature is not "for + // free". Please double check that this feature is indeed required. Also note that this feature + // is only available for LFSRs that have a power-of-two width greater or equal 16bit. + parameter bit NonLinearOut = 1'b0 +) ( + input clk_i, + input rst_ni, + input seed_en_i, // load external seed into the state (takes precedence) + input [LfsrDw-1:0] seed_i, // external seed input + input lfsr_en_i, // enables the LFSR + input [EntropyDw-1:0] entropy_i, // additional entropy to be XOR'ed into the state + output logic [StateOutDw-1:0] state_o // (partial) LFSR state output +); + + // automatically generated with util/design/get-lfsr-coeffs.py script + localparam int unsigned LUT_OFF = 3; + localparam logic [167:0] LFSR_COEFFS [166] = + '{ 168'h6, + 168'hC, + 168'h14, + 168'h30, + 168'h60, + 168'hB8, + 168'h110, + 168'h240, + 168'h500, + 168'h829, + 168'h100D, + 168'h2015, + 168'h6000, + 168'hD008, + 168'h12000, + 168'h20400, + 168'h40023, + 168'h90000, + 168'h140000, + 168'h300000, + 168'h420000, + 168'hE10000, + 168'h1200000, + 168'h2000023, + 168'h4000013, + 168'h9000000, + 168'h14000000, + 168'h20000029, + 168'h48000000, + 168'h80200003, + 168'h100080000, + 168'h204000003, + 168'h500000000, + 168'h801000000, + 168'h100000001F, + 168'h2000000031, + 168'h4400000000, + 168'hA000140000, + 168'h12000000000, + 168'h300000C0000, + 168'h63000000000, + 168'hC0000030000, + 168'h1B0000000000, + 168'h300003000000, + 168'h420000000000, + 168'hC00000180000, + 168'h1008000000000, + 168'h3000000C00000, + 168'h6000C00000000, + 168'h9000000000000, + 168'h18003000000000, + 168'h30000000030000, + 168'h40000040000000, + 168'hC0000600000000, + 168'h102000000000000, + 168'h200004000000000, + 168'h600003000000000, + 168'hC00000000000000, + 168'h1800300000000000, + 168'h3000000000000030, + 168'h6000000000000000, + 168'hD800000000000000, + 168'h10000400000000000, + 168'h30180000000000000, + 168'h60300000000000000, + 168'h80400000000000000, + 168'h140000028000000000, + 168'h300060000000000000, + 168'h410000000000000000, + 168'h820000000001040000, + 168'h1000000800000000000, + 168'h3000600000000000000, + 168'h6018000000000000000, + 168'hC000000018000000000, + 168'h18000000600000000000, + 168'h30000600000000000000, + 168'h40200000000000000000, + 168'hC0000000060000000000, + 168'h110000000000000000000, + 168'h240000000480000000000, + 168'h600000000003000000000, + 168'h800400000000000000000, + 168'h1800000300000000000000, + 168'h3003000000000000000000, + 168'h4002000000000000000000, + 168'hC000000000000000018000, + 168'h10000000004000000000000, + 168'h30000C00000000000000000, + 168'h600000000000000000000C0, + 168'hC00C0000000000000000000, + 168'h140000000000000000000000, + 168'h200001000000000000000000, + 168'h400800000000000000000000, + 168'hA00000000001400000000000, + 168'h1040000000000000000000000, + 168'h2004000000000000000000000, + 168'h5000000000028000000000000, + 168'h8000000004000000000000000, + 168'h18600000000000000000000000, + 168'h30000000000000000C00000000, + 168'h40200000000000000000000000, + 168'hC0300000000000000000000000, + 168'h100010000000000000000000000, + 168'h200040000000000000000000000, + 168'h5000000000000000A0000000000, + 168'h800000010000000000000000000, + 168'h1860000000000000000000000000, + 168'h3003000000000000000000000000, + 168'h4010000000000000000000000000, + 168'hA000000000140000000000000000, + 168'h10080000000000000000000000000, + 168'h30000000000000000000180000000, + 168'h60018000000000000000000000000, + 168'hC0000000000000000300000000000, + 168'h140005000000000000000000000000, + 168'h200000001000000000000000000000, + 168'h404000000000000000000000000000, + 168'h810000000000000000000000000102, + 168'h1000040000000000000000000000000, + 168'h3000000000000006000000000000000, + 168'h5000000000000000000000000000000, + 168'h8000000004000000000000000000000, + 168'h18000000000000000000000000030000, + 168'h30000000030000000000000000000000, + 168'h60000000000000000000000000000000, + 168'hA0000014000000000000000000000000, + 168'h108000000000000000000000000000000, + 168'h240000000000000000000000000000000, + 168'h600000000000C00000000000000000000, + 168'h800000040000000000000000000000000, + 168'h1800000000000300000000000000000000, + 168'h2000000000000010000000000000000000, + 168'h4008000000000000000000000000000000, + 168'hC000000000000000000000000000000600, + 168'h10000080000000000000000000000000000, + 168'h30600000000000000000000000000000000, + 168'h4A400000000000000000000000000000000, + 168'h80000004000000000000000000000000000, + 168'h180000003000000000000000000000000000, + 168'h200001000000000000000000000000000000, + 168'h600006000000000000000000000000000000, + 168'hC00000000000000006000000000000000000, + 168'h1000000000000100000000000000000000000, + 168'h3000000000000006000000000000000000000, + 168'h6000000003000000000000000000000000000, + 168'h8000001000000000000000000000000000000, + 168'h1800000000000000000000000000C000000000, + 168'h20000000000001000000000000000000000000, + 168'h48000000000000000000000000000000000000, + 168'hC0000000000000006000000000000000000000, + 168'h180000000000000000000000000000000000000, + 168'h280000000000000000000000000000005000000, + 168'h60000000C000000000000000000000000000000, + 168'hC00000000000000000000000000018000000000, + 168'h1800000600000000000000000000000000000000, + 168'h3000000C00000000000000000000000000000000, + 168'h4000000080000000000000000000000000000000, + 168'hC000300000000000000000000000000000000000, + 168'h10000400000000000000000000000000000000000, + 168'h30000000000000000000006000000000000000000, + 168'h600000000000000C0000000000000000000000000, + 168'hC0060000000000000000000000000000000000000, + 168'h180000006000000000000000000000000000000000, + 168'h3000000000C0000000000000000000000000000000, + 168'h410000000000000000000000000000000000000000, + 168'hA00140000000000000000000000000000000000000 }; + + logic lockup; + logic [LfsrDw-1:0] lfsr_d, lfsr_q; + logic [LfsrDw-1:0] next_lfsr_state, coeffs; + + // Enable the randomization of DefaultSeed using DefaultSeedLocal in DV simulations. + `ifdef SIMULATION + `ifdef VERILATOR + localparam logic [LfsrDw-1:0] DefaultSeedLocal = DefaultSeed; + + `else + logic [LfsrDw-1:0] DefaultSeedLocal; + logic prim_lfsr_use_default_seed; + + initial begin : p_randomize_default_seed + if (!$value$plusargs("prim_lfsr_use_default_seed=%0d", prim_lfsr_use_default_seed)) begin + // 30% of the time, use the DefaultSeed parameter; 70% of the time, randomize it. + `ASSERT_I(UseDefaultSeedRandomizeCheck_A, std::randomize(prim_lfsr_use_default_seed) with { + prim_lfsr_use_default_seed dist {0:/7, 1:/3};}) + end + if (prim_lfsr_use_default_seed) begin + DefaultSeedLocal = DefaultSeed; + end else begin + // Randomize the DefaultSeedLocal ensuring its not all 0s or all 1s. + `ASSERT_I(DefaultSeedLocalRandomizeCheck_A, std::randomize(DefaultSeedLocal) with { + !(DefaultSeedLocal inside {'0, '1});}) + end + $display("%m: DefaultSeed = 0x%0h, DefaultSeedLocal = 0x%0h", DefaultSeed, DefaultSeedLocal); + end + `endif // ifdef VERILATOR + + `else + localparam logic [LfsrDw-1:0] DefaultSeedLocal = DefaultSeed; + + `endif // ifdef SIMULATION + + //////////////// + // Galois XOR // + //////////////// + if (64'(LfsrType) == 64'("GAL_XOR")) begin : gen_gal_xor + + // if custom polynomial is provided + if (CustomCoeffs > 0) begin : gen_custom + assign coeffs = CustomCoeffs[LfsrDw-1:0]; + end else begin : gen_lut + assign coeffs = LFSR_COEFFS[LfsrDw-LUT_OFF][LfsrDw-1:0]; + // check that the most significant bit of polynomial is 1 + `ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(LFSR_COEFFS)+LUT_OFF) + `ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(LFSR_COEFFS)+LUT_OFF) + end + + // calculate next state using internal XOR feedback and entropy input + assign next_lfsr_state = LfsrDw'(entropy_i) ^ ({LfsrDw{lfsr_q[0]}} & coeffs) ^ (lfsr_q >> 1); + + // lockup condition is all-zero + assign lockup = ~(|lfsr_q); + + // check that seed is not all-zero + `ASSERT_INIT(DefaultSeedNzCheck_A, |DefaultSeedLocal) + + + //////////////////// + // Fibonacci XNOR // + //////////////////// + end else if (64'(LfsrType) == "FIB_XNOR") begin : gen_fib_xnor + + // if custom polynomial is provided + if (CustomCoeffs > 0) begin : gen_custom + assign coeffs = CustomCoeffs[LfsrDw-1:0]; + end else begin : gen_lut + assign coeffs = LFSR_COEFFS[LfsrDw-LUT_OFF][LfsrDw-1:0]; + // check that the most significant bit of polynomial is 1 + `ASSERT_INIT(MinLfsrWidth_A, LfsrDw >= $low(LFSR_COEFFS)+LUT_OFF) + `ASSERT_INIT(MaxLfsrWidth_A, LfsrDw <= $high(LFSR_COEFFS)+LUT_OFF) + end + + // calculate next state using external XNOR feedback and entropy input + assign next_lfsr_state = LfsrDw'(entropy_i) ^ {lfsr_q[LfsrDw-2:0], ~(^(lfsr_q & coeffs))}; + + // lockup condition is all-ones + assign lockup = &lfsr_q; + + // check that seed is not all-ones + `ASSERT_INIT(DefaultSeedNzCheck_A, !(&DefaultSeedLocal)) + + + ///////////// + // Unknown // + ///////////// + end else begin : gen_unknown_type + assign coeffs = '0; + assign next_lfsr_state = '0; + assign lockup = 1'b0; + `ASSERT_INIT(UnknownLfsrType_A, 0) + end + + + ////////////////// + // Shared logic // + ////////////////// + + assign lfsr_d = (seed_en_i) ? seed_i : + (lfsr_en_i && lockup) ? DefaultSeedLocal : + (lfsr_en_i) ? next_lfsr_state : + lfsr_q; + + logic [LfsrDw-1:0] sbox_out; + if (NonLinearOut) begin : gen_out_non_linear + // The "aligned" permutation ensures that adjacent bits do not go into the same SBox. It is + // different from the state permutation that can be specified via the StatePerm parameter. The + // permutation taps out 4 SBox input bits at regular stride intervals. E.g., for a 16bit + // vector, the input assignment looks as follows: + // + // SBox0: 0, 4, 8, 12 + // SBox1: 1, 5, 9, 13 + // SBox2: 2, 6, 10, 14 + // SBox3: 3, 7, 11, 15 + // + // Note that this permutation can be produced by filling the input vector into matrix columns + // and reading out the SBox inputs as matrix rows. + localparam int NumSboxes = LfsrDw / 4; + // Fill in the input vector in col-major order. + logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_indices; + for (genvar j = 0; j < LfsrDw; j++) begin : gen_input_idx_map + assign matrix_indices[j / NumSboxes][j % NumSboxes] = j; + end + // Due to the LFSR shifting pattern, the above permutation has the property that the output of + // SBox(n) is going to be equal to SBox(n+1) in the subsequent cycle (unless the LFSR polynomial + // modifies some of the associated shifted bits via an XOR tap). + // We therefore tweak this permutation by rotating and reversing some of the assignment matrix + // columns. The rotation and reversion operations have been chosen such that this + // generalizes to all power of two widths supported by the LFSR primitive. For 16bit, this + // looks as follows: + // + // SBox0: 0, 6, 11, 14 + // SBox1: 1, 7, 10, 13 + // SBox2: 2, 4, 9, 12 + // SBox3: 3, 5, 8, 15 + // + // This can be achieved by: + // 1) down rotating the second column by NumSboxes/2 + // 2) reversing the third column + // 3) down rotating the fourth column by 1 and reversing it + // + logic [3:0][NumSboxes-1:0][LfsrIdxDw-1:0] matrix_rotrev_indices; + typedef logic [NumSboxes-1:0][LfsrIdxDw-1:0] matrix_col_t; + + // left-rotates a matrix column by the shift amount + function automatic matrix_col_t lrotcol(matrix_col_t col, integer shift); + matrix_col_t out; + for (int k = 0; k < NumSboxes; k++) begin + out[(k + shift) % NumSboxes] = col[k]; + end + return out; + endfunction : lrotcol + + // reverses a matrix column + function automatic matrix_col_t revcol(matrix_col_t col); + return {< StateOutDw) begin : gen_tieoff_unused + logic unused_sbox_out; + assign unused_sbox_out = ^sbox_out; + end + + end else begin : gen_no_state_perm + assign state_o = StateOutDw'(sbox_out); + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg + if (!rst_ni) begin + lfsr_q <= DefaultSeedLocal; + end else begin + lfsr_q <= lfsr_d; + end + end + + + /////////////////////// + // shared assertions // + /////////////////////// + + `ASSERT_KNOWN(DataKnownO_A, state_o) + +// the code below is not meant to be synthesized, +// but it is intended to be used in simulation and FPV +`ifndef SYNTHESIS + function automatic logic [LfsrDw-1:0] compute_next_state(logic [LfsrDw-1:0] lfsrcoeffs, + logic [EntropyDw-1:0] entropy, + logic [LfsrDw-1:0] current_state); + logic state0; + logic [LfsrDw-1:0] next_state; + + next_state = current_state; + + // Galois XOR + if (64'(LfsrType) == 64'("GAL_XOR")) begin + if (next_state == 0) begin + next_state = DefaultSeedLocal; + end else begin + state0 = next_state[0]; + next_state = next_state >> 1; + if (state0) next_state ^= lfsrcoeffs; + next_state ^= LfsrDw'(entropy); + end + // Fibonacci XNOR + end else if (64'(LfsrType) == "FIB_XNOR") begin + if (&next_state) begin + next_state = DefaultSeedLocal; + end else begin + state0 = ~(^(next_state & lfsrcoeffs)); + next_state = next_state << 1; + next_state[0] = state0; + next_state ^= LfsrDw'(entropy); + end + end else begin + $error("unknown lfsr type"); + end + + return next_state; + endfunction : compute_next_state + + // check whether next state is computed correctly + // we shift the assertion by one clock cycle (##1) in order to avoid + // erroneous SVA triggers right after reset deassertion in cases where + // the precondition is true throughout the reset. + // this can happen since the disable_iff evaluates using unsampled values, + // meaning that the assertion may already read rst_ni == 1 on an active + // clock edge while the flops in the design have not yet changed state. + `ASSERT(NextStateCheck_A, ##1 lfsr_en_i && !seed_en_i |=> lfsr_q == + compute_next_state(coeffs, $past(entropy_i), $past(lfsr_q))) + + // Only check this if enabled. + if (StatePermEn) begin : gen_perm_check + // Check that the supplied permutation is valid. + logic [LfsrDw-1:0] lfsr_perm_test; + initial begin : p_perm_check + lfsr_perm_test = '0; + for (int k = 0; k < LfsrDw; k++) begin + lfsr_perm_test[StatePerm[k]] = 1'b1; + end + // All bit positions must be marked with 1. + `ASSERT_I(PermutationCheck_A, &lfsr_perm_test) + end + end + +`endif + + `ASSERT_INIT(InputWidth_A, LfsrDw >= EntropyDw) + `ASSERT_INIT(OutputWidth_A, LfsrDw >= StateOutDw) + + // MSB must be one in any case + `ASSERT(CoeffCheck_A, coeffs[LfsrDw-1]) + + // output check + `ASSERT_KNOWN(OutputKnown_A, state_o) + if (!StatePermEn && !NonLinearOut) begin : gen_output_sva + `ASSERT(OutputCheck_A, state_o == StateOutDw'(lfsr_q)) + end + // if no external input changes the lfsr state, a lockup must not occur (by design) + //`ASSERT(NoLockups_A, (!entropy_i) && (!seed_en_i) |=> !lockup, clk_i, !rst_ni) + `ASSERT(NoLockups_A, lfsr_en_i && !entropy_i && !seed_en_i |=> !lockup) + + // 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 + // 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, + // there is theoretically no way we end up in a lockup condition + // in order to not distort coverage, this SVA can be disabled in such cases + if (LockupSVA) begin : gen_lockup_mechanism_sva + // check that a stuck LFSR is correctly reseeded + `ASSERT(LfsrLockupCheck_A, lfsr_en_i && lockup && !seed_en_i |=> !lockup) + end + + // If non-linear output requested, the LFSR width must be a power of 2 and greater than 16. + if(NonLinearOut) begin : gen_nonlinear_align_check_sva + `ASSERT_INIT(SboxByteAlign_A, 2**$clog2(LfsrDw) == LfsrDw && LfsrDw >= 16) + end + + if (MaxLenSVA) begin : gen_max_len_sva +`ifndef SYNTHESIS + // the code below is a workaround to enable long sequences to be checked. + // some simulators do not support SVA sequences longer than 2**32-1. + logic [LfsrDw-1:0] cnt_d, cnt_q; + logic perturbed_d, perturbed_q; + logic [LfsrDw-1:0] cmp_val; + + assign cmp_val = {{(LfsrDw-1){1'b1}}, 1'b0}; // 2**LfsrDw-2 + assign cnt_d = (lfsr_en_i && lockup) ? '0 : + (lfsr_en_i && (cnt_q == cmp_val)) ? '0 : + (lfsr_en_i) ? cnt_q + 1'b1 : + cnt_q; + + assign perturbed_d = perturbed_q | (|entropy_i) | seed_en_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_max_len + if (!rst_ni) begin + cnt_q <= '0; + perturbed_q <= 1'b0; + end else begin + cnt_q <= cnt_d; + perturbed_q <= perturbed_d; + end + end + + `ASSERT(MaximalLengthCheck0_A, cnt_q == 0 |-> lfsr_q == DefaultSeedLocal, + clk_i, !rst_ni || perturbed_q) + `ASSERT(MaximalLengthCheck1_A, cnt_q != 0 |-> lfsr_q != DefaultSeedLocal, + clk_i, !rst_ni || perturbed_q) +`endif + end + +endmodule diff --git a/synth/prim/rtl/prim_macros.svh b/synth/prim/rtl/prim_macros.svh new file mode 100755 index 0000000..5561f14 --- /dev/null +++ b/synth/prim/rtl/prim_macros.svh @@ -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 + +// Some generally useful macros for RTL. + +// Determine if __actual equals __expected with a margin of __allowed_less and __allowed_more, i.e., +// if __actual is in the interval [__expected - __allowed_less, __expected + __allowed_more], where +// lower and upper bounds are inclusive. +// +// The caller is responsible for ensuring that the data types are such that +// (1) __actual + __allowed_less +// (2) __expected + __allowed_more +// are well defined and do not overflow and +// (3) (1) >= __expected +// (4) __actual <= (2) +// are well defined and meaningful. Subtractions are deliberately not used, in order to prevent +// underflows. +`define WITHIN_MARGIN(__actual, __expected, __allowed_less, __allowed_more) \ + (((__actual) + (__allowed_less) >= (__expected)) && \ + ((__actual) <= (__expected) + (__allowed_more))) + +// Coverage pragmas, used around code for which we want to disable coverage collection. +// Don't forget to add a closing ON pragma after the code to be skipped. +// +// Some notes: +// - The first line is for VCS, the second for xcelium. It is okay to issue both regardless of +// the tool used. +// - For xcelium it is possible to discriminate between metrics to be disabled as follows +// //pragma coverage = on/off +// where metric can be block | expr | toggle | fsm. + +// TODO(https://github.com/chipsalliance/verible/issues/1498) Verible seems to get confused +// by these macros, so the code will inline these directives until this is fixed. +/* +`ifndef PRAGMA_COVERAGE_OFF +`define PRAGMA_COVERAGE_OFF \ +/``/VCS coverage off \ +/``/ pragma coverage off +`endif + +`ifndef PRAGMA_COVERAGE_ON +`define PRAGMA_COVERAGE_ON \ +/``/VCS coverage on \ +/``/ pragma coverage on +`endif +*/ diff --git a/synth/prim/rtl/prim_max_tree.sv b/synth/prim/rtl/prim_max_tree.sv new file mode 100755 index 0000000..e23a76f --- /dev/null +++ b/synth/prim/rtl/prim_max_tree.sv @@ -0,0 +1,152 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// The module implements a binary tree to find the maximal entry. the solution +// has O(N) area and O(log(N)) delay complexity, and thus scales well with +// many input sources. +// +// Note that only input values marked as "valid" are respected in the maximum computation. +// If there are multiple valid inputs with the same value, the tree will always select the input +// with the smallest index. +// +// If none of the input values are valid, the output index will be 0 and the output value will +// be equal to the input value at index 0. + + +`include "prim_assert.sv" + +module prim_max_tree #( + parameter int NumSrc = 32, + parameter int Width = 8, + // Derived parameters + localparam int SrcWidth = $clog2(NumSrc) +) ( + // The module is combinational - the clock and reset are only used for assertions. + input clk_i, + input rst_ni, + input [NumSrc-1:0][Width-1:0] values_i, // Input values + input [NumSrc-1:0] valid_i, // Input valid bits + output logic [Width-1:0] max_value_o, // Maximum value + output logic [SrcWidth-1:0] max_idx_o, // Index of the maximum value + output logic max_valid_o // Whether any of the inputs is valid +); + + /////////////////////// + // Binary tree logic // + /////////////////////// + + // This only works with 2 or more sources. + `ASSERT_INIT(NumSources_A, NumSrc >= 2) + + // Align to powers of 2 for simplicity. + // A full binary tree with N levels has 2**N + 2**N-1 nodes. + localparam int NumLevels = $clog2(NumSrc); + logic [2**(NumLevels+1)-2:0] vld_tree; + logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] idx_tree; + logic [2**(NumLevels+1)-2:0][Width-1:0] max_tree; + + 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 paPa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // This assigns the input values, their corresponding IDs and valid signals to the tree leafs. + if (level == NumLevels) begin : gen_leafs + if (offset < NumSrc) begin : gen_assign + assign vld_tree[Pa] = valid_i[offset]; + assign idx_tree[Pa] = offset; + assign max_tree[Pa] = values_i[offset]; + end else begin : gen_tie_off + assign vld_tree[Pa] = '0; + assign idx_tree[Pa] = '0; + assign max_tree[Pa] = '0; + end + // This creates the node assignments. + end else begin : gen_nodes + logic sel; // Local helper variable + // In case only one of the parents is valid, forward that one + // In case both parents are valid, forward the one with higher value + assign sel = (~vld_tree[C0] & vld_tree[C1]) | + (vld_tree[C0] & vld_tree[C1] & logic'(max_tree[C1] > max_tree[C0])); + // Forwarding muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. + assign vld_tree[Pa] = (sel) ? vld_tree[C1] : vld_tree[C0]; + assign idx_tree[Pa] = (sel) ? idx_tree[C1] : idx_tree[C0]; + assign max_tree[Pa] = (sel) ? max_tree[C1] : max_tree[C0]; + end + end : gen_level + end : gen_tree + + + // The results can be found at the tree root + assign max_valid_o = vld_tree[0]; + assign max_idx_o = idx_tree[0]; + assign max_value_o = max_tree[0]; + + //////////////// + // Assertions // + //////////////// + +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + // Helper functions for assertions below. + function automatic logic [Width-1:0] max_value (input logic [NumSrc-1:0][Width-1:0] values_i, + input logic [NumSrc-1:0] valid_i); + logic [Width-1:0] value = '0; + for (int k = 0; k < NumSrc; k++) begin + if (valid_i[k] && values_i[k] > value) begin + value = values_i[k]; + end + end + return value; + endfunction : max_value + + function automatic logic [SrcWidth-1:0] max_idx (input logic [NumSrc-1:0][Width-1:0] values_i, + input logic [NumSrc-1:0] valid_i); + logic [Width-1:0] value = '0; + logic [SrcWidth-1:0] idx = '0; + for (int k = NumSrc-1; k >= 0; k--) begin + if (valid_i[k] && values_i[k] >= value) begin + value = values_i[k]; + idx = k; + end + end + return idx; + endfunction : max_idx + + logic [Width-1:0] max_value_exp; + logic [SrcWidth-1:0] max_idx_exp; + assign max_value_exp = max_value(values_i, valid_i); + assign max_idx_exp = max_idx(values_i, valid_i); + //VCS coverage on + // pragma coverage on + + // TODO(10588): Below syntax is not supported in xcelium, track xcelium cases #46591452. + // `ASSERT(ValidInImpliesValidOut_A, |valid_i <-> max_valid_o) + `ASSERT(ValidInImpliesValidOut_A, |valid_i === max_valid_o) + `ASSERT(MaxComputation_A, max_valid_o |-> max_value_o == max_value_exp) + `ASSERT(MaxComputationInvalid_A, !max_valid_o |-> max_value_o == values_i[0]) + `ASSERT(MaxIndexComputation_A, max_valid_o |-> max_idx_o == max_idx_exp) + `ASSERT(MaxIndexComputationInvalid_A, !max_valid_o |-> max_idx_o == '0) +`endif + +endmodule : prim_max_tree diff --git a/synth/prim/rtl/prim_msb_extend.sv b/synth/prim/rtl/prim_msb_extend.sv new file mode 100755 index 0000000..9b37c64 --- /dev/null +++ b/synth/prim/rtl/prim_msb_extend.sv @@ -0,0 +1,27 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Extend the output with the msb of the input + +`include "prim_assert.sv" + +module prim_msb_extend # ( + parameter int InWidth = 2, + parameter int OutWidth = 2 +) ( + input [InWidth-1:0] in_i, + output [OutWidth-1:0] out_o +); + + `ASSERT_INIT(WidthCheck_A, OutWidth >= InWidth) + + localparam int WidthDiff = OutWidth - InWidth; + + if (WidthDiff == 0) begin : gen_feedthru + assign out_o = in_i; + end else begin : gen_tieoff + assign out_o = {{WidthDiff{in_i[InWidth-1]}}, in_i}; + end + +endmodule diff --git a/synth/prim/rtl/prim_mubi12_dec.sv b/synth/prim/rtl/prim_mubi12_dec.sv new file mode 100755 index 0000000..1bc8503 --- /dev/null +++ b/synth/prim/rtl/prim_mubi12_dec.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 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Decoder for multibit control signals with additional input buffers. + +`include "prim_assert.sv" + +module prim_mubi12_dec + import prim_mubi_pkg::*; +#( + parameter bit TestTrue = 1, + parameter bit TestStrict = 1 +) ( + input mubi12_t mubi_i, + output logic mubi_dec_o +); + +logic [MuBi12Width-1:0] mubi, mubi_out; +assign mubi = MuBi12Width'(mubi_i); + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < MuBi12Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i ( mubi[k] ), + .out_o ( mubi_out[k] ) + ); +end + +if (TestTrue && TestStrict) begin : gen_test_true_strict + assign mubi_dec_o = mubi12_test_true_strict(mubi12_t'(mubi_out)); +end else if (TestTrue && !TestStrict) begin : gen_test_true_loose + assign mubi_dec_o = mubi12_test_true_loose(mubi12_t'(mubi_out)); +end else if (!TestTrue && TestStrict) begin : gen_test_false_strict + assign mubi_dec_o = mubi12_test_false_strict(mubi12_t'(mubi_out)); +end else if (!TestTrue && !TestStrict) begin : gen_test_false_loose + assign mubi_dec_o = mubi12_test_false_loose(mubi12_t'(mubi_out)); +end else begin : gen_unknown_config + `ASSERT_INIT(UnknownConfig_A, 0) +end + +endmodule : prim_mubi12_dec diff --git a/synth/prim/rtl/prim_mubi12_sender.sv b/synth/prim/rtl/prim_mubi12_sender.sv new file mode 100755 index 0000000..230efdc --- /dev/null +++ b/synth/prim/rtl/prim_mubi12_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "prim_assert.sv" + +module prim_mubi12_sender + import prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi12_t ResetValue = MuBi12False +) ( + input clk_i, + input rst_ni, + input mubi12_t mubi_i, + output mubi12_t mubi_o +); + + logic [MuBi12Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi12Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + prim_flop #( + .Width(MuBi12Width), + .ResetValue(MuBi12Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi12_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi12False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + prim_sec_anchor_buf #( + .Width(12) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + prim_buf #( + .Width(12) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi12_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi12_sender diff --git a/synth/prim/rtl/prim_mubi12_sync.sv b/synth/prim/rtl/prim_mubi12_sync.sv new file mode 100755 index 0000000..30ee2d6 --- /dev/null +++ b/synth/prim/rtl/prim_mubi12_sync.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Double-synchronizer flop for multibit signals with additional output buffers. + +`include "prim_assert.sv" + +module prim_mubi12_sync + import prim_mubi_pkg::*; +#( + // Number of separately buffered output signals. + // The buffer cells have a don't touch constraint + // on them such that synthesis tools won't collapse + // all copies into one signal. + parameter int NumCopies = 1, + // This instantiates the synchronizer flops if set to 1. + // In special cases where the receiver is in the same clock domain as the sender, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // This controls whether the mubi module institutes stability checks when + // AsyncOn is set. If stability checks are on, a 3rd stage of storage is + // added after the synchronizers and the outputs only updated if the 3rd + // stage and sychronizer agree. If they do not agree, the ResetValue is + // output instead. + parameter bit StabilityCheck = 0, + // Reset value for the sync flops + parameter mubi12_t ResetValue = MuBi12False +) ( + input clk_i, + input rst_ni, + input mubi12_t mubi_i, + output mubi12_t [NumCopies-1:0] mubi_o +); + + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) + + logic [MuBi12Width-1:0] mubi; + if (AsyncOn) begin : gen_flops + logic [MuBi12Width-1:0] mubi_sync; + prim_flop_2sync #( + .Width(MuBi12Width), + .ResetValue(MuBi12Width'(ResetValue)) + ) u_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(MuBi12Width'(mubi_i)), + .q_o(mubi_sync) + ); + + if (StabilityCheck) begin : gen_stable_chks + logic [MuBi12Width-1:0] mubi_q; + prim_flop #( + .Width(MuBi12Width), + .ResetValue(MuBi12Width'(ResetValue)) + ) u_prim_flop_3rd_stage ( + .clk_i, + .rst_ni, + .d_i(mubi_sync), + .q_o(mubi_q) + ); + + logic [MuBi12Width-1:0] sig_unstable; + prim_xor2 #( + .Width(MuBi12Width) + ) u_mubi_xor ( + .in0_i(mubi_sync), + .in1_i(mubi_q), + .out_o(sig_unstable) + ); + + logic [MuBi12Width-1:0] reset_value; + assign reset_value = ResetValue; + + for (genvar k = 0; k < MuBi12Width; k++) begin : gen_bufs_muxes + logic [MuBi12Width-1:0] sig_unstable_buf; + + // each mux gets its own buffered output, this ensures the OR-ing + // cannot be defeated in one place. + prim_sec_anchor_buf #( + .Width(MuBi12Width) + ) u_sig_unstable_buf ( + .in_i(sig_unstable), + .out_o(sig_unstable_buf) + ); + + // if any xor indicates signal is unstable, output the reset + // value. note that the input and output signals of this mux + // are driven/read by constrained primitive cells (regs, buffers), + // hence this mux can be implemented behaviorally. + assign mubi[k] = (|sig_unstable_buf) ? reset_value[k] : mubi_q[k]; + end + +// Note regarding SVAs below: +// +// 1) Without the sampled rst_ni pre-condition, this may cause false assertion failures right after +// a reset release, since the "disable iff" condition with the rst_ni is sampled in the "observed" +// SV scheduler region after all assignments have been evaluated (see also LRM section 16.12, page +// 423). This is a simulation artifact due to reset synchronization in RTL, which releases rst_ni +// on the active clock edge. This causes the assertion to evaluate although the reset was actually +// 0 when entering this simulation cycle. +// +// 2) Similarly to 1) there can be sampling mismatches of the lc_en_i signal since that signal may +// originate from a different clock domain. I.e., in cases where the lc_en_i signal changes exactly +// at the same time that the clk_i signal rises, the SVA will not pick up that change in that clock +// cycle, whereas RTL will because SVAs sample values in the "preponed" region. To that end we make +// use of an RTL helper variable to sample the lc_en_i signal, hence ensuring that there are no +// sampling mismatches. +`ifdef INC_ASSERT + mubi12_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputIfUnstable_A, sig_unstable |-> mubi_o == {NumCopies{reset_value}}) + `ASSERT(OutputDelay_A, + rst_ni |-> ##[3:4] sig_unstable || mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}}) +`endif + end else begin : gen_no_stable_chks + assign mubi = mubi_sync; +`ifdef INC_ASSERT + mubi12_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputDelay_A, + rst_ni |-> ##3 (mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}} || + $past(mubi_in_sva_q, 2) != $past(mubi_in_sva_q, 1))) +`endif + end + end else begin : gen_no_flops + + //VCS coverage off + // pragma coverage off + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for synthesis since it is unloaded. + mubi12_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi12False; + end else begin + unused_logic <= mubi_i; + end + end + + //VCS coverage on + // pragma coverage on + + assign mubi = MuBi12Width'(mubi_i); + + `ASSERT(OutputDelay_A, mubi_o == {NumCopies{mubi_i}}) + end + + for (genvar j = 0; j < NumCopies; j++) begin : gen_buffs + logic [MuBi12Width-1:0] mubi_out; + for (genvar k = 0; k < MuBi12Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i(mubi[k]), + .out_o(mubi_out[k]) + ); + end + assign mubi_o[j] = mubi12_t'(mubi_out); + end + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi12_sync diff --git a/synth/prim/rtl/prim_mubi16_dec.sv b/synth/prim/rtl/prim_mubi16_dec.sv new file mode 100755 index 0000000..1741147 --- /dev/null +++ b/synth/prim/rtl/prim_mubi16_dec.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 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Decoder for multibit control signals with additional input buffers. + +`include "prim_assert.sv" + +module prim_mubi16_dec + import prim_mubi_pkg::*; +#( + parameter bit TestTrue = 1, + parameter bit TestStrict = 1 +) ( + input mubi16_t mubi_i, + output logic mubi_dec_o +); + +logic [MuBi16Width-1:0] mubi, mubi_out; +assign mubi = MuBi16Width'(mubi_i); + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < MuBi16Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i ( mubi[k] ), + .out_o ( mubi_out[k] ) + ); +end + +if (TestTrue && TestStrict) begin : gen_test_true_strict + assign mubi_dec_o = mubi16_test_true_strict(mubi16_t'(mubi_out)); +end else if (TestTrue && !TestStrict) begin : gen_test_true_loose + assign mubi_dec_o = mubi16_test_true_loose(mubi16_t'(mubi_out)); +end else if (!TestTrue && TestStrict) begin : gen_test_false_strict + assign mubi_dec_o = mubi16_test_false_strict(mubi16_t'(mubi_out)); +end else if (!TestTrue && !TestStrict) begin : gen_test_false_loose + assign mubi_dec_o = mubi16_test_false_loose(mubi16_t'(mubi_out)); +end else begin : gen_unknown_config + `ASSERT_INIT(UnknownConfig_A, 0) +end + +endmodule : prim_mubi16_dec diff --git a/synth/prim/rtl/prim_mubi16_sender.sv b/synth/prim/rtl/prim_mubi16_sender.sv new file mode 100755 index 0000000..e12b99d --- /dev/null +++ b/synth/prim/rtl/prim_mubi16_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "prim_assert.sv" + +module prim_mubi16_sender + import prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi16_t ResetValue = MuBi16False +) ( + input clk_i, + input rst_ni, + input mubi16_t mubi_i, + output mubi16_t mubi_o +); + + logic [MuBi16Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi16Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + prim_flop #( + .Width(MuBi16Width), + .ResetValue(MuBi16Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi16_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi16False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + prim_sec_anchor_buf #( + .Width(16) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + prim_buf #( + .Width(16) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi16_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi16_sender diff --git a/synth/prim/rtl/prim_mubi16_sync.sv b/synth/prim/rtl/prim_mubi16_sync.sv new file mode 100755 index 0000000..02be5a0 --- /dev/null +++ b/synth/prim/rtl/prim_mubi16_sync.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Double-synchronizer flop for multibit signals with additional output buffers. + +`include "prim_assert.sv" + +module prim_mubi16_sync + import prim_mubi_pkg::*; +#( + // Number of separately buffered output signals. + // The buffer cells have a don't touch constraint + // on them such that synthesis tools won't collapse + // all copies into one signal. + parameter int NumCopies = 1, + // This instantiates the synchronizer flops if set to 1. + // In special cases where the receiver is in the same clock domain as the sender, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // This controls whether the mubi module institutes stability checks when + // AsyncOn is set. If stability checks are on, a 3rd stage of storage is + // added after the synchronizers and the outputs only updated if the 3rd + // stage and sychronizer agree. If they do not agree, the ResetValue is + // output instead. + parameter bit StabilityCheck = 0, + // Reset value for the sync flops + parameter mubi16_t ResetValue = MuBi16False +) ( + input clk_i, + input rst_ni, + input mubi16_t mubi_i, + output mubi16_t [NumCopies-1:0] mubi_o +); + + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) + + logic [MuBi16Width-1:0] mubi; + if (AsyncOn) begin : gen_flops + logic [MuBi16Width-1:0] mubi_sync; + prim_flop_2sync #( + .Width(MuBi16Width), + .ResetValue(MuBi16Width'(ResetValue)) + ) u_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(MuBi16Width'(mubi_i)), + .q_o(mubi_sync) + ); + + if (StabilityCheck) begin : gen_stable_chks + logic [MuBi16Width-1:0] mubi_q; + prim_flop #( + .Width(MuBi16Width), + .ResetValue(MuBi16Width'(ResetValue)) + ) u_prim_flop_3rd_stage ( + .clk_i, + .rst_ni, + .d_i(mubi_sync), + .q_o(mubi_q) + ); + + logic [MuBi16Width-1:0] sig_unstable; + prim_xor2 #( + .Width(MuBi16Width) + ) u_mubi_xor ( + .in0_i(mubi_sync), + .in1_i(mubi_q), + .out_o(sig_unstable) + ); + + logic [MuBi16Width-1:0] reset_value; + assign reset_value = ResetValue; + + for (genvar k = 0; k < MuBi16Width; k++) begin : gen_bufs_muxes + logic [MuBi16Width-1:0] sig_unstable_buf; + + // each mux gets its own buffered output, this ensures the OR-ing + // cannot be defeated in one place. + prim_sec_anchor_buf #( + .Width(MuBi16Width) + ) u_sig_unstable_buf ( + .in_i(sig_unstable), + .out_o(sig_unstable_buf) + ); + + // if any xor indicates signal is unstable, output the reset + // value. note that the input and output signals of this mux + // are driven/read by constrained primitive cells (regs, buffers), + // hence this mux can be implemented behaviorally. + assign mubi[k] = (|sig_unstable_buf) ? reset_value[k] : mubi_q[k]; + end + +// Note regarding SVAs below: +// +// 1) Without the sampled rst_ni pre-condition, this may cause false assertion failures right after +// a reset release, since the "disable iff" condition with the rst_ni is sampled in the "observed" +// SV scheduler region after all assignments have been evaluated (see also LRM section 16.12, page +// 423). This is a simulation artifact due to reset synchronization in RTL, which releases rst_ni +// on the active clock edge. This causes the assertion to evaluate although the reset was actually +// 0 when entering this simulation cycle. +// +// 2) Similarly to 1) there can be sampling mismatches of the lc_en_i signal since that signal may +// originate from a different clock domain. I.e., in cases where the lc_en_i signal changes exactly +// at the same time that the clk_i signal rises, the SVA will not pick up that change in that clock +// cycle, whereas RTL will because SVAs sample values in the "preponed" region. To that end we make +// use of an RTL helper variable to sample the lc_en_i signal, hence ensuring that there are no +// sampling mismatches. +`ifdef INC_ASSERT + mubi16_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputIfUnstable_A, sig_unstable |-> mubi_o == {NumCopies{reset_value}}) + `ASSERT(OutputDelay_A, + rst_ni |-> ##[3:4] sig_unstable || mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}}) +`endif + end else begin : gen_no_stable_chks + assign mubi = mubi_sync; +`ifdef INC_ASSERT + mubi16_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputDelay_A, + rst_ni |-> ##3 (mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}} || + $past(mubi_in_sva_q, 2) != $past(mubi_in_sva_q, 1))) +`endif + end + end else begin : gen_no_flops + + //VCS coverage off + // pragma coverage off + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for synthesis since it is unloaded. + mubi16_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi16False; + end else begin + unused_logic <= mubi_i; + end + end + + //VCS coverage on + // pragma coverage on + + assign mubi = MuBi16Width'(mubi_i); + + `ASSERT(OutputDelay_A, mubi_o == {NumCopies{mubi_i}}) + end + + for (genvar j = 0; j < NumCopies; j++) begin : gen_buffs + logic [MuBi16Width-1:0] mubi_out; + for (genvar k = 0; k < MuBi16Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i(mubi[k]), + .out_o(mubi_out[k]) + ); + end + assign mubi_o[j] = mubi16_t'(mubi_out); + end + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi16_sync diff --git a/synth/prim/rtl/prim_mubi4_dec.sv b/synth/prim/rtl/prim_mubi4_dec.sv new file mode 100755 index 0000000..b822fa8 --- /dev/null +++ b/synth/prim/rtl/prim_mubi4_dec.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 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Decoder for multibit control signals with additional input buffers. + +`include "prim_assert.sv" + +module prim_mubi4_dec + import prim_mubi_pkg::*; +#( + parameter bit TestTrue = 1, + parameter bit TestStrict = 1 +) ( + input mubi4_t mubi_i, + output logic mubi_dec_o +); + +logic [MuBi4Width-1:0] mubi, mubi_out; +assign mubi = MuBi4Width'(mubi_i); + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < MuBi4Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i ( mubi[k] ), + .out_o ( mubi_out[k] ) + ); +end + +if (TestTrue && TestStrict) begin : gen_test_true_strict + assign mubi_dec_o = mubi4_test_true_strict(mubi4_t'(mubi_out)); +end else if (TestTrue && !TestStrict) begin : gen_test_true_loose + assign mubi_dec_o = mubi4_test_true_loose(mubi4_t'(mubi_out)); +end else if (!TestTrue && TestStrict) begin : gen_test_false_strict + assign mubi_dec_o = mubi4_test_false_strict(mubi4_t'(mubi_out)); +end else if (!TestTrue && !TestStrict) begin : gen_test_false_loose + assign mubi_dec_o = mubi4_test_false_loose(mubi4_t'(mubi_out)); +end else begin : gen_unknown_config + `ASSERT_INIT(UnknownConfig_A, 0) +end + +endmodule : prim_mubi4_dec diff --git a/synth/prim/rtl/prim_mubi4_sender.sv b/synth/prim/rtl/prim_mubi4_sender.sv new file mode 100755 index 0000000..85b7af8 --- /dev/null +++ b/synth/prim/rtl/prim_mubi4_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "prim_assert.sv" + +module prim_mubi4_sender + import prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi4_t ResetValue = MuBi4False +) ( + input clk_i, + input rst_ni, + input mubi4_t mubi_i, + output mubi4_t mubi_o +); + + logic [MuBi4Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi4Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + prim_flop #( + .Width(MuBi4Width), + .ResetValue(MuBi4Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi4_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi4False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + prim_sec_anchor_buf #( + .Width(4) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + prim_buf #( + .Width(4) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi4_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi4_sender diff --git a/synth/prim/rtl/prim_mubi4_sync.sv b/synth/prim/rtl/prim_mubi4_sync.sv new file mode 100755 index 0000000..0239540 --- /dev/null +++ b/synth/prim/rtl/prim_mubi4_sync.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Double-synchronizer flop for multibit signals with additional output buffers. + +`include "prim_assert.sv" + +module prim_mubi4_sync + import prim_mubi_pkg::*; +#( + // Number of separately buffered output signals. + // The buffer cells have a don't touch constraint + // on them such that synthesis tools won't collapse + // all copies into one signal. + parameter int NumCopies = 1, + // This instantiates the synchronizer flops if set to 1. + // In special cases where the receiver is in the same clock domain as the sender, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // This controls whether the mubi module institutes stability checks when + // AsyncOn is set. If stability checks are on, a 3rd stage of storage is + // added after the synchronizers and the outputs only updated if the 3rd + // stage and sychronizer agree. If they do not agree, the ResetValue is + // output instead. + parameter bit StabilityCheck = 0, + // Reset value for the sync flops + parameter mubi4_t ResetValue = MuBi4False +) ( + input clk_i, + input rst_ni, + input mubi4_t mubi_i, + output mubi4_t [NumCopies-1:0] mubi_o +); + + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) + + logic [MuBi4Width-1:0] mubi; + if (AsyncOn) begin : gen_flops + logic [MuBi4Width-1:0] mubi_sync; + prim_flop_2sync #( + .Width(MuBi4Width), + .ResetValue(MuBi4Width'(ResetValue)) + ) u_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(MuBi4Width'(mubi_i)), + .q_o(mubi_sync) + ); + + if (StabilityCheck) begin : gen_stable_chks + logic [MuBi4Width-1:0] mubi_q; + prim_flop #( + .Width(MuBi4Width), + .ResetValue(MuBi4Width'(ResetValue)) + ) u_prim_flop_3rd_stage ( + .clk_i, + .rst_ni, + .d_i(mubi_sync), + .q_o(mubi_q) + ); + + logic [MuBi4Width-1:0] sig_unstable; + prim_xor2 #( + .Width(MuBi4Width) + ) u_mubi_xor ( + .in0_i(mubi_sync), + .in1_i(mubi_q), + .out_o(sig_unstable) + ); + + logic [MuBi4Width-1:0] reset_value; + assign reset_value = ResetValue; + + for (genvar k = 0; k < MuBi4Width; k++) begin : gen_bufs_muxes + logic [MuBi4Width-1:0] sig_unstable_buf; + + // each mux gets its own buffered output, this ensures the OR-ing + // cannot be defeated in one place. + prim_sec_anchor_buf #( + .Width(MuBi4Width) + ) u_sig_unstable_buf ( + .in_i(sig_unstable), + .out_o(sig_unstable_buf) + ); + + // if any xor indicates signal is unstable, output the reset + // value. note that the input and output signals of this mux + // are driven/read by constrained primitive cells (regs, buffers), + // hence this mux can be implemented behaviorally. + assign mubi[k] = (|sig_unstable_buf) ? reset_value[k] : mubi_q[k]; + end + +// Note regarding SVAs below: +// +// 1) Without the sampled rst_ni pre-condition, this may cause false assertion failures right after +// a reset release, since the "disable iff" condition with the rst_ni is sampled in the "observed" +// SV scheduler region after all assignments have been evaluated (see also LRM section 16.12, page +// 423). This is a simulation artifact due to reset synchronization in RTL, which releases rst_ni +// on the active clock edge. This causes the assertion to evaluate although the reset was actually +// 0 when entering this simulation cycle. +// +// 2) Similarly to 1) there can be sampling mismatches of the lc_en_i signal since that signal may +// originate from a different clock domain. I.e., in cases where the lc_en_i signal changes exactly +// at the same time that the clk_i signal rises, the SVA will not pick up that change in that clock +// cycle, whereas RTL will because SVAs sample values in the "preponed" region. To that end we make +// use of an RTL helper variable to sample the lc_en_i signal, hence ensuring that there are no +// sampling mismatches. +`ifdef INC_ASSERT + mubi4_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputIfUnstable_A, sig_unstable |-> mubi_o == {NumCopies{reset_value}}) + `ASSERT(OutputDelay_A, + rst_ni |-> ##[3:4] sig_unstable || mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}}) +`endif + end else begin : gen_no_stable_chks + assign mubi = mubi_sync; +`ifdef INC_ASSERT + mubi4_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputDelay_A, + rst_ni |-> ##3 (mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}} || + $past(mubi_in_sva_q, 2) != $past(mubi_in_sva_q, 1))) +`endif + end + end else begin : gen_no_flops + + //VCS coverage off + // pragma coverage off + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for synthesis since it is unloaded. + mubi4_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi4False; + end else begin + unused_logic <= mubi_i; + end + end + + //VCS coverage on + // pragma coverage on + + assign mubi = MuBi4Width'(mubi_i); + + `ASSERT(OutputDelay_A, mubi_o == {NumCopies{mubi_i}}) + end + + for (genvar j = 0; j < NumCopies; j++) begin : gen_buffs + logic [MuBi4Width-1:0] mubi_out; + for (genvar k = 0; k < MuBi4Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i(mubi[k]), + .out_o(mubi_out[k]) + ); + end + assign mubi_o[j] = mubi4_t'(mubi_out); + end + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi4_sync diff --git a/synth/prim/rtl/prim_mubi8_dec.sv b/synth/prim/rtl/prim_mubi8_dec.sv new file mode 100755 index 0000000..0f7171f --- /dev/null +++ b/synth/prim/rtl/prim_mubi8_dec.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 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Decoder for multibit control signals with additional input buffers. + +`include "prim_assert.sv" + +module prim_mubi8_dec + import prim_mubi_pkg::*; +#( + parameter bit TestTrue = 1, + parameter bit TestStrict = 1 +) ( + input mubi8_t mubi_i, + output logic mubi_dec_o +); + +logic [MuBi8Width-1:0] mubi, mubi_out; +assign mubi = MuBi8Width'(mubi_i); + +// The buffer cells have a don't touch constraint on them +// such that synthesis tools won't collapse them +for (genvar k = 0; k < MuBi8Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i ( mubi[k] ), + .out_o ( mubi_out[k] ) + ); +end + +if (TestTrue && TestStrict) begin : gen_test_true_strict + assign mubi_dec_o = mubi8_test_true_strict(mubi8_t'(mubi_out)); +end else if (TestTrue && !TestStrict) begin : gen_test_true_loose + assign mubi_dec_o = mubi8_test_true_loose(mubi8_t'(mubi_out)); +end else if (!TestTrue && TestStrict) begin : gen_test_false_strict + assign mubi_dec_o = mubi8_test_false_strict(mubi8_t'(mubi_out)); +end else if (!TestTrue && !TestStrict) begin : gen_test_false_loose + assign mubi_dec_o = mubi8_test_false_loose(mubi8_t'(mubi_out)); +end else begin : gen_unknown_config + `ASSERT_INIT(UnknownConfig_A, 0) +end + +endmodule : prim_mubi8_dec diff --git a/synth/prim/rtl/prim_mubi8_sender.sv b/synth/prim/rtl/prim_mubi8_sender.sv new file mode 100755 index 0000000..7fb81f5 --- /dev/null +++ b/synth/prim/rtl/prim_mubi8_sender.sv @@ -0,0 +1,94 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Multibit sender module. This module is instantiates a hand-picked flop cell for each bit in the +// multibit signal such that tools do not optimize the multibit encoding. + +`include "prim_assert.sv" + +module prim_mubi8_sender + import prim_mubi_pkg::*; +#( + // This flops the output if set to 1. + // In special cases where the sender is in the same clock domain as the receiver, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // Enable anchor buffer + parameter bit EnSecBuf = 0, + // Reset value for the sender flops + parameter mubi8_t ResetValue = MuBi8False +) ( + input clk_i, + input rst_ni, + input mubi8_t mubi_i, + output mubi8_t mubi_o +); + + logic [MuBi8Width-1:0] mubi, mubi_int, mubi_out; + assign mubi = MuBi8Width'(mubi_i); + + // first generation block decides whether a flop should be present + if (AsyncOn) begin : gen_flops + prim_flop #( + .Width(MuBi8Width), + .ResetValue(MuBi8Width'(ResetValue)) + ) u_prim_flop ( + .clk_i, + .rst_ni, + .d_i ( mubi ), + .q_o ( mubi_int ) + ); + end else begin : gen_no_flops + assign mubi_int = mubi; + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for sythesis since it is unloaded. + mubi8_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi8False; + end else begin + unused_logic <= mubi_i; + end + end + end + + // second generation block determines output buffer type + // 1. If EnSecBuf -> always leads to a sec buffer regardless of first block + // 2. If not EnSecBuf and not AsyncOn -> use normal buffer + // 3. If not EnSecBuf and AsyncOn -> feed through + if (EnSecBuf) begin : gen_sec_buf + prim_sec_anchor_buf #( + .Width(8) + ) u_prim_sec_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else if (!AsyncOn) begin : gen_prim_buf + prim_buf #( + .Width(8) + ) u_prim_buf ( + .in_i(mubi_int), + .out_o(mubi_out) + ); + end else begin : gen_feedthru + assign mubi_out = mubi_int; + end + + assign mubi_o = mubi8_t'(mubi_out); + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi8_sender diff --git a/synth/prim/rtl/prim_mubi8_sync.sv b/synth/prim/rtl/prim_mubi8_sync.sv new file mode 100755 index 0000000..14e9254 --- /dev/null +++ b/synth/prim/rtl/prim_mubi8_sync.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// Double-synchronizer flop for multibit signals with additional output buffers. + +`include "prim_assert.sv" + +module prim_mubi8_sync + import prim_mubi_pkg::*; +#( + // Number of separately buffered output signals. + // The buffer cells have a don't touch constraint + // on them such that synthesis tools won't collapse + // all copies into one signal. + parameter int NumCopies = 1, + // This instantiates the synchronizer flops if set to 1. + // In special cases where the receiver is in the same clock domain as the sender, + // this can be set to 0. However, it is recommended to leave this at 1. + parameter bit AsyncOn = 1, + // This controls whether the mubi module institutes stability checks when + // AsyncOn is set. If stability checks are on, a 3rd stage of storage is + // added after the synchronizers and the outputs only updated if the 3rd + // stage and sychronizer agree. If they do not agree, the ResetValue is + // output instead. + parameter bit StabilityCheck = 0, + // Reset value for the sync flops + parameter mubi8_t ResetValue = MuBi8False +) ( + input clk_i, + input rst_ni, + input mubi8_t mubi_i, + output mubi8_t [NumCopies-1:0] mubi_o +); + + `ASSERT_INIT(NumCopiesMustBeGreaterZero_A, NumCopies > 0) + + logic [MuBi8Width-1:0] mubi; + if (AsyncOn) begin : gen_flops + logic [MuBi8Width-1:0] mubi_sync; + prim_flop_2sync #( + .Width(MuBi8Width), + .ResetValue(MuBi8Width'(ResetValue)) + ) u_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(MuBi8Width'(mubi_i)), + .q_o(mubi_sync) + ); + + if (StabilityCheck) begin : gen_stable_chks + logic [MuBi8Width-1:0] mubi_q; + prim_flop #( + .Width(MuBi8Width), + .ResetValue(MuBi8Width'(ResetValue)) + ) u_prim_flop_3rd_stage ( + .clk_i, + .rst_ni, + .d_i(mubi_sync), + .q_o(mubi_q) + ); + + logic [MuBi8Width-1:0] sig_unstable; + prim_xor2 #( + .Width(MuBi8Width) + ) u_mubi_xor ( + .in0_i(mubi_sync), + .in1_i(mubi_q), + .out_o(sig_unstable) + ); + + logic [MuBi8Width-1:0] reset_value; + assign reset_value = ResetValue; + + for (genvar k = 0; k < MuBi8Width; k++) begin : gen_bufs_muxes + logic [MuBi8Width-1:0] sig_unstable_buf; + + // each mux gets its own buffered output, this ensures the OR-ing + // cannot be defeated in one place. + prim_sec_anchor_buf #( + .Width(MuBi8Width) + ) u_sig_unstable_buf ( + .in_i(sig_unstable), + .out_o(sig_unstable_buf) + ); + + // if any xor indicates signal is unstable, output the reset + // value. note that the input and output signals of this mux + // are driven/read by constrained primitive cells (regs, buffers), + // hence this mux can be implemented behaviorally. + assign mubi[k] = (|sig_unstable_buf) ? reset_value[k] : mubi_q[k]; + end + +// Note regarding SVAs below: +// +// 1) Without the sampled rst_ni pre-condition, this may cause false assertion failures right after +// a reset release, since the "disable iff" condition with the rst_ni is sampled in the "observed" +// SV scheduler region after all assignments have been evaluated (see also LRM section 16.12, page +// 423). This is a simulation artifact due to reset synchronization in RTL, which releases rst_ni +// on the active clock edge. This causes the assertion to evaluate although the reset was actually +// 0 when entering this simulation cycle. +// +// 2) Similarly to 1) there can be sampling mismatches of the lc_en_i signal since that signal may +// originate from a different clock domain. I.e., in cases where the lc_en_i signal changes exactly +// at the same time that the clk_i signal rises, the SVA will not pick up that change in that clock +// cycle, whereas RTL will because SVAs sample values in the "preponed" region. To that end we make +// use of an RTL helper variable to sample the lc_en_i signal, hence ensuring that there are no +// sampling mismatches. +`ifdef INC_ASSERT + mubi8_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputIfUnstable_A, sig_unstable |-> mubi_o == {NumCopies{reset_value}}) + `ASSERT(OutputDelay_A, + rst_ni |-> ##[3:4] sig_unstable || mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}}) +`endif + end else begin : gen_no_stable_chks + assign mubi = mubi_sync; +`ifdef INC_ASSERT + mubi8_t mubi_in_sva_q; + always_ff @(posedge clk_i) begin + mubi_in_sva_q <= mubi_i; + end + `ASSERT(OutputDelay_A, + rst_ni |-> ##3 (mubi_o == {NumCopies{$past(mubi_in_sva_q, 2)}} || + $past(mubi_in_sva_q, 2) != $past(mubi_in_sva_q, 1))) +`endif + end + end else begin : gen_no_flops + + //VCS coverage off + // pragma coverage off + + // This unused companion logic helps remove lint errors + // for modules where clock and reset are used for assertions only + // This logic will be removed for synthesis since it is unloaded. + mubi8_t unused_logic; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + unused_logic <= MuBi8False; + end else begin + unused_logic <= mubi_i; + end + end + + //VCS coverage on + // pragma coverage on + + assign mubi = MuBi8Width'(mubi_i); + + `ASSERT(OutputDelay_A, mubi_o == {NumCopies{mubi_i}}) + end + + for (genvar j = 0; j < NumCopies; j++) begin : gen_buffs + logic [MuBi8Width-1:0] mubi_out; + for (genvar k = 0; k < MuBi8Width; k++) begin : gen_bits + prim_buf u_prim_buf ( + .in_i(mubi[k]), + .out_o(mubi_out[k]) + ); + end + assign mubi_o[j] = mubi8_t'(mubi_out); + end + + //////////////// + // Assertions // + //////////////// + + // The outputs should be known at all times. + `ASSERT_KNOWN(OutputsKnown_A, mubi_o) + +endmodule : prim_mubi8_sync diff --git a/synth/prim/rtl/prim_mubi_pkg.sv b/synth/prim/rtl/prim_mubi_pkg.sv new file mode 100755 index 0000000..13e167d --- /dev/null +++ b/synth/prim/rtl/prim_mubi_pkg.sv @@ -0,0 +1,545 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// ------------------- W A R N I N G: A U T O - G E N E R A T E D C O D E !! -------------------// +// PLEASE DO NOT HAND-EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED WITH THE FOLLOWING COMMAND: +// +// util/design/gen-mubi.py +// +// This package defines common multibit signal types, active high and active low values and +// the corresponding functions to test whether the values are set or not. + +`include "prim_assert.sv" + +package prim_mubi_pkg; + + ////////////////////////////////////////////// + // 4 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi4Width = 4; + typedef enum logic [MuBi4Width-1:0] { + MuBi4True = 4'h6, // enabled + MuBi4False = 4'h9 // disabled + } mubi4_t; + + // This is a prerequisite for the multibit functions below to work. + `ASSERT_STATIC_IN_PACKAGE(CheckMuBi4ValsComplementary_A, MuBi4True == ~MuBi4False) + + // Test whether the multibit value is one of the valid enumerations + function automatic logic mubi4_test_invalid(mubi4_t val); + return ~(val inside {MuBi4True, MuBi4False}); + endfunction : mubi4_test_invalid + + // Convert a 1 input value to a mubi output + function automatic mubi4_t mubi4_bool_to_mubi(logic val); + return (val ? MuBi4True : MuBi4False); + endfunction : mubi4_bool_to_mubi + + // Test whether the multibit value signals an "enabled" condition. + // The strict version of this function requires + // the multibit value to equal True. + function automatic logic mubi4_test_true_strict(mubi4_t val); + return MuBi4True == val; + endfunction : mubi4_test_true_strict + + // Test whether the multibit value signals a "disabled" condition. + // The strict version of this function requires + // the multibit value to equal False. + function automatic logic mubi4_test_false_strict(mubi4_t val); + return MuBi4False == val; + endfunction : mubi4_test_false_strict + + // Test whether the multibit value signals an "enabled" condition. + // The loose version of this function interprets all + // values other than False as "enabled". + function automatic logic mubi4_test_true_loose(mubi4_t val); + return MuBi4False != val; + endfunction : mubi4_test_true_loose + + // Test whether the multibit value signals a "disabled" condition. + // The loose version of this function interprets all + // values other than True as "disabled". + function automatic logic mubi4_test_false_loose(mubi4_t val); + return MuBi4True != val; + endfunction : mubi4_test_false_loose + + + // Performs a logical OR operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | act + // !act | act | act + // act | act | act + // + function automatic mubi4_t mubi4_or(mubi4_t a, mubi4_t b, mubi4_t act); + logic [MuBi4Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi4Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] || b_in[k]; + end else begin + out[k] = a_in[k] && b_in[k]; + end + end + return mubi4_t'(out); + endfunction : mubi4_or + + // Performs a logical AND operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | !act + // !act | act | !act + // act | act | act + // + function automatic mubi4_t mubi4_and(mubi4_t a, mubi4_t b, mubi4_t act); + logic [MuBi4Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi4Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] && b_in[k]; + end else begin + out[k] = a_in[k] || b_in[k]; + end + end + return mubi4_t'(out); + endfunction : mubi4_and + + // Performs a logical OR operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi4_t mubi4_or_hi(mubi4_t a, mubi4_t b); + return mubi4_or(a, b, MuBi4True); + endfunction : mubi4_or_hi + + // Performs a logical AND operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi4_t mubi4_and_hi(mubi4_t a, mubi4_t b); + return mubi4_and(a, b, MuBi4True); + endfunction : mubi4_and_hi + + // Performs a logical OR operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi4_t mubi4_or_lo(mubi4_t a, mubi4_t b); + return mubi4_or(a, b, MuBi4False); + endfunction : mubi4_or_lo + + // Performs a logical AND operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi4_t mubi4_and_lo(mubi4_t a, mubi4_t b); + return mubi4_and(a, b, MuBi4False); + endfunction : mubi4_and_lo + + ////////////////////////////////////////////// + // 8 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi8Width = 8; + typedef enum logic [MuBi8Width-1:0] { + MuBi8True = 8'h96, // enabled + MuBi8False = 8'h69 // disabled + } mubi8_t; + + // This is a prerequisite for the multibit functions below to work. + `ASSERT_STATIC_IN_PACKAGE(CheckMuBi8ValsComplementary_A, MuBi8True == ~MuBi8False) + + // Test whether the multibit value is one of the valid enumerations + function automatic logic mubi8_test_invalid(mubi8_t val); + return ~(val inside {MuBi8True, MuBi8False}); + endfunction : mubi8_test_invalid + + // Convert a 1 input value to a mubi output + function automatic mubi8_t mubi8_bool_to_mubi(logic val); + return (val ? MuBi8True : MuBi8False); + endfunction : mubi8_bool_to_mubi + + // Test whether the multibit value signals an "enabled" condition. + // The strict version of this function requires + // the multibit value to equal True. + function automatic logic mubi8_test_true_strict(mubi8_t val); + return MuBi8True == val; + endfunction : mubi8_test_true_strict + + // Test whether the multibit value signals a "disabled" condition. + // The strict version of this function requires + // the multibit value to equal False. + function automatic logic mubi8_test_false_strict(mubi8_t val); + return MuBi8False == val; + endfunction : mubi8_test_false_strict + + // Test whether the multibit value signals an "enabled" condition. + // The loose version of this function interprets all + // values other than False as "enabled". + function automatic logic mubi8_test_true_loose(mubi8_t val); + return MuBi8False != val; + endfunction : mubi8_test_true_loose + + // Test whether the multibit value signals a "disabled" condition. + // The loose version of this function interprets all + // values other than True as "disabled". + function automatic logic mubi8_test_false_loose(mubi8_t val); + return MuBi8True != val; + endfunction : mubi8_test_false_loose + + + // Performs a logical OR operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | act + // !act | act | act + // act | act | act + // + function automatic mubi8_t mubi8_or(mubi8_t a, mubi8_t b, mubi8_t act); + logic [MuBi8Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi8Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] || b_in[k]; + end else begin + out[k] = a_in[k] && b_in[k]; + end + end + return mubi8_t'(out); + endfunction : mubi8_or + + // Performs a logical AND operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | !act + // !act | act | !act + // act | act | act + // + function automatic mubi8_t mubi8_and(mubi8_t a, mubi8_t b, mubi8_t act); + logic [MuBi8Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi8Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] && b_in[k]; + end else begin + out[k] = a_in[k] || b_in[k]; + end + end + return mubi8_t'(out); + endfunction : mubi8_and + + // Performs a logical OR operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi8_t mubi8_or_hi(mubi8_t a, mubi8_t b); + return mubi8_or(a, b, MuBi8True); + endfunction : mubi8_or_hi + + // Performs a logical AND operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi8_t mubi8_and_hi(mubi8_t a, mubi8_t b); + return mubi8_and(a, b, MuBi8True); + endfunction : mubi8_and_hi + + // Performs a logical OR operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi8_t mubi8_or_lo(mubi8_t a, mubi8_t b); + return mubi8_or(a, b, MuBi8False); + endfunction : mubi8_or_lo + + // Performs a logical AND operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi8_t mubi8_and_lo(mubi8_t a, mubi8_t b); + return mubi8_and(a, b, MuBi8False); + endfunction : mubi8_and_lo + + ////////////////////////////////////////////// + // 12 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi12Width = 12; + typedef enum logic [MuBi12Width-1:0] { + MuBi12True = 12'h696, // enabled + MuBi12False = 12'h969 // disabled + } mubi12_t; + + // This is a prerequisite for the multibit functions below to work. + `ASSERT_STATIC_IN_PACKAGE(CheckMuBi12ValsComplementary_A, MuBi12True == ~MuBi12False) + + // Test whether the multibit value is one of the valid enumerations + function automatic logic mubi12_test_invalid(mubi12_t val); + return ~(val inside {MuBi12True, MuBi12False}); + endfunction : mubi12_test_invalid + + // Convert a 1 input value to a mubi output + function automatic mubi12_t mubi12_bool_to_mubi(logic val); + return (val ? MuBi12True : MuBi12False); + endfunction : mubi12_bool_to_mubi + + // Test whether the multibit value signals an "enabled" condition. + // The strict version of this function requires + // the multibit value to equal True. + function automatic logic mubi12_test_true_strict(mubi12_t val); + return MuBi12True == val; + endfunction : mubi12_test_true_strict + + // Test whether the multibit value signals a "disabled" condition. + // The strict version of this function requires + // the multibit value to equal False. + function automatic logic mubi12_test_false_strict(mubi12_t val); + return MuBi12False == val; + endfunction : mubi12_test_false_strict + + // Test whether the multibit value signals an "enabled" condition. + // The loose version of this function interprets all + // values other than False as "enabled". + function automatic logic mubi12_test_true_loose(mubi12_t val); + return MuBi12False != val; + endfunction : mubi12_test_true_loose + + // Test whether the multibit value signals a "disabled" condition. + // The loose version of this function interprets all + // values other than True as "disabled". + function automatic logic mubi12_test_false_loose(mubi12_t val); + return MuBi12True != val; + endfunction : mubi12_test_false_loose + + + // Performs a logical OR operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | act + // !act | act | act + // act | act | act + // + function automatic mubi12_t mubi12_or(mubi12_t a, mubi12_t b, mubi12_t act); + logic [MuBi12Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi12Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] || b_in[k]; + end else begin + out[k] = a_in[k] && b_in[k]; + end + end + return mubi12_t'(out); + endfunction : mubi12_or + + // Performs a logical AND operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | !act + // !act | act | !act + // act | act | act + // + function automatic mubi12_t mubi12_and(mubi12_t a, mubi12_t b, mubi12_t act); + logic [MuBi12Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi12Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] && b_in[k]; + end else begin + out[k] = a_in[k] || b_in[k]; + end + end + return mubi12_t'(out); + endfunction : mubi12_and + + // Performs a logical OR operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi12_t mubi12_or_hi(mubi12_t a, mubi12_t b); + return mubi12_or(a, b, MuBi12True); + endfunction : mubi12_or_hi + + // Performs a logical AND operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi12_t mubi12_and_hi(mubi12_t a, mubi12_t b); + return mubi12_and(a, b, MuBi12True); + endfunction : mubi12_and_hi + + // Performs a logical OR operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi12_t mubi12_or_lo(mubi12_t a, mubi12_t b); + return mubi12_or(a, b, MuBi12False); + endfunction : mubi12_or_lo + + // Performs a logical AND operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi12_t mubi12_and_lo(mubi12_t a, mubi12_t b); + return mubi12_and(a, b, MuBi12False); + endfunction : mubi12_and_lo + + ////////////////////////////////////////////// + // 16 Bit Multibit Type and Functions // + ////////////////////////////////////////////// + + parameter int MuBi16Width = 16; + typedef enum logic [MuBi16Width-1:0] { + MuBi16True = 16'h9696, // enabled + MuBi16False = 16'h6969 // disabled + } mubi16_t; + + // This is a prerequisite for the multibit functions below to work. + `ASSERT_STATIC_IN_PACKAGE(CheckMuBi16ValsComplementary_A, MuBi16True == ~MuBi16False) + + // Test whether the multibit value is one of the valid enumerations + function automatic logic mubi16_test_invalid(mubi16_t val); + return ~(val inside {MuBi16True, MuBi16False}); + endfunction : mubi16_test_invalid + + // Convert a 1 input value to a mubi output + function automatic mubi16_t mubi16_bool_to_mubi(logic val); + return (val ? MuBi16True : MuBi16False); + endfunction : mubi16_bool_to_mubi + + // Test whether the multibit value signals an "enabled" condition. + // The strict version of this function requires + // the multibit value to equal True. + function automatic logic mubi16_test_true_strict(mubi16_t val); + return MuBi16True == val; + endfunction : mubi16_test_true_strict + + // Test whether the multibit value signals a "disabled" condition. + // The strict version of this function requires + // the multibit value to equal False. + function automatic logic mubi16_test_false_strict(mubi16_t val); + return MuBi16False == val; + endfunction : mubi16_test_false_strict + + // Test whether the multibit value signals an "enabled" condition. + // The loose version of this function interprets all + // values other than False as "enabled". + function automatic logic mubi16_test_true_loose(mubi16_t val); + return MuBi16False != val; + endfunction : mubi16_test_true_loose + + // Test whether the multibit value signals a "disabled" condition. + // The loose version of this function interprets all + // values other than True as "disabled". + function automatic logic mubi16_test_false_loose(mubi16_t val); + return MuBi16True != val; + endfunction : mubi16_test_false_loose + + + // Performs a logical OR operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | act + // !act | act | act + // act | act | act + // + function automatic mubi16_t mubi16_or(mubi16_t a, mubi16_t b, mubi16_t act); + logic [MuBi16Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi16Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] || b_in[k]; + end else begin + out[k] = a_in[k] && b_in[k]; + end + end + return mubi16_t'(out); + endfunction : mubi16_or + + // Performs a logical AND operation between two multibit values. + // This treats "act" as logical 1, and all other values are + // treated as 0. Truth table: + // + // A | B | OUT + //------+------+----- + // !act | !act | !act + // act | !act | !act + // !act | act | !act + // act | act | act + // + function automatic mubi16_t mubi16_and(mubi16_t a, mubi16_t b, mubi16_t act); + logic [MuBi16Width-1:0] a_in, b_in, act_in, out; + a_in = a; + b_in = b; + act_in = act; + for (int k = 0; k < MuBi16Width; k++) begin + if (act_in[k]) begin + out[k] = a_in[k] && b_in[k]; + end else begin + out[k] = a_in[k] || b_in[k]; + end + end + return mubi16_t'(out); + endfunction : mubi16_and + + // Performs a logical OR operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi16_t mubi16_or_hi(mubi16_t a, mubi16_t b); + return mubi16_or(a, b, MuBi16True); + endfunction : mubi16_or_hi + + // Performs a logical AND operation between two multibit values. + // This treats "True" as logical 1, and all other values are + // treated as 0. + function automatic mubi16_t mubi16_and_hi(mubi16_t a, mubi16_t b); + return mubi16_and(a, b, MuBi16True); + endfunction : mubi16_and_hi + + // Performs a logical OR operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi16_t mubi16_or_lo(mubi16_t a, mubi16_t b); + return mubi16_or(a, b, MuBi16False); + endfunction : mubi16_or_lo + + // Performs a logical AND operation between two multibit values. + // This treats "False" as logical 1, and all other values are + // treated as 0. + function automatic mubi16_t mubi16_and_lo(mubi16_t a, mubi16_t b); + return mubi16_and(a, b, MuBi16False); + endfunction : mubi16_and_lo + +endpackage : prim_mubi_pkg diff --git a/synth/prim/rtl/prim_multibit_sync.sv b/synth/prim/rtl/prim_multibit_sync.sv new file mode 100755 index 0000000..79a3ab6 --- /dev/null +++ b/synth/prim/rtl/prim_multibit_sync.sv @@ -0,0 +1,103 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// WARNING: DO NOT USE THIS MODULE IF YOU DO NOT HAVE A GOOD REASON TO DO SO. +// +// This module is only meant to be used in special cases where a handshake synchronizer +// is not viable (this is for instance the case for the multibit life cycle signals). +// For handshake-based synchronization, consider using prim_sync_reqack_data. +// +// +// Description: +// +// This module implements a multibit synchronizer that employs a data consistency check to +// decide whether the synchronized multibit signal is stable and can be output or not. +// +// The number of consistency checkers can be controlled via NumChecks. Each check adds another +// delay register after the 2-flop synchronizer, and corresponding comparator that checks whether +// the register input is equal to the output of the last register in the chain. If all checks are +// successful, the output register is enabled such that the data can propagate to the output. +// +// This is illustrated bellow for NumChecks = 1: +// +// /--------\ /--------\ /--------\ +// | | | | | | +// data_i --/--> | flop | --x--> | flop | --x--> | flop | --/--> data_o +// Width | 2 sync | | | | | | | +// | | | | | | | en | +// \--------/ | \--------/ | \--------/ +// | v ^ +// | /----\ | +// \-------------> | == | ----/ +// \----/ +// +// Note: CDC tools will likely flag this module due to re-convergent logic. +// + +`include "prim_assert.sv" + +module prim_multibit_sync #( + // Width of the multibit signal. + parameter int Width = 8, + // Number of cycles the synchronized multi-bit signal needs to + // be stable until it is relased to the output. Each check adds + // a comparator and an additional delay register. + parameter int NumChecks = 1, + // Reset value of the multibit signal. + parameter logic [Width-1:0] ResetValue = '0 +) ( + input clk_i, + input rst_ni, + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + `ASSERT_INIT(NumChecks_A, NumChecks >= 1) + + // First, synchronize the input data to this clock domain. + logic [NumChecks:0][Width-1:0] data_check_d; + logic [NumChecks-1:0][Width-1:0] data_check_q; + + prim_flop_2sync #( + .Width(Width), + .ResetValue(ResetValue) + ) i_prim_flop_2sync ( + .clk_i, + .rst_ni, + .d_i(data_i), + .q_o(data_check_d[0]) + ); + + // Shift register. + assign data_check_d[NumChecks:1] = data_check_q[NumChecks-1:0]; + + // Consistency check. Always compare to the output of the last register. + logic [NumChecks-1:0] checks; + for (genvar k = 0; k < NumChecks; k++) begin : gen_checks + assign checks[k] = (data_check_d[k] == data_check_d[NumChecks]); + // Output is only allowed to change when all checks have passed. + `ASSERT(StableCheck_A, + data_o != $past(data_o) + |-> + $past(data_check_d[k]) == $past(data_check_d[NumChecks])) + end : gen_checks + + // Only propagate to output register if all checks have passed. + logic [Width-1:0] data_synced_d, data_synced_q; + assign data_synced_d = (&checks) ? data_check_d[NumChecks] : data_synced_q; + assign data_o = data_synced_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + data_synced_q <= ResetValue; + data_check_q <= {NumChecks{ResetValue}}; + end else begin + data_synced_q <= data_synced_d; + data_check_q <= data_check_d[NumChecks-1:0]; + end + end + + `ASSERT_KNOWN(DataKnown_A, data_o) + +endmodule : prim_multibit_sync diff --git a/synth/prim/rtl/prim_onehot_check.sv b/synth/prim/rtl/prim_onehot_check.sv new file mode 100755 index 0000000..b70ec76 --- /dev/null +++ b/synth/prim/rtl/prim_onehot_check.sv @@ -0,0 +1,156 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Onehot checker. +// +// This module checks whether the input vector oh_i is onehot0 and generates an error if not. +// +// Optionally, two additional checks can be activated: +// +// 1) EnableCheck: this check performs an OR reduction of the onehot vector, and compares +// the result with en_i. If there is a mismatch, an error is generated. +// 2) AddrCheck: this checks whether the onehot bit is in the correct position. +// It requires an additional address addr_i to be supplied to the module. +// +// All checks make use of an explicit binary tree implementation in order to minimize the delay. +// + +`include "prim_assert.sv" + +module prim_onehot_check #( + parameter int unsigned AddrWidth = 5, + // The onehot width can be <= 2**AddrWidth and does not have to be a power of two. + parameter int unsigned OneHotWidth = 2**AddrWidth, + // If set to 0, the addr_i input will not be used for the check and can be tied off. + parameter bit AddrCheck = 1, + // If set to 0, the en_i value will not be used for the check and can be tied off. + parameter bit EnableCheck = 1, + // If set to 1, the oh_i vector must always be one hot if en_i is set to 1. + // If set to 0, the oh_i vector may be 0 if en_i is set to 1 (useful when oh_i can be masked). + parameter bit StrictCheck = 1, + // This should only be disabled in special circumstances, for example + // in non-comportable IPs where an error does not trigger an alert. + parameter bit EnableAlertTriggerSVA = 1 +) ( + // The module is combinational - the clock and reset are only used for assertions. + input clk_i, + input rst_ni, + + input logic [OneHotWidth-1:0] oh_i, + input logic [AddrWidth-1:0] addr_i, + input logic en_i, + + output logic err_o +); + + /////////////////////// + // Binary tree logic // + /////////////////////// + + `ASSERT_INIT(NumSources_A, OneHotWidth >= 1) + `ASSERT_INIT(AddrWidth_A, AddrWidth >= 1) + `ASSERT_INIT(AddrRange_A, OneHotWidth <= 2**AddrWidth) + `ASSERT_INIT(AddrImpliesEnable_A, AddrCheck && EnableCheck || !AddrCheck) + + // Align to powers of 2 for simplicity. + // A full binary tree with N levels has 2**N + 2**N-1 nodes. + localparam int NumLevels = AddrWidth; + logic [2**(NumLevels+1)-2:0] or_tree; + logic [2**(NumLevels+1)-2:0] and_tree; // Used for the address check + logic [2**(NumLevels+1)-2:0] err_tree; // Used for the enable check + + 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 paPa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // This assigns the input values, their corresponding IDs and valid signals to the tree leafs. + if (level == NumLevels) begin : gen_leafs + if (offset < OneHotWidth) begin : gen_assign + assign or_tree[Pa] = oh_i[offset]; + assign and_tree[Pa] = oh_i[offset]; + end else begin : gen_tie_off + assign or_tree[Pa] = 1'b0; + assign and_tree[Pa] = 1'b0; + end + assign err_tree[Pa] = 1'b0; + // This creates the node assignments. + end else begin : gen_nodes + assign or_tree[Pa] = or_tree[C0] || or_tree[C1]; + assign and_tree[Pa] = (!addr_i[AddrWidth-1-level] && and_tree[C0]) || + (addr_i[AddrWidth-1-level] && and_tree[C1]); + assign err_tree[Pa] = (or_tree[C0] && or_tree[C1]) || err_tree[C0] || err_tree[C1]; + end + end : gen_level + end : gen_tree + + /////////////////// + // Onehot Checks // + /////////////////// + + logic enable_err, addr_err, oh0_err; + assign err_o = oh0_err || enable_err || addr_err; + + // Check that no more than 1 bit is set in the vector. + assign oh0_err = err_tree[0]; + `ASSERT(Onehot0Check_A, !$onehot0(oh_i) |-> err_o) + + // Check that en_i agrees with (|oh_i). + // Note: if StrictCheck 0, the oh_i vector may be all-zero if en_i == 1 (but not vice versa). + if (EnableCheck) begin : gen_enable_check + if (StrictCheck) begin : gen_strict + assign enable_err = or_tree[0] ^ en_i; + `ASSERT(EnableCheck_A, (|oh_i) != en_i |-> err_o) + end else begin : gen_not_strict + assign enable_err = !en_i && or_tree[0]; + `ASSERT(EnableCheck_A, !en_i && (|oh_i) |-> err_o) + end + end else begin : gen_no_enable_check + logic unused_or_tree; + assign unused_or_tree = ^or_tree; + assign enable_err = 1'b0; + end + + // Check that the set bit is actually in the correct position. + if (AddrCheck) begin : gen_addr_check_strict + assign addr_err = or_tree[0] ^ and_tree[0]; + `ASSERT(AddrCheck_A, oh_i[addr_i] != (|oh_i) |-> err_o) + end else begin : gen_no_addr_check_strict + logic unused_and_tree; + assign unused_and_tree = ^and_tree; + assign addr_err = 1'b0; + end + + // We want to know that a block that instantiates prim_onehot_check will raise an alert if we set + // our err_o output. + // + // For confidence that this is true, we use the scheme described in "Security Countermeasure + // Verification Framework". We expect a user of prim_onehot_check to use the + // ASSERT_PRIM_COUNT_ERROR_TRIGGER_ALERT macro to check that they will indeed raise an alert if we + // set err_o. + // + // That macro is also designed to drive our local unused_assert_connected variable to true. We add + // an assertion locally that checks (just after the start of time) that it is indeed true. This + // gives us confidence that the user has bound up the alert correctly. +`ifdef INC_ASSERT + logic unused_assert_connected; + `ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA) +`endif + +endmodule : prim_onehot_check diff --git a/synth/prim/rtl/prim_onehot_enc.sv b/synth/prim/rtl/prim_onehot_enc.sv new file mode 100755 index 0000000..35ecee1 --- /dev/null +++ b/synth/prim/rtl/prim_onehot_enc.sv @@ -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 + +// One-hot encoder +// Outputs a one-hot encoded version of an integer input. + +module prim_onehot_enc #( + parameter int unsigned OneHotWidth = 32, + localparam int unsigned InputWidth = $clog2(OneHotWidth) +) ( + input logic [InputWidth-1:0] in_i, + input logic en_i, // out_o == '0 when en_i == 0 + + output logic [OneHotWidth-1:0] out_o +); + + for (genvar i = 0; i < OneHotWidth; ++i) begin : g_out + assign out_o[i] = (in_i == i) & en_i; + end +endmodule diff --git a/synth/prim/rtl/prim_onehot_mux.sv b/synth/prim/rtl/prim_onehot_mux.sv new file mode 100755 index 0000000..70bd2c3 --- /dev/null +++ b/synth/prim/rtl/prim_onehot_mux.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 + +// One-hot mux +// A AND/OR mux with a one-hot select input. + +`include "prim_assert.sv" + +module prim_onehot_mux #( + parameter int Width = 32, + parameter int Inputs = 8 +) ( + // Clock and reset only for assertions + input clk_i, + input rst_ni, + + input logic [Width-1:0] in_i [Inputs], + input logic [Inputs-1:0] sel_i, // Must be one-hot or zero + output logic [Width-1:0] out_o +); + logic [Inputs-1:0] in_mux [Width]; + + for (genvar b = 0; b < Width; ++b) begin : g_in_mux_outer + logic [Inputs-1:0] out_mux_bits; + + for (genvar i = 0; i < Inputs; ++i) begin : g_in_mux_inner + assign in_mux[b][i] = in_i[i][b]; + end + + prim_and2 #(.Width(Inputs)) u_mux_bit_and( + .in0_i(in_mux[b]), + .in1_i(sel_i), + .out_o(out_mux_bits) + ); + + assign out_o[b] = |out_mux_bits; + end + + logic unused_clk; + logic unused_rst_n; + + // clock and reset only needed for assertion + assign unused_clk = clk_i; + assign unused_rst_n = rst_ni; + + `ASSERT(SelIsOnehot_A, $onehot0(sel_i)) +endmodule diff --git a/synth/prim/rtl/prim_otp_pkg.sv b/synth/prim/rtl/prim_otp_pkg.sv new file mode 100755 index 0000000..e32586c --- /dev/null +++ b/synth/prim/rtl/prim_otp_pkg.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 + +// Common interface definitions for OTP primitives. + +package prim_otp_pkg; + + parameter int CmdWidth = 3; + parameter int ErrWidth = 3; + + typedef enum logic [CmdWidth-1:0] { + Read = 3'b000, + Write = 3'b001, + // Raw commands ignore integrity + ReadRaw = 3'b010, + WriteRaw = 3'b011, + Init = 3'b111 + } cmd_e; + + typedef enum logic [ErrWidth-1:0] { + NoError = 3'h0, + MacroError = 3'h1, + MacroEccCorrError = 3'h2, + MacroEccUncorrError = 3'h3, + MacroWriteBlankError = 3'h4 + } err_e; + +endpackage : prim_otp_pkg diff --git a/synth/prim/rtl/prim_packer.sv b/synth/prim/rtl/prim_packer.sv new file mode 100755 index 0000000..2ee6a69 --- /dev/null +++ b/synth/prim/rtl/prim_packer.sv @@ -0,0 +1,371 @@ +// 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" + +// ICEBOX(#12958): Revise to send out the empty status. +module prim_packer #( + parameter int unsigned InW = 32, + parameter int unsigned OutW = 32, + + // If 1, The input/output are byte granularity + parameter int HintByteData = 0, + + // Turn on protect against FI for the pos variable + parameter bit EnProtection = 1'b 0 +) ( + input clk_i , + input rst_ni, + + input valid_i, + input [InW-1:0] data_i, + input [InW-1:0] mask_i, + output ready_o, + + output logic valid_o, + output logic [OutW-1:0] data_o, + output logic [OutW-1:0] mask_o, + input ready_i, + + input flush_i, // If 1, send out remnant and clear state + output logic flush_done_o, + + // When EnProtection is set, err_o raises an error case (position variable + // mismatch) + output logic err_o +); + + localparam int unsigned Width = InW + OutW; // storage width + localparam int unsigned ConcatW = Width + InW; // Input concatenated width + localparam int unsigned PtrW = $clog2(ConcatW+1); + localparam int unsigned IdxW = prim_util_pkg::vbits(InW); + localparam int unsigned OnesCntW = $clog2(InW+1); + + logic valid_next, ready_next; + logic [Width-1:0] stored_data, stored_mask; + logic [ConcatW-1:0] concat_data, concat_mask; + logic [ConcatW-1:0] shiftl_data, shiftl_mask; + logic [InW-1:0] shiftr_data, shiftr_mask; + + logic [PtrW-1:0] pos_q; // Current write position + logic [IdxW-1:0] lod_idx; // result of Leading One Detector + logic [OnesCntW-1:0] inmask_ones; // Counting Ones for mask_i + + logic ack_in, ack_out; + + logic flush_valid; // flush data out request + logic flush_done; + + // Computing next position ================================================== + always_comb begin + // counting mask_i ones + inmask_ones = '0; + for (int i = 0 ; i < InW ; i++) begin + inmask_ones = inmask_ones + OnesCntW'(mask_i[i]); + end + end + + logic [PtrW-1:0] pos_with_input; + assign pos_with_input = pos_q + PtrW'(inmask_ones); + + if (EnProtection == 1'b 0) begin : g_pos_nodup + logic [PtrW-1:0] pos_d; + + always_comb begin + pos_d = pos_q; + + unique case ({ack_in, ack_out}) + 2'b00: pos_d = pos_q; + 2'b01: pos_d = (int'(pos_q) <= OutW) ? '0 : pos_q - PtrW'(OutW); + 2'b10: pos_d = pos_with_input; + 2'b11: pos_d = (int'(pos_with_input) <= OutW) ? '0 : pos_with_input - PtrW'(OutW); + default: pos_d = pos_q; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + pos_q <= '0; + end else if (flush_done) begin + pos_q <= '0; + end else begin + pos_q <= pos_d; + end + end + + assign err_o = 1'b 0; // No checker logic + + end else begin : g_pos_dupcnt // EnProtection == 1'b 1 + // incr_en: Increase the pos by cnt_step. ack_in && !ack_out + // decr_en: Decrease the pos by cnt_step. !ack_in && ack_out + // set_en: Set to specific value in case of ack_in && ack_out. + // This case, the value could be increased or descreased based on + // the input size (inmask_ones) + logic cnt_incr_en, cnt_decr_en, cnt_set_en; + logic [PtrW-1:0] cnt_step, cnt_set; + + assign cnt_incr_en = ack_in && !ack_out; + assign cnt_decr_en = !ack_in && ack_out; + assign cnt_set_en = ack_in && ack_out; + + // counter has underflow protection. + assign cnt_step = (cnt_incr_en) ? PtrW'(inmask_ones) : PtrW'(OutW); + + always_comb begin : cnt_set_logic + + // default, consuming all data + cnt_set = '0; + + if (pos_with_input > PtrW'(OutW)) begin + // pos_q + inmask_ones is bigger than Output width. Still data remained. + cnt_set = pos_with_input - PtrW'(OutW); + end + end : cnt_set_logic + + + prim_count #( + .Width (PtrW), + .ResetValue ('0 ) + ) u_pos ( + .clk_i, + .rst_ni, + + .clr_i (flush_done), + + .set_i (cnt_set_en), + .set_cnt_i (cnt_set ), + + .incr_en_i (cnt_incr_en), + .decr_en_i (cnt_decr_en), + .step_i (cnt_step ), + .commit_i (1'b1 ), + + .cnt_o (pos_q ), // Current counter state + .cnt_after_commit_o ( ), // Next counter state + + .err_o + ); + end // g_pos_dupcnt + + //--------------------------------------------------------------------------- + + // Leading one detector for mask_i + always_comb begin + lod_idx = 0; + for (int i = InW-1; i >= 0 ; i--) begin + if (mask_i[i] == 1'b1) begin + lod_idx = IdxW'(unsigned'(i)); + end + end + end + + assign ack_in = valid_i & ready_o; + assign ack_out = valid_o & ready_i; + + // Data process ============================================================= + // shiftr : Input data shifted right to put the leading one at bit zero + assign shiftr_data = (valid_i) ? data_i >> lod_idx : '0; + assign shiftr_mask = (valid_i) ? mask_i >> lod_idx : '0; + + // shiftl : Input data shifted into the current stored position + assign shiftl_data = ConcatW'(shiftr_data) << pos_q; + assign shiftl_mask = ConcatW'(shiftr_mask) << pos_q; + + // concat : Merging stored and shiftl + assign concat_data = {{(InW){1'b0}}, stored_data & stored_mask} | + (shiftl_data & shiftl_mask); + assign concat_mask = {{(InW){1'b0}}, stored_mask} | shiftl_mask; + + logic [Width-1:0] stored_data_next, stored_mask_next; + + always_comb begin + unique case ({ack_in, ack_out}) + 2'b 00: begin + stored_data_next = stored_data; + stored_mask_next = stored_mask; + end + 2'b 01: begin + // ack_out : shift the amount of OutW + stored_data_next = {{OutW{1'b0}}, stored_data[Width-1:OutW]}; + stored_mask_next = {{OutW{1'b0}}, stored_mask[Width-1:OutW]}; + end + 2'b 10: begin + // ack_in : Store concat data + stored_data_next = concat_data[0+:Width]; + stored_mask_next = concat_mask[0+:Width]; + end + 2'b 11: begin + // both : shift the concat_data + stored_data_next = concat_data[ConcatW-1:OutW]; + stored_mask_next = concat_mask[ConcatW-1:OutW]; + end + default: begin + stored_data_next = stored_data; + stored_mask_next = stored_mask; + end + endcase + end + + // Store the data temporary if it doesn't exceed OutW + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_data <= '0; + stored_mask <= '0; + end else if (flush_done) begin + stored_data <= '0; + stored_mask <= '0; + end else begin + stored_data <= stored_data_next; + stored_mask <= stored_mask_next; + end + end + //--------------------------------------------------------------------------- + + // flush handling + typedef enum logic { + FlushIdle, + FlushSend + } flush_st_e; + flush_st_e flush_st, flush_st_next; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + flush_st <= FlushIdle; + end else begin + flush_st <= flush_st_next; + end + end + + always_comb begin + flush_st_next = FlushIdle; + + flush_valid = 1'b0; + flush_done = 1'b0; + + unique case (flush_st) + FlushIdle: begin + if (flush_i) begin + flush_st_next = FlushSend; + end else begin + flush_st_next = FlushIdle; + end + end + + FlushSend: begin + if (pos_q == '0) begin + flush_st_next = FlushIdle; + + flush_valid = 1'b 0; + flush_done = 1'b 1; + end else begin + flush_st_next = FlushSend; + + flush_valid = 1'b 1; + flush_done = 1'b 0; + end + end + default: begin + flush_st_next = FlushIdle; + + flush_valid = 1'b 0; + flush_done = 1'b 0; + end + endcase + end + + assign flush_done_o = flush_done; + + + // Output signals =========================================================== + assign valid_next = (int'(pos_q) >= OutW) ? 1'b 1 : flush_valid; + + // storage space is InW + OutW. So technically, ready_o can be asserted even + // if `pos_q` is greater than OutW. But in order to do that, the logic should + // use `inmask_ones` value whether pos_q+inmask_ones is less than (InW+OutW) + // with `valid_i`. It creates a path from `valid_i` --> `ready_o`. + // It may create a timing loop in some modules that use `ready_o` to + // `valid_i` (which is not a good practice though) + assign ready_next = int'(pos_q) <= OutW; + + // Output request + assign valid_o = valid_next; + assign data_o = stored_data[OutW-1:0]; + assign mask_o = stored_mask[OutW-1:0]; + + // ready_o + assign ready_o = ready_next; + //--------------------------------------------------------------------------- + + ////////////////////////////////////////////// + // Assertions, Assumptions, and Coverpoints // + ////////////////////////////////////////////// + // Assumption: mask_i should be contiguous ones + // e.g: 0011100 --> OK + // 0100011 --> Not OK + if (InW > 1) begin : gen_mask_assert + `ASSUME(ContiguousOnesMask_M, + valid_i |-> $countones(mask_i ^ {mask_i[InW-2:0],1'b0}) <= 2) + end + + // Flush and Write Enable cannot be asserted same time + `ASSUME(ExFlushValid_M, flush_i |-> !valid_i) + + // While in flush state, new request shouldn't come + `ASSUME(ValidIDeassertedOnFlush_M, + flush_st == FlushSend |-> $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)) + + `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 stored data is greater than the output width, valid should be asserted + `ASSERT(ValidOAssertedForStoredDataGTEOutW_A, + ($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 + `ASSERT(ExcessiveDataStored_A, + ack_in && ack_out && (($countones(mask_i) + $countones(stored_mask)) > OutW) + |=> (($past(data_i) & $past(mask_i)) >> + ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + == stored_data) + + `ASSERT(ExcessiveMaskStored_A, + ack_in && ack_out && (($countones(mask_i) + $countones(stored_mask)) > OutW) + |=> ($past(mask_i) >> + ($past(lod_idx)+OutW-$countones($past(stored_mask)))) + == stored_mask) + + // Assertions for byte hint enabled + if (HintByteData != 0) begin : g_byte_assert + `ASSERT_INIT(InputDividedBy8_A, InW % 8 == 0) + `ASSERT_INIT(OutputDividedBy8_A, OutW % 8 == 0) + + // Masking[8*i+:8] should be all zero or all one + for (genvar i = 0 ; i < InW/8 ; i++) begin : g_byte_input_masking + `ASSERT(InputMaskContiguous_A, + valid_i |-> (|mask_i[8*i+:8] == 1'b 0) + || (&mask_i[8*i+:8] == 1'b 1)) + end + for (genvar i = 0 ; i < OutW/8 ; i++) begin : g_byte_output_masking + `ASSERT(OutputMaskContiguous_A, + valid_o |-> (|mask_o[8*i+:8] == 1'b 0) + || (&mask_o[8*i+:8] == 1'b 1)) + end + end +endmodule diff --git a/synth/prim/rtl/prim_packer_fifo.sv b/synth/prim/rtl/prim_packer_fifo.sv new file mode 100755 index 0000000..95c143d --- /dev/null +++ b/synth/prim/rtl/prim_packer_fifo.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 +// +// Supports packed and unpacked modes +// Uses FIFO timing on the control signals +// No masking or flush functions supported + +// Timings - case where InW < OutW +// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__ +// wvalid_i _____|~~~~|_____|~~~~|_____|~~~~|_____|~~~~|___________________ +// wdata_i Val N |Val N+1 |Val N+2 |Val N+3 |------------------- +// wready_o ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|__________|~~~~~~~~ +// rvalid_o ___________________________________________|~~~~~~~~~~|________ +// rdata_o -------------------------------------------|Val |-------- +// rready_i _________________________________________________|~~~~|________ +// depth_o 0000000000|1111111111|2222222222|3333333333|4444444444|00000000 + + +// Timings - case where InW > OutW +// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__ +// wvalid_i _____|~~~~|____________________________________________________ +// wdata_i -----|Val |---------------------------------------------------- +// wready_o ~~~~~~~~~~|___________________________________________|~~~~~~~~ +// rvalid_o __________|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|________ +// rdata_o ----------|Val N |Val N+1 |Val N+2 |Val N+3 |-------- +// rready_i ________________|~~~~|_____|~~~~|_____|~~~~|_____|~~~~|________ +// depth_o 0000000000|4444444444|3333333333|2222222222|1111111111|00000000 + + +// Timings - case where InW = OutW +// clk_i __|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__~~|__|~~|__ +// wvalid_i _____|~~~~|____________________________________________________ +// wdata_i -----|Val |---------------------------------------------------- +// wready_o ~~~~~~~~~~|__________|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// rvalid_o __________|~~~~~~~~~~|_________________________________________ +// rdata_o ----------|Val |----------------------------------------- +// rready_i ________________|~~~~|_________________________________________ +// depth_o 0000000000|1111111111|00000000000000000000000000000000000000000 + + +`include "prim_assert.sv" + +module prim_packer_fifo #( + parameter int InW = 32, + parameter int OutW = 8, + parameter bit ClearOnRead = 1'b1, // if == 1 always output 0 after read + // derived parameters + localparam int MaxW = (InW > OutW) ? InW : OutW, + localparam int MinW = (InW < OutW) ? InW : OutW, + localparam int DepthW = $clog2(MaxW/MinW) +) ( + input logic clk_i , + input logic rst_ni, + + input logic clr_i, + input logic wvalid_i, + input logic [InW-1:0] wdata_i, + output logic wready_o, + + output logic rvalid_o, + output logic [OutW-1:0] rdata_o, + input logic rready_i, + output logic [DepthW:0] depth_o +); + + localparam int unsigned WidthRatio = MaxW / MinW; + localparam bit [DepthW:0] FullDepth = WidthRatio[DepthW:0]; + + // signals + logic load_data; + logic clear_data; + logic clear_status; + + // flops + logic [DepthW:0] depth_q, depth_d; + logic [MaxW-1:0] data_q, data_d; + logic clr_q, clr_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + depth_q <= '0; + data_q <= '0; + clr_q <= 1'b1; + end else begin + depth_q <= depth_d; + data_q <= data_d; + clr_q <= clr_d; + end + end + + // flop for handling reset case for clr + assign clr_d = clr_i; + + assign depth_o = depth_q; + + if (InW < OutW) begin : gen_pack_mode + logic [MaxW-1:0] wdata_shifted; + + assign wdata_shifted = {{OutW - InW{1'b0}}, wdata_i} << (depth_q*InW); + assign clear_status = (rready_i && rvalid_o) || clr_q; + assign clear_data = (ClearOnRead && clear_status) || clr_q; + assign load_data = wvalid_i && wready_o; + + assign depth_d = clear_status ? '0 : + load_data ? depth_q+1 : + depth_q; + + assign data_d = clear_data ? '0 : + load_data ? (wdata_shifted | (depth_q == 0 ? '0 : data_q)) : + data_q; + + // set outputs + assign wready_o = !(depth_q == FullDepth) && !clr_q; + assign rdata_o = data_q; + assign rvalid_o = (depth_q == FullDepth) && !clr_q; + + end else begin : gen_unpack_mode + logic [MaxW-1:0] rdata_shifted; + logic pull_data; + logic [DepthW:0] ptr_q, ptr_d; + logic [DepthW:0] lsb_is_one; + logic [DepthW:0] max_value; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ptr_q <= '0; + end else begin + ptr_q <= ptr_d; + end + end + + assign lsb_is_one = {{DepthW{1'b0}},1'b1}; + assign max_value = FullDepth; + assign rdata_shifted = data_q >> ptr_q*OutW; + assign clear_status = (rready_i && (depth_q == lsb_is_one)) || clr_q; + assign clear_data = (ClearOnRead && clear_status) || clr_q; + assign load_data = wvalid_i && wready_o; + assign pull_data = rvalid_o && rready_i; + + assign depth_d = clear_status ? '0 : + load_data ? max_value : + pull_data ? depth_q-1 : + depth_q; + + assign ptr_d = clear_status ? '0 : + pull_data ? ptr_q+1 : + ptr_q; + + assign data_d = clear_data ? '0 : + load_data ? wdata_i : + data_q; + + // set outputs + assign wready_o = (depth_q == '0) && !clr_q; + assign rdata_o = rdata_shifted[OutW-1:0]; + assign rvalid_o = !(depth_q == '0) && !clr_q; + + // Avoid possible lint errors in case InW > OutW. + if (InW > OutW) begin : gen_unused + logic [MaxW-MinW-1:0] unused_rdata_shifted; + assign unused_rdata_shifted = rdata_shifted[MaxW-1:MinW]; + end + end + + + ////////////////////////////////////////////// + // Assertions, Assumptions, and Coverpoints // + ////////////////////////////////////////////// + + // If not acked, valid_o should keep asserting + `ASSERT(ValidOPairedWithReadyI_A, + rvalid_o && !rready_i && !clr_i |=> rvalid_o) + + // If output port doesn't accept the data, the data should be stable + `ASSERT(DataOStableWhenPending_A, + ##1 rvalid_o && $past(rvalid_o) + && !$past(rready_i) && !$past(clr_i) |-> $stable(rdata_o)) + +endmodule diff --git a/synth/prim/rtl/prim_pad_wrapper_pkg.sv b/synth/prim/rtl/prim_pad_wrapper_pkg.sv new file mode 100755 index 0000000..05acab6 --- /dev/null +++ b/synth/prim/rtl/prim_pad_wrapper_pkg.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 + +package prim_pad_wrapper_pkg; + + typedef enum logic [2:0] { + BidirStd = 3'h0, // Standard bidirectional pad + BidirTol = 3'h1, // Voltage tolerant pad + BidirOd = 3'h2, // Open-drain capable pad + InputStd = 3'h3, // Input-only pad + AnalogIn0 = 3'h4, // Analog input pad + AnalogIn1 = 3'h5, // Analog input pad + DualBidirTol = 3'h6 // Dual Voltage tolerant pad + } pad_type_e; + + typedef enum logic [1:0] { + NoScan = 2'h0, + ScanIn = 2'h1, + ScanOut = 2'h2 + } scan_role_e; + + // Pad attributes + parameter int DriveStrDw = 4; + parameter int SlewRateDw = 2; + + typedef struct packed { + logic [DriveStrDw-1:0] drive_strength; // Drive strength (0000: weakest, 1111: strongest). + logic [SlewRateDw-1:0] slew_rate; // Slew rate (0: slowest, 11: fastest). + logic od_en; // Open-drain enable + logic schmitt_en; // Schmitt trigger enable. + logic keep_en; // Keeper enable. + logic pull_select; // Pull direction (0: pull down, 1: pull up). + logic pull_en; // Pull enable. + logic virt_od_en; // Virtual open drain enable. + logic invert; // Input/output inversion. + } pad_attr_t; + + parameter int AttrDw = $bits(pad_attr_t); + + // Power OK signals (library dependent) + parameter int PokDw = 8; + + typedef logic [PokDw-1:0] pad_pok_t; + +endpackage : prim_pad_wrapper_pkg diff --git a/synth/prim/rtl/prim_present.sv b/synth/prim/rtl/prim_present.sv new file mode 100755 index 0000000..76c03bc --- /dev/null +++ b/synth/prim/rtl/prim_present.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 +// +// This module is an implementation of the encryption pass of the 64bit PRESENT +// block cipher. It is a fully unrolled combinational implementation that +// supports both key sizes specified in the paper (80bit and 128bit). Further, +// the number of rounds is fully configurable, and the primitive supports a +// 32bit block cipher flavor which is not specified in the original paper. It +// should be noted, however, that the 32bit version is **not** secure and must +// not be used in a setting where cryptographic cipher strength is required. The +// 32bit variant is only intended to be used as a lightweight data scrambling +// device. +// +// See also: prim_prince, prim_cipher_pkg +// +// References: - https://en.wikipedia.org/wiki/PRESENT +// - https://en.wikipedia.org/wiki/Prince_(cipher) +// - http://www.lightweightcrypto.org/present/present_ches2007.pdf +// - https://eprint.iacr.org/2012/529.pdf +// - https://csrc.nist.gov/csrc/media/events/lightweight-cryptography-workshop-2015/ +// documents/papers/session7-maene-paper.pdf + +`include "prim_assert.sv" +module prim_present #( + parameter int DataWidth = 64, // {32, 64} + parameter int KeyWidth = 128, // {64, 80, 128} + // Number of rounds to perform in total (>0) + parameter int NumRounds = 31, + // Number of physically instantiated PRESENT rounds. + // This can be used to construct e.g. an iterative + // full-round implementation that only has one physical + // round instance by setting NumRounds = 31 and NumPhysRounds = 1. + // Note that NumPhysRounds needs to divide NumRounds. + parameter int NumPhysRounds = NumRounds, + // Note that the decryption pass needs a modified key, + // to be calculated by performing NumRounds key updates + parameter bit Decrypt = 0 // 0: encrypt, 1: decrypt +) ( + input [DataWidth-1:0] data_i, + input [KeyWidth-1:0] key_i, + // Starting round index for keyschedule [1 ... 31]. + // Set this to 5'd1 for a fully unrolled encryption, and 5'd31 for a fully unrolled decryption. + input [4:0] idx_i, + output logic [DataWidth-1:0] data_o, + output logic [KeyWidth-1:0] key_o, + // Next round index for keyschedule + // (Enc: idx_i + NumPhysRounds, Dec: idx_i - NumPhysRounds) + // Can be ignored for a fully unrolled implementation. + output logic [4:0] idx_o +); + + ////////////// + // datapath // + ////////////// + + logic [NumPhysRounds:0][DataWidth-1:0] data_state; + logic [NumPhysRounds:0][KeyWidth-1:0] round_key; + logic [NumPhysRounds:0][4:0] round_idx; + + // initialize + assign data_state[0] = data_i; + assign round_key[0] = key_i; + assign round_idx[0] = idx_i; + + for (genvar k = 0; k < NumPhysRounds; k++) begin : gen_round + logic [DataWidth-1:0] data_state_xor, data_state_sbox; + // cipher layers + assign data_state_xor = data_state[k] ^ round_key[k][KeyWidth-1 : KeyWidth-DataWidth]; + //////////////////////////////// + // decryption pass, performs inverse permutation, sbox and keyschedule + if (Decrypt) begin : gen_dec + // Decrement round count. + assign round_idx[k+1] = round_idx[k] - 1'b1; + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = 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], + round_idx[k]); + // 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], + round_idx[k]); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = prim_cipher_pkg::present_inv_update_key64(round_key[k], + round_idx[k]); + end + //////////////////////////////// + // encryption pass + end else begin : gen_enc + // Increment round count. + assign round_idx[k+1] = round_idx[k] + 1'b1; + // original 64bit variant + if (DataWidth == 64) begin : gen_d64 + assign data_state_sbox = 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], round_idx[k]); + // 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], round_idx[k]); + // reduced 64bit key variant + end else begin : gen_k64 + assign round_key[k+1] = prim_cipher_pkg::present_update_key64(round_key[k], round_idx[k]); + end + end // gen_enc + //////////////////////////////// + end // gen_round + + // This only needs to be applied after the last round. + // Note that for a full-round implementation the output index + // will be 0 for enc/dec for the last round (either due to wraparound or subtraction). + localparam int LastRoundIdx = (Decrypt != 0 || NumRounds == 31) ? 0 : NumRounds+1; + assign data_o = (int'(idx_o) == LastRoundIdx) ? + data_state[NumPhysRounds] ^ + round_key[NumPhysRounds][KeyWidth-1 : KeyWidth-DataWidth] : + data_state[NumPhysRounds]; + + assign key_o = round_key[NumPhysRounds]; + assign idx_o = round_idx[NumPhysRounds]; + + //////////////// + // assertions // + //////////////// + + `ASSERT_INIT(SupportedWidths_A, (DataWidth == 64 && KeyWidth inside {80, 128}) || + (DataWidth == 32 && KeyWidth == 64)) + `ASSERT_INIT(SupportedNumRounds_A, NumRounds > 0 && NumRounds <= 31) + `ASSERT_INIT(SupportedNumPhysRounds0_A, NumPhysRounds > 0 && NumPhysRounds <= NumRounds) + // Currently we do not support other arrangements + `ASSERT_INIT(SupportedNumPhysRounds1_A, (NumRounds % NumPhysRounds) == 0) + +endmodule : prim_present diff --git a/synth/prim/rtl/prim_prince.sv b/synth/prim/rtl/prim_prince.sv new file mode 100755 index 0000000..779999c --- /dev/null +++ b/synth/prim/rtl/prim_prince.sv @@ -0,0 +1,242 @@ +// 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. Optionally, registers for the +// data and key states can be enabled, if this is required. 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 + +`include "prim_assert.sv" +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, + // This instantiates a data register halfway in the primitive. + parameter bit HalfwayDataReg = 1'b0, + // This instantiates a key register halfway in the primitive. + parameter bit HalfwayKeyReg = 1'b0 +) ( + input clk_i, + input rst_ni, + + input valid_i, + input [DataWidth-1:0] data_i, + input [KeyWidth-1:0] key_i, + input dec_i, // set to 1 for decryption + output logic valid_o, + output logic [DataWidth-1:0] data_o +); + + /////////////////// + // key expansion // + /////////////////// + + logic [DataWidth-1:0] k0, k0_prime_d, k1_d, k0_new_d, k0_prime_q, k1_q, k0_new_q; + always_comb begin : p_key_expansion + k0 = key_i[2*DataWidth-1 : DataWidth]; + k0_prime_d = {k0[0], k0[DataWidth-1:2], k0[DataWidth-1] ^ k0[1]}; + k1_d = key_i[DataWidth-1:0]; + + // modify key for decryption + if (dec_i) begin + k0 = k0_prime_d; + k0_prime_d = key_i[2*DataWidth-1 : DataWidth]; + k1_d ^= prim_cipher_pkg::PRINCE_ALPHA_CONST[DataWidth-1:0]; + end + end + + if (UseOldKeySched) begin : gen_legacy_keyschedule + // In this case we constantly use k1. + assign k0_new_d = k1_d; + end else begin : gen_new_keyschedule + // Imroved keyschedule proposed by https://eprint.iacr.org/2014/656.pdf + // In this case we alternate between k1 and k0. + always_comb begin : p_new_keyschedule_k0_alpha + k0_new_d = key_i[2*DataWidth-1 : DataWidth]; + // We need to apply the alpha constant here as well, just as for k1 in decryption mode. + if (dec_i) begin + k0_new_d ^= prim_cipher_pkg::PRINCE_ALPHA_CONST[DataWidth-1:0]; + end + end + end + + if (HalfwayKeyReg) begin : gen_key_reg + always_ff @(posedge clk_i or negedge rst_ni) begin : p_key_reg + if (!rst_ni) begin + k1_q <= '0; + k0_prime_q <= '0; + k0_new_q <= '0; + end else begin + if (valid_i) begin + k1_q <= k1_d; + k0_prime_q <= k0_prime_d; + k0_new_q <= k0_new_d; + end + end + end + end else begin : gen_no_key_reg + // just pass the key through in this case + assign k1_q = k1_d; + assign k0_prime_q = k0_prime_d; + assign k0_new_q = k0_new_d; + end + + ////////////// + // datapath // + ////////////// + + // State variable for holding the rounds + logic [NumRoundsHalf:0][DataWidth-1:0] data_state_lo; + logic [NumRoundsHalf:0][DataWidth-1:0] data_state_hi; + + // pre-round XOR + always_comb begin : p_pre_round_xor + data_state_lo[0] = data_i ^ k0; + data_state_lo[0] ^= k1_d; + data_state_lo[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_lo[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_lo[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) begin : gen_fwd_key_odd + assign data_state_lo[k] = data_state_xor ^ k0_new_d; + end else begin : gen_fwd_key_even + assign data_state_lo[k] = data_state_xor ^ k1_d; + end + end + + // middle part + logic [DataWidth-1:0] data_state_middle_d, data_state_middle_q, data_state_middle; + if (DataWidth == 64) begin : gen_middle_d64 + always_comb begin : p_middle_d64 + data_state_middle_d = prim_cipher_pkg::sbox4_64bit(data_state_lo[NumRoundsHalf], + prim_cipher_pkg::PRINCE_SBOX4); + data_state_middle = prim_cipher_pkg::prince_mult_prime_64bit(data_state_middle_q); + 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_d = 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_q); + data_state_middle = prim_cipher_pkg::sbox4_32bit(data_state_middle, + prim_cipher_pkg::PRINCE_SBOX4_INV); + end + end + + if (HalfwayDataReg) begin : gen_data_reg + logic valid_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : p_data_reg + if (!rst_ni) begin + valid_q <= 1'b0; + data_state_middle_q <= '0; + end else begin + valid_q <= valid_i; + if (valid_i) begin + data_state_middle_q <= data_state_middle_d; + end + end + end + assign valid_o = valid_q; + end else begin : gen_no_data_reg + // just pass data through in this case + assign data_state_middle_q = data_state_middle_d; + assign valid_o = valid_i; + end + + assign data_state_hi[0] = 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 ((NumRoundsHalf + k + 1) % 2 == 1) begin : gen_bkwd_key_odd + assign data_state_xor0 = data_state_hi[k-1] ^ k0_new_q; + end else begin : gen_bkwd_key_even + assign data_state_xor0 = data_state_hi[k-1] ^ k1_q; + end + // 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_hi[k] = 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_hi[k] = 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_hi[NumRoundsHalf] ^ + prim_cipher_pkg::PRINCE_ROUND_CONST[11][DataWidth-1:0]; + data_o ^= k1_q; + data_o ^= k0_prime_q; + 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/synth/prim/rtl/prim_pulse_sync.sv b/synth/prim/rtl/prim_pulse_sync.sv new file mode 100755 index 0000000..4ce7a7e --- /dev/null +++ b/synth/prim/rtl/prim_pulse_sync.sv @@ -0,0 +1,98 @@ +// 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 + + + // source active must come far enough such that the destination domain has time + // to create a valid pulse. +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + // source active flag tracks whether there is an ongoing "toggle" event. + // Until this toggle event is accepted by the destination domain (negative edge of + // of the pulse output), the source side cannot toggle again. + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && dst_pulse_o; + + logic src_active_flag_d, src_active_flag_q; + assign src_active_flag_d = src_pulse_i || src_active_flag_q; + + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + src_active_flag_q <= '0; + end else begin + src_active_flag_q <= src_active_flag_d; + end + end + + //VCS coverage on + // pragma coverage on + + `ASSERT(SrcPulseCheck_M, src_pulse_i |-> !src_active_flag_q, clk_src_i, !rst_src_ni) +`endif + + ////////////////////////////////////////////////////////// + // synchronize level signal to destination clock domain // + ////////////////////////////////////////////////////////// + logic dst_level; + + prim_flop_2sync #(.Width(1)) prim_flop_2sync ( + // source clock domain + .d_i (src_level), + // destination clock domain + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .q_o (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; + + `ASSERT(DstPulseCheck_A, dst_pulse_o |=> !dst_pulse_o, clk_dst_i, !rst_dst_ni) + +endmodule diff --git a/synth/prim/rtl/prim_ram_1p_adv.sv b/synth/prim/rtl/prim_ram_1p_adv.sv new file mode 100755 index 0000000..71bd99b --- /dev/null +++ b/synth/prim/rtl/prim_ram_1p_adv.sv @@ -0,0 +1,269 @@ +// 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 +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "prim_assert.sv" + +module prim_ram_1p_adv import prim_ram_1p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = prim_util_pkg::vbits(Depth) +) ( + input clk_i, + input rst_ni, + + input req_i, + input write_i, + input [Aw-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, + output logic rvalid_o, // read response (rdata_o) is valid + output logic [1:0] rerror_o, // Bit1: Uncorrectable, Bit0: Correctable + + // config + input ram_1p_cfg_t cfg_i +); + + + `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? Width/8 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + // If byte parity is enabled, the write enable bits are used to write memory colums + // with 8 + 1 = 9 bit width (data plus corresponding parity bit). + // If ECC is enabled, the DataBitsPerMask is ignored. + localparam int LocalDataBitsPerMask = (EnableParity) ? 9 : + (EnableECC) ? TotalWidth : + DataBitsPerMask; + + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// + + logic req_q, req_d ; + logic write_q, write_d ; + logic [Aw-1:0] addr_q, addr_d ; + logic [TotalWidth-1:0] wdata_q, wdata_d ; + logic [TotalWidth-1:0] wmask_q, wmask_d ; + logic rvalid_q, rvalid_d, rvalid_sram_q ; + logic [Width-1:0] rdata_q, rdata_d ; + logic [TotalWidth-1:0] rdata_sram ; + logic [1:0] rerror_q, rerror_d ; + + prim_ram_1p #( + .MemInitFile (MemInitFile), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (LocalDataBitsPerMask) + ) u_mem ( + .clk_i, + + .req_i (req_q), + .write_i (write_q), + .addr_i (addr_q), + .wdata_i (wdata_q), + .wmask_i (wmask_q), + .rdata_o (rdata_sram), + .cfg_i + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_sram_q <= 1'b0; + end else begin + rvalid_sram_q <= req_q & ~write_q; + end + end + + assign req_d = req_i; + assign write_d = write_i; + assign addr_d = addr_i; + assign rvalid_o = rvalid_q; + assign rdata_o = rdata_q; + assign rerror_o = rerror_q; + + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// + + if (EnableParity == 0 && EnableECC) begin : gen_secded + logic unused_wmask; + assign unused_wmask = ^wmask_i; + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {16, 32}) + + // the wmask is constantly set to 1 in this case + `ASSERT(OnlyWordWritePossibleWithEccPortA_A, req_i |-> + wmask_i == {Width{1'b1}}) + + assign wmask_d = {TotalWidth{1'b1}}; + + if (Width == 16) begin : gen_secded_22_16 + if (HammingECC) begin : gen_hamming + prim_secded_inv_hamming_22_16_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + prim_secded_inv_hamming_22_16_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end else begin : gen_hsiao + prim_secded_inv_22_16_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + prim_secded_inv_22_16_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end + end else if (Width == 32) begin : gen_secded_39_32 + if (HammingECC) begin : gen_hamming + prim_secded_inv_hamming_39_32_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + prim_secded_inv_hamming_39_32_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end else begin : gen_hsiao + prim_secded_inv_39_32_enc u_enc ( + .data_i(wdata_i), + .data_o(wdata_d) + ); + prim_secded_inv_39_32_dec u_dec ( + .data_i (rdata_sram), + .data_o (rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (rerror_d) + ); + end + end + + end else if (EnableParity) begin : gen_byte_parity + + `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + + always_comb begin : p_parity + rerror_d = '0; + for (int i = 0; i < Width/8; i ++) begin + // Data mapping. We have to make 8+1 = 9 bit groups + // that have the same write enable such that FPGA tools + // can map this correctly to BRAM resources. + wmask_d[i*9 +: 8] = wmask_i[i*8 +: 8]; + wdata_d[i*9 +: 8] = wdata_i[i*8 +: 8]; + rdata_d[i*8 +: 8] = rdata_sram[i*9 +: 8]; + + // parity generation (odd parity) + wdata_d[i*9 + 8] = ~(^wdata_i[i*8 +: 8]); + wmask_d[i*9 + 8] = &wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + rerror_d[1] |= ~(^{rdata_sram[i*9 +: 8], rdata_sram[i*9 + 8]}); + end + end + end else begin : gen_nosecded_noparity + assign wmask_d = wmask_i; + assign wdata_d = wdata_i; + + assign rdata_d = rdata_sram[0+:Width]; + assign rerror_d = '0; + end + + assign rvalid_d = rvalid_sram_q; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + req_q <= '0; + write_q <= '0; + addr_q <= '0; + wdata_q <= '0; + wmask_q <= '0; + end else begin + req_q <= req_d; + write_q <= write_d; + addr_q <= addr_d; + wdata_q <= wdata_d; + wmask_q <= wmask_d; + end + end + end else begin : gen_dirconnect_input + assign req_q = req_d; + assign write_q = write_d; + assign addr_q = addr_d; + assign wdata_q = wdata_d; + assign wmask_q = wmask_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_q <= '0; + rdata_q <= '0; + rerror_q <= '0; + end else begin + rvalid_q <= rvalid_d; + rdata_q <= rdata_d; + // tie to zero if the read data is not valid + rerror_q <= rerror_d & {2{rvalid_d}}; + end + end + end else begin : gen_dirconnect_output + assign rvalid_q = rvalid_d; + assign rdata_q = rdata_d; + // tie to zero if the read data is not valid + assign rerror_q = rerror_d & {2{rvalid_d}}; + end + +endmodule : prim_ram_1p_adv diff --git a/synth/prim/rtl/prim_ram_1p_pkg.sv b/synth/prim/rtl/prim_ram_1p_pkg.sv new file mode 100755 index 0000000..d479629 --- /dev/null +++ b/synth/prim/rtl/prim_ram_1p_pkg.sv @@ -0,0 +1,20 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +package prim_ram_1p_pkg; + + typedef struct packed { + logic cfg_en; + logic [3:0] cfg; + } cfg_t; + + typedef struct packed { + cfg_t ram_cfg; // configuration for ram + cfg_t rf_cfg; // configuration for regfile + } ram_1p_cfg_t; + + parameter ram_1p_cfg_t RAM_1P_CFG_DEFAULT = '0; + +endpackage // prim_ram_1p_pkg diff --git a/synth/prim/rtl/prim_ram_1p_scr.sv b/synth/prim/rtl/prim_ram_1p_scr.sv new file mode 100755 index 0000000..36f786a --- /dev/null +++ b/synth/prim/rtl/prim_ram_1p_scr.sv @@ -0,0 +1,405 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// This is a draft implementation of a low-latency memory scrambling mechanism. +// +// The module is implemented as a primitive, in the same spirit as similar prim_ram_1p_adv wrappers. +// Hence, it can be conveniently instantiated by comportable IPs (such as OTBN) or in top_earlgrey +// for the main system memory. +// +// The currently implemented architecture uses a reduced-round PRINCE cipher primitive in CTR mode +// in order to (weakly) scramble the data written to the memory macro. Plain CTR mode does not +// diffuse the data since the keystream is just XOR'ed onto it, hence we also we perform byte-wise +// diffusion using a (shallow) substitution/permutation network layers in order to provide a limited +// avalanche effect within a byte. +// +// In order to break the linear addressing space, the address is passed through a bijective +// scrambling function constructed using a (shallow) substitution/permutation and a nonce. Due to +// that nonce, the address mapping is not fully baked into RTL and can be changed at runtime as +// well. +// +// See also: prim_cipher_pkg, prim_prince + +`include "prim_assert.sv" + +module prim_ram_1p_scr import prim_ram_1p_pkg::*; #( + parameter int Depth = 16*1024, // Needs to be a power of 2 if NumAddrScrRounds > 0. + parameter int Width = 32, // Needs to be byte aligned if byte parity is enabled. + parameter int DataBitsPerMask = 8, // Needs to be set to 8 in case of byte parity. + parameter bit EnableParity = 1, // Enable byte parity. + + // Scrambling parameters. Note that this needs to be low-latency, hence we have to keep the + // amount of cipher rounds low. PRINCE has 5 half rounds in its original form, which corresponds + // to 2*5 + 1 effective rounds. Setting this to 2 halves this to approximately 5 effective rounds. + // Number of PRINCE half rounds, can be [1..5] + parameter int NumPrinceRoundsHalf = 2, + // Number of extra diffusion rounds. Setting this to 0 to disables diffusion. + // NOTE: this is zero by default, since the non-linear transformation of data bits can interact + // adversely with end-to-end ECC integrity. Only enable this if you know what you are doing + // (e.g. using this primitive in a different context with byte parity). See #20788 for context. + parameter int NumDiffRounds = 0, + // This parameter governs the block-width of additional diffusion layers. + // For intra-byte diffusion, set this parameter to 8. + parameter int DiffWidth = DataBitsPerMask, + // Number of address scrambling rounds. Setting this to 0 disables address scrambling. + parameter int NumAddrScrRounds = 2, + // If set to 1, the same 64bit key stream is replicated if the data port is wider than 64bit. + // If set to 0, the cipher primitive is replicated, and together with a wider nonce input, + // a unique keystream is generated for the full data width. + parameter bit ReplicateKeyStream = 1'b0, + // Derived parameters + localparam int AddrWidth = prim_util_pkg::vbits(Depth), + // Depending on the data width, we need to instantiate multiple parallel cipher primitives to + // create a keystream that is wide enough (PRINCE has a block size of 64bit) + localparam int NumParScr = (ReplicateKeyStream) ? 1 : (Width + 63) / 64, + localparam int NumParKeystr = (ReplicateKeyStream) ? (Width + 63) / 64 : 1, + // This is given by the PRINCE cipher primitive. All parallel cipher modules + // use the same key, but they use a different IV + localparam int DataKeyWidth = 128, + // Each 64 bit scrambling primitive requires a 64bit IV + localparam int NonceWidth = 64 * NumParScr +) ( + input clk_i, + input rst_ni, + + // Key interface. Memory requests will not be granted if key_valid is set to 0. + input key_valid_i, + input [DataKeyWidth-1:0] key_i, + input [NonceWidth-1:0] nonce_i, + + // Interface to TL-UL SRAM adapter + input req_i, + output logic gnt_o, + input write_i, + input [AddrWidth-1:0] addr_i, + input [Width-1:0] wdata_i, + input [Width-1:0] wmask_i, // Needs to be byte-aligned for parity + // On integrity errors, the primitive surpresses any real transaction to the memory. + input intg_error_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 + output logic [31:0] raddr_o, // Read address for error reporting. + + // config + input ram_1p_cfg_t cfg_i +); + + ////////////////////// + // Parameter Checks // + ////////////////////// + + // The depth needs to be a power of 2 in case address scrambling is turned on + `ASSERT_INIT(DepthPow2Check_A, NumAddrScrRounds <= '0 || 2**$clog2(Depth) == Depth) + `ASSERT_INIT(DiffWidthMinimum_A, DiffWidth >= 4) + `ASSERT_INIT(DiffWidthWithParity_A, EnableParity && (DiffWidth == 8) || !EnableParity) + + ///////////////////////////////////////// + // Pending Write and Address Registers // + ///////////////////////////////////////// + + // Writes are delayed by one cycle, such the same keystream generation primitive (prim_prince) can + // be reused among reads and writes. Note however that with this arrangement, we have to introduce + // a mechanism to hold a pending write transaction in cases where that transaction is immediately + // followed by a read. The pending write transaction is written to memory as soon as there is no + // new read transaction incoming. The latter can be a special case if the incoming read goes to + // the same address as the pending write. To that end, we detect the address collision and return + // the data from the write holding register. + + // Read / write strobes + logic read_en, write_en_d, write_en_q; + assign gnt_o = req_i & key_valid_i; + + assign read_en = gnt_o & ~write_i; + assign write_en_d = gnt_o & write_i; + + logic write_pending_q; + logic addr_collision_d, addr_collision_q; + logic [AddrWidth-1:0] addr_scr; + logic [AddrWidth-1:0] waddr_scr_q; + assign addr_collision_d = read_en & (write_en_q | write_pending_q) & (addr_scr == waddr_scr_q); + + // Macro requests and write strobe + // The macro operation is silenced if an integrity error is seen + logic intg_error_buf, intg_error_w_q; + prim_buf u_intg_error ( + .in_i(intg_error_i), + .out_o(intg_error_buf) + ); + logic macro_req; + assign macro_req = ~intg_error_w_q & ~intg_error_buf & (read_en | write_en_q | write_pending_q); + // We are allowed to write a pending write transaction to the memory if there is no incoming read. + logic macro_write; + assign macro_write = (write_en_q | write_pending_q) & ~read_en & ~intg_error_w_q; + // New read write collision + logic rw_collision; + assign rw_collision = write_en_q & read_en; + + //////////////////////// + // Address Scrambling // + //////////////////////// + + // We only select the pending write address in case there is no incoming read transaction. + logic [AddrWidth-1:0] addr_mux; + assign addr_mux = (read_en) ? addr_scr : waddr_scr_q; + + // This creates a bijective address mapping using a substitution / permutation network. + if (NumAddrScrRounds > 0) begin : gen_addr_scr + logic [AddrWidth-1:0] addr_scr_nonce; + assign addr_scr_nonce = nonce_i[NonceWidth - AddrWidth +: AddrWidth]; + + prim_subst_perm #( + .DataWidth ( AddrWidth ), + .NumRounds ( NumAddrScrRounds ), + .Decrypt ( 0 ) + ) u_prim_subst_perm ( + .data_i ( addr_i ), + // Since the counter mode concatenates {nonce_i[NonceWidth-1-AddrWidth:0], addr} to form + // the IV, the upper AddrWidth bits of the nonce are not used and can be used for address + // scrambling. In cases where N parallel PRINCE blocks are used due to a data + // width > 64bit, N*AddrWidth nonce bits are left dangling. + .key_i ( addr_scr_nonce ), + .data_o ( addr_scr ) + ); + end else begin : gen_no_addr_scr + assign addr_scr = addr_i; + end + + // We latch the non-scrambled address for error reporting. + logic [AddrWidth-1:0] raddr_q; + assign raddr_o = 32'(raddr_q); + + ////////////////////////////////////////////// + // Keystream Generation for Data Scrambling // + ////////////////////////////////////////////// + + // This encrypts the IV consisting of the nonce and address using the key provided in order to + // generate the keystream for the data. Note that we instantiate a register halfway within this + // primitive to balance the delay between request and response side. + localparam int DataNonceWidth = 64 - AddrWidth; + logic [NumParScr*64-1:0] keystream; + logic [NumParScr-1:0][DataNonceWidth-1:0] data_scr_nonce; + for (genvar k = 0; k < NumParScr; k++) begin : gen_par_scr + assign data_scr_nonce[k] = nonce_i[k * DataNonceWidth +: DataNonceWidth]; + + prim_prince #( + .DataWidth (64), + .KeyWidth (128), + .NumRoundsHalf (NumPrinceRoundsHalf), + .UseOldKeySched (1'b0), + .HalfwayDataReg (1'b1), // instantiate a register halfway in the primitive + .HalfwayKeyReg (1'b0) // no need to instantiate a key register as the key remains static + ) u_prim_prince ( + .clk_i, + .rst_ni, + .valid_i ( gnt_o ), + // The IV is composed of a nonce and the row address + //.data_i ( {nonce_i[k * (64 - AddrWidth) +: (64 - AddrWidth)], addr} ), + .data_i ( {data_scr_nonce[k], addr_i} ), + // All parallel scramblers use the same key + .key_i, + // Since we operate in counter mode, this can always be set to encryption mode + .dec_i ( 1'b0 ), + // Output keystream to be XOR'ed + .data_o ( keystream[k * 64 +: 64] ), + .valid_o ( ) + ); + + // Unread unused bits from keystream + if (k == NumParKeystr-1 && (Width % 64) > 0) begin : gen_unread_last + localparam int UnusedWidth = 64 - (Width % 64); + logic [UnusedWidth-1:0] unused_keystream; + assign unused_keystream = keystream[(k+1) * 64 - 1 -: UnusedWidth]; + end + end + + // Replicate keystream if needed + logic [Width-1:0] keystream_repl; + assign keystream_repl = Width'({NumParKeystr{keystream}}); + + ///////////////////// + // Data Scrambling // + ///////////////////// + + // Data scrambling is a two step process. First, we XOR the write data with the keystream obtained + // by operating a reduced-round PRINCE cipher in CTR-mode. Then, we diffuse data within each byte + // in order to get a limited "avalanche" behavior in case parts of the bytes are flipped as a + // result of a malicious attempt to tamper with the data in memory. We perform the diffusion only + // within bytes in order to maintain the ability to write individual bytes. Note that the + // keystream XOR is performed first for the write path such that it can be performed last for the + // read path. This allows us to hide a part of the combinational delay of the PRINCE primitive + // behind the propagation delay of the SRAM macro and the per-byte diffusion step. + + logic [Width-1:0] rdata_scr, rdata; + logic [Width-1:0] wdata_scr_d, wdata_scr_q, wdata_q; + for (genvar k = 0; k < (Width + DiffWidth - 1) / DiffWidth; k++) begin : gen_diffuse_data + // If the Width is not divisible by DiffWidth, we need to adjust the width of the last slice. + localparam int LocalWidth = (Width - k * DiffWidth >= DiffWidth) ? DiffWidth : + (Width - k * DiffWidth); + + // Write path. Note that since this does not fan out into the interconnect, the write path is + // not as critical as the read path below in terms of timing. + // Apply the keystream first + logic [LocalWidth-1:0] wdata_xor; + assign wdata_xor = wdata_q[k*DiffWidth +: LocalWidth] ^ + keystream_repl[k*DiffWidth +: LocalWidth]; + + // Byte aligned diffusion using a substitution / permutation network + prim_subst_perm #( + .DataWidth ( LocalWidth ), + .NumRounds ( NumDiffRounds ), + .Decrypt ( 0 ) + ) u_prim_subst_perm_enc ( + .data_i ( wdata_xor ), + .key_i ( '0 ), + .data_o ( wdata_scr_d[k*DiffWidth +: LocalWidth] ) + ); + + // Read path. This is timing critical. The keystream XOR operation is performed last in order to + // hide the combinational delay of the PRINCE primitive behind the propagation delay of the + // SRAM and the byte diffusion. + // Reverse diffusion first + logic [LocalWidth-1:0] rdata_xor; + prim_subst_perm #( + .DataWidth ( LocalWidth ), + .NumRounds ( NumDiffRounds ), + .Decrypt ( 1 ) + ) u_prim_subst_perm_dec ( + .data_i ( rdata_scr[k*DiffWidth +: LocalWidth] ), + .key_i ( '0 ), + .data_o ( rdata_xor ) + ); + + // Apply Keystream, replicate it if needed + assign rdata[k*DiffWidth +: LocalWidth] = rdata_xor ^ + keystream_repl[k*DiffWidth +: LocalWidth]; + end + + //////////////////////////////////////////////// + // Scrambled data register and forwarding mux // + //////////////////////////////////////////////// + + // This is the scrambled data holding register for pending writes. This is needed in order to make + // back to back patterns of the form WR -> RD -> WR work: + // + // cycle: 0 | 1 | 2 | 3 | + // incoming op: WR0 | RD | WR1 | - | + // prince: - | WR0 | RD | WR1 | + // memory op: - | RD | WR0 | WR1 | + // + // The read transaction in cycle 1 interrupts the first write transaction which has already used + // the PRINCE primitive for scrambling. If this sequence is followed by another write back-to-back + // in cycle 2, we cannot use the PRINCE primitive a second time for the first write, and hence + // need an additional holding register that can buffer the scrambled data of the first write in + // cycle 1. + + // Clear this if we can write the memory in this cycle. Set only if the current write cannot + // proceed due to an incoming read operation. + logic write_scr_pending_d; + assign write_scr_pending_d = (macro_write) ? 1'b0 : + (rw_collision) ? 1'b1 : + write_pending_q; + + // Select the correct scrambled word to be written, based on whether the word in the scrambled + // data holding register is valid or not. Note that the write_scr_q register could in theory be + // combined with the wdata_q register. We don't do that here for timing reasons, since that would + // require another read data mux to inject the scrambled data into the read descrambling path. + logic [Width-1:0] wdata_scr; + assign wdata_scr = (write_pending_q) ? wdata_scr_q : wdata_scr_d; + + logic rvalid_q; + logic intg_error_r_q; + logic [Width-1:0] wmask_q; + always_comb begin : p_forward_mux + rdata_o = '0; + rvalid_o = 1'b0; + // Kill the read response in case an integrity error was seen. + if (!intg_error_r_q && rvalid_q) begin + rvalid_o = 1'b1; + // In case of a collision, we forward the valid bytes of the write data from the unscrambled + // holding register. + if (addr_collision_q) begin + for (int k = 0; k < Width; k++) begin + if (wmask_q[k]) begin + rdata_o[k] = wdata_q[k]; + end else begin + rdata_o[k] = rdata[k]; + end + end + // regular reads. note that we just return zero in case + // an integrity error was signalled. + end else begin + rdata_o = rdata; + end + end + end + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_wdata_buf + if (!rst_ni) begin + write_pending_q <= 1'b0; + addr_collision_q <= 1'b0; + rvalid_q <= 1'b0; + write_en_q <= 1'b0; + intg_error_r_q <= 1'b0; + intg_error_w_q <= 1'b0; + raddr_q <= '0; + waddr_scr_q <= '0; + wmask_q <= '0; + wdata_q <= '0; + wdata_scr_q <= '0; + end else begin + write_pending_q <= write_scr_pending_d; + addr_collision_q <= addr_collision_d; + rvalid_q <= read_en; + write_en_q <= write_en_d; + intg_error_r_q <= intg_error_buf; + + if (read_en) begin + raddr_q <= addr_i; + end + if (write_en_d) begin + waddr_scr_q <= addr_scr; + wmask_q <= wmask_i; + wdata_q <= wdata_i; + intg_error_w_q <= intg_error_buf; + end + if (rw_collision) begin + wdata_scr_q <= wdata_scr_d; + end + end + end + + ////////////////// + // Memory Macro // + ////////////////// + + prim_ram_1p_adv #( + .Depth(Depth), + .Width(Width), + .DataBitsPerMask(DataBitsPerMask), + .EnableECC(1'b0), + .EnableParity(EnableParity), + .EnableInputPipeline(1'b0), + .EnableOutputPipeline(1'b0) + ) u_prim_ram_1p_adv ( + .clk_i, + .rst_ni, + .req_i ( macro_req ), + .write_i ( macro_write ), + .addr_i ( addr_mux ), + .wdata_i ( wdata_scr ), + .wmask_i ( wmask_q ), + .rdata_o ( rdata_scr ), + .rvalid_o ( ), + .rerror_o, + .cfg_i + ); + + `include "prim_util_get_scramble_params.svh" + +endmodule : prim_ram_1p_scr diff --git a/synth/prim/rtl/prim_ram_1r1w_adv.sv b/synth/prim/rtl/prim_ram_1r1w_adv.sv new file mode 100755 index 0000000..ddcb4f9 --- /dev/null +++ b/synth/prim/rtl/prim_ram_1r1w_adv.sv @@ -0,0 +1,83 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Two-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "prim_assert.sv" + +module prim_ram_1r1w_adv import prim_ram_2p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = prim_util_pkg::vbits(Depth) +) ( + input clk_i, + input rst_ni, + + // Port A can only write + input a_req_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + + // Port B can only read + input b_req_i, + input [Aw-1:0] b_addr_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 ram_2p_cfg_t cfg_i +); + + prim_ram_1r1w_async_adv #( + .Depth (Depth), + .Width (Width), + .DataBitsPerMask (DataBitsPerMask), + .MemInitFile (MemInitFile), + .EnableECC (EnableECC), + .EnableParity (EnableParity), + .EnableInputPipeline (EnableInputPipeline), + .EnableOutputPipeline(EnableOutputPipeline), + .HammingECC (HammingECC) + ) i_prim_ram_1r1w_async_adv ( + .clk_a_i(clk_i), + .rst_a_ni(rst_ni), + .clk_b_i(clk_i), + .rst_b_ni(rst_ni), + .a_req_i, + .a_addr_i, + .a_wdata_i, + .a_wmask_i, + .b_req_i, + .b_addr_i, + .b_rdata_o, + .b_rvalid_o, + .b_rerror_o, + .cfg_i + ); + +endmodule : prim_ram_1r1w_adv diff --git a/synth/prim/rtl/prim_ram_1r1w_async_adv.sv b/synth/prim/rtl/prim_ram_1r1w_async_adv.sv new file mode 100755 index 0000000..a6b2bc9 --- /dev/null +++ b/synth/prim/rtl/prim_ram_1r1w_async_adv.sv @@ -0,0 +1,264 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Asynchronous Two-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "prim_assert.sv" + +module prim_ram_1r1w_async_adv import prim_ram_2p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = prim_util_pkg::vbits(Depth) +) ( + input clk_a_i, + input clk_b_i, + input rst_a_ni, + input rst_b_ni, + + // Port A can only write + input a_req_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + + // Port B can only read + input b_req_i, + input [Aw-1:0] b_addr_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 + + // config + input ram_2p_cfg_t cfg_i +); + + + `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? Width/8 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + // If byte parity is enabled, the write enable bits are used to write memory colums + // with 8 + 1 = 9 bit width (data plus corresponding parity bit). + // If ECC is enabled, the DataBitsPerMask is ignored. + localparam int LocalDataBitsPerMask = (EnableParity) ? 9 : + (EnableECC) ? TotalWidth : + DataBitsPerMask; + + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// + + logic a_req_q, a_req_d ; + logic [Aw-1:0] a_addr_q, a_addr_d ; + logic [TotalWidth-1:0] a_wdata_q, a_wdata_d ; + logic [TotalWidth-1:0] a_wmask_q, a_wmask_d ; + + logic b_req_q, b_req_d ; + logic [Aw-1:0] b_addr_q, b_addr_d ; + logic b_rvalid_q, b_rvalid_d, b_rvalid_sram_q ; + 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 ; + + prim_ram_1r1w #( + .MemInitFile (MemInitFile), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (LocalDataBitsPerMask) + ) u_mem ( + .clk_a_i (clk_a_i), + .clk_b_i (clk_b_i), + + .a_req_i (a_req_q), + .a_addr_i (a_addr_q), + .a_wdata_i (a_wdata_q), + .a_wmask_i (a_wmask_q), + + .b_req_i (b_req_q), + .b_addr_i (b_addr_q), + .b_rdata_o (b_rdata_sram), + + .cfg_i + ); + + always_ff @(posedge clk_b_i or negedge rst_b_ni) begin + if (!rst_b_ni) begin + b_rvalid_sram_q <= 1'b0; + end else begin + b_rvalid_sram_q <= b_req_q; + end + end + + assign a_req_d = a_req_i; + assign a_addr_d = a_addr_i; + + assign b_req_d = b_req_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; + + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// + + if (EnableParity == 0 && EnableECC) begin : gen_secded + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + + // the wmask is constantly set to 1 in this case + `ASSERT(OnlyWordWritePossibleWithEccPortA_A, a_req_i |-> + a_wmask_i == {Width{1'b1}}, clk_a_i, rst_a_ni) + + assign a_wmask_d = {TotalWidth{1'b1}}; + + if (Width == 32) begin : gen_secded_39_32 + if (HammingECC) begin : gen_hamming + prim_secded_inv_hamming_39_32_enc u_enc_a ( + .data_i(a_wdata_i), + .data_o(a_wdata_d) + ); + prim_secded_inv_hamming_39_32_dec u_dec_b ( + .data_i (b_rdata_sram), + .data_o (b_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (b_rerror_d) + ); + end else begin : gen_hsiao + prim_secded_inv_39_32_enc u_enc_a ( + .data_i(a_wdata_i), + .data_o(a_wdata_d) + ); + prim_secded_inv_39_32_dec u_dec_b ( + .data_i (b_rdata_sram), + .data_o (b_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (b_rerror_d) + ); + end + end + end else if (EnableParity) begin : gen_byte_parity + + `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + + always_comb begin : p_parity + b_rerror_d = '0; + for (int i = 0; i < Width/8; i ++) begin + // Data mapping. We have to make 8+1 = 9 bit groups + // that have the same write enable such that FPGA tools + // can map this correctly to BRAM resources. + a_wmask_d[i*9 +: 8] = a_wmask_i[i*8 +: 8]; + a_wdata_d[i*9 +: 8] = a_wdata_i[i*8 +: 8]; + b_rdata_d[i*8 +: 8] = b_rdata_sram[i*9 +: 8]; + + // parity generation (odd parity) + a_wdata_d[i*9 + 8] = ~(^a_wdata_i[i*8 +: 8]); + a_wmask_d[i*9 + 8] = &a_wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + b_rerror_d[1] |= ~(^{b_rdata_sram[i*9 +: 8], b_rdata_sram[i*9 + 8]}); + end + end + end else begin : gen_nosecded_noparity + assign a_wmask_d = a_wmask_i; + assign a_wdata_d = a_wdata_i; + assign b_rdata_d = b_rdata_sram[0+:Width]; + assign b_rerror_d = '0; + end + + assign b_rvalid_d = b_rvalid_sram_q; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_a_i or negedge rst_a_ni) begin + if (!rst_a_ni) begin + a_req_q <= '0; + a_addr_q <= '0; + a_wdata_q <= '0; + a_wmask_q <= '0; + end else begin + a_req_q <= a_req_d; + a_addr_q <= a_addr_d; + a_wdata_q <= a_wdata_d; + a_wmask_q <= a_wmask_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_addr_q <= '0; + end else begin + b_req_q <= b_req_d; + b_addr_q <= b_addr_d; + end + end + end else begin : gen_dirconnect_input + assign a_req_q = a_req_d; + assign a_addr_q = a_addr_d; + assign a_wdata_q = a_wdata_d; + assign a_wmask_q = a_wmask_d; + + assign b_req_q = b_req_d; + assign b_addr_q = b_addr_d; + end + + if (EnableOutputPipeline) begin : gen_regslice_output + // Put the register slices between ECC decoding to output + 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; + // tie to zero if the read data is not valid + b_rerror_q <= b_rerror_d & {2{b_rvalid_d}}; + end + end + end else begin : gen_dirconnect_output + assign b_rvalid_q = b_rvalid_d; + assign b_rdata_q = b_rdata_d; + // tie to zero if the read data is not valid + assign b_rerror_q = b_rerror_d & {2{b_rvalid_d}}; + end + +endmodule : prim_ram_1r1w_async_adv diff --git a/synth/prim/rtl/prim_ram_2p_adv.sv b/synth/prim/rtl/prim_ram_2p_adv.sv new file mode 100755 index 0000000..866217a --- /dev/null +++ b/synth/prim/rtl/prim_ram_2p_adv.sv @@ -0,0 +1,95 @@ +// 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 +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "prim_assert.sv" + +module prim_ram_2p_adv import prim_ram_2p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = prim_util_pkg::vbits(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, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + 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, + input [Width-1:0] b_wmask_i, // cannot be used with ECC, tie to 1 in that case + 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 ram_2p_cfg_t cfg_i +); + + prim_ram_2p_async_adv #( + .Depth (Depth), + .Width (Width), + .DataBitsPerMask (DataBitsPerMask), + .MemInitFile (MemInitFile), + .EnableECC (EnableECC), + .EnableParity (EnableParity), + .EnableInputPipeline (EnableInputPipeline), + .EnableOutputPipeline(EnableOutputPipeline), + .HammingECC (HammingECC) + ) i_prim_ram_2p_async_adv ( + .clk_a_i(clk_i), + .rst_a_ni(rst_ni), + .clk_b_i(clk_i), + .rst_b_ni(rst_ni), + .a_req_i, + .a_write_i, + .a_addr_i, + .a_wdata_i, + .a_wmask_i, + .a_rdata_o, + .a_rvalid_o, + .a_rerror_o, + .b_req_i, + .b_write_i, + .b_addr_i, + .b_wdata_i, + .b_wmask_i, + .b_rdata_o, + .b_rvalid_o, + .b_rerror_o, + .cfg_i + ); + +endmodule : prim_ram_2p_adv diff --git a/synth/prim/rtl/prim_ram_2p_async_adv.sv b/synth/prim/rtl/prim_ram_2p_async_adv.sv new file mode 100755 index 0000000..d3de6f6 --- /dev/null +++ b/synth/prim/rtl/prim_ram_2p_async_adv.sv @@ -0,0 +1,358 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Asynchronous Dual-Port SRAM Wrapper +// +// Supported configurations: +// - ECC for 32b and 64b wide memories with no write mask +// (Width == 32 or Width == 64, DataBitsPerMask is ignored). +// - Byte parity if Width is a multiple of 8 bit and write masks have Byte +// granularity (DataBitsPerMask == 8). +// +// Note that the write mask needs to be per Byte if parity is enabled. If ECC is enabled, the write +// mask cannot be used and has to be tied to {Width{1'b1}}. + +`include "prim_assert.sv" + +module prim_ram_2p_async_adv import prim_ram_2p_pkg::*; #( + parameter int Depth = 512, + parameter int Width = 32, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + // Configurations + parameter bit EnableECC = 0, // Enables per-word ECC + parameter bit EnableParity = 0, // Enables per-Byte Parity + parameter bit EnableInputPipeline = 0, // Adds an input register (read latency +1) + parameter bit EnableOutputPipeline = 0, // Adds an output register (read latency +1) + + // This switch allows to switch to standard Hamming ECC instead of the HSIAO ECC. + // It is recommended to leave this parameter at its default setting (HSIAO), + // since this results in a more compact and faster implementation. + parameter bit HammingECC = 0, + + localparam int Aw = prim_util_pkg::vbits(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 [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input [Width-1:0] a_wmask_i, // cannot be used with ECC, tie to 1 in that case + 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, + input [Width-1:0] b_wmask_i, // cannot be used with ECC, tie to 1 in that case + 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 + + // config + input ram_2p_cfg_t cfg_i +); + + + `ASSERT_INIT(CannotHaveEccAndParity_A, !(EnableParity && EnableECC)) + + // Calculate ECC width + localparam int ParWidth = (EnableParity) ? Width/8 : + (!EnableECC) ? 0 : + (Width <= 4) ? 4 : + (Width <= 11) ? 5 : + (Width <= 26) ? 6 : + (Width <= 57) ? 7 : + (Width <= 120) ? 8 : 8 ; + localparam int TotalWidth = Width + ParWidth; + + // If byte parity is enabled, the write enable bits are used to write memory colums + // with 8 + 1 = 9 bit width (data plus corresponding parity bit). + // If ECC is enabled, the DataBitsPerMask is ignored. + localparam int LocalDataBitsPerMask = (EnableParity) ? 9 : + (EnableECC) ? TotalWidth : + DataBitsPerMask; + + //////////////////////////// + // RAM Primitive Instance // + //////////////////////////// + + logic 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 [TotalWidth-1:0] a_wmask_q, a_wmask_d ; + logic a_rvalid_q, a_rvalid_d, a_rvalid_sram_q ; + 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 [TotalWidth-1:0] b_wmask_q, b_wmask_d ; + logic b_rvalid_q, b_rvalid_d, b_rvalid_sram_q ; + 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 ; + + prim_ram_2p #( + .MemInitFile (MemInitFile), + + .Width (TotalWidth), + .Depth (Depth), + .DataBitsPerMask (LocalDataBitsPerMask) + ) 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 (a_wmask_q), + .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 (b_wmask_q), + .b_rdata_o (b_rdata_sram), + + .cfg_i + ); + + always_ff @(posedge clk_a_i or negedge rst_a_ni) begin + if (!rst_a_ni) begin + a_rvalid_sram_q <= 1'b0; + end else begin + a_rvalid_sram_q <= 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_q <= 1'b0; + end else begin + b_rvalid_sram_q <= 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; + + ///////////////////////////// + // ECC / Parity Generation // + ///////////////////////////// + + if (EnableParity == 0 && EnableECC) begin : gen_secded + + // check supported widths + `ASSERT_INIT(SecDecWidth_A, Width inside {32}) + + // the wmask is constantly set to 1 in this case + `ASSERT(OnlyWordWritePossibleWithEccPortA_A, a_req_i |-> + a_wmask_i == {Width{1'b1}}, clk_a_i, rst_a_ni) + `ASSERT(OnlyWordWritePossibleWithEccPortB_A, b_req_i |-> + b_wmask_i == {Width{1'b1}}, clk_b_i, rst_b_ni) + + assign a_wmask_d = {TotalWidth{1'b1}}; + assign b_wmask_d = {TotalWidth{1'b1}}; + + if (Width == 32) begin : gen_secded_39_32 + if (HammingECC) begin : gen_hamming + prim_secded_inv_hamming_39_32_enc u_enc_a ( + .data_i(a_wdata_i), + .data_o(a_wdata_d) + ); + prim_secded_inv_hamming_39_32_dec u_dec_a ( + .data_i (a_rdata_sram), + .data_o (a_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (a_rerror_d) + ); + prim_secded_inv_hamming_39_32_enc u_enc_b ( + .data_i(b_wdata_i), + .data_o(b_wdata_d) + ); + prim_secded_inv_hamming_39_32_dec u_dec_b ( + .data_i (b_rdata_sram), + .data_o (b_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (b_rerror_d) + ); + end else begin : gen_hsiao + prim_secded_inv_39_32_enc u_enc_a ( + .data_i(a_wdata_i), + .data_o(a_wdata_d) + ); + prim_secded_inv_39_32_dec u_dec_a ( + .data_i (a_rdata_sram), + .data_o (a_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (a_rerror_d) + ); + prim_secded_inv_39_32_enc u_enc_b ( + .data_i(b_wdata_i), + .data_o(b_wdata_d) + ); + prim_secded_inv_39_32_dec u_dec_b ( + .data_i (b_rdata_sram), + .data_o (b_rdata_d[0+:Width]), + .syndrome_o ( ), + .err_o (b_rerror_d) + ); + end + end + end else if (EnableParity) begin : gen_byte_parity + + `ASSERT_INIT(ParityNeedsByteWriteMask_A, DataBitsPerMask == 8) + `ASSERT_INIT(WidthNeedsToBeByteAligned_A, Width % 8 == 0) + + always_comb begin : p_parity + a_rerror_d = '0; + b_rerror_d = '0; + for (int i = 0; i < Width/8; i ++) begin + // Data mapping. We have to make 8+1 = 9 bit groups + // that have the same write enable such that FPGA tools + // can map this correctly to BRAM resources. + a_wmask_d[i*9 +: 8] = a_wmask_i[i*8 +: 8]; + a_wdata_d[i*9 +: 8] = a_wdata_i[i*8 +: 8]; + a_rdata_d[i*8 +: 8] = a_rdata_sram[i*9 +: 8]; + b_wmask_d[i*9 +: 8] = b_wmask_i[i*8 +: 8]; + b_wdata_d[i*9 +: 8] = b_wdata_i[i*8 +: 8]; + b_rdata_d[i*8 +: 8] = b_rdata_sram[i*9 +: 8]; + + // parity generation (odd parity) + a_wdata_d[i*9 + 8] = ~(^a_wdata_i[i*8 +: 8]); + a_wmask_d[i*9 + 8] = &a_wmask_i[i*8 +: 8]; + b_wdata_d[i*9 + 8] = ~(^b_wdata_i[i*8 +: 8]); + b_wmask_d[i*9 + 8] = &b_wmask_i[i*8 +: 8]; + // parity decoding (errors are always uncorrectable) + a_rerror_d[1] |= ~(^{a_rdata_sram[i*9 +: 8], a_rdata_sram[i*9 + 8]}); + b_rerror_d[1] |= ~(^{b_rdata_sram[i*9 +: 8], b_rdata_sram[i*9 + 8]}); + end + end + end else begin : gen_nosecded_noparity + assign a_wmask_d = a_wmask_i; + assign b_wmask_d = b_wmask_i; + assign a_wdata_d = a_wdata_i; + assign b_wdata_d = b_wdata_i; + assign a_rdata_d = a_rdata_sram[0+:Width]; + assign b_rdata_d = b_rdata_sram[0+:Width]; + assign a_rerror_d = '0; + assign b_rerror_d = '0; + end + + assign a_rvalid_d = a_rvalid_sram_q; + assign b_rvalid_d = b_rvalid_sram_q; + + ///////////////////////////////////// + // Input/Output Pipeline Registers // + ///////////////////////////////////// + + if (EnableInputPipeline) begin : gen_regslice_input + // Put the register slices between ECC encoding to SRAM port + always_ff @(posedge clk_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; + a_wmask_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; + a_wmask_q <= a_wmask_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; + b_wmask_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; + b_wmask_q <= b_wmask_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 a_wmask_q = a_wmask_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; + assign b_wmask_q = b_wmask_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; + // tie to zero if the read data is not valid + a_rerror_q <= a_rerror_d & {2{a_rvalid_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; + // tie to zero if the read data is not valid + b_rerror_q <= b_rerror_d & {2{b_rvalid_d}}; + end + end + end else begin : gen_dirconnect_output + assign a_rvalid_q = a_rvalid_d; + assign a_rdata_q = a_rdata_d; + // tie to zero if the read data is not valid + assign a_rerror_q = a_rerror_d & {2{a_rvalid_d}}; + + assign b_rvalid_q = b_rvalid_d; + assign b_rdata_q = b_rdata_d; + // tie to zero if the read data is not valid + assign b_rerror_q = b_rerror_d & {2{b_rvalid_d}}; + end + +endmodule : prim_ram_2p_async_adv diff --git a/synth/prim/rtl/prim_ram_2p_pkg.sv b/synth/prim/rtl/prim_ram_2p_pkg.sv new file mode 100755 index 0000000..4ae0427 --- /dev/null +++ b/synth/prim/rtl/prim_ram_2p_pkg.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 +// + +package prim_ram_2p_pkg; + + typedef struct packed { + logic cfg_en; + logic [3:0] cfg; + } cfg_t; + + typedef struct packed { + cfg_t a_ram_fcfg; // configuration for a port + cfg_t b_ram_fcfg; // configuration for b port + cfg_t a_ram_lcfg; // configuration for a port + cfg_t b_ram_lcfg; // configuration for b port + } ram_2p_cfg_t; + + parameter ram_2p_cfg_t RAM_2P_CFG_DEFAULT = '0; + +endpackage // prim_ram_2p_pkg diff --git a/synth/prim/rtl/prim_reg_cdc.sv b/synth/prim/rtl/prim_reg_cdc.sv new file mode 100755 index 0000000..f323100 --- /dev/null +++ b/synth/prim/rtl/prim_reg_cdc.sv @@ -0,0 +1,197 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Component handling register CDC +// +// Currently, this module only works correctly when paired with tlul_adapter_reg. +// This is because tlul_adapter_reg does not emit a new transaction to the same +// register if it discovers it is currently busy. Please see the BusySrcReqChk_A +// assertion below for more details. +// +// If in the future this assumption changes, we can modify this module easily to +// support the new behavior. + +`include "prim_assert.sv" + +module prim_reg_cdc #( + parameter int DataWidth = 32, + parameter logic [DataWidth-1:0] ResetVal = 32'h0, + parameter logic [DataWidth-1:0] BitMask = 32'hFFFFFFFF, + // whether this instance needs to support independent hardware writes + parameter bit DstWrReq = 0 +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + input src_regwen_i, + input src_we_i, + input src_re_i, + input [DataWidth-1:0] src_wd_i, + output logic src_busy_o, + output logic [DataWidth-1:0] src_qs_o, + input [DataWidth-1:0] dst_ds_i, + input [DataWidth-1:0] dst_qs_i, + input dst_update_i, + output logic dst_we_o, + output logic dst_re_o, + output logic dst_regwen_o, + output logic [DataWidth-1:0] dst_wd_o +); + + //////////////////////////// + // Source domain + //////////////////////////// + localparam int TxnWidth = 3; + + logic src_ack; + logic src_busy_q; + logic [DataWidth-1:0] src_q; + logic [TxnWidth-1:0] txn_bits_q; + logic src_req; + + assign src_req = src_we_i | src_re_i; + + // busy indication back-pressures upstream if the register is accessed + // again. The busy indication is also used as a "commit" indication for + // resolving software and hardware write conflicts + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_busy_q <= '0; + end else if (src_req) begin + src_busy_q <= 1'b1; + end else if (src_ack) begin + src_busy_q <= 1'b0; + end + end + + // A src_ack should only be sent if there was a src_req. + // src_busy_q asserts whenever there is a src_req. By association, + // whenever src_ack is seen, then src_busy must be high. + `ASSERT(SrcAckBusyChk_A, src_ack |-> src_busy_q, clk_src_i, !rst_src_ni) + + assign src_busy_o = src_busy_q; + + // src_q acts as both the write holding register and the software read back + // register. + // When software performs a write, the write data is captured in src_q for + // CDC purposes. When not performing a write, the src_q reflects the most recent + // hardware value. For registes with no hardware access, this is simply the + // the value programmed by software (or in the case R1C, W1C etc) the value after + // the operation. For registers with hardware access, this reflects a potentially + // delayed version of the real value, as the software facing updates lag real + // time updates. + // + // To resolve software and hardware conflicts, the process is as follows: + // When software issues a write, this module asserts "busy". While busy, + // src_q does not take on destination value updates. Since the + // logic has committed to updating based on software command, there is an irreversible + // window from which hardware writes are ignored. Once the busy window completes, + // the cdc portion then begins sampling once more. + // + // This is consistent with prim_subreg_arb where during software / hardware conflicts, + // software is always prioritized. The main difference is the conflict resolution window + // is now larger instead of just one destination clock cycle. + + logic busy; + assign busy = src_busy_q & !src_ack; + + // This is the current destination value + logic [DataWidth-1:0] dst_qs; + logic src_update; + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_q <= ResetVal; + txn_bits_q <= '0; + end else if (src_req) begin + // See assertion below + // At the beginning of a software initiated transaction, the following + // values are captured in the src_q/txn_bits_q flops to ensure they cannot + // change for the duration of the synchronization operation. + src_q <= src_wd_i & BitMask; + txn_bits_q <= {src_we_i, src_re_i, src_regwen_i}; + end else if (src_busy_q && src_ack || src_update && !busy) begin + // sample data whenever a busy transaction finishes OR + // when an update pulse is seen. + // TODO: We should add a cover group to test different sync timings + // between src_ack and src_update. Ie, there can be 3 scearios: + // 1. update one cycle before ack + // 2. ack one cycle before update + // 3. update / ack on the same cycle + // During all 3 cases the read data should be correct + src_q <= dst_qs; + txn_bits_q <= '0; + end + end + + // The current design (tlul_adapter_reg) does not spit out a request if the destination it chooses + // (decoded from address) is busy. So this creates a situation in the current design where + // src_req_i and busy can never be high at the same time. + // While the code above could be coded directly to be expressed as `src_req & !busy`, which makes + // the intent clearer, it ends up causing coverage holes from the tool's perspective since that + // condition cannot be met. + // Thus we add an assertion here to ensure the condition is always satisfied. + `ASSERT(BusySrcReqChk_A, busy |-> !src_req, clk_src_i, !rst_src_ni) + + // reserved bits are not used + logic unused_wd; + assign unused_wd = ^src_wd_i; + + // src_q is always updated in the clk_src domain. + // when performing an update to the destination domain, it is guaranteed + // to not change by protocol. + assign src_qs_o = src_q; + assign dst_wd_o = src_q; + + //////////////////////////// + // CDC handling + //////////////////////////// + + logic dst_req_from_src; + logic dst_req; + + + // the software transaction is pulse synced across the domain. + // the prim_reg_cdc_arb module handles conflicts with ongoing hardware updates. + prim_pulse_sync u_src_to_dst_req ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .src_pulse_i(src_req), + .dst_pulse_o(dst_req_from_src) + ); + + prim_reg_cdc_arb #( + .DataWidth(DataWidth), + .ResetVal(ResetVal), + .DstWrReq(DstWrReq) + ) u_arb ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + .src_ack_o(src_ack), + .src_update_o(src_update), + .dst_req_i(dst_req_from_src), + .dst_req_o(dst_req), + .dst_update_i, + .dst_ds_i, + .dst_qs_i, + .dst_qs_o(dst_qs) + ); + + + // Each is valid only when destination request pulse is high + assign {dst_we_o, dst_re_o, dst_regwen_o} = txn_bits_q & {TxnWidth{dst_req}}; + + `ASSERT_KNOWN(SrcBusyKnown_A, src_busy_o, clk_src_i, !rst_src_ni) + `ASSERT_KNOWN(DstReqKnown_A, dst_req, clk_dst_i, !rst_dst_ni) + + // If busy goes high, we must eventually see an ack + `ifdef FPV_ON + `ASSERT(HungHandShake_A, $rose(src_req) |-> strong(##[0:$] src_ack), clk_src_i, !rst_src_ni) + // TODO: #14913 check if we can add additional sim assertions. + `endif +endmodule // prim_subreg_cdc diff --git a/synth/prim/rtl/prim_reg_cdc_arb.sv b/synth/prim/rtl/prim_reg_cdc_arb.sv new file mode 100755 index 0000000..815fef8 --- /dev/null +++ b/synth/prim/rtl/prim_reg_cdc_arb.sv @@ -0,0 +1,305 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Component handling register CDC + +`include "prim_assert.sv" + +// There are three handling scenarios. +// 1. The register can only be updated by software. +// 2. The register can be updated by both software and hardware. +// 3. The register can only be updated by hardware. +// +// For the first scenario, hardware updates are completely ignored. +// The software facing register (`src_q` in prim_reg_cdc) simply reflects +// the value affected by software. Since there is no possibility the +// register value can change otherwise, there is no need to sample or +// do any other coordination between the two domains. In this case, +// we use the gen_passthru block below. +// +// For the second scenario, one of 4 things can happen: +// 1. A software update without conflict +// 2. A hardware update without conflict +// 3. A software update is initiated when a hardware update is in-flight +// 4. A hardware update is initiated when a software update is in-flight +// +// For the first case, it behaves similarly to the gen_passthru scenario. +// +// For the second case, the hardware update indication and update value are +// captured, and the intent to change is synchronized back to the software +// domain. While this happens, other hardware updates are ignored. Any hardware +// change during the update is then detected as a difference between the +// transit register `dst_qs_o` and the current hardware register value `dst_qs_i`. +// When this change is observed after the current handshake completes, another +// handshake event is generated to bring the latest hardware value over to the +// software domain. +// +// For the third case, if a hardware update event is already in progress, the +// software event is held and not acknowledged. Once the hardware event completes, +// then the software event proceeds through its normal updating process. +// +// For the forth case, if a hardware update event is received while a software +// update is in progress, the hardware update is ignored, and the logic behaves +// similarly to the second case. Specifically, after the software update completes, +// a delta is observed between the transit register and the current hardware value, +// and a new handshake event is generated. +// +// The third scenario can be folded into the second scenario. The only difference +// is that of the 4 cases identified, only case 2 can happen since there is never a +// software initiated update. + +module prim_reg_cdc_arb #( + parameter int DataWidth = 32, + parameter logic [DataWidth-1:0] ResetVal = 32'h0, + parameter bit DstWrReq = 0 +) ( + input clk_src_i, + input rst_src_ni, + input clk_dst_i, + input rst_dst_ni, + // destination side acknowledging a software transaction + output logic src_ack_o, + // destination side requesting a source side update after + // after hw update + output logic src_update_o, + // input request from prim_reg_cdc + input dst_req_i, + // output request to prim_subreg + output logic dst_req_o, + input dst_update_i, + // ds allows us to sample the destination domain register + // one cycle earlier instead of waiting for it to be reflected + // in the qs. + // This is important because a general use case is that interrupts + // are captured alongside payloads from the destination domain into + // the source domain. If we rely on `qs` only, then it is very likely + // that the software observed value will be behind the interrupt + // assertion. If the destination clock is very slow, this can seem + // an error on the part of the hardware. + input [DataWidth-1:0] dst_ds_i, + input [DataWidth-1:0] dst_qs_i, + output logic [DataWidth-1:0] dst_qs_o +); + + typedef enum logic { + SelSwReq, + SelHwReq + } req_sel_e; + + typedef enum logic [1:0] { + StIdle, + StWait + } state_e; + + + // Only honor the incoming destinate update request if the incoming + // value is actually different from what is already completed in the + // handshake + logic dst_update; + assign dst_update = dst_update_i & (dst_qs_o != dst_ds_i); + + if (DstWrReq) begin : gen_wr_req + logic dst_lat_q; + logic dst_lat_d; + logic dst_update_req; + logic dst_update_ack; + req_sel_e id_q; + + state_e state_q, state_d; + // Make sure to indent the following later + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + state_q <= StIdle; + end else begin + state_q <= state_d; + end + end + + logic busy; + logic dst_req_q, dst_req; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_req_q <= '0; + end else if (dst_req_q && dst_lat_d) begin + // if request is held, when the transaction starts, + // automatically clear. + // dst_lat_d is safe to used here because dst_req_q, if set, + // always has priority over other hardware based events. + dst_req_q <= '0; + end else if (dst_req_i && !dst_req_q && busy) begin + // if destination request arrives when a handshake event + // is already ongoing, hold on to request and send later + dst_req_q <= 1'b1; + end + end + assign dst_req = dst_req_q | dst_req_i; + + // Hold data at the beginning of a transaction + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_qs_o <= ResetVal; + end else if (dst_lat_d) begin + dst_qs_o <= dst_ds_i; + end else if (dst_lat_q) begin + dst_qs_o <= dst_qs_i; + end + end + + // Which type of transaction is being ack'd back? + // 0 - software initiated request + // 1 - hardware initiated request + // The id information is used by prim_reg_cdc to disambiguate + // simultaneous updates from software and hardware. + // See scenario 2 case 3 for an example of how this is handled. + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + id_q <= SelSwReq; + end else if (dst_update_req && dst_update_ack) begin + id_q <= SelSwReq; + end else if (dst_req && dst_lat_d) begin + id_q <= SelSwReq; + end else if (!dst_req && dst_lat_d) begin + id_q <= SelHwReq; + end else if (dst_lat_q) begin + id_q <= SelHwReq; + end + end + + // if a destination update is received when the system is idle and there is no + // software side request, hw update must be selected. + `ASSERT(DstUpdateReqCheck_A, ##1 dst_update & !dst_req & !busy |=> id_q == SelHwReq, + clk_dst_i, !rst_dst_ni) + + // if hw select was chosen, then it must be the case there was a destination update + // indication or there was a difference between the transit register and the + // latest incoming value. + `ASSERT(HwIdSelCheck_A, $rose(id_q == SelHwReq) |-> $past(dst_update_i, 1) || + $past(dst_lat_q, 1), + clk_dst_i, !rst_dst_ni) + + + // send out prim_subreg request only when proceeding + // with software request + assign dst_req_o = ~busy & dst_req; + + logic dst_hold_req; + always_comb begin + state_d = state_q; + dst_hold_req = '0; + + // depending on when the request is received, we + // may latch d or q. + dst_lat_q = '0; + dst_lat_d = '0; + + busy = 1'b1; + + unique case (state_q) + StIdle: begin + busy = '0; + if (dst_req) begin + // there's a software issued request for change + state_d = StWait; + dst_lat_d = 1'b1; + end else if (dst_update) begin + state_d = StWait; + dst_lat_d = 1'b1; + end else if (dst_qs_o != dst_qs_i) begin + // there's a direct destination update + // that was blocked by an ongoing transaction + state_d = StWait; + dst_lat_q = 1'b1; + end + end + + StWait: begin + dst_hold_req = 1'b1; + if (dst_update_ack) begin + state_d = StIdle; + end + end + + default: begin + state_d = StIdle; + end + endcase // unique case (state_q) + end // always_comb + + assign dst_update_req = dst_hold_req | dst_lat_d | dst_lat_q; + logic src_req; + prim_sync_reqack u_dst_update_sync ( + .clk_src_i(clk_dst_i), + .rst_src_ni(rst_dst_ni), + .clk_dst_i(clk_src_i), + .rst_dst_ni(rst_src_ni), + .req_chk_i(1'b1), + .src_req_i(dst_update_req), + .src_ack_o(dst_update_ack), + .dst_req_o(src_req), + // immediate ack + .dst_ack_i(src_req) + ); + + assign src_ack_o = src_req & (id_q == SelSwReq); + assign src_update_o = src_req & (id_q == SelHwReq); + + // once hardware makes an update request, we must eventually see an update pulse + `ifdef FPV_ON + `ASSERT(ReqTimeout_A, $rose(id_q == SelHwReq) |-> s_eventually(src_update_o), + clk_src_i, !rst_src_ni) + // TODO: #14913 check if we can add additional sim assertions. + `endif + + `ifdef FPV_ON + //VCS coverage off + // pragma coverage off + + logic async_flag; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni or posedge src_update_o) begin + if (!rst_dst_ni) begin + async_flag <= '0; + end else if (src_update_o) begin + async_flag <= '0; + end else if (dst_update && !dst_req_o && !busy) begin + async_flag <= 1'b1; + end + end + + //VCS coverage on + // pragma coverage on + + // once hardware makes an update request, we must eventually see an update pulse + // TODO: #14913 check if we can add additional sim assertions. + `ASSERT(UpdateTimeout_A, $rose(async_flag) |-> s_eventually(src_update_o), + clk_src_i, !rst_src_ni) + `endif + + end else begin : gen_passthru + // when there is no possibility of conflicting HW transactions, + // we can assume that dst_qs_i will only ever take on the value + // that is directly related to the transaction. As a result, + // there is no need to latch further, and the end destination + // can in fact be used as the holding register. + assign dst_qs_o = dst_qs_i; + assign dst_req_o = dst_req_i; + + // since there are no hw transactions, src_update_o is always '0 + assign src_update_o = '0; + + prim_pulse_sync u_dst_to_src_ack ( + .clk_src_i(clk_dst_i), + .rst_src_ni(rst_dst_ni), + .clk_dst_i(clk_src_i), + .rst_dst_ni(rst_src_ni), + .src_pulse_i(dst_req_i), + .dst_pulse_o(src_ack_o) + ); + + logic unused_sigs; + assign unused_sigs = |{dst_ds_i, dst_update}; + end + + + +endmodule diff --git a/synth/prim/rtl/prim_reg_we_check.sv b/synth/prim/rtl/prim_reg_we_check.sv new file mode 100755 index 0000000..51373ca --- /dev/null +++ b/synth/prim/rtl/prim_reg_we_check.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 + +// Spurious write-enable checker for autogenerated CSR node. +// This module has additional simulation features for error injection testing. + +`include "prim_assert.sv" + +module prim_reg_we_check #( + parameter int unsigned OneHotWidth = 32 +) ( + // The module is combinational - the clock and reset are only used for assertions. + input clk_i, + input rst_ni, + + input logic [OneHotWidth-1:0] oh_i, + input logic en_i, + + output logic err_o +); + + // Prevent optimization of the onehot input buffer. + logic [OneHotWidth-1:0] oh_buf; + prim_buf #( + .Width(OneHotWidth) + ) u_prim_buf ( + .in_i(oh_i), + .out_o(oh_buf) + ); + + prim_onehot_check #( + .OneHotWidth(OneHotWidth), + .AddrWidth (prim_util_pkg::vbits(OneHotWidth)), + .EnableCheck(1), + // Since certain peripherals may have a very large address space + // (e.g. > 20bit), the inverse address decoding check (which is + // essentially an indexing operation) does not scale well and is + // hence omitted. + .AddrCheck(0), + // Due to REGWEN masking of write enable strobes, + // we do not perform strict checks. I.e., we allow cases + // where en_i is set to 1, but the oh_i vector is all-zeroes. + .StrictCheck(0) + ) u_prim_onehot_check ( + .clk_i, + .rst_ni, + .oh_i(oh_buf), + .addr_i('0), + .en_i, + .err_o + ); + +endmodule : prim_reg_we_check diff --git a/synth/prim/rtl/prim_rom_adv.sv b/synth/prim/rtl/prim_rom_adv.sv new file mode 100755 index 0000000..dbeec64 --- /dev/null +++ b/synth/prim/rtl/prim_rom_adv.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 +// +// ROM wrapper with rvalid register + +`include "prim_assert.sv" + +module prim_rom_adv import prim_rom_pkg::*; #( + // Parameters passed on the ROM primitive. + 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 logic clk_i, + input logic rst_ni, + input logic req_i, + input logic [Aw-1:0] addr_i, + output logic rvalid_o, + output logic [Width-1:0] rdata_o, + + input rom_cfg_t cfg_i +); + + prim_rom #( + .Width(Width), + .Depth(Depth), + .MemInitFile(MemInitFile) + ) u_prim_rom ( + .clk_i, + .req_i, + .addr_i, + .rdata_o, + .cfg_i + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid_o <= 1'b0; + end else begin + rvalid_o <= req_i; + end + end + + //////////////// + // ASSERTIONS // + //////////////// + + // Control Signals should never be X + `ASSERT(noXOnCsI, !$isunknown(req_i), clk_i, '0) +endmodule : prim_rom_adv diff --git a/synth/prim/rtl/prim_rom_pkg.sv b/synth/prim/rtl/prim_rom_pkg.sv new file mode 100755 index 0000000..1ac84ee --- /dev/null +++ b/synth/prim/rtl/prim_rom_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_rom_pkg; + + typedef struct packed { + logic cfg_en; + logic [3:0] cfg; + } rom_cfg_t; + + parameter rom_cfg_t ROM_CFG_DEFAULT = '0; + +endpackage // prim_rom_pkg diff --git a/synth/prim/rtl/prim_rst_sync.sv b/synth/prim/rtl/prim_rst_sync.sv new file mode 100755 index 0000000..b499a1a --- /dev/null +++ b/synth/prim/rtl/prim_rst_sync.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 +// +// Reset synchronizer +/** Conventional 2FF async assert sync de-assert reset synchronizer + * + */ + +module prim_rst_sync #( + // ActiveHigh should be 0 if the input reset is active low reset + parameter bit ActiveHigh = 1'b 0, + + // In certain case, Scan may be inserted at the following reset chain. + // Set SkipScan to 1'b 1 in that case. + parameter bit SkipScan = 1'b 0 +) ( + input clk_i, + input d_i, // raw reset (not synched to clk_i) + output logic q_o, // reset synched to clk_i + + // Scan chain + input scan_rst_ni, + input prim_mubi_pkg::mubi4_t scanmode_i +); + + logic async_rst_n, scan_rst; + logic rst_sync; + + // TODO: Check if 2FF set can be used. + if (ActiveHigh == 1'b 1) begin : g_rst_inv + assign async_rst_n = ~d_i; + assign scan_rst = ~scan_rst_ni; + end else begin : g_rst_direct + assign async_rst_n = d_i; + assign scan_rst = scan_rst_ni; + end + + prim_flop_2sync #( + .Width (1), + .ResetValue (ActiveHigh) + ) u_sync ( + .clk_i, + .rst_ni (async_rst_n), + .d_i (!ActiveHigh), // reset release value + .q_o (rst_sync ) + ); + + if (SkipScan) begin : g_skip_scan + logic unused_scan; + assign unused_scan = ^{scan_rst, scanmode_i}; + + assign q_o = rst_sync; + end else begin : g_scan_mux + prim_clock_mux2 #( + .NoFpgaBufG(1'b1) + ) u_scan_mux ( + .clk0_i(rst_sync ), + .clk1_i(scan_rst ), + .sel_i (prim_mubi_pkg::mubi4_test_true_strict(scanmode_i)), + .clk_o (q_o ) + ); + end + +endmodule : prim_rst_sync diff --git a/synth/prim/rtl/prim_sec_anchor_buf.sv b/synth/prim/rtl/prim_sec_anchor_buf.sv new file mode 100755 index 0000000..1ea7184 --- /dev/null +++ b/synth/prim/rtl/prim_sec_anchor_buf.sv @@ -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 + +`include "prim_assert.sv" + +module prim_sec_anchor_buf #( + parameter int Width = 1 +) ( + input [Width-1:0] in_i, + output logic [Width-1:0] out_o +); + + prim_buf #( + .Width(Width) + ) u_secure_anchor_buf ( + .in_i, + .out_o + ); + +endmodule diff --git a/synth/prim/rtl/prim_sec_anchor_flop.sv b/synth/prim/rtl/prim_sec_anchor_flop.sv new file mode 100755 index 0000000..ce18b47 --- /dev/null +++ b/synth/prim/rtl/prim_sec_anchor_flop.sv @@ -0,0 +1,27 @@ +// 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_sec_anchor_flop #( + parameter int Width = 1, + parameter logic [Width-1:0] ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + prim_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_secure_anchor_flop ( + .clk_i, + .rst_ni, + .d_i, + .q_o + ); + +endmodule diff --git a/synth/prim/rtl/prim_secded_22_16_dec.sv b/synth/prim/rtl/prim_secded_22_16_dec.sv new file mode 100755 index 0000000..4fa1196 --- /dev/null +++ b/synth/prim/rtl/prim_secded_22_16_dec.sv @@ -0,0 +1,45 @@ +// 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 util/design/secded_gen.py + +module prim_secded_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01496E); + syndrome_o[1] = ^(data_i & 22'h02F20B); + syndrome_o[2] = ^(data_i & 22'h048ED8); + syndrome_o[3] = ^(data_i & 22'h087714); + syndrome_o[4] = ^(data_i & 22'h10ACA5); + syndrome_o[5] = ^(data_i & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_22_16_dec diff --git a/synth/prim/rtl/prim_secded_22_16_enc.sv b/synth/prim/rtl/prim_secded_22_16_enc.sv new file mode 100755 index 0000000..41ad31b --- /dev/null +++ b/synth/prim/rtl/prim_secded_22_16_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + end + +endmodule : prim_secded_22_16_enc diff --git a/synth/prim/rtl/prim_secded_28_22_dec.sv b/synth/prim/rtl/prim_secded_28_22_dec.sv new file mode 100755 index 0000000..7df02e2 --- /dev/null +++ b/synth/prim/rtl/prim_secded_28_22_dec.sv @@ -0,0 +1,51 @@ +// 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 util/design/secded_gen.py + +module prim_secded_28_22_dec ( + input [27:0] data_i, + output logic [21:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 28'h07003FF); + syndrome_o[1] = ^(data_i & 28'h090FC0F); + syndrome_o[2] = ^(data_i & 28'h1271C71); + syndrome_o[3] = ^(data_i & 28'h23B6592); + syndrome_o[4] = ^(data_i & 28'h43DAAA4); + syndrome_o[5] = ^(data_i & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_28_22_dec diff --git a/synth/prim/rtl/prim_secded_28_22_enc.sv b/synth/prim/rtl/prim_secded_28_22_enc.sv new file mode 100755 index 0000000..f4e451a --- /dev/null +++ b/synth/prim/rtl/prim_secded_28_22_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_28_22_enc ( + input [21:0] data_i, + output logic [27:0] data_o +); + + always_comb begin : p_encode + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + end + +endmodule : prim_secded_28_22_enc diff --git a/synth/prim/rtl/prim_secded_39_32_dec.sv b/synth/prim/rtl/prim_secded_39_32_dec.sv new file mode 100755 index 0000000..10ee733 --- /dev/null +++ b/synth/prim/rtl/prim_secded_39_32_dec.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 decoder generated by util/design/secded_gen.py + +module prim_secded_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h012606BD25); + syndrome_o[1] = ^(data_i & 39'h02DEBA8050); + syndrome_o[2] = ^(data_i & 39'h04413D89AA); + syndrome_o[3] = ^(data_i & 39'h0831234ED1); + syndrome_o[4] = ^(data_i & 39'h10C2C1323B); + syndrome_o[5] = ^(data_i & 39'h202DCC624C); + syndrome_o[6] = ^(data_i & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_39_32_dec diff --git a/synth/prim/rtl/prim_secded_39_32_enc.sv b/synth/prim/rtl/prim_secded_39_32_enc.sv new file mode 100755 index 0000000..5668ff3 --- /dev/null +++ b/synth/prim/rtl/prim_secded_39_32_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + end + +endmodule : prim_secded_39_32_enc diff --git a/synth/prim/rtl/prim_secded_64_57_dec.sv b/synth/prim/rtl/prim_secded_64_57_dec.sv new file mode 100755 index 0000000..dc1fd42 --- /dev/null +++ b/synth/prim/rtl/prim_secded_64_57_dec.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 +// +// SECDED decoder generated by util/design/secded_gen.py + +module prim_secded_64_57_dec ( + input [63:0] data_i, + output logic [56:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 64'h0303FFF800007FFF); + syndrome_o[1] = ^(data_i & 64'h057C1FF801FF801F); + syndrome_o[2] = ^(data_i & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^(data_i & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^(data_i & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^(data_i & 64'h41F7BB56D5525488); + syndrome_o[6] = ^(data_i & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_64_57_dec diff --git a/synth/prim/rtl/prim_secded_64_57_enc.sv b/synth/prim/rtl/prim_secded_64_57_enc.sv new file mode 100755 index 0000000..6e83726 --- /dev/null +++ b/synth/prim/rtl/prim_secded_64_57_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_64_57_enc ( + input [56:0] data_i, + output logic [63:0] data_o +); + + always_comb begin : p_encode + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + end + +endmodule : prim_secded_64_57_enc diff --git a/synth/prim/rtl/prim_secded_72_64_dec.sv b/synth/prim/rtl/prim_secded_72_64_dec.sv new file mode 100755 index 0000000..112a64f --- /dev/null +++ b/synth/prim/rtl/prim_secded_72_64_dec.sv @@ -0,0 +1,95 @@ +// 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 util/design/secded_gen.py + +module prim_secded_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^(data_i & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^(data_i & 72'h0467003FF003E007C1); + syndrome_o[3] = ^(data_i & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^(data_i & 72'h10B671C711C4438884); + syndrome_o[5] = ^(data_i & 72'h20B5B65926488C9108); + syndrome_o[6] = ^(data_i & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^(data_i & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_72_64_dec diff --git a/synth/prim/rtl/prim_secded_72_64_enc.sv b/synth/prim/rtl/prim_secded_72_64_enc.sv new file mode 100755 index 0000000..5e9f674 --- /dev/null +++ b/synth/prim/rtl/prim_secded_72_64_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + end + +endmodule : prim_secded_72_64_enc diff --git a/synth/prim/rtl/prim_secded_hamming_22_16_dec.sv b/synth/prim/rtl/prim_secded_hamming_22_16_dec.sv new file mode 100755 index 0000000..135dc2f --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_22_16_dec.sv @@ -0,0 +1,45 @@ +// 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 util/design/secded_gen.py + +module prim_secded_hamming_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01AD5B); + syndrome_o[1] = ^(data_i & 22'h02366D); + syndrome_o[2] = ^(data_i & 22'h04C78E); + syndrome_o[3] = ^(data_i & 22'h0807F0); + syndrome_o[4] = ^(data_i & 22'h10F800); + syndrome_o[5] = ^(data_i & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + end +endmodule : prim_secded_hamming_22_16_dec diff --git a/synth/prim/rtl/prim_secded_hamming_22_16_enc.sv b/synth/prim/rtl/prim_secded_hamming_22_16_enc.sv new file mode 100755 index 0000000..9eb7295 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_22_16_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_hamming_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + end + +endmodule : prim_secded_hamming_22_16_enc diff --git a/synth/prim/rtl/prim_secded_hamming_39_32_dec.sv b/synth/prim/rtl/prim_secded_hamming_39_32_dec.sv new file mode 100755 index 0000000..1f41364 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_39_32_dec.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 decoder generated by util/design/secded_gen.py + +module prim_secded_hamming_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h0156AAAD5B); + syndrome_o[1] = ^(data_i & 39'h029B33366D); + syndrome_o[2] = ^(data_i & 39'h04E3C3C78E); + syndrome_o[3] = ^(data_i & 39'h0803FC07F0); + syndrome_o[4] = ^(data_i & 39'h1003FFF800); + syndrome_o[5] = ^(data_i & 39'h20FC000000); + syndrome_o[6] = ^(data_i & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + end +endmodule : prim_secded_hamming_39_32_dec diff --git a/synth/prim/rtl/prim_secded_hamming_39_32_enc.sv b/synth/prim/rtl/prim_secded_hamming_39_32_enc.sv new file mode 100755 index 0000000..47b60f5 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_39_32_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_hamming_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + end + +endmodule : prim_secded_hamming_39_32_enc diff --git a/synth/prim/rtl/prim_secded_hamming_72_64_dec.sv b/synth/prim/rtl/prim_secded_hamming_72_64_dec.sv new file mode 100755 index 0000000..7eaaa40 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_72_64_dec.sv @@ -0,0 +1,95 @@ +// 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 util/design/secded_gen.py + +module prim_secded_hamming_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 72'h02CD9999999B33366D); + syndrome_o[2] = ^(data_i & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 72'h40FE00000000000000); + syndrome_o[7] = ^(data_i & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : prim_secded_hamming_72_64_dec diff --git a/synth/prim/rtl/prim_secded_hamming_72_64_enc.sv b/synth/prim/rtl/prim_secded_hamming_72_64_enc.sv new file mode 100755 index 0000000..a24f3c8 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_72_64_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_hamming_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + end + +endmodule : prim_secded_hamming_72_64_enc diff --git a/synth/prim/rtl/prim_secded_hamming_76_68_dec.sv b/synth/prim/rtl/prim_secded_hamming_76_68_dec.sv new file mode 100755 index 0000000..418a233 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_76_68_dec.sv @@ -0,0 +1,99 @@ +// 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 util/design/secded_gen.py + +module prim_secded_hamming_76_68_dec ( + input [75:0] data_i, + output logic [67:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^(data_i & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^(data_i & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 76'h40FFE00000000000000); + syndrome_o[7] = ^(data_i & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : prim_secded_hamming_76_68_dec diff --git a/synth/prim/rtl/prim_secded_hamming_76_68_enc.sv b/synth/prim/rtl/prim_secded_hamming_76_68_enc.sv new file mode 100755 index 0000000..3e94083 --- /dev/null +++ b/synth/prim/rtl/prim_secded_hamming_76_68_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_hamming_76_68_enc ( + input [67:0] data_i, + output logic [75:0] data_o +); + + always_comb begin : p_encode + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + end + +endmodule : prim_secded_hamming_76_68_enc diff --git a/synth/prim/rtl/prim_secded_inv_22_16_dec.sv b/synth/prim/rtl/prim_secded_inv_22_16_dec.sv new file mode 100755 index 0000000..5d53680 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_22_16_dec.sv @@ -0,0 +1,45 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01496E); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02F20B); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h048ED8); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h087714); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10ACA5); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_inv_22_16_dec diff --git a/synth/prim/rtl/prim_secded_inv_22_16_enc.sv b/synth/prim/rtl/prim_secded_inv_22_16_enc.sv new file mode 100755 index 0000000..a057b0d --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_22_16_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + data_o ^= 22'h2A0000; + end + +endmodule : prim_secded_inv_22_16_enc diff --git a/synth/prim/rtl/prim_secded_inv_28_22_dec.sv b/synth/prim/rtl/prim_secded_inv_28_22_dec.sv new file mode 100755 index 0000000..b54b647 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_28_22_dec.sv @@ -0,0 +1,51 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_28_22_dec ( + input [27:0] data_i, + output logic [21:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 28'hA800000) & 28'h07003FF); + syndrome_o[1] = ^((data_i ^ 28'hA800000) & 28'h090FC0F); + syndrome_o[2] = ^((data_i ^ 28'hA800000) & 28'h1271C71); + syndrome_o[3] = ^((data_i ^ 28'hA800000) & 28'h23B6592); + syndrome_o[4] = ^((data_i ^ 28'hA800000) & 28'h43DAAA4); + syndrome_o[5] = ^((data_i ^ 28'hA800000) & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_inv_28_22_dec diff --git a/synth/prim/rtl/prim_secded_inv_28_22_enc.sv b/synth/prim/rtl/prim_secded_inv_28_22_enc.sv new file mode 100755 index 0000000..b1998e3 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_28_22_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_28_22_enc ( + input [21:0] data_i, + output logic [27:0] data_o +); + + always_comb begin : p_encode + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + data_o ^= 28'hA800000; + end + +endmodule : prim_secded_inv_28_22_enc diff --git a/synth/prim/rtl/prim_secded_inv_39_32_dec.sv b/synth/prim/rtl/prim_secded_inv_39_32_dec.sv new file mode 100755 index 0000000..a40a86c --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_39_32_dec.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 decoder generated by util/design/secded_gen.py + +module prim_secded_inv_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h012606BD25); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h02DEBA8050); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04413D89AA); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0831234ED1); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h10C2C1323B); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h202DCC624C); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_inv_39_32_dec diff --git a/synth/prim/rtl/prim_secded_inv_39_32_enc.sv b/synth/prim/rtl/prim_secded_inv_39_32_enc.sv new file mode 100755 index 0000000..dedd585 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_39_32_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + data_o ^= 39'h2A00000000; + end + +endmodule : prim_secded_inv_39_32_enc diff --git a/synth/prim/rtl/prim_secded_inv_64_57_dec.sv b/synth/prim/rtl/prim_secded_inv_64_57_dec.sv new file mode 100755 index 0000000..6e34b50 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_64_57_dec.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 +// +// SECDED decoder generated by util/design/secded_gen.py + +module prim_secded_inv_64_57_dec ( + input [63:0] data_i, + output logic [56:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 64'h5400000000000000) & 64'h0303FFF800007FFF); + syndrome_o[1] = ^((data_i ^ 64'h5400000000000000) & 64'h057C1FF801FF801F); + syndrome_o[2] = ^((data_i ^ 64'h5400000000000000) & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^((data_i ^ 64'h5400000000000000) & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^((data_i ^ 64'h5400000000000000) & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^((data_i ^ 64'h5400000000000000) & 64'h41F7BB56D5525488); + syndrome_o[6] = ^((data_i ^ 64'h5400000000000000) & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_inv_64_57_dec diff --git a/synth/prim/rtl/prim_secded_inv_64_57_enc.sv b/synth/prim/rtl/prim_secded_inv_64_57_enc.sv new file mode 100755 index 0000000..99565b7 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_64_57_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_64_57_enc ( + input [56:0] data_i, + output logic [63:0] data_o +); + + always_comb begin : p_encode + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + data_o ^= 64'h5400000000000000; + end + +endmodule : prim_secded_inv_64_57_enc diff --git a/synth/prim/rtl/prim_secded_inv_72_64_dec.sv b/synth/prim/rtl/prim_secded_inv_72_64_dec.sv new file mode 100755 index 0000000..aa4c5b5 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_72_64_dec.sv @@ -0,0 +1,95 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0467003FF003E007C1); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h10B671C711C4438884); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h20B5B65926488C9108); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + end +endmodule : prim_secded_inv_72_64_dec diff --git a/synth/prim/rtl/prim_secded_inv_72_64_enc.sv b/synth/prim/rtl/prim_secded_inv_72_64_enc.sv new file mode 100755 index 0000000..87b36c4 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_72_64_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + data_o ^= 72'hAA0000000000000000; + end + +endmodule : prim_secded_inv_72_64_enc diff --git a/synth/prim/rtl/prim_secded_inv_hamming_22_16_dec.sv b/synth/prim/rtl/prim_secded_inv_hamming_22_16_dec.sv new file mode 100755 index 0000000..0f077ee --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_22_16_dec.sv @@ -0,0 +1,45 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_hamming_22_16_dec ( + input [21:0] data_i, + output logic [15:0] data_o, + output logic [5:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01AD5B); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02366D); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h04C78E); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h0807F0); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10F800); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + end +endmodule : prim_secded_inv_hamming_22_16_dec diff --git a/synth/prim/rtl/prim_secded_inv_hamming_22_16_enc.sv b/synth/prim/rtl/prim_secded_inv_hamming_22_16_enc.sv new file mode 100755 index 0000000..0b63ff6 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_22_16_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_22_16_enc ( + input [15:0] data_i, + output logic [21:0] data_o +); + + always_comb begin : p_encode + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + data_o ^= 22'h2A0000; + end + +endmodule : prim_secded_inv_hamming_22_16_enc diff --git a/synth/prim/rtl/prim_secded_inv_hamming_39_32_dec.sv b/synth/prim/rtl/prim_secded_inv_hamming_39_32_dec.sv new file mode 100755 index 0000000..f65e86d --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_39_32_dec.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 decoder generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_39_32_dec ( + input [38:0] data_i, + output logic [31:0] data_o, + output logic [6:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h0156AAAD5B); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h029B33366D); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04E3C3C78E); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0803FC07F0); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h1003FFF800); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h20FC000000); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + end +endmodule : prim_secded_inv_hamming_39_32_dec diff --git a/synth/prim/rtl/prim_secded_inv_hamming_39_32_enc.sv b/synth/prim/rtl/prim_secded_inv_hamming_39_32_enc.sv new file mode 100755 index 0000000..ee988db --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_39_32_enc.sv @@ -0,0 +1,24 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_hamming_39_32_enc ( + input [31:0] data_i, + output logic [38:0] data_o +); + + always_comb begin : p_encode + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + data_o ^= 39'h2A00000000; + end + +endmodule : prim_secded_inv_hamming_39_32_enc diff --git a/synth/prim/rtl/prim_secded_inv_hamming_72_64_dec.sv b/synth/prim/rtl/prim_secded_inv_hamming_72_64_dec.sv new file mode 100755 index 0000000..99c2e5d --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_72_64_dec.sv @@ -0,0 +1,95 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_hamming_72_64_dec ( + input [71:0] data_i, + output logic [63:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h02CD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40FE00000000000000); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : prim_secded_inv_hamming_72_64_dec diff --git a/synth/prim/rtl/prim_secded_inv_hamming_72_64_enc.sv b/synth/prim/rtl/prim_secded_inv_hamming_72_64_enc.sv new file mode 100755 index 0000000..3a47ca5 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_72_64_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_72_64_enc ( + input [63:0] data_i, + output logic [71:0] data_o +); + + always_comb begin : p_encode + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + data_o ^= 72'hAA0000000000000000; + end + +endmodule : prim_secded_inv_hamming_72_64_enc diff --git a/synth/prim/rtl/prim_secded_inv_hamming_76_68_dec.sv b/synth/prim/rtl/prim_secded_inv_hamming_76_68_dec.sv new file mode 100755 index 0000000..c2aa75e --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_76_68_dec.sv @@ -0,0 +1,99 @@ +// 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 util/design/secded_gen.py + +module prim_secded_inv_hamming_76_68_dec ( + input [75:0] data_i, + output logic [67:0] data_o, + output logic [7:0] syndrome_o, + output logic [1:0] err_o +); + + always_comb begin : p_encode + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 76'hAA00000000000000000) & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 76'hAA00000000000000000) & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 76'hAA00000000000000000) & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 76'hAA00000000000000000) & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 76'hAA00000000000000000) & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 76'hAA00000000000000000) & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 76'hAA00000000000000000) & 76'h40FFE00000000000000); + syndrome_o[7] = ^((data_i ^ 76'hAA00000000000000000) & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + end +endmodule : prim_secded_inv_hamming_76_68_dec diff --git a/synth/prim/rtl/prim_secded_inv_hamming_76_68_enc.sv b/synth/prim/rtl/prim_secded_inv_hamming_76_68_enc.sv new file mode 100755 index 0000000..324f9c6 --- /dev/null +++ b/synth/prim/rtl/prim_secded_inv_hamming_76_68_enc.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 +// +// SECDED encoder generated by util/design/secded_gen.py + +module prim_secded_inv_hamming_76_68_enc ( + input [67:0] data_i, + output logic [75:0] data_o +); + + always_comb begin : p_encode + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + data_o ^= 76'hAA00000000000000000; + end + +endmodule : prim_secded_inv_hamming_76_68_enc diff --git a/synth/prim/rtl/prim_secded_pkg.sv b/synth/prim/rtl/prim_secded_pkg.sv new file mode 100755 index 0000000..4c17534 --- /dev/null +++ b/synth/prim/rtl/prim_secded_pkg.sv @@ -0,0 +1,1787 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SECDED package generated by +// util/design/secded_gen.py from util/design/data/secded_cfg.hjson + +package prim_secded_pkg; + + typedef enum int { + SecdedNone, + Secded_22_16, + Secded_28_22, + Secded_39_32, + Secded_64_57, + Secded_72_64, + SecdedHamming_22_16, + SecdedHamming_39_32, + SecdedHamming_72_64, + SecdedHamming_76_68, + SecdedInv_22_16, + SecdedInv_28_22, + SecdedInv_39_32, + SecdedInv_64_57, + SecdedInv_72_64, + SecdedInvHamming_22_16, + SecdedInvHamming_39_32, + SecdedInvHamming_72_64, + SecdedInvHamming_76_68 + } prim_secded_e; + + function automatic int get_ecc_data_width(prim_secded_e ecc_type); + case (ecc_type) + Secded_22_16: return 16; + Secded_28_22: return 22; + Secded_39_32: return 32; + Secded_64_57: return 57; + Secded_72_64: return 64; + SecdedHamming_22_16: return 16; + SecdedHamming_39_32: return 32; + SecdedHamming_72_64: return 64; + SecdedHamming_76_68: return 68; + SecdedInv_22_16: return 16; + SecdedInv_28_22: return 22; + SecdedInv_39_32: return 32; + SecdedInv_64_57: return 57; + SecdedInv_72_64: return 64; + SecdedInvHamming_22_16: return 16; + SecdedInvHamming_39_32: return 32; + SecdedInvHamming_72_64: return 64; + SecdedInvHamming_76_68: return 68; + // Return a non-zero width to avoid VCS compile issues + default: return 32; + endcase + endfunction + + function automatic int get_ecc_parity_width(prim_secded_e ecc_type); + case (ecc_type) + Secded_22_16: return 6; + Secded_28_22: return 6; + Secded_39_32: return 7; + Secded_64_57: return 7; + Secded_72_64: return 8; + SecdedHamming_22_16: return 6; + SecdedHamming_39_32: return 7; + SecdedHamming_72_64: return 8; + SecdedHamming_76_68: return 8; + SecdedInv_22_16: return 6; + SecdedInv_28_22: return 6; + SecdedInv_39_32: return 7; + SecdedInv_64_57: return 7; + SecdedInv_72_64: return 8; + SecdedInvHamming_22_16: return 6; + SecdedInvHamming_39_32: return 7; + SecdedInvHamming_72_64: return 8; + SecdedInvHamming_76_68: return 8; + default: return 0; + endcase + endfunction + + parameter logic [5:0] Secded2216ZeroEcc = 6'h0; + parameter logic [21:0] Secded2216ZeroWord = 22'h0; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_22_16_t; + + parameter logic [5:0] Secded2822ZeroEcc = 6'h0; + parameter logic [27:0] Secded2822ZeroWord = 28'h0; + + typedef struct packed { + logic [21:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_28_22_t; + + parameter logic [6:0] Secded3932ZeroEcc = 7'h0; + parameter logic [38:0] Secded3932ZeroWord = 39'h0; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_39_32_t; + + parameter logic [6:0] Secded6457ZeroEcc = 7'h0; + parameter logic [63:0] Secded6457ZeroWord = 64'h0; + + typedef struct packed { + logic [56:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_64_57_t; + + parameter logic [7:0] Secded7264ZeroEcc = 8'h0; + parameter logic [71:0] Secded7264ZeroWord = 72'h0; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_72_64_t; + + parameter logic [5:0] SecdedHamming2216ZeroEcc = 6'h0; + parameter logic [21:0] SecdedHamming2216ZeroWord = 22'h0; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_hamming_22_16_t; + + parameter logic [6:0] SecdedHamming3932ZeroEcc = 7'h0; + parameter logic [38:0] SecdedHamming3932ZeroWord = 39'h0; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_hamming_39_32_t; + + parameter logic [7:0] SecdedHamming7264ZeroEcc = 8'h0; + parameter logic [71:0] SecdedHamming7264ZeroWord = 72'h0; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_hamming_72_64_t; + + parameter logic [7:0] SecdedHamming7668ZeroEcc = 8'h0; + parameter logic [75:0] SecdedHamming7668ZeroWord = 76'h0; + + typedef struct packed { + logic [67:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_hamming_76_68_t; + + parameter logic [5:0] SecdedInv2216ZeroEcc = 6'h2A; + parameter logic [21:0] SecdedInv2216ZeroWord = 22'h2A0000; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_22_16_t; + + parameter logic [5:0] SecdedInv2822ZeroEcc = 6'h2A; + parameter logic [27:0] SecdedInv2822ZeroWord = 28'hA800000; + + typedef struct packed { + logic [21:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_28_22_t; + + parameter logic [6:0] SecdedInv3932ZeroEcc = 7'h2A; + parameter logic [38:0] SecdedInv3932ZeroWord = 39'h2A00000000; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_39_32_t; + + parameter logic [6:0] SecdedInv6457ZeroEcc = 7'h2A; + parameter logic [63:0] SecdedInv6457ZeroWord = 64'h5400000000000000; + + typedef struct packed { + logic [56:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_64_57_t; + + parameter logic [7:0] SecdedInv7264ZeroEcc = 8'hAA; + parameter logic [71:0] SecdedInv7264ZeroWord = 72'hAA0000000000000000; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_72_64_t; + + parameter logic [5:0] SecdedInvHamming2216ZeroEcc = 6'h2A; + parameter logic [21:0] SecdedInvHamming2216ZeroWord = 22'h2A0000; + + typedef struct packed { + logic [15:0] data; + logic [5:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_22_16_t; + + parameter logic [6:0] SecdedInvHamming3932ZeroEcc = 7'h2A; + parameter logic [38:0] SecdedInvHamming3932ZeroWord = 39'h2A00000000; + + typedef struct packed { + logic [31:0] data; + logic [6:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_39_32_t; + + parameter logic [7:0] SecdedInvHamming7264ZeroEcc = 8'hAA; + parameter logic [71:0] SecdedInvHamming7264ZeroWord = 72'hAA0000000000000000; + + typedef struct packed { + logic [63:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_72_64_t; + + parameter logic [7:0] SecdedInvHamming7668ZeroEcc = 8'hAA; + parameter logic [75:0] SecdedInvHamming7668ZeroWord = 76'hAA00000000000000000; + + typedef struct packed { + logic [67:0] data; + logic [7:0] syndrome; + logic [1:0] err; + } secded_inv_hamming_76_68_t; + + function automatic logic [21:0] + prim_secded_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + return data_o; + endfunction + + function automatic secded_22_16_t + prim_secded_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01496E); + syndrome_o[1] = ^(data_i & 22'h02F20B); + syndrome_o[2] = ^(data_i & 22'h048ED8); + syndrome_o[3] = ^(data_i & 22'h087714); + syndrome_o[4] = ^(data_i & 22'h10ACA5); + syndrome_o[5] = ^(data_i & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [27:0] + prim_secded_28_22_enc (logic [21:0] data_i); + logic [27:0] data_o; + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + return data_o; + endfunction + + function automatic secded_28_22_t + prim_secded_28_22_dec (logic [27:0] data_i); + logic [21:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_28_22_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 28'h07003FF); + syndrome_o[1] = ^(data_i & 28'h090FC0F); + syndrome_o[2] = ^(data_i & 28'h1271C71); + syndrome_o[3] = ^(data_i & 28'h23B6592); + syndrome_o[4] = ^(data_i & 28'h43DAAA4); + syndrome_o[5] = ^(data_i & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + return data_o; + endfunction + + function automatic secded_39_32_t + prim_secded_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h012606BD25); + syndrome_o[1] = ^(data_i & 39'h02DEBA8050); + syndrome_o[2] = ^(data_i & 39'h04413D89AA); + syndrome_o[3] = ^(data_i & 39'h0831234ED1); + syndrome_o[4] = ^(data_i & 39'h10C2C1323B); + syndrome_o[5] = ^(data_i & 39'h202DCC624C); + syndrome_o[6] = ^(data_i & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [63:0] + prim_secded_64_57_enc (logic [56:0] data_i); + logic [63:0] data_o; + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + return data_o; + endfunction + + function automatic secded_64_57_t + prim_secded_64_57_dec (logic [63:0] data_i); + logic [56:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_64_57_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 64'h0303FFF800007FFF); + syndrome_o[1] = ^(data_i & 64'h057C1FF801FF801F); + syndrome_o[2] = ^(data_i & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^(data_i & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^(data_i & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^(data_i & 64'h41F7BB56D5525488); + syndrome_o[6] = ^(data_i & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + return data_o; + endfunction + + function automatic secded_72_64_t + prim_secded_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^(data_i & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^(data_i & 72'h0467003FF003E007C1); + syndrome_o[3] = ^(data_i & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^(data_i & 72'h10B671C711C4438884); + syndrome_o[5] = ^(data_i & 72'h20B5B65926488C9108); + syndrome_o[6] = ^(data_i & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^(data_i & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_hamming_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + return data_o; + endfunction + + function automatic secded_hamming_22_16_t + prim_secded_hamming_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 22'h01AD5B); + syndrome_o[1] = ^(data_i & 22'h02366D); + syndrome_o[2] = ^(data_i & 22'h04C78E); + syndrome_o[3] = ^(data_i & 22'h0807F0); + syndrome_o[4] = ^(data_i & 22'h10F800); + syndrome_o[5] = ^(data_i & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_hamming_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_39_32_t + prim_secded_hamming_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 39'h0156AAAD5B); + syndrome_o[1] = ^(data_i & 39'h029B33366D); + syndrome_o[2] = ^(data_i & 39'h04E3C3C78E); + syndrome_o[3] = ^(data_i & 39'h0803FC07F0); + syndrome_o[4] = ^(data_i & 39'h1003FFF800); + syndrome_o[5] = ^(data_i & 39'h20FC000000); + syndrome_o[6] = ^(data_i & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_hamming_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_72_64_t + prim_secded_hamming_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 72'h02CD9999999B33366D); + syndrome_o[2] = ^(data_i & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 72'h40FE00000000000000); + syndrome_o[7] = ^(data_i & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [75:0] + prim_secded_hamming_76_68_enc (logic [67:0] data_i); + logic [75:0] data_o; + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + return data_o; + endfunction + + function automatic secded_hamming_76_68_t + prim_secded_hamming_76_68_dec (logic [75:0] data_i); + logic [67:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_hamming_76_68_t dec; + + // Syndrome calculation + syndrome_o[0] = ^(data_i & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^(data_i & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^(data_i & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^(data_i & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^(data_i & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^(data_i & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^(data_i & 76'h40FFE00000000000000); + syndrome_o[7] = ^(data_i & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_inv_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00496E); + data_o[17] = ^(data_o & 22'h00F20B); + data_o[18] = ^(data_o & 22'h008ED8); + data_o[19] = ^(data_o & 22'h007714); + data_o[20] = ^(data_o & 22'h00ACA5); + data_o[21] = ^(data_o & 22'h0011F3); + data_o ^= 22'h2A0000; + return data_o; + endfunction + + function automatic secded_inv_22_16_t + prim_secded_inv_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01496E); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02F20B); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h048ED8); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h087714); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10ACA5); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h2011F3); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h32) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h23) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h19) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h7) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h2c) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h31) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h34) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'he) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h1c) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h15) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h2a) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'hb) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h16) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [27:0] + prim_secded_inv_28_22_enc (logic [21:0] data_i); + logic [27:0] data_o; + data_o = 28'(data_i); + data_o[22] = ^(data_o & 28'h03003FF); + data_o[23] = ^(data_o & 28'h010FC0F); + data_o[24] = ^(data_o & 28'h0271C71); + data_o[25] = ^(data_o & 28'h03B6592); + data_o[26] = ^(data_o & 28'h03DAAA4); + data_o[27] = ^(data_o & 28'h03ED348); + data_o ^= 28'hA800000; + return data_o; + endfunction + + function automatic secded_inv_28_22_t + prim_secded_inv_28_22_dec (logic [27:0] data_i); + logic [21:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_28_22_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 28'hA800000) & 28'h07003FF); + syndrome_o[1] = ^((data_i ^ 28'hA800000) & 28'h090FC0F); + syndrome_o[2] = ^((data_i ^ 28'hA800000) & 28'h1271C71); + syndrome_o[3] = ^((data_i ^ 28'hA800000) & 28'h23B6592); + syndrome_o[4] = ^((data_i ^ 28'hA800000) & 28'h43DAAA4); + syndrome_o[5] = ^((data_i ^ 28'hA800000) & 28'h83ED348); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'hd) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h25) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h19) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h29) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h31) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'he) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h16) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h26) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h1a) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h2a) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h32) ^ data_i[15]; + data_o[16] = (syndrome_o == 6'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 6'h2c) ^ data_i[17]; + data_o[18] = (syndrome_o == 6'h34) ^ data_i[18]; + data_o[19] = (syndrome_o == 6'h38) ^ data_i[19]; + data_o[20] = (syndrome_o == 6'h3b) ^ data_i[20]; + data_o[21] = (syndrome_o == 6'h3d) ^ data_i[21]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_inv_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h002606BD25); + data_o[33] = ^(data_o & 39'h00DEBA8050); + data_o[34] = ^(data_o & 39'h00413D89AA); + data_o[35] = ^(data_o & 39'h0031234ED1); + data_o[36] = ^(data_o & 39'h00C2C1323B); + data_o[37] = ^(data_o & 39'h002DCC624C); + data_o[38] = ^(data_o & 39'h0098505586); + data_o ^= 39'h2A00000000; + return data_o; + endfunction + + function automatic secded_inv_39_32_t + prim_secded_inv_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h012606BD25); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h02DEBA8050); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04413D89AA); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0831234ED1); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h10C2C1323B); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h202DCC624C); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h4098505586); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h19) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h54) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h61) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h34) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h1a) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h15) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h2a) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h38) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h49) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'hd) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h51) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h31) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h68) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h7) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h1c) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'hb) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h25) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h26) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h46) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h70) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h32) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h2c) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h13) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h23) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h29) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h16) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h52) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [63:0] + prim_secded_inv_64_57_enc (logic [56:0] data_i); + logic [63:0] data_o; + data_o = 64'(data_i); + data_o[57] = ^(data_o & 64'h0103FFF800007FFF); + data_o[58] = ^(data_o & 64'h017C1FF801FF801F); + data_o[59] = ^(data_o & 64'h01BDE1F87E0781E1); + data_o[60] = ^(data_o & 64'h01DEEE3B8E388E22); + data_o[61] = ^(data_o & 64'h01EF76CDB2C93244); + data_o[62] = ^(data_o & 64'h01F7BB56D5525488); + data_o[63] = ^(data_o & 64'h01FBDDA769A46910); + data_o ^= 64'h5400000000000000; + return data_o; + endfunction + + function automatic secded_inv_64_57_t + prim_secded_inv_64_57_dec (logic [63:0] data_i); + logic [56:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_64_57_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 64'h5400000000000000) & 64'h0303FFF800007FFF); + syndrome_o[1] = ^((data_i ^ 64'h5400000000000000) & 64'h057C1FF801FF801F); + syndrome_o[2] = ^((data_i ^ 64'h5400000000000000) & 64'h09BDE1F87E0781E1); + syndrome_o[3] = ^((data_i ^ 64'h5400000000000000) & 64'h11DEEE3B8E388E22); + syndrome_o[4] = ^((data_i ^ 64'h5400000000000000) & 64'h21EF76CDB2C93244); + syndrome_o[5] = ^((data_i ^ 64'h5400000000000000) & 64'h41F7BB56D5525488); + syndrome_o[6] = ^((data_i ^ 64'h5400000000000000) & 64'h81FBDDA769A46910); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'hd) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h15) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h25) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h45) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h19) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h29) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h49) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h31) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h51) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h61) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'he) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h16) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h26) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h46) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h1a) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h2a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h4a) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h32) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h52) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h62) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h1c) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h2c) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h4c) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h34) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h54) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h64) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h38) ^ data_i[31]; + data_o[32] = (syndrome_o == 7'h58) ^ data_i[32]; + data_o[33] = (syndrome_o == 7'h68) ^ data_i[33]; + data_o[34] = (syndrome_o == 7'h70) ^ data_i[34]; + data_o[35] = (syndrome_o == 7'h1f) ^ data_i[35]; + data_o[36] = (syndrome_o == 7'h2f) ^ data_i[36]; + data_o[37] = (syndrome_o == 7'h4f) ^ data_i[37]; + data_o[38] = (syndrome_o == 7'h37) ^ data_i[38]; + data_o[39] = (syndrome_o == 7'h57) ^ data_i[39]; + data_o[40] = (syndrome_o == 7'h67) ^ data_i[40]; + data_o[41] = (syndrome_o == 7'h3b) ^ data_i[41]; + data_o[42] = (syndrome_o == 7'h5b) ^ data_i[42]; + data_o[43] = (syndrome_o == 7'h6b) ^ data_i[43]; + data_o[44] = (syndrome_o == 7'h73) ^ data_i[44]; + data_o[45] = (syndrome_o == 7'h3d) ^ data_i[45]; + data_o[46] = (syndrome_o == 7'h5d) ^ data_i[46]; + data_o[47] = (syndrome_o == 7'h6d) ^ data_i[47]; + data_o[48] = (syndrome_o == 7'h75) ^ data_i[48]; + data_o[49] = (syndrome_o == 7'h79) ^ data_i[49]; + data_o[50] = (syndrome_o == 7'h3e) ^ data_i[50]; + data_o[51] = (syndrome_o == 7'h5e) ^ data_i[51]; + data_o[52] = (syndrome_o == 7'h6e) ^ data_i[52]; + data_o[53] = (syndrome_o == 7'h76) ^ data_i[53]; + data_o[54] = (syndrome_o == 7'h7a) ^ data_i[54]; + data_o[55] = (syndrome_o == 7'h7c) ^ data_i[55]; + data_o[56] = (syndrome_o == 7'h7f) ^ data_i[56]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_inv_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00B9000000001FFFFF); + data_o[65] = ^(data_o & 72'h005E00000FFFE0003F); + data_o[66] = ^(data_o & 72'h0067003FF003E007C1); + data_o[67] = ^(data_o & 72'h00CD0FC0F03C207842); + data_o[68] = ^(data_o & 72'h00B671C711C4438884); + data_o[69] = ^(data_o & 72'h00B5B65926488C9108); + data_o[70] = ^(data_o & 72'h00CBDAAA4A91152210); + data_o[71] = ^(data_o & 72'h007AED348D221A4420); + data_o ^= 72'hAA0000000000000000; + return data_o; + endfunction + + function automatic secded_inv_72_64_t + prim_secded_inv_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01B9000000001FFFFF); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h025E00000FFFE0003F); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0467003FF003E007C1); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h08CD0FC0F03C207842); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h10B671C711C4438884); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h20B5B65926488C9108); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40CBDAAA4A91152210); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'h807AED348D221A4420); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h7) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'hb) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h13) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h23) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h43) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h83) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'hd) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h15) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h25) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h45) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h85) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h19) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h29) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h49) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h89) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h31) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h51) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h91) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h61) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'ha1) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'hc1) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'he) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h16) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h26) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h46) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h86) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'h1a) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'h2a) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'h4a) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'h8a) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'h32) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'h52) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'h92) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'h62) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha2) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'hc2) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'h1c) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'h2c) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'h4c) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'h8c) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'h34) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'h54) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'h94) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'h64) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'ha4) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hc4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'h38) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'h58) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'h98) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'h68) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'ha8) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hc8) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'h70) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hb0) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hd0) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'he0) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'h6d) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hd6) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'h3e) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hcb) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hb3) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hb5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hce) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'h79) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = ^syndrome_o; + err_o[1] = ~err_o[0] & (|syndrome_o); + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [21:0] + prim_secded_inv_hamming_22_16_enc (logic [15:0] data_i); + logic [21:0] data_o; + data_o = 22'(data_i); + data_o[16] = ^(data_o & 22'h00AD5B); + data_o[17] = ^(data_o & 22'h00366D); + data_o[18] = ^(data_o & 22'h00C78E); + data_o[19] = ^(data_o & 22'h0007F0); + data_o[20] = ^(data_o & 22'h00F800); + data_o[21] = ^(data_o & 22'h1FFFFF); + data_o ^= 22'h2A0000; + return data_o; + endfunction + + function automatic secded_inv_hamming_22_16_t + prim_secded_inv_hamming_22_16_dec (logic [21:0] data_i); + logic [15:0] data_o; + logic [5:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_22_16_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 22'h2A0000) & 22'h01AD5B); + syndrome_o[1] = ^((data_i ^ 22'h2A0000) & 22'h02366D); + syndrome_o[2] = ^((data_i ^ 22'h2A0000) & 22'h04C78E); + syndrome_o[3] = ^((data_i ^ 22'h2A0000) & 22'h0807F0); + syndrome_o[4] = ^((data_i ^ 22'h2A0000) & 22'h10F800); + syndrome_o[5] = ^((data_i ^ 22'h2A0000) & 22'h3FFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 6'h23) ^ data_i[0]; + data_o[1] = (syndrome_o == 6'h25) ^ data_i[1]; + data_o[2] = (syndrome_o == 6'h26) ^ data_i[2]; + data_o[3] = (syndrome_o == 6'h27) ^ data_i[3]; + data_o[4] = (syndrome_o == 6'h29) ^ data_i[4]; + data_o[5] = (syndrome_o == 6'h2a) ^ data_i[5]; + data_o[6] = (syndrome_o == 6'h2b) ^ data_i[6]; + data_o[7] = (syndrome_o == 6'h2c) ^ data_i[7]; + data_o[8] = (syndrome_o == 6'h2d) ^ data_i[8]; + data_o[9] = (syndrome_o == 6'h2e) ^ data_i[9]; + data_o[10] = (syndrome_o == 6'h2f) ^ data_i[10]; + data_o[11] = (syndrome_o == 6'h31) ^ data_i[11]; + data_o[12] = (syndrome_o == 6'h32) ^ data_i[12]; + data_o[13] = (syndrome_o == 6'h33) ^ data_i[13]; + data_o[14] = (syndrome_o == 6'h34) ^ data_i[14]; + data_o[15] = (syndrome_o == 6'h35) ^ data_i[15]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[5]; + err_o[1] = |syndrome_o[4:0] & ~syndrome_o[5]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [38:0] + prim_secded_inv_hamming_39_32_enc (logic [31:0] data_i); + logic [38:0] data_o; + data_o = 39'(data_i); + data_o[32] = ^(data_o & 39'h0056AAAD5B); + data_o[33] = ^(data_o & 39'h009B33366D); + data_o[34] = ^(data_o & 39'h00E3C3C78E); + data_o[35] = ^(data_o & 39'h0003FC07F0); + data_o[36] = ^(data_o & 39'h0003FFF800); + data_o[37] = ^(data_o & 39'h00FC000000); + data_o[38] = ^(data_o & 39'h3FFFFFFFFF); + data_o ^= 39'h2A00000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_39_32_t + prim_secded_inv_hamming_39_32_dec (logic [38:0] data_i); + logic [31:0] data_o; + logic [6:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_39_32_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 39'h2A00000000) & 39'h0156AAAD5B); + syndrome_o[1] = ^((data_i ^ 39'h2A00000000) & 39'h029B33366D); + syndrome_o[2] = ^((data_i ^ 39'h2A00000000) & 39'h04E3C3C78E); + syndrome_o[3] = ^((data_i ^ 39'h2A00000000) & 39'h0803FC07F0); + syndrome_o[4] = ^((data_i ^ 39'h2A00000000) & 39'h1003FFF800); + syndrome_o[5] = ^((data_i ^ 39'h2A00000000) & 39'h20FC000000); + syndrome_o[6] = ^((data_i ^ 39'h2A00000000) & 39'h7FFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 7'h43) ^ data_i[0]; + data_o[1] = (syndrome_o == 7'h45) ^ data_i[1]; + data_o[2] = (syndrome_o == 7'h46) ^ data_i[2]; + data_o[3] = (syndrome_o == 7'h47) ^ data_i[3]; + data_o[4] = (syndrome_o == 7'h49) ^ data_i[4]; + data_o[5] = (syndrome_o == 7'h4a) ^ data_i[5]; + data_o[6] = (syndrome_o == 7'h4b) ^ data_i[6]; + data_o[7] = (syndrome_o == 7'h4c) ^ data_i[7]; + data_o[8] = (syndrome_o == 7'h4d) ^ data_i[8]; + data_o[9] = (syndrome_o == 7'h4e) ^ data_i[9]; + data_o[10] = (syndrome_o == 7'h4f) ^ data_i[10]; + data_o[11] = (syndrome_o == 7'h51) ^ data_i[11]; + data_o[12] = (syndrome_o == 7'h52) ^ data_i[12]; + data_o[13] = (syndrome_o == 7'h53) ^ data_i[13]; + data_o[14] = (syndrome_o == 7'h54) ^ data_i[14]; + data_o[15] = (syndrome_o == 7'h55) ^ data_i[15]; + data_o[16] = (syndrome_o == 7'h56) ^ data_i[16]; + data_o[17] = (syndrome_o == 7'h57) ^ data_i[17]; + data_o[18] = (syndrome_o == 7'h58) ^ data_i[18]; + data_o[19] = (syndrome_o == 7'h59) ^ data_i[19]; + data_o[20] = (syndrome_o == 7'h5a) ^ data_i[20]; + data_o[21] = (syndrome_o == 7'h5b) ^ data_i[21]; + data_o[22] = (syndrome_o == 7'h5c) ^ data_i[22]; + data_o[23] = (syndrome_o == 7'h5d) ^ data_i[23]; + data_o[24] = (syndrome_o == 7'h5e) ^ data_i[24]; + data_o[25] = (syndrome_o == 7'h5f) ^ data_i[25]; + data_o[26] = (syndrome_o == 7'h61) ^ data_i[26]; + data_o[27] = (syndrome_o == 7'h62) ^ data_i[27]; + data_o[28] = (syndrome_o == 7'h63) ^ data_i[28]; + data_o[29] = (syndrome_o == 7'h64) ^ data_i[29]; + data_o[30] = (syndrome_o == 7'h65) ^ data_i[30]; + data_o[31] = (syndrome_o == 7'h66) ^ data_i[31]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[6]; + err_o[1] = |syndrome_o[5:0] & ~syndrome_o[6]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [71:0] + prim_secded_inv_hamming_72_64_enc (logic [63:0] data_i); + logic [71:0] data_o; + data_o = 72'(data_i); + data_o[64] = ^(data_o & 72'h00AB55555556AAAD5B); + data_o[65] = ^(data_o & 72'h00CD9999999B33366D); + data_o[66] = ^(data_o & 72'h00F1E1E1E1E3C3C78E); + data_o[67] = ^(data_o & 72'h0001FE01FE03FC07F0); + data_o[68] = ^(data_o & 72'h0001FFFE0003FFF800); + data_o[69] = ^(data_o & 72'h0001FFFFFFFC000000); + data_o[70] = ^(data_o & 72'h00FE00000000000000); + data_o[71] = ^(data_o & 72'h7FFFFFFFFFFFFFFFFF); + data_o ^= 72'hAA0000000000000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_72_64_t + prim_secded_inv_hamming_72_64_dec (logic [71:0] data_i); + logic [63:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_72_64_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 72'hAA0000000000000000) & 72'h01AB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 72'hAA0000000000000000) & 72'h02CD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 72'hAA0000000000000000) & 72'h04F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 72'hAA0000000000000000) & 72'h0801FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 72'hAA0000000000000000) & 72'h1001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 72'hAA0000000000000000) & 72'h2001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 72'hAA0000000000000000) & 72'h40FE00000000000000); + syndrome_o[7] = ^((data_i ^ 72'hAA0000000000000000) & 72'hFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + function automatic logic [75:0] + prim_secded_inv_hamming_76_68_enc (logic [67:0] data_i); + logic [75:0] data_o; + data_o = 76'(data_i); + data_o[68] = ^(data_o & 76'h00AAB55555556AAAD5B); + data_o[69] = ^(data_o & 76'h00CCD9999999B33366D); + data_o[70] = ^(data_o & 76'h000F1E1E1E1E3C3C78E); + data_o[71] = ^(data_o & 76'h00F01FE01FE03FC07F0); + data_o[72] = ^(data_o & 76'h00001FFFE0003FFF800); + data_o[73] = ^(data_o & 76'h00001FFFFFFFC000000); + data_o[74] = ^(data_o & 76'h00FFE00000000000000); + data_o[75] = ^(data_o & 76'h7FFFFFFFFFFFFFFFFFF); + data_o ^= 76'hAA00000000000000000; + return data_o; + endfunction + + function automatic secded_inv_hamming_76_68_t + prim_secded_inv_hamming_76_68_dec (logic [75:0] data_i); + logic [67:0] data_o; + logic [7:0] syndrome_o; + logic [1:0] err_o; + + secded_inv_hamming_76_68_t dec; + + // Syndrome calculation + syndrome_o[0] = ^((data_i ^ 76'hAA00000000000000000) & 76'h01AAB55555556AAAD5B); + syndrome_o[1] = ^((data_i ^ 76'hAA00000000000000000) & 76'h02CCD9999999B33366D); + syndrome_o[2] = ^((data_i ^ 76'hAA00000000000000000) & 76'h040F1E1E1E1E3C3C78E); + syndrome_o[3] = ^((data_i ^ 76'hAA00000000000000000) & 76'h08F01FE01FE03FC07F0); + syndrome_o[4] = ^((data_i ^ 76'hAA00000000000000000) & 76'h10001FFFE0003FFF800); + syndrome_o[5] = ^((data_i ^ 76'hAA00000000000000000) & 76'h20001FFFFFFFC000000); + syndrome_o[6] = ^((data_i ^ 76'hAA00000000000000000) & 76'h40FFE00000000000000); + syndrome_o[7] = ^((data_i ^ 76'hAA00000000000000000) & 76'hFFFFFFFFFFFFFFFFFFF); + + // Corrected output calculation + data_o[0] = (syndrome_o == 8'h83) ^ data_i[0]; + data_o[1] = (syndrome_o == 8'h85) ^ data_i[1]; + data_o[2] = (syndrome_o == 8'h86) ^ data_i[2]; + data_o[3] = (syndrome_o == 8'h87) ^ data_i[3]; + data_o[4] = (syndrome_o == 8'h89) ^ data_i[4]; + data_o[5] = (syndrome_o == 8'h8a) ^ data_i[5]; + data_o[6] = (syndrome_o == 8'h8b) ^ data_i[6]; + data_o[7] = (syndrome_o == 8'h8c) ^ data_i[7]; + data_o[8] = (syndrome_o == 8'h8d) ^ data_i[8]; + data_o[9] = (syndrome_o == 8'h8e) ^ data_i[9]; + data_o[10] = (syndrome_o == 8'h8f) ^ data_i[10]; + data_o[11] = (syndrome_o == 8'h91) ^ data_i[11]; + data_o[12] = (syndrome_o == 8'h92) ^ data_i[12]; + data_o[13] = (syndrome_o == 8'h93) ^ data_i[13]; + data_o[14] = (syndrome_o == 8'h94) ^ data_i[14]; + data_o[15] = (syndrome_o == 8'h95) ^ data_i[15]; + data_o[16] = (syndrome_o == 8'h96) ^ data_i[16]; + data_o[17] = (syndrome_o == 8'h97) ^ data_i[17]; + data_o[18] = (syndrome_o == 8'h98) ^ data_i[18]; + data_o[19] = (syndrome_o == 8'h99) ^ data_i[19]; + data_o[20] = (syndrome_o == 8'h9a) ^ data_i[20]; + data_o[21] = (syndrome_o == 8'h9b) ^ data_i[21]; + data_o[22] = (syndrome_o == 8'h9c) ^ data_i[22]; + data_o[23] = (syndrome_o == 8'h9d) ^ data_i[23]; + data_o[24] = (syndrome_o == 8'h9e) ^ data_i[24]; + data_o[25] = (syndrome_o == 8'h9f) ^ data_i[25]; + data_o[26] = (syndrome_o == 8'ha1) ^ data_i[26]; + data_o[27] = (syndrome_o == 8'ha2) ^ data_i[27]; + data_o[28] = (syndrome_o == 8'ha3) ^ data_i[28]; + data_o[29] = (syndrome_o == 8'ha4) ^ data_i[29]; + data_o[30] = (syndrome_o == 8'ha5) ^ data_i[30]; + data_o[31] = (syndrome_o == 8'ha6) ^ data_i[31]; + data_o[32] = (syndrome_o == 8'ha7) ^ data_i[32]; + data_o[33] = (syndrome_o == 8'ha8) ^ data_i[33]; + data_o[34] = (syndrome_o == 8'ha9) ^ data_i[34]; + data_o[35] = (syndrome_o == 8'haa) ^ data_i[35]; + data_o[36] = (syndrome_o == 8'hab) ^ data_i[36]; + data_o[37] = (syndrome_o == 8'hac) ^ data_i[37]; + data_o[38] = (syndrome_o == 8'had) ^ data_i[38]; + data_o[39] = (syndrome_o == 8'hae) ^ data_i[39]; + data_o[40] = (syndrome_o == 8'haf) ^ data_i[40]; + data_o[41] = (syndrome_o == 8'hb0) ^ data_i[41]; + data_o[42] = (syndrome_o == 8'hb1) ^ data_i[42]; + data_o[43] = (syndrome_o == 8'hb2) ^ data_i[43]; + data_o[44] = (syndrome_o == 8'hb3) ^ data_i[44]; + data_o[45] = (syndrome_o == 8'hb4) ^ data_i[45]; + data_o[46] = (syndrome_o == 8'hb5) ^ data_i[46]; + data_o[47] = (syndrome_o == 8'hb6) ^ data_i[47]; + data_o[48] = (syndrome_o == 8'hb7) ^ data_i[48]; + data_o[49] = (syndrome_o == 8'hb8) ^ data_i[49]; + data_o[50] = (syndrome_o == 8'hb9) ^ data_i[50]; + data_o[51] = (syndrome_o == 8'hba) ^ data_i[51]; + data_o[52] = (syndrome_o == 8'hbb) ^ data_i[52]; + data_o[53] = (syndrome_o == 8'hbc) ^ data_i[53]; + data_o[54] = (syndrome_o == 8'hbd) ^ data_i[54]; + data_o[55] = (syndrome_o == 8'hbe) ^ data_i[55]; + data_o[56] = (syndrome_o == 8'hbf) ^ data_i[56]; + data_o[57] = (syndrome_o == 8'hc1) ^ data_i[57]; + data_o[58] = (syndrome_o == 8'hc2) ^ data_i[58]; + data_o[59] = (syndrome_o == 8'hc3) ^ data_i[59]; + data_o[60] = (syndrome_o == 8'hc4) ^ data_i[60]; + data_o[61] = (syndrome_o == 8'hc5) ^ data_i[61]; + data_o[62] = (syndrome_o == 8'hc6) ^ data_i[62]; + data_o[63] = (syndrome_o == 8'hc7) ^ data_i[63]; + data_o[64] = (syndrome_o == 8'hc8) ^ data_i[64]; + data_o[65] = (syndrome_o == 8'hc9) ^ data_i[65]; + data_o[66] = (syndrome_o == 8'hca) ^ data_i[66]; + data_o[67] = (syndrome_o == 8'hcb) ^ data_i[67]; + + // err_o calc. bit0: single error, bit1: double error + err_o[0] = syndrome_o[7]; + err_o[1] = |syndrome_o[6:0] & ~syndrome_o[7]; + + dec.data = data_o; + dec.syndrome = syndrome_o; + dec.err = err_o; + return dec; + + endfunction + + +endpackage diff --git a/synth/prim/rtl/prim_sha2.sv b/synth/prim/rtl/prim_sha2.sv new file mode 100755 index 0000000..06559db --- /dev/null +++ b/synth/prim/rtl/prim_sha2.sv @@ -0,0 +1,494 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SHA-256/384/512 configurable mode engine (64-bit word datapath) + +module prim_sha2 import prim_sha2_pkg::*; +#( + parameter bit MultimodeEn = 0, // assert to enable multi-mode digest feature + + localparam int unsigned RndWidth256 = $clog2(NumRound256), + localparam int unsigned RndWidth512 = $clog2(NumRound512), + localparam sha_word64_t ZeroWord = '0 + ) ( + input clk_i, + input rst_ni, + + input wipe_secret_i, + input sha_word64_t wipe_v_i, + // control signals and message words input to the message FIFO + input fifo_rvalid_i, // indicates that the message FIFO (prim_sync_fifo) has words + // ready to write into the SHA-2 padding buffer + input sha_fifo64_t fifo_rdata_i, + output logic fifo_rready_o, // indicates the internal padding buffer is ready to receive + // words from the message FIFO + // control signals + input sha_en_i, // if disabled, it clears internal content + input hash_start_i, // start hashing: initialize data counter to zero and clear + // digest + input hash_continue_i, // continue hashing: set data counter to `message_length_i` + // and use current digest + input digest_mode_e digest_mode_i, + input hash_process_i, + output logic hash_done_o, + + input [127:0] message_length_i, // bits but byte based + input sha_word64_t [7:0] digest_i, + input logic [7:0] digest_we_i, + output sha_word64_t [7:0] digest_o, // tie off unused port slice when MultimodeEn = 0 + output logic hash_running_o, // `1` iff hash computation is active (as opposed to `idle_o`, which + // is also `0` and thus 'busy' when waiting for a FIFO input) + output logic idle_o +); + + logic msg_feed_complete; + logic shaf_rready; + logic shaf_rvalid; + + // control signals - shared for both modes + logic update_w_from_fifo, calculate_next_w; + logic init_hash, run_hash, one_chunk_done; + logic update_digest, clear_digest; + logic hash_done_next; // to meet the phase with digest value + logic hash_go; + + // datapath signals - shared for both modes + logic [RndWidth512-1:0] round_d, round_q; + logic [3:0] w_index_d, w_index_q; + digest_mode_e digest_mode_flag_d, digest_mode_flag_q; + sha_word64_t shaf_rdata; + + // tie off unused input ports and signals slices + if (!MultimodeEn) begin : gen_tie_unused + logic [7:0] unused_digest_upper; + for (genvar i = 0; i < 8; i++) begin : gen_unused_digest_upper + assign unused_digest_upper[i] = ^digest_i[i][63:32]; + end + logic unused_signals; + assign unused_signals = ^{wipe_v_i[63:32], shaf_rdata[63:32], unused_digest_upper}; + end + + // Most operations and control signals are identical no matter if we are starting or continuing + // to hash. + assign hash_go = hash_start_i | hash_continue_i; + + assign digest_mode_flag_d = hash_go ? digest_mode_i : // latch in configured mode + hash_done_o ? None : // clear + digest_mode_flag_q; // keep + + if (MultimodeEn) begin : gen_multimode + // datapath signal definitions for multi-mode + sha_word64_t [7:0] hash_d, hash_q; // a,b,c,d,e,f,g,h + sha_word64_t [15:0] w_d, w_q; + sha_word64_t [7:0] digest_d, digest_q; + + // compute w + always_comb begin : compute_w_multimode + w_d = w_q; + if (wipe_secret_i) begin + w_d = w_q ^ {16{wipe_v_i[63:0]}}; + end else if (!sha_en_i || hash_go) begin + w_d = '0; + end else if (!run_hash && update_w_from_fifo) begin + // this logic runs at the first stage of SHA: hash not running yet, + // still filling in first 16 words + w_d = {shaf_rdata, w_q[15:1]}; + end else if (calculate_next_w) begin // message scheduling/derivation for last 48/64 rounds + if (digest_mode_flag_q == SHA2_256) begin + // this computes the next w[16] and shifts out w[0] into compression below + w_d = {{32'b0, calc_w_256(w_q[0][31:0], w_q[1][31:0], w_q[9][31:0], + w_q[14][31:0])}, w_q[15:1]}; + end else if ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) begin + w_d = {calc_w_512(w_q[0], w_q[1], w_q[9], w_q[14]), w_q[15:1]}; + end + end else if (run_hash) begin + // just shift-out the words as they get consumed. There's no incoming data. + w_d = {ZeroWord, w_q[15:1]}; + end + end : compute_w_multimode + + // update w + always_ff @(posedge clk_i or negedge rst_ni) begin : update_w_multimode + if (!rst_ni) w_q <= '0; + else if (MultimodeEn) w_q <= w_d; + end : update_w_multimode + + // compute hash + always_comb begin : compression_multimode + hash_d = hash_q; + if (wipe_secret_i) begin + for (int i = 0; i < 8; i++) begin + hash_d[i] = hash_q[i] ^ wipe_v_i; + end + end else if (init_hash) begin + hash_d = digest_q; + end else if (run_hash) begin + if (digest_mode_flag_q == SHA2_256) begin + hash_d = compress_multi_256(w_q[0][31:0], + CubicRootPrime256[round_q[RndWidth256-1:0]], hash_q); + end else if ((digest_mode_flag_q == SHA2_512) || (digest_mode_flag_q == SHA2_384)) begin + hash_d = compress_512(w_q[0], CubicRootPrime512[round_q], hash_q); + end + end + end : compression_multimode + + // update hash + always_ff @(posedge clk_i or negedge rst_ni) begin : update_hash_multimode + if (!rst_ni) hash_q <= '0; + else hash_q <= hash_d; + end : update_hash_multimode + + // compute digest + always_comb begin : compute_digest_multimode + digest_d = digest_q; + if (wipe_secret_i) begin + for (int i = 0 ; i < 8 ; i++) begin + digest_d[i] = digest_q[i] ^ wipe_v_i; + end + end else if (hash_start_i) begin + for (int i = 0 ; i < 8 ; i++) begin + if (digest_mode_i == SHA2_256) begin + digest_d[i] = {32'b0, InitHash_256[i]}; + end else if (digest_mode_i == SHA2_384) begin + digest_d[i] = InitHash_384[i]; + end else if (digest_mode_i == SHA2_512) begin + digest_d[i] = InitHash_512[i]; + end + end + end else if (clear_digest) begin + digest_d = '0; + end else if (!sha_en_i) begin + for (int i = 0; i < 8; i++) begin + digest_d[i] = digest_we_i[i] ? digest_i[i] : digest_q[i]; + end + end else if (update_digest) begin + for (int i = 0 ; i < 8 ; i++) begin + digest_d[i] = digest_q[i] + hash_q[i]; + end + if (hash_done_o == 1'b1 && digest_mode_flag_q == SHA2_384) begin + // final digest truncation for SHA-2 384 + digest_d[6] = '0; + digest_d[7] = '0; + end else if (hash_done_o == 1'b1 && digest_mode_flag_q == SHA2_256) begin + // make sure to clear out most significant 32-bits of each digest word (zero-padding) + for (int i = 0 ; i < 8 ; i++) begin + digest_d[i][63:32] = 32'b0; + end + end + end + end : compute_digest_multimode + + // update digest + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) digest_q <= '0; + else digest_q <= digest_d; + end + + // assign digest to output + assign digest_o = digest_q; + + end else begin : gen_256 // MultimodeEn = 0 + // datapath signal definitions for SHA-2 256 only + sha_word32_t shaf_rdata256; + sha_word32_t [7:0] hash256_d, hash256_q; // a,b,c,d,e,f,g,h + sha_word32_t [15:0] w256_d, w256_q; + sha_word32_t [7:0] digest256_d, digest256_q; + + assign shaf_rdata256 = shaf_rdata[31:0]; + + always_comb begin : compute_w_256 + // ~MultimodeEn + w256_d = w256_q; + if (wipe_secret_i) begin + w256_d = w256_q ^ {16{wipe_v_i[31:0]}}; + end else if (!sha_en_i || hash_go) begin + w256_d = '0; + end else if (!run_hash && update_w_from_fifo) begin + // this logic runs at the first stage of SHA: hash not running yet, + // still filling in first 16 words + w256_d = {shaf_rdata256, w256_q[15:1]}; + end else if (calculate_next_w) begin // message scheduling/derivation for last 48/64 rounds + w256_d = {calc_w_256(w256_q[0][31:0], w256_q[1][31:0], w256_q[9][31:0], + w256_q[14][31:0]), w256_q[15:1]}; + end else if (run_hash) begin + // just shift-out the words as they get consumed. There's no incoming data. + w256_d = {ZeroWord[31:0], w256_q[15:1]}; + end + end : compute_w_256 + + // update w_256 + always_ff @(posedge clk_i or negedge rst_ni) begin : update_w_256 + if (!rst_ni) w256_q <= '0; + else if (!MultimodeEn) w256_q <= w256_d; + end : update_w_256 + + // compute hash_256 + always_comb begin : compression_256 + hash256_d = hash256_q; + if (wipe_secret_i) begin + for (int i = 0; i < 8; i++) begin + hash256_d[i] = hash256_q[i] ^ wipe_v_i[31:0]; + end + end else if (init_hash) begin + hash256_d = digest256_q; + end else if (run_hash) begin + hash256_d = compress_256(w256_q[0], CubicRootPrime256[round_q[RndWidth256-1:0]], hash256_q); + end + end : compression_256 + + // update hash_256 + always_ff @(posedge clk_i or negedge rst_ni) begin : update_hash256 + if (!rst_ni) hash256_q <= '0; + else hash256_q <= hash256_d; + end : update_hash256 + + // compute digest_256 + always_comb begin : compute_digest_256 + digest256_d = digest256_q; + if (wipe_secret_i) begin + for (int i = 0 ; i < 8 ; i++) begin + digest256_d[i] = digest256_q[i] ^ wipe_v_i[31:0]; + end + end else if (hash_start_i) begin + for (int i = 0 ; i < 8 ; i++) begin + digest256_d[i] = InitHash_256[i]; + end + end else if (clear_digest) begin + digest256_d = '0; + end else if (!sha_en_i) begin + for (int i = 0; i < 8; i++) begin + digest256_d[i] = digest_we_i[i] ? digest_i[i][31:0] : digest256_q[i]; + end + end else if (update_digest) begin + for (int i = 0 ; i < 8 ; i++) begin + digest256_d[i] = digest256_q[i] + hash256_q[i]; + end + end + end : compute_digest_256 + + // update digest_256 + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) digest256_q <= '0; + else digest256_q <= digest256_d; + end + + // assign digest to output + for (genvar i = 0; i < 8; i++) begin : gen_assign_digest_256 + assign digest_o[i][31:0] = digest256_q[i]; + assign digest_o[i][63:32] = 32'b0; + end + end + + // compute round counter (shared) + always_comb begin : round_counter + round_d = round_q; + if (!sha_en_i || hash_go) begin + round_d = '0; + end else if (run_hash) begin + if (((round_q[RndWidth256-1:0] == RndWidth256'(unsigned'(NumRound256-1))) && + (digest_mode_flag_q == SHA2_256 || !MultimodeEn)) || + ((round_q == RndWidth512'(unsigned'(NumRound512-1))) && + ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)))) begin + round_d = '0; + end else begin + round_d = round_q + 1; + end + end + end + + // update round counter (shared) + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) round_q <= '0; + else round_q <= round_d; + end + + // compute w_index (shared) + assign w_index_d = (~sha_en_i || hash_go) ? '0 : // clear + update_w_from_fifo ? w_index_q + 1 : // increment + w_index_q; // keep + // update w_index (shared) + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) w_index_q <= '0; + else w_index_q <= w_index_d; + end + + // ready for a word from the padding buffer in sha2_pad + assign shaf_rready = update_w_from_fifo; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) hash_done_o <= 1'b0; + else hash_done_o <= hash_done_next; + end + + typedef enum logic [1:0] { + FifoIdle, + FifoLoadFromFifo, + FifoWait + } fifoctl_state_e; + + fifoctl_state_e fifo_st_q, fifo_st_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) fifo_st_q <= FifoIdle; + else fifo_st_q <= fifo_st_d; + end + + always_comb begin + fifo_st_d = FifoIdle; + update_w_from_fifo = 1'b0; + hash_done_next = 1'b0; + + unique case (fifo_st_q) + FifoIdle: begin + if (hash_go) fifo_st_d = FifoLoadFromFifo; + else fifo_st_d = FifoIdle; + end + + FifoLoadFromFifo: begin + if (!shaf_rvalid) begin + // Wait until it is filled + fifo_st_d = FifoLoadFromFifo; + update_w_from_fifo = 1'b0; + end else if (w_index_q == 4'd 15) begin + fifo_st_d = FifoWait; + // To increment w_index and it rolls over to 0 + update_w_from_fifo = 1'b1; + end else begin + fifo_st_d = FifoLoadFromFifo; + update_w_from_fifo = 1'b1; + end + end + + FifoWait: begin + if (msg_feed_complete && one_chunk_done) begin + fifo_st_d = FifoIdle; + // hashing the full message is done + hash_done_next = 1'b1; + end else if (one_chunk_done) begin + fifo_st_d = FifoLoadFromFifo; + end else begin + fifo_st_d = FifoWait; + end + end + + default: begin + fifo_st_d = FifoIdle; + end + endcase + + if (!sha_en_i) begin + fifo_st_d = FifoIdle; + update_w_from_fifo = 1'b0; + end else if (hash_go) begin + fifo_st_d = FifoLoadFromFifo; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) digest_mode_flag_q <= None; + else digest_mode_flag_q <= digest_mode_flag_d; + end + + // SHA control (shared) + typedef enum logic [1:0] { + ShaIdle, + ShaCompress, + ShaUpdateDigest + } sha_st_t; + + sha_st_t sha_st_q, sha_st_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) sha_st_q <= ShaIdle; + else sha_st_q <= sha_st_d; + end + + logic sha_en_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) sha_en_q <= 1'b0; + else sha_en_q <= sha_en_i; + end + + assign clear_digest = hash_start_i | (~sha_en_i & sha_en_q); + + always_comb begin + update_digest = 1'b0; + calculate_next_w = 1'b0; + init_hash = 1'b0; + run_hash = 1'b0; + sha_st_d = sha_st_q; + + unique case (sha_st_q) + ShaIdle: begin + if (fifo_st_q == FifoWait) begin + init_hash = 1'b1; + sha_st_d = ShaCompress; + end else begin + sha_st_d = ShaIdle; + end + end + + ShaCompress: begin + run_hash = 1'b1; + if (((digest_mode_flag_q == SHA2_256 || ~MultimodeEn) && round_q < 48) || + (((digest_mode_flag_q == SHA2_384) || + (digest_mode_flag_q == SHA2_512)) && round_q < 64)) begin + calculate_next_w = 1'b1; + end else if (one_chunk_done) begin + sha_st_d = ShaUpdateDigest; + end else begin + sha_st_d = ShaCompress; + end + end + + ShaUpdateDigest: begin + update_digest = 1'b1; + if (fifo_st_q == FifoWait) begin + init_hash = 1'b1; + sha_st_d = ShaCompress; + end else begin + sha_st_d = ShaIdle; + end + end + + default: begin + sha_st_d = ShaIdle; + end + endcase + + if (!sha_en_i || hash_go) sha_st_d = ShaIdle; + end + + assign one_chunk_done = ((digest_mode_flag_q == SHA2_256 || ~MultimodeEn) + && (round_q == 7'd63)) ? 1'b1 : + (((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) + && (round_q == 7'd79)) ? 1'b1 : 1'b0; + + prim_sha2_pad #( + .MultimodeEn(MultimodeEn) + ) u_pad ( + .clk_i, + .rst_ni, + .fifo_rvalid_i, + .fifo_rdata_i, + .fifo_rready_o, + .shaf_rvalid_o (shaf_rvalid), // is set when the 512-bit chunk is ready in the padding buffer + .shaf_rdata_o (shaf_rdata), + .shaf_rready_i (shaf_rready), // indicates that w is ready for more words from padding buffer + .sha_en_i, + .hash_start_i, + .hash_continue_i, + .digest_mode_i, + .hash_process_i, + .hash_done_o, + .message_length_i, + .msg_feed_complete_o (msg_feed_complete) + ); + + assign hash_running_o = init_hash | run_hash | update_digest; + + // Idle + assign idle_o = (fifo_st_q == FifoIdle) && (sha_st_q == ShaIdle) && !hash_go; +endmodule : prim_sha2 diff --git a/synth/prim/rtl/prim_sha2_32.sv b/synth/prim/rtl/prim_sha2_32.sv new file mode 100755 index 0000000..f19cfaf --- /dev/null +++ b/synth/prim/rtl/prim_sha2_32.sv @@ -0,0 +1,276 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// 32-bit input wrapper for the SHA-2 engine + +module prim_sha2_32 import prim_sha2_pkg::*; +#( + parameter bit MultimodeEn = 0 // assert to enable multi-mode feature + ) ( + input clk_i, + input rst_ni, + + input wipe_secret_i, + input sha_word32_t wipe_v_i, + // Control signals and message words input to the message FIFO + input fifo_rvalid_i, // indicates that there are data words/word parts + // ready to write into the SHA-2 padding buffer + input sha_fifo32_t fifo_rdata_i, + output logic fifo_rready_o, // indicates that the wrapper word accumulation buffer is + // ready to receive words to feed into the SHA-2 engine + // Control signals + input sha_en_i, // if disabled, it clears internal content + input hash_start_i, + input hash_continue_i, + input digest_mode_e digest_mode_i, + input hash_process_i, + output logic hash_done_o, + input [127:0] message_length_i, // use extended message length 128 bits + input sha_word64_t [7:0] digest_i, + input logic [7:0] digest_we_i, + output sha_word64_t [7:0] digest_o, // use extended digest length + output logic hash_running_o, + output logic idle_o +); + // signal definitions shared for both 256-bit and multi-mode + sha_fifo64_t full_word; + logic sha_ready, hash_go; + + // Most operations and control signals are identical no matter if we are starting or re-starting + // to hash. + assign hash_go = hash_start_i | hash_continue_i; + + // tie off unused ports/port slices + if (!MultimodeEn) begin : gen_tie_unused + logic unused_signals; + assign unused_signals = ^{message_length_i[127:64], digest_mode_i, hash_go}; + end + + // logic and prim_sha2 instantiation for MultimodeEn = 1 + if (MultimodeEn) begin : gen_multimode_logic + // signal definitions for multi-mode + sha_fifo64_t word_buffer_d, word_buffer_q; + logic [1:0] word_part_count_d, word_part_count_q; + logic sha_process, process_flag_d, process_flag_q; + logic word_valid; + logic word_part_inc, word_part_reset; + digest_mode_e digest_mode_flag_d, digest_mode_flag_q; + + always_comb begin : multimode_combinational + word_part_inc = 1'b0; + word_part_reset = 1'b0; + full_word.mask = 8'hFF; // to keep the padding buffer ready to receive + full_word.data = 64'h0; + sha_process = 1'b0; + word_valid = 1'b0; + fifo_rready_o = 1'b0; + + // assign word_buffer + if (!sha_en_i || hash_go) word_buffer_d = 0; + else word_buffer_d = word_buffer_q; + + if (sha_en_i && fifo_rvalid_i) begin // valid incoming word part and SHA engine is enabled + if (word_part_count_q == 2'b00) begin + if (digest_mode_flag_q != SHA2_256) begin + // accumulate most significant 32 bits of word and mask bits + word_buffer_d.data[63:32] = fifo_rdata_i.data; + word_buffer_d.mask[7:4] = fifo_rdata_i.mask; + word_part_inc = 1'b1; + fifo_rready_o = 1'b1; + if (hash_process_i || process_flag_q) begin // ready to push out word (partial) + word_valid = 1'b1; + // add least significant padding + full_word.data = {fifo_rdata_i.data, 32'b0}; + full_word.mask = {fifo_rdata_i.mask, 4'h0}; + sha_process = 1'b1; + if (sha_ready == 1'b1) begin + // if word has been absorbed into hash engine + fifo_rready_o = 1'b1; // word pushed out to SHA engine, word buffer ready + word_part_inc = 1'b0; + end else begin + fifo_rready_o = 1'b0; + end + end + end else begin // SHA2_256 so pad and push out the word + word_valid = 1'b1; + // store the word with most significant padding + word_buffer_d.data = {32'b0, fifo_rdata_i.data}; + word_buffer_d.mask = {4'hF, fifo_rdata_i.mask}; // pad with all-1 byte mask + + // pad with all-zero data and all-one byte masking and push word out already for 256 + full_word.data = {32'b0, fifo_rdata_i.data}; + full_word.mask = {4'hF, fifo_rdata_i.mask}; + if (hash_process_i || process_flag_q) begin + sha_process = 1'b1; + end + if (sha_ready == 1'b1) begin + // if word has been absorbed into hash engine + fifo_rready_o = 1'b1; // word pushed out to SHA engine so word buffer ready + end else begin + fifo_rready_o = 1'b0; + end + end + end else if (word_part_count_q == 2'b01) begin + fifo_rready_o = 1'b1; // buffer still has room for another word + // accumulate least significant 32 bits and mask + word_buffer_d.data [31:0] = fifo_rdata_i.data; + word_buffer_d.mask [3:0] = fifo_rdata_i.mask; + + // now ready to pass full word through + word_valid = 1'b1; + full_word.data [63:32] = word_buffer_q.data[63:32]; + full_word.mask [7:4] = word_buffer_q.mask[7:4]; + full_word.data [31:0] = fifo_rdata_i.data; + full_word.mask [3:0] = fifo_rdata_i.mask; + + if (hash_process_i || process_flag_q) begin + sha_process = 1'b1; + end + if (sha_ready == 1'b1) begin + // word has been consumed + fifo_rready_o = 1'b1; // word pushed out to SHA engine so word buffer ready + word_part_reset = 1'b1; + word_part_inc = 1'b0; + end else begin + fifo_rready_o = 1'b1; + word_part_inc = 1'b1; + end + end else if (word_part_count_q == 2'b10) begin // word buffer is full and not loaded out yet + // fifo_rready_o is now deasserted: accumulated word is waiting to be pushed out + fifo_rready_o = 1'b0; + word_valid = 1'b1; // word buffer is ready to shift word out to SHA engine + full_word = word_buffer_q; + if (hash_process_i || process_flag_q) begin + sha_process = 1'b1; + end + if (sha_ready == 1'b1) begin // waiting on sha_ready to turn 1 + // do not assert fifo_rready_o yet + word_part_reset = 1'b1; + end + end + end else if (sha_en_i) begin // hash engine still enabled but no new valid input + // provide the last latched input so long as hash is enabled + full_word = word_buffer_q; + if (word_part_count_q == 2'b00 && (hash_process_i || process_flag_q)) begin + sha_process = 1'b1; // wait on hash_process_i + end else if (word_part_count_q == 2'b01 && (hash_process_i || process_flag_q)) begin + // 384/512: msg ended: apply 32-bit word packing and push last word + full_word.data [31:0] = 32'b0; + full_word.mask [3:0] = 4'h0; + word_valid = 1'b1; + sha_process = 1'b1; + if (sha_ready == 1'b1) begin // word has been consumed + word_part_reset = 1'b1; // which will also reset word_valid in the next cycle + end + end else if (word_part_count_q == 2'b01) begin // word feeding stalled but msg not ended + word_valid = 1'b0; + end else if (word_part_count_q == 2'b10 && (hash_process_i || process_flag_q)) begin + // 384/512: msg ended but last word still waiting to be fed in + word_valid = 1'b1; + sha_process = 1'b1; + if (sha_ready == 1'b1) begin // word has been consumed + word_part_reset = 1'b1; // which will also reset word_valid in the next cycle + end + end else if (word_part_count_q == 2'b10) begin // word feeding stalled + word_valid = 1'b0; + end + end + + // assign word_part_count_d + if ((word_part_reset || hash_go || !sha_en_i)) begin + word_part_count_d = '0; + end else if (word_part_inc) begin + word_part_count_d = word_part_count_q + 1'b1; + end else begin + word_part_count_d = word_part_count_q; + end + + // assign digest_mode_flag_d + if (hash_go) digest_mode_flag_d = digest_mode_i; // latch in configured mode + else if (hash_done_o) digest_mode_flag_d = None; // clear + else digest_mode_flag_d = digest_mode_flag_q; // keep + + // assign process_flag + if (!sha_en_i || hash_go) process_flag_d = 1'b0; + else if (hash_process_i) process_flag_d = 1'b1; + else process_flag_d = process_flag_q; + end : multimode_combinational + + prim_sha2 #( + .MultimodeEn(1) + ) u_prim_sha2_multimode ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wipe_secret_i (wipe_secret_i), + .wipe_v_i ({wipe_v_i, wipe_v_i}), + .fifo_rvalid_i (word_valid), + .fifo_rdata_i (full_word), + .fifo_rready_o (sha_ready), + .sha_en_i (sha_en_i), + .hash_start_i (hash_start_i), + .hash_continue_i (hash_continue_i), + .digest_mode_i (digest_mode_i), + .hash_process_i (sha_process), + .hash_done_o (hash_done_o), + .message_length_i (message_length_i), + .digest_i (digest_i), + .digest_we_i (digest_we_i), + .digest_o (digest_o), + .hash_running_o (hash_running_o), + .idle_o (idle_o) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) word_part_count_q <= '0; + else word_part_count_q <= word_part_count_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) word_buffer_q <= 0; + else word_buffer_q <= word_buffer_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) process_flag_q <= '0; + else process_flag_q <= process_flag_d; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) digest_mode_flag_q <= None; + else digest_mode_flag_q <= digest_mode_flag_d; + end + // logic and prim_sha2 instantiation for MultimodeEn = 0 + end else begin : gen_sha256_logic // MultimodeEn = 0 + always_comb begin : sha256_combinational + full_word.data = {32'b0, fifo_rdata_i.data}; + full_word.mask = {4'hF, fifo_rdata_i.mask}; + fifo_rready_o = sha_ready; + end : sha256_combinational + + prim_sha2 #( + .MultimodeEn(0) + ) u_prim_sha2_256 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wipe_secret_i (wipe_secret_i), + .wipe_v_i ({wipe_v_i, wipe_v_i}), + .fifo_rvalid_i (fifo_rvalid_i), // feed input directly + .fifo_rdata_i (full_word), + .fifo_rready_o (sha_ready), + .sha_en_i (sha_en_i), + .hash_start_i (hash_start_i), + .hash_continue_i (hash_continue_i), + .digest_mode_i (None), // unused input port tied to ground + .hash_process_i (hash_process_i), // feed input port directly to SHA-2 engine + .hash_done_o (hash_done_o), + .message_length_i ({{64'b0}, message_length_i[63:0]}), + .digest_i (digest_i), + .digest_we_i (digest_we_i), + .digest_o (digest_o), + .hash_running_o (hash_running_o), + .idle_o (idle_o) + ); + end + +endmodule : prim_sha2_32 diff --git a/synth/prim/rtl/prim_sha2_pad.sv b/synth/prim/rtl/prim_sha2_pad.sv new file mode 100755 index 0000000..60c7391 --- /dev/null +++ b/synth/prim/rtl/prim_sha2_pad.sv @@ -0,0 +1,356 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// SHA-256/Multi-Mode SHA-2 Padding logic + +`include "prim_assert.sv" + +module prim_sha2_pad import prim_sha2_pkg::*; +#( + parameter bit MultimodeEn = 1 + ) ( + input clk_i, + input rst_ni, + + // to actual FIFO + input fifo_rvalid_i, + input sha_fifo64_t fifo_rdata_i, + output logic fifo_rready_o, + // from SHA2 compression engine + output logic shaf_rvalid_o, + output sha_word64_t shaf_rdata_o, + input shaf_rready_i, + input sha_en_i, + input hash_start_i, + input hash_continue_i, + input digest_mode_e digest_mode_i, + input hash_process_i, + input hash_done_o, + input [127:0] message_length_i, // # of bytes in bits (8 bits granularity) + output logic msg_feed_complete_o // indicates all message is feeded +); + + logic [127:0] tx_count_d, tx_count; // fin received data count. + logic inc_txcount; + logic fifo_partial; + logic txcnt_eq_1a0; + logic hash_go; + + logic hash_process_flag_d, hash_process_flag_q; + digest_mode_e digest_mode_flag_d, digest_mode_flag_q; + + // Most operations and control signals are identical no matter if we are starting or continuing + // to hash. + assign hash_go = hash_start_i | hash_continue_i; + + // tie off unused inport ports and signals + if (!MultimodeEn) begin : gen_tie_unused + logic unused_signals; + assign unused_signals = ^{message_length_i[127:64]}; + end + + assign fifo_partial = MultimodeEn ? ~&fifo_rdata_i.mask : + ~&fifo_rdata_i.mask[3:0]; + + // tx_count[8:0] == 'h1c0 --> should send LenHi + assign txcnt_eq_1a0 = (digest_mode_flag_q == SHA2_256 || ~MultimodeEn) ? + (tx_count[8:0] == 9'h1a0) : + ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) ? + (tx_count[9:0] == 10'h340) : + '0; + + assign hash_process_flag_d = (~sha_en_i || hash_go || hash_done_o) ? 1'b0 : + hash_process_i ? 1'b1 : + hash_process_flag_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) hash_process_flag_q <= 1'b0; + else hash_process_flag_q <= hash_process_flag_d; + end + + // data path: fout_wdata + typedef enum logic [2:0] { + FifoIn, // fin_wdata, fin_wstrb + Pad80, // {8'h80, 8'h00} , strb (calc based on len[4:3]) + Pad00, // 32'h0, full strb + LenHi, // len[63:32], full strb + LenLo // len[31:0], full strb + } sel_data_e; + sel_data_e sel_data; + + always_comb begin + unique case (sel_data) + FifoIn: begin + shaf_rdata_o = fifo_rdata_i.data; + end + + Pad80: begin + // {a[7:0], b[7:0], c[7:0], d[7:0]} + // msglen[4:3] == 00 |-> {'h80, 'h00, 'h00, 'h00} + // msglen[4:3] == 01 |-> {msg, 'h80, 'h00, 'h00} + // msglen[4:3] == 10 |-> {msg[15:0], 'h80, 'h00} + // msglen[4:3] == 11 |-> {msg[23:0], 'h80} + if ((digest_mode_flag_q == SHA2_256) || ~MultimodeEn) begin + unique case (message_length_i[4:3]) + 2'b 00: shaf_rdata_o = 64'h 0000_0000_8000_0000; + 2'b 01: shaf_rdata_o = {32'h 0000_0000, fifo_rdata_i.data[31:24], 24'h 8000_00}; + 2'b 10: shaf_rdata_o = {32'h 0000_0000, fifo_rdata_i.data[31:16], 16'h 8000}; + 2'b 11: shaf_rdata_o = {32'h 0000_0000, fifo_rdata_i.data[31: 8], 8'h 80}; + default: shaf_rdata_o = 64'h0; + endcase + end else if ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) begin + unique case (message_length_i[5:3]) + 3'b 000: shaf_rdata_o = 64'h 8000_0000_0000_0000; + 3'b 001: shaf_rdata_o = {fifo_rdata_i.data[63:56], 56'h 8000_0000_0000_00}; + 3'b 010: shaf_rdata_o = {fifo_rdata_i.data[63:48], 48'h 8000_0000_0000}; + 3'b 011: shaf_rdata_o = {fifo_rdata_i.data[63:40], 40'h 8000_0000_00}; + 3'b 100: shaf_rdata_o = {fifo_rdata_i.data[63:32], 32'h 8000_0000}; + 3'b 101: shaf_rdata_o = {fifo_rdata_i.data[63:24], 24'h 8000_00}; + 3'b 110: shaf_rdata_o = {fifo_rdata_i.data[63:16], 16'h 8000}; + 3'b 111: shaf_rdata_o = {fifo_rdata_i.data[63:8], 8'h 80}; + default: shaf_rdata_o = 64'h0; + endcase + end else + shaf_rdata_o = '0; + end + + Pad00: begin + shaf_rdata_o = '0; + end + + LenHi: begin + shaf_rdata_o = ((digest_mode_flag_q == SHA2_256) || ~MultimodeEn) ? + {32'b0, message_length_i[63:32]}: + ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) ? + message_length_i[127:64] : '0; + end + + LenLo: begin + shaf_rdata_o = ((digest_mode_flag_q == SHA2_256) || ~MultimodeEn) ? + {32'b0, message_length_i[31:0]}: + ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) ? + message_length_i[63:0]: '0; + end + + default: begin + shaf_rdata_o = '0; + end + endcase + + if (!MultimodeEn) shaf_rdata_o [63:32] = 32'b0; // assign most sig 32 bits to constant 0 + end + + // Padded length + // $ceil(message_length_i + 8 + 64, 512) -> message_length_i [8:0] + 440 and ignore carry + //assign length_added = (message_length_i[8:0] + 9'h1b8) ; + + // fifo control + // add 8'h 80 , N 8'h00, 64'h message_length_i + + // Steps + // 1. `hash_start_i` from CPU (or DMA?) + // 2. calculate `padded_length` from `message_length_i` + // 3. Check if tx_count == message_length_i, then go to 5 + // 4. Receiving FIFO input (hand over to fifo output) + // 5. Padding bit 1 (8'h80) followed by 8'h00 if needed + // 6. Padding with length (high -> low) + + // State Machine + typedef enum logic [2:0] { + StIdle, // fin_full to prevent unwanted FIFO write + StFifoReceive, // Check tx_count == message_length_i + StPad80, // 8'h 80 + 8'h 00 X N + StPad00, + StLenHi, + StLenLo + } pad_st_e; + + pad_st_e st_q, st_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) st_q <= StIdle; + else st_q <= st_d; + end + + // Next state + always_comb begin + shaf_rvalid_o = 1'b0; + inc_txcount = 1'b0; + sel_data = FifoIn; + fifo_rready_o = 1'b0; + st_d = StIdle; + + unique case (st_q) + StIdle: begin + sel_data = FifoIn; + shaf_rvalid_o = 1'b0; + if (sha_en_i && hash_go) begin + inc_txcount = 1'b0; + st_d = StFifoReceive; + end else begin + st_d = StIdle; + end + end + + StFifoReceive: begin + sel_data = FifoIn; + if (fifo_partial && fifo_rvalid_i) begin + // End of the message (last bit is not word-aligned) , assume hash_process_flag is set + shaf_rvalid_o = 1'b0; // Update entry at StPad80 + inc_txcount = 1'b0; + fifo_rready_o = 1'b0; + st_d = StPad80; + end else if (!hash_process_flag_q) begin + fifo_rready_o = shaf_rready_i; + shaf_rvalid_o = fifo_rvalid_i; + inc_txcount = shaf_rready_i; + st_d = StFifoReceive; + end else if (((tx_count == message_length_i) & MultimodeEn) || + ((tx_count [63:0] == message_length_i [63:0]) & !MultimodeEn)) begin + // already received all msg and was waiting process flag + shaf_rvalid_o = 1'b0; + inc_txcount = 1'b0; + fifo_rready_o = 1'b0; + st_d = StPad80; + end else begin + shaf_rvalid_o = fifo_rvalid_i; + fifo_rready_o = shaf_rready_i; // 0 always + inc_txcount = shaf_rready_i; // 0 always + st_d = StFifoReceive; + end + end + + StPad80: begin + sel_data = Pad80; + shaf_rvalid_o = 1'b1; + fifo_rready_o = (digest_mode_flag_q == SHA2_256 || ~MultimodeEn) ? + shaf_rready_i && |message_length_i[4:3] : + ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) ? + shaf_rready_i && |message_length_i[5:3] : '0; // Only when partial + + // exactly 192 bits left, do not need to pad00's + if (shaf_rready_i && txcnt_eq_1a0) begin + st_d = StLenHi; + inc_txcount = 1'b1; + // it does not matter if value is < or > than 416 bits. If it's the former, 00 pad until + // length field. If >, then the next chunk will contain the length field with appropriate + // 0 padding. + end else if (shaf_rready_i && !txcnt_eq_1a0) begin + st_d = StPad00; + inc_txcount = 1'b1; + end else begin + st_d = StPad80; + inc_txcount = 1'b0; + end + + // # Below part is temporal code to speed up the SHA by 16 clocks per chunk + // # (80 clk --> 64 clk) + // # leaving this as a reference but needs to verify it. + //if (shaf_rready_i && !txcnt_eq_1a0) begin + // st_d = StPad00; + // + // inc_txcount = 1'b1; + // shaf_rvalid_o = (msg_word_aligned) ? 1'b1 : fifo_rvalid; + // fifo_rready = (msg_word_aligned) ? 1'b0 : 1'b1; + //end else if (!shaf_rready_i && !txcnt_eq_1a0) begin + // st_d = StPad80; + // + // inc_txcount = 1'b0; + // shaf_rvalid_o = (msg_word_aligned) ? 1'b1 : fifo_rvalid; + // + //end else if (shaf_rready_i && txcnt_eq_1a0) begin + // st_d = StLenHi; + // inc_txcount = 1'b1; + //end else begin + // // !shaf_rready_i && txcnt_eq_1a0 , just wait until fifo_rready asserted + // st_d = StPad80; + // inc_txcount = 1'b0; + //end + end + + StPad00: begin + sel_data = Pad00; + shaf_rvalid_o = 1'b1; + + if (shaf_rready_i) begin + inc_txcount = 1'b1; + if (txcnt_eq_1a0) st_d = StLenHi; + else st_d = StPad00; + end else begin + st_d = StPad00; + end + end + + StLenHi: begin + sel_data = LenHi; + shaf_rvalid_o = 1'b1; + + if (shaf_rready_i) begin + st_d = StLenLo; + inc_txcount = 1'b1; + end else begin + st_d = StLenHi; + inc_txcount = 1'b0; + end + end + + StLenLo: begin + sel_data = LenLo; + shaf_rvalid_o = 1'b1; + + if (shaf_rready_i) begin + st_d = StIdle; + inc_txcount = 1'b1; + end else begin + st_d = StLenLo; + inc_txcount = 1'b0; + end + end + + default: begin + st_d = StIdle; + end + endcase + + if (!sha_en_i) st_d = StIdle; + else if (hash_go) st_d = StFifoReceive; + end + + // tx_count + always_comb begin + tx_count_d = tx_count; + + if (hash_start_i) begin + // When starting a fresh hash, initialize the data counter to zero. + tx_count_d = '0; + end else if (hash_continue_i) begin + // When continuing to hash, set the data counter to the current message length. + tx_count_d = message_length_i; + end else if (inc_txcount) begin + if ((digest_mode_flag_q == SHA2_256) || !MultimodeEn) begin + tx_count_d[127:5] = tx_count[127:5] + 1'b1; + end else if ((digest_mode_flag_q == SHA2_384) || (digest_mode_flag_q == SHA2_512)) begin + tx_count_d[127:6] = tx_count[127:6] + 1'b1; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) tx_count <= '0; + else tx_count <= tx_count_d; + end + + assign digest_mode_flag_d = hash_start_i ? digest_mode_i : // latch in configured mode + hash_done_o ? None : // clear + digest_mode_flag_q; // keep + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) digest_mode_flag_q <= None; + else digest_mode_flag_q <= digest_mode_flag_d; + end + + // State machine is in Idle only when it meets tx_count == message length + assign msg_feed_complete_o = hash_process_flag_q && (st_q == StIdle); + +endmodule diff --git a/synth/prim/rtl/prim_sha2_pkg.sv b/synth/prim/rtl/prim_sha2_pkg.sv new file mode 100755 index 0000000..0f0afd2 --- /dev/null +++ b/synth/prim/rtl/prim_sha2_pkg.sv @@ -0,0 +1,238 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +package prim_sha2_pkg; + + localparam int NumRound256 = 64; // SHA-224, SHA-256 + localparam int NumRound512 = 80; // SHA-512, SHA-384 + + typedef logic [31:0] sha_word32_t; + typedef logic [63:0] sha_word64_t; + + localparam int WordByte32 = $bits(sha_word32_t)/8; + localparam int WordByte64 = $bits(sha_word64_t)/8; + + typedef struct packed { + sha_word32_t data; + logic [WordByte32-1:0] mask; // 4 mask bits: to mask off bytes if this is not word-aligned + // set to all-1 for word-aligned input + } sha_fifo32_t; + + typedef struct packed { + sha_word64_t data; + logic [WordByte64-1:0] mask; // 8 mask bits: to mask off bytes if this is not word-aligned + // set to all-1 for word-aligned input + } sha_fifo64_t; + + typedef enum logic [1:0] { + None, + SHA2_256, + SHA2_384, + SHA2_512 + } digest_mode_e; + + localparam sha_word32_t InitHash_256 [8]= '{ + 32'h 6a09_e667, 32'h bb67_ae85, 32'h 3c6e_f372, 32'h a54f_f53a, + 32'h 510e_527f, 32'h 9b05_688c, 32'h 1f83_d9ab, 32'h 5be0_cd19 + }; + + localparam sha_word64_t InitHash_384 [8]= '{ + 64'h cbbb_9d5d_c105_9ed8, 64'h 629a_292a_367c_d507, 64'h 9159_015a_3070_dd17, + 64'h 152f_ecd8_f70e_5939, 64'h 6733_2667_ffc0_0b31, 64'h 8eb4_4a87_6858_1511, + 64'h db0c_2e0d_64f9_8fa7, 64'h 47b5_481d_befa_4fa4 + }; + + localparam sha_word64_t InitHash_512 [8]= '{ + 64'h 6a09_e667_f3bc_c908, 64'h bb67_ae85_84ca_a73b, 64'h 3c6e_f372_fe94_f82b, + 64'h a54f_f53a_5f1d_36f1, 64'h 510e_527f_ade6_82d1, 64'h 9b05_688c_2b3e_6c1f, + 64'h 1f83_d9ab_fb41_bd6b, 64'h 5be0_cd19_137e_2179 + }; + + // SHA-256 constants + localparam sha_word32_t CubicRootPrime256 [NumRound256] = '{ + 32'h 428a_2f98, 32'h 7137_4491, 32'h b5c0_fbcf, 32'h e9b5_dba5, + 32'h 3956_c25b, 32'h 59f1_11f1, 32'h 923f_82a4, 32'h ab1c_5ed5, + 32'h d807_aa98, 32'h 1283_5b01, 32'h 2431_85be, 32'h 550c_7dc3, + 32'h 72be_5d74, 32'h 80de_b1fe, 32'h 9bdc_06a7, 32'h c19b_f174, + 32'h e49b_69c1, 32'h efbe_4786, 32'h 0fc1_9dc6, 32'h 240c_a1cc, + 32'h 2de9_2c6f, 32'h 4a74_84aa, 32'h 5cb0_a9dc, 32'h 76f9_88da, + 32'h 983e_5152, 32'h a831_c66d, 32'h b003_27c8, 32'h bf59_7fc7, + 32'h c6e0_0bf3, 32'h d5a7_9147, 32'h 06ca_6351, 32'h 1429_2967, + 32'h 27b7_0a85, 32'h 2e1b_2138, 32'h 4d2c_6dfc, 32'h 5338_0d13, + 32'h 650a_7354, 32'h 766a_0abb, 32'h 81c2_c92e, 32'h 9272_2c85, + 32'h a2bf_e8a1, 32'h a81a_664b, 32'h c24b_8b70, 32'h c76c_51a3, + 32'h d192_e819, 32'h d699_0624, 32'h f40e_3585, 32'h 106a_a070, + 32'h 19a4_c116, 32'h 1e37_6c08, 32'h 2748_774c, 32'h 34b0_bcb5, + 32'h 391c_0cb3, 32'h 4ed8_aa4a, 32'h 5b9c_ca4f, 32'h 682e_6ff3, + 32'h 748f_82ee, 32'h 78a5_636f, 32'h 84c8_7814, 32'h 8cc7_0208, + 32'h 90be_fffa, 32'h a450_6ceb, 32'h bef9_a3f7, 32'h c671_78f2 + }; + + // SHA-512/SHA-384 constants + localparam sha_word64_t CubicRootPrime512 [NumRound512] = '{ + 64'h 428a_2f98_d728_ae22, 64'h 7137_4491_23ef_65cd, 64'h b5c0_fbcf_ec4d_3b2f, + 64'h e9b5_dba5_8189_dbbc, 64'h 3956_c25b_f348_b538, 64'h 59f1_11f1_b605_d019, + 64'h 923f_82a4_af19_4f9b, 64'h ab1c_5ed5_da6d_8118, 64'h d807_aa98_a303_0242, + 64'h 1283_5b01_4570_6fbe, 64'h 2431_85be_4ee4_b28c, 64'h 550c_7dc3_d5ff_b4e2, + 64'h 72be_5d74_f27b_896f, 64'h 80de_b1fe_3b16_96b1, 64'h 9bdc_06a7_25c7_1235, + 64'h c19b_f174_cf69_2694, 64'h e49b_69c1_9ef1_4ad2, 64'h efbe_4786_384f_25e3, + 64'h 0fc1_9dc6_8b8c_d5b5, 64'h 240c_a1cc_77ac_9c65, 64'h 2de9_2c6f_592b_0275, + 64'h 4a74_84aa_6ea6_e483, 64'h 5cb0_a9dc_bd41_fbd4, 64'h 76f9_88da_8311_53b5, + 64'h 983e_5152_ee66_dfab, 64'h a831_c66d_2db4_3210, 64'h b003_27c8_98fb_213f, + 64'h bf59_7fc7_beef_0ee4, 64'h c6e0_0bf3_3da8_8fc2, 64'h d5a7_9147_930a_a725, + 64'h 06ca_6351_e003_826f, 64'h 1429_2967_0a0e_6e70, 64'h 27b7_0a85_46d2_2ffc, + 64'h 2e1b_2138_5c26_c926, 64'h 4d2c_6dfc_5ac4_2aed, 64'h 5338_0d13_9d95_b3df, + 64'h 650a_7354_8baf_63de, 64'h 766a_0abb_3c77_b2a8, 64'h 81c2_c92e_47ed_aee6, + 64'h 9272_2c85_1482_353b, 64'h a2bf_e8a1_4cf1_0364, 64'h a81a_664b_bc42_3001, + 64'h c24b_8b70_d0f8_9791, 64'h c76c_51a3_0654_be30, 64'h d192_e819_d6ef_5218, + 64'h d699_0624_5565_a910, 64'h f40e_3585_5771_202a, 64'h 106a_a070_32bb_d1b8, + 64'h 19a4_c116_b8d2_d0c8, 64'h 1e37_6c08_5141_ab53, 64'h 2748_774c_df8e_eb99, + 64'h 34b0_bcb5_e19b_48a8, 64'h 391c_0cb3_c5c9_5a63, 64'h 4ed8_aa4a_e341_8acb, + 64'h 5b9c_ca4f_7763_e373, 64'h 682e_6ff3_d6b2_b8a3, 64'h 748f_82ee_5def_b2fc, + 64'h 78a5_636f_4317_2f60, 64'h 84c8_7814_a1f0_ab72, 64'h 8cc7_0208_1a64_39ec, + 64'h 90be_fffa_2363_1e28, 64'h a450_6ceb_de82_bde9, 64'h bef9_a3f7_b2c6_7915, + 64'h c671_78f2_e372_532b, 64'h ca27_3ece_ea26_619c, 64'h d186_b8c7_21c0_c207, + 64'h eada_7dd6_cde0_eb1e, 64'h f57d_4f7f_ee6e_d178, 64'h 06f0_67aa_7217_6fba, + 64'h 0a63_7dc5_a2c8_98a6, 64'h 113f_9804_bef9_0dae, 64'h 1b71_0b35_131c_471b, + 64'h 28db_77f5_2304_7d84, 64'h 32ca_ab7b_40c7_2493, 64'h 3c9e_be0a_15c9_bebc, + 64'h 431d_67c4_9c10_0d4c, 64'h 4cc5_d4be_cb3e_42b6, 64'h 597f_299c_fc65_7e2a, + 64'h 5fcb_6fab_3ad6_faec, 64'h 6c44_198c_4a47_5817 + }; + + function automatic sha_word32_t conv_endian32(input sha_word32_t v, input logic swap); + sha_word32_t conv_data = {<<8{v}}; + conv_endian32 = (swap) ? conv_data : v; + endfunction : conv_endian32 + + function automatic sha_word64_t conv_endian64(input sha_word64_t v, input logic swap); + sha_word64_t conv_data = {<<8{v}}; + conv_endian64 = (swap) ? conv_data : v; + endfunction : conv_endian64 + + function automatic sha_word32_t rotr32(input sha_word32_t v, input integer amt); + rotr32 = (v >> amt) | (v << (32-amt)); + endfunction : rotr32 + + function automatic sha_word64_t rotr64(input sha_word64_t v, input integer amt); + rotr64 = (v >> amt) | (v << (64-amt)); + endfunction : rotr64 + + function automatic sha_word32_t shiftr32(input sha_word32_t v, input integer amt); + shiftr32 = (v >> amt); + endfunction : shiftr32 + + function automatic sha_word64_t shiftr64(input sha_word64_t v, input integer amt); + shiftr64 = (v >> amt); + endfunction : shiftr64 + + // compression function for SHA-256 in multi-mode configuration + function automatic sha_word64_t [7:0] compress_multi_256(input sha_word32_t w, + input sha_word32_t k, + input sha_word64_t [7:0] h_i); + // as input: takes 64-bit word hash vector, 32-bit slice of w[0] and 32-bit constant + automatic sha_word32_t sigma_0, sigma_1, ch, maj, temp1, temp2; + + sigma_1 = rotr32(h_i[4][31:0], 6) ^ rotr32(h_i[4][31:0], 11) ^ rotr32(h_i[4][31:0], 25); + ch = (h_i[4][31:0] & h_i[5][31:0]) ^ (~h_i[4][31:0] & h_i[6][31:0]); + temp1 = (h_i[7][31:0] + sigma_1 + ch + k + w); + sigma_0 = rotr32(h_i[0][31:0], 2) ^ rotr32(h_i[0][31:0], 13) ^ rotr32(h_i[0][31:0], 22); + maj = (h_i[0][31:0] & h_i[1][31:0]) ^ (h_i[0][31:0] & h_i[2][31:0]) ^ + (h_i[1][31:0] & h_i[2][31:0]); + temp2 = (sigma_0 + maj); + + // 32-bit zero padding to complete 64-bit words of hash vector for output + compress_multi_256[7] = {32'b0, h_i[6][31:0]}; // h = g + compress_multi_256[6] = {32'b0, h_i[5][31:0]}; // g = f + compress_multi_256[5] = {32'b0, h_i[4][31:0]}; // f = e + compress_multi_256[4] = {32'b0, h_i[3][31:0] + temp1}; // e = (d + temp1) + compress_multi_256[3] = {32'b0, h_i[2][31:0]}; // d = c + compress_multi_256[2] = {32'b0, h_i[1][31:0]}; // c = b + compress_multi_256[1] = {32'b0, h_i[0][31:0]}; // b = a + compress_multi_256[0] = {32'b0, (temp1 + temp2)}; // a = (temp1 + temp2) + endfunction : compress_multi_256 + + // compression function for SHA-256 in 256-only configuration + function automatic sha_word32_t [7:0] compress_256(input sha_word32_t w, + input sha_word32_t k, + input sha_word32_t [7:0] h_i); + automatic sha_word32_t sigma_0, sigma_1, ch, maj, temp1, temp2; + + sigma_1 = rotr32(h_i[4], 6) ^ rotr32(h_i[4], 11) ^ rotr32(h_i[4], 25); + ch = (h_i[4] & h_i[5]) ^ (~h_i[4] & h_i[6]); + temp1 = (h_i[7] + sigma_1 + ch + k + w); + sigma_0 = rotr32(h_i[0], 2) ^ rotr32(h_i[0], 13) ^ rotr32(h_i[0], 22); + maj = (h_i[0] & h_i[1]) ^ (h_i[0] & h_i[2]) ^ + (h_i[1] & h_i[2]); + temp2 = (sigma_0 + maj); + + compress_256[7] = h_i[6]; // h = g + compress_256[6] = h_i[5]; // g = f + compress_256[5] = h_i[4]; // f = e + compress_256[4] = h_i[3] + temp1; // e = (d + temp1) + compress_256[3] = h_i[2]; // d = c + compress_256[2] = h_i[1]; // c = b + compress_256[1] = h_i[0]; // b = a + compress_256[0] = temp1 + temp2; // a = (temp1 + temp2) + endfunction : compress_256 + + // compression function for SHA-512/384 + function automatic sha_word64_t [7:0] compress_512(input sha_word64_t w, + input sha_word64_t k, + input sha_word64_t [7:0] h_i); + automatic sha_word64_t sigma_0, sigma_1, ch, maj, temp1, temp2; + + sigma_1 = rotr64(h_i[4], 14) ^ rotr64(h_i[4], 18) ^ rotr64(h_i[4], 41); + ch = (h_i[4] & h_i[5]) ^ (~h_i[4] & h_i[6]); + temp1 = (h_i[7] + sigma_1 + ch + k + w); + sigma_0 = rotr64(h_i[0], 28) ^ rotr64(h_i[0], 34) ^ rotr64(h_i[0], 39); + maj = (h_i[0] & h_i[1]) ^ (h_i[0] & h_i[2]) ^ (h_i[1] & h_i[2]); + temp2 = (sigma_0 + maj); + + compress_512[7] = h_i[6]; // h = g + compress_512[6] = h_i[5]; // g = f + compress_512[5] = h_i[4]; // f = e + compress_512[4] = h_i[3] + temp1; // e = (d + temp1) + compress_512[3] = h_i[2]; // d = c + compress_512[2] = h_i[1]; // c = b + compress_512[1] = h_i[0]; // b = a + compress_512[0] = (temp1 + temp2); // a = (temp1 + temp2) + endfunction : compress_512 + + function automatic sha_word32_t calc_w_256(input sha_word32_t w_0, + input sha_word32_t w_1, + input sha_word32_t w_9, + input sha_word32_t w_14); + automatic sha_word32_t sum0, sum1; + sum0 = rotr32(w_1, 7) ^ rotr32(w_1, 18) ^ shiftr32(w_1, 3); + sum1 = rotr32(w_14, 17) ^ rotr32(w_14, 19) ^ shiftr32(w_14, 10); + calc_w_256 = w_0 + sum0 + w_9 + sum1; + endfunction : calc_w_256 + + function automatic sha_word64_t calc_w_512(input sha_word64_t w_0, + input sha_word64_t w_1, + input sha_word64_t w_9, + input sha_word64_t w_14); + automatic sha_word64_t sum0, sum1; + sum0 = rotr64(w_1, 1) ^ rotr64(w_1, 8) ^ shiftr64(w_1, 7); + sum1 = rotr64(w_14, 19) ^ rotr64(w_14, 61) ^ shiftr64(w_14, 6); + calc_w_512 = w_0 + sum0 + w_9 + sum1; + endfunction : calc_w_512 + + typedef enum logic [31:0] { + NoError = 32'h 0000_0000, + // SwPushMsgWhenShaDisabled is not used in this version. The error code is + // guarded by the HW. HW drops the message write request if `sha_en` is + // off. eunchan@ left the error code to not corrupt the code sequence. + // Need to rename to DeprecatedSwPush... + // + // Issue #3022 + SwPushMsgWhenShaDisabled = 32'h 0000_0001, + SwHashStartWhenShaDisabled = 32'h 0000_0002, + SwUpdateSecretKeyInProcess = 32'h 0000_0003, + SwHashStartWhenActive = 32'h 0000_0004, + SwPushMsgWhenDisallowed = 32'h 0000_0005 + } err_code_e; + +endpackage : prim_sha2_pkg diff --git a/synth/prim/rtl/prim_slicer.sv b/synth/prim/rtl/prim_slicer.sv new file mode 100755 index 0000000..3c61669 --- /dev/null +++ b/synth/prim/rtl/prim_slicer.sv @@ -0,0 +1,32 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Slicer chops the incoming bitstring into OutW granularity. +// It supports fractional InW/OutW which fills 0 at the end of message. + +`include "prim_assert.sv" + +module prim_slicer #( + parameter int InW = 64, + parameter int OutW = 8, + + parameter int IndexW = 4 +) ( + input [IndexW-1:0] sel_i, + input [InW-1:0] data_i, + output logic [OutW-1:0] data_o +); + + localparam int UnrollW = OutW*(2**IndexW); + + logic [UnrollW-1:0] unrolled_data; + + assign unrolled_data = UnrollW'(data_i); + + assign data_o = unrolled_data[sel_i*OutW+:OutW]; + + `ASSERT_INIT(ValidWidth_A, InW <= OutW*(2**IndexW)) + +endmodule + diff --git a/synth/prim/rtl/prim_sparse_fsm_flop.sv b/synth/prim/rtl/prim_sparse_fsm_flop.sv new file mode 100755 index 0000000..ca2599e --- /dev/null +++ b/synth/prim/rtl/prim_sparse_fsm_flop.sv @@ -0,0 +1,67 @@ +// 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_sparse_fsm_flop #( + parameter int Width = 1, + parameter type StateEnumT = logic [Width-1:0], + parameter logic [Width-1:0] ResetValue = '0, + // This should only be disabled in special circumstances, for example + // in non-comportable IPs where an error does not trigger an alert. + parameter bit EnableAlertTriggerSVA = 1 +`ifdef SIMULATION + , + // In case this parameter is set to a non-empty string, the + // prim_sparse_fsm_flop_if will also force the signal with this name + // in the parent module that instantiates prim_sparse_fsm_flop. + parameter string CustomForceName = "" +`endif +) ( + input clk_i, + input rst_ni, + input StateEnumT state_i, + output StateEnumT state_o +); + + logic unused_err_o; + + logic [Width-1:0] state_raw; + prim_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_state_flop ( + .clk_i, + .rst_ni, + .d_i(state_i), + .q_o(state_raw) + ); + assign state_o = StateEnumT'(state_raw); + + `ifdef INC_ASSERT + assign unused_err_o = is_undefined_state(state_o); + + function automatic logic is_undefined_state(StateEnumT sig); + // This is written with a vector in order to make it amenable to x-prop analysis. + logic is_defined = 1'b0; + for (int i = 0, StateEnumT t = t.first(); i < t.num(); i += 1, t = t.next()) begin + is_defined |= (sig === t); + end + return ~is_defined; + endfunction + + `else + assign unused_err_o = 1'b0; + `endif + + // If ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT is declared, the unused_assert_connected signal will + // be set to 1 and the below check will pass. + // If the assertion is not declared however, the statement below will fail. + `ifdef INC_ASSERT + logic unused_assert_connected; + + `ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA) + `endif + +endmodule diff --git a/synth/prim/rtl/prim_sram_arbiter.sv b/synth/prim/rtl/prim_sram_arbiter.sv new file mode 100755 index 0000000..e168dac --- /dev/null +++ b/synth/prim/rtl/prim_sram_arbiter.sv @@ -0,0 +1,154 @@ +// 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 unsigned N = 4, + parameter int unsigned SramDw = 32, + parameter int unsigned SramAw = 12, + parameter ArbiterImpl = "PPC", + parameter bit EnMask = 1'b 0 // Disable wmask if 0 +) ( + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [SramAw-1:0] req_addr_i [N], + input [ N-1:0] req_write_i, + input [SramDw-1:0] req_wdata_i[N], + input [SramDw-1:0] req_wmask_i[N], + output logic [ N-1:0] gnt_o, + + output logic [ N-1:0] rsp_rvalid_o, // Pulse + output logic [SramDw-1:0] rsp_rdata_o[N], + output logic [ 1:0] rsp_error_o[N], + + // SRAM Interface + output logic sram_req_o, + output logic [SramAw-1:0] sram_addr_o, + output logic sram_write_o, + output logic [SramDw-1:0] sram_wdata_o, + output logic [SramDw-1:0] sram_wmask_o, + input sram_rvalid_i, + input [SramDw-1:0] sram_rdata_i, + input [1:0] sram_rerror_i +); + + typedef struct packed { + logic write; + logic [SramAw-1:0] addr; + logic [SramDw-1:0] wdata; + logic [SramDw-1:0] wmask; + } req_t; + + req_t req_packed [N]; + + for (genvar i = 0 ; i < N ; i++) begin : gen_reqs + assign req_packed[i] = { + req_write_i[i], + req_addr_i [i], + req_wdata_i[i], + (EnMask) ? req_wmask_i[i] : {SramDw{1'b1}} + }; + end + + localparam int ARB_DW = $bits(req_t); + + req_t sram_packed; + assign sram_write_o = sram_packed.write; + assign sram_addr_o = sram_packed.addr; + assign sram_wdata_o = sram_packed.wdata; + assign sram_wmask_o = (EnMask) ? sram_packed.wmask : {SramDw{1'b1}}; + + if (EnMask == 1'b 0) begin : g_unused + logic unused_wmask; + + always_comb begin + unused_wmask = 1'b 1; + for (int unsigned i = 0 ; i < N ; i++) begin + unused_wmask ^= ^req_wmask_i[i]; + end + unused_wmask ^= ^sram_packed.wmask; + end + end + + + if (ArbiterImpl == "PPC") begin : gen_arb_ppc + prim_arbiter_ppc #( + .N (N), + .DW(ARB_DW) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_chk_i ( 1'b1 ), + .req_i, + .data_i ( req_packed ), + .gnt_o, + .idx_o ( ), + .valid_o ( sram_req_o ), + .data_o ( sram_packed ), + .ready_i ( 1'b1 ) + ); + end else if (ArbiterImpl == "BINTREE") begin : gen_tree_arb + prim_arbiter_tree #( + .N (N), + .DW(ARB_DW) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_chk_i ( 1'b1 ), + .req_i, + .data_i ( req_packed ), + .gnt_o, + .idx_o ( ), + .valid_o ( sram_req_o ), + .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_i + logic sram_ack; // Ack for rvalid. |sram_rvalid_i + + assign sram_ack = sram_rvalid_i & (|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_i (sram_req_o & ~sram_write_o), // Push only for read + .wready_o (), // TODO: Generate Error + .wdata_i (gnt_o), + .rvalid_o (), // TODO; Generate error if sram_rvalid_i but rvalid==0 + .rready_i (sram_ack), + .rdata_o (steer), + .full_o (), + .depth_o (), // Not used + .err_o () + ); + + assign rsp_rvalid_o = steer & {N{sram_rvalid_i}}; + + for (genvar i = 0 ; i < N ; i++) begin : gen_rsp + assign rsp_rdata_o[i] = sram_rdata_i; + assign rsp_error_o[i] = sram_rerror_i; // No SECDED yet + end + +endmodule diff --git a/synth/prim/rtl/prim_subreg.sv b/synth/prim/rtl/prim_subreg.sv new file mode 100755 index 0000000..0210928 --- /dev/null +++ b/synth/prim/rtl/prim_subreg.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 +// +// Register slice conforming to Comportibility guide. + +module prim_subreg + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, + parameter logic [DW-1:0] RESVAL = '0 , // reset value + parameter bit Mubi = 1'b0 +) ( + input clk_i, + input rst_ni, + + // 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, + + // ds and qs have slightly different timing. + // ds is the data that will be written into the flop, + // while qs is the current flop value exposed to software. + output logic [DW-1:0] ds, + output logic [DW-1:0] qs +); + + logic wr_en; + logic [DW-1:0] wr_data; + + prim_subreg_arb #( + .DW ( DW ), + .SwAccess ( SwAccess ), + .Mubi ( Mubi ) + ) wr_en_data_arb ( + .we, + .wd, + .de, + .d, + .q, + .wr_en, + .wr_data + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q <= RESVAL; + end else if (wr_en) begin + q <= wr_data; + end + end + + // feed back out for consolidation + assign ds = wr_en ? wr_data : qs; + assign qe = wr_en; + + if (SwAccess == SwAccessRC) begin : gen_rc + // In case of a SW RC colliding with a HW write, SW gets the value written by HW + // but the register is cleared to 0. See #5416 for a discussion. + assign qs = de && we ? d : q; + end else begin : gen_no_rc + assign qs = q; + end + +endmodule diff --git a/synth/prim/rtl/prim_subreg_arb.sv b/synth/prim/rtl/prim_subreg_arb.sv new file mode 100755 index 0000000..ba8a282 --- /dev/null +++ b/synth/prim/rtl/prim_subreg_arb.sv @@ -0,0 +1,187 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Write enable and data arbitration logic for register slice conforming to Comportibility guide. + +module prim_subreg_arb + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, + parameter bit Mubi = 1'b0 +) ( + // From SW: valid for RW, WO, W1C, W1S, W0C, RC. + // In case of RC, top connects read pulse to we. + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO. + input de, + input [DW-1:0] d, + + // From register: actual reg value. + input [DW-1:0] q, + + // To register: actual write enable and write data. + output logic wr_en, + output logic [DW-1:0] wr_data +); + import prim_mubi_pkg::*; + + if (SwAccess inside {SwAccessRW, SwAccessWO}) begin : gen_w + assign wr_en = we | de; + assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority + // Unused q - Prevent lint errors. + logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off + assign unused_q = q; + //VCS coverage on + // pragma coverage on + end else if (SwAccess == SwAccessRO) begin : gen_ro + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + //VCS coverage on + // pragma coverage on + end else if (SwAccess == SwAccessW1S) begin : gen_w1s + // If SwAccess is W1S, then assume hw tries to clear. + // So, give a chance HW to clear when SW tries to set. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = prim_mubi_pkg::mubi4_or_hi(prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi4_t'(wd) : + prim_mubi_pkg::MuBi4False)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = prim_mubi_pkg::mubi8_or_hi(prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi8_t'(wd) : + prim_mubi_pkg::MuBi8False)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = prim_mubi_pkg::mubi12_or_hi(prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi12_t'(wd) : + prim_mubi_pkg::MuBi12False)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = prim_mubi_pkg::mubi16_or_hi(prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi16_t'(wd) : + prim_mubi_pkg::MuBi16False)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) | (we ? wd : '0); + end + end else if (SwAccess == SwAccessW1C) begin : gen_w1c + // If SwAccess is W1C, then assume hw tries to set. + // So, give a chance HW to set when SW tries to clear. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = prim_mubi_pkg::mubi4_and_hi(prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi4_t'(~wd) : + prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = prim_mubi_pkg::mubi8_and_hi(prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi8_t'(~wd) : + prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = prim_mubi_pkg::mubi12_and_hi(prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi12_t'(~wd) : + prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = prim_mubi_pkg::mubi16_and_hi(prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi16_t'(~wd) : + prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? ~wd : '1); + end + end else if (SwAccess == SwAccessW0C) begin : gen_w0c + assign wr_en = we | de; + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = prim_mubi_pkg::mubi4_and_hi(prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi4_t'(wd) : + prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = prim_mubi_pkg::mubi8_and_hi(prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi8_t'(wd) : + prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = prim_mubi_pkg::mubi12_and_hi(prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi12_t'(wd) : + prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = prim_mubi_pkg::mubi16_and_hi(prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi16_t'(wd) : + prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? wd : '1); + end + end else if (SwAccess == SwAccessRC) begin : gen_rc + // This swtype is not recommended but exists for compatibility. + // WARN: we signal is actually read signal not write enable. + assign wr_en = we | de; + if (Mubi) begin : gen_mubi + if (DW == 4) begin : gen_mubi4 + assign wr_data = prim_mubi_pkg::mubi4_and_hi(prim_mubi_pkg::mubi4_t'(de ? d : q), + (we ? prim_mubi_pkg::MuBi4False : + prim_mubi_pkg::MuBi4True)); + end else if (DW == 8) begin : gen_mubi8 + assign wr_data = prim_mubi_pkg::mubi8_and_hi(prim_mubi_pkg::mubi8_t'(de ? d : q), + (we ? prim_mubi_pkg::MuBi8False : + prim_mubi_pkg::MuBi8True)); + end else if (DW == 12) begin : gen_mubi12 + assign wr_data = prim_mubi_pkg::mubi12_and_hi(prim_mubi_pkg::mubi12_t'(de ? d : q), + (we ? prim_mubi_pkg::MuBi12False : + prim_mubi_pkg::MuBi12True)); + end else if (DW == 16) begin : gen_mubi16 + assign wr_data = prim_mubi_pkg::mubi16_and_hi(prim_mubi_pkg::mubi16_t'(de ? d : q), + (we ? prim_mubi_pkg::mubi16_t'(wd) : + prim_mubi_pkg::MuBi16True)); + end else begin : gen_invalid_mubi + $error("%m: Invalid width for MuBi"); + end + end else begin : gen_non_mubi + assign wr_data = (de ? d : q) & (we ? '0 : '1); + end + // Unused wd - Prevent lint errors. + logic [DW-1:0] unused_wd; + //VCS coverage off + // pragma coverage off + assign unused_wd = wd; + //VCS coverage on + // pragma coverage on + end else begin : gen_hw + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + //VCS coverage off + // pragma coverage off + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + //VCS coverage on + // pragma coverage on + end + +endmodule diff --git a/synth/prim/rtl/prim_subreg_ext.sv b/synth/prim/rtl/prim_subreg_ext.sv new file mode 100755 index 0000000..30a40c6 --- /dev/null +++ b/synth/prim/rtl/prim_subreg_ext.sv @@ -0,0 +1,32 @@ +// 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] ds, + output logic [DW-1:0] qs +); + + // for external registers, there is no difference + // between qs and ds + assign ds = d; + assign qs = d; + assign q = wd; + assign qe = we; + assign qre = re; + +endmodule diff --git a/synth/prim/rtl/prim_subreg_pkg.sv b/synth/prim/rtl/prim_subreg_pkg.sv new file mode 100755 index 0000000..6e1da04 --- /dev/null +++ b/synth/prim/rtl/prim_subreg_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_subreg_pkg; + + // Register access specifier + typedef enum logic [2:0] { + SwAccessRW = 3'd0, // Read-write + SwAccessRO = 3'd1, // Read-only + SwAccessWO = 3'd2, // Write-only + SwAccessW1C = 3'd3, // Write 1 to clear + SwAccessW1S = 3'd4, // Write 1 to set + SwAccessW0C = 3'd5, // Write 0 to clear + SwAccessRC = 3'd6 // Read to clear. Do not use, only exists for compatibility. + } sw_access_e; +endpackage diff --git a/synth/prim/rtl/prim_subreg_shadow.sv b/synth/prim/rtl/prim_subreg_shadow.sv new file mode 100755 index 0000000..ede9047 --- /dev/null +++ b/synth/prim/rtl/prim_subreg_shadow.sv @@ -0,0 +1,196 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Shadowed register slice conforming to Comportibility guide. + +`include "prim_assert.sv" + +module prim_subreg_shadow + import prim_subreg_pkg::*; +#( + parameter int DW = 32, + parameter sw_access_e SwAccess = SwAccessRW, + parameter logic [DW-1:0] RESVAL = '0, // reset value + parameter bit Mubi = 1'b0 +) ( + input clk_i, + input rst_ni, + input rst_shadowed_ni, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC. + // SW reads clear phase unless SwAccess is RO. + input re, + // In case of RC, top connects read pulse to we. + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO. + input de, + input [DW-1:0] d, + + // Output to HW and Reg Read + output logic qe, + output logic [DW-1:0] q, + output logic [DW-1:0] ds, + output logic [DW-1:0] qs, + + // Phase output to HW + output logic phase, + + // Error conditions + output logic err_update, + output logic err_storage +); + + // Since the shadow register works with the 1's complement value, + // we need to invert the polarity of the SW access if it is either "W1S" or "W0C". + // W1C is forbidden since the W0S complement is not implemented. + `ASSERT_INIT(CheckSwAccessIsLegal_A, + SwAccess inside {SwAccessRW, SwAccessRO, SwAccessWO, SwAccessW1S, SwAccessW0C}) + localparam sw_access_e InvertedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessW0C : + (SwAccess == SwAccessW0C) ? SwAccessW1S : SwAccess; + + // For the staging register, we set the SwAccess to RW in case of W1S and W0C in + // order to always capture the data value on the first write operation - no matter + // whether the data value will actually have an effect. That way, we can still capture + // inconsistent double writes which would otherwise be ignored due to the data value filtering + // effect that W1S and W0C can have. + localparam sw_access_e StagedSwAccess = (SwAccess == SwAccessW1S) ? SwAccessRW : + (SwAccess == SwAccessW0C) ? SwAccessRW : SwAccess; + + // Subreg control signals + logic phase_clear; + logic phase_q; + logic staged_we, shadow_we, committed_we; + logic staged_de, shadow_de, committed_de; + + // Subreg status and data signals + logic committed_qe; + logic [DW-1:0] staged_q, shadow_q, committed_q; + logic [DW-1:0] committed_qs; + + // Effective write enable and write data signals. + // These depend on we, de and wd, d, q as well as SwAccess. + logic wr_en; + logic [DW-1:0] wr_data; + + prim_subreg_arb #( + .DW ( DW ), + .SwAccess ( SwAccess ) + ) wr_en_data_arb ( + .we ( we ), + .wd ( wd ), + .de ( de ), + .d ( d ), + .q ( q ), + .wr_en ( wr_en ), + .wr_data ( wr_data ) + ); + + // Phase clearing: + // - SW reads clear phase unless SwAccess is RO. + // - In case of RO, SW should not interfere with update process. + assign phase_clear = (SwAccess == SwAccessRO) ? 1'b0 : re; + + // Phase tracker: + // - Reads from SW clear the phase back to 0. + // - Writes have priority (can come from SW or HW). + always_ff @(posedge clk_i or negedge rst_ni) begin : phase_reg + if (!rst_ni) begin + phase_q <= 1'b0; + end else if (wr_en && !err_storage) begin + phase_q <= ~phase_q; + end else if (phase_clear || err_storage) begin + phase_q <= 1'b0; + end + end + + // The staged register: + // - Holds the 1's complement value. + // - Written in Phase 0. + // - Once storage error occurs, do not allow any further update until reset + assign staged_we = we & ~phase_q & ~err_storage; + assign staged_de = de & ~phase_q & ~err_storage; + prim_subreg #( + .DW ( DW ), + .SwAccess ( StagedSwAccess ), + .RESVAL ( ~RESVAL ) + ) staged_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .we ( staged_we ), + .wd ( ~wr_data ), + .de ( staged_de ), + .d ( ~d ), + .qe ( ), + .q ( staged_q ), + .ds ( ), + .qs ( ) + ); + + // The shadow register: + // - Holds the 1's complement value. + // - Written in Phase 1. + // - Writes are ignored in case of update errors. + // - Gets the value from the staged register. + // - Once storage error occurs, do not allow any further update until reset + assign shadow_we = we & phase_q & ~err_update & ~err_storage; + assign shadow_de = de & phase_q & ~err_update & ~err_storage; + prim_subreg #( + .DW ( DW ), + .SwAccess ( InvertedSwAccess ), + .RESVAL ( ~RESVAL ) + ) shadow_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_shadowed_ni ), + .we ( shadow_we ), + .wd ( staged_q ), + .de ( shadow_de ), + .d ( staged_q ), + .qe ( ), + .q ( shadow_q ), + .ds ( ), + .qs ( ) + ); + + // The committed register: + // - Written in Phase 1. + // - Writes are ignored in case of update errors. + assign committed_we = shadow_we; + assign committed_de = shadow_de; + prim_subreg #( + .DW ( DW ), + .SwAccess ( SwAccess ), + .RESVAL ( RESVAL ) + ) committed_reg ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .we ( committed_we ), + .wd ( wr_data ), + .de ( committed_de ), + .d ( d ), + .qe ( committed_qe ), + .q ( committed_q ), + .ds ( ds ), + .qs ( committed_qs ) + ); + + // Output phase for hwext. + assign phase = phase_q; + + // Error detection - all bits must match. + assign err_update = (~staged_q != wr_data) ? phase_q & wr_en : 1'b0; + assign err_storage = (~shadow_q != committed_q); + + // Remaining output assignments + assign qe = committed_qe; + assign q = committed_q; + assign qs = committed_qs; + + // prim_subreg_shadow does not support multi-bit software access yet + `ASSERT_NEVER(MubiIsNotYetSupported_A, Mubi) + logic unused_mubi; + assign unused_mubi = Mubi; + +endmodule diff --git a/synth/prim/rtl/prim_subst_perm.sv b/synth/prim/rtl/prim_subst_perm.sv new file mode 100755 index 0000000..e75e3d6 --- /dev/null +++ b/synth/prim/rtl/prim_subst_perm.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 +// +// This is a simple data diffusion primitive that is constructed in a similar fashion +// as the PRESENT cipher (i.e. it uses a substitution/permutation network). Note however +// that this is **not** cryptographically secure. The main purpose of this primitive is to +// provide a cheap diffusion mechanism for arbitrarily sized vectors. +// +// See also: prim_prince, prim_present, prim_cipher_pkg + +module prim_subst_perm #( + parameter int DataWidth = 64, + parameter int NumRounds = 31, + parameter bit Decrypt = 0 // 0: encrypt, 1: decrypt +) ( + input [DataWidth-1:0] data_i, + input [DataWidth-1:0] key_i, + output logic [DataWidth-1:0] data_o +); + + ////////////// + // datapath // + ////////////// + + // The "split_var" hint that we pass to verilator here tells it to schedule the different parts of + // data_state separately. This avoids an UNOPTFLAT error where it would otherwise see a dependency + // chain + // + // data_state -> data_state_sbox -> data_state + // + logic [NumRounds:0][DataWidth-1:0] data_state /* verilator split_var */; + + // initialize + assign data_state[0] = data_i; + + for (genvar r = 0; r < NumRounds; r++) begin : gen_round + logic [DataWidth-1:0] data_state_sbox, data_state_flipped; + //////////////////////////////// + // decryption pass, performs inverse permutation and sbox + if (Decrypt) begin : gen_dec + always_comb begin : p_dec + data_state_sbox = data_state[r] ^ key_i; + // Reverse odd/even grouping + data_state_flipped = data_state_sbox; + for (int k = 0; k < DataWidth/2; k++) begin + data_state_flipped[k * 2] = data_state_sbox[k]; + data_state_flipped[k * 2 + 1] = data_state_sbox[k + DataWidth/2]; + end + // Flip vector + for (int k = 0; k < DataWidth; k++) begin + data_state_sbox[DataWidth - 1 - k] = data_state_flipped[k]; + end + // Inverse SBox layer + for (int k = 0; k < DataWidth/4; k++) begin + data_state_sbox[k*4 +: 4] = prim_cipher_pkg::PRESENT_SBOX4_INV[data_state_sbox[k*4 +: 4]]; + end + data_state[r + 1] = data_state_sbox; + end + //////////////////////////////// + // encryption pass + end else begin : gen_enc + always_comb begin : p_enc + data_state_sbox = data_state[r] ^ key_i; + // This SBox layer is aligned to nibbles, so the uppermost bits may not be affected by this. + // However, the permutation below ensures that these bits get shuffled to a different + // position when performing multiple rounds. + for (int k = 0; k < DataWidth/4; k++) begin + data_state_sbox[k*4 +: 4] = prim_cipher_pkg::PRESENT_SBOX4[data_state_sbox[k*4 +: 4]]; + end + // Flip the vector to move the MSB positions into the LSB positions + for (int k = 0; k < DataWidth; k++) begin + data_state_flipped[DataWidth - 1 - k] = data_state_sbox[k]; + end + // Regroup bits such that all even indices are stacked up first, followed by all odd + // indices. Note that if the Width is odd, this is still ok, since + // the uppermost bit just stays in place in that case. + data_state_sbox = data_state_flipped; + for (int k = 0; k < DataWidth/2; k++) begin + data_state_sbox[k] = data_state_flipped[k * 2]; + data_state_sbox[k + DataWidth/2] = data_state_flipped[k * 2 + 1]; + end + data_state[r + 1] = data_state_sbox; + end + end // gen_enc + //////////////////////////////// + end // gen_round + + // finalize + assign data_o = data_state[NumRounds] ^ key_i; + +endmodule : prim_subst_perm diff --git a/synth/prim/rtl/prim_sum_tree.sv b/synth/prim/rtl/prim_sum_tree.sv new file mode 100755 index 0000000..2c8179a --- /dev/null +++ b/synth/prim/rtl/prim_sum_tree.sv @@ -0,0 +1,124 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Based on prim_max_tree, this module implements an explicit binary tree to find the +// sum of this inputs. The solution has O(N) area and O(log(N)) delay complexity, and +// thus scales well with many input sources. +// +// Note that only input values marked as "valid" are respected in the maximum computation. +// Invalid values are treated as 0. +// + +`include "prim_assert.sv" + +module prim_sum_tree #( + parameter int NumSrc = 32, + parameter int Width = 8 +) ( + // The module is combinational - the clock and reset are only used for assertions. + input clk_i, + input rst_ni, + input [NumSrc-1:0][Width-1:0] values_i, // Input values + input [NumSrc-1:0] valid_i, // Input valid bits + output logic [Width-1:0] sum_value_o, // Summation result + output logic sum_valid_o // Whether any of the inputs is valid +); + + /////////////////////// + // Binary tree logic // + /////////////////////// + + // This only works with 2 or more sources. + `ASSERT_INIT(NumSources_A, NumSrc >= 2) + + // Align to powers of 2 for simplicity. + // A full binary tree with N levels has 2**N + 2**N-1 nodes. + localparam int NumLevels = $clog2(NumSrc); + logic [2**(NumLevels+1)-2:0] vld_tree; + logic [2**(NumLevels+1)-2:0][Width-1:0] sum_tree; + + 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 paPa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // This assigns the input values, their corresponding IDs and valid signals to the tree leafs. + if (level == NumLevels) begin : gen_leafs + if (offset < NumSrc) begin : gen_assign + assign vld_tree[Pa] = valid_i[offset]; + assign sum_tree[Pa] = values_i[offset]; + end else begin : gen_tie_off + assign vld_tree[Pa] = '0; + assign sum_tree[Pa] = '0; + end + // This creates the node assignments. + end else begin : gen_nodes + logic [Width-1:0] node_sum; // Local helper variable + // In case only one of the parents is valid, forward that one + // In case both parents are valid, forward the one with higher value + assign node_sum = (vld_tree[C0] & vld_tree[C1]) ? sum_tree[C1] + sum_tree[C0] : + (vld_tree[C0]) ? sum_tree[C0] : + (vld_tree[C1]) ? sum_tree[C1] : + {Width'(0)}; + + // Forwarding muxes + // Note: these ternaries have triggered a synthesis bug in Vivado versions older + // than 2020.2. If the problem resurfaces again, have a look at issue #1408. + assign vld_tree[Pa] = vld_tree[C1] | vld_tree[C0]; + assign sum_tree[Pa] = node_sum; + end + end : gen_level + end : gen_tree + + + // The results can be found at the tree root + assign sum_valid_o = vld_tree[0]; + assign sum_value_o = sum_tree[0]; + + //////////////// + // Assertions // + //////////////// + +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + // Helper functions for assertions below. + function automatic logic [Width-1:0] sum_value (input logic [NumSrc-1:0][Width-1:0] values_i, + input logic [NumSrc-1:0] valid_i); + logic [Width-1:0] sum = '0; + for (int k = 0; k < NumSrc; k++) begin + if (valid_i[k]) begin + sum += values_i[k]; + end + end + return sum; + endfunction : sum_value + + logic [Width-1:0] sum_value_exp; + assign sum_value_exp = sum_value(values_i, valid_i); + //VCS coverage on + // pragma coverage on + + `ASSERT(ValidInImpliesValidOut_A, |valid_i === sum_valid_o) + `ASSERT(SumComputation_A, sum_valid_o |-> sum_value_o == sum_value_exp) + `ASSERT(SumComputationInvalid_A, !sum_valid_o |-> sum_value_o == '0) +`endif + +endmodule : prim_sum_tree diff --git a/synth/prim/rtl/prim_sync_reqack.sv b/synth/prim/rtl/prim_sync_reqack.sv new file mode 100755 index 0000000..518546f --- /dev/null +++ b/synth/prim/rtl/prim_sync_reqack.sv @@ -0,0 +1,404 @@ +// 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 (SRC) domain is not allowed to de-assert REQ without ACK. +// - The destination (DST) domain is not allowed to send an ACK without a REQ. +// - When resetting one domain, also the other domain needs to be reset with both resets being +// active at the same time. +// - This module works both when syncing from a faster to a slower clock domain and vice versa. +// - Internally, this module uses a non-return-to-zero (NRZ), two-phase handshake protocol by +// default. Assuming the DST domain responds with an ACK immediately, the latency from asserting +// the REQ in the SRC domain is: +// - 1 source + 2 destination clock cycles until the handshake is performed in the DST domain, +// - 1 source + 2 destination + 1 destination + 2 source clock cycles until the handshake is +// performed in the SRC domain. +// - Optionally, the module can also use a return-to-zero (RZ), four-phase handshake protocol. +// That one has lower throughput, but it is safe to partially reset either side, since the +// two FSMs cannot get out of sync due to persisting EVEN/ODD states. The handshake latencies +// are the same as for the NRZ protocol, but the throughput is half that of the NRZ protocol +// since the signals neet to return to zero first, causing two round-trips through the +// synchronizers instead of just one. +// +// For further information, see Section 8.2.4 in H. Kaeslin, "Top-Down Digital VLSI Design: From +// Architecture to Gate-Level Circuits and FPGAs", 2015. + +`include "prim_assert.sv" + +module prim_sync_reqack #( + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by default. + parameter bit EnRzHs = 1'b0 // By Default, the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake protocol + // if the FSMs need to be partial-reset-safe. +) ( + input clk_src_i, // REQ side, SRC domain + input rst_src_ni, // REQ side, SRC domain + input clk_dst_i, // ACK side, DST domain + input rst_dst_ni, // ACK side, DST domain + + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + + input logic src_req_i, // REQ side, SRC domain + output logic src_ack_o, // REQ side, SRC domain + output logic dst_req_o, // ACK side, DST domain + input logic dst_ack_i // ACK side, DST domain +); + + // req_chk_i is used for gating assertions only. + logic unused_req_chk; + assign unused_req_chk = req_chk_i; + + if (EnRzHs) begin : gen_rz_hs_protocol + ////////////////// + // RZ protocol // + ////////////////// + + // Types + typedef enum logic { + LoSt, HiSt + } rz_fsm_e; + + // Signals + rz_fsm_e src_fsm_d, src_fsm_q; + rz_fsm_e dst_fsm_d, dst_fsm_q; + logic src_ack, dst_ack; + logic src_req, dst_req; + + // REQ-side FSM (SRC domain) + always_comb begin : src_fsm + src_fsm_d = src_fsm_q; + src_ack_o = 1'b0; + src_req = 1'b0; + + unique case (src_fsm_q) + LoSt: begin + // Wait for the ack to go back to zero before starting + // a new transaction. + if (!src_ack && src_req_i) begin + src_fsm_d = HiSt; + end + end + HiSt: begin + src_req = 1'b1; + // Forward the acknowledgement. + src_ack_o = src_ack; + // If request drops out, we go back to LoSt. + // If DST side asserts ack, we also go back to LoSt. + if (!src_req_i || src_ack) begin + src_fsm_d = LoSt; + end + end + //VCS coverage off + // pragma coverage off + default: ; + //VCS coverage on + // pragma coverage on + endcase + end + + // Move ACK over to SRC domain. + prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i (clk_src_i), + .rst_ni (rst_src_ni), + .d_i (dst_ack), + .q_o (src_ack) + ); + + // Registers + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_fsm_q <= LoSt; + end else begin + src_fsm_q <= src_fsm_d; + end + end + + // ACK-side FSM (DST domain) + always_comb begin : dst_fsm + dst_fsm_d = dst_fsm_q; + dst_req_o = 1'b0; + dst_ack = 1'b0; + + unique case (dst_fsm_q) + LoSt: begin + if (dst_req) begin + // Forward the request. + dst_req_o = 1'b1; + // Wait for the request and acknowledge to be asserted + // before responding to the SRC side. + if (dst_ack_i) begin + dst_fsm_d = HiSt; + end + end + end + HiSt: begin + dst_ack = 1'b1; + // Wait for the request to drop back to zero. + if (!dst_req) begin + dst_fsm_d = LoSt; + end + end + //VCS coverage off + // pragma coverage off + default: ; + //VCS coverage on + // pragma coverage on + endcase + end + + // Move REQ over to DST domain. + prim_flop_2sync #( + .Width(1) + ) req_sync ( + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .d_i (src_req), + .q_o (dst_req) + ); + + // Registers + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_fsm_q <= LoSt; + end else begin + dst_fsm_q <= dst_fsm_d; + end + end + + end else begin : gen_nrz_hs_protocol + ////////////////// + // NRZ protocol // + ////////////////// + + // Types + typedef enum logic { + EVEN, ODD + } sync_reqack_fsm_e; + + // Signals + sync_reqack_fsm_e src_fsm_ns, src_fsm_cs; + sync_reqack_fsm_e dst_fsm_ns, dst_fsm_cs; + + logic src_req_d, src_req_q, src_ack; + logic dst_ack_d, dst_ack_q, dst_req; + logic src_handshake, dst_handshake; + + assign src_handshake = src_req_i & src_ack_o; + assign dst_handshake = dst_req_o & dst_ack_i; + + // Move REQ over to DST domain. + prim_flop_2sync #( + .Width(1) + ) req_sync ( + .clk_i (clk_dst_i), + .rst_ni (rst_dst_ni), + .d_i (src_req_q), + .q_o (dst_req) + ); + + // Move ACK over to SRC domain. + prim_flop_2sync #( + .Width(1) + ) ack_sync ( + .clk_i (clk_src_i), + .rst_ni (rst_src_ni), + .d_i (dst_ack_q), + .q_o (src_ack) + ); + + // REQ-side FSM (SRC domain) + always_comb begin : src_fsm + src_fsm_ns = src_fsm_cs; + + // By default, we keep the internal REQ value and don't ACK. + src_req_d = src_req_q; + src_ack_o = 1'b0; + + unique case (src_fsm_cs) + + EVEN: begin + // Simply forward REQ and ACK. + src_req_d = src_req_i; + src_ack_o = src_ack; + + // The handshake is done for exactly 1 clock cycle. + if (src_handshake) begin + src_fsm_ns = ODD; + end + end + + ODD: begin + // Internal REQ and ACK have inverted meaning now. If src_req_i is high again, this + // signals a new transaction. + src_req_d = ~src_req_i; + src_ack_o = ~src_ack; + + // The handshake is done for exactly 1 clock cycle. + if (src_handshake) begin + src_fsm_ns = EVEN; + end + end + + //VCS coverage off + // pragma coverage off + + default: ; + + //VCS coverage on + // pragma coverage on + + endcase + end + + // ACK-side FSM (DST domain) + always_comb begin : dst_fsm + dst_fsm_ns = dst_fsm_cs; + + // By default, we don't REQ and keep the internal ACK. + dst_req_o = 1'b0; + dst_ack_d = dst_ack_q; + + unique case (dst_fsm_cs) + + EVEN: begin + // Simply forward REQ and ACK. + dst_req_o = dst_req; + dst_ack_d = dst_ack_i; + + // The handshake is done for exactly 1 clock cycle. + if (dst_handshake) begin + dst_fsm_ns = ODD; + end + end + + ODD: begin + // Internal REQ and ACK have inverted meaning now. If dst_req goes low, this signals a new + // transaction. + dst_req_o = ~dst_req; + dst_ack_d = ~dst_ack_i; + + // The handshake is done for exactly 1 clock cycle. + if (dst_handshake) begin + dst_fsm_ns = EVEN; + end + end + + //VCS coverage off + // pragma coverage off + + default: ; + + //VCS coverage on + // pragma coverage on + + endcase + end + + // Registers + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_fsm_cs <= EVEN; + src_req_q <= 1'b0; + end else begin + src_fsm_cs <= src_fsm_ns; + src_req_q <= src_req_d; + end + end + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_fsm_cs <= EVEN; + dst_ack_q <= 1'b0; + end else begin + dst_fsm_cs <= dst_fsm_ns; + dst_ack_q <= dst_ack_d; + end + end + end + + //////////////// + // Assertions // + //////////////// + + `ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag; + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag <= '0; + end else if (src_req_i && !chk_flag) begin + chk_flag <= 1'b1; + end + end + //VCS coverage on + // pragma coverage on + + // SRC domain can only de-assert REQ after receiving ACK. + `ASSERT(SyncReqAckHoldReq, $fell(src_req_i) && req_chk_i && chk_flag |-> + $fell(src_ack_o), clk_src_i, !rst_src_ni || !rst_dst_ni || !req_chk_i || !chk_flag) + `endif + + // DST domain cannot assert ACK without REQ. + `ASSERT(SyncReqAckAckNeedsReq, dst_ack_i |-> + dst_req_o, clk_dst_i, !rst_src_ni || !rst_dst_ni) + + if (EnRstChks) begin : gen_assert_en_rst_chks + `ifdef INC_ASSERT + + //VCS coverage off + // pragma coverage off + // This assertion is written very oddly because it is difficult to reliably catch + // when rst drops. + // The problem is that reset assertion in the design is often associated with clocks + // stopping, this means things like rise / fell don't work correctly since there are + // no clocks. + // As a result of this, we end up detecting way past the interest point (whenever + // clocks are restored) and falsely assert an error. + // The code below instead tries to use asynchronous flags to determine when and if + // the two domains are properly reset. + logic src_reset_flag; + always_ff @(posedge clk_src_i or negedge rst_src_ni) begin + if (!rst_src_ni) begin + src_reset_flag <= '0; + end else if(src_req_i) begin + src_reset_flag <= 1'b1; + end + end + + logic dst_reset_flag; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + dst_reset_flag <= '0; + end else if (dst_req_o) begin + dst_reset_flag <= 1'b1; + end + end + //VCS coverage on + // pragma coverage on + + // Always reset both domains. Both resets need to be active at the same time. + `ASSERT(SyncReqAckRstSrc, $fell(rst_src_ni) |-> + (!src_reset_flag throughout !dst_reset_flag[->1]), + clk_src_i, 0) + `ASSERT(SyncReqAckRstDst, $fell(rst_dst_ni) |-> + (!dst_reset_flag throughout !src_reset_flag[->1]), + clk_dst_i, 0) + + `endif + + + end + +endmodule diff --git a/synth/prim/rtl/prim_sync_reqack_data.sv b/synth/prim/rtl/prim_sync_reqack_data.sv new file mode 100755 index 0000000..c4ef5e3 --- /dev/null +++ b/synth/prim/rtl/prim_sync_reqack_data.sv @@ -0,0 +1,178 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// REQ/ACK synchronizer with associated data. +// +// This module synchronizes a REQ/ACK handshake with associated data across a clock domain +// crossing (CDC). Both domains will see a handshake with the duration of one clock cycle. By +// default, the data itself is not registered. The main purpose of feeding the data through this +// module to have an anchor point for waiving CDC violations. If the data is configured to flow +// from the destination (DST) to the source (SRC) domain, an additional register stage can be +// inserted for data buffering. +// +// Under the hood, this module uses a prim_sync_reqack primitive for synchronizing the +// REQ/ACK handshake. See prim_sync_reqack.sv for more details. + +`include "prim_assert.sv" + +module prim_sync_reqack_data #( + parameter int unsigned Width = 1, + parameter bit EnRstChks = 1'b0, // Enable reset-related assertion checks, disabled by + // default. + parameter bit DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST, + // 1'b0 = DST to SRC + parameter bit DataReg = 1'b0, // Enable optional register stage for data, + // only usable with DataSrc2Dst == 1'b0. + parameter bit EnRzHs = 1'b0 // By Default, we the faster NRZ handshake protocol + // (EnRzHs = 0) is used. Enable the RZ handshake + // protocol if the FSMs need to be partial-reset-safe. +) ( + input clk_src_i, // REQ side, SRC domain + input rst_src_ni, // REQ side, SRC domain + input clk_dst_i, // ACK side, DST domain + input rst_dst_ni, // ACK side, DST domain + + input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation. + + input logic src_req_i, // REQ side, SRC domain + output logic src_ack_o, // REQ side, SRC domain + output logic dst_req_o, // ACK side, DST domain + input logic dst_ack_i, // ACK side, DST domain + + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + //////////////////////////////////// + // REQ/ACK synchronizer primitive // + //////////////////////////////////// + prim_sync_reqack #( + .EnRstChks(EnRstChks), + .EnRzHs(EnRzHs) + ) u_prim_sync_reqack ( + .clk_src_i, + .rst_src_ni, + .clk_dst_i, + .rst_dst_ni, + + .req_chk_i, + + .src_req_i, + .src_ack_o, + .dst_req_o, + .dst_ack_i + ); + + ///////////////////////// + // Data register stage // + ///////////////////////// + // Optional - Only relevant if the data flows from DST to SRC. In this case, it must be ensured + // that the data remains stable until the ACK becomes visible in the SRC domain. + // + // Note that for larger data widths, it is recommended to adjust the data sender to hold the data + // stable until the next REQ in order to save the cost of this register stage. + if (DataSrc2Dst == 1'b0 && DataReg == 1'b1) begin : gen_data_reg + logic data_we; + logic [Width-1:0] data_d, data_q; + + // Sample the data when seing the REQ/ACK handshake in the DST domain. + assign data_we = dst_req_o & dst_ack_i; + assign data_d = data_i; + always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin + if (!rst_dst_ni) begin + data_q <= '0; + end else if (data_we) begin + data_q <= data_d; + end + end + assign data_o = data_q; + + end else begin : gen_no_data_reg + // Just feed through the data. + assign data_o = data_i; + end + + //////////////// + // Assertions // + //////////////// + if (DataSrc2Dst == 1'b1) begin : gen_assert_data_src2dst +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag_d, chk_flag_q; + assign chk_flag_d = src_req_i && !chk_flag_q ? 1'b1 : chk_flag_q; + + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag_q <= '0; + end else begin + chk_flag_q <= chk_flag_d; + end + end + //VCS coverage on + // pragma coverage on + + // SRC domain cannot change data while waiting for ACK. + `ASSERT(SyncReqAckDataHoldSrc2Dst, !$stable(data_i) && chk_flag_q |-> + (!src_req_i || (src_req_i && src_ack_o)), + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) + + // Register stage cannot be used. + `ASSERT_INIT(SyncReqAckDataReg, DataSrc2Dst && !DataReg) +`endif + end else if (DataSrc2Dst == 1'b0 && DataReg == 1'b0) begin : gen_assert_data_dst2src + // DST domain shall not change data while waiting for SRC domain to latch it (together with + // receiving ACK). It takes 2 SRC cycles for ACK to cross over from DST to SRC, and 1 SRC cycle + // for the next REQ to cross over from SRC to DST. + // + // Denote the src clock where REQ & ACK as time zero. The data flowing through the CDC could be + // corrupted if data_o was not stable over the previous 2 clock cycles (so we want to check time + // points -2, -1 and 0). Moreover, the DST domain cannot know that it is allowed to change value + // until at least one more SRC cycle (the time taken for REQ to cross back from SRC to DST). + // + // To make this assertion, we will sample at each of 4 time points (-2, -1, 0 and +1), asserting + // that data_o is equal at each of these times. Note this won't detect glitches at intermediate + // timepoints. + // + // The SVAs below are designed not to consume time, which means that they can be disabled with + // an $assertoff(..) and won't linger to fail later. This wouldn't work properly if we used + // something like |=> instead of the $past(...) function. That means that we have to trigger at + // the "end" of the window. To make sure we don't miss a situation where the value changed at + // time -1 (causing corruption), but reset was asserted between time 0 and 1, we split the + // assertion into two pieces. The first (SyncReqAckDataHoldDst2SrcA) checks that data doesn't + // change in a way that could cause data corruption. The second (SyncReqAckDataHoldDst2SrcB) + // checks that the DST side doesn't do anything that it shouldn't know is safe. +`ifdef INC_ASSERT + //VCS coverage off + // pragma coverage off + logic effective_rst_n; + assign effective_rst_n = rst_src_ni && rst_dst_ni; + + logic chk_flag_d, chk_flag_q; + assign chk_flag_d = src_req_i && !chk_flag_q ? 1'b1 : chk_flag_q; + + always_ff @(posedge clk_src_i or negedge effective_rst_n) begin + if (!effective_rst_n) begin + chk_flag_q <= '0; + end else begin + chk_flag_q <= chk_flag_d; + end + end + //VCS coverage on + // pragma coverage on + + `ASSERT(SyncReqAckDataHoldDst2SrcA, + chk_flag_q && src_req_i && src_ack_o |-> + $past(data_o, 2) == data_o && $past(data_o) == data_o, + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) + `ASSERT(SyncReqAckDataHoldDst2SrcB, + chk_flag_q && $past(src_req_i && src_ack_o) |-> $past(data_o) == data_o, + clk_src_i, !rst_src_ni || !rst_dst_ni || !chk_flag_q) +`endif + end + +endmodule diff --git a/synth/prim/rtl/prim_sync_slow_fast.sv b/synth/prim/rtl/prim_sync_slow_fast.sv new file mode 100755 index 0000000..8bffa1d --- /dev/null +++ b/synth/prim/rtl/prim_sync_slow_fast.sv @@ -0,0 +1,59 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Slow to fast clock synchronizer +// This module is designed to be used for efficient sampling of signals from a slow clock to a much +// faster clock. +// +// The data is captured into flops on the negative edge of the slow clock (when the data should be +// stable). Because the slow clock passes through a two-flop synchronizer, the ratio of clock speeds +// needs to be high to guarantee that the data will be stable when sampled. +// +// A ratio of at-least 10:1 in clock speeds is recommended. + +module prim_sync_slow_fast #( + parameter int unsigned Width = 32 +) ( + input logic clk_slow_i, + input logic clk_fast_i, + input logic rst_fast_ni, + input logic [Width-1:0] wdata_i, // Slow domain + output logic [Width-1:0] rdata_o // Fast domain +); + + logic sync_clk_slow, sync_clk_slow_q; + logic wdata_en; + logic [Width-1:0] wdata_q; + + // Synchronize the slow clock to the fast domain + prim_flop_2sync #(.Width(1)) sync_slow_clk ( + .clk_i (clk_fast_i), + .rst_ni (rst_fast_ni), + .d_i (clk_slow_i), + .q_o (sync_clk_slow)); + + // Register the synchronized clk + always_ff @(posedge clk_fast_i or negedge rst_fast_ni) begin + if (!rst_fast_ni) begin + sync_clk_slow_q <= 1'b0; + end else begin + sync_clk_slow_q <= sync_clk_slow; + end + end + + // Find the negative edge of the synchronized slow clk + assign wdata_en = sync_clk_slow_q & !sync_clk_slow; + + // Sample the slow data on the negative edge + always_ff @(posedge clk_fast_i or negedge rst_fast_ni) begin + if (!rst_fast_ni) begin + wdata_q <= '0; + end else if (wdata_en) begin + wdata_q <= wdata_i; + end + end + + assign rdata_o = wdata_q; + +endmodule diff --git a/synth/prim/rtl/prim_trivium.sv b/synth/prim/rtl/prim_trivium.sv new file mode 100755 index 0000000..0661480 --- /dev/null +++ b/synth/prim/rtl/prim_trivium.sv @@ -0,0 +1,317 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Trivium and Bivium stream cipher primitives +// +// This module implements the Trivium [1] and its reduced variant Bivium [2] (more precisely +// Bivium B) stream cipher primitives. Internally, they use 3 (Trivium) or 2 (Bivium) non-linear +// feedback shift registers (NFSRs). The number of output bits produced per update can flexibly +// be tuned using the OutputWidth parameter to match the needs of integrators. Up to an output +// width of 64 bits, the critical path stays unchanged. For output widths above 64 bits, the +// critical path starts to increase. The asymptotic area of the two primitives is 30 GE / output +// bit (Trivium) and 20 GE / output bit (Bivium). For a thorough analysis of the two primitives +// including security evaluation, as well as area and critical path figures, refer to [3]. +// +// As thoroughly analyzed in [3], these primitives are suitable to be used as pseudo-random +// number generators for masking countermeasures in hardware. +// +// This implementation supports three different reseeding interfaces which can be selected using +// the SeedType parameter: +// 1. SeedTypeKeyIv: 2 x 80 bits for key and IV need to be provided. Before the key stream becomes +// usable, the primitive needs to be updated 1152 / OutputWidth (Trivium) or +// 708 / OutputWidth (Bivium) times. These initial updates are performed automatically by the +// primitives. Once the reseeding and the following initialization are done, this is indicated +// with the seed_done_o output. +// 2. SeedTypeStateFull: The full 288-bit (Trivium) or 177-bit (Bivium) state is reseeded in one +// shot. +// 3. SeedTypeStatePartial: PartialSeedWidth bits at a time are injected into the state. The +// primitive latches the seed_en_i signal and keeps requesting entropy until every +// PartialSeedWidth-wide part of the state has been overwritten once. +// To enable updating the primitive and using the key stream during the reseed operation, the +// number of output bits produced per update (OutputWidth) should be greater than the width of +// the smallest NFSR in the primitve (MinNfsrWidth = 84). Thanks to the strong diffusion +// properties of the primitives, the majority of state and key stream bits change after +// reseeding the first state part and performing the first couple of updates if OutputWidth is +// chosen sufficiently large. For Bivium, a quick evaluation hints that for OutputWidth equal +// to 84, the key stream seems usable after 3 updates and most state bits seem to change after +// 5 updates. For OutputWidth equal to 160, the key stream seems usable after only 2 updates and +// most state bits seem to change after 3 updates. +// If the next PartialSeedWidth bits of entropy arrive after having done at least one update +// but the new entropy hasn't sufficiently diffused yet into the state, there is a risk that +// previously injected entropy bits are partially or completely overwritten. It is the job of +// the integrator to ensure sufficiently many updates are performed between reseeding state +// parts. In practice, this should be relatively simple as there is typically a minimal latency +// between receiving entropy bits, e.g., due to clock domain crossings in the system. +// Independently of the chosen OutputWidth parameter, it's always safe to reseed the primitive +// while it's completely idle. +// +// For details, see the following specifications and papers: +// [1] De Canniere, "Trivium Specifications" available at +// https://www.ecrypt.eu.org/stream/p3ciphers/trivium/trivium_p3.pdf +// [2] Raddum "Cryptanalytic Results on Trivium" available at +// https://www.ecrypt.eu.org/stream/papersdir/2006/039.ps +// [3] Cassiers, "Randomness Generation for Secure Hardware Masking - Unrolled Trivium to the +// Rescue" available at https://eprint.iacr.org/2023/1134 + +`include "prim_assert.sv" + +module prim_trivium import prim_trivium_pkg::*; +#( + parameter bit BiviumVariant = 0, // 0: Trivium, 1: Bivium + parameter int unsigned OutputWidth = 64, // Number of output bits generated per update. + parameter bit StrictLockupProtection = 1, // Upon entering an all zero state, 1: always + // restore to the default seed, or 0: allow + // to keep the all zero state if requested by + // allow_lockup_i. + parameter seed_type_e SeedType = SeedTypeStateFull, // Reseeding inteface selection, see + // prim_trivium_pkg.sv for possible values. + parameter int unsigned PartialSeedWidth = PartialSeedWidthDefault, + + // derived parameter + localparam int unsigned StateWidth = BiviumVariant ? BiviumStateWidth : TriviumStateWidth, + + parameter trivium_lfsr_seed_t RndCnstTriviumLfsrSeed = RndCnstTriviumLfsrSeedDefault, + + // derived parameter + localparam logic [StateWidth-1:0] StateSeed = RndCnstTriviumLfsrSeed[StateWidth-1:0] +) ( + input logic clk_i, + input logic rst_ni, + + input logic en_i, // Update the primitive. + input logic allow_lockup_i, // Allow locking up in all zero state. + // Only taken into account if + // LockupParameter = 0 + input logic seed_en_i, // Start reseeding (pulse or level). + output logic seed_done_o, // Reseeding is done (pulse). + output logic seed_req_o, // Seed REQ handshake signal + input logic seed_ack_i, // Seed ACK handshake signal + input logic [KeyIvWidth-1:0] seed_key_i, // Seed input for SeedTypeKeyIV + input logic [KeyIvWidth-1:0] seed_iv_i, // Seed input for SeedTypeKeyIV + input logic [StateWidth-1:0] seed_state_full_i, // Seed input for SeedTypeStateFull + input logic [PartialSeedWidth-1:0] seed_state_partial_i, // Seed input for SeedTypeStatePartial + + output logic [OutputWidth-1:0] key_o, // Key stream output + output logic err_o // The primitive entered an all zero state and may have + // locked up or entered the default state defined by + // RndCnstTriviumLfsrSeed depending on the + // StrictLockupProtection parameter and the allow_lockup_i + // signal. +); + + localparam int unsigned LastStatePartFractional = StateWidth % PartialSeedWidth != 0 ? 1 : 0; + localparam int unsigned NumStateParts = StateWidth / PartialSeedWidth + LastStatePartFractional; + localparam int unsigned NumBitsLastPart = StateWidth - (NumStateParts - 1) * PartialSeedWidth; + localparam int unsigned LastStatePart = NumStateParts - 1; + // Width of the variable determining which state part to overwrite with the next partial seed. + localparam int unsigned StateIdxWidth = prim_util_pkg::vbits(NumStateParts); + + logic [StateWidth-1:0] state_d, state_q; + logic [StateWidth-1:0] state_update, state_seed; + logic seed_req_d, seed_req_q; + logic unused_seed; + logic update, update_init, wr_en_seed; + logic [StateIdxWidth-1:0] state_idx_d, state_idx_q; + logic last_state_part; + logic lockup, restore; + + assign update = en_i | update_init; + assign wr_en_seed = seed_req_o & seed_ack_i; + assign lockup = ~(|state_q); + assign err_o = lockup; + + ////////////////////////////////////////////////// + // Regular state updating and output generation // + ////////////////////////////////////////////////// + + // The current key stream depends on the current state only. + if (BiviumVariant) begin : gen_update_and_output_bivium + always_comb begin + state_update = state_q; + for (int unsigned i = 0; i < OutputWidth; i++) begin + key_o[i] = bivium_generate_key_stream(state_update); + state_update = bivium_update_state(state_update); + end + end + end else begin : gen_update_and_output_trivium + always_comb begin + state_update = state_q; + for (int unsigned i = 0; i < OutputWidth; i++) begin + key_o[i] = trivium_generate_key_stream(state_update); + state_update = trivium_update_state(state_update); + end + end + end + + /////////////// + // Reseeding // + /////////////// + + if (SeedType == SeedTypeKeyIv) begin : gen_seed_type_key_iv + if (BiviumVariant) begin : gen_seed_type_key_iv_bivium + assign state_seed = bivium_seed_key_iv(seed_key_i, seed_iv_i); + end else begin : gen_seed_type_key_iv_trivium + assign state_seed = trivium_seed_key_iv(seed_key_i, seed_iv_i); + end + + end else if (SeedType == SeedTypeStateFull) begin : gen_seed_type_state_full + assign state_seed = seed_state_full_i; + + end else begin : gen_seed_type_state_partial + // If the primitive is idle and an update is not currently being requested (update = 1'b0), the + // parts not currently being reseeded remain constant, i.e., the update function above doesn't + // modify the state. This is required to put the primitive into a known state e.g. for known + // answer testing. + // If the primitive is busy and an update is requested, the update function always modifies + // the state (but the part currently being reseeded is solely determined by the new seed). + // Otherwise the primitive could potentially produce the same key stream output twice in a row. + always_comb begin + state_seed = !update ? state_q : state_update; + // The last part may be shorter than PartialSeedWidth. + if (last_state_part) begin + state_seed[StateWidth - 1 -: NumBitsLastPart] = seed_state_partial_i[NumBitsLastPart-1:0]; + end else begin + state_seed[state_idx_q * PartialSeedWidth +: PartialSeedWidth] = seed_state_partial_i; + end + end + end + + ///////////////////////////////// + // State register and updating // + ///////////////////////////////// + + // The lockup protection can optionally be disabled at run time. This may be required to put the + // primitive into an all zero state, e.g., to switch off masking countermeasures for analysis if + // the primitive is used for generating masks. However, the err_o bit always signals this + // condition to the outside. + assign restore = lockup & (StrictLockupProtection | ~allow_lockup_i); + assign state_d = restore ? StateSeed : + wr_en_seed ? state_seed : + update ? state_update : state_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin : state_reg + if (!rst_ni) begin + state_q <= StateSeed; + end else begin + state_q <= state_d; + end + end + + // Latch the seed enable signal and keep the request high until the last request is acknowledged. + assign seed_req_d = (seed_en_i | seed_req_q) & (~seed_ack_i | ~last_state_part); + always_ff @(posedge clk_i or negedge rst_ni) begin : seed_req_reg + if (!rst_ni) begin + seed_req_q <= 1'b0; + end else begin + seed_req_q <= seed_req_d; + end + end + assign seed_req_o = seed_en_i | seed_req_q; + + if (SeedType == SeedTypeKeyIv) begin : gen_key_iv_seed_handling + // After receiving key and IV, the entire state needs to be updated 4 times before the key + // stream becomes usable. Depending on OutputWidth, a different number of initial updates are + // required for this. [3] + localparam int unsigned NumInitUpdatesFractional = (StateWidth * 4) % OutputWidth != 0 ? 1 : 0; + localparam int unsigned NumInitUpdates = + (StateWidth * 4) / OutputWidth + NumInitUpdatesFractional; + localparam int unsigned LastInitUpdate = NumInitUpdates - 1; + localparam int unsigned InitUpdatesCtrWidth = prim_util_pkg::vbits(NumInitUpdates); + + logic [InitUpdatesCtrWidth-1:0] init_update_ctr_d, init_update_ctr_q; + logic init_update_d, init_update_q; + logic last_init_update; + + // Count the number of initial updates done. + assign init_update_ctr_d = wr_en_seed ? '0 : + init_update_q ? init_update_ctr_q + 1'b1 : init_update_ctr_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : init_update_ctr_reg + if (!rst_ni) begin + init_update_ctr_q <= '0; + end else begin + init_update_ctr_q <= init_update_ctr_d; + end + end + + // Track whether we're currently doing the initial updates. + assign last_init_update = init_update_ctr_q == LastInitUpdate[InitUpdatesCtrWidth-1:0]; + assign init_update_d = wr_en_seed ? 1'b1 : + last_init_update ? 1'b0 : init_update_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : init_update_reg + if (!rst_ni) begin + init_update_q <= 1'b0; + end else begin + init_update_q <= init_update_d; + end + end + assign update_init = init_update_q; + + // We're done after performing the initial updates. + assign seed_done_o = init_update_q & last_init_update; + + // Tie off unused signals. + assign state_idx_d = '0; + assign state_idx_q = '0; + assign last_state_part = 1'b0; + assign unused_seed = ^{seed_state_full_i, + seed_state_partial_i, + state_idx_d, + state_idx_q, + last_state_part}; + + end else if (SeedType == SeedTypeStateFull) begin : gen_full_seed_handling + + // Only one handshake is required. + assign seed_done_o = seed_req_o & seed_ack_i; + + // Tie off unused signals. + assign update_init = 1'b0; + assign state_idx_d = '0; + assign state_idx_q = '0; + assign last_state_part = 1'b1; + assign unused_seed = ^{seed_key_i, + seed_iv_i, + seed_state_partial_i, + state_idx_d, + state_idx_q, + last_state_part}; + + end else begin : gen_partial_seed_handling + + // Seed PartialSeedWidth bits of the state at a time. Track the part idx using a counter. The + // counter is reset when seeding the last part. + assign last_state_part = state_idx_q == LastStatePart[StateIdxWidth-1:0]; + assign state_idx_d = wr_en_seed & last_state_part ? '0 : + wr_en_seed & ~last_state_part ? state_idx_q + 1'b1 : state_idx_q; + always_ff @(posedge clk_i or negedge rst_ni) begin : state_idx_reg + if (!rst_ni) begin + state_idx_q <= '0; + end else begin + state_idx_q <= state_idx_d; + end + end + + // We're done upon receiving the last state part. + assign seed_done_o = seed_req_o & seed_ack_i & last_state_part; + + // Tie off unused signals. + assign update_init = 1'b0; + assign unused_seed = ^{seed_key_i, + seed_iv_i, + seed_state_full_i}; + end + + ///////////////// + // Asssertions // + ///////////////// + + // While performing a partial reseed of the state, the primitive can be updated. However, this + // should only be done if the number of produced bits per update / shift amount per update is + // greater than the width of the smallest NFSR (= 84) inside the primitve. Otherwise, there is a + // risk of overwriting the previously provided partial seed which reduces the amount of fresh + // entropy injected per full reseed operation. + `ASSERT(PrimTriviumPartialStateSeedWhileUpdate_A, + (SeedType == SeedTypeStatePartial) && seed_req_o && en_i |-> OutputWidth >= MinNfsrWidth) + +endmodule diff --git a/synth/prim/rtl/prim_trivium_pkg.sv b/synth/prim/rtl/prim_trivium_pkg.sv new file mode 100755 index 0000000..317e0d4 --- /dev/null +++ b/synth/prim/rtl/prim_trivium_pkg.sv @@ -0,0 +1,159 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +package prim_trivium_pkg; + + typedef enum integer { + SeedTypeKeyIv, // 2 * 80 bits for key and IV, requires advancing the primitive + // 1152/OutputWidth (Trivium) or 708/OutputWidth (Bivium) times + // before the key stream becomes usable. + SeedTypeStateFull, // Seed the full state + SeedTypeStatePartial // Seed PartialSeedWidth bits of the state at a time. + } seed_type_e; + + parameter int unsigned KeyIvWidth = 80; + parameter int unsigned PartialSeedWidthDefault = 32; + parameter int unsigned MinNfsrWidth = 84; + + // These LFSR parameters have been generated with + // $ ./util/design/gen-lfsr-seed.py --width 288 --seed 31468618 --prefix "Trivium" + parameter int TriviumLfsrWidth = 288; + typedef logic [TriviumLfsrWidth-1:0] trivium_lfsr_seed_t; + parameter trivium_lfsr_seed_t RndCnstTriviumLfsrSeedDefault = { + 32'h758a4420, + 256'h31e1c461_6ea343ec_153282a3_0c132b57_23c5a4cf_4743b3c7_c32d580f_74f1713a + }; + + ///////////// + // Trivium // + ///////////// + + parameter int unsigned TriviumMaxNfsrWidth = 111; + parameter int TriviumStateWidth = TriviumLfsrWidth; + + function automatic logic [TriviumStateWidth-1:0] trivium_update_state( + logic [TriviumStateWidth-1:0] in + ); + logic [TriviumStateWidth-1:0] out; + logic mul_90_91, mul_174_175, mul_285_286; + logic add_65_92, add_161_176, add_242_287; + + // First state part intermediate results + mul_90_91 = in[90] & in[91]; + add_65_92 = in[65] ^ in[92]; + + // Second state part intermediate results + mul_174_175 = in[174] & in[175]; + add_161_176 = in[161] ^ in[176]; + + // Third state part intermediate results + mul_285_286 = in[285] & in[286]; + add_242_287 = in[242] ^ in[287]; + + // Updates - feedback part + out[0] = in[68] ^ (mul_285_286 ^ add_242_287); + out[93] = in[170] ^ (add_65_92 ^ mul_90_91); + out[177] = in[263] ^ (mul_174_175 ^ add_161_176); + + // Updates - shift part + out[92:1] = in[91:0]; + out[176:94] = in[175:93]; + out[287:178] = in[286:177]; + + return out; + endfunction + + function automatic logic trivium_generate_key_stream( + logic [TriviumStateWidth-1:0] state + ); + logic key; + logic add_65_92, add_161_176, add_242_287; + logic unused_state; + + add_65_92 = state[65] ^ state[92]; + add_161_176 = state[161] ^ state[176]; + add_242_287 = state[242] ^ state[287]; + key = add_161_176 ^ add_65_92 ^ add_242_287; + + unused_state = ^{state[286:243], + state[241:177], + state[175:162], + state[160:93], + state[91:66], + state[64:0]}; + return key; + endfunction + + function automatic logic [TriviumStateWidth-1:0] trivium_seed_key_iv( + logic [KeyIvWidth-1:0] key, + logic [KeyIvWidth-1:0] iv + ); + logic [TriviumStateWidth-1:0] state; + // [287:285] [284:173] [172:93] [92:80] [79:0] + state = {3'b111, 112'b0, iv, 13'b0, key}; + return state; + endfunction + + //////////// + // Bivium // + //////////// + + parameter int unsigned BiviumMaxNfsrWidth = 93; + parameter int BiviumStateWidth = 177; + + function automatic logic [BiviumStateWidth-1:0] bivium_update_state( + logic [BiviumStateWidth-1:0] in + ); + logic [BiviumStateWidth-1:0] out; + logic mul_90_91, mul_174_175; + logic add_65_92, add_161_176; + + // First state half intermediate results + mul_90_91 = in[90] & in[91]; + add_65_92 = in[65] ^ in[92]; + + // Second state half intermediate results + mul_174_175 = in[174] & in[175]; + add_161_176 = in[161] ^ in[176]; + + // Updates - feedback part + out[0] = in[68] ^ (mul_174_175 ^ add_161_176); + out[93] = in[170] ^ add_65_92 ^ mul_90_91; + + // Updates - shift part + out[92:1] = in[91:0]; + out[176:94] = in[175:93]; + + return out; + endfunction + + function automatic logic bivium_generate_key_stream( + logic [BiviumStateWidth-1:0] state + ); + logic key; + logic add_65_92, add_161_176; + logic unused_state; + + add_65_92 = state[65] ^ state[92]; + add_161_176 = state[161] ^ state[176]; + key = add_161_176 ^ add_65_92; + + unused_state = ^{state[175:162], + state[160:93], + state[91:66], + state[64:0]}; + return key; + endfunction + + function automatic logic [BiviumStateWidth-1:0] bivium_seed_key_iv( + logic [KeyIvWidth-1:0] key, + logic [KeyIvWidth-1:0] iv + ); + logic [BiviumStateWidth-1:0] state; + // [176:173] [172:93] [92:80] [79:0] + state = {4'b0, iv, 13'b0, key}; + return state; + endfunction + +endpackage diff --git a/synth/prim/rtl/prim_util_get_scramble_params.svh b/synth/prim/rtl/prim_util_get_scramble_params.svh new file mode 100755 index 0000000..e5e1860 --- /dev/null +++ b/synth/prim/rtl/prim_util_get_scramble_params.svh @@ -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 + +`ifndef SYNTHESIS + export "DPI-C" function simutil_get_scramble_key; + + function int simutil_get_scramble_key(output bit [127:0] val); + int valid; + valid = key_valid_i && DataKeyWidth == 128 ? 1 : 0; + if (valid == 1) val = key_i; + return valid; + endfunction + + export "DPI-C" function simutil_get_scramble_nonce; + + function int simutil_get_scramble_nonce(output bit [319:0] nonce); + int valid; + valid = key_valid_i && NonceWidth <= 320 ? 1 : 0; + if (valid == 1) begin + nonce = '0; + nonce[NonceWidth-1:0] = nonce_i; + end + return valid; + endfunction +`endif diff --git a/synth/prim/rtl/prim_util_memload.svh b/synth/prim/rtl/prim_util_memload.svh new file mode 100755 index 0000000..7f8c6da --- /dev/null +++ b/synth/prim/rtl/prim_util_memload.svh @@ -0,0 +1,68 @@ +// 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. + * + * Note this works with memories up to a maximum width of 312 bits. Should this maximum width be + * increased all of the `simutil_set_mem` and `simutil_get_mem` call sites must be found (e.g. using + * git grep) and adjusted appropriately. + */ + +`ifndef SYNTHESIS + // Task for loading 'mem' with SystemVerilog system task $readmemh() + export "DPI-C" task simutil_memload; + + task simutil_memload; + input string file; + $readmemh(file, mem); + endtask + + // Function for setting a specific element in |mem| + // Returns 1 (true) for success, 0 (false) for errors. + export "DPI-C" function simutil_set_mem; + + function int simutil_set_mem(input int index, input bit [311:0] val); + int valid; + valid = Width > 312 || index >= Depth ? 0 : 1; + if (valid == 1) mem[index] = val[Width-1:0]; + return valid; + endfunction + + // Function for getting a specific element in |mem| + export "DPI-C" function simutil_get_mem; + + function int simutil_get_mem(input int index, output bit [311:0] val); + int valid; + valid = Width > 312 || index >= Depth ? 0 : 1; + if (valid == 1) begin + val = 0; + val[Width-1:0] = mem[index]; + end + return valid; + endfunction +`endif + +initial begin + logic show_mem_paths; + + // Print the hierarchical path to the memory to help make formal connectivity checks easy. + void'($value$plusargs("show_mem_paths=%0b", show_mem_paths)); + if (show_mem_paths) $display("%m"); + + if (MemInitFile != "") begin : gen_meminit + $display("Initializing memory %m from file '%s'.", MemInitFile); + $readmemh(MemInitFile, mem); + end +end diff --git a/synth/prim/rtl/prim_util_pkg.sv b/synth/prim/rtl/prim_util_pkg.sv new file mode 100755 index 0000000..f5e3c92 --- /dev/null +++ b/synth/prim/rtl/prim_util_pkg.sv @@ -0,0 +1,98 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + + +/** + * Utility functions + */ +package prim_util_pkg; + /** + * Math function: $clog2 as specified in Verilog-2005 + * + * Do not use this function if $clog2() is available. + * + * clog2 = 0 for value == 0 + * ceil(log2(value)) for value >= 1 + * + * This implementation is a synthesizable variant of the $clog2 function as + * specified in the Verilog-2005 standard (IEEE 1364-2005). + * + * To quote the standard: + * The system function $clog2 shall return the ceiling of the log + * base 2 of the argument (the log rounded up to an integer + * value). The argument can be an integer or an arbitrary sized + * vector value. The argument shall be treated as an unsigned + * value, and an argument value of 0 shall produce a result of 0. + */ + function automatic integer _clog2(integer value); + integer result; + // Use an intermediate value to avoid assigning to an input port, which produces a warning in + // Synopsys DC. + integer v = value; + v = v - 1; + for (result = 0; v > 0; result++) begin + v = v >> 1; + end + return result; + endfunction + + + /** + * Math function: Number of bits needed to address |value| items. + * + * 0 for value == 0 + * vbits = 1 for value == 1 + * ceil(log2(value)) for value > 1 + * + * + * The primary use case for this function is the definition of registers/arrays + * which are wide enough to contain |value| items. + * + * This function identical to $clog2() for all input values except the value 1; + * it could be considered an "enhanced" $clog2() function. + * + * + * Example 1: + * parameter Items = 1; + * localparam ItemsWidth = vbits(Items); // 1 + * logic [ItemsWidth-1:0] item_register; // items_register is now [0:0] + * + * Example 2: + * parameter Items = 64; + * localparam ItemsWidth = vbits(Items); // 6 + * logic [ItemsWidth-1:0] item_register; // items_register is now [5:0] + * + * Note: If you want to store the number "value" inside a register, you need + * a register with size vbits(value + 1), since you also need to store + * the number 0. + * + * Example 3: + * logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0] + * logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0] + */ + function automatic integer vbits(integer value); +`ifdef XCELIUM + // The use of system functions was not allowed here in Verilog-2001, but is + // valid since (System)Verilog-2005, which is also when $clog2() first + // appeared. + // Xcelium < 19.10 does not yet support the use of $clog2() here, fall back + // to an implementation without a system function. Remove this workaround + // if we require a newer Xcelium version. + // See #2579 and #2597. + return (value == 1) ? 1 : _clog2(value); +`else + return (value == 1) ? 1 : $clog2(value); +`endif + endfunction + +`ifdef INC_ASSERT + // Package-scoped variable to detect the end of simulation. + // + // Used only in DV simulations. The bit will be used by assertions in RTL to perform end-of-test + // cleanup. It is set to 1 in `dv_test_status_pkg::dv_test_status()`, which is invoked right + // before the simulation is terminated, to signal the status of the test. + bit end_of_simulation; +`endif + +endpackage diff --git a/synth/prim/rtl/prim_xoshiro256pp.sv b/synth/prim/rtl/prim_xoshiro256pp.sv new file mode 100755 index 0000000..cb6d953 --- /dev/null +++ b/synth/prim/rtl/prim_xoshiro256pp.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 +// +// This module implements XoShiRo256++ PRNG +// +// Additional Entropy input to XOR fresh entropy into the state. +// Lockup protection that reseeds the generator if it falls into the all-zero state. +// +// Refs: [1] D. Blackman and S. Vigna, Scrambled Linear Pseudorndom Number Generators +// https://arxiv.org/pdf/1805.01407.pdf +// [2] https://prng.di.unimi.it/ +// [3] https://en.wikipedia.org/wiki/Xorshift#xoshiro_and_xoroshiro + +`include "prim_assert.sv" + +module prim_xoshiro256pp #( + // Output width, must be a multiple of 64 + parameter int unsigned OutputDw = 64, + // PRNG reset state, must be nonzero! + parameter logic [255:0] DefaultSeed = 256'(1'b1), + + parameter int unsigned NumStages = OutputDw / 64 // derived parameter +) ( + input logic clk_i, + input logic rst_ni, + input logic seed_en_i, // load external seed into the state + input logic [255:0] seed_i, // external seed input + input logic xoshiro_en_i, // enables the PRNG + input logic [255:0] entropy_i, // additional entropy to be XOR'ed into the state + output logic [OutputDw-1:0] data_o, // PRNG output + output logic all_zero_o // alert signal indicates the all-zero state +); + + logic [255:0] unrolled_state [NumStages+1]; + logic [63:0] mid [NumStages]; + + logic lockup; + logic [255:0] xoshiro_d, xoshiro_q, next_xoshiro_state; + + function automatic logic [255:0] state_update (input logic [255:0] data_in); + logic [63:0] a_in, b_in, c_in, d_in; + logic [63:0] a_out, b_out, c_out, d_out; + a_in = data_in[255:192]; + b_in = data_in[191:128]; + c_in = data_in[127:64]; + d_in = data_in[63:0]; + a_out = a_in ^ b_in ^ d_in; + b_out = a_in ^ b_in ^ c_in; + c_out = a_in ^ (b_in << 17) ^ c_in; + d_out = {d_in[18:0], d_in[63:19]} ^ {b_in[18:0], b_in[63:19]}; + return {a_out, b_out, c_out, d_out}; + endfunction: state_update + + assign unrolled_state[0] = xoshiro_q; + + for (genvar k = 0; k < NumStages; k++) begin : gen_state_functions + // State update function + assign unrolled_state[k+1] = state_update(unrolled_state[k]); + // State output function + assign mid[k] = unrolled_state[k][255:192] + unrolled_state[k][63:0]; + assign data_o[(k+1)*64-1:k*64] = {mid[k][40:0], mid[k][63:41]} + unrolled_state[k][255:192]; + end + + assign next_xoshiro_state = entropy_i ^ unrolled_state[NumStages]; + assign xoshiro_d = (seed_en_i) ? seed_i : + (xoshiro_en_i && lockup) ? DefaultSeed : + (xoshiro_en_i) ? next_xoshiro_state : xoshiro_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_reg_state + if (!rst_ni) begin + xoshiro_q <= DefaultSeed; + end else begin + xoshiro_q <= xoshiro_d; + end + end + + // lockup condition is all-zero + assign lockup = ~(|xoshiro_q); + + // Indicate that the state is all zeros. + assign all_zero_o = lockup; + + // check that seed is not all-zero + `ASSERT_INIT(DefaultSeedNzCheck_A, |DefaultSeed) + +endmodule diff --git a/synth/prim/util/prim_crc32_table_gen.py b/synth/prim/util/prim_crc32_table_gen.py new file mode 100755 index 0000000..a978d44 --- /dev/null +++ b/synth/prim/util/prim_crc32_table_gen.py @@ -0,0 +1,42 @@ +#!/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 binascii + +function_header = ''' +// Generated using hw/ip/prim/util/prim_crc32_table_gen.py +function automatic logic [31:0] crc32_byte_calc(logic [7:0] b); + unique case (b)''' + +function_footer = ''' default: crc32_byte_calc = '0; + endcase +endfunction +''' + +function_table_line = " 8'h{:02x}: crc32_byte_calc = 32'h{:08x};" + + +def main(): + print(function_header) + # Loop through all bytes to calculate a lookup table. + for i in range(256): + # The CRC update function using a byte lookup table is: + # + # crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ b] + # + # Where b is the byte input and the initial CRC is 0xffffffff. So given + # input i, an inverted i (i ^ 0xff) is the input to the table giving + # `table_in`. binascii.crc32 inverts the crc before returning. The ^ + # 0xff000000 reverses this along with the (crc >> 8) that is XORed to + # the table output giving table_out. + table_in = i ^ 0xff + b = i.to_bytes(1, byteorder='little') + table_out = binascii.crc32(b) ^ 0xff000000 + print(function_table_line.format(table_in, table_out)) + print(function_footer) + + +if __name__ == '__main__': + main() diff --git a/synth/prim/util/primgen.py b/synth/prim/util/primgen.py new file mode 100755 index 0000000..8ea6827 --- /dev/null +++ b/synth/prim/util/primgen.py @@ -0,0 +1,497 @@ +#!/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 os +import re +import shutil +import sys + +import yaml +from mako.template import Template + +# Make vendored packages available in the search path. +sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor')) + +try: + from yaml import CSafeDumper as YamlDumper + from yaml import CSafeLoader as YamlLoader +except ImportError: + from yaml import SafeDumper as YamlDumper + from yaml import SafeLoader as YamlLoader + + +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_verible(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header using Verible + + Implementation of _parse_module_header() which uses verible-verilog-syntax + to do the parsing. This is the primary implementation and is used when + supported Verible version is available. + + See _parse_module_header() for API details. + """ + + from google_verible_verilog_syntax_py.verible_verilog_syntax import ( + PreOrderTreeIterator, VeribleVerilogSyntax) + + parser = VeribleVerilogSyntax() + + data = parser.parse_file(generic_impl_filepath, + options={"skip_null": True}) + if data.errors: + for err in data.errors: + print( + f'Verible: {err.phase} error in line {err.line} column {err.column}' + + (': {err.message}' if err.message else '.')) + # Intentionally not raising an exception here. + # There are chances that Verible recovered from errors. + if not data.tree: + raise ValueError(f"Unable to parse {generic_impl_filepath!r}.") + + module = data.tree.find({"tag": "kModuleDeclaration"}) + header = module.find({"tag": "kModuleHeader"}) + if not header: + raise ValueError("Unable to extract module header from %s." % + (generic_impl_filepath, )) + + name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}, + iter_=PreOrderTreeIterator) + if not name: + raise ValueError("Unable to extract module name from %s." % + (generic_impl_filepath, )) + + imports = header.find_all({"tag": "kPackageImportDeclaration"}) + + parameters_list = header.find({"tag": "kFormalParameterList"}) + parameters = set() + if parameters_list: + for parameter in sorted( + parameters_list.iter_find_all({"tag": "kParamDeclaration"})): + if parameter.find({"tag": "parameter"}): + parameter_id = parameter.find( + {"tag": ["SymbolIdentifier", "EscapedIdentifier"]}) + parameters.add(parameter_id.text) + + ports = header.find({"tag": "kPortDeclarationList"}) + + return { + 'module_header': header.text, + 'package_import_declaration': '\n'.join([i.text for i in imports]), + 'parameter_port_list': parameters_list.text if parameters_list else '', + 'ports': ports.text if ports else '', + 'parameters': parameters, + 'parser': 'Verible' + } + + +def _parse_module_header_fallback(generic_impl_filepath, module_name): + """ Parse a SystemVerilog file to extract the 'module' header using RegExp + + Legacy implementation of _parse_module_header() using regular expressions. + It is not as robust as Verible-backed implementation, but doesn't need + Verible to work. + + See _parse_module_header() for API details. + """ + + # 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 + # package_import_declaration (optional, skipped) + r'\s*(?P(?:import\s+[^;]+;)+)?' + 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), + 'parser': + 'Fallback (regex)' + } + + +def test_parse_parameter_port_list(): + assert _parse_parameter_port_list("parameter enum_t P") == {'P'} + assert _parse_parameter_port_list("parameter integer P") == {'P'} + assert _parse_parameter_port_list("parameter logic [W-1:0] P") == {'P'} + assert _parse_parameter_port_list("parameter logic [W-1:0] P = '0") == { + 'P' + } + assert _parse_parameter_port_list("parameter logic [W-1:0] P = 'b0") == { + 'P' + } + assert _parse_parameter_port_list("parameter logic [W-1:0] P = 2'd0") == { + 'P' + } + + +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 list(sorted(parameters)) + + +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). + - parser: parser used to extract the data. + """ + + try: + return _parse_module_header_verible(generic_impl_filepath, module_name) + except Exception as e: + print(e) + print("Verible parser failed, using regex fallback instead.") + return _parse_module_header_fallback(generic_impl_filepath, + module_name) + + +def _check_gapi(gapi): + if 'cores' not in gapi: + print("Key 'cores' not found in GAPI structure. " + "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): + if not parameters: + s = " prim_{techlib}_{prim_name} u_impl_{techlib} (\n" + else: + s = " prim_{techlib}_{prim_name} #(\n" + s += ",\n".join(" .{p}({p})".format(p=p) for p in parameters) + s += "\n ) u_impl_{techlib} (\n" + s += " .*\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 """ + + # Sort list of technology libraries to produce a stable ordering in the + # generated wrapper. + techlibs_wo_generic = sorted( + [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 'generic' not 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'], + num_techlibs=len(techlibs), + # 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']), + parser_info=generic_hdr['parser']) + 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", Dumper=YamlDumper) + 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] + with open(gapi_filepath) as f: + gapi = yaml.load(f, 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/synth/prim/util/primgen/abstract_prim.sv.tpl b/synth/prim/util/primgen/abstract_prim.sv.tpl new file mode 100755 index 0000000..46f9911 --- /dev/null +++ b/synth/prim/util/primgen/abstract_prim.sv.tpl @@ -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 + +// This file is auto-generated. +// Used parser: ${parser_info} + +% if num_techlibs > 1: +`ifndef PRIM_DEFAULT_IMPL + `define PRIM_DEFAULT_IMPL prim_pkg::ImplGeneric +`endif +% endif + +// This is to prevent AscentLint warnings in the generated +// abstract prim wrapper. These warnings occur due to the .* +// use. TODO: we may want to move these inline waivers +// into a separate, generated waiver file for consistency. +//ri lint_check_off OUTPUT_NOT_DRIVEN INPUT_NOT_READ HIER_BRANCH_NOT_READ +module prim_${prim_name} +${module_header_imports} +#( +${module_header_params} +) ( + ${module_header_ports} +); +% if num_techlibs > 1: + localparam prim_pkg::impl_e Impl = `PRIM_DEFAULT_IMPL; +% endif + +${instances} + +endmodule +//ri lint_check_on OUTPUT_NOT_DRIVEN INPUT_NOT_READ HIER_BRANCH_NOT_READ diff --git a/synth/prim/util/primgen/prim_pkg.core.tpl b/synth/prim/util/primgen/prim_pkg.core.tpl new file mode 100755 index 0000000..4748315 --- /dev/null +++ b/synth/prim/util/primgen/prim_pkg.core.tpl @@ -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_abstract:prim_pkg:0.1" +description: "Constants used by the primitives" + +filesets: + files_rtl: + files: + - prim_pkg.sv + file_type: systemVerilogSource + +targets: + default: + filesets: + - files_rtl diff --git a/synth/prim/util/primgen/prim_pkg.sv.tpl b/synth/prim/util/primgen/prim_pkg.sv.tpl new file mode 100755 index 0000000..7974e5a --- /dev/null +++ b/synth/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/synth/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson b/synth/prim/util/vendor/google_verible_verilog_syntax_py.lock.hjson new file mode 100755 index 0000000..dc47172 --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py.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/google/verible.git + rev: ed1f3a9577047b801854fb36dd14ebe0aecbdddc + only_subdir: verilog/tools/syntax/export_json_examples + } +} diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson b/synth/prim/util/vendor/google_verible_verilog_syntax_py.vendor.hjson new file mode 100755 index 0000000..0aeafca --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py.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: "google_verible_verilog_syntax_py", + target_dir: "google_verible_verilog_syntax_py", + + upstream: { + url: "https://github.com/google/verible.git", + rev: "v0.0-943-ged1f3a9", + only_subdir: "verilog/tools/syntax/export_json_examples", + }, +} diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py/BUILD b/synth/prim/util/vendor/google_verible_verilog_syntax_py/BUILD new file mode 100755 index 0000000..85da887 --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py/BUILD @@ -0,0 +1,55 @@ +load("@rules_python//python:defs.bzl", "py_test", "py_library", "py_binary") + +licenses(["notice"]) + +py_library( + name = "verible_verilog_syntax_py", + srcs = ["verible_verilog_syntax.py"], + srcs_version = "PY3", + imports = ["."], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + deps = [ + "@python_anytree//:anytree", + "//third_party/py/dataclasses", + ], +) + +py_test( + name = "verible_verilog_syntax_py_test", + size = "small", + srcs = ["verible_verilog_syntax_test.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "verible_verilog_syntax_test.py", + deps = [":verible_verilog_syntax_py"], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) + +py_binary( + name = "print_modules", + srcs = ["print_modules.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "print_modules.py", + deps = [ + ":verible_verilog_syntax_py", + "@python_anytree//:anytree", + ], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) + +py_binary( + name = "print_tree", + srcs = ["print_tree.py"], + srcs_version = "PY3", + python_version = "PY3", + main = "print_tree.py", + deps = [ + ":verible_verilog_syntax_py", + "@python_anytree//:anytree", + ], + data = ["//verilog/tools/syntax:verible-verilog-syntax"], + args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"], +) diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py b/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py new file mode 100755 index 0000000..2974e66 --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_modules.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Print module name, ports, parameters and imports. + +Usage: print_modules.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\ + VERILOG_FILE [VERILOG_FILE [...]] + +This example shows how to use ``verible-verilog-syntax --export_json ...`` +output to extract information about module declarations found in System Verilog +source files. Extracted information: + +* module name +* module port names +* module parameter names +* module imports +* module header code +""" + +import sys + +import anytree + +import verible_verilog_syntax + + +def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData): + """Print information about modules found in SystemVerilog file. + + This function uses verible_verilog_syntax.Node methods to find module + declarations and specific tokens containing following information: + + * module name + * module port names + * module parameter names + * module imports + * module header code + + Args: + path: Path to source file (used only for informational purposes) + data: Parsing results returned by one of VeribleVerilogSyntax' parse_* + methods. + """ + if not data.tree: + return + + modules_info = [] + + # Collect information about each module declaration in the file + for module in data.tree.iter_find_all({"tag": "kModuleDeclaration"}): + module_info = { + "header_text": "", + "name": "", + "ports": [], + "parameters": [], + "imports": [], + } + + # Find module header + header = module.find({"tag": "kModuleHeader"}) + if not header: + continue + module_info["header_text"] = header.text + + # Find module name + name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}, + iter_=anytree.PreOrderIter) + if not name: + continue + module_info["name"] = name.text + + # Get the list of ports + for port in header.iter_find_all({"tag": ["kPortDeclaration", "kPort"]}): + port_id = port.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}) + module_info["ports"].append(port_id.text) + + # Get the list of parameters + for param in header.iter_find_all({"tag": ["kParamDeclaration"]}): + param_id = param.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]}) + module_info["parameters"].append(param_id.text) + + # Get the list of imports + for pkg in module.iter_find_all({"tag": ["kPackageImportItem"]}): + module_info["imports"].append(pkg.text) + + modules_info.append(module_info) + + # Print results + if len(modules_info) > 0: + print(f"\033[1;97;7m{path} \033[0m\n") + + def print_entry(key, values): + fmt_values = [f"\033[92m{v}\033[0m" for v in values] + value_part = (f"\n\033[33m// {' '*len(key)}".join(fmt_values) + or "\033[90m-\033[0m") + print(f"\033[33m// \033[93m{key}{value_part}") + + for module_info in modules_info: + print_entry("name: ", [module_info["name"]]) + print_entry("ports: ", module_info["ports"]) + print_entry("parameters: ", module_info["parameters"]) + print_entry("imports: ", module_info["imports"]) + print(f"\033[97m{module_info['header_text']}\033[0m\n") + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " + + "VERILOG_FILE [VERILOG_FILE [...]]") + return 1 + + parser_path = sys.argv[1] + files = sys.argv[2:] + + parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path) + data = parser.parse_files(files) + + for file_path, file_data in data.items(): + process_file_data(file_path, file_data) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py b/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py new file mode 100755 index 0000000..057c6da --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py/print_tree.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Pretty-print Verilog Concrete Syntax Tree + +Usage: print_tree.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\ + VERILOG_FILE [VERILOG_FILE [...]] + +Visualizes tree generated by ``verible-verilog-syntax --export_json ...``. +Values enclosed in ``[]`` are node tags. ``@(S-E)`` marks token's start (S) +and end (E) byte offsets in source code. When a token's text in source code +is not the same as its tag, the text is printed in single quotes. +""" + +import sys + +import anytree + +import verible_verilog_syntax + + +def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData): + """Print tree representation to the console. + + The function uses anytree module (which is a base of a tree implementation + used in verible_verilog_syntax) method to print syntax tree representation + to the console. + + Args: + path: Path to source file (used only for informational purposes) + data: Parsing results returned by one of VeribleVerilogSyntax' parse_* + methods. + """ + print(f"\033[1;97;7m{path} \033[0m\n") + if data.tree: + for prefix, _, node in anytree.RenderTree(data.tree): + print(f"\033[90m{prefix}\033[0m{node.to_formatted_string()}") + print() + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " + + "VERILOG_FILE [VERILOG_FILE [...]]") + return 1 + + parser_path = sys.argv[1] + files = sys.argv[2:] + + parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path) + data = parser.parse_files(files, options={"gen_tree": True}) + + for file_path, file_data in data.items(): + process_file_data(file_path, file_data) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py b/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py new file mode 100755 index 0000000..f7cb4f0 --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py @@ -0,0 +1,531 @@ +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Wrapper for ``verible-verilog-syntax --export_json``""" + +import collections +import json +import re +import subprocess +from typing import Any, Callable, Dict, Iterable, List, Optional, Union + +import anytree +import dataclasses + +_CSI_SEQUENCE = re.compile("\033\\[.*?m") + + +def _colorize(formats: List[str], strings: List[str]) -> str: + result = "" + fi = 0 + for s in strings: + result += f"\033[{formats[fi]}m{s}\033[0m" + fi = (fi+1) % len(formats) + return result + + +# Type aliases + +CallableFilter = Callable[["Node"], bool] +KeyValueFilter = Dict[str, Union[str, List[str]]] +TreeIterator = Union["_TreeIteratorBase", anytree.iterators.AbstractIter] + + +# Custom tree iterators with an option for reverse children iteration + +class _TreeIteratorBase: + def __init__(self, tree: "Node", + filter_: Optional[CallableFilter] = None, + reverse_children: bool = False): + self.tree = tree + self.reverse_children = reverse_children + self.filter_ = filter_ if filter_ else lambda n: True + + def __iter__(self) -> Iterable["Node"]: + yield from self._iter_tree(self.tree) + + def _iter_children(self, tree: Optional["Node"]) -> Iterable["Node"]: + if not tree or not hasattr(tree, "children"): + return [] + return tree.children if not self.reverse_children \ + else reversed(tree.children) + + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + raise NotImplementedError("Subclass must implement '_iter_tree' method") + + +class PreOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + if self.filter_(tree): + yield tree + for child in self._iter_children(tree): + yield from self._iter_tree(child) + + +class PostOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + for child in self._iter_children(tree): + yield from self._iter_tree(child) + if self.filter_(tree): + yield tree + + +class LevelOrderTreeIterator(_TreeIteratorBase): + def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]: + queue = collections.deque([tree]) + while len(queue) > 0: + n = queue.popleft() + if self.filter_(n): + yield n + queue.extend(self._iter_children(n)) + + +class Node(anytree.NodeMixin): + """Base VeribleVerilogSyntax syntax tree node. + + Attributes: + parent (Optional[Node]): Parent node. + """ + def __init__(self, parent: Optional["Node"] = None): + self.parent = parent + + @property + def syntax_data(self) -> Optional["SyntaxData"]: + """Parent SyntaxData""" + return self.parent.syntax_data if self.parent else None + + @property + def start(self) -> Optional[int]: + """Byte offset of node's first character in source text""" + raise NotImplementedError("Subclass must implement 'start' property") + + @property + def end(self) -> Optional[int]: + """Byte offset of a character just past the node in source text.""" + raise NotImplementedError("Subclass must implement 'end' property") + + @property + def text(self) -> str: + """Source code fragment spanning all tokens in a node.""" + start = self.start + end = self.end + sd = self.syntax_data + if ((start is not None) and (end is not None) and sd and sd.source_code + and end <= len(sd.source_code)): + return sd.source_code[start:end].decode("utf-8") + return "" + + def __repr__(self) -> str: + return _CSI_SEQUENCE.sub("", self.to_formatted_string()) + + def to_formatted_string(self) -> str: + """Print node representation formatted for printing in terminal.""" + return super().__repr__() + + +class BranchNode(Node): + """Syntax tree branch node + + Attributes: + tag (str): Node tag. + children (Optional[Node]): Child nodes. + """ + def __init__(self, tag: str, parent: Optional[Node] = None, + children: Optional[List[Node]] = None): + super().__init__(parent) + self.tag = tag + self.children = children if children is not None else [] + + @property + def start(self) -> Optional[int]: + first_token = self.find(lambda n: isinstance(n, TokenNode), + iter_=PostOrderTreeIterator) + return first_token.start if first_token else None + + @property + def end(self) -> Optional[int]: + last_token = self.find(lambda n: isinstance(n, TokenNode), + iter_=PostOrderTreeIterator, reverse_children=True) + return last_token.end if last_token else None + + def iter_find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None], + max_count: int = 0, + iter_: TreeIterator = LevelOrderTreeIterator, + **kwargs) -> Iterable[Node]: + """Iterate all nodes matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + nodes. + * Dict mapping Node attribute names to searched value or list of + searched values. + max_count: Stop searching after finding that many matching nodes. + iter_: Tree iterator. Decides in what order nodes are visited. + + Yields: + Nodes matching specified filter. + """ + def as_list(v): + return v if isinstance(v, list) else [v] + + if filter_ and not callable(filter_): + filters = filter_ + def f(node): + for attr,value in filters.items(): + if not hasattr(node, attr): + return False + if getattr(node, attr) not in as_list(value): + return False + return True + filter_ = f + + for node in iter_(self, filter_, **kwargs): + yield node + max_count -= 1 + if max_count == 0: + break + + def find(self, filter_: Union[CallableFilter, KeyValueFilter, None], + iter_: TreeIterator = LevelOrderTreeIterator, **kwargs) \ + -> Optional[Node]: + """Find node matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + node. + * Dict mapping Node attribute names to searched value or list of + searched values. + iter_: Tree iterator. Decides in what order nodes are visited. + + Returns: + First Node matching filter. + """ + return next(self.iter_find_all(filter_, max_count=1, iter_=iter_, + **kwargs), None) + + def find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None], + max_count: int = 0, iter_: TreeIterator = LevelOrderTreeIterator, + **kwargs) -> List[Node]: + """Find all nodes matching specified filter. + + Args: + filter_: Describes what to search for. Might be: + * Callable taking Node as an argument and returning True for accepted + nodes. + * Dict mapping Node attribute names to searched value or list of + searched values. + max_count: Stop searching after finding that many matching nodes. + iter_: Tree iterator. Decides in what order nodes are visited. + + Returns: + List of nodes matching specified filter. + """ + return list(self.iter_find_all(filter_, max_count=max_count, iter_=iter_, + **kwargs)) + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + return _colorize(["37", "1;97"], ["[", tag, "]"]) + + +class RootNode(BranchNode): + """Syntax tree root node.""" + def __init__(self, tag: str, syntax_data: Optional["SyntaxData"] = None, + children: Optional[List[Node]] = None): + super().__init__(tag, None, children) + self._syntax_data = syntax_data + + @property + def syntax_data(self) -> Optional["SyntaxData"]: + return self._syntax_data + + +class LeafNode(Node): + """Syntax tree leaf node. + + This specific class is used for null nodes. + """ + @property + def start(self) -> None: + """Byte offset of token's first character in source text""" + return None + + @property + def end(self) -> None: + """Byte offset of a character just past the token in source text.""" + return None + + def to_formatted_string(self) -> str: + return _colorize(["90"], ["null"]) + + +class TokenNode(LeafNode): + """Tree node with token data + + Represents single token in a syntax tree. + + Attributes: + tag (str): Token tag. + """ + + def __init__(self, tag: str, start: int, end: int, + parent: Optional[Node] = None): + super().__init__(parent) + self.tag = tag + self._start = start + self._end = end + + @property + def start(self) -> int: + return self._start + + @property + def end(self) -> int: + return self._end + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + parts = [ + _colorize(["37", "1;97"], ["[", tag, "]"]), + _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]), + ] + text = self.text + if self.tag != text: + parts.append(_colorize(["32", "92"], ["'", repr(text)[1:-1], "'"])) + return " ".join(parts) + + +class Token: + """Token data + + Represents single token in tokens and rawtokens lists. + + Attributes: + tag (str): Token tag. + start (int): Byte offset of token's first character in source text. + end (int): Byte offset of a character just past the token in source text. + syntax_data (Optional["SyntaxData"]): Parent SyntaxData. + """ + + def __init__(self, tag: str, start: int, end: int, + syntax_data: Optional["SyntaxData"] = None): + self.tag = tag + self.start = start + self.end = end + self.syntax_data = syntax_data + + @property + def text(self) -> str: + """Token text in source code.""" + sd = self.syntax_data + if sd and sd.source_code and self.end <= len(sd.source_code): + return sd.source_code[self.start:self.end].decode("utf-8") + return "" + + def __repr__(self) -> str: + return _CSI_SEQUENCE.sub("", self.to_formatted_string()) + + def to_formatted_string(self) -> str: + tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag) + parts = [ + _colorize(["37", "1;97"], ["[", tag, "]"]), + _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]), + _colorize(["32", "92"], ["'", repr(self.text)[1:-1], "'"]), + ] + return " ".join(parts) + + +@dataclasses.dataclass +class Error: + line: int + column: int + phase: str + message: str = "" + + +@dataclasses.dataclass +class SyntaxData: + source_code: Optional[str] = None + tree: Optional[RootNode] = None + tokens: Optional[List[Token]] = None + rawtokens: Optional[List[Token]] = None + errors: Optional[List[Error]] = None + + +class VeribleVerilogSyntax: + """``verible-verilog-syntax`` wrapper. + + This class provides methods for running ``verible-verilog-syntax`` and + transforming its output into Python data structures. + + Args: + executable: path to ``verible-verilog-syntax`` binary. + """ + + def __init__(self, executable: str = "verible-verilog-syntax"): + self.executable = executable + + @staticmethod + def _transform_tree(tree, data: SyntaxData, skip_null: bool) -> RootNode: + def transform(tree): + if tree is None: + return None + if "children" in tree: + children = [ + transform(child) or LeafNode() + for child in tree["children"] + if not (skip_null and child is None) + ] + tag = tree["tag"] + return BranchNode(tag, children=children) + tag = tree["tag"] + start = tree["start"] + end = tree["end"] + return TokenNode(tag, start, end) + + if "children" not in tree: + return None + + children = [ + transform(child) or LeafNode() + for child in tree["children"] + if not (skip_null and child is None) + ] + tag = tree["tag"] + return RootNode(tag, syntax_data=data, children=children) + + + @staticmethod + def _transform_tokens(tokens, data: SyntaxData) -> List[Token]: + return [Token(t["tag"], t["start"], t["end"], data) for t in tokens] + + + @staticmethod + def _transform_errors(tokens) -> List[Error]: + return [Error(t["line"], t["column"], t["phase"], t.get("message", None)) + for t in tokens] + + def _parse(self, paths: List[str], input_: str = None, + options: Dict[str, Any] = None) -> Dict[str, SyntaxData]: + """Common implementation of parse_* methods""" + options = { + "gen_tree": True, + "skip_null": False, + "gen_tokens": False, + "gen_rawtokens": False, + **(options or {}), + } + + args = ["-export_json"] + if options["gen_tree"]: + args.append("-printtree") + if options["gen_tokens"]: + args.append("-printtokens") + if options["gen_rawtokens"]: + args.append("-printrawtokens") + + proc = subprocess.run([self.executable, *args , *paths], + stdout=subprocess.PIPE, + input=input_, + encoding="utf-8", + check=False) + + json_data = json.loads(proc.stdout) + data = {} + for file_path, file_json in json_data.items(): + file_data = SyntaxData() + + if file_path == "-": + file_data.source_code = input_.encode("utf-8") + else: + with open(file_path, "rb") as f: + file_data.source_code = f.read() + + if "tree" in file_json: + file_data.tree = VeribleVerilogSyntax._transform_tree( + file_json["tree"], file_data, options["skip_null"]) + + if "tokens" in file_json: + file_data.tokens = VeribleVerilogSyntax._transform_tokens( + file_json["tokens"], file_data) + + if "rawtokens" in file_json: + file_data.rawtokens = VeribleVerilogSyntax._transform_tokens( + file_json["rawtokens"], file_data) + + if "errors" in file_json: + file_data.errors = VeribleVerilogSyntax._transform_errors( + file_json["errors"]) + + data[file_path] = file_data + + return data + + def parse_files(self, paths: List[str], options: Dict[str, Any] = None) \ + -> Dict[str, SyntaxData]: + """Parse multiple SystemVerilog files. + + Args: + paths: list of paths to files to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + A dict that maps file names to their parsing results in SyntaxData object. + """ + return self._parse(paths, options = options) + + def parse_file(self, path: str, options: Dict[str, Any] = None) \ + -> Optional[SyntaxData]: + """Parse single SystemVerilog file. + + Args: + path: path to a file to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + Parsing results in SyntaxData object. + """ + return self._parse([path], options = options).get(path, None) + + def parse_string(self, string: str, options: Dict[str, Any] = None) \ + -> Optional[SyntaxData]: + """Parse a string with SystemVerilog code. + + Args: + string: SystemVerilog code to parse. + options: dict with parsing options. + Available options: + gen_tree (boolean): whether to generate syntax tree. + skip_null (boolean): null nodes won't be stored in a tree if True. + gen_tokens (boolean): whether to generate tokens list. + gen_rawtokens (boolean): whether to generate raw token list. + By default only ``gen_tree`` is True. + + Returns: + Parsing results in SyntaxData object. + """ + return self._parse(["-"], input_=string, options=options).get("-", None) diff --git a/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py b/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py new file mode 100755 index 0000000..c183bee --- /dev/null +++ b/synth/prim/util/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# Copyright 2017-2020 The Verible Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""VeribleVerilogSyntax test""" + + +import sys +import tempfile +import unittest + +import verible_verilog_syntax + + +class VeribleVerilogSyntaxTest(unittest.TestCase): + def setUp(self): + if len(sys.argv) > 1: + self.parser = verible_verilog_syntax.VeribleVerilogSyntax( + executable=sys.argv[1]) + else: + self.parser = verible_verilog_syntax.VeribleVerilogSyntax() + + self.assertIsNotNone(self.parser) + + def create_temp_file(self, content): + tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".sv") + tmp.write(content) + tmp.flush() + return tmp + + +class TestParseMethods(VeribleVerilogSyntaxTest): + def test_parse_string(self): + data = self.parser.parse_string("module X(); endmodule;") + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNone(data.errors) + + def test_parse_string_with_all_options(self): + data = self.parser.parse_string("module X(); endmodule;", options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNotNone(data.tokens) + self.assertIsNotNone(data.rawtokens) + self.assertIsNone(data.errors) + + def test_parse_string_error(self): + data = self.parser.parse_string("endmodule X(); module;") + self.assertIsNotNone(data) + self.assertIsNotNone(data.errors) + + def test_parse_file(self): + file_ = self.create_temp_file("module X(); endmodule;") + data = self.parser.parse_file(file_.name) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNone(data.errors) + + def test_parse_file_with_all_options(self): + file_ = self.create_temp_file("module X(); endmodule;") + data = self.parser.parse_file(file_.name, options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.assertIsNotNone(data.tokens) + self.assertIsNotNone(data.rawtokens) + self.assertIsNone(data.errors) + + def test_parse_file_error(self): + file_ = self.create_temp_file("endmodule X(); module;") + data = self.parser.parse_file(file_.name) + self.assertIsNotNone(data) + self.assertIsNotNone(data.errors) + + def test_parse_files(self): + files = [ + self.create_temp_file("module X(); endmodule;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files]) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + self.assertIsNotNone(data[f.name].tree) + self.assertIsNone(data[f.name].errors) + + def test_parse_files_with_all_options(self): + files = [ + self.create_temp_file("module X(); endmodule;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files], options={ + "gen_tree": True, + "gen_tokens": True, + "gen_rawtokens": True, + }) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + self.assertIsNotNone(data[f.name].tree) + self.assertIsNotNone(data[f.name].tokens) + self.assertIsNotNone(data[f.name].rawtokens) + self.assertIsNone(data[f.name].errors) + + def test_parse_files_error(self): + # One file with, and one without errors + files = [ + self.create_temp_file("endmodule X(); module;"), + self.create_temp_file("module Y(); endmodule;"), + ] + data = self.parser.parse_files([f.name for f in files]) + self.assertIsNotNone(data) + for f in files: + self.assertIsNotNone(data[f.name]) + + self.assertIsNotNone(data[files[0].name].errors) + self.assertIsNone(data[files[1].name].errors) + + +class TestTree(VeribleVerilogSyntaxTest): + def setUp(self): + super().setUp() + data = self.parser.parse_string( + "module ModuleName#(parameter PARAM_NAME=42)\n" + + " (input portIn, output portOut);\n" + + " import import_pkg_name::*;\n" + + " wire wireName;\n" + + "endmodule;\n" + + "module OtherModule; endmodule;\n") + self.assertIsNotNone(data) + self.assertIsNotNone(data.tree) + self.tree = data.tree + + def test_find(self): + header = self.tree.find({"tag": "kModuleHeader"}) + self.assertIsNotNone(header) + module_name = header.find({"tag": "SymbolIdentifier"}) + self.assertIsNotNone(module_name) + self.assertEqual(module_name.text, "ModuleName") + nonexistent = header.find({"tag": "SomeUndefinedTag"}) + self.assertIsNone(nonexistent) + + def test_find_all(self): + headers = self.tree.find_all({"tag": "kModuleHeader"}) + self.assertEqual(len(headers), 2) + + identifiers = self.tree.find_all({"tag": "SymbolIdentifier"}) + self.assertEqual(len(identifiers), 7) + + some_identifiers = self.tree.find_all({"tag": "SymbolIdentifier"}, + max_count=4) + self.assertEqual(len(some_identifiers), 4) + + def test_iter_find_all(self): + identifiers = [n.text + for n + in self.tree.iter_find_all({"tag": "SymbolIdentifier"})] + self.assertIn("ModuleName", identifiers) + self.assertIn("PARAM_NAME", identifiers) + self.assertIn("OtherModule", identifiers) + + def test_custom_filter(self): + def tokens_past_135_byte(node): + return (isinstance(node, verible_verilog_syntax.TokenNode) + and node.start > 135) + + other_module_tokens = self.tree.find_all(tokens_past_135_byte) + self.assertGreaterEqual(len(other_module_tokens), 5) + for token in other_module_tokens: + self.assertGreater(token.start, 135) + + def test_search_order(self): + level_order = self.tree.find_all({"tag": "SymbolIdentifier"}) + depth_order = self.tree.find_all({"tag": "SymbolIdentifier"}, + iter_=verible_verilog_syntax.PreOrderTreeIterator) + + def check_items_order(iterable, items_to_check): + index = 0 + for item in iterable: + if items_to_check[index] == item: + index += 1 + if index == len(items_to_check): + return True + return False + + self.assertTrue(check_items_order([n.text for n in level_order], + ["ModuleName", "OtherModule", "portIn"])) + self.assertTrue(check_items_order([n.text for n in depth_order], + ["ModuleName", "portIn", "OtherModule"])) + + def test_node_properties(self): + header = self.tree.find({"tag": "kModuleHeader"}) + self.assertIsNotNone(header) + + module_kw = header.find({"tag": "module"}) + self.assertEqual(module_kw.text, "module") + self.assertEqual(module_kw.start, 0) + self.assertEqual(module_kw.end, 6) + + semicolon = header.find({"tag": ";"}) + self.assertEqual(semicolon.text, ";") + self.assertEqual(semicolon.start, 78) + self.assertEqual(semicolon.end, 79) + + self.assertEqual(header.start, module_kw.start) + self.assertEqual(header.end, semicolon.end) + self.assertTrue(header.text.startswith("module")) + self.assertTrue(header.text.endswith(");")) + + +class TestTokens(VeribleVerilogSyntaxTest): + def test_tokens(self): + data = self.parser.parse_string( + "module X(input portIn, output portOut); endmodule;", options={ + "gen_tree": False, + "gen_tokens": True, + }) + + self.assertIsNotNone(data) + self.assertIsNotNone(data.tokens) + + identifiers = [t for t in data.tokens if t.tag == "SymbolIdentifier"] + + module_name = identifiers[0] + self.assertEqual(module_name.text, "X") + self.assertEqual(module_name.start, 7) + self.assertEqual(module_name.end, 8) + + texts = [t.text for t in identifiers] + self.assertSequenceEqual(texts, ["X", "portIn", "portOut"]) + + + def test_rawtokens(self): + data = self.parser.parse_string( + "module X(input portIn, output portOut); endmodule;", options={ + "gen_tree": False, + "gen_rawtokens": True, + }) + + self.assertIsNotNone(data) + self.assertIsNotNone(data.rawtokens) + + identifiers = [t for t in data.rawtokens if t.tag == "SymbolIdentifier"] + + module_name = identifiers[0] + self.assertEqual(module_name.text, "X") + self.assertEqual(module_name.start, 7) + self.assertEqual(module_name.end, 8) + + texts = [t.text for t in identifiers] + self.assertSequenceEqual(texts, ["X", "portIn", "portOut"]) + + +if __name__ == "__main__": + unittest.main(argv=sys.argv[0:1], verbosity=2) diff --git a/synth/prim_generic/BUILD b/synth/prim_generic/BUILD new file mode 100755 index 0000000..c87fb86 --- /dev/null +++ b/synth/prim_generic/BUILD @@ -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 + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "all_files", + srcs = glob(["**"]) + [ + ], +) diff --git a/synth/prim_generic/lint/prim_generic_clock_buf.vlt b/synth/prim_generic/lint/prim_generic_clock_buf.vlt new file mode 100755 index 0000000..4408716 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_buf.vlt @@ -0,0 +1,8 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +`verilator_config + +lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_buf.sv" -match "Parameter is not used: 'NoFpgaBuf'" diff --git a/synth/prim_generic/lint/prim_generic_clock_buf.waiver b/synth/prim_generic/lint/prim_generic_clock_buf.waiver new file mode 100755 index 0000000..aad9fb1 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_buf.waiver @@ -0,0 +1,7 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# primitives: prim_generic_clock_buf +waive -rules PARAM_NOT_USED -location {prim_generic_clock_buf.sv} -regexp {Parameter '(NoFpgaBuf|RegionSel)' not used} \ + -comment "parameter unused but required to maintain uniform interface" diff --git a/synth/prim_generic/lint/prim_generic_clock_div.waiver b/synth/prim_generic/lint/prim_generic_clock_div.waiver new file mode 100755 index 0000000..ada8d2b --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_div.waiver @@ -0,0 +1,20 @@ +# 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_clock_div + +waive -rules CLOCK_EDGE -location {prim_generic_clock_div.sv} -msg {Falling edge of clock 'clk_i' used here, should use rising edge} \ + -comment "The clock switch signal is synchronized on negative edge to ensure it is away from any transition" + +waive -rules DUAL_EDGE_CLOCK -location {prim_generic_clock_div.sv} -regexp {.*} \ + -comment "The clock switch signal is synchronized on negative edge to ensure it is away from any transition" + +waive -rules CLOCK_MUX -location {prim_generic_clock_div.sv} -regexp {.*reaches a multiplexer here, used as a clock.*} \ + -comment "A mux is used during scan bypass, and for switching between div by 2 and div by 1 clocks" + +waive -rules CLOCK_USE -location {prim_generic_clock_div.sv} -regexp {'clk_i' is connected to 'prim_clock_mux2' port 'clk1_i', and used as a clock} \ + -comment "This clock mux usage is OK." + +waive -rules SAME_NAME_TYPE -location {prim_generic_clock_div.sv} -regexp {'ResetValue' is used as a parameter here, and as an enumeration value at} \ + -comment "Reused parameter name." diff --git a/synth/prim_generic/lint/prim_generic_clock_gating.vlt b/synth/prim_generic/lint/prim_generic_clock_gating.vlt new file mode 100755 index 0000000..c38cafc --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_gating.vlt @@ -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 +// + +`verilator_config + +lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_gating.sv" -match "Parameter is not used: 'NoFpgaGate'" +lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_gating.sv" -match "Parameter is not used: 'FpgaBufGlobal'" diff --git a/synth/prim_generic/lint/prim_generic_clock_gating.waiver b/synth/prim_generic/lint/prim_generic_clock_gating.waiver new file mode 100755 index 0000000..469cd42 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_gating.waiver @@ -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 +# +# 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" +waive -rules PARAM_NOT_USED -location {prim_generic_clock_gating.sv} -regexp {Parameter 'NoFpgaGate' not used} \ + -comment "parameter unused but required to maintain uniform interface" +waive -rules PARAM_NOT_USED -location {prim_generic_clock_gating.sv} -regexp {Parameter 'FpgaBufGlobal' not used} \ + -comment "parameter unused but required to maintain uniform interface" diff --git a/synth/prim_generic/lint/prim_generic_clock_mux2.vlt b/synth/prim_generic/lint/prim_generic_clock_mux2.vlt new file mode 100755 index 0000000..b7a50c7 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_mux2.vlt @@ -0,0 +1,8 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +`verilator_config + +lint_off -rule UNUSED -file "*/rtl/prim_generic_clock_mux2.sv" -match "Parameter is not used: 'NoFpgaBufG'" diff --git a/synth/prim_generic/lint/prim_generic_clock_mux2.waiver b/synth/prim_generic/lint/prim_generic_clock_mux2.waiver new file mode 100755 index 0000000..d69c7f7 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_clock_mux2.waiver @@ -0,0 +1,8 @@ +# 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 + +waive -rules PARAM_NOT_USED -location {prim_generic_clock_mux2.sv} -regexp {.*Parameter 'NoFpgaBufG' not used in.*} \ + -comment "This parameter serves no function in the generic model" diff --git a/synth/prim_generic/lint/prim_generic_flash.vlt b/synth/prim_generic/lint/prim_generic_flash.vlt new file mode 100755 index 0000000..544a7b7 --- /dev/null +++ b/synth/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/synth/prim_generic/lint/prim_generic_flash.waiver b/synth/prim_generic/lint/prim_generic_flash.waiver new file mode 100755 index 0000000..c9602d5 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_flash.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_flash + +# The prim generic module does not make use of the IO ports +waive -rules INOUT_AS_IN -location {prim_generic_flash.sv} \ + -regexp {Inout port 'flash_.*_io' has no driver} diff --git a/synth/prim_generic/lint/prim_generic_otp.vlt b/synth/prim_generic/lint/prim_generic_otp.vlt new file mode 100755 index 0000000..9e469b8 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_otp.vlt @@ -0,0 +1,10 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +`verilator_config + +// The generic OTP module doesn't use vendor-specific parameters +lint_off -rule UNUSED -file "*/rtl/prim_generic_otp.sv" -match "*VendorTestOffset*" +lint_off -rule UNUSED -file "*/rtl/prim_generic_otp.sv" -match "*VendorTestSize*" diff --git a/synth/prim_generic/lint/prim_generic_otp.waiver b/synth/prim_generic/lint/prim_generic_otp.waiver new file mode 100755 index 0000000..c85cee6 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_otp.waiver @@ -0,0 +1,16 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# + +waive -rules {CONST_FF} -location {prim_ram_1p_adv.sv} \ + -msg {Flip-flop 'rerror_q' is driven by constant zeros in module 'prim_ram_1p_adv' (Depth=1024,Width=22,EnableInputPipeline=1,EnableOutputPipeline=1)} \ + -comment "The read error bits are unused and hence set to zero." + +waive -rules {INOUT_AS_IN} -location {prim_generic_otp.sv} \ + -msg {Inout port 'ext_voltage_io' has no driver in module 'prim_generic_otp'} \ + -comment "This signal is not driven in the generic model." + +waive -rules {PARAM_NOT_USED} -location {prim_generic_otp.sv} \ + -regexp {Parameter '(VendorTestOffset|VendorTestSize)' not used in module 'prim_generic_otp'} \ + -comment "These two parameters are not used in the generic model." diff --git a/synth/prim_generic/lint/prim_generic_pad_wrapper.vlt b/synth/prim_generic/lint/prim_generic_pad_wrapper.vlt new file mode 100755 index 0000000..544a7b7 --- /dev/null +++ b/synth/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/synth/prim_generic/lint/prim_generic_pad_wrapper.waiver b/synth/prim_generic/lint/prim_generic_pad_wrapper.waiver new file mode 100755 index 0000000..f044586 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_pad_wrapper.waiver @@ -0,0 +1,27 @@ +# 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 TRI_DRIVER -regexp {'in_raw_o' is driven by a tristate driver} \ + -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*' is not read from} -location {prim_generic_pad_wrapper.sv} \ + -comment "Some IO attributes may not be implemented." +waive -rules Z_USE -regexp {Constant with 'Z literal value '1'bz' encountered} -location {prim_generic_pad_wrapper.sv} \ + -comment "This z assignment is correct." +waive -rules PARAM_NOT_USED -regexp {Parameter 'Variant' not used in module 'prim_generic_pad_wrapper'} -location {prim_generic_pad_wrapper.sv} \ + -comment "This parameter has been provisioned for later and is currently unused." +waive -rules PARAM_NOT_USED -regexp {Parameter 'ScanRole' not used in module 'prim_generic_pad_wrapper'} -location {prim_generic_pad_wrapper.sv} \ + -comment "This parameter has been provisioned for later and is currently unused." +waive -rules INPUT_NOT_READ -msg {Input port 'clk_scan_i' is not read from in module 'prim_generic_pad_wrapper'} \ + -comment "This clock is not read in RTL since it will be connected after synthesis during DFT insertion" diff --git a/synth/prim_generic/lint/prim_generic_ram_1p.vlt b/synth/prim_generic/lint/prim_generic_ram_1p.vlt new file mode 100755 index 0000000..544a7b7 --- /dev/null +++ b/synth/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/synth/prim_generic/lint/prim_generic_ram_1p.waiver b/synth/prim_generic/lint/prim_generic_ram_1p.waiver new file mode 100755 index 0000000..3a08d90 --- /dev/null +++ b/synth/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_1p + +waive -rules ALWAYS_SPEC -location {prim_generic_ram_1p.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 blackboxing causes above lint errors." +waive -rules IFDEF_CODE -location {prim_generic_ram_1p.sv} -regexp {Assignment to 'unused_cfg' contained within `ifndef} \ + -comment "Declaration of signal and assignment to it are in same `ifndef" diff --git a/synth/prim_generic/lint/prim_generic_ram_1r1w.vlt b/synth/prim_generic/lint/prim_generic_ram_1r1w.vlt new file mode 100755 index 0000000..700355a --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_ram_1r1w.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 +// + +`verilator_config diff --git a/synth/prim_generic/lint/prim_generic_ram_1r1w.waiver b/synth/prim_generic/lint/prim_generic_ram_1r1w.waiver new file mode 100755 index 0000000..0827cbe --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_ram_1r1w.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_1r1w + +waive -rules ALWAYS_SPEC -location {prim_generic_ram_1r1w.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_1r1w.sv.* is not read from in module 'prim_generic_ram_1r1w'} \ + -comment "Ascentlint blackboxes very deep RAMs to speed up runtime. This blackboxing causes above lint errors." +waive -rules IFDEF_CODE -location {prim_generic_ram_1r1w.sv} -regexp {Assignment to 'unused_cfg' contained within `ifndef} \ + -comment "Declaration of signal and assignment to it are in same `ifndef" diff --git a/synth/prim_generic/lint/prim_generic_ram_2p.vlt b/synth/prim_generic/lint/prim_generic_ram_2p.vlt new file mode 100755 index 0000000..2654984 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_ram_2p.vlt @@ -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 +// + +`verilator_config + +// That is the nature of a dual-port memory: both write ports can access the same storage simultaneously. +lint_off -rule MULTIDRIVEN -file "*/rtl/prim_generic_ram_2p.sv" -match "Signal has multiple driving blocks with different clocking: '*.mem'*" diff --git a/synth/prim_generic/lint/prim_generic_ram_2p.waiver b/synth/prim_generic/lint/prim_generic_ram_2p.waiver new file mode 100755 index 0000000..6457452 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_ram_2p.waiver @@ -0,0 +1,14 @@ +# 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_2p + +waive -rules MULTI_PROC_ASSIGN -location {prim_generic_ram_2p.sv} -regexp {Assignment to 'mem' 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_2p.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_2p.sv.* is not read from in module 'prim_generic_ram_2p'} \ + -comment "Ascentlint blackboxes very deep RAMs to speed up runtime. This blackboxing causes above lint errors." +waive -rules IFDEF_CODE -location {prim_generic_ram_2p.sv} -regexp {Assignment to 'unused_cfg' contained within `ifndef} \ + -comment "Declaration of signal and assignment to it are in same `ifndef" diff --git a/synth/prim_generic/lint/prim_generic_rom.vlt b/synth/prim_generic/lint/prim_generic_rom.vlt new file mode 100755 index 0000000..544a7b7 --- /dev/null +++ b/synth/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/synth/prim_generic/lint/prim_generic_rom.waiver b/synth/prim_generic/lint/prim_generic_rom.waiver new file mode 100755 index 0000000..9d0ded3 --- /dev/null +++ b/synth/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/synth/prim_generic/lint/prim_generic_usb_diff_rx.waiver b/synth/prim_generic/lint/prim_generic_usb_diff_rx.waiver new file mode 100755 index 0000000..dfc8626 --- /dev/null +++ b/synth/prim_generic/lint/prim_generic_usb_diff_rx.waiver @@ -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 +# +# waiver file for prim_generic_usb_diff_rx +# note that this code is NOT synthesizable and meant for sim only + +waive -rules TRI_DRIVER -regexp {'(input_pi|input_ni)' is driven by a tristate driver} -location {prim_generic_usb_diff_rx.sv} \ + -comment "This models the pullup behavior, hence the TRI driver." +waive -rules MULTI_DRIVEN -regexp {'(input_pi|input_ni)' has 2 drivers, also driven at} -location {prim_generic_usb_diff_rx.sv} \ + -comment "The simulation model has multiple drivers to emulate different IO terminations." +waive -rules DRIVE_STRENGTH -regexp {Drive strength '\(weak0,pull1\)' encountered on assignment to '(input_pi|input_ni)'} -location {prim_generic_usb_diff_rx.sv} \ + -comment "The simulation model uses driving strength attributes to emulate different IO terminations." diff --git a/synth/prim_generic/prim_generic_and2.core b/synth/prim_generic/prim_generic_and2.core new file mode 100755 index 0000000..2dbc79c --- /dev/null +++ b/synth/prim_generic/prim_generic_and2.core @@ -0,0 +1,39 @@ +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:and2" +description: "Generic 2-input and" +filesets: + files_rtl: + files: + - rtl/prim_generic_and2.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_buf.core b/synth/prim_generic/prim_generic_buf.core new file mode 100755 index 0000000..142bc8d --- /dev/null +++ b/synth/prim_generic/prim_generic_buf.core @@ -0,0 +1,39 @@ +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:buf" +description: "buffer" +filesets: + files_rtl: + files: + - rtl/prim_generic_buf.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_clock_buf.core b/synth/prim_generic/prim_generic_clock_buf.core new file mode 100755 index 0000000..04f6ed9 --- /dev/null +++ b/synth/prim_generic/prim_generic_clock_buf.core @@ -0,0 +1,41 @@ +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_buf" +description: "clock buffer" +filesets: + files_rtl: + files: + - rtl/prim_generic_clock_buf.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_buf.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_buf.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_clock_div.core b/synth/prim_generic/prim_generic_clock_div.core new file mode 100755 index 0000000..c509264 --- /dev/null +++ b/synth/prim_generic/prim_generic_clock_div.core @@ -0,0 +1,31 @@ +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_div" +description: "Generic clock divide" +filesets: + files_rtl: + depend: + - lowrisc:prim:prim_pkg + - lowrisc:prim:flop + - lowrisc:prim:clock_inv + - lowrisc:prim:clock_buf + files: + - rtl/prim_generic_clock_div.sv + file_type: systemVerilogSource + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_clock_div.waiver + file_type: waiver + +targets: + default: + filesets: + - tool_ascentlint ? (files_ascentlint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_clock_gating.core b/synth/prim_generic/prim_generic_clock_gating.core new file mode 100755 index 0000000..d083e86 --- /dev/null +++ b/synth/prim_generic/prim_generic_clock_gating.core @@ -0,0 +1,41 @@ +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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_clock_inv.core b/synth/prim_generic/prim_generic_clock_inv.core new file mode 100755 index 0000000..19920df --- /dev/null +++ b/synth/prim_generic/prim_generic_clock_inv.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:clock_inv" +description: "Clock inverter with scanmode bypass mux" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:clock_mux2 + files: + - rtl/prim_generic_clock_inv.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_generic_clock_inv.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + # - lint/prim_generic_clock_inv.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_clock_mux2.core b/synth/prim_generic/prim_generic_clock_mux2.core new file mode 100755 index 0000000..6e8ed69 --- /dev/null +++ b/synth/prim_generic/prim_generic_clock_mux2.core @@ -0,0 +1,43 @@ +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: + depend: + - lowrisc:prim:assert + 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_flash.core b/synth/prim_generic/prim_generic_flash.core new file mode 100755 index 0000000..29afdf8 --- /dev/null +++ b/synth/prim_generic/prim_generic_flash.core @@ -0,0 +1,49 @@ +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:ip:tlul + - lowrisc:prim:ram_1p + - "fileset_partner ? (partner:systems:ast_pkg)" + - "!fileset_partner ? (lowrisc:systems:ast_pkg)" + - lowrisc:ip:flash_ctrl_pkg + - lowrisc:ip:flash_ctrl_prim_reg_top + files: + - rtl/prim_generic_flash_bank.sv + - 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_flop.core b/synth/prim_generic/prim_generic_flop.core new file mode 100755 index 0000000..6209a7b --- /dev/null +++ b/synth/prim_generic/prim_generic_flop.core @@ -0,0 +1,39 @@ +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:flop" +description: "generic flop" +filesets: + files_rtl: + files: + - rtl/prim_generic_flop.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_flop_en.core b/synth/prim_generic/prim_generic_flop_en.core new file mode 100755 index 0000000..275e2a8 --- /dev/null +++ b/synth/prim_generic/prim_generic_flop_en.core @@ -0,0 +1,41 @@ +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:flop_en" +description: "generic enable flop" +filesets: + files_rtl: + depend: + - lowrisc:prim:sec_anchor + files: + - rtl/prim_generic_flop_en.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_otp.core b/synth/prim_generic/prim_generic_otp.core new file mode 100755 index 0000000..7c54827 --- /dev/null +++ b/synth/prim_generic/prim_generic_otp.core @@ -0,0 +1,43 @@ +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 + - lowrisc:prim:util + - lowrisc:prim:ram_1p_adv + - lowrisc:systems:ast_pkg + - lowrisc:prim:otp_pkg + - lowrisc:ip:otp_ctrl_prim_reg_top + 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/synth/prim_generic/prim_generic_pad_attr.core b/synth/prim_generic/prim_generic_pad_attr.core new file mode 100755 index 0000000..1cf1eaf --- /dev/null +++ b/synth/prim_generic/prim_generic_pad_attr.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:pad_attr" +description: "Technology-independent pad attribute WARL module (for sim only!)" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:pad_wrapper_pkg + files: + - rtl/prim_generic_pad_attr.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + #- lint/prim_generic_pad_attr.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + #- lint/prim_generic_pad_attr.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_pad_wrapper.core b/synth/prim_generic/prim_generic_pad_wrapper.core new file mode 100755 index 0000000..3a37482 --- /dev/null +++ b/synth/prim_generic/prim_generic_pad_wrapper.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:pad_wrapper" +description: "Technology-independent pad wrapper implementation (for sim only!)" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:pad_wrapper_pkg + 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_ram_1p.core b/synth/prim_generic/prim_generic_ram_1p.core new file mode 100755 index 0000000..1be782c --- /dev/null +++ b/synth/prim_generic/prim_generic_ram_1p.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:ram_1p" +description: "Single port RAM" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:ram_1p_pkg + - 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_ram_1r1w.core b/synth/prim_generic/prim_generic_ram_1r1w.core new file mode 100755 index 0000000..1aded35 --- /dev/null +++ b/synth/prim_generic/prim_generic_ram_1r1w.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:ram_1r1w" +description: "prim" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:ram_2p_pkg + - lowrisc:prim:util_memload + files: + - rtl/prim_generic_ram_1r1w.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_1r1w.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_ram_1r1w.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_ram_2p.core b/synth/prim_generic/prim_generic_ram_2p.core new file mode 100755 index 0000000..065962b --- /dev/null +++ b/synth/prim_generic/prim_generic_ram_2p.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:ram_2p" +description: "prim" +filesets: + files_rtl: + depend: + - lowrisc:prim:assert + - lowrisc:prim:ram_2p_pkg + - lowrisc:prim:util_memload + 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_rom.core b/synth/prim_generic/prim_generic_rom.core new file mode 100755 index 0000000..a8821e2 --- /dev/null +++ b/synth/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:rom_pkg + - 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 + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_usb_diff_rx.core b/synth/prim_generic/prim_generic_usb_diff_rx.core new file mode 100755 index 0000000..a633b67 --- /dev/null +++ b/synth/prim_generic/prim_generic_usb_diff_rx.core @@ -0,0 +1,41 @@ +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:usb_diff_rx" +description: "Generic differential USB receiver for emulation purposes" +filesets: + files_rtl: + files: + - rtl/prim_generic_usb_diff_rx.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + #- lint/prim_generic_usb_diff_rx.vlt + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + - lint/prim_generic_usb_diff_rx.waiver + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_xnor2.core b/synth/prim_generic/prim_generic_xnor2.core new file mode 100755 index 0000000..f7a9b25 --- /dev/null +++ b/synth/prim_generic/prim_generic_xnor2.core @@ -0,0 +1,39 @@ +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:xnor2" +description: "Generic 2-input xnor" +filesets: + files_rtl: + files: + - rtl/prim_generic_xnor2.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/prim_generic_xor2.core b/synth/prim_generic/prim_generic_xor2.core new file mode 100755 index 0000000..3fa56ce --- /dev/null +++ b/synth/prim_generic/prim_generic_xor2.core @@ -0,0 +1,39 @@ +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:xor2" +description: "Generic 2-input xor" +filesets: + files_rtl: + files: + - rtl/prim_generic_xor2.sv + file_type: systemVerilogSource + + files_verilator_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: vlt + + files_ascentlint_waiver: + depend: + # common waivers + - lowrisc:lint:common + files: + file_type: waiver + + files_veriblelint_waiver: + depend: + # common waivers + - lowrisc:lint:common + +targets: + default: + filesets: + - tool_verilator ? (files_verilator_waiver) + - tool_ascentlint ? (files_ascentlint_waiver) + - tool_veriblelint ? (files_veriblelint_waiver) + - files_rtl diff --git a/synth/prim_generic/rtl/prim_generic_and2.sv b/synth/prim_generic/rtl/prim_generic_and2.sv new file mode 100755 index 0000000..0b62bef --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_and2.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 + +`include "prim_assert.sv" + +module prim_generic_and2 #( + parameter int Width = 1 +) ( + input [Width-1:0] in0_i, + input [Width-1:0] in1_i, + output logic [Width-1:0] out_o +); + + assign out_o = in0_i & in1_i; + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_buf.sv b/synth/prim_generic/rtl/prim_generic_buf.sv new file mode 100755 index 0000000..1b2fbff --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_buf.sv @@ -0,0 +1,18 @@ +// 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_buf #( + parameter int Width = 1 +) ( + input [Width-1:0] in_i, + output logic [Width-1:0] out_o +); + + logic [Width-1:0] inv; + assign inv = ~in_i; + assign out_o = ~inv; + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_clock_buf.sv b/synth/prim_generic/rtl/prim_generic_clock_buf.sv new file mode 100755 index 0000000..a0e841a --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_clock_buf.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 + +`include "prim_assert.sv" + +module prim_generic_clock_buf #( + // Turning off these verilator lints because keeping these parameters makes it consistent with + // the IP in hw/ip/prim_xilinx/rtl/ . + /* verilator lint_off UNUSED */ + parameter bit NoFpgaBuf = 1'b0, // serves no function in generic + parameter bit RegionSel = 1'b0 // serves no function in generic + /* verilator lint_on UNUSED */ +) ( + input clk_i, + output logic clk_o +); + + logic inv; + assign inv = ~clk_i; + assign clk_o = ~inv; + +endmodule // prim_generic_clock_buf diff --git a/synth/prim_generic/rtl/prim_generic_clock_div.sv b/synth/prim_generic/rtl/prim_generic_clock_div.sv new file mode 100755 index 0000000..c092856 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_clock_div.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 + +`include "prim_assert.sv" + +module prim_generic_clock_div #( + parameter int unsigned Divisor = 2, + parameter logic ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input step_down_req_i, // step down divisor by 2x + output logic step_down_ack_o, // step down acknowledge + input test_en_i, + output logic clk_o +); + + + // Only even divide is supported at the moment + // For odd divide we need to introduce more parameters to control duty cycle + `ASSERT_INIT(DivEven_A, (Divisor % 2) == 0) + + // It is assumed the flops in this module are NOT on the scan-chain, as a result only + // the input values are guarded + logic step_down_req; + assign step_down_req = test_en_i ? '0 : step_down_req_i; + + logic clk_int; + logic clk_muxed; + + if (Divisor == 2) begin : gen_div2 + logic q_p, q_n; + + prim_flop # ( + .Width(1), + .ResetValue(ResetValue) + ) u_div2 ( + .clk_i, + .rst_ni, + .d_i(q_n), + .q_o(q_p) + ); + + prim_clock_inv # ( + .HasScanMode(1'b0) + ) u_inv ( + .clk_i(q_p), + .scanmode_i('0), + .clk_no(q_n) + ); + + logic step_down_nq; + always_ff @(negedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + step_down_nq <= 1'b0; + end else begin + step_down_nq <= step_down_req; + end + end + + // make sure selection point is away from both edges + prim_clock_mux2 #( + .NoFpgaBufG(1'b1) + ) u_step_down_mux ( + .clk0_i(q_p), + .clk1_i(clk_i), + .sel_i(step_down_nq), + .clk_o(clk_int) + ); + + assign step_down_ack_o = step_down_nq; + + end else begin : gen_div + + localparam int unsigned ToggleCnt = Divisor / 2; + localparam int unsigned CntWidth = $clog2(ToggleCnt); + logic [CntWidth-1:0] cnt; + logic [CntWidth-1:0] limit; + + assign limit = !step_down_req ? CntWidth'(ToggleCnt - 1) : + (ToggleCnt / 2) == 2 ? '0 : CntWidth'((ToggleCnt / 2) - 1); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cnt <= '0; + clk_int <= ResetValue; + end else if (cnt >= limit) begin + cnt <= '0; + clk_int <= ~clk_muxed; + end else begin + cnt <= cnt + 1'b1; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + step_down_ack_o <= 1'b0; + end else begin + step_down_ack_o <= step_down_req; + end + end + end + + // anchor points for constraints + prim_clock_mux2 #( + .NoFpgaBufG(1'b1) + ) u_clk_mux ( + .clk0_i(clk_int), + .clk1_i(clk_i), + .sel_i('0), + .clk_o(clk_muxed) + ); + + // Hookup point for OCC on divided clock. + prim_clock_buf u_clk_div_buf ( + .clk_i(clk_muxed), + .clk_o + ); + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_clock_gating.sv b/synth/prim_generic/rtl/prim_generic_clock_gating.sv new file mode 100755 index 0000000..d4b6456 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_clock_gating.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 +// +// Common Library: Clock Gating cell +// +// The logic assumes that en_i is synchronized (so the instantiation site might need to put a +// synchronizer before en_i). + +module prim_generic_clock_gating #( + parameter bit NoFpgaGate = 1'b0, // this parameter has no function in generic + parameter bit FpgaBufGlobal = 1'b1 // this parameter has no function in generic +) ( + input clk_i, + input en_i, + input test_en_i, + output logic clk_o +); + + logic en_latch /* verilator clock_enable */; + 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/synth/prim_generic/rtl/prim_generic_clock_inv.sv b/synth/prim_generic/rtl/prim_generic_clock_inv.sv new file mode 100755 index 0000000..31a439c --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_clock_inv.sv @@ -0,0 +1,32 @@ +// 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_generic_clock_inv #( + parameter bit HasScanMode = 1'b1, + parameter bit NoFpgaBufG = 1'b0 // only used in FPGA case +) ( + input clk_i, + input scanmode_i, + output logic clk_no // Inverted +); + + if (HasScanMode) begin : gen_scan + prim_clock_mux2 #( + .NoFpgaBufG(NoFpgaBufG) + ) i_dft_tck_mux ( + .clk0_i ( ~clk_i ), + .clk1_i ( clk_i ), // bypass the inverted clock for testing + .sel_i ( scanmode_i ), + .clk_o ( clk_no ) + ); + end else begin : gen_noscan + logic unused_scanmode; + assign unused_scanmode = scanmode_i; + assign clk_no = ~clk_i; + end + +endmodule : prim_generic_clock_inv diff --git a/synth/prim_generic/rtl/prim_generic_clock_mux2.sv b/synth/prim_generic/rtl/prim_generic_clock_mux2.sv new file mode 100755 index 0000000..143597d --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_clock_mux2.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 + +`include "prim_assert.sv" + +module prim_generic_clock_mux2 #( + parameter bit NoFpgaBufG = 1'b0 // this parameter serves no function in the generic model +) ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + + // We model the mux with logic operations for GTECH runs. + assign clk_o = (sel_i & clk1_i) | (~sel_i & clk0_i); + + // make sure sel is never X (including during reset) + // need to use ##1 as this could break with inverted clocks that + // start with a rising edge at the beginning of the simulation. + `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/synth/prim_generic/rtl/prim_generic_flash.sv b/synth/prim_generic/rtl/prim_generic_flash.sv new file mode 100755 index 0000000..df0dea3 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_flash.sv @@ -0,0 +1,146 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Overall flash wrapper +// + +module prim_generic_flash #( + parameter int NumBanks = 2, // number of banks + parameter int InfosPerBank = 1, // info pages per bank + parameter int InfoTypes = 1, // different info types + parameter int InfoTypesWidth = 1, // different info types + parameter int PagesPerBank = 256,// data pages per bank + parameter int WordsPerPage = 256,// words per page + parameter int DataWidth = 32, // bits per word + parameter int TestModeWidth = 2 +) ( + input clk_i, + input rst_ni, + input flash_phy_pkg::flash_phy_prim_flash_req_t [NumBanks-1:0] flash_req_i, + output flash_phy_pkg::flash_phy_prim_flash_rsp_t [NumBanks-1:0] flash_rsp_o, + output logic [flash_phy_pkg::ProgTypes-1:0] prog_type_avail_o, + output init_busy_o, + input tck_i, + input tdi_i, + input tms_i, + output logic tdo_o, + input prim_mubi_pkg::mubi4_t bist_enable_i, + input prim_mubi_pkg::mubi4_t scanmode_i, + input scan_en_i, + input scan_rst_ni, + input flash_power_ready_h_i, + input flash_power_down_h_i, + inout [TestModeWidth-1:0] flash_test_mode_a_io, + inout flash_test_voltage_h_io, + output logic flash_err_o, + // Alert indication (to be connected to alert sender in the instantiating IP) + output logic fatal_alert_o, + output logic recov_alert_o, + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // Observability + input ast_pkg::ast_obs_ctrl_t obs_ctrl_i, + output logic [7:0] fla_obs_o +); + + // convert this into a tlul write later + logic init; + assign init = 1'b1; + + logic [NumBanks-1:0] init_busy; + assign init_busy_o = |init_busy; + + // this represents the type of program operations that are supported + assign prog_type_avail_o[flash_ctrl_pkg::FlashProgNormal] = 1'b1; + assign prog_type_avail_o[flash_ctrl_pkg::FlashProgRepair] = 1'b1; + + for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_prim_flash_banks + + prim_generic_flash_bank #( + .InfosPerBank(InfosPerBank), + .InfoTypes(InfoTypes), + .InfoTypesWidth(InfoTypesWidth), + .PagesPerBank(PagesPerBank), + .WordsPerPage(WordsPerPage), + .DataWidth(DataWidth) + ) u_prim_flash_bank ( + .clk_i, + .rst_ni, + .rd_i(flash_req_i[bank].rd_req), + .prog_i(flash_req_i[bank].prog_req), + .prog_last_i(flash_req_i[bank].prog_last), + .prog_type_i(flash_req_i[bank].prog_type), + .pg_erase_i(flash_req_i[bank].pg_erase_req), + .bk_erase_i(flash_req_i[bank].bk_erase_req), + .erase_suspend_req_i(flash_req_i[bank].erase_suspend_req), + .he_i(flash_req_i[bank].he), + .addr_i(flash_req_i[bank].addr), + .part_i(flash_req_i[bank].part), + .info_sel_i(flash_req_i[bank].info_sel), + .prog_data_i(flash_req_i[bank].prog_full_data), + .ack_o(flash_rsp_o[bank].ack), + .done_o(flash_rsp_o[bank].done), + .rd_data_o(flash_rsp_o[bank].rdata), + .init_i(init), + .init_busy_o(init_busy[bank]), + .flash_power_ready_h_i, + .flash_power_down_h_i + ); + end + + logic unused_scanmode; + logic unused_scan_en; + logic unused_scan_rst_n; + logic [TestModeWidth-1:0] unused_flash_test_mode; + logic unused_flash_test_voltage; + logic unused_tck; + logic unused_tdi; + logic unused_tms; + + assign unused_scanmode = ^scanmode_i; + assign unused_scan_en = scan_en_i; + assign unused_scan_rst_n = scan_rst_ni; + assign unused_flash_test_mode = flash_test_mode_a_io; + assign unused_flash_test_voltage = flash_test_voltage_h_io; + assign unused_tck = tck_i; + assign unused_tdi = tdi_i; + assign unused_tms = tms_i; + assign tdo_o = '0; + + //////////////////////////////////// + // TL-UL Test Interface Emulation // + //////////////////////////////////// + + logic intg_err; + flash_ctrl_reg_pkg::flash_ctrl_prim_reg2hw_t reg2hw; + flash_ctrl_reg_pkg::flash_ctrl_prim_hw2reg_t hw2reg; + flash_ctrl_prim_reg_top u_reg_top ( + .clk_i, + .rst_ni, + .tl_i (tl_i), + .tl_o (tl_o), + .reg2hw (reg2hw), + .hw2reg (hw2reg), + .intg_err_o(intg_err) + ); + + logic unused_reg_sig; + assign unused_reg_sig = ^reg2hw; + assign hw2reg = '0; + + logic unused_bist_enable; + assign unused_bist_enable = ^bist_enable_i; + + // open source model has no error response at the moment + assign flash_err_o = 1'b0; + + assign fatal_alert_o = intg_err; + assign recov_alert_o = 1'b0; + + logic unused_obs; + assign unused_obs = |obs_ctrl_i; + assign fla_obs_o = '0; + + +endmodule // prim_generic_flash diff --git a/synth/prim_generic/rtl/prim_generic_flash_bank.sv b/synth/prim_generic/rtl/prim_generic_flash_bank.sv new file mode 100755 index 0000000..b6e0d8b --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_flash_bank.sv @@ -0,0 +1,455 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Emulate a single generic flash bank +// + +module prim_generic_flash_bank #( + parameter int InfosPerBank = 1, // info pages per bank + parameter int InfoTypes = 1, // different info types + parameter int InfoTypesWidth = 1, // different info types + parameter int PagesPerBank = 256, // data pages per bank + parameter int WordsPerPage = 256, // words per page + parameter int DataWidth = 32, // bits per word + + // 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 prog_last_i, + // the generic model does not make use of program types + input flash_ctrl_pkg::flash_prog_e prog_type_i, + input pg_erase_i, + input bk_erase_i, + input erase_suspend_req_i, + input he_i, + input [AddrW-1:0] addr_i, + input flash_ctrl_pkg::flash_part_e part_i, + input [InfoTypesWidth-1:0] info_sel_i, + input [DataWidth-1:0] prog_data_i, + output logic ack_o, + output logic done_o, + output logic [DataWidth-1:0] rd_data_o, + input init_i, + output logic init_busy_o, + input flash_power_ready_h_i, + input flash_power_down_h_i +); + + `ifdef SYNTHESIS + localparam int ReadLatency = 1; + localparam int ProgLatency = 50; + localparam int EraseLatency = 200; + + `else + int ReadLatency = 1; + int ProgLatency = 50; + int EraseLatency = 200; + + initial begin + bit flash_rand_delay_en; + void'($value$plusargs("flash_rand_delay_en=%0b", flash_rand_delay_en)); + + if (flash_rand_delay_en) begin + ReadLatency = $urandom_range(1, 5); + ProgLatency = $urandom_range(25, 50); + EraseLatency = $urandom_range(125, 200); + end + void'($value$plusargs("flash_read_latency=%0d", ReadLatency)); + void'($value$plusargs("flash_program_latency=%0d", ProgLatency)); + void'($value$plusargs("flash_erase_latency=%0d", EraseLatency)); + $display("%m: ReadLatency:%0d ProgLatency:%0d EraseLatency:%0d", + ReadLatency, ProgLatency, EraseLatency); + end + `endif + + // Emulated flash macro values + localparam int BkEraseCycles = 2000; + localparam int InitCycles = 100; + + // Locally derived values + localparam int WordsPerBank = PagesPerBank * WordsPerPage; + localparam int WordsPerInfoBank = InfosPerBank * WordsPerPage; + localparam int InfoAddrW = $clog2(WordsPerInfoBank); + + typedef enum logic [2:0] { + StReset = 'h0, + StInit = 'h1, + StIdle = 'h2, + StRead = 'h3, + StProg = 'h4, + StErase = 'h5, + StErSuspend = 'h6 + } 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 [DataWidth-1:0] mem_wdata; + logic [AddrW-1:0] mem_addr; + flash_ctrl_pkg::flash_part_e mem_part; + logic mem_bk_erase; + logic [InfoTypesWidth-1:0] mem_info_sel; + + // insert a fifo here to break the large fanout from inputs to memories on reads + typedef struct packed { + logic rd; + logic prog; + logic prog_last; + flash_ctrl_pkg::flash_prog_e prog_type; + logic pg_erase; + logic bk_erase; + logic [AddrW-1:0] addr; + flash_ctrl_pkg::flash_part_e part; + logic [InfoTypesWidth-1:0] info_sel; + logic [DataWidth-1:0] prog_data; + } cmd_payload_t; + + cmd_payload_t cmd_d, cmd_q; + logic cmd_valid; + logic pop_cmd; + logic mem_rd_q, mem_rd_d; + + assign cmd_d = '{ + rd : rd_i, + prog: prog_i, + prog_last: prog_last_i, + prog_type: prog_type_i, + pg_erase: pg_erase_i, + bk_erase: bk_erase_i, + addr: addr_i, + part: part_i, + info_sel: info_sel_i, + prog_data: prog_data_i + }; + + // for read transactions, in order to reduce latency, the + // command fifo is popped early (before done_o). This is to ensure that when + // the current transaction is complete, during the same cycle + // a new read can be issued. As a result, the command is popped + // immediately after the read is issued, rather than waiting for + // the read to be completed. The same restrictions are not necessary + // for program / erase, which do not have the same performance + // requirements. + + // when the flash is going through init, do not accept any transactions + logic wvalid; + logic ack; + assign wvalid = (rd_i | prog_i | pg_erase_i | bk_erase_i) & !init_busy_o; + assign ack_o = ack & !init_busy_o; + + prim_fifo_sync #( + .Width ($bits(cmd_payload_t)), + .Pass (0), + .Depth (2) + ) u_cmd_fifo ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(wvalid), + .wready_o(ack), + .wdata_i (cmd_d), + .depth_o (), + .full_o (), + .rvalid_o(cmd_valid), + .rready_i(pop_cmd), + .rdata_o (cmd_q), + .err_o () + ); + + logic rd_req, prog_req, pg_erase_req, bk_erase_req; + assign rd_req = cmd_valid & cmd_q.rd; + assign prog_req = cmd_valid & cmd_q.prog; + assign pg_erase_req = cmd_valid & cmd_q.pg_erase; + assign bk_erase_req = cmd_valid & cmd_q.bk_erase; + + // for read / program operations, the index cnt should be 0 + assign mem_rd_d = mem_req & ~mem_wr; + assign mem_addr = cmd_q.addr + index_cnt[AddrW-1:0]; + assign mem_part = cmd_q.part; + assign mem_bk_erase = cmd_q.bk_erase; + + assign mem_info_sel = cmd_q.info_sel; + + 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 + time_limit_q <= 'h0; + index_limit_q <= 'h0; + prog_pend_q <= 'h0; + mem_rd_q <= 'h0; + end else begin + time_limit_q <= time_limit_d; + index_limit_q <= index_limit_d; + prog_pend_q <= prog_pend_d; + mem_rd_q <= mem_rd_d; + end + end + + // latch read data from emulated memories the cycle after a read + logic [DataWidth-1:0] rd_data_q, rd_data_d; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rd_data_q <= '0; + end else if (mem_rd_q) begin + rd_data_q <= rd_data_d; + end + end + + // latch partiton being read since the command fifo is popped early + flash_ctrl_pkg::flash_part_e rd_part_q; + logic [InfoTypesWidth-1:0] info_sel_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rd_part_q <= flash_ctrl_pkg::FlashPartData; + info_sel_q <= '0; + end else if (mem_rd_d) begin + rd_part_q <= cmd_q.part; + info_sel_q <= cmd_q.info_sel; + end + end + + // if read cycle is only 1, we can expose the unlatched data directly + assign rd_data_o = ReadLatency == 1 ? rd_data_d : rd_data_q; + + // 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 <= 'h0; + index_cnt <= '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; + 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 = '0; + mem_wr = '0; + mem_wdata = '0; + time_cnt_inc = '0; + time_cnt_clr = '0; + time_cnt_set1 = '0; + index_cnt_inc = '0; + index_cnt_clr = '0; + + // i/o + init_busy_o = '0; + pop_cmd = '0; + done_o = '0; + + unique case (st_q) + StReset: begin + init_busy_o = 1'b1; + if (init_i && flash_power_ready_h_i && !flash_power_down_h_i) begin + st_d = StInit; + end + end + + // Emulate flash initilaization with a wait timer + StInit: begin + init_busy_o = 1'h1; + if (index_cnt < InitCycles) begin + st_d = StInit; + index_cnt_inc = 1'b1; + end else begin + st_d = StIdle; + index_cnt_clr = 1'b1; + end + end + + StIdle: begin + if (rd_req) begin + pop_cmd = 1'b1; + mem_req = 1'b1; + time_cnt_inc = 1'b1; + st_d = StRead; + end else if (prog_req) begin + mem_req = 1'b1; + prog_pend_d = 1'b1; + st_d = StRead; + end else if (pg_erase_req) begin + st_d = StErase; + index_limit_d = WordsPerPage; + time_limit_d = EraseLatency; + end else if (bk_erase_req) begin + st_d = StErase; + index_limit_d = WordsPerBank; + time_limit_d = BkEraseCycles; + end + end + + StRead: begin + if (time_cnt < ReadLatency) begin + time_cnt_inc = 1'b1; + + end else if (!prog_pend_q) begin + done_o = 1'b1; + + // if another request already pending + if (rd_req) begin + pop_cmd = 1'b1; + 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 + // if data is already 0, cannot program to 1 without erase + mem_wdata = cmd_q.prog_data & rd_data_q; + if (time_cnt < ProgLatency) begin + mem_req = 1'b1; + mem_wr = 1'b1; + time_cnt_inc = 1'b1; + end else begin + st_d = StIdle; + pop_cmd = 1'b1; + done_o = cmd_q.prog_last; + time_cnt_clr = 1'b1; + end + end + + StErase: begin + // Actual erasing of the page + if (erase_suspend_req_i) begin + st_d = StErSuspend; + end else 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}}; + time_cnt_inc = (time_cnt < time_limit_q); + index_cnt_inc = (index_cnt < index_limit_q); + end else begin + st_d = StIdle; + pop_cmd = 1'b1; + done_o = 1'b1; + time_cnt_clr = 1'b1; + index_cnt_clr = 1'b1; + end + end // case: StErase + + // The done can actually be signaled back in `StErase`, but move it + // to a different state to better model the ack_o/done_o timing separation + StErSuspend: begin + done_o = 1'b1; + pop_cmd = 1'b1; + time_cnt_clr = 1'b1; + index_cnt_clr = 1'b1; + st_d = StIdle; + end + + + default: begin + st_d = StIdle; + end + + endcase // unique case (st_q) + + // Emulate power down and power loss behavior + if (!flash_power_ready_h_i || flash_power_down_h_i) begin + st_d = StReset; + end + + end // always_comb + + logic [DataWidth-1:0] rd_data_main, rd_data_info; + logic [InfoTypes-1:0][DataWidth-1:0] rd_nom_data_info; + + // data memory is requested whenver it's a transaction targetted at the data partition + // OR if it's a bank erase + logic data_mem_req; + assign data_mem_req = mem_req & + (mem_part == flash_ctrl_pkg::FlashPartData | + mem_bk_erase); + + prim_ram_1p #( + .Width(DataWidth), + .Depth(WordsPerBank), + .DataBitsPerMask(DataWidth) + ) u_mem ( + .clk_i, + .req_i (data_mem_req), + .write_i (mem_wr), + .addr_i (mem_addr), + .wdata_i (mem_wdata), + .wmask_i ({DataWidth{1'b1}}), + .rdata_o (rd_data_main), + .cfg_i ('0) + ); + + for (genvar info_type = 0; info_type < InfoTypes; info_type++) begin : gen_info_types + + // when info partitions are selected for bank erase, all info types are erased. + // if NOT bank erase, then only the selected info partition is erased + logic info_mem_req; + assign info_mem_req = mem_req & + (mem_part == flash_ctrl_pkg::FlashPartInfo) & + ((mem_info_sel == info_type) | mem_bk_erase); + + prim_ram_1p #( + .Width(DataWidth), + .Depth(WordsPerInfoBank), + .DataBitsPerMask(DataWidth) + ) u_info_mem ( + .clk_i, + .req_i (info_mem_req), + .write_i (mem_wr), + .addr_i (mem_addr[0 +: InfoAddrW]), + .wdata_i (mem_wdata), + .wmask_i ({DataWidth{1'b1}}), + .rdata_o (rd_nom_data_info[info_type]), + .cfg_i ('0) + ); + end + + assign rd_data_info = rd_nom_data_info[info_sel_q]; + assign rd_data_d = rd_part_q == flash_ctrl_pkg::FlashPartData ? rd_data_main : rd_data_info; + + flash_ctrl_pkg::flash_prog_e unused_prog_type; + assign unused_prog_type = cmd_q.prog_type; + + logic unused_he; + assign unused_he = he_i; + +endmodule // prim_generic_flash diff --git a/synth/prim_generic/rtl/prim_generic_flop.sv b/synth/prim_generic/rtl/prim_generic_flop.sv new file mode 100755 index 0000000..67b6e89 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_flop.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 + +`include "prim_assert.sv" + +module prim_generic_flop #( + parameter int Width = 1, + parameter logic [Width-1:0] ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q_o <= ResetValue; + end else begin + q_o <= d_i; + end + end + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_flop_en.sv b/synth/prim_generic/rtl/prim_generic_flop_en.sv new file mode 100755 index 0000000..3c367b7 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_flop_en.sv @@ -0,0 +1,39 @@ +// 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_flop_en #( + parameter int Width = 1, + parameter bit EnSecBuf = 0, + parameter logic [Width-1:0] ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input en_i, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + logic en; + if (EnSecBuf) begin : gen_en_sec_buf + prim_sec_anchor_buf #( + .Width(1) + ) u_en_buf ( + .in_i(en_i), + .out_o(en) + ); + end else begin : gen_en_no_sec_buf + assign en = en_i; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q_o <= ResetValue; + end else if (en) begin + q_o <= d_i; + end + end + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_otp.sv b/synth/prim_generic/rtl/prim_generic_otp.sv new file mode 100755 index 0000000..d27f03e --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_otp.sv @@ -0,0 +1,433 @@ +// 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 + import prim_otp_pkg::*; +#( + // Native OTP word size. This determines the size_i granule. + parameter int Width = 16, + parameter int Depth = 1024, + // This determines the maximum number of native words that + // can be transferred accross the interface in one cycle. + parameter int SizeWidth = 2, + // Width of the power sequencing signal. + parameter int PwrSeqWidth = 2, + // Width of vendor-specific test control signal + parameter int TestCtrlWidth = 32, + parameter int TestStatusWidth = 32, + parameter int TestVectWidth = 8, + // Derived parameters + localparam int AddrWidth = prim_util_pkg::vbits(Depth), + localparam int IfWidth = 2**SizeWidth*Width, + // VMEM file to initialize the memory with + parameter MemInitFile = "", + // Vendor test partition offset and size (both in bytes) + parameter int VendorTestOffset = 0, + parameter int VendorTestSize = 0 +) ( + input clk_i, + input rst_ni, + // Observability + input ast_pkg::ast_obs_ctrl_t obs_ctrl_i, + output logic [7:0] otp_obs_o, + // Macro-specific power sequencing signals to/from AST + output logic [PwrSeqWidth-1:0] pwr_seq_o, + input [PwrSeqWidth-1:0] pwr_seq_h_i, + // External programming voltage + inout wire ext_voltage_io, + // Test interfaces + input [TestCtrlWidth-1:0] test_ctrl_i, + output logic [TestStatusWidth-1:0] test_status_o, + output logic [TestVectWidth-1:0] test_vect_o, + input tlul_pkg::tl_h2d_t test_tl_i, + output tlul_pkg::tl_d2h_t test_tl_o, + // Other DFT signals + input prim_mubi_pkg::mubi4_t scanmode_i, // Scan Mode input + input scan_en_i, // Scan Shift + input scan_rst_ni, // Scan Reset + // Alert indication (to be connected to alert sender in the instantiating IP) + output logic fatal_alert_o, + output logic recov_alert_o, + // Ready valid handshake for read/write command + output logic ready_o, + input valid_i, + // #(Native words)-1, e.g. size == 0 for 1 native word. + input [SizeWidth-1:0] size_i, + // 000: read, 001: write, 010: read raw, 011: write raw, 111: init + input cmd_e cmd_i, + input [AddrWidth-1:0] addr_i, + input [IfWidth-1:0] wdata_i, + // Response channel + output logic valid_o, + output logic [IfWidth-1:0] rdata_o, + output err_e err_o +); + + import prim_mubi_pkg::MuBi4False; + + // This is only restricted by the supported ECC poly further + // below, and is straightforward to extend, if needed. + localparam int EccWidth = 6; + `ASSERT_INIT(SecDecWidth_A, Width == 16) + + // Not supported in open-source emulation model. + logic [PwrSeqWidth-1:0] unused_pwr_seq_h; + assign unused_pwr_seq_h = pwr_seq_h_i; + assign pwr_seq_o = '0; + + logic unused_obs; + assign unused_obs = |obs_ctrl_i; + assign otp_obs_o = '0; + + wire unused_ext_voltage; + assign unused_ext_voltage = ext_voltage_io; + logic unused_test_ctrl_i; + assign unused_test_ctrl_i = ^test_ctrl_i; + + logic unused_scan; + assign unused_scan = ^{scanmode_i, scan_en_i, scan_rst_ni}; + + logic intg_err, fsm_err; + assign fatal_alert_o = intg_err || fsm_err; + assign recov_alert_o = 1'b0; + + assign test_vect_o = '0; + assign test_status_o = '0; + + //////////////////////////////////// + // TL-UL Test Interface Emulation // + //////////////////////////////////// + + otp_ctrl_reg_pkg::otp_ctrl_prim_reg2hw_t reg2hw; + otp_ctrl_reg_pkg::otp_ctrl_prim_hw2reg_t hw2reg; + otp_ctrl_prim_reg_top u_reg_top ( + .clk_i, + .rst_ni, + .tl_i (test_tl_i ), + .tl_o (test_tl_o ), + .reg2hw (reg2hw ), + .hw2reg (hw2reg ), + .intg_err_o(intg_err ) + ); + + logic unused_reg_sig; + assign unused_reg_sig = ^reg2hw; + assign hw2reg = '0; + + /////////////////// + // Control logic // + /////////////////// + + // Encoding generated with: + // $ ./util/design/sparse-fsm-encode.py -d 5 -m 9 -n 10 \ + // -s 2599950981 --language=sv + // + // Hamming distance histogram: + // + // 0: -- + // 1: -- + // 2: -- + // 3: -- + // 4: -- + // 5: |||||||||||||||||||| (52.78%) + // 6: ||||||||||||||| (41.67%) + // 7: | (2.78%) + // 8: | (2.78%) + // 9: -- + // 10: -- + // + // Minimum Hamming distance: 5 + // Maximum Hamming distance: 8 + // Minimum Hamming weight: 3 + // Maximum Hamming weight: 8 + // + localparam int StateWidth = 10; + typedef enum logic [StateWidth-1:0] { + ResetSt = 10'b1100000110, + InitSt = 10'b1000110011, + IdleSt = 10'b0101110000, + ReadSt = 10'b0010011111, + ReadWaitSt = 10'b1001001101, + WriteCheckSt = 10'b1111101011, + WriteWaitSt = 10'b0011000010, + WriteSt = 10'b0110100101, + ErrorSt = 10'b1110011000 + } state_e; + + state_e state_d, state_q; + err_e err_d, err_q; + logic valid_d, valid_q; + logic integrity_en_d, integrity_en_q; + logic req, wren, rvalid; + logic [1:0] rerror; + logic [AddrWidth-1:0] addr_q; + logic [SizeWidth-1:0] size_q; + logic [SizeWidth-1:0] cnt_d, cnt_q; + logic cnt_clr, cnt_en; + logic read_ecc_on, write_ecc_on; + logic wdata_inconsistent; + + + assign cnt_d = (cnt_clr) ? '0 : + (cnt_en) ? cnt_q + 1'b1 : cnt_q; + + assign valid_o = valid_q; + assign err_o = err_q; + + always_comb begin : p_fsm + // Default + state_d = state_q; + ready_o = 1'b0; + valid_d = 1'b0; + err_d = err_q; + req = 1'b0; + wren = 1'b0; + cnt_clr = 1'b0; + cnt_en = 1'b0; + read_ecc_on = 1'b1; + write_ecc_on = 1'b1; + fsm_err = 1'b0; + integrity_en_d = integrity_en_q; + + unique case (state_q) + // Wait here until we receive an initialization command. + ResetSt: begin + err_d = NoError; + ready_o = 1'b1; + if (valid_i) begin + if (cmd_i == Init) begin + state_d = InitSt; + end + end + end + // Wait for some time until the OTP macro is ready. + InitSt: begin + state_d = IdleSt; + valid_d = 1'b1; + err_d = NoError; + end + // In the idle state, we basically wait for read or write commands. + IdleSt: begin + ready_o = 1'b1; + err_d = NoError; + if (valid_i) begin + cnt_clr = 1'b1; + err_d = NoError; + unique case (cmd_i) + Read: begin + state_d = ReadSt; + integrity_en_d = 1'b1; + end + Write: begin + state_d = WriteCheckSt; + integrity_en_d = 1'b1; + end + ReadRaw: begin + state_d = ReadSt; + integrity_en_d = 1'b0; + end + WriteRaw: begin + state_d = WriteCheckSt; + integrity_en_d = 1'b0; + end + default: ; + endcase // cmd_i + end + end + // Issue a read command to the macro. + ReadSt: begin + state_d = ReadWaitSt; + req = 1'b1; + // Suppress ECC correction if needed. + read_ecc_on = integrity_en_q; + end + // Wait for response from macro. + ReadWaitSt: begin + // Suppress ECC correction if needed. + read_ecc_on = integrity_en_q; + if (rvalid) begin + cnt_en = 1'b1; + // Uncorrectable error, bail out. + if (rerror[1] && integrity_en_q) begin + state_d = IdleSt; + valid_d = 1'b1; + err_d = MacroEccUncorrError; + end else begin + if (cnt_q == size_q) begin + state_d = IdleSt; + valid_d = 1'b1; + end else begin + state_d = ReadSt; + end + // Correctable error, carry on but signal back. + if (rerror[0] && integrity_en_q) begin + err_d = MacroEccCorrError; + end + end + end + end + // First, read out to perform the write blank check and + // read-modify-write operation. + WriteCheckSt: begin + state_d = WriteWaitSt; + req = 1'b1; + // Register raw memory contents without correction so that we can + // perform the read-modify-write correctly. + read_ecc_on = 1'b0; + end + // Wait for readout to complete first. + WriteWaitSt: begin + // Register raw memory contents without correction so that we can + // perform the read-modify-write correctly. + read_ecc_on = 1'b0; + if (rvalid) begin + cnt_en = 1'b1; + + if (cnt_q == size_q) begin + cnt_clr = 1'b1; + state_d = WriteSt; + end else begin + state_d = WriteCheckSt; + end + end + end + // If the write data attempts to clear an already programmed bit, + // the MacroWriteBlankError needs to be asserted. + WriteSt: begin + req = 1'b1; + wren = 1'b1; + cnt_en = 1'b1; + // Suppress ECC calculation if needed. + write_ecc_on = integrity_en_q; + + if (wdata_inconsistent) begin + err_d = MacroWriteBlankError; + end + + if (cnt_q == size_q) begin + valid_d = 1'b1; + state_d = IdleSt; + end + end + // If the FSM is glitched into an invalid state. + ErrorSt: begin + fsm_err = 1'b1; + end + default: begin + state_d = ErrorSt; + fsm_err = 1'b1; + end + endcase // state_q + end + + /////////////////////////////////////////// + // Emulate using ECC protected Block RAM // + /////////////////////////////////////////// + + logic [AddrWidth-1:0] addr; + assign addr = addr_q + AddrWidth'(cnt_q); + + logic [Width-1:0] rdata_corr; + logic [Width+EccWidth-1:0] rdata_d, wdata_ecc, rdata_ecc, wdata_rmw; + logic [2**SizeWidth-1:0][Width-1:0] wdata_q, rdata_reshaped; + logic [2**SizeWidth-1:0][Width+EccWidth-1:0] rdata_q; + + // Use a standard Hamming ECC for OTP. + prim_secded_hamming_22_16_enc u_enc ( + .data_i(wdata_q[cnt_q]), + .data_o(wdata_ecc) + ); + + prim_secded_hamming_22_16_dec u_dec ( + .data_i (rdata_ecc), + .data_o (rdata_corr), + .syndrome_o ( ), + .err_o (rerror) + ); + + assign rdata_d = (read_ecc_on) ? {{EccWidth{1'b0}}, rdata_corr} + : rdata_ecc; + + // Read-modify-write (OTP can only set bits to 1, but not clear to 0). + assign wdata_rmw = (write_ecc_on) ? wdata_ecc | rdata_q[cnt_q] + : {{EccWidth{1'b0}}, wdata_q[cnt_q]} | rdata_q[cnt_q]; + + // This indicates if the write data is inconsistent (i.e., if the operation attempts to + // clear an already programmed bit to zero). + assign wdata_inconsistent = (rdata_q[cnt_q] & wdata_ecc) != rdata_q[cnt_q]; + + // Output data without ECC bits. + always_comb begin : p_output_map + for (int k = 0; k < 2**SizeWidth; k++) begin + rdata_reshaped[k] = rdata_q[k][Width-1:0]; + end + rdata_o = rdata_reshaped; + end + + prim_ram_1p_adv #( + .Depth (Depth), + .Width (Width + EccWidth), + .MemInitFile (MemInitFile), + .EnableInputPipeline (1), + .EnableOutputPipeline (1) + ) u_prim_ram_1p_adv ( + .clk_i, + .rst_ni, + .req_i ( req ), + .write_i ( wren ), + .addr_i ( addr ), + .wdata_i ( wdata_rmw ), + .wmask_i ( {Width+EccWidth{1'b1}} ), + .rdata_o ( rdata_ecc ), + .rvalid_o ( rvalid ), + .rerror_o ( ), + .cfg_i ( '0 ) + ); + + // Currently it is assumed that no wrap arounds can occur. + `ASSERT(NoWrapArounds_A, req |-> (addr >= addr_q)) + + ////////// + // Regs // + ////////// + + `PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, state_e, ResetSt) + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + valid_q <= '0; + err_q <= NoError; + addr_q <= '0; + wdata_q <= '0; + rdata_q <= '0; + cnt_q <= '0; + size_q <= '0; + integrity_en_q <= 1'b0; + end else begin + valid_q <= valid_d; + err_q <= err_d; + cnt_q <= cnt_d; + integrity_en_q <= integrity_en_d; + if (ready_o && valid_i) begin + addr_q <= addr_i; + wdata_q <= wdata_i; + size_q <= size_i; + end + if (rvalid) begin + rdata_q[cnt_q] <= rdata_d; + end + end + end + + //////////////// + // Assertions // + //////////////// + + // Check that the otp_ctrl FSMs only issue legal commands to the wrapper. + `ASSERT(CheckCommands0_A, state_q == ResetSt && valid_i && ready_o |-> cmd_i == Init) + `ASSERT(CheckCommands1_A, state_q != ResetSt && valid_i && ready_o + |-> cmd_i inside {Read, ReadRaw, Write, WriteRaw}) + + +endmodule : prim_generic_otp diff --git a/synth/prim_generic/rtl/prim_generic_pad_attr.sv b/synth/prim_generic/rtl/prim_generic_pad_attr.sv new file mode 100755 index 0000000..1dcfae5 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_pad_attr.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 +// + +`include "prim_assert.sv" + +module prim_generic_pad_attr + import prim_pad_wrapper_pkg::*; +#( + parameter pad_type_e PadType = BidirStd // currently ignored in the generic model +) ( + output pad_attr_t attr_warl_o +); + + // Currently supported pad attributes of the generic pad library: + // + // Input-only: + // + // - inversion + // - pullup / pulldown + // + // Bidirectional: + // + // - inversion + // - virtual open drain + // - pullup / pulldown + // - 1 driving strength bit + // + // Note that the last three attributes are not supported on Verilator. + if (PadType == InputStd) begin : gen_input_only_warl + always_comb begin : p_attr + attr_warl_o = '0; + attr_warl_o.invert = 1'b1; + attr_warl_o.pull_en = 1'b1; + attr_warl_o.pull_select = 1'b1; + end + end else if (PadType == BidirStd || + PadType == BidirTol || + PadType == DualBidirTol || + PadType == BidirOd) begin : gen_bidir_warl + always_comb begin : p_attr + attr_warl_o = '0; + attr_warl_o.invert = 1'b1; + attr_warl_o.virt_od_en = 1'b1; + attr_warl_o.pull_en = 1'b1; + attr_warl_o.pull_select = 1'b1; + // Driving strength is not supported by Verilator + `ifndef VERILATOR + // Only one driving strength bit is supported. + attr_warl_o.drive_strength[0] = 1'b1; + `endif + end + end else if (PadType == AnalogIn0) begin : gen_analog0_warl + // The analog pad type is basically just a feedthrough, + // and does hence not support any of the attributes. + always_comb begin : p_attr + attr_warl_o = '0; + end + end else begin : gen_invalid_config + // this should throw link warnings in elaboration + assert_static_in_generate_config_not_available + assert_static_in_generate_config_not_available(); + end + +endmodule : prim_generic_pad_attr diff --git a/synth/prim_generic/rtl/prim_generic_pad_wrapper.sv b/synth/prim_generic/rtl/prim_generic_pad_wrapper.sv new file mode 100755 index 0000000..e7da276 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_pad_wrapper.sv @@ -0,0 +1,112 @@ +// 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 + import prim_pad_wrapper_pkg::*; +#( + // These parameters are ignored in this model. + parameter pad_type_e PadType = BidirStd, + parameter scan_role_e ScanRole = NoScan +) ( + // This is only used for scanmode (not used in generic models) + input clk_scan_i, + input scanmode_i, + // Power sequencing signals (not used in generic models) + input pad_pok_t pok_i, + // Main Pad signals + inout wire inout_io, // bidirectional pad + output logic in_o, // input data + output logic in_raw_o, // uninverted output data + input ie_i, // input enable + input out_i, // output data + input oe_i, // output enable + input pad_attr_t attr_i // additional pad attributes +); + + // analog pads cannot have a scan role. + `ASSERT_INIT(AnalogNoScan_A, PadType != AnalogIn0 || ScanRole == NoScan) + + //VCS coverage off + // pragma coverage off + // not all signals are used here. + logic unused_sigs; + assign unused_sigs = ^{attr_i.slew_rate, + attr_i.drive_strength[3:1], + attr_i.od_en, + attr_i.schmitt_en, + attr_i.keep_en, + scanmode_i, + pok_i}; + //VCS coverage on + // pragma coverage on + + if (PadType == InputStd) begin : gen_input_only + //VCS coverage off + // pragma coverage off + logic unused_in_sigs; + assign unused_in_sigs = ^{out_i, + oe_i, + attr_i.virt_od_en, + attr_i.drive_strength}; + //VCS coverage on + // pragma coverage on + + assign in_raw_o = (ie_i) ? inout_io : 1'bz; + // input inversion + assign in_o = attr_i.invert ^ in_raw_o; + + // pulls are not supported by verilator + `ifndef VERILATOR + // pullup / pulldown termination + assign (weak0, weak1) inout_io = attr_i.pull_en ? attr_i.pull_select : 1'bz; + `endif + end else if (PadType == BidirTol || + PadType == DualBidirTol || + PadType == BidirOd || + PadType == BidirStd) begin : gen_bidir + + assign in_raw_o = (ie_i) ? inout_io : 1'bz; + // input inversion + assign in_o = attr_i.invert ^ in_raw_o; + + // virtual open drain emulation + logic oe, out; + assign out = out_i ^ attr_i.invert; + assign oe = oe_i & ((attr_i.virt_od_en & ~out) | ~attr_i.virt_od_en); + + // drive 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 && attr_i.drive_strength[0]) ? out : 1'bz; + assign (pull0, pull1) inout_io = (oe && !attr_i.drive_strength[0]) ? out : 1'bz; + // pullup / pulldown termination + assign (weak0, weak1) inout_io = attr_i.pull_en ? attr_i.pull_select : 1'bz; + `endif + end else if (PadType == AnalogIn0 || PadType == AnalogIn1) begin : gen_analog + + //VCS coverage off + // pragma coverage off + logic unused_ana_sigs; + assign unused_ana_sigs = ^{attr_i, out_i, oe_i, ie_i}; + //VCS coverage on + // pragma coverage on + + assign inout_io = 1'bz; // explicitly make this tristate to avoid lint errors. + assign in_o = inout_io; + assign in_raw_o = inout_io; + + end else begin : gen_invalid_config + // this should throw link warnings in elaboration + assert_static_in_generate_config_not_available + assert_static_in_generate_config_not_available(); + end + +endmodule : prim_generic_pad_wrapper diff --git a/synth/prim_generic/rtl/prim_generic_ram_1p.sv b/synth/prim_generic/rtl/prim_generic_ram_1p.sv new file mode 100755 index 0000000..8d84e63 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_ram_1p.sv @@ -0,0 +1,79 @@ +// 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 import prim_ram_1p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input logic clk_i, + + input logic req_i, + input logic write_i, + input logic [Aw-1:0] addr_i, + input logic [Width-1:0] wdata_i, + input logic [Width-1:0] wmask_i, + output logic [Width-1:0] rdata_o, // Read data. Data is returned one cycle after req_i is high. + input ram_1p_cfg_t cfg_i +); + +// For certain synthesis experiments we compile the design with generic models to get an unmapped +// netlist (GTECH). In these synthesis experiments, we typically black-box the memory models since +// these are going to be simulated using plain RTL models in netlist simulations. This can be done +// by analyzing and elaborating the design, and then removing the memory submodules before writing +// out the verilog netlist. However, memory arrays can take a long time to elaborate, and in case +// of dual port rams they can even trigger elab errors due to multiple processes writing to the +// same memory variable concurrently. To this end, we exclude the entire logic in this module in +// these runs with the following macro. +`ifndef SYNTHESIS_MEMORY_BLACK_BOXING + + // Width must be fully divisible by DataBitsPerMask + `ASSERT_INIT(DataBitsPerMaskCheck_A, (Width % DataBitsPerMask) == 0) + + logic unused_cfg; + assign unused_cfg = ^cfg_i; + + // Width of internal write mask. Note wmask_i input into the module is always assumed + // to be the full bit mask + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] wmask; + + for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask + assign wmask[k] = &wmask_i[k*DataBitsPerMask +: DataBitsPerMask]; + + // Ensure that all mask bits within a group have the same value for a write + `ASSERT(MaskCheck_A, req_i && write_i |-> + wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0}, + clk_i, '0) + end + + // using always instead of always_ff to avoid 'ICPD - illegal combination of drivers' error + // thrown when using $readmemh system task to backdoor load an image + always @(posedge clk_i) begin + if (req_i) begin + if (write_i) begin + for (int i=0; i < MaskWidth; i = i + 1) begin + if (wmask[i]) begin + mem[addr_i][i*DataBitsPerMask +: DataBitsPerMask] <= + wdata_i[i*DataBitsPerMask +: DataBitsPerMask]; + end + end + end else begin + rdata_o <= mem[addr_i]; + end + end + end + + `include "prim_util_memload.svh" +`endif +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_ram_1r1w.sv b/synth/prim_generic/rtl/prim_generic_ram_1r1w.sv new file mode 100755 index 0000000..396d506 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_ram_1r1w.sv @@ -0,0 +1,84 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Synchronous two-port SRAM register model +// This module is for simulation and small size SRAM. +// Implementing ECC should be done inside wrapper not this model. +`include "prim_assert.sv" +module prim_generic_ram_1r1w import prim_ram_2p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input clk_a_i, + input clk_b_i, + + // Port A can only write + input a_req_i, + input [Aw-1:0] a_addr_i, + input [Width-1:0] a_wdata_i, + input logic [Width-1:0] a_wmask_i, + + // Port B can only read + input b_req_i, + input [Aw-1:0] b_addr_i, + output logic [Width-1:0] b_rdata_o, + + input ram_2p_cfg_t cfg_i +); + +// For certain synthesis experiments we compile the design with generic models to get an unmapped +// netlist (GTECH). In these synthesis experiments, we typically black-box the memory models since +// these are going to be simulated using plain RTL models in netlist simulations. This can be done +// by analyzing and elaborating the design, and then removing the memory submodules before writing +// out the verilog netlist. However, memory arrays can take a long time to elaborate, and in case +// of two port rams they can even trigger elab errors due to multiple processes writing to the +// same memory variable concurrently. To this end, we exclude the entire logic in this module in +// these runs with the following macro. +`ifndef SYNTHESIS_MEMORY_BLACK_BOXING + + logic unused_cfg; + assign unused_cfg = ^cfg_i; + + // Width of internal write mask. Note *_wmask_i input into the module is always assumed + // to be the full bit mask. + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] a_wmask; + for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask + assign a_wmask[k] = &a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask]; + + // Ensure that all mask bits within a group have the same value for a write + `ASSERT(MaskCheckPortA_A, a_req_i |-> + a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0}, + clk_a_i, '0) + end + + // Xilinx FPGA specific Two-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 + 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 + end + + always @(posedge clk_b_i) begin + if (b_req_i) begin + b_rdata_o <= mem[b_addr_i]; + end + end + + `include "prim_util_memload.svh" +`endif +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_ram_2p.sv b/synth/prim_generic/rtl/prim_generic_ram_2p.sv new file mode 100755 index 0000000..6e1ebdb --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_ram_2p.sv @@ -0,0 +1,107 @@ +// 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. +`include "prim_assert.sv" +module prim_generic_ram_2p import prim_ram_2p_pkg::*; #( + parameter int Width = 32, // bit + parameter int Depth = 128, + parameter int DataBitsPerMask = 1, // Number of data bits per bit of write mask + parameter MemInitFile = "", // VMEM file to initialize the memory with + + localparam int Aw = $clog2(Depth) // derived parameter +) ( + input 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, + + input ram_2p_cfg_t cfg_i +); + +// For certain synthesis experiments we compile the design with generic models to get an unmapped +// netlist (GTECH). In these synthesis experiments, we typically black-box the memory models since +// these are going to be simulated using plain RTL models in netlist simulations. This can be done +// by analyzing and elaborating the design, and then removing the memory submodules before writing +// out the verilog netlist. However, memory arrays can take a long time to elaborate, and in case +// of dual port rams they can even trigger elab errors due to multiple processes writing to the +// same memory variable concurrently. To this end, we exclude the entire logic in this module in +// these runs with the following macro. +`ifndef SYNTHESIS_MEMORY_BLACK_BOXING + + logic unused_cfg; + assign unused_cfg = ^cfg_i; + + // Width of internal write mask. Note *_wmask_i input into the module is always assumed + // to be the full bit mask. + localparam int MaskWidth = Width / DataBitsPerMask; + + logic [Width-1:0] mem [Depth]; + logic [MaskWidth-1:0] a_wmask; + logic [MaskWidth-1:0] b_wmask; + + for (genvar k = 0; k < MaskWidth; k++) begin : gen_wmask + assign a_wmask[k] = &a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask]; + assign b_wmask[k] = &b_wmask_i[k*DataBitsPerMask +: DataBitsPerMask]; + + // Ensure that all mask bits within a group have the same value for a write + `ASSERT(MaskCheckPortA_A, a_req_i && a_write_i |-> + a_wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0}, + clk_a_i, '0) + `ASSERT(MaskCheckPortB_A, b_req_i && b_write_i |-> + b_wmask_i[k*DataBitsPerMask +: DataBitsPerMask] inside {{DataBitsPerMask{1'b1}}, '0}, + clk_b_i, '0) + 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 + + `include "prim_util_memload.svh" +`endif +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_rom.sv b/synth/prim_generic/rtl/prim_generic_rom.sv new file mode 100755 index 0000000..98d02a9 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_rom.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 + +`include "prim_assert.sv" + +module prim_generic_rom import prim_rom_pkg::*; #( + 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 logic clk_i, + input logic req_i, + input logic [Aw-1:0] addr_i, + output logic [Width-1:0] rdata_o, + input rom_cfg_t cfg_i +); + + logic unused_cfg; + assign unused_cfg = ^cfg_i; + + logic [Width-1:0] mem [Depth]; + + always_ff @(posedge clk_i) begin + if (req_i) begin + rdata_o <= mem[addr_i]; + end + end + + `include "prim_util_memload.svh" + + //////////////// + // ASSERTIONS // + //////////////// + + // Control Signals should never be X + `ASSERT(noXOnCsI, !$isunknown(req_i), clk_i, '0) +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_usb_diff_rx.sv b/synth/prim_generic/rtl/prim_generic_usb_diff_rx.sv new file mode 100755 index 0000000..b94d39c --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_usb_diff_rx.sv @@ -0,0 +1,50 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic differential receiver for USB. Note that this is meant for emulation purposes only, and +// the pull-up, calibration and pok signals are not connected in this module. + +`include "prim_assert.sv" + +module prim_generic_usb_diff_rx #( + parameter int CalibW = 32 +) ( + inout input_pi, // differential input + inout input_ni, // differential input + input input_en_i, // input buffer enable + input core_pok_h_i, // core power indication at VCC level + input pullup_p_en_i, // pullup enable for P + input pullup_n_en_i, // pullup enable for N + input [CalibW-1:0] calibration_i, // calibration input + output logic usb_diff_rx_obs_o, // observability output + output logic input_o // output of differential input buffer +); + + logic [CalibW-1:0] unused_calibration; + logic unused_core_pok; + assign unused_calibration = calibration_i; + assign unused_core_pok = core_pok_h_i; + + wire input_p, input_n; + assign input_p = input_pi; + assign input_n = input_ni; + +`ifdef VERILATOR + logic unused_pullup_p_en, unused_pullup_n_en; + assign unused_pullup_p_en = pullup_p_en_i; + assign unused_pullup_n_en = pullup_n_en_i; +`else + // pullup termination + assign (weak0, pull1) input_pi = pullup_p_en_i ? 1'b1 : 1'bz; + assign (weak0, pull1) input_ni = pullup_n_en_i ? 1'b1 : 1'bz; +`endif + + assign input_o = (input_en_i) ? input_p & ~input_n : 1'b0; + + prim_buf obs_buf ( + .in_i (input_o), + .out_o (usb_diff_rx_obs_o) + ); + +endmodule : prim_generic_usb_diff_rx diff --git a/synth/prim_generic/rtl/prim_generic_xnor2.sv b/synth/prim_generic/rtl/prim_generic_xnor2.sv new file mode 100755 index 0000000..1020f83 --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_xnor2.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 + +`include "prim_assert.sv" + +module prim_generic_xnor2 #( + parameter int Width = 1 +) ( + input [Width-1:0] in0_i, + input [Width-1:0] in1_i, + output logic [Width-1:0] out_o +); + + assign out_o = !(in0_i ^ in1_i); + +endmodule diff --git a/synth/prim_generic/rtl/prim_generic_xor2.sv b/synth/prim_generic/rtl/prim_generic_xor2.sv new file mode 100755 index 0000000..8282e8b --- /dev/null +++ b/synth/prim_generic/rtl/prim_generic_xor2.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 + +`include "prim_assert.sv" + +module prim_generic_xor2 #( + parameter int Width = 1 +) ( + input [Width-1:0] in0_i, + input [Width-1:0] in1_i, + output logic [Width-1:0] out_o +); + + assign out_o = in0_i ^ in1_i; + +endmodule diff --git a/synth/python/build_translated_names.py b/synth/python/build_translated_names.py new file mode 100755 index 0000000..a17dc77 --- /dev/null +++ b/synth/python/build_translated_names.py @@ -0,0 +1,47 @@ +#!/usr/bin/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 subprocess + +from flow_utils import * + + +def main(): + arg_parser = argparse.ArgumentParser( + description= + """Uses yosys to translate cell names from given timing reports to human + readable names (assumes flatted synthesis run)""") + + arg_parser.add_argument('top_level', help='Name of the top-level module') + arg_parser.add_argument( + 'gen_out', help='Path to place generated script and script output') + arg_parser.add_argument( + 'rpts', + nargs='+', + help='Report files to generate human readable names from') + + args = arg_parser.parse_args() + + cells_to_translate = set() + + for csv_rpt in args.rpts: + (new_cells_to_translate, path_info) = extract_path_info(csv_rpt) + cells_to_translate.update(new_cells_to_translate) + + create_translate_names_script(cells_to_translate, args.top_level, + args.gen_out) + + yosys_cmd = [ + 'yosys', '-s', '{}/{}'.format(args.gen_out, + ys_translate_script_filename) + ] + + subprocess.run(yosys_cmd) + + +if __name__ == "__main__": + main() diff --git a/synth/python/flow_utils.py b/synth/python/flow_utils.py new file mode 100755 index 0000000..40efb18 --- /dev/null +++ b/synth/python/flow_utils.py @@ -0,0 +1,82 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +import csv +import os +import re +import sys + +ys_translate_script_filename = 'translate_names.ys' +ys_translated_names = 'ys_translated_names' +generated_cell_re = re.compile(r'(_\w+_)/(\w+)') +ys_tcl = './tcl/yosys_post_synth.tcl' + + +def create_translate_names_script(cells_to_translate, top_level, gen_out): + yosys_script_file = open( + '{}/{}'.format(gen_out, ys_translate_script_filename), 'w') + + yosys_script_file.write('tcl {}\n'.format(ys_tcl)) + yosys_script_file.write('cd {}\n'.format(top_level)) + + for cell in cells_to_translate: + yosys_script_file.write('select {} %x:+[Q]\n'.format(cell)) + yosys_script_file.write('tee -a {}/{} select -list\n'.format( + gen_out, ys_translated_names)) + + yosys_script_file.close() + + +def extract_path_info(timing_csv): + timing_in = csv.reader(open(timing_csv)) + + cells_to_translate = set() + path_info = [] + + for line in timing_in: + points = [] + + for i in range(2): + cell_match = generated_cell_re.search(line[i]) + if cell_match: + cells_to_translate.add(cell_match.group(1)) + points.append(cell_match.group(1)) + else: + points.append(line[i]) + + path_info.append((points[0], points[1], float(line[2]))) + + return (cells_to_translate, path_info) + + +def build_translated_names_dict(translated_names_file): + translated_names = open(translated_names_file) + translated_name_lines = translated_names.readlines() + + translated_names_dict = {} + + for i in range(0, len(translated_name_lines), 2): + translated_name = translated_name_lines[i][:-1].split('/', 1)[1] + cell_name = translated_name_lines[i + 1][:-1].split('/', 1)[1] + + translated_names_dict[cell_name] = translated_name + + return translated_names_dict + + +def translate_path_info(path_info, translated_names_file): + translated_names_dict = build_translated_names_dict(translated_names_file) + + new_path_info = [] + + for path in path_info: + translated_path = list(path) + points = path[0:1] + for i in range(2): + if path[i] in translated_names_dict: + translated_path[i] = translated_names_dict[path[i]] + + new_path_info.append(tuple(translated_path)) + + return new_path_info diff --git a/synth/python/get_kge.py b/synth/python/get_kge.py new file mode 100755 index 0000000..e0f463d --- /dev/null +++ b/synth/python/get_kge.py @@ -0,0 +1,80 @@ +#!/usr/bin/python3 + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# Parse a yosys area report and give a kGE equivalient + +import argparse + + +def read_lib(lib_file_path, ref_cell): + with open(lib_file_path, 'r') as f: + lib_file = f.readlines() + + cell_dict = {} + weighted_dict = {} + cell_name = None + for line_idx, line in enumerate(lib_file): + if line.startswith(' cell ('): + if cell_name is not None: + raise RuntimeError('{}:{} Found cell while searching for area' + .format(lib_file_path, line_idx + 1)) + cell_name = line.split()[1].strip('()') + elif line.startswith('\tarea'): + if cell_name is None: + raise RuntimeError('{}:{} Found area while searching for cell' + .format(lib_file_path, line_idx + 1)) + try: + cell_area = line.split()[2].strip(';') + cell_dict[cell_name] = float(cell_area) + cell_name = None + except (IndexError, ValueError): + raise RuntimeError('{}:{} Area declaration misformatted' + .format(lib_file_path, line_idx + 1)) + + if ref_cell not in cell_dict: + raise RuntimeError('Specified reference cell: {} was not found in ' + 'library: {}' .format(ref_cell, lib_file_path)) + + for cell in cell_dict: + weighted_dict[cell] = cell_dict[cell] / cell_dict[ref_cell] + return weighted_dict + + +def get_kge(report_path, weighted_dict): + with open(report_path, 'r') as f: + report = f.readlines() + ge = 0.0 + for line_idx, line in enumerate(report): + data = line.split() + if not data: + continue + weight = weighted_dict.get(data[0]) + if weight is not None: + try: + ge += float(data[1]) * weight + except (IndexError, ValueError): + raise RuntimeError('{}:{} Cell {} matched but was misformatted' + .format(report_path, line_idx + 1, data[0])) + print("Area in kGE = ", round(ge/1000, 2)) + + +def main(): + arg_parser = argparse.ArgumentParser( + description="""Calculate kGE from a Yosys report and LIB file""") + + arg_parser.add_argument('lib_file_path', help='Path to the LIB file') + arg_parser.add_argument('report_path', help='Path to the report') + arg_parser.add_argument('--cell', help='Reference cell (default:NAND2_X1)', + default='NAND2_X1') + + args = arg_parser.parse_args() + + weighted_dict = read_lib(args.lib_file_path, args.cell) + get_kge(args.report_path, weighted_dict) + + +if __name__ == "__main__": + main() diff --git a/synth/python/translate_timing_csv.py b/synth/python/translate_timing_csv.py new file mode 100755 index 0000000..fcac614 --- /dev/null +++ b/synth/python/translate_timing_csv.py @@ -0,0 +1,41 @@ +#!/usr/bin/python3 + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +import argparse + +from flow_utils import * + + +def main(): + arg_parser = argparse.ArgumentParser( + description= + """Translates CSV timing report to have human readable start and end + points given yosys generated name translation file (generated with + build_translated_names.py""") + + arg_parser.add_argument('rpt_file', help='Name of the CSV report file') + arg_parser.add_argument( + 'gen_out', help='Path containing generated name translation file') + + args = arg_parser.parse_args() + + (cells_to_translate, path_info) = extract_path_info(args.rpt_file) + + path_info = translate_path_info( + path_info, '{}/{}'.format(args.gen_out, ys_translated_names)) + + translated_rpt_out = open(sys.argv[1], 'w') + + translated_rpt_out.write('Start Point, End Point, WNS (ns)\n') + + for p in path_info: + translated_rpt_out.write('{},{},{}\n'.format(p[0], p[1], p[2])) + + translated_rpt_out.close() + + +if __name__ == "__main__": + main() diff --git a/synth/rtl/latch_map.v b/synth/rtl/latch_map.v new file mode 100755 index 0000000..fb1f34d --- /dev/null +++ b/synth/rtl/latch_map.v @@ -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 + +// Map latch primitives to a specific cell +module $_DLATCH_P_ (input E, input D, output Q); +DLH_X1 _TECHMAP_REPLACE_ ( +.G(E), +.D(D), +.Q(Q) +); +endmodule diff --git a/synth/rtl/prim_clock_gating.v b/synth/rtl/prim_clock_gating.v new file mode 100755 index 0000000..14d1974 --- /dev/null +++ b/synth/rtl/prim_clock_gating.v @@ -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 + +// Example clock gating module for yosys synthesis + +module prim_clock_gating ( + input clk_i, + input en_i, + input test_en_i, + output clk_o +); + + reg en_latch; + + always @* 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/synth/syn_setup.example.sh b/synth/syn_setup.example.sh new file mode 100755 index 0000000..ed56ff0 --- /dev/null +++ b/synth/syn_setup.example.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +if [ $# -eq 1 ]; then + export LR_SYNTH_OUT_DIR=$1 +elif [ $# -eq 0 ]; then + export LR_SYNTH_OUT_DIR_PREFIX="syn_out/ibex" + LR_SYNTH_OUT_DIR=$(date +"${LR_SYNTH_OUT_DIR_PREFIX}_%d_%m_%Y_%H_%M_%S") + export LR_SYNTH_OUT_DIR +else + echo "Usage $0 [synth_out_dir]" + exit 1 +fi + +export LR_SYNTH_TIMING_RUN=1 +export LR_SYNTH_FLATTEN=1 + +# SETUP CELL LIBRARY PATH +# Uncomment the lines below and set the path to an appropriate .lib file +# export LR_SYNTH_CELL_LIBRARY_PATH=/path/to/NangateOpenCellLibrary_typical.lib +# export LR_SYNTH_CELL_LIBRARY_NAME=nangate diff --git a/synth/syn_setup_nox.sh b/synth/syn_setup_nox.sh new file mode 100755 index 0000000..3c89cef --- /dev/null +++ b/synth/syn_setup_nox.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +if [ $# -eq 1 ]; then + export LR_SYNTH_OUT_DIR=$1 +elif [ $# -eq 0 ]; then + export LR_SYNTH_OUT_DIR_PREFIX="syn_out/nox" + LR_SYNTH_OUT_DIR=$(date +"${LR_SYNTH_OUT_DIR_PREFIX}_%d_%m_%Y_%H_%M_%S") + export LR_SYNTH_OUT_DIR +else + echo "Usage $0 [synth_out_dir]" + exit 1 +fi + +export LR_SYNTH_TIMING_RUN=1 +export LR_SYNTH_FLATTEN=1 + +# SETUP CELL LIBRARY PATH +# Uncomment the lines below and set the path to an appropriate .lib file +export LR_SYNTH_CELL_LIBRARY_PATH=/test/synth/pdk_45nm/flow/platforms/nangate45/lib/NangateOpenCellLibrary_typical.lib +export LR_SYNTH_CELL_LIBRARY_NAME=nangate diff --git a/synth/syn_yosys.sh b/synth/syn_yosys.sh new file mode 100755 index 0000000..9c6ae23 --- /dev/null +++ b/synth/syn_yosys.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +# This script drives the experimental Ibex synthesis flow. More details can be +# found in README.md + +set -e +set -o pipefail + +error () { + echo >&2 "$@" + exit 1 +} + +teelog () { + tee "$LR_SYNTH_OUT_DIR/log/$1.log" +} + +if [ ! -f syn_setup_nox.sh ]; then + error "No syn_setup.sh file: see README.md for instructions" +fi + +#------------------------------------------------------------------------- +# setup flow variables +#------------------------------------------------------------------------- +source syn_setup_nox.sh + +#------------------------------------------------------------------------- +# use sv2v to convert all SystemVerilog files to Verilog +#------------------------------------------------------------------------- + +LR_DEP_SOURCES=( + "prim_generic/rtl/prim_generic_buf.sv" + "prim_generic/rtl/prim_generic_flop.sv" +) + +mkdir -p "$LR_SYNTH_OUT_DIR/generated" +mkdir -p "$LR_SYNTH_OUT_DIR/log" +mkdir -p "$LR_SYNTH_OUT_DIR/reports/timing" + +# Convert dependency sources +for file in "${LR_DEP_SOURCES[@]}"; do + module=$(basename -s .sv "$file") + + sv2v \ + --define=SYNTHESIS --define=YOSYS \ + -Iprim/rtl \ + "$file" \ + > "$LR_SYNTH_OUT_DIR"/generated/"${module}".v +done + +# Convert NoX s.verilog file +#nox_core=$(find "../rtl" -iname "*.sv" -type f) + +#nox_files=(" ../bus_arch_sv_pkg/amba_axi_pkg.sv" + #" ../bus_arch_sv_pkg/amba_ahb_pkg.sv" + #" ../rtl/inc/nox_pkg.svh" + #" ../rtl/inc/core_bus_pkg.svh" + #" ../rtl/inc/riscv_pkg.svh" + #" ../rtl/inc/nox_utils_pkg.sv" +#) +#sv2v \ + #-I../rtl/inc \ + #$nox_files > nox.v + +# Convert core sources +for file in ../rtl/*.sv; do + module=$(basename -s .sv "$file") + + # Skip packages + if echo "$module" | grep -q '_pkg$'; then + continue + fi + + sv2v \ + --define=SYNTHESIS --define=YOSYS --define=NO_ASSERTIONS \ + ../rtl/inc/nox_pkg.svh \ + ../rtl/inc/core_bus_pkg.svh \ + ../rtl/inc/riscv_pkg.svh \ + ../rtl/inc/nox_utils_pkg.sv \ + ../bus_arch_sv_pkg/*_pkg.sv \ + -I../rtl/inc \ + -I../vendor/lowrisc_ip/dv/sv/dv_utils \ + "$file" \ + > "$LR_SYNTH_OUT_DIR"/generated/"${module}".v + + # Make sure auto-generated primitives are resolved to generic primitives + # where available. + #sed -i 's/prim_buf/prim_generic_buf/g' "$LR_SYNTH_OUT_DIR"/generated/"${module}".v + #sed -i 's/prim_flop/prim_generic_flop/g' "$LR_SYNTH_OUT_DIR"/generated/"${module}".v +done + +# remove tracer (not needed for synthesis) +#rm -f "$LR_SYNTH_OUT_DIR"/generated/ibex_tracer.v + +# remove the FPGA & register-based register file (because we will use the +# latch-based one instead) +#rm -f "$LR_SYNTH_OUT_DIR"/generated/ibex_register_file_ff.v +#rm -f "$LR_SYNTH_OUT_DIR"/generated/ibex_register_file_fpga.v + +yosys -c ./tcl/yosys_run_synth.tcl |& teelog syn || { + error "Failed to synthesize RTL with Yosys" +} + +sta ./tcl/sta_run_reports.tcl |& teelog sta || { + error "Failed to run static timing analysis" +} + +./translate_timing_rpts.sh + +python/get_kge.py "$LR_SYNTH_CELL_LIBRARY_PATH" "$LR_SYNTH_OUT_DIR"/reports/area.rpt diff --git a/synth/tcl/flow_utils.tcl b/synth/tcl/flow_utils.tcl new file mode 100755 index 0000000..4d68ac0 --- /dev/null +++ b/synth/tcl/flow_utils.tcl @@ -0,0 +1,124 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +proc print_lr_synth_banner {} { +puts { _ ______ _____ _____ _____ } +puts {| | | ___ \|_ _|/ ___|/ __ \ } +puts {| | ___ __ __| |_/ / | | \ `--. | / \/ } +puts {| | / _ \ \ \ /\ / /| / | | `--. \| | } +puts {| || (_) | \ V V / | |\ \ _| |_ /\__/ /| \__/\ } +puts {|_| \___/ \_/\_/ \_| \_| \___/ \____/ \____/ } +puts { } +puts { } +puts { _ _ } +puts { | | | | } +puts { ___ _ _ _ __ | |_ | |__ } +puts { / __|| | | || '_ \ | __|| '_ \ } +puts { \__ \| |_| || | | || |_ | | | | } +puts { |___/ \__, ||_| |_| \__||_| |_| } +puts { __/ | } +puts { |___/ } +puts {} +puts {****** o p e n t o t h e c o r e ******* } +puts {} +} + +proc print_yosys_banner {} { +puts { +---------------------------------+} +puts { | _ _ ___ ___ _ _ ___ |} +puts { | | | | | / _ \ / __|| | | |/ __| |} +puts { | | |_| || (_) |\__ \| |_| |\__ \ |} +puts { | \__, | \___/ |___/ \__, ||___/ |} +puts { | __/ | __/ | |} +puts { | |___/ |___/ |} +puts { +---------------------------------+} +puts {} +} + +proc print_opensta_banner {} { +puts {+--------------------------------------------------+} +puts {| _____ _____ _____ ___ |} +puts {| | _ | / ___||_ _| / _ \ |} +puts {| | | | | _ __ ___ _ __ \ `--. | | / /_\ \ |} +puts {| | | | || '_ \ / _ \| '_ \ `--. \ | | | _ | |} +puts {| \ \_/ /| |_) || __/| | | |/\__/ / | | | | | | |} +puts {| \___/ | .__/ \___||_| |_|\____/ \_/ \_| |_/ |} +puts {| | | |} +puts {| |_| |} +puts {+--------------------------------------------------+} +puts {} +} + +proc set_flow_var {var_name var_default var_friendly_name} { + global lr_synth_flow_var_quiet + + set var_name "lr_synth_${var_name}" + global $var_name + set env_var_name [string toupper $var_name] + + if { [info exists ::env($env_var_name)] } { + set $var_name $::env($env_var_name) + puts "$var_friendly_name: $::env($env_var_name)" + } else { + set $var_name $var_default + puts "$var_friendly_name: $var_default (default)" + } +} + +proc set_flow_bool_var {var_name var_default var_friendly_name} { + global lr_synth_flow_var_quiet + + set var_name "lr_synth_${var_name}" + global $var_name + set env_var_name [string toupper $var_name] + + if { [info exists ::env($env_var_name)] } { + if { $::env($env_var_name) } { + set $var_name 1 + } else { + set $var_name 0 + } + } else { + set $var_name $var_default + } + + if { [set $var_name] } { + puts "$var_friendly_name: Enabled" + } else { + puts "$var_friendly_name: Disabled" + } +} + +proc write_sdc_out {sdc_file_in sdc_file_out} { + global lr_synth_clk_period + global lr_synth_clk_input + global lr_synth_outputs + global lr_synth_inputs + + set sdc_out [open $sdc_file_out "w"] + set sdc_in [open $sdc_file_in "r"] + + set sdc_in_contents [read $sdc_in] + puts $sdc_out $sdc_in_contents + + puts $sdc_out "#============ Auto-generated from config ============" + + set clk_period_ns [expr $lr_synth_clk_period / 1000.0] + puts $sdc_out "create_clock -name $lr_synth_clk_input -period $clk_period_ns {$lr_synth_clk_input}" + + foreach output $lr_synth_outputs { + set output_constraint [lindex $output 1] + set output_constraint [expr (1.0 - ($output_constraint / 100.0)) * $clk_period_ns] + puts $sdc_out "set_output_delay -clock $lr_synth_clk_input $output_constraint [lindex $output 0]" + } + + foreach input $lr_synth_inputs { + set input_constraint [lindex $input 1] + set input_constraint [expr ($input_constraint / 100.0) * $clk_period_ns] + puts $sdc_out "set_input_delay -clock $lr_synth_clk_input $input_constraint [lindex $input 0]" + } + + close $sdc_out + close $sdc_in +} diff --git a/synth/tcl/lr_synth_flow_var_setup.tcl b/synth/tcl/lr_synth_flow_var_setup.tcl new file mode 100755 index 0000000..057e73e --- /dev/null +++ b/synth/tcl/lr_synth_flow_var_setup.tcl @@ -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 + +puts "=================== Flow Vars ===================" + +set_flow_var cell_library_path "cmos_cells.lib" "Path to cell library" +set_flow_var top_module "nox" "top module" +set_flow_var out_dir "syn_out" "Output directory for synthesis" +set_flow_var pre_map_out "./${lr_synth_out_dir}/generated/${lr_synth_top_module}.pre_map.v" "Pre-mapping netlist out" +set_flow_var netlist_out "./${lr_synth_out_dir}/generated/${lr_synth_top_module}_netlist.v" "netlist out" +set_flow_var config_file "${lr_synth_top_module}_lr_synth_conf.tcl" "Synth config file" +set_flow_var rpt_out "./${lr_synth_out_dir}/reports" "Report output directory" +set_flow_bool_var flatten 1 "flatten" +set_flow_bool_var timing_run 0 "timing run" +set_flow_bool_var ibex_branch_target_alu 0 "Enable branch target ALU in Ibex" +set_flow_bool_var ibex_writeback_stage 0 "Enable writeback stage in Ibex" +set_flow_var ibex_bitmanip 0 "Bitmanip extenion setting for Ibex (see ibex_pkg::rv32b_e for permitted values. Enum names are not supported in Yosys.)" +set_flow_var ibex_multiplier 2 "Multiplier extension setting for Ibex (see ibex_pkg::rv32m_e for permitted values. Enum names are not supported in Yosys.)" +set_flow_var ibex_regfile 2 "Register file implementation selection for Ibex (see ibex_pkg::regfile_e for permitted values. Enum names are not supported in Yosys.)" + +source $lr_synth_config_file + +if { $lr_synth_timing_run } { + set_flow_var cell_library_name "nangate" "Name of cell library" + #set_flow_var sdc_file "${top_module}.sdc" "SDC file" + set_flow_var sdc_file_in "${lr_synth_top_module}.${lr_synth_cell_library_name}.sdc" "Input SDC file" + set_flow_var abc_sdc_file_in "${lr_synth_top_module}_abc.${lr_synth_cell_library_name}.sdc" "Input SDC file for ABC" + + set sdc_file_out_default [string range $lr_synth_sdc_file_in 0 [expr [string last ".sdc" $lr_synth_sdc_file_in] - 1]] + set sdc_file_out_default "./${lr_synth_out_dir}/generated/$sdc_file_out_default.out.sdc" + set_flow_var sdc_file_out $sdc_file_out_default "Output SDC file" + + set sta_netlist_out_default [string range $lr_synth_netlist_out 0 [expr [string last ".v" $lr_synth_netlist_out] - 1]] + set sta_netlist_out_default "$sta_netlist_out_default.sta.v" + set_flow_var sta_netlist_out $sta_netlist_out_default "STA netlist out" + set_flow_var sta_paths_per_group 1000 "STA paths reported per group" + set_flow_var sta_overall_paths 1000 "STA paths reported in overall report" + puts "clock period: $lr_synth_clk_period ps" + + if { $lr_synth_abc_clk_uprate > $lr_synth_clk_period } { + puts "WARNING: abc_clk_uprate must be less than clk_period otherwise ABC will be given a negative clk period" + } + +} + +puts "=================================================" diff --git a/synth/tcl/sta_common.tcl b/synth/tcl/sta_common.tcl new file mode 100755 index 0000000..f6d6fd9 --- /dev/null +++ b/synth/tcl/sta_common.tcl @@ -0,0 +1,18 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +source ./tcl/flow_utils.tcl +source ./tcl/lr_synth_flow_var_setup.tcl + +source ./tcl/sta_utils.tcl + +print_lr_synth_banner +print_opensta_banner + +read_liberty $lr_synth_cell_library_path +read_verilog $lr_synth_sta_netlist_out +link_design $lr_synth_top_module + +read_sdc $lr_synth_sdc_file_out + diff --git a/synth/tcl/sta_open_design.tcl b/synth/tcl/sta_open_design.tcl new file mode 100755 index 0000000..2e17195 --- /dev/null +++ b/synth/tcl/sta_open_design.tcl @@ -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 + +source ./tcl/sta_common.tcl + diff --git a/synth/tcl/sta_run_reports.tcl b/synth/tcl/sta_run_reports.tcl new file mode 100755 index 0000000..12d8e8c --- /dev/null +++ b/synth/tcl/sta_run_reports.tcl @@ -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 + +source ./tcl/sta_common.tcl + +set overall_rpt_file "${lr_synth_rpt_out}/timing/overall" +timing_report $lr_synth_clk_input $overall_rpt_file $lr_synth_sta_overall_paths + +set lr_synth_path_group_list [list] + +setup_path_groups $lr_synth_inputs $lr_synth_outputs lr_synth_path_group_list + +foreach path_group $lr_synth_path_group_list { + puts $path_group + set path_group_rpt_file "${lr_synth_rpt_out}/timing/$path_group" + timing_report $path_group $path_group_rpt_file $lr_synth_sta_paths_per_group +} + +exit + diff --git a/synth/tcl/sta_utils.tcl b/synth/tcl/sta_utils.tcl new file mode 100755 index 0000000..bdef3a1 --- /dev/null +++ b/synth/tcl/sta_utils.tcl @@ -0,0 +1,51 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +proc setup_path_groups {input_list output_list path_group_list_name} { + upvar $path_group_list_name path_group_list + + set flops_in [all_registers -edge_triggered -data_pins] + set flops_out [all_registers -edge_triggered -clock_pins] + group_path -name reg2reg -from $flops_out -to $flops_in + lappend path_group_list reg2reg + + foreach output $output_list { + set output_name [lindex $output 0] + lappend outputs_list [get_ports $output_name] + } + group_path -name reg2out -from $flops_out -to $outputs_list + lappend path_group_list reg2out + + foreach input $input_list { + set input_name [lindex $input 0] + lappend inputs_list [get_ports $input_name] + } + group_path -name in2reg -from $inputs_list -to $flops_in + lappend path_group_list in2reg + + group_path -name in2out -from $inputs_list -to $outputs_list + lappend path_group_list in2out + +} + +proc timing_report {path_group rpt_out path_count} { + set sta_report_out_filename "${rpt_out}.rpt" + set sta_csv_out_filename "${rpt_out}.csv.rpt" + + puts "Reporting $path_group to $sta_report_out_filename and $sta_csv_out_filename" + report_checks -group_count $path_count -path_group $path_group > $sta_report_out_filename + + set paths [find_timing_paths -group_count $path_count -path_group $path_group] + set sta_csv_out [open $sta_csv_out_filename "w"] + + foreach path $paths { + set startpoint_name [get_property [get_property $path startpoint] full_name] + set endpoint_name [get_property [get_property $path endpoint] full_name] + set slack [get_property $path slack] + puts $sta_csv_out [format "$startpoint_name,$endpoint_name,%.4f" $slack] + } + + close $sta_csv_out +} + diff --git a/synth/tcl/yosys_common.tcl b/synth/tcl/yosys_common.tcl new file mode 100755 index 0000000..b0d5669 --- /dev/null +++ b/synth/tcl/yosys_common.tcl @@ -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 + +source ./tcl/flow_utils.tcl + +print_lr_synth_banner +print_yosys_banner + +source ./tcl/lr_synth_flow_var_setup.tcl + +yosys "read_liberty -lib $lr_synth_cell_library_path" diff --git a/synth/tcl/yosys_post_synth.tcl b/synth/tcl/yosys_post_synth.tcl new file mode 100755 index 0000000..e8a705f --- /dev/null +++ b/synth/tcl/yosys_post_synth.tcl @@ -0,0 +1,10 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +source ./tcl/yosys_common.tcl + +yosys "read_liberty -lib $lr_synth_cell_library_path" +yosys "read_verilog $lr_synth_netlist_out" + + diff --git a/synth/tcl/yosys_pre_map.tcl b/synth/tcl/yosys_pre_map.tcl new file mode 100755 index 0000000..44f6e75 --- /dev/null +++ b/synth/tcl/yosys_pre_map.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 + +source ./tcl/yosys_common.tcl + +yosys "read_liberty -lib $lr_synth_cell_library_path" +yosys "read_verilog $lr_synth_pre_map_out" + diff --git a/synth/tcl/yosys_run_synth.tcl b/synth/tcl/yosys_run_synth.tcl new file mode 100755 index 0000000..c7af91d --- /dev/null +++ b/synth/tcl/yosys_run_synth.tcl @@ -0,0 +1,67 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +source ./tcl/yosys_common.tcl + +if { $lr_synth_flatten } { + set flatten_opt "-flatten" +} else { + set flatten_opt "" +} + +if { $lr_synth_timing_run } { + write_sdc_out $lr_synth_sdc_file_in $lr_synth_sdc_file_out +} + +yosys "read_verilog -defer -sv ./rtl/prim_clock_gating.v $lr_synth_out_dir/generated/*.v" + +#if { $lr_synth_ibex_branch_target_alu } { +# yosys "chparam -set BranchTargetALU 1 $lr_synth_top_module" +#} +# +#if { $lr_synth_ibex_writeback_stage } { +# yosys "chparam -set WritebackStage 1 $lr_synth_top_module" +#} +# +#yosys "chparam -set RV32B $lr_synth_ibex_bitmanip $lr_synth_top_module" +# +#yosys "chparam -set RV32M $lr_synth_ibex_multiplier $lr_synth_top_module" +# +#yosys "chparam -set RegFile $lr_synth_ibex_regfile $lr_synth_top_module" + +yosys "synth $flatten_opt -top $lr_synth_top_module" +yosys "opt -purge" + +yosys "write_verilog $lr_synth_pre_map_out" + +# Map latch primitives onto latch cells +yosys "techmap -map rtl/latch_map.v" + +yosys "dfflibmap -liberty $lr_synth_cell_library_path" +yosys "opt" + +set yosys_abc_clk_period [expr $lr_synth_clk_period - $lr_synth_abc_clk_uprate] + +if { $lr_synth_timing_run } { + yosys "abc -liberty $lr_synth_cell_library_path -constr $lr_synth_abc_sdc_file_in -D $yosys_abc_clk_period" +} else { + yosys "abc -liberty $lr_synth_cell_library_path" +} + +yosys "clean" +yosys "write_verilog $lr_synth_netlist_out" + +if { $lr_synth_timing_run } { + # Produce netlist that OpenSTA can use + yosys "setundef -zero" + yosys "splitnets" + yosys "clean" + yosys "write_verilog -noattr -noexpr -nohex -nodec $lr_synth_sta_netlist_out" +} + +yosys "check" +yosys "log ======== Yosys Stat Report ========" +yosys "tee -o $lr_synth_out_dir/reports/area.rpt stat -liberty $lr_synth_cell_library_path" +yosys "log ====== End Yosys Stat Report ======" + diff --git a/synth/translate_timing_rpts.sh b/synth/translate_timing_rpts.sh new file mode 100755 index 0000000..f402f3e --- /dev/null +++ b/synth/translate_timing_rpts.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +./python/build_translated_names.py nox ./"$LR_SYNTH_OUT_DIR"/generated ./"$LR_SYNTH_OUT_DIR"/reports/timing/*.csv.rpt + +for file in ./"$LR_SYNTH_OUT_DIR"/reports/timing/*.csv.rpt; do + ./python/translate_timing_csv.py "$file" ./"$LR_SYNTH_OUT_DIR"/generated +done +