Hi,

Long time listener, first time caller here 🙂

I am using the Bela in a 2xCTAG FACE (aka BEAST) + Bela cape combination (while waiting for the Audio Expansion capelet to arrive) in order to be able to access 16 audio inputs on a single Bela. The idea is to connect 16 microphones to the Bela and pass the audio through a Filter-and-add beam former, mixing it all down to two channels at the output. Once the capelet arrives, I will need to figure out if the difference in latency and sample rate between the CTAG and the Audio Expansion inputs will cause extra headaches, but for now this is the plan.

My issue is as follows:

Ideally, I would use a block size of 4 audio frames to keep the latency low. However, whenever I enable the analog inputs, I get spurious drops (and noticeable audio pops). The CPU usage is only at 48-50%, which is unexpected to me. I see these kinds of messages in the console:

McASP unexpected transmit frame sync occurred
audio frame 408848, errorCode: 3
McASP unexpected transmit frame sync occurred
Underrun detected: 1 blocks dropped
audio frame 518076, errorCode: 3

Sometimes these happen every 100k frames, sometimes they come in groups that are closer together. If I increase the block size to 8, the issue seems to disappear even with the analog inputs enabled. If I disable the analog inputs, I have no drops with block size of 4 (with 16x64 tap filters, 2x audio summing and 2x normalization at the outputs).

If I go to the other extreme and remove all the processing, and simply enable a passthrough of one of the CTAG inputs to one of the outputs, the problem still manifests itself with the 8 analog inputs enabled.

Thank you in advance for any tips on what could be wrong!

My version info:

Image:
v0.3.8h, 5 January 2023

Core code:
Last updated on '15 Sep 2024 18:59:59 '
from file 'Bela'
via 'update_board'
Update was successful
Git desc: '4e8133ca'

    cristian_pandele McASP unexpected transmit frame sync occurred
    audio frame 408848, errorCode: 3
    McASP unexpected transmit frame sync occurred
    Underrun detected: 1 blocks dropped
    audio frame 518076, errorCode: 3

    This issue isn't related to CPU usage but to signal quality of the digital audio lines. You have a 12 MHz signal going through 3 stacks of headers, that's not entirely unexpected, although it has worked well before. There may be some ringing there that makes the clocks behave non-ideally. I would recommend first off squeezing the boards together to make sure there are no loose connections. Then, you could try adding some large-valued (100k ? 10k?) resistors to ground from each of P9.29 and P9.31 and see if that helps. Otherwise we could look at fixing it in software by changing who generates the clocks in the hope that it will improve stuff.

    If I increase the block size to 8, the issue seems to disappear even with the analog inputs enabled

    This I cannot explain btw.

    Thank you very much for the quick reply, Giulio! That is definitely something to try first thing on Monday! It of course makes perfect sense given the long traces.

    Regarding the clock generation, how can one control that?

    Thank you once again!

    This would be a starting point:

    diff --git a/core/Spi_Codec.cpp b/core/Spi_Codec.cpp
    index c1a27ceb2..d46307b69 100644
    --- a/core/Spi_Codec.cpp
    +++ b/core/Spi_Codec.cpp
    @@ -154,8 +154,8 @@ int Spi_Codec::initCodec(){
            writeRegister(REG_ADC_CONTROL_1, 0x23);
            if(_isBeast)
            {
    -               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock / wclock in master mode
    -               writeRegister(REG_ADC_CONTROL_2, 0x7C);
    +               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock / wclock in slave mode
    +               writeRegister(REG_ADC_CONTROL_2, 0x34);
            } else {
                    // wclock format = 50/50, 8 channels, normal bclock, inverted wclock, bclock / wclock in master mode
                    writeRegister(REG_ADC_CONTROL_2, 0x6C);
    @@ -401,8 +401,8 @@ McaspConfig& Spi_Codec::getMcaspConfig() {
             mcaspConfig.params.bitDelay = 1;
             mcaspConfig.params.ahclkIsInternal = true; // ignored in practice
             mcaspConfig.params.ahclkFreq = 12000000; // ignored in practice
    -        mcaspConfig.params.aclkIsInternal = false;
    -        mcaspConfig.params.wclkIsInternal = false;
    +        mcaspConfig.params.aclkIsInternal = true;
    +        mcaspConfig.params.wclkIsInternal = true;
             mcaspConfig.params.wclkIsWord = true;
             mcaspConfig.params.wclkFalling = false;
             mcaspConfig.params.externalSamplesRisingEdge = true;

    However, I am not very confident it'll be enough and it's untested ... soooo....

    Thanks a lot, Giulio! I was afraid I overlooked an API for controlling the clock source. I will definitely give it a spin and see if it helps. That would be the simplest intervention but, as you said, testing is needed. The resistors should be a minor intervention as well, so I think either of the two should be feasible for the project.

    I'll let you know how it worked out. In the meantime, enjoy the weekend!

    Hi again,

    Took me a while longer to get back to this, but unfortunately I couldn't significantly improve the situation with either of the approaches.

    I tried 10k , 100k and 1M Ohm resistors on the two pins (P9.29 mcasp0_fsx and P9.31mcasp0_aclkx) to ground, neither seemed to do a definitive improvement. I also tried just one pin to ground at a time.

    The code changes were also not enough, unfortunately. I saw that the SPI_Codec.cpp code got compiled again after I made the changes, so I am sure I got to test out the intended changes. I could see that the output volume dropped drastically with either of the two blocks changed, and the signal basically drowned in noise.

    Do you have a link showing how the bits in REG_ADC_CONTROL_2 are mapped? I have to admit that I never dug into that part of the code.

    If you happen to have any other tips, I would be happy to try things out! Oh, and I made sure that the boards are as pressed together as thy can, that didn't change things.

    Thank you once again!

      cristian_pandele . I could see that the output volume dropped drastically with either of the two blocks changed, and the signal basically drowned in noise.

      That could be a data formatting issue ( a pain to solve, but doable). The most important question: are the frame sync errors gone?

      cristian_pandele Do you have a link showing how the bits in REG_ADC_CONTROL_2 are mapped? I have to admit that I never dug into that part of the code.

      https://www.analog.com/media/en/technical-documentation/data-sheets/AD1938.pdf

      Thanks!

      Oh, and sorry - I forgot to mention that the code changes suggested made the sync errors disappear from the console. I was confused about the sound output level, so I feared it was a false positive result.

      Looking at the datasheet, the changes to the control register make sense. Changing from 0x7C to 0x34 sets both LRCLK and BCLK to slave mode.

      With regards to data formatting, do you think that the result could be that the audio data is now misaligned by half word? So either doing a dirty half word swap (16 MSB with 16 LSB of each sample) or concatenating LSB half word of one sample with the MSB half word of the next would be reasonable? Well, I am not sure that it would be 16 bits for any particular reason, I guess that it could be any delay.

      That's what I thought: that the signals would be shifted by some bits, but I am not sure that's realistic. I think with these settings the board that's configured with follower jumpers should still work as expected, can you confirm that?

      Hi Giulio,

      I just tested the other board, the behaviour is unfortunately the same for those input/outputs.

      I would try a combination of the two clock options:
      this is one where the BCLK is generated by the AD1938 while the WCLK is generated by the BBB:

      diff --git a/core/Spi_Codec.cpp b/core/Spi_Codec.cpp
      index c1a27ceb..c0fc25ad 100644
      --- a/core/Spi_Codec.cpp
      +++ b/core/Spi_Codec.cpp
      @@ -154,8 +154,8 @@ int Spi_Codec::initCodec(){
              writeRegister(REG_ADC_CONTROL_1, 0x23);
              if(_isBeast)
              {
      -               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock / wclock in master mode
      -               writeRegister(REG_ADC_CONTROL_2, 0x7C);
      +               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock in master mode / wclock in slave mode
      +               writeRegister(REG_ADC_CONTROL_2, 0x74); // was 0x6C
              } else {
                      // wclock format = 50/50, 8 channels, normal bclock, inverted wclock, bclock / wclock in master mode
                      writeRegister(REG_ADC_CONTROL_2, 0x6C);
      @@ -402,7 +402,7 @@ McaspConfig& Spi_Codec::getMcaspConfig() {
               mcaspConfig.params.ahclkIsInternal = true; // ignored in practice
               mcaspConfig.params.ahclkFreq = 12000000; // ignored in practice
               mcaspConfig.params.aclkIsInternal = false;
      -        mcaspConfig.params.wclkIsInternal = false;
      +        mcaspConfig.params.wclkIsInternal = true;
               mcaspConfig.params.wclkIsWord = true;
               mcaspConfig.params.wclkFalling = false;
               mcaspConfig.params.externalSamplesRisingEdge = true;

      and this is another variation where the BCLK is generated by the BBB while the WCLK is generated by the AD1938:

      diff --git a/core/Spi_Codec.cpp b/core/Spi_Codec.cpp
      index c1a27ceb..a0839d47 100644
      --- a/core/Spi_Codec.cpp
      +++ b/core/Spi_Codec.cpp
      @@ -154,8 +154,8 @@ int Spi_Codec::initCodec(){
              writeRegister(REG_ADC_CONTROL_1, 0x23);
              if(_isBeast)
              {
      -               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock / wclock in master mode
      -               writeRegister(REG_ADC_CONTROL_2, 0x7C);
      +               // wclock format = 50/50, 16 channels, normal bclock, inverted wclock, bclock in slave mode / wclock in master mode
      +               writeRegister(REG_ADC_CONTROL_2, 0x3C); // was 0x74
              } else {
                      // wclock format = 50/50, 8 channels, normal bclock, inverted wclock, bclock / wclock in master mode
                      writeRegister(REG_ADC_CONTROL_2, 0x6C);
      @@ -401,7 +401,7 @@ McaspConfig& Spi_Codec::getMcaspConfig() {
               mcaspConfig.params.bitDelay = 1;
               mcaspConfig.params.ahclkIsInternal = true; // ignored in practice
               mcaspConfig.params.ahclkFreq = 12000000; // ignored in practice
      -        mcaspConfig.params.aclkIsInternal = false;
      +        mcaspConfig.params.aclkIsInternal = true;
               mcaspConfig.params.wclkIsInternal = false;
               mcaspConfig.params.wclkIsWord = true;
               mcaspConfig.params.wclkFalling = false;

      I think the first of the two has some chance of working ...

      Thanks once again, Giulio!

      Your timing is impeccable, as well. I was away from the lab the past two days and today in the afternoon I am getting some time to look at this again.

      I was also wracking my brain about this the past days. Somehow I wasn't sure that with the previous change the time offset would necessarily be constant (or easy to determine in the first place). And definitely my attempts didn't make sense either, changing either the block at line 154 or the one at 401. Sounds promising with these changes across the two blocks.

      Will let you know how that goes!

      OK, after a bit of fiddling, I have some results!

      First of all, the issue seems to be fixed. My understanding is not complete as to exactly why. I tried all 16 possible combinations and summarized them in the table below. This is hopefully going to be helpful to someone else reading this later on, but my initial hope is that it will help me understand what is going on.

      bclock mode     wclock mode   |  aclk source     wclock source   | RESULT
      ================================================================================================
      master           master       |  external        external        | original, intermittent noise
      // REG_ADC_CONTROL_2 = 0x7C   | // aclkIsInternal = false        |
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      slave           master        |  external        external        | continuous frame sync loss
      // REG_ADC_CONTROL_2 = 0x6C   | // aclkIsInternal = false        |
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      master          slave         |  external        external        | PRU interrupt timeout, -1 110 Connection timed out
      // REG_ADC_CONTROL_2 = 0x74   | // aclkIsInternal = false        | McASP error, abort
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      slave            slave        |  external        external        | PRU interrupt timeout, -1 110 Connection timed out
      // REG_ADC_CONTROL_2 = 0x34   | // aclkIsInternal = false        | McASP error, abort
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      master          master        |  external        internal        | noise gone, but don't use - bclock/wclock both masters
      // REG_ADC_CONTROL_2 = 0x7C   | // aclkIsInternal = false        | 
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      slave           master        |  external        internal        | no output
      // REG_ADC_CONTROL_2 = 0x6C   | // aclkIsInternal = false        |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      master          slave         |  external        internal        | noise gone, all good
      // REG_ADC_CONTROL_2 = 0x74   | // aclkIsInternal = false        |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      slave          slave          |  external        internal        | PRU interrupt timeout, -1 110 Connection timed out
      // REG_ADC_CONTROL_2 = 0x34   | // aclkIsInternal = false        | McASP error, abort
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      master           master       |  internal        external        | continuous frame sync loss
      // REG_ADC_CONTROL_2 = 0x7C   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      slave           master        |  internal        external        | continuous frame sync loss
      // REG_ADC_CONTROL_2 = 0x6C   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      master          slave         |  internal        external        | PRU interrupt timeout, -1 110 Connection timed out
      // REG_ADC_CONTROL_2 = 0x74   | // aclkIsInternal = true         | McASP error, abort
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      slave            slave        |  internal        internal        | PRU interrupt timeout, -1 110 Connection timed out
      // REG_ADC_CONTROL_2 = 0x34   | // aclkIsInternal = true         | McASP error, abort
                                    | // wclkIsInternal = false        |
      ________________________________________________________________________________________________
      master          master        |  internal        internal        | sound drowned out by noise
      // REG_ADC_CONTROL_2 = 0x7C   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      slave           master        |  internal        internal        | sound drowned out by noise
      // REG_ADC_CONTROL_2 = 0x6C   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      master          slave         |  internal        internal        | sound drowned out by noise
      // REG_ADC_CONTROL_2 = 0x74   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________
      slave          slave          |  internal        internal        | sound drowned out by noise
      // REG_ADC_CONTROL_2 = 0x34   | // aclkIsInternal = true         |
                                    | // wclkIsInternal = true         |
      ________________________________________________________________________________________________

      I marked one of your last suggestions (REG_ADC_CONTROL_2 = 0x6C, aclkIsInternal = false, wclkIsInternal = true as no output as I am in a different work location right now and do not have the same input gain on my digital interface here. It might be that the output was still there, drowned out by noise like in the last 4 entries in the table (which I tested earlier in the week).

      So although I am happy that there are 2 combinations which work fine so far, I am not sure that I understand how it all works. I don't understand how the AD1938 wclock in REG_ADC_CONTROL_2 can be in master mode, but the wclock for McASP be generated internally (by the BBB, right?). Well, this is all assuming that LRCLK on the AD1938 is connected to the BBB wclock.

      But I also see that we interpret REG_ADC_CONTROL_2 values differently, for example value 0x74 means bclock = master and wclock = slave in my version, your comment in the diff above has them the other way around.

      All this being said, I believe that I got somewhere workable right now, but I am not sure why 😃

      Enjoy the weekend!

        cristian_pandele But I also see that we interpret REG_ADC_CONTROL_2 values differently, for example value 0x74 means bclock = master and wclock = slave in my version, your comment in the diff above has them the other way around.

        Yup I had them wrong. Now I fixed them and the patch with 0x74 and

        aclkIsInternal = false;
        wclkIsInternal = true;

        seems to be the one that works for you and makes sense.

        It's not 100% clear to me on the CTAG which way the clocks go. I think there's a crystal connected to the AD1938 which generates its MCLK, then the AD1938 generates a BCLK which is sent to the BBB's aclk. So in that sense you can expect all aclkIsInternal attempts to fail badly because there'll be two competing BCLKs.

        As for why this works ...

        master          master        |  external        internal        | noise gone, all good
        // REG_ADC_CONTROL_2 = 0x7C   | // aclkIsInternal = false        |
                                      | // wclkIsInternal = true         |

        it may just be luck ? Maybe the external clock is gated while idle and then it's started at the right time that they are both producing the same (or compatible) wclk at the same time and somehow that works while each one is only taking into consideration its own? That is of course very fragile as any transient clock error would result in a permanent out-of-sync.

        Thanks for keeping up with me and sorry for forcing you to brute force this. Unfortunately I didn't have the necessary hardware to try it out myself.

        Hey Giulio,

        No worries, I am the one who should thank you! It was actually quite easy starting from your last suggestion. I got it working in the second try, with REG_ADC_CONTROL_2 = 0x74. But then I changed it to 0x7C and it still worked. That confused me and I hope that going through all combinations would be helpful to someone (me included).

        Agreed with your assessment, definitely looks like REG_ADC_CONTROL_2 = 0x7C works due to a sequence of unrelated events and it is not guaranteed to work over longer durations (or at every boot). I also updated above to indicate that future people should not try that combination.

          cristian_pandele

          cristian_pandele Agreed with your assessment, definitely looks like REG_ADC_CONTROL_2 = 0x7C works due to a sequence of unrelated events and it is not guaranteed to work over longer durations (or at every boot). I also updated above to indicate that future people should not try that combination.

          What one could do to verify that is to short the frame clock line P9.29 to ground: if everything keeps going unscathed (at least on the leader cape, the follower one will be affected), then you know the answer!

          25 days later

          Hi,

          Late reply, hijacking my own thread here 🙂

          I didn't get to try shorting the clock line to add to the findings here, but I ran into another little puzzle.

          I am trying to get 16 inputs working (8 audio inputs from a CTAG BEAST + 8 analog ins from an Audio Expander capelet), in a stack like this:

          • CTAG
          • CTAG
          • Bela
          • Audio Expander capelet

          However, there is no sound coming in from the Audio Expander capelet. There is a little pop at boot, then dead silence. If I remove the CTAG BEAST from the stack, I can read all 8 inputs the capelet offers (I did set the jumpers on J11 in advance of trying).

          This shouldn't be related to the clock changes above, but I thought to post here to keep this in context.

          As always, any pointers would be greatly appreciated!

          Cheers,
          Cristian

          Yes shouldn't be related to any of the changes above. Try only CTAG+BELA and see if you can receive any signal into the analog inputs.

          Hi Giulio,

          Thank you for the quick reply!

          Sorry, wrote the message too quickly and didn't provide all info. All trials I did used the same code, reading analog in channels 0-7. Analog is set to enabled in the IDE settings, Audio Expansion capelet is also enabled and input channels 0-7 are all on. Output channels are disabled on the capelet.

          I need to try out removing the capelet (both physically and in the config) and reading the analog pins off the Bela. Didn't try that out as it looks like the CTAGS being present interfere with the analog pin enablement. But I will try this out tomorrow (Wednesday) evening, just to make sure.

          Thank you once again!
          Cristian