blob: 6303f70112db93aafd4b77f2b6f4cdc4f573be5d [file] [log] [blame] [view]
---
title: "OTBN DV document"
---
## Goals
* **DV**
* Verify the OTBN processor by running dynamic simulations with a SV/UVM based testbench
* These simulations are grouped in tests listed in the [DV plan](#dv-plan) 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 & verification stage]({{< relref "hw" >}})
* [HW development stages]({{< relref "doc/project/development_stages" >}})
* [Simulation results](https://reports.opentitan.org/hw/ip/otbn/dv/uvm/latest/results.html)
## 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]({{< relref ".." >}}).
## Testbench architecture
The OTBN testbench is based on the [CIP testbench architecture]({{< relref "hw/dv/sv/cip_lib/doc" >}}).
It builds on the [dv_utils]({{< relref "hw/dv/sv/dv_utils/README.md" >}}) and [csr_utils]({{< relref "hw/dv/sv/csr_utils/README.md" >}}) 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](tb.svg)
### 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]({{< relref "hw/dv/sv/common_ifs#clk_rst_if" >}})
- A [TileLink interface]({{< relref "hw/dv/sv/tl_agent/README.md" >}}).
OTBN is a TL device, which expects to communicate with a TL host.
In the OpenTitan SoC, this will be the Ibex core.
- An idle signal, `idle_o`
- Two interrupts
- An [alert interface]({{< relref "hw/dv/sv/alert_esc_agent/README" >}})
The idle and interrupt signals are modelled with the basic
[`pins_if`]({{< relref "hw/dv/sv/common_ifs#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 model communicates with the testbench through an `otbn_model_if` interface, which is monitored by the `otbn_model_agent`, described below.
### 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.
### 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.
## Functional coverage
We distinguish between *architectural* and *micro-architectural* functional coverage.
The idea is that the points that go into architectural coverage are those that a DV engineer could derive by reading the block specification.
The points that go into micro-architectural coverage are those that require knowledge of the block's micro-architecture.
Some of these will come from DV engineers; others from the block's designers.
These two views are complementary and will probably duplicate coverage points.
For example, an architectural coverage point might be "the processor executed `ADDI` and the result overflowed".
This might overlap with something like "the `overflow` signal in the ALU was true when adding".
### Block-based coverage
#### Call stack
The [call stack]({{< relref ".#call-stack" >}}) is exposed as a special register behind `x1`.
It has a bounded depth of 8 elements.
We expect to see the following events:
- Push to the call stack
- Pop from the call stack
- Push and pop from the call stack on a single instruction
- An instruction with multiple reads from `x1`
All four of these events should be crossed with the three states of the call stack: empty, partially full, and full.
> Coverage for these points is tracked in the `call_stack_cg` covergroup.
> We track all possible combinations of push/pop flags (8 possibilities) in `flags_cp`.
> The 3 different fullness states are tracked as `fullness_cp`.
> These are then crossed to give `flags_fullness_cross`.
#### Loop stack
The [loop stack]({{< relref ".#loop-stack" >}}) is accessed by executing `LOOP` and `LOOPI` instructions.
Events concerning the start of loops are tracked at those instructions, but we can't track things like loop completion there.
> Coverage for these points is tracked with cover properties in the `otbn_loop_if` interface.
We expect to:
- Complete a loop.
Tracked as `LoopEnd_C`.
- Complete the last loop on the stack (emptying it) after the stack has been full.
Tracked as `FullToEmpty_C`.
- Complete a loop with one instruction and an iteration count of one.
Tracked as `ShortestLoop_C`.
- Complete a loop with the maximal number of iterations.
Obviously, this isn't a reasonable thing to do for real in a test (there's a 32-bit iteration counter!).
The testbench will have to force a signal to skip past some iterations.
Tracked as `MaxIterLoop_C`.
- Run through a "badly nested" loop, where the body contains the final instruction from an outer loop (checking that we don't wrongly skip back).
Tracked as `BadNestingEnd_C` and `BadNestingMiddle_C`.
- Jump into a loop body from outside.
Tracked as `JumpIntoLoop_C`.
- Jump/branch to the final instruction of a loop
Tracked as `JumpToLoopEnd_C`.
#### Flags
Each flag in each flag group should be set to one from zero by some instruction.
Similarly, each flag in each flag group should be cleared to zero from one by some instruction.
> These events are tracked in `flag_write_cg`.
> This is called from `on_insn()` once for each flag group that is being written.
> The covergroup contains eight coverpoints (for each flag set and cleared).
> These are then crossed with the flag group.
### Instruction-based coverage
As a processor, much of OTBN's coverage points are described in terms of instructions being executed.
Because OTBN doesn't have a complicated multi-stage pipeline or any real exception handling, we don't track much temporal information (such as sequences of instructions).
As well as instruction-specific coverage points detailed below, we include a requirement that each instruction is executed at least once.
For any instruction with one or more immediate fields, we require "toggle coverage" for those fields.
That is, we expect to see execution with each bit of each immediate field being zero and one.
We also expect to see each field with values `'0` and `'1` (all zeros and all ones).
If the field is treated as a signed number, we also expect to see it with the extremal values for its range (just the MSB set, for the most negative value; all but the MSB set, for the most positive value).
> The code to track this is split by encoding schema in `otbn_env_cov`.
> Each instruction listed below will specify its encoding schema.
> Each encoding schema then has its own covergroup.
> Rather than tracking toggle coverage as described above, we just track extremal values in a coverpoint.
> This also implies toggle coverage for both signed and unsigned fields.
> For unsigned fields of width `N`, the extremal values are `0` and `(1 << N) - 1`, represented by the bits `'0` and `'1` respectively.
> For signed fields of width `N+1`, the extremal values are `-(1 << N)` and `(1 << N) - 1`.
> These are represented by `{1'b1, {N{1'b0}}}` and `{1'b0, {N{1'b1}}}`: again, these toggle all the bits.
> For example, `beq` uses the `B` schema, which then maps to the `enc_b_cg` covergroup.
> This encoding schema's `OFF` field is tracked with the `off_cp` coverpoint.
> Finally, the relevant cross is called `off_cross`.
For any instruction that reads from or writes to a GPR, we expect to see that operand equal to `x0`, `x1` and an arbitrary register in the range `x2 .. x31`.
We don't have any particular coverage requirements for WDRs (since all of them work essentially the same).
> As for immediates, the code to track this is split by encoding schema in `otbn_env_cov`.
> Each register field gets a coverpoint with the same name, defined with the `DEF_GPR_CP` helper macro.
> If the encoding schema has more than one instruction, the coverpoint is then crossed with the mnemonic, using the `DEF_MNEM_CROSS` helper macro.
> For example, `add` is in the `enc_bnr_cg` covergroup.
> This encoding schema's `GRD` field is tracked with the `grd_cp` coverpoint.
> Finally, the relevant cross is called `grd_cross`.
For any source GPR or WDR, we require "toggle coverage" for its value.
For example, `ADD` reads from its `grs1` operand.
We want to see each of the 32 bits of that operand set and unset (giving 64 coverage points).
Similarly, `BN.ADD` reads from its `wrs1` operand.
We want to see each of the 256 bits of that operand set and unset (giving 512 coverage points).
> Again, the code to track this is split by encoding schema in `otbn_env_cov`.
> The trace interface takes a copy of GPR and WDR read data.
> The relevant register read data are then passed to the encoding schema's covergroup in the `on_insn` method.
> To avoid extremely repetitive code, the actual coverpoints and crosses are defined with the help of macros.
> The coverpoints are named with the base-2 expansion of the bit in question.
> For example, the cross in the `enc_bnr_cg` that tracks whether we've seen both values of bit 12 for the `grs1` operand is called `grs1_01100_cross` (since 12 is `5'b01100`).
If an instruction can generate flag changes, we expect to see each flag that the instruction can change being both set and cleared by the instruction.
This needn't be crossed with the two flag groups (that's tracked separately in the "Flags" block above).
For example, `BN.ADD` can write to each of the flags `C`, `M`, `L` and `Z`.
This paragraph implies eight coverage points (four flags times two values) for that instruction.
> Again, the code to track this is split by encoding schema in `otbn_env_cov`.
> The trace interface takes a copy of flag write data.
> It doesn't bother storing the flag write flags, since these are implied by the instruction anyway.
> There is a coverage coverpoint tracking both values for each of the flags that can be written.
> This is then crossed with the instruction mnemonic.
> For example, the coverpoint for the C flag (bit zero) in the `bnaf` encoding used by `BN.ADD` is called `flags_00_cp`.
> Some instructions only write the `M`, `L` and `Z` flags.
> These are found in the `bna`, `bnan`, `bnaqs` and `bnaqw` encoding groups.
> For these instructions, we only track bits `1`, `2` and `3` of the flags structure.
#### ADD
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_addsub_cg` (also used for `SUB`).
- Cross the three possible signs (negative, zero, positive) for each input operand (giving 9 points).
Tracked as `sign_a_sign_b_cross`.
#### ADDI
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_addi_cg`.
- Cross the three possible signs (negative, zero, positive) for each input operand (giving 9 points).
Tracked as `sign_cross`.
#### LUI
This instruction uses the `U` encoding schema, with covergroup `enc_u_cg`.
There are no further coverage points.
#### SUB
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_addsub_cg`.
- Cross the three possible signs (negative, zero, positive) for each input operand (giving 9 points).
Tracked as `sign_a_sign_b_cross`.
#### SLL
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_sll_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the top bit set.
Tracked as `shift15_cp`.
#### SLLI
This instruction uses the `Is` encoding schema, with covergroup `enc_is_cg`.
The instruction-specific covergroup is `insn_slli_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the top bit set.
Tracked as `shift15_cp`.
#### SRL
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_srl_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the bottom bit set.
Tracked as `shift15_cp`.
Note that this point also checks that we're performing a logical, rather than arithmetic, right shift.
#### SRLI
This instruction uses the `Is` encoding schema, with covergroup `enc_is_cg`.
The instruction-specific covergroup is `insn_srli_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the bottom bit set.
Tracked as `shift15_cp`.
Note that this point also checks that we're performing a logical, rather than arithmetic, right shift.
#### SRA
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_sra_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the bottom bit set.
Tracked as `shift15_cp`.
Note that this point also checks that we're performing an arithmetic, rather than logical, right shift.
#### SRAI
This instruction uses the `Is` encoding schema, with covergroup `enc_is_cg`.
The instruction-specific covergroup is `insn_srai_cg`.
- A shift of a nonzero value by zero.
Tracked as `nz_by_z_cp`.
- A shift of a value by `0x1f` which leaves the bottom bit set.
Tracked as `shift15_cp`.
Note that this point also checks that we're performing an arithmetic, rather than logical, right shift.
#### AND
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just AND'ing things with zero)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### ANDI
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just AND'ing things with zero)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### OR
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just OR'ing things with `'1`)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### ORI
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just OR'ing things with `'1`)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### XOR
This instruction uses the `R` encoding schema, with covergroup `enc_r_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just XOR'ing things with zero)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### XORI
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_log_binop_cg` (shared with other logical binary operations).
- Toggle coverage of the output result, not to `x0` (to ensure we're not just XOR'ing things with zero)
Tracked as `write_data_XXXXX_cross`, where `XXXXX` is the base-2 representation of the bit being checked.
#### LW
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_xw_cg` (shared with `SW`).
- Load from a valid address, where `<grs1>` is above the top of memory and a negative `<offset>` brings the load address in range.
Tracked as `oob_base_neg_off_cross`.
- Load from a valid address, where `<grs1>` is negative and a positive `<offset>` brings the load address in range.
Tracked as `neg_base_pos_off_cross`.
- Load from address zero.
Tracked as `addr0_cross`.
- Load from the top word of memory
Tracked as `top_addr_cross`.
- Load from an invalid address (aligned but above the top of memory)
Tracked as `oob_addr_cross`.
- Load from a "barely invalid" address (aligned but overlapping the top of memory)
Tracked as `barely_oob_addr_cross`.
- Misaligned address tracking.
Track loads from addresses that are in range for the size of the memory.
Cross the different values modulo 4 for `grs1` and `offset`.
Tracked as `align_cross`.
#### SW
This instruction uses the `I` encoding schema, with covergroup `enc_s_cg`.
The instruction-specific covergroup is `insn_xw_cg` (shared with `LW`).
- Store to a valid address, where `<grs1>` is above the top of memory and a negative `<offset>` brings the load address in range.
Tracked as `oob_base_neg_off_cross`.
- Store to a valid address, where `<grs1>` is negative and a positive `<offset>` brings the load address in range.
Tracked as `neg_base_pos_off_cross`.
- Store to address zero
Tracked as `addr0_cross`.
- Store to the top word of memory
Tracked as `top_addr_cross`.
- Store to an invalid address (aligned but above the top of memory)
Tracked as `oob_addr_cross`.
- Store to a "barely invalid" address (aligned but overlapping the top of memory)
Tracked as `barely_oob_addr_cross`.
- Misaligned address tracking.
Track stores from addresses that are in range for the size of the memory.
Cross the different values modulo 4 for `grs1` and `offset`.
Tracked as `align_cross`.
#### BEQ
This instruction uses the `B` encoding schema, with covergroup `enc_b_cg`.
The instruction-specific covergroup is `insn_bxx_cg` (shared with `BNE`).
All points should be crossed with branch taken / branch not taken.
- See each branch direction (forwards, backwards, current address).
Tracked as `eq_dir_cross`.
- Branch to a misaligned address (offset not a multiple of 4)
Each offset alignment is tracked in `eq_offset_align_cross`.
- Branch forwards to an invalid address, above the top of memory
Tracked as `eq_oob_cross`.
- Branch backwards to an invalid address (wrapping past zero)
Tracked as `eq_neg_cross`.
- Branch instruction at end of a loop.
Tracked as `eq_at_loop_end_cross` (which also crosses with whether the branch was taken or not).
The "branch to current address" item is problematic if we want to take the branch.
Probably we need some tests with short timeouts to handle this properly.
#### BNE
This instruction uses the `B` encoding schema, with covergroup `enc_b_cg`.
The instruction-specific covergroup is `insn_bxx_cg` (shared with `BEQ`).
All points should be crossed with branch taken / branch not taken.
- See each branch direction (forwards, backwards, current address).
Tracked as `eq_dir_cross`.
- Branch to a misaligned address (offset not a multiple of 4)
Each offset alignment is tracked in `eq_offset_align_cross`.
- Branch forwards to an invalid address, above the top of memory
Tracked as `eq_oob_cross`.
- Branch backwards to an invalid address (wrapping past zero)
Tracked as `eq_neg_cross`.
- Branch instruction at end of a loop.
Tracked as `eq_at_loop_end_cross` (which also crosses with whether the branch was taken or not).
The "branch to current address" item is problematic if we want to take the branch.
Probably we need some tests with short timeouts to handle this properly.
#### JAL
This instruction uses the `J` encoding schema, with covergroup `enc_j_cg`.
The instruction-specific covergroup is `insn_jal_cg`.
- See each jump direction (forwards, backwards, current address).
Tracked as `dir_cp`.
- Jump to a misaligned address (offset not a multiple of 4)
Offset alignments are tracked in `offset_align_cp`.
- Jump forwards to an invalid address, above the top of memory
Tracked as `oob_cp`.
- Jump backwards to an invalid address (wrapping past zero)
Tracked as `neg_cp`.
- Jump when the current PC is the top word in IMEM.
Tracked as `from_top_cp`.
- Jump instruction at end of a loop.
Tracked as `at_loop_end_cp`.
Note that the "jump to current address" item won't be a problem to test since it will quickly overflow the call stack.
#### JALR
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_jalr_cg`.
- See each jump offset (forwards, backwards, zero).
Tracked as `off_dir_cp`.
- Jump with a misaligned base address which `<offset>` aligns.
Pairs of base address / offset alignments tracked in `align_cross`.
- Jump with a large base address which wraps to a valid address by adding a positive `<offset>`.
Tracked as `pos_wrap_cp`.
- Jump with a base address just above top of IMEM but with a negative `<offset>` to give a valid target.
Tracked as `sub_cp`.
- Jump with a negative offset, wrapping to give an invalid target.
Tracked as `neg_wrap_cp`.
- Jump to an aligned address above top of IMEM.
Tracked as `oob_cp`.
- Jump to current address.
Tracked as `self_cp`.
- Jump when the current PC is the top word in IMEM.
Tracked as `from_top_cp`.
- Jump instruction at end of a loop.
Tracked as `at_loop_end_cp`.
Note that the "jump to current address" item won't be a problem to test since it will quickly over- or underflow the call stack, provided `<grd>` and `<grs1>` aren't both `x1`.
#### CSRRS
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_csrrs_cg`.
- Write with a non-zero `bits_to_set` to each valid CSR.
- Write to an invalid CSR.
These points are tracked with `csr_cross` in `insn_csrrs_cg`.
It crosses `csr_cp` (which tracks each valid CSR, plus an invalid CSR) with `bits_to_set_cp` (which tracks whether `bits_to_set` is nonzero.
#### CSRRW
This instruction uses the `I` encoding schema, with covergroup `enc_i_cg`.
The instruction-specific covergroup is `insn_csrrw_cg`.
- Write to every valid CSR with a `<grd>` other than `x0`.
- Write to every valid CSR with `<grd>` equal to `x0`.
- Write to an invalid CSR.
These points are tracked with `csr_cross` in `insn_csrrw_cg`.
It crosses `csr_cp` (which tracks each valid CSR, plus an invalid CSR) with `grd_cp_to_set_cp` (which tracks whether `grd` is equal to `x0`.
#### ECALL
This instruction uses the `I` encoding schema, but with every field set to a fixed value.
Encoding-level coverpoints are tracked in covergroup `enc_ecall_cg`.
No special coverage points for this instruction.
#### LOOP
This instruction uses the `loop` encoding schema, with covergroup `enc_loop_cg`.
The instruction-specific covergroup is `insn_loop_cg`.
- Loop with a zero iteration count (causing an error)
Tracked as the `'0` bin of `iterations_cp`.
- Loop with a count of `'1` (the maximal value)
Tracked as the `'1` bin of `iterations_cp`.
- Loop when the loop end address would be above the top of memory.
Tracked as `oob_end_addr_cp`.
- Loop when the loop stack is full, causing an overflow.
Tracked as `loop_stack_fullness_cp`.
- Loop at the end of a loop.
Tracked as `at_loop_end_cp`.
- Duplicate loop end address, matching top of stack
Tracked as `duplicate_loop_end_cp`.
#### LOOPI
This instruction uses the `loopi` encoding schema, with covergroup `enc_loopi_cg`.
The instruction-specific covergroup is `insn_loopi_cg`.
- Loop with a zero iteration count (causing an error)
This is tracked in `enc_loopi_cg` with the `'0` bin of `iterations_cp`.
- Loop when the loop end address would be above the top of memory.
Tracked as `oob_end_addr_cp`.
- Loop when the loop stack is full, causing an overflow.
Tracked as `loop_stack_fullness_cp`.
- Loop at the end of a loop.
Tracked as `at_loop_end_cp`.
- Duplicate loop end address, matching top of stack
Tracked as `duplicate_loop_end_cp`.
#### BN.ADD
This instruction uses the `bnaf` encoding schema, with covergroup `enc_bnaf_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero.
This is tracked in `enc_bnaf_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
This is tracked in `enc_bnaf_cg` as `srl_cross`.
#### BN.ADDC
This instruction uses the `bnaf` encoding schema, with covergroup `enc_bnaf_cg`.
The instruction-specific covergroup is `insn_bn_addc_cg`.
- Extremal values of shift for both directions where the shifted value is nonzero
This is tracked in `enc_bnaf_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
This is tracked in `enc_bnaf_cg` as `srl_cross`.
- Execute with both values of the carry flag for both flag groups (to make sure things are wired through properly)
Tracked as `carry_cross`.
#### BN.ADDI
This instruction uses the `bnai` encoding schema, with covergroup `enc_bnai_cg`.
There is no instruction-specific covergroup.
No special coverage.
#### BN.ADDM
This instruction uses the `bnam` encoding schema, with covergroup `enc_bnam_cg`.
The instruction-specific covergroup is `insn_bn_addm_cg`.
- Execute with the two extreme values of `MOD` (zero and all ones)
Tracked as `mod_cp`.
- Don't perform a subtraction (because the sum is less than `MOD`) when `MOD` is nonzero.
Tracked as `sum_lt_cp`.
- A calculation where the sum exactly equals a nonzero `MOD`
Tracked as `sum_eq_cp`.
- A calculation where the sum is greater than a nonzero `MOD`.
Tracked as `sum_gt_cp`.
- Perform a subtraction where the sum is at least twice a nonzero value of `MOD`.
Tracked as `sum_gt2_cp`.
- A calculation where the intermediate sum is greater than `2^256-1`, crossed with whether the subtraction of `MOD` results in a value that will wrap.
Tracked as `overflow_wrap_cross`.
#### BN.MULQACC
This instruction uses the `bnaq` encoding schema, with covergroup `enc_bnaq_cg`.
The instruction-specific covergroup is `insn_bn_mulqaccx_cg` (shared with the other `BN.MULQACC*` instructions).
- Cross `wrs1_qwsel` with `wrs2_qwsel` to make sure they are applied to the right inputs
This is tracked in `enc_bnaq_cg` as `qwsel_cross`.
- See the accumulator overflow
This is tracked in `insn_bnmulqaccx_cg` as `overflow_cross`.
#### BN.MULQACC.WO
This instruction uses the `bnaq` encoding schema, with an extra field not present in `bn.mulqacc`.
Encoding-level coverpoints are tracked in covergroup `enc_bnaqw_cg`.
The instruction-specific covergroup is `insn_bn_mulqaccx_cg` (shared with the other `BN.MULQACC*` instructions).
- Cross `wrs1_qwsel` with `wrs2_qwsel` to make sure they are applied to the right inputs
This is tracked in `enc_bnaqw_cg` as `qwsel_cross`.
- See the accumulator overflow
This is tracked in `insn_bnmulqaccx_cg` as `overflow_cross`.
#### BN.MULQACC.SO
This instruction uses the `bnaq` encoding schema, with an extra field not present in `bn.mulqacc`.
Encoding-level coverpoints are tracked in covergroup `enc_bnaqs_cg`.
The instruction-specific covergroup is `insn_bn_mulqaccx_cg` (shared with the other `BN.MULQACC*` instructions).
- Cross `wrs1_qwsel` with `wrs2_qwsel` to make sure they are applied to the right inputs
This is tracked in `enc_bnaqs_cg` as `qwsel_cross`.
- See the accumulator overflow
This is tracked in `insn_bnmulqaccx_cg` as `overflow_cross`.
- Cross the generic flag updates with `wrd_hwsel`, since the flag changes are different in the two modes.
This is tracked in `enc_bnaqs_cg` as `flags_01_cross`, `flags_10_cross` and `flags_11_cross`.
#### BN.SUB
This instruction uses the `bnaf` encoding schema, with covergroup `enc_bnaf_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
This is tracked in `enc_bnaf_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
This is tracked in `enc_bnaf_cg` as `srl_cross`.
#### BN.SUBB
This instruction uses the `bnaf` encoding schema, with covergroup `enc_bnaf_cg`.
The instruction-specific covergroup is `insn_bn_subcmpb_cg`.
- Extremal values of shift for both directions where the shifted value is nonzero
This is tracked in `enc_bnaf_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
This is tracked in `enc_bnaf_cg` as `srl_cross`.
- Execute with both values of the carry flag for both flag groups (to make sure things are wired through properly)
Tracked as `fg_carry_flag_cross`.
#### BN.SUBI
This instruction uses the `bnai` encoding schema, with covergroup `enc_bnai_cg`.
There is no instruction-specific covergroup.
No special coverage.
#### BN.SUBM
This instruction uses the `bnam` encoding schema, with covergroup `enc_bnam_cg`.
The instruction-specific covergroup is `insn_bn_subm_cg`.
- Execute with the two extreme values of `MOD` (zero and all ones)
Tracked as `mod_cp`.
- A non-negative intermediate result with a nonzero `MOD` (so `MOD` is not added).
Tracked as `diff_nonneg_cp`.
- An intermediate result that exactly equals a nonzero `-MOD`.
Tracked as `diff_minus_mod_cp`.
- A negative intermediate result with a nonzero `MOD`, so `MOD` is added and the result is positive.
Tracked as `diff_neg_cp`.
- A very negative intermediate result with a nonzero `MOD` (so `MOD` is added, but the top bit is still set)
Tracked as `diff_neg2_cp`.
#### BN.AND
This instruction uses the `bna` encoding schema, with covergroup `enc_bna_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bna_cg` as `st_sb_nz_shifted_cross`.
- Toggle coverage of the output result (to ensure we're not just AND'ing things with zero)
Tracked in `enc_bna_cg` as `wrd_XXXXXXXX_cross`, where `XXXXXXXX` is the base-2 representation of the bit being checked.
#### BN.OR
This instruction uses the `bna` encoding schema, with covergroup `enc_bna_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bna_cg` as `st_sb_nz_shifted_cross`.
- Toggle coverage of the output result (to ensure we're not just OR'ing things with zero)
Tracked in `enc_bna_cg` as `wrd_XXXXXXXX_cross`, where `XXXXXXXX` is the base-2 representation of the bit being checked.
#### BN.NOT
This instruction uses the `bnan` encoding schema, with covergroup `enc_bnan_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bnan_cg` as `st_sb_nz_shifted_cross`.
- Toggle coverage of the output result (to ensure nothing gets clamped)
Tracked in `enc_bnan_cg` as `wrd_XXXXXXXX_cp`, where `XXXXXXXX` is the base-2 representation of the bit being checked.
#### BN.XOR
This instruction uses the `bna` encoding schema, with covergroup `enc_bna_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bna_cg` as `st_sb_nz_shifted_cross`.
- Toggle coverage of the output result (to ensure we're not just XOR'ing things with zero)
Tracked in `enc_bna_cg` as `wrd_XXXXXXXX_cross`, where `XXXXXXXX` is the base-2 representation of the bit being checked.
#### BN.RSHI
This instruction uses the `bnr` encoding schema, with covergroup `enc_bnr_cg`.
There is no instruction-specific covergroup.
No special coverage.
#### BN.SEL
This instruction uses the `bns` encoding schema, with covergroup `enc_bns_cg`.
There is no instruction-specific covergroup.
- Cross flag group, flag and flag value (2 times 4 times 2 points)
Tracked in `enc_bns_cg` as `flag_cross`.
#### BN.CMP
This instruction uses the `bnc` encoding schema, with covergroup `enc_bnc_cg`.
There is no instruction-specific covergroup.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bnc_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
Tracked in `enc_bnc_cg` as `srl_cross`.
#### BN.CMPB
This instruction uses the `bnc` encoding schema, with covergroup `enc_bnc_cg`.
The instruction-specific covergroup is `insn_bn_subcmpb_cg`.
- Extremal values of shift for both directions where the shifted value is nonzero
Tracked in `enc_bnc_cg` as `st_sb_nz_shifted_cross`.
- A nonzero right shift with a value in `wrs2` whose top bit is set
Tracked in `enc_bnc_cg` as `srl_cross`.
- Execute with both values of the carry flag for both flag groups (to make sure things are wired through properly)
Tracked as `fg_carry_flag_cross`.
#### BN.LID
This instruction uses the `bnxid` encoding schema, with covergroup `enc_bnxid_cg`.
The instruction-specific covergroup is `insn_bn_xid_cg` (shared with `BN.SID`).
- Load from a valid address, where `grs1` is above the top of memory and a negative `offset` brings the load address in range.
Tracked as `oob_base_neg_off_cross`.
- Load from a valid address, where `grs1` is negative and a positive `offset` brings the load address in range.
Tracked as `neg_base_pos_off_cross`.
- Load from address zero
Tracked as `addr0_cross`.
- Load from the top word of memory
Tracked as `top_addr_cross`.
- Load from an invalid address (aligned but above the top of memory)
Tracked as `oob_addr_cross`.
- Misaligned address tracking.
Track loads from addresses that are in range for the size of the memory.
Crossing the possible misalignments for operand_a and offset would give a big cross (`32^2 = 1024`).
Instead, we split those alignments into 3 bins: 0, 1-30 and 31.
Crossing them gives 9 combinations, tracked as `part_align_cross`.
We also track all possible alignments of the sum as `addr_align_cross`.
- See an invalid instruction with both increments specified
Tracked in `enc_bnxid_cg` as a bin of `incd_inc1_cross`.
- See `grd` greater than 31, giving an illegal instruction error
Tracked as `bigb_cross`.
- Cross the three types of GPR for `grd` with `grd_inc`
Tracked in `enc_bnxid_cg` as `grx_incd_cross`.
- Cross the three types of GPR for `grs1` with `grd_inc`
Tracked in `enc_bnxid_cg` as `grs1_inc1_cross`.
#### BN.SID
This instruction uses the `bnxid` encoding schema, with covergroup `enc_bnxid_cg`.
The instruction-specific covergroup is `insn_bn_xid_cg` (shared with `BN.LID`).
- Store to a valid address, where `grs1` is above the top of memory and a negative `offset` brings the load address in range.
Tracked as `oob_base_neg_off_cross`.
- Store to a valid address, where `grs1` is negative and a positive `offset` brings the load address in range.
Tracked as `neg_base_pos_off_cross`.
- Store to address zero
Tracked as `addr0_cross`.
- Store to the top word of memory
Tracked as `top_addr_cross`.
- Store to an invalid address (aligned but above the top of memory)
Tracked as `oob_addr_cross`.
- Misaligned address tracking.
Track stores to addresses that are in range for the size of the memory.
Crossing the possible misalignments for operand_a and offset would give a big cross (`32^2 = 1024`).
Instead, we split those alignments into 3 bins: 0, 1-30 and 31.
Crossing them gives 9 combinations, tracked as `part_align_cross`.
We also track all possible alignments of the sum as `addr_align_cross`.
- See an invalid instruction with both increments specified
Tracked in `enc_bnxid_cg` as a bin of `incd_inc1_cross`.
- See `grd` greater than 31, giving an illegal instruction error
Tracked as `bigb_cross`.
- Cross the three types of GPR for `grs2` with `grs2_inc`
Tracked in `enc_bnxid_cg` as `grx_incd_cross`.
- Cross the three types of GPR for `grs1` with `grd_inc`
Tracked in `enc_bnxid_cg` as `grs1_inc1_cross`.
#### BN.MOV
This instruction uses the `bnmov` encoding schema, with covergroup `enc_bnmov_cg`.
There is no instruction-specific covergroup.
No special coverage otherwise.
#### BN.MOVR
This instruction uses the `bnmovr` encoding schema, with covergroup `enc_bnmovr_cg`.
There is no instruction-specific covergroup.
- See an invalid instruction with both increments specified
Tracked in `enc_bnmovr_cg` as a bin of `incd_inc1_cross`.
- Since MOVR signals an error if either of its source registers has a value greater than 31, cross whether the input register value at `grd` is greater than 31 with whether the register value at `grs` is greater than 31
Tracked in `enc_bnmovr_cg` as `big_gpr_cross`.
#### BN.WSRR
This instruction uses the `bnwcsr` encoding schema, with covergroup `enc_wcsr_cg`.
There is no instruction-specific covergroup.
- Read from each valid WSR
- Read from an invalid WSR
These points are tracked with `wsr_cross` in `enc_wcsr_cg`.
#### BN.WSRW
This instruction uses the `bnwcsr` encoding schema, with covergroup `enc_wcsr_cg`.
There is no instruction-specific covergroup.
- Write to each valid WSR, including read-only WSRs.
- Write to an invalid WSR
These points are tracked with `wsr_cross` in `enc_wcsr_cg`.
## Self-checking strategy
### Scoreboard
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.
### Assertions
Core TLUL protocol assertions are checked by binding the [TL-UL protocol checker]({{< relref "hw/ip/tlul/doc/TlulProtocolChecker.md" >}}) 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`]({{< relref "hw/dv/tools/README.md" >}}).
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:
```console
$ util/dvsim/dvsim.py hw/ip/otbn/dv/uvm/otbn_sim_cfg.hjson -i otbn_smoke
```
## DV plan
{{< incGenFromIpDesc "../../data/otbn_testplan.hjson" "testplan" >}}