forked from cores/microwatt
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
11 KiB
VHDL
329 lines
11 KiB
VHDL
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
|
|
library work;
|
|
use work.common.all;
|
|
|
|
entity control is
|
|
generic (
|
|
EX1_BYPASS : boolean := true;
|
|
PIPELINE_DEPTH : natural := 3
|
|
);
|
|
port (
|
|
clk : in std_ulogic;
|
|
rst : in std_ulogic;
|
|
|
|
complete_in : in instr_tag_t;
|
|
valid_in : in std_ulogic;
|
|
repeated : in std_ulogic;
|
|
flush_in : in std_ulogic;
|
|
busy_in : in std_ulogic;
|
|
deferred : in std_ulogic;
|
|
sgl_pipe_in : in std_ulogic;
|
|
stop_mark_in : in std_ulogic;
|
|
|
|
gpr_write_valid_in : in std_ulogic;
|
|
gpr_write_in : in gspr_index_t;
|
|
|
|
gpr_a_read_valid_in : in std_ulogic;
|
|
gpr_a_read_in : in gspr_index_t;
|
|
|
|
gpr_b_read_valid_in : in std_ulogic;
|
|
gpr_b_read_in : in gspr_index_t;
|
|
|
|
gpr_c_read_valid_in : in std_ulogic;
|
|
gpr_c_read_in : in gspr_index_t;
|
|
|
|
execute_next_tag : in instr_tag_t;
|
|
execute_next_cr_tag : in instr_tag_t;
|
|
|
|
cr_read_in : in std_ulogic;
|
|
cr_write_in : in std_ulogic;
|
|
|
|
valid_out : out std_ulogic;
|
|
stall_out : out std_ulogic;
|
|
stopped_out : out std_ulogic;
|
|
|
|
gpr_bypass_a : out std_ulogic;
|
|
gpr_bypass_b : out std_ulogic;
|
|
gpr_bypass_c : out std_ulogic;
|
|
cr_bypass : out std_ulogic;
|
|
|
|
instr_tag_out : out instr_tag_t
|
|
);
|
|
end entity control;
|
|
|
|
architecture rtl of control is
|
|
type state_type is (IDLE, WAIT_FOR_PREV_TO_COMPLETE, WAIT_FOR_CURR_TO_COMPLETE);
|
|
|
|
type reg_internal_type is record
|
|
state : state_type;
|
|
outstanding : integer range -1 to PIPELINE_DEPTH+2;
|
|
end record;
|
|
constant reg_internal_init : reg_internal_type := (state => IDLE, outstanding => 0);
|
|
|
|
signal r_int, rin_int : reg_internal_type := reg_internal_init;
|
|
|
|
signal gpr_write_valid : std_ulogic;
|
|
signal cr_write_valid : std_ulogic;
|
|
|
|
type tag_register is record
|
|
wr_gpr : std_ulogic;
|
|
reg : gspr_index_t;
|
|
recent : std_ulogic;
|
|
wr_cr : std_ulogic;
|
|
end record;
|
|
|
|
type tag_regs_array is array(tag_number_t) of tag_register;
|
|
signal tag_regs : tag_regs_array;
|
|
|
|
signal instr_tag : instr_tag_t;
|
|
|
|
signal gpr_tag_stall : std_ulogic;
|
|
signal cr_tag_stall : std_ulogic;
|
|
|
|
signal curr_tag : tag_number_t;
|
|
signal next_tag : tag_number_t;
|
|
|
|
signal curr_cr_tag : tag_number_t;
|
|
|
|
begin
|
|
control0: process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
assert rin_int.outstanding >= 0 and rin_int.outstanding <= (PIPELINE_DEPTH+1)
|
|
report "Outstanding bad " & integer'image(rin_int.outstanding) severity failure;
|
|
r_int <= rin_int;
|
|
for i in tag_number_t loop
|
|
if rst = '1' or flush_in = '1' then
|
|
tag_regs(i).wr_gpr <= '0';
|
|
tag_regs(i).wr_cr <= '0';
|
|
else
|
|
if complete_in.valid = '1' and i = complete_in.tag then
|
|
tag_regs(i).wr_gpr <= '0';
|
|
tag_regs(i).wr_cr <= '0';
|
|
report "tag " & integer'image(i) & " not valid";
|
|
end if;
|
|
if gpr_write_valid = '1' and tag_regs(i).reg = gpr_write_in then
|
|
tag_regs(i).recent <= '0';
|
|
if tag_regs(i).recent = '1' and tag_regs(i).wr_gpr = '1' then
|
|
report "tag " & integer'image(i) & " not recent";
|
|
end if;
|
|
end if;
|
|
if instr_tag.valid = '1' and i = instr_tag.tag then
|
|
tag_regs(i).wr_gpr <= gpr_write_valid;
|
|
tag_regs(i).reg <= gpr_write_in;
|
|
tag_regs(i).recent <= gpr_write_valid;
|
|
tag_regs(i).wr_cr <= cr_write_valid;
|
|
if gpr_write_valid = '1' then
|
|
report "tag " & integer'image(i) & " valid for gpr " & to_hstring(gpr_write_in);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
if rst = '1' then
|
|
curr_tag <= 0;
|
|
curr_cr_tag <= 0;
|
|
else
|
|
curr_tag <= next_tag;
|
|
if cr_write_valid = '1' then
|
|
curr_cr_tag <= instr_tag.tag;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
control_hazards : process(all)
|
|
variable gpr_stall : std_ulogic;
|
|
variable tag_a : instr_tag_t;
|
|
variable tag_b : instr_tag_t;
|
|
variable tag_c : instr_tag_t;
|
|
variable tag_s : instr_tag_t;
|
|
variable tag_t : instr_tag_t;
|
|
variable incr_tag : tag_number_t;
|
|
variable byp_a : std_ulogic;
|
|
variable byp_b : std_ulogic;
|
|
variable byp_c : std_ulogic;
|
|
variable tag_cr : instr_tag_t;
|
|
variable byp_cr : std_ulogic;
|
|
begin
|
|
tag_a := instr_tag_init;
|
|
for i in tag_number_t loop
|
|
if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_a_read_in then
|
|
tag_a.valid := gpr_a_read_valid_in;
|
|
tag_a.tag := i;
|
|
end if;
|
|
end loop;
|
|
if tag_match(tag_a, complete_in) then
|
|
tag_a.valid := '0';
|
|
end if;
|
|
tag_b := instr_tag_init;
|
|
for i in tag_number_t loop
|
|
if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_b_read_in then
|
|
tag_b.valid := gpr_b_read_valid_in;
|
|
tag_b.tag := i;
|
|
end if;
|
|
end loop;
|
|
if tag_match(tag_b, complete_in) then
|
|
tag_b.valid := '0';
|
|
end if;
|
|
tag_c := instr_tag_init;
|
|
for i in tag_number_t loop
|
|
if tag_regs(i).wr_gpr = '1' and tag_regs(i).recent = '1' and tag_regs(i).reg = gpr_c_read_in then
|
|
tag_c.valid := gpr_c_read_valid_in;
|
|
tag_c.tag := i;
|
|
end if;
|
|
end loop;
|
|
if tag_match(tag_c, complete_in) then
|
|
tag_c.valid := '0';
|
|
end if;
|
|
|
|
byp_a := '0';
|
|
if EX1_BYPASS and tag_match(execute_next_tag, tag_a) then
|
|
byp_a := '1';
|
|
end if;
|
|
byp_b := '0';
|
|
if EX1_BYPASS and tag_match(execute_next_tag, tag_b) then
|
|
byp_b := '1';
|
|
end if;
|
|
byp_c := '0';
|
|
if EX1_BYPASS and tag_match(execute_next_tag, tag_c) then
|
|
byp_c := '1';
|
|
end if;
|
|
|
|
gpr_bypass_a <= byp_a;
|
|
gpr_bypass_b <= byp_b;
|
|
gpr_bypass_c <= byp_c;
|
|
|
|
gpr_tag_stall <= (tag_a.valid and not byp_a) or
|
|
(tag_b.valid and not byp_b) or
|
|
(tag_c.valid and not byp_c);
|
|
|
|
incr_tag := curr_tag;
|
|
instr_tag.tag <= curr_tag;
|
|
instr_tag.valid <= valid_out and not deferred;
|
|
if instr_tag.valid = '1' then
|
|
incr_tag := (curr_tag + 1) mod TAG_COUNT;
|
|
end if;
|
|
next_tag <= incr_tag;
|
|
instr_tag_out <= instr_tag;
|
|
|
|
-- CR hazards
|
|
tag_cr.tag := curr_cr_tag;
|
|
tag_cr.valid := cr_read_in and tag_regs(curr_cr_tag).wr_cr;
|
|
if tag_match(tag_cr, complete_in) then
|
|
tag_cr.valid := '0';
|
|
end if;
|
|
byp_cr := '0';
|
|
if EX1_BYPASS and tag_match(execute_next_cr_tag, tag_cr) then
|
|
byp_cr := '1';
|
|
end if;
|
|
|
|
cr_bypass <= byp_cr;
|
|
cr_tag_stall <= tag_cr.valid and not byp_cr;
|
|
end process;
|
|
|
|
control1 : process(all)
|
|
variable v_int : reg_internal_type;
|
|
variable valid_tmp : std_ulogic;
|
|
variable stall_tmp : std_ulogic;
|
|
begin
|
|
v_int := r_int;
|
|
|
|
-- asynchronous
|
|
valid_tmp := valid_in and not flush_in;
|
|
stall_tmp := '0';
|
|
|
|
if flush_in = '1' then
|
|
v_int.outstanding := 0;
|
|
elsif complete_in.valid = '1' then
|
|
v_int.outstanding := r_int.outstanding - 1;
|
|
end if;
|
|
if r_int.outstanding >= PIPELINE_DEPTH + 1 then
|
|
valid_tmp := '0';
|
|
stall_tmp := '1';
|
|
end if;
|
|
|
|
if rst = '1' then
|
|
gpr_write_valid <= '0';
|
|
cr_write_valid <= '0';
|
|
v_int := reg_internal_init;
|
|
valid_tmp := '0';
|
|
end if;
|
|
|
|
-- Handle debugger stop
|
|
stopped_out <= '0';
|
|
if stop_mark_in = '1' and v_int.outstanding = 0 then
|
|
stopped_out <= '1';
|
|
end if;
|
|
|
|
-- state machine to handle instructions that must be single
|
|
-- through the pipeline.
|
|
case r_int.state is
|
|
when IDLE =>
|
|
if valid_tmp = '1' then
|
|
if (sgl_pipe_in = '1') then
|
|
if v_int.outstanding /= 0 then
|
|
v_int.state := WAIT_FOR_PREV_TO_COMPLETE;
|
|
stall_tmp := '1';
|
|
else
|
|
-- send insn out and wait on it to complete
|
|
v_int.state := WAIT_FOR_CURR_TO_COMPLETE;
|
|
end if;
|
|
else
|
|
-- let it go out if there are no GPR or CR hazards
|
|
stall_tmp := gpr_tag_stall or cr_tag_stall;
|
|
end if;
|
|
end if;
|
|
|
|
when WAIT_FOR_PREV_TO_COMPLETE =>
|
|
if v_int.outstanding = 0 then
|
|
-- send insn out and wait on it to complete
|
|
v_int.state := WAIT_FOR_CURR_TO_COMPLETE;
|
|
else
|
|
stall_tmp := '1';
|
|
end if;
|
|
|
|
when WAIT_FOR_CURR_TO_COMPLETE =>
|
|
if v_int.outstanding = 0 then
|
|
v_int.state := IDLE;
|
|
-- XXX Don't replicate this
|
|
if valid_tmp = '1' then
|
|
if (sgl_pipe_in = '1') then
|
|
if v_int.outstanding /= 0 then
|
|
v_int.state := WAIT_FOR_PREV_TO_COMPLETE;
|
|
stall_tmp := '1';
|
|
else
|
|
-- send insn out and wait on it to complete
|
|
v_int.state := WAIT_FOR_CURR_TO_COMPLETE;
|
|
end if;
|
|
else
|
|
-- let it go out if there are no GPR or CR hazards
|
|
stall_tmp := gpr_tag_stall or cr_tag_stall;
|
|
end if;
|
|
end if;
|
|
else
|
|
stall_tmp := '1';
|
|
end if;
|
|
end case;
|
|
|
|
if stall_tmp = '1' then
|
|
valid_tmp := '0';
|
|
end if;
|
|
|
|
gpr_write_valid <= gpr_write_valid_in and valid_tmp;
|
|
cr_write_valid <= cr_write_in and valid_tmp;
|
|
|
|
if valid_tmp = '1' and deferred = '0' then
|
|
v_int.outstanding := v_int.outstanding + 1;
|
|
end if;
|
|
|
|
-- update outputs
|
|
valid_out <= valid_tmp;
|
|
stall_out <= stall_tmp or deferred;
|
|
|
|
-- update registers
|
|
rin_int <= v_int;
|
|
end process;
|
|
end;
|