C64x+ DSP MMU faults, and how to disable the MMU.

Two days ago, while testing some image processing algorithms on the DSP I got the following message for the first time:


DSP MMU Error Fault!  MMU_IRQSTATUS = [0x1]. Virtual DSP addr reference that generated the interrupt = [0x85000000].

Outch!

I was aware that the DSP of the OMAP3530 has a memory management unit, but so far I never had to deal with it. Dsplink initialized the MMU and enabled access to all the DSP memory and all peripherals I accessed so far.

However, this time I passed a pointer to a memory block allocated via CMEM to the DSP. This triggered a page fault. Now what? I did some research and figured out that the CodecEngine enables access to the CMEM memory. I don’t use the CodecEngine, so I have to do it on my own.

Fortunately the TI folks have thought about that! The PROC module has functionality to modify the DSP MMU entries. Here are some ready to use functions:

int dsp_mmu_map (unsigned long physical_ptr, int size)
///////////////////////////////////////////////////////////
// Maps a physical memory region into the DSP address-space
{
  ProcMemMapInfo mapInfo;
  mapInfo.dspAddr = (Uint32)physical_ptr;
  mapInfo.size    = size;
  return DSP_SUCCEEDED(PROC_control(0, PROC_CTRL_CMD_MMU_ADD_ENTRY, &mapInfo));
 }


int dsp_mmu_unmap (unsigned long dspAddr, int size)
//////////////////////////////////////////////////////////////
// Unmaps a physical memory region into the DSP address-space
{
  ProcMemMapInfo mapInfo;
  mapInfo.dspAddr = (Uint32)dspAddr;
  mapInfo.size = size;
  return DSP_SUCCEEDED(PROC_control(0, PROC_CTRL_CMD_MMU_DEL_ENTRY, &mapInfo));
}

All nice and dandy now? No, it’s not. You can only map a limited number of memory regions (around 32 I think). That’s not enough for my needs. Also I don’t feel like tracking memory regions and swap them as needed. So the way CodecEngine does it is probably better: Enable access to the whole CMEM address-space: These two functions do this, but without CodecEngine:

int dsp_mmu_map_cmem (void)
/////////////////////////////////////////
// map first cmem block into the DSP MMU:
{
  CMEM_BlockAttrs info;

  if (CMEM_getBlockAttrs(0, &info) != 0)
    return 0;

  return dsp_mmu_map (info.phys_base, info.size);
}


int dsp_mmu_unmap_cmem (void)
//////////////////////////////////////////
// umap first cmem block into the DSP MMU:
{
  CMEM_BlockAttrs info;

  if (CMEM_getBlockAttrs(0, &info) != 0)
    return 0;

  return dsp_mmu_unmap (info.phys_base, info.size);
}

All problems solved. Great!

I could have stopped here, but I was eager to know if the MMU has any impact on the memory throughput. Is it possible to completely disable the MMU? Sure, this opens a can of worms. A bug in my code or a wrong DMA transfer could write to nearly any location. It could even erase the flash. But on the DaVinci I didn’t had a MMU and I never run into such problems. So I did some research, and:

It is simple!

The MMU has a disable bit, and the TRM sais that you have to do a soft-reset of the MMU if you fiddle with the settings. I gave it a try and it worked on the first try! You don’t even need a kernel-module for it. The following code will do all the magic from linux-user mode under the restriction that you need read and write access to /dev/mem.

Call this between PROC_load and PROC_start:

int dsp_mmu_disable (void)
//////////////////////////
// Disables the DSP MMU.
// Sets virtual = physical mapping.
{
  volatile unsigned long * mmu;
  int result = 0;

  // physical addres of the MMU2 and some register offsets:
  const unsigned MMU2_PHYSICAL = 0x5d000000;
  const unsigned MMU_SYSCONFIG = 4;
  const unsigned MMU_SYSSTATUS = 5;
  const unsigned MMU_CNTL      = 17;

  // needs Read+Write access to /dev/mem, so you'll better run this as root.
  int fd = open("/dev/mem", O_RDWR);

  if (fd>=0)
  {
    // get a pointer to the MMU2 register space:
    mmu = (unsigned long *) mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMU2_PHYSICAL);

    if (mmu != MAP_FAILED)
    {
      time_t start;

      // A timeout of 10 milliseconds is more then plenty.
      //
      // Usually the reset takes about 10 microseconds.
      // It never happend to me that the reset didn't
      // succeded, but better safe than sorry.
      time_t timeout = (CLOCKS_PER_SEC/100);

      // start MMU soft-reset:
      mmu[MMU_SYSCONFIG] |= 1;

      // wait (with timeout) until the reset is complete.
      start = clock();
      while ((!mmu[MMU_SYSSTATUS]) && (clock()-start < timeout)) {}

      if (mmu[MMU_SYSSTATUS])
      {
        // disable MMU
        mmu[MMU_CNTL] =0;
        
        // set result to SUCCESS.
        result = 1;
      }
      // remove mapping:
      munmap((void*)mmu, 4096);
    }
    close (fd);
  }

  // failed:
  return result;
}

And to answer my own question: No, the MMU does not has any negative impact on the performance. Also the MMU tables reside in the DDR2 memory, the pages are so large that the extra memory traffic for the MMU table-walks can’t even be measured.

Btw – I’ve made a little easy to use library out of the above functions and I’ll release them under the BSD license, so everyone can use it. Get it here: dsp_mmu_util.tgz

The next question would be: Can the DSP jail-break and disable it’s own MMU? That would be of little practical use but interesting to know..

This entry was posted in Beagleboard, DSP, Linux, OMAP3530. Bookmark the permalink.

2 Responses to C64x+ DSP MMU faults, and how to disable the MMU.

  1. Yan Huichen says:

    Helloļ¼I came across the MMU problem U mentioned. But I did not solve it by the calling of dsp_mmu_map_cmem(). So I tried dsp_mmu_disable(). I read and write a char from the addr 0x40166000, and only after that, my hypertrm did not work. I guess it is the flash problem, but how to solve it, could you give me a hand?
    THANKS A MILLION TIMES!!

  2. bsp says:

    Hi Nils,

    I ran into the same issues as you.

    You can simply disable the MMU (like on DRA44x/DM446/DRx45x — good ol’ times, eh :-))

    e.g. like this:

    dsp_reg_bit_clear(IVA2_MMU_CNTL, 1);

    w/

    #define IVA2_MMU_BASE (0x5d000000u)
    #define IVA2_MMU_CNTL (IVA2_MMU_BASE + 0x00000044u)

    Greetz,
    Bastian

Leave a Reply

Your email address will not be published. Required fields are marked *