CEVA-X16自由式编程-2-编写第一个“应用程序”

note:本文是我的原始文章的中文重写版本,语义上可能有一定出入

DSP地址空间“服务器”

  • 在本文中,我尝试利用CEVA-X16自由式编程-1-指令穷举一文中所做的探索,用已知的汇编指令编写一个最简单的、能在DSP上运行的程序。这个程序的功能也很简单:找到一块ARM和DSP都可以访问的共享内存,其中保存(由ARM指定的)一个地址,DSP要做的就是在DSP自己的地址空间读取这个地址的值,并将它保存到共享内存的另一个位置(传递给ARM),所以起了一个“DSP地址空间服务器”的名字。尽管没有指令集文档,我们根据以往的汇编语言知识和经验可以大概猜到各个指令名所代表的意思,完成简单的访存操作应该是足够的。
  • 在T32中我们可以看到这个DSP的代码和数据空间是独立的,因此我们刚好可以借这个机会探索一下它们在SoC平台上的映射关系

从ARM访问DSP

在我们着手编写和执行DSP程序之前,有必要了解如何控制这个手机上的DSP。这部分是笔者所用的SoC平台相关的知识,如果读者尝试在另一个平台上进行实验,那么这部分可能并不适合。
首先,Linux内核源码的这里这里告诉我们如何从ARM访问DSP的0x0部分内存。首先打开访问权限:

devmem 0x20900204 32 0xdb00 # AHB_CTRL1_ARM_DAHB_SLEEP_EN = 0
devmem 0x20900284 32 0x5 # BIT_ASHB_ARMTODSP_EN_I = 1

然后我们可以直接访问DSP的0x0地址了:

devmem 0x70000000

DSP控制

本平台上,DSP原本的任务是运行GSM和TD网络的底层协议栈,会和modem固件进行通讯。我们需要先停止modem虚拟机:

echo 2 > /proc/nk/stop

关闭安卓用户层(可选,如果是用USB连接adb的话建议关):

stop

因为我们知道DSP的GSM部分固件的启动地址(在ARM上)是0x00020080,我们可以把我们自己的代码加载到这个地址上面:

devmem 0x20080 32 0x3CA03CA0 # SQ.nop SQ.nop

要注意的是:

  • ARM上的 0x0 (DDR) 映射到DSP代码空间的0xC0000000
  • DSP代码空间的字节序和ARM是反的,你需要手动交换一下大小端

现在我们可以戳 这里的寄存器来复位和运行DSP了

停止/复位DSP:

devmem 0x2090028C 32 0 #stop DSP

启动DSP:

devmem 0x2090028C 32 1 #restart DSP

简单的DSP应用程序

为了实现本文开头的程序,我们需要尝试猜测一些基本指令的用法。需要一个类似MOV的指令把立即数传入寄存器,LOAD指令读取内存,STORE指令写入内存,JUMP指令实现无限循环;还需要知道几个通用32位寄存器。好在T32的调试环境已经给出了一个完整的内核寄存器列表,其中的rX和gX像是通用32位寄存器,我们先试试rX吧

经过简单的指令搜索我选择了几条看起来还算亲切的指令:

0x0A00 SC.mov #0x0,r0 //0x0传到r0
0x0A21 SC.mov #0x4,r1 //0x0传到r1
0x5001 LS0.ld{dw} (r0),r0 //从(r0)处加载32位数据到r0自身
0x5411 LS0.st{dw} r0,(r1) //将r0的32位内容存到(r1)指向的地址
0x9ABFF81F SQ.brr{t} 0xFFFFFFE0 // BRanchRelative? 应该是相对跳转

接下来我们约定一个最简单的ARM-DSP“通讯”流程:

[0x0] = src addr
[0x4] = val

DSP会反复从0x0处的32位数所指定的地址(在数据空间)读取一个dword值,然后保存到内存地址0x4里。因此在ARM上,我们只需要把想让DSP读取的指令写入0x70000000,如果DSP程序正常的话,就可以在0x70000004上得到DSP读取的值了。

这就是最终代码了(devmem会直接加载到对应的位置):

devmem 0x2090028C 32 0 #stop DSP
devmem 0x20080 32 0x0A000A21 # SC.mov #0x0,r0  SC.mov #0x4,r1 //r0 = 0x0, r1 = 0x4
devmem 0x20084 32 0x3CA03CA0 # SQ.nop SQ.nop
devmem 0x20088 32 0x50013CA0 # LS0.ld{dw} (r0),r0  SQ.nop //r0 is now loaded with address desired to read
devmem 0x2008C 32 0x3CA03CA0 # SQ.nop SQ.nop
devmem 0x20090 32 0x50013CA0 # LS0.ld{dw} (r0),r0  SQ.nop //r0 is now loaded with value we read
devmem 0x20094 32 0x3CA03CA0 # SQ.nop SQ.nop
devmem 0x20098 32 0x54113CA0 # LS0.st{dw} r0,(r1)  SQ.nop //value in r0 is stored to memory at 0x4
devmem 0x2009C 32 0x3CA03CA0 # SQ.nop SQ.nop
devmem 0x200A0 32 0x9ABFF81F # SQ.brr{t} 0xFFFFFFE0 (-0x20, to 0x20080) //loop
devmem 0x2090028C 32 1 #restart DSP

我们现在可以:

  • 把想要DSP读取的地址传递过去: devmem 0x70000000 32 0xc0020080
  • 获取DSP的执行结果: devmem 0x70000004

经过验证,我让DSP读取了0xC0020080,它立刻返回了0x0A000A21,这正是我们程序的第一个dword!这不仅说明我们的第一个DSP程序已经正常运行,也验证了在这个平台上,DSP的代码和数据空间合并成了统一的空间。

为什么要插入这么多NOP

因为我也是尝试了很久才意识到这个DSP的VLIW特性,流水线上同时执行的多条指令之间可能产生资源冲突,但对于很多VLIW的DSP、GPU来说,它们硬件本身并不会处理这种冲突,这个工作交给了编译器做。由于我们在手动汇编,所以这个问题也需要考虑进来。我这里的NOP是随意插入的,并不一定是最优的。如果你自己编写了一个汇编程序,但是无论如何也不能正常运行,可以试着在每条指令后面插入过量的NOP。接下来,找到指令之间的冲突关系,将会是比找到指令编码更加困难的一个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值