电子说
注意,这里的DDR指的是Double Data Rate,双倍数据速率。这篇文章并不是讲DDR存储器系列的东西。
不同于SDR,也就是单上升沿或下降沿,传输数据。DDR说我不想选择是上升沿还是下降沿传输数据,小孩子才做选择,大人只会说我全都要。
上升沿和下降沿全部都要传数据
通过一组图片就可以看到SDR和DDR的区别:
SDR
DDR
可以看到经过DDR处理的数据在数据时钟上升沿和下降沿都有数据更新,对于如何完整的取出数据,我仔细思考了许久,经历了否定之否定的过程,最终才找到了通用的解决方案。现在写出解决方案的心路历程:
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位, 低为低8位
else
q_data <= tx_frame ? {tx_d,6'd0} : {q_data[11:6], tx_d};
end
但是这么做肯定是有问题的,我们本来是要描述一个时序电路,最后always的敏感列表里面是一个信号,这么做就成了组合逻辑了,这么做不稳定不可取。
最后,我们在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总会报错。
总结:
如果是LVDS信号,需要先转单端再进IDDR;或者ODDR后再转差分输出;差分信号的处理方法可以看上一篇文章。
信号处理好之后,如果出现了时钟与数据对不上该怎么办,这个时候可以使用Idelay调整时序。
全部0条评论
快来发表一下你的评论吧 !