在ZYNQ7020上学习ARM架构(1)——练习指令集
1. 前言
最近在看ARM的架构,看到指令集 这一章,讲的比较简单,也没有具体的例子,所以就想着实践一下。ARM官网也给出了几个练习,就想着先尝试一下。但是官方的开发软件是收费的同时使用的还是模拟器,而我手边正好有搭载ARM Cortex-A9的ZYNQ7020,就直接在ZYNQ上练习好了。
Note: 这篇文章记录了我的思路历程,不是纯粹的教程,可能会繁琐。
2. ZYNQ的ARM核是怎么样启动的
到最后才发现,这里其实不需要知道就可以继续了,但是既然去了解了我就把他写下来。
2.1 stage0:BootROM
可以理解为固件,是写死在芯片上的(具体写在哪里我也没找到),在APU上电后或者系统复位后(PS_POR_B 接口复位)就开始执行。
首先判断是JTAG启动还是Flash Device启动
- 如果是JTAG启动,后面还会细分Cascade还是Independent模式,没看太懂,这里暂时不细致研究
- 如果是Flash Device启动,图上给出了很多细节,重点就是会把FSBL或者用户配置的代码搬运到OCM(On-chip Memory).
2.2 stage1:执行启动/用户代码
BootROM会从SD卡或者QSPI读取FSBL,具体步骤可以参见基础教程6.4节。这里有一个关键的理解就是,boot文件的构成
使用SDK生成Boot Image的时候,第一项会自动生成,第二项需要新建fsbl工程然后编译出fsbl.elf,第三项就是自己的工程代码然后编译xxx.elf。创建Boot Image的方法:
2.3 小结
了解了这一部分内容之后,如果想直接编写汇编程序简单操作ARM Cortex-A9而的话甚至不需要建立工程,将自己的.s文件编译好得到可执行文件.elf后即可制作boot image然后使用SD卡或者其他方式进行加载,当然也可以使用SDK的system debuger使用JTAG加载/调试。
3. 汇编程序求最大公约数GCD
这是ARM文档里的第一次Exercise
3.1 算法描述-更相减损法
可以参考博文
更相减损术
不妨设A>B,设A和B的最大公约数为X,所以 A=aX,B=bx,其中a和b都为正整数,且a>b。
C = A-B,则有:
C=aX−bX=(a−b)X
因为a和b均为正整数,所以C也能被X整除,即A、B、C最大公约数均为X
所以gcd(A,B) = gcd(B,A-B)
不断迭代后(a-b)一定可以归一,然后a b也会先后归一, X即为A=B的值。
3.2 官方答案
gcd:
CMP w0, w1 // Compare a and b
B.EQ end // If they are equal, skip to the end
B.LS less_than // If unsigned less than, branch to b = b -a
SUB w0, w0, w1 // a = a - b
B gcd // Branch back to start
less_than:
SUB w1, w1, w0 // b = b - a
B gcd // Branch back to start
end:
RET
官方给出的是基于ARMv8 A64的代码,而Cortex-A9是v7 A32的代码,同时架构不同。总体差别还是挺大的。
3.3 最终代码
我也不大会写汇编,东拼西凑把这个简单的函数凑出来了
.section GCD,"ax"
.align 2
.global gcd
// uint32_t gcd(uint32_t a, uint32_t b)
.type gcd, %function
.fnstart
gcd:
cmp r0,r1
beq end
blt less_than
sub r0,r0,r1
b gcd
less_than:
sub r1,r1,r0
b gcd
end:
bx lr
.fnend
3.4 主函数
主函数还是使用了c来写,同时使用了helloworld模板,简单做了UART输出
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
extern uint32_t gcd(uint32_t, uint32_t);
int main()
{
init_platform();
uint32_t result, a, b;
a = 24;
b = 86;
result = gcd(a, b);
printf("GCD Workbook: The GCD of %d and %d is %d\n", a, b, result);
cleanup_platform();
return 0;
}
输出结果: