Small rework of fetch stage.

This commit is contained in:
John Terrell 2023-03-24 07:19:45 -07:00
parent a579b7b813
commit 1fad6db424
Signed by untrusted user who does not match committer: johnt
GPG Key ID: 2E424258DD3731F4
3 changed files with 88 additions and 100 deletions

View File

@ -11,13 +11,17 @@ import GetPut::*;
import Memory::*; import Memory::*;
interface FetchStage_Ifc#(numeric type xlen); 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; interface ReadOnlyMemoryClient#(xlen, 32) memoryClient;
endinterface endinterface
module mkFetchStage#(IsaCfg#(xlen) cfg)(FetchStage_Ifc#(xlen)); 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 // Memory request output
Reg#(Bool) memoryRequestInFlight <- mkReg(False); Reg#(Maybe#(ReadOnlyMemoryRequest#(xlen))) inflightMemoryRequest <- mkReg(tagged Invalid);
Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire; Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire;
// Memory response input (FIFO) // 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 // the encoded instruction (or a trap if the original request
// was denied by the memory system) // 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#(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.common.isBubble = False;
if_id.npc = pc_if.pc + 4; if_id.npc = updatedPc;
if (!response.accessFault) begin if (!response.accessFault) begin
if_id.common.ir.value = response.data; 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 // 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. // 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_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 there's an active request, handle it if it's returned
if (pc_if.pc[1:0] != 0) begin if (isValid(inflightMemoryRequest)) begin
// Address request was misaligned... if (memoryResponses.notEmpty) begin
Trap#(xlen) trap = Trap { if_id = processMemoryResponse(memoryResponses.first);
cause: exception_INSTRUCTION_ADDRESS_MISALIGNED, memoryResponses.deq;
isInterrupt: False, inflightMemoryRequest <= tagged Invalid;
tval: 0 end else begin
}; // Memory request is in flight but hasn't returned yet.
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
end 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 end
return if_id; return if_id;

View File

@ -23,27 +23,18 @@ module mkTopModule(Empty);
FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(rv32); FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(rv32);
FIFOF#(ReadOnlyMemoryRequest#(32)) memoryRequests32 <- mkUGFIFOF1(); FIFOF#(ReadOnlyMemoryRequest#(32)) memoryRequests32 <- mkUGFIFOF1();
mkConnection(fetchStage32.memoryClient.request, toPut(asIfc(memoryRequests32))); mkConnection(fetchStage32.memoryClient.request, toPut(asIfc(memoryRequests32)));
(* no_implicit_conditions *) (* no_implicit_conditions *)
rule test; rule test;
PC_IF#(32) pc_if = defaultValue;
case(testNumber) 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 // Misaligned instruction trap
1: begin 0: begin
pc_if.pc = 'h101; ProgramCounter#(32) pc = 'h101;
pc_if.isBubble = False;
let if_id <- fetchStage32.step(pc_if); let if_id <- fetchStage32.step(pc, tagged Invalid);
dynamicAssert(if_id.common.pc == pc_if.pc, "Fetch - Misaligned instruction trap check - common.pc"); 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.ir == defaultValue, "Fetch - Misaligned instruction trap check - common.ir");
dynamicAssert(!if_id.common.isBubble, "Fetch - Misaligned instruction trap check - common.isBubble"); 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(isValid(if_id.common.trap), "Fetch - Misaligned instruction trap check - contains trap");
@ -51,22 +42,20 @@ module mkTopModule(Empty);
end end
// Memory request denied trap (request submit) // Memory request denied trap (request submit)
2: begin 1: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// The fetch should proceed and return a bubble. // 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"); dynamicAssert(if_id == defaultValue, "Fetch - Memory request denied trap check - request should return a bubble");
end end
// Memory request denied trap (request receipt) // Memory request denied trap (request receipt)
3: begin 2: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// Ensure the fetch returns a bubble while the memory request is in flight // 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(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"); dynamicAssert(memoryRequests32.notEmpty, "Fetch - Memory request denied trap check - memory request queue should not be empty");
@ -81,13 +70,12 @@ module mkTopModule(Empty);
end end
// Memory request denied trap (return trap check) // Memory request denied trap (return trap check)
4: begin 3: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// The fetch should proceed and return a bubble. // 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.common.pc == pc_if.pc, "Fetch - Memory request denied trap check - common.pc"); 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.ir == defaultValue, "Fetch - Memory request denied trap check - common.ir");
dynamicAssert(!if_id.common.isBubble, "Fetch - Memory request denied trap check - common.isBubble"); 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(isValid(if_id.common.trap), "Fetch - Memory request denied trap check - contains trap");
@ -95,22 +83,20 @@ module mkTopModule(Empty);
end end
// Normal memory request (request submit) // Normal memory request (request submit)
5: begin 4: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// The fetch should proceed and return a bubble. // 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"); dynamicAssert(if_id == defaultValue, "Fetch - Normal request - request should return a bubble");
end end
// Normal memory request (request receipt) // Normal memory request (request receipt)
6: begin 5: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// Ensure the fetch returns a bubble while the memory request is in flight // 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(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"); dynamicAssert(memoryRequests32.notEmpty, "Fetch - Normal request - memory request queue should not be empty");
@ -125,19 +111,19 @@ module mkTopModule(Empty);
end end
// Normal memory request (return value check) // Normal memory request (return value check)
7: begin 6: begin
pc_if.pc = 'h100; ProgramCounter#(32) pc = 'h100;
pc_if.isBubble = False;
// The fetch should proceed and return a bubble. // 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.common.pc == pc_if.pc, "Fetch - Normal request - common.pc"); dynamicAssert(if_id.common.pc == pc, "Fetch - Normal request - common.pc");
dynamicAssert(!if_id.common.isBubble, "Fetch - Normal request - common.isBubble"); dynamicAssert(!if_id.common.isBubble, "Fetch - Normal request - common.isBubble");
dynamicAssert(!isValid(if_id.common.trap), "Fetch - Normal request - contains no trap"); 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"); dynamicAssert(if_id.common.ir.value == 'haabb_ccdd, "Fetch - Normal request - contains expected instruction data");
end end
default: begin default: begin
dynamicAssert(testNumber == 7, "Fetch - not all tests run");
$display(">>>PASS"); $display(">>>PASS");
$finish(); $finish();
end end

View File

@ -1,6 +1,9 @@
import Trap::*; import Trap::*;
import DefaultValue::*; import DefaultValue::*;
//
// Instruction
//
typedef struct { typedef struct {
Bit#(32) value; Bit#(32) value;
} Instruction deriving(Bits, Eq, FShow); } Instruction deriving(Bits, Eq, FShow);
@ -11,54 +14,44 @@ instance DefaultValue #(Instruction);
}; };
endinstance endinstance
//
// ProgramCounter
//
typedef Bit#(xlen) ProgramCounter#(numeric type xlen);
// //
// PipelineRegisterCommon // PipelineRegisterCommon
// //
typedef struct { typedef struct {
Instruction ir; Instruction ir;
Bit#(xlen) pc; ProgramCounter#(xlen) pc;
Bool isBubble; Bool isBubble;
Maybe#(Trap#(xlen)) trap; Maybe#(Trap#(xlen)) trap;
} PipelineRegisterCommon#(numeric type xlen) deriving(Bits, Eq, FShow); } PipelineRegisterCommon#(numeric type xlen) deriving(Bits, Eq, FShow);
instance DefaultValue #(PipelineRegisterCommon#(xlen)); instance DefaultValue #(PipelineRegisterCommon#(xlen));
defaultValue = PipelineRegisterCommon { defaultValue = PipelineRegisterCommon {
ir: defaultValue, ir: defaultValue,
pc: 'h-1, pc: defaultValue,
isBubble: True, isBubble: True,
trap: tagged Invalid trap: tagged Invalid
}; };
endinstance 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 // IF_ID
// //
typedef struct { typedef struct {
PipelineRegisterCommon#(xlen) common; PipelineRegisterCommon#(xlen) common;
Bit#(1) epoch; Bit#(1) epoch;
Bit#(xlen) npc; // Next program counter ProgramCounter#(xlen) npc; // Next program counter
} IF_ID#(numeric type xlen) deriving(Bits, Eq, FShow); } IF_ID#(numeric type xlen) deriving(Bits, Eq, FShow);
instance DefaultValue #(IF_ID#(xlen)); instance DefaultValue #(IF_ID#(xlen));
defaultValue = IF_ID { defaultValue = IF_ID {
common: defaultValue, common: defaultValue,
epoch: 'h-1, epoch: 'h-1,
npc: 'h-1 npc: defaultValue
}; };
endinstance endinstance
@ -68,7 +61,7 @@ endinstance
typedef struct { typedef struct {
PipelineRegisterCommon#(xlen) common; PipelineRegisterCommon#(xlen) common;
Bit#(1) epoch; Bit#(1) epoch;
Bit#(xlen) npc; // Next program counter ProgramCounter#(xlen) npc; // Next program counter
Bit#(xlen) a; // Operand 1 Bit#(xlen) a; // Operand 1
Bit#(xlen) b; // Operand 2 Bit#(xlen) b; // Operand 2
Bool isBValid; // In the case of a CSR, this will indicate if 'b' is valid 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 { defaultValue = ID_EX {
common: defaultValue, common: defaultValue,
epoch: 0, epoch: 0,
npc: 'h-1, npc: defaultValue,
a: 'h-1, a: 'h-1,
b: 'h-1, b: 'h-1,
isBValid: True, isBValid: True,