[Computer Architecture] Assignment 1 - Assembly

"RISC-V Assembly"

[Objective]

Weight Stationary 방식의 C += A x B Matrix 연산을 Assembly 언어로 구현하기

[Kite Description]

· Kite는 RISC-V 5-Stage Pipeline Modeling에 사용하는 Simulator

▣ Program Code
<program_code>
· PC = 4부터 시작 (beq의 PC = 4)
· 명령어 하나 = 4-Byte (remu의 PC = 8)
· 전체 6개의 명령어 → 전체 Code Size = 24-Byte (4 ~ 28)
· 대소문자 구분 X

▣ Register State
<reg_state>
· Program 시작 시 Register 초기 값

** x0은 항상 0으로 변경 불가

▣ Memory State
<mem_state>
· 주소는 반드시 8의 배수
· 64-bit 데이터

[Background]

<행렬 곱셈 연산>
· C[i][j] = ∑A[i][k] x B[k][j]

<Output-Stationary>
· 결과 값(C[i][j])을 Register에 유지 → 계산 끝날 때만 메모리에 저장
① A행 + B열 Read
② 계속 누적
③ 마지막에 저장

△ 메모리 Write 적음

<Weight-Stationary>
· Weight(B 값)를 Register에 유지 → 출력은 계속 Update
① B 값 고정
② A를 바꿔가며 계산
③ 결과(C)는 계속 메모리에 반영

△ Weight(B)를 계속 재사용
▼ 메모리 Write 많음

[Implementation]

· Weight-Stationary 방식으로 C += A x B 구현

▣ Register State
<reg_state>
· Register는 위와 같이 초기화
· x10 = 1024, x11 = 2016, x12 = 3008로 초기화

▣ Memory State

<mem_state - A>
· A 행렬은 1024 메모리 주소부터 1336 메모리 주소까지 행 방향 순서대로 나열


<mem_state - B>
· B 행렬은 2016 메모리 주소부터 2200 메모리 주소까지 행 방향 순서대로 나열    


<mem_state - C>
· C 행렬은 3008 메모리 주소부터 3480 메모리 주소까지 행 방향 순서대로 나열

▣ Code Flow
1
2
3
4
5
6
7
8
for (k = 0; k < K; k++) {
    for (j = 0; j < N; j++) {
        weight = B[k][j];
        for (i = 0; i < M; i++) {
            C[i][j] += A[i][k] * weight;
        }
    }
}
cs

① Weight Stationary 방식이므로 Weight인 B 행렬 먼저 고정
② Loop를 돌며 A 행렬과 C 행렬 값 Load
③ A 행렬과 B 행렬(Weight)를 곱한 후, C 행렬 값과 누산

▣ Code Description
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    addi    x3,     x0,     0        # Initialize k = 0
 
LOOP_k:
    bge     x3,     x15,    MMA_WS   # If k >= K, then finish (K = 4)
    addi    x4,     x0,     0        # Next k -> Return to j = 0
 
LOOP_j:
    bge     x4,     x14,    NEXT_k   # If j >= N, then k++ (N = 6)
 
    # Calculate B[k][j] Memory Address
    mul     x16,    x3,     x14      # x16 = k x N
    add     x16,    x16,    x4       # x16 = (k x N) + j
    slli    x16,    x16,    3        # x16 = {(k x N) + j} x 2^3(= 8)
    add     x16,    x11,    x16      # x16 = B Base Address + Offset
    
    ld      x23,    0(x16)           # Load B[k][j] Value from Calculated Address to x23 Register
 
    addi    x5,     x0,     0        # Next j -> Return to i = 0
 
LOOP_i:
    bge     x5,     x13,    NEXT_j   # If i >= M, then j++ (M = 10)
 
    # Calculate A[i][k] Memory Address
    mul     x17,    x5,     x15      # x17 = i x K
    add     x17,    x17,    x3       # x17 = (i x K) + k
    slli    x17,    x17,    3        # x17 = {(i x K) + k} x 2^3(= 8)
    add     x17,    x10,    x17      # x17 = A Base Address + Offset
 
    ld      x24,    0(x17)           # Load A[i][k] Value from Calculated Address to x24 Register
 
    # Calculate C[i]][j] Memory Address
    mul     x18,    x5,     x14      # x18 = i x N
    add     x18,    x18,    x4       # x18 = (i x N) + j
    slli    x18,    x18,    3        # x18 = {(i x N) + j} x 2^3(= 8)
    add     x18,    x12,    x18      # x18 = C Base Address + Offset
 
    ld      x25,    0(x18)           # Load C[i][j] Value from Calculated Address to x25 Register
 
    # MAC
    mul     x19,    x24,    x23      # x19 = A[i][k] x B[k][j]
    add     x25,    x25,    x19      # x25 = C[i][j] + (A[i][k] x B[k][j])
 
    # Update C[i][j]
    sd      x25,    0(x18)           # Update C[i][j] Value in C[i][j] Memory Address
 
    addi    x5,     x5,     1        # i++
    beq     x0,     x0,     LOOP_i   # Return to LOOP_i and retry Loop
 
NEXT_j:
    addi    x4,     x4,     1        # j++
    beq     x0,     x0,     LOOP_j   # Return to LOOP_j and retry Loop
 
NEXT_k:
    addi    x3,     x3,     1        # k++
    beq     x0,     x0,     LOOP_k   # Return to LOOP_k and retry Loop
 
MMA_WS:
    jalr    x0,     0(x0)            # Finish
cs

① B[k][j]의 메모리 주소를 계산한 후, 해당 메모리 위치에 있는 값을 Load
② B[k][j]의 값을 고정하고, i = 0 ~ M-1까지 반복하면서 A[i][k]와 C[i][j]의 메모리 주소를 계산하여 해당 메모리 위치의 값을 Load
③ C[i][j] += A[i][k] x B[k][j] 계산을 한 후, C[i][j]를 메모리에 다시 저장
④ i = 0 ~ M-1 반복 → j = 0 ~ N-1 반복 → k = 0 ~ K-1 반복

<예상 값>


<계산 결과>

· 계산 결과, 예측 값과 동일한 값을 얻은 것을 확인 가능

[Reference]

· assembly (Computer Architecture) - William J. Song

댓글

이 블로그의 인기 게시물

[Mini-NPU RTL] NN Reference Model

[Mini-NPU RTL] TPU (Study Paper)