数字设计笔试Verilog手撕代码 - 无符号浮点加法器

news2024/11/16 22:27:22

前言

今天在网上看笔试题发现有个设计浮点累加器的题目,看了下题目说明感觉不太清楚,恰好记得之前做过浮点数的加法运算的设计,索性就改了下题目需求,作为一个小练习在重新设计一遍。具体设计要求如下:

设计需求

设计一个32bit浮点的加法器,out = A + B,假设AB均为无符号位,或者换个说法都为正数。

clk为系统时钟;rst_n为系统复位,低有效;en信号控制数据输入;busy指示模块工作状态,busy拉高时,输入无效;aIn和bIn是数据输入,out_vld,指示输出数据有效。

设计的信号列表如下:

module float_adder(
  input                clk,
  input              rst_n,
  input                 en, 
  input      [31:0]    aIn,
  input      [31:0]    bIn,
  output reg          busy,
  output reg       out_vld,   
  output reg [31:0]    out
);

32bit的浮点格式

EE标准754规定了三种浮点数格式:单精度、双精度、扩展精度。前两者正好对应C语言里头的float、double或者FORTRAN里头的real、double精度类型。本文设计实现的为单精度。

image-20230421163624150

单精度:N共32位,其中S占1位,E占8位,M占23位。

image-20230421163643098

双精度:N共64位,其中S占1位,E占11位,M占52位。

浮点数的加法过程

运算过程:对阶、尾数求和、规格化、舍入、溢出判断

对阶:

和定点数不相同的是,浮点数的指数量级不一定是一样的,所以这也就意味着,尾数直接进行加法运算时会存在问题,也就需要首先对阶数进行处理。该过程有点像科学计数法的加法处理,把科学计数法的指数化为一致,求出来指数相差多少,然后移位处理后再进行加法减法。所以这里处理也要先求阶差。

如果把阶码大的向阶码小的看齐,就要把阶码大的数的尾数部分左移,阶码减小。这个操作有可能在移位过程中把尾数的高位部分移掉,这样就引发了数据的错误,所以,尾数左移在计算机运算中不可取。

如果把阶码小的向阶码大的看齐,在移位过程中如果发生数据丢失,也是最右边的数据位发生丢失,最右边的数据位丢失,只会影响数据的精度,不会影响数据的大小。

尾数求和

这里就是常规的补码加法。

规格化:

右规(尾数的绝对值太大时,右规)
尾数右移一位,阶码加1。
当尾数溢出( >1 )时,需要右规
是否溢出,可以通过两位的符号位得出:
即尾数出现01.xx…xx或10.xx…xx(两位符号位不同)

提高浮点数的表示精度,这里设计考虑比较简单,我只考虑了同号数据相加的情况,所以这里只设计了右规的情况,不考虑符号位。

舍入判断:

这里直接用截断处理的方式,针对数据相加上溢的情况,规定了运算后上溢后将数据规定为最大值。

实现代码

module float_adder(
  input                clk,
  input              rst_n,
  input                 en, 
  input      [31:0]    aIn,
  input      [31:0]    bIn,
  output reg          busy,
  output reg       out_vld,   
  output reg [31:0]    out
);
//运算过程:对阶、尾数求和、规格化、舍入、溢出判断
//分离阶数、尾数
wire signal_bit = aIn[31];
wire [7:0] pow_a = aIn[30:23];
wire [7:0] pow_b = bIn[30:23];

wire [22:0] val_a = aIn[22:0];
wire [22:0] val_b = bIn[22:0];

//找到输入指数阶数较大,和阶数差
//对阶:在计算机中,采用小阶向大阶看齐的方法,实现对阶。即右移
reg [22:0] pow_max ;
reg [23:0] pow_dif ;
reg [22:0] val_max ;
reg [22:0] val_min ;
reg en_dly0;
always @(posedge clk or negedge rst_n) begin
  if(rst_n==0)begin
    pow_max <= 'd0;
    val_max <= 'd0;
    val_min <= 'd0;
    pow_dif <= 'd0;
    en_dly0 <= 'd0;
  end
  else if( en == 1 && busy == 0)begin
    if(pow_a >= pow_b)begin
      pow_max <= pow_a;
      val_max <= val_a;
      val_min <= val_b;
      en_dly0 <= 'd1;
      if ( pow_a - pow_b > 'd23) begin
        pow_dif <= 'd23;
      end 
      else begin
        pow_dif <= pow_a - pow_b;
      end
    end
    else begin
      pow_max <= pow_b;
      val_max <= val_b;
      val_min <= val_a;
      en_dly0 <= 'd1;
      if ( pow_b - pow_a > 'd23) begin
        pow_dif <= 'd23;
      end 
      else begin
        pow_dif <= pow_b - pow_a;
      end
    end
  end
  else begin
    pow_max <= pow_max;
    val_max <= val_max;
    val_min <= val_min;
    pow_dif <= pow_dif;
    en_dly0 <= 'd0;
  end
end

//移位忙指示信号
reg shift_busy;
reg [4:0] shift_cnt;
always @(posedge clk or negedge rst_n) begin
  if (rst_n==0) begin
    shift_busy<='d0;
  end
  else if(en_dly0 == 1 )begin
    shift_busy <='d1;
  end
  else if(shift_cnt == pow_dif)begin
    shift_busy <=  0;
  end
end

//移位计数

always @(posedge clk or negedge rst_n) begin
  if(rst_n == 0)begin
    shift_cnt <= 'd0;
  end
  else if (shift_busy ==1) begin
    if (shift_cnt == pow_dif) begin
      shift_cnt <= shift_cnt;
    end
    else begin
      shift_cnt <= shift_cnt + 1'b1;
    end
  end
  else begin
    shift_cnt <= 'd0;
  end
end
reg [22:0] val_shift;
always @(posedge clk or negedge rst_n) begin
  if(rst_n == 0)begin
    val_shift <= 'd0;
  end
  else if (en_dly0==1'b1) begin
    val_shift <= val_min;
  end
  else if (shift_busy == 1) begin
    val_shift <= {1'b0,val_shift[22:1]};
  end
  else begin
    val_shift <= val_shift;
  end
end

//尾数求和
wire val_add_flag = (shift_cnt == pow_dif)&&(shift_busy ==1);
reg [23:0] val_sum;
reg val_sum_vld;
always @(posedge clk or negedge rst_n) begin
  if (rst_n==0) begin
    val_sum<='d0;
    val_sum_vld<='d0;
  end
  else if(val_add_flag == 1)begin
    val_sum <= val_max + val_shift;
    val_sum_vld<='d1;
  end
  else begin
    val_sum <= val_sum;
    val_sum_vld<='d0;
  end
end

//规范
always @(posedge clk or negedge rst_n) begin
  if (rst_n==0) begin
    out<='d0;
    out_vld<='d0;
  end
  else if(val_sum_vld == 1)begin
    //尾数求和有溢出
    out_vld<='d1;
    out[31]<= signal_bit;
    if(val_sum[23] == 1 && out[30:23] == 8'hFF)begin
      out[30:23]<= 8'hFF;
      out[22:0] <= 23'h7F_FFFF;
    end
    else if(val_sum[23] == 1)begin
      out[30:23]<= pow_max + 1;
      out[22:0] <= val_sum[23:1];
    end
    else begin
      out[30:23]<= pow_max;
      out[22:0] <= val_sum[22:0];
  end 
  end
  else begin
    out <= out;
    out_vld<='d0;
  end
end

//运算忙指示
always @(posedge clk or negedge rst_n) begin
  if (rst_n==0) begin
    busy<='d0;
  end
  else if(en == 1 && busy == 0)begin
    busy<='d1;
  end
  else if(out_vld == 1 )begin
    busy<='d0;
  end
  else begin
    busy <= busy;
  end
end

endmodule

仿真代码

这里简单测试了下代码的功能,模拟了连续输入多个数据,核查是否数据会影响正常计算过程。

`timescale 1ns/1ps
module float_adder_tb;

  // Parameters

  // Ports
  reg clk = 1;
  reg rst_n = 0;
  reg en = 0;
  reg [31:0] aIn;
  reg [31:0] bIn;
  wire busy;
  wire out_vld;
  wire [31:0] out;

  float_adder float_adder_dut (
    .clk (clk ),
    .rst_n (rst_n ),
    .en (en ),
    .aIn (aIn ),
    .bIn (bIn ),
    .busy (busy ),
    .out_vld (out_vld ),
    .out  ( out)
  );
  always
    #5  clk = ! clk ;
  initial begin
    rst_n = 0;
    #100;
    rst_n = 1;
    #100;
    aIn = {1'b0,8'd2,23'd7};
    bIn = {1'b0,8'd2,23'd8};
    en  = 1 ;
    #10;
    aIn = {1'b0,8'd0,23'd7};
    bIn = {1'b0,8'd2,23'd8};
    en  = 1 ;
    #1000;
    $finish;
  end

endmodule

仿真测试

从仿真测试中可以看出,当输入信号连续输入两个浮点数时,在busy拉高状态下,第二次输入的数据无效,数据使能信号常为1,也不会影响正常模块运算过程,只有在该次运算完成busy拉低后数据可重新加载。

仿真截图

小结

本文的设计方法对于对阶移位的操作需要循环操作,也即当阶数相差较小时,结果输出延迟较小,在极端情况下,比如阶数差大于10,输出延时会比阶数差为0时,多10个周期,上限为23。如在实际使用时,对延时要求较小的情况,可针对移位操作部分,使用更多的资源来换取性能的提升。可使用case语句对具体情况进行遍历。

针对原始题目中的累加操作,可将任意一个输入和输出相接,即可实现累加操作。此外,如果要实现异号加法情况,则需要仔细考虑对阶,规范化等情况进行进一步设计。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/445766.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java锁策略-Java多线程(4)

(各位观众老爷下午好, 创作不易勒, 大家多多点赞收藏&#x1f618;) 咱们废话不多讲, 下面细&#x1f512; 目录 前言 乐观锁 VS 悲观锁 乐观锁 悲观锁 悲观乐观锁优缺点 轻量级锁 VS 重量级锁 重量级锁 轻量级锁 读写锁 自旋锁 互斥锁 可重入锁 VS 不可重入锁 死…

IPSCE

文章目录 1.什么是数据认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段?2.什么是身份认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段?3.什么VPN技术?4.VPN技术有哪些分类?5.IPSEC技术能够提供哪些安全服务?6.IPSEC的技术架构是什么?7.AH与ESP…

今天面了个字阿里拿38K出来的,真是纹身师闭眼,秀了我一脸啊

公司前段缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试中&#xff0c;不提测…

8.java程序员必知必会类库之嵌入式SQL数据库

前言 嵌入式内存数据库&#xff0c;作为嵌入到应用内部的数据库&#xff0c;在正常生产业务流程中使用不多。现在一般公司通用架构都是应用和数据分离&#xff0c;解耦数据和应用。但是&#xff0c;在某些特殊场景中&#xff0c;这种嵌入式数据库是比较好的选择。 在某些单元…

如何在开发阶段保证软件工程质量 (程序员要做些什么)

前言 大家常说&#xff1a;“代码和人&#xff0c;有一个能跑就行”&#xff0c;但这并不意味着我们可以放弃职业道德。与土木工程一样&#xff0c;软件工程也需要一些可度量的指标来衡量产品的交付质量。一个高质量的软件绝对不能只靠测试人员来保证&#xff0c;更不能相信程…

在SaleSmartly(ss客服)中为Messenger 提供无缝支持体验

客户希望您在他们所在的地方与他们见面&#xff0c;这意味着打开多个沟通渠道。但是&#xff0c;当您通过电子邮件、实时聊天、社交等方式进行通信时&#xff0c;对话很容易丢失、被忽视和杂乱无章。 而Messenger的受欢迎程度&#xff0c;以及Meta的无所不在&#xff0c;使Face…

每日学术速递4.18

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Inpaint Anything: Segment Anything Meets Image Inpainting 标题&#xff1a;Inpaint Anything&#xff1a;分割任何东西满足图像修复 作者&#xff1a;Tao Yu, Runseng Feng, R…

企业用户如何选择合适的服务器配置方法教程

随着互联网信息的飞速发展&#xff0c;任何企业都脱离不了互联网&#xff0c;越来越多的企业都通过互联网实施无纸化的办公&#xff0c;互联网推广一体化整体型推广、互联网电子商务。中小型企业网站如何选购云服务器配置呢&#xff1f;但是&#xff0c;实现这些的最最基础的条…

ai改写句子软件-ai改写

AI免费伪原创&#xff1a;助力网站内容升级 您是否曾经为网站优化而烦恼&#xff0c;无论是内容更新还是SEO优化&#xff0c;都需要大量的时间和精力。但是&#xff0c;您是否知道&#xff0c;现在有一款能够使用AI技术来帮助您完成这些任务&#xff0c;而且还是免费的呢&…

【Git 学习】

Git 学习 一、Git的使用1. Git下载安装2. Git 命令3. Git推送代码步骤4. Git基本工作流程5. Git历史版本切换6. Git分支管理6.1 创建新分支6.2 切换分支6.3 合并分支6.4 删除分支 7. 远程仓库的工作流程7.1 具体流程 8.推送到远程仓库9. 代码冲突问题10. IDEA 集成Git10.1 版本…

说说webpack的构建流程?

① 初始化流程 从配置文件和 Shell 语句中读取与合并参数&#xff0c;并初始化需要使用的插件和配置插件等执行环境所需要的参数。 配置文件默认下为 webpack.config.js&#xff0c;也可以通过命令的形式指定配置文件&#xff1b; 主要作用是用于激活webpack的加载项和插件&am…

手写axios源码系列一:axios核心知识点

文章目录 axios的核心功能1、axios 函数对象2、dispatchRequest 发送请求3、interceptors 拦截器4、cancelToken 取消请求 最近从头搭建了一个vue小项目&#xff0c;想使用 axios 作为请求接口的第三方库。结果使用了 axios 这么长时间&#xff0c;想封装一下 axios &#xff0…

Nacos2.2.2开启鉴权配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、更改application.properties中的配置二、修改配置 前言 最近公司开启了一个新的电商项目&#xff0c;项目中用到了Naocs作为注册中心和配置中心&#xff0…

WindowsUbuntu下python程序打包

Python程序的运行必须要有Python的环境&#xff0c;但是程序编出来是用的&#xff0c;如果是给别人用&#xff0c;而他/她的电脑上又没有Python程序运行的环境怎么办呢&#xff1f;总不能让他/她去安装一个吧&#xff1f;这时我们就要将Python程序打包为exe可执行文件&#xff…

【文章学习系列之模型】PatchTST

本章内容 文章概况模型结构实验结果长期预测表征学习 消融实验分块和通道独立性不同的回顾窗口 总结 文章概况 《A Time Series is Worth 64 Words: Long-term Forecasting with Transformers》是2023年发表于ICLR的一篇文章。该文章借鉴了计算机视觉领域的Vision Transformer…

树形DP分析

树形dp 简单来说树形 d p 就是在树上做 d p 罢了 简单来说树形dp就是在树上做dp罢了 简单来说树形dp就是在树上做dp罢了 树嘛&#xff0c;就要符合除了根节点外每个节点只有一个父节点 树嘛&#xff0c;就要符合除了根节点外每个节点只有一个父节点 树嘛&#xff0c;就要符合除…

# 从车灯模组的角度聊聊信息安全需求

文章目录 1. 前言2.信息安全需求2.1 硬件安全2.1.1 接口安全2.1.2 主板安全2.1.3 芯片安全 2.3 系统安全2.3.1 代码安全2.3.2 软件读保护2.3.3 安全启动2.3.4 安全升级2.3.5 安全诊断 2.4 通信安全2.5 数据安全 3. 安全启动流程3.1 基于签名技术的安全启动方案3.2 基于对称签名…

netty源码阅读--处理客户端请求

背景 netty是一个非常成熟的NIO框架&#xff0c;众多apache的顶级项目底层通信框架都是用的是netty,本系列博客主要是记录自己复习netty源码的过程&#xff0c;重在理解netty的关键如&#xff1a;如何启动&#xff0c;如何接受网络数据、netty的内存管理机制以及编解码器等&am…

Python学习笔记--面向对象

未完待续。。。。。 &#xff08;一&#xff09;面向对象的基本概念 我们之前学习的编程方式就是面向过程的 面相过程和面相对象&#xff0c;是两种不同的编程方式 对比面向过程的特点&#xff0c;可以更好地了解什么是面向对象 1.1过程和函数 过程是早期的一个编程概念 过程…

4.3 分段低次插值

学习目标&#xff1a; 如果我要学习分段低次插值&#xff0c;我可能会采取以下几个步骤&#xff1a; 学习插值的基本概念和方法 在学习分段低次插值之前&#xff0c;我需要先掌握插值的基本概念和方法&#xff0c;例如拉格朗日插值、牛顿插值和内维尔方法等。这些基础知识可…