initial import

This commit is contained in:
mariusmonton 2018-09-10 18:44:54 +02:00
parent 26e67681f0
commit 35e688837a
28 changed files with 4631 additions and 0 deletions

3
.gitignore vendored
View File

@ -30,3 +30,6 @@
*.exe
*.out
*.app
Log.txt
helper.ods

48
Makefile Normal file
View File

@ -0,0 +1,48 @@
TARGET = RISCV_TLM
SYSTEMC=/home/marius/Work/RiscV/code/systemc-2.3.2
TARGET_ARCH=linux64
CC = g++
# compiling flags here
CFLAGS = -Wall -I.
LINKER = g++
# linking flags here
LFLAGS = -Wall -I. -lm
LIBS = -lsystemc -lm $(EXTRA_LIBS)
# change these to proper directories where each file should be
SRCDIR = src
OBJDIR = obj
BINDIR = ./
INCDIR = -I. -I./inc -I$(SYSTEMC)/include -Ibasic_protocol -I$(SYSTEMC)/include/tlm_core/tlm_2
LIBDIR = -L. -L$(SYSTEMC)/lib-$(TARGET_ARCH)
SOURCES := $(wildcard $(SRCDIR)/*.cpp)
INCLUDES := $(wildcard $(INCDIR)/*.h)
OBJECTS := $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
rm = rm -f
$(BINDIR)/$(TARGET): $(OBJECTS)
@$(LINKER) $(OBJECTS) $(LFLAGS) $(LIBS) $(LIBDIR) -o $@
@echo "Linking complete!"
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.cpp
# @$(CC) $(CFLAGS) $(INCDIR) -c $< -o $@
@echo "Compiling "$<" ..."
@$(CC) $(CFLAGS) $(INCDIR) -c $< -o $@
@echo "Done!"
.PHONY: clean
clean:
@$(rm) $(OBJECTS)
@echo "Cleanup complete!"
.PHONY: remove
remove: clean
@$(rm) $(BINDIR)/$(TARGET)
@echo "Executable removed!"

90
README.md Normal file
View File

@ -0,0 +1,90 @@
# Another RISC-V ISA simulator.
**This code is suitable to hard refactor at any time**
This is another RISC-V ISA simulator, this is coded in SystemC + TLM-2.
Brief description of the modules:
* CPU: Top entity that includes all other modules.
* Memory: Memory highly based on TLM-2 example with read file capability
* Registers: Implements the register file and PC
* RISC_V_execute: Executes every ISA instruction
* Instruction: Decodes instruction and acces to any instruction field
Helper classes:
* Performance: Performance indicators stores here (singleton class)
* Log: Log class to log them all (singleton class)
## TODO
This is a preliminar and incomplete version.
Task to do:
* implement all missing instructions (RISC_V_execute)
* still unresolved data memory access (need to implement hierarchical socket
from CPU to RISC_V_execute)
* Implement CSRs
* Add full support to .elf and .hex filetypes to memory.h
(only partial .hex support)
* Connect some TLM peripherals
* Test, test, test & test. I'm sure there are a lot of bugs in the code
## compile
In order to compile the project you need SystemC-2.3.2 installed in your system.
Just change SYSTEMC path in Makefile.
```
$ make
```
Then, you need to modifiy your LD_LIBRARY_PATH environtment variable to add
path systemc library. In my case:
```
$ export LD_LIBRARY_PATH=/home/marius/Work/RiscV/code/systemc-2.3.2/lib-linux64
```
And then you can execute the simulator:
```
$ ./RISCV_TLM asm/BasicLoop.hex
```
## Test
In the asm directory there are some basic assembly examples.
I "compile" one file with the follwing command:
```
$ cd asm
$ riscv32-unknown-linux-gnu-as EternalLoop.asm
$ objcopy -O ihex a.out EternalLoop.hex
$ cd ..
$ ./RISCV_SCTLM asm/EternalLoop.hex
```
This example needs that you hit Ctr+C to stop execution.
## Documentation
The code is documented using doxygen. In the doc folder there is a Doxygen.cfg
file ready to be used.
## Contribute
There are several ways to contribute to this project:
* Test
* Pull request are welcome (see TODO list)
* Good documentation
## License
Copyright (C) 2018 Màrius Montón ([\@mariusmonton](https://twitter.com/mariusmonton/))
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

7
asm/BasicLoop.asm Normal file
View File

@ -0,0 +1,7 @@
# Basic loop
ADDI t1, zero, 10 # t1 to 10
ADDI t2, zero, 1 #t2 to 1
loop:
SUB t1, t1, t2
BNE t1, zero, loop
# END

2
asm/BasicLoop.hex Normal file
View File

@ -0,0 +1,2 @@
:100000001303A0009303100033037340E31E03FEA9
:00000001FF

7
asm/BasicRegisters.asm Normal file
View File

@ -0,0 +1,7 @@
li t1, 150
li t2, 300
li t3, -250
ADD t4, t1, t2
ADD t5, t2, t3
SUB t6, t2, t1
#SUB t7, t1, t2

8
asm/EternalLoop.asm Normal file
View File

@ -0,0 +1,8 @@
li t1, 150
li t2, 300
li t3, -250
loop:
ADD t4, t1, t2
ADD t5, t2, t3
SUB t6, t2, t1
J loop

3
asm/EternalLoop.hex Normal file
View File

@ -0,0 +1,3 @@
:10000000130360099303C012130E60F0B30E730064
:0C001000338FC301B38F63406FF05FFFBC
:00000001FF

3
asm/EternalLoop2.hex Normal file
View File

@ -0,0 +1,3 @@
:10000000130360099303C012130E60F0B30E730064
:0C001000338FC301B38F63406FF05FFFBC
:00000001FF

18
asm/memory.hex Normal file
View File

@ -0,0 +1,18 @@
# Simple example
# JAL +2048
#0010006F
# JAL +256
#1000006F
# JAL +4
0040006F
# ADD rd = R1, rs1 = R1, rs2 = R2
002080B3
# SUB rd = R3, rs1 = R1, rs2 = R2
402081B3
# ADD rd = R1, rs1 = R1, rs2 = R2
002080B3
# JAL -8
# FF9FF06F
# JAL -12
FF1FF06F
# END

3
asm/out.hex Normal file
View File

@ -0,0 +1,3 @@
:10000000130360099303C012130E60F0B30E730064
:0C001000338FC301B38F63406FF05FFFBC
:00000001FF

8
asm/test1.asm Normal file
View File

@ -0,0 +1,8 @@
# test1.asm
JAL +4
ADD t1, t1, t2
SUB t3, t1, t2
ADD t1, t1, t2
# JAL -12
J 10
LUI t2, 250

2443
doc/Doxygen.cfg Normal file

File diff suppressed because it is too large Load Diff

52
inc/CPU.h Normal file
View File

@ -0,0 +1,52 @@
/*!
\file CPU.h
\brief Main CPU class
\author Màrius Montón
\date August 2018
*/
#ifndef CPU_BASE_H
#define CPU_BASE_H
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "memory.h"
#include "Instruction.h"
#include "RISC_V_execute.h"
#include "Registers.h"
#include "Log.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
/**
* @brief ISC_V CPU model
* @param name name of the module
*/
class CPU: sc_module {
public:
tlm_utils::simple_initiator_socket<CPU> instr_bus;
//tlm_utils::simple_initiator_socket<cpu_base> data_bus;
//sc_in<sc_signal<bool> > interrupt;
CPU(sc_module_name name);
~CPU();
private:
Registers *register_bank;
RISC_V_execute *exec;
Performance *perf;
Log *log;
void CPU_thread(void);
};
#endif

279
inc/Instruction.h Normal file
View File

@ -0,0 +1,279 @@
/*!
\file Instruction.h
\brief Decode instructions part of the RISC-V
\author Màrius Montón
\date August 2018
*/
#ifndef INSTRUCTION__H
#define INSTRUCTION__H
#include "systemc"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
typedef enum {
OP_LUI,
OP_AUIPC,
OP_JAL,
OP_JALR,
OP_BEQ,
OP_BNE,
OP_BLT,
OP_BGE,
OP_BLTU,
OP_BGEU,
OP_LB,
OP_LH,
OP_LW,
OP_LBU,
OP_LHU,
OP_SB,
OP_SH,
OP_SW,
OP_ADDI,
OP_SLTI,
OP_SLTIU,
OP_XORI,
OP_ORI,
OP_ANDI,
OP_SLLI,
OP_SRLI,
OP_SRAI,
OP_ADD,
OP_SUB,
OP_SLL,
OP_SLT,
OP_SLTU,
OP_XOR,
OP_SRL,
OP_SRA,
OP_OR,
OP_AND,
OP_ERROR
} opCodes;
typedef enum {
LUI = 0b0110111,
AUIPC = 0b0010111,
JAL = 0b1101111,
JALR = 0b1100111,
BEQ = 0b1100011,
BEQ_F = 0b000,
BNE_F = 0b001,
BLT_F = 0b100,
BGE_F = 0b101,
BLTU_F = 0b110,
BGEU_F = 0b111,
LB = 0b0000011,
LB_F = 0b000,
LH_F = 0b001,
LW_F = 0b010,
LBU_F = 0b100,
LHU_F = 0b101,
SB = 0b0100011,
SB_F = 0b000,
SH_F = 0b001,
SW_F = 0b010,
ADDI = 0b0010011,
ADDI_F = 0b000,
SLTI_F = 0b010,
SLTIU_F = 0b011,
XORI_F = 0b100,
ORI_F = 0b110,
ANDI_F = 0b111,
SLLI_F = 0b001,
SRLI_F = 0b101,
SRLI_F7 = 0b0000000,
SRAI_F7 = 0b0100000,
ADD = 0b0110011,
ADD_F = 0b000,
SUB_F = 0b000,
ADD_F7 = 0b0000000,
SUB_F7 = 0b0100000,
SLL_F = 0b001,
SLT_F = 0b010,
SLTU_F = 0b011,
XOR_F = 0b100,
SRL_F = 0b101,
SRA_F = 0b101,
SRL_F7 = 0b0000000,
SRA_F7 = 0b0100000,
OR_F = 0b110,
AND_F = 0b111,
} Codes;
/**
* @brief Instruction decoding and fields access
*/
class Instruction{
public:
/**
* @brief Constructor
* @param instr Instruction to decode
*/
Instruction(sc_int<32> instr);
/**
* @brief Access to opcode field
* @return return opcode field
*/
inline int32_t opcode() {
// cout << "OP: " << m_instr << endl;
return m_instr.range(6,0);
}
/**
* @brief Access to rd field
* @return rd field
*/
inline int32_t rd() {
return m_instr.range(11, 7);
}
/**
* @brief Access to funct3 field
* @return funct3 field
*/
inline int32_t funct3() {
return m_instr.range(14, 12);
}
/**
* @brief Access to rs1 field
* @return rs1 field
*/
inline int32_t rs1() {
return m_instr.range(19, 15);
}
/**
* @brief Access to rs2 field
* @return rs2 field
*/
inline int32_t rs2() {
return m_instr.range(24, 20);
}
/**
* @brief Access to funct7 field
* @return funct7 field
*/
inline int32_t funct7() {
return m_instr.range(31, 25);
}
/**
* @brief Access to immediate field for I-type
* @return immediate_I field
*/
inline int32_t imm_I() {
int32_t aux = 0;
aux = m_instr.range(31, 20);
/* sign extension (optimize) */
if (m_instr[31] == 1) {
aux |= (0b11111111111111111111) << 12;
}
return aux;
}
/**
* @brief Access to immediate field for S-type
* @return immediate_S field
*/
inline int32_t imm_S() {
int32_t aux = 0;
aux = m_instr.range(31, 25) << 5;
aux |= m_instr.range(11,7);
if (m_instr[31] == 1) {
aux |= (0b11111111111111111111) << 12;
}
return aux;
}
/**
* @brief Access to immediate field for U-type
* @return immediate_U field
*/
inline int32_t imm_U() {
return m_instr.range(31, 12);
}
/**
* @brief Access to immediate field for B-type
* @return immediate_B field
*/
inline int32_t imm_B() {
int32_t aux = 0;
aux |= m_instr[7] << 11;
aux |= m_instr.range(30, 25) << 5;
aux |= m_instr[31] << 12;
aux |= m_instr.range(11, 8) << 1;
if (m_instr[31] == 1) {
aux |= (0b11111111111111111111) << 12;
}
return aux;
}
/**
* @brief Access to immediate field for J-type
* @return immediate_J field
*/
inline int32_t imm_J() {
int32_t aux = 0;
aux = m_instr[31] << 20;
aux |= m_instr.range(19,12) << 12;
aux |= m_instr[20] << 11;
aux |= m_instr.range(30,21) << 1;
/* bit extension (better way to do that?) */
if (m_instr[31] == 1) {
aux |= (0b111111111111) << 20;
}
return aux;
}
inline int32_t csr() {
return imm_I();
}
/**
* @brief Decodes opcode of instruction
* @return opcode of instruction
*/
opCodes decode();
inline void dump() {
cout << hex << "0x" << m_instr << dec << endl;
}
private:
sc_int<32> m_instr;
};
#endif

76
inc/Log.h Normal file
View File

@ -0,0 +1,76 @@
/*!
\file Log.h
\brief Class to manage Log
\author Màrius Montón
\date Aug 2018
*/
#ifndef LOG_H
#define LOG_H
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include <string>
#include <fstream>
#include <sstream>
#include "systemc"
#include "tlm.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
/**
* @brief Log management class
*
* Singleton class to be shared among all other classes
*/
class Log {
public:
enum LogLevel{
INFO=0,
DEBUG,
WARNING,
ERROR
} currentLogLevel;
/**
* @brief Constructor
* @return pointer to Log class
*/
static Log* getInstance();
/**
* @brief method to log some string
* @param msg mesasge string
* @param level level of the log (LogLevel)
*/
void SC_log(std::string msg, enum LogLevel level);
/**
* @brief method to log some string
* @param level level of the log (LogLevel)
* @return streaming
*
* This function can be used in the following way:
* \code
* my_log->SC_log(Log::WARNING) << "some warning text"
* \endcode
*/
std::ofstream& SC_log(enum LogLevel level);
/**
* @brief Sets log level
* @param newLevel Level of the log
*/
void setLogLevel(enum LogLevel newLevel);
private:
static Log* instance;
Log(const char* filename);
std::ofstream m_stream;
};
#endif

71
inc/Memory.h Normal file
View File

@ -0,0 +1,71 @@
/*!
\file Memory.h
\brief Basic TLM-2 memory model
\author Màrius Montón
\date August 2018
*/
#ifndef __MEMORY_H__
#define __MEMORY_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;
/**
* @brief Basic TLm-2 memory
*/
class Memory: sc_module {
public:
// TLM-2 socket, defaults to 32-bits wide, base protocol
tlm_utils::simple_target_socket<Memory> socket;
enum { SIZE = 1024 };
const sc_time LATENCY;
Memory(sc_module_name name, string filename);
// TLM-2 blocking transport method
virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay );
// *********************************************
// TLM-2 forward DMI method
// *********************************************
virtual bool get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
tlm::tlm_dmi& dmi_data);
void invalidation_process();
// *********************************************
// TLM-2 debug transport method
// *********************************************
virtual unsigned int transport_dbg(tlm::tlm_generic_payload& trans);
int mem[SIZE];
/**
* Reads file and stores in Code Memory. Uses propietary file format
* @brief Reads file and stores in Code Memory
* @param filename File name
*/
virtual void readCustomHexFile(string filename);
/**
* @brief Read Intel hex file
* @param filename file name to read
*/
virtual void readHexFile(string filename);
};
#endif /* __MEMORY_H__ */

102
inc/Performance.h Normal file
View File

@ -0,0 +1,102 @@
/*!
\file Performance.h
\brief Class to store performance of CPU
\author Màrius Montón
\date Aug 2018
*/
#ifndef PERFORMANCE_H
#define PERFORMANCE_H
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
/**
* @brief Performance indicators class
*
* Singleton class to be shared among all other classes
*/
class Performance{
public:
/**
* @brief Get an instance of the class
* @return pointer to Performance class
*/
static Performance* getInstance();
/**
* @brief Increment data memory read counter
*/
inline void dataMemoryRead() {
data_memory_read++;
}
/**
* @brief Increment data memory write counter
*/
inline void dataMemoryWrite() {
data_memory_write++;
}
/**
* @brief Increment code memory read counter
*/
inline void codeMemoryRead() {
code_memory_read++;
}
/**
* @brief Increment code memory write counter
*/
inline void codeMemoryWrite() {
code_memory_write++;
}
/**
* @brief Increment register read counter
*/
inline void registerRead() {
register_read++;
}
/**
* @brief Increment register write counter
*/
inline void registerWrite() {
register_write++;
}
/**
* @brief Increment instructions executed counter
*/
inline void instructionsInc() {
instructions_executed++;
}
/**
* @brief Dump counters to cout
*/
void dump();
private:
static Performance* instance;
Performance();
uint64_t data_memory_read = 0;
uint64_t data_memory_write = 0;
uint64_t code_memory_read = 0;
uint64_t code_memory_write = 0;
uint64_t register_read = 0;
uint64_t register_write = 0;
uint64_t instructions_executed = 0;
};
#endif

105
inc/RISC_V_execute.h Normal file
View File

@ -0,0 +1,105 @@
/*!
\file RISC_V_execute.h
\brief RISC-V ISA implementation
\author Màrius Montón
\date August 2018
*/
#ifndef RISC_V_EXECUTE_H
#define RISC_V_EXECUTE_H
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "memory.h"
#include "Instruction.h"
#include "Registers.h"
#include "Log.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
/**
* @brief Risc_V execute module
*/
class RISC_V_execute : sc_module {
public:
/**
* @brief Constructor
* @param name module name
* @param register_bank pointer to register bank to use
*/
RISC_V_execute(sc_module_name name,
Registers *register_bank);
void LUI(Instruction &inst);
void AUIPC(Instruction &inst);
void JAL(Instruction &inst);
void JALR(Instruction &inst);
void BEQ(Instruction &inst);
void BNE(Instruction &inst);
void BLT(Instruction &inst);
void BGE(Instruction &inst);
void BLTU(Instruction &inst);
void BGEU(Instruction &inst);
void LB(Instruction &inst);
void LH(Instruction &inst);
void LW(Instruction &inst);
void LBU(Instruction &inst);
void LHU(Instruction &inst);
void SB(Instruction &inst);
void SH(Instruction &inst);
void SW(Instruction &inst);
void SBU(Instruction &inst);
void SHU(Instruction &inst);
void ADDI(Instruction &inst);
void SLTI(Instruction &inst);
void SLTIU(Instruction &inst);
void XORI(Instruction &inst);
void ORI(Instruction &inst);
void ANDI(Instruction &inst);
void SLLI(Instruction &inst);
void SRLI(Instruction &inst);
void SRAI(Instruction &inst);
void ADD(Instruction &inst);
void SUB(Instruction &inst);
void SLL(Instruction &inst);
void SLT(Instruction &inst);
void SLTU(Instruction &inst);
void XOR(Instruction &inst);
void SRL(Instruction &inst);
void SRA(Instruction &inst);
void OR(Instruction &inst);
void AND(Instruction &inst);
void CSRRW(Instruction &inst);
void CSRRS(Instruction &inst);
void CSRRC(Instruction &inst);
void CSRRWI(Instruction &inst);
void CSRRSI(Instruction &inst);
void CSRRCI(Instruction &inst);
void NOP(Instruction &inst);
private:
uint32_t readDataMem(uint32_t addr);
Registers *regs;
Performance *perf;
Log *log;
};
#endif

105
inc/Registers.h Normal file
View File

@ -0,0 +1,105 @@
/*!
\file Registers.h
\brief Basic register file implementation
\author Màrius Montón
\date August 2018
*/
#ifndef REGISTERS_H
#define REGISTERS_H
#define SC_INCLUDE_DYNAMIC_PROCESSES
#include "systemc"
#include "tlm.h"
#include "Performance.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
/**
* @brief Register file implementation
*/
class Registers {
public:
/**
* Default constructor
*/
Registers();
/**
* Set value for a register
* @param reg_num register number
* @param value register value
*/
void setValue(int reg_num, int32_t value);
/**
* Returns register value
* @param reg_num register number
* @return register value
*/
int32_t getValue(int reg_num);
/**
* Returns PC value
* @return PC value
*/
uint32_t getPC();
/**
* Sets arbitraty value to PC
* @param new_pc new address to PC
*/
void setPC(uint32_t new_pc);
/**
* Increments PC couunter to next address
*/
inline void incPC() {
register_PC += 4;
}
/**
* @brief Get CSR value
* @param csr CSR number to access
* @return CSR value
*/
inline uint32_t getCSR(int csr) {
return CSR[csr];
}
/**
* @brief Set CSR value
* @param csr CSR number to access
* @param value new value to register
*/
inline void setCSR(int csr, uint32_t value) {
CSR[csr] = value;
}
/**
* Dump register data to console
*/
void dump();
private:
/**
* bank of registers (32 regs of 32bits each)
*/
int32_t register_bank[32];
/**
* Program counter (32 bits width)
*/
uint32_t register_PC;
/**
* CSR registers (4096 maximum)
*/
uint32_t CSR[4096];
Performance *perf;
};
#endif

103
src/CPU.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "CPU.h"
SC_HAS_PROCESS(CPU);
CPU::CPU(sc_module_name name): sc_module(name)
, instr_bus("instr_bus")
//, exec("RISC_V_exec", &register_bank)
//, data_bus("data_bus")
{
register_bank = new Registers();
exec = new RISC_V_execute("RISC_V_execute", register_bank);
perf = Performance::getInstance();
log = Log::getInstance();
SC_THREAD(CPU_thread);
}
CPU::~CPU() {
cout << "*********************************************" << endl;
register_bank->dump();
cout << sc_time_stamp() << endl;
perf->dump();
cout << "*********************************************" << endl;
}
/**
* main thread for CPU simulation
* @brief CPU mai thread
*/
void CPU::CPU_thread(void) {
tlm::tlm_generic_payload* trans = new tlm::tlm_generic_payload;
int32_t INSTR;
sc_time delay = SC_ZERO_TIME;
trans->set_command( tlm::TLM_READ_COMMAND );
trans->set_data_ptr( reinterpret_cast<unsigned char*>(&INSTR) );
trans->set_data_length( 4 );
trans->set_streaming_width( 4 ); // = data_length to indicate no streaming
trans->set_byte_enable_ptr( 0 ); // 0 indicates unused
trans->set_dmi_allowed( false ); // Mandatory initial value
trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE );
register_bank->dump();
while(1) {
/* Get new PC value */
trans->set_address( register_bank->getPC() );
instr_bus->b_transport( *trans, delay);
perf->codeMemoryRead();
if ( trans->is_response_error() ) {
SC_REPORT_ERROR("CPU base", "Read memory");
} else {
// cout << "INSTR: " << INSTR << endl;
log->SC_log(Log::INFO) << "PC: " << register_bank->getPC() << endl;
Instruction inst(INSTR);
switch(inst.decode()) {
case OP_LUI:
exec->LUI(inst);
break;
case OP_AUIPC:
exec->AUIPC(inst);
break;
case OP_JAL:
exec->JAL(inst);
break;
case OP_BEQ:
exec->BEQ(inst);
break;
case OP_BNE:
exec->BNE(inst);
break;
case OP_ADDI:
exec->ADDI(inst);
break;
case OP_ADD:
exec->ADD(inst);
break;
case OP_SUB:
exec->SUB(inst);
break;
default:
exec->NOP(inst);
}
perf->instructionsInc();
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)
} // CPU_thread

121
src/Instruction.cpp Normal file
View File

@ -0,0 +1,121 @@
#include "Instruction.h"
Instruction::Instruction(sc_int<32> instr) {
m_instr = instr;
}
opCodes Instruction::decode() {
switch (opcode()) {
case LUI:
return OP_LUI;
case AUIPC:
return OP_AUIPC;
case JAL:
return OP_JAL;
case JALR:
return OP_JALR;
case BEQ:
switch(funct3()) {
case BEQ_F:
return OP_BEQ;
case BNE_F:
return OP_BNE;
case BLT_F:
return OP_BLT;
case BGE_F:
return OP_BGE;
case BLTU_F:
return OP_BLTU;
case BGEU_F:
return OP_BGEU;
}
return OP_ERROR;
case LB:
switch(funct3()) {
case LB_F:
return OP_LB;
case LH_F:
return OP_LH;
case LW_F:
return OP_LW;
case LBU_F:
return OP_LBU;
case LHU_F:
return OP_LHU;
}
return OP_ERROR;
case SB:
switch(funct3()) {
case SB_F:
return OP_SB;
case SH_F:
return OP_SH;
case SW_F:
return OP_SW;
}
return OP_ERROR;
case ADDI:
switch(funct3()) {
case ADDI_F:
return OP_ADDI;
case SLTI_F:
return OP_SLTI;
case SLTIU_F:
return OP_SLTIU;
case XORI_F:
return OP_XORI;
case ORI_F:
return OP_ORI;
case ANDI_F:
return OP_ANDI;
case SLLI_F:
return OP_SLLI;
case SRLI_F:
switch(funct7()) {
case SRLI_F7:
return OP_SRLI;
case SRAI_F7:
return OP_SRAI;
}
return OP_ERROR;
}
return OP_ERROR;
case ADD: {
switch(funct3()) {
case ADD_F:
switch (funct7()) {
case ADD_F7:
return OP_ADD;
case SUB_F7:
return OP_SUB;
};
break;
case SLL_F:
return OP_SLL;
case SLT_F:
return OP_SLT;
case SLTU_F:
return OP_SLTU;
case XOR_F:
return OP_XOR;
case SRL_F:
switch(funct7()) {
case SRL_F7:
return OP_SRL;
case SRA_F7:
return OP_SRA;
}
case OR_F:
return OP_OR;
case AND_F:
return OP_AND;
}
} /* ADD */
return OP_ERROR;
default:
return OP_ERROR;
}
}

36
src/Log.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "Log.h"
Log* Log::getInstance()
{
if (instance == 0)
{
instance = new Log("Log.txt");
}
return instance;
}
Log::Log(const char* filename) {
m_stream.open(filename);
currentLogLevel = Log::INFO;
}
void Log::SC_log(std::string msg, enum LogLevel level) {
if (level >= currentLogLevel) {
m_stream << "time " << sc_core::sc_time_stamp() << ": " << msg << std::endl;
}
}
std::ofstream& Log::SC_log(enum LogLevel level) {
if (level >= currentLogLevel) {
m_stream << "time " << sc_core::sc_time_stamp() << ": ";
}
return m_stream;
}
void Log::setLogLevel(enum LogLevel newLevel) {
currentLogLevel = newLevel;
}
Log* Log::instance = 0;

177
src/Memory.cpp Normal file
View File

@ -0,0 +1,177 @@
#include "Memory.h"
SC_HAS_PROCESS(Memory);
Memory::Memory(sc_module_name name, string filename): sc_module(name)
,socket("socket")
,LATENCY(SC_ZERO_TIME) {
// Register callbacks for incoming interface method calls
socket.register_b_transport( this, &Memory::b_transport);
socket.register_get_direct_mem_ptr(this, &Memory::get_direct_mem_ptr);
socket.register_transport_dbg( this, &Memory::transport_dbg);
memset(mem, 0, SIZE*sizeof(int));
// readCustomHexFile("memory.hex");
readHexFile(filename);
SC_THREAD(invalidation_process);
}
void Memory::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();
// Obliged to check address range and check for unsupported features,
// i.e. byte enables, streaming, and bursts
// Can ignore extensions
// *********************************************
// Generate the appropriate error response
// *********************************************
if (adr >= sc_dt::uint64(SIZE)) {
trans.set_response_status( tlm::TLM_ADDRESS_ERROR_RESPONSE );
return;
}
if (byt != 0) {
trans.set_response_status( tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE );
return;
}
if (len > 4 || wid < len) {
trans.set_response_status( tlm::TLM_BURST_ERROR_RESPONSE );
return;
}
// cout << "MEM: addr=" << adr << endl;
// cout << "MEM: data=" << mem[adr] << endl;
// Obliged to implement read and write commands
if ( cmd == tlm::TLM_READ_COMMAND )
memcpy(ptr, &mem[adr], len);
else if ( cmd == tlm::TLM_WRITE_COMMAND )
memcpy(&mem[adr], ptr, len);
// Illustrates that b_transport may block
wait(delay);
// Reset timing annotation after waiting
delay = SC_ZERO_TIME;
// *********************************************
// Set DMI hint to indicated that DMI is supported
// *********************************************
trans.set_dmi_allowed(true);
// Obliged to set response status to indicate successful completion
trans.set_response_status( tlm::TLM_OK_RESPONSE );
}
bool Memory::get_direct_mem_ptr(tlm::tlm_generic_payload& trans,
tlm::tlm_dmi& dmi_data)
{
// Permit read and write access
dmi_data.allow_read_write();
// Set other details of DMI region
dmi_data.set_dmi_ptr( reinterpret_cast<unsigned char*>( &mem[0] ) );
dmi_data.set_start_address( 0 );
dmi_data.set_end_address( SIZE*4-1 );
dmi_data.set_read_latency( LATENCY );
dmi_data.set_write_latency( LATENCY );
return true;
}
void Memory::invalidation_process()
{
// Invalidate DMI pointers periodically
for (int i = 0; i < 4; i++)
{
wait(LATENCY*8);
socket->invalidate_direct_mem_ptr(0, SIZE-1);
}
}
unsigned int Memory::transport_dbg(tlm::tlm_generic_payload& trans)
{
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();
// Calculate the number of bytes to be actually copied
unsigned int num_bytes = (len < (SIZE - adr) * 4) ? len : (SIZE - adr) * 4;
if ( cmd == tlm::TLM_READ_COMMAND )
memcpy(ptr, &mem[adr], num_bytes);
else if ( cmd == tlm::TLM_WRITE_COMMAND )
memcpy(&mem[adr], ptr, num_bytes);
return num_bytes;
}
void Memory::readCustomHexFile(string filename) {
ifstream hexfile;
string line;
int i = 0;
hexfile.open(filename);
if (hexfile.is_open()) {
while(getline(hexfile, line) ) {
/* # is a comentary in the file */
if (line[0] != '#') {
cout << "i: " << i << endl;
mem[i] = stol(line.substr(0,8), nullptr, 16);
i++;
}
}
hexfile.close();
} else {
SC_REPORT_ERROR("Memory", "Open file error");
}
}
void Memory::readHexFile(string filename) {
ifstream hexfile;
string line;
int byte_count;
int address;
int i = 0;
hexfile.open(filename);
if (hexfile.is_open()) {
while(getline(hexfile, line) ) {
/* # is a comentary in the file */
if (line[0] == ':') {
if (line.substr(7,2) == "00")
{
/* Data */
byte_count = stol(line.substr(1,2), nullptr, 16);
address = stol(line.substr(3,4), nullptr, 16) / 4;
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(11+(i*8),2), nullptr, 16) << 8;
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;
}
}
}
}
hexfile.close();
} else {
SC_REPORT_ERROR("Memory", "Open file error");
}
}

26
src/Performance.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "Performance.h"
Performance* Performance::getInstance()
{
if (instance == 0)
{
instance = new Performance();
}
return instance;
}
Performance::Performance()
{}
void Performance::dump() {
cout << "# data memory reads: " << data_memory_read << endl;
cout << "# data memory writes: " << data_memory_write << endl;
cout << "# code memory reads: " << code_memory_read << endl;
cout << "# code memory writes: " << code_memory_write << endl;
cout << "# registers read: " << register_read << endl;
cout << "# registers write: " << register_write << endl;
cout << "# instructions executed: " << instructions_executed << endl;
}
Performance* Performance::instance = 0;

592
src/RISC_V_execute.cpp Normal file
View File

@ -0,0 +1,592 @@
#include "RISC_V_execute.h"
SC_HAS_PROCESS(RISC_V_execute);
RISC_V_execute::RISC_V_execute(sc_module_name name
, Registers *register_bank)
: sc_module(name)
, regs(register_bank) {
perf = Performance::getInstance();
log = Log::getInstance();
}
void RISC_V_execute::LUI(Instruction &inst) {
int rd;
uint32_t imm = 0;
rd = inst.rd();
imm = inst.imm_U() << 12;
regs->setValue(rd, imm);
log->SC_log(Log::INFO) << "LUI R" << rd << " -> " << imm << endl;
}
void RISC_V_execute::AUIPC(Instruction &inst) {
int rd;
uint32_t imm = 0;
int new_pc;
rd = inst.rd();
imm = inst.imm_U() << 12;
new_pc = regs->getPC() + imm;
regs->setPC(new_pc);
regs->setValue(rd, new_pc);
log->SC_log(Log::INFO) << "AUIPC R" << rd << " + PC -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::JAL(Instruction &inst) {
int32_t mem_addr = 0;
int rd;
int new_pc;
rd = inst.rd();
mem_addr = inst.imm_J();
new_pc = regs->getPC();
regs->setValue(rd, new_pc);
new_pc = new_pc + mem_addr;
regs->setPC(new_pc);
log->SC_log(Log::INFO) << "JAL R" << rd << " PC + " << mem_addr << " -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::JALR(Instruction &inst) {
uint32_t mem_addr = 0;
int rd;
int new_pc;
rd = inst.rd();
mem_addr = inst.imm_I();
new_pc = regs->getPC();
regs->setValue(rd, new_pc);
new_pc = (new_pc + mem_addr) & 0xFFFFFFFE;
regs->setPC(new_pc);
}
void RISC_V_execute::BEQ(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if (regs->getValue(rs1) == regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BEQ R" << rs1 << " == R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::BNE(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if (regs->getValue(rs1) != regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BNE R" << rs1 << " == R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::BLT(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if ((int32_t)regs->getValue(rs1) < (int32_t)regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BLT R" << rs1 << " < R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::BGE(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if ((int32_t)regs->getValue(rs1) >= (int32_t)regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BGE R" << rs1 << " > R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::BLTU(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if (regs->getValue(rs1) < regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BLTU R" << rs1 << " < R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::BGEU(Instruction &inst) {
int rs1, rs2;
int new_pc;
rs1 = inst.rs1();
rs2 = inst.rs2();
if (regs->getValue(rs1) >= regs->getValue(rs2)) {
new_pc = regs->getPC() + inst.imm_B() - 4;
regs->setPC(new_pc);
}
log->SC_log(Log::INFO) << "BGEU R" << rs1 << " > R" << rs2 << "? -> PC (" << new_pc << ")" << endl;
}
void RISC_V_execute::LB(Instruction &inst) {
uint32_t mem_addr = 0;
int rd, rs1;
uint32_t imm = 0;
uint32_t data;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_U() << 12;
mem_addr = imm + rs1;
data = readDataMem(mem_addr);
regs->setValue(rd, data);
}
void RISC_V_execute::ADDI(Instruction &inst) {
int rd, rs1;
int32_t imm = 0;
int32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
calc = regs->getValue(rs1) + imm;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ADDI: R" << rs1 << " + " << imm << " -> R" << rd << endl;
}
void RISC_V_execute::SLTI(Instruction &inst) {
int rd, rs1;
int32_t imm;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
if (regs->getValue(rs1) < imm) {
regs->setValue(rd, 1);
log->SC_log(Log::INFO) << "SLTI: R" << rs1 << " < " << imm
<< " => " << "1 -> R" << rd << endl;
} else {
regs->setValue(rd, 0);
log->SC_log(Log::INFO) << "SLTI: R" << rs1 << " < " << imm
<< " => " << "0 -> R" << rd << endl;
}
}
void RISC_V_execute::SLTIU(Instruction &inst) {
int rd, rs1;
int32_t imm;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
if ((uint32_t) regs->getValue(rs1) < (uint32_t)imm) {
regs->setValue(rd, 1);
log->SC_log(Log::INFO) << "SLTIU: R" << rs1 << " < " << imm
<< " => " << "1 -> R" << rd << endl;
} else {
regs->setValue(rd, 0);
log->SC_log(Log::INFO) << "SLTIU: R" << rs1 << " < " << imm
<< " => " << "0 -> R" << rd << endl;
}
}
void RISC_V_execute::XORI(Instruction &inst) {
int rd, rs1;
int32_t imm;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
calc = regs->getValue(rs1) ^ imm;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "XORI: R" << rs1 << " XOR " << imm
<< "-> R" << rd << endl;
}
void RISC_V_execute::ORI(Instruction &inst) {
int rd, rs1;
int32_t imm;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
calc = regs->getValue(rs1) | imm;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ORI: R" << rs1 << " OR " << imm
<< "-> R" << rd << endl;
}
void RISC_V_execute::ANDI(Instruction &inst) {
int rd, rs1;
int32_t imm;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
imm = inst.imm_I();
calc = regs->getValue(rs1) & imm;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ANDI: R" << rs1 << " AND " << imm
<< "-> R" << rd << endl;
}
void RISC_V_execute::SLLI(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = rs2 & 0x1F;
calc = ((uint32_t)regs->getValue(rs1)) << shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SLLI: R" << rs1 << " << " << shift << " -> R" << rd << endl;
}
void RISC_V_execute::SRLI(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = rs2 & 0x1F;
calc = ((uint32_t)regs->getValue(rs1)) >> shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SRLI: R" << rs1 << " >> " << shift << " -> R" << rd << endl;
}
void RISC_V_execute::SRAI(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
int32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = rs2 & 0x1F;
calc = regs->getValue(rs1) >> shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SRAI: R" << rs1 << " >> " << shift << " -> R" << rd << endl;
}
void RISC_V_execute::ADD(Instruction &inst) {
int rd, rs1, rs2;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
calc = regs->getValue(rs1) + regs->getValue(rs2);
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "ADD: R" << rs1 << " + R" << rs2 << " -> R" << rd << endl;
}
void RISC_V_execute::SUB(Instruction &inst) {
int rd, rs1, rs2;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
calc = regs->getValue(rs1) - regs->getValue(rs2);
regs->setValue(rd, calc);
/* Can insert some arbitrary execution time */
wait(sc_time(10, SC_NS));
log->SC_log(Log::INFO) << "SUB: R" << rs1 << " - R" << rs2 << " -> R" << rd << endl;
}
void RISC_V_execute::SLL(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = regs->getValue(rs2) & 0x1F;
calc = ((uint32_t)regs->getValue(rs1)) << shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SLL: R" << rs1 << " << " << shift << " -> R" << rd << endl;
}
/** */
void RISC_V_execute::SLT(Instruction &inst) {
int rd, rs1, rs2;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
if (regs->getValue(rs1) < regs->getValue(rs2)) {
regs->setValue(rd, 1);
log->SC_log(Log::INFO) << "SLT: R" << rs1 << " < R" << rs2
<< " => " << "1 -> R" << rd << endl;
} else {
regs->setValue(rd, 0);
log->SC_log(Log::INFO) << "SLT: R" << rs1 << " < R" << rs2
<< " => " << "0 -> R" << rd << endl;
}
}
void RISC_V_execute::SLTU(Instruction &inst) {
int rd, rs1, rs2;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
if ( (uint32_t)regs->getValue(rs1) < (uint32_t)regs->getValue(rs2)) {
regs->setValue(rd, 1);
log->SC_log(Log::INFO) << "SLTU: R" << rs1 << " < R" << rs2
<< " => " << "1 -> R" << rd << endl;
} else {
regs->setValue(rd, 0);
log->SC_log(Log::INFO) << "SLTU: R" << rs1 << " < R" << rs2
<< " => " << "0 -> R" << rd << endl;
}
}
void RISC_V_execute::XOR(Instruction &inst) {
int rd, rs1, rs2;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
calc = regs->getValue(rs1) ^ regs->getValue(rs2);
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "XOR: R" << rs1 << " XOR R" << rs2
<< "-> R" << rd << endl;
}
void RISC_V_execute::SRL(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = regs->getValue(rs2) & 0x1F;
calc = ((uint32_t)regs->getValue(rs1)) >> shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SRL: R" << rs1 << " >> " << shift << " -> R" << rd << endl;
}
void RISC_V_execute::SRA(Instruction &inst) {
int rd, rs1, rs2;
uint32_t shift;
int32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
shift = regs->getValue(rs2) & 0x1F;
calc = regs->getValue(rs1) >> shift;
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "SRA: R" << rs1 << " >> " << shift << " -> R" << rd << endl;
}
void RISC_V_execute::OR(Instruction &inst) {
int rd, rs1, rs2;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
calc = regs->getValue(rs1) | regs->getValue(rs2);
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "OR: R" << rs1 << " OR R" << rs2
<< "-> R" << rd << endl;
}
void RISC_V_execute::AND(Instruction &inst) {
int rd, rs1, rs2;
uint32_t calc;
rd = inst.rd();
rs1 = inst.rs1();
rs2 = inst.rs2();
calc = regs->getValue(rs1) & regs->getValue(rs2);
regs->setValue(rd, calc);
log->SC_log(Log::INFO) << "AND: R" << rs1 << " AND R" << rs2
<< "-> R" << rd << endl;
}
void RISC_V_execute::CSRRW(Instruction &inst) {
int rd, rs1;
int csr;
uint32_t aux;
rd = inst.rd();
rs1 = inst.rs1();
csr = inst.csr();
if (rd == 0) {
return;
}
/* These operations must be atomical */
aux = regs->getCSR(csr);
regs->setValue(rd, aux);
aux = regs->getValue(rs1);
regs->setCSR(csr, aux);
log->SC_log(Log::INFO) << "CSRRW: CSR #" << csr << " -> R" << rd
<< ". R" << rs1 << "-> CSR #" << csr << endl;
}
void RISC_V_execute::CSRRS(Instruction &inst) {
int rd, rs1;
int csr;
uint32_t bitmask, aux;
rd = inst.rd();
rs1 = inst.rs1();
csr = inst.csr();
if (rd == 0) {
return;
}
/* These operations must be atomical */
aux = regs->getCSR(csr);
regs->setValue(rd, aux);
bitmask = regs->getValue(rs1);
aux = aux | bitmask;
regs->setCSR(csr, aux);
log->SC_log(Log::INFO) << "CSRRS: CSR #" << csr << " -> R" << rd
<< ". R" << rs1 << " & CSR #" << csr << endl;
}
void RISC_V_execute::CSRRC(Instruction &inst) {
int rd, rs1;
int csr;
uint32_t bitmask, aux;
rd = inst.rd();
rs1 = inst.rs1();
csr = inst.csr();
if (rd == 0) {
return;
}
/* These operations must be atomical */
aux = regs->getCSR(csr);
regs->setValue(rd, aux);
bitmask = regs->getValue(rs1);
aux = aux & ~bitmask;
regs->setCSR(csr, aux);
log->SC_log(Log::INFO) << "CSRRC: CSR #" << csr << " -> R" << rd
<< ". R" << rs1 << " & CSR #" << csr << endl;
}
void RISC_V_execute::NOP(Instruction &inst) {
cout << endl;
regs->dump();
cout << "Simulation time " << sc_time_stamp() << endl;
perf->dump();
SC_REPORT_ERROR("RISC_V_execute", "NOP");
}
/**
* Access data memory to get data for LOAD & STORE OPs
* @note NOT IMPLEMENTED YET
* @param addr address to access to
* @return data value read
*/
uint32_t RISC_V_execute::readDataMem(uint32_t addr) {
// tlm::tlm_generic_payload* trans = new tlm::tlm_generic_payload;
// sc_time delay = SC_ZERO_TIME;
// data_bus->b_transport(*trans, delay);
return 0;
}

78
src/Registers.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "Registers.h"
Registers::Registers() {
memset(register_bank, 0, sizeof(int32_t)*32);
perf = Performance::getInstance();
register_PC = 0;
}
void Registers::dump(void) {
cout << "************************************" << endl;
cout << "Registers dump" << dec << endl;
#if 0
cout << "x0 (zero): " << register_bank[0] << "\t";
cout << "x1 (ra): " << register_bank[1] << "\t";
cout << "x2 (sp): " << register_bank[2] << "\t";
cout << "x3 (gp): " << register_bank[3] << "\t" << endl;
cout << "x4 (tp): " << register_bank[4] << "\t";
cout << "x5 (t0): " << register_bank[5] << "\t";
cout << "x6 (t1): " << register_bank[6] << "\t";
cout << "x7 (t2): " << register_bank[7] << "\t" << endl;
cout << "x8 (s0/fp): " << register_bank[8] << "\t";
cout << "x9 (s1): " << register_bank[9] << "\t";
cout << "x10 (a0): " << register_bank[10] << "\t";
cout << "x11 (a1): " << register_bank[11] << "\t" << endl;
cout << "x12 (a2): " << register_bank[12] << "\t";
cout << "x13 (a3): " << register_bank[13] << "\t";
cout << "x14 (a4): " << register_bank[14] << "\t";
cout << "x15 (a5): " << register_bank[15] << "\t" << endl;
cout << "x16 (a6): " << register_bank[16] << "\t";
cout << "x17 (a7): " << register_bank[17] << "\t";
cout << "x18 (s2): " << register_bank[18] << "\t";
cout << "x19 (s3): " << register_bank[19] << "\t" << endl;
#else
for(int i=0;i<32;i++) {
cout << "R" << dec << i << ": " << register_bank[i] << "\t";
if (i % 4 == 3) {
cout << endl;
}
}
#endif
cout << "PC: " << register_PC << endl;
cout << "************************************" << endl;
}
void Registers::setValue(int reg_num, int32_t value) {
if ((reg_num != 0) && (reg_num < 32)) {
register_bank[reg_num] = value;
perf->registerWrite();
}
}
int32_t Registers::getValue(int reg_num) {
if ((reg_num >= 0) && (reg_num < 32)){
perf->registerRead();
return register_bank[reg_num];
} else {
return 0xFFFFFFFF;
}
}
uint32_t Registers::getPC() {
return register_PC;
}
void Registers::setPC(uint32_t new_pc) {
register_PC = new_pc;
}

65
src/Simulator.cpp Normal file
View File

@ -0,0 +1,65 @@
#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 <signal.h>
#include "CPU.h"
#include "Memory.h"
using namespace sc_core;
using namespace sc_dt;
using namespace std;
string filename;
SC_MODULE(Top)
{
//Initiator *initiator;
CPU *cpu;
Memory *memory;
sc_signal<bool> IRQ;
SC_CTOR(Top)
{
cpu = new CPU("cpu");
memory = new Memory("memory", filename);
cpu->instr_bus.bind(memory->socket);
//cpu->interrupt.bind(IRQ);
}
~Top() {
cout << "Top destructor" << endl;
delete cpu;
delete memory;
}
};
Top *top;
void intHandler(int dummy) {
delete top;
//sc_stop();
exit(-1);
}
int sc_main(int argc, char* argv[])
{
signal(SIGINT, intHandler);
if (argv[1] == nullptr) {
cerr << "Filename needed for hex memory" << endl;
return -1;
}
filename = argv[1];
top = new Top("top");
sc_start();
return 0;
}