本实验是双核AMP(Asymmetric Multiprocessing)实验。双核实验需要CPU0向CPU1发出软件中断从而唤醒CPU1。CPU1对CPU0中的数据进行操作。
CPU0和CPU1共享一段内存空间(可以把这段共享空间当作数据交换的部分)。
首先要建立一个Vivado工程。这里完成的思路是:CPU0接收串口发来的数据,发出软件中断,唤醒CPU1. CPU1通过AXI接口控制LED的闪烁频率。
硬件部分:需要设计一个参数可调的分频器(输出频率10Hz-0.2Hz之间)。另外,还需要设计一个LED灯闪烁的模块,每当一个上升沿来临时,输出状态的LED就反转一下。(包括生成AXI总线的IP核的步骤,这里就不详细介绍了)
首先我们要明白:如果我们想要启动CPU1,那么只能通过CPU0唤醒CPU1。AMP相对比其余两种(分别是SMP、BMP)最大的优点点就是两个内核Run不同或相同的裸机程序/OS。
既然为双核,那么这两个核肯定有单独的运行内存。难道只有单独运行的内存吗?不是这样的,除了单独的运行内存,当然也存在共享内存。(笔者认为,在SDK中完全也可以不设置共享内存,让两个核各自做各自的工作,井水永远不犯河水)。
如果既能让CPU0和CPU1单独实现工作,又能让CPU0和CPU2完成通信岂不美哉?上边说过,CPU0和CPU1共享一段内存,所谓共享就是CPU0能读写这段内存那么CPU1照样也能读写这段内存(就像学生宿舍里边的空调一样,同学A能操作空调,同学B照样能操作空调。如果只有A同学能操作这个空调,那么这个空调恐怕不能叫做shared air conditioner而应该叫做privacy air conditioner)。当然,共享的事物总是有不足之处的,比如A同学正在拿着遥控器操作这个空调的同时,那么其他同学就不能对这个共享空调进行操作了。类比可知,这个所谓的共享内存,是在一定的条件下,CPU0/CPU1才能进行操作,如果同时操作CPU0和CPU1就冲突“打架“了。
为了保证这两个CPU不打架,还是拿这两个同学做类比:
A:我现在正在用这个遥控器,麻烦兄弟等一会再用。 B:好的,你先调合适的温度,一会儿风向由我决定。 … A:我好了,该你使用遥控器了。 B:好的,我知道了。 … | CPU0:我现在正在操作共享内存,麻烦兄弟等会儿再操作。 CPU1:好的,那我等一会儿再操作。 … CPU0:我好了,该你读写共享内存了。 CPU1:那我就开始读写这块内存了。 … |
那么究竟是什么导致CPU0和CPU1如此协调呢?在参考了正点原子和ALINX两家的例程后,笔者发现大致是这么个运行机制:
CPU0开启接收中断号1的中断; CPU1开启接收中断号2的中断;
CPU0向CPU1发送2号中断; CPU1向CPU0发送1号中断;
我们让这两个CPU约定好,你那边接收的中断号就是我这边发送出去的中断号,你那边发送的中断号就是我这边接收的中断号。这样一来,就形成了两个CPU的相互配合。我们还可以列个表格,更加清晰的认识这种机制(先读左半边,后读右半边)。
CPU0:我现在正在操作共享内存,麻烦兄弟等会儿再操作。 CPU1:好的,那我等一会儿再操作。 … CPU0:我好了,该你读写共享内存了。 CPU1:那我就开始读写这块内存了。 CPU0:好的,啥时候到我了,请叫我! … CPU1:我好了,该你了。 CPU0:我现在正在操作共享内存,麻烦兄弟等会儿再操作。 … | CPU0:在我没有发送2号中断前,你不要读写共享内存。 CPU1:我知道了,我接收到2号中断信号后再进行读写共享内存。 … CPU0:我已经给你发送过2号中断了。 CPU1:换我了。我没有发送1号中断前,你不要读写共享内存。 CPU0:OK,我接收到1号中断信号后再进行读写共享内存。 … CPU1:我发送过1号中断了,该你了。 CPU0:在我没有发送2号中断前,你不要读写共享内存。 … |
需要注意的是:用软件中断,必须要保证中断号在0~15内(参见UG585)。
现在给出配置这种机制的全部过程:
- 在Vivado中,配置Processing System(只需要配置1次)
- 搭建您想实现的功能。(笔者是设计了一个能改变LED闪烁频率的AXI的自定义IP;让CPU0通过UART的中断接收数字。将这个数字送入CPU1中,CPU1根据这个数字设定PL端LED的闪烁频率)
- 启动SDK,创建两个工程,创建工程的步骤就不说了,网上一大堆。
- 设定两个核的运行内存和共享内存。(注意!这两个核的内存不能重复)
- 软件程序设计(这个步骤繁琐,后边详细介绍)。
- 下载配置。(网上也有,还可参考ALINX或正点原子的教程)
- 程序固化(可选)。
现在对软件程序设计部分的步骤进行详细的描述:
创建CPU0的APP并设置运行内存的大小:
经过计算得知:现在的CPU0的内存范围:0x0010000~0X0FFFFFFF
按照这个,那么CPU1的内存的基地址设置成0X10000000最符合常理。
创建CPU1的APP并设置内存的大小。
CPU1的内存基地址是0X10000000,大小同样也设置成0X0FF00000。
那么CPU1的内存范围:0X1000_0000~0X1FEF_FFFF。
自认而然我们把0X1FF0_0000作为共享内存的基地址(其实所谓的共享内存基地址只要不是在CPU0和CPU1内存的范围内均可。正点原子是把下边的ps7_ram_1设置成了共享内存;而ALINX是在DDR3的最后留出了256Byte的共享地址)。
现在需要对CPU1的板级支持包做额外的配置,只有这样CPU1才能正常工作,选中cpu1对应的bsp文件夹,右键选择板级支持包设置,图中显示的地方补充输入: -DUSE_AMP=1
对CPU0进行程序设计(有关这两个程序的设计实在有点乱,也有点长。需要可以留言留下邮箱,看到之后会发送到邮箱):
CPU0实现的功能有:串口接收中断,设置软件1号中断(SGI #1),设置共享内存的基地址,设置CPU0的内存基地址及其大小,能够向CPU1发送软件2号中断。
对CPU1进行程序设计:
CPU1实现的功能有:能接收CPU0发来的2号中断,设置共享内存的基地址,设置CPU1的内存地址及其大小,能够向CPU0发送1号中断。将CPU0传输到共享内存的数据,传输到AXI的slv_reg0上。这样就能控制LED闪烁的频率了。
实验效果:发送2就是对10Hz 进行2分频。发送0x0A就是对10Hz进行10分频(注意16进制发送,不发送新行)。LED也会从快速闪烁到慢速闪烁!