diff --git a/groundtest/scripts/toaxe.py b/groundtest/scripts/toaxe.py new file mode 100755 index 00000000..684770ae --- /dev/null +++ b/groundtest/scripts/toaxe.py @@ -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: ':' ") + 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
# @") + # 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
# @") + # 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 # @") + # 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] diff --git a/groundtest/src/main/scala/tracegen.scala b/groundtest/src/main/scala/tracegen.scala new file mode 100644 index 00000000..1cff67cb --- /dev/null +++ b/groundtest/src/main/scala/tracegen.scala @@ -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 denote a generator id; +// denote an address (in hex); +// denote a value that is stored at an address; +// denote a unique request/response id; +// and