3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-15 03:35:40 +00:00
yosys/tests/opt/opt_first_fit_alloc.ys

1173 lines
35 KiB
Text

# Tests for opt_first_fit_alloc
#
# The pass detects greedy first-fit "running-rank" resource allocators (the
# serial taken[]/done[] loop) and replaces the loop-carried cone with a
# log-depth priority-encode + all-pairs category-equality + prefix-sum + gather
# network. Where a per-slot field table (an "xbar") is driven from the same
# allocation, it is rewritten from the shared scan.
#
# Each group exercises a specific facet:
# A: basic detection + formal equivalence across allocator shapes (N=8).
# B: depth / cell-count bounds after rewrite (the actual QoR win).
# C: the customer slice (dsel + xbar) at N=8 (equiv) and N=16 (structural).
# D: variant detection (bc on/off, LSB/MSB-first scan).
# E: negative / no-op cases (no false rewrites).
# F: extra fanout / input reuse.
#
# Convention: every object the pass emits is named with an `ffa_` suffix, so
# `select w:*ffa_*` is a reliable "did the rewrite fire" probe.
# ============================================================================
# Group A: basic shapes (equiv_opt -assert)
# ============================================================================
# A1: dsel-only first-fit allocator, N=8 NB=4 (W=2) C=2, LSB-first, with bc.
log -header "A1: dsel-only allocator N=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
# Confirm the rewrite actually fired.
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# A2: dsel + xbar (per-slot field gather) allocator, N=8.
log -header "A2: dsel + xbar allocator N=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
input logic [N-1:0] sw,
output logic [N*W-1:0] dsel_flat,
output logic [NB*4*5-1:0] xbar_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [4:0] xbar [0:NB*4-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
for (int i=0;i<NB*4;i++) xbar[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int l=0;l<4;l++)
xbar[(j*4)+l] = (5'(({3'b0,cat_flat[i*C +: C]}*4)+l)) ^ {3'b0, sw[i], 1'b0};
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
for (genvar g=0;g<NB*4;g++) assign xbar_flat[g*5 +: 5] = xbar[g];
endmodule
EOF
hierarchy -top top
proc
memory -nomap -norom -nordff
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# A3: dsel-only, no broadcast lanes (bc absent), N=8.
log -header "A3: dsel-only allocator without bc, N=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if ((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group B: depth / cell-count bounds (the QoR win)
# ============================================================================
# B1: N=8 allocator. The serial taken[]/done[] scan lowers to ~1000 $mux cells
# (a deep loop-carried chain). After the rewrite the rank gather is a log-depth
# network: the long mux chain is gone, replaced by all-pairs $eq + prefix-sum
# $add. Bounding the surviving $mux count is a robust proxy for the depth win.
log -header "B1: N=8 allocator structural bounds"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
# Serial baseline: hundreds of muxes in the loop-carried chain.
select -assert-min 200 t:$mux
opt_first_fit_alloc
opt_clean
# The deep mux chain is replaced by the log-depth scan (measured ~17 muxes).
select -assert-max 60 t:$mux
# All-pairs category equality and the prefix-sum cascade are present.
select -assert-min 1 t:$eq
select -assert-min 1 t:$add
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group C: the customer slice (dsel + xbar) and scaling
# ============================================================================
# C1: N=16 dsel-only allocator -- structural (full equiv at N=16 is SAT-hard,
# matching the serial-vs-parallel allocator equivalence cost; covered formally
# at N=8 in group A and by the in-pass fingerprint here).
log -header "C1: N=16 allocator structural"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=16, NB=8, C=3, W=3) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
select -assert-min 1000 t:$mux
opt_first_fit_alloc
opt_clean
select -assert-max 120 t:$mux
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# C2: N=16 dsel + xbar -- both deep cones must collapse from the shared scan.
log -header "C2: N=16 dsel + xbar structural"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=16, NB=8, C=3, W=3) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
input logic [N-1:0] sw,
output logic [N*W-1:0] dsel_flat,
output logic [NB*4*5-1:0] xbar_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [4:0] xbar [0:NB*4-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
for (int i=0;i<NB*4;i++) xbar[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int l=0;l<4;l++)
xbar[(j*4)+l] = (5'(({2'b0,cat_flat[i*C +: C]}*4)+l)) ^ {3'b0, sw[i], 1'b0};
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
for (genvar g=0;g<NB*4;g++) assign xbar_flat[g*5 +: 5] = xbar[g];
endmodule
EOF
hierarchy -top top
proc
memory -nomap -norom -nordff
opt
select -assert-min 1000 t:$mux
opt_first_fit_alloc
opt_clean
# Both the dsel and the xbar field gather were rewritten from one scan: the
# deep mux chains collapse (measured ~41 muxes) and the xbar per-slot field
# gather emits $bmux table-lookups (only the xbar path emits $bmux).
select -assert-min 1 w:*ffa_*
select -assert-max 120 t:$mux
select -assert-min 1 t:$bmux
design -reset
log -pop
# ============================================================================
# Group D: variant detection (scan direction, category width)
# ============================================================================
# D1: MSB-first scan -- lanes processed from the high index down. The pass must
# detect the reversed priority and stay equivalent.
log -header "D1: MSB-first allocator (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=N-1;i>=0;i--)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# D2: a wider category (C=3, up to 8 distinct categories) with NB=8 slots.
log -header "D2: wider category C=3, NB=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=8, C=3, W=3) (
input logic mode,
input logic [N-1:0] req,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if ((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group E: negative / no-op cases (no false rewrites)
# ============================================================================
# E1: a per-lane passthrough (dsel[i] = cat[i]) -- no allocation, no taken[]
# scan. There is no enable bus and the function is not a running rank.
log -header "E1: per-lane passthrough -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, C=2, W=2) (
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [W-1:0] dsel [0:N-1];
always_comb
for (int i=0;i<N;i++) dsel[i] = W'(cat_flat[i*C +: C]);
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# E2: a "last-fit" allocator (scans free slots from NB-1 down to 0) -- the same
# loop shape as first-fit but the OPPOSITE slot choice. The fingerprint must
# reject it as it is not the first-fit running rank.
log -header "E2: last-fit allocator near-miss -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=NB-1;j>=0;j--)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if ((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# E3: input width below min (N=2 < default min-width 4) -> no candidate root.
log -header "E3: width below min-width -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=2, NB=2, C=1, W=1) (
input logic mode,
input logic [N-1:0] req,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# E4: a plain unrelated mux feeding a small output -- nothing allocator-shaped.
log -header "E4: unrelated logic -> no rewrite"
log -push
design -reset
read_verilog <<EOF
module top(input wire sel, input wire [7:0] a, b, output wire [7:0] y);
assign y = sel ? a : b;
endmodule
EOF
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group F: extra fanout / shared inputs
# ============================================================================
# F1: the request and category inputs also feed unrelated outputs. The pass
# must still rewrite the allocator and stay equivalent (inputs are just wires).
log -header "F1: shared inputs / extra fanout (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic mode,
input logic [N-1:0] req,
input logic [N-1:0] oob,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat,
output logic [N-1:0] req_dbg,
output logic [N*C-1:0] cat_dbg
);
logic [N-1:0] en = req & {N{mode}};
logic [N-1:0] bc = oob & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (((k!=i) && en[k] && (cat_flat[k*C +: C]==cat_flat[i*C +: C])) || bc[k]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
assign req_dbg = ~req;
assign cat_dbg = cat_flat ^ {(N*C){mode}};
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group G: coalesce-matrix variant (precomputed same_cat[i][k], raw-input en)
# ============================================================================
#
# Some RTL precomputes a per-leader "same_cat[i][k]" mask (gated ONLY on the
# leader's enable) and forward-coalesces the leader's slot into lane k without
# re-checking en[k]. Disabled lanes after a same-category leader therefore
# inherit that leader's slot (rather than 0). The pass detects this as the
# enable-independent forward-coalescing variant. These modules also drive the
# scan straight from a top-level request port (lane_en), exercising the
# primary-input enable/broadcast candidate path.
# G1: coalesce-matrix dsel-only, raw-input enable, N=8 (equiv).
log -header "G1: coalesce-matrix allocator, raw-input enable, N=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic [N-1:0] lane_en,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] same_cat [0:N-1];
always_comb begin
for (int a=0;a<N;a++) begin
same_cat[a] = '0;
if (lane_en[a])
for (int b=a;b<N;b++)
if (cat_flat[a*C +: C]==cat_flat[b*C +: C]) same_cat[a][b] = 1'b1;
end
end
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (same_cat[i][k]) begin dsel[k] = W'(j); done[k] = 1'b1; end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
# The coalesce variant fired (log shows "coalesce").
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# G2: coalesce-matrix dsel + xbar, N=16 -- structural. Both deep cones must
# collapse from the shared scan (dsel via the coalesce gather, xbar via the
# per-slot field gather); full equiv at N=16 is SAT-hard (see group C).
log -header "G2: coalesce-matrix dsel + xbar, N=16 structural"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=16, NB=8, C=3, W=3, A=5) (
input logic [N-1:0] lane_en,
input logic [N*C-1:0] cat_flat,
input logic [N-1:0] swap_bit,
output logic [N*W-1:0] dsel_flat,
output logic [32*A-1:0] xbar_flat
);
logic [N-1:0] same_cat [0:N-1];
always_comb begin
for (int a=0;a<N;a++) begin
same_cat[a] = '0;
if (lane_en[a])
for (int b=a;b<N;b++)
if (cat_flat[a*C +: C]==cat_flat[b*C +: C]) same_cat[a][b] = 1'b1;
end
end
logic [NB-1:0] taken;
logic [N-1:0] done;
logic [W-1:0] dsel [0:N-1];
logic [A-1:0] xbar [0:31];
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
for (int i=0;i<32;i++) xbar[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int l=0;l<4;l++)
xbar[(j*4)+l] = (A'(({2'b0,cat_flat[i*C +: C]}*4)+l)) ^ {3'b0, swap_bit[i], 1'b0};
for (int k=0;k<N;k++)
if (same_cat[i][k]) begin dsel[k] = W'(j); done[k] = 1'b1; end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
for (genvar g=0;g<32;g++) assign xbar_flat[g*A +: A] = xbar[g];
endmodule
EOF
hierarchy -top top
proc
memory -nomap -norom -nordff
opt
select -assert-min 1000 t:$mux
opt_first_fit_alloc
opt_clean
select -assert-min 1 w:*ffa_*
# The deep mux chains collapse and the xbar emits $bmux table-lookups.
select -assert-max 200 t:$mux
select -assert-min 1 t:$bmux
design -reset
log -pop
# G3: coalesce-matrix but LAST-fit slot choice (scans free slots NB-1..0). The
# same_cat coalescing is present but the slot assignment is not first-fit, so
# neither the standard nor the coalesce fingerprint may match.
log -header "G3: coalesce-matrix last-fit near-miss -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic [N-1:0] lane_en,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] same_cat [0:N-1];
always_comb begin
for (int a=0;a<N;a++) begin
same_cat[a] = '0;
if (lane_en[a])
for (int b=a;b<N;b++)
if (cat_flat[a*C +: C]==cat_flat[b*C +: C]) same_cat[a][b] = 1'b1;
end
end
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=NB-1;j>=0;j--)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (same_cat[i][k]) begin dsel[k] = W'(j); done[k] = 1'b1; end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group H: coalesce generalization (spelling + shape variants)
# ============================================================================
#
# The coalesce variant is detected by functional fingerprinting of the dsel
# cone, so it should not depend on the exact way the same-category forwarding
# is written or on the specific N/NB/C shape. These cases vary both.
# H1: coalesce with the same-category forwarding written INLINE (no precomputed
# same_cat[i][k] matrix) -- the leader compares categories directly in its
# forward loop. Functionally identical to G1's matrix form; must still detect
# the enable-independent coalescing variant and prove equivalent.
log -header "H1: coalesce inline-compare spelling, N=8 (equiv + fires)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, C=2, W=2) (
input logic [N-1:0] lane_en,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=i;k<N;k++)
if (cat_flat[k*C +: C]==cat_flat[i*C +: C]) begin
dsel[k] = W'(j); done[k] = 1'b1;
end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# H2: coalesce at a different shape -- N=8 lanes but NB=8 slots (twice G1's
# slot count, wider dsel W=3). Confirms the coalesce fingerprint generalizes
# across slot/field widths, not just the N=8/NB=4 shape of G1.
log -header "H2: coalesce shape N=8 NB=8 W=3 (equiv + fires)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=8, C=2, W=3) (
input logic [N-1:0] lane_en,
input logic [N*C-1:0] cat_flat,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] same_cat [0:N-1];
always_comb begin
for (int a=0;a<N;a++) begin
same_cat[a] = '0;
if (lane_en[a])
for (int b=a;b<N;b++)
if (cat_flat[a*C +: C]==cat_flat[b*C +: C]) same_cat[a][b] = 1'b1;
end
end
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
for (int k=0;k<N;k++)
if (same_cat[i][k]) begin dsel[k] = W'(j); done[k] = 1'b1; end
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# ============================================================================
# Group I: exclusive saturating first-fit (no category / broadcast)
# ============================================================================
#
# Plain taken[]/done[] scan without same-cat coalescing: each enabled lane
# takes the next free slot until NB slots are exhausted. Later requesters get
# rank 0 / not done. This is the qor_vmw_slot_lane shape.
# I1: exclusive dsel-only, N=8 NB=4 (equiv + fires).
log -header "I1: exclusive saturating allocator N=8 NB=4 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, W=2) (
input logic mode,
input logic [N-1:0] req,
output logic [N*W-1:0] dsel_flat
);
logic [N-1:0] en = req & {N{mode}};
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I2: exclusive with raw-input enable + sibling done bus, N=16 NB=4.
log -header "I2: exclusive N=16 NB=4 raw-input en + done (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=16, NB=4, W=2) (
input logic [N-1:0] lane_en,
output logic [N*W-1:0] dsel_flat,
output logic [N-1:0] done_o
);
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
assign done_o = done;
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I3: exclusive with non-power-of-two slot budget (NB=3, W=2) -- learn nb=3.
log -header "I3: exclusive NB=3 (non-power-of-two) N=8 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=3, W=2) (
input logic [N-1:0] lane_en,
output logic [N*W-1:0] dsel_flat
);
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I4: exclusive last-fit near-miss (scan slots NB-1..0) -> no rewrite.
log -header "I4: exclusive last-fit near-miss -> no rewrite"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, W=2) (
input logic [N-1:0] lane_en,
output logic [N*W-1:0] dsel_flat
);
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=NB-1;j>=0;j--)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
opt_first_fit_alloc
select -assert-count 0 w:*ffa_*
design -reset
log -pop
# I5: exclusive MSB-first scan (lanes N-1..0), N=8 NB=4 (equiv).
log -header "I5: exclusive MSB-first scan N=8 NB=4 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, W=2) (
input logic [N-1:0] lane_en,
output logic [N*W-1:0] dsel_flat
);
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=N-1;i>=0;i--)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I6: exclusive + identity gather of wide lane payloads into NB slots
# (qor_vmw_slot_lane shape: slot_data[j] = data[leader at j]).
# DW != N so per-lane data buses are not mistaken for width-N enables.
log -header "I6: exclusive + identity gather N=8 NB=4 DW=16 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, W=2, DW=16) (
input logic [N-1:0] lane_en,
input logic [N*DW-1:0] data_flat,
output logic [N*W-1:0] dsel_flat,
output logic [NB*DW-1:0] slot_flat
);
logic [W-1:0] dsel [0:N-1];
logic [DW-1:0] data [0:N-1];
logic [DW-1:0] slot_data [0:NB-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
for (genvar g=0;g<N;g++) assign data[g] = data_flat[g*DW +: DW];
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
for (int j=0;j<NB;j++) slot_data[j] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (lane_en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
slot_data[j] = data[i];
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
for (genvar g=0;g<NB;g++) assign slot_flat[g*DW +: DW] = slot_data[g];
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I7: exclusive and2 enable (req = a & b), with sibling done — the
# qor_vmw_slot_lane enable shape after opt folds the named `req` wire.
# Equiv covers the and2 fingerprint path; I6 already covers identity gather.
log -header "I7: exclusive and2 enable + done N=16 NB=4 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=16, NB=4, W=2) (
input logic [N-1:0] a,
input logic [N-1:0] b,
output logic [N*W-1:0] dsel_flat,
output logic [N-1:0] done_o
);
logic [N-1:0] en = a & b;
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0; done = '0;
for (int i=0;i<N;i++)
if (en[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
assign done_o = done;
endmodule
EOF
hierarchy -top top
proc
opt
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop
# I8: exclusive and2 enable without a global `opt` — launch flop stays a
# plain $dff (sync reset) so leaf runs are data_q halves. Covers the pre-opt
# and2 path; $aldff sequential handling is exercised by qor_vmw_slot_lane.
log -header "I8: exclusive and2 enable pre-opt N=8 NB=4 (equiv)"
log -push
design -reset
read_verilog -sv <<EOF
module top #(parameter N=8, NB=4, W=2) (
input logic clk,
input logic [2*N-1:0] wdata,
output logic [N*W-1:0] dsel_flat
);
logic [2*N-1:0] data_q;
always_ff @(posedge clk)
data_q <= wdata;
wire [N-1:0] req = data_q[N-1:0] & data_q[2*N-1:N];
logic [W-1:0] dsel [0:N-1];
logic [NB-1:0] taken;
logic [N-1:0] done;
always_comb begin
for (int i=0;i<N;i++) dsel[i] = '0;
taken = '0;
done = '0;
for (int i=0;i<N;i++)
if (req[i])
for (int j=0;j<NB;j++)
if (!taken[j] && !done[i]) begin
dsel[i] = W'(j); done[i] = 1'b1; taken[j] = 1'b1;
end
end
for (genvar g=0;g<N;g++) assign dsel_flat[g*W +: W] = dsel[g];
endmodule
EOF
hierarchy -top top
proc
# Intentionally no `opt`.
check -assert
equiv_opt -assert opt_first_fit_alloc
design -load postopt
select -assert-min 1 w:*ffa_*
design -reset
log -pop