blob: 0dc3e28c9e775f6206e131d356440f6ef14b4b3c [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
{ name: "otp_ctrl",
clock_primary: "clk_i",
bus_device: "tlul",
bus_host: "none",
///////////////////////////
// Interrupts and Alerts //
///////////////////////////
interrupt_list: [
{ name: "otp_operation_done",
desc: "A direct access command or digest calculation operation has completed."
}
{ name: "otp_error",
desc: "An error has occurred in the OTP controller. Check the ERR_CODE register to get more information."
}
],
alert_list: [
{ name: "otp_integrity_mismatch",
desc: "This alert triggers if hardware detects a parity bit or digest error in the buffered partitions.",
}
{ name: "otp_consistency_mismatch",
desc: "This alert triggers if the digest over the buffered registers does not match with the digest stored in OTP.",
}
],
////////////////
// Parameters //
////////////////
param_list: [
{ name: "OtpByteAddressWidth",
desc: "Width of the OTP Byte address.",
type: "int",
default: "11",
local: "true"
},
{ name: "NumCreatorSwCfgPartitionWords",
desc: "Number of 32bit words in the software config partitions.",
type: "int",
default: "192",
local: "true"
},
{ name: "NumOwnerSwCfgPartitionWords",
desc: "Number of 32bit words in the software config partitions.",
type: "int",
default: "192",
local: "true"
},
{ name: "NumHwCfgWords",
desc: "Number of 32bit words in the hardware config partition.",
type: "int",
default: "50",
local: "true"
},
{ name: "NumLcPartitionWords",
desc: "Number of 32bit words in the life cycle partition.",
type: "int",
default: "22",
local: "true"
},
{ name: "NumSecret0PartitionWords",
desc: "Number of 32bit words in the secret0 partition.",
type: "int",
default: "10",
local: "true"
},
{ name: "NumSecret1PartitionWords",
desc: "Number of 32bit words in the secret1 partition.",
type: "int",
default: "14",
local: "true"
},
{ name: "NumSecret2PartitionWords",
desc: "Number of 32bit words in the secret2 partition.",
type: "int",
default: "32",
local: "true"
},
{ name: "NumDebugWindowWords",
desc: "Number of 32bit words in the debug window.",
type: "int",
default: "500",
local: "true"
},
]
/////////////////////////////
// Intermodule Connections //
/////////////////////////////
// TODO: these need to be refined during implementation and integration
inter_signal_list: [
// CSRNG interface
{ struct: "otp_csrng"
type: "req_rsp"
name: "otp_csrng"
act: "req"
package: "otp_ctrl_pkg"
}
// Power manager init command
{ struct: "pwr_otp"
type: "req_rsp"
name: "pwr_otp_init"
act: "rsp"
package: "pwrmgr_pkg"
}
// Status output to power manager
{ struct: "otp_pwr_state"
type: "uni"
name: "otp_pwr_state"
act: "req"
package: "otp_ctrl_pkg"
}
// LC transition command
{ struct: "lc_otp_program"
type: "req_rsp"
name: "lc_otp_program"
act: "rsp"
package: "otp_ctrl_pkg"
}
// Broadcast to LC
{ struct: "otp_lc_data"
type: "uni"
name: "otp_lc_data"
act: "req"
package: "otp_ctrl_pkg"
}
// Broadcast from LC
{ struct: "lc_tx"
type: "uni"
name: "lc_provision_en"
act: "rcv"
package: "lifecycle_pkg" // TODO: move to LC package?
}
{ struct: "lc_tx"
type: "uni"
name: "lc_test_en"
act: "rcv"
package: "lifecycle_pkg" // TODO: move to LC package?
}
// Broadcast to Key Manager
{ struct: "keymgr_key"
type: "uni"
name: "otp_keymgr_key"
act: "req"
package: "otp_ctrl_pkg" // TODO: move this to keymgr package?
}
// Broadcast to Flash Controller
{ struct: "flash_key"
type: "uni"
name: "otp_flash_key"
act: "req"
package: "otp_ctrl_pkg"
}
// Key request from Main RAM Scrambler
{ struct: "ram_main_key"
type: "req_rsp"
name: "otp_ram_main_key"
act: "rsp"
package: "otp_ctrl_pkg"
}
// Key request from Retention RAM Scrambler
{ struct: "ram_ret_aon_key"
type: "req_rsp"
name: "otp_ram_ret_aon_key"
act: "rsp"
package: "otp_ctrl_pkg"
}
// Key request from OTBN RAM Scrambler
{ struct: "otbn_ram_key"
type: "req_rsp"
name: "otp_otbn_ram_key"
act: "rsp"
package: "otp_ctrl_pkg"
}
] // inter_signal_list
regwidth: "32",
registers: [
////////////////////////
// Ctrl / Status CSRs //
////////////////////////
// TODO: this may have to be refined during implementation.
{ name: "STATUS",
desc: "OTP status register.",
swaccess: "ro",
hwaccess: "hwo",
fields: [
{ bits: "2:0"
enum: [
{ value: "0",
name: "IDLE",
desc: '''
OTP is in IDLE state and ready to accept commands.
'''
},
{ value: "1",
name: "INIT",
desc: '''
OTP is initializing.
'''
},
{ value: "2",
name: "ERROR",
desc: '''
An error condition has occurred.
Please check the ERR_CODE register for specifics.
'''
},
{ value: "3",
name: "READ_PENDING",
desc: '''
A read operation is pending.
'''
},
{ value: "4",
name: "WRITE_PENDING",
desc: '''
A write operation is pending.
'''
},
]
}
]
}
// TODO: need to define error codes for errors during background operations (or add alerts for them)
{ name: "ERR_CODE",
desc: '''This register holds information on error conditions and should be
checked when !!STATUS indicates that an error has occurred, or when
an !!INTR_STATE.otp_error has been triggered.
'''
swaccess: "ro",
hwaccess: "hwo",
fields: [
{
bits: "2:0"
enum: [
{ value: "0",
name: "NONE",
desc: '''
No error condition has occurred.
'''
},
{ value: "1",
name: "MISSING_INIT",
desc: '''
Read or write operation attempted before initialization.
The OTP controller automatically recovers from this error when initializing.
'''
},
{ value: "2",
name: "INVALID_CMD",
desc: '''
Invalid command has been written to !!DIRECT_ACCESS_CMD or the digest calculation registers.
The OTP controller automatically recovers from this error when issuing a new command.
'''
},
{ value: "3",
name: "READ_CORR",
desc: '''
A correctable error has occured during a read operation.
The OTP controller automatically recovers from this error when issuing a new command.
'''
},
{ value: "4",
name: "READ_UNCORR",
desc: '''
An uncorrectable error has occurred during a read operation.
The OTP controller automatically recovers from this error when issuing a new command.
'''
},
{ value: "5",
name: "READ_ERR",
desc: '''
An error has occurred during a read operation.
The OTP controller may not be able to automatically recover from this error and has to be reset.
'''
},
{ value: "6",
name: "WRITE_ERR",
desc: '''
An error has occurred during a programming operation.
The OTP controller may not be able to automatically recover from this error and has to be reset.
'''
},
{ value: "7",
name: "ESCALATED",
desc: '''
OTP has been rendered unusable due to an escalation.
This is a terminal state.
'''
},
]
}
]
}
{ name: "DIRECT_ACCESS_REGWEN",
desc: '''
Register write enable for all direct access interface registers.
''',
swaccess: "rw",
hwaccess: "hrw",
fields: [
{
bits: "0",
desc: ''' When true, the direct access interface registers become writable.
Otherwise, all direct access interface registers are write protected.
Note that the DAI hardware will temporarily set this to 0 when a transaction is in progress.
'''
resval: 0,
},
]
},
{ name: "DIRECT_ACCESS_CMD",
desc: "Command register for direct accesses.",
swaccess: "r0w1c",
hwaccess: "hro",
hwqe: "true",
regwen: "DIRECT_ACCESS_REGWEN",
fields: [
{ bits: "0",
name: "READ",
desc: '''
Initiates a readout sequence that reads the location specified
by !!DIRECT_ACCESS_ADDRESS. The command places the data read into
!!DIRECT_ACCESS_RDATA_0 and !!DIRECT_ACCESS_RDATA_1 (for 64bit partitions).
'''
}
{ bits: "1",
name: "WRITE",
desc: '''
Initiates a programming sequence that writes the data in !!DIRECT_ACCESS_WDATA_0
and !!DIRECT_ACCESS_WDATA_1 (for 64bit partitions) to the location specified by
!!DIRECT_ACCESS_ADDRESS.
'''
}
{ bits: "2",
name: "DIGEST",
desc: '''
Initiates the digest calculation and locking sequence for the partition specified by
!!DIRECT_ACCESS_ADDRESS.
'''
}
]
}
{ name: "DIRECT_ACCESS_ADDRESS",
desc: "Address register for direct accesses.",
swaccess: "rw",
hwaccess: "hro",
regwen: "DIRECT_ACCESS_REGWEN",
fields: [
{ bits: "OtpByteAddressWidth-1:0",
desc: '''
This is the address for the OTP word to be read or written through
the direct access interface. Note that the address is aligned to the access size
internally, hence bits 1:0 are ignored for 32bit accesses, and bits 2:0 are ignored
for 64bit accesses.
For the digest calculation command, set this register to the appropriate partition index
(0x2: !!HW_CFG_DIGEST, 0x4: !!SECRET0_DIGEST, 0x5: !!SECRET1_DIGEST and 0x6: !!SECRET2_DIGEST).
'''
}
]
}
{ multireg: {
name: "DIRECT_ACCESS_WDATA",
desc: '''Write data for direct accesses.
Hardware automatically determines the access granule (32bit or 64bit) based on which
partition is being written to.
''',
count: "2", // 2 x 32bit = 64bit
swaccess: "rw",
hwaccess: "hro",
regwen: "DIRECT_ACCESS_REGWEN",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "DIRECT_ACCESS_RDATA",
desc: '''Read data for direct accesses.
Hardware automatically determines the access granule (32bit or 64bit) based on which
partition is read from.
''',
count: "2", // 2 x 32bit = 64bit
swaccess: "ro",
hwaccess: "hwo",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ name: "CHECK_PERIOD_REGEN",
desc: '''
Register write enable for !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB.
''',
swaccess: "rw1c",
hwaccess: "hro",
fields: [
{ bits: "0",
desc: '''
When true, !!INTEGRITY_CHECK_PERIOD_MSB and !!CONSISTENCY_CHECK_PERIOD_MSB registers cannot be written anymore.
'''
resval: 1,
},
]
},
{ name: "INTEGRITY_CHECK_PERIOD_MSB",
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.
'''
swaccess: "rw",
hwaccess: "hro",
regwen: "CHECK_PERIOD_REGEN",
fields: [
{ bits: "5: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.
'''
resval: "25"
}
]
}
{ name: "CONSISTENCY_CHECK_PERIOD_MSB",
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).
'''
swaccess: "rw",
hwaccess: "hro",
regwen: "CHECK_PERIOD_REGEN",
fields: [
{ bits: "5: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.
'''
resval: "34"
}
]
}
////////////////////////////////////
// Dynamic Locks of SW Parititons //
////////////////////////////////////
{ name: "CREATOR_SW_CFG_READ_LOCK",
desc: '''
Runtime read lock for the creator software partition.
''',
swaccess: "rw1c",
hwaccess: "hro",
fields: [
{ bits: "0",
desc: '''
When true, read access to the !!CREATOR_SW_CFG partition is locked.
'''
resval: 1,
},
]
},
{ name: "OWNER_SW_CFG_READ_LOCK",
desc: '''
Runtime read lock for the owner software partition.
''',
swaccess: "rw1c",
hwaccess: "hro",
fields: [
{ bits: "0",
desc: '''
When true, read access to the !!OWNER_SW_CFG partition is locked.
'''
resval: 1,
},
]
},
///////////////////////
// Integrity Digests //
///////////////////////
{ multireg: {
name: "CREATOR_SW_CFG_DIGEST",
desc: '''
Integrity digest for the creator software config partition.
The integrity digest is 0 by default. Software must write this
digest value via the direct access interface in order to lock the partition.
After a reset, write access to the !!OWNER_SW_CFG partition is locked and
the digest becomes visible in this CSR.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "OWNER_SW_CFG_DIGEST",
desc: '''
Integrity digest for the owner software config partition.
The integrity digest is 0 by default. Software must write this
digest value via the direct access interface in order to lock the partition.
After a reset, write access to the !!OWNER_SW_CFG partition is locked and
the digest becomes visible in this CSR.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "HW_CFG_DIGEST",
desc: '''
Hardware config partition integrity digest.
The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD.
After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "SECRET0_DIGEST",
desc: '''
Integrity digest for the SECRET0 partition.
The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD.
After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "SECRET1_DIGEST",
desc: '''
Integrity digest for the SECRET1 partition.
The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD.
After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
{ multireg: {
name: "SECRET2_DIGEST",
desc: '''
Integrity digest for the SECRET2 partition.
The integrity digest is 0 by default. The digest calculation can be triggered via the !!DIRECT_ACCESS_CMD.
After a reset, the digest then becomes visible in this CSR, and the corresponding partition becomes write-locked.
''',
count: "2",
swaccess: "ro",
hwaccess: "hwo",
hwext: "true",
cname: "WORD",
fields: [
{ bits: "31:0"
}
]
}
},
///////////////////////////////
// Hardware Config Partition //
///////////////////////////////
{ skipto: "0x100" }
// TODO: there is likely more collateral that needs to be stored in the HW_CFG partition.
// add them as they become apparent.
//////////////////////////
// Life Cycle Partition //
//////////////////////////
{ skipto: "0x200" }
{ multireg: {
name: "LC_STATE",
desc: '''
Life cycle state, comprised of 12 16bit words.
TODO: add link to LC controller spec.
Other values than specified below are invalid.
''',
count: "12",
swaccess: "ro",
hwaccess: "hwo",
cname: "HALFWORD",
fields: [
{ bits: "15:0"
enum: [
{ value: "0",
name: "BLANK",
desc: '''
0x0000
'''
},
{ value: "62970",
name: "SET",
desc: '''
0xF5FA
'''
},
]
}
]
}
},
{ name: "TRANSITION_CNT",
desc: "Counter for total amount of state transition attempts.",
swaccess: "ro",
hwaccess: "hwo",
fields: [
{ bits: "31:0",
desc: '''
This counter will be incremented upon each state transition attempt,
or when the transition command coming from the life cycle controller is invalid.
'''
}
]
}
///////////////////////////////
// Software Config Partition //
///////////////////////////////
{ skipto: "0x300" }
{ window: {
name: "CREATOR_SW_CFG"
items: "NumCreatorSwCfgPartitionWords"
swaccess: "ro",
desc: '''
Any read to this window directly maps to the corresponding offset in the creator software
config partition, and triggers an OTP readout of the Bytes requested. Note that the transaction
will block until OTP readout has completed.
'''
}
}
{ skipto: "0x700" }
{ window: {
name: "OWNER_SW_CFG"
items: "NumOwnerSwCfgPartitionWords"
swaccess: "ro",
desc: '''
Any read to this window directly maps to the corresponding offset in the owner software
config partition, and triggers an OTP readout of the Bytes requested. Note that the transaction
will block until OTP readout has completed.
'''
}
}
//////////////////////
// Test Access Port //
//////////////////////
{ skipto: "0xB00" }
// TODO: may have to update description, once it is known how RAW unlock is handled.
{ window: {
name: "TEST_ACCESS"
items: "NumDebugWindowWords"
swaccess: "rw",
desc: '''
This maps to the register file of the proprietary OTP macro. Note that this is only
accessible during the TEST life cycle state.
'''
}
}
],
}