From 259bda2ca2bea9f7e170672254d10dd15b27eaab Mon Sep 17 00:00:00 2001 From: Zhongdi LUO Date: Sun, 12 Jul 2026 23:58:49 +0000 Subject: [PATCH] feat: make blackwell fexp synthesis-ready --- hw/rtl/VX_platform.vh | 25 +++++- hw/rtl/fpu/VX_fpu_exp.sv | 175 +++++++++++++++++++++++++++++++++++---- 2 files changed, 181 insertions(+), 19 deletions(-) diff --git a/hw/rtl/VX_platform.vh b/hw/rtl/VX_platform.vh index bf544995..278fc685 100644 --- a/hw/rtl/VX_platform.vh +++ b/hw/rtl/VX_platform.vh @@ -32,10 +32,27 @@ `define DCACHE_DISABLE `ifdef SYNTHESIS -`define NUM_BARRIERS 8 -`define NUM_CORES 4 -`define NUM_THREADS 8 -`define NUM_WARPS 8 +`ifndef NUM_BARRIERS +`define NUM_BARRIERS 4 +`endif +`ifndef NUM_CORES +`define NUM_CORES 1 +`endif +`ifndef NUM_THREADS +`define NUM_THREADS 4 +`endif +`ifndef NUM_WARPS +`define NUM_WARPS 4 +`endif +`ifndef NUM_TENSOR_WARPS +`define NUM_TENSOR_WARPS 2 +`endif +`ifndef LSUQ_SIZE +`define LSUQ_SIZE 32 +`endif +`ifndef EXT_T_BLACKWELL +`define EXT_T_BLACKWELL +`endif `define FPU_FPNEW // `define FIRESIM diff --git a/hw/rtl/fpu/VX_fpu_exp.sv b/hw/rtl/fpu/VX_fpu_exp.sv index 1ba68b3c..cb54efd5 100644 --- a/hw/rtl/fpu/VX_fpu_exp.sv +++ b/hw/rtl/fpu/VX_fpu_exp.sv @@ -12,11 +12,6 @@ // limitations under the License. `include "VX_fpu_define.vh" -`ifdef SV_DPI -`include "float_dpi.vh" -`else -`ERROR(("VX_fpu_exp requires SV_DPI; replace dpi_fexp with synthesizable exp RTL for synthesis")) -`endif module VX_fpu_exp import VX_fpu_pkg::*; #( parameter NUM_LANES = 1, @@ -49,6 +44,163 @@ module VX_fpu_exp import VX_fpu_pkg::*; #( fflags_t [NUM_LANES-1:0] per_lane_fflags; wire [NUM_LANES-1:0] lane_mask_out; + // Convert finite FP32 to signed Q12. Values outside the useful FP32 exp + // input range are saturated; the range-reduction logic below will turn + // them into the appropriate overflow or underflow result. + function automatic signed [23:0] f32_to_q12; + input [31:0] value; + reg [7:0] exponent; + reg [23:0] mantissa; + reg [63:0] magnitude; + integer shift; + begin + exponent = value[30:23]; + mantissa = {1'b1, value[22:0]}; + magnitude = 0; + if (exponent == 0) begin + f32_to_q12 = 24'sd0; + end else begin + shift = integer'(exponent) - 138; + if (shift >= 0) begin + if (shift < 40) + magnitude = {40'd0, mantissa} << shift; + else + magnitude = 64'h7fffff; + end else if (shift > -64) begin + magnitude = {40'd0, mantissa} >> (-shift); + end + if (magnitude > 64'h7fffff) + magnitude = 64'h7fffff; + f32_to_q12 = value[31] + ? -$signed({1'b0, magnitude[22:0]}) + : $signed({1'b0, magnitude[22:0]}); + end + end + endfunction + + // Q20 samples of exp(index / 16). The same LUT/interpolation structure is + // used by the TMEM softmax engine. Range reduction guarantees index <= 11. + function automatic [23:0] exp_frac_lut; + input [3:0] index; + begin + case (index) + 4'd0: exp_frac_lut = 24'd1048576; + 4'd1: exp_frac_lut = 24'd1116203; + 4'd2: exp_frac_lut = 24'd1188192; + 4'd3: exp_frac_lut = 24'd1264824; + 4'd4: exp_frac_lut = 24'd1346398; + 4'd5: exp_frac_lut = 24'd1433233; + 4'd6: exp_frac_lut = 24'd1525669; + 4'd7: exp_frac_lut = 24'd1624066; + 4'd8: exp_frac_lut = 24'd1728810; + 4'd9: exp_frac_lut = 24'd1840308; + 4'd10: exp_frac_lut = 24'd1958998; + 4'd11: exp_frac_lut = 24'd2085342; + default: exp_frac_lut = 24'd2219835; + endcase + end + endfunction + + function automatic [23:0] interpolate_exp_frac; + input [3:0] index; + input [7:0] remainder; + reg [23:0] lower; + reg [23:0] upper; + reg [31:0] delta; + reg [31:0] product; + reg [31:0] interpolated; + begin + lower = exp_frac_lut(index); + upper = exp_frac_lut(index + 1'b1); + delta = {8'd0, upper} - {8'd0, lower}; + product = delta * remainder; + interpolated = {8'd0, lower} + ((product + 32'd128) >> 8); + interpolate_exp_frac = interpolated[23:0]; + end + endfunction + + // Synthesizable FP32 exponential approximation. + // + // x = k*ln(2) + r, 0 <= r < ln(2) + // exp(x) = 2^k * exp(r) + // + // r is evaluated using the softmax-style Q12/LUT/interpolation datapath. + // The packed return value is {NV,DZ,OF,UF,NX,result[31:0]}. + function automatic [36:0] exp_approx_f32; + input [31:0] value; + reg signed [23:0] x_q12; + integer signed k; + integer signed rem_q12; + integer signed exp_field; + integer shift; + reg [23:0] exp_r_q20; + reg [23:0] significand; + reg [23:0] shifted; + reg [23:0] discarded_mask; + reg [23:0] discarded; + reg [23:0] halfway; + reg [22:0] mantissa; + reg [31:0] fp_result; + reg [4:0] fp_flags; + begin + fp_result = 32'h3f800000; + fp_flags = 5'b00000; + + if (value[30:23] == 8'hff) begin + if (value[22:0] != 0) begin + fp_result = 32'h7fc00000; + fp_flags[4] = ~value[22]; + end else if (value[31]) begin + fp_result = 32'h00000000; + end else begin + fp_result = 32'h7f800000; + end + end else if (value[30:0] != 0) begin + x_q12 = f32_to_q12(value); + k = integer'($signed(x_q12)) / 2839; + rem_q12 = integer'($signed(x_q12)) - k * 2839; + if (rem_q12 < 0) begin + k = k - 1; + rem_q12 = rem_q12 + 2839; + end + + exp_r_q20 = interpolate_exp_frac( + 4'(rem_q12 >> 8), 8'(rem_q12)); + significand = {1'b1, exp_r_q20[19:0], 3'b000}; + exp_field = 127 + k; + fp_flags[0] = 1'b1; + + if (exp_field >= 255) begin + fp_result = 32'h7f800000; + fp_flags[2] = 1'b1; + end else if (exp_field > 0) begin + mantissa = significand[22:0]; + fp_result = {1'b0, 8'(exp_field), mantissa}; + end else begin + shift = 1 - exp_field; + fp_flags[1] = 1'b1; + if (shift >= 25) begin + fp_result = 32'h00000000; + end else begin + shifted = significand >> shift; + discarded_mask = (24'd1 << shift) - 1'b1; + discarded = significand & discarded_mask; + halfway = 24'd1 << (shift - 1); + if ((discarded > halfway) + || ((discarded == halfway) && shifted[0])) + shifted = shifted + 1'b1; + if (shifted[23]) + fp_result = 32'h00800000; + else + fp_result = {9'd0, shifted[22:0]}; + end + end + end + + exp_approx_f32 = {fp_flags, fp_result}; + end + endfunction + VX_shift_register #( .DATAW (1 + NUM_LANES + TAGW), .DEPTH (`LATENCY_FEXP), @@ -64,23 +216,16 @@ module VX_fpu_exp import VX_fpu_pkg::*; #( assign ready_in = enable; for (genvar i = 0; i < NUM_LANES; ++i) begin - reg [63:0] r; - `UNUSED_VAR (r) - - fflags_t f; - - always @(*) begin - dpi_fexp(enable && valid_in, int'(0), {32'hffffffff, dataa[i]}, r, f); - end + wire [36:0] exp_approx = exp_approx_f32(dataa[i]); VX_shift_register #( .DATAW (32 + $bits(fflags_t)), .DEPTH (`LATENCY_FEXP) - ) shift_req_dpi ( + ) shift_req_exp ( .clk (clk), `UNUSED_PIN (reset), .enable (enable), - .data_in ({r[31:0], f}), + .data_in ({exp_approx[31:0], exp_approx[36:32]}), .data_out ({result[i], per_lane_fflags[i]}) ); end