Multiple SPDIF receiver to I2S

Sub forums for various specialist XMOS applications. e.g. USB audio, motor control and robotics.
Post Reply
User avatar
fabriceo
XCore Addict
Posts: 181
Joined: Mon Jan 08, 2018 4:14 pm

Multiple SPDIF receiver to I2S

Post by fabriceo »

Hi Guys,
Can someone help me to finalize my HW design by confirming feasibility of the following project ? also a first guidance on the application would be appreciated, just as a confirmation of feasibility and to sense the level of software headach to come :)

I m in the process of designing a multichannel dac board integrating XUF208 and I d like to manage 4 SPDIF receiver, as typically coming out of a player like OPPO-203 with Vanity 203 HD board. In this context the 4 SPDIF signals are synchronized together and generated from a single 44/48k or 88/96 master clock in the player.

The design includes a fractional PLL for generating a proper I2S synch clock, and the xmos will be configured as a single I2S slave channel in TDM8 mode.
the bit clock (11/12/22/24mhz) and the master clock (22/24mhz) are generated from the PLL. The TDM frame clock is generated by a logic divider.

here is a high level diagram of the configuration,
HLD multi spdif.png
(41.27 KiB) Not downloaded yet
HLD multi spdif.png
(41.27 KiB) Not downloaded yet
and the foreseen 1bit port mapping.
XUF208 pin map.png
(76.15 KiB) Not downloaded yet
XUF208 pin map.png
(76.15 KiB) Not downloaded yet
I have possibly a spare port (QSP_CLK / P1C0) that coud generate a reference clock for the PLL but I think it is not needed as the ClockGen.xc can calculate the value for the fractional PLL and send them over I2C right ?

Also I understood from other posts that the MCLOCK is not needed for the standard I2S_lib if we do not use XUD/USB right ?

I ve read many xmos application note and source code, including the famous AN00231_ASRC_SPDIF_TO_DAC. this all is inspiring but quite complex.

Many thanks for supporting our creativity!


User avatar
Caleb
Experienced Member
Posts: 82
Joined: Thu Apr 04, 2013 10:14 pm
Contact:

Post by Caleb »

You didn't say what the PLL is so it's not clear what it requires. But I don't think you want to try to design something that dynamically adjusts the frequency of a clock synthesizer (jitter).

Understanding clockgen: It is used to generate the frequency reference for a CS2100 clock multiplier. It counts the number of samples it receives from the spdif rx thread and toggles an I/O pin every time it receives the number of samples that should be expected for 600 Hz toggle rate / 300Hz reference clock frequency. It knows how many samples to expect because it measures the rate of samples received. The CS2100's multiply ratio must then be changed to multiply by 81920 for 48kHz/96/192 or 75264 for 44.1,etc. The pin that is toggled for the frequency reference can be any I/O pin -and no need to "waste" a 1-bit clock synchronize-able pin. Various designs have used a pin of a 4-bit, port. You can change the way clockgen works to get better low frequency jitter performance from the CS2100. I never understood why 300Hz was used except that it's clever to find the largest integer common divisor of 44.1 and 48kHz. You could alternatively make the reference frequency 44.1 or 48kHz and leave the multiply ratio always set to 8. Anyway, there is a mechanism in clockgen - a fallback mode - that will use a timer to continue to make the 300Hz reference if the spdif rx has no signal.

I've used clockgen to gather samples from multiple spdif receivers - just using one of them used as the source for the frequency reference generation.
User avatar
fabriceo
XCore Addict
Posts: 181
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Thank you for your answer and guidance !
So basically the Spdif_Rx and clockgen together determine the incoming spdif rate, and then the precise mclk (22/24mhz) is generated by the CS2100 just from the pin toggling around 300hz... okay.

is there a possibility to avoid this 300hz pin and instead write in the CS2100 register over I2C time to time...
probably yes by defining an interface or a Chanel to push the rate from clockgen and then a task combined with the i2c library to update the pll.

any help or code guidance would be appreciated ! thanks in advance
User avatar
Caleb
Experienced Member
Posts: 82
Joined: Thu Apr 04, 2013 10:14 pm
Contact:

Post by Caleb »

fabriceo wrote:Thank you for your answer and guidance !
So basically the Spdif_Rx and clockgen together determine the incoming spdif rate, and then the precise mclk (22/24mhz) is generated by the CS2100 just from the pin toggling around 300hz... okay.

is there a possibility to avoid this 300hz pin and instead write in the CS2100 register over I2C time to time...
probably yes by defining an interface or a Chanel to push the rate from clockgen and then a task combined with the i2c library to update the pll.
No, the incoming S/PDIF rate is fixed by the source of that signal. The mclk from CS2100 must be precisely synchronized to that rate. The CS2100 is not a clock synthesizer; it is a multiplier. It requires a frequency reference and then you configure its multiply factor / ratio via its serial control port (I2C or SPI). Each time you reconfigure the CS1200's control registers, its clock output stops for several milliseconds.

I don't know why the programmers of this software reference project chose to use 300Hz for all cases. We found that this results in elevated low-frequency jitter. We prefer using a reference that is synchronous with the sample rate. Then the jitter in that reference signal is better attenuated by the CS2100. For this same reason, If you were to attempt to synchronize a clock synthesizer by periodically changing its output frequency, I think this would result in large jitter amplitude. But I guess that depends on the latency of making the changes and the magnitude of the smallest increment of change that is possible. I don't think such a system is possible with CS1200 (since its output is interrupted after each reconfiguration).

I am familiar with a design that uses VCXO (voltage controlled crystal oscillators). These have an adjustment range of perhaps 150PPM. The control voltage is set by a DAC, which is updated periodically by a microcontroller that is constantly monitoring the phase relationship between the reference signal (framing of the incoming signal) and a sample clock that is divided from the VCXO's output. That basically describes a software loop filter for the VCXO-base PLL.

I've considered a similar experiment - except that the VCXO control signal could be generated by high-resolution PWM that is calculated in XMOS and output on a 32-bit buffered output pin, The duty cycle of the PWM would be adjusted to maintain phase-lock between a divided mclk (VCXO output) and the sample frequency reference (rate at which samples emerge from the spdif_rx software module. A basic analog filter of the PWM ouput would be a component of the loop bandwidth but the primary loop bandwidth characteristics are from software.

Whatever choice is made for a PLL structure, it's important to be aware that there is significant jitter in the rate at which samples are generated by the spdif_rx software module.
User avatar
fabriceo
XCore Addict
Posts: 181
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Thank you Caleb for your detailed answer and some of your thoughts or experience with different way of PLL-ing :)

I ve now spent many hours on going trough the clockgen.c and usb-audio-app and I think I have a better understanding.
May I explain here the exact concern or challenge, which as to do with spdif multichanel:

My Goal is/was to create a XMOS board (based xuf216) to interface a multichanel DAC board (like AK4458 or ES9018) with both USB and a Multichanel SPDIF flow, e.g. coming from a BluRAY reader with 4 coax for the 7.1 channels, like the audio praise vanity board, or any other use case from a professional AES multi-source.

So a first problem is to launch SpdifReceiver(c) on 4 logical core. fine but not clear if there are speed constrains here, I understand the task runs at 12.5mhz and requires 60ns and potentially 100ns at the end. so what is the recommended maximum number of core for properly handing this speed? Also is there any risk of pipeline overload due to the 3 additional stream channels?

a second problem is to have a PLL somewhere (like the CS2100 multiplier, driven by the 300hz clokcgen.c) as a purpose of the previous discussion, or I thought I could have one cheap CS8416 to immediately provide the master clock of the SPDIF, assuming the 4 channels are synch of course.

a third problem is to read 4 chanend inside the clockgen.c, populate the famous fifo g_ buffer to send that back to the Audio task. This requires I d say a major refactoring, that can be inspired by the ADAT_RX section which deals with up to 8 channels. But I also need to send that to the mixer so flow also go out of the I2S_Channel_dac, right ??

a fourth problem is to manage the spdif clock change, which is actually handled by an USB interrupt initiated by clockgen.c and subject to some issue, especially with thesicon driver or when there is no host (standalone mode) as discussed recently here: https://www.xcore.com/viewtopic.php?p=34540#p34540

so at this time, regarding the complexity of this effort I m skeptical on the feasibility with my coding skills on xcore, to say the least :)

anyone here could confirm the approach and problems above and maybe guide on a way forward to make this possible, preferably as open-source?
User avatar
fabriceo
XCore Addict
Posts: 181
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi
as part of this quest, I may have found a way to run 3 (but 4 shall be possible) SpdifReceive from 3/4 cores (on same tile as Audio_IO to avoid links congestion between the 2 tiles).
Due to the parallel usage of the clock block... one trick is to duplicate the 2 files SpdifReceive.h and SpdifReceive.s in a second version SpdifReceive2.h and .s and to edit this one for replacing all SpdifReceive by appending 2. You also have to quote all lines accessing R3 which reference the clock block adress parameter, except the first line which connects your p_spdif_rx2/3 port to the clock block of course.
to avoid the "parallel usage" error, I ve used a (int) type cast instead of the type "clock" and put unsafe statement before each task declaration.

then in the main.xc, I m able to launch SpdifReceive and then 2 instance of SpdifReceive2 with their respective port and streaming chanels.

so far, using a single tx and shorting all 3 inputs, I can use any of the 3 c_spdif_rx1/2/3 chanel and it proves to work, which means the spdifreceive task do their homework, and the clockgen is still happy with the standard SpdifReceive and original chanel.

Next step is to modify clockgen so that the extra channels can be transferred to the audio task... hopefully these are streamed chanel so I should not need to synch them all, let see.
still some work, but progressing.
Post Reply