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)
- Double-Word (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
The MIPS32® CPU ALU is designed to operate on 32-bit 2's complement numbers.
The CPU provides 2 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.
Because C ignores overflow, MIPS C compilers will 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 half-word 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 an int (32-bit integer on PIC32).
The MIPS32® CPU provides a pair of 32-bit registers to contain the 64-bit product, called HI and LO. (CPUs with the MIPS® DSP ASE Extension (such as PIC32MZ) provide an additional 3 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.
The MIPS32® CPU uses the same HI and LO registers for both multiply and divide operations. HI contains the remainder, and LO 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 0. 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 2's complement integer is viewed as a fraction representing real numbers between -1 and 1 (well, nearly 1)
- Q15: whereby a signed 16-bit 2's complement integer is viewed as a fraction representing real numbers between -1 and 1
- Q7: whereby a signed 8-bit 2'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 3 accumulator registers (ac0-ac3 in total) that become operands for multiply-accumulate operations.
MIPS® DSP ASE is fully documented in MIPS® Architecture for Programmers Volume IV-e: The MIPS® DSP Module for the MIPS32® Architecture (Document MD00374-2B-MIPS32DSP-AFB-02.41)
Floating-point Unit Data Types
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)