最近项目中要用到双向三态口,于是调用ALTERA 的Q2里LPM.
从上到下分别是真值表,和RTL view里的视图及原理图模块.由于是双向口所以在modelsim里防真.
以前曾自己用verilog写的
module bidir_infer(data,read_write);
input read_write;
inout [1:0] data;
reg[1:0] data_reg,state;//data_reg为映象寄存器,state为寄存器
always@(read_write or data)
begin
if(read_write==0)//0为输出,1为输入,0时state寄存器的值附给映象寄存器,映象寄存器输出到data端口
//1时为输入,data值直接附给state寄存器,且三态口要断开。
data_reg<=state;
else
state<=data;
end
assign data=(read_write==0)?data_reg:2'bz;//1时assign语句data是输出,
endmodule
其testbench
module bidir_test ;
reg read_writet;
reg [1:0] data_in;
wire [1:0] datat,data_out;
bidir_infer uut (datat,read_writet);
assign datat=(read_writet==1)?data_in:2'bz;
assign data_out=(read_writet==0)?datat:2'bz;
initial
begin
read_writet=1;
data_in=11;
#100 data_in=10;
#100 data_in=01;
#100 data_in=11;
#100 read_writet=0;
#100 read_writet=1;
data_in=10;
#100 data_in=01;
#100 read_writet=0;
end
endmodule
但是调用LPM的话,是否需要assign 语句使其赋高阻Z呢?
assign datat=(read_writet==1)?data_in:2'bz;
assign data_out=(read_writet==0)?datat:2'bz;
从真值表来看是不需要的,于是查了下相关资料,借鉴了下面一部分:
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完整地输出来。
文中很容易理解的是不需要自己去赋高阻,因为自己编的是模拟双向三态口,而LPM是已经具有双向三态口模型,但是注意到红色那段话:
当双向端口dinout作为输入口时,我们需要对它进行初始化赋值,此时关闭三态门。 而对双向端 口的初始化赋值,如果把它跟一般的输入口一样直接赋 值 给它,则会出错,因为在定义它的时候是wire型了,而 不是reg 型。 在许多 Verilog 书籍和参考资料中,有关双向端口的初始化赋值介绍的很少。这里需要 用到一个force 命令,用来给dinout 输入赋值。 我 们设定在200ns 后,让数据20,19,18,17,16,15,14,13,12,11,10 从dinout口输入模块,然后用 20ns的采样时钟从输出口 dout输 出。以下是它的仿真顶层模块
我也试过当没有用force 的话,是会出错的.
自己写的testbench如下:
initial
begin
clk = 1'b0; //时钟要赋初值
end
always #10clk= ~clk;
// in
initial
begin
in = 1'b0;
in = #100.0 1'b1;
in = #1100.0 1'b0;
//in = #2320.0 1'b1;
//in = #780.0 1'b0;
end
// out
initial
begin
out = 1'b0;
out = #1200.0 1'b1;
out = #2200.0 1'b0;
end
// wreq2
initial
begin
wreq2 = 1'b0;
wreq2 = #200.0 1'b1;
wreq2 = #1200.0 1'b0;
//wreq2 = #1940.0 1'b1;
//wreq2 = #880.0 1'b0;
end
// rdreq2
initial
begin
rdreq2 = 1'b0;
rdreq2 = #1300.0 1'b1;
rdreq2 = #2300.0 1'b0;
//rdreq2 = #940.0 1'b1;
//rdreq2 = #740.0 1'b0;
end
// wreq
initial
begin
wreq = 1'b0;
wreq = #100.0 1'b1;
wreq = #1100.0 1'b0;
//wreq = #2320.0 1'b1;
//wreq = #780.0 1'b0;
end
// rdreq
initial
begin
rdreq = 1'b0;
rdreq = #200.0 1'b1;
rdreq = #1300.0 1'b0;
//rdreq = #1580.0 1'b1;
end
// data[ 31 ]
// initial
// begin
// treg_data = 32'bZ;
// end
initial
begin
data2 = 32'b0;
# 200 for(i=0;i<50;i=i+1)
#20 data2=data2+1;
end
initial
begin
#100 force data="200";
for (j=0;j<50;j=j+1)
#20 force data="data"+1;
文章评论(0条评论)
登录后参与讨论