-
Notifications
You must be signed in to change notification settings - Fork 148
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
Better control of wrapper generation #714
Comments
Another potential challenge is that wrapper generation happens quite early in the compilation passes, prior to ctxreduce. I'm not sure how easy it is to solve for a particular type class instance at genwrap. I have noticed that in the output of |
The consensus from Friday was that the first step should be to add a pragma to specify that an action method input should be split into separate ports, and defer any more general approach using type classes. Splitting up interface Foo =
a :: UInt 8
b :: Bool
deriving (Bits)
interface Bar =
putFoo :: Foo -> Action {#- split_inputs #-}
{-# synthesize #-}
mkBar :: Module Bar
mkBar = ... would yield the wrapper
There are still a few design questions here. One is whether we should only split structs/interface fields that have the pragma, or if we should split input interfaces recursively down to non-interface fields (as is done with outputs.) One might argue in favor of the former approach, as making structs into interfaces to just get separate ports is arguably an abuse of interfaces. Doing so with outputs makes things more complicated for the scheduler, as this results in extra methods being created. However I am in favor of the latter approach, for a few reasons. This is more in line with the current behavior for output ports, and one can control whether a nested item should be split by making it a struct or interface. In cases where we do want to flatten out a complex structure of nested interfaces, it is more tedious to specify the pragma on every interface. This also shouldn't make things worse for the scheduler, since flattening into multiple input ports doesn't create additional methods. I'm also not quite sure how input port naming should work with the existing |
I think the first step is just to get BSC generating the separate ports -- I don't think you need to be required to first implement a new pragma just to try doing that. This is why I suggested a (hidden) flag to turn on this behavior as the default, while we're in this experimental stage. (Also, will you be implementing both BH pragmas and BSV attributes?)
I don't think this is specific to Action methods. Struct inputs to any method -- value, action, actionvalue -- should be split-able.
For one, this isn't just ActionValue, it's also value methods -- any method with an output. Two, I agree that changing the imported module info to allow specifying multiple output ports would be more complicated; however, I don't think that's necessary for implementing this feature. The module info could retain the struct type, and only when BSC generates the output Verilog (in So, actually, implementing the splitting of return values might even be easier than splitting input arguments, since it only involves work in However, I'm totally fine with delaying this (in whatever form) until after input splitting has been implemented.
Yes, but I think you need to step back. I'd first ask: Should there be multiple behaviors that can be specified? For example, ways to say both "split recusive" and "split non-recursive". I don't see why we couldn't have both. If we only do one, I assume it would be recursive. Also, a big thing I should have said up front: Why is
Am I missing something? So in the places where you say the user could change behavior by changing whether the argument is a struct or an interface -- that should go away, it's only structs.
Again, I don't understand why we're talking about interfaces here. But I do think people will want attributes in more places. I think in some places they'll want to write one pragma to control the entire interface, and in some places they'll want to control an individual input. For example:
I'm not yet sure that
Also note that, in BSV, attributes are immediately before the thing they are attached to -- so I've done the same in the above example. I'm surprised that you put the
The default generated name should be
Here the default names would be That said, maybe the user would want finer control over the renaming of the ports -- for example, to rename
Or specified somehow on the interface or module? For example, a pragma that specifies that port "x" should be renamed "y":
I'm not entirely sure. |
Instead of Although, if we're thinking in the long run, maybe someone wants to argue that splitting should be the default, and preserving structs as a single port would need an attribute. I dunno. In BSV, attributes have values. If you just say
Or things like that. |
Actually, |
We definitely want to include splitting a vector into its elements. That's an important use case. |
This is related, but essentially orthogonal to the idea in #713. This is a bit less well-formed of an idea but I wanted to get some notes down before the meeting today.
The motivation is that the wrapper generation logic is rather complex and inflexible. Specifically, the inputs and outputs of action methods are not split into separate ports, which makes interfacing with the generated verilog rather tedious.
The high-level idea is to have a type class
Wrapped
that maps interface types into a type-level description of the wrapper that should be generated. The description types would contain of tuples of the ports that should be generated. Wrapper generation would then consult theWrapped
type class to determine what ports should be generated.For example, consider the following interfaces:
The wrapper representation for
Foo
(corresponding to the way wrapper generation currently happens) would beyielding the wrapper
and
Bar
would beyielding the wrapper
It should be possible to generate these instances using generics (with a few complications, that I will get to in a moment.) There could also be an instance of
Wrapped
forVector
that adds the current behavior of unfolding vectors of interfaces into separate ports. Wrapper generation from these representation types is essentially trivial; theto
andfrom
methods can also make use of thewrap
andunwrap
methods in theWrapped
type class.The interesting part is that we can define some newtype wrappers for how interface fields/method parameters should be treated, e.g. to indicate that a parameter/action result should be recursively expanded into separate ports:
The type
Expand
has a special case instance forWrapped
that give the representation type forBaz
:which would yield the wrapper
My biggest uncertainty here is how to handle pragmas on interface fields. Presumably whatever needs to be preserved on the generated wrapper fields should also be encoded in the
Port
type. Also, the way that port names get specified for method inputs in the generated wrapper types isn't great - perhaps inputs should have their own representation type.Note that to make the recursive generic unfolding work, we would need to modify the generic info to indicate whether a
MetaData
is an interface. I'm not sure whether it's better to do that or just add a newMetaIfc
metadata that would wrapMetaData
? We may need to do something similar to include info about interface pragmas on fields in the generic representation. This is technically a breaking change. But perhaps we can have a policy that introducing new layers ofMeta
tags in generic representations something that we can do, and any generic instances need to have a default instance that ignores unknown metadata types.The text was updated successfully, but these errors were encountered: