Difference between revisions of "MADI transceiver"
(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= | + | |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 |
Contents
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;