Finish finite state machines for page table walker

This commit is contained in:
Thomas Fleming 2021-03-25 02:48:40 -04:00
parent 7367052e76
commit e3900bd0fa
4 changed files with 270 additions and 75 deletions

View File

@ -48,7 +48,7 @@ module ahblite (
input logic [1:0] MemSizeM,
// Signals from MMU
input logic [`XLEN-1:0] MMUPAdr,
input logic MMUTranslate,
input logic MMUTranslate, MMUTranslationComplete,
output logic [`XLEN-1:0] MMUReadPTE,
output logic MMUReady,
// Return from bus
@ -75,7 +75,8 @@ module ahblite (
);
logic GrantData;
logic [2:0] ISize;
logic [31:0] AccessAddress;
logic [2:0] AccessSize, PTESize, ISize;
logic [`AHBW-1:0] HRDATAMasked, ReadDataM, ReadDataNewW, ReadDataOldW, WriteData;
logic IReady, DReady;
logic CaptureDataM;
@ -89,47 +90,69 @@ module ahblite (
// Data accesses have priority over instructions. However, if a data access comes
// while an instruction read is occuring, the instruction read finishes before
// the data access can take place.
typedef enum {IDLE, MEMREAD, MEMWRITE, INSTRREAD, INSTRREADC, ATOMICREAD, ATOMICWRITE} statetype;
typedef enum {IDLE, MEMREAD, MEMWRITE, INSTRREAD, INSTRREADC, ATOMICREAD, ATOMICWRITE, MMUTRANSLATE, MMUIDLE} statetype;
statetype BusState, NextBusState;
flopenl #(.TYPE(statetype)) busreg(HCLK, ~HRESETn, 1'b1, NextBusState, IDLE, BusState);
always_comb
case (BusState)
IDLE: if (AtomicM[1]) NextBusState = ATOMICREAD;
else if (MemReadM) NextBusState = MEMREAD; // Memory has pirority over instructions
else if (MemWriteM) NextBusState = MEMWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
ATOMICREAD: if (~HREADY) NextBusState = ATOMICREAD;
else NextBusState = ATOMICWRITE;
ATOMICWRITE: if (~HREADY) NextBusState = ATOMICWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
MEMREAD: if (~HREADY) NextBusState = MEMREAD;
else if (InstrReadF) NextBusState = INSTRREADC;
else NextBusState = IDLE;
MEMWRITE: if (~HREADY) NextBusState = MEMWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
IDLE: if (MMUTranslate) NextBusState = MMUTRANSLATE;
else if (AtomicM[1]) NextBusState = ATOMICREAD;
else if (MemReadM) NextBusState = MEMREAD; // Memory has priority over instructions
else if (MemWriteM) NextBusState = MEMWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
MMUTRANSLATE: if (~HREADY) NextBusState = MMUTRANSLATE;
else NextBusState = MMUIDLE;
// *** Could the MMUIDLE state just be the normal idle state?
// Do we trust MMUTranslate to be high exactly when we need translation?
MMUIDLE: if (~MMUTranslationComplete)
NextBusState = MMUTRANSLATE;
else if (AtomicM[1]) NextBusState = ATOMICREAD;
else if (MemReadM) NextBusState = MEMREAD; // Memory has priority over instructions
else if (MemWriteM) NextBusState = MEMWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
ATOMICREAD: if (~HREADY) NextBusState = ATOMICREAD;
else NextBusState = ATOMICWRITE;
ATOMICWRITE: if (~HREADY) NextBusState = ATOMICWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
MEMREAD: if (~HREADY) NextBusState = MEMREAD;
else if (InstrReadF) NextBusState = INSTRREADC;
else NextBusState = IDLE;
MEMWRITE: if (~HREADY) NextBusState = MEMWRITE;
else if (InstrReadF) NextBusState = INSTRREAD;
else NextBusState = IDLE;
INSTRREAD:
if (~HREADY) NextBusState = INSTRREAD;
else NextBusState = IDLE; // if (InstrReadF still high)
INSTRREADC: if (~HREADY) NextBusState = INSTRREADC; // "C" for "competing", meaning please don't mess up the memread in the W stage.
else NextBusState = IDLE;
if (~HREADY) NextBusState = INSTRREAD;
else NextBusState = IDLE; // if (InstrReadF still high)
INSTRREADC: if (~HREADY) NextBusState = INSTRREADC; // "C" for "competing", meaning please don't mess up the memread in the W stage.
else NextBusState = IDLE;
endcase
// stall signals
assign #2 DataStall = (NextBusState == MEMREAD) || (NextBusState == MEMWRITE) ||
(NextBusState == ATOMICREAD) || (NextBusState == ATOMICWRITE);
assign #1 InstrStall = (NextBusState == INSTRREAD) || (NextBusState == INSTRREADC);
(NextBusState == ATOMICREAD) || (NextBusState == ATOMICWRITE) ||
(NextBusState == MMUTRANSLATE) || (NextBusState == MMUIDLE);
// *** Could get finer grained stalling if we distinguish between MMU
// instruction address translation and data address translation
assign #1 InstrStall = (NextBusState == INSTRREAD) || (NextBusState == INSTRREADC) ||
(NextBusState == MMUTRANSLATE) || (NextBusState == MMUIDLE);
// bus outputs
assign #1 GrantData = (NextBusState == MEMREAD) || (NextBusState == MEMWRITE) ||
(NextBusState == ATOMICREAD) || (NextBusState == ATOMICWRITE);
assign #1 HADDR = (GrantData) ? MemPAdrM[31:0] : InstrPAdrF[31:0];
(NextBusState == ATOMICREAD) || (NextBusState == ATOMICWRITE);
assign #1 AccessAddress = (GrantData) ? MemPAdrM[31:0] : InstrPAdrF[31:0];
assign #1 HADDR = (MMUTranslate) ? MMUPAdr[31:0] : AccessAddress;
generate
if (`XLEN == 32) assign PTESize = 3'b010; // in rv32, PTEs are 4 bytes
else assign PTESize = 3'b011; // in rv64, PTEs are 8 bytes
endgenerate
assign ISize = 3'b010; // 32 bit instructions for now; later improve for filling cache with full width; ignored on reads anyway
assign #1 HSIZE = GrantData ? {1'b0, MemSizeM} : ISize;
assign #1 AccessSize = (GrantData) ? {1'b0, MemSizeM} : ISize;
assign #1 HSIZE = (MMUTranslate) ? PTESize : AccessSize;
assign HBURST = 3'b000; // Single burst only supported; consider generalizing for cache fillsfH
assign HPROT = 4'b0011; // not used; see Section 3.7
assign HTRANS = (NextBusState != IDLE) ? 2'b10 : 2'b00; // NONSEQ if reading or writing, IDLE otherwise
@ -145,7 +168,10 @@ module ahblite (
// Route signals to Instruction and Data Caches
// *** assumes AHBW = XLEN
assign #1 MMUReady = (NextBusState == MMUIDLE);
assign InstrRData = HRDATA;
assign MMUReadPTE = HRDATA;
assign ReadDataM = HRDATAMasked; // changed from W to M dh 2/7/2021
assign CaptureDataM = ((BusState == MEMREAD) && (NextBusState != MEMREAD)) ||
((BusState == ATOMICREAD) && (NextBusState == ATOMICWRITE));

View File

@ -32,25 +32,29 @@ module pagetablewalker (
input logic [`XLEN-1:0] SATP_REGW,
input logic MemWriteM,
input logic ITLBMissF, DTLBMissM,
input logic [`XLEN-1:0] PCF, MemAdrM,
output logic [`XLEN-1:0] PageTableEntryF, PageTableEntryM,
output logic ITLBWriteF, DTLBWriteM,
// *** handshake to tlbs probably not needed, since stalls take effect
// output logic TranslationComplete
output logic MMUTranslationComplete,
// Signals from and to ahblite
input logic [`XLEN-1:0] MMUReadPTE,
input logic MMUReady,
output logic [`XLEN-1:0] MMUPAdr,
output logic MMUTranslate
output logic MMUTranslate,
// Faults
output logic InstrPageFaultM, LoadPageFaultM, StorePageFaultM
);
logic SvMode;
logic [`PPN_BITS-1:0] BasePageTablePPN;
logic [`XLEN-1:0] DirectInstrPTE, DirectMemPTE;
logic [`XLEN-1:0] DirectInstrPTE, DirectMemPTE, TranslationVAdr;
logic [9:0] DirectPTEFlags = {2'b0, 8'b00001111};
@ -73,71 +77,233 @@ module pagetablewalker (
end
endgenerate
flopenr #(`XLEN) instrpte(clk, reset, ITLBMissF, DirectInstrPTE, PageTableEntryF);
flopenr #(`XLEN) datapte(clk, reset, DTLBMissM, DirectMemPTE, PageTableEntryM);
//flopenr #(`XLEN) instrpte(clk, reset, ITLBMissF, DirectInstrPTE, PageTableEntryF);
//flopenr #(`XLEN) datapte(clk, reset, DTLBMissM, DirectMemPTE, PageTableEntryM);
flopr #(1) iwritesignal(clk, reset, ITLBMissF, ITLBWriteF);
flopr #(1) dwritesignal(clk, reset, DTLBMissM, DTLBWriteM);
//flopr #(1) iwritesignal(clk, reset, ITLBMissF, ITLBWriteF);
//flopr #(1) dwritesignal(clk, reset, DTLBMissM, DTLBWriteM);
// Prefer data address translations over instruction address translations
assign TranslationVAdr = (DTLBMissM) ? MemAdrM : PCF;
assign MMUTranslate = DTLBMissM || ITLBMissF;
/*
generate
if (`XLEN == 32) begin
assign SvMode = SATP_REGW[31];
logic VPN1 [9:0] = TranslationVAdr[31:22];
logic VPN0 [9:0] = TranslationVAdr[21:12]; // *** could optimize by not passing offset?
logic [9:0] VPN1 = TranslationVAdr[31:22];
logic [9:0] VPN0 = TranslationVAdr[21:12]; // *** could optimize by not passing offset?
logic TranslationPAdr [33:0];
logic [33:0] TranslationPAdr;
logic [21:0] CurrentPPN;
typedef enum {IDLE, DATA_LEVEL1, DATA_LEVEL0, DATA_LEAF, DATA FAULT} statetype;
logic Dirty, Accessed, Global, User,
Executable, Writable, Readable, Valid;
logic ValidPTE, AccessAlert, MegapageMisaligned, BadMegapage, LeafPTE;
typedef enum {IDLE, LEVEL1, LEVEL0, LEAF, FAULT} statetype;
statetype WalkerState, NextWalkerState;
always_ff @(posedge HCLK, negedge HRESETn)
if (~HRESETn) WalkerState <= #1 IDLE;
else WalkerState <= #1 NextWalkerState;
// *** Do we need a synchronizer here for walker to talk to ahblite?
flopenl #(.TYPE(statetype)) mmureg(clk, reset, 1'b1, NextWalkerState, IDLE, WalkerState);
always_comb begin
NextWalkerState = 'X;
case (WalkerState)
IDLE: if (TLBMissM) NextWalkerState = LEVEL1;
else NextWalkerState = IDLE;
LEVEL1: if (HREADY && ValidEntry) NextWalkerState = LEVEL0;
else if (HREADY) NextWalkerState = FAULT;
else NextWalkerState = LEVEL1;
LEVEL2: if (HREADY && ValidEntry) NextWalkerState = LEAF;
else if (HREADY) NextWalkerState = FAULT;
else NextWalkerState = LEVEL2;
LEAF: NextWalkerState = IDLE;
IDLE: if (MMUTranslate) NextWalkerState = LEVEL1;
else NextWalkerState = IDLE;
LEVEL1: if (~MMUReady) NextWalkerState = LEVEL1;
// else if (~ValidPTE || (LeafPTE && BadMegapage))
// NextWalkerState = FAULT;
// *** Leave megapage implementation for later
// else if (ValidPTE && LeafPTE) NextWalkerState = LEAF;
else if (ValidPTE && ~LeafPTE) NextWalkerState = LEVEL0;
else NextWalkerState = FAULT;
LEVEL0: if (~MMUReady) NextWalkerState = LEVEL0;
else if (ValidPTE && LeafPTE && ~AccessAlert)
NextWalkerState = LEAF;
else NextWalkerState = FAULT;
LEAF: if (MMUTranslate) NextWalkerState = LEVEL1;
else NextWalkerState = IDLE;
FAULT: if (MMUTranslate) NextWalkerState = LEVEL1;
else NextWalkerState = IDLE;
endcase
end
always_ff @(posedge HCLK, negedge HRESETn)
if (~HRESETn) begin
// unswizzle PTE bits
assign {Dirty, Accessed, Global, User,
Executable, Writable, Readable, Valid} = MMUReadPTE[7:0];
// A megapage is a Level 1 leaf page. This page must have zero PPN[0].
assign MegapageMisaligned = |(CurrentPPN[9:0]);
assign LeafPTE = Executable | Writable | Readable;
assign ValidPTE = Valid && ~(Writable && ~Readable);
assign AccessAlert = ~Accessed || (MemWriteM && ~Dirty);
assign BadMegapage = MegapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme
// *** Should translate this flop block into our flop module notation
always_ff @(posedge clk, negedge reset)
if (reset) begin
TranslationPAdr <= '0;
PageTableEntryF <= '0;
TranslationComplete <= '0;
MMUTranslationComplete <= '0;
DTLBWriteM <= '0;
ITLBWriteF <= '0;
InstrPageFaultM <= '0;
LoadPageFaultM <= '0;
StorePageFaultM <= '0;
end else begin
// default values
TranslationPAdr <= '0;
PageTableEntryF <= '0;
MMUTranslationComplete <= '0;
DTLBWriteM <= '0;
ITLBWriteF <= '0;
InstrPageFaultM <= '0;
LoadPageFaultM <= '0;
StorePageFaultM <= '0;
case (NextWalkerState)
LEVEL1: TranslationPAdr <= {BasePageTablePPN, VPN1, 2'b00};
LEVEL2: TranslationPAdr <= {CurrentPPN, VPN0, 2'b00};
LEVEL1: begin
TranslationPAdr <= {BasePageTablePPN, VPN1, 2'b00};
end
LEVEL0: begin
TranslationPAdr <= {CurrentPPN, VPN0, 2'b00};
end
LEAF: begin
PageTableEntryF <= CurrentPageTableEntry;
TranslationComplete <= '1;
end
PageTableEntryF <= MMUReadPTE;
PageTableEntryM <= MMUReadPTE;
MMUTranslationComplete <= '1;
DTLBWriteM <= DTLBMissM;
ITLBWriteF <= ~DTLBMissM; // Prefer data over instructions
end
FAULT: begin
InstrPageFaultM <= ~DTLBMissM;
LoadPageFaultM <= DTLBMissM && ~MemWriteM;
StorePageFaultM <= DTLBMissM && MemWriteM;
end
endcase
end
assign #1 Translate = (NextWalkerState == LEVEL1);
// Interpret inputs from ahblite
assign CurrentPPN = MMUReadPTE[31:10];
// Assign outputs to ahblite
// *** Currently truncate address to 32 bits. This must be changed if
// we support larger physical address spaces
assign MMUPAdr = TranslationPAdr[31:0];
end else begin
// sv39 not yet implemented
assign SvMode = SATP_REGW[63];
logic [8:0] VPN2 = TranslationVAdr[38:30];
logic [8:0] VPN1 = TranslationVAdr[29:21];
logic [8:0] VPN0 = TranslationVAdr[20:12]; // *** could optimize by not passing offset?
logic [55:0] TranslationPAdr;
logic [43:0] CurrentPPN;
logic Dirty, Accessed, Global, User,
Executable, Writable, Readable, Valid;
logic ValidPTE, AccessAlert, GigapageMisaligned, MegapageMisaligned,
BadGigapage, BadMegapage, LeafPTE;
typedef enum {IDLE, LEVEL2, LEVEL1, LEVEL0, LEAF, FAULT} statetype;
statetype WalkerState, NextWalkerState;
// *** Do we need a synchronizer here for walker to talk to ahblite?
flopenl #(.TYPE(statetype)) mmureg(clk, reset, 1'b1, NextWalkerState, IDLE, WalkerState);
always_comb begin
case (WalkerState)
IDLE: if (MMUTranslate) NextWalkerState = LEVEL1;
else NextWalkerState = IDLE;
LEVEL2: if (~MMUReady) NextWalkerState = LEVEL2;
else if (ValidPTE && ~LeafPTE) NextWalkerState = LEVEL1;
else NextWalkerState = FAULT;
LEVEL1: if (~MMUReady) NextWalkerState = LEVEL1;
// else if (~ValidPTE || (LeafPTE && BadMegapage))
// NextWalkerState = FAULT;
// *** Leave megapage implementation for later
// else if (ValidPTE && LeafPTE) NextWalkerState = LEAF;
else if (ValidPTE && ~LeafPTE) NextWalkerState = LEVEL0;
else NextWalkerState = FAULT;
LEVEL0: if (~MMUReady) NextWalkerState = LEVEL0;
else if (ValidPTE && LeafPTE && ~AccessAlert)
NextWalkerState = LEAF;
else NextWalkerState = FAULT;
LEAF: if (MMUTranslate) NextWalkerState = LEVEL2;
else NextWalkerState = IDLE;
FAULT: if (MMUTranslate) NextWalkerState = LEVEL2;
else NextWalkerState = IDLE;
endcase
end
// unswizzle PTE bits
assign {Dirty, Accessed, Global, User,
Executable, Writable, Readable, Valid} = MMUReadPTE[7:0];
// A megapage is a Level 1 leaf page. This page must have zero PPN[0].
assign GigapageMisaligned = |(CurrentPPN[17:0]);
assign MegapageMisaligned = |(CurrentPPN[8:0]);
assign LeafPTE = Executable | Writable | Readable;
assign ValidPTE = Valid && ~(Writable && ~Readable);
assign AccessAlert = ~Accessed || (MemWriteM && ~Dirty);
assign BadGigapage = GigapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme
assign BadMegapage = MegapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme
// *** Should translate this flop block into our flop module notation
always_ff @(posedge clk, negedge reset)
if (reset) begin
TranslationPAdr <= '0;
PageTableEntryF <= '0;
MMUTranslationComplete <= '0;
DTLBWriteM <= '0;
ITLBWriteF <= '0;
InstrPageFaultM <= '0;
LoadPageFaultM <= '0;
StorePageFaultM <= '0;
end else begin
// default values
TranslationPAdr <= '0;
PageTableEntryF <= '0;
MMUTranslationComplete <= '0;
DTLBWriteM <= '0;
ITLBWriteF <= '0;
InstrPageFaultM <= '0;
LoadPageFaultM <= '0;
StorePageFaultM <= '0;
case (NextWalkerState)
LEVEL2: begin
TranslationPAdr <= {BasePageTablePPN, VPN2, 3'b00};
end
LEVEL1: begin
TranslationPAdr <= {CurrentPPN, VPN1, 3'b00};
end
LEVEL0: begin
TranslationPAdr <= {CurrentPPN, VPN0, 3'b00};
end
LEAF: begin
PageTableEntryF <= MMUReadPTE;
PageTableEntryM <= MMUReadPTE;
MMUTranslationComplete <= '1;
DTLBWriteM <= DTLBMissM;
ITLBWriteF <= ~DTLBMissM; // Prefer data over instructions
end
FAULT: begin
InstrPageFaultM <= ~DTLBMissM;
LoadPageFaultM <= DTLBMissM && ~MemWriteM;
StorePageFaultM <= DTLBMissM && MemWriteM;
end
endcase
end
// Interpret inputs from ahblite
assign CurrentPPN = MMUReadPTE[53:10];
// Assign outputs to ahblite
// *** Currently truncate address to 32 bits. This must be changed if
// we support larger physical address spaces
assign MMUPAdr = TranslationPAdr[31:0];
end
endgenerate
// rv32 case
*/
endmodule

View File

@ -39,6 +39,7 @@ module privileged (
input logic InstrValidW, FloatRegWriteW, LoadStallD, BPPredWrongM,
input logic [3:0] InstrClassM,
input logic PrivilegedM,
input logic InstrPageFaultM, LoadPageFaultM, StorePageFaultM,
input logic InstrMisalignedFaultM, InstrAccessFaultF, IllegalIEUInstrFaultD,
input logic LoadMisalignedFaultM, LoadAccessFaultM,
input logic StoreMisalignedFaultM, StoreAccessFaultM,
@ -48,7 +49,7 @@ module privileged (
output logic [1:0] PrivilegeModeW,
output logic [`XLEN-1:0] SATP_REGW,
output logic [2:0] FRM_REGW,
input logic FlushD, FlushE, FlushM, StallD, StallW
input logic FlushD, FlushE, FlushM, StallD, StallW, StallE, StallM
);
logic [1:0] NextPrivilegeModeM;
@ -65,7 +66,6 @@ module privileged (
logic IllegalInstrFaultM;
logic BreakpointFaultM, EcallFaultM;
logic InstrPageFaultM, LoadPageFaultM, StorePageFaultM;
logic MTrapM, STrapM, UTrapM;
logic [1:0] STATUS_MPP;
@ -119,9 +119,11 @@ module privileged (
assign BreakpointFaultM = ebreakM; // could have other causes too
assign EcallFaultM = ecallM;
assign InstrPageFaultM = 0;
assign LoadPageFaultM = 0;
assign StorePageFaultM = 0;
// *** Page faults now driven by page table walker. Might need to make the
// below signals ORs of a walker fault and a tlb fault if both of those come in
// assign InstrPageFaultM = 0;
// assign LoadPageFaultM = 0;
// assign StorePageFaultM = 0;
// pipeline fault signals
flopenrc #(1) faultregD(clk, reset, FlushD, ~StallD, InstrAccessFaultF, InstrAccessFaultD);

View File

@ -76,6 +76,7 @@ module wallypipelinedhart (
logic InstrMisalignedFaultM;
logic DataMisalignedM;
logic IllegalBaseInstrFaultD, IllegalIEUInstrFaultD;
logic InstrPageFaultM, LoadPageFaultM, StorePageFaultM;
logic LoadMisalignedFaultM, LoadAccessFaultM;
logic StoreMisalignedFaultM, StoreAccessFaultM;
logic [`XLEN-1:0] InstrMisalignedAdrM;
@ -98,7 +99,7 @@ module wallypipelinedhart (
logic [`XLEN-1:0] PageTableEntryF, PageTableEntryM;
logic [`XLEN-1:0] MMUPAdr, MMUReadPTE;
logic MMUTranslate, MMUReady;
logic MMUTranslate, MMUTranslationComplete, MMUReady;
// bus interface to dmem
logic MemReadM, MemWriteM;