-- ============================================================================
--
-- Design unit      :   key_schedule_128 (AES 128-bit key schdeule)
--
-- File Name        :   key_schedule_128.vhd
--
-- Purpose          :   The module provides key expansion/subkey generaion
--                      functionality for AES algorithm by using ressource
--                      sharing architecture (32-bit datapath) to reduce
--                      required area
--
--
-- Depends on       :
--
--  - Package(s)    :   aes_definitions, aes_gf_2exp8_arith, tech_iface,
--                      key_schedule_iface, aux_funcs
--
--  - 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            :   requires external S-box stage and subkey memory
--
--
-- 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              2006-11-14  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.aes_gf_2exp8_arith.all;

use work.tech_iface.all;
use work.key_schedule_iface.all;
use work.aux_funcs.all;


entity key_schedule_128 is
		port (
			rst			: 	in	std_logic;						-- reset
			clk			:	in	std_logic;						-- clock
			key_i		:	in	key_schedule_128_in_type;		-- see package key_schedule_iface
			key_o		:	out	key_schedule_128_out_type;		-- see package key_schedule_iface
			sbox_i		:	in	sbox_stage_out_type;			-- see package tech_iface
			sbox_o		:	out	sbox_stage_in_type;				-- see package tech_iface
			skm_enc_o	:	out	subkey_memory_ks_in_type;		-- see package tech_iface
			skm_dec_o	:	out	subkey_memory_ks_in_type		-- see package tech_iface
		);
end key_schedule_128;


architecture behavioural of key_schedule_128 is


	-- control fsm, states: idle / load key / expand key
	type ctrl_fsm_t is (cf_idle, cf_load, cf_expand);

	type registers is record
		busy 		:	std_logic;			-- busy, key expansion in progress
		sk_slide	:	aes_state_t;		-- sliding subkey register
		key_id  	: 	key_id_t;			-- stored key id
		enc_sk_no	:	aes_round_no_t;		-- encryption subkey number
		dec_sk_no	:	aes_round_no_t;		-- decryption subkey number
		part_no		:	part_no_t;			-- number of 32-bit slice within subkey
		rconst		:	gf_2exp8_t;			-- round "constant"
		ctrl_fsm	:	ctrl_fsm_t;			-- control fsm
		din_sel		:	std_logic;			-- input data select (0 : input data, 1 : processing)
		sbox_sel	:	std_logic;			-- select S-box path
	end record;


	type variables is record
		sbox_dout	:	std_logic_vector(sbox_o.din'range);	-- input data to the S-box stage
	end record;


	signal	r, rin : registers;


begin


	-- combinatorial logic
	comb: process (rst, r, key_i, sbox_i)
		variable v : registers;
		variable t : variables;

	begin
		-- assign default values
		v := r;
--
--
--
		-- strobes
		v.sbox_sel := '0';


		-- count-up (down) subkey # / round constant / subkey / etc.
		if (r.busy = '1')
			then
				v.part_no := r.part_no + 1;

				if (r.part_no = "10")
					then
						v.sbox_sel := '1';
				end if;
		end if;


		-- count-up (down) subkey # / round constant / subkey / etc.
		if (r.sbox_sel = '1')
			then
				v.enc_sk_no := r.enc_sk_no + 1;
				v.dec_sk_no := r.dec_sk_no - 1;
				v.rconst := mul_gf_2exp8(r.rconst, X"02");
		end if;




		v.sk_slide(127 downto 32) := r.sk_slide(95 downto 0);

		if (r.din_sel = '0')
			then
				-- shift-in input data
				v.sk_slide(31 downto 0) := key_i.key;
				t.sbox_dout := rotl(key_i.key, 8);
			else
				if (r.sbox_sel = '1')
					then
						v.sk_slide(31 downto 0) := sbox_i.dout xor (r.rconst & X"000000") xor r.sk_slide(127 downto 96);
					else
						v.sk_slide(31 downto 0) := r.sk_slide(31 downto 0) xor r.sk_slide(127 downto 96);
				end if;
				t.sbox_dout := rotl(r.sk_slide(31 downto 0) xor r.sk_slide(127 downto 96), 8);
		end if;


		-- control fsm
		case r.ctrl_fsm is
			when cf_idle =>
				v.part_no := (others => '0');
				v.enc_sk_no := (others => '0');
				v.dec_sk_no := conv_std_logic_vector(AES_128_ROUNDS, skm_enc_o.rounds'length);
				v.rconst := X"01";

				-- handle key loading
				if (key_i.load_key = '1')
					then
						v.busy := '1';
						v.key_id := key_i.key_id;

						v.ctrl_fsm := cf_load;
				end if;

			when cf_load =>
				-- handle last input word
				if (r.part_no = "10")
					then
						v.din_sel := '1';			-- switch to processing data

						v.ctrl_fsm := cf_expand;
				end if;

			when cf_expand =>
				if (r.part_no = "11") and (r.enc_sk_no = AES_128_ROUNDS)
					then
						v.din_sel := '0';
						v.busy := '0';

						v.ctrl_fsm := cf_idle;
				end if;


			when others =>
				v.ctrl_fsm := cf_idle;
		end case;

--
--
--
		-- reset handling
		if (rst = '1')
			then
				v.ctrl_fsm := cf_idle;
				v.din_sel := '0';
				v.sbox_sel := '0';
				v.busy := '0';
				v.key_id := (others => '0');
				v.enc_sk_no := (others => '0');
				v.dec_sk_no := conv_std_logic_vector(AES_128_ROUNDS, skm_enc_o.rounds'length);
				v.part_no := (others => '0');
		end if;
--
--
--
		-- update registers
		rin <= v;
--
--
--
		-- drive outputs
		key_o.busy <= r.busy or (not sbox_i.ready);

		sbox_o.act <= r.busy;
		sbox_o.din <= t.sbox_dout;
		sbox_o.ed_mode <= '0';

		skm_enc_o.subkey_no <= r.enc_sk_no;
		skm_enc_o.part_no <= r.part_no;
		skm_enc_o.subkey <= r.sk_slide(31 downto 0);
		skm_enc_o.rounds <= conv_std_logic_vector(AES_128_ROUNDS, skm_enc_o.rounds'length);
		skm_enc_o.key_id <= r.key_id;
		skm_enc_o.write_en <= r.busy;

		skm_dec_o.subkey_no <= r.dec_sk_no;
		skm_dec_o.part_no <= r.part_no;
		skm_dec_o.subkey <= r.sk_slide(31 downto 0);
		skm_dec_o.rounds <= conv_std_logic_vector(AES_128_ROUNDS, skm_enc_o.rounds'length);
		skm_dec_o.key_id <= r.key_id;
		skm_dec_o.write_en <= r.busy;

	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;
