MMU: Implement data segment interrupts

A data segment interrupt (DSegI) occurs when an address to be
translated by the MMU is outside the range of the radix tree
or the top two bits of the address (the quadrant) are 01 or 10.
This is detected in a new state of the MMU state machine, and
is sent back to loadstore1 as an error, which sends it on to
execute1 to generate an interrupt to the 0x380 vector.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
pull/169/head
Paul Mackerras 4 years ago
parent 4e6fc6811a
commit f6a0d7f9da

@ -242,6 +242,7 @@ package common is

type Loadstore1ToExecute1Type is record
exception : std_ulogic;
segment_fault : std_ulogic;
end record;

type Loadstore1ToDcacheType is record
@ -280,6 +281,7 @@ package common is
done : std_ulogic;
invalid : std_ulogic;
badtree : std_ulogic;
segerr : std_ulogic;
sprval : std_ulogic_vector(63 downto 0);
end record;


@ -974,7 +974,11 @@ begin

-- generate DSI for load/store exceptions
if l_in.exception = '1' then
ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#300#, 64));
if l_in.segment_fault = '0' then
ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#300#, 64));
else
ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#380#, 64));
end if;
ctrl_tmp.srr1 <= msr_copy(ctrl.msr);
v.e.exc_write_enable := '1';
v.e.exc_write_reg := fast_spr_num(SPR_SRR0);

@ -372,7 +372,7 @@ begin
byte_sel := r.first_bytes;
end if;
if m_in.done = '1' then
if m_in.invalid = '0' and m_in.badtree = '0' then
if m_in.invalid = '0' and m_in.badtree = '0' and m_in.segerr = '0' then
-- retry the request now that the MMU has installed a TLB entry
req := '1';
if r.state = MMU_LOOKUP_1ST then
@ -480,9 +480,12 @@ begin

-- update exception info back to execute1
e_out.exception <= exception;
e_out.segment_fault <= m_in.segerr;
if exception = '1' then
v.dar := addr;
v.dsisr := dsisr;
if m_in.segerr = '0' then
v.dsisr := dsisr;
end if;
end if;

stall_out <= stall;

@ -26,11 +26,11 @@ architecture behave of mmu is

type state_t is (IDLE,
TLB_WAIT,
SEGMENT_CHECK,
RADIX_LOOKUP,
RADIX_READ_WAIT,
RADIX_LOAD_TLB,
RADIX_NO_TRANS,
RADIX_BAD_TREE
RADIX_ERROR
);

type reg_stage_t is record
@ -44,6 +44,9 @@ architecture behave of mmu is
mask_size : unsigned(4 downto 0);
pgbase : std_ulogic_vector(55 downto 0);
pde : std_ulogic_vector(63 downto 0);
invalid : std_ulogic;
badtree : std_ulogic;
segerror : std_ulogic;
end record;

signal r, rin : reg_stage_t;
@ -155,8 +158,6 @@ begin
variable v : reg_stage_t;
variable dcreq : std_ulogic;
variable done : std_ulogic;
variable invalid : std_ulogic;
variable badtree : std_ulogic;
variable tlb_load : std_ulogic;
variable tlbie_req : std_ulogic;
variable rts : unsigned(5 downto 0);
@ -164,13 +165,15 @@ begin
variable pgtable_addr : std_ulogic_vector(63 downto 0);
variable pte : std_ulogic_vector(63 downto 0);
variable data : std_ulogic_vector(63 downto 0);
variable nonzero : std_ulogic;
begin
v := r;
v.valid := '0';
dcreq := '0';
done := '0';
invalid := '0';
badtree := '0';
v.invalid := '0';
v.badtree := '0';
v.segerror := '0';
tlb_load := '0';
tlbie_req := '0';

@ -183,10 +186,11 @@ begin
case r.state is
when IDLE =>
-- rts == radix tree size, # address bits being translated
rts := unsigned('0' & r.pgtbl0(62 downto 61) & r.pgtbl0(7 downto 5)) + (31 - 12);
rts := unsigned('0' & r.pgtbl0(62 downto 61) & r.pgtbl0(7 downto 5));
-- mbits == # address bits to index top level of tree
mbits := unsigned('0' & r.pgtbl0(4 downto 0));
v.shift := rts - mbits;
-- 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 := r.pgtbl0(55 downto 8) & x"00";

@ -198,13 +202,12 @@ begin
v.state := TLB_WAIT;
else
v.valid := '1';
-- for now, take RPDS = 0 to disable radix translation
-- Use RPDS = 0 to disable radix tree walks
if mbits = 0 then
v.state := RADIX_NO_TRANS;
elsif mbits < 5 or mbits > 16 or mbits > rts then
v.state := RADIX_BAD_TREE;
v.state := RADIX_ERROR;
v.invalid := '1';
else
v.state := RADIX_LOOKUP;
v.state := SEGMENT_CHECK;
end if;
end if;
end if;
@ -218,6 +221,20 @@ begin
v.state := IDLE;
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.segerror := '1';
elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then
v.state := RADIX_ERROR;
v.badtree := '1';
else
v.state := RADIX_LOOKUP;
end if;

when RADIX_LOOKUP =>
dcreq := '1';
v.state := RADIX_READ_WAIT;
@ -234,7 +251,8 @@ begin
else
mbits := unsigned('0' & data(4 downto 0));
if mbits < 5 or mbits > 16 or mbits > r.shift then
v.state := RADIX_BAD_TREE;
v.state := RADIX_ERROR;
v.badtree := '1';
else
v.shift := v.shift - mbits;
v.mask_size := mbits(4 downto 0);
@ -244,10 +262,12 @@ begin
end if;
else
-- non-present PTE, generate a DSI
v.state := RADIX_NO_TRANS;
v.state := RADIX_ERROR;
v.invalid := '1';
end if;
else
v.state := RADIX_BAD_TREE;
v.state := RADIX_ERROR;
v.badtree := '1';
end if;
end if;

@ -256,15 +276,10 @@ begin
dcreq := '1';
v.state := TLB_WAIT;

when RADIX_NO_TRANS =>
when RADIX_ERROR =>
done := '1';
invalid := '1';
v.state := IDLE;

when RADIX_BAD_TREE =>
done := '1';
badtree := '1';
v.state := IDLE;
end case;

pgtable_addr := x"00" & r.pgbase(55 downto 19) &
@ -279,8 +294,9 @@ begin

-- drive outputs
l_out.done <= done;
l_out.invalid <= invalid;
l_out.badtree <= badtree;
l_out.invalid <= r.invalid;
l_out.badtree <= r.badtree;
l_out.segerr <= r.segerror;

d_out.valid <= dcreq;
d_out.tlbie <= tlbie_req;

Loading…
Cancel
Save