From 5e1448d1d64c6db822dafc6ecb5a4c69f040d5fe Mon Sep 17 00:00:00 2001 From: John Terrell Date: Fri, 17 Mar 2023 23:28:12 -0700 Subject: [PATCH] FetchStage fixups and tests. --- src/Cpu/FetchStage.bsv | 79 ++++++++++++---------------- src/Cpu/FetchStage_tb.bsv | 98 ++++++++++++++++++++++++++++++----- src/Cpu/PipelineRegisters.bsv | 46 ++++++++++------ 3 files changed, 148 insertions(+), 75 deletions(-) diff --git a/src/Cpu/FetchStage.bsv b/src/Cpu/FetchStage.bsv index d569feb..6334737 100644 --- a/src/Cpu/FetchStage.bsv +++ b/src/Cpu/FetchStage.bsv @@ -4,52 +4,46 @@ import PipelineRegisters::*; import RV_ISA::*; import Trap::*; +import Assert::*; import ClientServer::*; import FIFOF::*; import GetPut::*; import Memory::*; -import RWire::*; typedef struct { } FetchStage_Cfg#(numeric type xlen); interface FetchStage_Ifc#(numeric type xlen); - interface Put#(PC_IF#(xlen)) putPC_IF; - interface Get#(IF_ID#(xlen)) getIF_ID; - + method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if); interface ReadOnlyMemoryClient#(xlen, 32) memoryClient; - interface Get#(Bit#(xlen)) getNPC; - interface Put#(Bool) putStall; endinterface module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); - Wire#(PC_IF#(xlen)) pc_if <- mkWire; - Wire#(IF_ID#(xlen)) if_id <- mkWire; - Wire#(Bit#(xlen)) npc <- mkWire; - Reg#(Bool) stall <- mkReg(False); Reg#(Bool) externalStall <- mkReg(False); // Memory request output - Reg#(Bool) memoryRequestInFlight <- mkReg(False); + Reg#(Bool) memoryRequestInFlight <- mkReg(False); Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire; // Memory response input (FIFO) - FIFOF#(ReadOnlyMemoryResponse#(32)) memoryResponses <- mkFIFOF; + // Note: This is an unguarded FIFO so status much be checked before attempting to enq() and deq(). + FIFOF#(ReadOnlyMemoryResponse#(32)) memoryResponses <- mkUGFIFOF1; // // processMemoryResponse - takes a memory response and returns an IF_ID containing // the encoded instruction (or a trap if the original request // was denied by the memory system) // - function IF_ID#(xlen) processMemoryResponse(ReadOnlyMemoryResponse#(32) response); - IF_ID#(xlen) if_id_ = defaultValue; - if_id_.common.pc = pc_if.common.pc; - if_id_.common.isBubble = False; + function IF_ID#(xlen) processMemoryResponse(PC_IF#(xlen) pc_if, ReadOnlyMemoryResponse#(32) response); + IF_ID#(xlen) if_id = defaultValue; + if_id.common.pc = pc_if.pc; + if_id.common.isBubble = False; + if_id.npc = pc_if.pc + 4; if (!response.accessFault) begin - if_id_.common.ir = response.data; - if_id_.common.trap = tagged Invalid; + if_id.common.ir.value = response.data; + if_id.common.trap = tagged Invalid; end else begin Trap#(xlen) trap = Trap { cause: exception_INSTRUCTION_ACCESS_FAULT, @@ -57,23 +51,18 @@ module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); tval: 0 }; - if_id_.common.trap = tagged Valid(trap); + if_id.common.trap = tagged Valid(trap); end - return if_id_; + return if_id; endfunction - rule fetch; - IF_ID#(xlen) if_id_ = defaultValue; - if_id_.common.pc = pc_if.common.pc; - if_id_.common.isBubble = True; - - npc <= pc_if.common.pc + 4; - - if (!pc_if.common.isBubble && !stall) begin + method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if); + IF_ID#(xlen) if_id = defaultValue; + if (!pc_if.isBubble) begin // If there's an active request, handle it if it's returned if (memoryRequestInFlight) begin if (memoryResponses.notEmpty) begin - if_id_ = processMemoryResponse(memoryResponses.first); + if_id = processMemoryResponse(pc_if, memoryResponses.first); memoryResponses.deq; memoryRequestInFlight <= False; end else begin @@ -83,37 +72,37 @@ module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); // No memory request is in flight... // Check for a misaligned request - if (pc_if.common.pc[1:0] != 0) begin + if (pc_if.pc[1:0] != 0) begin // Address request was misaligned... Trap#(xlen) trap = Trap { cause: exception_INSTRUCTION_ADDRESS_MISALIGNED, isInterrupt: False, tval: 0 }; - if_id_.common.trap = tagged Valid(trap); - if_id_.common.isBubble = False; + if_id.common.pc = pc_if.pc; + if_id.common.trap = tagged Valid(trap); + if_id.common.isBubble = False; end else begin // Construct a memory request and send it out. ReadOnlyMemoryRequest#(xlen) request = ReadOnlyMemoryRequest { - address: pc_if.common.pc + address: pc_if.pc }; memoryRequest <= request; memoryRequestInFlight <= True; - - if_id_.common.isBubble = True; end end end - // Determine if fetching should stall on the next cycle. - stall <= externalStall; // || ichache_miss || itlb_busy; + return if_id; + endmethod - if_id <= if_id_; - endrule - - interface Put putPC_IF = toPut(asIfc(pc_if)); - interface Get getIF_ID = toGet(if_id); - interface ReadOnlyMemoryClient memoryClient = toGPClient(memoryRequest, memoryResponses); - interface Get getNPC = toGet(npc); - interface Put putStall = toPut(asIfc(externalStall)); + interface ReadOnlyMemoryClient memoryClient; + interface Get request = toGet(memoryRequest); + interface Put response; + method Action put(ReadOnlyMemoryResponse#(32) response); + dynamicAssert(memoryResponses.notFull, "FetchStage - attempt to put a memory respnose on a full queue"); + memoryResponses.enq(response); + endmethod + endinterface + endinterface endmodule diff --git a/src/Cpu/FetchStage_tb.bsv b/src/Cpu/FetchStage_tb.bsv index d6d5c77..47f0c51 100644 --- a/src/Cpu/FetchStage_tb.bsv +++ b/src/Cpu/FetchStage_tb.bsv @@ -1,35 +1,107 @@ import FetchStage::*; import IsaCfg::*; +import MemoryTypes::*; import PipelineRegisters::*; +import RV_ISA::*; +import Trap::*; +import Assert::*; +import ClientServer::*; import Connectable::*; +import FIFOF::*; import GetPut::*; module mkTopModule(Empty); + Reg#(Bit#(20)) testNumber <- mkReg(0); + // 32 bit FetchStage_Cfg#(32) fetchStageConfig32 = FetchStage_Cfg{}; FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(fetchStageConfig32); - Wire#(PC_IF#(32)) pc_if32 <- mkWire; - Wire#(IF_ID#(32)) if_id32 <- mkWire; + FIFOF#(ReadOnlyMemoryRequest#(32)) memoryRequests32 <- mkUGFIFOF1(); - mkConnection(toGet(pc_if32), fetchStage32.putPC_IF); - mkConnection(fetchStage32.getIF_ID, toPut(asIfc(if_id32))); + mkConnection(fetchStage32.memoryClient.request, toPut(asIfc(memoryRequests32))); // 64 bit FetchStage_Cfg#(64) fetchStageConfig64 = FetchStage_Cfg{}; FetchStage_Ifc#(64) fetchStage64 <- mkFetchStage(fetchStageConfig64); - Wire#(PC_IF#(64)) pc_if64 <- mkWire; - Wire#(IF_ID#(64)) if_id64 <- mkWire; - - mkConnection(toGet(pc_if64), fetchStage64.putPC_IF); - mkConnection(fetchStage64.getIF_ID, toPut(asIfc(if_id64))); - + (* no_implicit_conditions *) rule test; - //pc_if32 <= defaultValue; + PC_IF#(32) pc_if = defaultValue; - $display(">>>PASS"); - $finish(); + case(testNumber) + // Simple bubble passthrough + 0: begin + let if_id <- fetchStage32.step(pc_if); + dynamicAssert(if_id == defaultValue, "Fetch - Bubble passthrough check"); + end + + // Misaligned instruction trap + 1: begin + pc_if.pc = 'h101; + pc_if.isBubble = False; + + let if_id <- fetchStage32.step(pc_if); + dynamicAssert(if_id.common.pc == pc_if.pc, "Fetch - Misaligned instruction trap check - common.pc"); + dynamicAssert(if_id.common.ir == defaultValue, "Fetch - Misaligned instruction trap check - common.ir"); + dynamicAssert(!if_id.common.isBubble, "Fetch - Misaligned instruction trap check - common.isBubble"); + dynamicAssert(isValid(if_id.common.trap), "Fetch - Misaligned instruction trap check - contains trap"); + dynamicAssert(if_id.common.trap.Valid.cause == exception_INSTRUCTION_ADDRESS_MISALIGNED, "Fetch - Misaligned instruction trap check - cause is misaligned address"); + end + + // Memory request denied trap (request submit) + 2: begin + pc_if.pc = 'h100; + pc_if.isBubble = False; + + // The fetch should proceed and return a bubble. + let if_id <- fetchStage32.step(pc_if); + dynamicAssert(if_id == defaultValue, "Fetch - Memory request denied trap check - request should return a bubble"); + end + + // Memory request denied trap (request receipt) + 3: begin + pc_if.pc = 'h100; + pc_if.isBubble = False; + + // The fetch return a bubble while the memory request is in flight + let if_id <- fetchStage32.step(pc_if); + dynamicAssert(if_id == defaultValue, "Fetch - Memory request denied trap check - request should return a bubble while fetch is in flight"); + + dynamicAssert(memoryRequests32.notEmpty, "Fetch - Memory request denied trap check - memory request queue should not be empty"); + let memoryRequest = memoryRequests32.first; + memoryRequests32.deq; + + dynamicAssert(memoryRequest.address == 'h100, "Fetch - Memory request denied trap check - memory request should have correct address"); + fetchStage32.memoryClient.response.put(ReadOnlyMemoryResponse { + data: 'h-1, + accessFault: True + }); + end + + // Memory request denied trap (return trap check) + 4: begin + pc_if.pc = 'h100; + pc_if.isBubble = False; + + // The fetch should proceed and return a bubble. + let if_id <- fetchStage32.step(pc_if); + dynamicAssert(if_id.common.pc == pc_if.pc, "Fetch - Memory request denied trap check - common.pc"); + dynamicAssert(if_id.common.ir == defaultValue, "Fetch - Memory request denied trap check - common.ir"); + dynamicAssert(!if_id.common.isBubble, "Fetch - Memory request denied trap check - common.isBubble"); + dynamicAssert(isValid(if_id.common.trap), "Fetch - Memory request denied trap check - contains trap"); + dynamicAssert(if_id.common.trap.Valid.cause == exception_INSTRUCTION_ACCESS_FAULT, "Memory request denied trap check - cause is access fault"); + end + + default: begin + $display(">>>PASS"); + $finish(); + end + endcase + endrule + + rule increment_test_number; + testNumber <= testNumber + 1; endrule endmodule diff --git a/src/Cpu/PipelineRegisters.bsv b/src/Cpu/PipelineRegisters.bsv index d65a7ef..1cb65ea 100644 --- a/src/Cpu/PipelineRegisters.bsv +++ b/src/Cpu/PipelineRegisters.bsv @@ -1,11 +1,21 @@ import Trap::*; import DefaultValue::*; +typedef struct { + Bit#(32) value; +} Instruction deriving(Bits, Eq, FShow); + +instance DefaultValue #(Instruction); + defaultValue = Instruction { + value: 32'b0000000_00000_00000_000_00000_0110011 // ADD x0, x0, x0 + }; +endinstance + // // PipelineRegisterCommon // typedef struct { - Bit#(32) ir; + Instruction ir; Bit#(xlen) pc; Bool isBubble; Maybe#(Trap#(xlen)) trap; @@ -13,8 +23,8 @@ typedef struct { instance DefaultValue #(PipelineRegisterCommon#(xlen)); defaultValue = PipelineRegisterCommon { - ir: 32'b0000000_00000_00000_000_00000_0110011, // ADD x0, x0, x0 - pc: 'hf000c0de, + ir: defaultValue, + pc: 'h-1, isBubble: True, trap: tagged Invalid }; @@ -24,12 +34,14 @@ endinstance // PC_IF // typedef struct { - PipelineRegisterCommon#(xlen) common; + Bit#(xlen) pc; + Bool isBubble; } PC_IF#(numeric type xlen) deriving(Bits, Eq, FShow); instance DefaultValue #(PC_IF#(xlen)); defaultValue = PC_IF { - common: defaultValue + pc: 'h-1, + isBubble: True }; endinstance @@ -45,8 +57,8 @@ typedef struct { instance DefaultValue #(IF_ID#(xlen)); defaultValue = IF_ID { common: defaultValue, - epoch: 0, - npc: 'hbeefbeef + epoch: 'h-1, + npc: 'h-1 }; endinstance @@ -67,11 +79,11 @@ instance DefaultValue #(ID_EX#(xlen)); defaultValue = ID_EX { common: defaultValue, epoch: 0, - npc: 'hbeefbeef, - a: 0, - b: 0, + npc: 'h-1, + a: 'h-1, + b: 'h-1, isBValid: True, - imm: 0 + imm: 'h-1 }; endinstance @@ -88,8 +100,8 @@ typedef struct { instance DefaultValue #(EX_MEM#(xlen)); defaultValue = EX_MEM { common: defaultValue, - aluOutput: 0, - storeValueOrCSRWriteback: 0, + aluOutput: 'h-1, + storeValueOrCSRWriteback: 'h-1, cond: False }; endinstance @@ -106,8 +118,8 @@ typedef struct { instance DefaultValue #(MEM_WB#(xlen)); defaultValue = MEM_WB { common: defaultValue, - gprWritebackValue: 0, - csrWritebackValue: 0 + gprWritebackValue: 'h-1, + csrWritebackValue: 'h-1 }; endinstance @@ -123,7 +135,7 @@ typedef struct { instance DefaultValue#(WB_OUT#(xlen)); defaultValue = WB_OUT { common: defaultValue, - gpr_writeback_value: 0, - csr_writeback_value: 0 + gpr_writeback_value: 'h-1, + csr_writeback_value: 'h-1 }; endinstance