Chisel Passing Enum type as IO - scala

This is a related topic on chisel enum I have already looked at chisel "Enum(UInt(), 5)" failed
I am building a RISC-V in chisel and am running into a roadblock. I would like to abstract the ALU opcode from a combination of opcode, funct3, and funct7 to the actual ALU operation. Below I have a SystemVerilog module that shows the type of behavior I would like to emulate
// Defined in a types file
typedef enum bit [2:0] {
alu_add = 3'b000,
alu_sll = 3'b001,
alu_sra = 3'b010,
alu_sub = 3'b011,
alu_xor = 3'b100,
alu_srl = 3'b101,
alu_or = 3'b110,
alu_and = 3'b111
} alu_ops;
module alu
(
input alu_ops aluop,
input [31:0] a, b,
output logic [31:0] f
);
always_comb
begin
unique case (aluop)
alu_add: f = a + b;
alu_sll: f = a << b[4:0];
alu_sra: f = $signed(a) >>> b[4:0];
alu_sub: f = a - b;
alu_xor: f = a ^ b;
alu_srl: f = a >> b[4:0];
alu_or: f = a | b;
alu_and: f = a & b;
endcase
end
endmodule : alu
This is the current chisel file that I am using
AluType.scala
import chisel3._
import chisel3.Driver
import chisel3.experimental.ChiselEnum
package ALUType {
object AluOP extends ChiselEnum {
// type AluOP = Value
val ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SLRI, SRAI, ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND = Value
}
}
AluFile.scala
import chisel3._
import chisel3.Driver
import chisel3.experimental.ChiselEnum
import ALUType.AluOP._
class ALUFile(val dl_size: Int, val op_size: Int, val funct3_size: Int, val funct7_size: Int) extends Module {
val io = IO(new Bundle {
val val_a = Input(UInt(dl_size.W))
val val_b = Input(UInt(dl_size.W))
val aluop = Input(ALUType.AluOP.Type)
//val opcode = Input(UInt(op_size.W))
//val funct3 = Input(UInt(funct3_size.W))
//val funct7 = Input(UInt(funct7_size.W))
val val_out = Output(UInt(dl_size.W))
})
// Actual function
}
This is the result of the sbt run
$ sbt run
[info] welcome to sbt 1.4.7 (Oracle Corporation Java 1.8.0_281)
[info] loading project definition from C:\Chisel\Test1\RegFile\project
[info] loading settings for project regfile from build.sbt ...
[info] set current project to regfile (in build file:/C:/Chisel/Test1/RegFile/)
[info] compiling 1 Scala source to C:\Chisel\Test1\RegFile\target\scala-2.12\classes ...
[error] C:\Chisel\Test1\RegFile\src\main\scala\ALUFile.scala:43:30: inferred type arguments [ALUType.AluOP.Type.type] do not conform to method apply's type parameter bounds [T <: chisel3.Data]
[error] val aluop = Input(Wire(ALUType.AluOP.Type))
[error] ^
[error] C:\Chisel\Test1\RegFile\src\main\scala\ALUFile.scala:43:49: type mismatch;
[error] found : ALUType.AluOP.Type.type
[error] required: T
[error] val aluop = Input(Wire(ALUType.AluOP.Type))
[error] ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 4 s, completed Feb 11, 2021 8:35:18 PM
Do I just need to assign it to a UInt higher up, then decode it again? Seems silly to have to encode, then decode just to pass it from one module to the next. Is there a way to get AluOP.Type to conform to T? I would have thought that it would simply because it is a ChiselEnum.
EDIT :: 1
I tried enumerating with UInt but it says that is a non-literal type
[info] running ALUFile
[info] [0.000] Elaborating design...
[error] chisel3.internal.ChiselException: AluOp defined with a non-literal type
[error] ...
[error] at AluOp$.<init>(ALUFile.scala:41)
[error] at AluOp$.<clinit>(ALUFile.scala)
[error] at ALUFile$$anon$1.<init>(ALUFile.scala:49)
[error] at ALUFile.<init>(ALUFile.scala:46)
[error] at ALUFile$.$anonfun$new$6(ALUFile.scala:80)
object AluOp extends ChiselEnum {
val addi, slti, sltiu, xori, ori, andi, slli, slri, srai, add, sub, sll, slt, sltu, xor, srl, sra, or, and = Value(UInt()) // Line where error occurs
}
import AluOp._
class ALUFile(val dl_size: Int, val op_size: Int, val funct3_size: Int, val funct7_size: Int) extends Module {
val io = IO(new Bundle {
val val_a = Input(UInt(dl_size.W))
val val_b = Input(UInt(dl_size.W))
val aluop = Input( AluOp() )
val val_out = Output(UInt(dl_size.W))
})
switch (io.aluop) {
is (addi) {
io.val_out := 1.U
}
is (slti) {
io.val_out := 2.U
}
}
// Output result
io.val_out := 0.U
}
From https://github.com/chipsalliance/chisel3/blob/dd6871b8b3f2619178c2a333d9d6083805d99e16/src/test/scala/chiselTests/StrongEnum.scala
The only example they have for using enum in a switch statement, but they manually map values to the type WHICH IS NOT AN ENUM!!!
object StrongEnumFSM {
object State extends ChiselEnum {
val sNone, sOne1, sTwo1s = Value
val correct_annotation_map = Map[String, BigInt]("sNone" -> 0, "sOne1" -> 1, "sTwo1s" -> 2)
}
}
class StrongEnumFSM extends Module {
import StrongEnumFSM.State
import StrongEnumFSM.State._
// This FSM detects two 1's one after the other
val io = IO(new Bundle {
val in = Input(Bool())
val out = Output(Bool())
val state = Output(State())
})
val state = RegInit(sNone)
io.out := (state === sTwo1s)
io.state := state
switch (state) {
is (sNone) {
when (io.in) {
state := sOne1
}
}
is (sOne1) {
when (io.in) {
state := sTwo1s
} .otherwise {
state := sNone
}
}
is (sTwo1s) {
when (!io.in) {
state := sNone
}
}
}
}
One solution that might exist can be found here https://github.com/chipsalliance/chisel3/issues/885 where he defined his own Scala object and allowed for the call to return a UInt.
Additionally if I just use Enum, I can get it to compile which may just be the best solution for now. I would like to see a ChiselEnum be able to easily define states or operations at UInts and be able to pass them as IO so that I can get away from using numbers to define states and make them much more readable.
object AluOp {
val addi :: slti :: sltiu :: xori :: ori :: andi :: slli :: slri :: srai :: add :: sub :: sll :: slt :: sltu :: xor :: srl :: sra :: or :: and :: Nil = Enum(19)
}
import AluOp._
class ALUFile(val dl_size: Int, val op_size: Int, val funct3_size: Int, val funct7_size: Int) extends Module {
val io = IO(new Bundle {
val val_a = Input(UInt(dl_size.W))
val val_b = Input(UInt(dl_size.W))
// val aluop = Input( UInt(AluOp.getWidth.W) )
val aluop = Input(UInt(5.W))
// val opcode = Input(UInt(op_size.W))
// val funct3 = Input(UInt(funct3_size.W))
// val funct7 = Input(UInt(funct7_size.W))
val val_out = Output(UInt(dl_size.W))
})
// val reg_last = RegNext()
switch (io.aluop) {
is (addi) {
io.val_out := 1.U
}
is (slti) {
io.val_out := 2.U
}
}

I think all you need to do is use
val aluop = Input(AluOP())
Some simple example code can be found in the chisel3 unit tests
OP Edit ::
By adding a mapping:
import chisel3._
import chisel3.Driver
import chisel3.util._
import chisel3.experimental.ChiselEnum
package ALUType {
// object AluOp {
// val addi :: slti :: sltiu :: xori :: ori :: andi :: slli :: slri :: srai :: add :: sub :: sll :: slt :: sltu :: xor :: srl :: sra :: or :: and :: Nil = Enum(19)
// }
object AluOp extends ChiselEnum {
val add, sub, sll, slt, sltu, xor, srl, sra, or, and = Value
val correct_annotation_map = Map[String, UInt](
"add" -> 0.U,
"sub" -> 1.U,
"sll" -> 2.U,
"slt" -> 3.U,
"sltu" -> 4.U,
"xor" -> 5.U,
"srl" -> 6.U,
"sra" -> 7.U,
"or" -> 8.U,
"and" -> 9.U
)
}
}
The value can be passed as an input:
import ALUType.AluOp._
class ALUFile(val dl_size: Int, val op_size: Int, val funct3_size: Int, val funct7_size: Int) extends Module {
import ALUType._
import ALUType.AluOp._
val io = IO(new Bundle {
val val_a = Input(UInt(dl_size.W))
val val_b = Input(UInt(dl_size.W))
val aluop = Input( AluOp() )
// val aluop = Input(UInt(5.W))
// val opcode = Input(UInt(op_size.W))
// val funct3 = Input(UInt(funct3_size.W))
// val funct7 = Input(UInt(funct7_size.W))
val val_out = Output(UInt(dl_size.W))
})
// switch (io.aluop) {
// is (add) {
// io.val_out := io.val_a + io.val_b
// }
// is (slt) {
// io.val_out := 2.U
// }
// }
switch (io.aluop) {
is (add) {
io.val_out := io.val_a + io.val_b
}
is (slt) {
io.val_out := 2.U
}
}
// Output result
io.val_out := 0.U
}
This is still not ideal as I would like to not have to manually map the strings to UInt values, but it is what it is. Maybe a Scala foreach loop could take the tedious assign out, who knows.

Related

Can Record class be used to create RegInit?

I am trying to use Record class to generate RegInit. This can help to dynamically create RegInit with other information in Module.
I am trying to use Record class to generate RegInit.
Here are my codes.
class MyReg(reg_info: List[(String, UInt)]) extends Record {
override def elements: ListMap[String, UInt] = ListMap(
reg_info.map(
x =>
x._1 -> x._2
):_*
)
override def cloneType: MyReg.this.type = new MyReg(reg_info).asInstanceOf[this.type]
}
class reg_test(reg_list: List[(String, String, List[(String, Int)])]) extends Module with RegSugar {
val io = IO(new Bundle() {
val a = Input(UInt(16.W))
val b = Input(UInt(16.W))
val d = Output(UInt(16.W))
val e = Output(UInt(16.W))
})
val myBundle = new Bundle() {
val a = UInt(16.W)
val b = UInt(16.W)
}
val reg_init_bundle = RegInit(myBundle, 0.U.asTypeOf(myBundle))
println(reg_init_bundle)
reg_init_bundle.a := io.a
reg_init_bundle.b := io.b
val list_b = List(("a", UInt(16.W)), ("b", UInt(16.W)))
val myRecord = new MyReg(list_b)
val reg_init = RegInit(myRecord, 0.U.asTypeOf(myRecord))
reg_init.elements("a") := io.a
reg_init.elements("b") := io.b
io.d := reg_init.elements("a") + reg_init.elements("b")
io.e := reg_init_bundle.a + reg_init_bundle.b
}
In my sight, Bundle extends Record and if these code work on Bundle, they should also work on Record.
However, the error is reported on the line:
val reg_init = RegInit(myRecord, 0.U.asTypeOf(myRecord))
Exception in thread "main" chisel3.package$RebindingException: Attempted reassignment of binding to reg_test.reg_init_?.a: Wire[UInt<16>], from: ChildBinding(reg_test.reg_init_?: Reg[MyReg])
at ... ()
at implicit_test.reg_test.$anonfun$reg_init$2(reg_test.scala:61)
at chisel3.internal.prefix$.apply(prefix.scala:48)
at implicit_test.reg_test.$anonfun$reg_init$1(reg_test.scala:61)
at chisel3.internal.plugin.package$.autoNameRecursively(package.scala:33)
at implicit_test.reg_test.<init>(reg_test.scala:61)
at implicit_test.reg_test$.$anonfun$verilogString$1(reg_test.scala:77)
at ... ()
at ... (Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace)
It seems that these codes can successfully generate RegInit with Bundle but failed to generate RegInit with Record.
Is that a way to generate RegInit with Record? or there is a compiler error?
https://github.com/chipsalliance/chisel3/issues/2998. Thanks, guys. Here is the explanation for this Question. Case closed.

Vec of Bundle as a Module parameter

I'm writing a Wishbone Intercon module to make the address decoding automatically.
I have two Bundle classes that describe Wishbone master and Wishbone slave interface.
class WbMaster (val dwidth: Int,
val awidth: Int) extends Bundle {
val adr_o = Output(UInt(awidth.W))
//...
val cyc_o = Output(Bool())
}
// Wishbone slave interface
class WbSlave (val dwidth: Int,
val awidth: Int) extends Bundle {
val adr_i = Input(UInt(awidth.W))
//...
val cyc_i = Input(Bool())
}
I want to pass these Bundle as parameter to my module Wishbone like following:
class WbInterconOneMaster(val awbm: WbMaster,
val awbs: Vec(WbSlave)) extends Module {
val io = IO(new Bundle{
val wbm = Flipped(new WbMaster(awbm.dwidth, awbm.awidth))
val wbs = Vec(?)
})
}
The objective is to permit a variable number of wishbone slaves and let the module doing the plumbing. Like following:
val spi2Wb = Module(new Spi2Wb(dwidth, awidth))
val wbMdio1 = Module(new MdioWb(mainFreq, targetFreq))
val wbMdio2 = Module(new MdioWb(mainFreq, targetFreq))
val slavesVec = Vec(Seq(wbMdio1, wbMdio2))
val wbIntercon = Module(new WbIntercon(spi2Wb.io.wbm, slavesVec))
The question is multiple:
is it the right way to do it ?
How to declare the Vec() in module parameters ?
I tryied this but does not work:
// Wishbone Intercone with one master and several slaves
// data bus is same size as master
class WbInterconOneMaster(val awbm: WbMaster,
val awbs: Vec[Seq[WbSlave]]) extends Module {
val io = IO(new Bundle{
val wbm = Flipped(new WbMaster(awbm.dwidth, awbm.awidth))
val wbs = Vec.fill(awbs.size){awbs.map(_.cloneType())}
})
}
Try thinking about your parameters as generators of the types that you need. The following is a toy example of this idea. In this case the one constructor parameter bgen is a generator method that will return an instance of a Bundle. It shows the use of this generator as is and also as part of a Vec
class BundleX extends Bundle {
val a = UInt(8.W)
val b = UInt(8.W)
}
class ModuleX(bgen: () => BundleX, numInputs: Int) extends Module {
val io = IO(new Bundle{
val in1 = Input(Vec(numInputs, bgen()))
val out1 = Output(bgen())
})
// output fields a and b are the the sum of all the corresponding inputs
io.out1.a := io.in1.foldLeft(0.U) { case (res, value) => res +% value.a}
io.out1.b := io.in1.foldLeft(0.U) { case (res, value) => res +% value.b}
}
class BundleXSpec extends ChiselPropSpec {
property("testname") {
elaborate(new ModuleX(() => new BundleX, 4))
}
}
I found a solution with MixedVec (experimental) module. I simply pass a Seq of WbSlave Bundle as a module parameter and I made a MixedVec (WbSlave can have different parameters in fact):
class WbInterconOneMaster(val awbm: WbMaster,
val awbs: Seq[WbSlave]) extends Module {
val io = IO(new Bundle{
val wbm = Flipped(new WbMaster(awbm.dwidth, awbm.awidth))
val wbs = MixedVec(awbs.map{i => new WbSlave(i.dwidth, i.awidth)})
})
io.wbm.dat_i := 0.U
io.wbm.ack_i := 0.U
for(i <- 0 until io.wbs.size){
io.wbs(i).dat_o := 0.U
io.wbs(i).ack_o := 0.U
}
}
That compile in the testbench.

Currying in hardware

I'm trying to implement a simple Address Decoder with a curried function inside. The code below won't compile, could anybody help me with this?
class AddrDecoder[T<:UInt] (dType:T, n:Int) extends Module {
val io = IO (new Bundle {
//val range = (Vec(Seq.fill(n){(dType,dType)})) // This won't compile, how to fix ?
val range = (List.fill(n){(dType,dType)})
val addr = Input (dType)
val en = Input (Bool())
val sel = Output(Bool())
})
def inside (range:(T,T))(addr:T):Bool = {
addr >= range._1 && addr < range._1 + range._2
}
when (io.en) {
io.sel := io.range map (inside(_)(io.addr))
}
}
[error] found : List[chisel3.Bool]
[error] (which expands to) List[chisel3.core.Bool]
[error] required: chisel3.core.Data
[error] io.sel := io.range map (inside(_)(io.addr))
#jkoenig provided an excellent solution. Posting it here for other's benefit
class AddrDecoder[T<:Data with Num[T]] (dType:T, n:Int) extends Module {
val io = IO (new Bundle {
val range0 = Input (Vec(n,dType))
val range1 = Input (Vec(n,dType))
val addr = Input (dType)
val en = Input (Bool())
val sel = Output(Vec(n,Bool()))
})
// Curried function which accepts a tuple and an input addr
// Use map to apply it to inputs
def inside (range:(T,T))(addr:T):Bool = {
addr >= range._1 && addr < range._1 + range._2
}
// MUX output
for (i <- 0 until n) {
io.sel(i) := false.B
}
when (io.en) {
io.sel := io.range0 zip io.range1 map (inside(_)(io.addr))
}
// $onehot0 output encoding check
assert (PopCount(io.sel) <= 1.U, "Invalid addr decoding")
}

Chisel testing error in sodor/ rocket core ALU

I am trying to customize and use the ALU source file of Sodor processor/Rocket core in a different project using chisel3. Running sbt test gives me the following error.
[info] - should carry out proper arithmatic and logical operations (with verilator) *** FAILED ***
[info] chisel3.core.Binding$BindingException: 'this' (chisel3.core.UInt#7): Missing IO() wrapper
[info] at chisel3.core.Binding$.checkSynthesizable(Binding.scala:185)
[info] at chisel3.core.Bits.do_apply(Bits.scala:97)
[info] at chisel3.core.Bits.do_apply(Bits.scala:109)
[info] at RiscvIoT.ALU$.isSub(alu.scala:44)
[info] at RiscvIoT.ALU.<init>(alu.scala:68)
[info] at RiscvIoT.ALUTester$$anonfun$2$$anonfun$apply$1$$anonfun$apply$mcV$sp$1.apply(aluTest.scala:41)
[info] at RiscvIoT.ALUTester$$anonfun$2$$anonfun$apply$1$$anonfun$apply$mcV$sp$1.apply(aluTest.scala:41)
[info] at chisel3.core.Module$.do_apply(Module.scala:29)
[info] at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:191)
[info] at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:191)
Following the guide on chisel 3 wiki on testing and debugging I believe that the initial error is at the line 44 (isSub function definition). But I can't figure out the problem and nothing I did solves the problem. I am posting my complete source file below for convenience.
import chisel3._
import chisel3.util._
import Common._
object ALU
{
val SZ_ALU_FN = 4
val ALU_X = Bits(0)
val ALU_ADD = Bits(0)
val ALU_SLL = Bits(1)
val ALU_XOR = Bits(4)
val ALU_OR = Bits(6)
val ALU_AND = Bits(7)
val ALU_SRL = Bits(5)
val ALU_SUB = Bits(10)
val ALU_SRA = Bits(11)
val ALU_SLT = Bits(12)
val ALU_SLTU = Bits(14)
val ALU_COPY1= Bits(8)
def isMulFN(fn: Bits, cmp: Bits) = fn(1,0) === cmp(1,0)
def isSub(cmd: Bits) = cmd(3)
def isSLTU(cmd: Bits) = cmd(0)
}
import ALU._
class ALUIO extends Bundle {
val xprlen = 32
val fn = Bits(INPUT, SZ_ALU_FN)
val in2 = UInt(INPUT, xprlen)
val in1 = UInt(INPUT, xprlen)
val out = UInt(OUTPUT, xprlen)
val adder_out = UInt(OUTPUT, xprlen)
}
class ALU extends Module
{
val io = new ALUIO
val xprlen = 32
val msb = xprlen-1
// ADD, SUB
val sum = io.in1 + Mux(isSub(io.fn), -io.in2, io.in2)
// SLT, SLTU
val less = Mux(io.in1(msb) === io.in2(msb), sum(msb),
Mux(isSLTU(io.fn), io.in2(msb), io.in1(msb)))
// SLL, SRL, SRA
val shamt = io.in2(4,0).toUInt
val shin_r = io.in1(31,0)
val shin = Mux(io.fn === ALU_SRL || io.fn === ALU_SRA, shin_r, Reverse(shin_r))
val shout_r = (Cat(isSub(io.fn) & shin(msb), shin).toSInt >> shamt)(msb,0)
val shout_l = Reverse(shout_r)
val bitwise_logic =
Mux(io.fn === ALU_AND, io.in1 & io.in2,
Mux(io.fn === ALU_OR, io.in1 | io.in2,
Mux(io.fn === ALU_XOR, io.in1 ^ io.in2,
io.in1))) // ALU_COPY1
// val out64 =
val out_xpr_length =
Mux(io.fn === ALU_ADD || io.fn === ALU_SUB, sum,
Mux(io.fn === ALU_SLT || io.fn === ALU_SLTU, less,
Mux(io.fn === ALU_SRL || io.fn === ALU_SRA, shout_r,
Mux(io.fn === ALU_SLL, shout_l,
bitwise_logic))))
io.out := out_xpr_length(31,0).toUInt
io.adder_out := sum
}
The exception thrown: chisel3.core.Binding$BindingException: 'this' (chisel3.core.UInt#7): Missing IO() wrapper is due to the fact that in chisel3, io must be wrapped in IO(...)
It is perhaps a little hidden in the documentation (see https://github.com/ucb-bar/chisel3/wiki/Chisel3-vs-Chisel2#deprecated-usage)
To fix, change the io declaration in ALU to:
val io = IO(new ALUIO)
You should also change the ALUIO to:
class ALUIO extends Bundle {
val xprlen = 32
val fn = Input(Bits(SZ_ALU_FN.W))
val in2 = Input(UInt(xprlen.W))
val in1 = Input(UInt(xprlen.W))
val out = Output(UInt(xprlen.W))
val adder_out = Output(UInt(xprlen.W))
}
Although I believe the old way is deprecated rather than an error.

Chisel: Access to Module Parameters from Tester

How does one access the parameters used to construct a Module from inside the Tester that is testing it?
In the test below I am passing the parameters explicitly both to the Module and to the Tester. I would prefer not to have to pass them to the Tester but instead extract them from the module that was also passed in.
Also I am new to scala/chisel so any tips on bad techniques I'm using would be appreciated :).
import Chisel._
import math.pow
class TestA(dataWidth: Int, arrayLength: Int) extends Module {
val dataType = Bits(INPUT, width = dataWidth)
val arrayType = Vec(gen = dataType, n = arrayLength)
val io = new Bundle {
val i_valid = Bool(INPUT)
val i_data = dataType
val i_array = arrayType
val o_valid = Bool(OUTPUT)
val o_data = dataType.flip
val o_array = arrayType.flip
}
io.o_valid := io.i_valid
io.o_data := io.i_data
io.o_array := io.i_array
}
class TestATests(c: TestA, dataWidth: Int, arrayLength: Int) extends Tester(c) {
val maxData = pow(2, dataWidth).toInt
for (t <- 0 until 16) {
val i_valid = rnd.nextInt(2)
val i_data = rnd.nextInt(maxData)
val i_array = List.fill(arrayLength)(rnd.nextInt(maxData))
poke(c.io.i_valid, i_valid)
poke(c.io.i_data, i_data)
(c.io.i_array, i_array).zipped foreach {
(element,value) => poke(element, value)
}
expect(c.io.o_valid, i_valid)
expect(c.io.o_data, i_data)
(c.io.o_array, i_array).zipped foreach {
(element,value) => poke(element, value)
}
step(1)
}
}
object TestAObject {
def main(args: Array[String]): Unit = {
val tutArgs = args.slice(0, args.length)
val dataWidth = 5
val arrayLength = 6
chiselMainTest(tutArgs, () => Module(
new TestA(dataWidth=dataWidth, arrayLength=arrayLength))){
c => new TestATests(c, dataWidth=dataWidth, arrayLength=arrayLength)
}
}
}
If you make the arguments dataWidth and arrayLength members of TestA you can just reference them. In Scala this can be accomplished by inserting val into the argument list:
class TestA(val dataWidth: Int, val arrayLength: Int) extends Module ...
Then you can reference them from the test as members with c.dataWidth or c.arrayLength