From 998be50f07e483044b768808baa2f11b46297813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 13:19:44 +0100 Subject: [PATCH 01/17] urukul: handle MSB in att_reg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/urukul.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index 9f96a92ed..b37590e1a 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -174,7 +174,7 @@ class CPLD: self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0, io_update=0, mask_nu=0, clk_sel=clk_sel, sync_sel=sync_sel, rst=0, io_rst=0) - self.att_reg = att + self.att_reg = int32(att) self.sync_div = sync_div @kernel From e565ca6b823c0d749698994cbffdf9b25dad25d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 13:21:18 +0100 Subject: [PATCH 02/17] urukul: slow down att write to datasheet limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/urukul.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index b37590e1a..6605d852b 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -14,7 +14,8 @@ SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END | # SPI clock write and read dividers SPIT_CFG_WR = 2 SPIT_CFG_RD = 16 -SPIT_ATT_WR = 2 +# 30 MHz fmax, 20 ns setup, 40 ns shift to latch (limiting) +SPIT_ATT_WR = 6 SPIT_ATT_RD = 16 SPIT_DDS_WR = 2 SPIT_DDS_RD = 16 From 38c6878d498da26e3608b0cb347ce46f4f80ef68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 13:32:05 +0100 Subject: [PATCH 03/17] urukul: mention min/max attenuation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/urukul.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index 6605d852b..59b528e62 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -300,7 +300,8 @@ class CPLD: :param channel: Attenuator channel (0-3). :param att: Attenuation setting in dB. Higher value is more - attenuation. + attenuation. Minimum attenuation is 0*dB, maximum attenuation is + 31.5*dB. """ self.set_att_mu(channel, 255 - int32(round(att*8))) From fe3d6661ebf1b851676fa69b56ed27ffb5d2558c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 15:00:59 +0100 Subject: [PATCH 04/17] manual: kasli device name for zadig on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- doc/manual/installing.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index 05a761493..1e8642a0d 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -131,7 +131,8 @@ On Windows, a third-party tool, `Zadig `_, is necessary. 1. Make sure the FPGA board's JTAG USB port is connected to your computer. 2. Activate Options → List All Devices. -3. Select the "Digilent Adept USB Device (Interface 0)" device from the drop-down list. +3. Select the "Digilent Adept USB Device (Interface 0)" or "FTDI Quad-RS232 HS" (or similar) + device from the drop-down list. 4. Select WinUSB from the spinner list. 5. Click "Install Driver" or "Replace Driver". From 14b6b63916b6344e52cf76f64c09174b0c0b4aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 18:21:33 +0000 Subject: [PATCH 05/17] ad9910: rewire io_delay tuning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This now reliably locates the SYNC_CLK-IO_UPDATE edge by doing two scans at different delays between start and stop IO_UPDATE. It also works well when one delay is very close to the edge. And it correctly identifies which (start or stop) pulse hit or crossed the SYNC_CLK edge. for #1143 Signed-off-by: Robert Jördens --- artiq/coredevice/ad9910.py | 61 ++++++++++++++++++---------- artiq/test/coredevice/test_ad9910.py | 31 ++++++++------ 2 files changed, 57 insertions(+), 35 deletions(-) diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index d0538f5be..a37f48705 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -466,17 +466,18 @@ class AD9910: raise ValueError("no valid window/delay") @kernel - def measure_io_update_alignment(self, io_up_delay): + def measure_io_update_alignment(self, delay_start, delay_stop): """Use the digital ramp generator to locate the alignment between IO_UPDATE and SYNC_CLK. The ramp generator is set up to a linear frequency ramp - (dFTW/t_SYNC_CLK=1) and started at a RTIO time stamp. + (dFTW/t_SYNC_CLK=1) and started at a coarse RTIO time stamp plus + `delay_start` and stopped at a coarse RTIO time stamp plus + `delay_stop`. - After scanning the alignment, an IO_UPDATE delay midway between two - edges should be chosen. - - :return: odd/even SYNC_CLK cycle indicator + :param delay_start: Start IO_UPDATE delay in machine units. + :param delay_stop: Stop IO_UPDATE delay in machine units. + :return: Odd/even SYNC_CLK cycle indicator. """ # set up DRG # DRG ACC autoclear and LRR on io update @@ -489,14 +490,16 @@ class AD9910: self.write32(_AD9910_REG_DRAMPR, 0x00010000) # dFTW = 1, (work around negative slope) self.write64(_AD9910_REG_DRAMPS, -1, 0) - at_mu(now_mu() + 0x10 & ~0xf) # align to RTIO/2 - self.cpld.io_update.pulse_mu(8) + # delay io_update after RTIO/2 edge + t = now_mu() + 0x10 & ~0xf + at_mu(t + delay_start) + self.cpld.io_update.pulse_mu(32 - delay_start) # realign # disable DRG autoclear and LRR on io_update self.write32(_AD9910_REG_CFR1, 0x00000002) # stop DRG self.write64(_AD9910_REG_DRAMPS, 0, 0) - at_mu((now_mu() + 0x10 & ~0xf) + io_up_delay) # delay - self.cpld.io_update.pulse_mu(32 - io_up_delay) # realign + at_mu(t + 0x1000 + delay_stop) + self.cpld.io_update.pulse_mu(32 - delay_stop) # realign ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW delay(100*us) # slack # disable DRG @@ -510,22 +513,36 @@ class AD9910: Scan through increasing IO_UPDATE delays until a delay is found that lets IO_UPDATE be registered in the next SYNC_CLK cycle. Return a - IO_UPDATE delay that is midway between two such SYNC_CLK transitions. + IO_UPDATE delay that is as far away from that SYNC_CLK edge + as possible. This method assumes that the IO_UPDATE TTLOut device has one machine - unit resolution (SERDES) and that the ratio between fine RTIO frequency - (RTIO time machine units) and SYNC_CLK is 4. + unit resolution (SERDES). :return: Stable IO_UPDATE delay to be passed to the constructor :class:`AD9910` via the device database. """ - period = 4 # f_RTIO/f_SYNC = 4 - max_delay = 8 # mu, 1 ns - d0 = self.io_update_delay - t0 = int32(self.measure_io_update_alignment(d0)) - for i in range(max_delay - 1): - t = self.measure_io_update_alignment( - (d0 + i + 1) & (max_delay - 1)) - if t != t0: - return (d0 + i + period//2) & (period - 1) + period = self.sysclk_per_mu * 4 # SYNC_CLK period + repeat = 100 + for i in range(period): + t = 0 + # check whether the sync edge is strictly between i, i+2 + for j in range(repeat): + t += self.measure_io_update_alignment(i, i + 2) + if t != 0: # no certain edge + continue + # check left/right half: i,i+1 and i+1,i+2 + t1 = [0, 0] + for j in range(repeat): + t1[0] += self.measure_io_update_alignment(i, i + 1) + t1[1] += self.measure_io_update_alignment(i + 1, i + 2) + if ((t1[0] == 0 and t1[1] == repeat) or # edge left of i + 1 + (t1[0] == repeat and t1[1] == 0) or # edge right of i + 1 + (t1[0] != 0 and t1[1] != 0 and # edge very close to i + 1 + t1[0] != repeat and t1[1] != repeat)): + # the good delay is period//2 after the edge + return (i + 1 + period//2) & (period - 1) + else: # can't interpret result + raise ValueError( + "no clear IO_UPDATE-SYNC_CLK alignment edge found") raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found") diff --git a/artiq/test/coredevice/test_ad9910.py b/artiq/test/coredevice/test_ad9910.py index 4ce47fdee..93e77c0b4 100644 --- a/artiq/test/coredevice/test_ad9910.py +++ b/artiq/test/coredevice/test_ad9910.py @@ -104,18 +104,21 @@ class AD9910Exp(EnvExperiment): self.core.break_realtime() self.dev.cpld.init() self.dev.init() - bins = [0]*8 - self.scan_io_delay(bins) - self.set_dataset("bins", bins) - self.set_dataset("dly", self.dev.io_update_delay) + bins1 = [0]*4 + bins2 = [0]*4 + self.scan_io_delay(bins1, bins2) + self.set_dataset("bins1", bins1) + self.set_dataset("bins2", bins2) + self.set_dataset("dly", self.dev.tune_io_update_delay()) @kernel - def scan_io_delay(self, bins): + def scan_io_delay(self, bins1, bins2): delay(100*us) n = 100 for i in range(n): - for phase in range(len(bins)): - bins[phase] += self.dev.measure_io_update_alignment(phase) + for j in range(len(bins1)): + bins1[j] += self.dev.measure_io_update_alignment(j, j + 1) + bins2[j] += self.dev.measure_io_update_alignment(j, j + 2) delay(10*ms) @kernel @@ -174,12 +177,14 @@ class AD9910Test(ExperimentCase): def test_io_update_delay(self): self.execute(AD9910Exp, "io_update_delay") dly = self.dataset_mgr.get("dly") - bins = self.dataset_mgr.get("bins") - print(dly, bins) - n = max(bins) - # test for 4-periodicity (SYNC_CLK) and maximal contrast - for i in range(len(bins)): - self.assertEqual(abs(bins[i] - bins[(i + 4) % 8]), n) + bins1 = self.dataset_mgr.get("bins1") + bins2 = self.dataset_mgr.get("bins2") + print(dly, bins1, bins2) + n = max(bins2) + # no edge at optimal delay + self.assertEqual(bins2[(dly + 1) & 3], 0) + # edge at expected position + self.assertEqual(bins2[(dly + 3) & 3], n) def test_sw_readback(self): self.execute(AD9910Exp, "sw_readback") From e92755182745b39438f0d0c2e38f5480d23f8c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 19:39:11 +0100 Subject: [PATCH 06/17] manual: add highfinesse-net port --- doc/manual/default_network_ports.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/manual/default_network_ports.rst b/doc/manual/default_network_ports.rst index 6bb699ab9..33fa74a05 100644 --- a/doc/manual/default_network_ports.rst +++ b/doc/manual/default_network_ports.rst @@ -46,4 +46,6 @@ Default network ports +---------------------------------+--------------+ | TOPTICA Laser SDK (out-of-tree) | 3272 | +---------------------------------+--------------+ +| HighFinesse (out-of-tree) | 3273 | ++---------------------------------+--------------+ From a4997c56cfe8670bc26cff6c136ffcfab59c1a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 9 Nov 2018 18:54:34 +0000 Subject: [PATCH 07/17] ad9910: simplify edge detection logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/ad9910.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index a37f48705..ff5954874 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -536,13 +536,12 @@ class AD9910: for j in range(repeat): t1[0] += self.measure_io_update_alignment(i, i + 1) t1[1] += self.measure_io_update_alignment(i + 1, i + 2) - if ((t1[0] == 0 and t1[1] == repeat) or # edge left of i + 1 - (t1[0] == repeat and t1[1] == 0) or # edge right of i + 1 - (t1[0] != 0 and t1[1] != 0 and # edge very close to i + 1 - t1[0] != repeat and t1[1] != repeat)): - # the good delay is period//2 after the edge - return (i + 1 + period//2) & (period - 1) - else: # can't interpret result + if ((t1[0] == 0 and t1[1] == 0) or + (t1[0] == repeat and t1[1] == repeat)): + # edge is not close to i + 1, can't interpret result raise ValueError( "no clear IO_UPDATE-SYNC_CLK alignment edge found") + else: + # the good delay is period//2 after the edge + return (i + 1 + period//2) & (period - 1) raise ValueError("no IO_UPDATE-SYNC_CLK alignment edge found") From 68aad3e4822921d4d2cdaf60fed76ccf3e3e7bf7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 12 Nov 2018 15:28:27 +0000 Subject: [PATCH 08/17] firmware: fix TOCTTOU race in sync/async RPC code. Before this commit, the main loop in session code was laid like: 1. process_kern_queued_rpc 2. process_host_message 3. process_kern_message If a host message (such as an RPC reply) caused the kernel to exit, then any async RPCs would not complete, since RunFinished immediately shuts down the kernel. Fix this by reordering 1 and 2. --- artiq/firmware/runtime/session.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index d9812861b..28dbcadef 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -497,16 +497,16 @@ fn host_kernel_worker(io: &Io, let mut session = Session::new(congress); loop { - while !rpc_queue::empty() { - process_kern_queued_rpc(stream, &mut session)? - } - if stream.can_recv() { process_host_message(io, stream, &mut session)? } else if !stream.may_recv() { return Ok(()) } + while !rpc_queue::empty() { + process_kern_queued_rpc(stream, &mut session)? + } + if mailbox::receive() != 0 { process_kern_message(io, Some(stream), &mut session)?; } From 248c1cf7dce15b358f857c8fce009cb33559ff78 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 12 Nov 2018 15:39:55 +0000 Subject: [PATCH 09/17] firmware: fix another TOCTTOU race in sync/async RPC code. --- artiq/firmware/ksupport/lib.rs | 13 +++++++++++++ artiq/firmware/libproto_artiq/kernel_proto.rs | 1 + artiq/firmware/runtime/session.rs | 8 +++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 8d481f7b5..d176f5551 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -518,6 +518,19 @@ pub unsafe fn main() { attribute_writeback(typeinfo as *const ()); } + // Make sure all async RPCs are processed before exiting. + // Otherwise, if the comms and kernel CPU run in the following sequence: + // + // comms kernel + // ----------------------- ----------------------- + // check for async RPC + // post async RPC + // post RunFinished + // check for mailbox + // + // the async RPC would be missed. + send(&RpcFlush); + send(&RunFinished); loop {} diff --git a/artiq/firmware/libproto_artiq/kernel_proto.rs b/artiq/firmware/libproto_artiq/kernel_proto.rs index ae794ec34..1dd60e77f 100644 --- a/artiq/firmware/libproto_artiq/kernel_proto.rs +++ b/artiq/firmware/libproto_artiq/kernel_proto.rs @@ -73,6 +73,7 @@ pub enum Message<'a> { }, RpcRecvRequest(*mut ()), RpcRecvReply(Result>), + RpcFlush, CacheGetRequest { key: &'a str }, CacheGetReply { value: &'static [i32] }, diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index 28dbcadef..f95febc4a 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -417,7 +417,13 @@ fn process_kern_message(io: &Io, mut stream: Option<&mut TcpStream>, kern_acknowledge() } } - } + }, + &kern::RpcFlush => { + // See ksupport/lib.rs for the reason this request exists. + // We do not need to do anything here because of how the main loop is + // structured. + kern_acknowledge() + }, &kern::CacheGetRequest { key } => { let value = session.congress.cache.get(key); From 2af6edb8f550c5a9b61dd18123eeccffc3e9389f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 13 Nov 2018 13:00:37 +0000 Subject: [PATCH 10/17] eem: fix reset/sync in suservo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/gateware/eem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 96050b748..56f9cb25e 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -541,7 +541,7 @@ class SUServo(_EEM): target.submodules += phy target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) - pads = target.platform.request("{}_dds_reset".format(eem_urukul1)) + pads = target.platform.request("{}_dds_reset_sync_in".format(eem_urukul1)) target.specials += DifferentialOutput(0, pads.p, pads.n) for i, signal in enumerate("sw0 sw1 sw2 sw3".split()): From a52d1be1407e43ffa88533acd541913e898665ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 14 Nov 2018 07:43:56 +0100 Subject: [PATCH 11/17] urukul: expose PROFILE setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add documentation * add unittest Signed-off-by: Robert Jördens --- artiq/coredevice/urukul.py | 12 ++++++++++++ artiq/test/coredevice/test_urukul.py | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index 59b528e62..8bb514c09 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -335,3 +335,15 @@ class CPLD: ftw = ftw_max//div assert ftw*div == ftw_max self.sync.set_mu(ftw) + + @kernel + def set_profile(self, profile): + """Set the PROFILE pins. + + The PROFILE pins are common to all four DDS channels. + + :param profile: PROFILE pins in numeric representation (0-7). + """ + cfg = self.cfg_reg & ~(7 << CFG_PROFILE) + cfg |= (profile & 7) << CFG_PROFILE + self.cfg_write(cfg) diff --git a/artiq/test/coredevice/test_urukul.py b/artiq/test/coredevice/test_urukul.py index 2141303c4..878b37d32 100644 --- a/artiq/test/coredevice/test_urukul.py +++ b/artiq/test/coredevice/test_urukul.py @@ -98,6 +98,13 @@ class UrukulExp(EnvExperiment): self.dev.init() self.dev.set_sync_div(2) + @kernel + def profile(self): + self.core.break_realtime() + self.dev.init() + self.dev.set_profile(7) + self.dev.set_profile(0) + class UrukulTest(ExperimentCase): def test_instantiate(self): @@ -147,3 +154,6 @@ class UrukulTest(ExperimentCase): def test_sync(self): self.execute(UrukulExp, "sync") + + def test_profile(self): + self.execute(UrukulExp, "profile") From d0cadfeb4bfb46c281a728ff16424b909f026302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 14 Nov 2018 07:55:01 +0100 Subject: [PATCH 12/17] ad9910: more idiomatic register names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/ad9910.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index ff5954874..96993447b 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -26,22 +26,22 @@ _AD9910_REG_CFR1 = 0x00 _AD9910_REG_CFR2 = 0x01 _AD9910_REG_CFR3 = 0x02 _AD9910_REG_AUX_DAC = 0x03 -_AD9910_REG_IO_UPD = 0x04 +_AD9910_REG_IO_UPDATE = 0x04 _AD9910_REG_FTW = 0x07 _AD9910_REG_POW = 0x08 _AD9910_REG_ASF = 0x09 -_AD9910_REG_MSYNC = 0x0A -_AD9910_REG_DRAMPL = 0x0B -_AD9910_REG_DRAMPS = 0x0C -_AD9910_REG_DRAMPR = 0x0D -_AD9910_REG_PR0 = 0x0E -_AD9910_REG_PR1 = 0x0F -_AD9910_REG_PR2 = 0x10 -_AD9910_REG_PR3 = 0x11 -_AD9910_REG_PR4 = 0x12 -_AD9910_REG_PR5 = 0x13 -_AD9910_REG_PR6 = 0x14 -_AD9910_REG_PR7 = 0x15 +_AD9910_REG_SYNC = 0x0a +_AD9910_REG_RAMP_LIMIT = 0x0b +_AD9910_REG_RAMP_STEP = 0x0c +_AD9910_REG_RAMP_RATE = 0x0d +_AD9910_REG_PROFILE0 = 0x0e +_AD9910_REG_PROFILE1 = 0x0f +_AD9910_REG_PROFILE2 = 0x10 +_AD9910_REG_PROFILE3 = 0x11 +_AD9910_REG_PROFILE4 = 0x12 +_AD9910_REG_PROFILE5 = 0x13 +_AD9910_REG_PROFILE6 = 0x14 +_AD9910_REG_PROFILE7 = 0x15 _AD9910_REG_RAM = 0x16 @@ -297,7 +297,7 @@ class AD9910: # is equivalent to an output pipeline latency. dt = int32(now_mu()) - int32(ref_time) pow += dt*ftw*self.sysclk_per_mu >> 16 - self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw) + self.write64(_AD9910_REG_PROFILE0, (asf << 16) | pow, ftw) delay_mu(int64(self.io_update_delay)) self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYSCLK at_mu(now_mu() & ~0xf) @@ -389,7 +389,7 @@ class AD9910: :param window: Symmetric SYNC_IN validation window (0-15) in steps of ~75ps for both hold and setup margin. """ - self.write32(_AD9910_REG_MSYNC, + self.write32(_AD9910_REG_SYNC, (window << 28) | # SYNC S/H validation delay (1 << 27) | # SYNC receiver enable (0 << 26) | # SYNC generator disable @@ -485,11 +485,11 @@ class AD9910: # DRG -> FTW, DRG enable self.write32(_AD9910_REG_CFR2, 0x01090000) # no limits - self.write64(_AD9910_REG_DRAMPL, -1, 0) + self.write64(_AD9910_REG_RAMP_LIMIT, -1, 0) # DRCTL=0, dt=1 t_SYNC_CLK - self.write32(_AD9910_REG_DRAMPR, 0x00010000) + self.write32(_AD9910_REG_RAMP_RATE, 0x00010000) # dFTW = 1, (work around negative slope) - self.write64(_AD9910_REG_DRAMPS, -1, 0) + self.write64(_AD9910_REG_RAMP_STEP, -1, 0) # delay io_update after RTIO/2 edge t = now_mu() + 0x10 & ~0xf at_mu(t + delay_start) @@ -497,7 +497,7 @@ class AD9910: # disable DRG autoclear and LRR on io_update self.write32(_AD9910_REG_CFR1, 0x00000002) # stop DRG - self.write64(_AD9910_REG_DRAMPS, 0, 0) + self.write64(_AD9910_REG_RAMP_STEP, 0, 0) at_mu(t + 0x1000 + delay_stop) self.cpld.io_update.pulse_mu(32 - delay_stop) # realign ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW From c3178c2cab5638544957d5c487897945873bb17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 14 Nov 2018 08:30:28 +0100 Subject: [PATCH 13/17] ad9910: profile support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/coredevice/ad9910.py | 10 ++++++---- artiq/test/coredevice/test_ad9910.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index 96993447b..ae5842808 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -256,7 +256,7 @@ class AD9910: @kernel def set_mu(self, ftw, pow=0, asf=0x3fff, phase_mode=_PHASE_MODE_DEFAULT, - ref_time=-1): + ref_time=-1, profile=0): """Set profile 0 data in machine units. This uses machine units (FTW, POW, ASF). The frequency tuning word @@ -276,6 +276,7 @@ class AD9910: by :meth:`set_phase_mode` for this call. :param ref_time: Fiducial time used to compute absolute or tracking phase updates. In machine units as obtained by `now_mu()`. + :param profile: Profile number to set (0-7, default: 0). :return: Resulting phase offset word after application of phase tracking offset. When using :const:`PHASE_MODE_CONTINUOUS` in subsequent calls, use this value as the "current" phase. @@ -297,7 +298,7 @@ class AD9910: # is equivalent to an output pipeline latency. dt = int32(now_mu()) - int32(ref_time) pow += dt*ftw*self.sysclk_per_mu >> 16 - self.write64(_AD9910_REG_PROFILE0, (asf << 16) | pow, ftw) + self.write64(_AD9910_REG_PROFILE0 + profile, (asf << 16) | pow, ftw) delay_mu(int64(self.io_update_delay)) self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYSCLK at_mu(now_mu() & ~0xf) @@ -332,7 +333,7 @@ class AD9910: @kernel def set(self, frequency, phase=0.0, amplitude=1.0, - phase_mode=_PHASE_MODE_DEFAULT, ref_time=-1): + phase_mode=_PHASE_MODE_DEFAULT, ref_time=-1, profile=0): """Set profile 0 data in SI units. .. seealso:: :meth:`set_mu` @@ -342,11 +343,12 @@ class AD9910: :param asf: Amplitude in units of full scale :param phase_mode: Phase mode constant :param ref_time: Fiducial time stamp in machine units + :param profile: Profile to affect :return: Resulting phase offset in turns """ return self.pow_to_turns(self.set_mu( self.frequency_to_ftw(frequency), self.turns_to_pow(phase), - self.amplitude_to_asf(amplitude), phase_mode, ref_time)) + self.amplitude_to_asf(amplitude), phase_mode, ref_time, profile)) @kernel def set_att_mu(self, att): diff --git a/artiq/test/coredevice/test_ad9910.py b/artiq/test/coredevice/test_ad9910.py index 93e77c0b4..fe5ad6a66 100644 --- a/artiq/test/coredevice/test_ad9910.py +++ b/artiq/test/coredevice/test_ad9910.py @@ -134,6 +134,20 @@ class AD9910Exp(EnvExperiment): sw_off = (self.dev.cpld.sta_read() >> (self.dev.chip_select - 4)) & 1 self.set_dataset("sw", (sw_on, sw_off)) + @kernel + def profile_readback(self): + self.core.break_realtime() + self.dev.cpld.init() + self.dev.init() + for i in range(8): + self.dev.set_mu(ftw=i, profile=i) + ftw = [0] * 8 + for i in range(8): + self.dev.cpld.set_profile(i) + ftw[i] = self.dev.read32(_AD9910_REG_FTW) + delay(100*us) + self.set_dataset("ftw", ftw) + class AD9910Test(ExperimentCase): def test_instantiate(self): @@ -189,3 +203,7 @@ class AD9910Test(ExperimentCase): def test_sw_readback(self): self.execute(AD9910Exp, "sw_readback") self.assertEqual(self.dataset_mgr.get("sw"), (1, 0)) + + def test_profile_readback(self): + self.execute(AD9910Exp, "profile_readback") + self.assertEqual(self.dataset_mgr.get("ftw"), list(range(8))) From f77a75ab1728b452856337a1d83200f04b1190b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 14 Nov 2018 07:41:24 +0000 Subject: [PATCH 14/17] test_ad9910: robustify w.r.t. profile synchronization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Jördens --- artiq/test/coredevice/test_ad9910.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/artiq/test/coredevice/test_ad9910.py b/artiq/test/coredevice/test_ad9910.py index fe5ad6a66..b5294e80e 100644 --- a/artiq/test/coredevice/test_ad9910.py +++ b/artiq/test/coredevice/test_ad9910.py @@ -144,6 +144,10 @@ class AD9910Exp(EnvExperiment): ftw = [0] * 8 for i in range(8): self.dev.cpld.set_profile(i) + # If PROFILE is not alligned to SYNC_CLK a multi-bit change + # doesn't transfer cleanly. Use IO_UPDATE to load the profile + # again. + self.dev.cpld.io_update.pulse_mu(8) ftw[i] = self.dev.read32(_AD9910_REG_FTW) delay(100*us) self.set_dataset("ftw", ftw) From 494ffca4d3d027da4557ae7badaaea92837a4d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 14 Nov 2018 21:47:22 +0100 Subject: [PATCH 15/17] gui,scan: add CenterScan Scannable variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * parametrized by center/span/step instead of start/stop/npoints which is more convenient in some applications * no scan widget support so far Signed-off-by: Robert Jördens --- artiq/gui/entries.py | 79 ++++++++++++++++++++++++++++++++++++++++++ artiq/language/scan.py | 40 ++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/artiq/gui/entries.py b/artiq/gui/entries.py index 2469edfbf..bfb4f405d 100644 --- a/artiq/gui/entries.py +++ b/artiq/gui/entries.py @@ -275,6 +275,78 @@ class _RangeScan(LayoutWidget): randomize.setChecked(state["randomize"]) +class _CenterScan(LayoutWidget): + def __init__(self, procdesc, state): + LayoutWidget.__init__(self) + + scale = procdesc["scale"] + + def apply_properties(widget): + widget.setDecimals(procdesc["ndecimals"]) + if procdesc["global_min"] is not None: + widget.setMinimum(procdesc["global_min"]/scale) + else: + widget.setMinimum(float("-inf")) + if procdesc["global_max"] is not None: + widget.setMaximum(procdesc["global_max"]/scale) + else: + widget.setMaximum(float("inf")) + if procdesc["global_step"] is not None: + widget.setSingleStep(procdesc["global_step"]/scale) + if procdesc["unit"]: + widget.setSuffix(" " + procdesc["unit"]) + + center = ScientificSpinBox() + disable_scroll_wheel(center) + apply_properties(center) + center.setPrecision() + center.setRelativeStep() + center.setValue(state["center"]) + self.addWidget(center, 0, 1) + self.addWidget(QtWidgets.QLabel("Center:"), 0, 0) + + span = ScientificSpinBox() + disable_scroll_wheel(span) + apply_properties(span) + span.setPrecision() + span.setRelativeStep() + span.setMinimum(0) + span.setValue(state["span"]) + self.addWidget(span, 1, 1) + self.addWidget(QtWidgets.QLabel("Span:"), 1, 0) + + step = ScientificSpinBox() + disable_scroll_wheel(step) + apply_properties(step) + step.setPrecision() + step.setRelativeStep() + step.setMinimum(0) + step.setValue(state["step"]) + self.addWidget(step, 2, 1) + self.addWidget(QtWidgets.QLabel("Step:"), 2, 0) + + randomize = QtWidgets.QCheckBox("Randomize") + self.addWidget(randomize, 3, 1) + randomize.setChecked(state["randomize"]) + + def update_center(value): + state["center"] = value*scale + + def update_span(value): + state["span"] = value*scale + + def update_step(value): + state["step"] = value*scale + + def update_randomize(value): + state["randomize"] = value + + center.valueChanged.connect(update_center) + span.valueChanged.connect(update_span) + step.valueChanged.connect(update_step) + randomize.stateChanged.connect(update_randomize) + + class _ExplicitScan(LayoutWidget): def __init__(self, state): LayoutWidget.__init__(self) @@ -307,6 +379,7 @@ class ScanEntry(LayoutWidget): self.widgets = OrderedDict() self.widgets["NoScan"] = _NoScan(procdesc, state["NoScan"]) self.widgets["RangeScan"] = _RangeScan(procdesc, state["RangeScan"]) + self.widgets["CenterScan"] = _CenterScan(procdesc, state["CenterScan"]) self.widgets["ExplicitScan"] = _ExplicitScan(state["ExplicitScan"]) for widget in self.widgets.values(): self.stack.addWidget(widget) @@ -314,6 +387,7 @@ class ScanEntry(LayoutWidget): self.radiobuttons = OrderedDict() self.radiobuttons["NoScan"] = QtWidgets.QRadioButton("No scan") self.radiobuttons["RangeScan"] = QtWidgets.QRadioButton("Range") + self.radiobuttons["CenterScan"] = QtWidgets.QRadioButton("Center") self.radiobuttons["ExplicitScan"] = QtWidgets.QRadioButton("Explicit") scan_type = QtWidgets.QButtonGroup() for n, b in enumerate(self.radiobuttons.values()): @@ -343,6 +417,8 @@ class ScanEntry(LayoutWidget): "NoScan": {"value": 0.0, "repetitions": 1}, "RangeScan": {"start": 0.0, "stop": 100.0*scale, "npoints": 10, "randomize": False}, + "CenterScan": {"center": 0.*scale, "span": 100.*scale, + "step": 10.*scale, "randomize": False}, "ExplicitScan": {"sequence": []} } if "default" in procdesc: @@ -361,6 +437,9 @@ class ScanEntry(LayoutWidget): state[ty]["npoints"] = default["npoints"] state[ty]["randomize"] = default["randomize"] state[ty]["seed"] = default["seed"] + elif ty == "CenterScan": + for key in "center span step randomize seed".split(): + state[ty][key] = default[key] elif ty == "ExplicitScan": state[ty]["sequence"] = default["sequence"] else: diff --git a/artiq/language/scan.py b/artiq/language/scan.py index 3df898ad0..4dd4cdd1b 100644 --- a/artiq/language/scan.py +++ b/artiq/language/scan.py @@ -27,7 +27,7 @@ from artiq.language import units __all__ = ["ScanObject", - "NoScan", "RangeScan", "ExplicitScan", + "NoScan", "RangeScan", "CenterScan", "ExplicitScan", "Scannable", "MultiScanManager"] @@ -93,6 +93,43 @@ class RangeScan(ScanObject): "seed": self.seed} +class CenterScan(ScanObject): + """A scan object that yields evenly spaced values within a span around a + center. If ``step`` is finite, then ``center`` is always included. + Values outside ``span`` around center are never included. + If ``randomize`` is True the points are randomly ordered.""" + def __init__(self, center, span, step, randomize=False, seed=None): + self.center = center + self.span = span + self.step = step + self.randomize = randomize + self.seed = seed + + if step == 0.: + self.sequence = [] + else: + n = 1 + int(span/(2.*step)) + self.sequence = [center + sign*i*step + for i in range(n) for sign in [-1, 1]][1:] + + if randomize: + rng = random.Random(seed) + random.shuffle(self.sequence, rng.random) + + def __iter__(self): + return iter(self.sequence) + + def __len__(self): + return len(self.sequence) + + def describe(self): + return {"ty": "CenterScan", + "center": self.center, "step": self.step, + "span": self.span, + "randomize": self.randomize, + "seed": self.seed} + + class ExplicitScan(ScanObject): """A scan object that yields values from an explicitly defined sequence.""" def __init__(self, sequence): @@ -111,6 +148,7 @@ class ExplicitScan(ScanObject): _ty_to_scan = { "NoScan": NoScan, "RangeScan": RangeScan, + "CenterScan": CenterScan, "ExplicitScan": ExplicitScan } From d3483c1d26ad8049384e656ad773b5637517df52 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 15 Nov 2018 19:40:35 +0800 Subject: [PATCH 16/17] kasli: fix SDRAM read delay reset/wrap issue. Closes #1149 --- artiq/firmware/libboard_misoc/sdram.rs | 18 ++++++++++++++++++ conda/artiq-dev/meta.yaml | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/artiq/firmware/libboard_misoc/sdram.rs b/artiq/firmware/libboard_misoc/sdram.rs index 0c41e6c0e..6e3071c4b 100644 --- a/artiq/firmware/libboard_misoc/sdram.rs +++ b/artiq/firmware/libboard_misoc/sdram.rs @@ -242,6 +242,12 @@ mod ddr { ddrphy::dly_sel_write(1 << (DQS_SIGNAL_COUNT - n - 1)); ddrphy::rdly_dq_rst_write(1); + #[cfg(soc_platform = "kasli")] + { + for _ in 0..3 { + ddrphy::rdly_dq_bitslip_write(1); + } + } for _ in 0..DDRPHY_MAX_DELAY { let mut working = true; @@ -327,6 +333,12 @@ mod ddr { let mut max_seen_valid = 0; ddrphy::rdly_dq_rst_write(1); + #[cfg(soc_platform = "kasli")] + { + for _ in 0..3 { + ddrphy::rdly_dq_bitslip_write(1); + } + } for delay in 0..DDRPHY_MAX_DELAY { let mut valid = true; @@ -384,6 +396,12 @@ mod ddr { // Set delay to the middle ddrphy::rdly_dq_rst_write(1); + #[cfg(soc_platform = "kasli")] + { + for _ in 0..3 { + ddrphy::rdly_dq_bitslip_write(1); + } + } for _ in 0..mean_delay { ddrphy::rdly_dq_inc_write(1); } diff --git a/conda/artiq-dev/meta.yaml b/conda/artiq-dev/meta.yaml index 48d448b2e..4b596ccd1 100644 --- a/conda/artiq-dev/meta.yaml +++ b/conda/artiq-dev/meta.yaml @@ -15,7 +15,7 @@ requirements: - python >=3.5.3,<3.6 - setuptools 33.1.1 - migen 0.8 py35_0+git2d62c0c - - misoc 0.11 py35_31+git5ce139dd + - misoc 0.11 py35_33+git128750aa - jesd204b 0.10 - microscope - binutils-or1k-linux >=2.27 From de9d21ffc87e4d1bb9299b235439490b6dd2ac03 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Fri, 16 Nov 2018 15:14:20 +0800 Subject: [PATCH 17/17] nix: use fetchFromGitHub for llvmlite --- nix/llvmlite.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nix/llvmlite.nix b/nix/llvmlite.nix index e03f408e8..5db02ca35 100644 --- a/nix/llvmlite.nix +++ b/nix/llvmlite.nix @@ -1,14 +1,14 @@ -{ stdenv, fetchgit, llvm-or1k, makeWrapper, python3, ncurses, zlib, python3Packages }: +{ stdenv, fetchFromGitHub, llvm-or1k, makeWrapper, python3, ncurses, zlib, python3Packages }: let version = "0f4ebae"; in stdenv.mkDerivation rec { name = "llvmlite-${version}"; - src = fetchgit { - url = "https://github.com/m-labs/llvmlite"; + src = fetchFromGitHub { rev = "401dfb713166bdd2bc0d3ab2b7ebf12e7a434130"; - sha256 = "1ci1pnpspv1pqz712yix1nmplq7568vpsr6gzzl3a33w9s0sw2nq"; - leaveDotGit = true; + owner = "m-labs"; + repo = "llvmlite"; + sha256 = "1hqahd87ihwgjsaxv0y2iywldi1zgyjxjfy3sy3rr1gnwvxb47xw"; }; buildInputs = [ makeWrapper python3 ncurses zlib llvm-or1k python3Packages.setuptools ];