Skip to content

Repository files navigation

CHI Simulator

一个不依赖 SystemC、Verilator 或第三方 C++ 库的 AMBA CHI 周期级教学模型。项目重点不是复刻 完整 CHI IP,而是用可以单步运行、可以打印事务日志的代码解释三件事:

  1. RN-F、HN-F、SN-F 在基本读写事务中分别负责什么。
  2. 普通读返回和 DMT(Direct Memory Transfer)返回为什么走不同路径。
  3. 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。

已实现事务

1. ReadShared,经 HN-F 返回

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 read

2. ReadShared,使用 DMT

RN-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 dmt

3. WriteNoSnpFull 和 DBID

RN-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 模型

Token 守恒

可以把一个 credit 看成接收 FIFO 中一个槽位的所有权。任意稳定周期都必须满足:

发送端可用 credit
+ 正向链路中的 flit
+ 接收 FIFO 中的 flit
+ 返回链路中的 credit
+ 等待发送的初始/归还 credit
= 接收 FIFO 深度

CreditChannel::assert_credit_conservation() 每周期检查这个等式。 它能直接捕获以下错误:

  • 没有 credit 仍然发送 flit;
  • 一个 FIFO 槽位被重复归还;
  • credit 丢失;
  • 接收 FIFO 溢出;
  • credit counter 超过协商上限。

初始化

发送端不是通过构造函数直接获得全部 credit。模型按链路初始化过程工作:

  1. 发送端 credit counter 从 0 开始。
  2. 接收端根据真实 FIFO 深度逐周期发放初始 L-Credit。
  3. L-Credit 经过返回流水后到达发送端。
  4. 只有 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 公开资料交叉核对:

模型的教学简化和建模假设记录在 docs/protocol-scope.md。遇到冲突时 以对应版本的 Arm 规范为准。

后续路线

建议按以下顺序扩展:

  1. 增加第二个 RN-F、简单 cache state 和 HN-F directory。
  2. 实现 SnpShared/SnpResp,观察干净 sharer 的流程。
  3. 实现 DCT,让持有数据的 RN-F 直接向请求 RN-F 返回 CompData
  4. 增加 retry credit、Protocol Credit 和资源依赖死锁测试。
  5. ChiMessage 映射为 Chisel Bundle,将 CreditChannel 映射为计数器、流水寄存器和 Queue。

License

本项目采用 MIT License。AMBA、CHI 和 Arm 是其各自所有者的商标;许可证 不授予使用这些商标的额外权利。

About

A cycle-accurate AMBA CHI instructional model with no dependencies on SystemC, Verilator, or external C++ libraries.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages