Настоящий документ представляет собой описание выносного блока цифро-аналогового преобразования (ЦАП) под условным названием ClockXtream.
Основные особенности конструкции:
Упрощенная функциональная блок-схема устройства представлена на рис.1
Рисунок 1
Особенности
конструкции
Конструктивно, блок ЦАП предполагается выполнить в виде двух
плат – основной платы ЦАП и платы первичных стабилизаторов напряжения. Силовые
трансформаторы (для питания цифровой, аналоговой частей и схемы тактового
генератора) располагаются на шасси корпуса ЦАП.
На задней стенке шасси расположены:
На передней панели блока ЦАП расположены:
Предусмотрено подключение платы управления и индикации через специальный разъем на основной плате. В отсутствии платы управления и индикации, управление режимами работы блока ЦАП осуществляется либо с помощью перемычек (Jumpers), либо через интерфейс RS-232 командами с удаленного компьютера. Подробное описание управляющих перемычек (Jumpers) и команд управления интерфейса RS-232 приведены в соответствующих разделах. Индикация основных режимов работы осуществляется светодиодами, установленными на передней панели.
Подключение дополнительных источников (DVD-A), предусмотрено через дополнительную плату приемника S/PDIF, через специальный разъем, установленный на основной плате ЦАП.
Конструктивно, основная плата представляет собой 4-х слойную печатную плату (диэлектрик – FR4, толщина проводников - ). Цифровая и аналоговая части развязаны гальванически с помощью оптоэлектронных трансиверов ISO150. При этом, задающий тактовый генератор и триггеры конечной синхронизации располагаются в аналоговой части платы, в непосредственной близости от м\с ЦАП. Тактовый сигнал CLK (256x), необходимый для работы цифровой части (цифрового фильтра, приемника аудиоданных, FPGA и тд), передается из аналоговой части в цифровую по одному из каналов ISO150. Цифровая часть содержит все необходимые элементы для приема аудиоданных (CS8412), передискретизации (PMD-100), управления режимами работы (FPGA и MCU) и индикации. Для уменьшения влияния цифровой части на аналоговую и задающий генератор, предусмотрена возможность электрического экранирования цифровой и аналоговой частей отдельными экранами. Кроме того, модуль задающего генератора дополнительно экранирован внешним эканом.
Данное устройство обладает возможностью коммутации различных источников цифрового сигнала:
Выбор
реализации Функциональных Элементов Блока ЦАП
Наличие режима Slave, и доступность на рынке, определили выбор в пользу CS8412.
Был выбран фильтр PMD-100 (HDCD) фирмы Pacific Microsonics. Данный фильтр многими оценивается как один из лучших для формата CD, и, кроме того, обладает широким набором дополнительных функций, доступных через программное управление (регулировка громкости, семь уровней псевдошума (dither), режимы 2Х, 4Х). Как альтернатива, может быть использован любой цифровой фильтр передискретизации (SM5842, SM5803, DF1700 …) с соответствующей коррекцией схемы.
Выбор схемы Тактового генератора и топологии доставки тактовых сигналов до м\с ЦАП – ключевое место в решении проблемы джиттера. Кардинальным решением представлялся бы выбор высокостабильного термостабилизированного (OCXO) генератора с синусоидальным выходным сигналом, преобразование сигнала генератора в ЭСЛ формат, построение драйвера тактовых сигналов и триггеров конечной пересинхронизации на ЭСЛ логике, преобразование сигналов в ТТЛ\КМОП непосредственно на входе м\с ЦАП. К сожалению генераторы OCXO крайне дороги и, как правило, разрабатываются на несколько стандартных частот (5, 10, 20 МГц). Как альтернатива OCXO, возможно использование более дешевого термокомпенсированного генератора (TCXO). При наличии высококачественного кристалла кварца, имеет смысл разработать малошумящий тактовый генератор на дискретных элементах. В любом случае, кварц или всю плату генератора следует изолировать механически от основной платы ЦАП и задемпфировать. Учитывая отсутствие отдельного кристалла кварца на необходимую частоту, и предложение Сергея Васянина (SN.Vasyanin@VAZ.RU) поделиться TCXO генератором на частоту 512х (22,5792 МГц), как начальный вариант, был выбран генератор TCXO с последующим делением частоты на два. При этом сохраняется возможность подключить вместо исходного генератора TCXO любой другой, более высококачественный генератор.
Ввиду отсутствия опыта работы с ЭСЛ и желания сократить временные затраты на разработку, было решено строить схему пересинхронизации на КМОП элементах серий АСТ\НСТ. Серия НСТ характеризуется ограниченным быстродействием, малыми искажениями формы сигналов и малыми излучениями на ВЧ. Среднеквадратичная величина джиттера на один вентиль составляет около 2.2 нс [??]. Серия АСТ характеризуется большим быстродействием (меньшей длительностью фронтов), но в тоже время требует приложения определенных усилий для предотвращения искажения сигнала (выбросов на фронтах). Среднеквадратичная величина джиттера на один вентиль составляет около 1 нс [??].
Тактовый сигнал с выхода TCXO генератора поступает на делитель на два, и далее – на коммутатор тактовых сигналов. Данный коммутатор (мультиплексор) необходим для обеспечения работы ЦАП либо от внутреннего TCXO генератора, либо от транспорта, либо от внешнего синхронизирующего сигнала. Драйвер, стоящий после мультиплексора, размножает тактовый сигнал на несколько отдельных сигналов. Каждый из входных сигналов м\с ЦАП пересинхронизируется на отдельном триггере. Таким образом, каждый триггер тактируется отдельным сигналом, поступающим от драйвера тактового сигнала. Данное построение представляет своего рода компромисс, так как увеличивает число цифровых устройств на пути тактового сигнала.
При выборе м\с ЦАП симпатии были на стороне ”мультибитовых” ЦАП. Кроме субъективных оценок, которые, как правило, положительны, данный тип ЦАП обладает тем преимуществом, что позволяет работать с частотами дискретизации от 44.1 до 400 кГц. К примеру, можно реализовать как режим 1х (Fs=44.1 кГц) без применения цифровой фильтрации, так и режим 8х (Fs=352.8 кГц), используя промышленный или программный фильтр-интерполятор. В случае использования источника DVD-A 192 kHz, возможно обойтись без использования цифровой фильтрации при ц\а преобразовании, подав сигнал непосредственно на ЦАП.
Среди ”мультибитовых” ЦАП, наверное, самым достойным является модуль Ultra Analog D20400, обеспечивающий “честные” 20 бит разрядности при частоте дискретизации 400 кГц. В качестве альтернативы редким и уже не выпускающимся ЦАП Ultra Analog D20400 можно применить м\с PCM63 и PCM1702 с соответствующей коррекцией схемы питания, схемы преобразования ток-напряжение и топологии печатной платы. Для понижения уровня шумов квантования (и соответственно увеличения динамического диапазона), можно применить параллельное включение нескольких одинаковых ЦАП. Удвоение числа ЦАП дает около 3 дБ выигрыша в силу некоррелированности шумов квантования отдельных ЦАП. Реальный же выигрыш, скорее всего будет ниже.
Не имея собственного опыта прослушивания различных вариантов схем аналоговой постфильтрации, и основываясь исключительно на отзывах сторонних разработчиков ЦАП, было решено делать два параллельных варианта фильтров. Первый – пассивный L-C фильтр Баттерворта третьего, четвертого или пятого порядка [??]. Конкретный порядок фильтра, а так же тип используемых компонентов будет определяться в процессе настройки и прослушивания. Предполагается обеспечить максимальную гибкость в установке различных элементов фильтра на плате на этапе разводки.
Второй вариант фильтра - активный фильтр Бесселя 7-го порядка на GIC элементах. Данная схема (вместе с выходным буфером) была позаимствована из Application Notes фирмы Analog Devices [??]. При желании, данная схема может быть трансформирована в GIC фильтр более низкого порядка (естественно, с соответствующим перерасчетом номиналов элементов). Выход данного фильтра представляет собой дифференциальный сигнал.
Для пассивного L-C фильтра предполагается использовать буферный каскад (“параллельный” повторитель) на дискретных элементах. Поскольку выход м\с представляет собой источник напряжения (с размахом сигнала 5В), возникло желание обойтись без традиционного усилителя напряжения, сократив таким образом число активных компонентов в цепи аналогового сигнала. Недостатком данного решения видится уменьшение амплитуды сигнала в два раза, что потребует наличия запаса по усилению последующего УНЧ. Данные потери обусловлены желанием иметь симметричный по нагрузкам входа\выхода фильтр. При этом, выходное сопротивление источника сигнала для фильтра реализуется последовательным включением резистора на выходе ЦАП.
Режимы работы транзисторов выбраны из условия работы буфера на низкоомную нагрузку (30 Ом) при малом сигнале (около 100 мВт). Данное условие определено желанием иметь возможность подключения низкоомных головных телефонов (типа Grado SR-125) непосредственно на выход буферного каскада.
Для питания цифровых устройств использованы интегральные стабилизаторы MIC5205 (Micrel). Данные стабилизаторы обладают хорошей стабильностью, малыми потерями и низкими шумами. Единственный недостаток – небольшой выходной ток (не более 200 мА). Поэтому для относительно сильноточных устройств (как фильтр PMD100 и светодиоды) использованы стабилизаторы MC78M05 (500mA). Вся цифровая часть условно разбита на несколько узлов, каждый из которых питается от отдельного стабилизатора. Наиболее пристальное внимание питанию было уделено в схемах задающего генератора, драйвера тактовых импульсов и триггеров пересинхронизации непосредственно перед модулем ЦАП. Каждая микросхема запитывается через локальный фильтр, состоящий из “ferrite bead” и керамической емкости.
Питание аналоговой части ЦАП и последующих каскадов предполагается осуществлять с помощью быстродействующих параллельных стабилизаторов, питающихся, в свою очередь, от источника тока. В качестве генераторов опорного напряжения активно использованы устройства TL431. В качестве альтернативы, в случае неудовлетворительной работы данных стабилизаторов, имеется возможность задействовать традиционные параметрические стабилизаторы.
Всю цифровую часть можно разбить на собственно цифровую (содержащую специализированные СБИС, микроконтроллер и программируемую логическую матрицу - FPGA) и аналоговую часть, содержащую тактовый генератор, и схему пересинхронизации. В отличие от других проектов [??], раздел между цифровой и аналоговой частями был проведен на входе конечных триггеров пересинхронизации. Принципиальным моментом здесь является то, что между тактовым генератором и цифровыми входами м\с ЦАП должно быть минимум цифровых устройств, и не должно содержаться никаких цифро-аналоговых трансляторов сигнала (типа оптопар, развязок ISO-150 и др.). Исходя из данных на разброс параметра задержки оптоизолятора ISO-150, можно сделать вывод о гораздо большем вкладе в увеличение фазовых флюктуаций фронтов этих устройств, чем типовых цифровых КМОП устройств (серий АСТ и НСТ).
Таким образом, тактовый генератор, и схема конечной пересинхронизации отделена от остальной (основной) цифровой части и располагается в непосредственной близи от м\с ЦАП. Вопрос взаимовлияния тактового генератора, схемы пересинхронизации и аналоговых каскадов (включая сам ЦАП) будет решен конструктивно, выбором соответствующей топологии печатной платы.
Собственно цифровая часть содержит приемник аудиоданных, цифровой фильтр, чип FPGA, контроллер, чип преобразователя уровней интерфейса RS-232, отдельные логические элементы, необходимые для работы платы, ГУН – формирователь частоты 384*Fs, буферы выходных синхросигналов.
Входной поток аудиоданных поступает на вход приемника CS8412 через импульсный трансформатор. Предполагается использование одного из двух входов для связи с источником аудиоданных – либо 50-ти омный BNC разъем, либо 75-ти омный RCA разъем. Одновременное подключение источников к двум входам не допускается. Приемник CS8412 включен по традиционной схеме, за исключением режима SLAVE, что подразумевает использования выводов SCK и FSYNC как входов для внешнего генератора тактовых сеток (64*Fs, Fs). Сигналы SCK и FSYNC поступают от FPGA, где реализован основной объем логических элементов. Используются два сигнала управления режимами работы приемника CS8412:
Подключение цифрового фильтра PMD-100 к CS8412 традиционно, за исключением следующего:
Для уменьшения интерференции с основным тактовым сигналом (MCLK), тактовый сигнал,
выделенный приемником из потока аудиоданных (MCK) блокируется вентилем U105, когда ЦАП работает в режиме MASTER (сигнал M/S_SEL =
“0”). В случае, когда ЦАП работает в режиме SLAVE (тактируется от транспорта), сигнал MCK[R] поступает в аналоговую часть на коммутатор тактового сигнала и
далее возвращается обратно в цифровую часть под видом основного тактового
сигнала MCLK.
В цифровом фильтре использованы практически все возможности управления режимами работы. При этом, в зависимости от того, используется ли процессорное управление, или управление с помощью перемычек, фильтр PMD-100 как же переходит либо в режим Program, либо в режим Stand Alone.
Чип FPGA управляется контроллером (MCU) через 8-ми разрядную шину данных. В отсутствии контроллера и каждый раз после включения питания, все внутренние регистры FPGA, устанавливаются в определенное исходное состояние (см. описание FPGA).
Микроконтроллер тактируется сигналом MCU_CLK (сигнал SCK),
частотой MCLK/4=Fs*64 (2.8224 МГц, в случае
использования основного тактового генератора с частотой 11.2896 МГц, или 3.072,
в случае использования основного тактового генератора с частотой 12.288 МГц).
Это позволяет использовать встроенный в микроконтроллер UART на скорости 1200 бит\с в первом
случае или 19200 бит\с во втором без рассогласования частот интерфейса.
Описание внутренней структуры FPGA
Большая часть логических элементов, необходимых для выполнения системных функций управления блоком ЦАП, реализована в виде программируемой логической матрицы (FPGA) EPF6016ATC144-3 фирмы Altera. Выбор конкретного чипа был обусловлен его наличием на этапе проектирования. Может быть использован другой чип FPGA (фирм Altera, Xilinx …) достаточного объема и быстродействия. Исходный код написан на языке Verilog, что упрощает его перенос на чипы других производителей FPGA/PLD. Исходтый текст программы для всех модулей приведен в приложении 1.
Функциональная блок-схема модулей FPGA приведена ниже:
Функциональное описание модулей:
Внутренние регистры FPGA
Внутренне адресное пространство FPGA состоит из пяти 8-ми разрядных регистров:
Address |
R/W |
Function |
Description |
0 |
R/W |
Data_Control_Register_0 |
d[2:0] Audio
Data Source Select: 0 – PMD-100. The 8x oversampling mode as
default. The 4x, 2x mode are available by PMD-100 program control from MCU. 1 – CS8412 (CD-DA 1x mode); 2 – DSP (external). The sampling frequency is
equal Fclk/32, which corresponds to 8x mode for CD-DA source. 3 – reserved; 4 – ASI (External, 1x mode, 48kHz); 5 – ASI (External, 2x mode, 96kHz); 6 – ASI (External, 4x mode, 192 kHz); 7 – reserved; d[3] Phase
Reverse 0 – Data as is; 1 – Data Phase is Reversed; d[4]
Soft Mute by PMD-100. Operating
when PMD-100 Data Source is selected. 0 – No Mute; 1 – Soft Mute is ON; d[5]
Hard Mute by PMD-100. Operating
when PMD-100 Data Source is selected. 0 – Hard Mute is ON; 1 – No Mute; d[6]
Dither by PMD-100. Operating
when PMD-100 Data Source is selected. 0 – Dither OFF; 1 – Dither ON; [7] Non-HDCD
signal Scaling method by PMD-100.
Scaling
(decreasing by 2 time) method selection for Standard (Non HDCD) signals. Operating
when PMD-100 Data Source is selected. 0 – Scaling in Digital domain (degrading of
CD-DA resolution to 15 bit!). In this case, the loudness will be similar for
regular and HDCD tracks; 1 – Scaling in Analog Domain (if realized).
This feature have not been realized, so regular CD-DA signals passes through
as is, and sounds two time louder then HDCD tracks; The
default value after reset: [7:0] 11100000 |
1 |
R/W |
Data_Control_Register_1 |
d[2:0] Digital_Sync_Out_Select Defines
the frequency at the Digital Sync Output 0 – Digital Sync is disabled; 1 – Fs (Focs/256); 2 – 2*Fs; 3 – 32*Fs; 4 – 64*Fs; 5 – 128*Fs; 6 – 256*Fs; 7 – 384*Fs; d[3] CS8412
Mode 0 – Normal Mode; 1 – Special Mode (No repeat data samples on
ERRORs); d[4]
CS8412 Output Codes Mode . 0 – ERROR Code is presented on E0/C0, E1/CA,
E2/CB outputs; 1 – Channel Status Code is presented on F0/CC
and other outputs. Used for De-Emphasis automatic control. d[5]
DAC Unit Synchronization mode. Defines
the Master/Slave mode for ClockXtream DAC unit. 0 – DAC is Master; 1 – DAC is Slave; d[6]
Hardware Control Mode. Defines
the type of hardware and signal control mode. 0 – Processor Control. Available only when
J103 is closed. 1 – Jumpers Control; [7] ASI
Bit Clock Phase Select Defines
the SCKI signal phase referenced to FSI signal. 0 – Each bit clock sample burst begins with
falling edge of SCKI signal. The data bits are latched to FPGA by rising edge of SCKI; 1 – Each bit clock sample burst begins with
rising edge of SCKI signal. The data bits are latched to FPGA by falling edge of SCKI The
default value after reset: [7:0] 00010000 |
2 |
R/W |
De-Glitch_Control_0 |
d[4:0]
De-Glitch Strobe. Rising Edge position of DG signal. The 5’b00000
value corresponds to position of rising edge of DG signal at the falling edge
of WCKO sample clock signal (D/A conversion begins). d[7:5]
Reserved |
3 |
R/W |
De-Glitch_Control_1 |
d[4:0]
De-Glitch Strobe. Falling Edge position of DG signal. The 5’b00000
value corresponds to position of falling edge of DG signal at the falling
edge of WCKO sample clock signal (D/A conversion begins). d[7:5]
Reserved |
4 |
R/W |
GPO_Register |
D[7:0] General
Purpose Register. Defines the value at the GP7…GP0 output pins of FPGA.
This pins connected to P218…P225 test pins of the pc board. |
5…7 |
- |
No |
|
Выбор контроллера (MCU)
Для управления режимами работы ЦАП был использован микроконтроллер AT90LS8535-4AC фирмы ATMEL. Параметры микроконтроллера, определившие выбор:
· 8
K bytes FLASH с возможностью
программирования через SPI
(используя принтерный порт РС);
·
512
bytes SRAM;
·
UART;
·
32 I/O
Lines;
·
Low
Cost ($4)
Команды управления блоком ЦАП по интерфейсу RS-232
TBD
Описание разъемов аппаратного управления (jumpers)
J101 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
FPGA Reset |
Нормальное функционирование FPGA |
Close |
Сброс FPGA |
J103 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
Control Mode |
Аппаратное управление (jumpers control) |
Close |
Программное управление (при состоянии сигнала PROC_CTR в “0”) |
J401 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
MSB_X |
Выравнивание слова данных на входе ЦАП (MSB first) |
Close |
Выравнивание слова данных на входе ЦАП (LSB first) |
J402/403 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
Passive Filter |
Should
be Close when Passive Filter is used |
Close |
||||
3 |
4 |
Open |
Active Filter 1 |
Should
be Close when Active Filter 1 is used |
Close |
||||
5 |
6 |
Open |
Active Filter 2 |
Should
be Close when Active Filter 2 is used |
Close |
J501 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
Output Data for DSP |
Данные на интерфейсном разъеме J502 запрещены (Z-состояние) |
Close |
Данные на интерфейсном разъеме J502 разрешены |
J505 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
DITHER |
Dither
ON |
Close |
Dither OFF |
|||
3 |
4 |
Open |
HARD
MUTE |
Hard
MUTE is ON |
Close |
Hard MUTE
is OFF (normal operation) |
|||
5 |
6 |
Open |
SOFT
MUTE |
Soft
MUTE is ON |
Close |
Soft MUTE
is OFF (normal operation) |
|||
7 |
8 |
Open |
CS8412
MODE |
Special
audio port mode (No repeat on Error) |
Close |
Normal
audio port mode (L/R, 16-24 bits) |
|||
13 |
14 |
Open |
Non
HDCD Signal 6dB
Gain Scaling |
Analog
Gain Scaling (not realized) |
Close |
Gain
Scaling is performed in the Digital domain of PMD-100 |
J506 |
Сост. |
Название перемычки |
Описание положений |
|
1 |
2 |
Open |
PMD-100
Output
word size |
Should
be Close for 20-bits output |
Close |
||||
3 |
4 |
Open |
Should
be Open for 20-bits output |
|
Close |
||||
5 |
6 |
Open |
PMD-100
Output
data format |
2’s
complement (default) |
Close |
Complementary
offset binary |
|||
7 |
8 |
Open |
PMD-100
Input
data justification |
Data
right justified 16 bits |
Close |
Data
assumed to be left justified up to 24 bits in length |
|||
9 |
10 |
Open |
PMD-100
Input
data latching |
Input
data latched on falling edge of BCKI |
Close |
Input
data latched on rising edge of BCKI |
|||
11 |
12 |
Open |
DAC Synchronization
Mode |
DAC
is SLAVE |
Close |
DAC is
MASTER |
|||
13 |
14 |
Open |
CS8412
Status/Errors Outputs mode |
Channel
Status displays on CA…CE (emphasis control) |
Close |
ERROR
information displays on E0…E2 |
Описание
Интерфейсных разъемов
J201
J502
J503
J504
J507
J508
J404
J405
J406
Параметры
блока ЦАП
Приложение
1. Исходные коды для модулей FPGA (Verilog)
// Sergey
V. Smirnov
//`include
"sys_logic.v"
//`include
"error_decoder.v"
//`include
"clks_former.v"
//`include
"cs8412_1x_conv.v"
//`include
"asi_1x_conv.v"
//`include
"dsp_dg_conv.v"
//`include
"sync_out_former.v"
//`include
"out_data_mux.v"
module top(
data, address, oe, wr, clk, rst, //
Data, Address, Read, Write, Master Clock
bck_en, wcko_o, dat_l, dat_r,
dg_o, // Re-Clocked Output Data for DAC
bcko, wcko, dol, dor, dg, // Input Data from PD-100
sdata, fsync, sck, // Input Data from SC8412, Data
Clocks for CS8412
sdi, scki, fsi, // External data from up to
192kSps source, clocks for external ASI chip (CS8416)
dsp_dl, dsp_dr, dsp_wck,
dsp_bcken, // External data from DSP
m3, sel, // CS8412 Mode Control
ms_sel, proc_ctrl, // Sysrem Hardware Control
dig_sync_out, // Digital Sync Output (1x,
2x, 32x, 64x, 128x, 256x, 384x, disable)
h_mute, s_mute, dither, scal, // PD-100 Hardware Control
vco_out, pd_ref, pd_osc, // 384x PLL VCO signals
gp0, gp1, gp2, gp3,
gp4, gp5, gp6, gp7, // General Purpose Outputs
e0, e1, e2, // Error Code from CS8412
vbh, cf, sls, pari, bpce, nl // Decoded Errors signals to LEDs
);
inout [7:0]
data;
input [2:0]
address;
input oe,
wr, clk, rst,
bcko, wcko, dol, dor, dg,
dsp_dl, dsp_dr, dsp_wck, dsp_bcken,
sdata, sdi,
vco_out; // 384*Fs signal
input e0,
e1, e2; // Error Code from
CS8412
output
bck_en, wcko_o, dat_l, dat_r, dg_o,
// Re-Clocked Output Data for DAC
fsync, sck, // Fs and 64*Fs for CS8412 Receiver
fsi, scki, // Fs and 64*Fs for External ASI Receiver chip (CS8416)
m3, sel, // CS8412 Mode Control
dig_sync_out, // Synchronization Output Clock for Transport
ms_sel, proc_ctrl, // Sysrem Hardware Control
h_mute, s_mute, dither, scal, // PD-100 Hardware Control
pd_ref, pd_osc; // 384x PLL VCO signals
output vbh,
cf, sls, pari, bpce, nl, //
LEDs
gp0, gp1, gp2, gp3, gp4, gp5, gp6,
gp7; // General Purpose Outputs
wire oe,
wr, clk, rst;
wire [7:0]
data_in, data_out;
wire fsync,
sck, m3, sel, dig_sync_out,
ms_sel, proc_ctrl, h_mute, s_mute,
dither, scal,
pd_ref, pd_osc, phase_rev;
wire
bck_en, wcko_o, dat_l, dat_r, dg_o;
wire clk1x,
clk2x, clk32x, clk4x, clk8x, clk16x, clk64x, clk128x, clk384x;
wire
cs8412_dl, cs8412_dr, cs8412_wck, cs8412_bcken, cs8412_dg;
wire
asi_dl, asi_dr, asi_wck, asi_bcken, asi_dg;
wire
dsp_dl, dsp_dr, dsp_wck, dsp_bcken, dsp_dg;
wire fsync_strb,
sck_strb, fsi_strb, scki_strb, bcphase;
wire [2:0]
data_source_sel, sync_out_sel;
wire [4:0]
dgi_rise, dgi_fall;
assign
pd_ref = clk128x;
assign
data_in = data;
assign data
= oe ? 8'bz : data_out;
/////
//assign
//ctsroota1
clocktree_0(.Y(clk), .A(clk_in));
sys_logic
sys_logic_0 ( data_in, data_out, address, wr, rst,
data_source_sel,
sync_out_sel, phase_rev, bcphase,
h_mute, s_mute,
dither, scal,
m3, sel, ms_sel,
proc_ctrl,
dgi_rise,
dgi_fall, // De-Glitch
strobe, Edges Position (resolution is 1/32 of Fs)
gp0, gp1, gp2, gp3,
gp4, gp5, gp6, gp7 ); // General
Purpose Outputs
error_decoder
error_decoder_0(e0, e1, e2, // Error Code
vbh, cf, sls,
pari, bpce, nl); // Decoded signals
to LEDs
sync_out_former
sync_out_former_0( clk, rst, vco_out,
pd_osc, dig_sync_out, // CLK =
256*Fs, VCO_OUT = 384*Fs
clk1x, clk2x, clk32x, clk64x, clk128x, //
sync_out_sel );
clks_former
clks_former_0( clk, rst, clk1x, clk2x,
clk4x, clk8x, clk16x, clk32x, clk64x, clk128x,
fsync, sck,
fsync_strb, sck_strb,
scki, fsi,
fsi_strb, scki_strb,
bcphase,
data_source_sel);
cs8412_1x_conv
cs8412_1x_conv_0( clk, rst, sdata, fsync, clk2x, clk4x, clk8x, clk16x,
fsync_strb, sck_strb,
dgi_rise,
dgi_fall,
cs8412_dl,
cs8412_dr, cs8412_wck, cs8412_bcken, cs8412_dg );
asi_1x_conv
asi_1x_conv_0( clk, rst,
data_source_sel,
clk2x, clk4x, clk8x, clk16x, clk32x, clk64x,
sdi, fsi,
fsi_strb, scki_strb,
dgi_rise,
dgi_fall,
asi_dl, asi_dr,
asi_wck, asi_bcken, asi_dg );
dsp_dg_conv
dsp_dg_conv_0( clk, rst,
dgi_rise,
dgi_fall,
dsp_wck, dsp_dg );
out_data_mux
out_data_mux_0( dat_l, dat_r, bck_en, wcko_o, dg_o, // Output Signals to DAC
data_source_sel,
clk, rst, phase_rev,
dol, dor, bcko,
wcko, dg, // From PD-100
Digital Filter
cs8412_dl,
cs8412_dr, cs8412_wck, cs8412_bcken, cs8412_dg, // From CS8412 S/PDIF Receiver (1x Mode)
asi_dl, asi_dr, asi_wck, asi_bcken, asi_dg, // From external S/PDIF Receiver (like
CS8416)
dsp_dl, dsp_dr,
dsp_wck, dsp_bcken, dsp_dg ); // From
external DSP (Fs= up to 384 kHz)
endmodule
//
module
asi_1x_conv( clk, rst, data_source_sel,
clk2x, clk4x, clk8x,
clk16x, clk32x, clk64x,
sdi, fsi, fsi_strb,
scki_strb,
dgi_rise, dgi_fall,
asi_dl, asi_dr, asi_wck,
asi_bcken, asi_dg );
input clk, rst;
// Global Clock and Reset
input [2:0]
data_source_sel;
input sdi, fsi, clk2x, clk4x, clk8x, clk16x,
clk32x, clk64x,
fsi_strb, scki_strb; // Strobes for save the data from External
Receiver
input [4:0]
dgi_rise, dgi_fall; // De-Glitch
Interval, rise and fall edge values
output asi_dl, asi_dr, asi_wck, asi_bcken,
asi_dg; // Output to Output Multiplexer
reg [19:0]
reg_l, reg_r; // Registers for
separate storing of left and right channel data from CS8416 receiver
reg [19:0]
dat_l, dat_r; // Registers for
outgoing the L/R data to DAC
reg
[4:0] wcko_reg; // Counter for WCKO and BCK_EN strobes
forming register
reg
[1:0] bck_en_reg; // Register for shifting of BCK_EN
strobe for two Clocks
reg
dg; // De-Glitch Interval Trigger
reg [4:0]
dg_int; // De-Glitch Interval
counter
//
De-Glitch Interval counter depends on Fs
always
@(fsi or clk2x or clk4x or clk8x or clk16x or clk32x or clk64x or
data_source_sel)
begin
case (data_source_sel)
3'd4 : dg_int = {~fsi, clk2x, clk4x,
clk8x, clk16x}; // for Fs = 48
kHz (if MCLK = 12,288 MHz)
3'd5 : dg_int = {~fsi, clk4x, clk8x,
clk16x, clk32x}; // for Fs = 96
kHz (if MCLK = 12,288 MHz)
3'd6 : dg_int = {~fsi, clk8x, clk16x,
clk32x, clk64x}; // for Fs = 192
kHz (if MCLK = 12,288 MHz)
default dg_int = 5'b0;
endcase
end
wire
bcko; // BitClock
Strobe, undelayed
assign bcko
= |wcko_reg; // BCKO will be 16+4=20
clocks of CLK
assign
asi_wck = ~(wcko_reg[4] | (wcko_reg[3] & wcko_reg[2] & (wcko_reg[1] |
wcko_reg[0])));
assign
asi_dl = dat_l[19];
assign
asi_dr = dat_r[19];
assign
asi_bcken = bck_en_reg[1]; // Delayed
by two clocks after WCK negedge
assign
asi_dg = dg;
// Save
Left Data to intermediate register
wire
scki_strb_fsi;
assign
scki_strb_fsi = scki_strb & fsi;
always
@(posedge clk or negedge rst)
if (~rst)
reg_l <= 20'b0;
else if (scki_strb_fsi)
reg_l <= {reg_l[18:0], sdi}; // Save data during first half of FSI strobe
else
reg_l <= reg_l;
// Save
Right Data to intermediate register
wire
scki_strb_notfsi;
assign
scki_strb_notfsi = scki_strb & ~fsi;
always
@(posedge clk or negedge rst)
if (~rst)
reg_r <= 20'b0;
else if (scki_strb_notfsi)
reg_r <= {reg_r[18:0], sdi}; // Save data during second half of FSI
strobe
else
reg_r <= reg_r;
// Remove
Left Data from intermediate register to output DAT_L register, and shifting out
when BCK_EN
always
@(posedge clk or negedge rst)
if (~rst)
dat_l <= 20'b0;
else if (fsi_strb) // Remove data from REG_L to DAT_L
dat_l <= reg_l;
else if (asi_bcken) // BCK_EN duration is 20
clocks
dat_l <= {dat_l[18:0], dat_l[0]}; // Shift out data and leave the least bit
as filled value
else
dat_l <= dat_l;
// Remove
Right Data from intermediate register to output DAT_Rregister, and shifting out
when BCK_EN
always
@(posedge clk or negedge rst)
if (~rst)
dat_r <= 20'b0;
else if (fsi_strb)
dat_r <= reg_r;
else if (asi_bcken)
dat_r <= {dat_r[18:0], dat_r[0]};
else
dat_r <= dat_r;
// WCKO and
BCK_EN strobe former
wire
state0; // High, when WCKO_REG
will reach the state = 19,
assign
state0 = ~(|wcko_reg);
always
@(posedge clk)
if (~rst)
wcko_reg <= 5'd20;
else if (fsi_strb) // Beginning of counting
wcko_reg <= 5'd20;
else if (state0) // State = 0 ??
wcko_reg <= wcko_reg; // Save the (00000) state until the new
FCYNC_STRB strobe will come
else //
So, the WCKO is 8 clocks of CLK, ~BCK_EN is 20 clocks of CLK
wcko_reg <= wcko_reg - 1; // Counting down during the 20 clocks
// BCK_EN
strobe shifting
always
@(posedge clk or negedge rst)
if (~rst)
bck_en_reg <= 2'b0; // Just Global reset
else
bck_en_reg <= {bck_en_reg[0],
bcko}; // Shifting the BCKO strobe by
two clocks
//
De-Glitch interval former
wire set,
reset;
assign set
= (dg_int == dgi_rise);
assign
reset = (dg_int == dgi_fall);
always
@(negedge clk or negedge rst) //
DeGlitch R-S Trigger
if (~rst)
dg <= 1'b0;
else if (set)
dg <= 1'b1;
else if (reset)
dg <= 1'b0;
else
dg <= dg;
endmodule
//
module
clks_former ( clk, rst, clk1x, clk2x, clk4x, clk8x, clk16x, clk32x, clk64x, clk128x,
fsync, sck, fsync_strb,
sck_strb,
scki, fsi, fsi_strb,
scki_strb,
bcphase,
data_source_sel);
input clk, rst,
// Global Clock and Reset
bcphase; // Bit Clock Phase of External Audio Serial Interface (ASI)
input [2:0]
data_source_sel;
output clk1x, clk2x, clk4x, clk8x, clk16x, clk32x,
clk64x, clk128x,
fsync, sck, // Bit Clock and Frame
Clock (Fs) of CS8412 Receiver
scki, fsi; // Bit Clock and Frame Clock (Fs) of External Audio Serial
Interface (ASI)
output fsync_strb, sck_strb, // Strobes for save the 1x data from CS8412
fsi_strb, scki_strb; // Strobes for save the data from External
S/PDIF Receiver
reg [7:0]
div256;
wire clk1x,
clk2x, clk4x, clk8x, clk16x, clk32x, clk64x, clk128x;
always
@(posedge clk or negedge rst)
if (~rst)
div256 <= 8'b0;
else
div256 <= div256 + 1;
assign clk1x = div256[7],
clk2x = div256[6],
clk4x = div256[5],
clk8x = div256[4],
clk16x = div256[3],
clk32x = div256[2],
clk64x = div256[1],
clk128x = div256[0];
assign fsync = div256[7],
sck
= ~div256[1],
fsync_strb = div256[0] & div256[1]
& div256[2] & div256[3] & div256[4] & div256[5] & div256[6]
& ~div256[7],
// Strobe
before FSYNC pos edge
sck_strb = div256[0] & ~div256[1]
& ~div256[6];
// Strobes for
SCK (first 16 bits after every edge of FSYNC)
///////
Multiplexer for External ASI Clock Signals
////// SCKI and FSI are used as source for
External S/PDIF Receiver
reg scki,
fsi, fsi_strb, scki_strb;
always
@(div256 or clk1x or clk2x or clk32x or clk64x or clk128x or clk or
data_source_sel or bcphase)
case (data_source_sel)
3'd4 :
begin //
for Fs = 48 kHz (if MCLK = 12,288 MHz)
fsi = clk1x;
scki = clk64x ^ bcphase;
fsi_strb = fsync_strb;
scki_strb = div256[0] &
~div256[1] & ~(div256[6] & (div256[5] | div256[4])); // 20 scki clocks
end
3'd5 :
begin //
for Fs = 96 kHz (if MCLK = 12,288 MHz)
fsi = clk2x;
scki = clk128x ^ bcphase;
fsi_strb = div256[0] &
div256[1] & div256[2] & div256[3] & div256[4] & div256[5] &
~div256[6];
scki_strb = div256[0] &
~(div256[5] & (div256[4] | div256[3]));
// 20 scki clocks
end
3'd6 :
begin //
for Fs = 192 kHz (if MCLK = 12,288 MHz)
fsi = div256[5]; // Clk4x =
192 kHz
scki = ~clk ^ bcphase;
fsi_strb = div256[0] &
div256[1] & div256[2] & div256[3] & div256[4] & ~div256[5];
scki_strb = ~(div256[4] &
(div256[3] | div256[2])); // 20 scki
clocks
end
default begin // Clocks are disabled when External ASI have not
selected
fsi = 1'b0;
scki = 1'b0;
fsi_strb = 1'b0;
scki_strb = 1'b0;
end
endcase
endmodule
//
module
cs8412_1x_conv ( clk, rst, sdata, fsync, clk2x, clk4x, clk8x, clk16x,
fsync_strb, sck_strb,
dgi_rise, dgi_fall,
cs8412_dl, cs8412_dr,
cs8412_wck, cs8412_bcken, cs8412_dg );
input clk, rst;
// Global Clock and Reset
input sdata, fsync, clk2x, clk4x, clk8x, clk16x,
fsync_strb, sck_strb; // Strobes for
save the 1x data from CS8412
input [4:0]
dgi_rise, dgi_fall; // De-Glitch Interval,
rise and fall edge values
output cs8412_dl, cs8412_dr, cs8412_wck,
cs8412_bcken, cs8412_dg; // Output to
Output Multiplexer
reg [15:0]
reg_l, reg_r; // Registers for
separate storing of left and right channel data from CS8412 receiver
reg [15:0]
dat_l, dat_r; // Registers for
outgoing the L/R data to DAC
reg
[4:0] wcko_reg; // Counter for WCKO and BCK_EN strobes
forming register
reg
[1:0] bck_en_reg; // Register for shifting of BCK_EN
strobe for two Clocks
reg
dg; // De-Glitch Interval Trigger
wire [4:0]
dg_int; // De-Glitch Interval
counter
assign
dg_int = {~fsync, clk2x, clk4x, clk8x, clk16x};
wire
bcko; // BitClock
Strobe, undelayed
assign bcko
= |wcko_reg; // BCKO will be 16+4=20
clocks of CLK
assign
cs8412_wck = ~(wcko_reg[4] | (wcko_reg[3] & wcko_reg[2] & (wcko_reg[1]
| wcko_reg[0])));
assign
cs8412_dl = dat_l[15];
assign
cs8412_dr = dat_r[15];
assign
cs8412_bcken = bck_en_reg[1];
assign
cs8412_dg = dg;
// Save
Left Data to intermediate register
wire
sck_strb_fsync;
assign
sck_strb_fsync = sck_strb & fsync;
always
@(posedge clk or negedge rst)
if (~rst)
reg_l <= 16'b0;
else if (sck_strb_fsync)
reg_l <= {reg_l[14:0], sdata}; // Save data during first half of FSYNC
strobe
else
reg_l <= reg_l;
// Save
Right Data to intermediate register
wire
sck_strb_notfsync;
assign
sck_strb_notfsync = sck_strb & ~fsync;
always
@(posedge clk or negedge rst)
if (~rst)
reg_r <= 16'b0;
else if (sck_strb_notfsync)
reg_r <= {reg_r[14:0], sdata}; // Save data during second half of FSYNC
strobe
else
reg_r <= reg_r;
// Remove
Left Data from intermediate register to output DAT_L register, and shifting out
when BCK_EN
always
@(posedge clk or negedge rst)
if (~rst)
dat_l <= 16'b0;
else if (fsync_strb) // Remove data from REG_L to
DAT_L
dat_l <= reg_l;
else if (cs8412_bcken) // BCK_EN duration is 20
clocks
dat_l <= {dat_l[14:0], dat_l[0]}; // Shift out data and leave the least bit
as filled value
else
dat_l <= dat_l;
// Remove
Right Data from intermediate register to output DAT_Rregister, and shifting out
when BCK_EN
always
@(posedge clk or negedge rst)
if (~rst)
dat_r <= 16'b0;
else if (fsync_strb)
dat_r <= reg_r;
else if (cs8412_bcken)
dat_r <= {dat_r[14:0], dat_r[0]};
else
dat_r <= dat_r;
// WCKO and
BCK_EN strobe former
//wire
state19; // High, when WCKO_REG
will reach the state = 19,
//assign
state19 = wcko_reg[4] & ~wcko_reg[3] & ~wcko_reg[2] & wcko_reg[1]
& wcko_reg[0];
wire
state0; // High, when WCKO_REG
will reach the state = 19,
assign
state0 = ~(|wcko_reg);
always
@(posedge clk)
if (~rst)
wcko_reg <= 5'd20;
else if (fsync_strb) // Beginning of counting
wcko_reg <= 5'd20;
else if (state0) // State = 0 ??
wcko_reg <= wcko_reg; // Save the (00000) state until the new
FCYNC_STRB strobe will come
else // So, the WCKO is 8 clocks of CLK, ~BCK_EN
is 20 clocks of CLK
wcko_reg <= wcko_reg - 1; // Counting down during the 20 clocks
// BCK_EN
strobe shifting
always
@(posedge clk or negedge rst)
if (~rst)
bck_en_reg <= 2'b0; // Just Global reset
else
bck_en_reg <= {bck_en_reg[0],
bcko}; // Shifting the BCKO strobe by
two clocks
//
De-Glitch interval former
wire set,
reset;
assign set
= (dg_int == dgi_rise);
assign
reset = (dg_int == dgi_fall);
always
@(negedge clk or negedge rst) //
DeGlitch R-S Trigger
if (~rst)
dg
<= 1'b0;
else if (set)
dg <= 1'b1;
else if (reset)
dg <= 1'b0;
else
dg <= dg;
endmodule
//
module
dsp_dg_conv( clk, rst,
dgi_rise, dgi_fall,
dsp_wck, dsp_dg );
input clk, rst;
// Global Clock and Reset
input dsp_wck;
// Strobes for save the 1x data from External Receiver
input [4:0]
dgi_rise, dgi_fall; // De-Glitch
Interval, rise and fall edge values
output dsp_dg; // Output to Output Multiplexer
reg
[4:0] dg_cnt; // Counter for WCKO and BCK_EN strobes forming register
reg
wck_strb_reg; // Register for
shifting of BCK_EN strobe for two Clocks
reg
dg; // De-Glitch
Interval Trigger
wire
wck_strb; // De-Glitch Interval
counter
assign
dsp_dg = dg;
assign
wck_strb = wck_strb_reg & ~dsp_wck;
// Strobe is One CLK period after negedge of DSP_WCKO
// Be
ginning of WCKO strobe
always
@(posedge clk or negedge rst)
if (~rst)
wck_strb_reg <= 2'b0; // Just Global reset
else
wck_strb_reg <= dsp_wck; // Shifting the DSP_WCKO strobe by one
clock
// DG
Interval Counter
always
@(posedge clk)
if (~rst)
dg_cnt <= 5'b00001;
else if (wck_strb) // Set to the second state (00001), because wck_strb is
delayed by one CLK after
dg_cnt <= 5'b00001; //
beginning of WCKO interval
else
dg_cnt <= dg_cnt + 1; // Counting during the 32 clocks
//
De-Glitch interval former
wire set,
reset;
assign set
= (dg_cnt == dgi_rise);
assign reset
= (dg_cnt == dgi_fall);
always
@(negedge clk or negedge rst) //
DeGlitch R-S Trigger
if (~rst)
dg <= 1'b0;
else if (set)
dg <= 1'b1;
else if (reset)
dg <= 1'b0;
else
dg <= dg;
endmodule
//
module
error_decoder (e0, e1, e2, // Error Code
vbh, cf, sls, pari,
bpce, nl); // Decoded signals to LEDs
input e0, e1, e2;
output vbh,
cf, sls, pari, bpce, nl;
reg [5:0]
leds;
wire [2:0]
error_code;
assign
error_code = {e2, e1, e0};
always
@(error_code)
begin
case (error_code)
3'd0 : leds = 6'b000000; // No Error
3'd1 : leds = 6'b000001; // Validity Bit High
3'd2 : leds = 6'b000010; // Confidence Flag
3'd3 : leds = 6'b000100; // Slipped Sample
3'd5 : leds = 6'b001000; // Parity Error
3'd6 : leds = 6'b010000; // Bi-Phase Coding Error
3'd7 : leds = 6'b100000; // No Lock
default leds = 6'b0;
endcase
end
assign vbh
= leds[0],
cf = leds[1],
sls = leds[2],
pari = leds[3],
bpce = leds[4],
nl = leds[5];
endmodule
//
module
out_data_mux (dat_l, dat_r, bck_en, wcko_o, dg_o, // Output Signals to DAC
data_source_sel, clk,
rst, phase_rev,
dol, dor, bcko, wcko, dg, // From PD-100 Digital Filter
cs8412_dl, cs8412_dr,
cs8412_wck, cs8412_bcken, cs8412_dg,
// From CS8412 S/PDIF Receiver (1x Mode)
asi_dl, asi_dr, asi_wck,
asi_bcken, asi_dg, // From external
S/PDIF Receiver (like CS8416)
dsp_dl, dsp_dr, dsp_wck,
dsp_bcken, dsp_dg // From external DSP
(Fs= up to 384 kHz)
);
input [2:0]
data_source_sel;
input clk,
rst, phase_rev, dol, dor, bcko, wcko, dg,
cs8412_dl, cs8412_dr, cs8412_wck,
cs8412_bcken, cs8412_dg,
asi_dl, asi_dr, asi_wck, asi_bcken,
asi_dg,
dsp_dl, dsp_dr, dsp_wck, dsp_bcken,
dsp_dg;
output
dat_l, dat_r, bck_en, wcko_o, dg_o;
reg dat_l,
dat_r, bck_en, wcko_o, dg_o; //
First Re-Clock Triggers (FPGA Outputs)
reg dl_n1,
dr_n1, bc_n1, wc_n1, dg_n1; //
Local nets beetween MUX and Re-Clock Triggers
reg
phase; // Phase of output data
reg tr1,
tr2; // Bit Clock Enable Strobe Former
// TR1 - Pulses beetween BCKO
and CLK Positive Edges
reg dl_pre,
dr_pre, wcko_pre, dg_pre;
/////////////
First Re-Clocking of Input Data from PMD-100
always
@(posedge clk)
begin
dl_pre <= dol;
dr_pre <= dor;
wcko_pre <= wcko;
dg_pre <= dg;
end
/////////////
BCK_EN Strobe Former
wire bcken;
assign
bcken = tr1;
wire
tr_rst;
assign
tr_rst = rst & ~tr2;
always
@(posedge bcko or negedge tr_rst)
if (~tr_rst)
tr1 <= 1'b0;
else
tr1 <= 1'b1;
always
@(posedge clk or negedge tr_rst)
if (~tr_rst)
tr2 <= 1'b0;
else
tr2 <= tr1;
// Phase
Reverse control signal is synchronized with negative edge of WCKO
always
@(negedge wcko_o or negedge rst)
if (~rst)
phase <= 1'b0;
else
phase <= phase_rev;
// Output
Multiplexer for DATA_LEFT
always
@(data_source_sel or dol or cs8412_dl or dsp_dl /*or tpatt_dl*/ or asi_dl)
begin
case (data_source_sel)
3'd0 : dl_n1 = phase ^ dl_pre;
3'd1 : dl_n1 = phase ^ cs8412_dl;
3'd2 : dl_n1 = phase ^ dsp_dl;
3'd4 : dl_n1 = phase ^ asi_dl;
3'd5 : dl_n1 = phase ^ asi_dl;
3'd6 : dl_n1 = phase ^ asi_dl;
default dl_n1 = 1'b0;
endcase
end
// Output
Multiplexer for DATA_RIGHT
always
@(data_source_sel or dor or cs8412_dr or dsp_dr or asi_dr)
begin
case (data_source_sel)
3'd0 : dr_n1 = phase ^ dr_pre;
3'd1 : dr_n1 = phase ^ cs8412_dr;
3'd2 : dr_n1 = phase ^ dsp_dr;
3'd4 : dr_n1 = phase ^ asi_dr;
3'd5 : dr_n1 = phase ^ asi_dr;
3'd6 : dr_n1 = phase ^ asi_dr;
default dr_n1 = 1'b0;
endcase
end
// Output
Multiplexer for WCKO
always
@(data_source_sel or wcko or cs8412_wck or dsp_wck or asi_wck)
begin
case (data_source_sel)
3'd0 : wc_n1 = wcko_pre;
3'd1 : wc_n1 = cs8412_wck;
3'd2 : wc_n1 = dsp_wck;
3'd4 : wc_n1 = asi_wck;
3'd5 : wc_n1 = asi_wck;
3'd6 : wc_n1 = asi_wck;
default wc_n1 = 1'b0;
endcase
end
// Output
Multiplexer for BCK_EN
always
@(data_source_sel or bcken or cs8412_bcken or dsp_bcken or asi_bcken)
begin
case (data_source_sel)
3'd0 : bc_n1 = bcken;
3'd1 : bc_n1 = cs8412_bcken;
3'd2 : bc_n1 = dsp_bcken;
3'd4 : bc_n1 = asi_bcken;
3'd5 : bc_n1 = asi_bcken;
3'd6 : bc_n1 = asi_bcken;
default bc_n1 = 1'b0;
endcase
end
// Output
Multiplexer for DG
always
@(data_source_sel or dg or cs8412_dg or dsp_dg or asi_dg)
begin
case (data_source_sel)
3'd0 : dg_n1 = dg_pre;
3'd1 : dg_n1 = cs8412_dg;
3'd2 : dg_n1 = dsp_dg;
3'd4 : dg_n1 = asi_dg;
3'd5 : dg_n1 = asi_dg;
3'd6 : dg_n1 = asi_dg;
default dg_n1 = 1'b0;
endcase
end
//
Re-Clocking at the Output of FPGA - dat_l, dat_r, bck_en, wcko_o, dg_o; (First Re-Clock Triggers )
// after
Output Data Multiplexer
always
@(posedge clk or negedge rst)
if (~rst)
dat_l <= 1'b0;
else
dat_l <= dl_n1;
always @(posedge
clk or negedge rst)
if (~rst)
dat_r <= 1'b0;
else
dat_r <= dr_n1;
always
@(posedge clk or negedge rst)
if (~rst)
wcko_o <= 1'b0;
else
wcko_o <= wc_n1;
always
@(posedge clk or negedge rst)
if (~rst)
bck_en <= 1'b0;
else
bck_en <= bc_n1;
always
@(posedge clk or negedge rst)
if (~rst)
dg_o <= 1'b0;
else
dg_o <= dg_n1;
endmodule
//
module
sync_out_former( clk, rst, vco_out, pd_osc, dig_sync_out,
clk1x, clk2x, clk32x,
clk64x, clk128x,
sync_out_sel );
input clk, rst, vco_out;
input clk1x, clk2x, clk32x, clk64x, clk128x;
input [2:0]
sync_out_sel;
output
pd_osc, dig_sync_out;
reg [1:0]
div3;
reg
dig_sync_out;
wire
state2;
assign
pd_osc = div3[0];
always
@(sync_out_sel or clk1x or clk2x or clk32x or clk64x or clk128x or clk or
vco_out)
begin
case (sync_out_sel )
3'd0 : dig_sync_out = 1'b0;
3'd1 : dig_sync_out = clk1x;
3'd2 : dig_sync_out = clk2x;
3'd3 : dig_sync_out = clk32x;
3'd4 : dig_sync_out = clk64x;
3'd5 : dig_sync_out = clk128x;
3'd6 : dig_sync_out = clk; // CLK = 256xFs
3'd7 : dig_sync_out = vco_out; // VCO_OUT = 384xFs
// default dig_sync_out = 1'b0;
endcase
end
////////
Divider by 3
assign
state2 = ~div3[0] & div3[1]; //
State2 = 1 when div3 = 2
always
@(posedge vco_out or negedge rst)
if (~rst)
div3 <= 2'b0;
else if (state2)
div3 <= 2'b0;
else
div3 <= div3 + 1;
endmodule
//
module
sys_logic ( data_in, data_out, address, wr, rst,
data_source_sel,
sync_out_sel, phase_rev, bcphase,
h_mute, s_mute, dither,
scal,
m3, sel, ms_sel, proc_ctrl,
dgi_rise, dgi_fall,
gp0, gp1, gp2, gp3, gp4,
gp5, gp6, gp7 );
input [7:0] data_in;
output
[7:0] data_out;
input [2:0] address;
input wr, rst;
output
[2:0] data_source_sel, sync_out_sel;
output
phase_rev, h_mute, s_mute, dither, scal,
m3, sel, ms_sel, proc_ctrl, bcphase,
gp0, gp1, gp2, gp3, gp4, gp5, gp6, gp7;
output
[4:0] dgi_rise, dgi_fall;
//////////
System Registers
reg [7:0]
sys_reg0, sys_reg1, sys_reg4;
reg [4:0]
sys_reg2, sys_reg3;
/////
Assign of Control signals from System Registers, (! First state is Default at
Reset !)
assign data_source_sel = sys_reg0[2:0], //
0:PMD-100, 1:CS8412(1x), 2:DSP(external), 3:TestPattern,
//
4:ASI/1x_External, 5:ASI/2x_External, 6:ASI/4x_External, 7:Reserved
phase_rev = sys_reg0[3], //
0:AsIs, 1:Phase_Reverse
s_mute = sys_reg0[4],
// 0:NO_Mute, 1:Soft_Mute_ON
h_mute = sys_reg0[5],
// 1:NO_Mute, 0:Hard_Mute_ON
dither = sys_reg0[6],
// 1:Dither_ON, 0:Dither_OFF
scal = sys_reg0[7],
// 1:Analog_Scaling, 0:Digital_Scaling
sync_out_sel = sys_reg1[2:0], //
Dig_Sync_Out_Select
//
0:Sync_Signal is disabled,
// 1:Fs, 2:2*Fs, 3:32*Fs, 4:64*Fs, 5:128*Fs, 6:256*Fs, 7:384*Fs
m3 = sys_reg1[3],
// CS8412 Mode: 0:Normal, 1:Special(NO_Repeat on Errors)
sel = sys_reg1[4],
// CS8412 Out_Code: 1:Channel_Status, 0:Error_Code_on_E0...E2
ms_sel = sys_reg1[5],
// 0:DAC is Master, 1:DAC is Slaver
proc_ctrl = sys_reg1[6], //
0:Processor Control, 1:Jumpers Control
bcphase =
sys_reg1[7], // Bit Clock Phase
of External Audio Serial Interface (ASI)
dgi_rise = sys_reg2[4:0], //
De-Glitch strobe, Rising Edge Position
dgi_fall = sys_reg3[4:0], //
De-Glitch strobe, Falling Edge Position
gp0 = sys_reg4[0], // General Purpose Outputs
gp1 = sys_reg4[1],
gp2 = sys_reg4[2],
gp3 = sys_reg4[3],
gp4 = sys_reg4[4],
gp5 = sys_reg4[5],
gp6 = sys_reg4[6],
gp7 = sys_reg4[7];
//////////
Data Read Multiplexer
reg [7:0]
data_out;
always
@(sys_reg0 or sys_reg1 or sys_reg2 or sys_reg3 or sys_reg4 or address)
begin
case (address)
4'd0 : data_out = sys_reg0;
4'd1 : data_out = sys_reg1;
4'd2 : data_out = {3'b0, sys_reg2};
4'd3 : data_out = {3'b0, sys_reg3};
4'd4 : data_out = sys_reg4;
default : data_out = 8'b0;
endcase
end
//////////
Address Decoder
reg [4:0]
fn;
wire fn0,
fn1, fn2, fn3, fn4;
assign fn0 = fn[0],
fn1 = fn[1],
fn2 = fn[2],
fn3 = fn[3],
fn4 = fn[4];
always
@(address)
begin
case (address)
4'd0 : fn = 5'b00001;
4'd1 : fn = 5'b00010;
4'd2 : fn = 5'b00100;
4'd3 : fn = 5'b01000;
4'd4 : fn = 5'b10000;
default fn = 5'b0;
endcase
end
//////////
Data Write to each System Register
always @(
posedge wr or negedge rst )
if (~rst)
sys_reg0 <= 8'b11100000;
else if (fn0)
sys_reg0 <= data_in;
always @(
posedge wr or negedge rst )
if (~rst)
sys_reg1 <= 8'b00010000;
else if (fn1)
sys_reg1 <= data_in;
always @(
posedge wr or negedge rst )
if (~rst)
sys_reg2 <= 5'b0;
else if (fn2)
sys_reg2 <= data_in[4:0];
always @(
posedge wr or negedge rst )
if (~rst)
sys_reg3 <= 5'b0;
else if (fn3)
sys_reg3 <= data_in[4:0];
always @(
posedge wr or negedge rst )
if (~rst)
sys_reg4 <= 8'b0;
else if (fn4)
sys_reg4 <= data_in;
endmodule
Литература
и ссылки
[1] http://www.hdcd.com
[2] http://www.mp3projects.com/digital/chimp/bins/i2s.pdf
[3] http://www.npcproducts.com/
[7] http://www.phyast.pitt.edu/~charng/schematic.html
[8] http://www.diyaudio.de/index.html
[9] http://members.brabant.chello.nl/~m.heijligers/DAChtml/dactop.htm
[10] http://www.geocities.com/ResearchTriangle/8231/cdplayer/index.html
[11] http://galstar.com/~ntracy/ACG/Xdac/ADreply/harley1.htm
[12] http://www.jitter.de/english/engc_navfr.html
[13] http://www.sound.au.com/misclink.htm
[14] http://www.megabaud.fi/~jtolonen/projects/jt-dac_no.3/jt-dac_no.3.html
[15] http://www.clarkson.edu/~stokessd/dac.html
[16] http://www.zeuslab.newmail.ru/
[17] http://users.cybercity.dk/~ida2439/athome/builddac.htm
[18] http://www.tnt-audio.com/clinica/tweaks.html