FPGA高速边沿采样的细节探讨
0赞FPGA高速边沿采样的细节探讨
也许这个问题以前从来没用注意过,因为FPGA片内是同步的,其次联调,外部速度也没那么快,以至于边沿采样,有效的时候,即可更新数据。
这几天调试一个东东,STM32的FSMC传输数据给Bingo自制的VGA控制器,由于没有直接打板,板间用了杜邦线连接。FMSC传输模式为最快的速度,FSMC写时序如下图所示,最快达到了72M(HCLK)的速度,如下图所示:
1. 细节问题一
由于这并非一块FPGA内部的通信,STM32与FPGA之间的数据不同步,有时候会导致数据的缺失,错位等,这我在前面的博文中也讲到过。因此,异步传输数据的第一步,必须先同步外部数据,以确保工作在同一个时钟域。在FPGA中可以用最高时钟去驱动,通过DFF触发,实现异步数据的同步。如下图所示:
2. 细节问题二
这是200M 的逻辑分析仪采样的,忽略信号传输线的延时,M3到M2,M6-M5,就是WR拉高到CS拉高的延时。从上图可见是66.7MHz到100MHz之间。查看相关手册,FSMC-SRAM模式时序图如下所示:
可见,WR拉高至CS拉高之间的时间是72MHz,这在Logic捕获的66.7到100M之间,理论复合实际现象。
由于STM32硬件FSMC传输数据,速度并非非常快,以至于最后信号检测达到了72MHz的程度,当时由于自己FPGA主频在50MHz,正因为如此,而经常跑一段时间,就检测不到了,没想到STM32 FSMC那么快。。。最后将主频升高为125MHz。
3. 细节问题三
当然,读取数据的标志是:wr上升沿,同时cs有效,因此先捕获wr上升沿,然后检测上升沿有效信号,最后在上升沿信号有效的时候读取数据。
但是当检测到WR上升沿到读取WR上升沿,开始读取数据,之间延时了一个CLK,即为125MHz。这固然不假,但是看了下面一幅图,你也许会明白……
分析如下:
(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