参考文章【设计经验】5、Verilog对数据进行四舍五入(round)与饱和(saturation)截位
当前还有存疑,请大家指正
题目:
请用Verilog实现算法 Q = K * (D - 16),其中输入数据D和输出数据D和输出数据Q都是无符号8位整数(无符号,8位整数,0位小数),输入参数K的数值为13Q10(共13位,1位符号位,10位小数位),中间过程保留完整精度,最后四舍五入。
答案1
1.先按照参考文章得到有符号整数输出,然后变到无符号输出
assign Q = Q1 + 128;转成无符号数。
module DaHua_2021_17(
input [7:0] D,
input signed [12:0] K,//13Q10
output [7:0] Q
);
wire signed [8:0] D_min_16;//9Q0
wire signed [21:0] K_mul;//22Q10
wire signed [12:0] rou_K_mul; //13Q0;
wire cin;
wire [13:0] un;
wire signed [7:0] Q1;
assign D_min_16 = D - 16;
assign K_mul = K * D_min_16;
assign cin = K_mul[21]? K_mul[9]&(|K_mul[8:0]) : K_mul[9];
assign rou_K_mul = {K_mul[21],K_mul[21:10]} + cin;
assign Q1 = (rou_K_mul[12:7] == 6'b111111 || rou_K_mul[12:7] == 6'b000000)? rou_K_mul[7:0]:{rou_K_mul[12],{7{!rou_K_mul[12]}}};
assign Q = Q1 + 128;
endmodule
答案2
按照matlab函数
quan_U8Q0_pattern = quantizer(‘ufixed’,‘round’,‘saturate’,[8,0]),
s_U8Q0 = quantize(quan_U8Q0_pattern,s);的结果而来
module DaHua_2021_17(
input [7:0] D,
input signed [12:0] K,//13Q10
output [7:0] Q
);
wire signed [8:0] D_min_16;//9Q0
wire signed [21:0] K_mul;//22Q10
wire signed [12:0] rou_K_mul; //13Q0;
wire cin;
wire [13:0] un;
wire signed [8:0] Q1;
assign D_min_16 = D - 16;
assign K_mul = K * D_min_16;
assign cin = K_mul[21]? K_mul[9]&(|K_mul[8:0]) : K_mul[9];
assign rou_K_mul = {K_mul[21],K_mul[21:10]} + cin;
assign Q1 = (rou_K_mul[12:8] == 5'b11111 || rou_K_mul[12:8] == 5'b00000)? rou_K_mul[8:0]:{rou_K_mul[12],{8{!rou_K_mul[12]}}};
assign Q = !Q1[8]? Q1[7:0]:0;
endmodule
对比s_U8Q0与Q,此时quan_U8Q0_pattern = quantizer(‘ufixed’,‘round’,‘saturate’,[8,0]);
结果和matlab s_U8Q0 = quantize(quan_U8Q0_pattern,s);得到的结果一样
中间过程
验证四舍五入结果
对比s = K_13Q10 .* s0与rou_K_mul
若要求输出有符号整数输出,答案1的Q1即为答案
quan_U8Q0_pattern = quantizer(‘fixed’,‘round’,‘saturate’,[8,0]);
D_U8Q0 = quantize(quan_U8Q0_pattern,D);
当D小于等于127时,D_U8Q0 = D,大于127时,输出127
s_U8Q0 = quantize(quan_U8Q0_pattern,s);
对比答案1的Q1与s_U8Q0
tb文件
module tb_dahua_2021_17();
reg [7:0] D;
reg [12:0] K;
reg I_clk,I_rst_n;
wire [7:0] Q;
parameter C_DATA_LENGTH = 4096;
reg [7:0] M_men_D[0:C_DATA_LENGTH-1];
reg [12:0] M_men_K[0:C_DATA_LENGTH-1];
reg [13:0] R_men_addr;
reg R_data_valid;
reg R_data_valid_t;
DaHua_2021_17 ins(
.D(D),
.K(K),//13Q10
.Q(Q)
);
initial begin
I_clk = 0;
I_rst_n = 0;
#67;
I_rst_n = 1;
end
always #5 I_clk = !I_clk;
initial begin
$readmemh("D:/job/signed_cal/D_U8Q0.txt",M_men_D);
$readmemh("D:/job/signed_cal/K_13Q10.txt",M_men_K);
end
always@(posedge I_clk or negedge I_rst_n) begin
if(!I_rst_n) begin
D <= 0;
K <= 0;
R_men_addr <= 14'd0;
R_data_valid <= 1'b0;
end
else if (R_men_addr == C_DATA_LENGTH) begin
R_men_addr <= C_DATA_LENGTH;
R_data_valid <= 1'b0;
end
else begin
D <= M_men_D[R_men_addr];
K <= M_men_K[R_men_addr];
R_data_valid <= 1'b1;
R_men_addr <= R_men_addr + 1;
end
end
always@(posedge I_clk or negedge I_rst_n) begin
if(!I_rst_n)
R_data_valid_t <= 1'b0;
else
R_data_valid_t <= R_data_valid;
end
integer fid;
initial begin
fid = $fopen("D:/job/signed_cal/s_vivado.txt" , "w");
if(!fid) begin
$display("**************************Can Not Open File**************");
$finish;
end
else begin
$display("**************************Open File Sucess****************");
end
end
matlab程序
clear
clc
data_length = 4096 - 8 ; % 定义数据长度,其中排除8种边界条件
D_min = 0 ; %D的数据格式为U80,所以它的最小值为0
D_max = 2^8-1 ; %a的数据格式为16Q14,所以它的最大值为2 - 1/(2^14)
K_min = -4 ; %b的数据格式为13Q10,所以它的最小值为-2^(13-10-1)
K_max = 4 - 1/(2^10) ; %b的数据格式为13Q10,所以它的最大值为2^(13-10-1) - 1/(2^10)
% 产生4088个均匀分布在a、b、c最大值与最小值之间的随机数
D_rand = D_min + (D_max - D_min)*rand(1,data_length) ;
K_rand = K_min + (K_max - K_min)*rand(1,data_length) ;
% 产生8种边界条件
K_boundary = [K_min K_min K_min K_min K_max K_max K_max K_max] ;
D_boundary = [D_min D_min D_max D_max D_min D_min D_max D_max] ;
% 随机数与边界值组合成为待量化的数据
K = [K_boundary K_rand];
D = [D_boundary D_rand];
% 定义量化规则,根据题目要求量化需采用四舍五入与饱和截位的方式
quan_13Q10_pattern = quantizer('fixed','round','saturate',[13,10]);
quan_U8Q0_pattern = quantizer('ufixed','round','saturate',[8,0]);
% 把a、b、c三个数据按照要求进行量化
K_13Q10 = quantize(quan_13Q10_pattern,K);
D_U8Q0 = quantize(quan_U8Q0_pattern,D);
% 计算Q = K *(D-16)的值
s0 = D_U8Q0 - 16;
s = K_13Q10 .* s0;
% 根据题目要求,s的数据格式为16Q14,所以这里把s量化为16Q14格式的数据
s_U8Q0 = quantize(quan_U8Q0_pattern,s);
% 把量化后的a、b、c变成整数方便写入.txt文件中
D_integer = D_U8Q0 ;
K_integer = K_13Q10 * 2^10 ;
s_integer = s_U8Q0 ;
% 由于在Verilog中测试激励文件的系统调用$readmemh读入的数据格式为16进制,所以
% 把数据写入.txt文件中之前需要把数据转化为补码的格式,这样负数才不会写错
K_complement = zeros(1,length(K_integer));
% 把量化后的a转化为补码
for i = 1:length(K_complement)
if(K_integer(i) < 0)
K_complement(i) = 2^13 + K_integer(i) ;
else
K_complement(i) = K_integer(i) ;
end
end
% 把量化后的a的补码写入txt文件
fid_a = fopen('K_13Q10.txt','w');
fprintf(fid_a, '%x\n', K_complement);
fclose(fid_a);
% 把量化后的b的补码写入txt文件
fid_b = fopen('D_U8Q0.txt','w');
fprintf(fid_b, '%x\n', D_integer);
fclose(fid_b);
存疑:
Q1及之前都是有符号数表示,有符号数怎么转成无符号数?
最后的无符号表示应为啥样的?不太清楚。