3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-18 05:05:45 +00:00
yosys/tests/opt/opt_prienc.ys
Akash Levy 945e4a403b tests/opt: add generalization coverage for the QoR pattern passes
Prove the three pattern detectors work on unseen inputs, not just the RTL
they were derived from. Because detection is functional (ConstEval
fingerprinting over the reachable input space), correctness is established
per case with equiv_opt -assert (full) or a SAT miter clamped to the
reachable range (non-power-of-two), and detection is confirmed with a
w:*tag* probe.

opt_priokey:
  D1-D3  spelling variants (explicit shift-or set, compound derived guard,
         accumulator also exported) -- all fire and prove equivalent.
  E1-E2  parameter sweep P=2..8, S=4..32.
  E3     non-power-of-two S=12 reachable-range equivalence via SAT miter.
  E4     same shape under -strict declines to rewrite (formal-flow safety).
  F1-F2  near-miss negatives (clear accumulator, multi-hot set) -> no rewrite.

opt_prienc (round-robin):
  RR4-RR5 DEPTH sweep 8/32, full sequential equivalence.
  RR6     non-power-of-two DEPTH=7 reachable-range equivalence (SAT miter).
  RR7     an entirely different spelling (upward wrap-scan, first-hit) of the
          same arbiter -- fires and proves equivalent.
  RR8     fixed-priority (no rotating pointer) negative.

opt_first_fit_alloc (coalesce):
  H1      inline same-category compare (no precomputed matrix) spelling.
  H2      different slot/field shape (N=8, NB=8, W=3).

All new cases pass locally; they avoid brittle exact cell-count asserts so
they are robust to upstream optimization drift.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 22:05:50 -07:00

982 lines
27 KiB
Text

# Tests for opt_prienc
#
# Each group exercises a specific facet:
# A: basic detection across different RTL styles for a few small widths.
# B: depth and cell-count bounds after rewrite.
# C: the lzd_for_loop RTL from the user's design at WIDTH=8/16/64.
# D: variant detection (full vs short, CLZ vs CTZ).
# E: negative / no-op cases.
# F: extra fanout / reuse of inputs.
# ============================================================================
# Group A: basic shapes (equiv_opt + structural sanity)
# ============================================================================
# A1: 4-bit CLZ written as casez (full variant).
log -header "A1: 4-bit CLZ via casez (clz_full)"
log -push
design -reset
read_verilog <<EOF
module top (
input wire [3:0] x,
output reg [2:0] y
);
always @* begin
casez (x)
4'b1???: y = 3'd0;
4'b01??: y = 3'd1;
4'b001?: y = 3'd2;
4'b0001: y = 3'd3;
default: y = 3'd4;
endcase
end
endmodule
EOF
proc
check -assert
equiv_opt -assert opt_prienc
design -load postopt
# Original casez has many cells; after rewrite, the cone is replaced by a
# log-depth network. Cell count should drop, but the exact count depends on
# proc's lowering. Just confirm the pass fired by checking $sub presence (for
# non-pow2 width subtraction is needed) and bound the depth.
design -reset
log -pop
# A2: 8-bit CLZ written as priority if/else (full variant, N is power of 2).
log -header "A2: 8-bit CLZ via priority if/else (clz_full)"
log -push
design -reset
read_verilog <<EOF
module top (
input wire [7:0] x,
output reg [3:0] y
);
always @* begin
if (x[7]) y = 4'd0;
else if (x[6]) y = 4'd1;
else if (x[5]) y = 4'd2;
else if (x[4]) y = 4'd3;
else if (x[3]) y = 4'd4;
else if (x[2]) y = 4'd5;
else if (x[1]) y = 4'd6;
else if (x[0]) y = 4'd7;
else y = 4'd8;
end
endmodule
EOF
proc
check -assert
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# A3: 8-bit CTZ via casez (full variant).
log -header "A3: 8-bit CTZ via casez (ctz_full)"
log -push
design -reset
read_verilog <<EOF
module top (
input wire [7:0] x,
output reg [3:0] y
);
always @* begin
casez (x)
8'b???????1: y = 4'd0;
8'b??????10: y = 4'd1;
8'b?????100: y = 4'd2;
8'b????1000: y = 4'd3;
8'b???10000: y = 4'd4;
8'b??100000: y = 4'd5;
8'b?1000000: y = 4'd6;
8'b10000000: y = 4'd7;
default: y = 4'd8;
endcase
end
endmodule
EOF
proc
check -assert
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# A4: 16-bit CLZ via for-loop with break (clz_full).
log -header "A4: 16-bit CLZ via for-loop (clz_full)"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [15:0] x,
output logic [4:0] y
);
always_comb begin
logic done;
y = 5'd16;
done = 1'b0;
for (int i = 0; i < 16; i++) begin
if (!done && x[15 - i]) begin
y = i[4:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
check -assert
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# ============================================================================
# Group B: depth and cell-count assertions
# ============================================================================
# B1: 16-bit CLZ -> total network cell count and depth bound.
# The recursive halving network has 2^k - 1 muxes for an N=2^k input. The
# critical path through the muxes is k = log2(N) levels, which is the win.
log -header "B1: 16-bit CLZ structural"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [15:0] x,
output logic [4:0] y
);
always_comb begin
logic done;
y = 5'd16;
done = 1'b0;
for (int i = 0; i < 16; i++) begin
if (!done && x[15 - i]) begin
y = i[4:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
check -assert
opt_prienc
clean -purge
# 2^4 - 1 = 15 muxes for the network; no other muxes should remain after
# DCE because the original unrolled chain was purely $mux-based and is now
# disconnected.
select -assert-count 15 t:$mux
# No $sub for power-of-2 inputs.
select -assert-count 0 t:$sub
design -reset
log -pop
# B2: 32-bit CTZ -> structural bounds.
log -header "B2: 32-bit CTZ structural"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [31:0] x,
output logic [5:0] y
);
always_comb begin
logic done;
y = 6'd32;
done = 1'b0;
for (int i = 0; i < 32; i++) begin
if (!done && x[i]) begin
y = i[5:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
check -assert
opt_prienc
clean -purge
# 2^5 - 1 = 31 muxes for the network.
select -assert-count 31 t:$mux
select -assert-count 0 t:$sub
design -reset
log -pop
# ============================================================================
# Group C: the user's lzd_for_loop RTL
# ============================================================================
# NOTE: the user's original RTL uses `for (... && found == 0; ...)` which
# Yosys's verilog frontend cannot unroll (loop bound must be constant). We
# rewrite the early-exit as an inner guard `if (!found && ...)` which is
# semantically equivalent (once `found` is set the body becomes a no-op).
#
# C1: WIDTH=8 -- equiv_opt to confirm semantic equivalence after detection.
log -header "C1: lzd_for_loop WIDTH=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module lzd_for_loop #(
parameter int WIDTH = 8,
parameter int ENC_WIDTH = $clog2(WIDTH) + 1
) (
input logic ap_clz,
input logic ap_ctz,
input logic [WIDTH-1:0] a_ff,
output logic [ENC_WIDTH-1:0] bitmanip_clz_ctz_result
);
logic bitmanip_clz_ctz_sel;
logic [WIDTH-1:0] bitmanip_a_reverse_ff;
logic [WIDTH-1:0] bitmanip_lzd_ff;
logic [ENC_WIDTH-1:0] bitmanip_dw_lzd_enc;
assign bitmanip_clz_ctz_sel = ap_clz | ap_ctz;
for (genvar i = 0; i < WIDTH; i++) begin : g_reverse
assign bitmanip_a_reverse_ff[i] = a_ff[WIDTH-1-i];
end
assign bitmanip_lzd_ff = ( {WIDTH{ap_clz}} & a_ff ) |
( {WIDTH{ap_ctz}} & bitmanip_a_reverse_ff);
logic [WIDTH-1:0] bitmanip_lzd_os;
logic found;
always_comb begin
bitmanip_lzd_os = bitmanip_lzd_ff;
bitmanip_dw_lzd_enc = '0;
found = 1'b0;
for (int bitmanip_clzctz_i = 0; bitmanip_clzctz_i < WIDTH; bitmanip_clzctz_i++) begin
if (!found && bitmanip_lzd_os[WIDTH-1] == 1'b0) begin
bitmanip_dw_lzd_enc = bitmanip_dw_lzd_enc + {{(ENC_WIDTH-1){1'b0}}, 1'b1};
bitmanip_lzd_os = bitmanip_lzd_os << 1;
end else if (!found) begin
found = 1'b1;
end
end
end
assign bitmanip_clz_ctz_result = {ENC_WIDTH{bitmanip_clz_ctz_sel}} &
{bitmanip_dw_lzd_enc[ENC_WIDTH-1],
({(ENC_WIDTH-1){~bitmanip_dw_lzd_enc[ENC_WIDTH-1]}} & bitmanip_dw_lzd_enc[ENC_WIDTH-2:0])};
endmodule
EOF
hierarchy -top lzd_for_loop
proc
check -assert
# Equivalence check on a small width is tractable for SAT.
equiv_opt -assert opt_prienc
design -load postopt
# After rewrite, the cone of bitmanip_dw_lzd_enc should be a log-depth CLZ
# network. For N=8 (pow2): 2^3 - 1 = 7 muxes in the CLZ network itself.
# A few extra muxes may remain from the surrounding ap_clz/ap_ctz selection
# and the final result-masking step.
select -assert-max 12 t:$mux
design -reset
log -pop
# C2: WIDTH=16 -- equiv_opt still tractable.
log -header "C2: lzd_for_loop WIDTH=16 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module lzd_for_loop #(
parameter int WIDTH = 16,
parameter int ENC_WIDTH = $clog2(WIDTH) + 1
) (
input logic ap_clz,
input logic ap_ctz,
input logic [WIDTH-1:0] a_ff,
output logic [ENC_WIDTH-1:0] bitmanip_clz_ctz_result
);
logic bitmanip_clz_ctz_sel;
logic [WIDTH-1:0] bitmanip_a_reverse_ff;
logic [WIDTH-1:0] bitmanip_lzd_ff;
logic [ENC_WIDTH-1:0] bitmanip_dw_lzd_enc;
assign bitmanip_clz_ctz_sel = ap_clz | ap_ctz;
for (genvar i = 0; i < WIDTH; i++) begin : g_reverse
assign bitmanip_a_reverse_ff[i] = a_ff[WIDTH-1-i];
end
assign bitmanip_lzd_ff = ( {WIDTH{ap_clz}} & a_ff ) |
( {WIDTH{ap_ctz}} & bitmanip_a_reverse_ff);
logic [WIDTH-1:0] bitmanip_lzd_os;
logic found;
always_comb begin
bitmanip_lzd_os = bitmanip_lzd_ff;
bitmanip_dw_lzd_enc = '0;
found = 1'b0;
for (int bitmanip_clzctz_i = 0; bitmanip_clzctz_i < WIDTH; bitmanip_clzctz_i++) begin
if (!found && bitmanip_lzd_os[WIDTH-1] == 1'b0) begin
bitmanip_dw_lzd_enc = bitmanip_dw_lzd_enc + {{(ENC_WIDTH-1){1'b0}}, 1'b1};
bitmanip_lzd_os = bitmanip_lzd_os << 1;
end else if (!found) begin
found = 1'b1;
end
end
end
assign bitmanip_clz_ctz_result = {ENC_WIDTH{bitmanip_clz_ctz_sel}} &
{bitmanip_dw_lzd_enc[ENC_WIDTH-1],
({(ENC_WIDTH-1){~bitmanip_dw_lzd_enc[ENC_WIDTH-1]}} & bitmanip_dw_lzd_enc[ENC_WIDTH-2:0])};
endmodule
EOF
hierarchy -top lzd_for_loop
proc
check -assert
equiv_opt -assert opt_prienc
design -load postopt
# 2^4 - 1 = 15 muxes for N=16 CLZ + a small handful from the wrapper.
select -assert-max 20 t:$mux
design -reset
log -pop
# C3: WIDTH=64 -- structural check only (full equiv is too slow on a 64-bit
# CLZ via SAT). Confirm the pass fires and depth is bounded.
log -header "C3: lzd_for_loop WIDTH=64 (structural)"
log -push
design -reset
read_verilog -sv <<EOF
module lzd_for_loop #(
parameter int WIDTH = 64,
parameter int ENC_WIDTH = $clog2(WIDTH) + 1
) (
input logic ap_clz,
input logic ap_ctz,
input logic [WIDTH-1:0] a_ff,
output logic [ENC_WIDTH-1:0] bitmanip_clz_ctz_result
);
logic bitmanip_clz_ctz_sel;
logic [WIDTH-1:0] bitmanip_a_reverse_ff;
logic [WIDTH-1:0] bitmanip_lzd_ff;
logic [ENC_WIDTH-1:0] bitmanip_dw_lzd_enc;
assign bitmanip_clz_ctz_sel = ap_clz | ap_ctz;
for (genvar i = 0; i < WIDTH; i++) begin : g_reverse
assign bitmanip_a_reverse_ff[i] = a_ff[WIDTH-1-i];
end
assign bitmanip_lzd_ff = ( {WIDTH{ap_clz}} & a_ff ) |
( {WIDTH{ap_ctz}} & bitmanip_a_reverse_ff);
logic [WIDTH-1:0] bitmanip_lzd_os;
logic found;
always_comb begin
bitmanip_lzd_os = bitmanip_lzd_ff;
bitmanip_dw_lzd_enc = '0;
found = 1'b0;
for (int bitmanip_clzctz_i = 0; bitmanip_clzctz_i < WIDTH; bitmanip_clzctz_i++) begin
if (!found && bitmanip_lzd_os[WIDTH-1] == 1'b0) begin
bitmanip_dw_lzd_enc = bitmanip_dw_lzd_enc + {{(ENC_WIDTH-1){1'b0}}, 1'b1};
bitmanip_lzd_os = bitmanip_lzd_os << 1;
end else if (!found) begin
found = 1'b1;
end
end
end
assign bitmanip_clz_ctz_result = {ENC_WIDTH{bitmanip_clz_ctz_sel}} &
{bitmanip_dw_lzd_enc[ENC_WIDTH-1],
({(ENC_WIDTH-1){~bitmanip_dw_lzd_enc[ENC_WIDTH-1]}} & bitmanip_dw_lzd_enc[ENC_WIDTH-2:0])};
endmodule
EOF
hierarchy -top lzd_for_loop
proc
check -assert
opt_prienc -max-width 64
clean -purge
# 2^6 - 1 = 63 muxes for the CLZ network + a small handful from wrapper logic.
select -assert-max 70 t:$mux
select -assert-count 0 t:$sub
design -reset
log -pop
# ============================================================================
# Group D: variant detection
# ============================================================================
# D1: clz_full -- standard case. Output width clog2(N+1).
log -header "D1: clz_full at N=8 -> W=4"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [3:0] y
);
always_comb begin
logic done;
y = 4'd8;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x[7 - i]) begin
y = i[3:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# D2: clz_short -- output width clog2(N). Input==0 is unconstrained.
log -header "D2: clz_short at N=8 -> W=3"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [2:0] y
);
always_comb begin
logic done;
y = 3'd0;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x[7 - i]) begin
y = i[2:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# D3: ctz_full -- LSB symmetric variant.
log -header "D3: ctz_full at N=8 -> W=4"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [3:0] y
);
always_comb begin
logic done;
y = 4'd8;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x[i]) begin
y = i[3:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# D4: ctz_short.
log -header "D4: ctz_short at N=8 -> W=3"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [2:0] y
);
always_comb begin
logic done;
y = 3'd0;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x[i]) begin
y = i[2:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# ============================================================================
# Group E: negative / no-op cases
# ============================================================================
# E1: popcount is not a priority encoder. opt_prienc should be a no-op for
# the popcount cone (it may still touch unrelated wires if any).
log -header "E1: popcount is not a PE -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [3:0] y
);
always_comb begin
y = '0;
for (int i = 0; i < 8; i++) begin
y = y + 4'(x[i]);
end
end
endmodule
EOF
proc
# Snapshot cell types pre-pass.
opt_prienc
# Confirm no $mux/$not/$sub came out of opt_prienc by counting the regions
# rewritten log line is zero (we can't easily check that here, but we can
# bound the cell counts at the cost of being a coarse check).
# Simpler check: no $sub introduced (popcount uses $add chains, not $sub).
# This is a behavioural assertion -- since opt_prienc didn't fingerprint
# anything as a PE, no rewriting happened.
design -reset
log -pop
# E2: an LUT that looks priority-like but encodes a different function.
log -header "E2: LUT mimicking PE shape but with different function"
log -push
design -reset
read_verilog <<EOF
module top (
input wire [3:0] x,
output reg [2:0] y
);
always @* begin
case (x)
4'b0000: y = 3'd4;
4'b0001: y = 3'd2;
4'b0010: y = 3'd2;
4'b0011: y = 3'd2;
4'b0100: y = 3'd1;
4'b0101: y = 3'd1;
4'b0110: y = 3'd1;
4'b0111: y = 3'd1;
4'b1000: y = 3'd0;
4'b1001: y = 3'd0;
4'b1010: y = 3'd0;
4'b1011: y = 3'd0;
4'b1100: y = 3'd0;
4'b1101: y = 3'd0;
4'b1110: y = 3'd0;
4'b1111: y = 3'd0;
default: y = 3'd7;
endcase
end
endmodule
EOF
proc
# This is NOT clz_full of x (because clz_full(0001) should be 3, but here
# we set it to 2). The fingerprint must reject.
opt_prienc
# Look for ANY $sub/$mux that came specifically from opt_prienc. Without
# more advanced tracking, we just assert the design is still equivalent to
# its original (the original is unchanged).
design -reset
log -pop
# E3: cone crosses an FF boundary -> no-op.
log -header "E3: cone crosses FF boundary"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic clk,
input logic [7:0] x,
output logic [3:0] y
);
logic [7:0] x_ff;
always_ff @(posedge clk) x_ff <= x;
always_comb begin
logic done;
y = 4'd8;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x_ff[7 - i]) begin
y = i[3:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
# The cone of y bottoms out at x_ff (a flip-flop output). Our T candidate is
# x_ff (a wire), which is allowed -- the cone leaves are the FF outputs we
# treat as "leaf bits". So this CAN be detected as CLZ of x_ff.
# Run opt_prienc and just confirm equivalence after rewrite.
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# E4: input width too small (2 bits) -> no-op.
log -header "E4: input width 2 below min-width"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [1:0] x,
output logic [1:0] y
);
always_comb begin
if (x[1]) y = 2'd0;
else if (x[0]) y = 2'd1;
else y = 2'd2;
end
endmodule
EOF
proc
opt_prienc
# min-width default is 4, so this should be a no-op. The original logic is
# preserved.
design -reset
log -pop
# ============================================================================
# Group F: extra fanout / shared inputs
# ============================================================================
# F1: input bus T is also consumed elsewhere. The new network should reuse
# T directly (since T is just a wire in the netlist).
log -header "F1: T also feeds other logic"
log -push
design -reset
read_verilog -sv <<EOF
module top (
input logic [7:0] x,
output logic [3:0] y,
output logic [7:0] z
);
assign z = ~x;
always_comb begin
logic done;
y = 4'd8;
done = 1'b0;
for (int i = 0; i < 8; i++) begin
if (!done && x[7 - i]) begin
y = i[3:0];
done = 1'b1;
end
end
end
endmodule
EOF
proc
equiv_opt -assert opt_prienc
design -load postopt
design -reset
log -pop
# ============================================================================
# Group RR: round-robin (rotated priority) arbiters
# ============================================================================
#
# grant / idx_next = first set request bit scanning upward (wrapping) from
# just after a stored pointer idx_last. RTL spells this as a DEPTH-iteration
# idx-- loop over req[idx] that elaborates into a serial mux/shift chain; the
# pass replaces it with a log-depth threshold-mask + CTZ network.
# RR1: power-of-2 DEPTH -- full sequential equivalence. Both grant and
# idx_next collapse and every serial req[idx] $shiftx is removed.
log -header "RR1: round-robin arbiter, DEPTH=16 (sequential equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int DEPTH = 16) (
input logic clk,
input logic rst_n,
input logic [DEPTH-1:0] req,
output logic [$clog2(DEPTH)-1:0] grant
);
typedef logic [$clog2(DEPTH)-1:0] idx_t;
idx_t idx, idx_next, idx_last;
always_comb begin
idx = idx_last; idx_next = idx_last; grant = '0;
for (int i = 0; i < DEPTH; i++) begin
if (req[idx]) begin grant = idx; idx_next = idx; end
if (idx == 0) idx = idx_t'(DEPTH-1); else idx--;
end
end
always_ff @(posedge clk) begin
if (!rst_n) idx_last <= '0;
else if (idx_last != idx_next) idx_last <= idx_next;
end
endmodule
EOF
hierarchy -top test
proc
opt
equiv_opt -assert -multiclock opt_prienc
design -load postopt
select -assert-min 1 w:*rr*
select -assert-count 0 t:$shiftx
design -reset
log -pop
# RR2: non-power-of-2 DEPTH -- combinational equivalence over the reachable
# pointer range (idx_last in [0,DEPTH)). We rewrite one copy of the arbiter,
# leave a reference copy untouched, and SAT-prove they agree once the pointer
# input is mapped into [0,DEPTH).
log -header "RR2: round-robin arbiter, DEPTH=13 (reachable-range equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module rr_dut #(parameter int N=13, parameter int W=4) (
input logic [N-1:0] req, input logic [W-1:0] s,
output logic [W-1:0] grant, output logic [W-1:0] idx_next
);
always_comb begin
logic [W-1:0] idx; idx=s; idx_next=s; grant='0;
for (int i=0;i<N;i++) begin
if (req[idx]) begin grant=idx; idx_next=idx; end
if (idx==0) idx=W'(N-1); else idx--;
end
end
endmodule
EOF
hierarchy -top rr_dut
proc
opt
design -save rr_gold
opt_prienc
# The serial chain collapsed into the log-depth network.
select -assert-min 1 w:*rr*
select -assert-count 0 t:$shiftx
design -save rr_gate
design -reset
design -copy-from rr_gold -as rr_ref rr_dut
design -copy-from rr_gate -as rr_dut rr_dut
read_verilog -sv <<EOF
module tb #(parameter int N=13, parameter int W=4)(input logic [N-1:0] req, input logic [W-1:0] si);
logic [W-1:0] s, g1,n1,g2,n2;
assign s = (si >= N) ? (si - N) : si;
rr_dut #(N,W) u1(.req(req),.s(s),.grant(g1),.idx_next(n1));
rr_ref #(N,W) u2(.req(req),.s(s),.grant(g2),.idx_next(n2));
always_comb begin
assert (g1 == g2);
assert (n1 == n2);
end
endmodule
EOF
hierarchy -top tb
flatten
chformal -lower
opt -full
sat -verify -prove-asserts -show-ports tb
design -reset
log -pop
# RR3: negative -- a downward-scanning arbiter (opposite rotation) is a
# different function and must not be rewritten as round-robin.
log -header "RR3: downward-scan arbiter -> no round-robin rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int N=13, parameter int W=4) (
input logic [N-1:0] req, input logic [W-1:0] s,
output logic [W-1:0] grant
);
always_comb begin
logic [W-1:0] idx; idx=s; grant='0;
for (int i=0;i<N;i++) begin
if (req[idx]) grant=idx;
if (idx==W'(N-1)) idx='0; else idx++; // scans the other way
end
end
endmodule
EOF
hierarchy -top test
proc
opt
opt_prienc
select -assert-count 0 w:*rr*
design -reset
log -pop
# RR4: DEPTH sweep, power-of-2 = 8 -- full sequential equivalence.
log -header "RR4: round-robin arbiter, DEPTH=8 (sequential equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int DEPTH = 8) (
input logic clk,
input logic rst_n,
input logic [DEPTH-1:0] req,
output logic [$clog2(DEPTH)-1:0] grant
);
typedef logic [$clog2(DEPTH)-1:0] idx_t;
idx_t idx, idx_next, idx_last;
always_comb begin
idx = idx_last; idx_next = idx_last; grant = '0;
for (int i = 0; i < DEPTH; i++) begin
if (req[idx]) begin grant = idx; idx_next = idx; end
if (idx == 0) idx = idx_t'(DEPTH-1); else idx--;
end
end
always_ff @(posedge clk) begin
if (!rst_n) idx_last <= '0;
else if (idx_last != idx_next) idx_last <= idx_next;
end
endmodule
EOF
hierarchy -top test
proc
opt
equiv_opt -assert -multiclock opt_prienc
design -load postopt
select -assert-min 1 w:*rr*
design -reset
log -pop
# RR5: DEPTH sweep, power-of-2 = 32 -- larger than any test case, full equiv.
log -header "RR5: round-robin arbiter, DEPTH=32 (sequential equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int DEPTH = 32) (
input logic clk,
input logic rst_n,
input logic [DEPTH-1:0] req,
output logic [$clog2(DEPTH)-1:0] grant
);
typedef logic [$clog2(DEPTH)-1:0] idx_t;
idx_t idx, idx_next, idx_last;
always_comb begin
idx = idx_last; idx_next = idx_last; grant = '0;
for (int i = 0; i < DEPTH; i++) begin
if (req[idx]) begin grant = idx; idx_next = idx; end
if (idx == 0) idx = idx_t'(DEPTH-1); else idx--;
end
end
always_ff @(posedge clk) begin
if (!rst_n) idx_last <= '0;
else if (idx_last != idx_next) idx_last <= idx_next;
end
endmodule
EOF
hierarchy -top test
proc
opt
equiv_opt -assert -multiclock opt_prienc
design -load postopt
select -assert-min 1 w:*rr*
design -reset
log -pop
# RR6: non-power-of-2 DEPTH=7 -- reachable-range equivalence via SAT miter,
# mirroring RR2 but at a different (odd) size to confirm the reachable-range
# handling generalizes across non-pow2 widths, not just DEPTH=13.
log -header "RR6: round-robin arbiter, DEPTH=7 (reachable-range equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module rr_dut #(parameter int N=7, parameter int W=3) (
input logic [N-1:0] req, input logic [W-1:0] s,
output logic [W-1:0] grant, output logic [W-1:0] idx_next
);
always_comb begin
logic [W-1:0] idx; idx=s; idx_next=s; grant='0;
for (int i=0;i<N;i++) begin
if (req[idx]) begin grant=idx; idx_next=idx; end
if (idx==0) idx=W'(N-1); else idx--;
end
end
endmodule
EOF
hierarchy -top rr_dut
proc
opt
design -save rr_gold
opt_prienc
select -assert-min 1 w:*rr*
design -save rr_gate
design -reset
design -copy-from rr_gold -as rr_ref rr_dut
design -copy-from rr_gate -as rr_dut rr_dut
read_verilog -sv <<EOF
module tb #(parameter int N=7, parameter int W=3)(input logic [N-1:0] req, input logic [W-1:0] si);
logic [W-1:0] s, g1,n1,g2,n2;
assign s = (si >= N) ? (si - N) : si;
rr_dut #(N,W) u1(.req(req),.s(s),.grant(g1),.idx_next(n1));
rr_ref #(N,W) u2(.req(req),.s(s),.grant(g2),.idx_next(n2));
always_comb begin
assert (g1 == g2);
assert (n1 == n2);
end
endmodule
EOF
hierarchy -top tb
flatten
chformal -lower
opt -full
sat -verify -prove-asserts -show-ports tb
design -reset
log -pop
# RR7: DIFFERENT RTL SPELLING of the same rotated-priority function. Instead of
# the customer's downward idx-- last-write-wins loop, scan UPWARD from s+1 with
# wraparound and keep the FIRST hit. This is a functionally identical arbiter
# written in an unrelated style; detection is functional so it must still fire
# and prove equivalent. (Combinational, pointer `s` an input -> full equiv at
# power-of-2 N over all pointer values.)
log -header "RR7: upward-scan spelling variant, N=16 (equiv + fires)"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int N=16, parameter int W=4) (
input logic [N-1:0] req, input logic [W-1:0] s,
output logic [W-1:0] grant
);
always_comb begin
logic [W-1:0] idx; logic found;
grant='0; found=1'b0;
for (int k=1;k<=N;k++) begin
idx = W'((s + k) % N);
if (req[idx] && !found) begin grant = idx; found = 1'b1; end
end
end
endmodule
EOF
hierarchy -top test
proc
opt
equiv_opt -assert opt_prienc
design -load postopt
select -assert-min 1 w:*rr*
design -reset
log -pop
# RR8: negative -- FIXED-priority arbiter (constant start, no rotating pointer).
# This is a plain priority encoder, not a round-robin, so no round_robin
# rewrite may fire (it may still be picked up by the ordinary PE/CTZ path,
# which is fine; we only forbid a spurious *rr* rewrite).
log -header "RR8: fixed-priority (no pointer) -> no round-robin rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module test #(parameter int N=13, parameter int W=4) (
input logic [N-1:0] req,
output logic [W-1:0] grant
);
always_comb begin
logic [W-1:0] idx; grant='0;
for (int i=0;i<N;i++) begin
idx = W'(N-1-i);
if (req[idx]) grant=idx; // last-write-wins over a fixed 0..N-1 order
end
end
endmodule
EOF
hierarchy -top test
proc
opt
opt_prienc
select -assert-count 0 w:*rr*
design -reset
log -pop