I2S interfacing with the Prop2
                    I've been looking at using Smartpins to generate I2S bus signals (initially for a DAC using synchronous serial tx).
My sample code is below, but I suspect there is more than one way to skin this particular cat (so to speak) with all the features
in the P2.
I've assumed that the Prop will be the bus master (so generating all the clocks), which I think is reasonable for most scenarios.
Things I wonder about include:
How best to handle an audio stream on the P2?
Can the streamers talk directly to synchronous serial smart pins?
Would FIFO reads and writes be best?
How to architect a system where an ADC will go through one or more cogs for DSP operations like digital filtering,
volume control, monitoring, and then out to a DAC again?
Is support for multiple ADCs or multiple DACs useful?
5.1 signal processor feasible?
How well would using the Prop2 ADC inputs work for quality audio (for feeding to an I2S DAC perhaps) - good enough
to implement a THD meter?
Can the Prop2 clock PLL scale to 196.608MHz? Would it be stable?
On the P1 things are sufficiently constrained that there are fewer choices/possibilities available.
I presume that synchronous serial RX will work well for ADCs, but so far I've only been looking at the 'scope screen
and getting outputs working. One nice thing is that its trivial to change the speed, so long as CLKS_PER_BIT is
a multiple of 8, since the bit timing is all handled in hardware. I think I managed upto 8x 39.0625kHz sample rate.
I've used 6.144MHz/12.288MHz xtals with Prop1 to run at 98.304MHz (power of two times 48kHz, the commonest I2S rate), but
I wonder if that's really necessary given the more flexible PPL.
[ incidentally I discovered the first bit shifted out in synchronous serial mode doesn't seem to last for the full duration, so the timing
is thrown by about one bit time. After its started it all runs in lockstep with WAITCT1 nicely though ]
                            My sample code is below, but I suspect there is more than one way to skin this particular cat (so to speak) with all the features
in the P2.
I've assumed that the Prop will be the bus master (so generating all the clocks), which I think is reasonable for most scenarios.
Things I wonder about include:
How best to handle an audio stream on the P2?
Can the streamers talk directly to synchronous serial smart pins?
Would FIFO reads and writes be best?
How to architect a system where an ADC will go through one or more cogs for DSP operations like digital filtering,
volume control, monitoring, and then out to a DAC again?
Is support for multiple ADCs or multiple DACs useful?
5.1 signal processor feasible?
How well would using the Prop2 ADC inputs work for quality audio (for feeding to an I2S DAC perhaps) - good enough
to implement a THD meter?
Can the Prop2 clock PLL scale to 196.608MHz? Would it be stable?
On the P1 things are sufficiently constrained that there are fewer choices/possibilities available.
' Smartpin I2S DAC driver  proof of concept, 3 smartpins for MCLK, BCLK, DOUT, plus LRCLK bit-banged
' Mark Tillotson  2019-01-12
CON
    OSCMODE = $010c3f04
    FREQ = 160_000_000
    NCO_FREQ =%0000_0000_000_00000_00000000_01_00110_0
    SYNC_TX = %0000_1001_000_00000_00000000_01_11100_0  ' sync serial tx using pin+1 as clk
    DOUT_PIN_NUM  = 48
    CLOCK_PIN_NUM = DOUT_PIN_NUM + 1
    MCLK_PIN_NUM  = 50
    LRCLK_PIN_NUM = 51
    ' must be a multiple of 8 for MCLK = 4*BCLK
    CLKS_PER_BIT = 64  ' for 39.0625kHz samplerate (1.5MHz bitclk) (would be 48kHz for sysclk = 12.288MHz x 16)
    ' 40 will give 62.5kSPS at 160MHz sysclk
PUB demo | i, a
    clkset(OSCMODE, FREQ)
    pausems (1000)    ' time for 'scope capture setup
    cognew (@tx_output)
    repeat
        pausems (1)
DAT
                org     0
tx_output
                wrpin   ##NCO_FREQ, #CLOCK_PIN_NUM
                wxpin   ##CLKS_PER_BIT/2, #CLOCK_PIN_NUM  ' every 64 cycles (32 instructions), bit clock
                wypin   ##$80000000, #CLOCK_PIN_NUM       ' toggle every other
                wrpin   ##NCO_FREQ, #MCLK_PIN_NUM
                wxpin   ##CLKS_PER_BIT/8, #MCLK_PIN_NUM   ' every 16 cycles (8 instructions), master clock
                wypin   ##$80000000, #MCLK_PIN_NUM        ' toggle every other
                dirh    ##CLOCK_PIN_NUM 
                dirh    ##MCLK_PIN_NUM    ' start clocks together
                mov     left, ##$80FFF000 ' test pat with bot 8 bits zero
                mov     t, left
                rev     t
                shl     t, #1
                dirh    lrclk_pin
        
                wrpin   ##SYNC_TX, dout_pin
                wxpin   ##%011111, dout_pin
                wypin   t, dout_pin
                GETCT   time
                dirh    dout_pin
                ADDCT1  time, idelay
                outl    lrclk_pin
                jmp     #.skipy       ' compensate for truncated first bit shifted by smartpin sync serial mode
                
.loop           ' left channel
                outl    lrclk_pin     ' drive left/right clock
.skipy
                WAITCT1
                ADDCT1 time, delay
                sub     right, #$37   ' testing pattern, 31 bits used, decrementing
                mov     t, right      ' preload right
                rev     t
                shl     t, #1         ' I2S justification, top bit is delayed 1 place
                wypin   t, dout_pin
                WAITCT1
                ADDCT1  time, delay
                
                ' right channel
                outh    lrclk_pin     ' drive left/right clock
                WAITCT1
                ADDCT1 time, delay
                
                add     left, #$100   ' testing pattern, strictly 24 bits
                mov     t, left       ' preload left
                rev     t
                shl     t, #1
                wypin   t, dout_pin
                WAITCT1
                ADDCT1  time, delay
                
                jmp     #.loop
left            long    0
right           long    0
lrclk_pin       long    LRCLK_PIN_NUM
dout_pin        long    DOUT_PIN_NUM
delay           long    16 * CLKS_PER_BIT
idelay          long    15 * CLKS_PER_BIT
time            res     1
t               res     1
I presume that synchronous serial RX will work well for ADCs, but so far I've only been looking at the 'scope screen
and getting outputs working. One nice thing is that its trivial to change the speed, so long as CLKS_PER_BIT is
a multiple of 8, since the bit timing is all handled in hardware. I think I managed upto 8x 39.0625kHz sample rate.
I've used 6.144MHz/12.288MHz xtals with Prop1 to run at 98.304MHz (power of two times 48kHz, the commonest I2S rate), but
I wonder if that's really necessary given the more flexible PPL.
[ incidentally I discovered the first bit shifted out in synchronous serial mode doesn't seem to last for the full duration, so the timing
is thrown by about one bit time. After its started it all runs in lockstep with WAITCT1 nicely though ]

                            
Comments
Nice to see interest in this topic!
Some months ago I bought a 6 channel analog amplifier, 6 speakers, 3 I2S modules (two channels per module) and a spdif-to-6-analog-out module (6 ch in DTS/DD mode) for doing exactly these kind of things on the P2. The plan is to have a cogdriver that can take multiple audio input streams, mix them together (pan, gain, sample rate conversion, antialaising) and output to smart pins, I2S or spdif with up to 6 out channels (think 5.1 setup). In spdif mode it is only realistic to have 2 out channels though, since realtime DD/DTS encoding is CPU heavy and complex. Having a pure sound driver/mixer in one cog and doing audio input and DSP in others is the best way to go I think. Then there needs to be some kind of hub mechanism to synchronize data producing cogs with the sounddriver/mixer cog.
not yet tested on a live DAC.
' Smartpin I2S DAC driver proof of concept, 3 smartpins for MCLK, BCLK, DOUT, plus LRCLK bit-banged ' CORDIC quadrature output cosine/sine to left/right as a test waveform ' Mark Tillotson 2019-01-12 CON OSCMODE = $010c3f04 FREQ = 160_000_000 NCO_FREQ =%0000_0000_000_00000_00000000_01_00110_0 SYNC_TX = %0000_1001_000_00000_00000000_01_11100_0 ' sync serial tx using pin+1 as clk DOUT_PIN_NUM = 48 CLOCK_PIN_NUM = DOUT_PIN_NUM + 1 MCLK_PIN_NUM = 50 LRCLK_PIN_NUM = 51 ' must be a multiple of 8 for MCLK = 4*BCLK CLKS_PER_BIT = 64 ' for 39.0625kHz samplerate (1.5MHz bitclk) (would be 48kHz for sysclk = 12.288MHz x 16) ' 40 will give 62.5kSPS at 160MHz sysclk NOMINAL_SAMPLE_RATE_MILLIHERTZ = 39_062_500 ' 48_000_000 if suitable sysclk TEST_FREQUENCY_MILLIHERTZ = 1_000_000 ' 1kHz PUB demo | i, a clkset(OSCMODE, FREQ) pausems (1000) ' time for 'scope capture setup cognew (@tx_output) repeat pausems (1) DAT org 0 tx_output ' compute DDS frequency QFRAC ##TEST_FREQUENCY_MILLIHERTZ, ##NOMINAL_SAMPLE_RATE_MILLIHERTZ WAITX #55 GETQX freq_reg wrpin ##NCO_FREQ, #CLOCK_PIN_NUM wxpin ##CLKS_PER_BIT/2, #CLOCK_PIN_NUM ' every 64 cycles (32 instructions), bit clock wypin ##$80000000, #CLOCK_PIN_NUM ' toggle every other wrpin ##NCO_FREQ, #MCLK_PIN_NUM wxpin ##CLKS_PER_BIT/8, #MCLK_PIN_NUM ' every 16 cycles (8 instructions), master clock wypin ##$80000000, #MCLK_PIN_NUM ' toggle every other dirh ##CLOCK_PIN_NUM dirh ##MCLK_PIN_NUM ' start clocks together add phase, freq_reg ' DDS QROTATE amplitude, phase ' to (cosine, sine) WAITX #55 GETQX left and left, ##-256 mov t, left rev t shl t, #1 dirh lrclk_pin wrpin ##SYNC_TX, dout_pin wxpin ##%011111, dout_pin wypin t, dout_pin GETCT time dirh dout_pin ADDCT1 time, idelay .loop ' left channel outl lrclk_pin ' drive left/right clock WAITCT1 ADDCT1 time, delay GETQY right and right, ##-256 mov t, right ' preload right rev t shl t, #1 ' I2S justification, top bit is delayed 1 place wypin t, dout_pin add phase, freq_reg QROTATE amplitude, phase WAITCT1 ADDCT1 time, delay ' right channel outh lrclk_pin ' drive left/right clock WAITCT1 ADDCT1 time, delay GETQX left and left, ##-256 mov t, left ' preload left rev t shl t, #1 wypin t, dout_pin WAITCT1 ADDCT1 time, delay jmp #.loop left long 0 right long 0 amplitude long $3FFFFFFF ' -6dB level phase long 0 lrclk_pin long LRCLK_PIN_NUM dout_pin long DOUT_PIN_NUM delay long 16 * CLKS_PER_BIT idelay long 15 * CLKS_PER_BIT freq_reg res 1 time res 1 t res 1Think it might be time to spin up a 64000 extension/accessory board with I2S ADC + DAC