一个不依赖 SystemC、Verilator 或第三方 C++ 库的 AMBA CHI 周期级教学模型。项目重点不是复刻 完整 CHI IP,而是用可以单步运行、可以打印事务日志的代码解释三件事:
- RN-F、HN-F、SN-F 在基本读写事务中分别负责什么。
- 普通读返回和 DMT(Direct Memory Transfer)返回为什么走不同路径。
- L-Credit 为什么属于相邻链路,credit 深度又如何限制持续吞吐。
Important
本项目是 CHI 教学子集,不是协议合规模型。它没有实现完整 opcode、cache state、目录、 retry、错误响应、链路激活、低功耗和协议检查规则,不能替代 Arm CHI 规范或商业 VIP。
环境要求:
- CMake 3.20 或更高版本
- 支持 C++17 的 GCC 或 Clang
- Ninja(使用仓库预设时需要)
cmake --preset debug
cmake --build --preset debug --parallel
ctest --preset debug
./build/debug/chi_demo --scenario all只看结果和 credit 统计,不打印逐 flit 日志:
./build/debug/chi_demo --scenario all --no-trace观察深链路下不同 credit 数量的吞吐:
./build/debug/chi_demo \
--scenario credit \
--forward-latency 2 \
--return-latency 2 \
--credits 6 \
--no-trace打印初始化和运行过程中的每一个 L-Credit 脉冲:
./build/debug/chi_demo --scenario dmt --credits 2 --trace-credits模型没有把 RN-F -> HN-F -> SN-F 写成直接函数调用,而是放入一个按 TgtID 路由的
简单 Fabric。这样才能正确表达 DMT 中“数据绕过 HN-F,但仍经过互连链路”的含义。
+------------------+
| CHI Fabric |
| TgtID routing |
+----+--------+----+
| |
REQ/RSP/DAT/SNP | | REQ/RSP/DAT/SNP
+ per-channel credit | | + per-channel credit
| |
+---+--+ +--+---+
| HN-F | | SN-F |
+------+ +------+
|
| REQ/RSP/DAT/SNP
| + per-channel credit
+---+---+
| RN-F0 |
+-------+
每个节点端口实际包含两条方向相反的 link;每条 link 又包含独立的 REQ/RSP/DAT/SNP
credit counter 和接收 FIFO。一个方向或一个通道被堵住,不会直接消耗另一个方向或通道的 credit。
RN-F0 --ReadShared-------> HN-F
HN-F --ReadNoSnp-------> SN-F
HN-F <--CompData--------- SN-F
RN-F0 <--CompData--------- HN-F
RN-F0 --CompAck----------> HN-F
HN-F 为下游请求分配自己的 TxnID。SN-F 返回的 CompData.TxnID 匹配 HN-F 的事务;HN-F
再将其转换回 RN-F0 的原始 TxnID。最后 RN-F0 使用数据响应中的 HomeNID/DBID 发送
CompAck,HN-F 才释放事务上下文。
运行命令:
./build/debug/chi_demo --scenario readRN-F0 --ReadShared-------> HN-F
HN-F --ReadNoSnp-------> SN-F
RN-F0 <--CompData--------- SN-F # Data bypasses HN-F transaction handling
RN-F0 --CompAck----------> HN-F
HN-F 发出的下游 ReadNoSnp 带有 ReturnNID=RN-F0 和原请求的 ReturnTxnID。SN-F
因此把 CompData 的目标设为 RN-F0。HN-F 虽然没有接收数据,仍通过 RN-F0 的 CompAck
确认 DMT 已完成并释放资源。
运行命令:
./build/debug/chi_demo --scenario dmtRN-F0 --WriteNoSnpFull--> HN-F
RN-F0 <-CompDBIDResp------ HN-F
RN-F0 --NCBWrData--------> HN-F
HN-F --WriteNoSnpFull--> SN-F
HN-F <-CompDBIDResp------ SN-F
HN-F --NCBWrData--------> SN-F
CompDBIDResp 不只是“成功响应”,还表示接收方已经分配数据缓冲。后续 DAT flit 使用
DBID,而不是继续使用原始 REQ 的 TxnID。HN-F 必须同时拿到 RN-F0 的写数据和 SN-F
分配的 DBID,才能继续向 SN-F 发送数据。
运行命令:
./build/debug/chi_demo --scenario write可以把一个 credit 看成接收 FIFO 中一个槽位的所有权。任意稳定周期都必须满足:
发送端可用 credit
+ 正向链路中的 flit
+ 接收 FIFO 中的 flit
+ 返回链路中的 credit
+ 等待发送的初始/归还 credit
= 接收 FIFO 深度
CreditChannel::assert_credit_conservation() 每周期检查这个等式。
它能直接捕获以下错误:
- 没有 credit 仍然发送 flit;
- 一个 FIFO 槽位被重复归还;
- credit 丢失;
- 接收 FIFO 溢出;
- credit counter 超过协商上限。
发送端不是通过构造函数直接获得全部 credit。模型按链路初始化过程工作:
- 发送端 credit counter 从 0 开始。
- 接收端根据真实 FIFO 深度逐周期发放初始 L-Credit。
- L-Credit 经过返回流水后到达发送端。
- 只有 credit counter 非零时,发送端才能发出 flit。
因此日志开头出现少量 STALL 是正常的初始化现象。使用 --trace-credits 可以看到完整过程。
对于持续每周期一个 flit 的目标,第一版估算是带宽时延积:
minimum credits >= ceil(target flits/cycle * credit reuse RTT)
本模型使用严格的 communication/commit 两阶段时序,所以 credit reuse RTT 为:
forward link latency
+ 1 cycle receiver consumption
+ credit return latency
+ 1 cycle sender observation
例如正向延迟 2、返回延迟 2:
minimum credits = 2 + 1 + 2 + 1 = 6
深度小于 6 时链路仍然正确,但发送端会周期性耗尽 credit;大于 6 通常不会继续增加这个单通道 benchmark 的稳态吞吐,只会增加缓冲面积和可容纳的突发长度。
真实 RTL 还要加入接收处理阻塞、仲裁、CDC、路由拥塞和业务资源等待。最终 credit 深度应由流量模型 和最坏路径仿真确定,而不是只套公式。
每个周期包含两个逻辑阶段:
communicate/evaluate: 节点和 Fabric 观察旧状态,提出本周期动作
commit/update: 所有 link 同时移动 flit、返回 credit、更新 FIFO
如果直接按 RN.tick(); HN.tick(); SN.tick(); 修改共享队列,先调用的对象可能把消息放进去,后调用的
对象又在同周期取走,结果一个 flit 可以零延迟穿越多跳。两阶段更新让 C++ 调用顺序不会改变硬件时序。
| 路径 | 内容 |
|---|---|
include/chi/chi_types.h |
opcode、消息字段和 trace 定义 |
include/chi/credit_channel.h |
单跳 credit channel 和四通道 link |
include/chi/chi_model.h |
RN-F、HN-F、SN-F、Fabric 和系统接口 |
src/credit_channel.cpp |
credit 初始化、正反向流水和守恒检查 |
src/chi_model.cpp |
节点事务状态机与 TgtID 路由 |
src/main.cpp |
命令行场景和性能 sweep |
tests/chi_model_test.cpp |
事务路径、数据结果和吞吐回归测试 |
docs/protocol-scope.md |
教学子集边界、ID 流和建模假设 |
完整检查:
./scripts/check.sh安装 Git hook:
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/pre-commit install
.venv/bin/pre-commit run -a测试覆盖:
- 普通
ReadShared的数据必须经过 HN-F; - DMT
CompData必须由 SN-F 直接以 RN-F0 为TgtID; - DMT 完成后 RN-F0 必须向 HN-F 发送
CompAck; WriteNoSnpFull两级 DBID 和最终内存更新;- 浅 credit pool 比 RTT 大小的 pool 产生更多 stall 和更低吞吐。
协议语义与以下 Arm 公开资料交叉核对:
- Learn the architecture: Introducing AMBA CHI
- AMBA CHI Architecture Specification
- AMBA TLM 2.0 Library Reference Manual
模型的教学简化和建模假设记录在 docs/protocol-scope.md。遇到冲突时
以对应版本的 Arm 规范为准。
建议按以下顺序扩展:
- 增加第二个 RN-F、简单 cache state 和 HN-F directory。
- 实现
SnpShared/SnpResp,观察干净 sharer 的流程。 - 实现 DCT,让持有数据的 RN-F 直接向请求 RN-F 返回
CompData。 - 增加 retry credit、Protocol Credit 和资源依赖死锁测试。
- 将
ChiMessage映射为 ChiselBundle,将CreditChannel映射为计数器、流水寄存器和 Queue。
本项目采用 MIT License。AMBA、CHI 和 Arm 是其各自所有者的商标;许可证 不授予使用这些商标的额外权利。