-- Kyle McGhee
-- kmcghee@purdue.edu
-- shift_register.vhd

-- This module shifts data in 32 bits at a time on the asynchronous LOAD clock.
-- Once all data has been loaded in (based on the opcode), it will set the busy flag high.
-- This flag will return low when the controller sets the READ flag high.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity SHIFT_REGISTER is
  port (
    CLK: in STD_LOGIC;
    LOAD: in STD_LOGIC;
    nRST: in STD_LOGIC;

    OPCODE_IN: in STD_LOGIC_VECTOR(3 downto 0);
    OPCODE_OUT: out STD_LOGIC_VECTOR(3 downto 0);

    DATA_IN: in STD_LOGIC_VECTOR(31 downto 0);
    DATA_OUT: out STD_LOGIC_VECTOR(511 downto 0);

    BUSY_OUT: out STD_LOGIC;
    READ: in STD_LOGIC);
end SHIFT_REGISTER;

architecture SHIFT_REGISTER_ARCH of SHIFT_REGISTER is
  constant OPCODE_SETMAT: STD_LOGIC_VECTOR(3 downto 0) := "0000";
  constant OPCODE_MULMAT: STD_LOGIC_VECTOR(3 downto 0) := "0001";
  constant OPCODE_RSTMAT: STD_LOGIC_VECTOR(3 downto 0) := "0010";
  constant OPCODE_XFOV: STD_LOGIC_VECTOR(3 downto 0) := "0011";
  constant OPCODE_YFOV: STD_LOGIC_VECTOR(3 downto 0) := "0100";
  constant OPCODE_PT3D: STD_LOGIC_VECTOR(3 downto 0) := "0101";
  constant OPCODE_LN3D: STD_LOGIC_VECTOR(3 downto 0) := "0110";
  constant OPCODE_COLOR: STD_LOGIC_VECTOR(3 downto 0) := "0111";
  constant OPCODE_CLR: STD_LOGIC_VECTOR(3 downto 0) := "1000";
  constant OPCODE_FLIP: STD_LOGIC_VECTOR(3 downto 0) := "1001";
  constant OPCODE_PT2D: STD_LOGIC_VECTOR(3 downto 0) := "1010";
  constant OPCODE_LN2D: STD_LOGIC_VECTOR(3 downto 0) := "1011";
  constant OPCODE_IDLE: STD_LOGIC_VECTOR(3 downto 0) := "1111";

  signal OLD_LOAD: STD_LOGIC;
  signal OLD_READ: STD_LOGIC;

  signal DATA: STD_LOGIC_VECTOR(511 downto 0);

  signal BUSY : STD_LOGIC;
  signal NEXT_BUSY: STD_LOGIC;

  signal COUNT: UNSIGNED(3 downto 0);
  signal NEXT_COUNT: UNSIGNED(3 downto 0);
begin
  DATA_OUT <= DATA;

  NEXT_COUNT <= COUNT - 1;
  BUSY <= NEXT_BUSY;
  BUSY_OUT <= BUSY;

  process (nRST, CLK, LOAD, READ)
  begin
    if (nRST = '0') then
      DATA <= x"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
      OLD_LOAD <= LOAD;
      OLD_READ <= READ;
      NEXT_BUSY <= '0';
      COUNT <= "0000";
      OPCODE_OUT <= "1111";
    elsif (CLK'event and CLK = '1') then
      -- On a synchronized READ reset BUSY
      if ((BUSY = '1') and (READ = '1') and (OLD_READ = '0')) then
        NEXT_BUSY <= '0';
      end if;

      -- On a synchronized LOAD possibly load in the next opcode or set BUSY
      if ((BUSY = '0') and (LOAD = '1') and (OLD_LOAD = '0')) then
        if (NEXT_COUNT = "1111") then
          OPCODE_OUT <= OPCODE_IN;

          case OPCODE_IN is
            when OPCODE_SETMAT =>
              COUNT <= "1111";
              NEXT_BUSY <= '0';
            when OPCODE_MULMAT =>
              COUNT <= "1111";
              NEXT_BUSY <= '0';
            when OPCODE_RSTMAT =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_XFOV =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_YFOV =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_PT3D =>
              COUNT <= "0011";
              NEXT_BUSY <= '0';
            when OPCODE_LN3D =>
              COUNT <= "0101";
              NEXT_BUSY <= '0';
            when OPCODE_COLOR =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_CLR =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_FLIP =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_PT2D =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when OPCODE_LN2D =>
              COUNT <= "0000";
              NEXT_BUSY <= '1';
            when others =>
              COUNT <= "0000";
              NEXT_BUSY <= '0';
          end case;
        else
          COUNT <= NEXT_COUNT;
          if (NEXT_COUNT = "0000") then
            NEXT_BUSY <= '1';
          end if;
        end if;

        -- Shift the data over
        DATA(511 downto 480) <= DATA(479 downto 448);
        DATA(479 downto 448) <= DATA(447 downto 416);
        DATA(447 downto 416) <= DATA(415 downto 384);
        DATA(415 downto 384) <= DATA(383 downto 352);
        DATA(383 downto 352) <= DATA(351 downto 320);
        DATA(351 downto 320) <= DATA(319 downto 288);
        DATA(319 downto 288) <= DATA(287 downto 256);
        DATA(287 downto 256) <= DATA(255 downto 224);
        DATA(255 downto 224) <= DATA(223 downto 192);
        DATA(223 downto 192) <= DATA(191 downto 160);
        DATA(191 downto 160) <= DATA(159 downto 128);
        DATA(159 downto 128) <= DATA(127 downto  96);
        DATA(127 downto  96) <= DATA( 95 downto  64);
        DATA( 95 downto  64) <= DATA( 63 downto  32);
        DATA( 63 downto  32) <= DATA( 31 downto   0);
        DATA( 31 downto   0) <= DATA_IN;
      end if;

      OLD_LOAD <= LOAD;
      OLD_READ <= READ;
    end if;
  end process;
end SHIFT_REGISTER_ARCH;
