-- ============================================================================
--
-- Design unit      :   cbc_mode_encryption (CBC-mode encryption)
--
-- File Name        :   cbc_mode_encryption.vhd
--
-- Purpose          :   The module provides CBC-mode encryption for AES
--                      using 32-bit datapath
--
--
-- Depends on       :
--
--  - Package(s)    :   aes_defnitions, tech_iface, engine_iface, cbc_mode_iface
--
--  - Module(s)     :   -
--
--
-- Dev. Platform    :   Xilinx ISE 8.1i SP3 / Win32 (W2k SP4)
--
--  - Simulator(s)  :   Modelsim XE III 6.0d
--
--  - Synthesis     :   Xilinx XST
--
--  - Place & Route :   Xilinx PAR
--
--
-- Errors           :   None known
--
--
-- Limitations      :   None known
--
--
-- Notes            :   Version for implementation using 32-bit datapath
--
--
-- Author(s)        :   A. Freund (af) <a.freund@dsi-it.de>
--                      Digitale Signalverabeitung & Informationstechnik GmbH,
--                      Bremen, Germany
--
--
-- ============================================================================
--
-- Revision List:
-- Version      Author(s)       Date        Changes
--
-- v0.00        af              2007-01-31  initial version
--
-- see SVN logs
--
-- ============================================================================
--
-- Naming conventions:
--
--  - coding style: two-process method (Gaisler-style)
--  - active-low signals are indicated by appending "_n"
--  - constants are in UPPERCASE
--  - name parts are separated by "_"
--
-- ============================================================================


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


use work.aes_definitions.all;
use work.tech_iface.all;
use work.engine_iface.all;
use work.cbc_mode_iface.all;


entity cbc_mode_encryption is
	port (
		rst		: 	in	std_logic;						-- reset
		clk		:	in	std_logic;						-- clock
		mode_i	:	in	cbc_mode_encryption_in_type;	-- see package cbc_mode_iface
		mode_o	:	out	cbc_mode_encryption_out_type;	-- see package cbc_mode_iface
		enc_i	:	in	encryption_engine_out_type;		-- see package engine_iface
		enc_o	:	out	encryption_engine_in_type		-- see package engine_iface
	);
end cbc_mode_encryption;


architecture behavioural of cbc_mode_encryption is


	-- control fsm, states: idle / load IV / load input data / wait while encrypting / output data
	type ctrl_fsm_t is (cf_idle, cf_load_iv, cf_load_data, cf_wait_enc, cf_out_data);

	type registers is record
		ctrl_fsm		:	ctrl_fsm_t;			-- control fsm
		busy			:	std_logic;			-- busy, encryption in progress
		part_no			:	part_no_t;			-- partial word number
		cbc_fb_shr		:	aes_state_t;		-- CBC-mode feedback shift-register
		fb_shr_src		:	std_logic;			-- data source for fb-shr: ciphertext (0) / IV @ din (1)
		fb_shr_shift	:	std_logic;			-- shift-data into / out of fb-shr
		key_id			:	key_id_t;			-- stored key ID
	end record;


	type variables is record
		dummy		:	std_logic;
	end record;


	signal	r, rin : registers;


begin


	-- combinatorial logic
	comb: process (rst, r, mode_i, enc_i)
		variable v : registers;
		variable t : variables;
	begin

		-- assign default values
		v := r;
--
--
--

		if (r.fb_shr_shift = '1') or (enc_i.valid = '1')
			then
				v.part_no := r.part_no + 1;
		end if;


		-- control fsm
		case r.ctrl_fsm is
			-- wait for start / IV loading
			when cf_idle =>
				-- handle IV loading
				if (mode_i.load_iv = '1')
					then
						v.busy := '1';
						v.key_id := mode_i.key_id;
						v.fb_shr_shift := '1';
						v.part_no := "01";

						v.ctrl_fsm := cf_load_iv;
				-- handle encryption start
				elsif (mode_i.start = '1')
					then
						v.fb_shr_src := '0';		-- use ciphertext as source
						v.fb_shr_shift := '1';
						v.part_no := "01";

						v.ctrl_fsm := cf_load_data;
				end if;

			-- load iv
			when cf_load_iv =>
				-- handle last input data word
				if (r.part_no = "11")
					then
						v.busy := '0';
						v.fb_shr_shift := '0';		-- partial number is hold at "00"

						v.ctrl_fsm := cf_idle;
				end if;

			-- load input data
			when cf_load_data =>
				-- handle last input data word
				if (r.part_no = "11")
					then
						v.fb_shr_shift := '0';		-- partial number is hold at "00"

						v.ctrl_fsm := cf_wait_enc;
				end if;

			-- wait while encrypting
			when cf_wait_enc =>
				if (enc_i.valid = '1')
					then
						v.part_no := "01";
						v.fb_shr_shift := '1';

						v.ctrl_fsm := cf_out_data;
				end if;

			-- ouput data
			when cf_out_data =>
				-- handle last output data word
				if (r.part_no = "11")
					then
						v.fb_shr_src := '1';
						v.fb_shr_shift := '0';

						v.ctrl_fsm := cf_idle;
				end if;


			when others =>
				v.ctrl_fsm := cf_idle;

		end case;


	-- CBC-mode feedback register operation (4x32-bit shift-register)
	if (mode_i.load_iv = '1') or (enc_i.valid = '1') or (r.fb_shr_shift = '1')
		then
			if (r.fb_shr_src = '1')
				then
					-- handle IV loading
					v.cbc_fb_shr(31 downto 0) := mode_i.din;
				else
					v.cbc_fb_shr(31 downto 0) := enc_i.cipher;
			end if;
	end if;

	if (mode_i.start = '1') or (r.fb_shr_shift = '1')
		then
			v.cbc_fb_shr(127 downto 32) := r.cbc_fb_shr(95 downto 0);
	end if;

--
--
--
		-- reset handling
		if (rst = '1')
			then
				v.fb_shr_shift := '0';
				v.fb_shr_src := '1';
				v.part_no := "00";
				v.busy := '0';
				v.key_id := (others => '0');

				v.ctrl_fsm := cf_idle;

		end if;
--
--
--
		-- update registers
		rin <= v;
--
--
--
		-- drive outputs
		enc_o.start		<=	mode_i.start;
		enc_o.plain		<=	mode_i.din xor r.cbc_fb_shr(127 downto 96);
		enc_o.key_id	<=	r.key_id;

		mode_o.valid	<=	enc_i.valid;
		mode_o.busy		<=	enc_i.busy or r.busy;
		mode_o.dout		<=	enc_i.cipher;


	end process;


	-- registers
	regs: process (clk, rst)
	begin
		if rising_edge(clk)
			then
				r <= rin;
		end if;

		-- async. reset
		if (rst = '1')
			then
		end if;
	end process;


end behavioural;
