시스톨릭 배열은 PE(Processing Element)라는 단순한 연산 장치를 격자로 배치하고, 이웃끼리 데이터를 주고받으며 계산하는 구조다. 이걸 아두이노로 만들려면 “PE 하나 = 보드 한 대”로 잡는 게 가장 직관적이다. 그렇게 2×2 격자에 네 대, 대조군에 한 대, 총 다섯 대를 썼다.
- PE 4대 (2×2 격자)
- 보드 사이 링크 (클럭 + 데이터)
- 타이밍 신호선 (D6)
- LCD로 결과 출력
보드마다 맡은 일
사용한 보드는 Arduino Nano 호환보드(SZH-EK025, ATmega328P)다. 역할은 이렇게 나눴다.
| 보드 | 역할 | 행렬 데이터 |
|---|---|---|
| PE(0,0) | 마스터. 데이터 주입과 타이밍 신호 발생. C[0][0] 누산 | A, B 전체 |
| PE(0,1) | A와 B 원소를 받아 C[0][1] 누산, B[k][1]을 아래로 전달 | 없음 (수신만) |
| PE(1,0) | B와 A 원소를 받아 C[1][0] 누산, A[1][k]를 오른쪽으로 전달 | 없음 (수신만) |
| PE(1,1) | 양방향 수신, C[1][1] 누산, 시간 측정과 LCD 출력 | 없음 (수신만) |
| 단일 보드 | 대조군. 순차 배열 접근으로 전체 행렬곱 수행 | A, B 전체 |
데이터를 가진 보드는 마스터 하나뿐이고, 나머지 PE는 이웃에게서 받은 값만으로 자기 담당 출력 원소의 부분합을 쌓는다. 시스톨릭 배열에서 “데이터가 격자를 흘러간다”는 말이 그대로 구현된 셈이다. 이런 방식이 출력 고정형(OS) 데이터 흐름이다.
보드 사이를 어떻게 이었나
D2·D4는 가로 링크, D3·D5는 세로 링크로 썼다. 각 연결은 클럭선(CLOCK)과 데이터선(DATA) 두 가닥을 한 쌍으로 쓴다.
| 연결 | 송신 | 수신 | 신호 |
|---|---|---|---|
| 1 | PE(0,0) D2, D4 | PE(0,1) D2, D4 | CLOCK A, DATA A |
| 2 | PE(0,0) D3, D5 | PE(1,0) D3, D5 | CLOCK B, DATA B |
| 3 | PE(0,1) D3, D5 | PE(1,1) D3, D5 | CLOCK B, DATA B |
| 4 | PE(1,0) D2, D4 | PE(1,1) D2, D4 | CLOCK A, DATA A |
| 5 | PE(0,0) D6 | PE(1,1) D6 | TIMING |
| 6 | 전 보드 GND | 공통 레일 | 기준점 공유 |
전원은 전원 공급형 USB 허브로 일괄 공급했다. 그리고 다섯 보드의 GND를 브레드보드의 공통 레일에 묶어 신호 기준점을 맞췄다. 보드가 서로 다른 전원 경로를 쓰는 환경에서 GND를 공유하지 않으면 HIGH/LOW 판정 자체가 어긋나기 때문이다.
시작 시각을 맞추는 법
여러 보드가 각자 micros()를 돌리면 시계가 서로 달라서 시간을 잴 수 없다. 그래서 PE(0,0)의 D6 핀을 PE(1,1)의 D6 핀에 직접 연결해 타이밍 신호선으로 썼다. PE(0,0)이 첫 MAC 연산 직전에 D6을 HIGH로 올리면, PE(1,1)이 그 순간을 시작 시각으로 기록한다. 보드 하나가 시간을 재고 결과를 LCD로 보여주는 구조다.
이 실험에서 PE 사이 통신은 I2C나 UART 같은 표준 프로토콜이 아니라, 클럭선과 데이터선을 직접 제어하는 GPIO 통신이다. 표준 프로토콜을 쓰지 않은 대신 통신 방식을 직접 통제할 수 있었고, 그 통신 비용이 이후 실험 결과를 좌우하게 된다.
행렬 크기는 이렇게 바꿨다
격자가 2×2로 고정돼 있으니 출력 행렬도 2×2로 고정하고, 내부 차원 K만 2, 4, 6, 8로 바꿨다(2×K 행렬과 K×2 행렬을 곱하는 방식). K를 바꿀 때는 PE(0,0)의 코드만 교체했다. 나머지 세 PE는 종료 신호가 올 때까지 반복하도록 짜서 K에 의존하지 않기 때문이다.
이렇게 만든 격자의 처리 시간이 어떻게 나왔는지는 이 글에서 이어진다.