CrazyBingo

FPGA高速边沿采样的细节探讨

0
阅读(3560)

FPGA高速边沿采样的细节探讨

也许这个问题以前从来没用注意过,因为FPGA片内是同步的,其次联调,外部速度也没那么快,以至于边沿采样,有效的时候,即可更新数据。

这几天调试一个东东,STM32的FSMC传输数据给Bingo自制的VGA控制器,由于没有直接打板,板间用了杜邦线连接。FMSC传输模式为最快的速度,FSMC写时序如下图所示,最快达到了72M(HCLK)的速度,如下图所示:

wps_clip_image-20863

1. 细节问题一

由于这并非一块FPGA内部的通信,STM32与FPGA之间的数据不同步,有时候会导致数据的缺失,错位等,这我在前面的博文中也讲到过。因此,异步传输数据的第一步,必须先同步外部数据,以确保工作在同一个时钟域。在FPGA中可以用最高时钟去驱动,通过DFF触发,实现异步数据的同步。如下图所示:

wps_clip_image-28603

2. 细节问题二

这是200M 的逻辑分析仪采样的,忽略信号传输线的延时,M3到M2,M6-M5,就是WR拉高到CS拉高的延时。从上图可见是66.7MHz到100MHz之间。查看相关手册,FSMC-SRAM模式时序图如下所示:

wps_clip_image-12750

可见,WR拉高至CS拉高之间的时间是72MHz,这在Logic捕获的66.7到100M之间,理论复合实际现象。

由于STM32硬件FSMC传输数据,速度并非非常快,以至于最后信号检测达到了72MHz的程度,当时由于自己FPGA主频在50MHz,正因为如此,而经常跑一段时间,就检测不到了,没想到STM32 FSMC那么快。。。最后将主频升高为125MHz。

3. 细节问题三

当然,读取数据的标志是:wr上升沿,同时cs有效,因此先捕获wr上升沿,然后检测上升沿有效信号,最后在上升沿信号有效的时候读取数据。

但是当检测到WR上升沿到读取WR上升沿,开始读取数据,之间延时了一个CLK,即为125MHz。这固然不假,但是看了下面一幅图,你也许会明白……

wps_clip_image-17645

分析如下:

(1)数据同步,这部分消耗的时钟不计算

(2)捕获到wr上升沿(pos_wr),这个需要消耗一个CLK,消耗1/125M的时间

(3)检测到pos_wr有效信号,这之间又消耗了1/125M的时间

(4)开始检测CS是否有效,有效则开始读取数据

因为(2)与(3)之间共需要1/62.5MHz的时间,但是FSMC的WR到CS拉高只消耗了一个HCLK=72MHz,由于62.5MHz < 72MHz,这使得有可能在检测到pos_wr之后,CS已经拉高,因此读取不到数据。。。。

之前因为没有想到这一步,导致STM32发数据的时候,总是意外的停止,接收到了错误的信号。。速度快是好,但是太快了也会有问题,睡觉咱异步而却高速呢……无奈到快崩溃的底部。最后才发现。。。。修改好,通信完美,perfect;

解决方案主要是将CS,RS等再打一拍,以实现wr的提前捕获,这样只消耗了1/125MHz的时间,数据依然有效。如下所示:

//-------------------------------------
//mcu data sync to fpga
reg    mcu_cs_r0,    mcu_cs_r1, mcu_cs_r2;
reg    mcu_rs_r0,    mcu_rs_r1, mcu_rs_r2;    //fsmc default 0; 8080 default 1;
reg    mcu_we_r0,    mcu_we_r1;                //lag one clk
reg    [15:0]    mcu_data_r0, mcu_data_r1, mcu_data_r2;
always@(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        begin
        mcu_cs_r0 <= 1;        //chip enable
        mcu_cs_r1 <= 1;
        mcu_cs_r2 <= 1;
        mcu_rs_r0 <= 0;        //record/statement
        mcu_rs_r1 <= 0;
        mcu_rs_r2 <= 0;    
        mcu_data_r0 <= 0;    //data bus
        mcu_data_r1 <= 0;
        mcu_data_r2 <= 0;

        mcu_we_r0 <= 1;        //write enable(rising)    
        mcu_we_r1 <= 1;        //lag one clk
        end
    else
        begin
        mcu_cs_r0 <= mcu_cs;    
        mcu_cs_r1 <= mcu_cs_r0;
        mcu_cs_r2 <= mcu_cs_r1;
        mcu_rs_r0 <= mcu_rs;    
        mcu_rs_r1 <= mcu_rs_r0;
        mcu_rs_r2 <= mcu_rs_r1;
        mcu_data_r0 <= mcu_data;
        mcu_data_r1 <= mcu_data_r0;
        mcu_data_r2 <= mcu_data_r1;

        mcu_we_r0 <= mcu_we;    
        mcu_we_r1 <= mcu_we_r0;
        end
end
wire    fpga_cs = mcu_cs_r2;                //lead one clk than fpga_we_flag
wire    fpga_rs = mcu_rs_r2;                //lead one clk than fpga_we_flag
wire    [15:0]    fpga_data = mcu_data_r2;    //lead one clk than fpga_we_flag
//To sure when fpga_we_flag = 1, cs & rs is valid, we lag one clk
wire    fpga_we_flag = (~mcu_we_r1 & mcu_we_r0) ? 1'b1 : 1'b0;        //rising edge of we