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
41 TX_ENB_MIX = (1 << 2) # Enable IQ mixer
45 RX2_RX1N = (1 << 0) # antenna switch between RX2 and TX/RX port
46 RXENABLE = (1 << 1) # enables mixer
47 PLL_LOCK_DETECT = (1 << 2) # Muxout pin from PLL -- MUST BE INPUT
48 MReset = (1 << 3) # NB6L239 Master Reset, asserted low
49 SELA0 = (1 << 4) # NB6L239 SelA0
50 SELA1 = (1 << 5) # NB6L239 SelA1
51 SELB0 = (1 << 6) # NB6L239 SelB0
52 SELB1 = (1 << 7) # NB6L239 SelB1
53 PLL_ENABLE = (1 << 8) # CE Pin on PLL
54 AUX_SCLK = (1 << 9) # ALT SPI SCLK
55 AUX_SDO = (1 << 10) # ALT SPI SDO
56 AUX_SEN = (1 << 11) # ALT SPI SEN
58 SPI_ENABLE_TX_A = usrp1.SPI_ENABLE_TX_A
59 SPI_ENABLE_TX_B = usrp1.SPI_ENABLE_TX_B
60 SPI_ENABLE_RX_A = usrp1.SPI_ENABLE_RX_A
61 SPI_ENABLE_RX_B = usrp1.SPI_ENABLE_RX_B
65 A few comments about the WBX boards:
66 They are half-duplex. I.e., transmit and receive are mutually exclusive.
67 There is a single LO for both the Tx and Rx sides.
68 The the shared control signals are hung off of the Rx side.
69 The shared io controls are duplexed onto the Rx side pins.
70 The wbx_high d'board always needs to be in 'auto_tr_mode'
74 class wbx_base(db_base.db_base):
76 Abstract base class for all wbx boards.
78 Derive board specific subclasses from db_wbx_base_{tx,rx}
80 def __init__(self, usrp, which):
82 @param usrp: instance of usrp.source_c
83 @param which: which side: 0 or 1 corresponding to side A or B respectively
86 # sets _u _which _tx and _slot
87 db_base.db_base.__init__(self, usrp, which)
90 self.spi_format = usrp1.SPI_FMT_MSB | usrp1.SPI_FMT_HDR_0
92 # FIXME -- the write reg functions don't work with 0xffff for masks
93 self._rx_write_oe(int(PLL_ENABLE|MReset|SELA0|SELA1|SELB0|SELB1|RX2_RX1N|RXENABLE), 0x7fff)
94 self._rx_write_io((PLL_ENABLE|MReset|0|RXENABLE), (PLL_ENABLE|MReset|RX2_RX1N|RXENABLE))
96 self._tx_write_oe((TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA), 0x7fff)
97 self._tx_write_io((0|RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA)) # TX off, TR switch set to RX
99 self.spi_enable = (SPI_ENABLE_RX_A, SPI_ENABLE_RX_B)[which]
101 self.set_auto_tr(False)
104 # title = "FlexRF Debug Rx"
106 # title = "FlexRF Debug Tx"
107 # self.gui = flexrf_debug_gui.flexrf_debug_gui(self, title)
108 # self.gui.Show(True)
112 #self._u.write_io(self._which, self.power_off, POWER_UP) # turn off power to board
113 #self._u._write_oe(self._which, 0, 0xffff) # turn off all outputs
114 self.set_auto_tr(False)
116 def _lock_detect(self):
118 @returns: the value of the VCO/PLL lock detect bit.
121 if self._rx_read_io() & PLL_LOCK_DETECT:
123 else: # Give it a second chance
124 if self._rx_read_io() & PLL_LOCK_DETECT:
129 # Both sides need access to the Rx pins.
130 # Write them directly, bypassing the convenience routines.
131 # (Sort of breaks modularity, but will work...)
133 def _tx_write_oe(self, value, mask):
134 return self._u._write_fpga_reg((FR_OE_0, FR_OE_2)[self._which],
135 ((mask & 0xffff) << 16) | (value & 0xffff))
137 def _rx_write_oe(self, value, mask):
138 return self._u._write_fpga_reg((FR_OE_1, FR_OE_3)[self._which],
139 ((mask & 0xffff) << 16) | (value & 0xffff))
141 def _tx_write_io(self, value, mask):
142 return self._u._write_fpga_reg((FR_IO_0, FR_IO_2)[self._which],
143 ((mask & 0xffff) << 16) | (value & 0xffff))
145 def _rx_write_io(self, value, mask):
146 return self._u._write_fpga_reg((FR_IO_1, FR_IO_3)[self._which],
147 ((mask & 0xffff) << 16) | (value & 0xffff))
149 def _rx_read_io(self):
150 t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
151 return (t >> 16) & 0xffff
153 def _tx_read_io(self):
154 t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
158 def _compute_regs(self, freq):
160 Determine values of registers, along with actual freq.
162 @param freq: target frequency in Hz
164 @returns: (R, N, func, init, actual_freq)
165 @rtype: tuple(int, int, int, int, float)
167 Override this in derived classes.
169 raise NotImplementedError
171 def _refclk_freq(self):
172 return float(self._u.fpga_master_clock_freq())/self._refclk_divisor()
174 def _refclk_divisor(self):
176 Return value to stick in REFCLK_DIVISOR register
180 # ----------------------------------------------------------------
182 def set_freq(self, freq):
184 @returns (ok, actual_baseband_freq) where:
185 ok is True or False and indicates success or failure,
186 actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
188 raise NotImplementedError
190 def gain_range(self):
192 Return range of gain that can be set by this d'board.
194 @returns (min_gain, max_gain, step_size)
195 Where gains are expressed in decibels (your mileage may vary)
197 raise NotImplementedError
199 def set_gain(self, gain):
203 @param gain: gain in decibels
206 raise NotImplementedError
208 def _set_pga(self, pga_gain):
209 if(self._which == 0):
210 self._u.set_pga (0, pga_gain)
211 self._u.set_pga (1, pga_gain)
213 self._u.set_pga (2, pga_gain)
214 self._u.set_pga (3, pga_gain)
216 def is_quadrature(self):
218 Return True if this board requires both I & Q analog channels.
220 This bit of info is useful when setting up the USRP Rx mux register.
224 # ----------------------------------------------------------------
226 class wbx_base_tx(wbx_base):
227 def __init__(self, usrp, which):
229 @param usrp: instance of usrp.sink_c
230 @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
232 wbx_base.__init__(self, usrp, which)
234 # power up the transmit side, NO -- but set antenna to receive
235 self._u.write_io(self._which, (TX_POWER), (TX_POWER|RX_TXN))
236 self._lo_offset = 0e6
238 # Gain is not set by the PGA, but the PGA must be set at max gain in the TX
239 return self._set_pga(self._u.pga_max())
242 # Power down and leave the T/R switch in the R position
243 self._u.write_io(self._which, (RX_TXN), (TX_POWER|RX_TXN|TX_ENB_MIX|TX_ENB_VGA))
244 wbx_base.__del__(self)
246 def set_auto_tr(self, on):
248 self.set_atr_mask (RX_TXN)
249 self.set_atr_txval(0)
250 self.set_atr_rxval(RX_TXN)
252 self.set_atr_mask (0)
253 self.set_atr_txval(0)
254 self.set_atr_rxval(0)
256 def set_enable(self, on):
258 Enable transmitter if on is True
260 mask = RX_TXN|TX_ENB_MIX|TX_ENB_VGA
263 self._u.write_io(self._which, TX_ENB_MIX|TX_ENB_VGA, mask)
265 self._u.write_io(self._which, RX_TXN, mask)
268 def set_lo_offset(self, offset):
270 Set amount by which LO is offset from requested tuning frequency.
272 @param offset: offset in Hz
274 self._lo_offset = offset
278 Get amount by which LO is offset from requested tuning frequency.
280 @returns Offset in Hz
282 return self._lo_offset
284 class wbx_base_rx(wbx_base):
285 def __init__(self, usrp, which):
287 @param usrp: instance of usrp.source_c
288 @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
290 wbx_base.__init__(self, usrp, which)
292 # set up for RX on TX/RX port
293 self.select_rx_antenna('TX/RX')
295 self.bypass_adc_buffers(True)
297 self._lo_offset = 0.0
301 self._u.write_io(self._which, 0, (RXENABLE))
302 wbx_base.__del__(self)
304 def set_auto_tr(self, on):
306 self.set_atr_mask (ENABLE)
307 self.set_atr_txval( 0)
308 self.set_atr_rxval(ENABLE)
310 self.set_atr_mask (0)
311 self.set_atr_txval(0)
312 self.set_atr_rxval(0)
314 def select_rx_antenna(self, which_antenna):
316 Specify which antenna port to use for reception.
317 @param which_antenna: either 'TX/RX' or 'RX2'
319 if which_antenna in (0, 'TX/RX'):
320 self._u.write_io(self._which, 0, RX2_RX1N)
321 elif which_antenna in (1, 'RX2'):
322 self._u.write_io(self._which, RX2_RX1N, RX2_RX1N)
324 raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'"
326 def set_gain(self, gain):
330 @param gain: gain in decibels
333 maxgain = self.gain_range()[1] - self._u.pga_max()
334 mingain = self.gain_range()[0]
336 pga_gain = gain-maxgain
337 assert pga_gain <= self._u.pga_max()
345 dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
346 assert dac_value>=0 and dac_value<4096
347 return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \
348 self._set_pga(int(pga_gain))
350 def set_lo_offset(self, offset):
352 Set amount by which LO is offset from requested tuning frequency.
354 @param offset: offset in Hz
356 self._lo_offset = offset
360 Get amount by which LO is offset from requested tuning frequency.
362 @returns Offset in Hz
364 return self._lo_offset
367 def i_and_q_swapped(self):
369 Return True if this is a quadrature device and ADC 0 is Q.
373 # ----------------------------------------------------------------
375 class _ADF410X_common(object):
377 # R-Register Common Values
378 self.R_RSV = 0 # bits 23,22,21
379 self.LDP = 1 # bit 20 Lock detect in 5 cycles
380 self.TEST = 0 # bit 19,18 Normal
381 self.ABP = 0 # bit 17,16 2.9ns
383 # N-Register Common Values
384 self.N_RSV = 0 # 23,22
385 self.CP_GAIN = 0 # 21
387 # Function Register Common Values
388 self.P = 0 # bits 23,22 0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65
389 self.PD2 = 0 # bit 21 Normal operation
390 self.CP2 = 4 # bits 20,19,18 CP Gain = 5mA
391 self.CP1 = 4 # bits 17,16,15 CP Gain = 5mA
392 self.TC = 0 # bits 14-11 PFD Timeout
393 self.FL = 0 # bit 10,9 Fastlock Disabled
394 self.CP3S = 0 # bit 8 CP Enabled
395 self.PDP = 0 # bit 7 Phase detector polarity, Positive=1
396 self.MUXOUT = 1 # bits 6:4 Digital Lock Detect
397 self.PD1 = 0 # bit 3 Normal operation
398 self.CR = 0 # bit 2 Normal operation
400 def _compute_regs(self, freq):
402 Determine values of R, control, and N registers, along with actual freq.
404 @param freq: target frequency in Hz
406 @returns: (R, N, control, actual_freq)
407 @rtype: tuple(int, int, int, float)
410 # Band-specific N-Register Values
411 phdet_freq = self._refclk_freq()/self.R_DIV
412 print "phdet_freq = %f" % (phdet_freq,)
413 desired_n = round(freq*self.freq_mult/phdet_freq)
414 print "desired_n %f" % (desired_n,)
415 actual_freq = desired_n * phdet_freq
416 print "actual freq %f" % (actual_freq,)
417 B = math.floor(desired_n/self._prescaler())
418 A = desired_n - self._prescaler()*B
419 print "A %d B %d" % (A,B)
420 self.B_DIV = int(B) # bits 20:8
421 self.A_DIV = int(A) # bit 6:2
422 #assert self.B_DIV >= self.A_DIV
423 if self.B_DIV < self.A_DIV:
425 R = (self.R_RSV<<21) | (self.LDP<<20) | (self.TEST<<18) | \
426 (self.ABP<<16) | (self.R_DIV<<2)
428 N = (self.N_RSV<<22) | (self.CP_GAIN<<21) | (self.B_DIV<<8) | (self.A_DIV<<2)
430 control = (self.P<<22) | (self.PD2<<21) | (self.CP2<<18) | (self.CP1<<15) | \
431 (self.TC<<11) | (self.FL<<9) | (self.CP3S<<8) | (self.PDP<<7) | \
432 (self.MUXOUT<<4) | (self.PD1<<3) | (self.CR<<2)
434 return (R,N,control,actual_freq/self.freq_mult)
436 def _write_all(self, R, N, control):
438 Write all PLL registers:
444 Adds 10ms delay between writing control and N if this is first call.
445 This is the required power-up sequence.
447 @param R: 24-bit R counter latch
449 @param N: 24-bit N counter latch
451 @param control: 24-bit control latch
455 self._write_func(control)
456 self._write_init(control)
462 def _write_R(self, R):
463 self._write_it((R & ~0x3) | 0)
465 def _write_N(self, N):
466 self._write_it((N & ~0x3) | 1)
468 def _write_func(self, func):
469 self._write_it((func & ~0x3) | 2)
471 def _write_init(self, init):
472 self._write_it((init & ~0x3) | 3)
474 def _write_it(self, v):
475 s = ''.join((chr((v >> 16) & 0xff),
476 chr((v >> 8) & 0xff),
478 self._u._write_spi(0, self.spi_enable, self.spi_format, s)
480 def _prescaler(self):
490 raise ValueError, "Prescaler out of range"
492 #----------------------------------------------------------------------
493 class _lo_common(_ADF410X_common):
495 _ADF410X_common.__init__(self)
497 # Band-specific R-Register Values
498 self.R_DIV = 4 # bits 15:2
500 # Band-specific C-Register values
501 self.P = 0 # bits 23,22 0 = Div by 8/9
502 self.CP2 = 4 # bits 19:17
503 self.CP1 = 4 # bits 16:14
505 # Band specifc N-Register Values
506 self.DIVSEL = 0 # bit 23
507 self.DIV2 = 0 # bit 22
508 self.CPGAIN = 0 # bit 21
514 def freq_range(self): # FIXME
515 return (50e6, 1000e6, 16e6)
517 def set_divider(self, main_or_aux, divisor):
518 if main_or_aux not in (0, 'main', 1, 'aux'):
519 raise ValueError, "main_or_aux must be 'main' or 'aux'"
520 if main_or_aux in (0, 'main'):
521 if divisor not in (1,2,4,8):
522 raise ValueError, "Main Divider Must be 1, 2, 4, or 8"
523 for (div,val) in ((1,0),(2,1),(4,2),(8,3)):
527 if divisor not in (2,4,8,16):
528 raise ValueError, "Aux Divider Must be 2, 4, 8 or 16"
529 for (div,val) in ((2,0),(4,1),(8,2),(16,3)):
533 vala = self.main_div*SELA0
534 valb = self.aux_div*SELB0
535 mask = SELA0|SELA1|SELB0|SELB1
537 self._rx_write_io(((self.main_div*SELA0) | (self.aux_div*SELB0)),
538 (SELA0|SELA1|SELB0|SELB1))
540 def set_freq(self, freq):
541 #freq += self._lo_offset
543 if(freq < 20e6 or freq > 1200e6):
544 raise ValueError, "Requested frequency out of range"
547 while lo_freq < 1e9 and div < 8:
549 lo_freq = lo_freq * 2
550 print "For RF freq of %f, we set DIV=%d and LO Freq=%f" % (freq, div, lo_freq)
551 self.set_divider('main', div)
552 self.set_divider('aux', div*2)
554 R, N, control, actual_freq = self._compute_regs(lo_freq)
555 print "R %d N %d control %d actual freq %f" % (R,N,control,actual_freq)
558 self._write_all(R, N, control)
559 return (self._lock_detect(), actual_freq/div/2)
562 #------------------------------------------------------------
563 class db_wbx_lo_tx(_lo_common, wbx_base_tx):
564 def __init__(self, usrp, which):
565 wbx_base_tx.__init__(self, usrp, which)
566 _lo_common.__init__(self)
568 def gain_range(self):
570 Return range of gain that can be set by this d'board.
572 @returns (min_gain, max_gain, step_size)
573 Where gains are expressed in decibels (your mileage may vary)
575 Gain is controlled by a VGA in the output amplifier, not the PGA
579 def set_gain(self, gain):
583 @param gain: gain in decibels
586 maxgain = self.gain_range()[1]
587 mingain = self.gain_range()[0]
598 dac_value = ((txvga_gain-mingain)*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
599 assert dac_value>=0 and dac_value<4096
600 print "DAC value %d" % (dac_value,)
601 return self._u.write_aux_dac(self._which, 1, int(dac_value))
603 class db_wbx_lo_rx(_lo_common, wbx_base_rx):
604 def __init__(self, usrp, which):
605 wbx_base_rx.__init__(self, usrp, which)
606 _lo_common.__init__(self)
608 def gain_range(self):
610 Return range of gain that can be set by this d'board.
612 @returns (min_gain, max_gain, step_size)
613 Where gains are expressed in decibels (your mileage may vary)
615 return (self._u.pga_min(), self._u.pga_max() + 45, 0.05)
617 #------------------------------------------------------------
618 # hook these daughterboard classes into the auto-instantiation framework
619 db_instantiator.add(usrp_dbid.WBX_LO_TX, lambda usrp, which : (db_wbx_lo_tx(usrp, which),))
620 db_instantiator.add(usrp_dbid.WBX_LO_RX, lambda usrp, which : (db_wbx_lo_rx(usrp, which),))