添加按键消抖IP
由于ddr读写IP的axi_init_axi_txn接入的是按键,这里按键按下会产生抖动,axi_init_axi_txn与好多读写信号关联,如果不添加消抖IP,在按键按下的时,产生的毛刺会进行影响后续的操作,从而导致读写操作的错误,也就是读写操作的指示灯会亮起。
系统复位后, 状态机处于初始状态,在该状态下等待外部输入的启动传输脉冲 init_txn_pulse。一旦检测到 init_txn_pulse 为高电平,状态机跳转到 INIT_WRITE 状态。
在 INIT_WRITE 状态下, 状态机拉高 start_single_burst_write 信号, 来不断地启动 AXI4 Master 接口对Slave 端大小为 4KB 的存储空间进行突发写操作。写操作完成后, write_done 信号会拉高,状态机进入INIT_READ 状态。
在 INIT_READ 状态下, 状态机拉高 start_single_burst_read 信号, 不断地启动 AXI4 Master 接口对 Slave端同一存储空间进行突发读操作, 同时将读出的数据与写入的数据进行对比。读操作完成后, read_done 信号拉高,状态机进入 INIT_COMPARE 状态。
在 INIT_COMPARE 状态下, 判断 AXI4 接口在读写过程中的是否发生错误, 并将错误状态赋值给ERROR 信号, 然后将 compare_done 信号拉高,表示一次读写测试完成。最后跳转到 IDLE 状态,等待下一次读写操作的启动信号。
这里的消抖模块直接添加之前写过的按键消抖模块即可,这里给出我的设计:
module key_filter( Clk, //50M时钟输入 Rst_n, //模块复位 key_in, //按键输入 key_flag, //按键标志信号 key_state //按键状态信号 ); input Clk; input Rst_n; input key_in; output reg key_flag; output reg key_state; localparam IDEL = 4'b0001, FILTER0 = 4'b0010, DOWN = 4'b0100, FILTER1 = 4'b1000; reg [3:0]state; reg [19:0]cnt; reg en_cnt; //使能计数寄存器 //对外部输入的异步信号进行同步处理 reg key_in_sa,key_in_sb; always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin key_in_sa <= 1'b0; key_in_sb <= 1'b0; end else begin key_in_sa <= key_in; key_in_sb <= key_in_sa; end reg key_tmpa,key_tmpb; wire pedge,nedge; reg cnt_full;//计数满标志信号 //使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态 always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin key_tmpa <= 1'b0; key_tmpb <= 1'b0; end else begin key_tmpa <= key_in_sb; key_tmpb <= key_tmpa; end //产生跳变沿信号 assign nedge = !key_tmpa & key_tmpb; assign pedge = key_tmpa & (!key_tmpb); always@(posedge Clk or negedge Rst_n) if(!Rst_n)begin en_cnt <= 1'b0; state <= IDEL; key_flag <= 1'b0; key_state <= 1'b1; end else begin case(state) IDEL : begin key_flag <= 1'b0; if(nedge)begin state <= FILTER0; en_cnt <= 1'b1; end else state <= IDEL; end FILTER0: if(cnt_full)begin key_flag <= 1'b1; key_state <= 1'b0; en_cnt <= 1'b0; state <= DOWN; end else if(pedge)begin state <= IDEL; en_cnt <= 1'b0; end else state <= FILTER0; DOWN: begin key_flag <= 1'b0; if(pedge)begin state <= FILTER1; en_cnt <= 1'b1; end else state <= DOWN; end FILTER1: if(cnt_full)begin key_flag <= 1'b1; key_state <= 1'b1; state <= IDEL; en_cnt <= 1'b0; end else if(nedge)begin en_cnt <= 1'b0; state <= DOWN; end else state <= FILTER1; default: begin state <= IDEL; en_cnt <= 1'b0; key_flag <= 1'b0; key_state <= 1'b1; end endcase end always@(posedge Clk or negedge Rst_n) if(!Rst_n) cnt <= 20'd0; else if(en_cnt) cnt <= cnt + 1'b1; else cnt <= 20'd0; always@(posedge Clk or negedge Rst_n) if(!Rst_n) cnt_full <= 1'b0; else if(cnt == 20'd999_999) cnt_full <= 1'b1; else cnt_full <= 1'b0; endmodule
添加完模块后系统设计如下:
注:axi_init_axi_txn是上升沿有效,这里为了保证系统上电后是初始默认随机状态,要确保按键未按下给启动脉冲时,是低电平。因为PYNQZ2开发板按键默认电位是低,按下为高,这里不用进行处理,若按键按下后为低,默认拉高,这里可以对按键进行添加非逻辑的IP进行取反。(使用 utility vector logic IP完成配置)
双击DDR读写的IP核进行配置,这里没有用到user的接口所以都设置为0,如图:
对于Base address,我们可以查看下接口的地址范围,避免数据操作超过可操作范围,这里我们就取中间值0X10000000。
然后我们进行generate output product 然后生成HDL封装。接着就对应引脚进行引脚约束即可(PYNQ的粉色开发板可以直接引用这个约束):
##LEDs set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { m0_axi_error_0 }]; #IO_L6N_T0_VREF_34 Sch=led[0] set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { m0_axi_txn_done_0}]; #IO_L6P_T0_34 Sch=led[1] ##Buttons set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { key }]; #IO_L4P_T0_35 Sch=btn[0]
完成约束后进行综合布局布线,等待生成bit流文件。
bit文件生成后在FILE处,点击导出硬件资源(包含bit流文件),接着launch SDK。
SDK软件部分
打开SDK后,新建application project。在main.c中输入以下代码:
#include "stdio.h" #include "xil_cache.h" #include "xil_printf.h" #include "xil_io.h" int main(){ int i; char chardata; Xil_DCacheDisable(); printf("AXI4-FULL RW TEST~\n\r"); while(1){ scanf("%c",chardata); if(chardata="y"){ printf("start\r\n"); for(i=0;i<4096;i=i+4){ printf("%d is %d\n",i,(int)(Xil_In32(0x10000000+i))); } } } return 0; }
代码简要说明
这里使用的IP我们设定成不需要进行缓存的,所以在main函数中调用Xil_DCacheDisable();
。使用Xil_In32(),对DDR对应位置的数据进
行读取。参数只需要传递所要读取的地址即可。因为一次写入的数据是32位的,每个地址的数据位宽是8位,所以在for循环中使用了i=i+4。
在串口中使用printf("%d is %d\n",i,(int)(Xil_In32(0x10000000+i)));对相应地址的数据进行读取显示。
运行效果
当按键未按下时,也就是未进行写入操作直接读取数据,DDR中的数据是默认的随机状态,如下所示:
当按键按下后,IP完成数据写入操作,数据是从1-1024自增的
如何波形进行debug?
这里我们选中要进行DEBUG的数据信号右击选中debug
然后点击自动连接,完成debug功能搭建
综合后会多出ILA的IP进行波形分析帮助DEBUG。
然后打开硬件设备,如下图在ila界面即可看到我们debug的波形数据了。
添加触发条件,标号2是单次触发,标号1是一直运行debug抓取波形。