Section 16.7 Floating-Point Hardware
Floating-point operations can be implemented in software, but the ARM processor in the Raspberry Pi includes floating-point hardware. Early models (Table 8.0.1) include a Coprocessor that provides additional registers and the capability to perform floating-point operations on values in those registers. The ARM Cortex A-53 used in the Raspberry Pi 3 B includes floating-point hardware in the main CPU. As with integer operation differences between AARCH32 and AARCH64, there are some differences between the coprocessor and built-in floating-point instructions. Both use the IEEE-754 32-bit and 64-bit storage formats. The AARCH32 architecture defines a Vector Floating-point subarchitecture. The versions used in the Raspberry Pi are shown in Table 16.7.1. It has thirty-two 32-bit registers,s0
—s31
. Each register can hold one float
. These registers can be used in pairs for double
(64-bit) operations, as shown in Table 16.7.2. Note that the pairing is not arbitrary. For example, registers s0
and s1
can be paired and called d0
, but s1
cannot be paired with s2
.
Raspberry Pi | ARM CPU | VFP version |
Pi Zero | ||
Pi 1 A+ | ARM1176JZFS | VFPv2 |
Pi 1 B+ | ||
Pi 2 B | Cortex-A7 | VFPv4 |
Pi 3 B | Cortex-A53 | VFPv4 |
Float | Double | ||
Bank | Name | Name | Usage |
0 |
s0 —s7
|
d0 —d3
|
Scalar |
1 |
s8 —s15
|
d4 —d7
|
Vectorial |
2 |
s16 —s23
|
d8 —d11
|
Vectorial |
3 |
s24 —s31
|
d12 —d15
|
Vectorial |
float
s.
/* * addFloat1.c * Add two floats. * 2017-09-29: Bob Plantz */ #include <stdio.h> int main() { float x = 1.23; float y = 4.56; float z; z = x + y; printf("%f + %f = %f\n", x, y, z); return 0; }
float
s. (C)gcc
compiled the C code in Listing 16.7.3.
.arch armv6 .file "addFloat1.c" .section .rodata .align 2 .LC0: .ascii "%f + %f = %f\012\000" .text .align 2 .global main .syntax unified .arm .fpu vfp .type main, %function main: @ args = 0, pretend = 0, frame = 16 @ frame_needed = 1, uses_anonymous_args = 0 push {fp, lr} add fp, sp, #4 sub sp, sp, #32 ldr r3, .L3 str r3, [fp, #-8] @ float ldr r3, .L3+4 str r3, [fp, #-12] @ float vldr.32 s14, [fp, #-8] @@ load x into fp reg vldr.32 s15, [fp, #-12] @@ load y into fp reg vadd.f32 s15, s14, s15 @@ fp add vstr.32 s15, [fp, #-16] vldr.32 s15, [fp, #-8] vcvt.f64.f32 d5, s15 @@ convert x to double vldr.32 s15, [fp, #-12] vcvt.f64.f32 d7, s15 @@ convert y to double vldr.32 s13, [fp, #-16] vcvt.f64.f32 d6, s13 @@ convert z to double vstr.64 d6, [sp, #8] @@ pass z on stack vstr.64 d7, [sp] @@ pass y on stack vmov r2, r3, d5 @@ pass x in r2/r3 ldr r0, .L3+8 @@ pointer to format string bl printf mov r3, #0 mov r0, r3 sub sp, fp, #4 @ sp needed pop {fp, pc} .L4: .align 2 .L3: .word 1067282596 .word 1083304837 .word .LC0 .ident "GCC: (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516"
float
s. (gcc asm)gcc
compiler generates pre-UAL assembly language. The differences between this and the UAL syntax are greatest with the floating-point instructions, so we will go directly to my solution of this problem in Listing 16.7.5, which uses the UAL syntax.
@ addFloat2.s @ Adds two floats and prints results @ 2017-09-29: Bob Plantz @ Define my Raspberry Pi .cpu cortex-a53 .fpu neon-fp-armv8 .syntax unified @ modern syntax @ Constants for assembler .equ arg3,0 @ args to printf .equ arg4,8 .equ argSpace,16 @ Constants for assembler .section .rodata .align 2 format: .asciz "%f + %f = %f\n" @ The program .text .align 2 .global main .type main, %function main: sub sp, sp, 24 @ space for saving regs @ (keeping 8-byte sp align) str r4, [sp, 4] @ save r4 str r5, [sp, 8] @ r5 str r6, [sp,12] @ r6 str fp, [sp, 16] @ fp str lr, [sp, 20] @ lr add fp, sp, 20 @ set our frame pointer sub sp, sp, argSpace @ room to pass args vldr s0, x @ load x into fp reg vldr s1, y @ load y into fp reg vadd.f32 s2, s1, s0 @ fp add ldr r0, formatAddr @ pointer to format string vcvt.f64.f32 d5, s0 @ convert x to double vmov r2, r3, d5 @ pass x in r2/r3 vcvt.f64.f32 d6, s1 @ convert y to double vstr d6, [sp, arg3] @ pass y on stack vcvt.f64.f32 d7, s2 @ convert z to double vstr d7, [sp, arg4] @ pass z on stack bl printf mov r0, 0 add sp, sp, argSpace @ deallocate arguments ldr r4, [sp, 4] @ restore r4 ldr r5, [sp, 8] @ r5 ldr r6, [sp,12] @ r6 ldr fp, [sp, 16] @ fp ldr lr, [sp, 20] @ lr add sp, sp, 24 @ sp bx lr @ return .align 2 x: .float 1.23 y: .float 4.56 formatAddr: .word format
float
s. (prog asm)VADD
-
Adds two floats or two doubles.
VADD{<c>}.F32 {<Sd>,} <Sn>, <Sm> % float VADD{<c>}.F64 {<Dd>,} <Dn>, <Dm> % double
<c>
is the condition code, Table 9.2.1.<Sd>
and<Dd>
are the destination registers, and<Sm>
and<Sn>
,<Dm>
and<Dn>
are the source registers.
All numbers are stored in IEEE 754 format. In the “float” form, the value in
<Sm>
is added to the 32-bit value in<Sn>
and the result is stored in<Sd>
. In the “double” form, the 64-bit value in<Dm>
is added to the value in<Dn>
and the result is stored in<Dd>
. VCVT
-
Converts between a float and a double.
VCVT{<c>}.F32.F64 <Sd>, <Dm> % double to float VCVT{<c>}.F64.F32 <Dd>, <Sm> % float to double
<c>
is the condition code, Table 9.2.1.<Sd>
and<Dd>
are the destination registers, and<Sm>
and<Dm>
and<Dn>
are the source registers.
All numbers are stored in IEEE 754 format. In the “double to float” form, the 64-bit value in
<Dm>
is converted to a 32-bit value and stored in<Sd>
. In the “float to double” form, the 32-bit value in<Sm>
is converted to a 64-bit value and stored in<Dd>
. VLDR
-
Loads a value from memory into a floating-point register.
VLDR{<c>} <Sd>, <label> % float VLDR{<c>} <Dd>, <label> % double
<c>
is the condition code, Table 9.2.1.<Sd>
and<Dd>
are the destination registers.<label>
is a programmer-labeled memory address. The labeled address must be within bytes of the location of this instruction.
In the “float” form, the 32-bit value stored at the memory location is loaded into
<Sd>
. In the “double” form, the 64-bit value stored at the memory location is loaded into<Dd>
. No format conversions are made. VSTR
-
Stores a value in a floating-point register in memory.
VSTR{<c>} <Sd>, <label> % float VSTR{<c>} <Dd>, <label> % double
<c>
is the condition code, Table 9.2.1.<Sd>
and<Dd>
are the source registers.<label>
is a programmer-labeled memory address. The labeled address must be within bytes of the location of this instruction.
In the “float” form, the 32-bit value in
<Sd>
is stored at the memory location. In the “double” form, the 64-bit value in<Dd>
is stored at the memory location. No format conversions are made. VMOV
-
Transfers a 64-bit value between a floating point register and two integer registers.
VMOV{<c>} <Dm>, <Rt>, <Rt2> % to double VMOV{<c>} <Rt>, <Rt2>, <Dm> % to int
<c>
is the condition code, Table 9.2.1.<Dm>
is the floating point register.<Rt>
is the low-order 32-bit portion and<Rt2>
is the high-order 32-bit portion in the integer registers.
In the “to double” form, the two 32-bit values in
<Rt2>
and<Rt>
are copied into<Dm>
. In the “to int” form, the 64-bit value in<Dm>
is copied into the<Rt2>
and<Rt>
registers. No format conversions are made.