语法详细讲解 强制激励

Preview:

DESCRIPTION

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

Citation preview

语法详细讲解强制激励

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

过程连续赋值往往是不可以综合的,通常用在测试模块中。 两种方式都有各自配套的命令来停止赋值过程。 两种不同方式均不允许赋值语句间的时间控制。

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 clk;

always begin

#period/2 clk=0;

#period/2 clk=1;

end

reg go; wire clk;

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

initial begin

go=0;

#(period/2) go=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

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

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

wait waitwait waitdata1 data2 data3 data4

cpu_data

clk

data_valid

data_read

read_cpu_state

语法详细讲解存储建模

目标

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

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

语法详细讲解存储建模

存储设备建模必须注意以下两个方面的问题:声明存储容量的大小。提供对内容的访问权限,例如:

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

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

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 建模

my_rom_data 0000 0101 1100 0011 1101 0010 0011 1111 1000 1001 1000 0001 1101 1010 0001 1101

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

语法详细讲解简单 ROM 建模

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

语法详细讲解简单 RAM 建模

`timescale 1ns/1nsmodule 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口做为输入口时,必须使输出逻辑失效。

使用元器件

语法详细讲解双向口建模

b2

b1

en_a_b

en_b_a

bus_a

bus_b

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,如果进行同时控制,则得不到预期的结果。

使用连续赋值en_a_

b

en_b_a

bus_a

bus_b

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 上

语法详细讲解双向口建模

b2

b1

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

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

语法详细讲解双向口建模

存储口建模

语法详细讲解双向口建模

测试模块 RAM 单元

数据总线数据寄存器

rd

wr

module ram_cell(databus,rd.wr);inout databus;input rd,wr;reg datareg; assign databus=rd? datareg:’bz; always @(negedge sr) datareg<=databus;endmodule

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

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

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

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

语法详细讲解双向口建模

目标 学会怎样定义或调用任务和函数。 学会怎样使用命名块。 学会怎样使命名块和任务失效。 熟悉有限状态机及怎样进行有限状态机显式建模。

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

可以通过把代码分成小的模块或者使用任务和函数,来把一项任务分成许多较小的、易于管理的部分,从而提高代码的重用性。 任务 一般用于执行调试操作,或者行为的描述硬件 可以包含时间控制( # delays, @, wait) 可以包含 input, output 和 inout参数 可以调用其他的任务或函数 函数 一般用于计算,或者用来代替组合逻辑 不能包含任何延迟;函数在零时间执行 只能使用 input参数,但可以通过函数名来返回一个值。 可以调用其他的函数,但不可以调用任务

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

注: 必须在模块内调用任务和函数。 在任务和函数中不能声明连线类型的变量。 所有的输入和输出都是真正的本地寄存器类型的数据

。 只有当任务或函数调用并执行完后,才能有返回值。

例如:当任务或函数中包含一个 forever循环时,就不可能有返回值。

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

语法详细讲解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)

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

@(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参数。

语法详细讲解Verilog 任务

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

在任务中可以使用时间控制。 在 Verilog 中,任务定义了一个新的范围。 使用关键字 disable禁止任务。

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

在任务或函数中,应给在父模块中声明的变量加注释。若在其它

模块中调用任务或函数,任务和函数中所使用的变量必须包含在输入 /输出口列表中。

语法详细讲解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); ……

语法详细讲解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); // 任务调用

语法详细讲解Verilog 任务

task muotme; // 任务定义 input [3:0] xme, tome; output [7:0] result; wait (en_mult) result=xme*tome; endtask endmodule

语法详细讲解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; endfunctionendmodule

语法详细讲解Verilog 函数

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

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

要点 函数定义不能包含定时控制语句。 函数必须含有输出,但不能含有输出和总线口; 一个函数只能返回一个值,该值的变量名与函数同名,数据类

型默认为 reg 类型。 传递给函数参数的顺序与函数输入参数声明的顺序相同。 函数定义必须包含在模块定义之内。 函数不能调用任务,但任务可以调用函数。 函数使 Verilog 有更广阔的适用范围。

语法详细讲解Verilog 函数

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

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

语法详细讲解Verilog 函数

语法详细讲解命名块

可以通过在关键字 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 都必须能复位,状态改变必须与一个单一的时钟信号同步。

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

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

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

语法详细讲解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;

语法详细讲解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

可以声明函数的返回值的类型为 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

语法详细讲解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); ...

语法详细讲解Verilog 函数

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

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 语句

0

1

datain = 0

datain = 1

out=datain; state=1’b0; end default: {state, out}=2’b00; endcaseendmodule

注: 可以在过程块中使用一个单一的时钟沿和一个 case 语句来描

述一个显式状态机。 必须规定一个状态变量,用来定义状态机的状态。 要改变当前的状态,必须改变状态变量的值,状态变量的值的

改变要与时钟沿同步。 最好为通常不会发生的状态规定默认动作。

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

转到下一个状态

默认状态指针

0 11 01 识别 11 序列

clk

rst

out

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)

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

0

1

datain = 0

datain = 1

begin: seq_block

out=1’b0;

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

@(posedge clk) // 状态二:输出第二位 out=datain;

end

endmodule

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

0 11 01 识别 11 序列

clk

rst

out

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

不必定义一个状态变量。 若没有强制状态重复(例如:在循环语句或 disable 语句中强制状

态重复),当下一个激活时钟沿到达时,状态改变。下一个装态将由条件语句决定。

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

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

目标 学会怎样创建逻辑使用的用户定义的原语。 用户定义的原语 (UDP) 的行为与嵌入的 Verilog 原

器件相似,可以用一个表格来定义它的功能。

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

在 Verilog 结构模型中,可以使用: 二十多个门级原器件。 用户定义的原语。UDP 可用于 ASIC 库单元设计和小规模芯片和中规模芯片设计。 可以使用 UDP 扩大预定义原器件的范围。 UDP 是自包含的, 不能调用其他的模块。 UDP 既可以用来代替时序逻辑元件,也可以代替组合逻辑元件。 使用真值表来描述 UDP 的行为。 调用 UDP 的方式与调用嵌入的原器件的方式相同。

语法详细讲解什么是 UDP ?

注: UDP 是一种很好的代替逻辑方式。 在嵌入原器件中,输入中包含的 x 不能在输出时自动转变成 x ,而在 UDP 中则不会出现此种情况。

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

语法详细讲解什么是 UDP ?

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

UDP 可以有多个输入端,最多允许有 10 个。 UDP 所有端口变量必须是标量,不允许使用双向端口。 UDP 不支持 Z 逻辑值。 在仿真的开始, UDP 的输出可以使用 initial 语句初

始化为一个已知值。 UDP 不能被综合。

语法详细讲解特征

注: UDP 只能有一个输出。如果设计时的输出多于一个,则需要把

其它的原器件连接到 UDP 输出,或同时使用多个 UDP 。 UDP 输入端最多可以有 10 个,但是当输入的个数多于 5 个时,

内存的需要将大大的增加。下页表列出了输入数目不同时,每个输入所需要的内存。

语法详细讲解特征

语法详细讲解特征

# 输入 内存1-5 <1

6 5

7 17

8 56

9 187

10 623

组合逻辑示例: 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; endtableendprimitive

原语名

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

注: 在模块外定义 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; endtalbeendprimitive

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

//全加器求和实现部分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; endtableendprimitive

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

若使用 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 ? : : -; endtableendprimitive

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

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

输出初始化为 1‘b1.

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

注: 锁存器的动作行为如下: 当时钟信号为 0 时,输入数据的值直接传给输出。 当时钟信号为 1 时,输出保持当前状态不变。 next state 栏中的 “ -” 表示输出保持不变。 输出必须定义为寄存器类型,用来保存前一个状态。 initial q=1’b1; 是时序 UDP 的初始化语句。使用此语句可以在

仿真的开始对输出进行赋值。 在实际的部件模型中,很少使用初始赋值。但在测试 UDP 的功

能时,初始赋值相当有用。

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

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

在大多数情况下,可以在任何表入口语句中规定一个输入过渡。 如果规定了任何输入过渡,则必须规定所有输入的所有过渡。

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

建模类型概述

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

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

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

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

一些循环语句: repeat

forever

while

for 的非结构用法一些数据类型 event

real

time

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

UDPs

fork…join 块 wait

过程连续赋值语句 assign 和 deassign

force 和 release

一些操作符 = = =

! = =

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

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

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;endendmodule

语法详细讲解寄存器

在下面的例子中,两个时钟沿包含两个存储元素,因此 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; endcaseendmodule

语法详细讲解条件语句

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

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

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

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

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

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

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

下面是 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;

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

赋值的类型的选择取决于建模的逻辑类型 在时序块的 RTL 代码中使用非阻塞赋值。 非阻塞赋值在块结束后才完成赋值操作,此赋值方式可以避免在

仿真出现冒险和竞争现象。 在组合的 RTL 代码中使用阻塞赋值。 使用阻塞方式对一个变量进行赋值时,此变量的值在在赋值语句

执行完后就立即改变。

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

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

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

下面的模块综合为触发器,其中采用了阻塞赋值方式:module bloc(clk,a,b);input clk, a;output b; reg b;reg y;always @(posedge clk)begin y=a; b=y;endendmodule

下面的模块综合为两个触发器,其中采用了非阻塞赋值方式:module nonbloc(clk,a,b);input clk, a;output b; reg b;reg y;always @(posedge clk)begin y<=a; b<=y;endendmodule

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

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

a b

clkclk

a by

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

语法详细讲解 复位建模

同步复位: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; endendmodule

语法详细讲解锁存器复位

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

FSM 指令包括: enum 指令 用于状态赋值列举,还用于将状态值绑定到状态矢量上。

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

语法详细讲解FSM 指令

`timescale 1ns/100psmodule 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;

语法详细讲解FSM 指令

定义列举名

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

always @(posedge clock)

/* ambit synthesis state_vector state –encoding one_hot*/

if(reset)

state<=stateA;

else

state<=nextstate;

语法详细讲解FSM 指令

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

资源共享即 RTL 级代码的两个或两个以上的部分共享一组逻辑。例如:always @(a or bor c or d)

if(a)

out=b+c;

else

out=b+d;

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

语法详细讲解资源共享

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

原代码: if (a) out=b+c; else out=b+d;

强制实现资源共享的代码: temp=a ? c : d;

out=b+temp;

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

语法详细讲解资源共享

++

out

a

b c b d

out

ab

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 块语句。

语法详细讲解条件语句

Recommended