CPE 225 Midterm Study Guide
Created: February 2, 2025 3:23 PM Class: CPE 225
1. RISC-V Architecture Fundamentals
1.1 Register Conventions
- Argument Registers (a0-a7)
- Used for passing parameters to subroutines
- a0 also used for return values
- Caller-save (not preserved across calls)
- First 8 arguments go in these registers
- Additional arguments go on stack
- Saved Registers (s0-s11)
- Must preserve values across subroutine calls
- Used for variables that persist across calls
- Callee must save and restore if used
- Often used for loop counters and important variables
- Temporary Registers (t0-t6)
- Used for intermediate calculations
- Caller-save (not preserved across calls)
- Can be freely used without preservation
- Good for short-term storage within a single subroutine
- Special Purpose Registers
- ra (x1): Return address
- sp (x2): Stack pointer
- zero (x0): Always contains 0
- gp (x3): Global pointer
- tp (x4): Thread pointer
1.2 Memory Organization
- Text Segment (0x00400000)
- Contains program instructions
- Read-only during execution
- Instructions aligned on word boundaries
- Data Segment (0x10010000)
- Static data (.data directive)
- Global variables
- String constants
- Initialized at program start
- Stack (0x7fffefff)
- Grows downward
- Used for:
- Local variables
- Saved registers
- Return addresses
- Additional arguments
- Automatically managed with sp register
2. Subroutines and Calling Convention
2.1 Calling Subroutines
# Basic subroutine call
jal subroutine_label # Jump and link
# Return happens here
# Returning from subroutine
ret # Actually jalr ra
2.2 Parameter Passing
# Passing parameters
mv a0, s0 # First parameter
mv a1, s1 # Second parameter
jal function # Call function
# Inside function
mv t0, a0 # Can use parameters directly
mv t1, a1 # No need to copy unless preserving
2.3 Nested Subroutine Calls
function1:
# Must save ra if calling another function
addi sp, sp, -4 # Make space on stack
sw ra, 0(sp) # Save return address
jal function2 # Call nested function
lw ra, 0(sp) # Restore return address
addi sp, sp, 4 # Restore stack
ret
3. Number Systems and Binary Operations
3.1 Number Base Conversions
Binary to Decimal
- Each position represents a power of 2, starting from 2⁰ on the right
- Multiply each bit by its position value and sum
Examples:
-
Short conversion:
-
Longer conversion:
-
Quick reference for powers of 2:
Decimal to Binary
Two main methods:
Method 1: Division by 2
- Divide by 2 repeatedly until quotient is 0
- Read remainders from bottom to top
Example: Convert 181₁₀ to binary
181 ÷ 2 = 90 remainder 1
90 ÷ 2 = 45 remainder 0
45 ÷ 2 = 22 remainder 1
22 ÷ 2 = 11 remainder 0
11 ÷ 2 = 5 remainder 1
5 ÷ 2 = 2 remainder 1
2 ÷ 2 = 1 remainder 0
1 ÷ 2 = 0 remainder 1
Reading bottom-up: 10110101b
Method 2: Subtraction of Powers of 2
- Find largest power of 2 less than number
- Subtract and repeat
Example: Convert 181₁₀ to binary
181 - 128 (2⁷) = 53 [1xxxxxxx]
53 - 32 (2⁵) = 21 [101xxxxx]
21 - 16 (2⁴) = 5 [1011xxxx]
5 - 4 (2²) = 1 [10110xxx]
1 - 1 (2⁰) = 0 [10110101]
Result: 10110101b
Hexadecimal Conversions
Binary to Hex Conversion Table:
Binary | Hex Binary | Hex
0000 | 0 1000 | 8
0001 | 1 1001 | 9
0010 | 2 1010 | A
0011 | 3 1011 | B
0100 | 4 1100 | C
0101 | 5 1101 | D
0110 | 6 1110 | E
0111 | 7 1111 | F
Examples:
-
Converting Binary to Hex:
-
Converting Hex to Binary:
3.2 Two's Complement
Basic Rules
- Most significant bit (leftmost) indicates sign
- 0 = positive
- 1 = negative
- For n bits:
- Range: -2^(n-1) to 2^(n-1)-1
- Example for 8 bits: -128 to 127
Converting Between Positive and Negative
-
Positive to Negative:
-
Negative to Positive (same process):
Common Gotchas
-
Sign Extension When extending to more bits, copy the sign bit
-
Overflow Occurs when result doesn't fit in available bits
-
Special Cases
- Negating smallest negative number gives itself
Practice Problems
-
Convert +47 to -47 in 8-bit two's complement
-
Add these 8-bit numbers: 0x3B + 0xC4
3.3 Bitwise Operations
# AND - useful for masking
and t0, t1, t2 # t0 = t1 & t2
# OR - useful for combining flags
or t0, t1, t2 # t0 = t1 | t2
# XOR - useful for toggling bits
xor t0, t1, t2 # t0 = t1 ^ t2
# Shifts
slli t0, t1, 2 # Logical left shift (multiply by 4)
srai t0, t1, 1 # Arithmetic right shift (divide by 2)
4. RISC-V Instructions
4.1 Core Instructions
# Arithmetic
add rd, rs1, rs2 # rd = rs1 + rs2
addi rd, rs1, imm # rd = rs1 + imm
sub rd, rs1, rs2 # rd = rs1 - rs2
# Memory
lw rd, offset(rs1) # rd = Memory[rs1 + offset]
sw rs2, offset(rs1)# Memory[rs1 + offset] = rs2
# Branches
beq rs1, rs2, label # Branch if equal
bne rs1, rs2, label # Branch if not equal
blt rs1, rs2, label # Branch if less than
bge rs1, rs2, label # Branch if greater/equal
4.2 Pseudo-Instructions
# Load immediate
li t0, 100 # Load immediate value
# Load address
la t0, label # Load address of label
# Move register
mv t0, t1 # Copy t1 to t0
# Branch always
b label # Unconditional branch
5. Control Structures
5.1 If Statement
# if (x > 0) { ... }
blez x, endif # Skip if x <= 0
# If body here
endif:
5.2 If-Else Statement
# if (x > y) { ... } else { ... }
ble x, y, else # Branch to else if x <= y
# If body here
b endif # Skip else block
else: # Else body here
endif:
5.3 Loops
# While loop
loop:
beq t0, zero, endloop # Exit condition
# Loop body
b loop
endloop:
# For loop (counting down)
li t0, 10 # Initialize counter
loop:
beqz t0, endloop # Check if done
# Loop body
addi t0, t0, -1 # Decrement
b loop
endloop:
6. Common Programming Patterns
6.1 Array Access
# Array base in t0, index in t1
slli t2, t1, 2 # Multiply index by 4 (word size)
add t2, t0, t2 # Add to base address
lw t3, 0(t2) # Load array[index]
6.2 String Processing
loop:
lb t0, 0(s0) # Load byte from string
beqz t0, done # Check for null terminator
# Process character
addi s0, s0, 1 # Move to next char
b loop
done:
7. Exam Preparation Tips
- Practice Problems
- Convert C code to assembly
- Implement basic algorithms
- Write helper functions
- Debug existing code
- Common Mistakes to Avoid
- Not saving ra in nested calls
- Forgetting to preserve s registers
- Incorrect parameter passing
- Misaligned memory access
- Testing Strategy
- Read entire question first
- Plan register usage before coding
- Test edge cases mentally
- Double-check register preservation
- Time Management
- Start with problems you’re confident about
- Don’t spend too long on any one problem
- Leave time to review all answers
- Check for basic mistakes