--- /dev/null
+# this file has been generated by sv2nmigen
+
+from nmigen import Signal, Module, Const, Cat, Elaboratable
+
+
+
+class tlb(Elaboratable):
+
+ def __init__(self):
+ self.clk_i = Signal() # input
+ self.rst_ni = Signal() # input
+ self.flush_i = Signal() # input
+ self.lu_access_i = Signal() # input
+ self.lu_asid_i = Signal(ASID_WIDTH) # input
+ self.lu_vaddr_i = Signal(64) # input
+ self.lu_is_2M_o = Signal() # output
+ self.lu_is_1G_o = Signal() # output
+ self.lu_hit_o = Signal() # output
+ def elaborate(self, platform=None):
+ m = Module()
+ return m
+
+#// Copyright 2018 ETH Zurich and University of Bologna.
+#// Copyright and related rights are licensed under the Solderpad Hardware
+#// License, Version 0.51 (the "License"); you may not use this file except in
+#// compliance with the License. You may obtain a copy of the License at
+#// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
+#// or agreed to in writing, software, hardware and materials distributed under
+#// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+#// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+#// specific language governing permissions and limitations under the License.
+#//
+#// Author: David Schaffenrath, TU Graz
+#// Author: Florian Zaruba, ETH Zurich
+#// Date: 21.4.2017
+#// Description: Translation Lookaside Buffer, SV39
+#// fully set-associative
+#
+#//import ariane_pkg::*;
+#
+#module tlb #(
+# parameter int TLB_ENTRIES = 4,
+# parameter int ASID_WIDTH = 1
+# )(
+# input logic clk_i, // Clock
+# input logic rst_ni, // Asynchronous reset active low
+# input logic flush_i, // Flush signal
+# // Update TLB
+# //input tlb_update_t update_i,
+# // Lookup signals
+# input logic lu_access_i,
+# input logic [ASID_WIDTH-1:0] lu_asid_i,
+# input logic [63:0] lu_vaddr_i,
+# //output riscv::pte_t lu_content_o,
+# output logic lu_is_2M_o,
+# output logic lu_is_1G_o,
+# output logic lu_hit_o
+#);
+#
+""" #docstring_begin
+ // SV39 defines three levels of page tables
+ struct packed {
+ logic [ASID_WIDTH-1:0] asid;
+ logic [8:0] vpn2;
+ logic [8:0] vpn1;
+ logic [8:0] vpn0;
+ logic is_2M;
+ logic is_1G;
+ logic valid;
+ } [TLB_ENTRIES-1:0] tags_q, tags_n;
+
+ riscv::pte_t [TLB_ENTRIES-1:0] content_q, content_n;
+ logic [8:0] vpn0, vpn1, vpn2;
+ logic [TLB_ENTRIES-1:0] lu_hit; // to replacement logic
+ logic [TLB_ENTRIES-1:0] replace_en; // replace the following entry, set by replacement strategy
+ //-------------
+ // Translation
+ //-------------
+ always_comb begin : translation
+ vpn0 = lu_vaddr_i[20:12];
+ vpn1 = lu_vaddr_i[29:21];
+ vpn2 = lu_vaddr_i[38:30];
+
+ // default assignment
+ lu_hit = '{default: 0};
+ lu_hit_o = 1'b0;
+ lu_content_o = '{default: 0};
+ lu_is_1G_o = 1'b0;
+ lu_is_2M_o = 1'b0;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ // first level match, this may be a giga page, check the ASID flags as well
+ if (tags_q[i].valid && lu_asid_i == tags_q[i].asid && vpn2 == tags_q[i].vpn2) begin
+ // second level
+ if (tags_q[i].is_1G) begin
+ lu_is_1G_o = 1'b1;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ // not a giga page hit so check further
+ end else if (vpn1 == tags_q[i].vpn1) begin
+ // this could be a 2 mega page hit or a 4 kB hit
+ // output accordingly
+ if (tags_q[i].is_2M || vpn0 == tags_q[i].vpn0) begin
+ lu_is_2M_o = tags_q[i].is_2M;
+ lu_content_o = content_q[i];
+ lu_hit_o = 1'b1;
+ lu_hit[i] = 1'b1;
+ end
+ end
+ end
+ end
+ end
+
+ // ------------------
+ // Update and Flush
+ // ------------------
+ always_comb begin : update_flush
+ tags_n = tags_q;
+ content_n = content_q;
+
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ if (flush_i) begin
+ // invalidate logic
+ if (lu_asid_i == 1'b0) // flush everything if ASID is 0
+ tags_n[i].valid = 1'b0;
+ else if (lu_asid_i == tags_q[i].asid) // just flush entries from this ASID
+ tags_n[i].valid = 1'b0;
+
+ // normal replacement
+ end else if (update_i.valid & replace_en[i]) begin
+ // update tag array
+ tags_n[i] = '{
+ asid: update_i.asid,
+ vpn2: update_i.vpn [26:18],
+ vpn1: update_i.vpn [17:9],
+ vpn0: update_i.vpn [8:0],
+ is_1G: update_i.is_1G,
+ is_2M: update_i.is_2M,
+ valid: 1'b1
+ };
+ // and content as well
+ content_n[i] = update_i.content;
+ end
+ end
+ end
+
+ // -----------------------------------------------
+ // PLRU - Pseudo Least Recently Used Replacement
+ // -----------------------------------------------
+ logic[2*(TLB_ENTRIES-1)-1:0] plru_tree_q, plru_tree_n;
+ always_comb begin : plru_replacement
+ plru_tree_n = plru_tree_q;
+ // The PLRU-tree indexing:
+ // lvl0 0
+ // / \
+ // / \
+ // lvl1 1 2
+ // / \ / \
+ // lvl2 3 4 5 6
+ // / \ /\/\ /\
+ // ... ... ... ...
+ // Just predefine which nodes will be set/cleared
+ // E.g. for a TLB with 8 entries, the for-loop is semantically
+ // equivalent to the following pseudo-code:
+ // unique case (1'b1)
+ // lu_hit[7]: plru_tree_n[0, 2, 6] = {1, 1, 1};
+ // lu_hit[6]: plru_tree_n[0, 2, 6] = {1, 1, 0};
+ // lu_hit[5]: plru_tree_n[0, 2, 5] = {1, 0, 1};
+ // lu_hit[4]: plru_tree_n[0, 2, 5] = {1, 0, 0};
+ // lu_hit[3]: plru_tree_n[0, 1, 4] = {0, 1, 1};
+ // lu_hit[2]: plru_tree_n[0, 1, 4] = {0, 1, 0};
+ // lu_hit[1]: plru_tree_n[0, 1, 3] = {0, 0, 1};
+ // lu_hit[0]: plru_tree_n[0, 1, 3] = {0, 0, 0};
+ // default: begin /* No hit */ end
+ // endcase
+ for (int unsigned i = 0; i < TLB_ENTRIES; i++) begin
+ automatic int unsigned idx_base, shift, new_index;
+ // we got a hit so update the pointer as it was least recently used
+ if (lu_hit[i] & lu_access_i) begin
+ // Set the nodes to the values we would expect
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+ // to circumvent the 32 bit integer arithmetic assignment
+ new_index = ~((i >> (shift-1)) & 32'b1);
+ plru_tree_n[idx_base + (i >> shift)] = new_index[0];
+ end
+ end
+ end
+ // Decode tree to write enable signals
+ // Next for-loop basically creates the following logic for e.g. an 8 entry
+ // TLB (note: pseudo-code obviously):
+ // replace_en[7] = &plru_tree_q[ 6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,1}
+ // replace_en[6] = &plru_tree_q[~6, 2, 0]; //plru_tree_q[0,2,6]=={1,1,0}
+ // replace_en[5] = &plru_tree_q[ 5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,1}
+ // replace_en[4] = &plru_tree_q[~5,~2, 0]; //plru_tree_q[0,2,5]=={1,0,0}
+ // replace_en[3] = &plru_tree_q[ 4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,1}
+ // replace_en[2] = &plru_tree_q[~4, 1,~0]; //plru_tree_q[0,1,4]=={0,1,0}
+ // replace_en[1] = &plru_tree_q[ 3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,1}
+ // replace_en[0] = &plru_tree_q[~3,~1,~0]; //plru_tree_q[0,1,3]=={0,0,0}
+ // For each entry traverse the tree. If every tree-node matches,
+ // the corresponding bit of the entry's index, this is
+ // the next entry to replace.
+ for (int unsigned i = 0; i < TLB_ENTRIES; i += 1) begin
+ automatic logic en;
+ automatic int unsigned idx_base, shift, new_index;
+ en = 1'b1;
+ for (int unsigned lvl = 0; lvl < $clog2(TLB_ENTRIES); lvl++) begin
+ idx_base = $unsigned((2**lvl)-1);
+ // lvl0 <=> MSB, lvl1 <=> MSB-1, ...
+ shift = $clog2(TLB_ENTRIES) - lvl;
+
+ // en &= plru_tree_q[idx_base + (i>>shift)] == ((i >> (shift-1)) & 1'b1);
+ new_index = (i >> (shift-1)) & 32'b1;
+ if (new_index[0]) begin
+ en &= plru_tree_q[idx_base + (i>>shift)];
+ end else begin
+ en &= ~plru_tree_q[idx_base + (i>>shift)];
+ end
+ end
+ replace_en[i] = en;
+ end
+ end
+
+ // sequential process
+ always_ff @(posedge clk_i or negedge rst_ni) begin
+ if(~rst_ni) begin
+ tags_q <= '{default: 0};
+ content_q <= '{default: 0};
+ plru_tree_q <= '{default: 0};
+ end else begin
+ tags_q <= tags_n;
+ content_q <= content_n;
+ plru_tree_q <= plru_tree_n;
+ end
+ end
+ //--------------
+ // Sanity checks
+ //--------------
+
+ //pragma translate_off
+ `ifndef VERILATOR
+
+ initial begin : p_assertions
+ assert ((TLB_ENTRIES % 2 == 0) && (TLB_ENTRIES > 1))
+ else begin $error("TLB size must be a multiple of 2 and greater than 1"); $stop(); end
+ assert (ASID_WIDTH >= 1)
+ else begin $error("ASID width must be at least 1"); $stop(); end
+ end
+
+ // Just for checking
+ function int countSetBits(logic[TLB_ENTRIES-1:0] vector);
+ automatic int count = 0;
+ foreach (vector[idx]) begin
+ count += vector[idx];
+ end
+ return count;
+ endfunction
+
+ assert property (@(posedge clk_i)(countSetBits(lu_hit) <= 1))
+ else begin $error("More then one hit in TLB!"); $stop(); end
+ assert property (@(posedge clk_i)(countSetBits(replace_en) <= 1))
+ else begin $error("More then one TLB entry selected for next replace!"); $stop(); end
+
+ `endif
+ //pragma translate_on
+"""
+#endmodule
+#
+#