2026년 11월 5일 목요일

AI와 대화로 FIR 필터 설계: C vs CUDA vs RTL

AI와 대화로 FIR 필터 설계: C vs CUDA vs RTL

목차:
0. C++ 코드 들이대기
Q1. 이 코드를 CUDA 로 변경해보라
Q2. CUDA 로 실행 할 경우 처리 속도 증가 요소가 있나?
Q3. 샘플링 주파수 4800hz 를 실시간으로 상시 처리하려고 하는 경우다.
Q4. 낮은 샘플링 레이트의 소형 음성처리 응용에 내장형 시스템을 구성하려고 하는 경우 베릴로그 RTL 처럼 전용 하드웨어를 제작하면 어떤가?
Q5. 제공된 C++ 코드에서 fir8 함수의 y 출력을 16비트를 8비트로 최적화 하라.
Q6. 포화시키면 정밀도가 매우 낮아진다.
Q7. 위의 방법2 를 가지고 베릴로그 RTL 로 합성하라
Q8. 클럭 레이턴시를 30 이하로 하고 곱셈기 수를 줄여라.
Q9. 하드웨어 규모를 평가하라.
Q10. 클럭 레이턴시가 증가하더라도 하드웨어 면적을 줄일 수 있도록 최적화 하라.
Q11. 하드웨어를 줄이기 위해 가산과 시프트(Shift-and-Add) 연산만을 반복하여 곱셈을 수행하는 비트-시리얼(Bit-serial) 방식으로 구현하라.
Q12. 하드웨어를 줄일 수 있다면 RAM 대신 FF 를 사용해도 좋다.
---------------------------------------------------------------------

0. C++ 코드 들이대기

아래의 C 코드를 아무 첨언 없이 AI(이하 구글 제미니)에게 줘봤다.

#include <stdio.h>
#include "fir8.h"

void fir8( acc_t *y, data_t x)
{
    acc_t acc;
    data_t data;
    coef_t c;

    static data_t shift_reg[FILTER_TAP_NUM];

    acc=0;

    for (int i=FILTER_TAP_NUM-1;i>=0;i--)
    {
        if (i==0)
        {
            shift_reg[0]=x;
            data = x;
        }
        else
        {
            shift_reg[i] = shift_reg[i-1];
            data = shift_reg[i-1];
        }

        c = filter_taps[i];
        acc = acc + data*c;
    }
    *y=acc;
}
답변은 이랬다. 알아서 코드를 분석하더니 급기야 최적화 까지 해줬다.

제시해주신 코드는 C/C++ 기반의 고위합성(HLS, High-Level Synthesis) 및 임베디드 시스템에서 자주 사용되는 8탭(또는 FILTER_TAP_NUM 크기) FIR(Finite Impulse Response) 필터 소스 코드입니다.

이 코드의 핵심 동작과 특징, 그리고 하드웨어 합성(HLS) 관점에서의 최적화 포인트를 정리해 드립니다.
아마도 질문자의 이전 검색 이력을 알고 있었던지 HLS에 대하여 언급한다. 

🔍 코드 구조 및 동작 분석

1. 데이터 업데이트와 연산의 결합
• 하나의 for 루프 안에서 배열의 값을 한 칸씩 뒤로 밀어내는 시프트 레지스터(Shift Register) 업데이트와 곱셈-누산(MAC, Multiply-Accumulate) 연산이 동시에 수행됩니다.
• i == 0일 때 현재 입력값 x를 시프트 레지스터의 가장 첫 번째 칸(shift_reg[0])에 집어넣습니다.

2. 정적 변수 (Static Variable)
• static data_t shift_reg[...]로 선언되어 함수가 종료되어도 이전 입력 데이터(상태)들이 메모리에 그대로 유지됩니다. 필터가 이전 데이터들을 기억해야 하므로 필수적인 구조입니다.

3. 역방향 루프 (Backward Loop)
• 루프가 FILTER_TAP_NUM-1부터 0까지 거꾸로 돕니다. 이는 앞의 데이터를 뒤로 밀 때(shift_reg[i] = shift_reg[i-1]) 데이터가 덮어씌워져 유실되는 것을 방지하기 위함입니다.

급기야 시키지도 않은 문제점과 설명까지 곁들인다.

⚡ 하드웨어 합성(HLS)을 위한 개선 및 최적화 포인트
이 코드는 기능적으로 완벽하지만, Xilinx Vivado HLS 등에서 파이프라이닝(Pipelining) 및 성능 최적화를 하려면 몇 가지 아쉬운 점이 있습니다.

1. 데이터 의존성(Dependency) 문제
현재 루프 내부를 보면 shift_reg[i] = shift_reg[i-1]; 구조 때문에 이전 루프의 결과가 다음 루프에 영향을 주는 데이터 의존성이 발생합니다. 이로 인해 루프를 펼치거나(Unroll) 파이프라인할 때 성능(II, Initiation Interval) 제약이 생길 수 있습니다.

2. 조건문(if-else) 제거
루프 내부에 if (i == 0) 조건문이 있으면 하드웨어 구현 시 멀티플렉서(MUX)가 생성되거나 제어 로직이 복잡해집니다. 루프 밖에서 처리하는 것이 좋습니다.

이어 최적화 코드를 제시한다.

🛠️ 최적화된 추천 코드 구조
하드웨어에서 최고의 병렬성과 낮은 지연 시간(Latency)을 얻기 위해 루프를 완전히 펼칠 수 있도록(Loop Unrolling) 구조를 변경한 예시입니다.

#define FILTER_TAP_NUM 8 // 예시 크기

void fir8(acc_t *y, data_t x) {
#pragma HLS INLINE // 또는 적절한 HLS 프라그마 설정

    static data_t shift_reg[FILTER_TAP_NUM] = {0};
    acc_t acc = 0;

    // 1. 시프트 레지스터 업데이트 (역순으로 밀기)
    // 이 루프는 완전히 Unroll되어 병렬 레지스터 시프트로 변환됩니다.

    Update_Loop: for (int i=FILTER_TAP_NUM-1; i > 0; i--) {
        #pragma HLS UNROLL
        shift_reg[i] = shift_reg[i-1];
    }

    shift_reg[0] = x; // 최신 입력 저장

    // 2. 곱셈 및 누산 (MAC) 연산
    // 모든 곱셈이 동시에 일어나 하드웨어 성능을 극대화합니다.
    MAC_Loop: for (int i = 0; i < FILTER_TAP_NUM; i++) {
        #pragma HLS UNROLL
        acc += (acc_t)shift_reg[i] * filter_taps[i];

    }

    *y = acc;
}

소스 코드 내에 주석까지 넣었다. 그리고 설명을 내놨다.

개선된 점:

• 조건문 제거: 루프 내부의 if-else를 없애고 최신 데이터 입력(shift_reg[0] = x)을 루프 밖으로 뺐습니다.

• 루프 분리 및 Unroll: 시프트 루프와 연산 루프를 분리하여 #pragma HLS UNROLL 적용 시 하드웨어가 완벽한 병렬 처리를 할 수 있도록 유도합니다. (8탭 정도의 작은 필터는 완전히 펼치는 것이 효율적입니다.)

이 코드를 Xilinx Vivado HLS나 Intel HLS 같은 특정 툴에서 합성할 예정이신가요? 목표로 하시는 클럭 타이밍(II=1 등)이나 특정 프라그마(Pragma) 가이드가 필요하시면 말씀해 주세요!

Q1. 이 코드를 CUDA 로 변경해보라

C 로 작성된 코드를 CUDA(Compute Unified Device Architecture) 환경에서 실행 시킬 수 있다고 한다.  C 코드를 CUDA에 맞게 고치라고 해봤다.
제공해주신 C/C++ 기반의 FIR 필터 코드를 CUDA(GPU) 환경으로 변환하는 방법을 안내해 드립니다.

GPU는 수많은 코어가 동시에 병렬 연산을 수행하는 구조입니다. 기존 코드처럼 "하나의 데이터 입력을 받아 시프트 레지스터를 밀면서 필터링하는 방식(연속적인 스트리밍)"은 GPU의 병렬성을 제대로 활용하기 어렵습니다. 따라서 CUDA로 변경할 때는 대량의 입력 데이터 배열(X)을 한 번에 GPU 메모리로 보내고, 각 스레드가 출력 배열(Y)의 한 원소씩을 맡아 동시에 연산하는 방식으로 구조를 설계해야 합니다.

스트리밍(순차입력) 방식의 계산 구조는 병렬성의 잇점을 살리는데 불리 하다며 조언을 곁들인다. 그러면서 CUDA 환경에서 수행할 수 있는 코드를 만들어 줬다. 먼저 커널 코드다. CUDA는 GPU를 활용하여 대규모 데이터 처리를 위한 병렬처리 환경이다. 인공지능도 그 응용의 하나다. 파이썬은 물론 C/C++용 API 도 제공한다. C 코드를 제시 해서인지 알아서 C++ API를 활용한 코드다.

1. CUDA 커널 (Kernel) 코드

각 스레드가 자신에게 할당된 인덱스 idx의 출력 데이터 하나를 계산합니다. idx를 기준으로 과거 FILTER_TAP_NUM만큼의 데이터를 역순으로 접근하여 계수(c)와 곱해줍니다.

cuda

#include <cuda_runtime.h>
#include <device_launch_parameters.h>

#define FILTER_TAP_NUM 8

// 임의의 데이터 타입 정의 (사용자 환경에 맞게 변경 가능)
typedef float data_t;
typedef float coef_t;
typedef float acc_t;

// 상수 메모리(Constant Memory)를 사용하여 필터 계수를 저장 (성능 최적화)
__constant__ coef_t d_filter_taps[FILTER_TAP_NUM];

// CUDA FIR 필터 커널
__global__ void fir_cuda_kernel(acc_t *d_y, const data_t *d_x, int data_len) {
    // 현재 스레드가 처리할 출력 데이터의 인덱스
    int idx = blockIdx.x * blockDim.x + threadIdx.x;

    // 배열 범위를 벗어나지 않는지 확인
    if (idx < data_len) {
        acc_t acc = 0;
        // 각 스레드가 자신만의 MAC(Multiply-Accumulate) 루프를 수행
        for (int i = 0; i < FILTER_TAP_NUM; i++) {
            // 과거 데이터의 인덱스 계산
            int data_idx = idx - i;

            // 만약 인덱스가 0보다 작으면(초기 상태), 0을 곱하거나 경계 처리를 함
            data_t data = (data_idx >= 0) ? d_x[data_idx] : 0;

            acc += data * d_filter_taps[i];
        }

        // 결과 저장
        d_y[idx] = acc;
    }
}

전통적인 C 에서 볼 수 없는 지시어들과 API 이 보인다. 생소해서 그렇지 다분히 직관적이다. 아래 블로그 글을 읽어보면 이해에 도움이 될 것이다.

    [참고] CUDA 란?

GPU는 범용 CPU의 보조 연산장치다. GPU(또는 NPU)에게 일을 시키려면 주인 역할을 하는 CPU에 호스트 코드를 작성해 주어야 한다. 일종의 드라이버 소프트웨어다. 아래 글을 읽어보자.

    [참고] CPU의 귀환(The CPU Comeback Is Upon Us)
    Agentic AI has made CPUs the new performance bottleneck

호스트에서 실행될 코드는 일종의 드라이버 소프트웨어와 응용 프로그램이다.

2. 호스트(Host, CPU) 메인 함수 및 메모리 관리
GPU 메모리를 할당하고 데이터를 복사한 뒤, 커널을 호출하는 전체 흐름입니다.

cuda

#include <iostream>

int main() {
    int data_len = 1000000; // 100만 개의 대량 데이터 처리 예시

    // 1. 호스트(CPU) 메모리 할당 및 초기화
    data_t *h_x = new data_t[data_len];
    acc_t *h_y = new acc_t[data_len];

    coef_t h_filter_taps[FILTER_TAP_NUM] = {0.1f, 0.2f, 0.3f, 0.4f, 0.4f, 0.3f, 0.2f, 0.1f};

    for (int i = 0; i < data_len; i++) {
        h_x[i] = static_cast<data_t>(i % 10); // 샘플 데이터 입력
    }

    // 2. 디바이스(GPU) 메모리 할당
    data_t *d_x;
    acc_t *d_y;
    cudaMalloc((void**)&d_x, data_len * sizeof(data_t));
    cudaMalloc((void**)&d_y, data_len * sizeof(acc_t));

    // 3. 데이터를 호스트에서 디바이스로 복사
    cudaMemcpy(d_x, h_x, data_len * sizeof(data_t), cudaMemcpyHostToDevice);

    // 필터 계수는 GPU의 고속 '상수 메모리'로 복사
    cudaMemcpyToSymbol(d_filter_taps, h_filter_taps, FILTER_TAP_NUM * sizeof(coef_t));

    // 4. CUDA 그리드 및 블록 크기 설정
    int blockSize = 256; // 블록당 스레드 수
    int gridSize = (data_len + blockSize - 1) / blockSize;

    // 5. 커널 실행
    fir_cuda_kernel<<<gridSize, blockSize>>>(d_y, d_x, data_len);

    // 에러 체크 및 동기화
    cudaDeviceSynchronize();

    // 6. 결과를 다시 호스트 메모리로 복사
    cudaMemcpy(h_y, d_y, data_len * sizeof(acc_t), cudaMemcpyDeviceToHost);

    // 결과 확인 (앞의 일부 데이터만 출력)
    std::cout << "출력 데이터 예시 (Y):" << std::endl;
    for (int i = 0; i < 10; i++) {
        std::cout << "Y[" << i << "] = " << h_y[i] << std::endl;
    }

    // 7. 메모리 해제
    cudaFree(d_x);
    cudaFree(d_y);
    delete[] h_x;
    delete[] h_y;

    return 0;
}

대량의 데이터를 취급 할 수 있다는 것을 자랑이라도 하려는 듯이 무려 100만개의 샘플을 할당한다. 그것도 호스트의 메모리에! 계산은 보조 프로세서에서 수행 될 테니까 호스트에 있던 대량의 데이터를 GPU의 메모리에 전송한다. GPU에게 계산을 시작하라고 지시하고 끝나길 기다렸다가 결과를 가져온다. 주변장치가 제아무리 고속의 PICe 버스에 붙어 있다지만 많은 양의 데이터를 주고받는데 시간이 걸린다. 어쩌면 계산 시간보다 데이터 주고받는 시간이 더 걸릴지도....

💡 CUDA 변환 시 핵심 최적화 포인트

1. 시프트 레지스터 제거 (static shift_reg 제거)
• GPU 병렬 처리에서는 메모리 값을 물리적으로 한 칸씩 미는 연산이 비효율적입니다. 대신 전역 입력 배열(d_x)을 인덱스(idx - i)로 직접 접근하여 시프트 연산 자체를 완전히 없앴습니다.

2. 상수 메모리 (__constant__) 활용
• FIR 필터의 계수(filter_taps)는 모든 스레드가 동시에 읽어서 사용하는 값입니다. 이를 __constant__ 메모리에 배치하면 GPU 내부의 전용 캐시를 타게 되어 메모리 대역폭을 크게 절약할 수 있습니다.

3. 경계 처리
• 필터 초기 가동 시 idx - i < 0이 되는 구간(0번째 데이터 이전)은 0으로 처리하도록 삼항 연산자를 추가했습니다.

이 구조는 일반적인 글로벌 메모리 접근 방식을 사용했습니다. 만약 탭 수가 8개가 아니라 64개, 128개 이상으로 커지거나 성능을 극한으로 끌어올려야 한다면, GPU 내부의 공유 메모리(Shared Memory)를 사용하여 이웃 스레드 간의 데이터 읽기를 최적화해야 합니다. 탭 수가 더 큰 필터를 다루시거나 공유 메모리 버전의 최적화가 필요하신가요?
단지 탭 수가 8개짜리(곱셈이 8회 이뤄짐)보다 대규모 정밀 필터에 유리하다는 말을 남긴다.

Q2. CUDA 로 실행 할 경우 처리 속도 증가 요소가 있나?

호스트의 C에서 보조장치 CUDA로 옮겨갔을 때 얻는 기대효과를 물어봤다. 역시 속도 향상이다.

기존 C/C++(CPU) 코드를 CUDA(GPU)로 변경했을 때, 수백만 개 이상의 대량 데이터를 처리하는 환경이라면 극적인 속도 향상(수십 배~수백 배)을 기대할 수 있습니다.

단순히 "코어가 많아서" 빠른 것을 넘어, GPU 구조상 FIR 필터 연산 속도를 가속하는 구체적인 기술적 요소 4가지는 다음과 같습니다.

1. 시프트 연산(메모리 이동)의 완전한 제거
• CPU (기존 코드): 새로운 데이터가 들어올 때마다 배열의 모든 값을 한 칸씩 뒤로 밀어야 합니다(shift_reg[i] = shift_reg[i-1]). 8탭 필터라면 데이터 1개당 7번의 메모리 쓰기(Copy)가 발생합니다.
• GPU (CUDA): 데이터를 물리적으로 밀지 않습니다. 전역 메모리에 입력 데이터가 이미 배열로 펼쳐져 있으므로, 각 스레드는 인덱스 연산(idx - i)만으로 과거 데이터에 직접 접근합니다. 불필요한 메모리 이동 오버헤드가 0(Zero)이 됩니다.

2. 하드웨어 수준의 곱셈-누산(MAC) 병렬화
• CPU: 대용량 데이터를 처리할 때 루프를 돌며 Y[0] 계산 완료 ➡️ Y[1] 계산 완료 순으로 순차 처리합니다.
• GPU: 수천 개의 코어가 동시에 가동됩니다. Y[0], Y[1], ..., Y[4095]를 동일한 클럭에 동시에 계산합니다. FIR 필터의 핵심인 곱셈과 덧셈 연산(MAC)이 수천 개 채널에서 병렬로 터지기 때문에 연산 처리량(Throughput)이 압도적입니다.

3. 고속 캐시 메모리 활용 (__constant__ 메모리)
• FIR 필터의 계수(filter_taps)는 모든 스레드가 '동시에, 똑같은 값'을 읽어야 합니다.
• CUDA의 상수 메모리(Constant Memory)는 이 조건에 특화되어 있습니다. 하나의 스레드가 계수 값을 읽으면 주변의 모든 스레드(워프 단위)에 브로드캐스트(Broadcast) 방식으로 값이 동시에 전달되며, 전용 캐시에 저장되므로 전역 메모리 접근 지연 시간(Latency)을 획기적으로 줄여줍니다.

4. 연속된 메모리 접근 최적화 (Memory Coalescing)
• GPU는 인웃 스레드들이 메모리의 연속된 공간을 동시에 읽을 때 가장 빠릅니다.
• 앞서 작성된 CUDA 코드를 보면 threadIdx.x가 0, 1, 2, 3으로 증가할 때, 각 스레드가 접근하는 첫 데이터(d_x[idx]) 역시 0, 1, 2, 3으로 연속되어 있습니다. 이를 병합된 메모리 접근(Coalesced Access)이라고 하며, 하드웨어 성능을 100% 끌어내는 핵심 요소입니다.

무조건 속도가 향상되는 것은 아니고 주의사항도 있다. 예상대로 주변장치와 호스트 사이의 통신(데이터 전송)이 문제다.

⚠️ 주의: 무조건 빨라지지 않는 예외 상황 (오버헤드)

모든 상황에서 CUDA가 빠른 것은 아닙니다. 아래 요소 때문에 오히려 CPU보다 느려질 수도 있습니다.

1. 데이터 양이 너무 적을 때: 처리할 데이터가 수천 개 수준으로 적다면, GPU 코어를 다 채우지 못해 병렬 이점이 사라집니다.

2. 호스트-디바이스 간 데이터 전송: CPU 메모리(RAM)에서 GPU 메모리(VRAM)로 데이터를 복사하는 데 비용이 듭니다. [전송 시간 + GPU 연산 시간]이 [CPU 순수 연산 시간]보다 짧아지는 손익분기점(보통 데이터 수십만 개 이상)을 넘어야 처리 속도가 증가합니다.

현재 처리하고자 하시는 전체 입력 데이터의 크기(배열의 길이)가 어느 정도 수준인가요? 또한 실시간 스트리밍(실시간 오디오/센서) 데이터인지, 아니면 대용량 파일(녹음 파일/이미지 등)을 한 번에 처리하는 방식인지 알려주시면 정확한 성능 타당성을 짚어드릴 수 있습니다.

Q3. 샘플링 주파수 4800hz 를 실시간으로 상시 처리하려고 하는 경우다.


AI가 겨우 8탭 짜리 필터를 GPU에서 처리하기는 불리할 수 있다는 의견을 내놓고 있다. C 로 제시한 코드의 응용처를 줘봤다.

결론부터 말씀드리면, 샘플링 주파수 4,800Hz(초당 4,800개 샘플)의 단일 채널 실시간 상시 처리 환경이라면 CUDA(GPU)를 사용하는 것은 권장하지 않으며, 기존 CPU(C/C++) 코드를 유지하는 것이 훨씬 효율적이고 빠릅니다.

GPU가 강점을 가지는 대규모 병렬 연산의 조건과 현재 주신 환경의 특성을 비교해 보면 그 이유가 명확해집니다.

AI는 군소리 없이 단도직입이다. 별로 효과 없단다. 그 이유를 설명해 준다.

❌ CUDA 도입 시 역효과가 나는 이유

1. 데이터 양이 너무 적어 GPU 코어가 놉니다.
• 4,800Hz는 오디오/신호 처리 기준으로 매우 낮은 주파수입니다. 1초에 4,800개의 데이터가 들어오며, 실시간 처리를 위해 10ms~20ms 단위로 버퍼링을 한다고 해도 한 번에 처리할 데이터(오디오 프레임 크기)는 겨우 48개~96개 수준입니다.
• 최신 GPU는 한 번에 수만~수십만 개의 연산을 동시에 처리하도록 설계되어 있습니다. 겨우 48개의 연산을 하기 위해 GPU를 깨우면, 수만 개의 코어 중 48개만 일하고 나머지는 놀게 되어 GPU의 병렬 처리 이점이 완전히 사라집니다.

2. PCIe 통신 오버헤드가 연산 시간보다 훨씬 큽니다.
• GPU에서 연산하려면 CPU 메모리 ➡️ PCIe 버스 ➡️ GPU 메모리(VRAM) ➡️ GPU 연산 ➡️ PCIe 버스 ➡️ CPU 메모리 과정을 거쳐야 합니다.
• 4,800Hz의 작은 데이터를 주고받는 데 걸리는 통신 지연 시간(Latency)이 GPU가 8탭 FIR 필터를 계산하는 시간보다 수십 배 길어지기 때문에, 오히려 전체 처리 속도가 떨어지고 실시간성이 깨질 수 있습니다.

3. 커널 호출 오버헤드 (Kernel Launch Overhead)
• CPU가 GPU에게 "이 연산을 실행해라" 하고 명령(커널 호출)을 내리는 것 자체만으로도 수 마이크로초(µs)의 드라이버 오버헤드가 발생합니다. 데이터가 수시로 들어오는 실시간 스트리밍 환경에서 매번 커널을 호출하는 것은 매우 비효율적입니다.
그러면서 조언을 아끼지 않는다.

💡 그럼 언제 CUDA를 써야 할까요? (성능 증가 조건)

만약 현재 환경이 아래 조건 중 하나에 해당한다면 CUDA 전환으로 엄청난 속도 향상을 볼 수 있습니다.

1. 다채널(Multi-channel) 동시 처리: 단일 채널이 아니라, 4,800Hz로 작동하는 센서나 마이크 수천~수만 개의 데이터를 동시에 상시 필터링해야 하는 경우 (예: 거대 마이크로폰 어레이, 대규모 센서 네트워크).

2. 탭 수가 극단적으로 큰 경우: 필터 탭 수(FILTER_TAP_NUM)가 8개가 아니라 수천~수만 개에 달해 데이터 1개당 연산량이 폭발적으로 많을 경우.

3. 블록(Batch) 단위 후처리: 실시간이 아니라, 이미 녹음된 며칠~몇 달 치 대용량 데이터를 한 번에 초고속으로 필터링해야 하는 경우.

🛠️ 현재 환경(4,800Hz 실시간 단일 채널) 최적의 솔루션

현재 요구사항에는 CPU 기반 코드를 고도화하는 것이 가장 지연 시간이 적고 안전합니다. 첫 번째 질문에서 다루었던 C/C++ 최적화 기법을 적용하되, 실시간 루프 구조를 다음과 같이 가져가는 것을 추천합니다.

• SIMD(Vectorization) 활용: CPU의 AVX2, AVX-512 또는 ARM NEON 명령어를 사용하여 8탭 연산을 1클럭 만에 끝내도록 컴파일러 최적화(-O3)를 켭니다.
• 고정크기 링 버퍼(Ring Buffer): 데이터가 들어올 때마다 배열을 미는 대신, 인덱스 포인터만 회전시키는 링 버퍼 구조를 사용하면 CPU 오버헤드가 거의 0이 됩니다.

혹시 4,800Hz를 처리하는 채널(또는 센서)의 개수가 수백~수천 개 이상으로 확장되는 상황인가요? 아니라면 현재 사용 중인 CPU 아키텍처(Intel/AMD x86 계열인지, 라즈베리파이 같은 ARM 계열인지)를 알려주시면 그에 맞는 최적의 CPU 가속 프라그마나 기법을 제안해 드리겠습니다.

Q4. 낮은 샘플링 레이트의 소형 음성처리 응용에 내장형 시스템을 구성하려고 하는 경우 베릴로그 RTL 처럼 전용 하드웨어를 제작하면 어떤가?

좋다. 그렇다면 먼저 제시했던 C 코드의 응용처를 구체적으로 제시해 봤다.

4,800Hz 수준의 낮은 샘플링 레이트를 가지는 소형 임베디드 음성 처리 시스템에서 Verilog RTL을 이용한 전용 하드웨어(FPGA 또는 ASIC)를 제작하는 것은 실시간성, 저전력, 신뢰성 측면에서 최고의 선택이 될 수 있습니다.

GPU(CUDA)가 대규모 병렬 처리에 특화되어 있다면, FPGA/ASIC 같은 전용 하드웨어는 최소한의 자원으로 지연 시간(Latency)을 극단적으로 줄이는 데 특화되어 있기 때문입니다.

RTL 기반 전용 하드웨어로 구현할 때의 확실한 장점과 설계 고려사항을 정리해 드립니다.

🌟 RTL 전용 하드웨어 제작 시 얻는 핵심 이점

1. 극단적으로 낮은 지연 시간 (Deterministic Zero-Latency)
• CPU/GPU: 운영체제(OS)의 스케줄링, 인터럽트 처리, 캐시 미스, 메모리 버스 경쟁 등으로 인해 데이터가 들어온 후 출력될 때까지의 시간이 미세하게 출렁입니다(Jitter 발생).
• RTL 하드웨어: 데이터 샘플이 들어오는 즉시 정확히 지정된 클럭 사이클(예: 8~10클럭) 후에 결과가 출력됩니다. 4,800Hz 기준으로는 사실상 '지연 시간이 0'인 실시간 제어가 가능합니다.

2. 압도적인 초저전력 (Ultra-Low Power)
• 범용 CPU나 GPU는 필터 연산 외에도 프로세서 유지를 위해 수많은 전력을 낭비합니다.
• Verilog로 8탭 FIR 필터를 고정소수점(Fixed-point)으로 구현하면, 단 몇 개의 곱셈기(DSP 블록)와 레지스터만 사용하므로 전력 소비량이 몇 밀리와트(mW) 혹은 마이크로와트(µW) 수준으로 떨어집니다. 배터리로 구동되는 소형 내장형(Embedded) 기기에 완벽히 부합합니다.

3. 칩 크기 및 단가 최소화
• 8탭 FIR 필터는 하드웨어 자원을 거의 차지하지 않는 매우 가벼운 로직입니다.
• 시장에서 가장 저렴하고 크기가 작은 소형 FPGA(예: Lattice iCE40 시리즈나 Intel Cyclone 소형 라인업)에도 아주 넉넉하게 들어갑니다. 이는 양산 시 하드웨어 비용을 크게 낮춰줍니다.

🛠️ 하드웨어 설계 시 핵심 고려사항 (RTL 구현 팁)

C 코드를 Verilog RTL로 옮길 때 반드시 변경해야 하는 아키텍처적 포인트입니다.

1. 부동소수점(Float) ➡️ 고정소수점(Fixed-point) 변환
• C 코드의 float이나 double 타입을 하드웨어에서 그대로 부동소수점 가산기/곱셈기로 만들면 자원(Logic Element)을 너무 많이 먹습니다.
• 음성 신호의 요구 정밀도에 따라 입력 데이터와 계수를 16비트 또는 24비트 정수(고정소수점)로 변환하여 설계해야 합니다. 8탭 정도라면 고정소수점 변환으로 인한 음질 저하(양자화 잡음)가 거의 없습니다.

2. 시리얼(Serial) vs 패러렐(Parallel) 구조 선택
• 패러렐 구조: 8개의 곱셈기를 동시에 배치하여 1클럭 만에 결과를 냅니다. 자원은 조금 더 들지만 제어가 직관적입니다.
• 시리얼 구조: 시스템 클럭(예: 12MHz)이 샘플링 레이트(4.8kHz)보다 훨씬 빠르므로, 곱셈기 단 1개만 두고 8클럭 동안 돌려가며 누산(MAC)하는 구조입니다. 자원을 극단적으로 아낄 수 있어 소형 시스템에 가장 추천합니다.

제시한 8탭 정수형 데이터를 사용하는 알고리즘이라면 전용 칩이 최적의 선택이라고 한다. 하지만 내장형 시스템에 훌륭한 CPU들이 적용되고 있으니 그것도 고려해 볼 만 하단다.

📋 종합 제언: 어떤 칩을 선택해야 할까?

개발 단계와 양산 계획에 따라 다음과 같은 접근을 추천합니다.

1. 프로토타입 및 소량 생산: 소형 FPGA(Lattice, AMD Spartan 등)를 사용하여 하드웨어를 구현합니다. 개발이 쉽고 칩 내부의 DSP 블록을 활용해 간단히 구현할 수 있습니다.

2. 초소형/초저전력 완제품 양산: FPGA 비용마저 아깝다면, 아예 디지털 필터(DFE) 하드웨어 블록이 내장된 소형 MCU(예: Cortex-M4/M7 계열의 DSP 확장 명령어 활용)를 쓰거나, 연산량이 많아질 경우 전용 ASIC 칩 개발을 검토할 수 있습니다.

단, 8탭 필터 하나만을 위해 하드웨어를 새로 만드는 것보다는, 전체 오디오 코덱 제어, 잡음 제거(Noise Cancellation), 통신 로직 등을 한 칩에 합친 전용 하드웨어(SoC 가속기 형태)로 설계할 때 RTL 제작의 가치가 극대화됩니다.

혹시 이번 응용에서 8탭 FIR 필터 외에 ADC/DAC 제어, I2S 통신, 또는 추가적인 음성 알고리즘(예: FFT, 에코 캔슬러)도 함께 하드웨어(RTL)로 구현할 계획이 있으신가요? 전체적인 시스템 구성 가이드나 간단한 Verilog FIR 필터 예시 코드가 필요하시면 언제든 말씀해 주세요!

Q5. 제공된 C++ 코드에서 fir8 함수의 y 출력을 16비트를 8비트로 최적화 하라.

C++ 코드를 베릴로그 RTL 전용 칩으로 구현하기로 결정 했다. 기왕이면 전용 칩 ASIC을 무료로 제작해 준다는 '내 칩 제작 서비스'의 MPW를 이용해 보기로 한다. 이 서비스에서 무료로 제공하는 칩은 28핀(전원 포함)으로 한정되었다. 앞서 준비된 C++ 코드를 칩으로 만들 경우 필요한 입출력 핀을 구해보자. 함수의 외형은 다음과 같다.

    void fir8( acc_t *y, data_t x)

데이터 타입은 다음과 같다.

    typedef uint8_t   coef_t;
    typedef uint8_t   data_t;
    typedef uint16_t  acc_t;

입력 x는 8비트, 출력 y는 16비트다. 디지털 회로를 고려 하면 추가로 리셋과 클럭이  있어야 한다. 그외 외부와 인터페이스 하려면 핸드쉐이크 신호도 필요하다. 결국 필요한 핀 수가 '내 칩' 의 조건의 넘는다. 출력 y가 16비트가 된 연유를 따져보니 8비트 계수 곱하기 8비트 입력이기 때문이다. 필터 계수 테이블을 보면 C 에서 제공하는 최소 자료형이 unsigned char 이기 때문이지 굳이 8비트를 쓸 필요는 없었다.

 static uint8_t filter_taps[FILTER_TAP_NUM] = {
  4, 12, 25, 34, 34, 25, 12, 4 };

최대 값이 34인데 이를 2진수로 나타내자니 6비트면 충분하다. 필터의 응용이 정밀할 필요 없다고 가정하면 출력을 입력과 8비트로 낮추기로 한다. AI에게 코드를 제시했더니 분석까지 해줬다.

제공된 fir8.c 코드의 for 루프 내부 구조는 시프트 레지스터 갱신(Shift Register Update)과 컨볼루션 연산(MAC: Multiply-Accumulate)이 거꾸로 뒤섞여 있어, 루프를 돌 때마다 이전 차수의 데이터가 아닌 잘못된 데이터가 곱해지는 치명적인 버그를 가지고 있습니다.

치명적인 오류가 있다고 지적하지만 그렇지 않다. AI의 과도한 지적이다. 이어서 출력을 8비트로 낮춰 준다며 제시한 코드를 보자.

따라서 단순히 출력 타입을 8비트로 바꾸는 것에 그치지 않고, 올바른 FIR 필터 동작을 수행하도록 코드를 완전히 수정하고, 최종 출력을 8비트로 최적화(포화 연산 및 스케일 다운)한 코드를 제시합니다.

최적화 및 버그 수정본 (fir8.c)

최종 출력을 uint8_t(8비트)로 변경하고, 내부 연산 후 오버플로우를 방지하기 위해 계수 합(4+12+25+34+34+25+12+4 = 150)을 고려한 스케일 다운 또는 포화 연산(Saturation)을 적용할 수 있도록 헤더 구조에 맞추어 최적화했습니다.

#include <stdio.h>
#include "fir8.h"

// 헤더의 void fir8(acc_t *y, data_t x)에서 *y 타입을 8비트(data_t)로 변경하고자 할 때의 구현입니다.
// 만약 헤더를 고칠 수 없다면 주소 참조 시 형변환을 해야 하므로, 함수 시그니처 자체를 data_t* 로 변경하는 것이 가장 깔끔합니다.

void fir8(data_t *y, data_t x)
{
    acc_t           acc = 0;
    static data_t   shift_reg[FILTER_TAP_NUM] = {0};

    // 1. 시프트 레지스터 업데이트 (뒤에서부터 밀기)
    for (int i = FILTER_TAP_NUM - 1; i > 0; i--)
    {
        shift_reg[i] = shift_reg[i - 1];
    }
    shift_reg[0] = x; // 새로운 입력 저장

    // 2. 필터 연산 (MAC) 수행
    for (int i = 0; i < FILTER_TAP_NUM; i++)
    {
        acc += (acc_t)shift_reg[i] * filter_taps[i];
    }

    // 3. 16비트 누적 값(acc)을 8비트(y)로 최적화 및 스케일링
    // 모든 계수의 합은 150이므로, x의 최댓값이 255일 때 acc의 최댓값은 255 * 150 = 38,250 (16비트 범위 안)
    
    // 방법 A: 단순 상위 8비트 취하기 (필터 이득(Gain)을 낮추어 매핑할 때, 시스템 설계에 따라 Shift 연산 크기 조절)
    // *y = (data_t)(acc >> 7); // 계수 합이 대략 128 부근이므로 7~8비트 우측 시프트가 적절합니다.

    // 방법 B: 포화 연산(Saturation) 적용 (255를 넘으면 255로 제한)
    if (acc > 255) 
    {
        *y = 255;
    }
    else 
    {
        *y = (data_t)acc;
    }
}
계수 값의 범위를 고려하여 포화 연산을 추가 하였다. AI의 설명을 들어보자.

주요 변경 및 최적화 포인트

• 함수 인터페이스 최적화: 출력 포인터 y의 타입을 acc_t*(16비트)에서 data_t*(8비트)로 변경하여 불필요한 16비트 메모리 쓰기를 방지합니다.
• 시프트 레지스터 버그 수정: 기존 코드는 i=FILTER_TAP_NUM-1부터 내려오면서 shift_reg[i] = shift_reg[i-1]을 한 뒤 곧바로 data = shift_reg[i-1]를 곱하여, 한 번 들어온 입력 x가 단 하나의 루프 안에서 모든 레지스터로 동시에 복사되는 치명적인 오류가 있었습니다. 이를 업데이트 루프와 연산 루프로 명확히 분리하여 해결했습니다.
• 8비트 다운스케일링 및 포화(Saturation) 처리: 8비트 입력과 8비트 계수를 곱해 나간 누적 값(acc)은 최대 38,250까지 커질 수 있으므로 내부 연산은 acc_t(16비트)로 안전하게 수행합니다. 이후 최종 출력 시점에 시스템이 요구하는 고정소수점 스케일에 맞춰 비트 시프트(>>)를 하거나, 최댓값 255를 넘지 않도록 포화(Saturation) 연산을 거쳐 최종 8비트 데이터(data_t)로 안전하게 형변환하여 저장합니다.
필터의 고정소수점 스케일을 유지하기 위해 정확히 몇 비트 우측 시프트(Shift) 처리를 해야 하는지, 혹은 헤더 파일(fir8.h)의 fir8 함수 원형을 직접 수정해도 괜찮은지 알려주시면 완벽히 들어맞는 코드로 세부 조정해 드리겠습니다. 

누산한 값의 하위 7비트를 아무고려 없이 잘라내고있는데 매우 부적할한 방법이다.

Q6. 포화시키면 정밀도가 매우 낮아진다.

포화 연산의 문제점을 지적 했더니 순순히 시인하고 좀더 정교하게 다듬어 주었다.

포화 연산(Saturation)을 적용했을 때 정밀도가 깨지는 이유는 계수의 총합(150)으로 인해 출력 신호의 스케일(Gain)이 입력보다 약 150배 커져서 대부분의 값이 상한선인 255에 걸려 잘리기 때문입니다. 고정소수점 연산에서 정밀도를 유지하면서 8비트로 출력하려면, 누적 값(acc)을 포화시키는 것이 아니라 전체 계수 합만큼 나누거나 비트 시프트(Bit Shift)를 통해 원래 스케일로 다운스케일링(Normalized Scaling)해야 합니다. 가장 정밀도가 높고 대중적인 두 가지 스케일링 방식을 적용한 코드를 제안합니다. 프로젝트 상황에 맞는 방식을 선택해 보시기 바랍니다.

방법 1. 수학적 정확도 우선 (정밀도 극대화)

계수의 총합인 150으로 직접 나누어 입력 신호와 동일한 부근의 0~255 스케일로 정확하게 맞추는 방식입니다. 나눗셈 연산이 들어가지만 정밀도가 가장 높습니다.

void fir8( data_t *y, data_t x)
{
    acc_t           acc = 0;
    static data_t   shift_reg[FILTER_TAP_NUM] = {0};

    // 1. 시프트 레지스터 업데이트
    for (int i = FILTER_TAP_NUM - 1; i > 0; i--)
    {
        shift_reg[i] = shift_reg[i - 1];
    }
    shift_reg[0] = x;

    // 2. 컨볼루션 연산
    for (int i = 0; i < FILTER_TAP_NUM; i++)
    {
        acc += (acc_t)shift_reg[i] * filter_taps[i];
    }

    // 3. 정밀도 최적화 다운스케일링 (Gain 보정)
    // 계수 합(150)으로 나누어 입력과 출력의 스케일을 1:1로 맞춥니다.
    // 반올림 효과를 내기 위해 분자에 (150 / 2 = 75)를 더해 정밀도를 향상시킵니다.
    *y = (data_t)((acc + 75) / 150);
}

방법 2. 고속 비트 시프트 연산 (임베디드 최적화)

MCU/DSP 등에서 나눗셈 오버헤드를 피하기 위해 사용하는 방식입니다. 계수의 합(150)과 가장 가까운 \(2^7 = 128\) 또는 \(2^8 = 256\)으로 나누는 비트 시프트를 적용합니다.
이 필터의 원본 설계 사이트(t-filter) 특성상 8비트 고정소수점(Q7 또는 Q8 Format)으로 계수가 설계되었을 확률이 매우 높습니다. 일반적으로 계수를 만들 때 전체 합이 128 또는 256에 가깝게 정규화되므로 아래와 같이 시프트합니다.

void fir8( data_t *y, data_t x)
{
    acc_t           acc = 0;
    static data_t   shift_reg[FILTER_TAP_NUM] = {0};

    // 1. 시프트 레지스터 업데이트
    for (int i = FILTER_TAP_NUM - 1; i > 0; i--)
    {
        shift_reg[i] = shift_reg[i - 1];
    }
    shift_reg[0] = x;

    // 2. 컨볼루션 연산
    for (int i = 0; i < FILTER_TAP_NUM; i++)
    {
        acc += (acc_t)shift_reg[i] * filter_taps[i];
    }

    // 3. 고속 비트 시프트 및 반올림 연산
    // [선택 A] 계수가 Q7 format(합이 128 기준)으로 설계된 경우 (추천: 이 필터 계수 합인 150과 가장 가깝습니다)
    *y = (data_t)((acc + 64) >> 7);

    // [선택 B] 계수가 Q8 format(합이 256 기준)으로 설계된 경우 (전체적으로 신호가 약 0.5배 감쇄됨)
    // *y = (data_t)((acc + 128) >> 8);
}
수정 효과
• 값 잘림(Clipping) 방지: 출력이 255로 뭉개지던 현상이 사라지고, 원래 신호의 파형(주파수 성분)이 정밀하게 복원됩니다.
• 반올림(Rounding) 추가: 단순히 소수점을 버리는 대신 + 75 또는 + 64를 더해 반올림 처리를 함으로써 고정소수점 연산에서 발생하는 양자화 오차(Quantization Error)를 최소화했습니다.

Q7. 위의 방법2 를 가지고 베릴로그 RTL 로 합성하라

첫번째 방법이 정밀하기는 하지만 10진수 75, 150 와 같은 값을 2진 수 체계에서 취급하려면 불리하다. 더구나 2의 승수로 구할 수 없는 숫자 150으로 나누기는 어짜피 정수 나누기 손해가 있다.
C 소스 코드의 [방법 2] 비트 시프트 방식(7비트 우측 시프트 및 반올림 적용)을 기반으로 하여, 실제 하드웨어로 합성 가능한 Verilog RTL 코드를 설계했습니다.
클록(Clock) 사이클마다 데이터를 정밀하게 처리할 수 있도록 3단계 파이프라인(Pipeline) 구조를 적용하여 높은 동작 주파수(Fmax)를 확보할 수 있도록 구현했습니다.

Verilog RTL 소스 코드 (fir8_parallel.v)

module fir8 (
    input  wire        clk,        // 시스템 클록
    input  wire        rst_n,      // 비동기 액티브 로우 리셋
    input  wire        in_valid,   // 입력 데이터 유효 신호
    input  wire [7:0]  x,          // 8비트 입력 데이터 (data_t)
    output reg         out_valid,  // 출력 데이터 유효 신호
    output reg  [7:0]  y           // 8비트 최적화 출력 데이터 (data_t)
);

C 로부터 베릴로그 RTL로 변환 해주면서 설명을 곁들인다.

하드웨어 합성(RTL) 설계 포인트

1. 파이프라인 최적화 (Timing Closure):
• 입력 시프트(shift_reg), 곱셈(prod), 누적 합(sum_acc), 최종 출력(y) 단계를 모두 클록 레지스터로 분리했습니다.
• 이 구조는 FPGA(Intel, AMD-Xilinx) 합성 시 내장된 DSP 블록(DSP48E 등)과 룩업 테이블(LUT) 내부 레지스터에 1:1로 깔끔하게 매핑되어 속도를 극대화합니다.
2. 비트폭 정밀도 계산:
• 8비트 데이터 × 8비트 계수 = 16비트 연산 결과를 보존합니다.
• 8개의 16비트 데이터를 동시 합산하기 위해 내부 가산기 비트폭을 19비트(sum_acc)로 확장하여 데이터 유실(Overflow)을 원천 차단했습니다.
3. 하드웨어 친화적 반올림(Rounding) 구현:
• C 코드의 (acc + 64) >> 7 연산을 구현하기 위해, 덧셈 트리 단계에서 미리 고정값 19'd64를 더해주고 최종 출력에서 비트 슬라이싱([18:7])을 통해 면적(Gate Count) 증가 없이 연산을 처리했습니다.
4. 포화 연산 안전장치:
• 비트 시프트 후 결과가 8비트(0~255)를 초과하는 경우를 대비해 shifted_sum > 255 비교 구문을 넣어 하드웨어 수준에서 값이 무너지는 현상(Wrapping)을 방지했습니다.
이 RTL 코드를 상용 합성 툴(예: Vivado, Quartus, Design Compiler)에 적용할 때 목표로 하는 동작 주파수(Clock Frequency)나, 데이터 입력이 연속으로 들어오는 스트리밍 구조(Ready-Valid Handshake 등)가 추가로 요구되는지 말씀해 주시면 설계에 반영해 드리겠습니다.

AI가 HLS에 더하여 세심한 설명까지 해주니 대견하다. 그동안 알고리즘을 전용 하드웨어로 전환 시키기 위한 수고를 거뜬히 해결해 주는 듯하다. 그렇다고 의심은 눈초리를 풀지는 말아야겠다.

[과제] AI가 내놓은 베릴로그 코드를 시뮬레이션으로 확인해 보라.

Q8. 클럭 레이턴시를 30 이하로 하고 곱셈기 수를 줄여라.

위의 베릴로그 코드에 8개의 곱셈기를 동원한다. 8비트 곱셈기를 하드웨어로 구현하면 규모가 과도하게 커진다. 다수의 곱셈기를 동원한 병렬처리로 매 클럭 마다 출력을 내므로 사용 클럭은 10kHz 미만이다. 샘플링 레이트가 4.8kHz에 불과한 응용 이라지만 (마이크로 프로세서를 채택하는) 내장형 시스템 클럭은 이보다 훨씬 높은 수 mHz다. 이를 감안 하여 클럭 레이턴시 조건을 넓혀놓기로 한다.

클록 레이턴시를 30 이하로 유지하면서 면적(Gate Count)과 DSP 블록의 핵심인 곱셈기(Multiplier)의 개수를 최소화하기 위해 '시리얼/시퀀셜(Sequential) 아키텍처'로 재설계했습니다. 기존 병렬 구조는 곱셈기 8개가 동시에 구동되었으나, 변경된 구조는 단 1개의 곱셈기만 사용하여 8클록 동안 순차적으로 누적 연산(MAC)을 수행합니다. 연산 시작부터 최종 출력까지의 총 레이턴시는 단 9클록으로, 요구하신 기준(30 이하)을 완벽하게 만족합니다.

고효율 시리얼 FIR 필터 Verilog RTL (fir8_serial.v)

module fir8 (
    input  wire        clk,        // 시스템 클록
    input  wire        rst_n,      // 비동기 액티브 로우 리셋
    input  wire        in_valid,   // 새로운 입력 데이터 x가 유효함을 알리는 1클록 펄스
    input  wire [7:0]  x,          // 8비트 입력 데이터
    output reg         out_valid,  // 출력 데이터 y가 유효함을 알리는 1클록 펄스
    output reg  [7:0]  y           // 8비트 최적화 출력 데이터 (레이턴시: 9클록)
);

곱셈기를 1개 사용하여 반복적인 연산을 위해 유한 상태 머신 FSM을 동원하고 있다. AI의 성능 분석을 들어보자.

자원 절약 및 성능 분석

1. 곱셈기 사용량 87.5% 절감:
• 기존 구조: 8개 세트의 곱셈 연산 유닛 배치
• 변경 구조: 단 1개만 배치하여 멀티플렉서(Mux)를 통해 시분할 공유. FPGA 내부의 DSP 블록 소모량이 극적으로 감소합니다.
2. 최적의 레이턴시 보장 (9 클록):
• in_valid 신호가 인가된 후 순차 곱셈에 8클록, 그리고 최종 비트 시프트 및 포화 출력을 내보내는 데 1클록이 소요되어 총 레이턴시는 9클록입니다. 30클록 조건보다 훨씬 빠르게 처리됩니다.
3. 타이밍 안정성 (High Fmax):
• 곱셈 결과와 누적 과정 사이에 레지스터 단계(prod_reg, acc_reg)를 엄격히 둠으로써 신호의 임계 경로(Critical Path)를 끊어 고속 클록에서도 셋업 타임(Setup Time) 위반 없이 안정적으로 합성됩니다.

역시 1개의 샘플을 얻었을 때 콘볼루션 연산(8번의 곱셈과 누산)에 9개 클럭을 사용한다. AI는 코드를 생성하고 설명을 붙이는데 덧붙어 추가적인 제안을 해준다.

이 필터는 새로운 in_valid 신호가 들어오면 기존 연산을 즉시 리셋하고 최신 데이터 기준으로 연산을 시작합니다. 만약 입력 데이터가 공백 없이 매 클록마다 1개씩 연속으로 들어오는 초고속 스트리밍 환경인가요? 그렇다면 연산 회로를 2개 혹은 4개로 복제하는 인터리빙(Interleaving) 기법을 적용해야 하므로, 시스템의 입력 데이터 주기를 알려주시면 최적화에 도움이 됩니다.

[과제] AI가 내놓은 베릴로그 코드를 시뮬레이션으로 확인하고 레이턴시를 비교하라.

Q9. 하드웨어 규모를 평가하라.

시뮬레이션으로 계산의 정확성과 클럭 레이턴시에 만족 했다면 RTL을 확정한다. 합성을 진행 하기 전에 하드웨어의 규모를 예측해본다. 소위 '견적'을 내본다. 

제시된 시리얼 아키텍처 FIR 필터(fir8_serial.v)의 하드웨어 규모(Hardware Resource Utilization)를 평가한 내용입니다. 이 설계는 다수의 곱셈기를 사용하는 병렬 구조와 달리, 시간 분할 공유(Time-sharing) 기법을 적용하여 하드웨어 자원을 극도로 아끼도록 최적화되었습니다. 상용 FPGA(AMD-Xilinx, Intel) 및 ASIC 합성 기준의 예상 자원 소모량은 다음과 같습니다.

주요 논리회로 자원들을 토대로 평가해 준다. 곱셈기와 플립플롭이 관건이다.

1. 주요 자원 소모량 평가 요약
-------------------+-------------+--------------------------------
자원 종류            |             |
(Resource Type)    |예상 소모량    | 하드웨어 평가 및 특징
-------------------+-------------+--------------------------------
DSP 블록/Multiplier |1개 8×8       |곱셈기 1개만 소모 (가장 큰 면적 절감)
플립플롭(Flip-Flop)  |약 120~130비트 |데이터 저장 및 파이프라인 제어용 레지스터
룩업 테이블(LUT)     |약 80 ~ 100개  |멀티플렉서(Mux) 및 가산기(Adder)
-------------------+-------------+--------------------------------

꽤나 정교한 세부분석을 내놓았다.

2. 항목별 세부 분석

💎 1. 곱셈기 (DSP Block / Multiplier) : 극소 (Minimal)

• 평가: 단 1개의 8비트 × 8비트 곱셈기만 사용합니다.
• 설명: 기존 병렬 구조에서는 8개의 DSP 블록이 필요했으나, 본 설계는 1개의 곱셈기를 8번 재사용하므로 하드웨어 면적을 87.5% 절감했습니다.
• FPGA 합성 시 내장 하드웨어 자원인 DSP 블록(예: Xilinx의 DSP48E, Intel의 9-bit DSP 요소를 1개 미만 분할 사용)에 깔끔하게 매핑됩니다.

💾 2. 레지스터 (Flip-Flops / Sequential Logic) : 매우 작음 (Very Small)

전체 플립플롭의 총 비트 수는 약 122비트 내외로 계산됩니다.
• 시프트 레지스터: 8비트 데이터 × 8개 탭 = 64비트
• 파이프라인 레지스터 (prod_reg): 곱셈 결과 저장용 = 16비트
• 누적기 레지스터 (acc_reg): 19비트 가산기 누적용 = 19비트
• 출력 레지스터 (y): 최종 8비트 출력 데이터 = 8비트
• 제어용 FSM 및 카운터: 상태 및 카운트 제어 (state_cnt, calc_running 등) = 약 5비트

⚡ 3. 조합 회로 (LUT / Combinational Logic) : 작음 (Small)

멀티플렉서와 가산기로 이루어지며, 게이트 수가 매우 적습니다.
• 데이터/계수 선택기 (Mux): 8비트 8:1 멀티플렉서 2개가 필요하며, 이는 FPGA 기준 약 30~40개의 LUT로 변환됩니다.
• 가산기 (Adder): 19비트 가산기 1개만 사용되므로 연산 면적이 매우 작습니다. (약 19개 LUT 소모)
• 포화 연산기 (Saturation Logic): 상한값(255) 비교 및 클리핑을 위한 12비트 비교기 1개가 소모됩니다.

총평에 더하여 응용 추천이라니!

3. 하드웨어 규모 총평 및 추천 적용처

• 종합 평가: 본 설계는 초소형(Ultra-low area) 및 저전력(Low power) 시스템에 완벽히 최적화된 아키텍처입니다. 하드웨어 자원 부담이 거의 제로에 가깝기 때문에 아무리 작고 저렴한 가성비 FPGA(예: Xilinx Spartan 시리즈, Intel Cyclone 시리즈)를 사용하더라도 자원 부족 문제를 겪지 않습니다.
• 트레이드 오프(Trade-off): 면적을 극도로 줄인 대신 하나의 출력을 얻기 위해 8클록 동안 연산을 순차 진행해야 하므로, 연속적인 데이터 입력 주기(Sampling Interval)가 최소 8클록 이상의 여유가 있는 시스템(예: 오디오 처리, 저속 센서 신호 처리 등)에 가장 적합합니다.
만약 이 필터를 FPGA가 아닌 ASIC(삼성/TSMC 공정 등)으로 합성할 계획이시거나, 타깃으로 삼고 있는 특정 FPGA 디바이스 모델명이 있으시다면 알려주세요. 해당 환경에 맞춘 더 정확한 게이트 카운트(Gate Count)나 에어리어(Area) 예측치를 도출해 드릴 수 있습니다.
Q10. 클럭 레이턴시가 증가하더라도 하드웨어 면적을 줄일 수 있도록 최적화 하라.

면적은 좀더 쥐어짜봤더니 상대적으로 넓은 면적을 차지하는 플립플롭 대신 메모리(RAM)을 동원 한다. 메모리 접근을 위해 주소와 읽기쓰기 제어 회로가 추가 된다.
 
클록 레이턴시가 늘어나더라도 하드웨어 면적을 극한으로 줄이기 위해, 기존 설계에서 면적을 가장 많이 차지하던 '시프트 레지스터(64비트 Flip-Flop)'와 '8:1 멀티플렉서(Mux)'들을 하드웨어에서 완전히 제거했습니다. 대신 FPGA 내부의 가장 작은 단위 메모리인 Distributed RAM(또는 1개의 듀얼 포트 초소형 RAM 블록)을 사용하여 데이터를 순차적으로 읽고 쓰는 'RAM 기반 시퀀셜 아키텍처'로 재설계했습니다. 이 구조는 데이터 이동을 위한 거대한 플립플롭 체인과 가산기/Mux 로직이 사라지기 때문에, RTL 합성 시 논리 게이트(LUT) 면적을 물리적으로 줄일 수 있는 궁극의 최적화 방식입니다. 새로운 샘플이 들어올 때마다 포인터를 순환(Circular Buffer)시키며, 연산 레이턴시는 약 10클록 수준으로 여전히 30 이하를 유지합니다.

초소형 RAM 기반 FIR 필터 Verilog RTL (fir8_ultra_low_area.v)

module fir8 (
    input  wire        clk,        // 시스템 클록
    input  wire        rst_n,      // 비동기 액티브 로우 리셋
    input  wire        in_valid,   // 새로운 입력 데이터 x 유효 신호 (1클록 펄스)
    input  wire [7:0]  x,          // 8비트 입력 데이터
    output reg         out_valid,  // 출력 데이터 y 유효 신호
    output reg  [7:0]  y           // 8비트 최적화 출력 데이터
);

이어진 설명이다.

면적 극소화 설계 포인트 (Ultra-low Area)

1. 시프트 레지스터 제거 ➔ RAM 매핑:
• 기존 구조에서는 데이터를 옆으로 한 칸씩 밀기 위해 64비트(8비트 × 8개) 분량의 물리적 플립플롭 유닛이 엮여 있어야 했습니다.
• 수정된 구조는 원형 버퍼 구조의 RAM을 사용하여, 데이터를 이동시키지 않고 주소 포인터(wr_ptr, rd_ptr)만 바꿉니다. FPGA 합성 시 복잡한 가산 구조 없이 순수 복제용 LUT RAM(Distributed RAM) 공간으로 밀려 들어가 로직 면적이 획기적으로 감소합니다.
2. 조합 회로 무력화 (대형 Mux 제거):
• 8개 레지스터에서 값을 동시에 고르기 위해 배치되었던 대형 8:1 멀티플렉서 회로들이 통째로 빠졌습니다. RAM 인터페이스의 단일 출력 포트(ram_rd_data) 하나로 간소화되어 게이트 카운트가 한 자릿수 수준으로 줄어듭니다.
3. 레이턴시 및 제어 균형:
• 연산 레이턴시는 데이터 로딩 파이프라인이 조금 더 정밀해짐에 따라 기존 9클록에서 10클록으로 단 1클록 증가했습니다. 요구 조건인 30클록 이하 조건을 여유롭게 충족하면서 하드웨어 다이(Die) 사이즈를 극소화할 수 있는 가장 정제된 하드웨어 아키텍처입니다.

Q11. 하드웨어를 줄이기 위해 가산과 시프트(Shift-and-Add) 연산만을 반복하여 곱셈을 수행하는 비트-시리얼(Bit-serial) 방식으로 구현하라.

신호처리 알고리즘의 하드웨어 구현에서 곱셈기는 공공의 적이다. 가나마 한개 있던 곱셈기 마져 줄여보자.

곱셈기(DSP 블록)마저 완전히 제거하고 오직 가산기(Adder)와 시프트 레지스터(Shift Register)만을 반복 사용하여 곱셈을 수행하는 비트-시리얼(Bit-serial / Shift-and-Add) 아키텍처로 구현했습니다. 이 설계는 하드웨어 코어 면적에서 가장 큰 비중을 차지하는 곱셈 연산 장치를 통째로 덜어내기 때문에 RTL 합성 시 논리 게이트 소모량을 물리적 한계까지 낮춘 궁극의 초소형 구조입니다. 8비트 곱셈 1회를 수행하는 데 8클록이 소요되며, 8개의 탭을 순차 처리하므로 최종 출력까지의 총 레이턴시는 약 18클록입니다. 여전히 요구 조건인 30클록 이하를 완벽히 충족합니다.

곱셈기 제로(0) 비트-시리얼 FIR 필터 Verilog RTL(fir8_no_mult.v)

module fir8 (
    input  wire        clk,        // 시스템 클록
    input  wire        rst_n,      // 비동기 액티브 로우 리셋
    input  wire        in_valid,   // 새로운 입력 데이터 x 유효 신호 (1클록 펄스)
    input  wire [7:0]  x,          // 8비트 입력 데이터
    output reg         out_valid,  // 출력 데이터 y 유효 신호
    output reg  [7:0]  y           // 8비트 최적화 출력 데이터 (레이턴시: 18클록)
);

곱셈기를 없앤 대신 레이턴시가 18클럭으로 늘었다.

하드웨어 극소화 메커니즘 분석

1. 곱셈기 하드웨어 소모 ZERO (DSP = 0):
• 8비트 곱셈 연산을 수행하기 위해 물리적 곱셈 유닛을 선언하지 않았습니다. 대신 계수의 비트 값(current_coef[bit_cnt])에 따라 가산기 입력 게이트를 열고 닫는 제어 회로와 비트 시프터(<< bit_cnt)만으로 대체했습니다.
• 이를 통해 FPGA 연산 자원(DSP Block) 소모를 완벽히 배제했습니다.
2. 배럴 시프터(Barrel Shifter) 생략 최적화:
• 시프트 연산 current_data << bit_cnt는 합성 툴에 의해 복잡한 시프트 로직 회로가 아닌, 카운터 비트에 맵핑되는 초소형 멀티플렉서 구조로 합성되므로 조합 회로의 다이(Die) 면적이 극적으로 낮아집니다.
3. 레이턴시 타협 (18 클록):
• 하드웨어 구조를 극단적으로 단순화한 대가로 처리 시간이 늘어났습니다. 새로운 샘플이 들어온 후 출력이 나오기까지 정확히 18클록이 소요됩니다.
• 시스템 요구 규격인 30클록보다 충분히 빠른 속도이므로 오디오 가공, 제어 계측 루프, 의료용 센서 모니터링 등 자원 절약이 최우선인 엣지 디바이스(Edge Device) 시스템에 가장 이상적인 최적화 형태입니다.

Q12. 하드웨어를 줄일 수 있다면 RAM 대신 FF 를 사용해도 좋다.

'내 칩' MPW의 디자인 킷은 내장 메모리(RAM Compiler) 라이브러리를 가지고 있지 않다. 플립플롭을 사용토록 하였다.

하드웨어 규모를 극도로 줄이는 관점에서, 8×8비트(총 64비트) 분량의 초소형 데이터 저장소는 RAM 블록이나 분산 RAM을 쓰는 것보다 플립플롭(FF, Flip-Flop)을 사용하는 것이 제어 로직 면에서 더 이득일 수 있습니다. RAM을 사용하면 주소를 생성하는 포인터(wr_ptr, rd_ptr), 디코더, 읽기 동기화 레지스터 등의 '주변 제어 로직(Overhead)'이 추가로 필요하여 배보다 배꼽이 더 커지기 때문입니다. 따라서 RAM과 주소 포인터를 과감히 모두 제거하고, 데이터는 8개의 단순 FF 체인으로 저장하되 연산은 곱셈기 없이 덧셈과 시프트(Shift-and-Add)만 수행하는 최종 압축 아키텍처를 설계했습니다. 총 레이턴시는 11클록으로 대폭 줄어들면서 조합 로직 면적은 최소화됩니다.

최종 최적화: FF 기반 비트-시리얼 FIR 필터 Verilog RTL (fir8_no_mult_FF.v)

module fir8 (
    input  wire        clk,        // 시스템 클록
    input  wire        rst_n,      // 비동기 액티브 로우 리셋
    input  wire        in_valid,   // 새로운 입력 데이터 x 유효 신호 (1클록 펄스)
    input  wire [7:0]  x,          // 8비트 입력 데이터
    output reg         out_valid,  // 출력 데이터 y 유효 신호
    output reg  [7:0]  y           // 8비트 최적화 출력 데이터 (레이턴시: 11클록)
);

RAM 사용에 필요한 인터페이스 로직 회로가 필요없다. 레이턴시도 11클록으로 줄었다.

면적 최소화 비교 및 개선 결과

1. 주변 제어 회로의 완벽한 몰락 (추가 면적 감소):
• 이전 RAM 기반 설계에서 필요했던 쓰기 포인터, 읽기 포인터, 복잡한 데이터 주소 디코딩 로직이 전부 제거되었습니다.
• 64비트 데이터를 저장하기 위해 소모되는 플립플롭은 소량 증가했으나, 이로 인해 지워지는 조합 회로(LUT)의 양이 훨씬 많으므로 전체 게이트 카운트(Gate Count)는 오히려 더 감소합니다.
2. 연산 스케줄러 최적화 (레이턴시 18클록 ➔ 11클록):
• 필터 계수 값들의 특성(최대 크기 34)을 분석한 결과, 6번 비트와 7번 비트는 모든 계수에서 0입니다.
• 따라서 불필요한 비트 루프 연산을 하드웨어에서 과감히 도려내어, 레이턴시를 기존 18클록에서 단 11클록으로 단축시켰습니다. 속도와 면적을 동시에 챙긴 결과입니다.
3. 진정한 의미의 곱셈기 제로(0):
• 하드웨어 툴이 곱셈 전용 연산 가산 트리(DSP)를 아예 매핑하지 못하도록 case 문 내부에서 상수 비트 시프트 연산(<< 0, << 1 등) 구조로 하드와이어링(Hard-wired) 제어를 수행합니다. 면적 보존 면에서 최상위 클래스의 고효율을 보여줍니다.
곱셈기와 하드웨어 RAM, 복잡한 멀티플렉서 제어까지 모두 덜어낸 최적의 다이어트 회로가 완성되었습니다.

---------------------------

AI를 활용하여 C++ 로 작성한 알고리즘(FIR 필터)를 상이한 여러 환경에서 구현하는 방법을 살펴봤다. AI 이전에는 경험과 노력을 동원하여 RTL 설계를 수행해왔다. 아래 문서는 C 코드로 부터 RTL 설계하는 과정을 다루고 있다.

    연구노트9: C++에서 RTL까지, FIR 필터(1부) [바로가기]

AI를 활용하면 C 코드를 가지고 용도에 따라 상이한 조건으로 베릴로그 RTL을 만들어 내는데 매우 높은 생산성을 보여준다. AI 시대에 필요한 덕목은 질문하는 기술이라고 한다. 그 질문은 그냥 얻어지는 것은 아니리라....

-----------------------------

Verilog RTL 실습:
'fir8_hls_with_Gemini' [깃허브 저장소]

 


2026년 11월 3일 화요일

[내칩강의2026/7주] 수업공지: 내 칩 MPW 공정 표준 셀 합성 및 제조도면 생성

[내칩강의2026/7주] 내 칩 MPW 공정 표준 셀 합성 및 제조도면 생성

이 강의의 목표는 '내 칩 제작 서비스'의 MPW에 지원[바로가기]하여 세상에서 유일한 내 칩을 가져보는 것입니다. 7주차 수업은 반도체 제조공장에 내 칩을 의뢰하기 위한 제조도면(레이아웃)을 만드는 과정을 살펴봅니다.

컴퓨팅 언어로 기술되어 시뮬레이션과 에뮬레이션으로 충분히 검증된 나의 아이디어를 이제 전용 칩(ASIC, Application Specific IC)으로 제작할 단계에 왔습니다. 우리가 칩 제작을 맞길 공장은 0.5um CMOS 공정을 갖춘 ETRI 특화 반도체 센터의 반도체 제조 시설입니다. 제조도면은 이 반도체 제조 시설에서 제공하는 도면 그리기 규칙(Design Rule)을 따라야 합니다. 이 규칙 문서[링크]에는 트랜지스터를 그리는 기하학적인 방법을 규정하고 있습니다. 하지만 무려 수천에 달하는 트랜지스터로 구성된 디지털 회로(예를 들어 '우주 침략자 게임')를 일일이 트랜지스터 하나하나 그릴수 없습니다.

디지털 회로의 제조도면 생성은 베릴로그 RTL을 합성하여 디지털 논리 소자들의 네트리스트(문서화된 회로도)를 가지고 시작합니다. 논리소자(각종 게이트와 플립플롭)들은 다수의 트랜지스터로 구성된 회로입니다. 이 작은 논리 소자 회로는 미리 라이브러리로 준비하여 디지털 디자인 킷으로 제공 합니다. 디지털 반도체 설계에서 합성 이후의 도면 생성의 과정은 자동화 되어 있습니다. 논리 소자들의 동작은 수학적으로 표현 할 수 있으므로 자동화가 가능 하지만 물리적 그리고 환경적 영향을 많이 받는 트랜지스터 조합의 회로는 자동화가 불가하다는 점도 기억해 둡시다.

이번주 수업에서 '우주 침략자'게임기 예제 중 Q.14[바로가기] 을 가지고 합성, 자동 표준셀 배치 배선 그리고 칩-탑 까지 함께 실시해볼 예정입니다. 아래의 문서를 미리 학습하시기 바랍니다.

    연구노트12: FIR PE의 GDS [바로가기]

이번주(7주차) 수업은 11월 5일(목요일)에 온-라인 줌 미팅 수업으로 진행 하겠습니다.

수업 공지:
- 11월 5일(목요일) 오후 9시에 온라-인 줌 회의로 실시합니다.
- 줌링크: https://snu-ac-kr.zoom.us/j/4984347294?pwd=0JVrMTnCscaN26KrkgjmAjVYh5LmV6.1
* 수업은 공개이므로 누구나 참여할 수 있습니다.


[내칩강의2026/6주] 지난주 까지 과제 되짚어 보기

[내칩강의2026/6주] 지난주 까지 과제 되짚어 보기

지난주까지 인공지능을 활용하여 비디오 게임기 "우주 침략자[링크]" 를 베릴로그 RTL로 작성하고 SystemC(C++) 테스트벤치를 작성하여 시스템 수준 검증과 FPGA를 동원하여 에뮬레이션 및 프로토 타이핑까지 실습해봤습니다. 코드는 인공지능이 작성해 주었고 이를 검증하기 위한 환경(테스트 벤치를 비롯하여 시뮬레이터, 에뮬레이터 빌드 스크립트 등)은 제공된 예제를 따라해 봤습니다. 그 과정에서 스스로 질문을 하고 해결책을 찾아보면서 디지털 반도체 설계 과정을 개략적으로 체득하는 중입니다. 학습 동료들의 학습 노트를 읽어보고 동료들은 어떤 생각을 하고 어떻게 공부하며 질문을 가지고 있는지 함께 돌아보시기 바랍니다.

1. 권효재의 5주차 학습노트[보기]
2. 김예원의 5주차 학습노트[보기]
3. 염재민의 5주차 학습노트[보기]
4. 이현서의 5주차 학습노트[보기]
5. 한찬희의 5주차 학습노트[보기]
6. 안준영의 5주차 학습노트[보기]
7. 김희창의 5/6주 학습노트[보기]
8. 안휘준의 5주차 학습노트[보기]
9. 이수빈의 5주차 학습노트[보기]

이 강의의 목표는 '내 칩 제작 서비스'의 MPW에 지원하여 세상에서 유일한 내 칩을 가져보는 것입니다.오는 7주차 수업은 반도체 제조공장에 내 칩을 의뢰하기 위한 제조도면(레이아웃)을 만드는 과정을 살펴보려고 합니다. 11월 5일(목요일)에 온-라인 줌 미팅 수업으로 진행 하겠습니다.

이제 과정의 중반을 넘긴 만큼 여러분 스스로 설계를 시작 하십시요. 제공한 예제에서 개선점을 찾아 '내 칩'으로 만들어도 좋습니다. 새로운 게임을 만들어도 좋습니다. 지난주 예제에서 봤던 수학적 알고리즘도 좋습니다. 알고리즘을 컴퓨팅 언어로 기술하고 이를 시뮬레이션과 에뮬레이션으로 검증하며 '내 칩'이 '남의 칩'과 어울려 동작하는 모습을 보여줄 수 있도록 준비하십시요. 자신의 아이디어를 가졌다면 인공지능을 포함하여 어떤 도구를 사용해도 좋습니다. 단, 반드시 검증되어야 합니다.

인공지능이 엔지니어를 대체할 것이라는 '예언'들을 흔히 합니다. 어쩌면 '현실'일지도 모릅니다. IEEE Spectrum Career Alert에 이런 글이 올라왔군요.

    "상사가 인공지능과 당신을 비교하면 어떻할래?"
    Convincing Your Boss an LLM Can't Replace You.

이에 대한 우리는 뭐라도 답할 준비가 있어야 겠지요. 원문을 읽어보시기 바랍니다.

----------------------------------------------------------------

Your Boss Built an App Over the Weekend (So Why Is Your Team So Slow?)

If you're an engineer, you may feel like your skills aren't commanding the premium they once did. Many engineers have faced layoffs or restructuring. But even for those of us who still have jobs, something corrosive is happening. You may have heard some version of this:

    "ChatGPT answered that in five seconds. Why can't you?"

Or,

    "I made this app in a weekend. Why is our team taking so long?"

I worked on a team where leadership said things like this directly to engineers. This was in 2024 and 2025, when the AI craze was in full swing and Claude and ChatGPT adoption was rising.

Demo-Driven Development

Generative AI (and the marketing behind it) gives people the illusion that the work is simpler than it is. A working demo hides complexity, and once someone has a website that works, they may decide they no longer need engineers, or start to distrust the ones they do have.

Some CEOs want to believe they can replace an engineering team with a few sexy prompts. Many companies also want them to believe this.

That website works great in the demo—under specific conditions and without users with thumbs for fingers. Then it has to plug into a system with millions of lines of old code, satisfy legal requirements, meet accessibility standards, and be delivered in a complex deployment process.

Thinking that writing code was the only bottleneck for deploying new software is like the Titanic seeing the tip of the iceberg and assuming there was nothing underneath it.

What you can do about it

Engineers have a very unique position in the AI space, because you have the capacity to understand how these models work much better than the average person. Use this to your advantage.

Learn how AI models work. If you can't already explain that LLMs are probabilistic, what tokens are, or why training data matters, spend a weekend fixing that. You don't need to become a full-blown AI researcher. A few fundamentals will put you ahead of most people making the AI decisions.

Teach upward. Turn that knowledge into teaching moments for the people who lead you. Explain the limits of these tools, and why a demo and a production system are different animals. It keeps people from truly believing that a notebook full of prompts can replace an engineering team.

Consider the criticism of today’s processes. Some of what we do has been treated as ceremony for too long. Be willing to say, "you know what, maybe you're right, this process is too slow for the speed we can work at now." Conceding that earns you the credibility to defend the steps that matter.

I still believe that speed at the cost of everything else is harmful—but that’s a topic for another time.

Find your voice. Engineers at the forefront of AI adoption need to push back and explain why we do things the way we do. Nobody else in the room is going to do it for us.

Don't chase (too many) shiny objects

Your goal, in my opinion, is to save your organization from buying into the hype so much that they chase the wrong thing. Our job, before and after AI, is building safe and useful products for human beings, and engineers are still best suited for that.

By Brian Jenney

-----------------------------------------------------------------
[인공지능 번역]

상사가 주말 동안 앱을 개발했는데 (그런데 왜 우리 팀은 이렇게 느린 걸까요?)

엔지니어라면 자신의 기술이 예전만큼 가치를 인정받지 못한다고 느낄 수도 있습니다. 많은 엔지니어들이 해고 나 구조 조정을 경험했습니다. 하지만 아직 직장을 유지하고 있는 사람들조차도 뭔가 좋지 않은 일이 벌어지고 있다는 것을 느끼고 있을 겁니다. 아마 이런 이야기를 들어보셨을 겁니다.

    "ChatGPT는 5초 만에 답했어요. 당신은 왜 못하죠?"

또는
    "나는 이 앱을 주말에 만들었는데, 우리 팀은 왜 이렇게 오래 걸리는 거죠?"

저는 리더십 팀에서 엔지니어들에게 이런 말을 직접적으로 하는 팀에서 일했습니다. 때는 AI 열풍이 한창이던 2024년과 2025년이었고, Claude와 ChatGPT의 사용이 급증하던 시기였습니다.

데모 기반 개발 방식,

생성형 AI(그리고 그 마케팅 전략)는 사람들에게 실제 작업보다 훨씬 간단하다는 착각을 불러일으킵니다. 작동하는 데모는 복잡성을 감추고, 일단 웹사이트가 제대로 작동하기 시작하면 엔지니어가 더 이상 필요 없다고 생각하거나 기존 엔지니어들을 불신하기 시작할 수도 있습니다.

일부 CEO들은 몇 가지 매력적인 프롬프트로 엔지니어링 팀을 대체할 수 있다고 믿고 싶어합니다. 그리고 많은 회사들도 그들이 그렇게 믿기를 바랍니다.

해당 웹사이트는 데모 환경에서는 특정 조건 하에서, 그리고 엄지손가락이 없는 사용자라면 훌륭하게 작동합니다. 하지만 실제로는 수백만 줄의 오래된 코드로 이루어진 시스템에 통합되어야 하고, 법적 요구 사항을 충족해야 하며, 접근성 표준을 준수하고, 복잡한 배포 프로세스를 거쳐 제공되어야 합니다.

새로운 소프트웨어 배포에 있어 코드 작성만이 유일한 병목 현상이라고 생각하는 것은 타이타닉호가 빙산의 일각만 보고 그 아래에는 아무것도 없다고 생각하는 것과 같습니다.

어떻게 해야 할까요?

엔지니어는 인공지능 분야에서 매우 특별한 위치에 있습니다. 일반인보다 이러한 모델의 작동 방식을 훨씬 더 잘 이해할 수 있기 때문입니다. 이 점을 활용하세요 .

AI 모델이 어떻게 작동하는지 배우세요 . LLM이 확률적이라는 것, 토큰이 무엇인지, 학습 데이터가 왜 중요한지 설명할 수 없다면 주말을 투자해서라도 이 부분을 보완하세요. 꼭 전문적인 AI 연구원이 될 필요는 없습니다. 몇 가지 기본 사항만 이해해도 AI 관련 의사 결정을 내리는 대부분의 사람들보다 앞서 나갈 수 있습니다.

상사에게 가르치세요 . 그 지식을 활용하여 당신을 이끄는 사람들에게 가르침을 주세요. 이러한 도구의 한계를 설명하고, 데모 시스템과 실제 운영 시스템이 왜 다른지 알려주세요. 이렇게 하면 노트에 적어둔 메모가 엔지니어링 팀을 대체할 수 있다고 사람들이 믿지 않도록 할 수 있습니다.

오늘날의 업무 절차에 대한 비판을 생각해 보세요 . 우리가 하는 일 중 일부는 너무 오랫동안 형식적인 절차로만 여겨져 왔습니다. "맞아요, 어쩌면 당신 말이 맞을지도 몰라요. 이 절차는 우리가 지금 일할 수 있는 속도에 비해 너무 느립니다."라고 기꺼이 말할 줄 알아야 합니다. 그런 점을 인정하는 순간, 중요한 절차들을 옹호할 수 있는 신뢰를 얻게 됩니다.

저는 여전히 다른 모든 것을 희생하면서까지 속도를 추구하는 것은 해롭다고 생각하지만, 그건 나중에 다시 이야기해 볼 주제입니다.

자신의 목소리를 찾으세요 . AI 도입의 최전선에 있는 엔지니어들은 왜 우리가 이런 방식으로 일을 하는지 설명하고 반박해야 합니다. 회의실에 있는 누구도 우리 대신 그 일을 해줄 수 없습니다.

(너무 많은) 반짝이는 것들을 쫓지 마세요.

제 생각에 여러분의 목표는 조직이 과대광고에 현혹되어 잘못된 것을 쫓지 않도록 막는 것입니다. 인공지능 이전이든 이후든 우리의 임무는 인간을 위한 안전하고 유용한 제품을 만드는 것이며, 엔지니어는 여전히 그 일을 가장 잘 해낼 수 있는 사람들입니다.

브라이언 제니 지음

[내칩강의2026/6주] 수업 공지: HLS(High Level Synthesis)

[내칩강의2026/6주] 수업 공지: HLS(High Level Synthesis)

지난 몇주동안 인공지능과 대화하며 비디오 게임기를 베릴로그 RTL로 설계해 봤습니다.

SystemC를 활용한 시스템 수준 테스트벤치를 익혔고 FPGA 로 에뮬레이션 검증도 해봤습니다. 이제 코-시뮬레이션(Co-Simulation)과 코-에뮬레이션(Co-Emulation)을 이해하고 반도체 설계에서 검증의 중요성도 알게 되었을 것입니다. 안다는 것에 머물지 않고 자신의 견해를 자신있게 피력할 수 있어야 합니다. 우리가 학습 과제를 (에세이)글을 작성하는 목적 이기도 합니다.

이번주에는 '게임기'에서 잠시 벗어나 HLS라고 하는 '고위 합성' 설계 방법론을 접해볼 것입니다.

HLS(High Level Synthesys)는 높은 추상화 수준 언어로 기술한 알고리즘을 디지털 반도체로 구현하는 설계 방법론입니다. 시스템 수준 C++언어에서 RTL 베릴로그로 합성합니다. 수많은 알고리즘(디지털 신호처리, 인공지능 등)들이 그동안 고성능 CPU에서 실행되어 오다가 한계(어떤 한계일까요?)에 이르자 하드웨어로 실현하고자 하는 요구가 높아졌습니다. 하지만 추상화 수준의 장벽이 너무 높아 이 과정은 설계자의 경험과 능력에 크게 좌우되어 왔습니다. 자동화 도구는 꿈으로 여겨질 정도 였고 많은 연구가 진행되어 실제로 고가의 자동화 도구들이 등장했습니다. 그동안의 HLS 와 상용 도구들은 아래 링크의 논문을 통해 살펴볼 수 있습니다.

    Towards Automatic High-Level Code Deployment on Reconfigurable Platforms: A Survey of High-Level Synthesis Tools and Toolchains, IEEE Access, Vol.8,2020,
    https://ieeexplore.ieee.org/abstract/document/9195872

그리고 아래 링크의 글을 참조하십시요. Xilinx의 Vitis-HLS 의 튜토리얼 가이드 문서를 정리하였습니다. 2021년도 문서임을 감안 하십시요.

    고위합성 튜토리얼(High-Level Synthesis Tutorial),
    https://hls-goodkook.blogspot.com/2021/08/ug871-xilinx-high-level-synthesis.html

인공지능은 이 꿈의 설계 방법론을 현실화 합니다. 이번주 학습에서 C/C++ 언어로 작성된 알고리즘을 인공지능이 어떻게 베릴로그 RTL 로 합성해 내는지 공부합니다. 가벼운 마음(C++를 쉽게 다룰 수 있으니까!)으로 HLS 예제를 살펴볼 것입니다. 간단한 '시계' 예제부터 조금 복잡한 FIR 필터를 다뤄 보겠습니다. 예제는 디지인 킷에 올려 놓았습니다. C++ 언어로 작성된 알고리즘과 이를 RTL 로 합성(인공지능을 통한 변환)한 후 시뮬레이션을 비교해 보십시요. 인공지능과 대화를 통해 C++ 를 제시하고 이를 RTL 로 변환하도록 지시하여 얻은 베릴로그 RTL을 
시뮬레이션하기 위한 SystemC 테스트벤치 그리고 코-에뮬레이션 검증까지 포함되어 있습니다. 인공지능인 신통하긴 하지만 완벽하지는 않습니다. 신통함에 머물지 않고 검증해야 한다는 자세를 견지하십시오.


소프트웨어에서는 프로그래밍 언어로 작성된 함수를 호출 하므로써 실행되지만 RTL은 '클럭'이라는 아주 특별한 시간 관리 체계에 따라 수행 됩니다. 클럭은 하드웨어의 병렬성(동시성)에 맞춰 순차적 알고리즘을 처리할 수 있게 해줍니다. 소프트웨어에서는 함수를 호출하고 되돌려 주는 값을 반환 받는 것으로 종료됩니다. 하지만 늘 작동중인(클럭은 쉬지 않으므로) 하드웨어는 어느 시점에 입력을 주고 계산을 시작하여 유효한 값을 출력하는지 알기위해 별도의 표시가 필요합니다. 이를 핸드쉐이크(hand-shake) 또는 '인터페이스 절차' 라고 합니다. 예제 '시계'는 클럭을 받아 이를 시-분-초로 세며 값을 표시합니다. 시계의 동작은 출력을 외부에서 읽었는지 상관 하지 않습니다. '시계'라는 장치의 알고리즘이 그렇기 때문입니다. 이에 반해 알고리즘의 복잡도와 구현(C++ 언어에서 하드웨어로 옮긴)한 방법(병렬 혹은 순차)에 따라 사용할 클력의 갯수가 달라지는 '디지털 필터' 예제는 입력을 주는 순간 계산을 개시하고 유효한 결과 값을 낼 때까지 기다려 다음 입력을 주어야 합니다. '필터'가 출력한 값을 놓쳐서도 않됩니다. 구현하려는 하드웨어의 성격(사양)에 따라 데이터를 주고받는 절차가 달라 질 수 있으니 '인터페이스'도 매우 중요한 설계 요소 입니다.

대면 수업은 하던대로 금요일 1시 강의실에서, 비대면 수강생들은 자습으로 HLS 예제를 수행하시기 바랍니다. 예제 디렉토리에 제미니와 대화 내용을 올려 놓았으니 참고 하십시요. 과제는 월요일에 게시됩니다.