Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chipflow_lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ def _parse_config() -> 'Config':
except FileNotFoundError:
raise ChipFlowError(f"Config file not found. I expected to find it at {config_file}")
except tomli.TOMLDecodeError as e:
raise ChipFlowError(f"TOML Error found when loading {config_file}: {e.msg} at line {e.lineno}, column {e.colno}")
raise ChipFlowError(f"{config_file} has a formatting error: {e.msg} at line {e.lineno}, column {e.colno}")
18 changes: 18 additions & 0 deletions chipflow_lib/_doit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 *-*
# SPDX-License-Identifier: BSD-2-Clause

from typing import List, Tuple, Callable, TypeVar, Generic
from typing_extensions import TypedDict, NotRequired

T=TypeVar('T')
class TaskParams(TypedDict, Generic[T]):
name: str
default: T
short: NotRequired[str]
long: NotRequired[str]
type: NotRequired[Callable[[T], str]]
choices: NotRequired[List[Tuple[str, str]]]
help: NotRequired[str]
inverse: NotRequired[str]


3 changes: 2 additions & 1 deletion chipflow_lib/_pin_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from pprint import pformat

from . import _parse_config, _ensure_chipflow_root, ChipFlowError
from .platforms._internal import top_components, LockFile, PACKAGE_DEFINITIONS
from .platforms._utils import top_components, LockFile
from .platforms._packages import PACKAGE_DEFINITIONS

# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logger = logging.getLogger(__name__)
Expand Down
20 changes: 13 additions & 7 deletions chipflow_lib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class UnexpectedError(ChipFlowError):
DEFAULT_STEPS = {
"silicon": "chipflow_lib.steps.silicon:SiliconStep",
"sim": "chipflow_lib.steps.sim:SimStep",
"software": "chipflow_lib.steps.software:SoftwareStep"
}


Expand All @@ -36,13 +37,16 @@ def run(argv=sys.argv[1:]):

if config.chipflow.steps:
steps = DEFAULT_STEPS |config.chipflow.steps
for step_name, step_reference in steps.items():
step_cls = _get_cls_by_reference(step_reference, context=f"step `{step_name}`")
try:
commands[step_name] = step_cls(config)
except Exception:
raise ChipFlowError(f"Encountered error while initializing step `{step_name}` "
f"using `{step_reference}`")
else:
steps = DEFAULT_STEPS

for step_name, step_reference in steps.items():
step_cls = _get_cls_by_reference(step_reference, context=f"step `{step_name}`")
try:
commands[step_name] = step_cls(config)
except Exception:
raise ChipFlowError(f"Encountered error while initializing step `{step_name}` "
f"using `{step_reference}`")

parser = argparse.ArgumentParser(
prog="chipflow",
Expand Down Expand Up @@ -109,4 +113,6 @@ def run(argv=sys.argv[1:]):
if hasattr(args, "action"):
cmd += f" {args.action}"
print(f"Error while executing `{cmd}`: {e}")
print("Caused by:")
traceback.print_exception(e.__cause__)
exit(1)
53 changes: 47 additions & 6 deletions chipflow_lib/config_models.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,47 @@
# SPDX-License-Identifier: BSD-2-Clause
from typing import Dict, Optional, Any, List
from enum import Enum
from typing import Dict, Optional, Any, List, Annotated

from pydantic import BaseModel
from pydantic import (
BaseModel, PlainSerializer, WrapValidator
)

from .platforms._internal import PACKAGE_DEFINITIONS, Process, Voltage
from ._appresponse import AppResponseModel, OmitIfNone

class Process(Enum):
"""
IC manufacturing process
"""
#: Skywater foundry open-source 130nm process
SKY130 = "sky130"
#: GlobalFoundries open-source 130nm process
GF180 = "gf180"
#: Pragmatic Semiconductor FlexIC process (old)
HELVELLYN2 = "helvellyn2"
#: GlobalFoundries 130nm BCD process
GF130BCD = "gf130bcd"
#: IHP open source 130nm SiGe Bi-CMOS process
IHP_SG13G2 = "ihp_sg13g2"

def known_package(package: str):
if package not in PACKAGE_DEFINITIONS.keys():
raise ValueError(f"{package} is not a valid package type. Valid package types are {PACKAGE_DEFINITIONS.keys()}")
def __str__(self):
return f'{self.value}'



Voltage = Annotated[
float,
PlainSerializer(lambda x: f'{x:.1e}V', return_type=str),
WrapValidator(lambda v, h: h(v.strip('Vv ') if isinstance(v, str) else h(v)))
]


class VoltageRange(AppResponseModel):
"""
Models a voltage range for a power domain or IO
"""
min: Annotated[Optional[Voltage], OmitIfNone()] = None
max: Annotated[Optional[Voltage], OmitIfNone()] = None
typical: Annotated[Optional[Voltage], OmitIfNone()] = None


class SiliconConfig(BaseModel):
Expand All @@ -22,13 +55,21 @@ class SiliconConfig(BaseModel):
class SimulationConfig(BaseModel):
num_steps: int = 3000000

class CompilerConfig(BaseModel):
cpu: str
abi: str

class SoftwareConfig(BaseModel):
riscv: CompilerConfig = CompilerConfig(cpu="baseline_rv32-a-c-d", abi="ilp32")

class ChipFlowConfig(BaseModel):
"""Root configuration for chipflow.toml."""
project_name: str
top: Dict[str, Any] = {}
steps: Optional[Dict[str, str]] = None
silicon: Optional[SiliconConfig] = None
simulation: SimulationConfig = SimulationConfig()
software: SoftwareConfig = SoftwareConfig()
clock_domains: Optional[List[str]] = None


Expand Down
7 changes: 4 additions & 3 deletions chipflow_lib/platforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .silicon import SiliconPlatformPort, SiliconPlatform
from .sim import SimPlatform
from ._software import SoftwarePlatform
from ._utils import (
IO_ANNOTATION_SCHEMA, IOSignature, IOModel, IOTripPoint, IOModelOptions,
OutputIOSignature, InputIOSignature, BidirIOSignature,
Expand All @@ -16,15 +17,15 @@
from ._sky130 import Sky130DriveMode
from ._signatures import (
JTAGSignature, SPISignature, I2CSignature, UARTSignature, GPIOSignature, QSPIFlashSignature,
attach_simulation_data
attach_data, SoftwareDriverSignature, SoftwareBuild
)

__all__ = ['IO_ANNOTATION_SCHEMA', 'IOSignature',
'IOModel', 'IOModelOptions', 'IOTripPoint',
'OutputIOSignature', 'InputIOSignature', 'BidirIOSignature',
'SiliconPlatformPort', 'SiliconPlatform',
'SimPlatform',
'SimPlatform', 'SoftwarePlatform',
'JTAGSignature', 'SPISignature', 'I2CSignature', 'UARTSignature', 'GPIOSignature', 'QSPIFlashSignature',
'attach_simulation_data',
'attach_data', 'SoftwareDriverSignature', 'SoftwareBuild',
'Sky130DriveMode',
'PACKAGE_DEFINITIONS']
80 changes: 61 additions & 19 deletions chipflow_lib/platforms/_annotate.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,70 @@

from collections.abc import Generator
from types import MethodType
import pydantic
from typing import TypeVar
from typing import (
Tuple, TypeVar,
)
from typing_extensions import is_typeddict
_T_TypedDict = TypeVar('_T_TypedDict')

def amaranth_annotate(modeltype: type['_T_TypedDict'], schema_id: str, member='__chipflow_annotation__', decorate_object = False):
import pydantic
from amaranth import Fragment
from amaranth.lib import meta, wiring


_T_TypedDict = TypeVar('_T_TypedDict')
def amaranth_annotate(modeltype: type[_T_TypedDict], schema_id: str, member: str = '__chipflow_annotation__', decorate_object = False):
# a bit of nastyness as can't set TypedDict as a bound yet
if not is_typeddict(modeltype):
raise TypeError(f'''amaranth_annotate must be passed a TypedDict, not {modeltype}''')
raise TypeError(f"amaranth_annotate must be passed a TypedDict, not {modeltype}")

# interesting pydantic issue gets hit if arbitrary_types_allowed is False
if hasattr(modeltype, '__pydantic_config__'):
config = getattr(modeltype, '__pydantic_config__')
config: pydantic.ConfigDict = getattr(modeltype, '__pydantic_config__')
config['arbitrary_types_allowed'] = True
else:
config = pydantic.ConfigDict()
config['arbitrary_types_allowed'] = True
setattr(modeltype, '__pydantic_config__', config)

PydanticModel = pydantic.TypeAdapter(modeltype)

def annotation_schema():
schema = PydanticModel.json_schema()
schema['$schema'] = 'https://json-schema.org/draft/2020-12/schema'
schema['$schema'] = "https://json-schema.org/draft/2020-12/schema"
schema['$id'] = schema_id
return schema

class Annotation:
'Generated annotation class'
class Annotation(meta.Annotation):
"Generated annotation class"
schema = annotation_schema()

def __init__(self, parent):
self.parent = parent

def origin(self):
@property
def origin(self): # type: ignore
return self.parent

def as_json(self):
return PydanticModel.dump_python(getattr(self.parent, member))
def as_json(self): # type: ignore
# TODO: this is slow, but atm necessary as dump_python doesn't do the appropriate
# transformation of things like PosixPath. Figure out why, maybe log issue/PR with
# pydantic
# return json.loads(PydanticModel.dump_json(getattr(self.parent, member)))
return PydanticModel.dump_python(getattr(self.parent, member), mode='json')

def decorate_class(klass):
if hasattr(klass, 'annotations'):
old_annotations = klass.annotations
else:
old_annotations = None

def annotations(self, obj):
def annotations(self, obj, /): # type: ignore
if old_annotations:
annotations = old_annotations(self, obj)
annotations = old_annotations(self, obj) # type: ignore
else:
annotations = super(klass, obj).annotations(obj)
annotation = Annotation(self)
return annotations + (annotation,)
return annotations + (annotation,) # type: ignore


klass.annotations = annotations
return klass
Expand All @@ -60,19 +75,46 @@ def decorate_obj(obj):
else:
old_annotations = None

def annotations(self = None, origin = None):
def annotations(self, origin , /): # type: ignore
if old_annotations:
annotations = old_annotations(origin)
else:
annotations = super(obj.__class__, obj).annotations(obj)
annotation = Annotation(self)
return annotations + (annotation,)
return annotations + (annotation,) # type: ignore

setattr(obj, 'annotations', MethodType(annotations, obj))
setattr(obj, 'annotations', MethodType(annotations, obj))
return obj

if decorate_object:
return decorate_obj
else:
return decorate_class


def submodule_metadata(fragment: Fragment, component_name: str, recursive=False) -> Generator[Tuple[wiring.Component, str| tuple, dict]]:
"""
Generator that finds `component_name` in `fragment` and
then yields the ``wiring.Component``s of that component's submodule, along with their names and metadata

Can only be run once for a given component (or its children)

If recursive = True, then name is a tuple of the heirarchy of names
otherwise, name is the string name of the first level component
"""

subfrag = fragment.find_subfragment(component_name)
design = subfrag.prepare()
for k,v in design.elaboratables.items():
full_name:tuple = design.fragments[design.elaboratables[k]].name
if len(full_name) > 1: # ignore the top component
if recursive:
name = full_name[1:]
else:
if len(full_name) != 2:
continue
name = full_name[1]
if isinstance(k, wiring.Component):
metadata = k.metadata.as_json()['interface']
yield k, name, metadata

12 changes: 0 additions & 12 deletions chipflow_lib/platforms/_internal.py

This file was deleted.

3 changes: 2 additions & 1 deletion chipflow_lib/platforms/_openframe.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import List, NamedTuple, Optional, Literal

from ._utils import Voltage, PowerPins, LinearAllocPackageDef, BringupPins
from ._utils import PowerPins, LinearAllocPackageDef, BringupPins
from ..config_models import Voltage

class OFPin(NamedTuple):
pin: int
Expand Down
Loading
Loading