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;