FetchStage fixups and tests.
This commit is contained in:
parent
4d9aa3063c
commit
5e1448d1d6
@ -4,29 +4,21 @@ import PipelineRegisters::*;
|
|||||||
import RV_ISA::*;
|
import RV_ISA::*;
|
||||||
import Trap::*;
|
import Trap::*;
|
||||||
|
|
||||||
|
import Assert::*;
|
||||||
import ClientServer::*;
|
import ClientServer::*;
|
||||||
import FIFOF::*;
|
import FIFOF::*;
|
||||||
import GetPut::*;
|
import GetPut::*;
|
||||||
import Memory::*;
|
import Memory::*;
|
||||||
import RWire::*;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
} FetchStage_Cfg#(numeric type xlen);
|
} FetchStage_Cfg#(numeric type xlen);
|
||||||
|
|
||||||
interface FetchStage_Ifc#(numeric type xlen);
|
interface FetchStage_Ifc#(numeric type xlen);
|
||||||
interface Put#(PC_IF#(xlen)) putPC_IF;
|
method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if);
|
||||||
interface Get#(IF_ID#(xlen)) getIF_ID;
|
|
||||||
|
|
||||||
interface ReadOnlyMemoryClient#(xlen, 32) memoryClient;
|
interface ReadOnlyMemoryClient#(xlen, 32) memoryClient;
|
||||||
interface Get#(Bit#(xlen)) getNPC;
|
|
||||||
interface Put#(Bool) putStall;
|
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen));
|
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) stall <- mkReg(False);
|
||||||
Reg#(Bool) externalStall <- mkReg(False);
|
Reg#(Bool) externalStall <- mkReg(False);
|
||||||
|
|
||||||
@ -35,21 +27,23 @@ module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen));
|
|||||||
Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire;
|
Wire#(ReadOnlyMemoryRequest#(xlen)) memoryRequest <- mkWire;
|
||||||
|
|
||||||
// Memory response input (FIFO)
|
// 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
|
// processMemoryResponse - takes a memory response and returns an IF_ID containing
|
||||||
// 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(ReadOnlyMemoryResponse#(32) response);
|
function IF_ID#(xlen) processMemoryResponse(PC_IF#(xlen) pc_if, ReadOnlyMemoryResponse#(32) response);
|
||||||
IF_ID#(xlen) if_id_ = defaultValue;
|
IF_ID#(xlen) if_id = defaultValue;
|
||||||
if_id_.common.pc = pc_if.common.pc;
|
if_id.common.pc = pc_if.pc;
|
||||||
if_id_.common.isBubble = False;
|
if_id.common.isBubble = False;
|
||||||
|
if_id.npc = pc_if.pc + 4;
|
||||||
|
|
||||||
if (!response.accessFault) begin
|
if (!response.accessFault) begin
|
||||||
if_id_.common.ir = response.data;
|
if_id.common.ir.value = response.data;
|
||||||
if_id_.common.trap = tagged Invalid;
|
if_id.common.trap = tagged Invalid;
|
||||||
end else begin
|
end else begin
|
||||||
Trap#(xlen) trap = Trap {
|
Trap#(xlen) trap = Trap {
|
||||||
cause: exception_INSTRUCTION_ACCESS_FAULT,
|
cause: exception_INSTRUCTION_ACCESS_FAULT,
|
||||||
@ -57,23 +51,18 @@ module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen));
|
|||||||
tval: 0
|
tval: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if_id_.common.trap = tagged Valid(trap);
|
if_id.common.trap = tagged Valid(trap);
|
||||||
end
|
end
|
||||||
return if_id_;
|
return if_id;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
rule fetch;
|
method ActionValue#(IF_ID#(xlen)) step(PC_IF#(xlen) pc_if);
|
||||||
IF_ID#(xlen) if_id_ = defaultValue;
|
IF_ID#(xlen) if_id = defaultValue;
|
||||||
if_id_.common.pc = pc_if.common.pc;
|
if (!pc_if.isBubble) begin
|
||||||
if_id_.common.isBubble = True;
|
|
||||||
|
|
||||||
npc <= pc_if.common.pc + 4;
|
|
||||||
|
|
||||||
if (!pc_if.common.isBubble && !stall) 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 (memoryRequestInFlight) begin
|
||||||
if (memoryResponses.notEmpty) begin
|
if (memoryResponses.notEmpty) begin
|
||||||
if_id_ = processMemoryResponse(memoryResponses.first);
|
if_id = processMemoryResponse(pc_if, memoryResponses.first);
|
||||||
memoryResponses.deq;
|
memoryResponses.deq;
|
||||||
memoryRequestInFlight <= False;
|
memoryRequestInFlight <= False;
|
||||||
end else begin
|
end else begin
|
||||||
@ -83,37 +72,37 @@ module mkFetchStage#(FetchStage_Cfg#(xlen) cfg)(FetchStage_Ifc#(xlen));
|
|||||||
// No memory request is in flight...
|
// No memory request is in flight...
|
||||||
|
|
||||||
// Check for a misaligned request
|
// 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...
|
// 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.trap = tagged Valid(trap);
|
if_id.common.pc = pc_if.pc;
|
||||||
if_id_.common.isBubble = False;
|
if_id.common.trap = tagged Valid(trap);
|
||||||
|
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.common.pc
|
address: pc_if.pc
|
||||||
};
|
};
|
||||||
memoryRequest <= request;
|
memoryRequest <= request;
|
||||||
memoryRequestInFlight <= True;
|
memoryRequestInFlight <= True;
|
||||||
|
|
||||||
if_id_.common.isBubble = True;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Determine if fetching should stall on the next cycle.
|
return if_id;
|
||||||
stall <= externalStall; // || ichache_miss || itlb_busy;
|
endmethod
|
||||||
|
|
||||||
if_id <= if_id_;
|
interface ReadOnlyMemoryClient memoryClient;
|
||||||
endrule
|
interface Get request = toGet(memoryRequest);
|
||||||
|
interface Put response;
|
||||||
interface Put putPC_IF = toPut(asIfc(pc_if));
|
method Action put(ReadOnlyMemoryResponse#(32) response);
|
||||||
interface Get getIF_ID = toGet(if_id);
|
dynamicAssert(memoryResponses.notFull, "FetchStage - attempt to put a memory respnose on a full queue");
|
||||||
interface ReadOnlyMemoryClient memoryClient = toGPClient(memoryRequest, memoryResponses);
|
memoryResponses.enq(response);
|
||||||
interface Get getNPC = toGet(npc);
|
endmethod
|
||||||
interface Put putStall = toPut(asIfc(externalStall));
|
endinterface
|
||||||
|
endinterface
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@ -1,35 +1,107 @@
|
|||||||
import FetchStage::*;
|
import FetchStage::*;
|
||||||
import IsaCfg::*;
|
import IsaCfg::*;
|
||||||
|
import MemoryTypes::*;
|
||||||
import PipelineRegisters::*;
|
import PipelineRegisters::*;
|
||||||
|
import RV_ISA::*;
|
||||||
|
import Trap::*;
|
||||||
|
|
||||||
|
import Assert::*;
|
||||||
|
import ClientServer::*;
|
||||||
import Connectable::*;
|
import Connectable::*;
|
||||||
|
import FIFOF::*;
|
||||||
import GetPut::*;
|
import GetPut::*;
|
||||||
|
|
||||||
module mkTopModule(Empty);
|
module mkTopModule(Empty);
|
||||||
|
Reg#(Bit#(20)) testNumber <- mkReg(0);
|
||||||
|
|
||||||
// 32 bit
|
// 32 bit
|
||||||
FetchStage_Cfg#(32) fetchStageConfig32 = FetchStage_Cfg{};
|
FetchStage_Cfg#(32) fetchStageConfig32 = FetchStage_Cfg{};
|
||||||
FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(fetchStageConfig32);
|
FetchStage_Ifc#(32) fetchStage32 <- mkFetchStage(fetchStageConfig32);
|
||||||
|
|
||||||
Wire#(PC_IF#(32)) pc_if32 <- mkWire;
|
FIFOF#(ReadOnlyMemoryRequest#(32)) memoryRequests32 <- mkUGFIFOF1();
|
||||||
Wire#(IF_ID#(32)) if_id32 <- mkWire;
|
|
||||||
|
|
||||||
mkConnection(toGet(pc_if32), fetchStage32.putPC_IF);
|
mkConnection(fetchStage32.memoryClient.request, toPut(asIfc(memoryRequests32)));
|
||||||
mkConnection(fetchStage32.getIF_ID, toPut(asIfc(if_id32)));
|
|
||||||
|
|
||||||
// 64 bit
|
// 64 bit
|
||||||
FetchStage_Cfg#(64) fetchStageConfig64 = FetchStage_Cfg{};
|
FetchStage_Cfg#(64) fetchStageConfig64 = FetchStage_Cfg{};
|
||||||
FetchStage_Ifc#(64) fetchStage64 <- mkFetchStage(fetchStageConfig64);
|
FetchStage_Ifc#(64) fetchStage64 <- mkFetchStage(fetchStageConfig64);
|
||||||
|
|
||||||
Wire#(PC_IF#(64)) pc_if64 <- mkWire;
|
(* no_implicit_conditions *)
|
||||||
Wire#(IF_ID#(64)) if_id64 <- mkWire;
|
|
||||||
|
|
||||||
mkConnection(toGet(pc_if64), fetchStage64.putPC_IF);
|
|
||||||
mkConnection(fetchStage64.getIF_ID, toPut(asIfc(if_id64)));
|
|
||||||
|
|
||||||
rule test;
|
rule test;
|
||||||
//pc_if32 <= defaultValue;
|
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;
|
||||||
|
|
||||||
|
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");
|
$display(">>>PASS");
|
||||||
$finish();
|
$finish();
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
endrule
|
||||||
|
|
||||||
|
rule increment_test_number;
|
||||||
|
testNumber <= testNumber + 1;
|
||||||
endrule
|
endrule
|
||||||
endmodule
|
endmodule
|
||||||
|
|||||||
@ -1,11 +1,21 @@
|
|||||||
import Trap::*;
|
import Trap::*;
|
||||||
import DefaultValue::*;
|
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
|
// PipelineRegisterCommon
|
||||||
//
|
//
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Bit#(32) ir;
|
Instruction ir;
|
||||||
Bit#(xlen) pc;
|
Bit#(xlen) pc;
|
||||||
Bool isBubble;
|
Bool isBubble;
|
||||||
Maybe#(Trap#(xlen)) trap;
|
Maybe#(Trap#(xlen)) trap;
|
||||||
@ -13,8 +23,8 @@ typedef struct {
|
|||||||
|
|
||||||
instance DefaultValue #(PipelineRegisterCommon#(xlen));
|
instance DefaultValue #(PipelineRegisterCommon#(xlen));
|
||||||
defaultValue = PipelineRegisterCommon {
|
defaultValue = PipelineRegisterCommon {
|
||||||
ir: 32'b0000000_00000_00000_000_00000_0110011, // ADD x0, x0, x0
|
ir: defaultValue,
|
||||||
pc: 'hf000c0de,
|
pc: 'h-1,
|
||||||
isBubble: True,
|
isBubble: True,
|
||||||
trap: tagged Invalid
|
trap: tagged Invalid
|
||||||
};
|
};
|
||||||
@ -24,12 +34,14 @@ endinstance
|
|||||||
// PC_IF
|
// PC_IF
|
||||||
//
|
//
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PipelineRegisterCommon#(xlen) common;
|
Bit#(xlen) pc;
|
||||||
|
Bool isBubble;
|
||||||
} PC_IF#(numeric type xlen) deriving(Bits, Eq, FShow);
|
} PC_IF#(numeric type xlen) deriving(Bits, Eq, FShow);
|
||||||
|
|
||||||
instance DefaultValue #(PC_IF#(xlen));
|
instance DefaultValue #(PC_IF#(xlen));
|
||||||
defaultValue = PC_IF {
|
defaultValue = PC_IF {
|
||||||
common: defaultValue
|
pc: 'h-1,
|
||||||
|
isBubble: True
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
@ -45,8 +57,8 @@ typedef struct {
|
|||||||
instance DefaultValue #(IF_ID#(xlen));
|
instance DefaultValue #(IF_ID#(xlen));
|
||||||
defaultValue = IF_ID {
|
defaultValue = IF_ID {
|
||||||
common: defaultValue,
|
common: defaultValue,
|
||||||
epoch: 0,
|
epoch: 'h-1,
|
||||||
npc: 'hbeefbeef
|
npc: 'h-1
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
@ -67,11 +79,11 @@ instance DefaultValue #(ID_EX#(xlen));
|
|||||||
defaultValue = ID_EX {
|
defaultValue = ID_EX {
|
||||||
common: defaultValue,
|
common: defaultValue,
|
||||||
epoch: 0,
|
epoch: 0,
|
||||||
npc: 'hbeefbeef,
|
npc: 'h-1,
|
||||||
a: 0,
|
a: 'h-1,
|
||||||
b: 0,
|
b: 'h-1,
|
||||||
isBValid: True,
|
isBValid: True,
|
||||||
imm: 0
|
imm: 'h-1
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
@ -88,8 +100,8 @@ typedef struct {
|
|||||||
instance DefaultValue #(EX_MEM#(xlen));
|
instance DefaultValue #(EX_MEM#(xlen));
|
||||||
defaultValue = EX_MEM {
|
defaultValue = EX_MEM {
|
||||||
common: defaultValue,
|
common: defaultValue,
|
||||||
aluOutput: 0,
|
aluOutput: 'h-1,
|
||||||
storeValueOrCSRWriteback: 0,
|
storeValueOrCSRWriteback: 'h-1,
|
||||||
cond: False
|
cond: False
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
@ -106,8 +118,8 @@ typedef struct {
|
|||||||
instance DefaultValue #(MEM_WB#(xlen));
|
instance DefaultValue #(MEM_WB#(xlen));
|
||||||
defaultValue = MEM_WB {
|
defaultValue = MEM_WB {
|
||||||
common: defaultValue,
|
common: defaultValue,
|
||||||
gprWritebackValue: 0,
|
gprWritebackValue: 'h-1,
|
||||||
csrWritebackValue: 0
|
csrWritebackValue: 'h-1
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
|
|
||||||
@ -123,7 +135,7 @@ typedef struct {
|
|||||||
instance DefaultValue#(WB_OUT#(xlen));
|
instance DefaultValue#(WB_OUT#(xlen));
|
||||||
defaultValue = WB_OUT {
|
defaultValue = WB_OUT {
|
||||||
common: defaultValue,
|
common: defaultValue,
|
||||||
gpr_writeback_value: 0,
|
gpr_writeback_value: 'h-1,
|
||||||
csr_writeback_value: 0
|
csr_writeback_value: 'h-1
|
||||||
};
|
};
|
||||||
endinstance
|
endinstance
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user