lhu
lyszt.net covers lhu and other RISC-V instructions in more detail.
Encoding format
Description
lhu reads a halfword (16 bits, or 2 bytes) from memory and treats it as unsigned. It pairs with lh, and the two differ in how they fill the upper half of the destination register. Both read 16 bits with the same addressing. Halfwords and the addressing form are covered on the lh page. The form of lhu is shown below.
lhu rd, offset(rs1)
A register holds 32 bits, so a 16-bit load must fill the top 16. lh copies the sign bit across them, which keeps negative numbers negative. lhu fills them with 0s, called zero extension, so the result is always non-negative, from 0 to 65535. The u in the name marks the unsigned reading.
The data decides which one to use. Pick lh for values that can be negative and lhu for values that are never negative. The bit pattern 0xFFFF loaded with lhu is 65535, and the same bits loaded with lh are -1.
The RISC-V manual defines lhu as loading a 16-bit value from memory and zero-extending it to 32 bits before storing it in rd. The effective address is register rs1 plus the sign-extended 12-bit offset.
Most 16-bit data is unsigned. Character codes in some text encodings, port numbers and checksums in network data, 16-bit pixel formats and length fields in file formats are examples. Sign extension would give a wrong value for any of them once the top bit is set, so lhu is the right load. Halfwords sit 2 bytes apart, so loops over them move the address by 2.
Example
.data val: .half 0xffff .text la t0, val lhu t1, 0(t0) # t1 = 65535 (zero-extended)
