diff --git a/.gitignore b/.gitignore index 6e9920603..630731b23 100644 --- a/.gitignore +++ b/.gitignore @@ -112,4 +112,6 @@ sim/results-error/ sim/test1.rep sim/vsim.log tests/coverage/*.elf -*.elf.memfile \ No newline at end of file +*.elf.memfile +sim/*Cache.log +sim/branch \ No newline at end of file diff --git a/bin/CacheSim.py b/bin/CacheSim.py new file mode 100755 index 000000000..5669b35c1 --- /dev/null +++ b/bin/CacheSim.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 + +########################################### +## CacheSim.py +## +## Written: lserafini@hmc.edu +## Created: 27 March 2023 +## Modified: 5 April 2023 +## +## Purpose: Simulate a L1 D$ or I$ for comparison with Wally +## +## A component of the CORE-V-WALLY configurable RISC-V project. +## +## Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University +## +## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +## +## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +## except in compliance with the License, or, at your option, the Apache License version 2.0. You +## may obtain a copy of the License at +## +## https:##solderpad.org/licenses/SHL-2.1/ +## +## Unless required by applicable law or agreed to in writing, any work distributed under the +## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +## either express or implied. See the License for the specific language governing permissions +## and limitations under the License. +################################################################################################ + +# how to invoke this simulator: +# CacheSim.py -f (-v) +# so the default invocation for rv64gc is 'CacheSim.py 64 4 56 44 -f ' +# the log files to run this simulator on can be generated from testbench.sv +# by setting I_CACHE_ADDR_LOGGER and/or D_CACHE_ADDR_LOGGER to 1 before running tests. +# I (Lim) recommend logging a single set of tests (such as wally64priv) at a time. +# This helps avoid unexpected logger behavior. +# With verbose mode off, the simulator only reports mismatches between its and Wally's behavior. +# With verbose mode on, the simulator logs each access into the cache. + +import sys +import math +import argparse +import os + +class CacheLine: + def __init__(self): + self.tag = 0 + self.valid = False + self.dirty = False + + def __str__(self): + string = "(V: " + str(self.valid) + ", D: " + str(self.dirty) + string += ", Tag: " + str(hex(self.tag)) + ")" + return string + + def __repr__(self): + return self.__str__() + +class Cache: + def __init__(self, numsets, numways, addrlen, taglen): + self.numways = numways + self.numsets = numsets + + self.addrlen = addrlen + self.taglen = taglen + self.setlen = int(math.log(numsets, 2)) + self.offsetlen = self.addrlen - self.taglen - self.setlen + + self.ways = [] + for i in range(numways): + self.ways.append([]) + for j in range(numsets): + self.ways[i].append(CacheLine()) + + self.pLRU = [] + for i in range(self.numsets): + self.pLRU.append([0]*(self.numways-1)) + + # flushes the cache by setting all dirty bits to False + def flush(self): + for way in self.ways: + for line in way: + line.dirty = False + + # invalidates the cache by setting all valid bits to False + def invalidate(self): + for way in self.ways: + for line in way: + line.valid = False + + # resets the pLRU to a fresh 2-D array of 0s + def clear_pLRU(self): + self.pLRU = [] + for i in range(self.numsets): + self.pLRU.append([0]*(self.numways-1)) + + # splits the given address into tag, set, and offset + def splitaddr(self, addr): + # no need for offset in the sim, but it's here for debug + tag = addr >> (self.setlen + self.offsetlen) & int('1'*self.taglen, 2) + setnum = (addr >> self.offsetlen) & int('1'*self.setlen, 2) + offset = addr & int('1'*self.offsetlen, 2) + return tag, setnum, offset + + # performs a cache access with the given address. + # returns a character representing the outcome: + # H/M/E/D - hit, miss, eviction, or eviction with writeback + def cacheaccess(self, addr, write=False): + tag, setnum, _ = self.splitaddr(addr) + + # check our ways to see if we have a hit + for waynum in range(self.numways): + line = self.ways[waynum][setnum] + if line.tag == tag and line.valid: + line.dirty = line.dirty or write + self.update_pLRU(waynum, setnum) + return 'H' + + # we didn't hit, but we may not need to evict. + # check for an empty way line. + for waynum in range(self.numways): + line = self.ways[waynum][setnum] + if not line.valid: + line.tag = tag + line.valid = True + line.dirty = write + self.update_pLRU(waynum, setnum) + return 'M' + + # we need to evict. Select a victim and overwrite. + victim = self.getvictimway(setnum) + line = self.ways[victim][setnum] + prevdirty = line.dirty + line.tag = tag + line.valid = True # technically redundant + line.dirty = write + self.update_pLRU(victim, setnum) + return 'D' if prevdirty else 'E' + + # updates the psuedo-LRU tree for the given set + # with an access to the given way + def update_pLRU(self, waynum, setnum): + if self.numways == 1: + return + + tree = self.pLRU[setnum] + bottomrow = (self.numways - 1)//2 + index = (waynum // 2) + bottomrow + tree[index] = int(not (waynum % 2)) + while index > 0: + parent = (index-1) // 2 + tree[parent] = index % 2 + index = parent + + # uses the psuedo-LRU tree to select + # a victim way from the given set + # returns the victim way as an integer + def getvictimway(self, setnum): + if self.numways == 1: + return 0 + + tree = self.pLRU[setnum] + index = 0 + bottomrow = (self.numways - 1) // 2 #first index on the bottom row of the tree + while index < bottomrow: + if tree[index] == 0: + # Go to the left child + index = index*2 + 1 + else: #tree[index] == 1 + # Go to the right child + index = index*2 + 2 + + victim = (index - bottomrow)*2 + if tree[index] == 1: + victim += 1 + + return victim + + def __str__(self): + string = "" + for i in range(self.numways): + string += "Way " + str(i) + ": " + for line in self.ways[i]: + string += str(line) + ", " + string += "\n\n" + return string + + def __repr__(self): + return self.__str__() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Simulates a L1 cache.") + parser.add_argument('numlines', type=int, help="The number of lines per way (a power of 2)", metavar="L") + parser.add_argument('numways', type=int, help="The number of ways (a power of 2)", metavar='W') + parser.add_argument('addrlen', type=int, help="Length of the address in bits (a power of 2)", metavar="A") + parser.add_argument('taglen', type=int, help="Length of the tag in bits", metavar="T") + parser.add_argument('-f', "--file", required=True, help="Log file to simulate from") + parser.add_argument('-v', "--verbose", action='store_true', help="verbose/full-trace mode") + + args = parser.parse_args() + cache = Cache(args.numlines, args.numways, args.addrlen, args.taglen) + #numtests = -1 + extfile = os.path.expanduser(args.file) + with open(extfile, "r") as f: + for ln in f: + ln = ln.strip() + lninfo = ln.split() + if len(lninfo) < 3: #non-address line + if len(lninfo) > 0 and (lninfo[0] == 'BEGIN' or lninfo[0] == 'TRAIN'): + # currently BEGIN and END traces aren't being recorded correctly + # trying TRAIN clears instead + cache.invalidate() # a new test is starting, so 'empty' the cache + cache.clear_pLRU() + #numtests +=1 + if args.verbose: + print("New Test") + + else: + if lninfo[1] == 'F': + cache.flush() + if args.verbose: + print("F") + elif lninfo[1] == 'I': + cache.invalidate() + if args.verbose: + print("I") + else: + addr = int(lninfo[0], 16) + iswrite = lninfo[1] == 'W' or lninfo[1] == 'A' + result = cache.cacheaccess(addr, iswrite) + if args.verbose: + tag, setnum, offset = cache.splitaddr(addr) + print(hex(addr), hex(tag), hex(setnum), hex(offset), lninfo[2], result) + if not result == lninfo[2]: + print("Result mismatch at address", lninfo[0], ". Wally:", lninfo[2],", Sim:", result) #, "in test", numtests) + + + + + diff --git a/sim/imperas.ic b/sim/imperas.ic index 2295c9d45..4c221f2af 100644 --- a/sim/imperas.ic +++ b/sim/imperas.ic @@ -4,24 +4,36 @@ --showcommands # Core settings +--override cpu/priv_version=1.12 +--override cpu/user_version=20191213 +# arch +--override cpu/mimpid=0x100 +--override refRoot/cpu/tvec_align=64 + +# clarify +#--override refRoot/cpu/mtvec_sext=F + +--override cpu/tval_ii_code=T + +#--override cpu/time_undefined=T +#--override cpu/cycle_undefined=T +#--override cpu/instret_undefined=T +#--override cpu/hpmcounter_undefined=T + +--override cpu/reset_address=0x80000000 + --override cpu/unaligned=F --override cpu/ignore_non_leaf_DAU=1 -#--override cpu/wfi_is_nop=T ---override cpu/mimpid=0x100 +--override cpu/wfi_is_nop=T --override cpu/misa_Extensions_mask=0x0 +#--override cpu/updatePTEA=T +#--override cpu/updatePTED=T +--override cpu/Sstc=T +# THIS NEEDS FIXING to 16 --override cpu/PMP_registers=16 --override cpu/PMP_undefined=T -# Wally-specific non-default configuraiton ---override refRoot/cpu/Sstc=T ---override cpu/add_implicit_Extensions=B ---override cpu/bitmanip_version=1.0.0 - -# Illegal instruction should not contain the bit pattern -# illegal pmp read contained this -# --override cpu/tval_ii_code=F - # PMA Settings # 'r': read access allowed # 'w': write access allowed @@ -51,19 +63,11 @@ #-override refRoot/cpu/cv/cover=basic #-override refRoot/cpu/cv/extensions=RV32I - - # Add Imperas simulator application instruction tracing ---override cpu/show_c_prefix=T - ---trace --tracechange --traceshowicount --tracemode -tracemem ASX --monitornetschange --traceafter 800000 - -# Exceptions and pagetables debug ---override cpu/debugflags=6 - -# Turn on verbose output for Imperas simulator and Model --verbose ---override cpu/verbose=1 +--trace --tracechange --traceshowicount --tracemode -tracemem ASX --monitornetschange --traceafter 0 +--override cpu/debugflags=6 --override cpu/verbose=1 +--override cpu/show_c_prefix=T # Store simulator output to logfile --output imperas.log diff --git a/src/hazard/hazard.sv b/src/hazard/hazard.sv index bc9f7baa0..11efacffa 100644 --- a/src/hazard/hazard.sv +++ b/src/hazard/hazard.sv @@ -36,7 +36,7 @@ module hazard ( input logic FCvtIntStallD, FPUStallD, input logic DivBusyE, FDivBusyE, input logic EcallFaultM, BreakpointFaultM, - input logic WFIStallM, + input logic wfiM, IntPendingM, // Stall & flush outputs output logic StallF, StallD, StallE, StallM, StallW, output logic FlushD, FlushE, FlushM, FlushW @@ -45,6 +45,12 @@ module hazard ( logic StallFCause, StallDCause, StallECause, StallMCause, StallWCause; logic LatestUnstalledD, LatestUnstalledE, LatestUnstalledM, LatestUnstalledW; logic FlushDCause, FlushECause, FlushMCause, FlushWCause; + + logic WFIStallM, WFIInterruptedM; + + // WFI logic + assign WFIStallM = wfiM & ~IntPendingM; // WFI waiting for an interrupt or timeout + assign WFIInterruptedM = wfiM & IntPendingM; // WFI detects a pending interrupt. Retire WFI; trap if interrupt is enabled. // stalls and flushes // loads: stall for one cycle if the subsequent instruction depends on the load @@ -68,7 +74,7 @@ module hazard ( assign FlushDCause = TrapM | RetM | CSRWriteFenceM | BPWrongE; assign FlushECause = TrapM | RetM | CSRWriteFenceM |(BPWrongE & ~(DivBusyE | FDivBusyE)); assign FlushMCause = TrapM | RetM | CSRWriteFenceM; - assign FlushWCause = TrapM; + assign FlushWCause = TrapM & ~WFIInterruptedM; // Stall causes // Most data depenency stalls are identified in the decode stage diff --git a/src/lsu/lsu.sv b/src/lsu/lsu.sv index f2e147f00..51efeccb2 100644 --- a/src/lsu/lsu.sv +++ b/src/lsu/lsu.sv @@ -62,7 +62,7 @@ module lsu ( output logic LoadPageFaultM, StoreAmoPageFaultM, // Page fault exceptions output logic LoadMisalignedFaultM, // Load address misaligned fault output logic LoadAccessFaultM, // Load access fault (PMA) - output logic HPTWInstrAccessFaultM, // HPTW generated access fault during instruction fetch + output logic HPTWInstrAccessFaultF, // HPTW generated access fault during instruction fetch // cpu hazard unit (trap) output logic StoreAmoMisalignedFaultM, // Store or AMO address misaligned fault output logic StoreAmoAccessFaultM, // Store or AMO access fault @@ -159,7 +159,7 @@ module lsu ( .IEUAdrExtM, .PTE, .IHWriteDataM, .PageType, .PreLSURWM, .LSUAtomicM, .IHAdrM, .HPTWStall, .SelHPTW, .IgnoreRequestTLB, .LSULoadAccessFaultM, .LSUStoreAmoAccessFaultM, - .LoadAccessFaultM, .StoreAmoAccessFaultM, .HPTWInstrAccessFaultM); + .LoadAccessFaultM, .StoreAmoAccessFaultM, .HPTWInstrAccessFaultF); end else begin // No HPTW, so signals are not multiplexed assign PreLSURWM = MemRWM; assign IHAdrM = IEUAdrExtM; @@ -170,7 +170,7 @@ module lsu ( assign LoadAccessFaultM = LSULoadAccessFaultM; assign StoreAmoAccessFaultM = LSUStoreAmoAccessFaultM; assign {HPTWStall, SelHPTW, PTE, PageType, DTLBWriteM, ITLBWriteF, IgnoreRequestTLB} = '0; - assign HPTWInstrAccessFaultM = '0; + assign HPTWInstrAccessFaultF = '0; end // CommittedM indicates the cache, bus, or HPTW are busy with a multiple cycle operation. diff --git a/src/mmu/hptw.sv b/src/mmu/hptw.sv index b093167cf..95ab43360 100644 --- a/src/mmu/hptw.sv +++ b/src/mmu/hptw.sv @@ -64,7 +64,7 @@ module hptw ( output logic SelHPTW, output logic HPTWStall, input logic LSULoadAccessFaultM, LSUStoreAmoAccessFaultM, - output logic LoadAccessFaultM, StoreAmoAccessFaultM, HPTWInstrAccessFaultM + output logic LoadAccessFaultM, StoreAmoAccessFaultM, HPTWInstrAccessFaultF ); typedef enum logic [3:0] {L0_ADR, L0_RD, @@ -98,12 +98,25 @@ module hptw ( logic [1:0] HPTWRW; logic [2:0] HPTWSize; // 32 or 64 bit access statetype WalkerState, NextWalkerState, InitialWalkerState; + logic HPTWLoadAccessFault, HPTWStoreAmoAccessFault, HPTWInstrAccessFault; + logic HPTWLoadAccessFaultDelay, HPTWStoreAmoAccessFaultDelay, HPTWInstrAccessFaultDelay; + logic HPTWAccessFaultDelay; + logic TakeHPTWFault, TakeHPTWFaultDelay; // map hptw access faults onto either the original LSU load/store fault or instruction access fault assign LSUAccessFaultM = LSULoadAccessFaultM | LSUStoreAmoAccessFaultM; - assign LoadAccessFaultM = WalkerState == IDLE ? LSULoadAccessFaultM : LSUAccessFaultM & DTLBWalk & MemRWM[1] & ~MemRWM[0]; - assign StoreAmoAccessFaultM = WalkerState == IDLE ? LSUStoreAmoAccessFaultM : LSUAccessFaultM & DTLBWalk & MemRWM[0]; - assign HPTWInstrAccessFaultM = WalkerState == IDLE ? 1'b0: LSUAccessFaultM & ~DTLBWalk; + assign HPTWLoadAccessFault = LSUAccessFaultM & DTLBWalk & MemRWM[1] & ~MemRWM[0]; + assign HPTWStoreAmoAccessFault = LSUAccessFaultM & DTLBWalk & MemRWM[0]; + assign HPTWInstrAccessFault = LSUAccessFaultM & ~DTLBWalk; + + flopr #(4) HPTWAccesFaultReg(clk, reset, {TakeHPTWFault, HPTWLoadAccessFault, HPTWStoreAmoAccessFault, HPTWInstrAccessFault}, + {TakeHPTWFaultDelay, HPTWLoadAccessFaultDelay, HPTWStoreAmoAccessFaultDelay, HPTWInstrAccessFaultDelay}); + + assign TakeHPTWFault = WalkerState != IDLE; + + assign LoadAccessFaultM = TakeHPTWFaultDelay ? HPTWLoadAccessFaultDelay : LSULoadAccessFaultM; + assign StoreAmoAccessFaultM = TakeHPTWFaultDelay ? HPTWStoreAmoAccessFaultDelay : LSUStoreAmoAccessFaultM; + assign HPTWInstrAccessFaultF = TakeHPTWFaultDelay ? HPTWInstrAccessFaultDelay : 1'b0; // Extract bits from CSRs and inputs assign SvMode = SATP_REGW[`XLEN-1:`XLEN-`SVMODE_BITS]; @@ -247,22 +260,26 @@ module hptw ( flopenl #(.TYPE(statetype)) WalkerStateReg(clk, reset | FlushW, 1'b1, NextWalkerState, IDLE, WalkerState); always_comb case (WalkerState) - IDLE: if (TLBMiss & ~DCacheStallM) NextWalkerState = InitialWalkerState; + IDLE: if (TLBMiss & ~DCacheStallM & ~HPTWAccessFaultDelay) NextWalkerState = InitialWalkerState; else NextWalkerState = IDLE; L3_ADR: NextWalkerState = L3_RD; // first access in SV48 L3_RD: if (DCacheStallM) NextWalkerState = L3_RD; + else if(LSUAccessFaultM) NextWalkerState = IDLE; else NextWalkerState = L2_ADR; L2_ADR: if (InitialWalkerState == L2_ADR | ValidNonLeafPTE) NextWalkerState = L2_RD; // first access in SV39 else NextWalkerState = LEAF; L2_RD: if (DCacheStallM) NextWalkerState = L2_RD; + else if(LSUAccessFaultM) NextWalkerState = IDLE; else NextWalkerState = L1_ADR; L1_ADR: if (InitialWalkerState == L1_ADR | ValidNonLeafPTE) NextWalkerState = L1_RD; // first access in SV32 else NextWalkerState = LEAF; L1_RD: if (DCacheStallM) NextWalkerState = L1_RD; + else if(LSUAccessFaultM) NextWalkerState = IDLE; else NextWalkerState = L0_ADR; L0_ADR: if (ValidNonLeafPTE) NextWalkerState = L0_RD; else NextWalkerState = LEAF; L0_RD: if (DCacheStallM) NextWalkerState = L0_RD; + else if(LSUAccessFaultM) NextWalkerState = IDLE; else NextWalkerState = LEAF; LEAF: if (`SVADU_SUPPORTED & HPTWUpdateDA) NextWalkerState = UPDATE_PTE; else NextWalkerState = IDLE; @@ -273,7 +290,8 @@ module hptw ( assign IgnoreRequestTLB = WalkerState == IDLE & TLBMiss; assign SelHPTW = WalkerState != IDLE; - assign HPTWStall = (WalkerState != IDLE) | (WalkerState == IDLE & TLBMiss); + assign HPTWAccessFaultDelay = HPTWLoadAccessFaultDelay | HPTWStoreAmoAccessFaultDelay | HPTWInstrAccessFaultDelay; + assign HPTWStall = (WalkerState != IDLE) | (WalkerState == IDLE & TLBMiss & ~(HPTWAccessFaultDelay)); assign ITLBMissOrUpdateDAF = ITLBMissF | (`SVADU_SUPPORTED & InstrUpdateDAF); assign DTLBMissOrUpdateDAM = DTLBMissM | (`SVADU_SUPPORTED & DataUpdateDAM); diff --git a/src/privileged/privileged.sv b/src/privileged/privileged.sv index 6fa8dcf98..7d34e22fc 100644 --- a/src/privileged/privileged.sv +++ b/src/privileged/privileged.sv @@ -65,7 +65,7 @@ module privileged ( // fault sources input logic InstrAccessFaultF, // instruction access fault input logic LoadAccessFaultM, StoreAmoAccessFaultM, // load or store access fault - input logic HPTWInstrAccessFaultM, // hardware page table access fault while fetching instruction PTE + input logic HPTWInstrAccessFaultF, // hardware page table access fault while fetching instruction PTE input logic InstrPageFaultF, // page faults input logic LoadPageFaultM, StoreAmoPageFaultM, // page faults input logic InstrMisalignedFaultM, // misaligned instruction fault @@ -93,7 +93,7 @@ module privileged ( output logic BigEndianM, // Use big endian in current privilege mode // Fault outputs output logic BreakpointFaultM, EcallFaultM, // breakpoint and Ecall traps should retire - output logic WFIStallM // Stall in Memory stage for WFI until interrupt or timeout + output logic wfiM, IntPendingM // Stall in Memory stage for WFI until interrupt pending or timeout ); logic [3:0] CauseM; // trap cause @@ -110,10 +110,10 @@ module privileged ( logic [11:0] MIP_REGW, MIE_REGW; // interrupt pending and enable bits logic [1:0] NextPrivilegeModeM; // next privilege mode based on trap or return logic DelegateM; // trap should be delegated - logic wfiM; // wait for interrupt instruction - logic IntPendingM; // interrupt is pending, even if not enabled. ends wfi logic InterruptM; // interrupt occuring logic ExceptionM; // Memory stage instruction caused a fault + logic HPTWInstrAccessFaultM; // Hardware page table access fault while fetching instruction PTE + // track the current privilege level privmode privmode(.clk, .reset, .StallW, .TrapM, .mretM, .sretM, .DelegateM, @@ -144,8 +144,8 @@ module privileged ( // pipeline early-arriving trap sources privpiperegs ppr(.clk, .reset, .StallD, .StallE, .StallM, .FlushD, .FlushE, .FlushM, - .InstrPageFaultF, .InstrAccessFaultF, .IllegalIEUFPUInstrD, - .InstrPageFaultM, .InstrAccessFaultM, .IllegalIEUFPUInstrM); + .InstrPageFaultF, .InstrAccessFaultF, .HPTWInstrAccessFaultF, .IllegalIEUFPUInstrD, + .InstrPageFaultM, .InstrAccessFaultM, .HPTWInstrAccessFaultM, .IllegalIEUFPUInstrM); // trap logic trap trap(.reset, @@ -156,7 +156,7 @@ module privileged ( .mretM, .sretM, .PrivilegeModeW, .MIP_REGW, .MIE_REGW, .MIDELEG_REGW, .MEDELEG_REGW, .STATUS_MIE, .STATUS_SIE, .InstrValidM, .CommittedM, .CommittedF, - .TrapM, .RetM, .wfiM, .InterruptM, .ExceptionM, .IntPendingM, .DelegateM, .WFIStallM, .CauseM); + .TrapM, .RetM, .wfiM, .InterruptM, .ExceptionM, .IntPendingM, .DelegateM, .CauseM); endmodule diff --git a/src/privileged/privpiperegs.sv b/src/privileged/privpiperegs.sv index c3d308c11..684b0ad73 100644 --- a/src/privileged/privpiperegs.sv +++ b/src/privileged/privpiperegs.sv @@ -33,24 +33,26 @@ module privpiperegs ( input logic StallD, StallE, StallM, input logic FlushD, FlushE, FlushM, input logic InstrPageFaultF, InstrAccessFaultF, // instruction faults + input logic HPTWInstrAccessFaultF, // hptw fault during instruction page fetch input logic IllegalIEUFPUInstrD, // illegal IEU instruction decoded output logic InstrPageFaultM, InstrAccessFaultM, // delayed instruction faults - output logic IllegalIEUFPUInstrM // delayed illegal IEU instruction + output logic IllegalIEUFPUInstrM, // delayed illegal IEU instruction + output logic HPTWInstrAccessFaultM // hptw fault during instruction page fetch ); // Delayed fault signals - logic InstrPageFaultD, InstrAccessFaultD; - logic InstrPageFaultE, InstrAccessFaultE; + logic InstrPageFaultD, InstrAccessFaultD, HPTWInstrAccessFaultD; + logic InstrPageFaultE, InstrAccessFaultE, HPTWInstrAccessFaultE; logic IllegalIEUFPUInstrE; // pipeline fault signals - flopenrc #(2) faultregD(clk, reset, FlushD, ~StallD, - {InstrPageFaultF, InstrAccessFaultF}, - {InstrPageFaultD, InstrAccessFaultD}); - flopenrc #(3) faultregE(clk, reset, FlushE, ~StallE, - {IllegalIEUFPUInstrD, InstrPageFaultD, InstrAccessFaultD}, - {IllegalIEUFPUInstrE, InstrPageFaultE, InstrAccessFaultE}); - flopenrc #(3) faultregM(clk, reset, FlushM, ~StallM, - {IllegalIEUFPUInstrE, InstrPageFaultE, InstrAccessFaultE}, - {IllegalIEUFPUInstrM, InstrPageFaultM, InstrAccessFaultM}); -endmodule \ No newline at end of file + flopenrc #(3) faultregD(clk, reset, FlushD, ~StallD, + {InstrPageFaultF, InstrAccessFaultF, HPTWInstrAccessFaultF}, + {InstrPageFaultD, InstrAccessFaultD, HPTWInstrAccessFaultD}); + flopenrc #(4) faultregE(clk, reset, FlushE, ~StallE, + {IllegalIEUFPUInstrD, InstrPageFaultD, InstrAccessFaultD, HPTWInstrAccessFaultD}, + {IllegalIEUFPUInstrE, InstrPageFaultE, InstrAccessFaultE, HPTWInstrAccessFaultE}); + flopenrc #(4) faultregM(clk, reset, FlushM, ~StallM, + {IllegalIEUFPUInstrE, InstrPageFaultE, InstrAccessFaultE, HPTWInstrAccessFaultE}, + {IllegalIEUFPUInstrM, InstrPageFaultM, InstrAccessFaultM, HPTWInstrAccessFaultM}); +endmodule diff --git a/src/privileged/trap.sv b/src/privileged/trap.sv index ace63b48b..b6e99f2e0 100644 --- a/src/privileged/trap.sv +++ b/src/privileged/trap.sv @@ -48,7 +48,6 @@ module trap ( output logic ExceptionM, // exception is occurring output logic IntPendingM, // Interrupt is pending, might occur if enabled output logic DelegateM, // Delegate trap to supervisor handler - output logic WFIStallM, // Stall due to WFI instruction output logic [3:0] CauseM // trap cause ); @@ -74,7 +73,6 @@ module trap ( assign InterruptM = (|ValidIntsM) & InstrValidM; // suppress interrupt if the memory system has partially processed a request. assign DelegateM = `S_SUPPORTED & (InterruptM ? MIDELEG_REGW[CauseM] : MEDELEG_REGW[CauseM]) & (PrivilegeModeW == `U_MODE | PrivilegeModeW == `S_MODE); - assign WFIStallM = wfiM & ~IntPendingM; /////////////////////////////////////////// // Trigger Traps and RET diff --git a/src/wally/wallypipelinedcore.sv b/src/wally/wallypipelinedcore.sv index 343cf1fdb..ee5df5956 100644 --- a/src/wally/wallypipelinedcore.sv +++ b/src/wally/wallypipelinedcore.sv @@ -106,7 +106,7 @@ module wallypipelinedcore ( logic [1:0] PrivilegeModeW; logic [`XLEN-1:0] PTE; logic [1:0] PageType; - logic sfencevmaM, WFIStallM; + logic sfencevmaM; logic SelHPTW; // PMA checker signals @@ -146,7 +146,7 @@ module wallypipelinedcore ( logic RASPredPCWrongM; logic IClassWrongM; logic [3:0] InstrClassM; - logic InstrAccessFaultF, HPTWInstrAccessFaultM; + logic InstrAccessFaultF, HPTWInstrAccessFaultF; logic [2:0] LSUHSIZE; logic [2:0] LSUHBURST; logic [1:0] LSUHTRANS; @@ -162,7 +162,8 @@ module wallypipelinedcore ( logic CommittedF; logic BranchD, BranchE, JumpD, JumpE; logic DCacheStallM, ICacheStallF; - + logic wfiM, IntPendingM; + // instruction fetch unit: PC, branch prediction, instruction cache ifu ifu(.clk, .reset, .StallF, .StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW, @@ -236,7 +237,7 @@ module wallypipelinedcore ( .StoreAmoPageFaultM, // connects to privilege .LoadMisalignedFaultM, // connects to privilege .LoadAccessFaultM, // connects to privilege - .HPTWInstrAccessFaultM, // connects to privilege + .HPTWInstrAccessFaultF, // connects to privilege .StoreAmoMisalignedFaultM, // connects to privilege .StoreAmoAccessFaultM, // connects to privilege .InstrUpdateDAF, @@ -265,7 +266,7 @@ module wallypipelinedcore ( .FCvtIntStallD, .FPUStallD, .DivBusyE, .FDivBusyE, .EcallFaultM, .BreakpointFaultM, - .WFIStallM, + .wfiM, .IntPendingM, // Stall & flush outputs .StallF, .StallD, .StallE, .StallM, .StallW, .FlushD, .FlushE, .FlushM, .FlushW); @@ -288,17 +289,18 @@ module wallypipelinedcore ( .LoadMisalignedFaultM, .StoreAmoMisalignedFaultM, .MTimerInt, .MExtInt, .SExtInt, .MSwInt, .MTIME_CLINT, .IEUAdrM, .SetFflagsM, - .InstrAccessFaultF, .HPTWInstrAccessFaultM, .LoadAccessFaultM, .StoreAmoAccessFaultM, .SelHPTW, + .InstrAccessFaultF, .HPTWInstrAccessFaultF, .LoadAccessFaultM, .StoreAmoAccessFaultM, .SelHPTW, .PrivilegeModeW, .SATP_REGW, .STATUS_MXR, .STATUS_SUM, .STATUS_MPRV, .STATUS_MPP, .STATUS_FS, .PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW, - .FRM_REGW,.BreakpointFaultM, .EcallFaultM, .WFIStallM, .BigEndianM); + .FRM_REGW,.BreakpointFaultM, .EcallFaultM, .wfiM, .IntPendingM, .BigEndianM); end else begin assign CSRReadValW = 0; assign UnalignedPCNextF = PC2NextF; assign RetM = 0; assign TrapM = 0; - assign WFIStallM = 0; + assign wfiM = 0; + assign IntPendingM = 0; assign sfencevmaM = 0; assign BigEndianM = 0; end diff --git a/testbench/testbench-linux-imperas.sv b/testbench/testbench-linux-imperas.sv index 00167e5fd..d3d71626f 100644 --- a/testbench/testbench-linux-imperas.sv +++ b/testbench/testbench-linux-imperas.sv @@ -413,10 +413,11 @@ module testbench; end end - always @(dut.core.MTimerInt) void'(rvvi.net_push("MTimerInterrupt", dut.core.MTimerInt)); - always @(dut.core.MExtInt) void'(rvvi.net_push("MExternalInterrupt", dut.core.MExtInt)); - always @(dut.core.SExtInt) void'(rvvi.net_push("SExternalInterrupt", dut.core.SExtInt)); - always @(dut.core.MSwInt) void'(rvvi.net_push("MSWInterrupt", dut.core.MSwInt)); + always @(dut.core.MTimerInt) void'(rvvi.net_push("MTimerInterrupt", dut.core.MTimerInt)); + always @(dut.core.MExtInt) void'(rvvi.net_push("MExternalInterrupt", dut.core.MExtInt)); + always @(dut.core.SExtInt) void'(rvvi.net_push("SExternalInterrupt", dut.core.SExtInt)); + always @(dut.core.MSwInt) void'(rvvi.net_push("MSWInterrupt", dut.core.MSwInt)); + always @(dut.core.priv.priv.csr.csrs.csrs.STimerInt) void'(rvvi.net_push("STimerInterrupt", dut.core.priv.priv.csr.csrs.csrs.STimerInt)); final begin void'(rvviRefShutdown()); diff --git a/testbench/testbench.sv b/testbench/testbench.sv index 2bc3622c1..adcb58d56 100644 --- a/testbench/testbench.sv +++ b/testbench/testbench.sv @@ -480,7 +480,7 @@ logic [3:0] dummy; assign EndSample = DCacheFlushStart & ~DCacheFlushDone; flop #(1) BeginReg(clk, StartSampleFirst, BeginDelayed); - assign Begin = StartSampleFirst & ~ BeginDelayed; + assign Begin = StartSampleFirst & ~BeginDelayed; end @@ -555,12 +555,16 @@ logic [3:0] dummy; end - if (`ICACHE_SUPPORTED && `I_CACHE_ADDR_LOGGER) begin + if (`ICACHE_SUPPORTED && `I_CACHE_ADDR_LOGGER) begin : ICacheLogger int file; string LogFile; logic resetD, resetEdge; logic Enable; - assign Enable = ~dut.core.StallD & ~dut.core.FlushD & dut.core.ifu.bus.icache.CacheRWF[1] & ~reset; + // assign Enable = ~dut.core.StallD & ~dut.core.FlushD & dut.core.ifu.bus.icache.CacheRWF[1] & ~reset; + + // this version of Enable allows for accurate eviction logging. + // Likely needs further improvement. + assign Enable = dut.core.ifu.bus.icache.icache.cachefsm.LRUWriteEn & ~reset; flop #(1) ResetDReg(clk, reset, resetD); assign resetEdge = ~reset & resetD; initial begin @@ -568,50 +572,64 @@ end file = $fopen(LogFile, "w"); $fwrite(file, "BEGIN %s\n", memfilename); end - string HitMissString; - assign HitMissString = dut.core.ifu.bus.icache.icache.CacheHit ? "H" : "M"; + string AccessTypeString, HitMissString; + assign HitMissString = dut.core.ifu.bus.icache.icache.CacheHit ? "H" : + dut.core.ifu.bus.icache.icache.vict.cacheLRU.AllValid ? "E" : "M"; + assign AccessTypeString = dut.core.ifu.InvalidateICacheM ? "I" : "R"; always @(posedge clk) begin if(resetEdge) $fwrite(file, "TRAIN\n"); if(Begin) $fwrite(file, "BEGIN %s\n", memfilename); if(Enable) begin // only log i cache reads - $fwrite(file, "%h R %s\n", dut.core.ifu.PCPF, HitMissString); + $fwrite(file, "%h %s %s\n", dut.core.ifu.PCPF, AccessTypeString, HitMissString); end if(EndSample) $fwrite(file, "END %s\n", memfilename); end end - if (`DCACHE_SUPPORTED && `D_CACHE_ADDR_LOGGER) begin + + if (`DCACHE_SUPPORTED && `D_CACHE_ADDR_LOGGER) begin : DCacheLogger int file; string LogFile; logic resetD, resetEdge; - string HitMissString; + logic Enabled; + string AccessTypeString, HitMissString; + flop #(1) ResetDReg(clk, reset, resetD); assign resetEdge = ~reset & resetD; - assign HitMissString = dut.core.lsu.bus.dcache.dcache.CacheHit ? "H" : "M"; + assign HitMissString = dut.core.lsu.bus.dcache.dcache.CacheHit ? "H" : + (!dut.core.lsu.bus.dcache.dcache.vict.cacheLRU.AllValid) ? "M" : + dut.core.lsu.bus.dcache.dcache.LineDirty ? "D" : "E"; + assign AccessTypeString = dut.core.lsu.bus.dcache.FlushDCache ? "F" : + dut.core.lsu.bus.dcache.CacheAtomicM[1] ? "A" : + dut.core.lsu.bus.dcache.CacheRWM == 2'b10 ? "R" : + dut.core.lsu.bus.dcache.CacheRWM == 2'b01 ? "W" : + "NULL"; + // assign Enabled = (dut.core.lsu.bus.dcache.dcache.cachefsm.CurrState == 0) & + // ~dut.core.lsu.bus.dcache.dcache.cachefsm.FlushStage & + // (AccessTypeString != "NULL"); + + // This version of enable allows for accurate eviction logging. + // Likely needs further improvement. + assign Enabled = dut.core.lsu.bus.dcache.dcache.cachefsm.LRUWriteEn & + ~dut.core.lsu.bus.dcache.dcache.cachefsm.FlushStage & + (AccessTypeString != "NULL"); + initial begin - LogFile = $psprintf("DCache.log"); + LogFile = $psprintf("DCache.log"); file = $fopen(LogFile, "w"); - $fwrite(file, "BEGIN %s\n", memfilename); - end + $fwrite(file, "BEGIN %s\n", memfilename); + end always @(posedge clk) begin if(resetEdge) $fwrite(file, "TRAIN\n"); if(Begin) $fwrite(file, "BEGIN %s\n", memfilename); - if(~dut.core.StallW & ~dut.core.FlushW & dut.core.InstrValidM) begin - if(dut.core.lsu.bus.dcache.CacheRWM == 2'b10) begin - $fwrite(file, "%h R %s\n", dut.core.lsu.PAdrM, HitMissString); - end else if (dut.core.lsu.bus.dcache.CacheRWM == 2'b01) begin - $fwrite(file, "%h W %s\n", dut.core.lsu.PAdrM, HitMissString); - end else if (dut.core.lsu.bus.dcache.CacheAtomicM[1] == 1'b1) begin // *** This may change - $fwrite(file, "%h A %s\n", dut.core.lsu.PAdrM, HitMissString); - end else if (dut.core.lsu.bus.dcache.FlushDCache) begin - $fwrite(file, "%h F %s\n", dut.core.lsu.PAdrM, HitMissString); - end + if(Enabled) begin + $fwrite(file, "%h %s %s\n", dut.core.lsu.PAdrM, AccessTypeString, HitMissString); end if(EndSample) $fwrite(file, "END %s\n", memfilename); end end - if (`BPRED_SUPPORTED) begin + if (`BPRED_SUPPORTED) begin : BranchLogger if (`BPRED_LOGGER) begin string direction; int file; diff --git a/testbench/testbench_imperas.sv b/testbench/testbench_imperas.sv index f63c640d7..56ca763af 100644 --- a/testbench/testbench_imperas.sv +++ b/testbench/testbench_imperas.sv @@ -198,10 +198,12 @@ module testbench; end - always @(dut.core.MTimerInt) void'(rvvi.net_push("MTimerInterrupt", dut.core.MTimerInt)); - always @(dut.core.MExtInt) void'(rvvi.net_push("MExternalInterrupt", dut.core.MExtInt)); - always @(dut.core.SExtInt) void'(rvvi.net_push("SExternalInterrupt", dut.core.SExtInt)); - always @(dut.core.MSwInt) void'(rvvi.net_push("MSWInterrupt", dut.core.MSwInt)); + always @(dut.core.MTimerInt) void'(rvvi.net_push("MTimerInterrupt", dut.core.MTimerInt)); + always @(dut.core.MExtInt) void'(rvvi.net_push("MExternalInterrupt", dut.core.MExtInt)); + always @(dut.core.SExtInt) void'(rvvi.net_push("SExternalInterrupt", dut.core.SExtInt)); + always @(dut.core.MSwInt) void'(rvvi.net_push("MSWInterrupt", dut.core.MSwInt)); + always @(dut.core.priv.priv.csr.csrs.csrs.STimerInt) void'(rvvi.net_push("STimerInterrupt", dut.core.priv.priv.csr.csrs.csrs.STimerInt)); + final begin void'(rvviRefShutdown()); diff --git a/tests/custom/cacheSimTest/CacheSimTest.py b/tests/custom/cacheSimTest/CacheSimTest.py new file mode 100755 index 000000000..44cc00b06 --- /dev/null +++ b/tests/custom/cacheSimTest/CacheSimTest.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +########################################### +## CacheSimTest.py +## +## Written: lserafini@hmc.edu +## Created: 4 April 2023 +## Modified: 5 April 2023 +## +## Purpose: Confirm that the cache simulator behaves as expected. +## +## A component of the CORE-V-WALLY configurable RISC-V project. +## +## Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University +## +## SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +## +## Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file +## except in compliance with the License, or, at your option, the Apache License version 2.0. You +## may obtain a copy of the License at +## +## https:##solderpad.org/licenses/SHL-2.1/ +## +## Unless required by applicable law or agreed to in writing, any work distributed under the +## License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +## either express or implied. See the License for the specific language governing permissions +## and limitations under the License. +################################################################################################ + +import sys +import os + +sys.path.append(os.path.expanduser("~/cvw/bin")) +import CacheSim as cs + +if __name__ == "__main__": + cache = cs.Cache(16, 4, 16, 8) + # 0xABCD -> tag: AB, set: C, offset: D + + #address split checking + assert (cache.splitaddr(0x1234) == (0x12,0x3,0x4)) + assert (cache.splitaddr(0x2638) == (0x26,0x3,0x8)) + assert (cache.splitaddr(0xA3E6) == (0xA3,0xE,0x6)) + + #insert way 0 set C tag AB + assert (cache.cacheaccess(0xABCD) == 'M') + assert (cache.ways[0][0xC].tag == 0xAB) + assert (cache.cacheaccess(0xABCD) == 'H') + assert (cache.pLRU[0xC] == [1,1,0]) + + #make way 0 set C dirty + assert (cache.cacheaccess(0xABCD, True) == 'H') + + #insert way 1 set C tag AC + assert (cache.cacheaccess(0xACCD) == 'M') + assert (cache.ways[1][0xC].tag == 0xAC) + assert (cache.pLRU[0xC] == [1,0,0]) + + #insert way 2 set C tag AD + assert (cache.cacheaccess(0xADCD) == 'M') + assert (cache.ways[2][0xC].tag == 0xAD) + assert (cache.pLRU[0xC] == [0,0,1]) + + #insert way 3 set C tag AE + assert (cache.cacheaccess(0xAECD) == 'M') + assert (cache.ways[3][0xC].tag == 0xAE) + assert (cache.pLRU[0xC] == [0,0,0]) + + #misc hit and pLRU checking + assert (cache.cacheaccess(0xABCD) == 'H') + assert (cache.pLRU[0xC] == [1,1,0]) + assert (cache.cacheaccess(0xADCD) == 'H') + assert (cache.pLRU[0xC] == [0,1,1]) + + #evict way 1, now set C has tag AF + assert (cache.cacheaccess(0xAFCD) == 'E') + assert (cache.ways[1][0xC].tag == 0xAF) + assert (cache.pLRU[0xC] == [1,0,1]) + + #evict way 3, now set C has tag AC + assert (cache.cacheaccess(0xACCD) == 'E') + assert (cache.ways[3][0xC].tag == 0xAC) + assert (cache.pLRU[0xC] == [0,0,0]) + + #evict way 0, now set C has tag EA + #this line was dirty, so there was a wb + assert (cache.cacheaccess(0xEAC2) == 'D') + assert (cache.ways[0][0xC].tag == 0xEA) + assert (cache.pLRU[0xC] == [1,1,0]) \ No newline at end of file