"for-loop" to reduce coding on Chisel3.2 - chisel

I want to code with for-loop for redundant part.
Coding.
//Priority Encoder
class P_Encoder(NumInputs: UInt) extends Module {
val io = new Bundle {
val Req[NumInputs] = Bool(INPUT) //Requests
val Rls[NumInputs] = Bool(INPUT) //Releases
val Grant[NumInputs] = UInt(OUTPUT(log(NumInputs))) //Grants
}
val cnt = 0
for (i<-0 to NumInputs-1) {
when (io.Req[i] & !io.Rls[i]) {
cnt := cnt + 1.W
io.Grant[i] = cnt
}
else {
io.Grant[i] = 0.W
}
}
}
I want to code using "for-loop" to code redundant part.

There are a few minor issues with this code:
Generally we name variables starting with a lower-case letter in Scala, this is primarily style, but it does have semantic meaning it certain circumstances (like pattern matching)
Square brackets are only used for type parameters (like Java generics), indexing uses normal parentheses in Scala
An input Vec of Bools is normally defined as: val req = Input(Vec(numInputs, Bool())) (assuming import chisel3._, but this should also work in Chisel._ for Chisel 3.2)
if and else are used for static parameterization (ie. at hardware elaboration time) while when and .otherwise are used for dynamic logic (eg. actual muxes)
UInt is for hardware types, if you have a static parameter (like numInputs), use a Scala Int
Other than the minor syntactical stuff, the trickiest part of getting this code right is understanding the difference between Scala constructs that are only run at elaboration time (ie. when the Scala program generates the hardware), vs. what actually shows up in the hardware. I would suggest reading this thread from the chisel-users mailing list for some more context about some of this stuff: https://groups.google.com/d/msg/chisel-users/gRoNnH-Y5hE/ynDCtmNPCAAJ
I'm a little confused as to what value io.grant is supposed to get, but I'm assuming it should be the index of the highest priority io.req.
Here's an untested version of your code that I think should work and do what you want:
//Priority Encoder
class P_Encoder(numInputs: Int) extends Module {
// We wrap ports in IO
val io = IO(new Bundle {
val req = Input(Vec(numInputs, Bool()))
val rls = Input(Vec(numInputs, Bool()))
val grant = Output(UInt(log2Up(numInputs).W))
})
io.grant := 0.U // default grant value
// Due to Chisel last connect semantics, the last connection wins
// Thus the highest index will have priority
for (i <- 0 to numInputs - 1) {
when (io.req(i) && !io.rls(i)) {
io.grant := i.U
}
}
}
This code is tricky because it's mixing an elaboration time for loop with hardware whens and connections, I'm going to manually unroll this loop to illustrate what it's doing:
io.grant := 0.U
when (io.req(0) && !io.rls(0)) {
io.grant := 0.U
}
when (io.req(1) && !io.rls(1)) {
io.grant := 1.U
}
when (io.req(2) && !io.rls(2)) {
io.grant := 2.U
}
...
Alternatively, we can just reuse the built-in PriorityEncoder utility if we want
import chisel3.util.PriorityEncoder
val enables = io.req.zip(io.rls).map { case (x, y) => x && !y }
// PriorityEncoder gives priority to *lowest* order bit
io.grant := PriorityEncoder(enables)

I agree with everything #jkoenig says.
In an another example assuming Jack's IO structure, there are times I like
using foldLeft combined with when/elsewhen
io.rel.zip(io.req).zipWithIndex.foldLeft(when(false.B){}) { case (lastWhen,((req, rel), index)) =>
lastWhen.elsewhen(req && !rel) {
io.grant := index.U
}
} otherwise {
io.grant := 0.U
}
when and elsewhen both return a WhenClause which can be used with foldLeft to keep adding clauses.

Related

suggestName for IO(Vec(...))

I have a module like so...
class ApbSplitter (clients : List[ApbRange]) extends MultiIOModule {
val nApb = clients.length
val apb = IO(Vec(nApb, new ApbChannel()))
val apb_m = IO(Flipped(new ApbChannel))
...
What I'd like to do is suggestName to each element of the Vec so that instead of prefixed as apb_0_ apb_1_ etc... it's whatever I provide for each element.
I can apb.suggestName but that only affects the leading prefix and the array indices remain. Doing apb(idx).suggestName("blah") compiles but has no effect.
Any way to make this happen?
Got this to work by eliminating the Vec and creating a list of IO
case class ApbRange (name: String, loAddr : Int, hiAddr : Int)
class ApbSplitter (clients : List[ApbRange]) extends MultiIOModule {
val apb = clients.map({x => IO(new ApbChannel).suggestName(x.name)})
val apb_m = IO(Flipped(new ApbChannel))
...
Not sure if this is canonical but seems to do the trick just fine.
Answering this with Brian's other post and comment on his own answer on this post in mind. This is going to be a long answer because it touches on a couple of warts in the Chisel API that are being improved but are certainly relevant in the current version (v3.4.3 as of 12 Aug 2021).
Brian's answer is correct that if you want to name the individual fields you need to use a Seq and not a Vec. The reason for this is that, from Chisel's perspective, an IO of type Vec is a single port with an aggregate type, whereas the Seq is just a sequence of unrelated ports. The Seq is a Scala construct (whereas Vec comes from Chisel), so Chisel itself doesn't know anything about the relationship between the ports in the Seq.
The problem then, is that you need a Vec to do dynamic indexing. You can use VecInit to create a dynamically indexable Wire from your Seq whenever you need to do dynamic indexing:
For example:
class MyModule(names: Seq[String]) extends RawModule {
val enq = names.map(n => IO(Flipped(Decoupled(UInt(8.W)))).suggestName(n))
val idx = IO(Input(UInt(log2Ceil(names.size).W)))
val deq = IO(Decoupled(UInt(8.W)))
// enqWire connects all fields of enq
val enqWire = VecInit(enq)
// Need to make sure backpressure is always driven
enqWire.foreach(_.ready := false.B)
deq <> enqWire(idx)
}
This will work so long as deq is itself a port. It will not work if deq were a Wire because <> is a commutative operator and is thus ambiguous when connecting 2 bidirectional wires. For a longer explanation, see this PR comment.
If deq needs to be a Wire for some reason, you could use a helper module that does have Vecs as ports:
For example:
class InnerHelper(n: Int) extends RawModule {
val enq = IO(Flipped(Vec(n, Decoupled(UInt(8.W)))))
val idx = IO(Input(UInt(log2Ceil(n).W)))
val jdx = IO(Input(UInt(log2Ceil(n).W)))
val deq = IO(Vec(n, Decoupled(UInt(8.W))))
// backpressure defaults
enq.foreach(_.ready := false.B)
deq.foreach { x =>
x.valid := false.B
x.bits := DontCare
}
deq(jdx) <> enq(idx)
}
class MyModule(names: Seq[String]) extends RawModule {
val enq = names.map(n => IO(Flipped(Decoupled(UInt(8.W)))).suggestName(n))
val idx = IO(Input(UInt(log2Ceil(names.size).W)))
val jdx = IO(Input(UInt(log2Ceil(names.size).W)))
val deq = names.map(n => IO(Decoupled(UInt(8.W))).suggestName(s"${n}_out"))
val helper = Module(new InnerHelper(names.size))
helper.enq <> enq
helper.idx := idx
helper.jdx := jdx
helper.deq <> deq
}
It's a bit of a pain, but it at least resolves the ambiguity. There are other utilities we could build--for example, instead of a custom InnerHelper for each case, we could make a utility method that creates a module so that the returned value of dynamically indexing a Seq is a port of a new submodule, but it's a bit tricky.
The good news is that a better way is coming--DataView in Chisel 3.5 should make it possible to view a Seq as a Vec (rather than having to use VecInit which creates a Wire) which makes it easier to avoid this Wire <> connect ambiguity issue. I also hope to either "fix" <> for Wires or perhaps provide a new operator that is not commutative :<>, but that is not yet being worked on.
I am guessing your new apbChannel has a bunch of Input Output signals or wires. So instead of apb(idx).suggestName if your apbChannel has a (say) val ip = Input(Bool()) you can do apb(idx).ip.suggestName("blah")

Is there an accepted way to get a Gray Code counter in Chisel?

I'm looking to write counters in Chisel3 that will be used to address subunits. If the counter matches some register in a subunit then the subunit fires, otherwise it doesn't.
I would much rather have the addresses cycle in Gray code than in binary. It's easy enough to write a binary counter in Chisel, but I see no provision for a Gray code counter.
I can write a new type akin to Uint and Sint, but I'm reluctant to reinvent it if it's already out there. Yet I don't see anything in the cookbook or other docs about Gray code. Github just turns up a Minecraft-oriented repo (because it matches "chisel") There is existing stuff for VHDL but I want to express this in Chisel.
So have I missed a resource that would provide a Gray counter in Chisel? Failing that, is building a new type akin to Uint a reasonable way to proceed?
I did a quick look around and didn't find anything quite like what you're looking for. The closest thing I could find was a simple Gray counter in rocket-chip (https://github.com/chipsalliance/rocket-chip/blob/29ce00180f2a69947546d6385a1da86cbc584376/src/main/scala/util/AsyncQueue.scala#L49) but it uses regular binary counting and then just returns a UInt in Gray code. It also doesn't take advantage of any Scala type safety.
I think this would be a reasonable thing to build, and if you want you could contribute it to https://github.com/freechipsproject/ip-contributions for increased visibility.
I think if you wanted a proper GrayCode type, it would be reasonable to create a custom type. Unfortunately, there is no way to extend Data for a Bits-like type (all of the types in that hierarchy are sealed), but you could create a custom Bundle that wraps a UInt and then implement your own set of operations, eg.
class GrayCode(private val w: Int) extends Bundle {
val value = UInt(w.W)
def +(that: GrayCode): GrayCode = ???
}
object GrayCode {
// Lets you write GrayCode(4.W)
// Width is defined in chisel3.internal.firrtl though which is awkward...
def apply(width: Width): GrayCode = ???
}
This is just a quick sketch. The DSP Tools library has examples of custom types for DSP: https://github.com/ucb-bar/dsptools
They tend to use Scala Typeclasses a lot which is a more advanced Scala feature. Just mentioning in case some of the syntax in their looks alien.
You might take a look at this link programmersought gray code fifo it seems like it may be relevant but I am not familiar with it otherwise.
As with Jack I'm not familiar with the math needed to actually increment values in Gray code, but something like the following code would convert Gray code to binary, add, then convert it back to Gray code. I'm not sure if the Vec() code below would work correctly but should make the idea clear.
import chisel3._
import chisel3.util._
class GrayCode(private val w: Int) extends Bundle {
val value = UInt(w.W)
def bin2grey(x : UInt) : UInt = {
x ^ (x >> 1.U)
}
def grey2bin(x : UInt, n : Int) : UInt = {
val tmp = Wire(Vec(n, Bool()))
tmp(n-1) := x(n-1)
for (i <- 0 to (n-2)) {
tmp(i) := x(i) ^ tmp(i+1)
}
Cat(tmp.reverse)
}
def +(that: GrayCode): GrayCode = {
val sum = new GrayCode(w)
sum.value := grey2bin(bin2grey(this.value) + bin2grey(that.value), w)
sum
}
}
It seems like all implementations here use binary-to-Gray conversion. For asynchronous FIFOs, this only works if the Gray code is latched just before crossing clock domains. What if you want a counter that actually counts Gray codes instead of converting binary values to Gray codes?
One option is to convert Gray to binary, add, then convert back to Gray and store the result. The other is to use custom arithmetic to calculate the next Gray value in the sequence. The typical sequence is a reflected-binary Gray code, but others exist.
The code below implements a Gray code counter using a reflected-binary Gray code. It was adapted from this blog post. It only counts up. It works like the Chisel Counter object, except it adds support for a synchronous reset and custom register name. It returns the counter and wrap status.
import chisel3._
import chisel3.util._
// a Gray counter counts in Gray code
object GrayCounter {
// Gray unit cell
// b is the current state of this bit
// returns (t, z_o) where t is the next state of this bit
def grayCell(b: Bool, q_i: Bool, z_i: Bool, enable: Bool, parity: Bool): (Bool, Bool) = {
(b ^ (enable && q_i && z_i && parity), (!q_i) && z_i)
}
// cond = counts when true
// n = count value, must be a power of 2
// synchronousReset = resets counter to 0
// name = name for this counter
def apply(cond: Bool, n: Int, synchronousReset: Bool = false.B, name: String = null) = {
require(isPow2(n), s"Gray counter must have power-of-2 length (you asked for $n)")
require(n > 2, s"Gray counter minimum count is 4 (you asked for $n)")
val counter = RegInit(0.U(log2Ceil(n).W))
if (name != null) {
counter.suggestName(name)
}
val counterNext = Wire(Vec(log2Ceil(n), Bool()))
counter := counterNext.asUInt
val z_wires = Wire(Vec(log2Ceil(n), Bool()))
val parity = counter.xorR
for (i <- 0 until log2Ceil(n)) {
if (i == 0) {
val grayCellOut = grayCell(counter(i), true.B, true.B, cond, !parity)
counterNext(i) := grayCellOut._1
z_wires(i) := grayCellOut._2
} else {
val grayCellOut = grayCell(counter(i), counter(i-1) || (i == log2Ceil(n)-1).B,
z_wires(i-1) || (i == 1).B, cond, parity)
counterNext(i) := grayCellOut._1
z_wires(i) := grayCellOut._2
}
}
when (synchronousReset) {
counter := 0.U
}
val wrap = counter === (n/2).U && cond
(counter, wrap)
}
}

How to initialize a Reg of Bundle in Chisel?

I declared a Bundle for my specific data :
class RValue (val cSize: Int = 16) extends Bundle {
val rvalue = Output(UInt(cSize.W))
val er = Output(UInt((cSize/2).W))
val part = Output(Bool()) /* set if value is partial */
}
And I want to use it as a register in my module :
val valueReg = Reg(new RValue(cSize))
//...
valueReg.rvalue := 0.U
valueReg.er := 0.U
That works well. But I want to initialize it at Register declaration with RegInit(). Is it Possible ?
val valueReg = RegInit(new RValue(cSize), ?? ) ??
Chick's answer of using Bundle Literals is the cool new way and is nice because you can give a Bundle arbitrary values in a single expression.
If you just want to zero-out the register at reset type, you could always cast from a literal zero to the Bundle:
val valueReg = RegInit(0.U.asTypeOf(new RValue(cSize))
You can do similar things with any literal if you want, but I wouldn't recommend it unless you're zeroing out or setting everything to 1s.
For setting each field to some other value, I think Chick's way is better, but the normal style you'll see in older code is something like:
val valueReg = RegInit({
val bundle = Wire(new RValue(cSize))
bundle.rvalue := 1.U
bundle.er := 2.U
bundle.part := 3.U
bundle
})
In Scala, you can put { } anywhere an expression is needed and the last expression in the Block will be the return value. Thus we can create a Wire with the values we want to reset the register to and then pass that Bundle as the initialization value. It would be equivalent to write:
val valueRegInit = Wire(new RValue(cSize))
valueRegInit.rvalue := 1.U
valueRegInit.er := 2.U
valueRegInit.part := 3.U
val valueReg = RegInit(valueRegInit)
I hope this helps!
BundleLiterals are the new way to do this. First
import chisel3.experimental.BundleLiterals._
Then
val valueReg = RegInit((new RValue(cSize)).Lit(_.rvalue -> 1.U, _.er -> 2.U, _.part -> true.B)
It's possible there will be some problem with having declared the fields in the Bundle with the OutputBinding. I would probably leave that off and just wrap with the output when necessary, e.g.
val rValueOut = IO(Output(new RValue(csize)))

Chisel : When-otherwise clause not working in function definition

I am trying to develop a simple circuit using Chisel 3 to generate the factorial for a number n. Here's my implementation :
class Factorial extends Module{
val io = IO(new Bundle{
val input = Input(UInt(8.W))
val output = Output(UInt(16.W))
})
def factorial(n: UInt): UInt = {
when (n === 0.U) {1.U}
.otherwise {n*factorial(n-1.U)}
}
io.out := factorial(io.in)
}
However, when I try to run it, I get the following error :
cmd26.sc:9: type mismatch;
found : Unit
required: chisel3.UInt
.otherwise {n*factorial(n-1.U)}
^Compilation Failed
Is there any particular reason for this? How do I solve this issue?
Also, I realize that an easy solution is to just have the number n to be of type Int, and have an if-else clause instead. Is there any way to type cast the parameter being passed during function call (i.e. from chisel3.UInt to Int)?
The Chisel when,elsewhen, and otherwise statement do not return a value.
Your design seems to be an attempt to compute the factorial value for an input in a single cycle. This is only going be practical for small input values and would probably be easier to implement via a lookup table.
I think what you are looking for (which would be a good learning exercise) is to build a circuit that given an input will return the factorial value after some number of cycles. This is very very similar to the way the GCD example works, GCD is included as an example in the chisel-template repo as an example. To do this you will need registers and ready and valid ports.
I suggest you figure out how that works and you should have a much easier time making your factorial. Good luck. And as suggested by #FabienM you will need a very large output port to contain the answer for even modest input values.
I thinks you can't do that. when(){}.otherwise{} is an hardware construction that don't return any value (Unit) as we can see in code.
With this construct you want to generate hardware «on the fly», which is impossible.
I think you have generate all solutions directly like it:
class Factorial extends Module{
val io = IO(new Bundle{
val input = Input(UInt(8.W))
val output = Output(UInt(1676.W))
})
def factorial(n: BigInt): BigInt = {
if(n == 0){
1
}else{
n*factorial(n-1)
}
}
io.output := 0.U
for(i <- 0 to 0xFF) {
when(io.input === i.U){
io.output := factorial(i).U
}
}
}
You can keep your recursive scala fonction but just for hardware generation step.
Note that 255! is a really big number you will need more than 16 bits UInt to output the value ;)

How to add a sbus master to rocket-chip periphery

I'm trying to implement a DMA like periphery to the rocket chip.
Meaning a module that is hooked to the pbus, and controlled by registers.
it also has a master hooked to the sbus.
I followed the sifive format to attach registers controlled peripheries without any problems.
My question is how do I add the sbus master ?, the bellow is what I've tried before getting to dead end.
To the attach parameters class I've added the sbus:
case class dmaAttachParams(
dma : dmaParams,
controlBus: TLBusWrapper,
masterBus : TLBusWrapper, // <-- my addition
....
) (implicit val p: Parameters)
Then I modified the attach method in the factory object:
def attach(params: dmaAttachParams): TLdma = {
implicit val p = params.p
val name = s"dma_${nextId()}"
val cbus = params.controlBus
val mbus = params.masterBus // <- my addition
val dma = LazyModule(new TLdma(params.dma))
dma.suggestName(name)
cbus.coupleTo(s"slave_named_name") {
dma.controlXing(params.controlXType) := TLFragmenter(cbus.beatBytes, cbus.blockBytes) := _
}
InModuleBody { dma.module.clock := params.mclock.map(_.getWrappedValue).getOrElse(cbus.module.clock) }
InModuleBody { dma.module.reset := params.mreset.map(_.getWrappedValue).getOrElse(cbus.module.reset) }
// this section is my problem // <-- this section is my addition
mbus.from(s"master_named_name") {
mbus.inwardNode := TLBuffer() := dma.mnode // <- what should i do here ???
}
dma
}
The mndoe is a node I have add to the dma class like this:
val mnode = TLClientNode(Seq(TLClientPortParameters(Seq(TLClientParameters(name = "dmaSbusMaster")))))
what should be the body of the mbus.from() method that will do the work?
trying to build this code gives this error:
Caused by: java.lang.IllegalArgumentException: requirement failed: buffer.node (A adapter node with parent buffer inside coupler_from_master_named_name) has 1 inputs and 0 outputs; they must match (Buffer.scala:69:28)
Any help will appreciated, in the rocket chip github issue forum, they no longer answer support questions. So if someone from there can answer here it will be great, as I am really stuck here.
P.S. just adding the way the attach method is invoked:
trait HasPeripheryDma { this: BaseSubsystem =>
val dmaNodes = p(PeripheryDmaKey).map { ps =>
dma.attach(dmaAttachParams(ps, pbus, sbus))
}
}
Update:
Implementing the body of the mbus.from() method as below:
mbus.from(s"master_named_name") {
mbus.inwardNode := TLBuffer(BufferParams.default) := dma.mnode
}
Does create a coupler from the dma on the SBUS , but it is not connected to the dma periphery. Any Ideas ?
I don't understand what is going wrong in your "Update", but this should work:
mbus.coupleFrom("master_named_dma") {
_ := TLBuffer(BufferParams.default) := dma.mnode
}
I have managed to attach the SBUS by reverse engineering of the way the slave is attached.
If someone can/wants to elaborate more feel free to do so.
I have added a "TLOutwardCrossingHelper" field to the DMA periphery like this:
class TLdma(params : dmaParams) (implicit p: Parameters) extends dma(params) with HasTLControlRegMap {
val controlXingMaster : TLOutwardCrossingHelper = this.crossOut(mnode)
}
please note that equivalent "TLInwardCrossingHelper" is defined in the "HasTLControlRegMap " trait that we extending.
Then,In the attach method, the next line did the work:
_ := TLBuffer(BufferParams.default) := dma.controlXingMaster(params.controlXType)
In this way I was able to also hook the periphery to the coupler on the sbus.
I assume the crossing object does something to the node , but I don't know what.