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

[RFC] Bundle and Vec .Lit and .Wire #1005

Open
ducky64 opened this issue Jan 26, 2019 · 2 comments
Open

[RFC] Bundle and Vec .Lit and .Wire #1005

ducky64 opened this issue Jan 26, 2019 · 2 comments

Comments

@ducky64
Copy link
Contributor

ducky64 commented Jan 26, 2019

Background

  • [RFC] Bundle Literals #805 proposes Bundle literal constructors (which is new functionality that can't really be replicated in the existing API), but syntactic sugar for constructing Bundle wires with a similar syntax also has uses. As proposed, Bundle literal constructors would not be able to create eg a Bundle wire with values pre-set to IOs.
  • WireDefault instead of WireInit, keep WireInit around #986 changes WireInit to WireDefault, which better reflects the underlying hardware (wires are not Regs, and cannot be initialized, but can have a default value given last-connect semantics). We currently have VecInit, which is analogous to what's now WireDefault but with no proposed replacement.
  • Vec literals #849 requests Vec literals, which would be useful for unpacking literal-to-bits, and for testers2 inputs and outputs.

Proposal

  • Keep the APIs between literal constructors and wire-with-default constructors separate. This is in line with the current philosophy of strict separation of types, especially when they have different functionality (eg litOption, litToBoolean). This also provides a cleaner path to literal types in the type system if we ever want to re-explore [RFC] Literal Types #777.
  • The Lit API is the proposal in [RFC] Bundle Literals #805, which creates a literal type given literal elements. For a example Bundle, it would be;
    (new MyBundle(8)).Lit(a=true.B, b=255.U)
    for a Vec, it would be similar to VecInit where the output type is inferred from elements:
    Vec.Lit( Seq(0.U, 1.U, ...) )
    Important note: Bundle literal constructors requires an instance of the Bundle as a template, whereas Vec literal constructors do not. We cannot infer the concrete Bundle type given a companion object since there may be parameters.
  • The Wire API would be similar structurally, but accept non-literal elements and return non-literal type:
    (new MyBundle(8)).Wire(a=true.B, b=io.bits)
    Vec.Wire( Seq(0.U, io.bits, ...) )
    Note that this will accept any hardware type, including (but not restricted to) literals. Vec.Wire is equivalent to VecInit.
  • VecInit would be 'deprecated' using the same mechanism we use for WireDefault instead of WireInit, keep WireInit around #986 / Notification on moving to WireDefault #1001, which will be at least standard lint rules and possibly also a deprecation.
  • A similar structure would apply to MixedVec and Record (possibly - the input would be a Map of string name to value, since there's no static structure we could generate a Lit method for) types.
  • Attempting to pass a non-literal into the .Lit method could give a good diagnostic error that suggests the use of the similar .Wire method.

Questions

  • Since we're introducing Vec.Wire (and also MixedVec.Wire), this is also a chance to revisit the API. Note that the Scala constructors for Seq/etc (nearest analogy to Vec.Wire) are all varargs, should Vec.Wire be varargs only? We could create a PML .toVecWire (and maybe also .toVecLit) which is more analogous to the Scala .toSeq conversions.
  • Perhaps something similar should be done for the Map-to-Bundle conversions, like .toBundleLit(bundleType)? In this case, Record would not have a .Lit or .Wire method, and Map.toRecordWire(recordType) / Map.toRecordLit(recordType) must be used instead.

Alternatives

  • An alternative would be to have a single constructor that dynamically determines whether the output is of Wire or Literal type, depending on if the inputs are Literals. This should be possible implementation-wise, but could make statically typed literals more difficult if we wanted to try that again in the future, and could make code less clear where a literal is required, especially for testers2. This is a bad idea for functional reasons described in the comments.

Type of issue: other enhancement

Impact: API addition (no impact on existing code), API modification

Development Phase: proposal

@grebe
Copy link
Contributor

grebe commented Jan 26, 2019

In alternatives, it sounds like you are saying the difference between a wire and a lit is that the entries are literals or not. I think this is not correct. For example,

val values = for (i <- 0 until 100) yield i.U
val wire = Vec.Wire(values)
when (io.en) { wire(3) := 5.U } // legal
val lit = Vec.Lit(values)
when (io.en) { lit(3) := 4.U } // illegal

For a dynamic system to determine if something is a literal or not, you would essentially have FIRRTL check if there are any later writers, which seems to destroy most of the value of the bindings system to me.

@ducky64
Copy link
Contributor Author

ducky64 commented Jan 26, 2019

Yeah, that's also a good point, in that a literal type functionally differs from a wire type in that it can't be reassigned. So definitely shouldn't have a shared .Wire and .Lit API.

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

No branches or pull requests

2 participants