首页 > 技术知识 > 正文

前言

关于高阻态,我们常见到的场景有两处,一种出现在仿真中,一种是逻辑设计中,它常常是无意乱出,还或者是有意而为之。

下面对这几种情况进行简单梳理。

高阻态回顾

我们在上一篇文章中的数据类型中介绍了包括高阻态的一系列数据类型,即线网型和变量型:

线网类型表示电路元件之间的连接,我们常用其中的一种wire,这足以创建所有线网类型的设计。

变量类型表示可以存储值得数据类型,我们常用的变量类型是reg,我们常用reg对寄存器或触发器进行建模,它在always块中进行赋值。另外,不能讲输入端口定义为reg型,输入应该是线网型,这很容易,因为现实中一个电路元件的输入来自于另一方的线网。

对这些数据类型进行赋值,使用到了逻辑值,有4种逻辑值:

0 logic ‘0’ or false condition 1 logic ‘1’ or true condition z high impedance state (used for tri-state buffer) x don’t care or unknown value

其中z就是高阻态。

例如:

reg [4:0] a = 5’bz //z is high impedance reg [4:0] a = 5’bx01 //z is high impedance

高阻态的应用

我们常见到这样的结构,使用inout端口类型定义一个变量,做如下处理:

module test ( inout bidirPin, input toPin, input direction, output fromPin ); assign bidirPin = direction ? 1bz : toPin; assign fromPin = bidirPin;

如下这条语句中就出现了高阻态的赋值:

assign bidirPin = direction ? 1bz : toPin;

这通常是什么含义呢?

可以这么解释:

当direction信号为低电平时,bidirPin则为输出,并且信号toPin连接至引脚。当direction处于高电平时,则bidirPin成为输入。在所有情况下,fromPin表示引脚的当前数据。

这种结构要求FPGA具有双向(三态)I / O缓冲器。许多(大多数?)都支持,但并非所有设备上的所有引脚都支持。

如果要在内部使用相同的结构(例如,您具有上述模块的多个实例,并将所有inout引脚连接到单根电线),那么显然可以推断出的内容必须与I / O引脚不同。大多数(如果不是全部)FPGA没有内部三态布线网络。换句话说,High-Z是不可能的。

然而,该逻辑是可综合的。发生的情况是,综合工具查看inout连接到同一网络的所有信号,并简单地推断出多路复用器逻辑。这实际上表示同一件事,但是需要附加仲裁逻辑。通常,使用多路复用器上的优先级编码器可以解决任何可能的总线争用,从而一次只有一个端口可以传送其信号。

如果您要设计与微控制器类似的架构,这可能是将许多外设(例如SPI,UART,RAM等)连接到共享数据总线的一种非常有用的方法。使用高Z值可使综合器处理任何仲裁逻辑。由于您必须遵循在任何RTL网表查看器中添加的其他逻辑,因此这会使调试更加困难,但这是一种完全有效的处理方式。

在仿真中,High-Z可以是可接受的状态(例如,如果您正在观察三态I / O引脚),也可以是主要问题的指示器(例如,如果您使用了generate循环而丢失了数据总线中的某个位)会变为高阻(Z)。

高Z值可以帮助您确定由于诸如生成循环之类的信号未连接而导致的问题,或者,如果您误说了变量名,则导致两个事物无法正确连接。如果您希望仿真中的信号能够变为High-Z,则没有问题。如果您不期望这样做,则需要对原因进行进一步调查。

内部High-Z总线(推断出仲裁的总线)可能会带来进一步的仿真麻烦。例如,如果仿真综合后网表,则可能永远不会在总线上看到High-Z,因为已经推断出仲裁逻辑。如果从源代码级别进行仿真,则可能会在总线上看到High-Z,因为仿真器非常乐意允许High-Z,因为它不知道综合工具将要推断出什么,因此仿真器会坚持下去完全符合您在代码中编写的内容。

设计示例

我们在使用逻辑设计RAM时可能会使用到inout,并使用到z,即高阻态。

例如:单端口RAM的同步读写逻辑:

`timescale 1ns / 1ps // Engineer: Reborn Lee // Module Name: single_port_syn_ram module single_port_syn_ram#( parameter ADDR_WIDTH = 4, parameter DATA_WIDTH = 16, parameter DEPTH = 2**ADDR_WIDTH )( input i_clk, input [ADDR_WIDTH – 1 : 0] addr, inout [DATA_WIDTH – 1 : 0] data, input cs, input wr, input oe ); reg [DATA_WIDTH – 1 : 0] mem[0 : DEPTH – 1]; reg [DATA_WIDTH – 1 : 0] mid_data; // write part always@(posedge i_clk) begin if(cs&wr) begin mem[addr] <= data; end end // read part always@(posedge i_clk) begin if(cs & !wr) begin mid_data <= mem[addr]; end end assign data = (cs & oe & !wr)? mid_data: hz; endmodule
<

对于:

assign data = (cs & oe & !wr)? mid_data: hz;

表达的意义是条件:cs & oe & !wr满足时(ram读),data作为输出端口,而不满足(ram写),data作为输入端口,将数据写入RAM。 总结一下:

assign x = a? b: hz;

当a有效时,x作为输出。 当a无效时,x作为输入值,在逻辑中将其设置为高阻态。

仿真中的高阻态

如果你经常仿真你会发现,高阻态时长会出现在仿真波形中,有两种情况,一种是它本身就应该是高阻态值,另一种情况是你弄错了。 例如,我在Xilinx论坛中,看到一个十分搞笑的错误,虽然搞笑,但能说明问题:

High Impedance Output in Simulation of A Simple Verilog Code

链接中贴出一段代码:

`timescale 1 ns / 1 ps module invT ( input wire inv_in, output wire inv_out ); reg tmp; always @(*) begin tmp <= !inv_in; end assign inv_output = tmp; endmodule

说自己仅仅实现一个反相器,对其进行仿真,给出了输入值,但是输出值一直为高阻态,见鬼了?

提问者很纳闷呀,一个反相器而已,仿真也出现高阻态,简直是对学习热情的打击,这就像是突然想学习一门编程语言,但是决定了几天,一个编码环境都没有搭建成功,这多令人灰心呀。

为什么说这能说明问题?

可以看看输出一致为高阻态的原因,很显然,旁观者清,你的输出根本就没有接入任何输入信号呀,是悬空的,当然是高阻态!

这说明了,输出未有输入值,会出现高阻态的情况,这可能是你的有意而为之,也可能是你的失手,这就为查找bug提供了思路。

当然,还有其他情况,当你遇到了,记得留意分析。

猜你喜欢