Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 0 deletions hw/ip/otbn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ See that document for integration overview within the broader top level system.
* Built-in access to random numbers.
* CSR / WSR based interface to KMAC HWIP to offload hashing operations.
* A CSR / WSR based Masking Accelerator Interface (MAI) for efficient and first-order SCA hardened masking operations.
* A WFI instruction which pauses an OTBN application and then allows a host to read/write the DMEM whilst paused.
The host must command to resume the execution.

## Description

Expand Down
22 changes: 22 additions & 0 deletions hw/ip/otbn/data/base-insns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,28 @@
rd: b00000
opcode: b11100

- mnemonic: wfi
synopsis: Wait For Interrupt
operands: []
straight-line: false
doc: |
Stalls an OTBN program until a host issues the RESUME command in the CMD register.
Once the RESUME command is issued, OTBN will continue executing the next instruction (PC + 4).
Triggers the `done` interrupt to indicate that OTBN is now paused.

This instruction is only valid when CTRL.wfi_enabled is set to 1.
Otherwise an invalid instruction error is raised.
errs:
- An `ILLEGAL_INSN` error if CTRL.wfi_enabled is 0.
encoding:
scheme: I
mapping:
imm: b000100000101
rs1: b00000
funct3: b000
rd: b00000
opcode: b11100

- mnemonic: loop
synopsis: Loop (indirect)
operands:
Expand Down
33 changes: 24 additions & 9 deletions hw/ip/otbn/data/otbn.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
]
interrupt_list: [
{ name: "done"
desc: "OTBN has completed the operation."
desc: "OTBN has completed the operation, encountered a WFI instruction or has locked itself."
}
]
alert_list: [
Expand Down Expand Up @@ -439,24 +439,25 @@

regwidth: "32"
registers: [
// The magic values for EXECUTE, SEC_WIPE_DMEM and SEC_WIPE_IMEM in the CMD
// register below were generated with the sparse-fsm-encode.py script:
// The magic values in the CMD register below were generated with the sparse-fsm-encode.py
// script:
//
// util/design/sparse-fsm-encode.py -d 4 -m 3 -n 8 --avoid-zero -s 1
// util/design/sparse-fsm-encode.py -d 4 -m 4 -n 8 --avoid-zero -s 1
//
// and have a hamming distance of at least 4 from one another and the zero
// word.
{ name: "CMD"
desc: '''
Command Register

A command initiates an OTBN operation. While performing the operation,
Any command except `RESUME` initiates an OTBN operation. While performing the operation,
OTBN is busy; the !!STATUS register reflects that.

All operations signal their completion by raising the done
interrupt; alternatively, software may poll the !!STATUS register.

Writes are ignored if OTBN is not idle.
The EXECUTE, SEC_WIPE_DMEM, and SEC_WIPE_IMEM commands take only effect if OTBN is idle.
The RESUME command only takes effect if OTBN is paused.
Unrecognized commands are ignored.
''',
swaccess: "wo",
Expand All @@ -475,6 +476,7 @@
| 0xd8 | EXECUTE | Starts the execution of the program stored in the instruction memory, starting at address zero. |
| 0xc3 | SEC_WIPE_DMEM | Securely removes all contents from the data memory. |
| 0x1e | SEC_WIPE_IMEM | Securely removes all contents from the instruction memory. |
| 0xa6 | RESUME | Resumes execution of a program that was previously paused by a WFI instruction. |
'''
tags: [
// Don't write this field in the automated CSR tests: it would
Expand All @@ -500,14 +502,26 @@
When set software errors produce fatal errors, rather than
recoverable errors.

Writes are ignored if OTBN is not idle.
'''
},
{ bits: "1",
name: "wfi_enabled",
resval: 0,
desc: '''
Controls whether the WFI instruction is enabled.

When set, the WFI instruction can be used to pause execution.
When cleared, the WFI instruction is treated as an invalid instruction.

Writes are ignored if OTBN is not idle.
'''
}
],
tags: [
// Don't write this register in the automated CSR tests, because those
// tests are not aware whether OTBN is idle or not. If OTBN is not idle,
// it ignores the write and the test would fail.
// Don't write this register in the automated CSR tests, because those tests are not aware
// whether OTBN is accepting commands or not. If OTBN is not accepting commands, it ignores
// the write and the test would fail.
"excl:CsrAllTests:CsrExclWrite"
]
}
Expand All @@ -534,6 +548,7 @@
| 0x02 | BUSY_SEC_WIPE_DMEM | OTBN is busy securely wiping the data memory. |
| 0x03 | BUSY_SEC_WIPE_IMEM | OTBN is busy securely wiping the instruction memory. |
| 0x04 | BUSY_SEC_WIPE_INT | OTBN is busy securely wiping the internal state. |
| 0x05 | PAUSED | OTBN is paused due to a WFI instruction. |
| 0xFF | LOCKED | OTBN is locked as reaction to a fatal error, and must be reset to unlock it again. See also the section "Reaction to Fatal Errors". |

'''
Expand Down
6 changes: 3 additions & 3 deletions hw/ip/otbn/doc/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ Referring to the [Comportable guideline for peripheral device functionality](htt

## Interrupts

| Interrupt Name | Type | Description |
|:-----------------|:-------|:----------------------------------|
| done | Event | OTBN has completed the operation. |
| Interrupt Name | Type | Description |
|:-----------------|:-------|:--------------------------------------------------------------------------------------|
| done | Event | OTBN has completed the operation, encountered a WFI instruction or has locked itself. |

## Security Alerts

Expand Down
399 changes: 1 addition & 398 deletions hw/ip/otbn/doc/otbn_operational_states.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 16 additions & 12 deletions hw/ip/otbn/doc/registers.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ Interrupt State Register
{"reg": [{"name": "done", "bits": 1, "attr": ["rw1c"], "rotate": -90}, {"bits": 31}], "config": {"lanes": 1, "fontsize": 10, "vspace": 80}}
```

| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:-------|:----------------------------------|
| 31:1 | | | | Reserved |
| 0 | rw1c | 0x0 | done | OTBN has completed the operation. |
| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:-------|:--------------------------------------------------------------------------------------|
| 31:1 | | | | Reserved |
| 0 | rw1c | 0x0 | done | OTBN has completed the operation, encountered a WFI instruction or has locked itself. |

## INTR_ENABLE
Interrupt Enable Register
Expand Down Expand Up @@ -91,13 +91,14 @@ Alert Test Register
## CMD
Command Register

A command initiates an OTBN operation. While performing the operation,
Any command except `RESUME` initiates an OTBN operation. While performing the operation,
OTBN is busy; the [`STATUS`](#status) register reflects that.

All operations signal their completion by raising the done
interrupt; alternatively, software may poll the [`STATUS`](#status) register.

Writes are ignored if OTBN is not idle.
The EXECUTE, SEC_WIPE_DMEM, and SEC_WIPE_IMEM commands take only effect if OTBN is idle.
The RESUME command only takes effect if OTBN is paused.
Unrecognized commands are ignored.
- Offset: `0x10`
- Reset default: `0x0`
Expand All @@ -122,23 +123,25 @@ The operation to perform.
| 0xd8 | EXECUTE | Starts the execution of the program stored in the instruction memory, starting at address zero. |
| 0xc3 | SEC_WIPE_DMEM | Securely removes all contents from the data memory. |
| 0x1e | SEC_WIPE_IMEM | Securely removes all contents from the instruction memory. |
| 0xa6 | RESUME | Resumes execution of a program that was previously paused by a WFI instruction. |

## CTRL
Control Register
- Offset: `0x14`
- Reset default: `0x0`
- Reset mask: `0x1`
- Reset mask: `0x3`

### Fields

```wavejson
{"reg": [{"name": "software_errs_fatal", "bits": 1, "attr": ["rw"], "rotate": -90}, {"bits": 31}], "config": {"lanes": 1, "fontsize": 10, "vspace": 210}}
{"reg": [{"name": "software_errs_fatal", "bits": 1, "attr": ["rw"], "rotate": -90}, {"name": "wfi_enabled", "bits": 1, "attr": ["rw"], "rotate": -90}, {"bits": 30}], "config": {"lanes": 1, "fontsize": 10, "vspace": 210}}
```

| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:--------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 31:1 | | | | Reserved |
| 0 | rw | 0x0 | software_errs_fatal | Controls the reaction to software errors. When set software errors produce fatal errors, rather than recoverable errors. Writes are ignored if OTBN is not idle. |
| Bits | Type | Reset | Name | Description |
|:------:|:------:|:-------:|:--------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 31:2 | | | | Reserved |
| 1 | rw | 0x0 | wfi_enabled | Controls whether the WFI instruction is enabled. When set, the WFI instruction can be used to pause execution. When cleared, the WFI instruction is treated as an invalid instruction. Writes are ignored if OTBN is not idle. |
| 0 | rw | 0x0 | software_errs_fatal | Controls the reaction to software errors. When set software errors produce fatal errors, rather than recoverable errors. Writes are ignored if OTBN is not idle. |

## STATUS
Status Register
Expand Down Expand Up @@ -170,6 +173,7 @@ All BUSY values represent an operation started by a write to the
| 0x02 | BUSY_SEC_WIPE_DMEM | OTBN is busy securely wiping the data memory. |
| 0x03 | BUSY_SEC_WIPE_IMEM | OTBN is busy securely wiping the instruction memory. |
| 0x04 | BUSY_SEC_WIPE_INT | OTBN is busy securely wiping the internal state. |
| 0x05 | PAUSED | OTBN is paused due to a WFI instruction. |
| 0xFF | LOCKED | OTBN is locked as reaction to a fatal error, and must be reset to unlock it again. See also the section "Reaction to Fatal Errors". |


Expand Down
28 changes: 22 additions & 6 deletions hw/ip/otbn/doc/theory_of_operation.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ These access 256b-aligned 256b words.
Both memories can be accessed through OTBN's register interface ([`DMEM`](registers.md#dmem) and [`IMEM`](registers.md#imem)).
All memory accesses through the register interface must be word-aligned 32b word accesses.

When OTBN is in any state other than [idle](#operational-states), reads return zero and writes have no effect.
Furthermore, a memory access when OTBN is neither idle nor locked will cause OTBN to generate a fatal error with code `ILLEGAL_BUS_ACCESS`.
A host processor can check whether OTBN is busy by reading the [`STATUS`](registers.md#status) register.
When OTBN is [idle](#operational-states), both memories are accessible through the register interface.
When OTBN is [paused](#operational-states) by a {{#otbn-insn-ref WFI}} instruction, only the DMEM is unlocked and accessible, while the IMEM remains locked.
Note that pausing is only possible when the {{#otbn-insn-ref WFI}} instruction is enabled in the [`CTRL`](registers.md#ctrl) register.
For any memory that is not accessible in the current state, reads return zero and writes have no effect.
Furthermore, an access to such an inaccessible memory will cause OTBN to generate a fatal error with code `ILLEGAL_BUS_ACCESS`, unless OTBN is locked.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for DMEM/IMEM to be locked when OTBN is locked?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old text was "a memory access when OTBN is neither idle nor locked will cause OTBN to generate a fatal error". So the memories can be accessed either in the idle or locked state. See the assignment imem_access_core and dmem_access_core in otbn.sv. Both the previous and new implementation allow this access. However, when OTBN is locked any read of the memories will return 0. See for example the imem_rdata_bus_en_d signal at otbn.sv:742.

If your question was if this can be changed so that the memories are locked when OTBN is locked: Yes would be possible. But as only 0 is returned I think there is no need to change the design.

A host processor can check the current state by reading the [`STATUS`](registers.md#status) register.

The underlying memories used to implement the IMEM and DMEM may not grant all access requests (see [Memory Scrambling](#memory-scrambling) for details).
A request won't be granted if new scrambling keys have been requested for the memory that aren't yet available.
Expand Down Expand Up @@ -97,19 +100,24 @@ Download the SVG from Google Draw, open it in Inkscape once and save it without
OTBN can be in different operational states.
After reset (*init*), OTBN performs a secure wipe of the internal state and then becomes *idle*.
OTBN is *busy* for as long it is performing an operation.
While executing an application, OTBN can become *paused* when executing the {{#otbn-insn-ref WFI}} instruction if this instruction is enabled in the [`CTRL`](registers.md#ctrl) register.
OTBN resumes the execution once the `RESUME` command is issued.
OTBN is *locked* if a fatal error was observed or after handling an RMA request.

The current operational state is reflected in the [`STATUS`](registers.md#status) register.
- After reset, OTBN is busy with the internal secure wipe and the [`STATUS`](registers.md#status) register is set to `BUSY_SEC_WIPE_INT`.
- If OTBN is idle, the [`STATUS`](registers.md#status) register is set to `IDLE`.
- If OTBN is busy, the [`STATUS`](registers.md#status) register is set to one of the values starting with `BUSY_`.
- If OTBN gets paused by a {{#otbn-insn-ref WFI}} instruction, the [`STATUS`](registers.md#status) register is set to `PAUSED`.
- If OTBN is locked, the [`STATUS`](registers.md#status) register is set to `LOCKED`.

OTBN transitions into the busy state as result of host software [issuing a command](#operations-and-commands); OTBN is then said to perform an operation.
OTBN transitions out of the busy state whenever the operation has completed.
OTBN transitions out of the busy state whenever the operation has completed or the execution gets paused.
In the [`STATUS`](registers.md#status) register the different `BUSY_*` values represent the operation that is currently being performed.

A transition out of the busy state is signaled by the `done` interrupt ([`INTR_STATE.done`](registers.md#intr_state)).
A transition out of the busy state or entering the `PAUSED` state is signaled by the `done` interrupt ([`INTR_STATE.done`](registers.md#intr_state)).

When paused, issuing a `RESUME` command makes the OTBN continue with the next instruction.

The locked state is a terminal state; transitioning out of it requires an OTBN reset.

Expand All @@ -124,6 +132,8 @@ The `SEC_WIPE_DMEM` command [securely wipes the data memory](#secure-wipe).

The `SEC_WIPE_IMEM` command [securely wipes the instruction memory](#secure-wipe).

The `RESUME` command resumes the execution after a {{#otbn-insn-ref WFI}} instruction.

### Software Execution

Software execution on OTBN is triggered by host software by [issuing the `EXECUTE` command](#operations-and-commands).
Expand All @@ -132,7 +142,13 @@ The software then runs to completion, without the ability for host software to i
- OTBN transitions into the busy state, and reflects this by setting [`STATUS`](registers.md#status) to `BUSY_EXECUTE`.
- The internal randomness source, which provides random numbers to the `URND` CSR and WSR, is re-seeded from the EDN.
- The instruction at address zero is fetched and executed.
- From this point on, all subsequent instructions are executed according to their semantics until either an {{#otbn-insn-ref ECALL}} instruction is executed, or an error is detected.
- From this point on, all subsequent instructions are executed according to their semantics until either:
- A {{#otbn-insn-ref WFI}} instruction is executed (if enabled).
In this case, the execution is paused and the DMEM is unlocked for the host.
The execution must be resumed by the host by issuing the `RESUME` command.
There can be multiple pauses per execution and each {{#otbn-insn-ref WFI}} instruction issues the `done` interrupt ([`INTR_STATE.done`](registers.md#intr_state)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, the "Wait For Interrupt" instruction triggers an interrupt and then waits for a (non-interrupt) ack from the host processor.

Has the name already been assigned somewhere? If not, I strongly advise renaming the instruction to something like PAUSE

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose the name WFI because this pausing feature is actually matching the WFI instruction defined by the RISCV specs (see privileged specs, chapter 3.3.3). I think it is a suitable name because:

I agree from a system view the WFI generates an interrupt which seems counter-intuitive. But looking from an OTBN SW perspective, the WFI instruction just pauses the execution until an interrupt happens. In this case the interrupt source is a change in OTBN's CTRL register. Which I think is a perfectly valid interrupt source.

That the 'done' interrupt is fired when a WFI instruction is hit is also a common and in my opinion valid behaviour. The RISCV spec says:

Execution of the WFI instruction can also be used to inform the hardware platform that suitable interrupts should preferentially be routed to this hart.

In our case the hardware platform is the Ibex / receiver of the 'done' interrupt. So I think firing the done interrupt is a valid action upon reaching the WFI instruction.

In addition, a similar behaviour can be found multi core systems / clusters. There are synchronization / fence instructions which pause a core until all other cores also reached this instruction. And each core then fires an 'interrupt' which the cluster controller registers. Once all cores are waiting this cluster controller then sends a signal to all cores to resume the execution.

And another idea why it should be named WFI is that I had the idea that in future a separate CSR can be used to mask out / enable certain interrupts for WFI if OTBN will ever feature interrupts (maybe from another accelerator/interface). So the WFI instruction then could either wait for a CTRL change or any other interrupt.

- An {{#otbn-insn-ref ECALL}} instruction is executed, or an error is detected.
This marks the end of the execution.
- A [secure wipe of internal state](#internal-state-secure-wipe) is performed.
- The [`ERR_BITS`](registers.md#err_bits) register is set to indicate either a successful execution (value `0`), or to indicate the error that was observed (a non-zero value).
- OTBN transitions into the [idle state](#operational-states) (in case of a successful execution, or a recoverable error) or the locked state (in case of a fatal error).
Expand Down
3 changes: 2 additions & 1 deletion hw/ip/otbn/dv/uvm/env/seq_lib/otbn_base_vseq.sv
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class otbn_base_vseq extends cip_base_vseq #(
1 :/ 9
};)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(num_wipes, num_wipes inside {[0 : 2]};)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(wipe_cmd, wipe_cmd != otbn_pkg::CmdExecute;)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(wipe_cmd,
wipe_cmd inside {otbn_pkg::CmdSecWipeDmem, otbn_pkg::CmdSecWipeImem};)
if (bogus_write) begin
// Write a bogus value while we are in IDLE state for coverage.
_send_bogus_cmd();
Expand Down
6 changes: 4 additions & 2 deletions hw/ip/otbn/dv/uvm/sva/otbn_idle_checker.sv
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ module otbn_idle_checker
end

// Our model of whether OTBN is running or not. We start on `do_start` once the initial secure
// wipe is done, and we stop on `done`.
// wipe is done, and we stop on `done` of an ecall instruction. The `done` interrupt also fires
// when OTBN pauses on a WFI instruction, but a pause does not end the operation. So we must not
// clear on the pause-entry `done`.
logic running_qq, running_q, running_d;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
Expand All @@ -76,7 +78,7 @@ module otbn_idle_checker
end
end
assign running_d = do_start & init_sec_wipe_done ? 1'b1 : // set
done ? 1'b0 : // clear
done & (status_q_i != otbn_pkg::StatusPaused) ? 1'b0 : // clear on completion
~init_sec_wipe_done & ~busy_secure_wipe ? 1'b0 : // clear
running_q; // keep

Expand Down
16 changes: 16 additions & 0 deletions hw/ip/otbn/dv/verilator/otbn_top_sim.sv
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ module otbn_top_sim (
assign keymgr_key.valid = 1'b1;

logic secure_wipe_running;
logic wfi_pending;
logic wfi_pending_q;

otbn_core #(
.ImemSizeByte ( ImemSizeByte ),
Expand Down Expand Up @@ -110,6 +112,10 @@ module otbn_top_sim (
.edn_urnd_i ( urnd_rsp ),
.edn_urnd_o ( urnd_req ),

.wfi_enabled_i ( 1'b1 ),
.wfi_pending_o ( wfi_pending ),
.wfi_resume_i ( wfi_pending_q ),

.insn_cnt_o ( insn_cnt ),
.insn_cnt_clear_i ( 1'b0 ),

Expand All @@ -131,6 +137,16 @@ module otbn_top_sim (
.kmac_app_rsp_i( '0 )
);

// Any WFI pause ends after 1 cycle. Pulse wfi_resume_i once because when unpausing the WFI
// instruction it still must retire to deassert wfi_pending_o.
always_ff @(posedge IO_CLK, negedge IO_RST_N) begin
if (!IO_RST_N) begin
wfi_pending_q <= 1'b0;
end else begin
wfi_pending_q <= wfi_pending & ~wfi_pending_q;
end
end

// The values returned by the mock EDN must match those set in `standalonesim.py`.
localparam logic [1:0][WLEN-1:0] FixedEdnVals = {{4{64'hCCCC_CCCC_BBBB_BBBB}},
{4{64'hAAAA_AAAA_9999_9999}}};
Expand Down
Loading
Loading