OS: Linux myarch 6.7.0-arch3-1 #1 SMP PREEMPT_DYNAMIC Sat, 13 Jan 2024 14:37:14 +0000 x86_64 GNU/Linux
I am attempting to implement a multi-input and multi-output xbar for AXI. My idea for the xbar is to collect the output of each master/slave, then use a MUX to select the current master/slave output to connect to the xbar for forwarding. For each master/slave input, a MUX is used to select whether it is the forwarding signal of the xbar or the default signal of 0. In this way, when the xbar is idle, both the master and slave think that the other is busy, and then they will block their information until the MUX is turned on.
In my implementation, there are two n-to-1 multiplexers. One can be compiled correctly, but the other one throws an error.
left := MuxLookup(m_sel, leftDefault) (
(0 until nr_m).map(i => ((1.U << i), masterOut(i)))) // A compilation error will occur.
right := MuxLookup(s_sel, rightDefault) (
(0 until nr_s).map(i => ((1.U << i), slaveOut(i))))
Here is my complete implementation:
import chisel3._
import chisel3.util._
import scala.reflect.runtime.universe._
class AXIIN extends Bundle {
val araddr = Input(UInt(32.W))
val arvalid = Input(Bool())
val rready = Input(Bool())
val awaddr = Input(UInt(32.W))
val awvalid = Input(Bool())
val wdata = Input(UInt(32.W))
val wstrb = Input(UInt(8.W))
val wvalid = Input(Bool())
val bready = Input(Bool())
}
class AXIOUT extends Bundle {
val arready = Output(Bool())
val rdata = Output(UInt(32.W))
val rrsp = Output(UInt(2.W))
val rvalid = Output(Bool())
val awready = Output(Bool())
val wready = Output(Bool())
val bresp = Output(UInt(2.W))
val bvalid = Output(Bool())
}
class XbarIO(nr_m: Int, nr_s: Int) extends Bundle {
val master = Vec(nr_m, new AXI)
val slave = Vec(nr_s, Flipped(new AXI))
}
class Xbar_tmp(nr_m: Int = 2, nr_s: Int = 1) extends Module {
val io = IO(new XbarIO(nr_m, nr_s))
val m_sel = RegInit(0.U(nr_m.W)) // One-hot code
val addr = RegInit(0.U(32.W))
val masterOut = Wire(Vec(nr_m, new AXIIN))
val slaveOut = Wire(Vec(nr_s, new AXIOUT))
val left = Wire(new AXIIN)
val right = Wire(new AXIOUT)
val leftDefault = Wire(new AXIIN)
for (enty <- leftDefault.getElements) {
enty := 0.U
}
val rightDefault = Wire(new AXIOUT)
for (enty <- rightDefault.getElements) {
enty := 0.U
}
// sfm
val s_idle :: s_arbiter :: s_link :: s_wait_ok :: Nil = Enum(4)
val state = RegInit(s_idle)
state := MuxLookup(state, s_idle) (
List(
s_idle -> Mux(io.master.map(m => m.arvalid || m.awvalid).reduce(_ || _), s_arbiter, s_idle), // If there is a handshake signal from the master, enter the arbitration stage
s_arbiter -> s_link, // Assign values to m_sel and addr, then addr will get s_sel through the decoder API
s_link -> Mux(right.arready, s_link, s_wait_ok), // If the slave is not idle, enter the waiting for completion
s_wait_ok -> Mux(right.arready, s_idle, s_wait_ok) // If the slave enters idle, it means completion, and return to idle state
)
)
m_sel := MuxLookup(state, m_sel) (
List(
s_idle -> 0.U,
s_arbiter -> PriorityMux(io.master.map(m => m.arvalid || m.awvalid), (0 until nr_m).map(i => 1.U << i))
)
)
addr := MuxLookup(state, addr) (
List(
s_idle -> 0.U,
s_arbiter -> PriorityMux(io.master.map(m => m.arvalid || m.awvalid), io.master.map(m => Mux(m.arvalid, m.rdata, m.wdata)))
)
)
val slaveSelector = Module(new SlaveSelector) // 通过 decoder api 获取 s_sel
slaveSelector.io.addr := addr
val s_sel = slaveSelector.io.s_sel
left := MuxLookup(m_sel, leftDefault) (
(0 until nr_m).map(i => ((1.U << i), masterOut(i)))) // This statement will fail
// left := Mux(state === s_idle, leftDefault, masterOut(0)) // This statement will also fail with the same error.
// left := masterOut(1) // This statement will compile successfully
// left := MuxLookup(m_sel, leftDefault) ( // This statement will also fail with the same error.
// List(
// (1.U << 0) -> masterOut(0),
// (1.U << 1) -> masterOut(1)
// )
// )
right := MuxLookup(s_sel, rightDefault) (
(0 until nr_s).map(i => ((1.U << i), slaveOut(i))))
// right := slaveOut(1)
for (i <- 0 until nr_m) {
for ((name, element) <- io.master(i).elements) {
if (typeOf[AXIOUT].members.exists(_.name.toString == name)) { // master's input
element := Mux(m_sel === (1.U << i), right.elements(name), 0.U)
}else {
masterOut(i).elements(name) := element
}
}
}
for (i <- 0 until nr_s) {
for ((name, element) <- io.slave(i).elements) {
if (typeOf[AXIIN].members.exists(_.name.toString == name)) { // slave's input
element := Mux(s_sel === (1.U << i), left.elements(name), 0.U)
}else {
slaveOut(i).elements(name) := element
}
}
}
}
What is the current behavior?
However, when compiling this code, the part that selects the current master output will throw an error.
The error message is as follows:
➜ npc git:(pa3) ✗ make sim
mkdir -p ./build
mill -i __.test.runMain Main -td ./build
No mill version specified.
You should provide a version via '.mill-version' file or --mill-version option.
Using mill version 0.11.6
[81/81] playground.test.runMain
Exception in thread "main" circt.stage.phases.Exceptions$FirtoolNonZeroExitCode: /home/amadeus/.cache/llvm-firtool/1.62.0/bin/firtool returned a non-zero exit code. Note that this version of Chisel (6.0.0) was published against firtool version 1.62.0.
------------------------------------------------------------------------------
ExitCode:
1
STDOUT:
STDERR:
<stdin>:328:5: error: Node cannot be analog and must be passive or passive under a flip '!firrtl.bundle<araddr flip: uint<32>, arvalid flip: uint<1>, rready flip: uint<1>, awaddr flip: uint<32>, awvalid flip: uint<1>, wdata flip: uint<32>, wstrb flip: uint<8>, wvalid flip: uint<1>, bready flip: uint<1>>'
node _left_T_3 = mux(_left_T_2, masterOut[0], leftDefault) @[playground/src/device/AXI/Xbar_tmp.scala 90:41]
^
------------------------------------------------------------------------------
1 targets failed
playground.test.runMain subprocess failed
make: *** [Makefile:11: verilog] Error 1
The strange thing about this problem is that the code related to the master side is symmetrically the same as the code related to the slave side. But if we comment out the code that selects the current master output and change it to default to select the second master, and keep the code on the slave side unchanged, the compilation can surprisingly pass.
// left := MuxLookup(m_sel, leftDefault) (
// (0 until nr_m).map(i => ((1.U << i), masterOut(i))))
// left := Mux(state === s_idle, leftDefault, masterOut(0)) // This statement will also fail with the same error.
// Modify the code that generates left
left := masterOut(1) // This statement will compile successfully
// left := MuxLookup(m_sel, leftDefault) (
// List(
// (1.U << 0) -> masterOut(0),
// (1.U << 1) -> masterOut(1)
// )
// )
// Keep unchanged
right := MuxLookup(s_sel, rightDefault) (
(0 until nr_s).map(i => ((1.U << i), slaveOut(i))))
I would greatly appreciate any clues on how to solve this problem.
Type of issue: Bug Report
Please tell us about your environment:
Linux myarch 6.7.0-arch3-1 #1 SMP PREEMPT_DYNAMIC Sat, 13 Jan 2024 14:37:14 +0000 x86_64 GNU/Linux
I am attempting to implement a multi-input and multi-output xbar for AXI. My idea for the xbar is to collect the output of each master/slave, then use a MUX to select the current master/slave output to connect to the xbar for forwarding. For each master/slave input, a MUX is used to select whether it is the forwarding signal of the xbar or the default signal of 0. In this way, when the xbar is idle, both the master and slave think that the other is busy, and then they will block their information until the MUX is turned on.
In my implementation, there are two n-to-1 multiplexers. One can be compiled correctly, but the other one throws an error.
Here is my complete implementation:
What is the current behavior? However, when compiling this code, the part that selects the current master output will throw an error. The error message is as follows:
The strange thing about this problem is that the code related to the master side is symmetrically the same as the code related to the slave side. But if we comment out the code that selects the current master output and change it to default to select the second master, and keep the code on the slave side unchanged, the compilation can surprisingly pass.
I would greatly appreciate any clues on how to solve this problem.