Vighnesh Iyer, Kevin Laeufer, Young-Jin Park, Rohit Agarwal, Lixiang Yin, Bryan Ngo, Oliver Yu, Koushik Sen, Borivoje Nikolić
UC Berkeley
PLARCH 2023
typedef union tagged {
bit [4:0] Register;
bit [21:0] Literal;
struct {
bit [4:0] regAddr;
bit [4:0] regIndex;
} Indexed;
} InstrOperand;
case (orand: InstrOperand) matches
tagged Register r : x = rf[r];
tagged Literal n : x = n;
tagged Indexed { ra, ri } : x = mem[rf[ra]+ri];
endcase
Ergonomic runtime tagged unions in Bluespec Verilog
class Packet;
rand bit [3:0] data [];
constraint size { data.size() > 5; data.size() < 10; }
constraint values {
foreach(data[i]) {
data[i] == i + 1;
data[i] inside {[0:8]};
}
}
endclass
Ergonomic declarative constrained random API in SystemVerilog
2 directions:
For HDLs, the eDSL primitives are hardware components, and the interpreter turns a netlist description into FIRRTL, CIRCT IR, etc.
HDLs implemented as eDSLs open the door for more eDSLs targeting other aspects of hardware design and verification
We should expand the horizons of eDSLs beyond RTL design into other complementary domains
We present three eDSLs that augment and use Chisel
Both provide all the benefits of being in a general-purpose language, while having fork/join primitives
However, their fork/join functionality is slow
We shouldn't have to compromise on performance
def enqueue(data: T): Command[Unit] = for {
_ <- poke(io.bits, data)
_ <- poke(io.valid, true.B)
_ <- waitUntil(io.ready, true.B)
_ <- step(1)
_ <- poke(io.valid, false.B)
} yield ()
val pushNPop: Command[Boolean] = for {
enqThread <- fork(enqueue(100.U))
deqThread <- fork(dequeue())
_ <- join(enqThread)
data <- join(deqThread)
} yield data.litValue == 100
test(new Queue(UInt(8.W), 4)) { c =>
val allGood = run(pushNPop, c.clock)
assert(allGood)
}
Command[R]
which describes a testbench operation that terminates with a value of type R
Command
sGen
)
val intGen: Gen[Int] = Gen[Int].range(0, 100)
val seqGen: Gen[Seq[Int]] = for {
lit <- Gen.range(1, 100)
tailGen <- Gen.oneOf(Gen(Seq()) -> 0.1, seqGen -> 0.9),
seqn <- tailGen.map(t => lit +: t)
} yield seqn
Use Scala's for-comprehensions for monadic composition
val hwUIntGen: Gen[UInt] = Gen[UInt].range(0, 100)
object MemOp extends ChiselEnum
case class MemTx extends Bundle {
val addr = UInt(32.W)
val data = UInt(64.W)
val op = MemOp
}
val memTxGen: Gen[MemTx] = Gen[MemTx].uniform
object MemOp extends ChiselEnum
case class MemTx extends Bundle {
val addr = UInt(32.W)
val data = UInt(64.W)
val op = MemOp
}
val memTxGen: Gen[MemTx] = Gen[MemTx].constrained { memTx =>
(memTx.op === MemOp.Write) && (addr(2,0) === 0.U)
}
Since Chisel is an eDSL, it can be leveraged for other hardware eDSLs
Gen[MemTx].generate(ScalaRandom(seed=10))
Gen[MemTx].generate(ParametricRandom(Seq[Byte](...)))
Unify both imperative and declarative constraint generators and introduce parametric control
Gen[A]
which describes a generator of values of type A
Gen
sOften, hardware designers manually convert imperative control flow to an explicit FSM
This process is repetitive, mechanical, and error-prone
We can design an eDSL to directly express cycle-level control flow and an interpreter to turn it into RTL
tick()
: advance a cycleaction { block }
: perform the assignments in the block nowwhileLoop (cond) { recipe }
: loop until cond is falsewhen (cond) { recipe }
: if cond is true, execute sub-recipe
def waitUntil(c: Bool) = whileLoop(!c, tick())
def forever(r: Recipe) = whileLoop(true.B, r)
Reading a memory from an AXI-Lite port
val readOnce = recipe(
waitUntil(axi.ar_valid === 1.B, active=axi.ar_ready),
action {
axi.r_data := RegNext(mem.read(axi.ar_addr))
axi.r_valid := 1.B
},
waitUntil(axi.r_ready === 1.B, active=axi.r_valid),
tick()
).compile()
Use Scala macros to inject source line instrumentation into eDSL primitives
Use Chisel printf and naming APIs to inject source info into RTL
Each primitive is a go-done circuit
Opportunity to use a lightweight HLS IR (e.g. Calyx) to produce optimized FSMs
Recipe
which describes a control flow machineWhat makes a good host language for an eDSL?
Scala (3) is quite good!
What new eDSLs should we work on?