[Mini-NPU RTL] NN Reference Model
"NN Reference Model"
1. Neuron
단 1개의 Neuron만 존재하며 해당 Neuron이 가중합을 잘 구하고 활성화 함수를 제대로 통과시키는지 확인
→ 28x28=784개의 입력 데이터와 784개의 가중치를 각각 곱한 후 ∑, 이후 활성화 함수를 통과시킨 뒤, 1개의 값 출력
→ "test_data.txt"에서 데이터를 읽어 Neuron에 직접 넣음
→ 가중치와 편향이 "w_1_0.mif", "b_1_0.mif"로부터 정적으로 고정되어 Load
neuron_tb.sv)
include.v) 공통
상수를 미리 정해두는 파일로, 숫자를 고치면 Network 깊이, Neuron 개수 등을 조절
ReLU.v) 공통
활성화 함수인 ReLU를 하드웨어로 구현한 코드
Sig_ROM.v) 공통
Sigmoid 함수 연산을 실시간으로 계산하지 않고 미리 계산된 결과값을 메모리에서 꺼내 쓰는 LookUp Table 방식의 구현
Weight_Memory.v) 공통
가중치를 저장하는 메모리 장치로 가중치를 읽고 쓴다.
Neuron.v) 공통
입력 데이터를 받아 가중치와 곱하고, 누적 한 후, 편향을 더하고, 활성화 함수를 거쳐 출력하는 연산 Unit
2. Network
수많은 Neuron이 합쳐진 전체 시스템의 데이터 흐름과 제어 Logic이 제대로 작동하는지 확인
→ 28x28=784개의 입력 데이터가 30개의 Neuron에게 동시에 전달되면 784x30개의 가중치를 곱해 30개의 결과 출력
(첫 번째 입력이 1 Cycle에 서로 다른 30개의 가중치와 병렬 연산으로 곱해진다. ∴ 784 Cycle)
→ 30개의 입력이 다음 Layer의 20개 Neuron에 전달되어 30x20개의 가중치를 곱해 20개의 결과 출력
→ 20개의 입력이 다음 Layer의 10개 Neuron에 전달되어 20x10개의 가중치를 곱해 10개의 결과 출력
→ 10개의 출력은 입력 이미지 0~9의 점수를 나타내며 점수가 큰 숫자가 최종 판단 결과에 해당
→ 표준 Bus Protocol인 "AXI-Lite" 사용하여 실제 CPU가 HW 가속기에 데이터를 쓰는 것처럼 Simulation 진행
→ 동적으로 가중치 Load
top_sim.v)
테스트를 통해 결과를 검증하고 정확도를 계산
· "`ifndef pretrained"로 확인 결과 메모리에 Weight와 Bias가 저장되어 있지 않은 경우
① "configWeights"와 "configBias" task 실행
② "configWeights"를 실행하여 for문을 통해 "w_Layer_Neuron.mif" 파일명 생성
③ 생성한 파일명과 일치하는 파일에 저장된 데이터를 메모리에 저장
④ "configBias"를 실행하여 for문을 통해 "b_Layer_Neuron.mif" 파일명 생성
⑤ 생성한 파일명과 일치하는 파일에 저장된 데이터를 메모리에 저장
⑥ 아래 Logic 수행
· "`ifndef pretrained"로 확인 결과 메모리에 Wight와 Bias가 저장되어 있는 경우
① for문을 돌려 "test_data_xxxx" 파일명 생성
② 생성한 파일명과 일치하는 파일에 저장된 데이터를 메모리에 저장
③ "sendData"를 통해 해당 데이터를 DUT(zyNet)에 스트리밍 입력으로 보내서 내부 연산을 수행
④ 정답을 파일의 마지막 값(785번째)으로 저장
⑤ 저장된 값이 읽은 값과 같은 경우 정확도 +1
⑥ 결과 출력
maxFinder.v)
0~9 중 가장 큰 값이 몇 번째에 위치하는지 찾는 기능을 수행하는 module
axi_lite_wrapper.v)CPU와 사용자가 만든 신경망 HW 사이를 연결해주는 Interface 역할
AXI4-Lite Protocol을 사용하여 CPU가 Register에 값을 R/W하며 신경망에 가중치를 설정하거나, 연산 결과를 가져오도록 설계
① 초기화 : CPU가 controlReg의 0번 bit를 제어하여 softReset으로 전체 Logic 초기화
② 설정 : CPU가 Layer 번호(0x0C)와 Neuron 번호(0x10) 설정
③ 가중치 주입 : CPU가 Weight(0x00) 또는 Bias(0x04)에 값을 쓰면, Wrapper가 유효 신호를 만들어 신경망에 값 전달
④ 연산 대기 : CPU가 statReg(0x18)을 계속 읽으며 1이 될 때까지 대기
⑤ 결과 : 연산이 끝나면 nnOut이 outputReg에 저장되고, CPU는 0x08 번지를 읽어 결과를 가져감
Layer_1.v)
Layer_1.v)
neuron module을 (n_0 ~ n_29) 이름으로 인스턴스화 & 각 Neuron은 서로 다른 "w_1_(0~29).mif", "b_1_(0~29).mif" 파일 사용
Layer_2.v)
neuron module을 (n_0 ~ n_19) 이름으로 인스턴스화 & 각 Neuron은 서로 다른 "w_2_(0~19).mif", "b_2_(0~19).mif" 파일 사용
Layer_3.v)
neuron module을 (n_0 ~ n_9) 이름으로 인스턴스화 & 각 Neuron은 서로 다른 "w_3_(0~9).mif", "b_3_(0~9).mif" 파일 사용
zynet.v)
위 module들을 하나로 통합하여 전체 신경망 가속기 시스템을 구성하는 최상위 module
CPU가 제어하고, AXI Stream으로 데이터를 받아, 3단계의 신경망 연산을 순차적으로 수행한 후, 분류 결과를 CPU에게 알려주는 가속기
3. Simulation Result
mnist.pkl.gz)
해당 파일은 훈련용 숫자 이미지 50,000개, 검증용 숫자 이미지 10,000개, 테스트용 숫자 이미지 10,000개로 구성
genTestData.py)
파일을 실행하여 test_data 10,000개를 Binary 표현으로 바꿔서 저장
상단의 "outputPath"와 "headerFilePath" 그리고 "def load_data():의 mnist_path"의 경로를 수정해야 함
result)
4. HW Architecture & Data Flow
각 Layer의 Neuron은 결과를 병렬로 출력하며 FSM이 이를 직렬화화여 다음 Layer는 한 Neuron씩 입력 받아 연산을 수행
5. 4x4 Systolic Array를 적용한 구조 Sketch
6. Thinking
1개의 입력 데이터(28x28) - 30 - 20 - 10 - 10(0~9) 중 최종 결과 판단
Weight Stationary 4x4 Systolic Array 방식
4개의 행에 784개의 입력 데이터를 나눠서 입력
→ 한 입력은 4개의 가중치와 곱해져서 4개 Neuron의 연산 결과가 됨
→ 행마다 784/4=196개의 입력 데이터를 계산한 후, 마지막에 Column을 기준으로 누적합 진행
→ 4개의 Neuron 결과가 출력 ∴ 30개의 Neuron 값을 얻으려면 8번 반복
→ 784/4 x 8 Cycle & 1 Cycle에 4개의 입력을 메모리에서 가져와야 함
Output Stationary 4x4 Systolic Array 방식
4개의 행에 784개의 입력 데이터를 똑같이 입력
→ 한 입력은 4개의 가중치와 곱해져서 4개의 Neuron의 연산 결과가 됨
→ 한 행에서 입력 데이터와 가중치가 곱해질 때 병렬로 연산 불가
→ 다른 행에서 Pipelining으로 진행하여 연산하지 않고 노는 것을 방지
→ 각 PE별로 누적합 진행
→ 4개의 Neuron 결과가 출력 ∴ 30개의 Neuron 값을 얻으려면 8번 반복
→ 784 x 8 Cycle (Pipeline Fill = 3 Cycle 무시 가능) & 1 Cycle에 1개의 입력을 메모리에서 가져와야 함
5x5 Systolic Array
→ 한 번 출력 될 때 5개의 Neuron에 대한 값이 출력
→ 30개의 Neuron을 만들기 위해 6번의 Pass가 필요
→ But, 보통 데이터는 8-bit로 32-bit Axi Data Bus가 1 Cycle에 4개의 데이터를 전송
→ 따라서, 5x5 SA보다 4x4 SA가 더 효율적
댓글
댓글 쓰기