Skip to content

Latest commit

 

History

History
89 lines (45 loc) · 5.65 KB

procedure.md

File metadata and controls

89 lines (45 loc) · 5.65 KB

riscv-formal Verification Procedure

The following formal test are performed to verify ISA compliance of RISC-V processors with riscv-formal. Depending on aspects like the strength of safety properties present in the core, the overall complexity of the core, and the verification requirements for the given application, the following tests might be set up as bounded model checks or as unbounded verification tasks.

For most cores the easiest approach is to create a wrapper HDL module and a genchecks.cfg file and use the genchecks.py scripts to create the formal checks. See cores/picorv32/ for an example implementation. The checks generated by genchecks.py are bounded model checks.

Standard Checks

The following checks are managed by genchecks.py and can be implemented using the standard RVFI wrapper interface.

Instruction Checks

The majority of formal checks needed to verify a core with riscv-formal are instruction checks (one per RVFI channel and RISC-V instruction supported by the core).

Instruction checks test if the instruction (rvfi_insn) matches the state transistion described by the other RVFI signals.

PC Checks

There are two PC checks: pc_fwd and pc_bwd. Both of them are run for each RVFI channel.

The pc_fwd check assumes that the core retires an instruction at the end of the bounded model check, and that the previous instruction in the program (rvfi_order-1) was retired earlier. It then tests if rvfi_pc_wdata of the previous instruction matches rvfi_pc_rdata of the next instruction.

pc_bwd is like pc_fwd but for pairs of instructions that have been executed out of order: The check assumes that the core retires an instruction at the end of the bounded model check, and that the next instruction in the program (rvfi_order+1) was retired earlier. It then tests if rvfi_pc_wdata of the previous instruction matches rvfi_pc_rdata of the next instruction.

Register Checks

This checks if writes to and reads from the register file are consistent with each other, i.e. if the value written to a register matches the value read from the register file by a later instructions.

This check assumes that the last instruction at the end of the bounded model check, reads a register. It then checks that the value read is consistent with the matching write to the same register by an earlier instruction.

Causality

The core may retire instructions out-of-order as long as causality is preserved. (This means a register write must be retired before the register reads that depend on it.) This check tests if the instruction stream is causal with respect to registers.

Liveness

This check makes sure that the core never freezes (unless an instruction with rvfi_halt asserted is retired): This check assumes that an instruction is retired at a configurable trigger point in the middle of the bounded model check. It then checks that the next instruction (rvfi_order+1) is also retired at some point during the span of the bounded model check.

It might be neccessary to add some bounded fairness constraints to the design for this check to succeed.

Uniqueness

This check makes sure that no two instructions with the same rvfi_order are retired by the core.

Other Checks

The following checks are not yet managed by genchecks.py and can not be implemented using the standard RVFI wrapper interface. Some of them may be integrated with genchecks.py in the future.

Instruction Memcheck

This check adds a memory abstraction that only emulates a single word of memory (at an unconstrained address). This memory word is read-only and has an unconstrained value. The check makes sure that instructions fetched from this memory word are handled correctly and that the data from that memory word makes its way into rvfi_insn unharmed.

See imemcheck.sv in cores/picorv32/ for an example implementation.

Data Memcheck

This check adds a memory abstraction that only emulates a single word of memory (at an unconstrained address). The memory word is read/write. The check tests if writes to and reads from the memory location (as reported via RVFI) are consistent.

See dmemcheck.sv in cores/picorv32/ for one possible implementation of this test.

Checking for equivalence of core with and without RVFI

An equivalence check of the core with and without RVFI (with respect to the non-RVFI outputs) is performed. This proves that the verification results for the core with enabled RVFI also prove that the (non-RVFI) production core is correct without extra burden on the core designer to isolate the RVFI implementation from the rest of the core.

See equiv.sh in cores/picorv32/ for an example implementation.

Complete

An additional check to make sure the core can not (without trap) retire any instructions that are not covered by the riscv-formal instruction checks.

See complete.sv in cores/picorv32/ for one possible implementation of this test.

Cover

A formal check using cover() SystemVerilog statements for various interesting RVFI events or sequences of events. The purpose of this formal check is to collect some data about the required bounds to reach certain states to set the bounds for the other bounded model checks.

See cover.sv in cores/picorv32/ for one possible implementation of this test.

Verification of riscv-formal models against spike models

The checks in tests/spike/ use the Yosys SimpleC back-end and CBMC to check the riscv-formal models and the C instruction models from spike for equivalence.