-- "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 crypt is
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 crypt;

architecture fishy of crypt is

  -- TODO: combine states, look at encrypt the whole time
  type CRYPT_STATES is (IDLE, START, CRUNCH1, CRUNCH2, FINAL1, FINAL2);
  signal crypt_state : CRYPT_STATES;

  signal count_up : bit;
  signal count_reset : bit;

  signal xL  : std_logic_vector(31 downto 0);
  signal xR  : std_logic_vector(31 downto 0);

  signal xL_next : std_logic_vector(31 downto 0);
  signal xR_next : std_logic_vector(31 downto 0);

begin
  
  -- the counter to feed parray_addr and keep track of where we are.
  process(clk, count_up, count_reset, parray_addr, crypt_state)
  begin
    if (clk'event AND clk = '1') then
      if (crypt_state = CRUNCH1) then
        if (count_up = '1') then
          parray_addr <= parray_addr + 1;
        else
          parray_addr <= parray_addr - 1;
        end if;
      elsif (parray_addr = 1 and count_up = '0') then
        parray_addr <= parray_addr - 1;
      elsif (parray_addr = 16 and count_up = '1') then
        parray_addr <= parray_addr + 1;
      end if;
    end if; -- clk
    if (count_reset = '1') then
      if (count_up = '1') then
        parray_addr <= b"00000"; -- 0
      else 
        parray_addr <= b"10001"; -- 17
      end if;
    end if;
  end process;

  -- this process does the combinational logic crunching we need
  process(crypt_state,parray_data,FxL_from_F,xL,xR,xL_next)
  begin
    if (crypt_state = CRUNCH1) then
      xL_next <= xL XOR parray_data;
      xL_to_F <= xL_next;
      xR_next <= xR;
    elsif (crypt_state = CRUNCH2) then
      xR_next <= xL;
      xL_next <= xR XOR FxL_from_F;
    elsif (crypt_state = FINAL1) then
      xR_next <= xL XOR parray_data;
      xL_next <= xR;
    elsif (crypt_state = FINAL2) then
      xR_next <= xR;
      xL_next <= xL XOR parray_data;
    else
      xR_next <= xR;
      xL_next <= xL;
    end if;
  end process;

  -- the process to control the state of the algorithm
  process(clk,ptext,encrypt,go,crypt_state,xR_next,xL_next,parray_addr,
          count_up,xL,xR)
  begin
    if (clk'event AND clk = '1') then
      xL <= xL_next;
      xR <= xR_next;
      if (crypt_state = IDLE) then
        if (go = '1') then
          if (encrypt = '1') then
	    count_up <= '1';
            count_reset <= '1';
          else
	    count_up <= '0';
	    count_reset <= '1';
          end if;
          crypt_state <= START;
          xL <= ptext(63 downto 32);
          xR <= ptext(31 downto 0);
          -- start fetching F(xL) and parray[0]/parray[17] right away
          done <= '0';
        else
          count_reset <= '1';
	  count_up <= '1';
          crypt_state <= IDLE;
          done <= '1';
	  ctext <= xL & xR;
        end if;
      elsif (crypt_state = START) then
        crypt_state <= CRUNCH1;
        count_reset <= '0';
        done <= '0';
      elsif (crypt_state = CRUNCH1) then
        crypt_state <= CRUNCH2;
      elsif (crypt_state = CRUNCH2) then
        if (count_up = '1') then
          if (parray_addr = 16) then -- 16 or 15?
            crypt_state <= FINAL1;
          else
            crypt_state <= CRUNCH1;
          end if;
        else
          if (parray_addr = 1) then -- 1 or 2?
            crypt_state <= FINAL1;
          else
            crypt_state <= CRUNCH1;
          end if;
        end if;
        done <= '0';
      elsif (crypt_state = FINAL1) then
        crypt_state <= FINAL2;
        done <= '0';
      elsif (crypt_state = FINAL2) then
        crypt_state <= IDLE;
        done <= '0';
      end if;
    end if; -- clk
  end process;

end;
