diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index ff0176a..34cb396 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -41,12 +41,17 @@ jobs: - name: Setup python run: | - python3 -m venv --system-site-packages .efpga - . .efpga/bin/activate + python3 -m venv --system-site-packages .logik + . .logik/bin/activate python3 -m pip install --upgrade pip pip3 install -e .[test] + # Temporary + pip3 uninstall -y siliconcompiler + pip3 install siliconcompiler@git+https://github.com/siliconcompiler/siliconcompiler@main + + - name: Run tests run: | - . .efpga/bin/activate + . .logik/bin/activate pytest diff --git a/examples/adder/adder.py b/examples/adder/adder.py index d598072..82e8fcd 100755 --- a/examples/adder/adder.py +++ b/examples/adder/adder.py @@ -3,39 +3,48 @@ # Copyright 2024 Zero ASIC Corporation # Licensed under the MIT License (see LICENSE for details) -from siliconcompiler import Chip +import siliconcompiler + from logik.flows import logik_flow -from logiklib.demo.K4_N8_6x6 import K4_N8_6x6 + +from logik.demo import z1000 def hello_adder(): + # 1. Create a Design object to hold source files and constraints. + design = siliconcompiler.Design('adder') + + design.add_file('adder.v', fileset="rtl") + design.set_topmodule('adder', fileset="rtl") + + # 2. Create an FPGA object + project = siliconcompiler.FPGA(design) + + project.add_fileset('rtl') - # Create compilation object - chip = Chip('adder') - chip.create_cmdline(switchlist=['-remote']) + # 2. Create an FPGA object and associate the design with it. + fpga = z1000.z1000() - # Specify design sources - chip.input('adder.v') + # Enable command-line processing for options like -remote. + # fpga.create_cmdline(switchlist=['-remote']) # TODO - # Specify pin constraints - chip.input('adder.pcf') + # 3. Load the specific FPGA part, which also sets the default flow and libraries. + project.set_fpga(fpga) - # Compiler options - chip.set('option', 'quiet', True) + # 4. Use the specific flow for this build. + # Note: z1000 might already load a flow, but it's good practice to specify it. + project.set_flow(logik_flow.LogikFlow()) - # Select target fpga - chip.set('fpga', 'partname', 'K4_N8_6x6') + # # 5. Set any general options. + project.set('option', 'quiet', True) - # Load target settings - chip.set('option', 'flow', 'logik_flow') - chip.use(logik_flow) - chip.use(K4_N8_6x6) + project.set('option', 'continue', True, step="place") - # Run compiler - chip.run() + # 6. Run the compilation. + project.run() - # Display compiler results - chip.summary() + # 7. Display the results summary. + project.summary() if __name__ == "__main__": diff --git a/examples/eth_mac_1g/eth_mac_1g.py b/examples/eth_mac_1g/eth_mac_1g.py index 311cf28..4b85979 100755 --- a/examples/eth_mac_1g/eth_mac_1g.py +++ b/examples/eth_mac_1g/eth_mac_1g.py @@ -3,56 +3,62 @@ # This is the logik run script for demonstrating RTL-to-bitstream # with Alex Forencich's 1G Ethernet MAC -from siliconcompiler import Chip +import siliconcompiler from logik.flows import logik_flow -from logiklib.zeroasic.z1000 import z1000 +from logik.demo import z1000 def build(): - chip = Chip('eth_mac_1g_wrapper') - - # Load target settings - chip.use(logik_flow) - chip.use(z1000) - chip.set('option', 'flow', 'logik_flow') - - # Set default part name - chip.set('fpga', 'partname', 'z1000') + design = siliconcompiler.Design('eth_mac_1g_wrapper') # Define source files from verilog-ethernet repo # First we need to register the verilog-ethernet repo # as a package - chip.register_source( - name='verilog-ethernet', - path='git+https://github.com/alexforencich/verilog-ethernet.git', - ref='77320a9471d19c7dd383914bc049e02d9f4f1ffb') + design.set_dataroot( + 'verilog-ethernet', + 'git+https://github.com/alexforencich/verilog-ethernet.git', + '77320a9471d19c7dd383914bc049e02d9f4f1ffb') # Then we can pull in the specific RTL we need from that # repository -- Silicon Compiler will download and cache the files # for us - for source_file in ('eth_mac_1g.v', - 'axis_gmii_rx.v', - 'axis_gmii_tx.v', - 'lfsr.v'): - chip.input(f'rtl/{source_file}', package='verilog-ethernet') + with design.active_dataroot('verilog-ethernet'): + for source_file in ('eth_mac_1g.v', + 'axis_gmii_rx.v', + 'axis_gmii_tx.v', + 'lfsr.v'): + design.add_file(f'rtl/{source_file}', fileset='rtl') # Add in our top-level wrapper, stored locally - chip.register_source('ethmac_example', __file__) - chip.input('eth_mac_1g_wrapper.v', package='ethmac_example') + design.set_dataroot('ethmac_example', __file__) + with design.active_dataroot('ethmac_example'): + design.add_file('eth_mac_1g_wrapper.v', fileset='rtl') + design.set_topmodule("eth_mac_1g_wrapper", fileset="rtl") + + # Add timing constraints + design.add_file('eth_mac_1g.sdc', fileset='sdc') + + # Define pin constraints + design.add_file("constraints/z1000/pin_constraints.pcf", + fileset='pcf') + + project = siliconcompiler.FPGA(design) + + project.add_fileset('rtl') + project.add_fileset('sdc') + project.add_fileset('pcf') - # Add timing constraints - chip.input('eth_mac_1g.sdc', package='ethmac_example') + fpga = z1000.z1000() - # Define pin constraints - chip.input(f"constraints/{chip.get('fpga', 'partname')}/pin_constraints.pcf", - package='ethmac_example') + project.set_fpga(fpga) + project.set_flow(logik_flow.LogikFlow()) # Customize steps for this design - chip.set('option', 'quiet', True) + project.set('option', 'quiet', True) - chip.run() - chip.summary() + project.run() + project.summary() if __name__ == '__main__': diff --git a/logik/demo/z1000.py b/logik/demo/z1000.py new file mode 100644 index 0000000..9d36e65 --- /dev/null +++ b/logik/demo/z1000.py @@ -0,0 +1,45 @@ +# Copyright 2024 Zero ASIC Corporation + +from logik.devices.logik_fpga import LogikFPGA + + +# #################################################### +# # Setup for z1000 FPGA +# #################################################### + +class z1000(LogikFPGA): + ''' + Logik driver for z1000 + ''' + def __init__(self): + super().__init__() + part_name = "z1000" + self.set_name(part_name) + + self.define_tool_parameter('convert_bitstream', 'bitstream_map', 'file', + 'map for fasm->bitstream conversion') + + self.set_dataroot( + part_name, f"github://siliconcompiler/logiklib/v0.1.0/{part_name}_cad.tar.gz", "0.1.0") + + self.package.set_vendor("ZeroASIC") + self.set_lutsize(4) + + self.add_yosys_registertype(["dff", "dffr", "dffe", "dffer"]) + self.add_yosys_featureset(["async_reset", "enable"]) + with self.active_dataroot(part_name): + self.set_yosys_flipfloptechmap("techlib/tech_flops.v") + + self.set_vpr_devicecode(part_name) + self.set_vpr_clockmodel("route") + self.set_vpr_channelwidth(50) + self.add_vpr_registertype(["dff", "dffr", "dffe", "dffer"]) + with self.active_dataroot(part_name): + self.set_vpr_archfile("cad/z1000.xml") + self.set_vpr_graphfile("cad/z1000_rr_graph.xml") + self.set_vpr_constraintsmap("cad/z1000_constraint_map.json") + + with self.active_dataroot(part_name): + self.set_convert_bitstream_bitstream_map('cad/z1000_bitstream_map.json') + + self.set_vpr_router_lookahead("classic") diff --git a/logik/devices/__init__.py b/logik/devices/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/logik/devices/logik_fpga.py b/logik/devices/logik_fpga.py new file mode 100644 index 0000000..7194fef --- /dev/null +++ b/logik/devices/logik_fpga.py @@ -0,0 +1,15 @@ +from siliconcompiler.tools.vpr import VPRFPGA +from siliconcompiler.tools.yosys import YosysFPGA +from siliconcompiler.tools.opensta import OpenSTAFPGA + + +class LogikFPGA(YosysFPGA, VPRFPGA, OpenSTAFPGA): + ''' + Class for logik FPGA devices + ''' + def __init__(self): + super().__init__() + + def set_convert_bitstream_bitstream_map(self, file: str, dataroot: str = None): + with self.active_dataroot(self._get_active_dataroot(dataroot)): + return self.set("tool", "convert_bitstream", "bitstream_map", file) diff --git a/logik/flows/logik_flow.py b/logik/flows/logik_flow.py index d4674fe..bf518fa 100644 --- a/logik/flows/logik_flow.py +++ b/logik/flows/logik_flow.py @@ -1,38 +1,39 @@ # Copyright 2024 Zero ASIC Corporation # Licensed under the MIT License (see LICENSE for details) -from siliconcompiler import Chip from siliconcompiler.flows import fpgaflow from logik.tools.fasm_to_bitstream import bitstream_finish -############################################################################ -# DOCS -############################################################################ -def make_docs(chip): - return setup() +class LogikFlow(fpgaflow.FPGAVPRFlow): + '''An open-source FPGA flow using Yosys, VPR, and GenFasm. + This flow is designed for academic and research FPGAs, utilizing VPR + (Versatile Place and Route) for placement and routing. -############################################################################ -# Flowgraph Setup -############################################################################ -def setup(flowname='logik_flow'): - ''' - ''' + The flow consists of the following steps: - flow = fpgaflow.setup( - flowname=flowname, - fpgaflow_type='vpr') + * **elaborate**: Elaborate the RTL design from sources. + * **synthesis**: Synthesize the elaborated design into a netlist using Yosys. + * **place**: Place the netlist components onto the FPGA architecture using VPR. + * **route**: Route the connections between placed components using VPR. + * **bitstream**: Generate the final bitstream using GenFasm. + * **convert_bitstream**: Format bitstream from fasm to bits. + ''' + def __init__(self, name: str = "logik_flow"): + """ + Initializes the FPGAVPRFlow. - # Add bitstream generation task - flow.node(flowname, 'convert_bitstream', bitstream_finish) - flow.edge(flowname, 'bitstream', 'convert_bitstream') + Args: + name (str): The name of the flow. + """ + super().__init__(name) - return flow + self.node("convert_bitstream", bitstream_finish.BitstreamFinishTask()) + self.edge("bitstream", "convert_bitstream") -################################################## +# ################################################## if __name__ == "__main__": - flow = make_docs(Chip('')) - flow.write_flowgraph(f"{flow.top()}.png", flow=flow.top(), landscape=True) + LogikFlow().write_flowgraph(f"{LogikFlow().name}.png") diff --git a/logik/tools/fasm_to_bitstream/bitstream_finish.py b/logik/tools/fasm_to_bitstream/bitstream_finish.py index 3cd11b5..e82e12f 100644 --- a/logik/tools/fasm_to_bitstream/bitstream_finish.py +++ b/logik/tools/fasm_to_bitstream/bitstream_finish.py @@ -3,47 +3,51 @@ from logik.tools.fasm_to_bitstream import \ fasm_to_bitstream as fasm_utils -from siliconcompiler.tools._common import get_tool_task -from siliconcompiler import SiliconCompilerError +from siliconcompiler.tool import Task -def setup(chip): - ''' - Perform bitstream finishing - ''' - tool = 'fasm_to_bitstream' - step = chip.get('arg', 'step') - index = chip.get('arg', 'index') - _, task = get_tool_task(chip, step, index) +class BitstreamFinishTask(Task): + def __init__(self): + super().__init__() - part_name = chip.get('fpga', 'partname') + def tool(self): + return "fasm_to_bitstream" - # Require that a lut size is set for FPGA scripts. - chip.add('tool', tool, 'task', task, 'require', - ",".join(['fpga', part_name, 'file', 'bitstream_map']), - step=step, index=index) + def task(self): + return "bitstream_finish" - chip.add('tool', tool, 'task', task, 'input', f'{chip.top()}.fasm', step=step, index=index) - chip.add('tool', tool, 'task', task, 'output', f'{chip.top()}.json', step=step, index=index) - chip.add('tool', tool, 'task', task, 'output', f'{chip.top()}.bin', step=step, index=index) + def setup(self): + ''' + Perform bitstream finishing + ''' + super().setup() + fpga = self.project.get('fpga', 'device') + fpga_obj = self.project.get('library', fpga, field='schema') + # dd_required_key(...) you can pass in an object the fpga and then finish the keypath + # but this will need to be "tool", "???", "bitstream_map" + self.add_required_key(fpga_obj, "tool", 'convert_bitstream', 'bitstream_map') -def run(chip): - part_name = chip.get('fpga', 'partname') + self.add_input_file(ext="fasm") + self.add_output_file(ext="json") + self.add_output_file(ext="bin") - topmodule = chip.top() - fasm_file = f"inputs/{topmodule}.fasm" + def run(self): + fpga = self.project.get('fpga', 'device') + fpga_obj = self.project.get('library', fpga, field='schema') - bitstream_maps = chip.find_files('fpga', part_name, 'file', 'bitstream_map') + # topmodule = self.top() + fasm_file = f"inputs/{self.design_topmodule}.fasm" - if len(bitstream_maps) == 1: - json_outfile = f"outputs/{topmodule}.json" - binary_outfile = f"outputs/{topmodule}.bin" + bitstream_map = fpga_obj.find_files("tool", 'convert_bitstream', 'bitstream_map') + + json_outfile = f"outputs/{self.design_topmodule}.json" + binary_outfile = f"outputs/{self.design_topmodule}.bin" # Finishing steps are as follows: # 1. Convert FASM to IR - config_bitstream = fasm_utils.fasm2bitstream(fasm_file, bitstream_maps[0]) + config_bitstream = fasm_utils.fasm2bitstream(fasm_file, bitstream_map) # 2. Write IR to JSON for inspection purposes fasm_utils.write_bitstream_json(config_bitstream, json_outfile) @@ -57,11 +61,4 @@ def run(chip): # 5. Write binary to file fasm_utils.write_bitstream_binary(binary_bitstream, binary_outfile) - elif len(bitstream_maps) == 0: - raise SiliconCompilerError( - "fasm_to_bitstream requires a bitstream map file", chip=chip) - else: - raise SiliconCompilerError( - "Only one bitstream map file can be passed to fasm_to_bitstream", chip=chip) - - return 0 + return 0 diff --git a/logik/tools/fasm_to_bitstream/fasm_to_bitstream.py b/logik/tools/fasm_to_bitstream/fasm_to_bitstream.py index 9fc4f38..7fd7d61 100755 --- a/logik/tools/fasm_to_bitstream/fasm_to_bitstream.py +++ b/logik/tools/fasm_to_bitstream/fasm_to_bitstream.py @@ -279,6 +279,9 @@ def generate_bitstream_from_fasm(address_map, bitstream[x][y].append([0] * len(address_map[x][y][address])) for fasm_feature in fasm_data: + if fasm_feature not in feature_index: + print(f"fasm feature '{fasm_feature}' not found in address map") + continue x_i = feature_index[fasm_feature]['x'] y_i = feature_index[fasm_feature]['y'] addr_i = feature_index[fasm_feature]['address'] diff --git a/tests/conftest.py b/tests/conftest.py index 0a42ef2..e1dbfa7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,7 +25,7 @@ def _mock_show(chip, filename=None, screenshot=False): # pytest's monkeypatch lets us modify sys.path for this test only. monkeypatch.syspath_prepend(ex_dir) # Mock chip.show() so it doesn't run. - monkeypatch.setattr(siliconcompiler.Chip, 'show', _mock_show) + monkeypatch.setattr(siliconcompiler.Project, 'show', _mock_show) return ex_dir diff --git a/tests/examples/test_files.py b/tests/examples/test_files.py index 2dee1e8..29c9802 100644 --- a/tests/examples/test_files.py +++ b/tests/examples/test_files.py @@ -1,19 +1,13 @@ # Copyright 2024 Zero ASIC Corporation # Licensed under the MIT License (see LICENSE for details) -import pytest import siliconcompiler -from logiklib.demo.K4_N8_6x6 import K4_N8_6x6 +from logik.demo.z1000 import z1000 -@pytest.mark.parametrize( - "part", - [ - K4_N8_6x6, - ]) -def test_file_paths(part): - chip = siliconcompiler.Chip("test") - chip.use(part) - - assert chip.check_filepaths() +def test_file_paths(): + project = siliconcompiler.FPGA("test") + project.set_fpga(z1000()) + project.set('option', 'builddir', '.') + assert project.check_filepaths()