Verilog语法学习笔记1(结合HDLBits刷题网站)

Getting Start

数字电路中,1对应高电平信号,0对应低电平信号。

Verilog Language

Basic

关键词assign用于连续赋值

assign left_side = right_side

NOT gate(非门),逻辑取反符号!,按位取反符号~
在这里插入图片描述
AND gate(与门),逻辑与符号&&,按位与符号&
在这里插入图片描述
OR gate(或门),逻辑或符号||,按位或符号|

下图为或非门,即在或门的基础上加个非
在这里插入图片描述
XNOR gate(同或门),逻辑同或符号^~
XOR gate(异或门),逻辑同或符号^

下图为同或门,可以看作在异或门的基础上加个非
在这里插入图片描述

Vectors

向量声明格式:类型 [高位:低位] 向量名

wire [99:0] my_vector;      // Declare a 100-element vector

未声明的向量在使用过程中会产生Bug,且它固定为一个位宽

wire [2:0] a, c;   // Two vectors
assign a = 3'b101;  // a = 101
assign b = a;       // b =   1  implicitly-created wire
assign c = b;       // c = 001  <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared.
                    // This could be a bug if the port was intended to be a vector.

禁止隐式声明功能需要添加一个宏定义

`default_nettype none

声明reg存储器类型时,可以在reg向量名后加存储单元个数

reg [7:0] mem [255:0];   // 有256个存储单元,每个存储单元是一个8bit的reg
reg mem2 [28:0];         // 有29个存储单元,每个存储单元是一个1bit的reg

向量部分选择操作符 [:]

assign out = my_vector[10]; // Part-select one bit out of the vector

向量连接操作符 {,},连接运算符中的每一个元素都需要标注位宽,否则是非法的

assign {w,x,y,z} = {a,b,c,d,e,f,2'b11};

向量复制操作符格式 {num{Vector}}

assign out = ~{{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}} ^ {5{a,b,c,d,e}}; 

Modules:Hierarchy

父子模块的连接,需要在父模块底下实例化子模块

module mod_a ( input in1, input in2, output out ); //定义了一个mod_a
    // Module body
endmodule

模块的实例化有两种方式,分为按位置按名称

按位置实例化是从左到右将模块的端口和外部的信号一一对应,显然,这种方式的缺点是一旦端口列表发生改变,所有模块实例化中的端口连接都需要调整。

例化格式:模块名 实例名 (外部信号1, 外部信号2, 外部信号3, ……,外部信号n);

mod_a instance1 ( wa, wb, wc );

按名称实例化就是将模块实例外部的信号直接对应于该模块的端口名称,这种方式下的端口顺序可以是任意的。

例化格式:模块名 实例名 ( .端口名(外部信号), .端口名(), .端口名(外部信号));

mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );

全加器表达式

assign sum = a^b^cin;
assign cout = a&b | a&cin | b&cin;

当然,为了更为贴近加法器,上述代码可以写成下面这样

assign {cout,sum} = a + b + cin;

再来一个半加器表达式

assign sum  = a^b;
assign cout = a&b;

Procedures

关键字 always
always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。

Combinational: always @(*)
Clocked: always @(posedge clk)

由于循环执行的特点,always 语句多用于仿真时钟的产生,信号行为的检测等。

always if语句

always @(*) begin
    if (condition) begin
        out = x;
        ...
    end
    else begin
        out = y;
        ...
    end
end

相当于下面的连续assign语句

assign out = (condition) ? x : y;

always case语句

always @(*) begin     // This is a combinational circuit
    case (in)
      1'b1: begin 
               out = 1'b1;  // begin-end if >1 statement
            end
      1'b0: out = 1'b0;
      default: out = 1'bx;
    endcase
end

always casez语句

always @(*) begin
    casez (in[3:0])
        4'bzzz1: out = 0;   // 输入in的高三位可以为任意值
        4'bzz1z: out = 1;
        4'bz1zz: out = 2;
        4'b1zzz: out = 3;
        default: out = 0;
    endcase
end

Latch(锁存器)

电平触发的存储单元,数据存储的动作取决于输入时钟(或者使能)信号的电平值。仅当锁存器处于使能状态时,输出才会随着数据输入发生变化。

当电平信号无效时,输出信号随输入信号变化,就像通过了缓冲器;当电平有效时,输出信号被锁存。激励信号的任何变化,都将直接引起锁存器输出状态的改变,很有可能会因为瞬态特性不稳定而产生振荡现象。

Latch 的主要危害:

  1. 输入状态可能多次变化,容易产生毛刺,增加了下一级电路的不确定性;
  2. 在大部分 FPGA 的资源中,可能需要比触发器更多的资源去实现 Latch 结构;
  3. 锁存器的出现使得静态时序分析变得更加复杂。

避免Latch的方法:

  1. if-else 或 case 语句,结构一定要完整
  2. 不要将赋值信号放在赋值源头,或条件判断中
  3. 敏感信号列表建议多用 always@(*)

More Verilog Features

三目运算符,跟C语言中的类似

(condition ? if_true : if_false)

之前学习了在两个值之间的按位运算符,但有时需要对一个向量的所有位进行按位运算操作
其实就是在向量前面加上按位运算符,如下

& a[3:0]     // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0]     // OR:  b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0]     // XOR: c[2]^c[1]^c[0]

这样的好处就在于不用写冗长的按位运算逻辑代码了

循环for语句

for(initial_assignment; condition ; step_assignment)  begin
   …………
end
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值