forked from Github_Repos/cvw
Complete basic page table walker
This commit is contained in:
parent
0994d03b28
commit
7126ab7864
@ -27,7 +27,7 @@
|
||||
// RV32 or RV64: XLEN = 32 or 64
|
||||
`define XLEN 32
|
||||
|
||||
`define MISA (32'h00000104 | 1 << 12)
|
||||
`define MISA (32'h00000104 | 1 << 20 | 1 << 18 | 1 << 12)
|
||||
`define A_SUPPORTED ((`MISA >> 0) % 2 == 1)
|
||||
`define C_SUPPORTED ((`MISA >> 2) % 2 == 1)
|
||||
`define D_SUPPORTED ((`MISA >> 3) % 2 == 1)
|
||||
@ -53,7 +53,7 @@
|
||||
`define MEM_DCACHE 0
|
||||
`define MEM_DTIM 1
|
||||
`define MEM_ICACHE 0
|
||||
`define MEM_VIRTMEM 0
|
||||
`define MEM_VIRTMEM 1
|
||||
|
||||
// Address space
|
||||
`define RESET_VECTOR 32'h80000000
|
||||
|
@ -54,7 +54,7 @@
|
||||
`define MEM_DCACHE 0
|
||||
`define MEM_DTIM 1
|
||||
`define MEM_ICACHE 0
|
||||
`define MEM_VIRTMEM 0
|
||||
`define MEM_VIRTMEM 1
|
||||
|
||||
// Address space
|
||||
`define RESET_VECTOR 64'h0000000080000000
|
||||
|
@ -54,7 +54,7 @@
|
||||
`define MEM_DCACHE 0
|
||||
`define MEM_DTIM 1
|
||||
`define MEM_ICACHE 0
|
||||
`define MEM_VIRTMEM 0
|
||||
`define MEM_VIRTMEM 1
|
||||
|
||||
// Address space
|
||||
`define RESET_VECTOR 64'h0000000080000000
|
||||
|
@ -57,13 +57,14 @@ module dmem (
|
||||
output logic DTLBMissM, DTLBHitM
|
||||
);
|
||||
|
||||
logic MemAccessM; // Whether memory needs to be accessed
|
||||
logic SquashSCM;
|
||||
|
||||
// *** temporary hack until walker is hooked up -- Thomas F
|
||||
// logic [`XLEN-1:0] PageTableEntryM = '0;
|
||||
logic DTLBFlushM = '0;
|
||||
// logic DTLBWriteM = '0;
|
||||
tlb #(3) dtlb(clk, reset, SATP_REGW, PrivilegeModeW, MemAdrM, PageTableEntryM, DTLBWriteM,
|
||||
tlb #(3) dtlb(clk, reset, SATP_REGW, PrivilegeModeW, MemAccessM, MemAdrM, PageTableEntryM, DTLBWriteM,
|
||||
DTLBFlushM, MemPAdrM, DTLBMissM, DTLBHitM);
|
||||
|
||||
// Determine if an Unaligned access is taking place
|
||||
@ -78,11 +79,12 @@ module dmem (
|
||||
// Squash unaligned data accesses and failed store conditionals
|
||||
// *** this is also the place to squash if the cache is hit
|
||||
assign MemReadM = MemRWM[1] & ~DataMisalignedM;
|
||||
assign MemWriteM = MemRWM[0] & ~DataMisalignedM && ~SquashSCM;
|
||||
assign MemWriteM = MemRWM[0] & ~DataMisalignedM && ~SquashSCM;
|
||||
assign MemAccessM = |MemRWM;
|
||||
|
||||
// Determine if address is valid
|
||||
assign LoadMisalignedFaultM = DataMisalignedM & MemRWM[1];
|
||||
assign LoadAccessFaultM = DataAccessFaultM & MemRWM[0];
|
||||
assign LoadAccessFaultM = DataAccessFaultM & MemRWM[1];
|
||||
assign StoreMisalignedFaultM = DataMisalignedM & MemRWM[0];
|
||||
assign StoreAccessFaultM = DataAccessFaultM & MemRWM[0];
|
||||
|
||||
@ -97,7 +99,7 @@ module dmem (
|
||||
assign scM = MemRWM[0] && AtomicM[0];
|
||||
assign WriteAdrMatchM = MemRWM[0] && (MemPAdrM[`XLEN-1:2] == ReservationPAdrW) && ReservationValidW;
|
||||
assign SquashSCM = scM && ~WriteAdrMatchM;
|
||||
always_comb begin // ReservationValidM (next valiue of valid reservation)
|
||||
always_comb begin // ReservationValidM (next value of valid reservation)
|
||||
if (lrM) ReservationValidM = 1; // set valid on load reserve
|
||||
else if (scM || WriteAdrMatchM) ReservationValidM = 0; // clear valid on store to same address or any sc
|
||||
else ReservationValidM = ReservationValidW; // otherwise don't change valid
|
||||
|
@ -49,6 +49,7 @@ module ahblite (
|
||||
// Signals from MMU
|
||||
input logic [`XLEN-1:0] MMUPAdr,
|
||||
input logic MMUTranslate, MMUTranslationComplete,
|
||||
input logic TrapM,
|
||||
output logic [`XLEN-1:0] MMUReadPTE,
|
||||
output logic MMUReady,
|
||||
// Return from bus
|
||||
@ -104,16 +105,16 @@ module ahblite (
|
||||
else if (InstrReadF) NextBusState = INSTRREAD;
|
||||
else NextBusState = IDLE;
|
||||
MMUTRANSLATE: if (~HREADY) NextBusState = MMUTRANSLATE;
|
||||
else NextBusState = MMUIDLE;
|
||||
else NextBusState = IDLE;
|
||||
// *** 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;
|
||||
// MMUIDLE: 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;
|
||||
ATOMICREAD: if (~HREADY) NextBusState = ATOMICREAD;
|
||||
else NextBusState = ATOMICWRITE;
|
||||
ATOMICWRITE: if (~HREADY) NextBusState = ATOMICWRITE;
|
||||
@ -133,13 +134,15 @@ module ahblite (
|
||||
endcase
|
||||
|
||||
// stall signals
|
||||
assign #2 DataStall = (NextBusState == MEMREAD) || (NextBusState == MEMWRITE) ||
|
||||
// Note that we need to extend both stalls when MMUTRANSLATE goes to idle,
|
||||
// since translation might not be complete.
|
||||
assign #2 DataStall = ~TrapM && ((NextBusState == MEMREAD) || (NextBusState == MEMWRITE) ||
|
||||
(NextBusState == ATOMICREAD) || (NextBusState == ATOMICWRITE) ||
|
||||
(NextBusState == MMUTRANSLATE) || (NextBusState == MMUIDLE);
|
||||
(NextBusState == MMUTRANSLATE) || (BusState == MMUTRANSLATE));
|
||||
// *** 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);
|
||||
assign #1 InstrStall = ~TrapM && ((NextBusState == INSTRREAD) || (NextBusState == INSTRREADC) ||
|
||||
(NextBusState == MMUTRANSLATE) || (BusState == MMUTRANSLATE));
|
||||
|
||||
// bus outputs
|
||||
assign #1 GrantData = (NextBusState == MEMREAD) || (NextBusState == MEMWRITE) ||
|
||||
@ -158,7 +161,7 @@ module ahblite (
|
||||
assign HTRANS = (NextBusState != IDLE) ? 2'b10 : 2'b00; // NONSEQ if reading or writing, IDLE otherwise
|
||||
assign HMASTLOCK = 0; // no locking supported
|
||||
assign HWRITE = (NextBusState == MEMWRITE) || (NextBusState == ATOMICWRITE);
|
||||
// delay write data by one cycle for
|
||||
// delay write data by one cycle for
|
||||
flop #(`XLEN) wdreg(HCLK, WriteData, HWDATA); // delay HWDATA by 1 cycle per spec; *** assumes AHBW = XLEN
|
||||
// delay signals for subword writes
|
||||
flop #(3) adrreg(HCLK, HADDR[2:0], HADDRD);
|
||||
@ -168,7 +171,7 @@ module ahblite (
|
||||
// Route signals to Instruction and Data Caches
|
||||
// *** assumes AHBW = XLEN
|
||||
|
||||
assign #1 MMUReady = (NextBusState == MMUIDLE);
|
||||
assign MMUReady = (BusState == MMUTRANSLATE && NextBusState == IDLE);
|
||||
|
||||
assign InstrRData = HRDATA;
|
||||
assign MMUReadPTE = HRDATA;
|
||||
|
@ -27,46 +27,71 @@
|
||||
`include "wally-config.vh"
|
||||
`include "wally-constants.vh"
|
||||
|
||||
module pagetablewalker (
|
||||
input logic clk, reset,
|
||||
/* ***
|
||||
TO-DO:
|
||||
- Faults have a timing issue and currently do not work.
|
||||
- Leaf state brings HADDR down to zeros (maybe fixed?)
|
||||
- Complete rv64ic case
|
||||
- Implement better accessed/dirty behavior
|
||||
- Implement read/write/execute checking (either here or in TLB)
|
||||
*/
|
||||
|
||||
module pagetablewalker (
|
||||
// Control signals
|
||||
input logic HCLK, HRESETn,
|
||||
input logic [`XLEN-1:0] SATP_REGW,
|
||||
|
||||
input logic MemWriteM,
|
||||
input logic ITLBMissF, DTLBMissM,
|
||||
// Signals from TLBs (addresses to translate)
|
||||
input logic [`XLEN-1:0] PCF, MemAdrM,
|
||||
input logic ITLBMissF, DTLBMissM,
|
||||
input logic [1:0] MemRWM,
|
||||
|
||||
// Outputs to the TLBs (PTEs to write)
|
||||
output logic [`XLEN-1:0] PageTableEntryF, PageTableEntryM,
|
||||
output logic ITLBWriteF, DTLBWriteM,
|
||||
// *** handshake to tlbs probably not needed, since stalls take effect
|
||||
output logic MMUTranslationComplete,
|
||||
|
||||
// Signals from and to ahblite
|
||||
// Signals from ahblite (PTEs from memory)
|
||||
input logic [`XLEN-1:0] MMUReadPTE,
|
||||
input logic MMUReady,
|
||||
|
||||
// Signals to ahblite (memory addresses to access)
|
||||
output logic [`XLEN-1:0] MMUPAdr,
|
||||
output logic MMUTranslate,
|
||||
output logic MMUTranslationComplete,
|
||||
|
||||
// Faults
|
||||
output logic InstrPageFaultM, LoadPageFaultM, StorePageFaultM
|
||||
);
|
||||
|
||||
logic SvMode;
|
||||
// Internal signals
|
||||
logic SvMode, TLBMiss;
|
||||
logic [`PPN_BITS-1:0] BasePageTablePPN;
|
||||
logic [`XLEN-1:0] DirectInstrPTE, DirectMemPTE, TranslationVAdr;
|
||||
logic [`XLEN-1:0] TranslationVAdr;
|
||||
logic [`XLEN-1:0] SavedPTE, CurrentPTE;
|
||||
logic [`PA_BITS-1:0] TranslationPAdr;
|
||||
logic [`PPN_BITS-1:0] CurrentPPN;
|
||||
logic MemStore;
|
||||
|
||||
logic [9:0] DirectPTEFlags = {2'b0, 8'b00001111};
|
||||
// PTE Control Bits
|
||||
logic Dirty, Accessed, Global, User,
|
||||
Executable, Writable, Readable, Valid;
|
||||
// PTE descriptions
|
||||
logic ValidPTE, AccessAlert, MegapageMisaligned, BadMegapage, LeafPTE;
|
||||
|
||||
// rv32 temp case
|
||||
logic [`VPN_BITS-1:0] PCPageNumber;
|
||||
logic [`VPN_BITS-1:0] MemAdrPageNumber;
|
||||
// Signals for direct, fake translations. Not part of the final Wally version.
|
||||
logic [`XLEN-1:0] DirectInstrPTE, DirectMemPTE;
|
||||
logic [9:0] DirectPTEFlags = {2'b0, 8'b00001111};
|
||||
|
||||
logic [`VPN_BITS-1:0] PCPageNumber, MemAdrPageNumber;
|
||||
|
||||
assign BasePageTablePPN = SATP_REGW[`PPN_BITS-1:0];
|
||||
|
||||
assign MemStore = MemRWM[0];
|
||||
|
||||
assign PCPageNumber = PCF[`VPN_BITS+11:12];
|
||||
assign MemAdrPageNumber = MemAdrM[`VPN_BITS+11:12];
|
||||
|
||||
// Create fake page table entries for direct virtual to physical translation
|
||||
generate
|
||||
if (`XLEN == 32) begin
|
||||
assign DirectInstrPTE = {PCPageNumber, DirectPTEFlags};
|
||||
@ -77,36 +102,39 @@ module pagetablewalker (
|
||||
end
|
||||
endgenerate
|
||||
|
||||
//flopenr #(`XLEN) instrpte(clk, reset, ITLBMissF, DirectInstrPTE, PageTableEntryF);
|
||||
//flopenr #(`XLEN) datapte(clk, reset, DTLBMissM, DirectMemPTE, PageTableEntryM);
|
||||
// Direct translation flops
|
||||
//flopenr #(`XLEN) instrpte(HCLK, ~HRESETn, ITLBMissF, DirectInstrPTE, PageTableEntryF);
|
||||
//flopenr #(`XLEN) datapte(HCLK, ~HRESETn, DTLBMissM, DirectMemPTE, PageTableEntryM);
|
||||
|
||||
//flopr #(1) iwritesignal(clk, reset, ITLBMissF, ITLBWriteF);
|
||||
//flopr #(1) dwritesignal(clk, reset, DTLBMissM, DTLBWriteM);
|
||||
//flopr #(1) iwritesignal(HCLK, ~HRESETn, ITLBMissF, ITLBWriteF);
|
||||
//flopr #(1) dwritesignal(HCLK, ~HRESETn, DTLBMissM, DTLBWriteM);
|
||||
|
||||
// Prefer data address translations over instruction address translations
|
||||
assign TranslationVAdr = (DTLBMissM) ? MemAdrM : PCF;
|
||||
assign MMUTranslate = DTLBMissM || ITLBMissF;
|
||||
|
||||
// unswizzle PTE bits
|
||||
assign {Dirty, Accessed, Global, User,
|
||||
Executable, Writable, Readable, Valid} = CurrentPTE[7:0];
|
||||
|
||||
// Assign PTE descriptors common across all XLEN values
|
||||
assign LeafPTE = Executable | Writable | Readable;
|
||||
assign ValidPTE = Valid && ~(Writable && ~Readable);
|
||||
assign AccessAlert = ~Accessed || (MemStore && ~Dirty);
|
||||
|
||||
generate
|
||||
if (`XLEN == 32) begin
|
||||
logic [9:0] VPN1, VPN0;
|
||||
|
||||
assign SvMode = SATP_REGW[31];
|
||||
|
||||
logic [9:0] VPN1 = TranslationVAdr[31:22];
|
||||
logic [9:0] VPN0 = TranslationVAdr[21:12]; // *** could optimize by not passing offset?
|
||||
|
||||
logic [33:0] TranslationPAdr;
|
||||
logic [21:0] CurrentPPN;
|
||||
|
||||
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;
|
||||
typedef enum {IDLE, LEVEL1, LEVEL0, LEAF, FAULT} walker_statetype;
|
||||
walker_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);
|
||||
flopenl #(.TYPE(walker_statetype)) mmureg(HCLK, ~HRESETn, 1'b1, NextWalkerState, IDLE, WalkerState);
|
||||
|
||||
// State transition logic
|
||||
always_comb begin
|
||||
case (WalkerState)
|
||||
IDLE: if (MMUTranslate) NextWalkerState = LEVEL1;
|
||||
@ -129,62 +157,59 @@ module pagetablewalker (
|
||||
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 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;
|
||||
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: begin
|
||||
TranslationPAdr <= {BasePageTablePPN, VPN1, 2'b00};
|
||||
end
|
||||
LEVEL0: begin
|
||||
TranslationPAdr <= {CurrentPPN, VPN0, 2'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
|
||||
assign VPN1 = TranslationVAdr[31:22];
|
||||
assign VPN0 = TranslationVAdr[21:12]; // *** could optimize by not passing offset?
|
||||
|
||||
// Interpret inputs from ahblite
|
||||
assign CurrentPPN = MMUReadPTE[31:10];
|
||||
// Assign combinational outputs
|
||||
always_comb begin
|
||||
// default values
|
||||
assign TranslationPAdr = '0;
|
||||
assign PageTableEntryF = '0;
|
||||
assign PageTableEntryM = '0;
|
||||
assign MMUTranslationComplete = '0;
|
||||
assign DTLBWriteM = '0;
|
||||
assign ITLBWriteF = '0;
|
||||
assign InstrPageFaultM = '0;
|
||||
assign LoadPageFaultM = '0;
|
||||
assign StorePageFaultM = '0;
|
||||
|
||||
case (NextWalkerState)
|
||||
LEVEL1: begin
|
||||
assign TranslationPAdr = {BasePageTablePPN, VPN1, 2'b00};
|
||||
end
|
||||
LEVEL0: begin
|
||||
assign TranslationPAdr = {CurrentPPN, VPN0, 2'b00};
|
||||
end
|
||||
LEAF: begin
|
||||
// Keep physical address alive to prevent HADDR dropping to 0
|
||||
assign TranslationPAdr = {CurrentPPN, VPN0, 2'b00};
|
||||
assign PageTableEntryF = CurrentPTE;
|
||||
assign PageTableEntryM = CurrentPTE;
|
||||
assign MMUTranslationComplete = '1;
|
||||
assign DTLBWriteM = DTLBMissM;
|
||||
assign ITLBWriteF = ~DTLBMissM; // Prefer data over instructions
|
||||
end
|
||||
FAULT: begin
|
||||
assign InstrPageFaultM = ~DTLBMissM;
|
||||
assign LoadPageFaultM = DTLBMissM && ~MemStore;
|
||||
assign StorePageFaultM = DTLBMissM && MemStore;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
|
||||
|
||||
// Capture page table entry from ahblite
|
||||
flopenr #(32) ptereg(HCLK, ~HRESETn, MMUReady, MMUReadPTE, SavedPTE);
|
||||
// *** Evil hack to get CurrentPTE a cycle early before it is saved.
|
||||
// Todo: Is it evil?
|
||||
mux2 #(32) ptemux(SavedPTE, MMUReadPTE, MMUReady, CurrentPTE);
|
||||
assign CurrentPPN = CurrentPTE[`PPN_BITS+9:10];
|
||||
|
||||
// Assign outputs to ahblite
|
||||
// *** Currently truncate address to 32 bits. This must be changed if
|
||||
@ -194,27 +219,19 @@ module pagetablewalker (
|
||||
end else begin
|
||||
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 [8:0] VPN2, VPN1, VPN0;
|
||||
|
||||
logic [55:0] TranslationPAdr;
|
||||
logic [43:0] CurrentPPN;
|
||||
logic GigapageMisaligned, BadGigapage;
|
||||
|
||||
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;
|
||||
typedef enum {IDLE, LEVEL2, LEVEL1, LEVEL0, LEAF, FAULT} walker_statetype;
|
||||
walker_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);
|
||||
flopenl #(.TYPE(walker_statetype)) mmureg(HCLK, ~HRESETn, 1'b1, NextWalkerState, IDLE, WalkerState);
|
||||
|
||||
always_comb begin
|
||||
case (WalkerState)
|
||||
IDLE: if (MMUTranslate) NextWalkerState = LEVEL1;
|
||||
IDLE: if (MMUTranslate) NextWalkerState = LEVEL2;
|
||||
else NextWalkerState = IDLE;
|
||||
LEVEL2: if (~MMUReady) NextWalkerState = LEVEL2;
|
||||
else if (ValidPTE && ~LeafPTE) NextWalkerState = LEVEL1;
|
||||
@ -237,67 +254,65 @@ module pagetablewalker (
|
||||
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].
|
||||
// A gigapage is a Level 2 leaf page. This page must have zero PPN[1] and
|
||||
// zero PPN[0]
|
||||
assign GigapageMisaligned = |(CurrentPPN[17:0]);
|
||||
// A megapage is a Level 1 leaf page. This page must have zero PPN[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
|
||||
assign VPN2 = TranslationVAdr[38:30];
|
||||
assign VPN1 = TranslationVAdr[29:21];
|
||||
assign VPN0 = TranslationVAdr[20:12]; // *** could optimize by not passing offset?
|
||||
|
||||
// Interpret inputs from ahblite
|
||||
assign CurrentPPN = MMUReadPTE[53:10];
|
||||
// *** Should translate this flop block into our flop module notation
|
||||
always_comb begin
|
||||
// default values
|
||||
assign TranslationPAdr = '0;
|
||||
assign PageTableEntryF = '0;
|
||||
assign PageTableEntryM = '0;
|
||||
assign MMUTranslationComplete = '0;
|
||||
assign DTLBWriteM = '0;
|
||||
assign ITLBWriteF = '0;
|
||||
assign InstrPageFaultM = '0;
|
||||
assign LoadPageFaultM = '0;
|
||||
assign StorePageFaultM = '0;
|
||||
|
||||
case (NextWalkerState)
|
||||
LEVEL2: begin
|
||||
assign TranslationPAdr = {BasePageTablePPN, VPN2, 3'b000};
|
||||
end
|
||||
LEVEL1: begin
|
||||
assign TranslationPAdr = {CurrentPPN, VPN1, 3'b000};
|
||||
end
|
||||
LEVEL0: begin
|
||||
assign TranslationPAdr = {CurrentPPN, VPN0, 3'b000};
|
||||
end
|
||||
LEAF: begin
|
||||
// Keep physical address alive to prevent HADDR dropping to 0
|
||||
assign TranslationPAdr = {CurrentPPN, VPN0, 3'b000};
|
||||
assign PageTableEntryF = CurrentPTE;
|
||||
assign PageTableEntryM = CurrentPTE;
|
||||
assign MMUTranslationComplete = '1;
|
||||
assign DTLBWriteM = DTLBMissM;
|
||||
assign ITLBWriteF = ~DTLBMissM; // Prefer data over instructions
|
||||
end
|
||||
FAULT: begin
|
||||
assign InstrPageFaultM = ~DTLBMissM;
|
||||
assign LoadPageFaultM = DTLBMissM && ~MemStore;
|
||||
assign StorePageFaultM = DTLBMissM && MemStore;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
// Capture page table entry from ahblite
|
||||
flopenr #(`XLEN) ptereg(HCLK, ~HRESETn, MMUReady, MMUReadPTE, SavedPTE);
|
||||
// *** Evil hack to get CurrentPTE a cycle early before it is saved.
|
||||
// Todo: Is it evil?
|
||||
mux2 #(`XLEN) ptemux(SavedPTE, MMUReadPTE, MMUReady, CurrentPTE);
|
||||
assign CurrentPPN = CurrentPTE[`PPN_BITS+9:10];
|
||||
|
||||
// Assign outputs to ahblite
|
||||
// *** Currently truncate address to 32 bits. This must be changed if
|
||||
|
@ -80,7 +80,7 @@ module ifu (
|
||||
// logic [`XLEN-1:0] PageTableEntryF = '0;
|
||||
logic ITLBFlushF = '0;
|
||||
// logic ITLBWriteF = '0;
|
||||
tlb #(3) itlb(clk, reset, SATP_REGW, PrivilegeModeW, PCF, PageTableEntryF, ITLBWriteF, ITLBFlushF,
|
||||
tlb #(3) itlb(clk, reset, SATP_REGW, PrivilegeModeW, 1'b1, PCF, PageTableEntryF, ITLBWriteF, ITLBFlushF,
|
||||
InstrPAdrF, ITLBMissF, ITLBHitF);
|
||||
|
||||
// branch predictor signals
|
||||
@ -96,7 +96,6 @@ module ifu (
|
||||
|
||||
assign PrivilegedChangePCM = RetM | TrapM;
|
||||
|
||||
|
||||
//mux3 #(`XLEN) pcmux(PCPlus2or4F, PCCorrectE, PrivilegedNextPCM, {PrivilegedChangePCM, BPPredWrongE}, UnalignedPCNextF);
|
||||
mux2 #(`XLEN) pcmux0(.d0(PCPlus2or4F),
|
||||
.d1(BPPredPCF),
|
||||
|
@ -63,6 +63,9 @@ module tlb #(parameter ENTRY_BITS = 3) (
|
||||
// Current privilege level of the processeor
|
||||
input [1:0] PrivilegeModeW,
|
||||
|
||||
// High if the TLB is currently being accessed
|
||||
input TLBAccess,
|
||||
|
||||
// Virtual address input
|
||||
input [`XLEN-1:0] VirtualAddress,
|
||||
|
||||
@ -82,6 +85,7 @@ module tlb #(parameter ENTRY_BITS = 3) (
|
||||
logic SvMode;
|
||||
logic Translate;
|
||||
|
||||
/*
|
||||
generate
|
||||
if (`XLEN == 32) begin
|
||||
assign SvMode = SATP_REGW[31]; // *** change to an enum somehow?
|
||||
@ -89,9 +93,10 @@ module tlb #(parameter ENTRY_BITS = 3) (
|
||||
assign SvMode = SATP_REGW[63]; // currently just a boolean whether translation enabled
|
||||
end
|
||||
endgenerate
|
||||
*/
|
||||
// *** Currently fake virtual memory being on for testing purposes
|
||||
// *** DO NOT ENABLE UNLESS TESTING
|
||||
// assign SvMode = 1;
|
||||
assign SvMode = 1;
|
||||
|
||||
assign Translate = SvMode & (PrivilegeModeW != `M_MODE);
|
||||
|
||||
@ -118,6 +123,8 @@ module tlb #(parameter ENTRY_BITS = 3) (
|
||||
// Page table entry matching the virtual address
|
||||
logic [`XLEN-1:0] PageTableEntry;
|
||||
|
||||
logic CAMHit;
|
||||
|
||||
assign VirtualPageNumber = VirtualAddress[`VPN_BITS+11:12];
|
||||
assign PageOffset = VirtualAddress[11:0];
|
||||
|
||||
@ -149,7 +156,8 @@ module tlb #(parameter ENTRY_BITS = 3) (
|
||||
end
|
||||
endgenerate
|
||||
|
||||
assign TLBMiss = ~TLBHit & ~(TLBWrite | TLBFlush) & Translate;
|
||||
assign TLBHit = CAMHit & TLBAccess;
|
||||
assign TLBMiss = ~TLBHit & ~TLBFlush & Translate & TLBAccess;
|
||||
endmodule
|
||||
|
||||
module tlb_ram #(parameter ENTRY_BITS = 3) (
|
||||
@ -185,7 +193,7 @@ module tlb_cam #(parameter ENTRY_BITS = 3,
|
||||
input TLBWrite,
|
||||
input TLBFlush,
|
||||
output [ENTRY_BITS-1:0] VPNIndex,
|
||||
output TLBHit
|
||||
output CAMHit
|
||||
);
|
||||
|
||||
localparam NENTRIES = 2**ENTRY_BITS;
|
||||
@ -220,7 +228,7 @@ module tlb_cam #(parameter ENTRY_BITS = 3,
|
||||
end
|
||||
|
||||
assign VPNIndex = matched_address_comb;
|
||||
assign TLBHit = match_found_comb & ~(TLBWrite | TLBFlush);
|
||||
assign CAMHit = match_found_comb & ~TLBFlush;
|
||||
|
||||
initial begin
|
||||
for (int i = 0; i < NENTRIES; i++)
|
||||
@ -236,6 +244,6 @@ module tlb_rand #(parameter ENTRY_BITS = 3) (
|
||||
|
||||
logic [31:0] data;
|
||||
assign data = $urandom;
|
||||
assign WriteIndex = data[ENTRY_BITS:0];
|
||||
assign WriteIndex = data[ENTRY_BITS-1:0];
|
||||
|
||||
endmodule
|
||||
|
@ -89,7 +89,7 @@ module csr (
|
||||
assign UnalignedNextEPCM = TrapM ? PCM : CSRWriteValM;
|
||||
assign NextEPCM = `C_SUPPORTED ? {UnalignedNextEPCM[`XLEN-1:1], 1'b0} : {UnalignedNextEPCM[`XLEN-1:2], 2'b00}; // 3.1.15 alignment
|
||||
assign NextCauseM = TrapM ? CauseM : CSRWriteValM;
|
||||
assign NextMtvalM = TrapM? NextFaultMtvalM : CSRWriteValM;
|
||||
assign NextMtvalM = TrapM ? NextFaultMtvalM : CSRWriteValM;
|
||||
assign CSRMWriteM = CSRWriteM && (PrivilegeModeW == `M_MODE);
|
||||
assign CSRSWriteM = CSRWriteM && (PrivilegeModeW[0]);
|
||||
assign CSRUWriteM = CSRWriteM;
|
||||
|
@ -354,7 +354,7 @@ string tests32i[] = {
|
||||
else tests = {tests, tests64iNOc};
|
||||
if (`M_SUPPORTED) tests = {tests, tests64m};
|
||||
if (`A_SUPPORTED) tests = {tests, tests64a};
|
||||
//if (`MEM_VIRTMEM) tests = {tests, tests64mmu};
|
||||
if (`MEM_VIRTMEM) tests = {tests, tests64mmu};
|
||||
end
|
||||
// tests = {tests64a, tests};
|
||||
end else begin // RV32
|
||||
@ -364,7 +364,7 @@ string tests32i[] = {
|
||||
else tests = {tests, tests32iNOc};
|
||||
if (`M_SUPPORTED % 2 == 1) tests = {tests, tests32m};
|
||||
if (`A_SUPPORTED) tests = {tests, tests32a};
|
||||
//if (`MEM_VIRTMEM) tests = {tests32mmu, tests};
|
||||
if (`MEM_VIRTMEM) tests = {tests32mmu, tests};
|
||||
end
|
||||
string signame, memfilename;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user