[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