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

Aggregates can now be marked as literals. #694

Closed
wants to merge 4 commits into from

Conversation

grebe
Copy link
Contributor

@grebe grebe commented Sep 16, 2017

This is inspired by dsptools DspReal(), which is implemented as a Bundle with a UInt inside. Bundles currently can't be literals, which causes problems because it is totally natural to want a literal DspReal().

This is an initial stab at making literal bundles work in this context. It doesn't go anywhere towards addressing #417, although I think it does address #418 to an extent. This is also related to ucb-bar/dsptools#55 and perhaps ucb-bar/dsptools#87.

The big difference between how lits need to work for Aggregates and UInt/SInt/etc. is that UInt/SInt/etc aren't meant to be subclassed, whereas Aggregates are (especially Bundle and Record). Some API needs to be provided for users to mark things as literals (checking that they actually are).

Currently, I check by calling isLit() on all children. This is fine for UInt/SInt/etc., but is problematic for Aggregates because they currently don't mark themselves as lits. I added a private var that gets set to something when you try to mark an Aggregate as a lit, but the actual value is ignored because I don't do anything with it. I'm not a fan of doing this, but not familiar enough with the backend to know what a better alternative would be.

@ducky64 @jackkoenig @chick


class BundleWithLit(lit: Option[Int]=None) extends Bundle {
val internal = lit.map(_.U).getOrElse(UInt(4.W))
override def cloneType = new BundleWithLit(lit).asInstanceOf[this.type]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this a mistake? I just realized that cloneType of a lit should not be a lit. This should probably be new BundleWithLit().asInstanceOf[this.type]

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's right, that cloneType should not return a Lit.

Copy link
Contributor

@edwardcwang edwardcwang left a comment

Choose a reason for hiding this comment

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

Bundle literals is something that's been brought up many times at the Chisel meetings. This might work for just getting lits to be emitted properly for the dsptools usecases you linked, though I think more thought is needed on how bundle literals should be generally. @ducky64

behavior of "Bundle literals"

it should "build the module without crashing" in {
println(chisel3.Driver.emitVerilog( new BundleWithLitModule ))
Copy link
Contributor

Choose a reason for hiding this comment

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

You may want to compare firrtl output, instead of just emitting Verilog.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough, I just did a "it's doing something" test to start with

object BundleWithBundleWithLit {
def apply(x: Int) = {
val ret = new BundleWithBundleWithLit(Some(x))
Aggregate.LitBind(ret)
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems strange to have to call this in user code; why is it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is patterned after UIntFactory, which isn't user code b/c you don't subtype UInt, but users do subtype bundles. I suppose you could have an aggregate try to detect if its members are literals, but that seemed to be a bigger change so I didn't take that step.

val io = IO(new Bundle {
val out = Output(BundleWithLit())
})
io.out := BundleWithLit(3)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the intended bundle literal syntax? Also, what about bundles with multiple members?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know if I'd say the literal syntax, but it is the kind of syntax we use with DspReal().
You're companion object could take multiple parameters

@edwardcwang edwardcwang requested a review from ducky64 September 16, 2017 06:33
@ducky64
Copy link
Contributor

ducky64 commented Sep 16, 2017

Agree with @edwardcwang, the frontend definitely needs more thought - from the testcases, the user has to do a lot of work to write bundle literal constructors. As for this just being a backend implementation, I need to cache back in the bindings and bundles system, but as far as I can remember, is there a reason a literal Bundle needs to be bound as such?

Literal bundle constructors (possibly implemented using macros) is on the docket of stuff for 3.1.0, and will likely be a part of Testers2. As a timeframe estimate, both are probably at least two months out, there's a list of more pressing stuff in terms of Chisel3 engineering dev.

@grebe
Copy link
Contributor Author

grebe commented Sep 16, 2017

I'm trying to remember the exact issues I was seeing in dsptools, but our solution is to create a wire and assign to it with literals. Sometimes you don't want to create the wire- I can't remember what situations cause problems (maybe if you're passing around a literal bundle just to get a width out of it or call cloneType in an IO? Like new TypeParameterizedModule(proto=DspReal(0.3))). This means that it is somewhat confusing whether something will create a wire or not.

It's more than just the annoyance of creating new wires, there's a reason UInts/SInts/etc. treat literals specially.

I agree that the example of the frontend is clunky. I think it can be made easier, e.g. a def MarkAsLit[T <: Aggregate](in: T): T so your companion object would just have def apply(x: Int) = MarkAsLit(new Whatever(x)) (doesn't eliminate the boilerplate or fragility, but makes it less ugly).

Do you see literal bundle constructors as changing how lits work at the core? It seems to me that whatever is implemented will have to do something to set litArg to Some(...). Perhaps it will do so by rewriting literal bundles to be of a subtype of Bundle that sets litArg, rather than the hackish private var trick.

@ducky64
Copy link
Contributor

ducky64 commented Sep 16, 2017

I'm not sure what the implementation for bundle literals should look like yet, that's something that needs more thought, possibly a fresh look into refactoring the entire literals system if warranted. That would be something that would be RFC-level, where it's probably worth having a detailed proposal and analysis before we start implementing away.

import org.scalatest._

class BundleWithLit(lit: Option[Int]=None) extends Bundle {
val internal = lit.map(_.U).getOrElse(UInt(4.W))
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the OrElse doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Providing a default width

@grebe
Copy link
Contributor Author

grebe commented Sep 17, 2017

@ducky64 I agree that if you are going to tackle the entire bundle literals problem (with implications in chisel, testers, and firrtl) that's a large task that should be done thoughtfully and with a process. I should be clear that what I'm pushing for here is to use the existing bindings infrastructure to be able to bind bundles as literals and nothing more ambitious than that.

I just pushed a cleaner version of the original idea. The examples are pretty clean, I think. It adds another nuance to how cloneType can fail (if it returns something that is bound), but this is already something that can happen and cause problems if you aren't careful.

One slightly nuanced thing is that empty Bundles should not be marked as literal. They're used all over the place in our testers, which will result in a module complaining that its IO is hardware when it should be chisel.

For whatever reason a Vec test is failing, I haven't tracked down why. I don't think it's a big deal, it's probably an easy fix if you understand how Vecs work.

@@ -13,7 +13,7 @@ import internal.sourceinfo.SourceInfo

class BasicTester extends Module() {
// The testbench has no IOs, rather it should communicate using printf, assert, and stop.
val io = IO(new Bundle())
val io = IO(new Bundle {val dummy = Input(UInt(0.W))})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, forgot to clean this up, this is no longer necessary.

Copy link
Contributor

@ducky64 ducky64 left a comment

Choose a reason for hiding this comment

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

Some code issues, but more importantly is the larger structural discussion.

Bindings questions

Currently, things are bound at the top, and leaf elements only have a ChildBinding. This proposal messes with that in the case of literals, now literals at the leaves (and all the way up the hierarchy) would have LitBindings. We can certainly rework how the binding system works, but let's try to keep things consistent.

Potential alternatives include having the top-level store the literal data of the children (but this decouples literal data from the element node) or having the literal data be independent of the binding (leaf elements continue to have ChildBinding, but also have their literal values locally - though this brings up another avenue for data inconsistency / disagreement).

Additional questions arise on the composability of literal bundles.

Frontend questions

The way the examples are structured doesn't seem like how I usually see Chisel code (pass in bitwidths instead of Data prototypes, which can either be bound or not). In fact, one of the goals of the cloneType and friends refactor was to make people strictly separate Chisel types vs. hardware nodes, which this style breaks.

Composability concerns should also be part of the frontend, though it would appear composition of literals is not an issue in your implementation.

Additionally, providing solutions for backwards compatibility would be nice. For example, we really shouldn't modify the Decoupled (for example) semantics because it's in wide use, but there should be a way to add Decoupled literals.

}
override def litArg = {
// TODO[grebe] make a lit for Aggregates
childLitArgs.map { x => ULit(0, 1.W) }
Copy link
Contributor

Choose a reason for hiding this comment

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

Things should fail noisily if this is an unexpected case instead of propagating bad values

}

protected[core] override def hasBinding = {
checkBinding
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks kind of hacky, kind of like a game of whackamole to insert checks into all the right places, it would make more sense for higher-level code to apply the binding as it is being set to a literal.

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is that bind() is private[chisel3] which makes it impossible to do that in user-land right now.

@@ -570,6 +608,10 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record {
Builder.exception(s"Parameterized Bundle ${this.getClass} needs cloneType method")
this
}

require(!newBundle.isLit, s"Bundle ${this.getClass} has a bad cloneType: it should never return a literal")
Copy link
Contributor

Choose a reason for hiding this comment

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

This require won't fire if the user overrides cloneType (essentially always given your current examples), right?

@ducky64 ducky64 mentioned this pull request Apr 13, 2018
@ducky64
Copy link
Contributor

ducky64 commented May 24, 2018

Superseded by #820 (with more discussion in #805), does that get closer to addressing your use cases?

@ducky64 ducky64 closed this May 24, 2018
@grebe
Copy link
Contributor Author

grebe commented May 24, 2018

Yeah, this should be closed.

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.

4 participants