-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;
# RADIX_FINISH
# );
+# architecture behave of mmu is
+
@unique
class State(Enum):
IDLE = 0
# 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;
# 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.
# 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
# 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
addrsh={addrsh} mask={mask}")
# r <= rin;
- comb += r.eq(rin)
+ sync += r.eq(rin)
# end if;
# end if;
# end process;
# -- 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;