verilator/test_regress/t/t_dfg_synthesis.v

506 lines
12 KiB
Systemverilog
Raw Normal View History

Optimize complex combinational logic in DFG (#6298) This patch adds DfgLogic, which is a vertex that represents a whole, arbitrarily complex combinational AstAlways or AstAssignW in the DfgGraph. Implementing this requires computing the variables live at entry to the AstAlways (variables read by the block), so there is a new ControlFlowGraph data structure and a classical data-flow analysis based live variable analysis to do that at the variable level (as opposed to bit/element level). The actual CFG construction and live variable analysis is best effort, and might fail for currently unhandled constructs or data types. This can be extended later. V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices. The DfgLogic are then subsequently synthesized into primitive operations by the new V3DfgSynthesize pass, which is a combination of the old V3DfgAstToDfg conversion and new code to handle AstAlways blocks with complex flow control. V3DfgSynthesize by default will synthesize roughly the same constructs as V3DfgAstToDfg used to handle before, plus any logic that is part of a combinational cycle within the DfgGraph. This enables breaking up these cycles, for which there are extensions to V3DfgBreakCycles in this patch as well. V3DfgSynthesize will then delete all non synthesized or non synthesizable DfgLogic vertices and the rest of the Dfg pipeline is identical, with minor changes to adjust for the changed representation. Because with this change we can now eliminate many more UNOPTFLAT, DFG has been disabled in all the tests that specifically target testing the scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Geza Lore.
// SPDX-License-Identifier: CC0-1.0
`define signal(name, expr) wire [$bits(expr)-1:0] ``name = expr
module t (
`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py
rand_a, rand_b, srand_a, srand_b
);
`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py
input rand_a;
input rand_b;
input srand_a;
input srand_b;
wire logic [63:0] rand_a;
wire logic [63:0] rand_b;
wire logic signed [63:0] srand_a;
wire logic signed [63:0] srand_b;
//////////////////////////////////////////////////////////////////////////
logic [2:0] simple;
always_comb begin
simple[0] = rand_a[0];
simple[1] = rand_a[1];
simple[2] = rand_a[2];
end
`signal(SIMPLE, simple);
logic [1:0] reassign;
always_comb begin
reassign[0] = rand_a[0];
reassign[0] = ~rand_a[0];
reassign[1] = rand_a[1];
reassign[1] = ~rand_a[1];
end
`signal(REASSIGN, reassign);
logic [1:0] use_intermediate_a;
logic [1:0] use_intermediate_b;
always_comb begin
use_intermediate_a[0] = rand_a[0];
use_intermediate_b[0] = ~use_intermediate_a[0];
use_intermediate_a[1] = rand_a[1];
use_intermediate_a[0] = ~rand_a[0];
use_intermediate_b[1] = ~use_intermediate_a[1];
use_intermediate_a[1] = ~rand_a[1];
end
`signal(USE_INTERMEDIATE, {use_intermediate_a, use_intermediate_b});
logic [2:0] self_circular;
always_comb begin
self_circular[0] = rand_a[0];
self_circular[1] = ~self_circular[0];
self_circular[2] = ~self_circular[1];
end
`signal(SELF_CIRCULAR, self_circular);
logic [2:0] part_circular;
always_comb begin
part_circular[0] = rand_a[0];
part_circular[1] = ~part_circular[0];
end
// part_circular[2] deliberately undriven!
`signal(PART_CIRCULAR, part_circular);
logic [3:0] split_circular;
always_comb begin
split_circular[0] = rand_a[0];
split_circular[2] = rand_a[1];
end
always_comb begin
split_circular[1] = ~split_circular[0];
split_circular[3] = ~split_circular[2];
end
`signal(SPLIT_CIRCULAR, split_circular);
logic [3:0] conditional_a;
always_comb begin
conditional_a = 4'd0;
if (rand_a[0]) begin
conditional_a = rand_b[3:0];
end else begin
conditional_a = ~rand_b[3:0];
end
end
`signal(CONDITONAL_A, conditional_a);
logic [3:0] conditional_b;
always_comb begin
conditional_b = 4'd0;
if (rand_a[0]) begin
conditional_b = rand_b[3:0];
end
end
`signal(CONDITONAL_B, conditional_b);
// verilator lint_off LATCH
logic [3:0] conditional_c;
always_comb begin // nosynth
if (rand_a[0]) begin
conditional_c = rand_b[3:0];
end
if (~rand_a[0]) begin
conditional_c = ~rand_b[3:0];
end
end
`signal(CONDITONAL_C, conditional_c);
// verilator lint_on LATCH
logic [3:0] conditional_d;
always_comb begin
if (rand_a[0]) begin
conditional_d = rand_b[3:0];
end else if (rand_a[1]) begin
conditional_d = ~rand_b[3:0];
end else begin
conditional_d = rand_b[7:4];
end
end
`signal(CONDITONAL_D, conditional_d);
logic [3:0] conditional_e;
always_comb begin
conditional_e = 4'd0;
if (rand_a[0]) begin
conditional_e = rand_b[3:0];
end else begin
if (rand_a[1]) begin
conditional_e = rand_b[3:0];
end else begin
conditional_e = rand_b[7:4];
end
conditional_e = ~conditional_e;
end
end
`signal(CONDITONAL_E, conditional_e);
logic condigional_f;
always_comb begin
if (rand_b[0]) begin
condigional_f = 1'h1;
if (rand_b[1]) begin
condigional_f = rand_a[0];
end
end else begin
condigional_f = 1'b0;
end
end
`signal(CONDITONAL_F, condigional_f);
logic [2:0] conditional_g;
always_comb begin
if (rand_a[0]) begin
if (rand_a[1]) begin
if (rand_a[2]) begin
conditional_g = 3'b111;
end else begin
conditional_g = 3'b011;
end
end else begin
if (rand_a[2]) begin
conditional_g = 3'b101;
end else begin
conditional_g = 3'b001;
end
end
end else begin
if (rand_a[1]) begin
if (rand_a[2]) begin
conditional_g = 3'b110;
end else begin
conditional_g = 3'b010;
end
end else begin
if (rand_a[2]) begin
conditional_g = 3'b100;
end else begin
conditional_g = 3'b000;
end
end
end
end
`signal(CONDITONAL_G, conditional_g);
logic [2:0] conditional_h;
always_comb begin
if (rand_a[0]) begin
if (rand_a[1]) begin
conditional_h = 3'b011;
if (rand_a[2]) begin
conditional_h = 3'b111;
end
end else begin
conditional_h = 3'b001;
if (rand_a[2]) begin
conditional_h = 3'b101;
end
end
end else begin
if (rand_a[1]) begin
conditional_h = 3'b010;
if (rand_a[2]) begin
conditional_h = 3'b110;
end
end else begin
conditional_h = 3'b000;
if (rand_a[2]) begin
conditional_h = 3'b100;
end
end
end
end
`signal(CONDITONAL_H, conditional_h);
logic [2:0] conditional_i;
always_comb begin // Dumbass trailing zeroes count
do begin
conditional_i = 3'd0;
if (rand_a[0]) break;
conditional_i = 3'd1;
if (rand_a[1]) break;
conditional_i = 3'd2;
if (rand_a[2]) break;
conditional_i = 3'd3;
if (rand_a[3]) break;
conditional_i = 3'd4;
end while (0);
end
`signal(CONDITONAL_I, conditional_i);
logic [2:0] conditional_j;
always_comb begin // Even more dumbass trailing ones count
do begin
conditional_j = 3'd0;
if (rand_a[0]) begin
conditional_j = 3'd1;
end else begin
break;
end
if (rand_a[1]) begin
conditional_j = 3'd2;
end else begin
break;
end
if (rand_a[2]) begin
conditional_j = 3'd3;
end else begin
break;
end
if (rand_a[3]) begin
conditional_j = 3'd4;
end
end while (0);
end
`signal(CONDITONAL_J, conditional_j);
logic [2:0] conditional_k;
always_comb begin
if (rand_b[0]) begin
do begin
conditional_k = 3'd0;
if (rand_a[0]) break;
conditional_k = 3'd1;
if (rand_a[1]) break;
conditional_k = 3'd2;
if (rand_a[2]) break;
conditional_k = 3'd3;
if (rand_a[3]) break;
conditional_k = 3'd4;
end while (0);
end else begin
do begin
conditional_k = 3'd0;
if (rand_a[0]) begin
conditional_k = 3'd1;
end else begin
break;
end
if (rand_a[1]) begin
conditional_k = 3'd2;
end else begin
break;
end
if (rand_a[2]) begin
conditional_k = 3'd3;
end else begin
break;
end
if (rand_a[3]) begin
conditional_k = 3'd4;
end
end while (0);
end
end
`signal(CONDITONAL_K, conditional_k);
logic [1:0] conditional_l_a;
logic [1:0] conditional_l_b;
always_comb begin
do begin
conditional_l_a = 2'd0;
if (rand_a[1:0] == 2'd0) break;
conditional_l_a = 2'd1;
if (rand_a[1:0] == 2'd1) break;
conditional_l_a = 2'd2;
if (rand_a[1:0] == 2'd2) break;
conditional_l_a = 2'd3;
end while (0);
do begin
conditional_l_b = 2'd0;
if (rand_b[1:0] == 2'd0) break;
conditional_l_b = 2'd1;
if (rand_b[1:0] == 2'd1) break;
conditional_l_b = 2'd2;
if (rand_b[1:0] == 2'd2) break;
conditional_l_b = 2'd3;
end while (0);
end
`signal(CONDITONAL_L, {conditional_l_b, conditional_l_a});
Optimize complex combinational logic in DFG (#6298) This patch adds DfgLogic, which is a vertex that represents a whole, arbitrarily complex combinational AstAlways or AstAssignW in the DfgGraph. Implementing this requires computing the variables live at entry to the AstAlways (variables read by the block), so there is a new ControlFlowGraph data structure and a classical data-flow analysis based live variable analysis to do that at the variable level (as opposed to bit/element level). The actual CFG construction and live variable analysis is best effort, and might fail for currently unhandled constructs or data types. This can be extended later. V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices. The DfgLogic are then subsequently synthesized into primitive operations by the new V3DfgSynthesize pass, which is a combination of the old V3DfgAstToDfg conversion and new code to handle AstAlways blocks with complex flow control. V3DfgSynthesize by default will synthesize roughly the same constructs as V3DfgAstToDfg used to handle before, plus any logic that is part of a combinational cycle within the DfgGraph. This enables breaking up these cycles, for which there are extensions to V3DfgBreakCycles in this patch as well. V3DfgSynthesize will then delete all non synthesized or non synthesizable DfgLogic vertices and the rest of the Dfg pipeline is identical, with minor changes to adjust for the changed representation. Because with this change we can now eliminate many more UNOPTFLAT, DFG has been disabled in all the tests that specifically target testing the scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
logic [7:0] partial_conditional_a;
always_comb begin
partial_conditional_a[1:0] = 2'd0;
if (rand_a[0]) begin
partial_conditional_a[0] = rand_b[0];
end else begin
partial_conditional_a[1] = rand_b[1];
end
partial_conditional_a[4:3] = rand_b[4:3];
end
`signal(PARTIAL_CONDITONAL_A, partial_conditional_a);
logic [3:0] partial_conditional_b;
always_comb begin
partial_conditional_b[1:0] = 2'd0;
if (rand_a[0]) begin
partial_conditional_b[0] = rand_b[0];
end
if (rand_a[1]) begin
partial_conditional_b[1] = rand_b[1];
end
end
`signal(PARTIAL_CONDITONAL_B, partial_conditional_b);
logic [3:0] becomes_full;
always_comb begin
becomes_full[2:0] = rand_a[2:0];
becomes_full[3] = ~rand_a[3];
if (rand_b[0]) begin
becomes_full = ~becomes_full;
end
end
`signal(BECOMES_FULL, becomes_full);
// verilator lint_off LATCH
logic [3:0] latch_a;
logic [3:0] latch_b;
always_comb begin // nosynth
if (rand_b[0]) begin
latch_a[3:1] = ~rand_a[3:1];
end
latch_b = latch_a;
end
assign latch_a[0] = rand_a[0];
`signal(LATCH, latch_b);
// verilator lint_on LATCH
// verilator lint_off MULTIDRIVEN
logic static_temporary_a;
logic static_temporary_b;
logic static_temporary_tmp;
always_comb begin // revert
static_temporary_tmp = rand_a[0];
static_temporary_a = ~static_temporary_tmp;
end
always_comb begin // revert
static_temporary_tmp = static_temporary_a;
static_temporary_b = ~static_temporary_tmp;
end
// verilator lint_on MULTIDRIVEN
`signal(STATIC_TEMPORARY, {static_temporary_tmp, static_temporary_b, static_temporary_a, rand_a[0]});
logic [2:0] partial_temporary_a;
logic [2:0] partial_temporary_tmp;
always_comb begin
partial_temporary_tmp[2] = rand_a[3];
partial_temporary_tmp[1] = rand_a[2];
partial_temporary_tmp[0] = rand_a[1];
partial_temporary_a = partial_temporary_tmp;
end
`signal(PARTIAL_TEMPORARY, partial_temporary_a);
logic circular_0_x;
logic circular_0_y;
logic circular_0_z;
always_comb begin
circular_0_x = 1'b0;
circular_0_y = 1'd0;
if (rand_b[0]) begin
circular_0_x = 1'b1;
if (circular_0_z) begin
circular_0_y = 1'd1;
end
end
end
assign circular_0_z = circular_0_x & rand_a[0];
`signal(CIRCULAR_0, {circular_0_x, circular_0_y, circular_0_z});
logic [2:0] nsp_a; // Non series-parallel CFG
always_comb begin
do begin
nsp_a = 3'd0; // BB 0 -> BB 1 / BB 2
if (rand_a[1:0] == 2'd0) begin
nsp_a = 3'd1; // BB 1 -> BB 4
end else begin
nsp_a = 3'd2; // BB 2 -> BB 3 / BB 4
if (rand_a[1:0] == 2'd1) begin
nsp_a = 3'd3; // BB3 -> BB 5
break;
end
end
nsp_a = 3'd4; // BB 4 -> BB 5
end while (0);
//nsp_a = 3'd5; // BB 5
end
`signal(NSP_A, nsp_a);
logic [2:0] nsp_b; // Non series-parallel CFG
always_comb begin
do begin
nsp_b = 3'd0;
if (rand_a[1:0] == 2'd0) begin
nsp_b = 3'd1;
end else begin
nsp_b = 3'd2;
if (rand_a[1:0] == 2'd1) begin
nsp_b = 3'd3;
break;
end else begin
nsp_b = 3'd4;
if (rand_a[1:0] == 2'd2) begin
nsp_b = 3'd5;
end else begin
nsp_b = 3'd6;
break;
end
end
end
nsp_b = 3'd7;
end while (0);
end
`signal(NSP_B, nsp_b);
logic [2:0] part_sp_a; // Contains series-parallel sub-graph CFG
always_comb begin
do begin
part_sp_a = 3'd0;
if (rand_a[0]) begin
part_sp_a = 3'd1;
if (rand_a[1]) begin
part_sp_a = 3'd2;
end
end else begin
part_sp_a = 3'd3;
if (rand_a[2]) begin
part_sp_a = 3'd4;
if (rand_a[3]) begin
part_sp_a = 3'd5;
end
break;
end
end
part_sp_a = 3'd6;
if (rand_a[4]) begin
part_sp_a = 3'd7;
end
end while (0);
end
`signal(PART_SP_A, part_sp_a);
logic [1:0] both_break;
always_comb begin
do begin
if (rand_a[0]) begin
both_break = 2'd0;
break;
end else begin
both_break = 2'd1;
break;
end
// Unreachable
if (rand_a[1]) begin
both_break = 2'd2;
end
end while(0);
end
`signal(BOTH_BREAK, both_break);
Optimize complex combinational logic in DFG (#6298) This patch adds DfgLogic, which is a vertex that represents a whole, arbitrarily complex combinational AstAlways or AstAssignW in the DfgGraph. Implementing this requires computing the variables live at entry to the AstAlways (variables read by the block), so there is a new ControlFlowGraph data structure and a classical data-flow analysis based live variable analysis to do that at the variable level (as opposed to bit/element level). The actual CFG construction and live variable analysis is best effort, and might fail for currently unhandled constructs or data types. This can be extended later. V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices. The DfgLogic are then subsequently synthesized into primitive operations by the new V3DfgSynthesize pass, which is a combination of the old V3DfgAstToDfg conversion and new code to handle AstAlways blocks with complex flow control. V3DfgSynthesize by default will synthesize roughly the same constructs as V3DfgAstToDfg used to handle before, plus any logic that is part of a combinational cycle within the DfgGraph. This enables breaking up these cycles, for which there are extensions to V3DfgBreakCycles in this patch as well. V3DfgSynthesize will then delete all non synthesized or non synthesizable DfgLogic vertices and the rest of the Dfg pipeline is identical, with minor changes to adjust for the changed representation. Because with this change we can now eliminate many more UNOPTFLAT, DFG has been disabled in all the tests that specifically target testing the scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
endmodule