Hi,
i have observed a problem with sprintf and double on a ARM
Cortex-M3-based microcontroller (the Stellaris LM3S8962 Evaluation
Kit) using "Sourcery G++ Lite 2009q1-161".
In short: the modified Hello-World-Example from Stellaris
int main(void)
{
char s[32];
double d = 1.2;
// Set the clocking to run directly from the crystal.
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_8MHZ);
// Initialize the OLED display.
RIT128x96x4Init(1000000);
RIT128x96x4StringDraw("Hello World!", 0, 0, 15);
sprintf(s, "%d", (int)d);
RIT128x96x4StringDraw(s, 0, 10, 15);
sprintf(s, "%f", d);
RIT128x96x4StringDraw(s, 0, 20, 15);
sprintf(s, "%e", d);
RIT128x96x4StringDraw(s, 0, 30, 15);
while(1) {
}
} produces the following output:
Hello World!
1
0.000000
1.397369e-76
Is someone successfully using sprintf with double on a cortex-m3
hardware?
Any suggestions what i am doing wrong?
I'm building on a ubuntu 8.04 Linux 32bit machine, with the following
commands (the output of a verbose build can be found in the attached
build_output_verbose.txt file):
arm-none-eabi-gcc -mthumb -mcpu=cortex-m3 -Os -ffunction-sections
-fdata-sections -MD -std=c99 -Wall -pedantic -DPART_LM3S8962 -c -I..
-Dgcc -o gcc/hello.o hello.c
arm-none-eabi-gcc -mthumb -mcpu=cortex-m3 -Os -ffunction-sections
-fdata-sections -MD -std=c99 -Wall -pedantic -DPART_LM3S8962 -c -I..
-Dgcc -o gcc/syscalls.o syscalls.c
arm-none-eabi-gcc -mthumb -mcpu=cortex-m3 -Os -ffunction-sections
-fdata-sections -MD -std=c99 -Wall -pedantic -DPART_LM3S8962 -c -I..
-Dgcc -o gcc/rit128x96x4.o rit128x96x4.c
arm-none-eabi-gcc -mthumb -mcpu=cortex-m3 -Os -ffunction-sections
-fdata-sections -MD -std=c99 -Wall -pedantic -DPART_LM3S8962 -c -I..
-Dgcc -o gcc/startup_gcc.o startup_gcc.c
arm-none-eabi-ld -T hello.ld --entry ResetISR --gc-sections -o
gcc/hello.axf gcc/hello.o gcc/syscalls.o gcc/rit128x96x4.o
gcc/startup_gcc.o ../driverlib/gcc/libdriver.a
/opt/CodeSourcery/Sourcery_Lite/bin/../lib/gcc/arm-none-eabi/4.3.3/../../../../arm-none-eabi/lib/thumb2/libc.a
/opt/CodeSourcery/Sourcery_Lite/bin/../lib/gcc/arm-none-eabi/4.3.3/thumb2/libgcc.a
Files used for building:
----------------------
hello.c
----------------------
#include <stdio.h>
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "drivers/rit128x96x4.h"
int main(void)
{
char s[32];
double d = 1.2;
// Set the clocking to run directly from the crystal.
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
SYSCTL_XTAL_8MHZ);
// Initialize the OLED display.
RIT128x96x4Init(1000000);
RIT128x96x4StringDraw("Hello World!", 0, 0, 15);
sprintf(s, "%d", (int)d);
RIT128x96x4StringDraw(s, 0, 10, 15);
sprintf(s, "%f", d);
RIT128x96x4StringDraw(s, 0, 20, 15);
sprintf(s, "%e", d);
RIT128x96x4StringDraw(s, 0, 30, 15);
while(1) {
}
}
----------------------
hello.ld
----------------------
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00010000
}
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} >FLASH
_flash_data = ADDR(.text) + SIZEOF(.text) + SIZEOF(.ARM.exidx);
.data : AT(_flash_data)
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
. = ALIGN (8);
_end = .;
} > SRAM
}
PROVIDE(_heap = _end);
PROVIDE(_eheap = ALIGN(ORIGIN(SRAM) + LENGTH(SRAM) - 8 ,8));
----------------------
syscalls.c
----------------------
#include <sys/types.h>
extern unsigned long _heap;
extern unsigned long _eheap;
static caddr_t heap = NULL;
caddr_t _sbrk(int incr)
{
caddr_t prevHeap;
caddr_t nextHeap;
if(heap == NULL) heap = (caddr_t) &_heap;
prevHeap = heap;
nextHeap = prevHeap + incr;
if(nextHeap >= (caddr_t) &_eheap) return (caddr_t) -1; // error -
no more memory
else {
heap = nextHeap;
return prevHeap;
}
}
----------------------
startup_gcc.c
----------------------
void ResetISR(void);
static void NmiSR(void);
static void FaultISR(void);
static void IntDefaultHandler(void);
extern int main(void);
static unsigned long pulStack[512];
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
// The initial stack pointer
ResetISR, // The reset handler
NmiSR, // The NMI handler
FaultISR, // The hard fault handler
IntDefaultHandler, // The MPU fault handler
IntDefaultHandler, // The bus fault handler
IntDefaultHandler, // The usage fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
IntDefaultHandler, // SVCall handler
IntDefaultHandler, // Debug monitor handler
0, // Reserved
IntDefaultHandler, // The PendSV handler
IntDefaultHandler, // The SysTick handler
IntDefaultHandler, // GPIO Port A
IntDefaultHandler, // GPIO Port B
IntDefaultHandler, // GPIO Port C
IntDefaultHandler, // GPIO Port D
IntDefaultHandler, // GPIO Port E
IntDefaultHandler, // UART0 Rx and Tx
IntDefaultHandler, // UART1 Rx and Tx
IntDefaultHandler, // SSI0 Rx and Tx
IntDefaultHandler, // I2C0 Master and Slave
IntDefaultHandler, // PWM Fault
IntDefaultHandler, // PWM Generator 0
IntDefaultHandler, // PWM Generator 1
IntDefaultHandler, // PWM Generator 2
IntDefaultHandler, // Quadrature Encoder 0
IntDefaultHandler, // ADC Sequence 0
IntDefaultHandler, // ADC Sequence 1
IntDefaultHandler, // ADC Sequence 2
IntDefaultHandler, // ADC Sequence 3
IntDefaultHandler, // Watchdog timer
IntDefaultHandler, // Timer 0 subtimer A
IntDefaultHandler, // Timer 0 subtimer B
IntDefaultHandler, // Timer 1 subtimer A
IntDefaultHandler, // Timer 1 subtimer B
IntDefaultHandler, // Timer 2 subtimer A
IntDefaultHandler, // Timer 2 subtimer B
IntDefaultHandler, // Analog Comparator 0
IntDefaultHandler, // Analog Comparator 1
IntDefaultHandler, // Analog Comparator 2
IntDefaultHandler, // System Control (PLL,
OSC, BO)
IntDefaultHandler, // FLASH Control
IntDefaultHandler, // GPIO Port F
IntDefaultHandler, // GPIO Port G
IntDefaultHandler, // GPIO Port H
IntDefaultHandler, // UART2 Rx and Tx
IntDefaultHandler, // SSI1 Rx and Tx
IntDefaultHandler, // Timer 3 subtimer A
IntDefaultHandler, // Timer 3 subtimer B
IntDefaultHandler, // I2C1 Master and Slave
IntDefaultHandler, // Quadrature Encoder 1
IntDefaultHandler, // CAN0
IntDefaultHandler, // CAN1
IntDefaultHandler, // CAN2
IntDefaultHandler, // Ethernet
IntDefaultHandler // Hibernate
};
extern unsigned long _etext;
extern unsigned long _flash_data;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
void ResetISR(void)
{
unsigned long *pulSrc, *pulDest;
// Copy the data segment initializers from flash to SRAM.
pulSrc = &_flash_data;
for(pulDest = &_data; pulDest < &_edata; )
{
*pulDest++ = *pulSrc++;
}
// Zero fill the bss segment. This is done with inline assembly
since this
// will clear the value of pulDest if it is not kept in a register.
__asm(" ldr r0, =_bss\n"
" ldr r1, =_ebss\n"
" mov r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
" cmp r0, r1\n"
" it lt\n"
" strlt r2, [r0], #4\n"
" blt zero_loop");
// Call the application's entry point.
main();
}
static void NmiSR(void) { while(1); }
static void FaultISR(void) { while(1); }
static void IntDefaultHandler(void) { while(1); }