MIPS 메모리 접근 방식 정리
MIPS의 메모리 접근
목차
MIPS 메모리의 기본 특성
1.1. 바이트 주소 지정 (Byte-Addressable Memory)
1.2. 워드(Word)의 정의와 크기
1.3. 데이터 정렬 제약 (Alignment Restriction)
1.4. 엔디안(Endianness)메모리 접근 명령어 (Data Transfer Instructions)
2.1. LOAD 명령어 (메모리 → 레지스터)
2.2. STORE 명령어 (레지스터 → 메모리)메모리 접근 주소 지정 방식 (Addressing Mode)
고급 활용 및 주의사항
4.1. Large Constant (큰 상수 로딩)
4.2. 가상 레지스터 $gp (Global Pointer)
4.3. 스택(Stack) 접근파이프라인 관점에서의 메모리 접근
MIPS는 전형적인 로드/스토어(Load/Store) 아키텍처를 따릅니다. 이는 CPU가 메모리에 직접 접근하여 연산을 수행하는 것을 허용하지 않고, 모든 데이터 연산이 레지스터 내에서만 이루어져야 한다는 근본적인 제약을 의미합니다. 메모리와 레지스터 간의 데이터 이동은 오직 LOAD
(메모리 -> 레지스터)와 STORE
(레지스터 -> 메모리)라는 두 가지 유형의 명령어만을 통해 이루어집니다.
1. MIPS 메모리의 기본 특성
1.1. 바이트 주소 지정 (Byte-Addressable Memory)
최소 주소 단위: MIPS 메모리는 1바이트(8비트) 단위로 주소가 지정됩니다. 즉, 메모리의 각 바이트는 고유한 32비트 주소를 가집니다.
이는 메모리의 특정 바이트를 참조하거나, 문자(character)와 같이 작은 단위의 데이터를 저장하고 읽을 때 필수적입니다.
1.2. 워드(Word)의 정의와 크기
MIPS에서 **워드(Word)**는 32비트(4바이트)를 의미합니다. MIPS 프로세서의 레지스터 크기와 대부분의 정수 연산 단위가 32비트이기 때문입니다.
CPU는 한 번에 32비트 워드를 처리하는 데 최적화되어 있습니다.
1.3. 데이터 정렬 제약 (Alignment Restriction)
MIPS의 가장 중요한 메모리 접근 규칙 중 하나는 **데이터 정렬(Alignment)**입니다.
워드(4바이트) 접근:
lw
(load word),sw
(store word)와 같이 4바이트 워드 단위로 메모리에 접근할 때는 해당 메모리 주소가 반드시 4의 배수여야 합니다.예: 0x0000, 0x0004, 0x0008, ...
하프워드(2바이트) 접근:
lh
(load halfword),sh
(store halfword)와 같이 2바이트 하프워드 단위로 메모리에 접근할 때는 해당 메모리 주소가 반드시 2의 배수여야 합니다.바이트(1바이트) 접근:
lb
(load byte),sb
(store byte)와 같이 1바이트 단위로 메모리에 접근할 때는 주소 정렬 제약이 없습니다 (모든 바이트 주소는 1의 배수).
정렬의 중요성 (Why Alignment? - Ultrathinking Perspective):
하드웨어 단순화 및 효율성: 메모리 버스(Bus)는 보통 32비트(4바이트) 또는 64비트(8바이트) 단위로 데이터를 전송합니다. 워드 정렬이 강제되면, CPU는 메모리 컨트롤러에게 특정 4바이트 블록의 시작 주소를 한 번만 알려주면 됩니다. 메모리 컨트롤러는 해당 주소부터 4바이트를 한 번의 연산으로 가져올 수 있습니다.
성능 최적화: 만약 워드가 정렬되지 않은 주소(예: 0x0001)에 있다면, 4바이트 워드를 읽기 위해 두 번의 메모리 접근이 필요할 수 있습니다 (0x0000~0x0003 블록에서 3바이트, 0x0004~0x0007 블록에서 1바이트). 이 두 번의 접근은 성능을 크게 저하시킵니다. 정렬은 이를 방지하고 예측 가능한 단일 사이클 메모리 접근을 가능하게 합니다.
예외 처리: MIPS 하드웨어는 정렬되지 않은 접근을 감지하면
Alignment Error
와 같은 예외(exception)를 발생시킵니다. 이는 프로그래머에게 잘못된 메모리 접근을 알리고 시스템 안정성을 유지하는 데 도움이 됩니다. (일부 시스템에서는 예외를 발생시키지 않고 소프트웨어적으로 처리하기도 하지만, 성능 저하가 큽니다.)
1.4. 엔디안(Endianness)
MIPS 아키텍처는 대부분 빅 엔디안(Big-Endian) 방식을 사용합니다. 빅 엔디안은 워드 내에서 가장 중요한 바이트(Most Significant Byte)를 가장 낮은 메모리 주소에 저장하는 방식입니다.
예를 들어, 32비트 값
0x12345678
이 주소0x1000
에 저장될 때:0x1000
:0x12
0x1001
:0x34
0x1002
:0x56
0x1003
:0x78
일부 MIPS 프로세서는 설정에 따라 리틀 엔디안(Little-Endian)을 지원하기도 하지만, 기본은 빅 엔디안입니다.
2. 메모리 접근 명령어 (Data Transfer Instructions)
MIPS의 모든 메모리 접근은 I-Type
명령어 형식을 사용합니다. 이 명령어들은 다음과 같은 공통 구조를 가집니다:
opcode rs rt offset
opcode
: 명령어의 종류 (예:lw
,sw
).rs
: 베이스 레지스터(Base Register). 메모리 주소 계산의 기준이 되는 주소 값을 담고 있는 레지스터.rt
: 목적지/소스 레지스터(Target/Source Register).LOAD
명령어의 경우 메모리에서 읽어온 데이터가 저장될 레지스터,STORE
명령어의 경우 메모리에 쓸 데이터가 담긴 레지스터.offset
: 16비트 부호 확장(Signed Extension)된 상수 값. 베이스 레지스터 값에 더해져 최종 메모리 주소를 계산하는 데 사용되는 변위(displacement).
2.1. LOAD 명령어 (메모리 -> 레지스터)
메모리에 있는 데이터를 레지스터로 가져오는 명령어들입니다.
lw rt, offset(rs)
: Load Wordrt
=Memory[rs + offset]
rs
레지스터 값과offset
(부호 확장된 16비트)을 더하여 메모리 주소를 계산합니다.계산된 주소에서 4바이트(1워드)를 읽어와
rt
레지스터에 저장합니다.정렬 제약:
rs + offset
결과 주소는 반드시 4의 배수여야 합니다.
lh rt, offset(rs)
: Load Halfwordrt
=SignExtend(Memory[rs + offset])
계산된 주소에서 2바이트(1하프워드)를 읽어와
rt
레지스터의 하위 16비트에 저장하고, 상위 16비트는 부호 확장으로 채웁니다.정렬 제약:
rs + offset
결과 주소는 반드시 2의 배수여야 합니다.
lhu rt, offset(rs)
: Load Halfword Unsignedrt
=ZeroExtend(Memory[rs + offset])
lh
와 동일하지만, 상위 16비트를 제로 확장으로 채웁니다 (음수 값 처리 없음).정렬 제약:
rs + offset
결과 주소는 반드시 2의 배수여야 합니다.
lb rt, offset(rs)
: Load Bytert
=SignExtend(Memory[rs + offset])
계산된 주소에서 1바이트를 읽어와
rt
레지스터의 하위 8비트에 저장하고, 상위 24비트는 부호 확장으로 채웁니다.정렬 제약: 없음 (모든 바이트 주소 유효).
lbu rt, offset(rs)
: Load Byte Unsignedrt
=ZeroExtend(Memory[rs + offset])
lb
와 동일하지만, 상위 24비트를 제로 확장으로 채웁니다.정렬 제약: 없음.
2.2. STORE 명령어 (레지스터 -> 메모리)
레지스터에 있는 데이터를 메모리로 저장하는 명령어들입니다.
sw rt, offset(rs)
: Store WordMemory[rs + offset]
=rt
rs
레지스터 값과offset
을 더하여 메모리 주소를 계산합니다.rt
레지스터의 4바이트 값을 계산된 주소에 저장합니다.정렬 제약:
rs + offset
결과 주소는 반드시 4의 배수여야 합니다.
sh rt, offset(rs)
: Store HalfwordMemory[rs + offset]
=rt[15:0]
rt
레지스터의 하위 2바이트(16비트)를 계산된 주소에 저장합니다.정렬 제약:
rs + offset
결과 주소는 반드시 2의 배수여야 합니다.
sb rt, offset(rs)
: Store ByteMemory[rs + offset]
=rt[7:0]
rt
레지스터의 하위 1바이트(8비트)를 계산된 주소에 저장합니다.정렬 제약: 없음.
3. 메모리 접근 주소 지정 방식 (Addressing Mode)
MIPS의 로드/스토어 명령어는 기본적인 베이스/변위(Base/Displacement) 주소 지정 방식을 사용합니다.
Effective_Address=Base_Register_Value+SignExt(Offset)
Base Register
: 프로그램의 기준 주소(예: 배열의 시작 주소, 스택 포인터)를 포함하는rs
레지스터입니다.Offset
: 기준 주소로부터의 상대적인 거리(변위)를 나타내는 16비트 상수입니다. 16비트이므로 ±32KB 범위의 주소에 접근할 수 있습니다.장점:
유연성: 배열 요소(
A[i]
)나 구조체 멤버(struct.member
)에 접근할 때 베이스 레지스터에 시작 주소를 담고, 오프셋으로 원하는 요소의 위치를 지정할 수 있어 매우 효율적입니다.코드 재배치 가능성(Relocatability): 프로그램의 절대 주소에 의존하지 않고, 레지스터에 담긴 베이스 주소를 기준으로 상대적인 오프셋을 사용하므로, 코드가 메모리 어디에 로드되든 올바르게 작동할 수 있습니다.
4. 고급 활용 및 주의사항
4.1. Large Constant (큰 상수 로딩)
lui rt, immediate
(Load Upper Immediate): 16비트immediate
값을rt
레지스터의 상위 16비트에 로드하고 하위 16비트를 0으로 채웁니다.큰 32비트 상수를 레지스터에 로드하거나, 16비트
offset
으로 접근할 수 없는 넓은 메모리 주소를 지정해야 할 때lui
와ori
또는addi
를 조합하여 사용합니다.예: 32비트 주소
0xABCD1234
에 접근하려면:코드 스니펫
lui $t0, 0xABCD # $t0 = 0xABCD0000 ori $t0, $t0, 0x1234 # $t0 = 0xABCD1234 lw $t1, 0($t0) # $t1 = Memory[0xABCD1234]
4.2. 가상 레지스터 $gp
(Global Pointer)
MIPS는 프로그램의 전역 데이터 영역에 효율적으로 접근하기 위해
$gp
(글로벌 포인터)라는 특정 레지스터를 사용합니다.$gp
는 전역 데이터 영역의 중간 지점을 가리키도록 설정되며,offset
을 이용하면$gp \pm 32KB
범위 내의 전역 변수에 단일 Load/Store 명령어로 접근할 수 있습니다. 이는 컴파일러가 전역 변수 접근 코드를 최적화하는 데 사용됩니다.
4.3. 스택(Stack) 접근
스택은 주로 지역 변수, 함수 인자 전달, 복귀 주소 저장 등에 사용됩니다.
$sp
(Stack Pointer) 레지스터는 스택의 현재 최상단(Top)을 가리킵니다.스택 접근은
lw
또는sw
명령어를offset($sp)
형태로 사용하여 이루어집니다.offset
은 스택 포인터($sp
)를 기준으로 특정 위치에 저장된 데이터에 접근하는 데 사용됩니다.
5. 파이프라인 관점에서의 메모리 접근
MIPS의 Load/Store 아키텍처는 5단계 파이프라인에서 MEM(Memory Access) 단계를 매우 단순하고 독립적으로 만들었습니다.
IF (Instruction Fetch): 명령어 메모리에서 명령어를 가져옴. (메모리 읽기)
ID (Instruction Decode/Register Fetch): 명령어 해독, 레지스터 값 읽기.
EX (Execute): ALU 연산 수행 (Load/Store 명령의 경우 메모리 주소 계산).
MEM (Memory Access): 계산된 주소를 사용하여 데이터 메모리에서 읽거나 씀. Load/Store 명령어만 이 단계에서 데이터 메모리에 실제로 접근합니다. 다른 연산 명령어는 이 단계를 그냥 통과합니다.
WB (Write Back): 연산 결과 또는 로드된 데이터를 레지스터 파일에 씀.
이러한 분리는 파이프라인의 **구조적 해저드(Structural Hazard)**를 줄이고, 각 단계의 실행 시간을 균등하게 만들어 파이프라인 효율을 극대화하는 데 기여합니다. (예: 명령어 Fetch가 명령어 메모리를 사용하고, 데이터 Load/Store가 데이터 메모리를 사용하도록 메모리를 분리하는 하버드 아키텍처 방식)