add working preprocessor (creates docstrings)
authorTobias Platen <tplaten@posteo.de>
Sun, 3 Nov 2019 15:45:49 +0000 (16:45 +0100)
committerTobias Platen <tplaten@posteo.de>
Sun, 3 Nov 2019 15:45:49 +0000 (16:45 +0100)
absyn.py
examples/load_store_unit.sv [new file with mode: 0644]
examples/mmu.sv [new file with mode: 0644]
examples/tlb.py [new file with mode: 0644]
examples/tlb.sv [new file with mode: 0644]
parse_sv.py
pypreproc.py [new file with mode: 0644]
svparse.py

index 6ccbb0529944f48fcc57d1578f573bfcd8cffd10..7d54d6e3e76092384e6c00ff19e03f6cb0b2ddc3 100644 (file)
--- a/absyn.py
+++ b/absyn.py
@@ -148,9 +148,10 @@ class Absyn:
         return clsdecl
 
     def appendComments(self,data):
-        lines = data.split("\n")
-        for line in lines:
-            self.printpy("#"+line)
+        self.outputfile.write(data)
+        #lines = data.split("\n")
+        #for line in lines:
+        #    self.printpy("#"+line)
 
     # combinatorical assign
     def cont_assign_1(self,p):
diff --git a/examples/load_store_unit.sv b/examples/load_store_unit.sv
new file mode 100644 (file)
index 0000000..1d15633
--- /dev/null
@@ -0,0 +1,481 @@
+// 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: Florian Zaruba, ETH Zurich
+// Date: 19.04.2017
+// Description: Load Store Unit, handles address calculation and memory interface signals
+
+//import ariane_pkg::*;
+
+module load_store_unit #(
+    parameter int ASID_WIDTH = 1
+ //   parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
+)(
+    input  logic                     clk_i,
+    input  logic                     rst_ni,
+    input  logic                     flush_i,
+    output logic                     no_st_pending_o,
+    input  logic                     amo_valid_commit_i,
+
+
+    //input  fu_data_t                 fu_data_i,
+    output logic                     lsu_ready_o,              // FU is ready e.g. not busy
+    input  logic                     lsu_valid_i,              // Input is valid
+
+    output logic [TRANS_ID_BITS-1:0] load_trans_id_o,          // ID of scoreboard entry at which to write back
+    output logic [63:0]              load_result_o,
+    output logic                     load_valid_o,
+    //output exception_t               load_exception_o,         // to WB, signal exception status LD exception
+
+    output logic [TRANS_ID_BITS-1:0] store_trans_id_o,         // ID of scoreboard entry at which to write back
+    output logic [63:0]              store_result_o,
+    output logic                     store_valid_o,
+    //output exception_t               store_exception_o,        // to WB, signal exception status ST exception
+
+    input  logic                     commit_i,                 // commit the pending store
+    output logic                     commit_ready_o,           // commit queue is ready to accept another commit request
+
+    input  logic                     enable_translation_i,     // enable virtual memory translation
+    input  logic                     en_ld_st_translation_i,   // enable virtual memory translation for load/stores
+
+    // icache translation requests
+    //input  icache_areq_o_t           icache_areq_i,
+    //output icache_areq_i_t           icache_areq_o,
+
+    //input  riscv::priv_lvl_t         priv_lvl_i,               // From CSR register file
+    //input  riscv::priv_lvl_t         ld_st_priv_lvl_i,         // From CSR register file
+    input  logic                     sum_i,                    // From CSR register file
+    input  logic                     mxr_i,                    // From CSR register file
+    input  logic [43:0]              satp_ppn_i,               // From CSR register file
+    input  logic [ASID_WIDTH-1:0]    asid_i,                   // From CSR register file
+    input  logic                     flush_tlb_i,
+    // Performance counters
+    output logic                     itlb_miss_o,
+    output logic                     dtlb_miss_o
+
+    // interface to dcache
+    //input  dcache_req_o_t [2:0]      dcache_req_ports_i,
+    //output dcache_req_i_t [2:0]      dcache_req_ports_o,
+    // AMO interface
+    //output amo_req_t                 amo_req_o,
+    //input  amo_resp_t                amo_resp_i
+);
+    #docstring_begin
+    // data is misaligned
+    logic data_misaligned;
+    // --------------------------------------
+    // 1st register stage - (stall registers)
+    // --------------------------------------
+    // those are the signals which are always correct
+    // e.g.: they keep the value in the stall case
+    lsu_ctrl_t lsu_ctrl;
+
+    logic      pop_st;
+    logic      pop_ld;
+
+    // ------------------------------
+    // Address Generation Unit (AGU)
+    // ------------------------------
+    // virtual address as calculated by the AGU in the first cycle
+    logic [63:0] vaddr_i;
+    logic [7:0]  be_i;
+
+    assign vaddr_i = $unsigned($signed(fu_data_i.imm) + $signed(fu_data_i.operand_a));
+
+    logic                     st_valid_i;
+    logic                     ld_valid_i;
+    logic                     ld_translation_req;
+    logic                     st_translation_req;
+    logic [63:0]              ld_vaddr;
+    logic [63:0]              st_vaddr;
+    logic                     translation_req;
+    logic                     translation_valid;
+    logic [63:0]              mmu_vaddr;
+    logic [63:0]              mmu_paddr;
+    exception_t               mmu_exception;
+    logic                     dtlb_hit;
+
+    logic                     ld_valid;
+    logic [TRANS_ID_BITS-1:0] ld_trans_id;
+    logic [63:0]              ld_result;
+    logic                     st_valid;
+    logic [TRANS_ID_BITS-1:0] st_trans_id;
+    logic [63:0]              st_result;
+
+    logic [11:0]              page_offset;
+    logic                     page_offset_matches;
+
+    exception_t               misaligned_exception;
+    exception_t               ld_ex;
+    exception_t               st_ex;
+
+    // -------------------
+    // MMU e.g.: TLBs/PTW
+    // -------------------
+    mmu #(
+        .INSTR_TLB_ENTRIES      ( 16                     ),
+        .DATA_TLB_ENTRIES       ( 16                     ),
+        .ASID_WIDTH             ( ASID_WIDTH             ),
+        .ArianeCfg              ( ArianeCfg              )
+    ) i_mmu (
+            // misaligned bypass
+        .misaligned_ex_i        ( misaligned_exception   ),
+        .lsu_is_store_i         ( st_translation_req     ),
+        .lsu_req_i              ( translation_req        ),
+        .lsu_vaddr_i            ( mmu_vaddr              ),
+        .lsu_valid_o            ( translation_valid      ),
+        .lsu_paddr_o            ( mmu_paddr              ),
+        .lsu_exception_o        ( mmu_exception          ),
+        .lsu_dtlb_hit_o         ( dtlb_hit               ), // send in the same cycle as the request
+        // connecting PTW to D$ IF
+        .req_port_i             ( dcache_req_ports_i [0] ),
+        .req_port_o             ( dcache_req_ports_o [0] ),
+        // icache address translation requests
+        .icache_areq_i          ( icache_areq_i          ),
+        .icache_areq_o          ( icache_areq_o          ),
+        .*
+    );
+    // ------------------
+    // Store Unit
+    // ------------------
+    store_unit i_store_unit (
+        .clk_i,
+        .rst_ni,
+        .flush_i,
+        .no_st_pending_o,
+
+        .valid_i               ( st_valid_i           ),
+        .lsu_ctrl_i            ( lsu_ctrl             ),
+        .pop_st_o              ( pop_st               ),
+        .commit_i,
+        .commit_ready_o,
+        .amo_valid_commit_i,
+
+        .valid_o               ( st_valid             ),
+        .trans_id_o            ( st_trans_id          ),
+        .result_o              ( st_result            ),
+        .ex_o                  ( st_ex                ),
+        // MMU port
+        .translation_req_o     ( st_translation_req   ),
+        .vaddr_o               ( st_vaddr             ),
+        .paddr_i               ( mmu_paddr            ),
+        .ex_i                  ( mmu_exception        ),
+        .dtlb_hit_i            ( dtlb_hit             ),
+        // Load Unit
+        .page_offset_i         ( page_offset          ),
+        .page_offset_matches_o ( page_offset_matches  ),
+        // AMOs
+        .amo_req_o,
+        .amo_resp_i,
+        // to memory arbiter
+        .req_port_i             ( dcache_req_ports_i [2] ),
+        .req_port_o             ( dcache_req_ports_o [2] )
+    );
+
+    // ------------------
+    // Load Unit
+    // ------------------
+    load_unit i_load_unit (
+        .valid_i               ( ld_valid_i           ),
+        .lsu_ctrl_i            ( lsu_ctrl             ),
+        .pop_ld_o              ( pop_ld               ),
+
+        .valid_o               ( ld_valid             ),
+        .trans_id_o            ( ld_trans_id          ),
+        .result_o              ( ld_result            ),
+        .ex_o                  ( ld_ex                ),
+        // MMU port
+        .translation_req_o     ( ld_translation_req   ),
+        .vaddr_o               ( ld_vaddr             ),
+        .paddr_i               ( mmu_paddr            ),
+        .ex_i                  ( mmu_exception        ),
+        .dtlb_hit_i            ( dtlb_hit             ),
+        // to store unit
+        .page_offset_o         ( page_offset          ),
+        .page_offset_matches_i ( page_offset_matches  ),
+        // to memory arbiter
+        .req_port_i            ( dcache_req_ports_i [1] ),
+        .req_port_o            ( dcache_req_ports_o [1] ),
+        .*
+    );
+
+    // ----------------------------
+    // Output Pipeline Register
+    // ----------------------------
+    shift_reg #(
+        .dtype ( logic[$bits(ld_valid) + $bits(ld_trans_id) + $bits(ld_result) + $bits(ld_ex) - 1: 0]),
+        .Depth ( NR_LOAD_PIPE_REGS )
+    ) i_pipe_reg_load (
+        .clk_i,
+        .rst_ni,
+        .d_i ( {ld_valid, ld_trans_id, ld_result, ld_ex} ),
+        .d_o ( {load_valid_o, load_trans_id_o, load_result_o, load_exception_o} )
+    );
+
+    shift_reg #(
+        .dtype ( logic[$bits(st_valid) + $bits(st_trans_id) + $bits(st_result) + $bits(st_ex) - 1: 0]),
+        .Depth ( NR_STORE_PIPE_REGS )
+    ) i_pipe_reg_store (
+        .clk_i,
+        .rst_ni,
+        .d_i ( {st_valid, st_trans_id, st_result, st_ex} ),
+        .d_o ( {store_valid_o, store_trans_id_o, store_result_o, store_exception_o} )
+    );
+
+    // determine whether this is a load or store
+    always_comb begin : which_op
+
+        ld_valid_i = 1'b0;
+        st_valid_i = 1'b0;
+
+        translation_req      = 1'b0;
+        mmu_vaddr            = 64'b0;
+
+        // check the operator to activate the right functional unit accordingly
+        unique case (lsu_ctrl.fu)
+            // all loads go here
+            LOAD:  begin
+                ld_valid_i           = lsu_ctrl.valid;
+                translation_req      = ld_translation_req;
+                mmu_vaddr            = ld_vaddr;
+            end
+            // all stores go here
+            STORE: begin
+                st_valid_i           = lsu_ctrl.valid;
+                translation_req      = st_translation_req;
+                mmu_vaddr            = st_vaddr;
+            end
+            // not relevant for the LSU
+            default: ;
+        endcase
+    end
+
+
+    // ---------------
+    // Byte Enable
+    // ---------------
+    // we can generate the byte enable from the virtual address since the last
+    // 12 bit are the same anyway
+    // and we can always generate the byte enable from the address at hand
+    assign be_i = be_gen(vaddr_i[2:0], extract_transfer_size(fu_data_i.operator));
+
+    // ------------------------
+    // Misaligned Exception
+    // ------------------------
+    // we can detect a misaligned exception immediately
+    // the misaligned exception is passed to the functional unit via the MMU, which in case
+    // can augment the exception if other memory related exceptions like a page fault or access errors
+    always_comb begin : data_misaligned_detection
+
+        misaligned_exception = {
+            64'b0,
+            64'b0,
+            1'b0
+        };
+
+        data_misaligned = 1'b0;
+
+        if (lsu_ctrl.valid) begin
+            case (lsu_ctrl.operator)
+                // double word
+                LD, SD, FLD, FSD,
+                AMO_LRD, AMO_SCD,
+                AMO_SWAPD, AMO_ADDD, AMO_ANDD, AMO_ORD,
+                AMO_XORD, AMO_MAXD, AMO_MAXDU, AMO_MIND,
+                AMO_MINDU: begin
+                    if (lsu_ctrl.vaddr[2:0] != 3'b000) begin
+                        data_misaligned = 1'b1;
+                    end
+                end
+                // word
+                LW, LWU, SW, FLW, FSW,
+                AMO_LRW, AMO_SCW,
+                AMO_SWAPW, AMO_ADDW, AMO_ANDW, AMO_ORW,
+                AMO_XORW, AMO_MAXW, AMO_MAXWU, AMO_MINW,
+                AMO_MINWU: begin
+                    if (lsu_ctrl.vaddr[1:0] != 2'b00) begin
+                        data_misaligned = 1'b1;
+                    end
+                end
+                // half word
+                LH, LHU, SH, FLH, FSH: begin
+                    if (lsu_ctrl.vaddr[0] != 1'b0) begin
+                        data_misaligned = 1'b1;
+                    end
+                end
+                // byte -> is always aligned
+                default:;
+            endcase
+        end
+
+        if (data_misaligned) begin
+
+            if (lsu_ctrl.fu == LOAD) begin
+                misaligned_exception = {
+                    riscv::LD_ADDR_MISALIGNED,
+                    lsu_ctrl.vaddr,
+                    1'b1
+                };
+
+            end else if (lsu_ctrl.fu == STORE) begin
+                misaligned_exception = {
+                    riscv::ST_ADDR_MISALIGNED,
+                    lsu_ctrl.vaddr,
+                    1'b1
+                };
+            end
+        end
+
+        // we work with SV39, so if VM is enabled, check that all bits [63:38] are equal
+        if (en_ld_st_translation_i && !((&lsu_ctrl.vaddr[63:38]) == 1'b1 || (|lsu_ctrl.vaddr[63:38]) == 1'b0)) begin
+
+            if (lsu_ctrl.fu == LOAD) begin
+                misaligned_exception = {
+                    riscv::LD_ACCESS_FAULT,
+                    lsu_ctrl.vaddr,
+                    1'b1
+                };
+
+            end else if (lsu_ctrl.fu == STORE) begin
+                misaligned_exception = {
+                    riscv::ST_ACCESS_FAULT,
+                    lsu_ctrl.vaddr,
+                    1'b1
+                };
+            end
+        end
+    end
+
+    // ------------------
+    // LSU Control
+    // ------------------
+    // new data arrives here
+    lsu_ctrl_t lsu_req_i;
+
+    assign lsu_req_i = {lsu_valid_i, vaddr_i, fu_data_i.operand_b, be_i, fu_data_i.fu, fu_data_i.operator, fu_data_i.trans_id};
+
+    lsu_bypass lsu_bypass_i (
+        .lsu_req_i          ( lsu_req_i   ),
+        .lus_req_valid_i    ( lsu_valid_i ),
+        .pop_ld_i           ( pop_ld      ),
+        .pop_st_i           ( pop_st      ),
+
+        .lsu_ctrl_o         ( lsu_ctrl    ),
+        .ready_o            ( lsu_ready_o ),
+        .*
+    );
+#docstring_end
+endmodule
+
+#docstring_begin
+// ------------------
+// LSU Control
+// ------------------
+// The LSU consists of two independent block which share a common address translation block.
+// The one block is the load unit, the other one is the store unit. They will signal their readiness
+// with separate signals. If they are not ready the LSU control should keep the last applied signals stable.
+// Furthermore it can be the case that another request for one of the two store units arrives in which case
+// the LSU control should sample it and store it for later application to the units. It does so, by storing it in a
+// two element FIFO. This is necessary as we only know very late in the cycle whether the load/store will succeed (address check,
+// TLB hit mainly). So we better unconditionally allow another request to arrive and store this request in case we need to.
+module lsu_bypass (
+    input  logic      clk_i,
+    input  logic      rst_ni,
+    input  logic      flush_i,
+
+    input  lsu_ctrl_t lsu_req_i,
+    input  logic      lus_req_valid_i,
+    input  logic      pop_ld_i,
+    input  logic      pop_st_i,
+
+    output lsu_ctrl_t lsu_ctrl_o,
+    output logic      ready_o
+    );
+
+    lsu_ctrl_t [1:0] mem_n, mem_q;
+    logic read_pointer_n, read_pointer_q;
+    logic write_pointer_n, write_pointer_q;
+    logic [1:0] status_cnt_n, status_cnt_q;
+
+    logic  empty;
+    assign empty = (status_cnt_q == 0);
+    assign ready_o = empty;
+
+    always_comb begin
+        automatic logic [1:0] status_cnt;
+        automatic logic write_pointer;
+        automatic logic read_pointer;
+
+        status_cnt = status_cnt_q;
+        write_pointer = write_pointer_q;
+        read_pointer = read_pointer_q;
+
+        mem_n = mem_q;
+        // we've got a valid LSU request
+        if (lus_req_valid_i) begin
+            mem_n[write_pointer_q] = lsu_req_i;
+            write_pointer++;
+            status_cnt++;
+        end
+
+        if (pop_ld_i) begin
+            // invalidate the result
+            mem_n[read_pointer_q].valid = 1'b0;
+            read_pointer++;
+            status_cnt--;
+        end
+
+        if (pop_st_i) begin
+            // invalidate the result
+            mem_n[read_pointer_q].valid = 1'b0;
+            read_pointer++;
+            status_cnt--;
+        end
+
+        if (pop_st_i && pop_ld_i)
+            mem_n = '0;
+
+        if (flush_i) begin
+            status_cnt = '0;
+            write_pointer = '0;
+            read_pointer = '0;
+            mem_n = '0;
+        end
+        // default assignments
+        read_pointer_n  = read_pointer;
+        write_pointer_n = write_pointer;
+        status_cnt_n    = status_cnt;
+    end
+
+    // output assignment
+    always_comb begin : output_assignments
+        if (empty) begin
+            lsu_ctrl_o = lsu_req_i;
+        end else begin
+            lsu_ctrl_o = mem_q[read_pointer_q];
+        end
+    end
+
+    // registers
+    always_ff @(posedge clk_i or negedge rst_ni) begin
+        if (~rst_ni) begin
+            mem_q           <= '0;
+            status_cnt_q    <= '0;
+            write_pointer_q <= '0;
+            read_pointer_q  <= '0;
+        end else begin
+            mem_q           <= mem_n;
+            status_cnt_q    <= status_cnt_n;
+            write_pointer_q <= write_pointer_n;
+            read_pointer_q  <= read_pointer_n;
+        end
+    end
+endmodule
+#docstring_end
diff --git a/examples/mmu.sv b/examples/mmu.sv
new file mode 100644 (file)
index 0000000..c6471d4
--- /dev/null
@@ -0,0 +1,365 @@
+// 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: Florian Zaruba, ETH Zurich
+// Date: 19/04/2017
+// Description: Memory Management Unit for Ariane, contains TLB and
+//              address translation unit. SV39 as defined in RISC-V
+//              privilege specification 1.11-WIP
+
+//import ariane_pkg::*;
+
+module mmu #(
+      parameter int INSTR_TLB_ENTRIES     = 4,
+      parameter int DATA_TLB_ENTRIES      = 4,
+      parameter int ASID_WIDTH            = 1
+     // parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig
+) (
+        input  logic                            clk_i,
+        input  logic                            rst_ni,
+        input  logic                            flush_i,
+        input  logic                            enable_translation_i,
+        input  logic                            en_ld_st_translation_i,   // enable virtual memory translation for load/stores
+        // IF interface
+        #docstring_begin
+        input  icache_areq_o_t                  icache_areq_i,
+        output icache_areq_i_t                  icache_areq_o,
+        #docstring_end
+        // LSU interface
+        // this is a more minimalistic interface because the actual addressing logic is handled
+        // in the LSU as we distinguish load and stores, what we do here is simple address translation
+        //input  exception_t                      misaligned_ex_i,
+        input  logic                            lsu_req_i,        // request address translation
+        input  logic [63:0]                     lsu_vaddr_i,      // virtual address in
+        input  logic                            lsu_is_store_i,   // the translation is requested by a store
+        // if we need to walk the page table we can't grant in the same cycle
+        // Cycle 0
+        output logic                            lsu_dtlb_hit_o,   // sent in the same cycle as the request if translation hits in the DTLB
+        // Cycle 1
+        output logic                            lsu_valid_o,      // translation is valid
+        output logic [63:0]                     lsu_paddr_o,      // translated address
+        //output exception_t                      lsu_exception_o,  // address translation threw an exception
+        // General control signals
+        //input riscv::priv_lvl_t                 priv_lvl_i,
+        //input riscv::priv_lvl_t                 ld_st_priv_lvl_i,
+        input logic                             sum_i,
+        input logic                             mxr_i,
+        // input logic flag_mprv_i,
+        input logic [43:0]                      satp_ppn_i,
+        input logic [ASID_WIDTH-1:0]            asid_i,
+        input logic                             flush_tlb_i,
+        // Performance counters
+        output logic                            itlb_miss_o,
+        output logic                            dtlb_miss_o
+        // PTW memory interface
+        //input  dcache_req_o_t                   req_port_i,
+        //output dcache_req_i_t                   req_port_o
+);
+
+    #docstring_begin
+    logic        iaccess_err;   // insufficient privilege to access this instruction page
+    logic        daccess_err;   // insufficient privilege to access this data page
+    logic        ptw_active;    // PTW is currently walking a page table
+    logic        walking_instr; // PTW is walking because of an ITLB miss
+    logic        ptw_error;     // PTW threw an exception
+
+    logic [38:0] update_vaddr;
+    tlb_update_t update_ptw_itlb, update_ptw_dtlb;
+
+    logic        itlb_lu_access;
+    riscv::pte_t itlb_content;
+    logic        itlb_is_2M;
+    logic        itlb_is_1G;
+    logic        itlb_lu_hit;
+
+    logic        dtlb_lu_access;
+    riscv::pte_t dtlb_content;
+    logic        dtlb_is_2M;
+    logic        dtlb_is_1G;
+    logic        dtlb_lu_hit;
+
+
+    // Assignments
+    assign itlb_lu_access = icache_areq_i.fetch_req;
+    assign dtlb_lu_access = lsu_req_i;
+
+
+    tlb #(
+        .TLB_ENTRIES      ( INSTR_TLB_ENTRIES          ),
+        .ASID_WIDTH       ( ASID_WIDTH                 )
+    ) i_itlb (
+        .clk_i            ( clk_i                      ),
+        .rst_ni           ( rst_ni                     ),
+        .flush_i          ( flush_tlb_i                ),
+
+        .update_i         ( update_ptw_itlb            ),
+
+        .lu_access_i      ( itlb_lu_access             ),
+        .lu_asid_i        ( asid_i                     ),
+        .lu_vaddr_i       ( icache_areq_i.fetch_vaddr              ),
+        .lu_content_o     ( itlb_content               ),
+
+        .lu_is_2M_o       ( itlb_is_2M                 ),
+        .lu_is_1G_o       ( itlb_is_1G                 ),
+        .lu_hit_o         ( itlb_lu_hit                )
+    );
+
+    tlb #(
+        .TLB_ENTRIES     ( DATA_TLB_ENTRIES             ),
+        .ASID_WIDTH      ( ASID_WIDTH                   )
+    ) i_dtlb (
+        .clk_i            ( clk_i                       ),
+        .rst_ni           ( rst_ni                      ),
+        .flush_i          ( flush_tlb_i                 ),
+
+        .update_i         ( update_ptw_dtlb             ),
+
+        .lu_access_i      ( dtlb_lu_access              ),
+        .lu_asid_i        ( asid_i                      ),
+        .lu_vaddr_i       ( lsu_vaddr_i                 ),
+        .lu_content_o     ( dtlb_content                ),
+
+        .lu_is_2M_o       ( dtlb_is_2M                  ),
+        .lu_is_1G_o       ( dtlb_is_1G                  ),
+        .lu_hit_o         ( dtlb_lu_hit                 )
+    );
+
+
+    ptw  #(
+        .ASID_WIDTH             ( ASID_WIDTH            )
+    ) i_ptw (
+        .clk_i                  ( clk_i                 ),
+        .rst_ni                 ( rst_ni                ),
+        .ptw_active_o           ( ptw_active            ),
+        .walking_instr_o        ( walking_instr         ),
+        .ptw_error_o            ( ptw_error             ),
+        .enable_translation_i   ( enable_translation_i  ),
+
+        .update_vaddr_o         ( update_vaddr          ),
+        .itlb_update_o          ( update_ptw_itlb       ),
+        .dtlb_update_o          ( update_ptw_dtlb       ),
+
+        .itlb_access_i          ( itlb_lu_access        ),
+        .itlb_hit_i             ( itlb_lu_hit           ),
+        .itlb_vaddr_i           ( icache_areq_i.fetch_vaddr         ),
+
+        .dtlb_access_i          ( dtlb_lu_access        ),
+        .dtlb_hit_i             ( dtlb_lu_hit           ),
+        .dtlb_vaddr_i           ( lsu_vaddr_i           ),
+
+        .req_port_i            ( req_port_i             ),
+        .req_port_o            ( req_port_o             ),
+
+        .*
+     );
+
+    // ila_1 i_ila_1 (
+    //     .clk(clk_i), // input wire clk
+    //     .probe0({req_port_o.address_tag, req_port_o.address_index}),
+    //     .probe1(req_port_o.data_req), // input wire [63:0]  probe1
+    //     .probe2(req_port_i.data_gnt), // input wire [0:0]  probe2
+    //     .probe3(req_port_i.data_rdata), // input wire [0:0]  probe3
+    //     .probe4(req_port_i.data_rvalid), // input wire [0:0]  probe4
+    //     .probe5(ptw_error), // input wire [1:0]  probe5
+    //     .probe6(update_vaddr), // input wire [0:0]  probe6
+    //     .probe7(update_ptw_itlb.valid), // input wire [0:0]  probe7
+    //     .probe8(update_ptw_dtlb.valid), // input wire [0:0]  probe8
+    //     .probe9(dtlb_lu_access), // input wire [0:0]  probe9
+    //     .probe10(lsu_vaddr_i), // input wire [0:0]  probe10
+    //     .probe11(dtlb_lu_hit), // input wire [0:0]  probe11
+    //     .probe12(itlb_lu_access), // input wire [0:0]  probe12
+    //     .probe13(icache_areq_i.fetch_vaddr), // input wire [0:0]  probe13
+    //     .probe14(itlb_lu_hit) // input wire [0:0]  probe13
+    // );
+
+    //-----------------------
+    // Instruction Interface
+    //-----------------------
+    logic match_any_execute_region;
+    // The instruction interface is a simple request response interface
+    always_comb begin : instr_interface
+        // MMU disabled: just pass through
+        icache_areq_o.fetch_valid  = icache_areq_i.fetch_req;
+        icache_areq_o.fetch_paddr  = icache_areq_i.fetch_vaddr; // play through in case we disabled address translation
+        // two potential exception sources:
+        // 1. HPTW threw an exception -> signal with a page fault exception
+        // 2. We got an access error because of insufficient permissions -> throw an access exception
+        icache_areq_o.fetch_exception      = '0;
+        // Check whether we are allowed to access this memory region from a fetch perspective
+        iaccess_err   = icache_areq_i.fetch_req && (((priv_lvl_i == riscv::PRIV_LVL_U) && ~itlb_content.u)
+                                                 || ((priv_lvl_i == riscv::PRIV_LVL_S) && itlb_content.u));
+
+        // MMU enabled: address from TLB, request delayed until hit. Error when TLB
+        // hit and no access right or TLB hit and translated address not valid (e.g.
+        // AXI decode error), or when PTW performs walk due to ITLB miss and raises
+        // an error.
+        if (enable_translation_i) begin
+            // we work with SV39, so if VM is enabled, check that all bits [63:38] are equal
+            if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[63:38]) == 1'b1 || (|icache_areq_i.fetch_vaddr[63:38]) == 1'b0)) begin
+                icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
+            end
+
+            icache_areq_o.fetch_valid = 1'b0;
+
+            // 4K page
+            icache_areq_o.fetch_paddr = {itlb_content.ppn, icache_areq_i.fetch_vaddr[11:0]};
+            // Mega page
+            if (itlb_is_2M) begin
+                icache_areq_o.fetch_paddr[20:12] = icache_areq_i.fetch_vaddr[20:12];
+            end
+            // Giga page
+            if (itlb_is_1G) begin
+                icache_areq_o.fetch_paddr[29:12] = icache_areq_i.fetch_vaddr[29:12];
+            end
+
+            // ---------
+            // ITLB Hit
+            // --------
+            // if we hit the ITLB output the request signal immediately
+            if (itlb_lu_hit) begin
+                icache_areq_o.fetch_valid = icache_areq_i.fetch_req;
+                // we got an access error
+                if (iaccess_err) begin
+                    // throw a page fault
+                    icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, icache_areq_i.fetch_vaddr, 1'b1};
+                end
+            end else
+            // ---------
+            // ITLB Miss
+            // ---------
+            // watch out for exceptions happening during walking the page table
+            if (ptw_active && walking_instr) begin
+                icache_areq_o.fetch_valid = ptw_error;
+                icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+            end
+        end
+        // if it didn't match any execute region throw an `Instruction Access Fault`
+        if (!match_any_execute_region) begin
+          icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr, 1'b1};
+        end
+    end
+
+    // check for execute flag on memory
+    assign match_any_execute_region = ariane_pkg::is_inside_execute_regions(ArianeCfg, icache_areq_o.fetch_paddr);
+
+    //-----------------------
+    // Data Interface
+    //-----------------------
+    logic [63:0] lsu_vaddr_n,     lsu_vaddr_q;
+    riscv::pte_t dtlb_pte_n,      dtlb_pte_q;
+    exception_t  misaligned_ex_n, misaligned_ex_q;
+    logic        lsu_req_n,       lsu_req_q;
+    logic        lsu_is_store_n,  lsu_is_store_q;
+    logic        dtlb_hit_n,      dtlb_hit_q;
+    logic        dtlb_is_2M_n,    dtlb_is_2M_q;
+    logic        dtlb_is_1G_n,    dtlb_is_1G_q;
+
+    // check if we need to do translation or if we are always ready (e.g.: we are not translating anything)
+    assign lsu_dtlb_hit_o = (en_ld_st_translation_i) ? dtlb_lu_hit :  1'b1;
+
+    // The data interface is simpler and only consists of a request/response interface
+    always_comb begin : data_interface
+        // save request and DTLB response
+        lsu_vaddr_n           = lsu_vaddr_i;
+        lsu_req_n             = lsu_req_i;
+        misaligned_ex_n       = misaligned_ex_i;
+        dtlb_pte_n            = dtlb_content;
+        dtlb_hit_n            = dtlb_lu_hit;
+        lsu_is_store_n        = lsu_is_store_i;
+        dtlb_is_2M_n          = dtlb_is_2M;
+        dtlb_is_1G_n          = dtlb_is_1G;
+
+        lsu_paddr_o           = lsu_vaddr_q;
+        lsu_valid_o           = lsu_req_q;
+        lsu_exception_o       = misaligned_ex_q;
+        // mute misaligned exceptions if there is no request otherwise they will throw accidental exceptions
+        misaligned_ex_n.valid = misaligned_ex_i.valid & lsu_req_i;
+
+        // Check if the User flag is set, then we may only access it in supervisor mode
+        // if SUM is enabled
+        daccess_err = (ld_st_priv_lvl_i == riscv::PRIV_LVL_S && !sum_i && dtlb_pte_q.u) || // SUM is not set and we are trying to access a user page in supervisor mode
+                      (ld_st_priv_lvl_i == riscv::PRIV_LVL_U && !dtlb_pte_q.u);            // this is not a user page but we are in user mode and trying to access it
+        // translation is enabled and no misaligned exception occurred
+        if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin
+            lsu_valid_o = 1'b0;
+            // 4K page
+            lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]};
+            // Mega page
+            if (dtlb_is_2M_q) begin
+              lsu_paddr_o[20:12] = lsu_vaddr_q[20:12];
+            end
+            // Giga page
+            if (dtlb_is_1G_q) begin
+                lsu_paddr_o[29:12] = lsu_vaddr_q[29:12];
+            end
+            // ---------
+            // DTLB Hit
+            // --------
+            if (dtlb_hit_q && lsu_req_q) begin
+                lsu_valid_o = 1'b1;
+                // this is a store
+                if (lsu_is_store_q) begin
+                    // check if the page is write-able and we are not violating privileges
+                    // also check if the dirty flag is set
+                    if (!dtlb_pte_q.w || daccess_err || !dtlb_pte_q.d) begin
+                        lsu_exception_o = {riscv::STORE_PAGE_FAULT, lsu_vaddr_q, 1'b1};
+                    end
+
+                // this is a load, check for sufficient access privileges - throw a page fault if necessary
+                end else if (daccess_err) begin
+                    lsu_exception_o = {riscv::LOAD_PAGE_FAULT, lsu_vaddr_q, 1'b1};
+                end
+            end else
+
+            // ---------
+            // DTLB Miss
+            // ---------
+            // watch out for exceptions
+            if (ptw_active && !walking_instr) begin
+                // page table walker threw an exception
+                if (ptw_error) begin
+                    // an error makes the translation valid
+                    lsu_valid_o = 1'b1;
+                    // the page table walker can only throw page faults
+                    if (lsu_is_store_q) begin
+                        lsu_exception_o = {riscv::STORE_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+                    end else begin
+                        lsu_exception_o = {riscv::LOAD_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
+                    end
+                end
+            end
+        end
+    end
+    // ----------
+    // Registers
+    // ----------
+    always_ff @(posedge clk_i or negedge rst_ni) begin
+        if (~rst_ni) begin
+            lsu_vaddr_q      <= '0;
+            lsu_req_q        <= '0;
+            misaligned_ex_q  <= '0;
+            dtlb_pte_q       <= '0;
+            dtlb_hit_q       <= '0;
+            lsu_is_store_q   <= '0;
+            dtlb_is_2M_q     <= '0;
+            dtlb_is_1G_q     <= '0;
+        end else begin
+            lsu_vaddr_q      <=  lsu_vaddr_n;
+            lsu_req_q        <=  lsu_req_n;
+            misaligned_ex_q  <=  misaligned_ex_n;
+            dtlb_pte_q       <=  dtlb_pte_n;
+            dtlb_hit_q       <=  dtlb_hit_n;
+            lsu_is_store_q   <=  lsu_is_store_n;
+            dtlb_is_2M_q     <=  dtlb_is_2M_n;
+            dtlb_is_1G_q     <=  dtlb_is_1G_n;
+        end
+    end
+#docstring_end
+endmodule
diff --git a/examples/tlb.py b/examples/tlb.py
new file mode 100644 (file)
index 0000000..5e08a9f
--- /dev/null
@@ -0,0 +1,272 @@
+# 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
+#
+#
diff --git a/examples/tlb.sv b/examples/tlb.sv
new file mode 100644 (file)
index 0000000..d9b8c69
--- /dev/null
@@ -0,0 +1,248 @@
+// 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
+#docstring_end
+endmodule
index 4973540dc4d55dc366c0d1034203623088f90462..02f315dc88117633b8b14d8c4865ccbff999e9a5 100644 (file)
@@ -23,9 +23,9 @@ from lib2to3.pytree import Node, Leaf
 from lib2to3.pgen2 import token
 from lib2to3.pygram import python_symbols as syms
 
-yacc1_debug = 0
-yacc2_debug = 0
-parse_debug = 0
+yacc1_debug = 1
+yacc2_debug = 1
+parse_debug = 1
 
 from ply import yacc, lex
 
diff --git a/pypreproc.py b/pypreproc.py
new file mode 100644 (file)
index 0000000..4569506
--- /dev/null
@@ -0,0 +1,39 @@
+quote = "\""
+import sys
+class Preprocessor:
+    def __init__(self):
+        self.docstrings = []
+    def removeComments(self,data):
+        #print(data)
+        ret = ""
+        in_docstring = False
+        docstring = ""
+        for line in data.split("\n"):
+            docstring_end = ("#docstring_end" in line)
+            if "#docstring_begin" in line:
+                ret += "//DOCSTRING_PLACEHOLDER\n"
+                in_docstring = True
+                docstring = ""
+            if(in_docstring==False):
+                ret += line + "\n"
+            else:
+                if(not docstring_end):  docstring += line + "\n"
+            if(docstring_end): 
+                in_docstring = False
+                self.docstrings += [docstring]
+                print(docstring)
+        return ret
+    def insertDocstrings(self,data):
+        ret =""
+        docstring_counter = 0
+        for line in data.split("\n"):
+            if("//DOCSTRING_PLACEHOLDER" in line):
+                ret += quote*3
+                ret += self.docstrings[docstring_counter]
+                ret += quote*3
+                ret += "\n"
+                docstring_counter += 1
+            else:
+                ret += "#"+line + "\n"
+        return ret
+        
index 9822485712d2ef24ce3671c733c0aedcfec34b44..3a495edc8c5ac4e58e9c6432f6ad6c3144592ff5 100644 (file)
@@ -3,6 +3,7 @@ import sys
 import lexor
 import parse_sv
 import absyn
+import pypreproc
 
 from ply import *
 import os 
@@ -13,7 +14,9 @@ if __name__ == '__main__':
     print(outputfn)
     with open(fname) as f:
         data = f.read()
+        preproc = pypreproc.Preprocessor()
+        data = preproc.removeComments(data)
         parse_sv.absyn = absyn.Absyn(outputfn)
         yacc.parse(data, debug=parse_sv.yacc2_debug)
         print("No Error")
-        parse_sv.absyn.appendComments(data)
+        parse_sv.absyn.appendComments(preproc.insertDocstrings(data))