Menu Close

Verilog 语言编写深入探讨

在Verilog程序设计中,关于模块,always过程,并行赋值语句,函数及task调用等等。如何声明,调用,嵌套及赋值等一系列问题,初学者往往无所适从。关于wire,reg类型的声明,初始化及赋值等也有一系列的问题需要注意。本文对上述提到的相关问题进行深入讨论。希望读者在理解之后举一反三,做到正确使用Verilog语法规则,使得代码清晰,简洁,准确。

 

1. 模块(module)的组织与正确使用

模块(module)是verilog工程的基础,所有的verilog程序都是在module中进行编写的,少量的宏定义可以在模块之外。一个文件可以只包含一个模块,也可以是一个文件包含多个模块。 一个模块不能分离存储到不同的文件中。

1)通常情况下一个文件(*.v)中包含一个module。但也可以在 同一个*.v 文件中,同时存在2个或者多个 module 模块。module 的格式请参考:Verilog 模块与端口。

2)在文件中,module 级别是最高一级的,其他的如always,initial,并发赋值等只能在module 模块下进行编写和使用。 和module 同一级别 的verilog语法声明也包括timescale,  include,  define 等宏定义,如例1所示。

例1:

3)module 和 module是同一级别的。 Verilog 语法中module 模块中不能再嵌套定义module了, 虽然Systemverilog 可以嵌套定义,但也有规则限制。

例2:

 

从例1,例2的使用可以看出:

    • Verilog 语法中module 下不能在定义 其他module了,但是systemverilog 可以(nested module declarations)。但是verilog 和 systemverilog 在module中都可以例化其他模块(module)。
    • module 和 include 是同一级别的, 即 include可以写在module 模块之外。 当然, include也可以写在模块内部。

    • module 和 define 是同一级别的, 即 define可以写在module 模块之外。 当然, define也可以写在模块内部。
    • module 和 timescale是同一级别的, 即 timescale可以写在module 模块之外。 当然, timescale也可以写在模块内部。但是很少将timescale写在module 内部声明。
    • 4)module嵌套时的端口传递

        • 被例化的module 输入端口(input)传递关系,能否将变量传递给被例化模块的输入端口(input),关键看驱动模型。
          •  符合驱动模型传递如下:
            • 本级模块的input 类型端口
            • 本级模块的output 类型端口
            • 本级模块的定义的reg 类型变量
            • 本级模块中的wire 类型变量
          • 不符合驱动模型传递如下:
            • 无
        • 被例化的module 输出端口(output)传递关系
          • 符合驱动模型传递如下:
            • 本级模块的output,但是有如下限制
              • 本级模块的output不能连接到多个被例化模块中的output端口上
              • 本级模块的output连接到1个被例化模块中的output端口上,不能在本级模块其他逻辑中赋值
            • 本机模块的wire,但是有如下限制
              • 本级模块的wire不能连接到多个被例化模块中的output端口上
              • 本级模块的wire连接到1个被例化模块中的output端口上,不能在本级模块其他逻辑中赋值
          • 不符合驱动模型传递如下:
            • 本级模块的reg 类型不能连接到被例化模块中的output 端口上
            • 本级模块中的output reg 类型端口,不能连接到被例化模块中的output端口上

      2. 仿真文件编写

      在仿真文件中, module 内部可以编写

      • reg声明
      • wire声明
      • task声明
      • function 声明,
      • module 例化,
      • generate,
      • define,
      • include,
      • timescale,
      • parameter,
      • localparam,
      • always,
      • assign
      • initial
      • IP, primitive 例化等 。

      也就是说这些声明,例化是同一级别的。

      举例

       

      在模块中可以使用wire,reg等声明,但要注意reg 声明时不能使用变量赋初值,如果需要,只能使用wire声明。

      例1:

       

      这里 reg a = b | c ; 是不正确的。  只能使用 wire k = b |c ; 或者是 wire k;  assign = b | c;  来替代。

      例2:always 多处赋值

       

       

      b 有4个地方赋值:

      • 声明的时候reg [3:0] b = 0;
      • 初始化的时候赋值 initial
      • always b <= ~b;
      • always b <= b + 1;

      这样的设计,在仿真的时候,可以正常使用。 但是相同的逻辑不能在综合的时候使用。原因是: 2 次 always  进行不同的赋值综合的时候会报错。调用模块alter_sim中的led (output 端口) 和 assign led = 8'h80; 赋值相冲突,最终输出为led = xx;

       

      例3:initial 中嵌套always, 不能正常编译。

      initial 中使用always 编译的时候会报错。但可以使用其他方式实现:

      或者

      例4:always 中使用 initial 不能正常编译:

       

      例5:2 个模块的输出(output) 连接到一起了。

      例6:always 中不能使用assign

       

      例7:always 中不能使用generate , 但是generate 中可以使用always。

       

      例8:forever, while, if, for, case 等必须在initial, always 下使用,不能独立存在。即使有些综合器没有报错。

       

      例9:define parameter可以在端口定义中使用,define 可以定义端口的种类和变量宽度,parameter 只能定义端口的宽度。举例:


      例10:always 使用


      ; 代表当前的always 结束了, 比如在仿真中使用@(posedge clk); 所以之后的begin ….   end 和 always 再也没有任何关系了。在always 中一定会有一个真实的执行体,不会只有always ,说得直白一些就是always 下一定能找到一个“;”。 举例:


       

       

      第一级 第二级 第三级
      module
      wire声明
      reg声明
      task 声明
      function 声明 不能使用 #10; 不能使用always@(posedge)等时序逻辑
      generate
      define 可以定义端口数量,改变端口声明
      include
      timescale
      parameter 可以定义端口宽度
      localparam
      always 不能使用使用initial,assign, 模块声明,例化 ,不能声明function, 但可以调用function
      initial 不能使用使用always, assign, 模块声明,例化,不能声明function, 但可以调用function
      assign 不能包括begin .... end, 只能使用逻辑,关系,等式,位操作等运算符
      IP, primitive 例化
      module 例化
      timescale
      define
      include

       

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章