/////////////////////////////////////////// // loggers.sv // // Written: Rose Thompson rose@rosethompson.net // Modified: 14 June 2023 // // Purpose: Log branch instructions, log instruction fetches, // log I$ misses, log data memory accesses, log D$ misses, and // log other related operations // // A component of the Wally configurable RISC-V project. // // Copyright (C) 2021 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. //////////////////////////////////////////////////////////////////////////////////////////////// module loggers import cvw::*; #(parameter cvw_t P, parameter PrintHPMCounters, parameter I_CACHE_ADDR_LOGGER, parameter D_CACHE_ADDR_LOGGER, parameter BPRED_LOGGER) ( input logic clk, input logic reset, input logic DCacheFlushStart, input logic DCacheFlushDone, // input logic BeginSample, // input logic StartSample, // input logic EndSample, input string memfilename, input string TEST ); // performance counter logging logic BeginSample; logic StartSample, EndSample; if((PrintHPMCounters | BPRED_LOGGER) & P.ZICNTR_SUPPORTED) begin : HPMCSample integer HPMCindex; logic StartSampleFirst; logic StartSampleDelayed, BeginDelayed; logic EndSampleFirst; logic [P.XLEN-1:0] InitialHPMCOUNTERH[P.COUNTERS-1:0]; logic EndSampleDelayed; string HPMCnames[] = '{"Mcycle", "------", "InstRet", "Br Count", "Jump Not Return", "Return", "BP Wrong", "BP Dir Wrong", "BP Target Wrong", "RAS Wrong", "Instr Class Wrong", "Load Stall", "Store Stall", "D Cache Access", "D Cache Miss", "D Cache Cycles", "I Cache Access", "I Cache Miss", "I Cache Cycles", "CSR Write", "FenceI", "SFenceVMA", "Interrupt", "Exception", "Divide Cycles" }; always_comb if (TEST == "embench") begin StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger"; EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger"; end else if (TEST == "coremark") begin StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time"; EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time"; end else begin StartSampleFirst = reset; EndSampleFirst = '0; end flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed); always_comb if (TEST == "embench" | TEST == "coremark") begin EndSample = EndSampleFirst & ~ EndSampleDelayed; end else begin EndSample = DCacheFlushStart & ~DCacheFlushDone; end /* if(TEST == "embench") begin // embench runs warmup then runs start_trigger // embench end with stop_trigger. //assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_trigger"; //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); //assign StartSample = StartSampleFirst & ~ StartSampleDelayed; //assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_trigger"; flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed); assign EndSample = EndSampleFirst & ~ EndSampleDelayed; end else if(TEST == "coremark") begin // embench runs warmup then runs start_trigger // embench end with stop_trigger. //assign StartSampleFirst = FunctionName.FunctionName.FunctionName == "start_time"; //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); //assign StartSample = StartSampleFirst & ~ StartSampleDelayed; //assign EndSampleFirst = FunctionName.FunctionName.FunctionName == "stop_time"; flopr #(1) EndSampleReg(clk, reset, EndSampleFirst, EndSampleDelayed); assign EndSample = EndSampleFirst & ~ EndSampleDelayed; end else begin // default start condiction is reset // default end condiction is end of test (DCacheFlushDone) //assign StartSampleFirst = reset; //flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); //assign StartSample = StartSampleFirst & ~ StartSampleDelayed; //assign EndSample = DCacheFlushStart & ~DCacheFlushDone; flop #(1) BeginReg(clk, StartSampleFirst, BeginDelayed); assign BeginSample = StartSampleFirst & ~BeginDelayed; end */ flopr #(1) StartSampleReg(clk, reset, StartSampleFirst, StartSampleDelayed); assign StartSample = StartSampleFirst & ~StartSampleDelayed; flop #(1) BeginReg(clk, StartSampleFirst, BeginDelayed); // ** is this redundant with StartSampleReg? assign BeginSample = StartSampleFirst & ~BeginDelayed; always @(negedge clk) begin if(StartSample) begin for(HPMCindex = 0; HPMCindex < 32; HPMCindex += 1) begin InitialHPMCOUNTERH[HPMCindex] <= dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex]; end end if(EndSample) begin for(HPMCindex = 0; HPMCindex < HPMCnames.size(); HPMCindex += 1) begin // unlikely to have more than 10M in any counter. $display("Cnt[%2d] = %7d %s", HPMCindex, dut.core.priv.priv.csr.counters.counters.HPMCOUNTER_REGW[HPMCindex] - InitialHPMCOUNTERH[HPMCindex], HPMCnames[HPMCindex]); end end end end if (P.ICACHE_SUPPORTED & I_CACHE_ADDR_LOGGER) begin : ICacheLogger int file; string LogFile; logic resetD, resetEdge; logic Enable; logic InvalDelayed, InvalEdge; assign Enable = dut.core.ifu.bus.icache.icache.cachefsm.LRUWriteEn & dut.core.ifu.immu.immu.pmachecker.Cacheable & ~dut.core.ifu.bus.icache.icache.cachefsm.FlushStage & dut.core.ifu.bus.icache.icache.cachefsm.CacheEn & ~reset; flop #(1) ResetDReg(clk, reset, resetD); assign resetEdge = ~reset & resetD; flop #(1) InvalReg(clk, dut.core.ifu.InvalidateICacheM, InvalDelayed); assign InvalEdge = dut.core.ifu.InvalidateICacheM & ~InvalDelayed; initial begin LogFile = "ICache.log"; file = $fopen(LogFile, "w"); $fwrite(file, "BEGIN %s\n", memfilename); end string AccessTypeString, HitMissString; always @(*) begin HitMissString = dut.core.ifu.bus.icache.icache.Hit ? "H" : dut.core.ifu.bus.icache.icache.vict.cacheLRU.AllValid ? "E" : "M"; end always @(posedge clk) begin if(resetEdge) $fwrite(file, "TRAIN\n"); if(BeginSample) $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); end if(InvalEdge) $fwrite(file, "0 I X\n"); if(EndSample) $fwrite(file, "END %s\n", memfilename); end end if (P.DCACHE_SUPPORTED & D_CACHE_ADDR_LOGGER) begin : DCacheLogger int file; string LogFile; logic resetD, resetEdge; logic Enabled; string AccessTypeString, HitMissString; flop #(1) ResetDReg(clk, reset, resetD); assign resetEdge = ~reset & resetD; always @(*) begin HitMissString = dut.core.lsu.bus.dcache.dcache.Hit ? "H" : (!dut.core.lsu.bus.dcache.dcache.vict.cacheLRU.AllValid) ? "M" : dut.core.lsu.bus.dcache.dcache.LineDirty ? "D" : "E"; AccessTypeString = dut.core.lsu.bus.dcache.FlushDCache ? "F" : dut.core.lsu.LSUAtomicM[1] ? "A" : dut.core.lsu.bus.dcache.CacheRWM == 2'b10 ? "R" : dut.core.lsu.bus.dcache.CacheRWM == 2'b01 ? "W" : dut.core.lsu.bus.dcache.dcache.CMOpM == 4'b1000 ? "Z" : // cbo.zero dut.core.lsu.bus.dcache.dcache.CMOpM == 4'b0001 ? "V" : // cbo.inval should just clear the valid and dirty bits dut.core.lsu.bus.dcache.dcache.CMOpM == 4'b0010 ? "C" : // cbo.clean should act like a read in terms of the lru, but clears the dirty bit dut.core.lsu.bus.dcache.dcache.CMOpM == 4'b0100 ? "L" : // cbo.flush should just clear and the valid and drity bits "NULL"; end assign Enabled = (dut.core.lsu.bus.dcache.dcache.cachefsm.LRUWriteEn | // don't include cbo.zero as it uses LRUWriteEn to update the LRU and would be double counted. ((AccessTypeString == "C" | AccessTypeString == "L" | AccessTypeString == "V") & ~dut.core.lsu.bus.dcache.dcache.cachefsm.CacheStall)) & ~dut.core.lsu.bus.dcache.dcache.cachefsm.FlushStage & dut.core.lsu.dmmu.dmmu.pmachecker.Cacheable & dut.core.lsu.bus.dcache.dcache.cachefsm.CacheEn & (AccessTypeString != "NULL"); initial begin LogFile = "DCache.log"; file = $fopen(LogFile, "w"); $fwrite(file, "BEGIN %s\n", memfilename); end always @(posedge clk) begin if(resetEdge) $fwrite(file, "TRAIN\n"); if(BeginSample) $fwrite(file, "BEGIN %s\n", memfilename); if(Enabled) begin $fwrite(file, "%h %s %s\n", dut.core.lsu.PAdrM, AccessTypeString, HitMissString); end if(dut.core.lsu.bus.dcache.dcache.cachefsm.FlushFlag) $fwrite(file, "0 F X\n"); if(EndSample) $fwrite(file, "END %s\n", memfilename); end end if (P.BPRED_SUPPORTED) begin : BranchLogger if (BPRED_LOGGER) begin string direction; int file, CFIfile; logic PCSrcM; string LogFile, CFILogFile; logic resetD, resetEdge; flopenrc #(1) PCSrcMReg(clk, reset, dut.core.FlushM, ~dut.core.StallM, dut.core.ifu.PCSrcE, PCSrcM); flop #(1) ResetDReg(clk, reset, resetD); assign resetEdge = ~reset & resetD; initial begin LogFile = "branch.log"; // will break some of Rose's research analysis scripts CFILogFile = "cfi.log"; // will break some of Rose's research analysis scripts //LogFile = $psprintf("branch_%s%0d.log", P.BPRED_TYPE, P.BPRED_SIZE); file = $fopen(LogFile, "w"); CFIfile = $fopen(CFILogFile, "w"); end always @(posedge clk) begin if(resetEdge) begin $fwrite(file, "TRAIN\n"); $fwrite(CFIfile, "TRAIN\n"); end if(StartSample) begin $fwrite(file, "BEGIN %s\n", memfilename); $fwrite(CFIfile, "BEGIN %s\n", memfilename); end if(dut.core.ifu.IClassM[0] & ~dut.core.StallW & ~dut.core.FlushW & dut.core.InstrValidM) begin direction = PCSrcM ? "t" : "n"; $fwrite(file, "%h %s\n", dut.core.PCM, direction); end if((|dut.core.ifu.IClassM) & ~dut.core.StallW & ~dut.core.FlushW & dut.core.InstrValidM) begin direction = PCSrcM ? "t" : "n"; $fwrite(CFIfile, "%h %s\n", dut.core.PCM, direction); end if(EndSample) begin $fwrite(file, "END %s\n", memfilename); $fwrite(CFIfile, "END %s\n", memfilename); end end end end endmodule