rubytest: seperated read and write ports.
This patch allows the ruby tester to support protocols where the i-cache and d-cache are managed by seperate controllers.
This commit is contained in:
parent
b00949d88b
commit
0a9f4b950f
9 changed files with 134 additions and 51 deletions
|
@ -89,7 +89,8 @@ if buildEnv['PROTOCOL'] == 'MOESI_hammer':
|
||||||
|
|
||||||
tester = RubyTester(check_flush = check_flush,
|
tester = RubyTester(check_flush = check_flush,
|
||||||
checks_to_complete = options.checks,
|
checks_to_complete = options.checks,
|
||||||
wakeup_frequency = options.wakeup_freq)
|
wakeup_frequency = options.wakeup_freq,
|
||||||
|
num_cpus = options.num_cpus)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Create the M5 system. Note that the Memory Object isn't
|
# Create the M5 system. Note that the Memory Object isn't
|
||||||
|
@ -110,9 +111,12 @@ system.ruby.randomization = True
|
||||||
|
|
||||||
for ruby_port in system.ruby._cpu_ruby_ports:
|
for ruby_port in system.ruby._cpu_ruby_ports:
|
||||||
#
|
#
|
||||||
# Tie the ruby tester ports to the ruby cpu ports
|
# Tie the ruby tester ports to the ruby cpu read and write ports
|
||||||
#
|
#
|
||||||
tester.cpuPort = ruby_port.slave
|
if ruby_port.support_data_reqs:
|
||||||
|
tester.cpuDataPort = ruby_port.slave
|
||||||
|
if ruby_port.support_inst_reqs:
|
||||||
|
tester.cpuInstPort = ruby_port.slave
|
||||||
|
|
||||||
#
|
#
|
||||||
# Tell each sequencer this is the ruby tester so that it
|
# Tell each sequencer this is the ruby tester so that it
|
||||||
|
|
|
@ -36,8 +36,9 @@
|
||||||
typedef RubyTester::SenderState SenderState;
|
typedef RubyTester::SenderState SenderState;
|
||||||
|
|
||||||
Check::Check(const Address& address, const Address& pc,
|
Check::Check(const Address& address, const Address& pc,
|
||||||
int _num_cpu_sequencers, RubyTester* _tester)
|
int _num_writers, int _num_readers, RubyTester* _tester)
|
||||||
: m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
|
: m_num_writers(_num_writers), m_num_readers(_num_readers),
|
||||||
|
m_tester_ptr(_tester)
|
||||||
{
|
{
|
||||||
m_status = TesterStatus_Idle;
|
m_status = TesterStatus_Idle;
|
||||||
|
|
||||||
|
@ -80,9 +81,9 @@ Check::initiatePrefetch()
|
||||||
{
|
{
|
||||||
DPRINTF(RubyTest, "initiating prefetch\n");
|
DPRINTF(RubyTest, "initiating prefetch\n");
|
||||||
|
|
||||||
int index = random() % m_num_cpu_sequencers;
|
int index = random() % m_num_readers;
|
||||||
RubyTester::CpuPort* port =
|
RubyTester::CpuPort* port =
|
||||||
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
|
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getReadableCpuPort(index));
|
||||||
|
|
||||||
Request::Flags flags;
|
Request::Flags flags;
|
||||||
flags.set(Request::PREFETCH);
|
flags.set(Request::PREFETCH);
|
||||||
|
@ -93,8 +94,8 @@ Check::initiatePrefetch()
|
||||||
if ((random() & 0x7) != 0) {
|
if ((random() & 0x7) != 0) {
|
||||||
cmd = MemCmd::ReadReq;
|
cmd = MemCmd::ReadReq;
|
||||||
|
|
||||||
// 50% chance that the request will be an instruction fetch
|
// if necessary, make the request an instruction fetch
|
||||||
if ((random() & 0x1) == 0) {
|
if (port->type == RubyTester::CpuPort::InstOnly) {
|
||||||
flags.set(Request::INST_FETCH);
|
flags.set(Request::INST_FETCH);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,9 +136,9 @@ Check::initiateFlush()
|
||||||
|
|
||||||
DPRINTF(RubyTest, "initiating Flush\n");
|
DPRINTF(RubyTest, "initiating Flush\n");
|
||||||
|
|
||||||
int index = random() % m_num_cpu_sequencers;
|
int index = random() % m_num_writers;
|
||||||
RubyTester::CpuPort* port =
|
RubyTester::CpuPort* port =
|
||||||
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
|
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getWritableCpuPort(index));
|
||||||
|
|
||||||
Request::Flags flags;
|
Request::Flags flags;
|
||||||
|
|
||||||
|
@ -166,9 +167,9 @@ Check::initiateAction()
|
||||||
DPRINTF(RubyTest, "initiating Action\n");
|
DPRINTF(RubyTest, "initiating Action\n");
|
||||||
assert(m_status == TesterStatus_Idle);
|
assert(m_status == TesterStatus_Idle);
|
||||||
|
|
||||||
int index = random() % m_num_cpu_sequencers;
|
int index = random() % m_num_writers;
|
||||||
RubyTester::CpuPort* port =
|
RubyTester::CpuPort* port =
|
||||||
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
|
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getWritableCpuPort(index));
|
||||||
|
|
||||||
Request::Flags flags;
|
Request::Flags flags;
|
||||||
|
|
||||||
|
@ -231,14 +232,14 @@ Check::initiateCheck()
|
||||||
DPRINTF(RubyTest, "Initiating Check\n");
|
DPRINTF(RubyTest, "Initiating Check\n");
|
||||||
assert(m_status == TesterStatus_Ready);
|
assert(m_status == TesterStatus_Ready);
|
||||||
|
|
||||||
int index = random() % m_num_cpu_sequencers;
|
int index = random() % m_num_readers;
|
||||||
RubyTester::CpuPort* port =
|
RubyTester::CpuPort* port =
|
||||||
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
|
safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getReadableCpuPort(index));
|
||||||
|
|
||||||
Request::Flags flags;
|
Request::Flags flags;
|
||||||
|
|
||||||
// 50% chance that the request will be an instruction fetch
|
// If necessary, make the request an instruction fetch
|
||||||
if ((random() & 0x1) == 0) {
|
if (port->type == RubyTester::CpuPort::InstOnly) {
|
||||||
flags.set(Request::INST_FETCH);
|
flags.set(Request::INST_FETCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +364,7 @@ Check::pickInitiatingNode()
|
||||||
{
|
{
|
||||||
assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
|
assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
|
||||||
m_status = TesterStatus_Idle;
|
m_status = TesterStatus_Idle;
|
||||||
m_initiatingNode = (random() % m_num_cpu_sequencers);
|
m_initiatingNode = (random() % m_num_writers);
|
||||||
DPRINTF(RubyTest, "picked initiating node %d\n", m_initiatingNode);
|
DPRINTF(RubyTest, "picked initiating node %d\n", m_initiatingNode);
|
||||||
m_store_count = 0;
|
m_store_count = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,8 @@ const int CHECK_SIZE = (1 << CHECK_SIZE_BITS);
|
||||||
class Check
|
class Check
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Check(const Address& address, const Address& pc, int _num_cpu_sequencer,
|
Check(const Address& address, const Address& pc, int _num_writers,
|
||||||
RubyTester* _tester);
|
int _num_readers, RubyTester* _tester);
|
||||||
|
|
||||||
void initiate(); // Does Action or Check or nether
|
void initiate(); // Does Action or Check or nether
|
||||||
void performCallback(NodeID proc, SubBlock* data);
|
void performCallback(NodeID proc, SubBlock* data);
|
||||||
|
@ -74,7 +74,8 @@ class Check
|
||||||
Address m_address;
|
Address m_address;
|
||||||
Address m_pc;
|
Address m_pc;
|
||||||
RubyAccessMode m_access_mode;
|
RubyAccessMode m_access_mode;
|
||||||
int m_num_cpu_sequencers;
|
int m_num_writers;
|
||||||
|
int m_num_readers;
|
||||||
RubyTester* m_tester_ptr;
|
RubyTester* m_tester_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@
|
||||||
#include "cpu/testers/rubytest/CheckTable.hh"
|
#include "cpu/testers/rubytest/CheckTable.hh"
|
||||||
#include "debug/RubyTest.hh"
|
#include "debug/RubyTest.hh"
|
||||||
|
|
||||||
CheckTable::CheckTable(int _num_cpu_sequencers, RubyTester* _tester)
|
CheckTable::CheckTable(int _num_writers, int _num_readers, RubyTester* _tester)
|
||||||
: m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
|
: m_num_writers(_num_writers), m_num_readers(_num_readers),
|
||||||
|
m_tester_ptr(_tester)
|
||||||
{
|
{
|
||||||
physical_address_t physical = 0;
|
physical_address_t physical = 0;
|
||||||
Address address;
|
Address address;
|
||||||
|
@ -94,7 +95,7 @@ CheckTable::addCheck(const Address& address)
|
||||||
}
|
}
|
||||||
|
|
||||||
Check* check_ptr = new Check(address, Address(100 + m_check_vector.size()),
|
Check* check_ptr = new Check(address, Address(100 + m_check_vector.size()),
|
||||||
m_num_cpu_sequencers, m_tester_ptr);
|
m_num_writers, m_num_readers, m_tester_ptr);
|
||||||
for (int i = 0; i < CHECK_SIZE; i++) {
|
for (int i = 0; i < CHECK_SIZE; i++) {
|
||||||
// Insert it once per byte
|
// Insert it once per byte
|
||||||
m_lookup_map[Address(address.getAddress() + i)] = check_ptr;
|
m_lookup_map[Address(address.getAddress() + i)] = check_ptr;
|
||||||
|
|
|
@ -43,7 +43,7 @@ class RubyTester;
|
||||||
class CheckTable
|
class CheckTable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CheckTable(int _num_cpu_sequencers, RubyTester* _tester);
|
CheckTable(int _num_writers, int _num_readers, RubyTester* _tester);
|
||||||
~CheckTable();
|
~CheckTable();
|
||||||
|
|
||||||
Check* getRandomCheck();
|
Check* getRandomCheck();
|
||||||
|
@ -66,7 +66,8 @@ class CheckTable
|
||||||
std::vector<Check*> m_check_vector;
|
std::vector<Check*> m_check_vector;
|
||||||
m5::hash_map<Address, Check*> m_lookup_map;
|
m5::hash_map<Address, Check*> m_lookup_map;
|
||||||
|
|
||||||
int m_num_cpu_sequencers;
|
int m_num_writers;
|
||||||
|
int m_num_readers;
|
||||||
RubyTester* m_tester_ptr;
|
RubyTester* m_tester_ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,17 +53,37 @@
|
||||||
RubyTester::RubyTester(const Params *p)
|
RubyTester::RubyTester(const Params *p)
|
||||||
: MemObject(p), checkStartEvent(this),
|
: MemObject(p), checkStartEvent(this),
|
||||||
_masterId(p->system->getMasterId(name())),
|
_masterId(p->system->getMasterId(name())),
|
||||||
|
m_num_cpus(p->num_cpus),
|
||||||
m_checks_to_complete(p->checks_to_complete),
|
m_checks_to_complete(p->checks_to_complete),
|
||||||
m_deadlock_threshold(p->deadlock_threshold),
|
m_deadlock_threshold(p->deadlock_threshold),
|
||||||
m_wakeup_frequency(p->wakeup_frequency),
|
m_wakeup_frequency(p->wakeup_frequency),
|
||||||
m_check_flush(p->check_flush)
|
m_check_flush(p->check_flush),
|
||||||
|
m_num_inst_ports(p->port_cpuInstPort_connection_count)
|
||||||
{
|
{
|
||||||
m_checks_completed = 0;
|
m_checks_completed = 0;
|
||||||
|
|
||||||
// create the ports
|
//
|
||||||
for (int i = 0; i < p->port_cpuPort_connection_count; ++i) {
|
// Create the requested inst and data ports and place them on the
|
||||||
ports.push_back(new CpuPort(csprintf("%s-port%d", name(), i),
|
// appropriate read and write port lists. The reason for the subtle
|
||||||
this, i));
|
// difference between inst and data ports vs. read and write ports is
|
||||||
|
// from the tester's perspective, it only needs to know whether a port
|
||||||
|
// supports reads (checks) or writes (actions). Meanwhile, the protocol
|
||||||
|
// controllers have data ports (support read and writes) or inst ports
|
||||||
|
// (support only reads).
|
||||||
|
// Note: the inst ports are the lowest elements of the readPort vector,
|
||||||
|
// then the data ports are added to the readPort vector
|
||||||
|
//
|
||||||
|
for (int i = 0; i < p->port_cpuInstPort_connection_count; ++i) {
|
||||||
|
readPorts.push_back(new CpuPort(csprintf("%s-instPort%d", name(), i),
|
||||||
|
this, i,
|
||||||
|
RubyTester::CpuPort::InstOnly));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < p->port_cpuDataPort_connection_count; ++i) {
|
||||||
|
CpuPort *port = NULL;
|
||||||
|
port = new CpuPort(csprintf("%s-dataPort%d", name(), i), this, i,
|
||||||
|
RubyTester::CpuPort::DataOnly);
|
||||||
|
readPorts.push_back(port);
|
||||||
|
writePorts.push_back(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the check start event to the event queue
|
// add the check start event to the event queue
|
||||||
|
@ -73,37 +93,57 @@ RubyTester::RubyTester(const Params *p)
|
||||||
RubyTester::~RubyTester()
|
RubyTester::~RubyTester()
|
||||||
{
|
{
|
||||||
delete m_checkTable_ptr;
|
delete m_checkTable_ptr;
|
||||||
for (int i = 0; i < ports.size(); i++)
|
// Only delete the readPorts since the writePorts are just a subset
|
||||||
delete ports[i];
|
for (int i = 0; i < readPorts.size(); i++)
|
||||||
|
delete readPorts[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RubyTester::init()
|
RubyTester::init()
|
||||||
{
|
{
|
||||||
assert(ports.size() > 0);
|
assert(writePorts.size() > 0 && readPorts.size() > 0);
|
||||||
|
|
||||||
m_last_progress_vector.resize(ports.size());
|
m_last_progress_vector.resize(m_num_cpus);
|
||||||
for (int i = 0; i < m_last_progress_vector.size(); i++) {
|
for (int i = 0; i < m_last_progress_vector.size(); i++) {
|
||||||
m_last_progress_vector[i] = 0;
|
m_last_progress_vector[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_num_cpu_sequencers = ports.size();
|
m_num_writers = writePorts.size();
|
||||||
|
m_num_readers = readPorts.size();
|
||||||
|
|
||||||
m_checkTable_ptr = new CheckTable(m_num_cpu_sequencers, this);
|
m_checkTable_ptr = new CheckTable(m_num_writers, m_num_readers, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
MasterPort &
|
MasterPort &
|
||||||
RubyTester::getMasterPort(const std::string &if_name, int idx)
|
RubyTester::getMasterPort(const std::string &if_name, int idx)
|
||||||
{
|
{
|
||||||
if (if_name != "cpuPort") {
|
if (if_name != "cpuInstPort" && if_name != "cpuDataPort") {
|
||||||
// pass it along to our super class
|
// pass it along to our super class
|
||||||
return MemObject::getMasterPort(if_name, idx);
|
return MemObject::getMasterPort(if_name, idx);
|
||||||
} else {
|
} else {
|
||||||
if (idx >= static_cast<int>(ports.size())) {
|
if (if_name == "cpuInstPort") {
|
||||||
panic("RubyTester::getMasterPort: unknown index %d\n", idx);
|
printf("print getting inst port %d\n", idx);
|
||||||
|
if (idx > m_num_inst_ports) {
|
||||||
|
panic("RubyTester::getMasterPort: unknown inst port idx %d\n",
|
||||||
|
idx);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// inst ports directly map to the lowest readPort elements
|
||||||
|
//
|
||||||
|
return *readPorts[idx];
|
||||||
|
} else {
|
||||||
|
assert(if_name == "cpuDataPort");
|
||||||
|
//
|
||||||
|
// add the inst port offset to translate to the correct read port
|
||||||
|
// index
|
||||||
|
//
|
||||||
|
int read_idx = idx + m_num_inst_ports;
|
||||||
|
if (read_idx >= static_cast<int>(readPorts.size())) {
|
||||||
|
panic("RubyTester::getMasterPort: unknown data port idx %d\n",
|
||||||
|
idx);
|
||||||
|
}
|
||||||
|
return *readPorts[read_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return *ports[idx];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,11 +177,19 @@ RubyTester::CpuPort::recvTiming(PacketPtr pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
MasterPort*
|
MasterPort*
|
||||||
RubyTester::getCpuPort(int idx)
|
RubyTester::getReadableCpuPort(int idx)
|
||||||
{
|
{
|
||||||
assert(idx >= 0 && idx < ports.size());
|
assert(idx >= 0 && idx < readPorts.size());
|
||||||
|
|
||||||
return ports[idx];
|
return readPorts[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
MasterPort*
|
||||||
|
RubyTester::getWritableCpuPort(int idx)
|
||||||
|
{
|
||||||
|
assert(idx >= 0 && idx < writePorts.size());
|
||||||
|
|
||||||
|
return writePorts[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -51,11 +51,28 @@ class RubyTester : public MemObject
|
||||||
RubyTester *tester;
|
RubyTester *tester;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CpuPort(const std::string &_name, RubyTester *_tester, int _idx)
|
//
|
||||||
: MasterPort(_name, _tester), tester(_tester), idx(_idx)
|
// Currently, each instatiation of the RubyTester::CpuPort supports
|
||||||
|
// only instruction or data requests, not both. However, for those
|
||||||
|
// RubyPorts that support both types of requests, separate InstOnly
|
||||||
|
// and DataOnly CpuPorts will map to that RubyPort
|
||||||
|
//
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
// Port supports only instruction requests
|
||||||
|
InstOnly,
|
||||||
|
// Port supports only data requests
|
||||||
|
DataOnly
|
||||||
|
};
|
||||||
|
|
||||||
|
CpuPort(const std::string &_name, RubyTester *_tester, int _idx,
|
||||||
|
Type _type)
|
||||||
|
: MasterPort(_name, _tester), tester(_tester), idx(_idx),
|
||||||
|
type(_type)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
int idx;
|
int idx;
|
||||||
|
Type type;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool recvTiming(PacketPtr pkt);
|
virtual bool recvTiming(PacketPtr pkt);
|
||||||
|
@ -90,7 +107,8 @@ class RubyTester : public MemObject
|
||||||
virtual MasterPort &getMasterPort(const std::string &if_name,
|
virtual MasterPort &getMasterPort(const std::string &if_name,
|
||||||
int idx = -1);
|
int idx = -1);
|
||||||
|
|
||||||
MasterPort* getCpuPort(int idx);
|
MasterPort* getReadableCpuPort(int idx);
|
||||||
|
MasterPort* getWritableCpuPort(int idx);
|
||||||
|
|
||||||
virtual void init();
|
virtual void init();
|
||||||
|
|
||||||
|
@ -136,13 +154,17 @@ class RubyTester : public MemObject
|
||||||
CheckTable* m_checkTable_ptr;
|
CheckTable* m_checkTable_ptr;
|
||||||
std::vector<Time> m_last_progress_vector;
|
std::vector<Time> m_last_progress_vector;
|
||||||
|
|
||||||
|
int m_num_cpus;
|
||||||
uint64 m_checks_completed;
|
uint64 m_checks_completed;
|
||||||
std::vector<CpuPort*> ports;
|
std::vector<CpuPort*> writePorts;
|
||||||
|
std::vector<CpuPort*> readPorts;
|
||||||
uint64 m_checks_to_complete;
|
uint64 m_checks_to_complete;
|
||||||
int m_deadlock_threshold;
|
int m_deadlock_threshold;
|
||||||
int m_num_cpu_sequencers;
|
int m_num_writers;
|
||||||
|
int m_num_readers;
|
||||||
int m_wakeup_frequency;
|
int m_wakeup_frequency;
|
||||||
bool m_check_flush;
|
bool m_check_flush;
|
||||||
|
int m_num_inst_ports;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream&
|
inline std::ostream&
|
||||||
|
|
|
@ -32,7 +32,9 @@ from m5.proxy import *
|
||||||
|
|
||||||
class RubyTester(MemObject):
|
class RubyTester(MemObject):
|
||||||
type = 'RubyTester'
|
type = 'RubyTester'
|
||||||
cpuPort = VectorMasterPort("the cpu ports")
|
num_cpus = Param.Int("number of cpus / RubyPorts")
|
||||||
|
cpuDataPort = VectorMasterPort("the cpu data cache ports")
|
||||||
|
cpuInstPort = VectorMasterPort("the cpu inst cache ports")
|
||||||
checks_to_complete = Param.Int(100, "checks to complete")
|
checks_to_complete = Param.Int(100, "checks to complete")
|
||||||
deadlock_threshold = Param.Int(50000, "how often to check for deadlock")
|
deadlock_threshold = Param.Int(50000, "how often to check for deadlock")
|
||||||
wakeup_frequency = Param.Int(10, "number of cycles between wakeups")
|
wakeup_frequency = Param.Int(10, "number of cycles between wakeups")
|
||||||
|
|
|
@ -44,6 +44,9 @@ class RubyPort(MemObject):
|
||||||
"should the rubyport atomically update phys_mem")
|
"should the rubyport atomically update phys_mem")
|
||||||
ruby_system = Param.RubySystem("")
|
ruby_system = Param.RubySystem("")
|
||||||
system = Param.System(Parent.any, "system object")
|
system = Param.System(Parent.any, "system object")
|
||||||
|
support_data_reqs = Param.Bool(True, "data cache requests supported")
|
||||||
|
support_inst_reqs = Param.Bool(True, "inst cache requests supported")
|
||||||
|
|
||||||
|
|
||||||
class RubyPortProxy(RubyPort):
|
class RubyPortProxy(RubyPort):
|
||||||
type = 'RubyPortProxy'
|
type = 'RubyPortProxy'
|
||||||
|
|
Loading…
Reference in a new issue