Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f3cd033
[fuse_ctrl, doc] Add register doc generation to partition script
andrea-caforio May 21, 2025
b5b3444
MICROSOFT AUTOMATED PIPELINE: Stamp 'lowrisc_fix_fuse_ctrl_doc' with …
andrea-caforio May 21, 2025
33b7f75
merged with main
ekarabu May 23, 2025
126bc8b
merged with main
ekarabu May 23, 2025
d9a7fe4
updated fuse map
ekarabu May 23, 2025
3b15112
removed an unused assertion
ekarabu May 23, 2025
1251491
Resolved Issue #439
ekarabu May 23, 2025
e9c5642
clarified FIPS zeroization flow
ekarabu May 23, 2025
7b9fce7
added details on optional fuses
ekarabu May 23, 2025
9a770e1
Resolved Issue #441
ekarabu May 23, 2025
fda7eff
Resolved issue #441 2
ekarabu May 23, 2025
3ec2497
fixed naming
ekarabu May 23, 2025
bc96efd
updated partition number in the doc
ekarabu May 23, 2025
29437f2
Merge branch 'main' into user/ekarabulut/otp_optional_items
ekarabu May 23, 2025
98ebd38
added owner PK hash
ekarabu May 23, 2025
d4e2131
[fuse_ctrl, test] Remove hardcoded addresses from tb services
andrea-caforio May 25, 2025
38f6bdc
[fuse_ctrl, rtl] Make pk hash vendor lock more generic
andrea-caforio May 27, 2025
e9d9963
MICROSOFT AUTOMATED PIPELINE: Stamp 'user/ekarabulut/otp_optional_ite…
andrea-caforio May 27, 2025
4bfb799
Resolved Issue #487
ekarabu May 27, 2025
0c4a59c
MICROSOFT AUTOMATED PIPELINE: Stamp 'user/ekarabulut/otp_optional_ite…
ekarabu May 27, 2025
f2cfeb4
Resolved #463
ekarabu May 28, 2025
9b72d28
Merge branch 'user/ekarabulut/otp_optional_items' of ssh://github.com…
ekarabu May 28, 2025
cce9b92
MICROSOFT AUTOMATED PIPELINE: Stamp 'user/ekarabulut/otp_optional_ite…
ekarabu May 29, 2025
a46ebbd
merged main
ekarabu May 30, 2025
c3ce1c0
extended scan path excl list
ekarabu May 30, 2025
a99d1c6
MICROSOFT AUTOMATED PIPELINE: Stamp 'user/ekarabulut/otp_optional_ite…
ekarabu May 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflow_metadata/pr_hash
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b770f828de1c7963d4f1c5935323aaaa8b1def22051c41a71fdc87e5d6e537425da7cc935d4774c11e9e55f00b320221
2e7f4bc71984c10fe00b112c427fab667fcba0a0d73cf16e4b5542d44e28e19d5882a7d8edda2d8589419c5c84ff97db
2 changes: 1 addition & 1 deletion .github/workflow_metadata/pr_timestamp
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1748029747
1748380825
12 changes: 5 additions & 7 deletions docs/CaliptraSSHardwareSpecification.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ FUSE controller is an RTL module that is responsible for programming and reading

## Partition Details

The Fuse Controller supports a total of **13 partitions** (See [Fuse Controller's Fuse Partition Map](../src/fuse_ctrl/doc/otp_ctrl_mmap.md)). Secret FUSE partitions are prefixed with the word "Secret" and are associated with specific Life Cycle (LC) states, such as "MANUF" or "PROD." This naming convention indicates the LC state required to provision each partition.
The Fuse Controller supports a total of **16 partitions** (See [Fuse Controller's Fuse Partition Map](../src/fuse_ctrl/doc/otp_ctrl_mmap.md)). Secret FUSE partitions are prefixed with the word "Secret" and are associated with specific Life Cycle (LC) states, such as "MANUF" or "PROD." This naming convention indicates the LC state required to provision each partition.

### Key Characteristics of Secret Partitions:
1. **Programming Access:**
Expand Down Expand Up @@ -492,19 +492,17 @@ Zeroization occurs under the following conditions:

2. **Transient Condition (Before Cold Reset):**
- The **`cptra_ss_FIPS_ZEROIZATION_PPD_i`** GPIO pin must be **asserted high**.
- The **`ss_soc_MCU_ROM_zeroization_mask_reg`** must also be set.
- MCU ROM support is needed.

### Zeroization Process

1. A new input port, `cptra_ss_FIPS_ZEROIZATION_PPD_i`, is introduced in the Caliptra Subsystem.
1. A new input port, `cptra_ss_FIPS_ZEROIZATION_PPD_i`, is introduced in the Caliptra Subsystem. SoC integrator needs to connect this signal to MCI generic input wires (see [MCI Generic Input Allocation](./CaliptraSSIntegrationSpecification.md#mci-integration-requirements)).
2. When this signal is asserted, it triggers preemptive zeroization of secret FUSEs before the SCRAP state transition.
3. The **MCU ROM** samples `cptra_ss_FIPS_ZEROIZATION_PPD_i` by reading the corresponding register storing its value.
4. If `cptra_ss_FIPS_ZEROIZATION_PPD_i == HIGH`, the MCU ROM executes the following sequence:
1. Writes `32'hFFFF_FFFF` to the `ss_soc_MCU_ROM_zeroization_mask_reg` register of **MCI**.
2. Creates a **Life Cycle Controller (LCC) transition request** to switch to the **SCRAP** state.
4. If `cptra_ss_FIPS_ZEROIZATION_PPD_i == HIGH`, the MCU ROM executes a **Life Cycle Controller (LCC) transition request** to switch to the **SCRAP** state.

- **Note:** The LCC state transition to SCRAP is completed **only after a cold reset**.
- **Note:** The `ss_soc_MCU_ROM_zeroization_mask_reg` register can be set only by MCU ROM that prohibits run-time firmware to update this register.


### Cold Reset and Final Zeroization

Expand Down
60 changes: 35 additions & 25 deletions docs/CaliptraSSIntegrationSpecification.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
- [Overview](#overview-3)
- [Parameters \& Defines](#parameters--defines-2)
- [Interface](#interface)
- [Memory Map / Address map](#memory-map-address-map)
- [Fuse Macro Memory Map and Fuse Controller CSR Address Map](#fuse-macro-memory-map-and-fuse-controller-csr-address-map)
- [FC Integration Requirements](#fc-integration-requirements)
- [Direct Access Interface](#direct-access-interface)
- [Initialization](#initialization)
Expand Down Expand Up @@ -827,10 +827,20 @@ For an in-depth understanding of the Fuse Controller's functionality, including
| Internal | Output | Struct | `otp_broadcast_o` | | FUSE broadcast output to Caliptra Core. This port broadcasts UDS-seed and Field-entropy-seed. |


## Memory Map / Address map
## Fuse Macro Memory Map and Fuse Controller CSR Address Map

The Caliptra Subsystem fuse controller supports a flexible and extensible memory map for storing one-time programmable (OTP) data. This structure is documented in the following files:
See [Fuse Controller Register Map](../src/fuse_ctrl/doc/otp_ctrl_registers.md) for registers.
See [Fuse Macor Memory Map](../src/fuse_ctrl/doc/otp_ctrl_mmap.md) for fuse partition map.

The current fuse memory map consists of **three main architectural segments**: **Caliptra-Core** (prefix: `CALIPTRA_CORE`), **Caliptra-Subsystem** (prefix: `CALIPTRA_SS`), **SoC/Vendor-Specific**.

This structure enables separation of responsibilities and flexibility in SoC integration. While the **Caliptra-Core fuse items** are mandatory and must adhere to the [Caliptra Fuse Map Specification](https://github.com/chipsalliance/Caliptra/blob/main/doc/Caliptra.md#fuse-map), **Caliptra-Subsystem** fuses are required only when the subsystem is instantiated with Caliptra Subsystem. These Caliptra Subsystem fuses can also be configured based on SoC requirements. The **SoC/Vendor-specific** items can be customized based on integrator needs and product requirements. Therefore, the fields under SoC-specific categories can be resized or eliminated if unused.


### **SOC_SPECIFIC_IDEVID_CERTIFICATE Usage**
This field defaults to 4 bytes but can be extended to accommodate storage of a full IDevID hybrid certificate (e.g., ML-DSA + ECC) if desired. Integrators must adjust its size in the YAML config used by the generation script.

See [Fuse Controller Register Map](../src/fuse_ctrl/doc/registers.md).

---

Expand Down Expand Up @@ -869,30 +879,28 @@ Fuse macros has to be programmed via the Direct Access Interface, which is compr

CSR Name | Description
-------------------------------------|------------------------------------
[`DIRECT_ACCESS_WDATA_0`](../src/fuse_ctrl/doc/registers.md#direct_access_wdata) | Low 32bit word to be written.
[`DIRECT_ACCESS_WDATA_1`](../src/fuse_ctrl/doc/registers.md#direct_access_wdata) | High 32bit word to be written.
[`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/registers.md#direct_access_rdata) | Low 32bit word that has been read.
[`DIRECT_ACCESS_RDATA_1`](../src/fuse_ctrl/doc/registers.md#direct_access_rdata) | High 32bit word that has been read.
[`DIRECT_ACCESS_ADDRESS`](../src/fuse_ctrl/doc/registers.md#direct_access_address) | byte address for the access.
[`DIRECT_ACCESS_CMD`](../src/fuse_ctrl/doc/registers.md#direct_access_cmd) | Command register to trigger a read or a write access.
[`DIRECT_ACCESS_REGWEN`](../src/fuse_ctrl/doc/registers.md#direct_access_regwen) | Write protection register for DAI.
[`DIRECT_ACCESS_WDATA_0`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_wdata) | Low 32bit word to be written.
[`DIRECT_ACCESS_WDATA_1`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_wdata) | High 32bit word to be written.
[`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_rdata) | Low 32bit word that has been read.
[`DIRECT_ACCESS_RDATA_1`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_rdata) | High 32bit word that has been read.
[`DIRECT_ACCESS_ADDRESS`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_address) | byte address for the access.
[`DIRECT_ACCESS_CMD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_cmd) | Command register to trigger a read or a write access.
[`DIRECT_ACCESS_REGWEN`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_regwen) | Write protection register for DAI.

## Initialization

The OTP controller initializes automatically upon power-up and is fully operational by the time the processor boots.
The only initialization steps that SW should perform are:

1. Check that the OTP controller has successfully initialized by reading [`STATUS`](../src/fuse_ctrl/doc/registers.md#status). I.e., make sure that none of the ERROR bits are set, and that the DAI is idle ([`STATUS.DAI_IDLE`](../src/fuse_ctrl/doc/registers.md#status)).
- Choose whether the periodic [background checks](#partition-checks) shall be subject to a timeout by programming a nonzero timeout cycle count to [`CHECK_TIMEOUT`](../src/fuse_ctrl/doc/registers.md#check_timeout).
In this case, the [`CHECK_TIMEOUT`](../src/fuse_ctrl/doc/registers.md#check_timeout) register must be set before the [`INTEGRITY_CHECK_PERIOD`](../src/fuse_ctrl/doc/registers.md#integrity_check_period) and [`CONSISTENCY_CHECK_PERIOD`](../src/fuse_ctrl/doc/registers.md#consistency_check_period) registers (see next point).
- Enable periodic [background checks](#partition-checks) by programming nonzero mask values to [`INTEGRITY_CHECK_PERIOD`](../src/fuse_ctrl/doc/registers.md#integrity_check_period) and [`CONSISTENCY_CHECK_PERIOD`](../src/fuse_ctrl/doc/registers.md#consistency_check_period).
- It is recommended to lock down the background check registers via [`CHECK_REGWEN`](../src/fuse_ctrl/doc/registers.md#check_regwen), once the background checks have been set up
1. Check that the OTP controller has successfully initialized by reading [`STATUS`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#status). I.e., make sure that none of the ERROR bits are set, and that the DAI is idle ([`STATUS.DAI_IDLE`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#status)).
- Choose whether the periodic background checks shall be subject to a timeout by programming a nonzero timeout cycle count to [`CHECK_TIMEOUT`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#check_timeout).
In this case, the [`CHECK_TIMEOUT`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#check_timeout) register must be set before the [`INTEGRITY_CHECK_PERIOD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#integrity_check_period) and [`CONSISTENCY_CHECK_PERIOD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#consistency_check_period) registers (see next point).
- Enable periodic background checks by programming nonzero mask values to [`INTEGRITY_CHECK_PERIOD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#integrity_check_period) and [`CONSISTENCY_CHECK_PERIOD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#consistency_check_period).
- It is recommended to lock down the background check registers via [`CHECK_REGWEN`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#check_regwen), once the background checks have been set up

If needed, one-off integrity and consistency checks can be triggered via [`CHECK_TRIGGER`](../src/fuse_ctrl/doc/registers.md#check_trigger).
If this functionality is not needed, it is recommended to lock down the trigger register via [`CHECK_TRIGGER_REGWEN`](../src/fuse_ctrl/doc/registers.md#check_trigger_regwen).

Later on during the boot process, SW may also choose to block read access to the SW managed partitions via the associated partition lock registers, e.g. [`CREATOR_SW_CFG_READ_LOCK`](../src/fuse_ctrl/doc/registers.md#creator_sw_cfg_read_lock) or [`OWNER_SW_CFG_READ_LOCK`](../src/fuse_ctrl/doc/registers.md#owner_sw_cfg_read_lock).

## Programming interface
The Fuse Controller (FC) programming interface is designed to manage lifecycle states, handle fuses with ECC support, and ensure secure interactions with the fuse macros. A key component in this architecture is the Fuse Controller Filter RTL. This module intercepts and verifies the fuse programming sequence by checking that all parts of the transaction originate from the same authorized source. In doing so, the filter guarantees that fuse provisioning is performed in an atomic manner.

Expand Down Expand Up @@ -933,18 +941,18 @@ Below are the key operations supported by the programming interface:

A typical readout sequence looks as follows:

- Check whether the DAI is idle by reading the [`STATUS`](../src/fuse_ctrl/doc/registers.md#status) register.
- Write the byte address for the access to [`DIRECT_ACCESS_ADDRESS`](../src/fuse_ctrl/doc/registers.md#direct_access_address).
- Check whether the DAI is idle by reading the [`STATUS`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#status) register.
- Write the byte address for the access to [`DIRECT_ACCESS_ADDRESS`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_address).
Note that the address is aligned with the granule, meaning that either 2 or 3 LSBs of the address are ignored, depending on whether the access granule is 32 or 64bit.
- Trigger a read command by writing 0x1 to [`DIRECT_ACCESS_CMD`](../src/fuse_ctrl/doc/registers.md#direct_access_cmd).
- Poll the [`STATUS`](../src/fuse_ctrl/doc/registers.md#status) until the DAI state goes back to idle.
- Trigger a read command by writing 0x1 to [`DIRECT_ACCESS_CMD`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_cmd).
- Poll the [`STATUS`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#status) until the DAI state goes back to idle.
Alternatively, the `otp_operation_done` interrupt can be enabled up to notify the processor once an access has completed.
- If the status register flags a DAI error, additional handling is required.
- If the region accessed has a 32bit access granule, the 32bit chunk of read data can be read from [`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/registers.md#direct_access_rdata).
If the region accessed has a 64bit access granule, the 64bit chunk of read data can be read from the [`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/registers.md#direct_access_rdata) and [`DIRECT_ACCESS_RDATA_1`](../src/fuse_ctrl/doc/registers.md#direct_access_rdata) registers.
- If the region accessed has a 32bit access granule, the 32bit chunk of read data can be read from [`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_rdata).
If the region accessed has a 64bit access granule, the 64bit chunk of read data can be read from the [`DIRECT_ACCESS_RDATA_0`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_rdata) and [`DIRECT_ACCESS_RDATA_1`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_rdata) registers.
- Go back to the first step and repeat until all data has been read.

The hardware will set [`DIRECT_ACCESS_REGWEN`](../src/fuse_ctrl/doc/registers.md#direct_access_regwen) to 0x0 while an operation is pending in order to temporarily lock write access to the CSRs registers.
The hardware will set [`DIRECT_ACCESS_REGWEN`](../src/fuse_ctrl/doc/otp_ctrl_registers.md#direct_access_regwen) to 0x0 while an operation is pending in order to temporarily lock write access to the CSRs registers.

## Sequences: Reset, Boot

Expand Down Expand Up @@ -1173,7 +1181,7 @@ Internal |output | 1 | `hw_rev_o` |
Internal |input | 253 | `cptra_ss_mcu_ext_int` | | Reflection of HW revision ID read from fuse controller |


## Memory Map / Address Map
## Fuse Macro Memory Map / Fuse Controller CSR Address Map

See [Life-cycle Controller Register Map](../src/lc_ctrl/rtl/lc_ctrl.rdl).

Expand All @@ -1190,6 +1198,8 @@ See [Life-cycle Controller Register Map](../src/lc_ctrl/rtl/lc_ctrl.rdl).
- Verify that all output signals, including alerts, remain within the expected ranges under normal operation.
2. **IMPORTANT SOC REQUIREMENT**:
- Life cycle controller allows you to switch from internal to external clock on a request from SOC over JTAG or AXI (As explained in other sections, this is typically used for Fuse programming scenarios when a stable clock is not yet available within the SOC). When the request arrives, life cycle controller requests the SOC to do the clock switch. When such a request is made, SOC MUST respond with an acknowledgement within 2 clock cycles of the internal clock. If this condition is not met, OTP controller will assert "program error" and the SOC must go through a reset cycle and redo the above steps. This must be verified by the SOC as a part of Caliptra subsystem integration checks.

To protect from clock stretching attacks Caliptra mandates using a clock source that is constructed within the SOC (eg. PLL, Calibrated Ring Oscillator, etc). For such a clock source, a SOC may require fuses to be programmed. TP programming demands a reliable and deterministic clock signal to ensure correct fuse write operations; which SOC may not have during the early phases of manufacturing flow due to above constraints. In order to overcome this issue, this `external clock` can be used typically in the manufacturing phase of a SOC; and for such SOCs this external clock is supplied from a platform (e.g an ATE). Since the Caliptra subsystem includes only one clock input (`cptra_ss_clk_i`), the SoC integrator is responsible for ensuring that this input can be switched to a stable source.

3. **Scan Path Exclusions**:
- Ensure that the RAW\_UNLOCK token is excluded from the scan chain. This token is different from other LC transition tokens as it is stored in the plaintext in gates, not in hashed form.
Expand Down
2 changes: 1 addition & 1 deletion src/fuse_ctrl/coverage/fuse_ctrl_cov_if.sv
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ interface fuse_ctrl_cov_if
covergroup fuse_ctrl_fuses_cg @(posedge clk_i);
option.per_instance = 1;
// SECRET_TEST_UNLOCK_PARTITION
CptraCoreManufDebugUnlockToken_cp: coverpoint `FC_MEM[CptraCoreManufDebugUnlockTokenOffset/2] { bins Fuse = { [1:$] }; }
CptraCoreManufDebugUnlockToken_cp: coverpoint `FC_MEM[CptraSsManufDebugUnlockTokenOffset/2] { bins Fuse = { [1:$] }; }
SecretTestUnlockPartitionDigest_cp: coverpoint `FC_MEM[SwTestUnlockPartitionDigestOffset/2] { bins Fuse = { [1:$] }; }
// SECRET_MANUF_PARTITION
CptraCoreUdsSeed_cp: coverpoint `FC_MEM[CptraCoreUdsSeedOffset/2] { bins Fuse = { [1:$] }; }
Expand Down
Loading