OpenCores
URL https://opencores.org/ocsvn/timerocd/timerocd/trunk

Subversion Repositories timerocd

[/] [timerocd/] [trunk/] [src/] [TimerOCD.vhd] - Rev 2

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------------
--	File name : TimerOCD.vhd
--------------------------------------------------------------------------------
--	Copyright (C) 2015 Donna Whisnant/Dewtronics.
--	Contact: http://www.dewtronics.com/
--
--	This file may be used under the terms of the GNU Lesser General Public License
--	version 3.0 as published by the Free Software Foundation and appearing
--	in the files lgpl-3.0.txt/gpl-3.0.txt included in the packaging of this file.
--	Please review the following information to ensure the GNU Lesser General
--	Public License version 3.0 requirements will be met:
--	https://www.gnu.org/licenses/lgpl-3.0.html
--	Attribution requested, but not required.
--
--	Target Device: Xilinx Spartan-6 XC6SLX9-2-TQG144
--		Using Numato Mimas Spartan 6 FPGA Development Board
--		http://numato.com/mimas-spartan-6-fpga-development-board.html
--
--
--	Timer Output Compare Driver
--
--	This FPGA/CPLD code serves as a SPI Device and functions as a multichannel
--	Timer Output Compare driver for use in the DRSSTC (Dual Resonance Solid-State
--	Tesla Coil) MIDI Interrupter.
--
--	My original DRSSTC MIDI Interrupter used a series of Arduino ProMini boards,
--	each with a built-in limit of six timer output compare channels, to drive
--	the many note signal timing generation.  But instead of cascading six to
--	eight of those processors per interrupter output, this is an attempt to do
--	it all in a single chip.
--
--	The SPI Interface functions as a slave device with the following stream:
--		Master must send MSbit First:
--
--		Input (9 bytes per data message transfer or 1 byte per command message transfer):
--			* 8-bit Command/Address Byte:
--				C7..C0 :
--
--				C7..C0 : Timer Address for Read and Write operations
--					C7 C6 C5 C4 C3 C2 C1 C0
--					-- -- -- -- -- -- -- --
--					|  |  |  |  |  |  |  |
--					|  |  +--+--+--+--+--+----- Timer Address 0-63 0x00-0x3F (000000-111111) (For Data Mode)
--					|  |						Commands (for Command Mode):
--					|  |							000000 = Reset (performs power-on reset equivalent) on Write (Read has no effect)
--					|  |							000001 = RESERVED
--					|  |							000010 = Switch to Note/Power/PitchBend Run mode (initial default) on Write (Read has no effect)
--					|  |							000011 = Switch to Timer On/Off Compare Value Run mode on Write (Read has no effect)
--					|  |							000100 = Bank Select 0 (Notes 0-63) for future transfers on Write (Read has no effect)
--					|  |							000101 = Bank Select 1 (Notes 64-127) for future transfers on Write (Read has no effect)
--					|  |							000110 = Bank Select 2 (Notes 128-191) for future transfers on Write (Read has no effect)
--					|  |							000111 = Bank Select 3 (Notes 192-255) for future transfers on Write (Read has no effect)
--					|  |							001xxx = TBD/RESERVED
--					|  |                              Note: on 00xxxx Commands (above) and with Timer Address in the Data Mode the Concurrent
--					|  |                                     Read Nybble with the command send will be as follows:
--					|  |                                  0 m b b
--					|  |                                  - - - -
--					|  |                                  | | | |
--					|  |                                  | | +-+-- Currently Selected Bank
--					|  |                                  | |           00 = Bank 0
--					|  |                                  | |           01 = Bank 1
--					|  |                                  | |           10 = Bank 2
--					|  |                                  | |           11 = Bank 3
--					|  |                                  | |
--					|  |                                  | +------ RunMode : 0 = Note/Power/PitchBend, 1 = Timer On/Off Compare Values
--					|  |                                  |
--					|  |                                  +-------- Reserved for 001xxx command, TBD, will be sent as 0
--					|  |
--					|  |							01xxxx = Read Push Button Switch 0-3 Status (Read Nybble is concurrent with sending command byte, Write has no effect)
--					|  |							10xxxx = Read/Set LEDs 0-3 (Read Nybble is concurrent with sending command byte)
--					|  |							11xxxx = Read/Set LEDs 4-7 (Read Nybble is concurrent with sending command byte)
--					|  |
--					|  +----------------------- Command/Data Select (0=Data, 1=Command)
--					|
--					+-------------------------- R/W Mode Select
--													0 = Write new value to TimerOCD and concurrently Read present value from TimerOCD
--													1 = Read present value from TimerOCD (Data received during read operation is ignored)
--
--		Note/Power/PitchBend Run Mode
--		-----------------------------
--			* 16-Bit : 7-Bit Note/Key and 7-Bit Power/Velocity for Left Channel:  (Data Mode Only)
--				0,N6..N0,0,P6..P0 :
--
--					0  N6 N5 N4 N3 N2 N1 N0
--					-- -- -- -- -- -- -- --
--					|  |  |  |  |  |  |  |
--					|  +--+--+--+--+--+--+-- Note/Key 0-127 0x00-0x7F (0000000-1111111) - Left Channel
--					|
--					+----------------------- Unused
--
--					0  P6 P5 P4 P3 P2 P1 P0
--					-- -- -- -- -- -- -- --
--					|  |  |  |  |  |  |  |
--					|  +--+--+--+--+--+--+-- Power/Velocity 0-127 0x00-0x7F (0000000-1111111) - Left Channel
--					|
--					+----------------------- Unused
--
--			* 16-Bit : 14-Bit Pitch Bend for Left Channel:  (Data Mode Only)
--				0,0,B13..B0 :
--
--					0  0  B13 B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0
--					-- -- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|  |  |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					|  |  +---+---+---+---+--+--+--+--+--+--+--+--+--+-- Pitch Bend 0000/2000(NoBend)/3FFF - Left Channel
--					|  |
--					+--+------------------------------------------------ Unused
--
--
--			* 16-Bit : 7-Bit Note/Key and 7-Bit Power/Velocity for Right Channel:  (Data Mode Only)
--				0,N6..N0,0,P6..P0 :
--
--					0  N6 N5 N4 N3 N2 N1 N0
--					-- -- -- -- -- -- -- --
--					|  |  |  |  |  |  |  |
--					|  +--+--+--+--+--+--+-- Note/Key 0-127 0x00-0x7F (0000000-1111111) - Right Channel
--					|
--					+----------------------- Unused
--
--					0  P6 P5 P4 P3 P2 P1 P0
--					-- -- -- -- -- -- -- --
--					|  |  |  |  |  |  |  |
--					|  +--+--+--+--+--+--+-- Power/Velocity 0-127 0x00-0x7F (0000000-1111111) - Right Channel
--					|
--					+----------------------- Unused
--
--			* 16-Bit : 14-Bit Pitch Bend for Right Channel:  (Data Mode Only)
--				0,0,B13..B0 :
--
--					0  0  B13 B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0
--					-- -- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|  |  |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					|  |  +---+---+---+---+--+--+--+--+--+--+--+--+--+-- Pitch Bend 0000/2000(NoBend)/3FFF - Right Channel
--					|  |
--					+--+------------------------------------------------ Unused
--
--
--			>> A Note Value of 0 or Power Level of 0 is considered "OFF" and will disable the timer compare channel
--
--
--		Timer On/Off Value Run Mode
--		---------------------------
--			* 16-Bit : Timer ON Value for Left Channel:		(Data Mode Only)
--				T15..T0 :
--
--					T15 T14 T13 T12 T11 T10 T9 T8 T7 T6 T5 T4 T3 T2 T1 T0
--					--- --- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|   |   |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					+---+---+---+---+---+---+--+--+--+--+--+--+--+--+--+--	Timer ON Value 0x0000 - 0xFFFF - Left Channel
--
--
--			* 16-Bit : Timer OFF Value for Left Channel:	(Data Mode Only)
--				T15..T0 :
--
--					T15 T14 T13 T12 T11 T10 T9 T8 T7 T6 T5 T4 T3 T2 T1 T0
--					--- --- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|   |   |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					+---+---+---+---+---+---+--+--+--+--+--+--+--+--+--+--	Timer OFF Value 0x0000 - 0xFFFF - Left Channel
--
--
--			* 16-Bit : Timer ON Value for Right Channel:	(Data Mode Only)
--				T15..T0 :
--
--					T15 T14 T13 T12 T11 T10 T9 T8 T7 T6 T5 T4 T3 T2 T1 T0
--					--- --- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|   |   |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					+---+---+---+---+---+---+--+--+--+--+--+--+--+--+--+--	Timer ON Value 0x0000 - 0xFFFF - Right Channel
--
--
--			* 16-Bit : Timer OFF Value for Right Channel:	(Data Mode Only)
--				T15..T0 :
--
--					T15 T14 T13 T12 T11 T10 T9 T8 T7 T6 T5 T4 T3 T2 T1 T0
--					--- --- --- --- --- --- -- -- -- -- -- -- -- -- -- --
--					|   |   |   |   |   |   |  |  |  |  |  |  |  |  |  |
--					+---+---+---+---+---+---+--+--+--+--+--+--+--+--+--+--	Timer OFF Value 0x0000 - 0xFFFF - Right Channel
--
--			>> Writing either Timer On or Timer Off to 0 is considered "OFF" and will disable the timer compare channel
--
--
--------------------------------------------------------------------------------
--
--	tmrMemBlkLeftXX/tmrMemBlkRightXX Memory Mapping:
--
--		Each Configured as 256 x 16-bit True Dual-Port RAM Memory
--			Port A = All Read/Write processing (xferdataproc only)
--			Port B = All Read-Only processing (tmrupdproc only)
--		(Address is 8-bits)
--
--		A7 A6 A5 A4 A3 A2 A1 A0
--		-- -- -- -- -- -- -- --
--		|  |  |  |  |  |  |  |
--		|  |  +--+--+--+--+--+------------	Subtimer Index (0-63) 0x00-0x3F
--		|  |
--		+--+--+---------------------------	Entry Selection:
--												00 = Note and Power Data (as sent/received to/from SPI) (Read/Written on SPI side) (Note in MSB, Power in LSB)
--												01 = Pitch Bend Value (as sent/received to/from SPI) (Read/Written on SPI side) (right-justified)
--												10 = Timer ON Compare Value (Calculated during SPI receive and written then, Read on signal generation timer processing)
--												11 = Timer OFF Compare Value (Calculated during SPI receive and written then, Read on signal generation timer processing)
--
--		Block Mapping:
--		--------------
--			tmrMemBlkLeft00/tmrMemBlkRight00 - Holds notes 0-63
--			tmrMemBlkLeft01/tmrMemBlkRight01 - Holds notes 64-127
--			tmrMemBlkLeft02/tmrMemBlkRight02 - Holds notes 128-191
--			tmrMemBlkLeft03/tmrMemBlkRight03 - Holds notes 192-255
--
--
--	tmrCTMemBlkLeftXX/tmrCTMemBlkRightXX Memory Mapping:
--
--		Each Configured as 64 x 16-bit Simple Dual-Port RAM Memory
--			Port A = All Read/Write processing (used exclusively by tmrupdproc)
--		(Address is 6-bits)
--
--		A5 A4 A3 A2 A1 A0
--		-- -- -- -- -- --
--		|  |  |  |  |  |
--		+--+--+--+--+--+-------	Subtimer Index (0-63) 0x00-0x3F
--
--
--			tmrCTMemBlkLeft00/tmrCTMemBlkRight00 - Holds notes 0-63
--			tmrCTMemBlkLeft01/tmrCTMemBlkRight01 - Holds notes 64-127
--			tmrCTMemBlkLeft02/tmrCTMemBlkRight02 - Holds notes 128-191
--			tmrCTMemBlkLeft03/tmrCTMemBlkRight03 - Holds notes 192-255
--
--------------------------------------------------------------------------------
--
--	cmpFreqMemBlk Memory Mapping:
--
--		Timer Output Compare FREQ Values
--			(OFF Values) = (FREQ Value) - (ON Value)
--
--		Configured as 128 x 16-bit Dual Port ROM Memory
--		(Address is 7-bits)
--
--		A6 A5 A4 A3 A2 A1 A0
--		-- -- -- -- -- -- --
--		|  |  |  |  |  |  |
--		+--+--+--+--+--+--+------	Note Value 0-127 (0 is reserved for output disabled compare value of 0)
--
--
--------------------------------------------------------------------------------
--
--	cmpOnMemBlk Memory Mapping:
--
--		Timer Output Compare ON Values
--
--		Configured as 16384 x 16-bit Dual Port ROM Memory
--		(Address is 14-bits)
--
--		A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
--		--- --- --- --- -- -- -- -- -- -- -- -- -- --
--		|   |   |   |   |  |  |  |  |  |  |  |  |  |
--		|   |   |   |   |  |  |  +--+--+--+--+--+--+------	Note Value 0-127 (0 is reserved for output disabled compare value of 0)
--		|   |   |   |   |  |  |
--		+---+---+---+---+--+--+---------------------------	Power Value 0-127 (0 is reserved for output disabled compare value of 0)
--
--------------------------------------------------------------------------------
--
-- For PitchBend calculations, the top-two bits (bit B13 and B12) of the PitchBend determine which note
--		compare stores are used in the PitchBend calculation as follows:
--
--			B13 B12
--			--- ---
--			0   0		-- Use Note-2 and Note-1
--			0   1		-- Use Note-1 and Note
--			1   0		-- Use Note and Note+1
--			1   1		-- Use Note+1 and Note+2
--
--		Notes in the extremes of the Note values (i.e. outside of the meaningful
--			88 note piano range where our compares wrap anyway) are excluded from
--			the pitch bend calculations
--
--
--------------------------------------------------------------------------------
 
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
 
library UNISIM;
use UNISIM.VCOMPONENTS.all;
 
--------------------------------------------------------------------------------
-- ENTITY DECLARATION
--------------------------------------------------------------------------------
entity TimerOCD is
	port (
		reset_n: in STD_LOGIC;					-- Master reset (Active Low)
		SPI_SS_n_in: in STD_LOGIC;				-- SPI Slave Select In (Active Low)
		SPI_CLK_in: in STD_LOGIC;				-- SPI Clock In
		SPI_MOSI_in: in STD_LOGIC;				-- SPI Master Out, Slave In
		SPI_MISO: out STD_LOGIC := 'Z';			-- SPI Master In, Slave Out
		FiberOut: out STD_LOGIC_VECTOR(1 downto 0) := (OTHERS => '0');		-- Fiber Optic Output Drive Left/Right Channels
		---
		---										Numato Mimas Spartan6 Module I/O Mapping:
		GCLK_in: in STD_LOGIC;					-- Master System Clock (GCLK)
		SW0: in STD_LOGIC;						-- Push-Button Switch 0
		SW1: in STD_LOGIC;						-- Push-Button Switch 1
		SW2: in STD_LOGIC;						-- Push-Button Switch 2
		SW3: in STD_LOGIC;						-- Push-Button Switch 3
		LED: out STD_LOGIC_VECTOR(7 downto 0)	-- LEDs 0 - 7
	);
end TimerOCD;
 
--------------------------------------------------------------------------------
-- ARCHITECTURE DECLARATION
--------------------------------------------------------------------------------
architecture TimerOCD_arch of TimerOCD is
	constant tmrclkPrescaler : INTEGER := 64;	-- Output Timer Clock Prescaler (Based on incoming GCLK frequency) tmrclk = 1.5625MHz
	constant LEDpostPrescaler : INTEGER := 6250000;		-- 1/8th of a second pulse per LED with 100MHz GCLK
 
	constant numTimers : INTEGER := 256;		-- Number of Timers Supported
	constant numTimerBits : INTEGER := 8;		-- Number of Timer Address Bits
	constant numSubTimers : INTEGER := 64;		-- Number of Timers per Block
	constant numSubTimerBits : INTEGER := 6;	-- Number of SubTimer Address Bits
	constant numTimerBlocks : INTEGER := 4;		-- Number of Timer Blocks
	constant numTimerBlockBits : INTEGER := 2;	-- Number of Timer Block Address Bits
	constant numCommandXferBits : INTEGER := 6;	-- Number of Bit of Address Xfer is Command Bits
	constant numXferBits : INTEGER := 64;		-- Number of bits transmitted/received during SPI data exchange
	constant numOutputs : INTEGER := 2;			-- Number of Outputs Supported
	constant LEFT_OUT_NDX : INTEGER := 0;		-- Left Output Index for array processing
	constant RIGHT_OUT_NDX : INTEGER := 1;		-- Right Output Index for array processing
	constant EVEN_NDX : STD_LOGIC := '0';		-- tmrupdproc Even-Half Index for array processing
	constant ODD_NDX : STD_LOGIC := '1';		-- tmrupdproc Odd-Half Index for array processing
 
	constant adrNotePower : STD_LOGIC_VECTOR := "00";	-- Note/Power address slots
	constant adrPitchBend : STD_LOGIC_VECTOR := "01";	-- PitchBend address slots
	constant adrTimeON : STD_LOGIC_VECTOR := "10";		-- Timer ON Compare address slots
	constant adrTimeOFF : STD_LOGIC_VECTOR := "11";		-- Timer OFF Compare address slots
 
	constant LOWERNOTE_INDEX : INTEGER := 21;	-- First "Piano" note index (i.e. lowest one considered in pitchbend)
	constant UPPERNOTE_INDEX : INTEGER := 108;	-- Last "Piano" note index (i.e. highest one considered in pitchbend)
 
	-- Conversion Functions:
	function TmrBlock(constant nAddrBlock : STD_LOGIC_VECTOR(numTimerBlockBits-1 downto 0)) return INTEGER is
	begin
		return CONV_INTEGER(nAddrBlock);
	end function TmrBlock;
 
	function Tmr(constant nSubTimer : STD_LOGIC_VECTOR(numSubTimerBits-1 downto 0)) return INTEGER is
	begin
		return CONV_INTEGER(nSubTimer);
	end function Tmr;
 
	function Tmr(constant nOddEven : STD_LOGIC; constant nSubTimerUpd : STD_LOGIC_VECTOR(numSubTimerBits-2 downto 0)) return INTEGER is
	begin
		return CONV_INTEGER(nOddEven & nSubTimerUpd);
	end function Tmr;
 
	component BUFG is
		port (
			I : in STD_ULOGIC;
			O : out STD_ULOGIC
		);
	end component;
 
	component timer_memblk
		port (
			clka : in STD_LOGIC;
			ena : in STD_LOGIC;
			wea : in STD_LOGIC_VECTOR(0 downto 0);
			addra : in STD_LOGIC_VECTOR(7 downto 0);
			dina : in STD_LOGIC_VECTOR(15 downto 0);
			douta : out STD_LOGIC_VECTOR(15 downto 0);
			----
			clkb : in STD_LOGIC;
			enb : in STD_LOGIC;
			web : in STD_LOGIC_VECTOR(0 downto 0);
			addrb : in STD_LOGIC_VECTOR(7 downto 0);
			dinb : in STD_LOGIC_VECTOR(15 downto 0);
			doutb : out STD_LOGIC_VECTOR(15 downto 0)
		);
	end component;
 
	component timerCT_memblk
		port (
			clka : in STD_LOGIC;
			ena : in STD_LOGIC;
			wea : in STD_LOGIC_VECTOR(0 downto 0);
			addra : in STD_LOGIC_VECTOR(5 downto 0);
			dina : in STD_LOGIC_VECTOR(15 downto 0);
			clkb : in STD_LOGIC;
			enb : in STD_LOGIC;
			addrb : in STD_LOGIC_VECTOR(5 downto 0);
			doutb : out STD_LOGIC_VECTOR(15 downto 0)
		);
	end component;
 
	component cmpFreqData_memblk
		port (
			clka : in STD_LOGIC;
			addra : in STD_LOGIC_VECTOR(6 downto 0);
			douta : out STD_LOGIC_VECTOR(15 downto 0);
			clkb : IN STD_LOGIC;
			addrb : IN STD_LOGIC_VECTOR(6 downto 0);
			doutb : OUT STD_LOGIC_VECTOR(15 downto 0)
		);
	end component;
 
	component cmpOnData_memblk
		port (
			clka : in STD_LOGIC;
			addra : in STD_LOGIC_VECTOR(13 downto 0);
			douta : out STD_LOGIC_VECTOR(15 downto 0);
			clkb : IN STD_LOGIC;
			addrb : IN STD_LOGIC_VECTOR(13 downto 0);
			doutb : OUT STD_LOGIC_VECTOR(15 downto 0)
		);
	end component;
 
	component InterpolateMultAdd
		port (
			a : in STD_LOGIC_VECTOR(15 downto 0);
			b : in STD_LOGIC_VECTOR(11 downto 0);
			c : in STD_LOGIC_VECTOR(27 downto 0);
			subtract : in STD_LOGIC;
			p : out STD_LOGIC_VECTOR(27 downto 12);
			pcout : out STD_LOGIC_VECTOR(47 downto 0)
		);
	end component;
 
	-- Master Timer:
	signal theTimer : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');
 
	-- The Outputs:
	--		This is the value of all output signals and their corresponding "pseudo" outputs forming the combined output.
	--		Each output will be generated by the 'OR' of all values for the numTimers associated with that output:
	type TOutputBlockArray is array (0 to numTimerBlocks-1) of STD_LOGIC_VECTOR(numSubTimers-1 downto 0);
	type TOutputArray is array (0 to numOutputs-1) of TOutputBlockArray;
	signal outputLevels : TOutputArray;			-- Toggles during On/Off transitions
	signal outputEnables : TOutputArray;		-- Set to '1' when timer loaded with valid compares and is enabled
	signal outputEnablesNext : TOutputArray;	-- Next state for outputEnables, set in xferdataproc and transferred to outputEnables in tmrupdproc when a timer compare occurs
	signal outputForceLoad : TOutputArray;		-- Toggled in the xferdataproc when receiving a timer value causing the tmrupdproc to force-load that timer during the next timer processing cycle
	signal outputForceLoad_done : TOutputArray;	-- Toggled when the tmrupdproc processes an OutputForceLoad flag
 
	signal LEDenable : STD_LOGIC_VECTOR(7 downto 0);			-- LED Outputs, set in RxData Processing
	signal LEDSet : STD_LOGIC;									-- Set to '1' whenever any LED value is written via SPI, used to switch from LEDpost to LEDenable mode
	signal LEDpost : STD_LOGIC_VECTOR(7 downto 0);				-- LED POST drive
	signal LEDpostScalerCount : STD_LOGIC_VECTOR(31 downto 0);	-- POST timer clock/counter
	signal LEDpostPulse : STD_LOGIC;							-- LED POST Pulse generated on scaler counter rollover, used to strobe LEDpost to the next state
 
	signal runMode : STD_LOGIC := '0';			-- Run Mode : 0 = Note/Power/PitchBend,  1 = Timer On/Off (via SPI command)
	constant rmodeNote : STD_LOGIC := '0';
	constant rmodeTime : STD_LOGIC := '1';
 
	-- SPI Connection:
	signal SPI_SS_n : STD_LOGIC;												-- Preprocessed SPI_SS
	signal SPI_CLK : STD_LOGIC;													-- Preprocessed SPI_CLK
	signal SPI_MOSI : STD_LOGIC;												-- Preprocessed SPI_MOSI
	signal SPI_addr : STD_LOGIC_VECTOR(7 downto 0) := (OTHERS => '0');			-- Current Address for SPI Read/Write
	signal SPI_addr_latch : STD_LOGIC := '0';									-- Address Latch enable from SPI
	signal SPI_rx_data : STD_LOGIC_VECTOR(numXferBits-1 downto 0);				-- Receive Data for On/Off Timer Values by Note/Velocity
	signal SPI_tx_data : STD_LOGIC_VECTOR(numXferBits-1 downto 0);				-- Transmit Data for On/Off Timer Values by Note/Velocity
	signal SPI_rrdy : STD_LOGIC := '0';											-- Receive Data Ready (when new command is available)
	signal SPI_cmd_addr : STD_LOGIC_VECTOR(2 downto 0) := (OTHERS => '0');		-- Command Address portion of Current Address for SPI Read/Write
	signal SPI_txcmd_data : STD_LOGIC_VECTOR(3 downto 0);						-- Transmit Command Data generated on SPI_cmd_data_latch synchronization logic in response to command byte
	signal SPI_busy : STD_LOGIC;												-- Cooked and Buffered (i.e. clock synchronized) SPI busy
 
	-- Timer MemBlk Connections:
	type TTimerAddrBus is array (0 to numTimerBlocks-1) of STD_LOGIC_VECTOR(7 downto 0);
	type TTimerDataBus is array (0 to numTimerBlocks-1) of STD_LOGIC_VECTOR(15 downto 0);
	-----------------------------------
	signal tmrMemBlkLeft_CLKXfer : STD_LOGIC;									-- PORT A : xferdataproc Processing (All Synchronous R/W)
	signal tmrMemBlkLeft_ENXfer : STD_LOGIC;
	signal tmrMemBlkLeft_WEXfer : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);
	signal tmrMemBlkLeft_ADDRXfer : STD_LOGIC_VECTOR(7 downto 0);
	signal tmrMemBlkLeft_DINXfer : TTimerDataBus;
	signal tmrMemBlkLeft_DOUTXfer : TTimerDataBus;
 
	signal tmrMemBlkLeft_CLKTmr : STD_LOGIC;									-- PORT B : tmrupdproc Processing (All Synchronous Read-Only)
	signal tmrMemBlkLeft_ENTmr : STD_LOGIC;
	signal tmrMemBlkLeft_ADDRTmr : TTimerAddrBus;
	signal tmrMemBlkLeft_DOUTTmr : TTimerDataBus;
 
	signal tmrMemBlkLeft_WE_GATE : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);		-- Port Write Enable Gate for xferdataproc processing
	-----------------------------------
	signal tmrMemBlkRight_CLKXfer : STD_LOGIC;									-- PORT A : xferdataproc Processing (All Synchronous R/W)
	signal tmrMemBlkRight_ENXfer : STD_LOGIC;
	signal tmrMemBlkRight_WEXfer : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);
	signal tmrMemBlkRight_ADDRXfer : STD_LOGIC_VECTOR(7 downto 0);
	signal tmrMemBlkRight_DINXfer : TTimerDataBus;
	signal tmrMemBlkRight_DOUTXfer : TTimerDataBus;
 
	signal tmrMemBlkRight_CLKTmr : STD_LOGIC;									-- PORT B : tmrupdproc Processing (All Synchronous Read-Only)
	signal tmrMemBlkRight_ENTmr : STD_LOGIC;
	signal tmrMemBlkRight_ADDRTmr : TTimerAddrBus;
	signal tmrMemBlkRight_DOUTTmr : TTimerDataBus;
 
	signal tmrMemBlkRight_WE_GATE : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);	-- Port Write Enable Gate for xferdataproc processing
 
	-- TimerCT MemBlk Connections:
	signal tmrCTMemBlkLeft_CLKA : STD_LOGIC;
	signal tmrCTMemBlkLeft_ENA : STD_LOGIC;
	signal tmrCTMemBlkLeft_WEA : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);
	signal tmrCTMemBlkLeft_ADDRA : STD_LOGIC_VECTOR(5 downto 0);
	signal tmrCTMemBlkLeft_DINA : TTimerDataBus;
	signal tmrCTMemBlkLeft_CLKB : STD_LOGIC;
	signal tmrCTMemBlkLeft_ENB : STD_LOGIC;
	signal tmrCTMemBlkLeft_ADDRB : STD_LOGIC_VECTOR(5 downto 0);
	signal tmrCTMemBlkLeft_DOUTB : TTimerDataBus;
 
	signal tmrMemBlkLeft_DOUTBpTimer : TTimerDataBus;								-- tmrMemBlkLeft_DOUTTmr Data plus timer from concurrent logic to load into tmrCTMemBlkLeft to reduce memory setup time in tmrupdproc
	signal tmrMemBlkLeft_DINAmuxODD : TTimerDataBus;								-- Multiplexer for tmrMemBlk Left ODD Data In Port A
	signal tmrMemBlkLeft_DINAmuxEVEN : TTimerDataBus;								-- Multiplexer for tmrMemBlk Left EVEN Data In Port A
	signal tmrCTMemBlkLeft_eqTimer : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);	-- Comparison result of tmrCTMemBlkLeft == theTimer, used to reduce memory setup time in tmrupdproc
 
	signal tmrCTMemBlkLeft_WE_GATE : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);	-- Port Write Enable Gate for tmrupdproc processing
	-----------------------------------
	signal tmrCTMemBlkRight_CLKA : STD_LOGIC;
	signal tmrCTMemBlkRight_ENA : STD_LOGIC;
	signal tmrCTMemBlkRight_WEA : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);
	signal tmrCTMemBlkRight_ADDRA : STD_LOGIC_VECTOR(5 downto 0);
	signal tmrCTMemBlkRight_DINA : TTimerDataBus;
	signal tmrCTMemBlkRight_CLKB : STD_LOGIC;
	signal tmrCTMemBlkRight_ENB : STD_LOGIC;
	signal tmrCTMemBlkRight_ADDRB : STD_LOGIC_VECTOR(5 downto 0);
	signal tmrCTMemBlkRight_DOUTB : TTimerDataBus;
 
	signal tmrMemBlkRight_DOUTBpTimer : TTimerDataBus;								-- tmrMemBlkRight_DOUTTmr Data plus timer from concurrent logic to load into tmrCTMemBlkRight to reduce memory setup time in tmrupdproc
	signal tmrMemBlkRight_DINAmuxODD : TTimerDataBus;								-- Multiplexer for tmrMemBlk Right ODD Data In Port A
	signal tmrMemBlkRight_DINAmuxEVEN : TTimerDataBus;								-- Multiplexer for tmrMemBlk Right EVEN Data In Port A
	signal tmrCTMemBlkRight_eqTimer : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);	-- Comparison result of tmrCTMemBlkRight == theTimer, used to reduce memory setup time in tmrupdproc
 
	signal tmrCTMemBlkRight_WE_GATE : STD_LOGIC_VECTOR(numTimerBlocks-1 downto 0);	-- Port Write Enable Gate for tmrupdproc processing
 
	-- Compare Freq MemBlk Connection:
	signal cmpFreqLeftMemBlk_CLK : STD_LOGIC;									-- Port A used by Left Channel
	signal cmpFreqLeftMemBlk_ADDR : STD_LOGIC_VECTOR(6 downto 0);
	signal cmpFreqLeftMemBlk_DOUT : STD_LOGIC_VECTOR(15 downto 0);
	----
	signal cmpFreqRightMemBlk_CLK : STD_LOGIC;									-- Port B used by Right Channel
	signal cmpFreqRightMemBlk_ADDR : STD_LOGIC_VECTOR(6 downto 0);
	signal cmpFreqRightMemBlk_DOUT : STD_LOGIC_VECTOR(15 downto 0);
 
	-- Compare On MemBlk Connection:
	signal cmpOnLeftMemBlk_CLK : STD_LOGIC;										-- Port A used by Left Channel
	signal cmpOnLeftMemBlk_ADDR : STD_LOGIC_VECTOR(13 downto 0);
	signal cmpOnLeftMemBlk_DOUT : STD_LOGIC_VECTOR(15 downto 0);
	-----
	signal cmpOnRightMemBlk_CLK : STD_LOGIC;									-- Port B used by Right Channel
	signal cmpOnRightMemBlk_ADDR : STD_LOGIC_VECTOR(13 downto 0);
	signal cmpOnRightMemBlk_DOUT : STD_LOGIC_VECTOR(15 downto 0);
 
	-- Calculations:
	signal pitchBendLeftNoteAIndex : STD_LOGIC_VECTOR(6 downto 0) := (OTHERS => '0');			-- Lower note index for pitch bend (see comments at top)
	signal pitchBendLeftNoteBIndex : STD_LOGIC_VECTOR(6 downto 0) := (OTHERS => '0');			-- Upper note index for pitch bend (see comments at top)
	signal pitchBendLeftMagnitude : STD_LOGIC_VECTOR(11 downto 0) := (OTHERS => '0');			-- Absolute value of magnitude of pitch bend => DSP InterpolateMultAdd(B)
 
	signal pitchBendLeftCalcFREQAlpha : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- NoteA Compare FREQ Value (lower) => DSP InterpolateMultAdd(C and delta)
	signal pitchBendLeftCalcFREQAlphaLeft : STD_LOGIC_VECTOR(27 downto 0) := (OTHERS => '0');	-- Left Justified NoteA Compare FREQ Value (lower) => DSP InterpolateMultAdd(C), Needed for addition to be "Multiplied" by 2^12
	signal pitchBendLeftCalcFreqBeta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- NoteB Compare FREQ Value (upper) => DSP InterpolateMultAdd(delta)
	signal pitchBendLeftCalcFREQDelta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- Compare FREQ (NoteB-NoteA) => DSP InterpolateMultAdd(A)
	signal pitchBendLeftCalcFREQResult : STD_LOGIC_VECTOR(15 downto 0);							-- Compare FREQ Output from DSP InterpolateMultAdd
 
	signal pitchBendLeftCalcONAlpha : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');			-- NoteA Compare ON Value (lower) => DSP InterpolateMultAdd(C and delta)
	signal pitchBendLeftCalcONAlphaLeft : STD_LOGIC_VECTOR(27 downto 0) := (OTHERS => '0');		-- Left Justified NoteA Compare ON Value (lower) => DSP InterpolateMultAdd(C), Needed for addition to be "Multiplied" by 2^12
	signal pitchBendLeftCalcONBeta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');			-- NoteB Compare ON Value (upper) => DSP InterpolateMultAdd(delta)
	signal pitchBendLeftCalcONDelta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');			-- Compare ON (NoteB-NoteA) => DSP InterpolateMultAdd(A)
	signal pitchBendLeftCalcONResult : STD_LOGIC_VECTOR(15 downto 0);							-- Compare ON Output from DSP InterpolateMultAdd
	-----
	signal pitchBendRightNoteAIndex : STD_LOGIC_VECTOR(6 downto 0) := (OTHERS => '0');			-- Lower note index for pitch bend (see comments at top)
	signal pitchBendRightNoteBIndex : STD_LOGIC_VECTOR(6 downto 0) := (OTHERS => '0');			-- Upper note index for pitch bend (see comments at top)
	signal pitchBendRightMagnitude : STD_LOGIC_VECTOR(11 downto 0) := (OTHERS => '0');			-- Absolute value of magnitude of pitch bend => DSP InterpolateMultAdd(B)
 
	signal pitchBendRightCalcFREQAlpha : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- NoteA Compare FREQ Value (lower) => DSP InterpolateMultAdd(C and delta)
	signal pitchBendRightCalcFREQAlphaLeft : STD_LOGIC_VECTOR(27 downto 0) := (OTHERS => '0');	-- Left Justified NoteA Compare FREQ Value (lower) => DSP InterpolateMultAdd(C), Needed for addition to be "Multiplied" by 2^12
	signal pitchBendRightCalcFREQBeta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- NoteB Compare FREQ Value (upper) => DSP InterpolateMultAdd(delta)
	signal pitchBendRightCalcFREQDelta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- Compare FREQ (NoteB-NoteA) => DSP InterpolateMultAdd(A)
	signal pitchBendRightCalcFREQResult : STD_LOGIC_VECTOR(15 downto 0);						-- Compare FREQ Output from DSP InterpolateMultAdd
 
	signal pitchBendRightCalcONAlpha : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- NoteA Compare ON Value (lower) => DSP InterpolateMultAdd(C and delta)
	signal pitchBendRightCalcONAlphaLeft : STD_LOGIC_VECTOR(27 downto 0) := (OTHERS => '0');	-- Left Justified NoteA Compare ON Value (lower) => DSP InterpolateMultAdd(C), Needed for addition to be "Multiplied" by 2^12
	signal pitchBendRightCalcONBeta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');			-- NoteB Compare ON Value (upper) => DSP InterpolateMultAdd(delta)
	signal pitchBendRightCalcONDelta : STD_LOGIC_VECTOR(15 downto 0) := (OTHERS => '0');		-- Compare ON (NoteB-NoteA) => DSP InterpolateMultAdd(A)
	signal pitchBendRightCalcONResult : STD_LOGIC_VECTOR(15 downto 0);							-- Compare ON Output from DSP InterpolateMultAdd
 
	-- Clocks
	signal GCLK : STD_LOGIC;				-- Buffered GCLK input
	signal tmrclk : STD_LOGIC;				-- Buffered Timer Clock (derived from GCLK based on timer prescaler)
	signal sysclk : STD_LOGIC;				-- Buffered System processing clock (equivalent to GCLK with a separate name reserved for possible scaling)
	signal xferdataclk : STD_LOGIC;			-- Buffered System Clock divided from sysclk for xferdataproc to slow it down enough to meet timing constraints for calculations
 
	signal tmrclk_in: STD_LOGIC;			-- Timer Clock In (Derived from GCLK by dividing by prescaler)
	signal tmrclk_prescaler_count: STD_LOGIC_VECTOR(15 downto 0);		-- tmrclk generation prescaler counter
 
	signal xferdataclk_in: STD_LOGIC := '0';	-- XferData Clock in (Derived from GCLK/sysclk by dividing it to meet timing constraints, currently a divide by 2)
 
	-- Misc
	signal syn_rrdy : STD_LOGIC;			-- Synchronous SPI_rrdy signal
	signal syn_rrdy_armed : STD_LOGIC;		-- Arming for Synchronous SPI_rrdy signal generation.  Used to extend the trigger period to at least one sysclk cycle
	signal syn_rrdy_clr_n : STD_LOGIC;		-- Synchronous rrdy clear signal (asserted when data has been read)
	signal syn_rxdata_trigger_n : STD_LOGIC := '1';		-- Synchronous rxdata processing trigger (asserted to start the rxdata receive process)
	signal syn_rxdata_processing_n : STD_LOGIC := '1';	-- Synchronous rxdata processing signal (asserted when we are processing incoming data)
	signal syn_cmd_processing_n : STD_LOGIC := '1';		-- Synchronous cmd processing signal (asserted hwen rxdataproc is processing the incoming SPI command)
	constant nRxSB : integer := 3;						-- Number of RxData State Bits (used for conversions)
	signal syn_rxdata_state : STD_LOGIC_VECTOR(nRxSB-1 downto 0) := (OTHERS => '0');	-- Synchronous rxdata processing state-machine state (See xferdataproc for details)
	signal syn_rxdata : STD_LOGIC_VECTOR(numXferBits-1 downto 0);		-- Synchronized received buffer to hold the SPI_rx_data before transferring to our internal logic
 
	signal txdata_trigger_n : STD_LOGIC := '1';			-- txdata trigger, set upon the rising edge of SPI_addr_latch, meaning latch address is in progress, reset when txdata_processing_n becomes active or SPI_SS_n deasserts indicating xfer was aborted
	signal txdata_processing_n : STD_LOGIC := '1';		-- txdata processing, set when xferdataproc starts transmitting data (to the buffer) to be sent out over SPI
	constant nTxSB : integer := 2;						-- Number of TxData State Bits (used for conversions)
	signal txdata_state : STD_LOGIC_VECTOR(nTxSB-1 downto 0) := (OTHERS => '0');	-- Synchronous txdata processing state-machine state (See xferdataproc for details)
 
	signal local_rst_n : STD_LOGIC := '1';	-- Local reset to process the reset command from SPI
	signal reset_trigger_n : STD_LOGIC := '1';			-- Reset trigger -- initiated by either reset_n or local_reset_n during sysclk processing of rxproc
	signal reset_in_progress_n : STD_LOGIC := '1';		-- Reset currently is progress -- initiated by reset_trigger_n, cleared when reset is complete
 
	signal tmrproc_trigger_n : STD_LOGIC := '1';		-- Timer Process Trigger - Set by tmrclk event in timerproc, acted on by tmrupdproc
	signal tmrproc_processing_n : STD_LOGIC := '1';		-- Timer Process Processing - Set in tmrupdproc in response to tmrclk to indicate tmrupdproc state-machine is running
	signal tmrproc_tmrndxEVEN : STD_LOGIC_VECTOR(numSubTimerBits-2 downto 0) := (OTHERS => '0');		-- Even Timer Index during the Timer Update Processing loop
	signal tmrproc_tmrndxODD : STD_LOGIC_VECTOR(numSubTimerBits-2 downto 0) := (OTHERS => '0');			-- Odd Timer Index during the Timer Update Processing loop
	signal tmrproc_tmrndxEVENLock : STD_LOGIC_VECTOR(numSubTimerBits-2 downto 0) := (OTHERS => '0');	-- Even Timer Index Locked during the Timer Output Update Processing loop
	signal tmrproc_tmrndxODDLock : STD_LOGIC_VECTOR(numSubTimerBits-2 downto 0) := (OTHERS => '0');		-- Odd Timer Index Locked during the Timer Output Update Processing loop
	constant nTSB : integer := 1;						-- Number of Timer State Bits (used for conversions)
	signal tmrproc_state : STD_LOGIC_VECTOR(nTSB-1 downto 0) := (OTHERS => '0');				-- Timer Update Processing state machine (See tmrupdproc for details)
 
	signal spi_xfer_mode : STD_LOGIC := '0';			-- Command/Data mode from SPI address (0=Data, 1=Command)
	signal syn_xfer_mode : STD_LOGIC := '0';			-- Latched version of spi_xfer_mode.  Latched at start of rxdata/xferdata proc in case new address starts transferring while we are still processing
	constant xmodeData : STD_LOGIC := '0';
	constant xmodeCmd : STD_LOGIC := '1';
	signal addr_subtimer : STD_LOGIC_VECTOR(numSubTimerBits-1 downto 0) := (OTHERS => '0');			-- SubTimer portion of Address received from SPI
	signal syn_addr_subtimer : STD_LOGIC_VECTOR(numSubTimerBits-1 downto 0) := (OTHERS => '0');		-- Latched version of addr_subtimer.  Latched at start of rxdata/xferdata proc in case new address starts transferring while we are still processing
	signal addr_block : STD_LOGIC_VECTOR(numTimerBlockBits-1 downto 0) := (OTHERS => '0');			-- Block address sent via Block Select Command
	signal spi_command : STD_LOGIC_VECTOR(numCommandXferBits-1 downto 0) := (OTHERS => '0');		-- Command received from SPI (when xfer_mode = 1)
	signal spi_rw : STD_LOGIC := '0';					-- SPI R/W Mode (0=Write, 1=Read)
	signal syn_rw : STD_LOGIC := '0';					-- Latched version of spi_rw.  Latched at start of rxdata/xferdata proc in case new address starts transferring while we are still processing
 
	-- Receive Data clarity mapping (Populated with data from syn_rxdata to make the code read better rather than dealing with arbitrary bit numbers everywhere):
	signal synrx_leftNote : STD_LOGIC_VECTOR(6 downto 0);
	signal synrx_leftPower : STD_LOGIC_VECTOR(6 downto 0);
	signal synrx_leftPitchBend : STD_LOGIC_VECTOR(13 downto 0);
	signal synrx_rightNote : STD_LOGIC_VECTOR(6 downto 0);
	signal synrx_rightPower : STD_LOGIC_VECTOR(6 downto 0);
	signal synrx_rightPitchBend : STD_LOGIC_VECTOR(13 downto 0);
	signal synrx_leftTimerON : STD_LOGIC_VECTOR(15 downto 0);
	signal synrx_leftTimerOFF : STD_LOGIC_VECTOR(15 downto 0);
	signal synrx_rightTimerON : STD_LOGIC_VECTOR(15 downto 0);
	signal synrx_rightTimerOFF : STD_LOGIC_VECTOR(15 downto 0);
 
begin
	-- Handle Clock Buffering:
	bufg_tmrclk : BUFG
		port map (
			O => tmrclk,
			I => tmrclk_in
		);
--	tmrclk <= tmrclk_in;
 
	bufg_xferdataclk : BUFG
		port map (
			O => xferdataclk,
			I => xferdataclk_in
		);
--	xferdataclk <= xferdataclk_in;
 
	bufg_GCLK : BUFG
		port map (
			O => GCLK,
			I => GCLK_in
		);
--	GCLK <= GCLK_in;
 
	-- System Clock doesn't use prescaler:
	sysclk <= GCLK;
 
	-- Incoming SPI Entity:
	iSPI : entity work.spi_slave
	generic map (
		cpol => '0',
		cpha => '0',
		d_width => numXferBits
	)
	port map (
		sync_clk => sysclk,
		reset_n => reset_n,
		ss_n => SPI_SS_n,
		sclk => SPI_CLK,
		mosi => SPI_MOSI,
		miso => SPI_MISO,
		rrdy => SPI_rrdy,
		rx_data => SPI_rx_data,
		tx_load_data => SPI_tx_data,
		addr => SPI_addr,
		addr_latch => SPI_addr_latch,
		cmd_load_data => SPI_txcmd_data,
		busy => SPI_busy
	);
 
	-- Timer MemBlk Entities:
	tmrMemBlkLeft00 : timer_memblk
		port map (
			clka => tmrMemBlkLeft_CLKXfer,
			ena => tmrMemBlkLeft_ENXfer,
			wea => tmrMemBlkLeft_WEXfer(0 downto 0),
			addra => tmrMemBlkLeft_ADDRXfer,
			dina => tmrMemBlkLeft_DINXfer(0),
			douta => tmrMemBlkLeft_DOUTXfer(0),
			----
			clkb => tmrMemBlkLeft_CLKTmr,
			enb => tmrMemBlkLeft_ENTmr,
			web => "0",
			addrb => tmrMemBlkLeft_ADDRTmr(0),
			dinb => "0000000000000000",
			doutb => tmrMemBlkLeft_DOUTTmr(0)
		);
 
	tmrMemBlkRight00 : timer_memblk
		port map (
			clka => tmrMemBlkRight_CLKXfer,
			ena => tmrMemBlkRight_ENXfer,
			wea => tmrMemBlkRight_WEXfer(0 downto 0),
			addra => tmrMemBlkRight_ADDRXfer,
			dina => tmrMemBlkRight_DINXfer(0),
			douta => tmrMemBlkRight_DOUTXfer(0),
			----
			clkb => tmrMemBlkRight_CLKTmr,
			enb => tmrMemBlkRight_ENTmr,
			web => "0",
			addrb => tmrMemBlkRight_ADDRTmr(0),
			dinb => "0000000000000000",
			doutb => tmrMemBlkRight_DOUTTmr(0)
		);
 
	tmrMemBlkLeft01 : timer_memblk
		port map (
			clka => tmrMemBlkLeft_CLKXfer,
			ena => tmrMemBlkLeft_ENXfer,
			wea => tmrMemBlkLeft_WEXfer(1 downto 1),
			addra => tmrMemBlkLeft_ADDRXfer,
			dina => tmrMemBlkLeft_DINXfer(1),
			douta => tmrMemBlkLeft_DOUTXfer(1),
			----
			clkb => tmrMemBlkLeft_CLKTmr,
			enb => tmrMemBlkLeft_ENTmr,
			web => "0",
			addrb => tmrMemBlkLeft_ADDRTmr(1),
			dinb => "0000000000000000",
			doutb => tmrMemBlkLeft_DOUTTmr(1)
		);
 
	tmrMemBlkRight01 : timer_memblk
		port map (
			clka => tmrMemBlkRight_CLKXfer,
			ena => tmrMemBlkRight_ENXfer,
			wea => tmrMemBlkRight_WEXfer(1 downto 1),
			addra => tmrMemBlkRight_ADDRXfer,
			dina => tmrMemBlkRight_DINXfer(1),
			douta => tmrMemBlkRight_DOUTXfer(1),
			----
			clkb => tmrMemBlkRight_CLKTmr,
			enb => tmrMemBlkRight_ENTmr,
			web => "0",
			addrb => tmrMemBlkRight_ADDRTmr(1),
			dinb => "0000000000000000",
			doutb => tmrMemBlkRight_DOUTTmr(1)
		);
 
	tmrMemBlkLeft02 : timer_memblk
		port map (
			clka => tmrMemBlkLeft_CLKXfer,
			ena => tmrMemBlkLeft_ENXfer,
			wea => tmrMemBlkLeft_WEXfer(2 downto 2),
			addra => tmrMemBlkLeft_ADDRXfer,
			dina => tmrMemBlkLeft_DINXfer(2),
			douta => tmrMemBlkLeft_DOUTXfer(2),
			----
			clkb => tmrMemBlkLeft_CLKTmr,
			enb => tmrMemBlkLeft_ENTmr,
			web => "0",
			addrb => tmrMemBlkLeft_ADDRTmr(2),
			dinb => "0000000000000000",
			doutb => tmrMemBlkLeft_DOUTTmr(2)
		);
 
	tmrMemBlkRight02 : timer_memblk
		port map (
			clka => tmrMemBlkRight_CLKXfer,
			ena => tmrMemBlkRight_ENXfer,
			wea => tmrMemBlkRight_WEXfer(2 downto 2),
			addra => tmrMemBlkRight_ADDRXfer,
			dina => tmrMemBlkRight_DINXfer(2),
			douta => tmrMemBlkRight_DOUTXfer(2),
			----
			clkb => tmrMemBlkRight_CLKTmr,
			enb => tmrMemBlkRight_ENTmr,
			web => "0",
			addrb => tmrMemBlkRight_ADDRTmr(2),
			dinb => "0000000000000000",
			doutb => tmrMemBlkRight_DOUTTmr(2)
		);
 
	tmrMemBlkLeft03 : timer_memblk
		port map (
			clka => tmrMemBlkLeft_CLKXfer,
			ena => tmrMemBlkLeft_ENXfer,
			wea => tmrMemBlkLeft_WEXfer(3 downto 3),
			addra => tmrMemBlkLeft_ADDRXfer,
			dina => tmrMemBlkLeft_DINXfer(3),
			douta => tmrMemBlkLeft_DOUTXfer(3),
			----
			clkb => tmrMemBlkLeft_CLKTmr,
			enb => tmrMemBlkLeft_ENTmr,
			web => "0",
			addrb => tmrMemBlkLeft_ADDRTmr(3),
			dinb => "0000000000000000",
			doutb => tmrMemBlkLeft_DOUTTmr(3)
		);
 
	tmrMemBlkRight03 : timer_memblk
		port map (
			clka => tmrMemBlkRight_CLKXfer,
			ena => tmrMemBlkRight_ENXfer,
			wea => tmrMemBlkRight_WEXfer(3 downto 3),
			addra => tmrMemBlkRight_ADDRXfer,
			dina => tmrMemBlkRight_DINXfer(3),
			douta => tmrMemBlkRight_DOUTXfer(3),
			----
			clkb => tmrMemBlkRight_CLKTmr,
			enb => tmrMemBlkRight_ENTmr,
			web => "0",
			addrb => tmrMemBlkRight_ADDRTmr(3),
			dinb => "0000000000000000",
			doutb => tmrMemBlkRight_DOUTTmr(3)
		);
 
	-- TimerCT MemBlk Entities:
	tmrCTMemBlkLeft00 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkLeft_CLKA,
			ena => tmrCTMemBlkLeft_ENA,
			wea => tmrCTMemBlkLeft_WEA(0 downto 0),
			addra => tmrCTMemBlkLeft_ADDRA,
			dina => tmrCTMemBlkLeft_DINA(0),
			clkb => tmrCTMemBlkLeft_CLKB,
			enb => tmrCTMemBlkLeft_ENB,
			addrb => tmrCTMemBlkLeft_ADDRB,
			doutb => tmrCTMemBlkLeft_DOUTB(0)
		);
 
	tmrCTMemBlkRight00 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkRight_CLKA,
			ena => tmrCTMemBlkRight_ENA,
			wea => tmrCTMemBlkRight_WEA(0 downto 0),
			addra => tmrCTMemBlkRight_ADDRA,
			dina => tmrCTMemBlkRight_DINA(0),
			clkb => tmrCTMemBlkRight_CLKB,
			enb => tmrCTMemBlkRight_ENB,
			addrb => tmrCTMemBlkRight_ADDRB,
			doutb => tmrCTMemBlkRight_DOUTB(0)
		);
 
	tmrCTMemBlkLeft01 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkLeft_CLKA,
			ena => tmrCTMemBlkLeft_ENA,
			wea => tmrCTMemBlkLeft_WEA(1 downto 1),
			addra => tmrCTMemBlkLeft_ADDRA,
			dina => tmrCTMemBlkLeft_DINA(1),
			clkb => tmrCTMemBlkLeft_CLKB,
			enb => tmrCTMemBlkLeft_ENB,
			addrb => tmrCTMemBlkLeft_ADDRB,
			doutb => tmrCTMemBlkLeft_DOUTB(1)
		);
 
	tmrCTMemBlkRight01 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkRight_CLKA,
			ena => tmrCTMemBlkRight_ENA,
			wea => tmrCTMemBlkRight_WEA(1 downto 1),
			addra => tmrCTMemBlkRight_ADDRA,
			dina => tmrCTMemBlkRight_DINA(1),
			clkb => tmrCTMemBlkRight_CLKB,
			enb => tmrCTMemBlkRight_ENB,
			addrb => tmrCTMemBlkRight_ADDRB,
			doutb => tmrCTMemBlkRight_DOUTB(1)
		);
 
	tmrCTMemBlkLeft02 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkLeft_CLKA,
			ena => tmrCTMemBlkLeft_ENA,
			wea => tmrCTMemBlkLeft_WEA(2 downto 2),
			addra => tmrCTMemBlkLeft_ADDRA,
			dina => tmrCTMemBlkLeft_DINA(2),
			clkb => tmrCTMemBlkLeft_CLKB,
			enb => tmrCTMemBlkLeft_ENB,
			addrb => tmrCTMemBlkLeft_ADDRB,
			doutb => tmrCTMemBlkLeft_DOUTB(2)
		);
 
	tmrCTMemBlkRight02 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkRight_CLKA,
			ena => tmrCTMemBlkRight_ENA,
			wea => tmrCTMemBlkRight_WEA(2 downto 2),
			addra => tmrCTMemBlkRight_ADDRA,
			dina => tmrCTMemBlkRight_DINA(2),
			clkb => tmrCTMemBlkRight_CLKB,
			enb => tmrCTMemBlkRight_ENB,
			addrb => tmrCTMemBlkRight_ADDRB,
			doutb => tmrCTMemBlkRight_DOUTB(2)
		);
 
	tmrCTMemBlkLeft03 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkLeft_CLKA,
			ena => tmrCTMemBlkLeft_ENA,
			wea => tmrCTMemBlkLeft_WEA(3 downto 3),
			addra => tmrCTMemBlkLeft_ADDRA,
			dina => tmrCTMemBlkLeft_DINA(3),
			clkb => tmrCTMemBlkLeft_CLKB,
			enb => tmrCTMemBlkLeft_ENB,
			addrb => tmrCTMemBlkLeft_ADDRB,
			doutb => tmrCTMemBlkLeft_DOUTB(3)
		);
 
	tmrCTMemBlkRight03 : timerCT_memblk
		port map (
			clka => tmrCTMemBlkRight_CLKA,
			ena => tmrCTMemBlkRight_ENA,
			wea => tmrCTMemBlkRight_WEA(3 downto 3),
			addra => tmrCTMemBlkRight_ADDRA,
			dina => tmrCTMemBlkRight_DINA(3),
			clkb => tmrCTMemBlkRight_CLKB,
			enb => tmrCTMemBlkRight_ENB,
			addrb => tmrCTMemBlkRight_ADDRB,
			doutb => tmrCTMemBlkRight_DOUTB(3)
		);
 
	-- Compare Off MemBlk Entity:
	cmpFreqMemBlk : cmpFreqData_memblk
		port map (
			clka => cmpFreqLeftMemBlk_CLK,
			addra => cmpFreqLeftMemBlk_ADDR,
			douta => cmpFreqLeftMemBlk_DOUT,
			-----
			clkb => cmpFreqRightMemBlk_CLK,
			addrb => cmpFreqRightMemBlk_ADDR,
			doutb => cmpFreqRightMemBlk_DOUT
		);
 
	-- Compare On MemBlk Entity:
	cmpOnMemBlk : cmpOnData_memblk
		port map (
			clka => cmpOnLeftMemBlk_CLK,
			addra => cmpOnLeftMemBlk_ADDR,
			douta => cmpOnLeftMemBlk_DOUT,
			----
			clkb => cmpOnRightMemBlk_CLK,
			addrb => cmpOnRightMemBlk_ADDR,
			doutb => cmpOnRightMemBlk_DOUT
		);
 
	-- DSP Interpolate MultAdd Entities:
	pitchBendFREQLeftDSPInterpolate : InterpolateMultAdd
		port map (
			a => pitchBendLeftCalcFREQDelta,
			b => pitchBendLeftMagnitude,
			c => pitchBendLeftCalcFREQAlphaLeft,
			subtract => '0',
			p => pitchBendLeftCalcFREQResult,
			pcout => open
		);
 
	pitchBendONLeftDSPInterpolate : InterpolateMultAdd
		port map (
			a => pitchBendLeftCalcONDelta,
			b => pitchBendLeftMagnitude,
			c => pitchBendLeftCalcONAlphaLeft,
			subtract => '0',
			p => pitchBendLeftCalcONResult,
			pcout => open
		);
	-----------------------------------
	pitchBendFREQRightDSPInterpolate : InterpolateMultAdd
		port map (
			a => pitchBendRightCalcFREQDelta,
			b => pitchBendRightMagnitude,
			c => pitchBendRightCalcFREQAlphaLeft,
			subtract => '0',
			p => pitchBendRightCalcFREQResult,
			pcout => open
		);
 
	pitchBendONRightDSPInterpolate : InterpolateMultAdd
		port map (
			a => pitchBendRightCalcONDelta,
			b => pitchBendRightMagnitude,
			c => pitchBendRightCalcONAlphaLeft,
			subtract => '0',
			p => pitchBendRightCalcONResult,
			pcout => open
		);
 
	-- Create Left Justified version of the pitchBendCalcOFFAlpha and pitchBendCalcONAlpha values.
	--		This will effectively multiply them by 2^12 since they get added before the divide by
	--		2^12 instead of after it.  This eliminates having additional math steps and hopefully
	--		the synthesizer and fitter will optimize it:
	pitchBendLeftCalcFREQAlphaLeft <= pitchBendLeftCalcFREQAlpha & "000000000000";
	pitchBendLeftCalcONAlphaLeft <= pitchBendLeftCalcONAlpha & "000000000000";
	----
	pitchBendRightCalcFREQAlphaLeft <= pitchBendRightCalcFREQAlpha & "000000000000";
	pitchBendRightCalcONAlphaLeft <= pitchBendRightCalcONAlpha & "000000000000";
 
	-- DSP Delta Input Calculation as concurrent logic:
	pitchBendLeftCalcONDelta <= pitchBendLeftCalcONBeta - pitchBendLeftCalcONAlpha;				--   On: Delta = Beta-Alpha
	pitchBendLeftCalcFREQDelta <= pitchBendLeftCalcFREQBeta - pitchBendLeftCalcFREQAlpha;		-- Freq: Delta = Beta-Alpha
 
	pitchBendRightCalcONDelta <= pitchBendRightCalcONBeta - pitchBendRightCalcONAlpha;			--   On: Delta = Beta-Alpha
	pitchBendRightCalcFREQDelta <= pitchBendRightCalcFREQBeta - pitchBendRightCalcFREQAlpha;	-- Freq: Delta = Beta-Alpha
 
	-- Receive Data clarity mapping (Populated with data from syn_rxdata to make the code read better rather than dealing with arbitrary bit numbers everywhere):
	synrx_leftNote <= syn_rxdata(62 downto 56);
	synrx_leftPower <= syn_rxdata(54 downto 48);
	synrx_leftPitchBend <= syn_rxdata(45 downto 32);
	synrx_rightNote <= syn_rxdata(30 downto 24);
	synrx_rightPower <= syn_rxdata(22 downto 16);
	synrx_rightPitchBend <= syn_rxdata(13 downto 0);
	synrx_leftTimerON <= syn_rxdata(63 downto 48);
	synrx_leftTimerOFF <= syn_rxdata(47 downto 32);
	synrx_rightTimerON <= syn_rxdata(31 downto 16);
	synrx_rightTimerOFF <= syn_rxdata(15 downto 0);
 
	-- Memory Block Clocking/Enable Logic:
	tmrMemBlkLeft_ENXfer <= reset_n;									-- Enable Port A (R/W) accesses during xferdataproc
	tmrMemBlkLeft_CLKXfer <= xferdataclk;
	tmrMemBlkLeft_WEXfer <= tmrMemBlkLeft_WE_GATE;
	tmrMemBlkLeft_ENTmr <= reset_n;										-- Enable Port B (R) accesses during tmrproc
	tmrMemBlkLeft_CLKTmr <= sysclk;
	-----
	tmrMemBlkRight_ENXfer <= reset_n;									-- Enable Port A (R/W) accesses during xferdataproc
	tmrMemBlkRight_CLKXfer <= xferdataclk;
	tmrMemBlkRight_WEXfer <= tmrMemBlkRight_WE_GATE;
	tmrMemBlkRight_ENTmr <= reset_n;									-- Enable Port B (R) accesses during tmrproc
	tmrMemBlkRight_CLKTmr <= sysclk;
	-----
	tmrCTMemBlkLeft_ENA <= reset_n and NOT tmrproc_processing_n;		-- Enable Port A (R/W) accesses during tmrproc
	tmrCTMemBlkLeft_ENB <= reset_n and NOT tmrproc_processing_n;
	tmrCTMemBlkLeft_CLKA <= sysclk;
	tmrCTMemBlkLeft_CLKB <= sysclk;
	tmrCTMemBlkLeft_WEA <= tmrCTMemBlkLeft_WE_GATE;
	-----
	tmrCTMemBlkRight_ENA <= reset_n and NOT tmrproc_processing_n;		-- Enable Port A (R/W) accesses during tmrproc
	tmrCTMemBlkRight_ENB <= reset_n and NOT tmrproc_processing_n;
	tmrCTMemBlkRight_CLKA <= sysclk;
	tmrCTMemBlkRight_CLKB <= sysclk;
	tmrCTMemBlkRight_WEA <= tmrCTMemBlkRight_WE_GATE;
	-----
	cmpOnLeftMemBlk_CLK <= sysclk;
	cmpFreqLeftMemBlk_CLK <= sysclk;
	cmpOnRightMemBlk_CLK <= sysclk;
	cmpFreqRightMemBlk_CLK <= sysclk;
 
	-- tmrCTMemBlk Precalc code:
	--	The tmrCTMemBlkPreCalc code asynchronously takes the Timer On/Off
	--		counts being outputted on the tmrMemBlk and adds it to theTimer
	--		to find the time of the next event, making it available to the
	--		tmrCTMemBlkxxx_DOUTBpTimer register which can be loaded if a
	--		compare event for the current timer cycle is detected.  It also
	--		compares the current tmrCTMemBlk memory output (i.e. the last
	--		event calculation) against theTimer to see if this cycle is an
	--		occurrence match.  Done asynchronously from the tmrupdproc
	--		state-machine logic in order to meet setup timing conditions
	--		for the RAM, etc, with the propagation delay of the logic:
	tmrCTMemBlkPreCalc:for i in 0 to numTimerBlocks-1 generate
	begin
		tmrMemBlkLeft_DOUTBpTimer(i) <= tmrMemBlkLeft_DOUTTmr(i) + theTimer;
		with (tmrCTMemBlkLeft_DOUTB(i) = theTimer) select
			tmrCTMemBlkLeft_eqTimer(i) <= '1' when true, '0' when OTHERS;
		tmrMemBlkRight_DOUTBpTimer(i) <= tmrMemBlkRight_DOUTTmr(i) + theTimer;
		with (tmrCTMemBlkRight_DOUTB(i) = theTimer) select
			tmrCTMemBlkRight_eqTimer(i) <= '1' when true, '0' when OTHERS;
	end generate tmrCTMemBlkPreCalc;
 
	-- tmrCTMemBlk Data Multiplexer code:
	--	The Odd/Even multiplexer code blocks asynchronously determine if the
	--		particular timer needs to get updated on the current timer cycle
	--		or not and loads either the next computed event time or the current
	--		event time accordingly.
	--	The final multiplexer code stage selects either the Odd or the Even
	--		data to send to the memory data input depending on which half-cycle
	--		of the state-machine logic we're on, making it available prior to
	--		the rising-edge of the clock which will clock it into the RAM:
	tmrCTMemBlkMuxODD:for i in 0 to numTimerBlocks-1 generate
	begin
		with (((outputEnables(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) = '0') AND
			   (outputForceLoad(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) /= outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)))) OR
			   (tmrCTMemBlkLeft_eqTimer(i) = '1')) select
			tmrMemBlkLeft_DINAmuxODD(i) <= tmrMemBlkLeft_DOUTBpTimer(i) when true, tmrCTMemBlkLeft_DOUTB(i) when OTHERS;
		with (((outputEnables(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) = '0') AND
			   (outputForceLoad(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) /= outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)))) OR
			   (tmrCTMemBlkRight_eqTimer(i) = '1')) select
			tmrMemBlkRight_DINAmuxODD(i) <= tmrMemBlkRight_DOUTBpTimer(i) when true, tmrCTMemBlkRight_DOUTB(i) when OTHERS;
	end generate tmrCTMemBlkMuxODD;
 
	tmrCTMemBlkMuxEVEN:for i in 0 to numTimerBlocks-1 generate
	begin
		with (((outputEnables(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) = '0') AND
			   (outputForceLoad(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) /= outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)))) OR
			   (tmrCTMemBlkLeft_eqTimer(i) = '1')) select
			tmrMemBlkLeft_DINAmuxEVEN(i) <= tmrMemBlkLeft_DOUTBpTimer(i) when true, tmrCTMemBlkLeft_DOUTB(i) when OTHERS;
		with (((outputEnables(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) = '0') AND
			   (outputForceLoad(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) /= outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)))) OR
			   (tmrCTMemBlkRight_eqTimer(i) = '1')) select
			tmrMemBlkRight_DINAmuxEVEN(i) <= tmrMemBlkRight_DOUTBpTimer(i) when true, tmrCTMemBlkRight_DOUTB(i) when OTHERS;
	end generate tmrCTMemBlkMuxEVEN;
 
	tmrCTMemBlkMux:for i in 0 to numTimerBlocks-1 generate
	begin
		-- NOTE: tmrproc_state values here are backwards from tmrupdproc state-machine because these
		--		calculations are AFTER the state transistion has happened and the rest of the state
		--		processing is occurring:
		with (CONV_INTEGER(tmrproc_state)) select
			tmrCTMemBlkLeft_DINA(i) <= tmrMemBlkLeft_DINAmuxODD(i) when 1, tmrMemBlkLeft_DINAmuxEVEN(i) when OTHERS;
		with (CONV_INTEGER(tmrproc_state)) select
			tmrCTMemBlkRight_DINA(i) <= tmrMemBlkRight_DINAmuxODD(i) when 1, tmrMemBlkRight_DINAmuxEVEN(i) when OTHERS;
	end generate tmrCTMemBlkMux;
 
	-- LED Outputs
	with (LEDSet) select
		LED <= LEDenable when '1', LEDpost when OTHERS;
 
-- --------------------------------------------------------------------
-- xferdataclkproc:
--	This process handles prescaling of xferdataclk (simple divide by two):
-- --------------------------------------------------------------------
xferdataclkproc:process(sysclk)
begin
	if (rising_edge(sysclk)) then
		xferdataclk_in <= not xferdataclk_in;
	end if;
end process xferdataclkproc;
-- --------------------------------------------------------------------
 
 
-- These provide a hook into the incoming SPI signals for an easy connection
--		point for testing and experimenting without code changes to alter
--		their naming:
SPI_CLK <= SPI_CLK_in;
SPI_MOSI <= SPI_MOSI_in;
SPI_SS_n <= SPI_SS_n_in;
 
 
-- --------------------------------------------------------------------
-- tmrclkproc:
--	This process handles prescaling of tmrclk
--	Counts and toggles tmrclk when prescaler value is reached:
-- --------------------------------------------------------------------
tmrclkproc:process(GCLK, reset_n)
begin
	if (reset_n = '0') then
		tmrclk_prescaler_count <= (OTHERS => '0');
		tmrclk_in <= '0';
	elsif (rising_edge(GCLK)) then
		tmrclk_prescaler_count <= tmrclk_prescaler_count + 1;
		if (CONV_INTEGER(tmrclk_prescaler_count) = (tmrclkPrescaler/2-1)) then
			tmrclk_prescaler_count <= (OTHERS => '0');
			tmrclk_in <= not tmrclk_in;
		end if;
	end if;
end process tmrclkproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- LEDpostclkproc:
--	This process handles prescaling of the LEDpostPulse used to
--		drive the LEDpost output pattern:
-- --------------------------------------------------------------------
LEDpostclkproc:process(GCLK, reset_n)
begin
	if (reset_n = '0') then
		LEDpostScalerCount <= (OTHERS => '0');
		LEDpostPulse <= '0';
	elsif (rising_edge(GCLK)) then
		LEDpostScalerCount <= LEDpostScalerCount + 1;
		if (CONV_INTEGER(LEDpostScalerCount) = LEDpostPrescaler) then
			LEDpostScalerCount <= (OTHERS => '0');
			LEDpostPulse <= not LEDpostPulse;
		end if;
	end if;
end process LEDpostclkproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- LEDpostproc:
--	This process handles outputting the LEDpost pattern:
-- --------------------------------------------------------------------
LEDpostproc:process(LEDpostPulse, reset_n)
begin
	if (reset_n = '0') then
		LEDpost <= (OTHERS => '0');
	elsif (rising_edge(LEDpostPulse)) then
		LEDpost <= LEDpost(LEDpost'HIGH-1 downto LEDpost'LOW) & not LEDpost(LEDpost'HIGH);
	end if;
end process LEDpostproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- txcmddata:
--	This combinatorial logic handles Command Nybble transfer data:
-- --------------------------------------------------------------------
with SPI_cmd_addr select SPI_txcmd_data <=
	"0" & runMode & addr_block when "100" | "000" | "001" | "010" | "011",
		-- Send the Bank/Mode data on either Command Mode 00 or Data Xfer
		-- Set SPI_txcmd_data to:
		--	0 m b b
		--	- - - -
		--	| | | |
		--	| | +-+-- Currently Selected Bank
		--	| |
		--	| +------ RunMode : 0 = Note/Power/PitchBend, 1 = Timer On/Off Compare Values
		--	|
		--	+-------- Reserved for 001xxx command, TBD, will be sent as 0
 
	SW3 & SW2 & SW1 & SW0 when "101",
		-- Switches are on Command Mode 01
		-- Set SPI_txcmd_data to Switch Status
 
	LEDenable(3 downto 0) when "110",
		-- Low LEDs are on Command Mode 10
		-- Set SPI_txcmd_data to LEDs 0-3
 
	LEDenable(7 downto 4) when "111",
		-- High LEDs are on Command Mode 11
		-- Set SPI_txcmd_data to LEDs 4-7
 
	"0000" when OTHERS;		-- Not needed except to keep ghdl compiler happy
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- txdataproc:
--	This process handles the coordinating of sending our transmit data
--		and latching of the transfer address logic for ALL modes.
--		It triggers txdata_trigger_n on the rising edge of SPI_addr_latch.
--		It resets when txdata_processing_n has begun or SPI_SS_n goes
--		high indicating the transfer was aborted.
-- --------------------------------------------------------------------
txdataproc:process(reset_n, txdata_processing_n, SPI_addr_latch)
begin
	if ((reset_n = '0') OR (txdata_processing_n = '0')) then
		txdata_trigger_n <= '1';
	elsif (rising_edge(SPI_addr_latch)) then
		txdata_trigger_n <= '0';
	end if;
end process txdataproc;
-- --------------------------------------------------------------------
 
 
-- Handle SPI Addr Processing, mapping to our local signals:
spi_rw <= SPI_addr(7);
spi_xfer_mode <= SPI_addr(6);
addr_subtimer <= SPI_addr(numSubTimerBits-1 downto 0);
spi_command <= SPI_addr(numCommandXferBits-1 downto 0);
SPI_cmd_addr <= SPI_addr(6 downto 4);
 
 
-- --------------------------------------------------------------------
-- rrdysynproc:
--	This process synchronizes the rrdy signal:
-- --------------------------------------------------------------------
rrdysynproc:process(reset_n, syn_rrdy_clr_n, sysclk)
begin
	if ((reset_n = '0') or (syn_rrdy_clr_n = '0')) then
		syn_rrdy <= '0';
		syn_rrdy_armed <= '0';
	elsif (rising_edge(sysclk)) then
		-- Capture the data as soon as it's available and arm the trigger to get ready for processing it:
		if ((syn_rrdy_armed = '0') AND (SPI_rrdy = '1')) then
			syn_rrdy_armed <= '1';
			syn_rw <= spi_rw;
			syn_xfer_mode <= spi_xfer_mode;
			syn_rxdata <= SPI_rx_data;
			syn_addr_subtimer <= addr_subtimer;
		elsif ((syn_rrdy_armed = '1') AND (txdata_processing_n = '1') AND (txdata_trigger_n = '1') AND (SPI_busy = '0')) then
			-- Keep it armed, but not running, until we have a rising sysclk after finishing any current txdata processing
			--		and the SPI is finished:
			syn_rrdy <= '1';
		end if;
	end if;
end process rrdysynproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- rxproc:
--	This process handles initial synchronous receive from SPI.  It
--		processes Command Bytes received and triggers rxdata and/or
--		the reset state-machines of xferdataproc as appropriate:
-- --------------------------------------------------------------------
rxproc:process(sysclk, reset_n, local_rst_n)
begin
	if ((reset_n = '0') OR
		(local_rst_n = '0')) then
		syn_rrdy_clr_n <= '1';
		syn_cmd_processing_n <= '1';
		syn_rxdata_trigger_n <= '1';
		runMode <= rmodeNote;
		addr_block <= (OTHERS => '0');
		LEDenable <= (OTHERS => '0');
		LEDSet <= '0';
		local_rst_n <= '1';
		reset_trigger_n <= '0';					-- Trigger a reset process
	elsif (falling_edge(sysclk)) then
		if (reset_trigger_n = '0') then
			if (reset_in_progress_n = '0') then
				reset_trigger_n <= '1';			-- Release trigger once xferdataproc starts reset
			end if;
		elsif (reset_in_progress_n = '1') then	-- Don't process incoming data while xferdataproc is resetting
			if ((syn_rrdy = '1') AND (syn_cmd_processing_n = '1')) then
				if (syn_xfer_mode = xmodeCmd) then
					if (syn_rw = '0') then
						-- Start Command receive:xferdataproc
						case spi_command(5 downto 4) is
							when "00" =>		-- Control Logic
								case spi_command(3 downto 0) is
									when "0000" =>
										local_rst_n <= '0';
									when "0010" =>
										runMode <= rmodeNote;
									when "0011" =>
										runMode <= rmodeTime;
									when "0100" =>
										addr_block <= "00";
									when "0101" =>
										addr_block <= "01";
									when "0110" =>
										addr_block <= "10";
									when "0111" =>
										addr_block <= "11";
									when OTHERS =>
								end case;
 
							when "01" =>		-- Switches (does nothing here on write)
 
							when "10" =>		-- Set LEDs 0-3
								LEDenable(3 downto 0) <= spi_command(3 downto 0);
								LEDSet <= '1';
 
							when "11" =>		-- Set LEDs 4-7
								LEDenable(7 downto 4) <= spi_command(3 downto 0);
								LEDSet <= '1';
 
							when OTHERS =>		-- Can't happen but ghdl complains without it
						end case;
					end if;	-- end syn_rw='0'
 
					syn_rxdata_trigger_n <= '1';
				else
					-- Start Data receive:
					syn_rxdata_trigger_n <= '0';
				end if;	-- end syn_xfer_mode=xmodeCmd
 
				syn_cmd_processing_n <= '0';
			elsif (syn_cmd_processing_n = '0') then
				if (syn_rrdy_clr_n = '0') then
					if (syn_rrdy = '0') then
						syn_rrdy_clr_n <= '1';
						syn_cmd_processing_n <= '1';
					end if;
				elsif (syn_rxdata_trigger_n = '1') then
					-- If this was just a command recept, release syn_rrdy here, since
					--		the command was processed above
					syn_rrdy_clr_n <= '0';
				elsif (syn_rxdata_processing_n = '0') then
					-- But, if this was a data recept, keep syn_rrdy active and
					--		syn_rxdata_trigger_n enabled until xferdataproc gets around
					--		to processing the received data, which should happen
					--		just as soon as any in-process tmrupdproc processes finish:
					syn_rxdata_trigger_n <= '1';
					syn_rrdy_clr_n <= '0';
				end if;
			end if;
		end if;
	end if;
end process rxproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- xferdataproc:
--	This process handles synchronous data transfer of data to set
--		the timer memory.  It also handles resetting of the timer
--		data and queuing of Transmit data.  This process runs at
--		the xferdataclk clock rate.
-- --------------------------------------------------------------------
xferdataproc:process(xferdataclk, reset_n, local_rst_n, reset_trigger_n)
begin
	if (reset_trigger_n = '0') then
		SPI_tx_data <= (OTHERS => '0');
		txdata_processing_n <= '1';
		syn_rxdata_processing_n <= '1';
		cmpOnLeftMemBlk_ADDR <= (OTHERS => '0');
		cmpFreqLeftMemBlk_ADDR <= (OTHERS => '0');
		----
		cmpOnRightMemBlk_ADDR <= (OTHERS => '0');
		cmpFreqRightMemBlk_ADDR <= (OTHERS => '0');
		----
		tmrMemBlkLeft_ADDRXfer <= (OTHERS => '0');
		tmrMemBlkRight_ADDRXfer <= (OTHERS => '0');
		----
		reset_in_progress_n <= '0';			-- Set the reset in progress flag so other rxproc knows we're working on it, otherwise remain in reset state
		-- For resetting, our "state" is the tmrMemBlk address and each cycle is a write of data to it, so enable write gate and set initial data and address:
		for i in 0 to numTimerBlocks-1 loop
			tmrMemBlkLeft_DINXfer(i) <= (OTHERS => '0');
			tmrMemBlkRight_DINXfer(i) <= (OTHERS => '0');
		end loop;
		tmrMemBlkLeft_WE_GATE <= (OTHERS => '1');	-- Note: Enabling this here won't engage memory writes until reset_n is deasserted  and our reset_in_progress state machine starts (see the concurrent logic)
		tmrMemBlkRight_WE_GATE <= (OTHERS => '1');
		----
		for i in 0 to numOutputs-1 loop
			for j in 0 to numTimerBlocks-1 loop
				outputEnablesNext(i)(j) <= (OTHERS => '0');
				outputForceLoad(i)(j) <= (OTHERS => '0');
			end loop;
		end loop;
	else
		if (falling_edge(xferdataclk)) then
			if (reset_in_progress_n = '0') then
				-- If we are resetting, run the Reset State-Machine to clear
				--		out the Timer Data:
				-- Note: The addresses here are current-state for setting up the "next" value:
				if (tmrMemBlkLeft_ADDRXfer = "11111111") then
					-- When we are done, clear memory write and stop state-machine:
					tmrMemBlkLeft_WE_GATE <= (OTHERS => '0');
					tmrMemBlkRight_WE_GATE <= (OTHERS => '0');
					reset_in_progress_n <= '1';
				elsif (tmrMemBlkLeft_ADDRXfer = "00111111") then
					-- If entering into PitchBend memory, set the defaults to 0x2000 which is midi value for "no bend":
					for i in 0 to numTimerBlocks-1 loop
						tmrMemBlkLeft_DINXfer(i) <= "0010000000000000";
						tmrMemBlkRight_DINXfer(i) <= "0010000000000000";
					end loop;
				elsif (tmrMemBlkLeft_ADDRXfer = "01111111") then
					-- If leaving PitchBend memory, return to zeroing everything:
					for i in 0 to numTimerBlocks-1 loop
						tmrMemBlkLeft_DINXfer(i) <= (OTHERS => '0');
						tmrMemBlkRight_DINXfer(i) <= (OTHERS => '0');
					end loop;
				end if;
				tmrMemBlkLeft_ADDRXfer <= tmrMemBlkLeft_ADDRXfer + 1;
				tmrMemBlkRight_ADDRXfer <= tmrMemBlkRight_ADDRXfer + 1;
			elsif ((txdata_trigger_n = '0') OR (txdata_processing_n = '0')) then
				if ((txdata_trigger_n = '0') AND (txdata_processing_n = '1')) then				-- If we aren't in reset, do normal TxData processing
					-- Start transmit state-machine:
					txdata_processing_n <= '0';
					txdata_state <= (OTHERS => '0');
 
					-- State -1:
					--		Select Note/Power or Timer ON data
					tmrMemBlkLeft_ADDRXfer <= runMode & "0" & addr_subtimer;					-- Select Note/Power or Timer ON data store for Left Channel
					tmrMemBlkRight_ADDRXfer <= runMode & "0" & addr_subtimer;					-- Select Note/Power or Timer ON data store for Right Channel
 
				elsif (txdata_processing_n = '0') then
 
					-- Handle transmit -- give it a higher priority that receive processing so we don't collide with memory accesses and this needs to be asynchronous when the SPI needs it
					case CONV_INTEGER(txdata_state) is
 
						-- State 0:
						--		Save Note/Power or Timer ON data
						--		Select PitchBend or Timer OFF data
						when 0 =>
							txdata_state <= CONV_STD_LOGIC_VECTOR(1, nTxSB);						-- Goto state 1
 
							SPI_tx_data(63 downto 48) <= tmrMemBlkLeft_DOUTXfer(TmrBlock(addr_block));	-- Save Note/Power or Timer ON data for Left Channel
							SPI_tx_data(31 downto 16) <= tmrMemBlkRight_DOUTXfer(TmrBlock(addr_block));	-- Save Note/Power or Timer ON data for Right Channel
							tmrMemBlkLeft_ADDRXfer <= runMode & "1" & addr_subtimer;				-- Select PitchBend or Timer OFF data store for Left Channel
							tmrMemBlkRight_ADDRXfer <= runMode & "1" & addr_subtimer;				-- Select PitchBend or Timer OFF data store for Right Channel
 
 
						-- State 1 :
						--		Save PitchBend or Timer OFF data
						when 1 =>
							txdata_state <= CONV_STD_LOGIC_VECTOR(3, nTxSB);						-- Goto state 3
 
							SPI_tx_data(47 downto 32) <= tmrMemBlkLeft_DOUTXfer(TmrBlock(addr_block));	-- Save PitchBend or Timer OFF data for Left Channel
							SPI_tx_data(15 downto 0) <= tmrMemBlkRight_DOUTXfer(TmrBlock(addr_block));	-- Save PitchBend or Timer OFF data for Right Channel
 
 
						-- State 3 :  (Gray-Code Numbered to minimize transition logic)
						--		Transition to done
						when 3 =>
							txdata_processing_n <= '1';												-- Done
 
 
						-- No other states should happen, but as safeguard, disable TX Processing:
						when OTHERS =>
							txdata_processing_n <= '1';
 
					end case;
				end if;
 
			elsif ((syn_rxdata_trigger_n = '0') AND (syn_rxdata_processing_n = '1')) then			-- If not queuing transmit data, process queued receive data:
				-- Start receive state-machine:
				syn_rxdata_processing_n <= '0';
				syn_rxdata_state <= (OTHERS => '0');
 
				if (runMode = rmodeNote) then
 
					-- Pre-Process receive state-machine for Note/Power/PitchBend Run Mode:
					-- State -1 :
					--		Calculate PitchBend Note Indexes
					--		Set tmrMemBlk Address and DataIn to Store the Note/Power Data
					--		Enable tmrMemBlk WEB Gate to write the Timer Memory Block on the next several states
					if ((CONV_INTEGER(synrx_leftNote) < (LOWERNOTE_INDEX+2)) OR
						(CONV_INTEGER(synrx_leftNote) > (UPPERNOTE_INDEX-2))) then
						-- Here if we cannot do pitch bend, so interpolate on same value to keep the same calc paths:
						pitchBendLeftNoteAIndex <= synrx_leftNote;
						pitchBendLeftNoteBIndex <= synrx_leftNote;
					else
						-- Here if we can pitch bend
						pitchBendLeftMagnitude <= synrx_leftPitchBend(11 downto 0);
						case synrx_leftPitchBend(13 downto 12) is
							when "00" =>
								pitchBendLeftNoteAIndex <= synrx_leftNote - 2;
								pitchBendLeftNoteBIndex <= synrx_leftNote - 1;
							when "01" =>
								pitchBendLeftNoteAIndex <= synrx_leftNote - 1;
								pitchBendLeftNoteBIndex <= synrx_leftNote;
							when "10" =>
								pitchBendLeftNoteAIndex <= synrx_leftNote;
								pitchBendLeftNoteBIndex <= synrx_leftNote + 1;
							when "11" =>
								pitchBendLeftNoteAIndex <= synrx_leftNote + 1;
								pitchBendLeftNoteBIndex <= synrx_leftNote + 2;
							when OTHERS =>		-- Shouldn't be needed in real logic, but needs to account for 'U','X', etc in simulation as needed by ghdl compiler
						end case;
					end if;
 
					if ((CONV_INTEGER(synrx_rightNote) < (LOWERNOTE_INDEX+2)) OR
						(CONV_INTEGER(synrx_rightNote) > (UPPERNOTE_INDEX-2))) then
						-- Here if we cannot do pitch bend, so interpolate on same value to keep the same calc paths:
						pitchBendRightNoteAIndex <= synrx_rightNote;
						pitchBendRightNoteBIndex <= synrx_rightNote;
					else
						-- Here if we can pitch bend
						pitchBendRightMagnitude <= synrx_rightPitchBend(11 downto 0);
						case synrx_rightPitchBend(13 downto 12) is
							when "00" =>
								pitchBendRightNoteAIndex <= synrx_rightNote - 2;
								pitchBendRightNoteBIndex <= synrx_rightNote - 1;
							when "01" =>
								pitchBendRightNoteAIndex <= synrx_rightNote - 1;
								pitchBendRightNoteBIndex <= synrx_rightNote;
							when "10" =>
								pitchBendRightNoteAIndex <= synrx_rightNote;
								pitchBendRightNoteBIndex <= synrx_rightNote + 1;
							when "11" =>
								pitchBendRightNoteAIndex <= synrx_rightNote + 1;
								pitchBendRightNoteBIndex <= synrx_rightNote + 2;
							when OTHERS =>		-- Shouldn't be needed in real logic, but needs to account for 'U','X', etc in simulation as needed by ghdl compiler
						end case;
					end if;
 
					tmrMemBlkLeft_ADDRXfer <= adrNotePower & syn_addr_subtimer;								-- Select Note/Power data store for Left Channel
					tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= "0" & synrx_leftNote & "0" & synrx_leftPower;		-- Save given Note (MSB) and Power (LSB)
					tmrMemBlkLeft_WE_GATE(TmrBlock(addr_block)) <= '1';												-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
					tmrMemBlkRight_ADDRXfer <= adrNotePower & syn_addr_subtimer;							-- Select Note/Power data store for Right Channel
					tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= "0" & synrx_rightNote & "0" & synrx_rightPower;		-- Save given Note (MSB) and Power (LSB)
					tmrMemBlkRight_WE_GATE(TmrBlock(addr_block)) <= '1';											-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
				else
 
					-- Pre-Process receive state-machine for Direct Timer On/Off Values Run Mode:
					-- State -1 :
					--		Set tmrMemBlk Address and DataIn to Store the Timer ON Value
					--		Enable tmrMemBlk WEB Gate to write the Timer Memory Block on the next several states
					tmrMemBlkLeft_ADDRXfer <= adrTimeON & syn_addr_subtimer;				-- Select Timer ON data for Left Channel
					tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= synrx_leftTimerON;
					tmrMemBlkLeft_WE_GATE(TmrBlock(addr_block)) <= '1';						-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
					tmrMemBlkRight_ADDRXfer <= adrTimeON & syn_addr_subtimer;				-- Select Timer ON data for Right Channel
					tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= synrx_RightTimerON;
					tmrMemBlkRight_WE_GATE(TmrBlock(addr_block)) <= '1';					-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
				end if;
 
			elsif (syn_rxdata_processing_n = '0') then
				if (runMode = rmodeNote) then
					-- Process receive state-machine for Note/Power/PitchBend Run Mode:
					case CONV_INTEGER(syn_rxdata_state) is
 
						-- State 0 :
						--		Set Compare On and Freq addresses for Lower Note Read (This will cause their DOUT to be the compare values next time)
						--		Set tmrMemBlk Address and DataIn to Store the PitchBend on next clock edge
						when 0 =>
							cmpOnLeftMemBlk_ADDR <= synrx_leftPower & pitchBendLeftNoteAIndex;			-- Power in upper 7-bits, Note in lower 7-bits of Address
							cmpFreqLeftMemBlk_ADDR <= pitchBendLeftNoteAIndex;
							tmrMemBlkLeft_ADDRXfer <= adrPitchBend & syn_addr_subtimer;					-- Select PitchBend data store for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= "00" & synrx_leftPitchBend;	-- Save given Pitchbend
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							cmpOnRightMemBlk_ADDR <= synrx_rightPower & pitchBendRightNoteAIndex;		-- Power in upper 7-bits, Note in lower 7-bits of Address
							cmpFreqRightMemBlk_ADDR <= pitchBendRightNoteAIndex;
							tmrMemBlkRight_ADDRXfer <= adrPitchBend & syn_addr_subtimer;						-- Select PitchBend data store for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= "00" & synrx_rightPitchBend;		-- Save given PitchBend
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(1, nRxSB);						-- Goto state 1
 
						-- State 1 :
						--		Save Compare On and Freq Values for Lower Note (Alpha)
						--		Set Compare On and Freq addresses for Upper Note Read (This will cause their DOUT to be the compare values next time)
						--		Disable tmrMemBlk WEB Gate to write the Timer Memory Block on the next several states
						--
						--		Note: Since Off is Freq-On, subtracting the two during the pitchbend cancels out the "on" portion,
						--				meaning we only need to do that operation on the final interpolated "on" value.
						when 1 =>
							pitchBendLeftCalcONAlpha <= cmpOnLeftMemBlk_DOUT;
							pitchBendLeftCalcFREQAlpha <= cmpFreqLeftMemBlk_DOUT;
							cmpOnLeftMemBlk_ADDR <= synrx_leftPower & pitchBendLeftNoteBIndex;				-- Power in upper 7-bits, Note in lower 7-bits of Address
							cmpFreqLeftMemBlk_ADDR <= pitchBendLeftNoteBIndex;
							--tmrMemBlkLeft_WE_GATE(TmrBlock(addr_block)) <= '0';
							tmrMemBlkLeft_WE_GATE <= (OTHERS => '0');
 
							pitchBendRightCalcONAlpha <= cmpOnRightMemBlk_DOUT;
							pitchBendRightCalcFREQAlpha <= cmpFreqRightMemBlk_DOUT;
							cmpOnRightMemBlk_ADDR <= synrx_rightPower & pitchBendRightNoteBIndex;			-- Power in upper 7-bits, Note in lower 7-bits of Address
							cmpFreqRightMemBlk_ADDR <= pitchBendRightNoteBIndex;
							--tmrMemBlkRight_WE_GATE(TmrBlock(addr_block)) <= '0';
							tmrMemBlkRight_WE_GATE <= (OTHERS => '0');
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(3, nRxSB);							-- Goto state 3
 
						-- State 3 :
						--		Save Compare On and Freq Values for Upper Note (Beta)
						--		Calculate Compare On and Freq Delta Values (done as part of concurrent logic)
						when 3 =>
							pitchBendLeftCalcONBeta <= cmpOnLeftMemBlk_DOUT;
							pitchBendLeftCalcFREQBeta <= cmpFreqLeftMemBlk_DOUT;
 
							pitchBendRightCalcONBeta <= cmpOnRightMemBlk_DOUT;
							pitchBendRightCalcFREQBeta <= cmpFreqRightMemBlk_DOUT;
 
							-- Note: At this step, the following concurrent calculations will be evaluated
							--			independently of this state-machine.  Results will be clocked after
							--			NOP states below, allowing DSP operations to complete.
							--
							-- pitchBendLeftCalcONDelta <= pitchBendLeftCalcONBeta - pitchBendLeftCalcONAlpha;
							-- pitchBendLeftCalcFREQDelta <= pitchBendLeftCalcFREQBeta - pitchBendLeftCalcFREQAlpha;
							-- pitchBendRightCalcONDelta <= pitchBendRightCalcONBeta - pitchBendRightCalcONAlpha;
							-- pitchBendRightCalcFREQDelta <= pitchBendRightCalcFREQBeta - pitchBendRightCalcFREQAlpha;
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(2, nRxSB);							-- Goto state 2
 
						-- State 2 :
						--		NOP -- needed to allow DSP slices to finish calculations as the subtraction
						--				for the delta in the above step doesn't stabilize in time to satisfy
						--				the setup required for the DSP multiply to complete.  The result
						--				takes 13-21nS depending on path, according to the simulation models,
						--				and might cause us to miss the calculation.
						--
						when 2 =>
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(6, nRxSB);							-- Goto state 6
 
						-- State 6 :
						--		NOP -- needed to allow DSP slices to finish calculations as the subtraction
						--				for the delta in the above step doesn't stabilize in time to satisfy
						--				the setup required for the DSP multiply to complete.  The result
						--				takes 13-21nS depending on path, according to the simulation models,
						--				and might cause us to miss the calculation.
						when 6 =>
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(7, nRxSB);							-- Goto state 7
 
						-- State 7 :
						--		Set tmrMemBlk Address and DataIn to Store the Compare ON Value (so we can save the DSP result)
						--		Enable tmrMemBlk WEB Gate to write the Timer Memory Block on the next several states
						when 7 =>
							tmrMemBlkLeft_ADDRXfer <= adrTimeON & syn_addr_subtimer;						-- Select Compare ON data store for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= pitchBendLeftCalcONResult;		-- Save the Compare ON Result for Left Channel
							tmrMemBlkLeft_WE_GATE(TmrBlock(addr_block)) <= '1';								-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
							tmrMemBlkRight_ADDRXfer <= adrTimeON & syn_addr_subtimer;						-- Select Compare ON data store for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= pitchBendRightCalcONResult;		-- Save the Compare ON Result for Right Channel
							tmrMemBlkRight_WE_GATE(TmrBlock(addr_block)) <= '1';							-- Enable writing to the Timer Memory Block during upcoming rising clock edges
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(5, nRxSB);							-- Goto state 5
 
						-- State 5 :
						--		Calculate the Compare OFF value by subtracting the ON value from the interpolated Freq value
						--		Set tmrMemBlk Address and DataIn to Store the Compare OFF Value (so we can save the DSP result)
						--		Enable/Disable Outputs based on results and force tmrproc reload if result wasn't 0
						when 5 =>
							tmrMemBlkLeft_ADDRXfer <= adrTimeOFF & syn_addr_subtimer;						-- Select Compare OFF data store for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= pitchBendLeftCalcFREQResult - pitchBendLeftCalcONResult;		-- Calculate and Save the Compare OFF Result for Left Channel
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							tmrMemBlkRight_ADDRXfer <= adrTimeOFF & syn_addr_subtimer;						-- Select Compare OFF data store for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= pitchBendRightCalcFREQResult - pitchBendRightCalcONResult;	-- Calculate and Save the Compare OFF Result for Right Channel
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							if ((pitchBendLeftCalcONResult /= "0000000000000000") AND
								(pitchBendLeftCalcFREQResult /= "0000000000000000")) then
								outputEnablesNext(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '1';
							else
								outputEnablesNext(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '0';
							end if;
							outputForceLoad(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= NOT outputForceLoad(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer));
 
							if ((pitchBendRightCalcONResult /= "0000000000000000") AND
								(pitchBendRightCalcFREQResult /= "0000000000000000")) then
								outputEnablesNext(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '1';
							else
								outputEnablesNext(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '0';
							end if;
							outputForceLoad(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= NOT outputForceLoad(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer));
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(4, nRxSB);							-- Goto state 4
 
						-- State 4 :
						--		Disable tmrMemBlk WEB Gate (we're done with it)
						--		Done -- Disable RX Processing
						when 4 =>
							tmrMemBlkLeft_WE_GATE <= (OTHERS => '0');										-- Disable writing to the Timer Memory Block (we're done)
							tmrMemBlkRight_WE_GATE <= (OTHERS => '0');										-- Disable writing to the Timer Memory Block (we're done)
							syn_rxdata_processing_n <= '1';
 
						-- No other states should happen, but as safeguard, disable RX Processing: (plus this is needed by ghdl compiler)
						when OTHERS =>
							syn_rxdata_processing_n <= '1';
					end case;
				else		-- End Note/Power/PitchBend RxData State-Machine
					-- Process receive state-machine for Direct Timer On/Off Values Run Mode:
					case CONV_INTEGER(syn_rxdata_state) is
 
						-- State 0 :
						--		Set tmrMemBlk Address and DataIn to Store the Timer OFF Value
						when 0 =>
							tmrMemBlkLeft_ADDRXfer <= adrTimeOFF & syn_addr_subtimer;						-- Select Timer OFF data for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= synrx_leftTimerOFF;
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							tmrMemBlkRight_ADDRXfer <= adrTimeOFF & syn_addr_subtimer;						-- Select Timer OFF data for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= synrx_rightTimerOFF;
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(1, nRxSB);							-- Goto state 1
 
						-- State 1 :
						--		Set tmrMemBlk Address and DataIn to clear Note/Power:
						when 1 =>
							tmrMemBlkLeft_ADDRXfer <= adrNotePower & syn_addr_subtimer;						-- Select Note/Power for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= (OTHERS => '0');					-- Clear it
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							tmrMemBlkRight_ADDRXfer <= adrNotePower & syn_addr_subtimer;					-- Select Note/Power for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= (OTHERS => '0');				-- Clear it
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(3, nRxSB);							-- Goto state 3
 
						-- State 3 :
						--		Set tmrMemBlk Address and DataIn to clear PitchBend:
						when 3 =>
							tmrMemBlkLeft_ADDRXfer <= adrPitchBend & syn_addr_subtimer;						-- Select PitchBend for Left Channel
							tmrMemBlkLeft_DINXfer(TmrBlock(addr_block)) <= "0010000000000000";				-- Clear it
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							tmrMemBlkRight_ADDRXfer <= adrPitchBend & syn_addr_subtimer;					-- Select PitchBend for Right Channel
							tmrMemBlkRight_DINXfer(TmrBlock(addr_block)) <= "0010000000000000";				-- Clear it
							-- Note tmrMemBlk_WEB_GATE already '1' to enable storing
 
							syn_rxdata_state <= CONV_STD_LOGIC_VECTOR(2, nRxSB);							-- Goto state 2
 
						-- State 2 :
						--		Enable Channel Outputs if corresponding On/Off wasn't 0
						--		Disable tmrMemBlk WEB Gate (we're done with it)
						--		Done -- Disable RX Processing
						when 2 =>
							if ((synrx_leftTimerON /= "0000000000000000") AND
								(synrx_leftTimerOFF /= "0000000000000000")) then
								outputEnablesNext(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '1';
							else
								outputEnablesNext(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '0';
							end if;
							outputForceLoad(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= NOT outputForceLoad(LEFT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer));
 
							if ((synrx_rightTimerON /= "0000000000000000") AND
								(synrx_rightTimerOFF /= "0000000000000000")) then
								outputEnablesNext(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '1';
							else
								outputEnablesNext(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= '0';
							end if;
							outputForceLoad(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer)) <= NOT outputForceLoad(RIGHT_OUT_NDX)(TmrBlock(addr_block))(Tmr(syn_addr_subtimer));
 
							tmrMemBlkLeft_WE_GATE <= (OTHERS => '0');									-- Disable writing to the Timer Memory Block (we're done)
							tmrMemBlkRight_WE_GATE <= (OTHERS => '0');									-- Disable writing to the Timer Memory Block (we're done)
							syn_rxdata_processing_n <= '1';
 
						-- Note: State 6 would be next with this Gray-Code encoding
 
						-- No other states should happen, but as safeguard, disable RX Processing:
						when OTHERS =>
							syn_rxdata_processing_n <= '1';
					end case;
 
				end if;		-- End Direct Timer On/Off RxData State-Machine
			end if;		-- syn_rxdata Processing
		end if;		-- Clock Edge
	end if;		-- Reset
end process xferdataproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- outproc:
--	This process generates the outputs:
-- --------------------------------------------------------------------
outproc:process(sysclk, reset_n)
	variable outputs : STD_LOGIC_VECTOR(numSubTimers-1 downto 0);
	variable out_val : STD_LOGIC;
begin
	if (reset_n = '0') then
		for i in 0 to numOutputs-1 loop
			FiberOut(i) <= '0';
		end loop;
	elsif (rising_edge(sysclk)) then
		for i in 0 to numOutputs-1 loop
			out_val := '0';
			for j in 0 to numTimerBlocks-1 loop
				outputs := (outputEnables(i)(j) AND outputLevels(i)(j));
				for k in 0 to numSubTimers-1 loop
					out_val := out_val OR outputs(k);
				end loop;
			end loop;
			FiberOut(i) <= out_val;
		end loop;
	end if;
end process outproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- tmroutproc:
--	This process handles the second-half of doing a round-robin update
--		of all of the timers checking timer data and toggling outputs
--		accrodingly.  This process is triggered by the main timerproc
--		(which runs at the tmrclk clock rate) and this process runs at
--		the sysclk clock rate (processing speed).
--
--	This process handles the output driving/toggling instigated by
--		tmrupdproc.  This process runs on the rising edge of sysclk,
--		whereas tmrupdproc runs on the falling eddge of sysclk.  This
--		allows us an extra 5nS for the output results and timer data
--		(selected via concurrent logic) to get setup before the clock,
--		otherwise we wouldn't have enough setup time.
-- --------------------------------------------------------------------
tmroutproc:process(sysclk, reset_n, local_rst_n, reset_in_progress_n)
begin
	if ((reset_n = '0') OR
		(local_rst_n = '0') OR
		(reset_in_progress_n = '0')) then
		----
		for i in 0 to numOutputs-1 loop
			for j in 0 to numTimerBlocks-1 loop
				outputEnables(i)(j) <= (OTHERS => '0');
				outputLevels(i)(j) <= (OTHERS => '0');
				outputForceLoad_done(i)(j) <= (OTHERS => '0');
			end loop;
		end loop;
	elsif (rising_edge(sysclk)) then
		if (tmrproc_processing_n = '0') then
			case CONV_INTEGER(tmrproc_state) is
				-- State 1:
				--		ODD-TRANSITION, EVEN-SETUP (Note state-numbers are flip-flopped because other state-machine already transitioned them at the falling edge)
				when 1 =>
					for i in 0 to numTimerBlocks-1 loop
						if (((outputEnables(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) = '0') AND
							 (outputForceLoad(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) /= outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)))) OR
							 (tmrCTMemBlkLeft_eqTimer(i) = '1')) then				-- If this Timer Left Channel matches our timer or wasn't enabled and becoming enabled, process toggle
							outputLevels(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= NOT outputLevels(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));		-- Toggle
							outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= NOT outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));
							outputEnables(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= outputEnablesNext(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));
						end if;
 
						if (((outputEnables(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) = '0') AND
							 (outputForceLoad(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) /= outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)))) OR
							 (tmrCTMemBlkRight_eqTimer(i) = '1')) then				-- If this Timer Right Channel matches our timer or wasn't enabled and becoming enabled, process toggle
							outputLevels(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= NOT outputLevels(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));		-- Toggle
							outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= NOT outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));
							outputEnables(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock)) <= outputEnablesNext(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODDLock));
						end if;
					end loop;
 
					tmrproc_tmrndxEVENLock <= tmrproc_tmrndxEVEN;						-- Lock EVEN Timer Index for EVEN-Setup
 
				-- State 0:
				--		EVEN-TRANSITION, ODD-SETUP (Note state-numbers are flip-flopped because other state-machine already transitioned them at the falling edge)
				when 0 =>
					for i in 0 to numTimerBlocks-1 loop
						if (((outputEnables(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) = '0') AND
							 (outputForceLoad(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) /= outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)))) OR
							 (tmrCTMemBlkLeft_eqTimer(i) = '1')) then				-- If this Timer Left Channel matches our timer or wasn't enabled and becoming enabled, process toggle
							outputLevels(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= NOT outputLevels(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));		-- Toggle
							outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= NOT outputForceLoad_done(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));
							outputEnables(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= outputEnablesNext(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));
						end if;
 
						if (((outputEnables(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) = '0') AND
							 (outputForceLoad(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) /= outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)))) OR
							 (tmrCTMemBlkRight_eqTimer(i) = '1')) then				-- If this Timer Right Channel matches our timer or wasn't enabled and becoming enabled, process toggle
							outputLevels(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= NOT outputLevels(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));		-- Toggle
							outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= NOT outputForceLoad_done(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));
							outputEnables(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock)) <= outputEnablesNext(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVENLock));
						end if;
					end loop;
 
					tmrproc_tmrndxODDLock <= tmrproc_tmrndxODD;							-- Lock ODD Timer Index for ODD-Setup
 
				when OTHERS =>				-- There are no other cases, but keep ghdl compiler happy...
			end case;
		end if;
	end if;
end process tmroutproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- tmrupdproc:
--	This process handles the primary-half of doing a round-robin update
--		of all of the timers checking timer data and toggling outputs
--		accrodingly.  This process is triggered by the main timerproc
--		(which runs at the tmrclk clock rate) and this process runs at
--		the sysclk clock rate (processing speed).
--
--	This process sets up the timer memory read addressing and handles
--		the round-robin indexing logic and instigating what values
--		come next, but doesn't handle the output driving/toggling,
--		which is done by tmroutproc.
-- --------------------------------------------------------------------
tmrupdproc:process(sysclk, reset_n, local_rst_n, reset_in_progress_n)
begin
	if ((reset_n = '0') OR
		(local_rst_n = '0') OR
		(reset_in_progress_n = '0')) then
		----
		tmrCTMemBlkLeft_ADDRA <= (OTHERS => '0');
		tmrCTMemBlkLeft_ADDRB <= (OTHERS => '0');
		tmrCTMemBlkLeft_WE_GATE <= (OTHERS => '0');
		----
		tmrCTMemBlkRight_ADDRA <= (OTHERS => '0');
		tmrCTMemBlkRight_ADDRB <= (OTHERS => '0');
		tmrCTMemBlkRight_WE_GATE <= (OTHERS => '0');
		----
		for i in 0 to numTimerBlocks-1 loop
			tmrMemBlkLeft_ADDRTmr(i) <= (OTHERS => '0');
			tmrMemBlkRight_ADDRTmr(i) <= (OTHERS => '0');
		end loop;
		----
		tmrproc_tmrndxEVEN <= "00000";
		tmrproc_tmrndxODD <= "00000";
		tmrproc_processing_n <= '1';
	elsif (falling_edge(sysclk)) then
		if ((tmrproc_trigger_n = '0') AND (tmrproc_processing_n = '1')) then			-- Handle setup coming out of reset:
			-- Start timer processing state-machine:
			tmrproc_processing_n <= '0';
			----
			-- Note : tmrCTMemBlkXXXX addresses set in reset to first state
 
			---- SETUP FOR EVEN TIMER ---
 
			for i in 0 to numTimerBlocks-1 loop
				if (outputLevels(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, "00000")) = '1') then
					tmrMemBlkLeft_ADDRTmr(i) <= adrTimeOFF & EVEN_NDX & "00000";	-- Get amount of time to be off
				else
					tmrMemBlkLeft_ADDRTmr(i) <= adrTimeON & EVEN_NDX & "00000";		-- Get amount of time to be on
				end if;
 
				if (outputLevels(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, "00000")) = '1') then
					tmrMemBlkRight_ADDRTmr(i) <= adrTimeOFF & EVEN_NDX & "00000";	-- Get amount of time to be off
				else
					tmrMemBlkRight_ADDRTmr(i) <= adrTimeON & EVEN_NDX & "00000";	-- Get amount of time to be on
				end if;
			end loop;
 
			tmrproc_state <= CONV_STD_LOGIC_VECTOR(1, nTSB);	-- Goto State 1 to start
		elsif (tmrproc_processing_n = '0') then
			-- Loop through timers processing compare logic:
 
			case CONV_INTEGER(tmrproc_state) is
				-- State 0:
				--		ODD-TRANSITION, EVEN-SETUP
				when 0 =>
					-- Enable ALL writes (for speed).  If a timer doesn't need updating, we'll
					--		simply write it back with the same value.  We have to do this here
					--		and on both paths so we don't accidentally write the first transition
					--		coming into this state-machine.  Likewise for disabling it, it has
					--		to be disabled in the exit/done state:
					tmrCTMemBlkLeft_WE_GATE <= (OTHERS => '1');
					tmrCTMemBlkRight_WE_GATE <= (OTHERS => '1');
 
					---- TRANSITION FOR ODD TIMER ----
 
					tmrCTMemBlkLeft_ADDRA <= ODD_NDX & tmrproc_tmrndxODD;				-- Setup to write the Timer Compare value for this timer Left Channel
					tmrCTMemBlkRight_ADDRA <= ODD_NDX & tmrproc_tmrndxODD;				-- Setup to write the Timer Compare value for this timer Right Channel
 
					tmrproc_state <= CONV_STD_LOGIC_VECTOR(1, nTSB);					-- Keep looping
					tmrproc_tmrndxODD <= tmrproc_tmrndxODD + 1;
 
					-------------------------------------------------------------------------------
 
					---- SETUP FOR EVEN TIMER ----
 
					tmrCTMemBlkLeft_ADDRB <= EVEN_NDX & tmrproc_tmrndxEVEN;				-- Setup to read the Timer Compare value for this timer Left Channel
					tmrCTMemBlkRight_ADDRB <= EVEN_NDX & tmrproc_tmrndxEVEN;			-- Setup to read the Timer Compare value for this timer Right Channel
 
					for i in 0 to numTimerBlocks-1 loop
						if (outputLevels(LEFT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVEN)) = '1') then
							tmrMemBlkLeft_ADDRTmr(i) <= adrTimeOFF & EVEN_NDX & tmrproc_tmrndxEVEN;		-- Get amount of time to be off
						else
							tmrMemBlkLeft_ADDRTmr(i) <= adrTimeON & EVEN_NDX & tmrproc_tmrndxEVEN;		-- Get amount of time to be on
						end if;
 
						if (outputLevels(RIGHT_OUT_NDX)(i)(Tmr(EVEN_NDX, tmrproc_tmrndxEVEN)) = '1') then
							tmrMemBlkRight_ADDRTmr(i) <= adrTimeOFF & EVEN_NDX & tmrproc_tmrndxEVEN;	-- Get amount of time to be off
						else
							tmrMemBlkRight_ADDRTmr(i) <= adrTimeON & EVEN_NDX & tmrproc_tmrndxEVEN;		-- Get amount of time to be on
						end if;
					end loop;
 
				-- State 1:
				--		EVEN-TRANSITION, ODD-SETUP
				when 1 =>
					-- Enable ALL writes (for speed).  If a timer doesn't need updating, we'll
					--		simply write it back with the same value.  We have to do this here
					--		and on both paths so we don't accidentally write the first transition
					--		coming into this state-machine.  Likewise for disabling it, it has
					--		to be disabled in the exit/done state:
					tmrCTMemBlkLeft_WE_GATE <= (OTHERS => '1');
					tmrCTMemBlkRight_WE_GATE <= (OTHERS => '1');
 
					---- TRANSITION FOR EVEN TIMER ----
 
					tmrCTMemBlkLeft_ADDRA <= EVEN_NDX & tmrproc_tmrndxEVEN;				-- Setup to write the Timer Compare value for this timer Left Channel
					tmrCTMemBlkRight_ADDRA <= EVEN_NDX & tmrproc_tmrndxEVEN;			-- Setup to write the Timer Compare value for this timer Right Channel
 
					tmrproc_state <= CONV_STD_LOGIC_VECTOR(0, nTSB);					-- Keep looping
					tmrproc_tmrndxEVEN <= tmrproc_tmrndxEVEN + 1;
 
					-------------------------------------------------------------------------------
 
					---- SETUP FOR ODD TIMER ----
 
					tmrCTMemBlkLeft_ADDRB <= ODD_NDX & tmrproc_tmrndxODD;				-- Setup to read the Timer Compare value for this timer Left Channel
					tmrCTMemBlkRight_ADDRB <= ODD_NDX & tmrproc_tmrndxODD;				-- Setup to read the Timer Compare value for this timer Right Channel
 
					for i in 0 to numTimerBlocks-1 loop
						if (outputLevels(LEFT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODD)) = '1') then
							tmrMemBlkLeft_ADDRTmr(i) <= adrTimeOFF & ODD_NDX & tmrproc_tmrndxODD;		-- Get amount of time to be off
						else
							tmrMemBlkLeft_ADDRTmr(i) <= adrTimeON & ODD_NDX & tmrproc_tmrndxODD;		-- Get amount of time to be on
						end if;
 
						if (outputLevels(RIGHT_OUT_NDX)(i)(Tmr(ODD_NDX, tmrproc_tmrndxODD)) = '1') then
							tmrMemBlkRight_ADDRTmr(i) <= adrTimeOFF & ODD_NDX & tmrproc_tmrndxODD;		-- Get amount of time to be off
						else
							tmrMemBlkRight_ADDRTmr(i) <= adrTimeON & ODD_NDX & tmrproc_tmrndxODD;		-- Get amount of time to be on
						end if;
					end loop;
 
				-- No other states should happen, but as safeguard, disable Timer Processing and make sure write gate is off as something bad happened:
				when OTHERS =>
					tmrCTMemBlkLeft_WE_GATE <= (OTHERS => '0');
					tmrCTMemBlkRight_WE_GATE <= (OTHERS => '0');
					tmrproc_processing_n <= '1';
			end case;
		end if;		-- End of Timer Processing
	end if;
end process tmrupdproc;
-- --------------------------------------------------------------------
 
 
-- --------------------------------------------------------------------
-- timerproc:
--	This process is the main Timer Tick processing.  It handles updating
--		the main timer and triggering processing synchronous logic
--		in tmrupdproc.
-- --------------------------------------------------------------------
timerproc:process(tmrclk, tmrproc_processing_n, reset_n, local_rst_n)
begin
	if ((reset_n='0') OR
		(local_rst_n='0')) then
		tmrproc_trigger_n <= '1';
		theTimer <= (OTHERS => '0');
	elsif (rising_edge(tmrclk)) then
		if (reset_in_progress_n = '1') then
			theTimer <= theTimer + 1;
			tmrproc_trigger_n <= '0';				-- Used to synchronize tmrupdproc to rising tmrclk edge
		end if;
	end if;
end process timerproc;
-- --------------------------------------------------------------------
 
end TimerOCD_arch;
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.