diff --git a/pyrtl/importexport.py b/pyrtl/importexport.py index 3c29d453..d79b4bbd 100644 --- a/pyrtl/importexport.py +++ b/pyrtl/importexport.py @@ -768,10 +768,11 @@ def _extra_checks(self, str): class _VerilogOutput: - def __init__(self, block: Block, add_reset: bool | str): + def __init__(self, block: Block, add_reset: bool | str, module_name="toplevel"): block = working_block(block) self.gate_graph = GateGraph(block) self.add_reset = add_reset + self.module_name = module_name if not isinstance(self.add_reset, bool) and self.add_reset != "asynchronous": msg = ( @@ -951,14 +952,17 @@ def _to_verilog_header(self, file: IO, initialize_registers: bool): """Print the header of the verilog implementation.""" print("// Generated automatically via PyRTL", file=file) print("// As one initial test of synthesis, map to FPGA with:", file=file) - print('// yosys -p "synth_xilinx -top toplevel" thisfile.v\n', file=file) + print( + f'// yosys -p "synth_xilinx -top {self.module_name}" thisfile.v\n', + file=file, + ) # ``declared_gates`` is the set of Gates with corresponding Verilog reg/wire # declarations. Generated Verilog code can refer to these Gates by name. self.declared_gates = self.gate_graph.inputs | self.gate_graph.outputs # Module name. - print(f"module toplevel({', '.join(self.io_list)});", file=file) + print(f"module {self.module_name}({', '.join(self.io_list)});", file=file) # Declare Inputs and Outputs. print(" input clk;", file=file) @@ -1309,7 +1313,9 @@ def output_verilog_testbench( print(" integer tb_addr;", file=dest_file) io_list_str = [f".{io}({io})" for io in self.io_list] - print(f" toplevel block({', '.join(io_list_str)});\n", file=dest_file) + print( + f" {self.module_name} block({', '.join(io_list_str)});\n", file=dest_file + ) # Generate the clock signal. print(" always", file=dest_file) @@ -1414,6 +1420,7 @@ def output_to_verilog( add_reset: bool | str = True, block: Block = None, initialize_registers: bool = False, + module_name: str = "toplevel", ): """A function to walk the ``block`` and output it in Verilog format to the open file. @@ -1434,8 +1441,12 @@ def output_to_verilog( When this argument is ``True``, a register like ``Register(name='foo', bitwidth=8, reset_value=4)`` generates Verilog like ``reg[7:0] foo = 8'd4;``. :param block: Block to be walked and exported. Defaults to the :ref:`working_block`. + :param module_name: name of the module. Defaults to toplevel + if the user puts nothing. """ - _VerilogOutput(block, add_reset).output_to_verilog(dest_file, initialize_registers) + _VerilogOutput(block, add_reset, module_name=module_name).output_to_verilog( + dest_file, initialize_registers + ) def output_verilog_testbench( @@ -1446,6 +1457,7 @@ def output_verilog_testbench( cmd: str | None = None, add_reset: bool | str = True, block: Block = None, + module_name: str = "toplevel", ): """Output a Verilog testbench for the block/inputs used in the simulation trace. @@ -1499,8 +1511,10 @@ def output_verilog_testbench( value passed in here should match the argument passed to :func:`output_to_verilog`. :param block: Block containing design to test. Defaults to the :ref:`working_block`. + :param module_name: Name of the module the user chooses. Defaults to toplevel + if nothing is inputted. """ - _VerilogOutput(block, add_reset).output_verilog_testbench( + _VerilogOutput(block, add_reset, module_name=module_name).output_verilog_testbench( dest_file, simulation_trace, toplevel_include, vcd, cmd ) diff --git a/tests/test_importexport.py b/tests/test_importexport.py index 931d889f..98bedcaf 100644 --- a/tests/test_importexport.py +++ b/tests/test_importexport.py @@ -1309,6 +1309,18 @@ def test_bit_slice_inputs(self): # bit-slice. self.assertTrue("assign tmp3 = c" in buffer.getvalue()) + def test_custom_module_name(self): + a, b = pyrtl.Input(1, "a"), pyrtl.Input(1, "b") + out = pyrtl.Output(name="out") + out <<= a & b + + buf = io.StringIO() + pyrtl.output_to_verilog(buf, module_name="custom_top") + text = buf.getvalue() + + self.assertIn("module custom_top", text) + self.assertNotIn("module toplevel", text) + verilog_input_counter = """\ module counter (clk, rst, en, count); @@ -1654,6 +1666,21 @@ def test_only_initialize_memblocks(self): # The testbench should not touch the RomBlock. self.assertTrue("my_rom" not in buffer.getvalue()) + def test_custom_module_name_testbench(self): + # Minimal design + a, b = pyrtl.Input(1, "a"), pyrtl.Input(1, "b") + out = pyrtl.Output(1, "out") + out <<= a & b + + buf = io.StringIO() + # Generate a testbench with a custom module name + pyrtl.output_verilog_testbench(buf, module_name="custom_tb") + text = buf.getvalue() + + # Verify the custom module name is used + self.assertIn("custom_tb block(", text) + self.assertNotIn("toplevel block(", text) + firrtl_output_concat_test = """\ circuit Example :