mirror of
https://github.com/SpinalHDL/VexRiscv.git
synced 2025-04-24 14:07:54 -04:00
3658 lines
99 KiB
C++
3658 lines
99 KiB
C++
#include "VVexRiscv.h"
|
|
#include "VVexRiscv_VexRiscv.h"
|
|
#ifdef REF
|
|
#include "VVexRiscv_RiscvCore.h"
|
|
#endif
|
|
#include "verilated.h"
|
|
#include "verilated_vcd_c.h"
|
|
#include <stdio.h>
|
|
#include <iostream>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <cstring>
|
|
#include <string.h>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <mutex>
|
|
#include <iomanip>
|
|
#include <queue>
|
|
#include <time.h>
|
|
#include "encoding.h"
|
|
|
|
using namespace std;
|
|
|
|
struct timespec timer_get(){
|
|
struct timespec start_time;
|
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time);
|
|
return start_time;
|
|
}
|
|
|
|
class Memory{
|
|
public:
|
|
uint8_t* mem[1 << 12];
|
|
|
|
Memory(){
|
|
for(uint32_t i = 0;i < (1 << 12);i++) mem[i] = NULL;
|
|
}
|
|
~Memory(){
|
|
for(uint32_t i = 0;i < (1 << 12);i++) if(mem[i]) delete [] mem[i];
|
|
}
|
|
|
|
uint8_t* get(uint32_t address){
|
|
if(mem[address >> 20] == NULL) {
|
|
uint8_t* ptr = new uint8_t[1024*1024];
|
|
for(uint32_t i = 0;i < 1024*1024;i+=4) {
|
|
ptr[i + 0] = 0xFF;
|
|
ptr[i + 1] = 0xFF;
|
|
ptr[i + 2] = 0xFF;
|
|
ptr[i + 3] = 0xFF;
|
|
}
|
|
mem[address >> 20] = ptr;
|
|
}
|
|
return &mem[address >> 20][address & 0xFFFFF];
|
|
}
|
|
|
|
void read(uint32_t address,uint32_t length, uint8_t *data){
|
|
for(int i = 0;i < length;i++){
|
|
data[i] = (*this)[address + i];
|
|
}
|
|
}
|
|
|
|
void write(uint32_t address,uint32_t length, uint8_t *data){
|
|
for(int i = 0;i < length;i++){
|
|
(*this)[address + i] = data[i];
|
|
}
|
|
}
|
|
|
|
uint8_t& operator [](uint32_t address) {
|
|
return *get(address);
|
|
}
|
|
|
|
/*T operator [](uint32_t address) const {
|
|
return get(address);
|
|
}*/
|
|
};
|
|
|
|
//uint8_t memory[1024 * 1024];
|
|
|
|
uint32_t hti(char c) {
|
|
if (c >= 'A' && c <= 'F')
|
|
return c - 'A' + 10;
|
|
if (c >= 'a' && c <= 'f')
|
|
return c - 'a' + 10;
|
|
return c - '0';
|
|
}
|
|
|
|
uint32_t hToI(char *c, uint32_t size) {
|
|
uint32_t value = 0;
|
|
for (uint32_t i = 0; i < size; i++) {
|
|
value += hti(c[i]) << ((size - i - 1) * 4);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void loadHexImpl(string path,Memory* mem) {
|
|
FILE *fp = fopen(&path[0], "r");
|
|
if(fp == 0){
|
|
cout << path << " not found" << endl;
|
|
}
|
|
//Preload 0x0 <-> 0x80000000 jumps
|
|
((uint32_t*)mem->get(0))[0] = 0x800000b7;
|
|
((uint32_t*)mem->get(0))[1] = 0x000080e7;
|
|
((uint32_t*)mem->get(0x80000000))[0] = 0x00000097;
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
uint32_t size = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
char* content = new char[size];
|
|
fread(content, 1, size, fp);
|
|
fclose(fp);
|
|
|
|
int offset = 0;
|
|
char* line = content;
|
|
while (1) {
|
|
if (line[0] == ':') {
|
|
uint32_t byteCount = hToI(line + 1, 2);
|
|
uint32_t nextAddr = hToI(line + 3, 4) + offset;
|
|
uint32_t key = hToI(line + 7, 2);
|
|
// printf("%d %d %d\n", byteCount, nextAddr,key);
|
|
switch (key) {
|
|
case 0:
|
|
for (uint32_t i = 0; i < byteCount; i++) {
|
|
*(mem->get(nextAddr + i)) = hToI(line + 9 + i * 2, 2);
|
|
//printf("%x %x %c%c\n",nextAddr + i,hToI(line + 9 + i*2,2),line[9 + i * 2],line[9 + i * 2+1]);
|
|
}
|
|
break;
|
|
case 2:
|
|
// cout << offset << endl;
|
|
offset = hToI(line + 9, 4) << 4;
|
|
break;
|
|
case 4:
|
|
// cout << offset << endl;
|
|
offset = hToI(line + 9, 4) << 16;
|
|
break;
|
|
default:
|
|
// cout << "??? " << key << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*line != '\n' && size != 0) {
|
|
line++;
|
|
size--;
|
|
}
|
|
if (size <= 1)
|
|
break;
|
|
line++;
|
|
size--;
|
|
}
|
|
|
|
delete [] content;
|
|
}
|
|
|
|
void loadBinImpl(string path,Memory* mem, uint32_t offset) {
|
|
FILE *fp = fopen(&path[0], "r");
|
|
if(fp == 0){
|
|
cout << path << " not found" << endl;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
uint32_t size = ftell(fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
char* content = new char[size];
|
|
fread(content, 1, size, fp);
|
|
fclose(fp);
|
|
|
|
for(int byteId = 0; byteId < size;byteId++){
|
|
*(mem->get(offset + byteId)) = content[byteId];
|
|
}
|
|
|
|
delete [] content;
|
|
}
|
|
|
|
|
|
|
|
#define TEXTIFY(A) #A
|
|
|
|
#define assertEq(x,ref) if(x != ref) {\
|
|
printf("\n*** %s is %d but should be %d ***\n\n",TEXTIFY(x),x,ref);\
|
|
throw std::exception();\
|
|
}
|
|
|
|
class success : public std::exception { };
|
|
|
|
|
|
|
|
|
|
#define MVENDORID 0xF11 // MRO Vendor ID.
|
|
#define MARCHID 0xF12 // MRO Architecture ID.
|
|
#define MIMPID 0xF13 // MRO Implementation ID.
|
|
#define MHARTID 0xF14 // MRO Hardware thread ID.Machine Trap Setup
|
|
#define MSTATUS 0x300 // MRW Machine status register.
|
|
#define MISA 0x301 // MRW ISA and extensions
|
|
#define MEDELEG 0x302 // MRW Machine exception delegation register.
|
|
#define MIDELEG 0x303 // MRW Machine interrupt delegation register.
|
|
#define MIE 0x304 // MRW Machine interrupt-enable register.
|
|
#define MTVEC 0x305 // MRW Machine trap-handler base address. Machine Trap Handling
|
|
#define MSCRATCH 0x340 // MRW Scratch register for machine trap handlers.
|
|
#define MEPC 0x341 // MRW Machine exception program counter.
|
|
#define MCAUSE 0x342 // MRW Machine trap cause.
|
|
#define MBADADDR 0x343 // MRW Machine bad address.
|
|
#define MIP 0x344 // MRW Machine interrupt pending.
|
|
#define MBASE 0x380 // MRW Base register.
|
|
#define MBOUND 0x381 // MRW Bound register.
|
|
#define MIBASE 0x382 // MRW Instruction base register.
|
|
#define MIBOUND 0x383 // MRW Instruction bound register.
|
|
#define MDBASE 0x384 // MRW Data base register.
|
|
#define MDBOUND 0x385 // MRW Data bound register.
|
|
#define MCYCLE 0xB00 // MRW Machine cycle counter.
|
|
#define MINSTRET 0xB02 // MRW Machine instructions-retired counter.
|
|
#define MCYCLEH 0xB80 // MRW Upper 32 bits of mcycle, RV32I only.
|
|
#define MINSTRETH 0xB82 // MRW Upper 32 bits of minstret, RV32I only.
|
|
|
|
|
|
#define SSTATUS 0x100
|
|
#define SIE 0x104
|
|
#define STVEC 0x105
|
|
#define SCOUNTEREN 0x106
|
|
#define SSCRATCH 0x140
|
|
#define SEPC 0x141
|
|
#define SCAUSE 0x142
|
|
#define STVAL 0x143
|
|
#define SIP 0x144
|
|
#define SATP 0x180
|
|
|
|
|
|
|
|
#define SSTATUS_SIE 0x00000002
|
|
#define SSTATUS_SPIE 0x00000020
|
|
#define SSTATUS_SPP 0x00000100
|
|
|
|
class RiscvGolden {
|
|
public:
|
|
int32_t pc, lastPc;
|
|
uint32_t lastInstruction;
|
|
int32_t regs[32];
|
|
|
|
uint32_t mscratch, sscratch;
|
|
uint32_t misa;
|
|
uint32_t privilege;
|
|
|
|
uint32_t medeleg;
|
|
uint32_t mideleg;
|
|
|
|
|
|
union status {
|
|
uint32_t raw;
|
|
struct {
|
|
uint32_t _1a : 1;
|
|
uint32_t sie : 1;
|
|
uint32_t _1b : 1;
|
|
uint32_t mie : 1;
|
|
uint32_t _2a : 1;
|
|
uint32_t spie : 1;
|
|
uint32_t _2b : 1;
|
|
uint32_t mpie : 1;
|
|
uint32_t spp : 1;
|
|
uint32_t _3 : 2;
|
|
uint32_t mpp : 2;
|
|
uint32_t _4 : 4;
|
|
uint32_t mprv : 1;
|
|
uint32_t sum : 1;
|
|
uint32_t mxr : 1;
|
|
};
|
|
}__attribute__((packed)) status;
|
|
|
|
|
|
|
|
uint32_t ipInput;
|
|
uint32_t ipSoft;
|
|
union IpOr {
|
|
uint32_t raw;
|
|
struct {
|
|
uint32_t _1a : 1;
|
|
uint32_t ssip : 1;
|
|
uint32_t _1b : 1;
|
|
uint32_t msip : 1;
|
|
uint32_t _2a : 1;
|
|
uint32_t stip : 1;
|
|
uint32_t _2b : 1;
|
|
uint32_t mtip : 1;
|
|
uint32_t _3a : 1;
|
|
uint32_t seip : 1;
|
|
uint32_t _3b : 1;
|
|
uint32_t meip : 1;
|
|
};
|
|
}__attribute__((packed));
|
|
|
|
IpOr getIp(){
|
|
IpOr ret;
|
|
ret.raw = ipSoft | ipInput;
|
|
return ret;
|
|
}
|
|
|
|
union mie {
|
|
uint32_t raw;
|
|
struct {
|
|
uint32_t _1a : 1;
|
|
uint32_t ssie : 1;
|
|
uint32_t _1b : 1;
|
|
uint32_t msie : 1;
|
|
uint32_t _2a : 1;
|
|
uint32_t stie : 1;
|
|
uint32_t _2b : 1;
|
|
uint32_t mtie : 1;
|
|
uint32_t _3a : 1;
|
|
uint32_t seie : 1;
|
|
uint32_t _3b : 1;
|
|
uint32_t meie : 1;
|
|
};
|
|
}__attribute__((packed)) ie;
|
|
|
|
union Xtvec {
|
|
uint32_t raw;
|
|
struct __attribute__((packed)) {
|
|
uint32_t _1 : 2;
|
|
uint32_t base : 30;
|
|
};
|
|
};
|
|
|
|
Xtvec mtvec, stvec;
|
|
|
|
|
|
|
|
union mcause {
|
|
uint32_t raw;
|
|
struct __attribute__((packed)) {
|
|
uint32_t exceptionCode : 31;
|
|
uint32_t interrupt : 1;
|
|
};
|
|
} mcause;
|
|
|
|
|
|
union scause {
|
|
uint32_t raw;
|
|
struct __attribute__((packed)){
|
|
uint32_t exceptionCode : 31;
|
|
uint32_t interrupt : 1;
|
|
};
|
|
} scause;
|
|
|
|
union satp {
|
|
uint32_t raw;
|
|
struct __attribute__((packed)){
|
|
uint32_t ppn : 22;
|
|
uint32_t _x : 9;
|
|
uint32_t mode : 1;
|
|
};
|
|
}satp;
|
|
|
|
union Tlb {
|
|
uint32_t raw;
|
|
struct __attribute__((packed)){
|
|
uint32_t v : 1;
|
|
uint32_t r : 1;
|
|
uint32_t w : 1;
|
|
uint32_t x : 1;
|
|
uint32_t u : 1;
|
|
uint32_t _dummy : 5;
|
|
uint32_t ppn : 22;
|
|
};
|
|
struct __attribute__((packed)){
|
|
uint32_t _dummyX : 10;
|
|
uint32_t ppn0 : 10;
|
|
uint32_t ppn1 : 12;
|
|
};
|
|
};
|
|
|
|
#define RESERVED_ENTRY_COUNT 1
|
|
struct ReservedEntry{
|
|
bool valid;
|
|
uint32_t address;
|
|
};
|
|
|
|
ReservedEntry reservedEntries[RESERVED_ENTRY_COUNT];
|
|
int reservedEntriesPtr = 0;
|
|
|
|
RiscvGolden() {
|
|
pc = 0x80000000;
|
|
regs[0] = 0;
|
|
for (int i = 0; i < 32; i++)
|
|
regs[i] = 0;
|
|
|
|
for(int i = 0;i < RESERVED_ENTRY_COUNT;i++) reservedEntries[i].valid = false;
|
|
|
|
|
|
status.raw = 0;
|
|
ie.raw = 0;
|
|
mtvec.raw = 0x80000020;
|
|
mcause.raw = 0;
|
|
mbadaddr = 0;
|
|
mepc = 0;
|
|
misa = 0; //TODO
|
|
status.raw = 0;
|
|
status.mpp = 3;
|
|
status.spp = 1;
|
|
privilege = 3;
|
|
medeleg = 0;
|
|
mideleg = 0;
|
|
satp.mode = 0;
|
|
ipSoft = 0;
|
|
ipInput = 0;
|
|
}
|
|
|
|
virtual void rfWrite(int32_t address, int32_t data) {
|
|
if (address != 0)
|
|
regs[address] = data;
|
|
}
|
|
|
|
virtual void pcWrite(int32_t target) {
|
|
if(isPcAligned(target)){
|
|
lastPc = pc;
|
|
pc = target;
|
|
} else {
|
|
trap(0, 0, target);
|
|
}
|
|
}
|
|
uint32_t mbadaddr, sbadaddr;
|
|
uint32_t mepc, sepc;
|
|
|
|
virtual bool iRead(int32_t address, uint32_t *data) = 0;
|
|
virtual bool dRead(int32_t address, int32_t size, uint32_t *data) = 0;
|
|
virtual void dWrite(int32_t address, int32_t size, uint32_t data) = 0;
|
|
|
|
enum AccessKind {READ,WRITE,EXECUTE};
|
|
virtual bool isMmuRegion(uint32_t v) = 0;
|
|
bool v2p(uint32_t v, uint32_t *p, AccessKind kind){
|
|
uint32_t effectivePrivilege = status.mprv && kind != EXECUTE ? status.mpp : privilege;
|
|
if(effectivePrivilege == 3 || satp.mode == 0 || !isMmuRegion(v)){
|
|
*p = v;
|
|
} else {
|
|
Tlb tlb;
|
|
dRead((satp.ppn << 12) | ((v >> 22) << 2), 4, &tlb.raw);
|
|
if(!tlb.v) return true;
|
|
bool superPage = true;
|
|
if(!tlb.x && !tlb.r && !tlb.w){
|
|
dRead((tlb.ppn << 12) | (((v >> 12) & 0x3FF) << 2), 4, &tlb.raw);
|
|
if(!tlb.v) return true;
|
|
superPage = false;
|
|
}
|
|
if(!tlb.u && effectivePrivilege == 0) return true;
|
|
if( tlb.u && effectivePrivilege == 1 && !status.sum) return true;
|
|
if(superPage && tlb.ppn0 != 0) return true;
|
|
switch(kind){
|
|
case READ: if(!tlb.r && !(status.mxr && tlb.x)) return true; break;
|
|
case WRITE: if(!tlb.w) return true; break;
|
|
case EXECUTE: if(!tlb.x) return true; break;
|
|
}
|
|
*p = (tlb.ppn1 << 22) | (superPage ? v & 0x3FF000 : tlb.ppn0 << 12) | (v & 0xFFF);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void trap(bool interrupt,int32_t cause) {
|
|
trap(interrupt, cause, false, 0);
|
|
}
|
|
void trap(bool interrupt,int32_t cause, uint32_t value) {
|
|
trap(interrupt, cause, true, value);
|
|
}
|
|
void trap(bool interrupt,int32_t cause, bool valueWrite, uint32_t value) {
|
|
#ifdef FLOW_INFO
|
|
cout << "TRAP " << (interrupt ? "interrupt" : "exception") << " cause=" << cause << " PC=0x" << hex << pc << " val=0x" << hex << value << dec << endl;
|
|
if(cause == 9){
|
|
cout << hex << " a7=0x" << regs[17] << " a0=0x" << regs[10] << " a1=0x" << regs[11] << " a2=0x" << regs[12] << dec << endl;
|
|
}
|
|
#endif
|
|
for(int i = 0;i < RESERVED_ENTRY_COUNT;i++) reservedEntries[i].valid = false;
|
|
//Check leguality of the interrupt
|
|
if(interrupt) {
|
|
bool hit = false;
|
|
for(int i = 0;i < 5;i++){
|
|
if(pendingInterrupts[i] == 1 << cause){
|
|
hit = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!hit){
|
|
cout << "DUT had trigger an interrupts which wasn't by the REF" << endl;
|
|
fail();
|
|
}
|
|
}
|
|
|
|
uint32_t deleg = interrupt ? mideleg : medeleg;
|
|
uint32_t targetPrivilege = 3;
|
|
if(deleg & (1 << cause)) targetPrivilege = 1;
|
|
targetPrivilege = max(targetPrivilege, privilege);
|
|
Xtvec xtvec = targetPrivilege == 3 ? mtvec : stvec;
|
|
|
|
switch(targetPrivilege){
|
|
case 3:
|
|
if(valueWrite) mbadaddr = value;
|
|
mcause.interrupt = interrupt;
|
|
mcause.exceptionCode = cause;
|
|
status.mpie = status.mie;
|
|
status.mie = false;
|
|
status.mpp = privilege;
|
|
mepc = pc;
|
|
break;
|
|
case 1:
|
|
if(valueWrite) sbadaddr = value;
|
|
scause.interrupt = interrupt;
|
|
scause.exceptionCode = cause;
|
|
status.spie = status.sie;
|
|
status.sie = false;
|
|
status.spp = privilege;
|
|
sepc = pc;
|
|
break;
|
|
}
|
|
|
|
privilege = targetPrivilege;
|
|
pcWrite(xtvec.base << 2);
|
|
if(interrupt) livenessInterrupt = 0;
|
|
|
|
if(!interrupt) step(); //As VexRiscv instruction which trap do not reach writeback stage fire
|
|
}
|
|
|
|
uint32_t currentInstruction;
|
|
void ilegalInstruction(){
|
|
trap(0, 2, currentInstruction);
|
|
}
|
|
|
|
virtual void fail() {
|
|
}
|
|
|
|
|
|
|
|
virtual bool csrRead(int32_t csr, uint32_t *value){
|
|
if(((csr >> 8) & 0x3) > privilege) return true;
|
|
switch(csr){
|
|
case MSTATUS: *value = status.raw; break;
|
|
case MIP: *value = getIp().raw; break;
|
|
case MIE: *value = ie.raw; break;
|
|
case MTVEC: *value = mtvec.raw; break;
|
|
case MCAUSE: *value = mcause.raw; break;
|
|
case MBADADDR: *value = mbadaddr; break;
|
|
case MEPC: *value = mepc; break;
|
|
case MSCRATCH: *value = mscratch; break;
|
|
case MISA: *value = misa; break;
|
|
case MEDELEG: *value = medeleg; break;
|
|
case MIDELEG: *value = mideleg; break;
|
|
|
|
case SSTATUS: *value = status.raw & 0xC0133; break;
|
|
case SIP: *value = getIp().raw & 0x333; break;
|
|
case SIE: *value = ie.raw & 0x333; break;
|
|
case STVEC: *value = stvec.raw; break;
|
|
case SCAUSE: *value = scause.raw; break;
|
|
case STVAL: *value = sbadaddr; break;
|
|
case SEPC: *value = sepc; break;
|
|
case SSCRATCH: *value = sscratch; break;
|
|
case SATP: *value = satp.raw; break;
|
|
default: return true; break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual uint32_t csrReadToWriteOverride(int32_t csr, uint32_t value){
|
|
if(((csr >> 8) & 0x3) > privilege) return true;
|
|
switch(csr){
|
|
case MIP: return ipSoft; break;
|
|
case SIP: return ipSoft & 0x333; break;
|
|
};
|
|
return value;
|
|
}
|
|
|
|
#define maskedWrite(dst, src, mask) dst=(dst & ~mask)|(src & mask);
|
|
|
|
virtual bool csrWrite(int32_t csr, uint32_t value){
|
|
if(((csr >> 8) & 0x3) > privilege) return true;
|
|
switch(csr){
|
|
case MSTATUS: status.raw = value; break;
|
|
case MIP: ipSoft = value; break;
|
|
case MIE: ie.raw = value; break;
|
|
case MTVEC: mtvec.raw = value; break;
|
|
case MCAUSE: mcause.raw = value; break;
|
|
case MBADADDR: mbadaddr = value; break;
|
|
case MEPC: mepc = value; break;
|
|
case MSCRATCH: mscratch = value; break;
|
|
case MISA: misa = value; break;
|
|
case MEDELEG: medeleg = value; break;
|
|
case MIDELEG: mideleg = value; break;
|
|
|
|
case SSTATUS: maskedWrite(status.raw, value,0xC0133); break;
|
|
case SIP: maskedWrite(ipSoft, value,0x333); break;
|
|
case SIE: maskedWrite(ie.raw, value,0x333); break;
|
|
case STVEC: stvec.raw = value; break;
|
|
case SCAUSE: scause.raw = value; break;
|
|
case STVAL: sbadaddr = value; break;
|
|
case SEPC: sepc = value; break;
|
|
case SSCRATCH: sscratch = value; break;
|
|
case SATP: satp.raw = value; break;
|
|
|
|
default: ilegalInstruction(); return true; break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
int livenessStep = 0;
|
|
int livenessInterrupt = 0;
|
|
uint32_t pendingInterruptsPtr = 0;
|
|
uint32_t pendingInterrupts[5] = {0,0,0,0,0};
|
|
virtual void liveness(bool inWfi){
|
|
uint32_t pendingInterrupt = getPendingInterrupt();
|
|
pendingInterrupts[pendingInterruptsPtr++] = getPendingInterrupt();
|
|
if(pendingInterruptsPtr >= 5) pendingInterruptsPtr = 0;
|
|
if(pendingInterrupt) livenessInterrupt++; else livenessInterrupt = 0;
|
|
if(!inWfi) livenessStep++; else livenessStep = 0;
|
|
|
|
if(livenessStep > 1000){
|
|
cout << "Liveness step failure" << endl;
|
|
fail();
|
|
}
|
|
|
|
if(livenessInterrupt > 1000){
|
|
cout << "Liveness interrupt failure" << endl;
|
|
fail();
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t getPendingInterrupt(){
|
|
uint32_t mEnabled = status.mie && privilege == 3 || privilege < 3;
|
|
uint32_t sEnabled = status.sie && privilege == 1 || privilege < 1;
|
|
|
|
uint32_t masked = getIp().raw & ~mideleg & -mEnabled & ie.raw;
|
|
if (masked == 0)
|
|
masked = getIp().raw & mideleg & -sEnabled & ie.raw & 0x333;
|
|
|
|
if (masked) {
|
|
if (masked & (MIP_MEIP | MIP_SEIP))
|
|
masked &= (MIP_MEIP | MIP_SEIP);
|
|
// software interrupts have next-highest priority
|
|
else if (masked & (MIP_MSIP | MIP_SSIP))
|
|
masked &= (MIP_MSIP | MIP_SSIP);
|
|
// timer interrupts have next-highest priority
|
|
else if (masked & (MIP_MTIP | MIP_STIP))
|
|
masked &= (MIP_MTIP | MIP_STIP);
|
|
else
|
|
fail();
|
|
}
|
|
|
|
return masked;
|
|
}
|
|
|
|
|
|
bool isPcAligned(uint32_t pc){
|
|
#ifdef COMPRESSED
|
|
return (pc & 1) == 0;
|
|
#else
|
|
return (pc & 3) == 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
virtual void step() {
|
|
livenessStep = 0;
|
|
#define rd32 ((i >> 7) & 0x1F)
|
|
#define iBits(lo, len) ((i >> lo) & ((1 << len)-1))
|
|
#define iBitsSigned(lo, len) int32_t(i) << (32-lo-len) >> (32-len)
|
|
#define iSign() iBitsSigned(31, 1)
|
|
#define i32_rs1 regs[(i >> 15) & 0x1F]
|
|
#define i32_rs2 regs[(i >> 20) & 0x1F]
|
|
#define i32_i_imm (int32_t(i) >> 20)
|
|
#define i32_s_imm (iBits(7, 5) + (iBitsSigned(25, 7) << 5))
|
|
#define i32_shamt ((i >> 20) & 0x1F)
|
|
#define i32_sb_imm ((iBits(8, 4) << 1) + (iBits(25,6) << 5) + (iBits(7,1) << 11) + (iSign() << 12))
|
|
#define i32_csr iBits(20, 12)
|
|
#define i32_func3 iBits(12, 3)
|
|
#define i16_addi4spn_imm ((iBits(6, 1) << 2) + (iBits(5, 1) << 3) + (iBits(11, 2) << 4) + (iBits(7, 4) << 6))
|
|
#define i16_lw_imm ((iBits(6, 1) << 2) + (iBits(10, 3) << 3) + (iBits(5, 1) << 6))
|
|
#define i16_addr2 (iBits(2,3) + 8)
|
|
#define i16_addr1 (iBits(7,3) + 8)
|
|
#define i16_rf1 regs[i16_addr1]
|
|
#define i16_rf2 regs[i16_addr2]
|
|
#define rf_sp regs[2]
|
|
#define i16_imm (iBits(2, 5) + (iBitsSigned(12, 1) << 5))
|
|
#define i16_j_imm ((iBits(3, 3) << 1) + (iBits(11, 1) << 4) + (iBits(2, 1) << 5) + (iBits(7, 1) << 6) + (iBits(6, 1) << 7) + (iBits(9, 2) << 8) + (iBits(8, 1) << 10) + (iBitsSigned(12, 1) << 11))
|
|
#define i16_addi16sp_imm ((iBits(6, 1) << 4) + (iBits(2, 1) << 5) + (iBits(5, 1) << 6) + (iBits(3, 2) << 7) + (iBitsSigned(12, 1) << 9))
|
|
#define i16_zimm (iBits(2, 5))
|
|
#define i16_b_imm ((iBits(3, 2) << 1) + (iBits(10, 2) << 3) + (iBits(2, 1) << 5) + (iBits(5, 2) << 6) + (iBitsSigned(12, 1) << 8))
|
|
#define i16_lwsp_imm ((iBits(4, 3) << 2) + (iBits(12, 1) << 5) + (iBits(2, 2) << 6))
|
|
#define i16_swsp_imm ((iBits(9, 4) << 2) + (iBits(7, 2) << 6))
|
|
uint32_t i;
|
|
uint32_t u32Buf;
|
|
uint32_t pAddr;
|
|
if (pc & 2) {
|
|
if(v2p(pc - 2, &pAddr, EXECUTE)){ trap(0, 12, pc - 2); return; }
|
|
if(iRead(pAddr, &i)){
|
|
trap(0, 1, 0);
|
|
return;
|
|
}
|
|
i >>= 16;
|
|
if (i & 3 == 3) {
|
|
uint32_t u32Buf;
|
|
if(v2p(pc + 2, &pAddr, EXECUTE)){ trap(0, 12, pc + 2); return; }
|
|
if(iRead(pAddr, &u32Buf)){
|
|
trap(0, 1, 0);
|
|
return;
|
|
}
|
|
i |= u32Buf << 16;
|
|
}
|
|
} else {
|
|
if(v2p(pc, &pAddr, EXECUTE)){ trap(0, 12, pc); return; }
|
|
if(iRead(pAddr, &i)){
|
|
trap(0, 1, 0);
|
|
return;
|
|
}
|
|
}
|
|
lastInstruction = i;
|
|
currentInstruction = i;
|
|
if ((i & 0x3) == 0x3) {
|
|
//32 bit
|
|
switch (i & 0x7F) {
|
|
case 0x37:rfWrite(rd32, i & 0xFFFFF000);pcWrite(pc + 4);break; // LUI
|
|
case 0x17:rfWrite(rd32, (i & 0xFFFFF000) + pc);pcWrite(pc + 4);break; //AUIPC
|
|
case 0x6F:rfWrite(rd32, pc + 4);pcWrite(pc + (iBits(21, 10) << 1) + (iBits(20, 1) << 11) + (iBits(12, 8) << 12) + (iSign() << 20));break; //JAL
|
|
case 0x67:{
|
|
uint32_t target = (i32_rs1 + i32_i_imm) & ~1;
|
|
if(isPcAligned(target)) rfWrite(rd32, pc + 4);
|
|
pcWrite(target);
|
|
} break; //JALR
|
|
case 0x63:
|
|
switch ((i >> 12) & 0x7) {
|
|
case 0x0:if (i32_rs1 == i32_rs2)pcWrite(pc + i32_sb_imm);else pcWrite(pc + 4);break;
|
|
case 0x1:if (i32_rs1 != i32_rs2)pcWrite(pc + i32_sb_imm);else pcWrite(pc + 4);break;
|
|
case 0x4:if (i32_rs1 < i32_rs2)pcWrite(pc + i32_sb_imm); else pcWrite(pc + 4);break;
|
|
case 0x5:if (i32_rs1 >= i32_rs2)pcWrite(pc + i32_sb_imm);else pcWrite(pc + 4);break;
|
|
case 0x6:if (uint32_t(i32_rs1) < uint32_t(i32_rs2)) pcWrite(pc + i32_sb_imm); else pcWrite(pc + 4);break;
|
|
case 0x7:if (uint32_t(i32_rs1) >= uint32_t(i32_rs2))pcWrite(pc + i32_sb_imm); else pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x03:{ //LOADS
|
|
uint32_t data;
|
|
uint32_t address = i32_rs1 + i32_i_imm;
|
|
uint32_t size = 1 << ((i >> 12) & 0x3);
|
|
if(address & (size-1)){
|
|
trap(0, 4, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
|
|
if(dRead(pAddr, size, &data)){
|
|
trap(0, 5, address);
|
|
} else {
|
|
switch ((i >> 12) & 0x7) {
|
|
case 0x0:rfWrite(rd32, int8_t(data));pcWrite(pc + 4);break;
|
|
case 0x1:rfWrite(rd32, int16_t(data));pcWrite(pc + 4);break;
|
|
case 0x2:rfWrite(rd32, int32_t(data));pcWrite(pc + 4);break;
|
|
case 0x4:rfWrite(rd32, uint8_t(data));pcWrite(pc + 4);break;
|
|
case 0x5:rfWrite(rd32, uint16_t(data));pcWrite(pc + 4);break;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
case 0x23: { //STORE
|
|
uint32_t address = i32_rs1 + i32_s_imm;
|
|
uint32_t size = 1 << ((i >> 12) & 0x3);
|
|
if(address & (size-1)){
|
|
trap(0, 6, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
|
|
dWrite(pAddr, size, i32_rs2);
|
|
pcWrite(pc + 4);
|
|
}
|
|
}break;
|
|
case 0x13: //ALUi
|
|
switch ((i >> 12) & 0x7) {
|
|
case 0x0:rfWrite(rd32, i32_rs1 + i32_i_imm);pcWrite(pc + 4);break;
|
|
case 0x1:
|
|
switch ((i >> 25) & 0x7F) {
|
|
case 0x00:rfWrite(rd32, i32_rs1 << i32_shamt);pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x2:rfWrite(rd32, i32_rs1 < i32_i_imm);pcWrite(pc + 4);break;
|
|
case 0x3:rfWrite(rd32, uint32_t(i32_rs1) < uint32_t(i32_i_imm));pcWrite(pc + 4);break;
|
|
case 0x4:rfWrite(rd32, i32_rs1 ^ i32_i_imm);pcWrite(pc + 4);break;
|
|
case 0x5:
|
|
switch ((i >> 25) & 0x7F) {
|
|
case 0x00:rfWrite(rd32, uint32_t(i32_rs1) >> i32_shamt);pcWrite(pc + 4);break;
|
|
case 0x20:rfWrite(rd32, i32_rs1 >> i32_shamt);pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x6:rfWrite(rd32, i32_rs1 | i32_i_imm);pcWrite(pc + 4);break;
|
|
case 0x7: rfWrite(rd32, i32_rs1 & i32_i_imm);pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x33: //ALU
|
|
if (((i >> 25) & 0x7F) == 0x01) {
|
|
switch ((i >> 12) & 0x7) {
|
|
case 0x0:rfWrite(rd32, int32_t(i32_rs1) * int32_t(i32_rs2));pcWrite(pc + 4);break;
|
|
case 0x1:rfWrite(rd32,(int64_t(i32_rs1) * int64_t(i32_rs2)) >> 32);pcWrite(pc + 4);break;
|
|
case 0x2:rfWrite(rd32,(int64_t(i32_rs1) * uint64_t(uint32_t(i32_rs2)))>> 32);pcWrite(pc + 4);break;
|
|
case 0x3:rfWrite(rd32,(uint64_t(uint32_t(i32_rs1)) * uint64_t(uint32_t(i32_rs2))) >> 32);pcWrite(pc + 4);break;
|
|
case 0x4:rfWrite(rd32,i32_rs2 == 0 ? -1 : int64_t(i32_rs1) / int64_t(i32_rs2));pcWrite(pc + 4);break;
|
|
case 0x5:rfWrite(rd32,i32_rs2 == 0 ? -1 : uint32_t(i32_rs1) / uint32_t(i32_rs2));pcWrite(pc + 4);break;
|
|
case 0x6:rfWrite(rd32,i32_rs2 == 0 ? i32_rs1 : int64_t(i32_rs1)% int64_t(i32_rs2));pcWrite(pc + 4);break;
|
|
case 0x7:rfWrite(rd32,i32_rs2 == 0 ? i32_rs1 : uint32_t(i32_rs1) % uint32_t(i32_rs2));pcWrite(pc + 4);break;
|
|
}
|
|
} else {
|
|
switch ((i >> 12) & 0x7) {
|
|
case 0x0:
|
|
switch ((i >> 25) & 0x7F) {
|
|
case 0x00:rfWrite(rd32, i32_rs1 + i32_rs2);pcWrite(pc + 4);break;
|
|
case 0x20:rfWrite(rd32, i32_rs1 - i32_rs2);pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x1:rfWrite(rd32, i32_rs1 << (i32_rs2 & 0x1F));pcWrite(pc + 4);break;
|
|
case 0x2:rfWrite(rd32, i32_rs1 < i32_rs2);pcWrite(pc + 4);break;
|
|
case 0x3:rfWrite(rd32, uint32_t(i32_rs1) < uint32_t(i32_rs2));pcWrite(pc + 4);break;
|
|
case 0x4:rfWrite(rd32, i32_rs1 ^ i32_rs2);pcWrite(pc + 4);break;
|
|
case 0x5:
|
|
switch ((i >> 25) & 0x7F) {
|
|
case 0x00:rfWrite(rd32, uint32_t(i32_rs1) >> (i32_rs2 & 0x1F));pcWrite(pc + 4);break;
|
|
case 0x20:rfWrite(rd32, i32_rs1 >> (i32_rs2 & 0x1F));pcWrite(pc + 4);break;
|
|
}
|
|
break;
|
|
case 0x6:rfWrite(rd32, i32_rs1 | i32_rs2);pcWrite(pc + 4);break;
|
|
case 0x7:rfWrite(rd32, i32_rs1 & i32_rs2); pcWrite(pc + 4);break;
|
|
}
|
|
}
|
|
break;
|
|
case 0x73:{
|
|
if(i32_func3 == 0){
|
|
switch(i){
|
|
case 0x30200073:{ //MRET
|
|
if(privilege < 3){ ilegalInstruction(); return;}
|
|
privilege = status.mpp;
|
|
status.mie = status.mpie;
|
|
status.mpie = 1;
|
|
status.mpp = 0;
|
|
pcWrite(mepc);
|
|
}break;
|
|
case 0x10200073:{ //SRET
|
|
if(privilege < 1){ ilegalInstruction(); return;}
|
|
privilege = status.spp;
|
|
status.sie = status.spie;
|
|
status.spie = 1;
|
|
status.spp = 0;
|
|
pcWrite(sepc);
|
|
}break;
|
|
case 0x00000073:{ //ECALL
|
|
trap(0, 8+privilege, 0x00000073); //To follow the VexRiscv area saving implementation
|
|
}break;
|
|
case 0x10500073:{ //WFI
|
|
pcWrite(pc + 4);
|
|
}break;
|
|
default:
|
|
if((i & 0xFE007FFF) == 0x12000073){ //SFENCE.VMA
|
|
pcWrite(pc + 4);
|
|
}else {
|
|
ilegalInstruction();
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
//CSR
|
|
uint32_t input = (i & 0x4000) ? ((i >> 15) & 0x1F) : i32_rs1;
|
|
uint32_t clear, set;
|
|
bool write;
|
|
switch ((i >> 12) & 0x3) {
|
|
case 1: clear = ~0; set = input; write = true; break;
|
|
case 2: clear = 0; set = input; write = ((i >> 15) & 0x1F) != 0; break;
|
|
case 3: clear = input; set = 0; write = ((i >> 15) & 0x1F) != 0; break;
|
|
}
|
|
uint32_t csrAddress = i32_csr;
|
|
uint32_t old;
|
|
if(csrRead(i32_csr, &old)) { ilegalInstruction();return; }
|
|
if(write) if(csrWrite(i32_csr, (csrReadToWriteOverride(i32_csr, old) & ~clear) | set)) { ilegalInstruction();return; }
|
|
rfWrite(rd32, old);
|
|
pcWrite(pc + 4);
|
|
}
|
|
break;
|
|
}
|
|
case 0x2F: // Atomic stuff
|
|
switch(i32_func3){
|
|
case 0x2:
|
|
switch(iBits(27,5)){
|
|
case 0x2:{ //LR
|
|
uint32_t data;
|
|
uint32_t address = i32_rs1;
|
|
if(address & 3){
|
|
trap(0, 4, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
|
|
if(dRead(pAddr, 4, &data)){
|
|
trap(0, 5, address);
|
|
} else {
|
|
reservedEntries[reservedEntriesPtr].valid = true;
|
|
reservedEntries[reservedEntriesPtr].address = address;
|
|
reservedEntriesPtr = (reservedEntriesPtr + 1) % RESERVED_ENTRY_COUNT;
|
|
rfWrite(rd32, data);
|
|
pcWrite(pc + 4);
|
|
}
|
|
}
|
|
} break;
|
|
case 0x3:{ //SC
|
|
uint32_t address = i32_rs1;
|
|
if(address & 3){
|
|
trap(0, 6, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
|
|
bool hit = false;
|
|
for(int i = 0;i < RESERVED_ENTRY_COUNT;i++) hit |= reservedEntries[i].valid && reservedEntries[i].address == address;
|
|
if(hit){
|
|
dWrite(pAddr, 4, i32_rs2);
|
|
}
|
|
rfWrite(rd32, !hit);
|
|
pcWrite(pc + 4);
|
|
}
|
|
} break;
|
|
default: ilegalInstruction(); break;
|
|
}
|
|
break;
|
|
default: ilegalInstruction(); break;
|
|
}
|
|
break;
|
|
case 0x0f:
|
|
if(i == 0x100F || (i & 0xF00FFFFF) == 0x000F){ // FENCE FENCE.I
|
|
pcWrite(pc + 4);
|
|
} else{
|
|
ilegalInstruction();
|
|
}
|
|
break;
|
|
default: ilegalInstruction(); break;
|
|
}
|
|
} else {
|
|
switch((iBits(0, 2) << 3) + iBits(13, 3)){
|
|
case 0: rfWrite(i16_addr2, rf_sp + i16_addi4spn_imm); pcWrite(pc + 2); break;
|
|
case 2: {
|
|
uint32_t data;
|
|
uint32_t address = i16_rf1 + i16_lw_imm;
|
|
if(address & 0x3){
|
|
trap(0, 4, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
|
|
if(dRead(pAddr, 4, &data)) {
|
|
trap(0, 5, address);
|
|
} else {
|
|
rfWrite(i16_addr2, data); pcWrite(pc + 2);
|
|
}
|
|
}
|
|
} break;
|
|
case 6: {
|
|
uint32_t address = i16_rf1 + i16_lw_imm;
|
|
if(address & 0x3){
|
|
trap(0, 6, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
|
|
dWrite(pAddr, 4, i16_rf2);
|
|
pcWrite(pc + 2);
|
|
}
|
|
}break;
|
|
case 8: rfWrite(rd32, regs[rd32] + i16_imm); pcWrite(pc + 2); break;
|
|
case 9: rfWrite(1, pc + 2);pcWrite(pc + i16_j_imm); break;
|
|
case 10: rfWrite(rd32, i16_imm);pcWrite(pc + 2); break;
|
|
case 11:
|
|
if(rd32 == 2) { rfWrite(2, rf_sp + i16_addi16sp_imm);pcWrite(pc + 2); }
|
|
else { rfWrite(rd32, i16_imm << 12);pcWrite(pc + 2); } break;
|
|
case 12:
|
|
switch(iBits(10,2)){
|
|
case 0: rfWrite(i16_addr1, uint32_t(i16_rf1) >> i16_zimm); pcWrite(pc + 2);break;
|
|
case 1: rfWrite(i16_addr1, i16_rf1 >> i16_zimm); pcWrite(pc + 2);break;
|
|
case 2: rfWrite(i16_addr1, i16_rf1 & i16_imm); pcWrite(pc + 2);break;
|
|
case 3:
|
|
switch(iBits(5,2)){
|
|
case 0: rfWrite(i16_addr1, i16_rf1 - i16_rf2); pcWrite(pc + 2);break;
|
|
case 1: rfWrite(i16_addr1, i16_rf1 ^ i16_rf2); pcWrite(pc + 2);break;
|
|
case 2: rfWrite(i16_addr1, i16_rf1 | i16_rf2); pcWrite(pc + 2);break;
|
|
case 3: rfWrite(i16_addr1, i16_rf1 & i16_rf2); pcWrite(pc + 2);break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 13: pcWrite(pc + i16_j_imm); break;
|
|
case 14: pcWrite(i16_rf1 == 0 ? pc + i16_b_imm : pc + 2); break;
|
|
case 15: pcWrite(i16_rf1 != 0 ? pc + i16_b_imm : pc + 2); break;
|
|
case 16: rfWrite(rd32, regs[rd32] << i16_zimm); pcWrite(pc + 2); break;
|
|
case 18:{
|
|
uint32_t data;
|
|
uint32_t address = rf_sp + i16_lwsp_imm;
|
|
if(address & 0x3){
|
|
trap(0, 4, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
|
|
if(dRead(pAddr, 4, &data)){
|
|
trap(0, 5, address);
|
|
} else {
|
|
rfWrite(rd32, data); pcWrite(pc + 2);
|
|
}
|
|
}
|
|
}break;
|
|
case 20:
|
|
if(i & 0x1000){
|
|
if(iBits(2,10) == 0){
|
|
|
|
} else if(iBits(2,5) == 0){
|
|
rfWrite(1, pc + 2); pcWrite(regs[rd32] & ~1);
|
|
} else {
|
|
rfWrite(rd32, regs[rd32] + regs[iBits(2,5)]); pcWrite(pc + 2);
|
|
}
|
|
} else {
|
|
if(iBits(2,5) == 0){
|
|
pcWrite(regs[rd32] & ~1);
|
|
} else {
|
|
rfWrite(rd32, regs[iBits(2,5)]); pcWrite(pc + 2);
|
|
}
|
|
}
|
|
break;
|
|
case 22: {
|
|
uint32_t address = rf_sp + i16_swsp_imm;
|
|
if(address & 3){
|
|
trap(0,6, address);
|
|
} else {
|
|
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
|
|
dWrite(pAddr, 4, regs[iBits(2,5)]); pcWrite(pc + 2);
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class SimElement{
|
|
public:
|
|
virtual ~SimElement(){}
|
|
virtual void onReset(){}
|
|
virtual void postReset(){}
|
|
virtual void preCycle(){}
|
|
virtual void postCycle(){}
|
|
};
|
|
|
|
|
|
|
|
class Workspace;
|
|
|
|
class Workspace{
|
|
public:
|
|
static mutex staticMutex;
|
|
static uint32_t testsCounter, successCounter;
|
|
static uint64_t cycles;
|
|
uint64_t instanceCycles = 0;
|
|
vector<SimElement*> simElements;
|
|
Memory mem;
|
|
string name;
|
|
uint64_t currentTime = 22;
|
|
uint64_t mTimeCmp = 0;
|
|
uint64_t mTime = 0;
|
|
VVexRiscv* top;
|
|
bool resetDone = false;
|
|
bool riscvRefEnable = false;
|
|
uint64_t i;
|
|
double cyclesPerSecond = 10e6;
|
|
double allowedCycles = 0.0;
|
|
uint32_t bootPc = -1;
|
|
uint32_t iStall = STALL,dStall = STALL;
|
|
#ifdef TRACE
|
|
VerilatedVcdC* tfp;
|
|
#endif
|
|
|
|
uint32_t seed;
|
|
|
|
bool withInstructionReadCheck = true;
|
|
Workspace* setIStall(bool enable) { iStall = enable; return this; }
|
|
Workspace* setDStall(bool enable) { dStall = enable; return this; }
|
|
|
|
ofstream regTraces;
|
|
ofstream memTraces;
|
|
ofstream logTraces;
|
|
ofstream debugLog;
|
|
|
|
struct timespec start_time;
|
|
|
|
class CpuRef : public RiscvGolden{
|
|
public:
|
|
Memory mem;
|
|
|
|
class MemWrite {
|
|
public:
|
|
int32_t address, size;
|
|
uint32_t data;
|
|
};
|
|
|
|
class MemRead {
|
|
public:
|
|
int32_t address, size;
|
|
uint32_t data;
|
|
bool error;
|
|
};
|
|
|
|
uint32_t periphWriteTimer = 0;
|
|
queue<MemWrite> periphWritesGolden;
|
|
queue<MemWrite> periphWrites;
|
|
queue<MemRead> periphRead;
|
|
Workspace *ws;
|
|
CpuRef(Workspace *ws){
|
|
this->ws = ws;
|
|
}
|
|
|
|
virtual void fail() { ws->fail(); }
|
|
|
|
|
|
virtual bool isMmuRegion(uint32_t v) {return ws->isMmuRegion(v);}
|
|
|
|
bool rfWriteValid;
|
|
int32_t rfWriteAddress;
|
|
int32_t rfWriteData;
|
|
virtual void rfWrite(int32_t address, int32_t data){
|
|
rfWriteValid = address != 0;
|
|
rfWriteAddress = address;
|
|
rfWriteData = data;
|
|
RiscvGolden::rfWrite(address,data);
|
|
}
|
|
|
|
|
|
virtual bool iRead(int32_t address, uint32_t *data){
|
|
bool error;
|
|
ws->iBusAccess(address, data, &error);
|
|
// ws->iBusAccessPatch(address,data,&error);
|
|
return error;
|
|
}
|
|
|
|
virtual bool dRead(int32_t address, int32_t size, uint32_t *data){
|
|
if(size < 1 || size > 4){
|
|
cout << "dRead size=" << size << endl;
|
|
fail();
|
|
}
|
|
if(address & (size-1) != 0)
|
|
cout << "Ref did a unaligned read" << endl;
|
|
if(ws->isPerifRegion(address)){
|
|
MemRead t = periphRead.front();
|
|
if(t.address != address || t.size != size){
|
|
cout << "DRead missmatch" << hex << endl;
|
|
cout << " REF : address=" << address << " size=" << size << endl;
|
|
cout << " DUT : address=" << t.address << " size=" << t.size << endl;
|
|
fail();
|
|
}
|
|
*data = t.data;
|
|
periphRead.pop();
|
|
return t.error;
|
|
}else {
|
|
mem.read(address, size, (uint8_t*)data);
|
|
}
|
|
return false;
|
|
}
|
|
virtual void dWrite(int32_t address, int32_t size, uint32_t data){
|
|
if(address & (size-1) != 0)
|
|
cout << "Ref did a unaligned write" << endl;
|
|
|
|
if(!ws->isPerifRegion(address)){
|
|
mem.write(address, size, (uint8_t*)&data);
|
|
}
|
|
if(ws->isDBusCheckedRegion(address)){
|
|
MemWrite w;
|
|
w.address = address;
|
|
w.size = size;
|
|
switch(size){
|
|
case 1: w.data = data & 0xFF; break;
|
|
case 2: w.data = data & 0xFFFF; break;
|
|
case 4: w.data = data; break;
|
|
}
|
|
periphWritesGolden.push(w);
|
|
if(periphWritesGolden.size() > 10){
|
|
cout << "??? periphWritesGolden" << endl;
|
|
fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void step() {
|
|
rfWriteValid = false;
|
|
RiscvGolden::step();
|
|
|
|
switch(periphWrites.empty() + uint32_t(periphWritesGolden.empty())*2){
|
|
case 3: periphWriteTimer = 0; break;
|
|
case 1: case 2: if(periphWriteTimer++ == 20){
|
|
cout << "periphWrite timout" << endl; fail();
|
|
} break;
|
|
case 0:
|
|
MemWrite t = periphWrites.front();
|
|
MemWrite t2 = periphWritesGolden.front();
|
|
if(t.address != t2.address || t.size != t2.size || t.data != t2.data){
|
|
cout << hex << "periphWrite missmatch" << endl;
|
|
cout << " DUT address=" << t.address << " size=" << t.size << " data=" << t.data << endl;
|
|
cout << " REF address=" << t2.address << " size=" << t2.size << " data=" << t2.data << endl;
|
|
fail();
|
|
}
|
|
periphWrites.pop();
|
|
periphWritesGolden.pop();
|
|
periphWriteTimer = 0;
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
};
|
|
|
|
CpuRef riscvRef = CpuRef(this);
|
|
|
|
Workspace(string name){
|
|
//seed = VL_RANDOM_I(32)^VL_RANDOM_I(32)^0x1093472;
|
|
//srand48(seed);
|
|
// setIStall(false);
|
|
// setDStall(false);
|
|
staticMutex.lock();
|
|
testsCounter++;
|
|
staticMutex.unlock();
|
|
this->name = name;
|
|
top = new VVexRiscv;
|
|
#ifdef TRACE_ACCESS
|
|
regTraces.open (name + ".regTrace");
|
|
memTraces.open (name + ".memTrace");hh
|
|
#endif
|
|
logTraces.open (name + ".logTrace");
|
|
debugLog.open (name + ".debugTrace");
|
|
fillSimELements();
|
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_time);
|
|
}
|
|
|
|
virtual ~Workspace(){
|
|
delete top;
|
|
#ifdef TRACE
|
|
delete tfp;
|
|
#endif
|
|
|
|
for(SimElement* simElement : simElements) {
|
|
delete simElement;
|
|
}
|
|
}
|
|
|
|
Workspace* loadHex(string path){
|
|
loadHexImpl(path,&mem);
|
|
loadHexImpl(path,&riscvRef.mem);
|
|
return this;
|
|
}
|
|
|
|
Workspace* loadBin(string path, uint32_t offset){
|
|
loadBinImpl(path,&mem, offset);
|
|
loadBinImpl(path,&riscvRef.mem, offset);
|
|
return this;
|
|
}
|
|
|
|
Workspace* setCyclesPerSecond(double value){
|
|
cyclesPerSecond = value;
|
|
return this;
|
|
}
|
|
|
|
Workspace* bootAt(uint32_t pc) {
|
|
bootPc = pc;
|
|
riscvRef.pc = pc;
|
|
return this;
|
|
}
|
|
|
|
Workspace* withRiscvRef(){
|
|
riscvRefEnable = true;
|
|
return this;
|
|
}
|
|
|
|
virtual bool isPerifRegion(uint32_t addr) { return false; }
|
|
virtual bool isMmuRegion(uint32_t addr) { return true;}
|
|
virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error) {
|
|
if(addr % 4 != 0) {
|
|
cout << "Warning, unaligned IBusAccess : " << addr << endl;
|
|
fail();
|
|
}
|
|
*data = ( (mem[addr + 0] << 0)
|
|
| (mem[addr + 1] << 8)
|
|
| (mem[addr + 2] << 16)
|
|
| (mem[addr + 3] << 24));
|
|
*error = false;
|
|
}
|
|
|
|
|
|
virtual bool isDBusCheckedRegion(uint32_t address){ return isPerifRegion(address);}
|
|
virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) {
|
|
assertEq(addr % (1 << size), 0);
|
|
if(!isPerifRegion(addr)) {
|
|
if(wr){
|
|
memTraces <<
|
|
#ifdef TRACE_WITH_TIME
|
|
(currentTime
|
|
#ifdef REF
|
|
-2
|
|
#endif
|
|
) <<
|
|
#endif
|
|
" : WRITE mem" << (1 << size) << "[" << addr << "] = " << *data << endl;
|
|
for(uint32_t b = 0;b < (1 << size);b++){
|
|
uint32_t offset = (addr+b)&0x3;
|
|
if((mask >> offset) & 1 == 1)
|
|
*mem.get(addr + b) = *data >> (offset*8);
|
|
}
|
|
|
|
}else{
|
|
*data = VL_RANDOM_I(32);
|
|
for(uint32_t b = 0;b < (1 << size);b++){
|
|
uint32_t offset = (addr+b)&0x3;
|
|
*data &= ~(0xFF << (offset*8));
|
|
*data |= mem[addr + b] << (offset*8);
|
|
}
|
|
memTraces <<
|
|
#ifdef TRACE_WITH_TIME
|
|
(currentTime
|
|
#ifdef REF
|
|
-2
|
|
#endif
|
|
) <<
|
|
#endif
|
|
" : READ mem" << (1 << size) << "[" << addr << "] = " << *data << endl;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if(wr){
|
|
if(isDBusCheckedRegion(addr)){
|
|
CpuRef::MemWrite w;
|
|
w.address = addr;
|
|
while((mask & 1) == 0){
|
|
mask >>= 1;
|
|
w.address++;
|
|
w.data >>= 8;
|
|
}
|
|
switch(mask){
|
|
case 1: size = 0; break;
|
|
case 3: size = min(1u, size); break;
|
|
case 15: size = min(2u, size); break;
|
|
}
|
|
w.size = 1 << size;
|
|
switch(size){
|
|
case 0: w.data = *data & 0xFF; break;
|
|
case 1: w.data = *data & 0xFFFF; break;
|
|
case 2: w.data = *data ; break;
|
|
}
|
|
riscvRef.periphWrites.push(w);
|
|
}
|
|
} else {
|
|
if(isPerifRegion(addr)){
|
|
CpuRef::MemRead r;
|
|
r.address = addr;
|
|
r.size = 1 << size;
|
|
r.data = *data;
|
|
r.error = *error;
|
|
riscvRef.periphRead.push(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
// void periphAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error){
|
|
// if(wr){
|
|
// CpuRef::MemWrite w;
|
|
// w.address = addr;
|
|
// w.size = 1 << size;
|
|
// w.data = *data;
|
|
// riscvRef.periphWrites.push(w);
|
|
// } else {
|
|
// CpuRef::MemRead r;
|
|
// r.address = addr;
|
|
// r.size = 1 << size;
|
|
// r.data = *data;
|
|
// r.error = *error;
|
|
// riscvRef.periphRead.push(r);
|
|
// }
|
|
// }
|
|
|
|
virtual void postReset() {}
|
|
virtual void checks(){}
|
|
virtual void pass(){ throw success();}
|
|
virtual void fail(){ throw std::exception();}
|
|
virtual void fillSimELements();
|
|
Workspace* noInstructionReadCheck(){withInstructionReadCheck = false; return this;}
|
|
void dump(int i){
|
|
#ifdef TRACE
|
|
if(i >= TRACE_START) tfp->dump(i);
|
|
#endif
|
|
}
|
|
Workspace* run(uint64_t timeout = 5000){
|
|
// cout << "Start " << name << endl;
|
|
if(timeout == 0) timeout = 0x7FFFFFFFFFFFFFFF;
|
|
|
|
currentTime = 4;
|
|
// init trace dump
|
|
#ifdef TRACE
|
|
Verilated::traceEverOn(true);
|
|
tfp = new VerilatedVcdC;
|
|
top->trace(tfp, 99);
|
|
tfp->open((string(name)+ ".vcd").c_str());
|
|
#endif
|
|
|
|
// Reset
|
|
top->clk = 0;
|
|
top->reset = 0;
|
|
|
|
|
|
top->eval(); currentTime = 3;
|
|
for(SimElement* simElement : simElements) simElement->onReset();
|
|
|
|
top->reset = 1;
|
|
top->eval();
|
|
top->clk = 1;
|
|
top->eval();
|
|
top->clk = 0;
|
|
top->eval();
|
|
#ifdef CSR
|
|
top->timerInterrupt = 0;
|
|
top->externalInterrupt = 1;
|
|
top->softwareInterrupt = 0;
|
|
#endif
|
|
#ifdef SUPERVISOR
|
|
top->externalInterruptS = 0;
|
|
#endif
|
|
#ifdef DEBUG_PLUGIN_EXTERNAL
|
|
top->timerInterrupt = 0;
|
|
top->externalInterrupt = 0;
|
|
#endif
|
|
dump(0);
|
|
top->reset = 0;
|
|
for(SimElement* simElement : simElements) simElement->postReset();
|
|
|
|
top->eval(); currentTime = 2;
|
|
|
|
|
|
postReset();
|
|
|
|
//Sync register file initial content
|
|
for(int i = 1;i < 32;i++){
|
|
riscvRef.regs[i] = top->VexRiscv->RegFilePlugin_regFile[i];
|
|
}
|
|
resetDone = true;
|
|
|
|
#ifdef REF
|
|
if(bootPc != -1) top->VexRiscv->core->prefetch_pc = bootPc;
|
|
#else
|
|
if(bootPc != -1) {
|
|
#if defined(IBUS_SIMPLE) || defined(IBUS_SIMPLE_WISHBONE)
|
|
top->VexRiscv->IBusSimplePlugin_fetchPc_pcReg = bootPc;
|
|
#ifdef COMPRESSED
|
|
top->VexRiscv->IBusSimplePlugin_decodePc_pcReg = bootPc;
|
|
#endif
|
|
#else
|
|
top->VexRiscv->IBusCachedPlugin_fetchPc_pcReg = bootPc;
|
|
#ifdef COMPRESSED
|
|
top->VexRiscv->IBusCachedPlugin_decodePc_pcReg = bootPc;
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
bool failed = false;
|
|
try {
|
|
// run simulation for 100 clock periods
|
|
for (i = 16; i < timeout*2; i+=2) {
|
|
/*while(allowedCycles <= 0.0){
|
|
struct timespec end_time;
|
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_time);
|
|
uint64_t diffInNanos = end_time.tv_sec*1e9 + end_time.tv_nsec - start_time.tv_sec*1e9 - start_time.tv_nsec;
|
|
start_time = end_time;
|
|
double dt = diffInNanos*1e-9;
|
|
allowedCycles += dt*cyclesPerSecond;
|
|
if(allowedCycles > cyclesPerSecond/100) allowedCycles = cyclesPerSecond/100;
|
|
}
|
|
allowedCycles-=1.0;*/
|
|
|
|
|
|
#ifndef REF_TIME
|
|
#ifndef MTIME_INSTR_FACTOR
|
|
mTime = i/2;
|
|
#else
|
|
mTime += top->VexRiscv->writeBack_arbitration_isFiring*MTIME_INSTR_FACTOR;
|
|
#endif
|
|
#endif
|
|
#ifdef TIMER_INTERRUPT
|
|
top->timerInterrupt = mTime >= mTimeCmp ? 1 : 0;
|
|
//if(mTime == mTimeCmp) printf("SIM timer tick\n");
|
|
#endif
|
|
|
|
currentTime = i;
|
|
|
|
#ifdef FLOW_INFO
|
|
if(i % 100000 == 0) cout << "PROGRESS TRACE_START=" << i << endl;
|
|
#endif
|
|
|
|
|
|
// dump variables into VCD file and toggle clock
|
|
|
|
dump(i);
|
|
//top->eval();
|
|
top->clk = 0;
|
|
top->eval();
|
|
|
|
#ifdef CSR
|
|
if(riscvRefEnable) {
|
|
riscvRef.ipInput = 0;
|
|
#ifdef TIMER_INTERRUPT
|
|
riscvRef.ipInput |= top->timerInterrupt << 7;
|
|
#endif
|
|
#ifdef EXTERNAL_INTERRUPT
|
|
riscvRef.ipInput |= top->externalInterrupt << 11;
|
|
#endif
|
|
#ifdef CSR
|
|
riscvRef.ipInput |= top->softwareInterrupt << 3;
|
|
#endif
|
|
#ifdef SUPERVISOR
|
|
// riscvRef.ipInput |= top->timerInterruptS << 5;
|
|
riscvRef.ipInput |= top->externalInterruptS << 9;
|
|
#endif
|
|
|
|
riscvRef.liveness(top->VexRiscv->execute_CsrPlugin_inWfi);
|
|
if(top->VexRiscv->CsrPlugin_interruptJump){
|
|
if(riscvRefEnable) riscvRef.trap(true, top->VexRiscv->CsrPlugin_interruptCode);
|
|
}
|
|
}
|
|
#endif
|
|
if(top->VexRiscv->writeBack_arbitration_isFiring){
|
|
if(riscvRefEnable) {
|
|
riscvRef.step();
|
|
bool mIntTimer = false;
|
|
bool mIntExt = false;
|
|
}
|
|
|
|
if(riscvRefEnable && top->VexRiscv->writeBack_PC != riscvRef.lastPc){
|
|
cout << hex << " pc missmatch " << top->VexRiscv->writeBack_PC << " should be " << riscvRef.lastPc << dec << endl;
|
|
fail();
|
|
}
|
|
|
|
|
|
bool rfWriteValid = false;
|
|
int32_t rfWriteAddress;
|
|
int32_t rfWriteData;
|
|
|
|
if(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_valid == 1 && top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address != 0){
|
|
rfWriteValid = true;
|
|
rfWriteAddress = top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address;
|
|
rfWriteData = top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_data;
|
|
#ifdef TRACE_ACCESS
|
|
regTraces <<
|
|
#ifdef TRACE_WITH_TIME
|
|
currentTime <<
|
|
#endif
|
|
" PC " << hex << setw(8) << top->VexRiscv->writeBack_PC << " : reg[" << dec << setw(2) << (uint32_t)top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address << "] = " << hex << setw(8) << top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_data << dec << endl;
|
|
#endif
|
|
} else {
|
|
#ifdef TRACE_ACCESS
|
|
regTraces <<
|
|
#ifdef TRACE_WITH_TIME
|
|
currentTime <<
|
|
#endif
|
|
" PC " << hex << setw(8) << top->VexRiscv->writeBack_PC << dec << endl;
|
|
#endif
|
|
}
|
|
if(riscvRefEnable) if(rfWriteValid != riscvRef.rfWriteValid ||
|
|
(rfWriteValid && (rfWriteAddress!= riscvRef.rfWriteAddress || rfWriteData!= riscvRef.rfWriteData))){
|
|
cout << "regFile write missmatch :" << endl;
|
|
if(rfWriteValid) cout << " REF: RF[" << riscvRef.rfWriteAddress << "] = 0x" << hex << riscvRef.rfWriteData << dec << endl;
|
|
if(rfWriteValid) cout << " DUT: RF[" << rfWriteAddress << "] = 0x" << hex << rfWriteData << dec << endl;
|
|
fail();
|
|
}
|
|
}
|
|
|
|
for(SimElement* simElement : simElements) simElement->preCycle();
|
|
|
|
dump(i + 1);
|
|
|
|
#ifndef COMPRESSED
|
|
if(withInstructionReadCheck){
|
|
if(top->VexRiscv->decode_arbitration_isValid && !top->VexRiscv->decode_arbitration_haltItself && !top->VexRiscv->decode_arbitration_flushAll){
|
|
uint32_t expectedData;
|
|
bool dummy;
|
|
iBusAccess(top->VexRiscv->decode_PC, &expectedData, &dummy);
|
|
assertEq(top->VexRiscv->decode_INSTRUCTION,expectedData);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
checks();
|
|
//top->eval();
|
|
top->clk = 1;
|
|
top->eval();
|
|
|
|
instanceCycles += 1;
|
|
|
|
for(SimElement* simElement : simElements) simElement->postCycle();
|
|
|
|
|
|
|
|
if (Verilated::gotFinish())
|
|
exit(0);
|
|
}
|
|
cout << "timeout" << endl;
|
|
fail();
|
|
} catch (const success e) {
|
|
staticMutex.lock();
|
|
cout <<"SUCCESS " << name << endl;
|
|
successCounter++;
|
|
cycles += instanceCycles;
|
|
staticMutex.unlock();
|
|
} catch (const std::exception& e) {
|
|
staticMutex.lock();
|
|
|
|
cout << "FAIL " << name << " at PC=" << hex << setw(8) << top->VexRiscv->writeBack_PC << dec; //<< " seed : " << seed <<
|
|
if(riscvRefEnable) cout << hex << " REF PC=" << riscvRef.lastPc << " REF I=" << riscvRef.lastInstruction << dec;
|
|
cout << endl;
|
|
|
|
cycles += instanceCycles;
|
|
staticMutex.unlock();
|
|
failed = true;
|
|
}
|
|
|
|
|
|
|
|
dump(i);
|
|
dump(i+10);
|
|
#ifdef TRACE
|
|
tfp->close();
|
|
#endif
|
|
#ifdef STOP_ON_ERROR
|
|
if(failed){
|
|
sleep(1);
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
return this;
|
|
}
|
|
};
|
|
|
|
|
|
class WorkspaceRegression : public Workspace {
|
|
public:
|
|
|
|
WorkspaceRegression(string name) : Workspace(name){
|
|
|
|
}
|
|
|
|
virtual bool isPerifRegion(uint32_t addr) { return (addr & 0xF0000000) == 0xF0000000;}
|
|
|
|
|
|
virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error){
|
|
Workspace::iBusAccess(addr,data,error);
|
|
*error = addr == 0xF00FFF60u;
|
|
}
|
|
|
|
virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) {
|
|
if(wr){
|
|
switch(addr){
|
|
case 0xF0010000u: {
|
|
cout << (char)*data;
|
|
logTraces << (char)*data;
|
|
break;
|
|
}
|
|
#ifdef EXTERNAL_INTERRUPT
|
|
case 0xF0011000u: top->externalInterrupt = *data & 1; break;
|
|
#endif
|
|
#ifdef SUPERVISOR
|
|
case 0xF0012000u: top->externalInterruptS = *data & 1; break;
|
|
#endif
|
|
#ifdef CSR
|
|
case 0xF0013000u: top->softwareInterrupt = *data & 1; break;
|
|
#endif
|
|
case 0xF00FFF00u: {
|
|
cout << (char)*data;
|
|
logTraces << (char)*data;
|
|
break;
|
|
}
|
|
#ifndef DEBUG_PLUGIN_EXTERNAL
|
|
case 0xF00FFF20u:
|
|
if(*data == 0)
|
|
pass();
|
|
else
|
|
fail();
|
|
break;
|
|
case 0xF00FFF24u:
|
|
cout << "TEST ERROR CODE " << *data << endl;
|
|
fail();
|
|
break;
|
|
#endif
|
|
case 0xF00FFF48u: mTimeCmp = (mTimeCmp & 0xFFFFFFFF00000000) | *data;break;
|
|
case 0xF00FFF4Cu: mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)*data) << 32); break;
|
|
}
|
|
}else{
|
|
switch(addr){
|
|
case 0xF00FFF10u:
|
|
*data = mTime;
|
|
#ifdef REF_TIME
|
|
mTime += 100000;
|
|
#endif
|
|
break;
|
|
case 0xF00FFF40u: *data = mTime; break;
|
|
case 0xF00FFF44u: *data = mTime >> 32; break;
|
|
case 0xF00FFF48u: *data = mTimeCmp; break;
|
|
case 0xF00FFF4Cu: *data = mTimeCmp >> 32; break;
|
|
case 0xF0010004u: *data = ~0; break;
|
|
}
|
|
memTraces <<
|
|
#ifdef TRACE_WITH_TIME
|
|
(currentTime
|
|
#ifdef REF
|
|
-2
|
|
#endif
|
|
) <<
|
|
#endif
|
|
" : READ mem" << (1 << size) << "[" << addr << "] = " << *data << endl;
|
|
}
|
|
|
|
*error = addr == 0xF00FFF60u;
|
|
Workspace::dBusAccess(addr,wr,size,mask,data,error);
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
#ifdef IBUS_SIMPLE
|
|
class IBusSimple : public SimElement{
|
|
public:
|
|
uint32_t pendings[256];
|
|
uint32_t rPtr = 0, wPtr = 0;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
IBusSimple(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->iBus_cmd_ready = 1;
|
|
top->iBus_rsp_valid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->iBus_cmd_valid && top->iBus_cmd_ready) {
|
|
//assertEq(top->iBus_cmd_payload_pc & 3,0);
|
|
pendings[wPtr] = (top->iBus_cmd_payload_pc);
|
|
wPtr = (wPtr + 1) & 0xFF;
|
|
//ws->iBusAccess(top->iBus_cmd_payload_pc,&inst_next,&error_next);
|
|
}
|
|
}
|
|
//TODO doesn't catch when instruction removed ?
|
|
virtual void postCycle(){
|
|
top->iBus_rsp_valid = 0;
|
|
if(rPtr != wPtr && (!ws->iStall || VL_RANDOM_I(7) < 100)){
|
|
uint32_t inst_next;
|
|
bool error_next;
|
|
ws->iBusAccess(pendings[rPtr], &inst_next,&error_next);
|
|
rPtr = (rPtr + 1) & 0xFF;
|
|
top->iBus_rsp_payload_inst = inst_next;
|
|
top->iBus_rsp_valid = 1;
|
|
top->iBus_rsp_payload_error = error_next;
|
|
} else {
|
|
top->iBus_rsp_payload_inst = VL_RANDOM_I(32);
|
|
top->iBus_rsp_payload_error = VL_RANDOM_I(1);
|
|
}
|
|
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(7) < 100;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef IBUS_TC
|
|
|
|
class IBusTc : public SimElement{
|
|
public:
|
|
|
|
uint32_t nextData;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
IBusTc(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->iBusTc_enable) {
|
|
if((top->iBusTc_address & 0x70000000) != 0 || (top->iBusTc_address & 0x20) == 0){
|
|
printf("IBusTc access out of range\n");
|
|
ws->fail();
|
|
}
|
|
bool error_next;
|
|
ws->iBusAccess(top->iBusTc_address, &nextData,&error_next);
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
top->iBusTc_data = nextData;
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef IBUS_SIMPLE_AVALON
|
|
|
|
struct IBusSimpleAvalonRsp{
|
|
uint32_t data;
|
|
bool error;
|
|
};
|
|
|
|
|
|
class IBusSimpleAvalon : public SimElement{
|
|
public:
|
|
queue<IBusSimpleAvalonRsp> rsps;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
IBusSimpleAvalon(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->iBusAvalon_waitRequestn = 1;
|
|
top->iBusAvalon_readDataValid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->iBusAvalon_read && top->iBusAvalon_waitRequestn) {
|
|
IBusSimpleAvalonRsp rsp;
|
|
ws->iBusAccess(top->iBusAvalon_address,&rsp.data,&rsp.error);
|
|
rsps.push(rsp);
|
|
}
|
|
}
|
|
//TODO doesn't catch when instruction removed ?
|
|
virtual void postCycle(){
|
|
if(!rsps.empty() && (!ws->iStall || VL_RANDOM_I(7) < 100)){
|
|
IBusSimpleAvalonRsp rsp = rsps.front(); rsps.pop();
|
|
top->iBusAvalon_readDataValid = 1;
|
|
top->iBusAvalon_readData = rsp.data;
|
|
top->iBusAvalon_response = rsp.error ? 3 : 0;
|
|
} else {
|
|
top->iBusAvalon_readDataValid = 0;
|
|
top->iBusAvalon_readData = VL_RANDOM_I(32);
|
|
top->iBusAvalon_response = VL_RANDOM_I(2);
|
|
}
|
|
if(ws->iStall)
|
|
top->iBusAvalon_waitRequestn = VL_RANDOM_I(7) < 100;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef IBUS_CACHED
|
|
class IBusCached : public SimElement{
|
|
public:
|
|
bool error_next = false;
|
|
uint32_t pendingCount = 0;
|
|
uint32_t address;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
IBusCached(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
|
|
virtual void onReset(){
|
|
top->iBus_cmd_ready = 1;
|
|
top->iBus_rsp_valid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->iBus_cmd_valid && top->iBus_cmd_ready && pendingCount == 0) {
|
|
assertEq(top->iBus_cmd_payload_address & 3,0);
|
|
pendingCount = (1 << top->iBus_cmd_payload_size)/4;
|
|
address = top->iBus_cmd_payload_address;
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
bool error;
|
|
top->iBus_rsp_valid = 0;
|
|
if(pendingCount != 0 && (!ws->iStall || VL_RANDOM_I(7) < 100)){
|
|
#ifdef IBUS_TC
|
|
if((address & 0x70000000) == 0 && (address & 0x20) != 0){
|
|
printf("IBUS_CACHED access out of range\n");
|
|
ws->fail();
|
|
}
|
|
#endif
|
|
ws->iBusAccess(address,&top->iBus_rsp_payload_data,&error);
|
|
top->iBus_rsp_payload_error = error;
|
|
pendingCount--;
|
|
address = address + 4;
|
|
top->iBus_rsp_valid = 1;
|
|
}
|
|
if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(7) < 100 && pendingCount == 0;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef IBUS_CACHED_AVALON
|
|
#include <queue>
|
|
|
|
struct IBusCachedAvalonTask{
|
|
uint32_t address;
|
|
uint32_t pendingCount;
|
|
};
|
|
|
|
class IBusCachedAvalon : public SimElement{
|
|
public:
|
|
uint32_t inst_next = VL_RANDOM_I(32);
|
|
bool error_next = false;
|
|
|
|
queue<IBusCachedAvalonTask> tasks;
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
|
|
IBusCachedAvalon(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->iBusAvalon_waitRequestn = 1;
|
|
top->iBusAvalon_readDataValid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->iBusAvalon_read && top->iBusAvalon_waitRequestn) {
|
|
assertEq(top->iBusAvalon_address & 3,0);
|
|
IBusCachedAvalonTask task;
|
|
task.address = top->iBusAvalon_address;
|
|
task.pendingCount = top->iBusAvalon_burstCount;
|
|
tasks.push(task);
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
bool error;
|
|
top->iBusAvalon_readDataValid = 0;
|
|
if(!tasks.empty() && (!ws->iStall || VL_RANDOM_I(7) < 100)){
|
|
uint32_t &address = tasks.front().address;
|
|
uint32_t &pendingCount = tasks.front().pendingCount;
|
|
bool error;
|
|
ws->iBusAccess(address,&top->iBusAvalon_readData,&error);
|
|
top->iBusAvalon_response = error ? 3 : 0;
|
|
pendingCount--;
|
|
address = (address & ~0x1F) + ((address + 4) & 0x1F);
|
|
top->iBusAvalon_readDataValid = 1;
|
|
if(pendingCount == 0)
|
|
tasks.pop();
|
|
}
|
|
if(ws->iStall)
|
|
top->iBusAvalon_waitRequestn = VL_RANDOM_I(7) < 100;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
#if defined(IBUS_CACHED_WISHBONE) || defined(IBUS_SIMPLE_WISHBONE)
|
|
#include <queue>
|
|
|
|
class IBusCachedWishbone : public SimElement{
|
|
public:
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
|
|
IBusCachedWishbone(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->iBusWishbone_ACK = !ws->iStall;
|
|
top->iBusWishbone_ERR = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
|
|
if(ws->iStall)
|
|
top->iBusWishbone_ACK = VL_RANDOM_I(7) < 100;
|
|
|
|
top->iBusWishbone_DAT_MISO = VL_RANDOM_I(32);
|
|
if (top->iBusWishbone_CYC && top->iBusWishbone_STB && top->iBusWishbone_ACK) {
|
|
if(top->iBusWishbone_WE){
|
|
|
|
} else {
|
|
bool error;
|
|
ws->iBusAccess(top->iBusWishbone_ADR << 2,&top->iBusWishbone_DAT_MISO,&error);
|
|
top->iBusWishbone_ERR = error;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef DBUS_SIMPLE
|
|
class DBusSimple : public SimElement{
|
|
public:
|
|
uint32_t data_next = VL_RANDOM_I(32);
|
|
bool error_next = false;
|
|
bool pending = false;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
DBusSimple(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->dBus_cmd_ready = 1;
|
|
top->dBus_rsp_ready = 1;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->dBus_cmd_valid && top->dBus_cmd_ready) {
|
|
pending = true;
|
|
data_next = top->dBus_cmd_payload_data;
|
|
ws->dBusAccess(top->dBus_cmd_payload_address,top->dBus_cmd_payload_wr,top->dBus_cmd_payload_size,0xF,&data_next,&error_next);
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
top->dBus_rsp_ready = 0;
|
|
if(pending && (!ws->dStall || VL_RANDOM_I(7) < 100)){
|
|
pending = false;
|
|
top->dBus_rsp_ready = 1;
|
|
top->dBus_rsp_data = data_next;
|
|
top->dBus_rsp_error = error_next;
|
|
} else{
|
|
top->dBus_rsp_data = VL_RANDOM_I(32);
|
|
}
|
|
|
|
if(ws->dStall) top->dBus_cmd_ready = VL_RANDOM_I(7) < 100 && !pending;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef DBUS_SIMPLE_AVALON
|
|
#include <queue>
|
|
struct DBusSimpleAvalonRsp{
|
|
uint32_t data;
|
|
bool error;
|
|
};
|
|
|
|
|
|
class DBusSimpleAvalon : public SimElement{
|
|
public:
|
|
queue<DBusSimpleAvalonRsp> rsps;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
DBusSimpleAvalon(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->dBusAvalon_waitRequestn = 1;
|
|
top->dBusAvalon_readDataValid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
if (top->dBusAvalon_write && top->dBusAvalon_waitRequestn) {
|
|
bool dummy;
|
|
ws->dBusAccess(top->dBusAvalon_address,1,2,top->dBusAvalon_byteEnable,&top->dBusAvalon_writeData,&dummy);
|
|
}
|
|
if (top->dBusAvalon_read && top->dBusAvalon_waitRequestn) {
|
|
DBusSimpleAvalonRsp rsp;
|
|
ws->dBusAccess(top->dBusAvalon_address,0,2,0xF,&rsp.data,&rsp.error);
|
|
rsps.push(rsp);
|
|
}
|
|
}
|
|
//TODO doesn't catch when instruction removed ?
|
|
virtual void postCycle(){
|
|
if(!rsps.empty() && (!ws->iStall || VL_RANDOM_I(7) < 100)){
|
|
DBusSimpleAvalonRsp rsp = rsps.front(); rsps.pop();
|
|
top->dBusAvalon_readDataValid = 1;
|
|
top->dBusAvalon_readData = rsp.data;
|
|
top->dBusAvalon_response = rsp.error ? 3 : 0;
|
|
} else {
|
|
top->dBusAvalon_readDataValid = 0;
|
|
top->dBusAvalon_readData = VL_RANDOM_I(32);
|
|
top->dBusAvalon_response = VL_RANDOM_I(2);
|
|
}
|
|
if(ws->iStall)
|
|
top->dBusAvalon_waitRequestn = VL_RANDOM_I(7) < 100;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#if defined(DBUS_CACHED_WISHBONE) || defined(DBUS_SIMPLE_WISHBONE)
|
|
#include <queue>
|
|
|
|
|
|
class DBusCachedWishbone : public SimElement{
|
|
public:
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
|
|
DBusCachedWishbone(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->dBusWishbone_ACK = !ws->iStall;
|
|
top->dBusWishbone_ERR = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
if(ws->iStall)
|
|
top->dBusWishbone_ACK = VL_RANDOM_I(7) < 100;
|
|
top->dBusWishbone_DAT_MISO = VL_RANDOM_I(32);
|
|
if (top->dBusWishbone_CYC && top->dBusWishbone_STB && top->dBusWishbone_ACK) {
|
|
if(top->dBusWishbone_WE){
|
|
bool dummy;
|
|
ws->dBusAccess(top->dBusWishbone_ADR << 2 ,1,2,top->dBusWishbone_SEL,&top->dBusWishbone_DAT_MOSI,&dummy);
|
|
} else {
|
|
bool error;
|
|
ws->dBusAccess(top->dBusWishbone_ADR << 2,0,2,0xF,&top->dBusWishbone_DAT_MISO,&error);
|
|
top->dBusWishbone_ERR = error;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef DBUS_CACHED
|
|
|
|
//#include "VVexRiscv_DataCache.h"
|
|
|
|
class DBusCached : public SimElement{
|
|
public:
|
|
uint32_t address;
|
|
bool error_next = false;
|
|
uint32_t pendingCount = 0;
|
|
bool wr;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
DBusCached(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->dBus_cmd_ready = 1;
|
|
top->dBus_rsp_valid = 0;
|
|
}
|
|
|
|
virtual void preCycle(){
|
|
VL_IN8(io_cpu_execute_isValid,0,0);
|
|
VL_IN8(io_cpu_execute_isStuck,0,0);
|
|
VL_IN8(io_cpu_execute_args_kind,0,0);
|
|
VL_IN8(io_cpu_execute_args_wr,0,0);
|
|
VL_IN8(io_cpu_execute_args_size,1,0);
|
|
VL_IN8(io_cpu_execute_args_forceUncachedAccess,0,0);
|
|
VL_IN8(io_cpu_execute_args_clean,0,0);
|
|
VL_IN8(io_cpu_execute_args_invalidate,0,0);
|
|
VL_IN8(io_cpu_execute_args_way,0,0);
|
|
|
|
// if(top->VexRiscv->dataCache_1->io_cpu_execute_isValid && !top->VexRiscv->dataCache_1->io_cpu_execute_isStuck
|
|
// && top->VexRiscv->dataCache_1->io_cpu_execute_args_wr){
|
|
// if(top->VexRiscv->dataCache_1->io_cpu_execute_args_address == 0x80025978)
|
|
// cout << "WR 0x80025978 = " << hex << setw(8) << top->VexRiscv->dataCache_1->io_cpu_execute_args_data << endl;
|
|
// if(top->VexRiscv->dataCache_1->io_cpu_execute_args_address == 0x8002596c)
|
|
// cout << "WR 0x8002596c = " << hex << setw(8) << top->VexRiscv->dataCache_1->io_cpu_execute_args_data << endl;
|
|
// }
|
|
if (top->dBus_cmd_valid && top->dBus_cmd_ready) {
|
|
if(pendingCount == 0){
|
|
pendingCount = top->dBus_cmd_payload_length+1;
|
|
address = top->dBus_cmd_payload_address;
|
|
wr = top->dBus_cmd_payload_wr;
|
|
}
|
|
if(top->dBus_cmd_payload_wr){
|
|
ws->dBusAccess(address,top->dBus_cmd_payload_wr,2,top->dBus_cmd_payload_mask,&top->dBus_cmd_payload_data,&error_next);
|
|
address += 4;
|
|
pendingCount--;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
if(pendingCount != 0 && !wr && (!ws->dStall || VL_RANDOM_I(7) < 100)){
|
|
ws->dBusAccess(address,0,2,0,&top->dBus_rsp_payload_data,&error_next);
|
|
top->dBus_rsp_payload_error = error_next;
|
|
top->dBus_rsp_valid = 1;
|
|
address += 4;
|
|
pendingCount--;
|
|
} else{
|
|
top->dBus_rsp_valid = 0;
|
|
top->dBus_rsp_payload_data = VL_RANDOM_I(32);
|
|
top->dBus_rsp_payload_error = VL_RANDOM_I(1);
|
|
}
|
|
|
|
top->dBus_cmd_ready = (ws->dStall ? VL_RANDOM_I(7) < 100 : 1) && (pendingCount == 0 || wr);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef DBUS_CACHED_AVALON
|
|
#include <queue>
|
|
|
|
struct DBusCachedAvalonTask{
|
|
uint32_t data;
|
|
bool error;
|
|
};
|
|
|
|
class DBusCachedAvalon : public SimElement{
|
|
public:
|
|
uint32_t beatCounter = 0;
|
|
queue<DBusCachedAvalonTask> rsps;
|
|
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
DBusCachedAvalon(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->dBusAvalon_waitRequestn = 1;
|
|
top->dBusAvalon_readDataValid = 0;
|
|
}
|
|
|
|
|
|
virtual void preCycle(){
|
|
if ((top->dBusAvalon_read || top->dBusAvalon_write) && top->dBusAvalon_waitRequestn) {
|
|
if(top->dBusAvalon_write){
|
|
bool error_next = false;
|
|
ws->dBusAccess(top->dBusAvalon_address + beatCounter * 4,1,2,top->dBusAvalon_byteEnable,&top->dBusAvalon_writeData,&error_next);
|
|
beatCounter++;
|
|
if(beatCounter == top->dBusAvalon_burstCount){
|
|
beatCounter = 0;
|
|
}
|
|
} else {
|
|
for(int beat = 0;beat < top->dBusAvalon_burstCount;beat++){
|
|
DBusCachedAvalonTask rsp;
|
|
ws->dBusAccess(top->dBusAvalon_address + beat * 4,0,2,0,&rsp.data,&rsp.error);
|
|
rsps.push(rsp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
if(!rsps.empty() && (!ws->dStall || VL_RANDOM_I(7) < 100)){
|
|
DBusCachedAvalonTask rsp = rsps.front();
|
|
rsps.pop();
|
|
top->dBusAvalon_response = rsp.error ? 3 : 0;
|
|
top->dBusAvalon_readData = rsp.data;
|
|
top->dBusAvalon_readDataValid = 1;
|
|
} else{
|
|
top->dBusAvalon_readDataValid = 0;
|
|
top->dBusAvalon_readData = VL_RANDOM_I(32);
|
|
top->dBusAvalon_response = VL_RANDOM_I(2); //TODO
|
|
}
|
|
|
|
top->dBusAvalon_waitRequestn = (ws->dStall ? VL_RANDOM_I(7) < 100 : 1);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUG_PLUGIN
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
/** Returns true on success, or false if there was an error */
|
|
bool SetSocketBlockingEnabled(int fd, bool blocking)
|
|
{
|
|
if (fd < 0) return false;
|
|
|
|
#ifdef WIN32
|
|
unsigned long mode = blocking ? 0 : 1;
|
|
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
|
|
#else
|
|
int flags = fcntl(fd, F_GETFL, 0);
|
|
if (flags < 0) return false;
|
|
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
|
|
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
|
|
#endif
|
|
}
|
|
|
|
struct DebugPluginTask{
|
|
bool wr;
|
|
uint32_t address;
|
|
uint32_t data;
|
|
};
|
|
|
|
class DebugPlugin : public SimElement{
|
|
public:
|
|
Workspace *ws;
|
|
VVexRiscv* top;
|
|
|
|
int serverSocket, clientHandle;
|
|
struct sockaddr_in serverAddr;
|
|
struct sockaddr_storage serverStorage;
|
|
socklen_t addr_size;
|
|
char buffer[1024];
|
|
uint32_t timeSpacer = 0;
|
|
bool taskValid = false;
|
|
DebugPluginTask task;
|
|
|
|
|
|
DebugPlugin(Workspace* ws){
|
|
this->ws = ws;
|
|
this->top = ws->top;
|
|
|
|
#ifdef DEBUG_PLUGIN_EXTERNAL
|
|
ws->mTimeCmp = ~0;
|
|
#endif
|
|
top->debugReset = 0;
|
|
|
|
|
|
|
|
//---- Create the socket. The three arguments are: ----//
|
|
// 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) //
|
|
serverSocket = socket(PF_INET, SOCK_STREAM, 0);
|
|
assert(serverSocket != -1);
|
|
SetSocketBlockingEnabled(serverSocket,0);
|
|
int flag = 1;
|
|
int result = setsockopt(serverSocket, /* socket affected */
|
|
IPPROTO_TCP, /* set option at TCP level */
|
|
TCP_NODELAY, /* name of option */
|
|
(char *) &flag, /* the cast is historical
|
|
cruft */
|
|
sizeof(int)); /* length of option value */
|
|
|
|
//---- Configure settings of the server address struct ----//
|
|
// Address family = Internet //
|
|
serverAddr.sin_family = AF_INET;
|
|
// Set port number, using htons function to use proper byte order //
|
|
serverAddr.sin_port = htons(7893);
|
|
// Set IP address to localhost //
|
|
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
// Set all bits of the padding field to 0 //
|
|
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
|
|
|
|
//---- Bind the address struct to the socket ----//
|
|
bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
|
|
|
|
//---- Listen on the socket, with 5 max connection requests queued ----//
|
|
listen(serverSocket,1);
|
|
|
|
//---- Accept call creates a new socket for the incoming connection ----//
|
|
addr_size = sizeof serverStorage;
|
|
|
|
clientHandle = -1;
|
|
}
|
|
|
|
virtual ~DebugPlugin(){
|
|
if(clientHandle != -1) {
|
|
shutdown(clientHandle,SHUT_RDWR);
|
|
usleep(100);
|
|
}
|
|
if(serverSocket != -1) {
|
|
close(serverSocket);
|
|
usleep(100);
|
|
}
|
|
}
|
|
|
|
virtual void onReset(){
|
|
top->debugReset = 1;
|
|
}
|
|
|
|
|
|
|
|
virtual void postReset(){
|
|
top->debugReset = 0;
|
|
}
|
|
|
|
void connectionReset(){
|
|
printf("CONNECTION RESET\n");
|
|
shutdown(clientHandle,SHUT_RDWR);
|
|
clientHandle = -1;
|
|
}
|
|
|
|
|
|
virtual void preCycle(){
|
|
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
top->reset = top->debug_resetOut;
|
|
if(timeSpacer == 0){
|
|
if(clientHandle == -1){
|
|
clientHandle = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);
|
|
if(clientHandle != -1)
|
|
printf("CONNECTED\n");
|
|
timeSpacer = 1000;
|
|
}
|
|
|
|
|
|
if(clientHandle != -1 && taskValid == false){
|
|
int requiredSize = 1 + 1 + 4 + 4;
|
|
int n;
|
|
timeSpacer = 20;
|
|
if(ioctl(clientHandle,FIONREAD,&n) != 0){
|
|
connectionReset();
|
|
} else if(n >= requiredSize){
|
|
if(requiredSize != read(clientHandle,buffer,requiredSize)){
|
|
connectionReset();
|
|
} else {
|
|
bool wr = buffer[0];
|
|
uint32_t size = buffer[1];
|
|
uint32_t address = *((uint32_t*)(buffer + 2));
|
|
uint32_t data = *((uint32_t*)(buffer + 6));
|
|
|
|
if((address & ~ 0x4) == 0xF00F0000){
|
|
assert(size == 2);
|
|
timeSpacer = 100;
|
|
|
|
taskValid = true;
|
|
task.wr = wr;
|
|
task.address = address;
|
|
task.data = data;
|
|
}
|
|
}
|
|
} else {
|
|
int error = 0;
|
|
socklen_t len = sizeof (error);
|
|
int retval = getsockopt (clientHandle, SOL_SOCKET, SO_ERROR, &error, &len);
|
|
if (retval != 0 || error != 0) {
|
|
connectionReset();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
timeSpacer--;
|
|
}
|
|
}
|
|
|
|
void sendRsp(uint32_t data){
|
|
if(clientHandle != -1){
|
|
if(send(clientHandle,&data,4,0) == -1) connectionReset();
|
|
}
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef DEBUG_PLUGIN_STD
|
|
class DebugPluginStd : public DebugPlugin{
|
|
public:
|
|
DebugPluginStd(Workspace* ws) : DebugPlugin(ws){
|
|
|
|
}
|
|
|
|
virtual void onReset(){
|
|
DebugPlugin::onReset();
|
|
top->debug_bus_cmd_valid = 0;
|
|
}
|
|
|
|
bool rspFire = false;
|
|
|
|
virtual void preCycle(){
|
|
DebugPlugin::preCycle();
|
|
|
|
if(rspFire){
|
|
sendRsp(top->debug_bus_rsp_data);
|
|
rspFire = false;
|
|
}
|
|
|
|
if(top->debug_bus_cmd_valid && top->debug_bus_cmd_ready){
|
|
taskValid = false;
|
|
if(!top->debug_bus_cmd_payload_wr){
|
|
rspFire = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
DebugPlugin::postCycle();
|
|
|
|
if(taskValid){
|
|
top->debug_bus_cmd_valid = 1;
|
|
top->debug_bus_cmd_payload_wr = task.wr;
|
|
top->debug_bus_cmd_payload_address = task.address;
|
|
top->debug_bus_cmd_payload_data = task.data;
|
|
}else {
|
|
top->debug_bus_cmd_valid = 0;
|
|
top->debug_bus_cmd_payload_wr = VL_RANDOM_I(1);
|
|
top->debug_bus_cmd_payload_address = VL_RANDOM_I(8);
|
|
top->debug_bus_cmd_payload_data = VL_RANDOM_I(32);
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG_PLUGIN_AVALON
|
|
class DebugPluginAvalon : public DebugPlugin{
|
|
public:
|
|
DebugPluginAvalon(Workspace* ws) : DebugPlugin(ws){
|
|
|
|
}
|
|
|
|
virtual void onReset(){
|
|
DebugPlugin::onReset();
|
|
top->debugBusAvalon_read = 0;
|
|
top->debugBusAvalon_write = 0;
|
|
}
|
|
|
|
bool rspFire = false;
|
|
|
|
virtual void preCycle(){
|
|
DebugPlugin::preCycle();
|
|
|
|
if(rspFire){
|
|
sendRsp(top->debugBusAvalon_readData);
|
|
rspFire = false;
|
|
}
|
|
|
|
if((top->debugBusAvalon_read || top->debugBusAvalon_write) && top->debugBusAvalon_waitRequestn){
|
|
taskValid = false;
|
|
if(top->debugBusAvalon_read){
|
|
rspFire = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void postCycle(){
|
|
DebugPlugin::postCycle();
|
|
|
|
if(taskValid){
|
|
top->debugBusAvalon_write = task.wr;
|
|
top->debugBusAvalon_read = !task.wr;
|
|
top->debugBusAvalon_address = task.address;
|
|
top->debugBusAvalon_writeData = task.data;
|
|
}else {
|
|
top->debugBusAvalon_write = 0;
|
|
top->debugBusAvalon_read = 0;
|
|
top->debugBusAvalon_address = VL_RANDOM_I(8);
|
|
top->debugBusAvalon_writeData = VL_RANDOM_I(32);
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
void Workspace::fillSimELements(){
|
|
#ifdef IBUS_SIMPLE
|
|
simElements.push_back(new IBusSimple(this));
|
|
#endif
|
|
#ifdef IBUS_SIMPLE_AVALON
|
|
simElements.push_back(new IBusSimpleAvalon(this));
|
|
#endif
|
|
#ifdef IBUS_CACHED
|
|
simElements.push_back(new IBusCached(this));
|
|
#endif
|
|
#ifdef IBUS_CACHED_AVALON
|
|
simElements.push_back(new IBusCachedAvalon(this));
|
|
#endif
|
|
#if defined(IBUS_CACHED_WISHBONE) || defined(IBUS_SIMPLE_WISHBONE)
|
|
simElements.push_back(new IBusCachedWishbone(this));
|
|
#endif
|
|
|
|
#ifdef IBUS_TC
|
|
simElements.push_back(new IBusTc(this));
|
|
#endif
|
|
|
|
#ifdef DBUS_SIMPLE
|
|
simElements.push_back(new DBusSimple(this));
|
|
#endif
|
|
#ifdef DBUS_SIMPLE_AVALON
|
|
simElements.push_back(new DBusSimpleAvalon(this));
|
|
#endif
|
|
#ifdef DBUS_CACHED
|
|
simElements.push_back(new DBusCached(this));
|
|
#endif
|
|
#ifdef DBUS_CACHED_AVALON
|
|
simElements.push_back(new DBusCachedAvalon(this));
|
|
#endif
|
|
#if defined(DBUS_CACHED_WISHBONE) || defined(DBUS_SIMPLE_WISHBONE)
|
|
simElements.push_back(new DBusCachedWishbone(this));
|
|
#endif
|
|
#ifdef DEBUG_PLUGIN_STD
|
|
simElements.push_back(new DebugPluginStd(this));
|
|
#endif
|
|
#ifdef DEBUG_PLUGIN_AVALON
|
|
simElements.push_back(new DebugPluginAvalon(this));
|
|
#endif
|
|
}
|
|
|
|
mutex Workspace::staticMutex;
|
|
uint64_t Workspace::cycles = 0;
|
|
uint32_t Workspace::testsCounter = 0, Workspace::successCounter = 0;
|
|
|
|
#ifndef REF
|
|
#define testA1ReagFileWriteRef {1,10},{2,20},{3,40},{4,60}
|
|
#define testA2ReagFileWriteRef {5,1},{7,3}
|
|
uint32_t regFileWriteRefArray[][2] = {
|
|
testA1ReagFileWriteRef,
|
|
testA1ReagFileWriteRef,
|
|
testA2ReagFileWriteRef,
|
|
testA2ReagFileWriteRef
|
|
};
|
|
|
|
class TestA : public WorkspaceRegression{
|
|
public:
|
|
|
|
|
|
uint32_t regFileWriteRefIndex = 0;
|
|
|
|
TestA() : WorkspaceRegression("testA") {
|
|
loadHex("../../resources/hex/testA.hex");
|
|
}
|
|
|
|
virtual void checks(){
|
|
if(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_valid == 1 && top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address != 0){
|
|
assertEq(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address, regFileWriteRefArray[regFileWriteRefIndex][0]);
|
|
assertEq(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_data, regFileWriteRefArray[regFileWriteRefIndex][1]);
|
|
//printf("%d\n",i);
|
|
|
|
regFileWriteRefIndex++;
|
|
if(regFileWriteRefIndex == sizeof(regFileWriteRefArray)/sizeof(regFileWriteRefArray[0])){
|
|
pass();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class TestX28 : public WorkspaceRegression{
|
|
public:
|
|
uint32_t refIndex = 0;
|
|
uint32_t *ref;
|
|
uint32_t refSize;
|
|
|
|
TestX28(string name, uint32_t *ref, uint32_t refSize) : WorkspaceRegression(name) {
|
|
this->ref = ref;
|
|
this->refSize = refSize;
|
|
loadHex("../../resources/hex/" + name + ".hex");
|
|
}
|
|
|
|
virtual void checks(){
|
|
if(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_valid == 1 && top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address == 28){
|
|
assertEq(top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_data, ref[refIndex]);
|
|
//printf("%d\n",i);
|
|
|
|
refIndex++;
|
|
if(refIndex == refSize){
|
|
pass();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class RiscvTest : public WorkspaceRegression{
|
|
public:
|
|
RiscvTest(string name) : WorkspaceRegression(name) {
|
|
loadHex("../../resources/hex/" + name + ".hex");
|
|
bootAt(0x800000bcu);
|
|
}
|
|
|
|
virtual void postReset() {
|
|
// #ifdef CSR
|
|
// top->VexRiscv->prefetch_PcManagerSimplePlugin_pcReg = 0x80000000u;
|
|
// #else
|
|
// #endif
|
|
}
|
|
|
|
virtual void checks(){
|
|
if(top->VexRiscv->writeBack_arbitration_isFiring && top->VexRiscv->writeBack_INSTRUCTION == 0x00000013){
|
|
uint32_t instruction;
|
|
bool error;
|
|
Workspace::mem.read(top->VexRiscv->writeBack_PC, 4, (uint8_t*)&instruction);
|
|
//printf("%x => %x\n", top->VexRiscv->writeBack_PC, instruction );
|
|
if(instruction == 0x00000073){
|
|
uint32_t code = top->VexRiscv->RegFilePlugin_regFile[28];
|
|
uint32_t code2 = top->VexRiscv->RegFilePlugin_regFile[3];
|
|
if((code & 1) == 0 && (code2 & 1) == 0){
|
|
cout << "Wrong error code"<< endl;
|
|
fail();
|
|
}
|
|
if(code == 1 || code2 == 1){
|
|
pass();
|
|
}else{
|
|
cout << "Error code " << code/2 << endl;
|
|
fail();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error){
|
|
WorkspaceRegression::iBusAccess(addr,data,error);
|
|
if(*data == 0x0ff0000f) *data = 0x00000013;
|
|
if(*data == 0x00000073) *data = 0x00000013;
|
|
}
|
|
};
|
|
#endif
|
|
class Dhrystone : public WorkspaceRegression{
|
|
public:
|
|
string hexName;
|
|
Dhrystone(string name,string hexName,bool iStall, bool dStall) : WorkspaceRegression(name) {
|
|
setIStall(iStall);
|
|
setDStall(dStall);
|
|
withRiscvRef();
|
|
loadHex("../../resources/hex/" + hexName + ".hex");
|
|
this->hexName = hexName;
|
|
}
|
|
|
|
virtual void checks(){
|
|
|
|
}
|
|
|
|
virtual void pass(){
|
|
FILE *refFile = fopen((hexName + ".logRef").c_str(), "r");
|
|
fseek(refFile, 0, SEEK_END);
|
|
uint32_t refSize = ftell(refFile);
|
|
fseek(refFile, 0, SEEK_SET);
|
|
char* ref = new char[refSize];
|
|
fread(ref, 1, refSize, refFile);
|
|
fclose(refFile);
|
|
|
|
|
|
logTraces.flush();
|
|
logTraces.close();
|
|
|
|
FILE *logFile = fopen((name + ".logTrace").c_str(), "r");
|
|
fseek(logFile, 0, SEEK_END);
|
|
uint32_t logSize = ftell(logFile);
|
|
fseek(logFile, 0, SEEK_SET);
|
|
char* log = new char[logSize];
|
|
fread(log, 1, logSize, logFile);
|
|
fclose(logFile);
|
|
|
|
if(refSize > logSize || memcmp(log,ref,refSize))
|
|
fail();
|
|
else
|
|
Workspace::pass();
|
|
}
|
|
};
|
|
|
|
class Compliance : public WorkspaceRegression{
|
|
public:
|
|
string name;
|
|
ofstream out32;
|
|
int out32Counter = 0;
|
|
Compliance(string name) : WorkspaceRegression(name) {
|
|
withRiscvRef();
|
|
loadHex("../../resources/hex/" + name + ".elf.hex");
|
|
out32.open (name + ".out32");
|
|
this->name = name;
|
|
if(name == "I-FENCE.I-01") withInstructionReadCheck = false;
|
|
}
|
|
|
|
|
|
virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) {
|
|
if(wr && addr == 0xF00FFF2C){
|
|
out32 << hex << setw(8) << std::setfill('0') << *data << dec;
|
|
if(++out32Counter % 4 == 0) out32 << "\n";
|
|
}
|
|
WorkspaceRegression::dBusAccess(addr,wr,size,mask,data,error);
|
|
}
|
|
|
|
virtual void checks(){
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void pass(){
|
|
FILE *refFile = fopen((string("../../resources/ref/") + name + ".reference_output").c_str(), "r");
|
|
fseek(refFile, 0, SEEK_END);
|
|
uint32_t refSize = ftell(refFile);
|
|
fseek(refFile, 0, SEEK_SET);
|
|
char* ref = new char[refSize];
|
|
fread(ref, 1, refSize, refFile);
|
|
fclose(refFile);
|
|
|
|
|
|
out32.flush();
|
|
out32.close();
|
|
|
|
FILE *logFile = fopen((name + ".out32").c_str(), "r");
|
|
fseek(logFile, 0, SEEK_END);
|
|
uint32_t logSize = ftell(logFile);
|
|
fseek(logFile, 0, SEEK_SET);
|
|
char* log = new char[logSize];
|
|
fread(log, 1, logSize, logFile);
|
|
fclose(logFile);
|
|
|
|
if(refSize > logSize || memcmp(log,ref,refSize))
|
|
fail();
|
|
else
|
|
Workspace::pass();
|
|
}
|
|
};
|
|
|
|
|
|
#ifdef DEBUG_PLUGIN
|
|
|
|
#include<pthread.h>
|
|
#include<stdlib.h>
|
|
#include<unistd.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#define RISCV_SPINAL_FLAGS_RESET 1<<0
|
|
#define RISCV_SPINAL_FLAGS_HALT 1<<1
|
|
#define RISCV_SPINAL_FLAGS_PIP_BUSY 1<<2
|
|
#define RISCV_SPINAL_FLAGS_IS_IN_BREAKPOINT 1<<3
|
|
#define RISCV_SPINAL_FLAGS_STEP 1<<4
|
|
#define RISCV_SPINAL_FLAGS_PC_INC 1<<5
|
|
|
|
#define RISCV_SPINAL_FLAGS_RESET_SET 1<<16
|
|
#define RISCV_SPINAL_FLAGS_HALT_SET 1<<17
|
|
|
|
#define RISCV_SPINAL_FLAGS_RESET_CLEAR 1<<24
|
|
#define RISCV_SPINAL_FLAGS_HALT_CLEAR 1<<25
|
|
|
|
class DebugPluginTest : public WorkspaceRegression{
|
|
public:
|
|
pthread_t clientThreadId;
|
|
char buffer[1024];
|
|
bool clientSuccess = false, clientFail = false;
|
|
|
|
static void* clientThreadWrapper(void *debugModule){
|
|
((DebugPluginTest*)debugModule)->clientThread();
|
|
return NULL;
|
|
}
|
|
|
|
int clientSocket;
|
|
void accessCmd(bool wr, uint32_t size, uint32_t address, uint32_t data){
|
|
buffer[0] = wr;
|
|
buffer[1] = size;
|
|
*((uint32_t*) (buffer + 2)) = address;
|
|
*((uint32_t*) (buffer + 6)) = data;
|
|
send(clientSocket,buffer,10,0);
|
|
}
|
|
|
|
void writeCmd(uint32_t size, uint32_t address, uint32_t data){
|
|
accessCmd(true, 2, address, data);
|
|
}
|
|
|
|
|
|
uint32_t readCmd(uint32_t size, uint32_t address){
|
|
accessCmd(false, 2, address, VL_RANDOM_I(32));
|
|
int error;
|
|
if((error = recv(clientSocket, buffer, 4, 0)) != 4){
|
|
printf("Should read 4 bytes, had %d", error);
|
|
while(1);
|
|
}
|
|
|
|
return *((uint32_t*) buffer);
|
|
}
|
|
|
|
|
|
|
|
void clientThread(){
|
|
struct sockaddr_in serverAddr;
|
|
|
|
//---- Create the socket. The three arguments are: ----//
|
|
// 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) //
|
|
clientSocket = socket(PF_INET, SOCK_STREAM, 0);
|
|
int flag = 1;
|
|
int result = setsockopt(clientSocket, /* socket affected */
|
|
IPPROTO_TCP, /* set option at TCP level */
|
|
TCP_NODELAY, /* name of option */
|
|
(char *) &flag, /* the cast is historical
|
|
cruft */
|
|
sizeof(int)); /* length of option value */
|
|
|
|
//---- Configure settings of the server address struct ----//
|
|
// Address family = Internet //
|
|
serverAddr.sin_family = AF_INET;
|
|
// Set port number, using htons function to use proper byte order //
|
|
serverAddr.sin_port = htons(7893);
|
|
// Set IP address to localhost //
|
|
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
// Set all bits of the padding field to 0 //
|
|
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
|
|
|
|
//---- Connect the socket to the server using the address struct ----//
|
|
socklen_t addr_size = sizeof serverAddr;
|
|
int error = connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);
|
|
// printf("!! %x\n",readCmd(2,0x8));
|
|
uint32_t debugAddress = 0xF00F0000;
|
|
uint32_t readValue;
|
|
|
|
while(resetDone != true){usleep(100);}
|
|
|
|
while((readCmd(2,debugAddress) & RISCV_SPINAL_FLAGS_HALT) == 0){usleep(100);}
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 0x8000000C){
|
|
printf("wrong breakA PC %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (1 << 15)); //Read regfile
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 10){
|
|
printf("wrong breakB PC %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (2 << 15)); //Read regfile
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 20){
|
|
printf("wrong breakC PC %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (3 << 15)); //Read regfile
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 30){
|
|
printf("wrong breakD PC %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (1 << 7) + (40 << 20)); //Write x1 with 40
|
|
writeCmd(2, debugAddress + 4, 0x80000eb7); //Write x29 with 0x10
|
|
writeCmd(2, debugAddress + 4, 0x010e8e93); //Write x29 with 0x10
|
|
writeCmd(2, debugAddress + 4, 0x67 + (29 << 15)); //Branch x29
|
|
writeCmd(2, debugAddress + 0, RISCV_SPINAL_FLAGS_HALT_CLEAR); //Run CPU
|
|
|
|
while((readCmd(2,debugAddress) & RISCV_SPINAL_FLAGS_HALT) == 0){usleep(100);}
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 0x80000014){
|
|
printf("wrong breakE PC 3 %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (3 << 15)); //Read regfile
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 60){
|
|
printf("wrong x1 %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
|
|
writeCmd(2, debugAddress + 4, 0x80000eb7); //Write x29 with 0x10
|
|
writeCmd(2, debugAddress + 4, 0x018e8e93); //Write x29 with 0x10
|
|
writeCmd(2, debugAddress + 4, 0x67 + (29 << 15)); //Branch x29
|
|
writeCmd(2, debugAddress + 0, RISCV_SPINAL_FLAGS_HALT_CLEAR); //Run CPU
|
|
|
|
|
|
|
|
while((readCmd(2,debugAddress) & RISCV_SPINAL_FLAGS_HALT) == 0){usleep(100);}
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 0x80000024){
|
|
printf("wrong breakF PC 3 %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
|
|
writeCmd(2, debugAddress + 4, 0x13 + (3 << 15)); //Read x3
|
|
if((readValue = readCmd(2,debugAddress + 4)) != 171){
|
|
printf("wrong x3 %x\n",readValue);
|
|
clientFail = true; return;
|
|
}
|
|
|
|
|
|
clientSuccess = true;
|
|
}
|
|
|
|
|
|
DebugPluginTest() : WorkspaceRegression("DebugPluginTest") {
|
|
loadHex("../../resources/hex/debugPlugin.hex");
|
|
pthread_create(&clientThreadId, NULL, &clientThreadWrapper, this);
|
|
noInstructionReadCheck();
|
|
}
|
|
|
|
virtual ~DebugPluginTest(){
|
|
if(clientSocket != -1) close(clientSocket);
|
|
}
|
|
|
|
virtual void checks(){
|
|
if(clientSuccess) pass();
|
|
if(clientFail) fail();
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
//#ifdef LITEX
|
|
//class LitexSoC : public Workspace{
|
|
//public:
|
|
//
|
|
// LitexSoC(string name) : Workspace(name) {
|
|
//
|
|
// }
|
|
// virtual bool isDBusCheckedRegion(uint32_t address){ return true;}
|
|
// virtual bool isPerifRegion(uint32_t addr) { return (addr & 0xF0000000) == 0xB0000000 || (addr & 0xE0000000) == 0xE0000000;}
|
|
// virtual bool isMmuRegion(uint32_t addr) { return (addr & 0xFF000000) != 0x81000000;}
|
|
//
|
|
// virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) {
|
|
// if(isPerifRegion(addr)) switch(addr){
|
|
// //TODO Emulate peripherals here
|
|
// case 0xFFFFFFE0: if(wr) fail(); else *data = mTime; break;
|
|
// case 0xFFFFFFE4: if(wr) fail(); else *data = mTime >> 32; break;
|
|
// case 0xFFFFFFE8: if(wr) mTimeCmp = (mTimeCmp & 0xFFFFFFFF00000000) | *data; else *data = mTimeCmp; break;
|
|
// case 0xFFFFFFEC: if(wr) mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)*data) << 32); else *data = mTimeCmp >> 32; break;
|
|
// case 0xFFFFFFF8:
|
|
// if(wr){
|
|
// cout << (char)*data;
|
|
// logTraces << (char)*data;
|
|
// logTraces.flush();
|
|
// } else fail();
|
|
// break;
|
|
// case 0xFFFFFFFC: fail(); break; //Simulation end
|
|
// default: cout << "Unmapped peripheral access : addr=0x" << hex << addr << " wr=" << wr << " mask=0x" << mask << " data=0x" << data << dec << endl; fail(); break;
|
|
// }
|
|
//
|
|
// Workspace::dBusAccess(addr,wr,size,mask,data,error);
|
|
// }
|
|
//};
|
|
//#endif
|
|
|
|
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
|
|
termios stdinRestoreSettings;
|
|
void stdinNonBuffered(){
|
|
static struct termios old, new1;
|
|
tcgetattr(STDIN_FILENO, &old); /* grab old terminal i/o settings */
|
|
new1 = old; /* make new settings same as old settings */
|
|
new1.c_lflag &= ~ICANON; /* disable buffered i/o */
|
|
new1.c_lflag &= ~ECHO;
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &new1); /* use these new terminal i/o settings now */
|
|
setvbuf(stdin, NULL, _IONBF, 0);
|
|
stdinRestoreSettings = old;
|
|
}
|
|
|
|
void stdoutNonBuffered(){
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
}
|
|
|
|
void stdinRestore(){
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &stdinRestoreSettings); /* use these new terminal i/o settings now */
|
|
}
|
|
|
|
bool stdinNonEmpty(){
|
|
struct timeval tv;
|
|
fd_set fds;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO(&fds);
|
|
FD_SET(STDIN_FILENO, &fds);
|
|
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
|
|
return (FD_ISSET(0, &fds));
|
|
}
|
|
|
|
void my_handler(int s){
|
|
printf("Caught signal %d\n",s);
|
|
stdinRestore();
|
|
exit(1);
|
|
}
|
|
#include <signal.h>
|
|
|
|
void captureCtrlC(){
|
|
struct sigaction sigIntHandler;
|
|
|
|
sigIntHandler.sa_handler = my_handler;
|
|
sigemptyset(&sigIntHandler.sa_mask);
|
|
sigIntHandler.sa_flags = 0;
|
|
|
|
sigaction(SIGINT, &sigIntHandler, NULL);
|
|
}
|
|
|
|
#ifdef LINUX_SOC
|
|
class LinuxSoc : public Workspace{
|
|
public:
|
|
|
|
LinuxSoc(string name) : Workspace(name) {
|
|
stdinNonBuffered();
|
|
stdoutNonBuffered();
|
|
captureCtrlC();
|
|
}
|
|
|
|
virtual ~LinuxSoc(){
|
|
stdinRestore();
|
|
}
|
|
virtual bool isDBusCheckedRegion(uint32_t address){ return true;}
|
|
virtual bool isPerifRegion(uint32_t addr) { return (addr & 0xF0000000) == 0xF0000000 || (addr & 0xE0000000) == 0xE0000000;}
|
|
virtual bool isMmuRegion(uint32_t addr) { return true; }
|
|
|
|
virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) {
|
|
if(isPerifRegion(addr)) switch(addr){
|
|
//TODO Emulate peripherals here
|
|
case 0xFFFFFFE0: if(wr) fail(); else *data = mTime; break;
|
|
case 0xFFFFFFE4: if(wr) fail(); else *data = mTime >> 32; break;
|
|
case 0xFFFFFFE8: if(wr) mTimeCmp = (mTimeCmp & 0xFFFFFFFF00000000) | *data; else *data = mTimeCmp; break;
|
|
case 0xFFFFFFEC: if(wr) mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)*data) << 32); else *data = mTimeCmp >> 32; break;
|
|
case 0xFFFFFFF8:
|
|
if(wr){
|
|
cout << (char)*data;
|
|
logTraces << (char)*data;
|
|
logTraces.flush();
|
|
} else {
|
|
if(stdinNonEmpty()){
|
|
char c;
|
|
read(0, &c, 1);
|
|
*data = c;
|
|
//cout << "getchar " << c << endl;
|
|
} else {
|
|
*data = -1;
|
|
//cout << "getchar NONE" << endl;
|
|
}
|
|
}
|
|
break;
|
|
case 0xFFFFFFFC: fail(); break; //Simulation end
|
|
default: cout << "Unmapped peripheral access : addr=0x" << hex << addr << " wr=" << wr << " mask=0x" << mask << " data=0x" << data << dec << endl; fail(); break;
|
|
}
|
|
|
|
Workspace::dBusAccess(addr,wr,size,mask,data,error);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
string riscvTestMain[] = {
|
|
//"rv32ui-p-simple",
|
|
"rv32ui-p-lui",
|
|
"rv32ui-p-auipc",
|
|
"rv32ui-p-jal",
|
|
"rv32ui-p-jalr",
|
|
"rv32ui-p-beq",
|
|
"rv32ui-p-bge",
|
|
"rv32ui-p-bgeu",
|
|
"rv32ui-p-blt",
|
|
"rv32ui-p-bltu",
|
|
"rv32ui-p-bne",
|
|
"rv32ui-p-add",
|
|
"rv32ui-p-addi",
|
|
"rv32ui-p-and",
|
|
"rv32ui-p-andi",
|
|
"rv32ui-p-or",
|
|
"rv32ui-p-ori",
|
|
"rv32ui-p-sll",
|
|
"rv32ui-p-slli",
|
|
"rv32ui-p-slt",
|
|
"rv32ui-p-slti",
|
|
"rv32ui-p-sra",
|
|
"rv32ui-p-srai",
|
|
"rv32ui-p-srl",
|
|
"rv32ui-p-srli",
|
|
"rv32ui-p-sub",
|
|
"rv32ui-p-xor",
|
|
"rv32ui-p-xori"
|
|
};
|
|
|
|
string riscvTestMemory[] = {
|
|
"rv32ui-p-lb",
|
|
"rv32ui-p-lbu",
|
|
"rv32ui-p-lh",
|
|
"rv32ui-p-lhu",
|
|
"rv32ui-p-lw",
|
|
"rv32ui-p-sb",
|
|
"rv32ui-p-sh",
|
|
"rv32ui-p-sw"
|
|
};
|
|
|
|
|
|
|
|
|
|
string riscvTestMul[] = {
|
|
"rv32um-p-mul",
|
|
"rv32um-p-mulh",
|
|
"rv32um-p-mulhsu",
|
|
"rv32um-p-mulhu"
|
|
};
|
|
|
|
string riscvTestDiv[] = {
|
|
"rv32um-p-div",
|
|
"rv32um-p-divu",
|
|
"rv32um-p-rem",
|
|
"rv32um-p-remu"
|
|
};
|
|
|
|
string freeRtosTests[] = {
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1",
|
|
// "test1","test1","test1","test1","test1","test1","test1","test1"
|
|
|
|
"AltQTest", "AltBlock", "AltPollQ", "blocktim", "countsem", "dead", "EventGroupsDemo", "flop", "integer", "QPeek",
|
|
"QueueSet", "recmutex", "semtest", "TaskNotify", "BlockQ", "crhook", "dynamic",
|
|
"GenQTest", "PollQ", "QueueOverwrite", "QueueSetPolling", "sp_flop", "test1"
|
|
//"BlockQ","BlockQ","BlockQ","BlockQ","BlockQ","BlockQ","BlockQ","BlockQ"
|
|
// "flop"
|
|
// "flop", "sp_flop" // <- Simple test
|
|
// "AltBlckQ" ???
|
|
};
|
|
|
|
|
|
|
|
string riscvComplianceMain[] = {
|
|
"I-IO",
|
|
"I-NOP-01",
|
|
"I-LUI-01",
|
|
"I-ADD-01",
|
|
"I-ADDI-01",
|
|
"I-AND-01",
|
|
"I-ANDI-01",
|
|
"I-SUB-01",
|
|
"I-OR-01",
|
|
"I-ORI-01",
|
|
"I-XOR-01",
|
|
"I-XORI-01",
|
|
"I-SRA-01",
|
|
"I-SRAI-01",
|
|
"I-SRL-01",
|
|
"I-SRLI-01",
|
|
"I-SLL-01",
|
|
"I-SLLI-01",
|
|
"I-SLT-01",
|
|
"I-SLTI-01",
|
|
"I-SLTIU-01",
|
|
"I-SLTU-01",
|
|
"I-AUIPC-01",
|
|
"I-BEQ-01",
|
|
"I-BGE-01",
|
|
"I-BGEU-01",
|
|
"I-BLT-01",
|
|
"I-BLTU-01",
|
|
"I-BNE-01",
|
|
"I-JAL-01",
|
|
"I-JALR-01",
|
|
"I-DELAY_SLOTS-01",
|
|
"I-ENDIANESS-01",
|
|
"I-RF_size-01",
|
|
"I-RF_width-01",
|
|
"I-RF_x0-01",
|
|
};
|
|
|
|
|
|
|
|
string complianceTestMemory[] = {
|
|
"I-LB-01",
|
|
"I-LBU-01",
|
|
"I-LH-01",
|
|
"I-LHU-01",
|
|
"I-LW-01",
|
|
"I-SB-01",
|
|
"I-SH-01",
|
|
"I-SW-01"
|
|
};
|
|
|
|
|
|
string complianceTestCsr[] = {
|
|
"I-CSRRC-01",
|
|
"I-CSRRCI-01",
|
|
"I-CSRRS-01",
|
|
"I-CSRRSI-01",
|
|
"I-CSRRW-01",
|
|
"I-CSRRWI-01",
|
|
#ifndef COMPRESSED
|
|
"I-MISALIGN_JMP-01", //Only apply for non RVC cores
|
|
#endif
|
|
"I-MISALIGN_LDST-01",
|
|
"I-ECALL-01",
|
|
};
|
|
|
|
|
|
string complianceTestMul[] = {
|
|
"MUL",
|
|
"MULH",
|
|
"MULHSU",
|
|
"MULHU",
|
|
};
|
|
|
|
string complianceTestDiv[] = {
|
|
"DIV",
|
|
"DIVU",
|
|
"REM",
|
|
"REMU",
|
|
};
|
|
|
|
|
|
string complianceTestC[] = {
|
|
"C.ADD",
|
|
"C.ADDI16SP",
|
|
"C.ADDI4SPN",
|
|
"C.ADDI",
|
|
"C.AND",
|
|
"C.ANDI",
|
|
"C.BEQZ",
|
|
"C.BNEZ",
|
|
"C.JAL",
|
|
"C.JALR",
|
|
"C.J",
|
|
"C.JR",
|
|
"C.LI",
|
|
"C.LUI",
|
|
"C.LW",
|
|
"C.LWSP",
|
|
"C.MV",
|
|
"C.OR",
|
|
"C.SLLI",
|
|
"C.SRAI",
|
|
"C.SRLI",
|
|
"C.SUB",
|
|
"C.SW",
|
|
"C.SWSP",
|
|
"C.XOR",
|
|
};
|
|
|
|
|
|
|
|
|
|
struct timespec timer_start(){
|
|
struct timespec start_time;
|
|
clock_gettime(CLOCK_REALTIME, &start_time); //CLOCK_PROCESS_CPUTIME_ID
|
|
return start_time;
|
|
}
|
|
|
|
long timer_end(struct timespec start_time){
|
|
struct timespec end_time;
|
|
clock_gettime(CLOCK_REALTIME, &end_time);
|
|
uint64_t diffInNanos = end_time.tv_sec*1e9 + end_time.tv_nsec - start_time.tv_sec*1e9 - start_time.tv_nsec;
|
|
return diffInNanos;
|
|
}
|
|
|
|
#define redo(count,that) for(uint32_t xxx = 0;xxx < count;xxx++) that
|
|
#include <pthread.h>
|
|
#include <queue>
|
|
#include <functional>
|
|
#include <thread>
|
|
|
|
|
|
static void multiThreading(queue<std::function<void()>> *lambdas, std::mutex *mutex){
|
|
uint32_t counter = 0;
|
|
while(true){
|
|
mutex->lock();
|
|
if(lambdas->empty()){
|
|
mutex->unlock();
|
|
break;
|
|
}
|
|
|
|
#ifdef SEED
|
|
uint32_t seed = SEED + counter;
|
|
counter++;
|
|
srand48(seed);
|
|
printf("FREERTOS_SEED=%d \n", seed);
|
|
#endif
|
|
std::function<void()> lambda = lambdas->front();
|
|
lambdas->pop();
|
|
mutex->unlock();
|
|
|
|
lambda();
|
|
}
|
|
}
|
|
|
|
|
|
static void multiThreadedExecute(queue<std::function<void()>> &lambdas){
|
|
std::mutex mutex;
|
|
std::thread * t[THREAD_COUNT];
|
|
for(int id = 0;id < THREAD_COUNT;id++){
|
|
t[id] = new thread(multiThreading,&lambdas,&mutex);
|
|
}
|
|
for(int id = 0;id < THREAD_COUNT;id++){
|
|
t[id]->join();
|
|
delete t[id];
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv, char **env) {
|
|
#ifdef SEED
|
|
srand48(SEED);
|
|
#endif
|
|
Verilated::randReset(2);
|
|
Verilated::commandArgs(argc, argv);
|
|
|
|
printf("BOOT\n");
|
|
timespec startedAt = timer_start();
|
|
|
|
|
|
|
|
|
|
//#ifdef LITEX
|
|
// LitexSoC("linux")
|
|
// .withRiscvRef()
|
|
// ->loadBin(EMULATOR, 0x80000000)
|
|
// ->loadBin(DTB, 0x81000000)
|
|
// ->loadBin(VMLINUX, 0xc0000000)
|
|
// ->loadBin(RAMDISK, 0xc2000000)
|
|
// ->setIStall(false) //TODO It currently improve speed but should be removed later
|
|
// ->setDStall(false)
|
|
// ->bootAt(0x80000000)
|
|
// ->run(0);
|
|
//#endif
|
|
|
|
// {
|
|
// static struct termios old, new1;
|
|
// tcgetattr(0, &old); /* grab old terminal i/o settings */
|
|
// new1 = old; /* make new settings same as old settings */
|
|
// new1.c_lflag &= ~ICANON; /* disable buffered i/o */
|
|
// new1.c_lflag &= ~ECHO;
|
|
// tcsetattr(0, TCSANOW, &new1); /* use these new terminal i/o settings now */
|
|
// }
|
|
//
|
|
// std::string initialCommand;
|
|
//
|
|
// while(true){
|
|
// if(!inputAvailable()) {
|
|
// std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
|
|
// sleep(1);
|
|
// } else {
|
|
// char c;
|
|
// read(0, &c, 1); printf("%d\n", c);
|
|
//// std::getline(std::cin, initialCommand);
|
|
// }
|
|
// }
|
|
//
|
|
|
|
// char c;
|
|
// while (1) { read(0, &c, 1); printf("%d\n", c); }
|
|
// while(true){
|
|
// char c = getchar();
|
|
// if(c > 0)
|
|
// {
|
|
// putchar(c);
|
|
// } else {
|
|
// putchar('*');
|
|
// sleep(500);
|
|
// }
|
|
// }
|
|
|
|
#ifdef LINUX_SOC
|
|
LinuxSoc("linux")
|
|
.withRiscvRef()
|
|
->loadBin(EMULATOR, 0x80000000)
|
|
->loadBin(VMLINUX, 0xC0000000)
|
|
->loadBin(DTB, 0xC3000000)
|
|
->loadBin(RAMDISK, 0xC2000000)
|
|
->setIStall(true) //TODO It currently improve speed but should be removed later
|
|
->setDStall(true)
|
|
->bootAt(0x80000000)
|
|
->run(0);
|
|
// ->run(1173000000l );
|
|
#endif
|
|
|
|
// #ifdef MMU
|
|
// redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3););
|
|
// #endif
|
|
// redo(REDO,WorkspaceRegression("deleg").withRiscvRef()->loadHex("../raw/deleg/build/deleg.hex")->bootAt(0x80000000u)->run(50e3););
|
|
// return 0;
|
|
|
|
redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3););
|
|
|
|
for(int idx = 0;idx < 1;idx++){
|
|
|
|
#if defined(DEBUG_PLUGIN_EXTERNAL) || defined(RUN_HEX)
|
|
{
|
|
WorkspaceRegression w("run");
|
|
#ifdef RUN_HEX
|
|
//w.loadHex("/home/spinalvm/hdl/zephyr/zephyrSpinalHdl/samples/synchronization/build/zephyr/zephyr.hex");
|
|
w.loadHex(RUN_HEX);
|
|
#endif
|
|
w.noInstructionReadCheck();
|
|
//w.setIStall(false);
|
|
//w.setDStall(false);
|
|
|
|
#if defined(TRACE) || defined(TRACE_ACCESS)
|
|
//w.setCyclesPerSecond(5e3);
|
|
//printf("Speed reduced 5Khz\n");
|
|
#endif
|
|
w.run(0xFFFFFFFFFFFF);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef ISA_TEST
|
|
|
|
// redo(REDO,TestA().run();)
|
|
for(const string &name : riscvComplianceMain){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
for(const string &name : complianceTestMemory){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
|
|
#ifdef COMPRESSED
|
|
for(const string &name : complianceTestC){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
#endif
|
|
|
|
#ifdef MUL
|
|
for(const string &name : complianceTestMul){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
#endif
|
|
#ifdef DIV
|
|
for(const string &name : complianceTestDiv){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
#endif
|
|
#ifdef CSR
|
|
for(const string &name : complianceTestCsr){
|
|
redo(REDO, Compliance(name).run();)
|
|
}
|
|
#endif
|
|
|
|
#ifdef FENCEI
|
|
redo(REDO, Compliance("I-FENCE.I-01").run();)
|
|
#endif
|
|
#ifdef EBREAK
|
|
redo(REDO, Compliance("I-EBREAK-01").run();)
|
|
#endif
|
|
|
|
for(const string &name : riscvTestMain){
|
|
redo(REDO,RiscvTest(name).run();)
|
|
}
|
|
for(const string &name : riscvTestMemory){
|
|
redo(REDO,RiscvTest(name).run();)
|
|
}
|
|
|
|
#ifdef MUL
|
|
for(const string &name : riscvTestMul){
|
|
redo(REDO,RiscvTest(name).run();)
|
|
}
|
|
#endif
|
|
#ifdef DIV
|
|
for(const string &name : riscvTestDiv){
|
|
redo(REDO,RiscvTest(name).run();)
|
|
}
|
|
#endif
|
|
|
|
#ifdef COMPRESSED
|
|
redo(REDO,RiscvTest("rv32uc-p-rvc").bootAt(0x800000FCu)->run());
|
|
#endif
|
|
|
|
#ifdef CSR
|
|
#ifndef COMPRESSED
|
|
uint32_t machineCsrRef[] = {1,11, 2,0x80000003u, 3,0x80000007u, 4,0x8000000bu, 5,6,7,0x80000007u ,
|
|
8,6,9,6,10,4,11,4, 12,13,0, 14,2, 15,5,16,17,1 };
|
|
redo(REDO,TestX28("../../cpp/raw/machineCsr/build/machineCsr",machineCsrRef, sizeof(machineCsrRef)/4).withRiscvRef()->noInstructionReadCheck()->run(10e4);)
|
|
#else
|
|
uint32_t machineCsrRef[] = {1,11, 2,0x80000003u, 3,0x80000007u, 4,0x8000000bu, 5,6,7,0x80000007u ,
|
|
8,6,9,6,10,4,11,4, 12,13, 14,2, 15,5,16,17,1 };
|
|
redo(REDO,TestX28("../../cpp/raw/machineCsr/build/machineCsrCompressed",machineCsrRef, sizeof(machineCsrRef)/4).withRiscvRef()->noInstructionReadCheck()->run(10e4);)
|
|
#endif
|
|
#endif
|
|
// #ifdef MMU
|
|
// uint32_t mmuRef[] = {1,2,3, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x22222222, 4, 0x11111111, 0x33333333, 0x33333333, 5,
|
|
// 13, 0xC4000000,0x33333333, 6,7,
|
|
// 1,2,3, 0x11111111, 0x11111111, 0x11111111, 0x22222222, 0x22222222, 0x22222222, 4, 0x11111111, 0x33333333, 0x33333333, 5,
|
|
// 13, 0xC4000000,0x33333333, 6,7};
|
|
// redo(REDO,TestX28("mmu",mmuRef, sizeof(mmuRef)/4).noInstructionReadCheck()->run(4e4);)
|
|
// #endif
|
|
|
|
#ifdef MMU
|
|
redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3););
|
|
#endif
|
|
#ifdef SUPERVISOR
|
|
redo(REDO,WorkspaceRegression("deleg").withRiscvRef()->loadHex("../raw/deleg/build/deleg.hex")->bootAt(0x80000000u)->run(50e3););
|
|
#endif
|
|
|
|
#ifdef DEBUG_PLUGIN
|
|
redo(REDO,DebugPluginTest().run(1e6););
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CUSTOM_SIMD_ADD
|
|
redo(REDO,WorkspaceRegression("custom_simd_add").loadHex("../custom/simd_add/build/custom_simd_add.hex")->bootAt(0x00000000u)->run(50e3););
|
|
#endif
|
|
|
|
#ifdef CUSTOM_CSR
|
|
redo(REDO,WorkspaceRegression("custom_csr").loadHex("../custom/custom_csr/build/custom_csr.hex")->bootAt(0x00000000u)->run(50e3););
|
|
#endif
|
|
|
|
|
|
#ifdef ATOMIC
|
|
redo(REDO,WorkspaceRegression("atomic").loadHex("../custom/atomic/build/atomic.hex")->bootAt(0x00000000u)->run(10e3););
|
|
#endif
|
|
|
|
#ifdef DHRYSTONE
|
|
Dhrystone("dhrystoneO3_Stall","dhrystoneO3",true,true).run(1.5e6);
|
|
#if defined(COMPRESSED)
|
|
Dhrystone("dhrystoneO3C_Stall","dhrystoneO3C",true,true).run(1.5e6);
|
|
#endif
|
|
#if defined(MUL) && defined(DIV)
|
|
Dhrystone("dhrystoneO3M_Stall","dhrystoneO3M",true,true).run(1.9e6);
|
|
#if defined(COMPRESSED)
|
|
Dhrystone("dhrystoneO3MC_Stall","dhrystoneO3MC",true,true).run(1.9e6);
|
|
#endif
|
|
#endif
|
|
Dhrystone("dhrystoneO3","dhrystoneO3",false,false).run(1.9e6);
|
|
#if defined(COMPRESSED)
|
|
Dhrystone("dhrystoneO3C","dhrystoneO3C",false,false).run(1.9e6);
|
|
#endif
|
|
#if defined(MUL) && defined(DIV)
|
|
Dhrystone("dhrystoneO3M","dhrystoneO3M",false,false).run(1.9e6);
|
|
#if defined(COMPRESSED)
|
|
Dhrystone("dhrystoneO3MC","dhrystoneO3MC",false,false).run(1.9e6);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef FREERTOS
|
|
#ifdef SEED
|
|
srand48(SEED);
|
|
#endif
|
|
//redo(1,WorkspaceRegression("freeRTOS_demo").loadHex("../../resources/hex/freeRTOS_demo.hex")->bootAt(0x80000000u)->run(100e6);)
|
|
vector <std::function<void()>> tasks;
|
|
|
|
/*for(int redo = 0;redo < 4;redo++)*/{
|
|
for(const string &name : freeRtosTests){
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32i_O0").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32i_O0.hex")->bootAt(0x80000000u)->run(4e6*15);});
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32i_O3").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32i_O3.hex")->bootAt(0x80000000u)->run(4e6*15);});
|
|
#ifdef COMPRESSED
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32ic_O0").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32ic_O0.hex")->bootAt(0x80000000u)->run(5e6*15);});
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32ic_O3").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32ic_O3.hex")->bootAt(0x80000000u)->run(4e6*15);});
|
|
#endif
|
|
#if defined(MUL) && defined(DIV)
|
|
#ifdef COMPRESSED
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32imac_O3").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32imac_O3.hex")->bootAt(0x80000000u)->run(4e6*15);});
|
|
#else
|
|
tasks.push_back([=]() { WorkspaceRegression(name + "_rv32im_O3").withRiscvRef()->loadHex("../../resources/freertos/" + name + "_rv32im_O3.hex")->bootAt(0x80000000u)->run(4e6*15);});
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
while(tasks.size() > FREERTOS_COUNT){
|
|
tasks.erase(tasks.begin() + (VL_RANDOM_I(32)%tasks.size()));
|
|
}
|
|
|
|
|
|
queue <std::function<void()>> tasksSelected(std::deque<std::function<void()>>(tasks.begin(), tasks.end()));
|
|
multiThreadedExecute(tasksSelected);
|
|
#endif
|
|
}
|
|
|
|
uint64_t duration = timer_end(startedAt);
|
|
cout << endl << "****************************************************************" << endl;
|
|
cout << "Had simulate " << Workspace::cycles << " clock cycles in " << duration*1e-9 << " s (" << Workspace::cycles / (duration*1e-6) << " Khz)" << endl;
|
|
if(Workspace::successCounter == Workspace::testsCounter)
|
|
cout << "SUCCESS " << Workspace::successCounter << "/" << Workspace::testsCounter << endl;
|
|
else
|
|
cout<< "FAILURE " << Workspace::testsCounter - Workspace::successCounter << "/" << Workspace::testsCounter << endl;
|
|
cout << "****************************************************************" << endl << endl;
|
|
|
|
|
|
exit(0);
|
|
}
|