FetchStage fixups and tests.

This commit is contained in:
John Terrell 2023-03-17 23:28:12 -07:00
parent 4d9aa3063c
commit 5e1448d1d6
Signed by untrusted user who does not match committer: johnt
GPG Key ID: 2E424258DD3731F4
3 changed files with 148 additions and 75 deletions

View File

@ -4,52 +4,46 @@ 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);
// Memory request output // Memory request output
Reg#(Bool) memoryRequestInFlight <- mkReg(False); Reg#(Bool) memoryRequestInFlight <- mkReg(False);
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

View File

@ -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;
$display(">>>PASS"); case(testNumber)
$finish(); // 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 endrule
endmodule endmodule

View File

@ -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