Instruction Set Overview Repertoire of instructions a computer understands. Different computers have different instruction sets, but with common aspects. Early and many modern computers use simple instruction sets for simplified implementation and higher performance at lower cost. RISC-V Instruction Set Open ISA developed at UC Berkeley, managed by RISC-V Foundation (riscv.org). Typical of many modern ISAs, widely used in embedded core markets (consumer electronics, network/storage, cameras, printers). Arithmetic Operations Most operations use three operands: two sources and one destination. Format: add a, b, c // a gets b + c Design Principle 1: Simplicity favors regularity. Regularity simplifies implementation. Simplicity enables higher performance at lower cost. Example: $f = (g + h) - (i + j)$; add t0, g, h // temp t0 = g + h add t1, i, j // temp t1 = i + j add f, t0, t1 // f = t0 - t1 Register Operands Arithmetic instructions use register operands for frequently accessed data. RISC-V has a 32 $\times$ 64-bit register file. 64-bit data is a "doubleword". 32-bit data is a "word". 32 general purpose registers: x0 to x31 . Design Principle 2: Smaller is faster. Registers are faster to access than main memory (millions of locations). RISC-V Register Conventions: x0 : The constant value 0 x1 : Return address x2 : Stack pointer x3 : Global pointer x4 : Thread pointer x5-x7, x28-x31 : Temporaries (not preserved by callee) x8 : Frame pointer x9, x18-x27 : Saved registers (callee saves/restores if used) x10-x11 : Function arguments/results x12-x17 : Function arguments Example: $f = (g + h) - (i + j)$; (variables mapped to registers) add x5, x20, x21 add x6, x22, x23 sub x19, x5, x6 Memory Operands Main memory is used for composite data (arrays, structures, dynamic data). Arithmetic operations require loading values from memory into registers and storing results back to memory. Memory is byte-addressed; each address identifies an 8-bit byte. RISC-V is Little Endian: least-significant byte at the least address of a word. RISC-V does not require words to be aligned in memory (unlike some other ISAs). Example: A[12] = h + A[8]; (h in x21, base address of A in x22) ld x9, 64(x22) // Load A[8] into x9 (8 * 8 bytes offset) add x9, x21, x9 // x9 = h + A[8] sd x9, 96(x22) // Store result into A[12] (12 * 8 bytes offset) Immediate Operands Constant data can be specified directly in an instruction. Example: addi x22, x22, 4 "Make the common case fast": Small constants are common, and immediate operands avoid a load instruction. Unsigned Binary Integers An $n$-bit number $X = x_{n-1}2^{n-1} + x_{n-2}2^{n-2} + ... + x_12^1 + x_02^0$. Range: 0 to $2^n - 1$. Example (8-bit): $0000\ 1011_2 = 11_{10}$. 64-bit range: 0 to $+18,446,774,073,709,551,615$. 2s-Complement Signed Integers An $n$-bit number $X = -x_{n-1}2^{n-1} + x_{n-2}2^{n-2} + ... + x_12^1 + x_02^0$. Range: $-2^{n-1}$ to $+2^{n-1} - 1$. Example (32-bit): $1111\ ...\ 1100_2 = -4_{10}$. 64-bit range: $-9,223,372,036,854,775,808$ to $+9,223,372,036,854,775,807$. Bit 63 is the sign bit (1 for negative, 0 for non-negative). Non-negative numbers have the same unsigned and 2s-complement representation. Specific numbers (64-bit): 0: $0000\ ...\ 0000$ -1: $1111\ ...\ 1111$ Most-negative: $1000\ ...\ 0000$ Most-positive: $0111\ ...\ 1111$ Signed Negation Complement (invert all bits: $1 \to 0, 0 \to 1$) and add 1. Property: $\bar{X} + X = 111...111_2 = -1$. Thus, $\bar{X}+1 = -X$. Example: Negate +2 ($0010_2$) $+2 = 0000\ ...\ 0010_2$ Complement: $1111\ ...\ 1101_2$ Add 1: $1111\ ...\ 1110_2 = -2_{10}$ Sign Extension Representing a number using more bits while preserving its numeric value. Replicate the sign bit to the left. Unsigned values are extended with 0s. Example: 8-bit to 16-bit $+2 (0000\ 0010_2) \to (0000\ 0000\ 0000\ 0010_2)$ $-2 (1111\ 1110_2) \to (1111\ 1111\ 1111\ 1110_2)$ RISC-V Instructions: lb : Sign-extend loaded byte. lbu : Zero-extend loaded byte (unsigned). Representing Instructions in the Computer Instructions are encoded in binary, called machine code. RISC-V instructions are encoded as 32-bit instruction words. Small number of formats encoding operation code (opcode), register numbers. Regularity in instruction formats simplifies hardware design. Hexadecimal Base 16 system. Compact representation of bit strings (4 bits per hex digit). Example: eca8 6420 is $1110\ 1100\ 1010\ 1000\ 0110\ 0100\ 0010\ 0000_2$. Hex Binary Hex Binary Hex Binary Hex Binary 0 0000 4 0100 8 1000 C 1100 1 0001 5 0101 9 1001 D 1101 2 0010 6 0110 A 1010 E 1110 3 0011 7 0111 B 1011 F 1111 RISC-V Instruction Formats R-format (Register-type) Used for register-to-register arithmetic and logical operations. funct7 rs2 rs1 funct3 rd opcode 7 bits 5 bits 5 bits 3 bits 5 bits 7 bits opcode : Main operation code. rd : Destination register number. funct3 : 3-bit function code (additional opcode). rs1 : First source register number. rs2 : Second source register number. funct7 : 7-bit function code (additional opcode). Example: add x9, x20, x21 (add contents of x20 and x21, store in x9) funct7 : 0000000 (0) rs2 : x21 (10101) rs1 : x20 (10100) funct3 : 000 (0) rd : x9 (01001) opcode : 0110011 (51) I-format (Immediate-type) Used for immediate arithmetic and load instructions. immediate rs1 funct3 rd opcode 12 bits 5 bits 3 bits 5 bits 7 bits rs1 : Source or base address register number. immediate : Constant operand or offset (2s-complement, sign-extended). Design Principle 3: Good design demands good compromises. Different formats complicate decoding but allow 32-bit instructions uniformly. Keep formats as similar as possible. S-format (Store-type) Used for store instructions with a different immediate format. imm[11:5] rs2 rs1 funct3 imm[4:0] opcode 7 bits 5 bits 5 bits 3 bits 5 bits 7 bits rs1 : Base address register number. rs2 : Source operand register number. immediate : Offset added to base address. Immediate field is split to keep rs1 and rs2 fields in the same place as other formats, simplifying decoding. Stored Program Computers Instructions are represented in binary, just like data. Instructions and data are stored in memory. Programs can operate on other programs (e.g., compilers, linkers). Binary compatibility allows compiled programs to work on different computers via standardized ISAs. The BIG Picture: C program $\to$ Compiler $\to$ Assembly $\to$ Assembler $\to$ Object (machine code) Object files (program + library routines) $\to$ Linker $\to$ Executable (machine code) Executable $\to$ Loader $\to$ Memory Logical Operations Instructions for bitwise manipulation. Useful for extracting and inserting groups of bits in a word. Operation C Java RISC-V Shift left << << slli Shift right >> >>> srli Bit-by-bit AND & & and, andi Bit-by-bit OR | | or, ori Bit-by-bit XOR ^ ^ xor, xori Bit-by-bit NOT ~ ~ Shift Operations funct6 immed rs1 funct3 rd opcode 6 bits 6 bits 5 bits 3 bits 5 bits 7 bits immed : Number of positions to shift. Shift left logical ( slli ): Shifts left, fills with 0s. Multiplies by $2^i$. Shift right logical ( srli ): Shifts right, fills with 0s. Divides by $2^i$ (unsigned only). AND Operations Useful to mask bits in a word. Select some bits, clear others to 0. Example: and x9, x10, x11 (x10 is data, x11 is mask, result in x9) x10: 0000...0000110111000000 x11: 0000...0011110000000000 x9: 0000...0000110000000000 OR Operations Useful to include bits in a word. Set some bits to 1, leave others unchanged. Example: or x9, x10, x11 (x10 is data, x11 is mask, result in x9) x10: 0000...0000110111000000 x11: 0000...0011110000000000 x9: 0000...0011110111000000 XOR Operations Differencing operation. Set some bits to 1, leave others unchanged. Can be used for NOT operation if one operand is all 1s. Example: xor x9, x10, x12 (x10 is data, x12 is mask, result in x9) x10: 0000...0000110111000000 x12: 1111...1111111111111111 x9: 1111...1111001000111111 Conditional Operations (Branches) Branch to a labeled instruction if a condition is true; otherwise, continue sequentially. beq rs1, rs2, L1 : If $rs1 == rs2$, branch to instruction labeled L1 . bne rs1, rs2, L1 : If $rs1 \neq rs2$, branch to instruction labeled L1 . Compiling If Statements: if (i==j) f = g+h; else f = g-h; bne x22, x23, Else // if (i != j) branch to Else add x19, x20, x21 // f = g + h beq x0, x0, Exit // Unconditional branch to Exit Else: sub x19, x20, x21 // f = g - h Exit: ... Compiling Loop Statements: while (save[i] == k) i += 1; Loop: slli x10, x22, 3 // x10 = i * 8 add x10, x10, x25 // x10 = address of save[i] ld x9, 0(x10) // x9 = save[i] bne x9, x24, Exit // if save[i] != k, exit addi x22, x22, 1 // i += 1 beq x0, x0, Loop // go to Loop Exit: ... Basic Blocks A sequence of instructions with no embedded branches (except at the end) and no branch targets (except at the beginning). Compilers identify basic blocks for optimization. Advanced processors can accelerate execution of basic blocks. More Conditional Operations blt rs1, rs2, L1 : If $rs1 L1 . bge rs1, rs2, L1 : If $rs1 \ge rs2$, branch to L1 . Example: if (a > b) a += 1; (a in x22, b in x23) bge x23, x22, Exit // if (b >= a) branch to Exit addi x22, x22, 1 // a += 1 Exit: ... Signed vs. Unsigned Comparison: Signed: blt , bge Unsigned: bltu , bgeu Example: $x22 = 111...\text{1 (all ones)}$, $x23 = 000...\text{01 (+1)}$ Signed: $x22 Unsigned: $x22 > x23$ ($+4,294,967,295 > +1$) is true. Procedure Calling (Steps Required) Place parameters in registers x10 to x17 . Transfer control to the procedure. Acquire storage for the procedure. Perform the procedure's operations. Place the result in a register for the caller. Return to the place of call (address in x1 ). Procedure Call Instructions Procedure Call: jal x1, ProcedureLabel Stores the address of the following instruction in x1 (return address). Jumps to ProcedureLabel . Procedure Return: jalr x0, 0(x1) Similar to jal , but jumps to $0 +$ address in x1 . Uses x0 as destination register ( x0 cannot be changed, effectively discards the return address from the jalr instruction itself). Can also be used for computed jumps (e.g., case/switch statements).