architecture behave of mmu is
type state_t is (IDLE,
+ DO_TLBIE,
TLB_WAIT,
PROC_TBL_READ,
PROC_TBL_WAIT,
RADIX_LOOKUP,
RADIX_READ_WAIT,
RADIX_LOAD_TLB,
- RADIX_ERROR
+ RADIX_FINISH
);
type reg_stage_t is record
store : std_ulogic;
priv : std_ulogic;
addr : std_ulogic_vector(63 downto 0);
+ inval_all : std_ulogic;
-- config SPRs
prtbl : std_ulogic_vector(63 downto 0);
pid : std_ulogic_vector(31 downto 0);
-- internal state
state : state_t;
+ done : std_ulogic;
+ err : std_ulogic;
pgtbl0 : std_ulogic_vector(63 downto 0);
pt0_valid : std_ulogic;
pgtbl3 : std_ulogic_vector(63 downto 0);
r.pt0_valid <= '0';
r.pt3_valid <= '0';
r.prtbl <= (others => '0');
+ r.pid <= (others => '0');
else
if rin.valid = '1' then
report "MMU got tlb miss for " & to_hstring(rin.addr);
end if;
if l_out.done = '1' then
- report "MMU completing op with invalid=" & std_ulogic'image(l_out.invalid) &
+ report "MMU completing op without error";
+ end if;
+ if l_out.err = '1' then
+ report "MMU completing op with err invalid=" & std_ulogic'image(l_out.invalid) &
" badtree=" & std_ulogic'image(l_out.badtree);
end if;
if rin.state = RADIX_LOOKUP then
mmu_1: process(all)
variable v : reg_stage_t;
variable dcreq : std_ulogic;
- variable done : std_ulogic;
variable tlb_load : std_ulogic;
variable itlb_load : std_ulogic;
variable tlbie_req : std_ulogic;
- variable inval_all : std_ulogic;
variable prtbl_rd : std_ulogic;
variable pt_valid : std_ulogic;
variable effpid : std_ulogic_vector(31 downto 0);
v := r;
v.valid := '0';
dcreq := '0';
- done := '0';
+ v.done := '0';
+ v.err := '0';
v.invalid := '0';
v.badtree := '0';
v.segerror := '0';
tlb_load := '0';
itlb_load := '0';
tlbie_req := '0';
- inval_all := '0';
+ v.inval_all := '0';
prtbl_rd := '0';
-- Radix tree data structures in memory are big-endian,
v.store := not (l_in.load or l_in.iside);
v.priv := l_in.priv;
if l_in.tlbie = '1' then
- dcreq := '1';
- tlbie_req := '1';
-- Invalidate all iTLB/dTLB entries for tlbie with
-- RB[IS] != 0 or RB[AP] != 0, or for slbia
- 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);
+ 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 := TLB_WAIT;
+ v.state := DO_TLBIE;
else
v.valid := '1';
if pt_valid = '0' then
v.state := PROC_TBL_READ;
elsif mbits = 0 then
-- Use RPDS = 0 to disable radix tree walks
- v.state := RADIX_ERROR;
+ v.state := RADIX_FINISH;
v.invalid := '1';
else
v.state := SEGMENT_CHECK;
v.pt3_valid := '0';
end if;
v.pt0_valid := '0';
- dcreq := '1';
- tlbie_req := '1';
- inval_all := '1';
- v.state := TLB_WAIT;
+ 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
- done := '1';
- v.state := IDLE;
+ v.state := RADIX_FINISH;
end if;
when PROC_TBL_READ =>
when PROC_TBL_WAIT =>
if d_in.done = '1' then
- if d_in.err = '0' 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_ERROR;
- v.invalid := '1';
- else
- v.state := SEGMENT_CHECK;
- end if;
+ 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 := RADIX_ERROR;
- v.badtree := '1';
+ 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_ERROR;
+ v.state := RADIX_FINISH;
v.segerror := '1';
elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then
- v.state := RADIX_ERROR;
+ v.state := RADIX_FINISH;
v.badtree := '1';
else
v.state := RADIX_LOOKUP;
when RADIX_READ_WAIT =>
if d_in.done = '1' then
- if d_in.err = '0' 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;
+ 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
- v.state := RADIX_ERROR;
- v.perm_err := not perm_ok;
- -- permission error takes precedence over RC error
- v.rc_error := perm_ok;
+ -- 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
- mbits := unsigned('0' & data(4 downto 0));
- if mbits < 5 or mbits > 16 or mbits > r.shift then
- v.state := RADIX_ERROR;
- 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;
+ 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
- -- non-present PTE, generate a DSI
- v.state := RADIX_ERROR;
- v.invalid := '1';
+ 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
- v.state := RADIX_ERROR;
- v.badtree := '1';
+ -- 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';
v.state := TLB_WAIT;
else
itlb_load := '1';
- done := '1';
v.state := IDLE;
end if;
- when RADIX_ERROR =>
- done := '1';
+ 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
-- drive outputs
if tlbie_req = '1' then
- addr := l_in.addr;
- tlb_data := l_in.rs;
+ addr := r.addr;
+ tlb_data := (others => '0');
elsif tlb_load = '1' then
addr := r.addr(63 downto 12) & x"000";
tlb_data := pte;
tlb_data := (others => '0');
end if;
- l_out.done <= done;
+ 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;
d_out.valid <= dcreq;
d_out.tlbie <= tlbie_req;
- d_out.doall <= inval_all;
+ d_out.doall <= r.inval_all;
d_out.tlbld <= tlb_load;
d_out.addr <= addr;
d_out.pte <= tlb_data;
i_out.tlbld <= itlb_load;
i_out.tlbie <= tlbie_req;
- i_out.doall <= inval_all;
+ i_out.doall <= r.inval_all;
i_out.addr <= addr;
i_out.pte <= tlb_data;