Added trace generator
This commit is contained in:
		
				
					committed by
					
						 Howard Mao
						Howard Mao
					
				
			
			
				
	
			
			
			
						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._ | ||||
|  | ||||
| // ============ | ||||
| // Static timer | ||||
| // ============ | ||||
|  | ||||
| // Timer with a statically-specified period. | ||||
|  | ||||
| class Timer(initCount: Int) extends Module { | ||||
|   val io = new Bundle { | ||||
|     val start = Bool(INPUT) | ||||
| @@ -36,3 +42,112 @@ object Timer { | ||||
|     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 | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user