一、赛灵思7系列FPGA
1.1 ODDR:实现数据的双边沿发送
支持两种模式:OPPOSITE_EDGE模式、SAME_EDGE模式
ODDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1,Q初始值
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q(Q), // 1-bit DDR output
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input,高电平使能发送数据
.D1(D1), // 1-bit data input (positive edge)
.D2(D2), // 1-bit data input (negative edge)
.R(R), // 1-bit reset,高电平复位
.S(S) // 1-bit set,高电平置位
);
1.1.1、OPPOSITE_EDGE模式
时钟上升沿提取D1数据赋值给Q,时钟下降沿提取D2数据赋值给Q。Q输出相比较于时钟边沿,有一定的延迟。
1.1.2、SAME_EDGE模式
时钟上升沿提取D1和D2,然后依次在一个时钟内赋值给Q,发送出去。
1.2 IDDR:实现输入信号的时钟双边沿接收
支持3种模式:OPPOSITE_EDGE模式、SAME_EDGE模式、SAME_EDGE_PIPELINED模式。
IDDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE", "SAME_EDGE"
// or "SAME_EDGE_PIPELINED"
.INIT_Q1(1'b0), // Initial value of Q1: 1'b0 or 1'b1,Q1的初始值
.INIT_Q2(1'b0), // Initial value of Q2: 1'b0 or 1'b1 Q2的初始值
.SRTYPE("SYNC") // Set/Reset type: "SYNC" or "ASYNC"
) IDDR_inst (
.Q1(Q1), // 1-bit output for positive edge of clock
.Q2(Q2), // 1-bit output for negative edge of clock
.C(C), // 1-bit clock input
.CE(CE), // 1-bit clock enable input,高电平时接收新数据
.D(D), // 1-bit DDR data input,IOB输入
.R(R), // 1-bit reset,高电平复位
.S(S) // 1-bit set,高电平置位
);
1.2.1、OPPOSITE_EDGE模式
时钟上升沿的DDR数据赋值给Q1,时钟下降沿时的数据赋值给Q2,Q1与Q2的输出相比较时钟上升沿与下降沿延迟了一段时间。
1.2.2、SAME_EDGE模式
在时钟上升沿同时将DDR数据赋值给Q1和Q2。时序图如下。CE有效后第一个时钟内,Q2为无效值。
1.2.3、SAME_EDGE_PIPELINED模式
在时钟上升沿将DDR数据赋值给Q1与Q2,Q1与Q2进行了流水线处理。
二、ultrascale架构FPGA
2.1、ODDRE1 实现数据的双边沿发送
仅支持SAME_EDGE模式。时钟上升沿提取数据,在一个时钟周期内发送出去。
ODDRE1 #(
.IS_C_INVERTED(1'b0), // Optional inversion for C,信号取反
.IS_D1_INVERTED(1'b0), // Unsupported, do not use
.IS_D2_INVERTED(1'b0), // Unsupported, do not use
.SIM_DEVICE("ULTRASCALE"), // Set the device version for simulation functionality (ULTRASCALE)
.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1),复位后初始值
)
ODDRE1_inst (
.Q(Q), // 1-bit output: Data output to IOB
.C(C), // 1-bit input: High-speed clock input
.D1(D1), // 1-bit input: Parallel data input 1
.D2(D2), // 1-bit input: Parallel data input 2
.SR(SR) // 1-bit input: Active-High Async Reset
);
2.2、IDDRE1 实现数据的双边沿发送
支持3种模式:OPPOSITE_EDGE模式、SAME_EDGE模式、SAME_EDGE_PIPELINED模式。
IDDRE1 #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
.IS_CB_INVERTED(1'b0), // Optional inversion for CB,0时不对CB取反,1时对CB取反
.IS_C_INVERTED(1'b0) // Optional inversion for C,0时不对C取反,1时对C取反
)
IDDRE1_inst (
.Q1(Q1), // 1-bit output: Registered parallel output 1
.Q2(Q2), // 1-bit output: Registered parallel output 2
.C(C), // 1-bit input: High-speed clock
.CB(CB), // 1-bit input: Inversion of High-speed clock C,C时钟的取反输入
.D(D), // 1-bit input: Serial Data Input
.R(R) // 1-bit input: Active-High Async Reset,高电平有效
);
2.2.1、OPPOSITE_EDGE模式
时钟上升沿的DDR数据赋值给Q1,时钟下降沿时的数据赋值给Q2
2.2.2、SAME_EDGE模式
在时钟上升沿。Q1与Q2同时有效。
2.2.3、SAME_EDGE_PIPELINED模式
相似与SAME_EDGE模式,数据Q1与Q2在时钟上升沿同时有效,不过数据对没有被拆分到不同时钟。
三、通过实例解析原语
假设有一需求,将SPARTAN7 8bits位宽的数据,采用双边沿DDR的方式发送Ultrascale系列的任意FPGA上。为实现该需求进行设计(仿真模型设计)。
首先确认spartan7需要使用的原语为ODDR,ultrascale FPGA使用原语为IDDRE1。
Spartan7示例程序:
`timescale 1ns/1ps
module S7_model
(output ddr_clk,//DDR数据同步时钟
output data_valid,//数据有效标志
output [3:0] ddr_data
);//-------------------模拟生成时钟---------
reg clk=0;//模拟生成100M时钟
always
begin
#5 clk <= ~clk;
end//----------------------基于clk实现逻辑----------------
wire clk_bufg;
wire not_clk_bufg;
reg [7:0] clk_cnt =0;//时钟计数
reg data_valid;
reg [7:0]data=0;
wire [3:0] data_pos;
wire [3:0] data_neg;assign ddr_clk = clk_bufg;
BUFG BUFG_inst (
.O(clk_bufg), // 1-bit output: Clock output.
.I(clk) // 1-bit input: Clock input.
);
always@(posedge clk_bufg)
begin
clk_cnt <= #1 clk_cnt + 1'b1;
endalways@(posedge clk_bufg)
begin
if(clk_cnt >= 8'h80) begin//clk_cnt从0x80到0x00有效
#1 data_valid <= 1'b1;
data <= clk_cnt;
end
else begin
#1 data_valid <= 1'b0;
end
endassign data_pos = data[3:0];
assign data_neg =data [7:4];//上升沿发送data_pos,下降沿发送data_neg.因为数据经过ODDR输出时存在延时,
//因此接收数据时,应该是上升沿接收data_neg,下降沿接收data_pos
generate
genvar i;
for(i=0;i<=3;i=i+1) begin
ODDR #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
.INIT(1'b0), // Initial value of Q: 1'b0 or 1'b1
.SRTYPE("ASYNC") // Set/Reset type: "SYNC" or "ASYNC"
) ODDR_inst (
.Q(ddr_data[i]), // 1-bit DDR output
.C(clk_bufg), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D1(data_pos[i]), // 1-bit data input (positive edge)
.D2(data_neg[i]), // 1-bit data input (negative edge)
.R(1'b0), // 1-bit reset
.S(1'b0) // 1-bit set
);
end
endgenerateendmodule
Ultra FPGA示例程序
`timescale 1ns / 1ps
module ultra_model(
input ddr_clk,//DDR数据同步时钟
input data_valid,//数据有效标志
input [3:0] ddr_data
);
wire [3:0] ddr_data_pos;//
wire [3:0] ddr_data_neg;//
wire [7:0] rx_data;//还原的8bits数据
reg rx_valid;//还原的数据有效标志wire ddr_clk_bufg;
BUFG BUFG_inst (
.O(ddr_clk_bufg), // 1-bit output: Clock output.
.I(ddr_clk) // 1-bit input: Clock input.
);
always@(posedge ddr_clk_bufg)
begin
#1 rx_valid <=data_valid;
end
assign rx_data = {ddr_data_pos,ddr_data_neg};
generate
genvar i;
for(i=0;i<=3;i=i+1) begin
IDDRE1 #(
.DDR_CLK_EDGE("OPPOSITE_EDGE"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
.IS_CB_INVERTED(1'b0), // Optional inversion for CB
.IS_C_INVERTED(1'b0) // Optional inversion for C
)
IDDRE1_inst (
.Q1(ddr_data_pos[i]), // 1-bit output: Registered parallel output 1
.Q2(ddr_data_neg[i]), // 1-bit output: Registered parallel output 2
.C(ddr_clk_bufg), // 1-bit input: High-speed clock
.CB(~ddr_clk_bufg), // 1-bit input: Inversion of High-speed clock C
.D(ddr_data[i]), // 1-bit input: Serial Data Input
.R(1'b0) // 1-bit input: Active-High Async Reset
);
end
endgenerate
endmodule
仿真顶层文件
`timescale 1ns/1psmodule testbench_ddr();
wire ddr_clk;//DDR数据同步时钟
wire data_valid;//数据有效标志
wire [3:0] ddr_data;S7_model S7_model
( .ddr_clk(ddr_clk),//DDR数据同步时钟
.data_valid(data_valid),//数据有效标志
.ddr_data(ddr_data)
);
ultra_model ultra_model
( .ddr_clk(ddr_clk),//DDR数据同步时钟
.data_valid(data_valid),//数据有效标志
.ddr_data(ddr_data)
);endmodule
仿真结果截图:
可以看到,Spartan7有效数据0x80、0x81、0x82·····,在ultra_model模块成功接收到0x80、0x81、0x82···
需要注意的是,S7上升沿发送数据A,则ultra接收该数据时,只能在下降沿提取到该数据A。因为A滞后于时钟上升沿。如果还是想在ultra FPGA的上升沿提取到该数据,则需要采取措施调整一下时钟数据的时序。比如:考虑在ultraFPGA使用IDELAYE3延迟一下时钟输入引脚,或者在spartan7上使用ODELAYE2,延迟以下数据输出引脚。