How to split an UInt() into a Vec of UInt to do subword extraction and assignment? - chisel

I have a 16bits register declared like that :
val counterReg = RegInit(0.U(16.W))
And I want to do indexed dibit assignment on module output like that :
//..
val io = IO(new Bundle {
val dibit = Output(UInt(2.W))
})
//..
var indexReg = RegInit(0.U(4.W))
//..
io.dibit = vectorizedCounter(indexReg)
But I have some difficulties to know how to declare vectorizedCounter().
I found some examples using Bundles, but for Vector I don't know. And I can't manage to do that with UInt():
val counterReg = RegInit(UInt(16.W))
//...
io.dibit := counterReg(indexReg*2.U + 1.U, indexReg*2.U)

You could dynamically shift and bit extract the result:
io.dibit := (counterReg >> indexReg)(1, 0)

Related

Generate dynamic bundle

In my project, I have many different custom Bundle.
They can be completely different.
For example these ones:
class MyBundle extends Bundle {
val v1 = UInt(8.W)
val v2 = Bool()
val v3 = UInt(4.W)
}
class MyBundle2 extends Bundle {
val v4 = UInt(18.W)
val v5 = UInt(2.W)
}
...
Instead of manually creating new Bundle to perform each operation, I want to be able to generate for all of them the corresponding Bundle.
So for MyBundle, I want to do:
// Must be generated for each Bundle
class MyGenBundle extends Bundle {
val v1 = UInt(log2Ceil(8 + 1).W) // width = MyBundle.v1 width + 1
val v2 = UInt(log2Ceil(1 + 1).W) // width = MyBundle.v2 width + 1
val v3 = UInt(log2Ceil(4 + 1).W) // width = MyBundle.v3 width + 1
}
class MyModule extends Module {
...
...
val w_b = Wire(new MyBundle())
val w_gb = Wire(new MyGenBundle())
// Must be automated for each Bundle
w_gb.v1 := PopCount(w_b.v1)
w_gb.v2 := PopCount(w_b.v2)
w_gb.v3 := PopCount(w_b.v3)
}
My goal is to automatically generate MyGenBundle (or similar directly in MyModule) from MyBundle, and perform in MyModule the same operation to all signals.
It also means that I need to dynamically address all signals in each Bundle.
Finally, I think the solution can have the following form:
val w_b = Wire(new MyBundle())
val w_gb = Wire(new AutoGenBundle(new MyBundle())) // AutoGenBundle generates a Bundle from parameter
val v_sig = Seq(v1, v1, v3) // Can be recovered automatically
// from w_b.elements I think
foreach (s <- v_sig) {
w_gb."s" := PopCount(w_b."s") // Here "s" the dynamic name of the signal
} // But how in the real case ?
Is this working technically possible in Chisel/Scala?
If so, do you have any ideas for implementing it?
Basic solution is to use the MixedVec chisel type for GenericBundle
class MyBundle extends Bundle {
val v1 = UInt(8.W)
val v2 = Bool()
val v3 = UInt(4.W)
}
class GenericBundle[T <: Bundle](data: T) extends Bundle {
val v = MixedVec(data.elements.values.map(x => UInt(log2Ceil(x.getWidth + 1).W)).toList)
def popCount(data: T): Unit = {
data.elements.values.zip(v).foreach { case (left, right) =>
right := PopCount(left.asUInt())
}
}
}
class MyModule extends MultiIOModule {
val w_b = IO(Input(new MyBundle))
val w_gb = IO(Output(new GenericBundle(w_b)))
w_gb.popCount(w_b)
}

Conditional Bulk Connection <>

I would like to do a conditional bulk connection of bidirectional buses, conceptually like the following.
val io = IO(new Bundle {
val master = Decoupled(UInt(8.W))
val slave0 = Flipped(Decoupled(UInt(8.W)))
val slave1 = Flipped(Decoupled(UInt(8.W)))
val select = Input(Bool())
})
when (select) {
io.slave0 <> io.master
io.slave1 <> some_null_decoupled
}.otherwise {
io.slave1 <> io.master
io.slave0 <> some_null_decoupled
}
This is cleaner than having to individually describe the logic for the io.master.ready, io.slave0.bits, io.slave0.valid, ... etc signals.
Is there a syntax similar to this which will work? When I try this in my code, I get a lot of firrtl.passes.CheckInitialization$RefNotInitializedException messages.
I suspect the issue is with the description of some_null_decoupled. That looks sane other than the fact that some_null_decoupled is missing. The following works fine for me (using Chisel 3.1.6):
import chisel3._
import chisel3.util._
class ConditionalBulkConnect extends Module {
val io = IO(new Bundle {
val master = Decoupled(UInt(8.W))
val slave0 = Flipped(Decoupled(UInt(8.W)))
val slave1 = Flipped(Decoupled(UInt(8.W)))
val select = Input(Bool())
})
val some_null_decoupled = Wire(Decoupled(UInt(8.W)))
some_null_decoupled.ready := false.B
when (io.select) {
io.slave0 <> io.master
io.slave1 <> some_null_decoupled
}.otherwise {
io.slave1 <> io.master
io.slave0 <> some_null_decoupled
}
}
object ConditionalBulkConnectTop extends App {
chisel3.Driver.execute(args, () => new ConditionalBulkConnect)
}
Does this help at all? Otherwise can you provide more information, like the implementation of some_null_decoupled and version of Chisel?

What is the meaning of :*= and :=* operators?

I see some examples in the RocketChip, but could not find info in the API reference
masterNode :=* tlOtherMastersNode
DisableMonitors { implicit p => tlSlaveXbar.node :*= slaveNode }
These are not Chisel operators. Instead, they're defined and used by Rocket Chip's diplomacy package. These are shorthand operators for doing different types of binding between diplomatic nodes.
No published API documentation for this exists, but you can start poking around in the diplomacy package. The releveant location where these are defined is src/main/scala/diplomacy/Nodes.scala.
The pull request comment on this API is very infomative.
Commonly, A := B creates a pair of master and slave ports in A and B.
A :=* B means the number of port pairs is decided by number of B := Other,
and A :*= B vice versa.
The most counterintuitive part is that the duplication of links is not achieved by
duplication of the intermediate module, but the intermediate module's expanding of port list.
I have written a simple example to explore star connectors' behavior.
In the following code snippet, an TLIdentifyNode connects 3 TLClientNode using :=,
and then it connects to a crossbar node using :=* as master to crossbar.
Meanwhile, an TLIdentifyNode connects 2 TLManagerNode using :=,
and then it connects to the same crossbar node using :*= as salve to crossbar.
import chisel3._
import chisel3.core.dontTouch
import freechips.rocketchip.config._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
class ClientConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class ManagerConnector(implicit p: Parameters) extends LazyModule {
val node = TLIdentityNode()
override lazy val module = new LazyModuleImp(this) {
(node.in zip node.out) foreach { case ((bundleIn, edgeIn), (bundleOut, edgeOut)) =>
bundleOut <> bundleIn
}
}
}
class Client(implicit p: Parameters) extends LazyModule {
val node = TLClientNode(
portParams = Seq(
TLClientPortParameters(Seq(
TLClientParameters("myclient1", IdRange(0, 1), supportsGet = TransferSizes(4), supportsProbe = TransferSizes(4))
))
)
)
override lazy val module = new LazyModuleImp(this) {
node.out.foreach { case(bundle, edge) =>
val (legal, a) = edge.Get(0.U, 0x1000.U, 2.U)
bundle.a.bits := a
bundle.a.valid := legal
bundle.d.ready := true.B
dontTouch(bundle)
}
}
}
class Manager(base: Int)(implicit p: Parameters) extends LazyModule {
val node = TLManagerNode(Seq(TLManagerPortParameters(Seq(TLManagerParameters(
address = Seq(AddressSet(base, 0xffff)),
supportsGet = TransferSizes(4)
)), beatBytes = 4)))
override lazy val module = new LazyModuleImp(this) {
node.in.foreach { case (bundle, edge) =>
when (bundle.a.fire()) {
val d = edge.AccessAck(bundle.a.bits, 0xdeadbeafL.U)
bundle.d.bits := d
bundle.d.valid := true.B
}
bundle.a.ready := true.B
}
}
}
class Playground(implicit p: Parameters) extends LazyModule {
val xbr = TLXbar()
val clientConnectors = LazyModule(new ClientConnector())
val managerConnectors = LazyModule(new ManagerConnector())
val clients = Seq.fill(3) { LazyModule(new Client()).node }
val managers = Seq.tabulate(2) { i: Int => LazyModule(new Manager(0x10000 * i)).node }
clients.foreach(clientConnectors.node := _)
managers.foreach(_ := managerConnectors.node)
managerConnectors.node :*= xbr
xbr :=* clientConnectors.node
override lazy val module = new LazyModuleImp(this) {
}
}
The corresponding verilog code of ManagerConnector is (in brief):
module ManagerConnector(
`tilelink_bundle(auto_in_0),
`tilelink_bundle(auto_in_1),
`tilelink_bundle(auto_out_0),
`tilelink_bundle(auto_out_1)
);
// ...
endmodule
We can see diplomacy framework is only responsible for parameter negotiation, port list generation and port connection. The duplication introduced by * connection is guaranteed by the common code pattern:
(node.in zip node.out) foreach { ... }
In my opinion, this API simplifies connection between crossbar node and various nodes inside a specific module, and keeps the connection syntax consistent.
[Reference] A rocketchip reading note: https://github.com/cnrv/rocket-chip-read/blob/master/diplomacy/Nodes.md
It might be useful to read the documentation on diplomacy by lowrisc: https://www.lowrisc.org/docs/diplomacy/

test rocket chip util 'Arbiters.scala', got error 'bits operated ... must be hardware ..."

I copied a rocket-chip util module 'Arbiters.scala' to a separate work directory, test with the following code:
object try_arbiter extends App {
chisel3.Driver.execute(args, () => new HellaCountingArbiter(UInt(4.W), 3, 5))
}
There's no problem when compiling. However, in the step 'Elaborating design...', it reported [error] chisel3.core.Binding$ExpectedHardwareException: mux condition 'chisel3.core.Bool#46' must be hardware, not a bare Chisel type"
The related code is
PriorityEncoder(io.in.map(_.valid))
when I change this line to the following one, the error's gone (the scala code still has this kind of errs, but not in this line).
PriorityEncoder(io.in.map(x => Wire(x.valid)))
The rocket chip codes should have been evaluated for many times, right? I think there must have sth I have missed... Some configurations?
Thank you for any hints!
Appendix(the Arbiters.scala):
package rocket_examples
import chisel3._
import chisel3.util._
/** A generalized locking RR arbiter that addresses the limitations of the
* version in the Chisel standard library */
abstract class HellaLockingArbiter[T <: Data](typ: T, arbN: Int, rr: Boolean = false)
extends Module {
val io = new Bundle {
val in = Vec(arbN, Decoupled(typ.cloneType)).flip
val out = Decoupled(typ.cloneType)
}
def rotateLeft[T <: Data](norm: Vec[T], rot: UInt): Vec[T] = {
val n = norm.size
Vec.tabulate(n) { i =>
Mux(rot < UInt(n - i), norm(UInt(i) + rot), norm(rot - UInt(n - i)))
}
}
val lockIdx = Reg(init = UInt(0, log2Up(arbN)))
val locked = Reg(init = Bool(false))
val choice = if (rr) {
PriorityMux(
rotateLeft(Vec(io.in.map(_.valid)), lockIdx + UInt(1)),
rotateLeft(Vec((0 until arbN).map(UInt(_))), lockIdx + UInt(1)))
} else {
PriorityEncoder(io.in.map(_.valid))
}
val chosen = Mux(locked, lockIdx, choice)
for (i <- 0 until arbN) {
io.in(i).ready := io.out.ready && chosen === UInt(i)
}
io.out.valid := io.in(chosen).valid
io.out.bits := io.in(chosen).bits
}
/** This locking arbiter determines when it is safe to unlock
* by peeking at the data */
class HellaPeekingArbiter[T <: Data](
typ: T, arbN: Int,
canUnlock: T => Bool,
needsLock: Option[T => Bool] = None,
rr: Boolean = false)
extends HellaLockingArbiter(typ, arbN, rr) {
def realNeedsLock(data: T): Bool =
needsLock.map(_(data)).getOrElse(Bool(true))
when (io.out.fire()) {
when (!locked && realNeedsLock(io.out.bits)) {
lockIdx := choice
locked := Bool(true)
}
// the unlock statement takes precedent
when (canUnlock(io.out.bits)) {
locked := Bool(false)
}
}
}
/** This arbiter determines when it is safe to unlock by counting transactions */
class HellaCountingArbiter[T <: Data](
typ: T, arbN: Int, count: Int,
val needsLock: Option[T => Bool] = None,
rr: Boolean = false)
extends HellaLockingArbiter(typ, arbN, rr) {
def realNeedsLock(data: T): Bool =
needsLock.map(_(data)).getOrElse(Bool(true))
// if count is 1, you should use a non-locking arbiter
require(count > 1, "CountingArbiter cannot have count <= 1")
val lock_ctr = Counter(count)
when (io.out.fire()) {
when (!locked && realNeedsLock(io.out.bits)) {
lockIdx := choice
locked := Bool(true)
lock_ctr.inc()
}
when (locked) {
when (lock_ctr.inc()) { locked := Bool(false) }
}
}
}
The issue is that almost all of the code in rocket-chip has been written against older Chisel2-style APIs and should be compiled with the compatibility wrapper import Chisel._. I see you used import chisel3._ which has somewhat stricter semantics.
For this specific case, the difference between Chisel2 and Chisel3 is that ports (val io) must be wrapped in IO(...), ie.
val io = IO(new Bundle {
val in = Flipped(Vec(arbN, Decoupled(typ)))
val out = Decoupled(typ)
})
Note that I also changed Vec(arbN, Decoupled(typ.cloneType)).flip to Flipped(Vec(arbN, Decoupled(typ))) and removed the .cloneType on val out. The latter two changes are not required for this to compile but they will be flagged as deprecation warnings as of Chisel 3.1.2.

Scala Lift - Dynamically called function

I've got a function which loads various models, and currently have this kind of setup:
if(message == "user") {
var model = User.findAll(
("room" -> "demo")
)
} else if (message == "chat") {
var model = Chat.findAll(
("room" -> "demo")
)
}
This is really clunky as I aim to add lots more models in future, I know in javascript you can do something like this:
var models = {
"user" : load_user,
"chat" : load_chat
}
Where "load_user" and "load_chat" would load the respective models, and so I can streamline the whole thing by doing:
var model = models[message]();
Is there a way I can do something similar in Scala, so I can have a simple function which just passes the "message" var to a List or Object of some kind to return the relevant data?
Thanks in advance for any help, much appreciated :)
In Scala you can do:
val model = message match {
case "user" => loadUser() // custom function
case "chat" => loadChat() // another custom function
case _ => handleFailure()
}
You can as well work with a Map like you did in your JavaScript example like so:
scala> def loadUser() = 1 // custom function
loadUser: Int
scala> def loadChat() = 2 // another custom function
loadChat: Int
scala> val foo = Map("user" -> loadUser _, "chat" -> loadChat _)
foo: scala.collection.immutable.Map[java.lang.String,() => Int] = Map(user -> <function0>, chat -> <function0>)
scala> foo("user")()
res1: Int = 1
Pay attention to the use of "_" in order to prevent evaluation of loadUser or loadChat when creating the map.
Personally, I'd stick with pattern matching.