blob: 8bcdf7166093df7dc6ef259c71a3f5a3f592394c [file] [log] [blame]
// 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"
/**
* OpenTitan Big Number Accelerator (OTBN) Core
*
* This module is the top-level of the OTBN processing core.
*/
// Below countermeasure (no data dependent control flow in OTBN ISA) is inherent to the design and
// has no directly associated RTL
// SEC_CM: CTRL_FLOW.SCA
module otbn_core
import otbn_pkg::*;
#(
// Register file implementation selection, see otbn_pkg.sv.
parameter regfile_e RegFile = RegFileFF,
// Size of the instruction memory, in bytes
parameter int ImemSizeByte = 4096,
// Size of the data memory, in bytes
parameter int DmemSizeByte = 4096,
// Default seed for URND PRNG
parameter urnd_prng_seed_t RndCnstUrndPrngSeed = RndCnstUrndPrngSeedDefault,
// Disable URND reseed and advance when not in use. Useful for SCA only.
parameter bit SecMuteUrnd = 1'b0,
parameter bit SecSkipUrndReseedAtStart = 1'b0,
localparam int ImemAddrWidth = prim_util_pkg::vbits(ImemSizeByte),
localparam int DmemAddrWidth = prim_util_pkg::vbits(DmemSizeByte)
) (
input logic clk_i,
input logic rst_ni,
input logic start_i, // start the operation
output logic done_o, // operation done
output logic locking_o, // The core is in or is entering the locked state
output logic secure_wipe_running_o, // the core is securely wiping its internal state
output core_err_bits_t err_bits_o, // valid when done_o is asserted
output logic recoverable_err_o,
// Instruction memory (IMEM)
output logic imem_req_o,
output logic [ImemAddrWidth-1:0] imem_addr_o,
input logic [38:0] imem_rdata_i,
input logic imem_rvalid_i,
// Data memory (DMEM)
output logic dmem_req_o,
output logic dmem_write_o,
output logic [DmemAddrWidth-1:0] dmem_addr_o,
output logic [ExtWLEN-1:0] dmem_wdata_o,
output logic [ExtWLEN-1:0] dmem_wmask_o,
output logic [BaseWordsPerWLEN-1:0] dmem_rmask_o,
input logic [ExtWLEN-1:0] dmem_rdata_i,
input logic dmem_rvalid_i,
input logic dmem_rerror_i,
// Entropy distribution network (EDN) connections
// One for RND, the other for URND
output logic edn_rnd_req_o,
input logic edn_rnd_ack_i,
input logic [EdnDataWidth-1:0] edn_rnd_data_i,
input logic edn_rnd_fips_i,
input logic edn_rnd_err_i,
output logic edn_urnd_req_o,
input logic edn_urnd_ack_i,
input logic [EdnDataWidth-1:0] edn_urnd_data_i,
output logic [31:0] insn_cnt_o,
input logic insn_cnt_clear_i,
output logic mems_sec_wipe_o, // Request secure wipe for imem and dmem
input logic req_sec_wipe_urnd_keys_i, // Request URND bits for temporary scramble keys.
// Keys below are valid cycle after request.
output logic [127:0] dmem_sec_wipe_urnd_key_o, // URND bits to give temporary dmem scramble key
output logic [127:0] imem_sec_wipe_urnd_key_o, // URND bits to give temporary imem scramble key
// Indicates an incoming escalation from some fatal error at the level above. The core needs to
// halt and then enter a locked state.
input prim_mubi_pkg::mubi4_t escalate_en_i,
// Indicates an incoming RMA request. The core needs to halt, trigger a secure wipe immediately
// and then enter a locked state.
input prim_mubi_pkg::mubi4_t rma_req_i,
output prim_mubi_pkg::mubi4_t rma_ack_o,
// When set software errors become fatal errors.
input logic software_errs_fatal_i,
input logic [1:0] sideload_key_shares_valid_i,
input logic [1:0][SideloadKeyWidth-1:0] sideload_key_shares_i
);
import prim_mubi_pkg::*;
// Create a lint error to reduce the risk of accidentally enabling this feature.
`ASSERT_STATIC_LINT_ERROR(OtbnSecMuteUrndNonDefault, SecMuteUrnd == 0)
// Fetch request (the next instruction)
logic [ImemAddrWidth-1:0] insn_fetch_req_addr;
logic insn_fetch_req_valid;
logic insn_fetch_req_valid_raw;
// Fetch response (the current instruction before it is decoded)
logic insn_fetch_resp_valid;
logic [ImemAddrWidth-1:0] insn_fetch_resp_addr;
logic [31:0] insn_fetch_resp_data;
logic insn_fetch_resp_clear;
logic insn_fetch_err;
logic insn_addr_err;
rf_predec_bignum_t rf_predec_bignum;
alu_predec_bignum_t alu_predec_bignum;
ctrl_flow_predec_t ctrl_flow_predec;
logic [ImemAddrWidth-1:0] ctrl_flow_target_predec;
ispr_predec_bignum_t ispr_predec_bignum;
mac_predec_bignum_t mac_predec_bignum;
logic lsu_addr_en_predec;
logic [NWdr-1:0] rf_bignum_rd_a_indirect_onehot;
logic [NWdr-1:0] rf_bignum_rd_b_indirect_onehot;
logic [NWdr-1:0] rf_bignum_wr_indirect_onehot;
logic rf_bignum_indirect_en;
// The currently executed instruction.
logic insn_valid;
logic insn_illegal;
logic [ImemAddrWidth-1:0] insn_addr;
insn_dec_base_t insn_dec_base;
insn_dec_bignum_t insn_dec_bignum;
insn_dec_shared_t insn_dec_shared;
logic [4:0] rf_base_wr_addr;
logic [4:0] rf_base_wr_addr_ctrl;
logic rf_base_wr_en;
logic rf_base_wr_en_ctrl;
logic rf_base_wr_commit;
logic rf_base_wr_commit_ctrl;
logic [31:0] rf_base_wr_data_no_intg;
logic [31:0] rf_base_wr_data_no_intg_ctrl;
logic [BaseIntgWidth-1:0] rf_base_wr_data_intg;
logic rf_base_wr_data_intg_sel, rf_base_wr_data_intg_sel_ctrl;
logic [4:0] rf_base_rd_addr_a;
logic rf_base_rd_en_a;
logic [BaseIntgWidth-1:0] rf_base_rd_data_a_intg;
logic [4:0] rf_base_rd_addr_b;
logic rf_base_rd_en_b;
logic [BaseIntgWidth-1:0] rf_base_rd_data_b_intg;
logic rf_base_rd_commit;
logic rf_base_call_stack_sw_err;
logic rf_base_call_stack_hw_err;
logic rf_base_intg_err;
logic rf_base_spurious_we_err;
alu_base_operation_t alu_base_operation;
alu_base_comparison_t alu_base_comparison;
logic [31:0] alu_base_operation_result;
logic alu_base_comparison_result;
logic lsu_load_req;
logic lsu_store_req;
insn_subset_e lsu_req_subset;
logic [DmemAddrWidth-1:0] lsu_addr;
logic [BaseIntgWidth-1:0] lsu_base_wdata;
logic [ExtWLEN-1:0] lsu_bignum_wdata;
logic [BaseIntgWidth-1:0] lsu_base_rdata;
logic [ExtWLEN-1:0] lsu_bignum_rdata;
logic lsu_rdata_err;
logic [WdrAw-1:0] rf_bignum_wr_addr;
logic [WdrAw-1:0] rf_bignum_wr_addr_ctrl;
logic [1:0] rf_bignum_wr_en;
logic [1:0] rf_bignum_wr_en_ctrl;
logic rf_bignum_wr_commit;
logic rf_bignum_wr_commit_ctrl;
logic [WLEN-1:0] rf_bignum_wr_data_no_intg;
logic [WLEN-1:0] rf_bignum_wr_data_no_intg_ctrl;
logic [ExtWLEN-1:0] rf_bignum_wr_data_intg;
logic rf_bignum_wr_data_intg_sel, rf_bignum_wr_data_intg_sel_ctrl;
logic [WdrAw-1:0] rf_bignum_rd_addr_a;
logic rf_bignum_rd_en_a;
logic [ExtWLEN-1:0] rf_bignum_rd_data_a_intg;
logic [WdrAw-1:0] rf_bignum_rd_addr_b;
logic rf_bignum_rd_en_b;
logic [ExtWLEN-1:0] rf_bignum_rd_data_b_intg;
logic rf_bignum_intg_err;
logic rf_bignum_spurious_we_err;
alu_bignum_operation_t alu_bignum_operation;
logic alu_bignum_operation_valid;
logic alu_bignum_operation_commit;
logic [WLEN-1:0] alu_bignum_operation_result;
logic alu_bignum_selection_flag;
logic alu_bignum_reg_intg_violation_err;
mac_bignum_operation_t mac_bignum_operation;
logic [WLEN-1:0] mac_bignum_operation_result;
flags_t mac_bignum_operation_flags;
flags_t mac_bignum_operation_flags_en;
logic mac_bignum_en;
logic mac_bignum_commit;
logic mac_bignum_reg_intg_violation_err;
ispr_e ispr_addr;
logic [31:0] ispr_base_wdata;
logic [BaseWordsPerWLEN-1:0] ispr_base_wr_en;
logic [ExtWLEN-1:0] ispr_bignum_wdata_intg;
logic ispr_bignum_wr_en;
logic ispr_wr_commit;
logic [ExtWLEN-1:0] ispr_rdata_intg;
logic ispr_rd_en;
logic [ExtWLEN-1:0] ispr_acc_intg;
logic [ExtWLEN-1:0] ispr_acc_wr_data_intg;
logic ispr_acc_wr_en;
logic ispr_init;
logic rnd_req;
logic rnd_prefetch_req;
logic rnd_valid;
logic [WLEN-1:0] rnd_data;
logic rnd_rep_err;
logic rnd_fips_err;
logic urnd_reseed_req;
logic urnd_reseed_ack;
logic urnd_reseed_err;
logic urnd_advance;
logic urnd_advance_start_stop_control;
logic [WLEN-1:0] urnd_data;
logic urnd_all_zero;
logic controller_start;
logic state_reset;
logic [31:0] insn_cnt;
logic secure_wipe_req, secure_wipe_ack;
logic sec_wipe_wdr_d, sec_wipe_wdr_q;
logic sec_wipe_wdr_urnd_d, sec_wipe_wdr_urnd_q;
logic sec_wipe_base;
logic sec_wipe_base_urnd;
logic [4:0] sec_wipe_addr, sec_wipe_wdr_addr_q;
logic sec_wipe_acc_urnd;
logic sec_wipe_mod_urnd;
logic sec_wipe_zero;
logic prefetch_en;
logic prefetch_loop_active;
logic [31:0] prefetch_loop_iterations;
logic [ImemAddrWidth:0] prefetch_loop_end_addr;
logic [ImemAddrWidth-1:0] prefetch_loop_jump_addr;
mubi4_t controller_fatal_escalate_en, controller_recov_escalate_en;
mubi4_t start_stop_escalate_en;
controller_err_bits_t controller_err_bits;
logic prefetch_ignore_errs;
core_err_bits_t err_bits_q, err_bits_d;
logic mubi_err;
logic start_stop_fatal_error;
logic rf_bignum_predec_error, alu_bignum_predec_error, ispr_predec_error, mac_bignum_predec_error;
logic controller_predec_error;
logic rd_predec_error, predec_error;
logic req_sec_wipe_urnd_keys_q;
// Start stop control start OTBN execution when requested and deals with any pre start or post
// stop actions.
otbn_start_stop_control #(
.SecMuteUrnd(SecMuteUrnd),
.SecSkipUrndReseedAtStart(SecSkipUrndReseedAtStart)
) u_otbn_start_stop_control (
.clk_i,
.rst_ni,
.start_i,
.escalate_en_i(start_stop_escalate_en),
.rma_req_i,
.rma_ack_o,
.controller_start_o(controller_start),
.urnd_reseed_req_o (urnd_reseed_req),
.urnd_reseed_ack_i (urnd_reseed_ack),
.urnd_reseed_err_o (urnd_reseed_err),
.urnd_advance_o (urnd_advance_start_stop_control),
.secure_wipe_req_i (secure_wipe_req),
.secure_wipe_ack_o (secure_wipe_ack),
.secure_wipe_running_o,
.done_o,
.sec_wipe_wdr_o (sec_wipe_wdr_d),
.sec_wipe_wdr_urnd_o (sec_wipe_wdr_urnd_d),
.sec_wipe_base_o (sec_wipe_base),
.sec_wipe_base_urnd_o(sec_wipe_base_urnd),
.sec_wipe_addr_o (sec_wipe_addr),
.sec_wipe_acc_urnd_o(sec_wipe_acc_urnd),
.sec_wipe_mod_urnd_o(sec_wipe_mod_urnd),
.sec_wipe_zero_o (sec_wipe_zero),
.ispr_init_o (ispr_init),
.state_reset_o(state_reset),
.fatal_error_o(start_stop_fatal_error)
);
// Depending on its usage, the instruction address (program counter) is qualified by two valid
// signals: insn_fetch_resp_valid (together with the undecoded instruction data), and insn_valid
// for valid decoded (i.e. legal) instructions. Duplicate the signal in the source code for
// consistent grouping of signals with their valid signal.
assign insn_addr = insn_fetch_resp_addr;
// Instruction fetch unit
otbn_instruction_fetch #(
.ImemSizeByte(ImemSizeByte)
) u_otbn_instruction_fetch (
.clk_i,
.rst_ni,
// Instruction memory interface
.imem_req_o,
.imem_addr_o,
.imem_rdata_i,
.imem_rvalid_i,
// Instruction to fetch
.insn_fetch_req_addr_i (insn_fetch_req_addr),
.insn_fetch_req_valid_i (insn_fetch_req_valid),
.insn_fetch_req_valid_raw_i(insn_fetch_req_valid_raw),
// Fetched instruction
.insn_fetch_resp_addr_o (insn_fetch_resp_addr),
.insn_fetch_resp_valid_o(insn_fetch_resp_valid),
.insn_fetch_resp_data_o (insn_fetch_resp_data),
.insn_fetch_resp_clear_i(insn_fetch_resp_clear),
.insn_fetch_err_o (insn_fetch_err),
.insn_addr_err_o (insn_addr_err),
.rf_predec_bignum_o (rf_predec_bignum),
.alu_predec_bignum_o (alu_predec_bignum),
.ctrl_flow_predec_o (ctrl_flow_predec),
.ctrl_flow_target_predec_o(ctrl_flow_target_predec),
.ispr_predec_bignum_o (ispr_predec_bignum),
.mac_predec_bignum_o (mac_predec_bignum),
.lsu_addr_en_predec_o (lsu_addr_en_predec),
.rf_bignum_rd_a_indirect_onehot_i(rf_bignum_rd_a_indirect_onehot),
.rf_bignum_rd_b_indirect_onehot_i(rf_bignum_rd_b_indirect_onehot),
.rf_bignum_wr_indirect_onehot_i (rf_bignum_wr_indirect_onehot),
.rf_bignum_indirect_en_i (rf_bignum_indirect_en),
.prefetch_en_i (prefetch_en),
.prefetch_loop_active_i (prefetch_loop_active),
.prefetch_loop_iterations_i(prefetch_loop_iterations),
.prefetch_loop_end_addr_i (prefetch_loop_end_addr),
.prefetch_loop_jump_addr_i (prefetch_loop_jump_addr),
.prefetch_ignore_errs_i (prefetch_ignore_errs),
.sec_wipe_wdr_en_i (sec_wipe_wdr_d),
.sec_wipe_wdr_addr_i(sec_wipe_addr)
);
// Instruction decoder
otbn_decoder u_otbn_decoder (
// The decoder is combinatorial; clk and rst are only used for assertions.
.clk_i,
.rst_ni,
// Instruction to decode
.insn_fetch_resp_data_i (insn_fetch_resp_data),
.insn_fetch_resp_valid_i(insn_fetch_resp_valid),
// Decoded instruction
.insn_valid_o (insn_valid),
.insn_illegal_o (insn_illegal),
.insn_dec_base_o (insn_dec_base),
.insn_dec_bignum_o(insn_dec_bignum),
.insn_dec_shared_o(insn_dec_shared)
);
// SEC_CM: CTRL.REDUN
// ALU and MAC predecode is only relevant when there is a valid instruction, as without one it is
// guaranteed there are no register reads (hence no sensitive data bits being fed into the blanked
// data paths). RF and ISPR predecode must always be checked to ensure read and write data paths
// are always correctly blanked.
assign rd_predec_error = |{rf_predec_bignum.rf_ren_a,
rf_predec_bignum.rf_ren_b,
ispr_predec_bignum.ispr_rd_en} & ~insn_valid;
assign predec_error =
((alu_bignum_predec_error | mac_bignum_predec_error | controller_predec_error) & insn_valid) |
rf_bignum_predec_error |
ispr_predec_error |
rd_predec_error;
// Controller: coordinate between functional units, prepare their inputs (e.g. by muxing between
// operand sources), and post-process their outputs as needed.
otbn_controller #(
.ImemSizeByte(ImemSizeByte),
.DmemSizeByte(DmemSizeByte)
) u_otbn_controller (
.clk_i,
.rst_ni,
.start_i (controller_start),
.locking_o,
.err_bit_clear_i (start_i),
.fatal_escalate_en_i(controller_fatal_escalate_en),
.recov_escalate_en_i(controller_recov_escalate_en),
.rma_req_i,
.err_bits_o (controller_err_bits),
.recoverable_err_o,
// Next instruction selection (to instruction fetch)
.insn_fetch_req_addr_o (insn_fetch_req_addr),
.insn_fetch_req_valid_o (insn_fetch_req_valid),
.insn_fetch_req_valid_raw_o(insn_fetch_req_valid_raw),
.insn_fetch_resp_clear_o (insn_fetch_resp_clear),
// The current instruction
.insn_valid_i (insn_valid),
.insn_illegal_i(insn_illegal),
.insn_addr_i (insn_addr),
// Decoded instruction from decoder
.insn_dec_base_i (insn_dec_base),
.insn_dec_bignum_i(insn_dec_bignum),
.insn_dec_shared_i(insn_dec_shared),
// To/from base register file
.rf_base_wr_addr_o (rf_base_wr_addr_ctrl),
.rf_base_wr_en_o (rf_base_wr_en_ctrl),
.rf_base_wr_commit_o (rf_base_wr_commit_ctrl),
.rf_base_wr_data_no_intg_o (rf_base_wr_data_no_intg_ctrl),
.rf_base_wr_data_intg_o (rf_base_wr_data_intg),
.rf_base_wr_data_intg_sel_o (rf_base_wr_data_intg_sel_ctrl),
.rf_base_rd_addr_a_o (rf_base_rd_addr_a),
.rf_base_rd_en_a_o (rf_base_rd_en_a),
.rf_base_rd_data_a_intg_i (rf_base_rd_data_a_intg),
.rf_base_rd_addr_b_o (rf_base_rd_addr_b),
.rf_base_rd_en_b_o (rf_base_rd_en_b),
.rf_base_rd_data_b_intg_i (rf_base_rd_data_b_intg),
.rf_base_rd_commit_o (rf_base_rd_commit),
.rf_base_call_stack_sw_err_i(rf_base_call_stack_sw_err),
.rf_base_call_stack_hw_err_i(rf_base_call_stack_hw_err),
// To/from bignum register file
.rf_bignum_wr_addr_o (rf_bignum_wr_addr_ctrl),
.rf_bignum_wr_en_o (rf_bignum_wr_en_ctrl),
.rf_bignum_wr_commit_o (rf_bignum_wr_commit_ctrl),
.rf_bignum_wr_data_no_intg_o (rf_bignum_wr_data_no_intg_ctrl),
.rf_bignum_wr_data_intg_o (rf_bignum_wr_data_intg),
.rf_bignum_wr_data_intg_sel_o(rf_bignum_wr_data_intg_sel_ctrl),
.rf_bignum_rd_addr_a_o (rf_bignum_rd_addr_a),
.rf_bignum_rd_en_a_o (rf_bignum_rd_en_a),
.rf_bignum_rd_data_a_intg_i (rf_bignum_rd_data_a_intg),
.rf_bignum_rd_addr_b_o (rf_bignum_rd_addr_b),
.rf_bignum_rd_en_b_o (rf_bignum_rd_en_b),
.rf_bignum_rd_data_b_intg_i (rf_bignum_rd_data_b_intg),
.rf_bignum_intg_err_i (rf_bignum_intg_err),
.rf_bignum_spurious_we_err_i (rf_bignum_spurious_we_err),
.rf_bignum_rd_a_indirect_onehot_o(rf_bignum_rd_a_indirect_onehot),
.rf_bignum_rd_b_indirect_onehot_o(rf_bignum_rd_b_indirect_onehot),
.rf_bignum_wr_indirect_onehot_o (rf_bignum_wr_indirect_onehot),
.rf_bignum_indirect_en_o (rf_bignum_indirect_en),
// To/from base ALU
.alu_base_operation_o (alu_base_operation),
.alu_base_comparison_o (alu_base_comparison),
.alu_base_operation_result_i (alu_base_operation_result),
.alu_base_comparison_result_i(alu_base_comparison_result),
// To/from bignum ALU
.alu_bignum_operation_o (alu_bignum_operation),
.alu_bignum_operation_valid_o (alu_bignum_operation_valid),
.alu_bignum_operation_commit_o(alu_bignum_operation_commit),
.alu_bignum_operation_result_i(alu_bignum_operation_result),
.alu_bignum_selection_flag_i (alu_bignum_selection_flag),
// To/from bignum MAC
.mac_bignum_operation_o (mac_bignum_operation),
.mac_bignum_operation_result_i(mac_bignum_operation_result),
.mac_bignum_en_o (mac_bignum_en),
.mac_bignum_commit_o (mac_bignum_commit),
// To/from LSU (base and bignum)
.lsu_load_req_o (lsu_load_req),
.lsu_store_req_o (lsu_store_req),
.lsu_req_subset_o (lsu_req_subset),
.lsu_addr_o (lsu_addr),
.lsu_addr_en_predec_i (lsu_addr_en_predec),
.lsu_base_wdata_o (lsu_base_wdata),
.lsu_bignum_wdata_o(lsu_bignum_wdata),
.lsu_base_rdata_i (lsu_base_rdata),
.lsu_bignum_rdata_i(lsu_bignum_rdata),
// Isprs read/write (base and bignum)
.ispr_addr_o (ispr_addr),
.ispr_base_wdata_o (ispr_base_wdata),
.ispr_base_wr_en_o (ispr_base_wr_en),
.ispr_bignum_wdata_intg_o(ispr_bignum_wdata_intg),
.ispr_bignum_wr_en_o (ispr_bignum_wr_en),
.ispr_wr_commit_o (ispr_wr_commit),
.ispr_rdata_intg_i (ispr_rdata_intg),
.ispr_rd_en_o (ispr_rd_en),
// RND interface
.rnd_req_o (rnd_req),
.rnd_prefetch_req_o(rnd_prefetch_req),
.rnd_valid_i (rnd_valid),
.urnd_reseed_err_i(urnd_reseed_err),
// Secure wipe
.secure_wipe_req_o (secure_wipe_req),
.secure_wipe_ack_i (secure_wipe_ack),
.sec_wipe_zero_i (sec_wipe_zero),
.secure_wipe_running_i (secure_wipe_running_o),
.state_reset_i(state_reset),
.insn_cnt_o (insn_cnt),
.insn_cnt_clear_i,
.mems_sec_wipe_o,
.software_errs_fatal_i,
.sideload_key_shares_valid_i,
.prefetch_en_o (prefetch_en),
.prefetch_loop_active_o (prefetch_loop_active),
.prefetch_loop_iterations_o(prefetch_loop_iterations),
.prefetch_loop_end_addr_o (prefetch_loop_end_addr),
.prefetch_loop_jump_addr_o (prefetch_loop_jump_addr),
.prefetch_ignore_errs_o (prefetch_ignore_errs),
.ctrl_flow_predec_i (ctrl_flow_predec),
.ctrl_flow_target_predec_i(ctrl_flow_target_predec),
.predec_error_o (controller_predec_error)
);
`ASSERT(InsnDataStableInStall, u_otbn_controller.state_q == OtbnStateStall |->
insn_fetch_resp_data == $past(insn_fetch_resp_data))
// Spot the fatal error bits from the controller
logic controller_fatal_err;
assign controller_fatal_err = |{controller_err_bits.fatal_software,
controller_err_bits.bad_internal_state,
controller_err_bits.reg_intg_violation};
logic non_controller_reg_intg_violation;
assign non_controller_reg_intg_violation =
|{alu_bignum_reg_intg_violation_err, mac_bignum_reg_intg_violation_err, rf_base_intg_err};
// Generate an err_bits output by combining errors from all the blocks in otbn_core
assign err_bits_d = '{
fatal_software: controller_err_bits.fatal_software,
bad_internal_state: |{controller_err_bits.bad_internal_state,
start_stop_fatal_error,
urnd_all_zero,
predec_error,
insn_addr_err,
rf_base_spurious_we_err,
mubi_err},
reg_intg_violation: |{controller_err_bits.reg_intg_violation,
non_controller_reg_intg_violation},
dmem_intg_violation: lsu_rdata_err,
imem_intg_violation: insn_fetch_err,
rnd_fips_chk_fail: rnd_fips_err,
rnd_rep_chk_fail: rnd_rep_err,
key_invalid: controller_err_bits.key_invalid,
loop: controller_err_bits.loop,
illegal_insn: controller_err_bits.illegal_insn,
call_stack: controller_err_bits.call_stack,
bad_insn_addr: controller_err_bits.bad_insn_addr,
bad_data_addr: controller_err_bits.bad_data_addr
};
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
err_bits_q <= '0;
end else begin
if (start_i && !locking_o) begin
err_bits_q <= '0;
end else begin
err_bits_q <= err_bits_q | err_bits_d;
end
end
end
assign err_bits_o = err_bits_q | err_bits_d;
// Pass an "escalation" signal down to the controller by ORing in error signals from the other
// modules in otbn_core. Note that each error signal except escalate_en_i that appears here also
// appears somewhere in err_bits_o above (checked in ErrBitsIfControllerEscalate_A)
assign controller_fatal_escalate_en =
mubi4_or_hi(escalate_en_i,
mubi4_bool_to_mubi(|{start_stop_fatal_error, urnd_all_zero, predec_error,
rf_base_intg_err, rf_base_spurious_we_err, lsu_rdata_err,
insn_fetch_err, non_controller_reg_intg_violation,
insn_addr_err}));
assign controller_recov_escalate_en =
mubi4_bool_to_mubi(|{rnd_rep_err, rnd_fips_err});
// Similarly for the start/stop controller
assign start_stop_escalate_en =
mubi4_or_hi(escalate_en_i,
mubi4_bool_to_mubi(|{urnd_all_zero, rf_base_intg_err, rf_base_spurious_we_err,
predec_error, lsu_rdata_err, insn_fetch_err,
controller_fatal_err, insn_addr_err}));
// Signal error if MuBi input signals take on invalid values as this means something bad is
// happening. The explicit error detection is required as the mubi4_or_hi operations above
// might mask invalid values depending on other input operands.
assign mubi_err = mubi4_test_invalid(escalate_en_i);
assign insn_cnt_o = insn_cnt;
// Load store unit: read and write data from data memory
otbn_lsu u_otbn_lsu (
.clk_i,
.rst_ni,
// Data memory interface
.dmem_req_o,
.dmem_write_o,
.dmem_addr_o,
.dmem_wdata_o,
.dmem_wmask_o,
.dmem_rmask_o,
.dmem_rdata_i,
.dmem_rvalid_i,
.dmem_rerror_i,
.lsu_load_req_i (lsu_load_req),
.lsu_store_req_i (lsu_store_req),
.lsu_req_subset_i(lsu_req_subset),
.lsu_addr_i (lsu_addr),
.lsu_base_wdata_i (lsu_base_wdata),
.lsu_bignum_wdata_i(lsu_bignum_wdata),
.lsu_base_rdata_o (lsu_base_rdata),
.lsu_bignum_rdata_o(lsu_bignum_rdata),
.lsu_rdata_err_o (lsu_rdata_err)
);
// Base Instruction Subset =======================================================================
otbn_rf_base #(
.RegFile(RegFile)
) u_otbn_rf_base (
.clk_i,
.rst_ni,
.state_reset_i (state_reset),
.sec_wipe_stack_reset_i(sec_wipe_zero),
.wr_addr_i (rf_base_wr_addr),
.wr_en_i (rf_base_wr_en),
.wr_data_no_intg_i (rf_base_wr_data_no_intg),
.wr_data_intg_i (rf_base_wr_data_intg),
.wr_data_intg_sel_i(rf_base_wr_data_intg_sel),
.wr_commit_i (rf_base_wr_commit),
.rd_addr_a_i (rf_base_rd_addr_a),
.rd_en_a_i (rf_base_rd_en_a),
.rd_data_a_intg_o(rf_base_rd_data_a_intg),
.rd_addr_b_i (rf_base_rd_addr_b),
.rd_en_b_i (rf_base_rd_en_b),
.rd_data_b_intg_o(rf_base_rd_data_b_intg),
.rd_commit_i (rf_base_rd_commit),
.call_stack_sw_err_o(rf_base_call_stack_sw_err),
.call_stack_hw_err_o(rf_base_call_stack_hw_err),
.intg_err_o (rf_base_intg_err),
.spurious_we_err_o (rf_base_spurious_we_err)
);
assign rf_base_wr_addr = sec_wipe_base ? sec_wipe_addr : rf_base_wr_addr_ctrl;
assign rf_base_wr_en = sec_wipe_base ? 1'b1 : rf_base_wr_en_ctrl;
assign rf_base_wr_commit = sec_wipe_base ? 1'b1 : rf_base_wr_commit_ctrl;
// Write data to Base RF
always_comb begin
if (sec_wipe_base) begin
// Wipe the Base RF with either random numbers or zeroes.
if (sec_wipe_base_urnd) begin
rf_base_wr_data_no_intg = urnd_data[31:0];
end else begin
rf_base_wr_data_no_intg = 32'b0;
end
rf_base_wr_data_intg_sel = 0;
end else begin
rf_base_wr_data_no_intg = rf_base_wr_data_no_intg_ctrl;
rf_base_wr_data_intg_sel = rf_base_wr_data_intg_sel_ctrl;
end
end
otbn_alu_base u_otbn_alu_base (
.clk_i,
.rst_ni,
.operation_i (alu_base_operation),
.comparison_i (alu_base_comparison),
.operation_result_o (alu_base_operation_result),
.comparison_result_o(alu_base_comparison_result)
);
otbn_rf_bignum #(
.RegFile(RegFile)
) u_otbn_rf_bignum (
.clk_i,
.rst_ni,
.wr_addr_i (rf_bignum_wr_addr),
.wr_en_i (rf_bignum_wr_en),
.wr_commit_i (rf_bignum_wr_commit),
.wr_data_no_intg_i (rf_bignum_wr_data_no_intg),
.wr_data_intg_i (rf_bignum_wr_data_intg),
.wr_data_intg_sel_i(rf_bignum_wr_data_intg_sel),
.rd_addr_a_i (rf_bignum_rd_addr_a),
.rd_en_a_i (rf_bignum_rd_en_a),
.rd_data_a_intg_o(rf_bignum_rd_data_a_intg),
.rd_addr_b_i (rf_bignum_rd_addr_b),
.rd_en_b_i (rf_bignum_rd_en_b),
.rd_data_b_intg_o(rf_bignum_rd_data_b_intg),
.intg_err_o(rf_bignum_intg_err),
.rf_predec_bignum_i(rf_predec_bignum),
.predec_error_o (rf_bignum_predec_error),
.spurious_we_err_o(rf_bignum_spurious_we_err)
);
always_ff @(posedge clk_i or negedge rst_ni) begin
if(!rst_ni) begin
sec_wipe_wdr_q <= 1'b0;
end else begin
sec_wipe_wdr_q <= sec_wipe_wdr_d;
end
end
always_ff @(posedge clk_i) begin
if (sec_wipe_wdr_d) begin
sec_wipe_wdr_addr_q <= sec_wipe_addr;
sec_wipe_wdr_urnd_q <= sec_wipe_wdr_urnd_d;
end
end
assign rf_bignum_wr_addr = sec_wipe_wdr_q ? sec_wipe_wdr_addr_q : rf_bignum_wr_addr_ctrl;
assign rf_bignum_wr_en = sec_wipe_wdr_q ? 2'b11 : rf_bignum_wr_en_ctrl;
assign rf_bignum_wr_commit = sec_wipe_wdr_q ? 1'b1 : rf_bignum_wr_commit_ctrl;
// Write data to WDR
always_comb begin
if (sec_wipe_wdr_q) begin
// Wipe the WDR with either random numbers or zeroes.
if (sec_wipe_wdr_urnd_q) begin
rf_bignum_wr_data_no_intg = urnd_data;
end else begin
rf_bignum_wr_data_no_intg = 256'b0;
end
rf_bignum_wr_data_intg_sel = 0;
end else begin
rf_bignum_wr_data_no_intg = rf_bignum_wr_data_no_intg_ctrl;
rf_bignum_wr_data_intg_sel = rf_bignum_wr_data_intg_sel_ctrl;
end
end
otbn_alu_bignum u_otbn_alu_bignum (
.clk_i,
.rst_ni,
.operation_i (alu_bignum_operation),
.operation_valid_i (alu_bignum_operation_valid),
.operation_commit_i(alu_bignum_operation_commit),
.operation_result_o(alu_bignum_operation_result),
.selection_flag_o (alu_bignum_selection_flag),
.alu_predec_bignum_i (alu_predec_bignum),
.ispr_predec_bignum_i(ispr_predec_bignum),
.ispr_addr_i (ispr_addr),
.ispr_base_wdata_i (ispr_base_wdata),
.ispr_base_wr_en_i (ispr_base_wr_en),
.ispr_bignum_wdata_intg_i(ispr_bignum_wdata_intg),
.ispr_bignum_wr_en_i (ispr_bignum_wr_en),
.ispr_wr_commit_i (ispr_wr_commit),
.ispr_init_i (ispr_init),
.ispr_rdata_intg_o (ispr_rdata_intg),
.ispr_rd_en_i (ispr_rd_en),
.ispr_acc_intg_i (ispr_acc_intg),
.ispr_acc_wr_data_intg_o(ispr_acc_wr_data_intg),
.ispr_acc_wr_en_o (ispr_acc_wr_en),
.reg_intg_violation_err_o(alu_bignum_reg_intg_violation_err),
.sec_wipe_mod_urnd_i(sec_wipe_mod_urnd),
.sec_wipe_zero_i (sec_wipe_zero),
.mac_operation_flags_i (mac_bignum_operation_flags),
.mac_operation_flags_en_i(mac_bignum_operation_flags_en),
.rnd_data_i (rnd_data),
.urnd_data_i(urnd_data),
.sideload_key_shares_i,
.alu_predec_error_o(alu_bignum_predec_error),
.ispr_predec_error_o(ispr_predec_error)
);
otbn_mac_bignum u_otbn_mac_bignum (
.clk_i,
.rst_ni,
.operation_i (mac_bignum_operation),
.operation_result_o (mac_bignum_operation_result),
.operation_flags_o (mac_bignum_operation_flags),
.operation_flags_en_o (mac_bignum_operation_flags_en),
.operation_intg_violation_err_o (mac_bignum_reg_intg_violation_err),
.mac_predec_bignum_i(mac_predec_bignum),
.predec_error_o (mac_bignum_predec_error),
.urnd_data_i (urnd_data),
.sec_wipe_acc_urnd_i(sec_wipe_acc_urnd),
.mac_en_i (mac_bignum_en),
.mac_commit_i(mac_bignum_commit),
.ispr_acc_intg_o (ispr_acc_intg),
.ispr_acc_wr_data_intg_i(ispr_acc_wr_data_intg),
.ispr_acc_wr_en_i (ispr_acc_wr_en)
);
otbn_rnd #(
.RndCnstUrndPrngSeed(RndCnstUrndPrngSeed)
) u_otbn_rnd (
.clk_i,
.rst_ni,
.opn_start_i (controller_start),
.opn_end_i (secure_wipe_req),
.rnd_req_i (rnd_req),
.rnd_prefetch_req_i(rnd_prefetch_req),
.rnd_valid_o (rnd_valid),
.rnd_data_o (rnd_data),
.rnd_rep_err_o (rnd_rep_err),
.rnd_fips_err_o (rnd_fips_err),
.urnd_reseed_req_i (urnd_reseed_req),
.urnd_reseed_ack_o (urnd_reseed_ack),
.urnd_advance_i (urnd_advance),
.urnd_data_o (urnd_data),
.urnd_all_zero_o (urnd_all_zero),
.edn_rnd_req_o,
.edn_rnd_ack_i,
.edn_rnd_data_i,
.edn_rnd_fips_i,
.edn_rnd_err_i,
.edn_urnd_req_o,
.edn_urnd_ack_i,
.edn_urnd_data_i
);
// Advance URND either when the start_stop_control commands it or when temporary secure wipe keys
// are requested.
// When SecMuteUrnd is enabled, signal urnd_advance_start_stop_control is muted. Therefore, it is
// necessary to enable urnd_advance using ispr_predec_bignum.ispr_rd_en[IsprUrnd] whenever URND
// data are consumed by the ALU.
assign urnd_advance = urnd_advance_start_stop_control | req_sec_wipe_urnd_keys_q |
(SecMuteUrnd & ispr_predec_bignum.ispr_rd_en[IsprUrnd]);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
req_sec_wipe_urnd_keys_q <= 1'b0;
end else begin
req_sec_wipe_urnd_keys_q <= req_sec_wipe_urnd_keys_i;
end
end
assign dmem_sec_wipe_urnd_key_o = urnd_data[127:0];
assign imem_sec_wipe_urnd_key_o = urnd_data[255:128];
// Asserts =======================================================================================
// All outputs should be known.
`ASSERT_KNOWN(DoneOKnown_A, done_o)
`ASSERT_KNOWN(ImemReqOKnown_A, imem_req_o)
`ASSERT_KNOWN_IF(ImemAddrOKnown_A, imem_addr_o, imem_req_o)
`ASSERT_KNOWN(DmemReqOKnown_A, dmem_req_o)
`ASSERT_KNOWN_IF(DmemWriteOKnown_A, dmem_write_o, dmem_req_o)
`ASSERT_KNOWN_IF(DmemAddrOKnown_A, dmem_addr_o, dmem_req_o)
`ASSERT_KNOWN_IF(DmemWdataOKnown_A, dmem_wdata_o, dmem_req_o & dmem_write_o)
`ASSERT_KNOWN_IF(DmemWmaskOKnown_A, dmem_wmask_o, dmem_req_o & dmem_write_o)
`ASSERT_KNOWN_IF(DmemRmaskOKnown_A, dmem_rmask_o, dmem_req_o)
`ASSERT_KNOWN(EdnRndReqOKnown_A, edn_rnd_req_o)
`ASSERT_KNOWN(EdnUrndReqOKnown_A, edn_urnd_req_o)
`ASSERT_KNOWN(InsnCntOKnown_A, insn_cnt_o)
`ASSERT_KNOWN(ErrBitsKnown_A, err_bits_o)
// Keep the EDN requests active until they are acknowledged.
`ASSERT(EdnRndReqStable_A, edn_rnd_req_o & ~edn_rnd_ack_i |=> edn_rnd_req_o)
`ASSERT(EdnUrndReqStable_A, edn_urnd_req_o & ~edn_urnd_ack_i |=> edn_urnd_req_o)
`ASSERT(OnlyWriteLoadDataBaseWhenDMemValid_A,
rf_bignum_wr_en_ctrl & insn_dec_bignum.rf_wdata_sel == RfWdSelLsu |-> dmem_rvalid_i)
`ASSERT(OnlyWriteLoadDataBignumWhenDMemValid_A,
rf_base_wr_en_ctrl & insn_dec_base.rf_wdata_sel == RfWdSelLsu |-> dmem_rvalid_i)
// Error handling: if we pass an error signal down to the controller then we should also be
// setting an error flag, unless the signal came from above.
`ASSERT(ErrBitsIfControllerEscalate_A,
(mubi4_test_true_loose(controller_fatal_escalate_en) ||
mubi4_test_true_loose(controller_recov_escalate_en)) &&
mubi4_test_false_strict(escalate_en_i)
|=> err_bits_q)
// Similarly, if we pass an escalation signal down to the start/stop controller then we should
// also be setting an error flag, unless the signal came from above.
`ASSERT(ErrBitsIfStartStopEscalate_A,
mubi4_test_true_loose(start_stop_escalate_en) && mubi4_test_false_strict(escalate_en_i)
|=> err_bits_q)
// The following assertions allow up to 400 cycles from escalation until the start/stop FSM locks.
// This is a long time, but it's necessary because following an escalation the start/stop FSM goes
// through two rounds of secure wiping with random data with an URND reseed in between. Depending
// on the delay configured in the EDN model, the reseed alone can take 200 cycles.
`ASSERT(OtbnStartStopGlobalEscCntrMeasure_A, err_bits_q && mubi4_test_true_loose(escalate_en_i)
&& mubi4_test_true_loose(start_stop_escalate_en)|=> ##[1:400]
u_otbn_start_stop_control.state_q == otbn_pkg::OtbnStartStopStateLocked)
`ASSERT(OtbnStartStopLocalEscCntrMeasure_A, err_bits_q && mubi4_test_false_strict(escalate_en_i)
&& mubi4_test_true_loose(start_stop_escalate_en) |=> ##[1:400]
u_otbn_start_stop_control.state_q == otbn_pkg::OtbnStartStopStateLocked)
// In contrast to the start/stop FSM, the controller FSM should lock quickly after an escalation,
// independent of the secure wipe.
`ASSERT(OtbnControllerGlobalEscCntrMeasure_A, err_bits_q && mubi4_test_true_loose(escalate_en_i)
&& mubi4_test_true_loose(controller_fatal_escalate_en)|=> ##[1:100]
u_otbn_controller.state_q == otbn_pkg::OtbnStateLocked)
`ASSERT(OtbnControllerLocalEscCntrMeasure_A, err_bits_q && mubi4_test_false_strict(escalate_en_i)
&& mubi4_test_true_loose(controller_fatal_escalate_en) |=> ##[1:100]
u_otbn_controller.state_q == otbn_pkg::OtbnStateLocked)
endmodule