打印

Verilog 代码风格-供参考

[复制链接]
1561|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiao6666|  楼主 | 2012-9-8 17:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Description

本文主要是收集一些重要的Verilog coding style。一个好的coding style可以减少错误的发生,增加电路的效能,以及较好的可读性。

Text

The order of module signals

一个module signal顺序如下 (由左至右):

Input

clock signals(clk_*)

set/reset signals(set_*, rst_*)

enable/disble signals(en_*, dis_*)

read/write enable signals(we_*, re_*, rw_*)

control signals(i_*)

address signals(i_*)

data signals(i_*)

Output

clock signals(o_clk_*)

set/reset signals(o_set_*, o_rst_*)

enable/disable signals(o_en_*, o_dis_*)

control signals(o_*)

address signals(o_*)

data signals(o_*)

In/Out

control signals(io_*)

address signals(io_*)

data signals(io_*)

Naming Rule

以下的namign rule为个人使用的规则。

命名方式分类

底线分隔型:xxx_yyy_zzz

大写底线分隔型:XXX_YYY_ZZZ

首字大写型:AbcDefGhi

首字小写型:avcDefGhi

各种元素所使用的命名

文件名称:底线分隔型, Ex: xxx_yyy_zzz.v

module名称:底线分隔型, Ex: xxx_yyy_zzz

module instance名称:底线分隔型, Ex: xxx_yyy_zzz

local wire名称:底线分隔型:Ex: xxx_yyy_zzz

local reg名称:底线分隔型, Ex: xxx_yyy_zzz

input signal名称:前置i_的底线分隔型, Ex: i_xxx_yyy_zzz

output signal名称:前置o_的底线分隔型, Ex: o_xxx_yyy_zzz

input/output signal名称:前置io_的底线分隔型, Ex: io_xxx_yyy_zzz

常数名称:大写底线分隔型, `XXX_YYY_ZZZ

parameter参数名称:大写底线分隔型, Ex: XXX_YYY_ZZZ

block名称:大写底线分隔型, Ex: XXX_YYY_ZZZ

特殊讯号名称

单一的clock signal: clk

多个clock signal: clk_xxx

负缘触发的clock signal: clk_n, clk_xxx_n

单一的reset signal: rst

多个reset signal: rst_xxx

负缘触发的reset signal: rst_n , rst_xxx_n

单一的set signal: set

多个set signals: set_xxx

负缘触发的set signals: set_n, set_xxx_n

致能讯号: en_xxx

除能讯号: dis_xxx


Procedural Assignments

使用指引


在撰写sequential logic时,使用nonblocking assignment。

在撰写latches电路时,使用nonblocking assignment。

在always block中撰写conbinational logic时,使用blocking assignment。

在同一个always block中同时撰写sequential及combinational logic时,一律使用nonblocking assignment。

别在同一个always block中混合使用nonblocking及blocking assignment。

别在两个以上的always block中对同一个变量设定数值。

使用$strobe来显示由nonblocking assignment所给定的变量。

不要对assignment使用#0延迟设定。

在procedural assignment中,LHS一定要是reg型态。非procedural assignment一定是net的型态。

常用的style

Combination logic

在always block中,如果使用combination logic,应当使用blocking assignment.

// All inputs used within always block should be listed in sensitive list.
always @(in1 or in2 or in3)
begin
  xxx = in1 ^ in2 & in3;
end
Sequential logic

在always block中,如果使用sequential logic,应当使用nonblocking assignment.

// Synchronous reset. The rst_n is NOT in sensitive list.
always @(posedge clk)
  begin
    if (~rst_n)
        begin
      xxx <= `INIT_VAL;
        end
    else
        begin
      xxx <= yyy;
        end
  end

// Asynchronous reset. The rst_n IS in sensitive list.
always @(posedge clk or negedge rst_n)
  begin
    if (~rst_n)
      begin

        xxx <= `INIT_VAL;
      end
    else
      begin
        xxx <= yyy;
      end
  end
Delay的建模

Combination logic

建模没有delay时,使用blocking assignment(ex: a = b;)

建模有惯性(inertial) delay时(即glitch不会传到后面的电路中)。使用delayed evaluation blocking assignments(#10 a = b;).

建模传输(transport)delay时(即glitch也会一并传到后面的电路中)。使用delayed assignment nonblocking assignments(ex: a <= #10 b;).

Sequential logic

建模没有delay时,使用non-blocking assignments (ex: q <= d; ).

建模有delay时,使用delayed assignment nonblocking assignments(ex: q <= #10 d;).

Finite State Machine

Moore FSM: 输出与输入没有直接关系。

由两个always block构成,一个是sequential block用来处理状态的变化。另一个为combinational block用来处理状态与输入之间的关系。注意,在sequential block中应全部使用nonblocking assignment。在combinational block中应使用blocking assignment。在某些简单的case中,combinational block也可直接由continuous assignment来取代。下面的范例是一般简单的FSM。

// Sequential always block.
always @(posedge clk or posedge rst)
  begin
    if (rst)
      state <= STATE_IDLE;
    else
      state <= next;
  end

// Combinational always block.
always @(state or input1 or input2 ... or inputN)
  begin
    next = STATE_IDLE;
    outputs = OUTPUT_IDLE:
    case (state)
      STATE_IDLE:

        begin
          ... // the logic to determine the next state.
          next = STATE_?????;
        end
      STATE_?????:
        begin
          ... // the logic to determine the next state.
          next = STATE_?????;      

          outputs = ?????; // The output of this state.
        end
    endcase
  end
针对simplified one-hot encoding的FSM范例:

// Sequential always block.
always @(posedge clk or posedge rst)
  begin
    if (rst)
      state <= n'b0;
      state[STATE_DEFAULT] <= 1'b0;
    else
      state <= next;
  end

// Combinational always block.
always @(state or input1 or input2 ... or inputN)
  begin
    next = n'b0;
    outputs = OUTPUT_DEFAULT:
    case (1'b1)  // synopsys full_case parallel_case
      state[STATE_DEFAULT]:
        begin
          ... // the logic to determine the next state.
          next[STATE_?????] = 1'b1;
        end
      state[STATE_?????]:
        begin
          ... // the logic to determine the next state.
          next[STATE_?????] = 1'b1;
          outputs = ?????; // The output of this state.
        end
      // synopsys translate_off
      default:

        $display("Bad state!!");
      // synopsys translate_on
    endcase
  end
针对simplified one-hot with zero-idle encoding的FSM:

// Sequential always block.
always @(posedge clk or posedge rst)
  begin
    if (rst)
      state <= n'b0;
    else
      state <= next;
  end

// Combinational always block.
always @(state or input1 or input2 ... or inputN)
  begin
    next = n'b0;
    outputs = OUTPUT_DEFAULT:
    case (1'b1)  // synopsys full_case parallel_case
      ~|state:  // IDLE
        begin
          ... // the logic to determine the next state.
          next[STATE_?????] = 1'b1;
        end
      state[STATE_?????]:
        begin
          ... // the logic to determine the next state.
          next[STATE_?????] = 1'b1;
          outputs = ?????; // The output of this state.
        end
      // synopsys translate_off
      default:
        $display("Bad state!!");
      // synopsys translate_on
    endcase
  end
Mealy FSM: 输出与输入有直接关系。

Mealy FSM的作法与上面的范例相类似。唯一的不同在于outputs的指定,需加上与input相关的逻辑判断。例如:

case(state)  // synopsys parallel_case full_case
  ...
  STATE_?????:
    begin
    ...
    if (input1 & input2)
      outputs = ?????;
    else
      outputs = ?????;
    end
  // synopsys translate_off
  default:
    $display("Bad FSM.");
  // synopsys translate_on
  default   

endcase
Datapath

参考:Coding Guidelines for Datapath Synthesis.

有号数的计算:若有需要关于有号数的计算,应当利用Verilog 2001所提供的signed及$signed()机制。

input   signed [7:0]  a, b;
output   signed [15:0]  o;
assign  o = a * b;
or

input     [7:0]  a, b;
output     [15:0]  o;
wire  signed [15:0]  o_sgn;
assugb  o_sgn = $signed(a) * $signed(b);
assign  o = $unsigned(o_sgn);
正负号的扩展:应多加利用Verilog的implicity signed extension,避免手动进行转换。

input  signed [7:0]  a, b;
input  signed [8:0]  o;
assign  o = a + b;  // Verilog会自动进行符号的扩展。
有号数与无号数的混合计算:不要在同一个verilog叙述中进行有号数与无号数的计算。应该要分成个别独立的叙述。在一个verilog叙述中只要有一个无号数的操作数,整个算式将被当成无号数进行计算。

input     [7:0]  a;
input   signed  [7:0]  b;
output  signed  [15:0]  o;

// Don't do this: assign o = a * b;
// The $signed({1'b0, a}) can convert the unsigned number to signed number.
assign o = $signed({1'b0, a}) * b;

input  signed  [7:0]  a;
output  signed  [15:0]  o;

// Don't do this: assign o = a * 8'b10111111;
// Use $signed() system task
assign o = a * $signed(8'b10111111);
// or sb keyword.
assign o = a * 8'sb10111111;
part-select运算过后的操作数是无号数。就算是选择的范围包含整个register或wire。

input  signed  [7:0]  a;
input  signed  [7:0]  b;
output  signed  [15:0]  o1, o2;

// Don't do this: assign o1 = a[7:0];
assign  o1 = a;
// Don't do this: assign o2 = a[6:0] * b;
assign  o2 = $signed(a[6:0]) + b;
Verilog的位宽度规则:技巧就是要善用LHS来限制位宽度。利用中介的讯号线来作为限制宽度用的LHS操作数。

在没有特别设定的状况下,Verilog会依据LHS的操作数宽度来决定RHS操作数的宽度。

input  [7:0]  a;
input  [7:0]  b;
output  [8:0]  o;

assign o = a + b;  // 9 bits.
对一个表示式而言,最大宽度的操作数决定了整体的宽度。

input  signed  [3:0]  a;
input  signed  [7:0]  b;
output    [11:0]  o;
wire  signed  [11:0]  o_sgn;

// Don't do this: assign o = $unsigned(a * b);  这将会是一个8 bit的运算。因为在刮号内的bits数是依据最大操作数b的宽度决定的。
assign   o_sgn = a * b;  // 12 bits。因为bit数是依据LHS宽度决定的。
assgign o = $unsigned(o_sgn);

input  [7:0]  a;
input  [7:0]  b;
input  [7:0]  c;
input  [7:0]  d;
output  o;
wire  [15:0]  tmp1;
wire  [15:0]  tmp2;

// Don't do this: assign o = (a + b) > (c * d); 因为(a + b)及(c * d)都会是8 bit的结果。
assign  tmp1 = a + b;  // 16 bits.
assign  tmp2 = c * d;  // 16 bits.
assign  o = tmp1 > tmp2;  // 1 bit.

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

48

主题

453

帖子

1

粉丝