.pccx 파일 포맷 명세¶
버전: 0.2 (포맷 메이저: 0x01, 마이너: 0x01)
상태: 활성 (Active)
진실의 원본 (Source of Truth):src/core/src/pccx_format.rs
개요¶
.pccx 바이너리 포맷은 pccx-lab이 생성하는 NPU 프로파일링 트레이스,
하드웨어 설정, 세션 메타데이터를 저장하는 공식 컨테이너 형식입니다.
설계 목표:
Zero-copy IPC: 페이로드는 재인코딩 없이 WebGL
ArrayBuffer에 직접 메모리 매핑할 수 있는 원시 바이트 블롭입니다.자기 서술적 (Self-describing): JSON 헤더가 외부 스키마 파일 없이 페이로드를 디코딩하는 데 필요한 모든 메타데이터를 포함합니다.
버전 관리: 메이저/마이너 버전 바이트로 하위 호환성을 유지하며 발전합니다.
무결성 검증: 선택적 FNV-1a 64비트 체크섬으로 빠른 훼손(tamper) 감지를 지원합니다.
바이너리 레이아웃¶
여러 바이트 정수는 특별한 언급이 없는 한 **리틀 엔디언(little-endian)**입니다.
오프셋 |
크기 |
필드 |
설명 |
|---|---|---|---|
0 |
4 |
매직 넘버 |
|
4 |
1 |
메이저 버전 |
호환 불가 변경 카운터 (현재 |
5 |
1 |
마이너 버전 |
추가적 변경 카운터 (현재 |
6 |
2 |
예약 (Reserved) |
|
8 |
8 |
헤더 길이 |
|
16 |
N |
JSON 헤더 |
UTF-8 JSON 객체 (아래 참조) |
16 + N |
M |
바이너리 페이로드 |
|
호환성 규칙: 파서는 메이저 버전이 기대값과 다를 경우 반드시 오류를 반환해야 합니다. 마이너 버전은 어떤 값도 허용하며, 알 수 없는 필드는 추가적 확장으로 처리해야 합니다.
JSON 헤더 필드¶
{
"pccx_lab_version": "v0.4.0-contention-aware", // 문자열
"format_minor": 1, // u8
"arch": {
"mac_dims": [32, 32], // 시스톨릭 MAC 어레이의 [행, 열]
"isa_version": "1.1", // 문자열
"peak_tops": 2.05 // f64, 이론적 최대 성능 (참고용)
},
"trace": {
"cycles": 12345678, // u64 — 총 시뮬레이션 사이클
"cores": 32, // u32 — 활성 NPU 코어 수
"clock_mhz": 1000 // u32 — 트레이스 생성 시 사용된 클록 주파수
},
"payload": {
"encoding": "bincode", // "bincode" | "flatbuf" | "raw"
"byte_length": 4096000, // u64 — 페이로드의 정확한 바이트 수
"checksum_fnv64": "0xcbf29ce4842223" // 16진수 문자열 | null — FNV-1a 64비트
}
}
페이로드 인코딩 목록¶
값 |
설명 |
|---|---|
|
Rust |
|
24바이트 패킹 구조체 배열 (아래 플랫 버퍼 레이아웃 참조) |
|
아키텍처별 원시 바이트 (비표준) |
플랫 버퍼 레이아웃 ("flatbuf" 인코딩)¶
각 이벤트는 24바이트이며 모든 필드는 리틀 엔디언입니다:
오프셋 |
크기 |
타입 |
필드 |
|---|---|---|---|
0 |
4 |
u32 |
|
4 |
8 |
u64 |
|
12 |
8 |
u64 |
|
20 |
4 |
u32 |
|
이벤트 타입 ID¶
ID |
이름 |
설명 |
|---|---|---|
0 |
|
알 수 없는 이벤트 |
1 |
|
MAC 어레이 연산 실행 |
2 |
|
AXI DMA 읽기 |
3 |
|
AXI DMA 쓰기 |
4 |
|
시스톨릭 파이프라인 드레인 스톨 |
5 |
|
코어 간 동기화 배리어 |
의사 코드 (Rust)¶
// 읽기
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
assert_eq!(&magic, b"PCCX");
let mut version = [0u8; 2];
reader.read_exact(&mut version)?;
let major = version[0]; // MAJOR_VERSION (0x01)과 일치해야 함
let minor = version[1]; // 어떤 값도 허용
let mut reserved = [0u8; 2];
reader.read_exact(&mut reserved)?;
let mut hlen_buf = [0u8; 8];
reader.read_exact(&mut hlen_buf)?;
let header_len = u64::from_le_bytes(hlen_buf);
let mut json_bytes = vec![0u8; header_len as usize];
reader.read_exact(&mut json_bytes)?;
let header: PccxHeader = serde_json::from_slice(&json_bytes)?;
let mut payload = vec![0u8; header.payload.byte_length as usize];
reader.read_exact(&mut payload)?;
버전 관리 정책¶
메이저 버전은 레이아웃 호환 불가 변경 시 증가합니다 (예: 예약 필드 크기 변경, 헤더 필드 삭제). 파서는 불일치 시 오류를 반환해야 합니다.
마이너 버전은 추가적 변경 시 증가합니다 (새 선택적 헤더 필드, 새 이벤트 타입 ID). 파서는 알 수 없는 필드를 무시해야 합니다.
pccx_lab_version문자열은 참고용이며 파싱에 영향을 주지 않습니다.
무결성 검증¶
선택적 checksum_fnv64 필드는 원시 페이로드 바이트의 FNV-1a 64비트 해시를 저장합니다.
파서는 이를 사용하여 우발적 손상을 감지할 수 있습니다:
fn fnv1a_64(data: &[u8]) -> u64 {
const BASIS: u64 = 0xcbf29ce484222325;
const PRIME: u64 = 0x00000100000001b3;
data.iter().fold(BASIS, |h, &b| (h ^ b as u64).wrapping_mul(PRIME))
}
불일치 시 기본적으로 경고를 출력하지만 치명적 오류로 처리하지는 않습니다 (설정 가능).