Menu Close

VHDL中std_ulogic与std_logic的区别

在VHDL程序设计中经常会涉及到std_ulogic与std_logic逻辑类型,这两种类型都是以数据包形式由IEEE库提供。VHDL包含很少的内置类型,但通过扩展包提供了几种附加类型。两种最广泛使用的类型是 std_logic 和 std_ulogic。它们之间的区别在于前者具有决断特性,而后者没有具有决断特性。那么什么具有决断特性呢?在IEEE.std_logic_1164数据中,提供了一个称为决断特性的函数。下面先看看std_ulogic和std_logic的数据类型,然后再研究一下决断函数,就明白了它们两者的区别。

1. std_ulogic与std_logic数据类型

Bit和boolean是标准包的一部分,不需要导入即可使用它们。但std_logic和std_ulogic只能在导入IEEE.std_logic_1164包后才能使用。要导入导入IEEE.std_logic_1164,只需将这些行添加到VHDL文件的顶部即可,导入格式如下:

 library ieee;

use ieee.std_logic_1164.all;

std_logic和 std_ulogic这两种类型的共同点是它们可以用表1的值表示:

表1

‘1’ Logic 1 逻辑 1
‘0’ Logic 0 逻辑 0
‘Z’ High impedance 高阻
‘W’ Weak signal, can’t tell if 0 or 1 弱信号,不能分辨是0还是1
‘L’ Weak 0, pulldown 弱0, 下拉(弱)
‘H’ Weak 1, pullup 弱1, 上拉(弱)
‘-‘ Don’t care 无关,一般指输入对逻辑或输出没有影响
‘U’ Uninitialized 未初始化
‘X’ Unknown, multiple drivers 未知,多重驱动的结果
大多数情况下,将使用“1”和“0”来表示逻辑高或低值。 'U' 将用于表示未初始化的值,例如启动时的RAM内容。有时也会看到“X”值,表示某种驱动程序冲突。
  • STD_ULOGIC – THE UNRESOLVED TYPE
我们先来看看std_ulogic类型,两者的未解析版本。下面是从std_logic_1164包的实现中摘录的类型声明。
TYPE std_ulogic IS ( 
                     'U',  -- Uninitialized
                     'X',  -- Forcing  Unknown
                     '0',  -- Forcing  0
                     '1',  -- Forcing  1
                     'Z',  -- High Impedance   
                     'W',  -- Weak     Unknown
                     'L',  -- Weak     0       
                     'H',  -- Weak     1       
                     '-'   -- Don't care
                   );

std_ulogic 是一种枚举类型,它将可能的值列为枚举文字。当使用std_ulogic作为类型声明信号或变量时,信号或变量的值可以是这些值中的任何一个,而不能再有其他值。“U”值是枚举值中的第一个。这就是如果没有初始值,std_ulogic 类型的信号将获得值“U”的原因。默认情况下,未初始化的信号将从类型声明中获取最左边的值。这就是 VHDL 的工作原理。那么为决断的数据类型到底是什么含义呢?看下面的例子,

例1:

library ieee;
use ieee.std_logic_1164.all;
 
entity Unresolved_tb is
end entity;
 
architecture sim of Unresolved_tb is
    signal Sig1 : std_ulogic := '0';
begin
 
    -- Driver A
    Sig1 <= '0';
 
    -- Driver B
    Sig1 <= '1' after 20 ns;
     
end architecture;

如果再Vivado或Modelsim下编译,可以看到如下类似的结果:

# ** Error: C:/proj/tb.vhd(8): Nonresolved signal ‘Sig1′ has multiple sources.

# Drivers:

# C:/proj/tb.vhd(12):Conditional signal assignment line__12

# C:proj/tb.vhd(15):Conditional signal assignment line__15

# ** Error: C:/proj/tb.vhd(17): VHDL Compiler exiting
从编译结果可以看出,编译器提示信号’Sig1’ 有多重驱动。分析一下原因就会发现,Sig1的两条赋值语句都是并发赋值语句,而且一条语句赋值为0,另一条赋值为1;因此最终结果未知。但是std_ulogic类型对于该运算结果并没有解决方案。

2. 决断函数

但是在std_logic_1164的程序包中,有定义另一种类型,即std_logic类型,SUBTYPE std_logic IS resolved std_ulogic;

可以看出std_logic类型是std_ulogic类型的子类型,而且是resolved std_ulogic类型。那么resolved到底起到什么作用呢?在该数据包里还定义了一个函数:

function resolved (s : std_ulogic_vector) return std_ulogic; 可以看出std_logic是resolved 函数的返回值,数据类型依然是std_ulogic。下面看看resolved 函数到底是什么,该函数的实现是在std_logic_1164-body.vhd实现的。首先看一个常数数组,即二维的决断表。

constant resolution_table : stdlogic_table := (
   --      ---------------------------------------------------------
   --      |  U    X    0    1    Z    W    L    H    -        |   |
   --      ---------------------------------------------------------
            ('U', 'U', 'U', 'U', 'U', 'U', 'U', 'U', 'U'),  -- | U |
            ('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'),  -- | X |
            ('U', 'X', '0', 'X', '0', '0', '0', '0', 'X'),  -- | 0 |
            ('U', 'X', 'X', '1', '1', '1', '1', '1', 'X'),  -- | 1 |
            ('U', 'X', '0', '1', 'Z', 'W', 'L', 'H', 'X'),  -- | Z |
            ('U', 'X', '0', '1', 'W', 'W', 'W', 'W', 'X'),  -- | W |
            ('U', 'X', '0', '1', 'L', 'W', 'L', 'W', 'X'),  -- | L |
            ('U', 'X', '0', '1', 'H', 'W', 'W', 'H', 'X'),  -- | H |
            ('U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X')   -- | - |
            );
function resolved (s : STD_ULOGIC_VECTOR) return STD_ULOGIC is
    variable result : STD_ULOGIC := 'Z';  -- weakest state default
  begin
    -- the test for a single driver is essential otherwise the
    -- loop would return 'X' for a single driver of '-' and that
    -- would conflict with the value of a single driver unresolved
    -- signal.
    if (s'length = 1) then return s(s'low);
    else
      for i in s'range loop
        result := resolution_table(result, s(i));
      end loop;
    end if;
    return result;
  end function resolved;

决断函数与一般函数有如下不同之处:

(1)信号赋值时自动调用

(2)使用没有范围限制的STD_ULOGIC_VECTOR矢量,表示多重赋值的次数不受限制,如果只有一次,就变成对std_ulogic类型的信号赋值。

(3)STD_ULOGIC_VECTOR矢量是指多重赋值的形成的赋值矢量,而不是对std_ulogic_vector<range> 类型的决断。

首先分析决断表的含义,如果有个变量前后两次的值不同且是并发赋值的,可以在2维数组交叉查出应该得到什么输出值。如上例中第一次为0,第二次为1,查表后得到’X’,因此输出就是’X’,如图1所示。这样就把问题解决了。因此resolved函数起到决断作用。修改例1的代码如例2所示:

%title插图%num

图1

例2:std_logic类型信号多重并发赋值(多驱动)。

library ieee;
use ieee.std_logic_1164.all;
 
entity resolved_tb is
end entity;
 
architecture sim of resolved_tb is
    signal Sig1 : std_logic := '0';
begin
 
    -- Driver A
    Sig1 <= '0';
 
    -- Driver B
    Sig1 <= '1' after 20 ns;
     
end architecture;

在vivado下仿真,没有编译错误,波形如图2所示,

%title插图%num

图2

从图1可以看出,在20 ns后,Sig1的输出为X。可见std_logic在多重驱动时有决断结果的。这样就为数字建模,仿真,调试带来方便。9值逻辑对问题的描述更细致,更准确。

例3:  观察std_ulogic类型的多重驱动在modelsim下编译情况

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity std_logic_test_tb is

end std_logic_test_tb;

architecture ARCH_std_logic_test of std_logic_test_tb is
signal test_a: std_ulogic;--:='L';
signal test_b: std_ulogic:='H';

begin
--test_a<='Z';
test_a<='1' after 60 ns;
test_a<='H' after 40 ns;


 --test_a<='1';
-- test_a<='1' after 20 ns;
-- test_a<='L' after 20 ns;

 test_a<='0' after 80 ns;
 test_a<='U' after 100 ns;
 
  test_a<='0' after 120 ns;
 
 test_b<='L' after 20 ns;
 test_b<='1' after 40 ns;
 test_b<='0' after 60 ns;

end  ARCH_std_logic_test;

上例在modelsim 下编译时会报出如下错误信息如图3所示,

%title插图%num

图3  std_ulogic多重驱动的编译错误信息

图3显示std_ulogic只是定义相应的数据类型,但多驱动时并没有解决方案。修该例3的代码,如例4所示,将会得到正确的输出结果。

例4:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity std_logic_test_tb is

end std_logic_test_tb;

architecture ARCH_std_logic_test of std_logic_test_tb is
signal test_a: std_logic;--:='L';
signal test_b: std_logic:='H';

begin
--test_a<='Z';
test_a<='1' after 60 ns;
test_a<='H' after 40 ns;


 --test_a<='1';
-- test_a<='1' after 20 ns;
-- test_a<='L' after 20 ns;

 test_a<='0' after 80 ns;
 test_a<='U' after 100 ns;
 
  test_a<='0' after 120 ns;
 
 test_b<='L' after 20 ns;
 test_b<='1' after 40 ns;
 test_b<='0' after 60 ns;

end  ARCH_std_logic_test;

例4使用std_logic数据类型后,编译正确通过,仿真波形如图4所示,

%title插图%num

图4

同时也可以看出,由于test_a在信号声明时为初始化,因此在多重并发赋值时只接受最后一个时刻的赋值评估,本例在100ns 时评估test_a有’H’- -40ns , ‘1’- -60 ns, ‘0’- – 80 ns, ‘U’- -100ns 。 由于’U’强于其它类型,因此最终的结果与初始结果一致,都为’U’。 对于test_a仅保留如下两句

test_a<=’1′ after 60 ns;
test_a<=’H’ after 40 ns;

仿真结果如图5所示,

%title插图%num

图5

图5的结果充分说明,对于未初始化的变量在决断时,仅对最后时刻的所有赋值进行评估并决断。而对于声明时有初值的信号,则按照信号发生的时刻进行评估。

修改例4的代码如例5所示,将test_a在声明时初始化。

例5:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity std_logic_test_tb is

end std_logic_test_tb;

architecture ARCH_std_logic_test of std_logic_test_tb is
signal test_a: std_logic:='L';
signal test_b: std_logic:='H';




begin
--test_a<='Z';
test_a<='1' after 60 ns;
test_a<='H' after 40 ns;


 --test_a<='1';
-- test_a<='1' after 20 ns;
-- test_a<='L' after 20 ns;

 test_a<='0' after 80 ns;
 test_a<='U' after 100 ns;
 
 test_a<='0' after 120 ns;
 
 test_b<='L' after 20 ns;
 test_b<='1' after 40 ns;
 test_b<='0' after 60 ns;

end  ARCH_std_logic_test;

仿真波形如图5所示,

%title插图%num

图5

从图5的显示结果可以看出,test_a输出结果逐渐从’L’,’H’,’1′, ‘X’, ‘U’的变化。而且由于’U’类型强度最高,即使在120 ns赋值‘0’也不会再发生变化。

思考题: 信号未初始化时的默认值为’U’与使用并发赋值语句对信号赋值’U’有何不同?


		
Posted in FPGA, FPGA 教材教案, IC, IC 教材教案, VHDL, VHDL, Vivado, 开发工具, 开发语言, 教材与教案, 数字集成电路, 文章, 编程语言

发表回复

相关链接