# Calculet NPU Runtime API 精确契约与错误恢复手册 日期:2026-08-02 真实 ABI 基线:归档的 CalRT 0.7.6 headers + `libcalrt-linux-x86_64.so.0.7.6` ## 1. 使用规则 1. 编译和实现以 0.7.6 头文件为准;PDF 用来解释概念,不能覆盖真实签名。 2. 所有 C++ `CalrtError_e` 都必须检查;`void` API 不能假设成功,需通过后续 status/health 验证。 3. `unique_ptr` 拥有 Calbin/VirtualDevice/InputBuf/OutputBuf;`CalbinModel*` 和 `CalrtTensor*` 是借用指针,不得跨所属对象生命周期。 4. 当前 `VirtualDevice` 只支持一个物理设备;per-chip 参数不代表多卡。 5. reset、任意寄存器/内存读写不属于普通业务 API。 ## 2. PDF 与 SDK 关键不兼容 | 主题 | PDF 写法 | 0.7.6 真实写法 | 实现结论 | | --- | --- | --- | --- | | 创建解析器 | 第 7 页示例为 `CreateParser`,第 15 页 API 参考为 `CreateCalbin` | `Calbin::CreateCalbin(const std::string &)` | PDF 内部版本漂移;0.7.6 只能使用后者 | | 全部模型 | 返回 vector | `std::vector *GetAllModels()` | 返回借用指针,判空并限制生命周期 | | 按名找模型 | `GetModelInfo(name)` | `GetModelByName(std::string_view)` | exact name | | infer 返回值 | 文档示例为 void | C++ 返回 `CalrtError_e` | 必须检查 | | CSR setter | 第 28 页写作 `void SetCsrByName(...)` | 返回 `CalrtError_e` | 0.7.6 必须检查返回码 | | VirtualDevice 物理设备数 | 第 29 页泛称 one or multiple | header 注释明确临时只支持一个,成员为单个 device | 多物理板保持 C4/C3 | | tensor 重定位 | `RelocateTensorAddress` | header 和 SO 无接口 | C4,不得调用 | | 设备内存分配 | `AllocDevMem` | 无同名公共接口 | C4,不用底层 `CreateBuf` 冒充 | ## 3. C++ 生命周期接口 ### 3.1 创建、配置和销毁 真实签名: ```cpp static std::unique_ptr calrt::VirtualDevice::CreateVDevice(calrt::CalrtDeviceType_e type); static std::unique_ptr calrt::Calbin::CreateCalbin(const std::string &path); calrt::CalrtError_e calrt::configure(calrt::VirtualDevice *vdev, calrt::Calbin *calbin, bool dumpIni = false); ``` 调用契约: | 输入 | 前置条件 | 输出/后置条件 | 失败处理 | | --- | --- | --- | --- | | `type=PCIE` | driver/设备存在 | 返回非空 vdev,随后 `Status()==Success` | 判空;非 success 停止启动 | | calbin path | 受信目录、目录完整、hash 已验证 | 返回 parser,内部模型借用指针有效 | 捕获异常/判空;不得 configure | | configure | vdev/calbin 非空,设备未被其他 generation 使用 | 参数/命令/内存 reservation 部署完成 | 按错误分类回滚;不接流 | | `dumpIni` | 仅诊断环境 | 可能写出地址/路径 | 生产默认 false,输出目录受控 | 销毁顺序必须是:停止 admission -> 等待所有 in-flight -> 销毁 buffer/KV manager -> reset configuration(仅按厂商契约)-> 销毁 Calbin -> Shutdown/Release vdev。不能先销毁 vdev 再让 OutputBuf 析构。 ### 3.2 最小启动代码 ```cpp StatusOr open_runtime(const std::string &calbin_dir) { auto vdev = calrt::VirtualDevice::CreateVDevice(calrt::PCIE); if (!vdev) return Status::Unavailable("CreateVDevice returned null"); CAL_RETURN_IF_ERROR(check_rc(vdev->Status(), "vdev.Status")); auto calbin = calrt::Calbin::CreateCalbin(calbin_dir); if (!calbin) return Status::InvalidArtifact("CreateCalbin returned null"); CAL_RETURN_IF_ERROR(validate_calbin(*calbin)); CAL_RETURN_IF_ERROR(check_rc(calrt::configure(vdev.get(), calbin.get(), false), "configure")); CAL_RETURN_IF_ERROR(check_rc(vdev->Status(), "vdev.Status.after_configure")); return RuntimeObjects{std::move(vdev), std::move(calbin)}; } ``` `CAL_RETURN_IF_ERROR` 是我方包装,不是 SDK API。 ## 4. Calbin 查询精确契约 真实接口: ```cpp CalrtCalbin &GetCalbinBrief(); CalbinModel *GetModelByName(std::string_view modelName); std::vector *GetAllModels(); std::vector GetModelByType(std::string_view type); std::vector &GetPairedElf(const std::string &, const std::vector &); CalbinModel &GetGlobalMemInfo(); const CalbinLLM_s &GetLLMInfo() const; void Report(); CalbinSectionPlace_e GetStackLoc(const std::string &modelName); const std::string &Version() const; uint64_t GetModelWorloadByName(const std::string &modelName); std::vector GetGoldenInputByModelName(const std::string &modelName); std::vector GetGoldenOutputByModelName(const std::string &modelName); ``` 字段/生命周期规则: - `GetAllModels()` 返回的 vector 由 Calbin 所有,禁止 delete,禁止在 Calbin 销毁后使用。 - `GetModelByName()` 返回借用指针;必须 exact match,null 是正常失败结果。 - `GetModelByType("prefill"/"decode"/"kv_update")` 只能用于发现,metadata 已知存在错误时不能取代 manifest。 - `GetLLMInfo()` 引用只在 Calbin 生命周期内有效;启动时复制必要的标量到 immutable generation。 - `GetGolden*()` 返回值由调用方拥有;空 vector 既可能是“无 golden”也可能是空数据,需结合产物 manifest 判定。 - `GetModelWorloadByName` 的拼写和单位需厂商确认,不能直接作为计费/容量口径。 启动校验至少断言: ```text models != nullptr expected exact model count and names llm.max_batch_size == service expected batch llm.max_seq_len == manifest max_seq each required input/output/CSR exists calbin Version is compatible with runtime/driver/firmware golden hash agrees with signed manifest, when golden is required ``` ## 5. Buffer 和 tensor 精确契约 ### 5.1 创建 ```cpp std::unique_ptr calrt::createInputBuf(const CalbinModel &model); std::unique_ptr calrt::createOutputBuf(const CalbinModel &model); ``` 创建后检查非空、`Name()`、`GetTensorNum()` 和 required tensor 集。对象不可复制;OutputBuf 也不可 move,所以 pool 应保存 `unique_ptr`,不把 OutputBuf 作为值移动。 ### 5.2 tensor 获取与映射 ```cpp CalrtTensor *CalrtInputBuf::GetTensorByName(const std::string &); CalrtTensor *CalrtOutputBuf::GetTensorByName(const std::string &); CalrtError_e CalrtInputBuf::SliceTensorByName(void *src, const std::string &, uint64_t offset, uint64_t size); CalrtError_e CalrtOutputBuf::SliceTensorByName(void *src, const std::string &, uint64_t offset, uint64_t size); CalrtError_e ResetTensorByName(const std::string &); void ResetAllTensors(); ``` 生产代码当前也直接使用 `CalrtTensor::MapBuf()` 和 `SliceTensor()`。安全包装必须保存:原始 ByteSize、当前 slice offset/size、host pointer、映射 generation。任何 slice 需满足: ```text offset <= byte_size size <= byte_size - offset size > 0 host backing capacity >= size offset and size satisfy Runtime alignment requirement (厂商确认) ``` 归还 buffer pool 前调用 `ResetAllTensors()`;若使用 tensor 级 `SliceTensor()`,同时调用匹配的 `UndoSlice()`。两套 slice API 不混用,除非 runner 证明 reset 能完全恢复。 ### 5.3 CSR ```cpp uint32_t ModelHyperParameters_s::GetCsrValueByName(const std::string &csrName); CalrtError_e ModelHyperParameters_s::SetCsrByName(const std::string &csrName, uint32_t value); ``` 当前图两个 CSR: | 名称 | 当前代码值 | 业务含义 | 检查 | | --- | --- | --- | --- | | `cur_seq_len[0]` | 当前 ubatch token 数 | 本次有效 token 数 | `1..max_seq` | | `past_kv_cur_seq_len[0]` | `past_len + cur_seq_len` | 推理后逻辑总长度 | `<=max_seq` | `GetCsrValueByName()` 用 0 表示错误,因此无法区分合法 0 和失败;只用于测试回读,不能作为错误判定主路径。setter 返回值才是主契约。 ## 6. 推理和完成精确契约 真实接口: ```cpp CalrtError_e calrt::infer(VirtualDevice *, CalbinModel *, CalrtInputBuf *, CalrtOutputBuf *); CalrtError_e calrt::infer_with_fixed_task_type( VirtualDevice *, CalbinModel *, CalrtInputBuf *, CalrtOutputBuf *, TaskType_e); CalrtError_e CalrtOutputBuf::Wait(); CalrtOutputBufStatus_e CalrtOutputBuf::GetStatus(); float CalrtInputBuf::GetInputTransferTime(); float CalrtOutputBuf::GetWaitTime(); float CalrtOutputBuf::GetOutputTransferTime(); ``` 状态枚举:`DEFAULT -> PENDING -> RUNNING -> DONE`,异常终态为 `DONE_CCU_EXCEPTION`。头文件只说明语义,未保证每次轮询都观察到中间状态;产品不能依赖一定出现 PENDING/RUNNING。 单次调用规则: 1. infer 前 OutputBuf 必须属于当前 job 且不是未完成的旧 job。 2. infer 返回非 success:不得 Wait 这个“新 job”,不得交付输出;是否可复用 buffer 由错误类别决定。 3. infer 返回 success:buffer、model、vdev、host memory 在 Wait 终止前全部保持有效。 4. Wait success 后仍检查 `GetStatus()!=DONE_CCU_EXCEPTION`。 5. 只有完成 D2H、shape/size/数值检查后才交付结果和提交 KV。 6. `GetWaitTime()` 是 SDK 计时,不等同端到端 infer;单位虽注释/当前使用为 ms,仍应由 runner 校准。 ## 7. PING/PONG 和 parallel mode 真实入口: ```cpp void VirtualDevice::EnableParallelMode(bool switch_on); void VirtualDevice::SubmitJob(CalbinModel *, CalrtInputBuf *, CalrtOutputBuf *, TaskType_e forceEngineMode = UNDEFINED_TASK); ``` 头文件注释只给出 `0=ping, 1=pong, -1=auto`。它没有给出: - 最大 in-flight; - 是否线程安全; - 同一 OutputBuf 能否连续提交; - H2D/compute/D2H 的重叠范围; - 两个 engine 的顺序、公平性和异常隔离; - parallel mode 开关时对在途任务的影响。 因此现状为 C1。隔离 runner 必须用两套完全独立 buffer/backing storage,强制 Ping/Pong,覆盖四种序列:P-P、Q-Q、P-Q、Q-P(P/Q 代表不同输入/seq),验证输出 hash、KV、状态和队列计数。未通过前产品使用普通 `infer()` 且 in-flight=1。 ## 8. VirtualDevice/Device 的可用面 | 接口 | 等级 | 产品用途 | 禁止事项 | | --- | --- | --- | --- | | `Status()` | C0 | 启动/健康检查 | 不能替代实际 golden | | `GetDeviceInfo(idx)` | C1 | 净化后版本信息 | idx 不证明多物理设备 | | `EnableTraceDevice(bool)` | C1 | 受控性能实验 | 不永久开启;限制磁盘 | | `ReadMem/WriteMem(...,chipId)` | C4 特权 | 厂商/故障诊断 | 不给普通 API 任意地址 | | `ResetCCU()` | C4 特权 | 明确 CCU 故障恢复 | 不在普通请求失败时调用 | | `ResetConfiguration()` | C4 特权 | drain 后卸载 generation | 未知影响范围前不自动化 | | `Reset()` | C4 特权 | 最后级设备恢复 | 全审计、限频、防 reset storm | | `Release()/Shutdown()` | C1 | 有界退出 | 不在 in-flight 时直接释放 | 0.7.6 header 明确写着临时仅支持一个物理设备,成员也是单个 `shared_ptr`。`GetDeviceInfo(size_t idx)` 不得作为多卡枚举 API 使用。 ## 9. KV Manager 精确契约 ```cpp bool canAllocate(size_t seq_num); void Allocate(KvSeqInfo &seq_info); void Free(KvSeqId seq_id); void RemoveTokensAtEnd(KvSeqId seq_id, size_t n_tokens); bool isExist(KvSeqId seq_id); bool Apply(KvSeqInfo &seq_info); void DoShift(KvBatchMode_e mode, std::vector &seqs); KvPos GetSeqLen(KvSeqId seq_id) const; void Clear(); KvPos seqPosMin(KvSeqId seq_id) const; KvPos seqPosMax(KvSeqId seq_id) const; ``` 已知限制: - `Allocate/Free/Remove/DoShift/Clear` 返回 void,失败传播语义不清晰。 - `Apply` 只有 bool,不提供错误码。 - `KvPos/KvSeqId` 为 int32,服务入口需限制范围。 - batch>1 的 K/V 布局不同;header 的 layout 说明不能替代 batch calbin。 - `KvBatchMode::ALL` 注释指向 16 lanes 和阈值建议,但不等于任意 B4/B8/B16 已可用。 - S8 明确只在字段说明中提到 V-cache 支持,具体 K/V 压缩范围和误差仍需厂商确认。 适配层最小支持表: | llama KV 语义 | 当前实现 | 对外策略 | | --- | --- | --- | | clear | 委托 `Clear()` | C0,补恢复测试 | | remove tail (`p1=-1`) | `RemoveTokensAtEnd` | C0 部分能力,补边界测试 | | arbitrary remove | 返回 false | 明确不支持 | | copy sequence | 空实现 | 禁用 prompt/KV copy | | keep only sequence | 空实现 | 禁用 | | add/div positions | 空实现 | 禁用 context shift 相关路径 | | state save/load | 空实现 | 禁止宣称 session state 支持 | | min/max | 委托 Runtime | C1,覆盖不存在/空/满场景 | ## 10. C API 精确签名和使用场景 C API 提供 `create_calbin/create_device/configure_device/create_*_buffer`、tensor 查询/复制、`block_infer_model/non_block_infer_model/wait_infer_done` 和 release。关键差异是两个 infer 提交函数返回 `void`。 因此: - 新 C++ 产品路径继续优先使用 C++ API,以获得 submit 错误码。 - C API 只适合兼容/ABI runner,必须在每步检查可检查的返回值并在 infer 后检查 wait/status/health。 - `set_csr_by_name` 也是 void,无法替代 C++ CSR setter 的错误处理。 - `cal_copy_mem` 需要调用方保证 tensor 方向;优先使用按 tensor name 的安全复制接口。 - 所有 `release_*` 只接收 handle,没有幂等保证;包装层释放后立即清空自己的 handle。 ## 11. 全部错误码与动作 | 值 | 名称 | 可重试 | 污染范围默认值 | 推荐动作 | | --- | --- | --- | --- | --- | | 0 | Success | - | 无 | 继续 | | 1 | MemoryAllocation | 条件性 | job/generation | 降低 admission;持续则摘模型 | | 2 | MemoryAlreadyRegistered | 否 | generation | 生命周期缺陷,drain | | 3 | Assert | 否 | 未知 | 停止提交,保留证据 | | 4 | InvalidValue | 否 | job | 拒绝输入/修代码 | | 5 | InvalidConfiguration | 否 | generation | 回滚产物 | | 6 | NoDevice | 条件性 | device | 503,等待设备/重启实例 | | 7 | AlreadyConfigured | 否 | generation | 串行化 load/unload | | 8 | IncompatibleDriver | 否 | instance | 升级 driver | | 9 | RequireNewerRT | 否 | instance | 升级 Runtime | | 10 | FileNotFound | 否 | artifact | 修复包/路径 | | 11 | BrokenFile | 否 | artifact | hash/完整性失败 | | 12 | InvalidELF | 否 | artifact | 编译器/兼容矩阵 | | 13 | Timeout | 条件性 | submitted job/可能 generation | 停止 admission,按恢复流程 | | 14 | OutOfRange | 否 | job | 本地边界应先拦截 | | 15 | NotExpected | 否 | 未知 | 保守 drain | | 16 | MissConfiguration | 否 | generation | 重新 configure/warmup | | 17 | DeviceBusy | 是,有界 | job/queue | backoff,不立刻 reset | | 18 | DeviceUnavailable | 条件性 | device | 摘流、恢复 | | 19 | InvalidCalbin | 否 | artifact | 回滚旧 generation | | 20 | Unknown | 否 | 未知 | 保守 drain,厂商分析 | | 21 | InvalidDataType | 否 | artifact/job | manifest mismatch | | 22 | InvalidDataShape | 否 | artifact/job | manifest mismatch | | 23 | InvalidLibDep | 否 | instance/artifact | 修复依赖 | | 24 | DeviceMapBroken | 否 | device | reset/restart | | 25 | FailedSoftReset | 否 | device | 进程/设备级恢复 | | 26 | FailedConnectServer | 条件性 | instance | 外部服务/EMU 路径诊断 | | 27 | DeviceUnsupport | 否 | feature | 关闭特性 | | 28 | DeviceCrash | 否 | device/generation | CCU fault 恢复 | | 29 | WrongDirection | 否 | job/programming | 修正 copy 方向 | “污染范围默认值”是我方保守策略,不是厂商保证。拿到官方 rollback/reset 语义后才可缩小。 ## 12. 对外服务 API ### 12.1 普通推理面 ```text POST /v1/inference request_id, model_alias, prompt/tokens, max_new_tokens, sampling parameters, deadline_ms, stream DELETE /v1/inference/{request_id} 只请求取消,不承诺立即停止硬件 GET /v1/models alias, generation, context_limit, ready state ``` 不得返回 calbin 路径、tensor 地址、寄存器、chip raw memory、内部子图名或未净化的 Runtime exception。 ### 12.2 观测面 ```text GET /internal/health GET /internal/runtime/version GET /internal/models/{alias}/manifest GET /metrics POST /internal/trace/start (鉴权、时长和大小限制) POST /internal/trace/stop ``` manifest 输出 hash、batch/context、dtype/shape、generation 和兼容版本,不输出绝对源路径/地址。 ### 12.3 特权控制面 reset、configure、unload、原始内存/寄存器访问放在独立管理进程或 Unix socket;mTLS/RBAC、双人审批(生产环境)、完整审计、速率限制。默认不提供任意 `ReadMem/WriteMem` HTTP 代理。 ## 13. API runner 测试矩阵 | 组 | 用例 | 通过条件 | | --- | --- | --- | | 生命周期 | 正常 load/configure/release 100 次 | 无泄漏、无 AlreadyConfigured | | calbin | 缺文件、坏 ELF、错版本、错 hash | 在接流前失败,错误分类正确 | | tensor | 缺名、错 dtype/rank/shape、零 size、越界 slice | 无设备提交、无越界 | | CSR | 正常边界、未知名、总长溢出 | setter/本地校验可见失败 | | infer | normal、submit busy/unavailable、Wait timeout/crash | job 状态和恢复符合表格 | | status | 快速 DONE、PENDING->RUNNING->DONE、CCU exception | 不依赖必现中间态,异常不交付 | | parallel | Auto/Ping/Pong 两任务排列 | 无串扰;未证明时不启用产品 | | KV | allocate/apply/free/remove/clear、容量耗尽 | 无忙等,长度/资源可回收 | | reset | CCU/config/device 分级恢复 | 厂商契约一致、warmup 后才 Ready | | 安全 | 未授权 reset/read/write/trace | 403/拒绝且有审计 | ## 14. 厂商必须补齐的接口契约 1. 0.7.6 对应的正式 API 文档和完整 error-to-recovery 表。 2. `Wait()` timeout 行为、是否可取消、底层 DMA 完成判定。 3. OutputBuf status 原子性、状态转换和复用前置条件。 4. parallel/PING/PONG 时序、最大 in-flight、线程安全与关闭语义。 5. KV Apply 的 bool 失败原因和 submit/Wait 失败后的 KV 一致性。 6. 三种 reset 对在途任务、KV、参数、另一芯粒和 host mapping 的精确影响。 7. trace/counter 的 schema、单位、时钟域、开销和清零规则。 8. PDF-only API 的正式删除或版本归属,避免后续工程误用。