
RISC-V의 모든 instruction은 32-bit, 1 WORD 길이로 일반화돼있다. 아래를 보면 모든 Instruction이 우리가 읽기에 친숙한 구조가 아닌데, 왜냐하면 RISC-V가 Little endian이기 때문이다.
opcode: Instruction operation coderd: 연산결과가 저장될 레지스터 번호 (Destination register number)funct3: 3-bit 짜리 function coders1: 연산에 사용될 operand register number 1rs2: 연산에 사용될 operand register number 2funct7: 7-bit 짜리 function code, funct3과 합친 10-bit가 operation의 세부적인 기능을 결정한다.
rd 영역을 상수를 표현하기 위한 추가 5-bit 영역으로 활용한다. rs2에 있는 데이터를 rs1에 저장한다.
Shift 연산은 operand가 2개일 필요가 없으므로 rs2가 immed로 대체됐다.
immed는 몇 bit shift 할 지 나타내는 상수이며 rs2가 5-bit 였던 것에 비해 immed는 6-bit다.
immed가 1bit 더 필요하게 됐다. 남은 칸은 6-bit 이므로 funct6 라는 이름의 칸이 있다.
왼쪽 shift는 slli라고 표현하고 i-bit 만큼 왼쪽으로 이동한다.
오른쪽 shift는 srli라고 표현하고 i-bit 만큼 오른쪽으로 이동한다.

beq (= Branch if equal, ==)과 bne (=Branch if not equal, !=)이 있다.blt (= Branch less than), bgt (= Branch greater than)도 있다.ble (= Branch less than or equal), bge (= Branch greater than or equal)도 있다.u를 붙힌 명령어를 사용하면 된다.if (i == j) f = g + h;
else f = g - h;
위와 같은 C언어 코드에서 f부터 차례대로 x19에 저장돼있다고 가정하면,
bne x22, x23, Else
add x19, x20, x21
beq x0, x0, Exit ;;; 무조건 진입한다.
Else: sub x19, x20, 21
Exit: ...
x22 (= i)와 x23 (= j)를 비교하고, 같지 않다면 Else 라는 이름의 분기문으로 진입한다.Else에서는 x19 (= f)에 x20 (=g)에서 x21 (=h)를 뺀 결과를 저장한 뒤 Exit로 진입한다.x19 (= f)에 x20 (=g)에서 x21 (=h)를 더한 결과를 저장한다.beq x0, x0는 무조건 참이다. 따라서 Else라벨을 지나서 바로 Exit로 넘어간다.while (A[i] == k) i++;
위와 같은 C언어 코드에서 i는 x22에, k는 x24에, A는 x25에 저장돼있다고 가정하면,
Loop: slli x10, x22, 3
add x10, x10, x25
ld x9, 0(x10)
bne x9, x24, Exit
addi x22, x22, 1
beq x0, x0, Loop
Exit: ...
i를 1 증가시켜주는 이유는 A의 다음 인덱스를 가리키기 위해서다.i를 2³만큼, 3-bit만큼 shift left 해줘야 한다.i를 8 증가시켜준 값을 x10이라는 temporary register에 저장한다.x25 (=A = A[0])에 x10을 더해주면 다음 인덱스의 주소가 되고 이를 다시 x10에 저장한다.x10으로 부터 0bit 떨어진 곳의 값을 x9에 저장한다.x10에 저장된 주소 안의 값 (=A[i])을 x9에 저장한다.x9 != x24라면, while문을 그만해야 하므로 Exit: 로 넘어가서 반복문을 종료한다.1을 i에 더한다.beq x0, x0은 무조건 진입하는 unconditional statement이므로 다시 Loop:로 돌아가서 위 과정을 반복한다.함수같은 프로시저를 호출할 때 발생하는 operation을 의미하고 과정은 다음과 같다.
x10 ~ x17 register에 input parameter들을 저장한다.x1에는 프로시저를 호출한 (= 돌아갈) 주소가 저장돼있으므로 그쪽으로 return 한다.위 과정을 컴파일하면 다음과 같다.
jal x1, label 로 x1에 PC + 1값을 저장해서 돌아갈 주소를 확보하고 label로 branch한다.jalr x0, 0(x1)으로 x1에 저장된 돌아갈 주소로 branch 한다.x0는 rd 역할을 하므로 경우에 따라 상수값을 넣어서 다른 곳으로 이동할 수도 있다.long long procedure (long long g, long long h,long long i, long long j) {
long long f = (g + h) - (i + j);
return f;
}
위 코드를 컴파일하면 아래와 같다.

;;; g부터 x10에 저장된다. f는 x20에 저장된다. x5, 6은 임시공간.
procedure: addi sp, sp, -24 ;;; Stack pointer를 3-Byte 확보.
sd x5, 16(sp) ;;; 임시공간 x5 공간확보
sd x6, 8(sp) ;;; 임시공간 x6 공간확보
sd x20, 0(sp) ;;; local variable f를 위한 공간확보
add x5, x10, x11 ;;; g + h
add x6, x12, x13 ;;; i + j
sub x20, x5, x6 ;;; f 구함
addi x10, x20, 0 ;;; x10에 return value 저장
ld x20, 0(sp) ;;; 저장해뒀던 x20값 백업
ld x6, 8(sp) ;;; 저장해뒀던 x6값 백업
ld x5, 16(sp) ;;; 저장해뒀던 x5값 백업
addi sp, sp, 24 ;;; Stack pointer 복구
jalr x0, 0(x1) ;;; 돌아갈 주소로 RETURN
x8, x9, x18~x27)는 사용 후 다시 원래 값으로 복원시켜줘야 함. 이때 위와 같이 stack을 사용할 수 있음.마이크로 아키텍처를 설계할 때 어떤 부분에 대해 초점을 맞췄고, 설계된 ISA가 어떤 목적에 따라 만들어졌는지 위주로 공부하자.