DSP 개요#
SP500 DSP API를 사용하면 REGULUS Cortex-A53 호스트의 애플리케이션이 Vision DSP에서 연산을 실행할 수 있습니다. API는 공유 라이브러리 libsp500.so 하나와 C 헤더 네 개로 구성됩니다.
지원 연산#
연산 |
헤더 |
설명 |
|---|---|---|
Letterbox |
|
종횡비를 유지한 리사이즈와 상수 패딩입니다. 카메라 프레임을 고정 크기의 모델 입력으로 만들고, 검출 결과를 원본 프레임 좌표로 되돌리는 데 필요한 배율과 패딩을 반환합니다. |
Log-mel spectrogram |
|
float32 PCM에서 Whisper 방식의 log-mel spectrogram을 계산합니다. Whisper, Qwen3-ASR 같은 음성 모델용이며, CPU 참조 구현도 포함합니다. |
YOLO decode |
|
YOLO 네트워크의 raw 출력 텐서를 검출 결과 목록으로 변환합니다. 박스 디코딩, 신뢰도 필터링, 중복 제거를 DSP 왕복 한 번으로 처리하며 detection, segmentation, pose를 지원합니다. |
sp500/dsp.h는 기반 헤더로, 상태 코드, DSP 연결, 공유 메모리, 버퍼를 정의합니다. 각 모듈 헤더가 이 헤더를 포함하므로 필요한 모듈 헤더만 포함하면 됩니다.
구조#
Application (Cortex-A53)
| sp500_* calls
v
libsp500.so -----------------------> Vision DSP firmware
| request / completion |
v v
+----------------------- shared-memory window ------------------------+
| input and output buffers, read and written in place by the DSP |
| and, through their physical address, by the NPU |
+---------------------------------------------------------------------+
애플리케이션은 호스트에서 API를 호출합니다. 호출할 때마다 DSP 펌웨어에 요청을 하나 보내고 완료될 때까지 기다립니다.
텐서는 버퍼로 전달합니다. 공유 메모리 영역에 할당한 버퍼는 DSP가 그 자리에서 직접 읽고 씁니다. 일반 애플리케이션 메모리에 있는 버퍼는 공유 메모리를 거쳐 전달되므로 방향마다 복사가 한 번씩 발생합니다.
공유 메모리 영역은 물리적으로 연속되어 있습니다. 공유 메모리 버퍼의 물리 주소를 NPU 런타임에 넘기면, DSP와 NPU가 복사 없이 같은 메모리를 사용합니다.
API 설계#
순수 C와 안정적인 ABI#
헤더는 순수 C이며 <stdint.h>와 <stddef.h>만 포함합니다. C는 물론 모든 C++ 컴파일러와 표준 라이브러리, C FFI를 지원하는 모든 언어에서 사용할 수 있습니다. 헤더가 정의하는 모든 이름은 sp500_ 또는 SP500_으로 시작합니다.
함수는 공유 라이브러리의 ABI를 구성하며 다음 규칙을 따릅니다.
모든 함수는
sp500_status(int32_t) 또는 고정 폭 스칼라를 반환합니다. 열거형은 상수 이름만 정의하며, 값은 ABI 경계에서int32_t또는uint32_t로 전달됩니다.구조체는 값으로 전달하거나 반환하지 않고 포인터로만 전달합니다.
모든 구조체는 0이어야 하는
reserved배열로 끝납니다. 이후 버전에서 이 필드에 의미가 부여되더라도 값 0은 현재 동작을 뜻합니다.어떤 함수도 예외를 던지지 않습니다.
파라미터 구조체와 기본값#
각 연산은 파라미터 구조체를 받고, 파라미터 구조체마다 기본값을 채우는 *_params_init() 함수가 있습니다. 기본값이 모두 0은 아니므로 필드를 설정하기 전에 항상 init 함수를 호출하세요. 모든 필드가 0인 구조체는 필수 필드 값이 0이면 유효하지 않으므로 거부됩니다. 하지만 0으로 채운 뒤 필수 필드만 설정한 구조체는 나머지 옵션이 기본값이 아닌 0으로 실행됩니다.
버퍼#
모든 텐서는 sp500_buffer입니다. 버퍼가 자신의 메모리 위치를 기록하므로 같은 함수가 두 종류의 버퍼를 모두 받습니다.
생성 함수 |
메모리 |
비용 |
|---|---|---|
|
공유 메모리 영역 |
복사 없음. |
|
애플리케이션이 이미 가진 메모리 |
호출마다 방향별로 한 번씩 복사합니다. 해제할 필요가 없습니다. |
|
없음 |
선택적 텐서를 사용하지 않는다고 표시합니다. |
크기가 큰 텐서와 NPU도 읽고 쓰는 텐서에는 공유 메모리 버퍼를 사용하세요.
스레드#
모든 함수는 여러 스레드와 여러 프로세스에서 동시에 호출할 수 있습니다. 각 DSP 호출은 왕복이 끝날 때까지 모든 스레드와 프로세스가 공유하는 잠금 하나를 잡으므로, 동시 호출은 안전하지만 한 번에 하나씩 실행됩니다. 여러 호출을 하나의 원자적 단위로 묶으려면 sp500_dsp_lock()과 sp500_dsp_unlock()으로 감싸세요.
상태 코드#
모든 호출은 sp500_status를 반환합니다. SP500_OK는 0이고 모든 오류는 음수이므로, rc != SP500_OK와 rc < 0 모두 올바른 검사입니다. sp500_status_string()은 코드를 문자열로 변환합니다. 출력 값은 SP500_OK를 반환했을 때만 유효합니다.
코드 |
값 |
의미 |
|---|---|---|
|
0 |
완료되었으며 출력 버퍼가 유효합니다. |
|
-1 |
파라미터가 없거나 범위를 벗어났거나 다른 파라미터와 맞지 않습니다. |
|
-2 |
모듈이 해당 조합을 실행할 수 없습니다. |
|
-3 |
전달한 버퍼가 결과를 담기에 너무 작습니다. |
|
-4 |
|
|
-5 |
호스트 또는 공유 메모리 할당에 실패했습니다. |
|
-6 |
DSP가 기한 내에 응답하지 않았습니다. |
|
-7 |
DSP가 실패를 보고했습니다. |
|
-8 |
예상하지 못한 내부 상태입니다. |
이후 버전에서 코드가 추가될 수 있으므로 알 수 없는 음수 값은 일반 실패로 처리하세요. 다음 두 결과는 오류가 아니라 성공입니다. 검출할 대상이 없는 프레임은 검출 수 0과 함께 SP500_OK를 반환하고, 샘플 수가 hop 하나보다 적은 log-mel 요청은 프레임 수 0과 함께 SP500_OK를 반환합니다.
버전 관리#
라이브러리 버전이 곧 API 버전입니다. sp500_version()은 로드된 라이브러리의 버전을 SP500_VERSION_INT와 같은 방식(major * 10000 + minor * 100 + patch)으로 반환하고, sp500_version_string()은 문자열로 반환합니다.
Major 릴리스는 라이브러리 SONAME이 바뀌므로 애플리케이션을 다시 빌드해야 합니다.
Minor와 patch 릴리스는 다시 빌드하지 않고 라이브러리만 교체하면 됩니다.
라이브러리는 세 가지 이름으로 설치됩니다.
libsp500.so -> libsp500.so.1 (link time: what -lsp500 finds)
libsp500.so.1 -> libsp500.so.1.0.0 (run time: the SONAME)
libsp500.so.1.0.0 (the library itself)
애플리케이션 바이너리에는 libsp500.so.1이 기록됩니다. 장치에 라이브러리를 복사하는 방법은 DSP 프로그래밍 가이드를 참고하세요.