-- "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 blowfish is
port (
  clk  : in std_logic;

  key      : in  std_logic_vector(447 downto 0);
  key_size : in  std_logic_vector(3 downto 0);  -- key size in 32-bit chunks

  data_in  : in  std_logic_vector(63 downto 0);
  data_out : out std_logic_vector(63 downto 0);

  new_key  : in bit;
  new_data : in bit;

  encrypt  : in bit; -- are we encrypting (high) or decrypting (low)?

  ready    : out bit  -- ready to receive a new key or new data. Alos means
                      -- that the output is valid for the last blowfish round.
);
end blowfish;

architecture fishy of blowfish is

  component sbox0_init
  port (
    address : in     std_logic_vector(7 downto 0);
    data    : inout std_logic_vector(31 downto 0)
  );
  end component;
  
  component sbox1_init
  port (
    address : in     std_logic_vector(7 downto 0);
    data    : inout std_logic_vector(31 downto 0)
  );
  end component;
  
  component sbox2_init
  port (
    address : in     std_logic_vector(7 downto 0);
    data    : inout std_logic_vector(31 downto 0)
  );
  end component;
  
  component sbox3_init
  port (
    address : in     std_logic_vector(7 downto 0);
    data    : inout std_logic_vector(31 downto 0)
  );
  end component;
  
  component parray_init
  port (
    keychunk : in  std_logic_vector(31 downto 0);
    address  : in  std_logic_vector(4 downto 0);
    data     : out std_logic_vector(31 downto 0)
  );
  end component;
  
  component parray
  port (
    clk : in std_logic;
  
    address  : in  std_logic_vector(4 downto 0);
    data_in  : in  std_logic_vector(31 downto 0);
    data_out : out std_logic_vector(31 downto 0);
  
    we : in bit
  );
  end component;
  
  component sbox
  port (
    clk : in std_logic;
  
    address : in     std_logic_vector(7 downto 0);
    data_in  : in  std_logic_vector(31 downto 0);
    data_out : out std_logic_vector(31 downto 0);
  
    we : in bit
  );
  end component;
  
  component crypt
  port (
    clk : in std_logic;
  
    parray_addr : buffer std_logic_vector(4 downto 0);
    parray_data : in     std_logic_vector(31 downto 0);
  
    ptext : in  std_logic_vector(63 downto 0);
    ctext : out std_logic_vector(63 downto 0);
  
    xL_to_F    : out std_logic_vector(31 downto 0); -- for the F(xL) function
    FxL_from_F : in  std_logic_vector(31 downto 0);
  
    encrypt : in bit; -- are we encrypting or decrypting?
  
    go   : in  bit;
    done : out bit
  );
  end component;
  
  component F
  port (
    clk   : in std_logic;
  
    sbox0_addr : out std_logic_vector(7 downto 0);
    sbox1_addr : out std_logic_vector(7 downto 0);
    sbox2_addr : out std_logic_vector(7 downto 0);
    sbox3_addr : out std_logic_vector(7 downto 0);
  
    sbox0_data : in std_logic_vector(31 downto 0);
    sbox1_data : in std_logic_vector(31 downto 0);
    sbox2_data : in std_logic_vector(31 downto 0);
    sbox3_data : in std_logic_vector(31 downto 0);
  
    xL   : in std_logic_vector(31 downto 0);
  
    FxL  : out std_logic_vector(31 downto 0)
  );
  end component;

  component keychunker is
  port (
    key       : in  std_logic_vector(447 downto 0);
    key_size  : in  std_logic_vector(3 downto 0);  -- key size in 32-bit chunks

    keychunk_blob : out std_logic_vector(447 downto 0)
  );
  end component;

  component 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;              -- we're done doing a sub
    
    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 component;

  component sboxes_control is
  port (
    clk  : in std_logic;
  
    init_sboxes : in bit; -- tells us to initialize the sboxes
    sub_sboxes  : in bit; -- tells us to run the zero_block through

    sub_sboxes_done : out bit;              -- we're done doing a sub
    
    sboxes_zero_block : in std_logic_vector(63 downto 0); -- to sboxes_control
    zero_block_grab   : in bit;
    
    sbox0_init_addr  : buffer std_logic_vector(7 downto 0);
    sbox1_init_addr  : buffer std_logic_vector(7 downto 0);
    sbox2_init_addr  : buffer std_logic_vector(7 downto 0);
    sbox3_init_addr  : buffer std_logic_vector(7 downto 0);
  
    sbox0_init_data  : in  std_logic_vector(31 downto 0);
    sbox1_init_data  : in  std_logic_vector(31 downto 0);
    sbox2_init_data  : in  std_logic_vector(31 downto 0);
    sbox3_init_data  : in  std_logic_vector(31 downto 0);
  
    sbox0_addr     : out std_logic_vector(7 downto 0);
    sbox1_addr     : out std_logic_vector(7 downto 0);
    sbox2_addr     : out std_logic_vector(7 downto 0);
    sbox3_addr     : out std_logic_vector(7 downto 0);
  
    sbox0_data_in  : out std_logic_vector(31 downto 0);
    sbox1_data_in  : out std_logic_vector(31 downto 0);
    sbox2_data_in  : out std_logic_vector(31 downto 0);
    sbox3_data_in  : out std_logic_vector(31 downto 0);
  
    sbox0_data_out : in  std_logic_vector(31 downto 0);
    sbox1_data_out : in  std_logic_vector(31 downto 0);
    sbox2_data_out : in  std_logic_vector(31 downto 0);
    sbox3_data_out : in  std_logic_vector(31 downto 0);
  
    sbox0_we   : out bit;
    sbox1_we   : out bit;      
    sbox2_we   : out bit;
    sbox3_we   : out bit;
    
    sboxes_crypt_go : out bit;
    sboxes_crypt_ptext : out std_logic_vector(63 downto 0);
    
    crypt_ctext : in  std_logic_vector(63 downto 0);
  
    F_sbox0_addr : in std_logic_vector(7 downto 0);
    F_sbox1_addr : in std_logic_vector(7 downto 0);
    F_sbox2_addr : in std_logic_vector(7 downto 0);
    F_sbox3_addr : in std_logic_vector(7 downto 0);
  
    F_sbox0_data : out std_logic_vector(31 downto 0);
    F_sbox1_data : out std_logic_vector(31 downto 0);
    F_sbox2_data : out std_logic_vector(31 downto 0);
    F_sbox3_data : out std_logic_vector(31 downto 0);
  
    crypt_done : in bit
    
  );
  end component;

  
-- sbox and parray and their respective init constant blocks

  signal sbox0_init_data : std_logic_vector(31 downto 0);
  signal sbox1_init_data : std_logic_vector(31 downto 0);
  signal sbox2_init_data : std_logic_vector(31 downto 0);
  signal sbox3_init_data : std_logic_vector(31 downto 0);
 
  signal sbox0_init_addr : std_logic_vector(7 downto 0);
  signal sbox1_init_addr : std_logic_vector(7 downto 0);
  signal sbox2_init_addr : std_logic_vector(7 downto 0);
  signal sbox3_init_addr : std_logic_vector(7 downto 0);
 
  signal parray_init_data : std_logic_vector(31 downto 0);
  signal parray_init_addr : std_logic_vector(4 downto 0);
  signal parray_init_chunk : std_logic_vector(31 downto 0);
 
  signal sbox0_data_in : std_logic_vector(31 downto 0);
  signal sbox1_data_in : std_logic_vector(31 downto 0);
  signal sbox2_data_in : std_logic_vector(31 downto 0);
  signal sbox3_data_in : std_logic_vector(31 downto 0);

  signal sbox0_data_out : std_logic_vector(31 downto 0);
  signal sbox1_data_out : std_logic_vector(31 downto 0);
  signal sbox2_data_out : std_logic_vector(31 downto 0);
  signal sbox3_data_out : std_logic_vector(31 downto 0);
  
  signal sbox0_addr : std_logic_vector(7 downto 0);
  signal sbox1_addr : std_logic_vector(7 downto 0);
  signal sbox2_addr : std_logic_vector(7 downto 0);
  signal sbox3_addr : std_logic_vector(7 downto 0);
 
  signal sbox0_we : bit;
  signal sbox1_we : bit;
  signal sbox2_we : bit;
  signal sbox3_we : bit;
 
  signal parray_data_in  : std_logic_vector(31 downto 0);
  signal parray_data_out : std_logic_vector(31 downto 0);
  signal parray_addr : std_logic_vector(4 downto 0);
  signal parray_we   : bit;
 
-- signals going to and from crypt and the F function

  signal crypt_ptext : std_logic_vector(63 downto 0);
  signal crypt_ctext : std_logic_vector(63 downto 0); 
 
  signal crypt_parray_data : std_logic_vector(31 downto 0);
  signal crypt_parray_addr : std_logic_vector(4 downto 0);

  signal xL_to_F    : std_logic_vector(31 downto 0);
  signal FxL_from_F : std_logic_vector(31 downto 0);  
 
  signal crypt_encrypt  : bit;
  signal crypt_go       : bit;
  signal crypt_done     : bit;

  signal F_sbox0_data : std_logic_vector(31 downto 0);
  signal F_sbox1_data : std_logic_vector(31 downto 0);
  signal F_sbox2_data : std_logic_vector(31 downto 0);
  signal F_sbox3_data : std_logic_vector(31 downto 0);
 
  signal F_sbox0_addr : std_logic_vector(7 downto 0);
  signal F_sbox1_addr : std_logic_vector(7 downto 0);
  signal F_sbox2_addr : std_logic_vector(7 downto 0);
  signal F_sbox3_addr : std_logic_vector(7 downto 0);  

-- signals for the state machines and other misc control signals

  signal init_parray : bit; -- tell the parray to start initializing
  signal init_sboxes : bit; -- tell the sboxes to start initializing

  signal sub_parray : bit; -- tell the parray to start init stage 2
  signal sub_sboxes : bit; -- tell the sboxes to start init stage 2

  signal sub_parray_done : bit;
  signal sub_sboxes_done : bit;
  
  signal parray_crypt_go : bit; -- the go signal for the init stage 2 fsms
  signal sboxes_crypt_go : bit; -- the go signal for the init stage 2 fsms

  signal parray_crypt_ptext : std_logic_vector(63 downto 0);
  signal sboxes_crypt_ptext : std_logic_vector(63 downto 0);

  type CONTROL_STATES is (S_NORMAL, S_INIT, S_INIT_START, S_SUB_PARRAY_START,
                          S_SUB_SBOXES_START, S_SUB_PARRAY, S_SUB_SBOXES);
  signal control_state : CONTROL_STATES;

  signal sboxes_zero_block : std_logic_vector(63 downto 0);
  signal zero_block_grab : bit;
  
  signal keychunk_blob : std_logic_vector(447 downto 0);


begin

  -- hierarchical cell instansiations
  SB0_INIT: sbox0_init port map(sbox0_init_addr, sbox0_init_data);
  SB1_INIT: sbox1_init port map(sbox1_init_addr, sbox1_init_data);
  SB2_INIT: sbox2_init port map(sbox2_init_addr, sbox2_init_data);
  SB3_INIT: sbox3_init port map(sbox3_init_addr, sbox3_init_data);
 
  PARR_INIT: parray_init port map(parray_init_chunk, parray_init_addr,
                                  parray_init_data); 
 
  SB0: sbox port map(clk, sbox0_addr, sbox0_data_in, sbox0_data_out, sbox0_we);
  SB1: sbox port map(clk, sbox1_addr, sbox1_data_in, sbox1_data_out, sbox1_we);
  SB2: sbox port map(clk, sbox2_addr, sbox2_data_in, sbox2_data_out, sbox2_we);
  SB3: sbox port map(clk, sbox3_addr, sbox3_data_in, sbox3_data_out, sbox3_we);
 
  PARR: parray port map(clk, parray_addr, parray_data_in, parray_data_out,
                        parray_we); 
 
  CRY: crypt port map(clk, crypt_parray_addr, crypt_parray_data, crypt_ptext,
		      crypt_ctext, xL_to_F, FxL_from_F, crypt_encrypt,
	              crypt_go, crypt_done);
 
  CRY_F: F port map(clk, F_sbox0_addr, F_sbox1_addr, F_sbox2_addr,
	            F_sbox3_addr, F_sbox0_data, F_sbox1_data, F_sbox2_data,
	            F_sbox3_data, xL_to_F, FxL_from_F);

  KCHUNK: keychunker port map(key, key_size, keychunk_blob);

  PARRCTRL: parray_control port map(clk,init_parray,sub_parray,sub_parray_done,
                                    sboxes_zero_block, parray_init_addr,
                                    parray_init_chunk, parray_init_data,
                                    parray_addr, parray_data_in,
                                    parray_data_out,parray_we,
                                    parray_crypt_go, parray_crypt_ptext,
                                    crypt_ctext, crypt_parray_addr,
                                    crypt_parray_data, crypt_done,
                                    keychunk_blob);

  SBCTRL: sboxes_control port map(clk,init_sboxes,sub_sboxes,sub_sboxes_done,
                                  sboxes_zero_block,
                                  zero_block_grab,sbox0_init_addr,
                                  sbox1_init_addr,sbox2_init_addr,
                                  sbox3_init_addr,sbox0_init_data,
                                  sbox1_init_data,sbox2_init_data,
                                  sbox3_init_data,sbox0_addr,
                                  sbox1_addr,sbox2_addr,sbox3_addr,
                                  sbox0_data_in,sbox1_data_in,sbox2_data_in,
                                  sbox3_data_in,sbox0_data_out,sbox1_data_out,
                                  sbox2_data_out,sbox3_data_out,sbox0_we,
                                  sbox1_we,sbox2_we,sbox3_we,sboxes_crypt_go,
                                  sboxes_crypt_ptext,crypt_ctext,F_sbox0_addr,
                                  F_sbox1_addr,F_sbox2_addr,F_sbox3_addr,
                                  F_sbox0_data,F_sbox1_data,F_sbox2_data,
                                  F_sbox3_data,crypt_done);

  crypt_go <= new_data OR parray_crypt_go OR sboxes_crypt_go;
  crypt_encrypt <= parray_crypt_go OR sboxes_crypt_go OR encrypt;
 
  crypt_ptext <= parray_crypt_ptext when (parray_crypt_go = '1') else
                 sboxes_crypt_ptext when (sboxes_crypt_go = '1') else
                 data_in;

  data_out <= crypt_ctext;
  
--  type CONTROL_STATES is (NORMAL, INIT, SUB_PARRAY, SUB_SBOXES);

  process(crypt_done, control_state)
  begin
    if (control_state = S_NORMAL) then
      ready <= crypt_done;
    else
      ready <= '0';    
    end if;
  end process;

  process(control_state)
  begin
    init_parray <= '0';
    init_sboxes <= '0';
    sub_parray <= '0';
    sub_sboxes <= '0';
    zero_block_grab <= '0';
    if (control_state = S_INIT_START) then
      init_parray <= '1';
      init_sboxes <= '1';
    end if;
    if (control_state = S_SUB_PARRAY_START) then
      sub_parray <= '1';
    end if;
    if (control_state = S_SUB_SBOXES_START) then
      sub_sboxes <= '1';
      zero_block_grab <= '1';
    end if;
  end process;
  
  process(clk, control_state, new_key, sub_parray_done, sub_sboxes_done)
  begin
    if (clk'event and clk = '1') then
      if (control_state = S_INIT_START) then
        control_state <= S_INIT;  
      elsif (control_state = S_SUB_PARRAY_START) then
        control_state <= S_SUB_PARRAY;
      elsif (control_state = S_SUB_SBOXES_START) then
        control_state <= S_SUB_SBOXES;
      elsif (control_state = S_NORMAL) then
        if (new_key = '1') then
          control_state <= S_INIT_START;
        else
          control_state <= S_NORMAL;
        end if;
      elsif (control_state = S_INIT) then
        if (sub_sboxes_done = '1' ) then
          control_state <= S_SUB_PARRAY_START;
        else
          control_state <= S_INIT;
        end if;
      elsif (control_state = S_SUB_PARRAY) then
        if (sub_parray_done = '1' ) then
          control_state <= S_SUB_SBOXES_START;
        else
          control_state <= S_SUB_PARRAY;  
        end if;
      elsif (control_state = S_SUB_SBOXES) then
        if (sub_sboxes_done = '1' ) then
          control_state <= S_NORMAL;
        else
          control_state <= S_SUB_SBOXES;
        end if;
      end if;
    end if;
  end process;

end;
