dau-core 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dau_core/__init__.py +118 -0
- dau_core/_lazy.py +46 -0
- dau_core/capabilities.py +79 -0
- dau_core/capability_contract.py +274 -0
- dau_core/composition.py +939 -0
- dau_core/config/__init__.py +155 -0
- dau_core/config/base.yaml +3 -0
- dau_core/config/dau-core/cores.yaml +4564 -0
- dau_core/config/task/tasks/temporal/hero-workflow.yaml +10 -0
- dau_core/cores.py +839 -0
- dau_core/design.py +107 -0
- dau_core/element_types.py +165 -0
- dau_core/exp_generator.py +397 -0
- dau_core/fixed_point.py +469 -0
- dau_core/generators.py +105 -0
- dau_core/hdl/.gitignore +1 -0
- dau_core/hdl/__init__.py +123 -0
- dau_core/hdl/dau_aggregation_pkg.sv +40 -0
- dau_core/hdl/dau_arrow_lite_stream_aggregation.sv +447 -0
- dau_core/hdl/dau_asof_backward.sv +412 -0
- dau_core/hdl/dau_axi_burst_reader.sv +219 -0
- dau_core/hdl/dau_axi_ram_sim.sv +209 -0
- dau_core/hdl/dau_axi_random_probe.sv +180 -0
- dau_core/hdl/dau_axi_record_writer.sv +233 -0
- dau_core/hdl/dau_axi_stream_job.sv +379 -0
- dau_core/hdl/dau_axi_write_mux.sv +125 -0
- dau_core/hdl/dau_bar_aggregation.sv +388 -0
- dau_core/hdl/dau_bitonic_sorter.sv +278 -0
- dau_core/hdl/dau_bram_stream_job.sv +175 -0
- dau_core/hdl/dau_broadcast_hash_join.sv +522 -0
- dau_core/hdl/dau_datetime_format.sv +489 -0
- dau_core/hdl/dau_field_sum_aggregation.sv +262 -0
- dau_core/hdl/dau_fixed_point_divide.sv +305 -0
- dau_core/hdl/dau_fixed_point_sqrt.sv +285 -0
- dau_core/hdl/dau_grouped_aggregation.sv +370 -0
- dau_core/hdl/dau_grouped_field_aggregation.sv +333 -0
- dau_core/hdl/dau_grouped_field_sum_aggregation.sv +321 -0
- dau_core/hdl/dau_hash_grouped_aggregation.sv +519 -0
- dau_core/hdl/dau_identity_axil.v +135 -0
- dau_core/hdl/dau_identity_registers.sv +66 -0
- dau_core/hdl/dau_int32_keyed_map_alu.sv +227 -0
- dau_core/hdl/dau_int32_keyed_predicate_filter.sv +250 -0
- dau_core/hdl/dau_int32_map_alu.sv +228 -0
- dau_core/hdl/dau_int32_predicate_filter.sv +297 -0
- dau_core/hdl/dau_int32_record_gather.sv +244 -0
- dau_core/hdl/dau_key_mask_dispatcher.sv +592 -0
- dau_core/hdl/dau_key_membership_filter.sv +526 -0
- dau_core/hdl/dau_keyed_state.sv +316 -0
- dau_core/hdl/dau_map_alu_pkg.sv +74 -0
- dau_core/hdl/dau_market_feature.sv +262 -0
- dau_core/hdl/dau_pair_key_filter.sv +251 -0
- dau_core/hdl/dau_predicate_pkg.sv +70 -0
- dau_core/hdl/dau_range_partitioner.sv +341 -0
- dau_core/hdl/dau_record_batch_aggregation.sv +492 -0
- dau_core/hdl/dau_record_gearbox.sv +188 -0
- dau_core/hdl/dau_rolling_extremum.sv +389 -0
- dau_core/hdl/dau_rolling_moments.sv +484 -0
- dau_core/hdl/dau_row_map_alu.sv +312 -0
- dau_core/hdl/dau_row_predicate_filter.sv +416 -0
- dau_core/hdl/dau_row_unpack.sv +578 -0
- dau_core/hdl/dau_scratchpad.sv +230 -0
- dau_core/hdl/dau_session_gap.sv +439 -0
- dau_core/hdl/dau_stream_aggregation.sv +178 -0
- dau_core/hdl/dau_stream_broadcast.sv +41 -0
- dau_core/hdl/dau_streaming_dedup.sv +418 -0
- dau_core/hdl/dau_streaming_top_k.sv +275 -0
- dau_core/hdl/dau_stride_distributor.sv +250 -0
- dau_core/hdl/dau_time_bucket_key.sv +269 -0
- dau_core/hdl/dau_wide_field_sum_aggregation.sv +346 -0
- dau_core/hdl/dau_wide_row_map_alu.sv +304 -0
- dau_core/hdl/dau_wide_row_predicate_filter.sv +364 -0
- dau_core/hdl/types.svh +71 -0
- dau_core/hero.py +204 -0
- dau_core/ln_generator.py +447 -0
- dau_core/models.py +126 -0
- dau_core/normal_cdf_generator.py +318 -0
- dau_core/operators.py +1993 -0
- dau_core/registers.py +464 -0
- dau_core/registry.py +194 -0
- dau_core/stream.py +356 -0
- dau_core/temporal.py +522 -0
- dau_core/tests/cocotb_benches/__init__.py +1 -0
- dau_core/tests/cocotb_benches/dau_arrow_lite_stream_aggregation_tb.py +189 -0
- dau_core/tests/cocotb_benches/dau_asof_backward_tb.py +288 -0
- dau_core/tests/cocotb_benches/dau_asof_feature_chain_tb.py +252 -0
- dau_core/tests/cocotb_benches/dau_axi_bar_job_tb.py +176 -0
- dau_core/tests/cocotb_benches/dau_axi_bar_noc_tb.py +207 -0
- dau_core/tests/cocotb_benches/dau_axi_burst_reader_tb.py +103 -0
- dau_core/tests/cocotb_benches/dau_axi_random_probe_tb.py +104 -0
- dau_core/tests/cocotb_benches/dau_axi_sort_noc_tb.py +241 -0
- dau_core/tests/cocotb_benches/dau_bar_aggregation_tb.py +220 -0
- dau_core/tests/cocotb_benches/dau_bitonic_sorter_tb.py +344 -0
- dau_core/tests/cocotb_benches/dau_broadcast_hash_join_small_tb.py +71 -0
- dau_core/tests/cocotb_benches/dau_broadcast_hash_join_tb.py +760 -0
- dau_core/tests/cocotb_benches/dau_bucket_agg_chain_tb.py +163 -0
- dau_core/tests/cocotb_benches/dau_datetime_format_tb.py +236 -0
- dau_core/tests/cocotb_benches/dau_field_sum_aggregation_tb.py +371 -0
- dau_core/tests/cocotb_benches/dau_fixed_point_divide_tb.py +393 -0
- dau_core/tests/cocotb_benches/dau_fixed_point_exp_tb.py +130 -0
- dau_core/tests/cocotb_benches/dau_fixed_point_ln_tb.py +248 -0
- dau_core/tests/cocotb_benches/dau_fixed_point_sqrt_tb.py +367 -0
- dau_core/tests/cocotb_benches/dau_grouped_aggregation_tb.py +206 -0
- dau_core/tests/cocotb_benches/dau_grouped_field_aggregation_tb.py +200 -0
- dau_core/tests/cocotb_benches/dau_grouped_field_sum_aggregation_tb.py +472 -0
- dau_core/tests/cocotb_benches/dau_hash_grouped_aggregation_tb.py +328 -0
- dau_core/tests/cocotb_benches/dau_hero_datapath_tb.py +179 -0
- dau_core/tests/cocotb_benches/dau_int32_keyed_map_alu_tb.py +410 -0
- dau_core/tests/cocotb_benches/dau_int32_keyed_predicate_filter_tb.py +418 -0
- dau_core/tests/cocotb_benches/dau_int32_map_alu_tb.py +206 -0
- dau_core/tests/cocotb_benches/dau_int32_predicate_filter_tb.py +193 -0
- dau_core/tests/cocotb_benches/dau_int32_record_gather_tb.py +256 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_exact_tb.py +130 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_hash_tb.py +127 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_multi_tb.py +158 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_records_tb.py +172 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_tb.py +269 -0
- dau_core/tests/cocotb_benches/dau_key_mask_dispatcher_wide_tb.py +193 -0
- dau_core/tests/cocotb_benches/dau_key_membership_filter_single_word_tb.py +97 -0
- dau_core/tests/cocotb_benches/dau_key_membership_filter_tb.py +625 -0
- dau_core/tests/cocotb_benches/dau_keyed_state_tb.py +196 -0
- dau_core/tests/cocotb_benches/dau_market_feature_tb.py +196 -0
- dau_core/tests/cocotb_benches/dau_normal_cdf_tb.py +145 -0
- dau_core/tests/cocotb_benches/dau_pair_key_filter_tb.py +296 -0
- dau_core/tests/cocotb_benches/dau_range_partitioner_tb.py +402 -0
- dau_core/tests/cocotb_benches/dau_record_batch_aggregation_tb.py +369 -0
- dau_core/tests/cocotb_benches/dau_record_gearbox_tb.py +229 -0
- dau_core/tests/cocotb_benches/dau_rolling_extremum_tb.py +252 -0
- dau_core/tests/cocotb_benches/dau_rolling_moments_tb.py +207 -0
- dau_core/tests/cocotb_benches/dau_row_map_alu_tb.py +339 -0
- dau_core/tests/cocotb_benches/dau_row_predicate_filter_tb.py +587 -0
- dau_core/tests/cocotb_benches/dau_row_predicate_filter_width_tb.py +150 -0
- dau_core/tests/cocotb_benches/dau_row_unpack_slots_tb.py +349 -0
- dau_core/tests/cocotb_benches/dau_row_unpack_tb.py +783 -0
- dau_core/tests/cocotb_benches/dau_row_unpack_wide_tb.py +428 -0
- dau_core/tests/cocotb_benches/dau_scratchpad_tb.py +194 -0
- dau_core/tests/cocotb_benches/dau_session_gap_tb.py +208 -0
- dau_core/tests/cocotb_benches/dau_stream_aggregation_tb.py +94 -0
- dau_core/tests/cocotb_benches/dau_streaming_dedup_tb.py +312 -0
- dau_core/tests/cocotb_benches/dau_streaming_top_k_tb.py +209 -0
- dau_core/tests/cocotb_benches/dau_stride_distributor_tb.py +120 -0
- dau_core/tests/cocotb_benches/dau_time_bucket_key_tb.py +182 -0
- dau_core/tests/cocotb_benches/dau_wide_field_sum_aggregation_tb.py +169 -0
- dau_core/tests/cocotb_benches/dau_wide_ingest_chain_tb.py +141 -0
- dau_core/tests/cocotb_benches/dau_wide_row_map_alu_tb.py +255 -0
- dau_core/tests/cocotb_benches/dau_wide_row_predicate_filter_tb.py +284 -0
- dau_core/tests/cocotb_benches/rate_probe.py +95 -0
- dau_core/tests/conftest.py +22 -0
- dau_core/tests/exhaustive_sweep.py +90 -0
- dau_core/tests/fixtures/ohlcv/README.md +17 -0
- dau_core/tests/fixtures/ohlcv/bars_full.csv +8 -0
- dau_core/tests/fixtures/ohlcv/bars_regular.csv +7 -0
- dau_core/tests/fixtures/ohlcv/prepared_full.csv +13 -0
- dau_core/tests/fixtures/ohlcv/prepared_regular.csv +12 -0
- dau_core/tests/fixtures/ohlcv/trades.csv +14 -0
- dau_core/tests/sv/dau_arrow_lite_stream_aggregation_tb.sv +250 -0
- dau_core/tests/sv/dau_asof_feature_chain.sv +149 -0
- dau_core/tests/sv/dau_axi_bar_job_sim.sv +195 -0
- dau_core/tests/sv/dau_axi_bar_job_tb.sv +130 -0
- dau_core/tests/sv/dau_axi_random_probe_sim.sv +73 -0
- dau_core/tests/sv/dau_bar_aggregation_tb.sv +151 -0
- dau_core/tests/sv/dau_bucket_agg_chain.sv +78 -0
- dau_core/tests/sv/dau_grouped_aggregation_tb.sv +170 -0
- dau_core/tests/sv/dau_hero_datapath.sv +230 -0
- dau_core/tests/sv/dau_int32_map_alu_tb.sv +170 -0
- dau_core/tests/sv/dau_int32_predicate_filter_tb.sv +165 -0
- dau_core/tests/sv/dau_record_batch_aggregation_tb.sv +214 -0
- dau_core/tests/sv/dau_stream_aggregation_tb.sv +170 -0
- dau_core/tests/sv/dau_wide_ingest_chain.sv +73 -0
- dau_core/tests/test_aggregation_goldens.py +43 -0
- dau_core/tests/test_aggregation_tile_hdl.py +71 -0
- dau_core/tests/test_benches_can_fail.py +125 -0
- dau_core/tests/test_capability_contract.py +235 -0
- dau_core/tests/test_composition.py +1342 -0
- dau_core/tests/test_contributed_cores.py +112 -0
- dau_core/tests/test_contributed_discovery.py +156 -0
- dau_core/tests/test_core_contract.py +38 -0
- dau_core/tests/test_cores.py +1006 -0
- dau_core/tests/test_datetime_format.py +125 -0
- dau_core/tests/test_design.py +58 -0
- dau_core/tests/test_docs.py +49 -0
- dau_core/tests/test_element_types.py +204 -0
- dau_core/tests/test_fixed_point_exp.py +145 -0
- dau_core/tests/test_fixed_point_ln.py +140 -0
- dau_core/tests/test_fixed_point_normal_cdf.py +72 -0
- dau_core/tests/test_generated_cores.py +131 -0
- dau_core/tests/test_hash_grouped.py +157 -0
- dau_core/tests/test_hdl_cocotb.py +1092 -0
- dau_core/tests/test_hdl_elaboration.py +167 -0
- dau_core/tests/test_hdl_parameter_bindings_resolve.py +87 -0
- dau_core/tests/test_hdl_sources.py +53 -0
- dau_core/tests/test_hdl_verilator.py +156 -0
- dau_core/tests/test_hero.py +163 -0
- dau_core/tests/test_lazy_exports.py +78 -0
- dau_core/tests/test_measured_from_matches_the_hdl.py +234 -0
- dau_core/tests/test_ohlcv_fixtures.py +82 -0
- dau_core/tests/test_operators.py +1359 -0
- dau_core/tests/test_output_beat_declarations.py +91 -0
- dau_core/tests/test_parameter_bounds_match_the_hdl.py +144 -0
- dau_core/tests/test_public_tile_surface.py +175 -0
- dau_core/tests/test_readme.py +26 -0
- dau_core/tests/test_record_batch.py +113 -0
- dau_core/tests/test_registers_and_capabilities.py +320 -0
- dau_core/tests/test_stream.py +73 -0
- dau_core/tests/test_temporal.py +242 -0
- dau_core/tests/test_validation_at_use.py +222 -0
- dau_core/workflows.py +53 -0
- dau_core-0.1.1.dist-info/METADATA +292 -0
- dau_core-0.1.1.dist-info/RECORD +211 -0
- dau_core-0.1.1.dist-info/WHEEL +4 -0
- dau_core-0.1.1.dist-info/entry_points.txt +2 -0
- dau_core-0.1.1.dist-info/licenses/LICENSE +201 -0
dau_core/__init__.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""dau-core: the tile palette, the device contracts and the goldens.
|
|
2
|
+
|
|
3
|
+
Exports load on first use (:mod:`dau_core._lazy`). Importing this package
|
|
4
|
+
must not import ccflow and its stack, because hydra's searchpath plugin
|
|
5
|
+
imports ``dau_core.config`` to discover what is installed, and discovery
|
|
6
|
+
wants none of these names.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from ._lazy import lazy_exports
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from .capabilities import DeviceCapabilities, DmaConstraints, OperatorCapability
|
|
15
|
+
from .capability_contract import (
|
|
16
|
+
CAPABILITY_CONTRACT_SCHEMA,
|
|
17
|
+
CapabilityContract,
|
|
18
|
+
LaneCapability,
|
|
19
|
+
TileCapability,
|
|
20
|
+
capability_contract,
|
|
21
|
+
opcode_bitmap,
|
|
22
|
+
tile_capability,
|
|
23
|
+
)
|
|
24
|
+
from .design import DauDesign, OperatorUnit, UnitKind, format_units, parse_units
|
|
25
|
+
from .operators import (
|
|
26
|
+
EmptyInputError,
|
|
27
|
+
UnsupportedAggregationError,
|
|
28
|
+
aggregate_values,
|
|
29
|
+
pack_splitters,
|
|
30
|
+
synthetic_values,
|
|
31
|
+
)
|
|
32
|
+
from .registers import (
|
|
33
|
+
CURRENT_REGISTER_MAP_VERSION,
|
|
34
|
+
DAU_MAGIC_WORD,
|
|
35
|
+
DEFAULT_STREAM_JOB_REGISTER_CONTRACT,
|
|
36
|
+
JobControlFlag,
|
|
37
|
+
JobStatusFlag,
|
|
38
|
+
RegisterBlockSnapshot,
|
|
39
|
+
RegisterMapVersion,
|
|
40
|
+
RegisterOffset,
|
|
41
|
+
RegisterWriteError,
|
|
42
|
+
ResetScope,
|
|
43
|
+
StreamJobRegisterContract,
|
|
44
|
+
StreamProtocolVersion,
|
|
45
|
+
)
|
|
46
|
+
from .stream import (
|
|
47
|
+
DEFAULT_RECORD_BATCH_LIMITS,
|
|
48
|
+
BatchHeader,
|
|
49
|
+
ColumnDescriptor,
|
|
50
|
+
GroupRole,
|
|
51
|
+
InvalidStreamError,
|
|
52
|
+
LogicalType,
|
|
53
|
+
OperationCode,
|
|
54
|
+
OperationDescriptor,
|
|
55
|
+
PolicyFlag,
|
|
56
|
+
RecordBatchLimits,
|
|
57
|
+
StreamAggregationErrorCode,
|
|
58
|
+
StreamFlag,
|
|
59
|
+
validate_record_batch,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
__version__ = "0.1.1"
|
|
63
|
+
|
|
64
|
+
# {submodule: the names it exports}. ``__all__`` and the lazy resolver both
|
|
65
|
+
# come from this one declaration, so a name cannot be in one and missing
|
|
66
|
+
# from the other; ``tests/test_lazy_exports.py`` checks it against the
|
|
67
|
+
# TYPE_CHECKING block above and resolves every name.
|
|
68
|
+
_EXPORTS = {
|
|
69
|
+
"capabilities": ("DeviceCapabilities", "DmaConstraints", "OperatorCapability"),
|
|
70
|
+
"capability_contract": (
|
|
71
|
+
"CAPABILITY_CONTRACT_SCHEMA",
|
|
72
|
+
"CapabilityContract",
|
|
73
|
+
"LaneCapability",
|
|
74
|
+
"TileCapability",
|
|
75
|
+
"capability_contract",
|
|
76
|
+
"opcode_bitmap",
|
|
77
|
+
"tile_capability",
|
|
78
|
+
),
|
|
79
|
+
"design": ("DauDesign", "OperatorUnit", "UnitKind", "format_units", "parse_units"),
|
|
80
|
+
"operators": (
|
|
81
|
+
"EmptyInputError",
|
|
82
|
+
"UnsupportedAggregationError",
|
|
83
|
+
"aggregate_values",
|
|
84
|
+
"pack_splitters",
|
|
85
|
+
"synthetic_values",
|
|
86
|
+
),
|
|
87
|
+
"registers": (
|
|
88
|
+
"CURRENT_REGISTER_MAP_VERSION",
|
|
89
|
+
"DAU_MAGIC_WORD",
|
|
90
|
+
"DEFAULT_STREAM_JOB_REGISTER_CONTRACT",
|
|
91
|
+
"JobControlFlag",
|
|
92
|
+
"JobStatusFlag",
|
|
93
|
+
"RegisterBlockSnapshot",
|
|
94
|
+
"RegisterMapVersion",
|
|
95
|
+
"RegisterOffset",
|
|
96
|
+
"RegisterWriteError",
|
|
97
|
+
"ResetScope",
|
|
98
|
+
"StreamJobRegisterContract",
|
|
99
|
+
"StreamProtocolVersion",
|
|
100
|
+
),
|
|
101
|
+
"stream": (
|
|
102
|
+
"DEFAULT_RECORD_BATCH_LIMITS",
|
|
103
|
+
"BatchHeader",
|
|
104
|
+
"ColumnDescriptor",
|
|
105
|
+
"GroupRole",
|
|
106
|
+
"InvalidStreamError",
|
|
107
|
+
"LogicalType",
|
|
108
|
+
"OperationCode",
|
|
109
|
+
"OperationDescriptor",
|
|
110
|
+
"PolicyFlag",
|
|
111
|
+
"RecordBatchLimits",
|
|
112
|
+
"StreamAggregationErrorCode",
|
|
113
|
+
"StreamFlag",
|
|
114
|
+
"validate_record_batch",
|
|
115
|
+
),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
__getattr__, __all__ = lazy_exports(__name__, _EXPORTS)
|
dau_core/_lazy.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Import a package's exports on first use.
|
|
2
|
+
|
|
3
|
+
Every model in this stack is a ccflow model, and importing ccflow imports
|
|
4
|
+
csp, pandas and the rest of its stack: about 0.9 s. A package whose
|
|
5
|
+
``__init__`` imports its model modules therefore pays all of it just to be
|
|
6
|
+
imported -- including when hydra's searchpath plugin imports the config
|
|
7
|
+
package to discover what is installed, which is the slow step in composing
|
|
8
|
+
the registry and wants none of those names.
|
|
9
|
+
|
|
10
|
+
So a package declares which submodule provides each exported name and this
|
|
11
|
+
builds the ``__getattr__`` that imports the one submodule the name needs.
|
|
12
|
+
The names stay importable exactly as before (``from dau_core import
|
|
13
|
+
RegisterOffset``), and a type checker reads them from the ``TYPE_CHECKING``
|
|
14
|
+
block the package keeps beside the declaration.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from importlib import import_module
|
|
21
|
+
|
|
22
|
+
__all__ = ("lazy_exports",)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def lazy_exports(package: str, exports: dict[str, tuple[str, ...]]) -> tuple[Callable[[str], object], list[str]]:
|
|
26
|
+
"""A module ``__getattr__`` and ``__all__`` for ``{submodule: names}``.
|
|
27
|
+
|
|
28
|
+
Returns both so the two cannot disagree: a name exported by nothing is
|
|
29
|
+
not reachable, and a name declared here and missing from its submodule
|
|
30
|
+
raises on first access rather than at import, which a guard test is what
|
|
31
|
+
turns back into an import-time error.
|
|
32
|
+
"""
|
|
33
|
+
providers = {}
|
|
34
|
+
for module, names in exports.items():
|
|
35
|
+
for name in names:
|
|
36
|
+
if name in providers:
|
|
37
|
+
raise ValueError(f"{package}: {name!r} is exported by both {providers[name]!r} and {module!r}")
|
|
38
|
+
providers[name] = module
|
|
39
|
+
|
|
40
|
+
def getattr_(name: str) -> object:
|
|
41
|
+
module = providers.get(name)
|
|
42
|
+
if module is None:
|
|
43
|
+
raise AttributeError(f"module {package!r} has no attribute {name!r}")
|
|
44
|
+
return getattr(import_module(f"{package}.{module}"), name)
|
|
45
|
+
|
|
46
|
+
return getattr_, sorted(providers)
|
dau_core/capabilities.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from .registers import RegisterMapVersion, StreamProtocolVersion
|
|
6
|
+
from .stream import LogicalType, OperationCode
|
|
7
|
+
|
|
8
|
+
# DAU platform variant 1 (dpv1) identity — the single home for these
|
|
9
|
+
# values; drivers, orchestration, and tooling import them rather than
|
|
10
|
+
# repeating literals. The wire identity word is the platform-variant name
|
|
11
|
+
# "DPV1" (the board's own product name is an implementation detail); shells
|
|
12
|
+
# report it in the PLATFORM_ID register.
|
|
13
|
+
DPV1_PLATFORM_ID = "DPV1"
|
|
14
|
+
XILINX_VENDOR_ID = 0x10EE
|
|
15
|
+
DPV1_DEVICE_ID = 0x7011
|
|
16
|
+
DPV1_PCI_ID = f"{XILINX_VENDOR_ID:04x}:{DPV1_DEVICE_ID:04x}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class DmaConstraints:
|
|
21
|
+
alignment_bytes: int
|
|
22
|
+
descriptor_bytes: int
|
|
23
|
+
max_transfer_bytes: int
|
|
24
|
+
read_channels: int
|
|
25
|
+
write_channels: int
|
|
26
|
+
|
|
27
|
+
def __post_init__(self) -> None:
|
|
28
|
+
if self.alignment_bytes <= 0:
|
|
29
|
+
raise ValueError("alignment_bytes must be positive")
|
|
30
|
+
if self.descriptor_bytes <= 0:
|
|
31
|
+
raise ValueError("descriptor_bytes must be positive")
|
|
32
|
+
if self.max_transfer_bytes <= 0:
|
|
33
|
+
raise ValueError("max_transfer_bytes must be positive")
|
|
34
|
+
if self.read_channels < 0 or self.write_channels < 0:
|
|
35
|
+
raise ValueError("DMA channel counts cannot be negative")
|
|
36
|
+
|
|
37
|
+
def is_aligned(self, value: int) -> bool:
|
|
38
|
+
return value % self.alignment_bytes == 0
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def max_aligned_transfer_bytes(self) -> int:
|
|
42
|
+
return self.max_transfer_bytes - (self.max_transfer_bytes % self.alignment_bytes)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class OperatorCapability:
|
|
47
|
+
opcode: OperationCode
|
|
48
|
+
input_types: tuple[LogicalType, ...]
|
|
49
|
+
output_type: LogicalType
|
|
50
|
+
supports_group_by: bool
|
|
51
|
+
|
|
52
|
+
def supports(self, opcode: OperationCode, logical_type: LogicalType) -> bool:
|
|
53
|
+
return self.opcode == opcode and logical_type in self.input_types
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class DeviceCapabilities:
|
|
58
|
+
"""What a probed device advertises.
|
|
59
|
+
|
|
60
|
+
``operators`` are hardware capabilities — opcodes some tile executes in
|
|
61
|
+
HDL. ``host_operators`` are protocol-level capabilities the host derives
|
|
62
|
+
from hardware results (AVERAGE = host divide of SUM/COUNT); they must
|
|
63
|
+
never be submitted to the device as operations.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
platform_id: str
|
|
67
|
+
pci_vendor_id: int
|
|
68
|
+
pci_device_id: int
|
|
69
|
+
register_map_version: RegisterMapVersion
|
|
70
|
+
stream_protocol_version: StreamProtocolVersion
|
|
71
|
+
dma: DmaConstraints
|
|
72
|
+
operators: tuple[OperatorCapability, ...]
|
|
73
|
+
host_operators: tuple[OperatorCapability, ...] = ()
|
|
74
|
+
|
|
75
|
+
def supports(self, opcode: OperationCode, logical_type: LogicalType) -> bool:
|
|
76
|
+
return any(operator.supports(opcode, logical_type) for operator in self.operators)
|
|
77
|
+
|
|
78
|
+
def supports_host_computed(self, opcode: OperationCode, logical_type: LogicalType) -> bool:
|
|
79
|
+
return any(operator.supports(opcode, logical_type) for operator in self.host_operators)
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Composition-derived capability contract: the single source of truth for
|
|
2
|
+
what a generated shell can do.
|
|
3
|
+
|
|
4
|
+
``capability_contract(spec)`` derives the contract from the same
|
|
5
|
+
``ScanCompositionSpec`` that generates the shell top, using the registry's
|
|
6
|
+
declared HDL facts (opcodes, input types, count ports, contract kinds, row
|
|
7
|
+
capacities). Three consumers read it:
|
|
8
|
+
|
|
9
|
+
- the identity register block (``operator_bitmap`` / ``lane_count`` /
|
|
10
|
+
``host_opcode_bitmap`` / ``sort_capacity`` words, register map 0.2);
|
|
11
|
+
- the shell-build artlink manifest (``to_mapping()``, JSON-able);
|
|
12
|
+
- host planners (``to_design()`` for the pushdown scheduler,
|
|
13
|
+
driver capability decode via the manifest mapping).
|
|
14
|
+
|
|
15
|
+
Hardware truth vs protocol truth is explicit: ``operator_bitmap`` carries
|
|
16
|
+
only OperationCodes some tile executes in HDL (MIN/MAX/SUM/COUNT);
|
|
17
|
+
AVERAGE is never a hardware opcode — when a tile provides both SUM and
|
|
18
|
+
COUNT it appears in ``host_opcodes`` (the host divides the results at
|
|
19
|
+
protocol level), never in ``opcodes``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections.abc import Mapping
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from pydantic import Field
|
|
28
|
+
|
|
29
|
+
from .composition import ScanCompositionSpec, TileSpec
|
|
30
|
+
from .cores import ContractKind
|
|
31
|
+
from .design import DauDesign, OperatorUnit, UnitKind
|
|
32
|
+
from .models import CheckedModel
|
|
33
|
+
from .registers import CURRENT_REGISTER_MAP_VERSION, RegisterMapVersion
|
|
34
|
+
from .registry import core
|
|
35
|
+
from .stream import LogicalType, OperationCode
|
|
36
|
+
|
|
37
|
+
CAPABILITY_CONTRACT_SCHEMA = 1
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def opcode_bitmap(opcodes: tuple[OperationCode, ...]) -> int:
|
|
41
|
+
"""The register encoding: bit ``1 << opcode`` per OperationCode."""
|
|
42
|
+
bitmap = 0
|
|
43
|
+
for opcode in opcodes:
|
|
44
|
+
bitmap |= 1 << int(opcode)
|
|
45
|
+
return bitmap
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TileCapability(CheckedModel):
|
|
49
|
+
"""One operator tile instance as the contract describes it: registry
|
|
50
|
+
identity, stream contract, HDL opcode truth, and instance config."""
|
|
51
|
+
|
|
52
|
+
core: str
|
|
53
|
+
module: str
|
|
54
|
+
contract_kind: ContractKind | None
|
|
55
|
+
count_port: str | None
|
|
56
|
+
opcodes: tuple[OperationCode, ...] = ()
|
|
57
|
+
input_types: tuple[LogicalType, ...] = ()
|
|
58
|
+
unit_kind: UnitKind | None = None
|
|
59
|
+
row_capacity: int | None = None
|
|
60
|
+
config: tuple[tuple[str, str], ...] = ()
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def host_opcodes(self) -> tuple[OperationCode, ...]:
|
|
64
|
+
"""Protocol-level opcodes the host can compute from this tile's
|
|
65
|
+
hardware results: AVERAGE = SUM/COUNT divided host-side. Never
|
|
66
|
+
hardware opcodes and never advertised as such."""
|
|
67
|
+
if OperationCode.SUM in self.opcodes and OperationCode.COUNT in self.opcodes:
|
|
68
|
+
return (OperationCode.AVERAGE,)
|
|
69
|
+
return ()
|
|
70
|
+
|
|
71
|
+
def to_mapping(self) -> dict[str, Any]:
|
|
72
|
+
return {
|
|
73
|
+
"core": self.core,
|
|
74
|
+
"module": self.module,
|
|
75
|
+
"contract_kind": self.contract_kind.value if self.contract_kind is not None else None,
|
|
76
|
+
"count_port": self.count_port,
|
|
77
|
+
"opcodes": [opcode.name.lower() for opcode in self.opcodes],
|
|
78
|
+
"input_types": [logical_type.name.lower() for logical_type in self.input_types],
|
|
79
|
+
"unit_kind": self.unit_kind.value if self.unit_kind is not None else None,
|
|
80
|
+
"row_capacity": self.row_capacity,
|
|
81
|
+
"config": dict(self.config),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def from_mapping(cls, mapping: Mapping[str, Any]) -> TileCapability:
|
|
86
|
+
return cls(
|
|
87
|
+
core=mapping["core"],
|
|
88
|
+
module=mapping["module"],
|
|
89
|
+
contract_kind=ContractKind(mapping["contract_kind"]) if mapping.get("contract_kind") else None,
|
|
90
|
+
count_port=mapping.get("count_port"),
|
|
91
|
+
opcodes=tuple(OperationCode[token.upper()] for token in mapping.get("opcodes", ())),
|
|
92
|
+
input_types=tuple(LogicalType[token.upper()] for token in mapping.get("input_types", ())),
|
|
93
|
+
unit_kind=UnitKind(mapping["unit_kind"]) if mapping.get("unit_kind") else None,
|
|
94
|
+
row_capacity=mapping.get("row_capacity"),
|
|
95
|
+
config=tuple(sorted((str(key), str(value)) for key, value in dict(mapping.get("config", {})).items())),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class LaneCapability(CheckedModel):
|
|
100
|
+
"""One scan lane: the terminal operator tile, its optional partition
|
|
101
|
+
filter, and the ordered mid-lane operator ``chain`` between them."""
|
|
102
|
+
|
|
103
|
+
tile: TileCapability
|
|
104
|
+
partition: TileCapability | None = None
|
|
105
|
+
chain: tuple[TileCapability, ...] = ()
|
|
106
|
+
|
|
107
|
+
def to_mapping(self) -> dict[str, Any]:
|
|
108
|
+
return {
|
|
109
|
+
"tile": self.tile.to_mapping(),
|
|
110
|
+
"partition": self.partition.to_mapping() if self.partition is not None else None,
|
|
111
|
+
"chain": [stage.to_mapping() for stage in self.chain],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def from_mapping(cls, mapping: Mapping[str, Any]) -> LaneCapability:
|
|
116
|
+
partition = mapping.get("partition")
|
|
117
|
+
return cls(
|
|
118
|
+
tile=TileCapability.from_mapping(mapping["tile"]),
|
|
119
|
+
partition=TileCapability.from_mapping(partition) if partition else None,
|
|
120
|
+
chain=tuple(TileCapability.from_mapping(stage) for stage in mapping.get("chain", ())),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CapabilityContract(CheckedModel):
|
|
125
|
+
"""The advertised capability truth of one composed design.
|
|
126
|
+
|
|
127
|
+
A config-data model (it is what the shell-build manifest carries and
|
|
128
|
+
what a planner matches against), so a ccflow model like the rest of the
|
|
129
|
+
registry's declarations; ``to_mapping``/``from_mapping`` remain the
|
|
130
|
+
manifest serialization, unchanged, because manifests already on disk
|
|
131
|
+
carry that form."""
|
|
132
|
+
|
|
133
|
+
name: str
|
|
134
|
+
lanes: tuple[LaneCapability, ...]
|
|
135
|
+
partitioner: TileCapability | None = None
|
|
136
|
+
register_map_version: RegisterMapVersion = Field(default_factory=lambda: CURRENT_REGISTER_MAP_VERSION)
|
|
137
|
+
# whether the lanes' chain config ports are runtime registers the host
|
|
138
|
+
# rewrites per job, so a planner matches this design structurally
|
|
139
|
+
# (enough stages of the right kinds) instead of config for config. A
|
|
140
|
+
# fact about the bitstream, carried in its manifest; never a caller's
|
|
141
|
+
# declaration.
|
|
142
|
+
reconfigurable: bool = False
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def lane_count(self) -> int:
|
|
146
|
+
return len(self.lanes)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def hardware_opcodes(self) -> tuple[OperationCode, ...]:
|
|
150
|
+
"""Union of the lanes' HDL opcodes (terminal tiles and chain
|
|
151
|
+
stages), ordered by opcode value."""
|
|
152
|
+
opcodes = {opcode for lane in self.lanes for tile in (lane.tile, *lane.chain) for opcode in tile.opcodes}
|
|
153
|
+
return tuple(sorted(opcodes, key=int))
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def host_opcodes(self) -> tuple[OperationCode, ...]:
|
|
157
|
+
"""Protocol-level opcodes derivable host-side (AVERAGE when a tile
|
|
158
|
+
provides both SUM and COUNT); explicitly not hardware opcodes."""
|
|
159
|
+
opcodes = {opcode for lane in self.lanes for tile in (lane.tile, *lane.chain) for opcode in tile.host_opcodes}
|
|
160
|
+
return tuple(sorted(opcodes, key=int))
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def operator_bitmap(self) -> int:
|
|
164
|
+
return opcode_bitmap(self.hardware_opcodes)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def host_opcode_bitmap(self) -> int:
|
|
168
|
+
return opcode_bitmap(self.host_opcodes)
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def sort_capacity(self) -> int:
|
|
172
|
+
capacities = [lane.tile.row_capacity or 0 for lane in self.lanes if lane.tile.unit_kind == UnitKind.SORT]
|
|
173
|
+
return max(capacities, default=0)
|
|
174
|
+
|
|
175
|
+
def to_design(self) -> DauDesign:
|
|
176
|
+
"""The operator inventory this contract advertises: identical lane
|
|
177
|
+
tiles that contribute a design unit collapse into one counted unit."""
|
|
178
|
+
counts: dict[TileCapability, int] = {}
|
|
179
|
+
for lane in self.lanes:
|
|
180
|
+
if lane.tile.unit_kind is None:
|
|
181
|
+
continue
|
|
182
|
+
key = TileCapability(
|
|
183
|
+
core=lane.tile.core,
|
|
184
|
+
module=lane.tile.module,
|
|
185
|
+
contract_kind=lane.tile.contract_kind,
|
|
186
|
+
count_port=lane.tile.count_port,
|
|
187
|
+
opcodes=lane.tile.opcodes,
|
|
188
|
+
input_types=lane.tile.input_types,
|
|
189
|
+
unit_kind=lane.tile.unit_kind,
|
|
190
|
+
row_capacity=lane.tile.row_capacity,
|
|
191
|
+
)
|
|
192
|
+
counts[key] = counts.get(key, 0) + 1
|
|
193
|
+
unit_list: list[OperatorUnit] = []
|
|
194
|
+
for tile, count in counts.items():
|
|
195
|
+
if tile.unit_kind is not None:
|
|
196
|
+
unit_list.append(OperatorUnit(kind=tile.unit_kind, count=count, opcodes=tile.opcodes, input_types=tile.input_types))
|
|
197
|
+
units = tuple(unit_list)
|
|
198
|
+
return DauDesign(name=self.name, units=units)
|
|
199
|
+
|
|
200
|
+
def to_mapping(self) -> dict[str, Any]:
|
|
201
|
+
"""JSON-able serialization for the shell-build manifest."""
|
|
202
|
+
return {
|
|
203
|
+
"contract": "dau-capability-contract",
|
|
204
|
+
"schema": CAPABILITY_CONTRACT_SCHEMA,
|
|
205
|
+
"name": self.name,
|
|
206
|
+
"register_map_version": f"{self.register_map_version.major}.{self.register_map_version.minor}",
|
|
207
|
+
"operator_bitmap": f"0x{self.operator_bitmap:08x}",
|
|
208
|
+
"host_opcode_bitmap": f"0x{self.host_opcode_bitmap:08x}",
|
|
209
|
+
"lane_count": self.lane_count,
|
|
210
|
+
"sort_capacity": self.sort_capacity,
|
|
211
|
+
"lanes": [lane.to_mapping() for lane in self.lanes],
|
|
212
|
+
"partitioner": self.partitioner.to_mapping() if self.partitioner is not None else None,
|
|
213
|
+
"reconfigurable": self.reconfigurable,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def from_mapping(cls, mapping: Mapping[str, Any]) -> CapabilityContract:
|
|
218
|
+
if mapping.get("contract") != "dau-capability-contract":
|
|
219
|
+
raise ValueError("not a dau capability contract mapping")
|
|
220
|
+
schema = mapping.get("schema")
|
|
221
|
+
if schema != CAPABILITY_CONTRACT_SCHEMA:
|
|
222
|
+
raise ValueError(f"unsupported capability contract schema {schema!r}")
|
|
223
|
+
major, _, minor = str(mapping["register_map_version"]).partition(".")
|
|
224
|
+
partitioner = mapping.get("partitioner")
|
|
225
|
+
return cls(
|
|
226
|
+
name=mapping["name"],
|
|
227
|
+
lanes=tuple(LaneCapability.from_mapping(lane) for lane in mapping.get("lanes", ())),
|
|
228
|
+
partitioner=TileCapability.from_mapping(partitioner) if partitioner else None,
|
|
229
|
+
register_map_version=RegisterMapVersion(int(major), int(minor)),
|
|
230
|
+
# absent in manifests written before the fact was advertised:
|
|
231
|
+
# every one of those designs binds its configs as literals
|
|
232
|
+
reconfigurable=bool(mapping.get("reconfigurable", False)),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def tile_capability(tile: TileSpec | str) -> TileCapability:
|
|
237
|
+
"""The registry-declared capability facts of one tile instance."""
|
|
238
|
+
spec = TileSpec(core=tile) if isinstance(tile, str) else tile
|
|
239
|
+
definition = core(spec.core)
|
|
240
|
+
return TileCapability(
|
|
241
|
+
core=definition.name,
|
|
242
|
+
module=definition.module,
|
|
243
|
+
contract_kind=definition.contract_kind,
|
|
244
|
+
count_port=definition.count_port,
|
|
245
|
+
opcodes=definition.opcodes,
|
|
246
|
+
input_types=definition.opcode_input_types,
|
|
247
|
+
unit_kind=definition.unit_kind,
|
|
248
|
+
row_capacity=definition.row_capacity,
|
|
249
|
+
config=tuple(sorted((str(key), str(value)) for key, value in spec.config.items())),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def capability_contract(spec: ScanCompositionSpec) -> CapabilityContract:
|
|
254
|
+
"""Derive the capability contract from the composition that generates
|
|
255
|
+
the shell top — same spec, same registry, one source of truth.
|
|
256
|
+
|
|
257
|
+
``reconfigurable`` stays false: the generated walker binds every config
|
|
258
|
+
port to the literal the composition names, so no composed design has
|
|
259
|
+
runtime config registers yet. When the generator can bind them to
|
|
260
|
+
registers, the composition will say so and this is where the contract
|
|
261
|
+
reads it."""
|
|
262
|
+
spec = spec.check()
|
|
263
|
+
return CapabilityContract(
|
|
264
|
+
name=spec.name,
|
|
265
|
+
lanes=tuple(
|
|
266
|
+
LaneCapability(
|
|
267
|
+
tile=tile_capability(lane.tile),
|
|
268
|
+
partition=tile_capability(lane.partition) if lane.partition is not None else None,
|
|
269
|
+
chain=tuple(tile_capability(stage) for stage in lane.chain),
|
|
270
|
+
)
|
|
271
|
+
for lane in spec.lanes
|
|
272
|
+
),
|
|
273
|
+
partitioner=tile_capability(spec.partitioner) if spec.partitioner is not None else None,
|
|
274
|
+
)
|