From 6a0e97f161b63670b053b27f9aa7d8cee58c2a0e Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Sat, 11 Apr 2015 22:52:35 -0600 Subject: [PATCH] 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 --- artiq/devices/pdq2/driver.py | 45 ++++++++++++++++++++---------------- artiq/test/pdq2.py | 6 ++--- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/artiq/devices/pdq2/driver.py b/artiq/devices/pdq2/driver.py index aa8b8ae31..79f4f9667 100644 --- a/artiq/devices/pdq2/driver.py +++ b/artiq/devices/pdq2/driver.py @@ -1,6 +1,6 @@ # Robert Jordens , 2012-2015 -from math import log2, sqrt +from math import log, sqrt import logging import struct @@ -95,7 +95,6 @@ class Channel: del self.segments[:] def new_segment(self): - # assert len(self.segments) < self.num_frames segment = Segment() self.segments.append(segment) return segment @@ -186,25 +185,31 @@ class Pdq2: s = self.channels[channel].segments[segment] self.write_mem(channel, s.data, s.adr) + def program_frame(self, frame_data): + segments = [c.new_segment() for c in self.channels] + for i, line in enumerate(frame_data): # segments are concatenated + dac_divider = line.get("dac_divider", 1) + shift = int(log(dac_divider, 2)) + if 2**shift != dac_divider: + raise ValueError("only power-of-two dac_dividers supported") + duration = line["duration"] + trigger = line.get("trigger", False) + for segment, data in zip(segments, line["channel_data"]): + if len(data) != 1: + raise ValueError("only one target per channel and line " + "supported") + for target, target_data in data.items(): + getattr(segment, target)( + shift=shift, duration=duration, trigger=trigger, + **target_data) + # append an empty line to stall the memory reader before jumping + # through the frame table + for segment in segments: + segment.line(typ=3, data=b"", trigger=True, duration=10) + return segments + def program(self, program): self.clear_all() for frame_data in program: - segments = [c.new_segment() for c in self.channels] - 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"] - 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, - trigger=trigger, wait=eof, jump=eof, - **target_data) + self.program_frame(frame_data) self.write_all() diff --git a/artiq/test/pdq2.py b/artiq/test/pdq2.py index 38decb43a..54dbd9b5e 100644 --- a/artiq/test/pdq2.py +++ b/artiq/test/pdq2.py @@ -46,7 +46,7 @@ class TestPdq2(unittest.TestCase): buf = self.test_cmd_program() tb = Pdq2Sim(buf) - tb.ctrl_pads.trigger.reset = 0 + 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:])) @@ -65,8 +65,8 @@ class TestPdq2(unittest.TestCase): yij = yij*20./2**16 if yij > 10: yij -= 20 - self.assertAlmostEqual(yij, yij_ref, 2, - "foo t={}, c={}".format(i, j)) + 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")