贡献者: DiVo Gen²AI RVDon 团队
许可: Apache 2.0 (VX_mem_axi_bridge.sv), 本文档
目标读者: 想给 Vortex GPGPU 接入真实 DDR4/5 控制器的 SoC 开发者
Vortex GPGPU 默认使用 DPI-C 行为级 DRAM 模型(DramSim),只能用于仿真,无法流片。真实芯片需要物理 DDR 控制器(DDR4/5 Memory Controller + PHY),但没有开源文档解释如何连接。
本指南填补此空白。基于我们在白杨 DDR4 控制器上的成功集成经验,提供通用连接方法,适用于任何 AXI4 接口的 DDR 控制器。
┌──────────────────────────────────────────────────────┐
│ Vortex GPGPU │
│ │
│ Core0 ... CoreN → L1$ → L2$ → L3$ │
│ │ │
│ mem_req/rsp arrays │
│ (VX_MEM_PORTS 组) │
└──────────────────────────────────────┬───────────────┘
│
┌──────────────────────────────────────▼───────────────┐
│ VX_mem_data_adapter │
│ 数据宽度适配 (L3_LINE → MEM_DATA) │
└──────────────────────────────────────┬───────────────┘
│
┌──────────────────────────────────────▼───────────────┐
│ VX_mem_bank_adapter │
│ Bank 映射 (PORTS → BANKS) │
└──────────────────────────────────────┬───────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────────▼────────┐ ┌───────▼────────┐ ┌───────▼────────┐
│ DPI-C DramSim │ │ 本指南的桥接层 │ │ 未来的控制器 │
│ (行为级, 默认) │ │ │ │ │
└─────────────────┘ └───────┬────────┘ └────────────────┘
│
┌────────────▼────────────┐
│ VX_mem_axi_bridge.sv │
│ mem_req/rsp → AXI4 │
│ (Apache 2.0, 通用IP) │
└────────────┬────────────┘
│ 标准 AXI4 Master
┌────────────▼────────────┐
│ 用户的 DDR 控制器 │
│ wrapper (宽度适配 + │
│ 初始化 + PHY 连接) │
└────────────┬────────────┘
│
┌────────────▼────────────┐
│ DDR4/5 Memory Controller │
│ (白杨 / Cadence / │
│ Synopsys / 自研) │
└────────────┬────────────┘
│ DFI
┌────────────▼────────────┐
│ DDR4/5 PHY │
│ (TinyPHY / 商业 PHY) │
└──────────────────────────┘
核心桥接层只有 2 步:
mem_req/rsp 信号数组→ VX_mem_axi_bridge →标准 AXI4- 用户自己写的
DDR 控制器 wrapper处理宽度适配和初始化
Vortex 内部使用简单的请求/响应握手协议:
| 信号 | 方向 | 描述 |
|---|---|---|
mem_req_valid[N] |
Input | 通道 N 有有效请求 |
mem_req_rw[N] |
Input | 0=读, 1=写 |
mem_req_addr[N] |
Input | 字地址 (word-addressable) |
mem_req_byteen[N] |
Input | 字节使能 (data_width/8 位) |
mem_req_data[N] |
Input | 写数据 |
mem_req_tag[N] |
Input | 事务标签 (uuid + 序列号) |
mem_req_ready[N] |
Output | 通道 N 可以接受请求 |
mem_rsp_valid[N] |
Output | 通道 N 有响应 |
mem_rsp_data[N] |
Output | 读数据 |
mem_rsp_tag[N] |
Output | 事务标签 (回传) |
mem_rsp_ready[N] |
Input | 通道 N 可以接受响应 |
关键特性:
- 独立的读写通道(请求和响应可以流水线执行)
- 地址是 word-addressable(1 word = DATA_WIDTH/8 bytes)
- TAG 包含 UUID (用于追踪事务来源) + 序列号
VX_mem_axi_bridge 输出标准 AXI4 Master 接口,5 个通道:
- AW (写地址) + W (写数据) + B (写响应)
- AR (读地址) + R (读数据)
| 参数 | 默认值 | 描述 |
|---|---|---|
NUM_PORTS |
1 | Vortex 内存端口数 (通常 = VX_MEM_PORTS) |
DATA_WIDTH |
512 | 总线数据宽度 (位) |
ADDR_WIDTH |
26 | 字地址宽度 (2^26 × DATA_SIZE 字节地址空间) |
TAG_WIDTH |
UUID_WIDTH+1 | 事务标签宽度 |
NUM_BANKS |
1 | AXI4 输出 bank 数 (支持多 bank 并行) |
INTERLEAVE |
0 | Bank 交错: 0=顺序, 1=交错 |
// 在 rtlsim_shim.sv 或你的平台模块中:
VX_mem_axi_bridge #(
.NUM_PORTS (VX_MEM_PORTS),
.DATA_WIDTH (`VX_CFG_L3_LINE_SIZE * 8),
.ADDR_WIDTH (VX_MEM_ADDR_WIDTH)
) bridge (
.clk (clk),
.reset (reset),
// Vortex 侧 — 来自 VX_mem_bank_adapter 的输出
.mem_req_valid (yq_mem_req_valid),
.mem_req_rw (yq_mem_req_rw),
.mem_req_byteen(yq_mem_req_byteen),
.mem_req_addr (yq_mem_req_addr),
.mem_req_data (yq_mem_req_data),
.mem_req_tag (yq_mem_req_tag),
.mem_req_ready (yq_mem_req_ready),
.mem_rsp_valid (yq_mem_rsp_valid),
.mem_rsp_data (yq_mem_rsp_data),
.mem_rsp_tag (yq_mem_rsp_tag),
.mem_rsp_ready (yq_mem_rsp_ready),
// AXI4 侧 — 连接你的 DDR 控制器 wrapper
.m_axi_awvalid (axi_awvalid),
.m_axi_awready (axi_awready),
.m_axi_awaddr (axi_awaddr),
// ... 其他 AXI4 信号
);如果 DDR 控制器的 AXI4 数据宽度 = Vortex 的数据宽度(常见于 512-bit DDR4 控制器),无需宽度适配:
VX_mem_axi_bridge (512-bit AXI4)
→ 你的 DDR 控制器 (512-bit AXI4 Slave)
→ DDR4/5 PHY
示例:
// 假设 mc_top 是 512-bit AXI4 Slave
mc_top u_mc (
.io_awio_awvalid (axi_awvalid[0]),
.io_awio_awready (axi_awready[0]),
.io_awio_awaddr (axi_awaddr[0]),
// ... 其余 AXI4 连接
);如果 DDR 控制器的数据宽度 < Vortex 的数据宽度(如白杨 256-bit),需要 wrapper 拆分/拼合:
VX_mem_axi_bridge (512-bit AXI4)
→ 宽度适配 wrapper (512→256: 写拆分, 读拼合)
→ DDR 控制器 (256-bit AXI4 Slave)
→ DDR4/5 PHY
宽度适配要点:
- 写:1×512-bit 拆分为 2×256-bit burst (awlen: N → 2N+1)
- 读:2×256-bit burst 拼合为 1×512-bit (先收低半部, 后收高半部)
如果 Vortex 有多个内存端口 (NUM_PORTS > 1) 且你想保留并行性:
VX_mem_axi_bridge #(
.NUM_PORTS (VX_MEM_PORTS),
.NUM_BANKS (VX_MEM_PORTS), // 1:1 不共享
.INTERLEAVE (0)
) bridge (
// 每个 bank 独立连接到对应的 DDR 控制器
);在 sim/rtlsim/Makefile 中添加:
# 启用外部 DDR 控制器
VL_TOP ?= rtlsim
ifeq ($(EXT_MC),1)
CONFIGS += -DVX_CFG_EXT_MC_ENABLE
$(info Building with external memory controller support)
endif`ifdef VX_CFG_EXT_MC_ENABLE
// 使用外部 DDR 控制器
VX_mem_axi_bridge #(...) bridge (...);
// 连接你的 DDR 控制器 wrapper
my_ddr_wrapper #(...) ddr (...);
`else
// 默认 DPI-C 行为级 DRAM
// ... (不做修改)
`endifVX_mem_axi_bridge 本身出错概率极低(~5%,仅限于参数配置错误)。真正的风险在下游:
| 层级 | 出错概率 | 常见问题 |
|---|---|---|
| VX_mem_axi_bridge | 🟢 5% | ADDR_WIDTH 配错、TAG_WIDTH 不匹配 |
| DDR 控制器 wrapper | 🟡 70% | 宽度适配的 beat 顺序、初始化序列、复位时序 |
| DDR 控制器 + PHY | 🟡 25% | DFI 时序、training 序列、时钟域交叉 |
| 场景 | Bridge 风险 | Wrapper 风险 | 说明 |
|---|---|---|---|
| Verilator rtlsim | 几乎为零 | 中 | 仿真无时序约束,主要验证功能正确性 |
| FPGA 原型 | 低 | 高 | 需处理复位序列、CDC、PHY 硬核对接 |
| ASIC 流片 | 低 | 极高 | DFI 时序收敛、training 算法、PVT 变异 |
Vortex 内部: word-addressable (1 word = 64B, ADDR_WIDTH=26 → 4GB)
AXI4 外部: byte-addressable (ADDR_WIDTH=32 → 4GB)
症状: 读写地址偏移错误,高地址访问失败
检查: 确认 ADDR_WIDTH_OUT = ADDR_WIDTH + $clog2(DATA_WIDTH/8)
Vortex tag: UUID_WIDTH + 1 (控制事务追踪)
AXI4 ID: 必须 >= TAG_WIDTH_IN + log2(NUM_PORTS)
症状: 多个并发事务的响应匹配错乱 检查: TAG_WIDTH_OUT >= TAG_WIDTH_IN + $clog2(NUM_PORTS_IN)
写: 512-bit → 2×256-bit: 低半部先发, 高半部后发
读: 2×256-bit → 512-bit: 低半部先收, 高半部后收
症状: 高32字节和低32字节数据错位 检查: 验证 64B 对齐的跨边界写入
VX_axi_adapter 默认对 AXI BRESP/RRESP(非 OKAY)响应只有 assertion,不处理错误恢复。 症状: AXI 错误时仿真 crash(rtlsim)或无响应挂死(FPGA) 建议: 在 wrapper 层添加错误计数器和超时机制
Verilator 5.032+ 的 LATCH 警告更严格。低版本(5.020)编译通过的代码在高版本可能报错。
建议: 固定 Verilator 版本(推荐 5.020),或在 Makefile 中添加 -Wno-LATCH
每个新 wrapper 必须通过以下验证:
Phase 1: 冒烟测试
├── 基本读写: 写 64B → 读 64B,验证数据完整性
├── 地址扫描: 在不同地址区域读写(起始/中间/末尾)
└── 预期: 0 错误
Phase 2: 并发测试
├── 多端口并发读写(如 NUM_PORTS >= 2)
├── 读写交错(连续写 + 连续读)
└── 预期: 数据一致性,无死锁
Phase 3: 压力测试
├── 全地址空间随机读写(>= 10000 次事务)
├── burst 对齐和跨边界测试
└── 预期: 0 数据错误
Phase 4 (FPGA 可选): 时序验证
├── Vivado/Quartus 时序报告无违规
├── 目标频率下 ChipScope/ILA 波形验证
└── 预期: 时序收敛,实际读写正确
module VX_mem_ctrl_wrapper_<控制器名> #(
parameter AXI_DATA_WIDTH = 512, // Vortex 侧数据宽度
parameter MC_DATA_WIDTH = ???, // 控制器侧数据宽度
parameter AXI_ADDR_WIDTH = 32, // byte-addressable
parameter AXI_ID_WIDTH = 8
) (
input wire clk,
input wire reset,
// === AXI4 Master (来自 VX_mem_axi_bridge) ===
// (标准 AXI4 5 通道,参见 bridge 端口定义)
// === 控制器专用接口 (DFI/APB/配置) ===
// (控制器特有的初始化、配置、PHY 接口)
// === 调试/监控 ===
output wire [31:0] debug_err_count, // AXI 错误计数
output wire debug_init_done, // 初始化完成
output wire [31:0] debug_rd_bytes, // 读字节计数
output wire [31:0] debug_wr_bytes // 写字节计数
);| 信号 | 宽度 | 描述 |
|---|---|---|
debug_init_done |
1 | MC 初始化完成标志 |
debug_err_count |
32 | AXI BRESP/RRESP != OKAY 累计 |
debug_rd_bytes |
32 | 成功读取的总字节数 |
debug_wr_bytes |
32 | 成功写入的总字节数 |
debug_timeout |
1 | 事务超时标志(>10K 周期无响应) |
| 控制器 | 标准 | 频率 | 数据宽度 | 模式 | 许可 | 验证状态 | Wrapper文件 | 维护者 |
|---|---|---|---|---|---|---|---|---|
| 白杨 (YuQuan) | DDR4 | 2400 | 256-bit | B | MulanPSL-2.0 | ✅ SimX | VX_yuquan_wrapper.sv |
DiVo |
| 白杨 DDR4-3200 | DDR4 | 3200 | 256-bit | B | MulanPSL-2.0 | ✅ APB3 Init TB | VX_mem_ctrl_wrapper_baiyang_ddr4.sv |
DiVo |
| DDR4-IP / CVA6 / serv | DDR4 | — | 512-bit | A | 各项目许可 | ✅ 62 tests | VX_mem_ctrl_wrapper_passthrough.sv |
DiVo |
| LiteDRAM | DDR3/4 | — | 128/256 | B | MIT | ✅ 20 tests | VX_mem_ctrl_wrapper_litedram.sv |
DiVo |
| DDR3-Controller | DDR3 | — | 128 | B | MIT | ✅ 20 tests | VX_mem_ctrl_wrapper_ddr3ctrl.sv |
DiVo |
| CVA6 AXI DRAM | DDR4 | — | 128/512 | A/B | Solderpad | ✅ 20 tests (passthrough) | VX_mem_ctrl_wrapper_cva6_axi.sv |
DiVo |
| 白杨 LPDDR5 | LPDDR5 | — | — | B | MulanPSL-2.0 | 🔮 远期 | — | — |
验证状态说明: "✅ N tests" 表示 Verilator 5.020 standalone testbench 通过 N 次写-读验证; "✅ APB3 Init TB" 表示仅 APB3 初始化序列通过(AXI4 数据通路为框架 TODO); "✅ SimX" 表示 Vortex SimX 全系统仿真通过
| 控制器 | 标准 | 数据宽度 | 难度 | 许可 |
|---|---|---|---|---|
| serv DDR | LPDDR | 32 | ⭐ | ISC |
| Caliptra AHB-Lite | SRAM | 32 | ⭐ | Apache 2.0 |
| LPDDR4-Controller | LPDDR4 | 16/32 | ⭐⭐⭐ | BSD-3 |
| 控制器 | 标准 | 数据宽度 | 难度 | 预估工时 |
|---|---|---|---|---|
| 白杨 DDR5(等上游开放) | DDR5 | 512 | ⭐⭐⭐⭐ | ~2周 |
| NVDLA SDP | 统一内存 | 512 | ⭐⭐⭐ | ~1周 |
| 难度 | 说明 | 示例 |
|---|---|---|
| ⭐ | 同宽直连(模式 A),无适配逻辑 | DDR4-IP 512-bit |
| ⭐⭐ | 宽度适配(模式 B),简单 FSM | LiteDRAM 128→512 |
| ⭐⭐⭐ | 复杂初始化序列 + CDC | 带 DDR training |
| ⭐⭐⭐⭐ | 全新协议(DFI 5.2 / LPDDR5) | 白杨 DDR5 |
在提交 PR 前,确认以下各项:
- 接口信号命名遵循
VX_mem_ctrl_wrapper_<name>约定 - 所有参数有默认值和注释
- 宽度适配逻辑有独立的状态机(不混入初始化逻辑)
- 初始化序列有清晰的 FSM 注释和状态图
- 包含调试信号(init_done, err_count, rd/wr_bytes)
- 条件编译用
VX_CFG_<NAME>_MC_ENABLE包裹
- rtlsim 基本读写通过(0 错误)
- 并发多端口读写通过(0 死锁)
- 压力测试通过(>= 10000 随机地址,0 数据错误)
- AXI 错误注入测试通过(超时/错误响应不 crash)
- Verilator 编译无 LATCh/UNUSED 警告
- 测试覆盖边界:地址 0、地址最大值、跨 bank、非对齐
- 本文件 §8 适配矩阵表已更新
- README 中有构建命令(
make EXT_MC=1) - wrapper 头注释说明支持的配置/频率
- 已知限制已标注
每个 wrapper 附带独立 testbench,无需完整 Vortex 构建即可验证。
hw/rtl/mem/wrappers/
├── VX_mem_ctrl_wrapper_passthrough.sv # 模式A: 同宽直连
├── VX_mem_ctrl_wrapper_litedram.sv # 模式B: Wishbone 适配
├── VX_mem_ctrl_wrapper_ddr3ctrl.sv # 模式B: DDR3 简单握手
├── VX_mem_ctrl_wrapper_cva6_axi.sv # 模式A/B: CVA6 AXI
├── VX_mem_ctrl_wrapper_baiyang_ddr4.sv # 模式B: 白杨 DDR4 (ifdef 保护)
└── common/
├── VX_mock_axi_memory.sv # AXI4 BRAM mock memory
├── tb_wrapper_passthrough_standalone.sv # passthrough TB (62 tests)
├── tb_wrapper_litedram_standalone.sv # litedram TB (20 tests)
├── tb_wrapper_ddr3ctrl_standalone.sv # ddr3ctrl TB (20 tests)
├── tb_wrapper_cva6_axi_standalone.sv # cva6_axi TB (20 tests)
├── tb_wrapper_baiyang_ddr4_standalone.sv# baiyang DDR4 TB (APB3)
├── main_passthrough.cpp # passthrough C++ main
└── main_wrappers.cpp # 通用 C++ main (多 TB)
cd hw/rtl/mem/wrappers/common
# passthrough (62 tests)
verilator --cc --exe --build -j 0 --language 1800-2012 \
-Wno-fatal -Wno-LATCH -Wno-WIDTHEXPAND -Wno-UNUSEDSIGNAL \
-Mdir obj_dir tb_wrapper_passthrough_standalone.sv \
../VX_mem_ctrl_wrapper_passthrough.sv VX_mock_axi_memory.sv \
main_passthrough.cpp -o tb_passthrough
# litedram (20 tests)
verilator --cc --exe --build -j 0 --language 1800-2012 \
-Wno-fatal -Wno-LATCH -Wno-WIDTHEXPAND -Wno-UNUSEDSIGNAL -Wno-UNUSEDPARAM \
-CFLAGS -DTB_LITEDRAM -Mdir obj_dir_ld \
tb_wrapper_litedram_standalone.sv ../VX_mem_ctrl_wrapper_litedram.sv \
main_wrappers.cpp -o tb_litedram
# ddr3ctrl (20 tests)
verilator --cc --exe --build -j 0 --language 1800-2012 \
-Wno-fatal -Wno-LATCH -Wno-WIDTHEXPAND -Wno-UNUSEDSIGNAL -Wno-UNUSEDPARAM \
-CFLAGS -DTB_DDR3CTRL -Mdir obj_dir_ddr3 \
tb_wrapper_ddr3ctrl_standalone.sv ../VX_mem_ctrl_wrapper_ddr3ctrl.sv \
main_wrappers.cpp -o tb_ddr3ctrl
# cva6_axi (20 tests, passthrough 模式)
verilator --cc --exe --build -j 0 --language 1800-2012 \
-Wno-fatal -Wno-LATCH -Wno-WIDTHEXPAND -Wno-UNUSEDSIGNAL -Wno-UNUSEDPARAM \
-CFLAGS -DTB_CVA6_AXI -Mdir obj_dir_cva6 \
tb_wrapper_cva6_axi_standalone.sv ../VX_mem_ctrl_wrapper_cva6_axi.sv \
main_wrappers.cpp -o tb_cva6
# baiyang DDR4 (APB3 初始化验证, 需 ifdef 宏)
verilator --cc --exe --build -j 0 --language 1800-2012 \
-Wno-fatal -Wno-LATCH -Wno-WIDTHEXPAND -Wno-UNUSEDSIGNAL -Wno-UNUSEDPARAM \
-DVX_CFG_YUQUAN_MC_ENABLE \
-CFLAGS -DTB_BAIYANG_DDR4 -Mdir obj_dir_baiyang \
tb_wrapper_baiyang_ddr4_standalone.sv ../VX_mem_ctrl_wrapper_baiyang_ddr4.sv \
main_wrappers.cpp -o tb_baiyang运行: ./obj_dir/tb_passthrough (输出 PASSED: ...)
| Wrapper | 测试类型 | 测试次数 | 结果 | 修复的 Bug |
|---|---|---|---|---|
| passthrough | 写-读验证 | 62 | PASSED | — |
| litedram | 512→128 WB 适配 | 20 | PASSED | — |
| ddr3ctrl | 512→128 cmd/data | 20 | PASSED | mask 反转 + NBA 读旧值 + 响应模型地址偏移 |
| cva6_axi | passthrough 模式 | 20 | PASSED | — |
| baiyang_ddr4 | APB3 初始化序列 | — | PASSED | — |
| ID | 限制 | 影响 | 计划 |
|---|---|---|---|
| W-1 | CVA6 AXI wrapper 宽度适配模式(512→128)为 stub | 仅 passthrough 模式可用 | 后续实现 |
| W-2 | 白杨 DDR4 wrapper AXI4 数据通路(512→256)为框架 | 仅 APB3 初始化可验证 | VX_yuquan_wrapper.sv (RVDon 私有) 中完整实现 |
| W-3 | 白杨 DDR4 wrapper DFI 信号仅框架定义 | 无法接真实 PHY | 用户按实际 PHY 补充 |
| W-4 | ddr3ctrl wrapper mask 语义反转 | ultraembedded 协议特有,非通用 | 已在 wrapper 内处理 |
| W-5 | litedram/ddr3ctrl 要求 AW 与 W 同周期到达 | AW 先到不锁存 → 死锁 | 上游 VX_axi_adapter 保征同拍; 后续独立锁存 |
| W-6 | 白杨 DDR4 APB3 信号为内部 reg 无端口 | 仅仿真可验证初始化序列 | 开源版仅仿真; 私有版有端口 |
| W-7 | TB 测试覆盖缺口: 无大地址回绕/非对齐/部分写 | 宽度适配高 beat 通路已验证 | 后续迭代补充 |
W-5 说明: litedram 和 ddr3ctrl wrapper 的 S_IDLE 要求
awvalid && wvalid同时为高才锁存。 这是因为 Vortex 的 VX_axi_adapter 在同一拍发出 AW 和 W 信号。 如果您的上游不保证同拍 AW/W,需修改 wrapper 为独立锁存模式(参照 passthrough wrapper)。
我们使用本指南的方法成功集成了白杨 DDR4 控制器 (MulanPSL-2.0):
- 集成电路图参见 §2 架构概览
- 512→256 宽度适配 (模式 B, §4.2)
- APB3 自动初始化 (PHY ready → 写配置寄存器 → MC 启动)
- DFI 3.1 仿真模型 (Verilator 可用 BRAM 替代)
- SimX 行为级验证通过
白杨集成详情见 openwiki/yuquan-integration.md。
欢迎提交 PR 添加新的 DDR 控制器兼容适配器。详见 §9 开发检查清单。
开源策略: "开源小手信" — 每个适配器小而精,充分验证,独立提交。
维护者: DiVo Gen²AI — 王掬琅 (Peter Wang) · 王潇奕 (Shawn Wang)
仓库: https://github.com/vortexgpgpu/vortex (上游)
联系: 通过 Vortex GitHub Issues 或 Discussions