// Assign fields from the instruction
new_record->instNum = head_inst->seqNum;
- new_record->load = head_inst->isLoad();
- new_record->store = head_inst->isStore();
new_record->commit = commit;
+ new_record->type = head_inst->isLoad() ? Record::LOAD :
+ (head_inst->isStore() ? Record::STORE :
+ Record::COMP);
// Assign fields for creating a request in case of a load/store
new_record->reqFlags = head_inst->memReqFlags;
ElasticTrace::updateCommitOrderDep(TraceInfo* new_record,
bool find_load_not_store)
{
- assert(new_record->store);
+ assert(new_record->isStore());
// Iterate in reverse direction to search for the last committed
// load/store that completed earlier than the new record
depTraceRevItr from_itr(depTrace.end());
uint32_t num_go_back = 0;
Tick execute_tick = 0;
- if (new_record->load) {
+ if (new_record->isLoad()) {
// The execution time of a load is when a request is sent
execute_tick = new_record->executeTick;
++numIssueOrderDepLoads;
- } else if (new_record->store) {
+ } else if (new_record->isStore()) {
// The execution time of a store is when it is sent, i.e. committed
execute_tick = curTick();
++numIssueOrderDepStores;
void
ElasticTrace::assignRobDep(TraceInfo* past_record, TraceInfo* new_record) {
DPRINTF(ElasticTrace, "%s %lli has ROB dependency on %lli\n",
- new_record->load ? "Load" : (new_record->store ? "Store" :
- "Non load/store"),
- new_record->instNum, past_record->instNum);
-
+ new_record->typeToStr(), new_record->instNum,
+ past_record->instNum);
// Add dependency on past record
new_record->robDepList.push_back(past_record->instNum);
// Update new_record's compute delay with respect to the past record
ElasticTrace::hasStoreCommitted(TraceInfo* past_record,
Tick execute_tick) const
{
- return (past_record->store && past_record->commitTick <= execute_tick);
+ return (past_record->isStore() && past_record->commitTick <= execute_tick);
}
bool
ElasticTrace::hasLoadCompleted(TraceInfo* past_record,
Tick execute_tick) const
{
- return(past_record->load && past_record->commit &&
+ return(past_record->isLoad() && past_record->commit &&
past_record->toCommitTick <= execute_tick);
}
Tick execute_tick) const
{
// Check if previous inst is a load sent earlier than this
- return (past_record->load && past_record->commit &&
+ return (past_record->isLoad() && past_record->commit &&
past_record->executeTick <= execute_tick);
}
ElasticTrace::hasCompCompleted(TraceInfo* past_record,
Tick execute_tick) const
{
- return(!past_record->store && !past_record->load &&
- past_record->toCommitTick <= execute_tick);
+ return(past_record->isComp() && past_record->toCommitTick <= execute_tick);
}
void
// computation delay
execution_tick = new_record->getExecuteTick();
- if (past_record->load) {
- if (new_record->store) {
+ if (past_record->isLoad()) {
+ if (new_record->isStore()) {
completion_tick = past_record->toCommitTick;
} else {
completion_tick = past_record->executeTick;
}
- } else if (past_record->store) {
+ } else if (past_record->isStore()) {
completion_tick = past_record->commitTick;
- } else {
+ } else if (past_record->isComp()){
completion_tick = past_record->toCommitTick;
}
assert(execution_tick >= completion_tick);
// completion tick of that instruction is when it wrote to the register,
// that is toCommitTick. In case, of a store updating a destination
// register, this is approximated to commitTick instead
- if (past_record->store) {
+ if (past_record->isStore()) {
completion_tick = past_record->commitTick;
} else {
completion_tick = past_record->toCommitTick;
Tick
ElasticTrace::TraceInfo::getExecuteTick() const
{
- if (load) {
+ if (isLoad()) {
// Execution tick for a load instruction is when the request was sent,
// that is executeTick.
return executeTick;
- } else if (store) {
+ } else if (isStore()) {
// Execution tick for a store instruction is when the request was sent,
// that is commitTick.
return commitTick;
depTraceItr dep_trace_itr_start = dep_trace_itr;
while (num_to_write > 0) {
TraceInfo* temp_ptr = *dep_trace_itr;
- // If no node dependends on a non load/store node then there is
- // no reason to track it in the dependency graph. We filter out such
+ assert(temp_ptr->type != Record::INVALID);
+ // If no node dependends on a comp node then there is no reason to
+ // track the comp node in the dependency graph. We filter out such
// nodes but count them and add a weight field to the subsequent node
// that we do include in the trace.
- if (temp_ptr->numDepts != 0 || temp_ptr->load || temp_ptr->store) {
-
+ if (!temp_ptr->isComp() || temp_ptr->numDepts != 0) {
DPRINTFR(ElasticTrace, "Instruction with seq. num %lli "
"is as follows:\n", temp_ptr->instNum);
- if (temp_ptr->load || temp_ptr->store) {
- DPRINTFR(ElasticTrace, "\tis a %s\n",
- (temp_ptr->load ? "Load" : "Store"));
+ if (temp_ptr->isLoad() || temp_ptr->isStore()) {
+ DPRINTFR(ElasticTrace, "\tis a %s\n", temp_ptr->typeToStr());
DPRINTFR(ElasticTrace, "\thas a request with addr %i, size %i,"
" flags %i\n", temp_ptr->addr, temp_ptr->size,
temp_ptr->reqFlags);
} else {
- DPRINTFR(ElasticTrace, "\tis not a load or store\n");
+ DPRINTFR(ElasticTrace, "\tis a %s\n", temp_ptr->typeToStr());
}
if (firstWin && temp_ptr->compDelay == -1) {
- if (temp_ptr->load) {
+ if (temp_ptr->isLoad()) {
temp_ptr->compDelay = temp_ptr->executeTick;
- } else if (temp_ptr->store) {
+ } else if (temp_ptr->isStore()) {
temp_ptr->compDelay = temp_ptr->commitTick;
} else {
temp_ptr->compDelay = temp_ptr->toCommitTick;
// Create a protobuf message for the dependency record
ProtoMessage::InstDepRecord dep_pkt;
dep_pkt.set_seq_num(temp_ptr->instNum);
- dep_pkt.set_load(temp_ptr->load);
- dep_pkt.set_store(temp_ptr->store);
+ dep_pkt.set_type(temp_ptr->type);
dep_pkt.set_pc(temp_ptr->pc);
- if (temp_ptr->load || temp_ptr->store) {
+ if (temp_ptr->isLoad() || temp_ptr->isStore()) {
dep_pkt.set_flags(temp_ptr->reqFlags);
dep_pkt.set_addr(temp_ptr->addr);
dep_pkt.set_size(temp_ptr->size);
;
}
+const std::string&
+ElasticTrace::TraceInfo::typeToStr() const
+{
+ return Record::RecordType_Name(type);
+}
+
const std::string
ElasticTrace::name() const
{
typedef typename O3CPUImpl::DynInstPtr DynInstPtr;
typedef typename std::pair<InstSeqNum, PhysRegIndex> SeqNumRegPair;
+ /** Trace record types corresponding to instruction node types */
+ typedef ProtoMessage::InstDepRecord::RecordType RecordType;
+ typedef ProtoMessage::InstDepRecord Record;
+
/** Constructor */
ElasticTrace(const ElasticTraceParams *params);
*/
/* Instruction sequence number. */
InstSeqNum instNum;
+ /** The type of trace record for the instruction node */
+ RecordType type;
/* Tick when instruction was in execute stage. */
Tick executeTick;
/* Tick when instruction was marked ready and sent to commit stage. */
Tick toCommitTick;
/* Tick when instruction was committed. */
Tick commitTick;
- /* If instruction was a load, a store, committed. */
- bool load, store, commit;
+ /* If instruction was committed, as against squashed. */
+ bool commit;
/* List of order dependencies. */
std::list<InstSeqNum> robDepList;
/* List of physical register RAW dependencies. */
Addr addr;
/* Request size in case of a load/store instruction */
unsigned size;
+ /** Default Constructor */
+ TraceInfo()
+ : type(Record::INVALID)
+ { }
+ /** Is the record a load */
+ bool isLoad() const { return (type == Record::LOAD); }
+ /** Is the record a store */
+ bool isStore() const { return (type == Record::STORE); }
+ /** Is the record a fetch triggering an Icache request */
+ bool isComp() const { return (type == Record::COMP); }
+ /** Return string specifying the type of the node */
+ const std::string& typeToStr() const;
/** @} */
/**
++numRetrySucceeded;
retryPkt = nullptr;
}
- } else if (node_ptr->isLoad || node_ptr->isStore) {
+ } else if (node_ptr->isLoad() || node_ptr->isStore()) {
// If there is no retryPkt, attempt to send a memory request in
// case of a load or store node. If the send fails, executeMemReq()
// returns a packet pointer, which we save in retryPkt. In case of
// dependencies complete. But as per dependency modelling we need
// to mark ROB dependencies of load and non load/store nodes which
// are based on successful sending of the load as complete.
- if (node_ptr->isLoad && !node_ptr->isStrictlyOrdered()) {
+ if (node_ptr->isLoad() && !node_ptr->isStrictlyOrdered()) {
// If execute succeeded mark its dependents as complete
DPRINTF(TraceCPUData, "Node seq. num %lli sent. Waking up "
"dependents..\n", node_ptr->seqNum);
while (child_itr != (node_ptr->dependents).end()) {
// ROB dependency of a store on a load must not be removed
// after load is sent but after response is received
- if (!(*child_itr)->isStore &&
+ if (!(*child_itr)->isStore() &&
(*child_itr)->removeRobDep(node_ptr->seqNum)) {
// Check if the child node has become dependency free
// marked complete. Thus it is safe to delete it. For
// stores and non load/store nodes all dependencies were
// marked complete so it is safe to delete it.
- if (!node_ptr->isLoad || node_ptr->isStrictlyOrdered()) {
+ if (!node_ptr->isLoad() || node_ptr->isStrictlyOrdered()) {
// Release all resources occupied by the completed node
hwResource.release(node_ptr);
// clear the dynamically allocated set of dependents
// If the request is strictly ordered, do not send it. Just return nullptr
// as if it was succesfully sent.
if (node_ptr->isStrictlyOrdered()) {
- node_ptr->isLoad ? ++numSOLoads : ++numSOStores;
+ node_ptr->isLoad() ? ++numSOLoads : ++numSOStores;
DPRINTF(TraceCPUData, "Skipping strictly ordered request %lli.\n",
node_ptr->seqNum);
return nullptr;
req->setPC(node_ptr->pc);
PacketPtr pkt;
uint8_t* pkt_data = new uint8_t[req->getSize()];
- if (node_ptr->isLoad) {
+ if (node_ptr->isLoad()) {
pkt = Packet::createRead(req);
} else {
pkt = Packet::createWrite(req);
// If this is the first attempt, print a debug message to indicate this.
if (first) {
DPRINTFR(TraceCPUData, "\t\tseq. num %lli(%s) with rob num %lli is now"
- " dependency free.\n", node_ptr->seqNum,
- node_ptr->isLoad ? "L" : (node_ptr->isStore ? "S" : "C"),
+ " dependency free.\n", node_ptr->seqNum, node_ptr->typeToStr(),
node_ptr->robNum);
}
auto graph_itr = depGraph.find(itr->seqNum);
GraphNode* node_ptr M5_VAR_USED = graph_itr->second;
DPRINTFR(TraceCPUData, "\t%lld(%s), %lld\n", itr->seqNum,
- node_ptr->isLoad ? "L" : (node_ptr->isStore ? "S" : "C"),
- itr->execTick);
+ node_ptr->typeToStr(), itr->execTick);
itr++;
}
}
oldestInFlightRobNum = inFlightNodes.begin()->second;
// Occupy Load/Store Buffer entry for the issued node if applicable
- if (new_node->isLoad) {
+ if (new_node->isLoad()) {
++numInFlightLoads;
- } else if (new_node->isStore) {
+ } else if (new_node->isStore()) {
++numInFlightStores;
} // else if it is a non load/store node, no buffer entry is occupied
// freed. But it occupies an entry in the Store Buffer until its response
// is received. A load is considered complete when a response is received,
// thus both ROB and Load Buffer entries can be released.
- if (done_node->isLoad) {
+ if (done_node->isLoad()) {
assert(numInFlightLoads != 0);
--numInFlightLoads;
}
// entry on response. For writes which are strictly ordered, for e.g.
// writes to device registers, we do that within release() which is called
// when node is executed and taken off from readyList.
- if (done_node->isStore && done_node->isStrictlyOrdered()) {
+ if (done_node->isStore() && done_node->isStrictlyOrdered()) {
releaseStoreBuffer();
}
}
if (num_in_flight_nodes >= sizeROB) {
return false;
}
- if (new_node->isLoad && numInFlightLoads >= sizeLoadBuffer) {
+ if (new_node->isLoad() && numInFlightLoads >= sizeLoadBuffer) {
return false;
}
- if (new_node->isStore && numInFlightStores >= sizeStoreBuffer) {
+ if (new_node->isStore() && numInFlightStores >= sizeStoreBuffer) {
return false;
}
return true;
if (trace.read(pkt_msg)) {
// Required fields
element->seqNum = pkt_msg.seq_num();
- element->isLoad = pkt_msg.load();
- element->isStore = pkt_msg.store();
+ element->type = pkt_msg.type();
element->compDelay = pkt_msg.comp_delay();
// Repeated field robDepList
TraceCPU::ElasticDataGen::GraphNode::writeElementAsTrace() const
{
DPRINTFR(TraceCPUData, "%lli", seqNum);
- DPRINTFR(TraceCPUData, ",%s", (isLoad ? "True" : "False"));
- DPRINTFR(TraceCPUData, ",%s", (isStore ? "True" : "False"));
- if (isLoad || isStore) {
+ DPRINTFR(TraceCPUData, ",%s", typeToStr());
+ if (isLoad() || isStore()) {
DPRINTFR(TraceCPUData, ",%i", addr);
DPRINTFR(TraceCPUData, ",%i", size);
DPRINTFR(TraceCPUData, ",%i", flags);
DPRINTFR(TraceCPUData, "\n");
}
+std::string
+TraceCPU::ElasticDataGen::GraphNode::typeToStr() const
+{
+ return Record::RecordType_Name(type);
+}
+
TraceCPU::FixedRetryGen::InputStream::InputStream(const std::string& filename)
: trace(filename)
{
/** Node ROB number type. */
typedef uint64_t NodeRobNum;
+ typedef ProtoMessage::InstDepRecord::RecordType RecordType;
+ typedef ProtoMessage::InstDepRecord Record;
+
/**
* The struct GraphNode stores an instruction in the trace file. The
* format of the trace file favours constructing a dependency graph of
/** ROB occupancy number */
NodeRobNum robNum;
- /** If instruction is a load */
- bool isLoad;
-
- /** If instruction is a store */
- bool isStore;
+ /** Type of the node corresponding to the instruction modelled by it */
+ RecordType type;
/** The address for the request if any */
Addr addr;
*/
std::vector<GraphNode *> dependents;
+ /** Is the node a load */
+ bool isLoad() const { return (type == Record::LOAD); }
+
+ /** Is the node a store */
+ bool isStore() const { return (type == Record::STORE); }
+
+ /** Is the node a compute (non load/store) node */
+ bool isComp() const { return (type == Record::COMP); }
+
/** Initialize register dependency array to all zeroes */
void clearRegDep();
* TraceCPUData.
*/
void writeElementAsTrace() const;
+
+ /** Return string specifying the type of the node */
+ std::string typeToStr() const;
};
/** Struct to store a ready-to-execute node and its execution tick. */
}
// Packet to encapsulate an instruction in the o3cpu data dependency trace.
-// The required fields include the instruction sequence number, whether it
-// is a load, and whether it is a store. The request related fields are
-// optional, namely address, size and flags. These exist only if the
-// instruction is a load or store. The dependency related information includes
-// a repeated field for order dependencies, a repeated field for register
-// dependencies and the computational delay with respect to the dependency
-// that completed last. A weight field is used to account for committed
-// instructions that were filtered out before writing the trace and is used
-// to estimate ROB occupancy during replay. An optional field is provided for
-// the instruction PC.
+// The required fields include the instruction sequence number and the type
+// of the record associated with the instruction e.g. load. The request related
+// fields are optional, namely address, size and flags. The dependency related
+// information includes a repeated field for order dependencies and register
+// dependencies for loads, stores and comp records. There is a field for the
+// computational delay with respect to the dependency that completed last. A
+// weight field is used to account for committed instruction that were
+// filtered out before writing the trace and is used to estimate ROB
+// occupancy during replay. An optional field is provided for the instruction
+// PC.
message InstDepRecord {
+ enum RecordType {
+ INVALID = 0;
+ LOAD = 1;
+ STORE = 2;
+ COMP = 3;
+ }
required uint64 seq_num = 1;
- required bool load = 2;
- required bool store = 3;
- optional uint64 addr = 4;
- optional uint32 size = 5;
- optional uint32 flags = 6;
- repeated uint64 rob_dep = 7;
- required uint64 comp_delay = 8;
- repeated uint64 reg_dep = 9;
- optional uint32 weight = 10;
- optional uint64 pc = 11;
-}
+ required RecordType type = 2 [default = INVALID];
+ optional uint64 addr = 3;
+ optional uint32 size = 4;
+ optional uint32 flags = 5;
+ repeated uint64 rob_dep = 6;
+ required uint64 comp_delay = 7;
+ repeated uint64 reg_dep = 8;
+ optional uint32 weight = 9;
+ optional uint64 pc = 10;
+}
\ No newline at end of file
# graph to ASCII format.
#
# The ASCII trace format uses one line per instruction with the format
-# instruction sequence number, (optional) pc, (optional) weight, load, store,
+# instruction sequence number, (optional) pc, (optional) weight, type
# (optional) flags, (optional) addr, (optional) size, comp delay,
# (repeated) order dependencies comma-separated, and (repeated) register
# dependencies comma-separated.
#
# examples:
-# seq_num,[pc],[weight,]load,store,[address,size,flags,]comp_delay:[rob_dep]:
+# seq_num,[pc],[weight,]type,[address,size,flags,]comp_delay:[rob_dep]:
# [reg_dep]
-# 1,1,False,False,8500::
-# 2,1,False,False,1000:,1:
-# 3,1,True,False,831248,4,74,500:,2:
-# 4,1,False,False,0:,2:
-# 5,1,False,False,500::,4
-# 6,1,False,True,831248,4,74,1000:,3:,4,5
+# 1,35652,1,COMP,8500::
+# 2,35656,1,COMP,0:,1:
+# 3,35660,1,LOAD,1748752,4,74,500:,2:
+# 4,35660,1,COMP,0:,3:
+# 5,35664,1,COMP,3000::,4
+# 6,35666,1,STORE,1748752,4,74,1000:,3:,4,5
+# 7,35666,1,COMP,3000::,4
+# 8,35670,1,STORE,1748748,4,74,0:,6,3:,7
+# 9,35670,1,COMP,500::,7
import protolib
import sys
print "Parsing packets"
+ print "Creating enum value,name lookup from proto"
+ enumNames = {}
+ desc = inst_dep_record_pb2.InstDepRecord.DESCRIPTOR
+ for namestr, valdesc in desc.enum_values_by_name.items():
+ print '\t', valdesc.number, namestr
+ enumNames[valdesc.number] = namestr
+
num_packets = 0
num_regdeps = 0
num_robdeps = 0
ascii_out.write(',%s' % (packet.weight))
else:
ascii_out.write(',1')
- # Write to file if it is a load and if it is a store
- ascii_out.write(',%s,%s' % (packet.load, packet.store))
+ # Write to file the type of the record
+ try:
+ ascii_out.write(',%s' % enumNames[packet.type])
+ except KeyError:
+ print "Seq. num", packet.seq_num, "has unsupported type", \
+ packet.type
+ exit(-1)
+
# Write to file if it has the optional fields addr, size, flags
if packet.HasField('addr'):
# graph to protobuf format.
#
# The ASCII trace format uses one line per instruction with the format
-# instruction sequence number, (optional) pc, (optional) weight, load, store,
+# instruction sequence number, (optional) pc, (optional) weight, type,
# (optional) flags, (optional) addr, (optional) size, comp delay,
# (repeated) order dependencies comma-separated, and (repeated) register
# dependencies comma-separated.
#
# examples:
-# seq_num,[pc],[weight,]load,store,[address,size,flags,]comp_delay:[rob_dep]:
+# seq_num,[pc],[weight,]type,[address,size,flags,]comp_delay:[rob_dep]:
# [reg_dep]
-# 1,1,False,False,8500::
-# 2,1,False,False,1000:,1:
-# 3,1,True,False,831248,4,74,500:,2:
-# 4,1,False,False,0:,2:
-# 5,1,False,False,500::,4
-# 6,1,False,True,831248,4,74,1000:,3:,4,5
+# 1,35652,1,COMP,8500::
+# 2,35656,1,COMP,0:,1:
+# 3,35660,1,LOAD,1748752,4,74,500:,2:
+# 4,35660,1,COMP,0:,3:
+# 5,35664,1,COMP,3000::,4
+# 6,35666,1,STORE,1748752,4,74,1000:,3:,4,5
+# 7,35666,1,COMP,3000::,4
+# 8,35670,1,STORE,1748748,4,74,0:,6,3:,7
+# 9,35670,1,COMP,500::,7
import protolib
import sys
print "Failed to import proto definitions"
exit(-1)
+DepRecord = inst_dep_record_pb2.InstDepRecord
+
def main():
if len(sys.argv) != 3:
print "Usage: ", sys.argv[0], " <ASCII input> <protobuf output>"
header.window_size = 120
protolib.encodeMessage(proto_out, header)
+ print "Creating enum name,value lookup from proto"
+ enumValues = {}
+ for namestr, valdesc in DepRecord.DESCRIPTOR.enum_values_by_name.items():
+ print '\t', namestr, valdesc.number
+ enumValues[namestr] = valdesc.number
+
num_records = 0
# For each line in the ASCII trace, create a packet message and
# write it to the encoded output
for line in ascii_in:
inst_info_str, rob_dep_str, reg_dep_str = (line.strip()).split(':')
inst_info_list = inst_info_str.split(',')
- dep_record = inst_dep_record_pb2.InstDepRecord()
+ dep_record = DepRecord()
dep_record.seq_num = long(inst_info_list[0])
dep_record.pc = long(inst_info_list[1])
dep_record.weight = long(inst_info_list[2])
- dep_record.load = True if inst_info_list[3] == 'True' else False
- dep_record.store = True if inst_info_list[4] == 'True' else False
+ # If the type is not one of the enum values, it should be a key error
+ try:
+ dep_record.type = enumValues[inst_info_list[3]]
+ except KeyError:
+ print "Seq. num", dep_record.seq_num, "has unsupported type", \
+ inst_info_list[3]
+ exit(-1)
+
+ if dep_record.type == DepRecord.INVALID:
+ print "Seq. num", dep_record.seq_num, "is of INVALID type"
+ exit(-1)
# If the instruction is a load or store record the addr, size flags
# in addition to recording the computation delay
- if dep_record.load or dep_record.store:
- addr, size, flags, comp_delay = inst_info_list[5:9]
+ if dep_record.type in [DepRecord.LOAD, DepRecord.STORE]:
+ addr, size, flags, comp_delay = inst_info_list[4:8]
dep_record.addr = long(addr)
dep_record.size = int(size)
dep_record.flags = int(flags)
dep_record.comp_delay = long(comp_delay)
- elif not dep_record.load and not dep_record.store:
+ else:
comp_delay = inst_info_list[4]
dep_record.comp_delay = long(comp_delay)
- else:
- print "Fatal:", seq_num, "is both load and store"
- exit(1)
# Parse the register and order dependencies both of which are
# repeated fields. An empty list is valid.