FPGA 中双向端口I/O的研究
时间:2007-03-07 来源: 作者:莫海永张申科 点击:2178 字体大小:【大 中 小】
摘要:针对现场可编程门阵列(FPGA)芯片的特点,研究 FPGA 中双向端口I/O 的设计,同时给出仿真初始化双向端口I/O 的方法。采用这种双向端口的设计方法,选用 Xilinx 的Spartan2E 芯片设计一个多通道图像信号处理系统。
引言
FPGA 是现场可编程门阵列的缩写,它是在PAL,GAL 及 EPLD 等可编程器件的基础上发展起来的。FPGA 具有速度快、密度高、功耗小的特点。采用 FPGA 芯片进行专用集成电路设计,既可以解决定制电路缺乏灵活性的不足,又可以通过相关的软硬件环境掌握芯片的最终功能,提高一次设计的成功率,所以,目前 FPGA 在电子设计中已被广泛使用。同时,由于 FPGA 经常要和外部存储器及 CPU 进行数据输入输出交换,而利用双向端口的设计来进行数据交换可以成倍地节省各自的引脚资源。
双向端口顾名思义是一种既可以作为输入端口接收数据,也可以作为输出端口发出数据,它对数据的操作是双向的。比如某个设计需要一个 16 位的数据输入口和一个16 位的数据输出口,并且数据输入和输出不会同时发生。如果数据输入口和输出口分别设计则需要32根数据线,而用双向端口来设计,则只需要16 根数据线,这样就节省了16 根数据线引脚。由于现在的大多数资料对双向端口的设计介绍很少,本文给出 FPGA 中双向端口的设计原理和方法,以及仿真和初始化双向端口的方法,同时选用Xilinx的Spartan2E 芯片进行实际应用。
1、 FPGA 中双向端口的设计原理和Verilog硬件语言程序设计
首先介绍双向端口在 FPGA 内部硬件资源是怎样实现的。在 FPGA 中它是通过对三态门控制来实现双向端口的,比如在 Xilinx 的Spartan2E 中的图例(如图1 所示):
图1 双向端口的硬件图
当z=0 时,上面输出的管子开通,此时数据可以从上面的管子中输出,这时双向端口就作为输出口;当z=1 时,上面的管子被置为高阻态,数据不能从上面的管子输出,此时数据只可以从下面的管子由外向内输入,这时的双向端口是输入口。限于篇幅,我们做一个简单的模型来说明双向端口的设计。下面我们用 Verilog 硬件语言进行双向端口的程序设计,为了看出双向端口分别作为输入端口和输出端口的功能,我们的模块分别定义一个数据输入口 din 和一个数据输出口 dout,一个三态门选通信号 z,触发时钟 clk,还有双向端口 dinout。我们设数据为8 位宽。图2为该模块图:
图2 定义的模块图
输入口din 定义:input [7:0] din;当双向端口 dinout作为输出口时,我们从 din 端口输入数据到模块中,让数据从dinout口出来。
输出口 dout定义:output [7:0] dout;当双向端口dinout作为输入口时,我们让数据从dinout口输入,从输出口dout输出。
双向端口dinout定义:inout[7:0] dinout;
三态门选通信号z:input z;
当z=1 时,把三态门置为高阻态,这时 dinout 作为输入口用;当z=0 时,开通三态门,这时 dinout 作为输出口用。
三态门控制语句为: asigndinout=(!z)?din_reg:8'bz;
总的完整程序如下:
module dinout(din,z,clk,dout,dinout);
input [7:0] din;
input z;
input clk;
output[7:0] dout;
inout [7:0] dinout;
reg [7:0] dout;
reg [7:0] din_reg;
asign dinout= (!z)?din_reg:8'bz;
always @ (posedge clk)
begin
if(!z)
din_reg=din;
else
dout=dinout;
end
Endmodule
2、仿真及初始化双向端口
下面我们对上述程序进行时序仿真。这里我们选用的 FPGA 芯片为 Xilinx 的 Spartan2E 系列,型号为 xc2s300e-7pq208,在ISE Foundation6.1 软件中综合及布局布线,并用 Modelsim Simulator进行时序仿真。
当双向端口dinout作为输出口时,我们不需要对它进行初始化,只要开通三态门。
我们设定在200ns后,让数据10,11,12,13,14,15,16,17,18,19,20 依次从 din 口输入,然后用20ns的采样时钟从dinout口输出。
它的测试仿真顶层模块为
`timescale 1ns/1ps
module dinoutest();
reg [7:0] din;
reg z;
reg clk;
wire [7:0] dout;
wire [7:0] dinout;
integer i;
dinout uut(
.din(din),
.z(z),
.clk(clk),
.dout(dout),
.dinout(dinout)
);
always #10clk=~clk;
initial begin
din = 0;
z = 0;
clk = 0;
# 200 din=10;
for(i=0;i<10;i=i+1)
#20 din=din+1;
end
endmodule
该仿真的时序图如图3 所示:
可以看出这时双向端口 dinout作为输出口, 输出
从din 输入口输入到模块中的数据。
当双向端口dinout作为输入口时,我们需要对它进行初始化赋值,此时关闭三态门。而对双向端口的初始化赋值,如果把它跟一般的输入口一样直接赋值给它,则会出错,因为在定义它的时候是wire型了,而不是reg 型。在许多 Verilog 书籍和参考资料中,有关双向端口的初始化赋值介绍的很少。这里需要用到一个force 命令,用来给dinout 输入赋值。我们设定在200ns 后,让数据20,19,18,17,16,15,14,13,12,11,10 从dinout口输入模块,然后用 20ns的采样时钟从输出口 dout输出。以下是它的仿真顶层模块
`timescale 1ns/1ps
module dinoutest();
reg [7:0]din;
reg z;
reg clk;
wire [7:0] dout;
wire [7:0] dinout;
integeri;
dinoutuut(
.din(din),
.z(z),
.clk(clk),
.dout(dout),
.dinout(dinout)
);
always #10clk= ~clk;
initial begin
z = 1;
clk = 0;
force dinout=20;
# 200 for (i=0;i<10;i=i+1)
#20 force dinout=dinout-1;
end
endmodule
该仿真的时序图如图4 所示:
图4 双向端口作为输入口时的时序仿真
可以看出这时双向端口 dinout作为输入口,数据从dinout口输入模块,然后从输出口dout完整地输出来。
3 、实际应用
这种双向端口设计方法已被我们用到目前开发的一个多通道图象捕获和显示控制系统中。在该系统中,FPGA 接收从两个图像传感器传来的数字视频信号,然后根据CPU的命令对这两路视频信号进行四种显示模式的组合操作。FPGA 将处理好的信号存到外部存储器中。如果LCD显示信号来了, FPGA 从外部存储器中取数据给LCD显示。如果CPU 想要对某一帧图像数据进行图象识别操作,可以经过 FPGA 从外部存储器中把该帧图象数据取下来。其中 LCD 的时钟频率为 6M,FPGA 对外部存储器及 CPU 数据操作的时钟频率为 24M,并且LCD显示操作优先级最高。在这个系统中用到了两个双向端口,一个是FPGA 与CPU的数据交换, 另一个是FPGA与外部存储器之间的数据交换,他们都是16 位的数据交换,由于用了双向端口,节省了32个引脚资源,同时优化了器件的选择和整体设计,降低了成本。图 5为多通道图象捕获和显示控制系统的框图。
4、结论
由于现在 FPGA 设计和外部存储器或 CPU 数据交换的频繁运用,以及引脚资源有限,而使用双向端口设计可以成倍的节省数据引脚线,所以设计好FPGA 的双向端口至关重要。在设计 FPGA 中的双向端口时应注意两点:
其一,要用三态门的控制来处理实现双向端口;
其二,要分别指定双向端口作为输出口和输入口时,对外部对象的数据操作。
(转)inout端口在VerilogHDL中的仿真
本文转自“璐珈山之鹰”的qzone。
-----------------------------------
很多初学者在写testbench进行仿真和验证的时候,被inout双向口难住了。仿真器老是提示错误不能进行。下面是我个人对inout端口写testbench仿真的一些总结,并举例进行说明。在这里先要说明一下inout口在testbench中要定义为wire型变量。
先假设有一源代码为:
module xx(data_inout , ........);
inout data_inout;
........................
assign data_inout=(! link)?datareg:1'bz;
endmodule
方法一:使用相反控制信号inout口,等于两个模块之间用inout双向口互连。这种方法要注意assign 语句只能放在initial和always块内。
module test();
wire data_inout;
reg data_reg;
reg link;
initial begin
..........
end
assign data_inout=link?data_reg:1'bz;
endmodule
方法二:使用force和release语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以反在块内。
module test();
wire data_inout;
reg data_reg;
reg link;
#xx; //延时
force data_inout=1'bx; //强制作为输入端口
...............
#xx;
release data_inout; //释放输入端口
endmodule