Verilog基础:表达式位宽的确定(位宽拓展)

这篇具有很好参考价值的文章主要介绍了Verilog基础:表达式位宽的确定(位宽拓展)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

相关文章

Verilog基础专栏https://blog.csdn.net/weixin_45791458/category_12263729.html


表达式位宽

        如果想要在计算表达式时获得和谐一致的结果,那么控制表达式中的位宽就很重要。很多时候方法很简单。例如,如果在两个16位数据的reg变量上做位与操作,那么计算结果很显然就是16位。但是在某种情况下,计算应该用多少位或者结果应该是多少位就不那么明显。

        例如,对两个16位数据做加法操作是选择用16位进行计算呢,还是为了包含可能的进位而选择用17位进行计算呢?这里就牵扯到了Verilog用来确定表达式位宽的规则。

例1
    reg [15 : 0] a, b;
    reg [15 : 0] sumA;
    reg [16 : 0] sumB;
    sumA = a + b;//赋值表达式右端按照a和b都为16位来计算,结果仍为16位,最后赋值给sumA且进位溢出;
    sumB = a + b;//a和b首先根据规则补零拓展(选择这种拓展是因为赋值表达式右端存在无符号数a,b)到17位,然后再执行相加,结果为17位,最后赋值给sumB,因此进位得以保留。

表达式位宽规则

为了在现实的情况下方便地解决位宽问题,Verilog规定了如下的表达式位宽规则。

  1. 表达式的位宽由表达式中的操作数本身或表达式所处的上下文决定。Verilog把所有表达式中的操作数分为自决定和上下文决定两类。

  1. 自决定表达式(self-determined expression)就是表达式(或整个表达式中的子表达式)中所有操作数的位宽完全由自己决定。

  1. 上下文决定表达式(context-determined expression)就是表达式(整个表达式中的子表达式)中所有操作数的位宽由整个表达式上下文环境中最大的位宽决定。

  1. 混合自决定和上下文决定表达式就是表达式(或整个表达式中的子表达式)中操作数部分自决定,部分上下文决定。

下面的图1和图2给出了表达式的位宽规则。注:图2来自官方文档。

Verilog基础:表达式位宽的确定(位宽拓展)

图1 各操作数位宽规则

Verilog基础:表达式位宽的确定(位宽拓展)

图2 各表达式位宽规则

        根据以上两图,我们就可以知道例1的表达式位宽计算过程。首先我们从图1得知,=操作符右端的操作数(不管是单个操作数还是子表达式作为操作数,以下不经特殊说明的操作数均作此解释)是由上下文环境决定位宽的,且根据注释我们还可以得知除了=右端的操作数,=操作符左端的操作数也会加入到上下文环境中。这是什么意思呢?我们可以用例1进行演示。

        sumA = a + b;执行这个语句时,+号两端的操作数是上下文环境决定位宽,且=左边也被加入上下文环境,所以sumA,a,b都被加入上下文环境,此时这三个变量的位宽是相同的16位,所以没有数据会拓展,可以直接进行运算,根据图2,a+b结果的位宽为max(L(a), L(b)),所以是16位,进位被舍去,最后将右端的16位值赋给左端的sumA;

        sumB=a+b;前面的步骤相同,当sumB,a,b都被加入上下文环境后,最大的位宽为17位,所以a,b首先被位补0拓展至17位,然后再执行加法得到17位的结果,因此保留了进位,最后将右端的17位值赋给左端的sumB;

        我们可以多看几个例子来有一个更加形象的认识。

例2
    reg [5 : 0] a = 6'b010101;
    reg [3 : 0] b = 4'b1111;
    reg [7 : 0] c;
    c = a & b;

        在例2中,a&b这个自决定作为子表达式成为了=右边的操作数,即是上下文决定位宽的,且我们根据图1又可以知道&运算符两边的操作数也是上下文决定位宽,也就是说,和例1的加法一样,如果一个表达式作为子表达式(或说操作数)会被加入上下文环境(作为=的右操作数),且表达式自己也是上下文决定表达式,那么表达式内部的所有操作数(a,b)都会被加入上下文环境,即上下文环境的嵌套不改变操作数的上下文性质。所以计算过程为:c,a,b都会被加入上下文环境,最大的位宽是8位,所以a,b首先被补零拓展至8位,然后执行按位与运算,根据图2,运算结果仍然是8位,最后将8位结果赋值给左端的c;过程如下面所示。

Verilog基础:表达式位宽的确定(位宽拓展)

Verilog基础:表达式位宽的确定(位宽拓展)

例3
    reg [5 : 0] a = 6'b010101;
    reg [3 : 0] b = 4'b1111;
    reg [7 : 0] c;
    c = a & (& b);

        在例3中,只有最后一步是有变化的,按位与操作符&的右操作数被换成了一个缩减运算子表达式,如果子表达式仍然是上下文决定表达式,那这和上面的两个例子没有任何差别,即a,b,c都会被加入上下文环境,只不过是嵌套的计数不同。但我们从图1,图2都可以注意到,缩减运算符的操作数是自决定的!这时的运算规则就有变化,自决定运算符中的变量不会被加入上下文环境,即b不会被加入上下文环境,而是由自己决定,即b的位宽就是6位,但(&b)作为一整体成为按位与操作符&的右操作数,它的结果的位宽还是会被加入上下文环境(a,c没有这种问题,像上面两例一样被加入上下文环境)。根据上面的规则,我们知道缩减运算的结果是1位,小于c的8位,所以此时最大位宽依然是8位。所以(&b)的结果和a都会被补零拓展至8位,然后按位与,得到结果是8位,最后赋值给c。过程如下面所示。

Verilog基础:表达式位宽的确定(位宽拓展)

Verilog基础:表达式位宽的确定(位宽拓展)

例4
    reg [15 : 0] a, b, answer;
    answer = (a + b) >> 1;

        如果你搞明白了上面三个例子,那么例4你就能自己解决。在看答案前,先自己按照规则尝试着想出表达式的运算过程。

答案:

        这里涉及到了一个混合运算符——右移>>,由规则我们可以知道,此运算符的左边即被移操作数为上下文决定,右边即移位量为自决定。我们可以首先找到被加入上下文环境的操作数,a和b作为+操作符的操作数,是上下文决定的,(a+b)作为整体位与移位操作符的左边也是上下文决定的,(a+b)>>1作为整体是=操作符的右操作数,也是上下文决定的,因此answer(因为=),a,b(因为三层上下文嵌套)都被加入上下文环境中,这三个变量的位宽都是一样的,所以他们三个在运算前不会有拓展。因此首先执行a+b,根据规则,结果为16位,进位被丢失,然后再右移一位,最高位补0,最后赋值给同为16位的answer。这里的1(不加基数,位宽的常数)默认是32位,且为自决定。

如果想要进位不被丢失应该怎么样?你可以先自己想一想,方法有很多。

  1. 把a+b;改成a+b+0;因为0(32位)此时和a,b一样被加入了上下文环境,所以此时最大位宽为32,所以a,b都会被补零拓展到32位,所以进位得以保留,两次加法结果任然是32位,移位后结果任然是32位,最后将32位值赋值给16位的answer,高位被截断。

  2. 把a+b;改成a+b+17'b0;原因同上,此时a,b被补零拓展至17位,所以相加后能保留进位。

  3. 把reg [15:0]answer;改为reg [16:0]answer;原因也是类似的,=左端的answer被添加到上下文环境后,最大位宽变为17,此时a,b被补零拓展至17位,所以相加后能保留进位。

例5
    reg [3 : 0] a, b, c;
    reg [4 : 0] d;
    initial begin
        a = 9;
        b = 8;
        c = 1;
        $display("answer = %b", c ? (a & b) : d);
    end

        运行结果是什么,为什么是这样?(注意,这里的表达式没有了赋值运算符,这是与之前的例子最大的区别)

        答案:运行结果为answer = 01000。

        从规则中我们知道,三目运算符的第一个操作符(条件项)是自决定的,而第二和第三操作符是上下文环境决定,而在这里第二操作符又是一个以上下文决定子表达式,根据上下文嵌套的规则,a,b和d都被加入上下文环境,最大位宽为5,c不会加入上下文环境,也不会受上下文环境影响。所以a,b被补零拓展为5位,然后执行与运算得到5位结果01000,然后根据c等于1,最后表达式的结果为01000(三目运算符结果的位宽为第二和第三操作数中最大的那个,在此例中都为5)。

例6
    reg [3 : 0] a;
    reg [5 : 0] b;
    reg [15 : 0]c;
    initial begin
        a = 4'hF;
        b = 6'hA;
        $display("a * b = %h", a * b);
        c = {a ** b};
        $display("a ** b = %h", c);
        c = a ** b;
        $display("c = %h", c);
    end

运行结果如下所示:

a * b = 16

a ** b = 1

c = ac61

解析:在第一个系统函数中,只有一个简单的乘式,又因为*两边的操作数是上下文决定的,将a和b加入上下文环境中,最大的位宽为6位,所以a首先被补零拓展到6位,然后和b相乘,结果根据规则,也是6位,假设位宽无限,那么结果为96h也就是10010110b,但是因为结果只取低六位即010110,所以以16进制展现出来就是16。

        第二个系统函数用来展示c,c在之前被赋值,c = {a ** b};我们发现a**b居然被放在了拼接运算符里面,这看上去拼接没有什么影响,如果你是这么觉得,你就会得到意想不到的答案。我们一步一步来,**乘方运算符的第一个操作数(底数)是上下文决定的,而第二个操作数(指数)是自决定的,按理说a应该被加入上下文环境,但在这里a**b作为自决定表达式拼接运算符{}的子表达式,所以a与b会被强制转换为自决定,所以在这里,意思就是上下文的嵌套不能穿过自决定表达式,在这里只有{a ** b}会作为=运算符的右操作数和c被加入上下文环境(就像例3一样),所以直接按原本的位宽计算a**b,按照规则,乘方运算结果的位宽与底数位宽相同,所以结果为4位,即取二进制数1000011001000011000010101010110001100001的低4位,也就是0001,上下文环境中最大位宽为16位,所以0001被补零拓展至0000000000000001,最后赋值给c。

        最后一个系统函数,展示的是没有拼接运算符{}的赋值结果,此时上下文关系得以传递,a和c都被加入上下文环境,a首先被拓展至16位,然后执行乘方,乘方运算结果的位宽与底数位宽相同,所以结果为16位,即取二进制数1000011001000011000010101010110001100001的低16位,也就是ac61h。

        相信通过以上几个例子,你已经可以自己解决关于表达式运算过程中的位宽问题了,但还有一个问题,为什么这里面遇到的都是补零拓展呢,什么时候会遇到符号拓展呢?但这又成为了一个新的专题,感兴趣的读者可以参看下面的文章。

Verilog基础:表达式符号的确定_日晨难再的博客-CSDN博客如果所有操作数有符号,结果才是有符号的。有两个系统函数就是为了调整表达式符号,分别是$signed和$unsigned,它们的返回值就是被转换符号后的数值。否则必须在基数前声明s标志才表示这个常数为有符号的,如3'd4是无符号数,3'sd4是有符号数。拼接操作符的结果是无符号的,不管操作数是否有符号,即使是只有一个有符号操作数也是如此。域选的结果是无符号数,不管域选操作数是否有符号,即使域选的结果是整个向量。比较操作符的结果是无符号的,不管操作数是否有符号。位选的结果是无符号数,不管位选操作数是否有符号。https://blog.csdn.net/weixin_45791458/article/details/128840843?spm=1001.2014.3001.5502文章来源地址https://www.toymoban.com/news/detail-416957.html

到了这里,关于Verilog基础:表达式位宽的确定(位宽拓展)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安全基础 --- 正则表达式

    正则表达式(Regular Expression),简称为 正则 或 Regex ,是一个用来描述、匹配和操作字符串的工具。 限定字符 多用于重复匹配次数 常用限定字符: 语法 说明 ? 上一项是可选的,最多匹配一次。 * 前一项将被匹配零次或多次。 + 前一项将被匹配一次或多次。 {N} 上一项完全匹

    2024年02月15日
    浏览(36)
  • 正则表达式 基础

    Tips: 字符串方法match()返回根据正则表达式匹配到的结果 ^ 以...开头 $ 以...结尾 + 号,匹配前面的字符1 ~ n次 * 号,匹配前面的字符0 ~ n次 ? 号,匹配前面的字符0 ~ 1次 {n,} ,匹配前面的字符至少n次 {n,m} ,匹配前面的字符n ~ m次 [abc] 匹配[]中的所有字符 [^abc] 匹配除去[^]中的所

    2024年02月04日
    浏览(47)
  • 正则表达式基础

    正则表达式指一个模式串,该模式串可匹配到所有满足该模式串指定规则的字符串; 模式串本身不含有特殊字符,搜索包含模式串的所有字符串; 特殊字符 含义 ^ 匹配一行的开头,如^a表示匹配所有以a开头的字符串 $ 匹配一行的结尾,如a$表示匹配所有以a结尾的字符串 . 匹

    2024年02月05日
    浏览(34)
  • JavaScript正则表达式基础

    创建正则表达式,一般有两种写法 .test(\\\'需要验证的字符\\\'),正则的方法用来验证正则的匹配结果true or false 修饰符 写法:/正则/ 修饰符 示例:/abc/i (匹配字符串abc并且不区分大小) i:执行对大小写不敏感的匹配(不区分字母的大小写) g:执行全局匹配(查找所有匹配而非在

    2024年02月09日
    浏览(49)
  • python正则表达式-正则基础

    目录 一、任一元素 二、匹配特定的字符类别          1、d  w 三、多个元素          1、两位元素 [][]          2、* + ?          3、重复次数 {}          4、位置匹配 ^ $          5、子表达式()         []:1、[ab] 匹配a或b;        2、[0-9] 匹配任意一个数

    2024年02月05日
    浏览(45)
  • 【自学笔记】01Java基础-08Java常用API:05正则表达式与Lambda表达式

    记录Java基础-常用API-正则表达式与Lambda表达式的内容。 正则表达式在线练习网站 正则表达式是一种强大的文本处理工具,它使用特殊的字符和模式来匹配、查找、替换或提取字符串中的特定内容。 在Java中,正则表达式的功能主要通过 java.util.regex 包中的 Pattern 和 Matcher 类实

    2024年01月19日
    浏览(67)
  • MFC使用正则表达式基础步骤

    ①头文件包含 #include ②明确声明正则表达式 更多正则表达式可查阅:https://blog.csdn.net/Next_Second/article/details/126696589 ③CString 转string ④进行匹配 完整函数

    2024年02月16日
    浏览(38)
  • Python爬虫基础之正则表达式

    目录 一、什么是正则表达式? 二、re.compile()编译函数 三、group()获取匹配结果函数 四、常用匹配规则 4.1匹配单个字符 4.2匹配前字符次数 4.3匹配原生字符串 4.4匹配字符串开头和结尾 4.5分组匹配 五、re.match()开头匹配函数 六、re.search()全文搜索函数 七、re.findall()查找所有函数

    2024年02月10日
    浏览(46)
  • 【C#基础】C# 正则表达式

    序号 系列文章 7 【C#基础】C# 常用数据结构 8 【C#基础】C# 面向对象编程 9 【C# 基础】C# 异常处理操作 🌼 hello大家好啊,我是writer桑。前面一章已经学习了 C# 中的异常处理操作,那本章就开始学习 C# 程序中正则表达式的应用。关于正则表达式网上已经有很多现成的学习资源

    2024年02月03日
    浏览(37)
  • Shell编程基础(十三)正则表达式

    格式:^ 表达式 $ 在不同的场景下,定义是一样,但使用要按照具体的命令去调用,中间的表达式都是通用的 普通元字符 所谓元数据,就是描述数据的数据,在这里就是描述正则的数据 ^ 表示正则从字符串 整行 起始位置匹配 ^abc 匹配 以abc开始的字符串 $ 表示正则一直匹配到

    2024年02月14日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包