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.

BBB MMU woes

I am trying to implement MMU bare metal with GCC. 
I am using MMUInit(), MMUMemRegionMap() and MMUEnable()
When I enable IRQ, the bone locks up.  I think just the interrupt controller locks up. I stripped out all the other code of the enetecho example, scrutinized similar problems on the forum, ensured everything runs a la starterware etc...
I am using XDS100V2 jtag.
Ideas anyone?  (like load script, DDR/EMIF config, PLL, how to map regions, linker idiosyncracies)

thx............dd

  • Don,

    Can you please provide the source of your MMU test example. Are you using IntMasterIRQEnable API to enable interrupts. Is the ARM in privileged mode when you turn on the interrupts. Are you linking to the system_config library meant to be used with GCC compiler. you need the init.S and cp15.S files (system_config\armv7a\gcc) linked to your application.

    Regards,
    Rahul
  • Thanks Rahul. u where right, I was using those .s files ported from the TI compiler library.
    Still no joy, but I am working on it. I really need an assembly dump from the ccs6 linker.

    I am always in SYS mode. 

    I do not use IntMasterIRQEnable  api, but coded it in my irq_init routine exactly.  It and all my irq code is proven to work (thusfar). 

    Do you think
    FMXR FPEXEC, r0
    is necessary for mmu stuff? I do not enable the VFP coprocessor. 

    FYI I am doing all this from the command line. 

    I suspect I may not be mapping all the sections correctly.  There was a similar problem in the post:  https://e2e.ti.com/support/arm/sitara_arm/f/791/t/208885

    "In reply to Alankar Dhobale:

    Hi Sujith,

    Finally i got MMU working in my code, the issue was, there was a default FAULT entry added for internal RAM region, and my stack memory was located in internal memory, so i added page table entry for internal RAM region and it worked..."

    but he did not say what he did!


    I will the back to u. I still do not grok the mmu. Does TI have a primer on it? later........dd

  • Hi again Rahul.  Below is the main program and load map.  
    I don't know how to attach a separate file.

    I stripped out all the unnecessary code and used the Starterware APIs.

    //
    // hack mmu/cache/irq test platform
    //
    #include <stdio.h>
    #include <stdint.h>
    #include "bbb_main.h"
    #include "mmu.h"
    #include "cache.h"
    #define START_ADDR_DDR                     (0x80000000)
    #define START_ADDR_DEV                     (0x44000000)
    #define START_ADDR_OCMC                    (0x40300000)
    #define NUM_SECTIONS_DDR                   (512)
    #define NUM_SECTIONS_DEV                   (960)
    #define NUM_SECTIONS_OCMC                  (1)

    static volatile unsigned int pageTable[4*1024] __attribute__((aligned(0x8000)));

    static void MMUConfigAndEnable(void);
    //
    // Function to setup MMU. This function Maps three regions (1. DDR
    // 2. OCMC and 3. Device memory) and enables MMU.
    //

    void MMUConfigAndEnable(void)
    {
        /*
        ** Define DDR memory region of AM335x. DDR can be configured as Normal
        ** memory with R/W access in user/privileged modes. The cache attributes
        ** specified here are,
        ** Inner - Write through, No Write Allocate
        ** Outer - Write Back, Write Allocate
        */
        REGION regionDdr = {
                            MMU_PGTYPE_SECTION, START_ADDR_DDR, NUM_SECTIONS_DDR,
                            MMU_MEMTYPE_NORMAL_NON_SHAREABLE(MMU_CACHE_WT_NOWA,
                                                             MMU_CACHE_WB_WA),
                            MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                            (unsigned int*)pageTable
                           };
        /*
        ** Define OCMC RAM region of AM335x. Same Attributes of DDR region given.
        */
        REGION regionOcmc = {
                             MMU_PGTYPE_SECTION, START_ADDR_OCMC, NUM_SECTIONS_OCMC,
                             MMU_MEMTYPE_NORMAL_NON_SHAREABLE(MMU_CACHE_WT_NOWA,
                                                              MMU_CACHE_WB_WA),
                             MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                             (unsigned int*)pageTable
                            };

        /*
        ** Define Device Memory Region. The region between OCMC and DDR is
        ** configured as device memory, with R/W access in user/privileged modes.
        ** Also, the region is marked 'Execute Never'.
        */
        REGION regionDev = {
                            MMU_PGTYPE_SECTION, START_ADDR_DEV, NUM_SECTIONS_DEV,
                            MMU_MEMTYPE_DEVICE_SHAREABLE,
                            MMU_REGION_NON_SECURE,
                            MMU_AP_PRV_RW_USR_RW  | MMU_SECTION_EXEC_NEVER,
                            (unsigned int*)pageTable
                           };

        /* Initialize the page table and MMU */
        MMUInit((unsigned int*)pageTable);

        /* Map the defined regions */
        MMUMemRegionMap(&regionDdr);
        MMUMemRegionMap(&regionOcmc);
        MMUMemRegionMap(&regionDev);

        /* Now Safe to enable MMU */
        MMUEnable((unsigned int*)pageTable);
    }

    //
    // main
    //
    uint32_t main() {
      uint32_t i=0;

      uart_tx(consoleUART, 0x0A);   // print n!
      uart_tx(consoleUART, 0x6E);
      uart_tx(consoleUART, 0x21);
      uart_tx(consoleUART, 0x0A);

      MMUConfigAndEnable();

      CacheEnable(CACHE_ALL);

      irq_init();

      asm volatile(".word 0xe7f000f0");  // undefined instruction to test UND isr
                                         // if AINTC is alive UND_isr will print "UND" once
      i = 0;
      while (1) {
        while(i < 10000000) i++;
        uart_tx(consoleUART, 0x30);    // print 0
        i = 0;  
      }
    }

    /* memmap */
    MEMORY
    {
       l3_ram : ORIGIN = 0x80000000, LENGTH = 0x7ffffff
    }

    SECTIONS
    {
      .text : {
        *(.text*)
      } > l3_ram
      __data_rom_start__ = .;
      .data : {
        __data_start__ = .;
        *(.data*)
      } > l3_ram
        __data_end__ = .;
        __data_size__ = __data_end__ - __data_start__;
      .bss  : {
        __bss_start__ = .;
        *(.bss*)
      } > l3_ram
      __bss_end__ = .;
      __bss_size__ = __bss_end__ - __bss_start__;

    }

    thanks..............don

  • Hey Rahul, where did u go?
    .....................dd
  • I don't really know much about StarerWare, but I do have the MMU working without problems in baremetal code on a BeagleBone Black. I've actually posted some version of my translation table init code on the ARM community site here.

    It only supports flat section mapping for simplicity. It also assumes your translation table was initially zero-filled, which is normally true for bss, but if you place it in external ram you'll need to zero-fill it after initializing EMIF. I personally place the translation table in internal SRAM.

    It maps both internal and external RAM by default, but it should be easy to tweak the mappings and cache policies.

    Don Lawrence said:
    Do you think 
    FMXR FPEXEC, r0 
    is necessary for mmu stuff? I do not enable the VFP coprocessor.

    VFP/Neon must actually be enabled before you can execute that instruction.

    BTW, beware that if you compile C/C++ code with gcc and the compiler is told VFP is available (which is the default for arm-linux-gnueabihf toolchains for example), then it may emit VFP/Neon instructions even if you don't explicitly make use of floating-point or vectors in your C code. In this case you must either disable fpu support in compiler settings (-msoft-float or -mfloat-abi=soft) or ensure VFP is enabled before any C code executes.

    You can check the effective settings you're using with -Q --help=target, e.g.:

    dev:~$ arm-none-eabi-gcc -Q --help=target | grep -m1 float-abi
      -mfloat-abi=                          soft
    dev:~$ arm-linux-gnueabihf-gcc -Q --help=target | grep -m1 float-abi
      -mfloat-abi=                          hard
    dev:~$ arm-linux-gnueabihf-gcc -msoft-float -Q --help=target | grep -m1 float-abi
      -mfloat-abi=                          soft
    

    In projects that depend on this setting I recommend setting it explicitly rather than relying on defaults.

    Don Lawrence said:
      CacheEnable(CACHE_ALL);

    If there's even the slightest doubt about whether L1D and L2 caches might have been enabled previously, invalidate them before enabling them. You don't want to end up fighting mysterious crashes caused by stale cache data.

    Don Lawrence said:
      asm volatile(".word 0xe7f000f0");  // undefined instruction to test UND isr
                                         // if AINTC is alive UND_isr will print "UND" once

    UND is an exception, not an irq. It is not routed via AINTC.

  • It seems the GCC support of StarterWare is quite poor. From time to time there are similar questions from people who have an application halting at some stranger point although they are using unchanged examples or code which works fine when using TI's ARM compiler. So for me it seems there is something fundamental missing when using GCC...
  • Well last time I looked at StarterWare (which, admittedly, was a while ago) I saw plenty of code that, assuming it worked at al, was relying on luck rather than correctness. At least one example miscompiled out-of-the-box if any sort of optimization was enabled.

  • thanks 4 the feedback dude.  Matthijs 2 the rescue once again.....

    i'll give this a whirl and get back 2 u.............dd

  • When you have some improvements for the StarterWare-code, I think the guys from sourceforge.net/.../ would happily apply a patch to their repository ;-)
  • lol, looks very active... last updated: 2015-06-28.

    examples/beaglebone/uart/uartEcho.c contains a busy-wait on a non-volatile variable which will compile into a deadloop if any optimization is enabled.

    The routines in system_config/armv7a/gcc/cpu.c have lots of problems. A suggested rewrite would be:

    unsigned int CPUIntStatus(void)
    {
    	unsigned int stat;
    	asm( "mrs %[result], CPSR" : [result] "=r"(stat) );
    	return stat & 0xc0;
    }
    
    void CPUirqd(void)
    {
    	asm(	"dsb\n\t"
    		"cpsid	ia" ::: "memory" );
    }
    
    void CPUirqe(void)
    {
    	asm(	"dsb\n\t"
    		"cpsie	ia" ::: "memory" );
    }

    I'm merely retaining the dsb instructions since they were there in the original. I don't think they belong in irq-enable/disable routines but perhaps some callers are relying on it. The asm statements were also using r0 without r0-clobbers, but I simply removed the need for them to use a temporary register. Without proper declaration of clobbers you can expect a bonfire if anyone tries to use -flto. I'm disabling/enabling async aborts alongside irqs since they behave like irqs and moreover are normally automatically disabled when an irq is entered. (The FIQ analogs would use "fia" instead of "ia", but they're not useful on most TI SoCs.)

  • hi Matthijs.  no-go on those fixes, but we r getting warm.  
    u were right about zero filling the pagetable AFTER init emif
    and disab fpu support in gcc
    and invalidating the caches before CacheEnable(CACHE_ALL)

    i used starterware CP15ICacheFlush and CP15DCacheFlush to invalidate.  
    i assume these invalidate L1 & L2 caches.  

    i have been studying your mmu.c code intensively.  very cute.  
    i definitely do not grok this cache business.  
    i have been studing ARM1156T2F-S trm called DDI0290G, the section on MPU.  
    is this not correct?  

    execution halts when i enab interrupts, before an interrupt occurs and
    before accessing memory.  so the problem must be in the mmu configuration.  

    please give me your thoughts on this...................dd

  • Don Lawrence said:
    i used starterware CP15ICacheFlush and CP15DCacheFlush to invalidate.  
    i assume these invalidate L1 & L2 caches.

    Well I wouldn't assume that, but after examining the code it appears CP15DCacheFlush indeed means "invalidate L1D and L2 caches".

    Don Lawrence said:
    i have been studing ARM1156T2F-S trm called DDI0290G, the section on MPU.  
    is this not correct?

    Uh, no. Wrong cpu. For the CPU-specifics grab the Cortex-A8 TRM, though all the general details are in the ARM v7-A/R Architecture Reference Manual. This is not a particularly easy document to read unfortunately.

    Don Lawrence said:
    please give me your thoughts on this

    I don't really have any, your problem statement is too vague and there is little context available.

    If with "enab interrupts" you mean "enable the MMU" then I'd assume you're getting prefetch aborts but I have no idea why. You can try testing the memory view of the cortex-A8 via JTAG when the core is halted (using e.g. the memory browser in CCS).

  • I enab the MMU, execution continues until I enab IRQ then it freezes. 

    I have spent 3 months on this alone.  I am probably close, but I am running out of ideas. 

    How much do you want for your code?

  • Hi Matthijs

    Thanks for the advice.  I can now load and run my bare-metal image from CCS/XDS100 and examine the registers. 
    (nice trick btw, I never did that b4)
    An Abort exception occurs.  Here are the registers before and after:

    SPSR_ABT  0x00000000
    R13_ABT   0x8101080C      (SP)
    R14_ABT   0x00000000      (LR)
    ====================
    SPSR_ABT  0x60000197  - CPSR = zero|carry  (no big deal) 
                            I bit set (strange, I did not mask it) and ABT mode 10111
    R13_ABT   0x8101080C  - no change
    R14_ABT   0x00020010  - ROM exception vector Data Abort address
                            The D_ABT service routine did not execute. 

    I don't think I set up my memory regions correctly. 

    thanks..............dd

  • BINGO!

    When the first interrupt occurs, execution jumps to the ROM exception vector,

    which way down at address 0x20000.  I did not map that region, which caused

    the DATA ABORT exception.  To fix that, just add the following to the main program:

    #define START_ADDR_ROM                     (0x00020000)
    #define NUM_SECTIONS_ROM                   (1)

    In routine MMUConfigAndEnable add:

        // Define ROM region of AM335x. Same Attributes of DDR region given.
        REGION regionROM = {
                             MMU_PGTYPE_SECTION, START_ADDR_ROM, NUM_SECTIONS_ROM,
                             MMU_MEMTYPE_NORMAL_NON_SHAREABLE(MMU_CACHE_WT_NOWA,
                                                              MMU_CACHE_WB_WA),
                             MMU_REGION_NON_SECURE, MMU_AP_PRV_RW_USR_RW,
                             (unsigned int*)pageTable
                            };

    and

        MMUMemRegionMap(&regionROM);

    just like the other mappings.  The starterware code does not do this, so they must disable

    the ROM exception vector somehow.  But this will do the job for now.

    Thanks Matthijs, I couldn't have done this without you.  btw, I was gonna pay u $1000 for your code. 

    later, happy hacking.........dd