dau_core.cores¶
Core registry models and accessors: every HDL building block, what it is, what it depends on, which golden defines its semantics, and what it costs.
The registry itself lives in dau-core’s hydra config tree
(dau_core/config/dau-core/cores.yaml): one CoreDefinition entry per
core, composed into the ccflow ModelRegistry at /dau-core/<name>
and discoverable on the shared search path via the hydra.lernaplugins
entry point. Composition IS the registry — this module defines the models
and thin cached accessors, never a core list. Resource measurements are
produced by the dau-build synthesize-cores task and recorded per part, clock
and module parameters; there is no other shape.
Attributes¶
Exceptions¶
A core names an HDL package that is not installed. |
|
A generated core was asked for its source before it was rendered. |
|
Base class for lookup errors. |
|
Inappropriate argument value (of correct type). |
Classes¶
Create a collection of name/value pairs. |
|
What a load-capable tile's LOAD-phase input stream is (the two-phase |
|
What a core is FOR where a composition has to know, declared by the |
|
Which streaming interface contract an operator tile speaks. Contract |
|
Resource counts: one LUT/FF/BRAM/DSP tuple. The usable face of a |
|
Create a collection of name/value pairs. |
|
One exact OOC result. Coordinates are deliberately discrete: a |
|
A stream core's guaranteed sustained ingest rate: |
|
An overridable SystemVerilog module parameter: its HDL default and the |
|
A core's declared storage need (STORAGE_AND_NOC.md). |
|
One HDL building block and its contract surround. |
Functions¶
|
Two clock periods naming the same physical clock. |
|
The on-disk directory of an HDL package. |
|
Where generated cores are materialized. |
Module Contents¶
- class dau_core.cores.CoreKind(*args, **kwds)¶
Bases:
enum.EnumCreate a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3
Access them by:
attribute access:
>>> Color.RED <Color.RED: 1>
value lookup:
>>> Color(1) <Color.RED: 1>
name lookup:
>>> Color['RED'] <Color.RED: 1>
Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3
>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
- OPERATOR = 'operator'¶
- STREAM_INFRA = 'stream-infra'¶
- SHELL_ADAPTER = 'shell-adapter'¶
- PACKAGE = 'package'¶
- SIM_MODEL = 'sim-model'¶
- class dau_core.cores.LoadImageKind(*args, **kwds)¶
Bases:
enum.EnumWhat a load-capable tile’s LOAD-phase input stream is (the two-phase load: cfg_load=1 streams the build image, cfg_load=0 probes). The composition validates image/length-grid rules per kind; a core without this marker has no cfg_load port and can never be phase-bound.
- BITMAP_64 = 'bitmap-64'¶
- QUAD_ROWS = 'quad-rows'¶
- class dau_core.cores.CoreRole(*args, **kwds)¶
Bases:
enum.EnumWhat a core is FOR where a composition has to know, declared by the registry rather than recognized by its name.
Two composition rules cannot be read off a tile’s ports: that the core in the optional front-stage feed-transform slot is one that widens packed rows, and that a key-routing partitioner’s lane count satisfies the decode its
MODEselects. Both were exact-name comparisons, which is the one thing “composition IS the registry” forbids – a second unpacker or a second router could not be written without editing this package, and a tile rename silently turned the check into dead code. A core says here what job it does; the composition asks.KEY_DISPATCHis a contract as well as a label: a core declaring it routes whole records to lanes by a decode over the record’s key, and carriesMODE(adau_core.operators.DispatchMode) andNUM_PARTITIONSparameters with those meanings. A range partitioner routes by ordered splitters instead and is deliberately not one.- FRONT_UNPACK = 'front-unpack'¶
- KEY_DISPATCH = 'key-dispatch'¶
- SCAN_READER = 'scan-reader'¶
- SCAN_BROADCAST = 'scan-broadcast'¶
- SCAN_RECORD_WRITER = 'scan-record-writer'¶
- SCAN_WRITE_MUX = 'scan-write-mux'¶
- SHELL_REGISTERS = 'shell-registers'¶
- STREAM_JOB_SHELL = 'stream-job-shell'¶
- FRONT_GEARBOX = 'front-gearbox'¶
- class dau_core.cores.ContractKind(*args, **kwds)¶
Bases:
enum.EnumWhich streaming interface contract an operator tile speaks. Contract tests select their validator from this tag; a new core declares its contract here rather than being special-cased in test code.
- ROW_STREAM = 'row-stream'¶
- ARROW_LITE_DESCRIPTOR = 'arrow-lite-descriptor'¶
- WIDE_ROW_STREAM = 'wide-row-stream'¶
- class dau_core.cores.ResourceEnvelope(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelResource counts: one LUT/FF/BRAM/DSP tuple. The usable face of a measurement point, or the sum a composition estimate adds up to. Never a registry entry on its own: the registry records measurement points, which carry the coordinates a count is meaningless without.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- lut: pydantic.NonNegativeInt¶
- ff: pydantic.NonNegativeInt¶
- bram36: pydantic.NonNegativeFloat¶
- dsp: pydantic.NonNegativeInt¶
- class dau_core.cores.TimingStatus(*args, **kwds)¶
Bases:
enum.EnumCreate a collection of name/value pairs.
Example enumeration:
>>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3
Access them by:
attribute access:
>>> Color.RED <Color.RED: 1>
value lookup:
>>> Color(1) <Color.RED: 1>
name lookup:
>>> Color['RED'] <Color.RED: 1>
Enumerations can be iterated over, and know how many members they have:
>>> len(Color) 3
>>> list(Color) [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.
- MET = 'met'¶
- VIOLATED = 'violated'¶
- class dau_core.cores.ResourceMeasurement(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelOne exact OOC result. Coordinates are deliberately discrete: a measurement says nothing about another part, clock, or parameter tuple.
A combinational core has no clock:
clock_nsisNone, no timing was reported and none is recorded, and the point answers for any clock because the logic costs the same at every one. A clocked point carries the clock it was constrained to, whether it met, and by how much.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- part: str¶
- clock_ns: float | None = None¶
- params: Annotated[collections.abc.Mapping[str, pydantic.StrictInt], *ReadOnly]¶
- lut: pydantic.NonNegativeInt¶
- ff: pydantic.NonNegativeInt¶
- bram36: pydantic.NonNegativeFloat¶
- dsp: pydantic.NonNegativeInt¶
- timing: TimingStatus | None = None¶
- wns_ns: float | None = None¶
- classmethod _accept_type_spellings(data)¶
A coordinate may name an element type where the synthesis run took one (“FIELD_WIDTH: int32”), and is stored as the encoding, so it compares equal to the same point written as a number. No parameter of an SV module has a legal non-numeric value other than a type, so a string here needs no further disambiguation.
- _timing_matches_wns() ResourceMeasurement¶
- answers_clock(clock_ns: float) bool¶
Whether this point prices a build at
clock_ns: an unclocked point prices any clock, a clocked one only its own.
- usable_envelope() ResourceEnvelope¶
Return resource counts only when timing met its target (or there was no timing to meet).
- class dau_core.cores.IngestRate(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelA stream core’s guaranteed sustained ingest rate:
rowsrows perper_cyclesclock cycles, an exact rational (1 row per 2 cycles is the composed lane rate the silicon proved; the key-mask dispatcher ingests 1 per 1). This is PUBLIC INTERFACE, not a heuristic: the scheduler/pricing software rate-matches a core it cannot inspect — a user-provided custom core tied to a custom operator is opaque except for its registry declaration, so the declaration carries the rate. Declare the FLOOR a core sustains under continuous valid input, with RECORD/DRAIN EMISSION AMORTIZED over the batch: close-out records, top-k drains, and per-group records are batch-tail or per-group costs, not per-row costs, so the declaration describes the streaming regime (groups much smaller than rows, drains at close). Data-dependent worst cases — every group key distinct, adversarial key runs into one dispatcher lane — are planner-regime concerns the planner sees in the plan (group keys, sortedness), not floor violations. A core may burst faster, and a measured improvement (a retime, a wider port) lands here the same release the RTL proves it. Compositions over these floors yield the sustained rate the scheduler plans on UNDER those stated assumptions.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- rows: pydantic.StrictInt¶
- per_cycles: pydantic.StrictInt¶
- scales_with_param: str | None = None¶
- frames_on_param: str | None = None¶
- classmethod _positive(value: int) int¶
- _one_scaling_shape() IngestRate¶
- property rows_per_cycle: fractions.Fraction¶
- effective(params: collections.abc.Mapping[str, int], declared: collections.abc.Mapping[str, ParameterSpec]) fractions.Fraction¶
The rate at concrete parameter values — what the scheduler uses.
paramsare the composition’s overrides;declaredthe core’s parameter specs (defaults fill unset overrides).
- class dau_core.cores.ParameterSpec(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelAn overridable SystemVerilog module parameter: its HDL default and the constraint a composition override must satisfy — mirroring the RTL’s own elaboration guards so an invalid override is rejected at composition time, never later as an HDL elaboration failure.
multiple_ofmirrors sizing (the membership bitmap isKEY_SPACE/64words);choicespins an exact legal set (the quad-stream widths are 64 or 128, nothing else);power_of_twomirrors mask/bit-select decodes (the key-mask dispatcher’sNUM_PARTITIONS);minimummirrors a lower bound (the rolling-moment window is>= 2— a size-1 sample variance is undefined). The HDL is the source of truth; this mirrors it.element_typemarks the parameter as naming WHAT AN ELEMENT IS rather than how big something is, and changes three things:defaultandchoicesmay be written as type spellings (int32,q16.16,float64) as well as integers, and are stored as thedau_core.element_typesencoding either way. A plain signed integer encodes as its own width, so32andint32are the same declaration and no existing entry changes meaning.choicesare matched on the whole type.int32andq16.16are both 32 bits wide and are NOT interchangeable, which is the distinction a width could not draw.minimum/maximum/multiple_of/power_of_twoconstrain the type’s WIDTH, because that is what the RTL guard they mirror is about (“FIELD_WIDTH must divide 128 and lie in [8, 64]” is a statement about the field’s bits, not about the number that names its type).
Set it only for a parameter that genuinely selects an element type. Bus and beat widths (
IN_WIDTH,OUT_WIDTH,DATA_WIDTH) count bits per handshake and are not types; declaring one here would let a design ask for a fixed-point AXI bus.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- default: int¶
- multiple_of: int¶
- choices: tuple[int, ...] | None = None¶
- power_of_two: bool = False¶
- minimum: int = 1¶
- maximum: int | None = None¶
- element_type: bool = False¶
- classmethod _accept_type_spellings(data)¶
Resolve declared type spellings to their encoding before the int fields see them, so everything downstream – the constraint checks, the
-generica synthesis run passes, the canonical spec hash – works on one integer form and never has to know how it was written.
- violation(value: int | str) str | None¶
Why
valueis not a legal override, orNonewhen it is. This is the ONE statement of the constraint – composition wraps it with tile context, and the model’s own coherence check runs the default through it, so the rule cannot drift between the two.
- normalize(value: int | str) int¶
The integer the RTL elaborates with, however the value was written.
A consumer holding a raw override – a build task assembling
-genericarguments, say – has to turn a spelling into its code before it reaches a tool, and cannot import this package to do it: dau-build is public and the encoding lives here. Asking the spec that declared the parameter keeps the vocabulary in one place.
- _violation(value: int, magnitude: int) str | None¶
valueis what the RTL elaborates with;magnitudeis what the bounds constrain – the same number for a size parameter, and the element type’s width for a type parameter.
- _spell(value: int) str¶
How a value appears in a message: a type parameter’s values are named, because ‘4128’ tells a reader nothing and ‘q16.16’ does.
- _constraints_cohere() ParameterSpec¶
The spec mirrors RTL elaboration guards, so an incoherent spec is a registry bug that would reject every override – catch it at registration, not at the first composition.
- class dau_core.cores.StorageIntent(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelA core’s declared storage need (STORAGE_AND_NOC.md).
owned= a private single-cycle arena;shared= handle-based tier transactions. Capacity prices against a declared module parameter so the planner can refuse what no tier serves — never discovered at synthesis.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- role: str¶
- access: Literal['owned', 'shared']¶
- bytes_per_unit: int¶
- scale_parameter: str | None = None¶
- tier_hint: str | None = None¶
- dau_core.cores._same_clock(a: float, b: float) bool¶
Two clock periods naming the same physical clock.
The unit is NANOSECONDS, so the tolerance is a picosecond: 6.66667 against a derived 6.666666666666667 differs by 3.3e-6 ns. Clock tiers in this registry are more than a nanosecond apart (8.0 vs 6.667 vs 10.0), so a picosecond is a thousand times too small to reach a neighbour.
- dau_core.cores.HDL_CLOSURE_DIGEST_VERSION = '1'¶
- dau_core.cores._HDL_INCLUDE¶
- class dau_core.cores.CoreDefinition(/, **data: Any)¶
Bases:
dau_core.models.CheckedModelOne HDL building block and its contract surround.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- name: str¶
- module: str¶
- source: str¶
- source_package: str = 'dau_core.hdl'¶
- generator: dau_core.generators.CoreGenerator | None = None¶
- description: str¶
- depends: tuple[str, ...] = ()¶
- golden: str | None = None¶
- bench: str | None = None¶
- resources: tuple[ResourceMeasurement, ...] | None = None¶
- measured_from: str | None = None¶
- count_port: str | None = None¶
- contract_kind: ContractKind | None = None¶
- accuracy_ulp: int | None = None¶
- ingest: IngestRate | None = None¶
- record_words: pydantic.StrictInt | None = None¶
- record_words_param: str | None = None¶
- output_record_words: pydantic.StrictInt | None = None¶
- output_record_words_param: str | None = None¶
- output_record_words_variable: bool = False¶
- supports_pipeline_depth: bool = False¶
- opcodes: tuple[dau_core.stream.OperationCode, ...] = ()¶
- opcode_input_types: tuple[dau_core.stream.LogicalType, ...] = ()¶
- row_capacity: int | None = None¶
- unit_kind: dau_core.design.UnitKind | None = None¶
- success_close_out: bool = False¶
- load_image: LoadImageKind | None = None¶
- parameters: Annotated[collections.abc.Mapping[str, ParameterSpec], *ReadOnly]¶
- storage_intent: tuple[StorageIntent, ...] = ()¶
- storage_bytes(params: collections.abc.Mapping[str, int]) tuple[tuple[StorageIntent, int], ...]¶
Each intent with its byte cost at the effective parameters (overrides win, declared defaults otherwise).
- model_post_init(context) None¶
Override this method to perform additional initialization after __init__ and model_construct. This is useful if you want to do some validation that requires the entire model to be initialized.
- _parameter_value(name: str, params: collections.abc.Mapping[str, int]) int¶
- effective_record_words(params: collections.abc.Mapping[str, int]) int¶
Concrete input-record width after applying parameter overrides.
- effective_output_record_words(params: collections.abc.Mapping[str, int]) int | None¶
Concrete emitted width, or
Nonefor runtime-variable output.
- resource_measurement(*, part: str, clock_ns: float, params: collections.abc.Mapping[str, int]) ResourceMeasurement¶
Resolve one exact measurement, never a neighboring point.
Composition overrides are expanded with declared defaults before comparison. An unclocked (combinational) point answers for any clock.
Clock periods are DERIVED (1000 / job_clock_mhz), so they are compared to the picosecond rather than by float equality: 150 MHz yields 6.666666666666667 while a registry writes 6.66667, and exact equality rejected a point while printing it in the same breath. Real clock tiers are more than a nanosecond apart, so this cannot reach a neighbour.
- source_path() pathlib.Path¶
The HDL this core synthesizes from.
A GENERATED core (one declaring a
generator) has no checked-in file, so it refuses until it has been rendered rather than rendering implicitly on first read. What sits on disk is what gets synthesized; rendering behind a read would hide which parameters produced it, and the whole reason this core is generated is that its constants are a function of those parameters.
- hdl_closure(resolve) tuple[tuple[str, str], ...]¶
Every HDL file this core synthesizes from, as
(name, sha256)pairs sorted by name.WHAT IS IN THE CLOSURE is the whole question, and the answer is: the files a synthesis run of THIS core reads. That is its own source, the source of every core reachable through
depends(the packages it imports), and every header those files `` include `, resolved the way the toolchain resolves them – beside the including file first, then along the other source directories, which is exactly the-include_dirslist the synthesis task builds. A header nothing includes is NOT in the closure, so adding a second one to the palette does not re-measure every tile that ignores it; and a package a tile imports IS, so an edit there reaches every tile that reads it.resolvemaps a core name to its definition. It is a parameter rather than a registry import because the build package reaches these models through the composed ccflow registry and must not import this one.A GENERATED core is hashed from its RENDERED TEXT, not from a file on disk: its RTL is a function of the generator and of the constants in its registry entry, so an edit to either changes what synthesis reads while every checked-in file stays byte-identical.
- hdl_closure_digest(resolve) str¶
The digest a measured envelope records as
measured_from.Over BYTES, deliberately. A digest cannot tell a retimed adder from a renamed module, and pretending otherwise would mean teaching it which edits are allowed to be free – which is the judgement the guard exists to force someone to make and write down. So a rename moves the digest too, and re-stamping it costs an argument in the pull request.
- render(*, into: pathlib.Path | None = None) pathlib.Path¶
Render this core’s HDL, returning the written path.
- golden_callable()¶
- exception dau_core.cores.UnknownCoreSourceError¶
Bases:
RuntimeErrorA core names an HDL package that is not installed.
Initialize self. See help(type(self)) for accurate signature.
- exception dau_core.cores.UnrenderedCoreError¶
Bases:
RuntimeErrorA generated core was asked for its source before it was rendered.
Initialize self. See help(type(self)) for accurate signature.
- exception dau_core.cores.UnmeasuredResourceError¶
Bases:
LookupErrorBase class for lookup errors.
Initialize self. See help(type(self)) for accurate signature.
- exception dau_core.cores.TimingViolationError¶
Bases:
ValueErrorInappropriate argument value (of correct type).
Initialize self. See help(type(self)) for accurate signature.
- dau_core.cores.package_root(package: str) pathlib.Path¶
The on-disk directory of an HDL package.
Resolved through importlib rather than a relative path from this file, so a core contributed by another distribution finds its own tree.
- dau_core.cores.render_root(package: str = 'dau_core.hdl') pathlib.Path¶
Where generated cores are materialized.
Defaults beside the declaring package so a checkout behaves like one with the files committed; a build tree points DAU_CORE_RENDER_ROOT at its own directory so two builds cannot race on one file.