This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28375D: CLA, CPU, ADC buses

Part Number: TMS320F28375D

If I assign ownership of the ADCs to CPU2, it states that the data registers are still readable by all processors, so if CLA on CPU2 is reading the ADC, which bus is it using? 

If CPU2 is reading a ADC register and the CLA on CPU2 is reading a register, are they using the same bus? or

The documentation talks about the CLA bus in the context of accessing shared memory, but doesn't really state with regard to say the ADC registers.

Does the CLA use the CLA bus to access the ADC data registers, just like memory, so there is no conflict with the CPU running it's programs and/or reading the ADC registers?

  • Hi Rob,

    Does the CLA use the CLA bus to access the ADC data registers, just like memory, so there is no conflict with the CPU running it's programs and/or reading the ADC registers?

    Yes, CLA uses it's own bus to read the ADC data/result registers hence there is not conflict with CPU running the program or reading the ADC data/result (please note only data/result register) registers.

    Regards,

    Vivek Singh

  • Okay, here is what I have finally narrowed the problem down to.

    I am observing the following...  I have CPU1 doing nothing, I have CPU doing 2 cases described below:  I have CLA on CPU2 with 4 interrupts ervicing the four ADCs... all responding to timer 0 from CPU2.

    I observe in case 1:

    If I just let CPU do nothing... so it essentially exists the main loop and terminates....  then CLA on CPU2 successfully services all 4 interrupts as desired... 

    case 2:

    I add a while (1) loop to CPU2 main function and I have it do some bogus memory operations on GSMRAM (ie it reads some meory, tests it for something, does something else...  just something to keep it busy and prevent the compiler from generating a really tight loop or any 'special case'  just in general to represent a program running... but specifically to ensure no shared resource conflicts, limited to just memory acess of shared ram between processors assigned to CPU2...

    ONce I do this without any change I can monitor on an external scope using GPIO lines, that it now is only servicing the first 2 ADC interrupts....

    So something on CPU2 is somehow affecting the CLA...  since the only thing that is shared in this test case is the timer0 on CPU2... I decided to use timer 0 on CPU1...

    When I do this (at the same rate around 200khz sampling rate) now all 4 interrupts get serviced on the CLA, just by changing their interrupt source from timer0 on CPU2 to timer 0 on CPU1...   note: CPU1 is doing nothing..

    While I don't yet know for sure what the connection is... it is a combination of our high sampling rate, combined with loading the CPU processor, and the CPU timer...

    Is there any reason that loading the CPU2 with a program should in any way affect the timer0, and specifically affect timer0 as a trigger to all 4 ADC s on the CLA... 

    The odd part in this is...even if loading the CPU2 affected timer 0, why is it only triggering 2 of the adc's on the CLA?  and why can I switch to timer0 on CPU1 and eliminate the problem... I actually have a small main loop running on CPU1 as well ie: CPU1 does have some processing happening, idly checking if CPU2 is going to give it data to send on the USB... but still, it's looping waiting for CPU2....  so it's 'doing' about as much as I have CPU2 doing... but this doesn't seem to affect it's timer 0 from affecting the CLA and the triggering of all 4 ADC conversions from the same timer.. 

    so cases I tried were:

    (with the CPU2 running a dummy main loop)

    case 1 CPU2 timer 0 192khz sampling rate (only services the first two interrupts on CLA)

    case 2 CPU1 tiemr 0 192khz sampling rate (services all 4 interrupts on CLA)

    case 3 CPU2 timer 1 192kHz sampling rate (only services the first two interrupts on CLA)

    case 4 CPU2 tiemr 1 100kHz sampling rate ( services 3 of the 4 interrupts)

    case 5 CPU2 timer 1 66kHz sampling rate (services all 4 of the interrupts)

    So what I see is that some combination of loading on the processor, which processor, and what the sampling rate is , is somehow influencing the triggering of the 4 interrupts from the timer on the CLA on CPU2...  meaning that I only receive the above stated interrupts (so when I say 2 are received that means A & B ar received but not C & D, when 3 are received the AB&C ar received but not D.

    NOTE:  in case 2 where all I did is switch the timer from CPU2 to CPU1... to suddenly see the CLA start working on all 4 vs only 2... tells me something weird, cause that ahs nothing to do with what code I have running, I configured both timers the same on both processors...  If this is a case of maximum rates?

    Thanks.

  • Okay, here is what I'm observing, and I've eliminated a lot of things to make a reasonably simple test case.

    I have CPU1, CPU2, and CLA on CPU2.

    the CLA on CPU2 is servicing the 4 ADCs, so I have 4 interrupts one for each ADC triggered

    right now I've turned off interrupts so the CLA is just 'running' the interrupts.. all it is doing is reading the ADC registers of the associated ADC and storing in memory.

    I added the GPIO pins to the CLA. so I can see when each of the 4 interrupts run.

    CPU1 is doing idle work and CPU2 is in a simple while loop reading memory... 

    the sample rate is 192khz.

    Only the first two interrupts are occurring.

    case 2:

    If I comment out the while loop on CPU2, so it essentially exist the main loop, change nothing  elese

    All of a sudden I now see all four interrupts occurring on the CLA.

    case 3:

    If I re-enable the while loop but change the sampling rate from 192khz to 100khz to 66khz... I see 2, 3, and then 4 interrupts occurring on the CLA.

    NOTE:  I am using timer 1 of CPU2 in all these cases so far to trigger the 4 CLA ADCs.

    case 4:

    If I set it to use timer 0 of CPU2 at 192kHz I observe the same as timer 1.

    case 5:

    If I set timer 0 of CPU1 to 192khz, and then have that run the CLA ADCs, then all 4 interrupts are triggered.

    SUMMARY:

    I am definitely seeing a strange 'influence' on the CLA's (on CPU2) ability to service the 4 ADCs.  It is influenced by sample rate, or which CPU timer is being used, or what the CPU2 is doing... so far only when the CPU2 is doing nothing does it seem to work right wwhen using timers on CPU2.

    I can send scope pictures if you wish, showing the observed GPIO pins that are toggling when it enters and exits the interrupt on the CLA.

    I think the point is I'm trying to isolate it... so to isolate it I have limited the CLA interrupt to the basics:  (code supplied below)

    and I'm doing nothing important on the CPUs... 

    So the original observation was when I did things in the main loop (processing) I saw the samples do weird things...

    CLUE:

    It Should be noted...  when the clock is set to 192kHz (5.2us)  only adc a and b interrupts occur, AND the ADC a interrupt occurs again at 3.0 us... not 5.2 us.

    however in modes (cases listed above) adc properly gets called at 5.2us when it's working.. the point is when something is affecting the calling of the interrupts.

    with sensitivity to the rate, and loading of the CPU2 main thread...

    My thinking is, since I'm not using any shared memory or any conflicting resource, that the only 'shared' resource between the CLA and the ADCs and CPU2 would be the timer... this is why I tried the tiemr on CPU1... and sure enough it worked... but the concern is....  CPU1 is busy doing bogus work as well... so why does it's timer work and not the timer on CPU2 when CPU2 is doing work?  The bigger concern is.... If I switch to using CPU1 timer because it works now.... as I continue to alter and adjust the main programs on CPU2 and CPU1... will there come a time when I start to observe this behavior again... for example I make some change on CPU1 to the code and all of a sudden I start observing this 'dropout' of ADC interrupts on CLA on CPU2?

    This is very definite and repeatable at this time... so we could probably successfully work with it now that I've isolated it to a more confined test case...

    I figure:

    I must have: a configured something wrong or inappropriately b.  Be asking to much of the part in some fashion...

    but I need to determine what I need to change in the setup, or what violation of limit I am exceeding...   since I can change some simple things (sample rate, which timer) and see changes in how it operates...   however in the main application I can't actually change these things... and since the timer from one CPU allows it to work as desired and not the other, but a change in sampling rate on the other 'unties' this knot...  it's very confusing.

    Thanks very much.

    CODE that each interrupt calls....  I of course vary which GPIOs I call to display on code...

    GpioDataRegs.GPCDAT.bit.GPIO94 = 1;

    GpioDataRegs.GPDDAT.bit.GPIO97 = 1;

     

    Cla1SoftIntRegs.SOFTINTEN.bit.TASK1 = 1;

    CLA_ADC_0_ASM();

    GpioDataRegs.GPCDAT.bit.GPIO94 = 0;

    GpioDataRegs.GPDDAT.bit.GPIO97 = 0;

    ASM code....

    _CLA_ADC_0_ASM:

    ;****

    ; Preping our workspace

    ;******

    MMOV16 MAR1, @_theCLADataBufferMemoryPtr

    MMOV16 MAR0, @_theCLAThisPtr ; load theCLAThisPointer once

    ; MMOV32 MR0, @_AdcaResultRegs.ADCRESULT0 ; load ADC Result register 0

    MUI16TOF32 MR0, @_AdcaResultRegs.ADCRESULT0; load ADC Result register 0

    ; MMOV32 MR1, @_AdcaResultRegs.ADCRESULT1 ; load ADC Result register 1

    MUI16TOF32 MR1, @_AdcaResultRegs.ADCRESULT1; load ADC Result register 1

    ; MMOV32 MR2, @_AdcaResultRegs.ADCRESULT2 ; load ADC Result register 2

    MUI16TOF32 MR2, @_AdcaResultRegs.ADCRESULT2; load ADC Result register 2

    MMOV32 *MAR1+[34], MR0 ; store the result

    MMOV32 *MAR1+[38], MR1 ; store the result

    MMOV32 *MAR1+[32], MR2 ; store the result

    ; MMOV32 MR0, @_AdcaResultRegs.ADCRESULT3 ; load ADC Result register 3

    MUI16TOF32 MR0, @_AdcaResultRegs.ADCRESULT3; load ADC Result register 3

    MMOV32 *MAR1+[36], MR0 ; store the result

    ; MMOV32 MR0, @_AdcaResultRegs.ADCRESULT4 ; load ADC Result register 4

    MUI16TOF32 MR0, @_AdcaResultRegs.ADCRESULT4; load ADC Result register 4

    MMOV32 *MAR1+[30], MR0 ; store the result

    ; MMOV32 MR0, @_AdcaResultRegs.ADCRESULT5 ; load ADC Result register 5

    MUI16TOF32 MR0, @_AdcaResultRegs.ADCRESULT5; load ADC Result register 5

    MMOV32 *MAR1+[40], MR0 ; store the result

    MMOV32 MR0, @_ADC_Flag

    MMOVI32 MR1, #0x0001 ; flag for this ADCA

    MMOVI32 MR2, #0x000F ; value to compare to

    MAND32 MR3, MR0, MR1 ; test if missing sample

    MCMP32 MR3, MR1

    MNOP

    MNOP

    MNOP

    MBCNDD branch_debugStop3, EQ

    MOR32 MR0, MR0, MR1 ; set the flag that this ADC has fired

    MCMP32 MR0, MR2

     MMOV32 @_ADC_Flag, MR0 ; store the flag

    MMOVI32 MR2, #0x0000 ; load so can zero flag

    MNOP

    MRCNDD NEQ

    then I basically return

    I just put the test in to see if I ever call one ADC twice before all ADCs have fired as a error condition...  which I have been using

    to catch this issue... since I'm giving enough time for all 4 interrupts to occur, it should never call the first one before the 4th one has completed...

  • Hi Rob,

    Thanks for providing the detailed overview of the issue and experiment you have tried so for. How are you triggering different CLA TASKs from same timer interrupt?

    Vivek Singh
  • Below is a copy of the ADC initialization , this is for ADCA and it is repeated for BC&D of course changing the registers appropriately.

    As such I can setup each ADC to trigger on the same timer. Each ADC I setup 6 SOCs to evenly convert 6 (for a total of 24 channels).

    In the CLA code I set using table 5-1 the associated ADC interrupt... and split each one to a task... so ADCA goes to task 1, ADCB goes to task 2 etc... currently I have the CLA to not generate interrupts when the tasks complete... and I effectively have them just copying the data for something to do... (to eliminate/debug this situation)

    so to answer your question, the ADCs are all set to the same trigger, the same timer.... but each CLA task responds to a given ADC... does that make sense?

    typedef enum {
    ADC_ERROR = 0,
    ADCA_1 = 1,
    ADCA_2 = 2,
    ADCA_3 = 3,
    ADCA_4 = 4,
    ADCB_1 = 6,
    ADCB_2 = 7,
    ADCB_3 = 8,
    ADCB_4 = 9,
    ADCC_1 = 11,
    ADCC_2 = 12,
    ADCC_3 = 13,
    ADCC_4 = 14,
    ADCD_1 = 16,
    ADCD_2 = 17,
    ADCD_3 = 18,
    ADCD_4 = 19
    } CLA_Interrupt;


    typedef enum {
    CPU1_TIMER_0 = 1,
    CPU1_TIMER_1 = 2,
    CPU1_TIMER_2 = 3,
    CPU2_TIMER_0 = 29,
    CPU2_TIMER_1 = 30,
    CPU2_TIMER_2 = 31
    } ADC_Timer;

    (theTimer below gets set to one of the above settings. as per table 10-30 and others for associated SOC)


    EALLOW;

    // setup Round robin/SOC servicing registers
    AdcaRegs.ADCSOCPRICTL.bit.SOCPRIORITY = 6; // SOC0 to SOC5 are high priority

    // Setup SOC registers
    AdcaRegs.ADCSOC0CTL.bit.CHSEL = 0; // SOC0 will convert ADCINA0
    AdcaRegs.ADCSOC1CTL.bit.CHSEL = 1; // SOC1 will convert ADCINA1
    AdcaRegs.ADCSOC2CTL.bit.CHSEL = 2; // SOC2 will convert ADCINA2
    AdcaRegs.ADCSOC3CTL.bit.CHSEL = 3; // SOC3 will convert ADCINA3
    AdcaRegs.ADCSOC4CTL.bit.CHSEL = 4; // SOC4 will convert ADCINA4
    AdcaRegs.ADCSOC5CTL.bit.CHSEL = 5; // SOC5 will convert ADCINA5

    AdcaRegs.ADCSOC0CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;
    AdcaRegs.ADCSOC1CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;
    AdcaRegs.ADCSOC2CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;
    AdcaRegs.ADCSOC3CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;
    AdcaRegs.ADCSOC4CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;
    AdcaRegs.ADCSOC5CTL.bit.ACQPS = ADC_SAMPLING_SYSCLK_CYCLES;

    AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = theTimer;
    AdcaRegs.ADCSOC1CTL.bit.TRIGSEL = theTimer;
    AdcaRegs.ADCSOC2CTL.bit.TRIGSEL = theTimer;
    AdcaRegs.ADCSOC3CTL.bit.TRIGSEL = theTimer;
    AdcaRegs.ADCSOC4CTL.bit.TRIGSEL = theTimer;
    AdcaRegs.ADCSOC5CTL.bit.TRIGSEL = theTimer;

    // Setup interrupt registers
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = ADC_EARLY_INTERRUPT;

    // This is to allow the CLA to control, or match the interrupts it assigns to The ADCs since
    // the CLA is in control of it's interrupt table, this way it can assign ADC objects whatever
    // interrupt it desires that it will respond to.
    switch ( theInterrupt ) {
    case ADCINT1:
    AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 5; // Only genearte EOC after SOC5 is complete
    AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1; // enable interrupt 1
    AdcaRegs.ADCINTSEL1N2.bit.INT1CONT = ADC_CONTINOUS_MODE;
    // AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // make sure int1 flag is cleared
    // NOTE: the TI code manually clears the int flag here. But it 'should be default'
    // left it commented out so if we find for some reason it's necessary we can
    // just follow suit.
    break;
    case ADCINT2:
    AdcaRegs.ADCINTSEL1N2.bit.INT2SEL = 5; // Only genearte EOC after SOC5 is complete
    AdcaRegs.ADCINTSEL1N2.bit.INT2E = 1; // enable interrupt 2
    AdcaRegs.ADCINTSEL1N2.bit.INT2CONT = ADC_CONTINOUS_MODE;
    // AdcaRegs.ADCINTFLGCLR.bit.ADCINT2 = 1; // Make sure int2 flag is cleared
    break;
    case ADCINT3:
    AdcaRegs.ADCINTSEL3N4.bit.INT3SEL = 5; // Only genearte EOC after SOC5 is complete
    AdcaRegs.ADCINTSEL3N4.bit.INT3E = 1; // enable interrupt 3
    AdcaRegs.ADCINTSEL3N4.bit.INT3CONT = ADC_CONTINOUS_MODE;
    // AdcaRegs.ADCINTFLGCLR.bit.ADCINT3 = 1; // Make sure int3 flag is cleared
    break;
    case ADCINT4:
    AdcaRegs.ADCINTSEL3N4.bit.INT4SEL = 5; // Only genearte EOC after SOC5 is complete
    AdcaRegs.ADCINTSEL3N4.bit.INT4E = 1; // enable interrupt 4
    AdcaRegs.ADCINTSEL3N4.bit.INT4CONT = ADC_CONTINOUS_MODE;
    // AdcaRegs.ADCINTFLGCLR.bit.ADCINT4 = 1; // Make sure int4 flag is cleared
    break;
    }
    EDIS;
  • Hi Rob, Sorry for late reply. Can you check the map file to see if there is anything mapped in LSx RAM for CPU2? Also would it be possible for you to share the sample code which we can run on our setup to reproduce the issue for further analysis.

    Regards,
    Vivek Singh
  • Hi Rob, Would it be possible to share the sample code for this issue?
  • You'd have to be more specific, yes there is lots mapped to LSxRAM for CPU2 since we are using the CLA processor on CPU2, that is where it's executable code and data go since that is the only ram it has access to... so ownership of the LSx ram has been given to the CLA.

    Although I have greatly narrowed the bug down, I will have to do some more work at extracting the minimum setup required that resembles our setup, so yes I will likely have time next week to work on this bug. I'll also look into the possibility that it is a development tool bug, ie: a bug that only occurs when using code composer... I haven't tested that. ie: can I replicate the bug when I let the processor start up on its own? don't know yet.

    One other extra bit of knowledge... I can replicate the bug simply by resetting (via code composer) and re-running the SAME code. So without any changes to the code I can get this bug to appear. and disappear... however the sensitivity by using the timer on CPU1 is MUCH less than if I use the timer on CPU2. So right now I'm using CPU1 timer as 80% of the time it allows me to run, only failing say 20% of the time... but ultimately I still need to locate why its doing this.

    I will try and narrow it down a bit more (make a simpler test case with less setup) and then hopefully I can send that to you.

    Thanks very much.
  • Thank you Rob. when I asked LSx mapping, my question was specific to CPU2 core (not CLA). Based on your reply, I think the answer is NO. 

    We will wait for sample code from you to reproduce this issue on our setup.

    Regards,

    Vivek Singh

  • Hi Rob,

    Do you have sample project for this issue to share?

    Vivek Singh
  • I am still working at this problem when I have time to do so.  However it's not top priority, so It will likely drag out over a long timeframe (weeks/months), just so you know.  I appreciate the support and will let you know when I have something to give you to replicate the problem.

    Thanks very much.

  • Thank you for the update Rob. Yes, please provide the sample code when you have time. We can keep this post open for tracking.
    I hope you are not gated due to this for now.

    Regards,
    Vivek Singh
  • This issue has finally been resolved.

    The issue was that the ADCs were not starting up at the same time, so on the very first sample clock might only trigger a random number of the 4 adcs... so it might trigger 1,2,3 or all 4 of them. When it triggered all 4 it would work. When it only caught less than 4 the code would catch this since we need all 4 ADCs (24 channels) simultaneously. The solution was to write a 'throw away' first time loop so if less than all 4 ADCs sent data before they started triggering for the next sample, throw that away (first time only) and then on the next clock cycle it would always get all 4 properly.

    This was also the issue when we removed the JTAG, it just happened more often.

    The problem was resolved by essentially detecting if one of th 4 adcs had received the 'next' interrupt (ADC Clock) and if all 4 had not been collected, then throw that data away on the very first execution only. Consequently the 2nd execution would always have all 4.

    Is there any built in approach to 'synchronize' all 4 ADCs? Essentially we desire all 24 to be sampled simultaneously, so we always need all 24 results, but on startup, is there a way to co-ordinate the hardware so that we can say 'GO' to all 4 on the next clock cycle?

    Not critical, but in idea we should be able not have to throw away the first incomplete set of samples.

    Thanks.
  • Rob,
    Vary glad to know you were able to isolate the issue and this is not a HW issue. We'll have our ADC expert to look into your query.

    Regards,
    Vivek Singh
  • Hi Rob,

    If you assign the same trigger to all the SOCs on all 4 ADCs, sampling should start at the same time based on that trigger. The samples won't all occur simultaneously, but instead SOC0 will convert first on each ADC, then SOC1 will convert on all the ADCs, etc (assuming all the ADCs priority and round-robin pointer are in a state that will cause SOC0 to be the first conversion, which is usually the case).

    The trigger could be an ePWM SOC event or a CPU timer if you want periodic samples. If you want to software trigger all the ADCs at the same time you should use the input XBAR input 5 / ADCEXTSOC to trigger all the ADCs in parallel using a GPIO (which you can toggle in SW).

    If you then want to process all the results, you should only need to have one of the ADCs trigger an ADCINT flag based on the last SOC (this should be the ADC with the most channels sampled, or any arbitrary ADC if they have the same number of samples triggered).