Commit 992c37c2 authored by Jens Korinth's avatar Jens Korinth
Browse files

Implement generic, round-robin Axi Mux

* muxes N AXI-MM masters to one AXI-MM slave
* read and write channels are mux'ed independently
* no interruptions during bursts, next schedule on LAST
* address valid is used to signal transfer requests
* may cost up to N-1 cycles latency
parent 410607d7
......@@ -30,6 +30,8 @@ class FifoAxiAdapterTest1(dataWidth : Int, size: Int) extends Module {
}
object AxiModuleBuilder extends ModuleBuilder {
implicit val axi = AxiConfiguration(addrWidth = 32, dataWidth = 64, idWidth = 1)
val modules: List[(() => Module, CoreDefinition)] = List(
( // test module with fixed data
() => Module(new FifoAxiAdapterTest1(dataWidth = 32, 256)),
......@@ -83,6 +85,16 @@ object AxiModuleBuilder extends ModuleBuilder {
version = "0.1",
root = root("AxiSlidingWindow")
)
),
( // AXI Crossbar
() => Module(new AxiMux(8)),
CoreDefinition(
name = "AxiMux",
vendor = "esa.cs.tu-darmstadt.de",
library = "chisel",
version = "0.1",
root = root("AxiMux")
)
)
)
}
package chisel.axiutils
import AXIDefs._
import Chisel._
/**
* I/O Bundle for AXI mux.
* @param n Number of slave interfaces.
* @param axi Implicit AXI interface configuration.
**/
class AxiMuxIO(n: Int)(implicit axi: AxiConfiguration) extends Bundle {
val saxi = (Vec.fill(n) { new AXIMasterIF(axi.addrWidth, axi.dataWidth, axi.idWidth) }).flip
val maxi = new AXIMasterIF(axi.addrWidth, axi.dataWidth, axi.idWidth)
def renameSignals() = {
for (i <- 0 until n)
saxi(i).renameSignals(Some("S%02d_".format(i)), None)
maxi.renameSignals(None, None)
}
}
/**
* AxiMux: Connect n AXI-MM masters to one AXI-MM slave.
* @param n Number of slave interfaces.
* @param axi Implicit AXI interface configuration.
**/
class AxiMux(n: Int)(implicit axi: AxiConfiguration) extends Module {
val io = new AxiMuxIO(n)
io.renameSignals()
val waiting :: in_burst :: Nil = Enum(UInt(), 2)
val r_curr = Reg(UInt(width = log2Up(n)))
val w_curr = Reg(UInt(width = log2Up(n)))
val r_state = Reg(init = waiting)
val w_state = Reg(init = waiting)
def next_r() = r_curr := Mux(r_curr === UInt(n - 1), UInt(0), r_curr + UInt(1))
def next_w() = w_curr := Mux(w_curr === UInt(n - 1), UInt(0), w_curr + UInt(1))
/* tie-offs / wire defaults for connected slaves */
for (s <- io.saxi) {
/* READ ADDR */
s.readAddr.ready := Bool(false)
/* READ DATA */
s.readData.valid := Bool(false)
s.readData.bits.data := UInt(0)
s.readData.bits.id := UInt(0)
s.readData.bits.last := Bool(false)
s.readData.bits.resp := UInt(0)
/* WRITE ADDR */
s.writeAddr.ready := Bool(false)
/* WRITE DATA */
s.writeData.ready := Bool(false)
}
/* wiring for currently selected slaves */
/* READ ADDRESS */
io.saxi(r_curr).readAddr.ready := io.maxi.readAddr.ready
io.maxi.readAddr.valid := io.saxi(r_curr).readAddr.valid
io.maxi.readAddr.bits := io.saxi(r_curr).readAddr.bits
/* READ DATA */
io.maxi.readData.ready := io.saxi(r_curr).readData.ready
io.saxi(r_curr).readData.valid := io.maxi.readData.valid
io.saxi(r_curr).readData.bits := io.maxi.readData.bits
/* WRITE ADDRESS */
io.saxi(w_curr).writeAddr.ready := io.maxi.writeAddr.ready
io.maxi.writeAddr.valid := io.saxi(w_curr).writeAddr.valid
io.maxi.writeAddr.bits := io.saxi(w_curr).writeAddr.bits
/* WRITE DATA */
io.saxi(w_curr).writeData.ready := io.maxi.writeData.ready
io.maxi.writeData.valid := io.saxi(w_curr).writeData.valid
io.maxi.writeData.bits := io.saxi(w_curr).writeData.bits
when (reset) {
r_curr := UInt(0)
w_curr := UInt(0)
}
.otherwise {
when (r_state === waiting) {
when (io.saxi(r_curr).readAddr.valid) { r_state := in_burst }
.otherwise { next_r() }
}
.otherwise {
when (io.saxi(r_curr).readData.bits.last) {
next_r()
r_state := waiting
}
}
when (w_state === waiting) {
when (io.saxi(w_curr).writeAddr.valid) { w_state := in_burst }
.otherwise { next_w() }
}
.otherwise {
when (io.saxi(w_curr).writeData.bits.last) {
next_w()
w_state := waiting
}
}
}
}
package chisel.axiutils
import Chisel._
import org.scalatest.junit.JUnitSuite
import org.scalatest.Assertions._
import org.junit.Test
/**
* Read test module for AxiMux:
* Checks parallel reads from multiple AXI-MM masters.
* @param n Number of parallel masters.
* @param axi Implicit AXI interface configuration.
**/
class AxiMuxReadTestModule(val n: Int)(implicit axi: AxiConfiguration) extends Module {
val io = new Bundle
val mux = Module(new AxiMux(n))
private val asmcfg = AxiSlaveModelConfiguration(
addrWidth = Some(axi.addrWidth),
dataWidth = axi.dataWidth,
idWidth = axi.idWidth,
size = Some(n * 128)
)
val saxi = Module(new AxiSlaveModel(asmcfg))
private val afacfg = AxiFifoAdapterConfiguration(
axi = axi,
fifoDepth = 8,
burstSize = Some(4)
)
val afa = for (i <- 0 until n) yield Module(new AxiFifoAdapter(afacfg))
val bases = (0 until n) map (_ * 128 * (axi.dataWidth / 8))
mux.io.maxi <> saxi.io.saxi
((afa zip mux.io.saxi) zip bases) map { case ((a, s), b) => {
a.io.maxi <> s
a.io.base := UInt(b)
a.io.deq.ready := Bool(true)
}}
}
/**
* Unit test for reading across an AxiMux module:
* Connects multiple AxiFifoAdapters with increasing base addresses
* to single AxiSlaveModel and checks the data for correctness.
* No performance measurement!
* @param m Test module.
* @param isTrace if true, will enable tracing in Chisel Tester.
**/
class AxiMuxReadTester(m: AxiMuxReadTestModule, isTrace: Boolean = false) extends Tester(m, isTrace) {
implicit val tester = this
AxiSlaveModel.fillWithLinearSeq(m.saxi, m.saxi.cfg.dataWidth)
reset(10)
var counter: Array[Int] = Array.fill[Int](m.n)(0)
def finished: Boolean = counter map (_ >= 128) reduce (_&&_)
def handshake(i: Int) = peek(m.afa(i).io.deq.ready) != 0 && peek(m.afa(i).io.deq.valid) != 0
while (! finished) {
for (i <- 0 until m.n if handshake(i)) {
val ok = peek(m.afa(i).io.deq.bits) == counter(i) + i * 128
assert(ok)
if (ok) counter(i) += 1
}
step(1)
}
}
/**
* Unit test suit for AxiMux.
**/
class AxiMuxSuite extends JUnitSuite {
val chiselArgs = Array("--backend", "c", "--genHarness", "--compile", "--test", "--vcd")
implicit val axi = AxiConfiguration(addrWidth = 32, dataWidth = 64, idWidth = 1)
private def testMuxRead(n: Int) = {
val args = chiselArgs ++ Array("--targetDir", "test/AxiMuxSuite/%02d".format(n))
chiselMainTest(args, () => Module(new AxiMuxReadTestModule(n)))
{ m => new AxiMuxReadTester(m, true) }
}
@Test def testMuxRead1 { testMuxRead( 1) }
@Test def testMuxRead2 { testMuxRead( 2) }
@Test def testMuxRead3 { testMuxRead( 3) }
@Test def testMuxRead10 { testMuxRead(10) }
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment