Home > OS >  arm cortex-m33 (trustzone, silabs efm32pg22) - assembler hardfaults accessing GPIO or almost any per
arm cortex-m33 (trustzone, silabs efm32pg22) - assembler hardfaults accessing GPIO or almost any per

Time:10-23

I am just lost here with this code trying to configure on baremetal the silicon labs efm32pg22 in theirs devkit accessed through internal J-Link from segger studio (great fast ide) - I have such example blink hello world in C working from theirs simplicity studio, but was trying to achieve the same thing I did on microchip pic32 mc00 or samd21g17d easily in pure assembler, having only clocks and startup configured through gui in mplab x... well, here I tried to go to segger IDE where is NO startup/clocks config easy way, or I didnt found it yet. On hardware level, registers of such cortex beasts are different by manufacturer, in C/C there is some not cheap unification over cmsis - but I want only to know what minimal is needed to just have working raw GPIO after clock/startup ... Segger project is generic cortex-m for specific efm32pg22 so cortex-M33 with trust-zone security - I probably dont know what all is locked or switched off or in which state MCU is, if privileged or nonprivileged - there are 2 sets of registers mapping, but nothing works. As far as I try to "store" or even "load" on GPIO config registers (or SMU regs to query someting too) it is throw hardfault exception. All using segger ide debugger over onboard j-link. Kindly please, what I am doing wrong, whats missing here?

in C, I have only this code:

extern void blink(void);

int main ( void )
{
    blink();
}

In blink.s I have this:

            ;@https://github.com/hubmartin/ARM-cortex-M-bare-metal-assembler-examples/blob/master/02 - Bare metal blinking LED/main.S
            ;@https://sites.google.com/site/hubmartin/arm/arm-cortex-bare-metal-assembly/02---arm-cortex-bare-metal-assembly-blinking-led
            ;@https://mecrisp-stellaris-folkdoc.sourceforge.io/projects/blink-f0disco-gdbtui/doc/readme.html
            ;@https://microcontrollerslab.com/use-gpio-pins-tm4c123g-tiva-launchpad/


            ;@!!! ENABLE GPIO CLOCK SOURCE ON EFM32 !!!
            ;@https://community.silabs.com/s/share/a5U1M000000knsWUAQ/hello-world-part-2-create-firmware-to-blink-the-led?language=en_US
            
            ;@EFM32 GPIO 
            ;@https://www.silabs.com/documents/public/application-notes/an0012-efm32-gpio.pdf

            ;@ ARM thumb2 ISA
            ;@https://www.engr.scu.edu/~dlewis/book3/docs/ARM_and_Thumb-2_Instruction_Set.pdf
            ;@https://sciencezero.4hv.org/index.php?title=ARM:_Cortex-M3_Thumb-2_instruction_set
            ;@!!! https://stackoverflow.com/questions/48561243/gnu-arm-assembler-changes-orr-into-movw

            ;@segger assembler
            ;@https://studio.segger.com/segger/UM20006_Assembler.pdf
            ;@https://www.segger.com/doc/UM20006_Assembler.html


            ;@!!! unfortunatelly, we dont know here yet how to include ASM SFR defines, nor for MPLAB ARM (Harmony) !!!
            ;@#include <xc.h>
            ;@#include "definitions.h"
            
            
            .cpu cortex-m33
            .thumb
            
            .text
            .section        .text.startup.main,"ax",%progbits
            .balign         2
            .p2align        2,,3
            
            .global         blink
            //.arch           armv8-m.base
            .arch           armv6-m
            .syntax         unified
            .code           16
            .thumb_func
            .fpu            softvfp
            .type           blink, %function
            

            //!!! here we have manually entered GPIO PORT defines for PIC32CM 
            .equ SYSCFG_BASE_ADDRESS,    0x50078000
            .equ SMU_BASE_ADDRESS,      0x54008000
            //.equ SMU_BASE_ADDRESS,      0x5400C000
            .equ CMU_BASE_ADDRESS,      0x50008000
            .equ GPIO_BASE_ADDRESS,     0x5003C000      // this differs totally from both "special" infineon and microchip "standard?" cortex devices !!!
            
            .equ DELAY,                 40000


            // Vector table
            .word               0x20001000      // Vector #0 - Stack pointer init value (0x20000000 is RAM address and 0x1000 is 4kB size, stack grows "downwards")
            .word               blink           // Vector #1 - Reset vector - where the code begins
                                                        // Vector #3..#n - I don't use Systick and another interrupts right now
                                                        // so it is not necessary to define them and code can start here

blink:
            LDR r0, =(SYSCFG_BASE_ADDRESS   0x200)      // SYSCFG SYSCFG_CTRL
            LDR r1, =0                                  // 0 diable address faults exceptions
            ldr r1, [r0]                                // Store R0 value to r1


            LDR r0, =(CMU_BASE_ADDRESS)                 // CMU CMU_SYSCLKCTRL PCLKPRESC   CLKSEL
            LDR r1, =0b10000000001                      // FSRCO 20MHz   PCLK = HCLK/2 = 10MHz
            STR r1, [r0, 0x70]                          // Store R0 value to r1

            LDR r0, =(CMU_BASE_ADDRESS)                 // CMU CMU_CLKEN0
            LDR r1, [r0, 0x64]
            LDR r2, =(1 << 25)                          // GPIO CLK EN
            orrs r1, r2                                 // !!! HORROR !!! -- orr is not possible in thumb2 ?? only orrs !! (width suffix)
            STR r1, [r0, 0x64]                          // Store R0 value to r1

            LDR r1, [r0, 0x68]
            LDR r2, =(1 << 14)                          // SMU CLK EN
            orrs r1, r2                                 // !!! HORROR !!! -- orr is not possible in thumb2 ?? only orrs !! (width suffix)
            STR r1, [r0, 0x68]                          // Store R0 value to r1

            //LDR r0, =(SMU_BASE_ADDRESS)                 // SMU SMU_LOCK
            //LDR r1, =11325013                           // SMU UNLOCK CODE
            //STR r1, [r0, 0x08]                          //Store R0 value to r1


            ldr r0, =(SMU_BASE_ADDRESS)                 // SMU reading values, detection - AGAIN, HARD FAULTS !!!!!!!
            ldr r1, [r0, 0x04]
            ldr r1, [r0, 0x20]
            ldr r1, [r0, 0x40]

            //LDR r0, =(GPIO_BASE_ADDRESS   0x300)        // GPIO UNLOCK
            //LDR r1, =0xA534
            //STR r1, [r0]                                // Store R0 value to r1



            //!! THIS BELOW IS OLD FOR SAMD , WE STILL SIMPLY CANT ENABLE GPIO !!!!

            // Enable PORTA pin 4 as output
            LDR r0, =(GPIO_BASE_ADDRESS)                // DIR PORTA
            LDR r1, =0b00000000000001000000000000000000
            STR r1, [r0, 0x04]                          // Store R0 value to r1

            LDR R2, =1
            

loop:

            // Write high to pin PA04
            LDR r0, =GPIO_BASE_ADDRESS                // OUT PORTA
            LDR r1, =0b10000                          // PORT_PA04
            STR r1, [r0, 0x10]                        // Store R1 value to address pointed by R0

            // Dummy counter to slow down my loop
            LDR R0, =0
            LDR R1, =DELAY
loop0:
            ADD R0, R2
            cmp R0, R1
            bne loop0

            // Write low to PA04
            LDR r0, =GPIO_BASE_ADDRESS                // OUT PORTA
            LDR r1, =0b00000
            STR r1, [r0, 0x10]                        // Store R1 value to address pointed by R0

            // Dummy counter to slow down my loop
            LDR R0, =0
            LDR R1, =DELAY
loop1:
            ADD R0, R2
            cmp R0, R1
            bne loop1

            b loop
     

UPDATE: well, now I tried it again in SimplicityStudio, placing blink() call after pregenerated system init:

extern void blink(void);

int main(void)
{
  // Initialize Silicon Labs device, system, service(s) and protocol stack(s).
  // Note that if the kernel is present, processing task(s) will be created by
  // this call.
  sl_system_init();

  blink();
}

having this code in blink.s: - and here it works this way and blinks ...

            .cpu cortex-m33
            .thumb
            
            .text
            .section        .text.startup.main,"ax",%progbits
            .balign         2
            .p2align        2,,3
            
            .global         blink
            //.arch           armv8-m.base
            .arch           armv6-m
            .syntax         unified
            .code           16
            .thumb_func
            .fpu            softvfp
            .type           blink, %function
            

            /*
            //!!! here we have manually entered GPIO PORT defines for PIC32CM 
            .equ SYSCFG_BASE_ADDRESS,    0x50078000
            .equ SMU_BASE_ADDRESS,      0x54008000
            //.equ SMU_BASE_ADDRESS,      0x5400C000
            .equ CMU_BASE_ADDRESS,      0x50008000
            */
            .equ GPIO_BASE_ADDRESS,     0x5003C000      // this differs totally from both "special" infineon and microchip "standard?" cortex devices !!!
            
            .equ DELAY,                 400000


            // Vector table
            .word               0x20001000      // Vector #0 - Stack pointer init value (0x20000000 is RAM address and 0x1000 is 4kB size, stack grows "downwards")
            .word               blink           // Vector #1 - Reset vector - where the code begins
                                                        // Vector #3..#n - I don't use Systick and another interrupts right now
                                                        // so it is not necessary to define them and code can start here

blink:
            // Enable PORTA pin 4 as output
            LDR r0, =(GPIO_BASE_ADDRESS)                // DIR PORTA
            LDR r1, =0b00000000000001000000000000000000
            STR r1, [r0, 0x04]


loop:

            // Write high to pin PA04
            LDR r0, =GPIO_BASE_ADDRESS                  // OUT PORTA
            LDR r1, =0b10000                            // PORT_PA04
            STR r1, [r0, 0x10]

            // Dummy counter to slow down my loop
            LDR R0, =0
            LDR R1, =DELAY
loop0:
            ADD R0, R2
            cmp R0, R1
            bne loop0

            // Write low to PA04
            LDR r0, =GPIO_BASE_ADDRESS                  // OUT PORTA
            LDR r1, =0b00000
            STR r1, [r0, 0x10]

            // Dummy counter to slow down my loop
            LDR R0, =0
            LDR R1, =DELAY
loop1:
            ADD R0, R2
            cmp R0, R1
            bne loop1

            b loop
     

... so NOW, I am just curious, what all is missing in pure assembly code to bring that cortex-m33 into some "easy" state, just ignoring trustzone, probably to use it similary as say, plain cortex-m3 ??

can anybody help? I am digging deeply into this datasheet/ref manual, but no luck till now ... https://www.silabs.com/documents/public/reference-manuals/efm32pg22-rm.pdf

UPDATE AGAIN: umm, will try to figure out ... by traversing system_init C-code its clear whats going on, there are also some chip errata workarounds, but I never touched DCDC while initializing, this may be culprit...

void sl_platform_init(void)
{
  CHIP_Init();
  sl_device_init_nvic();
  sl_board_preinit();
  sl_device_init_dcdc();
  sl_device_init_hfxo();
  sl_device_init_lfxo();
  sl_device_init_clocks();
  sl_device_init_emu();
  sl_board_init();
}

CodePudding user response:

well, okay, manufacturer specific code generation for MCU startup IS really important and useful thing )) ... such MCUs from different manufacturers are really much different at registers level (even that all are "cortex-m" core based), that its worthless to try to configure them manually in assembly if there is enough flash available, and it mostly IS. So, till now, no luck with segger/keil/iar "generic" arm/cortex IDEs to do this properly on specific parts, so using manufacturer specific IDE to (mostly) graphically configure startup clocks and peripherals IS CRUCIAL, or at least, its really easiest way (I know, quite expensive observation after all the assembly tries... )). After then, its easy to make even pure assembly "blink" helloworld test called as extern C-function. You may be asking why I am still considering assembly if there are even CMSIS (on arm) "platform abstraction layer" C-headers at least (no, it doesnt help in abstraction, as the devices are still very different, you only have registers symbols #defines and typedefs and enums to do something in C easily, okay). But I am trying to compare some C-compiled code with handwriten assembly for some specific purpose, which needs forced optimized algorithm from scratch and its often quite easier to think/design it directly in assembly that to rely on very complexly described C-compiler optimisations (each compiler has its own LONG document how his optimisations work and at this level, C is simply still too abstract and moving target, the more, you try to write something for even different MCU architectures (think ARM cortex-m, PIC32/mips, and/or even PIC16/18 PIC24, AVR , MSP430 ...) - while general algorithm may be described in shared pseudoassenbly to be as near to hardware as possible, withnout knowing all optimization quirks of each architecture C compiler(s) - there are often MORE different C compilers too. So, to compare C-compiler generated code with handwriten assembly you can do it, and I already tried such assembly blink on MANY VERY different architectures, in case I definitelly used mfg specific IDE to genearte startup in C, using all the GUI configurations and code generation down to always compilable empty C project, of course, having very different code size output using such generated startups. Most advanced MCUs are really very complex, mostly in clocks configuration and pins functions config and then different peripheral devices too, sure. Some similarities are possible only at single mfg level, to some extent, so MCU of single manufacturer often share similar approach, obviously. So final solution is to have startup generated and then switch to assembly immediatelly, this is feasible. Sure that in case of small flash, its further possible to optimize even startup code, but its mostly important on smallest 8bit parts, where startup IS quite easy anyway or the generated code is also small, obviously.

  • Related