-- "blowfishvhdl" is a free synthesizable VHDL Blowfish implementation
-- Copyright (C) 2000  Wesley J. Landaker <wjl@mindless.com>

-- This program 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.

-- This program 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 this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

-- See http://blowfishvhdl.sourceforge.net for more information.

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity parray_control is
port (
  clk  : in std_logic;

  init_parray : in bit; -- tells us to initialize the parray
  sub_parray  : in bit; -- tells us to run the zero_block through

  sub_parray_done : out bit;
  
  sboxes_zero_block : out std_logic_vector(63 downto 0); -- to sboxes_control

  parray_init_addr  : buffer std_logic_vector(4 downto 0);
  parray_init_chunk : out std_logic_vector(31 downto 0);
  parray_init_data  : in  std_logic_vector(31 downto 0);
  parray_addr     : out std_logic_vector(4 downto 0);
  parray_data_in  : out std_logic_vector(31 downto 0);
  parray_data_out : in  std_logic_vector(31 downto 0);
  parray_we   : out bit;
  parray_crypt_go : out bit;
  parray_crypt_ptext : out std_logic_vector(63 downto 0);
  
  crypt_ctext : in  std_logic_vector(63 downto 0);
  crypt_parray_addr : in std_logic_vector(4 downto 0);
  crypt_parray_data : out std_logic_vector(31 downto 0);
  crypt_done : in bit;

  keychunk_blob : in std_logic_vector(447 downto 0)
  
);
end parray_control;

architecture fishy of parray_control is

  type PARRAY_STATES is (PASS, INIT, SUB, SUB_WRITE1, SUB_WRITE2);
  signal parray_state  : PARRAY_STATES;  

  type KEYCHUNK_TYPE is array (0 to 13) of std_logic_vector(31 downto 0); 
  signal keychunk : KEYCHUNK_TYPE;

  signal zero_block : std_logic_vector(63 downto 0);
  
begin

  -- split up the blob into the chunks we just selected
  keychunk(0) <= keychunk_blob(31 downto 0);
  keychunk(1) <= keychunk_blob(63 downto 32);
  keychunk(2) <= keychunk_blob(95 downto 64);
  keychunk(3) <= keychunk_blob(127 downto 96);
  keychunk(4) <= keychunk_blob(159 downto 128);
  keychunk(5) <= keychunk_blob(191 downto 160);
  keychunk(6) <= keychunk_blob(223 downto 192);
  keychunk(7) <= keychunk_blob(255 downto 224);
  keychunk(8) <= keychunk_blob(287 downto 256);
  keychunk(9) <= keychunk_blob(319 downto 288);
  keychunk(10) <= keychunk_blob(351 downto 320);
  keychunk(11) <= keychunk_blob(383 downto 352);
  keychunk(12) <= keychunk_blob(415 downto 384);
  keychunk(13) <= keychunk_blob(447 downto 416);
  
  -- process to control parray initialization
  process(clk, parray_state, init_parray, crypt_done, parray_init_addr,
          sub_parray, keychunk)
    variable just_started : std_logic;
  begin
    if (clk'event AND clk = '1') then
      sub_parray_done <= '0';
      if (parray_state = SUB) then
        if (crypt_done = '1' AND just_started = '0') then
          parray_state <= SUB_WRITE1;
          --parray_init_addr - don't modify it
          parray_init_chunk <= (others => '0');
          parray_crypt_go <= '0';
        else
          just_started := '0';
          parray_state <= SUB;
          --parray_init_addr - don't modify it
	  parray_init_chunk <= (others => '0');
          parray_crypt_go <= '0';
        end if;
      elsif (parray_state = SUB_WRITE1) then
        parray_state <= SUB_WRITE2;
        parray_init_addr <= parray_init_addr + 1;
        parray_init_chunk <= (others => '0');
        parray_crypt_go <= '0';
      elsif (parray_state = SUB_WRITE2) then
        if (parray_init_addr = 17) then
          parray_state <= PASS;
          parray_crypt_go <= '0';
        else
	  parray_state <= SUB;
          parray_crypt_go <= '1';
        end if;
        parray_init_addr <= parray_init_addr + 1;
        parray_init_chunk <= (others => '0');
        just_started := '1';            -- so we don't write two in a row!
      elsif (sub_parray = '1') then
        parray_state <= SUB;
        parray_init_addr <= (others => '0');
        parray_init_chunk <= (others => '0');
        parray_crypt_go <= '1';  
        just_started := '1';
      elsif (parray_state = INIT) then
        if (parray_init_addr = 17) then
          parray_state <= PASS;
        else
          parray_state <= INIT;
        end if;
        parray_init_addr <= parray_init_addr + 1;
        case parray_init_addr is
          when "00000" => parray_init_chunk <= keychunk(1);
          when "00001" => parray_init_chunk <= keychunk(2);
          when "00010" => parray_init_chunk <= keychunk(3);
          when "00011" => parray_init_chunk <= keychunk(4);
          when "00100" => parray_init_chunk <= keychunk(5);
          when "00101" => parray_init_chunk <= keychunk(6);
          when "00110" => parray_init_chunk <= keychunk(7);
          when "00111" => parray_init_chunk <= keychunk(8);
          when "01000" => parray_init_chunk <= keychunk(9);
          when "01001" => parray_init_chunk <= keychunk(10);
          when "01010" => parray_init_chunk <= keychunk(11);
          when "01011" => parray_init_chunk <= keychunk(12);
          when "01100" => parray_init_chunk <= keychunk(13);
          when "01101" => parray_init_chunk <= keychunk(0);
          when "01110" => parray_init_chunk <= keychunk(1);
          when "01111" => parray_init_chunk <= keychunk(2);
          when others  => parray_init_chunk <= keychunk(3);
        end case;
        parray_crypt_go <= '0';
      elsif (init_parray = '1') then
	parray_state <= INIT;
        parray_init_addr <= (others => '0');
        parray_init_chunk <= keychunk(0);
        parray_crypt_go <= '0';
      else -- if we're not initializing, just pass-through
        parray_state <= PASS;
        parray_init_addr <= (others => '0');
	parray_init_chunk <= (others => '0');
        parray_crypt_go <= '0';
        sub_parray_done <= '1';
      end if;
    end if;
  end process;

  -- process to control the zero_block used in initalizations
  process(clk, parray_state, zero_block, crypt_ctext, sub_parray)
  begin
    if (clk'event AND clk = '1') then
      if (parray_state = SUB_WRITE1) then
        zero_block <= crypt_ctext;
        sboxes_zero_block <= crypt_ctext;
      elsif (sub_parray = '1') then
        zero_block <= (others => '0');
      end if;
    end if;
    parray_crypt_ptext <= zero_block;
  end process;


  crypt_parray_data <= parray_data_out;
  
  -- process to allow parray init or passthrough to crypt
  process(parray_state, parray_init_addr, parray_init_data, crypt_parray_addr,
          crypt_ctext)
  begin
    if (parray_state = SUB_WRITE1) then
      parray_we <= '1';
      parray_addr <= parray_init_addr;
      parray_data_in <= crypt_ctext(63 downto 32);
    elsif (parray_state = SUB_WRITE2) then
      parray_we <= '1';
      parray_addr <= parray_init_addr;
      parray_data_in <= crypt_ctext(31 downto 0);
    elsif (parray_state = INIT) then
      parray_we <= '1';
      parray_addr <= parray_init_addr;
      parray_data_in <= parray_init_data;
    else
      parray_we <= '0';
      parray_addr <= crypt_parray_addr;
      parray_data_in <= (others => '0');
    end if;
  end process;

end;

