Optimize decoder case statements into lookup tables (#7795)
Recognize "decoder" case statements (where every case item only assigns
constants to a fixed set of left-hand sides) and replace them with a
single packed constant lookup table indexed by the case expression.
Small tables are materialized inline in the generated code, and are
always optimized. Larger ones are placed in the constant pool and only
optimized if deemed beneficial over branches.
While this slightly conflicts with V3Table, and is not worth that much
on it's own, there will be a follow up patch that converts more cases of
this form which will be much more valuable. This patch does the
necessary analysis and the simple table conversion when possible.
Split -fcase into -fcase-table (this new conversion) and -fcase-tree (the
existing bitwise branch-tree conversion); -fno-case is now an alias for
both.
Default branches, assignments preceding the case (used as default values),
casez wildcards, multiple and partial left-hand sides, and both blocking and
non-blocking assignments are handled. Cases that cannot be safely tabled (e.g.
non-exhaustive with no default, overlapping writes to one variable, or mixed
blocking/non-blocking assignments) fall back to the existing if/else lowering.
Consequently disabled re-inlining of constant pool variables in V3Const,
and rebuild the constant pool hash in V3Dead (previously we didn't
create constant pool entries early enough for this to matter)
2026-06-18 10:30:50 +02:00
|
|
|
// DESCRIPTION: Verilator: Verilog Test module
|
|
|
|
|
//
|
|
|
|
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
|
|
|
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
|
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
|
//
|
|
|
|
|
// Case statements that become a "normal" (constant-pool) lookup table, followed by
|
|
|
|
|
// cases that must not be converted to one. Each output is compared against an
|
|
|
|
|
// equivalent reference computed without a case statement, so the reference itself is
|
|
|
|
|
// never tabled. Selectors are wide enough, with enough distinct values, that the
|
|
|
|
|
// branch lowering they replace is deep enough to make a table worthwhile.
|
|
|
|
|
|
|
|
|
|
// verilog_format: off
|
|
|
|
|
`define stop $stop
|
|
|
|
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
|
|
|
|
// verilog_format: on
|
|
|
|
|
|
|
|
|
|
module t;
|
|
|
|
|
logic clk = 1'b0;
|
|
|
|
|
always #5 clk = ~clk;
|
|
|
|
|
|
|
|
|
|
logic [31:0] cyc = 0;
|
|
|
|
|
|
|
|
|
|
// Accept A: single output, blocking assignment, all selector values covered.
|
|
|
|
|
logic [15:0] accept_a_out, accept_a_ref;
|
|
|
|
|
always_comb
|
|
|
|
|
case (cyc[3:0])
|
|
|
|
|
4'd0: accept_a_out = 16'h1111;
|
|
|
|
|
4'd1: accept_a_out = 16'h2222;
|
|
|
|
|
4'd2: accept_a_out = 16'h4444;
|
|
|
|
|
4'd3: accept_a_out = 16'h8888;
|
|
|
|
|
default: accept_a_out = 16'h0f0f;
|
|
|
|
|
endcase
|
|
|
|
|
assign accept_a_ref = (cyc[3:0] == 4'd0) ? 16'h1111
|
|
|
|
|
: (cyc[3:0] == 4'd1) ? 16'h2222
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 16'h4444
|
|
|
|
|
: (cyc[3:0] == 4'd3) ? 16'h8888 : 16'h0f0f;
|
|
|
|
|
|
|
|
|
|
// Accept B: single output, non-blocking assignment, with a default value set before
|
|
|
|
|
// the case and not all selector values covered.
|
|
|
|
|
logic [15:0] accept_b_out, accept_b_ref;
|
|
|
|
|
// verilator lint_off CASEINCOMPLETE
|
|
|
|
|
always_ff @(posedge clk) begin
|
|
|
|
|
accept_b_out <= 16'hffff;
|
|
|
|
|
case (cyc[3:0])
|
|
|
|
|
4'd0: accept_b_out <= 16'h0001;
|
|
|
|
|
4'd1: accept_b_out <= 16'h0002;
|
|
|
|
|
4'd2: accept_b_out <= 16'h0004;
|
|
|
|
|
4'd3: accept_b_out <= 16'h0008;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
// verilator lint_on CASEINCOMPLETE
|
|
|
|
|
always_ff @(posedge clk)
|
|
|
|
|
accept_b_ref <= (cyc[3:0] == 4'd0) ? 16'h0001
|
|
|
|
|
: (cyc[3:0] == 4'd1) ? 16'h0002
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 16'h0004
|
|
|
|
|
: (cyc[3:0] == 4'd3) ? 16'h0008 : 16'hffff;
|
|
|
|
|
|
|
|
|
|
// Accept C: three outputs, blocking assignment, with a default branch.
|
|
|
|
|
logic [11:0] accept_c_out_0, accept_c_ref_0;
|
|
|
|
|
logic [11:0] accept_c_out_1, accept_c_ref_1;
|
|
|
|
|
logic [11:0] accept_c_out_2, accept_c_ref_2;
|
|
|
|
|
always_comb
|
|
|
|
|
case (cyc[3:0])
|
2026-06-19 03:56:02 +02:00
|
|
|
4'd0: begin
|
|
|
|
|
accept_c_out_0 = 12'h001;
|
|
|
|
|
accept_c_out_1 = 12'h010;
|
|
|
|
|
accept_c_out_2 = 12'h100;
|
|
|
|
|
end
|
|
|
|
|
4'd1: begin
|
|
|
|
|
accept_c_out_0 = 12'h002;
|
|
|
|
|
accept_c_out_1 = 12'h020;
|
|
|
|
|
accept_c_out_2 = 12'h200;
|
|
|
|
|
end
|
|
|
|
|
4'd2: begin
|
|
|
|
|
accept_c_out_0 = 12'h004;
|
|
|
|
|
accept_c_out_1 = 12'h040;
|
|
|
|
|
accept_c_out_2 = 12'h400;
|
|
|
|
|
end
|
|
|
|
|
4'd3: begin
|
|
|
|
|
accept_c_out_0 = 12'h008;
|
|
|
|
|
accept_c_out_1 = 12'h080;
|
|
|
|
|
accept_c_out_2 = 12'h800;
|
|
|
|
|
end
|
|
|
|
|
default: begin
|
|
|
|
|
accept_c_out_0 = 12'h000;
|
|
|
|
|
accept_c_out_1 = 12'h0ff;
|
|
|
|
|
accept_c_out_2 = 12'hfff;
|
|
|
|
|
end
|
Optimize decoder case statements into lookup tables (#7795)
Recognize "decoder" case statements (where every case item only assigns
constants to a fixed set of left-hand sides) and replace them with a
single packed constant lookup table indexed by the case expression.
Small tables are materialized inline in the generated code, and are
always optimized. Larger ones are placed in the constant pool and only
optimized if deemed beneficial over branches.
While this slightly conflicts with V3Table, and is not worth that much
on it's own, there will be a follow up patch that converts more cases of
this form which will be much more valuable. This patch does the
necessary analysis and the simple table conversion when possible.
Split -fcase into -fcase-table (this new conversion) and -fcase-tree (the
existing bitwise branch-tree conversion); -fno-case is now an alias for
both.
Default branches, assignments preceding the case (used as default values),
casez wildcards, multiple and partial left-hand sides, and both blocking and
non-blocking assignments are handled. Cases that cannot be safely tabled (e.g.
non-exhaustive with no default, overlapping writes to one variable, or mixed
blocking/non-blocking assignments) fall back to the existing if/else lowering.
Consequently disabled re-inlining of constant pool variables in V3Const,
and rebuild the constant pool hash in V3Dead (previously we didn't
create constant pool entries early enough for this to matter)
2026-06-18 10:30:50 +02:00
|
|
|
endcase
|
|
|
|
|
assign accept_c_ref_0 = (cyc[3:0] == 4'd0) ? 12'h001 : (cyc[3:0] == 4'd1) ? 12'h002
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 12'h004 : (cyc[3:0] == 4'd3) ? 12'h008 : 12'h000;
|
|
|
|
|
assign accept_c_ref_1 = (cyc[3:0] == 4'd0) ? 12'h010 : (cyc[3:0] == 4'd1) ? 12'h020
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 12'h040 : (cyc[3:0] == 4'd3) ? 12'h080 : 12'h0ff;
|
|
|
|
|
assign accept_c_ref_2 = (cyc[3:0] == 4'd0) ? 12'h100 : (cyc[3:0] == 4'd1) ? 12'h200
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 12'h400 : (cyc[3:0] == 4'd3) ? 12'h800 : 12'hfff;
|
|
|
|
|
|
|
|
|
|
// Accept D: two outputs, non-blocking assignment, empty default branch, with default
|
|
|
|
|
// values set before the case.
|
|
|
|
|
logic [15:0] accept_d_out_0, accept_d_ref_0;
|
|
|
|
|
logic [15:0] accept_d_out_1, accept_d_ref_1;
|
|
|
|
|
always_ff @(posedge clk) begin
|
|
|
|
|
accept_d_out_0 <= 16'h0000;
|
|
|
|
|
accept_d_out_1 <= 16'hffff;
|
|
|
|
|
case (cyc[3:0])
|
2026-06-19 03:56:02 +02:00
|
|
|
4'd0: begin
|
|
|
|
|
accept_d_out_0 <= 16'h0001;
|
|
|
|
|
accept_d_out_1 <= 16'h0010;
|
|
|
|
|
end
|
|
|
|
|
4'd1: begin
|
|
|
|
|
accept_d_out_0 <= 16'h0002;
|
|
|
|
|
accept_d_out_1 <= 16'h0020;
|
|
|
|
|
end
|
|
|
|
|
4'd2: begin
|
|
|
|
|
accept_d_out_0 <= 16'h0004;
|
|
|
|
|
accept_d_out_1 <= 16'h0040;
|
|
|
|
|
end
|
|
|
|
|
4'd3: begin
|
|
|
|
|
accept_d_out_0 <= 16'h0008;
|
|
|
|
|
accept_d_out_1 <= 16'h0080;
|
|
|
|
|
end
|
|
|
|
|
default: begin
|
|
|
|
|
end
|
Optimize decoder case statements into lookup tables (#7795)
Recognize "decoder" case statements (where every case item only assigns
constants to a fixed set of left-hand sides) and replace them with a
single packed constant lookup table indexed by the case expression.
Small tables are materialized inline in the generated code, and are
always optimized. Larger ones are placed in the constant pool and only
optimized if deemed beneficial over branches.
While this slightly conflicts with V3Table, and is not worth that much
on it's own, there will be a follow up patch that converts more cases of
this form which will be much more valuable. This patch does the
necessary analysis and the simple table conversion when possible.
Split -fcase into -fcase-table (this new conversion) and -fcase-tree (the
existing bitwise branch-tree conversion); -fno-case is now an alias for
both.
Default branches, assignments preceding the case (used as default values),
casez wildcards, multiple and partial left-hand sides, and both blocking and
non-blocking assignments are handled. Cases that cannot be safely tabled (e.g.
non-exhaustive with no default, overlapping writes to one variable, or mixed
blocking/non-blocking assignments) fall back to the existing if/else lowering.
Consequently disabled re-inlining of constant pool variables in V3Const,
and rebuild the constant pool hash in V3Dead (previously we didn't
create constant pool entries early enough for this to matter)
2026-06-18 10:30:50 +02:00
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
always_ff @(posedge clk) begin
|
|
|
|
|
accept_d_ref_0 <= (cyc[3:0] == 4'd0) ? 16'h0001 : (cyc[3:0] == 4'd1) ? 16'h0002
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 16'h0004 : (cyc[3:0] == 4'd3) ? 16'h0008 : 16'h0000;
|
|
|
|
|
accept_d_ref_1 <= (cyc[3:0] == 4'd0) ? 16'h0010 : (cyc[3:0] == 4'd1) ? 16'h0020
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 16'h0040 : (cyc[3:0] == 4'd3) ? 16'h0080 : 16'hffff;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
// Accept E: casez with don't-care bits.
|
|
|
|
|
logic [15:0] accept_e_out, accept_e_ref;
|
|
|
|
|
always_comb
|
|
|
|
|
casez (cyc[3:0])
|
|
|
|
|
4'b00??: accept_e_out = 16'haaaa;
|
|
|
|
|
4'b01??: accept_e_out = 16'hbbbb;
|
|
|
|
|
4'b10??: accept_e_out = 16'hcccc;
|
|
|
|
|
4'b11??: accept_e_out = 16'hdddd;
|
|
|
|
|
endcase
|
|
|
|
|
assign accept_e_ref = (cyc[3:2] == 2'd0) ? 16'haaaa : (cyc[3:2] == 2'd1) ? 16'hbbbb
|
|
|
|
|
: (cyc[3:2] == 2'd2) ? 16'hcccc : 16'hdddd;
|
|
|
|
|
|
|
|
|
|
// Accept F: an item that can never match, and an item listing multiple values.
|
|
|
|
|
logic [15:0] accept_f_out, accept_f_ref;
|
|
|
|
|
// verilator lint_off CASEWITHX
|
|
|
|
|
always_comb
|
|
|
|
|
casez (cyc[3:0])
|
|
|
|
|
4'bxxx0: accept_f_out = 16'h0000; // X can never match in 2-state
|
|
|
|
|
4'b0001, 4'b0011, 4'b0101: accept_f_out = 16'h5555; // lists three values
|
|
|
|
|
default: accept_f_out = 16'h9999;
|
|
|
|
|
endcase
|
|
|
|
|
// verilator lint_on CASEWITHX
|
|
|
|
|
assign accept_f_ref = (cyc[3:0] == 4'd1 || cyc[3:0] == 4'd3 || cyc[3:0] == 4'd5)
|
|
|
|
|
? 16'h5555 : 16'h9999;
|
|
|
|
|
|
|
|
|
|
// Accept G: items assign different subsets of two outputs, with default values (and an
|
|
|
|
|
// unrelated output) set before the case.
|
|
|
|
|
logic [15:0] accept_g_out_0, accept_g_ref_0;
|
|
|
|
|
logic [15:0] accept_g_out_1, accept_g_ref_1;
|
|
|
|
|
logic [15:0] accept_g_out_2, accept_g_ref_2;
|
|
|
|
|
always_comb begin
|
|
|
|
|
accept_g_out_0 = 16'h0000;
|
|
|
|
|
accept_g_out_1 = 16'hffff;
|
|
|
|
|
accept_g_out_2 = 16'h3333; // not assigned in the case
|
|
|
|
|
case (cyc[3:0])
|
|
|
|
|
4'd0: accept_g_out_0 = 16'h0001;
|
|
|
|
|
4'd1: accept_g_out_1 = 16'h0002;
|
2026-06-19 03:56:02 +02:00
|
|
|
4'd2: begin
|
|
|
|
|
accept_g_out_0 = 16'h0004;
|
|
|
|
|
accept_g_out_1 = 16'h0008;
|
|
|
|
|
end
|
Optimize decoder case statements into lookup tables (#7795)
Recognize "decoder" case statements (where every case item only assigns
constants to a fixed set of left-hand sides) and replace them with a
single packed constant lookup table indexed by the case expression.
Small tables are materialized inline in the generated code, and are
always optimized. Larger ones are placed in the constant pool and only
optimized if deemed beneficial over branches.
While this slightly conflicts with V3Table, and is not worth that much
on it's own, there will be a follow up patch that converts more cases of
this form which will be much more valuable. This patch does the
necessary analysis and the simple table conversion when possible.
Split -fcase into -fcase-table (this new conversion) and -fcase-tree (the
existing bitwise branch-tree conversion); -fno-case is now an alias for
both.
Default branches, assignments preceding the case (used as default values),
casez wildcards, multiple and partial left-hand sides, and both blocking and
non-blocking assignments are handled. Cases that cannot be safely tabled (e.g.
non-exhaustive with no default, overlapping writes to one variable, or mixed
blocking/non-blocking assignments) fall back to the existing if/else lowering.
Consequently disabled re-inlining of constant pool variables in V3Const,
and rebuild the constant pool hash in V3Dead (previously we didn't
create constant pool entries early enough for this to matter)
2026-06-18 10:30:50 +02:00
|
|
|
4'd3: accept_g_out_0 = 16'h0010;
|
|
|
|
|
default: ;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
assign accept_g_ref_0 = (cyc[3:0] == 4'd0) ? 16'h0001 : (cyc[3:0] == 4'd2) ? 16'h0004
|
|
|
|
|
: (cyc[3:0] == 4'd3) ? 16'h0010 : 16'h0000;
|
|
|
|
|
assign accept_g_ref_1 = (cyc[3:0] == 4'd1) ? 16'h0002 : (cyc[3:0] == 4'd2) ? 16'h0008 : 16'hffff;
|
|
|
|
|
assign accept_g_ref_2 = 16'h3333;
|
|
|
|
|
|
|
|
|
|
// Accept H: unique0 enum case; the selector may hold an out-of-range value.
|
2026-06-19 03:56:02 +02:00
|
|
|
typedef enum logic [3:0] {
|
|
|
|
|
NE0,
|
|
|
|
|
NE1,
|
|
|
|
|
NE2,
|
|
|
|
|
NE3,
|
|
|
|
|
NE4
|
|
|
|
|
} ne_t;
|
Optimize decoder case statements into lookup tables (#7795)
Recognize "decoder" case statements (where every case item only assigns
constants to a fixed set of left-hand sides) and replace them with a
single packed constant lookup table indexed by the case expression.
Small tables are materialized inline in the generated code, and are
always optimized. Larger ones are placed in the constant pool and only
optimized if deemed beneficial over branches.
While this slightly conflicts with V3Table, and is not worth that much
on it's own, there will be a follow up patch that converts more cases of
this form which will be much more valuable. This patch does the
necessary analysis and the simple table conversion when possible.
Split -fcase into -fcase-table (this new conversion) and -fcase-tree (the
existing bitwise branch-tree conversion); -fno-case is now an alias for
both.
Default branches, assignments preceding the case (used as default values),
casez wildcards, multiple and partial left-hand sides, and both blocking and
non-blocking assignments are handled. Cases that cannot be safely tabled (e.g.
non-exhaustive with no default, overlapping writes to one variable, or mixed
blocking/non-blocking assignments) fall back to the existing if/else lowering.
Consequently disabled re-inlining of constant pool variables in V3Const,
and rebuild the constant pool hash in V3Dead (previously we didn't
create constant pool entries early enough for this to matter)
2026-06-18 10:30:50 +02:00
|
|
|
ne_t accept_h_in;
|
|
|
|
|
assign accept_h_in = ne_t'(cyc[3:0]);
|
|
|
|
|
logic [15:0] accept_h_out, accept_h_ref;
|
|
|
|
|
always_comb begin
|
|
|
|
|
accept_h_out = 16'hffff;
|
|
|
|
|
unique0 case (accept_h_in)
|
|
|
|
|
NE0: accept_h_out = 16'h0001;
|
|
|
|
|
NE1: accept_h_out = 16'h0002;
|
|
|
|
|
NE2: accept_h_out = 16'h0003;
|
|
|
|
|
NE3: accept_h_out = 16'h0004;
|
|
|
|
|
NE4: accept_h_out = 16'h0005;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
assign accept_h_ref = (cyc[3:0] == 4'd0) ? 16'h0001 : (cyc[3:0] == 4'd1) ? 16'h0002
|
|
|
|
|
: (cyc[3:0] == 4'd2) ? 16'h0003 : (cyc[3:0] == 4'd3) ? 16'h0004
|
|
|
|
|
: (cyc[3:0] == 4'd4) ? 16'h0005 : 16'hffff;
|
|
|
|
|
|
|
|
|
|
// The cases below are intentionally NOT converted to a lookup table.
|
|
|
|
|
|
|
|
|
|
// Reject A: too few distinct values, so the branch lowering is cheaper than a load.
|
|
|
|
|
logic [15:0] reject_a_out, reject_a_ref;
|
|
|
|
|
always_comb
|
|
|
|
|
case (cyc[3:0])
|
|
|
|
|
4'd0: reject_a_out = 16'h0001;
|
|
|
|
|
4'd1: reject_a_out = 16'h0002;
|
|
|
|
|
default: reject_a_out = 16'h00ff;
|
|
|
|
|
endcase
|
|
|
|
|
assign reject_a_ref = (cyc[3:0] == 4'd0) ? 16'h0001 : (cyc[3:0] == 4'd1) ? 16'h0002 : 16'h00ff;
|
|
|
|
|
|
|
|
|
|
// Reject B: a one-bit selector, too shallow to be worth a load.
|
|
|
|
|
logic [19:0] reject_b_out, reject_b_ref;
|
|
|
|
|
always_comb
|
|
|
|
|
case (cyc[0])
|
|
|
|
|
1'b0: reject_b_out = 20'h00001;
|
|
|
|
|
1'b1: reject_b_out = 20'h00002;
|
|
|
|
|
default: reject_b_out = 20'h00000;
|
|
|
|
|
endcase
|
|
|
|
|
assign reject_b_ref = cyc[0] ? 20'h00002 : 20'h00001;
|
|
|
|
|
|
|
|
|
|
// Reject C: a 12-bit selector, too wide to table.
|
|
|
|
|
logic [15:0] reject_c_out, reject_c_ref;
|
|
|
|
|
always_comb
|
|
|
|
|
case (cyc[11:0])
|
|
|
|
|
12'd0: reject_c_out = 16'h0001;
|
|
|
|
|
12'd1: reject_c_out = 16'h0002;
|
|
|
|
|
12'd2: reject_c_out = 16'h0004;
|
|
|
|
|
default: reject_c_out = 16'h0000;
|
|
|
|
|
endcase
|
|
|
|
|
assign reject_c_ref = (cyc[11:0] == 12'd0) ? 16'h0001
|
|
|
|
|
: (cyc[11:0] == 12'd1) ? 16'h0002
|
|
|
|
|
: (cyc[11:0] == 12'd2) ? 16'h0004 : 16'h0000;
|
|
|
|
|
|
|
|
|
|
// Reject D: a 17-bit selector, too wide to table.
|
|
|
|
|
logic [16:0] reject_d_in;
|
|
|
|
|
assign reject_d_in = cyc[16:0];
|
|
|
|
|
logic [15:0] reject_d_out, reject_d_ref;
|
|
|
|
|
// verilator lint_off CASEINCOMPLETE
|
|
|
|
|
always_comb begin
|
|
|
|
|
reject_d_out = 16'hbeef;
|
|
|
|
|
case (reject_d_in)
|
|
|
|
|
17'd0: reject_d_out = 16'h0001;
|
|
|
|
|
17'd1: reject_d_out = 16'h0002;
|
|
|
|
|
17'd2: reject_d_out = 16'h0004;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
// verilator lint_on CASEINCOMPLETE
|
|
|
|
|
assign reject_d_ref = (reject_d_in == 17'd0) ? 16'h0001
|
|
|
|
|
: (reject_d_in == 17'd1) ? 16'h0002
|
|
|
|
|
: (reject_d_in == 17'd2) ? 16'h0004 : 16'hbeef;
|
|
|
|
|
|
|
|
|
|
// Reject E: a whole output and a sub-range of it assigned in different items.
|
|
|
|
|
logic [7:0] reject_e_out, reject_e_ref;
|
|
|
|
|
always_comb begin
|
|
|
|
|
reject_e_out = 8'h00;
|
|
|
|
|
reject_e_out[3:0] = 4'h0;
|
|
|
|
|
case (cyc[1:0])
|
|
|
|
|
2'b00: reject_e_out = 8'haa; // assigns the whole output
|
|
|
|
|
2'b01: reject_e_out[3:0] = 4'h5; // assigns a sub-range of the same output
|
|
|
|
|
default: ;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
assign reject_e_ref = (cyc[1:0] == 2'd0) ? 8'haa : (cyc[1:0] == 2'd1) ? 8'h05 : 8'h00;
|
|
|
|
|
|
|
|
|
|
// Reject F: a sub-range's default value is overwritten by a later whole-output default
|
|
|
|
|
// before the case, so the sub-range's pre-case value is set elsewhere.
|
|
|
|
|
logic [31:0] reject_f_out, reject_f_ref;
|
|
|
|
|
always_comb begin
|
|
|
|
|
reject_f_out[15:0] = 16'h0005; // farther default for the sub-range
|
|
|
|
|
reject_f_out = 32'h0; // closer whole-output default overwrites the sub-range to 0
|
|
|
|
|
case (cyc[1:0])
|
|
|
|
|
2'b00: reject_f_out[15:0] = 16'habcd; // only the sub-range is assigned in the case
|
|
|
|
|
default: ;
|
|
|
|
|
endcase
|
|
|
|
|
end
|
|
|
|
|
assign reject_f_ref = (cyc[1:0] == 2'd0) ? 32'h0000abcd : 32'h00000000;
|
|
|
|
|
|
|
|
|
|
// Test driver/checker
|
|
|
|
|
always @(posedge clk) begin
|
|
|
|
|
`checkh(accept_a_out, accept_a_ref);
|
|
|
|
|
`checkh(accept_b_out, accept_b_ref);
|
|
|
|
|
`checkh(accept_c_out_0, accept_c_ref_0);
|
|
|
|
|
`checkh(accept_c_out_1, accept_c_ref_1);
|
|
|
|
|
`checkh(accept_c_out_2, accept_c_ref_2);
|
|
|
|
|
`checkh(accept_d_out_0, accept_d_ref_0);
|
|
|
|
|
`checkh(accept_d_out_1, accept_d_ref_1);
|
|
|
|
|
`checkh(accept_e_out, accept_e_ref);
|
|
|
|
|
`checkh(accept_f_out, accept_f_ref);
|
|
|
|
|
`checkh(accept_g_out_0, accept_g_ref_0);
|
|
|
|
|
`checkh(accept_g_out_1, accept_g_ref_1);
|
|
|
|
|
`checkh(accept_g_out_2, accept_g_ref_2);
|
|
|
|
|
`checkh(accept_h_out, accept_h_ref);
|
|
|
|
|
`checkh(reject_a_out, reject_a_ref);
|
|
|
|
|
`checkh(reject_b_out, reject_b_ref);
|
|
|
|
|
`checkh(reject_c_out, reject_c_ref);
|
|
|
|
|
`checkh(reject_d_out, reject_d_ref);
|
|
|
|
|
`checkh(reject_e_out, reject_e_ref);
|
|
|
|
|
`checkh(reject_f_out, reject_f_ref);
|
|
|
|
|
|
|
|
|
|
cyc <= cyc + 32'd1;
|
|
|
|
|
if (cyc == 32'd32) begin
|
|
|
|
|
$write("*-* All Finished *-*\n");
|
|
|
|
|
$finish;
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
endmodule
|