Verilog学习笔记——有符号数的乘法和加法

2023-11-02

     有符号数的计算在 Verilog 中是一个很重要的问题(也很容易会被忽视),在使用 Verilog 语言编写 FIR 滤波器时,需要涉及到有符号数的加法和乘法,在之前的程序中我把所有的输入输出和中间信号都定义成有符号数,这样在计算时没有出现问题(实际在之前的程序中遇到了有符号和无符号数的问题,最后滤波结果不对,博客的程序是已经改正过的),下面实际试验一下 Verilog 的乘法问题;

1. 编写程序测试无符号数和有符号数的乘法

     编写程序如下,其中,乘法的两个乘数分别是无符号、有符号的四种组合,输出的积也是分为无符号和有符号,共计 8 种可能;

module signed_test(
    input           [7:0]   data_in_unsigned_1,
    input           [7:0]   data_in_unsigned_2,

    input   signed  [7:0]   data_in_signed_1,
    input   signed  [7:0]   data_in_signed_2,
    
    output          [15:0]  data_out_000,
    output          [15:0]  data_out_001,
    output          [15:0]  data_out_010,
    output          [15:0]  data_out_011,
    
    output  signed  [15:0]  data_out_100,
    output  signed  [15:0]  data_out_101,
    output  signed  [15:0]  data_out_110,
    output  signed  [15:0]  data_out_111
);

//无符号 = 无符号 * 无符号 
assign data_out_000 = data_in_unsigned_1 * data_in_unsigned_2;
//无符号 = 无符号 * 有符号 
assign data_out_001 = data_in_unsigned_1 * data_in_signed_2;
//无符号 = 有符号 * 无符号 
assign data_out_010 = data_in_signed_1   * data_in_unsigned_2;
//无符号 = 有符号 * 有符号 
assign data_out_011 = data_in_signed_1   * data_in_signed_2; 

//有符号 = 无符号 * 无符号 
assign data_out_100 = data_in_unsigned_1 * data_in_unsigned_2;
//有符号 = 无符号 * 有符号 
assign data_out_101 = data_in_unsigned_1 * data_in_signed_2;
//有符号 = 有符号 * 无符号 
assign data_out_110 = data_in_signed_1   * data_in_unsigned_2;
//有符号 = 有符号 * 有符号 
assign data_out_111 = data_in_signed_1   * data_in_signed_2; 
    
endmodule

     生成的 RTL 图如下:
在这里插入图片描述
     可以看到,输出的积和符号无关,有符号数和无符号数实际上是同一个数,只看我们怎么定义它,比如乘积是 16 位的二进制 16’b1100_0000_0000_0011,当我们认为它是无符号数是,最高位的 1 就不是符号位,而是 2^15(2的15次方),这样这个数代表的十进制是 2^15 + 2^14 + 2^1 + 2^0 = 49155;

     如果把 16 位的二进制 16’b1100_0000_0000_0011 当成是一个有符号数来看,那么最高位是符号位,且剩下的数据时原来的数据二进制表示后取反再加1(补码表示),要计算它对应的十进制数
(1)先去掉符号位,保留剩下的 15-bit 的 100_0000_0000_0011;
(2)把 100_0000_0000_0011 取反,得到 011_1111_1111_1100;
(3)把 011_1111_1111_1100 的最低位 + 1,得到 011_1111_1111_1101;
(4)011_1111_1111_1101 按照无符号数换算成十进制是 16381;
(5)把最高位符号位加上,0代表正数,1代表负数,所以最后换算是 -16831;

     Windows 计算器默认最高位是符号位;

测试数据如下:
在这里插入图片描述

initial begin 
        data_in_unsigned_1 = 8'hff; //255
        data_in_unsigned_2 = 8'hf0; //240
    
        data_in_signed_1 = 8'hff;   //-1
        data_in_signed_2 = 8'hf0;   //-16
    
    #200;
        data_in_unsigned_1 = 8'hff; //255
        data_in_unsigned_2 = 8'h0f; //15
        data_in_signed_1 = 8'hff;   //-1
        data_in_signed_2 = 8'h0f;   //15
    
    #200;
        data_in_unsigned_1 = 8'd127; //127
        data_in_unsigned_2 = 8'd15;  //15
        
        data_in_signed_1 = -8'sd127;  //-127,十进制有符号数赋值,必须要用 sd 表示
        data_in_signed_2 = -8'sd15;   //-15
    
    #200;     
        data_in_unsigned_1 = 8'd128; //128
        data_in_unsigned_2 = 8'd15;  //15       
        data_in_signed_1 = -8'sd128;  //-128
        data_in_signed_2 = -8'sd15;   //-15
    
    #200;
        data_in_unsigned_1 = 8'd127; //127
        data_in_unsigned_2 = 8'd15;  //15           
        data_in_signed_1 = -8'sd127;  //-127
        data_in_signed_2 = 8'sd15;    //15
        
    #200;     
        data_in_unsigned_1 = 8'd128; //128
        data_in_unsigned_2 = 8'd15;  //15               
        data_in_signed_1 = -8'sd128;  //-128
        data_in_signed_2 = 8'sd15;   //15
    
    #200;
        data_in_unsigned_1 = 8'd127; //127
        data_in_unsigned_2 = 8'd15;  //15          
        data_in_signed_1 = 8'sd127;  //127
        data_in_signed_2 = -8'sd15;   //-15
            
    #200;          
        data_in_unsigned_1 = 8'd127; //127
        data_in_unsigned_2 = 8'd15;  //15
        data_in_signed_1 = 8'sd127;  //127
        data_in_signed_2 = 8'sd15;   //15        
    
    #200;
    $stop;
end

2. 仿真分析

     计算的结果仿真如下:
在这里插入图片描述
     对上图分析:
     (1)在 0 ~ 400 ns,仿真中使用十六进制赋值相同的十六进制数据给乘数,让乘数分别以无符号数和有符号数进行读取,可以看到对 8’hff(对应二进制 8’b1111_1111)以无符号数读取时是按照 原码 读取,对应十进制 255,以有符号数读取时是按照补码读取,按照上文所说的去掉符号位后取反、加1再计算十进制得 -1;
     (2)直接赋值十进制数据,乘数在以无符号数读取时时按照原码读取,127就对应 8 位二进制数 8’b0111_1111,十进制 128 就对应 8 位二进制 8’b1000_0000;而以有符号数读取的时候是会直接转换为补码形式,如 -127,先去掉符号位是 127,对应 7 位二进制数 7’b111_1111,取反为 7’b000_0000,加 1 为 7’b000_0001,将符号位补回到最高位为 8’b1000_0001;对于 -128 的表示比较特殊,8-bit的二进制数最高位是符号位,表示正负,剩下的 7-bit 能够表示的数的范围是 0 ~ 127,前面加上 ± 就能表示 -127 ~ 127,其中有 2 个数很特殊就是 8’b0000_0000 和 8’b1000_0000,按照上面会出现 +0 和 -0,为了区分出这两个数,前人定义 8’b0000_0000 表示 0,而 8’b1000_0000 表示 -128,这样不仅能区分开两个数,还多表示了一个数 -128(整个计算机体系通用,其他位数时类似表示一个负数);
在这里插入图片描述
     (3)实际上,观察下图数据可以发现,只有data_out_000 和 data_out_111 的数据时全部计算正确的,这也符合常理:
     无符号 * 无符号 = 无符号(只有0和正数);
     有符号 * 有符号 = 有符号(0,可正可负);
其它的计算为什么会出错呢?实际上这里遵循一个原则:
     如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数;
     这样也就解释了 0 ~ 400 ns 时的 data_out_001 和 data_out_010 的计算结果和 data_out_000 完全一致,它们都是把赋值的 8 位十六进制数当做无符号数计算的(这里不存在十进制到二进制原码、补码换算的问题,因为给的是十六进制);
     当后面设计输入输出时,如果是有符号,那么将相关计算的输入/输出和中间量都显式的用 signed 定义;
在这里插入图片描述

3. 有符号数乘法的另一种计算

     前面说的计算时将涉及到的相关量全部定义为有符号数是一种计算方法,此外,通常情况下可能会定义的无符号数,但是实际传入的是有符号数,比如下面的输入和输出都没有指定成 signed 有符号数,计算时默认是按照无符号数计算(实际上我感觉是把读取到的 8 位二进制数当做原码去算),此时若外部传入的数据实际上是有符号数(比如 FIR 滤波器传入了正负均有的待滤波信号),那么需要对符号位进行扩展来计算乘法和加法;

module signed_test_2(
input [7:0] data_in_1,
input [7:0] data_in_2,
output [15:0] data_out_1,
output [15:0] data_out_2
);
     对于乘法,需要扩展符号位 到 和积的位数相等,比如乘数a为 N-bit,乘数 b 为M-bit,两个相乘得到 N+M 位数据,此时需要对 a 扩展 M-bit 到 N+M 位,对 b 扩展 N-bit 到 N+M 位;
     下面,使用 位拼接符 { } 来做演示,位拼接符可以按照二进制的位来进行高低位的拼接,假设 data_in_1= 8’b1000_0011,对于 {{8{data_in_1[7]}},data_in_1} 可以这样理解:
     (1)先看 8{data_in_1[7]},表示取出 8-bit 数据 data_in_1 的最高位 data_in_1[7],重复 8 次,相当于
{ data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7] },
即高位扩展 8-bit 的 1;
     (2){{8{data_in_1[7]}},data_in_1} 相当于在 data_in_1 的前面补上 8 个 data_in_1[7],即 结果为 16-bit 的 16’b1111_1111_1000_0011;

//不做符号位扩展,直接相乘
assign data_out_1 = data_in_1 * data_in_2;
//做符号位扩展,再相乘
assign data_out_2 = {{8{data_in_1[7]}},data_in_1} * {{8{data_in_2[7]}},data_in_2};

     仿真测试数据如下,1 处用十六进制给出数据,2 处用有符号的十进制赋值,3 处是为了和 2 处对比,看最后赋值是否一样(看到有博客说 3 的赋值是错的,所以测试一下);
在这里插入图片描述
     仿真结果如下,可以看到上图 2 处和 3 处的赋值在仿真时是同样的数据,把所有数据都用有符号的十进制数显示(右键数据 Radix -> Signed Decimal);
在这里插入图片描述
     可以看到,data_out_1的结果是错的(没有补符号位),data_out_2的结果是对的(补符号位);
在这里插入图片描述
     对有符号数的加法,同样的,要么相关的运算全部定义成有符号数,要么进行符号位的扩展,对于加法操作,只需要每个被加数扩展 1 位符号位即可;

除此之外,还可以调用乘法器的 IP 来代替 乘法符号 *,或者加法器的 IP 来代替 加法符号 +,在 IP 核中配置输入输出为有符号数即可。

MATLAB 与 FPGA无线通信、图像处理、数字信号处理系列 汇总

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Verilog学习笔记——有符号数的乘法和加法 的相关文章

  • 在 C 中操作 80 位数据类型

    我正在用 C 实现一些加密算法 其中涉及 80 位密钥 特定操作涉及将密钥旋转移位 x 个位数 我已经尝试过 long double 类型 如果我没记错的话 它是 80 位 但这不适用于位移运算符 我能想到的唯一替代方案是使用 10 个元素
  • Verilog 中总是后面跟着 #(...) pound 是什么意思?

    在一个简单的时钟生成器示例中 我看到以下代码 always cycle 2 clk clk 我以前总是见过 但没见过井号 我试图在文档中找到它 但我所能找到的只是一些对 实值端口 的引用 没有进一步的阐述 这是一个延迟操作 它本质上只是读取
  • 如何生成异步复位verilog总是阻塞凿子

    Chisel 始终生成敏感度列表中仅包含时钟的块 always posedge clk begin end 是否可以将模块配置为使用异步重置并生成这样的始终块 always posedge clk or posedge reset begi
  • 使用 Verilator 和 VPI 读取寄存器数组

    所以我在我的verilog中定义了以下寄存器 reg 31 0 register mem 0 15 verilator public 我的目标是从我的 verilator c 代码中读取存储在其中的 16 个值中的每一个 我发现有关 VPI
  • Verilog 最佳实践 - 递增变量

    我绝不是 Verilog 专家 我想知道是否有人知道这些增加值的方法中哪一种更好 抱歉 如果这个问题太简单了 Way A 在组合逻辑块中 可能在状态机中 some condition count next count 1 然后在一个连续块中
  • Verilog:添加寄存器的各个位(组合逻辑,寄存器宽度可参数化)

    我正在尝试想出一种方法来添加寄存器的各个位 例如 if regA 111000 then regB 3 位的总和regA 1 Verilog或SystemVerilog中是否有可以直接使用的可综合函数 运算符来执行此操作 如果不是 那么问题
  • UIO 设备上的 mmap EINVAL 错误

    在尝试使用 UIO 而不是直接映射后 我在 Xilinx Zynq 上映射物理内存时遇到问题 dev mem 虽然计划是以普通用户身份运行应用程序 而不是root这仍在运行root 显然 第一个映射成功 其余映射到同一个文件描述符12 de
  • 模拟器和合成器之间初始化状态机的差异

    我的问题是关于合成状态机中使用的第一个状态 我正在使用莱迪思 iCE40 FPGA 用于仿真的 EDA Playground 和用于综合的莱迪思 Diamond Programmer 在下面的示例中 我生成一系列信号 该示例仅显示引用状态机
  • Verilog 错误:必须连接到结构网络表达式

    我收到错误 output or inout port Qout must be connected to a structural net expression 我评论了下面代码中发生错误的行 代码被修剪 压缩 我搜索了答案 似乎我无法将输
  • 赋值语句中的“others=>'0'”是什么意思?

    cmd register process rst n clk begin if rst n 0 then cmd r lt others gt 0 elsif clk event and clk 1 then cmd r lt end if
  • 对象 <名称> 未声明

    这是我的代码 据我所知 LEDs被定义为 module sevenseg LEDs in output reg 6 0 LEDs input 3 0 in always in begin case in 0 LEDs 7 b1000000
  • 开始后跟冒号和变量是什么意思?

    什么是data mux意思是这里 它只是块的名称吗 if PORT CONFIG 32 P0 1 b1 begin data mux end 这些是块名称 它们特别适用于generate块 例如 您可以定义一个generate块如 genv
  • 从 OpenCV 代码到 FPGA 代码的转换是否比 Matlab 代码更容易? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想做一个关于图像处理的项目 我想知道如果我想在FPGA上实现这个项目 我应该在第一阶段选择Matla
  • 映射 MMIO 区域写回不起作用

    我希望对 PCIe 设备的所有读写请求都由 CPU 缓存进行缓存 然而 它并没有像我预期的那样工作 这些是我对回写 MMIO 区域的假设 对 PCIe 设备的写入仅在缓存回写时发生 TLP 有效负载的大小是缓存块大小 64B 然而 捕获的
  • 如何从 Spartan 6 写入 Nexys 3 FPGA 板上的 Micron 外部蜂窝 RAM?

    我到处都查过了 数据表 Xilinx 网站 digilent 等等 但什么也没找到 我能够使用 Adept 工具来验证我的蜂窝 RAM 是否正常运行 但我找不到任何库存 VHDL 代码作为控制器来写入数据和从中读取数据 帮助 找到了此链接
  • 在 Verilog 程序中使用连续分配?

    在 Verilog 程序中使用连续赋值是否可能和 或有用 例如 是否有任何理由将assign里面一个always堵塞 例如这段代码 always begin assign data in Data end 此外 是否可以用这种方法生成顺序逻
  • Verilog 按位或 ("|") 单子

    我见过 Verilog 代码 其中使用了按位或运算符 目的是什么 例如 address 15 14 0 or address 15 14 io din ramrd 不能省略 吗在这些情况下 在这种情况下 它充当归约运算符 例如 4 b100
  • Verilog 双向握手示例

    我正在完成一个项目 要求是处理器内部功能单元之间的双向握手 我知道它是什么 但是有没有任何 标准 或一个简单的例子 我唯一能想到的就是两个单元之间 当它们之间有一条数据线并且当 X 发送到 Y 时 会给出一个单独的 发送 信号 当 Y 接收
  • 仿真输出全为零

    我的设计模块和测试平台代码已编译 但是 当我模拟时 我没有得到正确的输出 谁能告诉我我的代码哪里出了问题 这是测试平台的代码 module testbench reg 511 0 FROM LS reg CLK reg 63 0 TO IF
  • VHDL 中奇怪的 XNOR 行为

    导致问题的代码看起来像正常的 xnor 操作 如下所示 S 1 lt L 16 xnor L 26 该行会导致以下错误 ncvhdl p E EXPSMI HDL aes sbox enc depth16 vhd 169 14 expect

随机推荐

  • 【linux系统基础知识-Shell脚本学习笔记12-循环】

    12 1 循环说明解释 循环是可以使你多次执行一系列命令 循环包括 for循环 while循环 select循环 for do done while do done until do done 12 2 for循环 for循环使你对列表中的
  • 用TW8836驱动ST7701S TTL屏调试记录

    近段时间做一个项目 要调试3 2寸320x820分辨率的LCD 在此做下记录 屏规格书如上图 屏的主要接口如上图 1 查看屏的规格书 如图所示 需要8836和st7701s通讯 方式是3线SPI 2 通讯接口SDA SCK CS 3 RGB
  • zabbix搭建

    1 环境 本实验使用一台centos7主机 关闭了firewalld和selinux服务 zabbix版本为5 0版本 mysql使用版本为5 7版本 若要搭建6 0以上版本的zabbix 则需要使用mysql 8 0以上的版本 其它版本的
  • 微信公众号获取用户的openid

    公众号可获得关注者的OpenID 加密后的微信号 每个用户对每个公众号的OpenID是唯一的 对于不同公众号 同一用户的openid不同 公众号可通过本接口来根据OpenID获取用户基本信息 包括昵称 头像 性别 所在城市 语言和关注时间
  • shell 脚本中 $$、$#、$? 分别代表什么意思?

    0 这个程式的执行名字 n 这个程式的第 n 个参数值 n 1 9 这个程式的所有参数 此选项参数可超过 9 个 这个程式的参数个数 这个程式的 PID 脚本运行的当前进程 ID 号 执行上一个背景指令的 PID 后台运行的最后一个进程的进
  • Ubuntu16.04 ARM/Qt 交叉编译环境搭建

    嵌入式开发 Ubuntu16 04 ARM Qt 交叉编译环境搭建 背景 环境说明 安装交叉编译工具 下载Qt源码包 编译Qt源码 安装QtCreator 配置QtCreator 应用QtCreator交叉编译 Ubuntu16 04 AR
  • springboot配置双mysql数据源

    这两天一直在配置双数据源 找了网上很多资料 有的资料写的太乱而且注释不清楚 类不全 像我这样的刚开始配置的新手很难看明白 今天终于配置成功了 我把我总结的整理一下 做个日志以防以后遇到问题 一 创建一个springboot项目其中需要的po
  • h5页面判断 js判断 是否安装APP,如果安装就拉起APP 打开app ,否则就下载

    h5页面判断是否安装APP 如果安装就拉起APP 否则就下载 if navigator userAgent match iPhone iPod iPad i var loadDateTime new Date window location
  • PHP[多维数组转字符串]和{多维数组转一维数组}

    http blog csdn net aoyoo111 article details 8554585 php view plain copy print method 多维数组转字符串 param type array return ty
  • 基于Echarts4.0实现旭日图

    昨天Echarts4 0正式发布 随着4 0而来的是一系列的更新 挑几个主要的简单说明 1 展示方面通过增量渲染技术 4 0 ECharts 能够展现千万级的数据量 2 针对移动端优化 移动端小屏上适于用手指在坐标系中进行缩放 平移 可选的
  • 探索AIDL(1) -- 初识AIDL

    探索AIDL 1 初识AIDL 前言 1 在讨论这个问题之前必须先理解IPC的概念 IPC全称是Inner Process Communication 即跨进程通信 是指两个进程进行数据交换的过程 2 如果要传递对象必须先进行序列化 序列化
  • RV1126 SDK编译错误及解决记录

    RV1126 SDK编译错误及解决记录 1 错误 you need to install unbuffer from package expect or expect dev log saved on home h00003 RV1126
  • Win11怎么设置开机启动项?

    我们在使用电脑的时候经常会打开非常多的软件 而每次开机都需要手动去点击 就会变得非常的麻烦 那么在Win11操作系统中我们应该怎么设置呢 其实方法非常简单 下面小编就带着大家一起看看吧 操作方法 方法一 1 首先 按键盘上的 Win 键 或
  • 关于线索二叉树的详解

    线索二叉树的详解 目录 线索二叉树的详解 前言 一 线索二叉树是什么 三种二叉树线索化实例图 二 实现线索二叉树 1 二叉树的线索化 2 线索二叉树的遍历 中序线索二叉树寻找遍历的首节点 中序线索二叉树寻找节点的直接后继 遍历中序线索二叉树
  • 【国仁网络资讯】个人如何快速学会制作短视频的方法技巧

    对于微信视频号运营中 个人创作者们了解到视频号的一般设置选项 以及选择完毕视频号的创作领域方向之后 那就需要创作出短视频出来 就好比我们写文章需要有作品才可以 如果我们有创作能力的 那可以根据我们标准的风格 以及创作标准元素创作和分享就可以
  • python 读取图片 图片预处理 二值化

    python 读取图片 图片预处理 二值化 需求 毕设项目 需要从文件夹中批量读取文件 并将图片二值化处理 def preprocessing image directory in name directory out name 准备储存之
  • Firefox 中文语言包安装方法

    一 自动安装法 在mozilla的FTP上找到的 http ftp mozilla org 选择版本和对应操作系统http releases mozilla org pub mozilla org firefox releases 语言包在
  • dd命令使用详解

    1 命令简介 dd 的主要选项 指定数字的地方若以下列字符结尾乘以相应的数字 b 512 c 1 k 1024 w 2 xm number m if file 输入文件名 缺省为标准输入 of file 输出文件名 缺省为标准输出 ibs
  • HTML <rt> 标签

    实例 一个 ruby 注释
  • Verilog学习笔记——有符号数的乘法和加法

    有符号数的计算在 Verilog 中是一个很重要的问题 也很容易会被忽视 在使用 Verilog 语言编写 FIR 滤波器时 需要涉及到有符号数的加法和乘法 在之前的程序中我把所有的输入输出和中间信号都定义成有符号数 这样在计算时没有出现问