Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark Certain LazyModules For Inlining #2579

Merged
merged 18 commits into from
Aug 4, 2020
Merged

Mark Certain LazyModules For Inlining #2579

merged 18 commits into from
Aug 4, 2020

Conversation

hcook
Copy link
Member

@hcook hcook commented Jul 28, 2020

There are a number of situations where we insert a node in a diplomatic graph, but under certain edge cardinalities or parameterizations the resulting module only connects inputs to outputs, i.e. becomes "passthrough". This PR provides infrastructure to:

  • publicly flag when a given subclass of BaseNode will have no effect on the generated module circuitry (circuitIdentity)
  • publicly flag when a given LazyModule's module implementation should be inlined by FIRRTL (shouldBeInlined)
  • actually apply an extant FIRRTL annotation to the module to convey this intent to FIRRTL
  • report both the identity and shouldBeInlined flags in the graphml output (as empty ellipses and dotted-line rectangles respectively).

The goal with this top-down approach is to explicitly capture designer intent for when certain LazyModules should be elided (in contrast to past bottom-up attempts to infer it by lifting all input-output connections). For example, this approach has the beneficial side effect of only inlining modules that contain nodes for which we already know we do not insert Monitors. Chisel modules like OptimizationBarrier are unaffected.

The policy by which the flags are set remains open to the instantiater. The generic inlining policy for all LazyModules is now:

def shouldBeInlined: Boolean = nodes.forall(_.identity) && children.forall(_.shouldBeInlined)

This policy allows for recursive inlining of nested wrappers. The question becomes, which nodes are marked as circuitIdentity, and/or when do we override shouldBeInlined directly? Some node types (IdentityNode, NameNode, EphemeralNode) are definitionally circuitIdentity based on their kind. But for other types of nodes, I was less comfortable claiming that just because the node might e.g. appear to do nothing to the parameters, the matching circuit implementation is guaranteed to be desirable to inline.

Currently I am trying three different approaches in three different use cases:

  • Overriding circuitIdentity for certain specific TLAdapterNodes, TLNexusNodes and TLJunctionNodes. For the adapter cases, there is an node-specific function on the edge parameters (possibly when combined with an independent sizing parameter) that results in the adapter being passthrough. For the NexusNodes and JunctionNodes circuitIdentity often occurs when there is only a single input and output, or a ratio of 1. This cleans up a lot of confusingly named "xbars" that happen to frequently have 1:1 connections.
  • Overriding shouldBeInlined for certain TLBusWrappers. These usually contain a child with a NexusNode that might be circuitIdentity, but also a ClockSinkNode that never is (but serves no function in the nexus circuitIdentity case).

While it is tempting to set circuitIdentity for certain node types based purely on their inward/outward down/up parameters (i.e. cases where the parameters on both side of an AdapterNode are identical) I wasn't comfortable making that the default behavior. One could always write a circuit generator that modifies the data flowing through a node while not capturing the change in diplomatic parameters; but is doing so an anti-pattern that should require explicit control to prevent inlining? Even if so, it seems safer to having the circuitIdentity identification handled on a per-node basis. Some modules like TLFilter only change the parameters but not the actual circuit.

Some code generation results with SiFive datapoints: 8-20% reduction in the number of Module classes, 3-6% reduction in lines of verilog emitted. Obviously these modules were "empty" and the actual impact on final area has yet to be evaluated, but it can't hurt to spend less time processing this worthless connectivity in downstream tools.

Copy link
Member

@sequencer sequencer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed diplomacy package, I have no knowledge to tilelink package, leave that to others. :)

src/main/scala/diplomacy/Nodes.scala Outdated Show resolved Hide resolved
src/main/scala/diplomacy/Nodes.scala Outdated Show resolved Hide resolved
src/main/scala/diplomacy/Nodes.scala Outdated Show resolved Hide resolved
Copy link
Contributor

@terpstra terpstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a great idea.

src/main/scala/diplomacy/Nodes.scala Outdated Show resolved Hide resolved
@hcook hcook merged commit 6f0d142 into master Aug 4, 2020
@hcook hcook deleted the lazymodule-passthru branch September 3, 2020 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants