mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-01-13 15:18:09 +00:00
At present the busy/stall signal going to decode1 depends on whether control thinks it can issue the current instruction, and that depends on completion and bypass signals coming from execute1 and writeback. To improve the timing of stall_out, this rearranges decode2 so that stall_out is asserted when we have a valid instruction that couldn't be issued in the previous cycle. This means that decode1 could give us a new instruction when we haven't issued the previous instruction. This in turn means that we can only use d_in in the first cycle of processing an instruction. After the first cycle, we get register addresses etc. from dc2 rather than d_in. Then, to avoid the need to read register operands from register_file in each cycle until the instruction issues, we bring the bypass path for data being written to the register file into decode2 explicitly rather than having it in register_file. A new process called decode2_addrs does the process of calling decode_input_reg_* and decode_output_reg and sets up the register file addresses. This was split out (and decode_input_reg_* reworked) to try to reduce the number of passes through the decode2_1 process that need to be done in simulation. Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
228 lines
7.7 KiB
VHDL
228 lines
7.7 KiB
VHDL
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library work;
|
|
use work.common.all;
|
|
use work.crhelpers.all;
|
|
|
|
entity writeback is
|
|
port (
|
|
clk : in std_ulogic;
|
|
rst : in std_ulogic;
|
|
|
|
e_in : in Execute1ToWritebackType;
|
|
l_in : in Loadstore1ToWritebackType;
|
|
fp_in : in FPUToWritebackType;
|
|
|
|
w_out : out WritebackToRegisterFileType;
|
|
c_out : out WritebackToCrFileType;
|
|
f_out : out WritebackToFetch1Type;
|
|
|
|
wb_bypass : out bypass_data_t;
|
|
|
|
-- PMU event bus
|
|
events : out WritebackEventType;
|
|
|
|
flush_out : out std_ulogic;
|
|
interrupt_out: out std_ulogic;
|
|
complete_out : out instr_tag_t
|
|
);
|
|
end entity writeback;
|
|
|
|
architecture behaviour of writeback is
|
|
type irq_state_t is (WRITE_SRR0, WRITE_SRR1);
|
|
|
|
type reg_type is record
|
|
state : irq_state_t;
|
|
srr1 : std_ulogic_vector(63 downto 0);
|
|
end record;
|
|
|
|
signal r, rin : reg_type;
|
|
|
|
begin
|
|
writeback_0: process(clk)
|
|
variable x : std_ulogic_vector(0 downto 0);
|
|
variable y : std_ulogic_vector(0 downto 0);
|
|
variable w : std_ulogic_vector(0 downto 0);
|
|
begin
|
|
if rising_edge(clk) then
|
|
if rst = '1' then
|
|
r.state <= WRITE_SRR0;
|
|
r.srr1 <= (others => '0');
|
|
else
|
|
r <= rin;
|
|
end if;
|
|
|
|
-- Do consistency checks only on the clock edge
|
|
x(0) := e_in.valid;
|
|
y(0) := l_in.valid;
|
|
w(0) := fp_in.valid;
|
|
assert (to_integer(unsigned(x)) + to_integer(unsigned(y)) +
|
|
to_integer(unsigned(w))) <= 1 severity failure;
|
|
|
|
x(0) := e_in.write_enable;
|
|
y(0) := l_in.write_enable;
|
|
w(0) := fp_in.write_enable;
|
|
assert (to_integer(unsigned(x)) + to_integer(unsigned(y)) +
|
|
to_integer(unsigned(w))) <= 1 severity failure;
|
|
|
|
w(0) := e_in.write_cr_enable;
|
|
x(0) := l_in.rc;
|
|
y(0) := fp_in.write_cr_enable;
|
|
assert (to_integer(unsigned(w)) + to_integer(unsigned(x)) +
|
|
to_integer(unsigned(y))) <= 1 severity failure;
|
|
|
|
assert not (e_in.valid = '1' and e_in.instr_tag.valid = '0') severity failure;
|
|
assert not (l_in.valid = '1' and l_in.instr_tag.valid = '0') severity failure;
|
|
assert not (fp_in.valid = '1' and fp_in.instr_tag.valid = '0') severity failure;
|
|
end if;
|
|
end process;
|
|
|
|
writeback_1: process(all)
|
|
variable v : reg_type;
|
|
variable f : WritebackToFetch1Type;
|
|
variable scf : std_ulogic_vector(3 downto 0);
|
|
variable vec : integer range 0 to 16#fff#;
|
|
variable srr1 : std_ulogic_vector(15 downto 0);
|
|
variable intr : std_ulogic;
|
|
begin
|
|
w_out <= WritebackToRegisterFileInit;
|
|
c_out <= WritebackToCrFileInit;
|
|
f := WritebackToFetch1Init;
|
|
interrupt_out <= '0';
|
|
vec := 0;
|
|
v := r;
|
|
|
|
complete_out <= instr_tag_init;
|
|
if e_in.valid = '1' then
|
|
complete_out <= e_in.instr_tag;
|
|
elsif l_in.valid = '1' then
|
|
complete_out <= l_in.instr_tag;
|
|
elsif fp_in.valid = '1' then
|
|
complete_out <= fp_in.instr_tag;
|
|
end if;
|
|
events.instr_complete <= complete_out.valid;
|
|
events.fp_complete <= fp_in.valid;
|
|
|
|
intr := e_in.interrupt or l_in.interrupt or fp_in.interrupt;
|
|
|
|
if r.state = WRITE_SRR1 then
|
|
w_out.write_reg <= fast_spr_num(SPR_SRR1);
|
|
w_out.write_data <= r.srr1;
|
|
w_out.write_enable <= '1';
|
|
interrupt_out <= '1';
|
|
v.state := WRITE_SRR0;
|
|
|
|
elsif intr = '1' then
|
|
w_out.write_reg <= fast_spr_num(SPR_SRR0);
|
|
w_out.write_enable <= '1';
|
|
v.state := WRITE_SRR1;
|
|
srr1 := (others => '0');
|
|
if e_in.interrupt = '1' then
|
|
vec := e_in.intr_vec;
|
|
w_out.write_data <= e_in.last_nia;
|
|
srr1 := e_in.srr1;
|
|
elsif l_in.interrupt = '1' then
|
|
vec := l_in.intr_vec;
|
|
w_out.write_data <= l_in.srr0;
|
|
srr1 := l_in.srr1;
|
|
elsif fp_in.interrupt = '1' then
|
|
vec := fp_in.intr_vec;
|
|
w_out.write_data <= fp_in.srr0;
|
|
srr1 := fp_in.srr1;
|
|
end if;
|
|
v.srr1(63 downto 31) := e_in.msr(63 downto 31);
|
|
v.srr1(30 downto 27) := srr1(14 downto 11);
|
|
v.srr1(26 downto 22) := e_in.msr(26 downto 22);
|
|
v.srr1(21 downto 16) := srr1(5 downto 0);
|
|
v.srr1(15 downto 0) := e_in.msr(15 downto 0);
|
|
|
|
else
|
|
if e_in.write_enable = '1' then
|
|
w_out.write_reg <= e_in.write_reg;
|
|
w_out.write_data <= e_in.write_data;
|
|
w_out.write_enable <= '1';
|
|
end if;
|
|
|
|
if e_in.write_cr_enable = '1' then
|
|
c_out.write_cr_enable <= '1';
|
|
c_out.write_cr_mask <= e_in.write_cr_mask;
|
|
c_out.write_cr_data <= e_in.write_cr_data;
|
|
end if;
|
|
|
|
if e_in.write_xerc_enable = '1' then
|
|
c_out.write_xerc_enable <= '1';
|
|
c_out.write_xerc_data <= e_in.xerc;
|
|
end if;
|
|
|
|
if fp_in.write_enable = '1' then
|
|
w_out.write_reg <= fp_in.write_reg;
|
|
w_out.write_data <= fp_in.write_data;
|
|
w_out.write_enable <= '1';
|
|
end if;
|
|
|
|
if fp_in.write_cr_enable = '1' then
|
|
c_out.write_cr_enable <= '1';
|
|
c_out.write_cr_mask <= fp_in.write_cr_mask;
|
|
c_out.write_cr_data <= fp_in.write_cr_data;
|
|
end if;
|
|
|
|
if l_in.write_enable = '1' then
|
|
w_out.write_reg <= l_in.write_reg;
|
|
w_out.write_data <= l_in.write_data;
|
|
w_out.write_enable <= '1';
|
|
end if;
|
|
|
|
if l_in.rc = '1' then
|
|
-- st*cx. instructions
|
|
scf(3) := '0';
|
|
scf(2) := '0';
|
|
scf(1) := l_in.store_done;
|
|
scf(0) := l_in.xerc.so;
|
|
c_out.write_cr_enable <= '1';
|
|
c_out.write_cr_mask <= num_to_fxm(0);
|
|
c_out.write_cr_data(31 downto 28) <= scf;
|
|
end if;
|
|
|
|
end if;
|
|
|
|
-- Outputs to fetch1
|
|
f.redirect := e_in.redirect;
|
|
f.br_nia := e_in.last_nia;
|
|
f.br_last := e_in.br_last;
|
|
f.br_taken := e_in.br_taken;
|
|
if intr = '1' then
|
|
f.redirect := '1';
|
|
f.br_last := '0';
|
|
f.redirect_nia := std_ulogic_vector(to_unsigned(vec, 64));
|
|
f.virt_mode := '0';
|
|
f.priv_mode := '1';
|
|
-- XXX need an interrupt LE bit here, e.g. from LPCR
|
|
f.big_endian := '0';
|
|
f.mode_32bit := '0';
|
|
else
|
|
if e_in.abs_br = '1' then
|
|
f.redirect_nia := e_in.br_offset;
|
|
else
|
|
f.redirect_nia := std_ulogic_vector(unsigned(e_in.last_nia) + unsigned(e_in.br_offset));
|
|
end if;
|
|
-- send MSR[IR], ~MSR[PR], ~MSR[LE] and ~MSR[SF] up to fetch1
|
|
f.virt_mode := e_in.redir_mode(3);
|
|
f.priv_mode := e_in.redir_mode(2);
|
|
f.big_endian := e_in.redir_mode(1);
|
|
f.mode_32bit := e_in.redir_mode(0);
|
|
end if;
|
|
|
|
f_out <= f;
|
|
flush_out <= f_out.redirect;
|
|
|
|
-- Register write data bypass to decode2
|
|
wb_bypass.tag.tag <= complete_out.tag;
|
|
wb_bypass.tag.valid <= complete_out.valid and w_out.write_enable;
|
|
wb_bypass.data <= w_out.write_data;
|
|
|
|
rin <= v;
|
|
end process;
|
|
end;
|