354 lines
11 KiB
C++
354 lines
11 KiB
C++
/*
|
|
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Description: see RaceyPseudoThread.h
|
|
*/
|
|
|
|
#include "mem/ruby/tester/RaceyPseudoThread.hh"
|
|
#include "mem/ruby/tester/RaceyDriver.hh"
|
|
#include "gzstream.hh"
|
|
|
|
RaceyPseudoThread::RaceyPseudoThread(NodeID id, RaceyDriver& driver)
|
|
: m_driver(driver), m_proc_id(id) {
|
|
|
|
resetIC(); // IC contains the committed instruction number
|
|
m_last_progress = 0;
|
|
m_done = false;
|
|
m_stop = 0;
|
|
m_driver.eventQueue->scheduleEvent(this, 1);
|
|
}
|
|
|
|
RaceyPseudoThread::~RaceyPseudoThread() {
|
|
}
|
|
|
|
void RaceyPseudoThread::checkForDeadlock() {
|
|
Time current_time = m_driver.eventQueue->getTime();
|
|
if(!m_done && (current_time - m_last_progress) > g_DEADLOCK_THRESHOLD) {
|
|
WARN_EXPR(m_proc_id);
|
|
WARN_EXPR(m_ic_counter);
|
|
WARN_EXPR(m_last_progress);
|
|
ERROR_MSG("Deadlock detected.");
|
|
}
|
|
}
|
|
|
|
void RaceyPseudoThread::performCallback(int proc, Address address, uint8_t * data ) {
|
|
assert(proc == m_proc_id);
|
|
|
|
DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
|
|
DEBUG_EXPR(TESTER_COMP, LowPrio, address);
|
|
|
|
|
|
m_last_progress = m_driver.eventQueue->getTime();
|
|
|
|
if(m_read) {
|
|
int b0, b1, b2, b3;
|
|
b0 = data[0]; b0 <<= 0;
|
|
b1 = data[1]; b1 <<= 8;
|
|
b2 = data[2]; b2 <<= 16;
|
|
b3 = data[3]; b3 <<= 24;
|
|
m_value = b0 | b1 | b2 | b3;
|
|
} else {
|
|
char b0, b1, b2, b3;
|
|
b0 = (m_value>>0)&0xFF; data[0] = b0;
|
|
b1 = (m_value>>8)&0xFF; data[1] = b1;
|
|
b2 = (m_value>>16)&0xFF; data[2] = b2;
|
|
b3 = (m_value>>24)&0xFF; data[3] = b3;
|
|
}
|
|
|
|
// schedule wakeup for next requests in next cycle
|
|
m_driver.eventQueue->scheduleEvent(this, 1);
|
|
|
|
// new instruction
|
|
m_ic_counter++;
|
|
|
|
}
|
|
|
|
void RaceyPseudoThread::wakeup() {
|
|
// for debug
|
|
if(m_stop != 0) {
|
|
cout << m_proc_id << " " << m_stop << ((m_read)? " read ":" written ") << m_value;
|
|
if(0) cout << " [" << m_driver.eventQueue->getTime() << "]";
|
|
cout << endl;
|
|
}
|
|
|
|
assert(!m_done);
|
|
|
|
// Note, this function can not have ANY local variable!
|
|
|
|
switch(m_stop) {
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
goto L1;
|
|
case 2:
|
|
goto L2;
|
|
case 3:
|
|
goto L3;
|
|
case 4:
|
|
goto L4;
|
|
case 5:
|
|
goto L5;
|
|
case 6:
|
|
goto L6;
|
|
case 7:
|
|
goto L7;
|
|
case 8:
|
|
goto L8;
|
|
case 9:
|
|
goto L9;
|
|
case 10:
|
|
goto L10;
|
|
default:
|
|
WARN_EXPR(m_stop);
|
|
ERROR_MSG("RaceyPseudoThread: Bad context point!");
|
|
}
|
|
|
|
//
|
|
// initialization
|
|
//
|
|
if(m_proc_id == 0) {
|
|
for(m_looper = 0; m_looper < m_driver.m_num_procs; m_looper++) {
|
|
store_sig(m_looper, m_looper+1);
|
|
m_stop = 6; return;
|
|
L6: {};
|
|
}
|
|
for(m_looper = 0; m_looper < M_ELEM; m_looper++) {
|
|
store_m(m_looper, M_ELEM-m_looper);
|
|
m_stop = 7; return;
|
|
L7: {};
|
|
}
|
|
|
|
// init done
|
|
m_initialized = true;
|
|
} else {
|
|
// other processors
|
|
if(!m_driver.Thread0Initialized()) {
|
|
// wait for processors 0
|
|
m_driver.eventQueue->scheduleEvent(this, 1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
cout << "Thread " << m_proc_id << " started in parallel phase" << endl;
|
|
|
|
//
|
|
// main thread body
|
|
//
|
|
for(m_looper = 0 ; m_looper < m_driver.m_tester_length; m_looper++) {
|
|
/* m_value = */ load_sig(m_proc_id);
|
|
m_stop = 1; return;
|
|
L1: {};
|
|
m_num = m_value;
|
|
m_index1 = m_num%M_ELEM;
|
|
/* m_value = */ load_m(m_index1);
|
|
m_stop = 2; return;
|
|
L2: {};
|
|
m_num = mix(m_num, m_value);
|
|
m_index2 = m_num%M_ELEM;
|
|
/* m_value = */ load_m(m_index2);
|
|
m_stop = 3; return;
|
|
L3: {};
|
|
m_num = mix(m_num, m_value);
|
|
store_m(m_index2, m_num);
|
|
m_stop = 4; return;
|
|
L4: {};
|
|
store_sig(m_proc_id, m_num);
|
|
m_stop = 5; return;
|
|
L5: {};
|
|
} // end for
|
|
|
|
//
|
|
// compute final sig
|
|
//
|
|
if(m_proc_id == 0) {
|
|
// wait for other threads
|
|
while (m_driver.runningThreads() > 1) {
|
|
m_driver.registerThread0Wakeup();
|
|
m_stop = 10; return;
|
|
L10: {};
|
|
}
|
|
|
|
/* m_value = */ load_sig(0);
|
|
m_stop = 8; return;
|
|
L8: {};
|
|
m_final_sig = m_value;
|
|
for(m_looper = 1; m_looper < m_driver.m_num_procs; m_looper++) {
|
|
/* m_value = */ load_sig(m_looper);
|
|
m_stop = 9; return;
|
|
L9: {};
|
|
m_final_sig = mix(m_value, m_final_sig);
|
|
}
|
|
} // processors 0
|
|
|
|
// done
|
|
m_driver.joinThread();
|
|
m_done = true;
|
|
}
|
|
|
|
void RaceyPseudoThread::load_sig(unsigned index) {
|
|
cout << m_proc_id << " : load_sig " << index << endl;
|
|
|
|
m_read = true;
|
|
// timestamp, threadid, action, and logical address are used only by transactional memory, should be augmented
|
|
uint8_t * read_data = new uint8_t[4];
|
|
|
|
char name [] = "Sequencer_";
|
|
char port_name [13];
|
|
sprintf(port_name, "%s%d", name, m_proc_id);
|
|
|
|
// pc is zero, problem?
|
|
int64_t request_id = libruby_issue_request(libruby_get_port_by_name(port_name), RubyRequest(sig(index), read_data, 4, 0, RubyRequestType_LD, RubyAccessMode_User));
|
|
|
|
ASSERT(m_driver.requests.find(request_id) == m_driver.requests.end());
|
|
|
|
struct address_data request_data;
|
|
request_data.address = Address(sig(index));
|
|
request_data.data = read_data;
|
|
m_driver.requests.insert(make_pair(request_id, make_pair(m_proc_id, request_data)));
|
|
|
|
/*sequencer()->makeRequest(CacheMsg(Address(sig(index)), Address(sig(index)), CacheRequestType_LD,
|
|
Address(physical_address_t(1)),
|
|
AccessModeType_UserMode, 4,
|
|
PrefetchBit_No, 0, Address(0),
|
|
0, 0 , false)); */
|
|
}
|
|
|
|
void RaceyPseudoThread::load_m(unsigned index) {
|
|
// cout << m_proc_id << " : load_m " << index << endl;
|
|
|
|
m_read = true;
|
|
uint8_t * read_data = new uint8_t[4];
|
|
|
|
char name [] = "Sequencer_";
|
|
char port_name [13];
|
|
sprintf(port_name, "%s%d", name, m_proc_id);
|
|
|
|
// pc is zero, problem?
|
|
int64_t request_id = libruby_issue_request(libruby_get_port_by_name(port_name), RubyRequest(m(index), read_data, 4, 0, RubyRequestType_LD, RubyAccessMode_User));
|
|
|
|
ASSERT(m_driver.requests.find(request_id) == m_driver.requests.end());
|
|
|
|
struct address_data request_data;
|
|
request_data.address = Address(m(index));
|
|
request_data.data = read_data;
|
|
m_driver.requests.insert(make_pair(request_id, make_pair(m_proc_id, request_data)));
|
|
|
|
/*sequencer()->makeRequest(CacheMsg(Address(m(index)), Address(m(index)), CacheRequestType_LD,
|
|
Address(physical_address_t(1)),
|
|
AccessModeType_UserMode, 4,
|
|
PrefetchBit_No, 0, Address(0),
|
|
0, 0, false)); */
|
|
}
|
|
|
|
void RaceyPseudoThread::store_sig(unsigned index, unsigned value) {
|
|
cout << m_proc_id << " : store_sig " << index << " " << value << endl;
|
|
|
|
m_read = false;
|
|
m_value = value;
|
|
uint8_t * write_data = new uint8_t[4];
|
|
|
|
|
|
memcpy(write_data, &value, 4);
|
|
|
|
char name [] = "Sequencer_";
|
|
char port_name [13];
|
|
sprintf(port_name, "%s%d", name, m_proc_id);
|
|
|
|
// pc is zero, problem?
|
|
int64_t request_id = libruby_issue_request(libruby_get_port_by_name(port_name), RubyRequest(sig(index), write_data, 4, 0, RubyRequestType_ST, RubyAccessMode_User));
|
|
|
|
ASSERT(m_driver.requests.find(request_id) == m_driver.requests.end());
|
|
|
|
struct address_data request_data;
|
|
request_data.address = Address(sig(index));
|
|
request_data.data = write_data;
|
|
m_driver.requests.insert(make_pair(request_id, make_pair(m_proc_id, request_data)));
|
|
|
|
/*sequencer()->makeRequest(CacheMsg(Address(sig(index)), Address(sig(index)), CacheRequestType_ST,
|
|
Address(physical_address_t(1)),
|
|
AccessModeType_UserMode, 4,
|
|
PrefetchBit_No, 0, Address(0),
|
|
0, 0, false)); */
|
|
}
|
|
|
|
void RaceyPseudoThread::store_m(unsigned index, unsigned value) {
|
|
//cout << m_proc_id << " : store_m " << index << endl;
|
|
|
|
m_read = false;
|
|
m_value = value;
|
|
uint8_t * write_data = new uint8_t[4];
|
|
memcpy(write_data, &value, 4);
|
|
|
|
char name [] = "Sequencer_";
|
|
char port_name [13];
|
|
sprintf(port_name, "%s%d", name, m_proc_id);
|
|
|
|
// pc is zero, problem?
|
|
int64_t request_id = libruby_issue_request(libruby_get_port_by_name(port_name), RubyRequest(m(index), write_data, 4, 0, RubyRequestType_ST, RubyAccessMode_User));
|
|
|
|
ASSERT(m_driver.requests.find(request_id) == m_driver.requests.end());
|
|
|
|
struct address_data request_data;
|
|
request_data.address = Address(m(index));
|
|
request_data.data = write_data;
|
|
m_driver.requests.insert(make_pair(request_id, make_pair(m_proc_id, request_data)));
|
|
|
|
/*sequencer()->makeRequest(CacheMsg(Address(m(index)), Address(m(index)), CacheRequestType_ST,
|
|
Address(physical_address_t(1)),
|
|
AccessModeType_UserMode, 4,
|
|
PrefetchBit_No, 0, Address(0),
|
|
0, 0, false)); */
|
|
}
|
|
|
|
// Save and Load context of a thread
|
|
void RaceyPseudoThread::saveCPUStates(string filename) {
|
|
ogzstream out(filename.c_str());
|
|
out.write((char*)&m_looper, sizeof(int));
|
|
out.write((char*)&m_num, sizeof(unsigned));
|
|
out.write((char*)&m_index1, sizeof(unsigned));
|
|
out.write((char*)&m_index2, sizeof(unsigned));
|
|
out.write((char*)&m_stop, sizeof(unsigned));
|
|
out.close();
|
|
}
|
|
|
|
void RaceyPseudoThread::loadCPUStates(string filename) {
|
|
igzstream out(filename.c_str());
|
|
out.read((char*)&m_looper, sizeof(int));
|
|
out.read((char*)&m_num, sizeof(unsigned));
|
|
out.read((char*)&m_index1, sizeof(unsigned));
|
|
out.read((char*)&m_index2, sizeof(unsigned));
|
|
out.read((char*)&m_stop, sizeof(unsigned));
|
|
out.close();
|
|
}
|
|
|
|
void RaceyPseudoThread::print(ostream& out) const {
|
|
out << "[Racey Pseudo Thread: " << m_proc_id << "]" << endl;
|
|
}
|
|
|