查看完整版本: 【芝士】VLSI第六讲:Verilog HDL向量、存储、运算符

微时灬暮雨 发表于 2026-7-24 15:43:45

【芝士】VLSI第六讲:Verilog HDL向量、存储、运算符

向量是寄存器和总线的形态,存储器是向量的阵列延伸,而运算符则是真正干活儿的"动作"。把它们合在一起看,写起稍微复杂一点的电路就顺手多了。

值得一提的是,写完主题内容之后,让AI扩展一下,并进行排版,确实比纯手敲好不少。可惜这个网站貌似不支持markdown格式,不然能更方便一点


================================================================
一、标量与向量
================================================================

宽度为 1 位的变量叫"标量",声明里没指定位宽就是默认标量:

    wire a;       // a 是标量(1 位)
    regclk;   // clk 是标量(1 位)

宽度大于 1 位的变量叫"向量",wire 和 reg 都适用。向量宽度用方括号指定:

    wire bus;    // 4 位总线,左边是最高位
    reg data;   // 8 位寄存器

注意方括号里 这种写法: 是最高有效位(MSB), 是最低有效位(LSB)。也可以写成 ,最低位在前面,只是写法不同,用的时候别把索引搞混。




================================================================
二、位选择和域选择
================================================================

在表达式里可以选中向量中的某一位或者连续几位,分别叫"位选择"和"域选择",是 Verilog 里非常常用的操作。

· 位选择:选中向量里的某一位
· 域选择:选中向量里相邻的连续几位

    // 假设有 reg mybyte;
    A = mybyte;       // 位选择:取第 2 位
    B = mybyte;   // 域选择:取第 3 到第 1 位

再看一个实际场景:

    reg a, b;
    reg c;
    reg d;
    d = a & b;       // 位选择,分别取最高位做与运算
    c = a + b;   // 域选择,高 4 位和低 4 位相加

位选择和域选择也可以出现在赋值语句的左边:

    wire out;
    wire in;
    assign out = in;   // 把 out 的第 2 到 5 位赋成 in 的值

这段代码等效于:

    assign out = in;
    assign out = in;
    assign out = in;
    assign out = in;

[注意] 顺带提一下 vectored 关键字。如果声明成 wire vectored databus;,就表示这是个"向量类向量",这种情况下不能进行位选和域选;不加这个关键字(默认是标量类向量)就都可以选。一般情况下用默认的就好,vectored 只在特殊场合用。




================================================================
三、存储器(Memory)
================================================================

存储器可以看作一个二维的向量,或者说是由一组寄存器构成的阵列。简单理解:

    n 个相同宽度的 reg 向量排成一排,就是一个存储器。

声明语法:

    reg 存储名;

一个存储器声明包含两个维度的信息:

· 位宽(字长):每个存储单元有多宽,比如每个单元 8 bit
· 容量(深度):一共有多少个这样的单元,比如 64 个

举个例子:

    reg mymem ;
    // 这是一个存储器,名字叫 mymem
    // 每个单元 8 位宽(字长 8),一共有 64 个单元(容量 64)
    // 可以理解为:64 个 8 位寄存器排成的阵列

再举几个:

    reg Amem ;   // 64 个单元,每个单元 4 位
    reg       Bmem ;    //5 个单元,每个单元 1 位(不写位宽默认 1 位)

为了让尺寸好修改,可以用 parameter 参数化:

    parameter width = 8, memsize = 1024;
    reg mymem ;
    // 定义了字长 8 bit、容量 1024 的存储器




================================================================
四、存储器使用的注意事项
================================================================

存储器有几个限制一定不能踩,否则综合会报错。


4.1只能对存储器的某一个单元整体赋值

    reg mymem ;    // 定义
    mymem= 8'b10001001;   // 合法:给第 8 个单元赋值
    mymem = 65;            // 合法:十进制 65 赋给第 25 个单元
    mymem   = 8'b10001001;   // 非法!不能一次赋整个存储器


4.2不允许对存储器进行位选择和域选择

    mymem;       // 非法!存储器单元不能再拆位
    mymem;   // 非法!




================================================================
五、寄存器与存储器的区别
================================================================

这个区别很多初学者都会搞混,专门列一下:

    reg rega;            // 这是一个 n 位的寄存器
    reg       mema ;      // 这是一个由 n 个 1 位寄存器构成的存储器

赋值的区别:

    rega = 1'b1;            // 合法:对寄存器的某一位赋值
    mema = 1'b1;            // 合法:对存储器的某个单元赋值
    rega    = 8'b01011000;       // 合法:对寄存器整体赋值
    mema    = 8'b01011000;       // 非法!不允许对存储器的所有单元一次性赋值

简单记:一个 n 位的寄存器可以在一条语句里整体赋值,但一个完整的存储器是不行的,必须一个单元一个单元来。




================================================================
六、Verilog 运算符总览
================================================================

把向量和存储器讲完之后,接下来就是真正干活儿的运算符。Verilog 的运算符可以从两个维度来看:

· 按操作数个数分:单目、双目、三目
· 按功能分:算术、逻辑、位运算、关系、等式、缩位、移位、条件、位拼接

下面按功能分类一个个过一遍,尽量每种都配上例子。


6.1按操作数个数分类

· 单目运算符:只带一个操作数,比如~a
· 双目运算符:带两个操作数,比如a + b
· 三目运算符:带三个操作数(Verilog 里只有 ?:),比如sel ? a : b


6.2算术运算符(Arithmetic)

· +   加
· -   减
· *   乘
· /   除
· %   求模(取余)

这些和 C 语言基本一样,需要注意除法和取模在硬件里实现起来比较占资源。


6.3逻辑运算符(Logical)

· &&   逻辑与
· ||   逻辑或
· !    逻辑非

注意这里说的是"逻辑"运算。如果操作数不止一位,要把整个操作数当作一个整体来看:全 0 才算逻辑 0,只要有一位是 1 就整体看作逻辑 1。最后的结果都是 1 位的值。

举个例子,A=4'b0000; B=4'b0101; C=4'b0011; D=4'b0000;

    !A    = 1   // A 全 0,整体是逻辑 0,取非就是 1
    !B    = 0   // B 不是全 0,整体是逻辑 1,取非就是 0
    A&&B= 0   // A 是逻辑 0,与运算结果为 0
    B&&C= 1   // B 和 C 都至少有一位是 1,都是逻辑 1
    A||B= 1   // B 是逻辑 1
    B||C= 1
    A||D= 0   // A 和 D 都是逻辑 0


6.4位运算符(Bitwise)

位运算是把两个操作数按对应位分别做逻辑运算:

· ~      按位取反
· &      按位与
· |      按位或
· ^      按位异或
· ^~ / ~^按位同或(两个符号等价)

举个例子,A=4'b1001; B=4'b1101;

    ~A   = 4'b0110
    A&B= 4'b1001
    A|B= 4'b1101
    A^B= 4'b0100

[注意] 两个长度不同的操作数做位运算时,会自动按右端对齐,短的高位补 0。


6.5关系运算符(Relational)

· <   小于
· <=    小于或等于
· >   大于
· >=    大于或等于

返回值规则:

· 关系为假,返回 0
· 关系为真,返回 1
· 操作数有不定值 x,结果就是 x


6.6等式运算符(Equality)

· ==   等于
· !=   不等于
· ===    全等(不可综合)
· !==    不全等(不可综合)

和 C 语言的差别就在 === 和 !== 上。普通的 == 比较时,遇到不定态 x 或高阻 z,结果就是 x;只有 === 才要求每一位都严格相同,x 就是 x,z 就是 z,所以 === 不可综合,只用于仿真。

举个例子,D=4'b1x01, a=4'b1x01;

    a == b    // 结果是 x,因为有 x 参与
    a === b   // 结果是 1,每一位都严格相等


6.7缩位运算符(Reduction)

缩位运算和位运算不一样,是对单个操作数进行与、或、非递推,结果是 1 位。符号放在操作数前面:

· &      与
· ~&   与非
· |      或
· ~|   或非
· ^      异或
· ^~ / ~^同或

举个例子:

    reg a;
    b = &a;    // 等价于 b = ((a & a) & a) & a

若 A=5'b11001:

    &A   = 0   // 所有位都为 1 才为 1
    |A   = 1   // 所有位都为 0 才为 0
    ~|A= 0
    ^A   = 1   // 1 的个数为奇数


6.8移位运算符(Shift)

· >>   右移
· <<   左移

是逻辑移位,移出的位都用 0 填补。

举个例子,A=5'b11001;

    A >> 2 = 5'b00110    // 右移 2 位,左边补 0
    A << 2 = 5'b00100    // 左移 2 位,右边补 0(低 2 位被移出丢弃)


6.9条件运算符(Conditional)

这是 Verilog 里唯一的三目运算符,格式:

    信号 = 条件 ? 表达式1 : 表达式2;

当条件成立时,信号取表达式 1 的值,否则取表达式 2 的值。最典型的应用就是二选一多路选择器:

    // 二选一 MUX
    out = sel ? in1 : in0;   // sel=1 时 out=in1,否则 out=in0


6.10位拼接运算符(Concatenation)

用 { } 把两个或多个信号的某些位拼接起来:

    {信号1的某些位, 信号2的某些位, ..., 信号n的某些位}

例如:

    wire a, b;
    wire c;
    c = {a, b};   // 把 a 的高 3 位和 b 的低 3 位拼起来

位拼接还支持重复法来简化书写:

    {3{a, b}}   // 等价于 {{a, b}, {a, b}, {a, b}},也等价于 {a, b, a, b, a, b}
    {3{1'b0}}   // 结果为 6'b000000

位拼接在符号位扩展时也特别方便:

    wire Data;
    wire s_data;
    s_data = {{4{Data}}, Data};   // 把符号位扩展到 12 位




================================================================
七、运算符优先级
================================================================

运算符种类多了之后优先级容易搞混,所以最后强烈建议一个习惯:

    写程序的时候,如果不确定优先级,就直接用括号 ( ) 把它括起来!
    这一点对调试代码特别有用,能省掉很多"我明明写的没问题为什么结果不对"的纠结时间。




================================================================
八、整体小结
================================================================

这一篇比较长,把 Verilog 里"数据怎么放"和"数据怎么算"两块基础合在一起过了一遍。


[向量与存储器]

· 向量:宽度大于 1 位的变量,wire 和 reg 都可以是向量
· 位选择和域选择: 选一位, 选连续几位
· 存储器:reg 型的二维阵列,用 [位宽-1:0] 名字 [容量-1:0] 声明
· 存储器只能对单个单元整体赋值,不支持位选和域选
· n 位寄存器可以整体赋值,n 个 1 位寄存器排成的存储器不行


[运算符全家福]

· 算术:+、-、*、/、%
· 逻辑:&&、||、!
· 位运算:~、&、|、^、^~
· 关系:<、<=、>、>=
· 等式:==、!=、===、!==
· 缩位:对单个操作数递推,结果 1 位
· 移位:>>、<<
· 条件:?:(唯一的三目运算符)
· 位拼接:{ },支持重复语法



1739986716 发表于 2026-7-24 21:49:04

顺着老哥之前的帖子一路找过来了,我是准备明年考研的,也是计算机系的,想找老哥要点推荐的资料和课程{:5_180:}

繁星记得 发表于 2026-7-25 20:55:28

1739986716 发表于 2026-7-24 21:49
顺着老哥之前的帖子一路找过来了,我是准备明年考研的,也是计算机系的,想找老哥要点推荐的资料和课程{:5_ ...

找对了人孩子,我是去年看了楼主帖子考上研究生的,我私信你把焚决给你,明年考上了别忘了报喜

微时灬暮雨 发表于 2026-7-27 00:31:05

1739986716 发表于 2026-7-24 21:49
顺着老哥之前的帖子一路找过来了,我是准备明年考研的,也是计算机系的,想找老哥要点推荐的资料和课程{:5_ ...

具体啥专业啊,11408?

cheec 发表于 2026-7-27 13:07:10

下一篇写个 Vivado入门指南 手搓一个risc v出来{:4_108:}

微时灬暮雨 发表于 2026-7-27 14:38:30

cheec 发表于 2026-7-27 13:07
下一篇写个 Vivado入门指南 手搓一个risc v出来

那得拍到比较后面了,先把verilog语法讲清楚,我再想想vivado教程怎么写吧
页: [1]
查看完整版本: 【芝士】VLSI第六讲:Verilog HDL向量、存储、运算符