artiq/artiq/test/pdq2.py
Robert Jordens 6a0e97f161 pdq2: refactor program_frame(), cleanup test, stall correctly
Once the Sequencer ack's a line, the Parser starts preparing the
next one. This includes jumping through the frame table if necessary.
To stall the Parser while the Sequencer executes the last line of a
frame and to ensure that the frame select lines can be set up and their
sampling is synchronized to a trigger, we add a triggered stall line
at the end of the frame.

When that line is triggered the Parser jumps through the table and starts
parsing the first line of the next frame. We let the duration of this
last stall line be 10 cycles (200ns@50MHz) to be able to distinguish this
sampling of the frame select lines from the triggering of the first line
in the next frame.

frame           f
parser     n     f 0
stb        __---________---___
trigger    ___----_______----_
ack        ____-__________-___
sequencer  n-1 n          0
2015-04-14 18:18:16 -06:00

128 lines
3.7 KiB
Python

import unittest
import os
import io
from artiq.devices.pdq2.driver import Pdq2
from artiq.wavesynth.compute_samples import Synthesizer
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)
buf = self.dev.dev.getvalue()
self.assertEqual(buf, b"\xa5\x00")
def test_program(self):
# about 0.14 ms
self.dev.program(_test_program)
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)
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_gateware)
from gateware.pdq2 import Pdq2Sim
from migen.sim.generic import run_simulation
buf = self.test_cmd_program()
tb = Pdq2Sim(buf)
tb.ctrl_pads.trigger.reset = 1
run_simulation(tb, ncycles=len(buf) + 250)
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, "disagreement at "
"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": [.25, .025],
}},
],
},
{
"duration": 40,
"channel_data": [
{"bias": {"amplitude": [.4, .04, -2e-3]}},
{"bias": {
"amplitude": [.5],
"silence": True,
}},
{"dds": {
"amplitude": [.8, .08, -4e-3, 0],
"phase": [.25, .025, .02/40],
"clear": True,
}},
],
},
{
"duration": 20,
"channel_data": [
{"bias": {"amplitude": [.4, -.04, 2e-3]}},
{"bias": {"amplitude": [.5, 0, -7.5e-3, 7.5e-4]}},
{"dds": {
"amplitude": [.8, -.08, 4e-3, 0],
"phase": [-.25],
}},
],
},
]
]