-- 
-- Controller.vhd 
-- The core controller module of VM2413
--
-- Copyright (c) 2006 Mitsutaka Okazaki (brezza@pokipoki.org)
-- All rights reserved.
-- 
-- Redistribution and use of this source code or any derivative works, are 
-- permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice, 
--    this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright 
--    notice, this list of conditions and the following disclaimer in the 
--    documentation and/or other materials provided with the distribution.
-- 3. Redistributions may not be sold, nor may they be used in a commercial 
--    product or activity without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
-- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
--
-- [Description]
--
-- The Controller is the beginning module of the OPLL slot calculation.
-- It manages register accesses from I/O and sends proper voice parameters 
-- to the succeding PhaseGenerator and EnvelopeGenerator modules. 
-- The one cycle of the Controller consists of 4 stages as follows.
-- 
-- 1st stage: 
--   * Prepare to read the register value for the current slot from RegisterMemory.
--   * Prepare to read the voice parameter for the current slot from VoiceMemory.
--   * Prepare to read the user-voice data from VoiceMemory.
--             
-- 2nd stage: 
--   * Wait for RegisterMemory and VoiceMemory
--
-- 3rd clock stage:
--   * Update register value if wr='1' and addr points the current OPLL channel.
--   * Update voice parameter if wr='1' and addr points the voice parameter area.
--   * Write register value to RegisterMemory.
--   * Write voice parameter to VoiceMemory.
--
-- 4th stage:
--   * Send voice and register parameters to PhaseGenerator and EnvelopeGenerator.
--   * Increment the number of the current slot.
--
-- Each stage is completed in one clock. Thus the Controller traverses all 18 opll 
-- slots in 72 clocks. 
--

--
--	modified by t.hara
--

library	ieee;
	use	ieee.std_logic_1164.all;
	use	ieee.std_logic_unsigned.all;
	use	work.vm2413.all;

entity controller is port (

	clk		: in	std_logic;
	reset	: in	std_logic;
	clkena	: in	std_logic;
	
	slot	: in	std_logic_vector( 4 downto 0 );
	stage	: in	std_logic_vector( 1 downto 0 );
	
	wr		: in	std_logic;
	addr	: in	std_logic_vector( 7 downto 0 );
	data	: in	std_logic_vector( 7 downto 0 );
		
	-- output parameters for phasegenerator and envelopegenerator
	am		: out	am_type;
	pm		: out	pm_type;
	wf		: out	wf_type;
	ml		: out	ml_type;
	tl		: out	db_type;
	fb		: out	fb_type;
	ar		: out	ar_type;
	dr		: out	dr_type;
	sl		: out	sl_type;
	rr		: out	rr_type;
	
	blk		: out	blk_type;
	fnum	: out	fnum_type;
	rks		: out	rks_type;
		
	key		: out	std_logic;
	rhythm	: out	std_logic

	-- slot_out : out slot_id
);
end controller;

architecture rtl of controller is

	component registermemory
		port (
			clk		: in	std_logic;
			reset	: in	std_logic;
			addr	: in	std_logic_vector(  3 downto 0 );
			wr		: in	std_logic;
			idata	: in	std_logic_vector( 23 downto 0 );
			odata	: out	std_logic_vector( 23 downto 0 )
		);
	end component;
	
	component voicememory port (
		clk		: in std_logic;
		reset	: in std_logic;
		idata	: in voice_type;
		wr		 : in std_logic;
		rwaddr : in voice_id_type;
		roaddr : in voice_id_type;
		odata	: out voice_type;
		rodata : out voice_type );
	end component;
	
	-- the array which caches instrument number of each channel.
	type inst_array is array (ch_type'range) of integer range 0 to 15;
	signal inst_cache : inst_array;

	type kl_array is array (0 to 15) of std_logic_vector(5 downto 0);
	constant kl_table : kl_array := (
		"000000", "011000", "100000", "100101",
		"101000", "101011", "101101", "101111",
		"110000", "110010", "110011", "110100",
		"110101", "110110", "110111", "111000"
	); -- 0.75db/step, 6db/oct

	-- signals for the read-only access ports of voicememory module.
	signal slot_voice_addr		: voice_id_type;
	signal slot_voice_data		: voice_type;
	
	-- signals for the read-write access ports of voicememory module.
	signal user_voice_wr		: std_logic;
	signal user_voice_addr		: voice_id_type;
	signal user_voice_rdata		: voice_type;
	signal user_voice_wdata		: voice_type;
	
	-- signals for the registermemory module.
	signal regs_wr				: std_logic;
	signal regs_addr			: std_logic_vector(  3 downto 0 );
	signal regs_rdata			: std_logic_vector( 23 downto 0 );
	signal regs_wdata			: std_logic_vector( 23 downto 0 );

	signal rflag				: std_logic_vector(  7 downto 0 );
	signal w_channel			: std_logic_vector(  3 downto 0 );
	signal w_is_carrier			: std_logic;

begin	-- rtl

	--	レジスタ設定値を保持するためのメモリ 
	u_register_memory : RegisterMemory
	port map (
		clk		=> clk,
		reset	=> reset,
		addr	=> regs_addr,
		wr		=> regs_wr,
		idata	=> regs_wdata,
		odata	=> regs_rdata
	);
		
	vmem : voicememory port map (
		clk, reset, user_voice_wdata, user_voice_wr, user_voice_addr, slot_voice_addr,
		user_voice_rdata, slot_voice_data );

	--	レジスタアドレスラッチ (第１ステージ) 
	process( reset, clk )
	begin
		if( reset = '1' )then
			regs_addr	<= (others => '0');
		elsif( clk'event and clk = '1' )then
			if clkena='1' then
				if( stage = "00" )then
					regs_addr <= slot( 4 downto 1 );
				else
					--	hold
				end if;
			end if;
		end if;
	end process;

	--	現在のスロットの音色データ読み出しアドレスラッチ (第１ステージ) 
	process( reset, clk )
	begin
		if( reset = '1' )then
			slot_voice_addr	<= 0;
		elsif( clk'event and clk = '1' )then
			if clkena='1' then
				if( stage = "00" )then
					if( rflag(5) = '1' and w_channel >= "0110" )then
						--	リズムモードで ch6 以降 
						slot_voice_addr <= conv_integer(slot) - 12 + 32;
					else
						slot_voice_addr <= inst_cache(conv_integer(slot)/2) * 2 + conv_integer(slot) mod 2;
					end if;
				else
					--	hold
				end if;
			end if;
		end if;
	end process;

	w_channel		<= slot( 4 downto 1 );
	w_is_carrier	<= slot( 0 );

	process (clk, reset)

		variable kflag : std_logic;
		variable tll : std_logic_vector(db_type'high+1 downto 0);
		variable kll : std_logic_vector(db_type'high+1 downto 0);
		
		variable regs_tmp		: std_logic_vector(23 downto 0);
		variable user_voice_tmp : voice_type;
		
		variable fb_buf : fb_type;
		variable wf_buf : wf_type;
		
		variable extra_mode : std_logic;
		variable vindex : voice_id_type;

	begin	-- process
	
		if(reset = '1') then
			
			key	<= '0';
			rhythm <= '0';
			tll := (others=>'0');
			kll := (others=>'0');
			kflag := '0';
			rflag <= (others=>'0');
			user_voice_wr <= '0';
			user_voice_addr <= 0;
			regs_wr <='0';
			ar	<= (others=>'0');
			dr	<= (others=>'0');
			sl	<= (others=>'0');
			rr	<= (others=>'0');
			tl	<= (others=>'0');
			fb	<= (others=>'0');
			wf	<= '0';
			ml	<= (others=>'0');
			fnum <= (others=>'0');
			blk	<= (others=>'0');
			key	<= '0';
			rks	<= (others=>'0');
			rhythm <= '0';
			extra_mode := '0';
			vindex := 0;
			
		elsif clk'event and clk='1' then if clkena='1' then
		
			case stage is 
			--------------------------------------------------------------------------
			-- 1st stage (setting up a read request for register and voice memories.)
			--------------------------------------------------------------------------
			when "00" =>
				
--				if extra_mode = '0' then
					-- alternately read modulator or carrior.
					 vindex := conv_integer(slot) mod 2;
--				else
--					if vindex = voice_id_type'high then
--						vindex:= 0;
--					else
--						vindex:= vindex + 1;
--					end if;
--				end if;
				
				user_voice_addr <= vindex;				
				regs_wr <= '0';
				user_voice_wr <='0'; 
			
			--------------------------------------------------------------------------
			-- 2nd stage (just a wait for register and voice memories.)
			--------------------------------------------------------------------------
			when "01" =>
				null;			
			
			--------------------------------------------------------------------------
			-- 3rd stage (updating a register and voice parameters.)
			--------------------------------------------------------------------------
			when "10" =>
				
				if wr='1' then
				
--					if ( extra_mode = '0' and conv_integer(addr) < 8 ) or 
--						 ( extra_mode = '1' and ( conv_integer(addr) - 64 ) / 8 = vindex / 2 ) then 
					if( extra_mode = '0' and conv_integer(addr) < 8 )then 
						
						-- update user voice parameter.
						user_voice_tmp := user_voice_rdata;
					
						case addr(2 downto 1) is						
							when "00" =>
								if conv_integer(addr(0 downto 0)) = (vindex mod 2) then
									user_voice_tmp.am := data(7);
									user_voice_tmp.pm := data(6);
									user_voice_tmp.eg := data(5);
									user_voice_tmp.kr := data(4);
									user_voice_tmp.ml := data(3 downto 0);
									user_voice_wr <= '1';
								end if;
								
							when "01" =>			 
								if addr(0)='0' and (vindex mod 2 = 0) then
									user_voice_tmp.kl := data(7 downto 6);
									user_voice_tmp.tl := data(5 downto 0);
									user_voice_wr <= '1';
								elsif addr(0)='1' and (vindex mod 2 = 0) then
									user_voice_tmp.wf := data(3);
									user_voice_tmp.fb := data(2 downto 0);
									user_voice_wr <= '1';
								elsif addr(0)='1' and (vindex mod 2 = 1) then
									user_voice_tmp.kl := data(7 downto 6);
									user_voice_tmp.wf := data(4);
									user_voice_wr <= '1';
								end if;
								
							when "10" =>
								if conv_integer(addr(0 downto 0)) = (vindex mod 2) then
									user_voice_tmp.ar := data(7 downto 4);
									user_voice_tmp.dr := data(3 downto 0);
									user_voice_wr <= '1';
								end if;
								
							when "11" =>
								if conv_integer(addr(0 downto 0)) = (vindex mod 2) then
									user_voice_tmp.sl := data(7 downto 4);
									user_voice_tmp.rr := data(3 downto 0);
									user_voice_wr <= '1';
								end if;								
						end case;

						user_voice_wdata <= user_voice_tmp;
						
					elsif conv_integer(addr) = 14 then
					
						rflag <= data;
						
					elsif conv_integer(addr) < 16 then
					
						null;
						
					elsif conv_integer(addr) <= 63 then 
					
						if( conv_integer(addr(3 downto 0) ) = conv_integer(slot) / 2 ) then
							regs_tmp := regs_rdata;
							case addr( 5 downto 4 ) is 
								when "01" => -- 10h〜18h の場合（下位 F-Number） 
									regs_tmp(7 downto 0) := data;				--	F-Number
									regs_wr <= '1';							
								when "10" => -- 20h〜28h の場合（Sus, Key, Block, F-Number MSB） 
									regs_tmp(13) := data(5);					--	Sus
									regs_tmp(12) := data(4);					--	Key
									regs_tmp(11 downto 9) := data(3 downto 1);	--	Block
									regs_tmp(8) := data(0);						--	F-Number
									regs_wr <= '1';
								when "11" => -- 30h〜38h の場合（Inst, Vol） 
									regs_tmp(23 downto 20) := data(7 downto 4);	--	Inst
									regs_tmp(19 downto 16) := data(3 downto 0);	--	Vol
									regs_wr <='1';
								when others =>
									null;
							end case;
							regs_wdata <= regs_tmp;
						end if;
						
					elsif conv_integer(addr) = 240 then
					
						if data(7 downto 0) = "10000000" then
							extra_mode := '1';
						else
							extra_mode := '0';
						end if;

					end if;

				end if;
			
			--------------------------------------------------------------------------
			-- 4th stage (updating a register and voice parameters.)
			--------------------------------------------------------------------------
			when "11" =>

				-- output slot number (for explicit synchonization with other units).
				-- slot_out <= slot;

				-- updating insturument cache 
				inst_cache(conv_integer(slot)/2) <= conv_integer(regs_rdata(23 downto 20));

				rhythm <= rflag(5);

				-- updating rhythm status and key flag
				if rflag(5) = '1' and 12 <= slot then
					case slot is
						when "01100" | "01101" => -- bd
							kflag := rflag(4);
						when "01110" =>					-- hh
							kflag := rflag(0);			
						when "01111" =>					-- sd
							kflag := rflag(3);			
						when "10000" =>					-- tom
							kflag := rflag(2);			
						when "10001" =>					-- cym
							kflag := rflag(1); 
						when others => null;
					end case;
				else
					kflag := '0';
				end if;

				kflag := kflag or regs_rdata(12);

				-- calculate key-scale attenuation amount.
				kll := (("0"&kl_table(conv_integer(regs_rdata(8 downto 5)))) 
						 - ("0"&("111"-regs_rdata(11 downto 9))&"000")) & '0';
				
				if kll(kll'high) ='1' or slot_voice_data.kl = "00" then
					kll := (others=>'0');
				else
					kll := shr(kll, "11" - slot_voice_data.kl );
				end if;	

				-- calculate base total level from volume register value.
				if rflag(5) = '1' and (slot = "01110" or slot = "10000") then -- hh and cym
					tll := ('0' & regs_rdata(23 downto 20) & "000");
				elsif( slot(0) = '0' )then
					tll := ('0' & slot_voice_data.tl & '0'); -- mod
				else
					tll := ('0' & regs_rdata(19 downto 16) & "000");		 -- car
				end if;

				tll := tll + kll;
				
				if tll(tll'high) ='1' then
					tl <= (others=>'1');
				else
					tl <= tll(tl'range);
				end if;
				
				-- output rks, f-number, block and key-status.
				fnum <= regs_rdata(8 downto 0);
				blk	<= regs_rdata(11 downto 9);				
				key	<= kflag;
				
				if rflag(5) = '1' and 14 <= slot then
					if slot_voice_data.kr = '1' then
						rks <= "0101";
					else
						rks <= "00" & regs_rdata(11 downto 10);
					end if;
				else 
					if slot_voice_data.kr = '1' then
						rks <= regs_rdata(11 downto 9) & regs_rdata(8);
					else
						rks <= "00" & regs_rdata(11 downto 10);
					end if;
				end if;
				
				-- output voice parameters
				-- note that wf and fb output must keep its value
				-- at least 3 clocks since the operator module will fetch
				-- the wf and fb 2 clocks later of this stage.
				am <= slot_voice_data.am;
				pm <= slot_voice_data.pm;
				ml <= slot_voice_data.ml;
				wf_buf := slot_voice_data.wf;
				fb_buf := slot_voice_data.fb;
				wf <= wf_buf;
				fb <= fb_buf;
				ar <= slot_voice_data.ar;
				dr <= slot_voice_data.dr;
				sl <= slot_voice_data.sl;

				-- output release rate (depends on the sustine and envelope type).
				if( kflag = '1' ) then -- key on				
					if slot_voice_data.eg = '1' then
						rr <= "0000";
					else
						rr <= slot_voice_data.rr;
					end if;			 
				else -- key off
					if (slot(0) = '0') and not ( rflag(5) = '1' and (7 <= conv_integer(slot)/2) ) then
						rr	<= "0000";
					elsif regs_rdata(13) = '1' then
						rr	<= "0101";
					elsif slot_voice_data.eg = '0' then
						rr	<= "0111";
					else
						rr	<= slot_voice_data.rr;			 
					end if;
				end if;			 
				
			end case;
		
		end if; end if;

	end process;

end rtl;
