给 Verilog 写一个总结(一)

前言:我大致完成了一个学期的「数字逻辑实验」,是时候给我的 verilog 写一个总结了,好不容易学会的 verilog 不能说忘就忘,后续会创建一个仓库,把自己经常使用的代码收集起来

首先特别想吐槽 verilog 的 begin end,真的非常非常蠢。。。尤其是 case 里面的:

case (variable)
    
endcase

由于自己实现过一个简单的「解释器」,像这样语法方面的东西,真的说改就能改,作为一个 1992 左右年出来的语言, 就不能在语法方面向 C 语言靠拢吗

0X00 运行代码

verilog 不是非要和 vivado 等类似的工具在一起才能运行的,是有工具能够编译和执行的:

详细看我的这篇文章:

Verilog 入门(一)Hello World

0X01 变量基础

变量方面的知识绝对是一大难点,跟其他语言完全不一样!

数据类型以及变量的声明

  • NETS

wire 是 NETS 的一种,通常我们使用 wire 声明 NETS 变量

wire a;
  • Registers

reg 是 Registers 的一种,通常我们使用 reg 声明 Registers 变量

reg b;

module 输入/输出类型的声明

我们可以这样声明 input / output 类型:

module test(
    input a,
    output b
);
reg b;
...
endmodule

也可以这样声明 input / output 类型:

module test(
    input a,
    output reg b
);

...
endmodule

其中未声明的 input / output 默认是 wire 类型,这两种类型具体有啥区别,你在后面的内容中会慢慢感受到

为了方便,我将 NETS 类型和 Registers 类型分别用 wire 和 reg 代替

变量的赋值

在这里我们将遇到 wire 和 reg 的第一个不同点

  • 给 wire 赋值

你可以这样写:

wire a = 10;

也可以这样写:

wire a;
assign a = 10;

这两者赋值的方式等价!

  • 给 reg 赋值

给 reg 赋值不能用 assign,而且要在块里面赋值。

你可以这样赋值:

module test();
    reg t;
    always @(*) begin
        t = 10;
    end
endmodule

也可以这样赋值:

module test();
    reg t;
    always @(*) begin
        t <= 10;
    end
endmodule

这两种方式不等价,待会会具体抽出来比较这两者的区别

0X02 = VS <=

我们来看下面这两段代码:

// A.v
module A(
    input D, Clock;
    output reg Q1, Q2;
);
    always @(posedge Clock) begin
        Q1 = D;
        Q2 = Q1;
    end
endmodule
// B.v
module B(
    input D, Clock;
    output reg Q1, Q2;
);
    always @(posedge Clock) begin
        Q1 <= D;
        Q2 <= Q1;
    end
endmodule

下面分别是他们综合以后的图:


给 Verilog 写一个总结(一)_第1张图片
给 Verilog 写一个总结(一)_第2张图片

第一幅图是 A 的,第二幅图是 B 的。为什么会这样呢?

= 是阻塞赋值,<= 是非阻塞赋值。A 的代码会被认为:

Q1 = D;
Q2 = D;

所以会出现两个并行 D 触发器的样子

而 <= 赋值是等这个块结束以后再赋值,Q2 拿的不是 Q1 的新值,拿的是 Q1 的旧值。

0X03 变量的初始化

在其他语言中我们经常干的就是「变量初始化」:

String text = "你真的是我见过的最好看的女生"

在 verilog 中「变量的初始化」很不同!

首先对于 input 与 output 的初始化:

module A(
    input D, Clock;
    output reg Q1, Q2;
);
...
endmodule
  • input 不用初始化

input 是不能被初始化的,因为 input 是上一个 module 给的的,你初始化他干啥!

  • output 初始化的时候一定是 reg 类型

之前我们说过 reg 类型只能在块中被赋值

所以 reg 类型的初始化在

initial begin 
end

中。

写一段代码就是这样:

module A(
    input D, Clock;
    output reg Q1, Q2;
);
    initial begin 
        Q1 <= 0;
        Q2 <= 0;
    end
endmodule

最后说说对 wire 的赋值:

通常在块外使用 assign 给 wire 赋值,最简单的理解 assign 的方法就是想象「一条线」

assign y = 1;

相当于给 y 拉一条恒等于 1 的线。

0X04 Wire VS Reg

我们来到我这篇博客的最后一部分「Wire VS Reg」

  • wire 的 assign 的赋值都可以转换成 reg 在块中赋值

首先来看两段代码:

wire 的 assign 的赋值

// A.v
module A( a, b, y);
    input a, b;
    output y;
    wire a, b, y;
    assign y = a & b;
endmodule

这段代码综合以后的电路:

给 Verilog 写一个总结(一)_第3张图片

而 reg 在块中赋值

// B.v
module B( a, b, y);
        input a, b;
        output y;

        reg   y;
        wire a, b;

        always @ ( a or b)
        begin   
             y = a & b;
        end
endmodule

综合以后的电路:

给 Verilog 写一个总结(一)_第4张图片

这两者的电路真的一模一样!!!

所以 wire 的 assign 的赋值都可以转换成 reg 在块中赋值

未完待续...

你可能感兴趣的:(给 Verilog 写一个总结(一))