3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 00:55:32 +00:00

verilog: significant block scoping improvements

This change set contains a number of bug fixes and improvements related to
scoping and resolution in generate and procedural blocks. While many of the
frontend changes are interdependent, it may be possible bring the techmap
changes in under a separate PR.

Declarations within unnamed generate blocks previously encountered issues
because the data declarations were left un-prefixed, breaking proper scoping.
The LRM outlines behavior for generating names for unnamed generate blocks. The
original goal was to add this implicit labelling, but doing so exposed a number
of issues downstream. Additional testing highlighted other closely related scope
resolution issues, which have been fixed. This change also adds support for
block item declarations within unnamed blocks in SystemVerilog mode.

1. Unlabled generate blocks are now implicitly named according to the LRM in
   `label_genblks`, which is invoked at the beginning of module elaboration
2. The Verilog parser no longer wraps explicitly named generate blocks in a
   synthetic unnamed generate block to avoid creating extra hierarchy levels
   where they should not exist
3. The techmap phase now allows special control identifiers to be used outside
   of the topmost scope, which is necessary because such wires and cells often
   appear in unlabeled generate blocks, which now prefix the declarations within
4. Some techlibs required modifications because they relied on the previous
   invalid scope resolution behavior
5. `expand_genblock` has been simplified, now only expanding the outermost
   scope, completely deferring the inspection and elaboration of nested scopes;
   names are now resolved by looking in the innermost scope and stepping outward
6. Loop variables now always become localparams during unrolling, allowing them
   to be resolved and shadowed like any other identifier
7. Identifiers in synthetic function call scopes are now prefixed and resolved
   in largely the same manner as other blocks
     before: `$func$\func_01$tests/simple/scopes.blk.v:60$5$\blk\x`
      after: `\func_01$func$tests/simple/scopes.v:60$5.blk.x`
8. Support identifiers referencing a local generate scope nested more
   than 1 level deep, i.e. `B.C.x` while within generate scope `A`, or using a
   prefix of a current or parent scope, i.e. `B.C.D.x` while in `A.B`, `A.B.C`,
   or `A.B.C.D`
9. Variables can now be declared within unnamed blocks in SystemVerilog mode

Addresses the following issues: 656, 2423, 2493
This commit is contained in:
Zachary Snow 2021-01-27 13:30:22 -05:00
parent 98afe2b758
commit fe74b0cd95
33 changed files with 783 additions and 262 deletions

33
tests/simple/func_block.v Normal file
View file

@ -0,0 +1,33 @@
`default_nettype none
module top(inp, out1, out2, out3);
input wire [31:0] inp;
function automatic [31:0] func1;
input [31:0] inp;
reg [31:0] idx;
for (idx = 0; idx < 32; idx = idx + 1) begin : blk
func1[idx] = (idx & 1'b1) ^ inp[idx];
end
endfunction
function automatic [31:0] func2;
input [31:0] inp;
reg [31:0] idx;
for (idx = 0; idx < 32; idx = idx + 1) begin : blk
func2[idx] = (idx & 1'b1) ^ inp[idx];
end
endfunction
function automatic [31:0] func3;
localparam A = 32 - 1;
parameter B = 1 - 0;
input [31:0] inp;
func3[A:B] = inp[A:B];
endfunction
output wire [31:0] out1, out2, out3;
assign out1 = func1(inp);
assign out2 = func2(inp);
assign out3 = func3(inp);
endmodule

View file

@ -0,0 +1,25 @@
module top(
input wire [3:0] inp,
output wire [3:0] out1, out2
);
function automatic [3:0] pow_a;
input [3:0] base, exp;
begin
pow_a = 1;
if (exp > 0)
pow_a = base * pow_a(base, exp - 1);
end
endfunction
function automatic [3:0] pow_b;
input [3:0] base, exp;
begin
pow_b = 1;
if (exp > 0)
pow_b = base * pow_b(base, exp - 1);
end
endfunction
assign out1 = pow_a(inp, 3);
assign out2 = pow_b(2, 2);
endmodule

View file

@ -0,0 +1,41 @@
module top(inp, out1, out2);
input wire signed inp;
localparam WIDTH_A = 5;
function automatic [WIDTH_A-1:0] func1;
input reg [WIDTH_A-1:0] inp;
func1 = ~inp;
endfunction
wire [func1(0)-1:0] xc;
assign xc = 1'sb1;
wire [WIDTH_A-1:0] xn;
assign xn = func1(inp);
generate
if (1) begin : blk
localparam WIDTH_A = 6;
function automatic [WIDTH_A-1:0] func2;
input reg [WIDTH_A-1:0] inp;
func2 = ~inp;
endfunction
wire [func2(0)-1:0] yc;
assign yc = 1'sb1;
wire [WIDTH_A-1:0] yn;
assign yn = func2(inp);
localparam WIDTH_B = 7;
function automatic [WIDTH_B-1:0] func3;
input reg [WIDTH_B-1:0] inp;
func3 = ~inp;
endfunction
wire [func3(0)-1:0] zc;
assign zc = 1'sb1;
wire [WIDTH_B-1:0] zn;
assign zn = func3(inp);
end
endgenerate
output wire [1023:0] out1, out2;
assign out1 = {xc, 1'b0, blk.yc, 1'b0, blk.zc};
assign out2 = {xn, 1'b0, blk.yn, 1'b0, blk.zn};
endmodule

View file

@ -0,0 +1,27 @@
`default_nettype none
module top1;
generate
if (1) begin : foo
if (1) begin : bar
wire x;
end
assign bar.x = 1;
wire y;
end
endgenerate
endmodule
module top2;
genvar i;
generate
if (1) begin : foo
wire x;
for (i = 0; i < 1; i = i + 1) begin : foo
if (1) begin : foo
assign x = 1;
end
end
end
endgenerate
endmodule

View file

@ -0,0 +1,21 @@
`default_nettype none
module top(output wire x);
generate
if (1) begin : Z
if (1) begin : A
wire x;
if (1) begin : B
wire x;
if (1) begin : C
wire x;
assign B.x = 0;
wire z = A.B.C.x;
end
assign A.x = A.B.C.x;
end
assign B.C.x = B.x;
end
end
endgenerate
assign x = Z.A.x;
endmodule

View file

@ -0,0 +1,18 @@
`default_nettype none
module top(
output wire out1,
output wire out2
);
generate
if (1) begin : outer
if (1) begin : foo
wire x = 0;
if (1) begin : foo
wire x = 1;
assign out1 = foo.x;
end
assign out2 = foo.x;
end
end
endgenerate
endmodule

View file

@ -260,3 +260,66 @@ module gen_test8;
`ASSERT(gen_test8.A.C.x == 1)
`ASSERT(gen_test8.A.B.x == 0)
endmodule
// ------------------------------------------
module gen_test9;
// `define VERIFY
`ifdef VERIFY
`define ASSERT(expr) assert property (expr);
`else
`define ASSERT(expr)
`endif
wire [1:0] w = 2'b11;
generate
begin : A
wire [1:0] x;
begin : B
wire [1:0] y = 2'b00;
`ASSERT(w == 3)
`ASSERT(x == 2)
`ASSERT(y == 0)
`ASSERT(A.x == 2)
`ASSERT(A.C.z == 1)
`ASSERT(A.B.y == 0)
`ASSERT(gen_test9.w == 3)
`ASSERT(gen_test9.A.x == 2)
`ASSERT(gen_test9.A.C.z == 1)
`ASSERT(gen_test9.A.B.y == 0)
end
begin : C
wire [1:0] z = 2'b01;
`ASSERT(w == 3)
`ASSERT(x == 2)
`ASSERT(z == 1)
`ASSERT(A.x == 2)
`ASSERT(A.C.z == 1)
`ASSERT(A.B.y == 0)
`ASSERT(gen_test9.w == 3)
`ASSERT(gen_test9.A.x == 2)
`ASSERT(gen_test9.A.C.z == 1)
`ASSERT(gen_test9.A.B.y == 0)
end
assign x = B.y ^ 2'b11 ^ C.z;
`ASSERT(x == 2)
`ASSERT(A.x == 2)
`ASSERT(A.C.z == 1)
`ASSERT(A.B.y == 0)
`ASSERT(gen_test9.w == 3)
`ASSERT(gen_test9.A.x == 2)
`ASSERT(gen_test9.A.C.z == 1)
`ASSERT(gen_test9.A.B.y == 0)
end
endgenerate
`ASSERT(w == 3)
`ASSERT(A.x == 2)
`ASSERT(A.C.z == 1)
`ASSERT(A.B.y == 0)
`ASSERT(gen_test9.w == 3)
`ASSERT(gen_test9.A.x == 2)
`ASSERT(gen_test9.A.C.z == 1)
`ASSERT(gen_test9.A.B.y == 0)
endmodule

View file

@ -0,0 +1,11 @@
module top(out);
output integer out;
initial begin
integer i;
for (i = 0; i < 5; i = i + 1)
if (i == 0)
out = 1;
else
out += 2 ** i;
end
endmodule

View file

@ -0,0 +1,15 @@
module top(out);
genvar i;
generate
for (i = 0; i < 2; i = i + 1) begin : loop
localparam j = i + 1;
if (1) begin : blk
localparam i = j + 1;
wire [i:0] x;
assign x = 1'sb1;
end
end
endgenerate
output wire [63:0] out;
assign out = {loop[0].blk.x, loop[1].blk.x};
endmodule

View file

@ -0,0 +1,27 @@
`default_nettype none
module top;
generate
if (1) begin
wire t;
begin : foo
wire x;
end
wire u;
end
begin : bar
wire x;
wire y;
begin : baz
wire x;
wire z;
end
end
endgenerate
assign genblk1.t = 1;
assign genblk1.foo.x = 1;
assign genblk1.u = 1;
assign bar.x = 1;
assign bar.y = 1;
assign bar.baz.x = 1;
assign bar.baz.z = 1;
endmodule

View file

@ -0,0 +1,14 @@
`default_nettype none
module top;
generate
if (1) begin
wire x;
genvar i;
for (i = 0; i < 1; i = i + 1) begin
if (1) begin
assign x = 1;
end
end
end
endgenerate
endmodule

View file

@ -0,0 +1,17 @@
module top(z);
output integer z;
initial begin
integer x;
x = 1;
begin
integer y;
y = x + 1;
begin
integer z;
z = y + 1;
y = z + 1;
end
z = y + 1;
end
end
endmodule

View file

@ -1,13 +1,17 @@
module test(x, y, z);
`default_nettype none
module test;
localparam OFF = 0;
generate
if (OFF) ;
else input x;
if (!OFF) input y;
else wire x;
if (!OFF) wire y;
else ;
if (OFF) ;
else ;
if (OFF) ;
input z;
wire z;
endgenerate
assign genblk1.x = 0;
assign genblk2.y = 0;
assign z = 0;
endmodule

View file

@ -1,4 +1,4 @@
read_verilog gen_if_null.v
select -assert-count 1 test/x
select -assert-count 1 test/y
select -assert-count 1 test/genblk1.x
select -assert-count 1 test/genblk2.y
select -assert-count 1 test/z

12
tests/verilog/bug2493.ys Normal file
View file

@ -0,0 +1,12 @@
logger -expect error "Failed to detect width for identifier \\genblk1\.y!" 1
read_verilog <<EOT
module top1;
wire x;
generate
if (1) begin
mod y();
assign x = y;
end
endgenerate
endmodule
EOT

21
tests/verilog/bug656.v Normal file
View file

@ -0,0 +1,21 @@
module top #(
parameter WIDTH = 6
) (
input [WIDTH-1:0] a_i,
input [WIDTH-1:0] b_i,
output [WIDTH-1:0] z_o
);
genvar g;
generate
for (g = 0; g < WIDTH; g = g + 1) begin
if (g > 2) begin
wire tmp;
assign tmp = a_i[g] || b_i[g];
assign z_o[g] = tmp;
end
else begin
assign z_o[g] = a_i[g] && b_i[g];
end
end
endgenerate
endmodule

13
tests/verilog/bug656.ys Normal file
View file

@ -0,0 +1,13 @@
read_verilog bug656.v
select -assert-count 1 top/a_i
select -assert-count 1 top/b_i
select -assert-count 1 top/z_o
select -assert-none top/genblk1[0].genblk1.tmp
select -assert-none top/genblk1[1].genblk1.tmp
select -assert-none top/genblk1[2].genblk1.tmp
select -assert-count 1 top/genblk1[3].genblk1.tmp
select -assert-count 1 top/genblk1[4].genblk1.tmp
select -assert-count 1 top/genblk1[5].genblk1.tmp

View file

@ -0,0 +1,26 @@
module top;
parameter YES = 1;
generate
if (YES) wire y;
else wire n;
if (!YES) wire n;
else wire y;
case (YES)
1: wire y;
0: wire n;
endcase
case (!YES)
0: wire y;
1: wire n;
endcase
if (YES) wire y;
else wire n;
if (!YES) wire n;
else wire y;
endgenerate
endmodule

View file

@ -0,0 +1,15 @@
read_verilog genblk_case.v
select -assert-count 0 top/genblk1.n
select -assert-count 0 top/genblk2.n
select -assert-count 0 top/genblk3.n
select -assert-count 0 top/genblk4.n
select -assert-count 0 top/genblk5.n
select -assert-count 0 top/genblk6.n
select -assert-count 1 top/genblk1.y
select -assert-count 1 top/genblk2.y
select -assert-count 1 top/genblk3.y
select -assert-count 1 top/genblk4.y
select -assert-count 1 top/genblk5.y
select -assert-count 1 top/genblk6.y

View file

@ -0,0 +1,11 @@
logger -expect error "Identifier `\\y' is implicitly declared and `default_nettype is set to none" 1
read_verilog <<EOT
`default_nettype none
module top1;
wire x;
generate
if (1) wire y;
endgenerate
assign x = y;
endmodule
EOT

View file

@ -0,0 +1,28 @@
read_verilog <<EOT
module top;
initial begin : blk
integer x;
end
endmodule
EOT
delete
read_verilog -sv <<EOT
module top;
initial begin
integer x;
end
endmodule
EOT
delete
logger -expect error "Local declaration in unnamed block is only supported in SystemVerilog mode!" 1
read_verilog <<EOT
module top;
initial begin
integer x;
end
endmodule
EOT

View file

@ -0,0 +1,39 @@
// This test is taken directly from Section 27.6 of IEEE 1800-2017
module top;
parameter genblk2 = 0;
genvar i;
// The following generate block is implicitly named genblk1
if (genblk2) logic a; // top.genblk1.a
else logic b; // top.genblk1.b
// The following generate block is implicitly named genblk02
// as genblk2 is already a declared identifier
if (genblk2) logic a; // top.genblk02.a
else logic b; // top.genblk02.b
// The following generate block would have been named genblk3
// but is explicitly named g1
for (i = 0; i < 1; i = i + 1) begin : g1 // block name
// The following generate block is implicitly named genblk1
// as the first nested scope inside g1
if (1) logic a; // top.g1[0].genblk1.a
end
// The following generate block is implicitly named genblk4 since
// it belongs to the fourth generate construct in scope "top".
// The previous generate block would have been
// named genblk3 if it had not been explicitly named g1
for (i = 0; i < 1; i = i + 1)
// The following generate block is implicitly named genblk1
// as the first nested generate block in genblk4
if (1) logic a; // top.genblk4[0].genblk1.a
// The following generate block is implicitly named genblk5
if (1) logic a; // top.genblk5.a
endmodule

View file

@ -0,0 +1,8 @@
read_verilog -sv unnamed_genblk.sv
select -assert-count 0 top/genblk1.a
select -assert-count 1 top/genblk02.b
select -assert-count 0 top/genblk1.a
select -assert-count 1 top/genblk02.b
select -assert-count 1 top/g1[0].genblk1.a
select -assert-count 1 top/genblk4[0].genblk1.a
select -assert-count 1 top/genblk5.a