Cleaned up hptw.

This commit is contained in:
Ross Thompson 2024-06-19 12:02:56 -07:00
parent 24916d42e2
commit 9b6b6617af

View File

@ -104,7 +104,6 @@ module hptw import cvw::*; #(parameter cvw_t P) (
logic HPTWLoadPageFaultDelay, HPTWStoreAmoPageFaultDelay, HPTWInstrPageFaultDelay; logic HPTWLoadPageFaultDelay, HPTWStoreAmoPageFaultDelay, HPTWInstrPageFaultDelay;
logic HPTWAccessFaultDelay; logic HPTWAccessFaultDelay;
logic TakeHPTWFault; logic TakeHPTWFault;
logic [P.XLEN-1:0] ReadDataNoXM;
logic PBMTFaultM; logic PBMTFaultM;
logic HPTWFaultM; logic HPTWFaultM;
@ -171,9 +170,7 @@ module hptw import cvw::*; #(parameter cvw_t P) (
logic [P.XLEN-1:0] AccessedPTE; logic [P.XLEN-1:0] AccessedPTE;
assign AccessedPTE = {PTE[P.XLEN-1:8], (SetDirty | PTE[7]), 1'b1, PTE[5:0]}; // set accessed bit, conditionally set dirty bit assign AccessedPTE = {PTE[P.XLEN-1:8], (SetDirty | PTE[7]), 1'b1, PTE[5:0]}; // set accessed bit, conditionally set dirty bit
//assign ReadDataNoXM = (ReadDataM[0] === 'x) ? 0 : ReadDataM; // If the PTE.V bit is x because it was read from uninitialized memory set to 0 to avoid x propagation and hanging the simulation. mux2 #(P.XLEN) NextPTEMux(ReadDataM, AccessedPTE, UpdatePTE, NextPTE); // NextPTE = ReadDataM when ADUE = 0 because UpdatePTE = 0
assign ReadDataNoXM = ReadDataM; // *** temporary fix for synthesis; === and x in line above are not synthesizable.
mux2 #(P.XLEN) NextPTEMux(ReadDataNoXM, AccessedPTE, UpdatePTE, NextPTE); // NextPTE = ReadDataNoXM when ADUE = 0 because UpdatePTE = 0
flopenr #(P.PA_BITS) HPTWAdrWriteReg(clk, reset, SaveHPTWAdr, HPTWReadAdr, HPTWWriteAdr); flopenr #(P.PA_BITS) HPTWAdrWriteReg(clk, reset, SaveHPTWAdr, HPTWReadAdr, HPTWWriteAdr);
assign SaveHPTWAdr = WalkerState == L0_ADR; assign SaveHPTWAdr = WalkerState == L0_ADR;
@ -209,7 +206,7 @@ module hptw import cvw::*; #(parameter cvw_t P) (
assign UpdatePTE = (WalkerState == LEAF) & HPTWUpdateDA; // UpdatePTE will always be 0 if ADUE = 0 because HPTWUpdateDA will be 0 assign UpdatePTE = (WalkerState == LEAF) & HPTWUpdateDA; // UpdatePTE will always be 0 if ADUE = 0 because HPTWUpdateDA will be 0
end else begin // block: hptwwrites end else begin // block: hptwwrites
assign NextPTE = ReadDataNoXM; assign NextPTE = ReadDataM;
assign HPTWAdr = HPTWReadAdr; assign HPTWAdr = HPTWReadAdr;
assign HPTWUpdateDA = 1'b0; assign HPTWUpdateDA = 1'b0;
assign UpdatePTE = 1'b0; assign UpdatePTE = 1'b0;
@ -265,60 +262,47 @@ module hptw import cvw::*; #(parameter cvw_t P) (
end else begin end else begin
logic GigapageMisaligned, TerapageMisaligned; logic GigapageMisaligned, TerapageMisaligned;
assign InitialWalkerState = (SvMode == P.SV48) ? L3_ADR : L2_ADR; assign InitialWalkerState = (SvMode == P.SV48) ? L3_ADR : L2_ADR;
assign TerapageMisaligned = |(CurrentPPN[26:0]); // must have zero PPN2, PPN1, PPN0 assign TerapageMisaligned = |(CurrentPPN[26:0]); // Must have zero PPN2, PPN1, PPN0
assign GigapageMisaligned = |(CurrentPPN[17:0]); // must have zero PPN1 and PPN0 assign GigapageMisaligned = |(CurrentPPN[17:0]); // Must have zero PPN1 and PPN0
assign MegapageMisaligned = |(CurrentPPN[8:0]); // must have zero PPN0 assign MegapageMisaligned = |(CurrentPPN[8:0]); // Must have zero PPN0
assign Misaligned = ((WalkerState == L2_ADR) & TerapageMisaligned) | ((WalkerState == L1_ADR) & GigapageMisaligned) | ((WalkerState == L0_ADR) & MegapageMisaligned); assign Misaligned = ((WalkerState == L2_ADR) & TerapageMisaligned) | ((WalkerState == L1_ADR) & GigapageMisaligned) | ((WalkerState == L0_ADR) & MegapageMisaligned);
end end
// Page Table Walker FSM // Page Table Walker FSM
// *** there is a bug here (RT). Each memory access needs to be potentially flushed if the PMA/P checkers
// generate an access fault. Specially the store on UDPATE_PTE needs to check for access violation.
// I think the solution is to do 1 of the following
// 1. Allow the HPTW to generate exceptions and stop walking immediately.
// 2. If the store would generate an exception don't store to dcache but still write the TLB. When we go back
// to LEAF then the PMA/P. Wait this does not work. The PMA/P won't be looking a the address in the table, but
// rather than physical address of the translated instruction/data. So we must generate the exception.
flopenl #(.TYPE(statetype)) WalkerStateReg(clk, reset | FlushW, 1'b1, NextWalkerState, IDLE, WalkerState); flopenl #(.TYPE(statetype)) WalkerStateReg(clk, reset | FlushW, 1'b1, NextWalkerState, IDLE, WalkerState);
always_comb always_comb
case (WalkerState) case (WalkerState)
IDLE: if (TLBMissOrUpdateDA) NextWalkerState = InitialWalkerState; IDLE: if (TLBMissOrUpdateDA) NextWalkerState = InitialWalkerState;
else NextWalkerState = IDLE; else NextWalkerState = IDLE;
L3_ADR: NextWalkerState = L3_RD; // first access in SV48 L3_ADR: NextWalkerState = L3_RD; // First access in SV48
L3_RD: if (DCacheBusStallM) NextWalkerState = L3_RD; L3_RD: if (DCacheBusStallM) NextWalkerState = L3_RD;
else if(HPTWFaultM) NextWalkerState = FAULT; else if (HPTWFaultM) NextWalkerState = FAULT;
else NextWalkerState = L2_ADR; else NextWalkerState = L2_ADR;
L2_ADR: if (InitialWalkerState == L2_ADR | ValidNonLeafPTE) NextWalkerState = L2_RD; // first access in SV39 L2_ADR: if (InitialWalkerState == L2_ADR | ValidNonLeafPTE) NextWalkerState = L2_RD; // First access in SV39
else NextWalkerState = LEAF; else NextWalkerState = LEAF;
L2_RD: if (DCacheBusStallM) NextWalkerState = L2_RD; L2_RD: if (DCacheBusStallM) NextWalkerState = L2_RD;
else if(HPTWFaultM) NextWalkerState = FAULT; else if (HPTWFaultM) NextWalkerState = FAULT;
else NextWalkerState = L1_ADR; else NextWalkerState = L1_ADR;
L1_ADR: if (InitialWalkerState == L1_ADR | ValidNonLeafPTE) NextWalkerState = L1_RD; // first access in SV32 L1_ADR: if (InitialWalkerState == L1_ADR | ValidNonLeafPTE) NextWalkerState = L1_RD; // First access in SV32
else NextWalkerState = LEAF; else NextWalkerState = LEAF;
L1_RD: if (DCacheBusStallM) NextWalkerState = L1_RD; L1_RD: if (DCacheBusStallM) NextWalkerState = L1_RD;
else if(HPTWFaultM) NextWalkerState = FAULT; else if (HPTWFaultM) NextWalkerState = FAULT;
else NextWalkerState = L0_ADR; else NextWalkerState = L0_ADR;
L0_ADR: if (ValidNonLeafPTE) NextWalkerState = L0_RD; L0_ADR: if (ValidNonLeafPTE) NextWalkerState = L0_RD;
else NextWalkerState = LEAF; else NextWalkerState = LEAF;
L0_RD: if (DCacheBusStallM) NextWalkerState = L0_RD; L0_RD: if (DCacheBusStallM) NextWalkerState = L0_RD;
else if(HPTWFaultM) NextWalkerState = FAULT; else if (HPTWFaultM) NextWalkerState = FAULT;
else NextWalkerState = LEAF; else NextWalkerState = LEAF;
LEAF: if (P.SVADU_SUPPORTED & HPTWUpdateDA) NextWalkerState = UPDATE_PTE; LEAF: if (P.SVADU_SUPPORTED & HPTWUpdateDA) NextWalkerState = UPDATE_PTE;
else NextWalkerState = IDLE; else NextWalkerState = IDLE;
UPDATE_PTE: if(DCacheBusStallM) NextWalkerState = UPDATE_PTE; UPDATE_PTE: if (DCacheBusStallM) NextWalkerState = UPDATE_PTE;
else NextWalkerState = LEAF; else NextWalkerState = LEAF;
FAULT: NextWalkerState = IDLE; FAULT: NextWalkerState = IDLE;
default: NextWalkerState = IDLE; // should never be reached default: NextWalkerState = IDLE; // Should never be reached
endcase // case (WalkerState) endcase // case (WalkerState)
assign IgnoreRequestTLB = (WalkerState == IDLE & TLBMissOrUpdateDA) | (HPTWFaultM); // RT : 05 April 2023 if hptw request has pmp/a fault suppress bus access. assign IgnoreRequestTLB = (WalkerState == IDLE & TLBMissOrUpdateDA) | (HPTWFaultM); // If hptw request has pmp/a fault suppress bus access.
assign SelHPTW = WalkerState != IDLE; assign SelHPTW = WalkerState != IDLE;
// RT 30 May 2023: When there is an access fault caused by the hptw itself, the fsm jumps to FAULT, removes
// stall and asserts one of HPTWLoadAccessFault, HPTWStoreAmoAccessFault or HPTWInstrAccessFaultDelay.
// The FSM directly transistions to IDLE to ready for the next operation when the delayed version will not be high.
assign HPTWAccessFaultDelay = HPTWLoadAccessFaultDelay | HPTWStoreAmoAccessFaultDelay | HPTWInstrAccessFaultDelay; // *** unused - RT, can we delete?
assign HPTWStall = (WalkerState != IDLE & WalkerState != FAULT) | (WalkerState == IDLE & TLBMissOrUpdateDA); assign HPTWStall = (WalkerState != IDLE & WalkerState != FAULT) | (WalkerState == IDLE & TLBMissOrUpdateDA);
// HTPW address/data/control muxing // HTPW address/data/control muxing
@ -326,22 +310,17 @@ module hptw import cvw::*; #(parameter cvw_t P) (
// Once the walk is done and it is time to update the TLB we need to switch back // Once the walk is done and it is time to update the TLB we need to switch back
// to the orignal data virtual address. // to the orignal data virtual address.
assign SelHPTWAdr = SelHPTW & ~(DTLBWriteM | ITLBWriteF); assign SelHPTWAdr = SelHPTW & ~(DTLBWriteM | ITLBWriteF);
// always block interrupts when using the hardware page table walker.
// multiplex the outputs to LSU // multiplex the outputs to LSU
if(P.XLEN == 64) assign HPTWAdrExt = {{(P.XLEN+2-P.PA_BITS){1'b0}}, HPTWAdr}; // extend to 66 bits if (P.XLEN == 64) assign HPTWAdrExt = {{(P.XLEN+2-P.PA_BITS){1'b0}}, HPTWAdr}; // Extend to 66 bits
else assign HPTWAdrExt = HPTWAdr; else assign HPTWAdrExt = HPTWAdr;
mux2 #(2) rwmux(MemRWM, HPTWRW, SelHPTW, PreLSURWM); mux2 #(2) rwmux(MemRWM, HPTWRW, SelHPTW, PreLSURWM);
mux2 #(3) sizemux(Funct3M, HPTWSize, SelHPTW, LSUFunct3M); mux2 #(3) sizemux(Funct3M, HPTWSize, SelHPTW, LSUFunct3M);
mux2 #(7) funct7mux(Funct7M, 7'b0, SelHPTW, LSUFunct7M); mux2 #(7) funct7mux(Funct7M, 7'b0, SelHPTW, LSUFunct7M);
mux2 #(2) atomicmux(AtomicM, 2'b00, SelHPTW, LSUAtomicM); mux2 #(2) atomicmux(AtomicM, 2'b00, SelHPTW, LSUAtomicM);
mux2 #(P.XLEN+2) lsupadrmux(IEUAdrExtM, HPTWAdrExt, SelHPTWAdr, IHAdrM); mux2 #(P.XLEN+2) lsupadrmux(IEUAdrExtM, HPTWAdrExt, SelHPTWAdr, IHAdrM);
if(P.SVADU_SUPPORTED) if (P.SVADU_SUPPORTED)
mux2 #(P.XLEN) lsuwritedatamux(WriteDataM, PTE, SelHPTW, IHWriteDataM); mux2 #(P.XLEN) lsuwritedatamux(WriteDataM, PTE, SelHPTW, IHWriteDataM);
else assign IHWriteDataM = WriteDataM; else assign IHWriteDataM = WriteDataM;
endmodule endmodule
// another idea. We keep gating the control by ~FlushW, but this adds considerable length to the critical path.
// should we do this differently? For example TLBMissOrUpdateDA is gated by ~FlushW and then drives HPTWStall, which drives LSUStallM, which drives
// the hazard unit to issue stall and flush controlls. ~FlushW already suppresses these in the hazard unit.