Floating point in bare metal code


12 posts
by valtonia » Sat Jul 14, 2012 4:09 pm
Just thought I'd post this up as it may save others some time when trying to get floating point working in their bare metal projects.

Assuming you are using the arm-bcm2708hardfp-linux-gnueabi-* tools (used for building the Linux kernel) and you're linking against libgcc, you:
  • need to add -mhard-float and -mfpu=vfp to your build flags (actually -mhard-float is the default)
  • need to ensure that both .c and .s files are compiled using the same flags. Usually, Makefiles will use as directly for .s files, but if you just use gcc with exactly the same options as for .c files, it will figure out what it needs to do
  • need to include the following code in your .s startup file, before you jump to your 'main'

Code: Select all
@ enable the FPU
mrc p15, 0, r0, c1, c0, 2
orr r0, r0, #0x300000            /* single precision */
orr r0, r0, #0xC00000            /* double precision */
mcr p15, 0, r0, c1, c0, 2
mov r0, #0x40000000
fmxr fpexc,r0


(thanks go to DWelch for this code)

You should now be able to use float types and floating point math in your c code without triggering undefined instruction exceptions.

V
Posts: 26
Joined: Wed Jul 04, 2012 9:09 pm
by Cycl0ne » Sat Jul 14, 2012 4:40 pm
Question:

FPU uses new registers? When going into a "task switch" do i need to take care about?

What about division 0 ? Are new Traps enabled? ..
User avatar
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am
by valtonia » Sat Jul 14, 2012 4:54 pm
@Cycl0ne

Full details of the VFP FPU are here:

http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301g/DDI0301G_arm1176jzfs_r0p7_trm.pdf

I haven't read that much into it yet - will update here when I know more.

V.
Posts: 26
Joined: Wed Jul 04, 2012 9:09 pm
by teh_orph » Sat Jul 14, 2012 7:13 pm
Just to clarify, is this for programming sans-Linux (or other OS)? Surely you can't do that from user mode...
User avatar
Posts: 346
Joined: Mon Jan 30, 2012 2:09 pm
Location: London
by DexOS » Sat Jul 14, 2012 7:36 pm
teh_orph wrote:Just to clarify, is this for programming sans-Linux (or other OS)? Surely you can't do that from user mode...

Bare metal means without a OS, so linux would not be running.
Batteries not included, Some assembly required.
User avatar
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
by dwelch67 » Sun Jul 15, 2012 3:06 am
Yes the floating point unit has its own set of registers.
Posts: 690
Joined: Sat May 26, 2012 5:32 pm
by tufty » Wed Jul 18, 2012 8:23 pm
dwelch67 wrote:Yes the floating point unit has its own set of registers.

...and it means you need to deal with a whole bunch of other crap when you do a context switch.
Posts: 1454
Joined: Sun Sep 11, 2011 2:32 pm
by Cycl0ne » Wed Jul 18, 2012 8:54 pm
Exactly :) BUT .. i would suggest:

- this is a Co Processor. add a flag into your task structure "bool fpu". The task has to get the resource FPU, set it to true, and only if you see a task with true, take the 32 registers (16 double/32 single) with you.

and if you only have one task with true, never save them ;) this task is the only one using it.

Another thing would be to add a sort of FPU device, so you dont use the registers directly, only through this "device" . then you never need to save ;-) and the device queues the io from the tasks requesting the fpu.
User avatar
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am
by DexOS » Wed Jul 18, 2012 9:25 pm
This R-PI bare metal demo uses HW floating point in its code.
viewtopic.php?p=115987#p115987

Also, you could just stick to single tasking ;) .
Batteries not included, Some assembly required.
User avatar
Posts: 876
Joined: Wed May 16, 2012 6:32 pm
by mrvn » Thu Jan 24, 2013 9:22 pm
Cycl0ne wrote:Exactly :) BUT .. i would suggest:

- this is a Co Processor. add a flag into your task structure "bool fpu". The task has to get the resource FPU, set it to true, and only if you see a task with true, take the 32 registers (16 double/32 single) with you.


How do you prevent from using the fpu without first getting it? On x86/x86_64 the hardware has a flag for it that you clear on every context switch and then you get an exception on use and can save the old fpu context and restore the current threads one.

But how do you do that on arm?

Mrvn
Posts: 58
Joined: Wed Jan 09, 2013 6:50 pm
by rurwin » Thu Jan 24, 2013 9:59 pm
This brings back memories of using Forth under MSDOS. Saving FP state on a context switch was left as an exercise for the interested programmer. The solution generally was not to switch context with any partial results in the FP processor, and not to assume its state on return.
User avatar
Forum Moderator
Forum Moderator
Posts: 4166
Joined: Mon Jan 09, 2012 3:16 pm
by BrianW » Thu Jan 24, 2013 11:16 pm
Cycl0ne wrote:- this is a Co Processor. add a flag into your task structure "bool fpu". The task has to get the resource FPU, set it to true, and only if you see a task with true, take the 32 registers (16 double/32 single) with you.

Presumably you could set this flag automatically. Disable the FPU when running tasks with 'fpu' set to false. Any FPU instructions will trigger an exception. When you get this exception, set 'fpu' to true for that task, turn on the FPU and allow the instruction to execute. Then save/restore the FPU registers on context switch, and disable the FPU when switching to a non-'fpu' task
Posts: 83
Joined: Sun Jul 29, 2012 9:03 pm