Files
SMS_MiSTer/rtl/VM2413/controller.vhd
birdybro 26247dd359 Update edge detection method from VHDL 87 to VHDL 93 (#131)
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.
2022-07-07 16:00:25 +08:00

514 lines
20 KiB
VHDL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--
-- 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" => -- 10h18h の場合(下位 F-Number
regs_tmp(7 downto 0) := data; -- F-Number
regs_wr <= '1';
when "10" => -- 20h28h の場合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" => -- 30h38h の場合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;