-- Copyright (C) 1996 Morgan Kaufmann Publishers, Inc

-- This file is part of VESTs (Vhdl tESTs).

-- VESTs is free software; you can redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the
-- Free Software Foundation; either version 2 of the License, or (at
-- your option) any later version. 

-- VESTs is distributed in the hope that it will be useful, but WITHOUT
-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-- FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-- for more details. 

-- You should have received a copy of the GNU General Public License
-- along with VESTs; if not, write to the Free Software Foundation,
-- Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

-- ---------------------------------------------------------------------
--
-- $Id: ch_15_ctrl-b.vhd,v 1.3 2001-10-26 16:29:35 paw Exp $
-- $Revision: 1.3 $
--
-- ---------------------------------------------------------------------

library bv_utilities;
use bv_utilities.bv_arithmetic.all;

library work;
use work.dlx_instr.all;

architecture behavior of controller is

begin -- behavior

  sequencer : process is

                        variable current_instruction_bv : dlx_bv_word;

                      alias IR_opcode : dlx_opcode is current_instruction_bv(0 to 5);
                      alias IR_sp_func : dlx_sp_func is current_instruction_bv(26 to 31);
                      alias IR_fp_func : dlx_fp_func is current_instruction_bv(27 to 31);

                      alias IR_rs1 : reg_file_addr is current_instruction(6 to 10);
                      alias IR_rs2 : reg_file_addr is current_instruction(11 to 15);
                      alias IR_Itype_rd : reg_file_addr is current_instruction(11 to 15);
                      alias IR_Rtype_rd : reg_file_addr is current_instruction(16 to 20);

                      variable result_of_set_is_1, branch_taken : boolean;

                      variable disassembled_instr : string(1 to 40);
                      variable disassembled_instr_len : positive;

                      variable instr_count : natural := 0;

                      procedure bus_instruction_fetch is
                      begin
                        -- use PC as address
                        mem_addr_mux_sel <= '0' after Tpd_clk_ctrl;
                        -- set up memory control signals
                        width <= dlx_mem_width_word after Tpd_clk_ctrl;
                        ifetch <= '1' after Tpd_clk_ctrl;
                        write_enable <= '0' after Tpd_clk_ctrl;
                        mem_enable <= '1' after Tpd_clk_ctrl;
                        -- wait until phi2, then enable IR input
                        wait until rising_edge(phi2);
                        ir_latch_en <= '1' after Tpd_clk_ctrl;
                        -- wait until memory is ready at end of phi2
                        loop 
                          wait until falling_edge(phi2);
                          if To_bit(reset) = '1' then
                            return;
                          end if;
                          exit when To_bit(ready) = '1';
                        end loop;
                        -- disable IR input and memory control signals
                        ir_latch_en <= '0' after Tpd_clk_ctrl;
                        mem_enable <= '0' after Tpd_clk_ctrl;
                      end procedure bus_instruction_fetch;

                      procedure bus_data_read ( read_width : in dlx_mem_width ) is
                      begin
                        -- use MAR as address
                        mem_addr_mux_sel <= '1' after Tpd_clk_ctrl;
                        -- set up memory control signals
                        width <= read_width after Tpd_clk_ctrl;
                        ifetch <= '0' after Tpd_clk_ctrl;
                        write_enable <= '0' after Tpd_clk_ctrl;
                        mem_enable <= '1' after Tpd_clk_ctrl;
                        -- wait until phi2, then enable MDR input
                        wait until rising_edge(phi2);
                        mdr_mux_sel <= '1' after Tpd_clk_ctrl;
                        mdr_latch_en <= '1' after Tpd_clk_ctrl;
                        -- wait until memory is ready at end of phi2
                        loop 
                          wait until falling_edge(phi2);
                          if To_bit(reset) = '1' then
                            return;
                          end if;
                          exit when To_bit(ready) = '1';
                        end loop;
                        -- disable MDR input and memory control signals
                        mdr_latch_en <= '0' after Tpd_clk_ctrl;
                        mem_enable <= '0' after Tpd_clk_ctrl;
                      end procedure bus_data_read;

                      procedure bus_data_write ( write_width : in dlx_mem_width ) is
                      begin
                        -- use MAR as address
                        mem_addr_mux_sel <= '1' after Tpd_clk_ctrl;
                        -- enable MDR output
                        mdr_out_en3 <= '1' after Tpd_clk_ctrl;
                        -- set up memory control signals
                        width <= write_width after Tpd_clk_ctrl;
                        ifetch <= '0' after Tpd_clk_ctrl;
                        write_enable <= '1' after Tpd_clk_ctrl;
                        mem_enable <= '1' after Tpd_clk_ctrl;
                        -- wait until memory is ready at end of phi2
                        loop 
                          wait until falling_edge(phi2);
                          if To_bit(reset) = '1' then
                            return;
                          end if;
                          exit when To_bit(ready) = '1';
                        end loop;
                        -- disable MDR output and memory control signals
                        write_enable <= '0' after Tpd_clk_ctrl;
                        mem_enable <= '0' after Tpd_clk_ctrl;
                        mdr_out_en3 <= '0' after Tpd_clk_ctrl;
                      end procedure bus_data_write;

                      procedure do_set_result is
                      begin
                        wait until rising_edge(phi1);
                        if result_of_set_is_1 then
                          const2 <= X"0000_0001" after Tpd_clk_const;
                        else
                          const2 <= X"0000_0000" after Tpd_clk_const;
                        end if;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_pass_s2 after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        const2 <= disabled_dlx_word after Tpd_clk_const;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_set_result;    

                      procedure do_EX_set_unsigned ( immed : boolean ) is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        if immed then
                          ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
                          ir_immed2_unsigned <= '1' after Tpd_clk_ctrl;
                          ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        else
                          b_out_en <= '1' after Tpd_clk_ctrl;
                        end if;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_subu after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;
                        if immed then
                          ir_immed2_en <= '0' after Tpd_clk_ctrl;
                        else
                          b_out_en <= '0' after Tpd_clk_ctrl;
                        end if;

                        wait until falling_edge(phi2);
                        if immed then
                          case IR_opcode is
                            when op_sequi => 
                              result_of_set_is_1 := To_bit(alu_zero) = '1';
                            when op_sneui =>
                              result_of_set_is_1 := To_bit(alu_zero) /= '1';
                            when op_sltui =>
                              result_of_set_is_1 := To_bit(alu_overflow) = '1';
                            when op_sgtui => 
                              result_of_set_is_1 := To_bit(alu_overflow) /= '1' and To_bit(alu_zero) /= '1';
                            when op_sleui => 
                              result_of_set_is_1 := To_bit(alu_overflow) = '1' or To_bit(alu_zero) = '1';
                            when op_sgeui => 
                              result_of_set_is_1 := To_bit(alu_overflow) /= '1';
                            when others =>
                              null;
                          end case;
                        else
                          case IR_sp_func is
                            when sp_func_sequ => 
                              result_of_set_is_1 := To_bit(alu_zero) = '1';
                            when sp_func_sneu =>
                              result_of_set_is_1 := To_bit(alu_zero) /= '1';
                            when sp_func_sltu =>
                              result_of_set_is_1 := To_bit(alu_overflow) = '1';
                            when sp_func_sgtu =>
                              result_of_set_is_1 := To_bit(alu_overflow) /= '1' and To_bit(alu_zero) /= '1';
                            when sp_func_sleu => 
                              result_of_set_is_1 := To_bit(alu_overflow) = '1' or To_bit(alu_zero) = '1';
                            when sp_func_sgeu => 
                              result_of_set_is_1 := To_bit(alu_overflow) /= '1';
                            when others =>
                              null;
                          end case;
                        end if;

                        do_set_result;
                      end procedure do_EX_set_unsigned;

                      procedure do_EX_set_signed ( immed : boolean ) is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        if immed then
                          ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
                          ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
                          ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        else
                          b_out_en <= '1' after Tpd_clk_ctrl;
                        end if;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_sub after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;
                        if immed then
                          ir_immed2_en <= '0' after Tpd_clk_ctrl;
                        else
                          b_out_en <= '0' after Tpd_clk_ctrl;
                        end if;

                        wait until falling_edge(phi2);
                        if immed then
                          case IR_opcode is
                            when op_seqi => 
                              result_of_set_is_1 := To_bit(alu_zero) = '1';
                            when op_snei =>
                              result_of_set_is_1 := To_bit(alu_zero) /= '1';
                            when op_slti =>
                              result_of_set_is_1 := To_bit(alu_negative) = '1';
                            when op_sgti => 
                              result_of_set_is_1 := To_bit(alu_negative) /= '1' and To_bit(alu_zero) /= '1';
                            when op_slei => 
                              result_of_set_is_1 := To_bit(alu_negative) = '1' or To_bit(alu_zero) = '1';
                            when op_sgei => 
                              result_of_set_is_1 := To_bit(alu_negative) /= '1';
                            when others =>
                              null;
                          end case;
                        else
                          case IR_sp_func is
                            when sp_func_seq => 
                              result_of_set_is_1 := To_bit(alu_zero) = '1';
                            when sp_func_sne =>
                              result_of_set_is_1 := To_bit(alu_zero) /= '1';
                            when sp_func_slt =>
                              result_of_set_is_1 := To_bit(alu_negative) = '1';
                            when sp_func_sgt => 
                              result_of_set_is_1 := To_bit(alu_negative) /= '1' and To_bit(alu_zero) /= '1';
                            when sp_func_sle => 
                              result_of_set_is_1 := To_bit(alu_negative) = '1' or To_bit(alu_zero) = '1';
                            when sp_func_sge => 
                              result_of_set_is_1 := To_bit(alu_negative) /= '1';
                            when others =>
                              null;
                          end case;
                        end if;

                        do_set_result;
                      end procedure do_EX_set_signed;

                      procedure do_EX_arith_logic is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        b_out_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        case IR_sp_func is
                          when sp_func_add =>
                            alu_function <= alu_add after Tpd_clk_ctrl;
                          when sp_func_addu =>
                            alu_function <= alu_addu after Tpd_clk_ctrl;
                          when sp_func_sub =>
                            alu_function <= alu_sub after Tpd_clk_ctrl;
                          when sp_func_subu =>
                            alu_function <= alu_subu after Tpd_clk_ctrl;
                          when sp_func_and =>
                            alu_function <= alu_and after Tpd_clk_ctrl;
                          when sp_func_or =>
                            alu_function <= alu_or after Tpd_clk_ctrl;
                          when sp_func_xor =>
                            alu_function <= alu_xor after Tpd_clk_ctrl;
                          when sp_func_sll =>
                            alu_function <= alu_sll after Tpd_clk_ctrl;
                          when sp_func_srl =>
                            alu_function <= alu_srl after Tpd_clk_ctrl;
                          when sp_func_sra =>
                            alu_function <= alu_sra after Tpd_clk_ctrl;
                          when others =>
                            null;
                        end case;                  --  IR_sp_func

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;
                        b_out_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_EX_arith_logic;

                      procedure do_EX_arith_logic_immed is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
                        if IR_opcode = op_addi or IR_opcode = op_subi then
                          ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
                        else
                          ir_immed2_unsigned <= '1' after Tpd_clk_ctrl;
                        end if;
                        ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        case IR_opcode is
                          when op_addi =>
                            alu_function <= alu_add after Tpd_clk_ctrl;
                          when op_subi =>
                            alu_function <= alu_sub after Tpd_clk_ctrl;
                          when op_addui =>
                            alu_function <= alu_addu after Tpd_clk_ctrl;
                          when op_subui =>
                            alu_function <= alu_subu after Tpd_clk_ctrl;
                          when op_andi =>
                            alu_function <= alu_and after Tpd_clk_ctrl;
                          when op_ori =>
                            alu_function <= alu_or after Tpd_clk_ctrl;
                          when op_xori =>
                            alu_function <= alu_xor after Tpd_clk_ctrl;
                          when op_slli =>
                            alu_function <= alu_sll after Tpd_clk_ctrl;
                          when op_srli =>
                            alu_function <= alu_srl after Tpd_clk_ctrl;
                          when op_srai =>
                            alu_function <= alu_sra after Tpd_clk_ctrl;
                          when others =>
                            null;
                        end case;                      --  IR_opcode

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_EX_arith_logic_immed;

                      procedure do_EX_link is
                      begin
                        wait until rising_edge(phi1);
                        pc_out_en1 <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_pass_s1 after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        pc_out_en1 <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_EX_link;

                      procedure do_EX_lhi is
                      begin
                        wait until rising_edge(phi1);
                        ir_immed1_size_26 <= '0' after Tpd_clk_ctrl;
                        ir_immed1_unsigned <= '1' after Tpd_clk_ctrl;
                        ir_immed1_en <= '1' after Tpd_clk_ctrl;
                        const2 <= X"0000_0010" after Tpd_clk_const;         -- shift by 16 bits
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_sll after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        ir_immed1_en <= '0' after Tpd_clk_ctrl;
                        const2 <= disabled_dlx_word after Tpd_clk_const;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_EX_lhi;    

                      procedure do_EX_branch is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_pass_s1 after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        if IR_opcode = op_beqz then
                          branch_taken := To_bit(alu_zero) = '1';
                        else
                          branch_taken := To_bit(alu_zero) /= '1';
                        end if;
                      end procedure do_EX_branch;

                      procedure do_EX_load_store is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
                        ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_add after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        mar_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        mar_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_EX_load_store;

                      procedure do_MEM_jump is
                      begin
                        wait until rising_edge(phi1);
                        pc_out_en1 <= '1' after Tpd_clk_ctrl;
                        ir_immed2_size_26 <= '1' after Tpd_clk_ctrl;
                        ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_add after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        pc_out_en1 <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        pc_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        pc_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_MEM_jump;

                      procedure do_MEM_jump_reg is
                      begin
                        wait until rising_edge(phi1);
                        a_out_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_pass_s1 after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        a_out_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        pc_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        pc_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_MEM_jump_reg;

                      procedure do_MEM_branch is
                      begin
                        wait until rising_edge(phi1);
                        pc_out_en1 <= '1' after Tpd_clk_ctrl;
                        ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
                        ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '1' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_add after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;
                        pc_out_en1 <= '0' after Tpd_clk_ctrl;
                        ir_immed2_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        pc_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        pc_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_MEM_branch;

                      procedure do_MEM_load is
                        subtype ls_2_addr_bits is bit_vector(1 downto 0);
                      begin
                        wait until rising_edge(phi1);
                        if IR_opcode = op_lb or IR_opcode = op_lbu then
                          bus_data_read(dlx_mem_width_byte);
                        elsif IR_opcode = op_lh or IR_opcode = op_lhu then
                          bus_data_read(dlx_mem_width_halfword);
                        else
                          bus_data_read(dlx_mem_width_word);
                        end if;
                        if To_bit(reset) = '1' then
                          return;
                        end if;

                        if ( (IR_opcode = op_lb or IR_opcode = op_lbu) and To_bitvector(mem_addr) /= "00" )
                          or ( (IR_opcode = op_lh or IR_opcode = op_lhu) and To_bit(mem_addr(1)) /= '0' ) then
                          -- first step of extension: left-justify byte or halfword -> mdr
                          wait until rising_edge(phi1);
                          mdr_out_en1 <= '1' after Tpd_clk_ctrl;
                          if IR_opcode = op_lb or IR_opcode = op_lbu then
                            case ls_2_addr_bits'(To_bitvector(mem_addr)) is
                              when "00" => 
                                null;
                              when "01" => 
                                const2 <= X"0000_0008" after Tpd_clk_const;
                              when "10" => 
                                const2 <= X"0000_0010" after Tpd_clk_const;
                              when "11" => 
                                const2 <= X"0000_0018" after Tpd_clk_const;
                            end case;
                          else
                            const2 <= X"0000_0010" after Tpd_clk_const;
                          end if;
                          alu_function <= alu_sll after Tpd_clk_ctrl;
                          alu_in_latch_en <= '1' after Tpd_clk_ctrl;

                          wait until falling_edge(phi1);
                          mdr_out_en1 <= '0' after Tpd_clk_ctrl;
                          const2 <= disabled_dlx_word after Tpd_clk_const;
                          alu_in_latch_en <= '0' after Tpd_clk_ctrl;

                          wait until rising_edge(phi2);
                          mdr_mux_sel <= '0' after Tpd_clk_ctrl;
                          mdr_latch_en <= '1' after Tpd_clk_ctrl;

                          wait until falling_edge(phi2);
                          mdr_latch_en <= '0' after Tpd_clk_ctrl;
                        end if;

                        wait until rising_edge(phi1);
                        mdr_out_en1 <= '1' after Tpd_clk_ctrl;
                        if IR_opcode = op_lb or IR_opcode = op_lbu then
                          const2 <= X"0000_0018" after Tpd_clk_const;
                        elsif IR_opcode = op_lh or IR_opcode = op_lhu then
                          const2 <= X"0000_0010" after Tpd_clk_const;
                        else
                          const2 <= X"0000_0000" after Tpd_clk_const;
                        end if;
                        if IR_opcode = op_lbu or IR_opcode = op_lhu then
                          alu_function <= alu_srl after Tpd_clk_ctrl;
                        else
                          alu_function <= alu_sra after Tpd_clk_ctrl;
                        end if;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        mdr_out_en1 <= '0' after Tpd_clk_ctrl;
                        const2 <= disabled_dlx_word after Tpd_clk_const;
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        c_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        c_latch_en <= '0' after Tpd_clk_ctrl;
                      end procedure do_MEM_load;    

                      procedure do_MEM_store is
                        subtype ls_2_addr_bits is bit_vector(1 downto 0);
                      begin
                        wait until rising_edge(phi1);
                        b_out_en <= '1' after Tpd_clk_ctrl;
                        alu_function <= alu_pass_s2 after Tpd_clk_ctrl;
                        alu_in_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi1);
                        b_out_en <= '0' after Tpd_clk_ctrl;
                        alu_in_latch_en <= '0' after Tpd_clk_ctrl;

                        wait until rising_edge(phi2);
                        mdr_mux_sel <= '0' after Tpd_clk_ctrl;
                        mdr_latch_en <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        mdr_latch_en <= '0' after Tpd_clk_ctrl;

                        if ( IR_opcode = op_sb and To_bitvector(mem_addr) /= "11" )
                          or ( IR_opcode = op_sh and To_bit(mem_addr(1)) /= '1' ) then
                          -- align byte or halfword -> mdr
                          wait until rising_edge(phi1);
                          mdr_out_en1 <= '1' after Tpd_clk_ctrl;
                          if IR_opcode = op_sb then
                            case ls_2_addr_bits'(To_bitvector(mem_addr)) is
                              when "00" => 
                                const2 <= X"0000_0018" after Tpd_clk_const;
                              when "01" => 
                                const2 <= X"0000_0010" after Tpd_clk_const;
                              when "10" => 
                                const2 <= X"0000_0008" after Tpd_clk_const;
                              when "11" => 
                                null;
                            end case;
                          else
                            const2 <= X"0000_0010" after Tpd_clk_const;
                          end if;
                          alu_function <= alu_sll after Tpd_clk_ctrl;
                          alu_in_latch_en <= '1' after Tpd_clk_ctrl;

                          wait until falling_edge(phi1);
                          mdr_out_en1 <= '0' after Tpd_clk_ctrl;
                          const2 <= disabled_dlx_word after Tpd_clk_const;
                          alu_in_latch_en <= '0' after Tpd_clk_ctrl;

                          wait until rising_edge(phi2);
                          mdr_mux_sel <= '0' after Tpd_clk_ctrl;
                          mdr_latch_en <= '1' after Tpd_clk_ctrl;

                          wait until falling_edge(phi2);
                          mdr_latch_en <= '0' after Tpd_clk_ctrl;
                        end if;

                        wait until rising_edge(phi1);
                        if IR_opcode = op_sb then
                          bus_data_write(dlx_mem_width_byte);
                        elsif IR_opcode = op_sh then
                          bus_data_write(dlx_mem_width_halfword);
                        else
                          bus_data_write(dlx_mem_width_word);
                        end if;
                      end procedure do_MEM_store;

                      procedure do_WB ( Rd : reg_file_addr ) is
                      begin
                        wait until rising_edge(phi1);
                        reg_dest_addr <= Rd after Tpd_clk_ctrl;
                        reg_write <= '1' after Tpd_clk_ctrl;

                        wait until falling_edge(phi2);
                        reg_write <= '0' after Tpd_clk_ctrl;
                      end procedure do_WB;

                      procedure execute_op_special is
                      begin
                        case IR_sp_func is
                          when sp_func_nop =>
                            null;
                          when sp_func_add | sp_func_addu | sp_func_sub | sp_func_subu
                            | sp_func_sll | sp_func_srl | sp_func_sra
                            | sp_func_and | sp_func_or | sp_func_xor =>
                            do_EX_arith_logic;
                            do_WB(IR_Rtype_rd);
                          when sp_func_sequ | sp_func_sneu | sp_func_sltu
                            | sp_func_sgtu | sp_func_sleu | sp_func_sgeu => 
                            do_EX_set_unsigned(immed => false);
                            do_WB(IR_Rtype_rd);
                          when sp_func_seq | sp_func_sne | sp_func_slt
                            | sp_func_sgt | sp_func_sle | sp_func_sge => 
                            do_EX_set_signed(immed => false);
                            do_WB(IR_Rtype_rd);
                          when sp_func_movi2s | sp_func_movs2i
                            | sp_func_movf | sp_func_movd
                            | sp_func_movfp2i | sp_func_movi2fp  => 
                            report sp_func_names(bv_to_natural(IR_sp_func))
                              & " instruction not implemented" severity warning;
                          when others =>
                            report "undefined special instruction function" severity error;
                        end case;  
                      end procedure execute_op_special;

                      procedure execute_op_fparith is
                      begin
                        case IR_fp_func is
                          when fp_func_mult | fp_func_multu | fp_func_div | fp_func_divu
                            | fp_func_addf | fp_func_subf | fp_func_multf | fp_func_divf
                            | fp_func_addd | fp_func_subd | fp_func_multd | fp_func_divd
                            | fp_func_cvtf2d | fp_func_cvtf2i | fp_func_cvtd2f
                            | fp_func_cvtd2i | fp_func_cvti2f | fp_func_cvti2d
                            | fp_func_eqf | fp_func_nef | fp_func_ltf | fp_func_gtf
                            | fp_func_lef | fp_func_gef | fp_func_eqd | fp_func_ned
                            | fp_func_ltd | fp_func_gtd | fp_func_led | fp_func_ged => 
                            report fp_func_names(bv_to_natural(IR_fp_func))
                              & " instruction not implemented" severity warning;
                          when others =>
                            report "undefined floating point instruction function" severity error;
                        end case;
                      end procedure execute_op_fparith;

  begin -- sequencer

    ----------------------------------------------------------------
    -- initialize all control signals
    ----------------------------------------------------------------
    if debug > none then
      report "initializing";
    end if;

    halt <= '0' after Tpd_clk_ctrl;
    width <= dlx_mem_width_word after Tpd_clk_ctrl;
    write_enable <= '0' after Tpd_clk_ctrl;
    mem_enable <= '0' after Tpd_clk_ctrl;
    ifetch <= '0' after Tpd_clk_ctrl;
    alu_in_latch_en <= '0' after Tpd_clk_ctrl;
    alu_function <= alu_add after Tpd_clk_ctrl;
    reg_s1_addr <= B"00000" after Tpd_clk_ctrl;
    reg_s2_addr <= B"00000" after Tpd_clk_ctrl;
    reg_dest_addr <= B"00000" after Tpd_clk_ctrl;
    reg_write <= '0' after Tpd_clk_ctrl;
    c_latch_en <= '0' after Tpd_clk_ctrl;
    a_latch_en <= '0' after Tpd_clk_ctrl;
    a_out_en <= '0' after Tpd_clk_ctrl;
    b_latch_en <= '0' after Tpd_clk_ctrl;
    b_out_en <= '0' after Tpd_clk_ctrl;
    temp_latch_en <= '0' after Tpd_clk_ctrl;
    temp_out_en1 <= '0' after Tpd_clk_ctrl;
    temp_out_en2 <= '0' after Tpd_clk_ctrl;
    iar_latch_en <= '0' after Tpd_clk_ctrl;
    iar_out_en1 <= '0' after Tpd_clk_ctrl;
    iar_out_en2 <= '0' after Tpd_clk_ctrl;
    pc_latch_en <= '0' after Tpd_clk_ctrl;
    pc_out_en1 <= '0' after Tpd_clk_ctrl;
    pc_out_en2 <= '0' after Tpd_clk_ctrl;
    mar_latch_en <= '0' after Tpd_clk_ctrl;
    mar_out_en1 <= '0' after Tpd_clk_ctrl;
    mar_out_en2 <= '0' after Tpd_clk_ctrl;
    mem_addr_mux_sel <= '0' after Tpd_clk_ctrl;
    mdr_latch_en <= '0' after Tpd_clk_ctrl;
    mdr_out_en1 <= '0' after Tpd_clk_ctrl;
    mdr_out_en2 <= '0' after Tpd_clk_ctrl;
    mdr_out_en3 <= '0' after Tpd_clk_ctrl;
    mdr_mux_sel <= '0' after Tpd_clk_ctrl;
    ir_latch_en <= '0' after Tpd_clk_ctrl;
    ir_immed1_size_26 <= '0' after Tpd_clk_ctrl;
    ir_immed2_size_26 <= '0' after Tpd_clk_ctrl;
    ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
    ir_immed2_unsigned <= '0' after Tpd_clk_ctrl;
    ir_immed1_en <= '0' after Tpd_clk_ctrl;
    ir_immed2_en <= '0' after Tpd_clk_ctrl;
    const1 <= disabled_dlx_word after Tpd_clk_const;
    const2 <= disabled_dlx_word after Tpd_clk_const;

    instr_count := 0;

    wait on phi2 until falling_edge(phi2) and To_bit(reset) = '0';

    ----------------------------------------------------------------
    -- control loop
    ----------------------------------------------------------------
    loop
      exit when To_bit(reset) = '1';

      ----------------------------------------------------------------
      -- fetch next instruction (IF)
      ----------------------------------------------------------------
      wait until rising_edge(phi1);

      instr_count := instr_count + 1;
      if debug = msg_every_100_instructions and instr_count mod 100 = 0 then
        report "instruction count = " & natural'image(instr_count);
      end if;

      if debug >= msg_each_instruction then
        report "fetching instruction";
      end if;

      bus_instruction_fetch;
      exit when To_bit(reset) = '1';
      current_instruction_bv := To_bitvector(current_instruction);

      if debug >= trace_each_instruction then
        disassemble(current_instruction_bv, disassembled_instr, disassembled_instr_len);
        report disassembled_instr(1 to disassembled_instr_len);
      end if;

      ----------------------------------------------------------------
      -- instruction decode, source register read and PC increment (ID)
      ----------------------------------------------------------------
      wait until rising_edge(phi1);

      if debug = trace_each_step then
        report "decode, source register read and PC increment";
      end if;

      reg_s1_addr <= IR_rs1 after Tpd_clk_ctrl;
      reg_s2_addr <= IR_rs2 after Tpd_clk_ctrl;
      a_latch_en <= '1' after Tpd_clk_ctrl;
      b_latch_en <= '1' after Tpd_clk_ctrl;

      pc_out_en1 <= '1' after Tpd_clk_ctrl;
      const2 <= X"0000_0004" after Tpd_clk_const;
      alu_in_latch_en <= '1' after Tpd_clk_ctrl;
      alu_function <= alu_addu after Tpd_clk_ctrl;

      wait until falling_edge(phi1);
      a_latch_en <= '0' after Tpd_clk_ctrl;
      b_latch_en <= '0' after Tpd_clk_ctrl;
      alu_in_latch_en <= '0' after Tpd_clk_ctrl;
      pc_out_en1 <= '0' after Tpd_clk_ctrl;
      const2 <= disabled_dlx_word after Tpd_clk_const;

      wait until rising_edge(phi2);
      pc_latch_en <= '1' after Tpd_clk_ctrl;

      wait until falling_edge(phi2);
      pc_latch_en <= '0' after Tpd_clk_ctrl;

      ----------------------------------------------------------------
      -- execute instruction, (EX, MEM, WB)
      ----------------------------------------------------------------
      if debug = trace_each_step then
        report "execute";
      end if;

      case IR_opcode is
        when op_special =>
          execute_op_special;
        when op_fparith => 
          execute_op_fparith;
        when op_j =>
          do_MEM_jump;
        when op_jal =>
          do_EX_link;
          do_MEM_jump;
          do_WB(To_X01(natural_to_bv(link_reg, 5)));
        when op_jr =>
          do_MEM_jump_reg;
        when op_jalr =>
          do_EX_link;
          do_MEM_jump_reg;
          do_WB(To_X01(natural_to_bv(link_reg, 5)));
        when op_beqz | op_bnez => 
          do_EX_branch;
          if branch_taken then
            do_MEM_branch;
          end if;
        when op_addi | op_subi | op_addui | op_subui 
          | op_slli | op_srli | op_srai
          | op_andi | op_ori | op_xori =>
          do_EX_arith_logic_immed;
          do_WB(IR_Itype_rd);
        when op_lhi =>
          do_EX_lhi;
          do_WB(IR_Itype_rd);
        when op_sequi | op_sneui | op_sltui
          | op_sgtui | op_sleui | op_sgeui => 
          do_EX_set_unsigned(immed => true);
          do_WB(IR_Itype_rd);
        when op_seqi | op_snei | op_slti
          | op_sgti | op_slei | op_sgei => 
          do_EX_set_signed(immed => true);
          do_WB(IR_Itype_rd);
        when op_trap =>
          report "TRAP instruction encountered, execution halted"
            severity note;
          wait until rising_edge(phi1);
          halt <= '1' after Tpd_clk_ctrl;
          wait until reset = '1';
          exit;
        when op_lb | op_lh | op_lw | op_lbu | op_lhu => 
          do_EX_load_store;
          do_MEM_load;
          exit when reset = '1';
          do_WB(IR_Itype_rd);
        when op_sb | op_sh | op_sw =>
          do_EX_load_store;
          do_MEM_store;
          exit when reset = '1';
        when op_rfe | op_bfpt | op_bfpf | op_lf | op_ld | op_sf | op_sd =>
          report opcode_names(bv_to_natural(IR_opcode))
            & " instruction not implemented" severity warning;
        when others =>
          report "undefined instruction" severity error;
      end case;

      -- overflow and divide-by-zero exception handing
      -- (not implemented)

      if debug = trace_each_step then
        report "end of execution";
      end if;

    end loop;
    -- loop is only exited when reset active:
    -- process interpreter starts again from beginning
  end process sequencer;


end architecture behavior;