当前位置: 首页 > news >正文

【FPGA开发】Verilog-数据截断实现四舍五入效果、模块化改造、对比Matlab验证,Modelsim覆盖率

目录

  • 实现目标
  • 直接截断低位
  • 考虑四舍五入
  • 模块化实现四舍五入功能
  • Matlab对比验证程序
  • Testbench编写
  • Modelsim查看验证覆盖率(简易)

实现目标

由于FPGA以定点数运算为主,随着数字信号处理的流程增加,数据位宽会逐渐变大,有时,考虑到资源量问题,会对定点数进行截断处理。

对于定点数来说,低位表示小数部分,截断低位,意味着抛弃小数,也就是损失精度。

假设一乘法器的运算结果是32-bits,后续要对该结果进行滤波处理,由于32-bits过大,需要对齐进行截断处理。

直接截断低位

直接截断低位是最简单的办法,相当于FLOOR()函数。

考虑四舍五入

以32-bits截断成17-bits为例。
在这里插入图片描述

想保留的是[31:15]这部分数据,如果直接截断,直接舍弃[14:0]即可,也就是说,把[14:0]这部分看成了小数。

所以在理解定点数的四舍五入时,把[31:15]看做整数,把[14:0]看做小数,小数部分大于等于0.5,就进位1;小数部分不超过0.5,就舍弃。

得到下述代码:

module fixed_point_rounding (
    input wire [31:0] in_data,  // 假设输入是 32位定点数,小数点在第 15 位后
    output reg [17:0] out_data   // 输出是 17 位整数
);

always @(*) begin
    // 判断要舍弃部分的最高位
    if (in_data[14]) begin
        // 如果该位为 1,向前进位
        out_data = in_data[31:15] + 1;
    end else begin
        // 如果该位为 0,直接舍弃
        out_data = in_data[31:15];
    end
end

endmodule

再次基础上,考虑正数和负数在四舍五入时的区别。

1.3 四舍五入成 1
1.6 四舍五入成 2
-1.3 四舍五入成 -1
-1.6 四舍五入成 -2

正数向0的方向(数值变小的方向)舍,向数值变大的方向 入。
负数向0的方向(数值变大的方向)舍,向数值变小的方向 入。

这里的实现方法有很多,可以参考Verilog对数据进行四舍五入(round)与饱和(saturation)截位

这里再补充一种写法:

 assign buf_data = in_data[31] ? in_data + 15'b011_1111_1111_1111 : in_data + 15'b100_0000_0000_0000 ;

 assign out_data = buf_data[31:15];

考虑正数情况,把要截断的尾数看成小数,要截断15位, .100_0000_0000_0000 就是0.5,比这个大,就应该进位,所以正数情况要加 15’b100_0000_0000_0000,这样就能保证大于等于0.5的情况都能进位上去。

考虑负数情况,同样把要截断的尾数看成小数,要截断15位,.100_0000_0000_0000也是0.5,不过要注意的是,对于负数的补码来说,符号位的权值是负数((10.10) = (-2+0.5=-1.5)),对于负数来说,-1.5四舍五入后是要变成-2的,如果-1.5的小数位要是进位给整数位,会变成-1,这与实际不符合!所以,要保证.100_0000_0000_0000正好不产生进位,也就是要加15’b011_1111_1111_1111。

模块化实现四舍五入功能

module round_module #(
    parameter   P_PRE_DATA_WIDTH  = 32 , // 截断前数据位宽
                P_POST_DATA_WIDTH = 18   // 截断后数据位宽
)(
    input   [P_PRE_DATA_WIDTH - 1  : 0] i_data      ,  // 输入数据
    output  [P_POST_DATA_WIDTH - 1 : 0] o_data         // 输出数据
);

wire [P_PRE_DATA_WIDTH - 1 : 0] buf_data;

//正确的四舍五入操作
assign buf_data = i_data[P_PRE_DATA_WIDTH - 1] ? (i_data +  {{1'b0},{(P_PRE_DATA_WIDTH - P_POST_DATA_WIDTH -1){1'b1}}}) 
        : &i_data[P_PRE_DATA_WIDTH-2:P_PRE_DATA_WIDTH - P_POST_DATA_WIDTH] ? i_data : (i_data + {{1'b1},{(P_PRE_DATA_WIDTH - P_POST_DATA_WIDTH -1){1'b0}}}); //最好补写成1<<14格式,因为涉及补高位做加法,可能会出现错误  

// 截断操作
assign o_data = buf_data[P_PRE_DATA_WIDTH - 1 : P_PRE_DATA_WIDTH - P_POST_DATA_WIDTH];

endmodule

Matlab对比验证程序

主函数:

clc;clear;close all;

% 参数设置
P_PRE_DATA_WIDTH = 32;  % 输入数据位宽
P_POST_DATA_WIDTH = 18; % 输出数据位宽

% 生成随机测试数据
num_tests = 10000-3; % 测试用例数量

min = -2^(P_PRE_DATA_WIDTH-1);

max = 2^(P_PRE_DATA_WIDTH-1)-1;

num_rand = fix(min + (max - min)*rand(1,num_tests)) ;

num_rand = fix([num_rand min max 0]);

fileID = fopen('data.txt', 'w');
for i=1:num_tests+3
    num_rand_comp(i,:) = twos_complement(num_rand(i),P_PRE_DATA_WIDTH);
    % 将二进制字符串写入文件
    fprintf(fileID, '%s\n', num_rand_comp(i,:));
end

num_q = num_rand / 2^(P_PRE_DATA_WIDTH - P_POST_DATA_WIDTH);

ref = round(num_q); %标准的数值

decimal_data = read_binary_twos_complement("output_data.txt", 18);

decimal_data = decimal_data.';

count = 0;
for i=1:num_tests
    if(ref(i) == decimal_data(i))
        count = count + 1;
    end
end

disp(['数据准确率为:',num2str(count/num_tests*100),'%']);

twos_complement函数:

function binary_str = twos_complement(decimal_num, bit_width)
    % 将十进制有符号数转换为补码形式的二进制字符串
    % decimal_num: 输入的十进制数
    % bit_width: 二进制位数

    % 检查输入是否为有符号数
    if decimal_num < 0
        % 负数:计算补码
        complement = 2^bit_width + decimal_num; % 补码 = 2^N + 负数
        binary_str = dec2bin(complement, bit_width);
    else
        % 正数:直接转换为二进制
        binary_str = dec2bin(decimal_num, bit_width);
    end
end

read_binary_twos_complement函数:

function decimal_data = read_binary_twos_complement(filename, bit_width)
    % 读取二进制补码文件并转换为十进制数
    % filename: 文件名
    % bit_width: 二进制位数

    % 打开文件并读取内容
    fileID = fopen(filename, 'r');
    if fileID == -1
        error('无法打开文件');
    end

    % 读取文件内容
    binary_data = textscan(fileID, '%s', 'Delimiter', '\n');
    fclose(fileID);

    % 初始化输出数组
    num_values = length(binary_data{1});
    decimal_data = zeros(num_values, 1);

    % 遍历每一行二进制数据
    for i = 1:num_values
        binary_str = binary_data{1}{i}; % 获取二进制字符串
        decimal_data(i) = binary_twos_complement_to_decimal(binary_str, bit_width);
    end

end

binary_twos_complement_to_decimal函数:

function decimal_value = binary_twos_complement_to_decimal(binary_str, bit_width)
    % 将二进制补码字符串转换为十进制数
    % binary_str: 二进制字符串
    % bit_width: 二进制位数

    % 检查二进制字符串长度
    if length(binary_str) ~= bit_width
        error('二进制字符串长度与位宽不匹配');
    end

    % 判断是否为负数(最高位为1if binary_str(1) == '1'
        % 负数:计算补码值
        decimal_value = - (2^bit_width - bin2dec(binary_str));
    else
        % 正数:直接转换
        decimal_value = bin2dec(binary_str);
    end
end

Testbench编写

`timescale 1ns/1ps

module tb_test();

reg 	[31:0] in_data	;
wire  	[17:0] out_data ;

reg [31:0] test_data [0:10000-1] ;

initial begin
	$readmemb("data.txt",test_data);
end


// initial begin
// 	in_data = 'd0;
// 	#100
// 	in_data = 32'b0111_0000_1111_1111_1110_0000_0000_0000;
// 	#100
// 	in_data = 32'b1000_1111_0000_0000_0010_0000_0000_0000;

// 	#100
// 	in_data = 32'b0111_0000_1111_1111_1110_0110_0110_0110;
// 	#100
// 	in_data = 32'b1000_1111_0000_0000_0001_1001_1001_1010;

// 	#100
// 	in_data = 32'b0111_0000_1111_1111_1101_1001_1001_1010;
// 	#100
// 	in_data = 32'b1000_1111_0000_0000_0010_0110_0110_0110;
// 	#100
// 	in_data = 32'b0111_1111_1111_1111_1111_1111_1111_1111;
// 	#100
// 	in_data = 32'd2147483648;
// end

// 输入测试数据
integer i;
integer file_handle;

// 初始化:打开文件
initial begin
    file_handle = $fopen("output_data.txt", "w");
    if (!file_handle) begin
        $display("can not open file!");
        $finish;
    end
end

initial begin
	for (i = 0; i < 10000; i = i + 1) begin
		in_data = test_data[i];
		#50;
		$fwrite(file_handle,"%b\n",out_data);
	end
	$fclose(file_handle);
	// 打印完成信息
    $display("data already write in :  output_data.txt file!");

    // 结束仿真
    $finish;
end

round_module #(
	.P_PRE_DATA_WIDTH  (32	) ,
	.P_POST_DATA_WIDTH (18	)
)round_module_u0(
	.i_data      (in_data	),  // 
	.o_data      (out_data	)   // 
);


endmodule

Matlab对比数据,随机产生9997个数据,并添加最大值、最小值、0三个特殊边界值,验证程序,得到该模块的准确率为100%

在这里插入图片描述

Modelsim查看验证覆盖率(简易)

主要参考文章:
modelsim中代码覆盖率使用详解

代码覆盖率,英文Code Coverage,包含statement语句、branch分支、condition条件、expression表达、toggle信号翻转,fsm有限状态机等多种覆盖情况。

首先,准备好两个文件:testbench文件和模块文件
在这里插入图片描述
打开modelsim,新建工程。在这里插入图片描述
选择好路径后,在弹出的框中,把已经有的两个文件添进来
在这里插入图片描述
在这里插入图片描述

在Project窗口下,如果相对文件进行代码覆盖率分析,需要配置。这里的两个文件我都想分析,那就一个个来,先配置tb_test.v文件。

选中tb_test.v,右键,compile,compile properties
在这里插入图片描述

在弹出的窗口中,选择Coverage,全选后点击ok。同理,把round_module.v也重复上述操作。
在这里插入图片描述
都配置完后,进行编译在这里插入图片描述,编译通过后,会有绿色对钩出现,没出现就是代码有逻辑错误,需要回去找。下图就是编译通过。

在这里插入图片描述

编译通过后,准备开始放着,点击仿真按钮在这里插入图片描述
选择work中的tb_test,点击Others选项卡
在这里插入图片描述

勾选Enable code coverage,点击ok

在这里插入图片描述
点击ok后,modelsim的布局会kuku变,这是正常的,因为要添加很多分析的窗口。

这是需要配合Matlab进行验证。

在读取testbench输出数据的文件前,打一个断点,让matlba先生成一个输入数据的txt文件。
在这里插入图片描述
图中的data.txt就是输入数据。
在这里插入图片描述

有个这个data.txt后,可以在modelsim中restart一下在这里插入图片描述
然后再run-all在这里插入图片描述

点完后,选择否,不退出modelsim。
在这里插入图片描述
此时代码覆盖率信息就分析好了,界面的大概布局如下:

在这里插入图片描述

下面分别看看每个窗口里都有啥。首先是Files选项卡

在这里插入图片描述
拖动横向条可以看到,Stmt是Statement缩写,这里给出了语句覆盖率的的命中率,和分支Branch覆盖率。
在这里插入图片描述
analysis选项卡
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

【Bug记录】:在分析完代码覆盖率后,如果编辑了文本编码格式,utf-8这种,会报错。
Numeric coverage display disabled
尚未解决,先编辑文本编码格式,再分析代码覆盖率就没有错。

相关文章:

  • SSLScan实战指南:全面检测SSL/TLS安全配置
  • 股指期货交割日每月几号?为什么是第三个周五还是倒数第二个周五?
  • 第十五届蓝桥杯Scratch12月stema选拔赛真题—消失的水母
  • STM32之Unix时间戳
  • 计算机毕业设计Python+DeepSeek-R1大模型医疗问答系统 知识图谱健康膳食推荐系统 食谱推荐系统 医疗大数据(源码+LW文档+PPT+讲解)
  • Spring-framework源码编译
  • 完全背包-一维数组
  • Apache Commons Lang3 和 Commons Net 详解
  • 【JavaScript】DOM和BOM是什么?
  • VUE3开发-9、axios前后端跨域问题解决方案
  • 1998-2022年各地级市第三产业占GDP比重数据/地级市第三产业占比数据(全市)
  • Leetcode 3472. Longest Palindromic Subsequence After at Most K Operations
  • XGBoost常见面试题(五)——模型对比
  • 如何在rust中解析 windows 的 lnk文件(快捷方式)
  • 23、什么是菱形继承【中高频】
  • el-tree(append与getNode与过滤)、深拷贝deepClone、监听addEventListener、
  • Aruco 库详解:计算机视觉中的高效标记检测工具
  • C++ 接口(抽象类)
  • 深入理解 CAS 与 ABA 问题
  • 基于Docker去创建MySQL的主从架构
  • 网站做招聘需要什么资质/好用的种子搜索引擎
  • 马鞍山建设机械网站/seo 优化思路
  • 微信微网站怎么做/互联网推广平台有哪些公司
  • 湖南平台网站建设哪里好/长春网站建设技术托管
  • 个人做房产网站有哪些资料/第三方关键词优化排名
  • 澳门响应式网站建设/深度优化