Skip to content

Commit

Permalink
simplifying how the TDM sequence is built
Browse files Browse the repository at this point in the history
this removes the extra NOPs, but I'm not sure whether
the labels are correct, and doesn't work for multiple
qubits, and is an awful hack
  • Loading branch information
Daniel Ellard committed Jan 22, 2018
1 parent ceea4df commit fd595e3
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 108 deletions.
7 changes: 6 additions & 1 deletion QGL/Compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ def compile_to_hardware(seqs,
for wire in old_wire_instrs.keys():
wire.instrument = old_wire_instrs[wire]

# FIXME: a one-off hack for the TDM
for s in physWires.keys():
if s.label == 'BBNAPS1-12':
# print('----- %s' % str(s))
tdm_i = pattern_module.tdm_instructions(physWires[s][0])

This comment has been minimized.

Copy link
@dieris

dieris Jan 24, 2018

Collaborator

@dellard I understand it's a hack, but still I'm not sure how it works. The TDM shouldn't have the tx_channels key, so pattern_module can't be defined here. Instead of a label, can we search for the instrument type TDM? It's already defined as a subclass of APS2 in Auspex

This comment has been minimized.

Copy link
@dellard

dellard Jan 29, 2018

Contributor

We don't expect that the TDM has a tx_channel, but we need to build the TDM instruction stream from the instruction stream of AWGs, which do.

Maybe this will help explain what's happening here: for the APS channels, first we create the instructions as normal (with a few changes for the extensions to the APS instructions). Later, we use one of the APS instruction streams to construct the TDM instructions. We need this to be one of the AWG channels, and we need to select only one.

So the main hack is that we're assuming that 'BBNAPS1-12' is the name of a good channel to use. We should have a better way to chose.

This comment has been minimized.

Copy link
@dieris

dieris Jan 29, 2018

Collaborator

Thanks, I think I understand the idea, but I am still missing some details.

  1. When writing TDM instructions at this point,

    QGL/QGL/n.py

    Line 68 in 494f00a

    aps_metafile = compile_to_hardware([copy.copy(seq)], '/tmp/aps')
    how does 'BBNAPS1-12' - an APS2 channel - know that it should use APS2TDMPattern as translator? APS2Pattern doesn't have the function tdm_instructions.
  2. If TDM instructions are not written here yet, how are _TDM_INSTRUCTIONS already available here?

    QGL/QGL/n.py

    Line 69 in 494f00a

    tdm_instr = QGL.drivers.APS2TDMPattern.get_tdm_instructions()

# Return the filenames we wrote
return metafilepath

Expand Down Expand Up @@ -546,7 +552,6 @@ def compile_sequence(seq, channels=None):
continue
# propagate frame change from nodes to edges
for chan in channels:
print('chan %s channels %s' % (str(chan), str(channels)))
if block.pulses[chan].frameChange == 0:
continue
if chan in ChannelLibraries.channelLib.connectivityG.nodes():
Expand Down
33 changes: 0 additions & 33 deletions QGL/ControlFlow.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,36 +217,3 @@ def __str__(self):
base = "BARRIER({0})".format(self.chanlist)
return base

class CustomInstruction(ControlInstruction):

def __init__(self, name, in_addr, out_addr, **kwargs):
super(CustomInstruction, self).__init__(name)
self.in_addr = in_addr
self.out_addr = out_addr
self.args = kwargs

def MajorityVote(in_addr, out_addr):
return CustomInstruction('MAJORITY', in_addr, out_addr)

def MajorityMask(in_addr, out_addr):
return CustomInstruction('MAJORITYMASK', in_addr, out_addr)

class WriteAddrInstruction(ControlInstruction):

def __init__(self, name, channel, modifier, addr, value, **kwargs):
super(WriteAddrInstruction, self).__init__(name)
self.xchannel = channel
self.invalid = modifier
self.addr = addr
self.value = value

def Invalidate(addr, mask, channel=None):
return WriteAddrInstruction('INVALIDATE', channel, 1, addr, mask)

def WriteAddr(addr, value, channel=None):
return WriteAddrInstruction('WRITEADDR', channel, 0, addr, value)

def StoreMeas(addr, value, channel=None):
return WriteAddrInstruction('STOREMEAS', channel, 5, addr, value)

# TODO: the rest of the CUSTOM instructions
164 changes: 99 additions & 65 deletions QGL/drivers/APS2TDMPattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from QGL import Compiler, ControlFlow, BlockLabel, PatternUtils
from QGL.PatternUtils import hash_pulse, flatten
from QGL import TdmInstructions
from QGL import PulseSequencer

# Python 2/3 compatibility: use 'int' that subclasses 'long'
from builtins import int
Expand Down Expand Up @@ -295,7 +296,7 @@ def __str__(self):
self.payload & 0xffff,
(self.payload >> 16) & 0xffffffff)
else:
out += ' WriteAddr(addr=0x%x, value=0x%x' % (
out += ' WriteAddr(addr=0x%x, value=0x%x)' % (
self.payload & 0xffff,
(self.payload >> 16) & 0xffffffff)

Expand Down Expand Up @@ -678,8 +679,6 @@ def synchronize_clocks(seqs):
instr.startTime += instr.length
instr.length = 0

_TDM_INSTRUCTIONS = None

def pad_with_nops(list1, list2):
"""
Given two lists of instructions, pad the shorter until they
Expand Down Expand Up @@ -710,9 +709,6 @@ def create_seq_instructions(seqs, offsets):
all waveform and marker channels.
'''

global _TDM_INSTRUCTIONS
_TDM_INSTRUCTIONS = list()

# timestamp all entries before filtering (where we lose time information on control flow)
for seq in seqs:
timestamp_entries(seq)
Expand Down Expand Up @@ -744,7 +740,6 @@ def create_seq_instructions(seqs, offsets):
timeTuples.pop(0)
indexes[first_non_empty] += 1
instructions.append(Sync(label=label))
_TDM_INSTRUCTIONS.append(Sync(label=label))
label = None

while len(timeTuples) > 0:
Expand All @@ -762,11 +757,6 @@ def create_seq_instructions(seqs, offsets):
write_flags = [True] * len(entries)
for ct, (entry, seq_idx) in enumerate(entries):

# If then instructions list isn't the same length as the
# TDM_INSTRUCTIONS list, then append NOP instructions to
# the shorter of the two until they are.
pad_with_nops(instructions, _TDM_INSTRUCTIONS)

#use first non empty sequence for control flow
if seq_idx == first_non_empty and (
isinstance(entry, ControlFlow.ControlInstruction) or
Expand All @@ -780,7 +770,6 @@ def create_seq_instructions(seqs, offsets):
# control flow instructions
elif isinstance(entry, ControlFlow.Wait):
instructions.append(Wait(label=label))
_TDM_INSTRUCTIONS.append(Wait(label=label))
elif isinstance(entry, ControlFlow.LoadCmp):
instructions.append(LoadCmp(label=label))
elif isinstance(entry, ControlFlow.Sync):
Expand All @@ -803,51 +792,11 @@ def create_seq_instructions(seqs, offsets):
entry.value,
label=label))

# TDM instructions are ignored by the APS
elif isinstance(entry, TdmInstructions.CustomInstruction):
if entry.instruction == 'MAJORITY':
print('MAJORITY(in_addr=%x, out_addr=%x)' %
(entry.in_addr, entry.out_addr))
_TDM_INSTRUCTIONS.append(
MajorityVote(
entry.in_addr, entry.out_addr, label=label))
elif entry.instruction == 'MAJORITYMASK':
print('MAJORITYMASK(in_addr=%x, out_addr=%x)' %
(entry.in_addr, entry.out_addr))
_TDM_INSTRUCTIONS.append(
MajorityVoteMask(
entry.in_addr, entry.out_addr, label=label))
else:
print('UNSUPPORTED CUSTOM: %s(in_addr=0x%x, out_addr=0x%x)' %
(entry.instruction, entry.in_addr, entry.out_addr))

pass
elif isinstance(entry, TdmInstructions.WriteAddrInstruction):
if entry.instruction == 'INVALIDATE':
print('INVALIDATE(channel=%s, addr=0x%x, mask=0x%x)' %
(str(entry.channel), entry.addr, entry.value))
instr = Invalidate(entry.addr, entry.value, label=label)
if entry.channel:
instructions.append(instr)
else:
_TDM_INSTRUCTIONS.append(instr)

elif entry.instruction == 'WRITEADDR':
print('WRITEADDR(channel=%s, addr=0x%x, value=0x%x)' %
(str(entry.channel), entry.addr, entry.value))
instr = WriteAddr(entry.addr, entry.value, label=label)
if entry.channel:
instructions.append(instr)
else:
_TDM_INSTRUCTIONS.append(instr)
elif entry.instruction == 'STOREMEAS':
# TODO: STOREMEAS only happens on the TDM, right?
print('STOREMEAS(channel=%s, addr=0x%x, mapping=0x%x)' %
(str(entry.channel), entry.addr, entry.value))
_TDM_INSTRUCTIONS.append(StoreMeas(entry.addr, entry.value, label=label))
else:
print('UNSUPPORTED WriteAddr: %s(channel=%s, addr=0x%x, val=0x%x)' %
(entry.instruction, str(entry.channel),
entry.addr, entry.value))
continue
pass

continue

Expand All @@ -866,9 +815,6 @@ def create_seq_instructions(seqs, offsets):
# TODO: is this the right thing to do?
if entry.label == 'MEAS' and entry.maddr != (-1, 0):
print('GOT MEAS WAVEFORM WITH MADDR %s' % str(entry.maddr))
_TDM_INSTRUCTIONS.append(LoadCmp(label=label))
_TDM_INSTRUCTIONS.append(
StoreMeas(entry.maddr[0], 1 << entry.maddr[1]))
wfm_instr.payload |= (1 << 48)

instructions.append(wfm_instr)
Expand All @@ -893,14 +839,8 @@ def create_seq_instructions(seqs, offsets):
#clear label
label = None

pad_with_nops(instructions, _TDM_INSTRUCTIONS)

pad_with_nops(instructions, _TDM_INSTRUCTIONS)
return instructions

def get_tdm_instructions():
return [instr.flatten() for instr in _TDM_INSTRUCTIONS]

def create_instr_data(seqs, offsets, cache_lines):
'''
Constructs the complete instruction data vector, and does basic checks for validity.
Expand Down Expand Up @@ -1301,3 +1241,97 @@ def update_wf_library(filename, pulses, offsets):
MAX_WAVEFORM_VALUE * shape.real)
FID['/chan_2/waveforms'][offset:offset + length] = np.int16(
MAX_WAVEFORM_VALUE * shape.imag)


def tdm_instructions(seq):
instructions = list()

label = None
for s in seq:
if isinstance(s, BlockLabel.BlockLabel):
# carry label forward to next entry
label = s
continue

# FIXME: not sure if this is right...
# Need to put a SYNC at the beginning
if len(instructions) == 0:
instructions.append(Sync(label=label))

elif isinstance(s, ControlFlow.Wait):
instructions.append(Wait(label=label))

elif isinstance(s, TdmInstructions.WriteAddrInstruction):
if s.instruction == 'INVALIDATE':
print('INVALIDATE(channel=%s, addr=0x%x, mask=0x%x)' %
(str(s.channel), s.addr, s.value))
instructions.append(Invalidate(s.addr, s.value, label=label))

elif s.instruction == 'WRITEADDR':
print('WRITEADDR(channel=%s, addr=0x%x, value=0x%x)' %
(str(s.channel), s.addr, s.value))
instructions.append(WriteAddr(s.addr, s.value, label=label))

elif s.instruction == 'STOREMEAS':
# TODO: STOREMEAS only happens on the TDM, right?
print('STOREMEAS(channel=%s, addr=0x%x, mapping=0x%x)' %
(str(s.channel), s.addr, s.value))
instructions.append(StoreMeas(s.addr, s.value, label=label))
else:
print('UNSUPPORTED WriteAddr: %s(channel=%s, addr=0x%x, val=0x%x)' %
(s.instruction, str(s.channel),
s.addr, s.value))
continue

elif isinstance(s, TdmInstructions.CustomInstruction):

if s.instruction == 'MAJORITY':
print('MAJORITY(in_addr=%x, out_addr=%x)' %
(s.in_addr, s.out_addr))
instructions.append(
MajorityVote(s.in_addr, s.out_addr, label=label))
elif s.instruction == 'MAJORITYMASK':
print('MAJORITYMASK(in_addr=%x, out_addr=%x)' %
(s.in_addr, s.out_addr))
instructions.append(
MajorityVoteMask(s.in_addr, s.out_addr, label=label))
else:
print('UNSUPPORTED CUSTOM: %s(in_addr=0x%x, out_addr=0x%x)' %
(s.instruction, s.in_addr, s.out_addr))

elif isinstance(s, ControlFlow.Goto):
instructions.append(Goto(s.target, label=label))

elif isinstance(s, ControlFlow.Repeat):
instructions.append(Repeat(s.target, label=label))
elif isinstance(s, ControlFlow.Repeat):
instructions.append(Load(s.value - 1, label=label))

elif isinstance(s, Compiler.Waveform):
if s.label == 'MEAS' and s.maddr != (-1, 0):
print('GOT MEAS WAVEFORM WITH MADDR %s' % str(s.maddr))
instructions.append(LoadCmp(label=label))
instructions.append(StoreMeas(s.maddr[0], 1 << s.maddr[1]))
else:
# This isn't necessarily an error, because the TDM ignores a
# lot of instructions, but until this is debugged it's handy
# to see what's falling through.
#
print('OOPS: unhandled [%s]' % str(s))

# clear label
label = None

# for i in range(len(instructions)):
# instr_bits = instructions[i].flatten()
# # instr_txt = str(Instruction.unflatten(instr_bits))
# print('%5d: 0x%.16x - %s' % (i, instr_bits, str(instructions[i])))

global _TDM_INSTRUCTIONS
_TDM_INSTRUCTIONS = [i.flatten() for i in instructions]

def get_tdm_instructions():
return _TDM_INSTRUCTIONS



23 changes: 14 additions & 9 deletions QGL/n.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,21 @@ def setUp():


q1 = QubitFactory('q1')
q2 = QubitFactory('q2')
# q2 = QubitFactory('q2')

seq = [
X90(q1),
# X90(q2),
MEASA(q1, maddr=(10, 3)),
WriteAddr(1, 7, channel=q1),
Invalidate(addr=4, mask=0xfff),
Id(q1),

WriteAddr(1, 7),
MajorityMask(1, 0),
MajorityVote(10, 9),
WriteAddr(12, 13, channel=q1)

Invalidate(addr=4, mask=0xfff),

MEASA(q1, maddr=(10, 0)),
MEASA(q1, maddr=(10, 1)),
MEASA(q1, maddr=(10, 2)),

MajorityVote(10, 11),
]

aps_metafile = compile_to_hardware([seq], '/tmp/f')
Expand All @@ -75,17 +79,18 @@ def setUp():
print(aps_metadata)

for key in aps_metadata['instruments']:
print('')
print('INSTRUMENT %s' % str(key))
instructions = aps2_reader.raw_instructions(aps_metadata['instruments'][key])
# print('TYPE %s' % str(type(instructions)))
# aps2_reader.display_decompiled_instructions(instructions)

print('')
for i in range(len(instructions)):
instr_bits = instructions[i]
instr_txt = str(Instruction.unflatten(instr_bits))
print('%5d: 0x%.16x - %s' % (i, instr_bits, instr_txt))

print('')
print('INSTRUMENT tdm')
for i in range(len(tdm_instr)):
instr_bits = tdm_instr[i]
Expand Down

0 comments on commit fd595e3

Please sign in to comment.