-- Copyright 2000 UCR all rights reserved
-- this program may be copy or altered so
-- long as this header stays intake
-- Original design Randy January rjanuary@cs.ucr.edu

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use WORK.SCPU_LIB.all;

entity scpu_cntrl is 
   port(
	clk         : in std_logic;
	reset       : in std_logic;
        jmp         : in std_logic;
        rom_command : in unsigned (31 downto 0);
        interrupt   : in std_logic;
        W_sel       : out std_logic;
        R1_sel      : out std_logic;
        R2_sel      : out std_logic;
        O_sel       : out std_logic;
        r_wEn       : out std_logic;
        Zcheck      : out std_logic;
        signChk     : out std_logic;
        sign_out    : in  std_logic;
        mux_sel     : out unsigned (1 downto 0);
        sh_sel      : out unsigned (1 downto 0);
        alu_sel     : out unsigned (2 downto 0);
        rom_address : out integer;
        ram_address : out unsigned (8 downto 0);
        imm         : out unsigned (31 downto 0);
        Wrf_address : out unsigned (8 downto 0);
        Rrf1_address : out unsigned (8 downto 0);
        Rrf2_address : out unsigned (8 downto 0)
	);	
end scpu_cntrl;


architecture scpu_cntrl of scpu_cntrl is

  
--*****************************************************************************

type state_type is (S0, S1, S2, S3);
type sub_state_type is (SS0, SS1, SS2);

signal state: state_type; 		 
signal sub_state: sub_state_type;
signal haltsig: std_logic;
signal rom_add : unsigned (8 downto 0);
signal opcodes : unsigned (4 downto 0);
signal operand1s, operand2s, operand3s  : unsigned(8 downto 0);

--*****************************************************************************

begin

  process (clk, reset, jmp, rom_command, interrupt)

    variable OPCODE : unsigned(4 downto 0);
    variable OPERAND1, OPERAND2, OPERAND3 : unsigned(8 downto 0);
    variable PC : integer;

--*****************************************************************************

    procedure execute(OPCODE2 : unsigned (4 downto 0)) is
    begin			   
		

      -- loadi current takes in an 18 bit number and pads to
      -- the left the sign bit, this needs to be changed to
      -- seperate loadlow and loadhigh commands to get the
      -- full 32-bit input.
      if (OPCODE2 = loadi) then 
        mux_sel <= "00";
        W_sel <= '1';
        O_sel <= '0';
        for i in 18 to 31 loop
          imm(i) <= rom_command(17);
        end loop;  -- i 
        imm(17 downto 0) <= rom_command(17 downto 0);
        Rrf1_address <= OPERAND1;
        R1_sel <= '1';
        alu_sel <= "000";
        sh_sel <= "00";
        Wrf_address <= OPERAND1;

      elsif (OPCODE2 = loadm) then
        mux_sel <= "10";
        r_wEn <= '0';
        O_sel <= '0';
        W_sel <= '1';
        Wrf_address <= OPERAND1;
        ram_address <= OPERAND2;

      elsif (OPCODE2 = store) then
        mux_sel<= "01";
        r_wEn <= '1';
        O_sel <= '0';
        W_sel <= '0';
        ram_address <= OPERAND1;
        
      else
        Wrf_address <= OPERAND1;
        Rrf1_address <= OPERAND2;
        Rrf2_address <= OPERAND3;
        mux_sel <= "01";
        R1_sel <= '1';
        R2_sel <= '1';
    
        case OPCODE2 is
            
          when add =>
            O_sel <= '0';
            W_sel <= '1';
            alu_sel <= "001";
            sh_sel <= "00";
            signChk <= '0';
		
          when subt =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '1';
            alu_sel <= "010";
            sh_sel <= "00";
            signChk <= '0';
		
          when and_r =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '1';
            alu_sel <= "011";
            sh_sel <= "00";
            signChk <= '0';
           
          when xor_r =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '1';
            alu_sel <= "100";
            sh_sel <= "00";
            signChk <= '0';
            
          when or_r =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '1';
            alu_sel <= "110";
            sh_sel <= "00";
            signChk <= '0';
        
          when shl_r =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '0';
            alu_sel <= "000";
            sh_sel <= "10";
            signChk <= '0';
        
          when shr_r =>
            O_sel <= '0';
            W_sel <= '1';
            R2_sel <= '0';
            alu_sel <= "000";
            sh_sel <= "01";
            signChk <= '0';

          when sign =>
            O_sel <= '0';
            W_sel <= '0';
            alu_sel <= "000";
            sh_sel <= "00";
            signChk <= '1';
	    R2_sel <= '0';

          when jnz =>
            alu_sel <= "000";
            sh_sel <= "00";
            O_sel <= '0';
            W_sel <= '0';
            Zcheck <= '1';
            signChk <= '0';
	    R2_sel <= '0';
            
          when halt =>
            alu_sel <= "000";
            sh_sel <= "00";
            W_sel <= '0';
            O_sel <= '1';
	    haltsig <= '1';
            signChk <= '0';
          when others => NULL;   
                   
        end case;
      end if;
    end execute;  
--*****************************************************************************
	   	
  begin
    
--*****************************************************************************
    -- reset
    if (reset = '1') then
      haltsig <= '0';
      state <= S0;
      sub_state <= SS0;
      W_sel <= '0';
      R1_sel <= '0';
      R2_sel <= '0';
      Wrf_address <= CZ_9;
      Rrf1_address <= CZ_9;
      Rrf2_address <= CZ_9;
      haltsig <= '0';
      alu_sel <= "000";
      mux_sel <= "00";
      O_sel <= '0';
      rom_address <= 0;
      ram_address <= "000000000";
      Zcheck <= '0';
      signChk <= '0';
      
--*****************************************************************************

    elsif (clk'event and clk = '1') then
        
      case state is

         -- RESET STATE
         when S0 =>
           PC := 0;
           rom_address <= PC;
           imm <= "00000000000000000000000000000000";
           state <= S1;
      
		  
        when S1 =>                                  
          case sub_state is
              
            --FETCH STATE
            when SS0 =>  
              OPCODE := rom_command(31 downto 27);
              OPERAND1 := rom_command(26 downto 18);
              OPERAND2 := rom_command(17 downto 9);
              OPERAND3 := rom_command(8 downto 0);
              opcodes <= OPCODE;
              operand1s <= OPERAND1;
              operand2s <= OPERAND2;
              operand3s <= OPERAND3;
              sub_state <= SS1;
              state <= S1;

            -- WAIT STATE
            when SS2 =>
              sub_state <= SS0;
              state <= S2;

            --EXECUTE STATE
            when SS1 =>
              state <= S1;
              execute(OPCODE);
              sub_state <= SS2;
            when others => NULL;
                          
          end case;

        
        when S2 =>
           if(interrupt = '1') then
             state <= S3;   
           end if;
 
           case sub_state is
             when SS0 =>
--                 Zcheck <= '0';
                 W_sel <= '0';
--                 mux_sel <= "01";
--                 r_wEn <= '0';
--                 sh_sel <= "00";
                 alu_sel <= "000";
   
               if (haltsig = '1') then
                 sub_state <= SS2;
                 state <= S2;

               elsif (jmp = '1') then
                 PC := conv_integer(rom_command(8 downto 0));
                 rom_address <= PC;
                 sub_state <= SS1;
                 state <= S2;

               elsif (sign_out = '1') then 
                 PC := conv_integer(rom_command(8 downto 0));
                 rom_address <= PC;
                 sub_state <= SS1;
                 state <= S2;

               else
                 PC := PC + 1;
                 rom_address <= PC;
                 state <= S2;
                 sub_state <= SS1;
               end if;
               
             when SS1 =>
               signChk <= '0';
               Zcheck <= '0';
               W_sel <= '0';
               mux_sel <= "01";
               r_wEn <= '0';
               sh_sel <= "00";
               alu_sel <= "000";
               sub_state <= SS0;
               state <= S1;
             when SS2 =>
               W_sel <= '0';
               sh_sel <= "00";
               mux_sel <= "01";
               r_wEn <= '0';
               sub_state <= SS0;
               state <= S3;
           end case;

	when S3 =>
	  state <= S3;
    
      end case;
    end if;	
  end process;
end scpu_cntrl;





