Forums

21065L SHARC SPORT in multichannel mode w/ DMA chaining

Started by Alex Parkinson December 10, 2003
Hi everyone,

    I'm an EE student at the University of Florida trying to finish up my
senior project, an audio DSP dev. board based on the 21065L SHARC interfaced
with an AD1838A 24-bit, 96-kHz codec.  I have used the application notes on
ADI's website, describing interfacing the 21161N to the AD1836 and the
21065L to the AD1819, as a reference for both hardware and software. I'm
currently stuck, as I can't seem to get the SHARC to communicate with the
codec properly. I would appreciate any help or suggestions if anyone has any
experience with this. Please see a description of the problem and code
below. Sorry in advance for the long post.

Sincerely,
Alex Parkinson
Senior, Computer and Electrical Engineering
University of Florida
ahparky@ufl.edu


The Situation:
    The codec has 1 stereo input and 3 stereo outputs, interfaced as master
to the DSP in TDM mode. So the codec generates the FSTDM signal once every
20.8us (48 kHz sampling rate) and the bit clock frequency is 12.288 MHz (32
bits/word * 8 channels * fs). The codec sends the left and right ADC data on
channels 0 and 4. It receives the DAC data in the following order:
                DAC_L1, DAC_L2, DAC_L3, empty, DAC_R1, DAC_R2, DAC_R3, empty
I've written a program which simply takes the left and right input signals
and sends them out on all of the outputs. However, the only output which
works is DAC_L3. I've spent the last several days troubleshooting this
problem with an oscilloscope (I don't have the money for an emulator), and I
have a pretty good understanding of what's going wrong - I just don't know
why it's going wrong.

The Problem:
    The DSP DMA transfer begins one bit clock after the assertion of FSTDM
(MFD = 1), at which time it sends out the three left channels plus an empty
channel. Then, for some reason, the DSDATA line tri-states for the remainder
of the frame (four more channels). After that, another FSTDM is generated by
the codec, and the SHARC then sends the three right channels plus an empty
channel, followed by four tri-stated channels. So the left channel data
received by the codec is actually alternating between left and right channel
data. I suspect that the same is happening with the input. That is, the
processor is only reading the first channel (ADC_L1) of every frame, and is
considering the data to be left and right channels. For this reason, the
DAC_Lx outputs should all be sending out the correct data (but for the wrong
reasons).
    However, it's only the third channel which is outputting correctly (this
may be a problem with the codec - I'm going to check more into that
tomorrow). That still doesn't change the fact that the processor is not
communicating with the codec correctly. I should also mention that I am able
to succesfully communicate with the control registers fo the codec using the
other SPORT, but that does not use multichannel mode or DMA chaining.

The Code:

#include <def21065L.h>

/*************************************************
* AD1838A Definitions
*************************************************/
#define  READ_REG  0x0800
#define  WRITE_REG  0x0000

#define  DAC_CTRL1  0x0000
#define  DAC_CTRL2  0x1000
#define  DAC_VOL1  0x2000
#define  DAC_VOL2  0x3000
#define  DAC_VOL3  0x4000
#define  DAC_VOL4  0x5000
#define  DAC_VOL5  0x6000
#define  DAC_VOL6  0x7000
#define  RES_REG1  0x8000
#define  RES_REG2  0x9000
#define  ADC_PEAK0  0xA000
#define  ADC_PEAK1  0xB000
#define  ADC_CTRL1  0xC000
#define  ADC_CTRL2  0xD000
#define  ADC_CTRL3  0xE000
#define  RES_REG3  0xF000

#define  Int_ADC_L1  0
#define  Aux_ADC_L2  1
#define  Aux_ADC_L3  2
#define  Aux_ADC_L4  3
#define  Int_ADC_R1  4
#define  Aux_ADC_R2  5
#define  Aux_ADC_R3  6
#define  Aux_ADC_R4  7

#define  Int_DAC_L1  0
#define  Int_DAC_L2  1
#define  Int_DAC_L3  2
#define  Aux_DAC_L4  3
#define  Int_DAC_R1  4
#define  Int_DAC_R2  5
#define  Int_DAC_R3  6
#define  Aux_DAC_R4  7

/*************************************************
* Variable declarations
*************************************************/
.section/dm seg_dmda;

.var powerdown_AD1838[4] =
  DAC_CTRL1 | WRITE_REG | 0x004,
  DAC_CTRL1 | WRITE_REG | 0x004,
  ADC_CTRL1 | WRITE_REG | 0x080,
  ADC_CTRL1 | WRITE_REG | 0x080;

.var powerdown_rx_buf[4];

.var tx_ctrl_buf[19] =
  /* Write register commands */
  DAC_CTRL1 | WRITE_REG | 0x000,
  DAC_CTRL1 | WRITE_REG | 0x000,
  DAC_CTRL2 | WRITE_REG | 0x000,
  DAC_VOL1 | WRITE_REG | 0x3FF,
  DAC_VOL2 | WRITE_REG | 0x3FF,
  DAC_VOL3 | WRITE_REG | 0x3FF,
  DAC_VOL4 | WRITE_REG | 0x3FF,
  DAC_VOL5 | WRITE_REG | 0x3FF,
  DAC_VOL6 | WRITE_REG | 0x3FF,
  ADC_CTRL1 | WRITE_REG | 0x000,
  ADC_CTRL1 | WRITE_REG | 0x000,
  ADC_CTRL3 | WRITE_REG | 0x040,
  ADC_CTRL2 | WRITE_REG | 0x380,
  ADC_CTRL2 | WRITE_REG | 0x380,
  /* Read register commands */
  ADC_PEAK0 | READ_REG | 0x000,
  ADC_PEAK1 | READ_REG | 0x000,
  ADC_CTRL1 | READ_REG | 0x000,
  ADC_CTRL2 | READ_REG | 0x000,
  ADC_CTRL3 | READ_REG | 0x000;

.var rx_ctrl_buf[19];

.var tx0_buf[8] =
  0x00000000,
  0x00000000,
  0x00000000,
  0x00000000,
  0x00000000,
  0x00000000,
  0x00000000,
  0x00000000;

.var rx0_buf[8];

.var tx0_tcb[8] = 0, 0, 0, 0, 0, 8, 1, tx0_buf;
.var rx0_tcb[8] = 0, 0, 0, 0, 0, 8, 1, rx0_buf;

.var left_in1;
.var right_in1;

.var left_out1;
.var left_out2;
.var left_out3;
.var right_out1;
.var right_out2;
.var right_out3;

.var left_ch;
.var right_ch;

.endseg;

/*************************************************
* Interrupt Vector Table
*************************************************/
.segment/pm seg_rth;

  nop; nop; nop; nop;    /* Reserved */
  nop; jump start; rti; rti;  /* RSTI  reset vector */
  nop; nop; nop; nop;    /* Reserved */
  rti; rti; rti; rti;    /* SOVFI stack overflow */
  rti; rti; rti; rti;    /* TMZHI high timer */
  rti; rti; rti; rti;    /* VIRPTI vector interrupt */
  rti; rti; rti; rti;    /* IRQ2I irq2 vector */
  rti; rti; rti; rti;    /* IRQ1I irq1 vector */
  rti; rti; rti; rti;    /* IRQ0I irq0 vector */
  nop; nop; nop; nop;    /* Reserved */

spr0i_svc:
  jump process_audio_sample;  /* SPR0I sport0 rx */
  rti; rti; rti;

  rti; rti; rti; rti;    /* SPR1I sport1 rx */
  rti; rti; rti; rti;    /* SPT0I sport0 tx */
  rti; rti; rti; rti;    /* SPT1I sport1 tx */
  nop; nop; nop; nop;    /* Reserved */
  nop; nop; nop; nop;    /* Reserved */
  rti; rti; rti; rti;    /* EP0I  ext port buffer0 */
  rti; rti; rti; rti;    /* EP1I  ext port buffer1 */
  nop; nop; nop; nop;    /* Reserved */
  nop; nop; nop; nop;    /* Reserved */
  nop; nop; nop; nop;    /* Reserved */
  rti; rti; rti; rti;    /* CB7I  circ buffer7 overflow */
  rti; rti; rti; rti;    /* CB15I circ buffer15 overflow */
  rti; rti; rti; rti;    /* TMZLI low timer */
  rti; rti; rti; rti;    /* FIXI  fixed-pt overflow */
  rti; rti; rti; rti;    /* FLTOI floating-pt overflow */
  rti; rti; rti; rti;    /* FLTUI floating-pt underflow */
  rti; rti; rti; rti;    /* FLTII floating-pt invalid */
  rti; rti; rti; rti;    /* SFT0I user sw0 int */
  rti; rti; rti; rti;    /* SFT1I user sw1 int */
  rti; rti; rti; rti;    /* SFT2I user sw2 int */
  rti; rti; rti; rti;    /* SFT3I user sw3 int */
.endseg;

/*************************************************
* Main program
*************************************************/
.segment/pm seg_pmco;

start:

  call init_dsp;     /* Set up Flags and IRQs */
  call clear_sport_regs;   /* Set all SPROT regs to 0 */
  call init_codec_regs;   /* Initialize AD1838 control regs */
  call init_sport0_tdm_regs;  /* Set up SPORT0 for TDM */
  call init_sport0_dma_regs;  /* Set up SPORT0 for DMA chaining */

  IRPTL = 0x00000000;    /* Clear pending interrupts */
  bit set IMASK SPT0I|SPR0I;  /* Enable SPORT0 interrupts */

  USTAT2 = dm(SRCTL0);   /* Allow multichannel */
  bit set USTAT2 MCE;
  dm(SRCTL0) = USTAT2;

main_loop:
  idle;       /* Wait for interrupts */
  jump main_loop;

/*************************************************
* Interrupt routine to process incoming audio data
*************************************************/
process_audio_sample:

  r0 = dm(rx0_buf + Int_ADC_L1); /* Store ADC input data in memory */
  dm(left_in1) = r0;
  r1 = dm(rx0_buf + Int_ADC_R1);
  dm(right_in1) = r1;

  // Put code to process the signals here
  call (pc, process_audio);

  r0 = dm(left_out1);    /* Retrieve processed audio data from */
  dm(tx0_buf + Int_DAC_L1) = r0; /* memory and write to tx buffer */
  r1 = dm(left_out2);
  dm(tx0_buf + Int_DAC_L2) = r1;
  r2 = dm(left_out3);
  dm(tx0_buf + Int_DAC_L3) = r2;

  r0 = dm(right_out1);
  dm(tx0_buf + Int_DAC_R1) = r0;
  r1 = dm(right_out2);
  dm(tx0_buf + Int_DAC_R2) = r1;
  r2 = dm(right_out3);
  dm(tx0_buf + Int_DAC_R3) = r2;

  rti;

/*************************************************
* Subroutine to process incoming audio data
*************************************************/
process_audio:

  r2 = dm(left_in1);   /* Copy input to all outputs */

  dm(left_out1) = r2;
  dm(left_out2) = r2;
  dm(left_out3) = r2;

  r2 = dm(right_in1);

  dm(right_out1) = r2;
  dm(right_out2) = r2;
  dm(right_out3) = r2;

  rts;

/*************************************************
* Subroutine to initialize the DSP
*************************************************/
init_dsp:

  /* Set Flags 4-9 as outputs, Flags 10 & 11 as inputs */
  USTAT2 = dm(IOCTL);
  bit set USTAT2 FLG9O|FLG8O|FLG7O|FLG6O|FLG5O|FLG4O;
  bit clr USTAT2 FLG11O|FLG10O;
  dm(IOCTL) = USTAT2;

  /* Set Flags 0-3 as pushbutton inputs */
  bit clr MODE2 FLG3O|FLG2O|FLG1O|FLG0O;

  /* Enable IRQ 0-2 interrupts (pushbuttons) */
  IMASK = 0x0;
  IRPTL = 0x00000000;
  bit set MODE2 IRQ2E|IRQ1E|IRQ0E;
  bit set MODE1 IRPTEN|NESTM;
  bit set IMASK IRQ2I|IRQ1I|IRQ0I;

  L0 = 0;
  L1 = 0;
  L2 = 0;
  L3 = 0;
  L4 = 0;
  L5 = 0;
  L6 = 0;
  L7 = 0;
  L8 = 0;
  L9 = 0;
  L10 = 0;
  L11 = 0;
  L12 = 0;
  L13 = 0;
  L14 = 0;
  L15 = 0;

  rts;

/*************************************************
* Subroutine to clear the SPORT registers
*************************************************/
clear_sport_regs:

  /* Clear pending interrupts */
  IRPTL = 0x00000000;
  bit clr IMASK SPT1I|SPT0I|SPR1I|SPR0I;

  /* Clear SPORT control and div registers */
  r0 = 0x00000000;
  dm(STCTL0) = r0;
  dm(SRCTL0) = r0;
  dm(STCTL1) = r0;
  dm(SRCTL1) = r0;
  dm(TDIV0) = r0;
  dm(RDIV0) = r0;
  dm(TDIV1) = r0;
  dm(RDIV1) = r0;

  /* Clear SPORT multichannel registers */
  r0 = 0x00000000;
  dm(MTCS0) = r0;
  dm(MRCS0) = r0;
  dm(MTCS1) = r0;
  dm(MTCS0) = r0;

  /* Clear SPORT multichannel companding-enable registers */
  r0 = 0x00000000;
  dm(MTCCS0) = r0;
  dm(MRCCS0) = r0;
  dm(MTCCS1) = r0;
  dm(MRCCS1) = r0;

  rts;

/*************************************************
* Subroutine to initialize the codec registers
*************************************************/
init_codec_regs:

  r0 = 0x00000000;
  dm(STCTL1) = r0;
  dm(SRCTL1) = r0;
  USTAT1 = dm(STCTL1);
  USTAT2 = dm(SRCTL1);

  /* Setup SPORT1 DMA registers */
  r0 = powerdown_AD1838;  /* SPORT1 DMA memory index */
  dm(IIT1A) = r0;
  r0 = 1;      /* SPORT1 DMA memory modifier */
  dm(IMT1A) = r0;
  r0 = @powerdown_AD1838;  /* Number of SPORT1 DMA transfers */
  dm(CT1A) = r0;

  r0 = powerdown_rx_buf;  /* Power down the codec twice to */
  dm(IIR1A) = r0;    /* counter an anomaly with the codec */
  r0 = 1;
  dm(IMR1A) = r0;
  r0 = @powerdown_rx_buf;
  dm(CR1A) = r0;

  r0 = 0x0011003B;   /* FS = 18 clocks, TCLK ~ 1 MHz */
  dm(TDIV1) = r0;
  r0 = 0x00000000;
  dm(RDIV1) = r0;

  bit set USTAT1 SDEN_A|LAFS|LTFS|ITFS|TFSR|CKRE|ICLK|SLEN16|SPEN_A;
  dm(STCTL1) = USTAT1;

  bit set USTAT2 SDEN_A|LAFS|LRFS|RFSR|CKRE|SLEN16|SPEN_A;
  bit clr USTAT2 IRFS|ICLK;
  dm(SRCTL1) = USTAT2;

  bit set IMASK SPT1I|SPR1I;

power_cycle_not_done:    /* Wait for DMA completion
  idle;
  r1 = 0x00000008;
  r0 = dm(DMASTAT);
  r0 = r0 and r1;
  if NE jump power_cycle_not_done;

  bit clr IMASK SPT1I|SPR1I;

wait_powerdown:
  LCNTR = 3000, do waitloop until lce;
  nop;
  nop;
  nop;
waitloop: nop;

  bit clr USTAT1 0xFFFFFFFF; /* Clear SPORT1 CTRL regs */
  dm(SRCTL1) = USTAT1;
  dm(STCTL1) = USTAT1;
  IRPTL = 0;

SPORT_DMA_setup:
  USTAT1 = dm(STCTL1);
  USTAT2 = dm(SRCTL1);

  r0 = tx_ctrl_buf;   /* Set up SPORT1 DMA */
  dm(IIT1A) = r0;    /* Initialize codec control regs */
  r0 = 1;
  dm(IMT1A) = r0;
  r0 = @tx_ctrl_buf;
  dm(CT1A) = r0;

  r0 = rx_ctrl_buf;
  dm(IIR1A) = r0;
  r0 = 1;
  dm(IMR1A) = r0;
  r0 = @rx_ctrl_buf;
  dm(CR1A) = r0;

  r0 = 0x0011003B;
  dm(TDIV1) = r0;
  r0 = 0;
  dm(RDIV1) = r0;

  bit set USTAT1 SDEN_A|LAFS|LTFS|ITFS|TFSR|CKRE|ICLK|SLEN16|SPEN_A;
  dm(STCTL1) = USTAT1;

  bit set USTAT2 SDEN_A|LAFS|LTFS|RFSR|CKRE|SLEN16|SPEN_A;
  bit clr USTAT2 IRFS|ICLK;
  dm(SRCTL1) = USTAT2;

  bit set IMASK SPT1I|SPR1I;

SPORT_DMA_not_done:
  idle;       /* Wait for DMA completion */
  r1 = 0x0000000A;
  r0 = dm(DMASTAT);
  r0 = r0 and r1;
  if NE jump SPORT_DMA_not_done;

  bit clr USTAT1 0xFFFFFFFF;
  dm(SRCTL1) = USTAT1;
  dm(STCTL1) = USTAT1;
  IRPTL = 0;

  bit clr IMASK SPT1I|SPR1I;

  rts;

/*************************************************
* Subroutine to initialize SPORT0 TDM registers
*************************************************/
init_sport0_tdm_regs:

  r0 = 0x00000000;
  dm(TDIV0) = r0;
  dm(RDIV0) = r0;
  dm(STCTL0) = r0;
  dm(SRCTL0) = r0;
  nop;

  USTAT1 = dm(STCTL0);
  USTAT2 = dm(SRCTL0);

  /* Set MFD = 1, chaining enable, dma enable, word length = 32 bits */
  bit set USTAT1 MFD1|SCHEN_A|SDEN_A|SLEN32;
  dm(STCTL0) = USTAT1;

  /* Set num. channels = 8, chaining enable, dma enable, word length = 32 */
  /* Wait to set MCE bit */
  bit set USTAT2 NCH7|SCHEN_A|SDEN_A|SLEN32;
  bit clr USTAT2 IRFS|ICLK;
  dm(SRCTL0) = USTAT2;

  /* Enable channels 0-7 */
  r0 = 0x000000FF;
  dm(MTCS0) = r0;
  dm(MRCS0) = r0;

  /* No companding */
  r0 = 0x00000000;
  dm(MTCCS0) = r0;
  dm(MRCCS0) = r0;

  rts;

/*************************************************
* Subroutine to initialize SPORT0 DMA registers
*************************************************/
init_sport0_dma_regs:

  r1 = 0x0001FFFF;

  /* Set DMA chaining regs */
  r0 = tx0_buf;   /* index = tx0_buf */
  dm(tx0_tcb + 7) = r0;
  r0 = 1;     /* modifier = 1 */
  dm(tx0_tcb + 6) = r0;
  r0 = 8;     /* count = 8 */
  dm(tx0_tcb + 5) = r0;

  r0 = tx0_tcb + 7;  /* chain pointer = tx0_tcb + 7 */
  r0 = r0 and r1;   /* with CPI bit set */
  r0 = bset r0 by 17;
  dm(tx0_tcb + 4) = r0;
  dm(CPT0A) = r0;

  r0 = rx0_buf;   /* index = rx0_buf */
  dm(rx0_tcb + 7) = r0;
  r0 = 1;     /* modifier = 1 */
  dm(rx0_tcb + 6) = r0;
  r0 = 8;     /* count = 8 */
  dm(rx0_tcb + 5) = r0;

  r0 = rx0_tcb + 7;  /* chain pointer = rx0_buf + 7 */
  r0 = r0 and r1;   /* with CPI bit set */
  r0 = bset r0 by 17;
  dm(rx0_tcb + 4) = r0;
  dm(CPR0A) = r0;

  rts;

.endseg;


Alex Parkinson wrote:
[schnipp]
> The Problem: > The DSP DMA transfer begins one bit clock after the assertion of FSTDM > (MFD = 1), at which time it sends out the three left channels plus an empty > channel. Then, for some reason, the DSDATA line tri-states for the remainder > of the frame (four more channels).
[schnipp] SHARCs tri-state the tx data line in the TDM mode when they think they're not supposed to tx data on a given slot. This is so you can gang multiple sharcs up on a single TDM interface. This sure makes it sound like the TDM tx is only set up to tx four slots, or possible that it somehow thinks it has 16-bit slots instead of 32. But looking at your code, I can't see where any of those mistakes could have crept in. I did notice that MRCS1 isn't cleared, but I don't think that's the problem.
> /* Clear SPORT multichannel registers */ > r0 = 0x00000000; > dm(MTCS0) = r0; > dm(MRCS0) = r0; > dm(MTCS1) = r0; > dm(MTCS0) = r0;
Once thing you might do to make the code a little easier to read is to assign values to the ustat registers outright instead of using the bit set/clr operations. For instance, "ustat2=NCH7|SCHEN_A|SDEN_A|SLEN32;" is a perfectly legal instruction, and it eliminates the need to sort through the preceeding lines of code to make sure that no other bits in ustat2 were set beforehand. For debugging serial port problems with an o'scope, I recommend that you continually transmit the word 0xb38f. I like this word because it's easy to spot on a scope (1 hi, 1 lo, 2 hi, 2 lo, etc...). I recommend that you ignore the rx until you get the tx worked out - and that means quit writing to the tx buffer in the isr so you can tx static data (i.e., 0xb38f) instead. Then look at the tx line. Do you see four slots or eight? are they 32-bits or 16? Good luck with this. Hope this helped a little at least. -- Jim Thomas Principal Applications Engineer Bittware, Inc jthomas@bittware.com http://www.bittware.com (703) 779-7770 There's a fine line between clever and stupid
Jim Thomas wrote:
> > This sure makes it sound like the TDM tx is only set up to tx four > slots, or possible that it somehow thinks it has 16-bit slots instead of
32.
> ... > For debugging serial port problems with an o'scope, I recommend that you > continually transmit the word 0xb38f. I like this word because it's > easy to spot on a scope (1 hi, 1 lo, 2 hi, 2 lo, etc...). > > I recommend that you ignore the rx until you get the tx worked out - and > that means quit writing to the tx buffer in the isr so you can tx static > data (i.e., 0xb38f) instead. Then look at the tx line. Do you see four > slots or eight? are they 32-bits or 16?
I've taken your advice and sent static tx data to the codec (I also cleaned up my code a little bit). With this, I realized two things: 1) Some of the outputs on my codec were fried, and 2) the TDM is set up for four channels per frame. After replacing the codec, all three of the left outputs work as only DAC_L3 did previously. However, I still can't figure out why the DSP would only recognize four channels, since NCH is set to 7 (num. channels - 1) and MTCS0 and MRCS0 are set to 0x00FF (to enable DMA channels 0 - 7). Is it possible that I'm initializing certain registers in the wrong order? Alex Parkinson ahparky@ufl.edu
Alex Parkinson wrote:
> I've taken your advice and sent static tx data to the codec (I also cleaned > up my code a little bit). With this, I realized two things: 1) Some of the > outputs on my codec were fried, and 2) the TDM is set up for four channels > per frame. After replacing the codec, all three of the left outputs work as > only DAC_L3 did previously. However, I still can't figure out why the DSP > would only recognize four channels, since NCH is set to 7 (num. channels - > 1) and MTCS0 and MRCS0 are set to 0x00FF (to enable DMA channels 0 - 7). Is > it possible that I'm initializing certain registers in the wrong order?
That's puzzling alright. You're initializing the reigsters in a different order than I do, but that doesn't mean your order is wrong. Here's an order I've used (which works): Set up TCB's clear tx and rx control registers setup divisor regs. setup rx ctrl setup rx ctrl clear companding registers setup MTCSx setup MRCSx write to the DMA chain point. Writing to the chain pointer starts the dma. I dunno if it'll have an effect, but you could also try setting the div registers so that the fs part is 0xff. I THINK that only has an effect if the sharc is generating the frame sync, but you never know. -- Jim Thomas Principal Applications Engineer Bittware, Inc jthomas@bittware.com http://www.bittware.com (703) 779-7770 Having a smoking section in a restaurant is like having a peeing section in a swimming pool.