mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-11 06:05:49 +00:00
Cleaned up hptw.
This commit is contained in:
parent
24916d42e2
commit
9b6b6617af
@ -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.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user