From 1f6441948d1968aae091a18e2ebcb1f2ff6a24c6 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 30 Nov 2014 15:50:57 +0800 Subject: [PATCH] more TTL channels and larger input FIFOs on Papilio Pro --- doc/manual/fpga_board_ports.rst | 38 ++++++++++++++++++++--------- doc/manual/getting_started.rst | 6 ++--- examples/dds_test.py | 8 +++--- examples/photon_histogram.py | 4 +-- examples/pulse_performance.py | 2 +- examples/rtio_skew.py | 2 +- examples/transport.py | 6 ++--- soc/runtime/rtio.c | 2 +- soc/targets/artiq.py | 43 +++++++++++++++++++++++---------- test/full_stack.py | 6 ++--- 10 files changed, 75 insertions(+), 42 deletions(-) diff --git a/doc/manual/fpga_board_ports.rst b/doc/manual/fpga_board_ports.rst index e74f5d312..f66a3b339 100644 --- a/doc/manual/fpga_board_ports.rst +++ b/doc/manual/fpga_board_ports.rst @@ -13,14 +13,30 @@ The low-cost Papilio Pro FPGA board can be used with some limitations. When plugged to a QC-DAQ LVDS adapter, the AD9858 DDS hardware can be used in addition to a limited number of TTL channels. The TTL lines are mapped to RTIO channels as follows: -+--------------+----------+----------------+ -| RTIO channel | TTL line | Capability | -+==============+==========+================+ -| 0 | PMT0 | Output + input | -+--------------+----------+----------------+ -| 1 | TTL0 | Output only | -+--------------+----------+----------------+ -| 2 | TTL1 | Output only | -+--------------+----------+----------------+ -| 3 | TTL2 | Output only | -+--------------+----------+----------------+ ++--------------+----------+-----------------+ +| RTIO channel | TTL line | Capability | ++==============+==========+=================+ +| 0 | PMT0 | Input only | ++--------------+----------+-----------------+ +| 1 | PMT1 | Input only | ++--------------+----------+-----------------+ +| 2 | TTL0 | Output only | ++--------------+----------+-----------------+ +| 3 | TTL1 | Output only | ++--------------+----------+-----------------+ +| 4 | TTL2 | Output only | ++--------------+----------+-----------------+ +| 5 | TTL3 | Output only | ++--------------+----------+-----------------+ +| 6 | TTL4 | Output only | ++--------------+----------+-----------------+ +| 7 | TTL5 | Output only | ++--------------+----------+-----------------+ +| 8 | TTL6 | Output only | ++--------------+----------+-----------------+ +| 9 | TTL7 | Output only | ++--------------+----------+-----------------+ +| 10 | FUD | DDS driver only | ++--------------+----------+-----------------+ + +The input only limitation on channels 0 and 1 comes from the QC-DAQ adapter. When the adapter is not used (and physically unplugged from the Papilio Pro board), the corresponding pins on the Papilio Pro can be used as outputs. Do not configure these channels as outputs when the adapter is plugged, as this would cause electrical contention. diff --git a/doc/manual/getting_started.rst b/doc/manual/getting_started.rst index 2a5a0efa3..4503a11a8 100644 --- a/doc/manual/getting_started.rst +++ b/doc/manual/getting_started.rst @@ -96,11 +96,11 @@ Create a new file ``rtio.py`` containing the following: :: if __name__ == "__main__": with comm_serial.Comm() as comm: core_driver = core.Core(comm) - out_driver = rtio.RTIOOut(core=core_driver, channel=1) + out_driver = rtio.RTIOOut(core=core_driver, channel=2) exp = Tutorial(core=core_driver, o=out_driver) exp.run() -Connect an oscilloscope or logic analyzer to the RTIO channel 1 (pin C11 on the Papilio Pro) and run ``python3 rtio.py``. Notice that the generated signal's period is precisely 4 microseconds, and that it has a duty cycle of precisely 50%. This is not what you would expect if the delay and the pulse were implemented with CPU-controlled GPIO: overhead from the loop management, function calls, etc. would increase the signal's period, and asymmetry in the overhead would cause duty cycle distortion. +Connect an oscilloscope or logic analyzer to the RTIO channel 2 (pin C11 on the Papilio Pro, TTL0) and run ``python3 rtio.py``. Notice that the generated signal's period is precisely 4 microseconds, and that it has a duty cycle of precisely 50%. This is not what you would expect if the delay and the pulse were implemented with CPU-controlled GPIO: overhead from the loop management, function calls, etc. would increase the signal's period, and asymmetry in the overhead would cause duty cycle distortion. Instead, inside the core device, output timing is generated by the gateware and the CPU only programs switching commands with certain timestamps that the CPU computes. This guarantees precise timing as long as the CPU can keep generating timestamps that are increasing fast enough. In case it fails to do that (and attempts to program an event with a timestamp in the past), the :class:`artiq.coredevice.runtime_exceptions.RTIOUnderflow` exception is raised. The kernel causing it may catch it (using a regular ``try... except...`` construct), or it will be propagated to the host. @@ -138,7 +138,7 @@ Try the following code and observe the generated pulses on a 2-channel oscillosc self.o2.pulse(4*us) delay(4*us) -If you assign ``o2`` to the RTIO channel 2, the signal will be generated on the pin C10 of the Papilio Pro. +If you assign ``o2`` to the RTIO channel 3, the signal will be generated on the pin C10 (TTL1) of the Papilio Pro. Within a parallel block, some statements can be made sequential again using a ``with sequential`` construct. Observe the pulses generated by this code: :: diff --git a/examples/dds_test.py b/examples/dds_test.py index 9c31e3891..5921d4ef0 100644 --- a/examples/dds_test.py +++ b/examples/dds_test.py @@ -28,13 +28,13 @@ def main(): exp = DDSTest( core=coredev, a=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=0, rtio_switch=0), + reg_channel=0, rtio_switch=2), b=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=1, rtio_switch=1), + reg_channel=1, rtio_switch=3), c=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=2, rtio_switch=2), + reg_channel=2, rtio_switch=4), d=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=3, rtio_switch=3), + reg_channel=3, rtio_switch=5), led=gpio.GPIOOut(core=coredev, channel=0) ) exp.run() diff --git a/examples/photon_histogram.py b/examples/photon_histogram.py index c5863cf79..03d433ac3 100644 --- a/examples/photon_histogram.py +++ b/examples/photon_histogram.py @@ -41,9 +41,9 @@ def main(): exp = PhotonHistogram( core=coredev, bd=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=0, rtio_switch=1), + reg_channel=0, rtio_switch=2), bdd=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=1, rtio_switch=2), + reg_channel=1, rtio_switch=3), pmt=rtio.RTIOIn(core=coredev, channel=0), repeats=100, nbins=100 diff --git a/examples/pulse_performance.py b/examples/pulse_performance.py index 526b39d3a..a08cc5e62 100644 --- a/examples/pulse_performance.py +++ b/examples/pulse_performance.py @@ -30,5 +30,5 @@ if __name__ == "__main__": with comm_serial.Comm() as comm: coredev = core.Core(comm) exp = PulsePerformance(core=coredev, - o=rtio.RTIOOut(core=coredev, channel=1)) + o=rtio.RTIOOut(core=coredev, channel=2)) exp.run() diff --git a/examples/rtio_skew.py b/examples/rtio_skew.py index ca7d6de75..cb0024f0b 100644 --- a/examples/rtio_skew.py +++ b/examples/rtio_skew.py @@ -32,5 +32,5 @@ if __name__ == "__main__": coredev = core.Core(comm) exp = RTIOSkew(core=coredev, i=rtio.RTIOIn(core=coredev, channel=0), - o=rtio.RTIOOut(core=coredev, channel=1)) + o=rtio.RTIOOut(core=coredev, channel=2)) exp.run() diff --git a/examples/transport.py b/examples/transport.py index 2b917cd15..a7193d73e 100644 --- a/examples/transport.py +++ b/examples/transport.py @@ -120,16 +120,16 @@ if __name__ == "__main__": exp = Transport( core=coredev, bd=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=0, rtio_switch=1), + reg_channel=0, rtio_switch=2), bdd=dds.DDS(core=coredev, dds_sysclk=1*GHz, - reg_channel=1, rtio_switch=2), + reg_channel=1, rtio_switch=3), pmt=rtio.RTIOIn(core=coredev, channel=0), # a compound pdq device that wraps multiple usb devices (looked up # by usb "serial number"/id) into one electrodes=pdq2.CompoundPDQ2( core=coredev, ids=["qc_q1_{}".format(i) for i in range(4)], - rtio_trigger=3, rtio_frame=(4, 5, 6)), + rtio_trigger=4, rtio_frame=(5, 6, 7)), transport_data=data, # or: json.load wait_at_stop=100*us, speed=1.5, diff --git a/soc/runtime/rtio.c b/soc/runtime/rtio.c index 7e29e3f0e..6924be41f 100644 --- a/soc/runtime/rtio.c +++ b/soc/runtime/rtio.c @@ -78,7 +78,7 @@ int rtio_pileup_count(int channel) return r; } -#define RTIO_FUD_CHANNEL 4 +#define RTIO_FUD_CHANNEL 10 void rtio_fud_sync(void) { diff --git a/soc/targets/artiq.py b/soc/targets/artiq.py index 30bfc2c3b..f56f59539 100644 --- a/soc/targets/artiq.py +++ b/soc/targets/artiq.py @@ -9,12 +9,23 @@ from artiqlib import rtio, ad9858 _tester_io = [ ("user_led", 1, Pins("B:7"), IOStandard("LVTTL")), - ("ttl", 0, Pins("C:13"), IOStandard("LVTTL")), - ("ttl", 1, Pins("C:11"), IOStandard("LVTTL")), - ("ttl", 2, Pins("C:10"), IOStandard("LVTTL")), - ("ttl", 3, Pins("C:9"), IOStandard("LVTTL")), - ("ttl", 4, Pins("C:8"), IOStandard("LVTTL")), - ("ttl_tx_en", 0, Pins("A:9"), IOStandard("LVTTL")), + + ("pmt", 0, Pins("C:13"), IOStandard("LVTTL")), + ("pmt", 1, Pins("C:14"), IOStandard("LVTTL")), + + ("ttl", 0, Pins("C:11"), IOStandard("LVTTL")), + ("ttl", 1, Pins("C:10"), IOStandard("LVTTL")), + ("ttl", 2, Pins("C:9"), IOStandard("LVTTL")), + ("ttl", 3, Pins("C:8"), IOStandard("LVTTL")), + ("ttl", 4, Pins("C:7"), IOStandard("LVTTL")), + ("ttl", 5, Pins("C:6"), IOStandard("LVTTL")), + ("ttl", 6, Pins("C:5"), IOStandard("LVTTL")), + ("ttl", 7, Pins("C:4"), IOStandard("LVTTL")), + ("ttl_l_tx_en", 0, Pins("A:9"), IOStandard("LVTTL")), + + ("ttl", 8, Pins("C:3"), IOStandard("LVTTL")), + ("ttl_h_tx_en", 0, Pins("B:6"), IOStandard("LVTTL")), + ("dds", 0, Subsignal("a", Pins("A:5 B:10 A:6 B:9 A:7 B:8")), Subsignal("d", Pins("A:12 B:3 A:13 B:2 A:14 B:1 A:15 B:0")), @@ -74,18 +85,24 @@ class ARTIQMiniSoC(BaseSoC): platform.request("user_led", 0), platform.request("user_led", 1))) - self.comb += platform.request("ttl_tx_en").eq(1) - rtio_pads = [platform.request("ttl", i) for i in range(4)] fud = Signal() - rtio_pads.append(fud) + self.comb += [ + platform.request("ttl_l_tx_en").eq(1), + platform.request("ttl_h_tx_en").eq(1) + ] + rtio_ins = [platform.request("pmt") for i in range(2)] + rtio_outs = [platform.request("ttl", i) for i in range(8)] + [fud] + self.submodules.rtiocrg = _RTIOMiniCRG(platform) self.submodules.rtiophy = rtio.phy.SimplePHY( - rtio_pads, - output_only_pads={rtio_pads[1], rtio_pads[2], rtio_pads[3], fud}) - self.submodules.rtio = rtio.RTIO(self.rtiophy, 125000000) + rtio_ins + rtio_outs, + output_only_pads=set(rtio_outs)) + self.submodules.rtio = rtio.RTIO(self.rtiophy, + clk_freq=125000000, + ififo_depth=512) if with_test_gen: - self.submodules.test_gen = _TestGen(platform.request("ttl", 4)) + self.submodules.test_gen = _TestGen(platform.request("ttl", 8)) dds_pads = platform.request("dds") self.submodules.dds = ad9858.AD9858(dds_pads) diff --git a/test/full_stack.py b/test/full_stack.py index 0ccba86d2..4ac65118c 100644 --- a/test/full_stack.py +++ b/test/full_stack.py @@ -256,7 +256,7 @@ class RTIOCase(unittest.TestCase): uut = _RTIOLoopback( core=coredev, i=rtio.RTIOIn(core=coredev, channel=0), - o=rtio.RTIOOut(core=coredev, channel=1), + o=rtio.RTIOOut(core=coredev, channel=2), npulses=npulses ) uut.run() @@ -267,7 +267,7 @@ class RTIOCase(unittest.TestCase): coredev = core.Core(comm) uut = _RTIOUnderflow( core=coredev, - o=rtio.RTIOOut(core=coredev, channel=1) + o=rtio.RTIOOut(core=coredev, channel=2) ) with self.assertRaises(runtime_exceptions.RTIOUnderflow): uut.run() @@ -277,7 +277,7 @@ class RTIOCase(unittest.TestCase): coredev = core.Core(comm) uut = _RTIOSequenceError( core=coredev, - o=rtio.RTIOOut(core=coredev, channel=1) + o=rtio.RTIOOut(core=coredev, channel=2) ) with self.assertRaises(runtime_exceptions.RTIOSequenceError): uut.run()