# DSP 개요

SP500 DSP API를 사용하면 REGULUS Cortex-A53 호스트의 애플리케이션이 Vision DSP에서 연산을 실행할 수 있습니다. API는 공유 라이브러리 `libsp500.so` 하나와 C 헤더 네 개로 구성됩니다.

## 지원 연산

| 연산 | 헤더 | 설명 |
| --- | --- | --- |
| Letterbox | `sp500/letterbox.h` | 종횡비를 유지한 리사이즈와 상수 패딩입니다. 카메라 프레임을 고정 크기의 모델 입력으로 만들고, 검출 결과를 원본 프레임 좌표로 되돌리는 데 필요한 배율과 패딩을 반환합니다. |
| Log-mel spectrogram | `sp500/log_mel.h` | float32 PCM에서 Whisper 방식의 log-mel spectrogram을 계산합니다. Whisper, Qwen3-ASR 같은 음성 모델용이며, CPU 참조 구현도 포함합니다. |
| YOLO decode | `sp500/yolo.h` | YOLO 네트워크의 raw 출력 텐서를 검출 결과 목록으로 변환합니다. 박스 디코딩, 신뢰도 필터링, 중복 제거를 DSP 왕복 한 번으로 처리하며 detection, segmentation, pose를 지원합니다. |

`sp500/dsp.h`는 기반 헤더로, 상태 코드, DSP 연결, 공유 메모리, 버퍼를 정의합니다. 각 모듈 헤더가 이 헤더를 포함하므로 필요한 모듈 헤더만 포함하면 됩니다.

## 구조

```text
 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`입니다. 버퍼가 자신의 메모리 위치를 기록하므로 같은 함수가 두 종류의 버퍼를 모두 받습니다.

| 생성 함수 | 메모리 | 비용 |
| --- | --- | --- |
| `sp500_buffer_alloc()` | 공유 메모리 영역 | 복사 없음. `sp500_buffer_release()`로 해제합니다. |
| `sp500_buffer_wrap()` | 애플리케이션이 이미 가진 메모리 | 호출마다 방향별로 한 번씩 복사합니다. 해제할 필요가 없습니다. |
| `sp500_buffer_none()` | 없음 | 선택적 텐서를 사용하지 않는다고 표시합니다. |

크기가 큰 텐서와 NPU도 읽고 쓰는 텐서에는 공유 메모리 버퍼를 사용하세요.

### 스레드

모든 함수는 여러 스레드와 여러 프로세스에서 동시에 호출할 수 있습니다. 각 DSP 호출은 왕복이 끝날 때까지 모든 스레드와 프로세스가 공유하는 잠금 하나를 잡으므로, 동시 호출은 안전하지만 한 번에 하나씩 실행됩니다. 여러 호출을 하나의 원자적 단위로 묶으려면 `sp500_dsp_lock()`과 `sp500_dsp_unlock()`으로 감싸세요.

### 상태 코드

모든 호출은 `sp500_status`를 반환합니다. `SP500_OK`는 0이고 모든 오류는 음수이므로, `rc != SP500_OK`와 `rc < 0` 모두 올바른 검사입니다. `sp500_status_string()`은 코드를 문자열로 변환합니다. 출력 값은 `SP500_OK`를 반환했을 때만 유효합니다.

| 코드 | 값 | 의미 |
| --- | --- | --- |
| `SP500_OK` | 0 | 완료되었으며 출력 버퍼가 유효합니다. |
| `SP500_ERR_INVALID_ARG` | -1 | 파라미터가 없거나 범위를 벗어났거나 다른 파라미터와 맞지 않습니다. |
| `SP500_ERR_UNSUPPORTED` | -2 | 모듈이 해당 조합을 실행할 수 없습니다. |
| `SP500_ERR_BUFFER_TOO_SMALL` | -3 | 전달한 버퍼가 결과를 담기에 너무 작습니다. |
| `SP500_ERR_NOT_READY` | -4 | `sp500_dsp_open()`이 성공하지 않았습니다. |
| `SP500_ERR_NO_MEMORY` | -5 | 호스트 또는 공유 메모리 할당에 실패했습니다. |
| `SP500_ERR_TIMEOUT` | -6 | DSP가 기한 내에 응답하지 않았습니다. |
| `SP500_ERR_DSP` | -7 | DSP가 실패를 보고했습니다. |
| `SP500_ERR_INTERNAL` | -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** 릴리스는 다시 빌드하지 않고 라이브러리만 교체하면 됩니다.

라이브러리는 세 가지 이름으로 설치됩니다.

```text
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 프로그래밍 가이드](dsp_programming.md)를 참고하세요.
