| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Life cycle controller top. |
| // |
| |
| `include "prim_assert.sv" |
| |
| module lc_ctrl |
| import lc_ctrl_pkg::*; |
| import lc_ctrl_reg_pkg::*; |
| import lc_ctrl_state_pkg::*; |
| #( |
| // Enable asynchronous transitions on alerts. |
| parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}, |
| // Hardware revision number. |
| parameter logic [15:0] ChipGen = 16'hFFFF, |
| parameter logic [15:0] ChipRev = 16'hFFFF, |
| // Idcode value for the JTAG. |
| parameter logic [31:0] IdcodeValue = 32'h00000001, |
| // Random netlist constants |
| parameter lc_keymgr_div_t RndCnstLcKeymgrDivInvalid = LcKeymgrDivWidth'(0), |
| parameter lc_keymgr_div_t RndCnstLcKeymgrDivTestDevRma = LcKeymgrDivWidth'(1), |
| parameter lc_keymgr_div_t RndCnstLcKeymgrDivProduction = LcKeymgrDivWidth'(2), |
| parameter lc_token_mux_t RndCnstInvalidTokens = {TokenMuxBits{1'b1}} |
| ) ( |
| // Life cycle controller clock |
| input clk_i, |
| input rst_ni, |
| // Clock for KMAC interface |
| input clk_kmac_i, |
| input rst_kmac_ni, |
| // Bus Interface (device) |
| input tlul_pkg::tl_h2d_t tl_i, |
| output tlul_pkg::tl_d2h_t tl_o, |
| // JTAG TAP. |
| input jtag_pkg::jtag_req_t jtag_i, |
| output jtag_pkg::jtag_rsp_t jtag_o, |
| // This bypasses the clock inverter inside the JTAG TAP for scanmmode. |
| input scan_rst_ni, |
| input prim_mubi_pkg::mubi4_t scanmode_i, |
| // Alert outputs. |
| input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i, |
| output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o, |
| // Escalation inputs (severity 1 and 2). |
| // These need not be synchronized since the alert handler is |
| // in the same clock domain as the LC controller. |
| input prim_esc_pkg::esc_rx_t esc_scrap_state0_tx_i, |
| output prim_esc_pkg::esc_tx_t esc_scrap_state0_rx_o, |
| input prim_esc_pkg::esc_rx_t esc_scrap_state1_tx_i, |
| output prim_esc_pkg::esc_tx_t esc_scrap_state1_rx_o, |
| // Power manager interface (inputs are synced to lifecycle clock domain). |
| input pwrmgr_pkg::pwr_lc_req_t pwr_lc_i, |
| output pwrmgr_pkg::pwr_lc_rsp_t pwr_lc_o, |
| // Macro-specific test registers going to lifecycle TAP |
| output otp_ctrl_pkg::lc_otp_vendor_test_req_t lc_otp_vendor_test_o, |
| input otp_ctrl_pkg::lc_otp_vendor_test_rsp_t lc_otp_vendor_test_i, |
| // Life cycle transition command interface. |
| // No sync required since LC and OTP are in the same clock domain. |
| output otp_ctrl_pkg::lc_otp_program_req_t lc_otp_program_o, |
| input otp_ctrl_pkg::lc_otp_program_rsp_t lc_otp_program_i, |
| // Life cycle hashing interface for raw unlock |
| // Synchronized in the life cycle controller. |
| // SEC_CM: TOKEN.DIGEST |
| input kmac_pkg::app_rsp_t kmac_data_i, |
| output kmac_pkg::app_req_t kmac_data_o, |
| // OTP broadcast outputs |
| // No sync required since LC and OTP are in the same clock domain. |
| // SEC_CM: TOKEN_VALID.CTRL.MUBI |
| input otp_ctrl_pkg::otp_lc_data_t otp_lc_data_i, |
| // Life cycle broadcast outputs (all of them are registered). |
| // SEC_CM: INTERSIG.MUBI |
| output lc_tx_t lc_dft_en_o, |
| output lc_tx_t lc_nvm_debug_en_o, |
| output lc_tx_t lc_hw_debug_en_o, |
| output lc_tx_t lc_cpu_en_o, |
| output lc_tx_t lc_creator_seed_sw_rw_en_o, |
| output lc_tx_t lc_owner_seed_sw_rw_en_o, |
| output lc_tx_t lc_iso_part_sw_rd_en_o, |
| output lc_tx_t lc_iso_part_sw_wr_en_o, |
| output lc_tx_t lc_seed_hw_rd_en_o, |
| output lc_tx_t lc_keymgr_en_o, |
| output lc_tx_t lc_escalate_en_o, |
| output lc_tx_t lc_check_byp_en_o, |
| // Request and feedback to/from clock manager and AST. |
| // The ack is synced to the lc clock domain using prim_lc_sync. |
| // SEC_CM: INTERSIG.MUBI |
| output lc_tx_t lc_clk_byp_req_o, |
| input lc_tx_t lc_clk_byp_ack_i, |
| // Request and feedback to/from flash controller. |
| // The ack is synced to the lc clock domain using prim_lc_sync. |
| output lc_flash_rma_seed_t lc_flash_rma_seed_o, |
| // SEC_CM: INTERSIG.MUBI |
| output lc_tx_t lc_flash_rma_req_o, |
| input lc_tx_t lc_flash_rma_ack_i, |
| // State group diversification value for keymgr. |
| output lc_keymgr_div_t lc_keymgr_div_o, |
| // Hardware config input, needed for the DEVICE_ID field. |
| input otp_ctrl_pkg::otp_device_id_t otp_device_id_i, |
| // Hardware config input, needed for the MANUF_STATE field. |
| input otp_ctrl_pkg::otp_device_id_t otp_manuf_state_i, |
| // Hardware revision output (static) |
| output lc_hw_rev_t hw_rev_o |
| ); |
| |
| import prim_mubi_pkg::mubi8_t; |
| import prim_mubi_pkg::MuBi8False; |
| import prim_mubi_pkg::mubi8_test_true_strict; |
| import prim_mubi_pkg::mubi8_test_false_loose; |
| |
| //////////////////////// |
| // Integration Checks // |
| //////////////////////// |
| |
| // Check that the CSR parameters correspond with the ones used in the design. |
| `ASSERT_INIT(DecLcStateWidthCheck_A, CsrLcStateWidth == ExtDecLcStateWidth) |
| `ASSERT_INIT(DecLcCountWidthCheck_A, CsrLcCountWidth == DecLcCountWidth) |
| `ASSERT_INIT(DecLcIdStateWidthCheck_A, CsrLcIdStateWidth == ExtDecLcIdStateWidth) |
| `ASSERT_INIT(NumTokenWordsCheck_A, NumTokenWords == LcTokenWidth/32) |
| `ASSERT_INIT(OtpTestCtrlWidth_A, otp_ctrl_pkg::OtpTestCtrlWidth == CsrOtpTestCtrlWidth) |
| `ASSERT_INIT(HwRevFieldWidth_A, HwRevFieldWidth <= 16) |
| |
| ///////////// |
| // Regfile // |
| ///////////// |
| |
| lc_ctrl_reg_pkg::lc_ctrl_reg2hw_t reg2hw; |
| lc_ctrl_reg_pkg::lc_ctrl_hw2reg_t hw2reg; |
| |
| // SEC_CM: TRANSITION.CONFIG.REGWEN, STATE.CONFIG.SPARSE |
| logic fatal_bus_integ_error_q, fatal_bus_integ_error_d; |
| lc_ctrl_reg_top u_reg ( |
| .clk_i, |
| .rst_ni, |
| .tl_i, |
| .tl_o, |
| .reg2hw ( reg2hw ), |
| .hw2reg ( hw2reg ), |
| // SEC_CM: BUS.INTEGRITY |
| .intg_err_o( fatal_bus_integ_error_d ), |
| .devmode_i ( 1'b1 ) |
| ); |
| |
| //////////////////// |
| // Life Cycle TAP // |
| //////////////////// |
| |
| tlul_pkg::tl_h2d_t tap_tl_h2d; |
| tlul_pkg::tl_d2h_t tap_tl_d2h; |
| lc_ctrl_reg_pkg::lc_ctrl_reg2hw_t tap_reg2hw; |
| lc_ctrl_reg_pkg::lc_ctrl_hw2reg_t tap_hw2reg; |
| |
| lc_ctrl_reg_top u_reg_tap ( |
| .clk_i, |
| .rst_ni, |
| .tl_i ( tap_tl_h2d ), |
| .tl_o ( tap_tl_d2h ), |
| .reg2hw ( tap_reg2hw ), |
| .hw2reg ( tap_hw2reg ), |
| // The JTAG TAP does not support bus integrity. |
| .intg_err_o( ), |
| .devmode_i ( 1'b1 ) |
| ); |
| |
| |
| // This reuses the JTAG DTM and DMI from the RISC-V external |
| // debug v0.13 specification to read and write the lc_ctrl CSRs: |
| // https://github.com/riscv/riscv-debug-spec/blob/release/riscv-debug-release.pdf |
| // The register addresses correspond to the byte offsets of the lc_ctrl CSRs, divided by 4. |
| // Note that the DMI reset does not affect the LC controller in any way. |
| dm::dmi_req_t dmi_req; |
| logic dmi_req_valid; |
| logic dmi_req_ready; |
| dm::dmi_resp_t dmi_resp; |
| logic dmi_resp_ready; |
| logic dmi_resp_valid; |
| |
| logic scanmode; |
| prim_mubi4_dec u_prim_mubi4_dec ( |
| .mubi_i(scanmode_i), |
| .mubi_dec_o(scanmode) |
| ); |
| |
| logic tck_muxed; |
| logic trst_n_muxed; |
| prim_clock_mux2 #( |
| .NoFpgaBufG(1'b1) |
| ) u_prim_clock_mux2 ( |
| .clk0_i(jtag_i.tck), |
| .clk1_i(clk_i), |
| .sel_i (scanmode), |
| .clk_o (tck_muxed) |
| ); |
| |
| prim_clock_mux2 #( |
| .NoFpgaBufG(1'b1) |
| ) u_prim_rst_n_mux2 ( |
| .clk0_i(jtag_i.trst_n), |
| .clk1_i(scan_rst_ni), |
| .sel_i (scanmode), |
| .clk_o (trst_n_muxed) |
| ); |
| |
| logic req_ready; |
| assign req_ready = dmi_req_ready & dmi_resp_ready; |
| dmi_jtag #( |
| .IdcodeValue(IdcodeValue) |
| ) u_dmi_jtag ( |
| .clk_i, |
| .rst_ni, |
| .testmode_i ( scanmode ), |
| .test_rst_ni ( scan_rst_ni ), |
| .dmi_rst_no ( ), // unused |
| .dmi_req_o ( dmi_req ), |
| .dmi_req_valid_o ( dmi_req_valid ), |
| // unless there is room for response, stall |
| .dmi_req_ready_i ( req_ready ), |
| .dmi_resp_i ( dmi_resp ), |
| .dmi_resp_ready_o ( dmi_resp_ready ), |
| .dmi_resp_valid_i ( dmi_resp_valid ), |
| .tck_i ( tck_muxed ), |
| .tms_i ( jtag_i.tms ), |
| .trst_ni ( trst_n_muxed ), |
| .td_i ( jtag_i.tdi ), |
| .td_o ( jtag_o.tdo ), |
| .tdo_oe_o ( jtag_o.tdo_oe ) |
| ); |
| |
| // DMI to TL-UL transducing |
| tlul_adapter_host #( |
| .EnableDataIntgGen(1) |
| ) u_tap_tlul_host ( |
| .clk_i, |
| .rst_ni, |
| // do not make a request unless there is room for the response |
| .req_i ( dmi_req_valid & dmi_resp_ready ), |
| .gnt_o ( dmi_req_ready ), |
| .addr_i ( top_pkg::TL_AW'({dmi_req.addr, 2'b00}) ), |
| .we_i ( dmi_req.op == dm::DTM_WRITE ), |
| .wdata_i ( dmi_req.data ), |
| .wdata_intg_i ('0 ), |
| .be_i ( {top_pkg::TL_DBW{1'b1}} ), |
| .instr_type_i ( prim_mubi_pkg::MuBi4False ), |
| .valid_o ( dmi_resp_valid ), |
| .rdata_o ( dmi_resp.data ), |
| .rdata_intg_o ( ), |
| .err_o ( ), |
| .intg_err_o ( ), |
| .tl_o ( tap_tl_h2d ), |
| .tl_i ( tap_tl_d2h ) |
| ); |
| |
| // TL-UL to DMI transducing |
| assign dmi_resp.resp = '0; // unused inside dmi_jtag |
| |
| // These signals are unused |
| logic unused_tap_tl_d2h; |
| assign unused_tap_tl_d2h = ^{ |
| tap_tl_d2h.d_opcode, |
| tap_tl_d2h.d_param, |
| tap_tl_d2h.d_size, |
| tap_tl_d2h.d_source, |
| tap_tl_d2h.d_sink, |
| tap_tl_d2h.d_user, |
| tap_tl_d2h.d_error |
| }; |
| |
| /////////////////////////////////////// |
| // Transition Interface and HW Mutex // |
| /////////////////////////////////////// |
| |
| // All registers are HWext |
| logic trans_success_d, trans_success_q; |
| logic trans_cnt_oflw_error_d, trans_cnt_oflw_error_q; |
| logic trans_invalid_error_d, trans_invalid_error_q; |
| logic token_invalid_error_d, token_invalid_error_q; |
| logic flash_rma_error_d, flash_rma_error_q; |
| logic otp_prog_error_d, fatal_prog_error_q; |
| logic state_invalid_error_d, fatal_state_error_q; |
| logic otp_part_error_q; |
| mubi8_t sw_claim_transition_if_d, sw_claim_transition_if_q; |
| mubi8_t tap_claim_transition_if_d, tap_claim_transition_if_q; |
| logic transition_cmd; |
| lc_token_t transition_token_d, transition_token_q; |
| ext_dec_lc_state_t transition_target_d, transition_target_q; |
| // No need to register these. |
| ext_dec_lc_state_t dec_lc_state; |
| dec_lc_cnt_t dec_lc_cnt; |
| dec_lc_id_state_e dec_lc_id_state; |
| |
| logic lc_idle_d; |
| |
| // Assign hardware revision output |
| assign hw_rev_o = '{chip_gen: ChipGen, chip_rev: ChipRev}; |
| |
| // OTP Vendor control bits |
| logic use_ext_clock_d, use_ext_clock_q; |
| logic [CsrOtpTestCtrlWidth-1:0] otp_vendor_test_ctrl_d, otp_vendor_test_ctrl_q; |
| logic [CsrOtpTestStatusWidth-1:0] otp_vendor_test_status; |
| |
| always_comb begin : p_csr_assign_outputs |
| hw2reg = '0; |
| hw2reg.status.ready = lc_idle_d; |
| hw2reg.status.transition_successful = trans_success_q; |
| hw2reg.status.transition_count_error = trans_cnt_oflw_error_q; |
| hw2reg.status.transition_error = trans_invalid_error_q; |
| hw2reg.status.token_error = token_invalid_error_q; |
| hw2reg.status.flash_rma_error = flash_rma_error_q; |
| hw2reg.status.otp_error = fatal_prog_error_q; |
| hw2reg.status.state_error = fatal_state_error_q; |
| hw2reg.status.otp_partition_error = otp_part_error_q; |
| hw2reg.status.bus_integ_error = fatal_bus_integ_error_q; |
| hw2reg.lc_state = dec_lc_state; |
| hw2reg.lc_transition_cnt = dec_lc_cnt; |
| hw2reg.lc_id_state = {DecLcIdStateNumRep{dec_lc_id_state}}; |
| hw2reg.device_id = otp_device_id_i; |
| hw2reg.manuf_state = otp_manuf_state_i; |
| hw2reg.hw_rev.chip_gen = hw_rev_o.chip_gen; |
| hw2reg.hw_rev.chip_rev = hw_rev_o.chip_rev; |
| |
| // The assignments above are identical for the TAP. |
| tap_hw2reg = hw2reg; |
| |
| // Assignments gated by mutex. Again, the TAP has priority. |
| tap_hw2reg.claim_transition_if = tap_claim_transition_if_q; |
| hw2reg.claim_transition_if = sw_claim_transition_if_q; |
| if (mubi8_test_true_strict(tap_claim_transition_if_q)) begin |
| tap_hw2reg.transition_ctrl = use_ext_clock_q; |
| tap_hw2reg.transition_token = transition_token_q; |
| tap_hw2reg.transition_target = transition_target_q; |
| // SEC_CM: TRANSITION.CONFIG.REGWEN |
| tap_hw2reg.transition_regwen = lc_idle_d; |
| tap_hw2reg.otp_vendor_test_ctrl = otp_vendor_test_ctrl_q; |
| tap_hw2reg.otp_vendor_test_status = otp_vendor_test_status; |
| end else if (mubi8_test_true_strict(sw_claim_transition_if_q)) begin |
| hw2reg.transition_ctrl = use_ext_clock_q; |
| hw2reg.transition_token = transition_token_q; |
| hw2reg.transition_target = transition_target_q; |
| // SEC_CM: TRANSITION.CONFIG.REGWEN |
| hw2reg.transition_regwen = lc_idle_d; |
| hw2reg.otp_vendor_test_ctrl = otp_vendor_test_ctrl_q; |
| hw2reg.otp_vendor_test_status = otp_vendor_test_status; |
| end |
| end |
| |
| always_comb begin : p_csr_assign_inputs |
| sw_claim_transition_if_d = sw_claim_transition_if_q; |
| tap_claim_transition_if_d = tap_claim_transition_if_q; |
| transition_token_d = transition_token_q; |
| transition_target_d = transition_target_q; |
| transition_cmd = 1'b0; |
| otp_vendor_test_ctrl_d = otp_vendor_test_ctrl_q; |
| use_ext_clock_d = use_ext_clock_q; |
| |
| // Note that the mutex claims from the TAP and SW side could arrive within the same cycle. |
| // In that case we give priority to the TAP mutex claim in order to avoid a race condition. |
| // TAP mutex claim. |
| if (mubi8_test_false_loose(sw_claim_transition_if_q) && |
| tap_reg2hw.claim_transition_if.qe) begin |
| tap_claim_transition_if_d = mubi8_t'(tap_reg2hw.claim_transition_if.q); |
| // SW mutex claim. |
| end else if (mubi8_test_false_loose(tap_claim_transition_if_q) && |
| reg2hw.claim_transition_if.qe) begin |
| sw_claim_transition_if_d = mubi8_t'(reg2hw.claim_transition_if.q); |
| end |
| |
| |
| // The idle signal serves as the REGWEN in this case. |
| if (lc_idle_d) begin |
| // The TAP has priority. |
| if (mubi8_test_true_strict(tap_claim_transition_if_q)) begin |
| transition_cmd = tap_reg2hw.transition_cmd.q & |
| tap_reg2hw.transition_cmd.qe; |
| |
| if (tap_reg2hw.transition_ctrl.qe) begin |
| use_ext_clock_d |= tap_reg2hw.transition_ctrl.q; |
| end |
| |
| for (int k = 0; k < LcTokenWidth/32; k++) begin |
| if (tap_reg2hw.transition_token[k].qe) begin |
| transition_token_d[k*32 +: 32] = tap_reg2hw.transition_token[k].q; |
| end |
| end |
| |
| if (tap_reg2hw.transition_target.qe) begin |
| for (int k = 0; k < DecLcStateNumRep; k++) begin |
| transition_target_d[k] = dec_lc_state_e'( |
| tap_reg2hw.transition_target.q[k*DecLcStateWidth +: DecLcStateWidth]); |
| end |
| end |
| |
| if (tap_reg2hw.otp_vendor_test_ctrl.qe) begin |
| otp_vendor_test_ctrl_d = tap_reg2hw.otp_vendor_test_ctrl.q; |
| end |
| end else if (mubi8_test_true_strict(sw_claim_transition_if_q)) begin |
| transition_cmd = reg2hw.transition_cmd.q & |
| reg2hw.transition_cmd.qe; |
| |
| if (reg2hw.transition_ctrl.qe) begin |
| use_ext_clock_d |= reg2hw.transition_ctrl.q; |
| end |
| |
| for (int k = 0; k < LcTokenWidth/32; k++) begin |
| if (reg2hw.transition_token[k].qe) begin |
| transition_token_d[k*32 +: 32] = reg2hw.transition_token[k].q; |
| end |
| end |
| |
| if (reg2hw.transition_target.qe) begin |
| for (int k = 0; k < DecLcStateNumRep; k++) begin |
| transition_target_d[k] = dec_lc_state_e'( |
| reg2hw.transition_target.q[k*DecLcStateWidth +: DecLcStateWidth]); |
| end |
| end |
| |
| if (reg2hw.otp_vendor_test_ctrl.qe) begin |
| otp_vendor_test_ctrl_d = reg2hw.otp_vendor_test_ctrl.q; |
| end |
| end |
| end |
| end |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : p_csrs |
| if (!rst_ni) begin |
| trans_success_q <= 1'b0; |
| trans_cnt_oflw_error_q <= 1'b0; |
| trans_invalid_error_q <= 1'b0; |
| token_invalid_error_q <= 1'b0; |
| flash_rma_error_q <= 1'b0; |
| fatal_prog_error_q <= 1'b0; |
| fatal_state_error_q <= 1'b0; |
| sw_claim_transition_if_q <= MuBi8False; |
| tap_claim_transition_if_q <= MuBi8False; |
| transition_token_q <= '0; |
| transition_target_q <= {DecLcStateNumRep{DecLcStRaw}}; |
| otp_part_error_q <= 1'b0; |
| fatal_bus_integ_error_q <= 1'b0; |
| otp_vendor_test_ctrl_q <= '0; |
| use_ext_clock_q <= 1'b0; |
| end else begin |
| // All status and error bits are terminal and require a reset cycle. |
| trans_success_q <= trans_success_d | trans_success_q; |
| trans_cnt_oflw_error_q <= trans_cnt_oflw_error_d | trans_cnt_oflw_error_q; |
| trans_invalid_error_q <= trans_invalid_error_d | trans_invalid_error_q; |
| token_invalid_error_q <= token_invalid_error_d | token_invalid_error_q; |
| flash_rma_error_q <= flash_rma_error_d | flash_rma_error_q; |
| fatal_prog_error_q <= otp_prog_error_d | fatal_prog_error_q; |
| fatal_state_error_q <= state_invalid_error_d | fatal_state_error_q; |
| otp_part_error_q <= otp_lc_data_i.error | otp_part_error_q; |
| fatal_bus_integ_error_q <= fatal_bus_integ_error_d | fatal_bus_integ_error_q; |
| // Other regs, gated by mutex further below. |
| sw_claim_transition_if_q <= sw_claim_transition_if_d; |
| tap_claim_transition_if_q <= tap_claim_transition_if_d; |
| transition_token_q <= transition_token_d; |
| transition_target_q <= transition_target_d; |
| otp_vendor_test_ctrl_q <= otp_vendor_test_ctrl_d; |
| use_ext_clock_q <= use_ext_clock_d; |
| end |
| end |
| |
| assign lc_flash_rma_seed_o = transition_token_q[RmaSeedWidth-1:0]; |
| |
| // Gate the the vendor specific test ctrl/status bits to zero in production states. |
| // Buffer the enable signal to prevent optimization of the multibit signal. |
| lc_tx_t lc_raw_test_rma; |
| lc_tx_t [1:0] lc_raw_test_rma_buf; |
| prim_lc_sync #( |
| .NumCopies(2), |
| .AsyncOn(0) |
| ) u_prim_lc_sync ( |
| .clk_i, |
| .rst_ni, |
| .lc_en_i(lc_raw_test_rma), |
| .lc_en_o(lc_raw_test_rma_buf) |
| ); |
| |
| assign lc_otp_vendor_test_o.ctrl = (lc_raw_test_rma_buf[0] == On) ? |
| otp_vendor_test_ctrl_q : '0; |
| assign otp_vendor_test_status = (lc_raw_test_rma_buf[1] == On) ? |
| lc_otp_vendor_test_i.status : '0; |
| |
| ////////////////// |
| // Alert Sender // |
| ////////////////// |
| |
| logic [NumAlerts-1:0] alerts; |
| logic [NumAlerts-1:0] alert_test; |
| logic [NumAlerts-1:0] tap_alert_test; |
| |
| assign alerts = { |
| fatal_bus_integ_error_q, |
| fatal_state_error_q, |
| fatal_prog_error_q |
| }; |
| |
| assign alert_test = { |
| reg2hw.alert_test.fatal_bus_integ_error.q & |
| reg2hw.alert_test.fatal_bus_integ_error.qe, |
| reg2hw.alert_test.fatal_state_error.q & |
| reg2hw.alert_test.fatal_state_error.qe, |
| reg2hw.alert_test.fatal_prog_error.q & |
| reg2hw.alert_test.fatal_prog_error.qe |
| }; |
| |
| assign tap_alert_test = { |
| tap_reg2hw.alert_test.fatal_bus_integ_error.q & |
| tap_reg2hw.alert_test.fatal_bus_integ_error.qe, |
| tap_reg2hw.alert_test.fatal_state_error.q & |
| tap_reg2hw.alert_test.fatal_state_error.qe, |
| tap_reg2hw.alert_test.fatal_prog_error.q & |
| tap_reg2hw.alert_test.fatal_prog_error.qe |
| }; |
| |
| for (genvar k = 0; k < NumAlerts; k++) begin : gen_alert_tx |
| prim_alert_sender #( |
| .AsyncOn(AlertAsyncOn[k]), |
| .IsFatal(1) |
| ) u_prim_alert_sender ( |
| .clk_i, |
| .rst_ni, |
| .alert_test_i ( alert_test[k] | |
| tap_alert_test[k] ), |
| .alert_req_i ( alerts[k] ), |
| .alert_ack_o ( ), |
| .alert_state_o ( ), |
| .alert_rx_i ( alert_rx_i[k] ), |
| .alert_tx_o ( alert_tx_o[k] ) |
| ); |
| end |
| |
| ////////////////////////// |
| // Escalation Receivers // |
| ////////////////////////// |
| |
| // SEC_CM: MAIN.FSM.GLOBAL_ESC |
| // We still have two escalation receivers here for historical reasons. |
| // The two actions "wipe secrets" and "scrap lifecycle state" have been |
| // combined in order to simplify both DV and the design, as otherwise |
| // this separation of very intertwined actions would have caused too many |
| // unnecessary corner cases. The escalation receivers are now redundant and |
| // trigger both actions at once. |
| |
| // This escalation action moves the life cycle |
| // state into a temporary "SCRAP" state named "ESCALATE", |
| // and asserts the lc_escalate_en life cycle control signal. |
| logic esc_scrap_state0; |
| prim_esc_receiver #( |
| .N_ESC_SEV (alert_handler_reg_pkg::N_ESC_SEV), |
| .PING_CNT_DW (alert_handler_reg_pkg::PING_CNT_DW) |
| ) u_prim_esc_receiver0 ( |
| .clk_i, |
| .rst_ni, |
| .esc_req_o (esc_scrap_state0), |
| .esc_rx_o (esc_scrap_state0_rx_o), |
| .esc_tx_i (esc_scrap_state0_tx_i) |
| ); |
| |
| // This escalation action moves the life cycle |
| // state into a temporary "SCRAP" state named "ESCALATE". |
| logic esc_scrap_state1; |
| prim_esc_receiver #( |
| .N_ESC_SEV (alert_handler_reg_pkg::N_ESC_SEV), |
| .PING_CNT_DW (alert_handler_reg_pkg::PING_CNT_DW) |
| ) u_prim_esc_receiver1 ( |
| .clk_i, |
| .rst_ni, |
| .esc_req_o (esc_scrap_state1), |
| .esc_rx_o (esc_scrap_state1_rx_o), |
| .esc_tx_i (esc_scrap_state1_tx_i) |
| ); |
| |
| //////////////////////////// |
| // Synchronization of IOs // |
| //////////////////////////// |
| |
| // Signals going to and coming from power manager. |
| logic lc_init; |
| prim_flop_2sync #( |
| .Width(1) |
| ) u_prim_flop_2sync_init ( |
| .clk_i, |
| .rst_ni, |
| .d_i(pwr_lc_i.lc_init), |
| .q_o(lc_init) |
| ); |
| |
| logic lc_done_d, lc_done_q; |
| logic lc_idle_q; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin : p_sync_regs |
| if (!rst_ni) begin |
| lc_done_q <= 1'b0; |
| lc_idle_q <= 1'b0; |
| end else begin |
| lc_done_q <= lc_done_d; |
| lc_idle_q <= lc_idle_d; |
| end |
| end |
| |
| assign pwr_lc_o.lc_done = lc_done_q; |
| assign pwr_lc_o.lc_idle = lc_idle_q; |
| |
| //////////////////// |
| // KMAC Interface // |
| //////////////////// |
| |
| logic token_hash_req, token_hash_req_chk, token_hash_ack, token_hash_err, token_if_fsm_err; |
| lc_token_t hashed_token; |
| lc_ctrl_kmac_if u_lc_ctrl_kmac_if ( |
| .clk_i, |
| .rst_ni, |
| .clk_kmac_i, |
| .rst_kmac_ni, |
| .kmac_data_i, |
| .kmac_data_o, |
| .transition_token_i ( transition_token_q ), |
| .token_hash_req_i ( token_hash_req ), |
| .token_hash_req_chk_i ( token_hash_req_chk ), |
| .token_hash_ack_o ( token_hash_ack ), |
| .token_hash_err_o ( token_hash_err ), |
| .token_if_fsm_err_o ( token_if_fsm_err ), |
| .hashed_token_o ( hashed_token ) |
| ); |
| |
| //////////// |
| // LC FSM // |
| //////////// |
| |
| lc_ctrl_fsm #( |
| .RndCnstLcKeymgrDivInvalid ( RndCnstLcKeymgrDivInvalid ), |
| .RndCnstLcKeymgrDivTestDevRma ( RndCnstLcKeymgrDivTestDevRma ), |
| .RndCnstLcKeymgrDivProduction ( RndCnstLcKeymgrDivProduction ), |
| .RndCnstInvalidTokens ( RndCnstInvalidTokens ) |
| ) u_lc_ctrl_fsm ( |
| .clk_i, |
| .rst_ni, |
| .init_req_i ( lc_init ), |
| .init_done_o ( lc_done_d ), |
| .idle_o ( lc_idle_d ), |
| .esc_scrap_state0_i ( esc_scrap_state0 ), |
| .esc_scrap_state1_i ( esc_scrap_state1 ), |
| .lc_state_valid_i ( otp_lc_data_i.valid ), |
| .lc_state_i ( lc_state_e'(otp_lc_data_i.state) ), |
| .secrets_valid_i ( otp_lc_data_i.secrets_valid ), |
| .lc_cnt_i ( lc_cnt_e'(otp_lc_data_i.count) ), |
| .use_ext_clock_i ( use_ext_clock_q ), |
| .test_unlock_token_i ( otp_lc_data_i.test_unlock_token ), |
| .test_exit_token_i ( otp_lc_data_i.test_exit_token ), |
| .test_tokens_valid_i ( otp_lc_data_i.test_tokens_valid ), |
| .rma_token_i ( otp_lc_data_i.rma_token ), |
| .rma_token_valid_i ( otp_lc_data_i.rma_token_valid ), |
| .trans_cmd_i ( transition_cmd ), |
| .trans_target_i ( transition_target_q ), |
| .dec_lc_state_o ( dec_lc_state ), |
| .dec_lc_cnt_o ( dec_lc_cnt ), |
| .dec_lc_id_state_o ( dec_lc_id_state ), |
| .token_hash_req_o ( token_hash_req ), |
| .token_hash_req_chk_o ( token_hash_req_chk ), |
| .token_hash_ack_i ( token_hash_ack ), |
| .token_hash_err_i ( token_hash_err ), |
| .token_if_fsm_err_i ( token_if_fsm_err ), |
| .hashed_token_i ( hashed_token ), |
| .otp_prog_req_o ( lc_otp_program_o.req ), |
| .otp_prog_lc_state_o ( lc_otp_program_o.state ), |
| .otp_prog_lc_cnt_o ( lc_otp_program_o.count ), |
| .otp_prog_ack_i ( lc_otp_program_i.ack ), |
| .otp_prog_err_i ( lc_otp_program_i.err ), |
| .trans_success_o ( trans_success_d ), |
| .trans_cnt_oflw_error_o ( trans_cnt_oflw_error_d ), |
| .trans_invalid_error_o ( trans_invalid_error_d ), |
| .token_invalid_error_o ( token_invalid_error_d ), |
| .flash_rma_error_o ( flash_rma_error_d ), |
| .otp_prog_error_o ( otp_prog_error_d ), |
| .state_invalid_error_o ( state_invalid_error_d ), |
| .lc_raw_test_rma_o ( lc_raw_test_rma ), |
| .lc_dft_en_o, |
| .lc_nvm_debug_en_o, |
| .lc_hw_debug_en_o, |
| .lc_cpu_en_o, |
| .lc_creator_seed_sw_rw_en_o, |
| .lc_owner_seed_sw_rw_en_o, |
| .lc_iso_part_sw_rd_en_o, |
| .lc_iso_part_sw_wr_en_o, |
| .lc_seed_hw_rd_en_o, |
| .lc_keymgr_en_o, |
| .lc_escalate_en_o, |
| .lc_check_byp_en_o, |
| .lc_clk_byp_req_o, |
| .lc_clk_byp_ack_i, |
| .lc_flash_rma_req_o, |
| .lc_flash_rma_ack_i, |
| .lc_keymgr_div_o |
| ); |
| |
| //////////////// |
| // Assertions // |
| //////////////// |
| |
| `ASSERT_KNOWN(TlOKnown, tl_o ) |
| `ASSERT_KNOWN(AlertTxKnown_A, alert_tx_o ) |
| `ASSERT_KNOWN(PwrLcKnown_A, pwr_lc_o ) |
| `ASSERT_KNOWN(LcOtpProgramKnown_A, lc_otp_program_o ) |
| `ASSERT_KNOWN(LcOtpTokenKnown_A, kmac_data_o ) |
| `ASSERT_KNOWN(LcDftEnKnown_A, lc_dft_en_o ) |
| `ASSERT_KNOWN(LcNvmDebugEnKnown_A, lc_nvm_debug_en_o ) |
| `ASSERT_KNOWN(LcHwDebugEnKnown_A, lc_hw_debug_en_o ) |
| `ASSERT_KNOWN(LcCpuEnKnown_A, lc_cpu_en_o ) |
| `ASSERT_KNOWN(LcCreatorSwRwEn_A, lc_creator_seed_sw_rw_en_o ) |
| `ASSERT_KNOWN(LcOwnerSwRwEn_A, lc_owner_seed_sw_rw_en_o ) |
| `ASSERT_KNOWN(LcIsoSwRwEn_A, lc_iso_part_sw_rd_en_o ) |
| `ASSERT_KNOWN(LcIsoSwWrEn_A, lc_iso_part_sw_wr_en_o ) |
| `ASSERT_KNOWN(LcSeedHwRdEn_A, lc_seed_hw_rd_en_o ) |
| `ASSERT_KNOWN(LcKeymgrEnKnown_A, lc_keymgr_en_o ) |
| `ASSERT_KNOWN(LcEscalateEnKnown_A, lc_escalate_en_o ) |
| `ASSERT_KNOWN(LcCheckBypassEnKnown_A, lc_check_byp_en_o ) |
| `ASSERT_KNOWN(LcClkBypReqKnown_A, lc_clk_byp_req_o ) |
| `ASSERT_KNOWN(LcFlashRmaSeedKnown_A, lc_flash_rma_seed_o ) |
| `ASSERT_KNOWN(LcFlashRmaReqKnown_A, lc_flash_rma_req_o ) |
| `ASSERT_KNOWN(LcKeymgrDiv_A, lc_keymgr_div_o ) |
| |
| // Alert assertions for sparse FSMs. |
| `ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(CtrlLcFsmCheck_A, |
| u_lc_ctrl_fsm.u_fsm_state_regs, alert_tx_o[1]) |
| `ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(CtrlLcStateCheck_A, |
| u_lc_ctrl_fsm.u_state_regs, alert_tx_o[1], |
| !$past(otp_lc_data_i.valid) || |
| u_lc_ctrl_fsm.fsm_state_q inside {ResetSt, EscalateSt, PostTransSt, InvalidSt} || |
| u_lc_ctrl_fsm.esc_scrap_state0_i || |
| u_lc_ctrl_fsm.esc_scrap_state1_i) |
| `ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(CtrlLcCntCheck_A, |
| u_lc_ctrl_fsm.u_cnt_regs, alert_tx_o[1], |
| !$past(otp_lc_data_i.valid) || |
| u_lc_ctrl_fsm.fsm_state_q inside {ResetSt, EscalateSt, PostTransSt, InvalidSt} || |
| u_lc_ctrl_fsm.esc_scrap_state0_i || |
| u_lc_ctrl_fsm.esc_scrap_state1_i) |
| `ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(CtrlKmacIfFsmCheck_A, |
| u_lc_ctrl_kmac_if.u_state_regs, alert_tx_o[1], |
| u_lc_ctrl_fsm.fsm_state_q inside {EscalateSt} || |
| u_lc_ctrl_fsm.esc_scrap_state0_i || |
| u_lc_ctrl_fsm.esc_scrap_state1_i) |
| |
| // Alert assertions for reg_we onehot check |
| `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[2]) |
| `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(TapRegWeOnehotCheck_A, u_reg_tap, alert_tx_o[2], 0) |
| endmodule : lc_ctrl |