Generic Address Decoder - chisel

I'd like to implement a generic addr decoder. This is a follow up question to this post.
class AddrDecoder[T <: Data with Num[T]] (dType:T, n:Int) extends Module {
val io = IO (new Bundle {
val mmap = Map(BigInt, BigInt) // base, size ranges
val addr = Input (dType)
val en = Input (Bool())
//val sel = Output(Vec(n,Bool())) // $onehot0 selector
val sel = Output(Bool():_*) // $onehot0 selector
})
// 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
}
// Check addr range and assert high if matches
var idx = 0 // yes, variable
for ((k,v) <- io.mmap) {
when (io.en) {
io.sel(idx) := (k + v) (inside(_)(io.addr))
idx := idx + 1.U
}
}
// $onehot0 output encoding check
assert (PopCount(io.sel) >= 1.U, "Invalid addr decoding")
}
I get a compile errors:
[error] found : math.BigInt.type
[error] required: (?, ?)
[error] val mmap = Map(BigInt, BigInt) // base, size ranges
...
[error] found : chisel3.core.Bool
[error] required: Seq[?]
[error] val sel = Output(Bool():_*) // $onehot0 selector
Can I use Map and a variable Bool array as IO ports? If not, how to rewrite this correctly?
Thank you!

Okay, here's my solution. Not that generic, since I could not specialize it to BigInt, but works for me now:
class AddrDecoder (addrWidth:Int, mmap:Map[UInt, UInt]) extends Module {
val size = mmap.size
val io = IO (new Bundle {
val addr = Input (UInt(addrWidth.W))
val en = Input (Bool())
val sel = Output(Vec(size,Bool())) // $onehot0 selector
})
// Curried function which accepts a tuple and an input addr
def inside (range:(UInt,UInt))(addr:UInt):Bool = {
addr >= range._1 && addr < range._1 + range._2
}
// MUX output
for (i <- 0 until size) {
io.sel(i) := false.B
}
// Loop thru the Memory Map, pair with index and evaluate logic value for io.sel
mmap.zipWithIndex foreach { case (entry,idx) =>
when (io.en && inside(entry)(io.addr)) {
io.sel(idx) := true.B
} .otherwise {
io.sel(idx) := false.B
}
}
// $onehot0 output encoding check
assert (PopCount(io.sel) <= 1.U, "Invalid addr decoding")
}

Related

Simple chisel dual port memory Read port issue

I am trying to do the basic tutorial on chisel for verilog generation, I am trying to build a dual port memory:
import chisel3._
import chisel3.stage.ChiselStage
class Memo extends Module {
val io = IO(new Bundle {
val wen = Input(Bool())
val wrAddr = Input(UInt(8.W))
val wrData = Input(UInt(8.W))
val ren = Input(Bool())
val rdAddr = Input(UInt(8.W))
val rdData = Output(UInt(8.W))
})
val mem = Mem(256, UInt(8.W))
when(io.wen) {
mem(io.wrAddr) := io.wrData
}
io.rdData := 0.U
when(io.ren) {
io.rdData := mem(io.rdAddr)
}
}
println((new ChiselStage).emitVerilog(new Memo))
The above code compiles without issues. But I want to hold the rdData value to the old value if ren = 0. For that I commented line io.rdData := 0.U and I got the error:
Errors: 1: in the following tutorials
Tutorial Memo: exception #[:#6.4] : [module Memo] Reference io is not fully initialized.
#[Memo.scala 31:15:#15.6] : io.rdData <= mux(io.ren, mem._T_20.data, VOID) #[Memo.scala 31:15:#15.6]
How do I fix this? How to hold the previous value on rdData? Also what does the Error message mean, that io is not initialized?
The uninitialized error means there are possible simulation paths where the wire io.rdData had never been assigned. If you need to hold onto some value I'd suggest adding a register, something like this.
val mem = Mem(256, UInt(8.W))
when(io.wen) {
mem(io.wrAddr) := io.wrData
}
val lastValue = RegInit(0.U(8.W))
io.rdData := 0.U
when(io.ren) {
io.rdData := mem(io.rdAddr)
lastValue := io.rdData
}.otherwise {
io.rdData := lastValue
}

Vivado can't recognize the double-port RAM while using SyncReadMem

I want to create a true double-port RAM in Chisel and synthesize the Verilog code in Vivado 2018.3. Here is my Chisel code:
class DoublePortsRAM extends Module {
val io = IO(new Bundle {
val addr1 = Input(UInt(10.W))
val dataIn1 = Input(UInt(32.W))
val en1 = Input(Bool())
val we1 = Input(Bool())
val dataOut1 = Output(UInt(32.W))
val clk2 = Input(Clock())
val reset2 = Input(Bool())
val addr2 = Input(UInt(10.W))
val dataIn2 = Input(UInt(32.W))
val en2 = Input(Bool())
val we2 = Input(Bool())
val dataOut2 = Output(UInt(32.W))
})
val syncRAM = SyncReadMem(1024, UInt(32.W))
when(io.en1) {
when(io.we1) {
syncRAM.write(io.addr1, io.dataIn1)
io.dataOut1 := DontCare
} .otherwise {
io.dataOut1 := syncRAM.read(io.addr1)
}
} .otherwise {
io.dataOut1 := DontCare
}
withClockAndReset(io.clk2, io.reset2) {
when(io.en2) {
when(io.we2) {
syncRAM.write(io.addr2, io.dataIn2)
io.dataOut2 := DontCare
} .otherwise {
io.dataOut2 := syncRAM.read(io.addr2)
}
} .otherwise {
io.dataOut2 := DontCare
}
}
}
After compiling it, some Verilog codes like these:
always #(posedge clock) begin
...
if (_GEN_36) begin
syncRAM__T_1_addr_pipe_0 <= io_addr1;
end
end
...
assign syncRAM__T_1_addr = syncRAM__T_1_addr_pipe_0;
assign syncRAM__T_1_data = syncRAM[syncRAM__T_1_addr];
assign io_dataOut1 = syncRAM__T_1_data;
...
However, Vivado reports that it can't infer the pattern of the RAM. I must modify the Verilog code manually to these:
always #(posedge clock) begin
...
if (_GEN_36) begin
syncRAM__T_1_addr_pipe_0 <= syncRAM[io_addr1];
end
end
...
assign io_dataOut1 = syncRAM__T_1_addr_pipe_0;
...
In other words, the Synchronous Read RAM shouldn't use a register to store the reading address. On the contrary, it is OK to store the reading data. In addition, Vivado can synthesize the single-port RAM without modifying the Verilog. What's wrong with it?

"data to be connected 'chisel3.core.UInt#103' must be hardware, not a bare Chisel type" when rewrite OpenSoCFabric1.1.2 from Chisel2 to Chisel3

I am trying to rewrite OpenSoCFaric-1.1.2 from chisel2 to chisel3. But I encounter error messages "data to be connected 'chisel3.core.UInt#103' must be hardware, not a bare Chisel type" for below code:
File: packettoFlit.scala class: PacketToFlit
val flitWidth = Flit.fromBits(0.U, parms).getWidth
File:channel.scala object: Flit
object Flit {
def head(h: HeadFlit) : Flit = {
val f = new Flit(h.parms)
f.x := f.union.pack("Head", h)
f
}
def body(b: BodyFlit) : Flit = {
val f = new Flit(b.parms)
f.x := f.union.pack("Body", b)
f
}
def fromBits(n: UInt, parms: Parameters) : Flit = {
val f = new Flit(parms)
f.x := n
f
}
/*
def zeroHead(parms: Parameters) : HeadFlit = {
val x = new HeadFlit(parms)
x.
}
*/
}
And now I don't have good ideas about how to rewrite such code segments to fix the error. Could you give some help or suggestions ? Thanks a lot!
We would need to see more of the error message (line number) and more of the code to provide a definitive answer, but our guess is that somewhere a UInt(x.W) is being used where a literal (y.U) is expected, or a Wire()/WireInit() wrapper is missing.
NOTE: LBL is actively migrating this code to Chisel3. You might be better off waiting for their work to be published.
Below is source code for class Flit.
class Flit(parms: Parameters) extends Bundle {
val union = new BitUnion(Map("Head" -> new HeadFlit(parms), "Body" -> new BodyFlit(parms)))
val x = UInt(union.width.W)
val numVCs = parms.get[Int]("numVCs")
def asHead(dummy: Int = 0) : HeadFlit = union.unpack[HeadFlit]("Head", x)
def asBody(dummy: Int = 0) : BodyFlit = union.unpack[BodyFlit]("Body", x)
def whenHead(block: HeadFlit => Unit) { union.whenTag[HeadFlit]("Head", x)(block) }
def whenBody(block: BodyFlit => Unit) { union.whenTag[BodyFlit]("Body", x)(block) }
def isHead(dummy: Int = 0) : Bool = union.tagEquals("Head", x)
def isBody(dummy: Int = 0) : Bool = union.tagEquals("Body", x)
def isTail(dummy: Int = 0) : Bool = {
val tailBit = Bool()
when (isHead()) {
tailBit := union.unpack[HeadFlit]("Head", x).isTail
} .otherwise {
tailBit := union.unpack[BodyFlit]("Body", x).isTail
}
tailBit
}
def getVCPort(dummy: Int = 0) : UInt = {
val vcBits = UInt(log2Ceil(numVCs).W)
when (isHead()) {
vcBits := union.unpack[HeadFlit]("Head", x).vcPort
} .otherwise {
vcBits := union.unpack[BodyFlit]("Body", x).vcPort
}
vcBits
}
//override def clone = { new Flit(parms).asInstanceOf[this.type] }
override def cloneType: this.type = new Flit(parms).asInstanceOf[this.type]
// override def width : Int = {x.width}
}
Thanks a lot!
Bibo

Wiring Seq of chisel modules

I was following the answer from here: How to do a vector of modules?.
I have a question regrading how to address previus items in the Seq. here is my code:
val ccfLinks = for (j <- 0 until (len - 1)) yield {
val exe_unit = Module(new Ccflink(bitwidth))
if (j == 0) {
// connnect the first stage //
exe_unit.io.i_in := io.i_in
exe_unit.io.q_in := io.q_in
exe_unit.io.coeff_i := coeffs_i(0)
exe_unit.io.coeff_q := coeffs_q(0)
exe_unit.io.pre_i_product := SInt(0)
exe_unit.io.pre_q_product := SInt(0)
} else {
// connect the rest of the chain //
exe_unit.io.i_in := ccfLinks(j-1).io.i_out
exe_unit.io.q_in := ccfLinks(j-1).io.q_out
exe_unit.io.coeff_i := coeffs_i(j)
exe_unit.io.coeff_q := coeffs_q(j)
exe_unit.io.pre_i_product := ccfLinks(j-1).io.i_product
exe_unit.io.pre_q_product := ccfLinks(j-1).io.q_product
}
exe_unit
}
I am trying to connect the current module to the previous, how do I address the previous module ?
trying to compile the code above result in the next error:
"Ccf.scala:240: recursive value ccfLinks needs type [error] exe_unit.io.i_in := ccfLinks(j-1).io.i_out"
Here is fairly straight forward way of doing it. Note the use of foldLeft which takes an head of the list and calls the code thunk with successive pairs. Using more advanced list comprehensions can make this a little more succinct, but I think this is pretty clear what's going on when.
class Element extends Module {
val io = IO(new Bundle {
val in0 = Input(UInt(8.W))
val in1 = Input(UInt(8.W))
val out0 = Output(UInt(8.W))
val out1 = Output(UInt(8.W))
})
val reg0 = RegNext(io.in0, 0.U)
val reg1 = RegNext(io.in1, 0.U)
io.out0 := reg0
io.out1 := reg1
}
/**
* wire together a bunch of elements, into a basic queue
* #param elementCount how big is the queue
*/
class ElementQueue(val elementCount: Int) extends Module {
val io = IO(new Bundle {
val in0 = Input(UInt(8.W))
val in1 = Input(UInt(8.W))
val out0 = Output(UInt(8.W))
val out1 = Output(UInt(8.W))
})
// create a scala Seq of Elements
val elements = Seq.fill(elementCount)(Module(new Element))
// wire the head to the inputs
elements.head.io.in0 := io.in0
elements.head.io.in1 := io.in1
// wire the elements of the queue
val last = elements.tail.foldLeft(elements.head) { case (prior, next) =>
next.io.in0 := prior.io.out0
next.io.in1 := prior.io.out1
next
}
// wire the end of the queue to the outputs
io.out0 := last.io.out0
io.out1 := last.io.out1
}

How to create a vector of vector when I'm defining IO

I'm trying to define Vector of Vector for my IO, but I'm getting an error from the chisel saying:
vec element 'Vec(chisel3.util.DecoupledIO#2b57)' must be hardware, not a bare Chisel type
The code which I've written is like the following:
//Module's argument
,val ArgsOut: Array[Int]
...
...
val Args = for (i <- 0 until ArgsOut.length) yield {
val arg = Vec(ArgsOut(i), Decoupled(UInt()))
arg
}
val outputArg = Vec(Args)
Some things to consider
IO ports, i.e. members of the IO Bundle, must be chisel hardware constructs, Args is a scala Vector type, it needs to be chisel Vec
All elements of a Vec must be the same size, mostly a consequence of the need to be able to index elements of the Vec. You have each element of Args as a Vec whos's length is determined by some the elements of ArgsOut. Vec(n, type) will not
Do really mean to have a 2D Vec(Vec( of decoupled IO's?
Your UInt in the Decoupled has an unknown width. This is not strictly speaking an error, because Firrtl can infer the width in most situations. But again this can be a problem for the requirement that a Vec's element are all the same length. Inferred widths in IOs should be used cautiously.
I was able to construct at IOBundle like this
val io = IO(new Bundle {
val args = Vec(ArgsOut.length, Vec(ArgsOut(0), Decoupled(UInt(18.W))))
val outputArg = Flipped(args)
})
Which compiles but may not quite be what you had in mind. I was able to connect the io's using
io.outputArg <> io.args
If this doesn't seem to fit your use case, I need to know a bit more how you intend to use the fields and we should be able to figure out how to wire them up.
Here is an illustration of how to use a subclass of Record to manage a virtual array of Vectors of varying length. This example runs for me. It is not exactly the same as your examples. But I think it makes things clearer. This is for the use case where you do not need to access your Vecs via a UInt, but is for when you need to match a heterogenous mix of Vectors.
import chisel3._
import chisel3.iotesters.PeekPokeTester
import org.scalatest.{FreeSpec, Matchers}
import scala.collection.immutable.ListMap
final class VariableBundle(elts: (String, Vec[UInt])*) extends Record {
val elements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*)
def apply(elt: String): Vec[UInt] = elements(elt)
override def cloneType = (new VecVecBundle(elements.toList: _*)).asInstanceOf[this.type]
}
class SeqIO(val sizes: Array[Int]) extends Module {
val io = IO(new VariableBundle(Seq.tabulate(sizes.length) { i =>
s"vec_in_$i" -> Input(Vec(sizes(i), UInt(8.W)))
} ++
Seq.tabulate(sizes.length) { i =>
s"vec_out_$i" -> Output(Vec(sizes(i), UInt(8.W)))
}:_*
)
)
for(i <- sizes.indices) {
io(s"vec_out_$i") := io(s"vec_in_$i")
}
}
class SeqIOTester(c: SeqIO) extends PeekPokeTester(c) {
for(i <- c.sizes.indices) {
for(j <- 0 until c.sizes(i)) {
poke(c.io(s"vec_in_$i")(j), j)
}
}
step(1)
for(i <- c.sizes.indices) {
for(j <- 0 until c.sizes(i)) {
expect(c.io(s"vec_out_$i")(j), j)
}
}
}
class SeqIOSpec extends FreeSpec with Matchers {
"illustrate how to build bundles that have vecs wrapping different sized vecs" in {
iotesters.Driver.execute(Array.empty[String], () => new SeqIO(Array(1, 2, 3, 4))) { c =>
new SeqIOTester(c)
} should be (true)
}
}
Let me write the actual code:
class LoopLatch(val NumInputs: Int,
val ArgsOut: Array[Int],
val ID: Int)
(implicit val p: Parameters) extends Module with CoreParams with UniformPrintfs {
val io = IO(new Bundle {
val inputArg = Vec(NumInputs, Flipped(Decoupled(new DataBundle())))
val Args = Vec(for (i <- 0 until ArgsOut.length) yield {
val arg = Vec(ArgsOut(i), Decoupled(new DataBundle()))
arg
})
DataBundle() is a custom data type which I have defined it in a separate file. What I really want to do is that. I want to pass an array to the module like ArgsOut and then build a vector of vector of Databundle and each subsequent vector has its own number of Databundle which comes from the input array.
The loopLatch module is a wrapper for other module, LiveInNode which I have designed. Actually the LoopLatch module only has a vector of this elements and then provide an IO for it.
Here is the actual code:
class LoopStart(val NumInputs: Int,
val ArgsOut: Array[Int],
val ID: Int)
(implicit val p: Parameters) extends Module with CoreParams with UniformPrintfs {
val io = IO(new Bundle {
val inputArg = Vec(NumInputs, Flipped(Decoupled(new DataBundle())))
val Args = Vec(ArgsOut.length, Vec(ArgsOut(0), Decoupled(new DataBundle())))
val Out = new VecVecBundle(
Seq("inputArg" -> Vec(NumInputs, Flipped(Decoupled(new DataBundle())))) ++
(ArgsOut.indices).map { i =>
val arg = Vec(ArgsOut(i), Decoupled(new DataBundle()))
s"Args_$i" -> arg
}:_*
)
val pSignal = Vec(NumInputs, Flipped(Decoupled(Bool())))
val Finish = Vec(NumInputs, Flipped(Decoupled(new ControlBundle())))
}
)
val Args = for (i <- 0 until NumInputs) yield {
val arg = Module(new LiveInNode(NumOuts = 1, ID = i))
arg
}
//Iterating over each loopelement and connect them to the IO
for (i <- 0 until NumInputs) {
Args(i).io.InData <> io.inputArg(i)
Args(i).io.Finish <> io.Finish(i)
Args(i).io.pred <> io.pSignal(i)
}
for (i <- 0 until ArgsOut.length) {
io.Out("Args_0") <> Args(i).io.Out
}
}