def build_false_pum(cp, instruction): """ Build the integer representation of the false predicate update mask. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the false half of pum """ # OR in the predicates. false_pum = 0 for predicate, boolean in zip(instruction.predicate_update_indices, instruction.predicate_update_values): if predicate > cp.num_predicates: exception_string = f"Predicate {predicate} is out of range on the target architecture with " \ + f"{cp.num_predicates} predicates." raise AssemblyException(exception_string) if not boolean: false_pum |= 1 << predicate # Check sizing, and return the integer. false_pum = int(false_pum) if false_pum.bit_length() > cp.false_pum_width: raise AssemblyException("False pum exceeds its allotted bit width.") return false_pum
def build_ictb(cp, instruction): """ Build the integer representation of the input channel tag Booleans. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the ictb """ # Check sizing. if len(instruction.trigger.input_channel_tag_booleans ) > cp.max_num_input_channels_to_check: exception_string = f"The {len(instruction.trigger.input_channel_tag_booleans)} input channel tags to check " \ + f"exceed the architecture's specified maximum of {cp.max_num_input_channels_to_check}." raise AssemblyException(exception_string) # Concatenate the Booleans together. ictb = 0 for i, boolean in enumerate( reversed(instruction.trigger.input_channel_tag_booleans)): ictb |= boolean if i != len(instruction.trigger.input_channels) - 1: ictb <<= 1 # Append empty slots. num_null_slots = cp.max_num_input_channels_to_check - len( instruction.trigger.input_channels) ictb <<= num_null_slots # Check sizing, and return the integer. ictb = int(ictb) if ictb.bit_length() > cp.ictb_width: raise AssemblyException("ictb exceeds its allotted bit width.") return ictb
def build_ictv(cp, instruction): """ Build the integer representation of the input channel tag values. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the ictv """ # Check sizing. if len(instruction.trigger.input_channel_tags ) > cp.max_num_input_channels_to_check: exception_string = f"The {len(instruction.trigger.input_channel_tags)} input channel tags to check exceed " \ + f"the architecture's specified maximum of {cp.max_num_input_channels_to_check}." raise AssemblyException(exception_string) # Concatenate the values together. ictv = 0 for i, tag_value in enumerate( reversed(instruction.trigger.input_channel_tags)): ictv |= tag_value if i != len(instruction.trigger.input_channel_tags) - 1: ictv <<= cp.tag_width # Append empty slots. num_null_slots = cp.max_num_input_channels_to_check - len( instruction.trigger.input_channel_tags) ictv <<= num_null_slots * cp.tag_width # Check sizing, and return the integer. ictv = int(ictv) if ictv.bit_length() > cp.ictv_width: raise AssemblyException("ictv exceeds its allotted bit width.") return ictv
def build_ici(cp, instruction): """ Build the integer representation of the input channel indices. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the ici """ # Check sizing. if len(instruction.trigger.input_channels ) > cp.max_num_input_channels_to_check: exception_string = f"The {len(instruction.trigger.input_channels)} input channels to check exceed the " \ + f"architecture's specified maximum of {cp.max_num_input_channels_to_check}." raise AssemblyException(exception_string) # Concatenate the indices together. ici = 0 for i, input_channel in enumerate( reversed(instruction.trigger.input_channels)): ici |= input_channel + 1 # Recall that a zero-filled ici slot implies a null value. if i != len(instruction.trigger.input_channels) - 1: ici <<= cp.single_ici_width # Append empty slots. num_null_slots = cp.max_num_input_channels_to_check - len( instruction.trigger.input_channels) ici <<= num_null_slots * cp.single_ici_width # Check sizing and return the integer. ici = int(ici) if ici.bit_length() > cp.ici_width: raise AssemblyException("ici exceeds its allotted bit width.") return ici
def build_si(cp, instruction): """ Build the integer representation of the source indices. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of si """ # Check sizing. if len(instruction.source_indices) > 3: exception_string = f"The {len(instruction.source_indices)} sources exceed the architecture's specified " \ + f"maximum of 3." raise AssemblyException(exception_string) # Concatenate the values together. si = 0 num_source_indices = len(instruction.source_indices) if num_source_indices > 2: if instruction.source_indices[2] != 0: si |= instruction.source_indices[2] si <<= cp.single_si_width if num_source_indices > 1: if instruction.source_indices[1] != 0: si |= instruction.source_indices[1] si <<= cp.single_si_width if num_source_indices > 0: if instruction.source_indices[0] != 0: si |= instruction.source_indices[0] # Check sizing, and return the integer. si = int(si) if si.bit_length() > cp.si_width: raise AssemblyException("si exceeds its allotted bit width.") return si
def convert_processing_element_instructions_string_to_instructions( processing_element_instructions_string): """ Process an assembly string for an individual PE into a set of instructions. :param processing_element_instructions_string: multiline string containing an assembly program (instructions only) :return: the corresponding list of instructions """ # Remove comments (MIPS-style comments). processing_element_instructions_string = re.sub( r"#.*", "", processing_element_instructions_string) # Break the program into instruction strings. program_string_lines = processing_element_instructions_string.split( '\n') instruction_strings = [] i = 0 while i < len(program_string_lines): line = program_string_lines[i].strip() if line == "": i += 1 elif line.endswith(':'): instruction_strings.append(program_string_lines[i] + program_string_lines[i + 1]) i += 2 elif ':' in line: instruction_strings.append(program_string_lines[i]) i += 1 else: exception_string = f"Unexpected statement: {line} on source line {i}." raise AssemblyException(exception_string) # Build the list of instructions. instructions = [] for i, instruction_string in enumerate(instruction_strings): try: instruction = Instruction.from_string(instruction_string) instruction.number = i instructions.append(instruction) except Exception as e: instruction_string = " ".join( instruction_string.split()).strip() exception_string = f"Error encountered in assembling instruction {i}: {instruction_string} --> {str(e)}" raise AssemblyException(exception_string) # Return the completed list of instructions. return instructions
def build_immediate(cp, instruction): """ Build the integer representation of the immediate. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: the integer representation of the immediate. """ # Make sure we actually have an immediate value, and if not, return zero. if instruction.immediate is None: return 0 # Check sizing. if instruction.immediate.bit_length() > cp.immediate_width: raise AssemblyException( "The immediate exceeds its allotted bit width.") # Unsigned conversion and masking. unsigned = int(np.uint32( instruction.immediate)) # max immediate is 32 bits. mask = 0 for i in range(cp.immediate_width): mask |= 1 if i != cp.immediate_width - 1: mask <<= 1 masked = unsigned & mask # Return the masked value. return masked
def build_true_ptm(cp, instruction): """ Build the integer representation of the true predicate trigger mask. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the true half of the ptm """ # OR in the predicates. true_ptm = 0 for predicate in reversed(instruction.trigger.true_predicates): if predicate > cp.num_predicates: exception_string = f"Predicate {predicate} is out of range on the target architecture with " \ + f"{cp.num_predicates} predicates." raise AssemblyException(exception_string) true_ptm |= 1 << predicate # Check sizing, and return the integer. true_ptm = int(true_ptm) if true_ptm.bit_length() > cp.true_ptm_width: raise AssemblyException("True ptm exceeds its allotted bit width.") return true_ptm
def build_dt(cp, instruction): """ Build the integer representation of destination type. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of dt """ # Should already have the correct value. dt = instruction.destination_type # Check sizing, and return the integer. dt = int(dt) if dt.bit_length() > cp.di_width: raise AssemblyException("dt exceeds its allotted bit width.") return dt
def build_op(cp, instruction): """ Build the integer representation of a datapath operation. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of op """ # Should already have the correct value. op = instruction.op # Check sizing, and return the integer. op = int(op) if op.bit_length() > cp.op_width: raise AssemblyException("op exceeds its allotted bit width.") return op
def build_pum(cp, instruction): """ Build the integer representation of the concatenated predicate update mask. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of the pum """ # Concatenate the two pum halves. pum = build_true_pum(cp, instruction) << cp.false_ptm_width pum |= build_false_pum(cp, instruction) # Check sizing, and return the integer. pum = int(pum) if pum.bit_length() > cp.ptm_width: raise AssemblyException("pum exceeds its allotted bit width.") return pum
def extract_register_initialization_data_from_statement(statement): """ Extract the register index and register data from an initialization statement. :param statement: "set %r5 = 0x1234;", for example :return: the index and data as integers """ # Strip the statement of comments and whitespace. statement = re.sub(r"#.*", "", statement) statement = statement.strip() # Extract the register index. register_index_pattern = r"init\s*%r(\d+)\s*,.*" match = re.match(register_index_pattern, statement) if match is None: exception_string = f"Invalid register initialization statement: {statement}." raise AssemblyException(exception_string) try: register_index_string = match.group(1) except IndexError: exception_string = f"Invalid register initialization statement: {statement}." raise AssemblyException(exception_string) try: register_index = int(register_index_string) except ValueError: exception_string = f"Invalid register index in initialization statement: {statement}." raise AssemblyException(exception_string) # Extract the register data. register_data_pattern = r"init\s*%r\d+\s*,\s*\$(-*\d*|-*0x\d*);" match = re.match(register_data_pattern, statement) if match is None: exception_string = f"Invalid register initialization statement: {statement}." raise AssemblyException(exception_string) try: register_data_string = match.group(1) except IndexError: exception_string = f"Invalid register initialization statement: {statement}." raise AssemblyException(exception_string) if "0x" in register_data_string: base = 16 else: base = 10 try: register_data = int(register_data_string, base=base) except ValueError: exception_string = f"Invalid register data in initialization statement: {statement}." raise AssemblyException(exception_string) # Return the extracted information. return register_index, register_data
def build_program_binary(cp, program): """ Convert the program to a list of 32-bit words to be written out to disk or programmed into the hardware. :param cp: CoreParameters instance for the target architecture :param program: a ProcessingElementProgram instance :return: the register initialization settings and the instructions as two lists of 32-bit words """ # Generate register initialization data as 32-bit words. register_words = [] for register_value in program.register_values: unsigned = int(np.uint32(register_value)) # max word size is 32 bits. mask = 0 for i in range(cp.device_word_width): mask |= 1 if i != cp.device_word_width - 1: mask <<= 1 masked = unsigned & mask register_words.append(np.uint32(masked)) # Machine code instructions are sliced into individual 32-bit words. if cp.mm_instruction_width % 32 != 0: raise AssemblyException( "Memory-mapped instructions must be in multiples of 32-bit words.") mm_instruction_word_width = int(cp.mm_instruction_width / 32) assembled_instructions = [ build_machine_code_instruction(cp, instruction) for instruction in program.instructions ] instruction_words = [] for assembled_instruction in assembled_instructions: for i in range(mm_instruction_word_width): instruction_words.append((assembled_instruction >> i * 32) & 0xffffffff) # Append empty instructions to fill out remaining instruction memory. if len(assembled_instructions) < cp.num_instructions: num_empty_instructions = cp.num_instructions - len( assembled_instructions) for _ in range(num_empty_instructions): instruction_words += [0] * mm_instruction_word_width # Return the two lists. return register_words, instruction_words
def validate(self): """ Validate the register file values and instructions in a program against architectural parameters. """ # Validate register values. for i, register_value in enumerate(self.register_values): if register_value < 0: effective_bit_length = register_value.bit_length() + 1 else: effective_bit_length = register_value.bit_length() if effective_bit_length > self.cp.device_word_width: exception_string = f"In program {self.label}, register {i} initialized to too wide of a value for " \ + f"this architecture: {register_value}" raise AssemblyException(exception_string) # Will raise an exception if needed. for instruction in self.instructions: instruction.validate(self.cp)
def build_di(cp, instruction): """ Build the integer representation of destination index. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of di """ # If it exists, again we have to increment it by one to show it is not void. if instruction.destination_index is not None: di = instruction.destination_index else: di = 0 # Check sizing, and return the integer. di = int(di) if di.bit_length() > cp.di_width: raise AssemblyException("di exceeds its allotted bit width.") return di
def build_oct(cp, instruction): """ Build the integer representation of the destination tag. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of oct """ # If it exists, again we have to increment it by one to show it is not void. if instruction.output_channel_tag is not None: oct = instruction.output_channel_tag else: oct = 0 # Check sizing, and return the integer. oct = int(oct) if oct.bit_length() > cp.oct_width: raise AssemblyException("oct exceeds its allotted bit width.") return oct
def build_oci(cp, instruction): """ Build the integer representation of the output channel indices array. :param cp: CoreParameters instance of the target architecture. :param instruction: Instruction instance :return: integer representation of oci """ # OR in the output channels. oci = 0 for output_channel in reversed(range(cp.num_output_channels)): if output_channel in instruction.output_channel_indices: oci |= 1 if output_channel != 0: oci <<= 1 # Check sizing, and return the integer. oci = int(oci) if oci.bit_length() > cp.oci_width: raise AssemblyException("oci exceeds its allotted bit width.") return oci
def build_icd(cp, instruction): """ Build the integer representation of the input channels to dequeue. :param cp: CoreParameters instance for the target architecture :param instruction: Instruction instance :return: integer representation of icd """ # OR in the literal array of bits. icd = 0 for input_channel in reversed(range(cp.num_input_channels)): if input_channel in instruction.input_channels_to_dequeue: icd |= 1 if input_channel != 0: icd <<= 1 # Check sizing, and return the integer. icd = int(icd) if icd.bit_length() > cp.icd_width: raise AssemblyException("icd exceeds its allotted bit width.") return icd
def from_label_and_string(cls, cp, label, processing_element_program_string): """ Build from a processing element label and register-file initialization/instruction string. :param cp: a CoreParameters instance :param label: processing element label :param processing_element_program_string: register setting statements and instructions :return: an initialized instance """ # Build fields manually. try: register_string, instruction_string = \ ProcessingElementProgram.split_into_register_and_instruction_strings(processing_element_program_string) register_values = \ ProcessingElementProgram.convert_register_initialization_statements_to_register_values(cp, register_string) instructions = \ ProcessingElementProgram.convert_processing_element_instructions_string_to_instructions(instruction_string) except AssemblyException as e: exception_string = f"Error parsing program {label}: {str(e)}" raise AssemblyException(exception_string) return cls(cp, label, register_values, instructions)