mirror of
https://github.com/MiSTer-devel/SMS_MiSTer.git
synced 2026-05-17 03:04:32 +00:00
Technically, `Clk’event and clk = ‘1’` can result in non-`0` triggers (e.g. `1`, `U`, `H`, `X`, `Z`, `W`, etc...), whereas `rising_edge(clk)` does not.
514 lines
20 KiB
VHDL
514 lines
20 KiB
VHDL
--
|
||
-- 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 );
|
||
|
||
-- レジスタアドレスラッチ (第1ステージ)
|
||
process( reset, clk )
|
||
begin
|
||
if( reset = '1' )then
|
||
regs_addr <= (others => '0');
|
||
elsif rising_edge(clk) 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;
|
||
|
||
-- 現在のスロットの音色データ読み出しアドレスラッチ (第1ステージ)
|
||
process( reset, clk )
|
||
begin
|
||
if( reset = '1' )then
|
||
slot_voice_addr <= 0;
|
||
elsif rising_edge(clk) 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 rising_edge(clk) 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;
|