def generate_scheme(self): # declaring function input variable vx = self.implementation.add_input_variable("x", self.precision) vy = self.implementation.add_input_variable("y", self.precision) Cst0 = Constant(5, precision=self.precision) Cst1 = Constant(7, precision=self.precision) comp = Comparison(vx, vy, specifier=Comparison.Greater, precision=ML_Bool, tag="comp") comp_eq = Comparison(vx, vy, specifier=Comparison.Equal, precision=ML_Bool, tag="comp_eq") scheme = Statement( ConditionBlock( comp, Return(vy, precision=self.precision), ConditionBlock( comp_eq, Return(vx + vy * Cst0 - Cst1, precision=self.precision))), ConditionBlock(comp_eq, Return(Cst1 * vy, precision=self.precision)), Return(vx * vy, precision=self.precision)) return scheme
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 minmax_legalizer(optree): op0 = optree.get_input(0) op1 = optree.get_input(1) bool_prec = get_compatible_bool_format(optree) comp = Comparison(op0, op1, specifier=predicate, precision=bool_prec, tag="minmax_pred") # forward_stage_attributes(optree, comp) result = Select(comp, op0, op1, precision=optree.get_precision()) forward_attributes(optree, result) return result
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_pipeline_stage(entity): """ Process a entity to generate pipeline stages required """ retiming_map = {} retime_map = RetimeMap() output_assign_list = entity.implementation.get_output_assign() for output in output_assign_list: Log.report( Log.Verbose, "generating pipeline from output %s " % (output.get_str(depth=1))) retime_op(output, retime_map) process_statement = Statement() # adding stage forward process clk = entity.get_clk_input() clock_statement = Statement() for stage_id in sorted(retime_map.stage_forward.keys()): stage_statement = Statement(*tuple( assign for assign in retime_map.stage_forward[stage_id])) clock_statement.add(stage_statement) # To meet simulation / synthesis tools, we build # a single if clock predicate block which contains all # the stage register allocation clock_block = ConditionBlock( LogicalAnd(Event(clk, precision=ML_Bool), Comparison(clk, Constant(1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool), precision=ML_Bool), clock_statement) process_statement.add(clock_block) pipeline_process = Process(process_statement, sensibility_list=[clk]) for op in retime_map.pre_statement: pipeline_process.add_to_pre_statement(op) entity.implementation.add_process(pipeline_process) stage_num = len(retime_map.stage_forward.keys()) #print "there are %d pipeline stages" % (stage_num) return stage_num
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 legalize_mp_2elt_comparison(optree): """ Transform comparison on ML_Compound_FP_Format object into comparison on sub-fields """ specifier = optree.specifier lhs = optree.get_input(0) rhs = optree.get_input(1) # TODO/FIXME: assume than multi-limb operand are normalized if specifier == Comparison.Equal: return LogicalAnd( Comparison(lhs.hi, rhs.hi, specifier=Comparison.Equal, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=Comparison.Equal, precision=ML_Bool), precision=ML_Bool ) elif specifier == Comparison.NotEqual: return LogicalOr( Comparison(lhs.hi, rhs.hi, specifier=Comparison.NotEqual, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=Comparison.NotEqual, precision=ML_Bool), precision=ML_Bool ) elif specifier in [Comparison.Less, Comparison.Greater, Comparison.GreaterOrEqual, Comparison.LessOrEqual]: strict_specifier = { Comparison.Less: Comparison.Less, Comparison.Greater: Comparison.Greater, Comparison.LessOrEqual: Comparison.Less, Comparison.GreaterOrEqual: Comparison.Greater }[specifier] return LogicalOr( Comparison(lhs.hi, rhs.hi, specifier=strict_specifier, precision=ML_Bool), LogicalAnd( Comparison(lhs.hi, rhs.hi, specifier=Comparison.Equal, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=specifier, precision=ML_Bool), precision=ML_Bool ), precision=ML_Bool ) else: Log.report(Log.Error, "unsupported specifier {} in legalize_mp_2elt_comparison", specifier)
def generate_scheme(self): # declaring target and instantiating optimization engine vx = self.implementation.add_input_variable("x", self.precision) Log.set_dump_stdout(True) Log.report(Log.Info, "\033[33;1m generating implementation scheme \033[0m") if self.debug_flag: Log.report(Log.Info, "\033[31;1m debug has been enabled \033[0;m") # local overloading of RaiseReturn operation def ExpRaiseReturn(*args, **kwords): kwords["arg_value"] = vx kwords["function_name"] = self.function_name if self.libm_compliant: return RaiseReturn(*args, precision=self.precision, **kwords) else: return Return(kwords["return_value"], precision=self.precision) test_nan_or_inf = Test(vx, specifier=Test.IsInfOrNaN, likely=False, debug=debug_multi, tag="nan_or_inf") test_nan = Test(vx, specifier=Test.IsNaN, debug=debug_multi, tag="is_nan_test") test_positive = Comparison(vx, 0, specifier=Comparison.GreaterOrEqual, debug=debug_multi, tag="inf_sign") test_signaling_nan = Test(vx, specifier=Test.IsSignalingNaN, debug=debug_multi, tag="is_signaling_nan") return_snan = Statement( ExpRaiseReturn(ML_FPE_Invalid, return_value=FP_QNaN(self.precision))) # return in case of infinity input infty_return = Statement( ConditionBlock( test_positive, Return(FP_PlusInfty(self.precision), precision=self.precision), Return(FP_PlusZero(self.precision), precision=self.precision))) # return in case of specific value input (NaN or inf) specific_return = ConditionBlock( test_nan, ConditionBlock( test_signaling_nan, return_snan, Return(FP_QNaN(self.precision), precision=self.precision)), infty_return) # return in case of standard (non-special) input # exclusion of early overflow and underflow cases precision_emax = self.precision.get_emax() precision_max_value = S2 * S2**precision_emax exp_overflow_bound = sollya.ceil(log(precision_max_value)) early_overflow_test = Comparison(vx, exp_overflow_bound, likely=False, specifier=Comparison.Greater) early_overflow_return = Statement( ClearException() if self.libm_compliant else Statement(), ExpRaiseReturn(ML_FPE_Inexact, ML_FPE_Overflow, return_value=FP_PlusInfty(self.precision))) precision_emin = self.precision.get_emin_subnormal() precision_min_value = S2**precision_emin exp_underflow_bound = floor(log(precision_min_value)) early_underflow_test = Comparison(vx, exp_underflow_bound, likely=False, specifier=Comparison.Less) early_underflow_return = Statement( ClearException() if self.libm_compliant else Statement(), ExpRaiseReturn(ML_FPE_Inexact, ML_FPE_Underflow, return_value=FP_PlusZero(self.precision))) # constant computation invlog2 = self.precision.round_sollya_object(1 / log(2), sollya.RN) interval_vx = Interval(exp_underflow_bound, exp_overflow_bound) interval_fk = interval_vx * invlog2 interval_k = Interval(floor(inf(interval_fk)), sollya.ceil(sup(interval_fk))) log2_hi_precision = self.precision.get_field_size() - ( sollya.ceil(log2(sup(abs(interval_k)))) + 2) Log.report(Log.Info, "log2_hi_precision: %d" % log2_hi_precision) invlog2_cst = Constant(invlog2, precision=self.precision) log2_hi = round(log(2), log2_hi_precision, sollya.RN) log2_lo = self.precision.round_sollya_object( log(2) - log2_hi, sollya.RN) # argument reduction unround_k = vx * invlog2 unround_k.set_attributes(tag="unround_k", debug=debug_multi) k = NearestInteger(unround_k, precision=self.precision, debug=debug_multi) ik = NearestInteger(unround_k, precision=self.precision.get_integer_format(), debug=debug_multi, tag="ik") ik.set_tag("ik") k.set_tag("k") exact_pre_mul = (k * log2_hi) exact_pre_mul.set_attributes(exact=True) exact_hi_part = vx - exact_pre_mul exact_hi_part.set_attributes(exact=True, tag="exact_hi", debug=debug_multi, prevent_optimization=True) exact_lo_part = -k * log2_lo exact_lo_part.set_attributes(tag="exact_lo", debug=debug_multi, prevent_optimization=True) r = exact_hi_part + exact_lo_part r.set_tag("r") r.set_attributes(debug=debug_multi) approx_interval = Interval(-log(2) / 2, log(2) / 2) approx_interval_half = approx_interval / 2 approx_interval_split = [ Interval(-log(2) / 2, inf(approx_interval_half)), approx_interval_half, Interval(sup(approx_interval_half), log(2) / 2) ] # TODO: should be computed automatically exact_hi_interval = approx_interval exact_lo_interval = -interval_k * log2_lo opt_r = self.optimise_scheme(r, copy={}) tag_map = {} self.opt_engine.register_nodes_by_tag(opt_r, tag_map) cg_eval_error_copy_map = { vx: Variable("x", precision=self.precision, interval=interval_vx), tag_map["k"]: Variable("k", interval=interval_k, precision=self.precision) } #try: if is_gappa_installed(): eval_error = self.gappa_engine.get_eval_error_v2( self.opt_engine, opt_r, cg_eval_error_copy_map, gappa_filename="red_arg.g") else: eval_error = 0.0 Log.report(Log.Warning, "gappa is not installed in this environnement") Log.report(Log.Info, "eval error: %s" % eval_error) local_ulp = sup(ulp(sollya.exp(approx_interval), self.precision)) # FIXME refactor error_goal from accuracy Log.report(Log.Info, "accuracy: %s" % self.accuracy) if isinstance(self.accuracy, ML_Faithful): error_goal = local_ulp elif isinstance(self.accuracy, ML_CorrectlyRounded): error_goal = S2**-1 * local_ulp elif isinstance(self.accuracy, ML_DegradedAccuracyAbsolute): error_goal = self.accuracy.goal elif isinstance(self.accuracy, ML_DegradedAccuracyRelative): error_goal = self.accuracy.goal else: Log.report(Log.Error, "unknown accuracy: %s" % self.accuracy) # error_goal = local_ulp #S2**-(self.precision.get_field_size()+1) error_goal_approx = S2**-1 * error_goal Log.report(Log.Info, "\033[33;1m building mathematical polynomial \033[0m\n") poly_degree = max( sup( guessdegree( expm1(sollya.x) / sollya.x, approx_interval, error_goal_approx)) - 1, 2) init_poly_degree = poly_degree error_function = lambda p, f, ai, mod, t: dirtyinfnorm(f - p, ai) polynomial_scheme_builder = PolynomialSchemeEvaluator.generate_estrin_scheme #polynomial_scheme_builder = PolynomialSchemeEvaluator.generate_horner_scheme while 1: Log.report(Log.Info, "attempting poly degree: %d" % poly_degree) precision_list = [1] + [self.precision] * (poly_degree) poly_object, poly_approx_error = Polynomial.build_from_approximation_with_error( expm1(sollya.x), poly_degree, precision_list, approx_interval, sollya.absolute, error_function=error_function) Log.report(Log.Info, "polynomial: %s " % poly_object) sub_poly = poly_object.sub_poly(start_index=2) Log.report(Log.Info, "polynomial: %s " % sub_poly) Log.report(Log.Info, "poly approx error: %s" % poly_approx_error) Log.report( Log.Info, "\033[33;1m generating polynomial evaluation scheme \033[0m") pre_poly = polynomial_scheme_builder( poly_object, r, unified_precision=self.precision) pre_poly.set_attributes(tag="pre_poly", debug=debug_multi) pre_sub_poly = polynomial_scheme_builder( sub_poly, r, unified_precision=self.precision) pre_sub_poly.set_attributes(tag="pre_sub_poly", debug=debug_multi) poly = 1 + (exact_hi_part + (exact_lo_part + pre_sub_poly)) poly.set_tag("poly") # optimizing poly before evaluation error computation #opt_poly = self.opt_engine.optimization_process(poly, self.precision, fuse_fma = fuse_fma) #opt_sub_poly = self.opt_engine.optimization_process(pre_sub_poly, self.precision, fuse_fma = fuse_fma) opt_poly = self.optimise_scheme(poly) opt_sub_poly = self.optimise_scheme(pre_sub_poly) # evaluating error of the polynomial approximation r_gappa_var = Variable("r", precision=self.precision, interval=approx_interval) exact_hi_gappa_var = Variable("exact_hi", precision=self.precision, interval=exact_hi_interval) exact_lo_gappa_var = Variable("exact_lo", precision=self.precision, interval=exact_lo_interval) vx_gappa_var = Variable("x", precision=self.precision, interval=interval_vx) k_gappa_var = Variable("k", interval=interval_k, precision=self.precision) #print "exact_hi interval: ", exact_hi_interval sub_poly_error_copy_map = { #r.get_handle().get_node(): r_gappa_var, #vx.get_handle().get_node(): vx_gappa_var, exact_hi_part.get_handle().get_node(): exact_hi_gappa_var, exact_lo_part.get_handle().get_node(): exact_lo_gappa_var, #k.get_handle().get_node(): k_gappa_var, } poly_error_copy_map = { exact_hi_part.get_handle().get_node(): exact_hi_gappa_var, exact_lo_part.get_handle().get_node(): exact_lo_gappa_var, } if is_gappa_installed(): sub_poly_eval_error = -1.0 sub_poly_eval_error = self.gappa_engine.get_eval_error_v2( self.opt_engine, opt_sub_poly, sub_poly_error_copy_map, gappa_filename="%s_gappa_sub_poly.g" % self.function_name) dichotomy_map = [ { exact_hi_part.get_handle().get_node(): approx_interval_split[0], }, { exact_hi_part.get_handle().get_node(): approx_interval_split[1], }, { exact_hi_part.get_handle().get_node(): approx_interval_split[2], }, ] poly_eval_error_dico = self.gappa_engine.get_eval_error_v3( self.opt_engine, opt_poly, poly_error_copy_map, gappa_filename="gappa_poly.g", dichotomy=dichotomy_map) poly_eval_error = max( [sup(abs(err)) for err in poly_eval_error_dico]) else: poly_eval_error = 0.0 sub_poly_eval_error = 0.0 Log.report(Log.Warning, "gappa is not installed in this environnement") Log.report(Log.Info, "stopping autonomous degree research") # incrementing polynomial degree to counteract initial decrementation effect poly_degree += 1 break Log.report(Log.Info, "poly evaluation error: %s" % poly_eval_error) Log.report(Log.Info, "sub poly evaluation error: %s" % sub_poly_eval_error) global_poly_error = None global_rel_poly_error = None for case_index in range(3): poly_error = poly_approx_error + poly_eval_error_dico[ case_index] rel_poly_error = sup( abs(poly_error / sollya.exp(approx_interval_split[case_index]))) if global_rel_poly_error == None or rel_poly_error > global_rel_poly_error: global_rel_poly_error = rel_poly_error global_poly_error = poly_error flag = error_goal > global_rel_poly_error if flag: break else: poly_degree += 1 late_overflow_test = Comparison(ik, self.precision.get_emax(), specifier=Comparison.Greater, likely=False, debug=debug_multi, tag="late_overflow_test") overflow_exp_offset = (self.precision.get_emax() - self.precision.get_field_size() / 2) diff_k = Subtraction( ik, Constant(overflow_exp_offset, precision=self.precision.get_integer_format()), precision=self.precision.get_integer_format(), debug=debug_multi, tag="diff_k", ) late_overflow_result = (ExponentInsertion( diff_k, precision=self.precision) * poly) * ExponentInsertion( overflow_exp_offset, precision=self.precision) late_overflow_result.set_attributes(silent=False, tag="late_overflow_result", debug=debug_multi, precision=self.precision) late_overflow_return = ConditionBlock( Test(late_overflow_result, specifier=Test.IsInfty, likely=False), ExpRaiseReturn(ML_FPE_Overflow, return_value=FP_PlusInfty(self.precision)), Return(late_overflow_result, precision=self.precision)) late_underflow_test = Comparison(k, self.precision.get_emin_normal(), specifier=Comparison.LessOrEqual, likely=False) underflow_exp_offset = 2 * self.precision.get_field_size() corrected_exp = Addition( ik, Constant(underflow_exp_offset, precision=self.precision.get_integer_format()), precision=self.precision.get_integer_format(), tag="corrected_exp") late_underflow_result = ( ExponentInsertion(corrected_exp, precision=self.precision) * poly) * ExponentInsertion(-underflow_exp_offset, precision=self.precision) late_underflow_result.set_attributes(debug=debug_multi, tag="late_underflow_result", silent=False) test_subnormal = Test(late_underflow_result, specifier=Test.IsSubnormal) late_underflow_return = Statement( ConditionBlock( test_subnormal, ExpRaiseReturn(ML_FPE_Underflow, return_value=late_underflow_result)), Return(late_underflow_result, precision=self.precision)) twok = ExponentInsertion(ik, tag="exp_ik", debug=debug_multi, precision=self.precision) #std_result = twok * ((1 + exact_hi_part * pre_poly) + exact_lo_part * pre_poly) std_result = twok * poly std_result.set_attributes(tag="std_result", debug=debug_multi) result_scheme = ConditionBlock( late_overflow_test, late_overflow_return, ConditionBlock(late_underflow_test, late_underflow_return, Return(std_result, precision=self.precision))) std_return = ConditionBlock( early_overflow_test, early_overflow_return, ConditionBlock(early_underflow_test, early_underflow_return, result_scheme)) # main scheme Log.report(Log.Info, "\033[33;1m MDL scheme \033[0m") scheme = ConditionBlock( test_nan_or_inf, Statement(ClearException() if self.libm_compliant else Statement(), specific_return), std_return) return scheme
def generate_pipeline_stage(entity, reset=False, recirculate=False, one_process_per_stage=True): """ Process a entity to generate pipeline stages required """ retiming_map = {} retime_map = RetimeMap() output_assign_list = entity.implementation.get_output_assign() for output in output_assign_list: Log.report(Log.Verbose, "generating pipeline from output {} ", output) retime_op(output, retime_map) for recirculate_stage in entity.recirculate_signal_map: recirculate_ctrl = entity.recirculate_signal_map[recirculate_stage] Log.report(Log.Verbose, "generating pipeline from recirculation control signal {}", recirculate_ctrl) retime_op(recirculate_ctrl, retime_map) process_statement = Statement() # adding stage forward process clk = entity.get_clk_input() clock_statement = Statement() # handle towards the first clock Process (in generation order) # which must be the one whose pre_statement is filled with # signal required to be generated outside the processes first_process = False for stage_id in sorted(retime_map.stage_forward.keys()): stage_statement = Statement(*tuple( assign for assign in retime_map.stage_forward[stage_id])) if reset: reset_statement = Statement() for assign in retime_map.stage_forward[stage_id]: target = assign.get_input(0) reset_value = Constant(0, precision=target.get_precision()) reset_statement.push(ReferenceAssign(target, reset_value)) if recirculate: # inserting recirculation condition recirculate_signal = entity.get_recirculate_signal(stage_id) stage_statement = ConditionBlock( Comparison( recirculate_signal, Constant(0, precision=recirculate_signal.get_precision()), specifier=Comparison.Equal, precision=ML_Bool), stage_statement) stage_statement = ConditionBlock( Comparison(entity.reset_signal, Constant(1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool), reset_statement, stage_statement) # To meet simulation / synthesis tools, we build # a single if clock predicate block per stage clock_block = ConditionBlock( LogicalAnd(Event(clk, precision=ML_Bool), Comparison(clk, Constant(1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool), precision=ML_Bool), stage_statement) if one_process_per_stage: clock_process = Process(clock_block, sensibility_list=[clk]) entity.implementation.add_process(clock_process) first_process = first_process or clock_process else: clock_statement.add(clock_block) if one_process_per_stage: pass else: process_statement.add(clock_statement) pipeline_process = Process(process_statement, sensibility_list=[clk]) entity.implementation.add_process(pipeline_process) first_process = pipeline_process # statement that gather signals which must be pre-computed for op in retime_map.pre_statement: first_process.add_to_pre_statement(op) stage_num = len(retime_map.stage_forward.keys()) #print "there are %d pipeline stages" % (stage_num) return stage_num
def legalize_mp_3elt_comparison(optree): """ Transform comparison on ML_Compound_FP_Format object into comparison on sub-fields """ specifier = optree.specifier lhs = optree.get_input(0) rhs = optree.get_input(1) # TODO/FIXME: assume than multi-limb operand are normalized if specifier == Comparison.Equal: # renormalize if not constant lhs = lhs if is_constant(lhs) else BuildFromComponent(*Normalize_33(lhs.hi, lhs.me, lhs.lo, precision=lhs.precision.get_limb_precision(0)), precision=lhs.precision) rhs = rhs if is_constant(rhs) else BuildFromComponent(*Normalize_33(rhs.hi, rhs.me, rhs.lo, precision=rhs.precision.get_limb_precision(0)), precision=rhs.precision) return LogicalAnd( Comparison(lhs.hi, rhs.hi, specifier=Comparison.Equal, precision=ML_Bool), LogicalAnd( Comparison(lhs.me, rhs.me, specifier=Comparison.Equal, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=Comparison.Equal, precision=ML_Bool), precision=ML_Bool ), precision=ML_Bool ) elif specifier == Comparison.NotEqual: # renormalize if not constant lhs = lhs if is_constant(lhs) else BuildFromComponent(*Normalize_33(lhs.hi, lhs.me, lhs.lo, precision=lhs.precision.get_limb_precision(0)), precision=lhs.precision) rhs = rhs if is_constant(rhs) else BuildFromComponent(*Normalize_33(rhs.hi, rhs.me, rhs.lo, precision=rhs.precision.get_limb_precision(0)), precision=rhs.precision) return LogicalOr( Comparison(lhs.hi, rhs.hi, specifier=Comparison.NotEqual, precision=ML_Bool), LogicalOr( Comparison(lhs.me, rhs.me, specifier=Comparison.NotEqual, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=Comparison.NotEqual, precision=ML_Bool), precision=ML_Bool ), precision=ML_Bool ) elif specifier in [Comparison.LessOrEqual, Comparison.GreaterOrEqual, Comparison.Greater, Comparison.Less]: strict_specifier = { Comparison.Less: Comparison.Less, Comparison.Greater: Comparison.Greater, Comparison.LessOrEqual: Comparison.Less, Comparison.GreaterOrEqual: Comparison.Greater }[specifier] # renormalize if not constant lhs = lhs if is_constant(lhs) else BuildFromComponent(*Normalize_33(lhs.hi, lhs.me, lhs.lo, precision=lhs.precision.get_limb_precision(0)), precision=lhs.precision) rhs = rhs if is_constant(rhs) else BuildFromComponent(*Normalize_33(rhs.hi, rhs.me, rhs.lo, precision=rhs.precision.get_limb_precision(0)), precision=rhs.precision) return LogicalOr( Comparison(lhs.hi, rhs.hi, specifier=strict_specifier, precision=ML_Bool), LogicalAnd( Comparison(lhs.hi, rhs.hi, specifier=Comparison.Equal, precision=ML_Bool), LogicalOr( Comparison(lhs.me, rhs.me, specifier=strict_specifier, precision=ML_Bool), LogicalAnd( Comparison(lhs.me, rhs.me, specifier=Comparison.Equal, precision=ML_Bool), Comparison(lhs.lo, rhs.lo, specifier=specifier, precision=ML_Bool), precision=ML_Bool ), precision=ML_Bool ), precision=ML_Bool ), precision=ML_Bool ) else: Log.report(Log.Error, "unsupported specifier {} in legalize_mp_2elt_comparison", specifier)
def legalize_test(optree): """ transform a Test optree into a sequence of basic node """ op_input = optree.get_input(0) predicate = optree.specifier test_bool_format = get_compatible_bool_format(op_input) if op_input.precision.is_vector_format(): input_scalar_precision = op_input.precision.get_scalar_format() vector_size = op_input.precision.get_vector_size() int_precision = { ML_Int32: { 2: v2int32, 4: v4int32, 8: v8int32, }, ML_Int64: { 2: v2int64, 4: v4int64, 8: v8int64, } }[input_scalar_precision.get_integer_format()][vector_size] nanorinf_cst = [input_scalar_precision.get_nanorinf_exp_field() ] * vector_size zero_cst = [0] * vector_size one_cst = [1] * vector_size else: input_scalar_precision = op_input.precision int_precision = input_scalar_precision.get_integer_format() nanorinf_cst = input_scalar_precision.get_nanorinf_exp_field() zero_cst = 0 one_cst = 1 if predicate is Test.IsInfOrNaN: return Comparison(generate_exp_extraction(op_input), Constant(nanorinf_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format) elif predicate is Test.IsNaN: return LogicalAnd(Comparison(generate_exp_extraction(op_input), Constant(nanorinf_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), Comparison( generate_raw_mantissa_extraction(op_input), Constant(zero_cst, precision=int_precision), specifier=Comparison.NotEqual, precision=test_bool_format), precision=test_bool_format) elif predicate is Test.IsSubnormal: return Comparison(generate_exp_extraction(op_input), Constant(zero_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format) elif predicate is Test.IsSignalingNaN: quiet_bit_index = input_scalar_precision.get_field_size() - 1 return LogicalAnd( Comparison(generate_exp_extraction(op_input), Constant(nanorinf_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), LogicalAnd(Comparison(generate_raw_mantissa_extraction(op_input), Constant(zero_cst, precision=int_precision), specifier=Comparison.NotEqual, precision=test_bool_format), Comparison(generate_field_extraction( op_input, int_precision, quiet_bit_index, quiet_bit_index), Constant(zero_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), precision=test_bool_format), precision=test_bool_format) elif predicate is Test.IsQuietNaN: quiet_bit_index = input_scalar_precision.get_field_size() - 1 return LogicalAnd( Comparison(generate_exp_extraction(op_input), Constant(nanorinf_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), LogicalAnd(Comparison(generate_raw_mantissa_extraction(op_input), Constant(zero_cst, precision=int_precision), specifier=Comparison.NotEqual, precision=test_bool_format), Comparison(generate_field_extraction( op_input, int_precision, quiet_bit_index, quiet_bit_index), Constant(one_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), precision=test_bool_format), precision=test_bool_format) elif predicate is Test.IsInfty: return LogicalAnd(Comparison(generate_exp_extraction(op_input), Constant(nanorinf_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), Comparison( generate_raw_mantissa_extraction(op_input), Constant(zero_cst, precision=int_precision), specifier=Comparison.Equal, precision=test_bool_format), precision=test_bool_format) else: Log.report(Log.Error, "unsupported predicate {}".format(predicate), error=NotImplementedError)
def generate_scheme(self): # declaring function input variable vx = self.implementation.add_input_variable("x", ML_Binary32) # declaring specific interval for input variable <x> vx.set_interval(Interval(-1, 1)) # declaring free Variable y vy = Variable("y", precision=ML_Exact) # declaring expression with vx variable expr = vx * vx - vx * 2 # declaring second expression with vx variable expr2 = vx * vx - vx # optimizing expressions (defining every unknown precision as the # default one + some optimization as FMA merging if enabled) opt_expr = self.optimise_scheme(expr) opt_expr2 = self.optimise_scheme(expr2) # setting specific tag name for optimized expression (to be extracted # from gappa script ) opt_expr.set_tag("goal") opt_expr2.set_tag("new_goal") # defining default goal to gappa execution gappa_goal = opt_expr # declaring EXACT expression to be used as hint in Gappa's script annotation = self.opt_engine.exactify(vy * (1 / vy)) # the dict var_bound is used to limit the DAG part to be explored when # generating the gappa script, each pair (key, value), indicate a node to # stop at <key> # and a node to replace it with during the generation: <node>, # <node> must be a Variable instance with defined interval # vx.get_handle().get_node() is used to retrieve the node instanciating # the abstract node <vx> after the call to self.optimise_scheme var_bound = { vx.get_handle().get_node(): Variable("x", precision=ML_Binary32, interval=vx.get_interval()) } # generating gappa code to determine interval for <opt_expr> # NOTES: var_bound must be converted from an iterator to a list to avoid # implicit modification by get_interval_code gappa_code = self.gappa_engine.get_interval_code( [opt_expr], list(var_bound.keys()), var_bound) # add a manual hint to the gappa code # which state thtat vy * (1 / vy) -> 1 { vy <> 0 }; self.gappa_engine.add_hint( gappa_code, annotation, Constant(1, precision=ML_Exact), Comparison(vy, Constant(0, precision=ML_Integer), specifier=Comparison.NotEqual, precision=ML_Bool)) # adding the expression <opt_expr2> as an extra goal in the gappa script self.gappa_engine.add_goal(gappa_code, opt_expr2) # executing gappa on the script generated from <gappa_code> # extract the result and store them into <gappa_result> # which is a dict indexed by the goals' tag if is_gappa_installed(): gappa_result = execute_gappa_script_extract( gappa_code.get(self.gappa_engine)) Log.report(Log.Info, "eval error: ", gappa_result["new_goal"]) else: Log.report( Log.Warning, "gappa was not installed: unable to check execute_gappa_script_extract" ) # dummy scheme to make functionnal code generation scheme = Statement(Return(vx)) return scheme
def generate_pipeline_stage(entity, reset=False, recirculate=False, one_process_per_stage=True, synchronous_reset=True, negate_reset=False): """ Process a entity to generate pipeline stages required to implement pipeline structure described by node's stage attributes. :param entity: input entity to pipeline :type entity: ML_EntityBasis :param reset: indicate if a reset must be generated for pipeline registers :type reset: bool :param recirculate: trigger the integration of a recirculation signal to the stage flopping condition :type recirculate: bool :param one_process_per_stage:forces the generation of a separate process for each pipeline stage (else a unique process is generated for all the stages :type one_process_per_stage: bool :param synchronous_reset: triggers the generation of a clocked reset :type synchronous_reset: bool :param negate_reset: if set indicates the reset is triggered when reset signal is 0 (else 1) :type negate_reset: bool """ retiming_map = {} retime_map = RetimeMap() output_assign_list = entity.implementation.get_output_assign() for output in output_assign_list: Log.report(Log.Verbose, "generating pipeline from output {} ", output) retime_op(output, retime_map) for recirculate_stage in entity.recirculate_signal_map: recirculate_ctrl = entity.recirculate_signal_map[recirculate_stage] Log.report(Log.Verbose, "generating pipeline from recirculation control signal {}", recirculate_ctrl) retime_op(recirculate_ctrl, retime_map) process_statement = Statement() # adding stage forward process clk = entity.get_clk_input() clock_statement = Statement() global_reset_statement = Statement() Log.report(Log.Info, "design has {} flip-flop(s).", retime_map.register_count) # handle towards the first clock Process (in generation order) # which must be the one whose pre_statement is filled with # signal required to be generated outside the processes first_process = False for stage_id in sorted(retime_map.stage_forward.keys()): stage_statement = Statement( *tuple(assign for assign in retime_map.stage_forward[stage_id])) if reset: reset_statement = Statement() for assign in retime_map.stage_forward[stage_id]: target = assign.get_input(0) reset_value = Constant(0, precision=target.get_precision()) reset_statement.push(ReferenceAssign(target, reset_value)) if recirculate: # inserting recirculation condition recirculate_signal = entity.get_recirculate_signal(stage_id) stage_statement = ConditionBlock( Comparison( recirculate_signal, Constant(0, precision=recirculate_signal.get_precision()), specifier=Comparison.Equal, precision=ML_Bool ), stage_statement ) if synchronous_reset: # build a compound statement with reset and flops statement stage_statement = ConditionBlock( Comparison( entity.reset_signal, Constant(0 if negate_reset else 1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool ), reset_statement, stage_statement ) else: # for asynchronous reset, reset is in a non-clocked statement # and will be added at the end of stage to the same process than # register clocking global_reset_statement.add(reset_statement) # To meet simulation / synthesis tools, we build # a single if clock predicate block per stage clock_block = ConditionBlock( LogicalAnd( Event(clk, precision=ML_Bool), Comparison( clk, Constant(1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool ), precision=ML_Bool ), stage_statement ) if one_process_per_stage: if reset and not synchronous_reset: clock_block = ConditionBlock( Comparison( entity.reset_signal, Constant(0 if negate_reset else 1, precision=ML_StdLogic), specifier=Comparison.Equal, precision=ML_Bool ), reset_statement, clock_block ) clock_process = Process(clock_block, sensibility_list=[clk, entity.reset_signal]) else: # no reset, or synchronous reset (already appended to clock_block) clock_process = Process(clock_block, sensibility_list=[clk]) entity.implementation.add_process(clock_process) first_process = first_process or clock_process else: clock_statement.add(clock_block) if one_process_per_stage: # reset and clock processed where generated at each stage loop pass else: process_statement.add(clock_statement) if synchronous_reset: pipeline_process = Process(process_statement, sensibility_list=[clk]) else: process_statement.add(global_reset_statement) pipeline_process = Process(process_statement, sensibility_list=[clk, entity.reset_signal]) entity.implementation.add_process(pipeline_process) first_process = pipeline_process # statement that gather signals which must be pre-computed for op in retime_map.pre_statement: first_process.add_to_pre_statement(op) stage_num = len(retime_map.stage_forward.keys()) Log.report(Log.Info, "there are {} pipeline stage(s)", stage_num) return stage_num
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): # declaring target and instantiating optimization engine vx = self.implementation.add_input_variable("x", self.precision) vx.set_attributes(precision=self.precision, tag="vx", debug=debug_multi) Log.set_dump_stdout(True) Log.report(Log.Info, "\033[33;1m Generating implementation scheme \033[0m") if self.debug_flag: Log.report(Log.Info, "\033[31;1m debug has been enabled \033[0;m") C0 = Constant(0, precision=self.precision) C0_plus = Constant(FP_PlusZero(self.precision)) C0_minus = Constant(FP_MinusZero(self.precision)) def local_test(specifier, tag): """ Local wrapper to generate Test operations """ return Test(vx, specifier=specifier, likely=False, debug=debug_multi, tag="is_%s" % tag, precision=ML_Bool) test_NaN = local_test(Test.IsNaN, "is_NaN") test_inf = local_test(Test.IsInfty, "is_Inf") test_NaN_or_Inf = local_test(Test.IsInfOrNaN, "is_Inf_Or_Nan") test_negative = Comparison(vx, C0, specifier=Comparison.Less, debug=debug_multi, tag="is_Negative", precision=ML_Bool, likely=False) test_NaN_or_Neg = LogicalOr(test_NaN, test_negative, precision=ML_Bool) test_std = LogicalNot(LogicalOr(test_NaN_or_Inf, test_negative, precision=ML_Bool, likely=False), precision=ML_Bool, likely=True) test_zero = Comparison(vx, C0, specifier=Comparison.Equal, likely=False, debug=debug_multi, tag="Is_Zero", precision=ML_Bool) return_NaN_or_neg = Statement(Return(FP_QNaN(self.precision))) return_inf = Statement(Return(FP_PlusInfty(self.precision))) return_PosZero = Return(C0_plus) return_NegZero = Return(C0_minus) NR_init = ReciprocalSquareRootSeed(vx, precision=self.precision, tag="sqrt_seed", debug=debug_multi) result = compute_sqrt(vx, NR_init, int(self.num_iter), precision=self.precision) return_non_std = ConditionBlock( test_NaN_or_Neg, return_NaN_or_neg, ConditionBlock( test_inf, return_inf, ConditionBlock(test_zero, return_PosZero, return_NegZero))) return_std = Return(result) scheme = ConditionBlock(test_std, return_std, return_non_std) return scheme
def generate_datafile_testbench(self, tc_list, io_map, input_signals, output_signals, time_step, test_fname="test.input"): """ Generate testbench with input and output data externalized in a data file """ # textio function to read hexadecimal text def FCT_HexaRead_gen(input_format): legalized_input_format = input_format FCT_HexaRead = FunctionObject("hread", [HDL_LINE, legalized_input_format], ML_Void, FunctionOperator("hread", void_function=True, arity=2)) return FCT_HexaRead # textio function to read binary text FCT_Read = FunctionObject("read", [HDL_LINE, ML_StdLogic], ML_Void, FunctionOperator("read", void_function=True, arity=2)) input_line = Variable("input_line", precision=HDL_LINE, var_type=Variable.Local) # building ordered list of input and output signal names input_signal_list = [sname for sname in input_signals.keys()] input_statement = Statement() for input_name in input_signal_list: input_format = input_signals[input_name].precision input_var = Variable( "v_" + input_name, precision=input_format, var_type=Variable.Local) if input_format is ML_StdLogic: input_statement.add(FCT_Read(input_line, input_var)) else: input_statement.add(FCT_HexaRead_gen(input_format)(input_line, input_var)) input_statement.add(ReferenceAssign(input_signals[input_name], input_var)) output_signal_list = [sname for sname in output_signals.keys()] output_statement = Statement() for output_name in output_signal_list: output_format = output_signals[output_name].precision output_var = Variable( "v_" + output_name, precision=output_format, var_type=Variable.Local) if output_format is ML_StdLogic: output_statement.add(FCT_Read(input_line, output_var)) else: output_statement.add(FCT_HexaRead_gen(output_format)(input_line, output_var)) output_signal = output_signals[output_name] #value_msg = get_output_value_msg(output_signal, output_value) test_pass_cond, check_statement = get_output_check_statement(output_signal, output_name, output_var) input_msg = multi_Concatenation(*tuple(sum([[" %s=" % input_tag, signal_str_conversion(input_signals[input_tag], input_signals[input_tag].precision)] for input_tag in input_signal_list], []))) output_statement.add(check_statement) assert_statement = Assert( test_pass_cond, multi_Concatenation( "unexpected value for inputs ", input_msg, " expecting :", signal_str_conversion(output_var, output_format), " got :", signal_str_conversion(output_signal, output_format), precision = ML_String ), severity=Assert.Failure ) output_statement.add(assert_statement) self_component = self.implementation.get_component_object() self_instance = self_component(io_map = io_map, tag = "tested_entity") test_statement = Statement() DATA_FILE_NAME = test_fname with open(DATA_FILE_NAME, "w") as data_file: # dumping column tags data_file.write("# " + " ".join(input_signal_list + output_signal_list) + "\n") def get_raw_cst_string(cst_format, cst_value): size = int((cst_format.get_bit_size() + 3) / 4) return ("{:x}").format(cst_format.get_base_format().get_integer_coding(cst_value)).zfill(size) for input_values, output_values in tc_list: # TODO; generate test data file cst_list = [] for input_name in input_signal_list: input_value = input_values[input_name] input_format = input_signals[input_name].get_precision() cst_list.append(get_raw_cst_string(input_format, input_value)) for output_name in output_signal_list: output_value = output_values[output_name] output_format = output_signals[output_name].get_precision() cst_list.append(get_raw_cst_string(output_format, output_value)) # dumping line into file data_file.write(" ".join(cst_list) + "\n") input_stream = Variable("data_file", precision=HDL_FILE, var_type=Variable.Local) file_status = Variable("file_status", precision=HDL_OPEN_FILE_STATUS, var_type=Variable.Local) FCT_EndFile = FunctionObject("endfile", [HDL_FILE], ML_Bool, FunctionOperator("endfile", arity=1)) FCT_OpenFile = FunctionObject( "FILE_OPEN", [HDL_OPEN_FILE_STATUS, HDL_FILE, ML_String], ML_Void, FunctionOperator( "FILE_OPEN", arg_map={0: FO_Arg(0), 1: FO_Arg(1), 2: FO_Arg(2), 3: "READ_MODE"}, void_function=True)) FCT_ReadLine = FunctionObject( "readline", [HDL_FILE, HDL_LINE], ML_Void, FunctionOperator("readline", void_function=True, arity=2)) reset_statement = self.get_reset_statement(io_map, time_step) OPEN_OK = Constant("OPEN_OK", precision=HDL_OPEN_FILE_STATUS) testbench = CodeEntity("testbench") test_process = Process( reset_statement, FCT_OpenFile(file_status, input_stream, DATA_FILE_NAME), ConditionBlock( Comparison(file_status, OPEN_OK, specifier=Comparison.NotEqual), Assert( Constant(0, precision=ML_Bool), " \"failed to open file {}\"".format(DATA_FILE_NAME), severity=Assert.Failure ) ), # consume legend line FCT_ReadLine(input_stream, input_line), WhileLoop( LogicalNot(FCT_EndFile(input_stream)), Statement( FCT_ReadLine(input_stream, input_line), input_statement, Wait(time_step * (self.stage_num + 2)), output_statement, ), ), # end of test Assert( Constant(0, precision = ML_Bool), " \"end of test, no error encountered \"", severity = Assert.Warning ), # infinite end loop WhileLoop( Constant(1, precision=ML_Bool), Statement( Wait(time_step * (self.stage_num + 2)), ) ) ) 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]