ZedBoard学习手记(二) 开发自定义AXI总线外设IP核
想要发挥ZYNQ芯片的特长,让整个系统协同工作起来,就需要将PS与PL两部分结合在一起,在Cortex-A9核和FPGA逻辑资源之间建立通信的通道,这条通道就是AXI总线。ZedBoard推出的官方例子中已经介绍了如何将Xilinx做好的AXI总线IP(如AXI_Timer、AXI_GPIO等)添加到工程中,而下面就让我们一起来自己编写一个简单的AXI总线设备——读取板上的8个Swtich状态,并控制8个LED的开关。
有人会问我不了解AXI总线啊怎么办,无须担心,Xilinx已经为我们设计了建立向导,可以自动生成一个设备模板,即使不了解AXI协议,也可以轻松完成设计工作。
首先通过PlanAhead建立一个系统工程,导入ZedBoard的xml文件:zedboard_RevC_v2.xml。这步操作还不清楚的童鞋请在ZedBoard官网上下载ZedBoard_CTT_v14.1资料并跟随该文档进行扫盲。这个资料很详细,也是Step By Step的,可以帮助完成基本的操作学习。
工程建好后,进入XPS界面,选择Hardware→Create or Import Peripheral,开始创建外设。
<ignore_js_op>![1351174073_4290b362.jpg 1351174073_4290b362.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100445z8jn706xzwu90nxx.jpg)
![1351174073_4290b362.jpg 1351174073_4290b362.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100445z8jn706xzwu90nxx.jpg)
在弹出的窗口中选择新建模板,建立一个外设。
<ignore_js_op>![500_400-1351174164_d3e940c0.jpg 500_400-1351174164_d3e940c0.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/10053223y3e7ucy2fylete.jpg)
![500_400-1351174164_d3e940c0.jpg 500_400-1351174164_d3e940c0.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/10053223y3e7ucy2fylete.jpg)
Next后选择集成到XPS系统中,这样新建的外设就会保存在edk目录下的pcores文件夹中。
<ignore_js_op>![500_400-1351174196_a3e68012.jpg 500_400-1351174196_a3e68012.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1005469fqx8whhq58x9ec3.jpg)
![500_400-1351174196_a3e68012.jpg 500_400-1351174196_a3e68012.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1005469fqx8whhq58x9ec3.jpg)
接着Next,为自己的IP起个名字,这里叫my_gpio,全称就是my_gpio_v1_00_a了,注意这里的名字不能用大写字母。
<ignore_js_op>![500_400-1351174258_88700e6b.jpg 500_400-1351174258_88700e6b.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100606mmt0gc3l0l0lqc13.jpg)
![500_400-1351174258_88700e6b.jpg 500_400-1351174258_88700e6b.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100606mmt0gc3l0l0lqc13.jpg)
之后选择总线的类型,可以看出这里提供了几个AXI总线设备的变种形势,可以使设备具备猝发操作及大量数据传输的能力,这里我们选AXI4-Lite形式,也就是最简单的类型。
<ignore_js_op>![500_400-1351174279_8edcb90c.jpg 500_400-1351174279_8edcb90c.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100623a7z7wupaw1uz1m3i.jpg)
![500_400-1351174279_8edcb90c.jpg 500_400-1351174279_8edcb90c.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100623a7z7wupaw1uz1m3i.jpg)
下一步进行几个可选项配置,包括软件复位、主从模式等,勾选中间两项即可,虽然现在用不到软件复位。
<ignore_js_op>![500_400-1351174336_fd3ecc14.jpg 500_400-1351174336_fd3ecc14.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100638vxoionvairo7iiii.jpg)
![500_400-1351174336_fd3ecc14.jpg 500_400-1351174336_fd3ecc14.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100638vxoionvairo7iiii.jpg)
设置寄存器数量,这里我们设置两个32位寄存器(实际使用低8位,其它部分Not Care),一个用来控制LED,另一个用来读取SW状态。
<ignore_js_op>![500_400-1351174426_57052fb4.jpg 500_400-1351174426_57052fb4.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100831ctutothsueq9seys.jpg)
![500_400-1351174426_57052fb4.jpg 500_400-1351174426_57052fb4.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100831ctutothsueq9seys.jpg)
选择需要的IP连接信号,这些是与用户逻辑对接的信号,保持默认即可。
<ignore_js_op>![500_400-1351174458_4d6e8ed6.jpg 500_400-1351174458_4d6e8ed6.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100844ndltddv8yuo9tnvl.jpg)
![500_400-1351174458_4d6e8ed6.jpg 500_400-1351174458_4d6e8ed6.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100844ndltddv8yuo9tnvl.jpg)
这一步可以建立一个总线上的仿真设备,不需要,直接Next。
<ignore_js_op>![500_400-1351174563_a776f929.jpg 500_400-1351174563_a776f929.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100856z2gqrhrcqu82iu2c.jpg)
![500_400-1351174563_a776f929.jpg 500_400-1351174563_a776f929.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100856z2gqrhrcqu82iu2c.jpg)
来到另一个配置画面,第一个选项是使用Verilog来设计用户逻辑;第二个选项建立一个ISE的工程文件,方面写代码;第三个选项让XPS自动生成底层的控制代码。
<ignore_js_op>![500_400-1351175260_7114eb13.jpg 500_400-1351175260_7114eb13.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100908bnpcn5nbnqqmzqqp.jpg)
![500_400-1351175260_7114eb13.jpg 500_400-1351175260_7114eb13.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100908bnpcn5nbnqqmzqqp.jpg)
设置结束以后,在XPS的工程目录下就能找到IP的文件夹了,兔子这里的路径是” .\Hello_Zed.srcs\sources_1\edk\module_1\pcores\my_gpio_v1_00_a”。里面有三个文件夹,hdl文件夹包含了user_logic.v和my_gpio.vhd。前者就是我们要修改的用户逻辑文件,后者则是用来连接AXI与用户逻辑,并封装IP核的VHDL文件。这个文件只能是VHDL的,所以像兔子这样只会Verilog的就很苦闷了,还好里面的内容很简单,看一下就能明白。在devl\projnav文件夹下可以找到刚生成的ISE工程,用Project Navigator打开my_gpio.xise,这样就可以开始编辑user_logic了。
<ignore_js_op>![500_400-1351175331_44a9cd0e.jpg 500_400-1351175331_44a9cd0e.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100920aif5zicibdd68ggg.jpg)
![500_400-1351175331_44a9cd0e.jpg 500_400-1351175331_44a9cd0e.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100920aif5zicibdd68ggg.jpg)
user_logic中除了一些定义和信号连接以外,就是两个always块,第一个实现AXI总线向用户寄存器写入数据,第二个则是总线从寄存器中读取数据。需要注意的是,这里的AXI总线已经通过Xilinx的AXI Lite IP Interface进行了中转,到用户这里就变成了非常简单的读写逻辑。
为了控制板上的硬件,我们定义两组信号和相应的寄存器:SW_In信号连接8个Switch,LED_Out连接8个LED。
- input [7:0] SW_In;
- output [7:0] LED_Out;
在这里,规定寄存器0为Swtich状态寄存器,寄存器1为LED控制寄存器。添加一个always块,使写入寄存器1的数据在LED_Out上更新。
- always @( posedge Bus2IP_Clk )
- begin
- if ( Bus2IP_Resetn == 1'b0 )
- begin
- LED_Out <= 0;
- end
- else begin
- LED_Out[7:0] <= slv_reg1[7:0];
- end
- end // LED_OUTPUT_PROC
同时添加逻辑让Switch的状态通过简单的同步(不是去抖啊)写入寄存器0。
- // implement slave model register read mux
- always @( slv_reg_read_sel or slv_reg0 or slv_reg1 )
- begin
- case ( slv_reg_read_sel )
- 2'b10 : slv_ip2bus_data <= slv_reg0;
- 2'b01 : slv_ip2bus_data <= slv_reg1;
- default : slv_ip2bus_data <= 0;
- endcase
- end // SLAVE_REG_READ_PROC
- always @( posedge Bus2IP_Clk )
- begin
- if ( Bus2IP_Resetn == 1'b0 )
- begin
- SW_Reg0 <= 0;
- SW_Reg1 <= 0;
- slv_reg0 <= 0;
- end
- else begin
- SW_Reg0 <= SW_In;
- SW_Reg1 <= SW_Reg0;
- slv_reg0[7:0] <= SW_Reg1[7:0];
- end
- end // SWTICH_INPUT_PROC
之后还要修改读写逻辑,将case ( slv_reg_write_sel )下的2'b10项注释掉,令寄存器0只读(对写入的数据Not Care),寄存器1则可以读写。如此,我们的用户逻辑就完成了,简单吧。别忘了还要在my_gpio.vhd中为用户逻辑添加端口,两组信号在整个IP的对外接口中分别命名为GPIO_LED_Out和GPIO_SW_In。
<ignore_js_op>![500_400-1351175346_6ce5e10.jpg 500_400-1351175346_6ce5e10.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100932rd5wrmvfwhmdfg8v.jpg)
![500_400-1351175346_6ce5e10.jpg 500_400-1351175346_6ce5e10.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100932rd5wrmvfwhmdfg8v.jpg)
具体的代码内容在此:
<ignore_js_op>
my_gpio_hdl.rar
![](http://www.eeboard.com/bbs/static/image/filetype/rar.gif)
双击Synthesize,检查一下有没有问题,没问题的话就可以关掉Project Navigatorl 。
<ignore_js_op>![500_400-1351175380_f55812ea.jpg 500_400-1351175380_f55812ea.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100941cvimwxnyo93nxamx.jpg)
![500_400-1351175380_f55812ea.jpg 500_400-1351175380_f55812ea.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100941cvimwxnyo93nxamx.jpg)
回到XPS界面,我们的自定义IP已经在列表中了,但是这个时候将IP添加到工程中,刚才进行的编辑并不生效,GPIO端口并没有显示在IP核的框图中,这点兔子没有搞明白是为什么,可能要手动修改MPD文件吧。于是我选择将这个IP再添加一次(不得已而为之,有什么好办法望不吝赐教),还是刚才的方法,只不过这回选择导入已有的外设,这种方法也可以用于导入其他已有的IP核。
<ignore_js_op>![500_400-1351175555_f0658897.jpg 500_400-1351175555_f0658897.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100951izmmot27fpx2ifiu.jpg)
![500_400-1351175555_f0658897.jpg 500_400-1351175555_f0658897.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/100951izmmot27fpx2ifiu.jpg)
依旧起名为my_gpio,在弹出的提示中选Yes覆盖,之后一路Next到HDL Source Files窗口,选择IP核的\data\_my_gpio_xst.prj文件。
<ignore_js_op>![500_400-1351175619_2962a6f.jpg 500_400-1351175619_2962a6f.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1010072hhhdv92hks3svxv.jpg)
![500_400-1351175619_2962a6f.jpg 500_400-1351175619_2962a6f.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1010072hhhdv92hks3svxv.jpg)
继续Next,直到这里选择总线类型为AXI4-Lite Slave类型。
<ignore_js_op>![500_400-1351175841_21817f41.jpg 500_400-1351175841_21817f41.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1010493fbixbclubfuzwfa.jpg)
![500_400-1351175841_21817f41.jpg 500_400-1351175841_21817f41.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/1010493fbixbclubfuzwfa.jpg)
接着在参数窗口中指定High Address的参数为C_HIGHADDR。
<ignore_js_op>![500_400-1351175900_1d4d9473.jpg 500_400-1351175900_1d4d9473.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101109apzud00qsks9oud2.jpg)
![500_400-1351175900_1d4d9473.jpg 500_400-1351175900_1d4d9473.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101109apzud00qsks9oud2.jpg)
好了,Next到结束,这时刚才进行的修改已经生效并被成功导入了,现在双击my_gpio,保持默认选项不管,将其添加到系统中。
<ignore_js_op>![500_400-1351175927_1be2e863.jpg 500_400-1351175927_1be2e863.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101118ell6kkul5u8lrlmz.jpg)
![500_400-1351175927_1be2e863.jpg 500_400-1351175927_1be2e863.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101118ell6kkul5u8lrlmz.jpg)
在Graphic视图中,可以看到my_gpio的框图,包括一组已经连接的AXI总线,和两组我们刚才定义的端口。
<ignore_js_op>![500_400-1351176034_bc63f600.jpg 500_400-1351176034_bc63f600.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101127wzer2ix2rrefqrf2.jpg)
![500_400-1351176034_bc63f600.jpg 500_400-1351176034_bc63f600.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101127wzer2ix2rrefqrf2.jpg)
进入Port界面,将LED和SW端口设置为对外,确保最后看起来是这个样子。
<ignore_js_op>![500_400-1351176068_138ab731.jpg 500_400-1351176068_138ab731.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101137gcpnstwt6x4tgy6d.jpg)
![500_400-1351176068_138ab731.jpg 500_400-1351176068_138ab731.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101137gcpnstwt6x4tgy6d.jpg)
好了,关闭XPS,回到PlanAhead,生成Top HDL(如果之前做了可忽略)。
<ignore_js_op>![500_400-1351176104_37efe32c.jpg 500_400-1351176104_37efe32c.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101147qyjum4jyuo3fkpe3.jpg)
![500_400-1351176104_37efe32c.jpg 500_400-1351176104_37efe32c.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101147qyjum4jyuo3fkpe3.jpg)
新建一个约束文件,并设置SW和LED的端口约束。
<ignore_js_op>![500_400-1351176164_9eaee2f3.jpg 500_400-1351176164_9eaee2f3.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101200mqo0ctco8o10mo99.jpg)
![500_400-1351176164_9eaee2f3.jpg 500_400-1351176164_9eaee2f3.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101200mqo0ctco8o10mo99.jpg)
约束内容如下(建议手动输入,如果Copy了什么非法字符是很麻烦的,害我查了好久):
<ignore_js_op>![500_400-1351176209_74e4c68b.jpg 500_400-1351176209_74e4c68b.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/10121026sctckdd62674yr.jpg)
![500_400-1351176209_74e4c68b.jpg 500_400-1351176209_74e4c68b.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/10121026sctckdd62674yr.jpg)
选择Generate Bitstream,系统会自动完成编译综合工作,最后生成包含PL配置信息的System.bit文件。
<ignore_js_op>![500_400-1351176252_9ae6bdbb.jpg 500_400-1351176252_9ae6bdbb.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101219nzgw0woaqg0vf8qq.jpg)
![500_400-1351176252_9ae6bdbb.jpg 500_400-1351176252_9ae6bdbb.jpg](http://www.eeboard.com/bbs/data/attachment/forum/201301/06/101219nzgw0woaqg0vf8qq.jpg)
至此,my_gpio自定义外设就完成了,这个PL现在可以通过AXI总线与PS通信——让然还需要软件支持了,呵呵。
下篇将会介绍如何裸机软件调试外设,其步骤与ZedBoard_CTT文档中大同小异。