Combinational.ssa constructor
- List<
Conditional> construct(), { - String name = 'combinational_ssa',
Constructs a new Combinational where construct
generates a list of
Conditionals which use the provided remapping function to enable
a "static single-asssignment" (SSA) form for procedural execution. The
Wikipedia article has a good explanation:
https://en.wikipedia.org/wiki/Static_single-assignment_form
In SystemVerilog, an always_comb
block can easily produce
non-synthesizable or ambiguous design blocks which can lead to subtle
bugs and mismatches between simulation and synthesis. Since
Combinational maps directly to an always_comb
block, it is also
susceptible to these types of issues in the path to synthesis.
A large class of these issues can be prevented by avoiding a "write after read" scenario, where a signal is assigned a value after that value would have had an impact on prior procedural assignment in that same Combinational execution.
Combinational.ssa remaps signals such that signals are only "written" once.
The below example shows a simple use case:
Combinational.ssa((s) => [
s(y) < 1,
s(y) < s(y) + 1,
]);
Note that every variable in this case must be "initialized" before it can be used.
Note that signals returned by the remapping function (s
) are tied to
this specific instance of Combinational and shouldn't be used elsewhere
or you may see unexpected behavior. Also note that each instance of
signal returned by the remapping function should be used in at most
one Conditional and on either the receiving or driving side, but not
both. These restrictions are generally easy to adhere to unless you do
something strange.
There is a construction-time performance penalty for usage of this roughly proportional to the size of the design feeding into this instance. This is because it must search for any remapped signals along the entire combinational and sequential path feeding into each Conditional. This penalty is purely at generation time, not in simulation or the actual generated design. For very large designs, this penalty can be mitigated by constructing the Combinational.ssa before connecting inputs to the rest of the design, but usually the impact is so small that it will not be noticeable.
Implementation
factory Combinational.ssa(
List<Conditional> Function(Logic Function(Logic signal) s) construct,
{String name = 'combinational_ssa'}) {
final context = _ssaContextCounter++;
final ssas = <_SsaLogic>[];
Logic getSsa(Logic ref) {
final newSsa = _SsaLogic(ref, context);
ssas.add(newSsa);
return newSsa;
}
final conditionals = construct(getSsa);
ssas.forEach(_updateSsaDriverMap);
_processSsa(conditionals, context: context);
// no need to keep any of this old info around anymore
_signalToSsaDrivers.clear();
return Combinational(conditionals, name: name);
}