在使用FPGA时,往往会用到一些差分信号,比如HDMI接口,LVDS接口的ADC、显示器等等设备,而FPGA内部往往只会使用单端信号,就需要完成单端信号和差分信号的相互转换,xilinx提供了两个原语对所有IO信号实现差分和单端的转换,IBUFDS将FPGA输入的差分信号转换为单端信号,而OBUFDS负责把FPGA内部的单端信号转换为差分信号输出。
IBUFDS是一个支持低电压差分信号的输入缓冲器,图1是IBUFDS的框图。在IBUFDS中,有两个输入接口,一个是差分输入的正极端口I,另一个是差分输入的负极端口IB,两个端口的信号极性必须相反才能正常工作,输出端O将输入的差分信号转换为单端信号输出。
IBUFDS的真值表如图2所示(直接从UG768中截图),注意只有当I和IB极性相反的时候才能表示两个信号互为差分对,此时才能正常工作,输出I端口的电平。一般是不会出现均为高电平或低电平状态的,除非差分走线没有做等长处理,此时是硬件设计的问题。
IBUFDS的Verilog HDL原语模板如下所示:
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_inst (
.O(O), // Buffer output
.I(I), // Diff_p buffer input (connect directly to top-level port)
.IB(IB) // Diff_n buffer input (connect directly to top-level port)
);
IBUFDS是在FPGA的输入管脚使用,将外部的差分信号转为单端信号供内部使用。 OBUFDS刚好相反,其在输出管脚使用,将内部的单端信号转换为差分信号输出FPGA。图3是OBUFDS的框图,I为单端输入信号,O为差分输出正极,OB是差分输出负极。
OBUFDS功能比较简单,对应真值表(可以从UG768手册直接获取)如图4所示。
OBUFDS对应的Verilog HDL原语模板如下所示:
OBUFDS #(
.IOSTANDARD("DEFAULT"), // Specify the output I/O standard
.SLEW("SLOW") // Specify the output slew rate
) OBUFDS_inst (
.O(O), // Diff_p output (connect directly to top-level port)
.OB(OB), // Diff_n output (connect directly to top-level port)
.I(I) // Buffer input
);
除了上述单向的差分信号外,可以利用三态差分转换器件IOBUFDS实现双向差分转换,对应的框图如图5所示,T是三态的使能信号,低电平有效(图中少画了圈),T为低电平时,I作为FPGA输出的单端信号,IO和IOB作为转换后的差分输出引脚。当T为高电平时,三态门关闭,此时IO作为差分输入的正极,IOB作为差分输入的负极,O作为差分输入转换后的单端输入信号。
对应的真值表如图6所示,
IOBUFDS对应的Verilog HDL原语模板如下所示:
IOBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination ("TRUE"/"FALSE")
.IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE"
.IOSTANDARD("BLVDS_25"), // Specify the I/O standard
.SLEW("SLOW") // Specify the output slew rate
) IOBUFDS_inst (
.O(O), // Buffer output
.IO(IO), // Diff_p inout (connect directly to top-level port)
.IOB(IOB), // Diff_n inout (connect directly to top-level port)
.I(I), // Buffer input
.T(T) // 3-state enable input, high=input, low=output
);
这几个原语功能比较简单,使用一个工程完成三个原语的仿真,设计文件如下所示,外部输入的差分信号ibufds_p和ibufds_n,经过IBUFDS转为单端信号ibufds_o,该信号经过触发器延迟一个时钟得到ibufds_o_r,通过OBUFDS转换为差分信号obufds_p和obufds_n输出。外部输入一个三态控制信号t,用于控制IOBUFDS的三态使能端口,使能有效时,将IBUFDS寄存后的信号ibufds_o_r输出。
module buf_ctrl(
input clk ,//系统时钟信号;
input rst ,//系统复位信号,高电平有效;
input iobufds_t,//iobufds使能控制端;
input ibufds_p ,//ibufds正极输入;
input ibufds_n ,//ibufds负极输入;
output obufds_p ,//obufds正极输出;
output obufds_n ,//obufds负极输出;
inout iobufds_p,//iobufds正极输出;
inout iobufds_n,//iobufds负极输出;
output iobufds_i //将IOBUFDS输入的数据输出,防止倍优化掉;
);
reg ibufds_o_r;
wire ibufds_o ;
//例化IBUFDS原语
IBUFDS #(
.DIFF_TERM ( "FALSE" ),// Differential Termination
.IBUF_LOW_PWR ( "TRUE" ),// Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD ( "DEFAULT" ) // Specify the input I/O standard
)
u_IBUFDS (
.O (ibufds_o ),// Buffer output
.I (ibufds_p ),// Diff_p buffer input (connect directly to top-level port)
.IB (ibufds_n ) // Diff_n buffer input (connect directly to top-level port)
);
always@(posedge clk)begin
ibufds_o_r <= ibufds_o;
end
//例化OBUFDS原语
OBUFDS #(
.IOSTANDARD ( "DEFAULT" ),// Specify the output I/O standard
.SLEW ( "SLOW" ) // Specify the output slew rate
)
u_OBUFDS (
.O (obufds_p ),// Diff_p output (connect directly to top-level port)
.OB (obufds_n ),// Diff_n output (connect directly to top-level port)
.I (ibufds_o_r ) // Buffer input
);
//例化IOBUFDS原语
IOBUFDS #(
.DIFF_TERM ("FALSE" ),// Differential Termination ("TRUE"/"FALSE")
.IBUF_LOW_PWR ("TRUE" ),// Low Power - "TRUE", High Performance = "FALSE"
.IOSTANDARD ("BLVDS_25" ),// Specify the I/O standard
.SLEW ("SLOW" ) // Specify the output slew rate
)
u_IOBUFDS (
.O (iobufds_i ),// Buffer output
.IO (iobufds_p ),// Diff_p inout (connect directly to top-level port)
.IOB (iobufds_n ),// Diff_n inout (connect directly to top-level port)
.I (ibufds_o_r ),// Buffer input
.T (iobufds_t ) // 3-state enable input, high=input, low=output
);
endmodule
对应的TestBench文件如下所示:
`timescale 1 ns/1 ns
module test();
parameter CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns;
reg clk ;//系统时钟,默认100MHz;
reg rst ;//系统复位,默认高电平有效;
reg iobufds_t ;
reg ibufds_p ;
reg ibufds_n ;
wire obufds_p ;
wire obufds_n ;
wire iobufds_i ;
wire iobufds_p ;
wire iobufds_n ;
buf_ctrl u_buf_ctrl (
.clk ( clk ),
.rst ( rst ),
.iobufds_t ( iobufds_t ),
.ibufds_p ( ibufds_p ),
.ibufds_n ( ibufds_n ),
.obufds_p ( obufds_p ),
.obufds_n ( obufds_n ),
.iobufds_i ( iobufds_i ),
.iobufds_p ( iobufds_p ),
.iobufds_n ( iobufds_n )
);
reg iobufds_p_i;
reg iobufds_n_i;
assign iobufds_p = (iobufds_t) ? iobufds_p_i : 1'bz;
assign iobufds_n = (iobufds_t) ? iobufds_n_i : 1'bz;
//生成周期为CYCLE数值的系统时钟;
initial begin
clk = 1;
forever #(CYCLE/2) clk = ~clk;
end
//生成复位信号;
initial begin
rst = 0;
#2;
rst = 1;//开始时复位10个时钟;
#(10*CYCLE);
rst = 0;
repeat(220) @(posedge clk);
$stop;//停止仿真;
end
initial begin
#1;
ibufds_p = 1'b0;
ibufds_n = 1'b1;
iobufds_t = 1'b1;
iobufds_p_i = 1'b0;
iobufds_n_i = 1'b1;
#(CYCLE*12);
ibufds_n = ibufds_p;
#(CYCLE*2);
ibufds_p = ~ibufds_p;
#(CYCLE*2);
ibufds_n = ibufds_p;
#(CYCLE*2);
repeat(200)begin
ibufds_p = ({$random} % 2);
ibufds_n = ~ibufds_p;
#(CYCLE);
iobufds_t = ({$random} % 2);
#(CYCLE);
iobufds_p_i = ({$random} % 2);
iobufds_n_i = ~iobufds_p_i;
#(CYCLE);
end
end
endmodule
仿真结果如图7所示,图中对IBUFDS的四种输入取值做了仿真,当IBUFDS的输入同时为高电平或者低电平时,IBUFDS转换的输出信号会保持之前的状态不变。在实际情况下应该避免这种情况出现,其余情况IBUFDS与真值表结果一致,不再赘述。
下图主要关注OBUFDS相关信号,如图黄色信号为OBUFDS输入单端数据,而红色信号为OBUFDS输出差分信号,由图可知仿真正确。
IOBUFDS的仿真如图9所示,红色信号为IOBUFDS的三态使能信号,橙色信号是IOBUFDS的单端输出信号,对应真值表中的I,天蓝色信号表示IOBUFDS的双向差分信号,紫色信号是IOBUFDS的单端输入信号,对应真值表中的O。
当使能信号为高电平时,IOBUFDS的三态门关闭,此时两路天蓝色差分信号为输入信号,紫色信号是两路差分输入转换为单端的结果,紫色信号与差分正极的逻辑保持一致,与橙色信号无关,所以正确。
当使能信号为低电平时,IOBUFDS的三态门打开,此时IOBUFDS把橙色单端信号转换为两路天蓝色的差分信号输出,紫色信号此时与橙色信号是一致的,与普通三态门逻辑一致,仿真正确。
通过上文仿真可以知道逻辑实现,但是FPGA是如何实现差分输入和输出的呢?将上述工程分配管脚,然后实现,生产比特流文件,最后查看信号在芯片中的走线及布局。
如图10所示,是IBUFDS两个差分输入管脚的布局,Xilinx把两个相邻的管脚作为差分信号的两个管脚,通过两个IOB模块实现IOBUFDS的转换功能。
图11是OBUFDS的实现情况,把两个管脚组合成差分信号,通过OBUFDS位于P脚的IOB之中,也就是单端引脚OBUF的位置,该结构可以作为OBUFDS使用,如图12所示。
IOBUFDS的情况稍微复杂一点,因为是双向信号,所以会同时使用ILOGIC和OLOGIC,并且因为有三态使能信号,所以OLOGIC需要同时使用上下两条路径,如图13所示,ILOGIC和OLOGIC在IDDR和ODDR文中已经详细分析过,此处不再赘述。
综上,FPGA会将相邻两个管脚配置为差分对,保证两路信号足够近,延时足够小,IBUF和OBUF的位置根据功能不同可能实现IBUFDS、OBUFDS、IOBUFDS等功能,在FPGA中经常可以看见同一器件可以复用多种功能,OLOGCI被作为ODDR,OFB等功能。
参考资料:UG768 Xilinx 7 Series FPGA and Zynq-7000 All Programmable SoC Libraries Guide for HDL Designs
本文工程在后台回复“BUF”(不包括引号)即可,UG768直接回复”xilinx手册”即可,包含前文用到xilinx相关所有手册。