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
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
}
}
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.
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
}
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
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.