Split up expressions by generating new wires.
The basic idea of this transformation is to introduce temporary variables for sub-expressions, e.g., we might split up:
assign w = a + b * c + d;
into a series of simpler assignments, e.g.,
assign t1 = b * c; assign t2 = a + t1; assign w = t2 + d;
Almost true: we split up expressions until they involve either 0 or 1 operations. The twist is that we don't split up expressions that consist of "mere wiring", e.g., concatenation and bit- or part-selects. More precisely, we don't split up expressions that are already atomic or sliceable; see expr-slicing.
Splitting up expressions involves creating new wire declarations and
assignments to those wires. Sometimes the new modules are much bigger than the
old modules. We have seen cases where tens of thousands of new wires are
introduced. In fact, this transform was one of our initial motivations for
Context. I usually think of expression splitting as a kind of preprocessing step that leads toward occform; occform replaces simple assignments (e.g., like those to the temporary wires above) with module instances, but can't deal with complex expressions.
Prerequisites. We expect that argresolve has been run and that expression-sizing has already been done. Unsized expressions or named arguments will generally lead to fatal warnings being added to the module.
Soundness Concerns. If submodule ports are mislabeled, we might end up splitting up an input to a module instance that has backflow. That is, we could do somethign like this:
submod foo ( ..., .i(a + b), ...); ---> wire[3:0] tmp = a + b; submod foo ( ..., .i(tmp), ...);
And if
I don't really think this is a problem. I think we're saved because, since a new, fresh temporary wire is going to be used, whether or not that temporary is driven from both sides isn't really relevant. It can't be used anywhere else in the module or affect anything except for exactly this port.