Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4a15ed5
Add typed LogicArrayOf support
desmonddak Sep 4, 2026
e80fba1
missing port management
desmonddak Sep 5, 2026
8c604e3
fixed more corner cases than reported on LogicArrayOf mostly cloning …
desmonddak Sep 9, 2026
880af94
restructure `LogicArrayOf` and address review items
desmonddak Sep 9, 2026
0741a50
handle mixed net kinds and fix PairInterface cloning
desmonddak Sep 9, 2026
23c9fa7
rearchitected around TypedLogicArray/TypedLogicValue dichotomy
desmonddak Sep 10, 2026
f196b16
flattenOuter creates Logic not Const and nested arrays need codecs fo…
desmonddak Sep 10, 2026
c5f74cd
missed an empty dimension test
desmonddak Sep 10, 2026
a7b22eb
remove redundant methods and rename the TypedLogicValueArray
desmonddak Sep 11, 2026
cebaaca
remove flattenNestedDimensions and document retained method justifica…
desmonddak Sep 11, 2026
edaaedc
leftover code
desmonddak Sep 11, 2026
f2d1b5c
updated doc on typed value-array api
desmonddak Sep 11, 2026
4c3b10f
reduced reshape/transform operations to not involve connectivity, jus…
desmonddak Sep 12, 2026
8623115
further API simplifications
desmonddak Sep 13, 2026
18e8c8b
removed more redundant API elements
desmonddak Sep 14, 2026
4d1b71a
Merge branch 'main' into feature/logic-array-of
desmonddak Sep 14, 2026
0106e3e
updated expected value for packed value
desmonddak Sep 14, 2026
f8d9057
major push into synthesis for nested structures
desmonddak Sep 15, 2026
9f4bacc
remove rohd_hierarchy dependency (used during testing)
desmonddak Sep 15, 2026
02a8598
updated documentation on the LogicArray class hierarchy
desmonddak Sep 15, 2026
a8b1aeb
net bases issue, and partial array ranges fix
desmonddak Sep 15, 2026
b3e941d
fix CI failure
desmonddak Sep 15, 2026
5f0bc93
Merge branch 'main' into feature/logic-array-of
desmonddak Sep 15, 2026
6456cd6
updated flutter version for material-ui but checked that dart will no…
desmonddak Sep 15, 2026
e4e2f5d
conflict resolution
desmonddak Sep 15, 2026
511f836
added change descritptions to round out our PR
desmonddak Sep 15, 2026
8b7cce1
removed accidental checkin of conflict markers
desmonddak Sep 15, 2026
2af7e48
flutter 3.44 aligned
desmonddak Sep 16, 2026
9fc246e
better handling of inout, complex use of typed arrays in typed arrays…
desmonddak Sep 17, 2026
afed260
several simulator-related issues with workarounds
desmonddak Sep 17, 2026
ac974ff
comment out unpublished link for now
desmonddak Sep 17, 2026
2b204fb
fix tests to use BigInt due to Node failure, add TypedLogicArray nami…
desmonddak Sep 17, 2026
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
6 changes: 6 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",

"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "24"
}
},

"updateContentCommand": "tool/gh_codespaces/run_setup.sh",

"customizations": {
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

## Next Release

- Added `TypedLogicArray<TLogic, TValue>` for multidimensional arrays with fixed typed hardware elements and associated semantic values, including typed snapshots and ports, traversal, cloning, net support, and synthesis. Added shaped `LogicValueArray` and codec-backed `TypedLogicValueArray<TValue>` values with nested construction, packed `LogicValue` interoperability, and hardware-array assignment. Existing `LogicArray`, `LogicValueArray`, and concrete array port APIs remain available as specializations (<https://github.com/intel/rohd/pull/686>).
- Refined the typed-array hierarchy so `LogicArray` directly specializes `TypedLogicArray<Logic, LogicValue>`, and strengthened custom `TypedLogicArray` cloning to preserve runtime types, codecs, unpacked dimensions, and constructor-only naming prefixes without exposing them as public axis metadata (<https://github.com/intel/rohd/pull/686>).
- Expanded `TypedLogicArray` support for recursively nested typed and ordinary arrays, structures containing array fields, mixed packed/unpacked dimensions, structured net and inout ports, and recursive netlist type metadata. Fixed generated SystemVerilog for array-valued elements to use packed offsets consistent with declarations and module-port connections, avoiding invalid chained selections in Icarus and Verilator (<https://github.com/intel/rohd/pull/686>).
- Preserved configured SystemVerilog output object types for nested arrays and added the opt-in `iverilogWorkaroundForUnpackedArrayVariables` setting for child-driven unpacked array variables that remain unknown in Icarus Verilog 12.0 (<https://github.com/intel/rohd/pull/686>).
- Allowed connected, equivalent `reserved` and `renameable` signals with matching generated base names to share a declaration, including internal signals and port aliases, in generated outputs. Separate aliases may disappear and connected same-name reserved signals no longer cause naming errors; unrelated reserved-name collisions still fail (<https://github.com/intel/rohd/pull/712>).
- Fixed illegal scalar connections to singleton unpacked-array inputs when inlining constants or live signals into generated SystemVerilog (<https://github.com/intel/rohd/pull/714>).
- Added targeted Verilator compilation and two-state vector simulation checks, including a `buildOnly` mode, required in native CI and explicitly skipped locally when Verilator is unavailable (<https://github.com/intel/rohd/pull/714>).
Expand Down
148 changes: 145 additions & 3 deletions doc/user_guide/_docs/A20-logic-arrays.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
---
title: "Logic Arrays"
permalink: /docs/logic-arrays/
last_modified_at: 2022-6-5
last_modified_at: 2026-09-16
toc: true
---

A [`LogicArray`](https://intel.github.io/rohd/rohd/LogicArray-class.html) is a type of `LogicStructure` that mirrors multi-dimensional arrays in hardware languages like SystemVerilog. In ROHD, the `LogicArray` type inherits a lot of functionality from `LogicStructure`, so it can behave like a `Logic` where it makes sense or be individually referenced in other places.
Use [`LogicArray`](https://intel.github.io/rohd/rohd/LogicArray-class.html)
for multidimensional arrays of ordinary `Logic`. Use
`TypedLogicArray`
<!-- [`TypedLogicArray`](https://intel.github.io/rohd/rohd/TypedLogicArray-class.html) -->
when each array position has a specialized hardware type and semantic value
type. Both are `LogicStructure`s, so they can be indexed as arrays while still
participating in ordinary packed `Logic` assignments and operations.

`LogicArray`s can be constructed easily using the constructor:

Expand All @@ -22,6 +28,140 @@ LogicArray([5, 5, 5], 128);

As long as the total width of a `LogicArray` and another type of `Logic` (including `Logic`, `LogicStructure`, and another `LogicArray`) are the same, assignments and bitwise operations will work in per-element order. This means you can assign two `LogicArray`s of different dimensions to each other as long as the total width matches.

## Typed arrays

Use `TypedLogicArray<TLogic, TValue>` when every array position has the same
specialized hardware type and associated semantic value type. `LogicArray` is
the ordinary `TypedLogicArray<Logic, LogicValue>` specialization.

For example, these sample hardware and value types preserve named fields in hardware while exposing typed snapshots:

```dart
class Sample extends LogicStructure {
final Logic data;
final Logic valid;

factory Sample({String? name}) => Sample._(
Logic(name: 'data', width: 8),
Logic(name: 'valid'),
name: name ?? 'sample',
);

Sample._(this.data, this.valid, {required String name})
: super([data, valid], name: name);

@override
Sample clone({String? name}) => Sample(name: name ?? this.name);
}

class SampleValue {
final LogicValue value;

SampleValue(this.value);
}

SampleValue decodeSample(LogicValue value) => SampleValue(value);
LogicValue encodeSample(SampleValue value) => value.value;

const sampleCodec = LogicValueCodec<SampleValue>(
decode: decodeSample,
encode: encodeSample,
);

final samples = TypedLogicArray<Sample, SampleValue>(
[2, 3],
Sample.new,
valueCodec: sampleCodec,
);

final bottomRightData = samples.at([1, 2]).data;
final TypedLogicValueArray<SampleValue> currentSamples = samples.value;
```

The element builder must consistently produce the configured type, width,
ordered structure, and net kind. Every element must be entirely variable or
entirely net; a structure cannot mix `Logic` and `LogicNet` leaves. Use
`elementCompatibility` when equal width is not enough to establish compatible
representations.

`valueCodec` is optional only when `TValue` is `LogicValue`. A custom codec
should decode every four-state value that can appear in its hardware.
`dimensionNames` controls child naming during construction and cloning; it is
not public axis metadata. See the
`TypedLogicArray` API documentation
<!-- [`TypedLogicArray` API documentation](https://intel.github.io/rohd/rohd/TypedLogicArray-class.html) -->
for the complete constructor and cloning contracts.

Hardware shape changes should use ordinary construction and connection APIs rather than specialized typed-array adapters. Construct a new `TypedLogicArray` with the desired dimensions and builder, then connect it with `gets`/`<=` when row-major assignment is sufficient. For a transpose, connect corresponding coordinates explicitly with `indexedElements` and `at`; whole-array assignment does not infer a permutation. This keeps construction disconnected and leaves driver ownership with the caller.

### Traversal boundaries

These APIs intentionally stop at different boundaries:

- `elements` contains the immediate children of the outermost structure or array level.
- `TypedLogicArray<TLogic, TValue>.arrayElements` traverses exactly the dimensions declared by that array and then stops at `TLogic`. It is an unmodifiable `List<TLogic>`.
- `indexedElements` pairs those same typed array elements with their row-major multidimensional indices.
- `at(indices)` returns one typed array element.
- `leafElements` recursively traverses every nested array and structure until it reaches non-structure signals.

For example, a `[2, 3]` array of two-field `Sample`s has six `arrayElements` and twelve recursive `leafElements`. A `[2]` array whose elements are `[3]` arrays has two `arrayElements` and six recursive leaves. An eight-bit `Logic` is one leaf, not eight.

A `TypedLogicArray` element can be a `LogicStructure`, but it must be driveable.
Direct `Const` elements and structures containing a `Const` are rejected.
Nested arrays are supported for ROHD construction, packed assignment, and
`arrayElements`, `at`, and `indexedElements` traversal.

At a generated SystemVerilog boundary, a nested array-valued element may occupy
packed bits within its containing element rather than introduce another visible
array dimension. Its row-major value and structure-field order are preserved;
use `at`, `indexedElements`, and named fields in ROHD instead of depending on
the textual shape of generated selections.

Some simulators do not accept unpacked `inout` array ports. Prefer packed
outer dimensions for portable bidirectional interfaces.

Icarus Verilog 12.0 can leave child-driven unpacked array variables unknown
during simulation. When targeting that tool, enable
`SystemVerilogSynthesizerConfiguration.iverilogWorkaroundForUnpackedArrayVariables`.

Custom typed-array subclasses should override `createClone` to preserve their
runtime type and constructor configuration. See the API documentation for the
full subclassing contract.

## Value-domain arrays

Use `LogicValueArray` for fixed-width array data outside the hardware graph. Nested lists are the ordinary construction form and describe the shape directly. Flat row-major values use an explicitly named `fromFlat` constructor with shape metadata:

```dart
final values = LogicValueArray.fromInts(
[
[1, 2, 3],
[4, 5, 6],
],
elementWidth: 8,
);
final sameValues = LogicValueArray.fromFlatInts(
[2, 3],
[1, 2, 3, 4, 5, 6],
elementWidth: 8,
);
final emptyRows =
LogicValueArray.fromFlat([2, 0], const [], elementWidth: 8);

final transposed = values.transpose2D(); // Dimensions: [3, 2]
final signals = values.toLogicArray(name: 'values'); // signals are driven by values
```

Nested constructors reject ragged rows, inconsistent nesting depth, and mismatched element widths. Empty nested input cannot reveal the element width or trailing dimensions, so it must use `fromFlat`. `majorSlices` iterates the outer dimension rather than the total element count, so a `[2, 0]` value contains two empty `[0]` slices and can round-trip through `stack`.

`TypedLogicValueArray<T>` adds a `LogicValueCodec<T>` for application-level values. `LogicValueArray` remains its `TypedLogicValueArray<LogicValue>` specialization with the existing convenience constructors and concrete transform return types. Construction immediately encodes and decodes every value: the packed representation is authoritative, and a lossy codec therefore exposes normalized semantic values from the start. Decoded semantic elements are exposed by reference, so mutating a mutable element does not update the stored packed bits; callers using mutable semantic values are responsible for treating them consistently with snapshot semantics. Shape-only operations preserve those normalized values without re-encoding them. `stack` requires every typed value array to use the identical codec object because codec functions cannot be compared for semantic equivalence.

The root list of a nested constructor always represents an array dimension. Below the root, an object matching `T` is treated as one semantic value before it is considered as another list dimension. This permits list-valued semantic elements; use `fromFlat` when the intended interpretation would otherwise be ambiguous.

Both value-array classes are `LogicValue`s. Their `width` and deprecated `length` count packed bits, while `arrayValues.length` counts array positions. `arrayValues` has one entry for each configured array position in row-major order. Bit indexing, equality, hashing, arithmetic, and bitwise operations use the packed value and do not consider shape. The `packed` getter exposes the ordinary `LogicValue` representation.

`TypedLogicArray<TLogic, TValue>.value` and `previousValue` return `TypedLogicValueArray<TValue>` snapshots without adding hardware to the graph. `LogicArray` overrides these with the concrete `LogicValueArray` return type. The standard `changed`, `glitch`, and edge APIs remain packed `LogicValueChanged` events, so typed arrays retain the normal `Logic` event contract. Since all value arrays are `LogicValue`s, use the target-side `put` API for immediate assignment or `inject` for scheduled assignment. Both follow the ordinary packed-value contract, so same-width values remain assignable regardless of their shape metadata.

## Unpacked arrays

In SystemVerilog, there is a concept of "packed" vs. "unpacked" arrays which have different use cases and capabilities. In ROHD, all arrays act the same and you get the best of both worlds. You can indicate when constructing a `LogicArray` that some number of the dimensions should be "unpacked" as a hint to `Synthesizer`s. Marking an array with a non-zero `numUnpackedDimensions`, for example, will make that many of the dimensions "unpacked" in generated SystemVerilog signal declarations.
Expand All @@ -43,9 +183,11 @@ You can declare ports of `Module`s as being arrays (including with some dimensio

Array ports in generated SystemVerilog will match dimensions (including unpacked) as specified when the port is created.

Use the existing `addTypedInput`, `addTypedOutput`, and `addTypedInOut` methods for `TypedLogicArray` ports. Their generic `LogicType` preserves the complete array subtype, including its hardware element type, semantic value type, codec, net kind, dimensions, and unpacked-dimension configuration. This allows the module to access fields such as `samples.at([1, 2]).data` directly. The established `addInputArray`, `addOutputArray`, and `addInOutArray` APIs remain the concrete `LogicArray` helpers.

## Elements of arrays

To iterate through or access elements of a `LogicArray` (or bits of a simple `Logic`), use [`elements`](https://intel.github.io/rohd/rohd/Logic/elements.html). Using the normal `[n]` accessors will return the `n`th bit regardless for `LogicArray` and `Logic` to maintain API consistency.
Use `elements` to inspect immediate children, `arrayElements` or `indexedElements` to traverse declared array positions, and `leafElements` only when fully recursive traversal is intended. The normal `[n]` operator selects the `n`th packed bit for both `LogicArray` and `Logic`; use `at` for multidimensional typed element indexing.

## Index-based Selection in an Array

Expand Down
4 changes: 2 additions & 2 deletions lib/rohd.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (C) 2021-2023 Intel Corporation
// Copyright (C) 2021-2026 Intel Corporation
// SPDX-License-Identifier: BSD-3-Clause

export 'src/exceptions/exceptions.dart';
Expand All @@ -8,7 +8,7 @@ export 'src/interfaces/interfaces.dart';
export 'src/module.dart';
export 'src/modules/modules.dart';
export 'src/selection.dart';
export 'src/signals/signals.dart';
export 'src/signals/signals.dart' hide BaseLogicArray;
export 'src/simulator.dart';
export 'src/swizzle.dart';
export 'src/synthesizers/synthesizers.dart';
Expand Down
92 changes: 54 additions & 38 deletions lib/src/interfaces/interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd/src/signals/signals.dart';

/// Represents a logical interface to a [Module].
///
Expand Down Expand Up @@ -85,43 +86,53 @@ class Interface<TagType extends Enum> {
if (inputTags != null) {
for (final port in getPorts(inputTags).values) {
port <=
(port is LogicArray
? module.addInputArray(
uniquify(port.name),
srcInterface.port(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
)
: module.addInput(
uniquify(port.name),
srcInterface.port(port.name),
width: port.width,
));
switch (port) {
LogicArray() => module.addInputArray(
uniquify(port.name),
srcInterface.port(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
),
BaseLogicArray() => module.addTypedInput(
uniquify(port.name),
srcInterface.port(port.name) as BaseLogicArray,
),
_ => module.addInput(
uniquify(port.name),
srcInterface.port(port.name),
width: port.width,
),
};
}
}

if (outputTags != null) {
for (final port in getPorts(outputTags).values) {
final output = (port is LogicArray
? module.addOutputArray(
uniquify(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
)
: module.addOutput(
uniquify(port.name),
width: port.width,
));
final output = switch (port) {
LogicArray() => module.addOutputArray(
uniquify(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
),
BaseLogicArray() => module.addTypedOutput(
uniquify(port.name),
port.clone,
),
_ => module.addOutput(
uniquify(port.name),
width: port.width,
),
};
output <= port;
srcInterface.port(port.name) <= output;
}
}

if (inOutTags != null) {
for (final port in getPorts(inOutTags).values) {
if (port is LogicArray) {
if (port is BaseLogicArray) {
if (!port.isNet) {
throw PortTypeException(
port, 'LogicArray nets must be used for inOut array ports.');
Expand All @@ -132,19 +143,24 @@ class Interface<TagType extends Enum> {
}

port <=
(port is LogicArray
? module.addInOutArray(
uniquify(port.name),
srcInterface.port(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
)
: module.addInOut(
uniquify(port.name),
srcInterface.port(port.name),
width: port.width,
));
switch (port) {
LogicArray() => module.addInOutArray(
uniquify(port.name),
srcInterface.port(port.name),
dimensions: port.dimensions,
elementWidth: port.elementWidth,
numUnpackedDimensions: port.numUnpackedDimensions,
),
BaseLogicArray() => module.addTypedInOut(
uniquify(port.name),
srcInterface.port(port.name) as BaseLogicArray,
),
_ => module.addInOut(
uniquify(port.name),
srcInterface.port(port.name),
width: port.width,
),
};
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/interfaces/pair_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'dart:collection';

import 'package:meta/meta.dart';
import 'package:rohd/rohd.dart';
import 'package:rohd/src/signals/signals.dart';
import 'package:rohd/src/utilities/sanitizer.dart';

/// A direction for signals between a pair of components.
Expand Down Expand Up @@ -92,6 +93,8 @@ class PairInterface extends Interface<PairDirection> {
p.numUnpackedDimensions)
: LogicArray.port(name, p.dimensions, p.elementWidth,
p.numUnpackedDimensions);
case BaseLogicArray():
return p.clone();
case LogicNet():
return LogicNet.port(name, p.width);
default:
Expand Down
Loading
Loading