From 7f708f59784c0b424093dd9aa4cfc6cd5e730c7b Mon Sep 17 00:00:00 2001 From: Cole Poirier Date: Sun, 9 Aug 2020 18:50:08 -0700 Subject: [PATCH] mmu.vhdl translation to mmu.py 95 percent complete --- src/soc/experiment/mmu.py | 1271 +++++++++++++++++++++++++------------ 1 file changed, 850 insertions(+), 421 deletions(-) diff --git a/src/soc/experiment/mmu.py b/src/soc/experiment/mmu.py index 603ac12a..8775652b 100644 --- a/src/soc/experiment/mmu.py +++ b/src/soc/experiment/mmu.py @@ -1,9 +1,13 @@ -from enum import Enum, unique +"""MMU + +based on Anton Blanchard microwatt mmu.vhdl +""" +from enum import Enum, unique from nmigen import (Module, Signal, Elaboratable, Mux, Cat, Repl, signed, ResetSignal) from nmigen.cli import main - +from nmigen.iocontrol import RecordObject # library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; @@ -26,6 +30,8 @@ from nmigen.cli import main # RADIX_FINISH # ); +# architecture behave of mmu is + @unique class State(Enum): IDLE = 0 @@ -70,40 +76,42 @@ class State(Enum): # end record; -class RegStage(): - def __init__(self): +class RegStage(RecordObject): + def __init__(self, name=None): + super().__init__(self, name=name) # latched request from loadstore1 - self.valid = Signal(1) - self.iside = Signal(1) - self.store = Signal(1) - self.priv = Signal(1) - self.addr = Signal(64) - self.inval_all = Signal(1) + self.valid = Signal(reset_less=True) + self.iside = Signal(reset_less=True) + self.store = Signal(reset_less=True) + self.priv = Signal(reset_less=True) + self.addr = Signal(64, reset_less=True) + self.inval_all = Signal(reset_less=True) # config SPRs - self.prtbl = Signal(64) - self.pid = Signal(32 + self.prtbl = Signal(64, reset_less=True) + self.pid = Signal(32, reset_less=True) # internal state self.state = State.IDLE - self.done = Signal(1) - self.err = Signal(1) - self.pgtbl0 = Signal(64) - self.pt0_valid = Signal(1) - self.pgtbl3 = Signal(64) - self.pt3_valid = Signal(1) - self.shift = Signal(5) - self.mask_size = Signal(4) - self.pgbase = Signal(56) - self.pde = Signal(64) - self.invalid = Signal(1) - self.badtree = Signal(1) - self.segerror = Signal(1) - self.perm_err = Signal(1) - self.rc_error = Signal(1) + self.done = Signal(reset_less=True) + self.err = Signal(reset_less=True) + self.pgtbl0 = Signal(64, reset_less=True) + self.pt0_valid = Signal(reset_less=True) + self.pgtbl3 = Signal(64, reset_less=True) + self.pt3_valid = Signal(reset_less=True) + self.shift = Signal(6, reset_less=True) + self.mask_size = Signal(5, reset_less=True) + self.pgbase = Signal(56, reset_less=True) + self.pde = Signal(64, reset_less=True) + self.invalid = Signal(reset_less=True) + self.badtree = Signal(reset_less=True) + self.segerror = Signal(reset_less=True) + self.perm_err = Signal(reset_less=True) + self.rc_error = Signal(reset_less=True) -# architecture behave of mmu is +# Radix MMU +# Supports 4-level trees as in arch 3.0B, but not the two-step translation +# for guests under a hypervisor (i.e. there is no gRA -> hRA translation). class MMU(Elaboratable): - # entity mmu is # port ( # clk : in std_ulogic; @@ -118,17 +126,16 @@ class MMU(Elaboratable): # i_out : out MmuToIcacheType # ); # end mmu; - def __init__(self, l_in, l_out, d_out, d_in, i_out): - self.l_in = l_in - self.l_out = l_out - self.d_out = d_out - self.d_in = d_in - self.i_out = i_out - - # begin + def __init__(self): + self.l_in = Loadstore1ToMmuType() + self.l_out = MmuToLoadstore1Type() + self.d_out = MmuToDcacheType() + self.d_in = DcacheToMmuType() + self.i_out = MmuToIcacheType() + def elaborate(self, platform): -# -- Multiplex internal SPR values back to loadstore1, selected -# -- by l_in.sprn. +# -- Multiplex internal SPR values back to loadstore1, selected +# -- by l_in.sprn. # Multiplex internal SPR values back to loadstore1, selected by # l_in.sprn. @@ -156,18 +163,36 @@ class MMU(Elaboratable): # signal mask : std_ulogic_vector(15 downto 0); # signal finalmask : std_ulogic_vector(43 downto 0); addrsh = Signal(16) - mask = Signal(15) + mask = Signal(16) finalmask = Signal(44) +# begin + # l_out.sprval <= r.prtbl when l_in.sprn(9) = '1' with m.If(l_in.sprn[9] == 1): - m.d.comb += l_out.sprval.eq(r.prtbl) + comb += l_out.sprval.eq(r.prtbl) # else x"00000000" & r.pid; with m.Else(): - m.d.comb += l_out.sprval.eq(0x00000000 & r) + comb += l_out.sprval.eq(0x00000000 & r) + + + # mmu_0: process(clk) +class MMU0(Elaboratable): + def __init__(self, clk): + self.clk = clk + # begin + def elaborate(self, platform): + + m = Module() + + comb = m.d.comb + sync = m.d.sync + + rst = ResetSignal() + # if rising_edge(clk) then with m.If(rising_edge): # if rst = '1' then @@ -177,12 +202,12 @@ class MMU(Elaboratable): # r.pt0_valid <= '0'; # r.pt3_valid <= '0'; # r.prtbl <= (others => '0'); - r.state = State.IDLE - r.valid = 0 - r.pt0_valid = 0 - r.pt3_valid = 0 - # value should be vhdl (others => '0') in nmigen - r.prtbl = 0 + sync += r.state.eq(State.IDLE) + sync += r.valid.eq(0) + sync += r.pt0_valid.eq(0) + sync += r.pt3_valid.eq(0) + # TODO value should be vhdl (others => '0') in nmigen + sync += r.prtbl.eq(0) # else with m.Else(): # if rin.valid = '1' then @@ -225,7 +250,7 @@ class MMU(Elaboratable): addrsh={addrsh} mask={mask}") # r <= rin; - comb += r.eq(rin) + sync += r.eq(rin) # end if; # end if; # end process; @@ -233,379 +258,783 @@ class MMU(Elaboratable): # -- Shift address bits 61--12 right by 0--47 bits and # -- supply the least significant 16 bits of the result. # addrshifter: process(all) -# variable sh1 : std_ulogic_vector(30 downto 0); -# variable sh2 : std_ulogic_vector(18 downto 0); -# variable result : std_ulogic_vector(15 downto 0); -# begin -# case r.shift(5 downto 4) is -# when "00" => -# sh1 := r.addr(42 downto 12); -# when "01" => -# sh1 := r.addr(58 downto 28); -# when others => -# sh1 := "0000000000000" & r.addr(61 downto 44); -# end case; -# case r.shift(3 downto 2) is -# when "00" => -# sh2 := sh1(18 downto 0); -# when "01" => -# sh2 := sh1(22 downto 4); -# when "10" => -# sh2 := sh1(26 downto 8); -# when others => -# sh2 := sh1(30 downto 12); -# end case; -# case r.shift(1 downto 0) is -# when "00" => -# result := sh2(15 downto 0); -# when "01" => -# result := sh2(16 downto 1); -# when "10" => -# result := sh2(17 downto 2); -# when others => -# result := sh2(18 downto 3); -# end case; -# addrsh <= result; -# end process; -# -# -- generate mask for extracting address fields for PTE address generation -# addrmaskgen: process(all) -# variable m : std_ulogic_vector(15 downto 0); -# begin -# -- mask_count has to be >= 5 -# m := x"001f"; -# for i in 5 to 15 loop -# if i < to_integer(r.mask_size) then -# m(i) := '1'; -# end if; -# end loop; -# mask <= m; -# end process; -# -# -- generate mask for extracting address bits to go in TLB entry -# -- in order to support pages > 4kB -# finalmaskgen: process(all) -# variable m : std_ulogic_vector(43 downto 0); -# begin -# m := (others => '0'); -# for i in 0 to 43 loop -# if i < to_integer(r.shift) then -# m(i) := '1'; -# end if; -# end loop; -# finalmask <= m; -# end process; -# -# mmu_1: process(all) -# variable v : reg_stage_t; -# variable dcreq : std_ulogic; -# variable tlb_load : std_ulogic; -# variable itlb_load : std_ulogic; -# variable tlbie_req : std_ulogic; -# variable prtbl_rd : std_ulogic; -# variable pt_valid : std_ulogic; -# variable effpid : std_ulogic_vector(31 downto 0); -# variable prtable_addr : std_ulogic_vector(63 downto 0); -# variable rts : unsigned(5 downto 0); -# variable mbits : unsigned(5 downto 0); -# variable pgtable_addr : std_ulogic_vector(63 downto 0); -# variable pte : std_ulogic_vector(63 downto 0); -# variable tlb_data : std_ulogic_vector(63 downto 0); -# variable nonzero : std_ulogic; -# variable pgtbl : std_ulogic_vector(63 downto 0); -# variable perm_ok : std_ulogic; -# variable rc_ok : std_ulogic; -# variable addr : std_ulogic_vector(63 downto 0); -# variable data : std_ulogic_vector(63 downto 0); -# begin -# v := r; -# v.valid := '0'; -# dcreq := '0'; -# v.done := '0'; -# v.err := '0'; -# v.invalid := '0'; -# v.badtree := '0'; -# v.segerror := '0'; -# v.perm_err := '0'; -# v.rc_error := '0'; -# tlb_load := '0'; -# itlb_load := '0'; -# tlbie_req := '0'; -# v.inval_all := '0'; -# prtbl_rd := '0'; -# -# -- Radix tree data structures in memory are big-endian, -# -- so we need to byte-swap them -# for i in 0 to 7 loop -# data(i * 8 + 7 downto i * 8) := d_in.data((7 - i) * 8 + 7 downto -# (7 - i) * 8); -# end loop; -# -# case r.state is -# when IDLE => -# if l_in.addr(63) = '0' then -# pgtbl := r.pgtbl0; -# pt_valid := r.pt0_valid; -# else -# pgtbl := r.pgtbl3; -# pt_valid := r.pt3_valid; -# end if; -# -- rts == radix tree size, # address bits being translated -# rts := unsigned('0' & pgtbl(62 downto 61) & pgtbl(7 downto 5)); -# -- mbits == # address bits to index top level of tree -# mbits := unsigned('0' & pgtbl(4 downto 0)); -# -- set v.shift to rts so that we can use finalmask for the -# segment check -# v.shift := rts; -# v.mask_size := mbits(4 downto 0); -# v.pgbase := pgtbl(55 downto 8) & x"00"; -# -# if l_in.valid = '1' then -# v.addr := l_in.addr; -# v.iside := l_in.iside; -# v.store := not (l_in.load or l_in.iside); -# v.priv := l_in.priv; -# if l_in.tlbie = '1' then -# -- Invalidate all iTLB/dTLB entries for tlbie with -# -- RB[IS] != 0 or RB[AP] != 0, or for slbia -# v.inval_all := l_in.slbia or l_in.addr(11) or l_in. -# addr(10) or l_in.addr(7) or l_in.addr(6) -# or l_in.addr(5); -# -- The RIC field of the tlbie instruction comes across -# -- on the sprn bus as bits 2--3. RIC=2 flushes process -# -- table caches. -# if l_in.sprn(3) = '1' then -# v.pt0_valid := '0'; -# v.pt3_valid := '0'; -# end if; -# v.state := DO_TLBIE; -# else -# v.valid := '1'; -# if pt_valid = '0' then -# -- need to fetch process table entry -# -- set v.shift so we can use finalmask for generating -# -- the process table entry address -# v.shift := unsigned('0' & r.prtbl(4 downto 0)); -# v.state := PROC_TBL_READ; -# elsif mbits = 0 then -# -- Use RPDS = 0 to disable radix tree walks -# v.state := RADIX_FINISH; -# v.invalid := '1'; -# else -# v.state := SEGMENT_CHECK; -# end if; -# end if; -# end if; -# if l_in.mtspr = '1' then -# -- Move to PID needs to invalidate L1 TLBs and cached -# -- pgtbl0 value. Move to PRTBL does that plus -# -- invalidating the cached pgtbl3 value as well. -# if l_in.sprn(9) = '0' then -# v.pid := l_in.rs(31 downto 0); -# else -# v.prtbl := l_in.rs; -# v.pt3_valid := '0'; -# end if; -# v.pt0_valid := '0'; -# v.inval_all := '1'; -# v.state := DO_TLBIE; -# end if; -# -# when DO_TLBIE => -# dcreq := '1'; -# tlbie_req := '1'; -# v.state := TLB_WAIT; -# -# when TLB_WAIT => -# if d_in.done = '1' then -# v.state := RADIX_FINISH; -# end if; -# -# when PROC_TBL_READ => -# dcreq := '1'; -# prtbl_rd := '1'; -# v.state := PROC_TBL_WAIT; -# -# when PROC_TBL_WAIT => -# if d_in.done = '1' then -# if r.addr(63) = '1' then -# v.pgtbl3 := data; -# v.pt3_valid := '1'; -# else -# v.pgtbl0 := data; -# v.pt0_valid := '1'; -# end if; -# -- rts == radix tree size, # address bits being translated -# rts := unsigned('0' & data(62 downto 61) & data(7 downto 5)); -# -- mbits == # address bits to index top level of tree -# mbits := unsigned('0' & data(4 downto 0)); -# -- set v.shift to rts so that we can use finalmask for the -# -- segment check -# v.shift := rts; -# v.mask_size := mbits(4 downto 0); -# v.pgbase := data(55 downto 8) & x"00"; -# if mbits = 0 then -# v.state := RADIX_FINISH; -# v.invalid := '1'; -# else -# v.state := SEGMENT_CHECK; -# end if; -# end if; -# if d_in.err = '1' then -# v.state := RADIX_FINISH; -# v.badtree := '1'; -# end if; -# -# when SEGMENT_CHECK => -# mbits := '0' & r.mask_size; -# v.shift := r.shift + (31 - 12) - mbits; -# nonzero := or(r.addr(61 downto 31) and not finalmask( -# 30 downto 0)); -# if r.addr(63) /= r.addr(62) or nonzero = '1' then -# v.state := RADIX_FINISH; -# v.segerror := '1'; -# elsif mbits < 5 or mbits > 16 or mbits > -# (r.shift + (31 - 12)) then -# v.state := RADIX_FINISH; -# v.badtree := '1'; -# else -# v.state := RADIX_LOOKUP; -# end if; -# -# when RADIX_LOOKUP => -# dcreq := '1'; -# v.state := RADIX_READ_WAIT; -# -# when RADIX_READ_WAIT => -# if d_in.done = '1' then -# v.pde := data; -# -- test valid bit -# if data(63) = '1' then -# -- test leaf bit -# if data(62) = '1' then -# -- check permissions and RC bits -# perm_ok := '0'; -# if r.priv = '1' or data(3) = '0' then -# if r.iside = '0' then -# perm_ok := data(1) or (data(2) and not -# r.store); -# else -# -- no IAMR, so no KUEP support for now -# -- deny execute permission if cache inhibited -# perm_ok := data(0) and not data(5); -# end if; -# end if; -# rc_ok := data(8) and (data(7) or not r.store); -# if perm_ok = '1' and rc_ok = '1' then -# v.state := RADIX_LOAD_TLB; -# else -# v.state := RADIX_FINISH; -# v.perm_err := not perm_ok; -# -- permission error takes precedence over -# -- RC error -# v.rc_error := perm_ok; -# end if; -# else -# mbits := unsigned('0' & data(4 downto 0)); -# if mbits < 5 or mbits > 16 or mbits > r.shift then -# v.state := RADIX_FINISH; -# v.badtree := '1'; -# else -# v.shift := v.shift - mbits; -# v.mask_size := mbits(4 downto 0); -# v.pgbase := data(55 downto 8) & x"00"; -# v.state := RADIX_LOOKUP; -# end if; -# end if; -# else -# -- non-present PTE, generate a DSI -# v.state := RADIX_FINISH; -# v.invalid := '1'; -# end if; -# end if; -# if d_in.err = '1' then -# v.state := RADIX_FINISH; -# v.badtree := '1'; -# end if; -# -# when RADIX_LOAD_TLB => -# tlb_load := '1'; -# if r.iside = '0' then -# dcreq := '1'; -# v.state := TLB_WAIT; -# else -# itlb_load := '1'; -# v.state := IDLE; -# end if; -# -# when RADIX_FINISH => -# v.state := IDLE; -# -# end case; -# -# if v.state = RADIX_FINISH or (v.state = RADIX_LOAD_TLB -# and r.iside = '1') then -# v.err := v.invalid or v.badtree or v.segerror or v.perm_err -# or v.rc_error; -# v.done := not v.err; -# end if; -# -# if r.addr(63) = '1' then -# effpid := x"00000000"; -# else -# effpid := r.pid; -# end if; -# prtable_addr := x"00" & r.prtbl(55 downto 36) & -# ((r.prtbl(35 downto 12) and not finalmask( -# 23 downto 0)) or (effpid(31 downto 8) and -# finalmask(23 downto 0))) & effpid(7 downto 0) -# & "0000"; -# -# pgtable_addr := x"00" & r.pgbase(55 downto 19) & -# ((r.pgbase(18 downto 3) and not mask) or -# (addrsh and mask)) & "000"; -# -# pte := x"00" & ((r.pde(55 downto 12) and not finalmask) or -# (r.addr(55 downto 12) and finalmask)) & r.pde(11 downto 0); -# -# -- update registers -# rin <= v; -# -# -- drive outputs -# if tlbie_req = '1' then -# addr := r.addr; -# tlb_data := (others => '0'); -# elsif tlb_load = '1' then -# addr := r.addr(63 downto 12) & x"000"; -# tlb_data := pte; -# elsif prtbl_rd = '1' then -# addr := prtable_addr; -# tlb_data := (others => '0'); -# else -# addr := pgtable_addr; -# tlb_data := (others => '0'); -# end if; + +# Shift address bits 61--12 right by 0--47 bits and +# supply the least significant 16 bits of the result. +class AddrShifter(Elaboratable): + + def __init__(self): +# variable sh1 : std_ulogic_vector(30 downto 0); +# variable sh2 : std_ulogic_vector(18 downto 0); +# variable result : std_ulogic_vector(15 downto 0); + self.sh1 = Signal(31) + self.sh2 = Signal(19) + self.result = Signal(16) + + +# begin + def elaborate(self, platform): + + m = Module() + + comb = m.d.comb + sync = m.d.sync + + rst = ResetSignal() + + sh1 = self.sh1 + sh2 = self.sh2 + result = self.result + +# case r.shift(5 downto 4) is + with m.Switch(r.shift[4:6]): +# when "00" => +# sh1 := r.addr(42 downto 12); + with m.Case(0): + comb += sh1.eq(r.addr[12:43]) +# when "01" => +# sh1 := r.addr(58 downto 28); + with m.Case(1): + comb += sh1.eq(r.addr[28:59]) +# when others => +# sh1 := "0000000000000" & r.addr(61 downto 44); + with m.Default(): + comb += sh1.eq(r.addr[44:62]) +# end case; + +# case r.shift(3 downto 2) is + with m.Switch(r.shift[2:4]): +# when "00" => +# sh2 := sh1(18 downto 0); + with m.Case(0): + comb += sh2.eq(sh1[0:19]) +# when "01" => +# sh2 := sh1(22 downto 4); + with m.Case(1): + comb += sh2.eq(sh1[4:23]) +# when "10" => +# sh2 := sh1(26 downto 8); + with m.Case(2): + comb += sh2.eq(sh1[8:27]) +# when others => +# sh2 := sh1(30 downto 12); + with m.Default(): + comb += sh2.eq(sh1[12:31]) +# end case; + +# case r.shift(1 downto 0) is + with m.Switch(r.shift[0:2]): +# when "00" => +# result := sh2(15 downto 0); + with m.Case(0): + comb += result.eq(sh1[0:16]) +# when "01" => +# result := sh2(16 downto 1); + with m.Case(1): + comb += result.eq(sh1[1:17]) +# when "10" => +# result := sh2(17 downto 2); + with m.Case(2): + comb += result.eq(sh1[2:18]) +# when others => +# result := sh2(18 downto 3); + with m.Default(): + comb += result.eq(sh1[3:19]) +# end case; +# addrsh <= result; + comb += self.addrsh.eq(result) +# end process; + +# -- generate mask for extracting address fields for PTE address generation +# addrmaskgen: process(all) + # generate mask for extracting address fields for PTE address generation + class AddrMaskGen(Elaboratable): + def __init__(self): +# variable m : std_ulogic_vector(15 downto 0); + self.mask = Signal(16) + +# begin + def elaborate(self, platform): + m = Module() + + comb = m.d.comb + sync = m.d.sync + + rst = ResetSignal() + + mask = self.mask + +# -- mask_count has to be >= 5 +# m := x"001f"; + # mask_count has to be >= 5 + comb += mask.eq(0x001F) + +# for i in 5 to 15 loop + for i in range(5,16): +# if i < to_integer(r.mask_size) then + with m.If(i < r.mask_size): +# m(i) := '1'; + comb += mask[i].eq(1) +# end if; +# end loop; +# mask <= m; + comb += self.mask.eq(mask) +# end process; # -# l_out.done <= r.done; -# l_out.err <= r.err; -# l_out.invalid <= r.invalid; -# l_out.badtree <= r.badtree; -# l_out.segerr <= r.segerror; -# l_out.perm_error <= r.perm_err; -# l_out.rc_error <= r.rc_error; +# -- generate mask for extracting address bits to go in TLB entry +# -- in order to support pages > 4kB +# finalmaskgen: process(all) + +# generate mask for extracting address bits to go in TLB entry +# in order to support pages > 4kB + class FinalMaskGen(Elaboratable): +# variable m : std_ulogic_vector(43 downto 0); + def __init__(self): + self.mask = Signal(44) +# begin + def elaborate(self, platform): + m = Module() + + comb = m.d.comb + sync = m.d.sync + + rst = ResetSignal() + + mask = self.mask + +# m := (others => '0'); + # TODO value should be vhdl (others => '0') in nmigen + comb += mask.eq(0) + +# for i in 0 to 43 loop + for i in range(44): +# if i < to_integer(r.shift) then + with m.If(i < r.shift): +# m(i) := '1'; + comb += mask.eq(1) +# end if; +# end loop; +# finalmask <= m; + comb += self.finalmask(mask) +# end process; # -# d_out.valid <= dcreq; -# d_out.tlbie <= tlbie_req; -# d_out.doall <= r.inval_all; -# d_out.tlbld <= tlb_load; -# d_out.addr <= addr; -# d_out.pte <= tlb_data; +# mmu_1: process(all) + class MMU1(Elaboratable): + + def __init__(self): +# variable v : reg_stage_t; +# variable dcreq : std_ulogic; +# variable tlb_load : std_ulogic; +# variable itlb_load : std_ulogic; +# variable tlbie_req : std_ulogic; +# variable prtbl_rd : std_ulogic; +# variable pt_valid : std_ulogic; +# variable effpid : std_ulogic_vector(31 downto 0); +# variable prtable_addr : std_ulogic_vector(63 downto 0); +# variable rts : unsigned(5 downto 0); +# variable mbits : unsigned(5 downto 0); +# variable pgtable_addr : std_ulogic_vector(63 downto 0); +# variable pte : std_ulogic_vector(63 downto 0); +# variable tlb_data : std_ulogic_vector(63 downto 0); +# variable nonzero : std_ulogic; +# variable pgtbl : std_ulogic_vector(63 downto 0); +# variable perm_ok : std_ulogic; +# variable rc_ok : std_ulogic; +# variable addr : std_ulogic_vector(63 downto 0); +# variable data : std_ulogic_vector(63 downto 0); + self.v = RegStage() + self.dcrq = Signal() + self.tlb_load = Signal() + self.itlb_load = Signal() + self.tlbie_req = Signal() + self.prtbl_rd = Signal() + self.pt_valid = Signal() + self.effpid = Signal(32) + self.prtable_addr = Signal(64) + self.rts = Signal(6) + self.mbits = Signal(6) + self.pgtable_addr = Signal(64) + self.pte = Signal(64) + self.tlb_data = Signal(64) + self.nonzero = Signal() + self.pgtbl = Signal(64) + self.perm_ok = Signal() + self.rc_ok = Signal() + self.addr = Signal(64) + self.data = Signal(64) + +# begin + def elaborate(self, platform): + + m = Module() + + comb = m.d.comb + sync = m.d.sync + + rst = ResetSignal() + + + l_in = self.l_in + l_out = self.l_out + d_out = self.d_out + d_in = self.d_in + i_out = self.i_out + + r = self.r + + v = self.v + dcrq = self.dcrq + tlb_load = self.tlb_load + itlb_load = self.itlb_load + tlbie_req = self.tlbie_req + prtbl_rd = self.prtbl_rd + pt_valid = self.pt_valid + effpid = self.effpid + prtable_addr = self.prtable_addr + rts = self.rts + mbits = self.mbits + pgtable_addr = self.pgtable_addr + pte = self.pte + tlb_data = self.tlb_data + nonzero = self.nonzero + pgtbl = self.pgtbl + perm_ok = self.perm_ok + rc_ok = self.rc_ok + addr = self.addr + data = self.data + +# v := r; +# v.valid := '0'; +# dcreq := '0'; +# v.done := '0'; +# v.err := '0'; +# v.invalid := '0'; +# v.badtree := '0'; +# v.segerror := '0'; +# v.perm_err := '0'; +# v.rc_error := '0'; +# tlb_load := '0'; +# itlb_load := '0'; +# tlbie_req := '0'; +# v.inval_all := '0'; +# prtbl_rd := '0'; + + comb += v.eq(r) + comb += v.valid.eq(0) + comb += dcreq.eq(0) + comb += v.done.eq(0) + comb += v.err.eq(0) + comb += v.invalid.eq(0) + comb += v.badtree.eq(0) + comb += v.segerror.eq(0) + comb += v.perm_err.eq(0) + comb += v.rc_error.eq(0) + comb += tlb_load.eq(0) + comb += itlb_load.eq(0) + comb += tlbie_req.eq(0) + comb += v.inval_all.eq(0) + comb += prtbl_rd.eq(0) + + +# -- Radix tree data structures in memory are big-endian, +# -- so we need to byte-swap them +# for i in 0 to 7 loop + # Radix tree data structures in memory are big-endian, + # so we need to byte-swap them + for i in range(8): +# data(i * 8 + 7 downto i * 8) := d_in.data((7 - i) * 8 + 7 downto +# (7 - i) * 8); + comb += data[ + i * 8:i * 8 + 7 + 1 + ].eq(d_in.data[ + (7 - i) * 8:(7 - i) * 8 + 7 + 1 + ]) +# end loop; + +# case r.state is + with m.Switch(r.state): +# when IDLE => + with m.Case(State.IDLE): +# if l_in.addr(63) = '0' then +# pgtbl := r.pgtbl0; +# pt_valid := r.pt0_valid; + with m.If(l_in.addr[63] == 0): + comb += pgtbl.eq(r.pgtbl0) + comb += pt_valid.eq(r.pt0_valid) +# else +# pgtbl := r.pgtbl3; +# pt_valid := r.pt3_valid; + with m.Else(): + comb += pgtbl.eq(r.pt3_valid) +# end if; + +# -- rts == radix tree size, # address bits being translated +# rts := unsigned('0' & pgtbl(62 downto 61) & pgtbl(7 downto 5)); + # rts == radix tree size, number of address bits being translated + comb += rts.eq((0 & pgtbl[61:63] & pgtbl[5:8]).as_unsigned()) + +# -- mbits == # address bits to index top level of tree +# mbits := unsigned('0' & pgtbl(4 downto 0)); + # mbits == number of address bits to index top level of tree + comb += mbits.eq((0 & pgtbl[0:5]).as_unsigned()) +# -- set v.shift to rts so that we can use finalmask for the +# segment check +# v.shift := rts; +# v.mask_size := mbits(4 downto 0); +# v.pgbase := pgtbl(55 downto 8) & x"00"; + # set v.shift to rts so that we can use finalmask for the segment + # check + comb += v.shift.eq(rts) + comb += v.mask_size.eq(mbits[0:5]) + comb += v.pgbase.eq(pgtbl[8:56] & 0x00) + +# if l_in.valid = '1' then + with m.If(l_in.valid == 1): +# v.addr := l_in.addr; +# v.iside := l_in.iside; +# v.store := not (l_in.load or l_in.iside); +# v.priv := l_in.priv; + comb += v.addr.eq(l_in.addr + comb += v.iside.eq(l_in.iside) + comb += v.store.eq(~(l_in.load ^ l_in.siside)) +# if l_in.tlbie = '1' then + with m.If(l_in.tlbie == 1): +# -- Invalidate all iTLB/dTLB entries for tlbie with +# -- RB[IS] != 0 or RB[AP] != 0, or for slbia +# v.inval_all := l_in.slbia or l_in.addr(11) or l_in. +# addr(10) or l_in.addr(7) or l_in.addr(6) +# or l_in.addr(5); + # Invalidate all iTLB/dTLB entries for tlbie with + # RB[IS] != 0 or RB[AP] != 0, or for slbia + comb += v.inval_all.eq(l_in.slbia ^ l_in.addr[11] ^ + l_in.addr[10] ^ l_in.addr[7] ^ + l_in.addr[6] ^ l_in.addr[5]) +# -- The RIC field of the tlbie instruction comes across +# -- on the sprn bus as bits 2--3. RIC=2 flushes process +# -- table caches. +# if l_in.sprn(3) = '1' then + # The RIC field of the tlbie instruction comes across + # on the sprn bus as bits 2--3. RIC=2 flushes process + # table caches. + with m.If(l_in.sprn[3] == 1): +# v.pt0_valid := '0'; +# v.pt3_valid := '0'; + comb += v.pt0_valid.eq(0) + comb += v.pt3_valid.eq(0) +# end if; +# v.state := DO_TLBIE; + comb += v.state.eq(State.DO_TLBIE) +# else + with m.Else(): +# v.valid := '1'; + comb += v.valid.eq(1) +# if pt_valid = '0' then + with m.If(pt_valid == 0): +# -- need to fetch process table entry +# -- set v.shift so we can use finalmask for generating +# -- the process table entry address +# v.shift := unsigned('0' & r.prtbl(4 downto 0)); +# v.state := PROC_TBL_READ; + # need to fetch process table entry + # set v.shift so we can use finalmask for generating + # the process table entry address + comb += v.shift.eq((0 & r.prtble[0:5]).as_unsigned()) + comb += v.state.eq(State.PROC_TBL_READ) + +# elsif mbits = 0 then + with m.If(mbits == 0): +# -- Use RPDS = 0 to disable radix tree walks +# v.state := RADIX_FINISH; +# v.invalid := '1'; + # Use RPDS = 0 to disable radix tree walks + comb += v.state.eq(State.RADIX_FINISH) + comb += v.invalid.eq(1) +# else + with m.Else(): +# v.state := SEGMENT_CHECK; + comb += v.state.eq(State.SEGMENT_CHECK) +# end if; +# end if; +# end if; + +# if l_in.mtspr = '1' then + with m.If(l_in.mtspr == 1): +# -- Move to PID needs to invalidate L1 TLBs and cached +# -- pgtbl0 value. Move to PRTBL does that plus +# -- invalidating the cached pgtbl3 value as well. +# if l_in.sprn(9) = '0' then + # Move to PID needs to invalidate L1 TLBs and cached + # pgtbl0 value. Move to PRTBL does that plus + # invalidating the cached pgtbl3 value as well. + with m.If(l_in.sprn[9] == 0): +# v.pid := l_in.rs(31 downto 0); + comb += v.pid.eq(l_in.rs[0:32]) +# else + with m.Else(): +# v.prtbl := l_in.rs; +# v.pt3_valid := '0'; + comb += v.prtbl.eq(l_in.rs) + comb += v.pt3_valid.eq(0) +# end if; + +# v.pt0_valid := '0'; +# v.inval_all := '1'; +# v.state := DO_TLBIE; + comb += v.pt0_valid.eq(0) + comb += v.inval_all.eq(0) + comb += v.state.eq(State.DO_TLBIE) +# end if; + +# when DO_TLBIE => + with m.Case(State.DO_TLBIE): +# dcreq := '1'; +# tlbie_req := '1'; +# v.state := TLB_WAIT; + comb += dcreq.eq(1) + comb += tlbie_req.eq(1) + comb += v.state.eq(State.TLB_WAIT) + +# when TLB_WAIT => + with m.Case(State.TLB_WAIT): +# if d_in.done = '1' then + with m.If(d_in.done == 1): +# v.state := RADIX_FINISH; + comb += v.state.eq(State.RADIX_FINISH) +# end if; + +# when PROC_TBL_READ => + with m.Case(State.PROC_TBL_READ): +# dcreq := '1'; +# prtbl_rd := '1'; +# v.state := PROC_TBL_WAIT; + comb += dcreq.eq(1) + comb += prtbl_rd.eq(1) + comb += v.state.eq(State.PROC_TBL_WAIT) + +# when PROC_TBL_WAIT => + with m.Case(State.PROC_TBL_WAIT): +# if d_in.done = '1' then + with m.If(d_in.done == 1): +# if r.addr(63) = '1' then + with m.If(r.addr[63] == 1): +# v.pgtbl3 := data; +# v.pt3_valid := '1'; + comb += v.pgtbl3.eq(data) + comb += v.pt3_valid.eq(1) +# else + with m.Else(): +# v.pgtbl0 := data; +# v.pt0_valid := '1'; + comb += v.pgtbl0.eq(data) + comb += v.pt0_valid.eq(1) +# end if; +# -- rts == radix tree size, # address bits being translated +# rts := unsigned('0' & data(62 downto 61) & data(7 downto 5)); + # rts == radix tree size, # address bits being translated + comb += rts.eq((0 & data[61:63] & data[5:8]).as_unsigned()) +# -- mbits == # address bits to index top level of tree +# mbits := unsigned('0' & data(4 downto 0)); + # mbits == # address bits to index top level of tree + comb += mbits.eq((0 & data[0:5]).as_unsigned()) +# -- set v.shift to rts so that we can use finalmask for the +# -- segment check +# v.shift := rts; +# v.mask_size := mbits(4 downto 0); +# v.pgbase := data(55 downto 8) & x"00"; + # set v.shift to rts so that we can use finalmask for the + # segment check + comb += v.shift.eq(rts) + comb += v.mask_size.eq(mbits[0:5]) + comb += v.pgbase.eq(data[8:56] & 0x00) +# if mbits = 0 then + with m.If(mbits == 0): +# v.state := RADIX_FINISH; +# v.invalid := '1'; + comb += v.state.eq(State.RADIX_FINISH) + comb += v.invalid.eq(1) +# else +# v.state := SEGMENT_CHECK; + comb += v.state.eq(State.SEGMENT_CHECK) +# end if; +# end if; + +# if d_in.err = '1' then + with m.If(d_in.err === 1): +# v.state := RADIX_FINISH; +# v.badtree := '1'; + comb += v.state.eq(State.RADIX_FINISH) + comb += v.badtree.eq(1) +# end if; + +# when SEGMENT_CHECK => + with m.Case(State.SEGMENT_CHECK): +# mbits := '0' & r.mask_size; +# v.shift := r.shift + (31 - 12) - mbits; +# nonzero := or(r.addr(61 downto 31) and not finalmask( +# 30 downto 0)); + comb += mbits.eq(0 & r.mask_size) + comb += v.shift.eq(r.shift + (31 -12) - mbits) + comb += nonzero.eq('''TODO wrap in or (?)'''r.addr[31:62] + & (~finalmask[0:31])) +# if r.addr(63) /= r.addr(62) or nonzero = '1' then +# v.state := RADIX_FINISH; +# v.segerror := '1'; + with m.If((r.addr[63] != r.addr[62]) ^ (nonzero == 1)): + comb += v.state.eq(State.RADIX_FINISH) + comb += v.segerror.eq(1) +# elsif mbits < 5 or mbits > 16 or mbits > +# (r.shift + (31 - 12)) then +# v.state := RADIX_FINISH; +# v.badtree := '1'; + with m.If((mbits < 5) ^ (mbits > 16) ^ (mbits > (r.shift + + (31-12)))): + comb += v.state.eq(State.RADIX_FINISH) + comb += v.badtree.eq(1) +# else +# v.state := RADIX_LOOKUP; + with m.Else(): + comb += v.state.eq(State.RADIX_LOOKUP) +# end if; # -# i_out.tlbld <= itlb_load; -# i_out.tlbie <= tlbie_req; -# i_out.doall <= r.inval_all; -# i_out.addr <= addr; -# i_out.pte <= tlb_data; +# when RADIX_LOOKUP => + with m.Case(State.RADIX_LOOKUP): +# dcreq := '1'; +# v.state := RADIX_READ_WAIT; + comb += dcreq.eq(1) + comb += v.state.eq(State.RADIX_READ_WAIT) + +# when RADIX_READ_WAIT => + with m.Case(State.RADIX_READ_WAIT) +# if d_in.done = '1' then + with m.If(d_in.done == 1): +# v.pde := data; + comb += v.pde.eq(data) +# -- test valid bit +# if data(63) = '1' then + # test valid bit + with m.If(data[63] == 1): +# -- test leaf bit +# if data(62) = '1' then + # test leaf bit + with m.If(data[62] == 1): +# -- check permissions and RC bits +# perm_ok := '0'; + comb += perm_ok.eq(0) +# if r.priv = '1' or data(3) = '0' then + with m.If((r.priv == 1) ^ (data[3] == 0)): +# if r.iside = '0' then +# perm_ok := data(1) or (data(2) and not +# r.store); + with m.If(r.iside == 0): + comb += perm_ok.eq((data[1] ^ data[2]) & + (~r.store)) +# else + with m.Else(): +# -- no IAMR, so no KUEP support for now +# -- deny execute permission if cache inhibited +# perm_ok := data(0) and not data(5); + # no IAMR, so no KUEP support for now + # deny execute permission if cache inhibited + comb += perm_ok.eq(data[0] & (~data[5])) +# end if; +# end if; + +# rc_ok := data(8) and (data(7) or not r.store); + comb += rc_ok.eq(data[8] & (data[7] ^ (~r.store))) +# if perm_ok = '1' and rc_ok = '1' then +# v.state := RADIX_LOAD_TLB; + with m.If(perm_ok == 1 & rc_ok == 1): + comb += v.state.eq(State.RADIX_LOAD_TLB) +# else + with m.Else(): +# v.state := RADIX_FINISH; +# v.perm_err := not perm_ok; +# -- permission error takes precedence over +# -- RC error +# v.rc_error := perm_ok; + comb += vl.state.eq(State.RADIX_FINISH) + comb += v.perm_err.eq(~perm_ok) + # permission error takes precedence over + # RC error + comb += v.rc_error.eq(perm_ok) +# end if; +# else + with m.Else(): +# mbits := unsigned('0' & data(4 downto 0)); + comb += mbits.eq((0 & data[0:5]).as_unsigned()) +# if mbits < 5 or mbits > 16 or mbits > r.shift then +# v.state := RADIX_FINISH; +# v.badtree := '1'; + with m.If((mbits < 5) & (mbits > 16) ^ + (mbits > r.shift)): + comb += v.state.eq(State.RADIX_FINISH) + comb += v.badtree.eq(1) +# else + with m.Else(): +# v.shift := v.shift - mbits; +# v.mask_size := mbits(4 downto 0); +# v.pgbase := data(55 downto 8) & x"00"; +# v.state := RADIX_LOOKUP; + comb += v.shift.eq(v.shif - mbits) + comb += v.mask_size.eq(mbits[0:5]) + comb += v.pgbase.eq(mbits[8:56] & 0x00) + comb += v.state.eq(State.RADIX_LOOKUP) +# end if; +# end if; +# else + with m.Else(): +# -- non-present PTE, generate a DSI +# v.state := RADIX_FINISH; +# v.invalid := '1'; + # non-present PTE, generate a DSI + comb += v.state.eq(State.RADIX_FINISH) + comb += v.invalid.eq(1) +# end if; +# end if; + +# if d_in.err = '1' then + with m.If(d_in.err == 1): +# v.state := RADIX_FINISH; +# v.badtree := '1'; + comb += v.state.eq(State.RADIX_FINISH) + comb += v.badtree.eq(1) +# end if; + +# when RADIX_LOAD_TLB => + with m.Case(State.RADIX_LOAD_TLB): +# tlb_load := '1'; + comb += tlb_load.eq(1) +# if r.iside = '0' then + with m.If(r.iside == 0): +# dcreq := '1'; +# v.state := TLB_WAIT; + comb += dcreq.eq(1) + comb += v.state.eq(State.TLB_WAIT) +# else + with m.Else(): +# itlb_load := '1'; +# v.state := IDLE; + comb += itlb_load.eq(1) + comb += v.state.eq(State.IDLE) +# end if; + +# when RADIX_FINISH => +# v.state := IDLE; + with m.Case(State.RADIX_FINISH): + comb += v.state.eq(State.IDLE) +# end case; # -# end process; -# end; +# if v.state = RADIX_FINISH or (v.state = RADIX_LOAD_TLB +# and r.iside = '1') then + with m.If(v.state == State.RADIX_FINISH ^ (v.state == + State.RADIX_LOAD_TLB & r.iside == 1)) +# v.err := v.invalid or v.badtree or v.segerror or v.perm_err +# or v.rc_error; +# v.done := not v.err; + comb += v.err.eq(v.invalid ^ v.badtree ^ v.segerror ^ v.perm_err ^ + v.rc_error) + comb += v.done.eq(~v.err) +# end if; + +# if r.addr(63) = '1' then +# effpid := x"00000000"; + with m.If(r.addr[63] == 1): + comb += effpid.eq(0x00000000) +# else +# effpid := r.pid; + with m.Else(): + comb += effpid.eq(r.pid) +# end if; +# prtable_addr := x"00" & r.prtbl(55 downto 36) & +# ((r.prtbl(35 downto 12) and not finalmask( +# 23 downto 0)) or (effpid(31 downto 8) and +# finalmask(23 downto 0))) & effpid(7 downto 0) +# & "0000"; + comb += prtable_addr.eq(0x00 & r.prtble[36:56] & ((r.prtble[12:36] & + (~finalmask[0:24])) ^ effpid[8:32] & + finalmask[0:24]) & effpid[0:8] & 0x0000) + +# pgtable_addr := x"00" & r.pgbase(55 downto 19) & +# ((r.pgbase(18 downto 3) and not mask) or +# (addrsh and mask)) & "000"; + comb += pgtable_addr.eq(0x00 & r.pgbase[19:56] & ((r.pgbase[3:19] & + (~mask)) ^ (addrsh & mask)) & 0x000) + +# pte := x"00" & ((r.pde(55 downto 12) and not finalmask) or +# (r.addr(55 downto 12) and finalmask)) & r.pde(11 downto 0); + comb += pte.eq(0x00 & ((r.pde[12:56] & (~finalmask)) ^ (r.addr[12:56] + & finalmask)) & r.pde[0:12]) + +# -- update registers +# rin <= v; + # update registers + rin.eq(v + ) +# -- drive outputs +# if tlbie_req = '1' then + # drive outputs + with m.If(tlbie_req == 1): +# addr := r.addr; +# tlb_data := (others => '0'); + comb += addr.eq(r.addr) + comb += tlb_data.eq('''TODO ()others => '0') ''') +# elsif tlb_load = '1' then + with m.If(tlb_load == 1): +# addr := r.addr(63 downto 12) & x"000"; +# tlb_data := pte; + comb += addr.eq(r.addr[12:64] & 0x000) +# elsif prtbl_rd = '1' then + with m.If(prtbl_rd == 1): +# addr := prtable_addr; +# tlb_data := (others => '0'); + comb += addr.eq(prtable_addr) + comb += tlb_data.eq('''TODO (others => '0')''') +# else + with m.Else(): +# addr := pgtable_addr; +# tlb_data := (others => '0'); + comb += addr.eq(pgtable_addr) + comb += tlb_data.eq('''TODO (others => '0')''') +# end if; + +# l_out.done <= r.done; +# l_out.err <= r.err; +# l_out.invalid <= r.invalid; +# l_out.badtree <= r.badtree; +# l_out.segerr <= r.segerror; +# l_out.perm_error <= r.perm_err; +# l_out.rc_error <= r.rc_error; + comb += l_out.done.eq(r.done) + comb += l_out.err.eq(r.err) + comb += l_out.invalid.eq(r.invalid) + comb += l_out.badtree.eq(r.badtree) + comb += l_out.segerr.eq(r.segerror) + comb += l_out.perm_error.eq(r.perm_err) + comb += l_out.rc_error.eq(r.rc_error) + +# d_out.valid <= dcreq; +# d_out.tlbie <= tlbie_req; +# d_out.doall <= r.inval_all; +# d_out.tlbld <= tlb_load; +# d_out.addr <= addr; +# d_out.pte <= tlb_data; + comb += d_out.valid.eq(dcreq) + comb += d_out.tlbie.eq(tlbie_req) + comb += d_out.doall.eq(r.inval_all) + comb += d_out.tlbld.eeq(tlb_load) + comb += d_out.addr.eq(addr) + comb += d_out.pte.eq(tlb_data) + +# i_out.tlbld <= itlb_load; +# i_out.tlbie <= tlbie_req; +# i_out.doall <= r.inval_all; +# i_out.addr <= addr; +# i_out.pte <= tlb_data; + comb += i_out.tlbld.eq(itlb_load) + comb += i_out.tblie.eq(tlbie_req) + comb += i_out.doall.eq(r.inval_all) + comb += i_out.addr.eq(addr) + comb += i_out.pte.eq(tlb_data) + +# end process; +#end; -- 2.30.2