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.

SK-AM62-LP: Enabling the second serializer in McASP

Part Number: SK-AM62-LP
Other Parts Discussed in Thread: SYSCONFIG

Tool/software:

Hi,

I'm writing an application using the McASP driver.

I managed to configure the MCASP0 driver instance to transmit 8-slot/16-bit TDM frames at the rate of 48 kHz via a single serializer (either AXR0 or AXR1):

- AXR0:

- AXR1:

The problem is that when I enable 2 serializers (i.e., AXR0 and AXR1) and run the very same application I cannot see any data on the corresponding serial data pins - I can only see the serial bit clock signal:

I'd like to know how to enable 2 serializers so that they can can service data on both pins at the same time.

Is adding the second serializer in SysConfig enough?

Do I need to send more data?

Is there any exemplary application implementing a TDM multi-slot multi-serializer case available?

Plase find below the application code and SysConfig settings:

- application:

#include "mcasp_util.h"

#include "ti_drivers_config.h"

#include <FreeRTOS.h>
#include <drivers/mcasp.h>
#include <kernel/dpl/CacheP.h>
#include <kernel/dpl/DebugP.h>
#include <task.h>

#include <stdint.h>
#include <string.h>

#define SLOT_COUNT               (8u)
#define SLOT_SIZE_IN_B           (4u)
#define FRAME_COUNT              (2u)
#define FRAME_SIZE_IN_B          (SLOT_COUNT * SLOT_SIZE_IN_B)
#define DATA_SIZE_IN_B           (FRAME_COUNT * FRAME_SIZE_IN_B)
#define LOOPJOB_BUFFER_SIZE_IN_B (FRAME_SIZE_IN_B)

uint8_t txBuffers[FRAME_COUNT][FRAME_SIZE_IN_B] __attribute__((aligned(256)));

MCASP_Transaction txTransactions[FRAME_COUNT] = {};

uint32_t volatile txCallbackCallCount = 0u;

extern uint8_t txLoopjobBuffer[];

static void fillTxBuffers(uint8_t (* const buffer)[FRAME_COUNT][FRAME_SIZE_IN_B])
{
    for (uint32_t i = 0u; i < FRAME_COUNT; ++i)
    {
        for (uint32_t j = 0u; j < FRAME_SIZE_IN_B; ++j)
        {
            (*buffer)[i][j] = (FRAME_SIZE_IN_B * i + j) % 256u;
        }
    }
}

static void fillLoopjobBuffer(
    uint8_t (* const buffer)[LOOPJOB_BUFFER_SIZE_IN_B],
    uint32_t const value)
{
    for (uint32_t i = 0u; i < LOOPJOB_BUFFER_SIZE_IN_B; i += 4u)
    {
        *((uint32_t*)(&((*buffer)[i]))) = value;
    }
}

static void submitBuffers(
    MCASP_Handle const handle,
    MCASP_Transaction (* const transactions)[FRAME_COUNT],
    uint8_t (* const buffers)[FRAME_COUNT][FRAME_SIZE_IN_B],
    int32_t (* const callback)(MCASP_Handle, MCASP_Transaction*))
{
    for (uint32_t i = 0u; i < FRAME_COUNT; ++i)
    {
        (*transactions)[i].buf = &(*buffers)[i];
        (*transactions)[i].count = FRAME_SIZE_IN_B / SLOT_SIZE_IN_B;
        (*transactions)[i].timeout = 0xFFFFFFFFu;

        callback(handle, &(*transactions)[i]);
    }
}

static void awaitTransferCompletion(void)
{
    vTaskDelay(pdMS_TO_TICKS(100u));
}

static void withdrawBuffers(
    MCASP_Handle const handle,
    MCASP_Transaction* (* const callback)(MCASP_Handle))
{
    while (callback(handle) != NULL)
    {
    }
}

void mcaspMain(void* args)
{
    MCASP_Handle handle;

    fillTxBuffers(&txBuffers);
    CacheP_wb(&txBuffers, DATA_SIZE_IN_B, CacheP_TYPE_ALL);

    fillLoopjobBuffer(&txLoopjobBuffer, 0xFFFFFFFFu);
    CacheP_wb(&txLoopjobBuffer, LOOPJOB_BUFFER_SIZE_IN_B, CacheP_TYPE_ALL);

    handle = MCASP_getHandle(CONFIG_MCASP0);
    DebugP_assert(handle != NULL);

    submitBuffers(
        handle,
        &txTransactions,
        &txBuffers,
        &MCASP_submitTx
    );

    DebugP_assert(MCASP_startTransferTx(handle) == SystemP_SUCCESS);

    awaitTransferCompletion();

    MCASP_stopTransferTx(handle);

    withdrawBuffers(handle, &MCASP_withdrawTx);
}

void runOnTransmittingData(
    MCASP_Handle handle,
    MCASP_Transaction* const transaction)
{
    ++txCallbackCallCount;
}

- SysConfig settings:

/**
 * These arguments were used when this file was generated. They will be automatically applied on subsequent loads
 * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
 * @cliArgs --device "AM62x" --package "AMC" --part "Default" --context "a53ss0-0" --product "MCU_PLUS_SDK_AM62x@09.02.01"
 * @versions {"tool":"1.20.0+3587"}
 */

/**
 * Import the modules used in this configuration.
 */
const mcasp      = scripting.addModule("/drivers/mcasp/mcasp", {}, false);
const mcasp1     = mcasp.addInstance();
const debug_log  = scripting.addModule("/kernel/dpl/debug_log");
const mmu_armv8  = scripting.addModule("/kernel/dpl/mmu_armv8", {}, false);
const mmu_armv81 = mmu_armv8.addInstance();
const mmu_armv82 = mmu_armv8.addInstance();

/**
 * Write custom configuration values to the imported modules.
 */
mcasp1.$name                                   = "CONFIG_MCASP0";
mcasp1.txHclkSourceMux                         = 2;
mcasp1.rxHclkSourceMux                         = 2;
mcasp1.txCallbackFxn                           = "runOnTransmittingData";
mcasp1.rxCallbackFxn                           = "runOnReceivingData";
mcasp1.txLoopjobBuf                            = "txLoopjobBuffer";
mcasp1.rxLoopjobBuf                            = "rxLoopjobBuffer";
mcasp1.rxActiveSlotMask                        = 0x3;
mcasp1.rxDataMask                              = 0xFFFFFFFF;
mcasp1.enableLoopback                          = false;
mcasp1.clkSyncMode                             = "ASYNC";
mcasp1.enableMcaspRx                           = false;
mcasp1.TxMode                                  = "TDM";
mcasp1.NumTxSlots                              = 8;
mcasp1.txAfifoEnable                           = false;
mcasp1.txActiveSlotMask                        = 0xFF;
mcasp1.txFsPolarity                            = 0;
mcasp1.txBitClkPolarity                        = 0;
mcasp1.txDataDelay                             = 0;
mcasp1.TxSlotSize                              = 16;
mcasp1.txDataMask                              = 0xFF000000;
mcasp1.txLoopjobBufLength                      = 16;
mcasp1.MCASP.$assignAllowConflicts             = "MCASP0";
mcasp1.mcaspSer.create(2);
mcasp1.mcaspSer[0].$name                       = "CONFIG_MCASP_SER0";
mcasp1.mcaspSer[0].MCASP.$assignAllowConflicts = "MCASP0";
mcasp1.mcaspSer[1].$name                       = "CONFIG_MCASP_SER1";
mcasp1.mcaspSer[1].serNum                      = 1;
mcasp1.mcaspSer[1].MCASP.$assignAllowConflicts = "MCASP0";
scripting.suppress("Resource conflict,MCASP0 is also in use by @@@.+?@@@, @@@.+?@@@", mcasp1.MCASP, "$assign");
scripting.suppress("Resource conflict,MCASP0 is also in use by @@@.+?@@@, @@@.+?@@@", mcasp1.mcaspSer[0].MCASP, "$assign");
scripting.suppress("Resource conflict,MCASP0 is also in use by @@@.+?@@@, @@@.+?@@@", mcasp1.mcaspSer[1].MCASP, "$assign");

const udma         = scripting.addModule("/drivers/udma/udma", {}, false);
const udma1        = udma.addInstance({}, false);
udma1.$name        = "CONFIG_UDMA0";
mcasp1.bcDmaDriver = udma1;

const udma2         = udma.addInstance({}, false);
udma2.$name         = "CONFIG_UDMA1";
mcasp1.pktDmaDriver = udma2;

debug_log.enableUartLog        = true;
debug_log.uartLog.$name        = "CONFIG_UART_CONSOLE";
debug_log.uartLog.UART.$assign = "USART0";

mmu_armv81.size  = 0x80000000;
mmu_armv81.$name = "SOC_MEM_REGION";

mmu_armv82.vAddr     = 0x80000000;
mmu_armv82.pAddr     = 0x80000000;
mmu_armv82.size      = 0x80000000;
mmu_armv82.attribute = "MAIR7";
mmu_armv82.$name     = "DDR_REGION";

/**
 * Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
 * version of the tool will not impact the pinmux you originally saw.  These lines can be completely deleted in order to
 * re-solve from scratch.
 */
mcasp1.MCASP.AFSX.$suggestSolution             = "MCASP0_AFSX";
mcasp1.MCASP.ACLKX.$suggestSolution            = "MCASP0_ACLKX";
mcasp1.SYSTEM.$suggestSolution                 = "SYSTEM0";
mcasp1.mcaspSer[0].MCASP.AXR0.$suggestSolution = "MCASP0_AXR0";
mcasp1.mcaspSer[1].MCASP.AXR1.$suggestSolution = "MCASP0_AXR1";
debug_log.uartLog.UART.RXD.$suggestSolution    = "UART0_RXD";
debug_log.uartLog.UART.TXD.$suggestSolution    = "UART0_TXD";

BR,

Maciek

  • Greetings Maciek,

    Thanks for your question.

    I can see the SysConfig file attached by you. So I am going to move forward by assuming you are using MCU PLUS SDK for your development.

    Please correct my understanding on the same.

    Regards,

    Vaibhav

  • Hi Vaibhav,

    Thank you for your reply.

    Yes, I'm using MCU-PLUS-SDK-AM62X.

    FYI, I noticed that after changing the mode of transfer from DMA to Interrupt in SysConfig I can see data on both the serial data pins (AXR0 and AXR1).

    It looks like McASP v1 does not support multi-serializer DMA-based configuration - mcasp_dma.c does not take into account all the possible audio buffer formats (MCASP_AUDBUFF_FORMAT_MULTISER_MULTISLOT_SEMI_INTERLEAVED_1 and MCASP_AUDBUFF_FORMAT_MULTISER_MULTISLOT_SEMI_INTERLEAVED_2).

    On the other hand, McASP v0 used to support them.

    Unfortunatelly, I cannot use the previous driver version due to some missing dependencies (e.g., no edma.h file).

    Is it possible to use DMA along with multiple Tx serialziers?

    BR,

    Maciek

  • Greetings Maciej,

    Thank you for your response.

    Let me assign you another expert on this.

    Regards,

    Vaibhav

  • Hi Vaibhav,

    Do you, by any chance, have some exemplary application using McASP to transmit data over multiple serializers?

    I'm interested in a UDMA-based application, not the EDMA-based one.

    BR,

    Maciek

  • Greetings Maciej,

    Thanks for your response.

    Do you, by any chance, have some exemplary application using McASP to transmit data over multiple serializers?

    Multiple serializers are there only in AM62D.

    FYI, all other devices have only one serializers connected to the codec.

    Regards,

    Vaibhav

  • Hi Vaibhav,

    I tweaked my eval board to enable using more serializers.

    Either way, I was able to transmit data over 2 of them after enabling AFIFO.

    BR,

    Maciek

  • Hello Maciej,

    Either way, I was able to transmit data over 2 of them after enabling AFIFO.

    That is good to hear.

    I tweaked my eval board to enable using more serializers.

    Can you share the steps?

    Regards,

    Vaibhav

  • Hello Vaibhav,

    When it comes to modifying the board, I removed R348 (0 Ohm) to make some space for attaching a wire to pass the MCASP0 instance the

    FSYNC signal coming from an external clock master.

    I also removed R297 (10k Ohm) to decouple AXR1 from VCC3V3_EXP.

    It took me a while to figure out that, apparently, one needs to enable AFIFO to transmit data using more that one serializer.

    I patched the driver to lift that requirement by using information on active serializers:

    diff --git a/source/drivers/mcasp/v1/mcasp_dma.c b/source/drivers/mcasp/v1/mcasp_dma.c
    index e169eda..92a0164 100644
    --- a/source/drivers/mcasp/v1/mcasp_dma.c
    +++ b/source/drivers/mcasp/v1/mcasp_dma.c
    @@ -74,6 +74,8 @@ static void MCASP_udmaTrInit(uint8_t *pTrpd, uint64_t pBuf, uint32_t bufSize);
     static void MCASP_udmaHpdInit(Udma_ChHandle chHandle, uint8_t *pHpdMem,
                                   const void *destBuf, uint32_t length);
     
    +static int32_t configureTxPdmaChannel(MCASP_Object* const driverObject);
    +
     /* ========================================================================== */
     /*                          Function Definitions                              */
     /* ========================================================================== */
    @@ -461,28 +463,9 @@ int32_t MCASP_enableDmaTx(MCASP_Config *config)
     
         if(status == SystemP_SUCCESS)
         {
    -        Udma_ChPdmaPrms pdmaPrms;
             Udma_ChHandle txChHandle = obj->dmaChCfg->txChHandle;
     
    -        /* Config PDMA channel */
    -        UdmaChPdmaPrms_init(&pdmaPrms);
    -        pdmaPrms.elemSize = UDMA_PDMA_ES_32BITS;
    -
    -        /* Number of words received in each transfer */
    -        if(obj->txFifoEnable == 1)
    -        {
    -            pdmaPrms.elemCnt = MCASP_PDMA_ELEM_CNT_WITH_FIFO;
    -        }
    -        else
    -        {
    -            pdmaPrms.elemCnt = 1;
    -        }
    -        pdmaPrms.fifoCnt = 0U;
    -
    -        pdmaPrms.acc32 = 1U;
    -        pdmaPrms.burst = 1U;
    -
    -        status = Udma_chConfigPdma(txChHandle, &pdmaPrms);
    +        status = configureTxPdmaChannel(obj);
             DebugP_assert(SystemP_SUCCESS == status);
     
             status = Udma_chEnable(txChHandle);
    @@ -987,3 +970,48 @@ static void MCASP_udmaIsrRx(Udma_EventHandle eventHandle, uint32_t eventType, vo
             Udma_ringSetDoorBell(Udma_chGetFqRingHandle(rxChHandle), txnPushed);
         }
     }
    +
    +static int32_t configureTxPdmaChannel(MCASP_Object* const driverObject)
    +{
    +    Udma_ChPdmaPrms pdmaParameters;
    +
    +    UdmaChPdmaPrms_init(&pdmaParameters);
    +
    +    // This field specifies how much data is transferred in each write
    +    // which is performed by the DMA.
    +    pdmaParameters.elemSize = UDMA_PDMA_ES_32BITS;
    +
    +    // This field specifies how many elements to transfer each time a trigger
    +    // is received on the channel.
    +    // Its value depends on whether the Write FIFO (WFIFO) is enabled or not.
    +    // The WFIFO is 64-word long (1 word consists of 32 bits).
    +    bool const wfifoEnabled = driverObject->txFifoEnable == 1u;
    +    if (wfifoEnabled)
    +    {
    +        // When it's enabled, each transmit DMA request results in fetching
    +        // 32 words from the DMA so that the WFIFO becomes half full.
    +        // There can be up to 16 serializer in use so loading 32 words means
    +        // queuing 2 samples per each one of them.
    +        pdmaParameters.elemCnt = MCASP_PDMA_ELEM_CNT_WITH_FIFO;
    +    }
    +    else
    +    {
    +        // When it's disabled, the DMA needs to provide data for all
    +        // the active serializers to avoid the transmit underrun error.
    +        uint8_t const serializerCount = driverObject->XmtObj.serCount;
    +        pdmaParameters.elemCnt = serializerCount;
    +    }
    +
    +    // This field is irrelevant to TX and hence set to 0.
    +    pdmaParameters.fifoCnt = 0U;
    +
    +    // This field, when set, enables 32-bit access mode.
    +    // On a 32-bit PDMA, all accesses will have XCNT set to 4
    +    // to support legacy IP that is not fully VBUSP compliant.
    +    pdmaParameters.acc32 = 1U;
    +
    +    // This field, when set, enables VBUSP burst mode on the channel.
    +    pdmaParameters.burst = 1U;
    +
    +    return Udma_chConfigPdma(driverObject->dmaChCfg->txChHandle, &pdmaParameters);
    +}
    

    Do you happen to know why that was expected in the first place?

    What's the reason behind using 32-word long WFIFO?

    Is it because of the total number of available serializers (16 serializers, 2 samples / serializer).

    BR,

    Maciek

  • Hello Maciej,

    Thanks for stating the adjustments you made to make this work.

    Do you happen to know why that was expected in the first place?

    What's the reason behind using 32-word long WFIFO?

    Is it because of the total number of available serializers (16 serializers, 2 samples / serializer)

    You can expect responses on your open question by next week.

    Regards,

    Vaibhav