Friday, April 7, 2017

WCE7 : Read CPU registers on BeagleBone

Sometimes, it may be useful to watch the CPUs registers to confirm that a peripheral is configured properly or to monitor a signal change from the CPU point of view.
Here are some hints to achieve that easily.

Monitor MPU registers

MPU is the central processing unit in charge of interrupt handling. To read its registers, use the following code:
#include "am33x_irq.h" 
#include <am33x_interrupt_struct.h>

// Interrupt context
static AM33X_INTR_CONTEXT  s_intr;
AM33X_INTR_CONTEXT const *g_pIntr = &s_intr;

void somefunction(void)
{
   UINT32                        irqSIR, irqINTC_ITR_3, irqPENDING_IRQ3;
   PHYSICAL_ADDRESS pa;

   // Get interrupt controller and GPIO registers' virtual uncached addresses
    pa.QuadPart = GetAddressByDevice(AM_DEVICE_MPU);
    s_intr.pICLRegs = (AM33X_INTC_MPU_REGS*)MmMapIoSpace(pa, sizeof(AM33X_INTC_MPU_REGS), FALSE);
    if (s_intr.pICLRegs == NULL)
    {
        DEBUGMSG(1, (L"ERROR: Am3xxGpioInit: Failed map MPU controller registers\r\n"));
    }

    irqSIR = INREG32(&g_pIntr->pICLRegs->INTC_SIR_IRQ);
    irqINTC_ITR_3 = INREG32(&g_pIntr->pICLRegs->INTC_ITR3);
    irqPENDING_IRQ3 = INREG32(&g_pIntr->pICLRegs->INTC_PENDING_IRQ3);

    // Activate IRQs for GPIO0A
    //OUTREG32(&g_pIntr->pICLRegs->INTC_ILR[96], 0U); // Route to IRQ and set max priority
    //OUTREG32(&g_pIntr->pICLRegs->INTC_MIR3, irqMIR3 | 0x1);
}

 

Monitor GPIO registers

#include "am33x_irq.h" 
#include <am33x_interrupt_struct.h>

// Interrupt context
static AM33X_INTR_CONTEXT  s_intr;
AM33X_INTR_CONTEXT const *g_pIntr = &s_intr;

void  somefuncion(void)
{
    PHYSICAL_ADDRESS pa;

    // Bank 0
    pa.QuadPart = GetAddressByDevice(AM_DEVICE_GPIO0);
    s_intr.pGPIORegs[0] = (AM3XX_GPIO_REGS*)MmMapIoSpace(pa, sizeof(AM3XX_GPIO_REGS), FALSE);
    if (s_intr.pGPIORegs[0] == NULL)
    {
        DEBUGMSG(1, (L"ERROR: Am3xxGpioInit: Failed map GPIO0 controller registers\r\n"));
    }
    // Bank 1
    pa.QuadPart = GetAddressByDevice(AM_DEVICE_GPIO1);
    s_intr.pGPIORegs[1] = (AM3XX_GPIO_REGS*)MmMapIoSpace(pa, sizeof(AM3XX_GPIO_REGS), FALSE);
    if (s_intr.pGPIORegs[1] == NULL)
    {
        DEBUGMSG(1, (L"ERROR: Am3xxGpioInit: Failed map GPIO1 controller registers\r\n"));
    }
    // Bank 2
    pa.QuadPart = GetAddressByDevice(AM_DEVICE_GPIO2);
    s_intr.pGPIORegs[2] = (AM3XX_GPIO_REGS*)MmMapIoSpace(pa, sizeof(AM3XX_GPIO_REGS), FALSE);
    if (s_intr.pGPIORegs[2] == NULL)
    {
        DEBUGMSG(1, (L"ERROR: Am3xxGpioInit: Failed map GPIO2 controller registers\r\n"));
    }
    // Bank 3
    pa.QuadPart = GetAddressByDevice(AM_DEVICE_GPIO3);
    s_intr.pGPIORegs[3] = (AM3XX_GPIO_REGS*)MmMapIoSpace(pa, sizeof(AM3XX_GPIO_REGS), FALSE);
    if (s_intr.pGPIORegs[3] == NULL)
    {
        DEBUGMSG(1, (L"ERROR: Am3xxGpioInit: Failed map GPIO3 controller registers\r\n"));
    }
}

OALPAtoUA vs MmMapIoSpace

Both functions achieve the same goal so why use one instead of the other ?
OALxAtoxA functions are reserved for OAL use and can not be called from drivers (if you try, you will end with a link error saying that OALPAtoVA symbol has not been found). This is also why some parts of code use the following code:
#ifdef OAL
   // OALxAtoxA business
#endif
#ifdef DEVICE
  // MmMapIoSpace business
#endif
MmMapIoSpace is the function to use in drivers.

0 comments:

Post a Comment