Commit 4443aa0c authored by Jens Korinth's avatar Jens Korinth
Browse files

First draft of FifoAxiAdapter

* adapter to write data from Decoupled-Fifo as AXI4 master
* base address supplied as input
* all widths configurable
* FifoAxiAdapterTest1 uses DecoupledDataSource to test with
  constant data set
* can also be used to verify against AXI BFMs
parents
*.log
*.jou
.Xil
/target/
/project/
/test/
/ip/
[submodule "packaging"]
path = packaging
url = https://bitbucket.org/jkorinth/chisel-packaging.git
[submodule "miscutils"]
path = miscutils
url = https://bitbucket.org/jkorinth/chisel-miscutils.git
scalaVersion := "2.11.7"
libraryDependencies += "edu.berkeley.cs" %% "chisel" % "latest.release"
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.6" %"test"
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.5.4"
miscutils @ 5a1f9945
Subproject commit 5a1f99451060ccc762eb7f0ec88a9486f8fae6f6
packaging @ 4a10a2e7
Subproject commit 4a10a2e769b7185b8c2ce4da51fe68670b0cea0e
package AXIDefs
{
import Chisel._
import Literal._
import Node._
// Part I: Definitions for the actual data carried over AXI channels
// in part II we will provide definitions for the actual AXI interfaces
// by wrapping the part I types in Decoupled (ready/valid) bundles
// AXI channel data definitions
class AXIAddress(addrWidthBits: Int, idBits: Int) extends Bundle {
// address for the transaction, should be burst aligned if bursts are used
val addr = UInt(width = addrWidthBits)
// size of data beat in bytes
// set to UInt(log2Up((dataBits/8)-1)) for full-width bursts
val size = UInt(width = 3)
// number of data beats -1 in burst: max 255 for incrementing, 15 for wrapping
val len = UInt(width = 8)
// burst mode: 0 for fixed, 1 for incrementing, 2 for wrapping
val burst = UInt(width = 2)
// transaction ID for multiple outstanding requests
val id = UInt(width = idBits)
// set to 1 for exclusive access
val lock = Bool()
// cachability, set to 0010 or 0011
val cache = UInt(width = 4)
// generally ignored, set to to all zeroes
val prot = UInt(width = 3)
// not implemented, set to zeroes
val qos = UInt(width = 4)
override def clone = { new AXIAddress(addrWidthBits, idBits).asInstanceOf[this.type] }
}
class AXIWriteData(dataWidthBits: Int) extends Bundle {
val data = UInt(width = dataWidthBits)
val strb = UInt(width = dataWidthBits/8)
val last = Bool()
override def clone = { new AXIWriteData(dataWidthBits).asInstanceOf[this.type] }
}
class AXIWriteResponse(idBits: Int) extends Bundle {
val id = UInt(width = idBits)
val resp = UInt(width = 2)
override def clone = { new AXIWriteResponse(idBits).asInstanceOf[this.type] }
}
class AXIReadData(dataWidthBits: Int, idBits: Int) extends Bundle {
val data = UInt(width = dataWidthBits)
val id = UInt(width = idBits)
val last = Bool()
val resp = UInt(width = 2)
override def clone = { new AXIReadData(dataWidthBits, idBits).asInstanceOf[this.type] }
}
// Part II: Definitions for the actual AXI interfaces
// TODO add full slave interface definition
class AXIMasterIF(addrWidthBits: Int, dataWidthBits: Int, idBits: Int) extends Bundle {
// write address channel
val writeAddr = Decoupled(new AXIAddress(addrWidthBits, idBits))
// write data channel
val writeData = Decoupled(new AXIWriteData(dataWidthBits))
// write response channel (for memory consistency)
val writeResp = Decoupled(new AXIWriteResponse(idBits)).flip
// read address channel
val readAddr = Decoupled(new AXIAddress(addrWidthBits, idBits))
// read data channel
val readData = Decoupled(new AXIReadData(dataWidthBits, idBits)).flip
// rename signals to be compatible with those in the Xilinx template
def renameSignals() {
// write address channel
writeAddr.bits.addr.setName("M_AXI_AWADDR")
writeAddr.bits.prot.setName("M_AXI_AWPROT")
writeAddr.bits.size.setName("M_AXI_AWSIZE")
writeAddr.bits.len.setName("M_AXI_AWLEN")
writeAddr.bits.burst.setName("M_AXI_AWBURST")
writeAddr.bits.lock.setName("M_AXI_AWLOCK")
writeAddr.bits.cache.setName("M_AXI_AWCACHE")
writeAddr.bits.qos.setName("M_AXI_AWQOS")
writeAddr.bits.id.setName("M_AXI_AWID")
writeAddr.valid.setName("M_AXI_AWVALID")
writeAddr.ready.setName("M_AXI_AWREADY")
// write data channel
writeData.bits.data.setName("M_AXI_WDATA")
writeData.bits.strb.setName("M_AXI_WSTRB")
writeData.bits.last.setName("M_AXI_WLAST")
writeData.valid.setName("M_AXI_WVALID")
writeData.ready.setName("M_AXI_WREADY")
// write response channel
writeResp.bits.resp.setName("M_AXI_BRESP")
writeResp.bits.id.setName("M_AXI_BID")
writeResp.valid.setName("M_AXI_BVALID")
writeResp.ready.setName("M_AXI_BREADY")
// read address channel
readAddr.bits.addr.setName("M_AXI_ARADDR")
readAddr.bits.prot.setName("M_AXI_ARPROT")
readAddr.bits.size.setName("M_AXI_ARSIZE")
readAddr.bits.len.setName("M_AXI_ARLEN")
readAddr.bits.burst.setName("M_AXI_ARBURST")
readAddr.bits.lock.setName("M_AXI_ARLOCK")
readAddr.bits.cache.setName("M_AXI_ARCACHE")
readAddr.bits.qos.setName("M_AXI_ARQOS")
readAddr.bits.id.setName("M_AXI_ARID")
readAddr.valid.setName("M_AXI_ARVALID")
readAddr.ready.setName("M_AXI_ARREADY")
// read data channel
readData.bits.id.setName("M_AXI_RID")
readData.bits.data.setName("M_AXI_RDATA")
readData.bits.resp.setName("M_AXI_RRESP")
readData.bits.last.setName("M_AXI_RLAST")
readData.valid.setName("M_AXI_RVALID")
readData.ready.setName("M_AXI_RREADY")
}
override def clone = { new AXIMasterIF(addrWidthBits, dataWidthBits, idBits).asInstanceOf[this.type] }
}
}
../../../packaging/CoreDefinition.scala
\ No newline at end of file
../../../miscutils/src/main/scala/DecoupledDataSource.scala
\ No newline at end of file
import Chisel._
import AXIDefs._
class FifoAxiAdapterIO(addrWidth: Int, dataWidth: Int, idWidth: Int) extends Bundle {
val maxi = new AXIMasterIF(addrWidth, dataWidth, idWidth)
val inq = Decoupled(UInt(width = dataWidth)).flip()
val base = UInt(INPUT, width = addrWidth)
}
class FifoAxiAdapter(addrWidth: Int,
dataWidth: Int,
idWidth: Int,
size: Int,
stride: Int = 1) extends Module {
require (log2Up(size) <= addrWidth, "addrWidth (%d) must be large enough to address all %d element, at least %d bits".format(addrWidth, size, log2Up(size)))
val io = new FifoAxiAdapterIO(addrWidth, dataWidth, idWidth)
val wdata_valid = /*RegNext(*/io.inq.valid/*)*/
val offs = Reg(UInt(width = log2Up(size)))
val addr = RegNext(io.base) + offs
val data = io.inq.bits
val wa_ready = RegNext(io.maxi.writeAddr.ready)
val wd_ready = RegNext(io.maxi.writeData.ready)
val wr_valid = RegNext(io.maxi.writeResp.valid)
val addr_hs = Reg(Bool())
val data_hs = Reg(Bool())
val resp_hs = Reg(Bool())
io.maxi.writeAddr.valid := !reset && wdata_valid && !addr_hs
io.maxi.writeAddr.bits.addr := addr
io.maxi.writeAddr.bits.size := UInt(2) // one word (4 byte)
io.maxi.writeAddr.bits.len := UInt(0) // single word len
io.maxi.writeAddr.bits.burst := UInt(0) // no burst
io.maxi.writeAddr.bits.id := UInt(0) // id=0
io.maxi.writeAddr.bits.lock := UInt(0) // no lock
io.maxi.writeAddr.bits.cache := UInt(2) // no cache, modifiable
io.maxi.writeAddr.bits.prot := UInt(0) // no prot
io.maxi.writeAddr.bits.qos := UInt(0) // no qos
io.maxi.writeData.bits.data := data
io.maxi.writeData.bits.last := Bool(true)
io.maxi.writeData.bits.strb := UInt("b1111")
io.maxi.writeData.valid := !reset && wdata_valid && !data_hs
io.maxi.writeResp.ready := Bool(true)
io.maxi.readData.ready := Bool(false)
io.maxi.readAddr.valid := Bool(false)
io.maxi.readAddr.bits.addr := UInt(2) // one word (4 byte)
io.maxi.readAddr.bits.size := UInt(0) // one word
io.maxi.readAddr.bits.len := UInt(0) // single word len
io.maxi.readAddr.bits.burst := UInt(0) // no burst
io.maxi.readAddr.bits.id := UInt(0) // id=0
io.maxi.readAddr.bits.lock := UInt(0) // no lock
io.maxi.readAddr.bits.cache := UInt(2) // no cache, modifiable
io.maxi.readAddr.bits.prot := UInt(0) // no prot
io.maxi.readAddr.bits.qos := UInt(0) // no qos
io.inq.ready := addr_hs && data_hs && resp_hs
when (reset) {
offs := UInt(0)
addr_hs := Bool(false)
data_hs := Bool(false)
resp_hs := Bool(false)
}
.otherwise {
when (wdata_valid && wa_ready) { addr_hs := Bool(true) }
when (wdata_valid && wd_ready) { data_hs := Bool(true) }
when (wr_valid) { resp_hs := Bool(true) }
when (addr_hs && data_hs && resp_hs) {
offs := offs + UInt(dataWidth / 8 * stride) // stride to bytes
addr_hs := Bool(false)
data_hs := Bool(false)
resp_hs := Bool(false)
}
}
}
import scala.sys.process._
import java.nio.file.Paths
import Chisel._
import AXIDefs._
class FifoAxiAdapterTest1(dataWidth : Int, size: Int) extends Module {
val addrWidth = 32
val io = new Bundle {
val maxi = new AXIMasterIF(addrWidth, dataWidth, 1)
val base = UInt(INPUT, width = addrWidth)
}
val datasrc = Module (new DecoupledDataSource(UInt(width = dataWidth),
size = 256, n => UInt(n), false))
val fad = Module (new FifoAxiAdapter(addrWidth = addrWidth,
dataWidth = dataWidth, idWidth = 1, size = size))
io.maxi.renameSignals()
io.base.setName("base")
fad.io.base := io.base
fad.io.inq <> datasrc.io.out
fad.io.maxi <> io.maxi
}
object ModuleBuilder {
val chiselArgs = Array("--backend", "v", "--compile")
val modules: List[(() => Module, CoreDefinition)] = List(
( // test module with fixed data
() => Module(new FifoAxiAdapterTest1(dataWidth = 32, 256)),
CoreDefinition(
name = "FifoAxiAdapterTest1",
vendor = "esa.cs.tu-darmstadt.de",
library = "chisel",
version = "0.1",
root = Paths.get(".").toAbsolutePath.resolve("ip").resolve("FifoAxiAdapterTest1").toString
)
),
( // generic adapter module
() => Module(new FifoAxiAdapter(addrWidth = 32,
dataWidth = 64,
idWidth = 1,
size = scala.math.pow(2, 24).toInt)),
CoreDefinition(
name = "FifoAxiAdapter",
vendor = "esa.cs.tu-darmstadt.de",
library = "chisel",
version = "0.1",
root = Paths.get(".").toAbsolutePath.resolve("ip").resolve("FifoAxiAdapter").toString
)
)
)
def main(args: Array[String]) {
modules foreach { m =>
chiselMain(chiselArgs ++ Array("--targetDir", m._2.root), m._1)
val json = "%s/%s.json".format(m._2.root, m._2.name)
m._2.write(json)
"packaging/package.py %s".format(json) !
}
}
}
import Chisel._
import AXIDefs._
class AxiSlaveModelIO(addrWidth: Int, dataWidth: Int, idWidth: Int) extends Bundle {
val saxi = new AXIMasterIF(addrWidth, dataWidth, idWidth).flip()
val mem = new Bundle {
val d = UInt(OUTPUT, width = dataWidth)
val addr = UInt(INPUT, width = addrWidth)
}
}
class AxiSlaveModel(addrWidth: Int, dataWidth: Int, idWidth: Int) extends Module {
val io = new AxiSlaveModelIO(addrWidth = addrWidth, dataWidth = dataWidth, idWidth = idWidth)
val mem = Mem(scala.math.pow(2, addrWidth).toInt, UInt(width = dataWidth))
val wa_valid = RegNext(io.saxi.writeAddr.valid)
val wd_valid = RegNext(io.saxi.writeData.valid)
val wr_ready = RegNext(io.saxi.writeResp.ready)
val wa_addr = RegNext(io.saxi.writeAddr.bits.addr)
val wd_data = RegNext(io.saxi.writeData.bits.data)
val wa = Reg(UInt(width = addrWidth))
val wd = Reg(UInt(width = dataWidth))
val wr = Reg(UInt(width = 2))
val wa_hs = Reg(Bool()) // address handshake complete?
val wd_hs = Reg(Bool()) // data handshake complete?
val wr_hs = Reg(Bool()) // response handshake complete?
val wr_valid = Reg(Bool()) // response valid
val raddr = RegNext(io.mem.addr)
io.saxi.writeAddr.ready := !wa_hs
io.saxi.writeData.ready := !wd_hs
io.saxi.writeResp.bits.resp := wr
io.saxi.writeResp.bits.id := UInt(0)
io.saxi.writeResp.valid := wr_valid
io.mem.d := mem(raddr)
when (reset) {
wa := UInt(0)
wd := UInt(0)
wr := UInt(0) // OK
wr_valid := Bool(false)
wa_hs := Bool(false)
wd_hs := Bool(false)
wr_hs := Bool(false)
raddr := UInt(0)
}
.otherwise {
when (wa_valid) {
wa := wa_addr
wa_hs := Bool(true)
}
when (wd_valid) {
wd := wd_data
wd_hs := Bool(true)
}
when (wa_hs && wd_hs) {
mem(wa) := wd
wr_valid := Bool(true)
}
when (wa_hs && wd_hs && wr_valid && wr_ready) {
wa_hs := Bool(false)
wd_hs := Bool(false)
wr_valid := Bool(false)
}
}
}
import Chisel._
import org.scalatest.junit.JUnitSuite
import org.junit.Test
import org.junit.Assert._
class FifoAxiAdapterModule1(dataWidth : Int, size: Int) extends Module {
val io = new Bundle
val datasrc = Module (new DecoupledDataSource(UInt(width = dataWidth), size = 256, n => UInt(n), false))
val fad = Module (new FifoAxiAdapter(addrWidth = log2Up(size),
dataWidth = dataWidth, idWidth = 1, size = size))
val saxi = Module (new AxiSlaveModel(addrWidth = log2Up(size),
dataWidth = dataWidth, idWidth = 1))
fad.io.base := UInt(0)
fad.io.inq <> datasrc.io.out
fad.io.maxi <> saxi.io.saxi
}
class FifoAxiAdapterSuite extends JUnitSuite {
@Test def test1 {
chiselMainTest(Array("--genHarness", "--backend", "c", "--vcd", "--targetDir", "test", "--compile", "--test"),
() => Module(new FifoAxiAdapterModule1(dataWidth = 8, size = 256))) { m => new FifoAxiAdapterModule1Test(m) }
}
}
class FifoAxiAdapterModule1Test(fad: FifoAxiAdapterModule1) extends Tester(fad, false) {
import scala.util.Properties.{lineSeparator => NL}
private var cc = 0
// re-define step to output progress info
override def step(n: Int) {
super.step(n)
cc += n
if (cc % 1000 == 0) println("clock cycle: " + cc)
}
reset(10)
while (peek(fad.datasrc.io.out.valid) != 0) step(1)
step(10) // settle
// check
var errors: List[String] = List()
for (i <- 0 until 256) {
if (peekAt(fad.saxi.mem, i) != i)
errors = "Mem[%03d] = %d (expected: %d)".format(i, peekAt(fad.saxi.mem, i), i) :: errors
}
assertTrue (("mem does not match, errors: " :: errors).mkString(NL), errors.length == 0)
}
../../../miscutils/src/main/scala
\ No newline at end of file
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