Lot of changes:

* memory module parses 03 field and sets Program Counter (PC) to right value
* almost all RV32I instructions implemented
* added Trace module to mimic ARM ITM module
* added BusCtrl module as bus controler (very simple) to allow CPU & RISC_V_execute to access memory & peripherals
* lot of minor changes
This commit is contained in:
mariusmonton 2018-09-19 23:44:38 +02:00
parent 79cff335e3
commit 8dcbf09589
16 changed files with 382 additions and 42 deletions

2
.gitignore vendored
View File

@ -30,6 +30,8 @@
*.exe *.exe
*.out *.out
*.app *.app
*.elf
*.hex
Log.txt Log.txt
helper.ods helper.ods

50
inc/BusCtrl.h Normal file
View File

@ -0,0 +1,50 @@
/*!
\file Trace.h
\brief Basic TLM-2 Trace module
\author Màrius Montón
\date September 2018
*/
#ifndef __BUSCTRL_H__
#define __BUSCTRL_H__
#include <iostream>
#include <fstream>
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include "Log.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
#define TRACE_MEMORY_ADDRESS 0x40000000
class BusCtrl: sc_module {
public:
// TLM-2 socket, defaults to 32-bits wide, base protocol
tlm_utils::simple_target_socket<BusCtrl> cpu_instr_socket;
tlm_utils::simple_target_socket<BusCtrl> cpu_data_socket;
tlm_utils::simple_initiator_socket<BusCtrl> data_memory_socket;
tlm_utils::simple_initiator_socket<BusCtrl> trace_socket;
// Constructor
BusCtrl(sc_module_name name);
// TLM-2 blocking transport method
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay );
private:
Log *log;
};
#endif

View File

@ -37,7 +37,7 @@ public:
//sc_in<sc_signal<bool> > interrupt; //sc_in<sc_signal<bool> > interrupt;
CPU(sc_module_name name); CPU(sc_module_name name, uint32_t PC);
~CPU(); ~CPU();
RISC_V_execute *exec; RISC_V_execute *exec;

View File

@ -29,10 +29,10 @@ class Log {
public: public:
enum LogLevel{ enum LogLevel{
INFO=0, ERROR = 0,
DEBUG, DEBUG,
WARNING, WARNING,
ERROR INFO
} currentLogLevel; } currentLogLevel;

View File

@ -30,11 +30,14 @@ public:
// TLM-2 socket, defaults to 32-bits wide, base protocol // TLM-2 socket, defaults to 32-bits wide, base protocol
tlm_utils::simple_target_socket<Memory> socket; tlm_utils::simple_target_socket<Memory> socket;
enum { SIZE = 1024 }; enum { SIZE = 1024 * 1024 };
const sc_time LATENCY; const sc_time LATENCY;
Memory(sc_module_name name, string filename); Memory(sc_module_name name, string filename);
Memory(sc_module_name name, bool use_file); Memory(sc_module_name name, bool use_file);
virtual uint32_t getPCfromHEX();
// TLM-2 blocking transport method // TLM-2 blocking transport method
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ); virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay );
@ -53,8 +56,10 @@ public:
virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans); virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans);
private:
int mem[SIZE]; int mem[SIZE];
uint32_t program_counter;
/** /**
* Reads file and stores in Code Memory. Uses propietary file format * Reads file and stores in Code Memory. Uses propietary file format
* @brief Reads file and stores in Code Memory * @brief Reads file and stores in Code Memory

View File

@ -24,6 +24,73 @@ using namespace std;
class Registers { class Registers {
public: public:
enum {
x0 = 0,
x1 = 1,
x2,
x3,
x4,
x5,
x6,
x7,
x8,
x9,
x10,
x11,
x12,
x13,
x14,
x15,
x16,
x17,
x18,
x19,
x20,
x21,
x22,
x23,
x24,
x25,
x26,
x27,
x28,
x29,
x30,
x31,
zero = x0,
ra = x1,
sp = x2,
gp = x3,
tp = x4,
t0 = x5,
t1 = x6,
t2 = x7,
s0 = x8,
fp = x8,
s1 = x9,
a0 = x10,
a1 = x11,
a2 = x12,
a3 = x13,
a4 = x14,
a5 = x15,
a6 = x16,
a7 = x17,
s2 = x18,
s3 = x19,
s4 = x20,
s5 = x21,
s6 = x22,
s7 = x23,
s8 = x24,
s9 = x25,
s10 = x26,
s11 = x27,
t3 = x28,
t4 = x29,
t5 = x30,
t6 = x31
};
/** /**
* Default constructor * Default constructor
*/ */

37
inc/Trace.h Normal file
View File

@ -0,0 +1,37 @@
/*!
\file Trace.h
\brief Basic TLM-2 Trace module
\author Màrius Montón
\date September 2018
*/
#ifndef __TRACE_H__
#define __TRACE_H__
#include <iostream>
#include <fstream>
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
#include "tlm_utils/simple_target_socket.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
class Trace: sc_module {
public:
// TLM-2 socket, defaults to 32-bits wide, base protocol
tlm_utils::simple_target_socket<Trace> socket;
// Constructor
Trace(sc_module_name name);
// TLM-2 blocking transport method
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay );
};
#endif

35
src/BusCtrl.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "BusCtrl.h"
SC_HAS_PROCESS(BusCtrl);
BusCtrl::BusCtrl(sc_module_name name): sc_module(name)
,cpu_instr_socket("cpu_instr_socket")
,cpu_data_socket("cpu_data_socket")
,data_memory_socket("data_memory_socket")
,trace_socket("trace_socket")
{
cpu_instr_socket.register_b_transport(this, &BusCtrl::b_transport);
cpu_data_socket.register_b_transport(this, &BusCtrl::b_transport);
log = Log::getInstance();
}
void BusCtrl::b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ) {
tlm::tlm_command cmd = trans.get_command();
sc_dt::uint64 adr = trans.get_address() / 4;
if (adr == TRACE_MEMORY_ADDRESS / 4) {
trace_socket->b_transport(trans, delay);
} else {
data_memory_socket->b_transport(trans, delay);
}
#if 0
if (cmd == tlm::TLM_READ_COMMAND) {
log->SC_log(Log::DEBUG) << "RD Address: @0x" << hex << adr << dec << endl;
} else {
log->SC_log(Log::DEBUG) << "WR Address: @0x" << hex << adr << dec << endl;
}
#endif
trans.set_response_status( tlm::TLM_OK_RESPONSE );
}

View File

@ -2,7 +2,7 @@
#include "CPU.h" #include "CPU.h"
SC_HAS_PROCESS(CPU); SC_HAS_PROCESS(CPU);
CPU::CPU(sc_module_name name): sc_module(name) CPU::CPU(sc_module_name name, uint32_t PC): sc_module(name)
, instr_bus("instr_bus") , instr_bus("instr_bus")
{ {
register_bank = new Registers(); register_bank = new Registers();
@ -10,6 +10,7 @@ CPU::CPU(sc_module_name name): sc_module(name)
perf = Performance::getInstance(); perf = Performance::getInstance();
log = Log::getInstance(); log = Log::getInstance();
register_bank->setPC(PC);
SC_THREAD(CPU_thread); SC_THREAD(CPU_thread);
} }
@ -20,6 +21,7 @@ CPU::~CPU() {
perf->dump(); perf->dump();
cout << "*********************************************" << endl; cout << "*********************************************" << endl;
} }
/** /**
* main thread for CPU simulation * main thread for CPU simulation
* @brief CPU mai thread * @brief CPU mai thread
@ -49,8 +51,8 @@ void CPU::CPU_thread(void) {
if ( trans->is_response_error() ) { if ( trans->is_response_error() ) {
SC_REPORT_ERROR("CPU base", "Read memory"); SC_REPORT_ERROR("CPU base", "Read memory");
} else { } else {
// cout << "INSTR: " << INSTR << endl; log->SC_log(Log::INFO) << "PC: " << hex << register_bank->getPC()
log->SC_log(Log::INFO) << "PC: " << register_bank->getPC() << endl; << dec << endl;
Instruction inst(INSTR); Instruction inst(INSTR);
switch(inst.decode()) { switch(inst.decode()) {
@ -63,45 +65,127 @@ void CPU::CPU_thread(void) {
case OP_JAL: case OP_JAL:
exec->JAL(inst); exec->JAL(inst);
break; break;
case OP_JALR:
exec->JALR(inst);
break;
case OP_BEQ: case OP_BEQ:
exec->BEQ(inst); exec->BEQ(inst);
break; break;
case OP_BNE: case OP_BNE:
exec->BNE(inst); exec->BNE(inst);
break; break;
case OP_BLT:
exec->BLT(inst);
break;
case OP_BGE:
exec->BGE(inst);
break;
case OP_BLTU:
exec->BLTU(inst);
break;
case OP_BGEU:
exec->BGEU(inst);
break;
case OP_LB:
exec->LB(inst);
break;
case OP_LH:
exec->LB(inst);
break;
case OP_LW: case OP_LW:
exec->LW(inst); exec->LW(inst);
break; break;
case OP_LBU:
exec->LBU(inst);
break;
case OP_LHU:
exec->LHU(inst);
break;
case OP_SB:
exec->SB(inst);
break;
case OP_SH:
exec->SH(inst);
break;
case OP_SW: case OP_SW:
exec->SW(inst); exec->SW(inst);
break; break;
case OP_ADDI: case OP_ADDI:
exec->ADDI(inst); exec->ADDI(inst);
break; break;
case OP_SLTI:
exec->SLTI(inst);
break;
case OP_SLTIU:
exec->SLTIU(inst);
break;
case OP_XORI:
exec->XORI(inst);
break;
case OP_ORI:
exec->ORI(inst);
break;
case OP_ANDI:
exec->ANDI(inst);
break;
case OP_SLLI:
exec->SLLI(inst);
break;
case OP_SRLI:
exec->SRLI(inst);
break;
case OP_SRAI:
exec->SRAI(inst);
break;
case OP_ADD: case OP_ADD:
exec->ADD(inst); exec->ADD(inst);
break; break;
case OP_SUB: case OP_SUB:
exec->SUB(inst); exec->SUB(inst);
break; break;
case OP_SLL:
exec->SLL(inst);
break;
case OP_SLT:
exec->SLT(inst);
break;
case OP_SLTU:
exec->SLTU(inst);
break;
case OP_XOR:
exec->XOR(inst);
break;
case OP_SRL:
exec->SRL(inst);
break;
case OP_SRA:
exec->SRA(inst);
break;
case OP_OR:
exec->OR(inst);
break;
case OP_AND:
exec->AND(inst);
break;
#if 0
case OP_CSRRW:
exec->CSRRW(inst);
break;
case OP_CSRRS:
exec->CSRRS(inst);
break;
case OP_CSRRC:
exec->CSRRC(inst);
break;
#endif
default: default:
cout << endl << "Instruction not implemented: ";
inst.dump();
exec->NOP(inst); exec->NOP(inst);
} }
perf->instructionsInc(); perf->instructionsInc();
register_bank->incPC(); register_bank->incPC();
/* Simulation control, we stop at 10 instructions (if no NOP found)*/
if (register_bank->getPC() == 10*4) {
cout << "*********************************************" << endl;
register_bank->dump();
cout << sc_time_stamp() << endl;
cout << "*********************************************" << endl;
perf->dump();
sc_stop();
}
} }
} // while(1) } // while(1)
} // CPU_thread } // CPU_thread

View File

@ -3,8 +3,6 @@
Instruction::Instruction(sc_int<32> instr) { Instruction::Instruction(sc_int<32> instr) {
m_instr = instr; m_instr = instr;
} }
opCodes Instruction::decode() { opCodes Instruction::decode() {

View File

@ -12,7 +12,7 @@ Log* Log::getInstance()
Log::Log(const char* filename) { Log::Log(const char* filename) {
m_stream.open(filename); m_stream.open(filename);
currentLogLevel = Log::ERROR; currentLogLevel = Log::INFO;
} }
void Log::SC_log(std::string msg, enum LogLevel level) { void Log::SC_log(std::string msg, enum LogLevel level) {
@ -22,6 +22,7 @@ void Log::SC_log(std::string msg, enum LogLevel level) {
} }
std::ofstream& Log::SC_log(enum LogLevel level) { std::ofstream& Log::SC_log(enum LogLevel level) {
if (level >= currentLogLevel) { if (level >= currentLogLevel) {
m_stream << "time " << sc_core::sc_time_stamp() << ": "; m_stream << "time " << sc_core::sc_time_stamp() << ": ";
} }

View File

@ -28,6 +28,11 @@ Memory::Memory(sc_module_name name, bool use_file): sc_module(name)
SC_THREAD(invalidation_process); SC_THREAD(invalidation_process);
} }
uint32_t Memory::getPCfromHEX() {
return program_counter;
}
void Memory::b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ) void Memory::b_transport( tlm::tlm_generic_payload& trans, sc_time& delay )
{ {
tlm::tlm_command cmd = trans.get_command(); tlm::tlm_command cmd = trans.get_command();
@ -58,8 +63,8 @@ void Memory::b_transport( tlm::tlm_generic_payload& trans, sc_time& delay )
return; return;
} }
// cout << "MEM: addr=" << adr << endl; //cout << "MEM: addr=" << hex << adr << endl << endl;
// cout << "MEM: data=" << mem[adr] << endl; //cout << "MEM: data=" << mem[adr] << endl;
// Obliged to implement read and write commands // Obliged to implement read and write commands
if ( cmd == tlm::TLM_READ_COMMAND ) if ( cmd == tlm::TLM_READ_COMMAND )
@ -158,6 +163,7 @@ void Memory::readHexFile(string filename) {
int byte_count; int byte_count;
int address; int address;
int i = 0; int i = 0;
int extended_address = 0;
hexfile.open(filename); hexfile.open(filename);
@ -165,12 +171,12 @@ void Memory::readHexFile(string filename) {
while(getline(hexfile, line) ) { while(getline(hexfile, line) ) {
/* # is a comentary in the file */ /* # is a comentary in the file */
if (line[0] == ':') { if (line[0] == ':') {
if (line.substr(7,2) == "00") if (line.substr(7,2) == "00")
{ {
/* Data */ /* Data */
byte_count = stol(line.substr(1,2), nullptr, 16); byte_count = stol(line.substr(1,2), nullptr, 16);
address = stol(line.substr(3,4), nullptr, 16) / 4; address = stol(line.substr(3,4), nullptr, 16) / 4;
address = address + extended_address;
for (i=0; i < byte_count/4; i++) { for (i=0; i < byte_count/4; i++) {
mem[address+i] = stol(line.substr(9+(i*8), 2), nullptr, 16); mem[address+i] = stol(line.substr(9+(i*8), 2), nullptr, 16);
@ -178,6 +184,15 @@ void Memory::readHexFile(string filename) {
mem[address+i] |= stol(line.substr(13+(i*8),2), nullptr, 16) << 16; mem[address+i] |= stol(line.substr(13+(i*8),2), nullptr, 16) << 16;
mem[address+i] |= stol(line.substr(15+(i*8),2), nullptr, 16) << 24; mem[address+i] |= stol(line.substr(15+(i*8),2), nullptr, 16) << 24;
} }
} else if (line.substr(7,2) == "02") {
/* Extended segment address */
extended_address = stol(line.substr(9,4), nullptr, 16 ) * 4;
} else if (line.substr(7,2) == "03") {
/* Start segment address */
uint32_t code_segment;
code_segment = stol(line.substr(9,4), nullptr, 16) * 16; /* ? */
program_counter = stol(line.substr(13,4), nullptr, 16);
program_counter = program_counter + code_segment;
} }
} }
} }
@ -185,4 +200,11 @@ void Memory::readHexFile(string filename) {
} else { } else {
SC_REPORT_ERROR("Memory", "Open file error"); SC_REPORT_ERROR("Memory", "Open file error");
} }
#if 0
for(int i = 50;i<100; i++) {
cout << "Dump address: 0x" << hex << extended_address + i << ": 0x" <<
mem[extended_address+i] << dec << endl;
}
#endif
} }

View File

@ -17,7 +17,7 @@ void RISC_V_execute::LUI(Instruction &inst) {
rd = inst.rd(); rd = inst.rd();
imm = inst.imm_U() << 12; imm = inst.imm_U() << 12;
regs->setValue(rd, imm); regs->setValue(rd, imm);
log->SC_log(Log::INFO) << "LUI R" << rd << " -> " << imm << endl; log->SC_log(Log::INFO) << "LUI R" << rd << " <- " << imm << endl;
} }
@ -47,10 +47,10 @@ void RISC_V_execute::JAL(Instruction &inst) {
new_pc = regs->getPC(); new_pc = regs->getPC();
regs->setValue(rd, new_pc); regs->setValue(rd, new_pc);
new_pc = new_pc + mem_addr; new_pc = new_pc + mem_addr - 4;
regs->setPC(new_pc); regs->setPC(new_pc);
log->SC_log(Log::INFO) << "JAL R" << rd << " PC + " << mem_addr << " -> PC (" << new_pc << ")" << endl; log->SC_log(Log::INFO) << "JAL: R" << rd << " PC + " << mem_addr << " -> PC (" << new_pc << ")" << endl;
} }
void RISC_V_execute::JALR(Instruction &inst) { void RISC_V_execute::JALR(Instruction &inst) {
@ -320,7 +320,7 @@ void RISC_V_execute::ADDI(Instruction &inst) {
calc = regs->getValue(rs1) + imm; calc = regs->getValue(rs1) + imm;
regs->setValue(rd, calc); regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ADDI: R" << rs1 << " + " << imm << " -> R" << rd << endl; log->SC_log(Log::INFO) << dec << "ADDI: R" << rs1 << " + " << imm << " -> R" << rd << endl;
} }
void RISC_V_execute::SLTI(Instruction &inst) { void RISC_V_execute::SLTI(Instruction &inst) {
@ -406,7 +406,7 @@ void RISC_V_execute::ANDI(Instruction &inst) {
regs->setValue(rd, calc); regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ANDI: R" << rs1 << " AND " << imm log->SC_log(Log::INFO) << "ANDI: R" << rs1 << " AND " << imm
<< "-> R" << rd << endl; << " -> R" << rd << endl;
} }
void RISC_V_execute::SLLI(Instruction &inst) { void RISC_V_execute::SLLI(Instruction &inst) {

View File

@ -2,11 +2,11 @@
Registers::Registers() { Registers::Registers() {
memset(register_bank, 0, sizeof(int32_t)*32); memset(register_bank, 0, sizeof(int32_t)*32); // 32 registers of 32 bits each
perf = Performance::getInstance(); perf = Performance::getInstance();
register_bank[sp] = 1024-1; // SP points to end of memory
register_PC = 0; register_PC = 0x10000; // default _start address
} }
void Registers::dump(void) { void Registers::dump(void) {

View File

@ -9,6 +9,8 @@
#include "CPU.h" #include "CPU.h"
#include "Memory.h" #include "Memory.h"
#include "BusCtrl.h"
#include "Trace.h"
using namespace sc_core; using namespace sc_core;
using namespace sc_dt; using namespace sc_dt;
@ -20,27 +22,42 @@ SC_MODULE(Top)
{ {
//Initiator *initiator; //Initiator *initiator;
CPU *cpu; CPU *cpu;
Memory *InstrMemory; //Memory *InstrMemory;
Memory *DataMemory; //Memory *DataMemory;
Memory *MainMemory;
BusCtrl* Bus;
Trace *trace;
uint32_t start_PC;
sc_signal<bool> IRQ; sc_signal<bool> IRQ;
SC_CTOR(Top) SC_CTOR(Top)
{ {
cpu = new CPU("cpu");
InstrMemory = new Memory("InstrMemory", filename);
DataMemory = new Memory("Datamemory", false);
cpu->instr_bus.bind(InstrMemory->socket);
cpu->exec->data_bus.bind(DataMemory->socket); MainMemory = new Memory("Main_Memory", filename);
start_PC = MainMemory->getPCfromHEX();
cpu = new CPU("cpu", start_PC);
Bus = new BusCtrl("BusCtrl");
trace = new Trace("Trace");
cpu->instr_bus.bind(Bus->cpu_instr_socket);
cpu->exec->data_bus.bind(Bus->cpu_data_socket);
Bus->data_memory_socket.bind(MainMemory->socket);
Bus->trace_socket.bind(trace->socket);
//cpu->interrupt.bind(IRQ); //cpu->interrupt.bind(IRQ);
} }
~Top() { ~Top() {
cout << "Top destructor" << endl; cout << "Top destructor" << endl;
delete cpu; delete cpu;
delete InstrMemory; delete MainMemory;
delete DataMemory; delete Bus;
delete trace;
} }
}; };

22
src/Trace.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "Trace.h"
SC_HAS_PROCESS(Trace);
Trace::Trace(sc_module_name name): sc_module(name)
,socket("socket") {
socket.register_b_transport(this, &Trace::b_transport);
}
void Trace::b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ) {
//tlm::tlm_command cmd = trans.get_command();
//sc_dt::uint64 adr = trans.get_address() / 4;
unsigned char* ptr = trans.get_data_ptr();
//unsigned int len = trans.get_data_length();
//unsigned char* byt = trans.get_byte_enable_ptr();
//unsigned int wid = trans.get_streaming_width();
cout << (char) *ptr;
trans.set_response_status( tlm::TLM_OK_RESPONSE );
}