-- "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 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;
  
  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 sboxes_control;

architecture fishy of sboxes_control is

  type SBOXES_STATES is (PASS, INIT, SUB, SUB_WRITE1, SUB_WRITE2);
  signal sboxes_state  : SBOXES_STATES;  

  signal zero_block : std_logic_vector(63 downto 0);

  signal sboxes_counter : std_logic_vector(9 downto 0);
  
begin
  
  -- process to control sboxes initialization
  process(clk, sboxes_state, init_sboxes, crypt_done, sbox0_init_addr,
          sbox1_init_addr, sbox2_init_addr, sbox3_init_addr, sub_sboxes,
          sboxes_counter)
    variable just_started : std_logic;
  begin
    if (clk'event AND clk = '1') then
      sub_sboxes_done <= '0';
      if (sboxes_state = SUB) then
        if (crypt_done = '1' AND just_started = '0') then
          sboxes_state <= SUB_WRITE1;
          --sboxes_init_addr - don't modify it
          sboxes_crypt_go <= '0';
        else
          just_started := '0';
          sboxes_state <= SUB;
          --sboxes_init_addr - don't modify it
          sboxes_crypt_go <= '0';
        end if;
      elsif (sboxes_state = SUB_WRITE1) then
        sboxes_state <= SUB_WRITE2;
        sbox0_init_addr <= sbox0_init_addr + 1;
        sbox1_init_addr <= sbox1_init_addr + 1;
        sbox2_init_addr <= sbox2_init_addr + 1;
        sbox3_init_addr <= sbox3_init_addr + 1;        
        sboxes_crypt_go <= '0';
      elsif (sboxes_state = SUB_WRITE2) then
        if (sboxes_counter = 511) then
          sboxes_state <= PASS;
          sboxes_crypt_go <= '0';
        else
	  sboxes_state <= SUB;
          sboxes_crypt_go <= '1';
        end if;
        sbox0_init_addr <= sbox0_init_addr + 1;
        sbox1_init_addr <= sbox1_init_addr + 1;
        sbox2_init_addr <= sbox2_init_addr + 1;
        sbox3_init_addr <= sbox3_init_addr + 1;        
        just_started := '1';            -- so we don't write two in a row!
      elsif (sub_sboxes = '1') then
        sboxes_state <= SUB;
        sbox0_init_addr <= (others => '0');
        sbox1_init_addr <= (others => '0');
        sbox2_init_addr <= (others => '0');
        sbox3_init_addr <= (others => '0');        
        sboxes_crypt_go <= '1';  
        just_started := '1';
      elsif (sboxes_state = INIT) then
        if (sbox0_init_addr = 255) then
          sboxes_state <= PASS;
        else
          sboxes_state <= INIT;
        end if;
        sbox0_init_addr <= sbox0_init_addr + 1;
        sbox1_init_addr <= sbox1_init_addr + 1;
        sbox2_init_addr <= sbox2_init_addr + 1;
        sbox3_init_addr <= sbox3_init_addr + 1;        
        sboxes_crypt_go <= '0';
      elsif (init_sboxes = '1') then
	sboxes_state <= INIT;
        sbox0_init_addr <= (others => '0');
        sbox1_init_addr <= (others => '0');
        sbox2_init_addr <= (others => '0');
        sbox3_init_addr <= (others => '0');        
        sboxes_crypt_go <= '0';
      else -- if we're not initializing, just pass-through
        sboxes_state <= PASS;
        sbox0_init_addr <= (others => '0');
        sbox1_init_addr <= (others => '0');
        sbox2_init_addr <= (others => '0');
        sbox3_init_addr <= (others => '0');        
        sboxes_crypt_go <= '0';
        sub_sboxes_done <= '1';
      end if;
    end if;
  end process;

  -- process to count the number of writes we've done so far
  process(clk, sboxes_state,sboxes_counter)
  begin
    if (clk'event and clk = '1') then
      if (sboxes_state = SUB_WRITE2) then
        sboxes_counter <= sboxes_counter + 1;
      elsif (sboxes_state = PASS) then
        sboxes_counter <= (others => '0');
      end if;
    end if;
  end process;
  
  -- process to control the zero_block used in initalizations
  process(clk, sboxes_state, zero_block_grab, zero_block, crypt_ctext,
          sub_sboxes, sboxes_zero_block)
  begin
    if (clk'event AND clk = '1') then
      if (zero_block_grab = '1') then
        zero_block <= sboxes_zero_block;
      elsif (sboxes_state = SUB_WRITE1) then
        zero_block <= crypt_ctext;
      elsif (sub_sboxes = '1') then
        zero_block <= (others => '0');
      end if;
    end if;
    sboxes_crypt_ptext <= zero_block;
  end process;

  F_sbox0_data <= sbox0_data_out; 
  F_sbox1_data <= sbox1_data_out;
  F_sbox2_data <= sbox2_data_out; 
  F_sbox3_data <= sbox3_data_out;

  -- process to allow sboxes init or passthrough to crypt
  process(sboxes_state, sbox0_init_addr, sbox1_init_addr, sbox2_init_addr,
          sbox3_init_addr, sbox0_init_data, sbox1_init_data, sbox2_init_data,
          sbox3_init_data, F_sbox0_addr, F_sbox1_addr, F_sbox2_addr,
          F_sbox3_addr, crypt_ctext, sboxes_counter)
  begin
    if (sboxes_state = SUB_WRITE1) then
      if (sboxes_counter < 128) then
        sbox0_we <= '1';
      elsif (sboxes_counter < 256) then
        sbox1_we <= '1';
      elsif (sboxes_counter < 384) then
        sbox2_we <= '1';
      elsif (sboxes_counter < 512) then
        sbox3_we <= '1';      
      end if;
      sbox0_addr <= sbox0_init_addr;
      sbox1_addr <= sbox1_init_addr;
      sbox2_addr <= sbox2_init_addr;
      sbox3_addr <= sbox3_init_addr;
      sbox0_data_in <= crypt_ctext(63 downto 32);
      sbox1_data_in <= crypt_ctext(63 downto 32);
      sbox2_data_in <= crypt_ctext(63 downto 32);
      sbox3_data_in <= crypt_ctext(63 downto 32);
    elsif (sboxes_state = SUB_WRITE2) then
      if (sboxes_counter < 128) then
        sbox0_we <= '1';
      elsif (sboxes_counter < 256) then
        sbox1_we <= '1';
      elsif (sboxes_counter < 384) then
        sbox2_we <= '1';
      elsif (sboxes_counter < 512) then
        sbox3_we <= '1';      
      end if;
      sbox0_addr <= sbox0_init_addr;
      sbox1_addr <= sbox1_init_addr;
      sbox2_addr <= sbox2_init_addr;
      sbox3_addr <= sbox3_init_addr;
      sbox0_data_in <= crypt_ctext(31 downto 0);
      sbox1_data_in <= crypt_ctext(31 downto 0);
      sbox2_data_in <= crypt_ctext(31 downto 0);
      sbox3_data_in <= crypt_ctext(31 downto 0);
    elsif (sboxes_state = INIT) then
      sbox0_we <= '1';
      sbox1_we <= '1';
      sbox2_we <= '1';
      sbox3_we <= '1';      
      sbox0_addr <= sbox0_init_addr;
      sbox1_addr <= sbox1_init_addr;
      sbox2_addr <= sbox2_init_addr;
      sbox3_addr <= sbox3_init_addr;
      sbox0_data_in <= sbox0_init_data;
      sbox1_data_in <= sbox1_init_data;
      sbox2_data_in <= sbox2_init_data;
      sbox3_data_in <= sbox3_init_data;
    else
      sbox0_we <= '0';
      sbox1_we <= '0';
      sbox2_we <= '0';
      sbox3_we <= '0';      
      sbox0_addr <= F_sbox0_addr;
      sbox1_addr <= F_sbox1_addr;
      sbox2_addr <= F_sbox2_addr;
      sbox3_addr <= F_sbox3_addr;
      sbox0_data_in <= (others => '0');
      sbox1_data_in <= (others => '0');
      sbox2_data_in <= (others => '0');
      sbox3_data_in <= (others => '0');
    end if;
  end process;

end;

