全球最实用的IT互联网信息网站!

AI人工智能P2P分享&下载搜索网页发布信息网站地图

当前位置:诺佳网 > 电子/半导体 > 接口/总线/驱动 >

DDR信号的处理

时间:2023-06-16 10:22

人气:

作者:admin

标签: 号的  DDR  这里  处理  注意   

导读:注意,这里的DDR指的是Double Data Rate,双倍数据速率。这篇文章并不是讲DDR存储器系列的东西。...

注意,这里的DDR指的是Double Data Rate,双倍数据速率。这篇文章并不是讲DDR存储器系列的东西。

不同于SDR,也就是单上升沿或下降沿,传输数据。DDR说我不想选择是上升沿还是下降沿传输数据,小孩子才做选择,大人只会说我全都要。

上升沿和下降沿全部都要传数据

通过一组图片就可以看到SDR和DDR的区别:

图片

SDR

图片

DDR

可以看到经过DDR处理的数据在数据时钟上升沿和下降沿都有数据更新,对于如何完整的取出数据,我仔细思考了许久,经历了否定之否定的过程,最终才找到了通用的解决方案。现在写出解决方案的心路历程:

  1. 刚开始觉得,既然它上升沿和下降沿都有,不如用一个always检测时钟跳变,有跳变就开始取值。代码示例如下:
always @ (fb_clk)
begin
if (fb_clk == 1'b1) //上升沿跳变
   i_data <= tx_frame ? {tx_d,6'd0} : {i_data[11:6], tx_d};// tx_frame为高代表高6位, 低为低8else
   q_data <= tx_frame ? {tx_d,6'd0} : {q_data[11:6], tx_d};
end

但是这么做肯定是有问题的,我们本来是要描述一个时序电路,最后always的敏感列表里面是一个信号,这么做就成了组合逻辑了,这么做不稳定不可取。

  1. 第二种方法是使用锁相环输出一个与原数据时钟同频但相位延后180度的时钟fb_clk_180, fb_clk负责采样data_I, fb_clk_180负责data_Q。这种方法可以,但感觉麻烦,因为后面还要使用DDR输出信号,时钟转来转去有点麻烦。
  2. 第三种方法还是使用锁相环,输出一个同相但频率为原来频率2倍的时钟信号fb_clk_mul2。fb_clk_mul2的每次上升沿,对应着原时钟fb_clk的上升沿和下降沿,使用fb_clk_mul2就可以分离data_I和data_Q。但这种方法也有局限性,不仅增加时钟数量,当原时钟速率过高,这种方法的稳定性也将有待商榷。

最后,我们在Vivado里面找到了一种原语,完美解决这个问题。这就是IDDR和ODDR。

对于输入信号,我们使用IDDR解出原始数据,在Language Template找到IDDR原语示例,例子如下:

IDDR #(
         .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
                                         //    or "SAME_EDGE_PIPELINED"
         .INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1
         .INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1
         .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
      ) IDDR_inst (
         .Q1(rx_data_pos[i]), // 1-bit output for positive edge of clock
         .Q2(rx_data_neg[i]), // 1-bit output for negative edge of clock
         .C(data_clk),   // 1-bit clock input
         .CE(1'b1), // 1-bit clock enable input
         .D(rx_data_dly[i]),   // 1-bit DDR data input
//         .D(rx_data[i]),   // 1-bit DDR data input
         .R(1'b0),   // 1-bit reset
         .S(1'b0)    // 1-bit set
      );

设置好IDDR的4个常量参数之后,将数据时钟接入C端口,时钟使能CE端口拉高,待转数据信号接入D端口,Q1端口将会输出时钟上升沿采样的数据,Q2端口将会输出时钟下降沿采样的数据。注意设置好复位R和置位S端口。

设置好之后就可以在rx_data_pos,rx_data_neg看到数据。这里我使用了generate for生成块,所以出现了genvar变量i;

同样,对于DDR输出信号,使用ODDR原语解决:

ODDR #(
     .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
     .INIT(1'b0),    // Initial value of Q: 1'b0 or 1'b1
     .SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
  ) ODDR_inst (
     .Q(p0_data[i]),   // 1-bit DDR output
     .C(data_clk),   // 1-bit clock input
     .CE(1'b1), // 1-bit clock enable input
     .D1(idata[i]), // 1-bit data input (positive edge)
     .D2(qdata[i]), // 1-bit data input (negative edge)
     .R(1'b0),   // 1-bit reset
     .S(1'b0)    // 1-bit set
  );

设置好ODDR的3个常量参数之后,将数据时钟接入C端口,时钟使能CE端口拉高,Q端口输出DDR处理后的数据,数据时钟上升沿更新的数据接入D1端口,数据时钟下降沿更新的数据接入D2端口。注意设置好复位R和置位S端口。

ODDR还可以巧妙地输出时钟,在D1输入1'b1, D2输入1'b0,其他不变,则在数据时钟上升沿输出高电平,下降沿输出低电平。巧妙地输出了数据时钟。

注意,ODDR输出的数据只能经过IOBUF或者输出,曾经有人想使用ILA抓取ODDR的Q端口输出的数据,无奈Implemention总会报错。

总结:

  • 对于DDR信号,不能直接用always @ (data_clk)的方法采样信号,详细见上述(1)内容
  • 上述(2)和(3)的方法在一定范围内都有其可行性,但也有一些弊端,详细见上述(2)和(3)内容
  • 使用IDDR和ODDR最为妥当,IDDR和ODDR的数据端口都是1bit,多bit可以使用generate for生成块
  • 可以使用ODDR在普通IO上输出数据时钟
  • ODDR输出的数据只能经过IOBUF或者输出

如果是LVDS信号,需要先转单端再进IDDR;或者ODDR后再转差分输出;差分信号的处理方法可以看上一篇文章。

信号处理好之后,如果出现了时钟与数据对不上该怎么办,这个时候可以使用Idelay调整时序。

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信