MIPS 명령어셋 레퍼런스

MIPS 아키텍처의 명령어를 GAS 문법 기준으로 종합 정리합니다. 전통 RISC 설계 철학과 고정 32-bit 명령어, 32개 범용 레지스터와 HI/LO 특수 레지스터, 지연 슬롯 메커니즘, MIPS32/64 Release 2와 Release 6의 차이, R/I/J 3가지 인코딩 타입, 데이터 전송·산술·논리·분기·스택·시스템·원자적·MSA SIMD 명령어 카테고리 표, 인코딩 다이어그램, 커널 핵심 명령어(SYSCALL, ERET, LL/SC, MFC0/MTC0, TLBWI) 심화까지 Linux 커널 개발에 필요한 MIPS ISA 전체를 다룹니다.

전제 조건: 어셈블리 종합을 먼저 읽으세요. MIPS 명령어 레퍼런스는 RISC Load/Store 모델, 지연 슬롯 개념, CP0 레지스터에 대한 기본 이해가 필요합니다.
일상 비유: MIPS는 교과서적 RISC 설계의 대표입니다. 컴퓨터 구조 교육의 표준으로 사용될 만큼 깔끔한 명령어 세트를 가지고 있으며, 파이프라인 설계의 교본과 같습니다. 지연 슬롯은 파이프라인 효율을 위한 독특한 설계 결정입니다.

핵심 요약

  • 전통 RISC — 고정 32-bit 명령어, Load/Store 아키텍처, 균일한 레지스터 파일.
  • 32개 범용 레지스터 — $0=zero(항상 0), $31=ra, $29=sp, $4-$7=a0-a3(인자).
  • 지연 슬롯 — 분기/점프 다음 명령어가 항상 실행됨 (R6에서 compact branch로 개선).
  • HI/LO 레지스터 — 곱셈/나눗셈 결과 저장 (R6에서 제거, GPR 직접 사용).
  • R2 → R6 진화 — Release 6에서 지연 슬롯 없는 분기, HI/LO 제거, 새 인코딩 도입.

단계별 이해

  1. 레지스터 구조 파악
    $0-$31의 ABI 이름($zero, $at, $v0-$v1, $a0-$a3, $t0-$t9, $s0-$s7 등)을 먼저 익힙니다.
  2. 지연 슬롯 이해
    분기/점프 직후 명령어가 항상 실행되는 MIPS 고유 특성을 반드시 이해합니다.
  3. R/I/J 인코딩
    3가지 고정 인코딩 포맷의 필드 배치를 학습합니다.
  4. CP0와 특권 명령어
    MFC0/MTC0으로 접근하는 CP0 레지스터와 SYSCALL/ERET를 학습합니다.

아키텍처 개요

MIPS(Microprocessor without Interlocked Pipelined Stages)는 1981년 Stanford에서 John Hennessy가 설계한 RISC 아키텍처입니다. MIPS Computer Systems(1984) → SGI(1992) → Imagination Technologies(2012) → Wave Computing(2018) → MIPS(2021)를 거쳤습니다.

특성MIPS
설계 철학전통 RISC (교과서적 파이프라인 설계)
명령어 길이고정 32-bit
엔디언바이-엔디언 (Big 또는 Little)
범용 레지스터32개 ($0=hardwired zero)
특수 레지스터HI/LO (곱셈/나눗셈, R2), PC
지연 슬롯분기/점프 다음 1개 명령어 항상 실행 (R6: compact branch 도입)
주요 릴리스MIPS32/64 R2 (2003), R5 (2012), R6 (2014)
동작 모드커널 모드, 슈퍼바이저 모드, 유저 모드
주소 공간32-bit (MIPS32) 또는 64-bit (MIPS64)
R2 vs R6: Release 6(2014)에서 대폭 변경되었습니다. HI/LO 제거(곱셈/나눗셈 결과 GPR 직접), 지연 슬롯 없는 compact branch 도입, 일부 명령어 재인코딩. Linux 커널은 R2와 R6 모두 지원합니다.

MIPS 클래식 5단계 파이프라인

MIPS 클래식 5단계 파이프라인 (Classic 5-Stage Pipeline) IF Instruction Fetch ID Instruction Decode EX Execute MEM Memory Access WB Write Back 분기 명령어와 지연 슬롯 실행 타이밍 Cycle 1 Cycle 2 Cycle 3 Cycle 4 Cycle 5 BEQ (분기) IF ID+비교 EX 지연 슬롯 IF ID EX MEM 분기 타겟 IF ID EX BEQ가 ID에서 분기 결정 → 이미 다음 명령어(지연 슬롯)가 IF에 진입 설계 결정: 분기 결과를 ID 단계에서 결정해도 다음 명령어는 이미 IF에 진입했으므로 취소하는 대신 항상 실행(지연 슬롯). 이 설계는 파이프라인 버블을 제거하지만 프로그래머/컴파일러에게 지연 슬롯 관리 부담을 전가합니다.
파이프라인 해저드와 지연 슬롯: MIPS의 이름(Microprocessor without Interlocked Pipelined Stages)이 암시하듯, 초기 MIPS는 하드웨어 인터록 없이 파이프라인 해저드를 소프트웨어가 관리하도록 설계되었습니다. 분기 명령어가 ID 단계에서 결과를 결정하지만, 그 시점에 다음 명령어는 이미 IF 단계에 진입해 있습니다. 하드웨어로 이를 취소(flush)하는 대신, 항상 실행하도록 하여 1 사이클의 파이프라인 버블을 제거한 것이 지연 슬롯의 핵심 설계 결정입니다. 현대 프로세서에서는 이 결정이 오히려 복잡성을 증가시키기 때문에, R6에서 지연 슬롯 없는 compact branch가 도입되었습니다.

MIPS 가상 주소 공간 (32-bit)

MIPS32 가상 주소 공간 레이아웃 주소 범위 세그먼트 속성 0xE0000000 kseg3 0xFFFFFFFF 커널 매핑됨 (512MB) — TLB 사용 캐시 속성 TLB 엔트리에 의해 결정 0xC0000000 kseg2 / ksseg 0xDFFFFFFF 커널/슈퍼바이저 매핑됨 (512MB) — TLB 사용 vmalloc, ioremap 영역 0xA0000000 kseg1 0xBFFFFFFF 커널 비매핑 비캐시 (512MB) — TLB 미사용 MMIO, 부트 코드 (물리 주소 = VA - 0xA0000000) 0x80000000 kseg0 0x9FFFFFFF 커널 비매핑 캐시 (512MB) — TLB 미사용 커널 코드/데이터 (물리 주소 = VA - 0x80000000) 0x00000000 kuseg 0x7FFFFFFF 유저 매핑됨 (2GB) — TLB 사용 유저 프로세스 코드, 데이터, 스택, 힙 kseg0/kseg1은 물리 메모리 하위 512MB에 직접 매핑 (PA = VA & 0x1FFFFFFF). 부트 시 TLB 초기화 전에 사용 가능.

레지스터 셋

범용 레지스터 ($0-$31)

번호ABI 이름용도보존
$0$zero하드와이어 제로 (항상 0)
$1$at어셈블러 임시 (Assembler Temporary)
$2-$3$v0-$v1함수 반환값Caller
$4-$7$a0-$a3함수 인자 (o32 ABI)Caller
$8-$15$t0-$t7임시 레지스터Caller
$16-$23$s0-$s7Saved 레지스터Callee
$24-$25$t8-$t9임시 레지스터Caller
$26-$27$k0-$k1커널 전용 (예외 핸들러용)
$28$gp전역 포인터 (Global Pointer)Caller
$29$sp스택 포인터Callee
$30$fp ($s8)프레임 포인터 / Saved 레지스터Callee
$31$ra복귀 주소 (Return Address)Caller
$k0/$k1이 커널 전용인 이유: MIPS에서 예외가 발생하면 프로세서는 예외 벡터로 즉시 점프하지만, 레지스터를 자동으로 저장하지 않습니다. 예외 핸들러는 레지스터를 메모리에 저장하기 위해 최소 2개의 "스크래치" 레지스터가 필요합니다. $k0/$k1은 이 목적으로 예약되어 있으며, 예외가 언제든 발생할 수 있으므로 이 레지스터의 값은 예고 없이 덮어쓰여질 수 있습니다. 따라서 유저 코드나 일반 커널 코드에서 $k0/$k1을 사용하면 안 됩니다. Linux 커널의 TLB refill 핸들러에서 $k0으로 Context 레지스터를, $k1으로 PTE 값을 임시 저장하는 패턴이 대표적입니다.
MIPS 범용 레지스터 용도별 그룹 고정/특수 $0 (zero) 항상 0 $1 (at) 어셈블러 임시 $28 (gp) 전역 포인터 $29 (sp) 스택 포인터 $31 (ra) 복귀 주소 Caller-saved (임시) $2-$3 (v0-v1) 반환값 $4-$7 (a0-a3) 인자 $8-$15 (t0-t7) 임시 $24-$25 (t8-t9) 임시 Callee-saved (보존) $16-$23 (s0-s7) $30 (fp/s8) 함수 호출 전후 값 유지 피호출자가 저장/복원 책임 커널 예약 $26 (k0) 예외 임시 $27 (k1) 예외 임시 예외 발생 시 언제든 덮어쓰여질 수 있음 HI / LO 특수 레지스터 (R2 전용, R6에서 제거) MULT/DIV 결과 저장. MFHI/MFLO로 읽기, MTHI/MTLO로 쓰기. R6에서는 MUL/DIV가 GPR 직접 사용. PC (프로그램 카운터) 직접 접근 불가. JAL이 PC+8을 $ra에 저장. 분기는 PC 상대 오프셋 사용. o32 ABI: 인자 4개($a0-$a3), 임시 10개($t0-$t9), 보존 8개($s0-$s7), 반환 2개($v0-$v1) n64 ABI: 인자 8개($a0-$a7=$t0-$t3 재사용), 임시 6개($t4-$t9 축소), 보존/반환 동일

특수 레지스터

레지스터설명
HI / LO곱셈 상위/하위, 나눗셈 나머지/몫 (R2, R6에서 제거)
PC프로그램 카운터 (직접 접근 불가)

CP0 (Coprocessor 0) 레지스터 — 주요

번호이름설명
$8BadVAddr가장 최근 주소 오류의 가상 주소
$9Count카운터 (타이머)
$11Compare카운터 비교값 (타이머 인터럽트)
$12Status프로세서 상태 (IE, EXL, ERL, KSU, IM 등)
$13Cause예외 원인 (ExcCode, IP 등)
$14EPC예외 PC (복귀 주소)
$15PRId프로세서 ID
$16Config구성 레지스터
$0IndexTLB 인덱스
$1RandomTLB 랜덤 인덱스
$2-$3EntryLo0/1TLB 엔트리 하위 (짝수/홀수 페이지)
$4ContextTLB 미스 컨텍스트
$5PageMaskTLB 페이지 마스크
$6Wired고정 TLB 엔트리 수
$10EntryHiTLB 엔트리 상위 (VPN2, ASID)
$15.1EBase예외 베이스 주소 (R2+)
$30ErrorEPC에러 예외 PC

CP0 Status 레지스터 비트 필드 ($12)

CP0 Status 레지스터 ($12) 비트 레이아웃 비트 31 비트 16 CU3 CU2 CU1 CU0 RP FR RE MX PX BEV TS SR NMI IM[7:0] (인터럽트 마스크) 비트 7 비트 0 KX SX UX KSU[1:0] (모드) ERL EXL IE CU[3:0]: 코프로세서 사용 가능 (CU1=FPU) | BEV: 부트 예외 벡터 (1=ROM 벡터) | IM[7:0]: 개별 인터럽트 마스크 KSU: 00=커널, 01=슈퍼바이저, 10=유저 | ERL: 에러 레벨 (1=에러 예외 처리 중) | EXL: 예외 레벨 (1=예외 처리 중) IE: 전역 인터럽트 활성화 | FR: FPU 64-bit 레지스터 | KX/SX/UX: 64-bit 주소 모드 (MIPS64) 인터럽트 전달 조건: IE=1 AND EXL=0 AND ERL=0 AND 해당 IM 비트=1

CP0 Cause 레지스터 비트 필드 ($13)

CP0 Cause 레지스터 ($13) 비트 레이아웃 비트 31 비트 0 BD TI CE[1:0] DC PCI IV WP FDCI IP[7:0] (인터럽트 펜딩) 0 ExcCode[4:0] BD: 지연 슬롯에서 예외 (1이면 EPC-4가 분기 명령어) | TI: 타이머 인터럽트 | CE: 코프로세서 에러 번호 IV: 인터럽트 전용 벡터 사용 | IP[7:2]: 하드웨어 인터럽트 | IP[1:0]: 소프트웨어 인터럽트 (쓰기 가능) 주요 ExcCode 값: 0=Int(인터럽트) | 1=Mod(TLB수정) | 2=TLBL(TLB로드) | 3=TLBS(TLB저장) | 4=AdEL(주소에러로드) | 5=AdES(주소에러저장) 8=Sys(SYSCALL) | 9=Bp(BREAK) | 10=RI(예약 명령어) | 11=CpU(코프로세서 불가) | 13=Ov(오버플로)

MIPS64 n64 ABI 차이

n64 ABI: $a0-$a7 (8개 인자 레지스터), $v0-$v1 반환. o32 ABI는 $a0-$a3 (4개)만 사용하고 나머지는 스택으로 전달.

주소 지정 모드

MIPS는 매우 제한적인 주소 모드를 사용합니다: 베이스 + 16-bit 부호 확장 오프셋만 지원합니다.

패턴문법설명예제
베이스+오프셋offset(base)[base + sign_ext(offset)]lw $t0, 8($sp)
32-bit 즉시값lui + ori상위 16 + 하위 16 조합lui $t0, %hi(sym); ori $t0, $t0, %lo(sym)
의사 명령어 lili $t0, imm어셈블러가 lui+ori로 확장li $t0, 0x12345678
의사 명령어 lala $t0, symbol주소 로드la $t0, my_var
지연 슬롯 (Branch Delay Slot): MIPS R2에서 모든 분기/점프 명령어 다음 1개 명령어는 분기 결과와 무관하게 항상 실행됩니다. 이는 파이프라인 효율을 위한 설계입니다. NOP으로 채우거나 유용한 명령어를 배치합니다. R6의 compact branch(BC, BALC 등)는 지연 슬롯이 없습니다.
지연 슬롯 구체적 예제: 아래 코드에서 addiu는 분기 결과와 무관하게 항상 실행됩니다:
    beq     $t0, $t1, target       /* $t0 == $t1이면 target으로 분기 */
    addiu   $v0, $v0, 1            /* ← 지연 슬롯: 항상 실행됨! */
    /* 여기는 분기하지 않았을 때만 실행 */
    ...
target:
    /* $v0는 이미 +1된 상태 (지연 슬롯이 실행되었으므로) */
주의: 지연 슬롯에서 분기 조건의 소스 레지스터를 수정하면 안 됩니다. 또한 지연 슬롯에 또 다른 분기 명령어를 넣는 것은 미정의 동작입니다.
.set noreorder / .set reorder 지시자: 커널 어셈블리에서는 지연 슬롯을 직접 관리하기 위해 GAS 지시자를 사용합니다:
  • .set noreorder — 어셈블러가 명령어 재배치/NOP 삽입을 하지 않음. 프로그래머가 지연 슬롯을 직접 채워야 함. 커널 핵심 경로(예외 핸들러, TLB refill)에서 사용.
  • .set reorder — 어셈블러가 필요 시 NOP을 자동 삽입하고 명령어 순서를 최적화. 일반 C 호출 함수에서 사용.
    .set    noreorder
    beq     $t0, $zero, done
    addiu   $v0, $v0, 1            /* 유용한 명령어를 지연 슬롯에 배치 */
    .set    reorder
Linux 커널의 arch/mips/ 어셈블리 코드는 대부분 .set noreorder 블록 내에서 작성됩니다.

데이터 전송 명령어

명령어문법설명비고
LB / LBUlb $t0, 0($a0)바이트 로드 (부호/제로 확장)
LH / LHUlh $t0, 0($a0)하프워드 로드
LWlw $t0, 0($a0)워드 로드
LWUlwu $t0, 0($a0)워드 제로 확장 로드MIPS64
LDld $t0, 0($a0)더블워드 로드MIPS64
SB / SH / SW / SDsw $t0, 0($a0)바이트/하프/워드/더블 저장
LUIlui $t0, 0x1234상위 16-bit 즉시값 로드
LWL / LWRlwl $t0, 3($a0)비정렬 워드 로드 (좌/우)R2 전용
MFC0 / MTC0mfc0 $t0, $12CP0 레지스터 읽기/쓰기특권
MFHI / MFLOmfhi $t0HI/LO 레지스터 읽기R2
MTHI / MTLOmthi $t0HI/LO 레지스터 쓰기R2
MFC1 / MTC1mfc1 $t0, $f0FPU 레지스터 ↔ GPR
LWC1 / LDC1lwc1 $f0, 0($a0)FPU 메모리 로드
SWC1 / SDC1swc1 $f0, 0($a0)FPU 메모리 저장
비정렬 메모리 접근 (Unaligned Access): MIPS R2에서 LW/SW는 4바이트 정렬을 요구합니다. 비정렬 주소에서 워드를 읽으려면 LWL(Load Word Left)과 LWR(Load Word Right)을 쌍으로 사용해야 합니다. R6에서는 이 문제가 해결되어 LW/SW가 비정렬 접근을 하드웨어적으로 지원하며, LWL/LWR은 제거되었습니다.
/* R2: 비정렬 32-bit 로드 (LWL/LWR 패턴) */
/* $a0에 비정렬 주소가 있다고 가정 (예: 0x10003) */
/* Big-Endian 기준 */
    lwl     $t0, 0($a0)            /* 주소의 워드 좌측 바이트들 로드 */
    lwr     $t0, 3($a0)            /* 주소의 워드 우측 바이트들 로드 */
    /* 이제 $t0에 4바이트가 올바르게 로드됨 */

/* R2: 비정렬 32-bit 저장 (SWL/SWR 패턴) */
    swl     $t0, 0($a0)            /* 워드 좌측 바이트들 저장 */
    swr     $t0, 3($a0)            /* 워드 우측 바이트들 저장 */

/* R6: 단순화됨 — LW/SW가 비정렬 지원 */
    lw      $t0, 0($a0)            /* R6에서는 비정렬이어도 정상 동작 */
커널에서의 비정렬 접근: Linux 커널의 arch/mips/kernel/unaligned.c는 비정렬 접근 예외(AdEL/AdES)를 에뮬레이션합니다. R2 커널 코드에서 비정렬 접근이 필요한 경우 get_unaligned() / put_unaligned() 매크로를 사용하며, 이 매크로는 내부적으로 LWL/LWR 패턴을 사용합니다.

산술 명령어

명령어문법설명비고
ADD / ADDUaddu $t0, $t1, $t2덧셈 (ADD=오버플로 트랩, ADDU=무시)
ADDI / ADDIUaddiu $t0, $t1, 42즉시값 덧셈
SUB / SUBUsubu $t0, $t1, $t2뺄셈
MULT / MULTUmult $t0, $t1곱셈 → HI:LOR2
DIV / DIVUdiv $t0, $t1나눗셈 → LO=몫, HI=나머지R2
MUL (R6)mul $t0, $t1, $t2곱셈 → GPR (하위)R6
MUH / MULU / MUHU (R6)muh $t0, $t1, $t2곱셈 상위 / 부호없는R6
DIV / MOD (R6)div $t0, $t1, $t2나눗셈 / 나머지 → GPRR6
MADD / MADDUmadd $t0, $t1곱셈-누적: HI:LO += t0*t1R2
CLO / CLZclz $t0, $t1선행 1/0 카운트R2+
DADD / DADDU / DSUBdaddu $t0, $t1, $t264-bit 덧셈/뺄셈MIPS64
DMULT / DDIVdmult $t0, $t164-bit 곱셈/나눗셈MIPS64
R2 vs R6 곱셈/나눗셈 비교:
  • R2 방식: MULT rs, rt → 결과가 HI:LO에 저장됨 → MFHI/MFLO로 읽어야 함. 3개 명령어 필요.
  • R6 방식: MUL rd, rs, rt (하위 32-bit) / MUH rd, rs, rt (상위 32-bit) → GPR에 직접 저장. 1개 명령어로 완료.
  • R6에서 HI/LO 레지스터와 MFHI/MFLO/MTHI/MTLO/MADD/MSUB 모두 제거되었습니다.
/* R2: 곱셈 패턴 — result = $a0 * $a1 */
    mult    $a0, $a1               /* HI:LO = $a0 * $a1 */
    mflo    $v0                    /* $v0 = 결과 하위 32-bit (곱의 하위) */
    mfhi    $v1                    /* $v1 = 결과 상위 32-bit */

/* R2: 나눗셈 패턴 — quotient = $a0 / $a1, remainder = $a0 % $a1 */
    div     $zero, $a0, $a1        /* LO=몫, HI=나머지 */
    mflo    $v0                    /* $v0 = 몫 */
    mfhi    $v1                    /* $v1 = 나머지 */

/* R6: 곱셈 — 결과가 GPR에 직접 */
    mul     $v0, $a0, $a1          /* $v0 = ($a0 * $a1) 하위 32-bit */
    muh     $v1, $a0, $a1          /* $v1 = ($a0 * $a1) 상위 32-bit */

/* R6: 나눗셈 — 몫과 나머지 각각 별도 명령어 */
    div     $v0, $a0, $a1          /* $v0 = $a0 / $a1 (몫) */
    mod     $v1, $a0, $a1          /* $v1 = $a0 % $a1 (나머지) */

논리/시프트/비트 조작 명령어

명령어문법설명비고
AND / ANDIand $t0, $t1, $t2비트 AND
OR / ORIor $t0, $t1, $t2비트 OR
XOR / XORIxor $t0, $t1, $t2비트 XOR
NORnor $t0, $t1, $t2비트 NOR (NOT = nor $t0,$t1,$zero)
SLLsll $t0, $t1, 4논리 좌측 시프트 (즉시값)
SRLsrl $t0, $t1, 4논리 우측 시프트
SRAsra $t0, $t1, 4산술 우측 시프트
SLLV / SRLV / SRAVsllv $t0, $t1, $t2변수 시프트 (레지스터 양)
INSins $t0, $t1, pos, size비트 필드 삽입R2
EXText $t0, $t1, pos, size비트 필드 추출R2
WSBHwsbh $t0, $t1하프워드 내 바이트 스왑R2
SEB / SEHseb $t0, $t1바이트/하프워드 부호 확장R2
ROTR / ROTRVrotr $t0, $t1, 8우측 순환 시프트R2
BITSWAP (R6)bitswap $t0, $t1바이트 내 비트 반전R6
ALIGN (R6)align $t0, $t1, $t2, 2바이트 정렬 추출R6
DSLL / DSRL / DSRAdsll $t0, $t1, 464-bit 시프트MIPS64
DSLL32 / DSRL32 / DSRA32dsll32 $t0, $t1, 464-bit 시프트 (32+ 위치)MIPS64

비교/분기 명령어

명령어문법설명지연 슬롯
SLT / SLTIslt $t0, $t1, $t2부호 있는 < 비교 (1/0)
SLTU / SLTIUsltu $t0, $t1, $t2부호 없는 < 비교
BEQbeq $t0, $t1, label같으면 분기있음 (R2)
BNEbne $t0, $t1, label다르면 분기있음 (R2)
BGTZ / BLEZbgtz $t0, label>0 / <=0 분기있음 (R2)
BLTZ / BGEZbltz $t0, label<0 / >=0 분기있음 (R2)
BLTZAL / BGEZALbltzal $t0, label조건부 분기 + 링크있음 (R2)
Jj label무조건 점프 (26-bit 타겟)있음 (R2)
JALjal func함수 호출 ($ra ← PC+8)있음 (R2)
JRjr $ra간접 점프 (레지스터)있음 (R2)
JALRjalr $ra, $t0간접 호출있음 (R2)
BC (R6)bc label무조건 분기 (compact)없음
BALC (R6)balc label무조건 호출 (compact)없음
BEQZC / BNEZC (R6)beqzc $t0, label제로/비제로 compact 분기없음
BEQC / BNEC (R6)beqc $t0, $t1, label비교 compact 분기없음
MOVZ / MOVNmovz $t0, $t1, $t2조건부 이동 (R2)
SELEQZ / SELNEZ (R6)seleqz $t0, $t1, $t2조건부 선택 (R6 대체)
Branch Likely 명령어 (R2, R6에서 제거): BEQL, BNEL, BGTZL, BLEZL 등의 "branch likely" 변형은 분기가 taken일 때만 지연 슬롯을 실행합니다 (not-taken이면 지연 슬롯 명령어를 무효화). 이는 컴파일러가 taken 경로에 유용한 명령어를 배치하기 쉽게 해주었지만, 하드웨어 구현이 복잡해져 R6에서 완전히 제거되었습니다. Linux 커널에서는 R2 TLB 핸들러 등 성능 크리티컬한 코드에서 여전히 사용됩니다.
지연 슬롯 최적화 예제: NOP 대신 유용한 명령어를 지연 슬롯에 배치하여 성능을 향상시킵니다:
/* 비최적화: NOP 낭비 */
    addiu   $v0, $v0, 1
    bne     $v0, $a0, loop
    nop                             /* 낭비되는 사이클 */

/* 최적화: 유용한 명령어를 지연 슬롯에 이동 */
    bne     $v0, $a0, loop
    addiu   $v0, $v0, 1            /* 지연 슬롯에서 실행 — 1 사이클 절약 */

/* 함수 호출 최적화: 인자 설정을 지연 슬롯에 */
    jal     some_func
    move    $a0, $s0               /* 지연 슬롯: 인자를 $a0에 설정 */

스택/함수 호출 명령어

MIPS에는 PUSH/POP 명령어가 없습니다. addiu $spsw/lw로 수동 관리합니다.

/* 함수 프롤로그/에필로그 (o32 ABI) */
my_func:
    addiu   $sp, $sp, -32         /* 스택 프레임 할당 */
    sw      $ra, 28($sp)           /* 복귀 주소 저장 */
    sw      $fp, 24($sp)           /* 프레임 포인터 저장 */
    move    $fp, $sp               /* 프레임 포인터 설정 */
    /* ... 함수 본문 ... */
    move    $sp, $fp               /* 스택 복원 */
    lw      $fp, 24($sp)           /* 프레임 포인터 복원 */
    lw      $ra, 28($sp)           /* 복귀 주소 복원 */
    addiu   $sp, $sp, 32           /* 스택 프레임 해제 */
    jr      $ra                    /* 복귀 (지연 슬롯) */
    nop                             /* 지연 슬롯 */
o32 ABI 호출 규약:
  • 인자: $a0-$a3 (4개, 나머지 스택)
  • 반환값: $v0 (+ $v1)
  • Callee-saved: $s0-$s7, $fp, $sp
  • Caller-saved: $t0-$t9, $a0-$a3, $v0-$v1, $ra
  • n64 ABI: $a0-$a7 (8개 인자 레지스터)

시스템/특권 명령어

명령어설명비고
SYSCALL시스템 콜 트랩
BREAK디버그 브레이크포인트 트랩
ERET예외 복귀 (EPC → PC, Status 복원)
MFC0 / MTC0CP0 레지스터 읽기/쓰기
DMFC0 / DMTC064-bit CP0 접근MIPS64
WAIT저전력 대기 (인터럽트까지)
EI / DI인터럽트 활성화/비활성화R2+
TLBRTLB 읽기 (Index → EntryHi/Lo)
TLBWITLB 쓰기 (Index 지정)
TLBWRTLB 쓰기 (Random 인덱스)
TLBPTLB 탐색 (EntryHi → Index)
CACHE캐시 조작 (I-cache/D-cache)
SYNC메모리 배리어
RDHWR하드웨어 레지스터 읽기 (ULR 등)R2+
GINVI / GINVT (R6)전역 TLB 무효화R6

예외 벡터 레이아웃

MIPS 예외 벡터 주소 (BEV=0, kseg0 기준) BEV=0 0x80000000 TLB Refill (32-bit) — TLBL/TLBS, EXL=0 0x80000080 XTLB Refill (64-bit) — MIPS64 TLB 미스 0x80000100 Cache Error — 캐시 패리티/ECC 에러 0x80000180 General Exception — SYSCALL, 인터럽트, 기타 모든 예외 0x80000200 Interrupt (Cause.IV=1) — 전용 인터럽트 벡터 (선택적) BEV=1 0xBFC00000 Reset / NMI — kseg1 (비캐시). 부트 시 BEV=1, 커널이 BEV=0으로 전환
EI/DI 인터럽트 관리 (R2+): Release 2부터 도입된 EI(Enable Interrupts)과 DI(Disable Interrupts)는 CP0 Status의 IE 비트를 원자적으로 설정/해제합니다. 이전 R1에서는 MFC0/MTC0으로 Status 레지스터를 읽고-수정-쓰기 해야 했으며, 이 사이에 인터럽트가 발생할 수 있는 경쟁 조건이 있었습니다.
/* R2+: 원자적 인터럽트 비활성화 */
    di      $t0                    /* $t0 = 이전 Status, IE←0 */
    ehb                            /* Execution Hazard Barrier */
    /* ... 크리티컬 섹션 ... */
    ei                             /* IE←1, 인터럽트 재활성화 */

/* R1 (구형): MFC0/MTC0 패턴 — 경쟁 조건 가능 */
    mfc0    $t0, $12               /* Status 읽기 */
    ori     $t1, $t0, 1            /* IE 비트 세트 */
    mtc0    $t1, $12               /* Status 쓰기 — 여기서 인터럽트 경쟁 가능 */

원자적/동기화 명령어

명령어문법설명
LLll $t0, 0($a0)Load Linked (32-bit 독점 로드)
SCsc $t1, 0($a0)Store Conditional ($t1=0이면 실패, 1이면 성공)
LLDlld $t0, 0($a0)64-bit Load Linked (MIPS64)
SCDscd $t1, 0($a0)64-bit Store Conditional (MIPS64)
SYNCsync 0메모리 배리어 (0=full barrier)

LL/SC CAS 루프 패턴

/* compare_and_swap(addr=$a0, expected=$a1, desired=$a2) → old in $v0 */
cas_loop:
    ll      $v0, 0($a0)           /* Load Linked */
    bne     $v0, $a1, 1f          /* expected와 다르면 실패 */
    nop                             /* 지연 슬롯 */
    move    $t0, $a2               /* desired 값 복사 */
    sc      $t0, 0($a0)           /* Store Conditional */
    beqz    $t0, cas_loop          /* SC 실패(0) 시 재시도 */
    nop                             /* 지연 슬롯 */
1:
    jr      $ra                    /* $v0에 이전 값 반환 */
    nop                             /* 지연 슬롯 */
LL/SC 전진 보장 제약 조건: LL과 SC 사이의 코드는 매우 엄격한 제약을 따라야 합니다:
  • LL과 SC 사이에 다른 메모리 접근(load/store) 금지 — SC 실패를 유발할 수 있음
  • LL과 SC 사이의 명령어 수를 최소화 (구현에 따라 제한 있음, 일반적으로 수십 개 이내)
  • LL과 SC 사이에 분기/점프 금지 (조건부 이동 MOVZ/MOVN은 허용)
  • LL과 SC는 동일한 주소를 대상으로 해야 함
  • SC 결과 레지스터를 반드시 확인하여 실패 시 재시도해야 함
  • 다른 코어의 SC가 동일 캐시 라인에 쓰면 LL의 링크가 해제되어 SC 실패

LL/SC 스핀락 구현

/* arch_spin_lock(lock=$a0) — 간소화된 MIPS 스핀락 */
arch_spin_lock:
    .set    noreorder
1:
    ll      $t0, 0($a0)           /* 락 값 Load Linked */
    bnez    $t0, 1b               /* 이미 잠겨있으면 재시도 */
    li      $t1, 1                 /* 지연 슬롯: 잠금 값 준비 */
    sc      $t1, 0($a0)           /* Store Conditional (잠금 시도) */
    beqz    $t1, 1b               /* SC 실패 시 재시도 */
    nop                             /* 지연 슬롯 */
    sync                            /* 메모리 배리어 (acquire) */
    jr      $ra
    nop
    .set    reorder

/* arch_spin_unlock(lock=$a0) */
arch_spin_unlock:
    .set    noreorder
    sync                            /* 메모리 배리어 (release) */
    sw      $zero, 0($a0)          /* 락 해제 (일반 store로 충분) */
    jr      $ra
    nop
    .set    reorder
SYNC stype 변형: SYNC 명령어는 stype 필드(비트 10:6)로 배리어 범위를 지정합니다:
stype이름설명
0x00SYNC완전 배리어 — 모든 이전 load/store 완료 후 이후 접근 시작
0x10SYNC_WMB쓰기 배리어 — 이전 store 완료 후 이후 store 시작 (Linux wmb())
0x13SYNC_MB읽기-쓰기 배리어 (Linux mb())
0x12SYNC_RMB읽기 배리어 — 이전 load 완료 후 이후 load 시작 (Linux rmb())
Linux 커널의 arch/mips/include/asm/barrier.h에서 mb(), wmb(), rmb() 매크로가 이 stype 값을 사용합니다.

MSA (MIPS SIMD Architecture) 명령어

MSA는 128-bit 벡터 레지스터 $w0-$w31을 사용하며, 바이트(.b)/하프워드(.h)/워드(.w)/더블워드(.d) 요소 타입을 지원합니다.

명령어설명
LD.B/H/W/D $w0, 0($a0)벡터 로드 (128-bit)
ST.B/H/W/D $w0, 0($a0)벡터 저장
ADDV.B/H/W/D $w0, $w1, $w2벡터 정수 덧셈
SUBV.B/H/W/D $w0, $w1, $w2벡터 정수 뺄셈
MULV.B/H/W/D $w0, $w1, $w2벡터 정수 곱셈
MADDV / MSUBV벡터 곱셈-누적 / 곱셈-뺄셈
AND.V / OR.V / XOR.V / NOR.V벡터 비트 논리 연산
SLL.B/H/W/D / SRL / SRA벡터 시프트
CEQ.B/H/W/D / CLT / CLE벡터 비교 (같음/작음/이하)
FADD.W/D / FSUB / FMUL / FDIV벡터 부동소수점 연산
FMADD.W/D벡터 FMA
SHF.B/H/W $w0, $w1, imm4-요소 셔플
VSHF.B/H/W/D $w0, $w1, $w2벡터 셔플
PCKEV / PCKOD짝수/홀수 요소 팩
ILVL / ILVR좌/우 인터리브
HADD_S/U / HSUB_S/U수평 덧셈/뺄셈

커널 핵심 명령어 심화

예외 핸들러 디스패치 (Cause.ExcCode)

MIPS에서 General Exception 벡터(0x80000180)에 도달하면, 커널은 Cause.ExcCode 필드를 확인하여 적절한 핸들러로 분기합니다. Linux 커널의 arch/mips/kernel/genex.S에서 이 디스패치 로직을 구현합니다.

/* General Exception 벡터 진입점 (0x80000180) */
/* arch/mips/kernel/genex.S — except_vec3_generic */
except_vec3_generic:
    .set    noreorder
    mfc0    $k1, $13               /* Cause 레지스터 읽기 */
    andi    $k1, $k1, 0x7c         /* ExcCode 필드 추출 (비트 6:2) */
    /* ExcCode * 4 = exception_handlers 테이블 인덱스 (이미 <<2 위치) */
    la      $k0, exception_handlers
    addu    $k0, $k0, $k1          /* 테이블 + ExcCode*4 */
    lw      $k0, 0($k0)            /* 핸들러 주소 로드 */
    jr      $k0                    /* 핸들러로 점프 */
    nop                             /* 지연 슬롯 */
    .set    reorder

/* exception_handlers 테이블 (ExcCode별 핸들러 포인터) */
/*  0: handle_int       — 인터럽트 */
/*  1: handle_tlbm      — TLB Modified */
/*  2: handle_tlbl      — TLB Load 미스 */
/*  3: handle_tlbs      — TLB Store 미스 */
/*  4: handle_adel      — Address Error (Load) */
/*  5: handle_ades      — Address Error (Store) */
/*  8: handle_sys       — System Call */
/*  9: handle_bp        — Breakpoint */
/* 10: handle_ri        — Reserved Instruction */
/* 11: handle_cpu       — Coprocessor Unusable */
/* 13: handle_ov        — Overflow */

예외 진입/복귀 전체 시퀀스

/* 예외 진입 — 레지스터 저장 (arch/mips/kernel/genex.S SAVE_ALL 매크로) */
exception_entry:
    .set    noreorder
    .set    noat
    mfc0    $k0, $12               /* Status 레지스터 */
    mfc0    $k1, $14               /* EPC (복귀 주소) */

    /* 커널 스택 포인터 확보 */
    addiu   $sp, $sp, -320         /* pt_regs 구조체 크기만큼 할당 */

    /* 모든 범용 레지스터 저장 */
    sw      $zero, 0($sp)          /* $0 (형식상) */
    sw      $at, 4($sp)            /* $at */
    sw      $v0, 8($sp)            /* $v0 */
    sw      $v1, 12($sp)           /* $v1 */
    sw      $a0, 16($sp)           /* $a0-$a3 */
    sw      $a1, 20($sp)
    sw      $a2, 24($sp)
    sw      $a3, 28($sp)
    /* ... $t0-$t7, $s0-$s7, $t8-$t9 저장 생략 ... */
    sw      $gp, 112($sp)          /* $gp */
    sw      $fp, 120($sp)          /* $fp */
    sw      $ra, 124($sp)          /* $ra */

    /* CP0 레지스터 저장 */
    sw      $k0, 128($sp)          /* Status */
    sw      $k1, 132($sp)          /* EPC */
    mfc0    $t0, $13
    sw      $t0, 136($sp)          /* Cause */
    mfc0    $t0, $8
    sw      $t0, 140($sp)          /* BadVAddr */
    /* HI/LO도 저장 (R2의 경우) */
    mfhi    $t0
    sw      $t0, 144($sp)          /* HI */
    mflo    $t0
    sw      $t0, 148($sp)          /* LO */
    .set    at
    .set    reorder

/* 예외 복귀 — 레지스터 복원 (RESTORE_ALL 매크로) */
exception_exit:
    .set    noreorder
    .set    noat
    /* HI/LO 복원 */
    lw      $t0, 144($sp)
    mthi    $t0
    lw      $t0, 148($sp)
    mtlo    $t0
    /* CP0 복원 */
    lw      $t0, 128($sp)          /* Status */
    lw      $t1, 132($sp)          /* EPC */
    mtc0    $t0, $12               /* Status 복원 */
    mtc0    $t1, $14               /* EPC 복원 */
    /* 모든 범용 레지스터 복원 */
    lw      $ra, 124($sp)
    lw      $fp, 120($sp)
    lw      $gp, 112($sp)
    /* ... $s0-$s7, $t0-$t9, $a0-$a3, $v0-$v1 복원 ... */
    lw      $at, 4($sp)
    addiu   $sp, $sp, 320          /* 스택 프레임 해제 */
    eret                            /* PC←EPC, EXL←0 (원자적) */
    .set    at
    .set    reorder

SYSCALL 진입 경로

/* 유저 공간: SYSCALL 명령어 → 예외 벡터로 트랩 */
/* 예외 벡터: 0x80000180 (General Exception, BEV=0) */
/* Cause.ExcCode = 8 (Syscall) */

/* arch/mips/kernel/scall32-o32.S 핵심 경로 */
handle_sys:
    lw      $v0, PT_R2($sp)        /* syscall 번호 ($v0) */
    sll     $v0, $v0, 2            /* × 4 (테이블 인덱스) */
    la      $t1, sys_call_table
    addu    $t1, $t1, $v0
    lw      $t0, 0($t1)            /* 시스콜 핸들러 주소 */
    jalr    $t0                    /* 핸들러 호출 */
    nop                             /* 지연 슬롯 */

ERET — 예외 복귀

/* Status.EXL=1 → EPC에서 복귀, EXL=0으로 복원 */
    mfc0    $k0, $14               /* EPC 읽기 */
    mfc0    $k1, $12               /* Status 읽기 */
    /* Status 복원 처리 ... */
    mtc0    $k1, $12               /* Status 쓰기 */
    eret                            /* PC←EPC, EXL←0 */

TLB 조작 패턴

/* TLB Refill 핸들러 (0x80000000) */
tlb_refill:
    mfc0    $k0, $4                /* Context 레지스터 */
    lw      $k1, 0($k0)            /* PTE 로드 */
    mtc0    $k1, $2                /* EntryLo0 */
    lw      $k1, 4($k0)            /* 다음 PTE */
    mtc0    $k1, $3                /* EntryLo1 */
    tlbwr                           /* Random 인덱스로 TLB 쓰기 */
    eret

CACHE 명령어

/* D-cache writeback + invalidate */
static inline void flush_dcache_line(unsigned long addr)
{
    asm volatile(
        "cache %0, 0(%1)"
        :: "i"(Hit_Writeback_Inv_D), "r"(addr));
}
CACHE 명령어 연산 타입: CACHE 명령어는 5-bit op 필드로 캐시 종류(I/D)와 연산을 지정합니다. 하위 2비트가 캐시 종류, 상위 3비트가 연산을 나타냅니다:
op 값매크로설명
0x00Index_Invalidate_II-cache: 인덱스로 무효화
0x01Index_Writeback_Inv_DD-cache: 인덱스로 기록 후 무효화
0x08Index_Store_Tag_II-cache: 태그 저장 (초기화용)
0x09Index_Store_Tag_DD-cache: 태그 저장 (초기화용)
0x10Hit_Invalidate_II-cache: 주소 히트 시 무효화 (자주 사용)
0x11Hit_Invalidate_DD-cache: 주소 히트 시 무효화 (쓰기 미저장)
0x15Hit_Writeback_Inv_DD-cache: 주소 히트 시 기록 후 무효화 (가장 일반적)
0x19Hit_Writeback_DD-cache: 주소 히트 시 기록 (무효화하지 않음)
커널에서 가장 많이 사용되는 패턴: DMA 전송 전 Hit_Writeback_Inv_D로 D-cache를 메모리에 기록하고, 자체 수정 코드 후 Hit_Invalidate_I로 I-cache를 무효화합니다.

명령어 인코딩

MIPS는 3가지 기본 인코딩 타입을 사용합니다: R(레지스터), I(즉시값), J(점프).

MIPS 32-bit 인코딩 타입 (R / I / J) 31 26 25 21 20 16 15 11 10 6 5 0 R: op (6) rs (5) rt (5) rd (5) sa (5) funct (6) I: op (6) rs (5) rt (5) immediate (16) J: op (6) target (26) R: ADD/SUB/AND/OR/XOR/SLL/SRL/SRA/JR/JALR/MULT/DIV I: ADDI/LW/SW/BEQ/BNE/LUI/ORI/ANDI/SLTI | J: J/JAL

인코딩 실제 예: addu $t0, $t1, $t2

R-type 인코딩 분석: addu $t0, $t1, $t2는 R-type 명령어입니다. 각 필드에 실제 값을 대입하면:
필드이진수설명
op0 (SPECIAL)000000R-type은 op=0, funct로 구분
rs$t1 = $901001소스 레지스터 1
rt$t2 = $1001010소스 레지스터 2
rd$t0 = $801000목적지 레지스터
sa000000시프트 양 (ADDU에선 미사용)
funct0x21 (ADDU)100001ADDU 함수 코드
결과: 000000 01001 01010 01000 00000 100001 = 0x012A4021

R6 Compact Branch 인코딩

R6 Compact Branch 인코딩 (지연 슬롯 없음) BC: 110010 (0x32) offset[25:0] (부호 확장, <<2 → +/-128MB 범위) BEQZC: 110110 (0x36) rs (5) offset[20:0] (부호 확장, <<2 → +/-4MB 범위) BEQC: 001000 (0x08) rs (5) rt (5) offset[15:0] (부호 확장, <<2) R6 compact branch는 지연 슬롯이 없으며, 분기 다음 명령어는 분기하지 않았을 때만 실행됩니다. 기존 R2 BEQ/BNE 인코딩과 opcode가 다르므로 R2/R6 바이너리는 호환되지 않습니다.

TLB 관리 심화

MIPS는 소프트웨어 관리 TLB를 사용합니다. TLB 미스 시 CPU가 자동으로 페이지 테이블을 검색하는 x86/ARM64와 달리, MIPS는 TLB Refill 예외를 발생시키고 커널이 직접 TLB 엔트리를 채워야 합니다. 이를 위해 EntryHi, EntryLo0/1, PageMask, Index CP0 레지스터와 TLBWR/TLBWI/TLBR/TLBP 명령어를 사용합니다.

MIPS 소프트웨어 관리 TLB 구조 TLB 엔트리 구조 (쌍 페이지 매핑) EntryHi: VPN2 [31:13] | ASID [7:0] PageMask [28:13] Index [31,5:0] EntryLo0: PFN [29:6] | C [5:3] | D V G EntryLo1: PFN [29:6] | C [5:3] | D V G — 짝수/홀수 페이지 쌍 VPN2: 가상 페이지 번호 (짝수 페이지, VA[31:13]) | ASID: 주소 공간 식별자 (8비트, 256개) EntryLo0/EntryLo1 비트 필드 bit 0 (G) : Global — ASID 무시 bit 1 (V) : Valid — 유효한 매핑 bit 2 (D) : Dirty — 쓰기 허용 bit 5:3 (C): Cache Coherency 2=Uncached, 3=Cacheable(WB) 4=Cacheable(WA), 5=Uncached Accel bit 29:6 (PFN): 물리 프레임 번호 TLB 관리 명령어 TLBR Index → EntryHi/Lo0/Lo1 읽기 TLBWI Index 위치에 TLB 쓰기 TLBWR Random 위치에 TLB 쓰기 TLBP EntryHi로 TLB 검색 → Index TLBINV / TLBINVF (R6) TLB 무효화 TLB Refill 예외 흐름 (ExcCode=2/3) TLB 미스! ExcCode=2(L)/3(S) 0x80000000 진입 PTE 조회 MTC0 EntryLo0/1 TLBWR Context CP0: BadVPN2 [22:4] | PTEBase [31:23] → 페이지 테이블 엔트리 주소 직접 계산 TLB Refill 핸들러는 최소 명령어로 작성 (성능 크리티컬, ~20 명령어) PageMask: 4KB(0), 16KB(0x6000), 64KB(0x1E000), 256KB(0x7E000), ... Wired 레지스터: TLB[0..Wired-1]은 TLBWR에서 교체 대상 제외 (고정 매핑)
/* arch/mips/mm/tlbex.c — TLB Refill 핸들러 (빌드 시 동적 생성) */
/* 가장 성능 크리티컬한 커널 코드 — ~20 명령어로 최적화 */

/* 개념적 TLB Refill 핸들러 (R2/R6 공통 원리) */
tlb_refill_handler:
    /* 1. k0/k1은 예외에서 자유롭게 사용 가능 (커널 전용) */
    mfc0    k1, C0_CONTEXT       /* Context.PTEBase + BadVPN2 → PTE 주소 */
    lw      k0, 0(k1)            /* EntryLo0 = PTE[even] */
    lw      k1, 4(k1)            /* EntryLo1 = PTE[odd] */
    mtc0    k0, C0_ENTRYLO0      /* EntryLo0 설정 */
    mtc0    k1, C0_ENTRYLO1      /* EntryLo1 설정 */
    ehb                           /* Execution Hazard Barrier */
    tlbwr                         /* Random 위치에 TLB 쓰기 */
    eret                          /* 예외 복귀 (EPC로 점프) */

/* TLB 프로빙과 무효화 */
    mtc0    t0, C0_ENTRYHI       /* 검색할 VPN2|ASID 설정 */
    ehb
    tlbp                          /* TLB 검색 → Index에 결과 */
    ehb
    mfc0    t1, C0_INDEX          /* Index.P(bit31)=1이면 미스 */
    bltz    t1, not_found         /* 음수 → TLB에 없음 */
    /* Index >= 0 → 해당 위치에 무효 엔트리 쓰기 */
    mtc0    zero, C0_ENTRYLO0
    mtc0    zero, C0_ENTRYLO1
    ehb
    tlbwi                         /* Index 위치에 쓰기 (무효화) */

CP0 레지스터 비트 필드 상세

CP0 (Coprocessor 0)는 MIPS의 시스템 제어 레지스터 집합입니다. x86의 CR0-CR4/MSR, ARM64의 시스템 레지스터에 대응합니다. 커널은 mfc0/mtc0로 CP0에 접근하며, 예외 처리·TLB 관리·캐시 설정·인터럽트 제어의 핵심입니다.

MIPS CP0 핵심 레지스터 비트 필드 Status (CP0 $12, sel 0) bit 0 (IE) : Interrupt Enable bit 1 (EXL) : Exception Level (예외 중) bit 2 (ERL) : Error Level (에러 중) bit 4:3 (KSU): 모드 (00=커널,10=유저) bit 15:8 (IM): 인터럽트 마스크 (IP7:0) bit 22 (BEV): Bootstrap 예외 벡터 bit 27 (FR) : FPR 모드 (1=64비트) bit 28 (CU0): CP0 유저 접근 허용 bit 29 (CU1): FPU 활성화 EXL=1/ERL=1 → 인터럽트 비활성, 커널 모드 Cause (CP0 $13, sel 0) bit 6:2 (ExcCode): 예외 코드 0=Int, 1=TLB Mod, 2=TLBL 3=TLBS, 4=AdEL, 5=AdES 8=Syscall, 9=Bp, 10=RI 11=CpU, 13=Trap, 15=FPE bit 15:8 (IP): 인터럽트 Pending IP7: 타이머, IP6-2: 외부 인터럽트 IP1-0: 소프트웨어 인터럽트 bit 31 (BD): Branch Delay 슬롯 BD=1 → EPC는 분기 명령어 (재실행 필요) Config (CP0 $16, sel 0-5) bit 2:0 (K0): kseg0 캐시 정책 bit 14:13 (AT): 아키텍처 타입 Config1: I$/D$ 크기, TLB 엔트리 수 Config3: ULRI(UserLocal), VInt, VEIC 기타 핵심 CP0 레지스터 EPC ($14) 예외 복귀 주소 BadVAddr ($8) 주소 에러/TLB 미스 주소 PRId ($15) 프로세서 ID (제조사+모델) Count/Compare ($9/$11) 타이머 (IP7) 예외 벡터 주소 (BEV=0 기준) 0x80000000 TLB Refill (ExcCode=2/3, EXL=0) 0x80000180 일반 예외 (모든 ExcCode) 0x80000200 인터럽트 (Config3.VInt=1 시) EBase + 0x200 + n*0x20 벡터 인터럽트 (VI 모드) BEV=1(부팅): 0xBFC00200 (TLB Refill), 0xBFC00380 (일반) — ROM/Flash 영역
CP0 레지스터번호핵심 필드커널 사용
Index$0P(probe result), Index[5:0]TLBWI/TLBP 대상
Random$1Random[5:0]TLBWR 자동 감소 카운터
EntryLo0/1$2/$3PFN, C, D, V, GTLB 쓰기 데이터
Context$4PTEBase, BadVPN2TLB Refill 빠른 PTE 조회
PageMask$5Mask[28:13]가변 페이지 크기 (4KB-256MB)
Wired$6Wired[5:0]고정 TLB 엔트리 수
BadVAddr$8전체 VA주소 예외/TLB 미스 주소
Count$932비트 카운터타이머 (CPU 클럭/2)
EntryHi$10VPN2, ASIDTLB 매칭 키
Compare$1132비트 비교값Count==Compare → IP7 인터럽트
Status$12IE, EXL, ERL, KSU, IM, CU인터럽트/모드 제어
Cause$13ExcCode, IP, BD예외 원인 판별
EPC$14복귀 주소예외 복귀 (eret)
PRId$15,0Company, ProcessorCPU 식별
EBase$15,1Exception Base예외 벡터 베이스 재배치
Config$16K0, AT, AR, MT아키텍처 설정
⚠️

Hazard Barrier 필수: MIPS에서 CP0 레지스터를 mtc0로 수정한 후 해당 값이 실제로 반영되기까지 실행 해저드(Execution Hazard)가 있습니다. R2에서는 ehb(sll $0,$0,3) 명령어를, R6에서는 ehb 또는 충분한 nop 삽입이 필요합니다. TLBWI/TLBWR 전에 ehb를 빠뜨리면 이전 CP0 값이 반영되지 않아 잘못된 TLB 엔트리가 기록될 수 있습니다.

다음 학습: