def legalize_fixed_point_subselection(optree, input_prec_solver=default_prec_solver): """ Legalize a SubSignalSelection on a fixed-point node """ sub_input = optree.get_input(0) inf_index = evaluate_cst_graph(optree.get_inf_index(), input_prec_solver=input_prec_solver) sup_index = evaluate_cst_graph(optree.get_sup_index(), input_prec_solver=input_prec_solver) assert not inf_index is None and not sup_index is None input_format = ML_StdLogicVectorFormat( sub_input.get_precision().get_bit_size()) output_format = ML_StdLogicVectorFormat(sup_index - inf_index + 1) subselect = SubSignalSelection(TypeCast(sub_input, precision=input_format), inf_index, sup_index, precision=output_format) result = TypeCast( subselect, precision=optree.get_precision(), ) # TypeCast may be simplified during code generation so attributes # may also be forwarded to previous node forward_attributes(optree, subselect) forward_attributes(optree, result) return result
def raw_fp_field_extraction(optree): """ generate an operation sub-graph to extract the fp field (mantissa without implicit digit) of a fp node """ size = optree.get_precision().get_base_format().get_bit_size() field_size = optree.get_precision().get_base_format().get_field_size() return TypeCast(SubSignalSelection( TypeCast(optree, precision=ML_StdLogicVectorFormat(size)), 0, field_size - 1), precision=fixed_point(field_size, 0, signed=False))
def mantissa_extraction_modifier_from_fields(op, field_op, exp_is_zero, tag="mant_extr"): """ Legalizing a MantissaExtraction node into a sub-graph of basic operation, assuming <field_op> bitfield and <exp_is_zero> flag are already available """ op_precision = op.get_precision().get_base_format() implicit_digit = Select( exp_is_zero, Constant(0, precision=ML_StdLogic), Constant(1, precision=ML_StdLogic), precision=ML_StdLogic, tag=tag + "_implicit_digit", ) result = Concatenation( implicit_digit, TypeCast(field_op, precision=ML_StdLogicVectorFormat( op_precision.get_field_size())), precision=ML_StdLogicVectorFormat(op_precision.get_mantissa_size()), ) return result
def get_output_check_statement(output_signal, output_tag, output_value): """ Generate output value check statement """ test_pass_cond = Comparison( output_signal, output_value, specifier=Comparison.Equal, precision=ML_Bool ) check_statement = ConditionBlock( LogicalNot( test_pass_cond, precision = ML_Bool ), Report( Concatenation( " result for {}: ".format(output_tag), Conversion( output_signal if output_signal.get_precision() is ML_StdLogic else TypeCast( output_signal, precision=ML_StdLogicVectorFormat( output_signal.get_precision().get_bit_size() ) ), precision = ML_String ), precision = ML_String ) ) ) return test_pass_cond, check_statement
def signal_str_conversion(optree, op_format): """ converision of @p optree from op_format to ML_String """ return Conversion( optree if op_format is ML_StdLogic else TypeCast( optree, precision=ML_StdLogicVectorFormat( op_format.get_bit_size() ) ), precision=ML_String )
def comp_3to2(a, b, c): """ 3 digits to 2 digits compressor """ s = BitLogicXor(a, BitLogicXor(b, c, precision=ML_StdLogic), precision=ML_StdLogic) c = BitLogicOr(BitLogicAnd(a, b, precision=ML_StdLogic), BitLogicOr(BitLogicAnd(a, c, precision=ML_StdLogic), BitLogicAnd(c, b, precision=ML_StdLogic), precision=ML_StdLogic), precision=ML_StdLogic) return c, s a = TypeCast(a, precision=fixed_point(1, 0, signed=False)) b = TypeCast(b, precision=fixed_point(1, 0, signed=False)) c = TypeCast(c, precision=fixed_point(1, 0, signed=False)) full = TypeCast(Conversion(a + b + c, precision=fixed_point(2, 0, signed=False)), precision=ML_StdLogicVectorFormat(2)) carry = BitSelection(full, 1) digit = BitSelection(full, 0) return carry, digit
def generate_bitfield_extraction(target_format, input_node, lo_index, hi_index): shift = lo_index mask_size = hi_index - lo_index + 1 input_format = input_node.get_precision().get_base_format() if is_fixed_point(input_format) and is_fixed_point(target_format): frac_size = target_format.get_frac_size() int_size = input_format.get_bit_size() - frac_size cast_format = ML_Custom_FixedPoint_Format(int_size, frac_size, signed=False) else: cast_format = None # 1st step: shifting the input node the right amount shifted_node = input_node if shift == 0 else BitLogicRightShift( input_node, Constant(shift, precision=ML_Int32), precision=input_format) raw_format = ML_Custom_FixedPoint_Format(input_format.get_bit_size(), 0, signed=False) # 2nd step: masking the input node # TODO/FIXME: check thast mask does not overflow or wrap-around masked_node = BitLogicAnd(TypeCast(shifted_node, precision=raw_format), Constant((2**mask_size - 1), precision=raw_format), precision=raw_format) if not cast_format is None: casted_node = TypeCast(masked_node, precision=cast_format) else: casted_node = masked_node converted_node = Conversion(casted_node, precision=target_format) return converted_node
def generate_field_extraction(optree, precision, lo_index, hi_index): """ extract bit-field optree[lo_index:hi_index-1] and cast to precision """ if optree.precision != precision: optree = TypeCast(optree, precision=precision) result = optree if lo_index != 0: result = BitLogicRightShift(optree, Constant(vectorize_cst( lo_index, precision), precision=precision), precision=precision) if (hi_index - lo_index + 1) != precision.get_bit_size(): mask = Constant(vectorize_cst(2**(hi_index - lo_index + 1) - 1, precision), precision=precision) result = BitLogicAnd(result, mask, precision=precision) return result
def get_index_node(self, vx): assert vx.precision is self.precision int_precision = vx.precision.get_integer_format() index_size = self.exp_bits + self.field_bits # building an index mask from the index_size index_mask = Constant(2**index_size - 1, precision=int_precision) shift_amount = Constant(vx.get_precision().get_field_size() - self.field_bits, precision=int_precision) exp_offset = Constant(self.precision.get_integer_coding( S2**self.low_exp_value), precision=int_precision) return BitLogicAnd(BitLogicRightShift(Subtraction( TypeCast(vx, precision=int_precision), exp_offset, precision=int_precision), shift_amount, precision=int_precision), index_mask, precision=int_precision)
def legalize_vector_reduction_test(optree): """ Legalize a vector test (e.g. IsMaskNotAnyZero) to a sub-graph of basic operations """ op_input = optree.get_input(0) vector_size = op_input.get_precision().get_vector_size() conv_format = { 2: v2int32, 4: v4int32, 8: v8int32, }[vector_size] cast_format = { 2: ML_Int64, 4: ML_Int128, 8: ML_Int256, }[vector_size] return Comparison(TypeCast(Conversion(op_input, precision=conv_format), precision=cast_format), Constant(0, precision=cast_format), specifier=Comparison.Equal, precision=ML_Bool)
def generate_exp_insertion(optree, result_precision): """ generate the expanded version of ExponentInsertion with @p optree as input and assuming @p result_precision as output precision """ if result_precision.is_vector_format(): scalar_format = optree.precision.get_scalar_format() vector_size = optree.precision.get_vector_size() bias_cst = [-result_precision.get_scalar_format().get_bias() ] * vector_size shift_cst = [result_precision.get_scalar_format().get_field_size() ] * vector_size else: scalar_format = optree.precision bias_cst = -result_precision.get_bias() shift_cst = result_precision.get_field_size() assert is_std_integer_format(scalar_format) biased_exponent = Addition(optree, Constant(bias_cst, precision=optree.precision), precision=optree.precision) result = BitLogicLeftShift(biased_exponent, Constant(shift_cst, precision=optree.precision), precision=optree.precision) return TypeCast(result, precision=result_precision)
def mantissa_extraction_modifier(optree): """ Legalizing a MantissaExtraction node into a sub-graph of basic operation """ init_stage = optree.attributes.get_dyn_attribute("init_stage") op = optree.get_input(0) tag = optree.get_tag() or "mant_extr" op_precision = op.get_precision().get_base_format() exp_prec = ML_StdLogicVectorFormat(op_precision.get_exponent_size()) field_prec = ML_StdLogicVectorFormat(op_precision.get_field_size()) exp_op = RawExponentExtraction(op, precision=exp_prec, init_stage=init_stage, tag=tag + "_exp_extr") field_op = SubSignalSelection(TypeCast( op, precision=op.get_precision().get_support_format(), init_stage=init_stage, tag=tag + "_field_cast"), 0, op_precision.get_field_size() - 1, precision=field_prec, init_stage=init_stage, tag=tag + "_field") exp_is_zero = Comparison(exp_op, Constant(op_precision.get_zero_exponent_value(), precision=exp_prec, init_stage=init_stage), precision=ML_Bool, specifier=Comparison.Equal, init_stage=init_stage) result = mantissa_extraction_modifier_from_fields(op, field_op, exp_is_zero) forward_attributes(optree, result) return result
def legalizer(exp_insertion_node): optree = exp_insertion_node.get_input(0) if result_precision.is_vector_format(): scalar_format = optree.precision.get_scalar_format() vector_size = optree.precision.get_vector_size() bias_cst = [-result_precision.get_scalar_format().get_bias() ] * vector_size shift_cst = [ result_precision.get_scalar_format().get_field_size() ] * vector_size else: scalar_format = optree.precision bias_cst = -result_precision.get_bias() shift_cst = result_precision.get_field_size() assert is_std_integer_format(scalar_format) biased_exponent = Addition(optree, Constant(bias_cst, precision=optree.precision), precision=optree.precision) result = BitLogicLeftShift(biased_exponent, Constant(shift_cst, precision=optree.precision), precision=optree.precision) return TypeCast(result, precision=result_precision)
def convert_bit_heap_to_fixed_point(current_bit_heap, signed=False): # final propagating sum op_index = 0 op_list = [] op_statement = Statement() while current_bit_heap.max_count() > 0: op_size = current_bit_heap.max_index - current_bit_heap.min_index + 1 op_format = ML_StdLogicVectorFormat(op_size) op_reduce = Signal("op_%d" % op_index, precision=op_format, var_type=Variable.Local) offset_index = current_bit_heap.min_index for index in range(current_bit_heap.min_index, current_bit_heap.max_index + 1): out_index = index - offset_index bit_list = current_bit_heap.pop_bits(index, 1) if len(bit_list) == 0: op_statement.push( ReferenceAssign(BitSelection(op_reduce, out_index), Constant(0, precision=ML_StdLogic))) else: assert len(bit_list) == 1 op_statement.push( ReferenceAssign(BitSelection(op_reduce, out_index), bit_list[0])) op_precision = fixed_point(op_size + offset_index, -offset_index, signed=signed) op_list.append( PlaceHolder(TypeCast(op_reduce, precision=op_precision), op_statement)) op_index += 1 return op_list, op_statement
def generate_exp_insertion(optree, result_precision): """ generate the expanded version of ExponentInsertion with @p optree as input and assuming @p result_precision as output precision """ if result_precision.is_vector_format(): scalar_format = optree.precision.get_scalar_format() vector_size = optree.precision.get_vector_size() # determine the working format (for expression) work_format = VECTOR_TYPE_MAP[result_precision.get_scalar_format(). get_integer_format()][vector_size] bias_cst = [-result_precision.get_scalar_format().get_bias() ] * vector_size shift_cst = [result_precision.get_scalar_format().get_field_size() ] * vector_size else: scalar_format = optree.precision work_format = result_precision.get_integer_format() bias_cst = -result_precision.get_bias() shift_cst = result_precision.get_field_size() if not is_std_integer_format(scalar_format): Log.report( Log.Error, "{} should be a std integer format in generate_exp_insertion {} with precision {}", scalar_format, optree, result_precision) assert is_std_integer_format(scalar_format) biased_exponent = Addition(Conversion(optree, precision=work_format) if not optree.precision is work_format else optree, Constant(bias_cst, precision=work_format), precision=work_format) result = BitLogicLeftShift(biased_exponent, Constant( shift_cst, precision=work_format, ), precision=work_format) return TypeCast(result, precision=result_precision)
def generic_atan2_generate(self, _vx, vy=None): """ if vy is None, compute atan(_vx), else compute atan2(vy / vx) """ if vy is None: # approximation # if abs_vx <= 1.0 then atan(abx_vx) is directly approximated # if abs_vx > 1.0 then atan(abs_vx) = pi/2 - atan(1 / abs_vx) # # for vx >= 0, atan(vx) = atan(abs_vx) # # for vx < 0, atan(vx) = -atan(abs_vx) for vx < 0 # = -pi/2 + atan(1 / abs_vx) vx = _vx sign_cond = vx < 0 abs_vx = Select(vx < 0, -vx, vx, tag="abs_vx", debug=debug_multi) bound_cond = abs_vx > 1 inv_abs_vx = 1 / abs_vx # condition to select subtraction cond = LogicalOr(LogicalAnd(vx < 0, LogicalNot(bound_cond)), vx > 1, tag="cond", debug=debug_multi) # reduced argument red_vx = Select(bound_cond, inv_abs_vx, abs_vx, tag="red_vx", debug=debug_multi) offset = None else: # bound_cond is True iff Abs(vy / _vx) > 1.0 bound_cond = Abs(vy) > Abs(_vx) bound_cond.set_attributes(tag="bound_cond", debug=debug_multi) # vx and vy are of opposite signs #sign_cond = (_vx * vy) < 0 # using cast to int(signed) and bitwise xor # to determine if _vx and vy are of opposite sign rapidly fast_sign_cond = BitLogicXor( TypeCast(_vx, precision=self.precision.get_integer_format()), TypeCast(vy, precision=self.precision.get_integer_format()), precision=self.precision.get_integer_format()) < 0 # sign_cond = (_vx * vy) < 0 sign_cond = fast_sign_cond sign_cond.set_attributes(tag="sign_cond", debug=debug_multi) # condition to select subtraction # TODO: could be accelerated if LogicalXor existed slow_cond = LogicalOr( LogicalAnd(sign_cond, LogicalNot(bound_cond)), # 1 < (vy / _vx) < 0 LogicalAnd(bound_cond, LogicalNot(sign_cond)), # (vy / _vx) > 1 tag="cond", debug=debug_multi) cond = slow_cond numerator = Select(bound_cond, _vx, vy, tag="numerator", debug=debug_multi) denominator = Select(bound_cond, vy, _vx, tag="denominator", debug=debug_multi) # reduced argument red_vx = Abs(numerator) / Abs(denominator) red_vx.set_attributes(tag="red_vx", debug=debug_multi) offset = Select( _vx > 0, Constant(0, precision=self.precision), # vx < 0 Select( sign_cond, # vy > 0 Constant(sollya.pi, precision=self.precision), Constant(-sollya.pi, precision=self.precision), precision=self.precision), precision=self.precision, tag="offset") approx_fct = sollya.atan(sollya.x) if self.method == "piecewise": sign_vx = Select(cond, -1, 1, precision=self.precision, tag="sign_vx", debug=debug_multi) cst_sign = Select(sign_cond, -1, 1, precision=self.precision, tag="cst_sign", debug=debug_multi) cst = cst_sign * Select( bound_cond, sollya.pi / 2, 0, precision=self.precision) cst.set_attributes(tag="cst", debug=debug_multi) bound_low = 0.0 bound_high = 1.0 num_intervals = self.num_sub_intervals error_threshold = S2**-(self.precision.get_mantissa_size() + 8) approx, eval_error = piecewise_approximation( approx_fct, red_vx, self.precision, bound_low=bound_low, bound_high=bound_high, max_degree=None, num_intervals=num_intervals, error_threshold=error_threshold, odd=True) result = cst + sign_vx * approx result.set_attributes(tag="result", precision=self.precision, debug=debug_multi) elif self.method == "single": approx_interval = Interval(0, 1.0) # determining the degree of the polynomial approximation poly_degree_range = sollya.guessdegree( approx_fct / sollya.x, approx_interval, S2**-(self.precision.get_field_size() + 2)) poly_degree = int(sollya.sup(poly_degree_range)) + 4 Log.report(Log.Info, "poly_degree={}".format(poly_degree)) # arctan is an odd function, so only odd coefficient must be non-zero poly_degree_list = list(range(1, poly_degree + 1, 2)) poly_object, poly_error = Polynomial.build_from_approximation_with_error( approx_fct, poly_degree_list, [1] + [self.precision.get_sollya_object()] * (len(poly_degree_list) - 1), approx_interval) odd_predicate = lambda index, _: ((index - 1) % 4 != 0) even_predicate = lambda index, _: (index != 1 and (index - 1) % 4 == 0) poly_odd_object = poly_object.sub_poly_cond(odd_predicate, offset=1) poly_even_object = poly_object.sub_poly_cond(even_predicate, offset=1) sollya.settings.display = sollya.hexadecimal Log.report(Log.Info, "poly_error: {}".format(poly_error)) Log.report(Log.Info, "poly_odd: {}".format(poly_odd_object)) Log.report(Log.Info, "poly_even: {}".format(poly_even_object)) poly_odd = PolynomialSchemeEvaluator.generate_horner_scheme( poly_odd_object, abs_vx) poly_odd.set_attributes(tag="poly_odd", debug=debug_multi) poly_even = PolynomialSchemeEvaluator.generate_horner_scheme( poly_even_object, abs_vx) poly_even.set_attributes(tag="poly_even", debug=debug_multi) exact_sum = poly_odd + poly_even exact_sum.set_attributes(tag="exact_sum", debug=debug_multi) # poly_even should be (1 + poly_even) result = vx + vx * exact_sum result.set_attributes(tag="result", precision=self.precision, debug=debug_multi) else: raise NotImplementedError if not offset is None: result = result + offset std_scheme = Statement(Return(result)) scheme = std_scheme return scheme
def generate_auto_test(self, test_num=10, test_range=Interval(-1.0, 1.0), debug=False, time_step=10): """ time_step: duration of a stage (in ns) """ # instanciating tested component # map of input_tag -> input_signal and output_tag -> output_signal io_map = {} # map of input_tag -> input_signal, excludind commodity signals # (e.g. clock and reset) input_signals = {} # map of output_tag -> output_signal output_signals = {} # excluding clock and reset signals from argument list # reduced_arg_list = [input_port for input_port in self.implementation.get_arg_list() if not input_port.get_tag() in ["clk", "reset"]] reduced_arg_list = self.implementation.get_arg_list() for input_port in reduced_arg_list: input_tag = input_port.get_tag() input_signal = Signal(input_tag + "_i", precision=input_port.get_precision(), var_type=Signal.Local) io_map[input_tag] = input_signal if not input_tag in ["clk", "reset"]: input_signals[input_tag] = input_signal for output_port in self.implementation.get_output_port(): output_tag = output_port.get_tag() output_signal = Signal(output_tag + "_o", precision=output_port.get_precision(), var_type=Signal.Local) io_map[output_tag] = output_signal output_signals[output_tag] = output_signal # building list of test cases tc_list = [] self_component = self.implementation.get_component_object() self_instance = self_component(io_map=io_map, tag="tested_entity") test_statement = Statement() # initializing random test case generator self.init_test_generator() # Appending standard test cases if required if self.auto_test_std: tc_list += self.standard_test_cases for i in range(test_num): input_values = self.generate_test_case(input_signals, io_map, i, test_range) tc_list.append((input_values, None)) def compute_results(tc): """ update test case with output values if required """ input_values, output_values = tc if output_values is None: return input_values, self.numeric_emulate(input_values) else: return tc # filling output values tc_list = [compute_results(tc) for tc in tc_list] for input_values, output_values in tc_list: input_msg = "" # Adding input setting for input_tag in input_values: input_signal = io_map[input_tag] # FIXME: correct value generation depending on signal precision input_value = input_values[input_tag] test_statement.add( ReferenceAssign( input_signal, Constant(input_value, precision=input_signal.get_precision()))) value_msg = input_signal.get_precision().get_cst( input_value, language=VHDL_Code).replace('"', "'") value_msg += " / " + hex(input_signal.get_precision( ).get_base_format().get_integer_coding(input_value)) input_msg += " {}={} ".format(input_tag, value_msg) test_statement.add(Wait(time_step * self.stage_num)) # Adding output value comparison for output_tag in output_signals: output_signal = output_signals[output_tag] output_value = Constant( output_values[output_tag], precision=output_signal.get_precision()) output_precision = output_signal.get_precision() expected_dec = output_precision.get_cst( output_values[output_tag], language=VHDL_Code).replace('"', "'") expected_hex = " / " + hex( output_precision.get_base_format().get_integer_coding( output_values[output_tag])) value_msg = "{} / {}".format(expected_dec, expected_hex) test_pass_cond = Comparison(output_signal, output_value, specifier=Comparison.Equal, precision=ML_Bool) test_statement.add( ConditionBlock( LogicalNot(test_pass_cond, precision=ML_Bool), Report( Concatenation( " result for {}: ".format(output_tag), Conversion(TypeCast( output_signal, precision=ML_StdLogicVectorFormat( output_signal.get_precision( ).get_bit_size())), precision=ML_String), precision=ML_String)))) test_statement.add( Assert( test_pass_cond, "\"unexpected value for inputs {input_msg}, output {output_tag}, expecting {value_msg}, got: \"" .format(input_msg=input_msg, output_tag=output_tag, value_msg=value_msg), severity=Assert.Failure)) testbench = CodeEntity("testbench") test_process = Process( test_statement, # end of test Assert(Constant(0, precision=ML_Bool), " \"end of test, no error encountered \"", severity=Assert.Failure)) testbench_scheme = Statement(self_instance, test_process) if self.pipelined: half_time_step = time_step / 2 assert (half_time_step * 2) == time_step # adding clock process for pipelined bench clk_process = Process( Statement( ReferenceAssign(io_map["clk"], Constant(1, precision=ML_StdLogic)), Wait(half_time_step), ReferenceAssign(io_map["clk"], Constant(0, precision=ML_StdLogic)), Wait(half_time_step), )) testbench_scheme.push(clk_process) testbench.add_process(testbench_scheme) return [testbench]
def generate_scheme(self): int_precision = self.precision.get_integer_format() # We wish to compute vx / vy vx = self.implementation.add_input_variable("x", self.precision, interval=self.input_intervals[0]) vy = self.implementation.add_input_variable("y", self.precision, interval=self.input_intervals[1]) if self.mode is FULL_MODE: quo = self.implementation.add_input_variable("quo", ML_Pointer_Format(int_precision)) i = Variable("i", precision=int_precision, var_type=Variable.Local) q = Variable("q", precision=int_precision, var_type=Variable.Local) CI = lambda v: Constant(v, precision=int_precision) CF = lambda v: Constant(v, precision=self.precision) vx_subnormal = Test(vx, specifier=Test.IsSubnormal, tag="vx_subnormal") vy_subnormal = Test(vy, specifier=Test.IsSubnormal, tag="vy_subnormal") DELTA_EXP = self.precision.get_mantissa_size() scale_factor = Constant(2.0**DELTA_EXP, precision=self.precision) inv_scale_factor = Constant(2.0**-DELTA_EXP, precision=self.precision) normalized_vx = Select(vx_subnormal, vx * scale_factor, vx, tag="scaled_vx") normalized_vy = Select(vy_subnormal, vy * scale_factor, vy, tag="scaled_vy") real_ex = ExponentExtraction(vx, tag="real_ex", precision=int_precision) real_ey = ExponentExtraction(vy, tag="real_ey", precision=int_precision) # if real_e<x/y> is +1023 then it may Overflow in -real_ex for ExponentInsertion # which only supports downto -1022 before falling into subnormal numbers (which are # not supported by ExponentInsertion) real_ex_h0 = real_ex / 2 real_ex_h1 = real_ex - real_ex_h0 real_ey_h0 = real_ey / 2 real_ey_h1 = real_ey - real_ey_h0 EI = lambda v: ExponentInsertion(v, precision=self.precision) mx = Abs((vx * EI(-real_ex_h0)) * EI(-real_ex_h1), tag="mx") my = Abs((vy * EI(-real_ey_h0)) * EI(-real_ey_h1), tag="pre_my") # scale_ey is used to regain the unscaling of mx in the first loop # if real_ey >= real_ex, the first loop is never executed # so a different scaling is required mx_unscaling = Select(real_ey < real_ex, real_ey, real_ex) ey_half0 = (mx_unscaling) / 2 ey_half1 = (mx_unscaling) - ey_half0 scale_ey_half0 = ExponentInsertion(ey_half0, precision=self.precision, tag="scale_ey_half0") scale_ey_half1 = ExponentInsertion(ey_half1, precision=self.precision, tag="scale_ey_half1") # if only vy is subnormal we want to normalize it #normal_cond = LogicalAnd(vy_subnormal, LogicalNot(vx_subnormal)) normal_cond = vy_subnormal #LogicalAnd(vy_subnormal, LogicalNot(vx_subnormal)) my = Select(normal_cond, Abs(MantissaExtraction(vy * scale_factor)), my, tag="my") # vx / vy = vx * 2^-ex * 2^(ex-ey) / (vy * 2^-ey) # vx % vy post_mx = Variable("post_mx", precision=self.precision, var_type=Variable.Local) # scaling for half comparison VY_SCALING = Select(vy_subnormal, 1.0, 0.5, precision=self.precision) VX_SCALING = Select(vy_subnormal, 2.0, 1.0, precision=self.precision) def LogicalXor(a, b): return LogicalOr(LogicalAnd(a, LogicalNot(b)), LogicalAnd(LogicalNot(a), b)) rem_sign = Select(vx < 0, CF(-1), CF(1), precision=self.precision, tag="rem_sign") quo_sign = Select(LogicalXor(vx <0, vy < 0), CI(-1), CI(1), precision=int_precision, tag="quo_sign") loop_watchdog = Variable("loop_watchdog", precision=ML_Int32, var_type=Variable.Local) loop = Statement( real_ex, real_ey, mx, my, loop_watchdog, ReferenceAssign(loop_watchdog, 5000), ReferenceAssign(q, CI(0)), Loop( ReferenceAssign(i, CI(0)), i < (real_ex - real_ey), Statement( ReferenceAssign(i, i+CI(1)), ReferenceAssign(q, ((q << 1) + Select(mx >= my, CI(1), CI(0))).modify_attributes(tag="step1_q")), ReferenceAssign(mx, (CF(2) * (mx - Select(mx >= my, my, CF(0)))).modify_attributes(tag="step1_mx")), # loop watchdog ReferenceAssign(loop_watchdog, loop_watchdog - 1), ConditionBlock(loop_watchdog < 0, Return(-1)), ), ), # unscaling remainder ReferenceAssign(mx, ((mx * scale_ey_half0) * scale_ey_half1).modify_attributes(tag="scaled_rem")), ReferenceAssign(my, ((my * scale_ey_half0) * scale_ey_half1).modify_attributes(tag="scaled_rem_my")), Loop( Statement(), (my > Abs(vy)), Statement( ReferenceAssign(q, ((q << 1) + Select(mx >= Abs(my), CI(1), CI(0))).modify_attributes(tag="step2_q")), ReferenceAssign(mx, (mx - Select(mx >= Abs(my), Abs(my), CF(0))).modify_attributes(tag="step2_mx")), ReferenceAssign(my, (my * 0.5).modify_attributes(tag="step2_my")), # loop watchdog ReferenceAssign(loop_watchdog, loop_watchdog - 1), ConditionBlock(loop_watchdog < 0, Return(-1)), ), ), ReferenceAssign(q, q << 1), Loop( ReferenceAssign(i, CI(0)), mx > Abs(vy), Statement( ReferenceAssign(q, (q + Select(mx > Abs(vy), CI(1), CI(0))).modify_attributes(tag="step3_q")), ReferenceAssign(mx, (mx - Select(mx > Abs(vy), Abs(vy), CF(0))).modify_attributes(tag="step3_mx")), # loop watchdog ReferenceAssign(loop_watchdog, loop_watchdog - 1), ConditionBlock(loop_watchdog < 0, Return(-1)), ), ), ReferenceAssign(q, q + Select(mx >= Abs(vy), CI(1), CI(0))), ReferenceAssign(mx, (mx - Select(mx >= Abs(vy), Abs(vy), CF(0))).modify_attributes(tag="pre_half_mx")), ConditionBlock( # actual comparison is mx > | abs(vy * 0.5) | to avoid rounding effect when # vy is subnormal we mulitply both side by 2.0**60 ((mx * VX_SCALING) > Abs(vy * VY_SCALING)).modify_attributes(tag="half_test"), Statement( ReferenceAssign(q, q + CI(1)), ReferenceAssign(mx, (mx - Abs(vy))) ) ), ConditionBlock( # if the remainder is exactly half the dividend # we need to make sure the quotient is even LogicalAnd( Equal(mx * VX_SCALING, Abs(vy * VY_SCALING)), Equal(Modulo(q, CI(2)), CI(1)), ), Statement( ReferenceAssign(q, q + CI(1)), ReferenceAssign(mx, (mx - Abs(vy))) ) ), ReferenceAssign(mx, rem_sign * mx), ReferenceAssign(q, Modulo(TypeCast(q, precision=self.precision.get_unsigned_integer_format()), Constant(2**self.quotient_size, precision=self.precision.get_unsigned_integer_format()), tag="mod_q") ), ReferenceAssign(q, quo_sign * q), ) # NOTES: Warning QuotientReturn must always preceeds RemainderReturn if self.mode is QUOTIENT_MODE: # QuotientReturn = Return RemainderReturn = lambda _: Statement() elif self.mode is REMAINDER_MODE: QuotientReturn = lambda _: Statement() RemainderReturn = Return elif self.mode is FULL_MODE: QuotientReturn = lambda v: ReferenceAssign(Dereference(quo, precision=int_precision), v) RemainderReturn = Return else: raise NotImplemented # quotient invalid value QUO_INVALID_VALUE = 0 mod_scheme = Statement( # x or y is NaN, a NaN is returned ConditionBlock( LogicalOr(Test(vx, specifier=Test.IsNaN), Test(vy, specifier=Test.IsNaN)), Statement( QuotientReturn(QUO_INVALID_VALUE), RemainderReturn(FP_QNaN(self.precision)) ), ), # ConditionBlock( Test(vy, specifier=Test.IsZero), Statement( QuotientReturn(QUO_INVALID_VALUE), RemainderReturn(FP_QNaN(self.precision)) ), ), ConditionBlock( Test(vx, specifier=Test.IsZero), Statement( QuotientReturn(0), RemainderReturn(vx) ), ), ConditionBlock( Test(vx, specifier=Test.IsInfty), Statement( QuotientReturn(QUO_INVALID_VALUE), RemainderReturn(FP_QNaN(self.precision)) ) ), ConditionBlock( Test(vy, specifier=Test.IsInfty), Statement( QuotientReturn(0), RemainderReturn(vx), ) ), ConditionBlock( Abs(vx) < Abs(vy * 0.5), Statement( QuotientReturn(0), RemainderReturn(vx), ) ), ConditionBlock( Equal(vx, vy), Statement( QuotientReturn(1), # 0 with the same sign as x RemainderReturn(vx - vx), ), ), ConditionBlock( Equal(vx, -vy), Statement( # quotient is -1 QuotientReturn(-1), # 0 with the same sign as x RemainderReturn(vx - vx), ), ), loop, QuotientReturn(q), RemainderReturn(mx), ) quo_scheme = Statement( # x or y is NaN, a NaN is returned ConditionBlock( LogicalOr(Test(vx, specifier=Test.IsNaN), Test(vy, specifier=Test.IsNaN)), Return(QUO_INVALID_VALUE), ), # ConditionBlock( Test(vy, specifier=Test.IsZero), Return(QUO_INVALID_VALUE), ), ConditionBlock( Test(vx, specifier=Test.IsZero), Return(0), ), ConditionBlock( Test(vx, specifier=Test.IsInfty), Return(QUO_INVALID_VALUE), ), ConditionBlock( Test(vy, specifier=Test.IsInfty), Return(QUO_INVALID_VALUE), ), ConditionBlock( Abs(vx) < Abs(vy * 0.5), Return(0), ), ConditionBlock( Equal(vx, vy), Return(1), ), ConditionBlock( Equal(vx, -vy), Return(-1), ), loop, Return(q), ) return mod_scheme
def generate_scheme(self): # declaring CodeFunction and retrieving input variable vx = self.implementation.add_input_variable("x", self.precision) table_size_log = self.table_size_log integer_size = 31 integer_precision = ML_Int32 max_bound = sup(abs(self.input_intervals[0])) max_bound_log = int(ceil(log2(max_bound))) Log.report(Log.Info, "max_bound_log=%s " % max_bound_log) scaling_power = integer_size - max_bound_log Log.report(Log.Info, "scaling power: %s " % scaling_power) storage_precision = ML_Custom_FixedPoint_Format(1, 30, signed=True) Log.report(Log.Info, "tabulating cosine and sine") # cosine and sine fused table fused_table = ML_NewTable( dimensions=[2**table_size_log, 2], storage_precision=storage_precision, tag="fast_lib_shared_table") # self.uniquify_name("cossin_table")) # filling table for i in range(2**table_size_log): local_x = i / S2**table_size_log * S2**max_bound_log cos_local = cos( local_x ) # nearestint(cos(local_x) * S2**storage_precision.get_frac_size()) sin_local = sin( local_x ) # nearestint(sin(local_x) * S2**storage_precision.get_frac_size()) fused_table[i][0] = cos_local fused_table[i][1] = sin_local # argument reduction evaluation scheme # scaling_factor = Constant(S2**scaling_power, precision = self.precision) red_vx_precision = ML_Custom_FixedPoint_Format(31 - scaling_power, scaling_power, signed=True) Log.report( Log.Verbose, "red_vx_precision.get_c_bit_size()=%d" % red_vx_precision.get_c_bit_size()) # red_vx = NearestInteger(vx * scaling_factor, precision = integer_precision) red_vx = Conversion(vx, precision=red_vx_precision, tag="red_vx", debug=debug_fixed32) computation_precision = red_vx_precision # self.precision output_precision = self.get_output_precision() Log.report(Log.Info, "computation_precision is %s" % computation_precision) Log.report(Log.Info, "storage_precision is %s" % storage_precision) Log.report(Log.Info, "output_precision is %s" % output_precision) hi_mask_value = 2**32 - 2**(32 - table_size_log - 1) hi_mask = Constant(hi_mask_value, precision=ML_Int32) Log.report(Log.Info, "hi_mask=0x%x" % hi_mask_value) red_vx_hi_int = BitLogicAnd(TypeCast(red_vx, precision=ML_Int32), hi_mask, precision=ML_Int32, tag="red_vx_hi_int", debug=debugd) red_vx_hi = TypeCast(red_vx_hi_int, precision=red_vx_precision, tag="red_vx_hi", debug=debug_fixed32) red_vx_lo = red_vx - red_vx_hi red_vx_lo.set_attributes(precision=red_vx_precision, tag="red_vx_lo", debug=debug_fixed32) table_index = BitLogicRightShift(TypeCast(red_vx, precision=ML_Int32), scaling_power - (table_size_log - max_bound_log), precision=ML_Int32, tag="table_index", debug=debugd) tabulated_cos = TableLoad(fused_table, table_index, 0, tag="tab_cos", precision=storage_precision, debug=debug_fixed32) tabulated_sin = TableLoad(fused_table, table_index, 1, tag="tab_sin", precision=storage_precision, debug=debug_fixed32) error_function = lambda p, f, ai, mod, t: dirtyinfnorm(f - p, ai) Log.report(Log.Info, "building polynomial approximation for cosine") # cosine polynomial approximation poly_interval = Interval(0, S2**(max_bound_log - table_size_log)) Log.report(Log.Info, "poly_interval=%s " % poly_interval) cos_poly_degree = 2 # int(sup(guessdegree(cos(x), poly_interval, accuracy_goal))) Log.report(Log.Verbose, "cosine polynomial approximation") cos_poly_object, cos_approx_error = Polynomial.build_from_approximation_with_error( cos(sollya.x), [0, 2], [0] + [computation_precision.get_bit_size()], poly_interval, sollya.absolute, error_function=error_function) #cos_eval_scheme = PolynomialSchemeEvaluator.generate_horner_scheme(cos_poly_object, red_vx_lo, unified_precision = computation_precision) Log.report(Log.Info, "cos_approx_error=%e" % cos_approx_error) cos_coeff_list = cos_poly_object.get_ordered_coeff_list() coeff_C0 = cos_coeff_list[0][1] coeff_C2 = Constant(cos_coeff_list[1][1], precision=ML_Custom_FixedPoint_Format(-1, 32, signed=True)) Log.report(Log.Info, "building polynomial approximation for sine") # sine polynomial approximation sin_poly_degree = 2 # int(sup(guessdegree(sin(x)/x, poly_interval, accuracy_goal))) Log.report(Log.Info, "sine poly degree: %e" % sin_poly_degree) Log.report(Log.Verbose, "sine polynomial approximation") sin_poly_object, sin_approx_error = Polynomial.build_from_approximation_with_error( sin(sollya.x) / sollya.x, [0, 2], [0] + [computation_precision.get_bit_size()] * (sin_poly_degree + 1), poly_interval, sollya.absolute, error_function=error_function) sin_coeff_list = sin_poly_object.get_ordered_coeff_list() coeff_S0 = sin_coeff_list[0][1] coeff_S2 = Constant(sin_coeff_list[1][1], precision=ML_Custom_FixedPoint_Format(-1, 32, signed=True)) # scheme selection between sine and cosine if self.cos_output: scheme = self.generate_cos_scheme(computation_precision, tabulated_cos, tabulated_sin, coeff_S2, coeff_C2, red_vx_lo) else: scheme = self.generate_sin_scheme(computation_precision, tabulated_cos, tabulated_sin, coeff_S2, coeff_C2, red_vx_lo) result = Conversion(scheme, precision=self.get_output_precision()) Log.report( Log.Verbose, "result operation tree :\n %s " % result.get_str( display_precision=True, depth=None, memoization_map={})) scheme = Statement(Return(result)) return scheme