From 55cc46fb4b150b0f1916594d26b142497fadba15 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Sat, 9 Aug 2025 15:09:49 +0100 Subject: [PATCH 1/8] Add in minerva --- chipflow_digital_ip/processors/__init__.py | 5 ++++- pyproject.toml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/chipflow_digital_ip/processors/__init__.py b/chipflow_digital_ip/processors/__init__.py index e540e0e..9312d33 100644 --- a/chipflow_digital_ip/processors/__init__.py +++ b/chipflow_digital_ip/processors/__init__.py @@ -1,3 +1,6 @@ from ._openhw.cv32e40p import CV32E40P, OBIDebugModule +from minerva.core import Minerva -__all__ = ['CV32E40P', 'OBIDebugModule'] +__all__ = ['CV32E40P', 'OBIDebugModule', + 'Minerva' + ] diff --git a/pyproject.toml b/pyproject.toml index a4912c9..4feedd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ "chipflow-lib @ git+https://github.com/ChipFlow/chipflow-lib.git", "amaranth-soc @ git+https://github.com/amaranth-lang/amaranth-soc", "amaranth-stdio @ git+https://github.com/amaranth-lang/amaranth-stdio", + "minerva @ git+https://github.com/minerva-cpu/minerva", ] # Build system configuration From 548a973351e850f43854b6b9280eee76af12c923 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Thu, 16 Oct 2025 22:38:07 +0100 Subject: [PATCH 2/8] Update gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 0805338..6135d38 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,8 @@ __pycache__/ /.pdm-build /.venv /pdm.lock +/overrides.txt + +# simulation +*.gtkw +*.vcd From ede7d157b69a398826ccda9b928c9865faf81103 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Fri, 7 Nov 2025 16:16:23 +0000 Subject: [PATCH 3/8] Start of experiment with toml binding of verilog/generated verilog --- chipflow_digital_ip/io/_svtest.py | 108 +++++++++++++++++++++++++++ chipflow_digital_ip/io/usb_ohci.toml | 80 ++++++++++++++++++++ pyproject.toml | 3 +- 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 chipflow_digital_ip/io/_svtest.py create mode 100644 chipflow_digital_ip/io/usb_ohci.toml diff --git a/chipflow_digital_ip/io/_svtest.py b/chipflow_digital_ip/io/_svtest.py new file mode 100644 index 0000000..839e4a1 --- /dev/null +++ b/chipflow_digital_ip/io/_svtest.py @@ -0,0 +1,108 @@ +import os +import sys +import tomli + +from enum import StrEnum, auto +from pathlib import Path +from typing import Dict, Optional, Any, List, Annotated, Literal, Self + +from amaranth import Module, unsigned +from amaranth.lib import wiring +from amaranth.lib.wiring import In, Out, flipped, connect + +from pydantic import ( + BaseModel, ImportString, JsonValue, ValidationError, + model_validator + ) + +from chipflow import ChipFlowError + + +class Files(BaseModel): + module: Optional[ImportString] = None + path: Optional[Path] = None + + @model_validator(mode="after") + def verify_module_or_path(self) -> Self: + print(self.module) + print(self.path) + if (self.module and self.path) or (not self.module and not self.path): + raise ValueError("You must set `module` or `path`.") + return self + + +class Generators(StrEnum): + SPINALHDL = auto() + VERILOG = auto() + + def generate(self, vdir: Path, parameters: Dict[str, list|dict|str|bool|int|float|None], options: List[str]): + gen_args = [o.format(**parameters) for o in options] + match self.name: + case "SPINALHDL": + cmd = 'cd {path} && sbt "lib/runMain spinal.lib.com.usb.ohci.UsbOhciWishbone {args}"'.format( + path=vdir / "ext" / "SpinalHDL", args=" ".join(gen_args)) + print("!!! " + cmd) + if os.system(cmd) != 0: + raise OSError('Failed to run sbt') + case _ as v: + raise TypeError(f"Undefined generator type: {v}") + + + +class Generate(BaseModel): + parameters: List[str] = [] + defaults: Optional[Dict[str, JsonValue]] = None + generator: Generators + options: List[str] = [] + + +class Port(BaseModel): + interface: str # ImportString + params: Optional[Dict[str, JsonValue]] = None + vars: Optional[Dict[str, Literal["int"]]] = None + map: str | Dict[str, Dict[str, str] | str] + + +class ExternalWrap(BaseModel): + files: Files + generate: Optional[Generate] = None + clocks: Dict[str, str] = {} + resets: Dict[str, str] = {} + ports: Dict[str,Port] = {} + pins: Dict[str, Port] = {} + + +if __name__ == "__main__": + with open(sys.argv[1], "rb") as f: + wrapper = tomli.load(f) + + try: + # Validate with Pydantic + wrap = ExternalWrap.model_validate(wrapper) # Valiate + print(wrap) + + vloc = Path() + if wrap.files.module: + vloc = Path(wrap.files.module.data_location) + elif wrap.files.path: + vloc = path + else: + assert True + + if wrap.generate: + wrap.generate.generator.generate(vloc, wrap.generate.defaults, wrap.generate.options) + + + except ValidationError as e: + # Format Pydantic validation errors in a user-friendly way + error_messages = [] + for error in e.errors(): + location = ".".join(str(loc) for loc in error["loc"]) + message = error["msg"] + error_messages.append(f"Error at '{location}': {message}") + + error_str = "\n".join(error_messages) + raise ChipFlowError(f"Validation error in chipflow.toml:\n{error_str}") + + + diff --git a/chipflow_digital_ip/io/usb_ohci.toml b/chipflow_digital_ip/io/usb_ohci.toml new file mode 100644 index 0000000..5ff9d39 --- /dev/null +++ b/chipflow_digital_ip/io/usb_ohci.toml @@ -0,0 +1,80 @@ +[files] +module = 'pythondata_misc_usb_ohci' + +[generate] +parameters = ['nusb', 'dma_data_width', 'usb_clk_freq'] +defaults.nusb = 1 +defaults.dma_data_width = 32 +defaults.usb_clk_freq = 48e6 + +generator = 'spinalhdl' +options = [ '--port-count={nusb}', + '--phy-frequency={usb_clk_freq:.0f}', + '--dma-width={dma_data_width}', + ] + +[clocks] +usb = 'i_phy_clk' +sys = 'i_ctrl_clk' + +[resets] +usb = 'i_phy_reset' +sys = 'i_ctrl_reset' + +[ports.wb_ctl] +interface = 'amaranth_soc.wishbone.Interface' + +[ports.wb_ctl.params] +data_width=32 +address_width=32 +addressing='word' + +[ports.wb_ctl.map] +cyc = 'i_io_ctrl_CYC' +stb = 'i_io_ctrl_STB' +ack = 'o_io_ctrl_ACK' +we = 'i_io_ctrl_WE' +adr = 'i_io_ctrl_ADR' +dat.r = 'o_io_ctrl_DAT_MISO' +dat.w = 'i_io_ctrl_DAT_MOSI' +sel = 'i_io_ctrl_SEL' + +[ports.wb_dma] +interface = 'amaranth_soc.wishbone.Interface' + +[ports.wb_dma.params] +data_width=32 +address_width='{dma_data_width}' +addressing='word' + +[ports.wb_dma.map] +cyc = 'o_io_dma_CYC' +stb = 'o_io_dma_STB' +ack = 'i_io_dma_ACK' +we = 'o_io_dma_WE' +adr = 'o_io_dma_ADR' +dat.r = 'i_io_dma_DAT_MISO' +dat.w = 'o_io_dma_DAT_MOSI' +sel = 'o_io_dma_SEL' +err = 'i_io_dma_ERR' +cti = 'o_io_dma_CTI' +bte = 'o_io_dma_BTE' + +[ports.interrupt] +interface = 'amaranth.lib.wiring.Out(1)' +map = 'o_io_interrupt' + +[pins.usb] +interface = 'chipflow_lib.platforms.I2CSignature' + +[pins.usb.vars] +n = 'int' + +[pins.usb.map] +dp.i = 'i_io_usb_{n}_dp_read' +dp.o = 'o_io_usb_{n}_dp_write' +dp.oe = 'o_io_usb_{n}_dp_writeEnable' +dm.i = 'i_io_usb_{n}_dm_read' +dm.o = 'o_io_usb_{n}_dm_write' +dm.oe = 'o_io_usb_{n}_dm_writeEnable' + diff --git a/pyproject.toml b/pyproject.toml index 4feedd0..a02dfc6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,10 +16,11 @@ license-files = [ requires-python = ">=3.11" dependencies = [ "amaranth>=0.5,<0.6", - "chipflow-lib @ git+https://github.com/ChipFlow/chipflow-lib.git", + "chipflow @ git+https://github.com/ChipFlow/chipflow-lib.git", "amaranth-soc @ git+https://github.com/amaranth-lang/amaranth-soc", "amaranth-stdio @ git+https://github.com/amaranth-lang/amaranth-stdio", "minerva @ git+https://github.com/minerva-cpu/minerva", + "pythondata-misc-usb_ohci @ git+https://github.com/robtaylor/pythondata-misc-usb_ohci@update-spinalhdl", ] # Build system configuration From 86e795ec4743c5fca5f89f2ecffae829aff4932f Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Sun, 9 Nov 2025 18:52:41 +0000 Subject: [PATCH 4/8] Sort out typing --- chipflow_digital_ip/io/_glasgow_i2c.py | 2 +- chipflow_digital_ip/io/_glasgow_iostream.py | 14 ++++--- chipflow_digital_ip/io/_gpio.py | 1 - chipflow_digital_ip/io/_rfc_uart.py | 43 +++++++++++++-------- pyproject.toml | 6 ++- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/chipflow_digital_ip/io/_glasgow_i2c.py b/chipflow_digital_ip/io/_glasgow_i2c.py index c1419ef..1ff7f70 100644 --- a/chipflow_digital_ip/io/_glasgow_i2c.py +++ b/chipflow_digital_ip/io/_glasgow_i2c.py @@ -1,4 +1,4 @@ -from amaranth import * +from amaranth import Clock, Signal, In, Out, Module, Cat, C, Elaboratable from amaranth.lib.cdc import FFSynchronizer diff --git a/chipflow_digital_ip/io/_glasgow_iostream.py b/chipflow_digital_ip/io/_glasgow_iostream.py index cd3c4a3..0182b11 100644 --- a/chipflow_digital_ip/io/_glasgow_iostream.py +++ b/chipflow_digital_ip/io/_glasgow_iostream.py @@ -1,4 +1,6 @@ -from amaranth import * +from typing import Any + +from amaranth import In, Out, Module, Clock, Signal, Cat, ClockDomain, ClockSignal, ResetDomain, Shape, Array from amaranth.lib import data, wiring, stream, io from amaranth.lib.wiring import In, Out @@ -137,8 +139,8 @@ def i_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): "meta": meta_layout, })) - def __init__(self, ioshape, ports, /, *, ratio=1, init=None, meta_layout=0): - assert isinstance(ioshape, (int, dict)) + def __init__(self, ioshape: dict, ports, /, *, ratio=1, init=None, meta_layout=0): + assert isinstance(ioshape, dict) assert ratio in (1, 2) self._ioshape = ioshape @@ -161,7 +163,7 @@ def elaborate(self, platform): buffer_cls, latency = SimulatableDDRBuffer, 3 if isinstance(self._ports, io.PortLike): - m.submodules.buffer = buffer = buffer_cls("io", self._ports) + m.submodules.buffer = buffer = buffer_cls(io.Direction.Bidir, self._ports) if isinstance(self._ports, PortGroup): buffer = {} for name, sub_port in self._ports.__dict__.items(): @@ -231,7 +233,7 @@ def delay(value, name): class IOClocker(wiring.Component): @staticmethod - def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout=0): + def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout:Any=0): # Currently the only supported ratio is 1, but this will change in the future for # interfaces like HyperBus. return stream.Signature(data.StructLayout({ @@ -245,7 +247,7 @@ def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout=0): })) @staticmethod - def o_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): + def o_stream_signature(ioshape, /, *, ratio=1, meta_layout:Any=0): return IOStreamer.o_stream_signature(ioshape, ratio=ratio, meta_layout=meta_layout) def __init__(self, ioshape, *, clock, o_ratio=1, meta_layout=0, divisor_width=16): diff --git a/chipflow_digital_ip/io/_gpio.py b/chipflow_digital_ip/io/_gpio.py index 79f0f22..ba5d056 100644 --- a/chipflow_digital_ip/io/_gpio.py +++ b/chipflow_digital_ip/io/_gpio.py @@ -1,4 +1,3 @@ - from amaranth import Module, unsigned from amaranth.lib import wiring from amaranth.lib.wiring import In, Out, flipped, connect diff --git a/chipflow_digital_ip/io/_rfc_uart.py b/chipflow_digital_ip/io/_rfc_uart.py index e08f13e..337d8a8 100644 --- a/chipflow_digital_ip/io/_rfc_uart.py +++ b/chipflow_digital_ip/io/_rfc_uart.py @@ -2,17 +2,26 @@ The Amaranth SoC RFC UART from https://github.com/ChipFlow/chipflow-digital-ip """ +from typing import Generic, TypeVar + from amaranth import * from amaranth.lib import stream, wiring from amaranth.lib.wiring import In, Out, flipped, connect +from amaranth.hdl import ValueCastable + +from amaranth_types.types import HasElaborate, ShapeLike, ValueLike from amaranth_soc import csr __all__ = ["RxPhySignature", "TxPhySignature", "RxPeripheral", "TxPeripheral", "Peripheral"] +_T_ValueOrValueCastable = TypeVar("_T_ValueOrValueCastable", bound=Value | ValueCastable, covariant=True) +_T_ShapeLike = TypeVar("_T_ShapeLike", bound=ShapeLike, covariant=True) +_T_Symbol_ShapeLike = TypeVar("_T_Symbol_ShapeLike", bound=ShapeLike, covariant=True) + -class RxPhySignature(wiring.Signature): +class RxPhySignature(wiring.Signature, Generic[_T_ShapeLike, _T_Symbol_ShapeLike]): """Receiver PHY signature. Parameters @@ -38,7 +47,7 @@ class RxPhySignature(wiring.Signature): Receiver error flag. Pulsed for one clock cycle in case of an implementation-specific error (e.g. wrong parity bit). """ - def __init__(self, phy_config_shape, symbol_shape): + def __init__(self, phy_config_shape: _T_ShapeLike, symbol_shape: _T_Symbol_ShapeLike): super().__init__({ "rst": Out(1), "config": Out(phy_config_shape), @@ -48,7 +57,7 @@ def __init__(self, phy_config_shape, symbol_shape): }) -class TxPhySignature(wiring.Signature): +class TxPhySignature(wiring.Signature, Generic[_T_ShapeLike, _T_Symbol_ShapeLike]): """Transmitter PHY signature. Parameters @@ -68,7 +77,7 @@ class TxPhySignature(wiring.Signature): symbols : :py:`Out(stream.Signature(symbol_shape))` Symbol stream. The shape of its payload is given by the `symbol_shape` parameter. """ - def __init__(self, phy_config_shape, symbol_shape): + def __init__(self, phy_config_shape: _T_ShapeLike, symbol_shape: _T_Symbol_ShapeLike): super().__init__({ "rst": Out(1), "config": Out(phy_config_shape), @@ -98,7 +107,7 @@ def elaborate(self, platform): return m -class RxPeripheral(wiring.Component): +class RxPeripheral(wiring.Component, Generic[_T_ShapeLike, _T_ValueOrValueCastable, _T_Symbol_ShapeLike]): class Config(csr.Register, access="rw"): """Peripheral configuration register. @@ -141,7 +150,7 @@ class PhyConfig(csr.Register, access="rw"): phy_config_init : :class:`int` Initial value of the PHY configuration word. """ - def __init__(self, phy_config_shape, phy_config_init): + def __init__(self, phy_config_shape: _T_ShapeLike, phy_config_init: _T_ValueOrValueCastable): super().__init__(csr.Field(_PhyConfigFieldAction, phy_config_shape, init=phy_config_init)) @@ -199,7 +208,7 @@ class Data(csr.Register, access="r"): symbol_shape : :ref:`shape-like ` Shape of a symbol. """ - def __init__(self, symbol_shape): + def __init__(self, symbol_shape: _T_Symbol_ShapeLike): super().__init__(csr.Field(csr.action.R, symbol_shape)) """UART receiver peripheral. @@ -224,8 +233,8 @@ def __init__(self, symbol_shape): phy : :py:`Out(RxPhySignature(phy_config_shape, symbol_shape))` Interface between the peripheral and its PHY. """ - def __init__(self, *, addr_width, data_width, phy_config_shape=unsigned(16), - phy_config_init=0, symbol_shape=unsigned(8)): + def __init__(self, *, addr_width, data_width, phy_config_shape:_T_ShapeLike = unsigned(16), + phy_config_init: _T_ValueOrValueCastable = Value.cast(0), symbol_shape: _T_Symbol_ShapeLike = unsigned(8)): regs = csr.Builder(addr_width=addr_width, data_width=data_width) self._config = regs.add("Config", self.Config()) @@ -298,7 +307,7 @@ def elaborate(self, platform): return m -class TxPeripheral(wiring.Component): +class TxPeripheral(wiring.Component, Generic[_T_ShapeLike, _T_Symbol_ShapeLike, _T_ValueOrValueCastable]): class Config(csr.Register, access="rw"): """Peripheral configuration register. @@ -341,7 +350,7 @@ class PhyConfig(csr.Register, access="rw"): phy_config_init : :class:`int` Initial value of the PHY configuration word. """ - def __init__(self, phy_config_shape, phy_config_init): + def __init__(self, phy_config_shape: _T_ShapeLike, phy_config_init: _T_ValueOrValueCastable): super().__init__(csr.Field(_PhyConfigFieldAction, phy_config_shape, init=phy_config_init)) @@ -391,7 +400,7 @@ class Data(csr.Register, access="w"): symbol_shape : :ref:`shape-like ` Shape of a symbol. """ - def __init__(self, symbol_shape): + def __init__(self, symbol_shape: _T_Symbol_ShapeLike): super().__init__(csr.Field(csr.action.W, symbol_shape)) """UART transmitter peripheral. @@ -416,8 +425,8 @@ def __init__(self, symbol_shape): phy : :py:`Out(TxPhySignature(phy_config_shape, symbol_shape))` Interface between the peripheral and its PHY. """ - def __init__(self, *, addr_width, data_width=8, phy_config_shape=unsigned(16), - phy_config_init=0, symbol_shape=unsigned(8)): + def __init__(self, *, addr_width, data_width=8, phy_config_shape: _T_ShapeLike = unsigned(16), + phy_config_init: _T_ValueOrValueCastable = Value.cast(0), symbol_shape: _T_Symbol_ShapeLike = unsigned(8)): regs = csr.Builder(addr_width=addr_width, data_width=data_width) self._config = regs.add("Config", self.Config()) @@ -487,7 +496,7 @@ def elaborate(self, platform): return m -class Peripheral(wiring.Component): +class Peripheral(wiring.Component, Generic[_T_ShapeLike, _T_Symbol_ShapeLike, _T_ValueOrValueCastable]): """UART transceiver peripheral. This peripheral is composed of two subordinate peripherals. A :class:`RxPeripheral` occupies @@ -522,8 +531,8 @@ class Peripheral(wiring.Component): :exc:`TypeError` If ``addr_width`` is not a positive integer. """ - def __init__(self, *, addr_width, data_width=8, phy_config_shape=unsigned(16), - phy_config_init=0, symbol_shape=unsigned(8)): + def __init__(self, *, addr_width, data_width=8, phy_config_shape: _T_ShapeLike = unsigned(16), + phy_config_init: _T_ValueOrValueCastable = Value.cast(0), symbol_shape: _T_Symbol_ShapeLike = unsigned(8)): if not isinstance(addr_width, int) or addr_width <= 0: raise TypeError(f"Address width must be a positive integer, not {addr_width!r}") diff --git a/pyproject.toml b/pyproject.toml index a02dfc6..383ee6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,10 +49,12 @@ purelib = [ [tool.pyright] diagnosticMode=false -typeCheckingMode = "off" reportInvalidTypeForm = false reportMissingImports = false reportUnboundVariable = false +reportAttributeAccessIssue = false +reportWildcardImportFromLibrary = false +ignore = [ "tests", "vendor" ] [tool.ruff.lint] select = ['E4', 'E7', 'E9', 'F', 'W291', 'W293'] @@ -77,4 +79,6 @@ dev = [ "ruff>=0.9.2", "pytest>=7.2.0", "pytest-cov>=0.6", + "pyright>=1.1.407", + "amaranth-stubs>=0.1.1", ] From 59c4c1c66d2cf349c7139f0b496e5a30a3306133 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Sun, 9 Nov 2025 18:52:55 +0000 Subject: [PATCH 5/8] wip: verilog binding --- chipflow_digital_ip/io/_svtest.py | 64 +++++++++++++++++----------- chipflow_digital_ip/io/usb_ohci.toml | 18 +++++--- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/chipflow_digital_ip/io/_svtest.py b/chipflow_digital_ip/io/_svtest.py index 839e4a1..9f7ba23 100644 --- a/chipflow_digital_ip/io/_svtest.py +++ b/chipflow_digital_ip/io/_svtest.py @@ -4,11 +4,9 @@ from enum import StrEnum, auto from pathlib import Path -from typing import Dict, Optional, Any, List, Annotated, Literal, Self +from typing import Dict, Optional, List, Literal, Self -from amaranth import Module, unsigned from amaranth.lib import wiring -from amaranth.lib.wiring import In, Out, flipped, connect from pydantic import ( BaseModel, ImportString, JsonValue, ValidationError, @@ -30,40 +28,42 @@ def verify_module_or_path(self) -> Self: raise ValueError("You must set `module` or `path`.") return self +class GenerateSpinalHDL(BaseModel): + + scala_class: str + options: List[str] = [] + + def generate(self, source_path: Path, dest_path: Path, name: str, parameters: Dict[str, JsonValue]): + gen_args = [o.format(**parameters) for o in self.options] + path = source_path / "ext" / "SpinalHDL" + args=" ".join(gen_args + [f'--netlist-directory={dest_path.absolute()}', f'--netlist-name={name}']) + cmd = f'cd {path} && sbt "lib/runMain {self.scala_class} {args}"' + print("!!! " + cmd) + if os.system(cmd) != 0: + raise OSError('Failed to run sbt') + return [f'{name}.v'] + class Generators(StrEnum): SPINALHDL = auto() VERILOG = auto() - def generate(self, vdir: Path, parameters: Dict[str, list|dict|str|bool|int|float|None], options: List[str]): - gen_args = [o.format(**parameters) for o in options] - match self.name: - case "SPINALHDL": - cmd = 'cd {path} && sbt "lib/runMain spinal.lib.com.usb.ohci.UsbOhciWishbone {args}"'.format( - path=vdir / "ext" / "SpinalHDL", args=" ".join(gen_args)) - print("!!! " + cmd) - if os.system(cmd) != 0: - raise OSError('Failed to run sbt') - case _ as v: - raise TypeError(f"Undefined generator type: {v}") - - class Generate(BaseModel): - parameters: List[str] = [] - defaults: Optional[Dict[str, JsonValue]] = None + parameters: Optional[Dict[str, JsonValue]] = None generator: Generators - options: List[str] = [] + spinalhdl: Optional[GenerateSpinalHDL] = None class Port(BaseModel): interface: str # ImportString params: Optional[Dict[str, JsonValue]] = None vars: Optional[Dict[str, Literal["int"]]] = None - map: str | Dict[str, Dict[str, str] | str] + map: str | Dict[str, Dict[str, str] | str] class ExternalWrap(BaseModel): + name: str files: Files generate: Optional[Generate] = None clocks: Dict[str, str] = {} @@ -81,18 +81,32 @@ class ExternalWrap(BaseModel): wrap = ExternalWrap.model_validate(wrapper) # Valiate print(wrap) - vloc = Path() + source = Path() if wrap.files.module: - vloc = Path(wrap.files.module.data_location) + source = Path(wrap.files.module.data_location) elif wrap.files.path: - vloc = path + source = wrap.files.path else: assert True if wrap.generate: - wrap.generate.generator.generate(vloc, wrap.generate.defaults, wrap.generate.options) + dest = Path("./build/verilog") + dest.mkdir(parents=True, exist_ok=True) + files = getattr(wrap.generate, wrap.generate.generator.value).generate(source, Path(dest), wrap.name, wrap.generate.parameters) + print(f'Generated files: {files}') + + def init(self, **kwargs): + for name, value in kwargs.items(): + setattr(self, name, value) + + attr = { + '__init__': init + } + #_class = type(wrap.name, wiring.Component, attr) + + + - except ValidationError as e: # Format Pydantic validation errors in a user-friendly way error_messages = [] diff --git a/chipflow_digital_ip/io/usb_ohci.toml b/chipflow_digital_ip/io/usb_ohci.toml index 5ff9d39..081bed7 100644 --- a/chipflow_digital_ip/io/usb_ohci.toml +++ b/chipflow_digital_ip/io/usb_ohci.toml @@ -1,17 +1,21 @@ +name = 'UsbOhciPeripheral' + [files] module = 'pythondata_misc_usb_ohci' [generate] -parameters = ['nusb', 'dma_data_width', 'usb_clk_freq'] -defaults.nusb = 1 -defaults.dma_data_width = 32 -defaults.usb_clk_freq = 48e6 +parameters.nusb = 1 +parameters.dma_data_width = 32 +parameters.usb_clk_freq = 48e6 generator = 'spinalhdl' -options = [ '--port-count={nusb}', - '--phy-frequency={usb_clk_freq:.0f}', - '--dma-width={dma_data_width}', + +[generate.spinalhdl] +options = [ '--port-count {nusb}', + '--phy-frequency {usb_clk_freq:.0f}', + '--dma-width {dma_data_width}', ] +scala_class = 'spinal.lib.com.usb.ohci.UsbOhciWishbone' [clocks] usb = 'i_phy_clk' From 4f8d5f3e3a8ede20e98db867d56981d83b01af65 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Mon, 10 Nov 2025 17:06:56 +0000 Subject: [PATCH 6/8] More typing --- chipflow_digital_ip/io/_glasgow_iostream.py | 18 ++++++++++-------- chipflow_digital_ip/io/_svtest.py | 2 +- chipflow_digital_ip/io/usb_ohci.toml | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/chipflow_digital_ip/io/_glasgow_iostream.py b/chipflow_digital_ip/io/_glasgow_iostream.py index 0182b11..51174a7 100644 --- a/chipflow_digital_ip/io/_glasgow_iostream.py +++ b/chipflow_digital_ip/io/_glasgow_iostream.py @@ -1,11 +1,13 @@ -from typing import Any - from amaranth import In, Out, Module, Clock, Signal, Cat, ClockDomain, ClockSignal, ResetDomain, Shape, Array from amaranth.lib import data, wiring, stream, io from amaranth.lib.wiring import In, Out +from amaranth_types.types import ShapeLike + + __all__ = ["IOStreamer", "PortGroup"] + class PortGroup: """Group of Amaranth library I/O ports. @@ -120,7 +122,7 @@ class IOStreamer(wiring.Component): """ @staticmethod - def o_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): + def o_stream_signature(ioshape, /, *, ratio=1, meta_layout: ShapeLike = 0): return stream.Signature(data.StructLayout({ "port": _map_ioshape("o", ioshape, lambda width: data.StructLayout({ "o": width if ratio == 1 else data.ArrayLayout(width, ratio), @@ -131,7 +133,7 @@ def o_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): })) @staticmethod - def i_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): + def i_stream_signature(ioshape, /, *, ratio=1, meta_layout: ShapeLike = 0): return stream.Signature(data.StructLayout({ "port": _map_ioshape("i", ioshape, lambda width: data.StructLayout({ "i": width if ratio == 1 else data.ArrayLayout(width, ratio), @@ -139,7 +141,7 @@ def i_stream_signature(ioshape, /, *, ratio=1, meta_layout=0): "meta": meta_layout, })) - def __init__(self, ioshape: dict, ports, /, *, ratio=1, init=None, meta_layout=0): + def __init__(self, ioshape: dict, ports, /, *, ratio=1, init=None, meta_layout: ShapeLike = 0): assert isinstance(ioshape, dict) assert ratio in (1, 2) @@ -233,7 +235,7 @@ def delay(value, name): class IOClocker(wiring.Component): @staticmethod - def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout:Any=0): + def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout: ShapeLike = 0): # Currently the only supported ratio is 1, but this will change in the future for # interfaces like HyperBus. return stream.Signature(data.StructLayout({ @@ -247,10 +249,10 @@ def i_stream_signature(ioshape, /, *, _ratio=1, meta_layout:Any=0): })) @staticmethod - def o_stream_signature(ioshape, /, *, ratio=1, meta_layout:Any=0): + def o_stream_signature(ioshape, /, *, ratio=1, meta_layout: ShapeLike = 0): return IOStreamer.o_stream_signature(ioshape, ratio=ratio, meta_layout=meta_layout) - def __init__(self, ioshape, *, clock, o_ratio=1, meta_layout=0, divisor_width=16): + def __init__(self, ioshape, *, clock, o_ratio=1, meta_layout: ShapeLike = 0, divisor_width=16): assert isinstance(ioshape, dict) assert isinstance(clock, str) assert o_ratio in (1, 2) diff --git a/chipflow_digital_ip/io/_svtest.py b/chipflow_digital_ip/io/_svtest.py index 9f7ba23..d1b2c07 100644 --- a/chipflow_digital_ip/io/_svtest.py +++ b/chipflow_digital_ip/io/_svtest.py @@ -102,7 +102,7 @@ def init(self, **kwargs): attr = { '__init__': init } - #_class = type(wrap.name, wiring.Component, attr) + _class = type(wrap.name, (wiring.Component,), attr) diff --git a/chipflow_digital_ip/io/usb_ohci.toml b/chipflow_digital_ip/io/usb_ohci.toml index 081bed7..bcf7b6c 100644 --- a/chipflow_digital_ip/io/usb_ohci.toml +++ b/chipflow_digital_ip/io/usb_ohci.toml @@ -82,3 +82,18 @@ dm.i = 'i_io_usb_{n}_dm_read' dm.o = 'o_io_usb_{n}_dm_write' dm.oe = 'o_io_usb_{n}_dm_writeEnable' +[drivers.uboot] +CONFIG_USB_OHCI_NEW = true +CONFIG_SYS_USB_OHCI_REGS_BASE = "{regs_base}" + +[drivers.zephyr] +CONFIG_UHC_NXP_OHCI = true + +[drivers.dtsi] +# reg, interrupts, enabled automatically added +compatible = "nxp,uhc-ohci"; +maximum-speed = "full-speed"; + +[drivers.raw] +c_files = ['drivers/ohci_generic.c', 'drivers/ohci_hcd.c'] +h_files = ['ohci.h'] From 7ace3ccf178e883a22283324e724e09b80a1e0a2 Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Mon, 10 Nov 2025 17:11:47 +0000 Subject: [PATCH 7/8] mergeme: typing --- chipflow_digital_ip/io/_glasgow_i2c.py | 2 +- chipflow_digital_ip/io/_glasgow_iostream.py | 2 +- chipflow_digital_ip/io/_spi.py | 2 +- chipflow_digital_ip/io/_svtest.py | 1 + pyproject.toml | 1 - 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chipflow_digital_ip/io/_glasgow_i2c.py b/chipflow_digital_ip/io/_glasgow_i2c.py index 1ff7f70..8fd746f 100644 --- a/chipflow_digital_ip/io/_glasgow_i2c.py +++ b/chipflow_digital_ip/io/_glasgow_i2c.py @@ -1,4 +1,4 @@ -from amaranth import Clock, Signal, In, Out, Module, Cat, C, Elaboratable +from amaranth import * from amaranth.lib.cdc import FFSynchronizer diff --git a/chipflow_digital_ip/io/_glasgow_iostream.py b/chipflow_digital_ip/io/_glasgow_iostream.py index 51174a7..cca7561 100644 --- a/chipflow_digital_ip/io/_glasgow_iostream.py +++ b/chipflow_digital_ip/io/_glasgow_iostream.py @@ -1,4 +1,4 @@ -from amaranth import In, Out, Module, Clock, Signal, Cat, ClockDomain, ClockSignal, ResetDomain, Shape, Array +from amaranth import * from amaranth.lib import data, wiring, stream, io from amaranth.lib.wiring import In, Out diff --git a/chipflow_digital_ip/io/_spi.py b/chipflow_digital_ip/io/_spi.py index e0c890a..2cc20b3 100644 --- a/chipflow_digital_ip/io/_spi.py +++ b/chipflow_digital_ip/io/_spi.py @@ -1,4 +1,4 @@ -from amaranth import Module, Signal, Cat, C, unsigned +from amaranth import * from amaranth.lib import wiring from amaranth.lib.wiring import In, Out, connect, flipped diff --git a/chipflow_digital_ip/io/_svtest.py b/chipflow_digital_ip/io/_svtest.py index d1b2c07..09916ad 100644 --- a/chipflow_digital_ip/io/_svtest.py +++ b/chipflow_digital_ip/io/_svtest.py @@ -28,6 +28,7 @@ def verify_module_or_path(self) -> Self: raise ValueError("You must set `module` or `path`.") return self + class GenerateSpinalHDL(BaseModel): scala_class: str diff --git a/pyproject.toml b/pyproject.toml index 383ee6f..5b220bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ purelib = [ # Development workflow configuration [tool.pyright] -diagnosticMode=false reportInvalidTypeForm = false reportMissingImports = false reportUnboundVariable = false From 654122bb43f11d6e8af0997e51aefcbf72eff40c Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Wed, 12 Nov 2025 16:05:54 +0000 Subject: [PATCH 8/8] wip: more autobinding --- chipflow_digital_ip/io/_svtest.py | 3 ++- chipflow_digital_ip/io/usb_ohci.toml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/chipflow_digital_ip/io/_svtest.py b/chipflow_digital_ip/io/_svtest.py index 09916ad..5e23108 100644 --- a/chipflow_digital_ip/io/_svtest.py +++ b/chipflow_digital_ip/io/_svtest.py @@ -38,7 +38,8 @@ def generate(self, source_path: Path, dest_path: Path, name: str, parameters: Di gen_args = [o.format(**parameters) for o in self.options] path = source_path / "ext" / "SpinalHDL" args=" ".join(gen_args + [f'--netlist-directory={dest_path.absolute()}', f'--netlist-name={name}']) - cmd = f'cd {path} && sbt "lib/runMain {self.scala_class} {args}"' + cmd = f'cd {path} && sbt -J--enable-native-access=ALL-UNNAMED -v "lib/runMain {self.scala_class} {args}"' + os.environ["GRADLE_OPTS"] = "--enable-native-access=ALL-UNNAMED" print("!!! " + cmd) if os.system(cmd) != 0: raise OSError('Failed to run sbt') diff --git a/chipflow_digital_ip/io/usb_ohci.toml b/chipflow_digital_ip/io/usb_ohci.toml index bcf7b6c..65b9ff3 100644 --- a/chipflow_digital_ip/io/usb_ohci.toml +++ b/chipflow_digital_ip/io/usb_ohci.toml @@ -91,8 +91,8 @@ CONFIG_UHC_NXP_OHCI = true [drivers.dtsi] # reg, interrupts, enabled automatically added -compatible = "nxp,uhc-ohci"; -maximum-speed = "full-speed"; +compatible = "nxp,uhc-ohci" +maximum-speed = "full-speed" [drivers.raw] c_files = ['drivers/ohci_generic.c', 'drivers/ohci_hcd.c']