title: Introduction to Formal Verification of Digital Circuits author: Cesar Strauss theme: Copenhagen

date: FOSDEM 2024

Why Formal Verification?

  • A tool for finding bugs
  • Complementary to simulation
  • Helps finding corner cases
  • ... triggered by specific sequences of events

Comparison with traditional debugging concepts

formal traditional
Cover Simulation
Bounded Model Check Unit test
k-Induction Test fixture?
Exhaustive search random test cases
synthesizable test-bench can be procedural
"assume" inputs test vectors
"assert" outputs "assert" outputs

Workflow

  • HDL: includes assertions
  • SBY: work plan, drives the process
  • Yosys: synthesizes to logic functions:

    • state $s$: contents of all registers and inputs
    • initial predicate: $I(s)$
    • transition relation $T(s_1, s_2)$
    • assertions: $P(s)$
  • yosys-smtbmc: proves correctness or outputs a trace

    • exhaustive search for a path from the initial state to a bad state
    • if not found, the design is correct
    • if found, output an error trace

Unbounded inductive proof

  • bad trace:

$I(s_0) P(s_0) \wedge T(s_0,s_1)P(s_1) \wedge\dots\wedge T(s_{k-1},s_k) \overline{P(s_k)}$

  • k $\leftarrow$ 0
  • base case: no path from initial state leads to a bad state in k steps
  • if base case fails, report the bad trace
  • inductive case: no path ending in a bad state can be reached in k+1 steps
  • if inductive case fails, $k \leftarrow k + 1$ and repeat
  • otherwise, proof is complete, circuit is safe.

Single register with feedback

Registered output with internal state

Registered output with enable

Flip-flop with input

Verifying a flip-flop

Complete flip-flop with input and enable

Code for simple register with feedback

module simple(input clk);

reg r = 0;

always @(posedge clk)
    r <= r;

`ifdef FORMAL
always @*
    assert(!r);
`endif

SBY drive file

[options]
mode prove
depth 1

[engines]
smtbmc yices

[script]
read_verilog -formal simple.v
prep -top simple

[files]
simple.v

Output (simplified)

$ sby simple.sby

induction: Trying induction in step 1..
induction: Trying induction in step 0..
induction: Temporal induction successful.
basecase: Checking assumptions in step 0..
basecase: Checking assertions in step 0..
basecase: Status: passed
summary: engine_0 (smtbmc yices) returned pass
 for induction
summary: engine_0 (smtbmc yices) returned pass
 for basecase
summary: successful proof by k-induction.
DONE (PASS, rc=0)

Flip flop with enable (1/2)

from nmigen.asserts import Assert, Assume, Past
from nmutil.formaltest import FHDLTestCase
from nmigen import Signal, Module
import unittest

class Formal(FHDLTestCase):
    def test_enable(self):
        m = Module()
        r1 = Signal()
        r2 = Signal()
        s = Signal()
        en = Signal()
        m.d.sync += [r2.eq(r1), r1.eq(r2)]
        with m.If(en):
            m.d.sync += s.eq(r1 & r2)

Flip flop with enable (2/2)

        m.d.comb += Assert(~s)
        m.d.sync += Assume(Past(en) | en)
        m.d.comb += Assert(~r1 & ~r2)
        self.assertFormal(m, mode="prove", depth=5)




if __name__ == '__main__':
    unittest.main()

Induction failure example

summary: engine_0 returned pass for basecase
summary: engine_0 returned FAIL for induction
DONE (UNKNOWN, rc=4)

Verifying memories with a "victim address"

Verifying streams with transaction counters

Dynamic SIMD

exp-a    : ....0....0....0.... 1x 32-bit
exp-a    : ....0....0....1.... 1x 24-bit plus 1x 8-bit
exp-a    : ....0....1....0.... 2x 16-bit
...
...
exp-a    : ....1....1....0.... 2x 8-bit, 1x 16-bit
exp-a    : ....1....1....1.... 4x 8-bit

#

\centering {\Huge

The End

Thank you

Questions?

}

  • Discussion: http://lists.libre-soc.org
  • Libera IRC #libre-soc
  • http://libre-soc.org/
  • https://libre-soc.org/resources/
  • http://nlnet.nl/entrust
  • https://libre-soc.org/nlnet_2022_ongoing/
  • https://libre-soc.org/nlnet/#faq
  • https://git.libre-soc.org/?p=soc.git;a=tree;f=src/soc/experiment/formal;hb=HEAD