test_composition¶
Attributes¶
Functions¶
|
|
|
A tile's LUTs at the coordinate above. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The q6 filter+reduce lane: key-partitioned fan-out, a chain of two |
|
|
|
|
What the migration is FOR: a design says what its fields are, not how |
|
int32 IS 32, so the two spellings are one cache key rather than two -- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A parameter that changes the hardware has to change the price. |
|
|
|
|
|
|
|
|
|
The design-cache key must be stable across releases: ANY change to |
|
A tampered canonical form fails registry validation, never builds. |
|
The prefix is what a reader has instead of the palette that wrote the |
|
The defect this boundary closes, in one place. |
|
A handle table is HARDWARE — a table of flops and a resolve stage in |
The canonical spec mirrors the walker's width rules: a wide feed |
|
The rate model recomputes silicon: 64-bit two-beat = 1/2 row/cycle |
|
The wide lane: one lane of wide-row-stream stages consuming reader |
|
|
|
|
|
|
A terminal-shaped tile mid-lane (the temporal hero's as-of join and |
|
|
|
|
|
|
|
|
|
The rate benches measured the q6 map stage at one row per SEVEN |
|
The dispatcher as it sits BEHIND a gearbox: its bus is one whole |
|
|
|
The gearbox must appear in core_names: that tuple is what stages the |
|
The spec hash keys the design cache. If the gearbox were invisible to |
|
A GUARANTEED floor has to charge the stage that produces the rows. |
The record size is the DIVISOR of the reader's supply, so it has to |
|
stream_shape_mismatches exists to find exactly this, and the front |
|
|
|
|
|
|
A stream job stages one tile chain through its job core instead of |
|
The stream job has no fan-out and no burst feed, so accepting a |
The count_port waiver is the STREAM job's, not a general relaxation: |
|
|
A job master of some intermediate width is not hardware anyone builds, |
The packed feed and the wide lane compose. |
|
|
The packed feed's ceiling is the READER's packed-row supply, which is |
|
Composition IS the registry: the shell cores a composition places are |
Module Contents¶
- test_composition._BAR¶
- test_composition._PART = 'xc7k325tffg900-2'¶
- test_composition._CLOCK_NS = 8.0¶
- test_composition._priced(spec: dau_core.composition.ScanCompositionSpec)¶
- test_composition._tile_lut(name: str) int¶
A tile’s LUTs at the coordinate above.
- test_composition._partition(lane: int, lanes: int) dau_core.composition.TileSpec¶
- test_composition.bar_noc_spec(lanes: int = 4) dau_core.composition.ScanCompositionSpec¶
- test_composition.test_spec_validates_lane_tiles() None¶
- test_composition.test_core_names_cover_infrastructure_and_lanes() None¶
- test_composition.test_estimated_resources_scale_with_lanes() None¶
- test_composition.test_single_lane_filterless_scan_is_valid() None¶
- test_composition._range_partitioner(lanes: int) dau_core.composition.TileSpec¶
- test_composition.sorted_scan_spec(lanes: int = 4) dau_core.composition.ScanCompositionSpec¶
- test_composition.test_sorted_scan_resources_use_partitioner_envelope() None¶
- test_composition.q6_scan_spec(lanes: int = 4) dau_core.composition.ScanCompositionSpec¶
The q6 filter+reduce lane: key-partitioned fan-out, a chain of two row-predicate filters (the four-comparison WHERE split across two 3-term tiles) and the revenue map, closed by the field-sum tile.
- test_composition.test_chain_lanes_validate_and_close_core_names() None¶
- test_composition.test_chain_stages_must_be_operators() None¶
- test_composition.test_chain_stages_need_no_status_counter() None¶
- test_composition.test_chain_resources_are_counted_per_lane() None¶
- test_composition.test_tile_params_validate_declared_names_and_positive_values() None¶
- test_composition.test_a_design_may_pick_an_element_type_by_name() None¶
What the migration is FOR: a design says what its fields are, not how many bits they take.
- test_composition.test_a_type_and_its_width_are_the_same_design() None¶
int32 IS 32, so the two spellings are one cache key rather than two – a design cache that missed on spelling would rebuild an identical shell.
- test_composition.test_minimum_constraint_mirrors_the_rolling_window_guard() None¶
- test_composition.test_a_declared_zero_minimum_admits_zero() None¶
- test_composition.test_maximum_constraint_mirrors_the_bucket_bits_guard() None¶
- test_composition.test_choices_and_power_of_two_constraints_mirror_the_elaboration_guards() None¶
- test_composition.test_load_phase_shape_rules() None¶
- test_composition.test_param_override_is_validated_on_the_lane_tile_and_partition_slots() None¶
- test_composition.test_key_space_override_must_match_the_declared_multiple() None¶
- test_composition.test_a_sized_tile_prices_at_its_size_not_its_default() None¶
A parameter that changes the hardware has to change the price.
This assertion used to be the OPPOSITE – that a 6M-key membership filter priced identically to a default 4096-key one – and it was true only because the tile carried a scalar envelope that answered every KEY_SPACE with the same numbers. The membership filter is a BITMAP, so its BRAM cost IS the key space: 1 BRAM36 at 4096, 192 at 6,000,000, which is 57% of the Artix’s 335 budgeted blocks. Every composition using the silicon-proven configuration was priced at 1/192 of the BRAM it actually places.
- test_composition._FRONT_UNPACK_CONFIG¶
- test_composition._front_unpack() dau_core.composition.TileSpec¶
- test_composition.test_front_unpack_validates_and_reaches_core_names_and_resources() None¶
- test_composition.test_front_unpack_rejects_a_non_unpack_tile() None¶
- test_composition.test_front_unpack_less_composition_is_unchanged() None¶
- test_composition.test_exactly_one_success_close_out_per_lane_is_enforced() None¶
- test_composition.test_spec_canonical_round_trip_and_hash_stability() None¶
- test_composition.test_spec_hash_is_config_order_insensitive_and_content_sensitive() None¶
- test_composition.test_spec_hash_pinned_fixture() None¶
The design-cache key must be stable across releases: ANY change to the canonical form — field ordering, a new or changed spec field or default, hash construction — fails HERE, loudly, and must bump SPEC_CANONICAL_VERSION deliberately (defaults are included precisely so a changed default changes the key instead of colliding).
- test_composition.test_spec_from_canonical_revalidates() None¶
A tampered canonical form fails registry validation, never builds.
- test_composition.test_canonical_version_reads_the_prefix_a_recorded_hash_carries() None¶
The prefix is what a reader has instead of the palette that wrote the text beside it, so it has to be readable without one.
- test_composition.test_a_text_recorded_under_an_older_palette_is_refused_by_version_not_by_parse() None¶
The defect this boundary closes, in one place.
A build manifest written before the width-named tiles were renamed records a canonical text naming them. Reading it against today’s registry raises UnknownCoreError — an error about a missing tile, from a design that is built, flashed and answering. The version prefix beside the text is the fact that says the text predates this palette, and it says so without a parse.
- test_composition.test_spec_hash_stable_when_defaults_are_explicit() None¶
- test_composition.test_handle_table_capacity_is_design_identity_and_inert_at_zero() None¶
A handle table is HARDWARE — a table of flops and a resolve stage in front of the reader — so a design that carries one must not share a cache key with one that does not, and eight slots is not sixteen. Zero is the no-table default: a spec that never mentions it keeps the shape it has today and round-trips unchanged.
- test_composition.test_wide_data_width_composition_rules() None¶
The canonical spec mirrors the walker’s width rules: a wide feed needs a dispatcher (broadcast is 64-bit only), wide packed reads are a typed follow-up, and the tier set is closed.
- test_composition.test_throughput_reproduces_the_measured_rate_ladder()¶
The rate model recomputes silicon: 64-bit two-beat = 1/2 row/cycle (~99 ms q6), the 512-bit dispatch feed = 1 (the serializer, 60.3 ms measured 2026-07-30), and MORE LANES DO NOT HELP past the dispatcher’s declared ingest — the motivation for the wide-sum lane / distributor.
- test_composition.test_throughput_packed_front_re_times_delivery()¶
- test_composition.test_the_wide_lane_shape_composes_and_rate_matches()¶
The wide lane: one lane of wide-row-stream stages consuming reader beats directly — SLOTS rows per cycle, no partitioner. The q6 shape at 512 rates 4 rows/cycle (the parallel-row target).
- test_composition.test_wide_combinational_tile_pipeline_depth_bounds_are_enforced()¶
- test_composition.test_the_wide_lane_shape_rules_are_enforced()¶
- test_composition.test_wide_row_stream_cores_are_refused_in_the_partitioner_slot() None¶
- test_composition.test_the_fused_chain_shape_is_declared_not_inferred() None¶
A terminal-shaped tile mid-lane (the temporal hero’s as-of join and grouped aggregator) composes only when the spec DECLARES the fused chain: the refusal still catches an aggregator dropped into a chain by accident, while the declaration says the caller accepts that mid-stage close-outs are drained and only their errors reach the lane status.
- test_composition._temporal_hero_spec(*, include_feature_gather: bool = True) dau_core.composition.ScanCompositionSpec¶
- test_composition.test_temporal_hero_gathers_reconcile_every_stream_shape() None¶
- test_composition.test_stream_shape_mismatch_names_both_cores_and_shapes() None¶
- test_composition.test_stream_shape_mismatches_include_front_feed_and_partitioner() None¶
- test_composition.test_selectivity_reconciles_the_model_with_q6_silicon() None¶
The rate benches measured the q6 map stage at one row per SEVEN cycles standalone, which made the no-selectivity model predict 1/7 for q6 — 3.4x below the 0.483 rows/cycle silicon actually ran. Both numbers are right: two predicates upstream pass ~2% of rows, so the map never binds the feed. Charging each stage against the rows that reach it restores agreement, and the worst case stays the default because selectivity is data-dependent.
- test_composition.test_throughput_breakdown_names_the_binding_stage() None¶
- test_composition._record_dispatcher(record_words: int = 3) dau_core.composition.TileSpec¶
The dispatcher as it sits BEHIND a gearbox: its bus is one whole record wide, so it takes RECORD_INPUT rather than a row framing.
- test_composition._front_gearbox(record_words: int = 3) dau_core.composition.TileSpec¶
- test_composition.test_front_gearbox_reaches_core_names_and_resources() None¶
The gearbox must appear in core_names: that tuple is what stages the HDL a build compiles, so a gearbox missing from it is instantiated by the generated top and never handed to the toolchain — the design black-boxes at synthesis.
The spec hash keys the design cache. If the gearbox were invisible to the canonical form, a cached ungeared bitstream would be handed back for a geared design and silently run the wrong front.
- test_composition.test_the_gearbox_is_charged_as_the_front_delivery_it_is() None¶
A GUARANTEED floor has to charge the stage that produces the rows.
The gearbox was validated by five walkers and priced by none: absent from this function, a geared composition took the quad-row delivery of data_width/128 – 24-byte records charged as 16-byte ones – and the gearbox’s own one-record-per-cycle ceiling was never applied at all. At data_width=512 that reports 4 rows/cycle for a front that sustains 1.
- test_composition.test_a_wider_record_lowers_the_geared_delivery() None¶
The record size is the DIVISOR of the reader’s supply, so it has to move the answer – a delivery term that ignores it is the shape that charged 24-byte records at the 16-byte quad-row rate.
- test_composition.test_a_gearbox_record_mismatch_is_detected() None¶
stream_shape_mismatches exists to find exactly this, and the front stage it never looked at was the gearbox: a 3-word gearbox feeding a 4-word dispatcher validated clean and the detector returned ().
- test_composition.test_front_gearbox_refuses_a_narrow_feed_and_a_rival_front() None¶
- test_composition._stream_job_spec(job: str = 'bram-stream-job', tile: str = 'bar-aggregation') dau_core.composition.ScanCompositionSpec¶
- test_composition.test_a_stream_job_composition_declares_its_own_infrastructure() None¶
A stream job stages one tile chain through its job core instead of reading bursts and fanning out. Until it could be declared, its cores reached the build as a hand-copied list — so nothing checked that the list still matched what the top instantiates.
- test_composition.test_a_stream_job_composition_refuses_scan_shaped_fields() None¶
The stream job has no fan-out and no burst feed, so accepting a partitioner or a front stage would let a spec describe hardware the shell cannot build.
- test_composition.test_the_scan_shell_still_requires_a_lane_counter() None¶
The count_port waiver is the STREAM job’s, not a general relaxation: the scan shell’s record writer sizes its write-out from that port.
- test_composition.test_addr_width_is_32_or_64_only() None¶
A job master of some intermediate width is not hardware anyone builds, and permitting it would mean carrying padding logic through every register slice for a shape with nothing behind it: the AXI-Lite read path zero-extends cleanly at exactly 32 and 64 and needs an explicit pad at anything else.
- test_composition.test_a_packed_front_can_feed_a_wide_lane() None¶
The packed feed and the wide lane compose.
Each is silicon-proven alone and they were mutually exclusive: the wide lane read whole quad rows straight from the reader, so it burned 16 bytes of DDR per row, and the next SLOTS doubling would have demanded 19.2 GB/s from a 12.8 GB/s DDR3. Packed, the same lane demands 9.6.
Two widths are in play and they are NOT the same number.
data_widthis the READ width – SLOTS packed 64-bit rows per beat – while the lane downstream of the unpacker sees SLOTS whole 128-bit quad rows. Note the consequence: an unpacked SLOTS=8 lane would need data_width=1024, which is not a legal tier, so packing is what puts eight slots in reach at all.
- test_composition.test_a_packed_wide_feed_rates_at_the_unpacker_not_at_one_row() None¶
The packed feed’s ceiling is the READER’s packed-row supply, which is data_width/64, not a flat one row per cycle.
The flat cap read the packed path as “one 8-byte row per 64-bit beat”, true only while data_width was 64. A packed WIDE feed hands the unpacker data_width/64 packed rows per beat, and capping at 1 under-reported the silicon-measured eight-slot lane by 8x – the design would have been priced as no faster than the build it replaced.
- test_composition.test_shell_infrastructure_is_selected_by_role_not_by_name() None¶
Composition IS the registry: the shell cores a composition places are the ones declaring each infrastructure role, exactly one per role, and
composition.pynames none of them. The order the scan roles are placed in is pinned becausecore_namesfeeds the canonical hash.
- test_composition.test_a_stream_job_must_name_a_stream_job_shell() None¶