原创 System verilog语法

2010-9-15 09:10 7611 3 3 分类: FPGA/CPLD

SystemVerilog 的数据类型


这个手册将描述Systemverilog新引进的数据类型。他们大多数都是可以综合的,并且可以使RTL级描述更易于理解和书写。


整型和实型


SystemVerilog引进了几种新的数据类型。C语言程序员会熟悉其中的大多数。引进新的数据类型构思是这样的,如果C语言和SystemVerilog有相同的数据类型可以使C语言算法模型更容易的转化为SystemVerilog模型。


Verilog的变量类型有四态:既是0,1,X,Z。SystemVerilog引进了新的两态数据类型,每一位只可以是0或是1。当你不需要使用的X和Z值时,譬如在写Testbench和做为for语句的循环变量。使用两态变量的RTL级模型,可以使模拟器更有效率。并且使用得当的话将不会对综合结果产生影响。





类型



描述



例子



bit



user-defined size



bit [3:0] a_nibble;



byte



8 bits, unsigned



byte a, b;



shortint



16 bits, signed



shortint c, d;



int



32 bits, signed



int i,j;



longint



64 bits, signed



longint lword;


二态整型


注意到和C语言不一样,SystemVerilog指定了一些固定宽度的类型。


 





类型



描述



例子



reg



user-defined size



reg [7:0] a_byte;



logic



identical to reg in every way



logic [7:0] a_byte;



integer



32 bits, signed



integer i, j, k;


四态整型


logic是一种比reg型更好的类型,他更加的完善。我们将会看到你可以在过去verilog hdl中用reg型或是wire型的地方用logic型来代替。





类型



描述



例子



time



64-bit unsigned



time now;



shortreal



like float in C



shortreal f;



real



like double in C



double g;



realtime



identical to real



realtime now;


非整数类型


数组


在Verilog-1995中,你可以定义标量或是矢量类型的网线和变量。你也可以定义一维数组变量类型的存储器数组。在Verilog-2001中允许多维的网线和变量数组存在,并且取消了一些存储器数组用法的限制。


在SystemVerilog中数组有了新的发展,并对数组重新进行了重新定义,从而允许对数组进行更多的操作。


在SystemVerilog,中,数组可以具有压缩数组或是非压缩数组的属性,也可以同时具有两种属性。考虑下面的例子:


reg [3:0][7:0] register [0:9];


压缩数组是[3:0]和[7:0],非压缩数组是[0:9] 。(只要你喜欢可以有同样多的压缩数组和非压缩数组)


压缩数组



  • 保证将在存储器中产生持续的数据
  • 可以复制到任何其他的压缩对象中
  • 可切片("部分选取")
  • 仅限于"bit"类型(bit, logic, int等),其中有些(如int)有固定的尺寸


相比之下,非压缩数组可以放在模拟器选择的任何地方,可以可靠地复制到另一个相同数据类型的数组中。对于不同数据类型的数组,你必须使用映射(有几个非压缩数组映射到压缩数组的规则)。 非压缩数组可以是任何的类型,如实数数组。


SystemVerilog允许对完整的非压缩数组和部分选取的非压缩数组进行一些操作。对与此,部分选取的数组必须是相同的数据类型和大小。如非压缩数组必须要有相同的位数和长度。而压缩数组不一样,只要选取的部分有相同数量的位数。


允许的操作有



  • 读和写整个数组
  • 读和写部分选取的数组
  • 读和写数组中的位
  • 在数组,部分选取的数组和数组的位有同样的优先级


SystemVerilog也支持动态数组(在仿真中数组长度可以改变)和关联数组(数组中的数据非连续排列)。


    为了支持这些数组类型,SystemVerilog中提供了一些数组查找的函数和方法。如你可以使用$dimensions函数查询一个数组变量的的维数。


Typedef


    SystemVerilog的数据类型系统允许定义复杂的数据类型。为了使代码看起来清晰,引进了别名的方法。别名的方法允许使用者在他们的代码中用自己的名字来定义经常使用的数据类型,当构造复杂的数组时用别名的方法是很方便的。


typedef reg [7:0] octet;


octet b;


和reg [7:0] b;的效果是一致的


typedef octet [3:0] ;


quadOctet;


quadOctet qBytes [1:10];


和reg [3:0][7:0] qBytes [1:10];的效果是一致的


Enum


SystemVerilog也引进了枚举类型,例如


enum { circle, ellipse, freeform } c;


枚举允许您用于声明一组命名的常数。这样的数据类型是适用表示状态值、操作码和其它的非数字或象征性的数据。


Typedef和枚举经常一起使用, 象这样:


typedef enum { circle, ellipse, freeform } ClosedCurve;


ClosedCurve c;


枚举类型命名值的作用类似于常数。它的默认类型是int。您能复制他们到/从枚举类型的变量, 互相比较他们等等。枚举类型的强制类型。您无法复制一个数值到枚举类型变量, 除非您使用定义影射。


c = 2;               // ERROR


c = ClosedCurve'(2); // Casting – okay


然而,当你在一个表达式中使用了枚举类型,你所使用的值等效于分开写的整数; 所以例子中的这是一个比较好的枚举变量与整数的比较;而且它也可以在整数表示式中使用枚举值。


结构体和共同体


同样, SystemVerilog 也引进了结构体和共同体, s类似于 C.


struct {


  int x, y;


} p;


结构体成员选择使用 .名字的语法。


p.x = 1;


结构体的表达可以使用括号。


p = {1,2};


结构体在使用typedef声明新的结构类型和使用新的类型声明变量时是非常有用的。注意结构体也是可以被封装的。


typedef struct packed {


  int x, y;


} Point;


Point p;


共同体在用相同的硬件资源 (如寄存器)储存不同类型的值(如整数、浮点)时候是非常有用的。


SystemVerilog RTL Tutorial


这个手册将介绍systemverilog的一些新特点,这样使RTL级设计更加方便,更有效率。


新的操作符


SystemVerilog 增加了一些新的操作符,其中的大部分是从C语言中引进来的。新增加的操作符包括自增(++),自减(--)和指派运算符 (+=, -=)。全等运算符(===和 !==)作用类似于casex声明,值X和值Z都被认为是无关位。


新的循环声明


也是从C语言中引进来的,包括do-while循环,break 和 continue。新的foreach操作符用在数组变量中。而且增强了for循环的功能,下面的做法是正确的,


for (int i = 15, logic j = 0 ; i > 0 ; i--, j = ~j)


标签


在Verilog中, 你可以这样标识begin 和 fork语句:


begin : a_label


在SystemVerilog语句标签可以在end处重复一遍:


end : a_label


这对管理代码是非常有用的。在end处的标签必须和在开始处的标签相一致。Modules, tasks 和and functions也可以在在代码的末尾处重复标签名。


module MyModule ...


...


endmodule : MyModule


在SystemVerilog中允许在任何过程声明中添加标签:


loop : for (int i="0"; ...


这对for循环特别有用,因为这样的话for循环可以被终止。尽管通过这种方式增强了块的命名, 但是至少有一个这样的理由取消他们的使用,那就是在SystemVerilog 变量也可以在未命名的块中被使用!!


Relaxed Assignment Rules


对verilog的初学者来说这也许是最难的(甚至老练的Verilog 用户也时常被难住由它时常绊倒) 是线网和变量之间的不同点。SystemVerilog 包含了太多的混乱史: 变量可以通过过程赋值语句, 连续赋值语句和通过module例化连接到输出端来赋值。但不幸的是 , 仍不能连接variable到inout端口, 虽然你可以通过使用ref端口来实现。


那意味这在SystemVerilog中,你可以在大部分时间里使用 logic 数据类型, 而在verilog中你有时得用reg而有时却得使用wire。实际上 reg和logic几乎是相互等同的, 但logic是一个更合适的名字。


这里还有些限制,不允许使用多于一次的持续赋值语句和输出端口连接的给同一变量赋值。这是因为没有类似于线网的多重驱动变量的定论。因此,假如你通过这些方式给一个变量赋过值,你将不能再用过程赋值语句给变量赋值。


Port Connection Shorthand


    如果你使用Verilog-2001,编写测试环境的模块有如下声明:


module Design (input Clock, Reset, input [7:0] Data, output [7:0] Q);


在测试环境中,你可以这样声明regs 和 wires:


reg Clock, Reset;


reg [7:0] Data;


wire [7:0] Q;


你可以这样例化模块:


Design DUT ( Clock, Reset, Data, Q );


更好的可以这样声明


Design DUT ( .Clock(Clock), .Reset(Reset), .Data(Data), .Q(Q) );


但这样有点过于重复。Systemverilog允许你使用如下的速记符号:


Design DUT ( .Clock, .Reset, .Data, .Q );


如果先前适当的声明了线网和变量,也可以这样:


logic Clock, Reset;


logic [7:0] Data;


logic [7:0] Q;


如果还嫌太罗嗦,你也可以这样写:


Design DUT ( .* );


   意思是"连接所有端口到和端口相同名字变量或网线"。你无须这样连接所有的端口。例如:


Design DUT ( .Clock(SysClock), .* );


意思是”连接Clock 端口到 SysClock,然后别的端口变量连接到到和端口相同名字变量或网线”。


综合用语


Verilog 非常广泛的应用在RTL 综合, 即使它本身不是作为综合语言设计的。可以非常容易的写出仿真正确的Verilog 代码, 但设计却是不正确的, 例如,它总是有意无意地容易推断透明琐存器。SystemVerilog 解决一种方式是引进了新的always关键字: always_comb 、always_latch 和always_ff 。


always_comb 被用于描述组合逻辑。它通过读取进程中的变量和线网自动地产生一张完全敏感列表, 象总@ * 在Verilog-2001中一样:


always_comb


  if (sel)


    f = x;


  else


    f = y;


此外,为了自动的产生一个完整的敏感列表,它递归的查看功能单元然后把别的必要的信号插入到敏感列表中。它被定义为在组合逻辑中执行一些规则,它可以作为一个暗示(特别是对综合工具)采用更为严谨的综合风格而且添加了新的语义checks.finally、always_comb:它暗示了敏感列表截至进程的结束, 以便它在时间为零时评估一次并因此所有它的输出有占去适当的值在仿真时间开始处理进步之前。这是一次公正评价时间为零,因此所有产出值负起适当时间开始前模拟进度.


always_latchalways_ff 分别被用来暗示产生琐存器和触发器。下面是 always_ff的例子:


always_ff @(posedge clock iff reset == 0 or posedge reset)


  if (reset)


    q <= 0;


  else if (enable)


    q++;


使用这些新的always的优点是综合工具可以检查出作者的设计意图。


Unique and Priority


其它在RTL Verilog中常见错误是parallel_case 和full_case pragmas 的误用。问题出现的原因是仿真器忽略了这些注释, 而这些注释可以用于指导综合。SystemVerilog引进了二个新的主题词: priority 和 unique。


不同于pragmas, 这些主题词适用于if声明和case声明语句。每个强加欣然被映射对被综合的硬件独特强制执行完整性和独特有条件的具体模仿行为; 换句话说, 一个分支有条件应该确切地被采取在执行时间。如果附属在执行时间的具体条件不会允许超过一个分支有条件, 或分支根本, 被采取, 有一个运行错误。例如, 它是可接受为选择器在条件语句重叠, 但如果那个交叠情况被查出在运行时间然后这是错误。它同样是好有一个独特的条件语句没有缺省分支, 或如果声明没有分支, 但在运行时间模拟器检查某一分支的确被采取。综合工具可能使用这信息, 如同他们可能full_case 方针, 宁可推断, 门闩不应该被创造。


优先权强制执行有些较不严谨套检查, 检查只至少一个分支有条件被采取。它允许因此可能性, 超过一个分支有条件能被采取在执行时间。它准许综合创造更加侈奢的优先权逻辑在这样情况。


SystemVerilog Interfaces Tutorial


Interface在systemverilog中是一种主要的新构造体,创造出来的目的是为了封装block间的通信,提供了可以平滑的从抽象的系统级转换到低级别的寄存器级和门级的可能性。Interface便于重用性设计。Interface是层次化的结构可以包含别的Interface。


使用Interface的几个优点如下:



  • 封装了连通性:一个使用Interface可以通过把一组条目看做一个端口来传递,取代了单个端口一个一个的连接方式。这样就减少了模块端口连接的代码,从而改善了代码的可维护性和可读性。
  • 封装了功能性:通过隔离的模块可通过Interface互连。因此,抽象级别和通信协议的大小可以是非常精确的而完全不受模块的限制。
  • Interface可以包含parameters, constants, variables, functions 和 tasks, processes和 持续赋值,并且对系统级建模和testbench都是有用的。
  • Interface可以帮助建立类似的功能覆盖率记录和报告,协议检查和断言的应用。
  • Interface可以帮助建立类似于功能覆盖率记录和报告以及协议的检查和断言的应用。
  • Interface可以用于无端口的访问:一个Interface可以在一个module内部做为静态数据目标来直接例化。这种方法用来内部状态信息(关于interface的命名可能是设计的不同点共享信息)的共享。
  • 弹性:一个Interface可以通过和module的方式来参数化。一个module头可以创建为未定义的interface例化(称为一般interface),这个Interface可以在module例化以后再指定


最简单的说方是,Interface是一组wire的名字,类似于struct,但是interface允许作为模块端口而struct不被允许。


下面的例子通过一个简单的例子来说明Interface的定义:


// Interface definition


interface Bus;


  logic [7:0] Addr, Data;


  logic RWn;


endinterface


// Using the interface


module TestRAM;


  Bus TheBus();                   // Instance the interface


  logic[7:0] mem[0:7];


  RAM TheRAM (.MemBus(TheBus));   // Connect it


  initial


  begin


    TheBus.RWn = 0;               // Drive and monitor the bus


    TheBus.Addr = 0;


    for (int I="0"; I<7; I++)


      TheBus.Addr = TheBus.Addr + 1;


    TheBus.RWn = 1;


    TheBus.Data  = mem[0];


  end


endmodule


module RAM (Bus MemBus);


  logic [7:0] mem[0:255];


  always @*


    if (MemBus.RWn)


      MemBus.Data = mem[MemBus.Addr];


    else


      mem[MemBus.Addr] = MemBus.Data;


endmodule


 


Interface Ports


一个interface 也可以有input,output或是inout端口。当interface例化时,只有当变量或是线网声明在一个interface的端口列表中才能通过名字或是位置来互连,因此可以和别的interface共享。端口声明使用标准的ANSI风格


下面是一个有时钟端口的interface的例子:


interface ClockedBus (input Clk);


  logic[7:0] Addr, Data;


  logic RWn;


endinterface


module RAM (ClockedBus Bus);


  always @(posedge Bus.Clk)


    if (Bus.RWn)


      Bus.Data = mem[Bus.Addr];


    else


      mem[Bus.Addr] = Bus.Data;


endmodule


// Using the interface


module Top;


  reg Clock;


  // Instance the interface with an input, using named connection


  ClockedBus TheBus (.Clk(Clock));


  RAM TheRAM (.Bus(TheBus));


  ...


endmodule


 


Parameterised Interface


    下面是一个简单的参数化的interface的例子:


interface Channel #(parameter N = 0)


    (input bit Clock, bit Ack, bit Sig);


  bit Buff[N-1:0];


  initial


    for (int i = 0; i < N; i++)


      Buff = 0;


  always @ (posedge Clock)       


   if(Ack = 1)


     Sig = Buff[N-1];


   else


     Sig = 0;


endinterface


// Using the interface


module Top;


  bit Clock, Ack, Sig;


  // Instance the interface. The parameter N is set to 7using named


  // connection while the ports are connected using implicit connection


  Channel #(.N(7)) TheCh (.*);


  TX TheTx (.Ch(TheCh));


  ...


endmodule


 


Modports in Interfaces


一种新加的和interface有关系的构造体是Modport 。它提供了module的interface端口和在特定的module中控制task和function使用的方向性信息。这些端口的方向可以在module中可以看到。


下面这个例子包含了Modport用来详细说明信号在interface中的方向。在我们的RAM例子中,方向就是从module去看modport所连接的方向。


 


interface MSBus (input Clk);


  logic [7:0] Addr, Data;


  logic RWn;


  modport Slave (input Addr, inout Data);


endinterface


module TestRAM;


  logic Clk;


  MSBus TheBus(.Clk(Clk));


  RAM TheRAM (.MemBus(TheBus.Slave));


  ...


endmodule


module RAM (MSBus.Slave MemBus);


  // MemBus.Addr is an input of RAM


endmodule


 


Tasks in Interfaces


Task和function可以定义在interface中,从而允许构造更抽象级的模型。下一个例子显示了两个task在interface用来模仿总线功能模型。Task被叫做内部的testRAM module:


 


interface MSBus (input Clk);


  logic [7:0] Addr, Data;


  logic RWn;


  task MasterWrite (input logic [7:0] waddr,


                    input logic [7:0] wdata);


    Addr = waddr;


    Data = wdata;


    RWn = 0;


    #10ns RWn = 1;


    Data = 'z;


  endtask


  task MasterRead (input  logic [7:0] raddr,


                   output logic [7:0] rdata);


    Addr = raddr;


    RWn = 1;


    #10ns rdata = Data;


  endtask


endinterface


module TestRAM;


  logic Clk;


  MSBus TheBus(.Clk(Clk));


  RAM TheRAM (.MemBus(TheBus));


  initial


  begin


    // Write to the RAM


    for (int i; i<256; i++)


      TheBus.MasterWrite(i[7:0],i[7:0]);


    // Read from the RAM


    for (int i; i<256; i++)


    begin


      TheBus.MasterRead(i[7:0],data);


      ReadCheck : assert (data === i[7:0]);


        else $error("memory read error");


    end


  end


endmodule

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /3 下一条