从学校里开始,我所接触的就一直是VerilogHDL而非更老牌的VHDL,而且后续接触的项目中也多半是Verilog的用户,坦白的讲,Verilog的活力也确实更足一些,从IEEE1800-2005开始的SystemVerilog的标准化,将Verification和Design的一体化的尝试,我个人认为,是走在正确的道路上。
所以,我确实想不到,我竟然也要回头学起VHDL来了,毕竟一些老牌公司,特别是欧洲的公司,往往因为历史原因,仍然在使用VHDL,看来终究是绕不过去的了——正如一个Design Verification工程师在目前想完全的不和SpecmanE打交道是很难的一样。
下面记载的是两种语言学习过程中的一些体会,而且更多的是从语法角度出发,算是梳理一下思路吧,而且,以前从来没有接触过VHDL,当然会存在很多非常初级的东西。
而在本文之后,打算再写一篇简单阐述下结合Cadence的IUS工具,使用SystemVerilog对VHDL进行验证的基本方法。
从Verilog撞进VHDL的世界,有些东西要先搞清楚,否则会一头雾水:
1. 大小写敏感: Verilog是大小写敏感的,VHDL则非;
2. 注释:Verilog的行注释为//,块注释为/**/;VHDL只支持行注释–;1
VHDL被认为是要求更严格,更多讲究的语言,相比和C类似的Verilog,架构上更严谨一些:
1. 基本结构:从上往下为
USE定义区(调用库和包);
Package定义区(自定义程序包);
Entity定义区(定义电路实体外观,I/O接口的规格); 2
Architecture定义区(描述内部功能); 3
Configuration定义区(决定那个Architecture被使用) 4
2. 并行与串行:
在这一点上,VHDL和Verilog有些类似,书写在Architecture内的语句,直接被认为是并行执行,无论书写顺序的(就如同Verilog中的并列的Always语句块);而顺序执行的串行语句必须放置在进程语句(process)当中,正如Verilog的begin…end。
3. process:
和Verilog不同的是这里的Process是要求有敏感变量列表作为输入的,正如试图在Verilog中用Always语句实现组合逻辑时类似。所以当写这样的语句,不妨多回想Verilog中对于敏感变量列表的要求。
4. 例化:
在VHDL当中尝试例化一个entity,比起Verilog要麻烦不少。首先,必须在Architecture当中用Component语法来声明这个实体的Port和Generic(参数);然后才可在后续内容当中例化实体,并且用generic map和port map来进行参数赋值和port连接。
5. 库,包和配置:
VHDL这方面的组织比起Verilog显然要严密多了,使用configuration,用简单的语句,就可以把不同的architecture实现和entity实现绑定,而此时我们就能看出VHDL之所以要分开所谓entity和architecture的目的所在了。
CONFIGURATION 配置名 OF 实体名 IS
FOR 为实体选配的构造体名
END FOR;
END 配置名;
而事实上,configuration还可以直接指定某个Hierachy的某个实体究竟适用那种entity来例化,语法如下(参见VHDL Configuration):
configuration TopMixed of Top is —TopMixed是配置名,Top是实体名
for Structure —Structure是结构体名,是和实体Top相对应的结构体
for B1: Blk —B1和B2是结构体structure中的元件例化语句的标号
use entity Work.Blk(RTL);— 此语句说明,在元件例化的时候,利用用户自定义
—的实体blk来例化,其结构体是RTL
end for;
for B2: Blk
use entity Work.GateLevelBlk(Synth)— 此语句说明,元件例化B2时,利用实体
—GateLevelBLK对应的Synth构造体来例化
port map (IP => To_Vector(A),
To_Int8(OP) => F);
end for;
end for;
end TopMixed;
只不过这似乎太麻烦了一点。而Library和Package更好理解一些。用户可以将一些公共的定义统一靠Package和Package Body的关键字来定义到一个包里面去,而这些包又可以被编译在Library当中。不过Library的建立似乎只依赖于工具的解决,例如在编译的时候选择将包内容编译到某个库之中,然后再使用。
使用的过程,则是利用Library,Use等关键字来import这些内容。而要提醒的是,当前编译代码都是被认为是缺省的编入到了work库当中,所以如果需要引用代码中定义的某个包内容,应该用 work. (packet_name).all之类的path来声明。
1. 强类型语言:
VHDL是强类型语言,对类型要求非常严,一个对象只能有一种数据类型,而且不同类型数据间赋值是严格禁止的,这点Verilog用户应当注意。
2. Procedure和Function:
很好理解,直接对应Verilog的Task和Function,需要注意的就是在VHDL中还可以对Function进行重载,这个,也许有用吧。
3. 属性:
VHDL针对不同的对象,都具有不同的属性,这些属性不妨理解成Verilog当中的Predefined tasks or methods,但是似乎更加的丰富和多样,相信能够有效的减少代码量。
本文目的不在于语法的教程或者指导(本来我也刚看VHDL不过一个下午而已……),更希望能够迅速、扼要的抽出VHDL和Verilog的一些区别和共性,加速学习的过程。不过,我相信,一个熟悉Verilog的工程师,在吃透了上面这些不同点后,至少可以跌跌撞撞的开始写VHDL代码了,更多的东西,自然需要在实践中去体会和学习。
接下来,我的计划是结合Cadence的IUS工具,写一写最基本的SystemVerilog对于VHDL的验证的QuickStart了。5
Powered by ScribeFire.
现在开始我们假设要在Cadence的NC工具下,对一个VHDL的设计进行验证,那么就这样按部就班的一步步探索吧。
对于Cadence的工具来说,为人熟知的几个步骤就是ncvhdl, ncelab和ncsim。
ncvhdl是分析VHDL代码的句法和语义,如果没有错误的话,会生成HDL设计模块的内部表述,并且放置在指定的Library当中;
ncvlog是用于Verilog的编译指令,使用-sv可以编译SystemVerilog 文件。
ncelab则是独立于语言的工具(verilog或者VHDL进经过前一步之后,都被转化成同意的内部表述),它负责整个结构树的生成、连接、初值计算等等。关于前文提到的一些配置和entity的对应也应当是在这一步绑定的。经过这一步,生成的snapshot就被存放在database文件中了。
ncsim则很好理解,利用前面生成的目标文件进行仿真。
对于这几个命令的详细信息,可以参看ius的帮助文档,有非常详尽的说明。
对于我们想要进行的混合仿真来说,实际上思路很清晰,无非是对不同语言的文件用各自的语言编译器来编译,然后用独立于语言的ncelab来elaborate,最后进行仿真。这一点无论是哪一家Vendor都是殊途同归的。当然,要混仿,一般EDA TOOL都会要求编码时,需要满足一定的rules。而利用SystemVerilog构造顶层Testbench来包住VHDL的DUT,进行测试,是我们的目的。
所谓验证语言的基本问题,在我看来无非就是验证环境如何与RTL代码之间实现互动?包括信号驱动、检测,包括Scope问题。
那么假设我们已经将VHDL的简单模块dut放置在了一个top的SystemVerilog的环境当中,那么如何回答上面的问题?
简单的演示如下:
vhdl的dut代码:(top.vhdl)
1 entity top_vhdl is
2 port (
3 dut_a: out bit_vector (3 downto 0)
4 );
5 end ;
6 architecture u_top of top_vhdl is
7 signal reg_a: bit_vector (3 downto 0);
8 begin
9 test_process: process
10 begin
11 dut_a <= “1100″;
12 wait for 10 ns;
13 dut_a <= “1101″;
14 wait for 10 ns;
15 dut_a <= “0011″;
16 reg_a <= not reg_a;
17 –assert false report “END of SIM” severity note;
18 –wait;
19 end process test_process;
20 end u_top;
非常简单,一个随便赋值了两下的模块。
接下来是一个更简单的verilog的dut模块:(top_verilog.sv)
1 module top_verilog;
2 reg verilog_bit;
3 initial begin
4 forever begin
5 verilog_bit = 1′b0;
6 #100
7 verilog_bit = ~verilog_bit;
8 end
9 end
10 endmodule
不停翻转的一个bit。
然后是包裹着着两个dut的testbench:(top.sv)
1 `timescale 1ns/100ps
2 module top_test;
3 reg [3:0] v_bits;
4 top_vhdl u_vhdl(
5 .dut_a(v_bits)
6 );
7
8 top_verilog u_verilog();
9
10 always @(v_bits) begin
11 $display(”vhdl’s value is %0x.”, v_bits);
12 end
13 always @(u_verilog.verilog_bit) begin
14 $display(”verilog’s value is %0b.”, u_verilog.verilog_bit);
15 end
16 demo_pro u_pro();
17 endmodule
18
19 program demo_pro;
20 initial begin
21 $display(”In first initial:”);
22 @(top_test.u_verilog.verilog_bit);
23 $display(”hit one toggle!”);
24 #100
25 $finish();
26 end
27 endprogram
这里我特意区分开了对verilog和VHDL的dut的信号的引用方式,从这里不难看出,Program中的验证代码部分可以按照hierarchy直接访问到Verilog的模块的内部信号,而对于VHDL的这种情况的访问却只能到port为止,就如同Vera时期的情况类似,信号交互都是发生在Port上,所以这在使用上需要注意。
到此为止,一段VHDL的代码已经被正确的import到了SystemVerilog当中,剩下的事情,就和普通的验证流程相仿,无论是要使用OVM/VMM等等,那就同等处理即可,还是相当简单的。
而执行以上步骤的命令行如下:
ncvhdl top.vhdl;
ncvlog top.sv top_verilog.sv;
ncelab worklib.top_test:module;
ncsim worklib.top_test:module;
按照同事的看法,VHDL这种语言必然会死掉,正确与否先不讨论,但是使用起来,针对SV的隔离感还是存在的,希望以后工作中间能够有更多的体会和解决的办法。
Powered by ScribeFire.