From 7ec8970c69c570d0a889666e3f125c5e7c7300d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 05:04:55 +0000 Subject: [PATCH 01/20] Bump the python-packages group across 1 directory with 2 updates Updates the requirements on [pybind11](https://github.com/pybind/pybind11) and [siliconcompiler](https://github.com/siliconcompiler/siliconcompiler) to permit the latest version. Updates `pybind11` to 3.0.0 - [Release notes](https://github.com/pybind/pybind11/releases) - [Changelog](https://github.com/pybind/pybind11/blob/master/docs/changelog.md) - [Commits](https://github.com/pybind/pybind11/compare/v2.6.0...v3.0.0) Updates `siliconcompiler` to 0.34.1 - [Release notes](https://github.com/siliconcompiler/siliconcompiler/releases) - [Changelog](https://github.com/siliconcompiler/siliconcompiler/blob/main/Changes) - [Commits](https://github.com/siliconcompiler/siliconcompiler/compare/v0.27.0...v0.34.1) --- updated-dependencies: - dependency-name: pybind11 dependency-version: 3.0.0 dependency-type: direct:production dependency-group: python-packages - dependency-name: siliconcompiler dependency-version: 0.34.1 dependency-type: direct:production dependency-group: python-packages ... Signed-off-by: dependabot[bot] --- pyproject.toml | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f9915418..3286574c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=42", "wheel", "pybind11 >=2.6,<3"] +requires = ["setuptools>=42", "wheel", "pybind11 >=2.6,<4"] build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index 611687a4..87f36a9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ numpy tqdm -siliconcompiler >= 0.27.0, < 0.32.0 +siliconcompiler >= 0.27.0, < 0.35.0 # Testing dependencies #:test From df11667ff5069652b99786733b2faf08438b936e Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 6 Oct 2025 09:56:39 -0400 Subject: [PATCH 02/20] bumped min python version to 3.10 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3027d719..8139a3e1 100644 --- a/setup.py +++ b/setup.py @@ -76,7 +76,7 @@ def parse_reqs(): version=__version__, packages=find_packages(), include_package_data=True, - python_requires=">=3.7", + python_requires=">=3.10", install_requires=install_reqs, extras_require=extras_req, ext_modules=ext_modules, From 475a4fe9a38e04468126f0b4511fd06ff1c62bed Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 6 Oct 2025 17:31:44 -0400 Subject: [PATCH 03/20] WIP --- .github/workflows/regression.yml | 18 + examples/umiram/__init__.py | 0 examples/umiram/test.py | 56 +- switchboard/deps/__init__.py | 0 switchboard/deps/verilog_axi.py | 22 + switchboard/sbdut.py | 310 ++------- switchboard/sbdut_old.py | 831 +++++++++++++++++++++++++ switchboard/verilog/__init__.py | 0 switchboard/verilog/common/__init__.py | 0 switchboard/verilog/common/common.py | 24 + switchboard/verilog/fpga/__init__.py | 0 switchboard/verilog/fpga/fpga.py | 19 + switchboard/verilog/sim/__init__.py | 0 switchboard/verilog/sim/sim.py | 26 + 14 files changed, 1028 insertions(+), 278 deletions(-) create mode 100644 examples/umiram/__init__.py create mode 100644 switchboard/deps/__init__.py create mode 100644 switchboard/deps/verilog_axi.py create mode 100644 switchboard/sbdut_old.py create mode 100644 switchboard/verilog/__init__.py create mode 100644 switchboard/verilog/common/__init__.py create mode 100644 switchboard/verilog/common/common.py create mode 100644 switchboard/verilog/fpga/__init__.py create mode 100644 switchboard/verilog/fpga/fpga.py create mode 100644 switchboard/verilog/sim/__init__.py create mode 100644 switchboard/verilog/sim/sim.py diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml index 616a56a0..ee0d0322 100644 --- a/.github/workflows/regression.yml +++ b/.github/workflows/regression.yml @@ -29,6 +29,15 @@ jobs: pip3 install .[test] pip3 install -r examples/requirements.txt + # Temporary workaround for versioning issues + - name: Update python packages + run: | + pip3 uninstall -y siliconcompiler + pip3 install siliconcompiler@git+https://github.com/siliconcompiler/siliconcompiler.git@main + + pip3 uninstall -y umi + pip3 install umi@git+https://github.com/zeroasiccorp/umi.git@main + - name: Run pytest working-directory: examples run: | @@ -61,6 +70,15 @@ jobs: run: | pip3 install . + # Temporary workaround for versioning issues + - name: Update python packages + run: | + pip3 uninstall -y siliconcompiler + pip3 install siliconcompiler@git+https://github.com/siliconcompiler/siliconcompiler.git@main + + pip3 uninstall -y umi + pip3 install umi@git+https://github.com/zeroasiccorp/umi.git@main + - name: Setup GIT to pull from https working-directory: examples run: | diff --git a/examples/umiram/__init__.py b/examples/umiram/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/umiram/test.py b/examples/umiram/test.py index ddb07ea5..95ce6943 100755 --- a/examples/umiram/test.py +++ b/examples/umiram/test.py @@ -10,7 +10,10 @@ import numpy as np from pathlib import Path from switchboard import SbDut, UmiTxRx, binary_run -from umi import sumi +from umi.sumi import Fifo, RAM + +from siliconcompiler import Design + THIS_DIR = Path(__file__).resolve().parent @@ -83,20 +86,45 @@ def python_intf(umi): assert val2 == 0xCD +class UmiRam(Design): + + def __init__(self): + super().__init__("testbench") + + from switchboard import sb_path + + from switchboard.verilog.common.common import Common + from switchboard.verilog.sim.sim import Sim as SB_SIM + + dr_path = sb_path() / ".." / "examples" / "umiram" + + self.set_dataroot('umiram', dr_path) + + files = [ + "testbench.sv", + "../common/verilog/umiram.sv" + ] + + deps = [Common(), SB_SIM(), Fifo(), RAM()] + + with self.active_fileset('rtl'): + self.set_topmodule("testbench") + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) + + def build_testbench(): extra_args = { '--mode': dict(default='python', choices=['python', 'cpp'], help='Programming language used for the test stimulus.') } - dut = SbDut('testbench', cmdline=True, trace_type='fst', extra_args=extra_args) - - dut.input('testbench.sv') - dut.input(THIS_DIR.parent / 'common' / 'verilog' / 'umiram.sv') - - dut.use(sumi) + dut = SbDut(UmiRam()) + dut.option.set_nodashboard(True) - dut.build() + print(f"dut.build() output = {dut.build()}") return dut @@ -111,12 +139,12 @@ def main(): # launch the simulation dut.simulate() - if dut.args.mode == 'python': - python_intf(umi) - elif dut.args.mode == 'cpp': - binary_run(THIS_DIR / 'client').wait() - else: - raise ValueError(f'Invalid mode: {dut.args.mode}') + #if dut.args.mode == 'python': + #python_intf(umi) + #elif dut.args.mode == 'cpp': + binary_run(THIS_DIR / 'client').wait() + #else: + # raise ValueError(f'Invalid mode: {dut.args.mode}') if __name__ == '__main__': diff --git a/switchboard/deps/__init__.py b/switchboard/deps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/deps/verilog_axi.py b/switchboard/deps/verilog_axi.py new file mode 100644 index 00000000..b69c5b90 --- /dev/null +++ b/switchboard/deps/verilog_axi.py @@ -0,0 +1,22 @@ +from siliconcompiler import Design + + +class VerilogAxi(Design): + def __init__(self): + + self.set_dataroot( + name="verilog_axi", + path="git+https://github.com/alexforencich/verilog-axis.git@master", + tag="25912d48fec2abbf3565bbefe402c1cff99fe470" + ) + + path = "rtl" + + files = [ + f"{path}/arbiter.v", + ] + + with self.active_fileset('rtl'): + for item in files: + self.add_files(item) + diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 101246c0..0a543e84 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -16,7 +16,7 @@ from copy import deepcopy from pathlib import Path -from typing import List, Dict, Any +from typing import List, Dict, Any, Union from .switchboard import path as sb_path from .verilator import verilator_run @@ -28,16 +28,17 @@ normalize_parameters, create_intf_objs) from .cmdline import get_cmdline_args -import siliconcompiler +from siliconcompiler import Design, Sim + SB_DIR = sb_path() -class SbDut(siliconcompiler.Chip): +class SbDut(Sim): def __init__( self, - design: str = 'testbench', - tool: str = 'verilator', + design: Union[Design, str] = None, + tool: str = 'icarus', default_main: bool = True, trace: bool = True, trace_type: str = 'vcd', @@ -67,85 +68,18 @@ def __init__( suffix=None, threads=None ): - """ - Parameters - ---------- - design: string - Name of the top level chip design module. - - tool: string, optional - Which tool to use to compile simulator. Options are "verilator" or - "icarus". - - default_main: bool, optional - If True, the default testbench.cc will be used and does not need to - be provided via the add() function - - trace: bool, optional - If true, a waveform dump file will be produced using the file type - specified by `trace_type`. - - trace_type: str, optional - File type for the waveform dump file. Defaults to vcd. - - module: str, optional - module containing the siliconcompiler driver for this object - - fpga: bool, optional - If True, compile using switchboard's library of modules for FPGA emulation, - rather than the modules for RTL simulation. - - xyce: bool, optional - If True, compile for xyce co-simulation. - - frequency: float, optional - If provided, the default frequency of the clock generated in the testbench, - in seconds. - - period: float, optional - If provided, the default period of the clock generated in the testbench, - in seconds. - - max_rate: float, optional - If provided, the maximum real-world rate that the simulation is allowed to run - at, in Hz. Can be useful to encourage time-sharing between many processes and - for performance modeling when latencies are large and/or variable. - - start_delay: float, optional - If provided, the real-world time to delay before the first clock tick in the - simulation. Can be useful to make sure that programs start at approximately - the same time and to prevent simulations from stepping on each other's toes - when starting up. - warnings: List[str], optional - If provided, a list of tool-specific warnings to enable. If not provided, a default - set of warnings will be included. Warnings can be disabled by setting this argument - to an empty list. + super().__init__(design) + self.add_fileset("rtl") - cmdline: bool, optional - If True, accept configuration settings from the command line, such as "--trace", - "--tool TOOL", and "--fast". - - fast: bool, optional - If True, the simulation binary will not be rebuilt if an existing one is found. - The setting here can be overridden when build() is called by setting its argument - with the same name. - - extra_args: dict, optional - If provided and cmdline=True, a dictionary of additional command line arguments - to be made available. The keys of the dictionary are the arguments ("-n", "--test", - etc.) and the values are themselves dictionaries that contain keyword arguments - accepted by argparse ("action": "store_true", "default": 42, etc.) - """ - - # call the super constructor - - if autowrap and (not subcomponent): - toplevel = 'testbench' - else: - toplevel = design + top_lvl_module_name = None + main_filesets = self.option.get_fileset() + if main_filesets and len(main_filesets) != 0: + main_fileset = main_filesets[0] + top_lvl_module_name = design.get_topmodule( + fileset=main_fileset + ) - super().__init__(toplevel) # parse command-line options if desired @@ -198,14 +132,14 @@ def __init__( self.autowrap = autowrap if (suffix is None) and subcomponent: - suffix = f'_unq_{design}' + suffix = f'_unq_{top_lvl_module_name}' self.suffix = suffix if suffix is not None: - self.dut = f'{design}{suffix}' + self.dut = f'{top_lvl_module_name}{suffix}' else: - self.dut = design + self.dut = top_lvl_module_name self.parameters = normalize_parameters(parameters) self.intf_defs = normalize_interfaces(interfaces) @@ -231,177 +165,48 @@ def __init__( if subcomponent: # the subcomponent build flow is tool-agnostic, producing a single Verilog # file as output, as opposed to a simulator binary - builddir = buildroot / metadata_str(design=design, parameters=parameters) - else: - builddir = buildroot / metadata_str(design=design, parameters=parameters, - tool=tool, trace=trace, trace_type=trace_type, threads=threads) - - self.set('option', 'builddir', str(Path(builddir).resolve())) - - self.set('option', 'clean', True) # preserve old behavior - - if not subcomponent: - if fpga: - # library dirs - self.set('option', 'ydir', sb_path() / 'verilog' / 'fpga') - self.add('option', 'ydir', sb_path() / 'deps' / 'verilog-axi' / 'rtl') - - # include dirs - self.set('option', 'idir', sb_path() / 'verilog' / 'fpga' / 'include') - - for opt in ['ydir', 'idir']: - if not fpga: - self.set('option', opt, sb_path() / 'verilog' / 'sim') - self.add('option', opt, sb_path() / 'verilog' / 'common') - - if trace: - self.set('tool', 'verilator', 'task', 'compile', 'var', 'trace', True) - self.add('option', 'define', 'SB_TRACE') - - if self.trace_type == 'fst': - self.add('option', 'define', 'SB_TRACE_FST') - - if tool == 'icarus': - self._configure_icarus() - else: - if module is None: - if tool == 'verilator': - module = 'siliconcompiler' - else: - raise ValueError('Must specify the "module" argument,' - ' which is the name of the module containing the' - ' SiliconCompiler driver for this simulator.') - - self._configure_build( - module=module, - default_main=default_main, - fpga=fpga + builddir = buildroot / metadata_str( + design=top_lvl_module_name, + parameters=parameters ) - - if xyce: - self._configure_xyce() - else: - # special mode that produces a standalone Verilog netlist - # rather than building/running a simulation - - flowname = 'package' - - self.package_flow = siliconcompiler.Flow(flowname) - - from siliconcompiler.tools.surelog import parse - self.package_flow.node(flowname, 'parse', parse) - - from .sc.sed import remove - self.package_flow.node(flowname, 'remove', remove) - - from .sc.morty import uniquify - self.package_flow.node(flowname, 'uniquify', uniquify) - - self.package_flow.edge(flowname, 'parse', 'remove') - self.package_flow.edge(flowname, 'remove', 'uniquify') - - self.use(self.package_flow) - self.set('option', 'flow', flowname) - - def _configure_build( - self, - module: str, - default_main: bool = False, - fpga: bool = False - ): - if not fpga: - self.input(SB_DIR / 'dpi' / 'switchboard_dpi.cc') - - if default_main and (self.tool == 'verilator'): - self.input(SB_DIR / 'verilator' / 'testbench.cc') - - if fpga and (self.tool == 'verilator'): - self.set('tool', 'verilator', 'task', 'compile', 'file', 'config', - sb_path() / 'verilator' / 'config.vlt') - self.set('tool', 'verilator', 'task', 'compile', 'warningoff', 'TIMESCALEMOD') - - # enable specific warnings that aren't included by default - if self.tool == 'verilator': - if self.warnings is None: - warnings = ['BLKSEQ'] else: - warnings = self.warnings - - for warning in warnings: - self.set('tool', 'verilator', 'task', 'compile', 'option', f'-Wwarn-{warning}') - - self.set('tool', self.tool, 'task', 'compile', 'var', 'cflags', - ['-Wno-unknown-warning-option']) - self.set('tool', self.tool, 'task', 'compile', 'dir', 'cincludes', [SB_DIR / 'cpp']) - self.set('tool', self.tool, 'task', 'compile', 'var', 'ldflags', ['-pthread']) - - if self.trace and (self.tool == 'verilator'): - self.set('tool', 'verilator', 'task', 'compile', 'var', 'trace_type', self.trace_type) - - if self.tool == 'verilator': - timeunit = self.timeunit - timeprecision = self.timeprecision - - if (timeunit is not None) or (timeprecision is not None): - if timeunit is None: - timeunit = '1ps' # default from Verilator documentation + builddir = buildroot / metadata_str( + design=top_lvl_module_name, + parameters=parameters, + tool=tool, + trace=trace, + trace_type=trace_type, + threads=threads + ) - if timeprecision is None: - timeprecision = '1ps' # default from Verilator documentation + self.option.set_builddir(str(Path(builddir).resolve())) + # preserve old behavior + self.option.set_clean(True) - timescale = f'{timeunit}/{timeprecision}' - self.add('tool', 'verilator', 'task', 'compile', 'option', '--timescale') - self.add('tool', 'verilator', 'task', 'compile', 'option', timescale) - if (self.threads is not None) and (self.tool == 'verilator'): - self.add('tool', 'verilator', 'task', 'compile', 'option', '--threads') - self.add('tool', 'verilator', 'task', 'compile', 'option', str(self.threads)) + if self.tool == 'icarus': + self._configure_icarus() - self.set('option', 'libext', ['v', 'sv']) - # Set up flow that compiles RTL - # TODO: this will be built into SC - self.set('option', 'flow', 'simflow') - compile = importlib.import_module(f'{module}.tools.{self.tool}.compile') - self.node('simflow', 'compile', compile) def _configure_icarus(self): - self.add('option', 'libext', 'sv') - self.set('tool', 'icarus', 'task', 'compile', 'var', 'verilog_generation', '2012') - # use dvflow to execute Icarus, but set steplist so we don't run sim - from siliconcompiler.flows import dvflow - self.use(dvflow) - - self.set('option', 'flow', 'dvflow') - self.set('option', 'to', 'compile') - - def _configure_xyce(self): - if self.xyce: - # already configured, so return early - return - - self.add('option', 'define', 'SB_XYCE') - - if self.tool != 'icarus': - self.input(SB_DIR / 'dpi' / 'xyce_dpi.cc') + from siliconcompiler.flows.dvflow import DVFlow - xyce_c_includes, xyce_ld_flags = xyce_flags() + self.set_flow(DVFlow(tool="icarus")) + from siliconcompiler.tools import get_task + from siliconcompiler.tools.icarus.compile import CompileTask + get_task(self, filter=CompileTask).set("var", "verilog_generation", "2012") - self.add('tool', self.tool, 'task', 'compile', 'dir', 'cincludes', xyce_c_includes) - self.add('tool', self.tool, 'task', 'compile', 'var', 'ldflags', xyce_ld_flags) - - # indicate that build is configured for Xyce. for Icarus simulation, this flag is used - # to determine whether a VPI object should be built for Xyce - self.xyce = True + self.set('option', 'to', 'compile') def find_sim(self): if self.tool == 'icarus': result_kind = 'vvp' else: result_kind = 'vexe' - + print(f"results found = {self.find_result(result_kind, step='compile')}") return self.find_result(result_kind, step='compile') def build(self, cwd: str = None, fast: bool = None): @@ -423,9 +228,10 @@ def build(self, cwd: str = None, fast: bool = None): if self.tool == 'icarus': if (not fast) or (icarus_find_vpi(cwd, name='switchboard') is None): icarus_build_vpi(cwd, name='switchboard') - if self.xyce and ((not fast) or (icarus_find_vpi(cwd, name='xyce') is None)): - cincludes, ldflags = xyce_flags() - icarus_build_vpi(cwd, name='xyce', cincludes=cincludes, ldflags=ldflags) + + #if self.xyce and ((not fast) or (icarus_find_vpi(cwd, name='xyce') is None)): + # cincludes, ldflags = xyce_flags() + # icarus_build_vpi(cwd, name='xyce', cincludes=cincludes, ldflags=ldflags) # if "fast" is set, then we can return early if the # simulation binary already exists @@ -434,31 +240,7 @@ def build(self, cwd: str = None, fast: bool = None): if sim is not None: return sim - # build the wrapper if needed - if self.autowrap: - from .autowrap import autowrap - - filename = Path(self.get('option', 'builddir')).resolve() / 'testbench.sv' - - filename.parent.mkdir(exist_ok=True, parents=True) - - instance = f'{self.dut}_i' - - autowrap( - instances={instance: self.dut}, - parameters={instance: self.parameters}, - interfaces={instance: self.intf_defs}, - clocks={instance: self.clocks}, - resets={instance: self.resets}, - tieoffs={instance: self.tieoffs}, - filename=filename - ) - - self.input(filename) - - # if we get to this point, then we need to rebuild - # the simulation binary - self.run() + assert self.run() return self.find_sim() diff --git a/switchboard/sbdut_old.py b/switchboard/sbdut_old.py new file mode 100644 index 00000000..101246c0 --- /dev/null +++ b/switchboard/sbdut_old.py @@ -0,0 +1,831 @@ +# Build and simulation automation built on SiliconCompiler + +# Copyright (c) 2024 Zero ASIC Corporation +# This code is licensed under Apache License 2.0 (see LICENSE for details) + +"""Class inheriting from the SiliconCompiler Chip class that can be used for building a +Switchboard-based testbench. + +This class is meant to be interacted with like a regular Chip object, but it has some parameters +automatically configured to abstract away setup of files that are required by all Switchboard +testbenches. +""" + +import importlib +import subprocess + +from copy import deepcopy +from pathlib import Path +from typing import List, Dict, Any + +from .switchboard import path as sb_path +from .verilator import verilator_run +from .icarus import icarus_build_vpi, icarus_find_vpi, icarus_run +from .util import plusargs_to_args, binary_run, ProcessCollection +from .xyce import xyce_flags +from .ams import make_ams_spice_wrapper, make_ams_verilog_wrapper, parse_spice_subckts +from .autowrap import (normalize_clocks, normalize_interfaces, normalize_resets, normalize_tieoffs, + normalize_parameters, create_intf_objs) +from .cmdline import get_cmdline_args + +import siliconcompiler + +SB_DIR = sb_path() + + +class SbDut(siliconcompiler.Chip): + def __init__( + self, + design: str = 'testbench', + tool: str = 'verilator', + default_main: bool = True, + trace: bool = True, + trace_type: str = 'vcd', + module: str = None, + fpga: bool = False, + xyce: bool = False, + frequency: float = 100e6, + period: float = None, + max_rate: float = -1, + start_delay: float = None, + timeunit: str = None, + timeprecision: str = None, + warnings: List[str] = None, + cmdline: bool = False, + fast: bool = False, + extra_args: dict = None, + autowrap: bool = False, + parameters=None, + interfaces=None, + clocks=None, + resets=None, + tieoffs=None, + buildroot=None, + builddir=None, + args=None, + subcomponent=False, + suffix=None, + threads=None + ): + """ + Parameters + ---------- + design: string + Name of the top level chip design module. + + tool: string, optional + Which tool to use to compile simulator. Options are "verilator" or + "icarus". + + default_main: bool, optional + If True, the default testbench.cc will be used and does not need to + be provided via the add() function + + trace: bool, optional + If true, a waveform dump file will be produced using the file type + specified by `trace_type`. + + trace_type: str, optional + File type for the waveform dump file. Defaults to vcd. + + module: str, optional + module containing the siliconcompiler driver for this object + + fpga: bool, optional + If True, compile using switchboard's library of modules for FPGA emulation, + rather than the modules for RTL simulation. + + xyce: bool, optional + If True, compile for xyce co-simulation. + + frequency: float, optional + If provided, the default frequency of the clock generated in the testbench, + in seconds. + + period: float, optional + If provided, the default period of the clock generated in the testbench, + in seconds. + + max_rate: float, optional + If provided, the maximum real-world rate that the simulation is allowed to run + at, in Hz. Can be useful to encourage time-sharing between many processes and + for performance modeling when latencies are large and/or variable. + + start_delay: float, optional + If provided, the real-world time to delay before the first clock tick in the + simulation. Can be useful to make sure that programs start at approximately + the same time and to prevent simulations from stepping on each other's toes + when starting up. + + warnings: List[str], optional + If provided, a list of tool-specific warnings to enable. If not provided, a default + set of warnings will be included. Warnings can be disabled by setting this argument + to an empty list. + + cmdline: bool, optional + If True, accept configuration settings from the command line, such as "--trace", + "--tool TOOL", and "--fast". + + fast: bool, optional + If True, the simulation binary will not be rebuilt if an existing one is found. + The setting here can be overridden when build() is called by setting its argument + with the same name. + + extra_args: dict, optional + If provided and cmdline=True, a dictionary of additional command line arguments + to be made available. The keys of the dictionary are the arguments ("-n", "--test", + etc.) and the values are themselves dictionaries that contain keyword arguments + accepted by argparse ("action": "store_true", "default": 42, etc.) + """ + + # call the super constructor + + if autowrap and (not subcomponent): + toplevel = 'testbench' + else: + toplevel = design + + super().__init__(toplevel) + + # parse command-line options if desired + + if cmdline: + self.args = get_cmdline_args(tool=tool, trace=trace, trace_type=trace_type, + frequency=frequency, period=period, fast=fast, max_rate=max_rate, + start_delay=start_delay, threads=threads, extra_args=extra_args) + elif args is not None: + self.args = args + else: + self.args = None + + if self.args is not None: + trace = self.args.trace + trace_type = self.args.trace_type + fast = self.args.fast + tool = self.args.tool + frequency = self.args.frequency + period = self.args.period + max_rate = self.args.max_rate + start_delay = self.args.start_delay + threads = self.args.threads + + # input validation + + if trace_type not in ('vcd', 'fst'): + raise ValueError('Invalid trace_type, expected one of "vcd" or "fst"') + + # save settings + + self.tool = tool + self.trace = trace + self.trace_type = trace_type + self.fpga = fpga + self.xyce = False # is set True by _configure_xyce + self.warnings = warnings + self.fast = fast + + if (period is None) and (frequency is not None): + period = 1 / frequency + self.period = period + self.max_rate = max_rate + self.start_delay = start_delay + + self.threads = threads + + self.timeunit = timeunit + self.timeprecision = timeprecision + + self.autowrap = autowrap + + if (suffix is None) and subcomponent: + suffix = f'_unq_{design}' + + self.suffix = suffix + + if suffix is not None: + self.dut = f'{design}{suffix}' + else: + self.dut = design + + self.parameters = normalize_parameters(parameters) + self.intf_defs = normalize_interfaces(interfaces) + self.clocks = normalize_clocks(clocks) + self.resets = normalize_resets(resets) + self.tieoffs = normalize_tieoffs(tieoffs) + + # initialization + + self.intfs = {} + + # keep track of processes started + self.process_collection = ProcessCollection() + + # simulator-agnostic settings + + if builddir is None: + if buildroot is None: + buildroot = 'build' + + buildroot = Path(buildroot).resolve() + + if subcomponent: + # the subcomponent build flow is tool-agnostic, producing a single Verilog + # file as output, as opposed to a simulator binary + builddir = buildroot / metadata_str(design=design, parameters=parameters) + else: + builddir = buildroot / metadata_str(design=design, parameters=parameters, + tool=tool, trace=trace, trace_type=trace_type, threads=threads) + + self.set('option', 'builddir', str(Path(builddir).resolve())) + + self.set('option', 'clean', True) # preserve old behavior + + if not subcomponent: + if fpga: + # library dirs + self.set('option', 'ydir', sb_path() / 'verilog' / 'fpga') + self.add('option', 'ydir', sb_path() / 'deps' / 'verilog-axi' / 'rtl') + + # include dirs + self.set('option', 'idir', sb_path() / 'verilog' / 'fpga' / 'include') + + for opt in ['ydir', 'idir']: + if not fpga: + self.set('option', opt, sb_path() / 'verilog' / 'sim') + self.add('option', opt, sb_path() / 'verilog' / 'common') + + if trace: + self.set('tool', 'verilator', 'task', 'compile', 'var', 'trace', True) + self.add('option', 'define', 'SB_TRACE') + + if self.trace_type == 'fst': + self.add('option', 'define', 'SB_TRACE_FST') + + if tool == 'icarus': + self._configure_icarus() + else: + if module is None: + if tool == 'verilator': + module = 'siliconcompiler' + else: + raise ValueError('Must specify the "module" argument,' + ' which is the name of the module containing the' + ' SiliconCompiler driver for this simulator.') + + self._configure_build( + module=module, + default_main=default_main, + fpga=fpga + ) + + if xyce: + self._configure_xyce() + else: + # special mode that produces a standalone Verilog netlist + # rather than building/running a simulation + + flowname = 'package' + + self.package_flow = siliconcompiler.Flow(flowname) + + from siliconcompiler.tools.surelog import parse + self.package_flow.node(flowname, 'parse', parse) + + from .sc.sed import remove + self.package_flow.node(flowname, 'remove', remove) + + from .sc.morty import uniquify + self.package_flow.node(flowname, 'uniquify', uniquify) + + self.package_flow.edge(flowname, 'parse', 'remove') + self.package_flow.edge(flowname, 'remove', 'uniquify') + + self.use(self.package_flow) + self.set('option', 'flow', flowname) + + def _configure_build( + self, + module: str, + default_main: bool = False, + fpga: bool = False + ): + if not fpga: + self.input(SB_DIR / 'dpi' / 'switchboard_dpi.cc') + + if default_main and (self.tool == 'verilator'): + self.input(SB_DIR / 'verilator' / 'testbench.cc') + + if fpga and (self.tool == 'verilator'): + self.set('tool', 'verilator', 'task', 'compile', 'file', 'config', + sb_path() / 'verilator' / 'config.vlt') + self.set('tool', 'verilator', 'task', 'compile', 'warningoff', 'TIMESCALEMOD') + + # enable specific warnings that aren't included by default + if self.tool == 'verilator': + if self.warnings is None: + warnings = ['BLKSEQ'] + else: + warnings = self.warnings + + for warning in warnings: + self.set('tool', 'verilator', 'task', 'compile', 'option', f'-Wwarn-{warning}') + + self.set('tool', self.tool, 'task', 'compile', 'var', 'cflags', + ['-Wno-unknown-warning-option']) + self.set('tool', self.tool, 'task', 'compile', 'dir', 'cincludes', [SB_DIR / 'cpp']) + self.set('tool', self.tool, 'task', 'compile', 'var', 'ldflags', ['-pthread']) + + if self.trace and (self.tool == 'verilator'): + self.set('tool', 'verilator', 'task', 'compile', 'var', 'trace_type', self.trace_type) + + if self.tool == 'verilator': + timeunit = self.timeunit + timeprecision = self.timeprecision + + if (timeunit is not None) or (timeprecision is not None): + if timeunit is None: + timeunit = '1ps' # default from Verilator documentation + + if timeprecision is None: + timeprecision = '1ps' # default from Verilator documentation + + timescale = f'{timeunit}/{timeprecision}' + self.add('tool', 'verilator', 'task', 'compile', 'option', '--timescale') + self.add('tool', 'verilator', 'task', 'compile', 'option', timescale) + + if (self.threads is not None) and (self.tool == 'verilator'): + self.add('tool', 'verilator', 'task', 'compile', 'option', '--threads') + self.add('tool', 'verilator', 'task', 'compile', 'option', str(self.threads)) + + self.set('option', 'libext', ['v', 'sv']) + + # Set up flow that compiles RTL + # TODO: this will be built into SC + self.set('option', 'flow', 'simflow') + + compile = importlib.import_module(f'{module}.tools.{self.tool}.compile') + self.node('simflow', 'compile', compile) + + def _configure_icarus(self): + self.add('option', 'libext', 'sv') + self.set('tool', 'icarus', 'task', 'compile', 'var', 'verilog_generation', '2012') + + # use dvflow to execute Icarus, but set steplist so we don't run sim + from siliconcompiler.flows import dvflow + self.use(dvflow) + + self.set('option', 'flow', 'dvflow') + self.set('option', 'to', 'compile') + + def _configure_xyce(self): + if self.xyce: + # already configured, so return early + return + + self.add('option', 'define', 'SB_XYCE') + + if self.tool != 'icarus': + self.input(SB_DIR / 'dpi' / 'xyce_dpi.cc') + + xyce_c_includes, xyce_ld_flags = xyce_flags() + + self.add('tool', self.tool, 'task', 'compile', 'dir', 'cincludes', xyce_c_includes) + self.add('tool', self.tool, 'task', 'compile', 'var', 'ldflags', xyce_ld_flags) + + # indicate that build is configured for Xyce. for Icarus simulation, this flag is used + # to determine whether a VPI object should be built for Xyce + self.xyce = True + + def find_sim(self): + if self.tool == 'icarus': + result_kind = 'vvp' + else: + result_kind = 'vexe' + + return self.find_result(result_kind, step='compile') + + def build(self, cwd: str = None, fast: bool = None): + """ + Parameters + --------- + cwd: str, optional + Working directory for the simulation build + + fast: bool, optional + If True, the simulation binary will not be rebuilt if an existing one + is found. Defaults to the value provided to the SbDut constructor, + which in turn defaults to False. + """ + + if fast is None: + fast = self.fast + + if self.tool == 'icarus': + if (not fast) or (icarus_find_vpi(cwd, name='switchboard') is None): + icarus_build_vpi(cwd, name='switchboard') + if self.xyce and ((not fast) or (icarus_find_vpi(cwd, name='xyce') is None)): + cincludes, ldflags = xyce_flags() + icarus_build_vpi(cwd, name='xyce', cincludes=cincludes, ldflags=ldflags) + + # if "fast" is set, then we can return early if the + # simulation binary already exists + if fast: + sim = self.find_sim() + if sim is not None: + return sim + + # build the wrapper if needed + if self.autowrap: + from .autowrap import autowrap + + filename = Path(self.get('option', 'builddir')).resolve() / 'testbench.sv' + + filename.parent.mkdir(exist_ok=True, parents=True) + + instance = f'{self.dut}_i' + + autowrap( + instances={instance: self.dut}, + parameters={instance: self.parameters}, + interfaces={instance: self.intf_defs}, + clocks={instance: self.clocks}, + resets={instance: self.resets}, + tieoffs={instance: self.tieoffs}, + filename=filename + ) + + self.input(filename) + + # if we get to this point, then we need to rebuild + # the simulation binary + self.run() + + return self.find_sim() + + def simulate( + self, + plusargs=None, + args=None, + extra_args=None, + cwd: str = None, + trace: bool = None, + period: float = None, + frequency: float = None, + max_rate: float = None, + start_delay: float = None, + run: str = None, + intf_objs: bool = True + ) -> subprocess.Popen: + """ + Parameters + ---------- + plusargs: str or list or tuple, optional + additional arguments to pass to simulator that must be preceded + with a +. These are listed after `args`. + + args: str or list or tuple, optional + additional arguments to pass to simulator listed before `plusargs` and + `extra_args` + + extra_args: str or list or tuple, optional + additional arguments to pass to simulator listed after `args` and + `plusargs` + + cwd: str, optional + working directory where simulation binary is saved + + trace: bool, optional + If true, a waveform dump file will be produced + + period: float, optional + If provided, the period of the clock generated in the testbench, + in seconds. + """ + + # set up interfaces if needed + + if max_rate is None: + max_rate = self.max_rate + + if intf_objs: + self.intfs = create_intf_objs(self.intf_defs, max_rate=max_rate) + + # set defaults + + if plusargs is None: + plusargs = [] + else: + plusargs = deepcopy(plusargs) + + if args is None: + args = [] + + if extra_args is None: + extra_args = [] + + if trace is None: + trace = self.trace + + if (period is None) and (frequency is not None): + period = 1 / frequency + + if period is None: + period = self.period + + if start_delay is None: + start_delay = self.start_delay + + # build the simulation if necessary + + sim = self.build(cwd=cwd, fast=True) + + # enable tracing if desired. it's convenient to define +trace + # when running Icarus Verilog, even though it is not necessary, + # since logic in the testbench can use that flag to enable/disable + # waveform dumping in a simulator-agnostic manner. + + if trace: + carefully_add_plusarg(key='trace', args=args, plusargs=plusargs) + + if period is not None: + carefully_add_plusarg(key='period', value=period, args=args, plusargs=plusargs) + + if max_rate is not None: + carefully_add_plusarg(key='max-rate', value=max_rate, args=args, plusargs=plusargs) + + if start_delay is not None: + carefully_add_plusarg( + key='start-delay', value=start_delay, args=args, plusargs=plusargs) + + # add plusargs that define queue connections + + for name, value in self.intf_defs.items(): + wire = value.get('wire', None) + uri = value.get('uri', None) + + if (wire is not None) and (uri is not None): + plusargs += [(wire, uri)] + + # run-specific configurations (if running the same simulator build multiple times + # in parallel) + + if run is not None: + dumpfile = f'{run}.{self.trace_type}' + plusargs.append(('dumpfile', dumpfile)) + + # run the simulation + + p = None + + if self.tool == 'icarus': + names = ['switchboard'] + modules = [] + + if self.xyce: + names.append('xyce') + + for name in names: + vpi = icarus_find_vpi(cwd=cwd, name=name) + assert vpi is not None, f'Could not find VPI binary "{name}"' + modules.append(vpi) + + # set the trace format + if self.trace_type == 'fst' and ('-fst' not in extra_args): + extra_args.append('-fst') + + p = icarus_run( + sim, + plusargs=plusargs, + modules=modules, + extra_args=args + extra_args + ) + else: + # make sure that the simulator was built with tracing enabled + if trace and not self.trace: + raise ValueError('Simulator was built without tracing enabled.' + ' Please set trace=True in the SbDut and try again.') + + if self.tool == 'verilator': + p = verilator_run( + sim, + plusargs=plusargs, + args=args + ) + else: + p = binary_run( + sim, + args=plusargs_to_args(plusargs) + args + ) + + # Add newly created Popen object to subprocess list + self.process_collection.add(p) + + # return a Popen object that one can wait() on + + return p + + def terminate( + self, + stop_timeout=10, + use_sigint=False + ): + self.process_collection.terminate(stop_timeout=stop_timeout, use_sigint=use_sigint) + + def input_analog( + self, + filename: str, + pins: List[Dict[str, Any]] = None, + name: str = None, + check_name: bool = True, + dir: str = None + ): + """ + Specifies a SPICE subcircuit to be used in a mixed-signal simulation. This involves + providing the path to the SPICE file containing the subcircuit definition and describing + how real-valued outputs in the SPICE subcircuit should be converted to binary values in + the Verilog simulation (and vice versa for subcircuit inputs). + + Each of these conversions is specified as an entry in the "pins" argument, which is a + list of dictionaries, each representing a single pin of the SPICE subcircuit. Each + dictionary may have the following keys: + * "name": name of the pin. Bus notation may be used, e.g. "myBus[7:0]". In that case, + it is expected that the SPICE subcircuit has pins corresponding to each bit in the bus, + e.g. "myBus[0]", "myBus[1]", etc. + * "type": direction of the pin. May be "input", "output", or "constant". If "constant", + then this pin will not show up the Verilog module definition to be instantiated in user + code. Instead, the SPICE subcircuit pin with that name will be tied off to a fixed + voltage specified in the "value" field (below). + * "vil": low voltage threshold, below which a real-number voltage from the SPICE + simulation is considered to be a logical "0". + * "vih": high voltage threshold, above which a real-number voltage from the SPICE + simulation is considered to be a logical "1". + * "vol": real-number voltage to pass to a SPICE subcircuit input when the digital value + driven is "0". + * "voh": real-number voltage to pass to a SPICE subcircuit input when the digital value + driven is "1". + * "tr": time taken in the SPICE simulation to transition from a logic "0" value to a + logic "1" value. + * "tf": time taken in the SPICE simulation to transition from a logic "1" value to a + logic "0" value. + * "initial": initial value of a SPICE subcircuit pin. Currently only implemented for + subcircuit outputs. This is sometimes helpful, because there is a slight delay between + t=0 and the time when the SPICE simulation reports values for its outputs. Specifying + "initial" for subcircuit outputs prevents the corresponding digital signals from being + driven to "X" at t=0. + + Parameters + ---------- + filename: str + The path of the SPICE file containing the subcircuit definition. + pins: List[Dict[str, Any]] + List of dictionaries, each describing a pin of the subcircuit. + name: str + Name of the SPICE subcircuit that will be instantiated in the mixed-signal simulation. + If not provided, Switchboard guesses that the name is filename stem. For example, + if filename="myCircuit.cir", then Switchboard will guess that the subcircuit name + is "myCircuit" + check_name: bool + If True (default), Switchboard parses the provided file to make sure that there + is a subcircuit definition matching the given name. + dir: str + Running a mixed-signal simulation involves creating SPICE and Verilog wrappers. This + argument specifies the directory where those wrappers should be written. If not + provided, defaults to the directory where filename is located. + """ + + # automatically configures for Xyce co-simulation if not already configured + + self._configure_xyce() + + # set defaults + + if pins is None: + pins = [] + + if name is None: + # guess the name of the subcircuit from the filename + guessed = True + name = Path(filename).stem + else: + guessed = False + + if check_name: + # make sure that a subcircuit matching the provided or guessed + # name exists in the file provided. this is not foolproof, since + # the SPICE parser is minimal and won't consider things like + # .INCLUDE. hence, this feature can be disabled by setting + # check_name=False + + subckts = parse_spice_subckts(filename) + + for subckt in subckts: + if name.lower() == name.lower(): + break + else: + if guessed: + raise Exception(f'Inferred subckt named "{name}" from the filename,' + ' however a corresponding subckt definition was not found. Please' + ' specify a subckt name via the "name" argument.') + else: + raise Exception(f'Could not find a subckt definition for "{name}".') + + if dir is None: + dir = Path(filename).resolve().parent + + spice_wrapper = make_ams_spice_wrapper( + name=name, + filename=filename, + pins=pins, + dir=dir + ) + + verilog_wrapper = make_ams_verilog_wrapper( + name=name, + filename=spice_wrapper, + pins=pins, + dir=dir + ) + + self.input(verilog_wrapper) + + def package(self, suffix=None, fast=None): + # set defaults + + if suffix is None: + suffix = self.suffix + + if fast is None: + fast = self.fast + + # see if we can exit early + + if fast: + package = self.find_package(suffix=suffix) + + if package is not None: + return package + + # if not, parse with surelog and postprocess with morty + + if suffix: + self.set('tool', 'morty', 'task', 'uniquify', 'var', 'suffix', suffix) + + self.set('tool', 'sed', 'task', 'remove', 'var', 'to_remove', '`resetall') + + self.run() + + # return the path to the output + return self.find_package(suffix=suffix) + + def find_package(self, suffix=None): + if suffix is None: + return self.find_result('v', step='parse') + else: + return self.find_result('v', step='uniquify') + + +def metadata_str(design: str, tool: str = None, trace: bool = False, + trace_type: str = None, threads: int = None, parameters: dict = None) -> Path: + + opts = [] + + opts += [design] + + if parameters is not None: + for k, v in parameters.items(): + opts += [k, v] + + if tool is not None: + opts += [tool] + + if trace: + assert trace_type is not None + opts += [trace_type] + + if threads is not None: + opts += ['threads', threads] + + return '-'.join(str(opt) for opt in opts) + + +def carefully_add_plusarg(key, args, plusargs, value=None): + for plusarg in plusargs: + if isinstance(plusarg, (list, tuple)): + if (len(plusarg) >= 1) and (key == plusarg[0]): + return + elif key == plusarg: + return + + if f'+{key}' in args: + return + + if any(elem.startswith(f'+{key}+') for elem in args): + return + + if any(elem.startswith(f'+{key}=') for elem in args): + return + + if value is None: + plusargs.append(key) + else: + plusargs.append((key, value)) diff --git a/switchboard/verilog/__init__.py b/switchboard/verilog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/verilog/common/__init__.py b/switchboard/verilog/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/verilog/common/common.py b/switchboard/verilog/common/common.py new file mode 100644 index 00000000..ef265720 --- /dev/null +++ b/switchboard/verilog/common/common.py @@ -0,0 +1,24 @@ +from siliconcompiler import Design + +from switchboard import sb_path + + +class Common(Design): + def __init__(self): + + super().__init__("common") + + files = [ + "uart_xactor.sv", + "umi_gpio.v" + ] + deps = [] + + self.set_dataroot('sb_verilog_common', sb_path() / "verilog" / "common") + + with self.active_fileset('rtl'): + self.add_idir(".") + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) diff --git a/switchboard/verilog/fpga/__init__.py b/switchboard/verilog/fpga/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/verilog/fpga/fpga.py b/switchboard/verilog/fpga/fpga.py new file mode 100644 index 00000000..a439b2dd --- /dev/null +++ b/switchboard/verilog/fpga/fpga.py @@ -0,0 +1,19 @@ +from siliconcompiler import Design + +from switchboard import path as sb_path + + +class FPGA(Design): + def __init__(self): + files = [ + "axi_reader.sv" + ] + deps = [] + + self.set_dataroot('sb_verilog_fpga', sb_path() / "verilog" / "fpga") + + with self.active_fileset('rtl'): + for item in files: + self.add_files(item) + for item in deps: + self.add_depfileset(item) diff --git a/switchboard/verilog/sim/__init__.py b/switchboard/verilog/sim/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/verilog/sim/sim.py b/switchboard/verilog/sim/sim.py new file mode 100644 index 00000000..0b25c016 --- /dev/null +++ b/switchboard/verilog/sim/sim.py @@ -0,0 +1,26 @@ +from siliconcompiler import Design + +from switchboard import sb_path + + +class Sim(Design): + def __init__(self): + super().__init__("sb_sim") + + files = [ + "sb_clk_gen.sv", + "queue_to_sb_sim.sv", + "sb_to_queue_sim.sv", + "umi_to_queue_sim.sv", + "queue_to_umi_sim.sv", + "umi_rx_sim.sv" + ] + deps = [] + + self.set_dataroot('sb_verilog_sim', sb_path() / "verilog" / "sim") + + with self.active_fileset('rtl'): + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) From e8ed4067767c72482dc578ecd932068c015bdc30 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Thu, 9 Oct 2025 14:47:04 -0400 Subject: [PATCH 04/20] WIP: Verilator working --- examples/network/test.py | 120 ++++++++++++++--- examples/test_examples.py | 4 +- examples/umiram/test.py | 62 +++------ examples/umiram/umiram.py | 44 +++++++ switchboard/__init__.py | 2 +- switchboard/deps/verilog_axi.py | 5 +- switchboard/dpi/__init__.py | 0 switchboard/dpi/switchboard_dpi.py | 13 ++ switchboard/sbdut.py | 121 ++++++++++++------ switchboard/verilator/__init__.py | 0 switchboard/verilator/verilator.py | 13 ++ .../{verilator.py => verilator_run.py} | 0 switchboard/verilog/common/common.py | 4 +- switchboard/verilog/fpga/fpga.py | 6 +- switchboard/verilog/sim/sim.py | 26 ---- switchboard/verilog/sim/switchboard_sim.py | 47 +++++++ 16 files changed, 332 insertions(+), 135 deletions(-) create mode 100644 examples/umiram/umiram.py create mode 100644 switchboard/dpi/__init__.py create mode 100644 switchboard/dpi/switchboard_dpi.py create mode 100644 switchboard/verilator/__init__.py create mode 100644 switchboard/verilator/verilator.py rename switchboard/{verilator.py => verilator_run.py} (100%) delete mode 100644 switchboard/verilog/sim/sim.py create mode 100644 switchboard/verilog/sim/switchboard_sim.py diff --git a/examples/network/test.py b/examples/network/test.py index daef8c08..f5d1e117 100755 --- a/examples/network/test.py +++ b/examples/network/test.py @@ -22,8 +22,13 @@ def main(): # create the building blocks umi_fifo = make_umi_fifo(net) + umi_fifo.option.set_nodashboard(True) + umi2axil = make_umi2axil(net) + umi2axil.option.set_nodashboard(True) + axil_ram = make_axil_ram(net) + axil_ram.option.set_nodashboard(True) # connect them together @@ -103,12 +108,39 @@ def make_umi_fifo(net): 'umi_out_nreset' ] - dut = net.make_dut('umi_fifo', parameters=parameters, interfaces=interfaces, - clocks=clocks, resets=resets, tieoffs=tieoffs) + from siliconcompiler import Design + + class UmiFifo(Design): + def __init__(self): + super().__init__('umi_fifo_wrapper') - dut.use(sumi) + top_module = "umi_fifo" - dut.input('sumi/rtl/umi_fifo.v', package='umi') + from umi.sumi import Fifo + with self.active_fileset('rtl'): + self.set_topmodule(top_module) + self.add_depfileset(Fifo()) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + dut = net.make_dut( + design=UmiFifo(), + parameters=parameters, + interfaces=interfaces, + clocks=clocks, + resets=resets, + tieoffs=tieoffs + ) + + #dut.use(sumi) + + #dut.input('sumi/rtl/umi_fifo.v', package='umi') return dut @@ -128,19 +160,48 @@ def make_axil_ram(net): resets = [dict(name='rst', delay=8)] - dut = net.make_dut('axil_ram', parameters=parameters, - interfaces=interfaces, resets=resets) + from siliconcompiler import Design + + class AxilRam(Design): + def __init__(self): + super().__init__('axil_ram') + + self.set_dataroot( + name='axil_ram', + path="git+https://github.com/alexforencich/verilog-axi.git", + tag="38915fb" + ) + top_module = "axil_ram" - dut.register_source( - 'verilog-axi', - 'git+https://github.com/alexforencich/verilog-axi.git', - '38915fb' + with self.active_fileset('rtl'): + self.add_file('rtl/axil_ram.v') + self.set_topmodule(top_module) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + dut = net.make_dut( + design=AxilRam(), + parameters=parameters, + interfaces=interfaces, + resets=resets ) - dut.input('rtl/axil_ram.v', package='verilog-axi') + #dut.register_source( + # 'verilog-axi', + # 'git+https://github.com/alexforencich/verilog-axi.git', + # '38915fb' + #) - dut.add('tool', 'verilator', 'task', 'compile', 'warningoff', - ['WIDTHTRUNC', 'TIMESCALEMOD']) + #dut.input('rtl/axil_ram.v', package='verilog-axi') + + #dut.add('tool', 'verilator', 'task', 'compile', 'warningoff', + # ['WIDTHTRUNC', 'TIMESCALEMOD']) return dut @@ -164,12 +225,37 @@ def make_umi2axil(net): resets = ['nreset'] - dut = net.make_dut('umi2axilite', parameters=parameters, - interfaces=interfaces, resets=resets) + from siliconcompiler import Design + + class Umi2Axil(Design): + def __init__(self): + super().__init__('umi2axil_wrapper') + + top_module = "umi2axil" + + from umi.adapters import UMI2AXIL + with self.active_fileset('rtl'): + self.set_topmodule(top_module) + self.add_depfileset(UMI2AXIL()) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + dut = net.make_dut( + design=Umi2Axil(), + parameters=parameters, + interfaces=interfaces, + resets=resets + ) - dut.use(sumi) + #dut.use(sumi) - dut.input('utils/rtl/umi2axilite.v', package='umi') + #dut.input('utils/rtl/umi2axilite.v', package='umi') return dut diff --git a/examples/test_examples.py b/examples/test_examples.py index 1e152236..b5e99e80 100755 --- a/examples/test_examples.py +++ b/examples/test_examples.py @@ -41,8 +41,8 @@ ['umiparam-network', None, 'icarus'], ['umiram', None, 'cpp'], ['umiram', None, 'verilator'], - ['xyce', None, 'icarus'], - ['xyce', None, 'verilator'] + #['xyce', None, 'icarus'], + #['xyce', None, 'verilator'] ]) def test_make(path, expected, target): cmd = ['make'] diff --git a/examples/umiram/test.py b/examples/umiram/test.py index 95ce6943..47976762 100755 --- a/examples/umiram/test.py +++ b/examples/umiram/test.py @@ -10,9 +10,10 @@ import numpy as np from pathlib import Path from switchboard import SbDut, UmiTxRx, binary_run -from umi.sumi import Fifo, RAM -from siliconcompiler import Design +from switchboard.cmdline import get_cmdline_args + +from umiram import UmiRam THIS_DIR = Path(__file__).resolve().parent @@ -86,52 +87,21 @@ def python_intf(umi): assert val2 == 0xCD -class UmiRam(Design): - - def __init__(self): - super().__init__("testbench") - - from switchboard import sb_path - - from switchboard.verilog.common.common import Common - from switchboard.verilog.sim.sim import Sim as SB_SIM - - dr_path = sb_path() / ".." / "examples" / "umiram" - - self.set_dataroot('umiram', dr_path) - - files = [ - "testbench.sv", - "../common/verilog/umiram.sv" - ] - - deps = [Common(), SB_SIM(), Fifo(), RAM()] - - with self.active_fileset('rtl'): - self.set_topmodule("testbench") - for item in files: - self.add_file(item) - for item in deps: - self.add_depfileset(item) - +def main(): -def build_testbench(): extra_args = { '--mode': dict(default='python', choices=['python', 'cpp'], help='Programming language used for the test stimulus.') } - dut = SbDut(UmiRam()) - dut.option.set_nodashboard(True) + args = get_cmdline_args(extra_args=extra_args) - print(f"dut.build() output = {dut.build()}") + dut = SbDut( + UmiRam(), + fileset=args.tool + ) - return dut - - -def main(): - # build the simulator - dut = build_testbench() + dut.build() # create queues umi = UmiTxRx('to_rtl.q', 'from_rtl.q', fresh=True) @@ -139,12 +109,12 @@ def main(): # launch the simulation dut.simulate() - #if dut.args.mode == 'python': - #python_intf(umi) - #elif dut.args.mode == 'cpp': - binary_run(THIS_DIR / 'client').wait() - #else: - # raise ValueError(f'Invalid mode: {dut.args.mode}') + if args.mode == 'python': + python_intf(umi) + elif args.mode == 'cpp': + binary_run(THIS_DIR / 'client').wait() + else: + raise ValueError(f'Invalid mode: {dut.args.mode}') if __name__ == '__main__': diff --git a/examples/umiram/umiram.py b/examples/umiram/umiram.py new file mode 100644 index 00000000..7042c71f --- /dev/null +++ b/examples/umiram/umiram.py @@ -0,0 +1,44 @@ +from siliconcompiler import Design + +from umi.sumi import Fifo, RAM + +from switchboard.verilog.sim.switchboard_sim import SwitchboardSim +from switchboard import sb_path + + +class UmiRam(Design): + + def __init__(self): + super().__init__("testbench") + + top_module = "testbench" + + dr_path = sb_path() / ".." / "examples" / "umiram" + + self.set_dataroot('umiram', dr_path) + + files = [ + "testbench.sv", + "../common/verilog/umiram.sv" + ] + + deps = [ + Fifo(), + RAM() + ] + + with self.active_fileset('rtl'): + self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") diff --git a/switchboard/__init__.py b/switchboard/__init__.py index 39985d59..e5dc96ff 100644 --- a/switchboard/__init__.py +++ b/switchboard/__init__.py @@ -11,7 +11,7 @@ from .umi import UmiTxRx, random_umi_packet from .util import binary_run, ProcessCollection -from .verilator import verilator_run +#from .verilator import verilator_run from .icarus import icarus_build_vpi, icarus_run from .sbdut import SbDut from .loopback import umi_loopback diff --git a/switchboard/deps/verilog_axi.py b/switchboard/deps/verilog_axi.py index b69c5b90..6a1e0f08 100644 --- a/switchboard/deps/verilog_axi.py +++ b/switchboard/deps/verilog_axi.py @@ -3,9 +3,10 @@ class VerilogAxi(Design): def __init__(self): + super().__init__('verilog_axis') self.set_dataroot( - name="verilog_axi", + name="verilog_axis", path="git+https://github.com/alexforencich/verilog-axis.git@master", tag="25912d48fec2abbf3565bbefe402c1cff99fe470" ) @@ -18,5 +19,5 @@ def __init__(self): with self.active_fileset('rtl'): for item in files: - self.add_files(item) + self.add_file(item) diff --git a/switchboard/dpi/__init__.py b/switchboard/dpi/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/dpi/switchboard_dpi.py b/switchboard/dpi/switchboard_dpi.py new file mode 100644 index 00000000..fdeff87a --- /dev/null +++ b/switchboard/dpi/switchboard_dpi.py @@ -0,0 +1,13 @@ +from siliconcompiler import Design + +from switchboard import sb_path + + +class SwitchboardDPI(Design): + def __init__(self): + super().__init__("switchboard_dpi") + + self.set_dataroot('localroot', sb_path() / "dpi") + + with self.active_fileset('sim'): + self.add_file("switchboard_dpi.cc") diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 0a543e84..45901244 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -16,17 +16,16 @@ from copy import deepcopy from pathlib import Path -from typing import List, Dict, Any, Union +from typing import List, Dict, Any, Union, Tuple from .switchboard import path as sb_path -from .verilator import verilator_run from .icarus import icarus_build_vpi, icarus_find_vpi, icarus_run +from .verilator_run import verilator_run from .util import plusargs_to_args, binary_run, ProcessCollection from .xyce import xyce_flags from .ams import make_ams_spice_wrapper, make_ams_verilog_wrapper, parse_spice_subckts from .autowrap import (normalize_clocks, normalize_interfaces, normalize_resets, normalize_tieoffs, normalize_parameters, create_intf_objs) -from .cmdline import get_cmdline_args from siliconcompiler import Design, Sim @@ -38,6 +37,7 @@ class SbDut(Sim): def __init__( self, design: Union[Design, str] = None, + fileset: str = 'verilator', tool: str = 'icarus', default_main: bool = True, trace: bool = True, @@ -52,9 +52,7 @@ def __init__( timeunit: str = None, timeprecision: str = None, warnings: List[str] = None, - cmdline: bool = False, fast: bool = False, - extra_args: dict = None, autowrap: bool = False, parameters=None, interfaces=None, @@ -70,39 +68,18 @@ def __init__( ): super().__init__(design) - self.add_fileset("rtl") + self.add_fileset(fileset) - top_lvl_module_name = None + self.fileset = fileset + + self.top_lvl_module_name = None main_filesets = self.option.get_fileset() if main_filesets and len(main_filesets) != 0: main_fileset = main_filesets[0] - top_lvl_module_name = design.get_topmodule( + self.top_lvl_module_name = design.get_topmodule( fileset=main_fileset ) - - # parse command-line options if desired - - if cmdline: - self.args = get_cmdline_args(tool=tool, trace=trace, trace_type=trace_type, - frequency=frequency, period=period, fast=fast, max_rate=max_rate, - start_delay=start_delay, threads=threads, extra_args=extra_args) - elif args is not None: - self.args = args - else: - self.args = None - - if self.args is not None: - trace = self.args.trace - trace_type = self.args.trace_type - fast = self.args.fast - tool = self.args.tool - frequency = self.args.frequency - period = self.args.period - max_rate = self.args.max_rate - start_delay = self.args.start_delay - threads = self.args.threads - # input validation if trace_type not in ('vcd', 'fst'): @@ -132,14 +109,14 @@ def __init__( self.autowrap = autowrap if (suffix is None) and subcomponent: - suffix = f'_unq_{top_lvl_module_name}' + suffix = f'_unq_{self.top_lvl_module_name}' self.suffix = suffix if suffix is not None: - self.dut = f'{top_lvl_module_name}{suffix}' + self.dut = f'{self.top_lvl_module_name}{suffix}' else: - self.dut = top_lvl_module_name + self.dut = self.top_lvl_module_name self.parameters = normalize_parameters(parameters) self.intf_defs = normalize_interfaces(interfaces) @@ -166,12 +143,12 @@ def __init__( # the subcomponent build flow is tool-agnostic, producing a single Verilog # file as output, as opposed to a simulator binary builddir = buildroot / metadata_str( - design=top_lvl_module_name, + design=self.top_lvl_module_name, parameters=parameters ) else: builddir = buildroot / metadata_str( - design=top_lvl_module_name, + design=self.top_lvl_module_name, parameters=parameters, tool=tool, trace=trace, @@ -183,12 +160,51 @@ def __init__( # preserve old behavior self.option.set_clean(True) + if not subcomponent: + if self.tool == 'icarus': + self._configure_icarus() + elif self.tool == 'verilator': + self._configure_verilator() + else: + flowname = package - if self.tool == 'icarus': - self._configure_icarus() + def _configure_verilator(self): + from siliconcompiler.flows.dvflow import DVFlow + + self.set_flow(DVFlow(tool="verilator")) + from siliconcompiler.tools import get_task + from siliconcompiler.tools.verilator.compile import CompileTask + from siliconcompiler.tools.verilator import VerilatorTask + + get_task(self, filter=VerilatorTask).add_warningoff("TIMESCALEMOD") + + get_task(self, filter=CompileTask).set("var", "cincludes", [SB_DIR / 'cpp']) + #self.set('tool', self.tool, 'task', 'compile', 'var', 'ldflags', ['-pthread']) + + if self.trace and (self.tool == 'verilator'): + get_task(self, filter=CompileTask).set("var", "trace_type", self.trace_type) + #if self.tool == 'verilator': + # timeunit = self.timeunit + # timeprecision = self.timeprecision + # if (timeunit is not None) or (timeprecision is not None): + # if timeunit is None: + # timeunit = '1ps' # default from Verilator documentation + # if timeprecision is None: + # timeprecision = '1ps' # default from Verilator documentation + + # timescale = f'{timeunit}/{timeprecision}' + # self.add('tool', 'verilator', 'task', 'compile', 'option', '--timescale') + # self.add('tool', 'verilator', 'task', 'compile', 'option', timescale) + + #if (self.threads is not None) and (self.tool == 'verilator'): + # self.add('tool', 'verilator', 'task', 'compile', 'option', '--threads') + # self.add('tool', 'verilator', 'task', 'compile', 'option', str(self.threads)) + + # Set up flow that compiles RTL + self.set('option', 'to', 'compile') def _configure_icarus(self): # use dvflow to execute Icarus, but set steplist so we don't run sim @@ -240,6 +256,35 @@ def build(self, cwd: str = None, fast: bool = None): if sim is not None: return sim + # build the wrapper if needed + if self.autowrap: + from .autowrap import autowrap + + filename = Path(self.option.get_builddir()).resolve() / 'testbench.sv' + + filename.parent.mkdir(exist_ok=True, parents=True) + + instance = f'{self.top_lvl_module_name}_i' + + autowrap( + instances={instance: self.top_lvl_module_name}, + parameters={instance: self.parameters}, + interfaces={instance: self.intf_defs}, + clocks={instance: self.clocks}, + resets={instance: self.resets}, + tieoffs={instance: self.tieoffs}, + filename=filename + ) + + from switchboard.verilog.sim.switchboard_sim import SwitchboardSim + with self.design.active_fileset(self.fileset): + self.design.set_topmodule("testbench") + self.design.add_depfileset(SwitchboardSim()) + self.design.add_file(str(filename)) + + self.set_design(self.design) + self.add_fileset(self.fileset) + assert self.run() return self.find_sim() diff --git a/switchboard/verilator/__init__.py b/switchboard/verilator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/switchboard/verilator/verilator.py b/switchboard/verilator/verilator.py new file mode 100644 index 00000000..5061b1c3 --- /dev/null +++ b/switchboard/verilator/verilator.py @@ -0,0 +1,13 @@ +from siliconcompiler import Design + +from switchboard import sb_path + + +class Verilator(Design): + def __init__(self): + super().__init__("verilator") + + self.set_dataroot('localroot', sb_path() / "verilator") + + with self.active_fileset('sim'): + self.add_file("testbench.cc") diff --git a/switchboard/verilator.py b/switchboard/verilator_run.py similarity index 100% rename from switchboard/verilator.py rename to switchboard/verilator_run.py diff --git a/switchboard/verilog/common/common.py b/switchboard/verilog/common/common.py index ef265720..011994c0 100644 --- a/switchboard/verilog/common/common.py +++ b/switchboard/verilog/common/common.py @@ -1,5 +1,7 @@ from siliconcompiler import Design +from umi.sumi import Unpack, Pack + from switchboard import sb_path @@ -12,7 +14,7 @@ def __init__(self): "uart_xactor.sv", "umi_gpio.v" ] - deps = [] + deps = [Unpack(), Pack()] self.set_dataroot('sb_verilog_common', sb_path() / "verilog" / "common") diff --git a/switchboard/verilog/fpga/fpga.py b/switchboard/verilog/fpga/fpga.py index a439b2dd..2c56ff67 100644 --- a/switchboard/verilog/fpga/fpga.py +++ b/switchboard/verilog/fpga/fpga.py @@ -1,10 +1,12 @@ from siliconcompiler import Design -from switchboard import path as sb_path +from switchboard import sb_path class FPGA(Design): def __init__(self): + super().__init__("FPGA") + files = [ "axi_reader.sv" ] @@ -14,6 +16,6 @@ def __init__(self): with self.active_fileset('rtl'): for item in files: - self.add_files(item) + self.add_file(item) for item in deps: self.add_depfileset(item) diff --git a/switchboard/verilog/sim/sim.py b/switchboard/verilog/sim/sim.py deleted file mode 100644 index 0b25c016..00000000 --- a/switchboard/verilog/sim/sim.py +++ /dev/null @@ -1,26 +0,0 @@ -from siliconcompiler import Design - -from switchboard import sb_path - - -class Sim(Design): - def __init__(self): - super().__init__("sb_sim") - - files = [ - "sb_clk_gen.sv", - "queue_to_sb_sim.sv", - "sb_to_queue_sim.sv", - "umi_to_queue_sim.sv", - "queue_to_umi_sim.sv", - "umi_rx_sim.sv" - ] - deps = [] - - self.set_dataroot('sb_verilog_sim', sb_path() / "verilog" / "sim") - - with self.active_fileset('rtl'): - for item in files: - self.add_file(item) - for item in deps: - self.add_depfileset(item) diff --git a/switchboard/verilog/sim/switchboard_sim.py b/switchboard/verilog/sim/switchboard_sim.py new file mode 100644 index 00000000..1aa5dc06 --- /dev/null +++ b/switchboard/verilog/sim/switchboard_sim.py @@ -0,0 +1,47 @@ +from siliconcompiler import Design + +from switchboard.verilator.verilator import Verilator +from switchboard.dpi.switchboard_dpi import SwitchboardDPI +from switchboard.verilog.common.common import Common +from switchboard import sb_path + + +class SwitchboardSim(Design): + def __init__(self): + super().__init__("sb_sim") + + files = [ + "auto_stop_sim.sv", + "perf_meas_sim.sv", + "queue_to_sb_sim.sv", + "queue_to_umi_sim.sv", + "sb_axil_m.sv", + "sb_axi_m.sv", + "sb_jtag_rbb_sim.sv", + "sb_to_queue_sim.sv", + "umi_to_queue_sim.sv", + "sb_axil_s.sv", + "sb_clk_gen.sv", + "sb_rx_sim.sv", + "sb_tx_sim.sv", + "umi_rx_sim.sv", + "umi_tx_sim.sv", + "xyce_intf.sv" + ] + deps = [Common()] + + self.set_dataroot("sb_verilog_sim", sb_path() / "verilog" / "sim") + + with self.active_fileset("rtl"): + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) + + with self.active_fileset("verilator"): + self.add_depfileset(self, "rtl") + self.add_depfileset(Verilator()) + self.add_depfileset(SwitchboardDPI()) + + with self.active_fileset("icarus"): + self.add_depfileset(self, "rtl") From dfb316e266c76ef67bb7615d18adce051d3e1a7c Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Thu, 9 Oct 2025 22:32:04 -0400 Subject: [PATCH 05/20] WIP: verilator standalone netlist working. Needs major cleaning / refactor --- examples/network/test.py | 3 + switchboard/network.py | 63 +++++++++++++++------ switchboard/sbdut.py | 15 +++-- switchboard/sc/morty/morty.py | 67 +++++++++++++++++++++++ switchboard/sc/sed/sed.py | 8 --- switchboard/sc/sed/sed_remove.py | 56 +++++++++++++++++++ switchboard/sc/standalone_netlist_flow.py | 25 +++++++++ 7 files changed, 205 insertions(+), 32 deletions(-) delete mode 100644 switchboard/sc/sed/sed.py create mode 100644 switchboard/sc/sed/sed_remove.py create mode 100644 switchboard/sc/standalone_netlist_flow.py diff --git a/examples/network/test.py b/examples/network/test.py index f5d1e117..394258b9 100755 --- a/examples/network/test.py +++ b/examples/network/test.py @@ -131,6 +131,7 @@ def __init__(self): dut = net.make_dut( design=UmiFifo(), + fileset="verilator", parameters=parameters, interfaces=interfaces, clocks=clocks, @@ -187,6 +188,7 @@ def __init__(self): dut = net.make_dut( design=AxilRam(), + fileset="verilator", parameters=parameters, interfaces=interfaces, resets=resets @@ -248,6 +250,7 @@ def __init__(self): dut = net.make_dut( design=Umi2Axil(), + fileset="verilator", parameters=parameters, interfaces=interfaces, resets=resets diff --git a/switchboard/network.py b/switchboard/network.py index b8b497f3..1444d4fc 100644 --- a/switchboard/network.py +++ b/switchboard/network.py @@ -185,7 +185,14 @@ def __init__(self, cmdline=False, tool: str = 'verilator', trace: bool = False, self.single_netlist = single_netlist if single_netlist: - self.dut = SbDut(args=self.args) + from siliconcompiler import Design + class SNDesign(Design): + def __init__(self): + super().__init__("SNDesign") + + + + self.dut = SbDut(design=SNDesign(), args=self.args) else: self._intf_defs = {} @@ -214,7 +221,7 @@ def instantiate(self, block, name: str = None): # generate a name if needed if name is None: if isinstance(block, SbDut): - prefix = block.dut + prefix = block.top_lvl_module_name else: prefix = block.name @@ -404,11 +411,19 @@ def build(self): ('tool', 'verilator', 'task', 'compile', 'warningoff') ] + self.dut.fileset = "verilator" for block in unique_blocks: - self.dut.input(block.package()) - - for passthrough in passthroughs: - self.dut.add(*passthrough, block.get(*passthrough)) + with self.dut.design.active_fileset(self.dut.fileset): + #self.dut.design.set_topmodule("testbench") + #self.dut.design.add_depfileset(SwitchboardSim()) + print(f"running block {block.design}") + block_pkg = block.package() + print(f"block_pkg = {block_pkg}") + self.dut.design.add_file(block.package()) + #self.dut.input(block.package()) + + #for passthrough in passthroughs: + #self.dut.add(*passthrough, block.get(*passthrough)) filename = Path(self.dut.get('option', 'builddir')).resolve() / 'testbench.sv' @@ -435,19 +450,31 @@ def build(self): interfaces[inst_name] = intf_defs # generate netlist that connects everything together, and input() it - self.dut.input( - autowrap( - instances={inst.name: inst.block.dut for inst in self.insts.values()}, - toplevel='testbench', - parameters={inst.name: inst.block.parameters for inst in self.insts.values()}, - interfaces=interfaces, - clocks={inst.name: inst.block.clocks for inst in self.insts.values()}, - resets={inst.name: inst.block.resets for inst in self.insts.values()}, - tieoffs={inst.name: inst.block.tieoffs for inst in self.insts.values()}, - filename=filename - ) + top_lvl = autowrap( + instances={inst.name: inst.block.dut for inst in self.insts.values()}, + toplevel='testbench', + parameters={inst.name: inst.block.parameters for inst in self.insts.values()}, + interfaces=interfaces, + clocks={inst.name: inst.block.clocks for inst in self.insts.values()}, + resets={inst.name: inst.block.resets for inst in self.insts.values()}, + tieoffs={inst.name: inst.block.tieoffs for inst in self.insts.values()}, + filename=filename ) + print(f"adding top lvl = {top_lvl}") + from switchboard.verilog.sim.switchboard_sim import SwitchboardSim + with self.dut.design.active_fileset(self.dut.fileset): + self.dut.design.set_topmodule("testbench") + self.dut.design.add_depfileset(SwitchboardSim()) + self.dut.design.add_file(str(top_lvl)) + + self.dut.set_design(self.dut.design) + self.dut.add_fileset(self.dut.fileset) + + + + + # build the single-netlist simulation self.dut.build() else: @@ -659,5 +686,5 @@ def make_dut(self, *args, **kwargs): for k, v in cfg.items(): if k not in kwargs: kwargs[k] = v - + print(f"design args = {args}, kwargs = {kwargs}") return SbDut(*args, **kwargs) diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 45901244..7f0e4fc6 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -37,7 +37,7 @@ class SbDut(Sim): def __init__( self, design: Union[Design, str] = None, - fileset: str = 'verilator', + fileset: str = None, tool: str = 'icarus', default_main: bool = True, trace: bool = True, @@ -68,7 +68,8 @@ def __init__( ): super().__init__(design) - self.add_fileset(fileset) + if fileset: + self.add_fileset(fileset) self.fileset = fileset @@ -166,7 +167,9 @@ def __init__( elif self.tool == 'verilator': self._configure_verilator() else: - flowname = package + from switchboard.sc.standalone_netlist_flow import StandaloneNetlistFlow + self.set_flow(StandaloneNetlistFlow()) + def _configure_verilator(self): from siliconcompiler.flows.dvflow import DVFlow @@ -595,7 +598,7 @@ def package(self, suffix=None, fast=None): # if not, parse with surelog and postprocess with morty if suffix: - self.set('tool', 'morty', 'task', 'uniquify', 'var', 'suffix', suffix) + self.set('tool', 'morty', 'task', 'uniquify_verilog_modules', 'var', 'suffix', suffix) self.set('tool', 'sed', 'task', 'remove', 'var', 'to_remove', '`resetall') @@ -606,9 +609,9 @@ def package(self, suffix=None, fast=None): def find_package(self, suffix=None): if suffix is None: - return self.find_result('v', step='parse') + return self.find_result('sv', step='parse') else: - return self.find_result('v', step='uniquify') + return self.find_result('sv', step='uniquify') def metadata_str(design: str, tool: str = None, trace: bool = False, diff --git a/switchboard/sc/morty/morty.py b/switchboard/sc/morty/morty.py index 97bdccdc..33dbbc85 100644 --- a/switchboard/sc/morty/morty.py +++ b/switchboard/sc/morty/morty.py @@ -1,6 +1,9 @@ # Copyright (c) 2024 Zero ASIC Corporation # This code is licensed under Apache License 2.0 (see LICENSE for details) +from siliconcompiler import Task + + def setup(chip): tool = 'morty' @@ -12,3 +15,67 @@ def setup(chip): def parse_version(stdout): return stdout.split()[-1] + + +class UniquifyVerilogModules(Task): + + def __init__(self): + super().__init__() + + self.add_parameter( + name='suffix', + type='str', + help='suffix to be added to the end of module names' + ) + + self.add_parameter( + name='prefix', + type='str', + help='prefix to be added to the beginning of module names' + ) + + def tool(self): + return "morty" + + def task(self): + return "uniquify_verilog_modules" + + def setup(self): + super().setup() + + self.set_exe("morty") + + self.add_input_file(ext="sv") + self.add_output_file(ext="sv") + + def runtime_options(self): + options = super().runtime_options() + + idirs = [] + for lib, fileset in self.project.get_filesets(): + idirs.extend(lib.get_idir(fileset)) + + cmdlist = [] + + prefix = self.get("var", "prefix") + if prefix: + cmdlist.extend(['--prefix', prefix]) + + suffix = self.get("var", "suffix") + if suffix: + print(f"got suffix = {suffix}") + cmdlist.extend(['--suffix', suffix]) + + out_file = f"outputs/{self.design_topmodule}.sv" + + cmdlist.extend(['-o', out_file]) + + for value in idirs: + cmdlist.append('-I' + value) + + input_file = f"inputs/{self.design_topmodule}.sv" + + options.extend(cmdlist) + options.append(input_file) + + return options diff --git a/switchboard/sc/sed/sed.py b/switchboard/sc/sed/sed.py deleted file mode 100644 index 9a1d2950..00000000 --- a/switchboard/sc/sed/sed.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2024 Zero ASIC Corporation -# This code is licensed under Apache License 2.0 (see LICENSE for details) - -def setup(chip): - tool = 'sed' - - chip.set('tool', tool, 'exe', 'sed') - chip.set('tool', tool, 'vendor', tool) diff --git a/switchboard/sc/sed/sed_remove.py b/switchboard/sc/sed/sed_remove.py new file mode 100644 index 00000000..2fe02a95 --- /dev/null +++ b/switchboard/sc/sed/sed_remove.py @@ -0,0 +1,56 @@ +# Copyright (c) 2024 Zero ASIC Corporation +# This code is licensed under Apache License 2.0 (see LICENSE for details) + +from siliconcompiler import Task + + +class SedRemove(Task): + + def __init__(self): + super().__init__() + + self.add_parameter( + name="to_remove", + type="[str]", + help="strings to remove from the Verilog source file" + ) + + def tool(self): + return "sed" + + def task(self): + return "remove" + + def setup(self): + super().setup() + + self.set_exe("sed") + + self.add_output_file(ext="sv") + + def runtime_options(self): + options = super().runtime_options() + + to_remove = self.get("var", "to_remove") + + print(f"to remove = {to_remove}") + + script = [f's/{elem}//g' for elem in to_remove] + script += [f'w outputs/{self.design_topmodule}.sv'] + print(f"script = {script}") + script = '; '.join(script) + + options.extend(["-n", f'{script}', ]) + + ####################### + # Sources + ####################### + filesets = self.project.get_filesets() + for lib, fileset in filesets: + for value in lib.get_file(fileset=fileset, filetype="systemverilog"): + options.append(value) + for lib, fileset in filesets: + for value in lib.get_file(fileset=fileset, filetype="verilog"): + options.append(value) + + return options diff --git a/switchboard/sc/standalone_netlist_flow.py b/switchboard/sc/standalone_netlist_flow.py new file mode 100644 index 00000000..8d13eeb6 --- /dev/null +++ b/switchboard/sc/standalone_netlist_flow.py @@ -0,0 +1,25 @@ +from siliconcompiler import Flowgraph +from siliconcompiler.tools.surelog.parse import ElaborateTask + +from switchboard.sc.sed.sed_remove import SedRemove +from switchboard.sc.morty.morty import UniquifyVerilogModules + + +class StandaloneNetlistFlow(Flowgraph): + def __init__(self, name: str = None): + if name is None: + name = "standalone-netlist-flow" + super().__init__(name) + + self.node("parse", ElaborateTask) + self.node("remove", SedRemove) + self.node("uniquify", UniquifyVerilogModules) + + self.edge("parse", "remove") + self.edge("remove", "uniquify") + + +################################################## +if __name__ == "__main__": + flow = StandaloneNetlistFlow() + flow.write_flowgraph(f"{flow.name}.png") From 53b1a801f1a2a79b9c4610b495ef8cd9e9962285 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Fri, 10 Oct 2025 17:34:46 -0400 Subject: [PATCH 06/20] WIP: standalong network netlist working and a little cleaner --- examples/network/test.py | 23 +-- examples/umiparam-network/test.py | 55 +++++- examples/umiram/test.py | 21 +-- examples/umiram/umiram.py | 2 + switchboard/network.py | 186 +++++++++++---------- switchboard/sbdut.py | 186 +++++++++++++++------ switchboard/sc/morty/morty.py | 14 -- switchboard/sc/morty/uniquify.py | 67 -------- switchboard/sc/sed/remove.py | 52 ------ switchboard/sc/sed/sed_remove.py | 25 ++- switchboard/verilator/verilator.py | 2 +- switchboard/verilog/sim/switchboard_sim.py | 1 + 12 files changed, 319 insertions(+), 315 deletions(-) delete mode 100644 switchboard/sc/morty/uniquify.py delete mode 100644 switchboard/sc/sed/remove.py diff --git a/examples/network/test.py b/examples/network/test.py index 394258b9..67ff53da 100755 --- a/examples/network/test.py +++ b/examples/network/test.py @@ -7,7 +7,6 @@ import numpy as np -from umi import sumi from switchboard import SbNetwork from pathlib import Path @@ -22,13 +21,10 @@ def main(): # create the building blocks umi_fifo = make_umi_fifo(net) - umi_fifo.option.set_nodashboard(True) umi2axil = make_umi2axil(net) - umi2axil.option.set_nodashboard(True) axil_ram = make_axil_ram(net) - axil_ram.option.set_nodashboard(True) # connect them together @@ -51,7 +47,7 @@ def main(): # launch the simulation - net.simulate() + net.simulate(run=True) # interact with the simulation @@ -88,6 +84,7 @@ def make_umi_fifo(net): bypass="1'b0", chaosmode="1'b0", fifo_full=None, + fifo_almost_full=None, fifo_empty=None, vdd="1'b1", vss="1'b0" @@ -139,10 +136,6 @@ def __init__(self): tieoffs=tieoffs ) - #dut.use(sumi) - - #dut.input('sumi/rtl/umi_fifo.v', package='umi') - return dut @@ -194,14 +187,6 @@ def __init__(self): resets=resets ) - #dut.register_source( - # 'verilog-axi', - # 'git+https://github.com/alexforencich/verilog-axi.git', - # '38915fb' - #) - - #dut.input('rtl/axil_ram.v', package='verilog-axi') - #dut.add('tool', 'verilator', 'task', 'compile', 'warningoff', # ['WIDTHTRUNC', 'TIMESCALEMOD']) @@ -256,10 +241,6 @@ def __init__(self): resets=resets ) - #dut.use(sumi) - - #dut.input('utils/rtl/umi2axilite.v', package='umi') - return dut diff --git a/examples/umiparam-network/test.py b/examples/umiparam-network/test.py index 67710750..71253753 100755 --- a/examples/umiparam-network/test.py +++ b/examples/umiparam-network/test.py @@ -16,6 +16,14 @@ from pathlib import Path THIS_DIR = Path(__file__).resolve().parent +from siliconcompiler import Design + +from umi.sumi import Endpoint + +from switchboard.verilog.sim.switchboard_sim import SwitchboardSim +from switchboard import sb_path + + def main(): # create network @@ -110,15 +118,52 @@ def make_umiparam(net): resets = ['nreset'] - dut = net.make_dut('umiparam', parameters=parameters, interfaces=interfaces, resets=resets) + dut = net.make_dut( + design=UmiParam(), + parameters=parameters, + interfaces=interfaces, + resets=resets + ) + + return dut - dut.use(sumi) - dut.set('option', 'idir', sb_path() / 'verilog' / 'common') +class UmiParam(Design): - dut.input('../common/verilog/umiparam.sv') + def __init__(self): + super().__init__("umiparam") - return dut + top_module = "umiparam" + + dr_path = sb_path() / ".." / "examples" / "common" + + self.set_dataroot('sb_ex_common', dr_path) + + files = [ + "verilog/umiparam.sv" + ] + + deps = [ + Endpoint() + ] + + with self.active_fileset('rtl'): + self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) + self.add_depfileset(self, "rtl") if __name__ == '__main__': diff --git a/examples/umiram/test.py b/examples/umiram/test.py index 47976762..6beba90d 100755 --- a/examples/umiram/test.py +++ b/examples/umiram/test.py @@ -87,31 +87,32 @@ def python_intf(umi): assert val2 == 0xCD -def main(): - +def build_testbench(): extra_args = { '--mode': dict(default='python', choices=['python', 'cpp'], help='Programming language used for the test stimulus.') } - args = get_cmdline_args(extra_args=extra_args) - - dut = SbDut( - UmiRam(), - fileset=args.tool - ) + dut = SbDut(UmiRam(), cmdline=True, trace_type='fst', extra_args=extra_args) dut.build() + return dut + + +def main(): + # build the simulator + dut = build_testbench() + # create queues umi = UmiTxRx('to_rtl.q', 'from_rtl.q', fresh=True) # launch the simulation dut.simulate() - if args.mode == 'python': + if dut.args.mode == 'python': python_intf(umi) - elif args.mode == 'cpp': + elif dut.args.mode == 'cpp': binary_run(THIS_DIR / 'client').wait() else: raise ValueError(f'Invalid mode: {dut.args.mode}') diff --git a/examples/umiram/umiram.py b/examples/umiram/umiram.py index 7042c71f..8bb0b3e7 100644 --- a/examples/umiram/umiram.py +++ b/examples/umiram/umiram.py @@ -37,8 +37,10 @@ def __init__(self): with self.active_fileset('verilator'): self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) self.add_depfileset(self, "rtl") with self.active_fileset('icarus'): self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) self.add_depfileset(self, "rtl") diff --git a/switchboard/network.py b/switchboard/network.py index 1444d4fc..ac8755b7 100644 --- a/switchboard/network.py +++ b/switchboard/network.py @@ -1,6 +1,8 @@ # Copyright (c) 2024 Zero ASIC Corporation # This code is licensed under Apache License 2.0 (see LICENSE for details) +from typing import Set + from pathlib import Path from copy import deepcopy from itertools import count @@ -17,6 +19,7 @@ from _switchboard import delete_queues +from siliconcompiler import Design class SbIntf: def __init__(self, inst, name, width=None, indices=None): @@ -119,12 +122,82 @@ def __init__(self, name, block): self.__setattr__(name, SbIntf(inst=self, name=name, width=width)) +class SingleNetlistNetwork(Design): + def __init__( + self, + unique_blocks: Set[SbInst], + wrapper_filename: str, + fileset: str, + insts + ): + super().__init__("SNDesign") + unique_blocks = unique_blocks + + # Pickle each block in the network + pickled_sources = [block.package() for block in unique_blocks] + + # populate the interfaces dictionary + interfaces = {} + + for inst_name, inst in insts.items(): + # make a copy of the interface definitions for this block + intf_defs = deepcopy(inst.block.intf_defs) + + # wiring + for intf_name, props in inst.mapping.items(): + intf_defs[intf_name]['wire'] = props['wire'] + intf_defs[intf_name]['external'] = intf_name in inst.external + + # prepend instance name to init interfaces + for value in intf_defs.values(): + if value['type'] == 'plusarg': + value['wire'] = f"{inst_name}_{value['wire']}" + value['plusarg'] = f"{inst_name}_{value['plusarg']}" + + interfaces[inst_name] = intf_defs + + # generate netlist that connects everything together, and input() it + top_lvl = autowrap( + instances={inst.name: inst.block.get_topmodule_name() for inst in insts.values()}, + toplevel='testbench', + parameters={inst.name: inst.block.parameters for inst in insts.values()}, + interfaces=interfaces, + clocks={inst.name: inst.block.clocks for inst in insts.values()}, + resets={inst.name: inst.block.resets for inst in insts.values()}, + tieoffs={inst.name: inst.block.tieoffs for inst in insts.values()}, + filename=wrapper_filename + ) + + sources = pickled_sources + [str(top_lvl)] + + from switchboard.verilog.sim.switchboard_sim import SwitchboardSim + + with self.active_fileset(fileset): + self.set_topmodule("testbench") + self.add_depfileset(SwitchboardSim()) + for file in sources: + self.add_file(file) + + class SbNetwork: - def __init__(self, cmdline=False, tool: str = 'verilator', trace: bool = False, - trace_type: str = 'vcd', frequency: float = 100e6, period: float = None, - max_rate: float = -1, start_delay: float = None, fast: bool = False, - extra_args: dict = None, cleanup: bool = True, args=None, - single_netlist: bool = False, threads: int = None, name: str = None): + def __init__( + self, + cmdline=False, + tool: str = 'verilator', + trace: bool = False, + trace_type: str = 'vcd', + frequency: float = 100e6, + period: float = None, + max_rate: float = -1, + start_delay: float = None, + fast: bool = False, + extra_args: dict = None, + cleanup: bool = True, + args=None, + single_netlist: bool = False, + threads: int = None, + name: str = None + ): self.insts = {} @@ -185,14 +258,7 @@ def __init__(self, cmdline=False, tool: str = 'verilator', trace: bool = False, self.single_netlist = single_netlist if single_netlist: - from siliconcompiler import Design - class SNDesign(Design): - def __init__(self): - super().__init__("SNDesign") - - - - self.dut = SbDut(design=SNDesign(), args=self.args) + self.single_netlist_dut = SbDut(design="single_netlist_network", args=self.args) else: self._intf_defs = {} @@ -213,7 +279,7 @@ def cleanup_func(uri_set=self.uri_set): @property def intf_defs(self): if self.single_netlist: - return self.dut.intf_defs + return self.single_netlist_dut.intf_defs else: return self._intf_defs @@ -221,7 +287,7 @@ def instantiate(self, block, name: str = None): # generate a name if needed if name is None: if isinstance(block, SbDut): - prefix = block.top_lvl_module_name + prefix = block.design.name else: prefix = block.name @@ -404,79 +470,25 @@ def add_tcp_intf(self, intf, intf_def, uri): raise Exception(f'Unsupported direction: {tcp_direction}') def build(self): - unique_blocks = set(inst.block for inst in self.insts.values()) + unique_blocks: Set[SbInst] = set(inst.block for inst in self.insts.values()) if self.single_netlist: - passthroughs = [ - ('tool', 'verilator', 'task', 'compile', 'warningoff') - ] - - self.dut.fileset = "verilator" - for block in unique_blocks: - with self.dut.design.active_fileset(self.dut.fileset): - #self.dut.design.set_topmodule("testbench") - #self.dut.design.add_depfileset(SwitchboardSim()) - print(f"running block {block.design}") - block_pkg = block.package() - print(f"block_pkg = {block_pkg}") - self.dut.design.add_file(block.package()) - #self.dut.input(block.package()) - - #for passthrough in passthroughs: - #self.dut.add(*passthrough, block.get(*passthrough)) - - filename = Path(self.dut.get('option', 'builddir')).resolve() / 'testbench.sv' + filename = Path(self.single_netlist_dut.option.get_builddir()).resolve() / 'testbench.sv' filename.parent.mkdir(exist_ok=True, parents=True) - # populate the interfaces dictionary - interfaces = {} - - for inst_name, inst in self.insts.items(): - # make a copy of the interface definitions for this block - intf_defs = deepcopy(inst.block.intf_defs) - - # wiring - for intf_name, props in inst.mapping.items(): - intf_defs[intf_name]['wire'] = props['wire'] - intf_defs[intf_name]['external'] = intf_name in inst.external - - # prepend instance name to init interfaces - for value in intf_defs.values(): - if value['type'] == 'plusarg': - value['wire'] = f"{inst_name}_{value['wire']}" - value['plusarg'] = f"{inst_name}_{value['plusarg']}" - - interfaces[inst_name] = intf_defs - - # generate netlist that connects everything together, and input() it - top_lvl = autowrap( - instances={inst.name: inst.block.dut for inst in self.insts.values()}, - toplevel='testbench', - parameters={inst.name: inst.block.parameters for inst in self.insts.values()}, - interfaces=interfaces, - clocks={inst.name: inst.block.clocks for inst in self.insts.values()}, - resets={inst.name: inst.block.resets for inst in self.insts.values()}, - tieoffs={inst.name: inst.block.tieoffs for inst in self.insts.values()}, - filename=filename + self.single_netlist_dut.set_design( + SingleNetlistNetwork( + unique_blocks=unique_blocks, + fileset=self.tool, + wrapper_filename=filename, + insts=self.insts + ) ) - - print(f"adding top lvl = {top_lvl}") - from switchboard.verilog.sim.switchboard_sim import SwitchboardSim - with self.dut.design.active_fileset(self.dut.fileset): - self.dut.design.set_topmodule("testbench") - self.dut.design.add_depfileset(SwitchboardSim()) - self.dut.design.add_file(str(top_lvl)) - - self.dut.set_design(self.dut.design) - self.dut.add_fileset(self.dut.fileset) - - - - + self.single_netlist_dut.add_fileset(self.tool) # build the single-netlist simulation - self.dut.build() + self.single_netlist_dut.build() else: for block in unique_blocks: block.build() @@ -562,12 +574,18 @@ def simulate(self, start_delay=None, run=None, intf_objs=True, plusargs=None): for inst_plusarg, value in inst_plusargs: plusargs_processed.append((f"{inst_name}_{inst_plusarg}", value)) plusargs = plusargs_processed - process = self.dut.simulate(start_delay=start_delay, run=run, - intf_objs=intf_objs, plusargs=plusargs) + + process = self.single_netlist_dut.simulate( + start_delay=start_delay, + run=run, + intf_objs=intf_objs, + plusargs=plusargs + ) + self.process_collection.add(process) if intf_objs: - self.intfs = self.dut.intfs + self.intfs = self.single_netlist_dut.intfs else: if intf_objs: self.intfs = create_intf_objs(self.intf_defs) @@ -686,5 +704,5 @@ def make_dut(self, *args, **kwargs): for k, v in cfg.items(): if k not in kwargs: kwargs[k] = v - print(f"design args = {args}, kwargs = {kwargs}") + return SbDut(*args, **kwargs) diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 7f0e4fc6..8c59c56c 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -26,19 +26,60 @@ from .ams import make_ams_spice_wrapper, make_ams_verilog_wrapper, parse_spice_subckts from .autowrap import (normalize_clocks, normalize_interfaces, normalize_resets, normalize_tieoffs, normalize_parameters, create_intf_objs) +from .cmdline import get_cmdline_args from siliconcompiler import Design, Sim +from siliconcompiler.tools import get_task SB_DIR = sb_path() +class AutowrapDesign(Design): + def __init__( + self, + design: Design, + fileset: str, + parameters=None, + intf_defs=None, + clocks=None, + resets=None, + tieoffs=None, + filename=None + ): + + super().__init__("AutowrapDesign") + + from switchboard.autowrap import autowrap + + instance = f'{design.name}_i' + + autowrap( + toplevel="testbench", + instances={instance: design.get_topmodule(fileset=fileset)}, + parameters={instance: parameters}, + interfaces={instance: intf_defs}, + clocks={instance: clocks}, + resets={instance: resets}, + tieoffs={instance: tieoffs}, + filename=filename + ) + + from switchboard.verilog.sim.switchboard_sim import SwitchboardSim + + with self.active_fileset(fileset): + self.set_topmodule("testbench") + self.add_depfileset(design) + self.add_depfileset(SwitchboardSim()) + self.add_file(str(filename)) + + class SbDut(Sim): def __init__( self, design: Union[Design, str] = None, + tool: str = 'verilator', fileset: str = None, - tool: str = 'icarus', default_main: bool = True, trace: bool = True, trace_type: str = 'vcd', @@ -52,7 +93,9 @@ def __init__( timeunit: str = None, timeprecision: str = None, warnings: List[str] = None, + cmdline: bool = False, fast: bool = False, + extra_args: dict = None, autowrap: bool = False, parameters=None, interfaces=None, @@ -68,18 +111,40 @@ def __init__( ): super().__init__(design) - if fileset: - self.add_fileset(fileset) - - self.fileset = fileset - self.top_lvl_module_name = None - main_filesets = self.option.get_fileset() - if main_filesets and len(main_filesets) != 0: - main_fileset = main_filesets[0] - self.top_lvl_module_name = design.get_topmodule( - fileset=main_fileset + self.option.set_nodashboard(True) + + ########################################## + # parse command-line options if desired + ########################################## + if cmdline: + self.args = get_cmdline_args( + tool=tool, + trace=trace, + trace_type=trace_type, + frequency=frequency, + period=period, + fast=fast, + max_rate=max_rate, + start_delay=start_delay, + threads=threads, + extra_args=extra_args ) + elif args is not None: + self.args = args + else: + self.args = None + + if self.args is not None: + trace = self.args.trace + trace_type = self.args.trace_type + fast = self.args.fast + tool = self.args.tool + frequency = self.args.frequency + period = self.args.period + max_rate = self.args.max_rate + start_delay = self.args.start_delay + threads = self.args.threads # input validation @@ -109,22 +174,36 @@ def __init__( self.autowrap = autowrap - if (suffix is None) and subcomponent: - suffix = f'_unq_{self.top_lvl_module_name}' - - self.suffix = suffix - - if suffix is not None: - self.dut = f'{self.top_lvl_module_name}{suffix}' - else: - self.dut = self.top_lvl_module_name - self.parameters = normalize_parameters(parameters) self.intf_defs = normalize_interfaces(interfaces) self.clocks = normalize_clocks(clocks) self.resets = normalize_resets(resets) self.tieoffs = normalize_tieoffs(tieoffs) + if not fileset: + fileset = self.tool + + self.fileset = fileset + + self.design_name = None + if isinstance(design, Design): + self.design_name = design.name + else: + self.design_name = design + + #self.top_lvl_module_name = None + #main_filesets = self.option.get_fileset() + #if main_filesets and len(main_filesets) != 0: + # main_fileset = main_filesets[0] + # self.top_lvl_module_name = design.get_topmodule( + # fileset=main_fileset + # ) + + if (suffix is None) and subcomponent: + suffix = f'_unq_{self.design_name}' + + self.suffix = suffix + # initialization self.intfs = {} @@ -144,12 +223,12 @@ def __init__( # the subcomponent build flow is tool-agnostic, producing a single Verilog # file as output, as opposed to a simulator binary builddir = buildroot / metadata_str( - design=self.top_lvl_module_name, + design=self.design_name, parameters=parameters ) else: builddir = buildroot / metadata_str( - design=self.top_lvl_module_name, + design=self.design_name, parameters=parameters, tool=tool, trace=trace, @@ -170,16 +249,29 @@ def __init__( from switchboard.sc.standalone_netlist_flow import StandaloneNetlistFlow self.set_flow(StandaloneNetlistFlow()) + def get_topmodule_name(self): + top_lvl_module_name = None + main_filesets = self.option.get_fileset() + if main_filesets and len(main_filesets) != 0: + main_fileset = main_filesets[0] + top_lvl_module_name = self.design.get_topmodule( + fileset=main_fileset + ) + + if self.suffix is not None: + return f'{top_lvl_module_name}{self.suffix}' + return top_lvl_module_name + def _configure_verilator(self): from siliconcompiler.flows.dvflow import DVFlow self.set_flow(DVFlow(tool="verilator")) - from siliconcompiler.tools import get_task from siliconcompiler.tools.verilator.compile import CompileTask from siliconcompiler.tools.verilator import VerilatorTask get_task(self, filter=VerilatorTask).add_warningoff("TIMESCALEMOD") + get_task(self, filter=VerilatorTask).add_warningoff("WIDTHTRUNC") get_task(self, filter=CompileTask).set("var", "cincludes", [SB_DIR / 'cpp']) #self.set('tool', self.tool, 'task', 'compile', 'var', 'ldflags', ['-pthread']) @@ -214,7 +306,6 @@ def _configure_icarus(self): from siliconcompiler.flows.dvflow import DVFlow self.set_flow(DVFlow(tool="icarus")) - from siliconcompiler.tools import get_task from siliconcompiler.tools.icarus.compile import CompileTask get_task(self, filter=CompileTask).set("var", "verilog_generation", "2012") @@ -225,7 +316,6 @@ def find_sim(self): result_kind = 'vvp' else: result_kind = 'vexe' - print(f"results found = {self.find_result(result_kind, step='compile')}") return self.find_result(result_kind, step='compile') def build(self, cwd: str = None, fast: bool = None): @@ -255,37 +345,31 @@ def build(self, cwd: str = None, fast: bool = None): # if "fast" is set, then we can return early if the # simulation binary already exists if fast: + self.add_fileset(self.fileset) sim = self.find_sim() if sim is not None: return sim # build the wrapper if needed if self.autowrap: - from .autowrap import autowrap - filename = Path(self.option.get_builddir()).resolve() / 'testbench.sv' filename.parent.mkdir(exist_ok=True, parents=True) - instance = f'{self.top_lvl_module_name}_i' - - autowrap( - instances={instance: self.top_lvl_module_name}, - parameters={instance: self.parameters}, - interfaces={instance: self.intf_defs}, - clocks={instance: self.clocks}, - resets={instance: self.resets}, - tieoffs={instance: self.tieoffs}, + wrapped_design = AutowrapDesign( + design=self.design, + fileset=self.fileset, + parameters=self.parameters, + intf_defs=self.intf_defs, + clocks=self.clocks, + resets=self.resets, + tieoffs=self.tieoffs, filename=filename ) - from switchboard.verilog.sim.switchboard_sim import SwitchboardSim - with self.design.active_fileset(self.fileset): - self.design.set_topmodule("testbench") - self.design.add_depfileset(SwitchboardSim()) - self.design.add_file(str(filename)) - - self.set_design(self.design) + self.set_design(wrapped_design) + self.add_fileset(self.fileset) + else: self.add_fileset(self.fileset) assert self.run() @@ -578,7 +662,7 @@ def input_analog( self.input(verilog_wrapper) - def package(self, suffix=None, fast=None): + def package(self, suffix: str = None, fast: bool = None) -> str: # set defaults if suffix is None: @@ -590,24 +674,30 @@ def package(self, suffix=None, fast=None): # see if we can exit early if fast: + self.add_fileset(self.fileset) package = self.find_package(suffix=suffix) if package is not None: return package + from switchboard.sc.morty.morty import UniquifyVerilogModules + from switchboard.sc.sed.sed_remove import SedRemove + # if not, parse with surelog and postprocess with morty if suffix: - self.set('tool', 'morty', 'task', 'uniquify_verilog_modules', 'var', 'suffix', suffix) + get_task(self, filter=UniquifyVerilogModules).set("var", "suffix", suffix) + + get_task(self, filter=SedRemove).set("var", "to_remove", "`resetall") - self.set('tool', 'sed', 'task', 'remove', 'var', 'to_remove', '`resetall') + self.add_fileset(self.fileset) self.run() # return the path to the output return self.find_package(suffix=suffix) - def find_package(self, suffix=None): + def find_package(self, suffix=None) -> str: if suffix is None: return self.find_result('sv', step='parse') else: diff --git a/switchboard/sc/morty/morty.py b/switchboard/sc/morty/morty.py index 33dbbc85..16e357f5 100644 --- a/switchboard/sc/morty/morty.py +++ b/switchboard/sc/morty/morty.py @@ -4,19 +4,6 @@ from siliconcompiler import Task -def setup(chip): - tool = 'morty' - - chip.set('tool', tool, 'exe', 'morty') - chip.set('tool', tool, 'vendor', tool) - - chip.set('tool', tool, 'vswitch', '--version') - - -def parse_version(stdout): - return stdout.split()[-1] - - class UniquifyVerilogModules(Task): def __init__(self): @@ -63,7 +50,6 @@ def runtime_options(self): suffix = self.get("var", "suffix") if suffix: - print(f"got suffix = {suffix}") cmdlist.extend(['--suffix', suffix]) out_file = f"outputs/{self.design_topmodule}.sv" diff --git a/switchboard/sc/morty/uniquify.py b/switchboard/sc/morty/uniquify.py deleted file mode 100644 index a752fd62..00000000 --- a/switchboard/sc/morty/uniquify.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2024 Zero ASIC Corporation -# This code is licensed under Apache License 2.0 (see LICENSE for details) - -from .morty import setup as setup_tool -from siliconcompiler.tools._common import get_tool_task, input_provides - - -def setup(chip): - '''Task that uses morty to rewrite a Verilog file with a unique prefix and/or - suffix for all module definitions.''' - - setup_tool(chip) - - tool = 'morty' - step = chip.get('arg', 'step') - index = chip.get('arg', 'index') - _, task = get_tool_task(chip, step, index) - - chip.set('tool', tool, 'task', task, 'var', 'suffix', - 'suffix to be added to the end of module names', - field='help') - - chip.set('tool', tool, 'task', task, 'var', 'prefix', - 'prefix to be added to the beginning of module names', - field='help') - - design = chip.top() - if f'{design}.v' in input_provides(chip, step, index): - chip.set('tool', tool, 'task', task, 'input', f'{design}.v', step=step, index=index) - else: - chip.set('tool', tool, 'task', task, 'input', f'{design}.sv', step=step, index=index) - chip.set('tool', tool, 'task', task, 'output', f'{design}.v', step=step, index=index) - - -def runtime_options(chip): - tool = 'morty' - step = chip.get('arg', 'step') - index = chip.get('arg', 'index') - _, task = get_tool_task(chip, step, index) - design = chip.top() - - cmdlist = [] - - prefix = chip.get('tool', tool, 'task', task, 'var', 'prefix', step=step, index=index) - if prefix: - if isinstance(prefix, list) and (len(prefix) == 1) and isinstance(prefix[0], str): - cmdlist = ['--prefix', prefix[0]] + cmdlist - else: - raise ValueError('"prefix" does not have the expected format') - - suffix = chip.get('tool', tool, 'task', task, 'var', 'suffix', step=step, index=index) - if suffix: - if isinstance(suffix, list) and (len(suffix) == 1) and isinstance(suffix[0], str): - cmdlist = ['--suffix', suffix[0]] + cmdlist - else: - raise ValueError('"suffix" does not have the expected format') - - if f'{design}.v' in input_provides(chip, step, index): - infile = f'inputs/{design}.v' - else: - infile = f'inputs/{design}.sv' - outfile = f'outputs/{design}.v' - cmdlist += ['-o', outfile] - - cmdlist += [infile] - - return cmdlist diff --git a/switchboard/sc/sed/remove.py b/switchboard/sc/sed/remove.py deleted file mode 100644 index dfafa320..00000000 --- a/switchboard/sc/sed/remove.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2024 Zero ASIC Corporation -# This code is licensed under Apache License 2.0 (see LICENSE for details) - -from .sed import setup as setup_tool -from siliconcompiler.tools._common import get_tool_task, input_provides - - -def setup(chip): - '''Task that removes specific strings from a Verilog source file.''' - - setup_tool(chip) - - tool = 'sed' - step = chip.get('arg', 'step') - index = chip.get('arg', 'index') - _, task = get_tool_task(chip, step, index) - - chip.set('tool', tool, 'task', task, 'var', 'to_remove', - 'strings to remove from the Verilog source file', - field='help') - - design = chip.top() - if f'{design}.v' in input_provides(chip, step, index): - chip.set('tool', tool, 'task', task, 'input', f'{design}.v', step=step, index=index) - else: - chip.set('tool', tool, 'task', task, 'input', f'{design}.sv', step=step, index=index) - chip.set('tool', tool, 'task', task, 'output', f'{design}.v', step=step, index=index) - - -def runtime_options(chip): - tool = 'sed' - step = chip.get('arg', 'step') - index = chip.get('arg', 'index') - _, task = get_tool_task(chip, step, index) - design = chip.top() - - if f'{design}.v' in input_provides(chip, step, index): - infile = f'inputs/{design}.v' - else: - infile = f'inputs/{design}.sv' - outfile = f'outputs/{design}.v' - - to_remove = chip.get('tool', tool, 'task', task, 'var', 'to_remove', step=step, index=index) - - script = [f's/{elem}//g' for elem in to_remove] - script += [f'w {outfile}'] - - script = '; '.join(script) - - cmdlist = ['-n', f'"{script}"', infile] - - return cmdlist diff --git a/switchboard/sc/sed/sed_remove.py b/switchboard/sc/sed/sed_remove.py index 2fe02a95..b6c00314 100644 --- a/switchboard/sc/sed/sed_remove.py +++ b/switchboard/sc/sed/sed_remove.py @@ -26,6 +26,7 @@ def setup(self): self.set_exe("sed") + self.add_input_file(ext="sv") self.add_output_file(ext="sv") def runtime_options(self): @@ -33,24 +34,22 @@ def runtime_options(self): to_remove = self.get("var", "to_remove") - print(f"to remove = {to_remove}") - script = [f's/{elem}//g' for elem in to_remove] script += [f'w outputs/{self.design_topmodule}.sv'] - print(f"script = {script}") script = '; '.join(script) options.extend(["-n", f'{script}', ]) - ####################### - # Sources - ####################### - filesets = self.project.get_filesets() - for lib, fileset in filesets: - for value in lib.get_file(fileset=fileset, filetype="systemverilog"): - options.append(value) - for lib, fileset in filesets: - for value in lib.get_file(fileset=fileset, filetype="verilog"): - options.append(value) + input_file = f"inputs/{self.design_topmodule}.sv" + + #filesets = self.project.get_filesets() + #for lib, fileset in filesets: + # for value in lib.get_file(fileset=fileset, filetype="systemverilog"): + # options.append(value) + #for lib, fileset in filesets: + # for value in lib.get_file(fileset=fileset, filetype="verilog"): + # options.append(value) + + options.append(input_file) return options diff --git a/switchboard/verilator/verilator.py b/switchboard/verilator/verilator.py index 5061b1c3..06a293b6 100644 --- a/switchboard/verilator/verilator.py +++ b/switchboard/verilator/verilator.py @@ -9,5 +9,5 @@ def __init__(self): self.set_dataroot('localroot', sb_path() / "verilator") - with self.active_fileset('sim'): + with self.active_fileset('verilator'): self.add_file("testbench.cc") diff --git a/switchboard/verilog/sim/switchboard_sim.py b/switchboard/verilog/sim/switchboard_sim.py index 1aa5dc06..a924be4a 100644 --- a/switchboard/verilog/sim/switchboard_sim.py +++ b/switchboard/verilog/sim/switchboard_sim.py @@ -45,3 +45,4 @@ def __init__(self): with self.active_fileset("icarus"): self.add_depfileset(self, "rtl") + self.add_define("__ICARUS__") From d99c9c0489b55a545b87292018df35d83718714e Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 13 Oct 2025 10:00:49 -0400 Subject: [PATCH 07/20] WIP update in progress --- examples/umiparam-network/test.py | 18 ++++------ examples/umiparam/test.py | 59 +++++++++++++++++++++++++++---- examples/umiram/test.py | 2 -- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/examples/umiparam-network/test.py b/examples/umiparam-network/test.py index 71253753..a2757225 100755 --- a/examples/umiparam-network/test.py +++ b/examples/umiparam-network/test.py @@ -5,25 +5,21 @@ # Copyright (c) 2024 Zero ASIC Corporation # This code is licensed under Apache License 2.0 (see LICENSE for details) -from umi import sumi +from pathlib import Path import numpy as np - from copy import deepcopy -from switchboard import SbNetwork, sb_path -from switchboard.cmdline import get_cmdline_args - -from pathlib import Path -THIS_DIR = Path(__file__).resolve().parent +from umi.sumi import Endpoint from siliconcompiler import Design -from umi.sumi import Endpoint - +from switchboard import SbNetwork, sb_path +from switchboard.cmdline import get_cmdline_args from switchboard.verilog.sim.switchboard_sim import SwitchboardSim -from switchboard import sb_path +THIS_DIR = Path(__file__).resolve().parent + def main(): # create network @@ -157,12 +153,10 @@ def __init__(self): with self.active_fileset('verilator'): self.set_topmodule(top_module) - self.add_depfileset(SwitchboardSim()) self.add_depfileset(self, "rtl") with self.active_fileset('icarus'): self.set_topmodule(top_module) - self.add_depfileset(SwitchboardSim()) self.add_depfileset(self, "rtl") diff --git a/examples/umiparam/test.py b/examples/umiparam/test.py index 22f04f86..cc9ac955 100755 --- a/examples/umiparam/test.py +++ b/examples/umiparam/test.py @@ -5,12 +5,19 @@ # Copyright (c) 2024 Zero ASIC Corporation # This code is licensed under Apache License 2.0 (see LICENSE for details) -from umi import sumi import numpy as np +from umi.sumi import Endpoint + +from siliconcompiler import Design + from switchboard import SbDut +from switchboard import sb_path +from switchboard.verilog.sim.switchboard_sim import SwitchboardSim from pathlib import Path + + THIS_DIR = Path(__file__).resolve().parent @@ -25,6 +32,42 @@ def main(): assert value == 42 +class UmiParam(Design): + + def __init__(self): + super().__init__("umiparam") + + top_module = "umiparam" + + dr_path = sb_path() / ".." / "examples" / "common" + + self.set_dataroot('sb_ex_common', dr_path) + + files = [ + "verilog/umiparam.sv" + ] + + deps = [ + Endpoint() + ] + + with self.active_fileset('rtl'): + self.set_topmodule(top_module) + self.add_depfileset(SwitchboardSim()) + for item in files: + self.add_file(item) + for item in deps: + self.add_depfileset(item) + + with self.active_fileset('verilator'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + with self.active_fileset('icarus'): + self.set_topmodule(top_module) + self.add_depfileset(self, "rtl") + + def build_testbench(): dw = 32 aw = 64 @@ -44,12 +87,14 @@ def build_testbench(): resets = ['nreset'] - dut = SbDut('umiparam', cmdline=True, autowrap=True, parameters=parameters, - interfaces=interfaces, resets=resets) - - dut.use(sumi) - - dut.input('../common/verilog/umiparam.sv') + dut = SbDut( + UmiParam(), + cmdline=True, + autowrap=True, + parameters=parameters, + interfaces=interfaces, + resets=resets + ) dut.build() diff --git a/examples/umiram/test.py b/examples/umiram/test.py index 6beba90d..bbb9490b 100755 --- a/examples/umiram/test.py +++ b/examples/umiram/test.py @@ -11,8 +11,6 @@ from pathlib import Path from switchboard import SbDut, UmiTxRx, binary_run -from switchboard.cmdline import get_cmdline_args - from umiram import UmiRam From edfa95ebe11368196525c25c7e0c67755ec6deef Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 13 Oct 2025 12:08:41 -0400 Subject: [PATCH 08/20] WIP: trace fixes --- switchboard/sbdut.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 8c59c56c..76e6d4d9 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -245,6 +245,13 @@ def __init__( self._configure_icarus() elif self.tool == 'verilator': self._configure_verilator() + + if trace: + self.design.add_define("SB_TRACE") + + if self.trace_type == 'fst': + self.design.add_define("SB_TRACE_FST") + else: from switchboard.sc.standalone_netlist_flow import StandaloneNetlistFlow self.set_flow(StandaloneNetlistFlow()) @@ -276,7 +283,8 @@ def _configure_verilator(self): get_task(self, filter=CompileTask).set("var", "cincludes", [SB_DIR / 'cpp']) #self.set('tool', self.tool, 'task', 'compile', 'var', 'ldflags', ['-pthread']) - if self.trace and (self.tool == 'verilator'): + if self.trace: + get_task(self, filter=CompileTask).set("var", "trace", True) get_task(self, filter=CompileTask).set("var", "trace_type", self.trace_type) #if self.tool == 'verilator': From 807fef8ee2ca742b6e63503dc10d2da55f0ff277 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Wed, 15 Oct 2025 14:58:48 -0400 Subject: [PATCH 09/20] TEMP: PyUmi Read Rework --- python/switchboard_pybind.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/switchboard_pybind.cc b/python/switchboard_pybind.cc index 3bd6e68b..b9c5fcab 100644 --- a/python/switchboard_pybind.cc +++ b/python/switchboard_pybind.cc @@ -650,13 +650,13 @@ class PyUmi { // make sure that max_bytes is set appropriately - if (max_bytes > UMI_PACKET_DATA_BYTES) { - printf("WARNING: max_bytes is greater than the data payload" - " of a single UMI packet (%d vs. %d bytes). Change max_bytes" - " to %d or smaller to clear this warning.\n", - max_bytes, UMI_PACKET_DATA_BYTES, UMI_PACKET_DATA_BYTES); - max_bytes = UMI_PACKET_DATA_BYTES; - } + //if (max_bytes > UMI_PACKET_DATA_BYTES) { + // printf("WARNING: max_bytes is greater than the data payload" + // " of a single UMI packet (%d vs. %d bytes). Change max_bytes" + // " to %d or smaller to clear this warning.\n", + // max_bytes, UMI_PACKET_DATA_BYTES, UMI_PACKET_DATA_BYTES); + // max_bytes = UMI_PACKET_DATA_BYTES; + //} if (max_bytes < bytes_per_elem) { throw std::runtime_error("max_bytes must be greater than or equal to bytes_per_elem."); @@ -682,7 +682,7 @@ class PyUmi { uint32_t size = highest_bit(bytes_per_elem); // determine the maximum length of an individual packet - uint32_t max_len = max_bytes / bytes_per_elem; + //uint32_t max_len = max_bytes / bytes_per_elem; // used to keep track of responses uint32_t to_recv = num; @@ -691,8 +691,8 @@ class PyUmi { while ((num > 0) || (to_recv > 0)) { if (num > 0) { // send read request - uint32_t len = std::min(num, max_len); - uint32_t eom = (len == num) ? 1 : 0; + uint32_t len = num; + uint32_t eom = 1; uint32_t cmd = umi_pack(UMI_REQ_READ, 0, size, len - 1, eom, 1, qos, prot); UmiTransaction request(cmd, addr, srcaddr); if (umisb_send(request, m_tx, false)) { From f78ec8a408630e4f4e5dcfe16c5b1c07ae785d19 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Thu, 16 Oct 2025 17:24:22 -0400 Subject: [PATCH 10/20] WIP: switchboard pybind read func tweaks --- python/switchboard_pybind.cc | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/python/switchboard_pybind.cc b/python/switchboard_pybind.cc index b9c5fcab..2ea8113f 100644 --- a/python/switchboard_pybind.cc +++ b/python/switchboard_pybind.cc @@ -639,8 +639,7 @@ class PyUmi { } py::array read(uint64_t addr, uint32_t num, size_t bytes_per_elem, uint64_t srcaddr = 0, - uint32_t max_bytes = UMI_PACKET_DATA_BYTES, uint32_t qos = 0, uint32_t prot = 0, - bool error = true) { + uint32_t qos = 0, uint32_t prot = 0, bool error = true) { // read "num" bytes from the given address. "num" may be any value, // including greater than the length of a header packet, and values @@ -648,16 +647,6 @@ class PyUmi { // the source address to which responses should be sent. this // function is blocking. - // make sure that max_bytes is set appropriately - - //if (max_bytes > UMI_PACKET_DATA_BYTES) { - // printf("WARNING: max_bytes is greater than the data payload" - // " of a single UMI packet (%d vs. %d bytes). Change max_bytes" - // " to %d or smaller to clear this warning.\n", - // max_bytes, UMI_PACKET_DATA_BYTES, UMI_PACKET_DATA_BYTES); - // max_bytes = UMI_PACKET_DATA_BYTES; - //} - if (max_bytes < bytes_per_elem) { throw std::runtime_error("max_bytes must be greater than or equal to bytes_per_elem."); } @@ -682,7 +671,7 @@ class PyUmi { uint32_t size = highest_bit(bytes_per_elem); // determine the maximum length of an individual packet - //uint32_t max_len = max_bytes / bytes_per_elem; + uint32_t max_len = max_bytes / bytes_per_elem; // used to keep track of responses uint32_t to_recv = num; @@ -691,8 +680,8 @@ class PyUmi { while ((num > 0) || (to_recv > 0)) { if (num > 0) { // send read request - uint32_t len = num; - uint32_t eom = 1; + uint32_t len = std::min(num, max_len); + uint32_t eom = (len == num) ? 1 : 0; uint32_t cmd = umi_pack(UMI_REQ_READ, 0, size, len - 1, eom, 1, qos, prot); UmiTransaction request(cmd, addr, srcaddr); if (umisb_send(request, m_tx, false)) { From 3fb5f4654f2e5d1c0ef00b22aeb3c17327759f30 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Thu, 16 Oct 2025 17:25:28 -0400 Subject: [PATCH 11/20] WIP: removed modification of design object from within SbDut --- python/switchboard_pybind.cc | 3 +- switchboard/sbdesign.py | 55 ++++++++++++++++++++++++++++++++++++ switchboard/sbdut.py | 6 ---- 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 switchboard/sbdesign.py diff --git a/python/switchboard_pybind.cc b/python/switchboard_pybind.cc index 2ea8113f..8a7ac3c6 100644 --- a/python/switchboard_pybind.cc +++ b/python/switchboard_pybind.cc @@ -639,7 +639,8 @@ class PyUmi { } py::array read(uint64_t addr, uint32_t num, size_t bytes_per_elem, uint64_t srcaddr = 0, - uint32_t qos = 0, uint32_t prot = 0, bool error = true) { + uint32_t max_bytes = UMI_PACKET_DATA_BYTES, uint32_t qos = 0, uint32_t prot = 0, + bool error = true) { // read "num" bytes from the given address. "num" may be any value, // including greater than the length of a header packet, and values diff --git a/switchboard/sbdesign.py b/switchboard/sbdesign.py new file mode 100644 index 00000000..a8c944c2 --- /dev/null +++ b/switchboard/sbdesign.py @@ -0,0 +1,55 @@ +from siliconcompiler import Design +from typing import List, Tuple + + +class SbDesign(Design): + def __init__( + self, + name: str = "SbDesign", + trace: bool = False, + topmodule: str = None, + dep: List[Design] = None, + files: List[str] = None, + idir: List[str] = None, + define: List[str] = None, + undefine: List[str] = None, + param: List[Tuple] = None + ): + + super().__init__(name) + + # Taking care of Nones + if idir is None: + idir = [] + if dep is None: + dep = [] + if define is None: + define = [] + if undefine is None: + undefine = [] + if param is None: + param = [] + + # Setting RTL list, others outside + with self.active_fileset('rtl'): + if topmodule: + self.set_topmodule(topmodule) + for item in files: + self.add_file(item) + for item in idir: + self.add_idir(item) + for item in dep: + self.add_depfileset(item) + for item in define: + self.add_define(item) + for item in undefine: + self.add_undefine(item) + for item in param: + self.add_param(item[0], item[1]) + + with self.active_fileset('icarus'): + if topmodule: + self.set_topmodule(topmodule) + self.add_depfileset(self, 'rtl') + if trace: + self.add_define("SB_TRACE") diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 76e6d4d9..1966eca9 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -246,12 +246,6 @@ def __init__( elif self.tool == 'verilator': self._configure_verilator() - if trace: - self.design.add_define("SB_TRACE") - - if self.trace_type == 'fst': - self.design.add_define("SB_TRACE_FST") - else: from switchboard.sc.standalone_netlist_flow import StandaloneNetlistFlow self.set_flow(StandaloneNetlistFlow()) From a97ae82369baccbd8d2c3c6f4922b81792ddc98c Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Fri, 17 Oct 2025 14:49:03 -0400 Subject: [PATCH 12/20] bumped silicon compiler version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 87f36a9e..429584f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ numpy tqdm -siliconcompiler >= 0.27.0, < 0.35.0 +siliconcompiler >= 0.35.0, < 0.35.1 # Testing dependencies #:test From 651db9e1e192a5e4f0ca5a50699367116d1f7821 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Fri, 17 Oct 2025 16:48:50 -0400 Subject: [PATCH 13/20] Added reset awareness to UMI driver / monitor --- examples/umiparam/test.py | 4 +- switchboard/autowrap.py | 68 +++++----- switchboard/verilog/common/switchboard.vh | 6 +- switchboard/verilog/sim/queue_to_sb_sim.sv | 116 ++++++++-------- switchboard/verilog/sim/queue_to_umi_sim.sv | 2 + switchboard/verilog/sim/sb_axi_m.sv | 6 + switchboard/verilog/sim/sb_axil_m.sv | 6 + switchboard/verilog/sim/sb_axil_s.sv | 6 + switchboard/verilog/sim/sb_to_queue_sim.sv | 141 ++++++++++---------- switchboard/verilog/sim/umi_to_queue_sim.sv | 2 + 10 files changed, 201 insertions(+), 156 deletions(-) diff --git a/examples/umiparam/test.py b/examples/umiparam/test.py index cc9ac955..cc61d7a3 100755 --- a/examples/umiparam/test.py +++ b/examples/umiparam/test.py @@ -80,8 +80,8 @@ def build_testbench(): ) interfaces = { - 'udev_req': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='input', txrx='udev'), - 'udev_resp': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='output', txrx='udev'), + 'udev_req': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='input', txrx='udev', reset='nreset'), + 'udev_resp': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='output', txrx='udev', reset='nreset'), 'value': dict(type='plusarg', width=32, default=77) } diff --git a/switchboard/autowrap.py b/switchboard/autowrap.py index 337fd42d..cded675d 100644 --- a/switchboard/autowrap.py +++ b/switchboard/autowrap.py @@ -351,6 +351,34 @@ def autowrap( lines += [''] + max_rst_dly = None + + for inst_resets in resets.values(): + if len(inst_resets) > 0: + # find the max reset delay for this instance + inst_max_rst_dly = max(reset['delay'] for reset in inst_resets) + + # update the overall max reset delay + if (max_rst_dly is None) or (inst_max_rst_dly > max_rst_dly): + max_rst_dly = inst_max_rst_dly + + if max_rst_dly is not None: + lines += [ + tab + f"reg [{max_rst_dly}:0] rstvec = '1;" + '', + tab + 'always @(posedge clk) begin' + ] + + if max_rst_dly > 0: + lines += [(2 * tab) + f"rstvec <= {{rstvec[{max_rst_dly - 1}:0], 1'b0}};"] + else: + lines += [(2 * tab) + "rstvec <= 1'b0;"] + + lines += [ + tab + 'end', + '' + ] + for instance in instances: # declare wires for tieoffs @@ -411,11 +439,19 @@ def autowrap( if decl_wire and (wire is not None): lines += [tab + f'`SB_UMI_WIRES({wire}, {dw}, {cw}, {aw});'] + # Extract interface reset information + reset = normalize_reset(value['reset']) + reset_delay = reset['delay'] + reset_sig = f'rstvec[{reset_delay}]' + if external: if direction_is_input(direction): - lines += [tab + f'`QUEUE_TO_UMI_SIM({wire}, {dw}, {cw}, {aw}, "");'] + lines += [ + tab + + f'`QUEUE_TO_UMI_SIM({wire}, {dw}, {cw}, {aw}, "", 1, clk, {reset_sig});' + ] elif direction_is_output(direction): - lines += [tab + f'`UMI_TO_QUEUE_SIM({wire}, {dw}, {cw}, {aw}, "");'] + lines += [tab + f'`UMI_TO_QUEUE_SIM({wire}, {dw}, {cw}, {aw}, "", 1, clk, {reset_sig});'] else: raise Exception(f'Unsupported UMI direction: {direction}') elif type == 'axi': @@ -485,34 +521,6 @@ def autowrap( lines += [''] - max_rst_dly = None - - for inst_resets in resets.values(): - if len(inst_resets) > 0: - # find the max reset delay for this instance - inst_max_rst_dly = max(reset['delay'] for reset in inst_resets) - - # update the overall max reset delay - if (max_rst_dly is None) or (inst_max_rst_dly > max_rst_dly): - max_rst_dly = inst_max_rst_dly - - if max_rst_dly is not None: - lines += [ - tab + f"reg [{max_rst_dly}:0] rstvec = '1;" - '', - tab + 'always @(posedge clk) begin' - ] - - if max_rst_dly > 0: - lines += [(2 * tab) + f"rstvec <= {{rstvec[{max_rst_dly - 1}:0], 1'b0}};"] - else: - lines += [(2 * tab) + "rstvec <= 1'b0;"] - - lines += [ - tab + 'end', - '' - ] - for instance, module in instances.items(): # start of the instantiation diff --git a/switchboard/verilog/common/switchboard.vh b/switchboard/verilog/common/switchboard.vh index 6d93f9b0..400414d5 100644 --- a/switchboard/verilog/common/switchboard.vh +++ b/switchboard/verilog/common/switchboard.vh @@ -21,7 +21,7 @@ `define UMI_PORT_WIRES_WIDTHS(prefix, dw, cw, aw) \ `SB_UMI_WIRES(prefix, dw, cw, aw) -`define QUEUE_TO_UMI_SIM(signal, dw, cw, aw, file, vldmode=1, clk_signal=clk) \ +`define QUEUE_TO_UMI_SIM(signal, dw, cw, aw, file, vldmode=1, clk_signal=clk, reset_sig=1'b0) \ queue_to_umi_sim #( \ .VALID_MODE_DEFAULT(vldmode), \ .DW(dw), \ @@ -30,6 +30,7 @@ .FILE(file) \ ) signal``_sb_inst ( \ .clk(clk_signal), \ + .reset(reset_sig), \ .data(signal``_data), \ .srcaddr(signal``_srcaddr), \ .dstaddr(signal``_dstaddr), \ @@ -38,7 +39,7 @@ .valid(signal``_valid) \ ) -`define UMI_TO_QUEUE_SIM(signal, dw, cw, aw, file, rdymode=1, clk_signal=clk) \ +`define UMI_TO_QUEUE_SIM(signal, dw, cw, aw, file, rdymode=1, clk_signal=clk, reset_sig=1'b0) \ umi_to_queue_sim #( \ .READY_MODE_DEFAULT(rdymode), \ .DW(dw), \ @@ -47,6 +48,7 @@ .FILE(file) \ ) signal``_sb_inst ( \ .clk(clk_signal), \ + .reset(reset_sig), \ .data(signal``_data), \ .srcaddr(signal``_srcaddr), \ .dstaddr(signal``_dstaddr), \ diff --git a/switchboard/verilog/sim/queue_to_sb_sim.sv b/switchboard/verilog/sim/queue_to_sb_sim.sv index a92f4603..a967c4bd 100644 --- a/switchboard/verilog/sim/queue_to_sb_sim.sv +++ b/switchboard/verilog/sim/queue_to_sb_sim.sv @@ -16,6 +16,7 @@ module queue_to_sb_sim #( parameter FILE="" ) ( input clk, + input reset, output [DW-1:0] data, output reg [31:0] dest=32'b0, output reg last=1'b0, @@ -83,67 +84,72 @@ module queue_to_sb_sim #( // main logic - always @(posedge clk) begin - if (ready && valid) begin - // the transaction has completed, so we can try to get another - // packet if we want to. whether we try to do this or not depends - // on the valid_mode setting. - - if ((valid_mode == 32'd1) || - ((valid_mode == 32'd2) && ($random % 2 == 32'd1))) begin - // try to receive a packet - if (id != -1) begin - /* verilator lint_off IGNOREDRETURN */ - `SB_EXT_FUNC(pi_sb_recv)(id, rdata, rdest, rlast, success); - /* verilator lint_on IGNOREDRETURN */ + always @(posedge clk or posedge reset) begin + if (reset) begin + valid <= 1'b0; + last <= 1'b0; + end else begin + if (ready && valid) begin + // the transaction has completed, so we can try to get another + // packet if we want to. whether we try to do this or not depends + // on the valid_mode setting. + + if ((valid_mode == 32'd1) || + ((valid_mode == 32'd2) && ($random % 2 == 32'd1))) begin + // try to receive a packet + if (id != -1) begin + /* verilator lint_off IGNOREDRETURN */ + `SB_EXT_FUNC(pi_sb_recv)(id, rdata, rdest, rlast, success); + /* verilator lint_on IGNOREDRETURN */ + end else begin + /* verilator lint_off BLKSEQ */ + success = 32'd0; + /* verilator lint_on BLKSEQ */ + end + + // if a packet was received, mark the output as valid + if (success == 32'd0) begin + valid <= 1'b0; + end else begin + valid <= 1'b1; + data_padded <= rdata; + dest <= rdest; + last <= rlast; + end end else begin - /* verilator lint_off BLKSEQ */ - success = 32'd0; - /* verilator lint_on BLKSEQ */ - end - - // if a packet was received, mark the output as valid - if (success == 32'd0) begin valid <= 1'b0; - end else begin - valid <= 1'b1; - data_padded <= rdata; - dest <= rdest; - last <= rlast; end - end else begin - valid <= 1'b0; - end - end else if (!valid) begin - // if there isn't a packet being presented, we can try to get one - // to present. whether we do or not depends on valid_mode: if - // valid_mode=2, then flip a coin to decide if a new packet is read. - // in any other case, try to read a packet. - - if ((valid_mode == 32'd0) || (valid_mode == 32'd1) || - ((valid_mode == 32'd2) && ($random % 2 == 32'd1))) begin - // try to receive a packet - if (id != -1) begin - /* verilator lint_off IGNOREDRETURN */ - `SB_EXT_FUNC(pi_sb_recv)(id, rdata, rdest, rlast, success); - /* verilator lint_on IGNOREDRETURN */ + end else if (!valid) begin + // if there isn't a packet being presented, we can try to get one + // to present. whether we do or not depends on valid_mode: if + // valid_mode=2, then flip a coin to decide if a new packet is read. + // in any other case, try to read a packet. + + if ((valid_mode == 32'd0) || (valid_mode == 32'd1) || + ((valid_mode == 32'd2) && ($random % 2 == 32'd1))) begin + // try to receive a packet + if (id != -1) begin + /* verilator lint_off IGNOREDRETURN */ + `SB_EXT_FUNC(pi_sb_recv)(id, rdata, rdest, rlast, success); + /* verilator lint_on IGNOREDRETURN */ + end else begin + /* verilator lint_off BLKSEQ */ + success = 32'd0; + /* verilator lint_on BLKSEQ */ + end + + // if a packet was received, mark the output as valid + if (success == 32'd0) begin + valid <= 1'b0; + end else begin + valid <= 1'b1; + data_padded <= rdata; + dest <= rdest; + last <= rlast; + end end else begin - /* verilator lint_off BLKSEQ */ - success = 32'd0; - /* verilator lint_on BLKSEQ */ - end - - // if a packet was received, mark the output as valid - if (success == 32'd0) begin valid <= 1'b0; - end else begin - valid <= 1'b1; - data_padded <= rdata; - dest <= rdest; - last <= rlast; end - end else begin - valid <= 1'b0; end end end diff --git a/switchboard/verilog/sim/queue_to_umi_sim.sv b/switchboard/verilog/sim/queue_to_umi_sim.sv index ef1dadbf..197510e4 100644 --- a/switchboard/verilog/sim/queue_to_umi_sim.sv +++ b/switchboard/verilog/sim/queue_to_umi_sim.sv @@ -11,6 +11,7 @@ module queue_to_umi_sim #( parameter FILE="" ) ( input clk, + input reset, output [DW-1:0] data, output [AW-1:0] srcaddr, output [AW-1:0] dstaddr, @@ -25,6 +26,7 @@ module queue_to_umi_sim #( .FILE(FILE) ) rx_i ( .clk(clk), + .reset(reset), .data({data, srcaddr, dstaddr, cmd}), .dest(), .last(), diff --git a/switchboard/verilog/sim/sb_axi_m.sv b/switchboard/verilog/sim/sb_axi_m.sv index 8b23d8f2..5f7509f2 100644 --- a/switchboard/verilog/sim/sb_axi_m.sv +++ b/switchboard/verilog/sim/sb_axi_m.sv @@ -16,6 +16,7 @@ module sb_axi_m #( parameter FILE="" ) ( input wire clk, + input wire reset, // AXI master interface // adapted from https://github.com/alexforencich/verilog-axi @@ -62,6 +63,7 @@ module sb_axi_m #( .DW(ADDR_WIDTH + 3 + ID_WIDTH + 8 + 3 + 2 + 1 + 4) ) aw_channel ( .clk(clk), + .reset(reset), .data({m_axi_awcache, m_axi_awlock, m_axi_awburst, m_axi_awsize, m_axi_awlen, m_axi_awid, m_axi_awprot, m_axi_awaddr}), .dest(), @@ -77,6 +79,7 @@ module sb_axi_m #( .DW(DATA_WIDTH + STRB_WIDTH + 1) ) w_channel ( .clk(clk), + .reset(reset), .data({m_axi_wlast, m_axi_wstrb, m_axi_wdata}), .dest(), .last(), @@ -91,6 +94,7 @@ module sb_axi_m #( .DW(2 + ID_WIDTH) ) b_channel ( .clk(clk), + .reset(reset), .data({m_axi_bid, m_axi_bresp}), .dest(), .last(), @@ -105,6 +109,7 @@ module sb_axi_m #( .DW(ADDR_WIDTH + 3 + ID_WIDTH + 8 + 3 + 2 + 1 + 4) ) ar_channel ( .clk(clk), + .reset(reset), .data({m_axi_arcache, m_axi_arlock, m_axi_arburst, m_axi_arsize, m_axi_arlen, m_axi_arid, m_axi_arprot, m_axi_araddr}), .dest(), @@ -120,6 +125,7 @@ module sb_axi_m #( .DW(DATA_WIDTH + 2 + ID_WIDTH + 1) ) r_channel ( .clk(clk), + .reset(reset), .data({m_axi_rlast, m_axi_rid, m_axi_rresp, m_axi_rdata}), .dest(), .last(), diff --git a/switchboard/verilog/sim/sb_axil_m.sv b/switchboard/verilog/sim/sb_axil_m.sv index d5ac2ab4..9ec32d27 100644 --- a/switchboard/verilog/sim/sb_axil_m.sv +++ b/switchboard/verilog/sim/sb_axil_m.sv @@ -15,6 +15,7 @@ module sb_axil_m #( parameter FILE="" ) ( input wire clk, + input wire reset, // AXI lite master interface // adapted from https://github.com/alexforencich/verilog-axi @@ -45,6 +46,7 @@ module sb_axil_m #( .DW(ADDR_WIDTH + 3) ) aw_channel ( .clk(clk), + .reset(reset), .data({m_axil_awprot, m_axil_awaddr}), .dest(), .last(), @@ -59,6 +61,7 @@ module sb_axil_m #( .DW(DATA_WIDTH + STRB_WIDTH) ) w_channel ( .clk(clk), + .reset(reset), .data({m_axil_wstrb, m_axil_wdata}), .dest(), .last(), @@ -73,6 +76,7 @@ module sb_axil_m #( .DW(2) ) b_channel ( .clk(clk), + .reset(reset), .data(m_axil_bresp), .dest(), .last(), @@ -87,6 +91,7 @@ module sb_axil_m #( .DW(ADDR_WIDTH + 3) ) ar_channel ( .clk(clk), + .reset(reset), .data({m_axil_arprot, m_axil_araddr}), .dest(), .last(), @@ -101,6 +106,7 @@ module sb_axil_m #( .DW(DATA_WIDTH + 2) ) r_channel ( .clk(clk), + .reset(reset), .data({m_axil_rresp, m_axil_rdata}), .dest(), .last(), diff --git a/switchboard/verilog/sim/sb_axil_s.sv b/switchboard/verilog/sim/sb_axil_s.sv index 063a2886..7b8fd6a4 100644 --- a/switchboard/verilog/sim/sb_axil_s.sv +++ b/switchboard/verilog/sim/sb_axil_s.sv @@ -15,6 +15,7 @@ module sb_axil_s #( parameter FILE="" ) ( input wire clk, + input wire reset, // AXI lite master interface // adapted from https://github.com/alexforencich/verilog-axi @@ -45,6 +46,7 @@ module sb_axil_s #( .DW(ADDR_WIDTH + 3) ) aw_channel ( .clk(clk), + .reset(reset), .data({s_axil_awprot, s_axil_awaddr}), .dest(), .last(), @@ -59,6 +61,7 @@ module sb_axil_s #( .DW(DATA_WIDTH + STRB_WIDTH) ) w_channel ( .clk(clk), + .reset(reset), .data({s_axil_wstrb, s_axil_wdata}), .dest(), .last(), @@ -73,6 +76,7 @@ module sb_axil_s #( .DW(2) ) b_channel ( .clk(clk), + .reset(reset), .data(s_axil_bresp), .dest(), .last(), @@ -87,6 +91,7 @@ module sb_axil_s #( .DW(ADDR_WIDTH + 3) ) ar_channel ( .clk(clk), + .reset(reset), .data({s_axil_arprot, s_axil_araddr}), .dest(), .last(), @@ -101,6 +106,7 @@ module sb_axil_s #( .DW(DATA_WIDTH + 2) ) r_channel ( .clk(clk), + .reset(reset), .data({s_axil_rresp, s_axil_rdata}), .dest(), .last(), diff --git a/switchboard/verilog/sim/sb_to_queue_sim.sv b/switchboard/verilog/sim/sb_to_queue_sim.sv index 57fac59a..803b0e1d 100644 --- a/switchboard/verilog/sim/sb_to_queue_sim.sv +++ b/switchboard/verilog/sim/sb_to_queue_sim.sv @@ -16,6 +16,7 @@ module sb_to_queue_sim #( parameter FILE="" ) ( input clk, + input reset, input [DW-1:0] data, input [31:0] dest, input last, @@ -83,68 +84,86 @@ module sb_to_queue_sim #( // main logic - always @(posedge clk) begin - if (ready && valid) begin - // try to send a packet, with success==1 indicating that the - // send was successful. in general, sends should succeed, - // unless the queue they're trying to push to is full. - if (id != -1) begin - /* verilator lint_off IGNOREDRETURN */ - `SB_EXT_FUNC(pi_sb_send)(id, data_padded, dest, last, success); - /* verilator lint_on IGNOREDRETURN */ - end else begin - /* verilator lint_off BLKSEQ */ - success = 32'd0; - /* verilator lint_on BLKSEQ */ - end + always @(posedge clk or posedge reset) begin + if (reset) begin + pending <= 1'b0; + ready <= 1'b0; + slast <= 1'b0; + end else begin + if (ready && valid) begin + // try to send a packet, with success==1 indicating that the + // send was successful. in general, sends should succeed, + // unless the queue they're trying to push to is full. + if (id != -1) begin + /* verilator lint_off IGNOREDRETURN */ + `SB_EXT_FUNC(pi_sb_send)(id, data_padded, dest, last, success); + /* verilator lint_on IGNOREDRETURN */ + end else begin + /* verilator lint_off BLKSEQ */ + success = 32'd0; + /* verilator lint_on BLKSEQ */ + end - // if the send was not successful, mark it pending. ready cannot be asserted - // if there is a pending re-send, since the next send may fail, and there - // would be no place to store the data for the new resend. we could have a - // queue, but that would have finite depth, so we would still have to be able - // to apply backpressure. - if (success == 32'd0) begin - pending <= 1'b1; - ready <= 1'b0; - sdata <= data_padded; - sdest <= dest; - slast <= last; - end else begin - pending <= 1'b0; - if (ready_mode == 32'd0) begin + // if the send was not successful, mark it pending. ready cannot be asserted + // if there is a pending re-send, since the next send may fail, and there + // would be no place to store the data for the new resend. we could have a + // queue, but that would have finite depth, so we would still have to be able + // to apply backpressure. + if (success == 32'd0) begin + pending <= 1'b1; ready <= 1'b0; - end else if (ready_mode == 32'd1) begin - ready <= 1'b1; + sdata <= data_padded; + sdest <= dest; + slast <= last; end else begin - /* verilator lint_off WIDTH */ - ready <= ($random % 2); - /* verilator lint_on WIDTH */ + pending <= 1'b0; + if (ready_mode == 32'd0) begin + ready <= 1'b0; + end else if (ready_mode == 32'd1) begin + ready <= 1'b1; + end else begin + /* verilator lint_off WIDTH */ + ready <= ($random % 2); + /* verilator lint_on WIDTH */ + end + end + end else if (pending) begin + // try to re-send a packet. note that in a given cycle, a packet can be sent + // for the first time or re-sent, but not both, because ready cannot be asserted + // if there is a packet pending, for the reason given above. + if (id != -1) begin + /* verilator lint_off IGNOREDRETURN */ + `SB_EXT_FUNC(pi_sb_send)(id, sdata, sdest, slast, success); + /* verilator lint_on IGNOREDRETURN */ + end else begin + /* verilator lint_off BLKSEQ */ + success = 32'd0; + /* verilator lint_on BLKSEQ */ end - end - end else if (pending) begin - // try to re-send a packet. note that in a given cycle, a packet can be sent - // for the first time or re-sent, but not both, because ready cannot be asserted - // if there is a packet pending, for the reason given above. - if (id != -1) begin - /* verilator lint_off IGNOREDRETURN */ - `SB_EXT_FUNC(pi_sb_send)(id, sdata, sdest, slast, success); - /* verilator lint_on IGNOREDRETURN */ - end else begin - /* verilator lint_off BLKSEQ */ - success = 32'd0; - /* verilator lint_on BLKSEQ */ - end - // if the re-send was unsuccessful, we have to keep ready de-asserted, - // but if it was successful we can assert ready if we want to, - // depending on ready_mode - if (success == 32'd0) begin - pending <= 1'b1; - ready <= 1'b0; + // if the re-send was unsuccessful, we have to keep ready de-asserted, + // but if it was successful we can assert ready if we want to, + // depending on ready_mode + if (success == 32'd0) begin + pending <= 1'b1; + ready <= 1'b0; + end else begin + pending <= 1'b0; + if (ready_mode == 32'd0) begin + ready <= 1'b0; + end else if (ready_mode == 32'd1) begin + ready <= 1'b1; + end else begin + /* verilator lint_off WIDTH */ + ready <= ($random % 2); + /* verilator lint_on WIDTH */ + end + end end else begin - pending <= 1'b0; + // if there's nothing pending, then we can assert ready + // if we want to. whether we do or not depends on ready_mode. if (ready_mode == 32'd0) begin - ready <= 1'b0; + ready <= valid; end else if (ready_mode == 32'd1) begin ready <= 1'b1; end else begin @@ -153,18 +172,6 @@ module sb_to_queue_sim #( /* verilator lint_on WIDTH */ end end - end else begin - // if there's nothing pending, then we can assert ready - // if we want to. whether we do or not depends on ready_mode. - if (ready_mode == 32'd0) begin - ready <= valid; - end else if (ready_mode == 32'd1) begin - ready <= 1'b1; - end else begin - /* verilator lint_off WIDTH */ - ready <= ($random % 2); - /* verilator lint_on WIDTH */ - end end end diff --git a/switchboard/verilog/sim/umi_to_queue_sim.sv b/switchboard/verilog/sim/umi_to_queue_sim.sv index fc87dbff..3576466c 100644 --- a/switchboard/verilog/sim/umi_to_queue_sim.sv +++ b/switchboard/verilog/sim/umi_to_queue_sim.sv @@ -11,6 +11,7 @@ module umi_to_queue_sim #( parameter FILE="" ) ( input clk, + input reset, input [DW-1:0] data, input [AW-1:0] srcaddr, input [AW-1:0] dstaddr, @@ -25,6 +26,7 @@ module umi_to_queue_sim #( .FILE(FILE) ) tx_i ( .clk(clk), + .reset(reset), .data({data, srcaddr, dstaddr, cmd}), .dest({16'h0000, dstaddr[55:40]}), .last(cmd[22]), From 91839bdd6bc289d69d2daf62d1515ada149d6d6f Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 20 Oct 2025 12:03:59 -0400 Subject: [PATCH 14/20] Walked back reset awareness in autowrap --- examples/umiparam/test.py | 4 +-- switchboard/autowrap.py | 68 +++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/examples/umiparam/test.py b/examples/umiparam/test.py index cc61d7a3..cc9ac955 100755 --- a/examples/umiparam/test.py +++ b/examples/umiparam/test.py @@ -80,8 +80,8 @@ def build_testbench(): ) interfaces = { - 'udev_req': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='input', txrx='udev', reset='nreset'), - 'udev_resp': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='output', txrx='udev', reset='nreset'), + 'udev_req': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='input', txrx='udev'), + 'udev_resp': dict(type='umi', dw=dw, aw=aw, cw=cw, direction='output', txrx='udev'), 'value': dict(type='plusarg', width=32, default=77) } diff --git a/switchboard/autowrap.py b/switchboard/autowrap.py index cded675d..337fd42d 100644 --- a/switchboard/autowrap.py +++ b/switchboard/autowrap.py @@ -351,34 +351,6 @@ def autowrap( lines += [''] - max_rst_dly = None - - for inst_resets in resets.values(): - if len(inst_resets) > 0: - # find the max reset delay for this instance - inst_max_rst_dly = max(reset['delay'] for reset in inst_resets) - - # update the overall max reset delay - if (max_rst_dly is None) or (inst_max_rst_dly > max_rst_dly): - max_rst_dly = inst_max_rst_dly - - if max_rst_dly is not None: - lines += [ - tab + f"reg [{max_rst_dly}:0] rstvec = '1;" - '', - tab + 'always @(posedge clk) begin' - ] - - if max_rst_dly > 0: - lines += [(2 * tab) + f"rstvec <= {{rstvec[{max_rst_dly - 1}:0], 1'b0}};"] - else: - lines += [(2 * tab) + "rstvec <= 1'b0;"] - - lines += [ - tab + 'end', - '' - ] - for instance in instances: # declare wires for tieoffs @@ -439,19 +411,11 @@ def autowrap( if decl_wire and (wire is not None): lines += [tab + f'`SB_UMI_WIRES({wire}, {dw}, {cw}, {aw});'] - # Extract interface reset information - reset = normalize_reset(value['reset']) - reset_delay = reset['delay'] - reset_sig = f'rstvec[{reset_delay}]' - if external: if direction_is_input(direction): - lines += [ - tab - + f'`QUEUE_TO_UMI_SIM({wire}, {dw}, {cw}, {aw}, "", 1, clk, {reset_sig});' - ] + lines += [tab + f'`QUEUE_TO_UMI_SIM({wire}, {dw}, {cw}, {aw}, "");'] elif direction_is_output(direction): - lines += [tab + f'`UMI_TO_QUEUE_SIM({wire}, {dw}, {cw}, {aw}, "", 1, clk, {reset_sig});'] + lines += [tab + f'`UMI_TO_QUEUE_SIM({wire}, {dw}, {cw}, {aw}, "");'] else: raise Exception(f'Unsupported UMI direction: {direction}') elif type == 'axi': @@ -521,6 +485,34 @@ def autowrap( lines += [''] + max_rst_dly = None + + for inst_resets in resets.values(): + if len(inst_resets) > 0: + # find the max reset delay for this instance + inst_max_rst_dly = max(reset['delay'] for reset in inst_resets) + + # update the overall max reset delay + if (max_rst_dly is None) or (inst_max_rst_dly > max_rst_dly): + max_rst_dly = inst_max_rst_dly + + if max_rst_dly is not None: + lines += [ + tab + f"reg [{max_rst_dly}:0] rstvec = '1;" + '', + tab + 'always @(posedge clk) begin' + ] + + if max_rst_dly > 0: + lines += [(2 * tab) + f"rstvec <= {{rstvec[{max_rst_dly - 1}:0], 1'b0}};"] + else: + lines += [(2 * tab) + "rstvec <= 1'b0;"] + + lines += [ + tab + 'end', + '' + ] + for instance, module in instances.items(): # start of the instantiation From 15f3e10cbb0743c1f2be34c347626552a48b2303 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 20 Oct 2025 12:05:52 -0400 Subject: [PATCH 15/20] Added toplevel param to SB_SETUP_PROBES in switchboard.vh --- switchboard/verilog/common/switchboard.vh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/switchboard/verilog/common/switchboard.vh b/switchboard/verilog/common/switchboard.vh index 400414d5..702c1e0f 100644 --- a/switchboard/verilog/common/switchboard.vh +++ b/switchboard/verilog/common/switchboard.vh @@ -373,7 +373,7 @@ .clk(clk_signal) \ ); -`define SB_SETUP_PROBES \ +`define SB_SETUP_PROBES(toplevel=testbench) \ `ifdef SB_TRACE \ string dumpfile_sb_value; \ initial begin \ @@ -387,7 +387,7 @@ $dumpfile("testbench.vcd"); \ `endif \ end \ - $dumpvars(0, testbench); \ + $dumpvars(0, ``toplevel); \ end \ end \ `endif From 28786269c5ef6f1e08b6108bfab16cd3a82d435a Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Mon, 20 Oct 2025 14:45:59 -0400 Subject: [PATCH 16/20] WIP: Adding SB APB master --- switchboard/apb.py | 266 +++++++++++++++++++++ switchboard/verilog/common/switchboard.vh | 33 +++ switchboard/verilog/sim/sb_apb_m.sv | 146 +++++++++++ switchboard/verilog/sim/switchboard_sim.py | 3 +- 4 files changed, 447 insertions(+), 1 deletion(-) create mode 100644 switchboard/apb.py create mode 100644 switchboard/verilog/sim/sb_apb_m.sv diff --git a/switchboard/apb.py b/switchboard/apb.py new file mode 100644 index 00000000..315175f5 --- /dev/null +++ b/switchboard/apb.py @@ -0,0 +1,266 @@ +# Python interface for AXI-Lite reads and writes + +# Copyright (c) 2024 Zero ASIC Corporation +# This code is licensed under Apache License 2.0 (see LICENSE for details) + +import numpy as np + +from math import floor, ceil, log2 +from numbers import Integral + +from _switchboard import PySbPacket, PySbTx, PySbRx + + +class ApbTxRx: + def __init__( + self, + uri: str, + fresh: bool = True, + data_width: int = 32, + addr_width: int = 16, + prot: int = 0, + slv_err_expected: bool = False, + queue_suffix: str = '.q', + max_rate: float = -1 + ): + """ + Parameters + ---------- + uri: str + Base name of for switchboard queues used to convey APB transactions. + fresh: bool, optional + If True (default), the queue specified by the uri parameter will get cleared + before executing the simulation. + data_width: int, optional + Width the write and read data buses, in bits. + addr_width: int, optional + Width the write and read address buses, in bits. + prot: int, optional + Default value of PROT to use for read and write transactions. Can be + overridden on a transaction-by-transaction basis. + slv_err_expected: bool, optional + Default response to expect from reads and writes. + None means "don't check the response". + This default can be overridden on a transaction-by-transaction basis. + queue_suffix: str, optional + File extension/suffix to use when naming switchboard queues that carry + APB transactions. For example, if set to ".queue", the write address + queue name will be "{uri}-aw.queue" + """ + + # check data types + assert isinstance(data_width, Integral), 'data_width must be an integer' + assert isinstance(addr_width, Integral), 'addr_width must be an integer' + + # check that data width is a multiple of a byte + data_width_choices = [8, 16, 32, 64, 128, 256, 512, 1024] + assert data_width in data_width_choices, \ + f'data_width must be in {data_width_choices}' + + # check that data and address widths are supported + SBDW = 416 + assert 0 < data_width <= floor(SBDW / (1 + (1 / 8))), 'data_width out of range' + assert 0 < addr_width <= SBDW - 3, 'addr_width out of range' + + # save settings + self.data_width = data_width + self.addr_width = addr_width + self.default_prot = prot + self.default_slv_err_expected = slv_err_expected + + # create the queues + self.apb_req = PySbTx(f'{uri}_apb_req{queue_suffix}', fresh=fresh, max_rate=max_rate) + self.apb_resp = PySbRx(f'{uri}_apb_resp{queue_suffix}', fresh=fresh, max_rate=max_rate) + + @property + def strb_width(self): + return self.data_width // 8 + + def write( + self, + addr: Integral, + data, + prot: Integral = None, + slv_err_expected: bool = None + ): + """ + Parameters + ---------- + addr: int + Address to write to + + data: np.uint8, np.uint16, np.uint32, np.uint64, or np.array + Data to write + + prot: Integral + Value of PROT for this transaction. Defaults to the value provided in the + ApbTxRx constructor if not provided, which in turn defaults to 0. + + slv_err_expected: str, optional + Response to expect for this transaction. + None means, "don't check the response". Defaults to the + value provided in the ApbTxRx constructor if not provided, which in turn + defaults to False. + + Returns + ------- + bool + slv_err: True if SLVERR was received, False otherwise. + """ + (rd_data, slv_err) = self.transaction( + write=True, + addr=addr, + data=data, + prot=prot, + slv_err_expected=slv_err_expected + ) + return slv_err + + def read( + self, + addr: Integral, + prot: Integral = None, + resp_expected: str = None + ): + """ + Parameters + ---------- + addr: int + Address to read from + + prot: Integral + Value of PROT for this transaction. Defaults to the value provided in the + AxiLiteTxRx constructor if not provided, which in turn defaults to 0. + + resp_expected: str, optional + Response to expect for this transaction. Options are 'OKAY', 'EXOKAY', 'SLVERR', + 'DECERR', and None. None means, "don't check the response". Defaults to the + value provided in the AxiLiteTxRx constructor if not provided, which in turn + defaults to 'OKAY' + + Returns + ------- + int + Value read, as an arbitrary-size Python integer. + """ + (rd_data, slv_err) = self.transaction( + write=False, + addr=addr, + data=None, + prot=prot, + slv_err_expected=resp_expected + ) + return rd_data + + def transaction( + self, + write: bool, + addr: Integral, + data, + prot: Integral = None, + slv_err_expected: bool = None + ): + """ + Parameters + ---------- + addr: int + Address to write to + + data: np.uint8, np.uint16, np.uint32, np.uint64 + Data to write + + prot: Integral + Value of PROT for this transaction. Defaults to the value provided in the + ApbTxRx constructor if not provided, which in turn defaults to 0. + + slv_err_expected: str, optional + Response to expect for this transaction. + None means, "don't check the response". Defaults to the + value provided in the ApbTxRx constructor if not provided, which in turn + defaults to False. + + Returns + ------- + bool + slv_err: True if SLVERR was received, False otherwise. + """ + + # set defaults + + if prot is None: + prot = self.default_prot + + if slv_err_expected is None: + slv_err_expected = self.default_slv_err_expected + + # check/standardize data types + + assert isinstance(addr, Integral), 'addr must be an integer' + addr = int(addr) + + assert isinstance(prot, Integral), 'prot must be an integer' + prot = int(prot) + + if data is None: + data = np.zeros((self.data_width // 8,), dtype=np.uint8) + elif isinstance(data, np.integer): + data = np.frombuffer(data.tobytes(), dtype=np.uint8) + else: + raise TypeError(f"Unknown data type: {type(data)}") + + bytes_to_send = data.size + + # range validation + + assert 0 <= addr < (1 << self.addr_width), 'addr out of range' + assert addr + bytes_to_send <= (1 << self.addr_width), \ + "transaction exceeds the address space." + + assert 0 <= prot < (1 << 3), 'prot out of range' + + header_bytes: int = int(ceil((1 + 3 + self.strb_width) / 8.0)) + data_bytes: int = self.data_width // 8 + addr_bytes: int = self.addr_width // 8 + + addr_mask = (1 << self.addr_width) - 1 + addr_mask >>= ceil(log2(data_bytes)) + addr_mask <<= ceil(log2(data_bytes)) + + # calculate strobe value based on the offset and number + # of bytes that we're writing. + strb = (1 << data_bytes) - 1 + header = int(write) + header = (header << 3) | prot + header = (header << self.strb_width) | strb + header = np.frombuffer( + header.to_bytes(header_bytes, 'little'), + dtype=np.uint8 + ) + + addr_as_buff = np.frombuffer( + (addr & addr_mask).to_bytes((self.addr_width + 7) // 8, 'little'), + dtype=np.uint8 + ) + + pack = np.empty((header_bytes + addr_bytes + data_bytes,), dtype=np.uint8) + pack[0:data_bytes] = data + pack[data_bytes:data_bytes + addr_bytes] = addr_as_buff + pack[data_bytes + addr_bytes:] = header + pack = PySbPacket(data=pack, flags=1, destination=0) + # Transmit request + self.apb_req.send(pack, True) + + # wait for response + pack = self.apb_resp.recv(True) + pack = pack.data.tobytes() + pack = int.from_bytes(pack, 'little') + + # decode the response + rd_data = pack & ((1 << self.data_width) - 1) + slv_err = bool((pack >> self.data_width) & 0b1) + + # check the response if desired + if slv_err_expected is not None: + assert slv_err == slv_err_expected, f'Unexpected response: slv_err = {slv_err}' + + return (rd_data, slv_err) diff --git a/switchboard/verilog/common/switchboard.vh b/switchboard/verilog/common/switchboard.vh index 702c1e0f..bd8c8bbd 100644 --- a/switchboard/verilog/common/switchboard.vh +++ b/switchboard/verilog/common/switchboard.vh @@ -156,6 +156,39 @@ .valid(signal``_valid) \ ) +`define SB_APB_WIRES(signal, dw, aw) \ + wire signal``_psel; \ + wire signal``_penable; \ + wire signal``_pwrite; \ + wire [2:0] signal``_pprot; \ + wire [((aw)-1):0] signal``_paddr; \ + wire [((dw)/8)-1:0] signal``_pstrb; \ + wire [((dw)-1):0] signal``_pwdata; \ + wire [((dw)-1):0] signal``_prdata; \ + wire signal``_pready; \ + wire signal``_pslverr + +`define SB_APB_M(signal, dw, aw, file, vldmode=1, clk_signal=clk, rst_signal=1'b0) \ + sb_apb_m #( \ + .DATA_WIDTH(dw), \ + .ADDR_WIDTH(aw), \ + .VALID_MODE_DEFAULT(vldmode), \ + .FILE(file) \ + ) signal``_sb_inst ( \ + .clk(clk_signal), \ + .reset(rst_signal), \ + .m_apb_psel(signal``_psel), \ + .m_apb_penable(signal``_penable), \ + .m_apb_pwrite(signal``_pwrite), \ + .m_apb_pprot(signal``_pprot), \ + .m_apb_paddr(signal``_paddr), \ + .m_apb_pstrb(signal``_pstrb), \ + .m_apb_pwdata(signal``_pwdata), \ + .m_apb_prdata(signal``_prdata), \ + .m_apb_pready(signal``_pready), \ + .m_apb_pslverr(signal``_pslverr) \ + ) + `define SB_AXIL_WIRES(signal, dw, aw) \ wire [((aw)-1):0] signal``_awaddr; \ wire [2:0] signal``_awprot; \ diff --git a/switchboard/verilog/sim/sb_apb_m.sv b/switchboard/verilog/sim/sb_apb_m.sv new file mode 100644 index 00000000..018230b5 --- /dev/null +++ b/switchboard/verilog/sim/sb_apb_m.sv @@ -0,0 +1,146 @@ +// Copyright (c) 2024 Zero ASIC Corporation +// This code is licensed under Apache License 2.0 (see LICENSE for details) + +`default_nettype none + +module sb_apb_m #( + // AXI settings + parameter DATA_WIDTH = 32, + parameter ADDR_WIDTH = 16, + parameter STRB_WIDTH = (DATA_WIDTH/8), + + // Switchboard settings + parameter integer VALID_MODE_DEFAULT=1, + parameter FILE="" +) ( + input wire clk, + input wire reset, + + // APB master interface + output wire m_apb_psel, + output wire m_apb_penable, + output wire m_apb_pwrite, + output wire [2:0] m_apb_pprot, + output wire [ADDR_WIDTH-1:0] m_apb_paddr, + output wire [STRB_WIDTH-1:0] m_apb_pstrb, + output wire [DATA_WIDTH-1:0] m_apb_pwdata, + input wire [DATA_WIDTH-1:0] m_apb_prdata, + input wire m_apb_pready, + input wire m_apb_pslverr +); + + // APB FSM state register type + typedef enum logic [1:0] { + APB_IDLE, + APB_SETUP, + APB_ACCESS + } apb_state_t; + + // Wires + wire apb_trans_avail; + wire apb_fire; + + // APB FSM state register + apb_state_t apb_state; + + // APB request channel + queue_to_sb_sim #( + .VALID_MODE_DEFAULT(VALID_MODE_DEFAULT), + .DW(1 + 3 + STRB_WIDTH + ADDR_WIDTH + DATA_WIDTH) + ) apb_req_channel ( + .clk(clk), + .reset(reset), + .data({m_apb_pwrite, m_apb_pprot, m_apb_pstrb, m_apb_paddr, m_apb_pwdata}), + .dest(), + .last(), + .valid(apb_trans_avail), + .ready(apb_fire) + ); + + // APB response channel + sb_to_queue_sim #( + // Queue should always be ready to receive responses + .READY_MODE_DEFAULT(1), + .DW(DATA_WIDTH + 1) + ) apb_resp_channel ( + .clk(clk), + .reset(reset), + .data({m_apb_pslverr, m_apb_prdata}), + .dest(), + .last(), + .valid(apb_fire), + .ready() + ); + + // APB master state machine + always_ff @(posedge clk or posedge reset) + if (reset) + apb_state <= APB_IDLE; + else + case (apb_state) + APB_IDLE: + if (apb_trans_avail) apb_state <= APB_SETUP; + APB_SETUP: + apb_state <= APB_ACCESS; + APB_ACCESS: + if (m_apb_pready) apb_state <= APB_IDLE; + default: + apb_state <= APB_IDLE; + endcase + + assign m_apb_psel = (apb_state == APB_SETUP || apb_state == APB_ACCESS); + assign m_apb_penable = (apb_state == APB_ACCESS); + assign apb_fire = m_apb_psel & m_apb_penable & m_apb_pready; + + // handle differences between simulators + + `ifdef __ICARUS__ + `define SB_START_FUNC task + `define SB_END_FUNC endtask + `else + `define SB_START_FUNC function void + `define SB_END_FUNC endfunction + `endif + + `SB_START_FUNC init(input string uri); + string s; + + /* verilator lint_off IGNOREDRETURN */ + $sformat(s, "%0s_apb_req.q", uri); + apb_req_channel.init(s); + + $sformat(s, "%0s_apb_resp.q", uri); + apb_resp_channel.init(s); + /* verilator lint_on IGNOREDRETURN */ + `SB_END_FUNC + + `SB_START_FUNC set_valid_mode(input integer value); + /* verilator lint_off IGNOREDRETURN */ + apb_req_channel.set_valid_mode(value); + /* verilator lint_on IGNOREDRETURN */ + `SB_END_FUNC + + `SB_START_FUNC set_ready_mode(input integer value); + /* verilator lint_off IGNOREDRETURN */ + apb_resp_channel.set_ready_mode(value); + /* verilator lint_on IGNOREDRETURN */ + `SB_END_FUNC + + // initialize + + initial begin + if (FILE != "") begin + /* verilator lint_off IGNOREDRETURN */ + init(FILE); + /* verilator lint_on IGNOREDRETURN */ + end + end + + // clean up macros + + `undef SB_START_FUNC + `undef SB_END_FUNC + +endmodule + +`default_nettype wire diff --git a/switchboard/verilog/sim/switchboard_sim.py b/switchboard/verilog/sim/switchboard_sim.py index a924be4a..6f3a0321 100644 --- a/switchboard/verilog/sim/switchboard_sim.py +++ b/switchboard/verilog/sim/switchboard_sim.py @@ -26,7 +26,8 @@ def __init__(self): "sb_tx_sim.sv", "umi_rx_sim.sv", "umi_tx_sim.sv", - "xyce_intf.sv" + "xyce_intf.sv", + "sb_apb_m.sv" ] deps = [Common()] From bad948e05a2c7380e650bfa6494f22caf395e3b9 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Tue, 21 Oct 2025 14:36:19 -0400 Subject: [PATCH 17/20] Fixed setup probes --- switchboard/autowrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/switchboard/autowrap.py b/switchboard/autowrap.py index 337fd42d..f55bef0e 100644 --- a/switchboard/autowrap.py +++ b/switchboard/autowrap.py @@ -633,7 +633,7 @@ def autowrap( lines += [''] - lines += [tab + '`SB_SETUP_PROBES'] + lines += [tab + '`SB_SETUP_PROBES();'] lines += [''] lines += ['endmodule'] From 0c601d9e93538700d7aec87ae51a5c7d66fce78e Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Tue, 21 Oct 2025 15:31:13 -0400 Subject: [PATCH 18/20] Added APB type to autowrap.py --- switchboard/autowrap.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/switchboard/autowrap.py b/switchboard/autowrap.py index f55bef0e..230755c5 100644 --- a/switchboard/autowrap.py +++ b/switchboard/autowrap.py @@ -9,6 +9,7 @@ from .umi import UmiTxRx from .axi import AxiTxRx from .axil import AxiLiteTxRx +from switchboard.apb import ApbTxRx from .bitvector import slice_to_msb_lsb from _switchboard import PySbTx, PySbRx @@ -118,7 +119,7 @@ def normalize_interface(name, value): value['txrx'] = None if 'uri' not in value: value['uri'] = f'{name}.q' - elif type in ['axi', 'axil']: + elif type in ['axi', 'axil', 'apb']: if 'dw' not in value: value['dw'] = 32 if 'aw' not in value: @@ -694,7 +695,7 @@ def normalize_direction(type, direction): return 'inout' else: raise Exception(f'Unsupported direction for interface type "{type}": "{direction}"') - elif type_is_axi(type) or type_is_axil(type): + elif type_is_axi(type) or type_is_axil(type) or type_is_apb(type): if direction_is_manager(direction): return 'manager' elif direction_is_subordinate(direction): @@ -794,6 +795,10 @@ def type_is_axil(type): return type.lower() in ['axil'] +def type_is_apb(type): + return type.lower() in ['apb'] + + def type_is_input(type): return type.lower() in ['i', 'in', 'input'] @@ -823,6 +828,8 @@ def normalize_intf_type(type): return 'axi' elif type_is_axil(type): return 'axil' + elif type_is_apb(type): + return 'apb' elif type_is_input(type): return 'input' elif type_is_output(type): @@ -966,6 +973,27 @@ def create_intf_obj(value, fresh=True, max_rate=-1): addr_width=value['aw'], **kwargs) else: raise Exception(f'Unsupported AXI-Lite direction: "{direction}"') + elif type_is_apb(type): + kwargs = {} + + if 'prot' in value: + kwargs['prot'] = value['prot'] + + if 'max_rate' in value: + kwargs['max_rate'] = value['max_rate'] + else: + # use default if not set for this particular interface + kwargs['max_rate'] = max_rate + + if direction_is_subordinate(direction): + obj = ApbTxRx( + uri=value['uri'], + data_width=value['dw'], + addr_width=value['aw'], + **kwargs + ) + else: + raise Exception(f'Unsupported APB direction: "{direction}"') else: raise Exception(f'Unsupported interface type: "{type}"') From 6a190b72bfb6e43558aa21497d2cfbcb9b0b4564 Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Tue, 21 Oct 2025 15:55:15 -0400 Subject: [PATCH 19/20] Added apb_uris func to apb.py --- switchboard/apb.py | 12 ++++++++++++ switchboard/network.py | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/switchboard/apb.py b/switchboard/apb.py index 315175f5..b72db912 100644 --- a/switchboard/apb.py +++ b/switchboard/apb.py @@ -264,3 +264,15 @@ def transaction( assert slv_err == slv_err_expected, f'Unexpected response: slv_err = {slv_err}' return (rd_data, slv_err) + + +def apb_uris(prefix, suffix='.q'): + # returns a list of the URIs associated with a given APB + # prefix. For example, apb_uris('apb') returns ['apb_apb_req.q', + # 'apb_apb_resp.q']. Changing the optional suffix + # argument changes the file extension assumed in generating this list. + + return [ + f'{prefix}_apb_req{suffix}', + f'{prefix}_apb_resp{suffix}' + ] diff --git a/switchboard/network.py b/switchboard/network.py index ac8755b7..9a7443ce 100644 --- a/switchboard/network.py +++ b/switchboard/network.py @@ -10,8 +10,9 @@ from .sbdut import SbDut from .axi import axi_uris +from .apb import apb_uris from .autowrap import (directions_are_compatible, normalize_intf_type, - type_is_umi, type_is_sb, create_intf_objs, type_is_axi, type_is_axil, + type_is_umi, type_is_sb, create_intf_objs, type_is_axi, type_is_axil, type_is_apb, autowrap, flip_intf, normalize_direction, WireExpr, types_are_compatible) from .cmdline import get_cmdline_args from .sbtcp import start_tcp_bridge @@ -21,6 +22,7 @@ from siliconcompiler import Design + class SbIntf: def __init__(self, inst, name, width=None, indices=None): self.inst = inst @@ -667,6 +669,8 @@ def generate_inst_name(self, prefix): def register_uri(self, type, uri, fresh=True): if type_is_axi(type) or type_is_axil(type): uris = axi_uris(uri) + elif type_is_apb(type): + uris = apb_uris(uri) else: uris = [uri] From 3db07e9b44585f1f9d2bfa8c52d5ee326654b2db Mon Sep 17 00:00:00 2001 From: Rice Shelley Date: Tue, 21 Oct 2025 16:51:00 -0400 Subject: [PATCH 20/20] Added queue removal functionality to sbdut --- switchboard/sbdut.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/switchboard/sbdut.py b/switchboard/sbdut.py index 1966eca9..777bc719 100644 --- a/switchboard/sbdut.py +++ b/switchboard/sbdut.py @@ -25,8 +25,10 @@ from .xyce import xyce_flags from .ams import make_ams_spice_wrapper, make_ams_verilog_wrapper, parse_spice_subckts from .autowrap import (normalize_clocks, normalize_interfaces, normalize_resets, normalize_tieoffs, - normalize_parameters, create_intf_objs) + normalize_parameters, create_intf_objs, type_is_axi, type_is_axil, type_is_apb) from .cmdline import get_cmdline_args +from .apb import apb_uris +from .axi import axi_uris from siliconcompiler import Design, Sim from siliconcompiler.tools import get_task @@ -540,6 +542,30 @@ def simulate( return p + def remove_queues_on_exit(self): + import atexit + from _switchboard import delete_queues + + def cleanup_func(uris=self.get_uris()): + if len(uris) > 0: + delete_queues(uris) + + atexit.register(cleanup_func) + + def get_uris(self): + uris = [] + for _, intf in self.intf_defs.items(): + uri = intf.get('uri', None) + type = intf.get('type', None) + if uri is not None: + if type_is_axi(type) or type_is_axil(type): + uris.extend(axi_uris(uri)) + elif type_is_apb(type): + uris.extend(apb_uris(uri)) + else: + uris.append(uri) + return uris + def terminate( self, stop_timeout=10,