Open krame505 opened 4 months ago
To clarify, this would not affect anything to do with ready/enabled signals; the only change would be to the Verilog representation of (non-interface) fields of interfaces. Whether a value method should be split into separate ports would still be controlled solely by marking it as a struct or interface; many types that are currently interfaces solely to permit accessing the fields as separate ports could instead become structs. Trying to e.g. permit multiple interface action methods to share an enabled signal is not in scope.
Also note that we would still like to generate Verilog packed structs for interfaces that are used as action method inputs, since these are not split into separate ports by wrapper generation.
@mieszko pointed out that one might wish to specify the name of the generated Verilog struct for a type without overriding the generic implementation of its representation. There is no reason why these would need to be the same type class:
class VerilogRepr a where
verilogRepr :: a -> (String, String)
class (VerilogRepr a) => VerilogImpl a where
verilogImpl :: a -> Maybe String
These classes could have separate default generic implementations. Organizationally this split might also be helpful as VerilogRepr
probably needs to be in the Prelude
, if it is consulted during wrapper generation, but VerilogImpl
(which would contain the bulk of the complexity) could live in a library, alongside utilities for writing out header files at elaboration time, etc.
@nanavati pointed out that simply turning input/output ports into structs would not help much with debugging, since all the logic within a module would still be just in terms of bit vectors and slicing.
We also discussed a separate idea for using generics to improve wrapper generation, which is mostly orthogonal to this idea. I'll open a separate issue for that.
Currently, at synthesis boundaries all interface fields get turned into ports whose types are simply flattened bit vectors. This is rather annoying when trying to instantiate a Bluespec-synthesized module in hand-written Verilog. If an interface field contains complex structured data, the options are essentially to either
SystemVerilog has support for writing packed structs as port types. It would be very helpful if bsc could generate Verilog definitions of packed structs/unions corresponding to types used in interfaces, and use those types in synthesized module ports in place of bit vectors. (This would only affect the synthesized module interface - everything internal in the module would still be in terms of bit vectors.) There are two main challenges here - how to generate the definitions, and getting module synthesis to make use of them.
Note that the same types can be used in multiple interfaces and synthesized modules. It would be preferable to avoid generating multiple versions of the same type in a project. Thus I don't think this should be tied to synthesizing a particular module - it would be nice if bsc could spit out a separate
.vh
file containing the struct definitions. Users should also be able to customize the Verilog representation for types with a customBits
instance.I think for Verilog struct generation, an approach similar to the
GenCRepr
library I wrote would be preferable to making the compiler do this directly. This would be "just" a library using generics to generate the code at elaboration time. The Verilog representation for a type could be defined as a type class likeThe methods here take a proxy value to specify the type for the instance.
verilogRepr
is the left- and right-side portion of the type in verilog (e.gUInt 8
would belogic
and[7:0]
.) Parametric types would be monomorphised, e.g.Maybe (Int 8)
asMaybe_int8
or something like that.verilogDecl
is the definition of the type, if it is a struct/data. This class could have a default instance for structs/data using generics, but users can override it if their type has a customBits
instance.Using generics tricks we could also have a utility to easilly write out the library containing the verilog representations for all the data types appearing in interfaces used by the project:
Controlling when interface fields get flattened or use the struct representation is something I'm a bit less sure about. This should definitely be an opt-in feature, and probably something controlled per-type. Since there would be a default generic instance for
VerilogRepr
, we don't want to use the Verilog representation for any type with an instance. We could have another classsuch that users could write
instance VerilogIfc MyType
for any type appearing in an interface that should not be flattened. Another option, if we want finer-grained control, is to introduce a pragma on interface fields to specify the structured representation should be used; however specifying this on every interface would be tedious if one wishes to generally opt-in to this feature.My current thinking is maybe have wrapper generation check for an instance of
VerilogIfc
for each field type, and if so add the pragma. This may require type representation to be computed as a type-level string, since this would happen significantly before elaboration time; i.e. something likeThis wouldn't be too much of an issue, except that we would need to actually add a type-level string append function like
TAdd
and friends.The general idea originated in brainstorming with @mieszko, but I would like feedback from others before going too much further. We can discuss in the sync meeting on Friday - hopefully @quark17 is available? I would like to run this past @nanavati too but I think he is on vacation for the rest of the week. On a logistical note, I have about 2 months left in my internship this summer, so whatever I attempt I would like to have essentially wrapped up by the first week of September.