diff --git a/rtl/fus/ternip_rowwise_operation.sv b/rtl/fus/ternip_rowwise_operation.sv index a17bb62..8d1edf7 100644 --- a/rtl/fus/ternip_rowwise_operation.sv +++ b/rtl/fus/ternip_rowwise_operation.sv @@ -466,7 +466,7 @@ ternip_sig_parallelized #( .FixedPointExponent(Cfg.FixedPointExponent), .VectorParallelism(Cfg.VectorParallelism), .LutParallelism(Cfg.LutParallelism), - .UseHardSigmoid(Cfg.UseHardSigmoid) + .SigmoidModel(Cfg.SigmoidModel) ) sig_parallelized ( .clk_i, .rst_ni, @@ -485,7 +485,7 @@ ternip_csig_parallelized #( .FixedPointExponent(Cfg.FixedPointExponent), .VectorParallelism(Cfg.VectorParallelism), .LutParallelism(Cfg.LutParallelism), - .UseHardSigmoid(Cfg.UseHardSigmoid) + .SigmoidModel(Cfg.SigmoidModel) ) csig_parallelized ( .clk_i, .rst_ni, @@ -504,7 +504,7 @@ ternip_silu_parallelized #( .FixedPointExponent(Cfg.FixedPointExponent), .VectorParallelism(Cfg.VectorParallelism), .LutParallelism(Cfg.LutParallelism), - .UseHardSigmoid(Cfg.UseHardSigmoid) + .SigmoidModel(Cfg.SigmoidModel) ) silu_parallelized ( .clk_i, .rst_ni, diff --git a/rtl/math/ternip_csig.sv b/rtl/math/ternip_csig.sv index 29ea004..63dd7ea 100644 --- a/rtl/math/ternip_csig.sv +++ b/rtl/math/ternip_csig.sv @@ -30,8 +30,9 @@ // // Scalar fixed-point complementary sigmoid. // -// Computes y_o = 1 - sigmoid(a_i). With UseHardSigmoid set, this uses a -// first-order approximation. Otherwise it reads a precomputed LUT. +// Computes y_o = 1 - sigmoid(a_i). SIGMOID_LUT reads a +// precomputed LUT; every approximation instantiates ternip_sig and subtracts, +// which is exact for the complement and avoids duplicating the approximation. // This is combinational. module ternip_csig #( @@ -39,7 +40,7 @@ module ternip_csig #( parameter int FixedPointPrecision = Cfg.FixedPointPrecision, parameter int FixedPointExponent = Cfg.FixedPointExponent, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0] ) ( @@ -48,28 +49,30 @@ module ternip_csig #( ); localparam fixed_point_t FixedPointOne = ternip_pkg::fixed_point_one(FixedPointExponent); -localparam int FixedPointUnaryOperationLutSize = UseHardSigmoid ? 1 : (2 ** FixedPointPrecision); +localparam int FixedPointUnaryOperationLutSize = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? (2 ** FixedPointPrecision) : 1; -if (UseHardSigmoid) begin : gen_hard_csig - - // 1 - hard_sigmoid(x) = clamp(0.5 - x/4, 0, 1) - always_comb begin - fixed_point_t linear; - linear = (FixedPointOne / 2) - (a_i / 4); - if (linear <= 0) - y_o = '0; - else if (linear >= FixedPointOne) - y_o = FixedPointOne; - else - y_o = linear; - end - -end else begin : gen_lut_csig +if (SigmoidModel == ternip_pkg::SIGMOID_LUT) begin : gen_lut_csig fixed_point_t CSIGMOID_LUT [FixedPointUnaryOperationLutSize]; initial $readmemh(`READMEM_PATH(LUT_csig_FixedPoint_to_FixedPoint.memh), CSIGMOID_LUT); assign y_o = CSIGMOID_LUT[$unsigned(a_i)]; +end else begin : gen_approx_csig + + fixed_point_t sigmoid_result; + + ternip_sig #( + .FixedPointPrecision(FixedPointPrecision), + .FixedPointExponent(FixedPointExponent), + .SigmoidModel(SigmoidModel) + ) sigmoid ( + .a_i, + .y_o(sigmoid_result) + ); + + assign y_o = FixedPointOne - sigmoid_result; + end endmodule diff --git a/rtl/math/ternip_csig_parallelized.sv b/rtl/math/ternip_csig_parallelized.sv index 25df822..e2a1c39 100644 --- a/rtl/math/ternip_csig_parallelized.sv +++ b/rtl/math/ternip_csig_parallelized.sv @@ -39,7 +39,7 @@ module ternip_csig_parallelized #( parameter int FixedPointExponent = Cfg.FixedPointExponent, parameter int VectorParallelism = Cfg.VectorParallelism, parameter int LutParallelism = Cfg.LutParallelism, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0], localparam type vector_chunk_t = fixed_point_t [VectorParallelism-1:0] @@ -56,7 +56,10 @@ module ternip_csig_parallelized #( output vector_chunk_t vector_data_o ); -localparam int Parallelism = UseHardSigmoid ? VectorParallelism : LutParallelism; +// Only the LUT implementation is area-limited; the arithmetic approximations +// run at full vector width. +localparam int Parallelism = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? LutParallelism : VectorParallelism; logic r_in_ready; logic r_in_valid; @@ -65,6 +68,8 @@ logic r_out_ready; logic r_out_valid; fixed_point_t [Parallelism-1:0] r_out_data; +fixed_point_t [Parallelism-1:0] pipelined_data; + logic w_in_ready; logic w_in_valid; fixed_point_t [Parallelism-1:0] w_in_data; @@ -94,20 +99,36 @@ bsg_parallel_in_serial_out #( .yumi_i(r_out_ready && r_out_valid) ); +// Register the serialiser output before the combinational csig. Without this +// stage the path runs PISO memory read -> csig -> SIPO setup in a single +// cycle, which is the dominant critical path in rowwise_operation. +ternip_pipelined_interconnect #( + .DataWidth(Parallelism*FixedPointPrecision), + .NumStages(1) +) piso_csig_pipeline ( + .clk_i, + .rst_ni, + + .in_ready_o(r_out_ready), + .in_valid_i(r_out_valid), + .in_data_i(r_out_data), + + .out_ready_i(w_in_ready), + .out_valid_o(w_in_valid), + .out_data_o(pipelined_data) +); + for (genvar i_GEN = 0; i_GEN < Parallelism; i_GEN++) begin ternip_csig #( .FixedPointPrecision(FixedPointPrecision), .FixedPointExponent(FixedPointExponent), - .UseHardSigmoid(UseHardSigmoid) + .SigmoidModel(SigmoidModel) ) csig ( - .a_i(r_out_data[i_GEN]), + .a_i(pipelined_data[i_GEN]), .y_o(w_in_data[i_GEN]) ); end -assign r_out_ready = w_in_ready; -assign w_in_valid = r_out_valid; - // https://github.com/bespoke-silicon-group/basejump_stl/blob/a43571d2/bsg_dataflow/bsg_serial_in_parallel_out_full.sv bsg_serial_in_parallel_out_full #( .width_p(Parallelism*FixedPointPrecision), diff --git a/rtl/math/ternip_sig.sv b/rtl/math/ternip_sig.sv index f6a2a4f..3fdc2af 100644 --- a/rtl/math/ternip_sig.sv +++ b/rtl/math/ternip_sig.sv @@ -30,16 +30,16 @@ // // Scalar fixed-point sigmoid. // -// Computes y_o = sigmoid(a_i). With UseHardSigmoid set, this uses a -// first-order approximation. Otherwise it reads a precomputed LUT. -// This is combinational. +// Computes y_o = sigmoid(a_i). SigmoidModel selects a precomputed LUT (exact, +// but 2**FixedPointPrecision entries) or one of six piecewise-linear minimax +// approximations; see sigmoid_model_e for their errors. This is combinational. module ternip_sig #( parameter ternip_pkg::ternip_cfg_t Cfg = `TERNIP_CFG, parameter int FixedPointPrecision = Cfg.FixedPointPrecision, parameter int FixedPointExponent = Cfg.FixedPointExponent, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0] ) ( input fixed_point_t a_i, @@ -47,27 +47,90 @@ module ternip_sig #( ); localparam fixed_point_t FixedPointOne = ternip_pkg::fixed_point_one(FixedPointExponent); -localparam int FixedPointUnaryOperationLutSize = UseHardSigmoid ? 1 : (2 ** FixedPointPrecision); +localparam int FixedPointUnaryOperationLutSize = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? (2 ** FixedPointPrecision) : 1; -if (UseHardSigmoid) begin : gen_hard_sig +if (SigmoidModel == ternip_pkg::SIGMOID_LUT) begin : gen_lut_sig + + fixed_point_t SIGMOID_LUT [FixedPointUnaryOperationLutSize]; + initial $readmemh(`READMEM_PATH(LUT_sig_FixedPoint_to_FixedPoint.memh), SIGMOID_LUT); + assign y_o = SIGMOID_LUT[$unsigned(a_i)]; + +end else begin : gen_piecewise_sig + + // y = slope*x + intercept on whichever segment holds x; 0 below the first + // segment, 1 at or above the last bound. + // + // The segment is chosen FIRST and only then is the arithmetic done, so this + // costs one shift/multiply rather than one per segment. Evaluating every + // segment in parallel and muxing the results afterwards cost ~2.8 ns of + // slack on a D=2048 build -- the model was fine, the structure was not. + // + // POWER2 models have every slope a negative power of two, so the scaling is + // a variable right shift. The bias term makes that shift truncate toward + // zero rather than floor, which is what reproduces the long-standing hard + // sigmoid bit-for-bit under SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER. + localparam int NumSegments = ternip_pkg::sigmoid_segment_count(SigmoidModel); + localparam bit SlopesArePowersOfTwo = ternip_pkg::sigmoid_slopes_are_powers_of_two(SigmoidModel); + localparam int SlopeFractionBits = 16; + + fixed_point_t selected_intercept; + logic [7:0] selected_shift; // POWER2 path: log2(1/slope) + longint selected_slope; // general path: slope << SlopeFractionBits + logic below_first_segment; + logic above_last_segment; - // hard_sigmoid(x) = clamp(x/4 + 0.5, 0, 1) always_comb begin - fixed_point_t linear; - linear = (a_i / 4) + (FixedPointOne / 2); - if (linear <= 0) - y_o = '0; - else if (linear >= FixedPointOne) - y_o = FixedPointOne; - else - y_o = linear; + selected_intercept = '0; + selected_shift = '0; + selected_slope = 0; + below_first_segment = 0; + above_last_segment = 1; + for (int segment_index = NumSegments-1; segment_index >= 0; segment_index--) begin + if (a_i < fixed_point_t'($rtoi( + ternip_pkg::sigmoid_segment_upper_bound(SigmoidModel, segment_index) + * (2.0 ** (-FixedPointExponent)) + + ((ternip_pkg::sigmoid_segment_upper_bound(SigmoidModel, segment_index) < 0.0) + ? -0.5 : 0.5)))) begin + selected_intercept = fixed_point_t'($rtoi( + ternip_pkg::sigmoid_segment_intercept(SigmoidModel, segment_index) + * (2.0 ** (-FixedPointExponent)) + 0.5)); + // Plain assignment, not a width cast: sv2v leaves a literal-width + // cast like 8'(...) untranslated and Vivado's Verilog parser + // rejects it. Assignment to the 8-bit target truncates the same way. + selected_shift = $rtoi( + -$ln(ternip_pkg::sigmoid_segment_slope(SigmoidModel, segment_index)) / $ln(2.0) + 0.5); + selected_slope = longint'( + ternip_pkg::sigmoid_segment_slope(SigmoidModel, segment_index) + * (2.0 ** SlopeFractionBits) + 0.5); + above_last_segment = 0; + end + end + below_first_segment = (a_i < fixed_point_t'($rtoi( + -ternip_pkg::sigmoid_segment_upper_bound(SigmoidModel, NumSegments-1) + * (2.0 ** (-FixedPointExponent)) - 0.5))); end -end else begin : gen_lut_sig + fixed_point_t scaled_input; - fixed_point_t SIGMOID_LUT [FixedPointUnaryOperationLutSize]; - initial $readmemh(`READMEM_PATH(LUT_sig_FixedPoint_to_FixedPoint.memh), SIGMOID_LUT); - assign y_o = SIGMOID_LUT[$unsigned(a_i)]; + if (SlopesArePowersOfTwo) begin : gen_shift_scale + // Bias makes the arithmetic shift truncate toward zero, matching a divide. + fixed_point_t truncation_bias; + assign truncation_bias = (a_i < 0) + ? fixed_point_t'((1 << selected_shift) - 1) + : '0; + assign scaled_input = fixed_point_t'((a_i + truncation_bias) >>> selected_shift); + end else begin : gen_multiply_scale + logic signed [FixedPointPrecision+SlopeFractionBits+1:0] product; + assign product = selected_slope * a_i; + assign scaled_input = fixed_point_t'(product / (2 ** SlopeFractionBits)); + end + + always_comb begin + if (below_first_segment) y_o = '0; + else if (above_last_segment) y_o = FixedPointOne; + else y_o = scaled_input + selected_intercept; + end end diff --git a/rtl/math/ternip_sig_parallelized.sv b/rtl/math/ternip_sig_parallelized.sv index 3c896f0..38e8cbd 100644 --- a/rtl/math/ternip_sig_parallelized.sv +++ b/rtl/math/ternip_sig_parallelized.sv @@ -39,7 +39,7 @@ module ternip_sig_parallelized #( parameter int FixedPointExponent = Cfg.FixedPointExponent, parameter int VectorParallelism = Cfg.VectorParallelism, parameter int LutParallelism = Cfg.LutParallelism, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0], localparam type vector_chunk_t = fixed_point_t [VectorParallelism-1:0] @@ -56,7 +56,10 @@ module ternip_sig_parallelized #( output vector_chunk_t vector_data_o ); -localparam int Parallelism = UseHardSigmoid ? VectorParallelism : LutParallelism; +// Only the LUT implementation is area-limited; the arithmetic approximations +// run at full vector width. +localparam int Parallelism = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? LutParallelism : VectorParallelism; logic r_in_ready; logic r_in_valid; @@ -65,6 +68,8 @@ logic r_out_ready; logic r_out_valid; fixed_point_t [Parallelism-1:0] r_out_data; +fixed_point_t [Parallelism-1:0] pipelined_data; + logic w_in_ready; logic w_in_valid; fixed_point_t [Parallelism-1:0] w_in_data; @@ -94,20 +99,36 @@ bsg_parallel_in_serial_out #( .yumi_i(r_out_ready && r_out_valid) ); +// Register the serialiser output before the combinational sigmoid. Without this +// stage the path runs PISO memory read -> sigmoid -> SIPO setup in a single +// cycle, which is the dominant critical path in rowwise_operation. +ternip_pipelined_interconnect #( + .DataWidth(Parallelism*FixedPointPrecision), + .NumStages(1) +) piso_sig_pipeline ( + .clk_i, + .rst_ni, + + .in_ready_o(r_out_ready), + .in_valid_i(r_out_valid), + .in_data_i(r_out_data), + + .out_ready_i(w_in_ready), + .out_valid_o(w_in_valid), + .out_data_o(pipelined_data) +); + for (genvar i_GEN = 0; i_GEN < Parallelism; i_GEN++) begin ternip_sig #( .FixedPointPrecision(FixedPointPrecision), .FixedPointExponent(FixedPointExponent), - .UseHardSigmoid(UseHardSigmoid) + .SigmoidModel(SigmoidModel) ) sig ( - .a_i(r_out_data[i_GEN]), + .a_i(pipelined_data[i_GEN]), .y_o(w_in_data[i_GEN]) ); end -assign r_out_ready = w_in_ready; -assign w_in_valid = r_out_valid; - // https://github.com/bespoke-silicon-group/basejump_stl/blob/a43571d2/bsg_dataflow/bsg_serial_in_parallel_out_full.sv bsg_serial_in_parallel_out_full #( .width_p(Parallelism*FixedPointPrecision), diff --git a/rtl/math/ternip_silu.sv b/rtl/math/ternip_silu.sv index 95a6c00..5b6fc22 100644 --- a/rtl/math/ternip_silu.sv +++ b/rtl/math/ternip_silu.sv @@ -30,15 +30,15 @@ // // Scalar fixed-point SiLU. // -// Computes y_o = a_i * sigmoid(a_i). With UseHardSigmoid set, this uses a -// first-order approximation. Otherwise it reads a precomputed LUT. +// Computes y_o = a_i * sigmoid(a_i). SIGMOID_LUT reads a +// precomputed LUT; every approximation instantiates ternip_sig and multiplies. module ternip_silu #( parameter ternip_pkg::ternip_cfg_t Cfg = `TERNIP_CFG, parameter int FixedPointPrecision = Cfg.FixedPointPrecision, parameter int FixedPointExponent = Cfg.FixedPointExponent, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, parameter ternip_pkg::mul_impl_e MultiplicationImplementation = Cfg.MultiplicationImplementation, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0] @@ -55,24 +55,22 @@ module ternip_silu #( output fixed_point_t y_o ); -localparam fixed_point_t FixedPointOne = ternip_pkg::fixed_point_one(FixedPointExponent); -localparam int FixedPointUnaryOperationLutSize = UseHardSigmoid ? 1 : (2 ** FixedPointPrecision); +localparam int FixedPointUnaryOperationLutSize = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? (2 ** FixedPointPrecision) : 1; -if (UseHardSigmoid) begin : gen_hard_silu +if (SigmoidModel != ternip_pkg::SIGMOID_LUT) begin : gen_approx_silu - // hard_silu(x) = x * clamp(x/4 + 0.5, 0, 1) + // silu(x) = x * sigmoid(x); the approximation lives entirely in ternip_sig. fixed_point_t sig_result; - always_comb begin - fixed_point_t linear; - linear = (a_i / 4) + (FixedPointOne / 2); - if (linear <= 0) - sig_result = '0; - else if (linear >= FixedPointOne) - sig_result = FixedPointOne; - else - sig_result = linear; - end + ternip_sig #( + .FixedPointPrecision(FixedPointPrecision), + .FixedPointExponent(FixedPointExponent), + .SigmoidModel(SigmoidModel) + ) sigmoid ( + .a_i, + .y_o(sig_result) + ); // Pipeline register — breaks combinational path from sigmoid into ternip_mul. // in_ready_o: accept when stage is empty or ternip_mul is consuming this cycle. diff --git a/rtl/math/ternip_silu_parallelized.sv b/rtl/math/ternip_silu_parallelized.sv index 264beee..9af443b 100644 --- a/rtl/math/ternip_silu_parallelized.sv +++ b/rtl/math/ternip_silu_parallelized.sv @@ -39,7 +39,7 @@ module ternip_silu_parallelized #( parameter int FixedPointExponent = Cfg.FixedPointExponent, parameter int VectorParallelism = Cfg.VectorParallelism, parameter int LutParallelism = Cfg.LutParallelism, - parameter bit UseHardSigmoid = Cfg.UseHardSigmoid, + parameter ternip_pkg::sigmoid_model_e SigmoidModel = Cfg.SigmoidModel, parameter ternip_pkg::mul_impl_e MultiplicationImplementation = Cfg.MultiplicationImplementation, localparam type fixed_point_t = logic signed [FixedPointPrecision-1:0], @@ -57,7 +57,10 @@ module ternip_silu_parallelized #( output vector_chunk_t vector_data_o ); -localparam int Parallelism = UseHardSigmoid ? VectorParallelism : LutParallelism; +// Only the LUT implementation is area-limited; the arithmetic approximations +// run at full vector width. +localparam int Parallelism = + (SigmoidModel == ternip_pkg::SIGMOID_LUT) ? LutParallelism : VectorParallelism; logic r_in_ready; logic r_in_valid; @@ -95,7 +98,7 @@ bsg_parallel_in_serial_out #( .yumi_i(r_out_ready && r_out_valid) ); -if (UseHardSigmoid) begin : gen_hard_silu +if (SigmoidModel != ternip_pkg::SIGMOID_LUT) begin : gen_approx_silu // ternip_silu is multi-cycle logic [Parallelism-1:0] silu_in_ready; @@ -108,7 +111,7 @@ if (UseHardSigmoid) begin : gen_hard_silu ternip_silu #( .FixedPointPrecision(FixedPointPrecision), .FixedPointExponent(FixedPointExponent), - .UseHardSigmoid(UseHardSigmoid), + .SigmoidModel(SigmoidModel), .MultiplicationImplementation(MultiplicationImplementation) ) silu ( .clk_i, @@ -134,7 +137,7 @@ end else begin : gen_lut_silu ternip_silu #( .FixedPointPrecision(FixedPointPrecision), .FixedPointExponent(FixedPointExponent), - .UseHardSigmoid(UseHardSigmoid), + .SigmoidModel(SigmoidModel), .MultiplicationImplementation(MultiplicationImplementation) ) silu ( .clk_i, diff --git a/rtl/ternip_pkg.sv b/rtl/ternip_pkg.sv index fff34d3..b179c29 100644 --- a/rtl/ternip_pkg.sv +++ b/rtl/ternip_pkg.sv @@ -44,6 +44,101 @@ typedef enum logic [1:0] { MUL_NONE } mul_impl_e; +// Piecewise-linear sigmoid approximations, symmetric about (0, 1/2). Each is the +// minimax fit for its segment count; the POWER2 variants restrict every slope to a +// power of two so the multiply degrades to a shift. Max |error| vs true sigmoid: +// LUT exact (2**FixedPointPrecision entries) +// APPROXIMATE_1ST_ORDER 0.056050 +// APPROXIMATE_3RD_ORDER 0.017376 +// APPROXIMATE_5TH_ORDER 0.008362 +// APPROXIMATE_POWER2_SLOPE_1ST_ORDER 0.119203 (the long-standing hard sigmoid) +// APPROXIMATE_POWER2_SLOPE_3RD_ORDER 0.034857 +// APPROXIMATE_POWER2_SLOPE_5TH_ORDER 0.015848 +typedef enum logic [2:0] { + SIGMOID_LUT, + SIGMOID_APPROXIMATE_1ST_ORDER, + SIGMOID_APPROXIMATE_3RD_ORDER, + SIGMOID_APPROXIMATE_5TH_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER +} sigmoid_model_e; + +function automatic int sigmoid_segment_count(sigmoid_model_e model); + case (model) + SIGMOID_APPROXIMATE_1ST_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER: return 1; + SIGMOID_APPROXIMATE_3RD_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER: return 3; + SIGMOID_APPROXIMATE_5TH_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER: return 5; + default: return 0; // LUT + endcase +endfunction + +// Upper bound of segment `index`; below segment 0's lower bound the output is 0, +// at or above the last bound it is 1. +function automatic bit sigmoid_slopes_are_powers_of_two(sigmoid_model_e model); + case (model) + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER, + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER: return 1; + default: return 0; + endcase +endfunction + +function automatic real sigmoid_segment_upper_bound(sigmoid_model_e model, int index); + case (model) + SIGMOID_APPROXIMATE_1ST_ORDER: return 2.823822; + SIGMOID_APPROXIMATE_3RD_ORDER: + case (index) 0: return -1.652934; 1: return 1.652934; default: return 4.035162; endcase + SIGMOID_APPROXIMATE_5TH_ORDER: + case (index) 0: return -2.508140; 1: return -1.243333; 2: return 1.243333; + 3: return 2.508140; default: return 4.775714; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER: return 2.0; + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER: + case (index) 0: return -1.245525; 1: return 1.245525; default: return 4.263425; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER: + case (index) 0: return -2.559516; 1: return -0.938899; 2: return 0.938899; + 3: return 2.559516; default: return 4.565853; endcase + default: return 0.0; + endcase +endfunction + +function automatic real sigmoid_segment_slope(sigmoid_model_e model, int index); + case (model) + SIGMOID_APPROXIMATE_1ST_ORDER: return 0.177065; + SIGMOID_APPROXIMATE_3RD_ORDER: + case (index) 1: return 0.215776; default: return 0.060169; endcase + SIGMOID_APPROXIMATE_5TH_ORDER: + case (index) 2: return 0.228825; 1, 3: return 0.117462; default: return 0.029515; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER: return 0.25; + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER: + case (index) 1: return 0.25; default: return 0.0625; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER: + case (index) 2: return 0.25; 1, 3: return 0.125; default: return 0.03125; endcase + default: return 0.0; + endcase +endfunction + +function automatic real sigmoid_segment_intercept(sigmoid_model_e model, int index); + case (model) + SIGMOID_APPROXIMATE_1ST_ORDER: return 0.5; + SIGMOID_APPROXIMATE_3RD_ORDER: + case (index) 0: return 0.242793; 1: return 0.5; default: return 0.757207; endcase + SIGMOID_APPROXIMATE_5TH_ORDER: + case (index) 0: return 0.140956; 1: return 0.361539; 2: return 0.5; + 3: return 0.638461; default: return 0.859044; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_1ST_ORDER: return 0.5; + SIGMOID_APPROXIMATE_POWER2_SLOPE_3RD_ORDER: + case (index) 0: return 0.266464; 1: return 0.5; default: return 0.733536; endcase + SIGMOID_APPROXIMATE_POWER2_SLOPE_5TH_ORDER: + case (index) 0: return 0.142683; 1: return 0.382638; 2: return 0.5; + 3: return 0.617362; default: return 0.857317; endcase + default: return 0.0; + endcase +endfunction + typedef enum logic [1:0] { DIV_BSG, DIV_ROUNDROBIN, @@ -60,7 +155,7 @@ typedef struct packed { int unsigned LutParallelism; int unsigned FixedPointPrecision; int FixedPointExponent; - bit UseHardSigmoid; + sigmoid_model_e SigmoidModel; int unsigned BatchSize; int unsigned NumVectorRegisters; int unsigned ImmediateWidth; diff --git a/rtl/ternip_types.sv b/rtl/ternip_types.sv index 23cc547..48a9343 100644 --- a/rtl/ternip_types.sv +++ b/rtl/ternip_types.sv @@ -36,7 +36,7 @@ class ternip_types #( parameter ternip_pkg::ternip_cfg_t Cfg = `TERNIP_CFG ); - localparam int FixedPointUnaryOperationLutSize = Cfg.UseHardSigmoid ? 1 : (2 ** Cfg.FixedPointPrecision); + localparam int FixedPointUnaryOperationLutSize = (Cfg.SigmoidModel == ternip_pkg::SIGMOID_LUT) ? (2 ** Cfg.FixedPointPrecision) : 1; localparam int VectorSizeInBytes = Cfg.D * Cfg.FixedPointPrecision / 8; localparam int BytesPerFixedPointNum = Cfg.FixedPointPrecision / 8;