
컴퓨터 구조 및 설계(David A. Patterson & John L. Hennessy) 와 학부 '컴퓨터 구조' 강의를 기반으로 이해한 내용을 정리하였습니다.
Load / Store & Instruction Format(R, I)
Memroy Operand 정리
배열, 구조체 등 복합 데이터는 register에 담을 수 없습니다. 그렇기 때문에 memory를 사용하는 것은 불가피하죠.
그러나 산술 연산(add, sub 등)의 피연산자는 반드시 register에 있어야 합니다.
| 옛날 ISA 방식 | 산술 instruction 자체에 memory operand를 허용하도록 확장 |
| 현재 RISC 방식 | 산술 instruction은 register만, 별도의 전용 instruction으로 memory ↔ register 전송 |
RISC-V는 현재 RISC 방식을 채택합니다. Memory Operand를 표시하려면 주소(32 bit)가 필요해서 instruction이 너무 길어지기 때문이에요. Register는 5bit(0 - 31)로 충분하지만, memory 주소는 32bit가 필요합니다.
Load & Store
전담 연산
메모리에 있는 데이터를 연산하고 싶으면
| Load(lw) | memory → register. 데이터를 실어온다. |
| Store(sw) | register → memory. 연산 결과를 저장. |
이것들은 산술 연산이 아니에요. 단지 데이터를 옮기는 것이 전부입니다.
그러나 instruciton 세계에서는 이것도 하나의 연산(operation)입니다.
Base + Offset
Memory Operand의 표현 방식
모든 memory의 위치는
기준 주소(base address) + 얼마나 떨어졌는지(offset)
로 표시합니다.
A[i] = base + i * element_size
memory 위치는 배열 접근 방식과 본질적으로 같습니다.
RISC-V Instruction에서는 이것을 offset(base_register) 형태로 표기합니다.
# e.g.
32(x22) = x22
# register에 들어있는 주소로부터 32 byte 떨어진 곳
base register에는 시작 주소가 들어있고, offset은 거기서 얼마나 떨어졌는지를 나타냅니다. 이 두 가지를 조합하면 특정 memory 위치를 지정할 수 있습니다.
A[12] = h + A[8]
A의 시작 주소가 x22에, h가 x21에 있다고 가정해볼께요.
lw x0, 32(x22) # x9 = A[8] (8*4=32 byte offset)
add x9, x21, x9 # x9 = h + A[8]
sw x9, 48(x22) # A[12] = x9 (12*4=48 byte offset)
lw가 memory에서 register로 전송(load), sw가 register에서 memory 전송(store)이죠. 중간의 add는 평소처럼 register 간 연산입니다.
Alignment
Alignment란
n-byte 데이터는 n의 배수 주소에서 시작해야 한다는 규칙이다.
- 4-byte(word) 데이터 → 0, 4, 8, 12, ... 4의 배수
- 2-byte(halfword) 데이터 → 0, 2, 4, 6, ... 2의 배수
- 8-byte(doubleword) 데이터 → 0, 8, 16, ... 8의 배수
Alignment를 지키면 하드웨어 구현이 효율적입니다. 대부분의 기존 아키텍쳐는 alignment를 강제했어요. RISC-V는 alignment를 강제하지 않습니다. Unaligned access를 허용합니다.
기술이 발전하면서 성능 영향이 줄어들었기 때문이에요.
줄 맞추는 것은 compiler가 합니다. compiler가 변수를 배치할 때 빈자리가 생기면 나중에 작은 데이터를 거기 넣으면 되니까 큰 제약이 아니에요.
Sign Extension
부호 확장
작은 bit 수의 데이터를 더 큰 bit 수로 표현할 때 수치르 보존해야 합니다.
양수의 경우
상위 비트를 0으로 채웁니다.
e.g.
8-bit 0000_0010 → 16-bit 0000_0000_0000_0010
음수의 경우
상위 비트를 1(부호 비트)로 채웁니다.
e.g.
8-bit 1111_1110 → 16-bit 1111_1111_1111_1110
RISC-V instruction 관련
| lb(load byte) | sign-extend. 부호 확장하여 register에 적재 |
| lbu(load byte unsigned) | zero-extend. 0으로 확장 |
| addi | immediate 값을 sign-extend하여 사용 |
Instruction Format
0과 1로 표현하기
모든 instruction은 궁극적으로 0과 1의 이진수로 표현되어야 합니다.
RISC-V의 모든 instruction은 32 bits 고정 길이 입니다.
이 32 bits를 어떻게 나눠 쓰느냐에 따라 Instruction Format이 결정됩니다.
32 bit라는 제한된 공간 안에서 연산의 종류와 피연산자를 모두 담아내야 합니다. 일종의 Zero-Sum game이죠.
opcode에 비트를 많이 할당하면 피연산자 공간이 줄어듭니다.
R-Format(Register format)
모든 피연산자가 register인 instruction에 사용됩니다.
여기서 R은 Register입니다.
| 필드 | funct7 | rs2 | rs1 | funct3 | rd | opcode |
| 비트 수 | 7 | 5 | 5 | 3 | 5 | 7 |
| 역할 | 보조 연산 코드 | source 2 | source 1 | 보조 연산 코드 | destination | 연산 종류 |
opcode(7 bits)
연산의 종류를 나타냅니다. 7 bits는 최대 128가지이지만, 실질적으로는 30 - 40개 정도만 고유하게 사용 가능합니다.
add만 해도 add와 addi가 있고, load만 해도 lw, lb, lbu 등 여러 변종이 있어서 명목상 128개가 실질적으로는 부족하기 때문이에요.
func7 + func3(10 bits)
opcode만으로 부족한 연산 종류를 구별하기 위한 확장 공간입니다.
register가 5bit 씩 3개로 15bit, opcode 7bit 해서 총 22bit
32bit에서 22bit를 빼고 10bit가 남으니 이것을 fuction code로 활용하는 겁니다. 같은 opcode를 가진 서로 다른 연산을 funct 값으로 구별합니다.

add x9, x20, x21 표현해볼께요.
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
| 0000000 | 10101 (21) | 10100 (20) | 000 | 01001 (9) | 0110011 (51) |
32bit 이진수: 0000 0001 0101 1010 0000 0100 1011 0011 = 0x015A04B3
I-Format(Immediate format)
R-format 에서 rs2와 funct7 자리(12 bits)를 합쳐서 immediate 값을 수용합니다.
| 필드 | immediate[11:0] | rs1 | funct3 | rd | opcode |
| 비트 수 | 12 | 5 | 3 | 5 | 7 |
I-Format이 사용되는 경우는 크게 두 가지가 있어요.
- Immediate 산술 연산(e.g. addi etc.) - 상수를 포함하는 연산
- Load instruction(e.g. lw etc.) - memory 주소의 offset
포맷의 개수는 적을수록 좋습니다. addi와 lw는 본질적으로 다른 연산이지만 형태가 비슷하니까 같은 포맷을 공유하죠. rs1의 의미만 달라집니다. addi 에서는 source, lw에서는 base address register.
rs1, rs2의 위치가 고정된 이유
R-Format과 I-Format을 비교하면, rs1의 위치는 동일합니다. rs2가 있을 때에는 그 자리에 있고, 필요 없으면 다른 용도(Immediate)로 쓰되 위치는 고정합니다.
영어 문법처럼 동사(opcode)를 보면 뒤에 무엇이 올지 예측할 수 있죠. opcode를 보고 이 instruction이 어떤 format인지 알면, 어디서 rs1을 찾아야 하는지, rd를 찾아야 하는지를 바로 알 수 있습니다.
12-bit Immediate의 표현 범위
부호 없을 때
$0$ ~ $2^{12} - 1 = 4095$
부호 있을 때(2의 보수)
$-2^{11}$ ~ $2^{11} - 1 = -2048 ~ 2047$
연구자들이 프로파일링을 해보니 대부분의 프로그램에서 상수는 이 정도면 충분히 표현 가능하다고 합니다. 안되는 경우도 있지만, 대부분 괜찮다고 해요.
S-Format
Store instruction(e.g. sw etc.)을 위한 format입니다. I-Format과 비슷하지만 결정적 차이가 있어요.
- I-format(lw): rd(destination register) 필요, rs2 불필요.
- S-format(sw): rd 불필요, rs2(저장할 데이터의 source register) 필요.
| 필드 | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode |
| 비트 수 | 7 | 5 | 5 | 3 | 5 | 7 |
| 역할 | offset 상위 | source data | base address | 보조 opcode | offset 하위 | 연산 종류 |
Load(lw)에서 register가 2개 필요합니다. 하나는 base address(rs1), 하나는 destination(rd).
Store(sw)에서도 register가 2개 필요합니다. 하나는 base address(rs1), 하나는 저장할 데이터(rs2).
차이는 두 번째 register가 destination 이냐 source 이냐 뿐이에요.
왜 rd 자리에 rs2를 그대로 안넣는거임?
S-format에서 Immediate가 두 조각으로 분리된 이유가 있어요.
rs1, rs2, rd의 위치를 모든 format에서 고정하기 위함입니다.
- rd가 있으면 항상 같은 bit 위치
- rs1이 있으면 항상 같은 bit 위치
- rs2가 있으면 항상 같은 bit 위치
rd를 찾고 싶으면 반드시 이 자리에 있다라는 것. rs1이 있으면 반드시 이 자리에 있습니다. 필요 없으면 다른 용도로 써도 되지만, 있으면 항상 같은 위치입니다. 이렇게 해야 0과 1을 다루는 하드웨어가 효율적으로 구현될 수 있어요.
3칸 요약
| Format | 용도 | 특징 |
| R-type | 산술 연산(add, sub etc.) | 3개 register, funct7 + funct3로 연산 구별 |
| I-type | Immediate 연산(e.g. addi), Load (lw, lb etc.) | 12-bit immediate, rs2 / funct7 자리를 합쳐 사용 |
| S-type | Store(sw, sb etc.) | immediate가 두 조각으로 분산, rs2 위치 보존 |

모든 format에서 공통적으로
opcode는 항상 하위 7 bits, rs1이 있으면 항상 같은 위치, rs2가 있으면 항상 같은 위치
Stored Program Computer

메모리에는 데이터뿐만 아니라 프로그램(instruction)도 저장됩니다.
메모리에 동시에 존재하는 것들을 예로 들어보면
회계 프로그램 - machine code
에디터 프로그램 - machine code
C compiler - machine code
회계 데이터 - data
소스 코드 - data
Stored Program의 핵심은
회계 프로그램을 고치고 싶으면 소스 코드를 에디터로 편집합니다. 이때 에디터가 프로그램이고, 소스 코드가 데이터입니다. 편집이 끝나면 compiler가 프로그램이 되고, 소스 코드는 입력 데이터가 됩니다. Compiler의 출력(machine code)이 새 회계 프로그램이 됩니다. 프로그램이 상황에 따라 데이터가 될 수 있습니다. 이것이
Binary Comopatibility
같은 ISA를 공유하는 다른 기계에서도 프로그램을 실행할 수 있습니다. ISA가 바뀌면 호환이 깨지죠.
Logical Operations
논리 연산

Bitwise vs Logical 연산
C언어에서 두 연산의 차이를 한 번 봅시다.
| Bitwise | Logical | |
| AND | & (비트 단위) | && (논리) |
| $a = 2, b = 1$ 일 때 | 2 & 1 = 0 (0010 & 0001 = 0000) | 2 && 1 = 1 , 둘 다 참이므로 |
C 언어에서 0은 거짓, 0이 아닌 모든 값은 참입니다. 따라서 logical AND에서 2와 1은 둘 다 참이므로 결과도 참입니다. 반면 bitwise AND에서는 bit 단위로 AND하므로 0010 & 0001 = 0000 = 0 입니다.
RISC-V 논리 연산 Instruction
| 연산 | C | RISC-V | immediate 버전 |
| AND | & | and | andi |
| OR | | | or | ori |
| XOR | ^ | xor | xori |
| NOT | ~ | xori(1's로) | ㅡ |
xori를 활용한 NOT의 구현
RISC-V에는 별도의 NOT instruction이 없습니다.
대신 XOR with all 1's로 NOT을 구현합니다.
A XOR 1 = A' 는 1의 보수
XOR은 홀수 함수이기 때문에 1과 XOR 하면 비트가 반전됩니다.
1의 개수가 홀수면 결과가 1, 짝수면 0이 되는 XOR의 성질을 이용합니다. 비트 하나에 대해 1과 XOR하면 반대가 되죠. 따라서 별도의 NOT 연산자를 정의하지 않고 xori의 immediate 버전을 갖다 쓰면 NOT이 자연스럽게 됩니다.
Mask Operation
논리 연산의 응용
- AND - masking: 특정 bit만 남기고 나머지를 0으로 지웁니다. mask의 1인 위치만 보존됩니다.
- OR - setting: 특정 bit를 무조건 1로 설정합니다. 나머지는 그대로 유지됩니다.
- XOR - toggling: 특정 bit만 반전됩니다. 바꿔치기죠. 나머지는 그대로입니다.
Shift 연산
a << 1 // 왼쪽 shift 1, * 2
a >> 1 // 오른쪽 shift 1, / 2
전광판 스크롤을 생각해보면 직관적일 것 같아요. 왼쪽 스크롤하면 왼쪽 끝이 떨어져 나가고, 오른쪽 빈칸에는 새 LED가 꺼진 채로(0) 들어오죠.
왜 오른쪽 Shift에 두 종류가 있음?
부호 있는 수(음수)의 오른쪽 shift
4-bit 단위에서 예시를 들어볼께요.
- 1100 = -4
- 2의 보수
- 왼쪽 shift 1: 1000 = -8
- *2가 되었기 때문에 정확합니다.
- 오른쪽 shift 1: 0 채우고, 0110 = 6
- 부호가 바뀌었기 때문에 틀렸어요.
- 오른쪽 shift 1: 부호 bit 채우면 1110 = -2
- 2로 나눠졌기 때문에 정확합니다.
0으로 채우면 음수가 양수로 바뀌어 버립니다. 부호를 유지하려면 빈칸을 부호 bit(MSB)로 채워야 해요.
이것은 이전에 배운 sign extension과 동일한 원리입니다.
왼쪽 shift는 부호와 무관하게 0으로 채우는 하나의 방식만 존재합니다.
오른쪽 shift만 logical, 0으로 채우는 것과 부호를 채우는 arithmentic 두 종류가 필요하죠.
| 종류 | Instruction | 빈칸 채우기 | 용도 |
| Shift Left Logical | sll / slli | 0으로 채움 | $* 2^n$ 부호 무관 |
| Shift Right Logical | srl / srli | 0으로 채움 | $÷ 2^n$ unsigned만 |
| Shift Right Arithmetic | sra / srai | 부호 bit로 채움 | $ ÷ 2^n$ signed 가능 |
Shift Instruction의 Format
피연산자 3개(shift할 데이터, shift 양, 결과)이기 때문에 기본적으로 R-format을 사용합니다.
Immediate 버전(slli, srli, srai)
I-Format이지만 shift 양은 최대 31(32-bit 데이터 기준) 이므로 5 bits만 필요합니다. 12-bit immediate 중 5 bits만 사용하고, 나머지 7 bits를 func7처럼 연산 종류 구별에 활용합니다.
shift 양이 32이면 데이터가 다 나가서 아무것도 안 남습니다. 따라서 최대 31이고, 이것을 표현하는 데 5 bits면 충분하죠.
'CS & AI > Computer Architecture' 카테고리의 다른 글
| <컴퓨터구조> Procedure, memory & branch (0) | 2026.05.14 |
|---|---|
| [궁금증] 보수 (0) | 2026.04.20 |
| <컴퓨터 구조> 기본 산술 instruction과 RISC-V Assembly Language 요약 (0) | 2026.04.17 |
| <컴퓨터구조> Instructions - Language of the Computer (0) | 2026.04.16 |
| <컴퓨터구조> Performance (0) | 2026.04.16 |
나의 성장 드라마
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!