linux-sunxi / u-boot-sunxi

Allwinner A1x native u-boot support
https://github.com/linux-sunxi/u-boot-sunxi/wiki
316 stars 332 forks source link

baremetal build with standard libraries #87

Open mMerlin opened 8 years ago

mMerlin commented 8 years ago

Can someone help me out please! I am out of my previous areas here, and do not know the right question to ask, or the right place to ask it. What I currently want, is to create a program that will run standalone (no OS) on a A20-OLinuXino-Micro-4GB board, that needs to use (at least) some standard math libraries. Eventually, I will want to load it into NAND, and run it on powerup, but for now I am trying to manually load it (loady) from the U-Boot serial 'console', after booting from an SD card. Standalone is needed, because the linux distro level access to the hardware GPIO ports seems not very flexible, when working with more than one bit at a time, and quite slow. Too slow for the target application, and I did not really want to try modifying / adding a kernal module just to see if that would be fast enough.

I have been able to create bootable U-Boot images on SD cards from the repo, either building directly from the linux-sunxi distro that was supplied with the board, or by cross-compiling from a Fedora 21 machine. Same for the standalone hello_world program that came in the examples for U-boot, which can be loaded and run from the U-Boot console. My current testing does not use any math library. Just stdio and printf, to try to get the build process right.

Starting from, and simplifying, the hello_world standalone example and Makefile provided with the U-Boot sources, I created a minimal program, and a script to use fairly minimal build options, that still runs successfully when loaded from U-Boot.

I created a standard hello_world program, plus a script to use as similar as possible options to the the U-Boot standalone example, while still being able to execute it normally from a linux shell.

I then tried to adjusted the build options to replicate the result from the initial standalone example program. That is, being able to load and run it from the U-Boot 'console'. So far without success. With the latest test, executing the program from the console fails with:

software interrupt
pc : [<48000008>]          lr : [<48000008>]
sp : 7fb66da8  ip : 00000000     fp : 00000000
r10: 00000002  r9 : 7fb66f0c     r8 : 7fb67778
r7 : 7ffbbaf8  r6 : 00000001     r5 : 7fb6777c  r4 : 48000000
r3 : 00000000  r2 : 7fb6777c     r1 : 7fb6777c  r0 : 00000001
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

Since the start point is (supposed to be) 0x48000000, that is right at the beginning.

For all cases, the procedure in the U-Boot console is:

loady
go 0x48000000

One of the current differences in the build paths, is the U-Boot environment is built with soft floating point. Changing to hard there fails because the needed libstubs.o is currently built with soft. Trying to build the standard program with soft in the linux environment complains about mismatch as well. Something to look at later.

Am I missing something obvious that is typically needed to build freestanding programs that are to run without and OS? Is it something that is specific to the ARM processor or board being used? Do I need to include something extra in the program to reference (again) something from the U-Boot code tree?

standalone hello_world.c for U-Boot environment

/*
 * Simple hello_world program to check creation of freestanding program to be run
 * standalone (without OS) from u-boot
 *
 * variant to check how the u-boot standalone programs are built
 */

#include <common.h>

int main (void)
{
    printf ("Hello World\n");
    return (0);
}

freestanding hello_world.c for sunxi environment

/*
 * Simple hello_world program to check creation of freestanding program to be run
 * standalone (without OS) from u-boot
 */

#include <stdio.h>

int main (void)
{
    printf ("Hello World\n");
    return (0);
}

build script for U-Boot environment

#! /bin/bash
HW="hello_world"

#clean
rm -f $HW $HW.o $HW.d $HW.s $HW.i $HW.su $HW.bin

FLOATA="soft"
INCLS="-I/home/olimex/u-boot-sunxi/include \
    -I/home/olimex/u-boot-sunxi/arch/arm/include"
OBJS="$HW.o \
    /home/olimex/u-boot-sunxi/examples/standalone/libstubs.o"
EGRP=
LPATHS=
LIBS=

LNKOPTS="-Ttext 0x48000000"
WARNINGS=
MACH="-marm -mfloat-abi=$FLOATA"
DIAG=
EMBD="-ffreestanding"
DEFS="-D__KERNEL__"
CMPOPTS=

set -x

#compile
gcc -c $CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \
    $HW.c -o $HW.o
echo

#link
ld.bfd -static $LNKOPTS $OBJS $LPATHS -o $HW $LIBS $EGRP
echo

#loadable binary
objcopy -O binary $HW $HW.bin

base build script for sunxi environment

#! /bin/bash
HW="hello_world"

#clean
rm -f $HW $HW.o $HW.d $HW.s $HW.i $HW.su $HW.bin

FLOATA="hard"
INCLS=
OBJS="/usr/lib/arm-linux-gnueabihf/crt1.o \
    /usr/lib/arm-linux-gnueabihf/crti.o \
    /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtbeginT.o \
    $HW.o"
EGRP="--end-group \
    /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtend.o \
    /usr/lib/arm-linux-gnueabihf/crtn.o"
LPATHS="-L/usr/lib/gcc/arm-linux-gnueabihf/4.6 \
    -L/usr/lib/arm-linux-gnueabihf \
    -L/usr/lib \
    -L/lib/arm-linux-gnueabihf \
    -L/usr/lib/arm-linux-gnueabihf"
LIBS="--start-group -lgcc -lgcc_eh -lc"

LNKOPTS="-Ttext 0x48000000"
WARNINGS=
MACH="-marm -mfloat-abi=$FLOATA"
DIAG=
EMBD="-ffreestanding"
DEFS="-D__KERNEL__"
CMPOPTS=

set -x

#compile
gcc -c $CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \
    $HW.c -o $HW.o
echo

#link
ld.bfd -static $LNKOPTS $OBJS $LPATHS -o $HW $LIBS $EGRP
echo

#loadable binary
objcopy -O binary $HW $HW.bin

current modified build script for sunxi, to run from U-Boot console

#! /bin/bash
HW="hello_world"

#clean
rm -f $HW $HW.o $HW.d $HW.s $HW.i $HW.su $HW.bin

FLOATA="hard"
INCLS=
OBJS="$HW.o"
EGRP=
LPATHS="-L/usr/lib/gcc/arm-linux-gnueabihf/4.6 \
    -L/usr/lib/arm-linux-gnueabihf \
    -L/usr/lib \
    -L/lib/arm-linux-gnueabihf \
    -L/usr/lib/arm-linux-gnueabihf"
LIBS="--start-group -lgcc -lgcc_eh -lc"

LNKOPTS="-Ttext 0x48000000"
WARNINGS=
MACH="-marm -mfloat-abi=$FLOATA"
DIAG=
EMBD="-ffreestanding"
DEFS="-D__KERNEL__"
CMPOPTS=

set -x

#compile
gcc -c $CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \
    $HW.c -o $HW.o
echo

#link
ld.bfd -static $LNKOPTS $OBJS $LPATHS -o $HW $LIBS $EGRP
echo

#loadable binary
objcopy -O binary $HW $HW.bin

output from script for the U-Boot environment

+ gcc -c -marm -mfloat-abi=soft -ffreestanding -D__KERNEL__ -I/home/olimex/u-boot-sunxi/include -I/home/olimex/u-boot-sunxi/arch/arm/include hello_world.c -o hello_world.o
+ echo
+ ld.bfd -static -Ttext 0x48000000 hello_world.o /home/olimex/u-boot-sunxi/examples/standalone/libstubs.o -o hello_world
ld.bfd: warning: cannot find entry symbol _start; defaulting to 48000000
+ echo
+ objcopy -O binary hello_world hello_world.bin

output from base script for the U-Boot environment

+ gcc -c -marm -mfloat-abi=hard -ffreestanding -D__KERNEL__ hello_world.c -o hello_world.o
+ echo
+ ld.bfd -static -Ttext 0x48000000 /usr/lib/arm-linux-gnueabihf/crt1.o /usr/lib/arm-linux-gnueabihf/crti.o /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtbeginT.o hello_world.o -L/usr/lib/gcc/arm-linux-gnueabihf/4.6 -L/usr/lib/arm-linux-gnueabihf -L/usr/lib -L/lib/arm-linux-gnueabihf -L/usr/lib/arm-linux-gnueabihf -o hello_world --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtend.o /usr/lib/arm-linux-gnueabihf/crtn.o
+ echo
+ objcopy -O binary hello_world hello_world.bin

output from modified script for the U-Boot environment

+ gcc -c -marm -mfloat-abi=hard -ffreestanding -D__KERNEL__ hello_world.c -o hello_world.o
+ echo
+ ld.bfd -static -Ttext 0x48000000 hello_world.o -L/usr/lib/gcc/arm-linux-gnueabihf/4.6 -L/usr/lib/arm-linux-gnueabihf -L/usr/lib -L/lib/arm-linux-gnueabihf -L/usr/lib/arm-linux-gnueabihf -o hello_world --start-group -lgcc -lgcc_eh -lc
ld.bfd: warning: cannot find entry symbol _start; defaulting to 48000000
+ echo
+ objcopy -O binary hello_world hello_world.bin

the compiler defaults: gcc -v

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.3-14' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf
Thread model: posix
gcc version 4.6.3 (Debian 4.6.3-14)
oliv3r commented 8 years ago

Hey Phil,

just a quick note, how fast do you want the pins to toggle? I think I once read somewhere that 300kHz is pretty much the max, though take that with a big grain of salt ;)

On 17-10-15 03:41, Phil Duby wrote:

Can someone help me out please! I am out of my previous areas here, and do not know the right question to ask, or the right place to ask it. What I currently want, is to create a program that will run standalone (no OS) on a A20-OLinuXino-Micro-4GB https://www.olimex.com/Products/OLinuXino/A20/A20-OLinuXino-MICRO-4GB/ board, that needs to use (at least) some standard math libraries. Eventually, I will want to load it into NAND, and run it on powerup, but for now I am trying to manually load it (loady) from the U-Boot https://github.com/linux-sunxi/u-boot-sunxi/wiki serial 'console', after booting from an SD card. Standalone is needed, because the linux distro level access to the hardware GPIO ports seems not very flexible, when working with more than one bit at a time, and quite slow. Too slow for the target application, and I did not really want to try modifying / adding a kernal module just to see if that would be fast enough.

I have been able to create bootable U-Boot images on SD cards from the repo, either building directly from the linux-sunxi http://linux-sunxi.org/Main_Page distro that was supplied with the board, or by cross-compiling from a Fedora 21 machine. Same for the standalone hello_world program that came in the examples for U-boot, which can be loaded and run from the U-Boot console. My current testing does not use any math library. Just stdio and printf, to try to get the build process right.

Starting from, and simplifying, the hello_world standalone example and Makefile provided with the U-Boot sources, I created a minimal program, and a script to use fairly minimal build options, that still runs successfully when loaded from U-Boot.

I created a standard hello_world program, plus a script to use as similar as possible options to the the U-Boot standalone example, while still being able to execute it normally from a linux shell.

I then tried to adjusted the build options to replicate the result from the initial standalone example program. That is, being able to load and run it from the U-Boot 'console'. So far without success. With the latest test, executing the program from the console fails with:

software interrupt pc : [<48000008>] lr : [<48000008>] sp : 7fb66da8 ip : 00000000 fp : 00000000 r10: 00000002 r9 : 7fb66f0c r8 : 7fb67778 r7 : 7ffbbaf8 r6 : 00000001 r5 : 7fb6777c r4 : 48000000 r3 : 00000000 r2 : 7fb6777c r1 : 7fb6777c r0 : 00000001 Flags: nZCv IRQs off FIQs off Mode SVC_32 Resetting CPU ...

Since the start point is (supposed to be) 0x48000000, that is right at the beginning.

For all cases, the procedure in the U-Boot console is:

loady go 0x48000000

One of the current differences in the build paths, is the U-Boot environment is built with soft floating point. Changing to hard there fails because the needed libstubs.o is currently built with soft. Trying to build the standard program with soft in the linux environment complains about mismatch as well. Something to look at later.

Am I missing something obvious that is typically needed to build freestanding programs that are to run without and OS? Is it something that is specific to the ARM processor or board being used? Do I need to include something extra in the program to reference (again) something from the U-Boot code tree?

  standalone hello_world.c for U-Boot environment

/*

  • Simple hello_world program to check creation of freestanding program to be run
  • standalone (without OS) from u-boot *
  • variant to check how the u-boot standalone programs are built */

include

int main (void) { printf ("Hello World\n"); return (0); }

  freestanding hello_world.c for sunxi environment

/*

  • Simple hello_world program to check creation of freestanding program to be run
  • standalone (without OS) from u-boot */

include

int main (void) { printf ("Hello World\n"); return (0); }

  build script for U-Boot environment

! /bin/bash

HW="hello_world"

clean

rm -f$HW $HW.o$HW.d$HW.s$HW.i$HW.su$HW.bin

FLOATA="soft" INCLS="-I/home/olimex/u-boot-sunxi/include\ -I/home/olimex/u-boot-sunxi/arch/arm/include" OBJS="$HW.o\ /home/olimex/u-boot-sunxi/examples/standalone/libstubs.o" EGRP= LPATHS= LIBS=

LNKOPTS="-Ttext 0x48000000" WARNINGS= MACH="-marm -mfloat-abi=$FLOATA" DIAG= EMBD="-ffreestanding" DEFS="-DKERNEL" CMPOPTS=

set -x

compile

gcc -c$CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \ $HW.c -o$HW.o echo

link

ld.bfd -static$LNKOPTS $OBJS $LPATHS -o$HW $LIBS $EGRP echo

loadable binary

objcopy -O binary$HW $HW.bin

  base build script for sunxi environment

! /bin/bash

HW="hello_world"

clean

rm -f$HW $HW.o$HW.d$HW.s$HW.i$HW.su$HW.bin

FLOATA="hard" INCLS= OBJS="/usr/lib/arm-linux-gnueabihf/crt1.o\ /usr/lib/arm-linux-gnueabihf/crti.o\ /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtbeginT.o\ $HW.o" EGRP="--end-group\ /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtend.o\ /usr/lib/arm-linux-gnueabihf/crtn.o" LPATHS="-L/usr/lib/gcc/arm-linux-gnueabihf/4.6\ -L/usr/lib/arm-linux-gnueabihf\ -L/usr/lib\ -L/lib/arm-linux-gnueabihf\ -L/usr/lib/arm-linux-gnueabihf" LIBS="--start-group -lgcc -lgcc_eh -lc"

LNKOPTS="-Ttext 0x48000000" WARNINGS= MACH="-marm -mfloat-abi=$FLOATA" DIAG= EMBD="-ffreestanding" DEFS="-DKERNEL" CMPOPTS=

set -x

compile

gcc -c$CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \ $HW.c -o$HW.o echo

link

ld.bfd -static$LNKOPTS $OBJS $LPATHS -o$HW $LIBS $EGRP echo

loadable binary

objcopy -O binary$HW $HW.bin

  current modified build script for sunxi, to run from U-Boot console

! /bin/bash

HW="hello_world"

clean

rm -f$HW $HW.o$HW.d$HW.s$HW.i$HW.su$HW.bin

FLOATA="hard" INCLS= OBJS="$HW.o" EGRP= LPATHS="-L/usr/lib/gcc/arm-linux-gnueabihf/4.6\ -L/usr/lib/arm-linux-gnueabihf\ -L/usr/lib\ -L/lib/arm-linux-gnueabihf\ -L/usr/lib/arm-linux-gnueabihf" LIBS="--start-group -lgcc -lgcc_eh -lc"

LNKOPTS="-Ttext 0x48000000" WARNINGS= MACH="-marm -mfloat-abi=$FLOATA" DIAG= EMBD="-ffreestanding" DEFS="-DKERNEL" CMPOPTS=

set -x

compile

gcc -c$CMPOPTS $MACH $EMBD $DIAG $WARNINGS $DEFS $INCLS \ $HW.c -o$HW.o echo

link

ld.bfd -static$LNKOPTS $OBJS $LPATHS -o$HW $LIBS $EGRP echo

loadable binary

objcopy -O binary$HW $HW.bin

  output from script for the U-Boot environment

|+ gcc -c -marm -mfloat-abi=soft -ffreestanding -DKERNEL -I/home/olimex/u-boot-sunxi/include -I/home/olimex/u-boot-sunxi/arch/arm/include hello_world.c -o hello_world.o

  • echo
  • ld.bfd -static -Ttext 0x48000000 hello_world.o /home/olimex/u-boot-sunxi/examples/standalone/libstubs.o -o hello_world ld.bfd: warning: cannot find entry symbol _start; defaulting to 48000000
  • echo
  • objcopy -O binary hello_world hello_world.bin |

    output from base script for the U-Boot environment

|+ gcc -c -marm -mfloat-abi=hard -ffreestanding -DKERNEL hello_world.c -o hello_world.o

  • echo
  • ld.bfd -static -Ttext 0x48000000 /usr/lib/arm-linux-gnueabihf/crt1.o /usr/lib/arm-linux-gnueabihf/crti.o /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtbeginT.o hello_world.o -L/usr/lib/gcc/arm-linux-gnueabihf/4.6 -L/usr/lib/arm-linux-gnueabihf -L/usr/lib -L/lib/arm-linux-gnueabihf -L/usr/lib/arm-linux-gnueabihf -o hello_world --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/arm-linux-gnueabihf/4.6/crtend.o /usr/lib/arm-linux-gnueabihf/crtn.o
  • echo
  • objcopy -O binary hello_world hello_world.bin |

    output from modified script for the U-Boot environment

|+ gcc -c -marm -mfloat-abi=hard -ffreestanding -DKERNEL hello_world.c -o hello_world.o

  • echo
  • ld.bfd -static -Ttext 0x48000000 hello_world.o -L/usr/lib/gcc/arm-linux-gnueabihf/4.6 -L/usr/lib/arm-linux-gnueabihf -L/usr/lib -L/lib/arm-linux-gnueabihf -L/usr/lib/arm-linux-gnueabihf -o hello_world --start-group -lgcc -lgcc_eh -lc ld.bfd: warning: cannot find entry symbol _start; defaulting to 48000000
  • echo
  • objcopy -O binary hello_world hello_world.bin |

    the compiler defaults: gcc -v

Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper Target: arm-linux-gnueabihf Configured with: ../src/configure -v --with-pkgversion='Debian 4.6.3-14' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.6 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --disable-sjlj-exceptions --with-arch=armv7-a --with-fpu=vfpv3-d16 --with-float=hard --with-mode=thumb --enable-checking=release --build=arm-linux-gnueabihf --host=arm-linux-gnueabihf --target=arm-linux-gnueabihf Thread model: posix gcc version 4.6.3 (Debian 4.6.3-14)

— Reply to this email directly or view it on GitHub https://github.com/linux-sunxi/u-boot-sunxi/issues/87.

Met vriendelijke groeten, Kind regards, 与亲切的问候

Olliver Schinagl Software Engineer Research & Development Ultimaker B.V.

mMerlin commented 8 years ago

I think that speed is talking to the ports through the linux interface. I found another reference that said you could get 4 MHz through bare metal. In my testing, I actually got double that. I expect that is because the context was for toggling a single pin (port in a port group), which requires a read, mask, write sequence. For my case, I can write every pin in the group in a single operation. Since it appears that (at the raw hardware level, every read/write memory access to the I/O port location requires (eyeballing on the scope) 62.5 ns, that extra read cuts the maximum toggle rate by half.

For my application, I need to get the overall cycle time in the 100KHz range, which includes control of pins on multiple ports, plus some reasonably intensive math. I've got the hardware pin control setup, actually limited by the access speed of an attached ADC. Now I need to get the math part to work along with that.

For the original query, I am slowly learning more. I found the hidden .{xxxx}.cmd files in …/examples/standalone, so I can recreate libstubs with different options (hardware floating point), but I do not understand what libstubs is doing to facilitate a minimal hello world building to (depending on optimization flags) a 316 byte .bin, while a normal hello world, compile/linked as freestanding and static is over 400K. I have to assume that libstubs is providing accessing to internals of U-Boot, but I have not seen how that works. continuing forward (and sideways).