| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // OTP Controller top. |
| // |
| |
| `include "prim_assert.sv" |
| |
| module otp_ctrl |
| import otp_ctrl_pkg::*; |
| import otp_ctrl_reg_pkg::*; |
| ( |
| input clk_i, |
| input rst_ni, |
| // TODO: signals to AST |
| // Bus Interface (device) |
| input tlul_pkg::tl_h2d_t tl_i, |
| output tlul_pkg::tl_d2h_t tl_o, |
| // Interrupt Requests |
| output logic intr_otp_access_done_o, |
| output logic intr_otp_ctrl_err_o, |
| // Alerts |
| 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, |
| // Power manager interface |
| input pwr_otp_init_req_t pwr_otp_init_req_i, |
| output pwr_otp_init_rsp_t pwr_otp_init_rsp_o, |
| output otp_pwr_state_t otp_pwr_state_o, |
| // Lifecycle transition command interface |
| input lc_otp_program_req_t lc_otp_program_req_i, |
| output lc_otp_program_rsp_t lc_otp_program_rsp_o, |
| // Lifecycle broadcast inputs |
| input lc_tx_t lc_provision_en_i, |
| input lc_tx_t lc_test_en_i, |
| // OTP broadcast outputs |
| output otp_lc_data_t otp_lc_data_o, |
| output keymgr_key_t otp_keymgr_key_o, |
| output flash_key_t otp_flash_key_o |
| // TODO: other hardware broadcast outputs |
| ); |
| |
| ////////////////////////////////// |
| // Regfile Breakout and Mapping // |
| ////////////////////////////////// |
| |
| tlul_pkg::tl_h2d_t tl_win_h2d[2]; |
| tlul_pkg::tl_d2h_t tl_win_d2h[2]; |
| |
| otp_ctrl_reg_pkg::otp_ctrl_reg2hw_t reg2hw; |
| otp_ctrl_reg_pkg::otp_ctrl_hw2reg_t hw2reg; |
| |
| otp_ctrl_reg_top u_reg ( |
| .clk_i , |
| .rst_ni, |
| .tl_i, |
| .tl_o, |
| .tl_win_o ( tl_win_h2d ), |
| .tl_win_i ( tl_win_d2h ), |
| .reg2hw ( reg2hw ), |
| .hw2reg ( hw2reg ), |
| .devmode_i( 1'b1 ) |
| ); |
| |
| //////////////// |
| // Interrupts // |
| //////////////// |
| |
| logic otp_access_done; |
| logic otp_ctrl_err; |
| |
| // dummy connections |
| assign otp_access_done = reg2hw.direct_access_size.q[0]; |
| assign otp_ctrl_err = reg2hw.direct_access_size.q[1]; |
| |
| prim_intr_hw #( |
| .Width(1) |
| ) i_intr_esc0 ( |
| .event_intr_i ( otp_access_done ), |
| .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.otp_access_done.q ), |
| .reg2hw_intr_test_q_i ( reg2hw.intr_test.otp_access_done.q ), |
| .reg2hw_intr_test_qe_i ( reg2hw.intr_test.otp_access_done.qe ), |
| .reg2hw_intr_state_q_i ( reg2hw.intr_state.otp_access_done.q ), |
| .hw2reg_intr_state_de_o ( hw2reg.intr_state.otp_access_done.de ), |
| .hw2reg_intr_state_d_o ( hw2reg.intr_state.otp_access_done.d ), |
| .intr_o ( intr_otp_access_done_o ) |
| ); |
| |
| prim_intr_hw #( |
| .Width(1) |
| ) i_intr_esc1 ( |
| .event_intr_i ( otp_ctrl_err ), |
| .reg2hw_intr_enable_q_i ( reg2hw.intr_enable.otp_ctrl_err.q ), |
| .reg2hw_intr_test_q_i ( reg2hw.intr_test.otp_ctrl_err.q ), |
| .reg2hw_intr_test_qe_i ( reg2hw.intr_test.otp_ctrl_err.qe ), |
| .reg2hw_intr_state_q_i ( reg2hw.intr_state.otp_ctrl_err.q ), |
| .hw2reg_intr_state_de_o ( hw2reg.intr_state.otp_ctrl_err.de ), |
| .hw2reg_intr_state_d_o ( hw2reg.intr_state.otp_ctrl_err.d ), |
| .intr_o ( intr_otp_ctrl_err_o ) |
| ); |
| |
| /////////////////// |
| // Alert Senders // |
| /////////////////// |
| |
| logic parity_mismatch; |
| logic digest_mismatch; |
| |
| // dummy connections |
| assign parity_mismatch = reg2hw.direct_access_cmd.read.q && |
| reg2hw.direct_access_cmd.read.qe; |
| assign digest_mismatch = reg2hw.direct_access_cmd.write.q && |
| reg2hw.direct_access_cmd.write.qe; |
| |
| prim_alert_sender #( |
| .AsyncOn(AlertAsyncOn[0]) |
| ) i_prim_alert_sender0 ( |
| .clk_i, |
| .rst_ni, |
| .alert_i ( parity_mismatch ), |
| .alert_rx_i ( alert_rx_i[0] ), |
| .alert_tx_o ( alert_tx_o[0] ) |
| ); |
| |
| prim_alert_sender #( |
| .AsyncOn(AlertAsyncOn[1]) |
| ) i_prim_alert_sender1 ( |
| .clk_i, |
| .rst_ni, |
| .alert_i ( digest_mismatch ), |
| .alert_rx_i ( alert_rx_i[1] ), |
| .alert_tx_o ( alert_tx_o[1] ) |
| ); |
| |
| /////////////// |
| // OTP Macro // |
| /////////////// |
| |
| localparam int OtpWidth = 8; |
| localparam int OtpDepth = 1024; |
| localparam int OtpAddrWidth = $clog2(OtpDepth); |
| localparam int OtpErrWidth = 8; |
| |
| logic otp_init_req, otp_init_done; |
| logic otp_err_valid; |
| logic [OtpErrWidth-1:0] otp_err_code; |
| logic otp_valid, otp_ready; |
| logic [OtpAddrWidth-1:0] otp_addr; |
| logic [OtpWidth-1:0] otp_wdata, otp_rdata; |
| logic otp_wren, otp_rvalid; |
| |
| // a couple of dummy connections to the OTP macro |
| assign otp_valid = reg2hw.direct_access_wdata[1].qe | reg2hw.direct_access_cmd.write.qe; |
| assign otp_wren = reg2hw.direct_access_wdata[1].qe; |
| assign otp_addr = reg2hw.direct_access_address.q; |
| assign otp_wdata = reg2hw.direct_access_wdata[1].q[OtpWidth-1:0]; |
| |
| assign otp_init_req = 1'b1; |
| |
| prim_otp #( |
| .Width(OtpWidth), |
| .Depth(OtpDepth), |
| .ErrWidth(OtpErrWidth) |
| ) i_prim_otp ( |
| .clk_i, |
| .rst_ni, |
| // Test inerface |
| .test_tl_i ( tl_win_h2d[1] ), |
| .test_tl_o ( tl_win_d2h[1] ), |
| // Init and error signals |
| .init_req_i ( otp_init_req ), |
| .init_done_o ( otp_init_done ), |
| .err_valid_o ( otp_err_valid ), |
| .err_code_o ( otp_err_code ), |
| // Read / Write command interface |
| .ready_o ( otp_ready ), |
| .valid_i ( otp_valid ), |
| .addr_i ( otp_addr ), |
| .wdata_i ( otp_wdata ), |
| .wren_i ( otp_wren ), |
| // Read data out |
| .rdata_o ( otp_rdata ), |
| .rvalid_o ( otp_rvalid ) |
| ); |
| |
| //////////////////// |
| // OTP Ctrl Logic // |
| //////////////////// |
| |
| logic [31:0] gate_gen_out; |
| logic gate_gen_out_valid; |
| |
| // gate generator with dummy connection to regfile |
| prim_gate_gen #( |
| .NumGates(30000) |
| ) i_prim_gate_gen ( |
| .clk_i, |
| .rst_ni, |
| .data_i ( reg2hw.direct_access_wdata[0].q ), |
| .valid_i ( reg2hw.direct_access_wdata[0].qe ), |
| .data_o ( gate_gen_out ), |
| .valid_o ( gate_gen_out_valid ) |
| ); |
| |
| ///////////////////// |
| // PRESENT ENC/DEC // |
| ///////////////////// |
| |
| logic [63:0] otp_scrambler_out; |
| logic otp_scrambler_out_valid; |
| |
| otp_ctrl_scrmbl i_otp_ctrl_scrmbl ( |
| .clk_i, |
| .rst_ni, |
| .data_i ( {reg2hw.direct_access_wdata[1].q, |
| reg2hw.direct_access_wdata[0].q} ), |
| .cmd_i ( otp_scrmbl_cmd_e'(reg2hw.direct_access_wdata[1].q[2:0]) ), |
| .valid_i ( reg2hw.direct_access_wdata[1].qe ), |
| .ready_o ( ), |
| .data_o ( otp_scrambler_out ), |
| .valid_o ( otp_scrambler_out_valid ) |
| ); |
| |
| /////////////////////////////////// |
| // Dummy Connections and Tie Off // |
| /////////////////////////////////// |
| |
| always_comb begin : p_tie_off_or_connect |
| // tie off |
| hw2reg.status = '0; |
| hw2reg.err_code = '0; |
| hw2reg.direct_access_rdata = '0; |
| hw2reg.lc_state = '0; |
| hw2reg.id_state = '0; |
| hw2reg.test_xxx_cnt = '0; |
| hw2reg.transition_cnt = '0; |
| hw2reg.secret_integrity_digest = '0; |
| hw2reg.hw_cfg_lock = '0; |
| hw2reg.hw_cfg = '0; |
| hw2reg.hw_cfg_integrity_digest = '0; |
| hw2reg.sw_cfg_integrity_digest = '0; |
| |
| // TODO: initialization should only performed once after reset. subsequent |
| // init requests should just be immediately ack'ed without performing the complete |
| // OTP boot sequence. |
| pwr_otp_init_rsp_o = '0; |
| otp_pwr_state_o = '0; |
| lc_otp_program_rsp_o = '0; |
| otp_lc_data_o = '0; |
| otp_keymgr_key_o = '0; |
| otp_flash_key_o = '0; |
| |
| // dummy connections |
| {hw2reg.direct_access_rdata[0].d, |
| hw2reg.direct_access_rdata[1].d} = otp_scrambler_out ^ {gate_gen_out, gate_gen_out}; |
| hw2reg.direct_access_rdata[0].de = gate_gen_out_valid ^ gate_gen_out_valid; |
| hw2reg.direct_access_rdata[1].de = gate_gen_out_valid ^ gate_gen_out_valid; |
| hw2reg.lc_state[0] = otp_rdata ^ {8{otp_rvalid}}; |
| end |
| |
| |
| endmodule : otp_ctrl |