summaryrefslogtreecommitdiff
path: root/platform/wavelet3d/gfx_fpint_lane.sv
diff options
context:
space:
mode:
Diffstat (limited to 'platform/wavelet3d/gfx_fpint_lane.sv')
-rw-r--r--platform/wavelet3d/gfx_fpint_lane.sv379
1 files changed, 379 insertions, 0 deletions
diff --git a/platform/wavelet3d/gfx_fpint_lane.sv b/platform/wavelet3d/gfx_fpint_lane.sv
new file mode 100644
index 0000000..0010f06
--- /dev/null
+++ b/platform/wavelet3d/gfx_fpint_lane.sv
@@ -0,0 +1,379 @@
+module gfx_fpint_lane
+(
+ input logic clk,
+
+ input gfx::float a,
+ b,
+
+ input logic mul_float_m1,
+ unit_b_m1,
+ float_a_1,
+ int_hi_a_1,
+ int_lo_a_1,
+ zero_flags_a_1,
+ zero_b_1,
+ copy_flags_2,
+ copy_flags_5,
+ enable_norm_6,
+ copy_flags_10,
+ copy_flags_11,
+ enable_round_11,
+ encode_special_13,
+
+ output gfx::float q
+);
+
+ import gfx::*;
+
+ /* Notas de implementación para floating-point.
+ *
+ * === PRODUCTO ===
+ * Queremos calcular q = a * b.
+ *
+ * Donde a = (-1)^s * 1.m * 2^f,
+ * b = (-1)^t * 1.n * 2^g
+ *
+ * Entonces q = (-1)^(s + t) (1.m * 1.n) 2^(f + g)
+ *
+ * El producto es entre números >= 1.0 y < 2.0. En el peor caso:
+ * Mejor caso: 1.000... * 1.000... ~ 1.000...
+ * Peor caso: 1.999... * 1.999... ~ 3.999... = 2^1 * 1.999
+ *
+ * Así que, si el producto es >= 2, hay que hacerle >> 1 a la mantisa
+ * y sumarle 1 al exponente para normalizar.
+ *
+ *
+ * === SUMA/RESTA ===
+ *
+ * Queremos calcular q = a + b. Curiosamente, eso es más complicado que a * b.
+ * Hay que ajustar el exponente del menor entre a y b para que coincida
+ * con el del mayor (desnormalizando), realizar la operación y finalmente
+ * renormalizar. Se hace suma o resta dependiendo de relaciones de signos,
+ * no según la operación de entrada (eso último solo le hace xor al signo de b).
+ * Recordar aquí que IEEE 754 es una especie de signo-magnitud y no complemento.
+ *
+ * En el caso de una resta, el exponente normalizado puede ser mucho más
+ * pequeño que cualquiera de los exponentes de entrada. Necesitamos
+ * entonces de lǵoica CLZ (count leading zeros) para renormalizar.
+ */
+
+ logic exp_step, guard_0, guard_1, guard_2, guard_3, guard_4, guard_5, guard_10,
+ lo_msb, lo_reduce, overflow_0, overflow_1, overflow_10, overflow_12,
+ round_0, round_1, round_2, round_3, round_4, round_5, round_10, sign_0,
+ sign_10, sign_11, sign_12, slow_1, slow_2, slow_3, slow_4, slow_5, slow_10,
+ slow_11, slow_12, slow_in_1, slow_in_next, slow_out, sticky_1, sticky_2,
+ sticky_3, sticky_4, sticky_5, sticky_10, sticky_last, zero_1, zero_2, zero_3,
+ zero_4, zero_5, zero_10, zero_11, zero_12;
+
+ float a_add, a_m1, a_mul, b_add, b_0, b_m1, b_mul,
+ max_2, max_3, max_4, max_5, min_2, min_3, min_4;
+
+ float_class a_class_0, a_class_1, b_class_0, b_class_1,
+ max_class_2, max_class_3, min_class_2, min_class_3, min_class_4;
+
+ word clz_in, product_hi, product_lo;
+ dword product;
+ float_exp exp, exp_11, exp_10, exp_12, exp_delta;
+ float_mant mant_10, mant_11, mant_12;
+ float_mant_full hi;
+ logic[$bits(float_mant_full) - 3:0] lo;
+
+ typedef logic[$bits(float_mant_full) + 1:0] extended_mant;
+ localparam bit[$clog2($bits(extended_mant)):0] MAX_SHIFT = 1 << $clog2($bits(extended_mant));
+
+ localparam int SHIFT_WIDTH = {{($bits(int) - $bits(MAX_SHIFT)){1'b0}}, MAX_SHIFT};
+ localparam int CLZ_EXTEND_BITS = $bits(float_exp) - $bits(clz_shift) + 1;
+
+ typedef logic[$bits(float_mant_full) + 2:0] mant_sum;
+
+ mant_sum add_sub, normalized;
+ extended_mant max_mant, min_mant, sticky_mask;
+ logic[$clog2(MAX_SHIFT):0] clz_shift, exp_shift;
+
+ struct packed
+ {
+ float max;
+ logic guard,
+ round,
+ slow,
+ sticky,
+ zero;
+ mant_sum add_sub;
+ } clz_hold[FADD_CLZ_STAGES], clz_hold_out;
+
+ gfx_clz #($bits(word)) clz
+ (
+ .clk(clk),
+ .clz(clz_shift),
+ .value(clz_in)
+ );
+
+ function extended_mant extend_min_max(float in, float_class in_class);
+ extend_min_max = {~in_class.exp_min, in.mant, 2'b00};
+ endfunction
+
+ assign lo_msb = lo[$bits(lo) - 1];
+ assign slow_out = &exp_12 || slow_12 || overflow_12;
+ assign exp_delta = max_2.exp - min_2.exp;
+ assign lo_reduce = |lo[$bits(lo) - 2:0];
+ assign normalized = add_sub << clz_shift;
+ assign clz_hold_out = clz_hold[FADD_CLZ_STAGES - 1];
+ assign slow_in_next = is_float_special(a_class_0) | is_float_special(b_class_0);
+ assign {product_hi, product_lo} = product;
+ assign {hi, guard_0, round_0, lo} = product[2 * $bits(float_mant_full) - 1:0];
+
+ always_comb begin
+ clz_in = {add_sub, {($bits(clz_in) - $bits(add_sub)){1'b0}}};
+ if (~enable_norm_6)
+ clz_in[$bits(clz_in) - 1:$bits(clz_in) - 2] = 2'b01;
+ end
+
+ always_ff @(posedge clk) begin
+ // Stage -1:
+
+ a_m1 <= a;
+ b_m1 <= b;
+ a_mul <= a;
+ b_mul <= b;
+
+ /* Nótese que el orden es sign-exp-mant. Esto coloca el 1. implícito
+ * en la posición correcta para multiplicar mantisas.
+ */
+ if (mul_float_m1) begin
+ a_mul.exp <= 1;
+ b_mul.exp <= 1;
+ a_mul.sign <= 0;
+ b_mul.sign <= 0;
+ end
+
+ // Genera un nop junto a lo anterior
+ if (unit_b_m1) begin
+ b_mul.exp <= 0;
+ b_mul.mant <= 1;
+ end
+
+ // Stage 0: multiplicación de fp o enteros
+
+ b_0 <= b_m1;
+ sign_0 <= a_m1.sign ^ b_m1.sign;
+ product <= a_mul * b_mul;
+ a_class_0 <= classify_float(a_m1);
+ b_class_0 <= classify_float(b_m1);
+ {overflow_0, exp} <= {1'b0, a_m1.exp} + {1'b0, b_m1.exp} - {1'b0, FLOAT_EXP_BIAS};
+
+ // Stage 1: normalización
+
+ slow_in_1 <= slow_in_next;
+ overflow_1 <= 0;
+
+ if (float_a_1) begin
+ slow_1 <= slow_in_next | (overflow_0 & ~a_class_0.exp_min & ~a_class_1.exp_min);
+ zero_1 <= a_class_0.exp_min | b_class_0.exp_min;
+ end else begin
+ slow_1 <= 0;
+ zero_1 <= 0;
+ end
+
+ a_add.sign <= sign_0;
+ if (hi[$bits(hi) - 1]) begin
+ guard_1 <= guard_0;
+ round_1 <= round_0;
+ sticky_1 <= lo_msb | lo_reduce;
+ a_add.mant <= implicit_mant(hi);
+ {overflow_1, a_add.exp} <= {1'b0, exp} + 1;
+ end else begin
+ /* Bit antes de msb es necesariamente 1, ya que los msb de
+ * ambos multiplicandos son 1. Ver assert en implicit_mant().
+ */
+ guard_1 <= round_0;
+ round_1 <= lo[$bits(lo) - 1];
+ sticky_1 <= lo_reduce;
+ a_add.exp <= exp;
+ a_add.mant <= implicit_mant({hi[$bits(hi) - 2:0], guard_0});
+ end
+
+ unique case (1'b1)
+ float_a_1: ;
+
+ int_hi_a_1:
+ a_add <= product_hi;
+
+ int_lo_a_1:
+ a_add <= product_lo;
+ endcase
+
+ a_class_1 <= a_class_0;
+ if (zero_flags_a_1)
+ a_class_1 <= classify_float(0);
+
+ if (zero_b_1) begin
+ b_add <= 0;
+ b_class_1 <= classify_float(0);
+ end else begin
+ b_add <= b_0;
+ b_class_1 <= b_class_0;
+ end
+
+ /* Stage 2: ordenar tal que abs(max) >= abs(min). Wiki dice:
+ *
+ * A property of the single- and double-precision formats is that
+ * their encoding allows one to easily sort them without using
+ * floating-point hardware, as if the bits represented sign-magnitude
+ * integers, although it is unclear whether this was a design
+ * consideration (it seems noteworthy that the earlier IBM hexadecimal
+ * floating-point representation also had this property for normalized
+ * numbers).
+ */
+ if ({b_add.exp, b_add.mant} > {a_add.exp, a_add.mant}) begin
+ max_2 <= b_add;
+ min_2 <= a_add;
+ max_class_2 <= b_class_1;
+ min_class_2 <= a_class_1;
+ end else begin
+ max_2 <= a_add;
+ min_2 <= b_add;
+ max_class_2 <= a_class_1;
+ min_class_2 <= b_class_1;
+ end
+
+ guard_2 <= guard_1;
+ round_2 <= round_1;
+ sticky_2 <= sticky_1;
+
+ if (copy_flags_2) begin
+ slow_2 <= slow_1 | overflow_1;
+ zero_2 <= zero_1;
+ end else begin
+ slow_2 <= slow_in_1;
+ zero_2 <= 0;
+ end
+
+ // Stage 3: exp_shift amount
+
+ max_3 <= max_2;
+ min_3 <= min_2;
+ slow_3 <= slow_2;
+ zero_3 <= zero_2;
+ guard_3 <= guard_2;
+ round_3 <= round_2;
+ sticky_3 <= sticky_2;
+ max_class_3 <= max_class_2;
+ min_class_3 <= min_class_2;
+
+ exp_shift <= exp_delta[$bits(exp_shift) - 1:0];
+ if (exp_delta > {{($bits(exp_delta) - $bits(MAX_SHIFT)){1'b0}}, MAX_SHIFT})
+ exp_shift <= MAX_SHIFT;
+
+ // Stage 4: shifts
+
+ max_4 <= max_3;
+ min_4 <= min_3;
+ slow_4 <= slow_3;
+ zero_4 <= zero_3;
+ guard_4 <= guard_3;
+ round_4 <= round_3;
+ sticky_4 <= sticky_3;
+ min_class_4 <= min_class_3;
+
+ max_mant <= extend_min_max(max_3, max_class_3);
+ min_mant <= extend_min_max(min_3, min_class_3) >> exp_shift;
+ sticky_mask <= {($bits(min_mant)){1'b1}} << exp_shift;
+
+ // Stage 5: suma/resta y sticky
+
+ max_5 <= max_4;
+ slow_5 <= slow_4;
+ zero_5 <= zero_4;
+ guard_5 <= guard_4;
+ round_5 <= round_4;
+
+ if (copy_flags_5)
+ sticky_5 <= sticky_4;
+ else
+ sticky_5 <= |(extend_min_max(min_4, min_class_4) & ~sticky_mask);
+
+ if (max_4.sign ^ min_4.sign)
+ add_sub <= {1'b0, max_mant - min_mant};
+ else
+ add_sub <= {1'b0, max_mant} + {1'b0, min_mant};
+
+ // Stages 6-9: clz
+
+ clz_hold[0].max <= max_5;
+ clz_hold[0].slow <= slow_5;
+ clz_hold[0].zero <= zero_5;
+ clz_hold[0].guard <= guard_5;
+ clz_hold[0].round <= round_5;
+ clz_hold[0].sticky <= sticky_5;
+ clz_hold[0].add_sub <= add_sub;
+
+ for (int i = 1; i < FADD_CLZ_STAGES; ++i)
+ clz_hold[i] <= clz_hold[i - 1];
+
+ // Stage 10: normalización
+
+ sign_10 <= clz_hold_out.max.sign;
+ slow_10 <= clz_hold_out.slow;
+ zero_10 <= clz_hold_out.zero;
+ sticky_10 <= clz_hold_out.sticky;
+
+ {mant_10, guard_10, round_10, sticky_last} <=
+ normalized[$bits(normalized) - 2:$bits(normalized) - $bits(float_mant) - 4];
+
+ {overflow_10, exp_10} <=
+ {1'b0, clz_hold_out.max.exp} - {{CLZ_EXTEND_BITS{1'b0}}, clz_shift} + 1;
+
+ if (clz_shift[$bits(clz_shift) - 1])
+ zero_10 <= 1;
+
+ if (copy_flags_10) begin
+ guard_10 <= clz_hold_out.guard;
+ round_10 <= clz_hold_out.round;
+ sticky_last <= 0;
+ overflow_10 <= 0;
+ end
+
+ // Stage 11: redondeo
+
+ exp_11 <= exp_10;
+ mant_11 <= mant_10;
+ sign_11 <= sign_10;
+ slow_11 <= slow_10 | (~copy_flags_11 & overflow_10 & ~zero_10);
+ zero_11 <= zero_10;
+ exp_step <= 0;
+
+ // Este es el modo más común: round to nearest, ties to even
+ if (enable_round_11 & guard_10 & (round_10 | sticky_10 | sticky_last | mant_10[0]))
+ {exp_step, mant_11} <= {1'b0, mant_10} + 1;
+
+ // Stage 12: ajuste de exponente por redondeo
+
+ sign_12 <= sign_11;
+ slow_12 <= slow_11;
+ zero_12 <= zero_11;
+ mant_12 <= mant_11;
+ overflow_12 <= 0;
+
+ if (exp_step)
+ {overflow_12, exp_12} <= {1'b0, exp_11} + 1;
+ else
+ exp_12 <= exp_11;
+
+ // Stage 13: ceros y NaNs
+
+ q.exp <= exp_12;
+ q.mant <= mant_12;
+ q.sign <= sign_12;
+
+ if (encode_special_13) begin
+ if (slow_out) begin
+ q.exp <= FLOAT_EXP_MAX;
+ q.mant <= 1;
+ end else if (zero_12) begin
+ q.exp <= 0;
+ q.mant <= 0;
+ end
+ end
+ end
+
+endmodule