Data Types in Registers and Memory
MIPS32® Central Processing Units (CPUs) define the following data formats:
- Bit (suffix b)
- Byte (8 bits, suffix B or b)
- Halfword (16 bits, suffix H or h)
- Word (32 bits, suffix W or w)
- Doubleword (64 bits, suffix D or d - available in implementations that include a 64-bit floating point unit)
These CPUs can load or store between 1-4 bytes in a single operation.
Integer Data Types
Byte and halfword loads come in two flavors: sign-extending and zero-extending.
Sign-extending instructions, lb and lh, load the value into the least significant bits of the 32-bit register but fill the high-order bits by copying the sign bit (bit 7 of a byte, bit 15 of a halfword).
Unsigned, zero-extending instructions, lbu and lbh, zero-extend the data; they load the value into the least significant bits of a 32-bit register and fill the high-order bits with zeros.
For example, if the byte-wide memory location whose address is in t1 contains the value 0xFE (-2, or 254 if interpreted as unsigned), then:
will leave t2 holding the value 0xFFFFFFFE (-2 as signed 32-bit value) and t3 holding the value 0x000000FE (254 as signed or unsigned 32-bit value).
Data Types in Arithmetic Operations
Addition/Subtraction
The MIPS32 CPU Arithmetic Logic Unit (ALU) is designed to operate on 32-bit two's complement numbers.
The CPU provides two kinds of arithmetic instructions to deal with potential overflow in arithmetic operations:
- Add (add), add immediate (addi), and subtract (sub) cause exceptions on overflow.
- Add unsigned (addu), add immediate unsigned (addiu), and subtract unsigned (subu) do not cause exceptions on overflow.
Note: Because C ignores overflow, MIPS C compilers always generate the unsigned versions of the arithmetic instructions addu, addiu, subu no matter what the type of the variables.
There are no byte or halfword arithmetic operations. Where a C program explicitly does arithmetic as short or char, a MIPS compiler must insert extra code to make sure that the results wrap and overflow as they would on a native 8- or 16-bit machine.
When porting code that uses small integer variables to a PIC32 MCU, you should consider identifying variables which can safely be changed to a int (32-bit integer on PIC32).
Multiplication
The MIPS32 CPU provides a pair of 32-bit registers to contain the 64-bit product, called HI and LO registers. (CPUs with the MIPS Digital Signal Processing (DSP) Application-Specific Extension (ASE), such as PIC32MZ, provide an additional three HI/LO accumulators).
To produce a properly signed or unsigned product, MIPS has two instructions:
- Multiply (mult)
- Multiply unsigned (multu)
To fetch the integer 64-bit result, the programmer uses move from hi (mfhi) and move from lo (mflo) instructions.
Both MIPS multiply instructions ignore overflow, so it is up to the software to check to see if the product is too big to fit in 32-bits.
Division
The MIPS32 CPU uses the same HI and LO registers for both multiply and divide operations. The HI register contains the remainder, and the LO register contains the quotient after the divide instruction completes. To fetch these results, the programmer uses move from hi (mfhi) and move from lo (mflo) instructions.
To handle both signed integers and unsigned integers, MIPS has two instructions:
- Divide (div)
- Divide unsigned (divu)
MIPS divide instructions ignore overflow, so it is up to the software to check to see if the quotient is too large. In addition to overflow, division can also result in an improper calculation: division by zero. Modern MIPS32 implementations (PIC32MX/MZ) provide an option to enable a trap exception in this case.
Fixed-point Fractional Data Types
The MIPS DSP ASE introduces several new data types often used for digital signal processing applications:
- Q31: whereby a signed 32-bit two's complement integer is viewed as a fraction representing real numbers between -1 and 1 (well, nearly 1)
- Q15: whereby a signed 16-bit two's complement integer is viewed as a fraction representing real numbers between -1 and 1
- Q7: whereby a signed 8-bit two's complement integer is viewed as a fraction representing real numbers between -1 and 1 (8-bit data types are often used in video DSP algorithms)
This article provides a good introduction to fixed-point arithmetic, fixed-point fractional data types, as well as the Qm.n notation.
The MIPS DSP ASE also introduces a bundle of new features for working with these data types:
- Saturating arithmetic: Software-overflow checks are too slow for DSP algorithms. The DSP ASE provides saturating arithmetic operations that mimic real analog circuit behavior.
- Fractional Multiplication: Q31 x Q31 gives a Q62 result, having an extra sign bit. The ASE provides fractional multiply operations that automatically left-shift-by-1 producing a convenient Q63 result. Q15 fractional multiply operations perform the same function, creating Q31 results.
- Rounding options: The ASE provides a variety of rounding options to enable unbiased rounding and produce better approximations when writing full precision (64-bit) results into lower precision (32-bit) registers or memory.
- Multiply-Accumulate operations: The traditional HI/LO register pair is renamed ac0, and the ASE provides an additional three accumulator registers (ac0-ac3 in total) that become operands for multiply-accumulate operations.
MIPS DSP ASE Revision 2 DSP instructions are fully documented in "MIPS® Architecture for Programmers Volume IV-e: MIPS® DSP Module for MIPS32™ Architecture".
Floating-point Unit Data Types
The MIPS64® FPU supports the single-precision and double-precision floating point data types as specified by the IEEE 754 Standard.
In addition, the FPU provides two fixed-point data types which are the signed integers that are provided by the MIPS architecture:
- 32-bit Word Fixed Point Format (type W)
- 64-bit Long Word Fixed Point Format (type L)
For more information on the PIC32 FPU data types, please refer to chapter 12 (50.12) in the PIC32MZ family reference manual "Section 50. CPU for Devices with MIPS32® microAptiv™ and M-Class Cores".