-
Notifications
You must be signed in to change notification settings - Fork 17
RocketTile
The cake pattern involves two parallel chains of inheritance. It also involves "twins", where twins involve lazy modules. So, one twin establishes the presence of a circuit element, but doesn't have a concrete instance of it - this is the outer twin. It's job is three fold:
- it establishes the presence of the circuit element, and
- it establishes the software interface, and
- it takes part in Diplomacy calculations about the concrete details of the interface, such as how many bits wide, and how many copies, and most especially, which particular concrete implementation.
The other "twin" has an "inner" twin -- this is where you get instances of objects that are circuit elements. Not all inner twins instantiate an object, but if one is instantiated, it's inside an inner twin.
The idea is that the first twin establishes the presence of a circuit element (or a container that has many circuit elements or more containers, in a hierarchy). This is established statically. So when the program initializes, these first twins are present, and provide hooks by which the Diplomacy calculations can take place. Then, once the Diplomacy has completed, the second twins create the actual circuit elements and connects them together.
The way this is accomplished is via the Lazy Module mechanism.
In the code, how it looks is that there is a rather empty outer twin class, and "trait" that defines the inner twin. (To make things complicated, the outer twin could also be a trait, but doesn't have to be, but the inner twin must be a trait). At the top of a particular hierarchy, the outer twin directly includes the inner twin, via:
lazy val module = new RocketTileModule(this)
Where this code is placed inside the outer twin's class definition (in this case, inside "class RocketTile" in the file RocketTiles.scala), and "new" creates an instance of the inner twin. Note that the name of the inner twin has a convention, in June 2017 it ends with "Module" but in newer it ends with "Impl" which is short for "Implementation".
The complication is that there isn't always an instantiation of the inner twin inside the class of the outer twin! The Cake Pattern allows long chains of inheritance. What's important is that, at the end of the chain, where the inner is, indeed, instantiated (as shown above) inside the outer, that each of the traits in the inner twin's chain do have a corresponding outer twin in the outer twin's chain.
Note that in the RocketTiles.scala code, the RocketTile class declares a cpuDevice (val cpuDevice = new Device
) -- this is not lazy and this is not an outer twin - instead, this is support for the OS. The Device is used to create a device tree, which the OS kernel reads, in order to gain information about the devices that are in the design. The "Resource" portion of Rocket Chip defines many helper functions that help build the device tree.