一、实验环境及目的
板卡:AX7010
Vivado版本:2017.4
开发机:I5 2.2GHZ 8GB WIN7_X64
参考文档:《ALINX黑金ZYNQ7000开发平台配套教程》
实验目的:掌握ZYNQ PL端的开发流程,创建自定义IP的方法,在ReWorks下利用该IP对RTC进行访问。
黑金给出的实验是如何创建自定义IP,但是并没有对代码进行解读,可能对于FPGA会的人比较简单,但是我还是想看看IP核里面具体的实现代码,然后访问控制也是在sdk下去做的,这里我在自己的国产RTOS——ReWorks下,进行驱动。
二、ReWorks介绍
ReWorks是中国电子科技集团有限公司第三十二研究所自主研制的嵌入式实时操作系统,目前已经广泛应用于国防军工,轨道交通和工业控制等领域,具有以下特性:
(1) 强实时性
ReWorks的内核可抢占,中断可嵌套,具有快速响应的中断管理机制和高效的任务调度、上下文切换算法,提供优先级继承、优先级天花板协议,系统调度可预测。
(2) 可裁剪性
ReWorks采用微内核的体系结构和面向对象的设计方法,提供组件化的内核服务,最大限度提高内核的可裁剪性。
(3) 适用性
ReWorks支持多种主流的CPU/DSP芯片和硬件体系结构,提供典型的设备驱动。支持应用的动态加/卸载,并提供丰富的面向领域的外围组件,如通信、CLDC/MIDP、图形组件等,满足军民应用需求。
(4) 标准化
ReWorks支持C/C++,提供符合POSIX 1003.13-2003/POSIX 1003.1-2001规范的系统调用接口,保障应用的可移植性。
(5) 可靠性
ReWorks提供基于MMU的存储域保护机制,支持用户态模式下的设备驱动管理,实现故障监测、隔离和恢复框架,防止用户应用程序的故障导致系统崩溃,增强了系统的可靠性。
(6) 可扩展性
ReWorks提供核心扩展接口,支持包含驱动在内的组件动态插拔,支持调度算法的注入与动态配置,提高系统可维护性。
(7) 易用性
配套的ReDe开发环境集设计、开发、配置、调试、仿真、测试、运行、部署为一体,方便用户使用,提高了应用开发效率。
(8) 可持续性
ReWorks拥有可控的源代码,具备优秀的维护和升级能力,部分集成组件及子系统源自开放源码,与相关组织同步发展。
ReWorks是一个高可靠、强实时嵌入式实时操作系统,采用微内核及组件技术,能根据应用需要对操作系统内核进行配置、裁剪、扩展与定制,具备较强的伸缩能力。
ReWorks的体系结构如图2‑1所示。
图2‑1 ReWorks体系结构
ReWorks嵌入式实时操作系统包括以下功能模块:
(1) 核心模块
提供基本的操作系统功能,包括任务管理、异常/中断管理、时钟/定时器管理、内存管理、信号量管理、消息队列、管理信号管理、事件管理、错误管理,为了兼容和移植的需要,ReWorks提供了VxWorks兼容接口,ReWorks接口(包括符合Posix规范的接口和扩展功能接口)。
(2) 总线和驱动模块
支持X86/ARM/PowerPC/龙芯/DSP/ZYNQ处理器,提供串口、中断、鼠标、键等字符设备、硬盘、U盘、SD卡、FLASH等存储设备、网络设备、图形设备以及包括PCI、USB协议等在内的主流硬件设备驱动或总线适配。
(3) 扩展模块
包括:
1) I/O模块:对上层应用程序提供统一的接口,支持对硬件设备的统一访问;
2) 动态加卸模块:支持库的动态加载和卸载,实现软件功能构件的在线装载与卸载;
3) 文件系统:提供文件系统基本框架,支持根文件系统,能够挂接多种文件系统;提供高可靠文件系统支持掉电保护,防止系统崩溃,对系统中的元数据和用户数据提供保存和保护功能;
4) 网络协议栈:支持TCP、UDP、IP、ICMP、ARP等网络协议,提供标准BSD socket编程接口;
5) 图形系统:提供了图形引擎的基本操作,包括画线、画/填充矩形、画/填充多边形、区域拷贝、图像显示、字体显示、设置颜色、输入设备事件处理等,提供丰富图形控件,支持用户界面的开发。
三、创建自定义IP
1.创建vivado工程
工程名称为user_ip_rtc
选择RTL project,勾选Do not specify sources at this time,后面我们自己添加源文件
选择开发板型号,然后点击next ,点击finish完成,如下图:
2.创建block design
如下图,双击zynq7000的ip,配置ddr的型号及uart的io,如下图:
3.创建自定义IP
3.1 ax7010的rtc
可以看到RTC是接到PL端的,搜索原理图发现在bank34和bank35的IO上,这里跟后面的IO约束文件有关系。具体芯片型号是常用的DS1302,外接32768khz的晶振,如果有电池,可以一直时间tick。一般来说没有做过FPGA的,对I2C的了解就是通过CPU自带的I2C控制器对芯片进行访问,控制SDA和SCL两个线,还有一个RST信号。这里自定义IP,我理解的是根据DS1302的读写时序,用IO进行模拟,其实也可以用PS的GPIO进行模拟,这是目前我理解的,具体的需要看下IP的代码是如何实现的。
3.2创建自定义IP
tools->create and package new ip,选择创建新的axi4 ip,如下图:
修改ip名称和存放位置,其余ip的版本和描述也可以自行修改,下一步,选择创建的ip的名称,axi的类型,数据宽度和寄存器数量,如下图:
这里有几个要搞清楚一下,第一个是axi接口的种类,这里为什么选择Lite,我看axi的种类有lite ,full和stream三种,《手册》解释具体如下:
AXI-Lite:
具有轻量级,结极简单的特点,适合小批量数据、简单控制场合。不支持批量传输,读写时一次叧能读写一个字(32bit)。 主要用亍访问一些低速外设和外设的控刢。
AXI4:
接口和 AXI-Lite 差丌多,叧是增加了一种功能就是批量传输,可以连续对一片地址迕行一次性读写。 也就是说具有数据读写的burst 功能。上面两种均采用内存映射控制方式,即 ARM 将用户自定义 IP 编入某一地址进行访问,读写时就像在读写自己片内 RAM,编程也很方便,开发难度较低。代价就是资源占用过多,需要额外的读地址线、写地址线、读数据线、写数据线、写应答线这些信号线。
AXI-Stream:
返是一种连续流接口,不需要地址线(很像 FIFO,一直读或者一直写就行)。对于这类 IP,ARM 不能通过上面的内存映射方式控制(FIFO 根本没有地址的概念),必项有一个转换装置,例如 AXI-DMA 模块来实现内存映射到流式接口的转换。 AXI-Stream 适用的场合有很多:视频流处理;通信协议转换;数字信号处理;无线通信等。其本质都是针对数值流构建的数据通
路,从信源(例如 ARM 内存、 DMA、无线接收前端等)到信宿(例如 HDMI 显示器、 高速AD 音频输出等)构建起连续的数据流,特别接口适合做实时信号处理。这里I2C的接口属于简单的低速总线,所以选择axi_lite即可,寄存器的位宽和axi的类型匹配。
点击next,finish,ip的创建完成,下一步是edit。
3.3 edit ip
点击IP catalog,在user ip目录下,axi4 peripheral下,可以看到创建的自定义IP,如下图:
右键该ip,选择“edit in IP packager”,然后确定
打开.v文件,在下图位置添加管脚定义:
在下图rtc_ip_v1_0.v位置添加RTC管脚的例化:
在rtc_ip_v1_0_s00_AXI.v文件下对应位置添加如下代码:
同样在该文件下,把下图未知的reg0和reg1改为信号类型:
这里为什么要把reg 改成wire,上一篇文章刚知道,reg就是类似一个存储变量,保存最后一次赋值的,这里wire我理解的是它只是作为信号之间的链接,并没有存储的功能,状态随时都会改变的。reg0和reg1具体做什么的,等下再看。
接下来黑金的手册里,将reg0和reg1的写入屏蔽掉了,因为对于rtc来说,读取时间的功能确实不需要写入,我是这样的理解的。
更改如下:
最后,在该文件的底部,添加自己的逻辑代码,这部分应该是对RTC读写的实现,但是里面调用了很多外部的接口,应该是其他模块提供的,代码如下:
wire [7:0] Time_year;
wire [7:0] Time_month;
wire [7:0] Time_date;
wire [7:0] Time_hour;
wire [7:0] Time_munite;
wire [7:0] Time_second;
//wire time_set_en=slv_reg1[31];
wire [7:0] Time_year_set=slv_reg3[23:16];
wire [7:0] Time_month_set=slv_reg3[15:8];
wire [7:0] Time_date_set=slv_reg3[7:0];
wire [7:0] Time_hour_set=slv_reg2[23:16];
wire [7:0] Time_munite_set=slv_reg2[15:8];
wire [7:0] Time_second_set=slv_reg2[7:0];
assign slv_reg0={8'd0,Time_hour,Time_munite,Time_second};
assign slv_reg1={8'd0,Time_year,Time_month,Time_date};
rtc_time U1 (
.CLK( S_AXI_ACLK ),
.RSTn( S_AXI_ARESETN ),
.Time_set_en(slv_reg3[31]),
.Time_year_set( Time_year_set ), //DS1302设置的年数据
.Time_month_set( Time_month_set ), //DS1302设置的月数据
.Time_date_set( Time_date_set ), //DS1302设置的日数据
.Time_second_set( Time_second_set ), //DS1302设置的秒数据
.Time_munite_set( Time_munite_set ), //DS1302设置的分数据
.Time_hour_set( Time_hour_set ), //DS1302设置的时数据
.Time_year( Time_year ), //DS1302读到的年数据
.Time_month( Time_month ), //DS1302读到的月数据
.Time_date( Time_date ), //DS1302读到的日数据
.Time_second( Time_second ), //DS1302读到的秒数据
.Time_munite( Time_munite ), //DS1302读到的分数据
.Time_hour( Time_hour ), //DS1302读到的时数据
.RST( DS1302_RST ),
.SCLK( DS1302_SCLK ),
.SIO( DS1302_SIO )
);
这里黑金提供了几个现成的.v文件供添加,我的路径是在:
F:\ZYNQ\AX7010\course_s1\course_s1\07_custom_rtc\custom_rtc.srcs\sources_1\bd\system\ipshared\3b34\hdl
右键design sources,添加file,结果如下:
接着,打开IP-XACT目录下的comonet.xml文件,找到port and interface,电机merge changes from ports and interfaces wizzard,如下图:
这时候会看到我们添加进去的rtc的管脚端口,如下图:
接下来对file groups,同样merge一下,如下图:
最后,选择review and pakage,选择re-package IP
repackege完成之后,点击完成,自动关闭ip核的设计,回到主界面。接下来就要把刚才创建的自定义IP添加到block当中,这时候已经可以看到我们自定义的IP在列表里了,如下图;
之后,点击自动布线,选中全部端口,让系统去自动布线,但是这时候,rtc的三个管脚并没有引出来,这时候右键 make external,手动引出来。如下图;
3.4生成bit文件
这以上之后,在source栏目下,system选择generate output projects,然后create HDL wrapper,结果如下:
然后为系统添加管脚约束文件,在constrants目录下,新建system.xdc文件,如下:
最后点击generate bitstream,生成比特流文件,这里我在生成bit文件的时候报错了,反复确认是否和教程有何不同,发现步骤每一步都是一样的,除了vivado的版本不一样,然后我用2017.4打开了黑金自带的工程,发现可以编译生成bit文件,这里错误提示如下:
[DRC NSTD-1] Unspecified I/O Standard: 3 out of 133 logical ports use I/O standard (IOSTANDARD) value 'DEFAULT', instead of a user assigned specific value. This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. To correct this violation, specify all I/O standards. This design will fail to generate a bitstream unless all logical ports have a user specified I/O standard value defined. To allow bitstream creation with unspecified I/O standard values (not recommended), use this command: set_property SEVERITY {Warning} [get_drc_checks NSTD-1]. NOTE: When using the Vivado Runs infrastructure (e.g. launch_runs Tcl command), add this command to a .tcl file and add that file as a pre-hook for write_bitstream step for the implementation run. Problem ports: DS1302_SCLK_0, DS1302_SIO_0, and DS1302_RST_0.
[DRC UCIO-1] Unconstrained Logical Port: 3 out of 133 logical ports have no user assigned specific location constraint (LOC). This may cause I/O contention or incompatibility with the board power or connectivity affecting performance, signal integrity or in extreme cases cause damage to the device or the components to which it is connected. To correct this violation, specify all pin locations. This design will fail to generate a bitstream unless all logical ports have a user specified site LOC constraint defined. To allow bitstream creation with unspecified pin locations (not recommended), use this command: set_property SEVERITY {Warning} [get_drc_checks UCIO-1]. NOTE: When using the Vivado Runs infrastructure (e.g. launch_runs Tcl command), add this command to a .tcl file and add that file as a pre-hook for write_bitstream step for the implementation run. Problem ports: DS1302_SCLK_0, DS1302_SIO_0, and DS1302_RST_0.
这里根据提示,似乎是IO口没有约束,会造成性能,完整性甚至板卡烧坏,但是反复确认过,io口的约束文件是添加正确的,端口也有对应的配置。查找了下解决方法,成功的生成了bit文件,这里解决方法如下:
根据提示,可以通过设置一段代码,来取消这个报错:set_property SEVERITY {Warning} [get_drc_checks NSTD-1].
编辑一个文件,名称后缀为.tcl,输入以下代码:
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
我的保存为uer_ip.tcl,放在了该vivado工程的根目录下,如下图:
然后邮件generate bitstream,在生成bit文件的配置项bitstream steeing里,将该文件添加进去,如下图:
点击OK,生成bit文件,成功生成了bit文件。
4.编写reworks代码,读取和设置时间
4.1配置说明
这里其实黑金提供了比较完整的测试代码,只要知道基地址和偏移地址,根据pl端的代码,也知道四个寄存器都是干嘛的,在reworks下,只要将对应的base addr映射相对应的长度到用户这边即可,因为我们的虚拟地址和物理地址是一一映射的,所以,在mmu里配置一下即可。
配置如下:
这里勾选串口和控制台驱动,用到串口打印输出,MMU的配置,在BSP中如下:
这里PS_DEVICE_ADDR是0x40000000,SIZE为1G,默认PL对PS映射的内存都在这个地址空间,这样我们就可以在应用中访问了。
4.2编写测试代码
在rede集成开发环境下,在自引导工程user_rtc_ip下,新建一个文件夹,名称为test.c,内容如下:
#include
#include
#include
#define RTC_BASEADDR 0x43C00000
#define Xil_Out32 OUT_REG32
#define Xil_In32 IN_REG32
#define RTC_IP_mWriteReg(BaseAddress, RegOffset, Data) \
Xil_Out32((BaseAddress) + (RegOffset), (u32)(Data))
#define RTC_IP_mReadReg(BaseAddress, RegOffset) \
Xil_In32((BaseAddress) + (RegOffset))
unsigned char test_time[6]; //format: second/minute/hour/date/month/year
int test_rtc(void)
{
u32 Delay,rtc_reg0,rtc_reg1;
int i;
unsigned char time_temp[6];
unsigned char second_old=0;
//Write RTC Register3, Setting date, year=16, month=10, date=10
RTC_IP_mWriteReg (RTC_BASEADDR, 12, 0x00160A0A);
//Write RTC Register3, Setting time, hour=12, month=00, date=00
RTC_IP_mWriteReg (RTC_BASEADDR, 8, 0x000C0000);
//Write RTC Register3, enable time setting
RTC_IP_mWriteReg (RTC_BASEADDR, 12, 0x80160A0A);
for (Delay = 0; Delay < 10000000; Delay++);
//Write RTC Register1, enable time setting
RTC_IP_mWriteReg (RTC_BASEADDR, 12, 0x00160A0A);
while(1) {
//read RTC Register1
rtc_reg1 = RTC_IP_mReadReg (RTC_BASEADDR, 4);
//read RTC Register0
rtc_reg0 = RTC_IP_mReadReg (RTC_BASEADDR, 0);
second_old=test_time[0];
time_temp[5] = (unsigned char)(rtc_reg1 >> 16); //RTC year
time_temp[4] = (unsigned char)(rtc_reg1 >> 8); //RTC month
time_temp[3] = (unsigned char)(rtc_reg1); //RTC date
time_temp[2] = (unsigned char)(rtc_reg0 >> 16); //RTC Hour
time_temp[1] = (unsigned char)(rtc_reg0 >> 8); //RTC Munite
time_temp[0] = (unsigned char)(rtc_reg0); //RTC Second
for (i=0;i<6;i++){
test_time[i] = time_temp[i]/16*10+time_temp[i]%16;//格式为: 秒 分 时 日 月 星期 年
}
if (second_old!=test_time[0]){
printf("current time is:20%d-%d-%d %d:%d:%d\n",test_time[5],test_time[4],test_time[3],test_time[2],test_time[1],test_time[0]); //convert string
}
}
return 0;
}
然后编译,生成reworks.elf
4.3测试运行
配置uboot的环境变量,代码如下:
setenv ipaddr 192.168.1.100;setenv serverip 192.168.1.128;tftpboot 0x10000000 reworks.elf;bootelf
因为黑金ax7010 uboot我这块板子默认不提供qspi的支持,无法保存环境变量,在securecrt下做一个button,每次按一下就可以直接tftp下载,这里网卡要配置成百兆全双工,千兆不支持tftp下载。
下载完成系统启动如下:
进入了控制台,输入test_rtc,发现没有输出,实验结果并没有出来,在SDK下拷贝黑金的代码,发现sdk下就做不通,看来bit文件还是有问题。
我用黑金做好的工程的bit烧写进去,启动reworks,发现可以成功的设置和读取rtc的值。输出如下:
结束语:
虽然实验没有在2017.4下成功,但是还是很详细的学习了自定义IP的创建和设计方法,具体的黑金的提供的例程,还关联了一个i2c的模块,一个控制模块,都是2015版本的,在添加的时候会有警告,不知道是不是版本不同的原因,以目前的水平,里面的代码也暂时没有办法去解读,虽然能看个大概,手里也没有2015.4的环境,暂时先搁置,等回头能发现这个问题了,再回来解决好了。