Difference between revisions of "MADI transceiver"

From Hackerspace ACKspace
Jump to: navigation, search
(Created page with "{{Project |State=Active |Members=Danny Witberg |Description=This project descibes a MADI transceiver in VHDL }} == Introduction == The Multichannel Audio Digital Interface or MA...")
 
 
(4 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
{{Project
 
{{Project
|State=Active
+
|State=Completed
 
|Members=Danny Witberg
 
|Members=Danny Witberg
 
|Description=This project descibes a MADI transceiver in VHDL
 
|Description=This project descibes a MADI transceiver in VHDL
Line 11: Line 11:
  
 
The MADI protocol is based on the not-so-popular-anymore FDDI protocol, which was a networking protocol. MADI was invented by the AES or Audio Engineering Society back in 1991, and became known as the AES10 standard. The line speed or symbol speed of MADI is 125Mbps, while the transmission speed is 100Mbps. The difference is because of a line encoding scheme known as 4B5B, which converts 4 incoming bits into 5 outgoing bits. Also, for synchronisation purposes, there is a special bit sequence. A MADI datastream consists of a continuous flow of frames, each carrying up to 64 channels. The maximum length of a frame is equal to the sampling rate, because between frames, the synchronisation symbol is sent. Each channel has 4 mode bits, 24 bits representing the audio data, and 4 other bits used for other purposes. So, a total of 32 bits is needed for a channel. The maximum effective transmission speed is 32 bits x 64 channels x 48000Hz is 98304000 bits per second or 98.304Mbps. This leaves a minimum of 1.696Mbps for synchronisation.
 
The MADI protocol is based on the not-so-popular-anymore FDDI protocol, which was a networking protocol. MADI was invented by the AES or Audio Engineering Society back in 1991, and became known as the AES10 standard. The line speed or symbol speed of MADI is 125Mbps, while the transmission speed is 100Mbps. The difference is because of a line encoding scheme known as 4B5B, which converts 4 incoming bits into 5 outgoing bits. Also, for synchronisation purposes, there is a special bit sequence. A MADI datastream consists of a continuous flow of frames, each carrying up to 64 channels. The maximum length of a frame is equal to the sampling rate, because between frames, the synchronisation symbol is sent. Each channel has 4 mode bits, 24 bits representing the audio data, and 4 other bits used for other purposes. So, a total of 32 bits is needed for a channel. The maximum effective transmission speed is 32 bits x 64 channels x 48000Hz is 98304000 bits per second or 98.304Mbps. This leaves a minimum of 1.696Mbps for synchronisation.
 +
 +
== MADI decoding ==
 +
 +
To decode a MADI stream you will need to extract the data bits out of the stream. MADI is NRZI encoded, meaning a '0' is sent as no signal change, and a '1' is sent as a change in the signal. There are a couple advantages to this. First of all, the maximum signal transition rate of the data signal is 62.5MHz, meaning cheaper hardware can be used that don't have to handle with a 125MHz signal. Second, the actual level of the signal does not matter, only changes in the signal has to be detected.
 +
 +
The symbol speed of MADI is 125Mbps and is pretty high compared to the nominal speeds that cheap FPGA's are capable of. Also, the clock signal is not transmitted with the data, so we only know that the symbol speed is ABOUT 125Mbps. We can not simply feed a 125MHz signal to a data latch and input our stream data. A speed variance of only 25ppm (parts-per-million) means we would be missing 3125 bits every second! To succesfully extract all the data bits out of the stream, we would have to do one of two things: 1) Determine the exact clock rate of the datastream or 2) Oversample the stream to make sure we get all the data. The first solution is mostly an analog process, involving a Phase Locked Loop (PLL) and a good analog filter to extract the clock signal out of the datastream. Since the FPGA is all digital logic, we can not use this solution. The second solution means we have to sample the incoming signal at a much higher rate than the transmission speed. Oversampling factors of 6 and higher is no luxury! 125MHz x 6 is 750MHz and that would instantly turn the implementation of this design onto an FPGA to a very expensive project!
 +
 +
=== Multi Phase Deserialising ===
 +
 +
We can still implement the decoding of the MADI stream in an FPGA thanks to a very cool feature of the PLL inside of the FPGA. Inside the FPGA, there is a PLL to convert an incoming clock into a derative clock signal. For example, we can convert an incoming 25MHz signal into a 5 times higher frequency of 125MHz. The PLL has a number of outputs, and each of those outputs is capable of outputting the frequency at a different phase. This is the feature we need for oversampling the MADI datastream! The Altera Cyclone II we intended to use has 3 outputs for each PLL, C0, C1 and C2. We can output the same clock speed at the C1 output as the C0 output, but at a 60 degrees phase advance compared to the C0 output. If we set the C2 output at 120 degrees advance, we have basically 6 phases we can detect.
 +
 +
=== Count the sampling moments ===
 +
 +
Now that we have a parallel signal representation of our incoming datastream, we can count the bits that the incoming datastream stays the same. What use does this have? Well, if we know for how long the incoming stream stays the same, we know how many '0' bits have been transmitted before the change (meaning a '1') has been sent. The best way to discriminate this is by means of bit buckets. If you are using a 6x oversampling scheme, you could say if 5 to 7 bits stayed the same before the signal changed, a '1' had been sent. from 11 to 13 bits, a "01" has been sent, etc. The way the MADI symbols are encoded, the maximum amount of '0's sent before a '1' is 4. This means that we only have to create bit buckets for '0', "01", "001", "0001" and "00001". There are no other possibilities.
 +
 +
=== Accumulate the incoming bits ===
 +
 +
Since we now have way to extract the actually sent databits out of our stream, we can focus on alignment and decoding. MADI uses a distinct synchronization bit pattern, which is known as "JK" in 4B5B terms. This bitpattern of "11000 10001" we have to look out for in our incoming bits. Note that there is a '1' at the end of this pattern, meaning that, at the moment this pattern is received, we are in alignment. This is important because we con only decode a '0' or a stream of '0's at the moment that it is followed by a '1'.
 +
 +
=== Decoding the bitstream ===
 +
 +
After we are aligned, we know that we have to decode bits by groups of 5. We can use a counter to count how many bits are received. If the counter is under 5, we can not decode yet. If it is 5 exactly, we can decode 1 symbol, and reset the counter. But if it is more than 5 bits, we have to decrease the counter by 5, and get the last 5 bits.
 +
 +
{| class="wikitable" style="text-align:center;"
 +
|4 bit code || 5 bit symbol
 +
|-
 +
| 0000 || 11110
 +
|-
 +
| 0001 || 01001
 +
|-
 +
| 0010 || 10100
 +
|-
 +
| 0011 || 10101
 +
|-
 +
| 0100 || 01010
 +
|-
 +
| 0101 || 01011
 +
|-
 +
| 0110 || 01110
 +
|-
 +
| 0111 || 01111
 +
|-
 +
| 1000 || 10010
 +
|-
 +
| 1001 || 10011
 +
|-
 +
| 1010 || 10110
 +
|-
 +
| 1011 || 10111
 +
|-
 +
| 1100 || 11010
 +
|-
 +
| 1101 || 11011
 +
|-
 +
| 1110 || 11100
 +
|-
 +
| 1111 || 11101
 +
|}
 +
 +
== VHDL code ==
 +
 +
The following VHDL code is highly experimental, but it explains the theory in a more practical manner:
 +
 +
<pre>
 +
library ieee;
 +
use ieee.std_logic_1164.all;
 +
use ieee.std_logic_unsigned.all;
 +
 +
entity madi_direct is
 +
port(
 +
  madi_clk_25 : in std_logic;
 +
  madi_input : in std_logic;
 +
  madi_output : out std_logic;
 +
  madi_rx_data : out std_logic_vector(23 downto 0);
 +
  madi_rx_channel : out std_logic_vector(5 downto 0);
 +
  madi_rx_write_enable : out std_logic;
 +
  madi_tx_data : in std_logic_vector(23 downto 0);
 +
  madi_tx_channel : out std_logic_vector(5 downto 0);
 +
  madi_wordclock_out : out std_logic;
 +
  madi_wordclock_in : in std_logic
 +
);
 +
end entity madi_direct;
 +
 +
architecture behavioural of madi_direct is
 +
 +
type madi_nibble_buffer is array(7 downto 0) of std_logic_vector(3 downto 0);
 +
 +
signal madi_clock_0_degrees : std_logic;
 +
signal madi_clock_45_degrees : std_logic;
 +
signal madi_clock_90_degrees : std_logic;
 +
signal madi_clock_135_degrees : std_logic;
 +
 +
signal madi_input_buffer_c0 : std_logic;
 +
signal madi_input_buffer_c1 : std_logic;
 +
signal madi_input_buffer_c2 : std_logic;
 +
signal madi_input_buffer_c3 : std_logic;
 +
signal madi_input_buffer_i0 : std_logic;
 +
signal madi_input_buffer_i1 : std_logic;
 +
signal madi_input_buffer_i2 : std_logic;
 +
signal madi_input_buffer : std_logic_vector(7 downto 0);
 +
signal madi_input_buffer_nrz : std_logic_vector(7 downto 0);
 +
signal madi_input_buffer_last : std_logic;
 +
signal madi_input_bit_stream_counter : std_logic_vector(4 downto 0);
 +
signal madi_input_bit_stream_max : std_logic_vector(4 downto 0);
 +
signal madi_input_bit_stream : std_logic_vector(9 downto 0);
 +
signal madi_input_bit_stream_trigger : std_logic;
 +
signal madi_input_bit_stream_error : std_logic;
 +
signal madi_input_bit_counter : std_logic_vector(3 downto 0) := "1111";
 +
signal madi_input_symbol : std_logic_vector(4 downto 0);
 +
signal madi_input_symbol_active : std_logic;
 +
signal madi_input_symbol_shift : std_logic_vector(1 downto 0);
 +
signal madi_input_nibble : std_logic_vector(3 downto 0);
 +
signal madi_input_nibble_clk : std_logic;
 +
signal madi_input_nibble_rst : std_logic;
 +
signal madi_input_nibble_cnt : std_logic_vector(2 downto 0);
 +
signal madi_input_nibble_buffer : madi_nibble_buffer;
 +
signal madi_input_mute_shift : std_logic_vector(1 downto 0) := (others => '0');
 +
signal madi_input_channel_cnt : std_logic_vector(5 downto 0) := (others => '0');
 +
signal madi_input_channel_rst : std_logic := '0';
 +
signal madi_input_wordclk_shift : std_logic_vector(1 downto 0);
 +
signal madi_input_wordclk_current : std_logic_vector(11 downto 0) := (others => '0');
 +
signal madi_input_wordclk_reference : std_logic_vector(24 downto 0) := (others => '0');
 +
signal madi_input_wordclk_count : std_logic_vector(11 downto 0) := (others => '0');
 +
 +
signal madi_output_wordclock_shift : std_logic_vector(1 downto 0);
 +
signal madi_output_data : std_logic_vector(23 downto 0);
 +
signal madi_output_buffer : std_logic_vector(9 downto 0) := (others => '0');
 +
signal madi_output_nibble : std_logic_vector(3 downto 0) := (others => '0');
 +
signal madi_output_transmit_idle : std_logic_vector(1 downto 0) := (others => '0');
 +
signal madi_output_start_nibble : std_logic_vector(3 downto 0) := (others => '0');
 +
signal madi_output_vucp : std_logic_vector (3 downto 0) := "0000";
 +
signal madi_output_symbol : std_logic_vector(4 downto 0);
 +
signal madi_output_first_frame : std_logic := '0';
 +
signal madi_output_transmit_frame : std_logic := '0';
 +
signal madi_output_transmit_commence : std_logic := '0';
 +
signal madi_output_address : std_logic_vector(8 downto 0) := (others => '0');
 +
signal madi_output_bit_counter : std_logic_vector(2 downto 0) := (others => '0');
 +
signal madi_output_symbol_shift : std_logic_vector(4 downto 0);
 +
 +
component rec_pll is
 +
port(
 +
  inclk0 : in std_logic;
 +
  c0 : out std_logic;
 +
  c1 : out std_logic;
 +
  c2 : out std_logic;
 +
  c3 : out std_logic
 +
);
 +
end component rec_pll;
 +
 +
begin
 +
 +
receive_pll : rec_pll
 +
port map(
 +
  inclk0 => madi_clk_25,
 +
  c0 => madi_clock_0_degrees,
 +
  c1 => madi_clock_45_degrees,
 +
  c2 => madi_clock_90_degrees,
 +
  c3 => madi_clock_135_degrees 
 +
);
 +
 +
madi_receive_stream_0_degrees : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  madi_input_buffer_c0 <= madi_input;
 +
  end if;
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '0' then
 +
  madi_input_buffer_i0 <= madi_input;
 +
  end if;
 +
end process madi_receive_stream_0_degrees;
 +
 +
madi_receive_stream_45_degrees : process (madi_clock_45_degrees)
 +
begin
 +
  if madi_clock_45_degrees'event and madi_clock_45_degrees = '1' then
 +
  madi_input_buffer_c1 <= madi_input;
 +
  end if;
 +
  if madi_clock_45_degrees'event and madi_clock_45_degrees = '0' then
 +
  madi_input_buffer_i1 <= madi_input;
 +
  end if;
 +
end process madi_receive_stream_45_degrees;
 +
 +
  madi_receive_stream_90_degrees : process (madi_clock_90_degrees)
 +
begin
 +
  if madi_clock_90_degrees'event and madi_clock_90_degrees = '1' then
 +
  madi_input_buffer_c2 <= madi_input;
 +
  end if;
 +
  if madi_clock_90_degrees'event and madi_clock_90_degrees = '0' then
 +
  madi_input_buffer_i2 <= madi_input;
 +
  end if;
 +
end process madi_receive_stream_90_degrees;
 +
 +
madi_receive_stream_135_degrees : process (madi_clock_135_degrees)
 +
begin
 +
  if madi_clock_135_degrees'event and madi_clock_135_degrees = '1' then
 +
  madi_input_buffer_c3 <= madi_input;
 +
  end if;
 +
  if madi_clock_135_degrees'event and madi_clock_135_degrees = '0' then
 +
  madi_input_buffer <= madi_input_buffer_c0 & madi_input_buffer_c1 & madi_input_buffer_c2 & madi_input_buffer_c3 & madi_input_buffer_i0 & madi_input_buffer_i1 & madi_input_buffer_i2 & madi_input;
 +
  end if;
 +
end process madi_receive_stream_135_degrees;
 +
 +
madi_receive_bit_stream : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  madi_input_buffer_last <= madi_input_buffer(madi_input_buffer'right);
 +
  madi_input_buffer_nrz(7) <= madi_input_buffer_last xor madi_input_buffer(7);
 +
  madi_input_buffer_nrz(6) <= madi_input_buffer(7) xor madi_input_buffer(6);
 +
  madi_input_buffer_nrz(5) <= madi_input_buffer(6) xor madi_input_buffer(5);
 +
  madi_input_buffer_nrz(4) <= madi_input_buffer(5) xor madi_input_buffer(4);
 +
  madi_input_buffer_nrz(3) <= madi_input_buffer(4) xor madi_input_buffer(3);
 +
  madi_input_buffer_nrz(2) <= madi_input_buffer(3) xor madi_input_buffer(2);
 +
  madi_input_buffer_nrz(1) <= madi_input_buffer(2) xor madi_input_buffer(1);
 +
  madi_input_buffer_nrz(0) <= madi_input_buffer(1) xor madi_input_buffer(0);
 +
  end if;
 +
end process madi_receive_bit_stream;
 +
 +
madi_receive_bit_stream_decode : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  case madi_input_buffer_nrz is
 +
  when "00000000" =>
 +
  madi_input_bit_stream_counter <= madi_input_bit_stream_counter + 8;
 +
  madi_input_bit_stream_trigger <= '0';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "10000000" =>
 +
  madi_input_bit_stream_counter <= "00111";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "01000000" =>
 +
  madi_input_bit_stream_counter <= "00110";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 1;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00100000" =>
 +
  madi_input_bit_stream_counter <= "00101";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 2;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00010000" =>
 +
  madi_input_bit_stream_counter <= "00100";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 3;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00001000" =>
 +
  madi_input_bit_stream_counter <= "00011";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 4;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00000100" =>
 +
  madi_input_bit_stream_counter <= "00010";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 5;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00000010" =>
 +
  madi_input_bit_stream_counter <= "00001";
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 6;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
  when "00000001" =>
 +
  madi_input_bit_stream_counter <= (others => '0');
 +
  madi_input_bit_stream_max <= madi_input_bit_stream_counter + 7;
 +
  madi_input_bit_stream_trigger <= '1';
 +
  madi_input_bit_stream_error <= '0';
 +
    when others =>
 +
  madi_input_bit_stream_error <= '1';
 +
end case;
 +
  end if;
 +
end process madi_receive_bit_stream_decode;
 +
 +
madi_receive_bit_stream_put : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  if madi_input_bit_stream_trigger = '1' then
 +
    if madi_input_bit_stream_max >= 8 and madi_input_bit_stream_counter <= 9 then
 +
    madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-1 downto 0) & '1';
 +
    madi_input_bit_counter <= madi_input_bit_counter + 1;
 +
    end if;
 +
    if madi_input_bit_stream_max >= 17 and madi_input_bit_stream_counter <= 18 then
 +
    madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-2 downto 0) & "01";
 +
    madi_input_bit_counter <= madi_input_bit_counter + 2;
 +
    end if;
 +
    if madi_input_bit_stream_max >= 25 and madi_input_bit_stream_counter <= 27 then
 +
    madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-3 downto 0) & "001";
 +
    madi_input_bit_counter <= madi_input_bit_counter + 3;
 +
    end if;
 +
    if madi_input_bit_stream_max >= 33 and madi_input_bit_stream_counter <= 36 then
 +
    madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-2 downto 0) & "0001";
 +
    if madi_input_bit_stream(5 downto 0) = "110001" then
 +
      madi_input_bit_counter <= (others => '0');
 +
    else
 +
      madi_input_bit_counter <= madi_input_bit_counter + 4;
 +
    end if;
 +
    end if;
 +
  else
 +
    if madi_input_bit_counter = 5 then
 +
    madi_input_symbol <= madi_input_bit_stream (4 downto 0);
 +
    madi_input_bit_counter <= (others => '0');
 +
    madi_input_symbol_active <= '1';
 +
    else
 +
    if madi_input_bit_counter = 6 then
 +
      madi_input_symbol <= madi_input_bit_stream (5 downto 1);
 +
      madi_input_bit_counter <= "0001";
 +
      madi_input_symbol_active <= '1';
 +
    else
 +
      if madi_input_bit_counter = 7 then
 +
      madi_input_symbol <= madi_input_bit_stream (6 downto 2);
 +
      madi_input_bit_counter <= "0010";
 +
      madi_input_symbol_active <= '1';
 +
      else
 +
      if madi_input_bit_counter = 8 then
 +
        madi_input_symbol <= madi_input_bit_stream (7 downto 3);
 +
        madi_input_bit_counter <= "0011";
 +
        madi_input_symbol_active <= '1';
 +
      else
 +
        if madi_input_bit_counter = 9 then
 +
        madi_input_symbol <= madi_input_bit_stream (8 downto 4);
 +
        madi_input_bit_counter <= "0100";
 +
        madi_input_symbol_active <= '1';
 +
        else
 +
        if madi_input_symbol_active = '1' then
 +
          madi_input_symbol_active <= '0';
 +
        end if;
 +
        end if;
 +
      end if;
 +
      end if;
 +
    end if;
 +
    end if;
 +
  end if;
 +
  end if;
 +
end process madi_receive_bit_stream_put;
 +
 +
madi_receive_symbol_decode : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  madi_input_symbol_shift <= madi_input_symbol_shift(0) & madi_input_symbol_active;
 +
  if madi_input_symbol_shift(1) = '1' then
 +
    case madi_input_symbol is
 +
    when "11110" =>
 +
      madi_input_nibble <= "0000";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "01001" =>
 +
      madi_input_nibble <= "0001";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10100" =>
 +
      madi_input_nibble <= "0010";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10101" =>
 +
      madi_input_nibble <= "0011";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "01010" =>
 +
      madi_input_nibble <= "0100";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "01011" =>
 +
      madi_input_nibble <= "0101";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "01110" =>
 +
      madi_input_nibble <= "0110";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "01111" =>
 +
      madi_input_nibble <= "0111";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10010" =>
 +
      madi_input_nibble <= "1000";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10011" =>
 +
      madi_input_nibble <= "1001";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10110" =>
 +
      madi_input_nibble <= "1010";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "10111" =>
 +
      madi_input_nibble <= "1011";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "11010" =>
 +
      madi_input_nibble <= "1100";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "11011" =>
 +
      madi_input_nibble <= "1101";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "11100" =>
 +
      madi_input_nibble <= "1110";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when "11101" =>
 +
      madi_input_nibble <= "1111";
 +
      madi_input_nibble_clk <= '1';
 +
      madi_input_nibble_rst <= '0';
 +
    when others  =>
 +
      madi_input_nibble <= "0000";
 +
      madi_input_nibble_clk <= '0';
 +
      madi_input_nibble_rst <= '1';
 +
    end case; 
 +
  end if;
 +
  end if;
 +
end process madi_receive_symbol_decode;
 +
 +
madi_input_place_nibble : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  if madi_input_nibble_rst = '1' then
 +
    madi_input_nibble_cnt <= (others => '0');
 +
  end if;
 +
  if madi_input_channel_rst = '1' then
 +
    madi_input_channel_rst <= '0';
 +
  end if;
 +
  if madi_input_nibble_clk = '1' then
 +
    madi_input_nibble_cnt <= madi_input_nibble_cnt + 1;
 +
    case madi_input_nibble_cnt is
 +
    when "000" =>
 +
      madi_input_nibble_buffer(0) <= madi_input_nibble;
 +
    when "001" =>
 +
      madi_input_nibble_buffer(1) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "010" =>
 +
      madi_input_nibble_buffer(2) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "011" =>
 +
      madi_input_nibble_buffer(3) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "100" =>
 +
      madi_input_nibble_buffer(4) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "101" =>
 +
      madi_input_nibble_buffer(5) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "110" =>
 +
      madi_input_nibble_buffer(6) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
 +
    when "111" =>
 +
      madi_input_nibble_buffer(7) <= madi_input_nibble;
 +
    when others =>
 +
    end case;
 +
    if madi_input_nibble(3) = '1' and madi_input_nibble_cnt = "000" then
 +
    madi_input_channel_rst <= '1';
 +
    madi_input_channel_cnt <= (others => '0');
 +
    end if;
 +
    madi_rx_write_enable <= madi_input_channel_rst;
 +
    if madi_input_nibble_cnt = 7 then
 +
    madi_rx_data <= madi_input_nibble_buffer(6) & madi_input_nibble_buffer(5) & madi_input_nibble_buffer(4) & madi_input_nibble_buffer(3) & madi_input_nibble_buffer(2) & madi_input_nibble_buffer(1);
 +
    madi_input_channel_cnt <= madi_input_channel_cnt + 1;
 +
    madi_rx_channel <= madi_input_channel_cnt;
 +
    end if;
 +
  end if;
 +
  end if;
 +
end process madi_input_place_nibble;
 +
 +
madi_input_generate_wordclk : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  madi_input_wordclk_shift <= madi_input_wordclk_shift(0) & madi_input_channel_rst;
 +
  if madi_input_wordclk_shift = "01" then
 +
    madi_input_wordclk_count <= (others => '0');
 +
    madi_input_wordclk_current <= madi_input_wordclk_count;
 +
  else
 +
    madi_input_wordclk_count <= madi_input_wordclk_count + 1;
 +
  end if;
 +
  if madi_input_wordclk_count < '0' & madi_input_wordclk_current(madi_input_wordclk_current'left downto 1) then
 +
    madi_wordclock_out <= '1';
 +
  else
 +
    madi_wordclock_out <= '0';
 +
  end if;
 +
  end if;
 +
end process madi_input_generate_wordclk;
 +
 +
madi_tx_channel <= madi_output_address(8 downto 3);
 +
 +
madi_output_shift_wordclock : process (madi_clk_25)
 +
begin
 +
  if madi_clk_25'event and madi_clk_25 = '1' then
 +
  madi_output_wordclock_shift <= madi_output_wordclock_shift(0) & madi_wordclock_in;
 +
  end if;
 +
end process madi_output_shift_wordclock;
 +
 +
madi_output_shift_buffer : process (madi_clk_25)
 +
begin
 +
  if madi_clk_25'event and madi_clk_25 = '1' then
 +
  if madi_output_wordclock_shift = "01" then
 +
    madi_output_transmit_commence <= '1';
 +
  end if;
 +
  if madi_output_transmit_commence = '1' and madi_output_transmit_idle = "01" then
 +
    madi_output_address <= "000000001";
 +
    madi_output_transmit_frame <= '1';
 +
    madi_output_transmit_commence <= '0';
 +
    madi_output_transmit_idle <= "00";
 +
    madi_output_first_frame <= '0';
 +
  else
 +
    if madi_output_transmit_frame = '0' then
 +
    case madi_output_transmit_idle is
 +
      when "00" =>
 +
      madi_output_transmit_idle <= "10";
 +
      when "01" =>
 +
      madi_output_transmit_idle <= "10";
 +
      when "10" =>
 +
      madi_output_transmit_idle <= "01";
 +
      when others =>
 +
    end case;
 +
    else
 +
    madi_output_address <= madi_output_address + 1;
 +
    if madi_output_address = 511 then
 +
      madi_output_transmit_frame <= '0';
 +
      madi_output_first_frame <= '1';
 +
      madi_output_address <= (others => '0');
 +
    end if;
 +
    end if;
 +
  end if;
 +
  case madi_output_address(2 downto 0) is
 +
    when "000" =>
 +
    madi_output_nibble <= madi_output_first_frame & '1' & not(madi_output_address(3)) & '0';
 +
    madi_output_data <= madi_tx_data;
 +
    madi_output_vucp(0) <= madi_tx_data(23) xor madi_tx_data(22) xor madi_tx_data(21) xor madi_tx_data(20) xor madi_tx_data(19) xor madi_tx_data(18) xor madi_tx_data(17) xor madi_tx_data(16) xor madi_tx_data(15) xor madi_tx_data(14) xor madi_tx_data(13) xor madi_tx_data(12) xor madi_tx_data(11) xor madi_tx_data(10) xor madi_tx_data(9) xor madi_tx_data(8) xor madi_tx_data(7) xor madi_tx_data(6) xor madi_tx_data(5) xor madi_tx_data(4) xor madi_tx_data(3) xor madi_tx_data(2) xor madi_tx_data(1) xor madi_tx_data(0) xor madi_output_vucp(3) xor madi_output_vucp(2) xor madi_output_vucp(1);
 +
    when "001" =>
 +
    madi_output_nibble <= madi_output_data(0) & madi_output_data(1) & madi_output_data(2) & madi_output_data(3);
 +
    when "010" =>
 +
    madi_output_nibble <= madi_output_data(4) & madi_output_data(5) & madi_output_data(6) & madi_output_data(7);
 +
    when "011" =>
 +
    madi_output_nibble <= madi_output_data(8) & madi_output_data(9) & madi_output_data(10) & madi_output_data(11);
 +
    when "100" =>
 +
    madi_output_nibble <= madi_output_data(12) & madi_output_data(13) & madi_output_data(14) & madi_output_data(15);
 +
    when "101" =>
 +
    madi_output_nibble <= madi_output_data(16) & madi_output_data(17) & madi_output_data(18) & madi_output_data(19);
 +
    when "110" =>
 +
    madi_output_nibble <= madi_output_data(20) & madi_output_data(21) & madi_output_data(22) & madi_output_data(23);
 +
    when "111" =>
 +
    madi_output_nibble <= madi_output_vucp;
 +
    when others =>
 +
  end case;
 +
  end if;   
 +
end process madi_output_shift_buffer;
 +
 +
madi_output_nibble_to_symbol : process (madi_clk_25)
 +
begin
 +
  if madi_clk_25'event and madi_clk_25 = '1' then
 +
  if madi_output_transmit_idle = "00" then
 +
    case madi_output_nibble is
 +
    when "0000" =>
 +
      madi_output_symbol <= "11110";
 +
    when "0001" =>
 +
      madi_output_symbol <= "01001";
 +
    when "0010" =>
 +
      madi_output_symbol <= "10100";
 +
    when "0011" =>
 +
      madi_output_symbol <= "10101";
 +
    when "0100" =>
 +
      madi_output_symbol <= "01010";
 +
    when "0101" =>
 +
      madi_output_symbol <= "01011";
 +
    when "0110" =>
 +
      madi_output_symbol <= "01110";
 +
    when "0111" =>
 +
      madi_output_symbol <= "01111";
 +
    when "1000" =>
 +
      madi_output_symbol <= "10010";
 +
    when "1001" =>
 +
      madi_output_symbol <= "10011";
 +
    when "1010" =>
 +
      madi_output_symbol <= "10110";
 +
      when "1011" =>
 +
      madi_output_symbol <= "10111";
 +
    when "1100" =>
 +
      madi_output_symbol <= "11010";
 +
    when "1101" =>
 +
      madi_output_symbol <= "11011";
 +
    when "1110" =>
 +
      madi_output_symbol <= "11100";
 +
    when "1111" =>
 +
      madi_output_symbol <= "11101";
 +
    when others =>
 +
    end case;
 +
  else
 +
    if madi_output_transmit_idle = "10" then
 +
    madi_output_symbol <= "11000";
 +
    else
 +
    madi_output_symbol <= "10001";
 +
    end if;
 +
  end if;
 +
  end if;
 +
end process madi_output_nibble_to_symbol;
 +
 +
madi_output_bit_count : process (madi_clock_0_degrees)
 +
begin
 +
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
 +
  if madi_output_bit_counter >= "100" then
 +
  madi_output_bit_counter <= (others => '0');
 +
  madi_output_symbol_shift <= madi_output_symbol;
 +
else
 +
  madi_output_bit_counter <= madi_output_bit_counter + 1;
 +
  madi_output_symbol_shift <= madi_output_symbol_shift(3 downto 0) & '0';
 +
end if;
 +
  end if;
 +
end process madi_output_bit_count;
 +
 +
madi_output <= madi_output_symbol_shift(madi_output_symbol_shift'left);
 +
 +
end behavioural;
 +
</pre>

Latest revision as of 17:47, 31 January 2016

Project: MADI transceiver
Featured:
State Completed
Members Danny Witberg
GitHub No GitHub project defined. Add your project here.
Description This project descibes a MADI transceiver in VHDL
Picture
No project picture! Fill in form Picture or Upload a jpeg here

Introduction

The Multichannel Audio Digital Interface or MADI protocol is a way to send multiple digital audio channels over a single transmission line. The protocol describes up to 64 channels of up to 24 bit audio at a sample rate of 48kHz. The sample rate can be higher at the expense of the number of channels, so up to 32 channels of 96kHz sampled audio is also a possibility. With the S-MUX protocol, even higher sample rates can be achieved with putting consecutive samples in adjacent channels. This project describes an implementation of the MADI protocol with the use of an FPGA in the VHDL language.

MADI protocol

The MADI protocol is based on the not-so-popular-anymore FDDI protocol, which was a networking protocol. MADI was invented by the AES or Audio Engineering Society back in 1991, and became known as the AES10 standard. The line speed or symbol speed of MADI is 125Mbps, while the transmission speed is 100Mbps. The difference is because of a line encoding scheme known as 4B5B, which converts 4 incoming bits into 5 outgoing bits. Also, for synchronisation purposes, there is a special bit sequence. A MADI datastream consists of a continuous flow of frames, each carrying up to 64 channels. The maximum length of a frame is equal to the sampling rate, because between frames, the synchronisation symbol is sent. Each channel has 4 mode bits, 24 bits representing the audio data, and 4 other bits used for other purposes. So, a total of 32 bits is needed for a channel. The maximum effective transmission speed is 32 bits x 64 channels x 48000Hz is 98304000 bits per second or 98.304Mbps. This leaves a minimum of 1.696Mbps for synchronisation.

MADI decoding

To decode a MADI stream you will need to extract the data bits out of the stream. MADI is NRZI encoded, meaning a '0' is sent as no signal change, and a '1' is sent as a change in the signal. There are a couple advantages to this. First of all, the maximum signal transition rate of the data signal is 62.5MHz, meaning cheaper hardware can be used that don't have to handle with a 125MHz signal. Second, the actual level of the signal does not matter, only changes in the signal has to be detected.

The symbol speed of MADI is 125Mbps and is pretty high compared to the nominal speeds that cheap FPGA's are capable of. Also, the clock signal is not transmitted with the data, so we only know that the symbol speed is ABOUT 125Mbps. We can not simply feed a 125MHz signal to a data latch and input our stream data. A speed variance of only 25ppm (parts-per-million) means we would be missing 3125 bits every second! To succesfully extract all the data bits out of the stream, we would have to do one of two things: 1) Determine the exact clock rate of the datastream or 2) Oversample the stream to make sure we get all the data. The first solution is mostly an analog process, involving a Phase Locked Loop (PLL) and a good analog filter to extract the clock signal out of the datastream. Since the FPGA is all digital logic, we can not use this solution. The second solution means we have to sample the incoming signal at a much higher rate than the transmission speed. Oversampling factors of 6 and higher is no luxury! 125MHz x 6 is 750MHz and that would instantly turn the implementation of this design onto an FPGA to a very expensive project!

Multi Phase Deserialising

We can still implement the decoding of the MADI stream in an FPGA thanks to a very cool feature of the PLL inside of the FPGA. Inside the FPGA, there is a PLL to convert an incoming clock into a derative clock signal. For example, we can convert an incoming 25MHz signal into a 5 times higher frequency of 125MHz. The PLL has a number of outputs, and each of those outputs is capable of outputting the frequency at a different phase. This is the feature we need for oversampling the MADI datastream! The Altera Cyclone II we intended to use has 3 outputs for each PLL, C0, C1 and C2. We can output the same clock speed at the C1 output as the C0 output, but at a 60 degrees phase advance compared to the C0 output. If we set the C2 output at 120 degrees advance, we have basically 6 phases we can detect.

Count the sampling moments

Now that we have a parallel signal representation of our incoming datastream, we can count the bits that the incoming datastream stays the same. What use does this have? Well, if we know for how long the incoming stream stays the same, we know how many '0' bits have been transmitted before the change (meaning a '1') has been sent. The best way to discriminate this is by means of bit buckets. If you are using a 6x oversampling scheme, you could say if 5 to 7 bits stayed the same before the signal changed, a '1' had been sent. from 11 to 13 bits, a "01" has been sent, etc. The way the MADI symbols are encoded, the maximum amount of '0's sent before a '1' is 4. This means that we only have to create bit buckets for '0', "01", "001", "0001" and "00001". There are no other possibilities.

Accumulate the incoming bits

Since we now have way to extract the actually sent databits out of our stream, we can focus on alignment and decoding. MADI uses a distinct synchronization bit pattern, which is known as "JK" in 4B5B terms. This bitpattern of "11000 10001" we have to look out for in our incoming bits. Note that there is a '1' at the end of this pattern, meaning that, at the moment this pattern is received, we are in alignment. This is important because we con only decode a '0' or a stream of '0's at the moment that it is followed by a '1'.

Decoding the bitstream

After we are aligned, we know that we have to decode bits by groups of 5. We can use a counter to count how many bits are received. If the counter is under 5, we can not decode yet. If it is 5 exactly, we can decode 1 symbol, and reset the counter. But if it is more than 5 bits, we have to decrease the counter by 5, and get the last 5 bits.

4 bit code 5 bit symbol
0000 11110
0001 01001
0010 10100
0011 10101
0100 01010
0101 01011
0110 01110
0111 01111
1000 10010
1001 10011
1010 10110
1011 10111
1100 11010
1101 11011
1110 11100
1111 11101

VHDL code

The following VHDL code is highly experimental, but it explains the theory in a more practical manner:

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

entity madi_direct is
 port(
  madi_clk_25 : in std_logic;
  madi_input : in std_logic;
  madi_output : out std_logic;
  madi_rx_data : out std_logic_vector(23 downto 0);
  madi_rx_channel : out std_logic_vector(5 downto 0);
  madi_rx_write_enable : out std_logic;
  madi_tx_data : in std_logic_vector(23 downto 0);
  madi_tx_channel : out std_logic_vector(5 downto 0);
  madi_wordclock_out : out std_logic;
  madi_wordclock_in : in std_logic
 );
end entity madi_direct;

architecture behavioural of madi_direct is

 type madi_nibble_buffer is array(7 downto 0) of std_logic_vector(3 downto 0);
 
 signal madi_clock_0_degrees : std_logic;
 signal madi_clock_45_degrees : std_logic;
 signal madi_clock_90_degrees : std_logic;
 signal madi_clock_135_degrees : std_logic;
 
 signal madi_input_buffer_c0 : std_logic;
 signal madi_input_buffer_c1 : std_logic;
 signal madi_input_buffer_c2 : std_logic;
 signal madi_input_buffer_c3 : std_logic;
 signal madi_input_buffer_i0 : std_logic;
 signal madi_input_buffer_i1 : std_logic;
 signal madi_input_buffer_i2 : std_logic;
 signal madi_input_buffer : std_logic_vector(7 downto 0);
 signal madi_input_buffer_nrz : std_logic_vector(7 downto 0);
 signal madi_input_buffer_last : std_logic;
 signal madi_input_bit_stream_counter : std_logic_vector(4 downto 0);
 signal madi_input_bit_stream_max : std_logic_vector(4 downto 0);
 signal madi_input_bit_stream : std_logic_vector(9 downto 0);
 signal madi_input_bit_stream_trigger : std_logic;
 signal madi_input_bit_stream_error : std_logic;
 signal madi_input_bit_counter : std_logic_vector(3 downto 0) := "1111";
 signal madi_input_symbol : std_logic_vector(4 downto 0);
 signal madi_input_symbol_active : std_logic;
 signal madi_input_symbol_shift : std_logic_vector(1 downto 0);
 signal madi_input_nibble : std_logic_vector(3 downto 0);
 signal madi_input_nibble_clk : std_logic;
 signal madi_input_nibble_rst : std_logic;
 signal madi_input_nibble_cnt : std_logic_vector(2 downto 0);
 signal madi_input_nibble_buffer : madi_nibble_buffer;
 signal madi_input_mute_shift : std_logic_vector(1 downto 0) := (others => '0');
 signal madi_input_channel_cnt : std_logic_vector(5 downto 0) := (others => '0');
 signal madi_input_channel_rst : std_logic := '0';
 signal madi_input_wordclk_shift : std_logic_vector(1 downto 0);
 signal madi_input_wordclk_current : std_logic_vector(11 downto 0) := (others => '0');
 signal madi_input_wordclk_reference : std_logic_vector(24 downto 0) := (others => '0');
 signal madi_input_wordclk_count : std_logic_vector(11 downto 0) := (others => '0');
 
 signal madi_output_wordclock_shift : std_logic_vector(1 downto 0);
 signal madi_output_data : std_logic_vector(23 downto 0); 
 signal madi_output_buffer : std_logic_vector(9 downto 0) := (others => '0');
 signal madi_output_nibble : std_logic_vector(3 downto 0) := (others => '0');
 signal madi_output_transmit_idle : std_logic_vector(1 downto 0) := (others => '0');
 signal madi_output_start_nibble : std_logic_vector(3 downto 0) := (others => '0');
 signal madi_output_vucp : std_logic_vector (3 downto 0) := "0000";
 signal madi_output_symbol : std_logic_vector(4 downto 0);
 signal madi_output_first_frame : std_logic := '0';
 signal madi_output_transmit_frame : std_logic := '0';
 signal madi_output_transmit_commence : std_logic := '0';
 signal madi_output_address : std_logic_vector(8 downto 0) := (others => '0');
 signal madi_output_bit_counter : std_logic_vector(2 downto 0) := (others => '0');
 signal madi_output_symbol_shift : std_logic_vector(4 downto 0); 
 
 component rec_pll is
 port(
  inclk0 : in std_logic;
  c0 : out std_logic;
  c1 : out std_logic;
  c2 : out std_logic;
  c3 : out std_logic
 );
 end component rec_pll;

begin

 receive_pll : rec_pll
 port map(
  inclk0 => madi_clk_25,
  c0 => madi_clock_0_degrees,
  c1 => madi_clock_45_degrees,
  c2 => madi_clock_90_degrees,
  c3 => madi_clock_135_degrees  
 );
 
 madi_receive_stream_0_degrees : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   madi_input_buffer_c0 <= madi_input;
  end if;
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '0' then
   madi_input_buffer_i0 <= madi_input;
  end if;
 end process madi_receive_stream_0_degrees;

 madi_receive_stream_45_degrees : process (madi_clock_45_degrees)
 begin
  if madi_clock_45_degrees'event and madi_clock_45_degrees = '1' then
   madi_input_buffer_c1 <= madi_input;
  end if;
  if madi_clock_45_degrees'event and madi_clock_45_degrees = '0' then
   madi_input_buffer_i1 <= madi_input;
  end if;
 end process madi_receive_stream_45_degrees;

  madi_receive_stream_90_degrees : process (madi_clock_90_degrees)
 begin
  if madi_clock_90_degrees'event and madi_clock_90_degrees = '1' then
   madi_input_buffer_c2 <= madi_input;
  end if;
  if madi_clock_90_degrees'event and madi_clock_90_degrees = '0' then
   madi_input_buffer_i2 <= madi_input;
  end if;
 end process madi_receive_stream_90_degrees;
 
 madi_receive_stream_135_degrees : process (madi_clock_135_degrees)
 begin
  if madi_clock_135_degrees'event and madi_clock_135_degrees = '1' then
   madi_input_buffer_c3 <= madi_input;
  end if;
  if madi_clock_135_degrees'event and madi_clock_135_degrees = '0' then
   madi_input_buffer <= madi_input_buffer_c0 & madi_input_buffer_c1 & madi_input_buffer_c2 & madi_input_buffer_c3 & madi_input_buffer_i0 & madi_input_buffer_i1 & madi_input_buffer_i2 & madi_input;
  end if;
 end process madi_receive_stream_135_degrees;
 
 madi_receive_bit_stream : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   madi_input_buffer_last <= madi_input_buffer(madi_input_buffer'right);
   madi_input_buffer_nrz(7) <= madi_input_buffer_last xor madi_input_buffer(7);
   madi_input_buffer_nrz(6) <= madi_input_buffer(7) xor madi_input_buffer(6);
   madi_input_buffer_nrz(5) <= madi_input_buffer(6) xor madi_input_buffer(5);
   madi_input_buffer_nrz(4) <= madi_input_buffer(5) xor madi_input_buffer(4);
   madi_input_buffer_nrz(3) <= madi_input_buffer(4) xor madi_input_buffer(3);
   madi_input_buffer_nrz(2) <= madi_input_buffer(3) xor madi_input_buffer(2);
   madi_input_buffer_nrz(1) <= madi_input_buffer(2) xor madi_input_buffer(1);
   madi_input_buffer_nrz(0) <= madi_input_buffer(1) xor madi_input_buffer(0);
  end if;
 end process madi_receive_bit_stream;
 
 madi_receive_bit_stream_decode : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   case madi_input_buffer_nrz is
  when "00000000" =>
   madi_input_bit_stream_counter <= madi_input_bit_stream_counter + 8;
   madi_input_bit_stream_trigger <= '0';
   madi_input_bit_stream_error <= '0';
  when "10000000" =>
   madi_input_bit_stream_counter <= "00111";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "01000000" =>
   madi_input_bit_stream_counter <= "00110";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 1;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00100000" =>
   madi_input_bit_stream_counter <= "00101";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 2;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00010000" =>
   madi_input_bit_stream_counter <= "00100";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 3;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00001000" =>
   madi_input_bit_stream_counter <= "00011";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 4;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00000100" =>
   madi_input_bit_stream_counter <= "00010";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 5;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00000010" =>
   madi_input_bit_stream_counter <= "00001";
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 6;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
  when "00000001" =>
   madi_input_bit_stream_counter <= (others => '0');
   madi_input_bit_stream_max <= madi_input_bit_stream_counter + 7;
   madi_input_bit_stream_trigger <= '1';
   madi_input_bit_stream_error <= '0';
    when others =>
   madi_input_bit_stream_error <= '1';
 end case;
  end if;
 end process madi_receive_bit_stream_decode;
 
 madi_receive_bit_stream_put : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   if madi_input_bit_stream_trigger = '1' then
    if madi_input_bit_stream_max >= 8 and madi_input_bit_stream_counter <= 9 then
     madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-1 downto 0) & '1';
     madi_input_bit_counter <= madi_input_bit_counter + 1;
    end if;
    if madi_input_bit_stream_max >= 17 and madi_input_bit_stream_counter <= 18 then
     madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-2 downto 0) & "01";
     madi_input_bit_counter <= madi_input_bit_counter + 2;
    end if;
    if madi_input_bit_stream_max >= 25 and madi_input_bit_stream_counter <= 27 then
     madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-3 downto 0) & "001";
     madi_input_bit_counter <= madi_input_bit_counter + 3;
    end if;
    if madi_input_bit_stream_max >= 33 and madi_input_bit_stream_counter <= 36 then
     madi_input_bit_stream <= madi_input_bit_stream(madi_input_bit_stream'left-2 downto 0) & "0001";
     if madi_input_bit_stream(5 downto 0) = "110001" then
      madi_input_bit_counter <= (others => '0');
     else
      madi_input_bit_counter <= madi_input_bit_counter + 4;
     end if;
    end if;
   else
    if madi_input_bit_counter = 5 then
     madi_input_symbol <= madi_input_bit_stream (4 downto 0);
     madi_input_bit_counter <= (others => '0');
     madi_input_symbol_active <= '1';
    else
     if madi_input_bit_counter = 6 then
      madi_input_symbol <= madi_input_bit_stream (5 downto 1);
      madi_input_bit_counter <= "0001";
      madi_input_symbol_active <= '1';
     else
      if madi_input_bit_counter = 7 then
       madi_input_symbol <= madi_input_bit_stream (6 downto 2);
       madi_input_bit_counter <= "0010";
       madi_input_symbol_active <= '1';
      else
       if madi_input_bit_counter = 8 then
        madi_input_symbol <= madi_input_bit_stream (7 downto 3);
        madi_input_bit_counter <= "0011";
        madi_input_symbol_active <= '1';
       else
        if madi_input_bit_counter = 9 then
         madi_input_symbol <= madi_input_bit_stream (8 downto 4);
         madi_input_bit_counter <= "0100";
         madi_input_symbol_active <= '1';
        else
         if madi_input_symbol_active = '1' then
          madi_input_symbol_active <= '0';
         end if;
        end if;
       end if;
      end if;
     end if;
    end if;
   end if;
  end if;
 end process madi_receive_bit_stream_put;
 
 madi_receive_symbol_decode : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   madi_input_symbol_shift <= madi_input_symbol_shift(0) & madi_input_symbol_active;
   if madi_input_symbol_shift(1) = '1' then
    case madi_input_symbol is
     when "11110" =>
      madi_input_nibble <= "0000";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "01001" =>
      madi_input_nibble <= "0001";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10100" =>
      madi_input_nibble <= "0010";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10101" =>
      madi_input_nibble <= "0011";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "01010" =>
      madi_input_nibble <= "0100";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "01011" =>
      madi_input_nibble <= "0101";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "01110" =>
      madi_input_nibble <= "0110";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "01111" =>
      madi_input_nibble <= "0111";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10010" =>
      madi_input_nibble <= "1000";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10011" =>
      madi_input_nibble <= "1001";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10110" =>
      madi_input_nibble <= "1010";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "10111" =>
      madi_input_nibble <= "1011";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "11010" =>
      madi_input_nibble <= "1100";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "11011" =>
      madi_input_nibble <= "1101";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "11100" =>
      madi_input_nibble <= "1110";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when "11101" =>
      madi_input_nibble <= "1111";
      madi_input_nibble_clk <= '1';
      madi_input_nibble_rst <= '0';
     when others  =>
      madi_input_nibble <= "0000";
      madi_input_nibble_clk <= '0';
      madi_input_nibble_rst <= '1';
    end case;  
   end if;
  end if;
 end process madi_receive_symbol_decode;
 
 madi_input_place_nibble : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   if madi_input_nibble_rst = '1' then
    madi_input_nibble_cnt <= (others => '0');
   end if;
   if madi_input_channel_rst = '1' then
    madi_input_channel_rst <= '0';
   end if;
   if madi_input_nibble_clk = '1' then
    madi_input_nibble_cnt <= madi_input_nibble_cnt + 1;
    case madi_input_nibble_cnt is
     when "000" =>
      madi_input_nibble_buffer(0) <= madi_input_nibble;
     when "001" =>
      madi_input_nibble_buffer(1) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "010" =>
      madi_input_nibble_buffer(2) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "011" =>
      madi_input_nibble_buffer(3) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "100" =>
      madi_input_nibble_buffer(4) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "101" =>
      madi_input_nibble_buffer(5) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "110" =>
      madi_input_nibble_buffer(6) <= madi_input_nibble(0) & madi_input_nibble(1) & madi_input_nibble(2) & madi_input_nibble(3);
     when "111" =>
      madi_input_nibble_buffer(7) <= madi_input_nibble;
     when others =>
    end case;
    if madi_input_nibble(3) = '1' and madi_input_nibble_cnt = "000" then
     madi_input_channel_rst <= '1';
     madi_input_channel_cnt <= (others => '0');
    end if;
    madi_rx_write_enable <= madi_input_channel_rst;
    if madi_input_nibble_cnt = 7 then
     madi_rx_data <= madi_input_nibble_buffer(6) & madi_input_nibble_buffer(5) & madi_input_nibble_buffer(4) & madi_input_nibble_buffer(3) & madi_input_nibble_buffer(2) & madi_input_nibble_buffer(1);
     madi_input_channel_cnt <= madi_input_channel_cnt + 1;
     madi_rx_channel <= madi_input_channel_cnt;
    end if;
   end if;
  end if;
 end process madi_input_place_nibble;
 
 madi_input_generate_wordclk : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   madi_input_wordclk_shift <= madi_input_wordclk_shift(0) & madi_input_channel_rst;
   if madi_input_wordclk_shift = "01" then
    madi_input_wordclk_count <= (others => '0');
    madi_input_wordclk_current <= madi_input_wordclk_count;
   else
    madi_input_wordclk_count <= madi_input_wordclk_count + 1;
   end if;
   if madi_input_wordclk_count < '0' & madi_input_wordclk_current(madi_input_wordclk_current'left downto 1) then
    madi_wordclock_out <= '1';
   else
    madi_wordclock_out <= '0';
   end if;
  end if;
 end process madi_input_generate_wordclk;
 
 madi_tx_channel <= madi_output_address(8 downto 3);
 
 madi_output_shift_wordclock : process (madi_clk_25)
 begin
  if madi_clk_25'event and madi_clk_25 = '1' then
   madi_output_wordclock_shift <= madi_output_wordclock_shift(0) & madi_wordclock_in;
  end if;
 end process madi_output_shift_wordclock;
 
madi_output_shift_buffer : process (madi_clk_25)
 begin
  if madi_clk_25'event and madi_clk_25 = '1' then
   if madi_output_wordclock_shift = "01" then
    madi_output_transmit_commence <= '1';
   end if;
   if madi_output_transmit_commence = '1' and madi_output_transmit_idle = "01" then
    madi_output_address <= "000000001";
    madi_output_transmit_frame <= '1';
    madi_output_transmit_commence <= '0';
    madi_output_transmit_idle <= "00";
    madi_output_first_frame <= '0';
   else
    if madi_output_transmit_frame = '0' then
     case madi_output_transmit_idle is
      when "00" =>
       madi_output_transmit_idle <= "10";
      when "01" =>
       madi_output_transmit_idle <= "10";
      when "10" =>
       madi_output_transmit_idle <= "01";
      when others =>
     end case;
    else
     madi_output_address <= madi_output_address + 1; 
     if madi_output_address = 511 then
      madi_output_transmit_frame <= '0';
      madi_output_first_frame <= '1';
      madi_output_address <= (others => '0');
     end if;
    end if;
   end if;
   case madi_output_address(2 downto 0) is
    when "000" =>
     madi_output_nibble <= madi_output_first_frame & '1' & not(madi_output_address(3)) & '0';
     madi_output_data <= madi_tx_data;
     madi_output_vucp(0) <= madi_tx_data(23) xor madi_tx_data(22) xor madi_tx_data(21) xor madi_tx_data(20) xor madi_tx_data(19) xor madi_tx_data(18) xor madi_tx_data(17) xor madi_tx_data(16) xor madi_tx_data(15) xor madi_tx_data(14) xor madi_tx_data(13) xor madi_tx_data(12) xor madi_tx_data(11) xor madi_tx_data(10) xor madi_tx_data(9) xor madi_tx_data(8) xor madi_tx_data(7) xor madi_tx_data(6) xor madi_tx_data(5) xor madi_tx_data(4) xor madi_tx_data(3) xor madi_tx_data(2) xor madi_tx_data(1) xor madi_tx_data(0) xor madi_output_vucp(3) xor madi_output_vucp(2) xor madi_output_vucp(1);
    when "001" => 
     madi_output_nibble <= madi_output_data(0) & madi_output_data(1) & madi_output_data(2) & madi_output_data(3);
    when "010" => 
     madi_output_nibble <= madi_output_data(4) & madi_output_data(5) & madi_output_data(6) & madi_output_data(7);
    when "011" => 
     madi_output_nibble <= madi_output_data(8) & madi_output_data(9) & madi_output_data(10) & madi_output_data(11);
    when "100" => 
     madi_output_nibble <= madi_output_data(12) & madi_output_data(13) & madi_output_data(14) & madi_output_data(15);
    when "101" => 
     madi_output_nibble <= madi_output_data(16) & madi_output_data(17) & madi_output_data(18) & madi_output_data(19);
    when "110" => 
     madi_output_nibble <= madi_output_data(20) & madi_output_data(21) & madi_output_data(22) & madi_output_data(23);
    when "111" => 
     madi_output_nibble <= madi_output_vucp;
    when others =>
   end case;
  end if;    
 end process madi_output_shift_buffer;
 
 madi_output_nibble_to_symbol : process (madi_clk_25)
 begin
  if madi_clk_25'event and madi_clk_25 = '1' then
   if madi_output_transmit_idle = "00" then
    case madi_output_nibble is
     when "0000" =>
      madi_output_symbol <= "11110";
     when "0001" =>
      madi_output_symbol <= "01001";
     when "0010" =>
      madi_output_symbol <= "10100";
     when "0011" =>
      madi_output_symbol <= "10101";
     when "0100" =>
      madi_output_symbol <= "01010";
     when "0101" =>
      madi_output_symbol <= "01011";
     when "0110" =>
      madi_output_symbol <= "01110";
     when "0111" =>
      madi_output_symbol <= "01111";
     when "1000" =>
      madi_output_symbol <= "10010";
     when "1001" =>
      madi_output_symbol <= "10011";
     when "1010" =>
      madi_output_symbol <= "10110";
      when "1011" =>
      madi_output_symbol <= "10111";
     when "1100" =>
      madi_output_symbol <= "11010";
     when "1101" =>
      madi_output_symbol <= "11011";
     when "1110" =>
      madi_output_symbol <= "11100";
     when "1111" =>
      madi_output_symbol <= "11101";
     when others =>
    end case;
   else
    if madi_output_transmit_idle = "10" then
     madi_output_symbol <= "11000";
    else
     madi_output_symbol <= "10001";
    end if;
   end if;
  end if;
 end process madi_output_nibble_to_symbol;
 
 madi_output_bit_count : process (madi_clock_0_degrees)
 begin
  if madi_clock_0_degrees'event and madi_clock_0_degrees = '1' then
   if madi_output_bit_counter >= "100" then
  madi_output_bit_counter <= (others => '0');
  madi_output_symbol_shift <= madi_output_symbol;
 else
  madi_output_bit_counter <= madi_output_bit_counter + 1;
  madi_output_symbol_shift <= madi_output_symbol_shift(3 downto 0) & '0';
 end if;
  end if;
 end process madi_output_bit_count;
 
madi_output <= madi_output_symbol_shift(madi_output_symbol_shift'left); 
 
end behavioural;