#include "VVexRiscv.h" #include "VVexRiscv_VexRiscv.h" #ifdef REF #include "VVexRiscv_RiscvCore.h" #endif #include "verilated.h" #include "verilated_vcd_c.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 periphWritesGolden; queue periphWrites; queue 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 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 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 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 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 struct DBusSimpleAvalonRsp{ uint32_t data; bool error; }; class DBusSimpleAvalon : public SimElement{ public: queue 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 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 struct DBusCachedAvalonTask{ uint32_t data; bool error; }; class DBusCachedAvalon : public SimElement{ public: uint32_t beatCounter = 0; queue 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 #include #include #include #include #include #include #include /** 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 #include #include #include #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 #include #include 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 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 #include #include #include static void multiThreading(queue> *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 lambda = lambdas->front(); lambdas->pop(); mutex->unlock(); lambda(); } } static void multiThreadedExecute(queue> &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 > 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 > tasksSelected(std::deque>(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); }