----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    11:30:30 08/27/2006 
-- Design Name: 
-- Module Name:    ac97_test - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

use work.tech_iface.all;
use work.key_schedule_iface.all;
use work.engine_iface.all;

use work.ecb_mode_iface.all;
use work.ecb_core_iface.all;

use work.cbc_mode_iface.all;
use work.cbc_core_iface.all;

use work.cpu_pkg.all;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;

entity messe_demo is
	Port (
			sys_rst_n_in	: in std_logic;
			sys_clk_in		: in std_logic;
			sys_btn			: in std_logic_vector(4 downto 0);
--			sys_dip			: in std_logic_vector(7 downto 0);
			sys_led			: out std_logic_vector(8 downto 0);
			sys_rx			: in std_logic;
			sys_tx			: out std_logic;
			sys_lcd_d		: inout std_logic_vector(3 downto 0);
			sys_lcd_e		: out std_logic;
			sys_lcd_rs		: out std_logic;
			sys_lcd_rw		: out std_logic;
			ac97_bit_clk	: in  STD_LOGIC;
			ac97_sdata_in	: in  STD_LOGIC;
			ac97_reset_n	: out STD_LOGIC;
			ac97_sdata_out	: out STD_LOGIC;
			ac97_sync		: out STD_LOGIC
		);
end messe_demo;

architecture Behavioral of messe_demo is

	constant fa : REAL := 48.0E3;
	
	type stereo_t is (left, right);
	type pcm_data_t is array (stereo_t) of signed(17 downto 0);
	type stereo_bits_t is array (stereo_t) of std_logic;
	type pcm_block_t is array (0 to 7) of unsigned(15 downto 0);
	signal plain_block_in : pcm_block_t;
	signal ecb_block_out : pcm_block_t;
	signal cbc_block_out : pcm_block_t;
	signal encrypt_audio : std_logic;
	
------------------------------------------------------------------
	COMPONENT singleshot
	GENERIC (mode : integer);
	PORT(
		rst : IN std_logic;
		clk : IN std_logic;
		input : IN std_logic;          
		output : OUT std_logic
		);
	END COMPONENT;

	COMPONENT ac_io
    Port (
			rst				: in  std_logic;
			clk				: in  std_logic;
			ready			: out std_logic;
			sync_strobe		: out unsigned(0 to 2);
			slot_valid		: out unsigned(0 to 12);
			stat_addr		: out unsigned (19 downto 0);
			stat_data		: out unsigned (19 downto 0);
			rx_pcm_addr		: in  unsigned (3 downto 0);
			rx_pcm_data		: out unsigned (19 downto 0);
			cmd_addr 		: in  unsigned(19 downto 0);
			cmd_data		: in  unsigned(19 downto 0);
			cmd_we			: in  std_logic;
			tx_pcm_addr		: in  unsigned(3 downto 0);
			tx_pcm_data		: in  unsigned(19 downto 0);
			tx_pcm_we		: in  std_logic;
			ac_sdata_in		: in  std_logic;
			ac_bit_clk		: in  std_logic;          
			ac_reset_n		: out std_logic;
			ac_sdata_out	: out std_logic;
			ac_ssync		: out std_logic
			);
	END COMPONENT;

	SIGNAL sync_strobe		: unsigned(0 to 2);
	SIGNAL slot_valid		: unsigned(0 to 12);
	SIGNAL cmd_we			: std_logic;
	SIGNAL cmd_addr			: unsigned(19 downto 0);
	SIGNAL cmd_data			: unsigned(19 downto 0);
	SIGNAL stat_addr		: unsigned(19 downto 0);
	SIGNAL stat_data		: unsigned(19 downto 0);
	SIGNAL tx_pcm_addr		: unsigned(3 downto 0);
	SIGNAL tx_pcm_data		: unsigned(19 downto 0);
	SIGNAL rx_pcm_addr		: unsigned(3 downto 0);
	SIGNAL tx_pcm_we		: std_logic;
	SIGNAL rx_pcm_data		: unsigned(19 downto 0);

	COMPONENT cpu_embedded
	Port (
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		ce			: in  STD_LOGIC;
		int_in		: in  STD_LOGIC;
		int_ack		: out  STD_LOGIC;
		xmem_we		: out STD_LOGIC;
		xmem_re		: out STD_LOGIC;
		xmem_din	: in  unsigned (DMEM_DATA_WIDTH-1 downto 0);
		xmem_dout	: out unsigned (DMEM_DATA_WIDTH-1 downto 0);
		xmem_addr	: out unsigned (DMEM_ADDR_WIDTH-1 downto 0);
		io_sel		: out STD_LOGIC
	);
	END COMPONENT;

	signal cpu_ce  : std_logic;
	signal cpu_int_in : std_logic;
	signal cpu_int_ack : std_logic;
	signal cpu_din : dmem_data_t;
	signal cpu_dout : dmem_data_t;
	signal cpu_addr : dmem_addr_t;
	signal cpu_we : std_logic;
	signal cpu_re : std_logic;
	signal cpu_io_sel : std_logic;

	COMPONENT xrom
	Port (
		clk			: in  STD_LOGIC;
		ce			: in  STD_LOGIC;
		addr		: in  dmem_addr_t;
		dout		: out dmem_data_t
	);
	END COMPONENT;
	signal xrom_data : dmem_data_t;

	COMPONENT lcd_port
	PORT (
			rst			: in std_logic;
			clk			: in std_logic;
			we			: in std_logic;
			din			: in unsigned(7 downto 0);
			dout		: out unsigned(7 downto 0);
			lcd_d		: inout std_logic_vector(3 downto 0);
			lcd_e		: out std_logic;
			lcd_rs		: out std_logic;
			lcd_rw		: out std_logic
		);
	END COMPONENT;

	signal cpu_lcd_in_reg, cpu_lcd_out_reg : unsigned(DMEM_DATA_WIDTH-1 downto 0);
	signal cpu_lcd_we : std_logic;
	
	------------------------------------------------------------------
--		X"1F400", -- W: DAC rate 8000Hz
--		X"AC440", -- W: DAC rate 44100Hz
	------------------------------------------------------------------

	type state_t is (st_reset, st_pcm_in, st_pcm_in3, st_pcm_in4, st_pcm_out, st_pcm_out3, st_pcm_out4);
	type enc_state_t is 
	(
		enc_init, 
		enc_keyload0, enc_keyload1, enc_keyload2, enc_keyload3,
		enc_ivload0, enc_ivload1, enc_ivload2, enc_ivload3,
		enc_wait_block,
		enc_block_in0, enc_block_in1, enc_block_in2, enc_block_in3);
		
	signal ecb_state, ecb_state_next : enc_state_t;
	signal cbc_state, cbc_state_next : enc_state_t;
	
	signal rst, clk : std_logic;
	signal acio_ready : std_logic;
	signal pcm_active : std_logic;
	signal state, nextstate : state_t;
	signal ac_reset_n : std_logic;
	signal ac_rst : std_logic;
	signal ac_ssync : std_logic;
	signal pcm_request, pcm_request_set, pcm_request_ack : std_logic;
	signal started_up : std_logic := '1';
	signal cpu_cmd_addr, cpu_cmd_data, cpu_stat_addr, cpu_stat_data : unsigned(15 downto 0);
	signal cpu_pcmin_dc_adj_left, cpu_pcmin_dc_adj_right : unsigned(15 downto 0);

	
	subtype low is unsigned(7 downto 0);
	subtype high is unsigned(15 downto 8);
	
	signal pcm_in_data : pcm_data_t;
	signal pcm_out_data : pcm_data_t;
	
	signal pcm_in_data_we, pcm_in_valid : stereo_bits_t;
	
	type pcmout_mode_t is (PCMOUT_PASSTHROUGH, PCMOUT_CIPHER_ECB, PCMOUT_CIPHER_CBC);
	signal pcmout_mode : pcmout_mode_t;
	signal cpu_cmd_access, cpu_cmd_ready, cpu_stat_ready : std_logic;
	signal cpu_led_reg, cpu_btn_reg, cpu_dip_reg, cpu_pcmmode_reg : unsigned(DMEM_DATA_WIDTH-1 downto 0);
	signal cpu_cmd_we : std_logic;
	
	------------------------------------------------------------------
	constant KEY_ID1 : key_id_t := std_logic_vector(to_unsigned(0, key_id_t'length));

	signal ecb_mode_i	:	ecb_mode_encryption_in_type;	-- see package ecb_mode_iface
	signal ecb_mode_o	:	ecb_mode_encryption_out_type;	-- see package ecb_mode_iface
	signal ecb_key_i	:	key_schedule_128_in_type;		-- see package key_schedule_iface
	signal ecb_key_o	:	key_schedule_128_out_type;		-- see package key_schedule_iface
	signal ecb_sbld_i	:	sbox_stage_load_in_type;		-- see package tech_iface
	signal ecb_cipher_pcm : unsigned(17 downto 0);

	signal cbc_mode_i	:	cbc_mode_encryption_in_type;	-- see package cbc_mode_iface
	signal cbc_mode_o	:	cbc_mode_encryption_out_type;	-- see package cbc_mode_iface
	signal cbc_key_i	:	key_schedule_128_in_type;		-- see package key_schedule_iface
	signal cbc_key_o	:	key_schedule_128_out_type;		-- see package key_schedule_iface
	signal cbc_sbld_i	:	sbox_stage_load_in_type;		-- see package tech_iface
	signal cbc_cipher_pcm : unsigned(17 downto 0);

	signal plain_block_in_rdy : std_logic;
	signal encryption_constant_plain : unsigned(15 downto 0);
	
	------------------------------------------------------------------
	function GetRequest(slot1 : unsigned) return unsigned is
	variable res : unsigned(3 to 12);
	begin
		res := (others => '0');
		for i in res'range loop
			res(i) := not slot1(14-i);
		end loop;
		return res;	

	end GetRequest;
	
begin

	------------------------------------------------------------------
	cmd_data <= (cpu_cmd_data & "0000");
	cmd_addr <= (cpu_cmd_addr & "0000");

	ac_rst <= not ac_reset_n;
	ac97_reset_n <= ac_reset_n;
	ac97_sync <= ac_ssync;
	
	sys_tx <= sys_rx;

	cpu_ce <= '1';
	clk <= sys_clk_in;
	rst <= not (started_up and sys_rst_n_in);
--	rst <= not (sys_rst_n_in);

	cpu_int_in <= sync_strobe(0);
	
	------------------------------------------------------------------
	proc_led_btn:
	process (rst, clk)
	begin
		if rising_edge(clk) then
			cpu_btn_reg <= "000" & unsigned(sys_btn);
--			cpu_dip_reg <= unsigned(sys_dip);
			cpu_dip_reg <= (others => '0');		-- for FX12 (ML-403)
			if rst = '1' then
				sys_led <= (others => '0');
			else	
				sys_led(7 downto 0) <= STD_LOGIC_VECTOR(cpu_led_reg);
			end if;
		end if;
	end process;	

	------------------------------------------------------------------
	ecb_fsm: process(ecb_state, plain_block_in_rdy, ecb_mode_i, ecb_key_i, plain_block_in)
	begin
		ecb_state_next <= ecb_state;
		
		ecb_mode_i.start <= '0';
		ecb_mode_i.plain <= (others => '0');
		ecb_mode_i.key_id <= KEY_ID1;
		ecb_key_i.load_key <= '0';
		ecb_key_i.key_id <= KEY_ID1;
		ecb_key_i.key <= (others => '0');
		
		ecb_sbld_i.act <= '0';
		ecb_sbld_i.wen <= '0';
		
		case ecb_state is 
		when enc_init =>
			if ecb_key_o.busy = '0' then
				ecb_state_next <= enc_keyload0;
			end if;	
			
		when enc_keyload0 =>
			ecb_key_i.load_key <= '1';
			ecb_key_i.key <= X"2b7e1516";
			ecb_state_next <= enc_keyload1;
		when enc_keyload1 =>
			ecb_key_i.key <= X"28aed2a6";
			ecb_state_next <= enc_keyload2;
		when enc_keyload2 =>
			ecb_key_i.key <= X"abf71588";
			ecb_state_next <= enc_keyload3;
		when enc_keyload3 =>
			ecb_key_i.key <= X"09cf4f3c";
			if ecb_key_o.busy = '0' then
				ecb_state_next <= enc_wait_block;
			end if;	
			
		when enc_wait_block => 
			if plain_block_in_rdy = '1' then
				ecb_state_next <= enc_block_in0;
			end if;	
			
		when enc_block_in0 =>
			ecb_mode_i.start <= '1';
			ecb_mode_i.plain <= std_logic_vector(plain_block_in(1)) & std_logic_vector(plain_block_in(0));
			ecb_state_next <= enc_block_in1;
		when enc_block_in1 =>
			ecb_mode_i.plain <= std_logic_vector(plain_block_in(3)) & std_logic_vector(plain_block_in(2));
			ecb_state_next <= enc_block_in2;
		when enc_block_in2 =>
			ecb_mode_i.plain <= std_logic_vector(plain_block_in(5)) & std_logic_vector(plain_block_in(4));
			ecb_state_next <= enc_block_in3;
		when enc_block_in3 =>
			ecb_mode_i.plain <= std_logic_vector(plain_block_in(7)) & std_logic_vector(plain_block_in(6));
			if ecb_mode_o.busy = '0' then
				ecb_state_next <= enc_wait_block;
			end if;	
		
		when others =>
			ecb_state_next <= enc_init;
		
		end case;
	end process;
	
	proc_ecb_fsm_next:
	process (rst, clk)
	begin
		if rst = '1' then
			ecb_state <= enc_init;
		elsif rising_edge(clk) then
			ecb_state <= ecb_state_next;
		end if;
	end process;	
	
	------------------------------------------------------------------
	cbc_fsm: process(cbc_state, plain_block_in_rdy, cbc_mode_i, cbc_key_i, plain_block_in)
	begin
		cbc_state_next <= cbc_state;
		
		cbc_mode_i.start <= '0';
		cbc_mode_i.din <= (others => '0');
		cbc_mode_i.key_id <= KEY_ID1;
		cbc_mode_i.load_iv <= '0';

		cbc_key_i.key <= (others => '0');
		cbc_key_i.key_id <= KEY_ID1;
		cbc_key_i.load_key <= '0';
		
		cbc_sbld_i.act <= '0';
		cbc_sbld_i.wen <= '0';
		
		case cbc_state is 
		when enc_init =>
			if cbc_key_o.busy = '0' then
				cbc_state_next <= enc_keyload0;
			end if;	
			
		when enc_keyload0 =>
			cbc_key_i.load_key <= '1';
			cbc_key_i.key <= X"2b7e1516";
			cbc_state_next <= enc_keyload1;
		when enc_keyload1 =>
			cbc_key_i.key <= X"28aed2a6";
			cbc_state_next <= enc_keyload2;
		when enc_keyload2 =>
			cbc_key_i.key <= X"abf71588";
			cbc_state_next <= enc_keyload3;
		when enc_keyload3 =>
			cbc_key_i.key <= X"09cf4f3c";
			if cbc_key_o.busy = '0' then
				cbc_state_next <= enc_ivload0;
			end if;	

		when enc_ivload0 =>
			cbc_mode_i.load_iv <= '1';
			cbc_mode_i.din <= X"00010203";
			cbc_state_next <= enc_ivload1;
		when enc_ivload1 =>
			cbc_mode_i.din <= X"04050607";
			cbc_state_next <= enc_ivload2;
		when enc_ivload2 =>
			cbc_mode_i.din <= X"08090a0b";
			cbc_state_next <= enc_ivload3;
		when enc_ivload3 =>
			cbc_mode_i.din <= X"0c0d0e0f";
			if cbc_mode_o.busy = '0' then
				cbc_state_next <= enc_wait_block;
			end if;	

		when enc_wait_block => 
			if plain_block_in_rdy = '1' then
				cbc_state_next <= enc_block_in0;
			end if;	
			
		when enc_block_in0 =>
			cbc_mode_i.start <= '1';
			cbc_mode_i.din <= std_logic_vector(plain_block_in(1)) & std_logic_vector(plain_block_in(0));
			cbc_state_next <= enc_block_in1;
		when enc_block_in1 =>
			cbc_mode_i.din <= std_logic_vector(plain_block_in(3)) & std_logic_vector(plain_block_in(2));
			cbc_state_next <= enc_block_in2;
		when enc_block_in2 =>
			cbc_mode_i.din <= std_logic_vector(plain_block_in(5)) & std_logic_vector(plain_block_in(4));
			cbc_state_next <= enc_block_in3;
		when enc_block_in3 =>
			cbc_mode_i.din <= std_logic_vector(plain_block_in(7)) & std_logic_vector(plain_block_in(6));
			if cbc_mode_o.busy = '0' then
				cbc_state_next <= enc_wait_block;
			end if;	
		
		when others =>
			cbc_state_next <= enc_wait_block;
		
		end case;
	end process;
	
	proc_cbc_fsm_next:
	process (rst, clk)
	begin
		if rst = '1' then
			cbc_state <= enc_init;
		elsif rising_edge(clk) then
			cbc_state <= cbc_state_next;
		end if;
	end process;	
	
	------------------------------------------------------------------
	proc_plain_block_in:
	process (rst, clk)
	variable index : natural range 0 to 15;
	variable dc_adj : unsigned(15 downto 0);
	
	begin
		dc_adj := unsigned(resize(signed(cpu_pcmin_dc_adj_left), dc_adj'length));
		if rst = '1' then
			index := 0;
		elsif rising_edge(clk) then
			plain_block_in_rdy <= '0';
			if pcm_in_data_we(left) = '1' then
				if encrypt_audio = '1' then
					plain_block_in(index) <= (rx_pcm_data(19 downto 4) and X"F000");
				else
					plain_block_in(index) <= encryption_constant_plain;
				end if;					
				if index < 7 then
					index := index + 1;
				else
					index := 0;
					plain_block_in_rdy <= '1';
				end if;	
			end if;	
		end if;
	end process;	
	
	------------------------------------------------------------------
	proc_ecb_block_out:
	process (rst, clk)
	variable index : natural range 0 to 7;
	variable write_index : natural range 0 to 7;
	variable cipher : unsigned(31 downto 0);
	begin
		if rst = '1' then
			index := 0;
			write_index := 0;
		elsif rising_edge(clk) then
			if ecb_mode_o.valid = '1' then
				cipher := unsigned(ecb_mode_o.cipher);
				ecb_block_out(write_index+0) <= cipher(15 downto 0);
				ecb_block_out(write_index+1) <= cipher(31 downto 16);
				if write_index < 6 then
					write_index := write_index + 2;
				else	
					index := 0;
					write_index := 0;
				end if;	
			end if;
			if pcm_request = '1' and sync_strobe(2) = '1' then
				if index < 7 then
					index := index + 1;
				else
					index := 0;
				end if;	
			end if;	
		end if;
		ecb_cipher_pcm <= "00" & ecb_block_out(index);
	end process;	

	------------------------------------------------------------------
	proc_cbc_block_out:
	process (rst, clk)
	variable index : natural range 0 to 7;
	variable write_index : natural range 0 to 7;
	variable cipher : unsigned(31 downto 0);
	variable cipher_rdy : std_logic;
	begin
		if rst = '1' then
			index := 0;
			write_index := 0;
			cipher_rdy := '0';
		elsif rising_edge(clk) then
			if cbc_mode_o.valid = '1' then
				cipher := unsigned(cbc_mode_o.dout);
				cbc_block_out(write_index+0) <= cipher(15 downto 0);
				cbc_block_out(write_index+1) <= cipher(31 downto 16);
				if write_index < 6 then
					write_index := write_index + 2;
				else	
					index := 0;
					write_index := 0;
				end if;	
			end if;
			if pcm_request = '1' and sync_strobe(2) = '1' then
				if index < 7 then
					index := index + 1;
				else
					index := 0;
				end if;	
			end if;	
		end if;	
		cbc_cipher_pcm <= "00" & cbc_block_out(index);
	end process;	

	------------------------------------------------------------------
	host_din : process (acio_ready, sync_strobe, slot_valid, stat_addr, state, pcm_request, pcm_out_data)
	variable request : unsigned(3 to 12);
	variable rx_frame_valid : std_logic;
	variable sync_frame, sync_status, sync_tx : std_logic;
	
	begin
		request := GetRequest(stat_addr);
		rx_frame_valid := slot_valid(0);
		sync_frame := sync_strobe(0);
		sync_status := sync_strobe(1);
		sync_tx := sync_strobe(2);
		
		tx_pcm_we <= '0';
		tx_pcm_addr <= to_unsigned(3, tx_pcm_addr'length);
		tx_pcm_data <= to_unsigned(0, tx_pcm_data'length);

		rx_pcm_addr <= to_unsigned(3, rx_pcm_addr'length);
		
		nextstate <= state;
		pcm_active <= '0';
		pcm_request_set <= '0';
		pcm_request_ack <= '0';
		pcm_in_data_we <= (others => '0');
		
		case state is
			
		when st_reset =>
		if rx_frame_valid = '1' and sync_status = '1' and acio_ready = '1' then
			nextstate <= st_pcm_in;
		end if;	
					
		when st_pcm_in =>
		if rx_frame_valid = '1' and sync_frame = '1' then 
			nextstate <= st_pcm_in3;
		end if;
			
		when st_pcm_in3 => 
		nextstate <= st_pcm_in4;
		rx_pcm_addr <= to_unsigned(3, rx_pcm_addr'length);
		if slot_valid(3) = '1' then
			pcm_in_data_we(left) <= '1';
		end if;	

		when st_pcm_in4 =>
		nextstate <= st_pcm_out;
		rx_pcm_addr <= to_unsigned(4, rx_pcm_addr'length);
		if slot_valid(4) = '1' then
			pcm_in_data_we(right) <= '1';
		end if;	
		
		when st_pcm_out =>
		pcm_active <= '1';
		if rx_frame_valid = '1' and sync_status = '1' then
			if request(3) = '1' and request(4) = '1' then
				pcm_request_set <= '1';
			end if;	
		end if;	
		if pcm_request = '1' and sync_tx = '1' then
			nextstate <= st_pcm_out3;
		end if;	
		
		when st_pcm_out3 =>
		tx_pcm_data <= unsigned(pcm_out_data(left)) & "00";
		pcm_active <= '1';
		tx_pcm_addr <= to_unsigned(3, tx_pcm_addr'length);
		tx_pcm_we <= '1';
		pcm_request_ack <= '1';
		nextstate <= st_pcm_out4;

		when st_pcm_out4 =>
		tx_pcm_data <= unsigned(pcm_out_data(right)) & "00";
		pcm_active <= '1';
		tx_pcm_addr <= to_unsigned(4, tx_pcm_addr'length);
		tx_pcm_we <= '1';
		nextstate <= st_pcm_in;
		
		when others => 
			nextstate <= st_reset;
	
		end case;					

	end process;

	proc_fsm_next:
	process (rst, clk)
	begin
		if rst = '1' then
			state <= st_reset;
		elsif rising_edge(clk) then
			state <= nextstate;
		end if;
	end process;	

	-------------------------------------------------------------
	-- proc_pcm_in (stereo - for bypass)
	-------------------------------------------------------------
	proc_pcm_in:
	process (rst, clk, pcm_in_data_we, rx_pcm_data)
	variable dc_adj : pcm_data_t;
	begin
		dc_adj(left) := resize(signed(cpu_pcmin_dc_adj_left), dc_adj(left)'length);
		dc_adj(right) := resize(signed(cpu_pcmin_dc_adj_right), dc_adj(right)'length);
		
		if rst = '1' then
			for i in pcm_in_data'range loop
				dc_adj(i) := (others => '0');
				pcm_in_data(i) <= (others => '0');
				pcm_in_valid(i) <= '0';
			end loop;	
		elsif rising_edge(clk) then
			for i in pcm_in_data'range loop
				pcm_in_valid(i) <= '0';
			end loop;	
			for i in pcm_in_data'range loop
				if pcm_in_data_we(i) = '1' then 
					pcm_in_valid(i) <= '1';
					pcm_in_data(i) <= dc_adj(i) + signed(rx_pcm_data(19 downto 2));
				end if;
			end loop;	
		end if;
	end process;	

	-------------------------------------------------------------
	-- proc_pcmout_mode_mapper
	-------------------------------------------------------------
	proc_pcmout_mode_mapper:
	process (cpu_pcmmode_reg)
	begin
		case cpu_pcmmode_reg is
		when X"00" =>
			pcmout_mode <= PCMOUT_PASSTHROUGH;
		when X"01" =>
			pcmout_mode <= PCMOUT_CIPHER_ECB;
		when X"02" =>
			pcmout_mode <= PCMOUT_CIPHER_CBC;
		when others =>
			pcmout_mode <= PCMOUT_PASSTHROUGH;
		end case;
	end process;	

	-------------------------------------------------------------
	-- proc_pcm_out_mux
	-------------------------------------------------------------
	proc_pcm_out_mux:
	process (clk)
	begin
		if rising_edge(clk) then
			if pcm_request = '1' and sync_strobe(2) = '1' then
				case pcmout_mode is
					when PCMOUT_PASSTHROUGH =>
						pcm_out_data(left to right) <= pcm_in_data(left to right);
					when PCMOUT_CIPHER_ECB =>
						pcm_out_data(left) <= signed(ecb_cipher_pcm);
						pcm_out_data(right) <= signed(ecb_cipher_pcm);
					when PCMOUT_CIPHER_CBC =>
						pcm_out_data(left) <= signed(cbc_cipher_pcm);
						pcm_out_data(right) <= signed(cbc_cipher_pcm);
					when others => 
						pcm_out_data(left to right) <= pcm_in_data(left to right);
				end case;
			end if;	
		end if;
	end process;	

	-------------------------------------------------------------
	-- proc_cpu_flags
	-------------------------------------------------------------
	proc_cpu_stat_reg_control:
	process (clk, sync_strobe, slot_valid)
	variable rx_frame_valid : std_logic;
	variable sync_frame, sync_status, sync_tx : std_logic;
	begin
		rx_frame_valid := slot_valid(0);
		sync_frame := sync_strobe(0);
		sync_status := sync_strobe(1);
		sync_tx := sync_strobe(2);

		if rising_edge(clk) then
			if rst = '1' then
				cpu_cmd_ready <= '0';
				cpu_stat_ready <= '0';
				cpu_stat_addr <= (others => '0');
				cpu_stat_data <= (others => '0');
			else
				cpu_cmd_we <= '0';
				if cpu_cmd_access = '1' then
					cpu_cmd_ready <= '0';
					cpu_stat_ready <= '0';
				end if;
			
				if cpu_cmd_ready = '0' then
					if sync_tx = '1' then
						cpu_cmd_we <= '1';
						cpu_cmd_ready <= '1';
					end if;	
				end if;
				
				if sync_status = '1' and slot_valid(0 to 2) = "111" then
					cpu_stat_ready <= '1';
					cpu_stat_addr <= stat_addr(19 downto 4);
					cpu_stat_data <= stat_data(19 downto 4);
				end if;
			end if;
		end if;	

	end process;	

	-------------------------------------------------------------
	-- proc_cpu_dout_reg
	-------------------------------------------------------------
	proc_cpu_dout_reg:
	process (clk)
	variable addr : dmem_addr_t;
	begin
		if rising_edge(clk) then
			if rst = '1' then
				cpu_led_reg <= (others => '0');
				cpu_pcmmode_reg <= (others => '0');
				encrypt_audio <= '1';
				cpu_cmd_addr <= (others => '0');
				cpu_cmd_data <= (others => '0');
				cpu_lcd_out_reg <= (others => '0');
				cpu_pcmin_dc_adj_left <= (others => '0');
				cpu_pcmin_dc_adj_right <= (others => '0');
				encryption_constant_plain <= (others => '0');
			else 	
				addr := cpu_addr;
				cpu_cmd_access <= '0';
				cpu_lcd_we <= '0';
				if cpu_we = '1' and cpu_io_sel = '1' then
					case addr is
					when X"00" =>
						cpu_led_reg <= cpu_dout;
					when X"01" =>
						cpu_pcmmode_reg <= cpu_dout;
					when X"02" =>
						encrypt_audio <= cpu_dout(0);
					when X"04" => 
						cpu_cmd_addr(low'range) <= cpu_dout;
					when X"05" => 
						cpu_cmd_addr(high'range) <= cpu_dout;
					when X"06" => 
						cpu_cmd_access <= '1';
						cpu_cmd_data(low'range) <= cpu_dout;
					when X"07" => 
						cpu_cmd_data(high'range) <= cpu_dout;
					when X"08" => 
						cpu_lcd_we <= '1';
						cpu_lcd_out_reg <= cpu_dout;
					when X"0D" => 
						cpu_pcmin_dc_adj_left(low'range) <= cpu_dout;
					when X"0E" => 
						cpu_pcmin_dc_adj_left(high'range) <= cpu_dout;
					when X"0F" => 
						cpu_pcmin_dc_adj_right(low'range) <= cpu_dout;
					when X"10" => 
						cpu_pcmin_dc_adj_right(high'range) <= cpu_dout;
					when X"11" =>
						encryption_constant_plain(7 downto 0) <= cpu_dout;
					when X"12" =>
						encryption_constant_plain(15 downto 8) <= cpu_dout;
					when others => null;
					end case;	
				end if;	
			end if;
		end if;
	end process;	

	-------------------------------------------------------------
	-- proc_cpu_din_reg
	-------------------------------------------------------------
	proc_cpu_din_reg:
	process (clk, cpu_io_sel, xrom_data)
	variable addr : dmem_addr_t;
	begin
	
		if rising_edge(clk) then
			addr := cpu_addr;
		end if;

		if cpu_io_sel = '0' then
			cpu_din <= xrom_data;
		else
			case addr is
			when X"00" =>
				cpu_din <= cpu_led_reg;
			when X"01" =>
				cpu_din <= cpu_pcmmode_reg;
			when X"02" =>
				cpu_din <= "0000000" & encrypt_audio;
			when X"04" =>
				cpu_din <= cpu_stat_addr(low'range);
			when X"05" =>
				cpu_din <= cpu_stat_addr(high'range);
			when X"06" =>
				cpu_din <= cpu_stat_data(low'range);
			when X"07" =>
				cpu_din <= cpu_stat_data(high'range);
			when X"08" =>
				cpu_din <= cpu_lcd_in_reg;
			when X"0D" =>
				cpu_din <= cpu_pcmin_dc_adj_left(low'range);
			when X"0E" =>
				cpu_din <= cpu_pcmin_dc_adj_left(high'range);
			when X"0F" =>
				cpu_din <= cpu_pcmin_dc_adj_right(low'range);
			when X"10" =>
				cpu_din <= cpu_pcmin_dc_adj_right(high'range);
			when X"11" =>
				cpu_din <= encryption_constant_plain(7 downto 0);
			when X"12" =>
				cpu_din <= encryption_constant_plain(15 downto 8);
			when X"80" =>
				cpu_din <= cpu_btn_reg;
			when X"81" =>
				cpu_din <= cpu_dip_reg;
			when X"82" =>
				cpu_din <= dmem_data_t(pcm_in_data(left)(low'range));
			when X"83" =>
				cpu_din <= dmem_data_t(pcm_in_data(left)(high'range));
			when X"84" =>
				cpu_din <= dmem_data_t(pcm_in_data(right)(low'range));
			when X"85" =>
				cpu_din <= dmem_data_t(pcm_in_data(right)(high'range));
			when X"A0" =>
				cpu_din <= "0000" & ecb_mode_o.busy & cpu_stat_ready & cpu_cmd_ready & (pcm_active and acio_ready and slot_valid(0));
			when others =>
				if addr(0) = '0' then
					cpu_din <= X"EF";
				else
					cpu_din <= X"BE";
				end if;
			end case;	
		end if;	

	end process;	
	-------------------------------------------------------------
	proc_pcm_request:
	process (clk, pcm_request_set, pcm_request_ack)
	begin
		if rising_edge(clk) then
			if pcm_request_ack = '1' or rst = '1' then
				pcm_request <= '0';
			elsif pcm_request_set = '1' then
				pcm_request <= '1';
			end if;
		end if;
	end process;	

	------------------------------------------------------------------
	-- Instantiate the Unit Under Test (UUT)
	inst_ac_io: ac_io PORT MAP(
		rst				=> rst,				
		clk				=> clk,				
		ready			=> acio_ready,				
		sync_strobe		=> sync_strobe,	
		slot_valid		=> slot_valid,	
		stat_addr		=> stat_addr,			
		stat_data		=> stat_data,			
		rx_pcm_addr		=> rx_pcm_addr,		
		rx_pcm_data		=> rx_pcm_data,		
		cmd_addr 		=> cmd_addr, 	
		cmd_data		=> cmd_data,		
		cmd_we			=> cpu_cmd_we,	
		tx_pcm_addr		=> tx_pcm_addr,		
		tx_pcm_data		=> tx_pcm_data,		
		tx_pcm_we		=> tx_pcm_we,	
		ac_sdata_in		=> ac97_sdata_in,		
		ac_bit_clk		=> ac97_bit_clk,		
		ac_reset_n		=> ac_reset_n,		
		ac_sdata_out	=> ac97_sdata_out,	
		ac_ssync		=> ac_ssync		
	);
	
	inst_cpu_embedded: cpu_embedded
	PORT MAP(
		rst => rst,
		clk => clk,
		ce => cpu_ce,
		int_in => cpu_int_in,
		int_ack => cpu_int_ack,
		xmem_we => cpu_we,
		xmem_re => cpu_re,
		xmem_din => cpu_din,
		xmem_dout => cpu_dout,
		xmem_addr => cpu_addr,
		io_sel => cpu_io_sel
	);

	inst_xrom: xrom
	PORT MAP(
		clk			=> clk,
		ce			=> cpu_ce,
		addr		=> cpu_addr,
		dout 		=> xrom_data
	);

	inst_lcd_port: lcd_port
	PORT MAP(
		rst => rst,
		clk => clk,
		we => cpu_lcd_we,
		din => cpu_lcd_out_reg,
		dout => cpu_lcd_in_reg,
		lcd_d => sys_lcd_d,
		lcd_e => sys_lcd_e,
		lcd_rs => sys_lcd_rs,
		lcd_rw => sys_lcd_rw
	);

	inst_ecb_128_encryption_core: ecb_128_encryption_core
		port map (
			rst		=> 	rst,
			clk		=>	clk,
			mode_i	=>	ecb_mode_i,
			mode_o	=>	ecb_mode_o,
			key_i	=>	ecb_key_i,
			key_o	=>	ecb_key_o,
			sbld_i	=>	ecb_sbld_i
		);

	inst_cbc_128_encryption_core: cbc_128_encryption_core
		port map (
			rst		=> 	rst,
			clk		=>	clk,
			mode_i	=>	cbc_mode_i,
			mode_o	=>	cbc_mode_o,
			key_i	=>	cbc_key_i,
			key_o	=>	cbc_key_o,
			sbld_i	=>	cbc_sbld_i
		);

	------------------------------------------------------------------
	STARTUP_VIRTEX4_inst : STARTUP_VIRTEX4
	port map (
		EOS => started_up,      -- End of Startup 1-bit output
		CLK => open,      -- Clock input for start-up sequence
		GSR => '0', -- Global Set/Reset input (GSR cannot be used for the port name)
		GTS => '0', -- Global 3-state input (GTS cannot be used for the port name)
		USRCCLKO => '0',   -- USRCCLKO 1-bit input
		USRCCLKTS => '0', -- USRCCLKTS 1-bit input
		USRDONEO => '0',   -- USRDONEO 1-bit input
		USRDONETS => '0'  -- USRDONETS 1-bit input
	);
   
	------------------------------------------------------------------
   
end Behavioral;

