Baremetal ARM? Time to don the geek glasses!

Aargh!

You have no idea how long I spent googling keywords contained in the title of this post.  Especially now that you found this post, you lucky duck.  I wasted literal DAYS trying to get a stupid assembly program to run on my TIVA Connected Launchpad.  I've done a handful of programs for the TIVA in the past, but they have been all in C and all the examples TI gave were also in C.


All I needed was an example on the syntax that Code Composer uses for assembly and some of the directives I would need to get it going.  Sadly, I could not find this anywhere.  My friend Andrew Mendez who is also working on the Launchpad, pointed me to this webpage of UT professor Jonathan Valvano where he has starter files for several simple projects in his textbook, which I highly recommend and you can purchase from Amazon here.

The page has files for both TIVA Launchpads in C as well as assembly.  However, all of them are formatted to run in Keil uVision assembly, which uses a totally different syntax than what Code Composer or the GNU compiler use.  I found this out the hard way.  And yes, the examples did run flawlessly in Keil uVision v4, but I did not want to use yet another IDE and then have to worry about porting my existing CCS C projects.  Not to mention the free MDK-Lite version of the software is limited to only 32 Kilobytes, which granted would probably be enough for most microcontroller programs, but chaining down the beast that is the 120 MHz Launchpad with over 1 MB of flash memory to a measly 32 K seemed like a sin to me.  Plus, the Keil syntax for assembly just looks odd to me as I'm much more used to the dot (.) syntax.

It wasn't until I took a closer look at that webpage where Prof. Valvano is kind enough to provide a guide for converting the weird Keil syntax into something CCS can understand.

Even with that, the journey wasn't as easy as I thought it would be for a multitude of reasons that I won't get into, but eventually I was actually able to get an LED to blink.  Now that I've vented some of my frustrations, I can get on with the guide.

Step 1: Create a New Project in Code Composer Studio

Open up CCS.  Click File -> New -> CCS Project.


You will be met by a familiar window.  Select you target board (for the Connected Launchpad, it's "Tiva TM4C1294NCPDT").  Select your method of programming the board (for the Connected Launchpad, it's "Stellaris In-Circuit Debug Interface").  Type in a name of varying degrees of creativity for your project.  Now here's the trap.  Do not select "Empty Assembly-Only Project" as your project type.  I did so and even with code that I knew worked, it would not run properly on the board.  I spent over an hour fiddling with the project settings to no avail so let's not waste any more time on something so mundane and let's just do it the easy way that works!  Select "Empty Project (with main.c)" Hit Finish and let's move on.


Step 2: Set up project files

Go into the project tree and delete the main.c file.  We won't need it.


Now select the project folder, right-click it and add New -> File.


When the dialog pops up, name it "main.asm" this will hold the code for the main assembly program.


Step 3: Program assembly

Now for the fun part!  Open up main.asm, type in your program and voila!  Here's a basic assembly structure:

    .global  main                                   ; makes main accessible from outside this file.
    .thumb                                          ; use thumb, duh
    .data                                           ; set memory location to sram
    ; put your variables here
    .text                                           ; set memory location to flash
main:
    ; code goes here
OK it's not as easy as that since the real challenge of this came from finding a working example of a full assembly program specifically for the TIVA Connected Launchpad written in a syntax that Code Composer would understand.  None existed (or my google-fu is rusty), so I had to make my own.  Fortunately for you, here it is in all its baremetal glory!

;=========================================================================================================
; Blink in Assembly
; By Ricardo Angeli (03/04/15)
; Designed for Tiva Connected Launchpad (TM4C1294NCPDT)
;=========================================================================================================
; Description:
; Blinks an onboard LED at an approximate rate of once
; per second.  Designed as a test for an assembly-only
; program on the Launchpad.
;=========================================================================================================

    .global  main                                   ; makes main accessible from outside this file.
    .thumb                                          ; use thumb, duh

    .text                                           ; set memory location to flash
    .align 4                                        ; align on 32-bit boundary

;=========================================================================================================
; Hardware Constants
;=========================================================================================================
GPIO_PORTJ_DATA_R             .field 0x400603FC,32
GPIO_PORTJ_DIR_R              .field 0x40060400,32
GPIO_PORTJ_AFSEL_R            .field 0x40060420,32
GPIO_PORTJ_PUR_R              .field 0x40060510,32
GPIO_PORTJ_DEN_R              .field 0x4006051C,32
GPIO_PORTJ_AMSEL_R            .field 0x40060528,32
GPIO_PORTJ_PCTL_R             .field 0x4006052C,32
PJ0                           .field 0x40060004,32
PJ1                           .field 0x40060008,32
SWITCHES                      .field 0x4006000C,32
SW1_PRESSED                   .equ 0x02              ; value read from location SWITCHES when just SW1 is
                                                     ; pressed.
SW2_PRESSED                   .equ 0x01              ; value read from location SWITCHES when just SW2 is
                                                     ; pressed.
BOTH_PRESSED                  .equ 0x00              ; value read from location SWITCHES when both
                                                     ; switches are pressed
NO_PRESSED                    .equ 0x03              ; value read from location SWITCHES when neither
                                                     ; switch is pressed.
GPIO_PORTN_DATA_R             .field 0x400643FC,32
GPIO_PORTN_DIR_R              .field 0x40064400,32
GPIO_PORTN_AFSEL_R            .field 0x40064420,32
GPIO_PORTN_DEN_R              .field 0x4006451C,32
GPIO_PORTN_AMSEL_R            .field 0x40064528,32
GPIO_PORTN_PCTL_R             .field 0x4006452C,32
PN0                           .field 0x40064004,32
PN1                           .field 0x40064008,32
LEDS                          .field 0x4006400C,32
LED1_ON                       .equ 0x02               ; value written to location PN1 or LEDS to turn on
                                                      ; LED1.
LED2_ON                       .equ 0x01               ; value written to location PN0 or LEDS to turn on
                                                      ; LED2.
SYSCTL_RCGCGPIO_R             .field 0x400FE608,32
SYSCTL_RCGCGPIO_R12           .equ 0x00001000         ; GPIO Port N Run Mode Clock Gating Control
SYSCTL_RCGCGPIO_R8            .equ 0x00000100         ; GPIO Port J Run Mode Clock Gating Control
SYSCTL_PRGPIO_R               .field 0x400FEA08,32
SYSCTL_PRGPIO_R12             .equ 0x00001000         ; GPIO Port N Peripheral Ready
SYSCTL_PRGPIO_R8              .equ 0x00000100         ; GPIO Port J Peripheral Ready

;=========================================================================================================
; Custom Constants
;=========================================================================================================
CPU_FREQ                      .field 8000000,32       ; Current CPU clockspeed stored in Hertz

;=========================================================================================================
; Program Code
;=========================================================================================================
        .align 2                                      ; align on 16-bit boundary

main:                                                 ; must be called main to execute on its own

        push {r0-r2, lr}                              ; push registers we will use into the stack,
                                                      ; useful for integrating within C code.

        ; Enable GPIO port
        ldr r0, SYSCTL_RCGCGPIO_R
        mov r1, #SYSCTL_RCGCGPIO_R12
        str r1, [r0]

        ; Wait a few cycles for the peripheral to be enabled
        nop
        nop
        nop
        nop
        nop

        ldr r0, GPIO_PORTN_DIR_R
        mov r1, #0x01
        str r1, [r0]

        ldr r0, GPIO_PORTN_DEN_R
        mov r1, #0x01
        str r1, [r0]

        ldr r0, GPIO_PORTN_DATA_R

blink:
        ; Turn on the LED
        orr r1, r1, #LED2_ON
        str r1, [r0]

        ; Initiate wait drive!
        ldr r2, CPU_FREQ                              ; load number of cycles we want to wait
wait1:
        sub r2, r2, #10                               ; subtract 10 every 10 cycles
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        cmp r2, #0
        bgt wait1

        ; Turn off the LED
        bic r1, r1, #LED2_ON
        str r1, [r0]

        ; Initiate wait drive!
        ldr r2, CPU_FREQ                              ; load number of cycles we want to wait
wait2:
        sub r2, r2, #10                               ; subtract 10 every 10 cycles
        nop
        nop
        nop
        nop
        nop
        nop
        nop
        cmp r2, #0
        bgt wait2                                     ; loop back unless r2 <= 0

        ; loop to the beginning of the blink routine all over again
        b blink

        ; if calling this assembly program from C, this would pop back used registers and return back to C
        pop {r0-r2, lr}
        mov pc,lr
    
I know this is not the cleanest or most efficient code for this sort of thing.  But I wanted a simple program to get up and go and this features many common instructions.  I'm not even going to deal with interrupts until I can get an LED to blink.  Note that the .field is required to specify a 32-bit word.  The maximum number of bits for .equ is 16, I think.  That was another stumbling point through this.

Notice how to call an immediate value stored in memory, you use the # symbol followed by the name of the label such as in orr r1, r1, #LED2_ON.  Yet if you are using an instruction to call a memory location, then you simply type the name of the label like in ldr r0, SYSCTL_RCGCGPIO_R.

Well I hope you found this post useful.  If you did, please let me know via the comments section.  If you managed to find a good source that explains all of this let me know as well.

Downloads

If you want to download the Code Composer Project file, you may do so below. All you need is to unzip it and import it into your workspace.  It's the same code shown in the example.

Download BlinkyASM.zip