2016-08-16 07:03:03 +02:00
|
|
|
// See LICENSE for license details.
|
|
|
|
|
|
|
|
package rocketchip
|
|
|
|
|
|
|
|
import Chisel._
|
|
|
|
import cde.{Parameters, Field}
|
|
|
|
import rocket.Util._
|
|
|
|
import junctions._
|
2016-09-13 19:47:36 +02:00
|
|
|
import junctions.NastiConstants._
|
2016-08-16 07:03:03 +02:00
|
|
|
|
2016-09-11 08:39:29 +02:00
|
|
|
case object BuildExampleTop extends Field[Parameters => ExampleTop]
|
2016-09-10 00:12:05 +02:00
|
|
|
case object SimMemLatency extends Field[Int]
|
2016-09-11 08:39:29 +02:00
|
|
|
|
2016-09-15 09:38:46 +02:00
|
|
|
class TestHarness(q: Parameters) extends Module {
|
2016-08-16 07:03:03 +02:00
|
|
|
val io = new Bundle {
|
|
|
|
val success = Bool(OUTPUT)
|
|
|
|
}
|
2016-09-15 09:38:46 +02:00
|
|
|
val dut = q(BuildExampleTop)(q).module
|
|
|
|
implicit val p = dut.p
|
2016-08-16 07:03:03 +02:00
|
|
|
|
|
|
|
// This test harness isn't especially flexible yet
|
|
|
|
require(dut.io.mem_clk.isEmpty)
|
|
|
|
require(dut.io.mem_rst.isEmpty)
|
|
|
|
require(dut.io.mem_ahb.isEmpty)
|
|
|
|
require(dut.io.mem_tl.isEmpty)
|
|
|
|
require(dut.io.bus_clk.isEmpty)
|
|
|
|
require(dut.io.bus_rst.isEmpty)
|
|
|
|
require(dut.io.mmio_clk.isEmpty)
|
|
|
|
require(dut.io.mmio_rst.isEmpty)
|
|
|
|
require(dut.io.mmio_ahb.isEmpty)
|
|
|
|
require(dut.io.mmio_tl.isEmpty)
|
|
|
|
|
|
|
|
for (int <- dut.io.interrupts)
|
|
|
|
int := false
|
|
|
|
|
|
|
|
if (dut.io.mem_axi.nonEmpty) {
|
2016-09-15 09:38:46 +02:00
|
|
|
val memSize = p(GlobalAddrMap)("mem").size
|
2016-08-16 07:03:03 +02:00
|
|
|
require(memSize % dut.io.mem_axi.size == 0)
|
2016-09-10 00:12:05 +02:00
|
|
|
for (axi <- dut.io.mem_axi) {
|
|
|
|
val mem = Module(new SimAXIMem(memSize / dut.io.mem_axi.size))
|
|
|
|
mem.io.axi.ar <> axi.ar
|
|
|
|
mem.io.axi.aw <> axi.aw
|
|
|
|
mem.io.axi.w <> axi.w
|
|
|
|
axi.r <> LatencyPipe(mem.io.axi.r, p(SimMemLatency))
|
|
|
|
axi.b <> LatencyPipe(mem.io.axi.b, p(SimMemLatency))
|
|
|
|
}
|
2016-08-16 07:03:03 +02:00
|
|
|
}
|
|
|
|
|
2016-09-02 03:38:39 +02:00
|
|
|
if (!p(IncludeJtagDTM)) {
|
2016-08-19 23:01:33 +02:00
|
|
|
// Todo: enable the usage of different clocks
|
|
|
|
// to test the synchronizer more aggressively.
|
2016-08-19 18:46:43 +02:00
|
|
|
val dtm_clock = clock
|
|
|
|
val dtm_reset = reset
|
2016-09-02 03:38:39 +02:00
|
|
|
if (dut.io.debug_clk.isDefined) dut.io.debug_clk.get := dtm_clock
|
|
|
|
if (dut.io.debug_rst.isDefined) dut.io.debug_rst.get := dtm_reset
|
|
|
|
val dtm = Module(new SimDTM).connect(dtm_clock, dtm_reset, dut.io.debug.get,
|
|
|
|
dut.io.success, io.success)
|
|
|
|
} else {
|
|
|
|
val jtag = Module(new JTAGVPI).connect(dut.io.jtag.get, reset, io.success)
|
2016-08-16 07:03:03 +02:00
|
|
|
}
|
2016-08-23 16:34:01 +02:00
|
|
|
|
2016-08-20 03:57:34 +02:00
|
|
|
for (bus_axi <- dut.io.bus_axi) {
|
|
|
|
bus_axi.ar.valid := Bool(false)
|
|
|
|
bus_axi.aw.valid := Bool(false)
|
|
|
|
bus_axi.w.valid := Bool(false)
|
|
|
|
bus_axi.r.ready := Bool(false)
|
|
|
|
bus_axi.b.ready := Bool(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
for (mmio_axi <- dut.io.mmio_axi) {
|
|
|
|
val slave = Module(new NastiErrorSlave)
|
|
|
|
slave.io <> mmio_axi
|
|
|
|
}
|
|
|
|
|
2016-08-16 07:03:03 +02:00
|
|
|
}
|
|
|
|
|
2016-09-10 00:12:05 +02:00
|
|
|
class SimAXIMem(size: BigInt)(implicit p: Parameters) extends NastiModule()(p) {
|
2016-08-16 07:03:03 +02:00
|
|
|
val io = new Bundle {
|
|
|
|
val axi = new NastiIO().flip
|
|
|
|
}
|
|
|
|
|
|
|
|
val rValid = Reg(init = Bool(false))
|
|
|
|
val ar = RegEnable(io.axi.ar.bits, io.axi.ar.fire())
|
|
|
|
io.axi.ar.ready := !rValid
|
|
|
|
when (io.axi.ar.fire()) { rValid := true }
|
|
|
|
when (io.axi.r.fire()) {
|
|
|
|
assert(ar.burst === NastiConstants.BURST_INCR)
|
|
|
|
ar.addr := ar.addr + (UInt(1) << ar.size)
|
|
|
|
ar.len := ar.len - 1
|
|
|
|
when (ar.len === UInt(0)) { rValid := false }
|
|
|
|
}
|
|
|
|
|
|
|
|
val w = io.axi.w.bits
|
2016-09-10 00:12:05 +02:00
|
|
|
require((size * 8) % nastiXDataBits == 0)
|
|
|
|
val depth = (size * 8) / nastiXDataBits
|
2016-08-16 07:03:03 +02:00
|
|
|
val mem = Mem(depth.toInt, w.data)
|
|
|
|
|
|
|
|
val wValid = Reg(init = Bool(false))
|
|
|
|
val bValid = Reg(init = Bool(false))
|
|
|
|
val aw = RegEnable(io.axi.aw.bits, io.axi.aw.fire())
|
|
|
|
io.axi.aw.ready := !wValid && !bValid
|
|
|
|
io.axi.w.ready := wValid
|
|
|
|
when (io.axi.b.fire()) { bValid := false }
|
|
|
|
when (io.axi.aw.fire()) { wValid := true }
|
|
|
|
when (io.axi.w.fire()) {
|
|
|
|
assert(aw.burst === NastiConstants.BURST_INCR)
|
|
|
|
aw.addr := aw.addr + (UInt(1) << aw.size)
|
|
|
|
aw.len := aw.len - 1
|
|
|
|
when (aw.len === UInt(0)) {
|
|
|
|
wValid := false
|
|
|
|
bValid := true
|
|
|
|
}
|
|
|
|
|
2016-09-10 00:12:05 +02:00
|
|
|
def row = mem((aw.addr >> log2Ceil(nastiXDataBits/8))(log2Ceil(depth)-1, 0))
|
2016-08-16 07:03:03 +02:00
|
|
|
val mask = FillInterleaved(8, w.strb)
|
|
|
|
val newData = mask & w.data | ~mask & row
|
|
|
|
row := newData
|
|
|
|
}
|
|
|
|
|
|
|
|
io.axi.b.valid := bValid
|
|
|
|
io.axi.b.bits.id := aw.id
|
2016-09-13 19:47:36 +02:00
|
|
|
io.axi.b.bits.resp := RESP_OKAY
|
2016-08-16 07:03:03 +02:00
|
|
|
|
|
|
|
io.axi.r.valid := rValid
|
|
|
|
io.axi.r.bits.id := ar.id
|
2016-09-10 00:12:05 +02:00
|
|
|
io.axi.r.bits.data := mem((ar.addr >> log2Ceil(nastiXDataBits/8))(log2Ceil(depth)-1, 0))
|
2016-09-13 19:47:36 +02:00
|
|
|
io.axi.r.bits.resp := RESP_OKAY
|
2016-08-16 07:03:03 +02:00
|
|
|
io.axi.r.bits.last := ar.len === UInt(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
class SimDTM(implicit p: Parameters) extends BlackBox {
|
|
|
|
val io = new Bundle {
|
|
|
|
val clk = Clock(INPUT)
|
|
|
|
val reset = Bool(INPUT)
|
|
|
|
val debug = new uncore.devices.DebugBusIO
|
|
|
|
val exit = UInt(OUTPUT, 32)
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2016-09-02 03:38:39 +02:00
|
|
|
def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.DebugBusIO,
|
|
|
|
dutsuccess: Option[Bool], tbsuccess: Bool) = {
|
|
|
|
io.clk := tbclk
|
|
|
|
io.reset := tbreset
|
|
|
|
dutio <> io.debug
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2016-09-02 03:38:39 +02:00
|
|
|
tbsuccess := dutsuccess.getOrElse(io.exit === 1)
|
|
|
|
when (io.exit >= 2) {
|
|
|
|
printf("*** FAILED *** (exit code = %d)\n", io.exit >> 1)
|
|
|
|
stop(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2016-09-02 03:38:39 +02:00
|
|
|
class JTAGVPI(implicit val p: Parameters) extends BlackBox {
|
2016-08-19 18:46:43 +02:00
|
|
|
val io = new Bundle {
|
2016-09-02 03:38:39 +02:00
|
|
|
val jtag = new JTAGIO(false)
|
2016-08-19 18:46:43 +02:00
|
|
|
val enable = Bool(INPUT)
|
|
|
|
val init_done = Bool(INPUT)
|
|
|
|
}
|
2016-09-02 03:38:39 +02:00
|
|
|
|
|
|
|
def connect(dutio: JTAGIO, tbreset: Bool, tbsuccess: Bool) = {
|
|
|
|
dutio <> io.jtag
|
|
|
|
|
|
|
|
// To be proper,
|
|
|
|
// TRST should really be synchronized
|
|
|
|
// with TCK. But this is a fairly
|
|
|
|
// accurate representation of how
|
|
|
|
// HW may drive this signal.
|
|
|
|
// Neither OpenOCD nor JtagVPI drive TRST.
|
|
|
|
|
|
|
|
dutio.TRST := tbreset
|
|
|
|
io.enable := ~tbreset
|
|
|
|
io.init_done := ~tbreset
|
|
|
|
|
|
|
|
// Success is determined by the gdbserver
|
|
|
|
// which is controlling this simulation.
|
|
|
|
tbsuccess := Bool(false)
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
}
|
2016-09-10 00:12:05 +02:00
|
|
|
|
2016-09-14 00:34:42 +02:00
|
|
|
class LatencyPipe[T <: Data](typ: T, latency: Int) extends Module {
|
|
|
|
val io = new Bundle {
|
|
|
|
val in = Decoupled(typ).flip
|
|
|
|
val out = Decoupled(typ)
|
|
|
|
}
|
|
|
|
|
2016-09-10 00:12:05 +02:00
|
|
|
def doN[T](n: Int, func: T => T, in: T): T =
|
|
|
|
(0 until n).foldLeft(in)((last, _) => func(last))
|
|
|
|
|
2016-09-14 00:34:42 +02:00
|
|
|
io.out <> doN(latency, (last: DecoupledIO[T]) => Queue(last, 1, pipe=true), io.in)
|
|
|
|
}
|
|
|
|
|
|
|
|
object LatencyPipe {
|
|
|
|
def apply[T <: Data](in: DecoupledIO[T], latency: Int): DecoupledIO[T] = {
|
|
|
|
val pipe = Module(new LatencyPipe(in.bits, latency))
|
|
|
|
pipe.io.in <> in
|
|
|
|
pipe.io.out
|
|
|
|
}
|
2016-09-10 00:12:05 +02:00
|
|
|
}
|