注意,這里的DDR指的是Double Data Rate,雙倍數(shù)據(jù)速率。這篇文章并不是講DDR存儲器系列的東西。
不同于SDR,也就是單上升沿或下降沿,傳輸數(shù)據(jù)。DDR說我不想選擇是上升沿還是下降沿傳輸數(shù)據(jù),小孩子才做選擇,大人只會說我全都要。
上升沿和下降沿全部都要傳數(shù)據(jù)
通過一組圖片就可以看到SDR和DDR的區(qū)別:
SDR
DDR
可以看到經(jīng)過DDR處理的數(shù)據(jù)在數(shù)據(jù)時鐘上升沿和下降沿都有數(shù)據(jù)更新,對于如何完整的取出數(shù)據(jù),我仔細(xì)思考了許久,經(jīng)歷了否定之否定的過程,最終才找到了通用的解決方案?,F(xiàn)在寫出解決方案的心路歷程:
- 剛開始覺得,既然它上升沿和下降沿都有,不如用一個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位, 低為低8位
else
q_data <= tx_frame ? {tx_d,6'd0} : {q_data[11:6], tx_d};
end
但是這么做肯定是有問題的,我們本來是要描述一個時序電路,最后always的敏感列表里面是一個信號,這么做就成了組合邏輯了,這么做不穩(wěn)定不可取。
- 第二種方法是使用鎖相環(huán)輸出一個與原數(shù)據(jù)時鐘同頻但相位延后180度的時鐘fb_clk_180, fb_clk負(fù)責(zé)采樣data_I, fb_clk_180負(fù)責(zé)data_Q。這種方法可以,但感覺麻煩,因?yàn)楹竺孢€要使用DDR輸出信號,時鐘轉(zhuǎn)來轉(zhuǎn)去有點(diǎn)麻煩。
- 第三種方法還是使用鎖相環(huán),輸出一個同相但頻率為原來頻率2倍的時鐘信號fb_clk_mul2。fb_clk_mul2的每次上升沿,對應(yīng)著原時鐘fb_clk的上升沿和下降沿,使用fb_clk_mul2就可以分離data_I和data_Q。但這種方法也有局限性,不僅增加時鐘數(shù)量,當(dāng)原時鐘速率過高,這種方法的穩(wěn)定性也將有待商榷。
最后,我們在Vivado里面找到了一種原語,完美解決這個問題。這就是IDDR和ODDR。
對于輸入信號,我們使用IDDR解出原始數(shù)據(jù),在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
);
設(shè)置好IDDR的4個常量參數(shù)之后,將數(shù)據(jù)時鐘接入C端口,時鐘使能CE端口拉高,待轉(zhuǎn)數(shù)據(jù)信號接入D端口,Q1端口將會輸出時鐘上升沿采樣的數(shù)據(jù),Q2端口將會輸出時鐘下降沿采樣的數(shù)據(jù)。注意設(shè)置好復(fù)位R和置位S端口。
設(shè)置好之后就可以在rx_data_pos,rx_data_neg看到數(shù)據(jù)。這里我使用了generate for生成塊,所以出現(xiàn)了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
);
設(shè)置好ODDR的3個常量參數(shù)之后,將數(shù)據(jù)時鐘接入C端口,時鐘使能CE端口拉高,Q端口輸出DDR處理后的數(shù)據(jù),數(shù)據(jù)時鐘上升沿更新的數(shù)據(jù)接入D1端口,數(shù)據(jù)時鐘下降沿更新的數(shù)據(jù)接入D2端口。注意設(shè)置好復(fù)位R和置位S端口。
ODDR還可以巧妙地輸出時鐘,在D1輸入1'b1, D2輸入1'b0,其他不變,則在數(shù)據(jù)時鐘上升沿輸出高電平,下降沿輸出低電平。巧妙地輸出了數(shù)據(jù)時鐘。
注意,ODDR輸出的數(shù)據(jù)只能經(jīng)過IOBUF或者輸出,曾經(jīng)有人想使用ILA抓取ODDR的Q端口輸出的數(shù)據(jù),無奈Implemention總會報(bào)錯。
總結(jié):
- 對于DDR信號,不能直接用always @ (data_clk)的方法采樣信號,詳細(xì)見上述(1)內(nèi)容
- 上述(2)和(3)的方法在一定范圍內(nèi)都有其可行性,但也有一些弊端,詳細(xì)見上述(2)和(3)內(nèi)容
- 使用IDDR和ODDR最為妥當(dāng),IDDR和ODDR的數(shù)據(jù)端口都是1bit,多bit可以使用generate for生成塊
- 可以使用ODDR在普通IO上輸出數(shù)據(jù)時鐘
- ODDR輸出的數(shù)據(jù)只能經(jīng)過IOBUF或者輸出
如果是LVDS信號,需要先轉(zhuǎn)單端再進(jìn)IDDR;或者ODDR后再轉(zhuǎn)差分輸出;差分信號的處理方法可以看上一篇文章。
信號處理好之后,如果出現(xiàn)了時鐘與數(shù)據(jù)對不上該怎么辦,這個時候可以使用Idelay調(diào)整時序。
-
存儲器
+關(guān)注
關(guān)注
38文章
7653瀏覽量
167409 -
DDR
+關(guān)注
關(guān)注
11文章
732瀏覽量
66826 -
SDR
+關(guān)注
關(guān)注
7文章
235瀏覽量
51164 -
LVDS信號
+關(guān)注
關(guān)注
0文章
18瀏覽量
8019 -
Vivado
+關(guān)注
關(guān)注
19文章
835瀏覽量
68778
發(fā)布評論請先 登錄
信號完整性仿真:DDR3/4/5系列地址信號端接優(yōu)化對比
DDR4信號完整性測試要求

240-4C6678_K7_DDR3_VPX高速信號處理板
針對DDR2-800和DDR3的PCB信號完整性設(shè)計(jì)
嵌入式DDR布線分析 DDR信號布線介紹

DDR工作原理_DDR DQS信號的處理

DDR高速信號線的布線原則和技巧

DDR總線時間的解決辦法
DDR和DDR2與DDR3的設(shè)計(jì)資料總結(jié)

ZCU10中MPSoC對DDR復(fù)位信號設(shè)計(jì)

關(guān)于DDR信號的如何去判斷信號質(zhì)量?

DDR加終端匹配電阻和不加信號質(zhì)量的區(qū)別
0706線下活動 I DDR4/DDR5內(nèi)存技術(shù)高速信號專題設(shè)計(jì)技術(shù)交流活動

普源DHO1072示波器DDR信號測試要點(diǎn)

評論