How to add reset to the Queue chisel class - chisel

I am trying to use the chisel Queue class and want to be able to flush it on reset.
It seems that in the past there was an option for a reset in the Class constructor
#deprecated("Module constructor with override _reset deprecated, use withReset", "chisel3")
def this(gen: T, entries: Int, pipe: Boolean, flow: Boolean, _reset: Bool)
However, as the deprecation message implies I should use withReset.
how do I do that ?
this question from 3 years ago is similar but the answer suggest to use _reset argumnet which is now deprecated.

That deprecation method is referring to withReset. The (Spartan) API documentation can be found here and the source code is located here. Additionally, there's a discussion of multiple clock domains on the Chisel3 Wiki that gives some example usages.
These methods enable you to change which clock, reset, or clock and reset are used within a block of code. If you want to change which reset is used, something like the following will do that:
import chisel3.experimental.withReset
// ...
withReset(myReset) {
// Anything in here will be reset to myReset
}
For a more detailed example, the MultiClockSpec tests provide some concrete examples. See: src/test/scala/chiselTests/MultiClockSpec.scala.

I managed to flush the Async fifo (Chisel 2 code) rewriting it with special Queue as following :
class QueueIOFlush[T <: Data](gen: T, entries: Int) extends Bundle {
val enq = Decoupled(gen.cloneType).flip
val deq = Decoupled(gen.cloneType)
val count = UInt(OUTPUT, log2Up(entries + 1))
val flush = Bool(INPUT)
}
/** Asynchronous Fifo. Used to cross two clock domains.
*
* #param T the data to transmit
* #param entries size of fifo. The actual size will be
* arrounded in the upper 2^n (size = 1<<log2Up(entries))
* #param enq_clk clock for the writing side (input)
* #param deq_clk clock for the reading side (output)
*/
class AsyncFifoFlushable[T<:Data](gen: T,
entries: Int,
enq_clk: Clock = null,
deq_clk: Clock) extends Module(enq_clk) {
val io = new QueueIOFlush(gen, entries)
val asize = log2Up(entries)
val s1_rptr_gray = Reg(init=UInt(0, asize+1), clock=enq_clk)
val s2_rptr_gray = Reg(init=UInt(0, asize+1), clock=enq_clk)
val s1_rst_deq = Reg(init=Bool(false), clock=enq_clk)
val s2_rst_deq = Reg(init=Bool(false), clock=enq_clk)
val s1_wptr_gray = Reg(init=UInt(0, asize+1), clock=deq_clk)
val s2_wptr_gray = Reg(init=UInt(0, asize+1), clock=deq_clk)
val s1_rst_enq = Reg(init=Bool(false), clock=deq_clk)
val s2_rst_enq = Reg(init=Bool(false), clock=deq_clk)
val s1_flush = Reg(init=Bool(false), clock=enq_clk)
val s2_flush = Reg(init=Bool(false), clock=enq_clk)
val slow_flush = Reg(init=Bool(false), clock=deq_clk)
val wptr_bin = Reg(init=UInt(0, asize+1), clock=enq_clk)
val wptr_gray = Reg(init=UInt(0, asize+1), clock=enq_clk)
val not_full = Reg(init=Bool(false), clock=enq_clk)
val wptr_bin_next = wptr_bin + (io.enq.valid & not_full)
val wptr_gray_next = (wptr_bin_next >> UInt(1)) ^ wptr_bin_next
val not_full_next = !(wptr_gray_next === Cat(~s2_rptr_gray(asize,asize-1),
s2_rptr_gray(asize-2,0)))
val rptr_bin = Reg(init=UInt(0, asize+1), clock=deq_clk)
val rptr_gray = Reg(init=UInt(0, asize+1), clock=deq_clk)
val not_empty = Reg(init=Bool(false), clock=deq_clk)
val rptr_bin_next = rptr_bin + (io.deq.ready & not_empty)
val rptr_gray_next = (rptr_bin_next >> UInt(1)) ^ rptr_bin_next
val not_empty_next = !(rptr_gray_next === s2_wptr_gray)
s2_rptr_gray := s1_rptr_gray; s1_rptr_gray := rptr_gray
s2_rst_deq := s1_rst_deq; s1_rst_deq := enq_clk.getReset
s2_wptr_gray := s1_wptr_gray; s1_wptr_gray := wptr_gray
s2_rst_enq := s1_rst_enq; s1_rst_enq := deq_clk.getReset
s1_flush := io.flush; s2_flush := s1_flush;
/* flush read pointer (deqclk) */
slow_flush := io.flush
when(slow_flush) {
rptr_bin := UInt(0, rptr_bin.getWidth())
}.otherwise{
rptr_bin := rptr_bin_next
}
/* flush write pointer (enqclk) */
when(s2_flush) {
wptr_bin := UInt(0, wptr_bin.getWidth())
}.otherwise{
wptr_bin := wptr_bin_next
}
wptr_gray := wptr_gray_next
not_full := not_full_next && !s2_rst_deq
rptr_gray := rptr_gray_next
not_empty := not_empty_next && !s2_rst_enq
io.enq.ready := not_full
io.deq.valid := not_empty
val mem = Mem(gen, 1 << asize, clock=enq_clk)
when (io.enq.valid && io.enq.ready) {
mem(wptr_bin(asize-1,0)) := io.enq.bits
}
io.deq.bits := mem(rptr_bin(asize-1,0))
}

Related

IP block generation/testing when using diplomacy. Possible to give dummy node?

I've been studying rocket-chip for utilizing diplomacy and I have a decent grasp on the overall structure of how diplomacy works. (I don't understand it totally, but well enough to create some examples on my own). I would like to develop some IP in which the main objective is to have a regmap through the use of a *RegisterRouter.
If I use/modify one of the RegisterNodeExamples from rocket-chip, I get the following:
class MyDeviceController(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("my-device", Seq("tutorial,my-device0"))
val node = APBRegisterNode(
//address = Seq(AddressSet(0x10028000, 0xfff)), (Modified since not in APBRegisterNode)
address = AddressSet(0x002000, 0xfff),
//device = device, (Removed since not in APBRegisterNode)
beatBytes = 8)
lazy val module = new LazyModuleImp(this) {
val bigReg = RegInit(0.U(64.W))
val mediumReg = RegInit(0.U(32.W))
val smallReg = RegInit(0.U(16.W))
val tinyReg0 = RegInit(0.U(4.W))
val tinyReg1 = RegInit(0.U(4.W))
node.regmap(
0x00 -> Seq(RegField(64, bigReg)),
0x08 -> Seq(RegField(32, mediumReg)),
0x0C -> Seq(RegField(16, smallReg)),
0x0E -> Seq(
RegField(4, tinyReg0),
RegField(4, tinyReg1)))
}
}
I'm using APB at the moment as I'm very familiar with AMBA protocols and it has the smallest code base under the diplomacy package. And I could make is so either of the AMBA or TL protocols are used later.
My Question
Is there a way to generate verilog just for MyDeviceController as a stand alone component?
I have not been able to figure this out if there is. Obviously if I just try to instantiate MyDeviceController I will get an error for the inward parameters of node not being connected. I'm not sure if you can give a "dummy" node connection? Or if there is some method that can handle that.
Why I want to do this
It is desirable to test the IP standalone in it's own test environment without a full SoC.
My Current Workaround/Solution
To work around this I essentially created a "wrapper" that creates an APBMasterNode and connects to the APBRegisterNode in MyDeviceController.
class APBMaster()(implicit p: Parameters) extends LazyModule {
val apbMasterParameters = APBMasterParameters(
name = "apbMaster"
)
val apbMasterPortParameters = APBMasterPortParameters(
masters = Seq(apbMasterParameters)
)
val node = APBMasterNode(
portParams = Seq(apbMasterPortParameters)
)
lazy val module = new LazyModuleImp(this) {
val io = IO(new Bundle {
val wtf = Output(Bool())
val start = Input(Bool())
})
val myreg = RegInit(0.U(16.W))
myreg := myreg + 1.U
val prdata = Wire(UInt(64.W))
prdata := node.out.head._1.prdata
//seems to need these things to generate the logic
io.wtf := node.out.head._1.pready && !(node.out.head._1.prdata === 0.U)
node.out.head._1.pstrb := 63.U
node.out.head._1.pprot := 0.U
when(myreg(3,0) === 8.U && io.start) {
node.out.head._1.paddr := myreg
node.out.head._1.psel := true.B
node.out.head._1.penable := false.B
node.out.head._1.pwrite := true.B
node.out.head._1.pwdata := myreg + 1.U
} .elsewhen(myreg(3,0) === 9.U) {
node.out.head._1.paddr := myreg
node.out.head._1.psel := true.B
node.out.head._1.penable := true.B
node.out.head._1.pwrite := true.B
node.out.head._1.pwdata := myreg
} otherwise {
node.out.head._1.paddr := 0.U
node.out.head._1.psel := false.B
node.out.head._1.penable := false.B
node.out.head._1.pwrite := false.B
node.out.head._1.pwdata := 0.U
}
}
}
One issue with this, was that I had to create some controls for each of the APB signals. If I did not, the Chisel/FIRRTL compiler/generator would not create any Verilog for MyDeviceController. This is what you see above with the myreg counter being used to do some basic APB transaction.
The wrapper would look like the following:
class APBTop()(implicit p: Parameters) extends LazyModule {
val master = LazyModule(new APBMaster)
val slave = LazyModule(new MyDeviceController()(Parameters.empty))
slave.node := master.node
lazy val module = new LazyModuleImp(this) {
val io = IO(new Bundle {
val busy = Output(Bool())
val wtf = Output(Bool())
val start = Input(Bool())
})
io.busy := true.B
io.wtf := master.module.io.wtf
master.module.io.start := io.start
}
}
I can create this wrapper/master as a typical testing component, then in my testenv just instatiate the MyDeviceController RTL, however I was wondering if there was another solution. It appears that diplomacy is fairly holistic (which I understand why), but was looking for suggestions on how IP level development is tackled for a Diplomatic infrastructure flow.
Thanks
Edit: Update March 2021
It's been a few months and I've spent more time with RocketChip/Chipyard/Diplomacy. This is a better solution, but leaving the old one below.
There is a makeIOs method for several Nodes. Using these we can actually punch out the respective AMBA/TL interface. This allows you to not have to use a wrapper that has nothing but connections.
Here is what it would look like compared to the previous version I suggested
class MyWrapper()(implicit p: Parameters) extends LazyModule {
//val master = LazyModule(new APBMaster)
val ApbPort = APBMasterNode(
portParams = Seq(APBMasterPortParameters(masters = Seq(APBMasterParameters(name = "ApbPort"))))
)
val apbport = InModuleBody {ApbPort.makeIOs()}
This reduces the need for the APBMaster dummy class as well.
Just to have an answer, I ended up using a combination of what myself and Jack Koenig went back and forth on.
If time permits I'll see if there is a way to make a "template" or LazyModule wrapper that does this for testing purposes (for each of the main protocols) and submit it to the Chisel repo.
class APBMaster()(implicit p: Parameters) extends LazyModule {
val apbMasterParameters = APBMasterParameters(
name = "apbMaster"
)
val apbMasterPortParameters = APBMasterPortParameters(
masters = Seq(apbMasterParameters)
)
val node = APBMasterNode(
portParams = Seq(apbMasterPortParameters)
)
lazy val module = new LazyModuleImp(this) {
//The dontTouch here preserves the interface so logic is generated
dontTouch(node.out.head._1)
}
}
class MyWrapper()(implicit p: Parameters) extends LazyModule {
val master = LazyModule(new APBMaster)
val slave = LazyModule(new MySlave()(Parameters.empty))
slave.node := master.node
lazy val module = new LazyModuleImp(this) {
//nothing???
}
}
object MyTestextends App {SSVPllFreqDetect)))
(new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => LazyModule(new MyWrapper()(Parameters.empty)).module)))
}

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
}

What does this Chisel exception mean: Caused by: chisel3.package$RebindingException: Attempted reassignment of binding to Reset

I got the error chisel3.package$RebindingException: Attempted reassignment of binding to Reset(IO in unelaborated TLDebugModule) when attempting to change the module (TLDebugModule) from a regular Module to a RawModule. I'm not sure what this message is saying, can you help understand what it's saying so I can look for what I am doing wrong?
Here's the diff of my RTL:
val io = IO(new Bundle {
val debug_clock = Input(Clock())
val debug_reset = Input(Reset())
+ val tl_clock = IO(Input(Clock()))
+ val tl_reset = IO(Input(Reset()))
+
val ctrl = new DebugCtrlBundle(nComponents)
val dmi = (!p(ExportDebug).apb).option(Flipped(new ClockedDMIIO()))
val apb_clock = p(ExportDebug).apb.option(Input(Clock()))
## -1780,11 +1789,11 ## class TLDebugModule(beatBytes: Int)(implicit p: Parameters) extends LazyModule {
dmOuter.module.rf_reset := r
}
- dmInner.module.clock := io.debug_clock
- dmInner.module.reset := io.debug_reset
dmInner.module.rf_reset := io.debug_reset
- dmInner.module.io.tlClock := clock
- dmInner.module.io.tlReset := reset
+ dmInner.module.io.debug_clock := io.debug_clock
+ dmInner.module.io.debug_reset := io.debug_reset
+ dmInner.module.io.tl_clock := io.tl_clock
+ dmInner.module.io.tl_reset := io.tl_reset
+++ b/src/main/scala/devices/debug/Periphery.scala
## -103,6 +103,8 ## trait HasPeripheryDebugModuleImp extends LazyModuleImp {
val psd = IO(new PSDIO)
val resetctrl = outer.debugOpt.map { outerdebug =>
+ outerdebug.module.io.tl_reset := reset
+ outerdebug.module.io.tl_clock := clock
val resetctrl = IO(new ResetCtrlIO(outerdebug.dmOuter.dmOuter.intnode.edges.out.size))
outerdebug.module.io.hartIsInReset := resetctrl.hartIsInReset
I think your problem is that you have nested calls to IO at
try removing the inner IO calls.

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?

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
}