diff --git a/chipflow_lib/pin_lock.py b/chipflow_lib/pin_lock.py index 934f7ad4..a88b961a 100644 --- a/chipflow_lib/pin_lock.py +++ b/chipflow_lib/pin_lock.py @@ -45,9 +45,11 @@ def allocate_pins(name: str, member: Dict[str, Any], pins: List[str]) -> Tuple[D name = name sig = member['annotations'][PIN_ANNOTATION_SCHEMA] width = sig['width'] + options = sig['options'] pin_map[name] = {'pins': pins[0:width], 'direction': sig['direction'], - 'type': 'io'} + 'type': 'io', + 'options': options} logger.debug(f"added '{name}':{pin_map[name]} to pin_map") return pin_map, pins[width:] elif member['type'] == 'interface': diff --git a/chipflow_lib/platforms/silicon.py b/chipflow_lib/platforms/silicon.py index 1a32624f..bec848ba 100644 --- a/chipflow_lib/platforms/silicon.py +++ b/chipflow_lib/platforms/silicon.py @@ -70,13 +70,19 @@ def __init__(self, invert: bool = False): self._direction = io.Direction(port.direction) self._invert = invert + self._options = port.options self._i = self._o = self._oe = Signal(1) if self._direction in (io.Direction.Input, io.Direction.Bidir): self._i = Signal(port.width, name=f"{component}_{name}__i") if self._direction in (io.Direction.Output, io.Direction.Bidir): self._o = Signal(port.width, name=f"{component}_{name}__o") - self._oe = Signal(1, name=f"{component}_{name}__oe") + if self._direction is io.Direction.Bidir: + if "all_have_oe" in self._options and self._options["all_have_oe"]: + self._oe = Signal(port.width, name=f"{component}_{name}__oe") + else: + self._oe = Signal(1, name=f"{component}_{name}__oe") + self._pins = port.pins logger.debug(f"Created SiliconPlatformPort {name}, width={len(port.pins)},dir{self._direction}") @@ -120,7 +126,11 @@ def __len__(self): assert len(self._o) == len(self._oe) return len(self._o) if self._direction is io.Direction.Bidir: - assert len(self._i) == len(self._o) == len(self._oe) + assert len(self._i) == len(self._o) + if self._options["all_have_oe"]: + assert len(self.o) == len(self._oe) + else: + assert len(self._oe) == 1 return len(self._i) assert False # :nocov: diff --git a/chipflow_lib/platforms/utils.py b/chipflow_lib/platforms/utils.py index c8bcbe6b..02502af5 100644 --- a/chipflow_lib/platforms/utils.py +++ b/chipflow_lib/platforms/utils.py @@ -33,6 +33,7 @@ class _PinAnnotationModel(BaseModel): model_config = ConfigDict(use_enum_values=True) direction: io.Direction width: int + options: dict = {} @classmethod def _annotation_schema(cls): @@ -65,10 +66,15 @@ def as_json(self): # type: ignore class PinSignature(wiring.Signature): """Amaranth Signtaure used to decorate wires that would - usually be brought out onto a pin on the package. + usually be brought out onto a port on the package. + + direction: Input, Output or Bidir + width: width of port + all_have_oe: For Bidir ports, should Output Enable be per wire or for the whole port + init: a :ref:`const-castable object ` for the initial values of the port """ - def __init__(self, direction, width=1, init=None): + def __init__(self, direction: io.Direction, width: int = 1, all_have_oe: bool = False, init = None): self._direction = direction self._width = width self._init = init @@ -76,7 +82,7 @@ def __init__(self, direction, width=1, init=None): case io.Direction.Bidir: sig = { "o": Out(width), - "oe": Out(1), + "oe": Out(width if all_have_oe else 1), "i": In(width) } case io.Direction.Input: @@ -85,16 +91,21 @@ def __init__(self, direction, width=1, init=None): sig = {"o": Out(width)} case _: assert False + self._options = { + "all_have_oe": all_have_oe, + "init": init, + } super().__init__(sig) def annotations(self, *args): annotations = wiring.Signature.annotations(self, *args) - pin_annotation = _PinAnnotation(direction=self._direction, width=self._width) + pin_annotation = _PinAnnotation(direction=self._direction, width=self._width, options=self._options) return annotations + (pin_annotation,) def __repr__(self): - return f"PinSignature({self._direction}, {self._width})" + opts = ', '.join(f"{k}={v}" for k, v in self._options.items()) + return f"PinSignature({self._direction}, {self._width}, {opts})" def OutputPinSignature(width, **kwargs): @@ -297,6 +308,7 @@ class Port(pydantic.BaseModel): type: str pins: List[str] direction: Optional[str] = None + options: Optional[dict] = None @property def width(self): diff --git a/chipflow_lib/steps/silicon.py b/chipflow_lib/steps/silicon.py index a9031bfa..a59ac25e 100644 --- a/chipflow_lib/steps/silicon.py +++ b/chipflow_lib/steps/silicon.py @@ -12,7 +12,7 @@ import time import dotenv - +from pprint import pformat from amaranth import * from .. import ChipFlowError @@ -65,15 +65,15 @@ def build_cli_parser(self, parser): def run_cli(self, args): if args.action == "submit" and not args.dry_run: + dotenv.load_dotenv() if self.project_name is None: raise ChipFlowError( - "Key `chipflow.project_id` is not defined in chipflow.toml; " - "see https://chipflow.io/beta for details on how to join the beta") - if ("CHIPFLOW_API_KEY_ID" not in os.environ or - "CHIPFLOW_API_KEY_SECRET" not in os.environ): + "Key `chipflow.project_name` is not defined in chipflow.toml") + if "CHIPFLOW_API_KEY" not in os.environ: raise ChipFlowError( - "Environment variables `CHIPFLOW_API_KEY_ID` and `CHIPFLOW_API_KEY_SECRET` " - "must be set in order to submit a design") + "Environment variable `CHIPFLOW_API_KEY` " + "must be set in order to submit a design. " + "You can set this in a `.env` file in your project root.") rtlil_path = self.prepare() # always prepare before submission if args.action == "submit": @@ -89,7 +89,6 @@ def prepare(self): def submit(self, rtlil_path, *, dry_run=False): """Submit the design to the ChipFlow cloud builder. """ - dotenv.load_dotenv() git_head = subprocess.check_output( ["git", "-C", os.environ["CHIPFLOW_ROOT"], "rev-parse", "HEAD"], @@ -124,13 +123,13 @@ def submit(self, rtlil_path, *, dry_run=False): pads = {} for iface, port in self.platform._ports.items(): width = len(port.pins) - print(f"iface={iface}, port={port}, dir={port.direction}, width={width}") + logger.debug(f"iface={iface}, port={port}, dir={port.direction}, width={width}") if width > 1: for i in range(width): padname = f"{iface}{i}" - print(f"padname={padname}, port={port}, loc={port.pins[i:i+1]}, " + logger.debug(f"padname={padname}, port={port}, loc={port.pins[i]}, " f"dir={port.direction}, width={width}") - pads[padname] = {'loc': port.pins[i:i+1], 'dir': port.direction} + pads[padname] = {'loc': port.pins[i], 'dir': port.direction.value} config = { "dependency_versions": dep_versions, @@ -149,32 +148,14 @@ def submit(self, rtlil_path, *, dry_run=False): logger.info(f"Submitting {submission_name} for project {self.project_name}") resp = requests.post( - os.environ.get("CHIPFLOW_API_ENDPOINT", "https://app.chipflow-infra.com/api/builds"), - auth=(os.environ["CHIPFLOW_API_KEY_ID"], os.environ["CHIPFLOW_API_KEY_SECRET"]), + os.environ.get("CHIPFLOW_API_ENDPOINT", "https://build.chipflow.org/api/builds"), + auth=os.environ["CHIPFLOW_API_KEY"], data=data, files={ "rtlil": open(rtlil_path, "rb"), "config": json.dumps(config), }) resp_data = resp.json() - if resp.status_code == 403: - raise ChipFlowError( - "Authentication failed; please verify the values of the the CHIPFLOW_API_KEY_ID " - "and CHIPFLOW_API_KEY_SECRET environment variables, if the issue persists, " - "contact support to resolve it") - elif resp.status_code >= 400: - raise ChipFlowError( - f"Submission failed ({resp_data['statusCode']} {resp_data['error']}: " - f"{resp_data['message']}); please contact support and provide this error message") - elif resp.status_code >= 300: - assert False, "3xx responses should not be returned" - elif resp.status_code >= 200: - if not resp_data["ok"]: - raise ChipFlowError( - f"Submission failed ({resp_data['msg']}); please contact support and provide " - f"this error message") - else: - print(f"{resp_data['msg']} (#{resp_data['id']}: {resp_data['name']}); " - f"{resp_data['url']}") - else: - ChipFlowError(f"Unexpected response from API: {resp}") + print(resp_data) + if resp.status_code != 200: + raise ChipFlowError(f"Failed to submit: {resp_data}")