Verilog学习手记一

Verilog学习手记一

 

  1. 数字设计的流程如下:

设计输入(Verilog语言描述系统)       前仿真(验证设计系统的功能)       编译和综合(把设计的各个部分翻译成一个中间格式,再把所有部分连接起来,生成对应的逻辑,然后再目标硬件上布局布线,生成时序信息)       后仿真(考虑时序信息,修改设计)       生成硬件电路

  1. 所谓的寄存器传输级(RTL描述指的就是:数据在总线以及寄存器与寄存器之间的传递,用高层次的语法来描述一个所要设计的系统,将这个设计分成若干小模块,用总线将其连接搭建起来,然后再描述以及实现这些小模块的设计方法。
  2. 用RTL级描述Verilog结构包括三种形式:

过程说明:过程化的描述系统,例如使用if—else和case等过程结构;

连续赋值:描述系的逻辑块,总线赋值和总线输入、输出互联线;

例化说明:高层次的设计直接引用低层次的模块,用低层次的原件直接搭建一个完整的系统。

  1. 所谓前仿真:即是不考虑系统的线延迟、门延迟等时序问题(竞争冒险,毛刺,建立保持时间),而仅仅是想从行为上判断系统是否能够达到设计目的;后仿真则是针对建立的硬件模型,要考虑完整的时序信息(又编译和综合生成),如时间延迟、时钟频率、门级延迟、电路的建立保持时间等问题,重新仿真,以便修改设计。
  2. RTL的设计首先将设计分成数据部分以及控制部分。数据部分又叫数据单元、数据通道:包括寄存器、组合逻辑单元、寄存器阵列、连接它们的总线,它的输入输出来自外部,控制信号来信控制单元,同时也可以反馈给控制单元控制信号;控制部分又叫控制单元,通常由一个或多个状态机构成,接受并反馈给外部控制信号。如下图(1)所示:

 

图(1)

  1. Verilog模块的描述方式:

原语例化:所谓原语(基元)指的是最基本的逻辑门。该方式就是在设计中直接调用这些原语。

连续赋值:用布尔表达式来描述逻辑关系,常用assign语句进行赋值。

条件表达式:w = s ? a :b ,若s=1,则w=a;否则,w=b。

过程块: 常用always,if—else和case等过程结构。

模块例化: 将自己设计的模块当做原语调用。该方式要注意调用模块的实际端口要与电路中的信号端口要对应准确。例如:全加器full_adder(input a,b,output sum,count), 进行模块例化为

full_adder U1(.a(u1_a),.b(u1_b),.sum(u1_sum),.count(u1_count)),书写格式为:

以(.)开头,后面紧跟调用模块的实际端口,括号内书写电路中的信号端口。

或者例化为;

full_adder U1(u1_a,u1_b,u1_sum,u1_count), 格式为:按照对应位置书写。

  1. wire与reg类型的区别:

wire型数据常用来表示以assign关键字指定的组合逻辑信号。模块的输入输出端口类型都默认为wire型。默认初始值是z。

reg型表示的寄存器类型。always模块内被赋值的信号,必须定义为reg型,代表触发器。

默认初始值是x。

reg相当于存储单元,wire相当于物理连线。

Verilog 中变量的物理数据分为线型和寄存器型。这两种类型的变量在定义时要设置位宽,缺省为1位。变量的每一位可以是0,1,X,Z。其中x代表一个未被预置初始状态的变量或者是由于由两个或多个驱动装置试图将之设定为不同的值而引起的冲突型线型变量。z代表高阻状态或浮空量。

线型数据包括wire,wand,wor等几种类型在被一个以上激励源驱动时,不同的线型数据有各自决定其最终值的分辨办法。

两者的区别是:寄存器型数据保持最后一次的赋值,而线型数据需要持续的驱动

输入端口可以由net/reg驱动,但输入端口只能是net,如a = b & c,输入端口a 只能是net型,但其驱动bc可以是net/reg型;输出端口可以是net/reg类型,输出端口只能驱动net,如a = b & c,模块的输出端口b,c可以是net/reg型,但它们驱动的a必须是net型;若输出端口在过程块(always/initial)中赋值则为reg型,若在过程块外赋值则为net型。用关键词inout声明一个双向端口, inout端口不能声明为寄存器类型,只能是net类型。

wire表示直通,即只要输入有变化,输出马上无条件地反映;reg表示一定要有触发,输出才会反映输入。

不指定就默认为1位wire类型。专门指定出wire类型,可能是多位或为使程序易读。wire只能被assign连续赋值,reg只能在initial和always中赋值。wire使用在连续赋值语句中,而reg使用在过程赋值语句中。

在连续赋值语句中,表达式右侧的计算结果可以立即更新表达式的左侧。在理解上,相当于一个逻辑之后直接连了一条线,这个逻辑对应于表达式的右侧,而这条线就对应于wire。在过程赋值语句中,表达式右侧的计算结果在某种条件的触发下放到一个变量当中,而这个变量可以声明成reg类型的。根据触发条件的不同,过程赋值语句可以建模不同的硬件结构:如果这个条件是时钟的上升沿或下降沿,那么这个硬件模型就是一个触发器;如果这个条件是某一信号的高电平或低电平,那么这个硬件模型就是一个锁存器;如果这个条件是赋值语句右侧任意操作数的变化,那么这个硬件模型就是一个组合逻辑。

wire型的变量综合出来一般是一根导线;
reg
变量在always块中有两种情况:
   always
后的敏感表中是(a or b or c)形式的,也就是不带时钟边沿的,综合出来还是组合逻辑
  always
后的敏感表中是(posedge clk)形式的,也就是带边沿的,综合出来一般是时序逻辑,会包含触发器(FlipFlop

在设计中,输入信号一般来说你是不知道上一级是寄存器输出还是组合逻辑输出,那么对于本级来说就是一根导线,也就是wire型。而输出信号则由你自己来决定是寄存器输出还是组合逻辑输出,wire型、reg型都可以。但一般的,整个设计的外部输出(即最顶层模块的输出),要求是寄存器输出,较稳定、扇出能力也较好。

  1. Verilog延迟语句的运用

module full_adder(a,b,sum);

input a,b;

output reg sum;

always @(a,b) #13 sum = (a & b) ;   或者   always @(a,b) sum = #13 (a & b) ;

endmodule

注意到两个always语句的延迟语句的位置不同,后一个语句称为内部指定延迟

第一个always语句说明在a或b变化后,先阻塞运行,延迟13ns,再根据当前的a,b的值,计算sum的值。这就导致计算得到的sum值可能与13ns之前的不一样。

第二个always语句表示的是a或b变化后,立即计算当前的sum值,13ns后将该值赋给sum,得到的是13ns之前的值。

在相同的Testbench下仿真:

module test_fulladder;

// Inputs

reg a;

reg b;

// Outputs

wire sum;

// Instantiate the Unit Under Test (UUT)

full_adder uut (.a(a), .b(b), .sum(sum)    );

      initial begin

           // Initialize Inputs

           a = 0;

           b = 0;

           // Wait 100 ns for global reset to finish

           #10 a =1;

           #11 b =1;

           #11 a =0;

           #25 a = 1;

               // Add stimulus here

end

endmodule

结果如下:

图片

第一个always语句对应的图

 图片

第二个always语句对应的图

由图可知,在21ns时刻,b变化,此时a=1,b=1,sum=a&b=1,,而第一个图的结果在13ns后即34ns时刻的值却是0,第二个图则是1,。说明第一图是将34ns的计算结果值赋给了sum,而第二图是将21ns的sum计算结果延迟13ns到34ns时刻再赋给sum。因此,两种延迟结构的特点很显而易见了。

posted on 2011-11-09 17:49  zchdsp  阅读(1343)  评论(1)    收藏  举报

导航