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>
jtag-port
Paul Mackerras 5 years ago
parent 4e6fc6811a
commit f6a0d7f9da

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


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


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



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


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

@ -372,7 +372,7 @@ begin
byte_sel := r.first_bytes; byte_sel := r.first_bytes;
end if; end if;
if m_in.done = '1' then 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 -- retry the request now that the MMU has installed a TLB entry
req := '1'; req := '1';
if r.state = MMU_LOOKUP_1ST then if r.state = MMU_LOOKUP_1ST then
@ -480,10 +480,13 @@ begin


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


stall_out <= stall; stall_out <= stall;



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


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


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


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


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


@ -198,13 +202,12 @@ begin
v.state := TLB_WAIT; v.state := TLB_WAIT;
else else
v.valid := '1'; v.valid := '1';
-- for now, take RPDS = 0 to disable radix translation -- Use RPDS = 0 to disable radix tree walks
if mbits = 0 then if mbits = 0 then
v.state := RADIX_NO_TRANS; v.state := RADIX_ERROR;
elsif mbits < 5 or mbits > 16 or mbits > rts then v.invalid := '1';
v.state := RADIX_BAD_TREE;
else else
v.state := RADIX_LOOKUP; v.state := SEGMENT_CHECK;
end if; end if;
end if; end if;
end if; end if;
@ -218,6 +221,20 @@ begin
v.state := IDLE; v.state := IDLE;
end if; 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 => when RADIX_LOOKUP =>
dcreq := '1'; dcreq := '1';
v.state := RADIX_READ_WAIT; v.state := RADIX_READ_WAIT;
@ -234,7 +251,8 @@ begin
else else
mbits := unsigned('0' & data(4 downto 0)); mbits := unsigned('0' & data(4 downto 0));
if mbits < 5 or mbits > 16 or mbits > r.shift then if mbits < 5 or mbits > 16 or mbits > r.shift then
v.state := RADIX_BAD_TREE; v.state := RADIX_ERROR;
v.badtree := '1';
else else
v.shift := v.shift - mbits; v.shift := v.shift - mbits;
v.mask_size := mbits(4 downto 0); v.mask_size := mbits(4 downto 0);
@ -244,10 +262,12 @@ begin
end if; end if;
else else
-- non-present PTE, generate a DSI -- non-present PTE, generate a DSI
v.state := RADIX_NO_TRANS; v.state := RADIX_ERROR;
v.invalid := '1';
end if; end if;
else else
v.state := RADIX_BAD_TREE; v.state := RADIX_ERROR;
v.badtree := '1';
end if; end if;
end if; end if;


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


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


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


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


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


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

Loading…
Cancel
Save