The App Map

Learn what a formal app is, which apps need a hand-written rule and which generate their own, and which app catches which bug.

Lesson
F5.1
Time
10 min
Runs on
SymbiYosys
Check
4 questions

A team bought a formal licence and was asked to "run formal on the SoC". They opened property verification on the whole top level, wrote nothing for a month, and reported that it did not converge. The connectivity and register checkers in the same licence needed a spreadsheet each and no property at all.

A swapped pad, caught by a rule someone had to think of
012clkfreerstfreepad_sel020freeuart_txfreespi_mosipad41wrong function on pad41SBY reports FAIL

Measured with smtbmc yices at depth 20: FAIL at reported step 2. F5.3 finds the same bug from one typed table row, with no property written by hand.

One Narrow Question per App

App: the narrow questionWhat you give itWhat it generates or findsBuilt by hand in
Automatic checks: can the code reach a state it implies is impossible?the RTL onlya cover per state; index, x and carry assertsF5.2
Connectivity: is pin A wired to pin B?a pin tableone equality per row, a cover per conditionF5.3
Registers: does each field behave as the map says?a register mapa fixed check set per access typeF5.4
X: can an unknown reach an output?the RTL onlyunknowns as free values, asserts on outputsF5.5
CDC/RDC: is each crossing synchronised, and safe to synchronise?the clock and reset domainsa crossing list, then protocol propertiesF5.6
Unreachability: can this coverage bin ever be hit?the unhit binsassert(!bin) under prove, or a traceF5.7
Information flow: can the secret reach this pin?a secret and its sinkstwo copies with equal public inputsF5.8
Post-silicon: how could the lab symptom happen?a symptom and the lab notesa cover under assumptionsF5.9
Equivalence: do two designs behave the same?two designsa miter of the twoF6

Commercial formal suites sell most of these rows as separate apps under their own names. Underneath, every row is assert, assume and cover around a generator.

A formal app is the three verbs (F1.3) packaged around one narrow question. Because the question is fixed, the tool can write the properties from a table, from the RTL itself or from a second design. The design can then be far larger than a hand-written proof could handle.

Each row of the map is built by hand in this module on the open engine, so you see exactly what the app generates and where it stops.

Who Writes the Rule

Who Writes the RuleApps split by whether a person writes the rule or a generator writes it from a table, the RTL or a second design. Arrows mean "is a special case of".
call: who calls whom
Who writes the ruleApps split by whether a person writes the rule or a generator writes it from a table, the RTL or a second design. Arrows mean "is a special case of". component Propertyverification (F1to F4) the general case: youwrite every rule component Generated from atable or the RTL automatic checks,connectivity,registers, X,unreachability component Generated from asecond design equivalence (F6) component A person namesthe rule CDC protocolproperties,information flow,post-silicon covers component Where a team withno formalengineer starts connectivity andregisters: aspreadsheet in, averdict out
Read the diagram as text
  • Property verification (F1 to F4): the general case: you write every rule
  • Generated from a table or the RTL: automatic checks, connectivity, registers, X, unreachability
  • Generated from a second design: equivalence (F6)
  • A person names the rule: CDC protocol properties, information flow, post-silicon covers
  • Where a team with no formal engineer starts: connectivity and registers: a spreadsheet in, a verdict out
  • Generated from a table or the RTL → Property verification (F1 to F4)
  • Generated from a second design → Property verification (F1 to F4)
  • A person names the rule → Property verification (F1 to F4)
  • Generated from a table or the RTL → Where a team with no formal engineer starts

The generated group needs no formal expertise to run, but someone still has to read every verdict against the spec.

The generated group is where a team without a formal specialist starts: connectivity and registers turn an existing spreadsheet into a verdict. The hand-written group needs someone who can state the rule: which crossing protocol, which secret and sink, which symptom.

The trace above shows the difference. Two hand-written rows found the swapped pad: FAIL at reported step 2. In F5.3 a generator writes those rows from the pin table, along with the other seventeen.

Which App Catches Which Bug

BugApp that catches itLesson
an FSM state that can never be enteredautomatic checks (or unreachability)F5.2
a W1C bit that behaves as read-writeregistersF5.4
two pad selects swappedconnectivityF5.3
an unreset flop that reaches an enableX-propagationF5.5
a binary pointer across clock domainsCDC, protocol propertyF5.6
a coverage bin no test ever hitsunreachabilityF5.7
a key bit on a debug portinformation flowF5.8
a deadlock seen once in the labpost-siliconF5.9
a FIFO that overflows under one sequenceno app: write the propertyF3.4
a request that is never grantedno app: livenessF3.7

The last two rows belong to no app on purpose. Most design-specific bugs still need a person to write the rule.

An app proves its generated properties and nothing else. A narrow, fixed property set means a wrong spec stays wrong, and a bug outside the question is invisible. Use the apps for what they are good at, then write the properties only you can write.

Asked in Interviews

QuestionAnswer
What are formal apps, and which are "automatic"?Packaged narrow questions. The automatic ones take a table, the RTL or a second design and generate their properties: automatic checks, connectivity, registers, unreachability, equivalence.
Which app would you run first on a new SoC?Connectivity and registers: no property to write, a spreadsheet already exists, and they pay back at once.
Do the automatic apps prove the design correct?No. They prove a fixed, narrow set of generated properties.

Try It

Two hand-written rows for pad41. A generator would write all nineteen from the pin table (F5.3).
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  reg pipe_in_q=0, rst_q=1;   // last cycle's pipe_in and rst
  always @(posedge clk) begin pipe_in_q <= pipe_in; rst_q <= rst; end
  // generated from the connection table
  always @(posedge clk) if (!rst) begin
    if (pad_sel == 2'd0) assert(pad41 == gpio_o[0]);
    if (pad_sel == 2'd1) assert(pad41 == uart_tx);
    if (pad_sel == 2'd2) assert(pad41 == spi_mosi);
  end
The whole task.
[options]
mode bmc
depth 20
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv
prep -top conn_top
[files]
design.sv
  1. Run it

    Run as it is: FAIL at reported step 2. Which select value does the trace use?

  2. Fix the mux

    Swap the two pad41 selects back in the RTL: BMC PASS to depth 20.

  3. Count the rows

    How many rows like these would a 200-pin pad ring need? That is the case for a generator.

design.sv

props.sv

Use assert property (@(posedge clk) ...);. Leave blank for immediate assertions in the design.

Inputs are free unless constrained. Run to find a counterexample or check a proof.

Model: one positive-edge clock, binary data. Sampled history requires declaration-initialized variables; primary-input history is unsupported.

SBY summary and log

Check Yourself

Predict the outputconn_top with pad41's selects 1 and 2 swapped, mode bmc, default depth 20, smtbmc yices, reset assumed in step 0, two hand-written rows checking pad41 at selects 1 and 2. The verdict is:
module conn_top(input clk, rst, input [11:0] src, input uart_tx, spi_mosi, input [1:0] gpio_o,
  input [1:0] pad_sel, input pipe_in, output [11:0] dst, output pad41, pad42, output reg pipe_out=0);
  assign dst = src;
  assign pad41 = pad_sel == 2'd0 ? gpio_o[0] : pad_sel == 2'd1 ? spi_mosi : pad_sel == 2'd2 ? uart_tx : 1'b0;   // bug: 1 and 2 swapped
  assign pad42 = pad_sel == 2'd0 ? gpio_o[1] : pad_sel == 2'd1 ? spi_mosi : pad_sel == 2'd2 ? uart_tx : 1'b0;
  always @(posedge clk) pipe_out <= rst ? 1'b0 : pipe_in;
`ifdef FORMAL
  always @(posedge clk) begin
    if ($initstate) begin assume(rst); end else begin assume(!rst); end
  end
  reg pipe_in_q=0, rst_q=1;   // last cycle's pipe_in and rst
  always @(posedge clk) begin pipe_in_q <= pipe_in; rst_q <= rst; end
  // generated from the connection table
  always @(posedge clk) if (!rst) begin
    if (pad_sel == 2'd0) assert(pad41 == gpio_o[0]);
    if (pad_sel == 2'd1) assert(pad41 == uart_tx);
    if (pad_sel == 2'd2) assert(pad41 == spi_mosi);
  end
`endif
endmodule
ConceptA team has a 400-row pin spreadsheet and no formal engineer. Which app do they start with?
ConceptWhat separates an app from general property verification?
ConceptWhich app finds a flop in the write-clock domain whose input comes from a read-clock flop with no synchroniser?

Remember

  • A formal app is assert, assume and cover around a generator, aimed at one narrow question.
  • Generated apps take a table, the RTL or a second design; hand-written ones need someone to name the rule.
  • Start a new SoC with connectivity and registers: no property to write.
  • An app proves its generated properties only; design-specific bugs still need your own.