Using the data stored in the CommandQueue, Extract and align compatible sequences of Primitives and compile/optimize the Primitives down into a stream of Level 2 device agnostic primitives.

        BACKGROUND:
        Device Specific primitives present a special opportunity for
        optimization. Many JTAG systems program one device on the
        chain at a time. But because all devices on a JTAG chain are
        sent information at once, NO-OP instructions are sent to these
        other devices.

        When programming multiple devices, Sending these NO-OPS is a
        missed opportunity for optimization. Instead of configuring
        one device at a time, it is more efficient to collect
        instructions for all deices, and align them so multiple
        devices can be configured at the same time.

        WAT THIS METHOD DOES:
        This method takes in a list of Primitives, groups the device
        specific primitives by target device, aligns the sequences of
        device instructions, and expands the aligned sequences into a
        flat list of device agnostic primitives.

        Args:
            debug: A boolean for if debug information should be generated.
            stages: A list to be edited by this method to store snapshots of the compilation state. Used if debug is True.
            stagenames: A list of strings describing each debug snapshot of the compiilation process. Used if debug is True.

def _compile_device_specific_prims(self, debug=False,
                                       stages=None, stagenames=None):
        """Using the data stored in the CommandQueue, Extract and align compatible sequences of Primitives and compile/optimize the Primitives down into a stream of Level 2 device agnostic primitives.

        BACKGROUND:
        Device Specific primitives present a special opportunity for
        optimization. Many JTAG systems program one device on the
        chain at a time. But because all devices on a JTAG chain are
        sent information at once, NO-OP instructions are sent to these
        other devices.

        When programming multiple devices, Sending these NO-OPS is a
        missed opportunity for optimization. Instead of configuring
        one device at a time, it is more efficient to collect
        instructions for all deices, and align them so multiple
        devices can be configured at the same time.

        WAT THIS METHOD DOES:
        This method takes in a list of Primitives, groups the device
        specific primitives by target device, aligns the sequences of
        device instructions, and expands the aligned sequences into a
        flat list of device agnostic primitives.

        Args:
            debug: A boolean for if debug information should be generated.
            stages: A list to be edited by this method to store snapshots of the compilation state. Used if debug is True.
            stagenames: A list of strings describing each debug snapshot of the compiilation process. Used if debug is True.

        """
        ############### GROUPING BY EXEC BOUNDARIES!################

        fences = []
        fence = [self[0]]
        for p in self[1:]:
            if type(fence[0])._layer == type(p)._layer and\
               isinstance(fence[0], DeviceTarget) == \
                  isinstance(p, DeviceTarget):
                fence.append(p)
            else:
                fences.append(fence)
                fence = [p]
        fences.append(fence)

        if debug: #pragma: no cover
            formatted_fences = []
            for fence in fences:
                formatted_fence = [p.snapshot() for p in fence]
                formatted_fences.append(formatted_fence)
                formatted_fences.append([])
            stages.append(formatted_fences[:-1]) #Ignore trailing []
            stagenames.append("Fencing off execution boundaries")

        ############## SPLIT GROUPS BY DEVICE TARGET! ##############

        split_fences = []
        for fence in fences:
            tmp_chains = {}
            for p in fence:
                k = p._device_index \
                    if isinstance(p, DeviceTarget) else "chain"
                subchain = tmp_chains.setdefault(k, []).append(p)
            split_fences.append(list(tmp_chains.values()))

        if debug:#pragma: no cover
            formatted_split_fences = []
            for fence in split_fences:
                for group in fence:
                    formatted_split_fences.append([p.snapshot()
                                                   for p in group])
                formatted_split_fences.append([])
            stages.append(formatted_split_fences[:-1])
            stagenames.append("Grouping prims of each boundary by "
                              "target device")

        ############## ALIGN SEQUENCES AND PAD FRAMES ##############
        #FIRST DEV REQUIRED LINE
        grouped_fences = [
            FrameSequence(self._chain, *fence).finalize()
            for f_i, fence in enumerate(split_fences)
        ]

        if debug:#pragma: no cover
            formatted_grouped_fences = []
            for fence in grouped_fences:
                formatted_grouped_fences += fence.snapshot() + [[]]
            stages.append(formatted_grouped_fences[:-1])
            stagenames.append("Aligning and combining each group dev "
                              "prim stream")

        ################## RECOMBINE FRAME GROUPS ##################

        ingested_chain = grouped_fences[0]
        for fence in grouped_fences[1:]:
            ingested_chain += fence

        if debug:#pragma: no cover
            stages.append(ingested_chain.snapshot())
            stagenames.append("Recombining sanitized exec boundaries")

        ###################### POST INGESTION ######################
        ################ Flatten out LV3 Primitives ################
        while(any((f._layer == 3 for f in ingested_chain))):
            ################# COMBINE COMPATIBLE PRIMS #################
            ingested_chain = _merge_prims(ingested_chain)

            if debug:#pragma: no cover
                stages.append(ingested_chain.snapshot())
                stagenames.append("Combining compatible lv3 prims.")

            ################ TRANSLATION TO LOWER LAYER ################
            sm = JTAGStateMachine(self._chain._sm.state)
            expanded_prims = FrameSequence(self._chain)
            for f in ingested_chain:
                if f._layer == 3:
                    expanded_prims += f.expand_macro(sm)
                else:
                    expanded_prims.append(f)
            expanded_prims.finalize()
            ingested_chain = expanded_prims

            if self._fsm is None:
                self._fsm = sm
            assert self._fsm == sm, "Target %s != Actual %s"%\
                (self._fsm.state, sm.state)

            if debug:#pragma: no cover
                stages.append(ingested_chain.snapshot())
                stagenames.append("Expanding lv3 prims")


        ############## Flatten out Dev LV2 Primitives ##############
        while(any((isinstance(f._valid_prim, DeviceTarget)
                   for f in ingested_chain))):
            ################# COMBINE COMPATIBLE PRIMS #################
            ingested_chain = _merge_prims(ingested_chain)

            if debug:#pragma: no cover
                stages.append(ingested_chain.snapshot())
                stagenames.append("Merging Device Specific Prims")

            ################ TRANSLATION TO LOWER LAYER ################

            sm = JTAGStateMachine(self._chain._sm.state)
            expanded_prims = FrameSequence(self._chain)
            for f in ingested_chain:
                if issubclass(f._prim_type, DeviceTarget):
                    expanded_prims += f.expand_macro(sm)
                else:
                    f[0].apply_tap_effect(sm)
                    expanded_prims.append(f)
            expanded_prims.finalize()
            ingested_chain = expanded_prims
            if self._fsm is None:
                self._fsm = sm
            assert self._fsm == sm, "Target %s != Actual %s"%\
                 (self._fsm.state, sm.state)

            if debug:#pragma: no cover
                stages.append(ingested_chain.snapshot())
                stagenames.append("Expanding Device Specific Prims")

        ############ Convert FrameSequence to flat array ###########
        flattened_prims = [f._valid_prim for f in ingested_chain]
        if debug:#pragma: no cover
            stages.append([[p.snapshot() for p in flattened_prims]])
            stagenames.append("Converting format to single stream.")

        return flattened_prims