I'm trying to run out my firmware inside stm32f103. So when I use opencod+gdb uploading and debugging elf file, everything is fine, my firmware is working and I can set and remove breakpoints.
But it doens't work when I try to upload this firmware (which was built together with elf file) using st-flash and writing it into 0x8000000. Although I get the message that 'the firmware was uploaded successfully'.
I can see if my code runs when my leds start blinking.
BOOT0 is connected to the DTR pin of a cp2102 via npn transistor, according to the datasheet to enable bootloader. I have to set BOOT0 to high. But my serial (cp2102) is not connected when I upload my fw via st-link. So I think that DTR pin is floating or pulled down. Where is my mistake?
I was trying to mass erase my flash before uploading, it gives the same results
here is my linker's ld file:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
CCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 0
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
MEMORY_ARRAY (xrw) : ORIGIN = 0x00000000, LENGTH = 0
}
and sections https://pastebin.ubuntu.com/p/N32zQf9sCm/
Try this
.globl _start
_start:
.word 0x20001000
.word reset
.word loop
.word loop
.thumb_func
reset:
add r0,#1
b reset
.thumb_func
loop:
b loop
build, can use arm-whatever-whatever (arm-none-eabi, arm-linux-gnueabi, etc)
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x08000000 so.o -o so.elf
arm-none-eabi-objcopy -O binary so.elf so.bin
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000015 stmdaeq r0, {r0, r2, r4}
800000c: 08000015 stmdaeq r0, {r0, r2, r4}
08000010 <reset>:
8000010: 3001 adds r0, #1
8000012: e7fd b.n 8000010 <reset>
08000014 <loop>:
8000014: e7fe b.n 8000014 <loop>
Not that the vectors are odd, they are the address of the handler orred with one. If you don't see this the processor won't boot.
You have said you have openocd+gdb working so either through that path or via openocd+telnet or if you have another way using the uart bootloader for example. But use reset or a power on with boot0 set for application and then attach with openocd without resetting it, then halt and examine r0, resume, halt and examine again, is it counting, did this code load and run from flash.
If you have a blue pill then you can use this code to blink the led.
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.align
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
.end
blinker01.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCCBASE+0x18);
ra|=1<<4; //enable port c
PUT32(RCCBASE+0x18,ra);
//config
ra=GET32(GPIOCBASE+0x04);
ra&=~(3<<20); //PC13
ra|=1<<20; //PC13
ra&=~(3<<22); //PC13
ra|=0<<22; //PC13
PUT32(GPIOCBASE+0x04,ra);
for(rx=0;;rx++)
{
PUT32(GPIOCBASE+0x10,1<<(13+0));
for(ra=0;ra<200000;ra++) dummy(ra);
PUT32(GPIOCBASE+0x10,1<<(13+16));
for(ra=0;ra<200000;ra++) dummy(ra);
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
build
arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -c blinker01.c -o blinker01.o
arm-none-eabi-ld -o blinker01.elf -T flash.ld flash.o blinker01.o
arm-none-eabi-objdump -D blinker01.elf > blinker01.list
arm-none-eabi-objcopy blinker01.elf blinker01.bin -O binary
examine vector table
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000041 stmdaeq r0, {r0, r6}
8000008: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800000c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000010: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000014: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000018: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800001c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000020: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000024: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000028: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800002c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000030: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000034: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000038: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800003c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
08000040 <reset>:
8000040: f000 f80a bl 8000058 <notmain>
8000044: e7ff b.n 8000046 <hang>
08000046 <hang>:
8000046: e7fe b.n 8000046 <hang>
Looks good. Now program and reset.
Your schematic is not a blue pill so adjust accordingly to enable the gpio clocks and make the pin an output and so on for your led.
with either of these programs or your own, I don't use gdb have no use for it I use openocd+telnet if anything. but either should let you dump the flash with telnet
mdw 0x00000000 20
mdw 0x08000000 20
both should have the same data. If not then you have a boot0 problem.
Edit
If DTR is high then boot0 is 0/GND yes? That is what is required to boot normally. boot0 tied low and reset tied high with a power on or low to high.
You can write a program to force DTR one way or the other.
int dtr_bit=TIOCM_DTR;
...
dtr_bit=TIOCM_DTR;
ioctl(ser_hand,TIOCMBIC,&dtr_bit);
...
dtr_bit=TIOCM_DTR;
ioctl(ser_hand,TIOCMBIS,&dtr_bit);
with the usual termios stuff to open the handle.
Or assuming DTR is supported by your dumb terminal (minicom, etc), attach to the uart, then power on and/or reset the board (paperclip, or whatever you have handy).
Since I often use the serial bootloader to load my stm32 parts or even if I use SWD, I always provide myself a solution to control boot0 and reset, be it push-buttons or jumpers or pads or some combination.
Have you tried to use the STM32CubeProgrammer?
It allows you to program the firmware using UART, SWD, JTAG and USB. Please try SWD firstly and then the UART mode to see if it is a ST bug or your UART wiring.
#old_timer Thank you very much for your detailed answer. I found that the problem is in oversized firmware. My chip has only 64k of flash and fw is 68k, so eclipse didn't tell me that the firmware is oversized and tried to flash it. Also, template in which my project based uses newlib, where startup.s was replaced by C files and I guess there is some issue. So I created a new project from openstm32 tamplate (it copied all necessary files from stdperiph lib, cmsis, etc + ld), after that I added my files, optimized build by removing unused files and set -Os flag, and it seems working for me, but the firmware size is almost oversized. I will try yr explanation )
Related
I have a STM32F103C8 MCU, and I want to control GPIO registers without Cube MX. The MCU has an embedded LED and I want control it. I'm currently using CubeMX and IAR Software, and I make the pin an output (in CubeMX) with this code:
HAL_GPIO_TogglePin(Ld2_GPIO_Port,Ld2_Pin);
HAL_Delay(1000);
This works, but I want to do it without Cube and HAL library; I want to edit the register files directly.
Using GPIO using registers is very easy. You fo not have to write your own startup (as ion the #old_timer answer). Only 2 steps are needed
you will need the STM provided CMSIS headers with datatypes declarations and human readable #defines and the reference manual
Enable GPIO port clock.
ecample: RCC -> APB2ENR |= RCC_APB2ENR_IOPAEN;
Configure the pins using CRL/CRH GPIO registers
#define GPIO_OUTPUT_2MHz (0b10)
#define GPIO_OUTPUT_PUSH_PULL (0 << 2)
GPIOA -> CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
GPIOA -> CRL |= GPIO_OUTPUT_2MHz | GPIO_OUTPUT_PUSH_PULL;
Manipulate the output
/* to toggle */
GPIOA -> ODR ^= (1 << pinNummer);
/* to set */
GPIOA -> BSRR = (1 << pinNummer);
/* to reset */
GPIOA -> BRR = (1 << pinNummer);
//or
GPIOA -> BSRR = (1 << (pinNummer + 16));
It is very good to know how to do bare metal without the canned libraries, and or to be able to read through those libraries and understand what you are getting yourself into by using them.
This blinks port C pin 13 that is where you generally find the user led on the stm32 blue pill boards. You can figure it out from here and the documentation for the STM32F103C8.
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.word loop
.thumb_func
reset:
bl notmain
b loop
.thumb_func
loop: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
so.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000
#define STK_CSR 0xE000E010
#define STK_RVR 0xE000E014
#define STK_CVR 0xE000E018
#define STK_MASK 0x00FFFFFF
static int delay ( unsigned int n )
{
unsigned int ra;
while(n--)
{
while(1)
{
ra=GET32(STK_CSR);
if(ra&(1<<16)) break;
}
}
return(0);
}
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCCBASE+0x18);
ra|=1<<4; //enable port c
PUT32(RCCBASE+0x18,ra);
//config
ra=GET32(GPIOCBASE+0x04);
ra&=~(3<<20); //PC13
ra|=1<<20; //PC13
ra&=~(3<<22); //PC13
ra|=0<<22; //PC13
PUT32(GPIOCBASE+0x04,ra);
PUT32(STK_CSR,4);
PUT32(STK_RVR,1000000-1);
PUT32(STK_CVR,0x00000000);
PUT32(STK_CSR,5);
for(rx=0;;rx++)
{
PUT32(GPIOCBASE+0x10,1<<(13+0));
delay(50);
PUT32(GPIOCBASE+0x10,1<<(13+16));
delay(50);
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
build
arm-none-eabi-as --warn --fatal-warnings flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -c so.c -o so.o
arm-none-eabi-ld -o so.elf -T flash.ld flash.o so.o
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf so.bin -O binary
PUT32/GET32 is IMO a highly recommended style of abstraction, decades of experience and it has many benefits over the volatile pointer or worse the misuse of unions thing that is the current FAD. Not meant to be a library but to show code that does not require any libraries, only the files provided are required.
Most mcus you need to enable clocks to the peripheral before you can talk to it. You can see the read-modify-write of an RCC register.
Most MCUs the GPIO pins reset to inputs so you need to set one to an output to drive/blink an led. Even within the STM32 world but certainly across brands/families the GPIO (and other) peripherals are not expected to be identical nor even compatible so you have to refer to the documentation for that part and it will show how to make a pin an output. very good idea to read-modify-write instead of just write, but since you are in complete control over the chip you can just write if you wish, try that later.
This chip has a nice register that allows us to change the output state of one or more but not necessarily all GPIO outputs in a single write, no read-modify-write required. So I can set or clear pin 13 of GPIOC without affecting the state of the other GPIOC pins.
Some cortex-ms have a systick timer, for example not all cortex-m3s have to have one it is up to the chip folks usually and some cores may not have the option. This chip does so you can use it. In this example the timer is set to roll over every 1 million clocks, the delay function waits for N number of rollovers before returning. so 50,000,000 clocks between led state changes. since this code runs right from reset without messing with the clocking or other systems, the internal HSI 8MHz clock is used 50/8 = 6.25 seconds between led state changes. systick is very easy to use, but remember it is a 24 bit counter not 32 so if you want to do now vs then you must mask it.
I don't remember if it is an up counter
elapsed = (now - then) & 0x00FFFFFF;
or down
elapsed = (then - now) & 0x00FFFFFF;
(now = GET32(systick count register address))
The systick timer is in the arm documentation not the chip documentation necessarily although sometimes ST produces their own version, you want the arm one for sure and maybe then the st one. infocenter.arm.com (you have to give up an email address or you can Google sometimes you get lucky, someone will illegally post them somewhere) this chip will tell you it uses a cortex-m3 so find the technical reference manual for the cortex-m3 in that you will find it is based on architecture armv7-m so under architecture find the armv7-m documentation, between these you see how the vector table works, the systick timer and its addresses, etc.
Examine vector table
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000041 stmdaeq r0, {r0, r6}
8000008: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800000c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000010: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000014: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000018: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800001c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000020: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000024: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000028: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800002c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000030: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000034: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000038: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800003c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
08000040 <reset>:
8000040: f000 f806 bl 8000050 <notmain>
8000044: e7ff b.n 8000046 <loop>
08000046 <loop>:
8000046: e7fe b.n 8000046 <loop>
The entry point code with our vector table which starts off with the value we would like to put in the stack pointer on reset should be the first thing, then vector tables which are the address of the handler ORRed with 1 (not as easy to find in the docs sometimes). the disassembly of these addresses is because I used the disassembler to view them those are not actual instructions in the vector table it is a table of vectors. the tool is just doing its best to disassemble everything, if you look at the rest of the output it also disassembles the ascii tables and other things which are also not code.
.data is not supported in this example a bunch more work would be required.
I recommend if/when you get yours working you then examine the HAL library sources to see that when you dig through layers of sometimes bloated or scary code, you will end up with the same core registers, they may choose to always configure all the gpio registers for example, speed and pull up/down, turn off the alternate function, etc. Or not. the above knows it is coming out of reset and the state of the system so doesn't go to those lengths for some peripherals you can pop the reset for that peripheral and put it in a known state rather than try to make a library that anticipates it being left in any condition and trying to configure from that state. YMMV.
It is good professionally to know how to work at this level as well as how to use libraries. An MCU chip vendor will often have two libraries, certainly for older parts like these, the current library product and the legacy library product, when a new library comes out to keep it fresh and competitive (looking) the oldest one will drop off from support and you sometimes have current and prior. depends on the vendor, depends on the part, depends on how they manage their software products (same goes for their IDEs and other tools).
Most of the stm32 parts esp a blue pill and other boards you can get do not require the fancy IDEs to program but external hardware is sometimes required unless you get a NUCLEO or Discovery board then you have at least enough to program the part with free software not attached to ST. with a nucleo it is mbed style where you simply copy the .bin file to the virtual usb drive and the board takes care of programming the development MCU.
In our c project, we need to build a binary firmware into a image at a fixed file offset 0x80000.
Then when the image is loaded to memory. We can load firmware from offset 0x80000 to a specified address.
Meanwhile, as the firmware is placed at file offset 0x80000, we can upgrade the firmare independently.
So I'm trying to use GNU linker script to implement that.
What I do now is use incbin to include my binary file in a asm file.
And in linker script, my code is:
.fw_image_start : {
*(.__fw_image_start)
}
.fw_image : {
KEEP(*(.fw_image))
}
.fw_image_end : {
*(.__fw_image_end)
}
Then I can use fw_image_start to load firmware in image code.
But I still can't find a way to put the firmware binary to file offset 0x80000 in the final image.
Could you help me on this?
Thank you in advance!
What did you find when you looked at the documentation and examples from GNU? Some of it is admittedly confusing or misleading, but some is pretty easy. This should give a hit of at least one way to do it (there are multiple ways to solve your problem).
novectors.s
.global _start
_start:
bl notmain
b .
.globl bounce
bounce:
bx lr
.section .hello_world
.word 1,2,3,4
notmain.c
void bounce ( unsigned int );
unsigned int mybss[8];
int notmain ( void )
{
unsigned int ra;
for(ra=0;ra<1000;ra++) bounce(ra);
return(0);
}
memmap.ld
MEMORY
{
ram : ORIGIN = 0x80000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.hello_world : { *(.hello_world) } > ram
.bss : { *(.bss*) } > ram
}
build
arm-none-eabi-as --warn --fatal-warnings novectors.s -o novectors.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T memmap.ld novectors.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
examine results
Disassembly of section .text:
00080000 <_start>:
80000: eb000001 bl 8000c <notmain>
80004: eafffffe b 80004 <_start+0x4>
00080008 <bounce>:
80008: e12fff1e bx lr
0008000c <notmain>:
8000c: e92d4010 push {r4, lr}
80010: e3a04000 mov r4, #0
80014: e1a00004 mov r0, r4
80018: e2844001 add r4, r4, #1
8001c: ebfffff9 bl 80008 <bounce>
80020: e3540ffa cmp r4, #1000 ; 0x3e8
80024: 1afffffa bne 80014 <notmain+0x8>
80028: e3a00000 mov r0, #0
8002c: e8bd4010 pop {r4, lr}
80030: e12fff1e bx lr
Disassembly of section .hello_world:
00080034 <.hello_world>:
80034: 00000001 andeq r0, r0, r1
80038: 00000002 andeq r0, r0, r2
8003c: 00000003 andeq r0, r0, r3
80040: 00000004 andeq r0, r0, r4
Disassembly of section .bss:
00080034 <mybss>:
...
Naturally:
MEMORY
{
bob : ORIGIN = 0x80000, LENGTH = 0x1000
ted : ORIGIN = 0xB0000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
.rodata : { *(.rodata*) } > bob
.hello_world : { *(.hello_world) } > ted
.bss : { *(.bss*) } > ted
}
gives
Disassembly of section .text:
00080000 <_start>:
80000: eb000001 bl 8000c <notmain>
80004: eafffffe b 80004 <_start+0x4>
00080008 <bounce>:
80008: e12fff1e bx lr
0008000c <notmain>:
8000c: e92d4010 push {r4, lr}
80010: e3a04000 mov r4, #0
80014: e1a00004 mov r0, r4
80018: e2844001 add r4, r4, #1
8001c: ebfffff9 bl 80008 <bounce>
80020: e3540ffa cmp r4, #1000 ; 0x3e8
80024: 1afffffa bne 80014 <notmain+0x8>
80028: e3a00000 mov r0, #0
8002c: e8bd4010 pop {r4, lr}
80030: e12fff1e bx lr
Disassembly of section .hello_world:
000b0000 <.hello_world>:
b0000: 00000001 andeq r0, r0, r1
b0004: 00000002 andeq r0, r0, r2
b0008: 00000003 andeq r0, r0, r3
b000c: 00000004 andeq r0, r0, r4
Disassembly of section .bss:
000b0000 <mybss>:
...
and as demonstrated the name ram, rom, etc are not special can call it other things like bob, ted, alice...I assume there are some reserved words you cant use.
Again there are numerous solutions, see the GNU documentation, I like this method as it reads better for me, but you will see solutions that skip the MEMORY part.
(no this wasnt intended to be completely correct code, but demonstrates the assembly language bootstrap, the C code and the linker script).
I'm trying the light an LED (on port c, pin 13) on STM32F103C8T6. I'm not using IDE. Code:
#include "include/stm32f10x.h"
int main()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~GPIO_CRH_CNF13;
GPIOC->CRH |= GPIO_CRH_MODE13_0;
GPIOC->BSRR = GPIO_BSRR_BR13;
while(1)
{
GPIOC->BSRR = GPIO_BSRR_BS13;
}
return 0;
}
Links to include files in include directory:
system_stm32f10x.h
core_cmInstr.h
core_cmFunc.h
core_cm3.h
stm32f10x.h part1
stm32f10x.h part2
That's how I compile it
arm-none-eabi-gcc --specs=nosys.specs -o output led.c
After uploading it to mc nothing happens.
to light the led on a blue pill you need PC13 low (reset) so write with reset bit 13 set in the gpioc bsrr register, then end the program, making the gpio line go high turns off the led.
#include "include/stm32f10x.h"
int main()
{
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~GPIO_CRH_CNF13;
GPIOC->CRH |= GPIO_CRH_MODE13_0;
GPIOC->BSRR = GPIO_BSRR_BR13;
while(1)
{
continue;
}
return 0;
}
Just look at the schematic.
Understand this is not a universal truth that low is on. You have to look at the design of the board. Also not all stm32f103c8t6 chips have an led on that pin, but I assume this is an stm32 "blue pill" board.
EDIT
a complete working blinker example for that board
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.align
.thumb_func
.globl PUT16
PUT16:
strh r1,[r0]
bx lr
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
.end
blinker01.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCCBASE+0x18);
ra|=1<<4; //enable port c
PUT32(RCCBASE+0x18,ra);
//config
ra=GET32(GPIOCBASE+0x04);
ra&=~(3<<20); //PC13
ra|=1<<20; //PC13
ra&=~(3<<22); //PC13
ra|=0<<22; //PC13
PUT32(GPIOCBASE+0x04,ra);
for(rx=0;;rx++)
{
PUT32(GPIOCBASE+0x10,1<<(13+0));
for(ra=0;ra<200000;ra++) dummy(ra);
PUT32(GPIOCBASE+0x10,1<<(13+16));
for(ra=0;ra<200000;ra++) dummy(ra);
}
return(0);
}
build
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m0 -march=armv6-m -c blinker01.c -o blinker01.thumb.o
arm-none-eabi-ld -o blinker01.thumb.elf -T flash.ld flash.o blinker01.thumb.o
arm-none-eabi-objdump -D blinker01.thumb.elf > blinker01.thumb.list
arm-none-eabi-objcopy blinker01.thumb.elf blinker01.thumb.bin -O binary
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker01.c -o blinker01.thumb2.o
arm-none-eabi-ld -o blinker01.thumb2.elf -T flash.ld flash.o blinker01.thumb2.o
arm-none-eabi-objdump -D blinker01.thumb2.elf > blinker01.thumb2.list
arm-none-eabi-objcopy blinker01.thumb2.elf blinker01.thumb2.bin -O binary
builds both with and without thumb2 extensions (flash.s doesnt have to be thumb only, just leftovers from prior examples).
now what file and how are you uploading it to the mcu? loading an elf does you no good there isnt an operating system but depending on the tool you are using it may read elf and download the loadable portions. I wrote my own tool since it is so simple to interface with the bootloader uart interface. I have also used openocd with various swd/jtag interfaces (stlink, j-link), to write these. They come from asia locked so the first time you have to unlock them, know you can do it from the uart interface, pretty sure I figured it out from openocd as well...
So either your binary build is bad/wont boot right, or the download is the issue (or both).
the flash in the stm32 maps at 0x08000000, they re-map it to 0x00000000 if/when booting from the application. Should be able to use 0x00000000 as well, but most I have seen use 0x08000000. As documented the first word is loaded into the stack pointer on reset, the second word is the thumb address to the reset vector (lsbit is set to indicate thumb so 0x41 below means the reset vector is at address 0x40 as shown)
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000041 stmdaeq r0, {r0, r6}
8000008: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800000c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000010: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000014: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000018: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800001c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000020: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000024: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000028: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800002c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000030: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000034: 08000047 stmdaeq r0, {r0, r1, r2, r6}
8000038: 08000047 stmdaeq r0, {r0, r1, r2, r6}
800003c: 08000047 stmdaeq r0, {r0, r1, r2, r6}
08000040 <reset>:
8000040: f000 f80a bl 8000058 <notmain>
8000044: e7ff b.n 8000046 <hang>
08000046 <hang>:
8000046: e7fe b.n 8000046 <hang>
You have to resolve this for starters, then once you have something with a chance of booting, then you can look at the code.
I have a binary firmware image for ARM Cortex M that I know should be loaded at 0x20000000. I would like to convert it to a format that I can use for assembly level debugging with gdb, which I assume means converting to an .elf. But I have not been able to figure out how to add enough metadata to the .elf for this to happen. Here is what I've tried so far.
arm-none-eabi-objcopy -I binary -O elf32-littlearm --set-section-flags \
.data=alloc,contents,load,readonly \
--change-section-address .data=0x20000000 efr32.bin efr32.elf
efr32.elf: file format elf32-little
efr32.elf
architecture: UNKNOWN!, flags 0x00000010:
HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .data 00000168 20000000 20000000 00000034 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
SYMBOL TABLE:
20000000 l d .data 00000000 .data
20000000 g .data 00000000 _binary_efr32_bin_start
20000168 g .data 00000000 _binary_efr32_bin_end
00000168 g *ABS* 00000000 _binary_efr32_bin_size
Do I need to start by converting the binary to .o and write a simple linker script? Should I add an architecture option to the objcopy command?
A little experiment...
58: 480a ldr r0, [pc, #40] ; (84 <spi_write_byte+0x38>)
5a: bf08 it eq
5c: 4809 ldreq r0, [pc, #36] ; (84 <spi_write_byte+0x38>)
5e: f04f 01ff mov.w r1, #255 ; 0xff
you dont have that of course, but you can read the binary and do this with it:
.thumb
.globl _start
_start:
.inst.n 0x480a
.inst.n 0xbf08
.inst.n 0x4809
.inst.n 0xf04f
.inst.n 0x01ff
then see what happens.
arm-none-eabi-as test.s -o test.o
arm-none-eabi-ld -Ttext=0x58 test.o -o test.elf
arm-none-eabi-objdump -D test.elf
test.elf: file format elf32-littlearm
Disassembly of section .text:
00000058 <_start>:
58: 480a ldr r0, [pc, #40] ; (84 <_start+0x2c>)
5a: bf08 it eq
5c: 4809 ldreq r0, [pc, #36] ; (84 <_start+0x2c>)
5e: f04f 01ff mov.w r1, #255 ; 0xff
but the reality is it wont work...if this binary has any thumb2 extensions it isnt going to work, you cant disassemble variable length instructions linearly. You have to deal with them in execution order. So to do this correctly you have to write a dissassembler that walks through the code in execution order, determining the instructions you can figure out, mark them as instructions...
80: d1e8 bne.n 54 <spi_write_byte+0x8>
82: bd70 pop {r4, r5, r6, pc}
84: 40005200
88: F7FF4000
8c: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr}
90: 4887 ldr r0, [pc, #540] ; (2b0 <notmain+0x224>)
.thumb
.globl _start
_start:
.inst.n 0xd1e8
.inst.n 0xbd70
.inst.n 0x5200
.inst.n 0x4000
.inst.n 0x4000
.inst.n 0xF7FF
.inst.n 0xe92d
.inst.n 0x41f0
.inst.n 0x4887
80: d1e8 bne.n 54 <_start-0x2c>
82: bd70 pop {r4, r5, r6, pc}
84: 5200 strh r0, [r0, r0]
86: 4000 ands r0, r0
88: 4000 ands r0, r0
8a: f7ff e92d ; <UNDEFINED> instruction: 0xf7ffe92d
8e: 41f0 rors r0, r6
90: 4887 ldr r0, [pc, #540] ; (2b0 <_start+0x230>)
it will recover, and break and recover, etc...
instead you have to write a disassembler that walks through the code (doesnt necessarily have to disassemble to assembly language but enough to walk the code and recurse down all possible branch paths). all data not determined to be instructions mark as instructions
.thumb
.globl _start
_start:
.inst.n 0xd1e8
.inst.n 0xbd70
.word 0x40005200
.word 0xF7FF4000
.inst.n 0xe92d
.inst.n 0x41f0
.inst.n 0x4887
00000080 <_start>:
80: d1e8 bne.n 54 <_start-0x2c>
82: bd70 pop {r4, r5, r6, pc}
84: 40005200 andmi r5, r0, r0, lsl #4
88: f7ff4000 ; <UNDEFINED> instruction: 0xf7ff4000
8c: e92d 41f0 stmdb sp!, {r4, r5, r6, r7, r8, lr}
90: 4887 ldr r0, [pc, #540] ; (2b0 <_start+0x230>)
and our stmdb instruction is now correct.
good luck.
I have spent multiple days trying to figure this out and I just can't. I have some C code. I have made the assembly code for this C program, copy pasted the assembly to someone else's project (that only contains a single assembly file) and assembled that. In these case things work. But if I try to compile from C directly to generate the binaries, it doesn't work. Even though everything else should be identical. This is my C code:
#include <stdint.h>
#define REGISTERS_BASE 0x3F000000
#define MAIL_BASE 0xB880 // Base address for the mailbox registers
// This bit is set in the status register if there is no space to write into the mailbox
#define MAIL_FULL 0x80000000
// This bit is set in the status register if there is nothing to read from the mailbox
#define MAIL_EMPTY 0x40000000
struct Message
{
uint32_t messageSize;
uint32_t requestCode;
uint32_t tagID;
uint32_t bufferSize;
uint32_t requestSize;
uint32_t pinNum;
uint32_t on_off_switch;
uint32_t end;
};
struct Message m =
{
.messageSize = sizeof(struct Message),
.requestCode =0,
.tagID = 0x00038041,
.bufferSize = 8,
.requestSize =0,
.pinNum = 130,
.on_off_switch = 1,
.end = 0,
};
/** Main function - we'll never return from here */
int _start(void)
{
uint32_t mailbox = MAIL_BASE + REGISTERS_BASE + 0x18;
volatile uint32_t status;
do
{
status = *(volatile uint32_t *)(mailbox);
}
while((status & 0x80000000));
*(volatile uint32_t *)(MAIL_BASE + REGISTERS_BASE + 0x20) = ((uint32_t)(&m) & 0xfffffff0) | (uint32_t)(8);
while(1);
}
This is a linker file I copied from the successful method:
/*
* Very simple linker script, combing the text and data sections
* and putting them starting at address 0x800.
*/
SECTIONS {
/* Put the code at 0x80000, leaving room for ARM and
* the stack. It also conforms to the standard expecations.
*/
.init 0x8000 : {
*(.init)
}
.text : {
*(.text)
}
/* Put the data after the code */
.data : {
*(.data)
}
}
And these is how I am compiling and linking everything:
arm-none-eabi-gcc -O0 -march=armv8-a PiTest.c -nostartfiles -o kernel.o
arm-none-eabi-ld kernel.o -o kernel.elf -T kernel.ld
arm-none-eabi-objcopy kernel.elf -O binary kernel.img
My target architecture is armv8 since that's what the pi model 3 uses.
I have no idea how the generated assembly works, but the C code directly does not. Please help I am on the verge of madness.
EDIT: The expected behaviour is for the pi's light to turn on. which it does with the first method I described. With the second method the light remains off.
EDIT4: Made some changes to files, deleted previous edits with outdated info to reduce post size
kernel.elf: file format elf32-littlearm
Disassembly of section .init:
00008000 <_start>:
8000: e3a0dd7d mov sp, #8000 ; 0x1f40
8004: eaffffff b 8008 <kernel_main>
Disassembly of section .text:
00008008 <kernel_main>:
8008: e52db004 push {fp} ; (str fp, [sp, #-4]!)
800c: e28db000 add fp, sp, #0
8010: e24dd00c sub sp, sp, #12
8014: e30b3898 movw r3, #47256 ; 0xb898
8018: e3433f00 movt r3, #16128 ; 0x3f00
801c: e50b3008 str r3, [fp, #-8]
8020: e51b3008 ldr r3, [fp, #-8]
8024: e5933000 ldr r3, [r3]
8028: e50b300c str r3, [fp, #-12]
802c: e51b300c ldr r3, [fp, #-12]
8030: e3530000 cmp r3, #0
8034: bafffff9 blt 8020 <kernel_main+0x18>
8038: e30b38a0 movw r3, #47264 ; 0xb8a0
803c: e3433f00 movt r3, #16128 ; 0x3f00
8040: e3082050 movw r2, #32848 ; 0x8050
8044: e3402001 movt r2, #1
8048: e3c2200f bic r2, r2, #15
804c: e3822008 orr r2, r2, #8
8050: e5832000 str r2, [r3]
8054: eafffffe b 8054 <kernel_main+0x4c>
Disassembly of section .data:
00008058 <__data_start>:
8058: 00000020 andeq r0, r0, r0, lsr #32
805c: 00000000 andeq r0, r0, r0
8060: 00038041 andeq r8, r3, r1, asr #32
8064: 00000008 andeq r0, r0, r8
8068: 00000000 andeq r0, r0, r0
806c: 00000082 andeq r0, r0, r2, lsl #1
8070: 00000001 andeq r0, r0, r1
8074: 00000000 andeq r0, r0, r0
Disassembly of section .ARM.attributes:
00000000 <_stack-0x80021>:
0: 00002e41 andeq r2, r0, r1, asr #28
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 00000024 andeq r0, r0, r4, lsr #32
10: 412d3805 ; <UNDEFINED> instruction: 0x412d3805
14: 070e0600 streq r0, [lr, -r0, lsl #12]
18: 09010841 stmdbeq r1, {r0, r6, fp}
1c: 14041202 strne r1, [r4], #-514 ; 0xfffffdfe
20: 17011501 strne r1, [r1, -r1, lsl #10]
24: 1a011803 bne 46038 <__bss_end__+0x3dfc0>
28: 2a012201 bcs 48834 <__bss_end__+0x407bc>
2c: Address 0x000000000000002c is out of bounds.
Disassembly of section .comment:
00000000 <.comment>:
0: 3a434347 bcc 10d0d24 <_stack+0x1050d03>
4: 35312820 ldrcc r2, [r1, #-2080]! ; 0xfffff7e0
8: 392e343a stmdbcc lr!, {r1, r3, r4, r5, sl, ip, sp}
c: 732b332e ; <UNDEFINED> instruction: 0x732b332e
10: 33326e76 teqcc r2, #1888 ; 0x760
14: 37373131 ; <UNDEFINED> instruction: 0x37373131
18: 2029312d eorcs r3, r9, sp, lsr #2
1c: 2e392e34 mrccs 14, 1, r2, cr9, cr4, {1}
20: 30322033 eorscc r2, r2, r3, lsr r0
24: 35303531 ldrcc r3, [r0, #-1329]! ; 0xfffffacf
28: 28203932 stmdacs r0!, {r1, r4, r5, r8, fp, ip, sp}
2c: 72657270 rsbvc r7, r5, #112, 4
30: 61656c65 cmnvs r5, r5, ror #24
34: 00296573 eoreq r6, r9, r3, ror r5
kernel8.img
12345678
00000800
00080264
00000000
12345678
kernel8-32.img
12345678
00008320
00008224
200001DA
12345678
kernel7.img
12345678
00000700
00008224
200001DA
12345678
kernel.img
12345678
00000000
00008224
200001DA
12345678
when I wrote and posted this code this is what I got so if you name your file kernel.img then 0x8000 is your entry point the answer I gave in your other SO question is a complete raspberry pi starting point. You can simply add your mailbox stuff, although if you are struggling with this I thing the mailbox and video are not where you should start IMO.
if you name the file kernel8.img then the entry point is 0x80000 change the linker script to match.
I have a serial port based bootloader you can use to save on the sd card dance, can get a long way with that then simply use the binary version of what you are creating to write to the flash once your application is working.
EDIT
Okay this is incredibly disgusting and by posting it here maybe that means you cant use it in your classwork...you should really do this right and not use inline assembly for your bootstrap...
so.c
asm(
".globl _start\n"
"_start:\n"
"mov sp,#0x8000\n"
"bl centry\n"
"b .\n"
);
unsigned int centry ( void )
{
return(5);
}
build
arm-none-eabi-gcc -O2 -c so.c -o so.o
arm-none-eabi-ld -Ttext=0x8000 so.o -o so.elf
arm-none-eabi-objdump -D so.elf > so.list
arm-none-eabi-objcopy so.elf -O binary kernel.img
examine
Disassembly of section .text:
00008000 <_start>:
8000: e3a0d902 mov sp, #32768 ; 0x8000
8004: eb000000 bl 800c <centry>
8008: eafffffe b 8008 <_start+0x8>
0000800c <centry>:
800c: e3a00005 mov r0, #5
8010: e12fff1e bx lr
A complete raspberry pi C with bootstrap example that will work on any of the flavors of pi (so far as I know they might have changed the GPU bootloader in the last few months but assume the didnt).
There are a couple of things I see wrong here. The most obvious ones are:
You aren't leaving anything at address 0, so the CPU is left executing blank memory at startup. You need to put something (like a branch instruction!) at 0x0.
On ARM Cortex-A, the stack pointer is not initialized at startup. You have to initialize it yourself in _start -- which means you will need to write that function in assembly.
First, cudos to old timer for his patience helping me.
The mistakes were:
Wrong entry point for the program, fixed by creating an assembly file with the label _start to set the stack pointer and using the linker to put the init section at address 0x8000
The compilation line itself was also wrong, it was missing a -c argument