From 7126ab7864d069063523f6ab66f70323d9e82235 Mon Sep 17 00:00:00 2001 From: Thomas Fleming Date: Tue, 30 Mar 2021 22:19:27 -0400 Subject: [PATCH] Complete basic page table walker --- wally-pipelined/config/rv32ic/wally-config.vh | 4 +- wally-pipelined/config/rv64ic/wally-config.vh | 2 +- .../config/rv64icfd/wally-config.vh | 2 +- wally-pipelined/src/dmem/dmem.sv | 10 +- wally-pipelined/src/ebu/ahblite.sv | 31 +- wally-pipelined/src/ebu/pagetablewalker.sv | 313 +++++++++--------- wally-pipelined/src/ifu/ifu.sv | 3 +- wally-pipelined/src/mmu/tlb.sv | 18 +- wally-pipelined/src/privileged/csr.sv | 2 +- .../testbench/testbench-imperas.sv | 4 +- 10 files changed, 208 insertions(+), 181 deletions(-) diff --git a/wally-pipelined/config/rv32ic/wally-config.vh b/wally-pipelined/config/rv32ic/wally-config.vh index 5ee1d170..0c820d00 100644 --- a/wally-pipelined/config/rv32ic/wally-config.vh +++ b/wally-pipelined/config/rv32ic/wally-config.vh @@ -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 diff --git a/wally-pipelined/config/rv64ic/wally-config.vh b/wally-pipelined/config/rv64ic/wally-config.vh index c3f9849a..1288889f 100644 --- a/wally-pipelined/config/rv64ic/wally-config.vh +++ b/wally-pipelined/config/rv64ic/wally-config.vh @@ -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 diff --git a/wally-pipelined/config/rv64icfd/wally-config.vh b/wally-pipelined/config/rv64icfd/wally-config.vh index 9b3dae29..6a0f56d2 100644 --- a/wally-pipelined/config/rv64icfd/wally-config.vh +++ b/wally-pipelined/config/rv64icfd/wally-config.vh @@ -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 diff --git a/wally-pipelined/src/dmem/dmem.sv b/wally-pipelined/src/dmem/dmem.sv index d1c11d93..d23e16e9 100644 --- a/wally-pipelined/src/dmem/dmem.sv +++ b/wally-pipelined/src/dmem/dmem.sv @@ -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 diff --git a/wally-pipelined/src/ebu/ahblite.sv b/wally-pipelined/src/ebu/ahblite.sv index 90ef018b..45a51812 100644 --- a/wally-pipelined/src/ebu/ahblite.sv +++ b/wally-pipelined/src/ebu/ahblite.sv @@ -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; diff --git a/wally-pipelined/src/ebu/pagetablewalker.sv b/wally-pipelined/src/ebu/pagetablewalker.sv index 16bb9ae2..c90cff24 100644 --- a/wally-pipelined/src/ebu/pagetablewalker.sv +++ b/wally-pipelined/src/ebu/pagetablewalker.sv @@ -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 diff --git a/wally-pipelined/src/ifu/ifu.sv b/wally-pipelined/src/ifu/ifu.sv index eb65e167..a19991da 100644 --- a/wally-pipelined/src/ifu/ifu.sv +++ b/wally-pipelined/src/ifu/ifu.sv @@ -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), diff --git a/wally-pipelined/src/mmu/tlb.sv b/wally-pipelined/src/mmu/tlb.sv index 77b5efba..d510eb2c 100644 --- a/wally-pipelined/src/mmu/tlb.sv +++ b/wally-pipelined/src/mmu/tlb.sv @@ -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 diff --git a/wally-pipelined/src/privileged/csr.sv b/wally-pipelined/src/privileged/csr.sv index 0c38b688..6744f874 100644 --- a/wally-pipelined/src/privileged/csr.sv +++ b/wally-pipelined/src/privileged/csr.sv @@ -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; diff --git a/wally-pipelined/testbench/testbench-imperas.sv b/wally-pipelined/testbench/testbench-imperas.sv index f50f700c..48bfbdf9 100644 --- a/wally-pipelined/testbench/testbench-imperas.sv +++ b/wally-pipelined/testbench/testbench-imperas.sv @@ -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;