ADAT project

From Hackerspace ACKspace

Jump to: navigation, search
Project: ADAT project
State Completed
Members Danny Witberg
Description This project descibes the ADAT protocol

Contents

Introduction

This project describes the AlesisĀ© ADAT lightpipe protocol. This professional format multichannel digital audio protocol is widely used, but also closed source. Initially, it supported up to 8 channels of 24 bit digital audio at a sample frequency of 48kHz, along with a number of auxilary datastreams. At a further point, support for higher sample rates were added at the costs of the number of channels. This project describes the workings of the ADAT protocol, and proposes a possible implementation in an FPGA.

Please be advised that this project was completely reverse engineered and can only be used for educational purposes. Any results, including but not limited by, harm, pain, discomfort, death, destruction or disabling any appliance, animal or humans can not be held responsible to this project, website, page, writers or participants whatsoever. However, you are greatly encouraged to experiment, share, and learn.

The ADAT protocol

The ADAT lightpipe consists of an unidirectional optical connection, carrying up to 8 channels of 24 bit audio data, plus a number of auxilary data streams. The connection is divided into packets, each containing 256 bits. These packets are sent every sample point, so for an 48KHz sample rate, 48000 times a second meaning once every 20.83 microseconds. The bit rate implied by this is 12,288,000 bits per second or 12.288Mbps. This datastream is NRZI encoded for a 6.144Mbps symbol rate.

The physical interface for sending an ADAT lightpipe tream is the Toshiba TOSlink connector. This LED based connection, along with a PTFE based carrier link, can transmit ADAT up to a distance of about 10 meters and is the same that is used in an optical SP/DIF connection. However, the ADAT protocol is not backwards compatible with SP/DIF, they only share the same physical interface type.

Decoding ADAT

Because the data rate of 12.288Mbps is too fast for the popular TOTX173 fiber tramsit module, data is NRZI encoded by sending a '1' as a change in signal level, a '0' being sent as no change in the signal. For ensuring enough signal transitions are made, a '1' bit is sent after every 4 databits for correct decoding of the ADAT stream. This is true for almost everything about the ADAT protocol, except the synchronisation sequence. The synchronication consists of 10 '0' bits being sent, meaning a longer time that the stream stays at a defined level (being high or low level does not make a diffrence with NRZI) then anywhere else in the data signal.

With a 24 bit audio sample, this can be transmitted with this encoding scheme in 6 times a nibble-wide data chunk, each followed by a '1' bit to ensure data integrity. In all, 30 bits are used for an audio sample, and 8 of these audio samples, 240 bits out of our 256 bits are explained. Of the remaining 16, ten of those are in the synchonisation sequence, leaving 6 bits. 4 user bits can be transmitted, containing auxilary data, and this user data is encapsulated in '1' bith before and after, again for data integrity. All of the bits in the ADAT protocol are now assigned. Adat protocol.gif

FPGA implementation

The choice is made to make the implementation of a ADAT decoder as a feed-forward receiver. This means that, apart from the data signal itself and a high frequency unrelated clock signal, the core produces its own synchronisation and support signals for correctly decoding the stream. As a start, a frame synchronising machine is needed. The sync sequence of adat are 10 bits being '0', this means that during sync there are no transitions. This is something we can measure. Take a counter, and as long as the data signal does not change, it counts up. If the data signal changes, it resets and puts the max value is a seperate register.

FPGA maxtime.gif

Next, we need a "flag" signal that changes if there is no transition for about three quarters of the "maximum time without signal transition"-register. This means that the flag signal is active when there is a constant level for 7.5 databits or up. This is only going to happen at the sync signal, because all other times the ADAT signal is going to change after 5 bits tops. Three quarters is chosen because it is binary easy to create. Take the register, bit-shift to the left and you have only half the value left. Bit-shift to the left again, and you'll have a quarter of the original value. Add one half to one quarter, and you'll end up with three quarters.

FPGA threequarters.gif

If you feed the three-quarters-signal and compare it to the transition counter, which we feed into a second counter, you will count the time it takes for the ADAT signal between two synchronisation sequences. This is a very useful signal, as it also represents the time of one complete frame. This signal we want to put in a register, a "frame time register". This completes the frame synchronication machine.

FPGA frametime.gif

The ADAT frame time signal is at the very base of the decoding process of the ADAT signal. Next, we want the individual bits out of the frames. We are taking advantage to the fact that a maximum of 5 bits can be transmitted, between signal changes. If we measure the time between two signal transitions, we'll have to determine how much bits are transmitted in that time. The easiest way is to create bins: from 0.5 bit time to 1.5 bit time: 1 bit is transmitted, 1.5 to 2.5 bits, there are two bits transmitted etc. How we know the bit time? Easy: just divide the frame time by 256. In reality, a division by 256 means a 8 bit shift right action. If we successfully extracted a number of bits out of the stream, we have to decode it at certain bitcounts. For example, after the synchronisation sequence, at bitcount 5, we want to decode the ADAT user bits. Then again, at bitcount 35, we can decode channel data 1, and so on. Depending on the number of bits that have passed since sync pattern, we know what channel we are on.

Test waveforms

Below is a signaltap waveform overview of a working ADAT to SP-DIF converter.

Adat2spdif.png

VHDL code


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

entity adat_receiver is
 port(
  m_clk : in std_logic;
  adat_in : in std_logic;
  adat_user : out std_logic_vector(3 downto 0); -- adat user bits
  adat_wordclock : out std_logic; -- adat wordclock out (approx 50% symmetry)
  address_out : out std_logic_vector(2 downto 0);
  write_en_out : out std_logic;
  data_out : out std_logic_vector(23 downto 0)
 );
end adat_receiver;

architecture behavioral of adat_receiver is
 signal adat_input_shift : std_logic_vector(1 downto 0);

 signal adat_edge_detect : std_logic;
 signal adat_edge_shift : std_logic_vector(1 downto 0);
 signal adat_edge_cur_time : std_logic_vector(9 downto 0) := (others=>'0');
 signal adat_edge_max_time : std_logic_vector(9 downto 0) := (others=>'0');
 signal wait_increase : std_logic_vector(15 downto 0);

 signal adat_inc_word_time : std_logic_vector(11 downto 0) := (others=>'0');
 signal adat_cur_word_time : std_logic_vector(11 downto 0) := (others=>'0');
 signal adat_sync_mask_time : std_logic_vector(8 downto 0) := (others=>'0');
 signal adat_sync_mask : std_logic := '0';
 signal adat_sync_mask_shift : std_logic_vector(1 downto 0) := (others=>'0');
 signal adat_bit_counter : std_logic_vector(7 downto 0) := (others=>'0');
 signal adat_data : std_logic_vector(1 downto 0);
 signal adat_address : std_logic_vector(2 downto 0) := (others=>'0');

 signal adat_data_shift : std_logic_vector(29 downto 0) := (others=>'0');
 signal adat_write_shift : std_logic_vector(2 downto 0) := (others=>'0');

begin

 shift_adat_input : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   adat_input_shift <= adat_input_shift(0) & adat_in;
  end if;
 end process shift_adat_input;

 detect_adat_sync : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   if (adat_input_shift="01") or (adat_input_shift="10") then
    adat_edge_detect <= '1';
    adat_edge_cur_time <= (others => '0');
   else
    adat_edge_cur_time <= adat_edge_cur_time + 1;
    adat_edge_detect <= '0';
    if adat_edge_cur_time > adat_edge_max_time then
     adat_edge_max_time <= adat_edge_cur_time;
    else
     if wait_increase = "1111111111111111" then
      adat_edge_max_time <= adat_edge_max_time - 1;
      wait_increase <= (others => '0');
     else
      wait_increase <= wait_increase + 1;
     end if;
    end if;
   end if;
  end if;
 end process detect_adat_sync;

 shift_adat_edge : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   adat_edge_shift <= adat_edge_shift(0) & adat_edge_detect;
  end if;
 end process shift_adat_edge;

 mask_adat_edge : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   adat_sync_mask_time <= adat_edge_max_time(adat_edge_max_time'left downto 1) + adat_edge_max_time(adat_edge_max_time'left downto 2);
   if adat_edge_cur_time <= adat_sync_mask_time then
    adat_sync_mask <= '1';
   else
    adat_sync_mask <= '0';
   end if;
  end if;
 end process mask_adat_edge;
 
  shift_adat_mask : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   adat_sync_mask_shift <= adat_sync_mask_shift(0) & adat_sync_mask;
  end if;
 end process shift_adat_mask;

 detect_adat_bits : process (m_clk)
 begin
  if m_clk'event and m_clk='1' then
   if adat_inc_word_time > '0' & adat_cur_word_time(adat_cur_word_time'left downto 1) then
    adat_wordclock <= '0';
   else
    adat_wordclock <= '1';
   end if;
   if (adat_input_shift = "01" or adat_input_shift = "10") and adat_sync_mask = '1' then
    if adat_edge_cur_time > ("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) and adat_edge_cur_time <= (("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) then
     adat_data_shift <= adat_data_shift(adat_data_shift'left-1 downto 0) & '1';
     adat_bit_counter <= adat_bit_counter + 1;
    elsif adat_edge_cur_time > (("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) and adat_edge_cur_time <= (("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("00000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-4))) then
     adat_data_shift <= adat_data_shift(adat_data_shift'left-2 downto 0) & "01";
     adat_bit_counter <= adat_bit_counter + 2;
    elsif adat_edge_cur_time > (("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) and adat_edge_cur_time <= (("00000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-4)) + ("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) then
     adat_data_shift <= adat_data_shift(adat_data_shift'left-3 downto 0) & "001";
     adat_bit_counter <= adat_bit_counter + 3;
    elsif adat_edge_cur_time > (("00000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-4)) + ("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) and adat_edge_cur_time <= ("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("0000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-5)) then
     adat_data_shift <= adat_data_shift(adat_data_shift'left-4 downto 0) & "0001";
     adat_bit_counter <= adat_bit_counter + 4;
    elsif adat_edge_cur_time > ("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("0000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-5)) and adat_edge_cur_time <= (("0000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-2)) + ("0000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-5)) + ("000000" & adat_cur_word_time(adat_cur_word_time'left downto adat_cur_word_time'left-3))) then
     adat_data_shift <= adat_data_shift(adat_data_shift'left-5 downto 0) & "00001";
     adat_bit_counter <= adat_bit_counter + 5;
    end if;
   elsif adat_sync_mask = '0' then
    adat_bit_counter <= (others => '0');
    adat_data_shift <= (others => '0');
   end if;
   if adat_sync_mask_shift="01" then
    adat_inc_word_time <= (others => '0');
    adat_cur_word_time <= adat_inc_word_time;
   else
     adat_inc_word_time <= adat_inc_word_time + 1;
   end if;
   if adat_bit_counter = "00000101" then
    adat_user <= adat_data_shift(adat_data_shift'left-25 downto 1);
    adat_address <= "111";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "00100011" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "000";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "01000001" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "001";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "01011111" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "010";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "01111101" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "011";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "10011011" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "100";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "10111001" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "101";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "11010111" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "110";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   elsif adat_bit_counter = "11110101" then
    data_out <=  adat_data_shift(29 downto 26) & adat_data_shift(24 downto 21) & adat_data_shift(19 downto 16) & adat_data_shift(14 downto 11) & adat_data_shift(9 downto 6) & adat_data_shift(4 downto 1);
    adat_address <= "111";
    adat_write_shift <= adat_write_shift(1 downto 0) & '1';
   else
    adat_write_shift <= adat_write_shift(1 downto 0) & '0';   
   end if;
  end if;
 end process detect_adat_bits;

 write_en_out <= adat_write_shift(2);
 address_out <= adat_address;

end behavioral;

Personal tools
View and edit namespaces data
Variants
Actions
Navigation
Toolbox