当前位置: 首页 > news >正文

VDK中接收memcpy传递结构体时,interface被访问多次问题

文章目录

    • memcpy
  • RISC-V 汇编代码分析:内存复制函数
    • 1. 初始检查和参数设置
    • 2. 64字节块复制(优化路径)
      • 64字节复制循环体
    • 3. 8字节块复制
      • 8字节复制循环体
    • 4. 单字节复制(剩余部分)
    • 5. 函数返回
    • 关键优化点
    • 结论

问题现象:在VDK软件中,SDMA接受从SCP/AP侧发来的命令帧后自动解析命令帧,然后执行相应操作,但是在使用SCP 给 SDMA发送命令帧时,发现SDMA的回调调用了多次,因此查看反汇编出来的代码,是否给SDMA的interface发送了多次的transaction,造成了回调调用多次。
猜测:memcpy时,是多次transaction
SCP测试代码如下:

SDMA_BRUST_HEAD sdma_frame_;
uintptr_t addr_sdma_ = (0x11002000);
memcpy(addr_sdma_, &sdma_frame_, sizeof(SDMA_BRUST_HEAD));

需要调试的sdma代码如下

/* 1 */
lui a5, 0xe0           # 将 0xe0 << 12 = 0xe0000 加载到 a5 高20位
c.addi a5, 20          # a5 = 0xe0000 + 20 = 0xe0014
c.sd a5, 32(sp)        # 将 a5 的值存储到 [sp+32] 的内存位置
c.ld a5, 32(sp)        # 从 [sp+32] 重新加载到 a5 (看起来冗余)
c.ld a5, 32(sp)        # 再次加载 (可能是编译器生成的冗余指令)
sw zero, 0(a5)         # 将 0 写入 [a5+0] 的内存位置
/* 2 */
lui a5, 0x11002        # 加载 0x11002000 到 a5
c.sd a5, 24(sp)        # 存储目标地址到 [sp+24]
c.mv a5, sp            # a5 = sp (栈指针)
c.li a2, 1             # a2 = 1 (复制长度=1字节)
c.mv a1, a5            # a1 = sp (源地址)
c.ldsp a0, 24(sp)      # a0 = [sp+24] = 0x11002000 (目标地址)
jal ra, 0xe000000000fd <memcpy> # 调用 memcpy
  1. 地址加载与存储(这段代码将0xe0014地址处的内存清零)
  2. 准备memcpy参数(准备参数并调用memcpy,将栈上1字节数据复制到0x11002000地址)

最关键的部分来了

memcpy

c63d      c.beqz  a2,0xe00001046 
82aa     c.mv t0, a0
83b2     c.mv t2, a2
fc03f31     andi t1, t2, -64
9316     c.add t1, t0
0255863     bge a0, t1, 0xe000f4c
6190     c.ld a2, 0(a1)
6594     c.ld a3, 8(a1)
6998     c.ld a4, 16(a1)
6d9c     c.ld a5, 24(a1)
e110     c.sd a2, 0(a0)
e514     c.sd a3, 8(a0)
e918     c.sd a4, 16(a0)
ed1c     c.sd a5, 24(a0)
7190     c.ld a2, 32(a1)
7594     c.ld a3,40(a1)
7998     c.ld a4,48(a1)
7d9c     c.ld  a5,56(a1)
f110     c.sd a2, 32(a0)
f514     c.sd a3, 40(a0)
f918     c.sd a4, 48(a0)
fd1c     c.sd a5, 56(a0)
04058593     addi a1, a1, 64
04050513     andi a0, a0, 64fc654ce3      blt a0, t1,0xe000000fe8 <memcpy+0x10>
ff83f313      andi t1, t2, -8
0053033     addi a0, t1, t0
0655863      bge a0, t1,0xe00000102c <_loader_start+0x2c>
6190       c.ld a2, 0(a1)
0521       c.addi a1, 8
e110       c.sd a2, 0(a0)0521       c.addi a0, 8
fe654ce3      blt a0, t1,0xe000001020 <_loader_start+0x20>
00728333     add t1, t0, t2
00655a63       bge a0, t1,0xe000001044 <_loader_start+0x44>
005c603      lbu a2, 0(a1)
0585       c.addi a1, 1
00c50023      sb a2, 0(a0)
0505       c.addi a0, 1
fe654ae3      blt a0, t1,0xe000001034 <__loader_start+0x34>
8516       c.mv a0, t0
8082       c.jr ra

RISC-V 汇编代码分析:内存复制函数

这段代码实现了一个优化的内存复制函数(memcpy),我将其分为几个关键部分进行分析:

1. 初始检查和参数设置

c63d      c.beqz  a2,0xe00001046    # 如果复制长度(a2)为0,直接跳转到结束
82aa     c.mv t0, a0               # 保存原始目标地址到t0
83b2     c.mv t2, a2               # 保存原始长度到t2

2. 64字节块复制(优化路径)

fc03f31     andi t1, t2, -64       # 计算64字节对齐的长度
9316     c.add t1, t0              # t1 = 目标地址 + 对齐长度
0255863     bge a0, t1, 0xe000f4c  # 如果不需要64字节复制,跳转到小数据复制

64字节复制循环体

6190     c.ld a2, 0(a1)           # 从源地址加载8字节
6594     c.ld a3, 8(a1)           # 加载+8字节
6998     c.ld a4, 16(a1)          # 加载+16字节
6d9c     c.ld a5, 24(a1)          # 加载+24字节
e110     c.sd a2, 0(a0)           # 存储到目标地址
e514     c.sd a3, 8(a0)           # 存储+8字节
e918     c.sd a4, 16(a0)          # 存储+16字节
ed1c     c.sd a5, 24(a0)          # 存储+24字节
7190     c.ld a2, 32(a1)          # 加载+32字节
7594     c.ld a3,40(a1)           # 加载+40字节
7998     c.ld a4,48(a1)           # 加载+48字节
7d9c     c.ld a5,56(a1)           # 加载+56字节
f110     c.sd a2, 32(a0)          # 存储+32字节
f514     c.sd a3, 40(a0)          # 存储+40字节
f918     c.sd a4, 48(a0)          # 存储+48字节
fd1c     c.sd a5, 56(a0)          # 存储+56字节
04058593     addi a1, a1, 64      # 源地址+64
04050513     addi a0, a0, 64      # 目标地址+64
fc654ce3      blt a0, t1,0xe000000fe8 # 循环直到完成所有64字节块

3. 8字节块复制

ff83f313      andi t1, t2, -8     # 计算8字节对齐的长度
0053033       add t1, t0, t1      # t1 = 目标地址 + 对齐长度
0655863      bge a0, t1,0xe00000102c # 如果不需要8字节复制,跳转到单字节复制

8字节复制循环体

6190       c.ld a2, 0(a1)         # 加载8字节
0521       c.addi a1, 8           # 源地址+8
e110       c.sd a2, 0(a0)         # 存储8字节
0521       c.addi a0, 8           # 目标地址+8
fe654ce3      blt a0, t1,0xe000001020 # 循环直到完成所有8字节块

4. 单字节复制(剩余部分)

00728333     add t1, t0, t2       # t1 = 原始目标地址 + 原始长度
00655a63       bge a0, t1,0xe000001044 # 检查是否已完成
005c603      lbu a2, 0(a1)        # 加载1字节
0585       c.addi a1, 1           # 源地址+1
00c50023      sb a2, 0(a0)        # 存储1字节
0505       c.addi a0, 1           # 目标地址+1
fe654ae3      blt a0, t1,0xe000001034 # 循环直到完成所有单字节

5. 函数返回

8516       c.mv a0, t0            # 返回原始目标地址
8082       c.jr ra                # 返回调用者

关键优化点

  1. 分层复制策略:先尝试64字节块复制,然后是8字节块,最后是单字节
  2. 寄存器重用:使用a2-a5作为临时寄存器加载/存储数据
  3. 循环展开:64字节复制部分完全展开,避免循环开销
  4. 压缩指令:大量使用c.xxx压缩指令减少代码大小

这个实现是典型的memcpy优化,优先处理大块对齐数据以获得最佳性能,然后处理剩余的小数据。

结论

答案已经很明显了,结果就是因为memcpy会分层复制,导致了给interface 传不定次的 transaction, 解决方案也很简单,根据帧格式,去解析指令,每次来的transaction,提取其中的数据,将其push_back 到 vector中,每当收集到tail帧,就解析整个帧,执行相应逻辑后,clear整个vector,问题完美解决,最后贴一下解决的代码

void SysDMA::handle_sdma_disp_cmd_if_protocol_engine_b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& time) {// FLYME: Default implementation. Implement this.for (int i = 0; i < trans.get_data_length(); ++i) {dispatch_frame._push_back(*(trans.get_data_ptr() + i));}for (int i = 0; i < dispatch_frame_size(); ++i) {if (i % 16 == 0 && dispatch_frame_size() % 16 == 0) {uint8_t temp = dispatch_frame[i] & 2;if (temp) {HandleTransactionData(dispatch_frame_data());dispatch_frame.clear();}}trans.set_response_status(tlm::TLM_OK_RESPONSE);}
}

代码分析:

  1. 这是一个SystemC TLM模型的DMA处理函数
  2. 首先将传输数据(trans)逐个字节推入dispatch_frame缓冲区
  3. 然后检查dispatch_frame中的数据,每16字节检查一次特定标志位(第2位)
  4. 如果标志位被设置,则调用HandleTransactionData处理数据并清空缓冲区
  5. 最后设置传输响应状态为OK

相关文章:

  • Spring事务简单操作
  • 中国地图上标注颜色的方法
  • Ubuntu 20.04安装及配置docker
  • 龙虎榜——20250521
  • ESP32-S3 (ESP IDF 5.4.1 - LVGL 9.2.0)九宫格拼音输入法
  • Java 实现二进制与十进制之间的互相转换
  • 7.数据的预测分析及可视化
  • 网页 HTML布局(详解)
  • Javascript 编程基础(4)函数 | 4.2、this 绑定机制
  • 全球证券交易系统开发方案
  • Pytorch基础操作
  • C#中Task.Run的线程管理最佳实践与并发控制
  • 【Linux系统】第七节—git+cgdb(详解)
  • 更新ubuntu软件源遇到GPG error
  • MySQL中的重要常见知识点(入门到入土!)
  • Python高效网络爬虫开发指南
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(26):のは ・ のが ・ のを
  • 东莞一锂离子电池公司IPO终止,客户与供应商重叠,社保缴纳情况引疑
  • 力扣周赛置换环的应用,最少交换次数
  • Python爬虫实战:获取小说网最新风云榜数据并分析,为创作者提供参考素材
  • 网站建设的局限性/网站建设网络推广平台
  • 网站信息可以/网络科技有限公司
  • 初学网站开发书籍/百度竞价推广点击器
  • 北京家装设计公司/百度搜索关键词排名优化技术
  • 亚马逊网站建设案例/独立站seo是什么
  • 怎样做网站分流赚钱/怎么买域名自己做网站