三态门介绍:
三态门包括:输入状态、输出状态、高阻态。三态信号定义:inout
在FPGA设计中,经常会用到数据输入与输出,与外部芯片进行数据传输时,经常会用到三态门,比如我现在正学的SDRAM,为了节约端口资源,通常会将一些数据端口即作为输入又作为输出,这样的端口就叫做三态门。
SDRAM芯片的数据端口dq就是典型的三态门,在执行写操作时,dq作为输入端口;在执行读操作时,dq作为输出端口;其他时间为高阻态。在写FPGA和SDRAM接口时,必然需要一个三态信号dq作为与SDRAM连接的信号。
用一个例子来直观说明一下:
以下是Verilog程序:
module tri_state(
input clk ,
input rst_n ,
input data ,
input dout_en ,
output reg odata ,
inout dout
);
assign dout = (dout_en==1)? data : 1'bz;
always@(posedge clk or negedge rst_n)begin
if(rst_n == 0)
odata <= 0;
else if(dout_en!=1)
odata <= dout;
end
endmodule
三态信号为dout,其RTL视图:
可以看出,当dout_en为1时,dout输出data数据,当dout_en为0时,dout为高阻,此时,dout可作为输入端口,作为输入端口时,FPGA内部自动生成一个buffer作为输入数据缓冲。
三态门quartus编译及Modelsim仿真问题:
1. quartus编译三态门
以在写的SDRAM接口为例:在verilog代码里有关三态信号dq的代码段如下(有错):
inout reg [ 15: 0] dq ; //数据线
output reg [ 15: 0] rdate ;
//dq信号输出,数据线
always@(posedge clk or negedge rst_n)begin
if(rst_n == 0)begin
dq <= 16'hzzzz;
end
else if(wr_write_start || state_c==wr_write)
dq <= wdata;
else
dq <= 16'hzzzz;
end
always@(posedge clk or negedge rst_n)begin
if(rst_n == 0)
rdate <= 0;
else if(state_c==rd_read || state_c==rd_pre)
rdate <= dq;
end
此时在quartus里编译不会报错,但在Modelsim里编译时便会报错,原因在于,三态信号应定义为wire型信号,这样在Modelsim里编译便不会报错。在我的理解看来:其实dq三态信号定义为wire或者reg型都可以,虽然reg型在Modelsim里编译不通过,但下载在板子中结果都是一样的,有兴趣自己可以试一下。但因此默认情况下我们就将三态信号设置成wire型。修改后的代码如下:
inout [ 15: 0] dq ;
reg [ 15: 0] dq_t ;
//dq信号输出,数据线
assign dq = dq_t;
always@(posedge clk or negedge rst_n)begin
if(rst_n == 0)begin
dq_t <= 16'hzzzz;
end
else if(wr_write_start || state_c==wr_write)
dq_t <= wdata;
else
dq_t <= 16'hzzzz;
end
always@(posedge clk or negedge rst_n)begin
if(rst_n == 0)
rdate <= 0;
else if(state_c==rd_read || state_c==rd_pre)
rdate <= dq;
end
主要就是将dq定义成wire型,可以再定义一个reg型中间信号dq_t,在赋值给dq。
2.Modelsim编译三态门
在编写测试文件时,对inout型信号,如果这样写:
wire [15:0] dq;
initial begin
for(i=1 ;i<260;i=i+1)begin
dq = (i==1)? 1:(dq+1'b1);
#(clk_period);
end
end
Modelsim会报错:Illegal reference to net "dq".
dq为三态门,定义wire型,却当reg型给数据,肯定会报错。若定义为reg型,如下:
reg [15:0] dq;
initial begin
for(i=1 ;i<260;i=i+1)begin
dq = (i==1)? 1:(dq+1'b1);
#(clk_period);
end
end
编译不会报错,但是会出现家在错误:# Error loading design.
对三态信号进行这样处理(又用了其他代码,看着很乱,理解意思就行):(加一个reg型中间信号,用assign赋给dq,下边赋值时,就用这个信号)
parameter WIDTH = 16;
wire [WIDTH-1 : 0] dq;
reg tri_en;
reg [WIDTH-1 : 0] data_w;
assign dq = (tri_en) ? data_w : 16'hzzzz;
module_1 u_module_1(
);
initial begin
tri_en = 0;
#(clk_period);
tri_en = 1;
data_w = 16'h1111;
#(clk_period);
tri_en = 0;
data_w = ......
end
因此在对三态门编写测试文件时应满足:
- 三态信号为wire型;
- 定义reg型中间信号assign给三态信号;
- 在需要三态信号作为输入端时,用中间信号对其赋值。
最后,总结一下:
- 在工程中写三态信号Verilog代码将三态信号定义为wire型;
- 测试文件中三态信号也定义为wire型,并用中间信号代替三态信号进行赋值输入。
最后还想说一句,这篇写的很乱,我很难受,但理解意思就好啦,主要看一下总结。后续会对SDRAM接口的编写过程进行分享。