FPGA学习.PART8
1. 概述
- 本节展示软硬件协调性设计的加速方法分析与实现,使软件PS部分和PL部分合理分工;
- 本节实验基于上节的代码进行优化与分析;
2. 实验步骤
2.1 打开项目
打开上节中的OLED项目,启动SDK,针对软件部分进行优化;
2.2 函数分析
首先看到如下的函数,
1 | //向SSD1306写入一个字节的命令。 |
C语言中此函数为模拟时序,因为在单片机中无法控制硬件的逻辑如此操作为迫不得已的,而在 FPGA 中,可以将此部分独立出来写一个加速器;
2.3 硬件描述
OLED 写入时序如下图所示,
我们可以通过 Verilog 硬件描述语言进行实现,
SPI 通信协议主要有两根线进行数据传输,一个是 SCLK 时钟线,一个是 SDIN 数据线,
在SCLK 的上升沿时,设备对 SDIN 数据线进行一次采样,
所以,SDIN 的电平持续时间应当尽量为,前一个脉冲的下降沿到后一个时钟的下降沿,
这样与 SCLK 的上升沿刚好形成半个周期的时延,
其 Verilog 代码如下,
1 |
|
2.4 加速器仿真
左侧 “IP INTEGRATOR” -> “Open Block Design” 打开块设计,选中我们创建的 IP 核,进行编辑(Edit in IP Packager),
在新打开的窗口添加上述代码,左上角 “Sources” 中点击加号新建,或者是将 .v 文件放置至 IP 核目录下 hdl 文件夹再添加路径即可;
点击综合分析代码错误,若未提示有误,则可进入仿真,
首先将 top (也就是我们新建的文件)设置为顶层,
左侧工作区,点击 “Run Simulation” 选择行为仿真,
仿真结果如下图,
在 write 信号出现后,busy 跳为高电平,内部开始计数,
SCK 产生上升沿,读取 SDIN 数据,将其写入寄存器中,
八次读取数据为 8’b1010 1010,即给的输入 din 的值 aa,(一次读取 din 的一位,由高位到低位)
左上角工作区 “top” -> “U1”,可以右击将 cntr 也列入波形视窗中观察其变化,
添加完成后点击 “Relunch Simulation” 即可,如下所示,
计数器由0开始计数一直到19再清零,可以看出模块的功能能够得以实现
2.5 将加速器写入 IP 核
打开左上角工作区 “Soureces” -> “Design Sources” -> “my_OLED_ip_v1_0” -> “my_OLED_ip_v1_0_S00_AXI_inst:my_OLED_ip_v1_0_S00_AXI”,
大约在120行左右位置我们曾添加了
1 | assign {OLED_DC, OLED_RES, OLED_SCLK, OLED_SDIN, OLED_VBAT, OLED_VDD} = slv_reg0[5:0]; |
为了使 OLED 能够通过加速器传递信号,而非都通过 PS 部分,所以要将其分离,
我们要将 DC,SCLK,SDIN 拿出将其加速,所以进行如下修改,
1 | assign { OLED_VBAT, OLED_VDD } = slv_reg0[1:0]; |
在写信号出现时,数据开始写入寄存器,由于第0个寄存器最开始用于控制每一个bit,
现在可以用其他的寄存器来存储数据,
例如可以将 write 定义为,要对第二个寄存器进行写入的标志,
我们可以找到写入寄存器部分的描述,在大约220行附近,在前面添加我们新增的代码,
1 | //将写命令放入寄存器1,写数据放入寄存器2,并产生 write 信号 |
160行附近, axi_awready 信号表示写地址就绪,
200行附近, axi_wready 信号表示写就绪,
处理器在写寄存器之前需要知道寄存器是否就绪,需要两个信号都变为 1 时,才能进行写入,否则进行等待,所以可以让 busy 信号挂在这上面,所以在前边定义一个
1 | wire busy; |
在调用两个信号之前,
在160行附近,将写地址就绪改为 busy 取反的判断,
当 busy 为 1 时证明正在写入,所以 axi_awready 为 0 以表示寄存器未就绪,
1 | if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en) |
同理,200行附近的写地址就绪信号同样需要进行更改,
1 | if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en ) |
修改完毕后,保存并进行分析检查语法错误;
分析完毕后 Re-Package IP 核进行重封装即可;
2.6 在SDK内进行更改
更新顶层设计中的 IP 核,生成比特流文件,接下来进入SDK进行改动,
主要对 write_cmd 与 write_data 进行更改,
32位总线类型,4字节,也就是8位,所以寄存器 2 所对应的及地址应该 + 8,
同理,写命令为 + 4,
所以函数为:
1 | void write_cmd(u8 data) |
将比特流文件与PS端文件写入 FPGA.
2.7 问题记录
烧录PS端出现问题:(但PL端能够正常烧录,通过其他实验验证芯片并未出现问题)
1 | Error while launching program: |
以及,
1 | ERROR: |
有时还会存在,
1 | Error while launching program: |
有的时候烧录不报错但运行的结果很显然不符合预期;
导致原因可能有
- 调试接口不正常
- 目标板供电不足
- DDR3配置有误,原理图DDR3为两块16位的 MT41K128M16JT-125:K
- PL资源改动未及时export到SDK
- 跳线帽未正常切换
检查了上面的情况均未成功运行,还尝试过的解决方案
- 配置DDR3型号与原理图一致
- DDR3的起止地址为 0x00100000 ~ 0x1FFFFFFF 并未存在问题
- 换调试接口(但是没有JTAG线,之前的实验烧录PS端也是通过usb线完成的)
- 重开一次项目,使用 Vivado 的自动配置配置 ZYNQ7 的 IP核
- Copy 了资料中的 .v 文件,并将其与工程文件适配
- 更改 Run Configurations ,勾选 Reset entire system
2.8 问题解决
烧录的问题问了问大佬学长(´。_。`),先是晶振的频率设置出了点问题,在之前找原因改的时候把 33.333333 MHz 改成了 50 MHz,导致驱动不起来…
然后学长又表演了一手单核程序的固化,帮俺解决了不能烧录的问题,(我太菜了,非常非常感谢学长(´。_。`),但是研究生实验室压迫感满满直接逃跑…),单核固化程序的记录会在下一P。
2.9 运行结果
运行结果如下图,
3. 总结
加速的过程会让软件部分节省多条指令,但在运行的过程中仍有可能造成写入时处理器一直等待写完毕而造成无操作挂起的状态,导致速度受到限制;
在接口上进行优化,或者使 DELAY_HI_BIT 循环计数器尽可能小,也能使速度提升;
本质上加速的方法:加入 FIFO (先入先出队列),将要写入的队列写入 FIFO ,便可不再进行等待,直接返回,以协调处理器使其不会挂起;
导入sdk的时候最好直接把sdk整个文件夹扬了再导入一次,记得备份已经写好的函数;
生成比特流文件的时候,将 Number of jobs 改为 16
菜就多练😕