|
2 | 2 | import katcp |
3 | 3 | import os |
4 | 4 | import random |
| 5 | +from enum import Enum, auto |
5 | 6 |
|
6 | 7 | LOGGER = logging.getLogger(__name__) |
7 | 8 |
|
@@ -125,6 +126,72 @@ class RFDC(object): |
125 | 126 | IMR_LOWPASS = 0 |
126 | 127 | IMR_HIGHPASS = 1 |
127 | 128 |
|
| 129 | + # adc threshold settings |
| 130 | + UPDATE_THRESHOLD0 = 1 |
| 131 | + UPDATE_THRESHOLD1 = 2 |
| 132 | + UPDATE_THRESHOLD_BOTH = 4 |
| 133 | + |
| 134 | + THRESHOLD_OFF = 0 |
| 135 | + THRESHOLD_STICKY_OVER = 1 |
| 136 | + THRESHOLD_STICKY_UNDER = 2 |
| 137 | + THRESHOLD_HYSTERISIS = 3 |
| 138 | + |
| 139 | + THRESHOLD_CLR_MANUAL = 1 |
| 140 | + THRESHOLD_CLR_AUTO = 2 |
| 141 | + |
| 142 | + # interrupt masks |
| 143 | + class InterruptMasks(Enum): |
| 144 | + """ |
| 145 | + adc and dac datapath interrupt indicating one of the datapath interrupts is active. |
| 146 | + To clear, all datapath sub-interrupts must be cleared |
| 147 | + """ |
| 148 | + IXR_FIFOUSRDAT_MASK = 0x0000000F |
| 149 | + IXR_FIFOUSRDAT_OF_MASK = 0x00000001 |
| 150 | + IXR_FIFOUSRDAT_UF_MASK = 0x00000002 |
| 151 | + IXR_FIFOMRGNIND_OF_MASK = 0x00000004 |
| 152 | + IXR_FIFOMRGNIND_UF_MASK = 0x00000008 |
| 153 | + ADC_IXR_DATAPATH_MASK = 0x00000FF0 |
| 154 | + ADC_IXR_DMON_STG_MASK = 0x000003F0 |
| 155 | + DAC_IXR_DATAPATH_MASK = 0x00001FF0 |
| 156 | + DAC_IXR_INTP_STG_MASK = 0x000003F0 |
| 157 | + DAC_IXR_INTP_I_STG0_MASK = 0x00000010 |
| 158 | + DAC_IXR_INTP_I_STG1_MASK = 0x00000020 |
| 159 | + DAC_IXR_INTP_I_STG2_MASK = 0x00000040 |
| 160 | + DAC_IXR_INTP_Q_STG0_MASK = 0x00000080 |
| 161 | + DAC_IXR_INTP_Q_STG1_MASK = 0x00000100 |
| 162 | + DAC_IXR_INTP_Q_STG2_MASK = 0x00000200 |
| 163 | + ADC_IXR_DMON_I_STG0_MASK = 0x00000010 |
| 164 | + ADC_IXR_DMON_I_STG1_MASK = 0x00000020 |
| 165 | + ADC_IXR_DMON_I_STG2_MASK = 0x00000040 |
| 166 | + ADC_IXR_DMON_Q_STG0_MASK = 0x00000080 |
| 167 | + ADC_IXR_DMON_Q_STG1_MASK = 0x00000100 |
| 168 | + ADC_IXR_DMON_Q_STG2_MASK = 0x00000200 |
| 169 | + IXR_QMC_GAIN_PHASE_MASK = 0x00000400 |
| 170 | + IXR_QMC_OFFST_MASK = 0x00000800 |
| 171 | + DAC_IXR_INVSNC_OF_MASK = 0x00001000 |
| 172 | + SUBADC_IXR_DCDR_MASK = 0x00FF0000 |
| 173 | + SUBADC0_IXR_DCDR_OF_MASK = 0x00010000 |
| 174 | + SUBADC0_IXR_DCDR_UF_MASK = 0x00020000 |
| 175 | + SUBADC1_IXR_DCDR_OF_MASK = 0x00040000 |
| 176 | + SUBADC1_IXR_DCDR_UF_MASK = 0x00080000 |
| 177 | + SUBADC2_IXR_DCDR_OF_MASK = 0x00100000 |
| 178 | + SUBADC2_IXR_DCDR_UF_MASK = 0x00200000 |
| 179 | + SUBADC3_IXR_DCDR_OF_MASK = 0x00400000 |
| 180 | + SUBADC3_IXR_DCDR_UF_MASK = 0x00800000 |
| 181 | + ADC_OVR_VOLTAGE_MASK = 0x04000000 |
| 182 | + ADC_OVR_RANGE_MASK = 0x08000000 |
| 183 | + ADC_DAT_OVR_MASK = 0x40000000 |
| 184 | + ADC_FIFO_OVR_MASK = 0x80000000 |
| 185 | + ADC_CMODE_OVR_MASK = 0x10000000 # (Gen 3) |
| 186 | + ADC_CMODE_UNDR_MASK = 0x20000000 # (Gen 3) |
| 187 | + |
| 188 | + def parse_interrupt_mask(self, interrupt_mask): |
| 189 | + interrupt_status = {} |
| 190 | + for interrupt in self.InterruptMasks: |
| 191 | + interrupt_status[interrupt.name] = bool(interrupt_mask & interrupt.value) |
| 192 | + |
| 193 | + return interrupt_status |
| 194 | + |
128 | 195 | class tile(object): |
129 | 196 | pass |
130 | 197 |
|
@@ -157,7 +224,7 @@ def __init__(self, parent, device_name, device_info, initialise=False): |
157 | 224 | """ |
158 | 225 | apply the dtbo for the rfdc driver |
159 | 226 |
|
160 | | - ideally, this would be incorporated as part of an extended `fpg` implementation that includes the device tree overlwy by including the |
| 227 | + ideally, this would be incorporated as part of an extended `fpg` implementation that includes the device tree overlay by including the |
161 | 228 | dtbo as part of the programming process. The rfdc is the only block that is using the dto at the moment, so instead of completely |
162 | 229 | implement this extended fpg functionality the rfdc instead manages its own application of the dto. |
163 | 230 | """ |
@@ -1845,32 +1912,6 @@ def update_nco_mts(self, adc_mask, dac_mask, freq): |
1845 | 1912 | print(i) |
1846 | 1913 | return True |
1847 | 1914 |
|
1848 | | - """ |
1849 | | - Set the inverse sinc filter mode; 0 - disabled, 1 - first nyquist, and for gen 3 devices 2 - second nyquist. |
1850 | | -
|
1851 | | - :param ntile: Tile index of where target converter block is, in the range (0-3) |
1852 | | - :type ntile: int |
1853 | | - :param nblk: Block index within target converter tile, in the range (0-3) |
1854 | | - :type nblk: int |
1855 | | - :param invsinc_fir_mode: inverse sinc filter mode; 0 - disabled, 1 - first nyquist, and for gen 3 devices 2 - second nyquist. |
1856 | | -
|
1857 | | - :type invsinc_fir_mode: int |
1858 | | -
|
1859 | | - :return: 0 if disabled, 1 if first nyquist, and 2 for second nyquist (gen 3 devices only). Returns None if converter is disabled. |
1860 | | - :rtype: int |
1861 | | -
|
1862 | | - Examples |
1863 | | - ---------- |
1864 | | - >>>> rfdc.set_invsinc_fir(0,0,rfdc.INVSINC_FIR_DISABLED) |
1865 | | - 0 # disabled |
1866 | | -
|
1867 | | - >>>> rfdc.set_invsinc_fir(0,0,rfdc.INVSINC_FIR_NYQUIST1) |
1868 | | - 1 # nyquist zone 1 |
1869 | | -
|
1870 | | - >>>> rfdc.set_invsinc_fir(0,0,rfdc.INVSINC_FIR_NYQUIST2) |
1871 | | - 2 # nyquist zone 2 |
1872 | | - """ |
1873 | | - |
1874 | 1915 |
|
1875 | 1916 | def set_mixer_mode(self, ntile, nblk, converter_type, mixer_mode, force=1): |
1876 | 1917 | """ |
@@ -2109,9 +2150,212 @@ def get_mixer_settings(self, ntile, nblk, converter_type): |
2109 | 2150 |
|
2110 | 2151 | return mixer_config |
2111 | 2152 |
|
2112 | | - def get_adc_snapshot(self, ntile, nblk): |
| 2153 | + def get_adc_threshold(self, ntile, nblk): |
| 2154 | + """ |
| 2155 | + Get the threshold settings for target ADC |
| 2156 | +
|
| 2157 | + :param ntile: Tile index of where target converter block is, in the range (0-3) |
| 2158 | + :type ntile: int |
| 2159 | + :param nblk: Block index within target converter tile, in the range (0-3) |
| 2160 | + :type nlblk: int |
| 2161 | +
|
| 2162 | + :return: dict[str, int] of threshold settings, otherwise None if target converter is disabled |
| 2163 | +
|
| 2164 | + Examples |
| 2165 | + --------- |
| 2166 | + >>>> rfdc.set_adc_thresh(0, 0, rfdc.UPDATE_THRESHOLD_BOTH, rfdc.THRESHOLD_STICKY_UNDER, rfdc.THRESHOLD_STICKY_OVER, 8, 8, 1000, 1000, 12000, 12000) |
| 2167 | + >>>> {'UpdateThreshold': 4, |
| 2168 | + 'ThresholdMode0': 2, |
| 2169 | + 'ThresholdMode1': 1, |
| 2170 | + 'ThresholdAvgVal0': 8, |
| 2171 | + 'ThresholdAvgVal1': 8, |
| 2172 | + 'ThresholdUnderVal0': 1000, |
| 2173 | + 'ThresholdUnderVal1': 1000, |
| 2174 | + 'ThresholdOverVal0': 12000, |
| 2175 | + 'ThresholdOverVal1': 12000} |
| 2176 | + """ |
| 2177 | + t = self.parent.transport |
| 2178 | + |
| 2179 | + args = (ntile, nblk) |
| 2180 | + reply, informs = t.katcprequest(name='rfdc-get-adc-thresh', request_timeout=t._timeout, request_args=args) |
| 2181 | + |
| 2182 | + thresh_config = {} |
| 2183 | + info = informs[0].arguments[0].decode().split(', ') |
| 2184 | + if len(info) == 1: # (disabled) response |
| 2185 | + return thresh_config |
| 2186 | + |
| 2187 | + for stat in info: |
| 2188 | + k,v = stat.split(' ') |
| 2189 | + thresh_config[k] = int(v) |
| 2190 | + |
| 2191 | + return thresh_config |
| 2192 | + |
| 2193 | + |
| 2194 | + def set_adc_threshold(self, ntile, nblk, threshold_to_update, threshold0_mode, threshold1_mode, threshold0_avg_val, threshold1_avg_val, |
| 2195 | + threshold0_under_val, threshold1_under_val, threshold0_over_val, threshold1_over_val): |
| 2196 | + """ |
| 2197 | + Configure threshold settings for target ADC |
| 2198 | +
|
| 2199 | + Threshold update constants are: UPDATE_THRESHOLD0 (1), UPDATE_THRESHOLD1 (2), UPDATE_THRESHOLD_BOTH (4) |
| 2200 | + Threshold mode constants are: THRESHOLD_OFF (0), THRESHOLD_STICKY_UNDER (1), THRESHOLD_STICKY_OVER (2), THRESHOLD_HYSTERISIS (3) |
| 2201 | +
|
| 2202 | + :param ntile: Tile index of where target converter block is, in the range (0-3) |
| 2203 | + :type ntile: int |
| 2204 | + :param nblk: Block index within target converter tile, in the range (0-3) |
| 2205 | + :type nlblk: int |
| 2206 | + :param threshold_to_update: Update settings for Threshold0, Threshold1, or both simultaneously |
| 2207 | + :type threshold_to_update: int |
| 2208 | + :param threshold0_mode: The operating mode for Threshold0, {0 Off, sticky-over, sicky-under, hysteresis} |
| 2209 | + :type threshold0_mode: int |
| 2210 | + :param threshold1_mode: The operating mode for Threshold0, {Off, sticky-over, sicky-under, hysteresis} |
| 2211 | + :type threshold1_mode: int |
| 2212 | + :param threshold0_avg_val: Delay value before asserting Threshold0 |
| 2213 | + :type threshold0_avg_val: int |
| 2214 | + :param thresdhol1_avg_val: Delay value before asserting Threshold1 |
| 2215 | + :type threshold1_avg_val: int |
| 2216 | + :param threshold0_under_val: The under "lower" value to use on Threshold0 |
| 2217 | + :type threshold0_under_val: int |
| 2218 | + :param threshold1_under_val: The under "lower" value to use on Threshold1 |
| 2219 | + :type threshold1_under_val: int |
| 2220 | + :param threshold0_over_val: The over "upper" value to use on Threshold0 |
| 2221 | + :type threshold0_over_val: int |
| 2222 | + :param threshold1_over_val: The over "upper" value to use on Threshold1 |
| 2223 | + :type threshold1_over_val: int |
| 2224 | +
|
| 2225 | + :return: dict[str, int] of threshold settings, otherwise None if target converter is disabled |
| 2226 | +
|
| 2227 | + Examples |
| 2228 | + --------- |
| 2229 | + >>>> rfdc.set_adc_thresh(0, 0, rfdc.UPDATE_THRESHOLD_BOTH, rfdc.THRESHOLD_STICKY_UNDER, rfdc.THRESHOLD_STICKY_OVER, 8, 8, 1000, 1000, 12000, 12000) |
| 2230 | + >>>> {'UpdateThreshold': 4, |
| 2231 | + 'ThresholdMode0': 2, |
| 2232 | + 'ThresholdMode1': 1, |
| 2233 | + 'ThresholdAvgVal0': 8, |
| 2234 | + 'ThresholdAvgVal1': 8, |
| 2235 | + 'ThresholdUnderVal0': 1000, |
| 2236 | + 'ThresholdUnderVal1': 1000, |
| 2237 | + 'ThresholdOverVal0': 12000, |
| 2238 | + 'ThresholdOverVal1': 12000} |
| 2239 | + """ |
| 2240 | + t = self.parent.transport |
| 2241 | + args = (ntile, nblk, threshold_to_update, threshold0_mode, threshold1_mode, |
| 2242 | + threshold0_avg_val, threshold1_avg_val, |
| 2243 | + threshold0_under_val, threshold1_under_val, |
| 2244 | + threshold0_over_val, threshold1_over_val) |
| 2245 | + reply, informs = t.katcprequest(name='rfdc-set-adc-thresh', request_timeout=t._timeout, request_args=args) |
| 2246 | + |
| 2247 | + thresh_config = {} |
| 2248 | + info = informs[0].arguments[0].decode().split(', ') |
| 2249 | + if len(info) == 1: # (disabled) response |
| 2250 | + return thresh_config |
| 2251 | + |
| 2252 | + for stat in info: |
| 2253 | + k,v = stat.split(' ') |
| 2254 | + thresh_config[k] = int(v) |
| 2255 | + |
| 2256 | + return thresh_config |
| 2257 | + |
| 2258 | + |
| 2259 | + def set_thresh_clr_mode(self, ntile, nblk, threshold_to_update, clr_mode): |
| 2260 | + """ |
| 2261 | + Configure threshold flag to be cleared manually with `thresh_sticky_clr` or with a QMC gain update for target ADC. |
| 2262 | + The default mode for an enabled threshold is to be cleared manually. Thereshold clear modes are integer values: |
| 2263 | + rfdc.THRESHOLD_CLR_MANUAL=1, rfdc.THRESHOLD_CLR_AUTO=2. |
| 2264 | +
|
| 2265 | + :param ntile: Tile index of where target converter block is, in the range (0-3) |
| 2266 | + :type ntile: int |
| 2267 | + :param nblk: Block index within target converter tile, in the range (0-3) |
| 2268 | + :type nblk: int |
| 2269 | + :param threshold_to_update: Update settings for Threshold0, Threshold1, or both simultaneously |
| 2270 | + :type threshold_to_update: int |
| 2271 | + :param clr_mode: The clear mode to be used |
| 2272 | + :type clr_mode: int |
| 2273 | +
|
| 2274 | + :return: None |
| 2275 | +
|
| 2276 | + Examples |
| 2277 | + _________ |
| 2278 | + >>>> rfdc.set_thresh_clr_mode(0, 0, rfdc.UPDATE_THRESHOLD_BOTH, rfdc.THRESHOLD_CLR_MANUAL) |
| 2279 | + """ |
| 2280 | + t = self.parent.transport |
| 2281 | + args = (ntile, nblk, threshold_to_update, clr_mode) |
| 2282 | + reply, informs = t.katcprequest(name='rfdc-set-thresh-clrmode', request_timeout=t._timeout, request_args=args) |
| 2283 | + |
| 2284 | + |
| 2285 | + def thresh_sticky_clr(self, ntile, nblk, threshold_to_update): |
2113 | 2286 | """ |
| 2287 | + Clears sticky threshold flags for target ADC with threshold flag clear mode `THRESHOLD_CLR_MANUAL`. |
| 2288 | +
|
| 2289 | + :param ntile: Tile index of where target converter block is, in the range (0-3) |
| 2290 | + :type ntile: int |
| 2291 | + :param nblk: Block index within target converter tile, in the range (0-3) |
| 2292 | + :type nblk: int |
| 2293 | + :param threshold_to_update: Update settings for Threshold0, Threshold1, or both simultaneously |
| 2294 | + :type threshold_to_update: int |
| 2295 | + :param clr_mode: The clear mode to be used |
| 2296 | + :type clr_mode: int |
| 2297 | +
|
| 2298 | + :return: None |
| 2299 | +
|
| 2300 | + Examples |
| 2301 | + _________ |
| 2302 | + >>>> rfdc.thresh_sticky_clr(0, 0, rfdc.UPDATE_THRESHOLD_BOTH) |
2114 | 2303 | """ |
2115 | | - raise NotImplemented() |
| 2304 | + t = self.parent.transport |
| 2305 | + args = (ntile, nblk, threshold_to_update) |
| 2306 | + reply, informs = t.katcprequest(name='rfdc-thresh-stickyclr', request_timeout=t._timeout, request_args=args) |
| 2307 | + |
| 2308 | + def get_en_intr(self, ntile, nblk, converter_type): |
| 2309 | + """ |
| 2310 | + """ |
| 2311 | + t = self.parent.transport |
| 2312 | + |
| 2313 | + args = (ntile, nblk, "adc" if converter_type == self.ADC_TILE else "dac") |
| 2314 | + reply, informs = t.katcprequest(name='rfdc-get-en-intr', request_timeout=t._timeout, request_args=args) |
| 2315 | + |
| 2316 | + enabled_intr = {} |
| 2317 | + info = informs[0].arguments[0].decode().split(' ') |
| 2318 | + print(info) |
| 2319 | + if len(info) == 1: # (disabled) response |
| 2320 | + return intr_status |
| 2321 | + else: |
| 2322 | + mask = int(info[1]) |
| 2323 | + |
| 2324 | + return self.parse_interrupt_mask(mask) |
| 2325 | + |
| 2326 | + def set_en_intr(self, ntile, nblk, converter_type, interrupt_mask): |
| 2327 | + """ |
| 2328 | + """ |
| 2329 | + t = self.parent.transport |
| 2330 | + |
| 2331 | + args = (ntile, nblk, "adc" if converter_type == self.ADC_TILE else "dac", interrupt_mask) |
| 2332 | + reply, informs = t.katcprequest(name='rfdc-set-en-intr', request_timeout=t._timeout, request_args=args) |
| 2333 | + |
| 2334 | + enabled_intr = {} |
| 2335 | + info = informs[0].arguments[0].decode().split(' ') |
| 2336 | + print(info) |
| 2337 | + if len(info) == 1: # (disabled) response |
| 2338 | + return intr_status |
| 2339 | + else: |
| 2340 | + mask = int(info[1]) |
| 2341 | + |
| 2342 | + return self.parse_interrupt_mask(mask) |
| 2343 | + |
| 2344 | + def get_intr_status(self, ntile, nblk, converter_type): |
| 2345 | + """ |
| 2346 | + """ |
| 2347 | + t = self.parent.transport |
| 2348 | + |
| 2349 | + args = (ntile, nblk, "adc" if converter_type == self.ADC_TILE else "dac") |
| 2350 | + reply, informs = t.katcprequest(name='rfdc-get-intr-status', request_timeout=t._timeout, request_args=args) |
| 2351 | + |
| 2352 | + intr_status = {} |
| 2353 | + info = informs[0].arguments[0].decode().split(' ') |
| 2354 | + print(info) |
| 2355 | + if len(info) == 1: # (disabled) response |
| 2356 | + return intr_status |
| 2357 | + else: |
| 2358 | + mask = int(info[1]) |
2116 | 2359 |
|
| 2360 | + return self.parse_interrupt_mask(mask) |
2117 | 2361 |
|
0 commit comments