Skip to content

Commit 2a1aa8a

Browse files
authored
Allow passing custom module_name to output_to_verilog and output_verilog_testbench (#474)
1 parent c9a7b10 commit 2a1aa8a

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

pyrtl/importexport.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -768,10 +768,11 @@ def _extra_checks(self, str):
768768

769769

770770
class _VerilogOutput:
771-
def __init__(self, block: Block, add_reset: bool | str):
771+
def __init__(self, block: Block, add_reset: bool | str, module_name="toplevel"):
772772
block = working_block(block)
773773
self.gate_graph = GateGraph(block)
774774
self.add_reset = add_reset
775+
self.module_name = module_name
775776

776777
if not isinstance(self.add_reset, bool) and self.add_reset != "asynchronous":
777778
msg = (
@@ -951,14 +952,17 @@ def _to_verilog_header(self, file: IO, initialize_registers: bool):
951952
"""Print the header of the verilog implementation."""
952953
print("// Generated automatically via PyRTL", file=file)
953954
print("// As one initial test of synthesis, map to FPGA with:", file=file)
954-
print('// yosys -p "synth_xilinx -top toplevel" thisfile.v\n', file=file)
955+
print(
956+
f'// yosys -p "synth_xilinx -top {self.module_name}" thisfile.v\n',
957+
file=file,
958+
)
955959

956960
# ``declared_gates`` is the set of Gates with corresponding Verilog reg/wire
957961
# declarations. Generated Verilog code can refer to these Gates by name.
958962
self.declared_gates = self.gate_graph.inputs | self.gate_graph.outputs
959963

960964
# Module name.
961-
print(f"module toplevel({', '.join(self.io_list)});", file=file)
965+
print(f"module {self.module_name}({', '.join(self.io_list)});", file=file)
962966

963967
# Declare Inputs and Outputs.
964968
print(" input clk;", file=file)
@@ -1309,7 +1313,9 @@ def output_verilog_testbench(
13091313
print(" integer tb_addr;", file=dest_file)
13101314

13111315
io_list_str = [f".{io}({io})" for io in self.io_list]
1312-
print(f" toplevel block({', '.join(io_list_str)});\n", file=dest_file)
1316+
print(
1317+
f" {self.module_name} block({', '.join(io_list_str)});\n", file=dest_file
1318+
)
13131319

13141320
# Generate the clock signal.
13151321
print(" always", file=dest_file)
@@ -1414,6 +1420,7 @@ def output_to_verilog(
14141420
add_reset: bool | str = True,
14151421
block: Block = None,
14161422
initialize_registers: bool = False,
1423+
module_name: str = "toplevel",
14171424
):
14181425
"""A function to walk the ``block`` and output it in Verilog format to the open
14191426
file.
@@ -1434,8 +1441,12 @@ def output_to_verilog(
14341441
When this argument is ``True``, a register like ``Register(name='foo',
14351442
bitwidth=8, reset_value=4)`` generates Verilog like ``reg[7:0] foo = 8'd4;``.
14361443
:param block: Block to be walked and exported. Defaults to the :ref:`working_block`.
1444+
:param module_name: name of the module. Defaults to toplevel
1445+
if the user puts nothing.
14371446
"""
1438-
_VerilogOutput(block, add_reset).output_to_verilog(dest_file, initialize_registers)
1447+
_VerilogOutput(block, add_reset, module_name=module_name).output_to_verilog(
1448+
dest_file, initialize_registers
1449+
)
14391450

14401451

14411452
def output_verilog_testbench(
@@ -1446,6 +1457,7 @@ def output_verilog_testbench(
14461457
cmd: str | None = None,
14471458
add_reset: bool | str = True,
14481459
block: Block = None,
1460+
module_name: str = "toplevel",
14491461
):
14501462
"""Output a Verilog testbench for the block/inputs used in the simulation trace.
14511463
@@ -1499,8 +1511,10 @@ def output_verilog_testbench(
14991511
value passed in here should match the argument passed to
15001512
:func:`output_to_verilog`.
15011513
:param block: Block containing design to test. Defaults to the :ref:`working_block`.
1514+
:param module_name: Name of the module the user chooses. Defaults to toplevel
1515+
if nothing is inputted.
15021516
"""
1503-
_VerilogOutput(block, add_reset).output_verilog_testbench(
1517+
_VerilogOutput(block, add_reset, module_name=module_name).output_verilog_testbench(
15041518
dest_file, simulation_trace, toplevel_include, vcd, cmd
15051519
)
15061520

tests/test_importexport.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,18 @@ def test_bit_slice_inputs(self):
13091309
# bit-slice.
13101310
self.assertTrue("assign tmp3 = c" in buffer.getvalue())
13111311

1312+
def test_custom_module_name(self):
1313+
a, b = pyrtl.Input(1, "a"), pyrtl.Input(1, "b")
1314+
out = pyrtl.Output(name="out")
1315+
out <<= a & b
1316+
1317+
buf = io.StringIO()
1318+
pyrtl.output_to_verilog(buf, module_name="custom_top")
1319+
text = buf.getvalue()
1320+
1321+
self.assertIn("module custom_top", text)
1322+
self.assertNotIn("module toplevel", text)
1323+
13121324

13131325
verilog_input_counter = """\
13141326
module counter (clk, rst, en, count);
@@ -1654,6 +1666,21 @@ def test_only_initialize_memblocks(self):
16541666
# The testbench should not touch the RomBlock.
16551667
self.assertTrue("my_rom" not in buffer.getvalue())
16561668

1669+
def test_custom_module_name_testbench(self):
1670+
# Minimal design
1671+
a, b = pyrtl.Input(1, "a"), pyrtl.Input(1, "b")
1672+
out = pyrtl.Output(1, "out")
1673+
out <<= a & b
1674+
1675+
buf = io.StringIO()
1676+
# Generate a testbench with a custom module name
1677+
pyrtl.output_verilog_testbench(buf, module_name="custom_tb")
1678+
text = buf.getvalue()
1679+
1680+
# Verify the custom module name is used
1681+
self.assertIn("custom_tb block(", text)
1682+
self.assertNotIn("toplevel block(", text)
1683+
16571684

16581685
firrtl_output_concat_test = """\
16591686
circuit Example :

0 commit comments

Comments
 (0)