コード例 #1
0
def connect_quartets(quartet_a, quartet_b, direction_a_to_b):
    """
    Use the processing element connection function to conveniently connect two quartets along an axis defined by
    direction_a_to_b.

    :param quartet_a: the first quartet
    :param quartet_b: the second quartet
    :param direction_a_to_b: the cardinal direction between processing elements a and b
    """

    # Possibly something to refactor at some point.
    if direction_a_to_b == Direction.north:
        for j in range(2):
            connect_processing_elements(
                quartet_a.processing_elements[0 * 2 + j],
                quartet_b.processing_elements[1 * 2 + j], Direction.north)
    elif direction_a_to_b == Direction.east:
        for i in range(2):
            connect_processing_elements(
                quartet_a.processing_elements[i * 2 + 1],
                quartet_b.processing_elements[i * 2 + 0], Direction.east)
    elif direction_a_to_b == Direction.south:
        for j in range(2):
            connect_processing_elements(
                quartet_a.processing_elements[1 * 2 + j],
                quartet_b.processing_elements[0 * 2 + j], Direction.south)
    elif direction_a_to_b == Direction.west:
        for i in range(2):
            connect_processing_elements(
                quartet_a.processing_elements[i * 2 + 0],
                quartet_b.processing_elements[i * 2 + 1], Direction.west)
    else:
        raise SimulatorException("Invalid direction to connect two quartets.")
コード例 #2
0
    def peek(self):
        """
        Return the next item in the channel.

        :return: head of the channel's FIFO
        """

        # Perform a quick check then just return what we see.
        if self.empty:
            raise SimulatorException(
                "Attempted to peek the next value on an empty buffer.")
        return self.deque[0]
コード例 #3
0
    def dequeue(self):
        """
        Dequeue the next value on the channel. Will raise an exception if the channel is empty.

        :return: dequeued packet
        """

        # Perform a quick check then stage a packet to dequeue, and return the contents of said packet.
        if self.empty:
            raise SimulatorException(
                "Attempted to dequeue a packet from an empty buffer.")
        self.staged_dequeue = True
        self.pending = True
        return self.peek()
コード例 #4
0
    def enqueue(self, packet):
        """
        Enqueue a packet on the channel. Will raise an exception if the channel is full.

        :param packet: packet to enqueue
        """

        # Perform a quick check then stage a packet to be enqueued.
        if self.full:
            raise SimulatorException(
                "Attempted to enqueue a packet on a full buffer.")
        self.staged_enqueue = True
        self.staged_packet = packet
        self.pending = True
コード例 #5
0
    def initialize_registers(self, register_values):
        """
        Initialize registers.

        :param register_values: abstract Python integer representations of data register contents
        """

        # Check usage an perform explicit datatype conversion.
        if len(register_values) != len(self.registers):
            raise SimulatorException(
                "Register initialization data length and register file size do not match."
            )
        for i, register_value in enumerate(register_values):
            self.registers[i] = np.uint32(register_value)
コード例 #6
0
    def connect_to_receiver_channel_buffer(self, direction, receiver_channel_buffer):
        """
        Connect the router to a buffer that receives packets.

        :param direction: direction from the current processing element to the buffer
        :param receiver_channel_buffer: receiving buffer
        """

        # Make sure we are connecting to the correct type of buffer.
        if not isinstance(receiver_channel_buffer, ReceiverChannelBuffer):
            exception_string = f"Cannot connect a channel buffer interface to a {type(receiver_channel_buffer)}."
            raise SimulatorException(exception_string)

        # The buffer is now one of our immediate destinations.
        self.destination_buffers[direction] = receiver_channel_buffer
コード例 #7
0
    def connect_to_sender_channel_buffer(self, direction, sender_channel_buffer):
        """
        Connect the router to a buffer that emits packets.

        :param direction: direction from the current processing element to the buffer
        :param sender_channel_buffer: sending buffer
        """

        # Make sure we are connecting to the correct type of buffer.
        if not isinstance(sender_channel_buffer, SenderChannelBuffer):
            exception_string = f"Cannot connect a channel buffer interface to a {type(sender_channel_buffer)}."
            raise SimulatorException(exception_string)

        # The buffer is now one of our immediate sources.
        self.source_buffers[direction] = sender_channel_buffer
コード例 #8
0
    def __init__(self, name, cp, ip):
        """
        Initialize a processing element.
        
        :param name: an English name
        :param cp: a CoreParameters instance
        :param ip: an InterconnectParameters instance
        """

        # Initialize each component.
        self.name = name
        self.core = Core(name, cp)
        if ip.router_type not in router_type_name_type_map:
            raise SimulatorException("Unsupported router type.")
        self.router = router_type_map[router_type_name_type_map[ip.router_type]](ip, self.core)
コード例 #9
0
    def register(self, element):
        """
        Register a functional unit (processing element, memory, etc.) with the event loop.
        
        :param element: functional unit 
        """

        # Make sure the functional unit has a special registration method.
        registration_operation = getattr(element, "_register")
        if not callable(registration_operation):
            exception_string = f"The functional unit of type {type(element)} does not have internal system " \
                               + f"registration method."
            raise SimulatorException(exception_string)

        # Call the functional unit's internal method.
        element._register(self)
コード例 #10
0
    def connect_to_processing_element(self, direction, processing_element):
        """
        Link a router to another software router/core processing element.

        :param direction: direction from the current processing element to the neighboring processing element
        :param processing_element: neighboring processing element
        """

        # Make sure we are connecting to a processing element that is also using a software router.
        if not isinstance(processing_element.router, SoftwareRouter):
            raise SimulatorException(f"Cannot connect a {type(self)} to a {type(processing_element.router)}.")

        # Associate the two processing elements' source and destination buffers directly with their input and output
        # channel buffers.
        self.destination_buffers[direction] \
            = processing_element.core.input_channel_buffers[reverse_direction_map[direction]]
        self.source_buffers[direction] \
            = processing_element.core.output_channel_buffers[reverse_direction_map[direction]]
コード例 #11
0
    def __init__(self, name, row_base_index, column_base_index, num_columns,
                 cp, ip):
        """
        Initialize a quartet.
        
        :param name: the quartet name 
        :param row_base_index: base index for row iteration
        :param column_base_index: base index for column iteration
        :param num_columns: number of columns of processing elements in the entire array
        :param cp: a CoreParameters instance
        :param ip: an InterconnectParameters instance
        """

        # Fill in name, and instantiate underlying processing elements.
        self.name = name
        processing_element_names = []
        for i in range(row_base_index, row_base_index + 2):
            for j in range(column_base_index, column_base_index + 2):
                processing_element_index = i * num_columns + j
                processing_element_names.append(
                    f"processing_element_{processing_element_index}")
        if len(processing_element_names) != 4:
            raise SimulatorException(
                "Every processing element must have a name.")
        self.processing_elements = []
        for processing_element_name in processing_element_names:
            self.processing_elements.append(
                ProcessingElement(name=processing_element_name, cp=cp, ip=ip))

        # Wire up the processing elements.
        for i in range(2):
            for j in range(2):
                if j < 2 - 1:
                    connect_processing_elements(
                        self.processing_elements[i * 2 + j],
                        self.processing_elements[i * 2 + j + 1],
                        Direction.east)
                if i < 2 - 1:
                    connect_processing_elements(
                        self.processing_elements[i * 2 + j],
                        self.processing_elements[(i + 1) * 2 + j],
                        Direction.south)
コード例 #12
0
    def finalize(self):
        """
        Alphabetize components in the event loop for clean debug output and make sure all processing elements are
        indexed.
        """

        # The numerical strings are the ones we care about.
        def natural_number_sort_key(entity):
            name = entity.name
            key_string_list = re.findall(r"(\d+)", name)
            if len(key_string_list) > 0:
                return [int(key_string) for key_string in key_string_list]
            else:
                return []

        # Sort all the entities.
        self.processing_elements = sorted(self.processing_elements,
                                          key=natural_number_sort_key)
        for i, processing_element in enumerate(self.processing_elements):
            if processing_element.name != f"processing_element_{i}":
                exception_string = f"Missing processing element {i}."
                raise SimulatorException(exception_string)
        self.memories = sorted(self.memories, key=natural_number_sort_key)
        self.buffers = sorted(self.buffers, key=natural_number_sort_key)
コード例 #13
0
    def iterate(self, debug, keep_execution_trace):
        """
        Perform a single cycle of execution.

        :param debug: whether to print out information about internal state
        :param keep_execution_trace: whether to maintain a running log of indices of fired instructions
        """

        # Show PE state for debugging purposes if necessary.
        if debug:
            print(f"name: {self.name}")
            predicate_string_list = []
            for predicate in self.predicates:
                if predicate:
                    predicate_string_list.append("1")
                else:
                    predicate_string_list.append("0")
            predicate_string = "".join(predicate_string_list)
            predicate_string = predicate_string[::-1]
            print(f"predicates: {predicate_string}")
            print("registers:")
            register_strings = [
                f"{i:02d}: 0x{register:08x}"
                for i, register in enumerate(self.registers)
            ]
            print("\n".join(register_strings))
            print(f"number of instructions: {len(self.instructions)}")
            i = None
            valid_instruction = None
            for i, instruction in enumerate(self.instructions):
                if self.check_trigger(instruction.trigger):
                    valid_instruction = instruction
                    break
            if i is not None and valid_instruction is not None:
                print(f"valid instruction: {i}")
                print(f"triggered instruction: {valid_instruction.op.name}")
            else:
                print("valid instruction: None")
                print("triggered instruction: nop")
            print(f"halt register: {self.halt_register}")
            print(f"instructions retired: {self.instructions_retired}")
            print(f"untriggered cycles: {self.untriggered_cycles}\n")

        # Check for an halt condition.
        valid_instruction = None
        if not self.halt_register:
            # Find a valid instruction if one exists, searching the list of instructions in priority order.
            for instruction in self.instructions:
                if self.check_trigger(instruction.trigger):
                    valid_instruction = instruction
                    break

            # Execute any valid instruction.
            if valid_instruction is not None:
                # Increment the performance counter.
                self.instructions_retired += 1

                # Set the halt register if required.
                if valid_instruction.op == Op.halt:
                    self.halt_register = True

                # Get operands from their sources and perform any source actions.
                a_type, b_type, c_type = valid_instruction.source_types
                a_index, b_index, c_index = valid_instruction.source_indices
                if a_type == SourceType.immediate:
                    a = valid_instruction.immediate
                elif a_type == SourceType.channel:
                    a = self.input_channel_buffers[a_index].peek().value
                elif a_type == SourceType.register:
                    a = self.registers[a_index]
                elif a_type == SourceType.null:
                    a = 0
                else:
                    raise SimulatorException(
                        "Unknown source type for operand a.")
                if b_type == SourceType.immediate:
                    b = valid_instruction.immediate
                elif b_type == SourceType.channel:
                    b = self.input_channel_buffers[b_index].peek().value
                elif b_type == SourceType.register:
                    b = self.registers[b_index]
                elif b_type == SourceType.null:
                    b = 0
                else:
                    raise SimulatorException(
                        "Unknown source type for operand b.")
                if c_type == SourceType.immediate:
                    c = valid_instruction.immediate
                elif c_type == SourceType.channel:
                    c = self.input_channel_buffers[c_index].peek().value
                elif c_type == SourceType.register:
                    c = self.registers[c_index]
                elif c_type == SourceType.null:
                    c = 0
                else:
                    raise SimulatorException(
                        "Unknown source type for operand b.")

                # Perform operation.
                if valid_instruction.op == Op.lsw:
                    if self.scratchpad is None:
                        raise SimulatorException(
                            "Attempting to load a word in a core that has no scratchpad."
                        )
                    result = self.scratchpad[a]
                elif valid_instruction.op == Op.ssw:
                    if self.scratchpad is None:
                        raise SimulatorException(
                            "Attempting to store a word in a core that has no scratchpad."
                        )
                    self.scratchpad[b] = a
                    result = 0
                else:
                    result = op_implementation_map[valid_instruction.op](a, b,
                                                                         c)

                # Store results in the destination.
                destination_type = valid_instruction.destination_type
                destination_index = valid_instruction.destination_index
                if destination_type == DestinationType.channel:
                    result_packet = Packet(
                        valid_instruction.output_channel_tag, result)
                    for i in valid_instruction.output_channel_indices:
                        self.output_channel_buffers[i].enqueue(result_packet)
                elif destination_type == DestinationType.register:
                    self.registers[destination_index] = result
                elif destination_type == DestinationType.predicate:
                    self.predicates[destination_index] = bool(result)
                elif destination_type == DestinationType.null:
                    pass
                else:
                    raise SimulatorException("Unknown destination type.")

                # Dequeue any required input channels.
                for i in valid_instruction.input_channels_to_dequeue:
                    self.input_channel_buffers[i].dequeue()

                # Perform any PE updates necessary.
                for i, index in enumerate(
                        valid_instruction.predicate_update_indices):
                    self.predicates[
                        index] = valid_instruction.predicate_update_values[i]
            else:
                # No valid instruction this cycle.
                self.untriggered_cycles += 1

        # Internal execution trace.
        if keep_execution_trace:
            self.execution_trace.append(valid_instruction.number if
                                        valid_instruction is not None else -1)