A Bus Interface: APB and AXI4-Lite
Write one APB property module that verifies either side, see why flipping its verbs is not free, and run the same idea over the five AXI4-Lite channels.
- Lesson
- F8.5
- Time
- 25 min
- Runs on
- SymbiYosys
- Check
- 4 questions
A team proved an APB completer whose bench bounded the wait states at zero, because their completer never waited. Later they flipped every verb to verify a requester, ran it, and got PASS in a second. That requester changed PADDR during wait states, and the flipped bound had promised there would be none.
Read the diagram as text
- Free requester inputs: completer bench: PSEL, PENABLE, PADDR ... chosen by the solver
- apb_fv, CPL = 1: requester rules assumed, wait bound asserted
- apb_cpl_f: the completer under test
- Free completer inputs: requester bench: PREADY chosen by the solver
- apb_fv, CPL = 0: requester rules asserted, wait bound assumed
- apb_req_f: the requester under test
- Free requester inputs → apb_fv, CPL = 1: assume
- apb_fv, CPL = 1 → apb_cpl_f: assert
- Free completer inputs → apb_fv, CPL = 0: assume
- apb_fv, CPL = 0 → apb_req_f: assert
The text of each rule never changes. What changes is who owes it, and so which verb the tool uses.
Every Rule Has a Side
| Rule (IHI 0024E) | Owed by | Wire in apb_fv | Completer bench | Requester bench |
|---|---|---|---|---|
| SETUP lasts one cycle and ACCESS follows (4.1) | requester | r_access | assume | assert |
| PENABLE only after SETUP (3.1, 4.1) | requester | r_enable | assume | assert |
| PADDR, PWRITE, PWDATA, PSTRB stable until PREADY (3.1.2) | requester | r_stable | assume | assert |
| No strobes on a read (3.2) | requester | r_strobe | assume | assert |
| At most MAXW wait states (a bounded response) | completer | c_wait | assert | assume |
| PREADY may take any value while PENABLE is LOW (3.1.2) | nobody | no rule | - | - |
The rules P3.2 and P3.8 teach in simulation. The last row is a permission: a checker that flags it raises a false alarm.
A protocol property file lists every rule once and says who owes it. On the completer bench the requester's rules are assumptions and the completer's are assertions; on the requester bench the verbs swap. Here a parameter picks the verb for each wire.
PREADY may be high while PENABLE is low. A completer with a fixed two-cycle access can tie it high. Adding "no PREADY outside ACCESS" to the bench is a classic over-strict checker.
The Completer Bench
Measured with smtbmc yices: FAIL at reported step 3, 0.5 s. The ID value appears a cycle after the transfer ended.
| apb_cpl_f, completer bench | Measured result |
|---|---|
| No wait states, MAXW 0, bmc 12 | BMC PASS to depth 12, 0.6 s |
| Same, prove k = 4 | UNKNOWN at k = 4 (base case passed, induction step failed), 0.5 s |
Same, abc pdr | Prove PASS (abc pdr), 0.6 s |
| P3 late_rdata fault, bmc 12 | FAIL at reported step 3, 0.5 s |
| Two wait states, MAXW 2, bmc 12 | BMC PASS to depth 12, 0.6 s |
| ready_in_setup (legal), bmc 12 | BMC PASS to depth 12, 0.6 s |
| ready_in_setup with the over-strict line | FAIL at reported step 2, 0.6 s |
Covers with two wait states: a waited access at step 3, an error response at step 5, back-to-back transfers at step 6. The completer has 66 flops.
The data check belongs to this completer, not to the protocol: a read returns the register. From an arbitrary start, k-induction cannot tie the bench's copy of CTRL to the design's. PDR closes it.
A Flipped File Needs Its Own Covers
Measured: FAIL at reported step 5, 0.6 s, on r_stable.
| apb_req_f with BUG_ADDR_CHANGE | Assertions (bmc 12) | Covers |
|---|---|---|
| MAXW 0, copied from the completer bench | BMC PASS to depth 12 | waited access unreached; completion at step 4 |
| MAXW 3, the system's real bound | FAIL at reported step 5 | waited access at step 4 |
The fixed requester with MAXW 3: BMC PASS to depth 12; prove k = 4: Prove PASS (k = 4).
On the requester bench the completer's wait bound is an assumption. Set to the old completer's zero, it says every access completes at once, and the bug needs a wait state. The cover for a waited access comes back unreached and gives the hollow PASS away (F3.2).
A flipped file needs its own covers, and every flipped assertion must be checked as an assumption: is it true of every partner, or only of the one you proved?
AXI4-Lite: Five Handshakes
| Channel | Sender | VALID holds until READY | Payload stable while waiting |
|---|---|---|---|
| AW | manager | assume | assume |
| W | manager | assume | assume |
| AR | manager | assume | assume |
| B | subordinate | assert | assert |
| R | subordinate | assert | assert |
IHI 0022L A2.3. Plus two response rules, asserted: no BVALID before an accepted address and data, no RVALID before an accepted read address (A2.3.2). The block is P2.15's register block, 142 flops in this formal copy.
Measured: FAIL at reported step 4, 0.6 s.
Every channel has the same two rules. The manager's three are assumptions here; the subordinate's two are assertions. Correct block: BMC PASS to depth 12, and abc pdr Prove PASS (abc pdr). The covers reach a stalled write response, a stalled read and data before address.
Asked in Interviews
| Question | Answer |
|---|---|
| How do you decide a failure is the environment's fault on a bus? | Check the rule table: if the free input breaks a rule the other side owes, an assumption is missing. Cite the clause. |
| How does one property file serve both sides? | A parameter picks the verb per rule. Each side adds its own covers and checks every flipped assertion as an assumption. |
| What are the AXI handshake rules? | VALID holds until READY; the payload is stable while waiting; a sender never waits for READY before raising VALID. |
Try It
[options]
mode bmc
depth 12
[engines]
smtbmc yices
[script]
read_verilog -formal -sv design.sv
prep -top req_top
[files]
design.sv`define REQ_RULE(p) if (CPL) assume property (p); else assert property (p)
`REQ_RULE(@(posedge clk) disable iff (!presetn) psel && !penable |=> psel && penable);
`REQ_RULE(@(posedge clk) disable iff (!presetn) psel && !(penable && pready) |=> $stable({paddr, pwrite, pwdata, pstrb}));
`REQ_RULE(@(posedge clk) disable iff (!presetn) psel && !pwrite |-> pstrb == 0);module apb_fv #(parameter CPL = 1, MAXW = 0)(input clk, presetn, psel, penable, pwrite,
input [3:0] paddr, input [31:0] pwdata, input [3:0] pstrb, input pready);
reg setup_q = 0, wait_q = 0, done_q = 0; // last cycle: SETUP, a waited ACCESS, a completion
reg [3:0] paddr_q = 0, pstrb_q = 0;
reg [31:0] pwdata_q = 0;
reg pwrite_q = 0;
reg [2:0] waits = 0; // wait states so far in this access
always @(posedge clk) begin
setup_q <= presetn && psel && !penable;
wait_q <= presetn && psel && penable && !pready;
done_q <= presetn && psel && penable && pready;
paddr_q <= paddr; pwdata_q <= pwdata; pstrb_q <= pstrb; pwrite_q <= pwrite;
waits <= presetn && psel && penable && !pready ? waits + 3'd1 : 3'd0;
end
// Requester rules (IHI 0024E 3.1, 3.2, 4.1)
wire r_access = !(setup_q || wait_q) || psel && penable; // SETUP and a waited ACCESS go on to ACCESS
wire r_enable = !penable || psel && (setup_q || wait_q); // PENABLE only after SETUP
wire r_stable = !(setup_q || wait_q) || paddr == paddr_q && pwrite == pwrite_q && pwdata == pwdata_q && pstrb == pstrb_q;
wire r_strobe = !(psel && !pwrite) || pstrb == 4'd0; // no strobes on a read
// Completer rule: at most MAXW wait states
wire c_wait = !(psel && penable && !pready) || waits < MAXW;
always @(posedge clk) if (presetn) begin
if (CPL) begin
assume(r_access); assume(r_enable); assume(r_stable); assume(r_strobe);
assert(c_wait);
end else begin
assert(r_access); assert(r_enable); assert(r_stable); assert(r_strobe);
assume(c_wait);
end
end
endmoduleAssign the sides
Before opening the code, mark each row of the rule table: who owes it?
The hollow bench
Run the MAXW 0 twin: BMC PASS to depth 12. Run its covers and find the one that is unreached.
The real bound
Run as loaded with MAXW 3: FAIL at reported step 5. Walk the trace to the cycle PADDR moves.
AXI4-Lite
Run the AXI4-Lite bug twin and name the rule that fired.
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
Remember
- A protocol file lists each rule once with its owner; a parameter picks assume or assert.
- PREADY high while PENABLE is low is legal; a checker that flags it is over-strict.
- A flipped assertion is an assumption on the new bench: check it, and cover what it might exclude.
- AXI4-Lite is five handshakes with the same two rules, plus response-after-request rules.