活动介绍
file-type

Verilog实现浮点数乘法器及绝对值计算

版权申诉
317KB | 更新于2024-12-14 | 27 浏览量 | 1 下载量 举报 收藏
download 限时特惠:#14.90
这是一个可以直接被调用的硬件描述语言代码资源,适用于需要浮点数运算的数字电路设计领域。" 知识点详细说明: 1. 浮点数乘法器概念:浮点数乘法器是一种数字电路硬件组件,用于执行两个浮点数之间的乘法运算。浮点数是由符号位、指数位和尾数位组成的数字表示形式,它们能够表示非常大或非常小的数值。在计算机和数字信号处理领域中,浮点数乘法器是实现复杂算术运算的基础组件。 2. Verilog语言介绍:Verilog是一种硬件描述语言(HDL),它被广泛用于模拟电子系统,尤其是数字逻辑电路的设计与验证。使用Verilog,设计者能够通过代码描述硬件的行为和结构,并进行仿真测试,最终生成可在实际硬件中实现的设计文件。 3. 浮点数乘法器设计:在设计浮点数乘法器时,需要考虑IEEE浮点数标准(通常是IEEE 754标准),以便确保设计的乘法器能够正确处理规格化、非规格化以及特殊数值(如无穷大和NaN - 非数字)的运算。设计通常包括以下步骤:指数加减运算、尾数乘法运算、舍入处理、溢出和下溢检查等。 4. 绝对值运算介绍:绝对值运算是一种基础的数学运算,用于获取一个数的非负值。在浮点数乘法器中加入绝对值运算功能,意味着该乘法器可以处理负数输入,输出结果总是正数,这在某些应用场合非常有用,比如信号处理中的功率计算。 5. Verilog代码结构和模块化:Verilog代码通常由模块(module)构成,每个模块可以实现特定的功能,比如一个浮点数乘法器。在模块内部,可以定义输入输出端口、内部信号、函数、任务等。模块化的代码结构使得硬件设计更易于管理和复用。 6. 直接调用代码资源:在数字电路设计流程中,经常需要直接调用预先编写好的代码资源,以加速开发过程。本资源提供的是一个经过封装的模块,可以直接在更大的设计中调用,这样设计者可以不必从头开始编写浮点数乘法器和绝对值功能,节省了设计时间和资源。 7. 应用领域:此类浮点数乘法器模块在许多领域都有应用,包括但不限于:数字信号处理器(DSP)、图形处理单元(GPU)、数字通信系统、嵌入式系统、科学计算和高性能计算等。 8. 仿真与验证:在实际集成到硬件设计中之前,使用Verilog编写的浮点数乘法器模块需要进行详细的仿真测试,以验证其正确性和性能。仿真环境可以模拟不同的输入条件和边界情况,确保模块在所有可能的场景下都能正确工作。 9. 可参数化设计:在更高级的设计中,设计者可能希望模块具有可配置性,即能够根据不同的需求调整参数,如浮点数的位宽、舍入模式等。这样的设计要求代码具有一定的灵活性和可扩展性,以便用户可以根据自己的需要定制模块的行为。 10. 综合与实现:最终,设计者需要将Verilog代码综合成可以在特定集成电路(ASIC)或现场可编程门阵列(FPGA)上实现的电路。这一步骤涉及将高级的硬件描述转换为实际的逻辑门和触发器等硬件元素,同时也需要考虑时序约束和资源优化。

相关推荐

filetype

`timescale 1ns/1ps module fft_sim( ); reg clk; reg rst_n; reg [7 : 0] s_axis_config_tdata; reg s_axis_config_tvalid; wire s_axis_config_tready; reg [15 : 0] s_axis_data_tdata_img,s_axis_data_tdata_real; //signed(1.15) reg s_axis_data_tvalid; wire s_axis_data_tready; reg s_axis_data_tlast; wire [26 : 0] m_axis_data_tdata_img,m_axis_data_tdata_real; reg[63:0] m_axis_data_tdata; wire [31 : 27] m_axis_data_null; wire [63:59] null; wire m_axis_data_tvalid; wire m_axis_data_tlast; wire [15:0]m_axis_data_tuser; //xilinx pg109-xfft.pdf page24 parameter FFTCONFIG_FWD_INV_FFT = 1'b1; //FFT parameter FFTCONFIG_FWD_INV_IFFT = 1'b0; //IFFT //FFT IP Core wire event_frame_started; wire event_tlast_unexpected; wire event_tlast_missing; wire event_data_in_channel_halt; xfft_0 uut_xfft_0 ( .aclk (clk), // input wire aclk .s_axis_config_tdata (s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata .s_axis_config_tvalid (s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tready (s_axis_config_tready), // output wire s_axis_config_tready .s_axis_data_tdata ({s_axis_data_tdata_img,s_axis_data_tdata_real}), // input wire [31 : 0] s_axis_data_tdata .s_axis_data_tvalid (s_axis_data_tvalid), // input wire s_axis_data_tvalid .s_axis_data_tready (s_axis_data_tready), // output wire s_axis_data_tready .s_axis_data_tlast (s_axis_data_tlast), // input wire s_axis_data_tlast .m_axis_data_tdata ({null,m_axis_data_tdata_img,m_axis_data_null,m_axis_data_tdata_real}), .m_axis_data_tuser(m_axis_data_tuser), // output wire [63 : 0] m_axis_data_tdata .m_axis_data_tvalid (m_axis_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tlast (m_axis_data_tlast), // output wire m_axis_data_tlast .event_frame_started (event_frame_started), // output wire event_frame_started .event_tlast_unexpected (event_tlast_unexpected), // output wire event_tlast_unexpected .event_tlast_missing (event_tlast_missing), // output wire event_tlast_missing .event_data_in_channel_halt (event_data_in_channel_halt) // output wire event_data_in_channel_halt ); integer i,j; reg[15:0] data_mem [0:999]; initial #500 $readmemh("E:/Matlab/R2021/bin/shiyan/test_4/time_domain_cos.txt", data_mem); initial begin clk = 0; rst_n = 0; s_axis_config_tdata <= 8'd0; s_axis_config_tvalid <= 1'b0; s_axis_data_tdata_img <= 16'd0; s_axis_data_tdata_real <= 16'd0; s_axis_data_tvalid <= 1'b0; s_axis_data_tlast <= 1'b0; #1000; @(posedge clk); s_axis_config_tdata <= {7'd0,FFTCONFIG_FWD_INV_FFT}; s_axis_config_tvalid <= 1'b1; repeat(2) begin @(posedge clk); end s_axis_config_tvalid <= 1'b0; #1000; @(posedge clk); i <= 1; #10000; @(posedge clk); #100_0000; $fclose(w1_file); $fclose(w2_file); #1000; $stop; end always @(*) begin if((i>0) && (i<1001)) s_axis_data_tdata_real <= $signed(data_mem[i-1]); else s_axis_data_tdata_real <= 16'd0; end always @(posedge clk) begin if(i == 0) i <= 0; else if(i <= 1024) begin if(s_axis_data_tready && s_axis_data_tvalid) i <= i+1; else ; end else if(i < 1050) i <= i+1; else ; end always @(posedge clk) begin if((i>=1) && (i<=1024)) s_axis_data_tvalid <= 1'b1; else s_axis_data_tvalid <= 1'b0; end always @(posedge clk) begin if(i==1024) s_axis_data_tlast <= 1'b1; else s_axis_data_tlast <= 1'b0; end always #10 clk = ~clk; integer w1_file,w2_file; initial w1_file = $fopen("E:/FPGA/fft_result_reals.txt","w"); initial w2_file = $fopen("E:/FPGA/fft_result_images.txt","w"); always @(posedge clk) begin if(m_axis_data_tvalid) begin m_axis_data_tdata<={null ,m_axis_data_tdata_img,m_axis_data_null,m_axis_data_tdata_real}; $fwrite(w1_file, "%d\n", m_axis_data_tdata_real); $fwrite(w2_file, "%d\n", m_axis_data_tdata_img); $display("%d , %d",m_axis_data_tdata_real,m_axis_data_tdata_img); end end endmodule`timescale 1ns/1ps module fft_sim( ); reg clk; reg rst_n; reg [7 : 0] s_axis_config_tdata; reg s_axis_config_tvalid; wire s_axis_config_tready; reg [15 : 0] s_axis_data_tdata_img,s_axis_data_tdata_real; //signed(1.15) reg s_axis_data_tvalid; wire s_axis_data_tready; reg s_axis_data_tlast; wire [26 : 0] m_axis_data_tdata_img,m_axis_data_tdata_real; reg[63:0] m_axis_data_tdata; wire [31 : 27] m_axis_data_null; wire [63:59] null; wire m_axis_data_tvalid; wire m_axis_data_tlast; wire [15:0]m_axis_data_tuser; //xilinx pg109-xfft.pdf page24 parameter FFTCONFIG_FWD_INV_FFT = 1'b1; //FFT parameter FFTCONFIG_FWD_INV_IFFT = 1'b0; //IFFT //FFT IP Core wire event_frame_started; wire event_tlast_unexpected; wire event_tlast_missing; wire event_data_in_channel_halt; xfft_0 uut_xfft_0 ( .aclk (clk), // input wire aclk .s_axis_config_tdata (s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata .s_axis_config_tvalid (s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tready (s_axis_config_tready), // output wire s_axis_config_tready .s_axis_data_tdata ({s_axis_data_tdata_img,s_axis_data_tdata_real}), // input wire [31 : 0] s_axis_data_tdata .s_axis_data_tvalid (s_axis_data_tvalid), // input wire s_axis_data_tvalid .s_axis_data_tready (s_axis_data_tready), // output wire s_axis_data_tready .s_axis_data_tlast (s_axis_data_tlast), // input wire s_axis_data_tlast .m_axis_data_tdata ({null,m_axis_data_tdata_img,m_axis_data_null,m_axis_data_tdata_real}), .m_axis_data_tuser(m_axis_data_tuser), // output wire [63 : 0] m_axis_data_tdata .m_axis_data_tvalid (m_axis_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tlast (m_axis_data_tlast), // output wire m_axis_data_tlast .event_frame_started (event_frame_started), // output wire event_frame_started .event_tlast_unexpected (event_tlast_unexpected), // output wire event_tlast_unexpected .event_tlast_missing (event_tlast_missing), // output wire event_tlast_missing .event_data_in_channel_halt (event_data_in_channel_halt) // output wire event_data_in_channel_halt ); integer i,j; reg[15:0] data_mem [0:999]; initial #500 $readmemh("E:/Matlab/R2021/bin/shiyan/test_4/time_domain_cos.txt", data_mem); initial begin clk = 0; rst_n = 0; s_axis_config_tdata <= 8'd0; s_axis_config_tvalid <= 1'b0; s_axis_data_tdata_img <= 16'd0; s_axis_data_tdata_real <= 16'd0; s_axis_data_tvalid <= 1'b0; s_axis_data_tlast <= 1'b0; #1000; @(posedge clk); s_axis_config_tdata <= {7'd0,FFTCONFIG_FWD_INV_FFT}; s_axis_config_tvalid <= 1'b1; repeat(2) begin @(posedge clk); end s_axis_config_tvalid <= 1'b0; #1000; @(posedge clk); i <= 1; #10000; @(posedge clk); #100_0000; $fclose(w1_file); $fclose(w2_file); #1000; $stop; end always @(*) begin if((i>0) && (i<1001)) s_axis_data_tdata_real <= $signed(data_mem[i-1]); else s_axis_data_tdata_real <= 16'd0; end always @(posedge clk) begin if(i == 0) i <= 0; else if(i <= 1024) begin if(s_axis_data_tready && s_axis_data_tvalid) i <= i+1; else ; end else if(i < 1050) i <= i+1; else ; end always @(posedge clk) begin if((i>=1) && (i<=1024)) s_axis_data_tvalid <= 1'b1; else s_axis_data_tvalid <= 1'b0; end always @(posedge clk) begin if(i==1024) s_axis_data_tlast <= 1'b1; else s_axis_data_tlast <= 1'b0; end always #10 clk = ~clk; integer w1_file,w2_file; initial w1_file = $fopen("E:/FPGA/fft_result_reals.txt","w"); initial w2_file = $fopen("E:/FPGA/fft_result_images.txt","w"); always @(posedge clk) begin if(m_axis_data_tvalid) begin m_axis_data_tdata<={null ,m_axis_data_tdata_img,m_axis_data_null,m_axis_data_tdata_real}; $fwrite(w1_file, "%d\n", m_axis_data_tdata_real); $fwrite(w2_file, "%d\n", m_axis_data_tdata_img); $display("%d , %d",m_axis_data_tdata_real,m_axis_data_tdata_img); end end endmodule这是我的FPGA端的代码,进行的是1024点的fft,输入采用的是fixed point形式,现在我想提高fft计算精度想要将输入变成floating point 并且进行2048点的fft,我该怎么改代码呢

filetype

module floatReciprocal #( parameter DATA_WIDTH = 32, // IEEE 754 单精度浮点数 parameter MAX_ITER = 3, // 最大迭代次数(FP32 通常需要 2-3 次) parameter THRESHOLD = 32'h0001 // 收敛阈值(尾数最低位变化容忍) ) ( input [DATA_WIDTH-1:0] number, // 输入浮点数 (IEEE 754 FP32) input enable, // 计算使能信号 (高电平启动) input clk, // 时钟信号 input rstn, // 复位信号 (低有效) output reg [DATA_WIDTH-1:0] output_rec, // 输出倒数 (1/number) output reg ack // 计算完成确认信号 ); // IEEE 754 单精度浮点数常量定义 localparam ONE = 32'h3F80_0000; // 1.0 localparam TWO = 32'h4000_0000; // 2.0 localparam INF_POS = 32'h7F80_0000; // 正无穷大 localparam INF_NEG = 32'hFF80_0000; // 负无穷大 localparam ZERO = 32'h0000_0000; // 零 localparam NAN = 32'h7FC0_0000; // NaN // 参考模块的初始值常数 (43/17 和 -32/17) localparam P1 = 32'b01000000001101001011010010110101; // ≈2.5294117647 localparam P2 = 32'b10111111111100001111000011110001; // ≈-1.8823529412 // 内部寄存器 reg [DATA_WIDTH-1:0] current_x; // 当前迭代值 reg [2:0] iter_count; // 迭代计数器 reg [DATA_WIDTH-1:0] Ddash; // 归一化后的输入值 reg input_sign; // 输入符号位 reg [7:0] input_exp; // 输入指数部分 reg [DATA_WIDTH-1:0] final_result; // 最终迭代结果 // 牛顿迭代计算单元 wire [DATA_WIDTH-1:0] prod_DX; // Ddash * current_x wire [DATA_WIDTH-1:0] two_minus; // 2 - prod_DX wire [DATA_WIDTH-1:0] x_next; // current_x * two_minus // 初始值计算单元 (参考模块的核心公式) wire [DATA_WIDTH-1:0] P2_times_Ddash; wire [DATA_WIDTH-1:0] initial_guess; // P1 + P2*Ddash // 浮点运算器实例化 floatMult mult1 ( .floatA(Ddash), .floatB(current_x), .product(prod_DX) ); floatAdd sub1 ( .floatA(TWO), .floatB({~prod_DX[31], prod_DX[30:0]}), // 取负:符号位取反 .sum(two_minus) ); floatMult mult2 ( .floatA(current_x), .floatB(two_minus), .product(x_next) ); // 初始值计算单元 (关键修改) floatMult mult_initial ( .floatA(P2), .floatB(Ddash), .product(P2_times_Ddash) ); floatAdd add_initial ( .floatA(P1), .floatB(P2_times_Ddash), .sum(initial_guess) ); // 主状态机 reg [1:0] current_state, next_state; localparam IDLE = 2'b00; localparam INIT = 2'b01; localparam ITERATE = 2'b10; localparam DONE = 2'b11; // 收敛检测:计算绝对差值 wire [22:0] abs_diff = (current_x[22:0] > x_next[22:0]) ? (current_x[22:0] - x_next[22:0]) : (x_next[22:0] - current_x[22:0]); always @(posedge clk or negedge rstn) begin if (!rstn) begin current_state <= IDLE; end else begin current_state <= next_state; end end always @(*) begin next_state = current_state; case(current_state) IDLE: begin if(enable) begin if((number[30:0] == 0) || (number[30:23] == 8'hFF)) next_state = DONE; else next_state = INIT; end end INIT: begin next_state = ITERATE; // 直接进入迭代状态 end ITERATE: begin if ((iter_count == MAX_ITER - 1) || (abs_diff < THRESHOLD)) next_state = DONE; end DONE: begin if(ack) next_state = IDLE; end default: next_state = IDLE; endcase end always @(posedge clk or negedge rstn) begin if (!rstn) begin ack <= 0; iter_count <= 0; output_rec <= 0; input_sign <= 0; input_exp <= 0; Ddash <= 0; current_x <= 0; end else begin case (current_state) IDLE: begin ack <= 0; if (enable) begin // 检查特殊情况 if (number[30:0] == 0) begin // 输入为0 output_rec <= (number[31]) ? INF_NEG : INF_POS; ack <= 1; end else if (number[30:23] == 8'hFF) begin // 无穷大或NaN output_rec <= (number[22:0] == 0) ? ZERO : // 无穷大倒数为0 NAN; // NaN输入输出NaN ack <= 1; end else begin // 正常数值 // 保存符号和指数 input_sign <= number[31]; input_exp <= number[30:23]; // 归一化输入:构造 D' = {0, 126, mantissa} Ddash <= {1'b0, 8'd126, number[22:0]}; end end end INIT: begin // 关键修改:使用参考模块的初始值公式 current_x <= initial_guess; // Xi = P1 + P2*Ddash iter_count <= 0; end ITERATE: begin // 更新迭代值 current_x <= x_next; iter_count <= iter_count + 1; // 检查终止条件 if ((iter_count == MAX_ITER - 1) || (abs_diff < THRESHOLD)) begin final_result <= x_next; end end DONE: begin // 计算结果:1/number = sign * 2^(253 - input_exp) * mantissa output_rec <= {input_sign, 8'b11111101 - input_exp, final_result[22:0]}; ack <= 1; end endcase end end endmodule 请修改代码,让算192个FP32的指数和的倒数误差小一点

filetype

// Code your design here module top_design( input [15:0] in, output [15:0] out , output reg [15:0] final_int, output reg [15:0] result_mult ); wire sign_bit = in[15]; //multiplication operation reg Exception; reg Overflow; reg Underflow; // reg [15:0] result_mult; wire [15:0] b; assign b = 16'b0011110111000011; //approx multiplication_16 m1( .a_operand(in), .b_operand(b), .Exception(Exception), .Overflow(Overflow), .Underflow(Underflow), .result(result_mult) ); //solving integer part reg [15:0] result_int; pow2_integer_part_only p( .x_in(result_mult), .result(result_int) ); //converting integer part answer into floating point half precision format //reg [15:0] final_int; int16_to_halfprecision converter( .int_in(result_int), .half_out(final_int) ); //calculating fractional part reg [15:0] final_float; reg [15:0] final_float_ans; fp16_subtractor cs( .a(result_mult), // Operand A .b(final_int), // Operand B .result(final_float) // A - B ); reg [15:0] CONST_9567_Q14 =16'b0011101110100111; half_precision_adder gp( .a(final_float), // Operand A .b(CONST_9567_Q14), // Operand B .result(final_float_ans) // A + B ); //multiplying the final answers reg Exception1; reg Overflow1; reg Underflow1; multiplication_16 m2( .a_operand(final_int), .b_operand(final_float_ans), .Exception(Exception1), .Overflow(Overflow1), .Underflow(Underflow1), .result(out) ); endmodule module fp16_subtractor ( input [15:0] a, // Operand A input [15:0] b, // Operand B output [15:0] result // A - B ); // Internal fields wire sign_a = a[15]; wire sign_b = b[15]; wire [4:0] exp_a = a[14:10]; wire [4:0] exp_b = b[14:10]; wire [10:0] frac_a = {1'b1, a[9:0]}; // Implicit 1 for normalized wire [10:0] frac_b = {1'b1, b[9:0]}; wire [4:0] exp_diff; wire [10:0] frac_b_shifted; wire [11:0] frac_diff; reg [4:0] exp_result; reg [10:0] frac_result; reg sign_result; // Step 1: Align exponents assign exp_diff = (exp_a > exp_b) ? (exp_a - exp_b) : (exp_b - exp_a); assign frac_b_shifted = (exp_a > exp_b) ? (frac_b >> exp_diff) : frac_b; wire [10:0] aligned_frac_a = (exp_a > exp_b) ? frac_a : (frac_a >> exp_diff); // Step 2: Perform subtraction assign frac_diff = aligned_frac_a - frac_b_shifted; always @(*) begin if (exp_a > exp_b) begin exp_result = exp_a; end else begin exp_result = exp_b; end // Determine signa if (a == b) begin frac_result = 0; exp_result = 0; sign_result = 0; end else if (frac_a >= frac_b_shifted) begin frac_result = frac_diff[10:0]; sign_result = sign_a; end else begin frac_result = -frac_diff[10:0]; sign_result = ~sign_a; // Flip sign end // Normalize result (basic leading zero detection) while (frac_result[10] == 0 && exp_result > 0) begin frac_result = frac_result << 1; exp_result = exp_result - 1; end end // Pack the result assign result = {sign_result, exp_result, frac_result[9:0]}; endmodule // Code your design here module half_precision_adder ( input [15:0] a, // Half-precision input 1 input [15:0] b, // Half-precision input 2 output [15:0] result // Half-precision output ); // Internal fields reg sign_a, sign_b, sign_r; reg [4:0] exp_a, exp_b, exp_r; reg [10:0] mant_a, mant_b; reg [11:0] mant_sum; reg [9:0] mant_r; reg [15:0] res; always @(*) begin // Step 1: Extract sign, exponent, mantissa sign_a = a[15]; sign_b = b[15]; exp_a = a[14:10]; exp_b = b[14:10]; mant_a = {1'b1, a[9:0]}; // Prepend implicit 1 mant_b = {1'b1, b[9:0]}; // Step 2: Align exponents if (exp_a > exp_b) begin mant_b = mant_b >> (exp_a - exp_b); exp_r = exp_a; end else begin mant_a = mant_a >> (exp_b - exp_a); exp_r = exp_b; end // Step 3: Perform addition (assuming same sign for simplicity) mant_sum = mant_a + mant_b; // Step 4: Normalize result if (mant_sum[11] == 1) begin mant_sum = mant_sum >> 1; exp_r = exp_r + 1; end mant_r = mant_sum[9:0]; sign_r = 0; // Assuming both inputs are positive // Step 5: Assemble result res = {sign_r, exp_r, mant_r}; end assign result = res; endmodule // Code your design here module int16_to_halfprecision ( input wire signed [15:0] int_in, output reg [15:0] half_out ); reg [15:0] abs_val; reg [4:0] msb_pos; reg [9:0] mantissa; reg [4:0] biased_exp; integer i; always @(*) begin if (int_in == 16'd0) begin half_out = 16'd0; end else begin // Step 1: Get sign and absolute value half_out[15] = int_in[15]; // Sign bit abs_val = int_in[15] ? -int_in : int_in; // Step 2: Find MSB position msb_pos = 0; for (i = 15; i >= 0; i = i - 1) begin if (abs_val[i] == 1'b1 && msb_pos == 0) msb_pos = i[4:0]; end // Step 3: Biased exponent biased_exp = msb_pos + 5'd15; half_out[14:10] = biased_exp; // Step 4: Mantissa extraction if (msb_pos > 10) mantissa = (abs_val >> (msb_pos - 10)) & 10'h3FF; else mantissa = (abs_val << (10 - msb_pos)) & 10'h3FF; half_out[9:0] = mantissa; end end endmodule // Code your design here module pow2_integer_part_only ( input wire [15:0] x_in, // 16-bit half-precision float output reg [15:0] result // 16-bit output = 2^floor(x) ); wire sign; wire [4:0] exponent; wire [9:0] mantissa; reg [15:0] int_part; real value; assign sign = x_in[15]; assign exponent = x_in[14:10]; assign mantissa = x_in[9:0]; always @(*) begin result = 16'd0; if (sign == 1'b1 || exponent == 5'd0) begin // Negative or subnormal/zero result = 16'd1; end else begin // Normalized: 1.mantissa × 2^(exp - 15) // Convert to real value value = (1.0 + mantissa / 1024.0) * (2.0 ** (exponent - 15)); int_part = $floor(value); if (int_part > 15) result = 16'd0; // Avoid overflow else result = 16'd1 << int_part; end end endmodule module multiplication_16( input [15:0] a_operand, input [15:0] b_operand, output Exception, output Overflow, output Underflow, output [15:0] result ); localparam EXP_BIAS = 15; wire sign, normalised, zero, round_bit; wire [10:0] operand_a, operand_b; wire [21:0] product, product_shifted; wire [9:0] guard_bits; wire [5:0] sum_exponent; wire [4:0] exponent; wire [9:0] mantissa; wire a_is_nan = (a_operand[14:10] == 5'b11111) && (a_operand[9:0] != 0); wire b_is_nan = (b_operand[14:10] == 5'b11111) && (b_operand[9:0] != 0); wire a_is_inf = (a_operand[14:10] == 5'b11111) && (a_operand[9:0] == 0); wire b_is_inf = (b_operand[14:10] == 5'b11111) && (b_operand[9:0] == 0); wire a_is_zero = (a_operand[14:0] == 15'd0); wire b_is_zero = (b_operand[14:0] == 15'd0); assign sign = a_operand[15] ^ b_operand[15]; assign Exception = a_is_nan | b_is_nan | (a_is_inf & b_is_zero) | (a_is_zero & b_is_inf); assign operand_a = (a_operand[14:10] == 0) ? {1'b0, a_operand[9:0]} : {1'b1, a_operand[9:0]}; assign operand_b = (b_operand[14:10] == 0) ? {1'b0, b_operand[9:0]} : {1'b1, b_operand[9:0]}; assign product = operand_a * operand_b; assign normalised = product[21]; assign product_shifted = normalised ? product : product << 1; assign guard_bits = product_shifted[10:1]; assign round_bit = |guard_bits; wire [9:0] mantissa_unrounded = product_shifted[20:11]; wire [10:0] mantissa_rounded = mantissa_unrounded + (product_shifted[10] & round_bit); wire mantissa_carry = mantissa_rounded[10]; assign mantissa = mantissa_carry ? 10'd0 : mantissa_rounded[9:0]; wire [5:0] base_exponent = a_operand[14:10] + b_operand[14:10] - EXP_BIAS + normalised + mantissa_carry; assign exponent = base_exponent[4:0]; assign zero = (mantissa == 0 && exponent == 0); // Only assert these if no Exception assign Overflow = ~Exception & (base_exponent[5] & ~base_exponent[4]) & ~zero; assign Underflow = ~Exception & (base_exponent[5] & base_exponent[4]) & ~zero; assign result = Exception ? 16'h7E00 : a_is_inf | b_is_inf ? {sign, 5'b11111, 10'd0} : a_is_zero | b_is_zero ? {sign, 15'd0} : Overflow ? {sign, 5'b11111, 10'd0} : Underflow ? {sign, 15'd0} : {sign, exponent, mantissa}; endmodule分析一下各个模块的含义

filetype

module floatReciprocal #( parameter DATA_WIDTH = 32, // IEEE 754 单精度浮点数 parameter MAX_ITER = 3, // 最大迭代次数(FP32 通常需要 2-3 次) parameter THRESHOLD = 32’h0001 // 收敛阈值(尾数最低位变化容忍) ) ( input [DATA_WIDTH-1:0] number, // 输入浮点数 (IEEE 754 FP32) input enable, // 计算使能信号 (高电平启动) input clk, // 时钟信号 output reg [DATA_WIDTH-1:0] output_rec, // 输出倒数 (1/number) output reg ack // 计算完成确认信号 ); // IEEE 754 单精度浮点数常量定义 localparam ONE = 32’h3F80_0000; // 1.0 localparam TWO = 32’h4000_0000; // 2.0 localparam INIT_GUESS = 32’h3F0C_CCCD; // 初始猜测值 ≈0.55 (经验值) localparam INF_POS = 32’h7F80_0000; // 正无穷大 localparam INF_NEG = 32’hFF80_0000; // 负无穷大 localparam ZERO = 32’h0000_0000; // 零 localparam NAN = 32’h7FC0_0000; // NaN // 内部寄存器 reg [DATA_WIDTH-1:0] current_x; // 当前迭代值 reg [2:0] iter_count; // 迭代计数器 reg [DATA_WIDTH-1:0] Ddash; // 归一化后的输入值 reg input_sign; // 输入符号位 reg [7:0] input_exp; // 输入指数部分 // 牛顿迭代计算单元 wire [DATA_WIDTH-1:0] prod_DX; // Ddash * current_x wire [DATA_WIDTH-1:0] two_minus; // 2 - prod_DX wire [DATA_WIDTH-1:0] x_next; // current_x * two_minus // 浮点运算模块实例化 floatMult mult1 ( .a(Ddash), .b(current_x), .result(prod_DX) ); floatSub sub1 ( .a(TWO), .b(prod_DX), .result(two_minus) ); floatMult mult2 ( .a(current_x), .b(two_minus), .result(x_next) ); // 主状态机 reg [1:0] state; localparam IDLE = 2’b00; localparam INIT = 2’b01; localparam ITERATE = 2’b10; localparam DONE = 2’b11; always @(posedge clk) begin case (state) IDLE: begin ack <= 0; iter_count <= 0; if (enable) begin // 检查特殊情况 if (number[30:0] == 0) begin // 输入为0 output_rec <= (number[31]) ? INF_NEG : INF_POS; ack <= 1; state <= DONE; end else if (number[30:23] == 8'hFF) begin // 无穷大或NaN output_rec <= (number[22:0] == 0) ? ((number[31]) ? ZERO : ZERO) : // 无穷大倒数为0 NAN; // NaN输入输出NaN ack <= 1; state <= DONE; end else begin // 正常数值 // 保存符号和指数 input_sign <= number[31]; input_exp <= number[30:23]; // 归一化输入:构造 D' = {0, 126, mantissa} // 相当于将输入映射到 [0.5, 1.0) 区间 Ddash <= {1'b0, 8'd126, number[22:0]}; // 设置初始猜测值 current_x <= INIT_GUESS; state <= INIT; end end end INIT: begin // 初始迭代准备 iter_count <= 0; state <= ITERATE; end ITERATE: begin // 更新迭代值 current_x <= x_next; iter_count <= iter_count + 1; // 检查终止条件:达到最大迭代次数或收敛 if ((iter_count == MAX_ITER - 1) || (current_x[22:0] - x_next[22:0] < THRESHOLD)) begin state <= DONE; end end DONE: begin // 计算结果: // 1/number = sign * 2^(253 - input_exp) * mantissa(x_next) output_rec <= {input_sign, 8'd(253 - input_exp), x_next[22:0]}; ack <= 1; state <= IDLE; end endcase end endmodule // 浮点减法模块(使用加法器实现) module floatSub #( parameter DATA_WIDTH = 32 ) ( input [DATA_WIDTH-1:0] a, input [DATA_WIDTH-1:0] b, output [DATA_WIDTH-1:0] result ); // 减法实现:a - b = a + (-b) floatAdd add_sub ( .a(a), .b({~b[DATA_WIDTH-1], b[DATA_WIDTH-2:0]}), // 符号位取反 .result(result) ); endmodule 我有浮点数乘法floatMult(product=floatA*floatB)和浮点数加法floatAdd(sum=floatA+floatB),请修改代码

filetype

module softmax(inputs,clk,enable,outputs,ackSoft); parameter DATA_WIDTH=32; localparam inputNum=10; // 十分类 input [DATA_WIDTH*inputNum-1:0] inputs; input clk; input enable; output reg [DATA_WIDTH*inputNum-1:0] outputs; output reg ackSoft; // softmax应答信号,表示计算完毕 wire [DATA_WIDTH-1:0] expSum; // 所有指数和 wire [DATA_WIDTH-1:0] expReciprocal; // expSum的倒数 wire [DATA_WIDTH-1:0] outMul; wire [DATA_WIDTH*inputNum-1:0] exponents; // 所有指数模块的输出整合 wire [inputNum-1:0] acksExp; // 指数模块的应答信号 wire ackDiv; // 倒数模块的应答信号 reg enableDiv; //signal to enable division unit initially zero reg [DATA_WIDTH-1:0] outExpReg; reg [3:0] mulCounter; reg [3:0] addCounter; // 一个输入对应一个指数模块,所以循环10次 genvar i; generate for (i = 0; i < inputNum; i = i + 1) begin exponent #(.DATA_WIDTH(DATA_WIDTH)) exp ( .x(inputs[DATA_WIDTH*i+:DATA_WIDTH]), .enable(enable), .clk(clk), .output_exp(exponents[DATA_WIDTH*i+:DATA_WIDTH]), .ack(acksExp[i])); end endgenerate // 计算所有指数模块的和 floatAdd FADD1 (exponents[DATA_WIDTH*addCounter+:DATA_WIDTH],outExpReg,expSum); // 计算倒数得到分母 floatReciprocal #(.DATA_WIDTH(DATA_WIDTH)) FR (.number(expSum),.clk(clk),.output_rec(expReciprocal),.ack(ackDiv),.enable(enableDiv)); // 计算softmax的值,得到每个种类的分类概率 floatMult FM1 (exponents[DATA_WIDTH*mulCounter+:DATA_WIDTH],expReciprocal,outMul); //multiplication with reciprocal always @ (negedge clk) begin if(enable==1'b1) begin if(ackSoft==1'b0) begin if(acksExp[0]==1'b1) begin // 等待指数模块计算出一个 if(enableDiv==1'b0) begin // 还没有计算倒数 if(addCounter<4'b1001) begin // 计算出了10个和 addCounter=addCounter+1; outExpReg=expSum; end else begin enableDiv=1'b1; // 求倒数模块的启动信号 end end else if(ackDiv==1'b1) begin // 分母算出来,就开始算softmax if(mulCounter<4'b1010) begin outputs[DATA_WIDTH*mulCounter+:DATA_WIDTH]=outMul; mulCounter=mulCounter+1; end else begin ackSoft=1'b1; end end end end end else begin //if enable is off reset all counters and acks mulCounter=4'b0000; addCounter=4'b0000; outExpReg=32'b00000000000000000000000000000000; ackSoft=1'b0; enableDiv=1'b0; end end endmodule这个模块是怎么计算指数、和、softmax值的,串行还是并行

filetype

仿照该模块:module floatReciprocal(number,enable,clk,output_rec,ack); parameter DATA_WIDTH=32; input [DATA_WIDTH-1:0] number; //the number that we need to get the 1/number of input clk,enable; output reg[DATA_WIDTH-1:0] output_rec; // = 1/number output reg ack; wire [DATA_WIDTH-1:0] Ddash; // D’ = Mantissa of D and exponent of -1 wire [DATA_WIDTH-1:0] P2Ddash; // (-32/17) * D’ wire [DATA_WIDTH-1:0] Xi ; // X[i]= 43/17 - (32/17)D’ wire [DATA_WIDTH-1:0] Xip1; //X[i+1] wire [DATA_WIDTH-1:0] out0; // XiD wire [DATA_WIDTH-1:0] out1; // 1-XiD wire [DATA_WIDTH-1:0] out2; // X*(1-Xi*D) reg [DATA_WIDTH-1:0] mux; localparam P1=32’b01000000001101001011010010110101; // 43/17 localparam P2=32’b10111111111100001111000011110001; // -32/17 assign Ddash={{1’b0,8’b01111110},number[22:0]}; floatMult FM1 (P2,Ddash,P2Ddash); // -(32/17)* D’ floatAdd FADD1 (P2Ddash,P1,Xi); // 43/17 * (-32/17)D’ floatMult FM2 (mux,Ddash,out0); // XiD’ floatAdd FSUB1 (32’b00111111100000000000000000000000,{1’b1,out0[DATA_WIDTH-2:0]},out1); // 1-XiD floatMult FM3 (mux,out1,out2); // X*(1-XiD) floatAdd FADD2 (mux,out2,Xip1); //Xi+Xi(1-D*Xi) /always @(number) begin //when a new input is entered the ack signal is reset and the mux is Xi ack=1’b0; //reset finish bit reset=1’b1; end/ always @ (negedge clk) begin if (enable1’b0) begin mux=Xi; ack=1’b0; end else begin if(muxXip1) begin ack=1’b1; //set ack bit to show that the division is done output_rec={{number[31],8’b11111101-number[30:23]},Xip1[22:0]}; //sign of number, new exponent, mantissa of Xip1 end else begin mux=Xip1; //continue until ack is 1 end end end endmodule,请帮我修改下面模块,让其算得和参考模块值一样:module floatReciprocal #( parameter DATA_WIDTH = 32, // IEEE 754 单精度浮点数 parameter MAX_ITER = 3, // 最大迭代次数(FP32 通常需要 2-3 次) parameter THRESHOLD = 32’h0001 // 收敛阈值(尾数最低位变化容忍) ) ( input [DATA_WIDTH-1:0] number, // 输入浮点数 (IEEE 754 FP32) input enable, // 计算使能信号 (高电平启动) input clk, // 时钟信号 input rstn, // 复位信号 (低有效) output reg [DATA_WIDTH-1:0] output_rec, // 输出倒数 (1/number) output reg ack // 计算完成确认信号 ); // IEEE 754 单精度浮点数常量定义 localparam ONE = 32’h3F80_0000; // 1.0 localparam TWO = 32’h4000_0000; // 2.0 localparam INIT_GUESS = 32’h3F0C_CCCD; // 初始猜测值 ≈0.55 (经验值) localparam INF_POS = 32’h7F80_0000; // 正无穷大 localparam INF_NEG = 32’hFF80_0000; // 负无穷大 localparam ZERO = 32’h0000_0000; // 零 localparam NAN = 32’h7FC0_0000; // NaN // 内部寄存器 reg [DATA_WIDTH-1:0] current_x; // 当前迭代值 reg [2:0] iter_count; // 迭代计数器 reg [DATA_WIDTH-1:0] Ddash; // 归一化后的输入值 reg input_sign; // 输入符号位 reg [7:0] input_exp; // 输入指数部分 reg [DATA_WIDTH-1:0] final_result; // 最终迭代结果 // 牛顿迭代计算单元 wire [DATA_WIDTH-1:0] prod_DX; // Ddash * current_x wire [DATA_WIDTH-1:0] two_minus; // 2 - prod_DX wire [DATA_WIDTH-1:0] x_next; // current_x * two_minus // 浮点乘法器实例化 floatMult mult1 ( .floatA(Ddash), .floatB(current_x), .product(prod_DX) ); // 浮点加法器实现减法: 2 - prod_DX floatAdd sub1 ( .floatA(TWO), .floatB({~prod_DX[31], prod_DX[30:0]}), // 取负:符号位取反 .sum(two_minus) ); // 浮点乘法器实例化 floatMult mult2 ( .floatA(current_x), .floatB(two_minus), .product(x_next) ); // 主状态机 reg [1:0] current_state, next_state; localparam IDLE = 2’b00; localparam INIT = 2’b01; localparam ITERATE = 2’b10; localparam DONE = 2’b11; // 收敛检测:计算绝对差值 wire [22:0] abs_diff = (current_x[22:0] > x_next[22:0]) ? (current_x[22:0] - x_next[22:0]) : (x_next[22:0] - current_x[22:0]); always @(posedge clk or negedge rstn) begin if (!rstn) begin current_state <= IDLE; end else begin current_state <= next_state; end end always @(*) begin next_state = current_state; case(current_state) IDLE: begin if(enable) begin if((number[30:0] == 0) || (number[30:23] == 8’hFF)) next_state = DONE; else next_state = INIT; end end INIT: begin next_state = ITERATE; end ITERATE: begin if ((iter_count == MAX_ITER - 1) || (abs_diff < THRESHOLD)) next_state <= DONE; end DONE: begin if(ack) next_state = IDLE; end default: next_state = IDLE; endcase end always @(posedge clk or negedge rstn) begin if (!rstn) begin ack <= 0; iter_count <= 0; output_rec <= 0; input_sign <= 0; input_exp <= 0; Ddash <= 0; end else begin case (current_state) IDLE: begin if (enable) begin // 检查特殊情况 if (number[30:0] == 0) begin // 输入为0 output_rec <= (number[31]) ? INF_NEG : INF_POS; ack <= 1; end else if (number[30:23] == 8’hFF) begin // 无穷大或NaN output_rec <= (number[22:0] == 0) ? ZERO : // 无穷大倒数为0 NAN; // NaN输入输出NaN ack <= 1; end else begin // 正常数值 // 保存符号和指数 input_sign <= number[31]; input_exp <= number[30:23]; // 归一化输入:构造 D' = {0, 126, mantissa} Ddash <= {1'b0, 8'd126, number[22:0]}; // 设置初始猜测值 current_x <= INIT_GUESS; end end end INIT: begin // 初始迭代准备 iter_count <= 0; end ITERATE: begin // 更新迭代值 current_x <= x_next; iter_count <= iter_count + 1; // 检查终止条件:达到最大迭代次数或收敛 if ((iter_count == MAX_ITER - 1) || (abs_diff < THRESHOLD)) begin final_result <= x_next; // 保存最终结果 end end DONE: begin // 计算结果:1/number = sign * 2^(253 - input_exp) * mantissa output_rec <= {input_sign, 8'b11111101-number[30:23], final_result[22:0]}; ack <= 1; end endcase end end endmodule

filetype

module EigenDecomposition4x4 ( input clk, input rst, input load_en, input [3:0] addr_in, input [15:0] data_in, output reg [15:0] data_out, output reg done ); // 存储器定义 reg [15:0] A_mem [0:3][0:3]; // 存储输入矩阵 reg [15:0] V_mem [0:3][0:3]; // 存储特征向量矩阵 // 状态定义 reg [2:0] state; localparam IDLE = 0, FIND_MAX = 1, CALC_ANGLE = 2, ROTATE = 3, UPDATE_V = 4, CONVERGE_CHECK = 5; reg [1:0] p, q; // 最大非对角元素的位置 reg [15:0] max_val; // 最大非对角元素的值 reg [3:0] counter; // 循环计数器 reg [3:0] iter_count; // 迭代次数计数器 // CORDIC接口 wire [15:0] theta; reg [15:0] x_in, y_in; reg cordic_start; wire cordic_done; // 旋转参数 reg [15:0] sin_val, cos_val; reg signed [31:0] mult_temp; // 收敛判断 reg converge; // 矩阵索引 wire [1:0] row_idx = addr_in[3:2]; wire [1:0] col_idx = addr_in[1:0]; integer i,j,k; // 实例化CORDIC模块 cordic_arctan u_cordic( .clk(clk), .rst(rst), .start(cordic_start), .x_in(x_in), .y_in(y_in), .theta_out(theta), .done(cordic_done) ); // 存储控制逻辑 always @(posedge clk) begin if (rst) begin // 初始化特征向量矩阵为单位矩阵 for ( i = 0; i < 4; i = i + 1) begin for ( j = 0; j < 4; j = j + 1) begin V_mem[i][j] <= (i == j) ? 16'h4000 : 16'h0000; // 1.0 in Q2.14 end end end else if (load_en && state == IDLE) begin // 加载矩阵数据 A_mem[row_idx][col_idx] <= data_in; end end // 数据输出 always @(posedge clk) begin if (state == IDLE) begin data_out <= V_mem[row_idx][col_idx]; end end // 状态机控制 always @(posedge clk or posedge rst) begin if (rst) begin state <= IDLE; counter <= 0; iter_count <= 0; cordic_start <= 0; done <= 0; max_val <= 0; converge <= 0; end else begin case(state) IDLE: begin done <= 0; if (load_en) begin state <= FIND_MAX; counter <= 0; max_val <= 0; iter_count <= 0; end end FIND_MAX: begin if (counter == 4'd15) begin state <= (max_val > 16'h0100) ? CALC_ANGLE : CONVERGE_CHECK; // 准备CORDIC输入 x_in <= A_mem[q][q] - A_mem[p][p]; y_in <= (A_mem[p][q] << 1); // 2*A[p][q] cordic_start <= 1; end else begin // 只遍历上三角部分 (i < j) if (counter[3:2] < counter[1:0]) begin if (A_mem[counter[3:2]][counter[1:0]] > max_val) begin max_val <= A_mem[counter[3:2]][counter[1:0]]; p <= counter[3:2]; q <= counter[1:0]; end end counter <= counter + 1; end end CALC_ANGLE: begin cordic_start <= 0; if (cordic_done) begin state <= ROTATE; // 计算sin/cos // 简化实现 - 实际应用中需使用更精确的方法 if (theta[15]) begin // 负角 sin_val <= -((16'h4000 - (theta >> 2)) << 2); cos_val <= ((16'h4000 - (theta[14:0] >> 2)) << 2); end else begin sin_val <= (theta >> 2) << 2; cos_val <= (16'h4000 - (theta >> 2)) << 2; end counter <= 0; end end ROTATE: begin // 执行雅可比旋转(仅更新受影响的行/列) for ( k = 0; k < 4; k = k + 1) begin // 更新A矩阵行 if (k == p k == q) begin // 临时存储行元素 reg [15:0] apk, aqk; apk = A_mem[p][k]; aqk = A_mem[q][k]; // 旋转乘法 // A[p][k] = cos*apk - sin*aqk mult_temp = cos_val * apk; mult_temp = mult_temp - sin_val * aqk; A_mem[p][k] <= mult_temp >>> 14; // Q2.14乘法调整 // A[q][k] = sin*apk + cos*aqk mult_temp = sin_val * apk; mult_temp = mult_temp + cos_val * aqk; A_mem[q][k] <= mult_temp >>> 14; end // 更新V矩阵列 if (k < 4) begin // V[k][p] = cos*V[k][p] - sin*V[k][q] mult_temp = cos_val * V_mem[k][p]; mult_temp = mult_temp - sin_val * V_mem[k][q]; V_mem[k][p] <= mult_temp >>> 14; // V[k][q] = sin*V[k][p] + cos*V[k][q] mult_temp = sin_val * V_mem[k][p]; mult_temp = mult_temp + cos_val * V_mem[k][q]; V_mem[k][q] <= mult_temp >>> 14; end end // 确保对称性 (A[i][j] = A[j][i]) for ( i = 0; i < 4; i = i + 1) begin for ( j = i + 1; j < 4; j = j + 1) begin A_mem[i][j] <= A_mem[j][i]; end end state <= UPDATE_V; end UPDATE_V: begin iter_count <= iter_count + 1; counter <= 0; max_val <= 0; state <= (iter_count >= 10) ? CONVERGE_CHECK : FIND_MAX; end CONVERGE_CHECK: begin converge <= (max_val < 16'h0100); state <= IDLE; done <= 1; end default: state <= IDLE; endcase end end endmodule检查并修改要求可以在vivado 2018.3软件中可以运行(使用VerilogHDL与语言)

filetype

`timescale 1ns / 1ps //fp16乘法器,输出fp32 module fp16_mult ( input [15:0] a, input [15:0] b, output reg [31:0] result ); reg sign_a ; reg [4:0] exp_a; reg [9:0] mant_a; reg sign_b ; reg [4:0] exp_b ; reg [9:0] mant_b ; reg [10:0] fraction_a; reg [10:0] fraction_b; reg sign_result; reg [7:0] exp_result; reg [22:0] mant_result; reg [7:0] exp_result_product; reg [21:0] mant_result_product; integer i; integer highest_bit; integer flag; wire a_is_nan = (exp_a == 5'b11111) && (mant_a != 0); wire b_is_nan = (exp_b == 5'b11111) && (mant_b != 0); wire a_is_inf = (exp_a == 5'b11111) && (mant_a == 0); wire b_is_inf = (exp_b == 5'b11111) && (mant_b == 0); always @(*) begin sign_a = a[15]; exp_a = a[14:10]; mant_a = a[9:0]; sign_b = b[15]; exp_b = b[14:10]; mant_b = b[9:0]; fraction_a={1'b1,mant_a}; fraction_b={1'b1,mant_b}; sign_result=sign_a^sign_b; mant_result_product=fraction_a*fraction_b; exp_result_product={3'b000, exp_a} + {3'b000, exp_b} - 8'd30+8'd127; flag=1; for (i=21;(i>=0) && flag;i=i-1) begin if(mant_result_product[i])begin highest_bit = i; flag=0; end end if(highest_bit==21)begin mant_result_product=mant_result_product<<1; mant_result={mant_result_product,1'b0}; exp_result=exp_result_product+1; end else begin mant_result_product=mant_result_product<<2; mant_result={mant_result_product,1'b0}; exp_result=exp_result_product; end //任何数 x nan 等于nan if (a_is_nan || b_is_nan) result = {sign_result, 8'b11111111, 23'b10000000000000000000000}; //Inf × 0 等于nan else if ((a == 16'b0 && b_is_inf) || (b == 16'b0 && a_is_inf)) result = {sign_result, 8'b11111111, 23'b10000000000000000000000}; //inf x 非0 等于 inf else if ((a_is_inf && b_is_inf) || (a_is_inf && |mant_b) || (b_is_inf && |mant_a)) result = {sign_re

局外狗
  • 粉丝: 94
上传资源 快速赚钱