fpu vex regression goldenModel can now assert FPU interface

This commit is contained in:
Dolu1990 2021-02-19 17:55:56 +01:00
parent 3f226b758c
commit a6e89fe05c
2 changed files with 284 additions and 30 deletions

View File

@ -1,11 +1,14 @@
package vexriscv.plugin
import spinal.core._
import spinal.core.internals.{BoolLiteral, Literal}
import spinal.lib._
import vexriscv._
import vexriscv.Riscv._
import vexriscv.ip.fpu._
import scala.collection.mutable.ArrayBuffer
class FpuPlugin(externalFpu : Boolean = false,
p : FpuParameter) extends Plugin[VexRiscv]{
@ -150,6 +153,68 @@ class FpuPlugin(externalFpu : Boolean = false,
dBusEncoding.addLoadWordEncoding(FLD)
dBusEncoding.addStoreWordEncoding(FSD)
}
exposeEncoding()
}
def exposeEncoding(): Unit ={
val d = pipeline.service(classOf[DecoderSimplePlugin])
val commits, rsps, rs1, commitsN, rspsN, rs1N = ArrayBuffer[MaskedLiteral]()
def filter(encoding : Int, list : ArrayBuffer[MaskedLiteral]) = list.filter(e => (e.value & 0x7F) == encoding)
def filterNotLs(list : ArrayBuffer[MaskedLiteral]) = list.filter(e => !List(0x7, 0x27).contains(e.value & 0x7F))
for((key, t) <- d.encodings; if(t.map(_._1).contains(FPU_ENABLE));
(s, v) <- t){
def isSet = v.head.source.asInstanceOf[Literal].getValue == 1
if(s == FPU_COMMIT) (if(isSet)commits += key else commitsN += key)
if(s == FPU_RSP) (if(isSet)rsps += key else rspsN += key)
if(s == pipeline.config.RS1_USE) (if(isSet)rs1 += key else rs1N += key)
}
// println("COMMIT => ")
// filter(0x53, commits).foreach(println)
// println("COMMITN => ")
// filter(0x53, commitsN).foreach(println)
// println("RSP => ")
// filter(0x53, rsps).foreach(println)
// println("RSPN => ")
// filter(0x53, rspsN).foreach(println)
val commitLut, rspLut, rs1Lut = Array.fill(32)(false)
filter(0x53,commits).foreach{m =>
val idx = (m.value >> 27).toInt
commitLut(idx) = true
}
filter(0x53,commitsN).foreach{m =>
val idx = (m.value >> 27).toInt
assert(!commitLut(idx))
}
println("COMMIT => ")
println(commitLut.mkString(","))
filter(0x53,rsps).foreach{m =>
val idx = (m.value >> 27).toInt
rspLut(idx) = true
}
filter(0x53,rspsN).foreach{m =>
val idx = (m.value >> 27).toInt
assert(!rspLut(idx))
}
println("RSP => ")
println(rspLut.mkString(","))
filter(0x53,rs1).foreach{m =>
val idx = (m.value >> 27).toInt
rs1Lut(idx) = true
}
filter(0x53,rs1N).foreach{m =>
val idx = (m.value >> 27).toInt
assert(!rs1Lut(idx))
}
println("rs1 => ")
println(rs1Lut.mkString(","))
}
override def build(pipeline: VexRiscv): Unit = {
@ -228,7 +293,7 @@ class FpuPlugin(externalFpu : Boolean = false,
insert(FPU_COMMIT_LOAD) := input(FPU_OPCODE) === FpuOpcode.LOAD
}
writeBack plug new Area{
writeBack plug new Area{ //WARNING IF STAGE CHANGE, update the regression rsp capture filter for the golden model (top->VexRiscv->lastStageIsFiring)
import writeBack._
val dBusEncoding = pipeline.service(classOf[DBusEncodingService])
@ -257,7 +322,7 @@ class FpuPlugin(externalFpu : Boolean = false,
}
// Manage $load
val commit = Stream(FpuCommit(p))
val commit = Stream(FpuCommit(p)).addTag(Verilator.public)
commit.valid := isCommit && !arbitration.isStuck
commit.value(31 downto 0) := (input(FPU_COMMIT_LOAD) ? dBusEncoding.loadData()(31 downto 0) | input(RS1))
if(p.withDouble) commit.value(63 downto 32) := dBusEncoding.loadData()(63 downto 32)

View File

@ -236,13 +236,42 @@ class success : public std::exception { };
#ifdef SUPERVISOR
#define MSTATUS_READ_MASK 0xFFFFFFFF
#else
#define MSTATUS_READ_MASK 0x1888
#define MSTATUS_READ_MASK 0x7888
#endif
#ifdef RVF
#define STATUS_FS_MASK 0x6000
#else
#define STATUS_FS_MASK 0x0000
#endif
#define FFLAGS 0x1
#define FRM 0x2
#define FCSR 0x3
#define u32 uint32_t
#define u32 uint64_t
#define u64 uint64_t
class FpuRsp{
public:
u32 flags;
u64 value;
};
class FpuCommit{
public:
u64 value;
};
class FpuCompletion{
public:
u32 flags;
};
bool fpuCommitLut[32] = {true,true,true,true,true,true,false,false,true,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false};
bool fpuRspLut[32] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false};
bool fpuRs1Lut[32] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false};
class RiscvGolden {
public:
int32_t pc, lastPc;
@ -257,6 +286,9 @@ public:
uint32_t medeleg;
uint32_t mideleg;
queue<FpuRsp> fpuRsp;
queue<FpuCommit> fpuCommit;
queue<FpuCompletion> fpuCompletion;
union status {
uint32_t raw;
@ -272,7 +304,8 @@ public:
uint32_t spp : 1;
uint32_t _3 : 2;
uint32_t mpp : 2;
uint32_t _4 : 4;
uint32_t fs : 2;
uint32_t _4 : 2;
uint32_t mprv : 1;
uint32_t sum : 1;
uint32_t mxr : 1;
@ -381,9 +414,18 @@ public:
};
};
union fcsr {
uint32_t raw;
struct __attribute__((packed)){
uint32_t flags : 5;
uint32_t frm : 3;
};
}fcsr;
bool lrscReserved;
uint32_t lrscReservedAddress;
u32 fpuCompletionTockens;
RiscvGolden() {
pc = 0x80000000;
@ -391,7 +433,6 @@ public:
for (int i = 0; i < 32; i++)
regs[i] = 0;
status.raw = 0;
ie.raw = 0;
mtvec.raw = 0x80000020;
mcause.raw = 0;
@ -401,6 +442,11 @@ public:
status.raw = 0;
status.mpp = 3;
status.spp = 1;
#ifdef RVF
status.fs = 1;
#endif
fcsr.flags = 0;
fcsr.frm = 0;
privilege = 3;
medeleg = 0;
mideleg = 0;
@ -410,6 +456,7 @@ public:
stepCounter = 0;
sbadaddr = 42;
lrscReserved = false;
fpuCompletionTockens = 0;
}
virtual void rfWrite(int32_t address, int32_t data) {
@ -429,8 +476,8 @@ public:
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;
virtual bool dRead(int32_t address, int32_t size, uint8_t *data) = 0;
virtual void dWrite(int32_t address, int32_t size, uint8_t *data) = 0;
enum AccessKind {READ,WRITE,EXECUTE,READ_WRITE};
virtual bool isMmuRegion(uint32_t v) = 0;
@ -440,11 +487,11 @@ public:
*p = v;
} else {
Tlb tlb;
dRead((satp.ppn << 12) | ((v >> 22) << 2), 4, &tlb.raw);
dRead((satp.ppn << 12) | ((v >> 22) << 2), 4, (uint8_t*)&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);
dRead((tlb.ppn << 12) | (((v >> 12) & 0x3FF) << 2), 4, (uint8_t*)&tlb.raw);
if(!tlb.v) return true;
superPage = false;
}
@ -559,6 +606,13 @@ public:
case SEPC: *value = sepc; break;
case SSCRATCH: *value = sscratch; break;
case SATP: *value = satp.raw; break;
#ifdef RVF
case FCSR: *value = fcsr.raw; break;
case FRM: *value = fcsr.frm; break;
case FFLAGS: *value = fcsr.flags; break;
#endif
default: return true; break;
}
return false;
@ -590,7 +644,7 @@ public:
case MEDELEG: medeleg = value & (~0x8); break;
case MIDELEG: mideleg = value; break;
case SSTATUS: maskedWrite(status.raw, value,0xC0133); break;
case SSTATUS: maskedWrite(status.raw, value,0xC0133 | STATUS_FS_MASK); break;
case SIP: maskedWrite(ipSoft, value,0x333); break;
case SIE: maskedWrite(ie.raw, value,0x333); break;
case STVEC: stvec.raw = value; break;
@ -600,6 +654,13 @@ public:
case SSCRATCH: sscratch = value; break;
case SATP: satp.raw = value; break;
#ifdef RVF
case FCSR: fcsr.raw = value & 0x7F; break;
case FRM: fcsr.frm = value; break;
case FFLAGS: fcsr.flags = value; break;
#endif
default: ilegalInstruction(); return true; break;
}
return false;
@ -671,6 +732,13 @@ public:
virtual void step() {
stepCounter++;
livenessStep = 0;
while(fpuCompletionTockens != 0 && !fpuCompletion.empty()){
FpuCompletion completion = fpuCompletion.front(); fpuCompletion.pop();
fcsr.flags |= completion.flags;
fpuCompletionTockens -= 1;
}
#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)
@ -683,6 +751,7 @@ public:
#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 i32_func7 iBits(25, 7)
#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)
@ -728,6 +797,95 @@ public:
if ((i & 0x3) == 0x3) {
//32 bit
switch (i & 0x7F) {
#ifdef RVF
case 0x43:// RVFD
case 0x47:
case 0x4B:
case 0x4F:
case 0x53: {
u32 format = iBits(25,2);
u32 opcode = iBits(27,5);
bool withCommit = fpuCommitLut[opcode];
bool withRsp = fpuRspLut[opcode];
bool withRs1 = fpuRs1Lut[opcode];
if((i & 0x7F) != 0x53) { // FMADD
withCommit = true;
withRsp = false;
}
#ifdef RVD
if(format > 1) ilegalInstruction();
#else
if(format > 0) ilegalInstruction();
#endif
if(withCommit){
FpuCommit commit = fpuCommit.front(); fpuCommit.pop();
fpuCompletionTockens += 1;
// cout << "withRs1 " << withRs1 << " " << opcode << endl;
if(withRs1 && memcmp(&i32_rs1, &commit.value, 4)){
cout << "FPU commit missmatch DUT=" << hex << commit.value << " REF=" << i32_rs1 << dec << endl;
fail();
return;
}
}
if(withRsp){
auto rsp = fpuRsp.front(); fpuRsp.pop();
fcsr.flags |= rsp.flags;
rfWrite(rd32, (u32)rsp.value);
}
pcWrite(pc + 4);
} break;
case 0x07: { //Fpu load
uint32_t size = 1 << ((i >> 12) & 0x3);
if(size < 4) ilegalInstruction();
#ifdef RVD
if(size > 8) ilegalInstruction();
#else
if(format > 4) ilegalInstruction();
#endif
auto commit = fpuCommit.front(); fpuCommit.pop();
fpuCompletionTockens += 1;
uint64_t data = 0;
uint32_t address = i32_rs1 + i32_i_imm;
if(address & (size-1)){
trap(0, 4, address);
} else {
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
if(dRead(pAddr, size, (uint8_t*)&data)){
trap(0, 5, address);
} else {
if(memcmp(&data, &commit.value, size)){
cout << "FPU load missmatch DUT=" << hex << commit.value << " REF=" << data << dec << endl;
fail();
} else {
pcWrite(pc + 4);
}
}
}
} break;
case 0x27: { //Fpu store
uint32_t size = 1 << ((i >> 12) & 0x3);
if(size < 4) ilegalInstruction();
#ifdef RVD
if(size > 8) ilegalInstruction();
#else
if(format > 4) ilegalInstruction();
#endif
auto rsp = fpuRsp.front(); fpuRsp.pop();
fcsr.flags |= rsp.flags;
uint32_t address = i32_rs1 + i32_s_imm;
if(address & (size-1)){
trap(0, 6, address);
} else {
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
dWrite(pAddr, size, (uint8_t*) &rsp.value);
pcWrite(pc + 4);
}
} break;
#endif
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
@ -754,7 +912,7 @@ public:
trap(0, 4, address);
} else {
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
if(dRead(pAddr, size, &data)){
if(dRead(pAddr, size, (uint8_t*)&data)){
trap(0, 5, address);
} else {
switch ((i >> 12) & 0x7) {
@ -774,7 +932,7 @@ public:
trap(0, 6, address);
} else {
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
dWrite(pAddr, size, i32_rs2);
dWrite(pAddr, size, (uint8_t*)&i32_rs2);
pcWrite(pc + 4);
}
}break;
@ -897,7 +1055,7 @@ public:
trap(0, 4, address);
} else {
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
if(dRead(pAddr, 4, &data)){
if(dRead(pAddr, 4, (uint8_t*)&data)){
trap(0, 5, address);
} else {
lrscReserved = true;
@ -919,7 +1077,7 @@ public:
bool hit = lrscReserved;
#endif
if(hit){
dWrite(pAddr, 4, i32_rs2);
dWrite(pAddr, 4, (uint8_t*)&i32_rs2);
}
lrscReserved = false;
rfWrite(rd32, !hit);
@ -941,7 +1099,7 @@ public:
uint32_t pAddr;
if(v2p(addr, &pAddr, READ_WRITE)){ trap(0, 15, addr); return; }
if(dRead(pAddr, 4, (uint32_t*)&readValue)){
if(dRead(pAddr, 4, (uint8_t*)&readValue)){
trap(0, 15, addr); return;
return;
}
@ -958,7 +1116,7 @@ public:
case 0x1C: writeValue = max((unsigned int)src, (unsigned int)readValue); break;
default: ilegalInstruction(); return; break;
}
dWrite(pAddr, 4, writeValue);
dWrite(pAddr, 4, (uint8_t*)&writeValue);
rfWrite(rd32, readValue);
pcWrite(pc + 4);
#endif
@ -991,7 +1149,7 @@ public:
trap(0, 4, address);
} else {
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
if(dRead(pAddr, 4, &data)) {
if(dRead(pAddr, 4, (uint8_t*)&data)) {
trap(0, 5, address);
} else {
rfWrite(i16_addr2, data); pcWrite(pc + 2);
@ -1004,7 +1162,7 @@ public:
trap(0, 6, address);
} else {
if(v2p(address, &pAddr, WRITE)){ trap(0, 15, address); return; }
dWrite(pAddr, 4, i16_rf2);
dWrite(pAddr, 4, (uint8_t*)&i16_rf2);
pcWrite(pc + 2);
}
}break;
@ -1040,7 +1198,7 @@ public:
trap(0, 4, address);
} else {
if(v2p(address, &pAddr, READ)){ trap(0, 13, address); return; }
if(dRead(pAddr, 4, &data)){
if(dRead(pAddr, 4,(uint8_t*) &data)){
trap(0, 5, address);
} else {
rfWrite(rd32, data); pcWrite(pc + 2);
@ -1070,7 +1228,7 @@ public:
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);
dWrite(pAddr, 4, (uint8_t*)&regs[iBits(2,5)]); pcWrite(pc + 2);
}
}break;
}
@ -1178,8 +1336,8 @@ public:
return error;
}
virtual bool dRead(int32_t address, int32_t size, uint32_t *data){
if(size < 1 || size > 4){
virtual bool dRead(int32_t address, int32_t size, uint8_t *data){
if(size < 1 || size > 8){
cout << "dRead size=" << size << endl;
fail();
}
@ -1195,28 +1353,28 @@ public:
}
for(int i = 0; i < size; i++){
((uint8_t*)data)[i] = t.data42[i];
data[i] = t.data42[i];
}
periphRead.pop();
return t.error;
}else {
mem.read(address, size, (uint8_t*)data);
mem.read(address, size, data);
}
return false;
}
virtual void dWrite(int32_t address, int32_t size, uint32_t data){
virtual void dWrite(int32_t address, int32_t size, uint8_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);
mem.write(address, size, data);
}
if(ws->isDBusCheckedRegion(address)){
MemWrite w;
w.address = address;
w.size = size;
for(int i = 0; i < size; i++){
w.data42[i] = ((uint8_t*)&data)[i];
w.data42[i] = data[i];
}
periphWritesGolden.push(w);
if(periphWritesGolden.size() > 10){
@ -1566,6 +1724,37 @@ public:
}
}
#endif
#ifdef RVF
if(riscvRefEnable) {
if(top->VexRiscv->writeBack_FpuPlugin_commit_valid && top->VexRiscv->writeBack_FpuPlugin_commit_ready && top->VexRiscv->writeBack_FpuPlugin_commit_payload_write){
FpuCommit c;
c.value = top->VexRiscv->writeBack_FpuPlugin_commit_payload_value;
riscvRef.fpuCommit.push(c);
}
if(top->VexRiscv->FpuPlugin_port_rsp_valid && top->VexRiscv->FpuPlugin_port_rsp_ready && top->VexRiscv->lastStageIsFiring){
FpuRsp c;
c.value = top->VexRiscv->FpuPlugin_port_rsp_payload_value;
c.flags = (top->VexRiscv->FpuPlugin_port_rsp_payload_NX << 0) |
(top->VexRiscv->FpuPlugin_port_rsp_payload_NV << 4);
riscvRef.fpuRsp.push(c);
}
if(top->VexRiscv->FpuPlugin_port_completion_valid && top->VexRiscv->FpuPlugin_port_completion_payload_written){
FpuCompletion c;
c.flags = (top->VexRiscv->FpuPlugin_port_completion_payload_flags_NX << 0) |
(top->VexRiscv->FpuPlugin_port_completion_payload_flags_UF << 1) |
(top->VexRiscv->FpuPlugin_port_completion_payload_flags_OF << 2) |
(top->VexRiscv->FpuPlugin_port_completion_payload_flags_DZ << 3) |
(top->VexRiscv->FpuPlugin_port_completion_payload_flags_NV << 4);
riscvRef.fpuCompletion.push(c);
}
}
#endif
if(top->VexRiscv->lastStageIsFiring){
if(riscvRefEnable) {
// privilegeCounters[riscvRef.privilege]++;
@ -3871,12 +4060,12 @@ int main(int argc, char **argv, char **env) {
#ifdef RVF
for(const string &name : riscvTestFloat){
redo(REDO,RiscvTest(name).bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
}
#endif
#ifdef RVD
for(const string &name : riscvTestDouble){
redo(REDO,RiscvTest(name).bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
}
#endif
//return 0;