type state_t is (IDLE, -- ready for instruction
SECOND_REQ, -- send 2nd request of unaligned xfer
ACK_WAIT, -- waiting for ack from dcache
- LD_UPDATE, -- writing rA with computed addr on load
MMU_LOOKUP, -- waiting for MMU to look up translation
TLBIE_WAIT, -- waiting for MMU to finish doing a tlbie
- SPR_CMPLT -- complete a mf/tspr operation
+ COMPLETE -- extra cycle to complete an operation
);
type reg_stage_t is record
busy : std_ulogic;
wait_dcache : std_ulogic;
wait_mmu : std_ulogic;
+ do_update : std_ulogic;
+ extra_cycle : std_ulogic;
end record;
type byte_sel_t is array(0 to 7) of std_ulogic;
if rst = '1' then
r.state <= IDLE;
r.busy <= '0';
- r.wait_dcache <= '0';
- r.wait_mmu <= '0';
+ r.do_update <= '0';
else
r <= rin;
end if;
begin
v := r;
req := '0';
- byte_sel := (others => '0');
- addr := lsu_sum;
- maddr := l_in.addr2; -- address from RB for tlbie
v.mfspr := '0';
mmu_mtspr := '0';
itlb_fault := '0';
mmureq := '0';
write_enable := '0';
- do_update := '0';
+
+ do_update := r.do_update;
+ v.do_update := '0';
-- load data formatting
byte_offset := unsigned(r.addr(2 downto 0));
-- Busy calculation.
-- We need to minimize the delay from clock to busy valid because it
-- gates the start of execution of the next instruction.
- busy := r.busy or (r.wait_dcache and not d_in.valid) or (r.wait_mmu and not m_in.done);
+ busy := r.busy and not ((r.wait_dcache and d_in.valid) or (r.wait_mmu and m_in.done));
+ v.busy := busy;
done := '0';
if r.state /= IDLE and busy = '0' then
end if;
exception := '0';
+ if r.dwords_done = '1' or r.state = SECOND_REQ then
+ maddr := next_addr;
+ byte_sel := r.second_bytes;
+ else
+ maddr := r.addr;
+ byte_sel := r.first_bytes;
+ end if;
+ addr := maddr;
+
case r.state is
when IDLE =>
when SECOND_REQ =>
- addr := next_addr;
- byte_sel := r.second_bytes;
req := '1';
v.state := ACK_WAIT;
v.last_dword := '0';
if d_in.error = '1' then
-- dcache will discard the second request if it
-- gets an error on the 1st of two requests
- if r.dwords_done = '1' then
- maddr := next_addr;
- else
- maddr := r.addr;
- end if;
if d_in.cache_paradox = '1' then
-- signal an interrupt straight away
exception := '1';
end if;
else
write_enable := r.load;
- if r.load = '1' and r.update = '1' then
+ if r.extra_cycle = '1' then
-- loads with rA update need an extra cycle
- v.state := LD_UPDATE;
+ v.state := COMPLETE;
+ v.do_update := r.update;
else
-- stores write back rA update in this cycle
do_update := r.update;
end if;
+ v.busy := '0';
end if;
end if;
+ -- r.wait_dcache gets set one cycle after we come into ACK_WAIT state,
+ -- which is OK because the dcache always takes at least two cycles.
+ v.wait_dcache := r.last_dword and not r.extra_cycle;
when MMU_LOOKUP =>
- if r.dwords_done = '1' then
- addr := next_addr;
- byte_sel := r.second_bytes;
- else
- addr := r.addr;
- byte_sel := r.first_bytes;
- end if;
if m_in.done = '1' then
if r.instr_fault = '0' then
-- retry the request now that the MMU has installed a TLB entry
when TLBIE_WAIT =>
- when LD_UPDATE =>
- do_update := '1';
-
- when SPR_CMPLT =>
+ when COMPLETE =>
end case;
if done = '1' or exception = '1' then
v.state := IDLE;
+ v.busy := '0';
end if;
-- Note that l_in.valid is gated with busy inside execute1
v.nc := l_in.ci;
v.virt_mode := l_in.virt_mode;
v.priv_mode := l_in.priv_mode;
+ v.wait_dcache := '0';
+ v.wait_mmu := '0';
+ v.do_update := '0';
+ v.extra_cycle := '0';
+
+ addr := lsu_sum;
+ maddr := l_in.addr2; -- address from RB for tlbie
-- XXX Temporary hack. Mark the op as non-cachable if the address
-- is the form 0xc------- for a real-mode access.
when OP_LOAD =>
req := '1';
v.load := '1';
+ -- Allow an extra cycle for RA update on loads
+ v.extra_cycle := l_in.update;
when OP_DCBZ =>
req := '1';
v.dcbz := '1';
mmureq := '1';
v.tlbie := '1';
v.state := TLBIE_WAIT;
+ v.wait_mmu := '1';
when OP_MFSPR =>
v.mfspr := '1';
-- partial decode on SPR number should be adequate given
-- reading one of the SPRs in the MMU
v.sprval := m_in.sprval;
end if;
- v.state := SPR_CMPLT;
+ v.state := COMPLETE;
when OP_MTSPR =>
if sprn(9) = '0' and sprn(5) = '0' then
if sprn(0) = '0' then
else
v.dar := l_in.data;
end if;
- v.state := SPR_CMPLT;
+ v.state := COMPLETE;
else
-- writing one of the SPRs in the MMU
mmu_mtspr := '1';
v.state := TLBIE_WAIT;
+ v.wait_mmu := '1';
end if;
when OP_FETCH_FAILED =>
-- send it to the MMU to do the radix walk
v.instr_fault := '1';
mmureq := '1';
v.state := MMU_LOOKUP;
+ v.wait_mmu := '1';
when others =>
assert false report "unknown op sent to loadstore1";
end case;
v.state := SECOND_REQ;
end if;
end if;
- end if;
- -- Work out whether we'll be busy next cycle
- v.busy := '0';
- v.wait_dcache := '0';
- v.wait_mmu := '0';
- case v.state is
- when SECOND_REQ =>
- v.busy := '1';
- when ACK_WAIT =>
- if v.last_dword = '0' or (v.load = '1' and v.update = '1') then
- v.busy := '1';
- else
- v.wait_dcache := '1';
- end if;
- when MMU_LOOKUP =>
- if v.instr_fault = '0' then
- v.busy := '1';
- else
- v.wait_mmu := '1';
- end if;
- when TLBIE_WAIT =>
- v.wait_mmu := '1';
- when others =>
- -- not busy next cycle
- end case;
+ v.busy := req or mmureq or mmu_mtspr;
+ end if;
-- Update outputs to dcache
d_out.valid <= req;