每日签到 地址发布 老王说明书 宣传中心 繁/简
此板块只作为纯讨论

正经话题,不搞色情!贤者时间必备
查看: 2.1K|回复: 5
收起左侧

[学习讨论] 【芝士】VLSI第六讲:Verilog HDL向量、存储、运算符

[复制链接]
跳转到指定楼层
楼主
发表于 2026-7-24 15:43:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?免费注册

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

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


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

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

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

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

    wire [3:0] bus;    // 4 位总线,左边是最高位
    reg  [7:0] data;   // 8 位寄存器

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




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

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

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

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

再看一个实际场景:

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

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

    wire [7:0] out;
    wire [3:0] in;
    assign out[5:2] = in;   // 把 out 的第 2 到 5 位赋成 in 的值

这段代码等效于:

    assign out[5] = in[3];
    assign out[4] = in[2];
    assign out[3] = in[1];
    assign out[2] = in[0];

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




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

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

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

声明语法:

    reg [n-1:0] 存储名;

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

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

举个例子:

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

再举几个:

    reg [3:0] Amem [63:0];   // 64 个单元,每个单元 4 位
    reg       Bmem [5:1];    //  5 个单元,每个单元 1 位(不写位宽默认 1 位)

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

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




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

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


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

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


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

    mymem[8][3];       // 非法!存储器单元不能再拆位
    mymem[8][3:1];     // 非法!




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

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

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

赋值的区别:

    rega[2] = 1'b1;              // 合法:对寄存器的某一位赋值
    mema[2] = 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 [3:0] a;
    b = &a;    // 等价于 b = ((a[0] & a[1]) & a[2]) & a[3]

若 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 [3:0] a, b;
    wire [5:0] c;
    c = {a[3:1], b[2:0]};   // 把 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 [7:0]  Data;
    wire [11:0] s_data;
    s_data = {{4{Data[7]}}, Data};   // 把符号位扩展到 12 位




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

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

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




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

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


[向量与存储器]

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


[运算符全家福]

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



收藏收藏5 转播转播 分享淘帖 赞1 踩
把本文推荐给朋友或其他网站上,有用户注册并在论坛充值消费,您将获得多层会员奖励.
回复

使用道具 举报

沙发
发表于 2026-7-24 21:49:04 手机版 | 只看该作者
顺着老哥之前的帖子一路找过来了,我是准备明年考研的,也是计算机系的,想找老哥要点推荐的资料和课程
回复 支持 反对

使用道具 举报

板凳
发表于 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?
回复 支持 反对

使用道具 举报

5#
发表于 2026-7-27 13:07:10 手机版 | 只看该作者
下一篇写个 Vivado入门指南 手搓一个risc v出来
回复 支持 反对

使用道具 举报

6#
 楼主| 发表于 2026-7-27 14:38:30 | 只看该作者
cheec 发表于 2026-7-27 13:07
下一篇写个 Vivado入门指南 手搓一个risc v出来

那得拍到比较后面了,先把verilog语法讲清楚,我再想想vivado教程怎么写吧
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 免费注册
点击进行验证

本版积分规则

我们不生产资源,只做资源的搬运工。

广告合作-tags标签-app下载-Archiver-小黑屋-