
컴퓨터 구조 및 설계(David A. Patterson & John L. Hennessy) 와 학부 '컴퓨터 구조' 강의를 기반으로 이해한 내용을 정리하였습니다.

Instructions
Instruction은 컴퓨터의 언어 입니다.
소프트웨어가 하드웨어에게 무엇을 해야 하는지 알려주기 위한 매개(language) 수단이죠. C 언어가 사람과 컴파일러 사이의 언어라면, Instruction은 소프트웨어와 하드웨어 사이의 언어(Machince Language) 입니다.
스프트웨어와 하드웨어 간에 쓰는 언어이다 보니, 사람이 보기에는 그다지 편한 글처럼 보이지 않을 수 있어요. 우리가 쓰는 자연어도 아닌, 심지어 Python이나 C언어같은 언어도 이 친구들한테는 고급 언어입니다. 훨씬 저수준이라는 거죠.
아래와 같은 특징이 있습니다.
- 고급 언어보다 훨씬 원시적(primitive)이다. 복잡한 control flow(다양한 형태의 반복, 조건문)가 없다.
- 피연산자(operand) 수가 제한되어 있다. a + b + c 라는 연산이 있으면 이를 한 번에 할 수 없고, a + b를 먼저 한 다음 그 결과에 c를 더하는 방식으로 계산합니다.
- Instruction Set: 한 컴퓨터가 실행할 수 있는 instruction들의 전체 목록(레퍼토리)
CISC vs RISC
역사적으로 instruction set은 단조 증가(monotomically increasing)하는 경향이 있었습니다. 새로운 응용이 등장할 때마다(멀티미디어, AI의 Convolution 등) 그에 맞는 instruction을 추가했고, 호환성(backward compatibility) 때문에 한번 추가된 instruction은 제거하지 못했습니다. 이렇게 점점 복잡해진 것이 CISC(Complex Instruction Set Computer)에요.
어느 시점에서 연구자들이 안쓰는 instruction이 너무 많다는 것을 발견하고, 불필요한 것을 제거하여 새로 설계한 것이 RISC(Reduced Intruction Set Computer)입니다.
Reduced라는 단어는 이 맥락에서 나왔던거죠. 원래 커졌던 것을 줄였다는 뜻이니까요.
Instruction Set이 크면 소프트웨어에게 표현이 간결해지기 때문에 유리합니다. 하지만 모든 instruction을 실행할 수 있는 회로를 만들어야 하기 때문에 하드웨어의 부담이 커지죠.
RISC-V Instruction Set
- UC Berkeley에서 오픈 ISA로 개발
- 현재 RISC-V Foundation에서 관리
Home
RISC-V is an open standard Instruction Set Architecture (ISA) enabling a new era of processor innovation through open collaboration.
riscv.org
- V는 로마 숫자 5를 의미. RISC의 다섯 번째 버전인 것
- 오픈소스이기 때문에 자유롭게 사용 가능하고 최신 설계이기 때문에 이전 ISA들의 시행착오를 반영하여 깔끔하게 설계되어 있다.
RISC-V Operand와 기본 Instruction
Instruction = Operation code + Operands
Instruction은 근본적으로 operation code(연산자)와 operand(피연산자)로 구성됩니다. 고급 언어(e.g. C etc.)에서의 표현과 instruction의 표현 사이에는 근본적인 시각 차이가 있어요.
고급 언어는 사람의 생각을 효과적으로 표현하는 것이 목적입니다.
Instruction(명령어)은 하드웨어에서 잘 실행하는 방법을 제시하는 것이 목적입니다. 하드웨어 구현까지 염두해 둔 것이죠.
RISC-V는 다른 x86과 같은 ISA에 비해 instruction을 보는 시각이 훨씬 구조화되어 있고 단순합니다. x86같은 CISC 아키텍쳐와 비교하면 상대적으로 매우 간단합니다.
RISC-V operand
- 32개의 레지스터: x0 ~ x31 (32비트 버전 기준, 32 * 32-bit register file)
- 메모리: $2^{30}$ memory words, byte addressing 사용(각 word는 4개 주소를 차지)
아래 게시글 참고
https://neverthe1ess.tistory.com/295
<컴퓨터 구조> 기본 산술 instruction과 RISC-V Assembly Language 요약
기본 산술 InstructionInstructionExample의미설명Addadd x5, x6 x7$x5 = x6 + x7$세 레지스터 operand; 덧셈Subtractsub x5, x6 x7$x5 = x6 - x7$세 레지스터 operand; 뺄셈Add immediateaddi x5, x6, 20$x5 = x6 + 20$상수를 더할 때 사용 Ar
neverthe1ess.tistory.com
Register vs Memory
Instruction의 시각에서 피연산자를 둘 수 있는 공간은 두 곳이 있어요. Register와 Memory.
역사적으로 처음에는 메모리만 있었습니다. Processor와 Memory 두 구성 요소로 출발했기 때문이죠. 그러나 시간이 지나면서 성능 개선을 위해 register라는 장치를 도입했습니다.
| 특성 | Memory | Register |
| 공간 크기 | 매우 큼($2^{32}$ bytes) | 매우 작음(32개 * 32 bit) |
| 접근 속도 | 느림 | 빠름 |
| 유연성 | 높음 (다양한 크기 데이터 수용) | 낮음(고정 크기) |
| 활용 | 배열, 구조체 등 복합 데이터 | 빈번히 접근하는 데이터 |
Register는 메모리의 느린 접근 속도를 해결하기 위해 고안됐습니다. 따라서 가능하면 피연산자를 register에 두는 것이 유리해요. 후술할 내용에서는 피연산자가 register에 있다고 가정하고 작성할 계획입니다.
Register Operand
RISC-V(32-bit version)에서는 32개의 32-bit register가 있씁니다. register 번호를 매길 때 다른 ISA는 보통 'R'을 쓰지만 RISC-V는 'x'를 사용해요(x0 - x31).
- 32-bit 데이터를 word라고 부릅니다.
- 64-bit 데이터를 double word라고 부릅니다. 프로그래밍 언어에도 'dw'라는 표현이 남아있어요.
add와 피연산자 3개
산술 instruction 예시를 좀 살펴볼께요.
Operand 개수, 왜 3개일까?
C 언어에서
a = b + c;
라는 문장을 봅시다. C 언어의 시각에서 연산자는 2개입니다.
+(더하기)와 =(assignment)
피연산자도 C 시각에서는 +의 피연산자와 2개(b, c)와 =의 피연산자 2개(a, b+c의 결과) 이렇게 나뉩니다.
그런데 RISC-V instruction에서는 이것을 하나로 합쳐서
add a, b, c
이렇게 표현합니다. 여기서 피연산자가 3개에요.
Source 2개 + destination 1개
즉, add라는 instruction은 엄밀히 말하면 더하고 저장하라(add and assign)는 의미가 합축되어 있습니다.
더하기가 끝이 아니라는 겁니다. 더해서 어디에 저장하라는 의미가 내포되어 있다는거죠. assign을 안하는 instruction도 물론 있지만, 대부분의 연산은 assign을 포함합니다.
복합 Expression의 Instruction 변환
f = (g + h) - (i + j);
고급 언어에서는 한 줄이지만, instruction의 세계에서는 한 instruction으로 표현이 불가능합니다.
instruction은 한 번에 하나의 연산만 수행하기 때문에 3개의 instruction으로 분해해야 합니다.
add t0, g , h # temp t0 = g + h
add t1, i, j # temp t1 = i + j
sub f, t0, t1 # f = t0 - t1
중간 결과를 저장할 임시(temporary) 공간이 필요하고, 그 임시 공간조차도 register를 사용합니다.
Register 할당이 어떻게 이루어지는지 예시로 한 번 봐볼께요.
변수 f, g, h, i, j가 각각 x19, x20, x21, x22, x23에 대응된다고 가정해봅시다.
add x5, x20, x21 # temp $t0 = g + h
add x6, x22, x23 # temp $t1 = i + j
sub x19, x5, x6 # f = $t0 - $t1
변수 이름이 register 번호로 바뀌었네요. 이 대응(mapping)은 compiler가 담당합니다.
Memory Operand가 왜 필요할까?
단순 변수(e.g. register)는 register에 대응시킬 수 있습니다. 그러나 배열(array)은 register에 넣을 수 없어요.
그런데에는 두 가지 이유가 있습니다.
- 배열은 크기가 작다. 32개의 register로 감당이 불가
- 배열은 본질적으로 주소 연산(address arithmetic)을 전제
e.g.
A[12] = h + A[8];
여기서 A[8]이나 A[12]는 메모리에 있을 수 밖에 없습니다.

32-bit Architecture와 Byte Addressing
32-bit architecture라 함은 데이터도 32 bit, 주소도 32 bit, 데이터 전달 통로(bus)도 32 bit라는 의미입니다.
32차선 도로가 있으면 차량 32대가 동시에 갈 수 있겠죠. 16차선이라도 두 번에 보내면 되지만, 데이터가 기본적으로 32bit면 32bit 폭의 통로가 제일 바람직합니다.
Byte Addressing
Memory는 $2^{32}$개의 byte로 구성되어 있어요. 주소는 byte 단위로 매깁니다.
메모리 주소가 10이다. == 시작에서 10 byte 떨어져 있다.
가장 흔한 데이터 크기는 32bit(= 4 bytes)인 word이기 때문에, 데이터 하나가 주소 4개를 점유합니다.
word 단위로 보면 메모리에는 $2^{30}$개의 word가 있습니다.
우리 대학교에는 대학로 99번지 다음에 101번지가 없습니다. 우리 대학교가 차지하는 공간이 너무 커서 101번지도 같은 우리 대학교인 것처럼, 한 word가 주소 4칸을 차지하는 것과 같은 맥락이라 볼 수 있죠.
배열식으로 표현하면
Memory[0], Memory[4], Memory[8], ... 여기서 4는 byte 주소 단위이므로 word가 4칸씩 점프합니다.
Big Endian vs Little Endian
32-bit 데이터를 byte 단위로 쪼개면 4개의 byte가 되고, 이것을 메모리에 저장할 때 순서 문제가 발생합니다.
4 byte 데이터를 MSB(최상위 byte)부터 a, b, c, d라 하고, 주소 $\alpha$에 저장한다고 하면
| Big Endian | Little Endian | |
| $\alpha$ | a (MSB) | d (LSB) |
| $\alpha + 1$ | b | c |
| $\alpha + 2$ | c | b |
| $\alpha + 3$ | d (LSB) | a (MSB) |

RISC-V는 Little Endian을 사용합니다.
크기가 다른 데이터에(e.g. 4 byte + 2 byte)를 더할 때, Little Endian에서는 LSB가 항상 같은 주소에서 시작하므로 서로 다른 크기의 데이터 간 pairing이 자연스러워요.
4바이트 정수 0x00000042가 주소 1000번지에 저장된 상황을 봐볼께요.
# Little Endian
주소: 1000 1001 1002 1003
값: 0x42 0x00 0x00 0x00
LSB ←────────────→ MSB
# Big Endian
주소: 1000 1001 1002 1003
값: 0x00 0x00 0x00 0x42
MSB ←────────────→ LSB
이제 이 값을 1 byte짜리로 읽고 싶다면
- Little Endian → 그냥 같은 주소 1000번지에서 1바트오 읽으면 0x42
Big Endian → 1000번지를 읽으면 0x00이 나온다. 1003번지를 읽어야 0x42가 나온다.
2bytes로 읽을 때에도 마찬가지
- Little Endian → 같은 주소 1000번지에서 2바이트 읽으면 0x0042
- Big Endian → 1000번지에서 읽으면 0x0000. 1002번지부터 읽어야 한다.
결국 Little Endian이 선호되는 이유는 시작점이 똑같기 때문이라고 볼 수 있습니다.
몇 바이트를 읽든 base address가 항상 동일하죠. 그래서 데이터 크기가 바뀌어도 주소 계산을 다시 할 필요가 없습니다.
반면 Big Endian은 크기가 바뀔 때마다 시작 주소를 재계산해야 하죠. 과정이 하나가 더 추가된다는 점이 차이입니다.
이 차이가 type casting 회로를 하드웨어 설계에 있어서 단순하게 만들어주기 때문에 Little Endian이 선호되는 거죠.
RISC-V, x86도 Little Endian을 쓰고 있습니다.
배열과 주소 계산
C 언어의 포인터 / 배열 관계를 통해 메모리 주소 표현에 대해 알아봅시다.

A = 배열의 시작 주소
A + 8 = 주소 연산. 8칸(element) 떨어진 곳.
1000 + 8 * 4 = 1032 번지 (32bit int 기준)
*(A + 8) = 1032 번지에 있는 값 = A[8]과 동일
A[8] = *(A + 8)
이 두 표현은 C 언어에서 완전히 같습니다. 배열 접근은 곧 시작 주소 + offset 으로의 주소 계산이죠.
이것이 instruction에서 메모리 operand를 표현하는 기본 아이디어가 됩니다.

'CS & AI > Computer Architecture' 카테고리의 다른 글
| <컴퓨터구조> Instruction Cont'd (0) | 2026.04.19 |
|---|---|
| <컴퓨터 구조> 기본 산술 instruction과 RISC-V Assembly Language 요약 (0) | 2026.04.17 |
| <컴퓨터구조> Performance (0) | 2026.04.16 |
| <컴퓨터 구조> Computer Abstractions & Technology (0) | 2026.04.06 |
| <컴퓨터 구조> Digital Logiac Circuits 디지털 논리 회로 (0) | 2026.03.22 |
나의 성장 드라마
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!