2021-03-18 18:35:46 +00:00
///////////////////////////////////////////
// pagetablewalker.sv
//
// Written: tfleming@hmc.edu 2 March 2021
2021-06-01 21:50:37 +00:00
// Modified: kmacsaigoren@hmc.edu 1 June 2021
// implemented SV48 on top of SV39. This included, adding a level of the FSM for the extra page number segment
// adding support for terapage encoding, and for setting the TranslationPAdr using the new level,
// adding the internal SvMode signal
2021-03-18 18:35:46 +00:00
//
// Purpose: Page Table Walker
// Part of the Memory Management Unit (MMU)
2021-07-16 16:12:57 +00:00
//
2021-03-18 18:35:46 +00:00
// A component of the Wally configurable RISC-V project.
2021-07-16 16:12:57 +00:00
//
2021-03-18 18:35:46 +00:00
// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
2021-07-16 16:12:57 +00:00
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
2021-03-18 18:35:46 +00:00
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
2021-07-16 16:12:57 +00:00
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
2021-03-18 18:35:46 +00:00
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////
`include " wally-config.vh "
2021-07-01 22:17:53 +00:00
module pagetablewalker
(
2021-07-16 16:12:57 +00:00
input logic clk , reset ,
2021-07-17 21:08:07 +00:00
input logic [ `XLEN - 1 : 0 ] SATP_REGW , // includes SATP.MODE to determine number of levels in page table
input logic [ `XLEN - 1 : 0 ] PCF , MemAdrM , // addresses to translate
input logic ITLBMissF , DTLBMissM , // TLB Miss
input logic [ 1 : 0 ] MemRWM , // 10 = read, 01 = write
input logic [ `XLEN - 1 : 0 ] HPTWReadPTE , // page table entry from LSU
input logic HPTWStall , // stall from LSU
output logic [ `XLEN - 1 : 0 ] PTE , // page table entry to TLBs
output logic [ 1 : 0 ] PageType , // page type to TLBs
output logic ITLBWriteF , DTLBWriteM , // write TLB with new entry
output logic SelPTW , // LSU Arbiter should select signals from the PTW rather than from the IEU
2021-07-17 23:02:18 +00:00
output logic [ `XLEN - 1 : 0 ] TranslationVAdr ,
output logic [ `PA_BITS - 1 : 0 ] TranslationPAdr ,
output logic UseTranslationVAdr ,
2021-07-17 21:08:07 +00:00
output logic HPTWRead , // HPTW requesting to read memory
output logic WalkerInstrPageFaultF , WalkerLoadPageFaultM , WalkerStorePageFaultM // faults
) ;
2021-07-15 03:26:07 +00:00
2021-03-18 18:35:46 +00:00
generate
2021-07-06 03:35:44 +00:00
if ( `MEM_VIRTMEM ) begin
// Internal signals
2021-07-17 19:50:52 +00:00
logic DTLBWalk ; // register TLBs translation miss requests
2021-07-16 16:12:57 +00:00
logic [ `PPN_BITS - 1 : 0 ] BasePageTablePPN ;
logic [ `PPN_BITS - 1 : 0 ] CurrentPPN ;
2021-07-17 23:09:13 +00:00
logic MemWrite ;
2021-07-17 18:29:20 +00:00
logic Executable , Writable , Readable , Valid ;
2021-07-17 18:36:27 +00:00
logic MegapageMisaligned , GigapageMisaligned , TerapageMisaligned ;
logic ValidPTE , LeafPTE , ValidLeafPTE , ValidNonLeafPTE ;
2021-07-16 16:12:57 +00:00
logic StartWalk ;
logic EndWalk ;
2021-07-17 18:16:33 +00:00
logic PRegEn ;
2021-07-17 17:54:58 +00:00
logic [ 1 : 0 ] NextPageType ;
2021-07-17 19:50:52 +00:00
logic [ `SVMODE_BITS - 1 : 0 ] SvMode ;
2021-07-16 16:12:57 +00:00
2021-07-17 18:36:27 +00:00
typedef enum { LEVEL0_SET_ADR , LEVEL0_READ , LEVEL0 ,
LEVEL1_SET_ADR , LEVEL1_READ , LEVEL1 ,
LEVEL2_SET_ADR , LEVEL2_READ , LEVEL2 ,
LEVEL3_SET_ADR , LEVEL3_READ , LEVEL3 ,
2021-07-17 08:55:01 +00:00
LEAF , IDLE , FAULT } statetype ;
2021-07-17 17:54:58 +00:00
statetype WalkerState , NextWalkerState , InitialWalkerState ;
2021-07-16 16:12:57 +00:00
2021-07-17 19:50:52 +00:00
// Extract bits from CSRs and inputs
2021-07-06 03:35:44 +00:00
assign SvMode = SATP_REGW [ `XLEN - 1 : `XLEN - `SVMODE_BITS ] ;
assign BasePageTablePPN = SATP_REGW [ `PPN_BITS - 1 : 0 ] ;
2021-07-17 16:01:43 +00:00
assign MemWrite = MemRWM [ 0 ] ;
2021-07-06 03:35:44 +00:00
2021-07-17 18:16:33 +00:00
// Determine which address to translate
assign TranslationVAdr = DTLBWalk ? MemAdrM : PCF ;
2021-07-17 21:08:07 +00:00
assign CurrentPPN = PTE [ `PPN_BITS + 9 : 10 ] ;
2021-07-06 03:35:44 +00:00
2021-07-17 21:08:07 +00:00
// State flops
2021-07-17 23:09:13 +00:00
flopenrc # ( 1 ) TLBMissMReg ( clk , reset , EndWalk , StartWalk | EndWalk , DTLBMissM , DTLBWalk ) ; // track whether walk is for DTLB or ITLB
2021-07-17 21:08:07 +00:00
flopenr # ( `XLEN ) PTEReg ( clk , reset , PRegEn , HPTWReadPTE , PTE ) ; // Capture page table entry from data cache
2021-07-06 03:35:44 +00:00
// Assign PTE descriptors common across all XLEN values
2021-07-17 18:29:20 +00:00
// For non-leaf PTEs, D, A, U bits are reserved and ignored. They do not cause faults while walking the page table
2021-07-17 19:50:52 +00:00
assign { Executable , Writable , Readable , Valid } = PTE [ 3 : 0 ] ;
2021-07-17 18:29:20 +00:00
assign LeafPTE = Executable | Writable | Readable ;
2021-07-06 03:35:44 +00:00
assign ValidPTE = Valid & & ~ ( Writable & & ~ Readable ) ;
2021-07-17 18:36:27 +00:00
assign ValidLeafPTE = ValidPTE & LeafPTE ;
assign ValidNonLeafPTE = ValidPTE & ~ LeafPTE ;
2021-07-17 18:29:20 +00:00
2021-07-17 19:50:52 +00:00
// Enable and select signals based on states
assign StartWalk = ( WalkerState = = IDLE ) & ( DTLBMissM | ITLBMissF ) ;
assign EndWalk = ( WalkerState = = LEAF ) | | ( WalkerState = = FAULT ) ;
assign PRegEn = ( NextWalkerState = = LEVEL3 ) | ( NextWalkerState = = LEVEL2 ) | ( NextWalkerState = = LEVEL1 ) | ( NextWalkerState = = LEVEL0 ) ;
assign HPTWRead = ( WalkerState = = LEVEL3_READ ) | ( WalkerState = = LEVEL2_READ ) | ( WalkerState = = LEVEL1_READ ) | ( WalkerState = = LEVEL0_READ ) ;
2021-07-17 08:12:31 +00:00
assign SelPTW = ( WalkerState ! = IDLE ) & ( WalkerState ! = FAULT ) ;
2021-07-17 18:13:00 +00:00
assign DTLBWriteM = ( WalkerState = = LEAF ) & DTLBWalk ;
assign ITLBWriteF = ( WalkerState = = LEAF ) & ~ DTLBWalk ;
2021-07-17 23:02:18 +00:00
assign UseTranslationVAdr = ( NextWalkerState = = LEAF ) | | ( WalkerState = = LEAF ) ;
2021-07-17 08:12:31 +00:00
2021-07-17 18:13:00 +00:00
// Raise faults. DTLBMiss
assign WalkerInstrPageFaultF = ( WalkerState = = FAULT ) & ~ DTLBWalk ;
assign WalkerLoadPageFaultM = ( WalkerState = = FAULT ) & DTLBWalk & ~ MemWrite ;
assign WalkerStorePageFaultM = ( WalkerState = = FAULT ) & DTLBWalk & MemWrite ;
2021-07-17 08:55:01 +00:00
2021-07-17 17:54:58 +00:00
// FSM to track PageType based on the levels of the page table traversed
flopr # ( 2 ) PageTypeReg ( clk , reset , NextPageType , PageType ) ;
always_comb
case ( WalkerState )
LEVEL3: NextPageType = 2 'b11 ; // terapage
LEVEL2: NextPageType = 2 'b10 ; // gigapage
LEVEL1: NextPageType = 2 'b01 ; // megapage
LEVEL0: NextPageType = 2 'b00 ; // kilopage
default : NextPageType = PageType ;
endcase
2021-07-17 08:06:26 +00:00
// TranslationPAdr mux
2021-07-17 21:08:07 +00:00
if ( `XLEN = = 32 ) begin // RV32
2021-07-17 23:16:56 +00:00
logic [ 9 : 0 ] VPN1 , VPN0 , VPN ;
logic [ `PPN_BITS - 1 : 0 ] PPN ;
2021-07-17 08:06:26 +00:00
assign VPN1 = TranslationVAdr [ 31 : 22 ] ;
assign VPN0 = TranslationVAdr [ 21 : 12 ] ;
2021-07-17 23:16:56 +00:00
assign VPN = ( ( WalkerState = = LEVEL1_SET_ADR ) | ( WalkerState = = LEVEL1_READ ) ) ? VPN1 : VPN0 ;
assign PPN = ( ( WalkerState = = LEVEL1_SET_ADR ) | ( WalkerState = = LEVEL1_READ ) ) ? BasePageTablePPN : CurrentPPN ;
assign TranslationPAdr = { PPN , VPN , 2 'b00 } ;
/ * always_comb
2021-07-17 08:06:26 +00:00
case ( WalkerState )
2021-07-17 18:02:59 +00:00
LEVEL1_SET_ADR: TranslationPAdr = { BasePageTablePPN , VPN1 , 2 'b00 } ;
2021-07-17 18:36:27 +00:00
LEVEL1_READ: TranslationPAdr = { BasePageTablePPN , VPN1 , 2 'b00 } ;
2021-07-17 23:09:13 +00:00
LEVEL1: if ( NextWalkerState = = LEAF ) TranslationPAdr = 0 ; // {2'b00, TranslationVAdr[31:0]}; // *** 7/17/21 Ross will check this and similar in LEVEL0 and LEAF
2021-07-17 18:02:59 +00:00
else TranslationPAdr = { CurrentPPN , VPN0 , 2 'b00 } ;
LEVEL0_SET_ADR: TranslationPAdr = { CurrentPPN , VPN0 , 2 'b00 } ;
2021-07-17 18:36:27 +00:00
LEVEL0_READ: TranslationPAdr = { CurrentPPN , VPN0 , 2 'b00 } ;
2021-07-17 23:09:13 +00:00
LEVEL0: TranslationPAdr = 0 ; // {2'b00, TranslationVAdr[31:0]};
LEAF: TranslationPAdr = 0 ; // {2'b00, TranslationVAdr[31:0]};
2021-07-17 08:06:26 +00:00
default : TranslationPAdr = 0 ; // cause seg fault if this is improperly used
2021-07-17 23:16:56 +00:00
endcase */
2021-07-17 21:08:07 +00:00
end else begin // RV64
2021-07-17 08:06:26 +00:00
logic [ 8 : 0 ] VPN3 , VPN2 , VPN1 , VPN0 ;
assign VPN3 = TranslationVAdr [ 47 : 39 ] ;
assign VPN2 = TranslationVAdr [ 38 : 30 ] ;
assign VPN1 = TranslationVAdr [ 29 : 21 ] ;
assign VPN0 = TranslationVAdr [ 20 : 12 ] ;
always_comb
case ( WalkerState )
2021-07-17 18:02:59 +00:00
LEVEL3_SET_ADR: TranslationPAdr = { BasePageTablePPN , VPN3 , 3 'b000 } ;
2021-07-17 18:36:27 +00:00
LEVEL3_READ: TranslationPAdr = { BasePageTablePPN , VPN3 , 3 'b000 } ;
2021-07-17 23:09:13 +00:00
LEVEL3: if ( NextWalkerState = = LEAF ) TranslationPAdr = 0 ; //TranslationVAdr[`PA_BITS-1:0];
2021-07-17 08:06:26 +00:00
else TranslationPAdr = { ( SvMode = = `SV48 ) ? CurrentPPN : BasePageTablePPN , VPN2 , 3 'b000 } ;
2021-07-17 18:02:59 +00:00
LEVEL2_SET_ADR: TranslationPAdr = { ( SvMode = = `SV48 ) ? CurrentPPN : BasePageTablePPN , VPN2 , 3 'b000 } ;
2021-07-17 18:36:27 +00:00
LEVEL2_READ: TranslationPAdr = { ( SvMode = = `SV48 ) ? CurrentPPN : BasePageTablePPN , VPN2 , 3 'b000 } ;
2021-07-17 23:09:13 +00:00
LEVEL2: if ( NextWalkerState = = LEAF ) TranslationPAdr = 0 ; //TranslationVAdr[`PA_BITS-1:0];
2021-07-17 08:06:26 +00:00
else TranslationPAdr = { CurrentPPN , VPN1 , 3 'b000 } ;
2021-07-17 18:02:59 +00:00
LEVEL1_SET_ADR: TranslationPAdr = { CurrentPPN , VPN1 , 3 'b000 } ;
2021-07-17 18:36:27 +00:00
LEVEL1_READ: TranslationPAdr = { CurrentPPN , VPN1 , 3 'b000 } ;
2021-07-17 23:09:13 +00:00
LEVEL1: if ( NextWalkerState = = LEAF ) TranslationPAdr = 0 ; //TranslationVAdr[`PA_BITS-1:0];
2021-07-17 08:06:26 +00:00
else TranslationPAdr = { CurrentPPN , VPN0 , 3 'b000 } ;
2021-07-17 18:02:59 +00:00
LEVEL0_SET_ADR: TranslationPAdr = { CurrentPPN , VPN0 , 3 'b000 } ;
2021-07-17 18:36:27 +00:00
LEVEL0_READ: TranslationPAdr = { CurrentPPN , VPN0 , 3 'b000 } ;
2021-07-17 23:09:13 +00:00
LEVEL0: TranslationPAdr = 0 ; //TranslationVAdr[`PA_BITS-1:0];
LEAF: TranslationPAdr = 0 ; //TranslationVAdr[`PA_BITS-1:0];
2021-07-17 08:06:26 +00:00
default : TranslationPAdr = 0 ; // cause seg fault if this is improperly used
endcase
end
2021-07-17 15:31:16 +00:00
if ( `XLEN = = 32 ) begin
2021-07-17 18:02:59 +00:00
assign InitialWalkerState = LEVEL1_SET_ADR ;
2021-07-17 15:31:16 +00:00
assign TerapageMisaligned = 0 ; // not applicable
assign GigapageMisaligned = 0 ; // not applicable
assign MegapageMisaligned = | ( CurrentPPN [ 9 : 0 ] ) ; // must have zero PPN0
end else begin
2021-07-17 18:02:59 +00:00
assign InitialWalkerState = ( SvMode = = `SV48 ) ? LEVEL3_SET_ADR : LEVEL2_SET_ADR ;
2021-07-17 15:31:16 +00:00
assign TerapageMisaligned = | ( CurrentPPN [ 26 : 0 ] ) ; // must have zero PPN2, PPN1, PPN0
assign GigapageMisaligned = | ( CurrentPPN [ 17 : 0 ] ) ; // must have zero PPN1 and PPN0
assign MegapageMisaligned = | ( CurrentPPN [ 8 : 0 ] ) ; // must have zero PPN0
2021-07-17 15:41:43 +00:00
end
2021-07-15 03:26:07 +00:00
2021-07-17 18:02:59 +00:00
// Page Table Walker FSM
2021-07-17 21:08:07 +00:00
// If the setup time on the D$ RAM is short, it should be possible to merge the LEVELx_READ and LEVELx states
// to decrease the latency of the HPTW. However, if the D$ is a cycle limiter, it's better to leave the
// HPTW as shown below to keep the D$ setup time out of the critical path.
flopenl # ( . TYPE ( statetype ) ) WalkerStateReg ( clk , reset , 1 'b1 , NextWalkerState , IDLE , WalkerState ) ;
2021-07-17 15:55:24 +00:00
always_comb
2021-07-16 16:12:57 +00:00
case ( WalkerState )
2021-07-17 16:07:51 +00:00
IDLE: if ( StartWalk ) NextWalkerState = InitialWalkerState ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = IDLE ;
2021-07-17 18:36:27 +00:00
LEVEL3_SET_ADR: NextWalkerState = LEVEL3_READ ;
LEVEL3_READ: if ( HPTWStall ) NextWalkerState = LEVEL3_READ ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = LEVEL3 ;
2021-07-17 18:36:27 +00:00
LEVEL3: if ( ValidLeafPTE & & ~ TerapageMisaligned ) NextWalkerState = LEAF ;
else if ( ValidNonLeafPTE ) NextWalkerState = LEVEL2_SET_ADR ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = FAULT ;
2021-07-17 18:36:27 +00:00
LEVEL2_SET_ADR: NextWalkerState = LEVEL2_READ ;
LEVEL2_READ: if ( HPTWStall ) NextWalkerState = LEVEL2_READ ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = LEVEL2 ;
2021-07-17 18:36:27 +00:00
LEVEL2: if ( ValidLeafPTE & & ~ GigapageMisaligned ) NextWalkerState = LEAF ;
else if ( ValidNonLeafPTE ) NextWalkerState = LEVEL1_SET_ADR ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = FAULT ;
2021-07-17 18:36:27 +00:00
LEVEL1_SET_ADR: NextWalkerState = LEVEL1_READ ;
LEVEL1_READ: if ( HPTWStall ) NextWalkerState = LEVEL1_READ ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = LEVEL1 ;
2021-07-17 18:36:27 +00:00
LEVEL1: if ( ValidLeafPTE & & ~ MegapageMisaligned ) NextWalkerState = LEAF ;
else if ( ValidNonLeafPTE ) NextWalkerState = LEVEL0_SET_ADR ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = FAULT ;
2021-07-17 18:36:27 +00:00
LEVEL0_SET_ADR: NextWalkerState = LEVEL0_READ ;
LEVEL0_READ: if ( HPTWStall ) NextWalkerState = LEVEL0_READ ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = LEVEL0 ;
2021-07-17 18:36:27 +00:00
LEVEL0: if ( ValidLeafPTE ) NextWalkerState = LEAF ;
2021-07-17 15:41:43 +00:00
else NextWalkerState = FAULT ;
LEAF: NextWalkerState = IDLE ;
FAULT: NextWalkerState = IDLE ;
2021-07-17 15:33:16 +00:00
default : begin
2021-07-17 15:41:43 +00:00
$error ( " Default state in HPTW should be unreachable " ) ;
NextWalkerState = IDLE ; // should never be reached
2021-07-17 15:33:16 +00:00
end
2021-07-16 16:12:57 +00:00
endcase
2021-07-17 21:08:07 +00:00
end else begin // No Virtual memory supported; tie HPTW outputs to 0
2021-07-17 23:02:18 +00:00
assign HPTWRead = 0 ; assign SelPTW = 0 ;
2021-07-17 21:08:07 +00:00
assign WalkerInstrPageFaultF = 0 ; assign WalkerLoadPageFaultM = 0 ; assign WalkerStorePageFaultM = 0 ;
2021-07-17 23:09:13 +00:00
assign TranslationVAdr = 0 ; assign TranslationPAdr = 0 ; assign UseTranslationVAdr = 0 ;
2021-03-25 06:48:40 +00:00
end
endgenerate
2021-04-13 16:27:12 +00:00
endmodule