Skip to content

Commit

Permalink
Added multi-pin TDM support.
Browse files Browse the repository at this point in the history
This allows you use use more than one TX or RX pin in TDM mode,
allowing for up to 64 channels of record or playback data

This fix goes hand-in-hand with the multi-channel USB support.
PaulStoffregen/cores#732

input_tdm: every odd channel had every other sample swapped
In every odd channel in TDM input (1, 3, 5, 7, 9, 11, 13, 15), every
other word was swapped due to an incorrect copy from 32-bits to
16-bits. This fix corrects the odd channels.   The Shift-by zeros  and the
extraneous logical ands are there for clarity, and I verified they
don't end up affecting final code optimization as long as optimization
is turned on.
  • Loading branch information
ccrome committed Jan 18, 2024
1 parent f377760 commit 28d3ab4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 42 deletions.
109 changes: 74 additions & 35 deletions input_tdm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@
#include "utility/imxrt_hw.h"

DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
audio_block_t * AudioInputTDM::block_incoming[16] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*TDM_IN_N_AUDIO_BLOCKS];
audio_block_t * AudioInputTDM::block_incoming[TDM_IN_N_AUDIO_BLOCKS];
bool AudioInputTDM::update_responsibility = false;
DMAChannel AudioInputTDM::dma(false);


void AudioInputTDM::begin(void)
{
for (int i = 0; i < TDM_IN_N_AUDIO_BLOCKS; i++)
block_incoming[i] = nullptr;
dma.begin(true); // Allocate the DMA channel first

// TODO: should we set & clear the I2S_RCSR_SR bit here?
Expand All @@ -68,7 +66,22 @@ void AudioInputTDM::begin(void)
dma.attachInterrupt(isr);
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // TEENSY P8, GPIO_B1_00_ALT3 as rx 0
// When using combine mode, you *must* use the data pins in order.
switch (AUDIO_N_SAI1_RX_DATAPINS) {
case 4:
CORE_PIN32_CONFIG = 3; //RX_DATA3
IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // TEENSY P32, GPIO_B0_12_ALT3 as rx 1
case 3:
CORE_PIN9_CONFIG = 3; //RX_DATA2
IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // TEENSY P9, GPIO_B0_11_ALT3 as rx 2
case 2:
CORE_PIN6_CONFIG = 3; //RX_DATA1
IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // TEENSY P6, GPIO_B0_10_ALT3 as rx 1
break;
default:
break;
}
dma.TCD->SADDR = &I2S1_RDR0;
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
Expand All @@ -89,33 +102,65 @@ void AudioInputTDM::begin(void)
#endif
}

// TODO: needs optimization...
static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
{
uint32_t i, in1, in2;

for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
in1 = *src;
in2 = *(src+8);
src += 16;
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
}
// Since we're grabbing data out of the fifo at 32-bits, but the words
// are 16-bits and we're sharing fifos in a round-robbin manner, with
//
// 1 FIFO (pin) active, the word/channel order for the first frame is
// C00 C02 C04 C06 C08 C10 C12 C14
// C01 C03 C05 C07 C09 C11 C13 C15
//
// 2 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C02 C18 C04 C20 C06 C22 C08 C24 C10 C26 C12 C28 C14 C30
// C01 C17 C03 C19 C05 C21 C07 C23 C09 C25 C11 C27 C13 C29 C15 C31
//
// 3 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C32 C02 C18 C34 C04 C20 C36 C06 C22 C38 C08 C24 C40 C10 C26 C42 C12 C28 C44 C14 C30 C46
// C01 C17 C33 C03 C19 C35 C05 C21 C37 C07 C23 C39 C09 C25 C41 C11 C27 C43 C13 C29 C45 C15 C31 C47
//
// 4 FIFO (pin) active, the word/channel order for the first frame is
// C00 C16 C32 C48|C02 C18 C34 C50|C04 C20 C36 C52|C06 C22 C38 C54|C08 C24 C40 C56|C10 C26 C42 C58|C12 C28 C44 C60|C14 C30 C46 C62
// C01 C17 C33 C49|C03 C19 C35 C51|C05 C21 C37 C53|C07 C23 C39 C55|C09 C25 C41 C57|C11 C27 C43 C59|C13 C29 C45 C61|C15 C31 C47 C63
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <--- word index
// where each column is a single 32-bit word popped from the fifo
static void deinterleave(audio_block_t *block_incoming[TDM_IN_N_AUDIO_BLOCKS], const uint32_t *src) {
int l = 0;
for (int i=0; i < TDM_IN_N_AUDIO_BLOCKS/2; i++) {
// ix = 0, 2, 4, 6, ... 1 pin
// ix = 0, 16, 2, 18, 3, 20, ... 2 pins
// ix = 0, 16, 32, 2, 18, 33 ... 3 pins
// ix = 0, 16, 32, 48, 2, 18 ... 4 pins

// i = 0, 1, 2, 3, 4, 5,
// ix = 0, 16, 32, 2, 18, 33 ... 3 pins
// = (0*16 1*16 2*16)+i
int ix = (l*16) + i/AUDIO_N_SAI1_RX_DATAPINS*2;
l = (l + 1) % AUDIO_N_SAI1_RX_DATAPINS;
uint32_t *dest1 = (uint32_t *)(block_incoming[ix]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[ix+1]->data);
const uint32_t *psrc = src;
for (int j=0; j < AUDIO_BLOCK_SAMPLES/2; j++) {
uint32_t in1 = *psrc;
uint32_t in2 = *(psrc + TDM_IN_N_AUDIO_BLOCKS * AUDIO_N_SAI1_RX_DATAPINS / 2);
psrc += TDM_IN_N_AUDIO_BLOCKS;
*dest1++ = ((in1 >> 16) & 0x0000FFFF) | ((in2 << 0) & 0xFFFF0000);
*dest2++ = ((in1 >> 0) & 0x0000FFFF) | ((in2 << 16) & 0xFFFF0000);
}
src++;
}
}

void AudioInputTDM::isr(void)
{
uint32_t daddr;
const uint32_t *src;
unsigned int i;

daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();

if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*8];
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES * TDM_IN_N_AUDIO_BLOCKS / 2];
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
Expand All @@ -125,25 +170,19 @@ void AudioInputTDM::isr(void)
#if IMXRT_CACHE_ENABLED >=1
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
#endif
for (i=0; i < 16; i += 2) {
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
memcpy_tdm_rx(dest1, dest2, src);
src++;
}
deinterleave(block_incoming, src);
}
if (update_responsibility) update_all();
}


void AudioInputTDM::update(void)
{
unsigned int i, j;
audio_block_t *new_block[16];
audio_block_t *out_block[16];

// allocate 16 new blocks. If any fails, allocate none
for (i=0; i < 16; i++) {
unsigned int i, j;
audio_block_t *new_block[TDM_IN_N_AUDIO_BLOCKS];
audio_block_t *out_block[TDM_IN_N_AUDIO_BLOCKS];
// allocate TDM_IN_N_AUDIO_BLOCKS new blocks. If any fails, allocate none
for (i=0; i < TDM_IN_N_AUDIO_BLOCKS; i++) {
new_block[i] = allocate();
if (new_block[i] == nullptr) {
for (j=0; j < i; j++) {
Expand All @@ -158,8 +197,8 @@ void AudioInputTDM::update(void)
memcpy(block_incoming, new_block, sizeof(block_incoming));
__enable_irq();
if (out_block[0] != nullptr) {
// if we got 1 block, all 16 are filled
for (i=0; i < 16; i++) {
// if we got 1 block, all TDM_IN_N_AUDIO_BLOCKS are filled
for (i=0; i < TDM_IN_N_AUDIO_BLOCKS; i++) {
transmit(out_block[i], i);
release(out_block[i]);
}
Expand Down
5 changes: 4 additions & 1 deletion input_tdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
#include <Arduino.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/Arduino.h
#include <AudioStream.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h
#include <DMAChannel.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/DMAChannel.h
#include "AudioRate.h"

#define TDM_IN_N_AUDIO_BLOCKS (TDM_CHANNELS_PER_PIN*AUDIO_N_SAI1_RX_DATAPINS)

class AudioInputTDM : public AudioStream
{
Expand All @@ -42,7 +45,7 @@ class AudioInputTDM : public AudioStream
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_incoming[16];
static audio_block_t *block_incoming[TDM_IN_N_AUDIO_BLOCKS];
};

#endif
4 changes: 2 additions & 2 deletions input_tdm2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
in1 = *src;
in2 = *(src+8);
src += 16;
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
*dest1++ = ((in1 >> 16) & 0x0000FFFF) | ((in2 << 0) & 0xFFFF0000);
*dest2++ = ((in1 >> 0) & 0x0000FFFF) | ((in2 << 16) & 0xFFFF0000);
}
}

Expand Down
17 changes: 13 additions & 4 deletions output_tdm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include <Arduino.h>

#if !defined(KINETISL)
Expand Down Expand Up @@ -319,20 +318,30 @@ void AudioOutputTDM::config_tdm(void)
I2S1_TCR1 = I2S_TCR1_RFW(4);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_TCR3 = I2S_TCR3_TCE;

// enable all TX data pins. Channels must be used in order, so
// enabled chanels can (0b0001, 0b0011, 0b0111, or 0b1111) only. This
// limitation is because of the fifo combine mode limitation.
I2S1_TCR3 = ((1 << AUDIO_N_SAI1_TX_DATAPINS)-1) << 16;

I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSD;
I2S1_TCR4 |= (AUDIO_N_SAI1_TX_DATAPINS > 1) ? I2S_TCR4_FCOMB_ENABLED_ON_WRITES : 0;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(4);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(0);
I2S1_RCR3 = I2S_RCR3_RCE;
// enable all RX data pins. Channels must be used in order, so
// enabled chanels can (0b0001, 0b0011, 0b0111, or 0b1111) only. This
// limitation is because of the fifo combine mode limitation.
I2S1_RCR3 = ( (1 << AUDIO_N_SAI1_RX_DATAPINS) - 1 ) << 16;

I2S1_RCR4 = I2S_RCR4_FRSZ(7) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S1_RCR4 |= (AUDIO_N_SAI1_RX_DATAPINS > 1) ? I2S_RCR4_FCOMB_ENABLED_ON_READS : 0;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
Expand Down
1 change: 1 addition & 0 deletions output_tdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <Arduino.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/Arduino.h
#include <AudioStream.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h
#include <DMAChannel.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/DMAChannel.h
#include "AudioRate.h"

class AudioOutputTDM : public AudioStream
{
Expand Down

0 comments on commit 28d3ab4

Please sign in to comment.