2 # Copyright 2007 Free Software Foundation, Inc.
4 # This file is part of GNU Radio
6 # GNU Radio is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2, or (at your option)
11 # GNU Radio is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with GNU Radio; see the file COPYING. If not, write to
18 # the Free Software Foundation, Inc., 51 Franklin Street,
19 # Boston, MA 02110-1301, USA.
22 from gnuradio import usrp1
25 from usrpm import usrp_dbid
27 import db_instantiator
28 from usrpm.usrp_fpga_regs import *
30 #debug_using_gui = True # Must be set to True or False
31 debug_using_gui = False # Must be set to True or False
34 # import flexrf_debug_gui
36 # d'board i/o pin defs
39 TX_POWER = (1 << 0) # TX Side Power
40 RX_TXN = (1 << 1) # T/R antenna switch for TX/RX port
43 RX2_RX1N = (1 << 0) # antenna switch between RX2 and TX/RX port
44 RXENABLE = (1 << 1) # enables mixer
45 PLL_LOCK_DETECT = (1 << 2) # Muxout pin from PLL -- MUST BE INPUT
46 MReset = (1 << 3) # NB6L239 Master Reset, asserted low
47 SELA0 = (1 << 4) # NB6L239 SelA0
48 SELA1 = (1 << 5) # NB6L239 SelA1
49 SELB0 = (1 << 6) # NB6L239 SelB0
50 SELB1 = (1 << 7) # NB6L239 SelB1
51 PLL_ENABLE = (1 << 8) # CE Pin on PLL
52 AUX_SCLK = (1 << 9) # ALT SPI SCLK
53 AUX_SDO = (1 << 10) # ALT SPI SDO
54 AUX_SEN = (1 << 11) # ALT SPI SEN
56 SPI_ENABLE_TX_A = usrp1.SPI_ENABLE_TX_A
57 SPI_ENABLE_TX_B = usrp1.SPI_ENABLE_TX_B
58 SPI_ENABLE_RX_A = usrp1.SPI_ENABLE_RX_A
59 SPI_ENABLE_RX_B = usrp1.SPI_ENABLE_RX_B
63 A few comments about the WBX boards:
64 They are half-duplex. I.e., transmit and receive are mutually exclusive.
65 There is a single LO for both the Tx and Rx sides.
66 The the shared control signals are hung off of the Rx side.
67 The shared io controls are duplexed onto the Rx side pins.
68 The wbx_high d'board always needs to be in 'auto_tr_mode'
72 class wbx_base(db_base.db_base):
74 Abstract base class for all wbx boards.
76 Derive board specific subclasses from db_wbx_base_{tx,rx}
78 def __init__(self, usrp, which):
80 @param usrp: instance of usrp.source_c
81 @param which: which side: 0 or 1 corresponding to side A or B respectively
84 # sets _u _which _tx and _slot
85 db_base.db_base.__init__(self, usrp, which)
88 self.spi_format = usrp1.SPI_FMT_MSB | usrp1.SPI_FMT_HDR_0
90 # FIXME -- the write reg functions don't work with 0xffff for masks
91 self._rx_write_oe(int(PLL_ENABLE|MReset|SELA0|SELA1|SELB0|SELB1|RX2_RX1N|RXENABLE), 0x7fff)
92 self._rx_write_io((PLL_ENABLE|MReset|0|RXENABLE), (PLL_ENABLE|MReset|RX2_RX1N|RXENABLE))
94 self._tx_write_oe((TX_POWER|RX_TXN), 0x7fff)
95 self._tx_write_io((0|RX_TXN), (TX_POWER|RX_TXN)) # TX off, TR switch set to RX
97 self.spi_enable = (SPI_ENABLE_RX_A, SPI_ENABLE_RX_B)[which]
99 self.set_auto_tr(False)
102 # title = "FlexRF Debug Rx"
104 # title = "FlexRF Debug Tx"
105 # self.gui = flexrf_debug_gui.flexrf_debug_gui(self, title)
106 # self.gui.Show(True)
110 #self._u.write_io(self._which, self.power_off, POWER_UP) # turn off power to board
111 #self._u._write_oe(self._which, 0, 0xffff) # turn off all outputs
112 self.set_auto_tr(False)
114 def _lock_detect(self):
116 @returns: the value of the VCO/PLL lock detect bit.
119 if self._rx_read_io() & PLL_LOCK_DETECT:
121 else: # Give it a second chance
122 if self._rx_read_io() & PLL_LOCK_DETECT:
127 # Both sides need access to the Rx pins.
128 # Write them directly, bypassing the convenience routines.
129 # (Sort of breaks modularity, but will work...)
131 def _tx_write_oe(self, value, mask):
132 return self._u._write_fpga_reg((FR_OE_0, FR_OE_2)[self._which],
133 ((mask & 0xffff) << 16) | (value & 0xffff))
135 def _rx_write_oe(self, value, mask):
136 return self._u._write_fpga_reg((FR_OE_1, FR_OE_3)[self._which],
137 ((mask & 0xffff) << 16) | (value & 0xffff))
139 def _tx_write_io(self, value, mask):
140 return self._u._write_fpga_reg((FR_IO_0, FR_IO_2)[self._which],
141 ((mask & 0xffff) << 16) | (value & 0xffff))
143 def _rx_write_io(self, value, mask):
144 return self._u._write_fpga_reg((FR_IO_1, FR_IO_3)[self._which],
145 ((mask & 0xffff) << 16) | (value & 0xffff))
147 def _rx_read_io(self):
148 t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
149 return (t >> 16) & 0xffff
151 def _tx_read_io(self):
152 t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
156 def _compute_regs(self, freq):
158 Determine values of registers, along with actual freq.
160 @param freq: target frequency in Hz
162 @returns: (R, N, func, init, actual_freq)
163 @rtype: tuple(int, int, int, int, float)
165 Override this in derived classes.
167 raise NotImplementedError
169 def _refclk_freq(self):
170 return float(self._u.fpga_master_clock_freq())/self._refclk_divisor()
172 def _refclk_divisor(self):
174 Return value to stick in REFCLK_DIVISOR register
178 # ----------------------------------------------------------------
180 def set_freq(self, freq):
182 @returns (ok, actual_baseband_freq) where:
183 ok is True or False and indicates success or failure,
184 actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
186 raise NotImplementedError
188 def gain_range(self):
190 Return range of gain that can be set by this d'board.
192 @returns (min_gain, max_gain, step_size)
193 Where gains are expressed in decibels (your mileage may vary)
195 raise NotImplementedError
197 def set_gain(self, gain):
201 @param gain: gain in decibels
204 raise NotImplementedError
206 def _set_pga(self, pga_gain):
207 if(self._which == 0):
208 self._u.set_pga (0, pga_gain)
209 self._u.set_pga (1, pga_gain)
211 self._u.set_pga (2, pga_gain)
212 self._u.set_pga (3, pga_gain)
214 def is_quadrature(self):
216 Return True if this board requires both I & Q analog channels.
218 This bit of info is useful when setting up the USRP Rx mux register.
222 # ----------------------------------------------------------------
224 class wbx_base_tx(wbx_base):
225 def __init__(self, usrp, which):
227 @param usrp: instance of usrp.sink_c
228 @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
230 wbx_base.__init__(self, usrp, which)
232 # power up the transmit side, NO -- but set antenna to receive
233 self._u.write_io(self._which, (TX_POWER), (TX_POWER|RX_TXN))
234 self._lo_offset = 0e6
236 # Gain is not set by the PGA, but the PGA must be set at max gain in the TX
237 return self._set_pga(self._u.pga_max())
240 # Power down and leave the T/R switch in the R position
241 self._u.write_io(self._which, (RX_TXN), (TX_POWER|RX_TXN))
242 wbx_base.__del__(self)
244 def set_auto_tr(self, on):
246 self.set_atr_mask (RX_TXN)
247 self.set_atr_txval(0)
248 self.set_atr_rxval(RX_TXN)
250 self.set_atr_mask (0)
251 self.set_atr_txval(0)
252 self.set_atr_rxval(0)
254 def set_enable(self, on):
256 Enable transmitter if on is True
262 self._u.write_io(self._which, v, RX_TXN)
264 def set_lo_offset(self, offset):
266 Set amount by which LO is offset from requested tuning frequency.
268 @param offset: offset in Hz
270 self._lo_offset = offset
274 Get amount by which LO is offset from requested tuning frequency.
276 @returns Offset in Hz
278 return self._lo_offset
280 class wbx_base_rx(wbx_base):
281 def __init__(self, usrp, which):
283 @param usrp: instance of usrp.source_c
284 @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
286 wbx_base.__init__(self, usrp, which)
288 # set up for RX on TX/RX port
289 self.select_rx_antenna('TX/RX')
291 self.bypass_adc_buffers(True)
293 self._lo_offset = -4e6
297 self._u.write_io(self._which, 0, (RXENABLE))
298 wbx_base.__del__(self)
300 def set_auto_tr(self, on):
302 self.set_atr_mask (ENABLE)
303 self.set_atr_txval( 0)
304 self.set_atr_rxval(ENABLE)
306 self.set_atr_mask (0)
307 self.set_atr_txval(0)
308 self.set_atr_rxval(0)
310 def select_rx_antenna(self, which_antenna):
312 Specify which antenna port to use for reception.
313 @param which_antenna: either 'TX/RX' or 'RX2'
315 if which_antenna in (0, 'TX/RX'):
316 self._u.write_io(self._which, 0, RX2_RX1N)
317 elif which_antenna in (1, 'RX2'):
318 self._u.write_io(self._which, RX2_RX1N, RX2_RX1N)
320 raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'"
322 def set_gain(self, gain):
326 @param gain: gain in decibels
329 maxgain = self.gain_range()[1] - self._u.pga_max()
330 mingain = self.gain_range()[0]
332 pga_gain = gain-maxgain
333 assert pga_gain <= self._u.pga_max()
341 dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
342 assert dac_value>=0 and dac_value<4096
343 return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \
344 self._set_pga(int(pga_gain))
346 def set_lo_offset(self, offset):
348 Set amount by which LO is offset from requested tuning frequency.
350 @param offset: offset in Hz
352 self._lo_offset = offset
356 Get amount by which LO is offset from requested tuning frequency.
358 @returns Offset in Hz
360 return self._lo_offset
363 def i_and_q_swapped(self):
365 Return True if this is a quadrature device and ADC 0 is Q.
369 # ----------------------------------------------------------------
371 class _ADF410X_common(object):
373 # R-Register Common Values
374 self.R_RSV = 0 # bits 23,22,21
375 self.LDP = 1 # bit 20 Lock detect in 5 cycles
376 self.TEST = 0 # bit 19,18 Normal
377 self.ABP = 0 # bit 17,16 2.9ns
379 # N-Register Common Values
380 self.N_RSV = 0 # 23,22
381 self.CP_GAIN = 0 # 21
383 # Function Register Common Values
384 self.P = 0 # bits 23,22 0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65
385 self.PD2 = 0 # bit 21 Normal operation
386 self.CP2 = 7 # bits 20,19,18 CP Gain = 5mA
387 self.CP1 = 7 # bits 17,16,15 CP Gain = 5mA
388 self.TC = 0 # bits 14-11 PFD Timeout
389 self.FL = 0 # bit 10,9 Fastlock Disabled
390 self.CP3S = 0 # bit 8 CP Enabled
391 self.PDP = 0 # bit 7 Phase detector polarity, Positive=1
392 self.MUXOUT = 1 # bits 6:4 Digital Lock Detect
393 self.PD1 = 0 # bit 3 Normal operation
394 self.CR = 0 # bit 2 Normal operation
396 def _compute_regs(self, freq):
398 Determine values of R, control, and N registers, along with actual freq.
400 @param freq: target frequency in Hz
402 @returns: (R, N, control, actual_freq)
403 @rtype: tuple(int, int, int, float)
406 # Band-specific N-Register Values
407 phdet_freq = self._refclk_freq()/self.R_DIV
408 print "phdet_freq = %f" % (phdet_freq,)
409 desired_n = round(freq*self.freq_mult/phdet_freq)
410 print "desired_n %f" % (desired_n,)
411 actual_freq = desired_n * phdet_freq
412 print "actual freq %f" % (actual_freq,)
413 B = math.floor(desired_n/self._prescaler())
414 A = desired_n - self._prescaler()*B
415 print "A %d B %d" % (A,B)
416 self.B_DIV = int(B) # bits 20:8
417 self.A_DIV = int(A) # bit 6:2
418 #assert self.B_DIV >= self.A_DIV
419 if self.B_DIV < self.A_DIV:
421 R = (self.R_RSV<<21) | (self.LDP<<20) | (self.TEST<<18) | \
422 (self.ABP<<16) | (self.R_DIV<<2)
424 N = (self.N_RSV<<22) | (self.CP_GAIN<<21) | (self.B_DIV<<8) | (self.A_DIV<<2)
426 control = (self.P<<22) | (self.PD2<<21) | (self.CP2<<18) | (self.CP1<<15) | \
427 (self.TC<<11) | (self.FL<<9) | (self.CP3S<<8) | (self.PDP<<7) | \
428 (self.MUXOUT<<4) | (self.PD1<<3) | (self.CR<<2)
430 return (R,N,control,actual_freq/self.freq_mult)
432 def _write_all(self, R, N, control):
434 Write all PLL registers:
440 Adds 10ms delay between writing control and N if this is first call.
441 This is the required power-up sequence.
443 @param R: 24-bit R counter latch
445 @param N: 24-bit N counter latch
447 @param control: 24-bit control latch
451 self._write_func(control)
452 self._write_init(control)
458 def _write_R(self, R):
459 self._write_it((R & ~0x3) | 0)
461 def _write_N(self, N):
462 self._write_it((N & ~0x3) | 1)
464 def _write_func(self, func):
465 self._write_it((func & ~0x3) | 2)
467 def _write_init(self, init):
468 self._write_it((init & ~0x3) | 3)
470 def _write_it(self, v):
471 s = ''.join((chr((v >> 16) & 0xff),
472 chr((v >> 8) & 0xff),
474 self._u._write_spi(0, self.spi_enable, self.spi_format, s)
476 def _prescaler(self):
486 raise ValueError, "Prescaler out of range"
488 #----------------------------------------------------------------------
489 class _lo_common(_ADF410X_common):
491 _ADF410X_common.__init__(self)
493 # Band-specific R-Register Values
494 self.R_DIV = 4 # bits 15:2
496 # Band-specific C-Register values
497 self.P = 0 # bits 23,22 0 = Div by 8/9
498 self.CP2 = 7 # bits 19:17
499 self.CP1 = 7 # bits 16:14
501 # Band specifc N-Register Values
502 self.DIVSEL = 0 # bit 23
503 self.DIV2 = 0 # bit 22
504 self.CPGAIN = 0 # bit 21
510 def freq_range(self): # FIXME
511 return (50e6, 1000e6, 16e6)
513 def set_divider(self, main_or_aux, divisor):
514 if main_or_aux not in (0, 'main', 1, 'aux'):
515 raise ValueError, "main_or_aux must be 'main' or 'aux'"
516 if main_or_aux in (0, 'main'):
517 if divisor not in (1,2,4,8):
518 raise ValueError, "Main Divider Must be 1, 2, 4, or 8"
519 for (div,val) in ((1,0),(2,1),(4,2),(8,3)):
523 if divisor not in (2,4,8,16):
524 raise ValueError, "Aux Divider Must be 2, 4, 8 or 16"
525 for (div,val) in ((2,0),(4,1),(8,2),(16,3)):
529 vala = self.main_div*SELA0
530 valb = self.aux_div*SELB0
531 mask = SELA0|SELA1|SELB0|SELB1
533 self._rx_write_io(((self.main_div*SELA0) | (self.aux_div*SELB0)),
534 (SELA0|SELA1|SELB0|SELB1))
536 def set_freq(self, freq):
537 #freq += self._lo_offset
539 if(freq < 20e6 or freq > 1200e6):
540 raise ValueError, "Requested frequency out of range"
543 while lo_freq < 1e9 and div < 8:
545 lo_freq = lo_freq * 2
546 print "For RF freq of %f, we set DIV=%d and LO Freq=%f" % (freq, div, lo_freq)
547 self.set_divider('main', div)
548 self.set_divider('aux', div*2)
550 R, N, control, actual_freq = self._compute_regs(lo_freq)
551 print "R %d N %d control %d actual freq %f" % (R,N,control,actual_freq)
554 self._write_all(R, N, control)
555 return (self._lock_detect(), actual_freq/div/2)
558 #------------------------------------------------------------
559 class db_wbx_lo_tx(_lo_common, wbx_base_tx):
560 def __init__(self, usrp, which):
561 wbx_base_tx.__init__(self, usrp, which)
562 _lo_common.__init__(self)
564 def gain_range(self):
566 Return range of gain that can be set by this d'board.
568 @returns (min_gain, max_gain, step_size)
569 Where gains are expressed in decibels (your mileage may vary)
571 Gain is controlled by a VGA in the output amplifier, not the PGA
575 def set_gain(self, gain):
579 @param gain: gain in decibels
582 maxgain = self.gain_range()[1]
583 mingain = self.gain_range()[0]
594 dac_value = ((txvga_gain-mingain)*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
595 assert dac_value>=0 and dac_value<4096
596 print "DAC value %d" % (dac_value,)
597 return self._u.write_aux_dac(self._which, 1, int(dac_value))
599 class db_wbx_lo_rx(_lo_common, wbx_base_rx):
600 def __init__(self, usrp, which):
601 wbx_base_rx.__init__(self, usrp, which)
602 _lo_common.__init__(self)
604 def gain_range(self):
606 Return range of gain that can be set by this d'board.
608 @returns (min_gain, max_gain, step_size)
609 Where gains are expressed in decibels (your mileage may vary)
611 return (self._u.pga_min(), self._u.pga_max() + 45, 0.05)
613 #------------------------------------------------------------
614 # hook these daughterboard classes into the auto-instantiation framework
615 db_instantiator.add(usrp_dbid.WBX_LO_TX, lambda usrp, which : (db_wbx_lo_tx(usrp, which),))
616 db_instantiator.add(usrp_dbid.WBX_LO_RX, lambda usrp, which : (db_wbx_lo_rx(usrp, which),))