Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions rtl/fus/ternip_rowwise_operation.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
41 changes: 22 additions & 19 deletions rtl/math/ternip_csig.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@
//
// 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 #(
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]
) (
Expand All @@ -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
35 changes: 28 additions & 7 deletions rtl/math/ternip_csig_parallelized.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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),
Expand Down
101 changes: 82 additions & 19 deletions rtl/math/ternip_sig.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,107 @@
//
// 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,
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_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

Expand Down
35 changes: 28 additions & 7 deletions rtl/math/ternip_sig_parallelized.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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),
Expand Down
Loading
Loading