[otp_ctrl] Implement consistency/integrity check timer
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/otp_ctrl/data/otp_ctrl.hjson b/hw/ip/otp_ctrl/data/otp_ctrl.hjson
index cf76384..93777d0 100644
--- a/hw/ip/otp_ctrl/data/otp_ctrl.hjson
+++ b/hw/ip/otp_ctrl/data/otp_ctrl.hjson
@@ -194,70 +194,78 @@
{ bits: "0"
name: "CREATOR_SW_CFG_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "1"
name: "OWNER_SW_CFG_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "2"
name: "HW_CFG_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "3"
- name: "SECRET0"
+ name: "SECRET0_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "4"
- name: "SECRET1"
+ name: "SECRET1_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "5"
- name: "SECRET2"
+ name: "SECRET2_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "6"
- name: "LIFE_CYCLE"
+ name: "LIFE_CYCLE_ERROR"
desc: '''
- Set to 1 if an error occurred in this partition. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in this partition.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "7"
name: "DAI_ERROR"
desc: '''
- Set to 1 if an error occurred in the DAI. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in the DAI.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "8"
name: "LCI_ERROR"
desc: '''
- Set to 1 if an error occurred in the LCI. If set to 1, SW should
- check the !!ERR_CODE register at the corresponding index to root-cause the error.
+ Set to 1 if an error occurred in the LCI.
+ If set to 1, SW should check the !!ERR_CODE register at the corresponding index.
'''
}
{ bits: "9"
+ name: "TIMEOUT_ERROR"
+ desc: "Set to 1 if an integrity or consistency check times out. This raises an otp_check_failed alert and is an unrecoverable error condition."
+ }
+ { bits: "10"
name: "DAI_IDLE"
desc: "Set to 1 if the DAI is idle and ready to accept commands."
}
+ { bits: "11"
+ name: "CHECK_PENDING"
+ desc: "Set to 1 if an integrity or consistency check triggered by the LFSR timer or via !!CHECK_TRIGGER is pending."
+ }
]
}
{ multireg: {
@@ -513,58 +521,130 @@
]
}
},
- { name: "CHECK_PERIOD_REGEN",
+
+ //////////////////////////////////////
+ // Integrity and Consistency Checks //
+ //////////////////////////////////////
+ { name: "CHECK_TRIGGER_REGWEN",
desc: '''
- Register write enable for !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB.
+ Register write enable for !!CHECK_TRIGGER.
''',
swaccess: "rw1c",
hwaccess: "hro",
fields: [
{ bits: "0",
desc: '''
- When true, !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB registers cannot be written anymore.
+ When cleared to 0, the !!CHECK_TRIGGER register cannot be written anymore.
+ Write 1 to clear this bit.
'''
resval: 1,
},
]
},
- { name: "INTEGRITY_CHECK_PERIOD_MSB",
+ { name: "CHECK_TRIGGER",
+ desc: "Command register for direct accesses.",
+ swaccess: "r0w1c",
+ hwaccess: "hro",
+ hwqe: "true",
+ regwen: "CHECK_TRIGGER_REGWEN",
+ fields: [
+ { bits: "0",
+ name: "INTEGRITY",
+ desc: '''
+ Writing 1 to this bit triggers an integrity check. SW should monitor !!STATUS.CHECK_PENDING
+ and wait until the check has been completed. If there are any errors, those will be flagged
+ in the !!STATUS and !!ERR_CODE registers, and via the interrupts and alerts.
+ '''
+ }
+ { bits: "1",
+ name: "CONSISTENCY",
+ desc: '''
+ Writing 1 to this bit triggers a consistency check. SW should monitor !!STATUS.CHECK_PENDING
+ and wait until the check has been completed. If there are any errors, those will be flagged
+ in the !!STATUS and !!ERR_CODE registers, and via interrupts and alerts.
+ '''
+ }
+ ]
+ },
+ { name: "CHECK_REGWEN",
+ desc: '''
+ Register write enable for !!INTEGRITY_CHECK_PERIOD and !!CONSISTENCY_CHECK_PERIOD.
+ ''',
+ swaccess: "rw1c",
+ hwaccess: "hro",
+ fields: [
+ { bits: "0",
+ desc: '''
+ When cleared to 0, !!INTEGRITY_CHECK_PERIOD and !!CONSISTENCY_CHECK_PERIOD registers cannot be written anymore.
+ Write 1 to clear this bit.
+ '''
+ resval: 1,
+ },
+ ]
+ },
+ { name: "CHECK_TIMEOUT",
+ desc: '''
+ Timeout value for the integrity and consistency checks.
+ ''',
+ swaccess: "rw",
+ hwaccess: "hro",
+ regwen: "CHECK_REGWEN",
+ fields: [
+ { bits: "31:0",
+ desc: '''
+ Timeout value in cycles for the for the integrity and consistency checks. If an integrity or consistency
+ check does not complete within the timeout window, an error will be flagged in the !!STATUS register,
+ an otp_error interrupt will be raised, and an otp_check_failed alert will be sent out. The timeout should
+ be set to a large value to stay on the safe side. The maximum check time can be upper bounded by the
+ number of cycles it takes to readout, scramble and digest the entire OTP array. Since this amounts to
+ roughly 25k cycles, it is recommended to set this value to at least 100'000 cycles in order to stay on the
+ safe side. A value of zero disables the timeout mechanism (default).
+ '''
+ resval: 0,
+ },
+ ]
+ },
+ { name: "INTEGRITY_CHECK_PERIOD",
desc: '''
This value specifies the maximum period that can be generated pseudo-randomly.
- Only applies to the HW_CFG and SECRET partitions if they are locked.
+ Only applies to the HW_CFG and SECRET* partitions, once they are locked.
'''
swaccess: "rw",
hwaccess: "hro",
- regwen: "CHECK_PERIOD_REGEN",
+ regwen: "CHECK_REGWEN",
fields: [
- { bits: "5:0",
+ { bits: "31:0",
desc: '''
- The pseudo-random period is generated using a 40bit LFSR internally, and this value defines
- the bit mask to be applied to the LFSR output in order to limit its range. A value of N will generate
- an internal mask of 2^N-1. So for N=16 this would allow the maximum pseudo-random period to be 0xFFFF cycles.
- The default value has been set to 25, which corresponds to a maximum period of a bit more than 1.3s at 25MHz.
+ The pseudo-random period is generated using a 40bit LFSR internally, and this register defines
+ the bit mask to be applied to the LFSR output in order to limit its range. The value of this
+ register is left shifted by 8bits and the lower bits are set to 8'hFF in order to form the 40bit mask.
+ A recommended value is 0x3_FFFF, corresponding to a maximum period of ~2.8s at 24MHz.
+ A value of zero disables the timer (default). Note that a one-off check can always be triggered via
+ !!CHECK_TRIGGER.INTEGRITY.
'''
- resval: "25"
+ resval: "0"
}
]
}
- { name: "CONSISTENCY_CHECK_PERIOD_MSB",
+ { name: "CONSISTENCY_CHECK_PERIOD",
desc: '''
This value specifies the maximum period that can be generated pseudo-randomly.
- This applies to the LIFE_CYCLE partition and the HW_CFG and SECRET partitions (but only if they are locked).
+ This applies to the LIFE_CYCLE partition and the HW_CFG and SECRET* partitions, once they are locked.
'''
swaccess: "rw",
hwaccess: "hro",
- regwen: "CHECK_PERIOD_REGEN",
+ regwen: "CHECK_REGWEN",
fields: [
- { bits: "5:0",
+ { bits: "31:0",
desc: '''
- The pseudo-random period is generated using a 40bit LFSR internally, and this value defines
- the bit mask to be applied to the LFSR output in order to limit its range. A value of N will generate
- an internal mask of 2^N-1. So for N=16 this would allow the maximum pseudo-random period to be 0xFFFF cycles.
- The default value has been set to 34, which corresponds to a maximum period of a bit more than 687s at 25MHz.
+ The pseudo-random period is generated using a 40bit LFSR internally, and this register defines
+ the bit mask to be applied to the LFSR output in order to limit its range. The value of this
+ register is left shifted by 8bits and the lower bits are set to 8'hFF in order to form the 40bit mask.
+ A recommended value is 0x3FF_FFFF, corresponding to a maximum period of ~716s at 24MHz.
+ A value of zero disables the timer (default). Note that a one-off check can always be triggered via
+ !!CHECK_TRIGGER.CONSISTENCY.
'''
- resval: "34"
+ resval: "0"
}
]
}
@@ -581,7 +661,8 @@
fields: [
{ bits: "0",
desc: '''
- When true, read access to the !!CREATOR_SW_CFG partition is locked.
+ When cleared to 0, read access to the !!CREATOR_SW_CFG partition is locked.
+ Write 1 to clear this bit.
'''
resval: 1,
},
@@ -596,7 +677,8 @@
fields: [
{ bits: "0",
desc: '''
- When true, read access to the !!OWNER_SW_CFG partition is locked.
+ When cleared to 0, read access to the !!OWNER_SW_CFG partition is locked.
+ Write 1 to clear this bit.
'''
resval: 1,
},
diff --git a/hw/ip/otp_ctrl/otp_ctrl.core b/hw/ip/otp_ctrl/otp_ctrl.core
index 8170acc..716ac7e 100644
--- a/hw/ip/otp_ctrl/otp_ctrl.core
+++ b/hw/ip/otp_ctrl/otp_ctrl.core
@@ -12,6 +12,7 @@
- lowrisc:prim:all
- lowrisc:prim:ram_1p
- lowrisc:prim:otp
+ - lowrisc:prim:lfsr
- lowrisc:ip:otp_ctrl_pkg
files:
- rtl/otp_ctrl_reg_top.sv
@@ -21,6 +22,7 @@
- rtl/otp_ctrl_lci.sv
- rtl/otp_ctrl_part_unbuf.sv
- rtl/otp_ctrl_part_buf.sv
+ - rtl/otp_ctrl_lfsr_timer.sv
- rtl/otp_ctrl.sv
file_type: systemVerilogSource
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index 889bb3a..10fe002 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -11,7 +11,9 @@
import otp_ctrl_pkg::*;
import otp_ctrl_reg_pkg::*;
#(
- parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}}
+ parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
+ // TODO: need to override this during build time randomization
+ parameter logic [TimerWidth-1:0] LfsrSeed = TimerWidth'(546532468)
) (
input clk_i,
input rst_ni,
@@ -25,6 +27,8 @@
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
+ // TODO: Complete entropy interface
+ input otp_entropy_t entropy_i,
// Power manager interface
input pwr_otp_init_req_t pwr_otp_init_req_i,
output pwr_otp_init_rsp_t pwr_otp_init_rsp_o,
@@ -153,6 +157,7 @@
logic [NumPart+1:0] part_errors_reduced;
logic otp_operation_done, otp_error;
logic otp_fatal_error, otp_check_failed;
+ logic chk_pending, chk_timeout;
always_comb begin : p_errors_alerts
hw2reg.err_code = part_error;
otp_fatal_error = 1'b0;
@@ -174,14 +179,17 @@
otp_check_failed |= part_error[k] inside {ParityErr,
IntegErr,
CnstyErr,
- FsmErr};
+ FsmErr} | chk_timeout;
end
end
// Assign these to the status register.
- assign hw2reg.status = {part_errors_reduced, dai_idle};
+ assign hw2reg.status = {chk_pending,
+ dai_idle,
+ chk_timeout,
+ part_errors_reduced};
// If we got an error, we trigger an interrupt.
- assign otp_error = |part_errors_reduced;
+ assign otp_error = |part_errors_reduced | chk_timeout;
//////////////////////////////////
// Interrupts and Alert Senders //
@@ -237,18 +245,43 @@
.alert_tx_o ( alert_tx_o[1] )
);
- ////////////////
- // LFSR Timer //
- ////////////////
+ ////////////////////////////////
+ // LFSR Timer and CSR mapping //
+ ////////////////////////////////
- // TBD: should we incorporate a timeout in the LFSR counter to cover the case where a
- // partition check never completes due to wedged arbitration (or some other condition
- // induced due to a tampering attempt)? This will likely be constructed in a similar
- // way as the ping timer inside the alert handler.
-
+ logic integ_chk_trig, cnsty_chk_trig;
logic [NumPart-1:0] integ_chk_req, integ_chk_ack;
logic [NumPart-1:0] cnsty_chk_req, cnsty_chk_ack;
+ assign integ_chk_trig = reg2hw.check_trigger.integrity.q &
+ reg2hw.check_trigger.integrity.qe;
+ assign cnsty_chk_trig = reg2hw.check_trigger.consistency.q &
+ reg2hw.check_trigger.consistency.qe;
+
+ otp_ctrl_lfsr_timer #(
+ .LfsrSeed(LfsrSeed),
+ .EntropyWidth(4)
+ ) u_otp_ctrl_lfsr_timer (
+ .clk_i,
+ .rst_ni,
+ .entropy_en_i ( entropy_i.en ),
+ // Lower entropy bits are used for reseeding secure erase LFSRs
+ .entropy_i ( entropy_i.data[31:28] ),
+ // We can enable the timer once OTP has initialized.
+ .timer_en_i ( pwr_otp_init_rsp_o.done ),
+ .integ_chk_trig_i ( integ_chk_trig ),
+ .cnsty_chk_trig_i ( cnsty_chk_trig ),
+ .chk_pending_o ( chk_pending ),
+ .timeout_i ( reg2hw.check_timeout.q ),
+ .integ_period_msk_i ( reg2hw.integrity_check_period.q ),
+ .cnsty_period_msk_i ( reg2hw.consistency_check_period.q ),
+ .integ_chk_req_o ( integ_chk_req ),
+ .cnsty_chk_req_o ( cnsty_chk_req ),
+ .integ_chk_ack_i ( integ_chk_ack ),
+ .cnsty_chk_ack_i ( cnsty_chk_ack ),
+ .chk_timeout_o ( chk_timeout )
+ );
+
///////////////////////////////
// OTP Macro and Arbitration //
///////////////////////////////
@@ -545,9 +578,13 @@
end else if (PartInfo[k].variant == Buffered) begin : gen_buffered
otp_ctrl_part_buf #(
.Info(PartInfo[k])
+ // TODO: .EntropyWidth(8)
) u_part_buf (
.clk_i,
.rst_ni,
+ // TODO: Entropy for clearing LFSRs
+ // .entropy_en_i ( entropy_i.en ),
+ // .entropy_i ( entropy_i.data[(k-NumUnbuffered) * 2 +: 2] ),
.init_req_i ( part_init_req ),
.init_done_o ( part_init_done[k] ),
.integ_chk_req_i ( integ_chk_req[k] ),
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
new file mode 100644
index 0000000..23647e8
--- /dev/null
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_lfsr_timer.sv
@@ -0,0 +1,278 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// This module implements the LFSR timer for triggering periodic consistency and integrity checks in
+// OTP. In particular, this module contains two 40bit counters (one for the consistency and one
+// for the integrity checks) and a 40bit LFSR to draw pseudo random wait counts.
+//
+// The integ_period_msk_i and cnsty_period_msk_i mask signals are used to mask off the LFSR outputs
+// and hence determine the maximum wait count that can be drawn. If these values are set to
+// zero, the corresponding timer is disabled.
+//
+// Once a particular check timer has expired, the module will send out a check request to all
+// partitions and wait for an acknowledgment. If a particular partition encounters an integrity or
+// consistency mismatch, this will be directly reported via the error and alert logic.
+//
+// In order to guard against wedged partition controllers or arbitration lock ups due to tampering
+// attempts, this check timer module also supports a 32bit timeout that can optionally be
+// programmed. If a particular check times out, chk_timeout_o will be asserted, which will raise
+// an alert via the error logic.
+//
+// If needed, the LFSR can be reseeded with fresh entropy from the CSRNG via entropy_i.
+//
+// It is also possible to trigger one-off checks via integ_chk_trig_i and cnsty_chk_trig_i.
+// This can be useful if SW chooses to leave the periodic checks disabled.
+//
+
+`include "prim_assert.sv"
+
+module otp_ctrl_lfsr_timer import otp_ctrl_pkg::*; #(
+ parameter logic [TimerWidth-1:0] LfsrSeed = TimerWidth'(1'b1),
+ parameter int EntropyWidth = 8
+) (
+ input clk_i,
+ input rst_ni,
+ input entropy_en_i, // entropy update pulse from CSRNG
+ input [EntropyWidth-1:0] entropy_i, // from CSRNG
+ 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
+ output logic chk_pending_o, // indicates whether there are pending checks
+ input [31:0] timeout_i, // check timeout
+ input [31:0] integ_period_msk_i, // maximum integrity check mask
+ input [31:0] cnsty_period_msk_i, // maximum consistency check mask
+ output logic [NumPart-1:0] integ_chk_req_o, // request to all partitions
+ output logic [NumPart-1:0] cnsty_chk_req_o, // request to all partitions
+ input [NumPart-1:0] integ_chk_ack_i, // response from partitions
+ input [NumPart-1:0] cnsty_chk_ack_i, // response from partitions
+ output logic chk_timeout_o // a check has timed out
+);
+
+ //////////
+ // PRNG //
+ //////////
+
+ logic lfsr_en;
+ logic [TimerWidth-1:0] lfsr_state, perm_state;
+
+ prim_lfsr #(
+ .LfsrDw ( TimerWidth ),
+ .EntropyDw ( EntropyWidth ),
+ .StateOutDw ( TimerWidth ),
+ .DefaultSeed ( LfsrSeed ),
+ .ExtSeedSVA ( 1'b0 ) // ext seed is unused
+ ) i_prim_lfsr (
+ .clk_i,
+ .rst_ni,
+ .seed_en_i ( 1'b0 ),
+ .seed_i ( '0 ),
+ .lfsr_en_i ( lfsr_en | entropy_en_i ),
+ .entropy_i ( entropy_i & {EntropyWidth{entropy_en_i}} ),
+ .state_o ( lfsr_state )
+ );
+
+ // This random permutation is meant to break the linear
+ // shifting pattern of the LFSR.
+ localparam int unsigned Perm [TimerWidth] = '{
+ 13, 17, 29, 11, 28, 12, 33, 27,
+ 5, 39, 31, 21, 15, 1, 24, 37,
+ 32, 38, 26, 34, 8, 10, 4, 2,
+ 19, 0, 20, 6, 25, 22, 3, 35,
+ 16, 14, 23, 7, 30, 9, 18, 36
+ };
+
+ for (genvar k = 0; k < 32; k++) begin : gen_perm
+ assign perm_state[k] = lfsr_state[Perm[k]];
+ end
+
+
+ //////////////
+ // Counters //
+ //////////////
+
+ logic [TimerWidth-1:0] integ_cnt_d, integ_cnt_q;
+ logic [TimerWidth-1:0] cnsty_cnt_d, cnsty_cnt_q;
+ logic [TimerWidth-1:0] integ_mask, cnsty_mask;
+ logic integ_load_period, integ_load_timeout, integ_cnt_zero;
+ logic cnsty_load_period, cnsty_load_timeout, cnsty_cnt_zero;
+ logic timeout_zero, integ_msk_zero, cnsty_msk_zero;
+
+ assign integ_mask = {integ_period_msk_i, {TimerWidth-32{1'b1}}};
+ assign cnsty_mask = {cnsty_period_msk_i, {TimerWidth-32{1'b1}}};
+
+ assign integ_cnt_d = (integ_load_period) ? lfsr_state & integ_mask :
+ (integ_load_timeout) ? timeout_i :
+ (integ_cnt_zero) ? '0 :
+ integ_cnt_q - 1'b1;
+
+
+ assign cnsty_cnt_d = (cnsty_load_period) ? lfsr_state & cnsty_mask :
+ (cnsty_load_timeout) ? timeout_i :
+ (cnsty_cnt_zero) ? '0 :
+ cnsty_cnt_q - 1'b1;
+
+ assign timeout_zero = (timeout_i == '0);
+ assign integ_msk_zero = (integ_period_msk_i == '0);
+ assign cnsty_msk_zero = (cnsty_period_msk_i == '0);
+ assign integ_cnt_zero = (integ_cnt_q == '0);
+ assign cnsty_cnt_zero = (cnsty_cnt_q == '0);
+
+ /////////////////////
+ // Request signals //
+ /////////////////////
+
+ logic set_all_integ_reqs, set_all_cnsty_reqs;
+ logic [NumPart-1:0] integ_chk_req_d, integ_chk_req_q;
+ logic [NumPart-1:0] cnsty_chk_req_d, cnsty_chk_req_q;
+ assign integ_chk_req_o = integ_chk_req_q;
+ assign cnsty_chk_req_o = cnsty_chk_req_q;
+ assign integ_chk_req_d = (set_all_integ_reqs) ? {NumPart{1'b1}} :
+ integ_chk_req_q & ~integ_chk_ack_i;
+ assign cnsty_chk_req_d = (set_all_cnsty_reqs) ? {NumPart{1'b1}} :
+ cnsty_chk_req_q & ~cnsty_chk_ack_i;
+
+
+ ////////////////////////////
+ // Ping and Timeout Logic //
+ ////////////////////////////
+
+ // Encoding generated with ./sparse-fsm-encode -d 5 -m 5 -n 9
+ // Hamming distance histogram:
+ //
+ // 0: --
+ // 1: --
+ // 2: --
+ // 3: --
+ // 4: --
+ // 5: |||||||||||||||||||| (60.00%)
+ // 6: ||||||||||||| (40.00%)
+ // 7: --
+ // 8: --
+ // 9: --
+ //
+ // Minimum Hamming distance: 5
+ // Maximum Hamming distance: 6
+ //
+ typedef enum logic [8:0] {
+ ResetSt = 9'b110010010,
+ IdleSt = 9'b011011101,
+ IntegWaitSt = 9'b100111111,
+ CnstyWaitSt = 9'b001000110,
+ ErrSt = 9'b101101000
+ } state_e;
+ state_e state_d, state_q;
+
+ always_comb begin : p_fsm
+ state_d = state_q;
+
+ // LFSR and counter signals
+ lfsr_en = 1'b0;
+ integ_load_period = 1'b0;
+ cnsty_load_period = 1'b0;
+ integ_load_timeout = 1'b0;
+ cnsty_load_timeout = 1'b0;
+
+ // Requests going to partitions.
+ set_all_integ_reqs = '0;
+ set_all_cnsty_reqs = '0;
+
+ // Status signals going to CSRs and error logic.
+ chk_timeout_o = 1'b0;
+ chk_pending_o = 1'b0;
+
+ unique case (state_q)
+ ///////////////////////////////////////////////////////////////////
+ // Wait until enabled. We never return to this state
+ // once enabled!
+ ResetSt: begin
+ if (timer_en_i) begin
+ state_d = IdleSt;
+ lfsr_en = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Wait here until one of the two timers expires (if enabled) or if
+ // a check is triggered externally.
+ IdleSt: begin
+ if ((!integ_msk_zero && integ_cnt_zero) || integ_chk_trig_i) begin
+ state_d = IntegWaitSt;
+ integ_load_timeout = 1'b1;
+ set_all_integ_reqs = 1'b1;
+ end else if ((!cnsty_msk_zero && cnsty_cnt_zero) || cnsty_chk_trig_i) begin
+ state_d = CnstyWaitSt;
+ cnsty_load_timeout = 1'b1;
+ set_all_cnsty_reqs = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Wait for all the partitions to respond and go back to idle.
+ // If the timeout is enabled, bail out into terminal error state
+ // if the timeout counter expires (this will raise an alert).
+ IntegWaitSt: begin
+ chk_pending_o = 1'b1;
+ if (!timeout_zero && integ_cnt_zero) begin
+ state_d = ErrSt;
+ end else if (integ_chk_req_q == '0) begin
+ state_d = IdleSt;
+ // This draws the next wait period.
+ integ_load_period = 1'b1;
+ lfsr_en = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Wait for all the partitions to respond and go back to idle.
+ // If the timeout is enabled, bail out into terminal error state
+ // if the timeout counter expires (this will raise an alert).
+ CnstyWaitSt: begin
+ chk_pending_o = 1'b1;
+ if (!timeout_zero && cnsty_cnt_zero) begin
+ state_d = ErrSt;
+ end else if (cnsty_chk_req_q == '0) begin
+ state_d = IdleSt;
+ // This draws the next wait period.
+ cnsty_load_period = 1'b1;
+ lfsr_en = 1'b1;
+ end
+ end
+ ///////////////////////////////////////////////////////////////////
+ // Terminal error state. This raises an alert.
+ ErrSt: begin
+ chk_timeout_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 = ErrSt;
+ end
+ ///////////////////////////////////////////////////////////////////
+ endcase
+ end
+
+ ///////////////
+ // Registers //
+ ///////////////
+
+ always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
+ if (!rst_ni) begin
+ state_q <= ResetSt;
+ integ_cnt_q <= '0;
+ cnsty_cnt_q <= '0;
+ integ_chk_req_q <= '0;
+ cnsty_chk_req_q <= '0;
+ end else begin
+ state_q <= state_d;
+ integ_cnt_q <= integ_cnt_d;
+ cnsty_cnt_q <= cnsty_cnt_d;
+ integ_chk_req_q <= integ_chk_req_d;
+ cnsty_chk_req_q <= cnsty_chk_req_d;
+ end
+ end
+
+ ////////////////
+ // Assertions //
+ ////////////////
+
+
+endmodule : otp_ctrl_lfsr_timer
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
index 8e5c685..1805c8b 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_pkg.sv
@@ -14,6 +14,8 @@
parameter int NumPart = 7;
parameter int NumPartWidth = vbits(NumPart);
+ // This defines the width of the check timers and LFSR
+ parameter int TimerWidth = 40;
// TODO: may need to tune this and make sure that this encoding not optimized away.
// Redundantly encoded and complementary values are used to for signalling to the partition
@@ -186,6 +188,17 @@
parameter int DaiIdx = 7;
parameter int LciIdx = 8;
+ parameter int NumUnbuffered = 2;
+
+ ////////////////////////
+ // Typedefs for CSRNG //
+ ////////////////////////
+
+ typedef struct packed {
+ logic en;
+ logic [31:0] data;
+ } otp_entropy_t;
+
///////////////////////////////
// Typedefs for LC Interface //
///////////////////////////////