From 2538840756ee76ee49a72dc714361f25be2170ff Mon Sep 17 00:00:00 2001 From: Marius Weber Date: Sun, 17 May 2020 14:09:11 +0100 Subject: [PATCH] Coredevice Input Validation (#1447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Input validation and masking of SI -> mu conversions (close #1446) Signed-off-by: Marius Weber * Update RELEASE_NOTES Signed-off-by: Marius Weber Co-authored-by: Robert Jördens --- RELEASE_NOTES.rst | 1 + artiq/coredevice/ad9910.py | 15 +++++++++------ artiq/coredevice/ad9912.py | 8 ++++---- artiq/coredevice/ad9914.py | 23 ++++++++++++++--------- artiq/coredevice/suservo.py | 2 ++ artiq/coredevice/urukul.py | 7 +++++-- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index e1a847bce..da4d0b272 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -11,6 +11,7 @@ Highlights: * Performance improvements: - #1432: SERDES TTL inputs can now detect edges on pulses that are shorter than the RTIO period +* Coredevice SI to mu conversions now always return valid codes, or raise a `ValueError`. * Zotino now exposes `voltage_to_mu()` Breaking changes: diff --git a/artiq/coredevice/ad9910.py b/artiq/coredevice/ad9910.py index a28d87d0a..4f8f99569 100644 --- a/artiq/coredevice/ad9910.py +++ b/artiq/coredevice/ad9910.py @@ -553,7 +553,7 @@ class AD9910: @portable(flags={"fast-math"}) def frequency_to_ftw(self, frequency): - """Return the frequency tuning word corresponding to the given + """Return the 32-bit frequency tuning word corresponding to the given frequency. """ return int32(round(self.ftw_per_hz*frequency)) @@ -567,9 +567,9 @@ class AD9910: @portable(flags={"fast-math"}) def turns_to_pow(self, turns): - """Return the phase offset word corresponding to the given phase + """Return the 16-bit phase offset word corresponding to the given phase in turns.""" - return int32(round(turns*0x10000)) + return int32(round(turns*0x10000)) & 0xffff @portable(flags={"fast-math"}) def pow_to_turns(self, pow_): @@ -579,9 +579,12 @@ class AD9910: @portable(flags={"fast-math"}) def amplitude_to_asf(self, amplitude): - """Return amplitude scale factor corresponding to given fractional - amplitude.""" - return int32(round(amplitude*0x3ffe)) + """Return 14-bit amplitude scale factor corresponding to given + fractional amplitude.""" + code = int32(round(amplitude * 0x3ffe)) + if code < 0 or code > (1 << 14) - 1: + raise ValueError("Invalid AD9910 fractional amplitude!") + return code @portable(flags={"fast-math"}) def asf_to_amplitude(self, asf): diff --git a/artiq/coredevice/ad9912.py b/artiq/coredevice/ad9912.py index 32854ef6e..e21477308 100644 --- a/artiq/coredevice/ad9912.py +++ b/artiq/coredevice/ad9912.py @@ -157,10 +157,10 @@ class AD9912: @portable(flags={"fast-math"}) def frequency_to_ftw(self, frequency): - """Returns the frequency tuning word corresponding to the given + """Returns the 48-bit frequency tuning word corresponding to the given frequency. """ - return int64(round(self.ftw_per_hz*frequency)) + return int64(round(self.ftw_per_hz*frequency)) & ((int64(1) << 48) - 1) @portable(flags={"fast-math"}) def ftw_to_frequency(self, ftw): @@ -171,10 +171,10 @@ class AD9912: @portable(flags={"fast-math"}) def turns_to_pow(self, phase): - """Returns the phase offset word corresponding to the given + """Returns the 16-bit phase offset word corresponding to the given phase. """ - return int32(round((1 << 14)*phase)) + return int32(round((1 << 14)*phase)) & 0xffff @kernel def set(self, frequency, phase=0.0): diff --git a/artiq/coredevice/ad9914.py b/artiq/coredevice/ad9914.py index 647ce58f1..ce7e3b1a2 100644 --- a/artiq/coredevice/ad9914.py +++ b/artiq/coredevice/ad9914.py @@ -236,10 +236,10 @@ class AD9914: @portable(flags={"fast-math"}) def frequency_to_ftw(self, frequency): - """Returns the frequency tuning word corresponding to the given + """Returns the 32-bit frequency tuning word corresponding to the given frequency. """ - return round(float(int64(2)**32*frequency/self.sysclk)) + return int32(round(float(int64(2)**32*frequency/self.sysclk))) @portable(flags={"fast-math"}) def ftw_to_frequency(self, ftw): @@ -250,9 +250,9 @@ class AD9914: @portable(flags={"fast-math"}) def turns_to_pow(self, turns): - """Returns the phase offset word corresponding to the given phase - in turns.""" - return round(float(turns*2**16)) + """Returns the 16-bit phase offset word corresponding to the given + phase in turns.""" + return round(float(turns*2**16)) & 0xffff @portable(flags={"fast-math"}) def pow_to_turns(self, pow): @@ -262,8 +262,12 @@ class AD9914: @portable(flags={"fast-math"}) def amplitude_to_asf(self, amplitude): - """Returns amplitude scale factor corresponding to given amplitude.""" - return round(float(amplitude*0x0fff)) + """Returns 12-bit amplitude scale factor corresponding to given + amplitude.""" + code = round(float(amplitude * 0x0fff)) + if code < 0 or code > 0xfff: + raise ValueError("Invalid AD9914 amplitude!") + return code @portable(flags={"fast-math"}) def asf_to_amplitude(self, asf): @@ -314,10 +318,11 @@ class AD9914: @portable(flags={"fast-math"}) def frequency_to_xftw(self, frequency): - """Returns the frequency tuning word corresponding to the given + """Returns the 63-bit frequency tuning word corresponding to the given frequency (extended resolution mode). """ - return int64(round(2.0*float(int64(2)**62)*frequency/self.sysclk)) + return int64(round(2.0*float(int64(2)**62)*frequency/self.sysclk)) & ( + (int64(1) << 63) - 1) @portable(flags={"fast-math"}) def xftw_to_frequency(self, xftw): diff --git a/artiq/coredevice/suservo.py b/artiq/coredevice/suservo.py index b3d27c870..932adf35b 100644 --- a/artiq/coredevice/suservo.py +++ b/artiq/coredevice/suservo.py @@ -558,5 +558,7 @@ class Channel: :param y: IIR state in units of full scale """ y_mu = int(round(y * Y_FULL_SCALE_MU)) + if y_mu < 0 or y_mu > (1 << 17) - 1: + raise ValueError("Invalid SUServo y-value!") self.set_y_mu(profile, y_mu) return y_mu diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index 2cad18188..cbc3edbfc 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -292,7 +292,7 @@ class CPLD: :meth:`get_att_mu` to retrieve the hardware state set in previous experiments. :param channel: Attenuator channel (0-3). - :param att: Digital attenuation setting: + :param att: 8-bit digital attenuation setting: 255 minimum attenuation, 0 maximum attenuation (31.5 dB) """ a = self.att_reg & ~(0xff << (channel * 8)) @@ -325,7 +325,10 @@ class CPLD: attenuation. Minimum attenuation is 0*dB, maximum attenuation is 31.5*dB. """ - self.set_att_mu(channel, 255 - int32(round(att*8))) + code = 255 - int32(round(att*8)) + if code < 0 or code > 255: + raise ValueError("Invalid urukul.CPLD attenuation!") + self.set_att_mu(channel, code) @kernel def get_att_mu(self):