Skip to content

Commit fe49c46

Browse files
authored
Merge pull request #386 from pllab/verilog-warning-on-reset
Add add_reset to verilog test bench generation, and add check for existing `rst` wire.
2 parents d8cd0c9 + 4471a48 commit fe49c46

File tree

2 files changed

+248
-45
lines changed

2 files changed

+248
-45
lines changed

pyrtl/importexport.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,11 @@ def output_to_verilog(dest_file, add_reset=True, block=None):
432432
file = dest_file
433433
internal_names = _VerilogSanitizer('_ver_out_tmp_')
434434

435+
if add_reset: # True or 'asynchronous'
436+
if block.get_wirevector_by_name('rst') is not None:
437+
raise PyrtlError("Found a user-defined wire named 'rst'. Pass in "
438+
"'add_reset=False' to use your existing reset logic.")
439+
435440
for wire in block.wirevector_set:
436441
internal_names.make_valid_string(wire.name)
437442

@@ -672,7 +677,7 @@ def _to_verilog_footer(file):
672677

673678

674679
def output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=None,
675-
vcd="waveform.vcd", cmd=None, block=None):
680+
vcd="waveform.vcd", cmd=None, add_reset=True, block=None):
676681
""" Output a Verilog testbench for the block/inputs used in the simulation trace.
677682
678683
:param dest_file: an open file to which the test bench will be printed.
@@ -692,6 +697,16 @@ def output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=
692697
specific values out during testbench evaluation (e.g. cmd='$display("%d", out);'
693698
will instruct the testbench to print the value of 'out' every cycle which can then
694699
be compared easy with a reference).
700+
:param add_reset: If reset logic should be added. Allowable options are:
701+
False (meaning no reset logic is added), True (default, for adding synchronous
702+
reset logic), and 'asynchronous' (for adding asynchronous reset logic).
703+
The value passed in here should match the argument passed to `output_to_verilog()`.
704+
:param block: Block containing design to test.
705+
706+
If `add_reset` is not False, a `rst` wire is added and will passed as an input to the
707+
instantiated toplevel module. The `rst` wire will be held low in the testbench, because
708+
initialization here occurs via the `initial` block. It is provided for consistency with
709+
`output_to_verilog()`.
695710
696711
The test bench does not return any values.
697712
@@ -707,8 +722,18 @@ def output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=
707722
output_verilog_testbench(fp, sim.tracer, vcd=None, cmd='$display("%d", out);')
708723
709724
"""
725+
if not isinstance(add_reset, bool):
726+
if add_reset != 'asynchronous':
727+
raise PyrtlError("Invalid add_reset option %s. Acceptable options are "
728+
"False, True, and 'asynchronous'")
729+
710730
block = working_block(block)
711731

732+
if add_reset: # True or 'asynchronous'
733+
if block.get_wirevector_by_name('rst') is not None:
734+
raise PyrtlError("Found a user-defined wire named 'rst'. Pass in "
735+
"'add_reset=False' to use your existing reset logic.")
736+
712737
inputs, outputs, registers, wires, memories = _verilog_block_parts(block)
713738

714739
ver_name = _VerilogSanitizer('_ver_out_tmp_')
@@ -760,6 +785,8 @@ def default_value():
760785

761786
# Declare all block inputs as reg
762787
print(' reg clk;', file=dest_file)
788+
if add_reset:
789+
print(' reg rst;', file=dest_file)
763790
for w in name_sorted(inputs):
764791
print(' reg{:s} {:s};'.format(_verilog_vector_decl(w), ver_name[w.name]),
765792
file=dest_file)
@@ -775,6 +802,8 @@ def default_value():
775802

776803
# Instantiate logic block
777804
io_list = ['clk'] + name_list(name_sorted(inputs)) + name_list(name_sorted(outputs))
805+
if add_reset:
806+
io_list.insert(1, 'rst')
778807
io_list_str = ['.{0:s}({0:s})'.format(w) for w in io_list]
779808
print(' toplevel block({:s});\n'.format(', '.join(io_list_str)), file=dest_file)
780809

@@ -792,6 +821,8 @@ def default_value():
792821

793822
# Initialize clk, and all the registers and memories
794823
print(' clk = 0;', file=dest_file)
824+
if add_reset:
825+
print(' rst = 0;', file=dest_file)
795826
for r in name_sorted(registers):
796827
print(' block.%s = %d;' % (ver_name[r.name], init_regvalue(r)), file=dest_file)
797828
for m in sorted(memories, key=lambda m: m.id):

tests/test_importexport.py

Lines changed: 216 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,55 +1281,52 @@ def test_blif_with_clock_passing(self):
12811281
"""
12821282

12831283

1284-
verilog_test_bench = """\
1285-
module tb();
1286-
reg clk;
1287-
reg[1:0] a100;
1288-
reg[3:0] w1;
1289-
reg[2:0] w12;
1290-
wire[1:0] out1;
1291-
wire[8:0] out10;
1292-
1293-
integer tb_iter;
1294-
toplevel block(.clk(clk), .a100(a100), .w1(w1), .w12(w12), .out1(out1), .out10(out10));
1295-
1296-
always
1297-
#5 clk = ~clk;
1298-
1299-
initial begin
1300-
$dumpfile ("waveform.vcd");
1301-
$dumpvars;
1284+
verilog_custom_reset = """\
1285+
// Generated automatically via PyRTL
1286+
// As one initial test of synthesis, map to FPGA with:
1287+
// yosys -p "synth_xilinx -top toplevel" thisfile.v
13021288
1303-
clk = 0;
1304-
block.r1 = 2;
1305-
block.r2 = 3;
1306-
block.tmp13 = 0;
1307-
for (tb_iter = 0; tb_iter < 32; tb_iter++) begin block.mem_0[tb_iter] = 0; end
1308-
block.mem_0[2] = 9;
1309-
block.mem_0[9] = 12;
1310-
a100 = 2'd0;
1311-
w1 = 4'd0;
1312-
w12 = 3'd0;
1289+
module toplevel(clk, rst);
1290+
input clk;
1291+
input rst;
13131292
1314-
#10
1315-
a100 = 2'd1;
1316-
w1 = 4'd4;
1317-
w12 = 3'd1;
1293+
reg[3:0] r;
13181294
1319-
#10
1320-
a100 = 2'd3;
1321-
w1 = 4'd2;
1322-
w12 = 3'd7;
1295+
wire const_0_1;
1296+
wire const_1_0;
1297+
wire const_2_0;
1298+
wire const_3_0;
1299+
wire[2:0] tmp0;
1300+
wire[3:0] tmp1;
1301+
wire[4:0] tmp2;
1302+
wire[3:0] tmp3;
1303+
wire[4:0] tmp4;
1304+
wire[4:0] tmp5;
1305+
wire[3:0] tmp6;
13231306
1324-
#10
1325-
a100 = 2'd2;
1326-
w1 = 4'd3;
1327-
w12 = 3'd4;
1307+
// Combinational
1308+
assign const_0_1 = 1;
1309+
assign const_1_0 = 0;
1310+
assign const_2_0 = 0;
1311+
assign const_3_0 = 0;
1312+
assign tmp0 = {const_1_0, const_1_0, const_1_0};
1313+
assign tmp1 = {tmp0, const_0_1};
1314+
assign tmp2 = r + tmp1;
1315+
assign tmp3 = {const_3_0, const_3_0, const_3_0, const_3_0};
1316+
assign tmp4 = {tmp3, const_2_0};
1317+
assign tmp5 = rst ? tmp4 : tmp2;
1318+
assign tmp6 = {tmp5[3], tmp5[2], tmp5[1], tmp5[0]};
13281319
1329-
#10
1330-
$finish;
1320+
// Registers
1321+
always @(posedge clk)
1322+
begin
1323+
begin
1324+
r <= tmp6;
1325+
end
13311326
end
1327+
13321328
endmodule
1329+
13331330
"""
13341331

13351332

@@ -1452,10 +1449,156 @@ def test_error_invalid_add_reset(self):
14521449
with self.assertRaisesRegex(pyrtl.PyrtlError, "Invalid add_reset option"):
14531450
pyrtl.output_to_verilog(buffer, add_reset='foobar')
14541451

1452+
def test_error_existing_reset_wire(self):
1453+
buffer = io.StringIO()
1454+
_rst = pyrtl.Input(1, 'rst')
1455+
with self.assertRaisesRegex(pyrtl.PyrtlError, "Found a user-defined wire named 'rst'."):
1456+
pyrtl.output_to_verilog(buffer)
1457+
1458+
def test_existing_reset_wire_without_add_reset(self):
1459+
buffer = io.StringIO()
1460+
rst = pyrtl.Input(1, 'rst')
1461+
r = pyrtl.Register(4, 'r')
1462+
r.next <<= pyrtl.select(rst, 0, r + 1)
1463+
pyrtl.output_to_verilog(buffer, add_reset=False)
1464+
self.assertEqual(buffer.getvalue(), verilog_custom_reset)
1465+
1466+
1467+
verilog_testbench = """\
1468+
module tb();
1469+
reg clk;
1470+
reg rst;
1471+
reg[1:0] a100;
1472+
reg[3:0] w1;
1473+
reg[2:0] w12;
1474+
wire[1:0] out1;
1475+
wire[8:0] out10;
1476+
1477+
integer tb_iter;
1478+
toplevel block(.clk(clk), .rst(rst), .a100(a100), .w1(w1), .w12(w12), .out1(out1), .out10(out10));
1479+
1480+
always
1481+
#5 clk = ~clk;
1482+
1483+
initial begin
1484+
$dumpfile ("waveform.vcd");
1485+
$dumpvars;
1486+
1487+
clk = 0;
1488+
rst = 0;
1489+
block.r1 = 2;
1490+
block.r2 = 3;
1491+
block.tmp0 = 0;
1492+
for (tb_iter = 0; tb_iter < 32; tb_iter++) begin block.mem_0[tb_iter] = 0; end
1493+
block.mem_0[2] = 9;
1494+
block.mem_0[9] = 12;
1495+
a100 = 2'd0;
1496+
w1 = 4'd0;
1497+
w12 = 3'd0;
1498+
1499+
#10
1500+
a100 = 2'd1;
1501+
w1 = 4'd4;
1502+
w12 = 3'd1;
1503+
1504+
#10
1505+
a100 = 2'd3;
1506+
w1 = 4'd2;
1507+
w12 = 3'd7;
1508+
1509+
#10
1510+
a100 = 2'd2;
1511+
w1 = 4'd3;
1512+
w12 = 3'd4;
1513+
1514+
#10
1515+
$finish;
1516+
end
1517+
endmodule
1518+
""" # noqa
1519+
1520+
verilog_testbench_no_reset = """\
1521+
module tb();
1522+
reg clk;
1523+
reg[1:0] a100;
1524+
reg[3:0] w1;
1525+
reg[2:0] w12;
1526+
wire[1:0] out1;
1527+
wire[8:0] out10;
1528+
1529+
integer tb_iter;
1530+
toplevel block(.clk(clk), .a100(a100), .w1(w1), .w12(w12), .out1(out1), .out10(out10));
1531+
1532+
always
1533+
#5 clk = ~clk;
1534+
1535+
initial begin
1536+
$dumpfile ("waveform.vcd");
1537+
$dumpvars;
1538+
1539+
clk = 0;
1540+
block.r1 = 2;
1541+
block.r2 = 3;
1542+
block.tmp0 = 0;
1543+
for (tb_iter = 0; tb_iter < 32; tb_iter++) begin block.mem_0[tb_iter] = 0; end
1544+
block.mem_0[2] = 9;
1545+
block.mem_0[9] = 12;
1546+
a100 = 2'd0;
1547+
w1 = 4'd0;
1548+
w12 = 3'd0;
1549+
1550+
#10
1551+
a100 = 2'd1;
1552+
w1 = 4'd4;
1553+
w12 = 3'd1;
1554+
1555+
#10
1556+
a100 = 2'd3;
1557+
w1 = 4'd2;
1558+
w12 = 3'd7;
1559+
1560+
#10
1561+
a100 = 2'd2;
1562+
w1 = 4'd3;
1563+
w12 = 3'd4;
1564+
1565+
#10
1566+
$finish;
1567+
end
1568+
endmodule
1569+
"""
1570+
1571+
verilog_testbench_custom_reset = """\
1572+
module tb();
1573+
reg clk;
1574+
reg rst;
1575+
1576+
integer tb_iter;
1577+
toplevel block(.clk(clk), .rst(rst));
1578+
1579+
always
1580+
#5 clk = ~clk;
1581+
1582+
initial begin
1583+
$dumpfile ("waveform.vcd");
1584+
$dumpvars;
1585+
1586+
clk = 0;
1587+
block.r = 0;
1588+
$finish;
1589+
end
1590+
endmodule
1591+
"""
1592+
14551593

14561594
class TestOutputTestbench(unittest.TestCase):
14571595
def setUp(self):
14581596
pyrtl.reset_working_block()
1597+
# To compare textual consistency, need to make
1598+
# sure we're starting at the same index for all
1599+
# automatically created names.
1600+
pyrtl.wire._reset_wire_indexers()
1601+
pyrtl.memory._reset_memory_indexer()
14591602

14601603
def test_verilog_testbench_does_not_throw_error(self):
14611604
zero = pyrtl.Input(1, 'zero')
@@ -1470,7 +1613,7 @@ def test_verilog_testbench_does_not_throw_error(self):
14701613
with io.StringIO() as tbfile:
14711614
pyrtl.output_verilog_testbench(tbfile, sim_trace)
14721615

1473-
def test_verilog_testbench_consistency(self):
1616+
def create_design(self):
14741617
# Various wire names so we can verify they are printed
14751618
# in deterministic order each time
14761619
i1, i2, i3 = pyrtl.input_list('w1/4 w12/3 a100/2')
@@ -1499,9 +1642,38 @@ def test_verilog_testbench_consistency(self):
14991642
'w12': [0, 1, 7, 4],
15001643
'a100': [0, 1, 3, 2],
15011644
})
1645+
return sim_trace
1646+
1647+
def test_verilog_testbench_consistency(self):
1648+
sim_trace = self.create_design()
15021649
with io.StringIO() as tbfile:
15031650
pyrtl.output_verilog_testbench(tbfile, sim_trace)
1504-
self.assertEqual(tbfile.getvalue(), verilog_test_bench)
1651+
self.assertEqual(tbfile.getvalue(), verilog_testbench)
1652+
1653+
def test_verilog_testbench_no_reset_consistency(self):
1654+
sim_trace = self.create_design()
1655+
with io.StringIO() as tbfile:
1656+
pyrtl.output_verilog_testbench(tbfile, sim_trace, add_reset=False)
1657+
self.assertEqual(tbfile.getvalue(), verilog_testbench_no_reset)
1658+
1659+
def test_error_verilog_testbench_invalid_add_reset(self):
1660+
tbfile = io.StringIO()
1661+
with self.assertRaisesRegex(pyrtl.PyrtlError, "Invalid add_reset option"):
1662+
pyrtl.output_verilog_testbench(tbfile, add_reset='foobar')
1663+
1664+
def test_error_verilog_testbench_existing_reset_wire(self):
1665+
tbfile = io.StringIO()
1666+
_rst = pyrtl.Input(1, 'rst')
1667+
with self.assertRaisesRegex(pyrtl.PyrtlError, "Found a user-defined wire named 'rst'."):
1668+
pyrtl.output_verilog_testbench(tbfile)
1669+
1670+
def test_verilog_testbench_existing_reset_wire_without_add_reset(self):
1671+
buffer = io.StringIO()
1672+
rst = pyrtl.Input(1, 'rst')
1673+
r = pyrtl.Register(4, 'r')
1674+
r.next <<= pyrtl.select(rst, 0, r + 1)
1675+
pyrtl.output_verilog_testbench(buffer, add_reset=False)
1676+
self.assertEqual(buffer.getvalue(), verilog_testbench_custom_reset)
15051677

15061678

15071679
firrtl_output_concat_test = """\

0 commit comments

Comments
 (0)