From 23e6065ac7e32e20f92553b810fb30baca2eb2d9 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sun, 21 Apr 2019 08:20:33 +0100 Subject: [PATCH] begin experimental ariane mmu.sv conversion --- TLB/src/ariane/mmu.py | 320 +++++++++++++++++++++++------------------- 1 file changed, 172 insertions(+), 148 deletions(-) diff --git a/TLB/src/ariane/mmu.py b/TLB/src/ariane/mmu.py index d1970e18..0cc04dbb 100644 --- a/TLB/src/ariane/mmu.py +++ b/TLB/src/ariane/mmu.py @@ -125,6 +125,7 @@ class MMU: ptw_error = Signal() # PTW threw an exception update_vaddr = Signal(39) + uaddr64 = Cat(update_vaddr, Const(0, 25)) # extend to 64bit with zeros update_ptw_itlb = TLBUpdate() update_ptw_dtlb = TLBUpdate() @@ -145,7 +146,6 @@ class MMU: dtlb_lu_access.eq(lsu_req_i) ] - # ITLB m.submodules.i_tlb = i_tlb = TLB(INSTR_TLB_ENTRIES, ASID_WIDTH) m.d.comb += [i_tlb.flush_i.eq(flush_tlb_i), @@ -218,174 +218,198 @@ class MMU: # 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 + # MMU disabled: just pass through + m.d.comb += [icache_areq_o.fetch_valid.eq(icache_areq_i.fetch_req), + # play through in case we disabled address translation + icache_areq_o.fetch_paddr.eq(icache_areq_i.fetch_vaddr) + ] + # 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 + m.d.comb += icache_areq_o.fetch_exception.eq(0) + # Check whether we are allowed to access this memory region + # from a fetch perspective + m.d.comb += iaccess_err.eq(icache_areq_i.fetch_req & \ + (((priv_lvl_i == PRIV_LVL_U) & \ + ~itlb_content.u) | \ + ((priv_lvl_i == :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. + with m.If (self.enable_translation_i): + # we work with SV39, so if VM is enabled, check that + # all bits [63:38] are equal + with m.If (icache_areq_i.fetch_req & \ + ~(((~icache_areq_i.fetch_vaddr[38:64]) == 0) | \ + (icache_areq_i.fetch_vaddr[38:64]) == 0)): + fe = icache_areq_o.fetch_exception + m.d.comb += [fe.cause.eq(INSTR_ACCESS_FAULT), + fe.tval.eq(icache_areq_i.fetch_vaddr), + fe.valid.eq(1) + ] + + m.d.comb += icache_areq_o.fetch_valid.eq(0) + + # 4K page + paddr = Signal.like(icache_areq_o.fetch_paddr) + paddr4k = Cat(icache_areq_i.fetch_vaddr[0:12], itlb_content.ppn) + m.d.comb += paddr.eq(paddr4k) + # Mega page + with m.If(itlb_is_2M): + m.d.comb += paddr[12:21].eq(icache_areq_i.fetch_vaddr[12:21]) end - end + # Giga page + with m.If(itlb_is_1G): + m.d.comb += paddr[12:30].eq(icache_areq_i.fetch_vaddr[12:30]) + m.d.comb += icache_areq_o.fetch_paddr.eq(paddr) + + # --------- + # ITLB Hit + # -------- + # if we hit the ITLB output the request signal immediately + with m.If(itlb_lu_hit): + m.d.comb += icache_areq_o.fetch_valid.eq( + icache_areq_i.fetch_req) + # we got an access error + with m.If (iaccess_err): + # throw a page fault + fe = icache_areq_o.fetch_exception + m.d.comb += [fe.cause.eq(INSTR_ACCESS_FAULT), + fe.tval.eq(icache_areq_i.fetch_vaddr), + fe.valid.eq(1) + ] + # --------- + # ITLB Miss + # --------- + # watch out for exceptions happening during walking the page table + with m.Elif(ptw_active & walking_instr): + m.d.comb += icache_areq_o.fetch_valid.eq(ptw_error) + fe = icache_areq_o.fetch_exception + m.d.comb += [fe.cause.eq(INSTR_PAGE_FAULT), + fe.tval.eq(uaddr64), + fe.valid.eq(1) + ] #----------------------- # 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 + + lsu_vaddr = Signal(64) + dtlb_pte = PTE() + misaligned_ex = RVException() + lsu_req = Signal() + lsu_is_store = Signal() + dtlb_hit = Signal() + dtlb_is_2M = Signal() + dtlb_is_1G = Signal() + + # check if we need to do translation or if we are always + # ready (e.g.: we are not translating anything) + m.d.comb += lsu_dtlb_hit_o.eq(Mux(en_ld_st_translation_i), + dtlb_lu_hit, 1) + + # The data interface is simpler and only consists of a + # request/response interface + m.d.comb += [ # 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 + lsu_vaddr.eq(lsu_vaddr_i), + lsu_req.eq(lsu_req_i), + misaligned_ex.eq(misaligned_ex_i), + dtlb_pte.eq(dtlb_content), + dtlb_hit.eq(dtlb_lu_hit), + lsu_is_store.eq(lsu_is_store_i), + dtlb_is_2M.eq(dtlb_is_2M), + dtlb_is_1G.eq(dtlb_is_1G), + ] + m.d.sync += [ + lsu_paddr_o.eq(lsu_vaddr), + lsu_valid_o.eq(lsu_req), + lsu_exception_o.eq(misaligned_ex), + ] + + m.d.comb += [ + # mute misaligned exceptions if there is no request + # otherwise they will throw accidental exceptions + misaligned_ex_n.valid.eq(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.eq( + # SUM is not set and we are trying to access a user + # page in supervisor mode + ld_st_priv_lvl_i == PRIV_LVL_S & ~sum_i & \ + dtlb_pte_q.u) | \ + # this is not a user page but we are in user mode and + # trying to access it + (ld_st_priv_lvl_i == PRIV_LVL_U & ~dtlb_pte_q.u)) + # translation is enabled and no misaligned exception occurred - if (en_ld_st_translation_i && !misaligned_ex_q.valid) begin - lsu_valid_o = 1'b0; + with m.If(en_ld_st_translation_i & ~misaligned_ex_q.valid): + m.d.comb += lsu_valid_o.eq(0) # 4K page - lsu_paddr_o = {dtlb_pte_q.ppn, lsu_vaddr_q[11:0]}; + paddr = Signal.like(lsu_vaddr_q) + paddr4k = Cat(lsu_vaddr_q[0:12], itlb_content.ppn) + m.d.comb += paddr.eq(paddr4k) # Mega page - if (dtlb_is_2M_q) begin - lsu_paddr_o[20:12] = lsu_vaddr_q[20:12]; + with m.If(dtlb_is_2M): + m.d.comb += paddr[12:21].eq(lsu_vaddr_q[12:21]) end # Giga page - if (dtlb_is_1G_q) begin - lsu_paddr_o[29:12] = lsu_vaddr_q[29:12]; - end + with m.If(dtlb_is_1G): + m.d.comb += paddr[12:30].eq(lsu_vaddr_q[12:30]) + m.d.comb += lsu_paddr_o.eq(paddr) + # --------- # DTLB Hit # -------- - if (dtlb_hit_q && lsu_req_q) begin - lsu_valid_o = 1'b1; + with m.If(dtlb_hit_q & lsu_req_q): + m.d.comb += lsu_valid_o.eq(1) # this is a store - if (lsu_is_store_q) begin - # check if the page is write-able and we are not violating privileges + with m.If (lsu_is_store_q): + # 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 - + with m.If(~dtlb_pte_q.w | daccess_err | ~dtlb_pte_q.d): + le = lsu_exception_o + m.d.comb += [le.cause.eq(STORE_PAGE_FAULT), + le.tval.eq(lsu_vaddr_q), + le.valid.eq(1) + ] + + # this is a load, check for sufficient access + # privileges - throw a page fault if necessary + with m.Elif(daccess_err): + le = lsu_exception_o + m.d.comb += [le.cause.eq(LOAD_PAGE_FAULT), + le.tval.eq(lsu_vaddr_q), + le.valid.eq(1) + ] # --------- # DTLB Miss # --------- # watch out for exceptions - if (ptw_active && !walking_instr) begin + with m.Elif (ptw_active & ~walking_instr): # page table walker threw an exception - if (ptw_error) begin + with m.If (ptw_error): # an error makes the translation valid - lsu_valid_o = 1'b1; + m.d.comb += lsu_valid_o.eq(1) # 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 + with m.If (lsu_is_store_q): + le = lsu_exception_o + m.d.comb += [le.cause.eq(STORE_PAGE_FAULT), + le.tval.eq(uaddr64), + le.valid.eq(1) + ] + with m.Else(): + m.d.comb += [le.cause.eq(LOAD_PAGE_FAULT), + le.tval.eq(uaddr64), + le.valid.eq(1) + ] + -- 2.30.2