Difference between revisions of "SP/DIF transmitter project"

From Hackerspace ACKspace
Jump to: navigation, search
(Created page with "{{Project |State=Completed |Members=Danny Witberg |Description=This project describes a lightweight SP/DIF transmitter in VHDL }} Introduction SP/DIF is a protocol for sending a...")
 
m
Line 4: Line 4:
 
|Description=This project describes a lightweight SP/DIF transmitter in VHDL
 
|Description=This project describes a lightweight SP/DIF transmitter in VHDL
 
}}
 
}}
Introduction
+
 
 +
== Introduction ==
  
 
SP/DIF is a protocol for sending and receiving digital audio over an optical or electrical transmission line. It is capable of sending two channels of up to 24 bits audio at a sample rate of up to 384kHz. However, most of the times you will encounter this protocol as 16 bit 44.1kHz, being the quality of a CD. Optical connector standard for SP/DIF is a TOSlink connector, developed by Toshiba. Electrical interface uses a cinch type connector.
 
SP/DIF is a protocol for sending and receiving digital audio over an optical or electrical transmission line. It is capable of sending two channels of up to 24 bits audio at a sample rate of up to 384kHz. However, most of the times you will encounter this protocol as 16 bit 44.1kHz, being the quality of a CD. Optical connector standard for SP/DIF is a TOSlink connector, developed by Toshiba. Electrical interface uses a cinch type connector.
Line 14: Line 15:
 
A frame consisting of two subframes are sent every sample. Every subframe contains, apart from the audio data from a channel, a preamble, validity, user, channel status and parity bit. The preamble breaks the rules of the biphase mark encoding for synchronisation purposes.
 
A frame consisting of two subframes are sent every sample. Every subframe contains, apart from the audio data from a channel, a preamble, validity, user, channel status and parity bit. The preamble breaks the rules of the biphase mark encoding for synchronisation purposes.
  
Biphase mark encoding through NRZI
+
 
 +
== Biphase mark encoding through NRZI ==
 +
 
 +
As said, a bit can be encoded in two ways, and it depends on the preceeding signal level. The first part has to be diffrent from the last part of the preceeding level. so if the last bit ended with a high signal level, a following '1' bit is encoded like "01". If the preceeding signal level was low, it is encoded "10".
 +
 
 +
All of this is very corresponding with the NRZI standard, where a '1' is encoded as a change in signal level, and a '0' is encoded with no change. Using this NRZI scheme, you can create a biphase marked '1' by sending "11" (two consecutive changes) and a '0' as "10" (a change, then no change). In short, sending a 1, then the actual bit through a NRZI encoder, results in a biphase marked signal.
 +
 
 +
== Parity generation ==
 +
 
 +
As SP/DIF uses even parity in its signal, every frame has an even number of ones and zeros. This means that every frame is sent using the same "signal polarity". The standard however states that both polarities have to decoded successfully because this can change by mixing up a balanced transmission line.
 +
 
 +
== VHDL design ==
 +
 
 +
A VHDL design has been developed, as lightweight as possible, resulting in only 81 logic elements (LE's) in an Altera FPGA.
 +
 
 +
<code>
 +
library ieee;
 +
use ieee.std_logic_1164.all;
 +
use ieee.std_logic_unsigned.all;
 +
 
 +
entity spdif_transmitter is
 +
port(
 +
  bit_clock : in std_logic; -- 128x Fsample (6.144MHz for 48K samplerate)
 +
  data_in : in std_logic_vector(23 downto 0);
 +
  address_out : out std_logic := '0'; -- 1 address bit means stereo only
 +
  spdif_out : out std_logic
 +
);
 +
end entity spdif_transmitter;
 +
 
 +
architecture behavioral of spdif_transmitter is
 +
 
 +
signal data_in_buffer : std_logic_vector(23 downto 0);
 +
signal bit_counter : std_logic_vector(5 downto 0) := (others => '0');
 +
signal frame_counter : std_logic_vector(8 downto 0) := (others => '0');
 +
signal data_biphase : std_logic := '0';
 +
signal data_out_buffer : std_logic_vector(7 downto 0);
 +
signal parity : std_logic;
 +
signal channel_status_shift : std_logic_vector(23 downto 0);
 +
signal channel_status : std_logic_vector(23 downto 0) := "001000000000000001000000";
 +
 +
begin
 +
 +
bit_clock_counter : process (bit_clock)
 +
begin
 +
  if bit_clock'event and bit_clock = '1' then
 +
  bit_counter <= bit_counter + 1;
 +
  end if;
 +
end process bit_clock_counter;
 +
 
 +
data_latch : process (bit_clock)
 +
begin
 +
  if bit_clock'event and bit_clock = '1' then
 +
parity <= data_in_buffer(23) xor data_in_buffer(22) xor data_in_buffer(21) xor data_in_buffer(20) xor data_in_buffer(19) xor data_in_buffer(18) xor data_in_buffer(17)  xor data_in_buffer(16) xor data_in_buffer(15) xor data_in_buffer(14) xor data_in_buffer(13) xor data_in_buffer(12) xor data_in_buffer(11) xor data_in_buffer(10) xor data_in_buffer(9) xor data_in_buffer(8) xor data_in_buffer(7) xor data_in_buffer(6) xor data_in_buffer(5) xor data_in_buffer(4) xor data_in_buffer(3) xor data_in_buffer(2) xor data_in_buffer(1) xor data_in_buffer(0) xor channel_status_shift(23);
 +
  if bit_counter = "000011" then
 +
data_in_buffer <= data_in;
 +
end if;
 +
  if bit_counter = "111111" then
 +
if frame_counter = "101111111" then
 +
  frame_counter <= (others => '0');
 +
else
 +
  frame_counter <= frame_counter + 1;
 +
end if;
 +
end if;
 +
  end if;
 +
end process data_latch;
 +
 
 +
data_output : process (bit_clock)
 +
begin
 +
  if bit_clock'event and bit_clock = '1' then
 +
  if bit_counter = "111111" then
 +
if frame_counter = "101111111" then -- next frame is 0, load preamble Z
 +
  address_out <= '0';
 +
  channel_status_shift <= channel_status;
 +
  data_out_buffer <= "10011100";
 +
else
 +
  if frame_counter(0) = '1' then -- next frame is even, load preamble X
 +
    channel_status_shift <= channel_status_shift(22 downto 0) & '0';
 +
  data_out_buffer <= "10010011";
 +
  address_out <= '0';
 +
  else -- next frame is odd, load preable Y
 +
  data_out_buffer <= "10010110";
 +
  address_out <= '1';
 +
  end if;
 +
end if;
 +
else
 +
if bit_counter(2 downto 0) = "111" then -- load new part of data into buffer
 +
  case bit_counter(5 downto 3) is
 +
  when "000" =>
 +
data_out_buffer <= '1' & data_in_buffer(0) & '1' & data_in_buffer(1) & '1' & data_in_buffer(2) & '1' & data_in_buffer(3);
 +
  when "001" =>
 +
data_out_buffer <= '1' & data_in_buffer(4) & '1' & data_in_buffer(5) & '1' & data_in_buffer(6) & '1' & data_in_buffer(7);
 +
  when "010" =>
 +
data_out_buffer <= '1' & data_in_buffer(8) & '1' & data_in_buffer(9) & '1' & data_in_buffer(10) & '1' & data_in_buffer(11);
 +
  when "011" =>
 +
data_out_buffer <= '1' & data_in_buffer(12) & '1' & data_in_buffer(13) & '1' & data_in_buffer(14) & '1' & data_in_buffer(15);
 +
  when "100" =>
 +
data_out_buffer <= '1' & data_in_buffer(16) & '1' & data_in_buffer(17) & '1' & data_in_buffer(18) & '1' & data_in_buffer(19);
 +
  when "101" =>
 +
data_out_buffer <= '1' & data_in_buffer(20) & '1' & data_in_buffer(21) & '1' & data_in_buffer(22) & '1' & data_in_buffer(23);
 +
  when "110" =>
 +
data_out_buffer <= "10101" & channel_status_shift(23) & "1" & parity;
 +
when others =>
 +
  end case;
 +
else
 +
  data_out_buffer <= data_out_buffer(6 downto 0) & '0';
 +
end if;
 +
end if;
 +
  end if;
 +
end process data_output;
 +
 +
biphaser : process (bit_clock)
 +
begin
 +
  if bit_clock'event and bit_clock = '1' then
 +
  if data_out_buffer(data_out_buffer'left) = '1' then
 +
    data_biphase <= not data_biphase;
 +
end if;
 +
  end if;
 +
end process biphaser;
 +
spdif_out <= data_biphase;
 +
 +
end behavioral;
 +
</code>

Revision as of 19:26, 29 July 2012

Project: SP/DIF transmitter project
Featured:
State Completed
Members Danny Witberg
GitHub No GitHub project defined. Add your project here.
Description This project describes a lightweight SP/DIF transmitter in VHDL
Picture
No project picture! Fill in form Picture or Upload a jpeg here


Introduction

SP/DIF is a protocol for sending and receiving digital audio over an optical or electrical transmission line. It is capable of sending two channels of up to 24 bits audio at a sample rate of up to 384kHz. However, most of the times you will encounter this protocol as 16 bit 44.1kHz, being the quality of a CD. Optical connector standard for SP/DIF is a TOSlink connector, developed by Toshiba. Electrical interface uses a cinch type connector.

SP/DIF is very similar to a professional standard, AES-EBU. In fact, only the signaling bit and their meaning is diffrent from SP/DIF. An SP/DIF transmitter can be connected to an AES-EBU receiver. However, from AES-EBU to SP/DIF can give unpredictable results.

Both AES/EBU and SP/DIF originate from the AES3 standard, developed by the Audio Engineering Society. It is a biphase mark encoded bitstream to ensure that signal integrity is maintained across the whole of the transmission line. Databits are sended as 10 or 01 sequence for a '1' bit, and 00 or 11 for a '0' bit.

A frame consisting of two subframes are sent every sample. Every subframe contains, apart from the audio data from a channel, a preamble, validity, user, channel status and parity bit. The preamble breaks the rules of the biphase mark encoding for synchronisation purposes.


Biphase mark encoding through NRZI

As said, a bit can be encoded in two ways, and it depends on the preceeding signal level. The first part has to be diffrent from the last part of the preceeding level. so if the last bit ended with a high signal level, a following '1' bit is encoded like "01". If the preceeding signal level was low, it is encoded "10".

All of this is very corresponding with the NRZI standard, where a '1' is encoded as a change in signal level, and a '0' is encoded with no change. Using this NRZI scheme, you can create a biphase marked '1' by sending "11" (two consecutive changes) and a '0' as "10" (a change, then no change). In short, sending a 1, then the actual bit through a NRZI encoder, results in a biphase marked signal.

Parity generation

As SP/DIF uses even parity in its signal, every frame has an even number of ones and zeros. This means that every frame is sent using the same "signal polarity". The standard however states that both polarities have to decoded successfully because this can change by mixing up a balanced transmission line.

VHDL design

A VHDL design has been developed, as lightweight as possible, resulting in only 81 logic elements (LE's) in an Altera FPGA.

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

entity spdif_transmitter is

port(
 bit_clock : in std_logic; -- 128x Fsample (6.144MHz for 48K samplerate)
 data_in : in std_logic_vector(23 downto 0);
 address_out : out std_logic := '0'; -- 1 address bit means stereo only
 spdif_out : out std_logic
);

end entity spdif_transmitter;

architecture behavioral of spdif_transmitter is

signal data_in_buffer : std_logic_vector(23 downto 0);
signal bit_counter : std_logic_vector(5 downto 0) := (others => '0');
signal frame_counter : std_logic_vector(8 downto 0) := (others => '0');
signal data_biphase : std_logic := '0';
signal data_out_buffer : std_logic_vector(7 downto 0);
signal parity : std_logic;
signal channel_status_shift : std_logic_vector(23 downto 0);
signal channel_status : std_logic_vector(23 downto 0) := "001000000000000001000000";

begin

bit_clock_counter : process (bit_clock)
begin
 if bit_clock'event and bit_clock = '1' then
  bit_counter <= bit_counter + 1;
 end if;
end process bit_clock_counter;
data_latch : process (bit_clock)
begin
 if bit_clock'event and bit_clock = '1' then

parity <= data_in_buffer(23) xor data_in_buffer(22) xor data_in_buffer(21) xor data_in_buffer(20) xor data_in_buffer(19) xor data_in_buffer(18) xor data_in_buffer(17) xor data_in_buffer(16) xor data_in_buffer(15) xor data_in_buffer(14) xor data_in_buffer(13) xor data_in_buffer(12) xor data_in_buffer(11) xor data_in_buffer(10) xor data_in_buffer(9) xor data_in_buffer(8) xor data_in_buffer(7) xor data_in_buffer(6) xor data_in_buffer(5) xor data_in_buffer(4) xor data_in_buffer(3) xor data_in_buffer(2) xor data_in_buffer(1) xor data_in_buffer(0) xor channel_status_shift(23);

  if bit_counter = "000011" then

data_in_buffer <= data_in; end if;

  if bit_counter = "111111" then

if frame_counter = "101111111" then frame_counter <= (others => '0'); else frame_counter <= frame_counter + 1; end if; end if;

 end if;
end process data_latch;
data_output : process (bit_clock)
begin
 if bit_clock'event and bit_clock = '1' then
  if bit_counter = "111111" then

if frame_counter = "101111111" then -- next frame is 0, load preamble Z address_out <= '0'; channel_status_shift <= channel_status; data_out_buffer <= "10011100"; else if frame_counter(0) = '1' then -- next frame is even, load preamble X

 	   channel_status_shift <= channel_status_shift(22 downto 0) & '0';

data_out_buffer <= "10010011";

	   address_out <= '0';

else -- next frame is odd, load preable Y data_out_buffer <= "10010110"; address_out <= '1'; end if; end if; else if bit_counter(2 downto 0) = "111" then -- load new part of data into buffer case bit_counter(5 downto 3) is when "000" => data_out_buffer <= '1' & data_in_buffer(0) & '1' & data_in_buffer(1) & '1' & data_in_buffer(2) & '1' & data_in_buffer(3); when "001" => data_out_buffer <= '1' & data_in_buffer(4) & '1' & data_in_buffer(5) & '1' & data_in_buffer(6) & '1' & data_in_buffer(7); when "010" => data_out_buffer <= '1' & data_in_buffer(8) & '1' & data_in_buffer(9) & '1' & data_in_buffer(10) & '1' & data_in_buffer(11); when "011" => data_out_buffer <= '1' & data_in_buffer(12) & '1' & data_in_buffer(13) & '1' & data_in_buffer(14) & '1' & data_in_buffer(15); when "100" =>

		 data_out_buffer <= '1' & data_in_buffer(16) & '1' & data_in_buffer(17) & '1' & data_in_buffer(18) & '1' & data_in_buffer(19);

when "101" => data_out_buffer <= '1' & data_in_buffer(20) & '1' & data_in_buffer(21) & '1' & data_in_buffer(22) & '1' & data_in_buffer(23); when "110" => data_out_buffer <= "10101" & channel_status_shift(23) & "1" & parity; when others => end case; else data_out_buffer <= data_out_buffer(6 downto 0) & '0'; end if; end if;

 end if;
end process data_output;

biphaser : process (bit_clock)
begin
 if bit_clock'event and bit_clock = '1' then
  if data_out_buffer(data_out_buffer'left) = '1' then
   data_biphase <= not data_biphase;

end if;

 end if;
end process biphaser;
spdif_out <= data_biphase;

end behavioral;