18 KiB
Calculet NPU Runtime API 精确契约与错误恢复手册
日期:2026-08-02
真实 ABI 基线:归档的 CalRT 0.7.6 headers + libcalrt-linux-x86_64.so.0.7.6
1. 使用规则
- 编译和实现以 0.7.6 头文件为准;PDF 用来解释概念,不能覆盖真实签名。
- 所有 C++
CalrtError_e都必须检查;voidAPI 不能假设成功,需通过后续 status/health 验证。 unique_ptr拥有 Calbin/VirtualDevice/InputBuf/OutputBuf;CalbinModel*和CalrtTensor*是借用指针,不得跨所属对象生命周期。- 当前
VirtualDevice只支持一个物理设备;per-chip 参数不代表多卡。 - 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<CalbinModel> *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 创建、配置和销毁
真实签名:
static std::unique_ptr<calrt::VirtualDevice>
calrt::VirtualDevice::CreateVDevice(calrt::CalrtDeviceType_e type);
static std::unique_ptr<calrt::Calbin>
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 最小启动代码
StatusOr<RuntimeObjects> 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 查询精确契约
真实接口:
CalrtCalbin &GetCalbinBrief();
CalbinModel *GetModelByName(std::string_view modelName);
std::vector<CalbinModel> *GetAllModels();
std::vector<CalbinModel *> GetModelByType(std::string_view type);
std::vector<Parsed_Elf_s> &GetPairedElf(const std::string &, const std::vector<uint32_t> &);
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<uint8_t> GetGoldenInputByModelName(const std::string &modelName);
std::vector<uint8_t> 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的拼写和单位需厂商确认,不能直接作为计费/容量口径。
启动校验至少断言:
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 创建
std::unique_ptr<calrt::CalrtInputBuf> calrt::createInputBuf(const CalbinModel &model);
std::unique_ptr<calrt::CalrtOutputBuf> calrt::createOutputBuf(const CalbinModel &model);
创建后检查非空、Name()、GetTensorNum() 和 required tensor 集。对象不可复制;OutputBuf 也不可 move,所以 pool 应保存 unique_ptr<BufferSlot>,不把 OutputBuf 作为值移动。
5.2 tensor 获取与映射
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 需满足:
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
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. 推理和完成精确契约
真实接口:
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。
单次调用规则:
- infer 前 OutputBuf 必须属于当前 job 且不是未完成的旧 job。
- infer 返回非 success:不得 Wait 这个“新 job”,不得交付输出;是否可复用 buffer 由错误类别决定。
- infer 返回 success:buffer、model、vdev、host memory 在 Wait 终止前全部保持有效。
- Wait success 后仍检查
GetStatus()!=DONE_CCU_EXCEPTION。 - 只有完成 D2H、shape/size/数值检查后才交付结果和提交 KV。
GetWaitTime()是 SDK 计时,不等同端到端 infer;单位虽注释/当前使用为 ms,仍应由 runner 校准。
7. PING/PONG 和 parallel mode
真实入口:
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<CalrtDevice>。GetDeviceInfo(size_t idx) 不得作为多卡枚举 API 使用。
9. KV Manager 精确契约
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<CalrtSeqShift> &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 普通推理面
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 观测面
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. 厂商必须补齐的接口契约
- 0.7.6 对应的正式 API 文档和完整 error-to-recovery 表。
Wait()timeout 行为、是否可取消、底层 DMA 完成判定。- OutputBuf status 原子性、状态转换和复用前置条件。
- parallel/PING/PONG 时序、最大 in-flight、线程安全与关闭语义。
- KV Apply 的 bool 失败原因和 submit/Wait 失败后的 KV 一致性。
- 三种 reset 对在途任务、KV、参数、另一芯粒和 host mapping 的精确影响。
- trace/counter 的 schema、单位、时钟域、开销和清零规则。
- PDF-only API 的正式删除或版本归属,避免后续工程误用。