Logic Structures

A LogicStructure is a useful way to group or bundle related Logic signals together. They operate in a similar way to “packed structs” in SystemVerilog, or a class containing multiple Logics in ROHD, but with some important differences.

LogicStructures will not convert to structs in generated SystemVerilog. They are purely a way to deal with signals during generation time in ROHD.

LogicStructures can be used anywhere a Logic can be. This means you can assign one structure to another structure, or inter-assign between normal signals and structures. As long as the overall width matches, the assignment will work. The order of assignment of bits is based on the order of the elements in the structure.

Elements within a LogicStructure can be individually assigned. This is a notable difference from individual bits of a plain Logic where you’d have to use something like withSet to effectively modify bits within a signal.

LogicArrays are a type of LogicStructure and thus inherit these behavioral traits.

Using LogicStructure to group signals

The simplest way to use a LogicStructure is to just use its constructor, which requires a collection of Logics.

For example, if you wanted to bundle together a ready and a valid signal together into one structure, you could do this:

final rvStruct = LogicStructure([Logic(name: 'ready'), Logic(name: 'valid')]);

You could now assign this like any other Logic all together:

Logic ready, valid;
rvStruct <= [ready, valid].rswizzle();

Or you can assign individual elements:

rvStruct.elements[0] <= ready;
rvStruct.elements[1] <= valid;

Making your own structure

Referencing elements by index is often not ideal for named signals. We can do better by building our own structure that inherits from LogicStructure.

class ReadyValidStruct extends LogicStructure {
  final Logic ready;
  final Logic valid;

  factory ReadyValidStruct() => MyStruct._(
        Logic(name: 'ready'),
        Logic(name: 'valid'),
      );

  ReadyValidStruct._(this.ready, this.valid)
      : super([ready, valid], name: 'readyValid');

  @override
  LogicStructure clone({String? name}) => ReadyValidStruct();
}

Here we’ve built a class that has ready and valid as fields, so we can reference those instead of by element index. We use some tricks with factorys to make this easier to work with.

We override the clone function so that we can make a duplicate structure of the same type.

There’s a lot more that can be done with a custom class like this, but this is a good start. There are places where it may even make sense to prefer a custom LogicStructure to an Interface.

Updated: