From 1fad6db424ea0e2feeaa64f3102570df75616911 Mon Sep 17 00:00:00 2001 From: John Terrell Date: Fri, 24 Mar 2023 07:19:45 -0700 Subject: [PATCH] Small rework of fetch stage. --- src/Cpu/FetchStage.bsv | 83 +++++++++++++++++++---------------- src/Cpu/FetchStage_tb.bsv | 64 +++++++++++---------------- src/Cpu/PipelineRegisters.bsv | 41 +++++++---------- 3 files changed, 88 insertions(+), 100 deletions(-) diff --git a/src/Cpu/FetchStage.bsv b/src/Cpu/FetchStage.bsv index 33c78cc..0b89be1 100644 --- a/src/Cpu/FetchStage.bsv +++ b/src/Cpu/FetchStage.bsv @@ -11,13 +11,17 @@ import GetPut::*; import Memory::*; interface FetchStage_Ifc#(numeric type xlen); - method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if); + method ActionValue#(IF_ID#(xlen)) step(ProgramCounter#(xlen) pc, Maybe#(ProgramCounter#(xlen)) redirection); + interface ReadOnlyMemoryClient#(xlen, 32) memoryClient; endinterface module mkFetchStage#(IsaCfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); + Reg#(ProgramCounter#(xlen)) updatedPc <- mkRegU; // Holds the updated PC that is calculated when a fetch is issued + // that is used to update the PC when the fetch completes. + // Memory request output - Reg#(Bool) memoryRequestInFlight <- mkReg(False); + Reg#(Maybe#(ReadOnlyMemoryRequest#(xlen))) inflightMemoryRequest <- mkReg(tagged Invalid); Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire; // Memory response input (FIFO) @@ -29,11 +33,11 @@ module mkFetchStage#(IsaCfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); // the encoded instruction (or a trap if the original request // was denied by the memory system) // - function IF_ID#(xlen) processMemoryResponse(PC_IF#(xlen) pc_if, ReadOnlyMemoryResponse#(32) response); + function IF_ID#(xlen) processMemoryResponse(ReadOnlyMemoryResponse#(32) response); IF_ID#(xlen) if_id = defaultValue; - if_id.common.pc = pc_if.pc; + if_id.common.pc = inflightMemoryRequest.Valid.address; if_id.common.isBubble = False; - if_id.npc = pc_if.pc + 4; + if_id.npc = updatedPc; if (!response.accessFault) begin if_id.common.ir.value = response.data; @@ -54,41 +58,46 @@ module mkFetchStage#(IsaCfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); // step - Execute fetch step by sending an instruction memory request if one isn't already in flight and // responding to any responses that have returned. If a request is in flight, a bubble is returned. // - method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if); + method ActionValue#(IF_ID#(xlen)) step(ProgramCounter#(xlen) pc, Maybe#(ProgramCounter#(xlen)) redirection); 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(pc_if, memoryResponses.first); - memoryResponses.deq; - memoryRequestInFlight <= False; - end else begin - // Memory request is in flight but hasn't returned yet. - end - end else begin - // No memory request is in flight... - // Check for a misaligned request - 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.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.pc - }; - memoryRequest <= request; - memoryRequestInFlight <= True; - end + // If there's an active request, handle it if it's returned + if (isValid(inflightMemoryRequest)) begin + if (memoryResponses.notEmpty) begin + if_id = processMemoryResponse(memoryResponses.first); + memoryResponses.deq; + inflightMemoryRequest <= tagged Invalid; + end else begin + // Memory request is in flight but hasn't returned yet. end + end else begin + // No memory request is in flight... + + // Determine the PC the instruction will be fetchted from. It's either the calculated PC + // or a redirected PC (due to a branch, jump, exception, etc) + ProgramCounter#(xlen) updatedPc_ = fromMaybe(pc, redirection); + + // Check for a misaligned request + if (updatedPc_[1:0] != 0) begin + // Address request was misaligned... + Trap#(xlen) trap = Trap { + cause: exception_INSTRUCTION_ADDRESS_MISALIGNED, + isInterrupt: False, + tval: 0 + }; + if_id.common.pc = updatedPc_; + 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: updatedPc_ + }; + memoryRequest <= request; + inflightMemoryRequest <= tagged Valid request; + end + + updatedPc <= updatedPc_; end return if_id; diff --git a/src/Cpu/FetchStage_tb.bsv b/src/Cpu/FetchStage_tb.bsv index e9c3b98..3f30676 100644 --- a/src/Cpu/FetchStage_tb.bsv +++ b/src/Cpu/FetchStage_tb.bsv @@ -23,27 +23,18 @@ module mkTopModule(Empty); FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(rv32); FIFOF#(ReadOnlyMemoryRequest#(32)) memoryRequests32 <- mkUGFIFOF1(); - mkConnection(fetchStage32.memoryClient.request, toPut(asIfc(memoryRequests32))); (* no_implicit_conditions *) rule test; - PC_IF#(32) pc_if = defaultValue; 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; + 0: begin + ProgramCounter#(32) pc = 'h101; - let if_id <- fetchStage32.step(pc_if); - dynamicAssert(if_id.common.pc == pc_if.pc, "Fetch - Misaligned instruction trap check - common.pc"); + let if_id <- fetchStage32.step(pc, tagged Invalid); + dynamicAssert(if_id.common.pc == 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"); @@ -51,22 +42,20 @@ module mkTopModule(Empty); end // Memory request denied trap (request submit) - 2: begin - pc_if.pc = 'h100; - pc_if.isBubble = False; + 1: begin + ProgramCounter#(32) pc = 'h100; // The fetch should proceed and return a bubble. - let if_id <- fetchStage32.step(pc_if); + let if_id <- fetchStage32.step(pc, tagged Invalid); 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; + 2: begin + ProgramCounter#(32) pc = 'h100; // Ensure the fetch returns a bubble while the memory request is in flight - let if_id <- fetchStage32.step(pc_if); + let if_id <- fetchStage32.step(pc, tagged Invalid); 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"); @@ -81,13 +70,12 @@ module mkTopModule(Empty); end // Memory request denied trap (return trap check) - 4: begin - pc_if.pc = 'h100; - pc_if.isBubble = False; + 3: begin + ProgramCounter#(32) pc = 'h100; // 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"); + let if_id <- fetchStage32.step(pc, tagged Invalid); + dynamicAssert(if_id.common.pc == 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"); @@ -95,22 +83,20 @@ module mkTopModule(Empty); end // Normal memory request (request submit) - 5: begin - pc_if.pc = 'h100; - pc_if.isBubble = False; + 4: begin + ProgramCounter#(32) pc = 'h100; // The fetch should proceed and return a bubble. - let if_id <- fetchStage32.step(pc_if); + let if_id <- fetchStage32.step(pc, tagged Invalid); dynamicAssert(if_id == defaultValue, "Fetch - Normal request - request should return a bubble"); end // Normal memory request (request receipt) - 6: begin - pc_if.pc = 'h100; - pc_if.isBubble = False; + 5: begin + ProgramCounter#(32) pc = 'h100; // Ensure the fetch returns a bubble while the memory request is in flight - let if_id <- fetchStage32.step(pc_if); + let if_id <- fetchStage32.step(pc, tagged Invalid); dynamicAssert(if_id == defaultValue, "Fetch - Normal request - request should return a bubble while fetch is in flight"); dynamicAssert(memoryRequests32.notEmpty, "Fetch - Normal request - memory request queue should not be empty"); @@ -125,19 +111,19 @@ module mkTopModule(Empty); end // Normal memory request (return value check) - 7: begin - pc_if.pc = 'h100; - pc_if.isBubble = False; + 6: begin + ProgramCounter#(32) pc = 'h100; // The fetch should proceed and return a bubble. - let if_id <- fetchStage32.step(pc_if); - dynamicAssert(if_id.common.pc == pc_if.pc, "Fetch - Normal request - common.pc"); + let if_id <- fetchStage32.step(pc, tagged Invalid); + dynamicAssert(if_id.common.pc == pc, "Fetch - Normal request - common.pc"); dynamicAssert(!if_id.common.isBubble, "Fetch - Normal request - common.isBubble"); dynamicAssert(!isValid(if_id.common.trap), "Fetch - Normal request - contains no trap"); dynamicAssert(if_id.common.ir.value == 'haabb_ccdd, "Fetch - Normal request - contains expected instruction data"); end default: begin + dynamicAssert(testNumber == 7, "Fetch - not all tests run"); $display(">>>PASS"); $finish(); end diff --git a/src/Cpu/PipelineRegisters.bsv b/src/Cpu/PipelineRegisters.bsv index ab97f46..60cba39 100644 --- a/src/Cpu/PipelineRegisters.bsv +++ b/src/Cpu/PipelineRegisters.bsv @@ -1,6 +1,9 @@ import Trap::*; import DefaultValue::*; +// +// Instruction +// typedef struct { Bit#(32) value; } Instruction deriving(Bits, Eq, FShow); @@ -11,54 +14,44 @@ instance DefaultValue #(Instruction); }; endinstance +// +// ProgramCounter +// +typedef Bit#(xlen) ProgramCounter#(numeric type xlen); + // // PipelineRegisterCommon // typedef struct { - Instruction ir; - Bit#(xlen) pc; - Bool isBubble; - Maybe#(Trap#(xlen)) trap; + Instruction ir; + ProgramCounter#(xlen) pc; + Bool isBubble; + Maybe#(Trap#(xlen)) trap; } PipelineRegisterCommon#(numeric type xlen) deriving(Bits, Eq, FShow); instance DefaultValue #(PipelineRegisterCommon#(xlen)); defaultValue = PipelineRegisterCommon { ir: defaultValue, - pc: 'h-1, + pc: defaultValue, isBubble: True, trap: tagged Invalid }; endinstance -// -// PC_IF -// -typedef struct { - Bit#(xlen) pc; - Bool isBubble; -} PC_IF#(numeric type xlen) deriving(Bits, Eq, FShow); - -instance DefaultValue #(PC_IF#(xlen)); - defaultValue = PC_IF { - pc: 'h-1, - isBubble: True - }; -endinstance - // // IF_ID // typedef struct { PipelineRegisterCommon#(xlen) common; Bit#(1) epoch; - Bit#(xlen) npc; // Next program counter + ProgramCounter#(xlen) npc; // Next program counter } IF_ID#(numeric type xlen) deriving(Bits, Eq, FShow); instance DefaultValue #(IF_ID#(xlen)); defaultValue = IF_ID { common: defaultValue, epoch: 'h-1, - npc: 'h-1 + npc: defaultValue }; endinstance @@ -68,7 +61,7 @@ endinstance typedef struct { PipelineRegisterCommon#(xlen) common; Bit#(1) epoch; - Bit#(xlen) npc; // Next program counter + ProgramCounter#(xlen) npc; // Next program counter Bit#(xlen) a; // Operand 1 Bit#(xlen) b; // Operand 2 Bool isBValid; // In the case of a CSR, this will indicate if 'b' is valid @@ -79,7 +72,7 @@ instance DefaultValue #(ID_EX#(xlen)); defaultValue = ID_EX { common: defaultValue, epoch: 0, - npc: 'h-1, + npc: defaultValue, a: 'h-1, b: 'h-1, isBValid: True,