[otp_ctrl] Implement KDI
This implements the key derivation interface inside the OTP controller.
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index 8f35715..c085117 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -293,15 +293,13 @@
otp_ctrl_lfsr_timer #(
.LfsrSeed(TimerLfsrSeed),
- .LfsrPerm(TimerLfsrPerm),
- .EntropyWidth(4)
+ .LfsrPerm(TimerLfsrPerm)
) u_otp_ctrl_lfsr_timer (
.clk_i,
.rst_ni,
.edn_req_o ( lfsr_edn_req ),
.edn_ack_i ( lfsr_edn_ack ),
- // Lower entropy bits are used for reseeding secure erase LFSRs
- .edn_data_i ( otp_edn_rsp_i.data[31:28] ),
+ .edn_data_i ( otp_edn_rsp_i.data ),
// We can enable the timer once OTP has initialized.
// Note that this is only the initial release that gets
// the timer FSM into an operational state.
@@ -577,7 +575,7 @@
) u_otp_ctrl_lci (
.clk_i,
.rst_ni,
- .init_req_i ( part_init_req ),
+ .lci_en_i ( pwr_otp_rsp_o.otp_done ),
.escalate_en_i ( lc_escalate_en_i ),
.error_o ( part_error[LciIdx] ),
.lci_idle_o ( lci_idle ),
@@ -618,7 +616,7 @@
otp_ctrl_kdi i_otp_ctrl_kdi (
.clk_i,
.rst_ni,
- .key_deriv_en_i ( pwr_otp_rsp_o.otp_done ),
+ .kdi_en_i ( pwr_otp_rsp_o.otp_done ),
.escalate_en_i ( lc_escalate_en_i ),
.fsm_err_o ( key_deriv_fsm_err ),
.scrmbl_key_seed_valid_i ( scrmbl_key_seed_valid ),
@@ -808,6 +806,7 @@
// Assertions //
////////////////
+ `ASSERT_KNOWN(OtpAstPwrSeqKnown_A, otp_ast_pwr_seq_o)
`ASSERT_KNOWN(TlOutKnown_A, tl_o)
`ASSERT_KNOWN(IntrOtpOperationDoneKnown_A, intr_otp_operation_done_o)
`ASSERT_KNOWN(IntrOtpErrorKnown_A, intr_otp_error_o)
@@ -816,7 +815,7 @@
`ASSERT_KNOWN(LcOtpProgramRspKnown_A, lc_otp_program_rsp_o)
`ASSERT_KNOWN(OtpLcDataKnown_A, otp_lc_data_o)
`ASSERT_KNOWN(OtpKeymgrKeyKnown_A, otp_keymgr_key_o)
- `ASSERT_KNOWN(OtpFlashKeyKnown_A, flash_otp_key_rsp_o)
+ `ASSERT_KNOWN(FlashOtpKeyRspKnown_A, flash_otp_key_rsp_o)
`ASSERT_KNOWN(OtpSramKeyKnown_A, sram_otp_key_rsp_o)
`ASSERT_KNOWN(OtpOtgnKeyKnown_A, otbn_otp_key_rsp_o)
`ASSERT_KNOWN(HwCfgKnown_A, hw_cfg_o)
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
index 1405d5d..daf7cd9 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
@@ -169,7 +169,7 @@
// Scrambling datapath
scrmbl_cmd_o = LoadShadow;
- scrmbl_sel_o = '0;
+ scrmbl_sel_o = CnstyDigest;
scrmbl_valid_o = 1'b0;
// Counter
@@ -235,16 +235,16 @@
if (dai_req_i) begin
// This clears previous (recoverable) errors.
error_d = NoErr;
- // Clear the temporary data register.
- data_clr = 1'b1;
unique case (dai_cmd_i)
DaiRead: begin
state_d = ReadSt;
+ // Clear the temporary data register.
+ data_clr = 1'b1;
base_sel_d = DaiOffset;
end
DaiWrite: begin
- // Fetch data block.
data_sel = DaiData;
+ // Fetch data block.
data_en = 1'b1;
base_sel_d = DaiOffset;
// If this partition is scrambled, directly go to write scrambling first.
@@ -319,7 +319,7 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
scrmbl_cmd_o = Decrypt;
- scrmbl_sel_o = PartInfo[part_idx].key_idx;
+ scrmbl_sel_o = PartInfo[part_idx].key_sel;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = DescrWaitSt;
end
@@ -329,7 +329,7 @@
// the mutex lock upon leaving this state.
DescrWaitSt: begin
scrmbl_mtx_req_o = 1'b1;
- scrmbl_sel_o = PartInfo[part_idx].key_idx;
+ scrmbl_sel_o = PartInfo[part_idx].key_sel;
data_sel = ScrmblData;
if (scrmbl_valid_i) begin
state_d = IdleSt;
@@ -394,7 +394,7 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
scrmbl_cmd_o = Encrypt;
- scrmbl_sel_o = PartInfo[part_idx].key_idx;
+ scrmbl_sel_o = PartInfo[part_idx].key_sel;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = ScrWaitSt;
end
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
index 0a3e1e0..8e117fb 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
@@ -19,7 +19,7 @@
input rst_ni,
// Pulse to enable this module after OTP partitions have
// been initialized.
- input key_deriv_en_i,
+ input kdi_en_i,
// Escalation input. This moves the FSM into a terminal state.
input lc_tx_t escalate_en_i,
// FSM is in error state
@@ -32,9 +32,9 @@
// EDN interface for requesting entropy
output logic edn_req_o,
input edn_ack_i,
- input [31:0] edn_data_i,
+ input [EdnDataWidth-1:0] edn_data_i,
// Lifecycle hashing request
- input lc_otp_token_rsp_t lc_otp_token_req_i,
+ input lc_otp_token_req_t lc_otp_token_req_i,
output lc_otp_token_rsp_t lc_otp_token_rsp_o,
// Scrambling key requests
input flash_otp_key_req_t flash_otp_key_req_i,
@@ -56,43 +56,454 @@
input logic [ScrmblBlockWidth-1:0] scrmbl_data_i
);
- // tie-off and unused assignments for preventing lint messages
- assign fsm_err_o = 1'b0;
- assign edn_req_o = '0;
- assign lc_otp_token_rsp_o = '0;
- assign flash_otp_key_rsp_o = '0;
- assign sram_otp_key_rsp_o = '0;
- assign otbn_otp_key_rsp_o = '0;
- assign scrmbl_mtx_req_o = '0;
- assign scrmbl_cmd_o = '0;
- assign scrmbl_sel_o = '0;
- assign scrmbl_data_o = '0;
- assign scrmbl_valid_o = '0;
+ import prim_util_pkg::vbits;
- logic unused_sigs_d, unused_sigs_q;
- assign unused_sigs_d = ^{key_deriv_en_i,
- escalate_en_i,
- scrmbl_key_seed_valid_i,
- flash_data_key_seed_i,
- flash_addr_key_seed_i,
- sram_data_key_seed_i,
- edn_ack_i,
- edn_data_i,
- lc_otp_token_req_i,
- flash_otp_key_req_i,
- sram_otp_key_req_i,
- otbn_otp_key_req_i,
- scrmbl_mtx_gnt_i,
- scrmbl_ready_i,
- scrmbl_valid_i,
- scrmbl_data_i};
+ ////////////////////////
+ // Integration Checks //
+ ////////////////////////
+
+ // LC, 2xFlash, OTBN + SRAM slots
+ localparam int NumReq = 4 + NumSramKeyReqSlots;
+ // Make sure key sizes in the system are multiples of 64bit and not larger than 256bit.
+ `ASSERT_INIT(KeyNonceSize0_A, (FlashKeySeedWidth <= 256) && ((FlashKeySeedWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize1_A, (SramKeySeedWidth <= 256) && ((SramKeySeedWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize2_A, (FlashKeyWidth <= 256) && ((FlashKeyWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize3_A, (SramKeyWidth <= 256) && ((SramKeyWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize4_A, (SramNonceWidth <= 256) && ((SramNonceWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize5_A, (OtbnKeyWidth <= 256) && ((OtbnKeyWidth % 64) == 0))
+ `ASSERT_INIT(KeyNonceSize6_A, (OtbnNonceWidth <= 256) && ((OtbnNonceWidth % 64) == 0))
+
+ // Make sure EDN interface has compatible width.
+ `ASSERT_INIT(EntropyWidthDividesDigestBlockWidth_A, (ScrmblKeyWidth % EdnDataWidth) == 0)
+
+ ///////////////////////////////////
+ // Input Mapping and Arbitration //
+ ///////////////////////////////////
+
+ // The key derivation and token hashing functions are aligned such that 2 x 128bit key
+ // seeds / token blocks are processed in two subsequent steps using the digest primitive.
+ // This effectively compresses these blocks down into 2 x 64bit blocks, thereby creating
+ // one 128bit key or token output.
+ //
+ // The same FSM is shared among the different flavors of key derivation and token
+ // hashing functions, and the following configuration options are available:
+ //
+ // 1) ingest an additional 128bit entropy block after ingesting a 128bit key seed.
+ // 2) keep digest state after producing the first 64bit block instead of reverting to the IV.
+ // 3) netlist constant index.
+ // 4) fetch additional entropy for the nonce output.
+ // 5) whether or not the key seed is valid. if not, it will be defaulted to '0.
+ // 6) 256bit key seed / token input.
+ //
+ // The configuration options are set further below, depending on the request type.
+
+ typedef struct packed {
+ logic ingest_entropy; // 1)
+ logic chained_digest; // 2)
+ digest_sel_e digest_sel; // 3)
+ logic fetch_nonce; // 4)
+ logic [1:0] nonce_size; // 4)
+ logic seed_valid; // 5)
+ logic [3:0][ScrmblBlockWidth-1:0] seed; // 6)
+ } req_bundle_t;
+
+ logic [NumReq-1:0] req, gnt;
+ req_bundle_t req_bundles [NumReq];
+
+ assign req[0] = lc_otp_token_req_i.req;
+ assign req[1] = flash_otp_key_req_i.data_req;
+ assign req[2] = flash_otp_key_req_i.addr_req;
+ assign req[3] = otbn_otp_key_req_i.req;
+
+ assign lc_otp_token_rsp_o.ack = gnt[0];
+ assign flash_otp_key_rsp_o.data_ack = gnt[1];
+ assign flash_otp_key_rsp_o.addr_ack = gnt[2];
+ assign otbn_otp_key_rsp_o.ack = gnt[3];
+
+ // Life cycle unlock token.
+ assign req_bundles[0] = '{ingest_entropy: 1'b0, // no random entropy added
+ chained_digest: 1'b1, // do not revert to netlist IV between blocks
+ digest_sel: LcRawDigest,
+ fetch_nonce: 1'b0, // no nonce needed
+ nonce_size: '0,
+ seed_valid: 1'b1, // valid if request is valid
+ seed: {lc_otp_token_req_i.token_input, // reuse same seed
+ lc_otp_token_req_i.token_input}};
+ // Flash data key
+ assign req_bundles[1] = '{ingest_entropy: 1'b0, // no random entropy added
+ chained_digest: 1'b0, // revert to netlist IV between blocks
+ digest_sel: FlashDataKey,
+ fetch_nonce: 1'b0, // no nonce needed
+ nonce_size: '0,
+ seed_valid: scrmbl_key_seed_valid_i,
+ seed: flash_data_key_seed_i}; // 2x128bit
+ // Flash addr key
+ assign req_bundles[2] = '{ingest_entropy: 1'b0, // no random entropy added
+ chained_digest: 1'b0, // revert to netlist IV between blocks
+ digest_sel: FlashAddrKey,
+ fetch_nonce: 1'b0, // no nonce needed
+ nonce_size: '0,
+ seed_valid: scrmbl_key_seed_valid_i,
+ seed: flash_addr_key_seed_i}; // 2x128bit
+ // OTBN key
+ assign req_bundles[3] = '{ingest_entropy: 1'b1, // ingest random data
+ chained_digest: 1'b0, // revert to netlist IV between blocks
+ digest_sel: SramDataKey,
+ fetch_nonce: 1'b1, // fetch nonce
+ nonce_size: 2'(OtbnNonceWidth/64-1),
+ seed_valid: scrmbl_key_seed_valid_i,
+ seed: {sram_data_key_seed_i, // reuse same seed
+ sram_data_key_seed_i}};
+
+ // SRAM keys
+ for (genvar k = 4; k < NumReq; k++) begin : gen_req_assign
+ assign req[k] = sram_otp_key_req_i[k-4].req;
+ assign sram_otp_key_rsp_o[k-4].ack = gnt[k];
+ assign req_bundles[k] = '{ingest_entropy: 1'b1, // ingest random data
+ chained_digest: 1'b0, // revert to netlist IV between blocks
+ digest_sel: SramDataKey,
+ fetch_nonce: 1'b1, // fetch nonce
+ nonce_size: 2'(SramNonceWidth/64-1),
+ seed_valid: scrmbl_key_seed_valid_i,
+ seed: {sram_data_key_seed_i, // reuse same seed
+ sram_data_key_seed_i}};
+ end
+
+ // This arbitrates among incoming key derivation requests on a
+ // round robin basis to prevent deadlock.
+ logic req_valid, req_ready;
+ req_bundle_t req_bundle;
+
+ prim_arbiter_tree #(
+ .N(NumReq),
+ .DW($bits(req_bundle_t)))
+ u_req_arb (
+ .clk_i,
+ .rst_ni,
+ .req_i ( req ),
+ .data_i ( req_bundles ),
+ .gnt_o ( gnt ),
+ .idx_o ( ),
+ .valid_o ( req_valid ),
+ .data_o ( req_bundle ),
+ .ready_i ( req_ready )
+ );
+
+ //////////////////////////////
+ // Temporary Regs and Muxes //
+ //////////////////////////////
+
+ logic seed_cnt_clr, seed_cnt_en, entropy_cnt_clr, entropy_cnt_en;
+ logic [1:0] seed_cnt_d, seed_cnt_q, entropy_cnt_d, entropy_cnt_q;
+
+ assign seed_cnt_d = (seed_cnt_clr) ? '0 :
+ (seed_cnt_en) ? seed_cnt_q + 1'b1 : seed_cnt_q;
+ assign entropy_cnt_d = (entropy_cnt_clr) ? '0 :
+ (entropy_cnt_en) ? entropy_cnt_q + 1'b1 : entropy_cnt_q;
+
+ logic key_reg_en, nonce_reg_en;
+ logic [ScrmblKeyWidth/ScrmblBlockWidth-1:0][ScrmblBlockWidth-1:0] key_out_d, key_out_q;
+ logic [3:0][ScrmblBlockWidth-1:0] nonce_out_d, nonce_out_q;
+
+ always_comb begin : p_outregs
+ key_out_d = key_out_q;
+ nonce_out_d = nonce_out_q;
+ if (key_reg_en) begin
+ key_out_d[seed_cnt_q[1]] = scrmbl_data_i;
+ end
+ if (nonce_reg_en) begin
+ nonce_out_d[entropy_cnt_q] = edn_data_i;
+ end
+ end
+
+ // Connect keys/nonce outputs to output regs.
+ assign lc_otp_token_rsp_o.hashed_token = key_out_q;
+ assign flash_otp_key_rsp_o.key = key_out_q;
+ assign otbn_otp_key_rsp_o.key = key_out_q;
+ assign otbn_otp_key_rsp_o.nonce = nonce_out_q;
+
+ for (genvar k = 0; k < NumSramKeyReqSlots; k++) begin : gen_out_assign
+ assign sram_otp_key_rsp_o[k].key = key_out_q;
+ assign sram_otp_key_rsp_o[k].nonce = nonce_out_q[0];
+ end
+
+ typedef enum logic {
+ SeedData,
+ EntropyData
+ } data_sel_e;
+
+ // Select correct 64bit block.
+ data_sel_e data_sel;
+ assign scrmbl_data_o = (data_sel == EntropyData) ? edn_data_i :
+ // Gate seed value to '0 if invalid.
+ (req_bundle.seed_valid) ? req_bundle.seed[seed_cnt_q] : '0;
+
+ /////////////////
+ // Control FSM //
+ /////////////////
+
+ // Encoding generated with ./sparse-fsm-encode.py -d 5 -m 10 -n 10 -s 2544133835
+ // Hamming distance histogram:
+ //
+ // 0: --
+ // 1: --
+ // 2: --
+ // 3: --
+ // 4: --
+ // 5: |||||||||||||||||||| (55.56%)
+ // 6: |||||||||||||||| (44.44%)
+ // 7: --
+ // 8: --
+ // 9: --
+ // 10: --
+ //
+ // Minimum Hamming distance: 5
+ // Maximum Hamming distance: 6
+ //
+ localparam int StateWidth = 10;
+ typedef enum logic [StateWidth-1:0] {
+ ResetSt = 10'b0111100000,
+ IdleSt = 10'b0001111101,
+ DigClrSt = 10'b1101101011,
+ DigLoadSt = 10'b0100011010,
+ DigEntropySt = 10'b0010001001,
+ DigFinSt = 10'b0110110111,
+ DigWaitSt = 10'b0001000110,
+ FetchNonceSt = 10'b1100000101,
+ FinishSt = 10'b1010101110,
+ ErrorSt = 10'b1111011100
+ } state_e;
+
+ state_e state_d, state_q;
+
+ always_comb begin : p_fsm
+ state_d = state_q;
+
+ // FSM Error output
+ fsm_err_o = 1'b0;
+
+ // Counters
+ seed_cnt_en = 1'b0;
+ seed_cnt_clr = 1'b0;
+ entropy_cnt_en = 1'b0;
+ entropy_cnt_clr = 1'b0;
+
+ // EDN 128bit block fetch request
+ edn_req_o = 1'b0;
+
+ // Data selection and temp registers
+ data_sel = SeedData;
+ key_reg_en = 1'b0;
+ nonce_reg_en = 1'b0;
+
+ // Scrambling datapath
+ scrmbl_mtx_req_o = 1'b0;
+ scrmbl_sel_o = req_bundle.digest_sel;
+ scrmbl_cmd_o = LoadShadow;
+ scrmbl_valid_o = 1'b0;
+
+ // Request acknowledgement
+ req_ready = 1'b0;
+
+ unique case (state_q)
+ ///////////////////////////////////////////////////////////////////
+ // State right after reset. Wait here until KDI gets enabled.
+ ResetSt: begin
+ if (kdi_en_i) begin
+ state_d = IdleSt;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Wait for a request, then go and acquire the mutex.
+ IdleSt: begin
+ if (req_valid) begin
+ state_d = DigClrSt;
+ seed_cnt_clr = 1'b1;
+ entropy_cnt_clr = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // First, acquire the mutex for the digest and clear the digest state.
+ DigClrSt: begin
+ scrmbl_mtx_req_o = 1'b1;
+ scrmbl_valid_o = 1'b1;
+ // Need to reset the digest state and set digest mode to "standard".
+ scrmbl_sel_o = StandardMode;
+ scrmbl_cmd_o = DigestInit;
+ if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
+ state_d = DigLoadSt;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Load two 64bit blocks of the seed, and trigger digest calculation.
+ DigLoadSt: begin
+ scrmbl_mtx_req_o = 1'b1;
+ scrmbl_valid_o = 1'b1;
+ // Trigger digest round in case this is the second block in a row.
+ if (seed_cnt_q[0]) begin
+ scrmbl_cmd_o = Digest;
+ if (scrmbl_ready_i) begin
+ // Go and ingest a block of entropy if required.
+ if (req_bundle.ingest_entropy) begin
+ state_d = DigEntropySt;
+ // Otherwise go to digest finalization state.
+ end else begin
+ state_d = DigFinSt;
+ end
+ end
+ // Just load first 64bit block and stay here.
+ end else if (scrmbl_ready_i) begin
+ seed_cnt_en = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Load two 64bit blocks of entropy data.
+ DigEntropySt: begin
+ scrmbl_mtx_req_o = 1'b1;
+ scrmbl_valid_o = 1'b1;
+ // Trigger digest round in case this is the second block in a row,
+ // and go to digest finalization.
+ if (entropy_cnt_q[0]) begin
+ scrmbl_cmd_o = Digest;
+ if (scrmbl_ready_i) begin
+ state_d = DigFinSt;
+ entropy_cnt_clr = 1'b1;
+ end
+ // Just load first 64bit block and stay here.
+ end else if (scrmbl_ready_i) begin
+ entropy_cnt_en = 1'b1;
+ end
+ end
+
+ ///////////////////////////////////////////////////////////////////
+ // Trigger digest finalization and go wait for the result.
+ DigFinSt: begin
+ scrmbl_mtx_req_o = 1'b1;
+ scrmbl_valid_o = 1'b1;
+ scrmbl_cmd_o = DigestFinalize;
+ if (scrmbl_ready_i) begin
+ state_d = DigWaitSt;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Wait for the digest to return, and write the result to the key
+ // output register. Go back and process the second part of the
+ // input seed if needed.
+ DigWaitSt: begin
+ scrmbl_mtx_req_o = 1'b1;
+ if (scrmbl_valid_i) begin
+ key_reg_en = 1'b1;
+ // Not finished yet, need to go back and produce second 64bit block.
+ if (seed_cnt_q == 2'h1) begin
+ seed_cnt_en = 1'b1;
+ // In this case the previous digest state is kept,
+ // which leads to a chained digest.
+ if (req_bundle.chained_digest) begin
+ state_d = DigLoadSt;
+ // In this case we revert the digest state to the netlist IV.
+ end else begin
+ state_d = DigClrSt;
+ end
+ // This was the second 64bit output block.
+ end else begin
+ seed_cnt_clr = 1'b1;
+ // Check whether we need to fetch additional nonce data.
+ if (req_bundle.fetch_nonce) begin
+ state_d = FetchNonceSt;
+ end else begin
+ // Finished, go and acknowledge this request.
+ state_d = FinishSt;
+ end
+ end
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Fetch additional nonce data. Note that the mutex is released in
+ // this state.
+ FetchNonceSt: begin
+ edn_req_o = 1'b1;
+ if (edn_ack_i) begin
+ nonce_reg_en = 1'b1;
+ // Finished, go and acknowledge this request.
+ if (entropy_cnt_q == req_bundle.nonce_size) begin
+ state_d = FinishSt;
+ entropy_cnt_clr = 1'b1;
+ // Keep on requesting entropy.
+ end else begin
+ entropy_cnt_en = 1'b1;
+ end
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Acknowledge request and go back to IdleSt.
+ FinishSt: begin
+ state_d = IdleSt;
+ req_ready = 1'b1;
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Terminal error state. This raises an alert.
+ ErrorSt: begin
+ fsm_err_o = 1'b1;
+ end
+ ///////////////////////////////////////////////////////////////////
+ // This should never happen, hence we directly jump into the
+ // error state, where an alert will be triggered.
+ default: begin
+ state_d = ErrorSt;
+ end
+ ///////////////////////////////////////////////////////////////////
+ endcase // state_q
+
+ // Unconditionally jump into the terminal error state in case of escalation.
+ if (escalate_en_i != Off) begin
+ state_d = ErrorSt;
+ end
+ end
+
+ ///////////////
+ // Registers //
+ ///////////////
+
+ // This primitive is used to place a size-only constraint on the
+ // flops in order to prevent FSM state encoding optimizations.
+ prim_flop #(
+ .Width(StateWidth),
+ .ResetValue(StateWidth'(ResetSt))
+ ) u_state_regs (
+ .clk_i,
+ .rst_ni,
+ .d_i ( state_d ),
+ .q_o ( state_q )
+ );
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
- unused_sigs_q <= '0;
+ seed_cnt_q <= '0;
+ entropy_cnt_q <= '0;
+ key_out_q <= '0;
+ nonce_out_q <= '0;
end else begin
- unused_sigs_q <= unused_sigs_d;
+ seed_cnt_q <= seed_cnt_d;
+ entropy_cnt_q <= entropy_cnt_d;
+ key_out_q <= key_out_d;
+ nonce_out_q <= nonce_out_d;
end
end
+ ////////////////
+ // Assertions //
+ ////////////////
+
+ `ASSERT_KNOWN(FsmErrKnown_A, fsm_err_o)
+ `ASSERT_KNOWN(EdnReqKnown_A, edn_req_o)
+ `ASSERT_KNOWN(LcOtpTokenRspKnown_A, lc_otp_token_rsp_o)
+ `ASSERT_KNOWN(FlashOtpKeyRspKnown_A, flash_otp_key_rsp_o)
+ `ASSERT_KNOWN(SramOtpKeyRspKnown_A, sram_otp_key_rsp_o)
+ `ASSERT_KNOWN(OtbnOtpKeyRspKnown_A, otbn_otp_key_rsp_o)
+ `ASSERT_KNOWN(ScrmblMtxReqKnown_A, scrmbl_mtx_req_o)
+ `ASSERT_KNOWN(ScrmblCmdKnown_A, scrmbl_cmd_o)
+ `ASSERT_KNOWN(ScrmblSelKnown_A, scrmbl_sel_o)
+ `ASSERT_KNOWN(ScrmblDataKnown_A, scrmbl_data_o)
+ `ASSERT_KNOWN(ScrmblValidKnown_A, scrmbl_valid_o)
+
endmodule : otp_ctrl_kdi
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_lci.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_lci.sv
index 8947989..5e2c73c 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_lci.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_lci.sv
@@ -16,7 +16,7 @@
) (
input clk_i,
input rst_ni,
- input init_req_i,
+ input lci_en_i,
// Escalation input. This moves the FSM into a terminal state and locks down
// the partition.
input lc_tx_t escalate_en_i,
@@ -117,10 +117,9 @@
unique case (state_q)
///////////////////////////////////////////////////////////////////
- // State right after reset. Wait here until we get a an
- // initialization request.
+ // State right after reset. Wait here until LCI gets enabled.
ResetSt: begin
- if (init_req_i) begin
+ if (lci_en_i) begin
state_d = IdleSt;
end
end
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
index 5809176..bef05c3 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
@@ -31,8 +31,6 @@
module otp_ctrl_lfsr_timer import otp_ctrl_pkg::*; #(
// Entropy reseeding is triggered every time this counter expires.
parameter int ReseedTimerWidth = 16,
- // Number of LFSR LSBs to reseed
- parameter int EntropyWidth = 4,
parameter logic [TimerWidth-1:0] LfsrSeed = TimerWidth'(1'b1),
parameter logic [TimerWidth-1:0][31:0] LfsrPerm = {
32'd13, 32'd17, 32'd29, 32'd11, 32'd28, 32'd12, 32'd33, 32'd27,
@@ -46,7 +44,7 @@
input rst_ni,
output logic edn_req_o, // request to EDN
input edn_ack_i, // ack from EDN
- input [EntropyWidth-1:0] edn_data_i, // from EDN
+ input [EdnDataWidth-1:0] edn_data_i, // from EDN
input timer_en_i, // enable timer
input integ_chk_trig_i, // one-off trigger for integrity check
input cnsty_chk_trig_i, // one-off trigger for consistency check
@@ -84,7 +82,7 @@
prim_lfsr #(
.LfsrDw ( TimerWidth ),
- .EntropyDw ( EntropyWidth ),
+ .EntropyDw ( TimerWidth ),
.StateOutDw ( TimerWidth ),
.DefaultSeed ( LfsrSeed ),
.StatePermEn ( 1'b1 ),
@@ -95,11 +93,15 @@
.rst_ni,
.seed_en_i ( 1'b0 ),
.seed_i ( '0 ),
- .lfsr_en_i ( lfsr_en | reseed_en ),
- .entropy_i ( edn_data_i & {EntropyWidth{reseed_en}} ),
+ .lfsr_en_i ( lfsr_en | reseed_en ),
+ .entropy_i ( edn_data_i[TimerWidth-1:0] & {TimerWidth{reseed_en}} ),
.state_o ( lfsr_state )
);
+ // Not all entropy bits are used.
+ logic unused_seed;
+ assign unused_seed = ^edn_data_i;
+
//////////////
// Counters //
//////////////
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
index 88f6729..c11bc28 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
@@ -165,7 +165,7 @@
// Scrambling datapath
scrmbl_cmd_o = LoadShadow;
- scrmbl_sel_o = '0;
+ scrmbl_sel_o = CnstyDigest;
scrmbl_valid_o = 1'b0;
// Counter
@@ -246,7 +246,7 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
scrmbl_cmd_o = Decrypt;
- scrmbl_sel_o = Info.key_idx;
+ scrmbl_sel_o = Info.key_sel;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = InitDescrWaitSt;
end
@@ -256,7 +256,7 @@
// the mutex lock upon leaving this state.
InitDescrWaitSt: begin
scrmbl_mtx_req_o = 1'b1;
- scrmbl_sel_o = Info.key_idx;
+ scrmbl_sel_o = Info.key_sel;
data_sel = ScrmblData;
if (scrmbl_valid_i) begin
state_d = InitSt;
@@ -388,7 +388,7 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
scrmbl_cmd_o = Encrypt;
- scrmbl_sel_o = Info.key_idx;
+ scrmbl_sel_o = Info.key_sel;
if (scrmbl_ready_i) begin
state_d = IntegScrWaitSt;
end
@@ -397,7 +397,7 @@
// Wait for the scrambled data to return.
IntegScrWaitSt: begin
scrmbl_mtx_req_o = 1'b1;
- scrmbl_sel_o = Info.key_idx;
+ scrmbl_sel_o = Info.key_sel;
if (scrmbl_valid_i) begin
state_d = IntegDigSt;
end
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
index facc70e..fd277be 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
@@ -12,6 +12,9 @@
// General Parameters //
////////////////////////
+ // Width of entropy input
+ parameter int EdnDataWidth = 64;
+
parameter int NumPart = 7;
parameter int NumPartWidth = vbits(NumPart);
// This defines the width of the check timers and LFSR
@@ -85,7 +88,6 @@
parameter int ScrmblKeyWidth = 128;
parameter int ScrmblBlockWidth = 64;
- parameter int DigestBlockWidth = 64;
parameter int NumPresentRounds = 31;
parameter int ScrmblBlockHalfWords = ScrmblBlockWidth / OtpWidth;
@@ -131,9 +133,23 @@
};
typedef enum logic [ConstSelWidth-1:0] {
+ Secret0Key,
+ Secret1Key,
+ Secret2Key
+ } key_sel_e;
+
+ typedef enum logic [ConstSelWidth-1:0] {
+ CnstyDigest,
+ LcRawDigest,
+ FlashDataKey,
+ FlashAddrKey,
+ SramDataKey
+ } digest_sel_e;
+
+ typedef enum logic [ConstSelWidth-1:0] {
StandardMode,
ChainedMode
- } otp_digest_mode_e;
+ } digest_mode_e;
////////////////////////
// Partition Metadata //
@@ -150,7 +166,7 @@
logic [OtpByteAddrWidth-1:0] offset;
logic [OtpByteAddrWidth-1:0] size;
// Key index to use for scrambling.
- logic [ConstSelWidth-1:0] key_idx;
+ key_sel_e key_sel;
// Attributes
logic scrambled; // Whether the partition is scrambled
logic hw_digest; // Whether the partition has a hardware digest
@@ -160,21 +176,21 @@
// TODO: need to parse this somehow from an hjson
localparam part_info_t PartInfo [NumPart] = '{
- // Variant | offset | size | key_idx | scrambled | HW digest | write_lock | read_lock
+ // Variant | offset | size | key_sel | scrambled | HW digest | write_lock | read_lock
// CREATOR_SW_CFG
- '{Unbuffered, 11'h0, 768, 0, 1'b0, 1'b0, 1'b1, 1'b0},
+ '{Unbuffered, 11'h0, 768, Secret0Key, 1'b0, 1'b0, 1'b1, 1'b0},
// OWNER_SW_CFG
- '{Unbuffered, 11'h300, 768, 0, 1'b0, 1'b0, 1'b1, 1'b0},
+ '{Unbuffered, 11'h300, 768, Secret0Key, 1'b0, 1'b0, 1'b1, 1'b0},
// HW_CFG
- '{Buffered, 11'h600, 176, 0, 1'b0, 1'b1, 1'b1, 1'b0},
+ '{Buffered, 11'h600, 176, Secret0Key, 1'b0, 1'b1, 1'b1, 1'b0},
// SECRET0
- '{Buffered, 11'h6B0, 40, 0, 1'b1, 1'b1, 1'b1, 1'b1},
+ '{Buffered, 11'h6B0, 40, Secret0Key, 1'b1, 1'b1, 1'b1, 1'b1},
// SECRET1
- '{Buffered, 11'h6D8, 88, 1, 1'b1, 1'b1, 1'b1, 1'b1},
+ '{Buffered, 11'h6D8, 88, Secret1Key, 1'b1, 1'b1, 1'b1, 1'b1},
// SECRET2
- '{Buffered, 11'h730, 120, 2, 1'b1, 1'b1, 1'b1, 1'b1},
+ '{Buffered, 11'h730, 120, Secret2Key, 1'b1, 1'b1, 1'b1, 1'b1},
// LIFE_CYCLE
- '{Buffered, 11'h7A8, 88, 0, 1'b0, 1'b0, 1'b0, 1'b0}
+ '{Buffered, 11'h7A8, 88, Secret0Key, 1'b0, 1'b0, 1'b0, 1'b0}
};
typedef enum {
@@ -201,20 +217,14 @@
// Typedefs for CSRNG //
////////////////////////
- // Unidirectional input type for LFSR reseeding.
- typedef struct packed {
- logic en;
- logic [31:0] data;
- } edn_otp_up_t;
-
// Bidirectional entropy requests for scramble key derivation.
typedef struct packed {
logic req;
} otp_edn_req_t;
typedef struct packed {
- logic ack;
- logic [31:0] data;
+ logic ack;
+ logic [EdnDataWidth-1:0] data;
} otp_edn_rsp_t;
///////////////////////////////
@@ -280,8 +290,13 @@
} lc_otp_token_req_t;
typedef struct packed {
+<<<<<<< HEAD
logic ack;
logic [LcTokenWidth-1:0] hashed_token;
+=======
+ logic ack;
+ logic [lc_ctrl_pkg::LcTokenWidth-1:0] hashed_token;
+>>>>>>> cd99a29a (fix)
} lc_otp_token_rsp_t;
@@ -321,7 +336,8 @@
} otp_keymgr_key_t;
typedef struct packed {
- logic req;
+ logic addr_req;
+ logic data_req;
} flash_otp_key_req_t;
typedef struct packed {
@@ -333,9 +349,9 @@
} otbn_otp_key_req_t;
typedef struct packed {
- logic ack;
- logic [FlashKeyWidth-1:0] addr_key;
- logic [FlashKeyWidth-1:0] data_key;
+ logic data_ack;
+ logic addr_ack;
+ logic [FlashKeyWidth-1:0] key;
} flash_otp_key_rsp_t;
typedef struct packed {
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
index 8fba552..509bfbf 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
@@ -142,7 +142,7 @@
key_state_sel_e key_state_sel;
logic data_state_en, data_shadow_copy, data_shadow_load, digest_state_en, key_state_en;
logic [ConstSelWidth-1:0] sel_d, sel_q;
- otp_digest_mode_e digest_mode_d, digest_mode_q;
+ digest_mode_e digest_mode_d, digest_mode_q;
assign otp_enc_key_mux = (sel_d < NumScrmblKeys) ?
OtpKey[sel_d[vbits(NumScrmblKeys)-1:0]] : '0;
@@ -280,7 +280,7 @@
sel_d = sel_i;
end
DigestInit: begin
- digest_mode_d = otp_digest_mode_e'(sel_i);
+ digest_mode_d = digest_mode_e'(sel_i);
is_first_d = 1'b1;
end
DigestFinalize: begin