加班猫

人于人区别,就是远见不一样。szfpga.com

AC701的DDR3测试读写(2)

2
阅读(5522)

       人这一辈子,不可能做一件事情。许许多多的成功人士都是做了无数的事情,只有一件事情成功的。或许有些事,你继续坚持带来微小的效应,那说明真的不合适。合适自己的东西,自己才会做的出色,独特,优秀。坚持多年的人,看看自己做的是否优秀,人生有几个十年,何必浪费时间在不合适的事情上面。

       这个是文章的开头,前些日子去面试,面试的人说,我没用过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),SPIUARTI2C,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 拉高电平。

    3.png

下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:

      1.png

上述是写入过程,再看下读取数据通道,方式有点类型,只不过不需要读取外部的数据。先等到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
     end
dram_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()
);


2.png