Verilog学习笔记 Created by Chen Lekai 2019/12/29 
1 基本语法(可综合Part) 1.1 基本元件 1 2 3 4 5 6 7 input  x1,x2,x3;output  answer;and (answer,x1,x2,x3);or (answer,x1,~x2,x3);not (answer,x1);xor (answer, x1, x2);xnor (answer, x1, x2);
 
 
1.2 modeul 1 2 3 module  modeulname(var1,var2);endmodule 
 
modeulname的括号内应包含所有input和output变量。
1.3 assign 1 2 3 assign  g=(x1&x2)|(~x3&x4);assign  h=(~x1&x2)|(~x3&x4);assign  f=g&h;
 
主要用于简化表达式。
1.4 always 1 2 3 4 5 6 7 8 9 10 module  xxx(x1,x2,s,f);    input  x1,x2,s;     output  f;     reg  f;     always  @(x1 or  x2 or  s)         if  (s==0 )             f=x1;     	else              f=x2; endmodule 
 
always的@后称为敏感事件列表 ,只要其中的任何一个变量改变,便会执行语句块内的内容,可以简化为如下形式:
1 2 3 4 5 6 7 8 9 module  xxx(x1,x2,s,f);    input  x1,x2,s;     output  reg  f;     always  @(x1,x2,s)         if  (s==0 )             f=x1;     	else              f=x2; endmodule 
 
进一步简化:
1 2 3 4 5 6 7 8 9 module  xxx(x1,x2,s,f);    input  x1,x2,s;     output  reg  f;     always  @*         if  (s==0 )             f=x1;     	else              f=x2; endmodule 
 
使用always@*替代主要是为了防止敏感变量太多,防止漏写。@*包含一切可能改变结果变量的敏感变量,在本例中,x1、x2会改变f的值,因此属于敏感变量列表。s在判断语句中,也会改变结果,所以也属于敏感变量列表。
1 2 3 4 5 always  @(posedge  clk, negedge  clear)    if  (clear)          qout=0 ; 	else          qout=in; 
 
always也可以不加@,就相当于C++中的while(true)循环。如果加了就是有判断条件的while循环。
注意事项: 
不要在不同的always块内为同一个变量赋值。 
不要在同一个always块内同时使用阻塞赋值(=)和非阻塞赋值(<=)。 
使用always块描述组合逻辑时使用阻塞赋值(=),在使用always块描述时序逻辑时使用非阻塞赋值(<=)。简单理解就是,在电平敏感的always块内使用阻塞赋值,在边沿敏感的always块内使用非阻塞赋值。 
任何在always块内“被赋值的变量”都必须是寄存器型(reg)。即<=或=左边的信号,必须是reg型。 
always的敏感列表中可以同时包括多个电平敏感事件,也可以同时包括多个边沿敏感事件,但不能同时有电平和边沿敏感事件。另外,敏感列表中,同时包括一个信号的上升沿和它的下降沿敏感事件也是不允许的,因为这两个事件可以合并为一个电平事件。电平触发比时钟沿触发更容易受到干扰,在高速时容易受干扰,不够好(解决方法见:https://zhidao.baidu.com/question/1512023017981552420.html)。  
always不可嵌套使用。 
一个语句块内多个非阻塞赋值是并行执行的(顾名思义),而阻塞赋值则是顺序执行。 
 
1.5 reg&wire reg 使用过程赋值语句,wire 使用连续赋值语句,reg 和wire 在赋值时实质上表现相同,只不过reg 只能在always 和initial 中赋值,而wire 只能被assign 连续赋值。即一个直接赋值,另一个需要条件 。
输入端口可以用wire/reg 驱动(连信号),但是输入端口只能是wire ;输出端口只能驱动wire ,但可以是wire/reg 。端口相当于元件的引脚,信号相当于连线,即一种数值容器。取值为0,1,X(不确定值),Z(高阻)。
reg 相当于储存单元,可以储存上一次的值,wire 相当于物理连线,需要持续驱动。
1.6 数组 1.6.1 一维数组和向量 reg [7:0] count  表示一个八维向量。reg count [7:0] 表示一个八位的一维数组。reg [7:0] count [3:0] 表示一个含有四个八维向量的数组。
如果要读取/写入一个储存单元的数据,则跟C语言一样:
1 2 3 reg  [4 :0 ] count [8 :0 ];x1=count[1 ]; count[4 ]=x2; 
 
但如果只读取一位数字,则需要经过变量转换,不然会编译错误。
 
还可以通过文件初始化:
1 2 3 4 5 module   memory ();	reg  [7 :0 ] my_memory [0 :255 ]; 	initial          $readmemh ("memory.list" , my_memory); endmodule 
 
也可以用$readmemb函数,b代表二进制文件,h代表十六进制文件。$readmemh函数原型为$readmemh(“file_name”, mem_array, start_addr, stop_addr);
xxxx.list文件范例:
 
注意事项: 
//可以使用注释。 
@表示跳到新地址。 
 
只初始化一部分数组可以用两种方法,一种是用@跳转地址,另一种是设定start_addr和stop_addr。
若q为一维数组,&q表示q[0]*q[1]*q[2]*q[3]*…*q[N]。
1.6.2 二维数组 二维数组声明: 
1 reg  [4 :0 ] count [8 :0 ] [8 :0 ];
 
1.7 =和<= “=”为阻塞赋值 ,“<=”为非阻塞赋值 。遵循以下使用原则:
时序逻辑一定用非阻塞赋值<=,一旦看到always 敏感列表中有posedge(or negedge,下同) 就用<=。组合逻辑一定用阻塞赋值=,一旦没有posedge ,或者看到assign ,就用=。虽然语法上支持你在always 里用=,或者在assign里用<=,但这是一种非常愚蠢的做法。 
时序逻辑和组合逻辑分成不同模块,即一个always 模块内只能出现<=或=。 
 
1 2 3 4 5 6 7 8 9 module  non_block(a,b,c,clk);	output  reg  b,c; 	input  a,clk; 	always  @(posedge  clk)         begin              b<=a;             c<=b;         end  endmodule 
 
1.8 case 类似于C中的switch语句。
1 2 3 4 5 6 7 8 9 always  @(cs_state)    begin          case (cs_state)              2 ’b00: next_state = 2 ’b01;             2 ’b01: next_state = 2 ’b00;             2 ’b10: next_state = 2 ’b10;             default : next_state = 2 ’b00;         endcase      end  
 
只写了if没写else,或者case没有写全所有条件分支,编译器认为条件不满足时,会引进锁存器保存原值。 
时序逻辑可利用上述特性来保持状态。 
组合逻辑必须列出所有条件分支,否则会产生隐含锁存器。 
 
在设计电路时,一个很重要的原则是尽量少的出现锁存器  。锁存器具备下列三个缺点:
对毛刺敏感(使能信号有效时,输出状态可能随输入多次变化,产生空翻) ,不能异步复位,因此在上电后处于不确定的状态,这对于下一级电路是极其危险的。 
锁存器会使静态时序分析变得非常复杂,不具备可重用性。 (首先, 锁存器没有时钟参与信号传递,无法做 STA;其次,综合工具会将 latch 优化掉,造成前后仿真结果不一致) 
在 PLD  芯片中,基本的单元是由查找表和触发器组成的,若生成锁存器反而需要更多的资源。根据锁存器的特点可以看出,在电路设计中,要对锁存器特别谨慎,如果设计经过综合后产生出和设计意图不一致的锁存器,则将导致设计错误,包括仿真和综合。因此,在设计中需要避免产生意想不到的锁存器。 
 
1.9 function 1 2 3 4 5 6 7 8 9 function  [x:0 ] funcname;    input  a,b,cin;     reg  s,cout;     begin          s=a^b^cin;         cout=(A&B) | (A&CIN) | (B&CIN);         funcname={cout,s};     end  endfunction 
 
函数调用不可做为单独语句出现,必须是一个等式。
2 基本语法(不可综合Part) 2.1 initial 1 2 3 4 5 6 initial     begin          reg1=0 ;         for (addr=0 ;addr<size;addr=addr+1 )              memory[addr]=0 ;     end  
 
主要面向功能模拟,通常不具有可综合性,只用于testbench。 
模拟0时刻开始执行,只执行一次。 
同一模块含有多个initial模块,模拟开始时并行执行,initial模块内的语句顺序执行。 
initial不能嵌套使用。   
 
2.2 forever&repeat&while while用法和C语言类似。
forever可以用于仿真时生成时钟:
1 2 3 4 begin clk = 0 ; forever  #10  clk = !clk;end 
 
repeat语法:
1 2 3 4 repeat (次数):	begin  		... 	end  
 
2.3 task task主要用于调试,不可综合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 
 
task可以有多个或者没有输入(输出)端口,调用task时只能用输出端口传递“返回值”。task内可以出现时间控制语句,task执行可以占用非零时间单位,其执行可以由disable中断。
2.4 系统任务和系统函数 2.4.1 标准输出任务 $display和$write在仿真执行时输出,区别是$write不会自动换行。$strobe在仿真结束时进行输出,自动换行。$monitor在信号列表中发生变化时进行输出,自动换行。
1 2 3 $display ("The binary value of A is: %b" , A);$write ("The register values are: " , Reg1, Reg2, Reg3, "\n" );$monitor ("x=%b,y=%b" ,x,y);
 
2.4.2 文件管理任务 1 2 3 4 5 6 7 8 9 10 11 module  disp;    integer  handle1. handle2, handle3;     initial          begin              handle1=$fopen ("file1.dat" )             handle2=$fopen ("file2.dat" )             handle3=$fopen ("file3.dat" )                          $display ("%h %h %h" ,handle1, handle2, handle3);         end  endmodule 
 
$readmemb&$readmemh 
1 2 3 4 5 6 'timescale 10 ns/1 ns module  test;    reg  set;     reg  [15 :0 ] memory[0 :7 ] 
 
2.4.3 仿真控制任务 2.4.4 时间函数 2.4.5 时间显示函数 2.4.6 其他 2.5 testbench 2.5.1 #和‘ 1 2 'timescale 1 ns/1 ps  #200   
 
3 基本器件设计 3.1 选择器&分配器 3.1.1 MUX 2选1选择器
1 2 3 4 5 module  mux2to1 (w0, w1, s, f);    input  w0, w1, s;     output  f;     assign  f = s ? w0 : w1; endmodule 
 
4选1选择器
1 2 3 4 5 6 module  mux4to1 (w0, w1, w2, w3, s, f);    input  w0, w1, w2, w3;     input  [1 :0 ] s;     output  f;     assign  f = s[1 ] ? (s[0 ] ? w0 : w1) : (s[0 ] ? w2 : w3); endmodule 
 
16选1选择器
1 2 3 4 5 6 7 8 9 10 11 module  mux4to1 (w, s, f);    input  [15 :0 ]w     input  [3 :0 ]s;     output  f;     wire  [3 :0 ] m;     mux4to1 MUX0 (w[3 :0 ], s[1 :0 ], m[0 ]);     mux4to1 MUX1 (w[7 :4 ], s[1 :0 ], m[1 ]);     mux4to1 MUX2 (w[11 :8 ], s[1 :0 ], m[2 ]);     mux4to1 MUX3 (w[15 :12 ], s[1 :0 ], m[3 ]);     mux4to1 MUX4 (m[3 :0 ], s[3 :2 ], f); endmodule 
 
3.1.2 交叉开关(Mux应用) 1 2 3 4 5 6 7 8 9 10 module  cross_switch(in0, in1, sel0, sel1, out0, out1);	parameter  WIDTH = 16 ; 	input  [WIDTH:0 ] in0, in1; 	input  sel0, sel1; 	output  [WIDTH-1 :0 ] out0, out1; 	 	assign  out0 = sel0 ? in1 : in0; 	assign  out1 = sel1 ? in1 : in0; 		 endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 module  cross_switch(in0, in1, sel0, sel1, out0, out1);	parameter  WIDTH = 16 ; 	input  [WIDTH:0 ] in0, in1, in2, in3; 	input  [1 :0 ]sel0, sel1, sel2, sel3; 	output  [WIDTH-1 :0 ] out0, out1, out2, out3; 	 	assign  out0 = sel0[1 ] ? (sel0[0 ] ? out1 : out1) : (sel0[0 ] ? out2 : out3); 	assign  out1 = sel1[1 ] ? (sel1[0 ] ? out1 : out1) : (sel1[0 ] ? out2 : out3); 	assign  out1 = sel2[1 ] ? (sel2[0 ] ? out1 : out1) : (sel2[0 ] ? out2 : out3); 	assign  out1 = sel3[1 ] ? (sel3[0 ] ? out1 : out1) : (sel3[0 ] ? out2 : out3); 		 endmodule 
 
3.1.3 DEMUX 1to4数据分配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  dmux (y0, y1, y2, y3, din, sel);    output  y0, y1, y2, y3;     input [1 :0 ] sel;     input  din;     reg  y0, y1, y2, y3;     always  @ (din, sel) begin          y0 = 0 ; y1 = 0 ; y2 = 0 ; y3 = 0 ;         case (sel[1 :0 ])             2 ’b00: y0 = din;             2 ’b01: y1 = din;             2 ’b10: y2 = din;             2 ’b11: y3 = din;             default :;         endcase      end  endmodule 
 
3.2 编码器&解码器 3.2.1 普通编码器 4to2编码器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module  encoder4_2(q, d);    input [3 :0 ] d;     output [1 :0 ] q;     reg [1 :0 ] q;     always @(d) begin          case (d)             4'b0001 : q<=2'b11 ;             4'b0010 : q<=2'b10 ;             4'b0100 : q<=2'b01 ;             4'b1000 : q<=2'b00 ;             default : q<=2'bxx ;         endcase      end  endmodule 
 
8to3编码器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module  encoder8_3(i, y);    input [7 :0 ] i;     output [2 :0 ] y;     reg [2 :0 ] y;     always  @ (i) begin          case (i[7 :0 ])             8 ’b00000001: y[2 :0 ] = 3'b000 ;             8 ’b00000010: y[2 :0 ] = 3'b001 ;             8 ’b00000100: y[2 :0 ] = 3'b010 ;             8 ’b00001000: y[2 :0 ] = 3'b011 ;             8 ’b00010000: y[2 :0 ] = 3'b100 ;             8 ’b00100000: y[2 :0 ] = 3'b101 ;             8 ’b01000000: y[2 :0 ] = 3'b110 ;             8 ’b10000000: y[2 :0 ] = 3'b111 ;             default : y[2 :0 ] = 3'bxxx ;         endcase      end  endmodule 
 
3.2.2 优先编码器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  priority_encoder(w, y, z);    input  [3 :0 ]w;     output  reg  [1 :0 ] Y;     output  reg  z;          always  @ (w) begin          z = 1 ;         casex (w)             4'b1xxx : y = 3 ;             4'b01xx : y = 2 ;             4'b001x : y = 1 ;             4'b0001 : y = 0 ;             default : begin  z = 0 ; y = 2'bxx ; end          endcase      end  endmodule 
 
PS:
也可以通过if-else嵌套实现,但是那样代码比较繁琐,二者生成的电路几乎是一样的(vivado综合时内部有优化) 
关于casex和casez使用哪个的问题,网上的普遍论调是casez更加常用,但具体哪个效果更好没有定论,有待深究,可以确定的是二者真值表是不一样的。 
 
3.3 ALU74381 ALU(算术逻辑单元)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module  ALU(s, a, b, f);    input  [2 :0 ] s;     input  [3 :0 ] a, b;     output  reg  [3 :0 ] f;     always  @ (s, a, b) begin          case (s)             0 : f = 4'b0000 ;             1 : f = b - a;             2 : f = a - b;             3 : f = a + b;             4 : f = a ^ b;             5 : f = a | b;             6 : f = a & b;             7 : f = 4'b1111 ;             default : f = 4 '0000 ;         endcase      end  endmodule 
 
3.4 数值比较器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  comparator (y1, y2, y3, a, b);    output  y1, y2, y3;     input [3 :0 ] a, b;     reg  y1, y2, y3;     always  @ (a, b) begin          if (a > b) begin              y1 = 1 ; y2 = 0 ; y3 = 0 ;         end          else  if (a == b) begin              y1 = 0 ; y2 = 1 ; y3 = 0 ;         end          else  begin              y1 = 0 ; y2 = 0 ; y3 = 1 ;         end      end  endmodule 
 
带reset的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 module  comp(CLK,RST,A,B,AGTB,ALTB,AEQB);    input  CLK,RST;     input [1 :0 ] A,B;     output  AGTB,ALTB,AEQB;     reg  AGTB,ALTB,AEQB;     always  @(posedge  CLK or  negedge  RST) begin          if (!RST) begin              AGTB<=0 ;             AEQB<=0 ;             ALTB<=0 ;         end          else  begin              if (A>B) begin                  AGTB<=1 ;                 AEQB<=0 ;                 ALTB<=0 ;         	end              else  if (A==B) begin                  AGTB<=0 ;                 AEQB<=1 ;                 ALTB<=0 ;             end              else  begin                  AGTB<=0 ;                 AEQB<=0 ;                 ALTB<=1 ;             end          end      end  endmodule 
 
3.5 加法器 3.5.1 半加器 若不考虑有来自低位的进位,将两个1 位二进制数相加,称为半加。实现半加运算的电路叫做半加器。
1 2 3 4 5 6 7 8 module  half_add1(input  a,b,output  cout,sum);     xor (sum,a,b);     and (cout,a,b); endmodule 
 
1 2 3 4 5 6 7 module  half_add (sum, cout, a, b);   output  sum, cout;    input  a, b;    assign  cout = a & b;    assign  sum = a ^ b; endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 module  half_add (sum, cout, a, b);   output  sum, cout;    input  a, b;    reg  sum,cout;   always  @  (a, b)     begin        sum = a ^ b;      cout = a & b;     end  endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module  half_add (sum, cout, a, b);   output  sum, cout;    input  a, b;     reg [2 :0 ] sum,cout;     always  @  (a, b)      begin        case [{a, b}]         2 ’b00:  begin  cout = 0 , sum = 0 ;end          2 ’b01:  begin  cout = 0 , sum = 1 ;end          2 ’b10:  begin  cout = 0 , sum = 1 ;end          2 ’b11:  begin  cout = 1 , sum = 0 ;end        endcase       end  endmodule 
 
3.5.2 全加器 全加器其实就是考虑到进位的加法器。
1 2 3 4 5 6 7 module  ful_adder(cout,sum,a,b,cin);    input  a,b;     input  cin;     output  reg  sum, cout;     always  @(a or  b or  cin)     {cout, sum} = a+b+cin; endmodule 
 
1 2 3 4 5 6 7 8 module  add1 (cin, sum, cout, a, b);   output  sum, cout;    input  a, b;    assign  cout = (a & b)|(a & cin)|(cin & b);    assign  sum = a ^ b ^ cin; endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 module  add1 (cin, sum, cout, a, b);   output  sum, cout;    input  a, b, cin;    reg  sum,cout;   always  @  (a, b, cin)     begin        sum = (a ^ b) ^ c;      cout = (a & b)|(a & cin)|(cin & b);     end  endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  add1 (cin, sum, cout, a, b);   output  sum, cout;    input  a, b;    reg  cout, m1, m2, m3;    wire  s1;   xor   (s1, a, b);  always  @ (a, b, cin)    begin       m1 = a & b;      m2 = a & cin;      m3 = cin & b;      cout = (m1 | m2) | m3;    end    assign  sum = s1 ^ cin; endmodule 
 
3.5.3 多位加法器 1 2 3 4 5 6 7 8 9 module  full_add (cin, sum, cout, a, b);   parameter  add_size = 4 ;    output  cout;    output [add_size -1 :0 ] sum;    input [add_size -1 :0 ] a, b;    input  cin;    assign  {cout, sum} = a + b + cin; endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 module  add4 (cin, sum, cout, a, b);    output [3 :0 ] sum;     output  cout;     input [3 :0 ] a, b;     input  cin;     wire  c1, c2, c3;     add1 f0(cin, sum[0 ], c1, a[0 ], b[0 ]);     add1 f1(c1, sum[0 ], c2, a[1 ], b[1 ]);     add1 f2(c2, sum[0 ], c3, a[2 ], b[2 ]);     add1 f3(c3, sum[0 ], cout, a[3 ], b[3 ]); endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 module  add4 (cin, sum, cout, a, b);   output [3 :0 ] sum;    output  cout;    input [3 :0 ] a, b;    input  cin;    reg  cout;    reg [3 :0 ] sum;   always  @  (*)     begin        {cout, sum} = a + b + cin;     end  endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 module  fulladd4(sum, c_out, a, b, cin);  	output  [3 :0 ] sum;   	output  c_out;   	input  [3 :0 ] a, b;   	input  cin;   	wire  p0, g0, p1, g1, p2, g2, p3, g3;   	wire  c4, c3, c2, c1;     assign  p0 = a[0 ] ^ b[0 ],         p1 = a[1 ] ^ b[1 ],         p2 = a[2 ] ^ b[2 ],         p3 = a[3 ] ^ b[3 ];     assign  g0 = a[0 ] & b[0 ],         g1 = a[0 ] & b[1 ],         g2 = a[0 ] & b[2 ],         g3 = a[0 ] & b[3 ];   assign  c1 = g0 | (p0 & cin),        c2 = g1 | (p1 & c1),       c3 = g2 | (p2 & c2),       c4 = g3 | (p3 & c2);     assign  sum[0 ] = p0 ^ cin,         sum[1 ] = p1 ^ c1,         sum[2 ] = p2 ^ c2,         sum[3 ] = p3 ^ c3;   assign  cout = c4; endmodule 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 module  adder8pip(cout,sum,cin,ina,inb,clk);    input  cin,clk;     input  [7 :0 ] ina,inb;     output  reg  cout;     output  reg  [7 :0 ] sum;     reg  firstco,secondco,thirdco;      reg  [1 :0 ] firstsum,thirdina,thirdinb;     reg  [3 :0 ] secondsum,secondina,secondinb;     reg  [5 :0 ] thirdsum,firstina,firstinb;     always  @ (posedge  clk) begin  		{firstco,firstsum} <= ina[1 :0 ]+inb[1 :0 ]+cin; 		firstina <= ina[7 :2 ];firstinb <= inb[7 :2 ];     end      always  @ (posedge  clk) begin  		secondsum[1 :0 ] <= firstsum; 		{secondco,secondsum[3 :2 ]} <= firstina[1 :0 ]+firstinb[1 :0 ]+firstco; 		secondina <= firstina[5 :2 ];secondinb <= firstinb[5 :2 ];     end      always  @ (posedge  clk) begin  		thirdsum[3 :0 ] <= secondsum; 		{thirdco,thirdsum[5 :4 ]} <= secondina[1 :0 ]+secondinb[1 :0 ]+secondco; 		thirdina <= secondina[3 :2 ];thirdinb <= secondinb[3 :2 ];     end      always  @ (posedge  clk) begin  		sum[5 :0 ] <= thirdsum; 		{cout,sum[7 :6 ]} <= thirdina[1 :0 ]+thirdinb[1 :0 ]+thirdco;     end  endmodule 
 
3.6 减法器 3.6.1 半减器 1 2 3 4 5 6 7 8 9 10 11 12 13 module  half_sub (dout, cout, a, b);  output  dout, cout;    input  a, b;    reg  dout, cout;   always  @  (a, b)     begin        dout = a ^ b;      cout = (~a) & b;     end  endmodule 
 
3.6.2 全减器 1 2 3 4 5 6 7 8 9 10 module  sub1 (cin, dout, cout, a, b);  output  dout, cout;    input  a, b;    reg  dout, cout;   always  @  (a, b)     begin          {cout, dout} = a – b – cin;     end  endmodule 
 
3.6.3 多位减法器 1 2 3 4 5 6 7 8 9 10 11 12 13 module  sub4 (cin, dout, cout, a, b);  output [3 :0 ] dout;    output  cout;    input [3 :0 ] a, b;    input  cin;    reg [3 ;0 ] dout,     reg  cout;   always  @  (a, b)     begin          {cout, dout} = a – b – cin;     end  endmodule 
 
3.7 锁存器 锁存器(latch):对脉冲电平敏感,在时钟脉冲的电平作用下改变状态  
锁存器是电平触发的存储单元,数据存储的动作取决于输入时钟(或者使能)信号的电平值,当锁存器处于使能状态时,输出才会随着数据输入发生变化。(简单地说,它有两个输入,分别是一个有效信号EN,一个输入数据信号DATA_IN,它有一个输出Q,它的功能就是在EN有效的时候把DATA_IN的值传给Q,也就是锁存的过程)。
不用锁存器的原因:
锁存器容易产生毛刺。 
锁存器在ASIC设计中应该说比FF要简单,消耗的资源少,但是在FPGA的资源中,大部分器件没有锁存器这个东西,所以需要用一个逻辑门和FF来组成锁存器,这样就浪费了资源。 
 
3.8 触发器 触发器(Flip-Flop,简写为 FF):对脉冲边沿敏感,其状态只在时钟脉冲的上升沿或下降沿的瞬间改变 
触发器也叫双稳态门,又称双稳态触发器,是一种可以在两种状态下运行的数字逻辑电路。触发器一直保持它们的状态,直到它们收到输入脉冲,又称为触发。当收到输入脉冲时,触发器输出就会根据规则改变状态,然后保持这种状态直到收到另一个触发。
3.8.1 D触发器 基本D触发器
1 2 3 4 5 6 7 8 module  async_rddf(clk, d,q,qb);    input  clk, d;     output  reg  q,qb;     always  @(posedge  clk) begin          q<=d;         qb<=~d;     end  endmodule 
 
同步复位的 D 触发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 module  sync_rddf(clk,reset,d,q,qb);    input  clk,reset,d;     output  reg  q,qb;     always  @(posedge  clk) begin          if (!reset) begin              q<=0 ;             qb<=1 ;         end          else  begin              q<=d;             qb<=~d;         end      end  endmodule 
 
异步复位的 D 触发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module  async_rddf(clk,reset,d,q,qb);    input  clk,reset,d;     output  q,qb;     reg  q,qb;     always  @(posedge  clk or  negedge  reset) begin          if (!reset) begin              q<=0 ;             qb<=1 ;         end          else  begin              q<=d;             qb<=~d;         end      end  endmodule 
 
同步置位/复位的 D 触发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 module  sync_rsddf(clk,reset,set,d,q,qb);    input  clk,reset,set;     input  d;     output  q,qb;     reg  q,qb;     always  @(posedge  clk) begin          if (!set && reset) begin              q<=1 ;             qb<=0 ;         end          else  if (set && !reset) begin              q<=0 ;             qb<=1 ;         end          else  begin              q<=d;             qb<=~d;         end      end  endmodule 
 
异步置位/复位的 D 触发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 module  async_rsddf(clk,reset,set,d,q,qb);    input  clk,reset,set;     input  d;     output  q,qb;     reg  q,qb;     always  @(posedge  clk or  negedge  set or  negedge  reset) begin          if (!set && reset) begin              q<=1 ;             qb<=0 ;         end          else  if (set && !reset) begin              q<=0 ;             qb<=1 ;         end          else  begin              q<=d;             qb<=~d;         end      end  endmodule 
 
3.8.2 JK触发器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 module  syn_jk_ff(clk,                rst_n,                 set_n,                 j,                 k,                 q                      );     input  clk;     input  rst_n;     input  set_n;     input  j;     input  k;     output  reg  q;     always @(posedge  clk) begin      if (!rst_n) q <= 1'b0 ;              else  if (!set_n) q <= 1'b1 ;         else  begin          case ({j,k})              2'b00  : q <= q;             2'b01  : q <= 0 ;             2'b10  : q <= 1 ;             default  : q <= ~q;         endcase      	end      end  endmodule 
 
异步JK触发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 module  asyn_jk_ff(clk,                rst_n,                 set_n,                 j,                 k,                 q                      );     input  clk;     input  rst_n;     input  set_n;     input  j;     input  k;     output  reg  q;     always @(posedge  clk or  negedge  rst_n or  negedge  set_n) begin          if (!rst_n) q <= 1'b0 ;                 else  if (!set_n) q <= 1'b1 ;            else  begin  			case ({j,k}) 				2'b00  : q <= q; 				2'b01  : q <= 0 ; 				2'b10  : q <= 1 ; 				default  : q <= ~q; 			endcase          end      end  endmodule 
 
3.8.3 T触发器 T触发器,带有同步复位信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  syn_t_trigger(clk,t,rst,q);	input  clk, t, rst; 	output  reg  q, qn; 	 	always @(posedge  clk) begin  		if (!rst)               			q <= 0 ; 			qn <= 0 ; 		else  if (t == 1 )   			q <= ~q; 			qn <= ~qn; 		else                  			q <= q; 			qn <= qn; 	end  endmodule 
 
T触发器,带有异步复位信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module  asyn_t_trigger(clk,t,rst,q);	input  clk, t, rst; 	output  reg  q, qn; 	 	always @(posedge  clk or  negedge  rst) begin  		if (!rst)               			q <= 0 ; 			qn <= 0 ; 		else  if (t == 1 )   			q <= ~q; 			qn <= ~qn; 		else                  			q <= q; 			qn <= qn; 	end  endmodule 
 
3.8.4 SR触发器 1 2 3 4 5 6 7 8 9 10 11 12 module  SY_RS_FF ( R, S, CLK, Q, QB );   input  R, S, CLK;            output  Q, QB;             reg  Q;                 assign  QB = ~Q;            always  @( posedge  CLK )            case  ({ R, S })              1 : Q <= 1 ;               2 : Q <= 0 ;               3 : Q <= 1'bx ;            endcase               endmodule               
 
带有R端D触发器
1 2 3 4 5 6 7 8 9 10 module  R_SY_D_FF ( RB, D, CLK, Q, QB );    input  RB, D, CLK;              output  Q, QB ;               reg  Q;                   assign  QB = ~Q;                always  @( posedge  CLK or  negedge  RB )                           Q <= ( !RB )? 0 : D;            endmodule                   
 
3.9 寄存器 通常由触发器构成寄存器,把多个D触发器的时钟端连接起来就可以构成一个存储多位二进制代码的寄存器。
3.9.1 普通寄存器 1 2 3 4 module  Register (input  [3 :0 ] D, input  Clk, output  reg  [3 :0 ] Q);	always  @(posedge  Clk) 		Q <= D; endmodule 
 
同步清零
1 2 3 4 5 6 7 8 9 10 11 module  reg8 (reset, CLK, D, Q);    input  reset;     input  CLK;     input  [7 :0 ] D;     output  reg  [7 :0 ] Q;     always  @(posedge  CLK)         if  (reset)         	Q <= 0 ;         else          	Q <= D; endmodule  
 
异步清零
1 2 3 4 5 6 7 8 9 10 11 12 13 module  regN (reset, CLK, D, Q);    input  reset;     input  CLK;     parameter  N = 8 ;      input  [N-1 :0 ] D;     output  [N-1 :0 ] Q;     reg  [N-1 :0 ] Q;     always  @(posedge  CLK or  posedge  reset)         if  (reset)         	Q <= 0 ;         else  if  (CLK == 1 )         	Q <= D; endmodule  
 
3.9.2 移位寄存器 右移
1 2 3 4 5 6 7 8 9 10 11 module  register_right(clk, din, dout);  	input  clk; 	input  din; 	reg  [15 :0 ] d;     output  dout;          always  @(posedge  clk) begin  		d <= {din, d[15 :1 ]};       	dout <= d[0 ];     end  endmodule 
 
左移
1 2 3 4 5 6 7 8 9 module  register_left(clk, din, dout);      input  clk;       input  din;       output  reg  [15 :0 ] dout; 	     always  @(posedge  clk)               dout <= {din[14 :0 ], din};     endmodule 
 
串行输入并行输出寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 module  left_shifter_reg(clk, din, dout);  	input  clk;   	input  din;   	output  [7 :0 ] dout;   	wire  [7 :0 ] dout; 	reg  [7 :0 ] qtemp; 	always @(posedge  clk)   		qtemp <= {qtemp[6 :0 ], din};  	assign  dout = qtemp;  endmodule 
 
并行输入串行输出移位寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module  right_shifter_reg(clk, en, din, dout);	input  [7 :0 ] din; 	input  en,clk;     output  reg  dout; 	reg  [7 :0 ] qtemp; 	 	always  @(posedge  clk)   		if (en == 1 ) 			qtemp <= din; 		else  begin  			dout <= qtemp[0 ]; 			qtemp <= {qtemp[0 ], qtemp[7 :1 ]}; 		end   endmodule  
 
并行输入并行输出移位寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 module  right_shifter_reg(clk, en, din, dout);	input  [7 :0 ] din; 	input  en,clk;     output  reg  [7 :0 ] dout; 	reg  [7 :0 ] qtemp; 	 	always  @(posedge  clk) 		if  (en == 1 ) begin  			dout <= 0 b'zzzzzzzz; 			qtemp <= din; 		end  		else  begin  			dout <= qtemp; 			qtemp <= 0 b'xxxxxxxx; 		end   endmodule  
 
4 中级器件设计 4.1 计数器 计数器的逻辑功能是用与记忆时钟脉冲的具体个数,通常计数器最多能记忆时钟的最大数目m 称为计数器的模 。基本原理是将几个触发器按照一定的顺序连接起来,然后根据触发器的组合状态,按照一定的技术规律随着时钟脉冲变化来记忆时钟脉冲的个数。
按照计数方向分为加法,减法和可逆计数器 。
按照其中触发器是否与时钟同步又分为同步计数器 和异步计数器 。
4.1.1 同步计数器 同步4位计数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module  cnt16 (cout, q, clk, clr, load, en, d);  output [3 :0 ] q;    output  cout;     input  clk, clr, load, en;    input [3 :0 ] d;    reg [3 :0 ] q;    reg  cout;    always  @  (posedge  clk)  begin      if  (clr) begin  q <= 0 ; end      else  if  (load) begin  q <= d; end              else  if  (en) begin   	             q <= q + 1 ; 	             if (q == 4 ’b1111) begin  cout <= 1 ; end  	             else  begin   cout <= 0 ; end                        end                      else  begin  q <= q; end          end  endmodule 
 
同步24进制计数器,就套了一个十进制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 module  cnt24 (ten, one, cout, clk, clr);	output [3 :0 ] ten, one;  	output  cout;  	input  clk, clr; 	reg [3 ;0 ] ten, one; 	reg  cout;  	always  @  (posedge  clk) begin  		if  (clr) begin   			ten <= 0 ; 			one <= 0 ;  		end  		else  begin    			if ({ten, one}) == 8 ’b0010_0011) begin   				ten <= 0 ;  				one <= 0 ;  				cout <= 1 ;  			end  			else  if (one==4 ’b1001) begin    				one <= 0 ;  				ten<=ten+1 ; 				cout <= 0 ;  			end    			else  begin   				one <= one + 1 ;  				cout <=0 ; 			end          end  	end  endmodule 
 
模为60的BCD码加法计数器。可以去复习一下BCD码,BCD码就是用四位二进制数表示一个十进制数,但是每一位代表的权值不同,最常见的是8421BCD码,也就是普通的二进制数,下面这个也就是套了一个BCD码形式的计数器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 module  count60(qout, cout,      data, load, cin, reset, clk); 	output  [7 :0 ] qout; 	output  cout; 	input  [7 :0 ] data; 	input  load, cin, clk, reset; 	reg  [7 :0 ] qout;     always  @ (posedge  clk) begin  		if  (reset) qout <= 0 ; 		else  if  (load) qout <= data; 		else  if  (cin) begin  			if (qout[3 :0 ] == 9 ) begin  				qout[3 :0 ] <= 0 ; 				if (qout[7 :4 ] == 5 ) qout[7 :4 ] <= 0 ; 				else  qout[7 :4 ] <= qout[7 :4 ]+1 ; 			end  			else  qout[3 :0 ] <= qout[3 :0 ]+1 ; 		end  	end  	assign  cout = ((qout == 8 ’h59)&cin)?1 :0 ; endmodule 
 
4.1.2 异步计数器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 module  asyn_cnt4(q, clk, rst);	output [3 :0 ] q; 	input  clk, rst; 	reg [3 :0 ] q; 	reg [3 :0 ] qn; 	always  @ (posedge  clk) begin   		if (!rst) begin  q[0 ] = 0 ; qn[0 ] = 1 ; end  		else  begin   q[0 ] = ~q[0 ]; qn[0 ] =~q[0 ]; end  	end  	 	always  @ (posedge  qn[0 ]) begin   		if (!rst) begin  q[1 ] = 0 ; qn[1 ] = 1 ; end  		else  begin  q[1 ] = ~q[1 ]; qn[1 ] =~q[1 ]; end   	end  	 	always  @ (posedge  qn[1 ]) begin   		if (!rst) begin  q[2 ] = 0 ; qn[2 ] = 1 ; end  		else  begin  q[2 ] = ~q[2 ]; qn[2 ] =~q[2 ]; end  	end  	 	always  @ (posedge  qn[2 ]) begin   		if (!rst) begin  q[3 ] = 0 ; qn[3 ] = 1 ; end  		else  begin  q[3 ] = ~q[3 ]; qn[3 ] =~q[3 ]; end  	end  endmodule 
 
4.1.3 可变模计数器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 module  cnt(q, clk, clr, load, m);	output [6 :0 ] q; 	input  clk, clr, load; 	input [6 :0 ] m; 	reg [6 :0 ] q; 	reg [6 :0 ] md; 	always  @ (posedge  clk) begin      		md <= m-1 ; 		if (!clr) q <= 0 ;         else  begin   			if (load) q  <=  md; 			else   begin  if (q == md) q <= 0 ; 			else   q <= q + 1 ;  			end  		end  	end  endmodule 
 
4.2 分频器 4.2.1 偶分频器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 module  clk_div #(  parameter  WIDTH = 3 , parameter  N = 6 ) (clk,reset, clk_out);   	input  clk; 	input  reset; 	output  clk_out; 	 	reg  [WIDTH-1 :0 ] r_reg; 	always  @(posedge  clk or  posedge  reset)begin  		if  (reset) begin  			r_reg <= 0 ; 			clk_out <= 1'b0 ; 		end  		else  if  (r_nxt == N) begin  			r_reg <= 0 ; 			clk_out <= ~clk_out; 		end  		else   			r_reg <= r_reg + 1 ; 	end  endmodule 
 
4.2.2 奇分频器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 module  clk_divn #(parameter  WIDTH = 3 ,parameter  N = 5 )(clk,reset, clk_out);   	input  clk; 	input  reset; 	output  clk_out; 	reg  [WIDTH-1 :0 ] pos_count, neg_count; 	always  @(posedge  clk) 		if  (reset) pos_count <=0 ; 		else  if  (pos_count ==N-1 ) pos_count <= 0 ; 		else  pos_count<= pos_count +1 ; 	always  @(negedge  clk) 		if  (reset) neg_count <=0 ; 		else   if  (neg_count ==N-1 ) neg_count <= 0 ; 		else  neg_count<= neg_count +1 ;  	assign  clk_out = ((pos_count > (N>>1 )) | (neg_count > (N>>1 )));  endmodule 
 
4.3 RAM 4.3.1 单端口RAM 带同步和异步两种功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 module  SinglePortRAM(	clk         ,      address     ,      data_in        ,      cs          ,      we          ,      oe            ,     data             );     input  clk, cs, we, oe, address;     input  [7 :0 ] data_in;           output  [7 :0 ]  data;           reg  [7 :0 ] data_temp_0;     reg  [7 :0 ] data_temp_1;     reg  [7 :0 ] mem [0 :1 ];          assign  data = (cs) ? ((oe && !we) ? data_temp_1 : 8'bzzzzzzzz ) : ((oe && !we) ? data_temp_0 : 8'bzzzzzzzz );          always  @ (posedge  clk) begin          if  (we)             mem[address] <= data_in;         else              mem[address] <= mem[address];     end  	 	 	always  @ (posedge  clk) begin          if  (!cs && !we && oe)             data_temp_0 <= mem[address]; 		else  			data_temp_0 <= data_temp_0; 	end           always  @ (address or  cs or  we or  oe) begin          if  (cs && !we && oe)             data_temp_1 = mem[address];         else              data_temp_1 = data_temp_1;     end  endmodule 
 
4.3.2 双端口RAM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 module  double_port_RAM(    input  clk,     input  [7 :0 ]address_0, 	inout  [7 :0 ]data_0,     input  cs_0,     input  we_0,     input  oe_0,     input  [7 :0 ]address_1, 	inout  [7 :0 ]data_1,     input  cs_1,     input  we_1,     input  oe_1 ); 	 	reg  [7 :0 ] data_temp_0; 	reg  [7 :0 ] data_temp_1; 	reg  [7 :0 ] adata_temp_0; 	reg  [7 :0 ] adata_temp_1; 	reg  [7 :0 ] mem[0 : 1  << 8  - 1 ]; 	assign  data_0 = (cs_0) ? ((oe_0 && !we_0) ? adata_temp_0 : 8'bzzzzzzzz ) : ((oe_0 && !we_0) ? data_temp_0 : 8'bzzzzzzzz ); 	assign  data_1 = (cs_1) ? ((oe_1 && !we_1) ? adata_temp_1 : 8'bzzzzzzzz ) : ((oe_1 && !we_1) ? data_temp_1 : 8'bzzzzzzzz ); 	always  @(posedge  clk) begin  		if (we_0) mem[address_0] <= data_0; 		else  if (we_1) mem[address_1] <= data_1; 	end  	 	always  @(posedge  clk)begin  		if  (!cs_0 && !we_0 && oe_0) data_temp_0 <= mem[address_0]; 		else  data_temp_0 <= 0 ; 	end  	always  @(posedge  clk)begin  		if  (!cs_1 && !we_1 && oe_1) data_temp_1 <= mem[address_1]; 		else  data_temp_1 <= 0 ; 	end  	 	always  @(address_0 or  cs_0 or  we_0 or  oe_0)begin  		if  (cs_0 && !we_0 && oe_0) adata_temp_0 = mem[address_0]; 		else  adata_temp_0 = 0 ; 	end  	always  @(address_1 or  cs_1 or  we_1 or  oe_1)begin  		if  (cs_1 && !we_1 && oe_1) adata_temp_1 = mem[address_1]; 		else  adata_temp_1 = 0 ; 	end  endmodule 
 
4.3.3 FIFO FIFO(First In First Out)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 module  double_port_RAM(    input  clk,     input  [7 :0 ]address_0, 	inout  [7 :0 ]data_0,     input  cs_0,     input  we_0,     input  oe_0,     input  [7 :0 ]address_1, 	inout  [7 :0 ]data_1,     input  cs_1,     input  we_1,     input  oe_1 ); 	 	reg  [7 :0 ] data_temp_0; 	reg  [7 :0 ] data_temp_1; 	reg  [7 :0 ] adata_temp_0; 	reg  [7 :0 ] adata_temp_1; 	reg  [7 :0 ] mem[0 : 1  << 8  - 1 ]; 	assign  data_0 = (cs_0) ? ((oe_0 && !we_0) ? adata_temp_0 : 8'bzzzzzzzz ) : ((oe_0 && !we_0) ? data_temp_0 : 8'bzzzzzzzz ); 	assign  data_1 = (cs_1) ? ((oe_1 && !we_1) ? adata_temp_1 : 8'bzzzzzzzz ) : ((oe_1 && !we_1) ? data_temp_1 : 8'bzzzzzzzz ); 	always  @(posedge  clk) begin  		if (we_0) mem[address_0] <= data_0; 		else  if (we_1) mem[address_1] <= data_1; 	end  	 	always  @(posedge  clk)begin  		if  (!cs_0 && !we_0 && oe_0) data_temp_0 <= mem[address_0]; 		else  data_temp_0 <= 0 ; 	end  	always  @(posedge  clk)begin  		if  (!cs_1 && !we_1 && oe_1) data_temp_1 <= mem[address_1]; 		else  data_temp_1 <= 0 ; 	end  	 	always  @(address_0 or  cs_0 or  we_0 or  oe_0)begin  		if  (cs_0 && !we_0 && oe_0) adata_temp_0 = mem[address_0]; 		else  adata_temp_0 = 0 ; 	end  	always  @(address_1 or  cs_1 or  we_1 or  oe_1)begin  		if  (cs_1 && !we_1 && oe_1) adata_temp_1 = mem[address_1]; 		else  adata_temp_1 = 0 ; 	end  endmodule 
 
4.4 有限状态机 4.4.1 Moore型&Mealy型 
Moore型状态机输出仅和状态机的当前状态有关,与外部输入无关。Mealy型状态机的输出与当前状态和输入都有关。 
体现在状态转移图上就是,Moore机的输出在状态圆圈内,Mealy机的输出在转移曲线上。 
Moore完全描述状态转移图会比Mealy机多一个状态。 
体现在verilog代码中就是,Moore机的最后输出逻辑只判断state,Mealy机的输出逻辑中判断。 
 
4.4.2 有限状态机的设计 
分析设计要求,列出全部可能状态 
画出状态转移图 
用HDL语言描述状态机 
 
4.4.3 有限状态机的verilog设计流程 以10010序列检测机为例。
定义输入输出端口,并用parameter 类型定义各个状态。
1 2 3 4 5 6 7 8 9 10 11 12 module  moorefsm(clk,rst,a,z);     input    clk,rst;      input    a;      output   z;      reg      z;      reg  [3 :0 ]  currentstate, nextstate;      parameter  S0 = 4'b0000 ;      parameter  S1 = 4'b0001 ;      parameter  S2 = 4'b0010 ;      parameter  S3 = 4'b0011 ;      parameter  S4 = 4'b0100 ;      parameter  S5 = 4'b0101 ; 
 
 
用两个reg 变量定义当前状态state和下一个状态state_next,然后用单独的一个always语句块完成状态转移,使用时序逻辑驱动。
1 2 3 4 5 6 always @(posedge  clk or  negedge  rst) begin        if (!rst)            currentstate <= S0;        else  		currentstate <= nextstate;    end  
 
 
用单独一个always语句块完成下一个状态state_next的确定,使用组合逻辑驱动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 always @(currentstate or  a or  rst) begin 	if (!rst) 		nextstate = S0; 	else  	   case (currentstate) 			S0: nextstate = (a==1 )?S1:S0;   			S1: nextstate = (a==0 )?S2:S1; 			S2: nextstate = (a==0 )?S3:S1;     			S3: nextstate = (a==1 )?S4:S0; 			S4: nextstate = (a==0 )?S5:S1;    			S5: nextstate = (a==0 )?S3:S1; 			default : nextstate = S0; 		endcase  end 
 
 
Moore型状态机用当前状态确定输出,Mealy型状态机用输入和当前状态确定输出,使用组合逻辑驱动输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 	always @(rst or  currentstate) begin  		if (!rst) 			z = 0 ; 		else  			case (currentstate) 				S0: z = 0 ; 				S1: z = 0 ; 				S2: z = 0 ; 				S3: z = 0 ; 				S4: z = 0 ; 				S5: z = 1 ; 				default : z = 0 ; 			endcase   	end  endmodule 
 
 
 
若用Mealy型实现,则代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 module  mealyfsm(clk, rst, a, z);	input         clk; 	input         rst; 	input           a; 	output   reg  z; 	reg  [3 :0 ]  state, state_next; 	 	parameter  S0 = 4'b0000 ; 	parameter  S1 = 4'b0001 ; 	parameter  S2 = 4'b0010 ; 	parameter  S3 = 4'b0011 ; 	parameter  S4 = 4'b0100 ; 	 	always @(posedge  clk or  negedge  rst) begin  		if  (!rst) 			state <= S0; 		else  			state <= state_next;     end  	 	always @(state or  a or  rst) begin  		if (!rst) 			state_next = S0; 		else   			case (state) 				S0: state_next = (a == 1 )? S1 : S0; 				S1: state_next = (a == 0 )? S2 : S1; 				S2: state_next = (a == 0 )? S3 : S1; 				S3: state_next = (a == 1 )? S4 : S0; 				S4: state_next = (a == 0 )? S2 : S0; 				default :state_next = S0; 			endcase  	end  	 	always @(a or  rst or  state or  state_next) begin  		if (!rst) 			z = 0 ; 		else  			case (state) 				S0: z = 0 ; 				S1: z = 0 ; 				S2: z = 0 ; 				S3: z = 0 ; 				S4: z = a == 0  ? 1  : 0 ; 				default :z = 0 ; 			endcase  	end  endmodule 
 
  5 分析与设计 5.1 组合逻辑分析 5.1.1 分析步骤 
分别用符号标注各级门的输出 
从输入端到输出端逐级写出输入变量到输出变量的逻辑表达式,最后得到输入变量表示的输出函数表达式。需要时用卡诺图或公式化简法化简。 
列真值表 
根据真值表和函数表达式确定电路的逻辑功能。难以用语言描述时列真值表即可。 
 
5.1.2 设计步骤 
进行逻辑抽象:
                      1. 分析因果关系,确定输入输出变量。
                          2. 定义逻辑状态含义
                          3. 根据因果关系列出真值表
 
确定输出函数,并利用卡诺图或公式法化简
 
选定器件类型:SSI、MSI、PLD等 
根据器件类型将逻辑函数化简或变换成适当形式 
画出逻辑电路图 
 
5.1.3 竞争与冒险 竞争 :当一个逻辑门的两个输入端信号同时向相反方向变化,而变化的时间有差异。冒险 :两个输入端信号取值的变化方向是相反时,如门电路输出端的逻辑表达式简化成两个互补信号相乘或相加,由竞争而可能产生输出干扰脉冲的现象。 由于竞争而在电路输出端可能产生尖峰脉冲的现象叫做竞争-冒险 。消除办法 : 一、接入滤波电容。优点 :简单。缺点 :增加了波形的上升和下降时间,波形变坏。 二、引入选通脉冲。优点 :简单。缺点 :正常的输出信号也会变成脉冲信号,宽度与选通脉冲相同,且此选通脉冲必须与输入信号同步。 三、修改设计(废话)。比如把$Y = AB + \overline{A}C$化成$Y = AB + \overline{A}C + BC$。