An example that defines a module over multiple notebook cells.

We must first import our Chisel dependencies:

import $file.^.source.load_ivy
import chisel3.{Input => Input}

import chisel3._

import chisel3.util._

import chisel3.tester._

import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}

import chisel3.tester.RawTester.{test => Test}

define the IO

If we define our IO as a standalone bundle we are able to reuse it for other modules

defined class ALUIO

A skeleton module will be parent class of all of our methods

This module currently has only IO defined. We will use Scala implicits to inject functionality into it.

defined class ALUSkeleton

The add method from Add class will be injected into the ALUSkeleton class

This syntax will be used to inject the add method into our skeleton module:Scalaclass Add(m: ALUSkeleton) { def add(a: SInt, b: SInt): SInt = a +& b }

defined class Add

Similarly we can define a sub method that will be added

changed auto-gen file
defined class Sub

Here multiple methods will be injected

Both mul and div will be injected.

defined class MulDiv

Adding the methods to our skeleton module:

implicit def call will add the methods of the RHS classes to the implicit def parameter:ALUSkeleton class.Note the name includeAdd does not matter as it won't be called again, but is good practice to give it a clear name of what it is doing (i.e includAdd <=> include the Add class' methods)

defined function includeAdd
defined function includeSub
defined function includeMulDiv

Use our implicitly defined functions

We extend our ALUSkeleton giving us access to all of our implicitly defined methods. We can match based on parameterized operator (at compile time) and then only instantiate hardware for that given operation.

defined class Operator

Summary

While this includes a few more steps than simply implementing everything directly into an Operator module, it can actually save some headaches when building much larger moudules. The best case for developing this way is if you're defining a specific arithmetic implmentation, which this example was meant to outline. For example a multiplier may require many sub methods to implement, and these implementation details can be hidden from the module using it. Also working like this puts you in the mindset of pulling out independent pieces of logic to write functions for that can later be reused in other modules.

defined class Tester

Some test funcs/classes/objects to be exported from this notebook:

defined function testTypeParams
class LotsOfApply(a: Int) {}
object LotsOfApply {
    def apply: LotsOfApply = new LotsOfApply(1)
    def apply(a: Int): LotsOfApply = new LotsOfApply(a)
}
defined class LotsOfApply
defined object LotsOfApply