From 5187574e8a45a3a993e88aafb0fda0b372c82e1f Mon Sep 17 00:00:00 2001 From: Kip Macsai-Goren Date: Tue, 1 Jun 2021 17:50:37 -0400 Subject: [PATCH] implemented Sv48. --- wally-pipelined/src/mmu/cam_line.sv | 20 +++-- wally-pipelined/src/mmu/page_number_mixer.sv | 87 +++++++++++++++----- wally-pipelined/src/mmu/pagetablewalker.sv | 60 ++++++++++---- wally-pipelined/src/mmu/tlb.sv | 33 +++++--- wally-pipelined/src/mmu/tlb_cam.sv | 25 +++--- 5 files changed, 160 insertions(+), 65 deletions(-) diff --git a/wally-pipelined/src/mmu/cam_line.sv b/wally-pipelined/src/mmu/cam_line.sv index b7577573..6bab0b60 100644 --- a/wally-pipelined/src/mmu/cam_line.sv +++ b/wally-pipelined/src/mmu/cam_line.sv @@ -2,7 +2,9 @@ // cam_line.sv // // Written: tfleming@hmc.edu & jtorrey@hmc.edu 6 April 2021 -// Modified: +// Modified: kmacsaigoren@hmc.edu 1 June 2021 +// Implemented SV48 on top of SV39. This included adding SvMode input signal and the wally constants +// Mostly this was done to make the PageNumberMixer work. // // Purpose: CAM line for the translation lookaside buffer (TLB) // Determines whether a virtual address matches the stored key. @@ -24,12 +26,17 @@ // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////// +`include "wally-constants.vh" + module cam_line #(parameter KEY_BITS = 20, parameter HIGH_SEGMENT_BITS = 10) ( input clk, reset, + // input to scheck which SvMode is running + input [`SVMODE_BITS-1:0] SvMode, + // The requested page number to compare against the key - input [KEY_BITS-1:0] VirtualPageNumber, + input [KEY_BITS-1:0] VirtualPageNumber, // Signals to write a new entry to this line input CAMLineWrite, @@ -38,10 +45,11 @@ module cam_line #(parameter KEY_BITS = 20, // Flush this line (set valid to 0) input TLBFlush, - // This entry is a key for a giga, mega, or kilopage. + // This entry is a key for a tera, giga, mega, or kilopage. // PageType == 2'b00 --> kilopage // PageType == 2'b01 --> megapage - // PageType == 2'b11 --> gigapage + // PageType == 2'b10 --> gigapage + // PageType == 2'b11 --> terapage output [1:0] PageType, // *** should this be the stored version or the always updated one? output Match ); @@ -67,9 +75,9 @@ module cam_line #(parameter KEY_BITS = 20, flopenr #(KEY_BITS) keyflop(clk, reset, CAMLineWrite, VirtualPageNumber, Key); // Calculate the actual query key based on the input key and the page type. - // For example, a megapage in sv39 only cares about VPN2 and VPN1, so VPN0 + // For example, a megapage in SV39 only cares about VPN2 and VPN1, so VPN0 // should automatically match. - page_number_mixer #(KEY_BITS, HIGH_SEGMENT_BITS) mixer(VirtualPageNumber, Key, PageType, VirtualPageNumberQuery); + page_number_mixer #(KEY_BITS, HIGH_SEGMENT_BITS) mixer(VirtualPageNumber, Key, PageType, SvMode, VirtualPageNumberQuery); assign Match = ({1'b1, VirtualPageNumberQuery} == {Valid, Key}); diff --git a/wally-pipelined/src/mmu/page_number_mixer.sv b/wally-pipelined/src/mmu/page_number_mixer.sv index 57b8e4b7..03851018 100644 --- a/wally-pipelined/src/mmu/page_number_mixer.sv +++ b/wally-pipelined/src/mmu/page_number_mixer.sv @@ -2,7 +2,11 @@ // page_number_mixer.sv // // Written: tfleming@hmc.edu & jtorrey@hmc.edu 6 April 2021 -// Modified: +// Modified: kmacsaigoren@hmc.edu 1 June 2021 +// Implemented SV48 on top of SV39. This included adding a 3rd Segment to each of the pagenumbers, +// Ensuring that the BITS and HIGH_SEGMENT_BITS inputs were correct everywhere this module gets instatniated, +// Adding seveeral muxes to decide the bit selection to turn pagenumbers into segments based on SV mode, +// Adding support for terapage/newgigapage encoding. // // Purpose: Takes two page numbers and replaces segments of the first page // number with segments from the second, based on the page type. @@ -25,22 +29,29 @@ /////////////////////////////////////////// `include "wally-config.vh" +`include "wally-constants.vh" module page_number_mixer #(parameter BITS = 20, parameter HIGH_SEGMENT_BITS = 10) ( - input [BITS-1:0] PageNumber, - input [BITS-1:0] MixPageNumber, - input [1:0] PageType, - output [BITS-1:0] PageNumberCombined + input [BITS-1:0] PageNumber, + input [BITS-1:0] MixPageNumber, + input [1:0] PageType, + input [`SVMODE_BITS-1:0] SvMode, + + output [BITS-1:0] PageNumberCombined ); + // The upper segment might have a different width than the lower segments. + // For example, an SV39 PTE has 26 bits for PPN2 and 9 bits for the other + // segments. This is outside the 'if XLEN' b/c the constant is already configured + // to the correct value for the XLEN in the relevant wally-constants.vh file. + localparam LOW_SEGMENT_BITS = `VPN_SEGMENT_BITS; + // *** each time this module is implemented, low segment bits is either + // `VPN_SEGMENT_BITS or `PPN_LOW_SEGMENT_BITS (if it existed) + // in every mode so far, these are the same, so it's left as it is above. + generate - // *** Just checking XLEN is not enough to support sv39 AND sv48. if (`XLEN == 32) begin - // The upper segment might have a different width than the lower segments. - // For example, an sv39 PTE has 26 bits for PPN2 and 9 bits for the other - // segments. - localparam LOW_SEGMENT_BITS = (BITS - HIGH_SEGMENT_BITS); logic [HIGH_SEGMENT_BITS-1:0] Segment1, MixSegment1, Segment1Combined; logic [LOW_SEGMENT_BITS-1:0] Segment0, MixSegment0, Segment0Combined; @@ -58,28 +69,60 @@ module page_number_mixer #(parameter BITS = 20, // Reswizzle segments of the combined page number assign PageNumberCombined = {Segment1Combined, Segment0Combined}; end else begin - // The upper segment might have a different width than the lower segments. - // For example, an sv39 PTE has 26 bits for PPN2 and 9 bits for the other - // segments. - localparam LOW_SEGMENT_BITS = (BITS - HIGH_SEGMENT_BITS) / 2; - logic [HIGH_SEGMENT_BITS-1:0] Segment2, MixSegment2, Segment2Combined; + // After segment 0 and 1 of the page number, the width of each segment is dependant on the SvMode. + // For this reason, each segment bus is the width of its widest value across each mode + // when a smaller value needs to be loaded in to a wider bus, it's loaded in the least significant bits + // and left padded with zeros. MAKE SURE that if a value is being padded with zeros here, + // that it's padded with zeros everywhere else in the MMU ans beyond to avoid false misses in the TLB. + logic [HIGH_SEGMENT_BITS-1:0] Segment3, MixSegment3, Segment3Combined; + logic [HIGH_SEGMENT_BITS + LOW_SEGMENT_BITS-1:0] Segment2, MixSegment2, Segment2Combined; logic [LOW_SEGMENT_BITS-1:0] Segment1, MixSegment1, Segment1Combined; logic [LOW_SEGMENT_BITS-1:0] Segment0, MixSegment0, Segment0Combined; + // Unswizzle segments of the input page number - assign {Segment2, Segment1, Segment0} = PageNumber; - assign {MixSegment2, MixSegment1, MixSegment0} = MixPageNumber; + // *** these muxes assume that only Sv48 and SV39 are implemented in rv64. for future SV57 and up, + // there will have to be more muxes to select which value each segment gets. + // as a cool reminder: BITS is the width of the page number, virt or phys, coming into this module + // while high segment bits is the width of the highest segment of that page number. + // Note for future work: this module has to work with both VPNs and PPNs and due to their differing + // widths and the fact that the ppn has one longer segment at the top makes the muxes below very confusing. + // Potentially very annoying thing for future workers: the number of bits in a ppn is always 44 (for SV39 and48) + // but in SV57 and above, this might be a new longer length. In that case these selectors will most likely + // become even more complicated and confusing. + assign Segment3 = (SvMode == `SV48) ? + PageNumber[BITS-1:3*LOW_SEGMENT_BITS] : // take the top segment or not + {HIGH_SEGMENT_BITS{1'b0}}; // for virtual page numbers in SV39, both options should be zeros. + assign Segment2 = (SvMode == `SV48) ? + {{HIGH_SEGMENT_BITS{1'b0}}, PageNumber[3*LOW_SEGMENT_BITS-1:2*LOW_SEGMENT_BITS]} : // just take another low segment left padded with zeros. + PageNumber[BITS-1:2*LOW_SEGMENT_BITS]; // otherwise take the rest of the PageNumber + assign Segment1 = PageNumber[2*LOW_SEGMENT_BITS-1:LOW_SEGMENT_BITS]; + assign Segment0 = PageNumber[LOW_SEGMENT_BITS-1:0]; + + + assign MixSegment3 = (SvMode == `SV48) ? + MixPageNumber[BITS-1:3*LOW_SEGMENT_BITS] : // take the top segment or not + {HIGH_SEGMENT_BITS{1'b0}}; // for virtual page numbers in SV39, both options should be zeros. + assign MixSegment2 = (SvMode == `SV48) ? + {{HIGH_SEGMENT_BITS{1'b0}}, MixPageNumber[3*LOW_SEGMENT_BITS-1:2*LOW_SEGMENT_BITS]} : // just take another low segment left padded with zeros. + MixPageNumber[BITS-1:2*LOW_SEGMENT_BITS]; // otherwise take the rest of the PageNumber + assign MixSegment1 = MixPageNumber[2*LOW_SEGMENT_BITS-1:LOW_SEGMENT_BITS]; + assign MixSegment0 = MixPageNumber[LOW_SEGMENT_BITS-1:0]; + // Pass through the high segment - assign Segment2Combined = Segment2; + assign Segment3Combined = Segment3; - // Either pass through or zero out segments 1 and 0 based on the page type - mux2 #(LOW_SEGMENT_BITS) segment1mux(Segment1, MixSegment1, PageType[1], Segment1Combined); - mux2 #(LOW_SEGMENT_BITS) segment0mux(Segment0, MixSegment0, PageType[0], Segment0Combined); + // Either pass through or zero out lower segments based on the page type + assign Segment2Combined = (PageType[1] && PageType[0]) ? MixSegment2 : Segment2; // terapage (page == 11) + assign Segment1Combined = (PageType[1]) ? MixSegment1 : Segment1; // gigapage and higher (page == 10 or 11) + assign Segment0Combined = (PageType[1] || PageType[0]) ? MixSegment0 : Segment0; // megapage and higher (page == 01 or 10 or 11) // Reswizzle segments of the combined page number - assign PageNumberCombined = {Segment2Combined, Segment1Combined, Segment0Combined}; + assign PageNumberCombined = (SvMode == `SV48) ? + {Segment3Combined, Segment2Combined[LOW_SEGMENT_BITS-1:0], Segment1Combined, Segment0Combined} : + {Segment2Combined, Segment1Combined, Segment0Combined}; end endgenerate endmodule diff --git a/wally-pipelined/src/mmu/pagetablewalker.sv b/wally-pipelined/src/mmu/pagetablewalker.sv index f2aada44..b0e4fe8e 100644 --- a/wally-pipelined/src/mmu/pagetablewalker.sv +++ b/wally-pipelined/src/mmu/pagetablewalker.sv @@ -2,7 +2,10 @@ // pagetablewalker.sv // // Written: tfleming@hmc.edu 2 March 2021 -// Modified: +// 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 // // Purpose: Page Table Walker // Part of the Memory Management Unit (MMU) @@ -70,6 +73,7 @@ module pagetablewalker ( logic [`XLEN-1:0] SavedPTE, CurrentPTE; logic [`PA_BITS-1:0] TranslationPAdr; logic [`PPN_BITS-1:0] CurrentPPN; + logic [`SVMODE_BITS-1:0] SvMode; logic MemStore; // PTE Control Bits @@ -82,6 +86,8 @@ module pagetablewalker ( logic [`XLEN-1:0] PageTableEntry; logic [1:0] PageType; + assign SvMode = SATP_REGW[`XLEN-1:`XLEN-`SVMODE_BITS]; + assign BasePageTablePPN = SATP_REGW[`PPN_BITS-1:0]; assign MemStore = MemRWM[0]; @@ -105,11 +111,12 @@ module pagetablewalker ( assign PageTypeF = PageType; assign PageTypeM = PageType; - localparam IDLE = 3'h0; + localparam LEVEL0 = 3'h0; localparam LEVEL1 = 3'h1; - localparam LEVEL0 = 3'h2; - localparam LEAF = 3'h3; - localparam FAULT = 3'h4; + // space left for more levels + localparam LEAF = 3'h5; + localparam IDLE = 3'h6; + localparam FAULT = 3'h7; logic [2:0] WalkerState, NextWalkerState; @@ -208,18 +215,32 @@ module pagetablewalker ( assign MMUPAdr = TranslationPAdr[31:0]; end else begin - localparam LEVEL2 = 3'h5; + localparam LEVEL2 = 3'h2; + localparam LEVEL3 = 3'h3; - logic [8:0] VPN2, VPN1, VPN0; + logic [8:0] VPN3, VPN2, VPN1, VPN0; - logic GigapageMisaligned, BadGigapage; + logic TerapageMisaligned, GigapageMisaligned, BadTerapage, BadGigapage; flopenl #(3) mmureg(HCLK, ~HRESETn, 1'b1, NextWalkerState, IDLE, WalkerState); always_comb begin case (WalkerState) - IDLE: if (MMUTranslate) NextWalkerState = LEVEL2; + IDLE: if (MMUTranslate) NextWalkerState = LEVEL3; else NextWalkerState = IDLE; + LEVEL3: if (SvMode != `SV48) NextWalkerState = LEVEL2; + // 3rd level used if SV48 is enabled. + else begin + if (~MMUReady) NextWalkerState = LEVEL3; + // *** According to the architecture, we should + // fault upon finding a superpage that is misaligned or has 0 + // access bit. The following commented line of code is + // supposed to perform that check. However, it is untested. + else if (ValidPTE && LeafPTE && ~BadTerapage) NextWalkerState = LEAF; + // else if (ValidPTE && LeafPTE) NextWalkerState = LEAF; // *** Once the above line is properly tested, delete this line. + else if (ValidPTE && ~LeafPTE) NextWalkerState = LEVEL2; + else NextWalkerState = FAULT; + end LEVEL2: if (~MMUReady) NextWalkerState = LEVEL2; // *** According to the architecture, we should // fault upon finding a superpage that is misaligned or has 0 @@ -242,24 +263,29 @@ module pagetablewalker ( else if (ValidPTE && LeafPTE && ~AccessAlert) NextWalkerState = LEAF; else NextWalkerState = FAULT; - LEAF: if (MMUTranslate) NextWalkerState = LEVEL2; + LEAF: if (MMUTranslate) NextWalkerState = LEVEL3; else NextWalkerState = IDLE; - FAULT: if (MMUTranslate) NextWalkerState = LEVEL2; + FAULT: if (MMUTranslate) NextWalkerState = LEVEL3; else NextWalkerState = IDLE; // Default case should never happen, but is included for linter. default: NextWalkerState = IDLE; endcase end + // A terapage is a level 3 leaf page. This page must have zero PPN[2], + // zero PPN[1], and zero PPN[0] + assign TerapageMisaligned = |(CurrentPPN[26: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 BadTerapage = TerapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme assign BadGigapage = GigapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme assign BadMegapage = MegapageMisaligned || AccessAlert; // *** Implement better access/dirty scheme + assign VPN3 = TranslationVAdr[47:39]; assign VPN2 = TranslationVAdr[38:30]; assign VPN1 = TranslationVAdr[29:21]; assign VPN0 = TranslationVAdr[20:12]; @@ -282,8 +308,13 @@ module pagetablewalker ( IDLE: begin MMUStall = '0; end + LEVEL3: begin + TranslationPAdr = {BasePageTablePPN, VPN3, 3'b000}; + // *** this is a huge breaking point. if we're going through level3 every time, even when sv48 is off, + // what should translationPAdr be when level3 is just off? + end LEVEL2: begin - TranslationPAdr = {BasePageTablePPN, VPN2, 3'b000}; + TranslationPAdr = {(SvMode == `SV48) ? CurrentPPN : BasePageTablePPN, VPN2, 3'b000}; end LEVEL1: begin TranslationPAdr = {CurrentPPN, VPN1, 3'b000}; @@ -295,8 +326,9 @@ module pagetablewalker ( // Keep physical address alive to prevent HADDR dropping to 0 TranslationPAdr = {CurrentPPN, VPN0, 3'b000}; PageTableEntry = CurrentPTE; - PageType = (WalkerState == LEVEL2) ? 2'b11 : - ((WalkerState == LEVEL1) ? 2'b01 : 2'b00); + PageType = (WalkerState == LEVEL3) ? 2'b11 : + ((WalkerState == LEVEL2) ? 2'b10 : + ((WalkerState == LEVEL1) ? 2'b01 : 2'b00)); DTLBWriteM = DTLBMissM; ITLBWriteF = ~DTLBMissM; // Prefer data over instructions end diff --git a/wally-pipelined/src/mmu/tlb.sv b/wally-pipelined/src/mmu/tlb.sv index 7ed594e4..1828c98e 100644 --- a/wally-pipelined/src/mmu/tlb.sv +++ b/wally-pipelined/src/mmu/tlb.sv @@ -2,7 +2,9 @@ // tlb.sv // // Written: jtorrey@hmc.edu 16 February 2021 -// Modified: +// Modified: kmacsaigoren@hmc.edu 1 June 2021 +// Implemented SV48 on top of SV39. This included adding the SvMode signal, +// and using it to decide the translate signal and get the virtual page number // // Purpose: Translation lookaside buffer // Cache of virtural-to-physical address translations @@ -25,7 +27,7 @@ /////////////////////////////////////////// /** - * sv32 specs + * SV32 specs * ---------- * Virtual address [31:0] (32 bits) * [________________________________] @@ -85,14 +87,11 @@ module tlb #(parameter ENTRY_BITS = 3, output TLBPageFault ); - logic SvMode; logic Translate; logic TLBAccess, ReadAccess, WriteAccess; - // *** If we want to support multiple virtual memory modes (ie sv39 AND sv48), - // we could have some muxes that control which parameters are current. - // Although then some of the signals are not big enough. But that's a problem - // for much later. + // Store current virtual memory mode (SV32, SV39, SV48, ect...) + logic [`SVMODE_BITS-1:0] SvMode; // Index (currently random) to write the next TLB entry logic [ENTRY_BITS-1:0] WriteIndex; @@ -116,17 +115,24 @@ module tlb #(parameter ENTRY_BITS = 3, // Whether the virtual address has a match in the CAM logic CAMHit; - // Grab the sv bit from SATP + // Grab the sv mode from SATP + assign SvMode = SATP_REGW[`XLEN-1:`XLEN-`SVMODE_BITS]; + + // The bus width is always the largest it could be for that XLEN. For example, vpn will be 36 bits wide in rv64 + // this, even though it could be 27 bits (SV39) or 36 bits (SV48) wide. When the value of VPN is narrower, + // is shorter, the extra bits are used as padded zeros on the left of the full value. generate if (`XLEN == 32) begin - assign SvMode = SATP_REGW[31]; // *** change to an enum somehow? + assign VirtualPageNumber = VirtualAddress[`VPN_BITS+11:12]; end else begin - assign SvMode = SATP_REGW[63]; // currently just a boolean whether translation enabled + assign VirtualPageNumber = (SvMode == `SV48) ? + VirtualAddress[`VPN_BITS+11:12] : + {{`VPN_SEGMENT_BITS{1'b0}}, VirtualAddress[3*`VPN_SEGMENT_BITS+11:12]}; end endgenerate // Whether translation should occur - assign Translate = SvMode & (PrivilegeModeW != `M_MODE); + assign Translate = (SvMode != `NO_TRANSLATE) & (PrivilegeModeW != `M_MODE); // Determine how the TLB is currently being used // Note that we use ReadAccess for both loads and instruction fetches @@ -134,7 +140,7 @@ module tlb #(parameter ENTRY_BITS = 3, assign WriteAccess = TLBAccessType[0]; assign TLBAccess = ReadAccess || WriteAccess; - assign VirtualPageNumber = VirtualAddress[`VPN_BITS+11:12]; + assign PageOffset = VirtualAddress[11:0]; // TLB entries are evicted according to the LRU algorithm @@ -188,9 +194,10 @@ module tlb #(parameter ENTRY_BITS = 3, // page number. For 4 KB pages, the entire virtual page number is replaced. // For superpages, some segments are considered offsets into a larger page. page_number_mixer #(`PPN_BITS, `PPN_HIGH_SEGMENT_BITS) - physical_mixer(PhysicalPageNumber, + physical_mixer(PhysicalPageNumber, {{EXTRA_PHYSICAL_BITS{1'b0}}, VirtualPageNumber}, HitPageType, + SvMode, PhysicalPageNumberMixed); // Provide physical address only on TLBHits to cause catastrophic errors if diff --git a/wally-pipelined/src/mmu/tlb_cam.sv b/wally-pipelined/src/mmu/tlb_cam.sv index 330bb382..78d9ff8d 100644 --- a/wally-pipelined/src/mmu/tlb_cam.sv +++ b/wally-pipelined/src/mmu/tlb_cam.sv @@ -2,7 +2,9 @@ // tlb_cam.sv // // Written: jtorrey@hmc.edu 16 February 2021 -// Modified: +// Modified: kmacsaigoren@hmc.edu 1 June 2021 +// Implemented SV48 on top of SV39. This included adding the SvMode signal input and wally constants +// Mostly this was to make the cam_lines work. // // Purpose: Stores virtual page numbers with cached translations. // Determines whether a given virtual page number is in the TLB. @@ -24,18 +26,21 @@ // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////// +`include "wally-constants.vh" + module tlb_cam #(parameter ENTRY_BITS = 3, parameter KEY_BITS = 20, parameter HIGH_SEGMENT_BITS = 10) ( - input clk, reset, - input [KEY_BITS-1:0] VirtualPageNumber, - input [1:0] PageTypeWrite, - input [ENTRY_BITS-1:0] WriteIndex, - input TLBWrite, - input TLBFlush, - output [ENTRY_BITS-1:0] VPNIndex, - output [1:0] HitPageType, - output CAMHit + input clk, reset, + input [KEY_BITS-1:0] VirtualPageNumber, + input [1:0] PageTypeWrite, + input [ENTRY_BITS-1:0] WriteIndex, + input [`SVMODE_BITS-1:0] SvMode, + input TLBWrite, + input TLBFlush, + output [ENTRY_BITS-1:0] VPNIndex, + output [1:0] HitPageType, + output CAMHit ); localparam NENTRIES = 2**ENTRY_BITS;