def make_array(self, expr: Array): # make the output signal that will hold the results output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) # compile the array elements and address to signals elements = [self.expr_to_signal(element) for element in expr.elements] address = self.expr_to_signal(expr.address) # extra step for analog signals -- they must be aligned to the output format if isinstance(expr.format_, RealFormat): # rename the elements list to indicate these values are not aligned to the output format non_aligned_elements = elements # created an "aligned" version of each signal elements = [Signal(name=next(self.namer), format_=expr.format_) for _ in range(len(expr))] for element, non_aligned_element in zip(elements, non_aligned_elements): self.make_signal(element) self.make_assign(input_=non_aligned_element, output=element) # now create the array case_statment(gen=self, sel=address.name, var=output.name, values=signal_names(elements), default=0) # and last return the output signal return output
def make_constant_array_mul_signal(self, expr: Product): # figure out which operand is the constant array and which is the signal (note: if both are constant arrays, # that is not handled in a special fashion. although that shouldn't usually occur due to the way that # expressions are built up.) if isinstance(expr.operands[0], Array) and expr.operands[0].all_constants: constant_array = expr.operands[0] signal = self.expr_to_signal(expr.operands[1]) elif isinstance(expr.operands[1], Array) and expr.operands[1].all_constants: constant_array = expr.operands[1] signal = self.expr_to_signal(expr.operands[0]) else: raise Exception(f'This expression does not represent a constant array times a signal: {expr}.') # create the output signal output = Signal(name=next(self.namer), format_=expr.format_) # create a short real variable to be assigned the selected value from the array array_name = next(self.namer) self.macro_call('MAKE_SHORT_REAL', array_name, compile_range_expr(constant_array.format_.range_)) # perform the multiplication self.macro_call('MUL_REAL', array_name, signal.name, output.name) # compile the array address to a signal address = self.expr_to_signal(constant_array.address) # compute the valuess to go in the array values = [f'`FROM_REAL({element.value}, {array_name})' for element in constant_array.elements] # create the array itself case_statment(gen=self, sel=address.name, var=array_name, values=values, default=0) # return the output signal return output
def make_constant(self, expr: Constant): output = Signal(name=next(self.namer), format_=expr.format_) if isinstance(expr.format_, RealFormat): # compile the range, width, and exponent expressions. if isinstance(expr.value, Integral): # avoid a synthesis corner-case: # https://forums.xilinx.com/t5/Synthesis/Possible-synthesis-bug-casting-integer-to-real-in-a-function/td-p/1140910 const = str(float(expr.value)) else: const = str(expr.value) range = compile_range_expr(expr.format_.range_) width = compile_width_expr(expr.format_.width) exponent = compile_exponent_expr(expr.format_.exponent) # call the appropriate macro if width is None: self.macro_call('MAKE_CONST_REAL', const, output.name) elif exponent is None: self.macro_call('MAKE_GENERIC_CONST_REAL', const, output.name, width) else: self.macro_call('MAKE_FORMAT_REAL', output.name, range, width, exponent) self.macro_call('ASSIGN_CONST_REAL', const, output.name) elif isinstance(expr.format_, IntFormat): self.make_signal(output) self.digital_assignment(output, expr.value) else: raise ValueError(f'Unknown expression format type: ' + expr.format_.__class__.__name__) return output
def make_constant_mul_signal(self, expr: Product): # figure out which operand is the constant and which is the signal (note: if both are constants, that is # not handled in a special fashion. although that shouldn't usually occur due to the way that expressions # are built up.) if isinstance(expr.operands[0], Constant): constant = expr.operands[0] signal = self.expr_to_signal(expr.operands[1]) elif isinstance(expr.operands[1], Constant): constant = expr.operands[1] signal = self.expr_to_signal(expr.operands[0]) else: raise Exception(f'This expression does not represent a constant times a signal: {expr}.') # create the output signal output = Signal(name=next(self.namer), format_=expr.format_) # call the special multiplication macro, avoiding a synthesis corner-case: # https://forums.xilinx.com/t5/Synthesis/Possible-synthesis-bug-casting-integer-to-real-in-a-function/td-p/1140910 if isinstance(constant.value, Integral): # avoid a synthesis corner-case: # https://forums.xilinx.com/t5/Synthesis/Possible-synthesis-bug-casting-integer-to-real-in-a-function/td-p/1140910 const_as_str = str(float(constant.value)) else: const_as_str = str(constant.value) self.macro_call('MUL_CONST_REAL', const_as_str, signal.name, output.name) # return the output signal return output
def make_constant(self, expr: Constant): output = Signal(name=next(self.namer), format_=expr.format_) if isinstance(expr.format_, RealFormat): # compile the range, width, and exponent expressions const = str(expr.value) range = compile_range_expr(expr.format_.range_) width = compile_width_expr(expr.format_.width) exponent = compile_exponent_expr(expr.format_.exponent) # call the appropriate macro if width is None: self.macro_call('MAKE_CONST_REAL', const, output.name) elif exponent is None: self.macro_call('MAKE_GENERIC_CONST_REAL', const, output.name, width) else: self.macro_call('MAKE_FORMAT_REAL', output.name, range, width, exponent) self.macro_call('ASSIGN_CONST_REAL', const, output.name) elif isinstance(expr.format_, IntFormat): self.make_signal(output) self.digital_assignment(output, expr.value) else: raise ValueError(f'Unknown expression format type: ' + expr.format_.__class__.__name__) return output
def make_bitwise_inv(self, expr: BitwiseInv): output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) input_ = self.expr_to_signal(expr.operand) value = f'~{input_.name}' self.digital_assignment(signal=output, value=value) return output
def make_concatenation(self, expr: Concatenate): output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) inputs_ = [self.expr_to_signal(operand) for operand in expr.operands] value = '{' + ', '.join(signal_names(inputs_)) + '}' self.digital_assignment(signal=output, value=value) return output
def make_arithmetic_shift(self, expr: ArithmeticShift): output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) input_ = self.expr_to_signal(expr.operand) value = f'{input_.name} {SHIFT_OP[type(expr)]} {expr.shift}' self.digital_assignment(signal=output, value=value) return output
def make_bitwise_operator(self, expr: BitwiseOperator): output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) inputs = [self.expr_to_signal(operand) for operand in expr.operands] value = BITWISE_OP[type(expr)].join(signal_names(inputs)) self.digital_assignment(signal=output, value=value) return output
def make_bitwise_access(self, expr: BitwiseAccess): output = Signal(name=next(self.namer), format_=expr.format_) self.make_signal(output) input_ = self.expr_to_signal(expr.operand) value = f'{input_.name}[{expr.msb}:{expr.lsb}]' self.digital_assignment(signal=output, value=value) return output
def make_type_conversion(self, expr: TypeConversion): # make the output signal output = Signal(name=next(self.namer), format_=expr.format_) # compile the input expression to a signal input_ = self.expr_to_signal(expr.operand) # handle the various cases if isinstance(expr, SIntToReal): self.macro_call('INT_TO_REAL', input_.name, str(input_.format_.width), output.name) elif isinstance(expr, RealToSInt): self.macro_call('REAL_TO_INT', input_.name, str(output.format_.width), output.name) elif isinstance(expr, UIntToSInt): # sanity check assert output.format_.width >= input_.format_.width+1, \ f'Output SInt width ({output.format_.width}) is not at least one greater than the input UInt width ({input_.format_.width}).' # make the output signal self.make_signal(output) # construct string representation of new SInt num_zeros = output.format_.width - input_.format_.width value = f'{{{self.zero_const(num_zeros)}, {input_.name}}}' # make the assignment self.digital_assignment(signal=output, value=value, comment='UInt -> SInt') elif isinstance(expr, SIntToUInt): # sanity check assert output.format_.width >= input_.format_.width-1, \ f'Output UInt width ({output.format_.width}) is not at least one less than the input SInt width ({input_.format_.width}).' # make the output signal self.make_signal(output) # then trim off the sign bit value = f'{input_.name}[{input_.format_.width-2}:0]' # pad with zeros if necessary num_zeros = output.format_.width - (input_.format_.width - 1) if num_zeros > 0: value = f'{{{self.zero_const(num_zeros)}, {value}}}' # make the assignment self.digital_assignment(signal=output, value=value, comment='SInt -> UInt') else: raise ValueError( f'Unknown type conversion: {expr.__class__.__name__}') return output
def make_random_integer(self, expr: RandomInteger): # validate input assert expr.format_.width == 32, 'Only width 32 is supported at this time.' # set defaults if expr.clk is None: clk = '`CLK_MSDSL' elif isinstance(expr.clk, str): clk = expr.clk else: clk = expr.clk.name if expr.rst is None: rst = '`RST_MSDSL' elif isinstance(expr.rst, str): rst = expr.rst else: rst = expr.rst.name if expr.cke is None: cke = "1'b1" elif isinstance(expr.cke, str): cke = expr.cke else: cke = expr.cke.name if expr.seed is None: seed = random.randint(0, (1<<expr.format_.width)-1) seed = self.hex_format(seed, expr.format_.width) elif isinstance(expr.seed, Integral): assert 0 <= expr.seed <= ((1<<expr.format_.width)-1), \ 'Seed is out of range.' seed = self.hex_format(expr.seed, expr.format_.width) elif isinstance(expr.seed, str): seed = expr.seed else: seed = expr.seed.name # determine the parameters of the output signal name = next(self.namer) output = Signal(name=name, format_=expr.format_) # assign the result if isinstance(expr, MT19937): self.macro_call('MT19937', clk, rst, cke, seed, output.name) elif isinstance(expr, LCG): self.macro_call('LCG_MSDSL', clk, rst, cke, seed, output.name) else: raise Exception(f'Unsupported expression: {expr}') # return the resulting signal return output
def make_compress_uint(self, expr: CompressUInt): # compile the input to a signal input_ = self.expr_to_signal(expr.operand) # determine the parameters of the output signal name = next(self.namer) format_ = expr.format_ output = Signal(name=name, format_=format_) # assign the result self.macro_call('COMPRESS_UINT', input_.name, str(input_.format_.width), output.name) # return the resulting signal return output
def make_comparison_operator(self, expr: ComparisonOperator): output = Signal(name=next(self.namer), format_=expr.format_) lhs = self.expr_to_signal(expr.lhs) rhs = self.expr_to_signal(expr.rhs) if isinstance(lhs.format_, RealFormat) and isinstance(rhs.format_, RealFormat): self.macro_call(REAL_COMP_OP[type(expr)], lhs.name, rhs.name, output.name) elif isinstance(lhs.format_, IntFormat) and isinstance(rhs.format_, IntFormat): # make the output signal self.make_signal(output) # assign the output signal value = f"({lhs} {INT_COMP_OP[type(expr)]} {rhs}) ? 1'b1 : 1'b0" self.digital_assignment(signal=output, value=value) else: raise ValueError(f'Unknown format type combination for LHS and RHS: {lhs.format_.__class__.__name__} and {rhs.format_.__class__.__name__}.') return output
def set_this_cycle(self, signal: Union[Signal, str], expr: ModelExpr): """ The behavior of this function is essentially a blocking assignment (in Verilog nomenclature). The provided expression is continuously written to the provided signal. :param signal: Signal object being assigned :param expr: Value of the expression to assign :return: """ if isinstance(signal, str): expr = wrap_constant(expr) signal = self.add_signal(Signal(name=signal, format_=expr.format_)) assignment_cls = BindingAssignment elif isinstance(signal, Signal): assignment_cls = ThisCycleAssignment else: raise Exception(f'Invalid signal type: {type(signal)}.') return self.add_assignment(assignment_cls(signal=signal, expr=expr))
def operator(a, b): # determine the parameters of the output signal name = next(self.namer) format_ = expr.function(a.format_, b.format_) # create the output signal c = Signal(name=name, format_=format_) # assign the result if isinstance(expr.format_, RealFormat): self.macro_call(REAL_ARITH_OP[type(expr)], a.name, b.name, c.name) elif isinstance(expr.format_, IntFormat): self.make_signal(c) value = INT_ARITH_OP[type(expr)](a.name, b.name) self.digital_assignment(signal=c, value=value) else: raise Exception(f'Unknown expression format type: {expr.format_.__class__.__name__}') # return the output signal return c
def make_constant_mul_signal(self, expr: Product): # figure out which operand is the constant and which is the signal (note: if both are constants, that is # not handled in a special fashion. although that shouldn't usually occur due to the way that expressions # are built up.) if isinstance(expr.operands[0], Constant): constant = expr.operands[0] signal = self.expr_to_signal(expr.operands[1]) elif isinstance(expr.operands[1], Constant): constant = expr.operands[1] signal = self.expr_to_signal(expr.operands[0]) else: raise Exception(f'This expression does not represent a constant times a signal: {expr}.') # create the output signal output = Signal(name=next(self.namer), format_=expr.format_) # call the special multiplication macro self.macro_call('MUL_CONST_REAL', str(constant.value), signal.name, output.name) # return the output signal return output