FVO — Formal Verification Online
Lesson 3.7 · SystemVerilog Assertions Essentials

Lab: properties for a round-robin arbiter

Time to put everything together. The design below is a parameterised round-robin arbiter: up to N requesters, at most one grant per cycle, and a rotating priority pointer so nobody starves.

rr_arbiter.sv
// rr_arbiter.sv — N-way round-robin arbiter, combinational grant
module rr_arbiter #(
  parameter int N = 4
) (
  input  logic         clk,
  input  logic         rst,
  input  logic [N-1:0] req,
  output logic [N-1:0] gnt
);
  localparam int PW = (N > 1) ? $clog2(N) : 1;
  logic [PW-1:0] ptr;                    // requester with the highest priority

  // Grant the first requester at or after ptr (wrapping around)
  always_comb begin
    gnt = '0;
    for (int k = N - 1; k >= 0; k--) begin   // lowest k wins: last assignment sticks
      if (req[(int'(ptr) + k) % N]) begin
        gnt = '0;
        gnt[(int'(ptr) + k) % N] = 1'b1;
      end
    end
  end

  // After a grant, the requester just after the winner gets top priority
  always_ff @(posedge clk) begin
    if (rst) begin
      ptr <= '0;
    end else begin
      for (int i = 0; i < N; i++)
        if (gnt[i]) ptr <= PW'((i + 1) % N);
    end
  end
endmodule

The verification plan in plain words

#RequirementKind
1At most one grant at a timeassertion
2A grant only goes to a requester that is askingassertion
3If anybody requests, somebody is granted (work conserving)assertion
4A waiting requester is granted within N cycles (fairness)assertion
5Requesters hold req until grantedassumption (interface rule)
6Every requester can be granted; two requesters can be served back-to-backcovers
rr_arbiter_fv.sv
// rr_arbiter_fv.sv — formal wrapper with assumptions, assertions and covers
module rr_arbiter_fv #(
  parameter int N = 4
) (
  input logic         clk,
  input logic         rst,
  input logic [N-1:0] req
);
  logic [N-1:0] gnt;
  rr_arbiter #(.N(N)) dut (.*);

  initial assume (rst);                       // traces start in reset
  default clocking cb @(posedge clk); endclocking
  default disable iff (rst);

  // ---------------------------------------------------------- environment
  for (genvar i = 0; i < N; i++) begin : g_env
    // (5) a pending request is held until granted
    m_req_held: assume property (req[i] && !gnt[i] |=> req[i]);
  end

  // ---------------------------------------------------------- checks
  a_onehot:        assert property ($onehot0(gnt));                 // (1)
  a_work_conserve: assert property (|req |-> |gnt);                 // (3)

  for (genvar i = 0; i < N; i++) begin : g_chk
    a_gnt_has_req: assert property (gnt[i] |-> req[i]);             // (2)
    a_fair:        assert property (req[i] |-> ##[0:N-1] gnt[i]);   // (4)

    // ---------------------------------------------------------- covers
    c_gnt:         cover property (gnt[i]);                          // (6)
    c_wait_max:    cover property ((req[i] && !gnt[i]) [*N-1] ##1 gnt[i]);
  end

  c_back_to_back:  cover property (gnt[0] ##1 gnt[1]);
endmodule
rr_arbiter.sby
# rr_arbiter.sby
[tasks]
prove
cover

[options]
prove: mode prove
cover: mode cover
depth 12

[engines]
prove: abc pdr
cover: smtbmc

[script]
read -formal rr_arbiter.sv rr_arbiter_fv.sv
prep -top rr_arbiter_fv

[files]
rr_arbiter.sv
rr_arbiter_fv.sv

What to expect

  • All assertions should be proven. a_fair needs the assumption m_req_held: comment it out and the tool shows a requester that drops its request just before its turn — not a design bug, but a reminder that fairness depends on the interface rule.
  • c_wait_max should be covered: requester i waits the full N-1 cycles while others are served first. This proves the bound in a_fair is tight — it could not be written as ##[0:N-2].
Was this page helpful?

Tip: select any text on the page before clicking Report and it will be quoted in your report.