OTBN DV document


  • DV
    • Verify the OTBN processor by running dynamic simulations with a SV/UVM based testbench
    • These simulations are grouped in tests listed in the testplan below.
    • Close code and functional coverage on the IP and all of its sub-modules
  • FPV
    • Verify TileLink device protocol compliance with an SVA based testbench

Current status

Design features

OTBN, the OpenTitan Big Number accelerator, is a cryptographic accelerator. For detailed information on OTBN design features, see the OTBN HWIP technical specification.

Testbench architecture

The OTBN testbench is based on the CIP testbench architecture. It builds on the dv_utils and csr_utils packages.

Block diagram

OTBN testing makes use of a DPI-based model called otbn_core_model. This is shown in the block diagram. The dotted interfaces in the otbn block are bound in by the model to access internal signals (register file and memory contents).

Block diagram

Top level testbench

The top-level testbench is located at hw/ip/otbn/dv/uvm/tb.sv. This instantiates the OTBN DUT module hw/ip/otbn/rtl/otbn.sv.

OTBN has the following interfaces:

  • A Clock and reset interface
  • A TileLink interface. OTBN is a TL-UL device, which expects to communicate with a TL-UL host. In the OpenTitan SoC, this will be the Ibex core.
  • Idle signals in each clock domain, idle_o, and idle_otp_o
  • One interrupt
  • An alert interface
  • A life cycle escalation interface
  • An OTP connection
  • Two EDN connections
  • A RAM configuration interface, which is passed through to the SRAM macros

The idle and interrupt signals are modelled with the basic pins_if interface.

As well as instantiating OTBN, the testbench also instantiates an otbn_core_model. This module wraps an ISS (instruction set simulator) subprocess and performs checks to make sure that OTBN behaves the same as the ISS. The otbn_core_model module communicates with test sequences through an otbn_model_if interface, which is monitored by the otbn_model_agent, described below. The module communicates with the Python subprocess as shown in the diagram below.

Model communication

OTBN model agent

The model agent is instantiated by the testbench to monitor the OTBN model. It is a passive agent (essentially just a monitor): the inputs to the model are set in tb.sv. The monitor for the agent generates transactions when it sees a start signal or a done signal.

The start signal is important because we “cheat” and pull it out of the DUT. To make sure that the processor is starting when we expect, we check start transactions against TL writes in the scoreboard.

Reference models

The main reference model for OTBN is the instruction set simulator (ISS), which is run as a subprocess by DPI code inside otbn_core_model. This Python-based simulator can be found at hw/ip/otbn/dv/otbnsim.

Stimulus strategy

When testing OTBN, we are careful to distinguish between

  • behaviour that can be triggered by particular instruction streams
  • behaviour that is triggered by particular external stimuli (register writes; surprise resets etc.)

Testing lots of different instruction streams doesn’t really use the UVM machinery, so we have a “pre-DV” phase of testing that generates constrained-random instruction streams (as ELF binaries) and runs a simple block-level simulation on each to check that the RTL matches the model. The idea is that this is much quicker for designers to use to smoke-test proposed changes, and can be run with Verilator, so it doesn’t require an EDA tool licence. This pre-DV phase cannot drive sign-off, but it does use much of the same tooling.

Once we are running full DV tests, we re-use this work, by using the same collection of randomised instruction streams and randomly picking from them for most of the sequences. At the moment, the full DV tests create binaries on the fly by running hw/ip/otbn/dv/uvm/gen-binaries.py. This results in one or more ELF files in a directory, which the simulation then picks from at random.

The pre-DV testing doesn’t address external stimuli like resets or TileLink-based register accesses. These are driven by specialised test sequences, described below.

Functional coverage

As a complicated IP block, OTBN has a lot of functional coverage points defined. To avoid overwhelming this document, these are described in OTBN functional coverage.

Test sequences

The test sequences can be found in hw/ip/otbn/dv/uvm/env/seq_lib. The basic test sequence (otbn_base_vseq) loads the instruction stream from a randomly chosen binary (see above), configures OTBN and then lets it run to completion.

More specialized sequences include things like multiple runs, register accesses during operation (which should fail) and memory corruption. We also check things like the correct operation of the interrupt registers.

Self-checking strategy


Much of the checking for these tests is actually performed in otbn_core_model, which ensures that the RTL and ISS have the same behaviour. However, the scoreboard does have some checks, to ensure that interrupt and idle signals are high at the expected times.


Core TLUL protocol assertions are checked by binding the TL-UL protocol checker into the design.

Outputs are also checked for 'X values by assertions in the design RTL. The design RTL contains other assertions defined by the designers, which will be checked in simulation (and won’t have been checked by the pre-DV Verilator simulations).

Finally, the otbn_idle_checker checks that the idle_o output correctly matches the running state that you’d expect, based on writes to the CMD register and responses that will appear in the DONE interrupt.

Building and running tests

Tests can be run with dvsim.py. The link gives details of the tool’s features and command line arguments. To run a basic smoke test, go to the top of the repository and run:

$ util/dvsim/dvsim.py hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson -i otbn_smoke



Milestone Name Tests Description
V1 smoke otbn_smoke

Smoke test, running a single fixed binary

This runs the binary from otbn/dv/smoke/smoke_test.s, which is designed to check most of the implemented instructions. The unchanging binary should mean this basic test is particularly appropriate for CI.

V1 single_binary otbn_single

Run a single randomly-chosen binary

This test drives the main bulk of OTBN testing. It picks a random binary from a pre-generated set and runs it, comparing against the model. We'll run this with a large number of seeds and use functional coverage to track when verification of the internals of the core is done.

Sometimes enable the "done" interrupt to check that it and the error interrupt work correctly.

V1 csr_hw_reset otbn_csr_hw_reset

Verify the reset values as indicated in the RAL specification.

  • Write all CSRs with a random value.
  • Apply reset to the DUT as well as the RAL model.
  • Read each CSR and compare it against the reset value. it is mandatory to replicate this test for each reset that affects all or a subset of the CSRs.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_rw otbn_csr_rw

Verify accessibility of CSRs as indicated in the RAL specification.

  • Loop through each CSR to write it with a random value.
  • Read the CSR back and check for correctness while adhering to its access policies.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_bit_bash otbn_csr_bit_bash

Verify no aliasing within individual bits of a CSR.

  • Walk a 1 through each CSR by flipping 1 bit at a time.
  • Read the CSR back and check for correctness while adhering to its access policies.
  • This verify that writing a specific bit within the CSR did not affect any of the other bits.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_aliasing otbn_csr_aliasing

Verify no aliasing within the CSR address space.

  • Loop through each CSR to write it with a random value
  • Shuffle and read ALL CSRs back.
  • All CSRs except for the one that was written in this iteration should read back the previous value.
  • The CSR that was written in this iteration is checked for correctness while adhering to its access policies.
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
  • Shuffle the list of CSRs first to remove the effect of ordering.
V1 csr_mem_rw_with_rand_reset otbn_csr_mem_rw_with_rand_reset

Verify random reset during CSR/memory access.

  • Run csr_rw sequence to randomly access CSRs
  • If memory exists, run mem_partial_access in parallel with csr_rw
  • Randomly issue reset and then use hw_reset sequence to check all CSRs are reset to default value
  • It is mandatory to run this test for all available interfaces the CSRs are accessible from.
V1 regwen_csr_and_corresponding_lockable_csr otbn_csr_rw

Verify regwen CSR and its corresponding lockable CSRs.

  • Randomly access all CSRs
  • Test when regwen CSR is set, its corresponding lockable CSRs become read-only registers


  • If regwen CSR is HW read-only, this feature can be fully tested by common CSR tests - csr_rw and csr_aliasing.
  • If regwen CSR is HW updated, a separate test should be created to test it.

This is only applicable if the block contains regwen and locakable CSRs.

V1 mem_walk otbn_mem_walk

Verify accessibility of all memories in the design.

  • Run the standard UVM mem walk sequence on all memories in the RAL model.
  • It is mandatory to run this test from all available interfaces the memories are accessible from.
V1 mem_partial_access otbn_mem_partial_access

Verify partial-accessibility of all memories in the design.

  • Do partial reads and writes into the memories and verify the outcome for correctness.
  • Also test outstanding access on memories
V2 reset_recovery otbn_reset

Run two binaries, resetting the first at an arbitrary time

Running another binary after a sudden and unexpected reset via the rst_ni signal will check that all state is properly re-initialized after a reset. We'd expect X-propagation checks to catch most problems like this, but an explicit reset sequence also adds the relevant FSM/toggle coverage.

V2 multi_error otbn_multi_err

Run instructions that cause multiple SW errors in a cycle

These are directed tests, designed to exhaustively trigger all the cases where a single instruction execution can fail for more than one reason. Since each of these instructions causes the operation to fail, we have to run an OTBN operation for each. To do this, we compile and run all the binaries in a collection of ISS unit tests. We have coverage points to ensure we see every event we expect.

V2 back_to_back otbn_multi

Run sequences back-to-back

This runs several sequences back-to-back, without resets between them. This should catch initialisation problems where not all state is cleared between programs when there's no reset.

V2 stress_all otbn_stress_all

Run assorted sequences back-to-back.

V2 lc_escalation otbn_escalate

Trigger the life cycle escalation input.

V2 zero_state_err_urnd otbn_zero_state_err_urnd

Trigger the "state is zero" error in URND, Check that fatal error is asserted.

V2 sw_errs_fatal_chk otbn_sw_errs_fatal_chk

Set ctrl.software_errs_fatal. When set software errors produce fatal errors, rather than recoverable errors.

V2 alert_test otbn_alert_test

Verify common alert_test CSR that allows SW to mock-inject alert requests.

  • Enable a random set of alert requests by writing random value to alert_test CSR.
  • Check each alert_tx.alert_p pin to verify that only the requested alerts are triggered.
  • During alert_handshakes, write alert_test CSR again to verify that: If alert_test writes to current ongoing alert handshake, the alert_test request will be ignored. If alert_test writes to current idle alert handshake, a new alert_handshake should be triggered.
  • Wait for the alert handshakes to finish and verify alert_tx.alert_p pins all sets back to 0.
  • Repeat the above steps a bunch of times.
V2 intr_test otbn_intr_test

Verify common intr_test CSRs that allows SW to mock-inject interrupts.

  • Enable a random set of interrupts by writing random value(s) to intr_enable CSR(s).
  • Randomly "turn on" interrupts by writing random value(s) to intr_test CSR(s).
  • Read all intr_state CSR(s) back to verify that it reflects the same value as what was written to the corresponding intr_test CSR.
  • Check the cfg.intr_vif pins to verify that only the interrupts that were enabled and turned on are set.
  • Clear a random set of interrupts by writing a randomly value to intr_state CSR(s).
  • Repeat the above steps a bunch of times.
V2 tl_d_oob_addr_access otbn_tl_errors

Access out of bounds address and verify correctness of response / behavior

V2 tl_d_illegal_access otbn_tl_errors

Drive unsupported requests via TL interface and verify correctness of response / behavior. Below error cases are tested bases on the [TLUL spec]({{< relref "hw/ip/tlul/doc/_index.md#explicit-error-cases" >}})

  • TL-UL protocol error cases
    • invalid opcode
    • some mask bits not set when opcode is PutFullData
    • mask does not match the transfer size, e.g. a_address = 0x00, a_size = 0, a_mask = 'b0010
    • mask and address misaligned, e.g. a_address = 0x01, a_mask = 'b0001
    • address and size aren't aligned, e.g. a_address = 0x01, a_size != 0
    • size is greater than 2
  • OpenTitan defined error cases
    • access unmapped address, expect d_error = 1 when devmode_i == 1
    • write a CSR with unaligned address, e.g. a_address[1:0] != 0
    • write a CSR less than its width, e.g. when CSR is 2 bytes wide, only write 1 byte
    • write a memory with a_mask != '1 when it doesn't support partial accesses
    • read a WO (write-only) memory
    • write a RO (read-only) memory
    • write with instr_type = True
V2 tl_d_outstanding_access otbn_csr_hw_reset

Drive back-to-back requests without waiting for response to ensure there is one transaction outstanding within the TL device. Also, verify one outstanding when back- to-back accesses are made to the same address.

V2 tl_d_partial_access otbn_csr_hw_reset

Access CSR with one or more bytes of data. For read, expect to return all word value of the CSR. For write, enabling bytes should cover all CSR valid fields.

V2S mem_integrity otbn_imem_err

Inject ECC errors into DMEM and IMEM and expect an alert

V2S internal_integrity

Corrupt internal state and expect an alert

V2S illegal_bus_access otbn_illegal_mem_acc

Trigger reads and writes to both DMEM and IMEM and expect a fatal alert for ILLEGAL_BUS_ACCESS. Check that *mem_rdata_bus pins are at 0 when reads are done

V2S tl_intg_err otbn_tl_intg_err

Verify that the data integrity check violation generates an alert.

  • Randomly inject errors on the control, data, or the ECC bits during CSR accesses. Verify that triggers the correct fatal alert.
  • Inject a fault at the onehot check in u_reg.u_prim_reg_we_check and verify the corresponding fatal alert occurs
V2S passthru_mem_tl_intg_err otbn_passthru_mem_tl_intg_err

Verify data integrity is stored in the passthru memory rather than generated after a read.

  • Randomly read a memory location and check the data integrity is correct.
  • Backdoor inject fault into this location.
  • Check the data integrity is incorrect but there is no d_error as the memory block should just pass the stored data and integrity to the processor where the integrity is compared.
  • Above sequences will be run with csr_rw_vseq to ensure it won't affect CSR accesses.
V2S prim_fsm_check otbn_sec_cm

Verify that entering to an undefined state generates a fatal alert.


  • Backdoor force the FSM to any of the undefined values.
  • Randomly force the FSM back to a defined state to ensure the error is latched and won't go away until reset.
  • Within the next few cycles, the FSM landing in an invalid state should trigger a fatal alert.
  • Repeat for ALL prim_fsm instances in the DUT.


  • Check that fatal alert is triggered.
  • Check that err_code/fault_status is updated correctly and preserved until reset.
  • Verify any operations that follow fail (as applicable).
V2S sec_cm_mem_scramble otbn_smoke

Verify the countermeasure(s) MEM.SCRAMBLE.

V2S sec_cm_data_mem_integrity otbn_imem_err

Verify the countermeasure(s) DATA.MEM.INTEGRITY.

V2S sec_cm_instruction_mem_integrity otbn_imem_err

Verify the countermeasure(s) INSTRUCTION.MEM.INTEGRITY.

V2S sec_cm_bus_integrity otbn_tl_intg_err

Verify the countermeasure(s) BUS.INTEGRITY.

V2S sec_cm_controller_fsm_global_esc otbn_escalate

Verify the countermeasure(s) CONTROLLER.FSM.GLOBAL_ESC.

V2S sec_cm_controller_fsm_local_esc otbn_imem_err

Verify the countermeasure(s) CONTROLLER.FSM.LOCAL_ESC.

V2S sec_cm_controller_fsm_sparse otbn_sec_cm

Verify the countermeasure(s) CONTROLLER.FSM.SPARSE.

V2S sec_cm_scramble_key_sideload

Verify the countermeasure(s) SCRAMBLE.KEY.SIDELOAD.

V2S sec_cm_scramble_ctrl_fsm_local_esc otbn_imem_err

Verify the countermeasure(s) SCRAMBLE_CTRL.FSM.LOCAL_ESC.

V2S sec_cm_scramble_ctrl_fsm_sparse otbn_sec_cm

Verify the countermeasure(s) SCRAMBLE_CTRL.FSM.SPARSE.

V2S sec_cm_start_stop_ctrl_fsm_global_esc otbn_escalate

Verify the countermeasure(s) START_STOP_CTRL.FSM.GLOBAL_ESC.

V2S sec_cm_start_stop_ctrl_fsm_local_esc otbn_imem_err

Verify the countermeasure(s) START_STOP_CTRL.FSM.LOCAL_ESC.

V2S sec_cm_start_stop_ctrl_fsm_sparse otbn_sec_cm

Verify the countermeasure(s) START_STOP_CTRL.FSM.SPARSE.

V2S sec_cm_data_reg_sw_sca

Verify the countermeasure(s) DATA_REG_SW.SCA.

V2S sec_cm_ctrl_redun

Verify the countermeasure(s) CTRL.REDUN.

V2S sec_cm_pc_ctrl_flow_redun

Verify the countermeasure(s) PC.CTRL_FLOW.REDUN.

V2S sec_cm_rnd_bus_consistency

Verify the countermeasure(s) RND.BUS.CONSISTENCY.

V2S sec_cm_rnd_rng_digest

Verify the countermeasure(s) RND.RNG.DIGEST.

V2S sec_cm_rf_base_data_reg_sw_integrity otbn_csr_rw

Verify the countermeasure(s) RF_BASE.DATA_REG_SW.INTEGRITY.

V2S sec_cm_rf_base_data_reg_sw_glitch_detect

Verify the countermeasure(s) RF_BASE.DATA_REG_SW.GLITCH_DETECT.

V2S sec_cm_stack_wr_ptr_ctr_redun

Verify the countermeasure(s) STACK_WR_PTR.CTR.REDUN.

V2S sec_cm_stack_wr_ptr_ctr_glitch_detect

Verify the countermeasure(s) STACK_WR_PTR.CTR.GLITCH_DETECT.

V2S sec_cm_rf_bignum_data_reg_sw_integrity otbn_csr_rw

Verify the countermeasure(s) RF_BIGNUM.DATA_REG_SW.INTEGRITY.

V2S sec_cm_rf_bignum_data_reg_sw_glitch_detect

Verify the countermeasure(s) RF_BIGNUM.DATA_REG_SW.GLITCH_DETECT.

V2S sec_cm_loop_stack_ctr_redun

Verify the countermeasure(s) LOOP_STACK.CTR.REDUN.

V2S sec_cm_loop_stack_addr_integrity

Verify the countermeasure(s) LOOP_STACK.ADDR.INTEGRITY.

V2S sec_cm_call_stack_addr_integrity

Verify the countermeasure(s) CALL_STACK.ADDR.INTEGRITY.

V2S sec_cm_start_stop_ctrl_state_consistency

Verify the countermeasure(s) START_STOP_CTRL.STATE.CONSISTENCY.

V3 stress_all_with_rand_reset otbn_stress_all_with_rand_reset

This test runs 3 parallel threads - stress_all, tl_errors and random reset. After reset is asserted, the test will read and check all valid CSR registers.


Name Description

Cover each lockable reg field with these 2 cases:

  • When regwen = 1, a different value is written to the lockable CSR field, and a read occurs after that.
  • When regwen = 0, a different value is written to the lockable CSR field, and a read occurs after that.

This is only applicable if the block contains regwen and locakable CSRs.


Cover the following error cases on TL-UL bus:

  • TL-UL protocol error cases.
  • OpenTitan defined error cases, refer to testpoint tl_d_illegal_access.

Cover all kinds of integrity errors (command, data or both) and cover number of error bits on each integrity check.

Cover the kinds of integrity errors with byte enabled write on memory if applicable: Some memories store the integrity values. When there is a subword write, design re-calculate the integrity with full word data and update integrity in the memory. This coverage ensures that memory byte write has been issued and the related design logic has been verfied.