Small rework of fetch stage.
This commit is contained in:
parent
a579b7b813
commit
1fad6db424
@ -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 there's an active request, handle it if it's returned
|
||||||
if (memoryRequestInFlight) begin
|
if (isValid(inflightMemoryRequest)) begin
|
||||||
if (memoryResponses.notEmpty) begin
|
if (memoryResponses.notEmpty) begin
|
||||||
if_id = processMemoryResponse(pc_if, memoryResponses.first);
|
if_id = processMemoryResponse(memoryResponses.first);
|
||||||
memoryResponses.deq;
|
memoryResponses.deq;
|
||||||
memoryRequestInFlight <= False;
|
inflightMemoryRequest <= tagged Invalid;
|
||||||
end else begin
|
end else begin
|
||||||
// Memory request is in flight but hasn't returned yet.
|
// Memory request is in flight but hasn't returned yet.
|
||||||
end
|
end
|
||||||
end else begin
|
end else begin
|
||||||
// No memory request is in flight...
|
// 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
|
// Check for a misaligned request
|
||||||
if (pc_if.pc[1:0] != 0) begin
|
if (updatedPc_[1:0] != 0) begin
|
||||||
// Address request was misaligned...
|
// Address request was misaligned...
|
||||||
Trap#(xlen) trap = Trap {
|
Trap#(xlen) trap = Trap {
|
||||||
cause: exception_INSTRUCTION_ADDRESS_MISALIGNED,
|
cause: exception_INSTRUCTION_ADDRESS_MISALIGNED,
|
||||||
isInterrupt: False,
|
isInterrupt: False,
|
||||||
tval: 0
|
tval: 0
|
||||||
};
|
};
|
||||||
if_id.common.pc = pc_if.pc;
|
if_id.common.pc = updatedPc_;
|
||||||
if_id.common.trap = tagged Valid(trap);
|
if_id.common.trap = tagged Valid(trap);
|
||||||
if_id.common.isBubble = False;
|
if_id.common.isBubble = False;
|
||||||
end else begin
|
end else begin
|
||||||
// Construct a memory request and send it out.
|
// Construct a memory request and send it out.
|
||||||
ReadOnlyMemoryRequest#(xlen) request = ReadOnlyMemoryRequest {
|
ReadOnlyMemoryRequest#(xlen) request = ReadOnlyMemoryRequest {
|
||||||
address: pc_if.pc
|
address: updatedPc_
|
||||||
};
|
};
|
||||||
memoryRequest <= request;
|
memoryRequest <= request;
|
||||||
memoryRequestInFlight <= True;
|
inflightMemoryRequest <= tagged Valid request;
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
updatedPc <= updatedPc_;
|
||||||
end
|
end
|
||||||
|
|
||||||
return if_id;
|
return if_id;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,12 +14,17 @@ 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);
|
||||||
@ -24,41 +32,26 @@ typedef struct {
|
|||||||
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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user