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 |
The VFP registers are arranged in four banks. Bank 0 is scalar, and banks 1–3 are vectorial. The differences between the banks come into play when doing vector computations, which are not covered in this book.
Listing 16.7.3 shows the addition of two float
s.
Listing 16.7.4 shows how the gcc
compiled the C code in Listing 16.7.3.
As pointed out earlier in this book, the 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.
The program in Listing 16.7.5 introduces five floating-point instructions.
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 \(\pm 1,020\) 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 \(\pm 1,020\) 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.