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 figure our right answer here...
91 #self._u._tx_write_oe(0, 0xffff) # turn off all outputs
92 #self._u._rx_write_oe(0, 0xffff) # turn off all outputs
94 self.set_auto_tr(False)
97 # title = "FlexRF Debug Rx"
99 # title = "FlexRF Debug Tx"
100 # self.gui = flexrf_debug_gui.flexrf_debug_gui(self, title)
101 # self.gui.Show(True)
105 #self._u.write_io(self._which, self.power_off, POWER_UP) # turn off power to board
106 #self._u._write_oe(self._which, 0, 0xffff) # turn off all outputs
107 self.set_auto_tr(False)
109 def _lock_detect(self):
111 @returns: the value of the VCO/PLL lock detect bit.
114 if self._u.rx_read_io(self._which) & PLL_LOCK_DETECT:
116 else: # Give it a second chance
117 if self._u.read_io(self._which) & PLL_LOCK_DETECT:
122 # Both sides need access to the Rx pins.
123 # Write them directly, bypassing the convenience routines.
124 # (Sort of breaks modularity, but will work...)
126 def _tx_write_oe(self, value, mask):
127 return self._u._write_fpga_reg((FR_OE_0, FR_OE_2)[self._which],
128 ((mask & 0xffff) << 16) | (value & 0xffff))
130 def _rx_write_oe(self, value, mask):
131 return self._u._write_fpga_reg((FR_OE_1, FR_OE_3)[self._which],
132 ((mask & 0xffff) << 16) | (value & 0xffff))
134 def _tx_write_io(self, value, mask):
135 return self._u._write_fpga_reg((FR_IO_0, FR_IO_2)[self._which],
136 ((mask & 0xffff) << 16) | (value & 0xffff))
138 def _rx_write_io(self, value, mask):
139 return self._u._write_fpga_reg((FR_IO_1, FR_IO_3)[self._which],
140 ((mask & 0xffff) << 16) | (value & 0xffff))
142 def _rx_read_io(self):
143 t = self._u._read_fpga_reg((FR_RB_IO_RX_A_IO_TX_A, FR_RB_IO_RX_B_IO_TX_B)[self._which])
144 return (t >> 16) & 0xffff
146 def _tx_read_io(self):
147 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 def _compute_regs(self, freq):
153 Determine values of registers, along with actual freq.
155 @param freq: target frequency in Hz
157 @returns: (R, N, func, init, actual_freq)
158 @rtype: tuple(int, int, int, int, float)
160 Override this in derived classes.
162 raise NotImplementedError
164 def _refclk_freq(self):
165 return float(self._u.fpga_master_clock_freq())/self._refclk_divisor()
167 def _refclk_divisor(self):
169 Return value to stick in REFCLK_DIVISOR register
173 # ----------------------------------------------------------------
175 def set_freq(self, freq):
177 @returns (ok, actual_baseband_freq) where:
178 ok is True or False and indicates success or failure,
179 actual_baseband_freq is the RF frequency that corresponds to DC in the IF.
181 raise NotImplementedError
183 def gain_range(self):
185 Return range of gain that can be set by this d'board.
187 @returns (min_gain, max_gain, step_size)
188 Where gains are expressed in decibels (your mileage may vary)
190 raise NotImplementedError
192 def set_gain(self, gain):
196 @param gain: gain in decibels
199 raise NotImplementedError
201 def _set_pga(self, pga_gain):
202 if(self._which == 0):
203 self._u.set_pga (0, pga_gain)
204 self._u.set_pga (1, pga_gain)
206 self._u.set_pga (2, pga_gain)
207 self._u.set_pga (3, pga_gain)
209 def is_quadrature(self):
211 Return True if this board requires both I & Q analog channels.
213 This bit of info is useful when setting up the USRP Rx mux register.
217 # ----------------------------------------------------------------
219 class wbx_base_tx(wbx_base):
220 def __init__(self, usrp, which):
222 @param usrp: instance of usrp.sink_c
223 @param which: 0 or 1 corresponding to side TX_A or TX_B respectively.
225 wbx_base.__init__(self, usrp, which)
226 self.spi_enable = (SPI_ENABLE_TX_A, SPI_ENABLE_TX_B)[which]
228 # power up the transmit side, but set antenna to receive
229 self._u._write_oe(self._which,(TX_POWER|RX_TXN), 0xffff)
230 self._u.write_io(self._which, (TX_POWER|RX_TXN), (TX_POWER|RX_TXN))
231 self._lo_offset = 0e6
233 # Gain is not set by the PGA, but the PGA must be set at max gain in the TX
234 return self._set_pga(self._u.pga_max())
237 # Power down and leave the T/R switch in the R position
238 self._u.write_io(self._which, (RX_TXN), (TX_POWER|RX_TXN))
239 wbx_base.__del__(self)
241 def set_auto_tr(self, on):
243 self.set_atr_mask (RX_TXN)
244 self.set_atr_txval(0)
245 self.set_atr_rxval(RX_TXN)
247 self.set_atr_mask (0)
248 self.set_atr_txval(0)
249 self.set_atr_rxval(0)
251 def set_enable(self, on):
253 Enable transmitter if on is True
259 self._u.write_io(self._which, v, RX_TXN)
261 def set_lo_offset(self, offset):
263 Set amount by which LO is offset from requested tuning frequency.
265 @param offset: offset in Hz
267 self._lo_offset = offset
271 Get amount by which LO is offset from requested tuning frequency.
273 @returns Offset in Hz
275 return self._lo_offset
277 class wbx_base_rx(wbx_base):
278 def __init__(self, usrp, which):
280 @param usrp: instance of usrp.source_c
281 @param which: 0 or 1 corresponding to side RX_A or RX_B respectively.
283 wbx_base.__init__(self, usrp, which)
284 self.spi_enable = (SPI_ENABLE_RX_A, SPI_ENABLE_RX_B)[which]
286 self._u._write_oe(self._which, (RX2_RX1N|RXENABLE), 0xffff)
287 self._u.write_io(self._which, (0|RXENABLE), (RX2_RX1N|RXENABLE))
289 # set up for RX on TX/RX port
290 self.select_rx_antenna('TX/RX')
292 self.bypass_adc_buffers(True)
294 self._lo_offset = -4e6
298 self._u.write_io(self._which, 0, (RXENABLE))
299 wbx_base.__del__(self)
301 def set_auto_tr(self, on):
303 self.set_atr_mask (ENABLE)
304 self.set_atr_txval( 0)
305 self.set_atr_rxval(ENABLE)
307 self.set_atr_mask (0)
308 self.set_atr_txval(0)
309 self.set_atr_rxval(0)
311 def select_rx_antenna(self, which_antenna):
313 Specify which antenna port to use for reception.
314 @param which_antenna: either 'TX/RX' or 'RX2'
316 if which_antenna in (0, 'TX/RX'):
317 self._u.write_io(self._which, 0, RX2_RX1N)
318 elif which_antenna in (1, 'RX2'):
319 self._u.write_io(self._which, RX2_RX1N, RX2_RX1N)
321 raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'"
323 def set_gain(self, gain):
327 @param gain: gain in decibels
330 maxgain = self.gain_range()[1] - self._u.pga_max()
331 mingain = self.gain_range()[0]
333 pga_gain = gain-maxgain
334 assert pga_gain <= self._u.pga_max()
342 dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
343 assert dac_value>=0 and dac_value<4096
344 return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \
345 self._set_pga(int(pga_gain))
347 def set_lo_offset(self, offset):
349 Set amount by which LO is offset from requested tuning frequency.
351 @param offset: offset in Hz
353 self._lo_offset = offset
357 Get amount by which LO is offset from requested tuning frequency.
359 @returns Offset in Hz
361 return self._lo_offset
364 # ----------------------------------------------------------------
366 class _ADF410X_common(object):
368 # R-Register Common Values
369 self.R_RSV = 0 # bits 23,22,21
370 self.LDP = 1 # bit 20 Lock detect in 5 cycles
371 self.TEST = 0 # bit 19,18 Normal
372 self.ABP = 0 # bit 17,16 2.9ns
374 # N-Register Common Values
375 self.N_RSV = 0 # 23,22
376 self.CP_GAIN = 0 # 21
378 # Function Register Common Values
379 self.P = 0 # bits 23,22 0 = 8/9, 1 = 16/17, 2 = 32/33, 3 = 64/65
380 self.PD2 = 0 # bit 21 Normal operation
381 self.CP2 = 7 # bits 20,19,18 CP Gain = 5mA
382 self.CP1 = 7 # bits 17,16,15 CP Gain = 5mA
383 self.TC = 0 # bits 14-11 PFD Timeout
384 self.FL = 0 # bit 10,9 Fastlock Disabled
385 self.CP3S = 0 # bit 8 CP Enabled
386 self.PDP = 1 # bit 7 Phase detector polarity, Positive=1
387 self.MUXOUT = 1 # bits 6:4 Digital Lock Detect
388 self.PD1 = 0 # bit 3 Normal operation
389 self.CR = 0 # bit 2 Normal operation
391 def _compute_regs(self, freq):
393 Determine values of R, control, and N registers, along with actual freq.
395 @param freq: target frequency in Hz
397 @returns: (R, control, N, actual_freq)
398 @rtype: tuple(int, int, int, float)
401 # Band-specific N-Register Values
402 phdet_freq = self._refclk_freq()/self.R_DIV
403 desired_n = round(freq*self.freq_mult/phdet_freq)
404 actual_freq = desired_n * phdet_freq
405 B = math.floor(desired_n/self._prescaler())
406 A = desired_n - self._prescaler()*B
407 self.B_DIV = int(B) # bits 20:8
408 self.A_DIV = int(A) # bit 6:2
409 #assert self.B_DIV >= self.A_DIV
410 if self.B_DIV < self.A_DIV:
412 R = (self.R_RSV<<21) | (self.LDP<<20) | (self.TEST<<18) | \
413 (self.ABP<<16) | (self.R_DIV<<2)
415 N = (self.N_RSV<<22) | (self.CP_GAIN<<21) | (self.B_DIV<<8) | (self.A_DIV<<2)
417 control = (self.P<<22) | (self.PD2<<21) | (self.CP2<<18) | (self.CP1<<15) | \
418 (self.TC<<11) | (self.FL<<9) | (self.CP3S<<8) | (self.PDP<<7) | \
419 (self.MUXOUT<<4) | (self.PD1<<3) | (self.CR<<2)
421 return (R,N,control,actual_freq/self.freq_mult)
423 def _write_all(self, R, N, control):
425 Write all PLL registers:
431 Adds 10ms delay between writing control and N if this is first call.
432 This is the required power-up sequence.
434 @param R: 24-bit R counter latch
436 @param N: 24-bit N counter latch
438 @param control: 24-bit control latch
442 self._write_func(func)
443 self._write_init(init)
449 def _write_R(self, R):
450 self._write_it((R & ~0x3) | 0)
452 def _write_N(self, N):
453 self._write_it((N & ~0x3) | 1)
455 def _write_func(self, func):
456 self._write_it((func & ~0x3) | 2)
458 def _write_init(self, init):
459 self._write_it((func & ~0x3) | 3)
461 def _write_it(self, v):
462 s = ''.join((chr((v >> 16) & 0xff),
463 chr((v >> 8) & 0xff),
465 self._u._write_spi(0, self.spi_enable, self.spi_format, s)
467 def _prescaler(self):
475 #----------------------------------------------------------------------
476 class _lo_common(_ADF410X_common):
478 _ADF410X_common.__init__(self)
480 # Band-specific R-Register Values
481 self.R_DIV = 16 # bits 15:2
483 # Band-specific C-Register values
484 self.P = 1 # bits 23,22 Div by 16/17
485 self.CP2 = 7 # bits 19:17
486 self.CP1 = 7 # bits 16:14
488 # Band specifc N-Register Values
489 self.DIVSEL = 0 # bit 23
490 self.DIV2 = 0 # bit 22
491 self.CPGAIN = 0 # bit 21
494 def freq_range(self): # FIXME
495 return (50e6, 1000e6, 16e6)
497 def set_divider(self, main_or_aux, divisor):
498 if main_or_aux not in (0, 'main', 1, 'aux'):
499 raise ValueError, "main_or_aux must be 'main' or 'aux'"
500 if main_or_aux in (0, 'main'):
501 if value not in (1,2,4,8):
502 raise ValueError, "Main Divider Must be 1, 2, 4, or 8"
503 for (div,val) in ((1,0),(2,1),(4,2),(8,3)):
507 if value not in (2,4,8,18):
508 raise ValueError, "Aux Divider Must be 2, 4, 8 or 16"
509 for (div,val) in ((2,0),(4,1),(8,2),(16,3)):
513 self._u._rx_write_io(self._which, ((self.main_div<<SELA0) | (self.aux_div<<SELB0)),
514 (SELA0|SELA1|SELB0|SELB1)) # only works on RX
516 def set_freq(self, freq):
517 #freq += self._lo_offset
519 if(freq < 20e6 or freq > 1200e6):
520 raise ValueError, "Requested frequency out of range"
523 while freq < 1e9 and div < 8:
525 lo_freq = lo_freq * 2
526 print "For RF freq of %f, we set DIV=%d and LO Freq=%f" % (freq, div, lo_freq)
527 self.set_divider('main', div)
528 self.set_divider('aux', 2)
530 R, N, control, actual_freq = self._compute_regs(freq)
533 self._write_all(R, N, control)
534 return (self._lock_detect(), actual_freq)
537 #------------------------------------------------------------
538 class db_wbx_lo_tx(_lo_common, wbx_base_tx):
539 def __init__(self, usrp, which):
540 self.power_on = ~POWER_UP
541 self.power_off = ~POWER_UP # powering it off kills the serial bus
542 wbx_base_tx.__init__(self, usrp, which)
543 _lo_common.__init__(self)
545 def gain_range(self):
547 Return range of gain that can be set by this d'board.
549 @returns (min_gain, max_gain, step_size)
550 Where gains are expressed in decibels (your mileage may vary)
552 Gain is controlled by a VGA in the output amplifier, not the PGA
556 def set_gain(self, gain):
560 @param gain: gain in decibels
563 maxgain = self.gain_range()[1]
564 mingain = self.gain_range()[0]
572 dac_value = (txvga_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale
573 assert dac_value>=0 and dac_value<4096
574 return self._u.write_aux_dac(self._which, 1, int(dac_value))
576 class db_wbx_lo_rx(_lo_common, wbx_base_rx):
577 def __init__(self, usrp, which):
578 self.power_on = ~POWER_UP
579 self.power_off = ~POWER_UP # Powering it off kills the serial bus
580 wbx_base_rx.__init__(self, usrp, which)
581 _lo_common.__init__(self)
583 def gain_range(self):
585 Return range of gain that can be set by this d'board.
587 @returns (min_gain, max_gain, step_size)
588 Where gains are expressed in decibels (your mileage may vary)
590 return (self._u.pga_min(), self._u.pga_max() + 45, 0.05)
592 #------------------------------------------------------------
593 # hook these daughterboard classes into the auto-instantiation framework
594 db_instantiator.add(usrp_dbid.WBX_LO_TX, lambda usrp, which : (db_wbx_lo_tx(usrp, which),))
595 db_instantiator.add(usrp_dbid.WBX_LO_RX, lambda usrp, which : (db_wbx_lo_rx(usrp, which),))