Added trace generator
This commit is contained in:
parent
da302504a5
commit
e63fc3bb44
148
groundtest/scripts/toaxe.py
Executable file
148
groundtest/scripts/toaxe.py
Executable file
@ -0,0 +1,148 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# This script takes memory-subsystem traces produced by the groundtest
|
||||||
|
# trace generator (see tracegen.scala) and puts them into a format
|
||||||
|
# that can be validated by the axe tool (see
|
||||||
|
# https://github.com/CTSRD-CHERI/axe).
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print "Usage: toaxe.py [FILE]"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
if sys.argv[1] == "-":
|
||||||
|
f = sys.stdin
|
||||||
|
else:
|
||||||
|
f = open(sys.argv[1], 'r')
|
||||||
|
if f == None:
|
||||||
|
print "File not found: ", sys.argv[1]
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
lineCount = 0
|
||||||
|
def error(msg):
|
||||||
|
print "Error at line ", lineCount, ": ", msg
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
# Mapping from address to axe address
|
||||||
|
addrMap = {}
|
||||||
|
nextAddr = 0
|
||||||
|
|
||||||
|
# Mapping from (thread id, tag id) to axe operation id
|
||||||
|
tagMap = {}
|
||||||
|
|
||||||
|
# Mapping from thread id to operation id
|
||||||
|
fenceReq = {}
|
||||||
|
|
||||||
|
# Mapping from thread id to operation id
|
||||||
|
loadReserve = {}
|
||||||
|
|
||||||
|
# Array of axe operations
|
||||||
|
ops = []
|
||||||
|
|
||||||
|
for line in f:
|
||||||
|
# Exit loop at end of trace
|
||||||
|
if line[0:9] == 'Completed': break
|
||||||
|
|
||||||
|
# Parse thread id and command
|
||||||
|
m = re.search(' *([0-9]+) *: *([^ ]*) (.*)', line)
|
||||||
|
if m == None: error("Expected: <thread-id> ':' <command>")
|
||||||
|
tid, cmd, line = m.group(1), m.group(2), m.group(3)
|
||||||
|
|
||||||
|
if cmd == 'fence-req':
|
||||||
|
# Parse time
|
||||||
|
m = re.search(' *@ *([0-9]+)', line)
|
||||||
|
if m == None: error ("expected timestamp")
|
||||||
|
# Insert placeholder containing request time
|
||||||
|
ops.append(str(m.group(1)))
|
||||||
|
fenceReq[tid] = len(ops)-1
|
||||||
|
elif cmd == 'fence-resp':
|
||||||
|
# Insert 'sync' operation
|
||||||
|
if not (tid in fenceReq) or fenceReq[tid] == None:
|
||||||
|
error("fence-resp without fence-req on thread " + tid)
|
||||||
|
startTime = ops[fenceReq[tid]]
|
||||||
|
op = str(tid) + ": sync @ " + startTime
|
||||||
|
# Add end-time
|
||||||
|
m = re.search(' *@ *([0-9]+)', line)
|
||||||
|
if m != None: op = op + ":" + str(m.group(1))
|
||||||
|
ops[fenceReq[tid]] = (op,)
|
||||||
|
fenceReq[tid] = None
|
||||||
|
elif cmd == 'load-req' or cmd == 'load-reserve-req':
|
||||||
|
# Parse address, tag, and time
|
||||||
|
m = re.search(' *([0-9a-fx]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
||||||
|
if m == None: error("expected <address> #<tag> @<timestamp>")
|
||||||
|
# Update address map
|
||||||
|
if not (m.group(1) in addrMap):
|
||||||
|
addrMap[m.group(1)] = nextAddr
|
||||||
|
nextAddr = nextAddr+1
|
||||||
|
# Insert place-holder
|
||||||
|
ops.append((cmd, None, addrMap[m.group(1)], m.group(3), None))
|
||||||
|
tagMap[(tid, m.group(2))] = len(ops)-1
|
||||||
|
if cmd == 'load-reserve-req':
|
||||||
|
loadReserve[tid] = len(ops)-1
|
||||||
|
elif cmd == 'store-req' or cmd == 'store-cond-req' or cmd == 'swap-req':
|
||||||
|
# Parse value, address, tag, and time
|
||||||
|
m = re.search(' *([0-9]+) *([0-9a-fx]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
||||||
|
if m == None: error("expected <value> <address> #<tag> @<timestamp>")
|
||||||
|
# Update address map
|
||||||
|
if not (m.group(2) in addrMap):
|
||||||
|
addrMap[m.group(2)] = nextAddr
|
||||||
|
nextAddr = nextAddr+1
|
||||||
|
# Insert place-holder
|
||||||
|
lr = loadReserve[tid] if tid in loadReserve else None
|
||||||
|
ops.append((cmd, m.group(1), addrMap[m.group(2)], m.group(4), lr))
|
||||||
|
tagMap[(tid, m.group(3))] = len(ops)-1
|
||||||
|
if cmd == 'store-cond-req': loadReserve[tid] = None
|
||||||
|
elif cmd == 'resp':
|
||||||
|
# Parse value and timestamp
|
||||||
|
m = re.search(' *([0-9]+) *# *([0-9]+) *@ *([0-9]+)', line)
|
||||||
|
if m == None: error("expected <value> #<tag> @<timestamp>")
|
||||||
|
# Find corresponding response
|
||||||
|
tag = m.group(2)
|
||||||
|
if not ((tid, tag) in tagMap) or tagMap[(tid, tag)] == None:
|
||||||
|
error("resp without associated req with tag " + tag + " on thread " + tid)
|
||||||
|
opId = tagMap[(tid, tag)]
|
||||||
|
(c, val, addr, start, lr) = ops[opId]
|
||||||
|
if c == 'load-req':
|
||||||
|
op = tid + ": M[" + str(addr) + '] == ' + m.group(1) + ' @ '
|
||||||
|
op += start + ':' + m.group(3)
|
||||||
|
ops[opId] = (op,)
|
||||||
|
elif c == 'store-req':
|
||||||
|
op = tid + ": M[" + str(addr) + '] := ' + val + ' @ '
|
||||||
|
op += start + ':' # + m.group(3)
|
||||||
|
ops[opId] = (op,)
|
||||||
|
elif c == 'load-reserve-req':
|
||||||
|
ops[opId] = (m.group(1), start, m.group(3))
|
||||||
|
elif c == 'store-cond-req':
|
||||||
|
if lr == None: error("store conditional without load-reserve")
|
||||||
|
(loadVal, loadStart, loadFin) = ops[lr]
|
||||||
|
if int(m.group(1)) != 0:
|
||||||
|
# SC fail
|
||||||
|
op = tid + ": M[" + str(addr) + "] == " + loadVal
|
||||||
|
op += " @ " + loadStart + ":" + loadFin
|
||||||
|
else:
|
||||||
|
# SC success
|
||||||
|
op = tid + ": { M[" + str(addr) + "] == " + loadVal + "; "
|
||||||
|
op += "M[" + str(addr) + "] := " + val + "} @ "
|
||||||
|
op += loadStart + ":" # + m.group(3)
|
||||||
|
ops[lr] = (op,)
|
||||||
|
ops[opId] = None
|
||||||
|
elif c == 'swap-req':
|
||||||
|
op = tid + ": { M[" + str(addr) + '] == ' + m.group(1)
|
||||||
|
op += '; M[' + str(addr) + '] := ' + val
|
||||||
|
op += '} @ ' + start + ':' # + m.group(3)
|
||||||
|
ops[opId] = (op,)
|
||||||
|
else:
|
||||||
|
error("Unknown command '" + cmd + "'")
|
||||||
|
|
||||||
|
lineCount = lineCount+1
|
||||||
|
|
||||||
|
# Print address map in comments
|
||||||
|
for addr in addrMap:
|
||||||
|
print ("# &M[" + str(addrMap[addr]) + "] == " + addr)
|
||||||
|
|
||||||
|
# Print axe trace
|
||||||
|
for op in ops:
|
||||||
|
if op != None and isinstance(op, tuple) and len(op) == 1:
|
||||||
|
print op[0]
|
499
groundtest/src/main/scala/tracegen.scala
Normal file
499
groundtest/src/main/scala/tracegen.scala
Normal file
@ -0,0 +1,499 @@
|
|||||||
|
// Generate memory traces that result from random sequences of memory
|
||||||
|
// operations. These traces can then be validated by an external
|
||||||
|
// tool. A trace is a simply sequence of memory requests and
|
||||||
|
// responses.
|
||||||
|
|
||||||
|
package groundtest
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import uncore._
|
||||||
|
import junctions._
|
||||||
|
import rocket._
|
||||||
|
import scala.util.Random
|
||||||
|
import cde.{Parameters, Field}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Trace-generator parameters
|
||||||
|
// ==========================
|
||||||
|
|
||||||
|
// Compile-time parameters:
|
||||||
|
//
|
||||||
|
// * The id of the generator (there may be more than one in a
|
||||||
|
// multi-core system).
|
||||||
|
//
|
||||||
|
// * The total number of generators present in the system.
|
||||||
|
//
|
||||||
|
// * The desired number of requests to be sent by each generator.
|
||||||
|
//
|
||||||
|
// * A list of physical addresses from which an address is drawn when
|
||||||
|
// generating a fresh request.
|
||||||
|
|
||||||
|
case object NumGens extends Field[Int]
|
||||||
|
case object NumReqsPerGen extends Field[Int]
|
||||||
|
case object AddressBag extends Field[List[Int]]
|
||||||
|
|
||||||
|
trait HasTraceGenParams {
|
||||||
|
implicit val p: Parameters
|
||||||
|
val numGens = p(NumGens)
|
||||||
|
val numBitsInId = log2Up(numGens)
|
||||||
|
val numReqsPerGen = p(NumReqsPerGen)
|
||||||
|
val memRespTimeout = 4096
|
||||||
|
val numBitsInWord = p(WordBits)
|
||||||
|
val numBytesInWord = numBitsInWord / 8
|
||||||
|
val numBitsInWordOffset = log2Up(numBytesInWord)
|
||||||
|
val addressBag = p(AddressBag)
|
||||||
|
val logNumAddrsInTrace = log2Up(addressBag.length)
|
||||||
|
|
||||||
|
require(numBytesInWord * 8 == numBitsInWord)
|
||||||
|
require(1 << logNumAddrsInTrace == addressBag.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// Trace format
|
||||||
|
// ============
|
||||||
|
|
||||||
|
// Let <id> denote a generator id;
|
||||||
|
// <addr> denote an address (in hex);
|
||||||
|
// <data> denote a value that is stored at an address;
|
||||||
|
// <tag> denote a unique request/response id;
|
||||||
|
// and <time> denote an integer representing a cycle-count.
|
||||||
|
|
||||||
|
// Each line in the trace takes one of the following formats.
|
||||||
|
//
|
||||||
|
// <id>: load-req <addr> #<tag> @<time>
|
||||||
|
// <id>: load-reserve-req <addr> #<tag> @<time>
|
||||||
|
// <id>: store-req <data> <addr> #<tag> @<time>
|
||||||
|
// <id>: store-cond-req <data> <addr> #<tag> @<time>
|
||||||
|
// <id>: swap-req <data> <addr> #<tag> @<time>
|
||||||
|
// <id>: resp <data> #<tag> @<time>
|
||||||
|
// <id>: fence-req @<time>
|
||||||
|
// <id>: fence-resp @<time>
|
||||||
|
|
||||||
|
// NOTE: The (address, value) pair of every generated store is unique,
|
||||||
|
// i.e. the same value is never written to the same address twice.
|
||||||
|
// This aids trace validation.
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// Random seeds
|
||||||
|
// ============
|
||||||
|
|
||||||
|
// The generator employs "unitialised registers" to seed its PRNGs;
|
||||||
|
// these are randomly initialised by the C++ backend. This means that
|
||||||
|
// the "-s" command-line argument to the Rocket emulator can be used
|
||||||
|
// to generate new traces, or to replay specific ones.
|
||||||
|
|
||||||
|
// ===========
|
||||||
|
// Tag manager
|
||||||
|
// ===========
|
||||||
|
|
||||||
|
// This is used to obtain unique tags for memory requests: each
|
||||||
|
// request must carry a unique tag since responses can come back
|
||||||
|
// out-of-order.
|
||||||
|
//
|
||||||
|
// The tag manager can be viewed as a set of tags. The user can take
|
||||||
|
// a tag out of the set (if there is one available) and later put it
|
||||||
|
// back.
|
||||||
|
|
||||||
|
class TagMan(val logNumTags : Int) extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
// Is there a tag available?
|
||||||
|
val available = Bool(OUTPUT)
|
||||||
|
// If so, which one?
|
||||||
|
val tagOut = UInt(OUTPUT, logNumTags)
|
||||||
|
// User pulses this to take the currently available tag
|
||||||
|
val take = Bool(INPUT)
|
||||||
|
// User pulses this to put a tag back
|
||||||
|
val put = Bool(INPUT)
|
||||||
|
// And the tag put back is
|
||||||
|
val tagIn = UInt(INPUT, logNumTags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total number of tags available
|
||||||
|
val numTags = 1 << logNumTags
|
||||||
|
|
||||||
|
// For each tag, record whether or not it is in use
|
||||||
|
val inUse = List.fill(numTags)(Reg(init = Bool(false)))
|
||||||
|
|
||||||
|
// Mapping from each tag to its 'inUse' bit
|
||||||
|
val inUseMap = (0 to numTags-1).map(i => UInt(i, logNumTags)).zip(inUse)
|
||||||
|
|
||||||
|
// Next tag to offer
|
||||||
|
val nextTag = Reg(init = UInt(0, logNumTags))
|
||||||
|
io.tagOut := nextTag
|
||||||
|
|
||||||
|
// Is the next tag available?
|
||||||
|
io.available := ~Lookup(nextTag, Bool(true), inUseMap)
|
||||||
|
|
||||||
|
// When user takes a tag
|
||||||
|
when (io.take) {
|
||||||
|
for ((i, b) <- inUseMap) {
|
||||||
|
when (i === nextTag) { b := Bool(true) }
|
||||||
|
}
|
||||||
|
nextTag := nextTag + UInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When user puts a tag back
|
||||||
|
when (io.put) {
|
||||||
|
for ((i, b) <- inUseMap) {
|
||||||
|
when (i === io.tagIn) { b := Bool(false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============
|
||||||
|
// Trace generator
|
||||||
|
// ===============
|
||||||
|
|
||||||
|
class TraceGenerator(id: Int)
|
||||||
|
(implicit p: Parameters) extends L1HellaCacheModule()(p)
|
||||||
|
with HasTraceGenParams {
|
||||||
|
val io = new Bundle {
|
||||||
|
val finished = Bool(OUTPUT)
|
||||||
|
val mem = new HellaCacheIO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random addresses
|
||||||
|
// ----------------
|
||||||
|
|
||||||
|
// Address list taken from module parameters.
|
||||||
|
|
||||||
|
val numAddrsInTrace = 1 << logNumAddrsInTrace
|
||||||
|
|
||||||
|
val bagOfAddrs = addressBag.map(x => UInt(x, numBitsInWord))
|
||||||
|
|
||||||
|
// A random index into the address bag.
|
||||||
|
|
||||||
|
val randAddrIndex = Module(new LCG(logNumAddrsInTrace)).io.out
|
||||||
|
|
||||||
|
// A random address from the address bag.
|
||||||
|
|
||||||
|
val addrIndices = (0 to numAddrsInTrace-1).
|
||||||
|
map(i => UInt(i, logNumAddrsInTrace))
|
||||||
|
|
||||||
|
val randAddr = MuxLookup(randAddrIndex, UInt(0),
|
||||||
|
addrIndices.zip(bagOfAddrs))
|
||||||
|
|
||||||
|
// Random opcodes
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
// Generate random opcodes for memory operations according to the
|
||||||
|
// given frequency distribution.
|
||||||
|
|
||||||
|
// Opcodes
|
||||||
|
val (opNop :: opLoad :: opStore ::
|
||||||
|
opFence :: opLRSC :: opSwap ::
|
||||||
|
opDelay :: Nil) = Enum(Bits(), 7)
|
||||||
|
|
||||||
|
// Distribution specified as a list of (frequency,value) pairs.
|
||||||
|
// NOTE: frequencies must sum to a power of two.
|
||||||
|
|
||||||
|
//val randOp = Frequency(List(
|
||||||
|
// (10, opLoad),
|
||||||
|
// (10, opStore),
|
||||||
|
// (4, opFence),
|
||||||
|
// (3, opLRSC),
|
||||||
|
// (3, opSwap),
|
||||||
|
// (2, opDelay)))
|
||||||
|
|
||||||
|
// For now, just generate loads and stores as this is enough to
|
||||||
|
// expose strange behaviour.
|
||||||
|
val randOp = Frequency(List(
|
||||||
|
(15, opLoad),
|
||||||
|
(15, opStore),
|
||||||
|
(2, opDelay)))
|
||||||
|
|
||||||
|
// Request/response tags
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
// Responses may come back out-of-order. Each request and response
|
||||||
|
// therefore contains a unique 7-bit identifier, referred to as a
|
||||||
|
// "tag", used to match each response with its corresponding request.
|
||||||
|
|
||||||
|
// Create a tag manager giving out unique 3-bit tags
|
||||||
|
val tagMan = Module(new TagMan(3))
|
||||||
|
|
||||||
|
// Default inputs
|
||||||
|
tagMan.io.take := Bool(false);
|
||||||
|
tagMan.io.put := Bool(false);
|
||||||
|
tagMan.io.tagIn := UInt(0);
|
||||||
|
|
||||||
|
// Cycle counter
|
||||||
|
// -------------
|
||||||
|
|
||||||
|
// 32-bit cycle count used to record send-times of requests and
|
||||||
|
// receive-times of respones.
|
||||||
|
|
||||||
|
val cycleCount = Reg(init = UInt(0, 32))
|
||||||
|
cycleCount := cycleCount + UInt(1);
|
||||||
|
|
||||||
|
// Delay timer
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
// Used to implement the delay operation and to insert random
|
||||||
|
// delays between load-reserve and store-conditional commands.
|
||||||
|
|
||||||
|
// A 16-bit timer is plenty
|
||||||
|
val delayTimer = Module(new DynamicTimer(16))
|
||||||
|
|
||||||
|
// Used to generate a random delay period
|
||||||
|
val randDelayBase = Module(new LCG16()).io.out
|
||||||
|
|
||||||
|
// Random delay period: usually small, occasionally big
|
||||||
|
val randDelay = Frequency(List(
|
||||||
|
(14, UInt(0, 13) ## randDelayBase(2, 0)),
|
||||||
|
(2, UInt(0, 11) ## randDelayBase(5, 0))))
|
||||||
|
|
||||||
|
// Default inputs
|
||||||
|
delayTimer.io.start := Bool(false)
|
||||||
|
delayTimer.io.period := randDelay
|
||||||
|
delayTimer.io.stop := Bool(false)
|
||||||
|
|
||||||
|
// Operation dispatch
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
// Hardware thread id
|
||||||
|
val tid = UInt(id, numBitsInId)
|
||||||
|
|
||||||
|
// Request & response count
|
||||||
|
val reqCount = Reg(init = UInt(0, 32))
|
||||||
|
val respCount = Reg(init = UInt(0, 32))
|
||||||
|
|
||||||
|
// Current operation being executed
|
||||||
|
val currentOp = Reg(init = opNop)
|
||||||
|
|
||||||
|
// If larger than 0, a multi-cycle operation is in progress.
|
||||||
|
// Value indicates stage of progress.
|
||||||
|
val opInProgress = Reg(init = UInt(0, 2))
|
||||||
|
|
||||||
|
// Indicate when a fresh request is to be sent
|
||||||
|
val sendFreshReq = Wire(Bool())
|
||||||
|
sendFreshReq := Bool(false)
|
||||||
|
|
||||||
|
// Used to generate unique data values
|
||||||
|
val nextData = Reg(init = UInt(1, numBitsInWord-numBitsInId))
|
||||||
|
|
||||||
|
// Registers for all the interesting parts of a request
|
||||||
|
val reqValid = Reg(init = Bool(false))
|
||||||
|
val reqAddr = Reg(init = UInt(0, numBitsInWord))
|
||||||
|
val reqData = Reg(init = UInt(0, numBitsInWord))
|
||||||
|
val reqCmd = Reg(init = UInt(0, 5))
|
||||||
|
val reqTag = Reg(init = UInt(0, 7))
|
||||||
|
|
||||||
|
// Condition on being allowed to send a fresh request
|
||||||
|
val canSendFreshReq = (!reqValid || io.mem.req.fire()) &&
|
||||||
|
tagMan.io.available
|
||||||
|
|
||||||
|
// Operation dispatch
|
||||||
|
when (reqCount < UInt(numReqsPerGen)) {
|
||||||
|
|
||||||
|
// No-op
|
||||||
|
when (currentOp === opNop) {
|
||||||
|
// Move on to a new operation
|
||||||
|
currentOp := randOp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fence
|
||||||
|
when (currentOp === opFence) {
|
||||||
|
when (opInProgress === UInt(0) && !reqValid) {
|
||||||
|
// Emit fence request
|
||||||
|
printf("%d: fence-req @%d\n", tid, cycleCount)
|
||||||
|
// Multi-cycle operation now in progress
|
||||||
|
opInProgress := UInt(1)
|
||||||
|
}
|
||||||
|
// Wait until all requests have had a response
|
||||||
|
.elsewhen (io.mem.ordered && reqCount === respCount) {
|
||||||
|
// Emit fence response
|
||||||
|
printf("%d: fence-resp @%d\n", tid, cycleCount)
|
||||||
|
// Move on to a new operation
|
||||||
|
currentOp := randOp
|
||||||
|
// Operation finished
|
||||||
|
opInProgress := UInt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay
|
||||||
|
when (currentOp === opDelay) {
|
||||||
|
when (opInProgress === UInt(0)) {
|
||||||
|
// Start timer
|
||||||
|
delayTimer.io.start := Bool(true)
|
||||||
|
// Multi-cycle operation now in progress
|
||||||
|
opInProgress := UInt(1)
|
||||||
|
}
|
||||||
|
.elsewhen (delayTimer.io.timeout) {
|
||||||
|
// Move on to a new operation
|
||||||
|
currentOp := randOp
|
||||||
|
// Operation finished
|
||||||
|
opInProgress := UInt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load, store, or atomic swap
|
||||||
|
when (currentOp === opLoad ||
|
||||||
|
currentOp === opStore ||
|
||||||
|
currentOp === opSwap) {
|
||||||
|
when (canSendFreshReq) {
|
||||||
|
// Set address
|
||||||
|
reqAddr := randAddr
|
||||||
|
// Set command
|
||||||
|
when (currentOp === opLoad) {
|
||||||
|
reqCmd := M_XRD
|
||||||
|
} .elsewhen (currentOp === opStore) {
|
||||||
|
reqCmd := M_XWR
|
||||||
|
} .elsewhen (currentOp === opSwap) {
|
||||||
|
reqCmd := M_XA_SWAP
|
||||||
|
}
|
||||||
|
// Send request
|
||||||
|
sendFreshReq := Bool(true)
|
||||||
|
// Move on to a new operation
|
||||||
|
currentOp := randOp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load-reserve and store-conditional
|
||||||
|
// First issue an LR, then delay, then issue an SC
|
||||||
|
when (currentOp === opLRSC) {
|
||||||
|
// LR request has not yet been sent
|
||||||
|
when (opInProgress === UInt(0)) {
|
||||||
|
when (canSendFreshReq) {
|
||||||
|
// Set address and command
|
||||||
|
reqAddr := randAddr
|
||||||
|
reqCmd := M_XLR
|
||||||
|
// Send request
|
||||||
|
sendFreshReq := Bool(true)
|
||||||
|
// Multi-cycle operation now in progress
|
||||||
|
opInProgress := UInt(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// LR request has been sent, start delay timer
|
||||||
|
when (opInProgress === UInt(1)) {
|
||||||
|
// Start timer
|
||||||
|
delayTimer.io.start := Bool(true)
|
||||||
|
// Indicate that delay has started
|
||||||
|
opInProgress := UInt(2)
|
||||||
|
}
|
||||||
|
// Delay in progress
|
||||||
|
when (opInProgress === UInt(2)) {
|
||||||
|
when (delayTimer.io.timeout) {
|
||||||
|
// Delay finished
|
||||||
|
opInProgress := UInt(3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delay finished, send SC request
|
||||||
|
when (opInProgress === UInt(3)) {
|
||||||
|
when (canSendFreshReq) {
|
||||||
|
// Set command, but leave address
|
||||||
|
// i.e. use same address as LR did
|
||||||
|
reqCmd := M_XSC
|
||||||
|
// Send request
|
||||||
|
sendFreshReq := Bool(true)
|
||||||
|
// Multi-cycle operation finished
|
||||||
|
opInProgress := UInt(0)
|
||||||
|
// Move on to a new operation
|
||||||
|
currentOp := randOp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sending of requests
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
when (sendFreshReq) {
|
||||||
|
// Grab a unique tag for the request
|
||||||
|
reqTag := Cat(UInt(0), tagMan.io.tagOut)
|
||||||
|
tagMan.io.take := Bool(true)
|
||||||
|
// Fill in unique data
|
||||||
|
reqData := Cat(nextData, tid)
|
||||||
|
nextData := nextData + UInt(1)
|
||||||
|
// Request is good to go!
|
||||||
|
reqValid := Bool(true)
|
||||||
|
// Increment request count
|
||||||
|
reqCount := reqCount + UInt(1)
|
||||||
|
}
|
||||||
|
.elsewhen (io.mem.req.fire()) {
|
||||||
|
// Request has been sent and there is no new request ready
|
||||||
|
reqValid := Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wire up interface to memory
|
||||||
|
io.mem.req.valid := reqValid
|
||||||
|
io.mem.req.bits.addr := reqAddr
|
||||||
|
io.mem.req.bits.data := reqData
|
||||||
|
io.mem.req.bits.typ := MT_D
|
||||||
|
io.mem.req.bits.cmd := reqCmd
|
||||||
|
io.mem.req.bits.tag := reqTag
|
||||||
|
io.mem.req.bits.kill := Bool(false)
|
||||||
|
io.mem.req.bits.phys := Bool(true)
|
||||||
|
|
||||||
|
// On cycle when request is actually sent, print it
|
||||||
|
when (io.mem.req.fire()) {
|
||||||
|
// Short-hand for address
|
||||||
|
val addr = io.mem.req.bits.addr
|
||||||
|
// Print thread id
|
||||||
|
printf("%d:", tid)
|
||||||
|
// Print command
|
||||||
|
when (reqCmd === M_XRD) {
|
||||||
|
printf(" load-req 0x%x", addr)
|
||||||
|
}
|
||||||
|
when (reqCmd === M_XLR) {
|
||||||
|
printf(" load-reserve-req 0x%x", addr)
|
||||||
|
}
|
||||||
|
when (reqCmd === M_XWR) {
|
||||||
|
printf(" store-req %d 0x%x", reqData, addr)
|
||||||
|
}
|
||||||
|
when (reqCmd === M_XSC) {
|
||||||
|
printf(" store-cond-req %d 0x%x", reqData, addr)
|
||||||
|
}
|
||||||
|
when (reqCmd === M_XA_SWAP) {
|
||||||
|
printf(" swap-req %d 0x%x", reqData, addr)
|
||||||
|
}
|
||||||
|
// Print tag
|
||||||
|
printf(" #%d", reqTag)
|
||||||
|
// Print time
|
||||||
|
printf(" @%d\n", cycleCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling of responses
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
// When a response is received
|
||||||
|
when (io.mem.resp.valid) {
|
||||||
|
// Put tag back in tag set
|
||||||
|
tagMan.io.tagIn := io.mem.resp.bits.tag
|
||||||
|
tagMan.io.put := Bool(true)
|
||||||
|
// Print response
|
||||||
|
printf("%d: resp %d #%d @%d\n", tid,
|
||||||
|
io.mem.resp.bits.data, io.mem.resp.bits.tag, cycleCount)
|
||||||
|
// Increment response count
|
||||||
|
respCount := respCount + UInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response timeouts
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// Raise an error if a response takes too long to come back.
|
||||||
|
|
||||||
|
val timeout = Timer(memRespTimeout, io.mem.req.fire(), io.mem.resp.valid)
|
||||||
|
assert(!timeout, s"Trace generator ${id} timed out waiting for response")
|
||||||
|
|
||||||
|
// Termination condition
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
io.finished := reqCount === UInt(numReqsPerGen) &&
|
||||||
|
respCount === UInt(numReqsPerGen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// Trace-generator wrapper
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
class GroundTestTraceGenerator(id: Int)(implicit p: Parameters)
|
||||||
|
extends GroundTest()(p) with HasTraceGenParams {
|
||||||
|
|
||||||
|
disablePorts(cache = false)
|
||||||
|
|
||||||
|
val traceGen = Module(new TraceGenerator(id))
|
||||||
|
io.cache <> traceGen.io.mem
|
||||||
|
|
||||||
|
io.finished := traceGen.io.finished
|
||||||
|
}
|
@ -2,6 +2,12 @@ package groundtest
|
|||||||
|
|
||||||
import Chisel._
|
import Chisel._
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// Static timer
|
||||||
|
// ============
|
||||||
|
|
||||||
|
// Timer with a statically-specified period.
|
||||||
|
|
||||||
class Timer(initCount: Int) extends Module {
|
class Timer(initCount: Int) extends Module {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
val start = Bool(INPUT)
|
val start = Bool(INPUT)
|
||||||
@ -36,3 +42,112 @@ object Timer {
|
|||||||
timer.io.timeout
|
timer.io.timeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============
|
||||||
|
// Dynamic timer
|
||||||
|
// =============
|
||||||
|
|
||||||
|
// Timer with a dynamically-settable period.
|
||||||
|
|
||||||
|
class DynamicTimer(width: Int) extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
val start = Bool(INPUT)
|
||||||
|
val period = UInt(INPUT, width)
|
||||||
|
val stop = Bool(INPUT)
|
||||||
|
val timeout = Bool(OUTPUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
val countdown = Reg(init = UInt(0, width))
|
||||||
|
val active = Reg(init = Bool(false))
|
||||||
|
|
||||||
|
when (io.start) {
|
||||||
|
countdown := io.period
|
||||||
|
active := Bool(true)
|
||||||
|
}
|
||||||
|
.elsewhen (active) {
|
||||||
|
countdown := countdown - UInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (io.stop) {
|
||||||
|
active := Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.timeout := countdown === UInt(0) && active
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// LCG16 module
|
||||||
|
// ============
|
||||||
|
|
||||||
|
// A 16-bit psuedo-random generator based on a linear conguential
|
||||||
|
// generator (LCG). The state is stored in an unitialised register.
|
||||||
|
// When using the C++ backend, it is straigtforward to arrange a
|
||||||
|
// random initial value for each uninitialised register, effectively
|
||||||
|
// seeding each LCG16 instance with a different seed.
|
||||||
|
|
||||||
|
class LCG16 extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
val out = UInt(OUTPUT, 16)
|
||||||
|
}
|
||||||
|
val state = Reg(UInt(width = 32))
|
||||||
|
state := state * UInt(1103515245, 32) + UInt(12345, 32)
|
||||||
|
io.out := state(30, 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========
|
||||||
|
// LCG module
|
||||||
|
// ==========
|
||||||
|
|
||||||
|
// An n-bit psuedo-random generator made from many instances of a
|
||||||
|
// 16-bit LCG. Parameter 'width' must be larger than 0.
|
||||||
|
|
||||||
|
class LCG(val width : Int) extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
val out = UInt(OUTPUT, width)
|
||||||
|
}
|
||||||
|
require(width > 0)
|
||||||
|
val numLCG16s : Int = (width+15)/16
|
||||||
|
val outs = List.fill(numLCG16s)(Module(new LCG16()).io.out)
|
||||||
|
io.out := Cat( outs(0)((width%16)-1, 0)
|
||||||
|
, outs.drop(1) : _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================
|
||||||
|
// Frequency distribution
|
||||||
|
// ======================
|
||||||
|
|
||||||
|
// Given a list of (frequency, value) pairs, return a random value
|
||||||
|
// according to the frequency distribution. The sum of the
|
||||||
|
// frequencies in the distribution must be a power of two.
|
||||||
|
|
||||||
|
object Frequency {
|
||||||
|
def apply(dist : List[(Int, Bits)]) : Bits = {
|
||||||
|
// Distribution must be non-empty
|
||||||
|
require(dist.length > 0)
|
||||||
|
|
||||||
|
// Require that the frequencies sum to a power of two
|
||||||
|
val (freqs, vals) = dist.unzip
|
||||||
|
val total = freqs.sum
|
||||||
|
require(isPow2(total))
|
||||||
|
|
||||||
|
// First item in the distribution
|
||||||
|
val (firstFreq, firstVal) = dist.head
|
||||||
|
|
||||||
|
// Result wire
|
||||||
|
val result = Wire(Bits(width = firstVal.getWidth))
|
||||||
|
result := UInt(0)
|
||||||
|
|
||||||
|
// Random value
|
||||||
|
val randVal = Module(new LCG(log2Up(total))).io.out
|
||||||
|
|
||||||
|
// Pick return value
|
||||||
|
var count = firstFreq
|
||||||
|
var select = when (randVal < UInt(firstFreq)) { result := firstVal }
|
||||||
|
for (p <- dist.drop(1)) {
|
||||||
|
count = count + p._1
|
||||||
|
select = select.elsewhen(randVal < UInt(count)) { result := p._2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user