VHDL设计之逻辑综合的原则以及可综合的代码设计风格


     4.5.1 always块语言指导原则

    使用always块进行可综合的代码设计时需要注意以下几个问题。

    (1)每个always块只能有一个事件控制“@(event-expression)”,而且要紧跟在always关键字后面。

    (2)always块可以表示时序逻辑或者组合逻辑,也可以用always块既表示敏感的透明锁存器又同时表示组合逻辑。但是不推荐使用这种描述方法,因为这容易产生错误和多余的电平敏感的透明锁存器。  

    (3) 带有posedge 或 negedge 关键字的事件表达式表示沿触发的时序逻辑;没有posedge 或negedge关键字的表示组合逻辑或电平敏感的锁存器,或者两种都表示。在表示时序和组合逻辑的事件控制表达式中如有多个沿和多个电平,其间必须用关键字“or” 连接 。

    (4)每个表示时序逻辑的always块只能由一个时钟跳变沿触发,置位或复位最好也由该时钟跳变沿触发。

    (5)每个在always块中赋值的信号都必需定义成reg型或整型。整型变量缺省为32bit,使用Verilog操作符可对其进行求补的算术运算。综合器还支持整型量的范围说明,这样就允许产生不是32位的整型量,句法结构如下:

    integer[:]。

    (6)always块中应该避免组合反馈回路。每次执行always块时,在生成组合逻辑的always块中赋值的所有信号必需都有明确的值;否则需要设计者在设计中加入电平敏感的锁存器来保持赋值前的最后一个值。

    只有这样,综合器才能正常生成。如果不这样做,综合器会发出警告,提示设计中插入了锁存器。如果在设计中存在综合器认为不是电平敏感锁存器的组合回路时,综合器会发出错误信息(例如设计中有异步状态机时)。

    用always块设计纯组合逻辑电路时,在生成组合逻辑的always块中,参与赋值的所有信号都必须有明确的值,即在赋值表达式右端参与赋值的信号都必需在always @(敏感电平列表)中列出。

    如果在赋值表达式右端引用了敏感电平列表中没有列出的信号,那么在综合时,将会为该信号产生一个隐含的透明锁存器。这是因为该信号的变化不会立刻引起所赋值的变化,而必须等到敏感电平列表中某一个信号变化时,它的作用才显现出来。

    也就是相当于存在着一个透明锁存器把该信号的变化暂存起来,待敏感电平列表中某一个信号变化时再起作用,纯组合逻辑电路不可能做到这一点。这样,综合后所得电路已经不是纯组合逻辑电路了。这时综合器会发出警告提示设计中插入了锁存器,如下所示:

   

input a,b,c;
reg e,d;
always @(a or b or c) begin
      e = d & a & b;      //因为d没有在敏感电平列表中,所以d变化时,e不能立刻变化,
                              //要等到a或b或c变化时才体现出来。这就是说实际上相当于存在
                              //一个电平敏感的透明锁存器在起作用, 把d信号的变化锁存其中
      d = e | c;
end

(7)对一个寄存器型(reg)或整型(integer)变量的赋值只允许在一个always块内进行,如果在另一always块也对其赋值,这是非法的。
(8)把某一信号值赋为'bx,综合器就把它解释成无关状态,因而综合器为其生成的硬件电路最简洁。


4.5.2  可综合风格的Verilog HDL模块实例
1.组合逻辑电路设计实例


例4.6:8位带进位端的加法器的设计实例(利用简单的算法描述)。

module adder_8(cout,sum,a,b,cin);              //模块声明
      output cout;
      output [7:0] sum;
      input cin;
      input[7:0] a,b;                            //端口声明
      assign {cout,sum} = a + b + cin;          //加法器算法
endmodule

 

例4.7:指令译码电路的设计实例(利用电平敏感的always块来设计组合逻辑)。

'define plus    3'd0                        //操作码的宏定义
'define minus    3'd1
'define band    3'd2
'define bor      3'd3'
'define unegate  3'd4

module alu(out,opcode,a,b);                //模块声明
      output [7:0] out;
      input [2:0] opcode;
      input [7:0] a,b;                      //端口声明
      reg  [7:0] out;                      //寄存器声明

      always @(opcode or a or b) begin      //用电平敏感的always块描述组合逻辑
            case(opcode)
                  'plus: out = a + b;        //算术运算
                  'minus: out = a - b;
                  'band: out = a & b;          //位运算
                  'bor:  out = a | b;
                  'unegate: out = ~a;        //单目运算
                  default: out = 8'hx;
      endcase
      end
endmodule

 

例4.8:比较后重组信号的组合逻辑(利用task和电平敏感的always块设计)。

module sort4(ra,rb,rc,rd,a,b,c,d);        //模块声明
      output [t:0] ra, rb, rc, rd;
      input [t:0] a, b, c, d;              //端口声明
      reg [t:0] ra, rb, rc, rd;
      reg [t:0] va, vb, vc, vd;            //寄存器声明
      parameter t=3;                            //参数声明

      always @(a or b or c or d) begin      //用电平敏感的always块描述组合逻辑
          {va,vb,vc,vd}={a,b,c,d};
          sort2(va,vc);                        //信号重组
          sort2(vb,vd);
          sort2(va,vb);
          sort2(vc,vd);
          sort2(vb,vc);
          {ra,rb,rc,rd}={va,vb,vc,vd};
      end

      task sort2;                            //x与y互换任务
            inout [t:0] x,y;
            reg [t:0] tmp;
            if(x > y) begin
                tmp = x;              //使用临时变量tmp保存x的值
                x = y;
                y = tmp;
      end
      endtask
endmodule

 

例4.9:比较器的设计实例(利用赋值语句设计组合逻辑)。

module compare(equal,a,b);              //模块声明
      output equal;
      input [size-1:0] a,b;            //端口声明
      parameter size=1;                  //参数声明
      assign  equal =(a==b)? 1 : 0;  //比较器
endmodule

 

例4.10:3-8译码器设计实例(利用赋值语句设计组合逻辑)。

module decoder(out,in);            //模块声明
      output [7:0] out;
      input [2:0] in;                //端口声明
      assign  out = 1'b1<<in;      //把最低位的1左移 in(根据从in口输入的值)位
                                          //将移位结果赋予out
endmodule

 

例4.11:3-8器的设计实例。
编码器设计方案一。

module encoder1(none_on,out,in);            //模块声明
      output none_on;
      output [2:0] out;
      input [7:0] in;                        //端口声明
      reg [2:0] out;
      reg none_on;                              //寄存器声明
      always @(in) begin: al                //in有变化时,触发
            integer  i;                          //变量声明
            out = 0;
            none_on = 1;                        //初始化
            for( i=0; i<8; i=i+1 ) begin      //for循环语句
                if( in[i] ) begin        //将in中值为1的位编码
                      out = i;
                      none_on = 0;
                end
            end
      end
endmodule