目录
1. 简介
2. JTAG 直接操作 DDR4
2.1 Block Design
2.2 AXI SmartConnect
2.3 DDR4 MIG
2.3.1 时钟和复位
2.3.2 AXI Slave 接口
2.4 XDC 约束
2.5 TCL 代码
2.5.1 写入 DDR4
2.5.2 读取 DDR4
3. HLS IP 操作 DDR4
3.1 Block Design
3.2 HLS IP
3.2.1 HLS 代码
3.2.2 HLS Report
3.3 TCL 代码
3.3.1 基本 proc
3.3.2 验证写入和读取
3.3.3 执行 HLS IP
4. 格式化显示报告
4.1 report_hw_axi_txn
4.2 保存到本地
4.2.1 带记录功能的读过程
4.2.2 带记录功能的写过程
4.2.3 将记录存储到本地
4.2.4 加载文件到控制台
5. 总结
1. 简介
本文分享了使用 JTAG to AXI Master 调试 DDR4 IP 的全过程。
- 在 Block Design 中通过 AXI SmartConnect 和 DDR4 MIG 进行地址分配和时钟管理。
- 对于 AXI SmartConnect 的时钟域管理,分享了同步和异步转换的优缺点,提供了在设计中优化时钟域交叉的方法,以减少延迟和资源消耗。
- 在 DDR4 MIG 的配置中,本文列出了时钟和复位信号的功能。
- 分享了 XDC 约束文件。
- 使用 TCL 脚本实现了对 DDR4 的读写操作,展示了如何通过创建 AXI 事务来进行数据传输。
- 通过封装 TCL 过程,使得写入和读取操作更加简洁和高效。
- 使用 HLS IP 初始化,分享内存访问的基本操作方法。
- 介绍了如何格式化 AXI 事务报告,以及如何将操作记录保存到本地文件中,便于后续的分析和调试。
2. JTAG 直接操作 DDR4
2.1 Block Design
地址分配:
2.2 AXI SmartConnect
- 默认情况下,SmartConnect 上的所有接口都在相同的时钟域中运行,通过 aclk 引脚接收。
- 支持多个时钟域(aclk1…aclkn),工具会自动识别并连接到对应的时钟域。
- 当沿任何 AXI 通道在属于不同时钟域的接口之间交换信息时,时钟转换逻辑会自动插入路径中。
- 当工具确定时钟域之间的关系是整数比,范围在1:16到16:1之间,并且时钟来自同一时钟源时,工具会自动配置时钟转换器以执行同步转换;否则,时钟转换器配置为异步模式。
- 当时钟转换器配置为异步模式时,所有时钟域交叉都在 FIFO 生成器核心的底层实例中执行,该核心设计用于在其写入和读取时钟域内部重新同步,无论相位或频率关系如何。在异步模式下,核心会生成适当的仅数据路径的时序约束,以覆盖所有重新同步路径。
- 时钟转换器会引入延迟。与同步转换相比,异步转换会产生更多延迟并使用更多的逻辑资源。
- 为了减少系统中的时钟转换器数量,可以级联 AXI SmartConnect 核心实例,将时钟相似的设备组合在一起。例如,将一组低频 AXI4-Lite 从设备连接到低频时钟的单独 AXI SmartConnect 核心,可以将时钟域交叉合并到级联的 AXI SmartConnect 核心实例之间的单个转换器路径中。
2.3 DDR4 MIG
2.3.1 时钟和复位
| 信号 | 方向 | 功能
|-------------------------|------- |--------------------------------------------------------
| C0_SYS_CLK | input | DRAM IP Core 时钟
| c0_ddr4_aresetn | input | AXI 复位输入,应该与 FPGA 逻辑时钟同步。
| sys_rst | input | 系统复位,高电平有效,重置整个内存模块
| c0_ddr4_ui_clk | output | 用户接口时钟,AXI 接口同步时钟取自 ui_clk。(Table 4-39)
| c0_ddr4_ui_clk_sync_rst | output | 用户接口复位,高电平有效
sys_rst 信号重置整个内存设计,包括由 MMCM 时钟(clkout0)驱动的通用互连(fabric)逻辑和 RIU 逻辑。MicroBlaze™ 和校准逻辑由 MMCM 时钟(clkout6)驱动。sys_rst 输入信号在内部同步,以创建 ui_clk_sync_rst 信号。ui_clk_sync_rst 重置信号同步地被断言和同步地被解除断言。
图3-14显示,在断言 sys_rst 后几个时钟延迟,ui_clk_sync_rst(fabric 重置)会同步断言。当 ui_clk_sync_rst 被断言时,在关闭时钟之前还有几个时钟周期。
2.3.2 AXI Slave 接口
- AXI4 从接口是可选的。
- AXI4 从接口块将 AXI4 事务映射到 UI(User Interface)。
- UI 在表 4-14 中描述(PG150),并连接到 FPGA 用户逻辑,以访问外部存储设备。
- UI 构建在早先在控制器描述中提到的本地接口之上。
- 两种工具之间的RTL是一致的。
2.4 XDC 约束
############# DDR4 Memory Controller ################################
set_property PACKAGE_PIN K22 [get_ports C0_SYS_CLK_0_clk_p]
set_property PACKAGE_PIN L22 [get_ports {c0_alert_n[0]}]
set_property IOSTANDARD SSTL12 [get_ports {c0_alert_n[0]}]
############# External Memory Interface - Clock #####################
set_property PACKAGE_PIN G24 [get_ports {ddr4_rtl_0_ck_t[0]}]
############# External Memory Interface - ADDR & CMD ################
set_property PACKAGE_PIN D26 [get_ports {ddr4_rtl_0_adr[0]}]
set_property PACKAGE_PIN D25 [get_ports {ddr4_rtl_0_adr[1]}]
set_property PACKAGE_PIN E26 [get_ports {ddr4_rtl_0_adr[2]}]
set_property PACKAGE_PIN C24 [get_ports {ddr4_rtl_0_adr[3]}]
set_property PACKAGE_PIN C26 [get_ports {ddr4_rtl_0_adr[4]}]
set_property PACKAGE_PIN F24 [get_ports {ddr4_rtl_0_adr[5]}]
set_property PACKAGE_PIN M26 [get_ports {ddr4_rtl_0_adr[6]}]
set_property PACKAGE_PIN B25 [get_ports {ddr4_rtl_0_adr[7]}]
set_property PACKAGE_PIN G26 [get_ports {ddr4_rtl_0_adr[8]}]
set_property PACKAGE_PIN B26 [get_ports {ddr4_rtl_0_adr[9]}]
set_property PACKAGE_PIN E25 [get_ports {ddr4_rtl_0_adr[10]}]
set_property PACKAGE_PIN H26 [get_ports {ddr4_rtl_0_adr[11]}]
set_property PACKAGE_PIN D23 [get_ports {ddr4_rtl_0_adr[12]}]
set_property PACKAGE_PIN F25 [get_ports {ddr4_rtl_0_adr[13]}]
set_property PACKAGE_PIN K25 [get_ports {ddr4_rtl_0_adr[14]}]
set_property PACKAGE_PIN E23 [get_ports {ddr4_rtl_0_adr[15]}]
set_property PACKAGE_PIN F22 [get_ports {ddr4_rtl_0_adr[16]}]
set_property PACKAGE_PIN K26 [get_ports {ddr4_rtl_0_bg[0]}]
set_property PACKAGE_PIN M25 [get_ports {ddr4_rtl_0_ba[0]}]
set_property PACKAGE_PIN F23 [get_ports {ddr4_rtl_0_ba[1]}]
set_property PACKAGE_PIN J26 [get_ports ddr4_rtl_0_act_n]
set_property IOSTANDARD SSTL12_DCI [get_ports ddr4_rtl_0_act_n]
############# External Memory Interface - Data ######################
set_property PACKAGE_PIN E16 [get_ports {ddr4_rtl_0_dqs_t[0]}]
set_property PACKAGE_PIN E17 [get_ports {ddr4_rtl_0_dqs_c[0]}]
set_property PACKAGE_PIN A17 [get_ports {ddr4_rtl_0_dqs_t[1]}]
set_property PACKAGE_PIN A18 [get_ports {ddr4_rtl_0_dqs_c[1]}]
set_property PACKAGE_PIN F20 [get_ports {ddr4_rtl_0_dqs_t[2]}]
set_property PACKAGE_PIN E20 [get_ports {ddr4_rtl_0_dqs_c[2]}]
set_property PACKAGE_PIN C21 [get_ports {ddr4_rtl_0_dqs_t[3]}]
set_property PACKAGE_PIN B21 [get_ports {ddr4_rtl_0_dqs_c[3]}]
set_property PACKAGE_PIN G15 [get_ports {ddr4_rtl_0_dm_n[0]}]
set_property PACKAGE_PIN C18 [get_ports {ddr4_rtl_0_dm_n[1]}]
set_property PACKAGE_PIN H18 [get_ports {ddr4_rtl_0_dm_n[2]}]
set_property PACKAGE_PIN A22 [get_ports {ddr4_rtl_0_dm_n[3]}]
set_property PACKAGE_PIN C16 [get_ports {ddr4_rtl_0_dq[0]}]
set_property PACKAGE_PIN G16 [get_ports {ddr4_rtl_0_dq[1]}]
set_property PACKAGE_PIN D15 [get_ports {ddr4_rtl_0_dq[2]}]
set_property PACKAGE_PIN G17 [get_ports {ddr4_rtl_0_dq[3]}]
set_property PACKAGE_PIN H17 [get_ports {ddr4_rtl_0_dq[4]}]
set_property PACKAGE_PIN H16 [get_ports {ddr4_rtl_0_dq[5]}]
set_property PACKAGE_PIN D16 [get_ports {ddr4_rtl_0_dq[6]}]
set_property PACKAGE_PIN E15 [get_ports {ddr4_rtl_0_dq[7]}]
set_property PACKAGE_PIN B19 [get_ports {ddr4_rtl_0_dq[8]}]
set_property PACKAGE_PIN C17 [get_ports {ddr4_rtl_0_dq[9]}]
set_property PACKAGE_PIN B20 [get_ports {ddr4_rtl_0_dq[10]}]
set_property PACKAGE_PIN B15 [get_ports {ddr4_rtl_0_dq[11]}]
set_property PACKAGE_PIN A19 [get_ports {ddr4_rtl_0_dq[12]}]
set_property PACKAGE_PIN A15 [get_ports {ddr4_rtl_0_dq[13]}]
set_property PACKAGE_PIN A20 [get_ports {ddr4_rtl_0_dq[14]}]
set_property PACKAGE_PIN B17 [get_ports {ddr4_rtl_0_dq[15]}]
set_property PACKAGE_PIN G20 [get_ports {ddr4_rtl_0_dq[16]}]
set_property PACKAGE_PIN D19 [get_ports {ddr4_rtl_0_dq[17]}]
set_property PACKAGE_PIN D20 [get_ports {ddr4_rtl_0_dq[18]}]
set_property PACKAGE_PIN F19 [get_ports {ddr4_rtl_0_dq[19]}]
set_property PACKAGE_PIN G21 [get_ports {ddr4_rtl_0_dq[20]}]
set_property PACKAGE_PIN E18 [get_ports {ddr4_rtl_0_dq[21]}]
set_property PACKAGE_PIN D18 [get_ports {ddr4_rtl_0_dq[22]}]
set_property PACKAGE_PIN F18 [get_ports {ddr4_rtl_0_dq[23]}]
set_property PACKAGE_PIN C23 [get_ports {ddr4_rtl_0_dq[24]}]
set_property PACKAGE_PIN C22 [get_ports {ddr4_rtl_0_dq[25]}]
set_property PACKAGE_PIN A24 [get_ports {ddr4_rtl_0_dq[26]}]
set_property PACKAGE_PIN B22 [get_ports {ddr4_rtl_0_dq[27]}]
set_property PACKAGE_PIN A25 [get_ports {ddr4_rtl_0_dq[28]}]
set_property PACKAGE_PIN D21 [get_ports {ddr4_rtl_0_dq[29]}]
set_property PACKAGE_PIN B24 [get_ports {ddr4_rtl_0_dq[30]}]
set_property PACKAGE_PIN E21 [get_ports {ddr4_rtl_0_dq[31]}]
############# External Memory Interface - Control ###################
set_property PACKAGE_PIN L24 [get_ports {ddr4_rtl_0_cke[0]}]
set_property PACKAGE_PIN D24 [get_ports {ddr4_rtl_0_cs_n[0]}]
set_property PACKAGE_PIN H24 [get_ports {ddr4_rtl_0_odt[0]}]
set_property PACKAGE_PIN L25 [get_ports ddr4_rtl_0_reset_n]
############################ Others #################################
set_property PACKAGE_PIN H13 [get_ports ext_reset_in_0]
set_property IOSTANDARD LVCMOS33 [get_ports ext_reset_in_0]
set_property PACKAGE_PIN H12 [get_ports led_0]
set_property IOSTANDARD LVCMOS33 [get_ports led_0]
########################## Debug Hub ################################
set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
connect_debug_port dbg_hub/clk [get_nets clk]
即使没有手动添加,Vivado 可能会在某些情况下自动插入 ILA 以便于调试。
Debug core 包括以下几种常见类型:
- ILA(Integrated Logic Analyzer):用于捕获和分析设计中的信号波形。
- VIO(Virtual Input/Output):用于监控和驱动信号,主要用于模拟外部输入输出。
- JTAG-to-AXI Master:用于通过 JTAG 接口驱动 AXI 总线,发送和接收数据包。
这些调试核心通过 Debug Hub 模块连接到 JTAG 接口,从而允许 Vivado Hardware Manager 通过 JTAG 控制和监控这些核心。
2.5 TCL 代码
2.5.1 写入 DDR4
1)将 32bit*4 作为整体直接发送:
create_hw_axi_txn \
write_txn [get_hw_axis hw_axi_1] \
-type WRITE \
-address 4000_0000 \
-len 4 \
-data {11112222_33334444_55556666_77778888} \
-force
run_hw_axi [get_hw_axi_txns write_txn]
“-data” 方向为左侧 MSB(即,地址 3)和右侧 LSB(即,地址 0)。
读取4个数据进行验证:
run_hw_axi [get_hw_axi_txns read_txn]
report_hw_axi_txn read_txn -w 4
---
4000 77778888
4004 55556666
4008 33334444
400c 11112222
2)将 32bit*4 分为四个数据发送:
create_hw_axi_txn \
write_txn [get_hw_axis hw_axi_1] \
-type WRITE \
-address 4000_0000 \
-len 4 \
-data {11112222 33334444 55556666 77778888} \
-force
run_hw_axi [get_hw_axi_txns write_txn]
“-data” 方向与情况一相反,为左侧 LSB(即,地址 0)和右侧 MSB(即,地址 3)
读取4个数据进行验证:
run_hw_axi [get_hw_axi_txns read_txn]
report_hw_axi_txn read_txn -w 4
---
4000 11112222
4004 33334444
4008 55556666
400c 77778888
3)封装写入过程的 TCL 代码:
proc write {address dataList} {
# 移除地址中的 "0x" 前缀(如果存在)和任何下划线
set address [string map {"0x" "" "_" ""} $address]
# 创建写事务
create_hw_axi_txn wr_txn [get_hw_axis hw_axi_1] -address $address -data $dataList -len [llength $dataList] -type write -force
# 执行事务
run_hw_axi wr_txn
}
write 0x4000_0000 {0x11111111 0x22222222 0x33333333 0x44444444}
2.5.2 读取 DDR4
proc read {address len} {
# 移除地址中的 "0x" 前缀(如果存在)和任何下划线
set address [string map {"0x" "" "_" ""} $address]
# 创建读事务
create_hw_axi_txn rd_txn [get_hw_axis hw_axi_1] -address $address -len $len -type read -force
# 执行事务
run_hw_axi rd_txn
}
read 0x4000_0000 4
3. HLS IP 操作 DDR4
3.1 Block Design
- 红色:166.625 MHz 时钟域
- 蓝色:333.250 MHz 时钟域
为什么这么安排时钟?
JTAG to AXI Master 的最高设计时钟是 200MHz,DDR4 IP 输出的用户时钟为 333.25MHz,通过BUFGCE_DIV 二分频为 166.625 MHz,此时钟给所有模块进行驱动。而 333.25MHz 仅给 AXI Interconnet 上与 DDR4 IP 的 AXI Slave 接口使用。
3.2 HLS IP
3.2.1 HLS 代码
一个简单的内存初始化函数:将一个整型数组初始化,数组的每个元素的值等于其索引。
void init_memory(int *mem)
{
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=m_axi port=mem depth=128
int i;
for (i = 0; i < 128; i++) {
mem[i] = i; // 初始化内存,值为 0 到 127
}
}
代码分析
- 函数 init_memory 接受一个指向整数的指针 mem 作为参数。这个指针指向的是需要被初始化的内存区域。
- HLS INTERFACE mode=s_axilite port=return:块级控制接口为 s_axilite。
- HLS INTERFACE mode=m_axi port=mem depth=128:指定 mem 参数应该通过一个 AXI Master 接口来访问,存储深度是 128。表示这个内存区域可以存储128个整数。
- 循环迭代:用于初始化内存区域。
地址递增分析
mem 是一个指向整数数组的起始地址。每次循环迭代,`i` 的值增加1,因此访问的内存地址也相应地递增。mem 是整数指针,占用4个字节。因此,每次数组索引递增时,内存地址递增的4个字节。
3.2.2 HLS Report
1)地址位宽
* M_AXI
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| Interface | Data Width | Address Width | Latency | Offset | Register | Max Widen | Max Read | Max Write | Num Read | Num Write |
| | (SW->HW) | | | | | Bitwidth | Burst Length | Burst Length | Outstanding | Outstanding |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| m_axi_gmem | 32 -> 32 | 32 | 0 | slave | 0 | 0 | 16 | 16 | 16 | 16 |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
Vitis HLS 工具默认生成的位宽是 64bit,通过取消勾选 m_axi_addr64,可以启用 32bit 位宽。
3.3 TCL 代码
3.3.1 基本 proc
proc write {address dataList} {
set address [string map {"0x" "" "_" ""} $address]
create_hw_axi_txn wr_txn [get_hw_axis hw_axi_1] -address $address -data $dataList -len [llength $dataList] -type write -force
run_hw_axi wr_txn
}
proc read {address len} {
set address [string map {"0x" "" "_" ""} $address]
create_hw_axi_txn rd_txn [get_hw_axis hw_axi_1] -address $address -len $len -type read -force
run_hw_axi rd_txn
}
3.3.2 验证写入和读取
首先写入特定的数据:
write 0x0000_0000 {0x12345678 0x22345678 0x32345678 0x42345678}
---
INFO: [Labtoolstcl 44-481] WRITE DATA is: 0x12345678 0x22345678 0x32345678 0x42345678
读取,以验证高低位:
report_hw_axi_txn read_txn -w 4
---
0000 12345678
0004 22345678
0008 32345678
000c 42345678
3.3.3 执行 HLS IP
寄存器描述:
// ==============================================================
// control
// 0x0 : Control signals
// bit 0 - ap_start (Read/Write/COH)
// bit 1 - ap_done (Read/COR)
// bit 2 - ap_idle (Read)
// bit 3 - ap_ready (Read/COR)
// bit 7 - auto_restart (Read/Write)
// bit 9 - interrupt (Read)
// others - reserved
// 0x4 : Global Interrupt Enable Register
// bit 0 - Global Interrupt Enable (Read/Write)
// others - reserved
// 0x8 : IP Interrupt Enable Register (Read/Write)
// bit 0 - enable ap_done interrupt (Read/Write)
// bit 1 - enable ap_ready interrupt (Read/Write)
// others - reserved
// 0xc : IP Interrupt Status Register (Read/COR)
// bit 0 - ap_done (Read/COR)
// bit 1 - ap_ready (Read/COR)
// others - reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
向 bit 0 - ap_start 写入 '1',启动一次 HLS IP。
write 0x8000_0000 0x0000_0001
此时,内存中的数据已被 HLS IP 初始化。读取 0x0000_0000 地址的数据以验证:
read 0x0000_0000 4
INFO: [Labtoolstcl 44-481] READ DATA is: 00000003000000020000000100000000
report_hw_axi_txn read_txn -w 4
---
0000 00000000
0004 00000001
0008 00000002
000c 00000003
4. 格式化显示报告
4.1 report_hw_axi_txn
描述
报告格式化的硬件 AXI 事务数据(读取事务和写入事务均可)。
语法
report_hw_axi_txn [-w <arg>] [-t <arg>] [-quiet] [-verbose] <hw_axi_txns>...
用法:
Name Description
--------------------------
[-w] 每行输出的数据字节数(默认值:8)
[-t] d[SIZE] 有符号十进制,每个整数SIZE字节
b[SIZE] 二进制,每个整数SIZE字节
o[SIZE] 八进制,每个整数SIZE字节
u[SIZE] 无符号十进制,每个整数SIZE字节
x[SIZE] 十六进制,每个整数SIZE字节,默认值:x4(4字节十六进制)
[-quiet] 忽略命令错误
[-verbose] 在命令执行期间暂停消息限制
<hw_axi_txns> 要报告的硬件AXI事务对象
示例(以默认的十六进制数显示):
创建传输事务(先写,后读):
create_hw_axi_txn \
write_txn [get_hw_axis hw_axi_1] \
-type WRITE \
-address 4000_0000 \
-len 4 \
-data {11111111_22222222_33333333_44444444} \
-force
run_hw_axi [get_hw_axi_txns write_txn]
create_hw_axi_txn \
read_txn [get_hw_axis hw_axi_1] \
-type READ \
-address 4000_0000 \
-len 4 \
-force
run_hw_axi [get_hw_axi_txns read_txn]
每行显示4个字节 :
report_hw_axi_txn read_txn -w 4
---
4000 44444444
4004 33333333
4008 22222222
400c 11111111
每行显示8个字节:
report_hw_axi_txn write_txn -w 8
---
4000 44444444 33333333
4008 22222222 11111111
每行显示16个字节:
report_hw_axi_txn write_txn -w 16
---
4000 44444444 33333333 22222222 11111111
4.2 保存到本地
4.2.1 带记录功能的读过程
proc read { address } {
# 定义全局变量data_list和num
global data_list
global num
# 创建一个AXI读事务命令。指定硬件接口为hw_axi_1,地址为传入的address,操作类型为读取。
create_hw_axi_txn read_txn [get_hw_axis hw_axi_1] -address $address -type read
# 执行创建的AXI读事务
run_hw_axi read_txn
# 从事务报告中获取读取的值,该值位于返回列表的第二个位置(索引为1)
set read_value [lindex [report_hw_axi_txn read_txn] 1];
# 将读取的事务信息格式化后追加到data_list列表中。包括事务编号、操作类型(读取)、地址和读取的值。
# 这里分别格式化事务编号、操作类型、地址和读取的值,并追加到data_list字符串中。
append data_list [format %3i $num]
append data_list [format %4s r]
append data_list [format %10s $address]
append data_list [format "%10s\n" $read_value]
# 删除已完成的读事务
delete_hw_axi_txn read_txn
# 递增事务编号
incr num
}
4.2.2 带记录功能的写过程
proc WriteReg { address data } {
# 定义全局变量data_list和num
global data_list
global num
# 创建一个AXI写事务命令。指定硬件接口为hw_axi_1,地址为传入的address,数据为传入的data,操作类型为写入。
create_hw_axi_txn write_txn [get_hw_axis hw_axi_1] -address $address -data $data -type write
# 执行创建的AXI写事务
run_hw_axi write_txn
# 从事务报告中获取写入的值,该值位于返回列表的第二个位置(索引为1)
set write_value [lindex [report_hw_axi_txn write_txn] 1];
# 将写入的事务信息格式化后追加到data_list列表中。包括事务编号、操作类型(写入)、地址和写入的值。
# 这里分别格式化事务编号、操作类型、地址和写入的值,并追加到data_list字符串中。
append data_list [format %3i $num]
append data_list [format %4s w]
append data_list [format %10s $address]
append data_list [format "%10s\n" $write_value]
# 删除已完成的写事务
delete_hw_axi_txn write_txn
# 递增事务编号
incr num
}
4.2.3 将记录存储到本地
#-----------------------------------
# 时间戳
#-----------------------------------
# 获取当前时间的秒数
set currentTime [clock seconds]
# 格式化当前时间,包括日期和时间,并存储在变量ctime中
set ctime "The time is: \
[clock format $currentTime -format %D] \
[clock format $currentTime -format %H:%M:%S] \n"
#-----------------------------------
# 文件保存:设置您自己的文件路径
#-----------------------------------
# 将命令数据保存到本地
# 设置文件路径,这里需要用户根据实际情况设置路径
set file_path "xx/log.txt"
# 以追加模式打开文件,如果文件不存在则创建文件
set fp [open $file_path a+]
# 将当前时间写入文件
puts $fp $ctime
# 将数据列表写入文件
puts $fp $data_list
# 关闭文件
close $fp
4.2.4 加载文件到控制台
# 从本地数据文件加载数据
# 以只读模式打开文件
set fp [open $file_path r]
# 读取文件的全部内容
set file_data [read $fp]
# 输出读取的数据到控制台,用于验证内容
puts $file_data
# 关闭文件
close $fp
5. 总结
本文分享了使用 JTAG to AXI Master 调试 DDR4 IP 的过程。
要点:
- 使用 JTAG to AXI Master 直连 DDR4 IP
- 添加 HLS IP 初始化 DDR4 IP
- 格式化显示传输事务
- 保存传输事务记录