From 0a9f4b950fb52db3951ad1f7aafc674b505d2679 Mon Sep 17 00:00:00 2001 From: Brad Beckmann Date: Fri, 6 Apr 2012 13:47:06 -0700 Subject: 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. --- src/cpu/testers/rubytest/Check.cc | 31 ++++++------ src/cpu/testers/rubytest/Check.hh | 7 +-- src/cpu/testers/rubytest/CheckTable.cc | 7 +-- src/cpu/testers/rubytest/CheckTable.hh | 5 +- src/cpu/testers/rubytest/RubyTester.cc | 86 ++++++++++++++++++++++++++-------- src/cpu/testers/rubytest/RubyTester.hh | 32 +++++++++++-- src/cpu/testers/rubytest/RubyTester.py | 4 +- 7 files changed, 124 insertions(+), 48 deletions(-) (limited to 'src/cpu/testers/rubytest') diff --git a/src/cpu/testers/rubytest/Check.cc b/src/cpu/testers/rubytest/Check.cc index 2444a14ab..6f119af06 100644 --- a/src/cpu/testers/rubytest/Check.cc +++ b/src/cpu/testers/rubytest/Check.cc @@ -36,8 +36,9 @@ typedef RubyTester::SenderState SenderState; Check::Check(const Address& address, const Address& pc, - int _num_cpu_sequencers, RubyTester* _tester) - : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester) + int _num_writers, int _num_readers, RubyTester* _tester) + : m_num_writers(_num_writers), m_num_readers(_num_readers), + m_tester_ptr(_tester) { m_status = TesterStatus_Idle; @@ -80,9 +81,9 @@ Check::initiatePrefetch() { DPRINTF(RubyTest, "initiating prefetch\n"); - int index = random() % m_num_cpu_sequencers; + int index = random() % m_num_readers; RubyTester::CpuPort* port = - safe_cast(m_tester_ptr->getCpuPort(index)); + safe_cast(m_tester_ptr->getReadableCpuPort(index)); Request::Flags flags; flags.set(Request::PREFETCH); @@ -93,8 +94,8 @@ Check::initiatePrefetch() if ((random() & 0x7) != 0) { cmd = MemCmd::ReadReq; - // 50% chance that the request will be an instruction fetch - if ((random() & 0x1) == 0) { + // if necessary, make the request an instruction fetch + if (port->type == RubyTester::CpuPort::InstOnly) { flags.set(Request::INST_FETCH); } } else { @@ -135,9 +136,9 @@ Check::initiateFlush() DPRINTF(RubyTest, "initiating Flush\n"); - int index = random() % m_num_cpu_sequencers; + int index = random() % m_num_writers; RubyTester::CpuPort* port = - safe_cast(m_tester_ptr->getCpuPort(index)); + safe_cast(m_tester_ptr->getWritableCpuPort(index)); Request::Flags flags; @@ -166,9 +167,9 @@ Check::initiateAction() DPRINTF(RubyTest, "initiating Action\n"); assert(m_status == TesterStatus_Idle); - int index = random() % m_num_cpu_sequencers; + int index = random() % m_num_writers; RubyTester::CpuPort* port = - safe_cast(m_tester_ptr->getCpuPort(index)); + safe_cast(m_tester_ptr->getWritableCpuPort(index)); Request::Flags flags; @@ -231,14 +232,14 @@ Check::initiateCheck() DPRINTF(RubyTest, "Initiating Check\n"); assert(m_status == TesterStatus_Ready); - int index = random() % m_num_cpu_sequencers; + int index = random() % m_num_readers; RubyTester::CpuPort* port = - safe_cast(m_tester_ptr->getCpuPort(index)); + safe_cast(m_tester_ptr->getReadableCpuPort(index)); Request::Flags flags; - // 50% chance that the request will be an instruction fetch - if ((random() & 0x1) == 0) { + // If necessary, make the request an instruction fetch + if (port->type == RubyTester::CpuPort::InstOnly) { flags.set(Request::INST_FETCH); } @@ -363,7 +364,7 @@ Check::pickInitiatingNode() { assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready); 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); m_store_count = 0; } diff --git a/src/cpu/testers/rubytest/Check.hh b/src/cpu/testers/rubytest/Check.hh index db1485548..1d84b446b 100644 --- a/src/cpu/testers/rubytest/Check.hh +++ b/src/cpu/testers/rubytest/Check.hh @@ -46,8 +46,8 @@ const int CHECK_SIZE = (1 << CHECK_SIZE_BITS); class Check { public: - Check(const Address& address, const Address& pc, int _num_cpu_sequencer, - RubyTester* _tester); + Check(const Address& address, const Address& pc, int _num_writers, + int _num_readers, RubyTester* _tester); void initiate(); // Does Action or Check or nether void performCallback(NodeID proc, SubBlock* data); @@ -74,7 +74,8 @@ class Check Address m_address; Address m_pc; RubyAccessMode m_access_mode; - int m_num_cpu_sequencers; + int m_num_writers; + int m_num_readers; RubyTester* m_tester_ptr; }; diff --git a/src/cpu/testers/rubytest/CheckTable.cc b/src/cpu/testers/rubytest/CheckTable.cc index f3335b48c..b4860b62b 100644 --- a/src/cpu/testers/rubytest/CheckTable.cc +++ b/src/cpu/testers/rubytest/CheckTable.cc @@ -32,8 +32,9 @@ #include "cpu/testers/rubytest/CheckTable.hh" #include "debug/RubyTest.hh" -CheckTable::CheckTable(int _num_cpu_sequencers, RubyTester* _tester) - : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester) +CheckTable::CheckTable(int _num_writers, int _num_readers, RubyTester* _tester) + : m_num_writers(_num_writers), m_num_readers(_num_readers), + m_tester_ptr(_tester) { physical_address_t physical = 0; Address address; @@ -94,7 +95,7 @@ CheckTable::addCheck(const Address& address) } 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++) { // Insert it once per byte m_lookup_map[Address(address.getAddress() + i)] = check_ptr; diff --git a/src/cpu/testers/rubytest/CheckTable.hh b/src/cpu/testers/rubytest/CheckTable.hh index 5a4ead337..35ea7440a 100644 --- a/src/cpu/testers/rubytest/CheckTable.hh +++ b/src/cpu/testers/rubytest/CheckTable.hh @@ -43,7 +43,7 @@ class RubyTester; class CheckTable { public: - CheckTable(int _num_cpu_sequencers, RubyTester* _tester); + CheckTable(int _num_writers, int _num_readers, RubyTester* _tester); ~CheckTable(); Check* getRandomCheck(); @@ -66,7 +66,8 @@ class CheckTable std::vector m_check_vector; m5::hash_map m_lookup_map; - int m_num_cpu_sequencers; + int m_num_writers; + int m_num_readers; RubyTester* m_tester_ptr; }; diff --git a/src/cpu/testers/rubytest/RubyTester.cc b/src/cpu/testers/rubytest/RubyTester.cc index e1942cf61..7cc892166 100644 --- a/src/cpu/testers/rubytest/RubyTester.cc +++ b/src/cpu/testers/rubytest/RubyTester.cc @@ -53,17 +53,37 @@ RubyTester::RubyTester(const Params *p) : MemObject(p), checkStartEvent(this), _masterId(p->system->getMasterId(name())), + m_num_cpus(p->num_cpus), m_checks_to_complete(p->checks_to_complete), m_deadlock_threshold(p->deadlock_threshold), 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; - // create the ports - for (int i = 0; i < p->port_cpuPort_connection_count; ++i) { - ports.push_back(new CpuPort(csprintf("%s-port%d", name(), i), - this, i)); + // + // Create the requested inst and data ports and place them on the + // appropriate read and write port lists. The reason for the subtle + // 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 @@ -73,37 +93,57 @@ RubyTester::RubyTester(const Params *p) RubyTester::~RubyTester() { delete m_checkTable_ptr; - for (int i = 0; i < ports.size(); i++) - delete ports[i]; + // Only delete the readPorts since the writePorts are just a subset + for (int i = 0; i < readPorts.size(); i++) + delete readPorts[i]; } void 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++) { 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 & 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 return MemObject::getMasterPort(if_name, idx); } else { - if (idx >= static_cast(ports.size())) { - panic("RubyTester::getMasterPort: unknown index %d\n", idx); + if (if_name == "cpuInstPort") { + 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(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* -RubyTester::getCpuPort(int idx) +RubyTester::getReadableCpuPort(int idx) +{ + assert(idx >= 0 && idx < readPorts.size()); + + return readPorts[idx]; +} + +MasterPort* +RubyTester::getWritableCpuPort(int idx) { - assert(idx >= 0 && idx < ports.size()); + assert(idx >= 0 && idx < writePorts.size()); - return ports[idx]; + return writePorts[idx]; } void diff --git a/src/cpu/testers/rubytest/RubyTester.hh b/src/cpu/testers/rubytest/RubyTester.hh index b24dddd83..82698f201 100644 --- a/src/cpu/testers/rubytest/RubyTester.hh +++ b/src/cpu/testers/rubytest/RubyTester.hh @@ -51,11 +51,28 @@ class RubyTester : public MemObject RubyTester *tester; 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; + Type type; protected: virtual bool recvTiming(PacketPtr pkt); @@ -90,7 +107,8 @@ class RubyTester : public MemObject virtual MasterPort &getMasterPort(const std::string &if_name, int idx = -1); - MasterPort* getCpuPort(int idx); + MasterPort* getReadableCpuPort(int idx); + MasterPort* getWritableCpuPort(int idx); virtual void init(); @@ -136,13 +154,17 @@ class RubyTester : public MemObject CheckTable* m_checkTable_ptr; std::vector