diff --git a/ChangeLog.md b/ChangeLog.md index 7302f6f..b5d353e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,10 +6,36 @@ This CHANGELOG contains information for the riscvOVPsim fixed platform which inc --- -NOTE: X-commit messages below refer to git commits in the following - Risc-V specification document repositories: - I-commit: https://github.com/riscv/riscv-isa-manual - V-commit: https://github.com/riscv/riscv-v-spec +NOTE: X-commit messages below refer to git commits in the following + Risc-V specification document repositories: + I-commit: https://github.com/riscv/riscv-isa-manual + V-commit: https://github.com/riscv/riscv-v-spec + +- Support for Debug mode has been added; see RISCV processor documentation for + more details. +- The priv_version parameter now includes a choice of 'master', which specifies + that the evolving 1.12 Privileged Architecture Specification should be used. + This has the following changes compared to the ratified 1.11 version: + - MRET and SRET instruction clear mstatus.MPRV when leaving M-mode; + - For RV32, a new mstatush CSR has been added; + - Data endian is now configurable using UBE, SBE and MBE fields in mstatus + and the new mstatush CSR. +- New parameter SEW_min has been added to specify the minimum SEW supported when + the Vector Extension is implemented; the default is 8 bits. +- When the Vector Extension is implemented, the maximum VLEN value supported + has increased from 2048 to 65536 bits. +- Some Vector Extension issues have been corrected: + - Behavior of vslidedown has been corrected in cases when vlcheckEndian) { + riscv->checkEndian = True; + vmirtFlushAllDicts(processor); + } +} + // // Common routine to read status using mstatus, sstatus or ustatus alias // @@ -549,8 +564,10 @@ static void statusW(riscvP riscv, Uns64 newValue, Uns64 mask) { // get new value using writable bit mask Uns32 oldIE = oldValue & WM_mstatus_IE; + Uns64 oldBE = oldValue & WM_mstatus_BE; newValue = ((newValue & mask) | (oldValue & ~mask)); Uns32 newIE = newValue & WM_mstatus_IE; + Uns64 newBE = newValue & WM_mstatus_BE; // update the CSR Uns8 oldMPP = RD_CSR_FIELD(riscv, mstatus, MPP); @@ -563,6 +580,11 @@ static void statusW(riscvP riscv, Uns64 newValue, Uns64 mask) { WR_CSR_FIELD(riscv, mstatus, MPP, oldMPP); } + // handle update of endianness if required + if(oldBE!=newBE) { + updateEndian(riscv); + } + // update current architecture if required riscvSetCurrentArch(riscv); @@ -601,6 +623,43 @@ static RISCV_CSR_WRITEFN(mstatusW) { return RD_CSR(riscv, mstatus); } +// +// Common routine to write statush using mstatush alias +// +static void statushW(riscvP riscv, Uns64 newValue, Uns32 mask) { + + // get old value + Uns32 oldValue = RD_CSR(riscv, mstatush); + + // get new value using writable bit mask + Uns32 oldBE = oldValue & WM_mstatush_BE; + newValue = ((newValue & mask) | (oldValue & ~mask)); + Uns32 newBE = newValue & WM_mstatush_BE; + + // update the CSR + WR_CSR(riscv, mstatush, newValue); + + // handle update of endianness if required + if(oldBE!=newBE) { + updateEndian(riscv); + riscvSetCurrentArch(riscv); + } +} + +// +// Write mstatush +// +static RISCV_CSR_WRITEFN(mstatushW) { + + Uns32 mask = RD_CSR_MASK(riscv, mstatush); + + // update the CSR + statushW(riscv, newValue, mask); + + // return written value + return RD_CSR(riscv, mstatush); +} + // // Read sstatus // @@ -1118,6 +1177,31 @@ inline static Uns64 getInstructions(riscvP riscv) { return vmirtGetExecutedICount((vmiProcessorP)riscv); } +// +// Should a counter be inhibited because dcsr.stopcount=1? +// +static Bool stopCount(riscvP riscv, Bool singleOnly) { + return ( + inDebugMode(riscv) && + RD_CSR_FIELD(riscv, dcsr, stopcount) && + (!singleOnly || !riscv->parent) + ); +} + +// +// Is cycle count inhibited? +// +Bool riscvInhibitCycle(riscvP riscv) { + return RD_CSR_FIELD(riscv, mcountinhibit, CY) || stopCount(riscv, True); +} + +// +// Is retired instruction count inhibited? +// +Bool riscvInhibitInstret(riscvP riscv) { + return RD_CSR_FIELD(riscv, mcountinhibit, IR) || stopCount(riscv, False); +} + // // Common routine to read cycle counter // @@ -1125,7 +1209,7 @@ static Uns64 cycleR(riscvP riscv) { Uns64 result = riscv->baseCycles; - if(!RD_CSR_FIELD(riscv, mcountinhibit, CY)) { + if(!riscvInhibitCycle(riscv)) { result = getCycles(riscv) - result; } @@ -1134,15 +1218,15 @@ static Uns64 cycleR(riscvP riscv) { // // Common routine to write cycle counter (NOTE: count is notionally incremented -// *before* the write) +// *before* the write if this is the result of a CSR write) // -static void cycleW(riscvP riscv, Uns64 newValue) { +static void cycleW(riscvP riscv, Uns64 newValue, Bool preIncrement) { - if(!RD_CSR_FIELD(riscv, mcountinhibit, CY)) { + if(!riscvInhibitCycle(riscv)) { newValue = getCycles(riscv) - newValue; - if(!riscv->artifactAccess) { + if(preIncrement && !riscv->artifactAccess) { newValue++; } } @@ -1172,9 +1256,9 @@ static RISCV_CSR_WRITEFN(mcycleW) { if(!hpmAccessValid(attrs, riscv)) { // no action } else if(RISCV_XLEN_IS_32(riscv)) { - cycleW(riscv, setLower(newValue, cycleR(riscv))); + cycleW(riscv, setLower(newValue, cycleR(riscv)), True); } else { - cycleW(riscv, newValue); + cycleW(riscv, newValue, True); } return newValue; @@ -1200,7 +1284,7 @@ static RISCV_CSR_READFN(mcyclehR) { static RISCV_CSR_WRITEFN(mcyclehW) { if(hpmAccessValid(attrs, riscv)) { - cycleW(riscv, setUpper(newValue, cycleR(riscv))); + cycleW(riscv, setUpper(newValue, cycleR(riscv)), True); } return newValue; @@ -1248,7 +1332,7 @@ static Uns64 instretR(riscvP riscv) { Uns64 result = riscv->baseInstructions; - if(!RD_CSR_FIELD(riscv, mcountinhibit, IR)) { + if(!riscvInhibitInstret(riscv)) { result = getInstructions(riscv) - result; } @@ -1261,7 +1345,7 @@ static Uns64 instretR(riscvP riscv) { // static void instretW(riscvP riscv, Uns64 newValue) { - if(!RD_CSR_FIELD(riscv, mcountinhibit, IR)) { + if(!riscvInhibitInstret(riscv)) { newValue = getInstructions(riscv) - newValue + 1; } @@ -1324,28 +1408,46 @@ static RISCV_CSR_WRITEFN(minstrethW) { return newValue; } +// +// Get state before possible inhibit update +// +void riscvPreInhibit(riscvP riscv, riscvCountStateP state) { + + state->inhibitCycle = riscvInhibitCycle(riscv); + state->inhibitInstret = riscvInhibitInstret(riscv); + state->cycle = cycleR(riscv); + state->instret = instretR(riscv); +} + +// +// Update state after possible inhibit update +// +void riscvPostInhibit(riscvP riscv, riscvCountStateP state, Bool preIncrement) { + + // set cycle and instret counters *after* mcountinhibit update + if(state->inhibitCycle != riscvInhibitCycle(riscv)) { + cycleW(riscv, state->cycle, preIncrement); + } + if(state->inhibitInstret != riscvInhibitInstret(riscv)) { + instretW(riscv, state->instret); + } +} + // // Write mcountinhibit // static RISCV_CSR_WRITEFN(mcountinhibitW) { - Bool oldCY = RD_CSR_FIELD(riscv, mcountinhibit, CY); - Bool oldIR = RD_CSR_FIELD(riscv, mcountinhibit, IR); + riscvCountState state; - // get cycle and instret counters *before* mcountinhibit update - Uns64 cycle = cycleR(riscv); - Uns64 instret = instretR(riscv); + // get state before possible inhibit update + riscvPreInhibit(riscv, &state); // update the CSR WR_CSR(riscv, mcountinhibit, newValue); - // set cycle and instret counters *after* mcountinhibit update - if(oldCY != RD_CSR_FIELD(riscv, mcountinhibit, CY)) { - cycleW(riscv, cycle); - } - if(oldIR != RD_CSR_FIELD(riscv, mcountinhibit, IR)) { - instretW(riscv, instret); - } + // refresh state after possible inhibit update + riscvPostInhibit(riscv, &state, True); return newValue; } @@ -1543,23 +1645,6 @@ static RISCV_CSR_READFN(vcsrR) { // initially clear register WR_CSR(riscv, vcsr, 0); - // update floating point fields only if enabled - if(riscv->currentArch & ISA_DF) { - - // construct effective flags from CSR and JIT flags - vmiFPFlags vmiFlags = getFPFlags(riscv); - - // compose flags in register value - WR_CSR_FIELD(riscv, vcsr, NX, vmiFlags.f.P); - WR_CSR_FIELD(riscv, vcsr, UF, vmiFlags.f.U); - WR_CSR_FIELD(riscv, vcsr, OF, vmiFlags.f.O); - WR_CSR_FIELD(riscv, vcsr, DZ, vmiFlags.f.Z); - WR_CSR_FIELD(riscv, vcsr, NV, vmiFlags.f.I); - - // compose frm in register value (mastered in fcsr) - WR_CSR_FIELD(riscv, vcsr, frm, getMasterFRM(riscv)); - } - // get fixed point saturation alias WR_CSR_FIELD(riscv, vcsr, vxsat, getSatFlags(riscv)); @@ -1575,30 +1660,8 @@ static RISCV_CSR_READFN(vcsrR) { // static RISCV_CSR_WRITEFN(vcsrW) { - Uns64 mask = RD_CSR_MASK(riscv, vcsr); - Uns8 oldRM = getMasterFRM(riscv); - // update the CSR - WR_CSR(riscv, vcsr, newValue & mask); - - // update floating point fields only if enabled - if(riscv->currentArch & ISA_DF) { - - vmiFPFlags vmiFlags = {bits: 0}; - - // extract flags from register value - vmiFlags.f.P = RD_CSR_FIELD(riscv, vcsr, NX); - vmiFlags.f.U = RD_CSR_FIELD(riscv, vcsr, UF); - vmiFlags.f.O = RD_CSR_FIELD(riscv, vcsr, OF); - vmiFlags.f.Z = RD_CSR_FIELD(riscv, vcsr, DZ); - vmiFlags.f.I = RD_CSR_FIELD(riscv, vcsr, NV); - - // assign CSR flags and clear JIT flags (floating point) - setFPFlags(riscv, vmiFlags); - - // handle change to rounding modes - setFPRoundingMode(riscv, oldRM, RD_CSR_FIELD(riscv, vcsr, frm)); - } + WR_CSR(riscv, vcsr, newValue & WM32_vcsr); // assign CSR flags and clear JIT flags (fixed point) setSatFlags(riscv, RD_CSR_FIELD(riscv, vcsr, vxsat)); @@ -1661,6 +1724,75 @@ void riscvSetVL(riscvP riscv, Uns64 vl) { WR_CSR(riscv, vl, vl); } +// +// Reset vector state +// +static void resetVLVType(riscvP riscv) { + + if(riscv->configInfo.arch & ISA_V) { + + // reset vtype CSR + riscvSetVType(riscv, !riscvValidSEW(riscv, 0), 0, 0); + + // reset VL CSR + riscvSetVL(riscv, 0); + + // set vector polymorphic key + riscvRefreshVectorPMKey(riscv); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// DEBUG MODE REGISTERS +//////////////////////////////////////////////////////////////////////////////// + +// +// Are Debug Mode registers present? +// +inline static RISCV_CSR_PRESENTFN(debugP) { + return riscv->configInfo.debug_mode; +} + +// +// Internal interface for dcsr write +// +static void dcsrWInt(riscvP riscv, Uns32 newValue, Bool updateCause) { + + Uns32 oldValue = RD_CSR(riscv, dcsr); + Uns32 mask = RD_CSR_MASK(riscv, dcsr); + riscvCountState state; + + // get state before possible inhibit update + riscvPreInhibit(riscv, &state); + + // preserve cause value unless an artifact write + if(!updateCause) { + mask &= ~(WM32_dcsr_cause|WM32_dcsr_nmip); + } + + // update value + WR_CSR(riscv, dcsr, ((newValue & mask) | (oldValue & ~mask))); + + // set step breakpoint if required + riscvSetStepBreakpoint(riscv); + + // refresh state after possible inhibit update + riscvPostInhibit(riscv, &state, True); +} + +// +// Write dcsr +// +static RISCV_CSR_WRITEFN(dcsrW) { + + // call internal interface + dcsrWInt(riscv, newValue, riscv->artifactAccess); + + // return written value + return RD_CSR(riscv, dcsr); +} + //////////////////////////////////////////////////////////////////////////////// // MACROS FOR UNIMPLEMENTED CSRS @@ -1922,6 +2054,7 @@ static const riscvCSRAttrs csrs[CSR_ID(LAST)] = { CSR_ATTR_T__ (mie, 0x304, 0, 0, 1_10, 1,0,0, "Machine Interrupt Enable", 0, 0, 0, 0, mieW ), CSR_ATTR_T__ (mtvec, 0x305, 0, 0, 1_10, 0,0,0, "Machine Trap-Vector Base-Address", 0, 0, 0, 0, mtvecW ), CSR_ATTR_TV_ (mcounteren, 0x306, ISA_SorU, 0, 1_10, 0,0,0, "Machine Counter Enable", 0, 0, 0, 0, 0 ), + CSR_ATTR_TV_ (mstatush, 0x310, ISA_XLEN_32, 0, 1_12, 0,0,0, "Machine Status High", 0, 0, 0, 0, mstatushW ), CSR_ATTR_TV_ (mcountinhibit,0x320, 0, 0, 1_11, 0,0,0, "Machine Counter Inhibit", 0, 0, 0, 0, mcountinhibitW), CSR_ATTR_T__ (mscratch, 0x340, 0, 0, 1_10, 0,0,0, "Machine Scratch", 0, 0, 0, 0, 0 ), CSR_ATTR_TV_ (mepc, 0x341, 0, 0, 1_10, 0,0,0, "Machine Exception Program Counter", 0, 0, mepcR, 0, 0 ), @@ -1952,10 +2085,10 @@ static const riscvCSRAttrs csrs[CSR_ID(LAST)] = { CSR_ATTR_NIP (tdata3, 0x7A3, 0, 0, 1_10, 0,0,0, "Debug/Trace Trigger Data 3" ), // name num arch access version attrs description present wState rCB rwCB wCB - // TODO: these are undefined in all modes - CSR_ATTR_NIP (dcsr, 0x7B0, 0, 0, 1_10, 0,0,0, "Debug Control and Status" ), - CSR_ATTR_NIP (dpc, 0x7B1, 0, 0, 1_10, 0,0,0, "Debug PC" ), - CSR_ATTR_NIP (dscratch, 0x7B2, 0, 0, 1_10, 0,0,0, "Debug Scratch" ), + CSR_ATTR_TV_ (dcsr, 0x7B0, 0, 0, 1_10, 0,0,0, "Debug Control and Status", debugP, 0, 0, 0, dcsrW ), + CSR_ATTR_T__ (dpc, 0x7B1, 0, 0, 1_10, 0,0,0, "Debug PC", debugP, 0, 0, 0, 0 ), + CSR_ATTR_T__ (dscratch0, 0x7B2, 0, 0, 1_10, 0,0,0, "Debug Scratch 0", debugP, 0, 0, 0, 0 ), + CSR_ATTR_T__ (dscratch1, 0x7B3, 0, 0, 1_10, 0,0,0, "Debug Scratch 1", debugP, 0, 0, 0, 0 ), }; @@ -2517,6 +2650,11 @@ static void fromConfiguredArch(riscvCSRAttrsCP attrs, riscvP riscv) { SET_CSR_FIELD_MASK_1_32(_CPU, _RNAME, _FIELD); \ SET_CSR_FIELD_MASK_1_64(_CPU, _RNAME, _FIELD) +// set field mask to all 1's in all CSR masks, alternate names for 32/64 bit +#define SET_CSR_FIELD_MASK_1_ALT(_CPU, _RNAME32, _RNAME64, _FIELD) \ + SET_CSR_FIELD_MASK_1_32(_CPU, _RNAME32, _FIELD); \ + SET_CSR_FIELD_MASK_1_64(_CPU, _RNAME64, _FIELD) + // // Reset CSR state // @@ -2538,12 +2676,18 @@ void riscvCSRReset(riscvP riscv) { // update cause register (to zero) WR_CSR(riscv, mcause, 0); + // reset vector state + resetVLVType(riscv); + // update current architecture on change to misa or mstatus riscvSetCurrentArch(riscv); // reset PMP unit riscvVMResetPMP(riscv); + // reset dcsr + dcsrWInt(riscv, RISCV_MODE_MACHINE, True); + // clear exclusive tag riscv->exclusiveTag = RISCV_NO_TAG; } @@ -2724,6 +2868,28 @@ void riscvCSRInit(riscvP riscv, Uns32 index) { } } + // initialize endian values and write masks + if(riscvSupportEndian(riscv)) { + + Bool BE = riscv->dendian; + + // enable and initialize MBE field in mstatus or mstatush + SET_CSR_FIELD_MASK_1_ALT(riscv, mstatush, mstatus, MBE); + WR_CSR_FIELD_ALT(riscv, mstatush, mstatus, MBE, BE); + + // enable and initialize SBE field in mstatus or mstatush + if(arch&ISA_S) { + SET_CSR_FIELD_MASK_1_ALT(riscv, mstatush, mstatus, SBE); + WR_CSR_FIELD_ALT(riscv, mstatush, mstatus, SBE, BE); + } + + // enable and initialize UBE field in mstatus + if(arch&ISA_U) { + SET_CSR_FIELD_MASK_1(riscv, mstatus, UBE); + WR_CSR_FIELD(riscv, mstatus, UBE, BE); + } + } + //-------------------------------------------------------------------------- // uepc, sepc, mepc masks //-------------------------------------------------------------------------- @@ -2912,25 +3078,6 @@ void riscvCSRInit(riscvP riscv, Uns32 index) { // set initial rounding-mode-valid state updateCurrentRMValid(riscv); - //-------------------------------------------------------------------------- - // vcsr mask - //-------------------------------------------------------------------------- - - if((arch&ISA_V) && riscvVFSupport(riscv, RVVF_VCSR_PRESENT)) { - - Uns32 vcsrMask = 0; - - // enable floating point fields if required - if(arch&ISA_DF) { - vcsrMask |= WM32_vcsr_f; - } - - // enable fixed point fields - vcsrMask |= WM32_vcsr_v; - - SET_CSR_MASK_V(riscv, vcsr, vcsrMask); - } - //-------------------------------------------------------------------------- // vlenb //-------------------------------------------------------------------------- @@ -2941,12 +3088,38 @@ void riscvCSRInit(riscvP riscv, Uns32 index) { // vstart mask and polymorphic key //-------------------------------------------------------------------------- - if((arch&ISA_V) && !riscvVFSupport(riscv, RVVF_VSTART_Z)) { - SET_CSR_MASK_V(riscv, vstart, cfg->VLEN-1); - } + SET_CSR_MASK_V(riscv, vstart, cfg->VLEN-1); // set initial vector polymorphic key riscvRefreshVectorPMKey(riscv); + + //-------------------------------------------------------------------------- + // dcsr mask and read-only fields + //-------------------------------------------------------------------------- + + // initialize dcsr writable fields + SET_CSR_FIELD_MASK_1(riscv, dcsr, ebreakm); + SET_CSR_FIELD_MASK_1(riscv, dcsr, stepie); + SET_CSR_FIELD_MASK_1(riscv, dcsr, stopcount); + SET_CSR_FIELD_MASK_1(riscv, dcsr, stoptime); + SET_CSR_FIELD_MASK_1(riscv, dcsr, cause); + SET_CSR_FIELD_MASK_1(riscv, dcsr, mprven); + SET_CSR_FIELD_MASK_1(riscv, dcsr, nmip); + SET_CSR_FIELD_MASK_1(riscv, dcsr, step); + SET_CSR_FIELD_MASK_1(riscv, dcsr, prv); + + // initialize dcsr mask writable fields if Supervisor mode present + if(arch&ISA_S) { + SET_CSR_FIELD_MASK_1(riscv, dcsr, ebreaks); + } + + // initialize dcsr mask writable fields if User mode present + if(arch&ISA_U) { + SET_CSR_FIELD_MASK_1(riscv, dcsr, ebreaku); + } + + // initialize dcsr read-only fields + WR_CSR_FIELD(riscv, dcsr, xdebugver, 4); } // @@ -3176,6 +3349,13 @@ static riscvArchitecture getInaccessibleCSRFeaturesMT( return getMissingCSRFeatures(attrs, riscv, required, current); } +// +// Is this an illegal Debug CSR access in Machine mode? +// +static Bool invalidDebugCSRAccess(riscvP riscv, Uns32 csrNum) { + return IS_DEBUG_CSR(csrNum) && !inDebugMode(riscv); +} + // // Validate CSR with the given index can be accessed for read or write in the // current processor mode, and return either a true CSR id or an error code id @@ -3190,7 +3370,13 @@ riscvCSRAttrsCP riscvValidateCSRAccess( riscvCSRAttrsCP attrs = getCSRAttrs(riscv, csrNum); riscvArchitecture missing; - if(!attrs || !checkCSRImplemented(attrs, riscv)) { + if(invalidDebugCSRAccess(riscv, csrNum)) { + + // CSR is not implemented + ILLEGAL_INSTRUCTION_MESSAGE(riscv, "CSR_NA", "Debug CSR not accessible"); + return 0; + + } else if(!attrs || !checkCSRImplemented(attrs, riscv)) { // CSR is not implemented ILLEGAL_INSTRUCTION_MESSAGE(riscv, "CSR_UNIMP", "Unimplemented CSR"); diff --git a/source/riscvCSR.h b/source/riscvCSR.h index 5cb9867..723b823 100644 --- a/source/riscvCSR.h +++ b/source/riscvCSR.h @@ -147,6 +147,7 @@ typedef enum riscvCSRIdE { CSR_ID (mie), // 0x304 CSR_ID (mtvec), // 0x305 CSR_ID (mcounteren), // 0x306 + CSR_ID (mstatush), // 0x310 CSR_ID (mcountinhibit),// 0x320 CSR_ID (mscratch), // 0x340 CSR_ID (mepc), // 0x341 @@ -167,15 +168,24 @@ typedef enum riscvCSRIdE { CSR_ID (tdata1), // 0x7A1 CSR_ID (tdata2), // 0x7A2 CSR_ID (tdata3), // 0x7A3 + CSR_ID (dcsr), // 0x7B0 CSR_ID (dpc), // 0x7B1 - CSR_ID (dscratch), // 0x7B2 + CSR_ID (dscratch0), // 0x7B2 + CSR_ID (dscratch1), // 0x7B3 // keep last (used to define size of the enumeration) CSR_ID (LAST) } riscvCSRId; +// +// CSRs in this range are accessible only in debug mode +// +#define CSR_DEGUG_START 0x7B0 +#define CSR_DEGUG_END 0x7BF +#define IS_DEBUG_CSR(_NUM) (((_NUM)>=CSR_DEGUG_START) && ((_NUM)<=CSR_DEGUG_END)) + //////////////////////////////////////////////////////////////////////////////// // INITIALIZATION @@ -287,6 +297,41 @@ Bool riscvGetCSRDetails(riscvP riscv, riscvCSRDetailsP details, Bool normal); void riscvNewCSR(riscvCSRAttrsCP attrs, riscvP riscv); +//////////////////////////////////////////////////////////////////////////////// +// COUNTER INHIBIT +//////////////////////////////////////////////////////////////////////////////// + +// +// Structure used when updating state when inhibit values change +// +typedef struct riscvCountStateS { + Bool inhibitCycle; // old value of cycle count inhibit + Bool inhibitInstret; // old value of retired instruction inhibit + Uns64 cycle; // cycle count before update + Uns64 instret; // retired instruction count before update +} riscvCountState, *riscvCountStateP; + +// +// Get state before possible inhibit update +// +void riscvPreInhibit(riscvP riscv, riscvCountStateP state); + +// +// Update state after possible inhibit update +// +void riscvPostInhibit(riscvP riscv, riscvCountStateP state, Bool preIncrement); + +// +// Is cycle count inhibited? +// +Bool riscvInhibitCycle(riscvP riscv); + +// +// Is retired instruction count inhibited? +// +Bool riscvInhibitInstret(riscvP riscv); + + //////////////////////////////////////////////////////////////////////////////// // SAVE/RESTORE SUPPORT //////////////////////////////////////////////////////////////////////////////// @@ -446,7 +491,7 @@ typedef struct { Uns32 MIE : 1; // Machine mode interrupt enable Uns32 UPIE : 1; // User mode interrupt enable (stacked), N only Uns32 SPIE : 1; // Supervisor mode interrupt enable (stacked) - Uns32 _u1 : 1; + Uns32 UBE : 1; // User mode big-endian Uns32 MPIE : 1; // Machine mode interrupt enable (stacked) Uns32 SPP : 1; // Supervisor previous mode Uns32 VS_9 : 2; // Vector Extension dirty state (version 0.9) @@ -472,10 +517,10 @@ typedef struct { Uns64 MIE : 1; // Machine mode interrupt enable Uns64 UPIE : 1; // User mode interrupt enable (stacked), N only Uns64 SPIE : 1; // Supervisor mode interrupt enable (stacked) - Uns64 _u1 : 1; + Uns64 UBE : 1; // User mode big-endian Uns64 MPIE : 1; // Machine mode interrupt enable (stacked) Uns64 SPP : 1; // Supervisor previous mode - Uns32 VS_9 : 2; // Vector Extension dirty state (version 0.9) + Uns32 VS_9 : 2; // Vector Extension dirty state (version 0.9) Uns64 MPP : 2; // Machine previous mode Uns64 FS : 2; // Floating point dirty state Uns64 XS : 2; // User extension dirty state @@ -485,11 +530,13 @@ typedef struct { Uns64 TVM : 1; // Trap virtual memory (requires S extension) Uns64 TW : 1; // Timeout wait (requires S extension) Uns64 TSR : 1; // Trap SRET (requires S extension) - Uns32 VS_8 : 2; // Vector Extension dirty state (version 0.8) + Uns32 VS_8 : 2; // Vector Extension dirty state (version 0.8) Uns64 _u3 : 7; Uns64 UXL : 2; // TODO: User mode XLEN Uns64 SXL : 2; // TODO: Supervisor mode XLEN - Uns64 _u4 : 27; + Uns64 SBE : 1; // Supervisor mode big-endian + Uns64 MBE : 1; // Machine mode big-endian + Uns64 _u4 : 25; Uns64 SD : 1; // Dirty state summary bit (read only) } CSR_REG_TYPE_64(status); @@ -513,6 +560,30 @@ typedef CSR_REG_TYPE(status) CSR_REG_TYPE(mstatus); #define WM_mstatus_VS_8 (3<<23) #define WM_mstatus_VS_9 (3<<9) #define WM_mstatus_IE 0xf +#define WM_mstatus_UBE (1<<6) +#define WM_mstatus_SBE (1ULL<<36) +#define WM_mstatus_MBE (1ULL<<37) +#define WM_mstatus_BE (WM_mstatus_UBE|WM_mstatus_SBE|WM_mstatus_MBE) + +// ----------------------------------------------------------------------------- +// mstatush (id 0x310) +// ----------------------------------------------------------------------------- + +// 32-bit view +typedef struct { + Uns32 _u0 : 4; + Uns32 SBE : 1; + Uns32 MBE : 1; + Uns32 _u1 : 26; +} CSR_REG_TYPE_32(mstatush); + +// define 32 bit type +CSR_REG_STRUCT_DECL_32(mstatush); + +// write masks +#define WM_mstatush_SBE (1ULL<<4) +#define WM_mstatush_MBE (1ULL<<5) +#define WM_mstatush_BE (WM_mstatush_SBE|WM_mstatush_MBE) // ----------------------------------------------------------------------------- // fflags (id 0x001) @@ -1015,8 +1086,41 @@ typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(tdata); // dcsr (id 0x7B0) // ----------------------------------------------------------------------------- -// define alias types -typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dcsr); +// cause for entry to Debug mode +typedef enum dmCauseE { + DMC_NONE = 0, + DMC_EBREAK = 1, + DMC_TRIGGER = 2, + DMC_HALTREQ = 3, + DMC_STEP = 4, + DMC_RESETHALTREQ = 5, + DMC_HALTGROUP = 6, +} dmCause; + +// 32-bit view +typedef struct { + Uns32 prv : 2; + Uns32 step : 1; + Uns32 nmip : 1; + Uns32 mprven : 1; + Uns32 _u0 : 1; + dmCause cause : 3; + Uns32 stoptime : 1; + Uns32 stopcount : 1; + Uns32 stepie : 1; + Uns32 ebreaku : 1; + Uns32 ebreaks : 1; + Uns32 _u1 : 1; + Uns32 ebreakm : 1; + Uns32 _u2 : 12; + Uns32 xdebugver : 4; +} CSR_REG_TYPE_32(dcsr); + +// define 32 bit type +CSR_REG_STRUCT_DECL_32(dcsr); + +#define WM32_dcsr_nmip 0x008 +#define WM32_dcsr_cause 0x1c0 // ----------------------------------------------------------------------------- // dpc (id 0x7B1) @@ -1025,12 +1129,31 @@ typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dcsr); // define alias types typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dpc); +// define write masks +#define WM32_dpc -2 +#define WM64_dpc -2 + +// ----------------------------------------------------------------------------- +// dscratch0 (id 0x7B2) +// ----------------------------------------------------------------------------- + +// define alias types +typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dscratch0); + +// define write masks +#define WM32_dscratch0 -1 +#define WM64_dscratch0 -1 + // ----------------------------------------------------------------------------- -// dscratch (id 0x7B2) +// dscratch1 (id 0x7B3) // ----------------------------------------------------------------------------- // define alias types -typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dscratch); +typedef CSR_REG_TYPE(genericXLEN) CSR_REG_TYPE(dscratch1); + +// define write masks +#define WM32_dscratch1 -1 +#define WM64_dscratch1 -1 // ----------------------------------------------------------------------------- // vstart (id 0x008) @@ -1079,24 +1202,16 @@ CSR_REG_STRUCT_DECL_32(vxrm); // 32-bit view typedef struct { - Uns32 NX : 1; - Uns32 UF : 1; - Uns32 OF : 1; - Uns32 DZ : 1; - Uns32 NV : 1; - Uns32 frm : 3; Uns32 vxsat : 1; Uns32 vxrm : 2; - Uns32 _u0 : 21; + Uns32 _u0 : 29; } CSR_REG_TYPE_32(vcsr); // define 32 bit type CSR_REG_STRUCT_DECL_32(vcsr); // write masks -#define WM32_vcsr_f 0x0ff -#define WM32_vcsr_v 0x700 -#define WM32_vcsr_frm_msb 0x080 +#define WM32_vcsr 0x00000007 // ----------------------------------------------------------------------------- // vl (id 0xC20) @@ -1200,6 +1315,7 @@ typedef struct riscvCSRsS { CSR_REG_DECL(mie); // 0x304 CSR_REG_DECL(mtvec); // 0x305 CSR_REG_DECL(mcounteren); // 0x306 + CSR_REG_DECL(mstatush); // 0x310 CSR_REG_DECL(mcountinhibit);// 0x320 CSR_REG_DECL(mscratch); // 0x340 CSR_REG_DECL(mepc); // 0x341 @@ -1207,6 +1323,12 @@ typedef struct riscvCSRsS { CSR_REG_DECL(mtval); // 0x343 CSR_REG_DECL(mip); // 0x344 + // DEBUG MODE CSRS + CSR_REG_DECL(dcsr); // 0x7B0 + CSR_REG_DECL(dpc); // 0x7B1 + CSR_REG_DECL(dscratch0); // 0x7B2 + CSR_REG_DECL(dscratch1); // 0x7B3 + } riscvCSRs; @@ -1223,7 +1345,6 @@ typedef struct riscvCSRMasksS { CSR_REG_DECL(fcsr); // 0x003 CSR_REG_DECL(utvec); // 0x005 CSR_REG_DECL(vstart); // 0x008 - CSR_REG_DECL(vcsr); // 0x00F CSR_REG_DECL(uepc); // 0x041 CSR_REG_DECL(ucause); // 0x042 @@ -1243,10 +1364,14 @@ typedef struct riscvCSRMasksS { CSR_REG_DECL(mie); // 0x304 CSR_REG_DECL(mtvec); // 0x305 CSR_REG_DECL(mcounteren); // 0x306 + CSR_REG_DECL(mstatush); // 0x310 CSR_REG_DECL(mcountinhibit);// 0x320 CSR_REG_DECL(mepc); // 0x341 CSR_REG_DECL(mcause); // 0x342 + // DEBUG MODE CSRS + CSR_REG_DECL(dcsr); // 0x7B0 + } riscvCSRMasks; @@ -1282,6 +1407,20 @@ typedef struct riscvCSRMasksS { (_CPU)->csr._RNAME.u64.fields._FIELD = _VALUE; \ } +// get CSR field using current XLEN from 32/64 bit alternate registers +#define RD_CSR_FIELD_ALT(_CPU, _RNAME32, _RNAME64, _FIELD) \ + (RISCV_XLEN_IS_32(_CPU) ? \ + (_CPU)->csr._RNAME32.u32.fields._FIELD : \ + (_CPU)->csr._RNAME64.u64.fields._FIELD) \ + +// set CSR field using current XLEN from 32/64 bit alternate registers +#define WR_CSR_FIELD_ALT(_CPU, _RNAME32, _RNAME64, _FIELD, _VALUE) \ + if(RISCV_XLEN_IS_32(_CPU)) { \ + (_CPU)->csr._RNAME32.u32.fields._FIELD = _VALUE; \ + } else { \ + (_CPU)->csr._RNAME64.u64.fields._FIELD = _VALUE; \ + } + // set CSR field when XLEN is 64 #define WR_CSR64_FIELD(_CPU, _RNAME, _FIELD, _VALUE) \ if(RISCV_XLEN_IS_64(_CPU)) { \ diff --git a/source/riscvCluster.c b/source/riscvCluster.c index aabb446..343fbb9 100644 --- a/source/riscvCluster.c +++ b/source/riscvCluster.c @@ -90,8 +90,8 @@ Bool riscvIsClusterMember(riscvP riscv) { // const char *riscvGetClusterVariant(riscvP cluster, riscvP member) { - Uns32 i = 0; - riscvP try; + Uns32 i = 0; + riscvP try; // get index of next variant string for(try=getChild(cluster); try!=member; try=getSibling(try)) { diff --git a/source/riscvConfig.h b/source/riscvConfig.h index f565770..030b138 100644 --- a/source/riscvConfig.h +++ b/source/riscvConfig.h @@ -36,6 +36,11 @@ #define RV_DOC_FN(_NAME) void _NAME(riscvP riscv, vmiDocNodeP node) typedef RV_DOC_FN((*riscvDocFn)); +// +// value indicating numHarts is 0 but configurable +// +#define RV_NUMHARTS_0 -1 + // // This is used to specify documentation and configuration for mandatory // extensions @@ -78,6 +83,8 @@ typedef struct riscvConfigS { Uns32 ELEN; // ELEN (vector extension) Uns32 SLEN; // SLEN (vector extension) Uns32 VLEN; // VLEN (vector extension) + Uns32 SEW_min; // minimum SEW (vector extension) + Bool debug_mode; // is Debug mode implemented? Bool Zvlsseg; // Zvlsseg implemented? Bool Zvamo; // Zvamo implemented? Bool Zvediv; // Zvediv implemented? diff --git a/source/riscvConfigList.c b/source/riscvConfigList.c index 9e5aca1..146c1e1 100644 --- a/source/riscvConfigList.c +++ b/source/riscvConfigList.c @@ -37,7 +37,8 @@ .Zvqmac = 1, \ .PMP_registers = _PMP_REGS, \ .tval_ii_code = True, \ - .ASID_bits = ((_ARCH)&RV64) ? 16 : 9 \ + .ASID_bits = ((_ARCH)&RV64) ? 16 : 9, \ + .numHarts = RV_NUMHARTS_0 \ } // diff --git a/source/riscvDebug.c b/source/riscvDebug.c index c5f79ae..f500e20 100644 --- a/source/riscvDebug.c +++ b/source/riscvDebug.c @@ -24,12 +24,14 @@ #include "vmi/vmiAttrs.h" #include "vmi/vmiDbg.h" #include "vmi/vmiMessage.h" +#include "vmi/vmiMt.h" #include "vmi/vmiRt.h" // model header files #include "riscvCSR.h" #include "riscvCSRTypes.h" #include "riscvDebug.h" +#include "riscvExceptions.h" #include "riscvFunctions.h" #include "riscvMessage.h" #include "riscvRegisters.h" @@ -104,7 +106,7 @@ static const vmiRegGroup groups[RV_RG_LAST+1] = { DEFINE_CS(isrDetails); // -// Structure filled with CSR register details by riscvGetCSRDetails +// Structure providing details of integration support registers // typedef struct isrDetailsS { const char *name; @@ -117,14 +119,29 @@ typedef struct isrDetailsS { vmiRegWriteFn writeCB; vmiRegAccess access; Bool noTraceChange; + Bool DM; } isrDetails; +// +// Write processor DM bit (enables or disables Debug mode) +// +static VMI_REG_WRITE_FN(writeDM) { + + riscvP riscv = (riscvP)processor; + Uns8 DM = *(Uns8*)buffer; + + riscvSetDM(riscv, DM&1); + + return True; +} + // // List of integration support registers // static const isrDetails isRegs[] = { - {"LRSCAddress", "LR/SC active lock address", ISA_A, 0, 0, RISCV_EA_TAG, 0, 0, vmi_RA_RW, 0}, + {"LRSCAddress", "LR/SC active lock address", ISA_A, 0, 0, RISCV_EA_TAG, 0, 0, vmi_RA_RW, 0, 0}, + {"DM", "Debug mode active", 0, 1, 8, RISCV_DM, 0, writeDM, vmi_RA_RW, 0, 1}, // KEEP LAST {0} @@ -144,7 +161,15 @@ static isrDetailsCP getNextISRDetails( riscvArchitecture arch = riscv->configInfo.arch; isrDetailsCP this = prev ? prev+1 : isRegs; - while(this->name && ((this->arch&arch)!=this->arch)) { + while( + this->name && + ( + // exclude registers not applicable to this architecture + ((this->arch&arch)!=this->arch) || + // exclude debug mode registers if that mode is absent + (this->DM && !riscv->configInfo.debug_mode) + ) + ) { this++; } @@ -506,7 +531,7 @@ void riscvFreeRegInfo(riscvP riscv) { vmirtRegImplRaw(processor, _REG, _FIELD, _BITS) // -// Helper macro for defining field-to register mappings +// Helper macro for defining field-to-register mappings // #define RISCV_FIELD_IMPL_RAW(_REGINFO, _FIELD) { \ Uns32 bits = sizeof(((riscvP)0)->_FIELD) * 8; \ @@ -519,12 +544,30 @@ void riscvFreeRegInfo(riscvP riscv) { #define RISCV_FIELD_IMPL_IGNORE(_FIELD) \ RISCV_FIELD_IMPL_RAW(0, _FIELD) +// +// Ignore register definitions for artifact vector index tables +// +static void offsetRegIgnore(riscvP riscv, riscvStrideOffset *base, Uns32 mul) { + + vmiProcessorP processor = (vmiProcessorP)riscv; + Uns32 vRegBytes = riscv->configInfo.VLEN/8; + + vmirtRegImplRaw( + processor, + 0, + vmimtGetExtReg(processor, base), + vRegBytes*sizeof(*base)*8*mul + ); +} + // // Specify vmiReg-to-vmiRegInfoCP correspondence for registers for which this // cannot be automatically derived // VMI_REG_IMPL_FN(riscvRegImpl) { + riscvP riscv = (riscvP)processor; + // specify that fpFlags is in fflags vmiRegInfoCP fflags = vmirtGetRegByName(processor, "fflags"); RISCV_FIELD_IMPL_RAW(fflags, fpFlagsMT); @@ -541,6 +584,13 @@ VMI_REG_IMPL_FN(riscvRegImpl) { RISCV_FIELD_IMPL_IGNORE(offsetsLMULx4); RISCV_FIELD_IMPL_IGNORE(offsetsLMULx8); RISCV_FIELD_IMPL_IGNORE(jumpBase); + + // exclude artifact vector index registers + if(riscv->configInfo.arch & ISA_V) { + offsetRegIgnore(riscv, riscv->offsetsLMULx2, 2); + offsetRegIgnore(riscv, riscv->offsetsLMULx4, 4); + offsetRegIgnore(riscv, riscv->offsetsLMULx8, 8); + } } diff --git a/source/riscvDecode.c b/source/riscvDecode.c index deb4aed..4364d42 100644 --- a/source/riscvDecode.c +++ b/source/riscvDecode.c @@ -501,6 +501,7 @@ typedef enum riscvIType32E { IT32_MRET_I, IT32_SRET_I, IT32_URET_I, + IT32_DRET_I, IT32_WFI_I, // system fence I-type instruction @@ -1094,6 +1095,7 @@ const static decodeEntry32 decodeCommon32[] = { DECODE32_ENTRY( MRET_I, "|001100000010|00000|000|00000|1110011|"), DECODE32_ENTRY( SRET_I, "|000100000010|00000|000|00000|1110011|"), DECODE32_ENTRY( URET_I, "|000000000010|00000|000|00000|1110011|"), + DECODE32_ENTRY( DRET_I, "|011110110010|00000|000|00000|1110011|"), DECODE32_ENTRY( WFI_I, "|000100000101|00000|000|00000|1110011|"), // system fence I-type instruction @@ -1878,6 +1880,7 @@ const static opAttrs attrsArray32[] = { ATTR32_NOP ( MRET_I, MRET_I, RVANY, "mret" ), ATTR32_NOP ( SRET_I, SRET_I, RVANY, "sret" ), ATTR32_NOP ( URET_I, URET_I, RVANYN, "uret" ), + ATTR32_NOP ( DRET_I, DRET_I, RVANY, "dret" ), ATTR32_NOP ( WFI_I, WFI_I, RVANY, "wfi" ), // system fence I-type instruction diff --git a/source/riscvDecodeTypes.h b/source/riscvDecodeTypes.h index 8503f2a..09cbca8 100644 --- a/source/riscvDecodeTypes.h +++ b/source/riscvDecodeTypes.h @@ -89,6 +89,7 @@ typedef enum riscvITypeE { RV_IT_MRET_I, RV_IT_SRET_I, RV_IT_URET_I, + RV_IT_DRET_I, RV_IT_WFI_I, // system fence I-type instruction diff --git a/source/riscvDoc.c b/source/riscvDoc.c index cb2658d..7221de8 100644 --- a/source/riscvDoc.c +++ b/source/riscvDoc.c @@ -28,6 +28,7 @@ #include "vmi/vmiRt.h" // model header files +#include "riscvCluster.h" #include "riscvDoc.h" #include "riscvFunctions.h" #include "riscvParameters.h" @@ -122,8 +123,8 @@ void riscvDoc(riscvP rootProcessor) { riscvP riscv = rootProcessor; riscvP child = getChild(rootProcessor); riscvConfigCP cfg = &riscv->configInfo; + Bool isSMP = child && !riscvIsCluster(riscv); Uns32 numHarts = cfg->numHarts; - Bool isSMP = numHarts && child && !cfg->members; Uns32 extIndex; riscvExtConfigCP extCfg; char string[1024]; @@ -707,9 +708,9 @@ void riscvDoc(riscvP rootProcessor) { snprintf( SNPRINTF_TGT(string), "Parameter VLEN is used to specify the number of bits in a vector " - "register (a power of two in the range 32 to 2048). By default, " + "register (a power of two in the range 32 to %u). By default, " "VLEN is set to %u in this variant.", - riscv->configInfo.VLEN + VLEN_MAX, riscv->configInfo.VLEN ); vmidocAddText(Parameters, string); @@ -717,9 +718,19 @@ void riscvDoc(riscvP rootProcessor) { snprintf( SNPRINTF_TGT(string), "Parameter SLEN is used to specify the striping distance (a power " - "of two in the range 32 to 2048). By default, SLEN is set to %u " + "of two in the range 32 to %u). By default, SLEN is set to %u " "in this variant.", - riscv->configInfo.SLEN + VLEN_MAX, riscv->configInfo.SLEN + ); + vmidocAddText(Parameters, string); + + // document SEW_min + snprintf( + SNPRINTF_TGT(string), + "Parameter SEW_min is used to specify the minimum supported SEW (a " + "power of two in the range 8 to ELEN). By default, SEW_min is set " + "to %u in this variant.", + riscv->configInfo.SEW_min ); vmidocAddText(Parameters, string); @@ -1073,7 +1084,7 @@ void riscvDoc(riscvP rootProcessor) { vmidocAddText( Version, - "Unstable master version as of 8 February 2020 (commit " + "Unstable master version as of "RVVV_MASTER_DATE" (commit " RVVV_MASTER_TAG"), with these changes compared to version 0.8:" ); vmidocAddText( @@ -1083,7 +1094,12 @@ void riscvDoc(riscvP rootProcessor) { vmidocAddText( Version, "- new CSR vcsr has been added and fields VXSAT and VXRM " - "fields relocated there from CSR fcsr." + "relocated there from CSR fcsr;" + ); + vmidocAddText( + Version, + "- segment loads and stores have been restricted to SEW " + "element size only." ); } } @@ -1118,6 +1134,210 @@ void riscvDoc(riscvP rootProcessor) { ); } + //////////////////////////////////////////////////////////////////////////// + // DEBUG MODE + //////////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP debugMode = vmidocAddSection(Root, "Debug Mode"); + + vmidocAddText( + debugMode, + "The model can be configured to implement Debug mode using " + "parameter \"debug_mode\". This implements features described " + "in Chapter 4 of the RISC-V External Debug Support specification " + "(see References). Some aspects of this mode are not defined in " + "the specification because they are implementation-specific; the " + "model provides infrastructure to allow implementation of a " + "Debug Module using a custom harness. Features added are described " + "below." + ); + + //////////////////////////////////////////////////////////////////////// + // DEBUG STATE ENTRY + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP StateEntry = vmidocAddSection( + debugMode, "Debug State Entry" + ); + + vmidocAddText( + StateEntry, + "The specification does not define how Debug mode is " + "implemented. In this model, Debug mode is enabled by a " + "Boolean pseudo-register, \"DM\". When \"DM\" is True, the " + "processor is in Debug mode. When \"DM\" is False, mode is " + "defined by \"mstatus\" in the usual way." + ); + vmidocAddText( + StateEntry, + "Entry to Debug mode can be performed in any of these ways:" + ); + vmidocAddText( + StateEntry, + "1. By writing True to register \"DM\" (e.g. using " + "opProcessorRegWrite) followed by simulation of at least one " + "cycle (e.g. using opProcessorSimulate);" + ); + vmidocAddText( + StateEntry, + "2. By writing a 1 then 0 to net \"haltreq\" (using " + "opNetWrite) followed by simulation of at least one cycle " + "(e.g. using opProcessorSimulate);" + ); + vmidocAddText( + StateEntry, + "3. By writing a 1 to net \"resethaltreq\" (using " + "opNetWrite) while the \"reset\" signal undergoes a negedge " + "transition, followed by simulation of at least one cycle " + "(e.g. using opProcessorSimulate);" + ); + vmidocAddText( + StateEntry, + "4. By executing an \"ebreak\" instruction when Debug mode " + "entry for the current processor mode is enabled by " + "dcsr.ebreakm, dcsr.ebreaks or dcsr.ebreaku." + ); + vmidocAddText( + StateEntry, + "In all cases, the processor will save required state in " + "\"dpc\" and \"dcsr\" and then return control immediately " + "to the harness, with stopReason of OP_SR_INTERRUPT. The " + "harness can then manipulate the processor in Debug state as " + "described below." + ); + } + + //////////////////////////////////////////////////////////////////////// + // DEBUG STATE EXIT + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP StateEntry = vmidocAddSection( + debugMode, "Debug State Exit" + ); + + vmidocAddText( + StateEntry, + "Exit from Debug mode can be performed in any of these ways:" + ); + vmidocAddText( + StateEntry, + "1. By writing False to register \"DM\" (e.g. using " + "opProcessorRegWrite) followed by simulation of at least one " + "cycle (e.g. using opProcessorSimulate);" + ); + vmidocAddText( + StateEntry, + "2. By executing an \"dret\" instruction when Debug mode." + ); + vmidocAddText( + StateEntry, + "In both cases, the processor will perform the steps described " + "in section 4.6 (Resume) of the Debug specification." + ); + } + + //////////////////////////////////////////////////////////////////////// + // DEBUG REGISTERS + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP Registers = vmidocAddSection( + debugMode, "Debug Registers" + ); + + vmidocAddText( + Registers, + "When Debug mode is enabled, registers \"dcsr\", \"dpc\", " + "\"dscratch0\" and \"dscratch1\" are implemented as described " + "in the specification. These may be manipulated by the " + "Debug Module using opProcessorRegRead or opProcessorRegWrite; " + "for example, the Debug Module could write \"dcsr\" to enable " + "\"ebreak\" instruction behavior as described above, or read " + "and write \"dpc\" to emulate stepping over an \"ebreak\" " + "instruction prior to resumption from Debug mode." + ); + } + + //////////////////////////////////////////////////////////////////////// + // DEBUG MODE EXECUTION + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP DebugExecution = vmidocAddSection( + debugMode, "Debug Mode Execution" + ); + + vmidocAddText( + DebugExecution, + "The specification allows execution of code fragments in Debug " + "mode. A Debug Module implementation can cause execution in " + "Debug mode by writing the address of a Program Buffer to the " + "core program counter using opProcessorPCSet, then calling " + "opProcessorSimulate. Control will be returned to the harness " + "in any of these cases:" + ); + vmidocAddText( + DebugExecution, + "1. By execution of an \"ebreak\" instruction; or:" + ); + vmidocAddText( + DebugExecution, + "2. By execution of an instruction that causes an exception." + ); + vmidocAddText( + DebugExecution, + "In both cases, the processor returns control immediately " + "to the harness, with stopReason of OP_SR_INTERRUPT." + ); + } + + //////////////////////////////////////////////////////////////////////// + // DEBUG SINGLE STEP + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP DebugExecution = vmidocAddSection( + debugMode, "Debug Single Step" + ); + + vmidocAddText( + DebugExecution, + "When in Debug mode, the harness can cause a single " + "instruction to be executed on return from that mode by using " + "opProcessorRegWrite to set dcsr.step, writing False to " + "register \"DM\" and then simulation of at least one cycle " + "(e.g. using opProcessorSimulate). After one instruction " + "has been executed, control will be returned to the harness. " + "The processor will remain in single-step mode until dcsr.step " + "is cleared." + ); + } + + //////////////////////////////////////////////////////////////////////// + // DEBUG PORTS + //////////////////////////////////////////////////////////////////////// + + { + vmiDocNodeP Ports = vmidocAddSection( + debugMode, "Debug Ports" + ); + + vmidocAddText( + Ports, + "Port \"haltreq\" is a rising-edge-triggered signal that " + "triggers entry to Debug mode (see above)." + ); + vmidocAddText( + Ports, + "Port \"resethaltreq\" is a level-sensitive signal that " + "triggers entry to Debug mode after reset (see above)." + ); + } + } + //////////////////////////////////////////////////////////////////////////// // DEBUG MASK //////////////////////////////////////////////////////////////////////////// @@ -1356,6 +1576,11 @@ void riscvDoc(riscvP rootProcessor) { vmidocAddText(References, string); } + vmidocAddText( + References, + "RISC-V External Debug Support Version 0.14.0-DRAFT" + ); + // add custom references if required addOptDocList(References, cfg->specificDocs); diff --git a/source/riscvExceptions.c b/source/riscvExceptions.c index 581d2ea..95e4c21 100644 --- a/source/riscvExceptions.c +++ b/source/riscvExceptions.c @@ -210,11 +210,42 @@ static Bool handleFF(riscvP riscv) { return suppress; } +// +// Halt the passed processor +// +static void haltProcessor(riscvP riscv, riscvDisableReason reason) { + + if(!riscv->disable) { + vmirtHalt((vmiProcessorP)riscv); + } + + riscv->disable |= reason; +} + +// +// Restart the passed processor +// +static void restartProcessor(riscvP riscv, riscvDisableReason reason) { + + riscv->disable &= ~reason; + + // restart if no longer disabled (maybe from blocked state not visible in + // disable code) + if(!riscv->disable) { + vmirtRestartNext((vmiProcessorP)riscv); + } +} + //////////////////////////////////////////////////////////////////////////////// // TAKING EXCEPTIONS //////////////////////////////////////////////////////////////////////////////// +// +// Forward reference +// +static void enterDM(riscvP riscv, dmCause cause); + // // Return PC to which to return after taking an exception. For processors with // instruction table extensions, the address should be the original instruction, @@ -386,77 +417,87 @@ void riscvTakeException( riscvException exception, Uns64 tval ) { - Bool isInterrupt = IS_INTERRUPT(exception); - Uns32 ecode = GET_ECODE(exception); - Uns64 EPC = getEPC(riscv); - Uns64 handlerPC = 0; - riscvMode modeY = getCurrentMode(riscv); - riscvMode modeX; - riscvExtCBP extCB; - Uns64 base; - Uns8 mode; + if(inDebugMode(riscv)) { - // adjust baseInstructions based on the exception code to take into account - // whether the previous instruction has retired, unless inhibited - if(!retiredCode(exception) && !RD_CSR_FIELD(riscv, mcountinhibit, IR)) { - riscv->baseInstructions++; - } + // terminate execution of program buffer + vmirtAbortRepeat((vmiProcessorP)riscv); + enterDM(riscv, DMC_NONE); - // latch or clear Access Fault detail depending on exception type - if(accessFaultCode(exception)) { - riscv->AFErrorOut = riscv->AFErrorIn; } else { - riscv->AFErrorOut = riscv_AFault_None; - } - // clear any active exclusive access - clearEA(riscv); + Bool isInt = IS_INTERRUPT(exception); + Uns32 ecode = GET_ECODE(exception); + Uns64 EPC = getEPC(riscv); + Uns64 handlerPC = 0; + riscvMode modeY = getCurrentMode(riscv); + riscvMode modeX; + riscvExtCBP extCB; + Uns64 base; + Uns8 mode; + + // adjust baseInstructions based on the exception code to take into + // account whether the previous instruction has retired, unless + // inhibited by mcountinhibit.IR + if(!retiredCode(exception) && !riscvInhibitInstret(riscv)) { + riscv->baseInstructions++; + } - // get exception target mode (X) - if(isInterrupt) { - modeX = getInterruptModeX(riscv, ecode); - } else { - modeX = getExceptionModeX(riscv, ecode); - } + // latch or clear Access Fault detail depending on exception type + if(accessFaultCode(exception)) { + riscv->AFErrorOut = riscv->AFErrorIn; + } else { + riscv->AFErrorOut = riscv_AFault_None; + } - // update state dependent on target exception level - if(modeX==RISCV_MODE_USER) { + // clear any active exclusive access + clearEA(riscv); - // target user mode - TARGET_MODE_X(riscv, U, u, isInterrupt, ecode, EPC, base, mode, tval); + // get exception target mode (X) + if(isInt) { + modeX = getInterruptModeX(riscv, ecode); + } else { + modeX = getExceptionModeX(riscv, ecode); + } - } else if(modeX==RISCV_MODE_SUPERVISOR) { + // update state dependent on target exception level + if(modeX==RISCV_MODE_USER) { - // target supervisor mode - TARGET_MODE_X(riscv, S, s, isInterrupt, ecode, EPC, base, mode, tval); - WR_CSR_FIELD(riscv, mstatus, SPP, modeY); + // target user mode + TARGET_MODE_X(riscv, U, u, isInt, ecode, EPC, base, mode, tval); - } else { + } else if(modeX==RISCV_MODE_SUPERVISOR) { - // target machine mode - TARGET_MODE_X(riscv, M, m, isInterrupt, ecode, EPC, base, mode, tval); - WR_CSR_FIELD(riscv, mstatus, MPP, modeY); - } + // target supervisor mode + TARGET_MODE_X(riscv, S, s, isInt, ecode, EPC, base, mode, tval); + WR_CSR_FIELD(riscv, mstatus, SPP, modeY); - // handle direct or vectored exception - if((mode == 0) || !isInterrupt) { - handlerPC = base; - } else { - handlerPC = base + (4 * ecode); - } + } else { - // switch to target mode - riscvSetMode(riscv, modeX); + // target machine mode + TARGET_MODE_X(riscv, M, m, isInt, ecode, EPC, base, mode, tval); + WR_CSR_FIELD(riscv, mstatus, MPP, modeY); + } - // indicate the taken exception - riscv->exception = exception; + // handle direct or vectored exception + if((mode == 0) || !isInt) { + handlerPC = base; + } else { + handlerPC = base + (4 * ecode); + } - // set address at which to execute - vmirtSetPCException((vmiProcessorP)riscv, handlerPC); + // switch to target mode + riscvSetMode(riscv, modeX); - // notify derived model of exception entry if required - for(extCB=riscv->extCBs; extCB; extCB=extCB->next) { - notifyTrapDerived(riscv, modeX, extCB->trapNotifier, extCB->clientData); + // indicate the taken exception + riscv->exception = exception; + + // set address at which to execute + vmirtSetPCException((vmiProcessorP)riscv, handlerPC); + + // notify derived model of exception entry if required + for(extCB=riscv->extCBs; extCB; extCB=extCB->next) { + notifyTrapDerived(riscv, modeX, extCB->trapNotifier, extCB->clientData); + } } } @@ -552,13 +593,6 @@ void riscvECALL(riscvP riscv) { riscvTakeException(riscv, exception, 0); } -// -// Take EBREAK exception -// -void riscvEBREAK(riscvP riscv) { - riscvTakeException(riscv, riscv_E_Breakpoint, getPC(riscv)); -} - //////////////////////////////////////////////////////////////////////////////// // EXCEPTION RETURN @@ -573,38 +607,72 @@ static riscvMode getERETMode(riscvP riscv, riscvMode newMode, riscvMode minMode) return riscvHasMode(riscv, newMode) ? newMode : minMode; } +// +// From version 1.12, MRET and SRET clear MPRV when leaving M-mode if new mode +// is less privileged than M-mode +// +static void clearMPRV(riscvP riscv, riscvMode newMode) { + if( + (RISCV_PRIV_VERSION(riscv)>RVPV_20190405) && + (newMode!=RISCV_MODE_MACHINE) + ) { + WR_CSR_FIELD(riscv, mstatus, MPRV, 0); + } +} + +// +// Do common actions when returning from an exception +// +static void doERETCommon( + riscvP riscv, + riscvMode retMode, + riscvMode newMode, + Uns64 epc +) { + // switch to target mode + riscvSetMode(riscv, newMode); + + // jump to return address + setPCxRET(riscv, epc); + + // notify derived model of exception return if required + notifyERETDerived(riscv, retMode); + + // check for pending interrupts + riscvTestInterrupt(riscv); +} + // // Return from M-mode exception // void riscvMRET(riscvP riscv) { - Uns32 MPP = RD_CSR_FIELD(riscv, mstatus, MPP); - riscvMode minMode = riscvGetMinMode(riscv); - riscvMode newMode = getERETMode(riscv, MPP, minMode); + // undefined behavior in Debug mode - NOP in this model + if(!inDebugMode(riscv)) { - // clear any active exclusive access - clearEAxRET(riscv); + Uns32 MPP = RD_CSR_FIELD(riscv, mstatus, MPP); + riscvMode minMode = riscvGetMinMode(riscv); + riscvMode newMode = getERETMode(riscv, MPP, minMode); + riscvMode retMode = RISCV_MODE_MACHINE; - // restore previous MIE - WR_CSR_FIELD(riscv, mstatus, MIE, RD_CSR_FIELD(riscv, mstatus, MPIE)) + // clear any active exclusive access + clearEAxRET(riscv); - // MPIE=1 - WR_CSR_FIELD(riscv, mstatus, MPIE, 1); + // restore previous MIE + WR_CSR_FIELD(riscv, mstatus, MIE, RD_CSR_FIELD(riscv, mstatus, MPIE)) - // MPP= - WR_CSR_FIELD(riscv, mstatus, MPP, minMode); + // MPIE=1 + WR_CSR_FIELD(riscv, mstatus, MPIE, 1); - // switch to target mode - riscvSetMode(riscv, newMode); + // MPP= + WR_CSR_FIELD(riscv, mstatus, MPP, minMode); - // jump to exception address - setPCxRET(riscv, RD_CSR_FIELD(riscv, mepc, value)); - - // notify derived model of exception return if required - notifyERETDerived(riscv, RISCV_MODE_MACHINE); + // clear mstatus.MPRV if required + clearMPRV(riscv, newMode); - // check for pending interrupts - riscvTestInterrupt(riscv); + // do common return actions + doERETCommon(riscv, retMode, newMode, RD_CSR_FIELD(riscv, mepc, value)); + } } // @@ -612,33 +680,32 @@ void riscvMRET(riscvP riscv) { // void riscvSRET(riscvP riscv) { - Uns32 SPP = RD_CSR_FIELD(riscv, mstatus, SPP); - riscvMode minMode = riscvGetMinMode(riscv); - riscvMode newMode = getERETMode(riscv, SPP, minMode); + // undefined behavior in Debug mode - NOP in this model + if(!inDebugMode(riscv)) { - // clear any active exclusive access - clearEAxRET(riscv); + Uns32 SPP = RD_CSR_FIELD(riscv, mstatus, SPP); + riscvMode minMode = riscvGetMinMode(riscv); + riscvMode newMode = getERETMode(riscv, SPP, minMode); + riscvMode retMode = RISCV_MODE_SUPERVISOR; - // restore previous SIE - WR_CSR_FIELD(riscv, mstatus, SIE, RD_CSR_FIELD(riscv, mstatus, SPIE)) + // clear any active exclusive access + clearEAxRET(riscv); - // SPIE=1 - WR_CSR_FIELD(riscv, mstatus, SPIE, 1); + // restore previous SIE + WR_CSR_FIELD(riscv, mstatus, SIE, RD_CSR_FIELD(riscv, mstatus, SPIE)) - // SPP= - WR_CSR_FIELD(riscv, mstatus, SPP, minMode); + // SPIE=1 + WR_CSR_FIELD(riscv, mstatus, SPIE, 1); - // switch to target mode - riscvSetMode(riscv, newMode); - - // jump to exception address - setPCxRET(riscv, RD_CSR_FIELD(riscv, sepc, value)); + // SPP= + WR_CSR_FIELD(riscv, mstatus, SPP, minMode); - // notify derived model of exception return if required - notifyERETDerived(riscv, RISCV_MODE_SUPERVISOR); + // clear mstatus.MPRV if required + clearMPRV(riscv, newMode); - // check for pending interrupts - riscvTestInterrupt(riscv); + // do common return actions + doERETCommon(riscv, retMode, newMode, RD_CSR_FIELD(riscv, sepc, value)); + } } // @@ -646,28 +713,193 @@ void riscvSRET(riscvP riscv) { // void riscvURET(riscvP riscv) { - riscvMode newMode = RISCV_MODE_USER; + // undefined behavior in Debug mode - NOP in this model + if(!inDebugMode(riscv)) { - // clear any active exclusive access - clearEAxRET(riscv); + riscvMode newMode = RISCV_MODE_USER; + riscvMode retMode = RISCV_MODE_USER; - // restore previous UIE - WR_CSR_FIELD(riscv, mstatus, UIE, RD_CSR_FIELD(riscv, mstatus, UPIE)) + // clear any active exclusive access + clearEAxRET(riscv); - // UPIE=1 - WR_CSR_FIELD(riscv, mstatus, UPIE, 1); + // restore previous UIE + WR_CSR_FIELD(riscv, mstatus, UIE, RD_CSR_FIELD(riscv, mstatus, UPIE)) - // switch to target mode - riscvSetMode(riscv, newMode); + // UPIE=1 + WR_CSR_FIELD(riscv, mstatus, UPIE, 1); - // jump to exception address - setPCxRET(riscv, RD_CSR_FIELD(riscv, uepc, value)); + // do common return actions + doERETCommon(riscv, retMode, newMode, RD_CSR_FIELD(riscv, uepc, value)); + } +} - // notify derived model of exception return if required - notifyERETDerived(riscv, RISCV_MODE_USER); - // check for pending interrupts - riscvTestInterrupt(riscv); +//////////////////////////////////////////////////////////////////////////////// +// DEBUG MODE +//////////////////////////////////////////////////////////////////////////////// + +// +// Enter Debug mode +// +static void enterDM(riscvP riscv, dmCause cause) { + + if(!inDebugMode(riscv)) { + + riscvCountState state; + + // get state before possible inhibit update + riscvPreInhibit(riscv, &state); + + // update current state + riscv->DM = True; + + // save current mode + WR_CSR_FIELD(riscv, dcsr, prv, getCurrentMode(riscv)); + + // save cause + WR_CSR_FIELD(riscv, dcsr, cause, cause); + + // save current instruction address + WR_CSR(riscv, dpc, getEPC(riscv)); + + // switch to Machine mode + riscvSetMode(riscv, RISCV_MODE_MACHINE); + + // refresh state after possible inhibit update + riscvPostInhibit(riscv, &state, False); + } + + // interrupt the processor + vmirtInterrupt((vmiProcessorP)riscv); +} + +// +// Leave Debug mode +// +static void leaveDM(riscvP riscv) { + + riscvMode newMode = RD_CSR_FIELD(riscv, dcsr, prv); + riscvMode retMode = RISCV_MODE_MACHINE; + riscvCountState state; + + // get state before possible inhibit update + riscvPreInhibit(riscv, &state); + + // update current state + riscv->DM = False; + + // clear mstatus.MPRV if required + clearMPRV(riscv, newMode); + + // do common return actions + doERETCommon(riscv, retMode, newMode, RD_CSR_FIELD(riscv, dpc, value)); + + // refresh state after possible inhibit update + riscvPostInhibit(riscv, &state, False); +} + +// +// Enter or leave Debug mode +// +void riscvSetDM(riscvP riscv, Bool DM) { + + Bool oldDM = riscv->DM; + + if((oldDM==DM) || riscv->inSaveRestore) { + // no change in state or state restore + } else if(DM) { + enterDM(riscv, DMC_HALTREQ); + } else { + leaveDM(riscv); + } +} + +// +// Instruction step breakpoint callback +// +VMI_ICOUNT_FN(riscvStepExcept) { + + riscvP riscv = (riscvP)processor; + + if(!inDebugMode(riscv) && RD_CSR_FIELD(riscv, dcsr, step)) { + enterDM(riscv, DMC_STEP); + } +} + +// +// Set step breakpoint if required +// +void riscvSetStepBreakpoint(riscvP riscv) { + + if(!inDebugMode(riscv) && RD_CSR_FIELD(riscv, dcsr, step)) { + vmirtSetICountInterrupt((vmiProcessorP)riscv, 1); + } +} + +// +// Return from Debug mode +// +void riscvDRET(riscvP riscv) { + + if(!inDebugMode(riscv)) { + + // report FS state + if(riscv->verbose) { + vmiMessage("W", CPU_PREFIX "_NDM", + SRCREF_FMT "Illegal instruction - not debug mode", + SRCREF_ARGS(riscv, getPC(riscv)) + ); + } + + // take Illegal Instruction exception + riscvIllegalInstruction(riscv); + + } else { + + // leave Debug mode + leaveDM(riscv); + } +} + +// +// Take EBREAK exception +// +void riscvEBREAK(riscvP riscv) { + + riscvMode mode = getCurrentMode(riscv); + Bool useDM = False; + + // determine whether ebreak should cause debug module entry + if(inDebugMode(riscv)) { + useDM = True; + } else if(mode==RISCV_MODE_USER) { + useDM = RD_CSR_FIELD(riscv, dcsr, ebreaku); + } else if(mode==RISCV_MODE_SUPERVISOR) { + useDM = RD_CSR_FIELD(riscv, dcsr, ebreaks); + } else if(mode==RISCV_MODE_MACHINE) { + useDM = RD_CSR_FIELD(riscv, dcsr, ebreakm); + } + + if(useDM) { + + // don't count the ebreak instruction if dcsr.stopcount is set + if(RD_CSR_FIELD(riscv, dcsr, stopcount)) { + if(!riscvInhibitCycle(riscv)) { + riscv->baseCycles++; + } + if(!riscvInhibitInstret(riscv)) { + riscv->baseInstructions++; + } + } + + // handle EBREAK as Debug module action + enterDM(riscv, DMC_EBREAK); + + } else { + + // handle EBREAK as normal exception + riscvTakeException(riscv, riscv_E_Breakpoint, getPC(riscv)); + } } @@ -860,7 +1092,8 @@ inline static Uns64 getPendingInterrupts(riscvP riscv) { // static Uns64 getPendingAndEnabledInterrupts(riscvP riscv) { - Uns64 result = getPendingInterrupts(riscv); + // NOTE: all interrupts are disabled in Debug mode + Uns64 result = inDebugMode(riscv) ? 0 : getPendingInterrupts(riscv); if(result) { @@ -969,11 +1202,24 @@ VMI_IFETCH_FN(riscvIFetchExcept) { Bool fetchOK = False; Uns64 intMask = getPendingAndEnabledInterrupts(riscv); - if(intMask) { + if(riscv->netValue.resethaltreqS) { - // handle pending interrupt - fetchOK = False; + // enter Debug mode out of reset + if(complete) { + riscv->netValue.resethaltreqS = False; + enterDM(riscv, DMC_RESETHALTREQ); + } + } else if(riscv->netValue.haltreq && !inDebugMode(riscv)) { + + // enter Debug mode + if(complete) { + enterDM(riscv, DMC_HALTREQ); + } + + } else if(intMask) { + + // handle pending interrupt if(complete) { doInterrupt(riscv, intMask); } @@ -981,7 +1227,6 @@ VMI_IFETCH_FN(riscvIFetchExcept) { } else if(!validateFetchAddress(riscv, domain, thisPC, complete)) { // fetch exception (handled in validateFetchAddress) - fetchOK = False; } else { @@ -1169,38 +1414,12 @@ inline static Bool negedge(Uns32 old, Uns32 new) { return old && !new; } -// -// Halt the passed processor -// -static void haltProcessor(riscvP riscv, riscvDisableReason reason) { - - if(!riscv->disable) { - vmirtHalt((vmiProcessorP)riscv); - } - - riscv->disable |= reason; -} - -// -// Restart the passed processor -// -static void restartProcessor(riscvP riscv, riscvDisableReason reason) { - - riscv->disable &= ~reason; - - // restart if no longer disabled (maybe from blocked state not visible in - // disable code) - if(!riscv->disable) { - vmirtRestartNext((vmiProcessorP)riscv); - } -} - // // Halt the processor in WFI state if required // void riscvWFI(riscvP riscv) { - if(!getPendingInterrupts(riscv)) { + if(!(inDebugMode(riscv) || getPendingInterrupts(riscv))) { haltProcessor(riscv, RVD_WFI); } } @@ -1276,6 +1495,9 @@ void riscvReset(riscvP riscv) { // restart the processor from any halted state restartProcessor(riscv, RVD_RESTART_RESET); + // exit Debug mode + riscvSetDM(riscv, False); + // switch to Machine mode riscvSetMode(riscv, RISCV_MODE_MACHINE); @@ -1294,6 +1516,9 @@ void riscvReset(riscvP riscv) { // set address at which to execute vmirtSetPCException((vmiProcessorP)riscv, riscv->configInfo.reset_address); + + // enter Debug mode out of reset if required + riscv->netValue.resethaltreqS = riscv->netValue.resethaltreq; } // @@ -1375,18 +1600,43 @@ static VMI_NET_CHANGE_FN(nmiPortCB) { riscvP riscv = ii->hart; Bool oldValue = riscv->netValue.nmi; - if(posedge(oldValue, newValue)) { + // do NMI actions when signal goes low unless in Debug mode + if(!inDebugMode(riscv) && negedge(oldValue, newValue)) { + doNMI(riscv); + } - // halt the processor while signal goes high - haltProcessor(riscv, RVD_NMI); + // mirror value in dcsr.nmip + WR_CSR_FIELD(riscv, dcsr, nmip, newValue); - } else if(negedge(oldValue, newValue)) { + riscv->netValue.nmi = newValue; +} - // do NMI actions when signal goes low - doNMI(riscv); +// +// haltreq signal (edge triggered) +// +static VMI_NET_CHANGE_FN(haltreqPortCB) { + + riscvInterruptInfoP ii = userData; + riscvP riscv = ii->hart; + Bool oldValue = riscv->netValue.haltreq; + + // do halt actions when signal goes high unless in Debug mode + if(!inDebugMode(riscv) && posedge(oldValue, newValue)) { + vmirtDoSynchronousInterrupt((vmiProcessorP)riscv); } - riscv->netValue.nmi = newValue; + riscv->netValue.haltreq = newValue; +} + +// +// resethaltreq signal (sampled at reset) +// +static VMI_NET_CHANGE_FN(resethaltreqPortCB) { + + riscvInterruptInfoP ii = userData; + riscvP riscv = ii->hart; + + riscv->netValue.resethaltreq = newValue; } // @@ -1460,12 +1710,23 @@ void riscvNewNetPorts(riscvP riscv) { // allocate reset port tail = newNetPort( - riscv, tail, "reset", vmi_NP_INPUT, resetPortCB, "Reset", 0 + riscv, + tail, + "reset", + vmi_NP_INPUT, + resetPortCB, + "Reset", 0 ); // allocate nmi port tail = newNetPort( - riscv, tail, "nmi", vmi_NP_INPUT, nmiPortCB, "NMI", 0 + riscv, + tail, + "nmi", + vmi_NP_INPUT, + nmiPortCB, + "NMI", + 0 ); // allocate implemented interrupt ports @@ -1489,6 +1750,32 @@ void riscvNewNetPorts(riscvP riscv) { ); } } + + // add Debug mode ports + if(riscv->configInfo.debug_mode) { + + // allocate haltreq port + tail = newNetPort( + riscv, + tail, + "haltreq", + vmi_NP_INPUT, + haltreqPortCB, + "haltreq (Debug halt request)", + 0 + ); + + // allocate resethaltreq port + tail = newNetPort( + riscv, + tail, + "resethaltreq", + vmi_NP_INPUT, + resethaltreqPortCB, + "resethaltreq (Debug halt request after reset)", + 0 + ); + } } // diff --git a/source/riscvExceptions.h b/source/riscvExceptions.h index 3df73b9..fc94097 100644 --- a/source/riscvExceptions.h +++ b/source/riscvExceptions.h @@ -86,6 +86,21 @@ void riscvSRET(riscvP riscv); // void riscvURET(riscvP riscv); +// +// Return from Debug mode +// +void riscvDRET(riscvP riscv); + +// +// Enter or leave debug mode +// +void riscvSetDM(riscvP riscv, Bool DM); + +// +// Set step breakpoint if required +// +void riscvSetStepBreakpoint(riscvP riscv); + // // Halt the processor in WFI state if required // diff --git a/source/riscvFunctions.h b/source/riscvFunctions.h index 88ec785..787532b 100644 --- a/source/riscvFunctions.h +++ b/source/riscvFunctions.h @@ -64,6 +64,7 @@ VMI_RD_ABORT_EXCEPT_FN(riscvRdAbortExcept); VMI_WR_ABORT_EXCEPT_FN(riscvWrAbortExcept); VMI_IFETCH_FN(riscvIFetchExcept); VMI_ARITH_RESULT_FN(riscvArithResult); +VMI_ICOUNT_FN(riscvStepExcept); // parameter support functions VMI_PROC_PARAM_SPECS_FN(riscvGetPreParamSpec); diff --git a/source/riscvMain.c b/source/riscvMain.c index da6aa65..4c466db 100644 --- a/source/riscvMain.c +++ b/source/riscvMain.c @@ -66,8 +66,9 @@ static void initLeafModelCBs(riscvP riscv) { riscv->cb.getXRegName = riscvGetXRegName; riscv->cb.getFRegName = riscvGetFRegName; riscv->cb.getVRegName = riscvGetVRegName; - riscv->cb.getTMode = riscvGetTMode; riscv->cb.setTMode = riscvSetTMode; + riscv->cb.getTMode = riscvGetTMode; + riscv->cb.getDataEndian = riscvGetDataEndian; // from riscvExceptions.h riscv->cb.illegalInstruction = riscvIllegalInstruction; @@ -80,6 +81,7 @@ static void initLeafModelCBs(riscvP riscv) { riscv->cb.writeRegSize = riscvWriteRegSize; riscv->cb.writeReg = riscvWriteReg; riscv->cb.getFPFlagsMt = riscvGetFPFlagsMT; + riscv->cb.getDataEndianMt = riscvGetCurrentDataEndianMT; riscv->cb.checkLegalRMMt = riscvEmitCheckLegalRM; riscv->cb.morphVOp = riscvMorphVOp; @@ -121,24 +123,17 @@ static riscvConfigCP getConfigVariantArg(riscvP riscv, riscvParamValuesP params) } // -// Return the number of child processors of he give processor +// Return the number of child processors of the given processor // inline static riscvP getParent(riscvP riscv) { return (riscvP)vmirtGetSMPParent((vmiProcessorP)riscv); } // -// Return the number of child processors of he give processor +// Return the number of child processors of the given processor // inline static Uns32 getNumChildren(riscvP riscv) { - return riscv->parent ? 0 : riscv->configInfo.numHarts; -} - -// -// Is the processor a cluster container? -// -inline static Bool isCluster(riscvP riscv) { - return getNumChildren(riscv) && riscv->configInfo.members; + return riscv->configInfo.numHarts; } // @@ -245,7 +240,7 @@ static void applyParamsSMP(riscvP riscv, riscvParamValuesP params) { cfg->Sv_modes = params->Sv_modes | RISCV_VMM_BARE; cfg->local_int_num = params->local_int_num; cfg->lr_sc_grain = powerOfTwo(params->lr_sc_grain, "lr_sc_grain"); - cfg->numHarts = params->numHarts; + cfg->debug_mode = params->debug_mode; cfg->updatePTEA = params->updatePTEA; cfg->updatePTED = params->updatePTED; cfg->unaligned = params->unaligned; @@ -264,10 +259,15 @@ static void applyParamsSMP(riscvP riscv, riscvParamValuesP params) { cfg->ELEN = powerOfTwo(params->ELEN, "ELEN"); cfg->SLEN = powerOfTwo(params->SLEN, "SLEN"); cfg->VLEN = powerOfTwo(params->VLEN, "VLEN"); + cfg->SEW_min = powerOfTwo(params->SEW_min, "SEW_min"); cfg->Zvlsseg = params->Zvlsseg; cfg->Zvamo = params->Zvamo; cfg->Zvediv = params->Zvediv; + // set number of children + Bool isSMPMember = riscv->parent && !riscvIsCluster(riscv->parent); + cfg->numHarts = isSMPMember ? 0 : params->numHarts; + // Zvqmac extension is only available after version RVVV_0_8_20191004 cfg->Zvqmac = params->Zvqmac && (params->vector_version>RVVV_0_8_20191004); @@ -289,6 +289,15 @@ static void applyParamsSMP(riscvP riscv, riscvParamValuesP params) { cfg->SLEN = cfg->VLEN; } + // force SEW_min <= ELEN + if(cfg->SEW_min>cfg->ELEN) { + vmiMessage("W", CPU_PREFIX"_ISEW", + "'SEW_min' (%u) exceeds 'ELEN' (%u) - forcing SEW_min=%u", + cfg->SEW_min, cfg->ELEN, cfg->ELEN + ); + cfg->SEW_min = cfg->ELEN; + } + if(misa_MXL==1) { // modify configuration for 32-bit cores @@ -493,6 +502,9 @@ VMI_DESTRUCTOR_FN(riscvDestructor) { // free exception state riscvExceptFree(riscv); + + // free vector extension data structures + riscvFreeVector(riscv); } diff --git a/source/riscvMode.h b/source/riscvMode.h index 62781b8..e27da87 100644 --- a/source/riscvMode.h +++ b/source/riscvMode.h @@ -25,9 +25,10 @@ typedef enum riscvModeS { RISCV_MODE_USER = 0, RISCV_MODE_SUPERVISOR = 1, - RISCV_MODE_HYPERVISOR = 2, // not currently in use + RISCV_MODE_HYPERVISOR = 2, // not currently in use RISCV_MODE_MACHINE = 3, - RISCV_MODE_LAST + RISCV_MODE_LAST, + RISCV_MODE_DEBUG = 4 // pseudo-mode (uses M-mode privileges) } riscvMode; // @@ -57,12 +58,11 @@ typedef enum riscvDisableReasonE { RVD_ACTIVE = 0x0, // processor running RVD_WFI = 0x1, // processor halted in WFI - RVD_NMI = 0x2, // processor halted in NMI - RVD_RESET = 0x4, // processor halted in reset + RVD_RESET = 0x2, // processor halted in reset // states from which to restart RVD_RESTART_WFI = (RVD_WFI), - RVD_RESTART_NMI = (RVD_WFI|RVD_NMI), + RVD_RESTART_NMI = (RVD_WFI), RVD_RESTART_RESET = (RVD_WFI|RVD_RESET) } riscvDisableReason; diff --git a/source/riscvModelCallbacks.h b/source/riscvModelCallbacks.h index e103b3f..5688725 100644 --- a/source/riscvModelCallbacks.h +++ b/source/riscvModelCallbacks.h @@ -88,6 +88,15 @@ typedef RISCV_GET_REG_NAME_FN((*riscvGetRegNameFn)); #define RISCV_GET_TMODE_FN(_NAME) Bool _NAME(riscvP riscv) typedef RISCV_GET_TMODE_FN((*riscvGetTModeFn)); +// +// Return data endianness in the given processor mode +// +#define RISCV_GET_DATA_ENDIAN_FN(_NAME) memEndian _NAME( \ + riscvP riscv, \ + riscvMode mode \ +) +typedef RISCV_GET_DATA_ENDIAN_FN((*riscvGetDataEndianFn)); + // // Enable or disable transaction mode // @@ -166,6 +175,12 @@ typedef RISCV_WRITE_REG_FN((*riscvWriteRegFn)); #define RISCV_GET_FP_FLAGS_MT_FN(_NAME) vmiReg _NAME(riscvP riscv) typedef RISCV_GET_FP_FLAGS_MT_FN((*riscvGetFPFlagsMtFn)); +// +// Return data endianness in the current processor mode at morph time +// +#define RISCV_GET_DATA_ENDIAN_MT_FN(_NAME) memEndian _NAME(riscvP riscv) +typedef RISCV_GET_DATA_ENDIAN_MT_FN((*riscvGetDataEndianMtFn)); + // // Validate the given rounding mode is legal and emit an Illegal Instruction // exception call if not @@ -296,6 +311,7 @@ typedef struct riscvModelCBS { riscvGetRegNameFn getVRegName; riscvSetTModeFn setTMode; riscvGetTModeFn getTMode; + riscvGetDataEndianFn getDataEndian; // from riscvExceptions.h riscvIllegalInstructionFn illegalInstruction; @@ -308,6 +324,7 @@ typedef struct riscvModelCBS { riscvWriteRegSizeFn writeRegSize; riscvWriteRegFn writeReg; riscvGetFPFlagsMtFn getFPFlagsMt; + riscvGetDataEndianMtFn getDataEndianMt; riscvCheckLegalRMMtFn checkLegalRMMt; riscvMorphVOpFn morphVOp; diff --git a/source/riscvMorph.c b/source/riscvMorph.c index dfb198f..5bf1911 100644 --- a/source/riscvMorph.c +++ b/source/riscvMorph.c @@ -17,6 +17,9 @@ * */ +// Imperas header files +#include "hostapi/impAlloc.h" + // VMI header files #include "vmi/vmiMessage.h" #include "vmi/vmiMt.h" @@ -173,10 +176,17 @@ inline static Uns64 getPC(riscvP riscv) { } // -// Return endian for data accesses +// Return endian for data accesses in the current mode // -inline static memEndian getDataEndian(riscvP riscv) { - return riscv->dendian; +memEndian riscvGetCurrentDataEndianMT(riscvP riscv) { + + // validate endianness blockMask if required + if(riscv->checkEndian) { + vmimtValidateBlockMask(ISA_BE); + } + + // return current data endianness + return riscvGetCurrentDataEndian(riscv); } // @@ -207,13 +217,6 @@ inline static void emitCheckPolymorphic(void) { vmimtPolymorphicBlock(16, RISCV_PM_KEY); } -// -// Are only unit stride load/store instructions supported? -// -inline static Bool unitStrideOnly(riscvP riscv) { - return riscvVFSupport(riscv, RVVF_UNIT_STRIDE_ONLY); -} - // // Should vxsat and vxrm be treated as members of fcsr for dirty state update? // @@ -249,6 +252,13 @@ inline static Bool vectorSignExtVMVXS(riscvP riscv) { return riscvVFSupport(riscv, RVVF_SEXT_VMV_X_S); } +// +// Are segmented load/store instructions restricted to SEW size? +// +inline static Bool vectorSegOnlySEW(riscvP riscv) { + return riscvVFSupport(riscv, RVVF_SEG_ONLY_SEW); +} + //////////////////////////////////////////////////////////////////////////////// // ILLEGAL INSTRUCTION HANDLING (REQUIRING PROCESSOR ONLY) @@ -1044,7 +1054,7 @@ static void emitLoadTModeMBO( memConstraint constraint ) { Bool sExtend = !state->info.unsExt; - memEndian endian = getDataEndian(state->riscv); + memEndian endian = riscvGetCurrentDataEndianMT(state->riscv); vmiCallFn cb = (vmiCallFn)doLoadTMode; // extend address to 64 bits if required @@ -1077,7 +1087,7 @@ static void emitStoreTModeMBO( vmiReg rs, memConstraint constraint ) { - memEndian endian = getDataEndian(state->riscv); + memEndian endian = riscvGetCurrentDataEndianMT(state->riscv); vmiCallFn cb = (vmiCallFn)doStoreTMode; // extend address to 64 bits if required @@ -1117,7 +1127,7 @@ static void emitLoadNormalMBO( memConstraint constraint ) { Bool sExtend = !state->info.unsExt; - memEndian endian = getDataEndian(state->riscv); + memEndian endian = riscvGetCurrentDataEndianMT(state->riscv); vmimtLoadRRO(rdBits, memBits, offset, rd, ra, endian, sExtend, constraint); } @@ -1133,7 +1143,7 @@ static void emitStoreNormalMBO( vmiReg rs, memConstraint constraint ) { - memEndian endian = getDataEndian(state->riscv); + memEndian endian = riscvGetCurrentDataEndianMT(state->riscv); vmimtStoreRRO(memBits, offset, ra, rs, endian, constraint); } @@ -1942,6 +1952,19 @@ static RISCV_MORPH_FN(emitMRET) { emitException(riscvMRET); } +// +// Implement DRET instruction +// +static RISCV_MORPH_FN(emitDRET) { + + riscvP riscv = state->riscv; + + // this instruction must be executed in Machine mode + requireModeMT(riscv, RISCV_MODE_MACHINE); + + emitException(riscvDRET); +} + // // Implement SRET instruction // @@ -3574,6 +3597,14 @@ void riscvConfigureVector(riscvP riscv) { Uns32 byte; Uns32 i; + // allocate vector registers + riscv->v = STYPE_CALLOC_N(Uns32, (vRegBytes/4)*VREG_NUM); + + // allocate LMULx2, LMULx4 and LMULx8 index tables + riscv->offsetsLMULx2 = STYPE_ALLOC_N(riscvStrideOffset, vRegBytes*2); + riscv->offsetsLMULx4 = STYPE_ALLOC_N(riscvStrideOffset, vRegBytes*4); + riscv->offsetsLMULx8 = STYPE_ALLOC_N(riscvStrideOffset, vRegBytes*8); + // iterate over stripes for(stripe=0; stripeconfigInfo.arch & ISA_V) { + STYPE_FREE(riscv->v); + STYPE_FREE(riscv->offsetsLMULx2); + STYPE_FREE(riscv->offsetsLMULx4); + STYPE_FREE(riscv->offsetsLMULx8); + } +} + //////////////////////////////////////////////////////////////////////////////// // VECTOR OPERATION DISPATCH @@ -3853,26 +3898,32 @@ static void getIndexedRegisterInt(vmiReg *r, vmiReg *base, Uns32 bytes) { // Return offset index table register for the current operation // static vmiReg getOffsetIndexTable( - iterDescP id, - vmiReg *offsetBaseP, - Uns32 eBytes + riscvMorphStateP state, + iterDescP id, + vmiReg *offsetBaseP, + Uns32 eBytes ) { - Uns32 eScale = eBytes*8/id->SEW; - riscvVLMULMt VLMUL = id->VLMUL*eScale; - vmiReg offsetIdx = VMI_NOREG; - Uns32 tableBytes = id->vBytesMax*eScale * sizeof(riscvStrideOffset); + riscvP riscv = state->riscv; + Uns32 eScale = eBytes*8/id->SEW; + riscvVLMULMt VLMUL = id->VLMUL*eScale; + vmiReg offsetIdx = VMI_NOREG; + riscvStrideOffset *base = 0; + Uns32 tableBytes = id->vBytesMax*eScale * sizeof(*base); // get table for the given VLMUL if(VLMUL==VLMULMT_2) { - offsetIdx = RISCV_OFFSETS_LMULx2; + base = riscv->offsetsLMULx2; } else if(VLMUL==VLMULMT_4) { - offsetIdx = RISCV_OFFSETS_LMULx4; + base = riscv->offsetsLMULx4; } else if(VLMUL==VLMULMT_8) { - offsetIdx = RISCV_OFFSETS_LMULx8; + base = riscv->offsetsLMULx8; } else { VMI_ABORT("Unexpected VLMUL %u", VLMUL); // LCOV_EXCL_LINE } + // get vmiReg for the given VLMUL base + offsetIdx = vmimtGetExtReg((vmiProcessorP)riscv, base); + // convert to indexed register getIndexedRegisterInt(&offsetIdx, offsetBaseP, tableBytes); @@ -3902,7 +3953,8 @@ static void initializeBase( vmiReg offsetBase = base->reg; Uns32 scale = elemBytes*lutEBytes; Uns32 eScale = elemBytes*8/id->SEW; - Uns32 lutBytes = VBYTES_MAX * lutEBytes * id->VLMUL * eScale; + Uns32 vRegBytes = state->riscv->configInfo.VLEN/8; + Uns32 lutBytes = vRegBytes * lutEBytes * id->VLMUL * eScale; // handle table offset scale > 8 if(scale>8) { @@ -3913,7 +3965,9 @@ static void initializeBase( } // adjust table base using scaled index - vmiReg offsetIdx = getOffsetIndexTable(id, &offsetBase, elemBytes); + vmiReg offsetIdx = getOffsetIndexTable( + state, id, &offsetBase, elemBytes + ); vmimtAddBaseR(offsetBase, index, scale, lutBytes, False, False); // get offset from table @@ -5770,7 +5824,11 @@ riscvSEWMt riscvValidSEW(riscvP riscv, Uns8 vsew) { Uns32 SEW = vsewToSEW(vsew); - return (SEW<=riscv->configInfo.ELEN) ? SEW : SEWMT_UNKNOWN; + if((SEWconfigInfo.SEW_min) || (SEW>riscv->configInfo.ELEN)) { + SEW = SEWMT_UNKNOWN; + } + + return SEW; } // @@ -6091,6 +6149,13 @@ static RISCV_MORPH_FN(emitVSetVLRRC) { // VECTOR MEMORY ACCESS INSTRUCTIONS //////////////////////////////////////////////////////////////////////////////// +// +// Is this an SEW width load/store? +// +inline static Bool isMemBitsSEW(Uns32 memBits) { + return memBits==-1; +} + // // Return the memory element size in bits // @@ -6098,7 +6163,7 @@ static Uns32 getVMemBits(riscvMorphStateP state, iterDescP id) { Uns32 memBits = state->info.memBits; - if(memBits==-1) { + if(isMemBitsSEW(memBits)) { memBits = id->SEW; } @@ -6119,6 +6184,56 @@ static Bool legalVMVRRegNum(Uns32 regNum) { return ((regNum==1) || (regNum==2) || (regNum==4) || (regNum==8)); } +// +// Emit checks specific to segment loads/stores +// +static Bool emitVLdStCheckSeg(riscvMorphStateP state, iterDescP id) { + + riscvP riscv = state->riscv; + Bool ok = True; + + if(state->info.isWhole) { + + // no action for whole register loads and stores + + } else if(!riscv->configInfo.Zvlsseg) { + + // Zvlsseg extension not configured + ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVLSEG", "Zvlsseg extension not configured"); + ok = False; + + } else if(!isMemBitsSEW(state->info.memBits) && vectorSegOnlySEW(riscv)) { + + // only SEW-byte element segment loads/stores supported + ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVSEGMB", "Illegal non-SEW segment load/store"); + ok = False; + } + + return ok; +} + +// +// Emit checks specific to whole register loads/stores +// +static Bool emitVLdStCheckWhole(riscvMorphStateP state, iterDescP id) { + + riscvP riscv = state->riscv; + Bool ok = True; + + if(!state->info.isWhole) { + + // no action unless whole register load or store + + } else if(state->info.nf && vectorRestrictWhole(riscv)) { + + // nf!=1 is not supported + ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVNF", "Illegal register count (must be 1)"); + ok = False; + } + + return ok; +} + // // Operation-specific argument checks for loads and stores // @@ -6136,12 +6251,16 @@ static RISCV_CHECKV_FN(emitVLdStCheckCB) { } else if(!state->info.nf) { - // not Zvlsseg extension instruction + // no action if only one field - } else if(!(riscv->configInfo.Zvlsseg || state->info.isWhole)) { + } else if(!emitVLdStCheckSeg(state, id)) { - // VLMUL must be 1 for load/store segment instructions - ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVLSEG", "Zvlsseg extension not configured"); + // segment loads/store check failed + ok = False; + + } else if(!emitVLdStCheckWhole(state, id)) { + + // whole register loads/store check failed ok = False; } else if(getVRegNum(state, id)>8) { @@ -6155,34 +6274,6 @@ static RISCV_CHECKV_FN(emitVLdStCheckCB) { // register indices must not wrap ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVWRAP", "Illegal vector index wrap-around"); ok = False; - - } else if(!state->info.isWhole) { - - // not whole-register operation - - } else if(!vectorRestrictWhole(riscv)) { - - // registers not constrained - - } else if(state->info.nf) { - - // nf!=1 is not currently supported in base specification - ILLEGAL_INSTRUCTION_MESSAGE(riscv, "IVNF", "Illegal register count (must be 1)"); - ok = False; - } - - return ok; -} - -// -// Disable non-unit-stride load/store instructions if required -// -static Bool nonUnitStrideVLdStOk(riscvP riscv) { - - Bool ok = !unitStrideOnly(riscv); - - if(!ok) { - ILLEGAL_INSTRUCTION_MESSAGE(riscv, "UVI", "Unimplemented instruction"); } return ok; @@ -6199,14 +6290,14 @@ static RISCV_CHECKV_FN(emitVLdStCheckUCB) { // Operation-specific argument checks for strided loads and stores // static RISCV_CHECKV_FN(emitVLdStCheckSCB) { - return nonUnitStrideVLdStOk(state->riscv) && emitVLdStCheckCB(state, id); + return emitVLdStCheckCB(state, id); } // // Operation-specific argument checks for indexed loads and stores // static RISCV_CHECKV_FN(emitVLdStCheckXCB) { - return nonUnitStrideVLdStOk(state->riscv) && emitVLdStCheckCB(state, id); + return emitVLdStCheckCB(state, id); } // @@ -8019,13 +8110,14 @@ static void emitVRSLIDEDOWNInt( vmiReg index, Uns32 xBits ) { - vmiLabelP skip = vmimtNewLabel(); + Uns32 vlMax = getVLMAXOp(id); + vmiLabelP skip = vmimtNewLabel(); // assume zero result is required vmimtMoveRC(id->SEW, id->r[0], 0); - // skip move from vector if index>=vl - vmimtCompareRRJumpLabel(xBits, vmi_COND_NB, index, CSR_REG_MT(vl), skip); + // skip move from vector if index>=vlmax + vmimtCompareRCJumpLabel(xBits, vmi_COND_NB, index, vlMax, skip); // do indexed move (if not skipped) moveIndexedVd0Vs1(state, id, index, skip); @@ -8036,6 +8128,7 @@ static void emitVRSLIDEDOWNInt( // static RISCV_MORPHV_FN(emitVRSLIDEDOWNCB) { + Uns32 vlMax = getVLMAXOp(id); Uns32 offsetBits = IMPERAS_POINTER_BITS; vmiReg offset = id->r[2]; vmiReg vstart = CSR_REG_MT(vstart); @@ -8043,9 +8136,9 @@ static RISCV_MORPHV_FN(emitVRSLIDEDOWNCB) { Uns32 sBits = (xBitsnumHarts; + + setUns32ParamDefault(param, numHarts==RV_NUMHARTS_0 ? 0 : numHarts); +} + // // Set default value of Sv_modes // @@ -422,7 +437,7 @@ static RISCV_CSR_PMDEFAULT_CFG_FN(stvec) static RISCV_CSR_PMDEFAULT_CFG_FN(utvec) // -// Set default values of ELEN, SLEN and VLEN (vector extensions) +// Set default values of ELEN, SLEN, VLEN and SEW_min (Vector Extension) // static RISCV_PDEFAULT_FN(default_ELEN) { setUns32ParamDefault(param, cfg->ELEN ? cfg->ELEN : ELEN_DEFAULT); @@ -433,6 +448,9 @@ static RISCV_PDEFAULT_FN(default_SLEN) { static RISCV_PDEFAULT_FN(default_VLEN) { setUns32ParamDefault(param, cfg->VLEN ? cfg->VLEN : VLEN_DEFAULT); } +static RISCV_PDEFAULT_FN(default_SEW_min) { + setUns32ParamDefault(param, cfg->SEW_min ? cfg->SEW_min : SEW_MIN); +} // // Set default values of Zvlsseg, Zvamo and Zvediv (vector extensions) @@ -456,6 +474,7 @@ static riscvParameter parameters[] = { { RVPV_FP, default_mstatus_fs_mode, VMI_ENUM_PARAM_SPEC (riscvParamValues, mstatus_fs_mode, FSModes, "Specify conditions causing update of mstatus.FS to dirty")}, { RVPV_ALL, 0, VMI_BOOL_PARAM_SPEC (riscvParamValues, verbose, False, "Specify verbose output messages")}, { RVPV_MPCORE, default_numHarts, VMI_UNS32_PARAM_SPEC (riscvParamValues, numHarts, 0, 0, 32, "Specify the number of hart contexts in a multiprocessor")}, + { RVPV_ALL, default_debug_mode, VMI_BOOL_PARAM_SPEC (riscvParamValues, debug_mode, False, "Specify whether Debug mode is implemented")}, { RVPV_S, default_updatePTEA, VMI_BOOL_PARAM_SPEC (riscvParamValues, updatePTEA, False, "Specify whether hardware update of PTE A bit is supported")}, { RVPV_S, default_updatePTED, VMI_BOOL_PARAM_SPEC (riscvParamValues, updatePTED, False, "Specify whether hardware update of PTE D bit is supported")}, { RVPV_ALL, default_unaligned, VMI_BOOL_PARAM_SPEC (riscvParamValues, unaligned, False, "Specify whether the processor supports unaligned memory accesses")}, @@ -496,12 +515,13 @@ static riscvParameter parameters[] = { { RVPV_ALL, default_mvendorid, VMI_UNS64_PARAM_SPEC (riscvParamValues, mvendorid, 0, 0, -1, "Override mvendorid register")}, { RVPV_ALL, default_marchid, VMI_UNS64_PARAM_SPEC (riscvParamValues, marchid, 0, 0, -1, "Override marchid register")}, { RVPV_ALL, default_mimpid, VMI_UNS64_PARAM_SPEC (riscvParamValues, mimpid, 0, 0, -1, "Override mimpid register")}, - { RVPV_ALL, default_mhartid, VMI_UNS64_PARAM_SPEC (riscvParamValues, mhartid, 0, 0, -1, "Override mhartid register")}, + { RVPV_ALL, default_mhartid, VMI_UNS64_PARAM_SPEC (riscvParamValues, mhartid, 0, 0, -1, "Override mhartid register (or first mhartid of an incrementing sequence if this is an SMP variant)")}, { RVPV_ALL, default_mtvec, VMI_UNS64_PARAM_SPEC (riscvParamValues, mtvec, 0, 0, -1, "Override mtvec register")}, { RVPV_FP, 0, VMI_UNS32_PARAM_SPEC (riscvParamValues, mstatus_FS, 0, 0, 3, "Override default value of mstatus.FS (initial state of floating point unit)")}, { RVPV_V, default_ELEN, VMI_UNS32_PARAM_SPEC (riscvParamValues, ELEN, 0, ELEN_MIN, ELEN_MAX, "Override ELEN (vector extension)")}, { RVPV_V, default_SLEN, VMI_UNS32_PARAM_SPEC (riscvParamValues, SLEN, 0, SLEN_MIN, VLEN_MAX, "Override SLEN (vector extension)")}, { RVPV_V, default_VLEN, VMI_UNS32_PARAM_SPEC (riscvParamValues, VLEN, 0, SLEN_MIN, VLEN_MAX, "Override VLEN (vector extension)")}, + { RVPV_V, default_SEW_min, VMI_UNS32_PARAM_SPEC (riscvParamValues, SEW_min, 0, SEW_MIN, ELEN_MAX, "Override minimum supported SEW (vector extension)")}, { RVPV_V, default_Zvlsseg, VMI_BOOL_PARAM_SPEC (riscvParamValues, Zvlsseg, False, "Specify that Zvlsseg is implemented (vector extension)")}, { RVPV_V, default_Zvamo, VMI_BOOL_PARAM_SPEC (riscvParamValues, Zvamo, False, "Specify that Zvamo is implemented (vector extension)")}, { RVPV_V, default_Zvediv, VMI_BOOL_PARAM_SPEC (riscvParamValues, Zvediv, False, "Specify that Zvediv is implemented (vector extension)")}, @@ -515,7 +535,17 @@ static riscvParameter parameters[] = { // Return any parent of the passed processor // inline static riscvP getParent(riscvP riscv) { - return (riscvP)vmirtGetSMPParent((vmiProcessorP)riscv); + return riscv ? (riscvP)vmirtGetSMPParent((vmiProcessorP)riscv) : 0; +} + +// +// Is the processor a member of an SMP? +// +static Bool isSMPMember(riscvP riscv) { + + riscvP parent = getParent(riscv); + + return (parent && !riscvIsCluster(parent)); } // @@ -538,17 +568,28 @@ static Bool selectPreParameter(riscvParameterP param) { // // Should this parameter be presented as a public one for the selected variant? // -static Bool selectParameter(riscvConfigCP cfg, riscvParameterP param) { - +static Bool selectParameter( + riscvP riscv, + riscvConfigCP cfg, + riscvParameterP param +) { if(cfg) { Bool isCluster = cfg->members; + Bool isVariant = param->variant & RVPV_VARIANT; - // cluster exposes only variant parameter - if(!(param->variant & RVPV_VARIANT) && isCluster) { + // cluster exposes only variant parameter; SMP member has no variant + if(!isVariant && isCluster) { + return False; + } else if(isVariant && isSMPMember(riscv)) { return False; } + // include parameters that are only required for multicore variants + if(param->variant & RVPV_MPCORE) { + return cfg->numHarts; + } + // include parameters that are only required when floating-point is // present if((param->variant & RVPV_FP) && !(cfg->arch&ISA_DF)) { @@ -578,11 +619,6 @@ static Bool selectParameter(riscvConfigCP cfg, riscvParameterP param) { if((param->variant & RVPV_V) && !(cfg->arch&ISA_V)) { return False; } - - // include parameters that are only required for multicore variants - if((param->variant & RVPV_MPCORE) && !cfg->numHarts) { - return False; - } } return True; @@ -625,13 +661,16 @@ static Uns32 countPreParameters(riscvParameterP param) { // // Count the number of visible parameters // -static Uns32 countParameters(riscvConfigCP cfg, riscvParameterP param) { - +static Uns32 countParameters( + riscvP riscv, + riscvConfigCP cfg, + riscvParameterP param +) { Uns32 i = 0; while(param->parameter.name) { - if(selectParameter(cfg, param)) { + if(selectParameter(riscv, cfg, param)) { i++; } @@ -748,14 +787,14 @@ static vmiParameterP createParameterList( Uns32 i; // count the number of entries in the parameter list - Uns32 entries = countParameters(cfg, src); + Uns32 entries = countParameters(riscv, cfg, src); // allocate the parameter list, including NULL terminator result = STYPE_CALLOC_N(vmiParameter, entries+1); for(i=0, dst=result; src->parameter.name; i++, src++) { - if(selectParameter(cfg, src)) { + if(selectParameter(riscv, cfg, src)) { *dst = src->parameter; @@ -805,10 +844,9 @@ static const char *refineVariant(riscvP riscv, const char *variant) { // VMI_PROC_PARAM_SPECS_FN(riscvGetPreParamSpec) { - riscvP riscv = (riscvP)processor; - riscvP parent = riscv ? getParent(riscv) : 0; + riscvP riscv = (riscvP)processor; - if(parent && !riscvIsCluster(parent)) { + if(isSMPMember(riscv)) { // allow parameterization of multiclusters and root level objects only return 0; @@ -817,6 +855,16 @@ VMI_PROC_PARAM_SPECS_FN(riscvGetPreParamSpec) { riscvConfigCP cfgList = riscvGetConfigList(riscv); + // if this is a cluster member, use the member configuration to fill + // parameters + if(riscvIsClusterMember(riscv)) { + + riscvP parent = getParent(riscv); + const char *variant = riscvGetClusterVariant(parent, riscv); + + cfgList = getSelectedConfig(cfgList, variant); + } + // fill variants and create pre-parameter list riscv->variantList = createVariantList(riscv); riscv->parameters = createPreParameterList(riscv, cfgList); @@ -837,17 +885,18 @@ VMI_PROC_PARAM_SPECS_FN(riscvGetPreParamSpec) { // VMI_SET_PARAM_VALUES_FN(riscvGetPreParamValues) { - riscvP riscv = (riscvP)processor; - riscvP parent = riscv ? getParent(riscv) : 0; + riscvP riscv = (riscvP)processor; + riscvConfigCP cfgList = riscvGetConfigList(riscv); - if(parent && !riscvIsCluster(parent)) { + if(isSMPMember(riscv)) { - // no action + // no action - all parameters for SMP processors are specified at the + // SMP level, and the hart-specific mhartid parameter specifies the + // *first* index of an incrementing sequence in this case } else { // get raw variant - riscvConfigCP cfgList = riscvGetConfigList(riscv); riscvParamValuesP params = (riscvParamValuesP)parameterValues; riscvConfigCP match = cfgList + params->variant; @@ -861,7 +910,7 @@ VMI_SET_PARAM_VALUES_FN(riscvGetPreParamValues) { // apply misa_Extensions override if required if(SETBIT(params->misa_Extensions)) { riscvArchitecture keep = riscv->configInfo.arch & (-1 << XLEN_SHIFT); - riscv->configInfo.arch = params->misa_Extensions | keep; + riscv->configInfo.arch = params->misa_Extensions | keep; } // apply add_Extensions override if required @@ -888,19 +937,7 @@ VMI_SET_PARAM_VALUES_FN(riscvGetPreParamValues) { // VMI_PROC_PARAM_TABLE_SIZE_FN(riscvParamValueSize) { - riscvP riscv = (riscvP)processor; - riscvP parent = riscv ? getParent(riscv) : 0; - - if(parent && !riscvIsCluster(parent)) { - - // allow parameterization of multiclusters and root level objects only - return 0; - - } else { - - // return structure size - return sizeof(riscvParamValues); - } + return sizeof(riscvParamValues); } // diff --git a/source/riscvParameters.h b/source/riscvParameters.h index 763276b..1854980 100644 --- a/source/riscvParameters.h +++ b/source/riscvParameters.h @@ -40,6 +40,7 @@ typedef struct riscvParamValuesS { VMI_ENUM_PARAM(fp16_version); VMI_ENUM_PARAM(mstatus_fs_mode); VMI_BOOL_PARAM(verbose); + VMI_BOOL_PARAM(debug_mode); VMI_BOOL_PARAM(updatePTEA); VMI_BOOL_PARAM(updatePTED); VMI_BOOL_PARAM(unaligned); @@ -87,6 +88,7 @@ typedef struct riscvParamValuesS { VMI_UNS32_PARAM(ELEN); VMI_UNS32_PARAM(SLEN); VMI_UNS32_PARAM(VLEN); + VMI_UNS32_PARAM(SEW_min); VMI_BOOL_PARAM(Zvlsseg); VMI_BOOL_PARAM(Zvamo); VMI_BOOL_PARAM(Zvediv); diff --git a/source/riscvRegisters.h b/source/riscvRegisters.h index 620ae19..5f31729 100644 --- a/source/riscvRegisters.h +++ b/source/riscvRegisters.h @@ -46,6 +46,7 @@ #define RISCV_SF_TMP RISCV_CPU_TEMP(SF) #define RISCV_LR RISCV_CPU_REG(x[RV_REG_X_RA]) #define RISCV_EA_TAG RISCV_CPU_REG(exclusiveTag) +#define RISCV_DM RISCV_CPU_REG(DM) #define RISCV_FP_FLAGS RISCV_CPU_REG(fpFlagsMT) #define RISCV_SF_FLAGS RISCV_CPU_REG(SFMT) #define RISCV_JUMP_BASE RISCV_CPU_REG(jumpBase) @@ -59,3 +60,4 @@ #define RISCV_OFFSETS_LMULx2 RISCV_CPU_REG(offsetsLMULx2) #define RISCV_OFFSETS_LMULx4 RISCV_CPU_REG(offsetsLMULx4) #define RISCV_OFFSETS_LMULx8 RISCV_CPU_REG(offsetsLMULx8) + diff --git a/source/riscvSemiHost.c b/source/riscvSemiHost.c index 6c715ce..7fc3ca4 100644 --- a/source/riscvSemiHost.c +++ b/source/riscvSemiHost.c @@ -24,6 +24,7 @@ // model header files #include "riscvFunctions.h" +#include "riscvMorph.h" #include "riscvRegisters.h" #include "riscvStructure.h" @@ -57,8 +58,8 @@ static Uns32 push4ByteArg(vmiProcessorP processor, Uns32 paramNum) { } else { - riscvP riscv = (riscvP)processor; - memEndian endian = riscv->dendian; + riscvP riscv = (riscvP)processor; + memEndian endian = riscvGetCurrentDataEndianMT(riscv); // argument on the stack - fetch into a temporary vmimtLoadRRO( @@ -97,8 +98,8 @@ static Uns32 push8ByteArg(vmiProcessorP processor, Uns32 paramNum) { } else { - riscvP riscv = (riscvP)processor; - memEndian endian = riscv->dendian; + riscvP riscv = (riscvP)processor; + memEndian endian = riscvGetCurrentDataEndianMT(riscv); // argument on the stack - fetch into a temporary vmimtLoadRRO( @@ -131,8 +132,8 @@ static Uns32 pushAddressArg(vmiProcessorP processor, Uns32 paramNum) { } else { - riscvP riscv = (riscvP)processor; - memEndian endian = riscv->dendian; + riscvP riscv = (riscvP)processor; + memEndian endian = riscvGetCurrentDataEndianMT(riscv); // argument on the stack - fetch into a temporary vmimtLoadRRO( diff --git a/source/riscvStructure.h b/source/riscvStructure.h index cf22930..96317c0 100644 --- a/source/riscvStructure.h +++ b/source/riscvStructure.h @@ -67,10 +67,13 @@ // Container for net values // typedef struct riscvNetValueS { - Uns64 ip; // bitmask of driven interrupt signals - Bool reset; // level of reset signal - Bool nmi; // level of NMI signal - Bool _u1[6]; // (for alignment) + Uns64 ip; // bitmask of driven interrupt signals + Bool reset; // level of reset signal + Bool nmi; // level of NMI signal + Bool haltreq; // haltreq (Debug mode) + Bool resethaltreq; // resethaltreq (Debug mode) + Bool resethaltreqS; // resethaltreq (Debug mode, sampled at reset) + Bool _u1[3]; // (for alignment) } riscvNetValue; // @@ -124,9 +127,7 @@ typedef struct riscvNetPortS { // Maximum supported value of VLEN and number of vector registers (vector // extension) // -#define VLEN_MAX 2048 -#define VBYTES_MAX (VLEN_MAX/8) -#define VDWORDS_MAX (VLEN_MAX/64) +#define VLEN_MAX 65536 #define VREG_NUM 32 #define ELEN_MIN 32 #define ELEN_MAX 64 @@ -138,13 +139,6 @@ typedef struct riscvNetPortS { #define LMUL_MAX 8 #define NUM_BASE_REGS 4 -// -// This defines sufficient 64-bit aligned space for VREG_NUM vector registers -// of up to VLEN_MAX bits. The assignment of the storage depends on the -// configured VLEN -// -typedef Uns64 riscvVRegBank[VDWORDS_MAX*VREG_NUM]; - // // This defines the type of elements of the stride tables used to handle // striping @@ -175,15 +169,18 @@ typedef struct riscvS { Bool inSaveRestore :1;// is save/restore active? Bool useTMode :1;// has transaction mode been enabled? Bool rmCheckValid :1;// whether RM valid check required + Bool checkEndian :1;// whether endian check required Uns16 pmKey; // polymorphic key Uns8 fpFlagsMT; // flags set by JIT instructions Uns8 fpFlagsCSR; // flags set by CSR write Uns8 SFMT; // SF set by JIT instructions Uns8 SFCSR; // SF set by CSR write Uns8 SF; // operation saturation flag + Bool DM; // whether in Debug mode Uns32 flags; // model control flags Uns32 flagsRestore; // saved flags during restore riscvConfig configInfo; // model configuration + riscvMode dmode; // mode in which to access data memEndian dendian; // data endianness memEndian iendian; // instruction endianness Uns64 jumpBase; // address of jump instruction @@ -248,19 +245,19 @@ typedef struct riscvS { riscvBlockStateP blockState; // active block state // Enhanced model support callbacks - riscvModelCB cb; // implemented by base model - riscvExtCBP extCBs; // implemented in extension + riscvModelCB cb; // implemented by base model + riscvExtCBP extCBs; // implemented in extension // Vector extension - Uns8 vFieldMask; // vector field mask - Uns8 vActiveMask; // vector active element mask - Bool vFirstFault; // vector first fault active? - Uns64 vTmp; // vector operation temporary - riscvVRegBank v; // vector registers (configurable size) - UnsPS vBase[NUM_BASE_REGS]; // indexed base registers - riscvStrideOffset offsetsLMULx2[VBYTES_MAX*2]; // LMULx2 stride offsets - riscvStrideOffset offsetsLMULx4[VBYTES_MAX*4]; // LMULx4 stride offsets - riscvStrideOffset offsetsLMULx8[VBYTES_MAX*8]; // LMULx8 stride offsets + Uns8 vFieldMask; // vector field mask + Uns8 vActiveMask; // vector active element mask + Bool vFirstFault; // vector first fault active? + Uns64 vTmp; // vector operation temporary + UnsPS vBase[NUM_BASE_REGS]; // indexed base registers + Uns32 *v; // vector registers (configurable size) + riscvStrideOffset *offsetsLMULx2; // LMULx2 stride offsets + riscvStrideOffset *offsetsLMULx4; // LMULx4 stride offsets + riscvStrideOffset *offsetsLMULx8; // LMULx8 stride offsets } riscv; @@ -283,4 +280,11 @@ inline static Uns32 getASIDMask(riscvP riscv) { return (1<configInfo.ASID_bits)-1; } +// +// Is the processor in Debug mode? +// +inline static Bool inDebugMode(riscvP riscv) { + return riscv->DM; +} + diff --git a/source/riscvUtils.c b/source/riscvUtils.c index f3aa306..8f4f07d 100644 --- a/source/riscvUtils.c +++ b/source/riscvUtils.c @@ -26,6 +26,7 @@ // model header files #include "riscvBlockState.h" #include "riscvDecode.h" +#include "riscvExceptions.h" #include "riscvFunctions.h" #include "riscvMessage.h" #include "riscvMode.h" @@ -81,6 +82,11 @@ void riscvSetCurrentArch(riscvP riscv) { } } + // handle big endian access if required + if(riscv->checkEndian && riscvGetCurrentDataEndian(riscv)) { + arch |= ISA_BE; + } + if(riscv->currentArch != arch) { vmiProcessorP processor = (vmiProcessorP)riscv; @@ -201,6 +207,42 @@ Uns32 riscvGetXlenMode(riscvP riscv) { return result; } +// +// Does the processor support configurable endianness? +// +Bool riscvSupportEndian(riscvP riscv) { + return (RISCV_PRIV_VERSION(riscv)>RVPV_20190405); +} + +// +// Return endianness for data access in the given mode +// +memEndian riscvGetDataEndian(riscvP riscv, riscvMode mode) { + + memEndian result = riscv->dendian; + + if(!riscvSupportEndian(riscv)) { + // no action + } else if(mode==RISCV_MODE_USER) { + result = RD_CSR_FIELD(riscv, mstatus, UBE); + } else if(mode==RISCV_MODE_SUPERVISOR) { + result = RD_CSR_FIELD_ALT(riscv, mstatush, mstatus, SBE); + } else if(mode==RISCV_MODE_MACHINE) { + result = RD_CSR_FIELD_ALT(riscv, mstatush, mstatus, MBE); + } else { + VMI_ABORT("invalid mode"); // LCOV_EXCL_LINE + } + + return result; +} + +// +// Return endianness for data access in the current mode +// +memEndian riscvGetCurrentDataEndian(riscvP riscv) { + return riscvGetDataEndian(riscv, riscv->dmode); +} + // // Return endianness of access // @@ -211,7 +253,7 @@ VMI_ENDIAN_FN(riscvGetEndian) { if(isFetch) { return riscv->iendian; } else { - return riscv->dendian; + return riscvGetCurrentDataEndian(riscv); } } @@ -265,6 +307,12 @@ static const vmiModeInfo modes[] = { .description = "Machine mode" }, + [RISCV_MODE_DEBUG] = { + .name = "Debug", + .code = RISCV_MODE_DEBUG, + .description = "Debug mode" + }, + // terminator {0} }; @@ -283,7 +331,7 @@ VMI_GET_MODE_FN(riscvGetMode) { riscvP riscv = (riscvP)processor; - return &modes[getCurrentMode(riscv)]; + return &modes[inDebugMode(riscv) ? RISCV_MODE_DEBUG : getCurrentMode(riscv)]; } // @@ -309,6 +357,9 @@ void riscvSetMode(riscvP riscv, riscvMode mode) { // refresh current data domain (may be modified by mstatus.MPRV, and may // have changed while taking an exception even if mode has not changed) riscvVMRefreshMPRVDomain(riscv); + + // set step breakpoint if required + riscvSetStepBreakpoint(riscv); } // @@ -337,6 +388,8 @@ Bool riscvHasMode(riscvP riscv, riscvMode mode) { return riscv->configInfo.arch & ISA_S; case RISCV_MODE_MACHINE: return True; + case RISCV_MODE_DEBUG: + return riscv->configInfo.debug_mode; default: return False; } @@ -510,7 +563,7 @@ const char *riscvGetVRegName(Uns32 index) { // vmiReg riscvGetVReg(riscvP riscv, Uns32 index) { - void *value = &riscv->v[index*riscv->configInfo.VLEN/64]; + void *value = &riscv->v[index*riscv->configInfo.VLEN/32]; return vmimtGetExtReg((vmiProcessorP)riscv, value); } diff --git a/source/riscvUtils.h b/source/riscvUtils.h index 64694ef..58f5687 100644 --- a/source/riscvUtils.h +++ b/source/riscvUtils.h @@ -52,6 +52,21 @@ Uns32 riscvGetFlenArch(riscvP riscv); // Uns32 riscvGetXlenMode(riscvP riscv); +// +// Does the processor support configurable endianness? +// +Bool riscvSupportEndian(riscvP riscv); + +// +// Return endianness for data access in the given mode +// +memEndian riscvGetDataEndian(riscvP riscv, riscvMode mode); + +// +// Return endianness for data access in the current mode +// +memEndian riscvGetCurrentDataEndian(riscvP riscv); + // // Register extension callback block with the base model // diff --git a/source/riscvVM.c b/source/riscvVM.c index f1977c3..98f1f00 100644 --- a/source/riscvVM.c +++ b/source/riscvVM.c @@ -261,8 +261,16 @@ inline static Bool getSUM(riscvP riscv) { // // Get effective value of MSTATUS.MPRV // -inline static Bool getMPRV(riscvP riscv) { - return RD_CSR_FIELD(riscv, mstatus, MPRV); +static Bool getMPRV(riscvP riscv) { + + Bool MPRV = RD_CSR_FIELD(riscv, mstatus, MPRV); + + // in debug mode, MPRV requires dcsr.mprven to be set + if(inDebugMode(riscv)) { + MPRV &= RD_CSR_FIELD(riscv, dcsr, mprven); + } + + return MPRV; } // @@ -273,7 +281,7 @@ inline static riscvMode getMPP(riscvP riscv) { } // -// Get effective value of MSTATUS.SUM +// Get effective value of SATP.ASID // static Uns32 getActiveASID(riscvP riscv) { return RD_CSR_FIELD(riscv, satp, ASID); @@ -402,7 +410,7 @@ static Uns64 readPageTableEntry( Uns32 entryBytes, memAccessAttrs attrs ) { - memEndian endian = riscv->dendian; + memEndian endian = riscvGetDataEndian(riscv, RISCV_MODE_SUPERVISOR); Uns64 result; // enter PTW context @@ -433,7 +441,7 @@ static void writePageTableEntry( memAccessAttrs attrs, Uns64 value ) { - memEndian endian = riscv->dendian; + memEndian endian = riscvGetDataEndian(riscv, RISCV_MODE_SUPERVISOR); // enter PTW context riscv->PTWActive = True; @@ -1876,14 +1884,14 @@ static tlbEntryP validateTLBEntryPriv( } else if(!(priv=checkEntryPermission(riscv, mode, entry, requiredPriv))) { // specified permissions are inadequate - entry = NULL; + entry = 0; } else if((requiredPriv&MEM_PRIV_W) && !entry->D) { // writing using an entry not marked as dirty: discard the entry and // reload it (will write the entry marked as dirty) deleteTLBEntry(riscv, riscv->tlb, entry); - entry = NULL; + entry = 0; } else { @@ -2805,6 +2813,9 @@ void riscvVMRefreshMPRVDomain(riscvP riscv) { mode = modeMPP; } + // record data access mode (affects endianness) + riscv->dmode = mode; + // look for virtual domain for this mode if required if(VM) { domain = riscv->vmDomains[mode][0]; diff --git a/source/riscvVariant.c b/source/riscvVariant.c index 1290bbc..87a5be6 100644 --- a/source/riscvVariant.c +++ b/source/riscvVariant.c @@ -33,178 +33,86 @@ Bool riscvVFSupport(riscvP riscv, riscvVFeature feature) { // version 0.7.1-draft-20190605 [RVVV_0_7_1] = { - [RVVF_W_SYNTAX] = 0, [RVVF_ZERO_TAIL] = 1, [RVVF_STRICT_OVERLAP] = 1, [RVVF_SEXT_IOFFSET] = 1, - [RVVF_SEXT_VMV_X_S] = 0, [RVVF_SETVLZ_MAX] = 1, - [RVVF_SETVLZ_PRESERVE] = 0, - [RVVF_VAMO_SEW] = 0, - [RVVF_ADC_SBC_MASK] = 0, - [RVVF_SEXT_SLIDE1_SRC] = 0, - [RVVF_FP_REQUIRES_FSNZ] = 0, - [RVVF_VLENB_PRESENT] = 0, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.7.1-draft-20190605+ [RVVV_0_7_1_P] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, [RVVF_SEXT_IOFFSET] = 1, [RVVF_SEXT_VMV_X_S] = 1, [RVVF_SETVLZ_MAX] = 1, - [RVVF_SETVLZ_PRESERVE] = 0, [RVVF_VAMO_SEW] = 1, - [RVVF_ADC_SBC_MASK] = 0, - [RVVF_SEXT_SLIDE1_SRC] = 0, - [RVVF_FP_REQUIRES_FSNZ] = 0, - [RVVF_VLENB_PRESENT] = 0, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.8-draft-20190906 [RVVV_0_8_20190906] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, [RVVF_SEXT_IOFFSET] = 1, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, [RVVF_SETVLZ_PRESERVE] = 1, [RVVF_VAMO_SEW] = 1, - [RVVF_ADC_SBC_MASK] = 0, - [RVVF_SEXT_SLIDE1_SRC] = 0, - [RVVF_FP_REQUIRES_FSNZ] = 0, - [RVVF_VLENB_PRESENT] = 0, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.8-draft-20191004 [RVVV_0_8_20191004] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, [RVVF_SEXT_IOFFSET] = 1, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, [RVVF_SETVLZ_PRESERVE] = 1, [RVVF_VAMO_SEW] = 1, - [RVVF_ADC_SBC_MASK] = 0, - [RVVF_SEXT_SLIDE1_SRC] = 0, - [RVVF_FP_REQUIRES_FSNZ] = 0, - [RVVF_VLENB_PRESENT] = 0, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.8-draft-20191117 [RVVV_0_8_20191117] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, - [RVVF_SEXT_IOFFSET] = 0, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, [RVVF_SETVLZ_PRESERVE] = 1, [RVVF_VAMO_SEW] = 1, [RVVF_ADC_SBC_MASK] = 1, [RVVF_SEXT_SLIDE1_SRC] = 1, - [RVVF_FP_REQUIRES_FSNZ] = 0, [RVVF_VLENB_PRESENT] = 1, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.8-draft-20191118 [RVVV_0_8_20191118] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, - [RVVF_SEXT_IOFFSET] = 0, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, - [RVVF_SETVLZ_PRESERVE] = 0, [RVVF_VAMO_SEW] = 1, [RVVF_ADC_SBC_MASK] = 1, [RVVF_SEXT_SLIDE1_SRC] = 1, - [RVVF_FP_REQUIRES_FSNZ] = 0, [RVVF_VLENB_PRESENT] = 1, - [RVVF_VCSR_PRESENT] = 0, - [RVVF_VS_STATUS_8] = 0, - [RVVF_VS_STATUS_9] = 0, - [RVVF_FP_RESTRICT_WHOLE] = 0, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version 0.8 [RVVV_0_8] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, - [RVVF_SEXT_IOFFSET] = 0, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, - [RVVF_SETVLZ_PRESERVE] = 0, [RVVF_VAMO_SEW] = 1, [RVVF_ADC_SBC_MASK] = 1, [RVVF_SEXT_SLIDE1_SRC] = 1, [RVVF_FP_REQUIRES_FSNZ] = 1, [RVVF_VLENB_PRESENT] = 1, - [RVVF_VCSR_PRESENT] = 0, [RVVF_VS_STATUS_8] = 1, - [RVVF_VS_STATUS_9] = 0, [RVVF_FP_RESTRICT_WHOLE] = 1, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, }, // version master [RVVV_MASTER] = { [RVVF_W_SYNTAX] = 1, - [RVVF_ZERO_TAIL] = 0, - [RVVF_STRICT_OVERLAP] = 0, - [RVVF_SEXT_IOFFSET] = 0, [RVVF_SEXT_VMV_X_S] = 1, - [RVVF_SETVLZ_MAX] = 0, - [RVVF_SETVLZ_PRESERVE] = 0, [RVVF_VAMO_SEW] = 1, [RVVF_ADC_SBC_MASK] = 1, [RVVF_SEXT_SLIDE1_SRC] = 1, [RVVF_FP_REQUIRES_FSNZ] = 1, [RVVF_VLENB_PRESENT] = 1, [RVVF_VCSR_PRESENT] = 1, - [RVVF_VS_STATUS_8] = 0, [RVVF_VS_STATUS_9] = 1, [RVVF_FP_RESTRICT_WHOLE] = 1, - [RVVF_UNIT_STRIDE_ONLY] = 0, - [RVVF_VSTART_Z] = 0, + [RVVF_SEG_ONLY_SEW] = 1, }, }; diff --git a/source/riscvVariant.h b/source/riscvVariant.h index 87e8c3b..30136d0 100644 --- a/source/riscvVariant.h +++ b/source/riscvVariant.h @@ -33,6 +33,7 @@ #define RM_INVALID_CHAR ('Z'+3) #define RISCV_FAND_CHAR ('Z'+4) #define MSTATUS_FS_CHAR ('Z'+5) +#define MSTATUS_BE_CHAR ('Z'+6) #define RISCV_FEATURE_INDEX(_C) ((_C)-'A') #define RISCV_FEATURE_BIT(_C) (1<