语法详细讲解
Download
1 / 120

语法详细讲解 强制激励 - PowerPoint PPT Presentation


  • 92 Views
  • Uploaded on

语法详细讲解 强制激励. 在一个过程块中,可以用两种不同的方式对信号变量或表达式进行连续赋值。 过程连续赋值往往是不可以综合的,通常用在测试模块中。 两种方式都有各自配套的命令来停止赋值过程。 两种不同方式均不允许赋值语句间的时间控制。 assign 和 deassign 适用于对寄存器类型的信号(例如: RTL 级上 的节点或测试模块中在多个地方被赋值的信号)进行赋值。 initial begin #10 assign top.dut.fsml.state_reg = `init_state;.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' 语法详细讲解 强制激励' - ciaran-guy


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
语法详细讲解强制激励

  • 在一个过程块中,可以用两种不同的方式对信号变量或表达式进行连续赋值。

  • 过程连续赋值往往是不可以综合的,通常用在测试模块中。

  • 两种方式都有各自配套的命令来停止赋值过程。

  • 两种不同方式均不允许赋值语句间的时间控制。

  • assign和deassign 适用于对寄存器类型的信号(例如:RTL级上

    的节点或测试模块中在多个地方被赋值的信号)进行赋值。

    initial begin

    #10 assign top.dut.fsml.state_reg = `init_state;


语法详细讲解字符串

语法详细讲解强制激励

语法详细讲解强制激励

#20 deassign top.dut.fsml.state_reg;

end

  • force 和 release 用于寄存器类型和网络连接类型(例如:门级扫描寄存器的输出)的强制赋值,强制改写其它地方的赋值。

    initial begin

    # 10 force top.dut.counter.scan_reg.q=0;

    # 20 release top.dut.counter.scan_reg.q;

    end

    在以上两个例子中,在10到20 这个时间段内,网络或寄存器类型的信号被强制赋值,而别处对该变量的赋值均无效。

    • force的赋值优先级高于assign。

    • 如果先使用assign,再使用force对同一信号赋值,则信号的值为force所赋 的值,


语法详细讲解强制激励

语法详细讲解强制激励

当执行release后,则信号的值为assign所赋 的值。

  • 如果用force对同一个信号赋了几次值,再执行release,则所有赋的值均不再存在。

  • 可以对信号的某(确定)位、某些(确定)位或拼接的信号,使用force和release赋值;但不能对信号的可变位使用force和release 来赋值。

  • 不能对寄存器类型的信号某位或某些位使用 assign 和deassign 来赋值。


语法详细讲解建立时钟

语法详细讲解建立时钟

虽然有时在设计中会包含时钟,但时钟通常用在测试模块中。下面

三个例子分别说明如何在门级和行为级建立不同波形的时钟模型。

[例1] 简单的对称方波时钟:

reg go; wire clk;

nand #(period/2) ul (clk,clk,go);

initial begin

go=0;

#(period/2) go=1;

end

reg clk;

always begin

#period/2 clk=0;

#period/2 clk=1;

end

注:在有些仿真器中,如果设计所用的时钟是由与其相同抽象级别的时钟模型产生的,则仿真器的性能就能得到提高。


语法详细讲解建立时钟

[例2]简单的带延迟的对称方波时钟:

reg clk;

initial begin

clk=0;

#(period)

forever

#(period/2) clk=!clk

end

reg go; wire clk;

nand #(period/2) ul (clk,clk,go);

initial begin

go=0;

#(period) go=1;

end

注:这两个时钟模型有些不同,行为描述的模型延迟期间一直是低电平,而门级描述的模型开始延迟有半个周期是不确定的。


语法详细讲解建立时钟

语法详细讲解建立时钟

[例3]. 带延迟、头一个脉冲不规则的、占空比不为1的时钟:

reg clk;

initial begin

#(period+1) clk=1;

#(period/2-1)

forever begin

#(period/4) clk=0;

#(3*period/4) clk=1;

end

end

reg go; wire clk;

nand #(3*period/4,period/4) ul(clk,clk,go);

initial begin

#(period/4+1) go=0;

#(5*period/4-1) go=1;

end

注:这两个时钟模型也有些不同,行为描述的模型一开始就有确定的电平,而门级描述的模型有延迟开始时电平是不确定的。


语法详细讲解怎样使用任务

语法详细讲解怎样使用任务

mocule bus_ctrl_tb

reg [7:0] data;

reg data_valid, data_rd;

cpu ul(data_valid,data,data_rd);

initial begin

cpu_driver(8’b0000_0000);

cpu_driver(8’b1010_1010);

cpu_driver(8’b0101_0101);

end

task cpu_driver;


语法详细讲解怎样使用任务

语法详细讲解怎样使用任务

input [7:0] data_in;

begin

#30 data_valid=1;

wait(data_rd==1);

#20 data=data_in;

wait(data_rd==0);

#20 data=8’hzz;

#30 data_valid=0;

end

endtask


语法详细讲解怎样使用任务

语法详细讲解怎样使用任务

endmodule

在测试模块中使用任务可以提高程序代码的效率,可以进行多次重复操作。

cpu_data

clk

data_valid

data_read

read_cpu_state

wait

wait

data1

data2

wait

data3

data4

wait


语法详细讲解存储建模

语法详细讲解存储建模

目标

  • 学会使用Verilog进行存储建模。

  • 学会在Verilog中进行双向口建模。


语法详细讲解存储设备建模

存储设备建模必须注意以下两个方面的问题:

  • 声明存储容量的大小。

  • 提供对内容的访问权限,例如:

    只读

    读写

    同步读写

    多次读,同时进行一次写

    多次同步读写,同时提供一些方法保证一致性


语法详细讲解简单ROM建模

my_rom_data

0000

0101

1100

0011

1101

0010

0011

1111

1000

1001

1000

0001

1101

1010

0001

1101

myrom.v

`timescale 1ns/10ps

module myrom(read_data,addr,read_en_);

input read_en_;

input [3:0] addr;

output [3:0] read_data;

reg [3:0] read_data;

reg [3:0] mem [0:15];

initial

$readmemb(“my_rom_data”,mem);

always @ (addr or read_en_)

if(!read_en_)

read_data=mem[addr];

endmodule

ROM的数据存储在另外的一个独立的文件中


语法详细讲解简单ROM建模

上面所示的ROM模型中使用二维的存储寄存器来定义存储容量。ROM中的数据保存在一个独立的文件中,如上面的右边所示。这是一种保存ROM数据的通用的方法,它可以使数据和ROM模型分开。


语法详细讲解简单RAM建模

`timescale 1ns/1ns

module mymem(data,addr,read,write);

inout [3:0] data;

inout [3:0] addr;

input read, write;

reg [3:0] memory [0:15]; //4 bits, 16 words

//read

assign data=read? memory[addr]:4’bz;

//write

always @ (posedge write)

memory[addr]=data;

endmodule

RAM模型比ROM模型稍微复杂,因为它必须具有读写能力,而进行读写时通常使用相同的数据总线,这就需要新技术来处理双向总线。当读出口没有被激活时,RAM模型不再激励总线,如果此时的总线写入变量也没有被激活,则总线进入高阻状态,这就避免了RAM中的读写竞争。


语法详细讲解简单RAM建模

上述模型是可综合的,但是许多工具只产生一系列的寄存器,这一般就需要更大的空间,从而比实际的存储器的价格更昂贵。


语法详细讲解容量可变的存储器建模

例:

module scalable_ROM(mem_word,address);

parameter addr_bits=8; //size of address bus

parameter wordsize=8; //width of a word

parameter words=(1<<addr_bits); //size of mem

output [wordsize:1] mem_word; //word of memory

input [addr_bits:1] address; //address bus

reg [wordsize:1] mem [0:words-1]; //mem declaration

//output one word of memory

wire [wordsize:1] mem_word=mem[address];

endmodule


语法详细讲解容量可变的存储器建模

上述的例子演示了怎样通过设置字长和地址位数来定义一个只读存储设备。

在上例中,存储字的范围从0开始的,而不是从1开始,这是因为内存是直接通过地址线定位的,同样地,也可以用下面的方法来定义内存和定位:

reg [wordsize:1] mem [1:words]; //memory starts at word 1

//address must be incremented to address all words in memory

wire [wordsize:1] mem_word=mem[address+1];


语法详细讲解载入存储设备

可以通过使用一个循环或系统任务来载入存有数据的整个存储器。

  • 使用循环把值赋给存储数组。

    for(i=0;i<memsize;i=i+i) // initialize memory

    mema[i]={wordsize{1’b1}};

  • 调用$readmem系统任务。

    //load memory data form a file

    $readmemb(“mem_file.txt”,mem);


语法详细讲解怎样使用双向口

使用inout关键字声明双向口。

inout [7:0] databus;

使用双向口需要遵循下面的规则:

  • 一个inout口只能声明为网络类型,而不能是寄存器类型的。

    因此仿真器能确定多个激励源的最终值。

  • 在设计中,每次只能激活inout的一个方向。

    例如:当使用总线读RAM中的数据时,同时又向RAM模

    型的双向数据总线写数据,就会产生逻辑竞争,导致总

    线脱离。

    必须为inout口设计控制逻辑,用来保证正确的操作。


语法详细讲解怎样使用双向口

注:

  • 可以声明一个inout口,用来输入或输出数据。inout口默认为网络类型,不可以对网络类型的数据进行过程赋值,但可以在过程块外对寄存器数据类型进行连续赋值,或者把它与元器件相连。

  • 必须为inout口设计控制逻辑,用来保证正确的操作。当把inout口做为输入口时,必须使输出逻辑失效。


b1

b2

语法详细讲解双向口建模

使用元器件

en_a_b

bus_a

bus_b

en_b_a

module bus_xcvr

(bus_a,bus_b,en_a_b,en_b_a);

inout bus_a,bus_b;

input en_a_b,en_b_a;

bufifl b1(bus_b,bus_a,en_a_b);

bufifl b2(bus_a,bus_b,en_b_a);

//结构模块逻辑

endmodule

当en_a_b=1时,元器件b1激活,bus_a的值传到bus_b上

当en_b_a=1时,元器件b1激活,bus_b的值传到bus_a上


语法详细讲解双向口建模

注:在上页的例子中,使用了en_a_b和en_b_a 来控制元器件bufifl,如果进行同时控制,则得不到预期的结果。


b1

b2

语法详细讲解双向口建模

使用连续赋值

en_a_b

bus_a

bus_b

en_b_a

module bus_xcvr

(bus_a,bus_b,en_a_b,en_b_a);

inout bus_a,bus_b;

input en_a_b,en_b_a;

assign bus_b=en_a_b? bus_a:’bz;

assign bus_a=en_b_a? bus_b:’bz;

//结构模块逻辑

endmodule

当en_a_b=1时,bus_a的值传到bus_b上

当en_b_a=1时,bus_b的值传到bus_a上


语法详细讲解双向口建模

注:在assign语句中,通过en_a_b和en_b_a控制bus_a与bus_b之间的数据交换。

如果进行同时控制,则得不到预期的结果。


语法详细讲解双向口建模

存储口建模

测试模块

RAM单元

rd

数据总线

数据

寄存

wr

当rd等于1时datareg的值被赋给databus

module ram_cell(databus,rd.wr);

inout databus;

input rd,wr;

reg datareg;

assign databus=rd? datareg:’bz;

always @(negedge sr)

datareg<=databus;

endmodule

当wr的下降沿到达时,databus的值被写入datareg


语法详细讲解双向口建模

注:上页存储单元在wr的下降沿到达时存入数据。上页模块在 wr处于高电平时,通过数据总线写入数据,但必须保证wr的高电平维持时间长于数据的写入时间。

在rd处于高电平时,上述存储单元通过数据总线读出数据。由于此模型为单口存储模型,因此wr和rd不能同时为高电平,否则就得不到预期的结果。


Verilog
语法详细讲解第十七部分 Verilog中的高级结构

目标

  • 学会怎样定义或调用任务和函数。

  • 学会怎样使用命名块。

  • 学会怎样使命名块和任务失效。

  • 熟悉有限状态机及怎样进行有限状态机显式建模。


Verilog1
语法详细讲解第十七部分 Verilog中的高级结构

可以通过把代码分成小的模块或者使用任务和函数,来把一项任务

分成许多较小的、易于管理的部分,从而提高代码的重用性。

  • 任务

    一般用于执行调试操作,或者行为的描述硬件

    可以包含时间控制(# delays, @, wait)

    可以包含input, output 和 inout参数

    可以调用其他的任务或函数

  • 函数

    一般用于计算,或者用来代替组合逻辑

    不能包含任何延迟;函数在零时间执行

    只能使用input参数,但可以通过函数名来返回一个值。

    可以调用其他的函数,但不可以调用任务


Verilog2
语法详细讲解第十七部分 Verilog中的高级结构

注:

  • 必须在模块内调用任务和函数。

  • 在任务和函数中不能声明连线类型的变量。

  • 所有的输入和输出都是真正的本地寄存器类型的数据。

  • 只有当任务或函数调用并执行完后,才能有返回值。例如:当任务或函数中包含一个forever循环时,就不可能有返回值。


Verilog3
语法详细讲解Verilog 任务

下面的任务含有时间控制和一个输入,并且指向一个模块变量,但是不包含输出、总线和内部变量,不显示任何内容。

时间控制中使用的信号(例如 clk)不必是任务的输入,这是因为输入值只向任务内部传递一次。

module top;

reg clk, a, b;

DUT u1(out, a, b, clk);

always #5 clk=!clk;

task neg_clocks;

input [31:0] number_of_edges;

repeat(number_of_edges)


语法详细讲解整数和实常数

语法详细讲解Verilog 任务

@(negedge clk);

endtask

initial begin

clk=0; a=1; b=1;

neg_clocks(3); //任务调用

a=0;

neg_clocks(5);

b=0;

end

endmodule

主要特征:

  • 任务调用是通过在Verilog模块中写入任务名来实现的。

  • 任务中可以包含input, output和inout参数。


Verilog4
语法详细讲解Verilog 任务

  • 传递给任务的参数与任务I/O声明参数的顺序相同。虽然传递给任务的参数名可以和任务内部I/O声明的参数名相同,但是为了提高任务的模块化程度,传递给任务的参数名通常是唯一的,而不使用与任务内部I/O声明的参数名相同的参数名。

  • 在任务中可以使用时间控制。

  • 在Verilog中,任务定义了一个新的范围。

  • 使用关键字disable禁止任务。

    注意:不要在程序的不同部分同时调用同一个任务。这是因为任务只有一组本地变量,同时调用两次将会导致错误。这种情况通常发生在使用时间控制的任务中。

    在任务或函数中,应给在父模块中声明的变量加注释。若在其它模块中调用任务或函数,任务和函数中所使用的变量必须包含在输入/输出口列表中。


Verilog5
语法详细讲解Verilog 任务

下面模块中的任务含有一个双口总线和一个内部变量,但是没有输入、输出和定时控制,没有引用本模块的变量,不显示任何内容。

在任务调用时,任务参数(口)类型被视为内部寄存器类型。

parameter MAX_BITS=8;

reg [MAX_BITS:1] D;

task reverse_bits;

inout [7:0] data; //双口总线被视为寄存器类型!

integer K;

for (k=0; k<MAX_BITS; K=K+1)

reverse_bits [MAXBITS – (K+1)=data[K];

endtask

always @ (posedge clk)

reverse_bits (D);

……


Verilog6
语法详细讲解Verilog 任务

下面模块中的任务含有输入、输出、时间控制和一个内部变量,并且引用了一个本模块的变量,但是没有输出,不显示任何内容。

任务调用时的参数顺序应与任务定义中声明的顺序相同。

module mult(clk, a, b, out, en_mult);

input clk, en_mult;

input [3:0] a, b;

output [7:0] out;

reg [15:0] out;

always @ (posedge clk)

multme(a, b, out); //任务调用


Verilog7
语法详细讲解Verilog 任务

task muotme; //任务定义

input [3:0] xme, tome;

output [7:0] result;

wait (en_mult)

result=xme*tome;

endtask

endmodule


Verilog8
语法详细讲解Verilog 函数

module orand(a, b, c, d, e, out);

input [7:0] a, b, c, d, e;

output [7:0] out;

reg [7:0] out;

always @ (a or b or c or d or e)

out=f_or_and(a, b, c, d, e); //函数调用

function [7:0] f_or_and;

input [7:0] a, b, c, d, e;

if(e==1)

f_or_and=(a|b)&(c|d);

else

f_or_and=0;

endfunction

endmodule


Verilog9
语法详细讲解Verilog 函数

虽然函数不能包含定时控制,但是可以在包含定时控制的过程块中调用函数。

在上述函数中使用了函数名f_or_and作为寄存器类型的变量。

要点

  • 函数定义不能包含定时控制语句。

  • 函数必须含有输出,但不能含有输出和总线口;

  • 一个函数只能返回一个值,该值的变量名与函数同名,数据类型默认为reg类型。

  • 传递给函数参数的顺序与函数输入参数声明的顺序相同。

  • 函数定义必须包含在模块定义之内。

  • 函数不能调用任务,但任务可以调用函数。

  • 函数使Verilog有更广阔的适用范围。


Verilog10
语法详细讲解Verilog 函数

  • 虽然函数只能返回一个值,但是它们的返回值可以直接的赋给一个信号拼接,从而使它们有多个输出。

    {o1, o2, o3, o4}=f_or_and(a, b, c, d, e);


语法详细讲解命名块

  • 可以通过在关键字begin或fork后加上:〈块名〉来给块命名。

    module named_blk;

    ……

    begin :seq_blk

    ……

    end

    ……

    fork : par_blk

    ……

    join

    ……

    endmodule

  • 可以在命名块中声明本地变量。

  • 可以使用disable禁止命名块。


语法详细讲解命名块

注意:

  • 命名块使Verilog有更广阔的适用范围。

  • 命名块的使用缩短了仿真的时间。


语法详细讲解禁止命名块和任务

module do_arith(out, a, b, c, d, e, clk, en_mult);

input clk, en_mult;

input [7:0] a, b, c, d, e;

output [15:0] out;

reg [14:0] out;

always @(posedge clk)

begin : arith_block //***命名块***

reg [3:0] tmp1, tmp2; //***本地变量***

{tmp, tmp2}=f_or_and(a, b, c, d, e); // 函数调用

if(en_mult) multme(tmp1, tmp2, out); //任务调用

end

always @(negedge en_mult) begin //停止计算

disable multme; //***禁止任务的执行***

diable arith_block; //***禁止命名块的执行***

end

//在此定义任务和函数

endmodle


语法详细讲解禁止命名块和任务

注意:

  • disable语句用来终止命名块或任务的执行,因此可以在执行所有的语句前,就能从命名块或任务的执行中返回。

    语法:

    disable 〈块名〉

    disable 〈任务名〉

  • 禁止执行命名块或任务后,所有在事件队列中安排的事件都将被删除。

  • 在综合中一般不支持disable语句。

    在上页的例子中,只禁止命名块也可以得到预期的结果:命名块中所有的事件、任务和函数的执行都将被取消。


语法详细讲解有限状态机(FSM)

隐式FSM:

  • 不需要状态寄存器

  • 仿真更加有效

  • 只能很好的处理线形状态改变

  • 大部分综合工具不支持隐式FSM

state 1

state 2

state 3

state 4


语法详细讲解编译引导语句

语法详细讲解有限状态机(FSM)

显式FSM:

  • 结构更加复杂

  • 可以很方便的用来处理默认状态

  • 能够处理复杂的状态改变

  • 所有的综合工具均支持显式FSM

state A

state A

state A

state A

state A


语法详细讲解有限状态机(FSMs)

注意:

  • 在隐式 FSMs 中,无论什么时候在一个时钟周期内写数据和在在另一个时钟周期内读数据,都会创建寄存器。

  • 所有的 FSMs 都必须能复位,状态改变必须与一个单一的时钟信号同步。

  • 一般的,如果状态改变比较简单,又定义的比较好,而且综合工具支持隐式状态机,就可以使用隐式类型。如果状态改变比较复杂,最好使用显式类型,这样会更加有效。

  • 隐式状态机应属于行为级,而不应属于RTL级,代码中主要包含循环、嵌入的定时控制,有时也含有命名事件、wait 和 disable 语句。因此,综合中一般不支持隐式机。


Verilog11
语法详细讲解Verilog 函数

要返回一个矢量值(超过一个位宽),可以在函数名前声明变量的位宽。在函数中,将语句放在 begin 和 end 块中。

在函数中无论多少次对函数名进行赋值,值只返回一次。下面的函数中声明了一个内部整型变量。

module foo;

input [7:0] loo;

output [7:0] goo;

//可以从连续赋值中调用函数

wire [7:0] goo=zero_count(loo);

function [3:0] zero_count;

input [7:0] in_bus;


Verilog12
语法详细讲解Verilog 函数

integer I;

begin

zero_count=0;

for(I=0; I<8; I=I+1)

if(!in_bus[I])

zero_count=zero_count+1;

end

endfunction

endmodule


Verilog13
语法详细讲解Verilog 函数

可以声明函数的返回值的类型为 integer、real 或 time, 可以在任何表达式中调用函数。

module checksub(neg, in_a, in_b);

output neg;

reg neg;

function integer_subtr;

input [7:0] in_a, in_b;

subtr=in_a-in_b; //结果可能是负数

endfunction

always @(a or b)

if(subtr(a, b)<0)

neg=1;

else neg=0;

endmodule


Verilog14
语法详细讲解Verilog 函数

可以给返回变量的每一位赋值,还可以参数化函数的大小、函数口、甚至函数的行为。

...

parameter MAX_BITS=8;

reg [MAX_BITS:1] D;

function [MAX_BITS:1] reverse_bits;

input [7:0] data;

integer K;

for(K=0; K<MAX_BITS; K=K+1)

reverse_bits [MAX_BITS-(K+1)]=data[K];

endfunction

always @(posedge clk)

D=reverse_bits(D);

...


datain = 0

0

datain = 1

1

语法详细讲解显式有限状态机

语法详细讲解显式有限状态机

module exp(out, datain, clk, rst);

input clk, rst, datain;

output out; reg out;

ret state;

always @(pasedge clk or posedge rst)

if(rst) {state, out}=2’b00;

else

case(state)

1’b0: begin

out=1’b0;

if(!datain) state=1’b0;

else state=1’b1;

end

1’b1 begin

状态变量

case语句


clk

rst

识别11序列

0

1

1

0

1

out

语法详细讲解显式有限状态机

out=datain;

state=1’b0;

end

default: {state, out}=2’b00;

endcase

endmodule

注:

  • 可以在过程块中使用一个单一的时钟沿和一个 case 语句来描述一个显式状态机。

  • 必须规定一个状态变量,用来定义状态机的状态。

  • 要改变当前的状态,必须改变状态变量的值,状态变量的值的改变要与时钟沿同步。

  • 最好为通常不会发生的状态规定默认动作。

转到下一个状态

默认状态指针


datain = 0

0

datain = 1

1

语法详细讲解隐式有限状态机

module imp(out, datain, clk, rst);

output out; reg out;

input clk, datain, rst;

always @(rst) //协同复位方法

if (rst) assing out=1’b0;

else

begin

deassign out;

disable seq_block; //返回到初始状态

end

always @(posedge clk)


clk

rst

识别11序列

0

1

1

0

1

out

语法详细讲解隐式有限状态机

begin: seq_block

out=1’b0;

if(!datain) //状态一:输出零

disable seq_block;

@(posedge clk) //状态二:输出第二位

out=datain;

end

endmodule


语法详细讲解隐式有限状态机

注意:

  • 可以在过程块使用多个时钟沿(一个状态一个)、条件语句、循环语句、disable语句来描述隐式FSM。

  • 不必定义一个状态变量。

  • 若没有强制状态重复(例如:在循环语句或disable语句中强制状态重复),当下一个激活时钟沿到达时,状态改变。下一个装态将由条件语句决定。

  • 在隐式状态机中,很难规定一个默认动作。


语法详细讲解第十八部分 用户定义的原语

目标

  • 学会怎样创建逻辑使用的用户定义的原语。

  • 用户定义的原语 (UDP) 的行为与嵌入的 Verilog 原器件相似,可以用一个表格来定义它的功能。


语法详细讲解什么是UDP?

在 Verilog 结构模型中,可以使用:

  • 二十多个门级原器件。

  • 用户定义的原语。

    UDP 可用于ASIC 库单元设计和小规模芯片和中规模芯片设计。

  • 可以使用 UDP 扩大预定义原器件的范围。

  • UDP 是自包含的, 不能调用其他的模块。

  • UDP 既可以用来代替时序逻辑元件,也可以代替组合逻辑元件。

  • 使用真值表来描述 UDP 的行为。

  • 调用 UDP 的方式与调用嵌入的原器件的方式相同。


语法详细讲解什么是UDP?

注:

  • UDP 是一种很好的代替逻辑方式。

  • 在嵌入原器件中,输入中包含的 x 不能在输出时自动转变成 x,而在 UDP 中则不会出现此种情况。

  • UDP 可以代替许多原器件逻辑。另外,UDP 的仿真时间和内存需要大大低于运行分立原器件。如果仿真工具合适,相同逻辑的行为模型甚至可以更快。


语法详细讲解特征

  • UDP 只能有一个输出端,而且必须是端口的说明列表的第一项。

  • UDP 可以有多个输入端,最多允许有 10 个。

  • UDP 所有端口变量必须是标量,不允许使用双向端口。

  • UDP 不支持 Z 逻辑值。

  • 在仿真的开始,UDP 的输出可以使用 initial 语句初始化为一个已知值。

  • UDP 不能被综合。


语法详细讲解特征

注:

  • UDP 只能有一个输出。如果设计时的输出多于一个,则需要把其它的原器件连接到 UDP 输出,或同时使用多个 UDP。

  • UDP 输入端最多可以有 10 个,但是当输入的个数多于 5 个时,内存的需要将大大的增加。下页表列出了输入数目不同时,每个输入所需要的内存。



语法详细讲解示例

原语名

组合逻辑示例:2-1 多路器

primitive multiplexer(o, a, b, s);

output o;

input s, a, b;

table

// a b s : o

0 ? 1 : 0;

1 ? 1 : 1;

? 0 0 : 0;

? 1 0 : 1;

0 0 x : 0;

1 1 x : 1;

endtable

endprimitive

输出端口必须为第一个端口


语法详细讲解示例

注:

  • 在模块外定义 UDP 。

  • 如果在表中没有规定输入组合,将输出 x。

  • 表的列中元素的顺序应与端口列表中的一致。

  • 表中的 ?的意义是:重复的输入 0,1或 x ;逻辑值。

  • 表中开始两行表示:当 s等于 1 时,不管 b 如何取值,输出 o 将与 输入 a 保持一致。

    表中的下两行表示:当 s 等于 0 时,不管 a 如何取值,输出 o 将与输入 b 保持一致。

    表中 的最后两行使此器件更加的全面、准确。它们表示:当输入 a 和 b 的逻辑值相同时,如果 sel 等于 x,则输出 o 的值 将与输入 a 和 b 的值相同。这种行为不能使用 Verilog 嵌入原器件进行建模。UDP 将 x 作为实际的未知值,而不是 Verilog 值来进行处理,使其比嵌入原器件更加准确。


语法详细讲解组合示例:全加器

可以仅使用两个 UDP 设计全加器。

// 全加器进位实现部分

primitive U_ADDR2_C(CO, A, B, CI);

output CO;

input A, B, CI,

table // A B CI : CO

1 1 ? : 1;

1 ? 1 : 1;

? 1 1 : 1;

0 0 ? : 0;

0 ? 0 : 0;

? 0 0 : 0;

endtalbe

endprimitive


语法详细讲解组合示例:全加器

//全加器求和实现部分

primitive U_ADDR2_S(S, A, B,CI);

output S;

input A, B, CI;

table // A B CI : S

0 0 0 : 0;

0 0 1 : 1;

0 1 0 : 1;

0 1 1 : 0;

1 0 0 : 1;

1 0 1 : 0;

1 1 0 : 0;

1 1 1 : 1;

endtable

endprimitive


语法详细讲解组合逻辑示例:全加器

若使用 UDP 设计全加器,仅需要两个 UDP; 而使用 Verilog 嵌入原器件,则需要 5 个Verilog 嵌入原器件。

  • 大量使用全加器时,这将大大减少内存的需要。

  • 事件的数目将大大降低。

  • ?代表 0,1或 x。


语法详细讲解级触发时序逻辑示例:锁存器

primitive latch(q, clock, data);

output q;

reg q;

input clock, data;

initial q=1’b1;

table

// clock data current next

// state state

0 1 : ? 1;

0 0 : ? 0;

1 ? : : -;

endtable

endprimitive

注意此寄存器的用法,此寄存器用来存储。

输出初始化为 1‘b1.

? 表示无须考虑输入和当前状态的值


语法详细讲解级触发时序逻辑示例:锁存器

注:

  • 锁存器的动作行为如下:

    当时钟信号为 0时,输入数据的值直接传给输出。

    当时钟信号为1时,输出保持当前状态不变。

  • next state 栏中的 “-” 表示输出保持不变。

  • 输出必须定义为寄存器类型,用来保存前一个状态。

  • initial q=1’b1;是时序 UDP 的初始化语句。使用此语句可以在仿真的开始对输出进行赋值。

    在实际的部件模型中,很少使用初始赋值。但在测试 UDP 的功能时,初始赋值相当有用。


语法详细讲解边沿敏感的时序逻辑示例:D 触发器

primitive d_edge_ff (q, clk, data);

output q;

input clk, data;

reg q;

table // clk dat state next

(01) 0 : ? : 0;

(01) 1 : ? : 1;

(0x) 1 : 1 : 1;

(0x) 0 : 0 : 0;

(x1) 0 : 0 : 0;

(x1) 1 : 1 : 1;


语法详细讲解边沿敏感的时序逻辑示例:D 触发器

// 忽略时钟的下降沿

(?0) ? : ? : -;

(1x) ? : ? : -;

// 忽略时钟稳定时的数据改变

endtable

endprimitive

  • 在大多数情况下,可以在任何表入口语句中规定一个输入过渡。

  • 如果规定了任何输入过渡,则必须规定所有输入的所有过渡。


Verilog15
语法详细讲解第十九部分 Verilog综合建模类型

建模类型概述

在任何时候,如果输出直接由当前的输入组合决定,则此逻辑为组合逻辑。

如果逻辑中具有记忆功能,则此逻辑为时序逻辑。在任何给定的时刻,如果输出不能由输入的状态确定,则此逻辑具有记忆功能。


Verilog16
语法详细讲解不受支持的 Verilog 结构

综合工具一般不支持下面的 Verilog 结构:

initial

一些循环语句:

repeat

forever

while

for 的非结构用法

一些数据类型

event

real

time


Verilog17
语法详细讲解不受支持的 Verilog 结构

UDPs

fork…join 块

wait

过程连续赋值语句

assign 和 deassign

force 和 release

一些操作符

= = =

!= =


语法详细讲解过程块

  • 两个边沿的任一个

    过程块到达所有输入信号的任一个边沿产生组合逻辑。此块称为组合块。

    always @(a or b) // 实现与门

    y=a&b;

  • 单个边沿

    过程块到达控制信号的单个边沿(下降沿或上升沿)产生同步逻辑。此块称为同步块。

    always @(posedge clk) //实现 D 触发器

    q<=d;


语法详细讲解过程块

同步块也可能对异步复位信号的改变敏感。

always @(posedge clk or negedge rst_)

if(!rst_)

q<=0;

else

q<=d;


语法详细讲解过程块中寄存器类型的信号变量

当在同步块中使用reg类型的信号变量时:

  • 如果此信号变量在一个时钟周期中被附值,而在另一个时钟周期中创建了其实例,则此信号变量仅作为硬件寄存器使用。

  • 如果此信号变量也是基本输出,则其将显示在综合列表中,但不一定是硬件寄存器。

  • 否则,信号可以被优化掉。

    当在组合块中使用reg类型的信号变量时:

  • 如果当块的任何输入的值改变时,此信号变量的值也随之改变,则此信号变量在综合输出中并不属于硬件寄存器。

  • 如果当块的任何输入的值改变时,此信号变量的值并一定改变,则此信号变量在综合输出中属于锁存器。


语法详细讲解寄存器

同步寄存器示例

在下面的例子中,rega 仅用作临时存储器,因此它被优化掉。

module ex1reg(d, clk, q);

input d, clk;

output q;

reg q, rega;

always @(posedge clk)

begin

rega=0;

if(d) rega=1;

q=rega;

end

endmodule


语法详细讲解寄存器

在下面的例子中,两个时钟沿包含两个存储元素,因此rega 未被优化掉。

module ex2reg(d, clk, q);

input d, clk;

output q;

reg q, rega;

always @(posedge clk)

begin

rega=0;

if(d) rega=1;

end


语法详细讲解寄存器

always @(posedge clk)

q=rega;

endmodule

注:在后面的例子中,块执行的顺序是不确定的,因此 q 可以获得 在前一个周期中赋给rega的值。


语法详细讲解寄存器

组合寄存器示例

在下面的两个例子中,rega是一个临时变量且被优化掉。

在下面的例子中,y和rega不断被赋新值;因此,下例是一个纯的组合逻辑。

module ex3reg(y, a, b, c);

input a, b, c;

output y;

reg y, rega;

always @(a or b or c)

begin


语法详细讲解寄存器

if(a&b)

rega=c;

else

rega=0;

y=rega;

end

endmodule

在下面的例子中,rega只是有时被赋新值;因此此例是一个以 y 作为输出的锁存器。


语法详细讲解寄存器

moudule ex4reg(y, a, b, c);

input a, b, c;

output y;

reg y, rega;

always @(a or b or c)

begin

if(a&b)

rega=c;

y=rega;

end

endmodule


语法详细讲解敏感列表

在下面的例子中,a、b 和 sl均是块的输入。

  • 在两个例子中, sl均为 always块的条件。

  • 在第二个例子中, a 和 b也用作always块的条件。

    不完整敏感列表:

    module sens(a, q, b, sl);

    input a, b, sl;

    output q;

    reg q;

    always @(sl)

    begin


语法详细讲解敏感列表

if(!sl)

q=a;

else

q=b;

end

endmodule

完整敏感列表:

module sens(q, a, b, sl);

input a, b, sl;

output q;


语法详细讲解敏感列表

reg q;

always @(sl or a or b)

begin

if(!sl)

q=a;

else

q=b;

end

endmodule

注:在敏感列表中最好包括所有的输入。对于不完整列表,不同的综合工具处理的方法不同:一些综合工具认为不完整列表是不合


语法详细讲解敏感列表

法的,而其他的综合工具则发出警告并将其当作完整列表处理。因此,综合输出可能与 RTL描述有所不同。


语法详细讲解连续赋值

在进行连续赋值时,输入的任何改变都将导致输出的同步更新。

module orand(out, a, b, c, d, e);

input a, b, c, d, e;

output out;

assign out=3&(a|b)&(c|d);

endmodule


语法详细讲解过程连续赋值

过程连续赋值是在过程块(always和 initial块)内部对寄存器类型的数据进行的连续赋值。

module latch_quasi(q, en, d);

input en, d;

output q;

reg q;

always @(en)

if(en)

assign q=d;

else

deassign q;

endmodule


语法详细讲解综合指令

  • 大部分综合工具都能处理指令。

  • 可在 Verilog 语句之间嵌入指令,Verilog 仿真器将忽略嵌入的指令,但在综合工具编译时,它们是有意义的。

  • 不同的综合工具有不同的指令语法,当它们使用指令的目的都是优化 RTL 代码。

    下面给出了一部分 Cadence 综合工具中支持的综合指令的列表,它们与其他综合工具(例如:Synopsys 设计编译器)中的指令非常相似。

    //ambit synthesis on

    //ambit synthesis off

    //ambit synthesis case=full、parallel、mux


语法详细讲解综合指令

结构指令

//ambit synthesis architecture=cla or rpl

有限状态机指令

//ambit synthesis enum xyz

//ambit synthesis stat_vector sig state_vector_flag

注:指令中通常包括综合工具或公司的名称,例如:上面指令中的ambit 表示使用的 Envisia Ambit 综合指令。


语法详细讲解综合指令

语法详细讲解综合指令

case 指令

case 指令一般被综合为优先编译器,其中列表中的case 项优先级较高。

case指令的含义如下所示:

  • //ambit synthesis case=parallel

    对解码逻辑进行并行编译,没有优先级。

  • //ambit synthesis case=mux

    如果库中含有多路器,则使用多路器编译解码逻辑。

  • //ambit synthesis case=full

    设定忽略没有包含在 case 语句中的情形(这些情形不会发生),这样可以更好的优化逻辑并可避免发生锁存。


语法详细讲解条件语句

完整条件语句

module comcase(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c or d)

case ({a,b})

2’b11: e=d;

2’b10: e=~c;

2’b01: e=1’b0;

2’b00: e=1’b1;

endcase

endmodule


语法详细讲解条件语句

module compif(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c or d)

if(a&b)

e=d;

else if (a&~b)

e=~c;

else if (~ a&b)

e=1’b0;

else if (~a&~b)

e=1’b1;

endmodule


语法详细讲解不完整条件语句

module inccase(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c ord)

case ({a,b})

2’b11: e=d;

2’b10: e=~c;

endcase

endmodule


语法详细讲解不完整条件语句

module incpif(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c or d)

if (a&b)

e=d;

else if(a&~b)

e=~c;

endmodule


语法详细讲解不完整条件语句

在前面所述的例子中,当 a 为 0 时,没有值赋给 e。因此,e 将保存原来的值,直到 a 变为 1。此行为与锁存器的特性相同。


语法详细讲解带有缺省项的完整条件语句

module comcase(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c or d)

case ({a,b})

2’b11: e=d;

2’b10: e=~c;

default: e=‘bx;

endmodule


语法详细讲解带有缺省项的完整条件语句

module compif(a, b, c, d, e);

input a, b, c, d;

output e;

reg e;

always @(a or b or c or d)

if (a&b)

e=d;

else if (a&~b)

e=~c;

else

e=‘bx;

endmodule


语法详细讲解带有缺省项的完整条件语句

在前面的例子中,虽然没有定义所有可能的选择,但为没有定义的选择定义了缺省的行为。因此,它们都是纯的组合逻辑,并没有产生额外的锁存器。


语法详细讲解带有指令的完整 case 语句

module dircase(a, b, c, d)

input b, c;

input [1:0] a;

output d;

reg d;

always @(a or b or c)

case (a) //ambit synthesis case = full

2’b00: d=b;

2’b01: d=c;

endcase

endmodule

在此例中,虽然没有定义所有可能的选择,但其中的指令通知优化器没有定义的选择将不会发生。此例为纯组合逻辑,不会产生锁存器。


语法详细讲解case 指令的例外情况

当设置了 case 指令为 full 时,也可从 case 语句中综合出锁存器。示例如下:

module select ( a, b, sl);

input [1:0] sl;

output a, b;

reg a, b;

always @(sl)

case (sl) //ambit synthesis case = full

2’b00: begin a=0; b=0; end

2’b01: begin a=1; b=1; end

2’b00: begin a=0; b=1; end


语法详细讲解case 指令的例外情况

2‘b11: b=1;

default: begin a=‘bx; b=‘bx; end

endcase

endmodule


语法详细讲解 函数

函数不包含时间控制,因此它们综合为组合逻辑。可以在过程块和连续赋值语句中调用函数。

下面是 or/and 块,其中在连续赋值语句中调用了函数。

module orand(out, a, b, c, d, e);

input a, b, c, d, e;

output out; wire out;

assign out=forand(a, b, c, d, e);

function forand;

if(e==1)

forand(a|b)&(c|d);


语法详细讲解 函数

else

forand=0;

endfunction

endmodule


语法详细讲解 任务

任务仅用于测试模块中,因为:

  • 不包含时间控制的任务与函数的作用相似。

  • 包含时间控制的任务是不可综合的。

    下面的 or/and 块中调用了任务:

    module orandtask(out, a, b, c, d, e);

    input a, b, c, d, e;

    output out; reg out;

    always @(a or b or c or d or e)

    orand(out, a, b, c, d, e);

    task orand;

    input a, b, c, d, e;


语法详细讲解 任务

output out;

if(e==1)

out=(a|b)&(c|d);

else

out=0;

endtask

endmodule


语法详细讲解 怎样产生锁存器

在 always 块中,如果没有规定所有的条件,则会产生锁存器。在下面的例子中,当 enable 为低电平时,没有定义怎样处理 q 和 data,因此 data 的值将会被保存下来。综合器必须使用存储元件来编译此逻辑。

module latch(q, data, enable);

input data,enable;

output q;

reg q;

always @(enable or data)

if(enable)

q=data;

endmodule


语法详细讲解 怎样产生同步反馈

综合工具一般只支持同步反馈,而不支持组合反馈。

在条件语句每个分支中,当过程块没有为每个输出赋值时,就会产生反馈

不产生反馈:

module dffn(q,d,clk,en);

input d, clk, en;

output q;

reg q;

always @(negedge clk)

if(en)

q<=d;

else

q=‘bx;

endmodule

产生反馈:

module dffn(q,d,clk,en);

input d, clk, en;

output q;

reg q;

always @(negedge clk)

if(en)

q<=d;

endmodule


语法详细讲解 阻塞与非阻塞(赋值方式)

赋值的类型的选择取决于建模的逻辑类型

  • 在时序块的 RTL 代码中使用非阻塞赋值。

    非阻塞赋值在块结束后才完成赋值操作,此赋值方式可以避免在仿真出现冒险和竞争现象。

  • 在组合的 RTL 代码中使用阻塞赋值。

    使用阻塞方式对一个变量进行赋值时,此变量的值在在赋值语句执行完后就立即改变。


语法详细讲解 阻塞与非阻塞(赋值方式)

使用非阻塞赋值方式进行赋值时,各个赋值语句同步执行;因此,通常在一个时钟沿对临时变量进行赋值,而在另一个时钟沿对其进行采样。

下面的模块综合为两个触发器

,其中采用了非阻塞赋值方式:

module nonbloc(clk,a,b);

input clk, a;

output b; reg b;

reg y;

always @(posedge clk)

begin

y<=a;

b<=y;

end

endmodule

下面的模块综合为触发器,

其中采用了阻塞赋值方式:

module bloc(clk,a,b);

input clk, a;

output b; reg b;

reg y;

always @(posedge clk)

begin

y=a;

b=y;

end

endmodule


语法详细讲解 阻塞与非阻塞(赋值方式)

上面的两个例子的综合的结果不同,左边的例子使用了阻塞赋值方式,综合器将其综合为一个触发器。右边的例子使用了非阻塞赋值方式,综合器将其综合为两个触发器,y 将出现在综合列表中,作为第二个触发器的输入。综合结果如下所示:

a

y

b

b

a

clk

clk


语法详细讲解 复位建模

复位是可综合风格代码的重要组成部分,通常在有限状态机中使用复位建模。

同步复位:

module sync(q,ck,r,d);

input ck, d, r;

output q; reg q;

always @(negedge ck)

if(r)

q<=0;

else

q<=d;

endmodule

同步块中的异步复位:

module async(q,ck,r,d);

input ck, d, r;

output q; reg q;

always @(negedge ck or

posedge r)

if(r)

q<=0;

else

q<=d;

endmodule


语法详细讲解 复位建模

在下面的例子中,使用了一个独立的异步复位来实现异步复位。

module async(q, ck, r, d);

input ck, d, r;

output q; reg q;

always @(negedge ck)

if(!r) q<=d;

always @(posedge r)

q<=0;

endmodule

不提倡使用上述风格的异步复位代码,上述代码是不可综合的。在

仿真时,若 r 和 ck 同时在同一个时间单元中改变,则结果是不确定

的。


语法详细讲解锁存器复位

下面的例子演示了更加复杂的复位建模。其中的敏感列表是完全的,

因为这是一个锁存器。

module latch(q, enable, set, clr, d);

input enable, d, set, clr;

output q; reg q;

always @(enable or set or clr or d)

begin

if(set)

q<=1;

else if (clr)

q<=0;

else if (enable)

q<=d;

end

endmodule


语法详细讲解FSM 指令

在状态机建模中也可以使用综合指令。FSM 指令将RTL级代码中状态机的性质信息传送给优化器。

FSM 指令包括:

  • enum 指令

    用于状态赋值列举,还用于将状态值绑定到状态矢量上。

  • state_vector 指令

    用于帮助综合器识别状态寄存器和解码类型。


语法详细讲解FSM 指令

`timescale 1ns/100ps

module state4(clock, reset, out);

input reset, clock;

outpt [1:0] out;

reg [1:0] out;

paramete /*ambit synthesis enum state_info */

stateA=2’b00,

stateB=2’b01,

stateC=2’b10,

stateD=2’b11;

reg [1:0] /*ambit synthesis enum state_info */ state;

reg [1:0] /*ambit synthesis enum state_info */ nextstate;

将列举名绑定到矢量state和nextstate上

定义列举名


语法详细讲解FSM 指令

always @(posedge clock)

/* ambit synthesis state_vector state –encoding one_hot*/

if(reset)

state<=stateA;

else

state<=nextstate;

定义状态寄存器并指定所需的解码类型


语法详细讲解资源共享

资源共享即 RTL 级代码的两个或两个以上的部分共享一组逻辑。

例如:

always @(a or bor c or d)

if(a)

out=b+c;

else

out=b+d;

注:资源共享与否取决于综合工具,但是一般地,资源共享的条件是共享逻辑所在的语句位于同一模块,同一 always 块的同一条件语句中。


c

d

b

c

b

d

a

b

+

+

a

out

out

语法详细讲解资源共享

资源共享

资源不共享

然而,可以通过改变 RTL 级代码的编码风格控制资源共享,例如:

原代码:

if (a)

out=b+c;

else

out=b+d;

强制实现资源共享的代码:

temp=a ? c : d;

out=b+temp;

out=b+(a ? c : d);

+


语法详细讲解条件语句

if 和 if-else语句

always # 20

if(index>0) // 外部 if 语句的开始

if(rega>regb) // 第一个内嵌 if 语句的开始

result=rega;

else

result=0; //第一个内嵌 if 语句的结束

else

if(index==0)

begin

$display(“Note: Index is zero”);


语法详细讲解条件语句

result=regb;

end

else

$display(“Note: Index is negative”);

注:在多重 if 嵌套语句中,else 与前面最近的 if 相对应(即与前面最近的 if 组成一对 if-else 语句。

为确保程序的可读性和语句的对应性,请使用 begin…end 块语句。


ad