From 8328b357c1b5196373befc4363181353d23e53e9 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sun, 21 Apr 2019 07:04:54 +0100 Subject: [PATCH] begin experimental ariane mmu.sv conversion --- TLB/src/ariane/mmu.py | 408 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 TLB/src/ariane/mmu.py diff --git a/TLB/src/ariane/mmu.py b/TLB/src/ariane/mmu.py new file mode 100644 index 00000000..d85abc9f --- /dev/null +++ b/TLB/src/ariane/mmu.py @@ -0,0 +1,408 @@ +""" +# 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::*; +""" + +from nmigen import Const, Signal, Cat, Module +from ptw import DCacheReqI, DCacheReqO + +PRIV_LVL_M = Const(0b11, 2) +PRIV_LVL_S = Const(0b01, 2) +PRIV_LVL_U = Const(0b00, 2) + + +class RVException: + def __init__(self): + self.cause = Signal(64) # cause of exception + self.tval = Signal(64) # more info of causing exception + # (e.g.: instruction causing it), + # address of LD/ST fault + self.valid = Signal() + + def __iter__(self): + yield self.cause + yield self.tval + yield self.valid + + def ports(self): + return list(self) + + +class ICacheReqI: + def __init__(self): + self.fetch_valid = Signal() # address translation valid + self.fetch_paddr = Signal(64) # physical address in + self.fetch_exception = RVException() // exception occurred during fetch + + def __iter__(self): + yield self.fetch_valid + yield self.fetch_paddr + yield from self.fetch_exception + + def ports(self): + return list(self) + + +class ICacheReqO: + def __init__(self): + self.fetch_req = Signal() # address translation request + self.fetch_vaddr = Signal(64) # virtual address out + + def __iter__(self): + yield self.fetch_req + yield self.fetch_vaddr + + def ports(self): + return list(self) + + +class MMU: + def __init__(self, INSTR_TLB_ENTRIES = 4, + DATA_TLB_ENTRIES = 4, + ASID_WIDTH = 1): + self.flush_i = Signal() + self.enable_translation_i = Signal() + self.en_ld_st_translation_i = Signal() # enable VM translation for LD/ST + # IF interface + self.icache_areq_i = ICacheReqO() + self.icache_areq_o = ICacheReqI() + # 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 + self.misaligned_ex_i = RVException() + self.lsu_req_i = Signal() # request address translation + self.lsu_vaddr_i = Signal(64) # virtual address in + self.lsu_is_store_i = Signal() # 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 + self.lsu_dtlb_hit_o = Signal() # sent in the same cycle as the request + # if translation hits in the DTLB + # Cycle 1 + self.lsu_valid_o = Signal() # translation is valid + self.lsu_paddr_o = Signal(64) # translated address + self.lsu_exception_o = RVException() # addr translate threw exception + + # General control signals + self.priv_lvl_i = Signal(2) + self.ld_st_priv_lvl_i = Signal(2) + self.sum_i = Signal() + self.mxr_i = Signal() + # input logic flag_mprv_i, + self.satp_ppn_i = Signal(44) + self.asid_i = Signal(ASID_WIDTH) + self.flush_tlb_i = Signal() + # Performance counters + self.itlb_miss_o = Signal() + self.dtlb_miss_o = Signal() + # PTW memory interface + self.req_port_i = DCacheReqO() + self.req_port_o = DCacheReqI() +); + + logic iaccess_err; # insufficient priv to access instr page + logic daccess_err; # insufficient priv to access 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 + #----------------------- + # 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 + end + + #----------------------- + # 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 +endmodule -- 2.30.2