【精品博文】加班猫:AC701的DDR3测试读写(2)
这个是文章的开头,前些日子去面试,面试的人说,我没用过V6芯片,S10芯片是不是档次太低了。
如今我在fpga行业已经待了快6年时光,这6年时光,感觉到fpga路子越来越窄的趋势。然后今年我忽然觉得,fpga 只是一种工具,一个人没有必要局限一个工具的。你用高档的10G示波器,用100M示波器,只是用来测试信号的。何必在意一个工具的使用问题。没用V6和S10就不牛逼了?笑死。
最重要是你自己对行业的认知和行业的积累。话说你搞图像,用DSP也好,PC也好,安卓系统也好。只要你知道基本原理都不是事情。你懂基本算法和实现基本过程,你的工资永远大于那些只懂v6使用芯片傻逼要值钱的。
话说回来,我写这些博客不都是为了一个开发板而已,或者单独为了一个A7芯片?不是的。我只是通过这个博客来教会大家使用fpga基本接口而已。这些接口基本设计要求交给大家,这个才是你设计的中心点的。fpga基本功就是懂基本接口的设计。既然是工具,知道使用就行了。何必在意工具的牌子呢?
所以,希望你们不要闭着眼睛去瞎学东西,怎么搞fpga才叫牛逼,不是芯片的事情,是你行业的经验。比如搞拼接显示的fpga ,基本知道缩放,插值,融合边缘,锐化操作。LED行业,你要熟悉基本LED刷新操作,怎么提高亮度,降低闪屏问题。
以上都是废话。
这些接口知识,以后会开个fpga接口培训班,专门讲接口。比如PCIE,以太网(千兆,万兆),DDR3(DDR2,SDRAM,SRAM),USB3.0(USB2.0),SATA2.0,HDMI(DVI,VESA),SPI,UART,I2C,MIPI。至于时间和地点,估计得明年三月份后,请大家关注博客最新的消息。或者加加班猫的QQ 393597601。
上节说到DDR3的控制器,要注意的是,给用户接口是只有这些接口:
//Application interface ports .app_addr (app_addr), .app_cmd (app_cmd), .app_en (app_en), .app_wdf_data (app_wdf_data), .app_wdf_end (app_wdf_end), .app_wdf_wren (app_wdf_wren), .app_rd_data (app_rd_data), .app_rd_data_end (app_rd_data_end), .app_rd_data_valid (app_rd_data_valid), .app_rdy (app_rdy), .app_wdf_rdy (app_wdf_rdy), .app_sr_req (1'b0), .app_ref_req (1'b0), .app_zq_req (1'b0), .app_sr_active (app_sr_active), .app_ref_ack (app_ref_ack), .app_zq_ack (app_zq_ack), .ui_clk (dram_clk), .ui_clk_sync_rst (dram_rst), .app_wdf_mask (app_wdf_mask),
app_addr[ADDR_WIDTH – 1:0] 是ddr3的地址,精确到每个col地址,但是因为实际突发长度要求8所以每个数据都是4位开始。app_cmd[2:0]是命令,其实就两种,3'b001是读,3'B000是写。app_en是命令输入使能信号。
app_wdf_data这个是写入数据,发现是不是地址的DQ信号的八倍长度?所以每一次都是写入8个数据。app_wdf_end是指示一个数据8个长度有效信号,否则你不用这个信号,表示无效8个数据输入。很奇葩xilinx这么做。可能为了兼容stratic模式。app_wdf_wren数据有效输入。app_wdf_rdy表示写入数据的fifo中可以写入信号。这个信号也就是传说中的fifo的满信号。
app_rd_data读取的数据。app_rd_data_end表示该数据是8个有效数据。app_rd_data_valid数据有效指令。配合app_rd_data_end&app_rd_data_valid才能得到正确有效的数据。
app_addr[ADDR_WIDTH – 1:0] 是ddr3的地址,精确到每个col地址,但是因为实际突发长度要求8所以每个数据都是4位开始。app_cmd[2:0]是命令,其实就两种,3'b001是读,3'B000是写。app_en是命令输入使能信号。
AET代码编辑器真的好蛋疼啊。AET主要是FPGA和mcu为主的论坛和博客,上面代码语言没有verilog和VHDL有没有搞错啊。废话不说了。
操作DDR3的时候,首选是等待init_calib_complete 拉高电平。
以下DDR3的写入数据代码,注意,这个是写入过程。首先是从外部读取一个数据,然后观察app_wdf_rdy是否高电平,然后写入数据。app_wdf_end<=1'b1 ,app_wdf_wren<=1'b1; 写完成了,继续观察 app_wdf_rdy是否高电平。如果为高电平,释放app_wdf_end<=1'b0 ,app_wdf_wren<=1'b0; 使用手册中信号hold的意思就是上述的过程,发出信号的时候,必须app_wdf_rdy在高电平的时候。
以app_wdf前缀都是写入fifo的数据通道,在任何DDR的控制器数据和命令都是分离的。有的人经常问SDRAM为啥那么多时序要求,怎么看数据和命令的关系,数据的时序是数据和数据的之间的时间,命令的时序是跟命令有关系,所以经常看时序图发现,发送行激活的命令,但是看数据通道是输出的数据。因为SDRAM数据通道和命令通道是分离的。
app_cmd 是发送指令,写入指令3'B000,写入DDR3的数据内容。 if( app_rdy==1'b1) app_en<=1'b1; 如果 app_rdy状态为1,使能命令app_en。写完该命令,再次观察app_rdy是否为1,如果1就释放app_en信号。
case(wrfifo_rd_number) 0:begin wrfifo_rd_number <=1; dram_wr_fifo_rd <=1'b1; end 1: begin wrfifo_rd_number <=2; dram_wr_fifo_rd <=1'b0; end 2:begin dram_wr_fifo_rd <=1'b0; if (app_wdf_rdy ==1'b1) ////发送一次 begin app_wdf_end <= 1'b1; app_wdf_wren <= 1'b1; app_wdf_data <= dram_wr_fifo_rd_data; wrfifo_rd_number <=3; end end 3:begin if (app_wdf_rdy ==1'b1) ////发送一次 begin app_wdf_end <= 1'b0; app_wdf_wren <= 1'b0; wrfifo_rd_number <=4; end end 4:begin app_cmd <=DDR3_WRITE_CMD;///// 写入命令 app_addr <= {dram_wr_row_couter[11:0],dram_wr_col_couter[10:0],3'b000}; if( app_rdy==1'b1) begin app_en<=1'b1; wrfifo_rd_number <=5; end else app_en<=1'b0; end 5:if(app_rdy==1'b1) ///////////等待 app_rdy为高电平,否则继续hold写入信号。 begin app_en<= 1'b0; wrfifo_rd_number <=0; dram_work_state <= dram_work_wait_wr_over; end default:
上述是写入过程,再看下读取数据通道,方式有点类型,只不过不需要读取外部的数据。先等到app_rdy是否正常,然后发送读请求。发送读信号,继续观察app_rdy是否正常 ,然后发现是高电平就释放app_rdy信号。
dram_work_wait_rd : begin if(dram_rd_row_couter== out_image_row_counters) begin dram_work_state<=dram_work_rd_addr_zero;//输出场信号是否为0 end else if(dram_rd_fifo_full_flag!=1'b1) ///没有满的情况下 begin app_cmd<=DDR3_READ_CMD; app_addr <={dram_rd_row_couter[11:0],dram_rd_col_couter[10:0],9'd000}; if(app_rdy==1'b1) begin dram_work_state<= dram_work_wait_rd_over; app_en <= 1'b1; end else begin app_en <= 1'b0; end end else begin dram_work_state<= dram_work_wait_wr; end enddram_work_wait_rd_over: begin if(app_rdy==1'b1) ///////////等待 app_rdy为高电平,否则继续hold写入信号。 begin app_en <= 1'b0; dram_work_state<=dram_work_rd_addr_change; end end
有人肯定要问了。怎么知道写入了。等app_rd_data_end 和app_rd_data_valid为高电平的时候就写入数据。有人肯定会担心这些发送写和读命令不会工作,但是fpga这些IP只要上述时序是正常,都会工作的。所以不用客户去关心app_rd_data_valid是否执行。
sdram_fifo_512b_64b_1024sdram_rd_fifo_xilinx(.rst(1'b0), //高电平有效.wr_clk(dram_clk),.wr_en(app_rd_data_end&app_rd_data_valid),.din(app_rd_data[511:0]),.wr_data_count(dram_rd_fifo_count),.rd_clk(hdmi_data_rd_clk),.rd_en(hdmi_rd_fifo_rd_en),.dout(hdmi_rd_fifo_rd_64data),.full(hdmi_rd_fifo_rd_full),.empty(hdmi_rd_fifo_rd_empty),.rd_data_count());