Files
calculet-npu-research-archive/reports/Calculet-NPU-Runtime-API精确契约与错误恢复手册-20260802.md
T

18 KiB
Raw Blame History

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/OutputBufCalbinModel*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<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。

单次调用规则:

  1. infer 前 OutputBuf 必须属于当前 job 且不是未完成的旧 job。
  2. infer 返回非 success:不得 Wait 这个“新 job”,不得交付输出;是否可复用 buffer 由错误类别决定。
  3. infer 返回 successbuffer、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

真实入口:

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. 厂商必须补齐的接口契约

  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 的正式删除或版本归属,避免后续工程误用。