『USB3.0Cypress』FPGA开发(2)GPIF II读写时序分析

目录

1.写时序分析

1.1写时序

1.2官方状态机设计

1.3缓冲区切换现象

1.4官方verilog代码Bug

2.读时序分析

2.1读时序

2.2官方状态机设计

2.3官方代码分析

3.传送门


1.写时序分析

1.1写时序

GPIF II接口如何去写?如下图所示,对于写时序来讲需要控制的信号有PCLK、SLCS、SLWR、db[31:0]、FLAGA、FLAGB、addr,可将其余控制线配置为固定值1,包括SLRD、SLOE、PKTEND(该信号与写操作有关,第4节单独介绍)。首先,FIFO地址稳定即通过addr信号选择与上行数据通道相连的线程,且 SLCS#信号被激活。然后在SLWR拉低的时候,向数据总线上写入32bit字即可。

什么时候才能去写?不同的应用场景,开始向GPIF II写数据的时机不同。但不论什么场景,写数据之前应该判断FLAGA的状态(缓冲区的状态),在缓冲区不满即FLAGA=1时还要判断FLAGB的状态,FLAGB=1代表局部标志就位后才能进行写操作。FLAGB被激活后需要一段时间恢复(取决于缓冲区另一端是否读走数据)。

什么时候停止写?在写的过程中应该持续关注水印值标志FLAGB,FLAGB=0代表缓冲区即将写满。此时应按照下图所示的公式计算还能写入的值。水印值设置为6,则FLAGB=0时,还能写入的数据字(32bit)=6x(32/32)-4=2。

当你要写的数量小于缓冲区大小(指单个)或者你精确的知道写入数据个数的时候,可以不用水印值标志。水印值被激活后,也可以选择不写。但不论怎样,开始写的时候必须要检测缓冲区状态。但需要注意的是,FLAGA与实际的写满的时刻有延迟,因此不能在写完一次突发之后紧接着判断FLAGA,应该通过延时同步FLAGA的指示状态后在判断。下图是读写测试时在vivado逻辑分析仪抓取到的波形,后缀_d的信号是将标志信号打拍后的信号,slwr_n是逻辑中打拍后的物理引脚上的信号。

在t0-t2阶段状态机处于IDLE状态,在t2-t3阶段检测到flag_a_d为高(此时flag_b_d也已经为高),但是由于状态机是串行的,因此需要依次判断flag_a_d,flag_b_d,此时状态机为WAIT_FLAGB状态,一个周期后,状态机跳转到WRITE状态,slwr_n拉低开始进行写操作。写入数据0x00000000,每个周期数据加一,在程序中计数器设置为0加到1023循环,缓冲区不满就会一直写,所以最后一个写周期数据为0x00003ff。

在t5-t6阶段检测到flag_b为低电平,一个周期后采样信号flag_b_d拉低,在t6-t7阶段检测到flag_b_d为低电平,于是下个周期拉高信号slwr_d,状态机跳转到DELAY状态。slwr_d又经过打一拍后输出到实际的物理管脚,即一个周期后slwr_n拉高。这样就形成了打拍后的flag_b_d信号拉低与slwr_n拉高相差两个时钟周期。由于局部标志情况下的通用公式:时钟沿到来(这时,对处于低电平的局部标志进行采样)后将要写入的数据字的数量=水印值x(32/总线宽度)–4=6-4=2,符合手册理论,结论成立。这里通过阅读手册结合代码,确定为采样后的(flag_b_d)写入数据字的数量。

由手册可得从最后一次激活 slwr_n到激活flag_a_d标志的延迟为3个周期+Tcflg(外部器件可在第 4 个时钟沿上对有效标志进行采样),因此正确的时序应该为以t8为基准,四个周期后flag_a_d拉低,同时状态机回到IDLE状态

1.2官方状态机设计

  • stream_in_idle 状态:该状态初始化状态机中所使用的所有寄存器和信号。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 1;SLRD# = 1;SLCS# = 0;SLWR# = 1;A[1:0] = 0。
  • stream_in_wait_flagb 状态 :每当 flaga_d =1 和 FPGA 主设备模式为 stream_in 时,状态机将进入该状态,并等待 flagb_d。
  • stream_in_write 状态:每当 flagb_d =1 时,状态机将进入该状态,并开始写入从设备 FIFO 接口。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 1;SLRD# = 1;SLCS# = 0;SLWR# = 0;A[1:0] = 0。
  • stream_in_write_wr_delay状态:每当 flagb_d = 0 时,状态机将进入该状态。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 1;SLRD# = 1;SLCS# = 0;SLWR# = 1;A[1:0] = 0经过一个时钟周期后,状态机将进入 stream_in_idle 状态。

1.3缓冲区切换现象

固件中用于上行数据通道的缓冲区设置的是为4*16kB,即4个大小为16KB的缓冲区。应该明确,flag指示的是单个缓冲区的状态,不论是读操作还是写操作都是这样,因此在连续发送数据的过程中当单个的缓冲区存满数据之后,FLAG会被激活直到缓冲区切换完毕之后,即flagaflagb变化时有两种可能性:第一种为当前缓冲区满,发生缓冲区切换,第二种为所有的缓冲区全部都满了。FPGA往FX3写4*16KB数据,上位机不去读取数据,数据将阻塞在FX3中,同时在FPGA代码中加入10秒延时,方便ILA抓取,ILA抓取到下图,可以看到缓冲区发生了三次切换。

如下图所示,通过marker工具可以看到当缓冲区进行切换时flaga和flagb拉低了67个clk,而单个clk为10ns,切换的大概时间为67*10ns=670ns,与文档中切换缓冲区操作大约需要1us的描述吻合。通过marker得到写了4096个clk数据,4096*32/8=16384B,16384/1024=16kB。

1.4官方verilog代码Bug

可以查看AN65974附带文件中有一个“slaveFIFO2b_streamIN.v”文件,这个代码是串入代码,即缓冲区可写的时候一直写。如下摘抄了文件中的状态机代码,可以看到,在stream_in_write_wr_delay状态只保持了一下周期,随即跳转到idle状态,去判断flaga决定是否执行下一次写操作了。由于flaga的状态变化滞后于slwr三个周期,即第四个周期能采集的flaga才能代表缓冲区的真实状态,因此如果stream_in_write_wr_delay只有一个clk,在idle状态采集到的flaga是不能代表缓冲区状态的。状态机会跳转到stream_in_wait_flagb等待flagb准备就绪,因此程序变成了依靠flagb进行”是否可以开始写“的判断,这和应依据flagb状态判断停止写,依据flaga判断开始写的理论违背。

因此应该在stream_in_write_wr_delay保持到flaga正确反应缓冲区状态时在跳转到idle状态,可以通过人为的控制此状态的保持时间避免这个潜在风险。

//StreamIN mode state machine combo
always @(*)begin
    next_stream_in_state = current_stream_in_state;
    case(current_stream_in_state)
    stream_in_idle:begin
        if((stream_in_mode_selected) & (flaga_d == 1'b1))begin
            next_stream_in_state = stream_in_wait_flagb; 
        end else begin
            next_stream_in_state = stream_in_idle;
        end 
    end
    stream_in_wait_flagb :begin
        if (flagb_d == 1'b1)begin
            next_stream_in_state = stream_in_write; 
        end else begin
            next_stream_in_state = stream_in_wait_flagb; 
        end
    end
    stream_in_write:begin
        if(flagb_d == 1'b0)begin
            next_stream_in_state = stream_in_write_wr_delay;
        end else begin
            next_stream_in_state = stream_in_write;
        end
    end
        stream_in_write_wr_delay:begin
            next_stream_in_state = stream_in_idle;
    end
    endcase
end

2.读时序分析

2.1读时序

该怎么读?对于读操作需要控制的信号有PCLK、SLCS、SLRD、SLOE、db[31:0]、FLAGC、FLAGD、addr,可将其余控制线配置为固定值1,包括SLWR、PKTEND。首先,FIFO 地址稳定即通过addr设置与下行数据通道关联的物理线程,且 SLCS#信号被激活。其次激活SLOE#,SLOE是一个使能信号,功能是用来驱动数据总线。然后在激活SLRD两个周期之后去总线上获取有效数据即可。

什么时候开始去读?不同的应用场景,从GPIF II读数据的时机不同,不论怎样,应该首先判断缓冲区是否有数据,避免空读发生未知错误。读数据之前应该先判断FLAGC的状态(缓冲区状态),在缓冲区不为空即FLAGC=1时去判断FLAGD的状态,FLAGD=1代表局部标志就位,才能进行读操作。FLAGD被激活后需要一段时间恢复(取决于缓冲区另一端是否继续写数据)。

什么时候停止去读?在读的过程中应该持续关注水印值标志FLAGD,FLAGD=0代表缓冲区即将读空。此时应按照下图所示的公式计算还能读出的值。如下图所示,水印值设置为6,则FLAGD=0时,SLOE#维持激活周期=6*(32/32)-1=5,即代表缓冲区还有5个数据字可用于读取,由于SLRD与数据总线存在两个周期延时,因此SLRD维持的周期数=5-2=3。

如下图所示,由于SLRD与数据总线的延时关系,红色圈1中的数据是此次读取的第一个有效数据,SLRD维持了8个周期,即读取了8个数据。Tap2代表的是fx3_flagd被激活后采样,采样得到的flagd信号为低电平后,SLRD维持3个周期,Tap3代表SLOE维持5个周期。在读数据过程中,也会发生缓冲区的切换,因此连续的批量数据读取的数量大于单个缓冲区的大小时,中间也会有间隔,现象同2.2节写过程的缓冲区切换一致。图中Tap3-Tap2则说明了在最后一次SLRD激活周期后,FLAGC在第3个周期被激活。这些现象与理论部分的阐述是一致的。

​​​​​​​

在t0-t1阶段状态机处于IDLE状态,在t1-t2阶段检测到flag_c_d为高(此时flag_d_d也已经为高),但是由于状态机是串行的,因此需要依次判断flag_c_d,flag_d_d,此时状态机为WAIT_FLAGD状态,一个周期后,状态机跳转到READ状态,slrd_n拉低开始进行读操作。这里需要注意读到的前两个数无效。所以sloe_n信号要比slrd_n延迟两个周期拉高,以便得到正确的数据。

在t5-t6阶段检测到flag_d为低电平,一个周期后采样信号flag_d_d拉低,在t6-t7阶段检测到flag_b_d为低电平,于是下个周期进入状态RD_OE_DELAY(两个周期),然后slrd_n拉高,状态机跳转到OE_DELAY状态。由公式局部标志情况下的通用公式6*32/32-1=5,加上之前已经经过的三个周期,所以OE_DELAY状态保持两个周期后sloe_n拉高。

2.2官方状态机设计

  • stream_out_idle 状态 :该状态初始化状态机中所使用的所有寄存器和信号。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 1;SLRD# = 1;SLCS# = 0;SLWR# = 1;A[1:0] = 3。
  • stream_out_flagc_rcvd 状态:每当 flagc_d = 1 和 FPGA 主设备为串流 OUT 模式时,状态机将进入该状态。
  • stream_out_wait_flagd 状态:经过一个时钟周期后,状态机将进入该状态。
  • stream_out_read 状态:每当 flagc_d = 1 时,状态机将进入该状态。状态机将激活读控制信号如下:PKTEND# = 1;SLOE# = 0;SLRD# = 0;SLCS# = 0;SLWR# = 1;A[1:0] = 3。
  • stream_out_read_rd_oe_delay 状态:每当 flagc_d = 0 时,状态机将进入该状态。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 0;SLRD# = 0;SLCS# = 0;SLWR# = 1;A[1:0] = 3。
  • stream_out_read_oe_delay 状态:每当 rd_oe_delay_cnt = 0 时,状态机将进入该状态。从设备 FIFO 控制线的状态为:PKTEND# = 1;SLOE# = 0;SLRD# = 1;SLCS# = 0;SLWR# = 1;A[1:0] = 3,如果 oe_delay_cnt = 0,状态机将从该状态切换到 stream_out_idle 状态。

2.3官方代码分析

官方代码附带在AN65974文件包中,verilog文件中有一个名为slaveFIFO2b_streamOUT.v的文件执行的是串出操作,即缓冲区不为空就一直读。状态机部分代码摘抄如下:

//steamOUT mode state machine combo
always @(*)begin
    next_stream_out_state = current_stream_out_state;
    case(current_stream_out_state)
    stream_out_idle:begin
        if((stream_out_mode_selected) & (flagc_d == 1'b1))begin
            next_stream_out_state = stream_out_flagc_rcvd;
        end else begin
            next_stream_out_state = stream_out_idle;
        end
    end 
        stream_out_flagc_rcvd:begin
        next_stream_out_state = stream_out_wait_flagd;
    end 
    stream_out_wait_flagd:begin
        if(flagd_d == 1'b1)begin
            next_stream_out_state = stream_out_read;
        end else begin
            next_stream_out_state = stream_out_wait_flagd;
        end
    end 
        stream_out_read :begin
                if(flagd_d == 1'b0)begin
            next_stream_out_state = stream_out_read_rd_and_oe_delay;
        end else begin
            next_stream_out_state = stream_out_read;
        end
    end
    stream_out_read_rd_and_oe_delay : begin
        if(rd_oe_delay_cnt == 0)begin
            next_stream_out_state = stream_out_read_oe_delay;
        end else begin
            next_stream_out_state = stream_out_read_rd_and_oe_delay;
        end
    end
        stream_out_read_oe_delay : begin
        if(oe_delay_cnt == 0)begin
            next_stream_out_state = stream_out_idle;
        end else begin
            next_stream_out_state = stream_out_read_oe_delay;
        end
    end
    endcase
end

当一次读取执行结束之后,若立即回到idle状态,在idle状态中会直接根据flagc的状态,进行下一步的动作,但是由于flagc要经过采样,会产生管脚到逻辑之间的延迟,故此时会产生错误的判断,本应flagc为0,等待写入,可由于采样延迟误判断此时为未空状态(flagc=1),故状态机执行将与本意偏离。因此应该在每次接收完一次状态数据之后,回到idle状态之前,自定义延时两个时钟周期,等待flag采样。那么cypress是如何规避这个问题的呢?水印值标志为0之后,sloe代表的是缓冲区中可用于读取的数据字的数量,应该为5个,slrd代表实际去读取的数据,当flagd置位之后,slrd应该在拉低3个周期去读5个已存数据(slrd有两个延迟3+2=5)。Cypress把sloe的延长时间做成6个,显示有6个可读数据,但是实际的slrd只拉低了3个时钟,那么不会造成空读数据,但是却巧妙的规避了flagc采样产生的延时问题。后续我会写一些固件和例程的测试代码,在代码中把SLOE的维持周期改为5个,使用自定义的延迟以对齐FLAGA有效的边沿。​​​​​​​

3.传送门

● 我的主页

USB3.0-Cypress通信方案专栏汇总导航

下一篇:FPGA开发(3)GPIF II短包零包时序分析​​​​​​​

                                                             END                                                                
🔈文章原创,首发于CSDN论坛。 
🔈欢迎点赞❤❤收藏⭐⭐打赏💴💴!
🔈欢迎评论区或私信指出错误❌,提出宝贵意见或疑问❓。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑾芳玉洁错过的烟火

原创不易,请多支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值