컴퓨터구조

3. Data Transfer Instructions
  • H
Mar 16, 2023
  1. r-format
  2. i-format
  3. lw
  4. sw
  5. lb
  6. sb

3. Data Transfer Instructions

Created by
  • H
    HM
Date
Mar 16, 2023
Keyword
  1. r-format
  2. i-format
  3. lw
  4. sw
  5. lb
  6. sb

Data Transfer Instructions

→ 메모리에 접근하는 명령어도 필요함
왜 필요할까?
CPU가 가지고 있는 레지스터의 크기가 너무 작다
→ 이 레지스터만으로 변수들을 관리할 수가 없다
메모리와 레지스터 사이에 데이터를 주고받을 수 있는 명령어가 필요함
Memory Address 메모리 주소
(CPU 외부에 있는 메모리를 의미한다)
→ 지금까지는 D-RAM 기반의 메인메모리 사용 → 이것이 메모리 주소 개념 가짐

Memory Operands

메모리에 있는 값을 적당한 주소를 지정해 가져와 레지스터에 넣고 / 레지스터로부터 메모리에 넣어야 업데이트를 해야 함
메모리는 byte addressable 하다 → 모든 주소는 바이트 단위로 구분할 수 있다는 것
1 = 1byte(8-bit)
4byte 단위로 데이터를 주고받기 때문에 → 주소는 항상 4의 배수여야 함
MIPS는 기본적으로 Big Endian
4byte를 가지고 있을 때 most signigicant byte를 가장 낮은 주소와 매칭시키는 것 (반대가 little endian)

Word Alignment

lw (load word) 4바이트를 가져오기 위한 명령어
/ 메모리 어딘가에서부터 32bit를 읽어서 특정 레지스터에 얹이고 싶다 (메모리 읽기)
1.
레지스터 이름은 내가 지정해야 함
2.
(중요) 메모리의 수많은 공간 중 4바이트를 명시해야 함
→ 가장 작은 주소에서부터 MSB를 채우는게 Big-endian
sw (store word) 쓸 때
→ 메모리 주소를 표현할 때 4의 배수를 사용하자 (1000, 1004, 1008…)

Endianness

Big Endian : MIPS, SPARC
Little Endian : x86, RISC-V
우리가 매번 특정 주소에 4byte만 쓴다고 하면 bit, little 뭘 쓰든 혼동될 여지가 없다
→ 문제는 더 작은 단위를 읽을 때 endianness를 유의해야 함
2023년 3월 14일

Memory Operand Example 1

g = h + A[8]
→ A array의 시작주소가 s3 에 있다고 가정
MIPS code
index 8이니까 32만큼의 offset이 필요하다
**lw $t0, 32($s3)** #load word (메모리에 있는 word를 레지스터로 가져온다 / 주소명시해줘야지: base + offset) **add $s1, $s2, $t0**

Memory Operand Example 2

A[12] = h + a[8]
h in $s2, base address of A in $s3
h는 레지스터에 있으니 괜찮다. 그러나 A는 멀리있다.
→ 가장 먼저 해야 하는 것
: source 1 하나를 메모리에서 레지스터로 가져와야 한다
: lw 명령어 사용
**lw $t0, 32($s3)** # load word (A[8]을 레지스터로 가져온다) # A의 base address로부터 8*4만큼 떨어져있는 아이를 데려온다 **add $t0, $s2, $t0** # src 2개 모두 피연산자에 있으니까 (넣었으니까) **sw $t0, 48($s3)** # 덧셈 결과를 메인 메모리에 쓴다 (A[12]에 $t0에 있는 값을 넣는다)

Immediate Operands

addi
교수님 said
add를 이용한다면 destination 뿐만 아닌, i도, 1도 레지스터에 있어야 한다.
그러나 1을 항상 보관하는 레지스터가 없다.
1은 어딘가 메모리에 들어있고 (메모리는 크니까),
그 주소에
lw를 통해 접근해서 가져오면 된다.
상수를 직접 instruction 내에서 표현하기 위해서는 addi 사용
add immediate : addi
자주 일어나는 걸 빠르게 하는 것이 중요함
(lw, add 두 개의 instruction이 아닌 하나의 instruction으로 자주 일어나는 연산을 할 수 있도록 한 것)
subi 는 존재하지 않는다.

I-Format

수치 연산과 데이터 전송 명령어에 사용됨
R-format 명령어들은 모두 레지스터에 3개의 operand를 가진다
I-format 명령어들은 3개의 operands 중 하나의 source는 명령어 자체에 내장되어 있을 수 있다
→ 명령어에서 즉시 꺼내 쓸 수 있음
immediate: 명령어 32bit중 하위 16bit
음수는 2의 보수 체계를 이용해 표현한다
→ 좋은 디자인은good compromise를 요구한다 : three instruction formats 가 있다
정의: n-bit N일 때, $2^n-N$
편하게 쓰기: 2진수 → flip (뒤집어) → 1(TWO)을 더해

addi

I format instruction
**addi rt, rs, imm** # rs와 imm가 src다
2023년 3월 15일

lw

I-format
lw rt address
offset의 값이 무한정 클 수는 없다
→ 2의 보수 체계를 따른 다음에 offset을 따르는 것 (범위 표시하기)
base address + offset이 4의 배수여야만 오류가 일어나지 않는다
opcode : 35
immedate 는 [-32768, 32767] 범위

sw

I-format
sw rt, address
base address + offset (4의 배수여야함) 에 저장
→ 레지스터에서 메인 메모리로 저장한다
opcode: 43

Constant Zero

레지스터 0번 → zero register
상수 0을 무조건 보관하고 있다
0 이외의 어떤 값도 쓰기가 불가능
레지스터 0번 이름에 접근하게 되면 무조건 0을 내뱉도록 디자인 됨
언제 유용할까?
1.
한 레지스터에 있는 값을 다른 레지스터에 복사하고 싶을 때
2.
mov 명령어 대신, 불필요한 instruction 사용하지 않고 가능
3.
loading immediate value (상수를 레지스터에 직접 로딩할 때)
4.
addi → 값을 넣을 때

CPU가 Memory 에 접근해야 하는 목적

CPU가 메모리에 접근해야 하는 목적
(성능과는 무관)
프로그램 안에 있는 instructions를 읽기 위해
메모리로부터 데이터를 읽어들이거나, 연산 결과를 메모리에 다시 덮어쓰기 위해 (데이터를 읽거나 쓰기 위해)
→ 항상 메모리에 접근하는 것은 비효율적
→ 이 일부를 CPU 일부인 cache에 저장하기도 한다

Instruction Access (Read)

CPU는 본질적으로 instruction을 읽어야 한다. 어떻게 ?
(순서가 중요)
Program Counter
32개 범용 레지스터 외 특별한 레지스터를 가지고 있다
→ 메모리 주소 어디를 읽어야 지금 앞으로 실행할 명령어를 잘 가져올 수 있을지
→ 다음에 실행할 instruction 주소를 트랙킹하는 역할
MIPS가 켜지게 도면 PC라는 32비트의 공간을, 기계적으로 0xBFC0_0000 로 세팅이 됨. 여기에 먼저 실행해야 할 명령어가 이 주소에 세팅되어 있다. 이걸 가져옴으로써 실행이 됨.
순차적이라면, PC는 CPU의 설계에 의해, 이 곳에서부터 4바이트만큼 첫 번째 명령어가 보관, 그만큼의 4바이트~ 다음 Instruction 보관
→ PC 값도 4바이트 크기만큼 !
PC는 알아서 바뀐다
명령어에 접근하는 법
(CPU가 초기화되었다고 가정)
CPU는 PC값을 참고해서 address bus를 통해 보낸다
sw 가 binary 형태로 CPU로 들어간다
메모리에 쓴다
PC는 자동적으로 4만큼 늘어난다 (다음 주소로)
→ PC는 매 Cycle마다 4씩 증가하도록 만들 것이다

Data Transfer Illustration

PC에 address 먼저 들어감
CPU에 명령어가 fetch 된다
Main Memory 의 해당 주소로 가서 $v1에 로딩

Word

가장 자연스럽게 접근할 수 있는 bit 단위
MIPS에서는 레지스터가 32비트이므로, word도 32비트로 취급
보통 레지스터의 크기를 따름
(레지스터가 더 커지더라도, 과거의 CPU와 호환하려고 과거 레지스터 크기를 따를 때도 있음)
Memory addressword의 배수여야 한다
(word단위로 읽거나 쓰려면 오른쪽 끝자리가 4의 배수인지 보면 된다)

Memory Address

Byte-address
모든 주소는 byte를 식별할 수 있도록

Loading and Storing Bytes

byte 단위로 읽거나 쓰는 일도 많이 함
MIPS의 경우, 바이트를 읽을 때는
lb lbu sb

lb

I-format
lb rt, address
메모리에서 한 바이트를 읽어서 레지스터에 복사한다
→ 이걸 byte (8-bit) 크기만큼 하겠다는 것
주소 → base address + offset
한 바이트만 읽으니까 base address + offset의 결과가 4의 배수가 될 필요는 없다
opcode는 32

lb 어디로 load하고 어떻게 하나?

sign - extension
한 바이트를 긁어와서 레지스터 어디다가 쓰나? (4 바이트 단위인데?)
레지스터가 가지고 있는 것 중 가장 오른쪽 (하단)에 쓰기로 약속 → LSB에 씀
남은 세 바이트는 sign extension 하기로 약속
: 1 바이트 값을 2의 보수 체계로 해석해서, 양수이면 남은 애들도 0으로 채우기, 음수이면 1로 채워야 같은 value로 해석할 수 있다
⇒ sign-extension을 하고싶지 않다면?
lbu : sign-extension을 하지 않을 수 있다 → 0 extension 하겠다
내가 가져온 8bit 값은, 2의 보수가 아니고 0~255까지의 unsigned value로 해석하는것.
→ 무조건 0으로 채운다
결과값이 4의 배수일 필요가 없다
destination은 레지스터 name으로 지정하면 된다
i type의 명령어

sb : store byte

레지스터에 있는 한 바이트값을 메모리로 이동하는 것
I format
sb rt, address
opcode: 40
주의해야 할 점
레지스터 어디에서 한 바이트를 옮길지를 정의해야 한다
→ 레지스터 내의 가장 오른쪽에 있는 바이트 (LSB)
0-extension, sign-extension 개념이 필요없다
→ 한 바이트 공간에 그대로 복사하는 것이므로 훼손시키지 않는다
lb lbu 서로 다른 명령어가 존재할 필요는 있지만, sbu 개념은 필요없다

Endianness

byte-addressable 메모리들은 bit-endian 또는 little-endian 방식으로 구성되어있다.
Big-endian | word의 MSB가 왼쪽에 온다
Little-endian | word의 LSB가 오른쪽에 온다
Example
Suppose that $s0 initially contains 0x23456789. After running the following program on a big-endian machine, what value does $s0 contain?
sw $s0, 0($0) # [0 + $0] <= $s0
lb $s0, 1($0) # $s0 <= [1 + $0]
👍