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;