Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1bca614d1109
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1d5f467da758
Choose a head ref
  • 4 commits
  • 5 files changed
  • 1 contributor

Commits on Apr 5, 2015

  1. Copy the full SHA
    051b01f View commit details
  2. Copy the full SHA
    1f54534 View commit details
  3. wavesynth: new semantics, fix compensation

    * "trigger" now means that the corresponding line will only start
      once the trigger line is high.
    * "jump" is implicit as the last line in a frame must jump back.
    * spline coefficients are now compensated for finite time step size
    jordens committed Apr 5, 2015
    Copy the full SHA
    e870b27 View commit details
  4. pdq2: implement changes in trigger/jump semantics, add unittest

    The unittests now runs the compute_samples.Synthesizer against the actual
    gateware and verifies similarity (up to integer rounding errors).
    jordens committed Apr 5, 2015
    Copy the full SHA
    1d5f467 View commit details
Showing with 248 additions and 178 deletions.
  1. +11 −8 artiq/devices/pdq2/driver.py
  2. +2 −7 artiq/devices/pdq2/mediator.py
  3. +53 −16 artiq/test/pdq2.py
  4. +118 −0 artiq/test/wavesynth.py
  5. +64 −147 artiq/wavesynth/compute_samples.py
19 changes: 11 additions & 8 deletions artiq/devices/pdq2/driver.py
Original file line number Diff line number Diff line change
@@ -22,14 +22,14 @@ def __init__(self):
self.data = b""

def line(self, typ, duration, data, trigger=False, silence=False,
aux=False, shift=0, jump=False, clear=False, wait_trigger=False):
aux=False, shift=0, jump=False, clear=False, wait=False):
assert len(data) % 2 == 0, data
assert len(data)//2 <= 14
#assert dt*(1 << shift) > 1 + len(data)//2
header = (
1 + len(data)//2 | (typ << 4) | (trigger << 6) | (silence << 7) |
(aux << 8) | (shift << 9) | (jump << 13) | (clear << 14) |
(wait_trigger << 15)
(wait << 15)
)
self.data += struct.pack("<HH", header, duration) + data

@@ -86,7 +86,7 @@ def dds(self, amplitude=[], phase=[], **kwargs):
coef = self.compensate([scale*a for a in amplitude])
if phase:
assert len(amplitude) == 4
coef += [p*self.max_val for p in phase]
coef += [p*self.max_val*2 for p in phase]
data = self.pack([0, 1, 2, 2, 0, 1, 1], coef)
self.line(typ=1, data=data, **kwargs)

@@ -195,20 +195,23 @@ def write_segment(self, channel, segment):

def program(self, program):
self.clear_all()
for segment_data in program:
for frame_data in program:
segments = [c.new_segment() for c in self.channels]
for line in segment_data:
for i, line in enumerate(frame_data): # segments are concatenated
dac_divider = line.get("dac_divider", 1)
shift = int(log2(dac_divider))
assert 2**shift == dac_divider
duration = line["duration"]
jump = line.get("jump", False)
wait_trigger = line.get("wait_trigger", False)
trigger = line.get("trigger", False)
if i == 0:
assert trigger
trigger = False # use wait on the last line
eof = i == len(frame_data) - 1
for segment, data in zip(segments, line.get("channel_data")):
assert len(data) == 1
for target, target_data in data.items():
getattr(segment, target)(
shift=shift, duration=duration,
wait_trigger=wait_trigger, jump=jump,
trigger=trigger, wait=eof, jump=eof,
**target_data)
self.write_all()
9 changes: 2 additions & 7 deletions artiq/devices/pdq2/mediator.py
Original file line number Diff line number Diff line change
@@ -113,13 +113,9 @@ def _get_program(self):
"dac_divider": dac_divider,
"duration": duration,
"channel_data": channel_data,
"wait_trigger": False,
"jump": False
} for dac_divider, duration, channel_data in segment.lines]
segment_program[-1]["wait_trigger"] = True
segment_program[0]["trigger"] = True
r += segment_program
r[-1]["wait_trigger"] = False
r[-1]["jump"] = True
return r

@kernel
@@ -203,8 +199,7 @@ def arm(self):
"duration": full_line["duration"],
"channel_data": full_line["channel_data"]
[n*channels_per_pdq2:(n+1)*channels_per_pdq2],
"wait_trigger": full_line["wait_trigger"],
"jump": full_line["jump"]
"trigger": full_line["trigger"],
}
frame_program.append(line)
program.append(frame_program)
69 changes: 53 additions & 16 deletions artiq/test/pdq2.py
Original file line number Diff line number Diff line change
@@ -3,14 +3,16 @@
import io

from artiq.devices.pdq2.driver import Pdq2
from artiq.wavesynth.compute_samples import Synthesizer


pdq2_source = os.getenv("ARTIQ_PDQ2_SOURCE")
pdq2_gateware = os.getenv("ARTIQ_PDQ2_GATEWARE")


class TestPdq2(unittest.TestCase):
def setUp(self):
self.dev = Pdq2(dev=io.BytesIO())
self.synth = Synthesizer(3, _test_program)

def test_reset(self):
self.dev.cmd("RESET", True)
@@ -21,40 +23,77 @@ def test_program(self):
# about 0.14 ms
self.dev.program(_test_program)

@unittest.skipUnless(pdq2_source, "no pdq2 source and gateware")
def test_gateware(self):
self.dev.cmd("START", False)
def test_cmd_program(self):
self.dev.cmd("ARM", False)
self.dev.cmd("START", False)
self.dev.program(_test_program)
self.dev.cmd("START", True)
self.dev.cmd("ARM", True)
#self.dev.cmd("TRIGGER", True)
buf = self.dev.dev.getvalue()
return self.dev.dev.getvalue()

def test_synth(self):
s = self.synth
s.select(0)
y = s.trigger()
return list(zip(*y))

def run_gateware(self):
import sys
sys.path.append(pdq2_source)
sys.path.append(pdq2_gateware)
from gateware.pdq2 import Pdq2Sim
from migen.sim.generic import run_simulation
from matplotlib import pyplot as plt
import numpy as np

buf = self.test_cmd_program()
tb = Pdq2Sim(buf)
tb.ctrl_pads.trigger.reset = 0
run_simulation(tb, vcd_name="pdq2.vcd", ncycles=len(buf) + 250)
out = np.array(tb.outputs, np.uint16).view(np.int16)
for outi in out[len(buf) + 100:].T:
plt.step(np.arange(len(outi)), outi)
delays = 7, 10, 30
y = list(zip(*tb.outputs[len(buf) + 130:]))
y = list(zip(*(yi[di:] for yi, di in zip(y, delays))))
self.assertGreaterEqual(len(y), 80)
self.assertEqual(len(y[0]), 3)
return y

@unittest.skipUnless(pdq2_gateware, "no pdq2 gateware")
def test_run_compare(self):
y_ref = self.test_synth()
y = self.run_gateware()

for i, (yi, yi_ref) in enumerate(zip(y, y_ref)):
for j, (yij, yij_ref) in enumerate(zip(yi, yi_ref)):
yij = yij*20./2**16
if yij > 10:
yij -= 20
self.assertAlmostEqual(yij, yij_ref, 2,
"foo t={}, c={}".format(i, j))

@unittest.skipUnless(pdq2_gateware, "no pdq2 gateware")
@unittest.skip("manual/visual test")
def test_run_plot(self):
from matplotlib import pyplot as plt
import numpy as np
y_ref = self.test_synth()
y_ref = np.array(y_ref)
y = self.run_gateware()
y = np.array(y, dtype=np.uint16).view(np.int16)
y = y*20./2**16
plt.step(np.arange(len(y)), y)
plt.step(np.arange(len(y_ref)), y_ref, "k")
plt.show()


_test_program = [
[
{
"trigger": True,
"duration": 20,
"channel_data": [
{"bias": {"amplitude": [0, 0, 2e-3]}},
{"bias": {"amplitude": [1, 0, -7.5e-3, 7.5e-4]}},
{"dds": {
"amplitude": [0, 0, 4e-3, 0],
"phase": [.5, .05],
"phase": [.25, .025],
}},
],
},
@@ -68,7 +107,7 @@ def test_gateware(self):
}},
{"dds": {
"amplitude": [.8, .08, -4e-3, 0],
"phase": [.5, .05, .04/40],
"phase": [.25, .025, .02/40],
"clear": True,
}},
],
@@ -80,11 +119,9 @@ def test_gateware(self):
{"bias": {"amplitude": [.5, 0, -7.5e-3, 7.5e-4]}},
{"dds": {
"amplitude": [.8, -.08, 4e-3, 0],
"phase": [-.5],
"phase": [-.25],
}},
],
"wait_trigger": True,
"jump": True,
},
]
]
118 changes: 118 additions & 0 deletions artiq/test/wavesynth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import unittest

from artiq.wavesynth import compute_samples


class TestSynthesizer(unittest.TestCase):
program = [
[
# frame 0
{
# frame 0, segment 0, line 0
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [0.0, 0.0, 0.01],
"phase": [0.0, 0.0, 0.0005],
"clear": False}
}
],
"trigger": True
},
{
# frame 0, segment 0, line 1
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [49.5, 1.0, -0.01],
"phase": [0.0, 0.05, 0.0005],
"clear": False}
}
],
"trigger": False
},
],
[
# frame 1
{
# frame 1, segment 0, line 0
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0, 0.0, -0.01],
"phase": [0.0, 0.1, -0.0005],
"clear": False}
}
],
"trigger": True
},
{
# frame 1, segment 0, line 1
"dac_divider": 1,
"duration": 100,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [50.5, -1.0, 0.01],
"phase": [0.0, 0.05, -0.0005],
"clear": False}
}
],
"trigger": False
}
],
[
# frame 2
{
# frame 2, segment 0, line 0
"dac_divider": 1,
"duration": 84,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0],
"phase": [0.0, 0.05],
"clear": False}
}
],
"trigger": True
},
{
# frame 2, segment 1, line 0
"dac_divider": 1,
"duration": 116,
"channel_data": [
{
# channel 0
"dds": {"amplitude": [100.0],
"phase": [0.0, 0.05],
"clear": True}
}
],
"trigger": True
}
]
]

def setUp(self):
self.dev = compute_samples.Synthesizer(1, self.program)
self.t = list(range(600))

def drive(self):
s = self.dev
y = []
for f in 0, 2, None, 1:
if f is not None:
s.select(f)
y += s.trigger()[0]
x = list(range(600))
return x, y

def test_run(self):
x, y = self.drive()
Loading