diff options
161 files changed, 10139 insertions, 625 deletions
diff --git a/firmware/octoclock/Makefile b/firmware/octoclock/Makefile new file mode 100644 index 000000000..6ef662bc6 --- /dev/null +++ b/firmware/octoclock/Makefile @@ -0,0 +1,56 @@ +# +# Copyright 2009 Ettus Research LLC +# + +################################################## +# Compiler +################################################## +CC = avr-gcc +OBJCOPY = avr-objcopy +STRIP = avr-strip +OBJDUMP = avr-objdump +SREC = srec_cat +#CFLAGS = -O2 -std=gnu99 -fshort-enums -pedantic-errors -Wall -Werror \ +# -Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow +CFLAGS = -std=gnu99 -O2 + +#-D IO_DEBUG + +################################################## +# Files +################################################## +HDRS = OctoClock-io.h +SRCS = OctoClock.c +TARGET = octoclock_fw + +################################################## +# Device +################################################## +MMCU = atmega128 +PROGRAMMER = avrisp2 +PORT = usb +AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT) + +################################################## +# Global Targets +################################################## +all: $(TARGET).hex + +clean: + $(RM) *.o *.elf *.hex + +install: all + $(AVRDUDE) -U flash:w:$(TARGET).hex:i + +################################################## +# Dependency Targets +################################################## + +$(TARGET).hex: $(TARGET).elf + $(OBJCOPY) -O ihex $< $@ + +$(TARGET).elf: $(SRCS:.c=.o) + $(CC) -mmcu=$(MMCU) $^ -o $@ + +%.o: %.c $(HDRS) Makefile + $(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS) diff --git a/firmware/octoclock/OctoClock-io.h b/firmware/octoclock/OctoClock-io.h new file mode 100644 index 000000000..88cd1499b --- /dev/null +++ b/firmware/octoclock/OctoClock-io.h @@ -0,0 +1,70 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#ifndef IO_H +#define IO_H + +#include <stdint.h> +#include <stdbool.h> + + +typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+
+typedef float f32;
+typedef double f64;
+typedef long double f128;
+
+typedef int error_indicator; /* A type for functions, usually... */
+typedef unsigned int boolean; /* "natural" type */
+
+typedef char c8; /* 8-bit character */
+typedef unsigned char uc8; /* 8-bit character, unsigned*/
+/* it is sometimes useful to*/
+/* make the distinction*/
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#define FALSE (0)
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#define TRUE (!FALSE)
+
+#define is :{
+#define esac break;}
+ + +// This other crap can be deleted, below, between the #if 0 and #endif inclusive + +#if 0 + +#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin)) +#define IO_PA(pin) IO_PX('A', pin) +#define IO_PB(pin) IO_PX('B', pin) +#define IO_PC(pin) IO_PX('C', pin) +#define IO_PD(pin) IO_PX('D', pin) + +typedef const uint8_t io_pin_t; + +void io_output_pin(io_pin_t pin); +void io_input_pin(io_pin_t pin); +void io_set_pin(io_pin_t pin); +void io_clear_pin(io_pin_t pin); +bool io_test_pin(io_pin_t pin); + +#endif //if 0 + +#endif /* IO_H */ diff --git a/firmware/octoclock/OctoClock.c b/firmware/octoclock/OctoClock.c new file mode 100644 index 000000000..d81a93fc3 --- /dev/null +++ b/firmware/octoclock/OctoClock.c @@ -0,0 +1,844 @@ +/* + * OctoClock.c + * + * V1.00 -- May 2013 + * + * + * V1.03 -- Correct the switch to be UP for Internal and DOWN for External + * This means that the bat handle "points at" (sort of) the lower-left LED, which + * is the "STATUS" LED, which gets lit up when the external 10 MHz is present + * The "10 MHz Signal Detect" code accepts a very wide range of "10 MHz" signals + * 23 April 2013 + * + * + * V1.02 -- Make LEDs consistent with Chassis - Top LED is INTERNAL; middle is EXTERNAL; bottom is STATUS + * + * STATUS is ON if the 10 MHz external input is present. 19 April 2013 + * + * + * V1.01: Modify TI chip initialization to be in differentail mode + * which allows 10 MHz input down to 0.1 Volts according to the datasheet. + * + * + * New Version that supports CLOCK board Version 1.0 + * + * Author: Michael@Cheponis.Com with code borrowed liberally from + * previous AVR projects + * + */ + +/* + * Copyright 2013 Ettus Research LLC + */ + + + + + + +/* CLKSEL0 = 1 SUT1..0 is 11 CKOPT = 0 CKSEL3..1 is 111 => big output swing, long time to start up */ +/* + +NOT in M103 compatibility mode, no WDT, CKOPT full rail-to-rail xtal osc, 16K CK (16K clock cycles), +additional delay 65ms for Crystal Oscillator, slowly rising power + +Very conservative settings; if lower power osc required, change CKOPT to '1' (UNPROGRAMMED) or, if you will, +CKOPT = [ ] + + + +M103C = [ ] +WDTON = [ ] +OCDEN = [ ] +JTAGEN = [X] +SPIEN = [X] +EESAVE = [ ] +BOOTSZ = 4096W_F000 +BOOTRST = [ ] +CKOPT = [X] +BODLEVEL = 2V7 +BODEN = [ ] +SUT_CKSEL = EXTHIFXTALRES_16KCK_64MS + +EXTENDED = 0xFF (valid) +HIGH = 0x89 (valid) +LOW = 0xFF (valid) + + +*/ + +// No interrupts are required + +#include "OctoClock-io.h" + +#include <avr/io.h> +#include <avr/interrupt.h> + + +#ifdef On +#undef On +#endif + +#ifdef Off +#undef OFf +#endif + +#define Off (0) +#define On (!Off) + + +// Important for the Serial Port, not used at the moment +#define FOSC (7372800) +#define BAUD (115200) + +#define MYUBRR FOSC/16/BAUD-1 + + +#define wait() for(u16 u=14000; u; u--) asm("nop"); + + + +enum LEDs {Top,Middle,Bottom}; // Top is 0, Mid is 1, and Bottom is 2 + +void +led(enum LEDs which, int turn_it_on){ + + u8 LED = 0x20 << which; // selects the proper bit + + if(turn_it_on) + PORTC |= LED; + else + PORTC &= ~LED; + +} + + +enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; + +void setup_TI_CDCE18005(enum TI_Input_10_MHz); + + + + +/***************************************************************************************** + + SPI routines + +******************************************************************************************/ + + + +/* All macros evaluate to compile-time constants */ + +/* *** helper macros * * */ + +/* turn a numeric literal into a hex constant + (avoids problems with leading zeros) + 8-bit constants max value 0x11111111, always fits in unsigned long + */ + #define HEX__(n) 0x##n##LU + +/* 8-bit conversion function */ + #define B8__(x) ((x&0x0000000FLU)?1:0) \ + +((x&0x000000F0LU)?2:0) \ + +((x&0x00000F00LU)?4:0) \ + +((x&0x0000F000LU)?8:0) \ + +((x&0x000F0000LU)?16:0) \ + +((x&0x00F00000LU)?32:0) \ + +((x&0x0F000000LU)?64:0) \ + +((x&0xF0000000LU)?128:0) + +// Damn, that is SERIOUS magic ... ;-) Yes, I know how it works +// but it's pretty cool.... + + +/* *** user macros *** */ + +/* for upto 8-bit binary constants */ + #define Bits_8(d) ((unsigned char)B8__(HEX__(d))) + +/* for upto 16-bit binary constants, MSB first */ + #define Bits_16(dmsb,dlsb) (((unsigned short)Bits_8(dmsb)<<8) \ + + Bits_8(dlsb)) + +/* for upto 32-bit binary constants, MSB first */ + #define Bits_32(dmsb,db2,db3,dlsb) (((unsigned long)Bits_8(dmsb)<<24) \ + + ((unsigned long)Bits_8(db2)<<16) \ + + ((unsigned long)Bits_8(db3)<<8) \ + + Bits_8(dlsb)) + +/* Sample usage: + Bits_8(01010101) = 85 + Bits_16(10101010,01010101) = 43605 + Bits_32(10000000,11111111,10101010,01010101) = 2164238933 + */ + + + +enum CDCE18005 {Reg0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, Reg8_Status_Control, + Read_Command=0xE, RAM_EEPROM_Unlock=0x1F, RAM_EEPROM_Lock=0x3f} + TI_CDCE18005; + +// Table of 32-bit constants to be written to the TI chip's registers. + +// Damn, inconsistent data sheet! Special settigns see p35 of TI datasheet + +// For the GPS's 10 MHz output +u32 table_Pri_Ref[] = { + + Bits_32(1,01010100,0,0), // Reg 0 + Bits_32(1,01010100,0,0), // Outputs LVCMOS Positive&Negative Active - Non-inverted + Bits_32(1,01010100,0,0), + Bits_32(1,01010100,0,0), + Bits_32(1,01010100,0,0), // All have output divide ratio to be 1; Aux Output is OFF + + Bits_32(0,0,1001,11010100), // Reg 5 LVCMOS in; p31 of TI datasheet + + Bits_32(1,0,0010000,0), // Reg 6 // SCAS863A – NOVEMBER 2008 – REVISED JUNE 2011 + + Bits_32(1,01000000,0,0), // Reg 7 + // 76543210 + Bits_32(0,0,1,10000000) // Reg8 Status/Control +}; + + +// Looks like it's doing the correct thing re: SPI interface +// This is *definitely* AC coupled. I removed those resistors to +3.3 and ground +// signal looked no different with differential measurement. Added 240+470 to +// center tap of secondary side to bias up to approx 1.2V for proper LVDS +// +// For the External 10 MHz input LVDS with external termination -- Effectively DC coupled + +u32 table_Sec_Ref[] = { + Bits_32(0001,01010100,0,100000),// Reg 0 -- use Secondary Reference for all channels + Bits_32(0001,01010100,0,100000),// Outputs LVCMOS Positive&Negative Active - Non-inverted + Bits_32(0001,01010100,0,100000), + Bits_32(0001,01010100,0,100000), + Bits_32(0001,01010100,0,100000), + +// Bits_32(0,0,00001000,10010111), // Reg 5 LVDS with External Termination p32 of TI datasheet +// Bits_32(0,0,00001000,11010111), // Reg 5 LVDS with INTERNAL Termination p32 of TI datasheet + +// May 2013 -- Turn OFF the LVDS Safe Mode, as it supposedly causes input thresholds to be increased. + +// Bits_32(0,0,1001,10011011), // Reg 5, try again. Pretty soon, try new board... + + Bits_32(0,0,1,10011011), // Reg 5, Failsafe OFF b5.11 = 0 + + +// Bits_32(0,0,1001,11011011), // Reg 5, try again. Pretty soon, try new board... + // Try with DC input termination; bit 6 is a "1" 2013 March + // Seems to not work correctly. + + +// Bits_32(1,0,0000000,0), // Reg 6; note that 6.12 must be 1 for LVDS w/External Termination, 0 int +// Bits_32(1,0,0000000,0), // Reg 6; try Internal and DC coupling + Bits_32(1,0,10000,0), // Reg 6; try again + + Bits_32(1,01000000,0,0), + Bits_32(0,0,1,10000000) // Reg8 Status/Control +}; + +//; Table 19 conflicts with Tables 5 thru 9 - in how LVCMOS outputs are defined +// extra error in Table 9, for bits 24 and 25 +// +// Could combine these into just table[][] with 1st subscript being 0 or 1 for Primary or Secondary +// Maybe want to to that. + +int table_size = sizeof (table_Pri_Ref) / sizeof(u32); +//int table_size = 1; // Testing read and write of Register 0 -- don't want excess SPI transactions +//NOTE!!! Still need to shift left by 4 and OR in register, as defined in TI_CDCE18005 enum, above. + + +enum Levels {Lo, Hi}; + +#define CLK (PA0) // Shift by 0 bits (PA.0) +#define CE_ (PA1) // Is really the "Chip Disable" signal, as Hi disables SPI +#define MOSI (PA2) +#define MISO (PA3) +#define PD_ (PA4) +#define SYNC_ (PA5) + +void +set_bit(u8 bit_number, enum Levels bit_value){ + + if(bit_value == Hi) + PORTA |= 1<<bit_number; + else + PORTA &= ~ (1<<bit_number); +} + + +bool +get_bit(u8 bit_number){ + asm("nop"); + + u8 portA = PINA; // Maybe something is strange they way PORTA is read? +// USART_Transmit( hex_table [0xf & (portA >> 4)], Control ); +// USART_Transmit( hex_table [0xf & portA], Control ); +// USART_Transmit(CR, Control); USART_Transmit(LF,Control); + + return (portA & 1<< bit_number) > 0 ? TRUE : FALSE; + //return (portA & 8) != 0; // It's always MISO, so nail it for the moment +} + + +void +send_SPI(u32 bits){ +// Send 32 bits to TI chip, LSB first. +// Don't worry about reading any bits back at this +// time, although for production, may want to do that +// as an error-check / integrity check. + +/* +#define CLK (PA0) // Shift by 0 bits (PA.0) +#define CE_ (PA1) // Is really the "Chip Disable" signal, as Hi disables SPI +#define MOSI (PA2) +#define MISO (PA3) +#define PD_ (PA4) +#define SYNC_ (PA5) +*/ + +//Basically, when the clock is low, one can set MOSI to anything, as it's ignored. + + set_bit(CE_, Lo); // Start SPI transaction with TI chip + + for (u8 i=0; i<32; i++){ // Foreach bit we need to send + set_bit(MOSI, ((bits & (1UL<<i)) ? Hi : Lo) ); // LSB first + asm("nop"); // Need a little more delay before L->H on clock; (REALLY?) + set_bit(CLK, Hi); + set_bit(CLK, Lo); // Pulse the clock to clock in the bit + } +// USART_Transmit(CR, Control); USART_Transmit(LF,Control); + //set_bit(MOSI, Lo); // Not needed, but keeps all bits zeros except /CE when idle + set_bit(CE_, Hi); // OK, transaction is over +// USART_Transmit(CR, Control); USART_Transmit(LF,Control); +} + +// Takes about 7.6 ms to init all regs (as seen on scope) +// There is a very interesting phenomenon that is occurring --- The bit-to-bit time +// at the beginning of transmission is 15 usec. However, as the number of bits +// shifted to the left increases (as i increases in the for() loop ) +// the time between bits elongates. It's about 37 usec between bits +// 30 and 31 (the last 2 bits). It's kinda cool, because it's easy to +// know when the new word begins because the clock pulses will be +// closer together. + +// See if it checks: (15+37)/2 = 26 usec between average bits +// 32 bits * 9 words * 26 usec = 7.49 ms --- but have to add +// in the little bit of time that CE_ goes high; so 7.6 ms +// is a very reasonable number. (Assumes linear increase in +// time as the number of shifts goes up, which seems to +// work OK here.) +// +// Of course, using a table instead of doing those shifts all the +// time would fix this; but it (should not) doesn't matter for this +// SPI interface. +// +// So far, the first word looks good, and the beginning of writing +// Register 1 also looks good. +// + + + + + + +// enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; + +void +reset_TI_CDCE18005(){ +// First, reset the chip. Or, if you will, pull /SYNC low then high +set_bit(CE_, Hi); +set_bit(PD_, Lo); +wait(); // This should put the EEPROM bits into the RAM -- we don't care, but should init the chip + +set_bit(PD_, Hi); // Out of Power Down state +wait(); + +set_bit(SYNC_, Lo); +wait(); +set_bit(SYNC_, Hi); + +wait(); +// Now, by gosh, that darn chip ought to be fully enabled! +} + +void +setup_TI_CDCE18005(enum TI_Input_10_MHz which_input){ + // Send the table of data to init the clock distribution chip. Uses SPI. + u32 temp; + + //reset_TI_CDCE18005(); // This REALLY should not be necessary + + if(which_input == Primary_GPS){ + + for(u8 i=0; i<table_size; i++){ + temp = table_Pri_Ref[i]<<4; + temp |= i; + // print_u32(temp); // Debug *mac* -- correct + send_SPI(temp); // Make sure the register's address is in the LSBs + } + } + else { // is Secondary_Ext -- External 10 MHz input from SMA connector + + for(u8 i=0; i<table_size; i++){ + temp = table_Sec_Ref[i]<<4; + temp |= i; + send_SPI(temp); // Make sure the register's address is in the LSBs + } + } +} +u32 +receive_SPI(){ + u32 bits; + + bits = 0; + + set_bit(CE_, Hi); // Make sure we're inactive + set_bit(CLK, Lo); // and clk line is inactive, too + set_bit(MOSI,Lo); // Make our bit output zero, for good measure + + + set_bit(CE_, Lo); // Start SPI transaction with TI chip; MOSI is don't care + + for (u8 i=0; i<32; i++){ // Foreach bit we need to get + bits >>= 1; // get ready for next bit - NOTE: Only do this if we REALLY are putting in another bit + set_bit(CLK, Hi); // CPU is so slow, it easily meets setup & hold times + // 76543210 + if( get_bit(MISO) ) bits |= 0x80000000; // because we receive the LSB first + set_bit(CLK, Lo); // Pulse the clock to clock in the bit + } + set_bit(CE_, Hi); // OK, transaction is over + + return (u32)(bits >> 4); // Ditch the lower 4 bits, which only contain the address +} + +u32 +get_TI_CDCE18005(enum CDCE18005 which_register){ + u32 get_reg_value; + + get_reg_value = 0; + get_reg_value = (0xf0 & which_register << 4) | Read_Command; + send_SPI(get_reg_value); // This tells the TI chip to send us the reg. value requested + return receive_SPI(); +}; + + +bool +check_TI_CDCE18005(enum TI_Input_10_MHz which_input, enum CDCE18005 which_register) { + // USART_Transmit(CR, Control); USART_Transmit(LF,Control); //reset_TI_CDCE18005(); + if(which_input == Primary_GPS){ + u32 read_value = get_TI_CDCE18005(which_register); + return read_value == table_Pri_Ref[which_register]; + } + else { + u32 read_value = get_TI_CDCE18005(which_register); + return read_value == table_Sec_Ref[which_register]; + } +}; +// This could obviously be done more elegantly to share more code; but this is +// simple and easy to understand + + + + + + + + +void +Setup_Atmel_IO_Ports(){ + + +///////////////////////////////////////////////////////////////////////////// +/* + * PORT A + * + *pin# Sig Our Functional Name + * + * p51 PA0 CLK_CDCE to U205 pin 24 -- L-->H edge latches MOSI and MISO in CDCE18005 + * p50 PA1 CE_CDCE Low = Chip Enabled for SPI comm to U205 pin 25 + * p49 PA2 MOSI_CDCE Goes to CDCE18005 - U205 pin 23 + * p48 PA3 MISO_CDCE Input Comes from U205 pin 22 + * p47 PA4 PD_CDCE Low = Chip is in Power-Down state; is Hi for normal operation U205 pin 12 + * p46 PA5 SYNC_CDCE Low = Chip is sync'd with interal dividers; Hi for normal operation U205 pin 14 + * p45 PA6 PPS_SEL Low --> PPS_EXT selected; Hi -> PPS_GPS selected; to U203 pin 1 + * p44 PA7 gps_lock Input Comes from M9107 - U206 pin 3 + * + */ + +// Bit #: 76543210 +PORTA = Bits_8(00110010); // /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start +DDRA = 1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0; //// all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs + + + +///////////////////////////////////////////////////////////////////////////// +/* + * Port B + * + *pin# Sig Our Functional Name + * + * p10 PB0 Ethernet /SEN + * p11 PB1 Ethernet SCLK + * p12 PB2 Ethernet MOSI + * p13 PB3 Ethernet MISO + * p14 PB4 Not connected, set as output with value 0 + * p15 PB5 Ethernet /RESET -- Set to HI for normal use, weak input + * p16 PB6 Ethernet /WOL --- Wake on LAN -- set, weak input + * p17 PB7 Not connected, set as output with value 0 + * + */ + + + PORTB = Bits_8(01100001); // Initial Value is all zeros + DDRB = 1<<DDB2 | 1<<DDB4 | 1<<DDB7; // MOSI is an output; the Not Connected pins are also outputs + + + +///////////////////////////////////////////////////////////////////////////// +/* + * Port C + * + *pin# Sig Our Functional Name + * + * p34 PC0 Not connected, set as output with value 0 + * p35 PC1 Reference Select Switch INPUT + * p36 PC2 Not connected, set as output with value 0 + * p37 PC3 Not connected, set as output with value 0 + * p38 PC4 Not connected, set as output with value 0 + * p40 PC5 "Top LED" of D103 3-stack of green LEDs + * p41 PC6 "Middle LED" + * p43 PC7 "Bottom LED" + * + */ +PORTC = 0; // Initial Value is all zeros +DDRC = ~( 1<<DDC1 ); // All bits are outputs, except PC1. including the 5 Not Connected bits + + + +///////////////////////////////////////////////////////////////////////////// +/* + * Port D + * + *pin# Sig Our Functional Name + * + * p25 PD0 Ethernet /INT input + * p26 PD1 GPS NMEA bit, output + * p27 PD2 GPS Serial Out (RXD; INT1) INPUT + * p28 PD3 GPS Serial In (TXD) OUTPUT + * p29 PD4 GPS Present, INPUT hi = Present + * p30 PD5 Not connected, set as output with value 0 + * p31 PD6 Not connected, set as output with value 0 + * p32 PD7 Not connected, set as output with value 0 + * + */ +PORTD = 0; // Initial Value is all zeros +DDRD = 1<<DDD3; + + + +///////////////////////////////////////////////////////////////////////////// +/* + * Port E + * + *pin# Sig Dir Our Functional Name + * + * p2 PE0 In avr_rxd (Also MOSI [PDI] when used for SPI programming of the chip) + * p3 PE1 Out avr_txd (Also MISO [PDO] when used for SPI programming of the chip) + * p4 PE2 In avr_cts + * p5 PE3 Out avr_rts DUE TO MOD, make this an input, too (as we go direct GPSDO to FPGA via level translators) + * p6 PE4 In PPS_GPS + * p7 PE5 In PPS_EXT_n + * p8 PE6 In Not Connected + * p9 PE7 In Not Connected + * + */ +PORTE = 0; +DDRE = 1<<DDE1; // make outputs, set to zero. PE1 is usart0 TXD + + +///////////////////////////////////////////////////////////////////////////// +/* + * Port F + * + * Split into 2 nibbles; goes to Amp/Filter board to select ENABLE and two bits to select band + * one bit per nibble is not connected. + * + * pin Sig Dir Our Functional Name + * num + * + * p61 PF0 Out J117 pin 3 (J117 pins 1 and 2 are GND) + * p60 PF1 Out J117 pin 4 + * p59 PF2 Out J117 pin 5 + * p58 PF3 Out J117 pin 6 + * p57 PF4 Out J118 pin 3 (J118 pins 1 and 2 are GND) + * p56 PF5 Out J118 pin 4 + * p55 PF6 Out J118 pin 5 + * p54 PF7 Out J118 pin 6 + * + */ + + +PORTF = 0; // Initial Value is all zeros; be sure ENABLE bits are active high!!!! +DDRF = 0xff; // All bits are outputs + + + + +led(Middle,On); +setup_TI_CDCE18005(Primary_GPS); // 10 MHz from Internal Source + +led(Top,On); +PORTA |= (1<<PA6); // PPS from Internal source + + +} + +///////////////////////////////////////////////////////////////////////////// + +//enum TI_Input_10_MHz {Primary_GPS, Secondary_Ext}; + +//setup_TI_CDCE18005(enum TI_Input_10_MHz); + +bool Global_GPS_Present = (bool)FALSE; +bool Global_Ext_Ref_Is_Present = (bool)FALSE; // NOT PRESENT unless proven so... +// This was initially global becasue it was to be set in an interrupt routine +// But it turned out interrupts were not needed. But kept this in because +// although it's a Global, it is the only one, and it makes it easier to +// go back and use interrupts if absolutely necessary. It could be +// removed and replaced with some local variable that gets passed +// around, but, really, it seems OK to me like this. + + + +void +LEDs_Off(){ + led(Top,Off); + led(Middle,Off); + led(Bottom,Off); +} + + +void +Force_Internal(){ + // led(Middle,On); + led(Top,On); + led(Middle,Off); + led(Bottom,On); + + setup_TI_CDCE18005(Primary_GPS); + + // Set PPS to Primary (1) n.b.: "1" in general means "Internal" for all such signals + PORTA |= (1<<PA6); // PPS from Internal source +} + + +void +Force_External(){ + // led(Middle, Off); + led(Top, Off); + led(Middle, On); + led(Bottom, On); + + setup_TI_CDCE18005(Secondary_Ext); + + // Set PPS to External (0 + PORTA &= ~(1<<PA6); // PPS from External source +} + + +///////////////////////////////////////////////////////////////////////////// + +void +Prefer_Internal(){ + + if(Global_GPS_Present) + Force_Internal(); + else if(Global_Ext_Ref_Is_Present) + Force_External(); + else + LEDs_Off(); +} + + + + + +void +Prefer_External(){ // IF EXTERNAL IS OK, then do this stuff + // if external is NOT OK, then force Internal + if(Global_Ext_Ref_Is_Present) + Force_External(); + else if(Global_GPS_Present) + Force_Internal(); + else + LEDs_Off(); +} + + + +// Turns out, we don't need interrupts + + +#if 0 +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +u8 Global_Tick_Counter = (u8)0; +u8 Global_Ext_Ref_Detect_Counter = (u8)0; + +// External Reference Detect interrupt; nominally at 610 Hz (10 MHz / 2**14 ) +ISR ( _VECTOR(1)){ + asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later + + + Global_Ext_Ref_Detect_Counter++ ; // We reset this elsewhere + + asm("sei"); // Global Interrupt Enable +} + + +// Timer 0 Overflow Handler +ISR ( _VECTOR(16)){ + static u8 led_state = Off; + + asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later + + led_state = (led_state ? Off : On); + + asm("sei"); // Global Interrupt Enable +} + +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +void +Setup_Atmel_Interrupts(){ + // Timer 0 is all we need -- but simplest if both Timer 0 AND IRQ1 (ext_ref_detect 610 Hz signal) also + // Nah, don't need this... +} + +#endif + + + +bool +Check_What_Is_Present(){ + + Global_GPS_Present = (PIND & (1<<DDD4)) != 0; // See if +5 scaled to 3.3 from GPSDO is there + + + volatile u8 portE = PINE; + volatile u8 prev, now; + + prev = ( portE & (1 << DDE7) ? 1 : 0); // Get PREVIOUS state of the input + for(u16 c=1; c; c++){ + portE = PINE; + now = ( portE & (1 << DDE7) ? 1 : 0); + if(prev != now){ + Global_Ext_Ref_Is_Present = (bool)TRUE; + return (bool)TRUE; + } + } + // Else, if it didn't wiggle in that time, then it didn't wiggle + // So ext. is NOT present + + Global_Ext_Ref_Is_Present = (bool)FALSE; + return (bool)FALSE; + +} + + +bool +get_Switch_State(){ + u8 portC = PINC; + + // return (bool)(portC & (1<<DDC1) ? On : Off); + return (bool)(portC & (1<<DDC1) ? Off : On); // UP is prefer internal, + // DOWN is prefer external +} + + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// M A I N // +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + + +int +main(void){ + + bool Old_Switch_State, Current_Switch_State, Old_Global_Ext_Ref_Is_Present = FALSE; + + + + asm("cli"); // Global Interrupt Disable --- enable with SEI if desired later + + Setup_Atmel_IO_Ports(); + + // Setup_Atmel_Interrupts(); + + + /* + * DO THIS FOREVER: + * + * + * get_switch_state + * + * if SWITCH_CHANGED: + * + * + * if PREFER_INTERNAL: + * if INTERNAL_PRESENT do_internal + * else if EXTERNAL_PRESENT do_external + * else LEDs OFF + * + * if PREFER_EXTERNAL: + * if EXTERNAL_PRESENT do_external + * else if INTERNAL_PRESENT do_internal + * else LEDs OFF + * + */ + + + + + Old_Switch_State = ! get_Switch_State(); + + // Because down below, we use this to get state swap... + // So we arbitrarily set the PREVIOUS state to be the "other" state + // so that, below, we trigger what happens when the switch changes + // This first "change" is therefore artificial to keep the logic, below, cleaner + + while(TRUE) { + Check_What_Is_Present(); // Set "Global_Ext_Ref_Is_Present" and "Global_GPS_Present" + + // Off means "Prefer External" -- DOWN + // On means "Prefer Internal" -- UP + + Current_Switch_State = get_Switch_State(); + + if( (Current_Switch_State != Old_Switch_State) || + (Global_Ext_Ref_Is_Present != Old_Global_Ext_Ref_Is_Present) ) { + + Old_Switch_State = Current_Switch_State; + Old_Global_Ext_Ref_Is_Present = Global_Ext_Ref_Is_Present; + + if(Current_Switch_State == On) + Prefer_Internal(); + else + Prefer_External(); + } // if() checking for different switch status + + + } // WHILE() loop + + +} /*end "main" of Program 'OctoClock.c */ diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index cdcfc6529..d6851156a 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -52,7 +52,7 @@ SET(LIBRARY_DIR lib${LIB_SUFFIX}) SET(INCLUDE_DIR include) SET(PKG_DATA_DIR share/uhd) IF(NOT DEFINED PKG_LIB_DIR) - SET(PKG_LIB_DIR ${PKG_DATA_DIR}) + SET(PKG_LIB_DIR ${LIBRARY_DIR}/uhd) ENDIF() SET(PKG_DOC_DIR share/doc/uhd) SET(PKG_MAN_DIR share/man/man1) @@ -110,6 +110,7 @@ IF(MSVC) -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE ) + ADD_DEFINITIONS(/MP) ENDIF(MSVC) IF(CYGWIN) @@ -211,8 +212,8 @@ UHD_INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_MD5SUM "f5acbc2f795fa1acfa9ad9986d59ed3b") -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.005.003-release.zip") +SET(UHD_IMAGES_MD5SUM "41df84d53cc777560a6654595b964dde") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/master_images/archive/uhd-images_003.005.003-128-g504c2e1f.zip") ######################################################################## # Register top level components diff --git a/host/README.txt b/host/README.txt index 473e0ab17..0e8e62ac1 100644 --- a/host/README.txt +++ b/host/README.txt @@ -13,6 +13,7 @@ N210 E100 E110 B100 +B200 ############################################### # Supported USRP Daughterboards @@ -29,6 +30,7 @@ DBSRX2 TVRX TVRX2 SBX +CBX ############################################### # Documentation diff --git a/host/cmake/Modules/FindORC.cmake b/host/cmake/Modules/FindORC.cmake new file mode 100644 index 000000000..0d9fc9ca1 --- /dev/null +++ b/host/cmake/Modules/FindORC.cmake @@ -0,0 +1,36 @@ +######################################################################## +# Find the library for ORC development files +######################################################################## + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_ORC "orc-0.4") +PKG_CHECK_MODULES(PC_ORC_V4_11 "orc-0.4 > 0.4.11") + +#we are using the pkg config as a version check +#if we have pkg config, the right version must be found +#the alternative is that no pkg config orc is found +if (PC_ORC_V4_11_FOUND OR (NOT PC_ORC_FOUND AND NOT PC_ORC_V4_11_FOUND)) + +FIND_PATH( + ORC_INCLUDE_DIRS + NAMES orc/orc.h + HINTS $ENV{ORC_DIR}/include/orc-0.4 + ${PC_ORC_INCLUDEDIR} + PATHS /usr/local/include/orc-0.4 + /usr/include/orc-0.4 +) + +FIND_LIBRARY( + ORC_LIBRARIES + NAMES orc-0.4 + HINTS $ENV{ORC_DIR}/lib + ${PC_ORC_LIBDIR} + PATHS /usr/local/lib + /usr/lib +) + +endif() #both PC ORC FOUND + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ORC DEFAULT_MSG ORC_LIBRARIES ORC_INCLUDE_DIRS) +MARK_AS_ADVANCED(ORC_LIBRARIES ORC_INCLUDE_DIRS) diff --git a/host/cmake/Modules/FindUSB1.cmake b/host/cmake/Modules/FindUSB1.cmake index 21005344b..a494e1350 100644 --- a/host/cmake/Modules/FindUSB1.cmake +++ b/host/cmake/Modules/FindUSB1.cmake @@ -32,7 +32,25 @@ FIND_LIBRARY(LIBUSB_LIBRARIES PATHS /usr/local/lib /usr/lib /opt/local/lib ) +enable_language(C) #needs C support for check below +include(CheckFunctionExists) +if(LIBUSB_INCLUDE_DIRS) + set(CMAKE_REQUIRED_INCLUDES ${LIBUSB_INCLUDE_DIRS}) +endif() +if(LIBUSB_LIBRARIES) + set(CMAKE_REQUIRED_LIBRARIES ${LIBUSB_LIBRARIES}) +endif() + +CHECK_FUNCTION_EXISTS("libusb_handle_events_timeout_completed" HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) +if(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) + list(APPEND LIBUSB_DEFINITIONS "HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED=1") +endif(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) + +CHECK_FUNCTION_EXISTS("libusb_error_name" HAVE_LIBUSB_ERROR_NAME) +if(HAVE_LIBUSB_ERROR_NAME) + list(APPEND LIBUSB_DEFINITIONS "HAVE_LIBUSB_ERROR_NAME=1") +endif(HAVE_LIBUSB_ERROR_NAME) + include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIRS) MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES) - diff --git a/host/cmake/Modules/NSIS.InstallOptions.ini.in b/host/cmake/Modules/NSIS.InstallOptions.ini.in new file mode 100644 index 000000000..969a3ae52 --- /dev/null +++ b/host/cmake/Modules/NSIS.InstallOptions.ini.in @@ -0,0 +1,37 @@ +[Settings] +NumFields=4 + +[Field 1] +Type=label +Text=By default the USRP Hardware Driver (UHD) does not add its directory to the system PATH. +Left=0 +Right=-1 +Top=0 +Bottom=20 + +[Field 2] +Type=radiobutton +Text=Do not add @CPACK_PACKAGE_NAME@ to the system PATH +Left=0 +Right=-1 +Top=30 +Bottom=40 +State=1 + +[Field 3] +Type=radiobutton +Text=Add @CPACK_PACKAGE_NAME@ to the system PATH for all users +Left=0 +Right=-1 +Top=40 +Bottom=50 +State=0 + +[Field 4] +Type=radiobutton +Text=Add @CPACK_PACKAGE_NAME@ to the system PATH for current user +Left=0 +Right=-1 +Top=50 +Bottom=60 +State=0 diff --git a/host/cmake/Modules/NSIS.template.in b/host/cmake/Modules/NSIS.template.in new file mode 100644 index 000000000..3ab55d907 --- /dev/null +++ b/host/cmake/Modules/NSIS.template.in @@ -0,0 +1,955 @@ +; CPack install script designed for a nmake build + +;-------------------------------- +; You must define these values + + !define VERSION "@CPACK_PACKAGE_VERSION@" + !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" + !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" + +;-------------------------------- +;Variables + + Var MUI_TEMP + Var STARTMENU_FOLDER + Var SV_ALLUSERS + Var START_MENU + Var DO_NOT_ADD_TO_PATH + Var ADD_TO_PATH_ALL_USERS + Var ADD_TO_PATH_CURRENT_USER + Var INSTALL_DESKTOP + Var IS_DEFAULT_INSTALLDIR +;-------------------------------- +;Include Modern UI + + !include "MUI.nsh" + + ;Default installation folder + InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + +;-------------------------------- +;General + + ;Name and file + Name "USRP Hardware Driver" + OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + + ;Set compression + SetCompressor @CPACK_NSIS_COMPRESSOR@ + +@CPACK_NSIS_DEFINES@ + + !include Sections.nsh + +;--- Component support macros: --- +; The code for the add/remove functionality is from: +; http://nsis.sourceforge.net/Add/Remove_Functionality +; It has been modified slightly and extended to provide +; inter-component dependencies. +Var AR_SecFlags +Var AR_RegFlags +@CPACK_NSIS_SECTION_SELECTED_VARS@ + +; Loads the "selected" flag for the section named SecName into the +; variable VarName. +!macro LoadSectionSelectedIntoVar SecName VarName + SectionGetFlags ${${SecName}} $${VarName} + IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits +!macroend + +; Loads the value of a variable... can we get around this? +!macro LoadVar VarName + IntOp $R0 0 + $${VarName} +!macroend + +; Sets the value of a variable +!macro StoreVar VarName IntValue + IntOp $${VarName} 0 + ${IntValue} +!macroend + +!macro InitSection SecName + ; This macro reads component installed flag from the registry and + ;changes checked state of the section on the components page. + ;Input: section index constant name specified in Section command. + + ClearErrors + ;Reading component status from registry + ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" "Installed" + IfErrors "default_${SecName}" + ;Status will stay default if registry value not found + ;(component was never installed) + IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags + IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off + IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit + + ; Note whether this component was installed before + !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags + IntOp $R0 $AR_RegFlags & $AR_RegFlags + + ;Writing modified flags + SectionSetFlags ${${SecName}} $AR_SecFlags + + "default_${SecName}:" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected +!macroend + +!macro FinishSection SecName + ; This macro reads section flag set by user and removes the section + ;if it is not selected. + ;Then it writes component installed flag to registry + ;Input: section index constant name specified in Section command. + + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags + ;Checking lowest bit: + IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} + IntCmp $AR_SecFlags 1 "leave_${SecName}" + ;Section is not selected: + ;Calling Section uninstall macro and writing zero installed flag + !insertmacro "Remove_${${SecName}}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ + "Installed" 0 + Goto "exit_${SecName}" + + "leave_${SecName}:" + ;Section is selected: + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ + "Installed" 1 + + "exit_${SecName}:" +!macroend + +!macro RemoveSection SecName + ; This macro is used to call section's Remove_... macro + ;from the uninstaller. + ;Input: section index constant name specified in Section command. + + !insertmacro "Remove_${${SecName}}" +!macroend + +; Determine whether the selection of SecName changed +!macro MaybeSelectionChanged SecName + !insertmacro LoadVar ${SecName}_selected + SectionGetFlags ${${SecName}} $R1 + IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits + + ; See if the status has changed: + IntCmp $R0 $R1 "${SecName}_unchanged" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected + + IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" + !insertmacro "Deselect_required_by_${SecName}" + goto "${SecName}_unchanged" + + "${SecName}_was_selected:" + !insertmacro "Select_${SecName}_depends" + + "${SecName}_unchanged:" +!macroend +;--- End of Add/Remove macros --- + +;-------------------------------- +;Interface Settings + + !define MUI_HEADERIMAGE + !define MUI_ABORTWARNING + +;-------------------------------- +; path functions + +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 + +;---------------------------------------- +; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" +;---------------------------------------- +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 +;==================================================== +; get_NT_environment +; Returns: the selected environment +; Output : head of the stack +;==================================================== +!macro select_NT_profile UN +Function ${UN}select_NT_profile + StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single + DetailPrint "Selected environment for all users" + Push "all" + Return + environment_single: + DetailPrint "Selected environment for current user only." + Push "current" + Return +FunctionEnd +!macroend +!insertmacro select_NT_profile "" +!insertmacro select_NT_profile "un." +;---------------------------------------------------- +!define NT_current_env 'HKCU "Environment"' +!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +!ifndef WriteEnvStr_RegKey + !ifdef ALL_USERS + !define WriteEnvStr_RegKey \ + 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + !else + !define WriteEnvStr_RegKey 'HKCU "Environment"' + !endif +!endif + +; AddToPath - Adds the given dir to the search path. +; Input - head of the stack +; Note - Win9x systems requires reboot + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + + # don't add if the path doesn't exist + IfFileExists "$0\*.*" "" AddToPath_done + + ReadEnvStr $1 PATH + ; if the path is too long for a NSIS variable NSIS will return a 0 + ; length string. If we find that, then warn and skip any path + ; modification as it will trash the existing path. + StrLen $2 $1 + IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done + CheckPathLength_ShowPathWarning: + Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" + Goto AddToPath_done + CheckPathLength_Done: + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + GetFullPathName /SHORT $3 $0 + Push "$1;" + Push "$3;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$3\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + + Call IsNT + Pop $1 + StrCmp $1 1 AddToPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" a + FileSeek $1 -1 END + FileReadByte $1 $2 + IntCmp $2 26 0 +2 +2 # DOS EOF + FileSeek $1 -1 END # write over EOF + FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" + FileClose $1 + SetRebootFlag true + Goto AddToPath_done + + AddToPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto DoTrim + ReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + DoTrim: + StrCmp $1 "" AddToPath_NTdoIt + Push $1 + Call Trim + Pop $1 + StrCpy $0 "$1;$0" + AddToPath_NTdoIt: + StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $0 + Goto DoSend + WriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $0 + DoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + AddToPath_done: + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Remove a given dir from the path +; Input: head of the stack + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + IntFmt $6 "%c" 26 # DOS EOF + + Call un.IsNT + Pop $1 + StrCmp $1 1 unRemoveFromPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" r + GetTempFileName $4 + FileOpen $2 $4 w + GetFullPathName /SHORT $0 $0 + StrCpy $0 "SET PATH=%PATH%;$0" + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoop: + FileRead $1 $3 + StrCpy $5 $3 1 -1 # read last char + StrCmp $5 $6 0 +2 # if DOS EOF + StrCpy $3 $3 -1 # remove DOS EOF so we can compare + StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "" unRemoveFromPath_dosLoopEnd + FileWrite $2 $3 + Goto unRemoveFromPath_dosLoop + unRemoveFromPath_dosLoopRemoveLine: + SetRebootFlag true + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoopEnd: + FileClose $2 + FileClose $1 + StrCpy $1 $WINDIR 2 + Delete "$1\autoexec.bat" + CopyFiles /SILENT $4 "$1\autoexec.bat" + Delete $4 + Goto unRemoveFromPath_done + + unRemoveFromPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto unDoTrim + unReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + unDoTrim: + StrCpy $5 $1 1 -1 # copy last char + StrCmp $5 ";" +2 # if last char != ; + StrCpy $1 "$1;" # append ; + Push $1 + Push "$0;" + Call un.StrStr ; Find `$0;` in $1 + Pop $2 ; pos of our dir + StrCmp $2 "" unRemoveFromPath_done + ; else, it is in path + # $0 - path to add + # $1 - path var + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 # $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove + StrCpy $3 $5$6 + + StrCpy $5 $3 1 -1 # copy last char + StrCmp $5 ";" 0 +2 # if last char == ; + StrCpy $3 $3 -1 # remove last char + + StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $3 + Goto unDoSend + unWriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $3 + unDoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + unRemoveFromPath_done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Uninstall sutff +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +########################################### +# Utility Functions # +########################################### + +;==================================================== +; IsNT - Returns 1 if the current system is NT, 0 +; otherwise. +; Output: head of the stack +;==================================================== +; IsNT +; no input +; output, top of the stack = 1 if NT or 0 if not +; +; Usage: +; Call IsNT +; Pop $R0 +; ($R0 at this point is 1 or 0) + +!macro IsNT un +Function ${un}IsNT + Push $0 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 IsNT_yes + ; we are not NT. + Pop $0 + Push 0 + Return + + IsNT_yes: + ; NT!!! + Pop $0 + Push 1 +FunctionEnd +!macroend +!insertmacro IsNT "" +!insertmacro IsNT "un." + +; StrStr +; input, top of stack = string to search for +; top of stack-1 = string to search in +; output, top of stack (replaces with the portion of the string remaining) +; modifies no other variables. +; +; Usage: +; Push "this is a long ass string" +; Push "ass" +; Call StrStr +; Pop $R0 +; ($R0 at this point is "ass string") + +!macro StrStr un +Function ${un}StrStr +Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." + +Function Trim ; Added by Pelaca + Exch $R1 + Push $R2 +Loop: + StrCpy $R2 "$R1" 1 -1 + StrCmp "$R2" " " RTrim + StrCmp "$R2" "$\n" RTrim + StrCmp "$R2" "$\r" RTrim + StrCmp "$R2" ";" RTrim + GoTo Done +RTrim: + StrCpy $R1 "$R1" -1 + Goto Loop +Done: + Pop $R2 + Exch $R1 +FunctionEnd + +Function ConditionalAddToRegisty + Pop $0 + Pop $1 + StrCmp "$0" "" ConditionalAddToRegisty_EmptyString + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \ + "$1" "$0" + ;MessageBox MB_OK "Set Registry: '$1' to '$0'" + DetailPrint "Set install registry entry: '$1' to '$0'" + ConditionalAddToRegisty_EmptyString: +FunctionEnd + +;-------------------------------- + +!ifdef CPACK_USES_DOWNLOAD +Function DownloadFile + IfFileExists $INSTDIR\* +2 + CreateDirectory $INSTDIR + Pop $0 + + ; Skip if already downloaded + IfFileExists $INSTDIR\$0 0 +2 + Return + + StrCpy $1 "@CPACK_DOWNLOAD_SITE@" + + try_again: + NSISdl::download "$1/$0" "$INSTDIR\$0" + + Pop $1 + StrCmp $1 "success" success + StrCmp $1 "Cancelled" cancel + MessageBox MB_OK "Download failed: $1" + cancel: + Return + success: +FunctionEnd +!endif + +;-------------------------------- +; Installation types +@CPACK_NSIS_INSTALLATION_TYPES@ + +;-------------------------------- +; Component sections +@CPACK_NSIS_COMPONENT_SECTIONS@ + +;-------------------------------- +; Define some macro setting for the gui +@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ +@CPACK_NSIS_INSTALLER_ICON_CODE@ +@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ +@CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@ + +;-------------------------------- +;Pages + !insertmacro MUI_PAGE_WELCOME + + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" + Page custom InstallOptionsPage + !insertmacro MUI_PAGE_DIRECTORY + + ;Start Menu Folder Page Configuration + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + + @CPACK_NSIS_PAGE_COMPONENTS@ + + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + !insertmacro MUI_LANGUAGE "Albanian" + !insertmacro MUI_LANGUAGE "Arabic" + !insertmacro MUI_LANGUAGE "Basque" + !insertmacro MUI_LANGUAGE "Belarusian" + !insertmacro MUI_LANGUAGE "Bosnian" + !insertmacro MUI_LANGUAGE "Breton" + !insertmacro MUI_LANGUAGE "Bulgarian" + !insertmacro MUI_LANGUAGE "Croatian" + !insertmacro MUI_LANGUAGE "Czech" + !insertmacro MUI_LANGUAGE "Danish" + !insertmacro MUI_LANGUAGE "Dutch" + !insertmacro MUI_LANGUAGE "Estonian" + !insertmacro MUI_LANGUAGE "Farsi" + !insertmacro MUI_LANGUAGE "Finnish" + !insertmacro MUI_LANGUAGE "French" + !insertmacro MUI_LANGUAGE "German" + !insertmacro MUI_LANGUAGE "Greek" + !insertmacro MUI_LANGUAGE "Hebrew" + !insertmacro MUI_LANGUAGE "Hungarian" + !insertmacro MUI_LANGUAGE "Icelandic" + !insertmacro MUI_LANGUAGE "Indonesian" + !insertmacro MUI_LANGUAGE "Irish" + !insertmacro MUI_LANGUAGE "Italian" + !insertmacro MUI_LANGUAGE "Japanese" + !insertmacro MUI_LANGUAGE "Korean" + !insertmacro MUI_LANGUAGE "Kurdish" + !insertmacro MUI_LANGUAGE "Latvian" + !insertmacro MUI_LANGUAGE "Lithuanian" + !insertmacro MUI_LANGUAGE "Luxembourgish" + !insertmacro MUI_LANGUAGE "Macedonian" + !insertmacro MUI_LANGUAGE "Malay" + !insertmacro MUI_LANGUAGE "Mongolian" + !insertmacro MUI_LANGUAGE "Norwegian" + !insertmacro MUI_LANGUAGE "Polish" + !insertmacro MUI_LANGUAGE "Portuguese" + !insertmacro MUI_LANGUAGE "PortugueseBR" + !insertmacro MUI_LANGUAGE "Romanian" + !insertmacro MUI_LANGUAGE "Russian" + !insertmacro MUI_LANGUAGE "Serbian" + !insertmacro MUI_LANGUAGE "SerbianLatin" + !insertmacro MUI_LANGUAGE "SimpChinese" + !insertmacro MUI_LANGUAGE "Slovak" + !insertmacro MUI_LANGUAGE "Slovenian" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Thai" + !insertmacro MUI_LANGUAGE "TradChinese" + !insertmacro MUI_LANGUAGE "Turkish" + !insertmacro MUI_LANGUAGE "Ukrainian" + !insertmacro MUI_LANGUAGE "Welsh" + + +;-------------------------------- +;Reserve Files + + ;These files should be inserted before other files in the data block + ;Keep these lines before any File command + ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) + + ReserveFile "NSIS.InstallOptions.ini" + !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS + +;-------------------------------- +;Installer Sections + +Section "-Core installation" + ;Use the entire tree produced by the INSTALL target. Keep the + ;list of directories here in sync with the RMDir commands below. + SetOutPath "$INSTDIR" + @CPACK_NSIS_FULL_INSTALL@ + + ;Store installation folder + WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + Push "DisplayName" + Push "@CPACK_NSIS_DISPLAY_NAME@" + Call ConditionalAddToRegisty + Push "DisplayVersion" + Push "@CPACK_PACKAGE_VERSION@" + Call ConditionalAddToRegisty + Push "Publisher" + Push "@CPACK_PACKAGE_VENDOR@" + Call ConditionalAddToRegisty + Push "UninstallString" + Push "$INSTDIR\Uninstall.exe" + Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + + !ifdef CPACK_NSIS_ADD_REMOVE + ;Create add/remove functionality + Push "ModifyPath" + Push "$INSTDIR\AddRemove.exe" + Call ConditionalAddToRegisty + !else + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + !endif + + ; Optional registration + Push "DisplayIcon" + Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" + Call ConditionalAddToRegisty + Push "HelpLink" + Push "@CPACK_NSIS_HELP_LINK@" + Call ConditionalAddToRegisty + Push "URLInfoAbout" + Push "@CPACK_NSIS_URL_INFO_ABOUT@" + Call ConditionalAddToRegisty + Push "Contact" + Push "@CPACK_NSIS_CONTACT@" + Call ConditionalAddToRegisty + !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + + ;Create shortcuts + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" +@CPACK_NSIS_CREATE_ICONS@ +@CPACK_NSIS_CREATE_ICONS_EXTRA@ + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\USRP2 Card Burner.lnk" "$INSTDIR\lib\uhd\utils\usrp2_card_burner_gui.py" "" "" "" SW_SHOWMINIMIZED + CreateShortcut "$SMPROGRAMS\$STARTMENU_FOLDER\USRP-N2XX Net Burner.lnk" "$INSTDIR\lib\uhd\utils\usrp_n2xx_net_burner_gui.py" "" "" "" SW_SHOWMINIMIZED + CreateShortcut "$SMPROGRAMS\$STARTMENU_FOLDER\UHD Images Downloader.lnk" "$INSTDIR\lib\uhd\utils\uhd_images_downloader.py" + + ;Read a value from an InstallOptions INI file + !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" + + ; Write special uninstall registry entries + Push "StartMenu" + Push "$STARTMENU_FOLDER" + Call ConditionalAddToRegisty + Push "DoNotAddToPath" + Push "$DO_NOT_ADD_TO_PATH" + Call ConditionalAddToRegisty + Push "AddToPathAllUsers" + Push "$ADD_TO_PATH_ALL_USERS" + Call ConditionalAddToRegisty + Push "AddToPathCurrentUser" + Push "$ADD_TO_PATH_CURRENT_USER" + Call ConditionalAddToRegisty + Push "InstallToDesktop" + Push "$INSTALL_DESKTOP" + Call ConditionalAddToRegisty + + !insertmacro MUI_STARTMENU_WRITE_END + +@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ + +SectionEnd + +Section "-Add to path" + Push $INSTDIR\bin + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath + StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 + Call AddToPath + doNotAddToPath: +SectionEnd + +;-------------------------------- +; Create custom pages +Function InstallOptionsPage + !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing the USRP Hardware Driver (UHD)" + !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" + +FunctionEnd + +;-------------------------------- +; determine admin versus local install +Function un.onInit + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + Goto done + + noLM: + ;Get installation folder from registry if available + + done: + +FunctionEnd + +;--- Add/Remove callback functions: --- +!macro SectionList MacroName + ;This macro used to perform operation on multiple sections. + ;List all of your components in following manner here. +@CPACK_NSIS_COMPONENT_SECTION_LIST@ +!macroend + +Section -FinishComponents + ;Removes unselected components and writes component status to registry + !insertmacro SectionList "FinishSection" + +!ifdef CPACK_NSIS_ADD_REMOVE + ; Get the name of the installer executable + System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' + StrCpy $R3 $R0 + + ; Strip off the last 13 characters, to see if we have AddRemove.exe + StrLen $R1 $R0 + IntOp $R1 $R0 - 13 + StrCpy $R2 $R0 13 $R1 + StrCmp $R2 "AddRemove.exe" addremove_installed + + ; We're not running AddRemove.exe, so install it + CopyFiles $R3 $INSTDIR\AddRemove.exe + + addremove_installed: +!endif +SectionEnd +;--- End of Add/Remove callback functions --- + +;-------------------------------- +; Component dependencies +Function .onSelChange + !insertmacro SectionList MaybeSelectionChanged +FunctionEnd + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + ReadRegStr $START_MENU SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" + ;MessageBox MB_OK "Start menu is in: $START_MENU" + ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath" + ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers" + ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser" + ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" + ReadRegStr $INSTALL_DESKTOP SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop" + ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " + +@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ + + ;Remove files we installed. + ;Keep the list of directories here in sync with the File commands above. +@CPACK_NSIS_DELETE_FILES@ +@CPACK_NSIS_DELETE_DIRECTORIES@ + +!ifdef CPACK_NSIS_ADD_REMOVE + ;Remove the add/remove program + Delete "$INSTDIR\AddRemove.exe" +!endif + + ;Remove the uninstaller itself. + Delete "$INSTDIR\Uninstall.exe" + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + ;Remove the installation directory if it is empty. + RMDir "$INSTDIR" + + ; Remove the registry entries. + DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + ; Removes all optional components + !insertmacro SectionList "RemoveSection" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP + + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS@ +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + startMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors startMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop + startMenuDeleteLoopDone: + + ; If the user changed the shortcut, then untinstall may not work. This should + ; try to fix it. + StrCpy $MUI_TEMP "$START_MENU" + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" + Delete "$SMPROGRAMS\$MUI_TEMP\USRP2 Card Burner.lnk" + Delete "$SMPROGRAMS\$MUI_TEMP\USRP-N2XX Net Burner.lnk" + Delete "$SMPROGRAMS\$MUI_TEMP\UHD Images Downloader.lnk" +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + secondStartMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors secondStartMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop + secondStartMenuDeleteLoopDone: + + DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + Push $INSTDIR\bin + StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 + Call un.RemoveFromPath + doNotRemoveFromPath: +SectionEnd + +;-------------------------------- +; determine admin versus local install +; Is install for "AllUsers" or "JustMe"? +; Default to "JustMe" - set to "AllUsers" if admin or on Win9x +; This function is used for the very first "custom page" of the installer. +; This custom page does not show up visibly, but it executes prior to the +; first visible page and sets up $INSTDIR properly... +; Choose different default installation folder based on SV_ALLUSERS... +; "Program Files" for AllUsers, "My Documents" for JustMe... + +Function .onInit + ; Reads components status for registry + !insertmacro SectionList "InitSection" + + ; check to see if /D has been used to change + ; the install directory by comparing it to the + ; install directory that is expected to be the + ; default + StrCpy $IS_DEFAULT_INSTALLDIR 0 + StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 + StrCpy $IS_DEFAULT_INSTALLDIR 1 + + StrCpy $SV_ALLUSERS "JustMe" + ; if default install dir then change the default + ; if it is installed for JustMe + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + + noLM: + StrCpy $SV_ALLUSERS "AllUsers" + ;Get installation folder from registry if available + + done: + StrCmp $SV_ALLUSERS "AllUsers" 0 +3 + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" + + noOptionsPage: +FunctionEnd diff --git a/host/cmake/Modules/UHDPackage.cmake b/host/cmake/Modules/UHDPackage.cmake index 85cbea81e..6af474f7a 100644 --- a/host/cmake/Modules/UHDPackage.cmake +++ b/host/cmake/Modules/UHDPackage.cmake @@ -117,7 +117,6 @@ SET(CPACK_COMPONENT_LIBRARIES_GROUP "Development") SET(CPACK_COMPONENT_HEADERS_GROUP "Development") SET(CPACK_COMPONENT_UTILITIES_GROUP "Runtime") SET(CPACK_COMPONENT_EXAMPLES_GROUP "Runtime") -SET(CPACK_COMPONENT_TESTS_GROUP "Runtime") SET(CPACK_COMPONENT_MANUAL_GROUP "Documentation") SET(CPACK_COMPONENT_DOXYGEN_GROUP "Documentation") SET(CPACK_COMPONENT_README_GROUP "Documentation") @@ -126,7 +125,6 @@ SET(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") SET(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers") SET(CPACK_COMPONENT_UTILITIES_DISPLAY_NAME "Utilities") SET(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Examples") -SET(CPACK_COMPONENT_TESTS_DISPLAY_NAME "Unit Tests") SET(CPACK_COMPONENT_MANUAL_DISPLAY_NAME "Manual") SET(CPACK_COMPONENT_DOXYGEN_DISPLAY_NAME "Doxygen") SET(CPACK_COMPONENT_README_DISPLAY_NAME "Readme") @@ -136,7 +134,6 @@ SET(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "Dynamic link library") SET(CPACK_COMPONENT_HEADERS_DESCRIPTION "C++ development headers") SET(CPACK_COMPONENT_UTILITIES_DESCRIPTION "Utility executables and python scripts") SET(CPACK_COMPONENT_EXAMPLES_DESCRIPTION "Example executables") -SET(CPACK_COMPONENT_TESTS_DESCRIPTION "Unit test executables") SET(CPACK_COMPONENT_MANUAL_DESCRIPTION "Manual/application notes (rst and html)") SET(CPACK_COMPONENT_DOXYGEN_DESCRIPTION "API documentation (html)") SET(CPACK_COMPONENT_README_DESCRIPTION "Readme files (txt)") @@ -148,7 +145,7 @@ SET(CPACK_COMPONENT_UTILITIES_DEPENDS libraries) SET(CPACK_COMPONENT_EXAMPLES_DEPENDS libraries) SET(CPACK_COMPONENT_TESTS_DEPENDS libraries) -SET(CPACK_COMPONENTS_ALL libraries headers utilities examples tests manual doxygen readme images) +SET(CPACK_COMPONENTS_ALL libraries headers utilities examples manual doxygen readme images) ######################################################################## # Setup CPack Debian @@ -191,11 +188,11 @@ SET(CPACK_NSIS_MODIFY_PATH ON) SET(HLKM_ENV "\\\"SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment\\\"") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " - WriteRegStr HKLM ${HLKM_ENV} \\\"UHD_PKG_DATA_PATH\\\" \\\"$INSTDIR\\\\share\\\\uhd\\\" + WriteRegStr HKLM ${HLKM_ENV} \\\"UHD_PKG_PATH\\\" \\\"$INSTDIR\\\" ") SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " - DeleteRegValue HKLM ${HLKM_ENV} \\\"UHD_PKG_DATA_PATH\\\" + DeleteRegValue HKLM ${HLKM_ENV} \\\"UHD_PKG_PATH\\\" ") IF(MSVC) diff --git a/host/cmake/Modules/UHDUnitTest.cmake b/host/cmake/Modules/UHDUnitTest.cmake new file mode 100644 index 000000000..47cddc521 --- /dev/null +++ b/host/cmake/Modules/UHDUnitTest.cmake @@ -0,0 +1,110 @@ +# +# Copyright 2010-2012 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Add a unit test and setup the environment for a unit test. +# Takes the same arguments as the ADD_TEST function. +# +# Before calling set the following variables: +# UHD_TEST_TARGET_DEPS - built targets for the library path +# UHD_TEST_LIBRARY_DIRS - directories for the library path +######################################################################## +function(UHD_ADD_TEST test_name) + + #Ensure that the build exe also appears in the PATH. + list(APPEND UHD_TEST_TARGET_DEPS ${ARGN}) + + #In the land of windows, all libraries must be in the PATH. + #Since the dependent libraries are not yet installed, + #we must manually set them in the PATH to run tests. + #The following appends the path of a target dependency. + foreach(target ${UHD_TEST_TARGET_DEPS}) + get_target_property(location ${target} LOCATION) + if(location) + get_filename_component(path ${location} PATH) + string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) + list(APPEND UHD_TEST_LIBRARY_DIRS ${path}) + endif(location) + endforeach(target) + + file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir) + file(TO_NATIVE_PATH "${UHD_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? + + #http://www.cmake.org/pipermail/cmake/2009-May/029464.html + #Replaced this add test + set environs code with the shell script generation. + #Its nicer to be able to manually run the shell script to diagnose problems. + #ADD_TEST(${ARGV}) + #SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}") + + if(UNIX) + set(LD_PATH_VAR "LD_LIBRARY_PATH") + if(APPLE) + set(LD_PATH_VAR "DYLD_LIBRARY_PATH") + endif() + + set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH") + list(APPEND libpath "$${LD_PATH_VAR}") + + #replace list separator with the path separator + string(REPLACE ";" ":" libpath "${libpath}") + list(APPEND environs "PATH=${binpath}" "${LD_PATH_VAR}=${libpath}") + + #generate a bat file that sets the environment and runs the test + find_program(SHELL sh) + set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) + file(WRITE ${sh_file} "#!${SHELL}\n") + #each line sets an environment variable + foreach(environ ${environs}) + file(APPEND ${sh_file} "export ${environ}\n") + endforeach(environ) + #load the command to run with its arguments + foreach(arg ${ARGN}) + file(APPEND ${sh_file} "${arg} ") + endforeach(arg) + file(APPEND ${sh_file} "\n") + + #make the shell file executable + execute_process(COMMAND chmod +x ${sh_file}) + + add_test(${test_name} ${SHELL} ${sh_file}) + + endif(UNIX) + + if(WIN32) + list(APPEND libpath ${DLL_PATHS} "%PATH%") + + #replace list separator with the path separator (escaped) + string(REPLACE ";" "\\;" libpath "${libpath}") + list(APPEND environs "PATH=${libpath}") + + #generate a bat file that sets the environment and runs the test + set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat) + file(WRITE ${bat_file} "@echo off\n") + #each line sets an environment variable + foreach(environ ${environs}) + file(APPEND ${bat_file} "SET ${environ}\n") + endforeach(environ) + #load the command to run with its arguments + foreach(arg ${ARGN}) + file(APPEND ${bat_file} "${arg} ") + endforeach(arg) + file(APPEND ${bat_file} "\n") + + add_test(${test_name} ${bat_file}) + endif(WIN32) + +endfunction(UHD_ADD_TEST) diff --git a/host/cmake/debian/postinst.in b/host/cmake/debian/postinst.in index eaca15394..bc27c46f0 100755 --- a/host/cmake/debian/postinst.in +++ b/host/cmake/debian/postinst.in @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2011 Ettus Research LLC +# Copyright 2011,2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ # if [ "$1" = "configure" ]; then - cp @CMAKE_INSTALL_PREFIX@/share/uhd/utils/uhd-usrp.rules /etc/udev/rules.d/uhd-usrp.rules + cp @CMAKE_INSTALL_PREFIX@/lib/uhd/utils/uhd-usrp.rules /etc/udev/rules.d/uhd-usrp.rules udevadm control --reload-rules ldconfig fi diff --git a/host/cmake/redhat/post_install.in b/host/cmake/redhat/post_install.in index 01fd3338f..25864e0ec 100755 --- a/host/cmake/redhat/post_install.in +++ b/host/cmake/redhat/post_install.in @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2011 Ettus Research LLC +# Copyright 2011,2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,6 +16,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -cp @CMAKE_INSTALL_PREFIX@/share/uhd/utils/uhd-usrp.rules /etc/udev/rules.d/uhd-usrp.rules +cp @CMAKE_INSTALL_PREFIX@/lib/uhd/utils/uhd-usrp.rules /etc/udev/rules.d/uhd-usrp.rules udevadm control --reload-rules ldconfig diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index cba97218a..17744db7d 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -34,6 +34,7 @@ SET(manual_sources usrp1.rst usrp2.rst usrp_b100.rst + usrp_b200.rst usrp_e1x0.rst ) diff --git a/host/docs/build.rst b/host/docs/build.rst index 0a18e9e9a..00f79aaaa 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - Build Guide +UHD Software - Build Guide ======================================================================== .. contents:: Table of Contents diff --git a/host/docs/calibration.rst b/host/docs/calibration.rst index 1468a9c2a..8ba3477b8 100644 --- a/host/docs/calibration.rst +++ b/host/docs/calibration.rst @@ -7,11 +7,11 @@ UHD - Calibration Application Notes ------------------------------------------------------------------------ Self-calibration ------------------------------------------------------------------------ -UHD comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. +UHD software comes with several self-calibration utilities for minimizing IQ imbalance and DC offset. These utilities perform calibration sweeps using transmit leakage into the receive path (special equipment is not required). The results from a calibration are written to a CSV file in the user's home directory. -UHD will automatically apply corrections at runtime when the user re-tunes the daughterboard LO. +UHD software will automatically apply corrections at runtime when the user re-tunes the daughterboard LO. Calibration results are specific to an individual RF board. **Note:** @@ -19,7 +19,7 @@ When a calibration table is present, and the user wishes to override the calibration settings through the API: the user should re-apply the desired setting every time the LO is re-tuned. -UHD comes with the following calibration utilities: +UHD software comes with the following calibration utilities: * **uhd_cal_rx_iq_balance:** - mimimizes RX IQ imbalance vs. LO frequency * **uhd_cal_tx_dc_offset:** - mimimizes TX DC offset vs. LO frequency @@ -27,14 +27,15 @@ UHD comes with the following calibration utilities: The following RF frontends are supported by the self-calibration utilities: + * RFX transceiver board * WBX transceiver board * SBX transceiver board - * RFX transceiver board + * CBX transceiver board ******************************************** Calibration Utilities ******************************************** -UHD installs the calibration utilities into **<install-path>/bin**. +UHD software installs the calibration utilities into **<install-path>/bin**. **Disconnect** any external hardware from the RF antenna ports, and run the following from the command line. Each utility will take several minutes to complete. diff --git a/host/docs/coding.rst b/host/docs/coding.rst index ef8cb5fe2..432307cca 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -11,7 +11,7 @@ Various API interfaces Low-Level: The device API ^^^^^^^^^^^^^^^^^^^^^^^^^^^ A device is an abstraction for hardware that is connected to the host system. -For a USRP, this means that the motherboard and everything on it would be +For a USRP device, this means that the motherboard and everything on it would be considered to be a "device". The device API provides ways to: * Discover devices that are physically connected to the host system. @@ -25,6 +25,6 @@ See the documentation in *device.hpp* for reference. ^^^^^^^^^^^^^^^^^^^^^^^^^^^ High-Level: The Multi-USRP ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Multi-USRP class provides a fat interface to a single USRP with -one or more channels, or multiple USRPs in a homogeneous setup. +The Multi-USRP class provides a fat interface to a single USRP device with +one or more channels, or multiple USRP devicess in a homogeneous setup. See the documentation in *usrp/multi_usrp.hpp* for reference. diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index 29812592f..4b5a074a8 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - Daughterboard Application Notes +UHD Daughterboard Application Notes ======================================================================== .. contents:: Table of Contents @@ -255,6 +255,11 @@ LEDs: * **RX1/RX2**: Receiver on RX2 antenna port ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +CBX +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +See SBX Series for more details. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ TVRX ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The TVRX board has 1 real-mode frontend. @@ -335,14 +340,14 @@ With the daughterboard plugged-in, run the following commands: cd <install-path>/share/uhd/utils ./usrp_burn_db_eeprom --id=0x000d --unit=RX --args=<args> --slot=<slot> -* **<args>** are device address arguments (optional if only one USRP is on your machine) -* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP device is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP device has only one slot) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RFX - Mod ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Older RFX boards require modifications to use the motherboard oscillator. -If this is the case, UHD will print a warning about the modification. +If this is the case, UHD software will print a warning about the modification. Please follow the modification procedures below: **Step 1: Disable the daughterboard clocks** @@ -377,5 +382,5 @@ With the daughterboard plugged-in, run the following commands: * **RFX1800:** 0x0035 * **RFX1200:** 0x002a * **RFX2400:** 0x002b -* **<args>** are device address arguments (optional if only one USRP is on your machine) -* **<slot>** is the name of the daughterboard slot (optional if the USRP has only one slot) +* **<args>** are device address arguments (optional if only one USRP device is on your machine) +* **<slot>** is the name of the daughterboard slot (optional if the USRP device has only one slot) diff --git a/host/docs/general.rst b/host/docs/general.rst index fc7caff3c..b116ba816 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -23,10 +23,10 @@ frequency and actual frequency. The user may also explicitly control both stages of tuning through through the **tune_request_t** object, which allows for more advanced tuning. -In general, Using UHD's advanced tuning is highly recommended as it makes it +In general, Using UHD software's advanced tuning is highly recommended as it makes it easy to move the DC component out of your band-of-interest. This can be done by -passing your desired LO offset to the **tune_request_t** object, and letting UHD -handle the rest. +passing your desired LO offset to the **tune_request_t** object, and letting the UHD +software handle the rest. Tuning the receive chain: :: @@ -125,29 +125,31 @@ Overflow notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When receiving, the device produces samples at a constant rate. Overflows occurs when the host does not consume data fast enough. -When UHD detects the overflow, it prints an "O" to stdout, +When UHD software detects the overflow, it prints an "O" or "D" to stdout, and pushes an inline message packet into the receive stream. **Network-based devices**: The host does not back-pressure the receive stream. When the kernel's socket buffer becomes full, it will drop subsequent packets. -UHD detects the overflow as a discontinuity in the packet's sequence numbers, +UHD software detects the overflow as a discontinuity in the packet's sequence numbers, and pushes an inline message packet into the receive stream. +In this case the character "D" is printed to stdout as an indication. **Other devices**: The host back-pressures the receive stream. Therefore, overflows always occur in the device itself. When the device's internal buffers become full, streaming is shut off, and an inline message packet is sent to the host. +In this case the character "O" is printed to stdout as an indication. If the device was in continuous streaming mode, -UHD will automatically restart streaming. +the UHD software will automatically restart streaming. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Underflow notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When transmitting, the device consumes samples at a constant rate. Underflow occurs when the host does not produce data fast enough. -When UHD detects underflow, it prints a "U" to stdout, +When UHD software detects the underflow, it prints a "U" to stdout, and pushes a message packet into the async message stream. ------------------------------------------------------------------------ @@ -157,7 +159,7 @@ Threading Notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Thread safety notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -For the most part, UHD is thread-safe. +For the most part, UHD software is thread-safe. Please observe the following limitations: **Fast-path thread requirements:** @@ -177,8 +179,8 @@ It is recommended to use at most one thread context for manipulating device sett Thread priority scheduling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When UHD spawns a new thread it may try to boost the thread's scheduling priority. -When setting the priority fails, UHD prints out an error. +When UHD software spawns a new thread it may try to boost the thread's scheduling priority. +When setting the priority fails, the UHD software prints out an error. This error is harmless; it simply means that the thread will have a normal scheduling priority. **Linux Notes:** @@ -211,7 +213,7 @@ Disabling or redirecting prints to stdout The user can disable the UHD library from printing directly to stdout by registering a custom message handler. The handler will intercept all messages, which can be dropped or redirected. Only one handler can be registered at a time. -Make **register_handler** your first call into UHD: +Make **register_handler** your first call into the UHD library: :: diff --git a/host/docs/identification.rst b/host/docs/identification.rst index a5e60e7f9..ba9b30898 100644 --- a/host/docs/identification.rst +++ b/host/docs/identification.rst @@ -5,7 +5,7 @@ UHD - Device Identification Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Identifying USRPs +Identifying USRP Devices ------------------------------------------------------------------------ Devices are addressed through key/value string pairs. These string pairs can be used to narrow down the search for a specific device or group of devices. @@ -89,10 +89,10 @@ such as detected daughterboards, frequency range, gain ranges, etc... uhd_usrp_probe --args <device-specific-address-args> ------------------------------------------------------------------------ -Naming a USRP +Naming a USRP Device ------------------------------------------------------------------------ -For convenience purposes, users may assign a custom name to their USRPs. -The USRP can then be identified via name, rather than a difficult to remember serial or address. +For convenience purposes, users may assign a custom name to their USRP device. +The USRP device can then be identified via name, rather than a difficult to remember serial or address. A name has the following properties: diff --git a/host/docs/images.rst b/host/docs/images.rst index dc9770460..95e2d96b0 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -29,7 +29,7 @@ See the UHD wiki for the download link. The pre-built images come in two forms: -* bundled with UHD in a platform-specific installer +* bundled with UHD software in a platform-specific installer * stand-alone platform-independent archive files ^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ When installing images from an archive, there are two options: **Option 1:** Unpack the archive into the UHD installation prefix. -UHD will always search **<install-path>/share/uhd/images** for image files. +UHD software will always search **<install-path>/share/uhd/images** for image files. Where **<install-path>** was set by the **CMAKE_INSTALL_PREFIX** at configure-time. **Option 2:** diff --git a/host/docs/index.rst b/host/docs/index.rst index 00b1c9618..27523c139 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -2,17 +2,17 @@ UHD - USRP Hardware Driver ======================================================================== -UHD is the "Universal Software Radio Peripheral" hardware driver. -The goal of UHD is to provide a host driver and API for current and future Ettus Research products. +UHD software is the "Universal Software Radio Peripheral" Hardware Driver software. +The goal of UHD software is to provide a host driver and API for current and future Ettus Research products. Users will be able to use the UHD driver standalone or with third-party applications. ------------------------------------------------------------------------ Contents ------------------------------------------------------------------------ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Building and Installing UHD -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Building and Installing UHD Software +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * `Build Guide <./build.html>`_ * `Installation Guide (Linux) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Linux>`_ * `Installation Guide (Windows) <http://code.ettus.com/redmine/ettus/projects/uhd/wiki/UHD_Windows>`_ @@ -27,6 +27,7 @@ Application Notes * `USRP2 Application Notes <./usrp2.html>`_ * `USRP-N2X0 Series Application Notes <./usrp2.html>`_ * `USRP-B100 Series Application Notes <./usrp_b100.html>`_ +* `USRP-B2X0 Series Application Notes <./usrp_b200.html>`_ * `USRP-E1X0 Series Application Notes <./usrp_e1x0.html>`_ * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ diff --git a/host/docs/sync.rst b/host/docs/sync.rst index 8622dc642..a1f6cb7df 100644 --- a/host/docs/sync.rst +++ b/host/docs/sync.rst @@ -4,9 +4,9 @@ UHD - Synchronization Application Notes .. contents:: Table of Contents -The following application notes explain how to synchronize multiple USRPs -with the goal of transmitting or receiving time-aligned samples for MIMO -or other applications requiring multiple USRPs operating synchronously. +The following application notes explain how to synchronize multiple USRP +devices with the goal of transmitting or receiving time-aligned samples for MIMO +or other applications requiring multiple USRP devices operating synchronously. **Note:** The following synchronization notes do not apply to USRP1, which does not support the advanced features available in newer products. @@ -14,7 +14,7 @@ which does not support the advanced features available in newer products. ------------------------------------------------------------------------ Common Reference Signals ------------------------------------------------------------------------ -USRPs take two reference signals in order to synchronize clocks and time: +USRP devices take two reference signals in order to synchronize clocks and time: * A 10MHz reference to provide a single frequency reference for both devices. * A pulse-per-second (PPS) to synchronize the sample time across devices. @@ -103,16 +103,16 @@ and the user can also parse this string to determine GPS time: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 3 - internal GPSDO ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -USRPs with internal GPSDOs properly configured will automatically +USRP devices with internal GPSDOs properly configured will automatically configure themselves to set the VITA time to current UTC time. See the `GPSDO Application Notes <./gpsdo.html>`_ for more details. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Method 4 - MIMO cable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A USRP can synchronize its time to another USRP via the MIMO cable. +A USRP device can synchronize its time to another USRP device via the MIMO cable. Unlike the other methods, this does not use a real "pulse per second". -Rather, the USRP sends an encoded time message over the MIMO cable. +Rather, the USRP device sends an encoded time message over the MIMO cable. The slave device will automatically synchronize to the time on the master device. See the `MIMO Cable Application Notes <./usrp2.html#using-the-mimo-cable>`_ for more detail. @@ -123,7 +123,7 @@ Synchronizing Channel Phase ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Align CORDICs in the DSP ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In order to achieve phase alignment between USRPs, the CORDICS in both +In order to achieve phase alignment between USRP devices, the CORDICS in both devices must be aligned with respect to each other. This is easily achieved by issuing stream commands with a time spec property, which instructs the streaming to begin at a specified time. Since the devices are already diff --git a/host/docs/transport.rst b/host/docs/transport.rst index 2e39e75d1..db89c8db9 100644 --- a/host/docs/transport.rst +++ b/host/docs/transport.rst @@ -14,7 +14,7 @@ These optional parameters control how the transport object allocates memory, resizes kernel buffers, spawns threads, etc. When not spcified, the transport layer will use values for these parameters that are known to perform well on a variety of systems. -The transport parameters are defined below for the various transports in the UHD: +The transport parameters are defined below for the various transports in the UHD software: ------------------------------------------------------------------------ UDP Transport (Sockets) @@ -154,11 +154,11 @@ so that non-root users may access the device: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Install USB driver (Windows) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A driver package must be installed to use a USB-based product with UHD: +A driver package must be installed to use a USB-based product with UHD software: * Download the driver from the UHD wiki page `here <http://files.ettus.com/binaries/misc/erllc_uhd_winusb_driver.zip>`_. * Unzip the file into a known location. We will refer to this as the **<directory>**. -* Open the device manager and plug in the USRP. You will see an unrecognized USB device in the device manager. +* Open the device manager and plug in the USRP device. You will see an unrecognized USB device in the device manager. * Right click on the unrecognized USB device and select update/install driver software (may vary for your OS). * In the driver installation wizard, select "browse for driver", browse to the **<directory>**, and select the **.inf** file. * Continue through the installation wizard until the driver is installed. diff --git a/host/docs/usrp1.rst b/host/docs/usrp1.rst index c1fdec146..b6fd24b09 100644 --- a/host/docs/usrp1.rst +++ b/host/docs/usrp1.rst @@ -79,7 +79,7 @@ Hardware Setup Notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ External clock modification ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The USRP can be modified to accept an external clock reference instead of the 64MHz onboard reference. +The USRP device can be modified to accept an external clock reference instead of the 64MHz onboard reference. * Solder SMA (**LTI-SASF54GT**) connector to **J2001**. * Move 0 ohm 0603 resistor **R2029** to **R2030**. * Move 0.01uF 0603 capacitor **C925** to **C926**. @@ -89,7 +89,7 @@ The new external clock needs to be a square wave between +7dBm and +15dBm After the hardware modification, the user should burn the setting into the EEPROM, -so UHD can initialize with the correct clock rate. +so UHD software can initialize with the correct clock rate. Run the following commands to record the setting into the EEPROM: :: diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index e675e61ba..8ef31af1a 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -148,7 +148,7 @@ any parameters: ifconfig -a **Note:** -When using UHD, if an IP address for the USRP2 is not specified, +When using UHD software, if an IP address for the USRP2 is not specified, the software will use UDP broadcast packets to locate the USRP2. On some systems, the firewall will block UDP broadcast packets. It is recommended that you change or disable your firewall settings. @@ -213,13 +213,13 @@ The following tips are designed to help narrow down and diagnose the problem. RuntimeError: no control response ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is a common error that occurs when you have set the subnet of your network -interface to a different subnet than the network interface of the USRP. For -example, if your network interface is set to **192.168.20.1**, and the USRP is +interface to a different subnet than the network interface of the USRP device. For +example, if your network interface is set to **192.168.20.1**, and the USRP device is **192.168.10.2** (note the difference in the third numbers of the IP addresses), you will likely see a 'no control response' error message. Fixing this is simple - just set the your host PC's IP address to the same -subnet as that of your USRP. Instructions for setting your IP address are in the +subnet as that of your USRP device. Instructions for setting your IP address are in the previous section of this documentation. @@ -238,7 +238,7 @@ or create a rule to allow all incoming packets with UDP source port **49152**. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ping the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The USRP will reply to ICMP echo requests. +The USRP device will reply to ICMP echo requests. A successful ping response means that the device has booted properly and that it is using the expected IP address. @@ -337,16 +337,14 @@ In order for the slave to synchronize to the master over MIMO cable, the following clock configuration must be set on the slave device: :: - uhd::clock_config_t clock_config; - clock_config.ref_source = uhd::clock_config_t::REF_MIMO; - clock_config.pps_source = uhd::clock_config_t::PPS_MIMO; - usrp->set_clock_config(clock_config, slave_index); + usrp->set_time_source("mimo", slave_index); + usrp->set_clock_source("mimo", slave_index); ------------------------------------------------------------------------ Alternative stream destination ------------------------------------------------------------------------ -It is possible to program the USRP to send RX packets to an alternative IP/UDP destination. +It is possible to program the USRP device to send RX packets to an alternative IP/UDP destination. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set the subnet and gateway @@ -429,7 +427,7 @@ Using a PPS signal for timestamp synchronization requires a square wave signal w Test the PPS input with the following app: -* **<args>** are device address arguments (optional if only one USRP is on your machine) +* **<args>** are device address arguments (optional if only one USRP device is on your machine) :: diff --git a/host/docs/usrp_b100.rst b/host/docs/usrp_b100.rst index cdb853b61..ac0942f5c 100644 --- a/host/docs/usrp_b100.rst +++ b/host/docs/usrp_b100.rst @@ -21,23 +21,23 @@ Comparative features list ------------------------------------------------------------------------ Specify a Non-standard Image ------------------------------------------------------------------------ -UHD will automatically select the USRP B-Series images from the installed images package. +UHD software will automatically select the USRP B100 images from the installed images package. The image selection can be overridden with the **--fpga=** and **--fw=** device address parameters. Example device address string representations to specify non-standard images: :: - fpga=usrp_b100_fpga_firmware.bin + fpga=usrp_b100_fpga_2rx.bin -- OR -- - fw=usrp_b100_fw_firmware.ihx + fw=usrp_b100_fw.ihx ------------------------------------------------------------------------ Changing the Master Clock Rate ------------------------------------------------------------------------ -The master clock rate of the USRP embedded feeds both the FPGA DSP and the codec chip. +The master clock rate of the B100 feeds both the FPGA DSP and the codec chip. Hundreds of rates between 32MHz and 64MHz are available. A few notable rates are: @@ -54,7 +54,7 @@ and X4 must be populated with a 61.44 MHz oscillator. * **J15** is a three pin header, move the jumper to (pin1, pin2) * **357LB3I061M4400** is the recommended oscillator for X4 -**Note:** See instructions below to communicate the desired clock rate into UHD. +**Note:** See instructions below to communicate the desired clock rate into UHD software. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set other rates - uses internal VCO @@ -63,7 +63,7 @@ To use other clock rates, the jumper will need to be in the default position. * **J15** is a three pin header, move the jumper to (pin2, pin3) -To communicate the desired clock rate into UHD, +To communicate the desired clock rate into UHD software, specify the a special device address argument, where the key is **master_clock_rate** and the value is a rate in Hz. Example: diff --git a/host/docs/usrp_b200.rst b/host/docs/usrp_b200.rst new file mode 100644 index 000000000..bb2a2876a --- /dev/null +++ b/host/docs/usrp_b200.rst @@ -0,0 +1,87 @@ +======================================================================== +UHD - USRP-B2X0 Series Application Notes +======================================================================== + +.. contents:: Table of Contents + +------------------------------------------------------------------------ +Comparative features list - B200 +------------------------------------------------------------------------ + +* integrated RF frontend +* 1 RX DDC chain in FPGA +* 1 TX DUC chain in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* Configurable clock rate +* Internal GPSDO option + +------------------------------------------------------------------------ +Comparative features list - B210 +------------------------------------------------------------------------ + +* integrated MIMO frontend +* 2 RX DDC chains in FPGA +* 2 TX DUC chains in FPGA +* Timed commands in FPGA +* Timed sampling in FPGA +* External PPS reference +* External 10MHz reference +* Configurable clock rate +* Internal GPSDO option + +------------------------------------------------------------------------ +Specify a Non-standard Image +------------------------------------------------------------------------ +UHD software will automatically select the USRP B2X0 images from the installed images package. +The image selection can be overridden with the **--fpga=** and **--fw=** device address parameters. + +Example device address string representations to specify non-standard images: + +:: + + fpga=usrp_b200_fpga.bin + + -- OR -- + + fw=usrp_b200_fw.hex + +------------------------------------------------------------------------ +Changing the Master Clock Rate +------------------------------------------------------------------------ +The master clock rate feeds the RF frontends and the DSP chains. +Users may select non-default clock rates to acheive integer decimations or interpolations in the DSP chains. +The default master clock rate defaults to 32 MHz, but can be set to any rate between 5 MHz and 61.44 MHz. + +The user can set the master clock rate through the usrp API call set_master_clock_rate(), +or the clock rate can be set through the device arguments, which many applications take: +:: + + uhd_usrp_probe --args="master_clock_rate=52e6" + +------------------------------------------------------------------------ +RF Frontend Notes +------------------------------------------------------------------------ +The B200 features and integrated RF frontend. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Frontend tuning +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The RF frontend has individually tunable receive and transmit chains. +On the B200, there is one transmit and one receive RF frontend. +On the B210, both transmit and receive can be used in a MIMO configuration. +For the MIMO case, both receive frontends share the RX LO, +and both transmit frontends share the TX LO. +Each LO is tunable between 50 MHz and 6 GHz. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Frontend gain +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +All frontends have individual analog gain controls. +The receive frontends have 73 dB of available gain; +and the transmit frontends have 89 dB of available gain. +Gain settings are application specific, +but its recommended that users consider using at least +half of the available gain to get reasonable dynamic range. diff --git a/host/docs/usrp_e1x0.rst b/host/docs/usrp_e1x0.rst index 189cbb86b..fc929e639 100644 --- a/host/docs/usrp_e1x0.rst +++ b/host/docs/usrp_e1x0.rst @@ -22,7 +22,7 @@ Comparative features list ------------------------------------------------------------------------ Specify a Non-standard Image ------------------------------------------------------------------------ -UHD will automatically select the USRP-Embedded FPGA image from the +UHD software will automatically select the USRP-Embedded FPGA image from the installed images package. The FPGA image selection can be overridden with the **--fpga=** device address parameter. @@ -53,7 +53,7 @@ on the device. * **J16** is a two pin header; remove the jumper (or leave it on pin1 only). * **J15** is a three pin header; move the jumper to (pin1, pin2). -**Note:** See instructions below to communicate the desired clock rate UHD. +**Note:** See instructions below to communicate the desired clock rate to UHD software. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Set other rates - uses internal VCO @@ -63,7 +63,7 @@ To use other clock rates, the jumpers will need to be in the default position. * **J16** is a two pin header; move the jumper to (pin1, pin2). * **J15** is a three pin header; move the jumper to (pin2, pin3). -To communicate the desired clock rate into UHD, +To communicate the desired clock rate into UHD software, specify the a special device address argument, where the key is **master_clock_rate** and the value is a rate in Hz. Example: @@ -103,7 +103,7 @@ a connector. Test the PPS input with the following app: -* **<args** are device address arguments (optional if only one USRP is on your machine). +* **<args** are device address arguments (optional if only one USRP device is on your machine). :: @@ -116,7 +116,7 @@ Internal GPSDO Please see the `Internal GPSDO Application Notes <./gpsdo.html>`_ for information on configuring and using the internal GPSDO. -UHD will always try to detect an installed GPSDO at runtime. +UHD software will always try to detect an installed GPSDO at runtime. There is not a special EEPROM value to burn for GPSDO detection. ------------------------------------------------------------------------ diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 8a000f6c3..02e5ef60a 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -22,9 +22,9 @@ #include <boost/program_options.hpp> #include <boost/format.hpp> #include <boost/thread/thread.hpp> -#include <boost/math/special_functions/round.hpp> #include <iostream> #include <complex> +#include <cstdlib> namespace po = boost::program_options; @@ -41,26 +41,20 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, const std::string &rx_otw){ +void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ uhd::set_thread_priority_safe(); - //create a receive streamer - uhd::stream_args_t stream_args(rx_cpu, rx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - //print pre-test summary std::cout << boost::format( - "Testing receive rate %f Msps" - ) % (usrp->get_rx_rate()/1e6) << std::endl; + "Testing receive rate %f Msps on %u channels" + ) % (usrp->get_rx_rate()/1e6) % rx_stream->get_num_channels() << std::endl; //setup variables and allocate buffer uhd::rx_metadata_t md; const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); std::vector<void *> buffs; - for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) buffs.push_back(&buff.front()); //same buffer for each channel bool had_an_overflow = false; uhd::time_spec_t last_time; @@ -69,16 +63,16 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); cmd.stream_now = (buffs.size() == 1); - usrp->issue_stream_cmd(cmd); + rx_stream->issue_stream_cmd(cmd); while (not boost::this_thread::interruption_requested()){ - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); //handle the error codes switch(md.error_code){ case uhd::rx_metadata_t::ERROR_CODE_NONE: if (had_an_overflow){ had_an_overflow = false; - num_dropped_samps += boost::math::iround((md.time_spec - last_time).get_real_secs()*rate); + num_dropped_samps += (md.time_spec - last_time).to_ticks(rate); } break; @@ -95,25 +89,19 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c } } - usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); } /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, const std::string &tx_otw){ +void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ uhd::set_thread_priority_safe(); - //create a transmit streamer - uhd::stream_args_t stream_args(tx_cpu, tx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); - //print pre-test summary std::cout << boost::format( - "Testing transmit rate %f Msps" - ) % (usrp->get_tx_rate()/1e6) << std::endl; + "Testing transmit rate %f Msps on %u channels" + ) % (usrp->get_tx_rate()/1e6) % tx_stream->get_num_channels() << std::endl; //setup variables and allocate buffer uhd::tx_metadata_t md; @@ -121,12 +109,12 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_c const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); std::vector<const void *> buffs; - for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) buffs.push_back(&buff.front()); //same buffer for each channel md.has_time_spec = (buffs.size() != 1); while (not boost::this_thread::interruption_requested()){ - num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md); + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels();; md.has_time_spec = false; } @@ -135,13 +123,13 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_c tx_stream->send(buffs, 0, md); } -void benchmark_tx_rate_async_helper(uhd::usrp::multi_usrp::sptr usrp){ +void benchmark_tx_rate_async_helper(uhd::tx_streamer::sptr tx_stream){ //setup variables and allocate buffer uhd::async_metadata_t async_md; while (not boost::this_thread::interruption_requested()){ - if (not usrp->get_device()->recv_async_msg(async_md)) continue; + if (not tx_stream->recv_async_msg(async_md)) continue; //handle the error codes switch(async_md.event_code){ @@ -231,14 +219,24 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //spawn the receive test thread if (vm.count("rx_rate")){ usrp->set_rx_rate(rx_rate); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_otw)); + //create a receive streamer + uhd::stream_args_t stream_args(rx_cpu, rx_otw); + for (size_t ch = 0; ch < usrp->get_rx_num_channels(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream)); } //spawn the transmit test thread if (vm.count("tx_rate")){ usrp->set_tx_rate(tx_rate); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_otw)); - thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, usrp)); + //create a transmit streamer + uhd::stream_args_t stream_args(tx_cpu, tx_otw); + for (size_t ch = 0; ch < usrp->get_tx_num_channels(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, tx_stream)); } //sleep for the required duration @@ -264,5 +262,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/latency_test.cpp b/host/examples/latency_test.cpp index 518d2383a..da06086f9 100644 --- a/host/examples/latency_test.cpp +++ b/host/examples/latency_test.cpp @@ -103,7 +103,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ stream_cmd.num_samps = buffer.size(); stream_cmd.stream_now = false; stream_cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.01); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); /*************************************************************** * Receive the requested packet @@ -133,7 +133,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ * Check the async messages for result **************************************************************/ uhd::async_metadata_t async_md; - if (not usrp->get_device()->recv_async_msg(async_md)){ + if (not tx_stream->recv_async_msg(async_md)){ std::cout << boost::format("failed:\n Async message recv timed out.\n") << std::endl; continue; } @@ -164,5 +164,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ **************************************************************/ std::cout << boost::format("\nACK %d, UNDERFLOW %d, TIME_ERR %d, other %d") % ack % underflow % time_error % other << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/network_relay.cpp b/host/examples/network_relay.cpp index e8f9e667f..bb09296b3 100644 --- a/host/examples/network_relay.cpp +++ b/host/examples/network_relay.cpp @@ -24,6 +24,7 @@ #include <iostream> #include <csignal> #include <vector> +#include <cstdlib> namespace po = boost::program_options; namespace asio = boost::asio; @@ -202,7 +203,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ << "Runs a network relay between UHD on one computer and a USRP on the network.\n" << "This example is basically for test purposes. Use at your own convenience.\n" << std::endl; - return ~0; + return EXIT_FAILURE; } { @@ -223,5 +224,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/rx_ascii_art_dft.cpp b/host/examples/rx_ascii_art_dft.cpp index fe8fb0347..df3256b09 100644 --- a/host/examples/rx_ascii_art_dft.cpp +++ b/host/examples/rx_ascii_art_dft.cpp @@ -25,6 +25,7 @@ #include <curses.h> #include <iostream> #include <complex> +#include <cstdlib> namespace po = boost::program_options; @@ -63,7 +64,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help") or not vm.count("rate")){ std::cout << boost::format("UHD RX ASCII Art DFT %s") % desc << std::endl; - return ~0; + return EXIT_FAILURE; } //create a usrp device @@ -82,7 +83,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //set the sample rate if (not vm.count("rate")){ std::cerr << "Please specify the sample rate with --rate" << std::endl; - return ~0; + return EXIT_FAILURE; } std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; usrp->set_rx_rate(rate); @@ -91,7 +92,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //set the center frequency if (not vm.count("freq")){ std::cerr << "Please specify the center frequency with --freq" << std::endl; - return ~0; + return EXIT_FAILURE; } std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; usrp->set_rx_freq(freq); @@ -147,7 +148,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //-- Initialize //------------------------------------------------------------------ initscr(); //curses init - usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); boost::system_time next_refresh = boost::get_system_time(); //------------------------------------------------------------------ @@ -188,11 +189,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //------------------------------------------------------------------ //-- Cleanup //------------------------------------------------------------------ - usrp->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); endwin(); //curses done //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 42ef33d70..4f312ad36 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -126,7 +126,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ stream_cmd.num_samps = total_num_samps; stream_cmd.stream_now = false; stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future); - usrp->issue_stream_cmd(stream_cmd); //tells all channels to stream + rx_stream->issue_stream_cmd(stream_cmd); //tells all channels to stream //meta-data will be filled in by recv() uhd::rx_metadata_t md; @@ -174,5 +174,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 5197bfe69..b0e498125 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -38,16 +38,24 @@ template<typename samp_type> void recv_to_file( const std::string &wire_format, const std::string &file, size_t samps_per_buff, - int num_requested_samples + unsigned long long num_requested_samples, + double time_requested = 0.0, + bool bw_summary = false, + bool stats = false, + bool null = false, + bool enable_size_map = false, + bool continue_on_bad_packet = false ){ - int num_total_samps = 0; + unsigned long long num_total_samps = 0; //create a receive streamer uhd::stream_args_t stream_args(cpu_format,wire_format); uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); uhd::rx_metadata_t md; std::vector<samp_type> buff(samps_per_buff); - std::ofstream outfile(file.c_str(), std::ofstream::binary); + std::ofstream outfile; + if (not null) + outfile.open(file.c_str(), std::ofstream::binary); bool overflow_message = true; //setup streaming @@ -58,10 +66,21 @@ template<typename samp_type> void recv_to_file( stream_cmd.num_samps = num_requested_samples; stream_cmd.stream_now = true; stream_cmd.time_spec = uhd::time_spec_t(); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); + + boost::system_time start = boost::get_system_time(); + unsigned long long ticks_requested = (long)(time_requested * (double)boost::posix_time::time_duration::ticks_per_second()); + boost::posix_time::time_duration ticks_diff; + boost::system_time last_update = start; + unsigned long long last_update_samps = 0; + + typedef std::map<size_t,size_t> SizeMap; + SizeMap mapSizes; while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ - size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0); + boost::system_time now = boost::get_system_time(); + + size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0, enable_size_map); if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { std::cout << boost::format("Timeout while streaming") << std::endl; @@ -81,17 +100,113 @@ template<typename samp_type> void recv_to_file( continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ - throw std::runtime_error(str(boost::format( + std::string error = str(boost::format( "Unexpected error code 0x%x" - ) % md.error_code)); + ) % md.error_code); + + if (continue_on_bad_packet){ + std::cerr << error << std::endl; + continue; + } + else + throw std::runtime_error(error); } + + if (enable_size_map){ + SizeMap::iterator it = mapSizes.find(num_rx_samps); + if (it == mapSizes.end()) + mapSizes[num_rx_samps] = 0; + mapSizes[num_rx_samps] += 1; + } num_total_samps += num_rx_samps; - outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + if (outfile.is_open()) + outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + + if (bw_summary){ + last_update_samps += num_rx_samps; + boost::posix_time::time_duration update_diff = now - last_update; + if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { + double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + double r = (double)last_update_samps / t; + std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; + last_update_samps = 0; + last_update = now; + } + } + + ticks_diff = now - start; + if (ticks_requested > 0){ + if ((unsigned long long)ticks_diff.ticks() > ticks_requested) + break; + } } + + if (outfile.is_open()) + outfile.close(); + + if (stats){ + std::cout << std::endl; - outfile.close(); + double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; + double r = (double)num_total_samps / t; + std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; + + if (enable_size_map) { + std::cout << std::endl; + std::cout << "Packet size map (bytes: count)" << std::endl; + for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) + std::cout << it->first << ":\t" << it->second << std::endl; + } + } +} + +typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t; + +bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){ + if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) + return false; + + boost::system_time start = boost::get_system_time(); + boost::system_time first_lock_time; + + std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; + std::cout.flush(); + + while (true){ + if ((not first_lock_time.is_not_a_date_time()) and + (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) + { + std::cout << " locked." << std::endl; + break; + } + + if (get_sensor_fn(sensor_name).to_bool()){ + if (first_lock_time.is_not_a_date_time()) + first_lock_time = boost::get_system_time(); + std::cout << "+"; + std::cout.flush(); + } + else{ + first_lock_time = boost::system_time(); //reset to 'not a date time' + + if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ + std::cout << std::endl; + throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); + } + + std::cout << "_"; + std::cout.flush(); + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + + std::cout << std::endl; + + return true; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -100,7 +215,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args, file, type, ant, subdev, ref, wirefmt; size_t total_num_samps, spb; - double rate, freq, gain, bw; + double rate, freq, gain, bw, total_time, setup_time; //setup the program options po::options_description desc("Allowed options"); @@ -110,15 +225,23 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to") ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive") + ("time", po::value<double>(&total_time)->default_value(0), "total number of seconds to receive") ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer") - ("rate", po::value<double>(&rate), "rate of incoming samples") - ("freq", po::value<double>(&freq), "RF center frequency in Hz") + ("rate", po::value<double>(&rate)->default_value(1e6), "rate of incoming samples") + ("freq", po::value<double>(&freq)->default_value(0.0), "RF center frequency in Hz") ("gain", po::value<double>(&gain), "gain for the RF chain") ("ant", po::value<std::string>(&ant), "daughterboard antenna selection") ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification") ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz") ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") + ("setup", po::value<double>(&setup_time)->default_value(1.0), "seconds of setup time") + ("progress", "show average bandwidth on exit") + ("stats", "periodically display short-term bandwidth") + ("sizemap", "track packet size and display breakdown on exit") + ("null", "run without writing to file") + ("continue", "don't abort on a bad packet") + ("skip-lo", "skip checking LO lock status") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -129,6 +252,15 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("UHD RX samples to file %s") % desc << std::endl; return ~0; } + + bool bw_summary = vm.count("progress") > 0; + bool stats = vm.count("stats") > 0; + bool null = vm.count("null") > 0; + bool enable_size_map = vm.count("sizemap") > 0; + bool continue_on_bad_packet = vm.count("continue") > 0; + + if (enable_size_map) + std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; //create a usrp device std::cout << std::endl; @@ -144,8 +276,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; //set the sample rate - if (not vm.count("rate")){ - std::cerr << "Please specify the sample rate with --rate" << std::endl; + if (rate <= 0.0){ + std::cerr << "Please specify a valid sample rate" << std::endl; return ~0; } std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl; @@ -153,13 +285,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; //set the center frequency - if (not vm.count("freq")){ - std::cerr << "Please specify the center frequency with --freq" << std::endl; - return ~0; - } - std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; - usrp->set_rx_freq(freq); - std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + if (vm.count("freq")){ //with default of 0.0 this will always be true + std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; + usrp->set_rx_freq(freq); + std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + } //set the rf gain if (vm.count("gain")){ @@ -178,41 +308,32 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //set the antenna if (vm.count("ant")) usrp->set_rx_antenna(ant); - boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for some setup time + boost::this_thread::sleep(boost::posix_time::seconds(setup_time)); //allow for some setup time - //Check Ref and LO Lock detect - std::vector<std::string> sensor_names; - sensor_names = usrp->get_rx_sensor_names(0); - if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end()) { - uhd::sensor_value_t lo_locked = usrp->get_rx_sensor("lo_locked",0); - std::cout << boost::format("Checking RX: %s ...") % lo_locked.to_pp_string() << std::endl; - UHD_ASSERT_THROW(lo_locked.to_bool()); - } - sensor_names = usrp->get_mboard_sensor_names(0); - if ((ref == "mimo") and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked") != sensor_names.end())) { - uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); - std::cout << boost::format("Checking RX: %s ...") % mimo_locked.to_pp_string() << std::endl; - UHD_ASSERT_THROW(mimo_locked.to_bool()); - } - if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { - uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); - std::cout << boost::format("Checking RX: %s ...") % ref_locked.to_pp_string() << std::endl; - UHD_ASSERT_THROW(ref_locked.to_bool()); - } + //check Ref and LO Lock detect + if (not vm.count("skip-lo")){ + check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); + if (ref == "mimo") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + if (ref == "external") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + } if (total_num_samps == 0){ std::signal(SIGINT, &sig_int_handler); std::cout << "Press Ctrl + C to stop streaming..." << std::endl; } +#define recv_to_file_args(format) \ + (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) //recv to file - if (type == "double") recv_to_file<std::complex<double> >(usrp, "fc64", wirefmt, file, spb, total_num_samps); - else if (type == "float") recv_to_file<std::complex<float> >(usrp, "fc32", wirefmt, file, spb, total_num_samps); - else if (type == "short") recv_to_file<std::complex<short> >(usrp, "sc16", wirefmt, file, spb, total_num_samps); + if (type == "double") recv_to_file<std::complex<double> >recv_to_file_args("fc64"); + else if (type == "float") recv_to_file<std::complex<float> >recv_to_file_args("fc32"); + else if (type == "short") recv_to_file<std::complex<short> >recv_to_file_args("sc16"); else throw std::runtime_error("Unknown type " + type); //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/rx_samples_to_udp.cpp b/host/examples/rx_samples_to_udp.cpp index 2acaa5d8b..0b3c6dce3 100644 --- a/host/examples/rx_samples_to_udp.cpp +++ b/host/examples/rx_samples_to_udp.cpp @@ -127,7 +127,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); stream_cmd.num_samps = total_num_samps; stream_cmd.stream_now = true; - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); //loop until total number of samples reached size_t num_acc_samps = 0; //number of accumulated samples @@ -168,5 +168,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 143bceb03..0eea2ffee 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -83,7 +83,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ stream_cmd.num_samps = total_num_samps; stream_cmd.stream_now = false; stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); //meta-data will be filled in by recv() uhd::rx_metadata_t md; @@ -124,5 +124,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp index 5b1e9f0e2..cfc745147 100644 --- a/host/examples/test_dboard_coercion.cpp +++ b/host/examples/test_dboard_coercion.cpp @@ -573,5 +573,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if(test_tx and test_rx) std::cout << std::endl; if(test_rx) std::cout << rx_results << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/test_messages.cpp b/host/examples/test_messages.cpp index afb092092..e39a8bd30 100644 --- a/host/examples/test_messages.cpp +++ b/host/examples/test_messages.cpp @@ -46,7 +46,7 @@ bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streame stream_cmd.num_samps = rx_stream->get_max_num_samps(); stream_cmd.stream_now = false; stream_cmd.time_spec = uhd::time_spec_t(100.0); //time in the past - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); uhd::rx_metadata_t md; @@ -90,7 +90,7 @@ bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streame uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); stream_cmd.stream_now = true; stream_cmd.num_samps = rx_stream->get_max_num_samps(); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); std::vector<std::complex<float> > buff(rx_stream->get_max_num_samps()); uhd::rx_metadata_t md; @@ -132,7 +132,7 @@ bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streame * Send a burst of many samples that will fragment internally. * We expect to get an burst ack async message. */ -bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ +bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ std::cout << "Test burst ack message... " << std::flush; uhd::tx_metadata_t md; @@ -148,7 +148,7 @@ bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer:: ); uhd::async_metadata_t async_md; - if (not usrp->get_device()->recv_async_msg(async_md)){ + if (not tx_stream->recv_async_msg(async_md)){ std::cout << boost::format( "failed:\n" " Async message recv timed out.\n" @@ -178,7 +178,7 @@ bool test_burst_ack_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer:: * Send a start of burst packet with no following end of burst. * We expect to get an underflow(within a burst) async message. */ -bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ +bool test_underflow_message(uhd::usrp::multi_usrp::sptr, uhd::rx_streamer::sptr, uhd::tx_streamer::sptr tx_stream){ std::cout << "Test underflow message... " << std::flush; uhd::tx_metadata_t md; @@ -189,7 +189,7 @@ bool test_underflow_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer:: tx_stream->send("", 0, md); uhd::async_metadata_t async_md; - if (not usrp->get_device()->recv_async_msg(async_md, 1)){ + if (not tx_stream->recv_async_msg(async_md, 1)){ std::cout << boost::format( "failed:\n" " Async message recv timed out.\n" @@ -233,7 +233,7 @@ bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer: tx_stream->send("", 0, md); uhd::async_metadata_t async_md; - if (not usrp->get_device()->recv_async_msg(async_md)){ + if (not tx_stream->recv_async_msg(async_md)){ std::cout << boost::format( "failed:\n" " Async message recv timed out.\n" @@ -258,9 +258,9 @@ bool test_time_error_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer: } } -void flush_async(uhd::usrp::multi_usrp::sptr usrp){ +void flush_async(uhd::tx_streamer::sptr tx_stream){ uhd::async_metadata_t async_md; - while (usrp->get_device()->recv_async_msg(async_md)){} + while (tx_stream->recv_async_msg(async_md)){} } void flush_recv(uhd::rx_streamer::sptr rx_stream){ @@ -331,7 +331,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ for (size_t n = 0; n < ntests; n++){ std::string key = tests.keys()[std::rand() % tests.size()]; bool pass = tests[key](usrp, rx_stream, tx_stream); - flush_async(usrp); + flush_async(tx_stream); flush_recv(rx_stream); //store result @@ -350,5 +350,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/test_pps_input.cpp b/host/examples/test_pps_input.cpp index 994b7ca87..889c98a45 100644 --- a/host/examples/test_pps_input.cpp +++ b/host/examples/test_pps_input.cpp @@ -31,12 +31,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args; + std::string time_source; //setup the program options po::options_description desc("Allowed options"); desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") + ("source", po::value<std::string>(&time_source)->default_value(""), "the time source (gpsdo, external) or blank for default") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); @@ -54,9 +56,15 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + //sleep off if gpsdo detected and time next pps already set + boost::this_thread::sleep(boost::posix_time::seconds(1)); + + //set time source if specified + if (not time_source.empty()) usrp->set_time_source(time_source); + //set the time at an unknown pps (will throw if no pps) std::cout << std::endl << "Attempt to detect the PPS and set the time..." << std::endl << std::endl; usrp->set_time_unknown_pps(uhd::time_spec_t(0.0)); std::cout << std::endl << "Success!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp index 34c83dfd6..5dee58887 100644 --- a/host/examples/test_timed_commands.cpp +++ b/host/examples/test_timed_commands.cpp @@ -97,7 +97,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ stream_cmd.stream_now = true; const uhd::time_spec_t stream_time = usrp->get_time_now() + uhd::time_spec_t(0.1); usrp->set_command_time(stream_time); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); usrp->clear_command_time(); //meta-data will be filled in by recv() @@ -125,5 +125,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index e32912eaa..a44e81a82 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -41,15 +41,9 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * RX Hammer **********************************************************************/ -void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, const std::string &rx_otw){ +void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ uhd::set_thread_priority_safe(); - //create a receive streamer - uhd::stream_args_t stream_args(rx_cpu, rx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - //print pre-test summary std::cout << boost::format( "Testing receive rate %f Msps" @@ -60,7 +54,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, cons const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); std::vector<void *> buffs; - for (size_t ch = 0; ch < stream_args.channels.size(); ch++) + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) buffs.push_back(&buff.front()); //same buffer for each channel bool had_an_overflow = false; uhd::time_spec_t last_time; @@ -74,7 +68,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, cons while (not boost::this_thread::interruption_requested()){ cmd.num_samps = rand() % 100000; - usrp->issue_stream_cmd(cmd); + rx_stream->issue_stream_cmd(cmd); num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md, timeout, true); //handle the error codes @@ -103,16 +97,15 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, cons /*********************************************************************** * TX Hammer **********************************************************************/ -void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, const std::string &tx_otw){ +void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ uhd::set_thread_priority_safe(); - //create a transmit streamer - uhd::stream_args_t stream_args(tx_cpu, tx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); uhd::tx_metadata_t md; - std::vector<std::complex<float> > buff(10000); + const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); + std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); + std::vector<void *> buffs; + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) + buffs.push_back(&buff.front()); //same buffer for each channel //print pre-test summary std::cout << boost::format( @@ -130,7 +123,7 @@ void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, cons while(num_acc_samps < total_num_samps){ //send a single packet - num_tx_samps += tx_stream->send(&buff, tx_stream->get_max_num_samps(), md, timeout); + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout); num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); } @@ -140,13 +133,13 @@ void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, cons } } -void tx_hammer_async_helper(uhd::usrp::multi_usrp::sptr usrp){ +void tx_hammer_async_helper(uhd::tx_streamer::sptr tx_stream){ //setup variables and allocate buffer uhd::async_metadata_t async_md; while (not boost::this_thread::interruption_requested()){ - if (not usrp->get_device()->recv_async_msg(async_md)) continue; + if (not tx_stream->recv_async_msg(async_md)) continue; //handle the error codes switch(async_md.event_code){ @@ -239,14 +232,24 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //spawn the receive test thread if (vm.count("rx_rate")){ usrp->set_rx_rate(rx_rate); - thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_cpu, rx_otw)); + //create a receive streamer + uhd::stream_args_t stream_args(rx_cpu, rx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_cpu, rx_stream)); } //spawn the transmit test thread if (vm.count("tx_rate")){ usrp->set_tx_rate(tx_rate); - thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_cpu, tx_otw)); - thread_group.create_thread(boost::bind(&tx_hammer_async_helper, usrp)); + //create a transmit streamer + uhd::stream_args_t stream_args(tx_cpu, tx_otw); + for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping + stream_args.channels.push_back(ch); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); + thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_cpu, tx_stream)); + thread_group.create_thread(boost::bind(&tx_hammer_async_helper, tx_stream)); } //sleep for the required duration @@ -272,5 +275,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp index 4cf5f2fd1..c58d3e178 100644 --- a/host/examples/tx_bursts.cpp +++ b/host/examples/tx_bursts.cpp @@ -147,7 +147,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::async_metadata_t async_md; bool got_async_burst_ack = false; //loop through all messages for the ACK packet (may have underflow messages in queue) - while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, seconds_in_future)){ + while (not got_async_burst_ack and tx_stream->recv_async_msg(async_md, seconds_in_future)){ got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); } std::cout << (got_async_burst_ack? "success" : "fail") << std::endl; @@ -156,5 +156,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/tx_samples_from_file.cpp b/host/examples/tx_samples_from_file.cpp index 04a6a63d4..f9447c25d 100644 --- a/host/examples/tx_samples_from_file.cpp +++ b/host/examples/tx_samples_from_file.cpp @@ -192,5 +192,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index 3b8cc75d4..2eef80389 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -116,7 +116,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::async_metadata_t async_md; bool got_async_burst_ack = false; //loop through all messages for the ACK packet (may have underflow messages in queue) - while (not got_async_burst_ack and usrp->get_device()->recv_async_msg(async_md, timeout)){ + while (not got_async_burst_ack and tx_stream->recv_async_msg(async_md, timeout)){ got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); } std::cout << (got_async_burst_ack? "success" : "fail") << std::endl; @@ -124,5 +124,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 3c5eecd65..40326ae93 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -260,5 +260,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 495c9f7e4..ef119b689 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -151,7 +151,7 @@ template<typename samp_type> void recv_to_file( stream_cmd.num_samps = num_requested_samples; stream_cmd.stream_now = false; stream_cmd.time_spec = uhd::time_spec_t(settling_time); - usrp->issue_stream_cmd(stream_cmd); + rx_stream->issue_stream_cmd(stream_cmd); while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, timeout); @@ -443,5 +443,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index 89c6332da..de39383c9 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -82,16 +82,6 @@ public: //! Make a new transmit streamer from the streamer arguments virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; - /*! - * Receive and asynchronous message from the device. - * \param async_metadata the metadata to be filled in - * \param timeout the timeout in seconds to wait for a message - * \return true when the async_metadata is valid, false for timeout - */ - virtual bool recv_async_msg( - async_metadata_t &async_metadata, double timeout = 0.1 - ) = 0; - //! Get access to the underlying property structure virtual boost::shared_ptr<property_tree> get_tree(void) const = 0; diff --git a/host/include/uhd/device_deprecated.ipp b/host/include/uhd/device_deprecated.ipp index 0ee1cd706..58c43a876 100644 --- a/host/include/uhd/device_deprecated.ipp +++ b/host/include/uhd/device_deprecated.ipp @@ -184,6 +184,16 @@ size_t get_max_recv_samps_per_packet(void){ return _rx_streamer->get_max_num_samps(); } +/*! + * Receive and asynchronous message from the device. + * \param async_metadata the metadata to be filled in + * \param timeout the timeout in seconds to wait for a message + * \return true when the async_metadata is valid, false for timeout + */ +virtual bool recv_async_msg( + async_metadata_t &async_metadata, double timeout = 0.1 +) = 0; + private: rx_streamer::sptr _rx_streamer; io_type_t::tid_t _recv_tid; diff --git a/host/include/uhd/exception.hpp b/host/include/uhd/exception.hpp index c05861788..69e902fd5 100644 --- a/host/include/uhd/exception.hpp +++ b/host/include/uhd/exception.hpp @@ -157,9 +157,9 @@ namespace uhd{ * If the code evaluates to false, throw an assertion error. * \param code the code that resolved to a boolean */ - #define UHD_ASSERT_THROW(code) if (not (code)) \ + #define UHD_ASSERT_THROW(code) {if (not (code)) \ throw uhd::assertion_error(UHD_THROW_SITE_INFO(#code)); \ - else void(0) + } } //namespace uhd diff --git a/host/include/uhd/stream.hpp b/host/include/uhd/stream.hpp index c8ab303ad..75b097ded 100644 --- a/host/include/uhd/stream.hpp +++ b/host/include/uhd/stream.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include <uhd/config.hpp> #include <uhd/types/metadata.hpp> #include <uhd/types/device_addr.hpp> +#include <uhd/types/stream_cmd.hpp> #include <uhd/types/ref_vector.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> @@ -124,6 +125,8 @@ class UHD_API rx_streamer : boost::noncopyable{ public: typedef boost::shared_ptr<rx_streamer> sptr; + virtual ~rx_streamer(void); + //! Get the number of channels associated with this streamer virtual size_t get_num_channels(void) const = 0; @@ -170,6 +173,19 @@ public: const double timeout = 0.1, const bool one_packet = false ) = 0; + + /*! + * Issue a stream command to the usrp device. + * This tells the usrp to send samples into the host. + * See the documentation for stream_cmd_t for more info. + * + * With multiple devices, the first stream command in a chain of commands + * should have a time spec in the near future and stream_now = false; + * to ensure that the packets can be aligned by their time specs. + * + * \param stream_cmd the stream command to issue + */ + virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; }; /*! @@ -181,6 +197,8 @@ class UHD_API tx_streamer : boost::noncopyable{ public: typedef boost::shared_ptr<tx_streamer> sptr; + virtual ~tx_streamer(void); + //! Get the number of channels associated with this streamer virtual size_t get_num_channels(void) const = 0; @@ -217,6 +235,16 @@ public: const tx_metadata_t &metadata, const double timeout = 0.1 ) = 0; + + /*! + * Receive and asynchronous message from this TX stream. + * \param async_metadata the metadata to be filled in + * \param timeout the timeout in seconds to wait for a message + * \return true when the async_metadata is valid, false for timeout + */ + virtual bool recv_async_msg( + async_metadata_t &async_metadata, double timeout = 0.1 + ) = 0; }; } //namespace uhd diff --git a/host/include/uhd/transport/bounded_buffer.ipp b/host/include/uhd/transport/bounded_buffer.ipp index 9c24005b7..35ffb293b 100644 --- a/host/include/uhd/transport/bounded_buffer.ipp +++ b/host/include/uhd/transport/bounded_buffer.ipp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ #include <boost/thread/condition.hpp> #include <boost/thread/locks.hpp> -namespace uhd{ namespace transport{ namespace{ /*anon*/ +namespace uhd{ namespace transport{ template <typename elem_type> class bounded_buffer_detail : boost::noncopyable{ public: @@ -142,6 +142,6 @@ namespace uhd{ namespace transport{ namespace{ /*anon*/ } }; -}}} //namespace +}} //namespace #endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP */ diff --git a/host/include/uhd/transport/usb_control.hpp b/host/include/uhd/transport/usb_control.hpp index 2af4b3bbe..92b10f339 100644 --- a/host/include/uhd/transport/usb_control.hpp +++ b/host/include/uhd/transport/usb_control.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -51,6 +51,7 @@ public: * \param index 2-byte (wIndex) * \param buff buffer to hold send or receive data * \param length 2-byte (wLength) + * \param timeout 4-byte (timeout, default is infinite wait) * \return number of bytes submitted or error code */ virtual ssize_t submit(boost::uint8_t request_type, @@ -58,7 +59,8 @@ public: boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length) = 0; + boost::uint16_t length, + boost::int32_t timeout = 0) = 0; }; }} //namespace diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index 6f8d868be..2951d7fbb 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -43,12 +43,24 @@ public: typedef boost::shared_ptr<usb_device_handle> sptr; /*! - * Return the device's serial number + * Return the device's serial number * \return a string describing the device's serial number */ virtual std::string get_serial() const = 0; /*! + * Return the device's manufacturer identification string + * \return a string describing the device's manufacturer string + */ + virtual std::string get_manufacturer() const = 0; + + /*! + * Return the device's product identification string + * \return a string describing the device's product string + */ + virtual std::string get_product() const = 0; + + /*! * Return the device's Vendor ID (usually assigned by the USB-IF) * \return a Vendor ID */ @@ -61,7 +73,7 @@ public: virtual boost::uint16_t get_product_id() const = 0; /*! - * Return a vector of USB devices on this host + * Return a vector of USB devices on this host * \return a vector of USB device handles that match vid and pid */ static std::vector<usb_device_handle::sptr> get_device_list(boost::uint16_t vid, boost::uint16_t pid); diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp index 24b709ec9..3990568c3 100644 --- a/host/include/uhd/transport/usb_zero_copy.hpp +++ b/host/include/uhd/transport/usb_zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -61,24 +61,6 @@ public: const size_t send_endpoint, const device_addr_t &hints = device_addr_t() ); - - /*! - * Make a wrapper around a zero copy implementation. - * The wrapper performs the following functions: - * - Pad commits to the frame boundary - * - Extract multiple packets on recv - * - * When enable multiple receive packets is set to true, - * the implementation inspects the vita length on transfers, - * and may split a single transfer into multiple managed buffers. - * - * \param usb_zc a usb zero copy interface object - * \param usb_frame_boundary bytes per frame - * \return a new zero copy wrapper object - */ - static sptr make_wrapper( - sptr usb_zc, size_t usb_frame_boundary = 512 - ); }; }} //namespace diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp index 1be480874..d16892281 100644 --- a/host/include/uhd/transport/vrt_if_packet.hpp +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,6 +26,9 @@ namespace uhd{ namespace transport{ namespace vrt{ + //! The maximum number of 32-bit words in the vrlp link layer + static const size_t num_vrl_words32 = 3; + //! The maximum number of 32-bit words in a vrt if packet header static const size_t max_if_hdr_words32 = 7; //hdr+sid+cid+tsi+tsf @@ -34,12 +37,24 @@ namespace vrt{ * The size fields are used for input and output depending upon * the operation used (ie the pack or unpack function call). */ - struct UHD_API if_packet_info_t{ - //packet type (pack only supports data) - enum packet_type_t { + struct UHD_API if_packet_info_t + { + if_packet_info_t(void); + + //link layer type - always set for pack and unpack + enum link_type_t + { + LINK_TYPE_NONE = 0x0, + LINK_TYPE_CHDR = 0x1, + LINK_TYPE_VRLP = 0x2, + } link_type; + + //packet type + enum packet_type_t + { PACKET_TYPE_DATA = 0x0, - PACKET_TYPE_EXTENSION = 0x1, - PACKET_TYPE_CONTEXT = 0x2 + PACKET_TYPE_IF_EXT = 0x1, + PACKET_TYPE_CONTEXT = 0x2, //extension context: has_sid = true } packet_type; //size fields @@ -100,6 +115,22 @@ namespace vrt{ if_packet_info_t &if_packet_info ); + UHD_INLINE if_packet_info_t::if_packet_info_t(void): + link_type(LINK_TYPE_NONE), + packet_type(PACKET_TYPE_DATA), + num_payload_words32(0), + num_payload_bytes(0), + num_header_words32(0), + num_packet_words32(0), + packet_count(0), + sob(false), eob(false), + has_sid(false), sid(0), + has_cid(false), cid(0), + has_tsi(false), tsi(0), + has_tsf(false), tsf(0), + has_tlr(false), tlr(0) + {} + } //namespace vrt }} //namespace diff --git a/host/include/uhd/types/serial.hpp b/host/include/uhd/types/serial.hpp index f66822775..7b565c633 100644 --- a/host/include/uhd/types/serial.hpp +++ b/host/include/uhd/types/serial.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -46,13 +46,18 @@ namespace uhd{ public: typedef boost::shared_ptr<i2c_iface> sptr; + virtual ~i2c_iface(void); + + //! Create an i2c_iface than can talk to 16 bit addressable EEPROMS + i2c_iface::sptr eeprom16(void); + /*! * Write bytes over the i2c. * \param addr the address * \param buf the vector of bytes */ virtual void write_i2c( - boost::uint8_t addr, + boost::uint16_t addr, const byte_vector_t &buf ) = 0; @@ -63,7 +68,7 @@ namespace uhd{ * \return a vector of bytes */ virtual byte_vector_t read_i2c( - boost::uint8_t addr, + boost::uint16_t addr, size_t num_bytes ) = 0; @@ -74,8 +79,8 @@ namespace uhd{ * \param buf the vector of bytes */ virtual void write_eeprom( - boost::uint8_t addr, - boost::uint8_t offset, + boost::uint16_t addr, + boost::uint16_t offset, const byte_vector_t &buf ); @@ -87,8 +92,8 @@ namespace uhd{ * \return a vector of bytes */ virtual byte_vector_t read_eeprom( - boost::uint8_t addr, - boost::uint8_t offset, + boost::uint16_t addr, + boost::uint16_t offset, size_t num_bytes ); }; @@ -128,6 +133,8 @@ namespace uhd{ public: typedef boost::shared_ptr<spi_iface> sptr; + virtual ~spi_iface(void); + /*! * Perform a spi transaction. * \param which_slave the slave device number @@ -182,6 +189,8 @@ namespace uhd{ public: typedef boost::shared_ptr<uart_iface> sptr; + virtual ~uart_iface(void); + /*! * Write to a serial port. * \param buf the data to write diff --git a/host/include/uhd/types/tune_request.hpp b/host/include/uhd/types/tune_request.hpp index 9c498bfe9..dd6123a9f 100644 --- a/host/include/uhd/types/tune_request.hpp +++ b/host/include/uhd/types/tune_request.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2012 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #define INCLUDED_UHD_TYPES_TUNE_REQUEST_HPP #include <uhd/config.hpp> +#include <uhd/types/device_addr.hpp> namespace uhd{ @@ -88,6 +89,18 @@ namespace uhd{ */ double dsp_freq; + /*! + * The args parameter is used to pass arbitrary key/value pairs. + * Possible keys used by args (depends on implementation): + * + * - mode_n: Allows the user to tell the daughterboard tune code + * to choose between an integer N diviver or fractional N divider. + * Default is fractional N on boards that support fractional N tuning. + * Fractional N provides greater tuning acuracy at the expense of spurs. + * Possible options for this key: "integer" or "fractional". + */ + device_addr_t args; + }; } //namespace uhd diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index 6a6a68321..b0f92e2ab 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -290,6 +290,8 @@ private: protected: dboard_iface(void); +public: + virtual ~dboard_iface(void); }; diff --git a/host/include/uhd/utils/msg.hpp b/host/include/uhd/utils/msg.hpp index 3aac9577a..2cc5893e7 100644 --- a/host/include/uhd/utils/msg.hpp +++ b/host/include/uhd/utils/msg.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include <uhd/config.hpp> #include <uhd/utils/pimpl.hpp> #include <ostream> +#include <iomanip> #include <string> /*! @@ -38,6 +39,10 @@ #define UHD_VAR(var) \ UHD_MSG(status) << #var << " = " << var << std::endl; +//! Helpful debug tool to print a variable in hex +#define UHD_HEX(var) \ + UHD_MSG(status) << #var << " = 0x" << std::hex << std::setfill('0') << std::setw(8) << var << std::dec << std::endl; + namespace uhd{ namespace msg{ //! Possible message types diff --git a/host/include/uhd/utils/paths.hpp b/host/include/uhd/utils/paths.hpp index f5a40b2c9..e0f455e92 100644 --- a/host/include/uhd/utils/paths.hpp +++ b/host/include/uhd/utils/paths.hpp @@ -29,8 +29,8 @@ namespace uhd{ //! Get a string representing the system's appdata directory UHD_API std::string get_app_path(void); - //! Get a string representing the system's pkg data directory - UHD_API std::string get_pkg_data_path(void); + //! Get a string representing the system's pkg directory + UHD_API std::string get_pkg_path(void); } //namespace uhd diff --git a/host/include/uhd/version.hpp b/host/include/uhd/version.hpp index ab693f2b0..a62ee8285 100644 --- a/host/include/uhd/version.hpp +++ b/host/include/uhd/version.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.4.0-3" +#define UHD_VERSION_ABI_STRING "3.6.0-1" namespace uhd{ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index a37a8ab85..4ca06af9a 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011,2013 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -88,6 +88,7 @@ CONFIGURE_FILE( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.cpp diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index 0d9d0983f..28defa8bc 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -24,12 +24,11 @@ MESSAGE(STATUS "") ######################################################################## # Look for Orc support ######################################################################## -FIND_PACKAGE(PkgConfig) -IF(PKG_CONFIG_FOUND) -PKG_CHECK_MODULES(ORC "orc-0.4 > 0.4.11") -ENDIF(PKG_CONFIG_FOUND) +FIND_PACKAGE(ORC) -FIND_PROGRAM(ORCC_EXECUTABLE orcc) +IF(NOT ORCC_EXECUTABLE) + FIND_PROGRAM(ORCC_EXECUTABLE orcc) +ENDIF() LIBUHD_REGISTER_COMPONENT("ORC" ENABLE_ORC ON "ENABLE_LIBUHD;ORC_FOUND;ORCC_EXECUTABLE" OFF) diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index dc2ab7847..b6e121db3 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -33,6 +33,11 @@ LIBUHD_PYTHON_GEN_SOURCE( ) LIBUHD_PYTHON_GEN_SOURCE( + ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2870_regs.py + ${CMAKE_CURRENT_BINARY_DIR}/max2870_regs.hpp +) + +LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4360_regs.py ${CMAKE_CURRENT_BINARY_DIR}/adf4360_regs.hpp ) diff --git a/host/lib/ic_reg_maps/gen_max2870_regs.py b/host/lib/ic_reg_maps/gen_max2870_regs.py new file mode 100644 index 000000000..f26c27281 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_max2870_regs.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## + +REGS_TMPL="""\ +######################################################################## +## Address 0x00 +## Divider control +## Write-only, default = 0x007D0000 +######################################################################## +int_n_mode 0x00[31] 0 frac_n, int_n +int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. +frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 +######################################################################## +## Address 0x01 +## Charge pump control +## Write-only, default = 0x2000FFF9 +######################################################################## +cpoc 0x01[31] 0 disabled, enabled +cpl 0x01[29:30] 1 disabled, enabled, res1, res2 +cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink +phase_12_bit 0x01[15:26] 1 ##sets phase shift +mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus +######################################################################## +## Address 0x02 +## Misc. control +## Write-only, default = 0x00004042 +######################################################################## +lds 0x02[31] 0 slow, fast +low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, low_spur_2 +muxout 0x02[26:28] 1 tri_state, high, low, rdiv, ndiv, ald, dld, res7 +reference_doubler 0x02[25] 0 disabled, enabled +reference_divide_by_2 0x02[24] 0 disabled, enabled +r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023 +double_buffer 0x02[13] 0 disabled, enabled +#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) +charge_pump_current 0x02[9:12] 7 $current_setting_enums +ldf 0x02[8] 0 frac_n, int_n +ldp 0x02[7] 0 10ns, 6ns +pd_polarity 0x02[6] 1 negative, positive +power_down 0x02[5] 0 normal, shutdown +cp_three_state 0x02[4] 0 disabled, enabled +counter_reset 0x02[3] 0 normal, reset +######################################################################## +## Address 0x03 +## VCO control +## Write-only, default = 0x0000000B +######################################################################## +vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd +vas 0x03[25] 0 enabled, disabled ##VCO autoselect +retune 0x03[24] 1 disabled, enabled +clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved +clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095 +######################################################################## +## Address 0x04 +## RF output control +## Write-only, default = 0x6180B23C +######################################################################## +res4 0x04[26:31] 0x18 +bs_msb 0x04[24:25] 0 ##Band select MSBs +feedback_select 0x04[23] 1 divided, fundamental +rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128 +band_select_clock_div 0x04[12:19] 0 +aux_output_select 0x04[9] 1 divided, fundamental +aux_output_enable 0x04[8] 0 disabled, enabled +aux_output_power 0x04[6:7] 0 m4dBm, m1dBm, 2dBm, 5dBm +rf_output_enable 0x04[5] 1 disabled, enabled +output_power 0x04[3:4] 3 m4dBm, m1dBm, 2dBm, 5dBm +######################################################################## +## Address 0x05 +## Misc +## Write only, default = 0x00400005 +######################################################################## +f01 0x05[24] 1 frac_n, auto +ld_pin_mode 0x05[22:23] 1 low, dld, ald, high +mux_sdo 0x05[18] 0 normal, sdo +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +enum addr_t{ + ADDR_R0 = 0, + ADDR_R1 = 1, + ADDR_R2 = 2, + ADDR_R3 = 3, + ADDR_R4 = 4, + ADDR_R5 = 5 +}; + +boost::uint32_t get_reg(boost::uint8_t addr){ + boost::uint32_t reg = addr & 0x7; + switch(addr){ + #for $addr in range(5+1) + case $addr: + #for $reg in filter(lambda r: r.get_addr() == addr, $regs) + reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); + #end for + break; + #end for + } + return reg; +} +""" + +if __name__ == '__main__': + import common; common.generate( + name='max2870_regs', + regs_tmpl=REGS_TMPL, + body_tmpl=BODY_TMPL, + file=__file__, + ) + diff --git a/host/lib/stream.cpp b/host/lib/stream.cpp new file mode 100644 index 000000000..9fafad9ec --- /dev/null +++ b/host/lib/stream.cpp @@ -0,0 +1,30 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/stream.hpp> + +using namespace uhd; + +rx_streamer::~rx_streamer(void) +{ + //empty +} + +tx_streamer::~tx_streamer(void) +{ + //empty +} diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 6524a8412..963edcf85 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -37,6 +37,10 @@ IF(ENABLE_USB) ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_base.hpp ) + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/libusb1_zero_copy.cpp + PROPERTIES COMPILE_DEFINITIONS "${LIBUSB_DEFINITIONS}" + ) ELSE(ENABLE_USB) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/usb_dummy_impl.cpp @@ -118,5 +122,4 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp ) diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index e28ce3aae..56e7c61bf 100644 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -48,12 +48,14 @@ TMPL_TEXT = """ using namespace uhd; using namespace uhd::transport; +using namespace uhd::transport::vrt; typedef size_t pred_type; typedef std::vector<pred_type> pred_table_type; #define pred_table_index(hdr) ((hdr >> 20) & 0x1ff) -static pred_table_type get_pred_unpack_table(void){ +static pred_table_type get_pred_unpack_table(void) +{ pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28) for (size_t i = 0; i < table.size(); i++){ boost::uint32_t vrt_hdr_word = i << 20; @@ -74,13 +76,43 @@ static const pred_table_type pred_unpack_table(get_pred_unpack_table()); //maps num empty bytes to trailer bits static const size_t occ_table[] = {0, 2, 1, 3}; +const boost::uint32_t VRLP = ('V' << 24) | ('R' << 16) | ('L' << 8) | ('P' << 0); +const boost::uint32_t VEND = ('V' << 24) | ('E' << 16) | ('N' << 8) | ('D' << 0); + +UHD_INLINE static boost::uint32_t chdr_to_vrt(const boost::uint32_t chdr, size_t &packet_count) +{ + boost::uint32_t vrt = chdr & 0xffff; //words32 + packet_count = (chdr >> 16) & 0xfff; + vrt |= ((chdr >> 31) & 0x1) << 30; //context packet + vrt |= ((chdr >> 30) & 0x1) << 26; //has tlr + vrt |= ((chdr >> 29) & 0x1) << 20; //has tsf + vrt |= ((chdr >> 28) & 0x1) << 24; //has eob + vrt |= (0x1) << 28; //has sid (always) + return vrt; +} + +UHD_INLINE static boost::uint32_t vrt_to_chdr(const boost::uint32_t vrt, const size_t packet_count) +{ + boost::uint32_t chdr = vrt & 0xffff; //words32 + chdr |= (packet_count & 0xfff) << 16; + chdr |= ((vrt >> 30) & 0x1) << 31; //context packet + chdr |= ((vrt >> 26) & 0x1) << 30; //has tlr + chdr |= ((vrt >> 20) & 0x1) << 29; //has tsf + chdr |= ((vrt >> 24) & 0x1) << 28; //has eob + return chdr; +} + ######################################################################## #def gen_code($XE_MACRO, $suffix) ######################################################################## -void vrt::if_hdr_pack_$(suffix)( +/*********************************************************************** + * interal impl of packing VRT IF header only + **********************************************************************/ +UHD_INLINE void __if_hdr_pack_$(suffix)( boost::uint32_t *packet_buff, - if_packet_info_t &if_packet_info + if_packet_info_t &if_packet_info, + boost::uint32_t &vrt_hdr_word32 ){ boost::uint32_t vrt_hdr_flags = 0; @@ -154,31 +186,33 @@ void vrt::if_hdr_pack_$(suffix)( } //fill in complete header word - packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0 + vrt_hdr_word32 = boost::uint32_t(0 | (if_packet_info.packet_type << 29) | vrt_hdr_flags | ((if_packet_info.packet_count & 0xf) << 16) | (if_packet_info.num_packet_words32 & 0xffff) - )); + ); } -void vrt::if_hdr_unpack_$(suffix)( +/*********************************************************************** + * interal impl of unpacking VRT IF header only + **********************************************************************/ +UHD_INLINE void __if_hdr_unpack_$(suffix)( const boost::uint32_t *packet_buff, - if_packet_info_t &if_packet_info + if_packet_info_t &if_packet_info, + const boost::uint32_t vrt_hdr_word32 ){ - //extract vrt header - boost::uint32_t vrt_hdr_word = $(XE_MACRO)(packet_buff[0]); - size_t packet_words32 = vrt_hdr_word & 0xffff; + const size_t packet_words32 = vrt_hdr_word32 & 0xffff; //failure case if (if_packet_info.num_packet_words32 < packet_words32) throw uhd::value_error("bad vrt header or packet fragment"); //extract fields from the header - if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word >> 29); - if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf; + if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word32 >> 29); + if_packet_info.packet_count = (vrt_hdr_word32 >> 16) & 0xf; - const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word)]; + const pred_type pred = pred_unpack_table[pred_table_index(vrt_hdr_word32)]; size_t empty_bytes = 0; @@ -259,6 +293,84 @@ void vrt::if_hdr_unpack_$(suffix)( } } +/*********************************************************************** + * link layer + VRT IF packing + **********************************************************************/ +void vrt::if_hdr_pack_$(suffix)( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +){ + boost::uint32_t vrt_hdr_word32 = 0; + switch (if_packet_info.link_type) + { + case if_packet_info_t::LINK_TYPE_NONE: + __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + packet_buff[0] = $(XE_MACRO)(vrt_hdr_word32); + break; + + case if_packet_info_t::LINK_TYPE_CHDR: + { + __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + const boost::uint32_t chdr = vrt_to_chdr(vrt_hdr_word32, if_packet_info.packet_count); + packet_buff[0] = $(XE_MACRO)(chdr); + break; + } + + case if_packet_info_t::LINK_TYPE_VRLP: + __if_hdr_pack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + if_packet_info.num_header_words32 += 2; + if_packet_info.num_packet_words32 += 3; + packet_buff[0] = $(XE_MACRO)(VRLP); + packet_buff[1] = $(XE_MACRO)(boost::uint32_t( + (if_packet_info.num_packet_words32 & 0xfffff) | + ((if_packet_info.packet_count & 0xfff) << 20) + )); + packet_buff[2] = $(XE_MACRO)(vrt_hdr_word32); + packet_buff[if_packet_info.num_packet_words32-1] = $(XE_MACRO)(VEND); + break; + } +} + +/*********************************************************************** + * link layer + VRT IF unpacking + **********************************************************************/ +void vrt::if_hdr_unpack_$(suffix)( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +){ + boost::uint32_t vrt_hdr_word32 = 0; + switch (if_packet_info.link_type) + { + case if_packet_info_t::LINK_TYPE_NONE: + vrt_hdr_word32 = $(XE_MACRO)(packet_buff[0]); + __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + break; + + case if_packet_info_t::LINK_TYPE_CHDR: + { + const boost::uint32_t chdr = $(XE_MACRO)(packet_buff[0]); + size_t packet_count = 0; + vrt_hdr_word32 = chdr_to_vrt(chdr, packet_count); + __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + if_packet_info.packet_count = packet_count; + break; + } + + case if_packet_info_t::LINK_TYPE_VRLP: + { + if ($(XE_MACRO)(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP"); + const boost::uint32_t vrl_hdr = $(XE_MACRO)(packet_buff[1]); + vrt_hdr_word32 = $(XE_MACRO)(packet_buff[2]); + if (if_packet_info.num_packet_words32 < (vrl_hdr & 0xfffff)) throw uhd::value_error("bad vrl header or packet fragment"); + if ($(XE_MACRO)(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND"); + __if_hdr_unpack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + if_packet_info.num_header_words32 += 2; //add vrl header + if_packet_info.packet_count = (vrl_hdr >> 20) & 0xfff; + break; + } + } +} + ######################################################################## #end def ######################################################################## diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index d4ec874f1..0ef53db0a 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include <boost/weak_ptr.hpp> #include <boost/thread/mutex.hpp> #include <boost/foreach.hpp> +#include <cstdlib> #include <iostream> using namespace uhd; @@ -59,6 +60,15 @@ libusb::session::sptr libusb::session::get_global_session(void){ //create a new global session sptr new_global_session(new libusb_session_impl()); global_session = new_global_session; + + //set logging if envvar is set + const char *level_string = getenv("LIBUSB_DEBUG_LEVEL"); + if (level_string != NULL) + { + const int level = int(level_string[0] - '0'); //easy conversion to integer + if (level >= 0 and level <= 3) libusb_set_debug(new_global_session->get_context(), level); + } + return new_global_session; } @@ -137,8 +147,13 @@ public: return _desc; } - std::string get_ascii_serial(void) const{ - if (this->get().iSerialNumber == 0) return ""; + std::string get_ascii_property(const std::string &what) const + { + boost::uint8_t off = 0; + if (what == "serial") off = this->get().iSerialNumber; + if (what == "product") off = this->get().iProduct; + if (what == "manufacturer") off = this->get().iManufacturer; + if (off == 0) return ""; libusb::device_handle::sptr handle( libusb::device_handle::get_cached_handle(_dev) @@ -146,7 +161,7 @@ public: unsigned char buff[512]; ssize_t ret = libusb_get_string_descriptor_ascii( - handle->get(), this->get().iSerialNumber, buff, sizeof(buff) + handle->get(), off, buff, sizeof(buff) ); if (ret < 0) return ""; //on error, just return empty string @@ -240,7 +255,15 @@ public: } std::string get_serial(void) const{ - return libusb::device_descriptor::make(this->get_device())->get_ascii_serial(); + return libusb::device_descriptor::make(this->get_device())->get_ascii_property("serial"); + } + + std::string get_manufacturer() const{ + return libusb::device_descriptor::make(this->get_device())->get_ascii_property("manufacturer"); + } + + std::string get_product() const{ + return libusb::device_descriptor::make(this->get_device())->get_ascii_property("product"); } boost::uint16_t get_vendor_id(void) const{ diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index 04c1d6574..7dab07fda 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -102,7 +102,7 @@ namespace libusb { //! get the underlying device descriptor virtual const libusb_device_descriptor &get(void) const = 0; - virtual std::string get_ascii_serial(void) const = 0; + virtual std::string get_ascii_property(const std::string &what) const = 0; }; /*! diff --git a/host/lib/transport/libusb1_control.cpp b/host/lib/transport/libusb1_control.cpp index 3d9b38785..c1b8fe6df 100644 --- a/host/lib/transport/libusb1_control.cpp +++ b/host/lib/transport/libusb1_control.cpp @@ -21,8 +21,6 @@ using namespace uhd::transport; -const int libusb_timeout = 0; - /*********************************************************************** * libusb-1.0 implementation of USB control transport **********************************************************************/ @@ -39,7 +37,8 @@ public: boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length + boost::uint16_t length, + boost::int32_t libusb_timeout = 0 ){ boost::mutex::scoped_lock lock(_mutex); return libusb_control_transfer(_handle->get(), diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 28bff9709..197e257da 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,11 +18,17 @@ #include "libusb1_base.hpp" #include <uhd/transport/usb_zero_copy.hpp> #include <uhd/transport/buffer_pool.hpp> +#include <uhd/transport/bounded_buffer.hpp> #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> #include <boost/make_shared.hpp> -#include <boost/thread/thread.hpp> +#include <boost/circular_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> #include <list> using namespace uhd; @@ -36,14 +42,51 @@ static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes #define LIBUSB_CALL #endif /*LIBUSB_CALL*/ +//! libusb_handle_events_timeout_completed is only in newer API +#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED + #define libusb_handle_events_timeout_completed(ctx, tx, completed) \ + libusb_handle_events_timeout(ctx, tx) +#endif + +//! libusb_error_name is only in newer API +#ifndef HAVE_LIBUSB_ERROR_NAME + #define libusb_error_name(code) \ + str(boost::format("LIBUSB_ERROR_CODE %d") % code) +#endif + +//! type for sharing the release queue with managed buffers +class libusb_zero_copy_mb; +typedef boost::shared_ptr<bounded_buffer<libusb_zero_copy_mb *> > mb_queue_sptr; + +/*! + * The libusb docs state that status and actual length can only be read in the callback. + * Therefore, this struct is intended to store data seen from the callback function. + */ +struct lut_result_t +{ + lut_result_t(void) + { + completed = 0; + status = LIBUSB_TRANSFER_COMPLETED; + actual_length = 0; + } + int completed; + libusb_transfer_status status; + int actual_length; +}; + /*! * All libusb callback functions should be marked with the LIBUSB_CALL macro * to ensure that they are compiled with the same calling convention as libusb. */ //! helper function: handles all async callbacks -static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ - *(static_cast<bool *>(lut->user_data)) = true; +static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) +{ + lut_result_t *r = (lut_result_t *)lut->user_data; + r->completed = 1; + r->status = lut->status; + r->actual_length = lut->actual_length; } /*! @@ -61,7 +104,8 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut){ * \param completed a reference to the completed flag * \return true for completion, false for timeout */ -UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, bool &completed){ +UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, int &completed) +{ //already completed by a previous call? if (completed) return true; @@ -69,7 +113,7 @@ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, b timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; - libusb_handle_events_timeout(ctx, &tv); + libusb_handle_events_timeout_completed(ctx, &tv, &completed); if (completed) return true; //finish the rest with a timeout loop @@ -78,73 +122,54 @@ UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, b timeval tv; tv.tv_sec = 0; tv.tv_usec = 10000; /*10ms*/ - libusb_handle_events_timeout(ctx, &tv); + libusb_handle_events_timeout_completed(ctx, &tv, &completed); } return completed; } /*********************************************************************** - * Reusable managed receiver buffer: + * Reusable managed buffer: * - Associated with a particular libusb transfer struct. * - Submits the transfer to libusb in the release method. **********************************************************************/ -class libusb_zero_copy_mrb : public managed_recv_buffer{ +class libusb_zero_copy_mb : public managed_buffer +{ public: - libusb_zero_copy_mrb(libusb_transfer *lut, const size_t frame_size): + libusb_zero_copy_mb(libusb_transfer *lut, const size_t frame_size, boost::function<void(libusb_zero_copy_mb *)> release_cb, const bool is_recv, const std::string &name): + _release_cb(release_cb), _is_recv(is_recv), _name(name), _ctx(libusb::session::get_global_session()->get_context()), _lut(lut), _frame_size(frame_size) { /* NOP */ } - void release(void){ - completed = false; - _lut->length = _frame_size; //always reset length - UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); - } - - sptr get_new(const double timeout, size_t &index){ - if (wait_for_completion(_ctx, timeout, completed)){ - index++; - return make(this, _lut->buffer, _lut->actual_length); - } - return managed_recv_buffer::sptr(); - } - - bool completed; - -private: - libusb_context *_ctx; - libusb_transfer *_lut; - const size_t _frame_size; -}; - -/*********************************************************************** - * Reusable managed send buffer: - * - Associated with a particular libusb transfer struct. - * - Submits the transfer to libusb in the commit method. - **********************************************************************/ -class libusb_zero_copy_msb : public managed_send_buffer{ -public: - libusb_zero_copy_msb(libusb_transfer *lut, const size_t frame_size): - _ctx(libusb::session::get_global_session()->get_context()), - _lut(lut), _frame_size(frame_size) { completed = true; } + void release(void){_release_cb(this);} - void release(void){ - completed = false; - _lut->length = size(); - UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); + UHD_INLINE void submit(void) + { + _lut->length = (_is_recv)? _frame_size : size(); //always set length + const int ret = libusb_submit_transfer(_lut); + if (ret != 0) throw uhd::runtime_error(str(boost::format( + "usb %s submit failed: %s") % _name % libusb_error_name(ret))); } - sptr get_new(const double timeout, size_t &index){ - if (wait_for_completion(_ctx, timeout, completed)){ - index++; - return make(this, _lut->buffer, _frame_size); + template <typename buffer_type> + UHD_INLINE typename buffer_type::sptr get_new(const double timeout) + { + if (wait_for_completion(_ctx, timeout, result.completed)) + { + if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format( + "usb %s transfer status: %d") % _name % int(result.status))); + result.completed = 0; + return make(reinterpret_cast<buffer_type *>(this), _lut->buffer, (_is_recv)? result.actual_length : _frame_size); } - return managed_send_buffer::sptr(); + return typename buffer_type::sptr(); } - bool completed; + lut_result_t result; private: + boost::function<void(libusb_zero_copy_mb *)> _release_cb; + const bool _is_recv; + const std::string _name; libusb_context *_ctx; libusb_transfer *_lut; const size_t _frame_size; @@ -153,39 +178,33 @@ private: /*********************************************************************** * USB zero_copy device class **********************************************************************/ -class libusb_zero_copy_impl : public usb_zero_copy{ +class libusb_zero_copy_single +{ public: - - libusb_zero_copy_impl( + libusb_zero_copy_single( libusb::device_handle::sptr handle, - const size_t recv_interface, - const size_t recv_endpoint, - const size_t send_interface, - const size_t send_endpoint, - const device_addr_t &hints + const size_t interface, const size_t endpoint, + const size_t num_frames, const size_t frame_size ): _handle(handle), - _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), - _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), - _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), - _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))), - _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), - _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), - _next_recv_buff_index(0), - _next_send_buff_index(0) + _num_frames(num_frames), + _frame_size(frame_size), + _buffer_pool(buffer_pool::make(_num_frames, _frame_size)), + _enqueued(_num_frames), _released(_num_frames) { - _handle->claim_interface(recv_interface); - _handle->claim_interface(send_interface); + const bool is_recv = (endpoint & 0x80) != 0; + const std::string name = str(boost::format("%s%d") % ((is_recv)? "rx" : "tx") % int(endpoint & 0x7f)); + _handle->claim_interface(interface); //flush the buffers out of the recv endpoint //limit the flushing to at most one second - for (size_t i = 0; i < 100; i++) + if (is_recv) for (size_t i = 0; i < 100; i++) { unsigned char buff[512]; int transfered = 0; const int status = libusb_bulk_transfer( _handle->get(), // dev_handle - (recv_endpoint & 0x7f) | 0x80, // endpoint + endpoint, // endpoint static_cast<unsigned char *>(buff), sizeof(buff), &transfered, //bytes xfered @@ -194,102 +213,170 @@ public: if (status == LIBUSB_ERROR_TIMEOUT) break; } - //allocate libusb transfer structs and managed receive buffers - for (size_t i = 0; i < get_num_recv_frames(); i++){ - + //allocate libusb transfer structs and managed buffers + for (size_t i = 0; i < get_num_frames(); i++) + { libusb_transfer *lut = libusb_alloc_transfer(0); UHD_ASSERT_THROW(lut != NULL); - _mrb_pool.push_back(boost::make_shared<libusb_zero_copy_mrb>(lut, this->get_recv_frame_size())); + _mb_pool.push_back(boost::make_shared<libusb_zero_copy_mb>( + lut, this->get_frame_size(), boost::bind(&libusb_zero_copy_single::enqueue_damn_buffer, this, _1), is_recv, name + )); libusb_fill_bulk_transfer( lut, // transfer _handle->get(), // dev_handle - (recv_endpoint & 0x7f) | 0x80, // endpoint - static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer - this->get_recv_frame_size(), // length + endpoint, // endpoint + static_cast<unsigned char *>(_buffer_pool->at(i)), // buffer + this->get_frame_size(), // length libusb_transfer_cb_fn(&libusb_async_cb), // callback - static_cast<void *>(&_mrb_pool.back()->completed), // user_data + static_cast<void *>(&_mb_pool.back()->result), // user_data 0 // timeout (ms) ); _all_luts.push_back(lut); - _mrb_pool.back()->release(); } - //allocate libusb transfer structs and managed send buffers - for (size_t i = 0; i < get_num_send_frames(); i++){ - - libusb_transfer *lut = libusb_alloc_transfer(0); - UHD_ASSERT_THROW(lut != NULL); - - _msb_pool.push_back(boost::make_shared<libusb_zero_copy_msb>(lut, this->get_send_frame_size())); - - libusb_fill_bulk_transfer( - lut, // transfer - _handle->get(), // dev_handle - (send_endpoint & 0x7f) | 0x00, // endpoint - static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer - this->get_send_frame_size(), // length - libusb_transfer_cb_fn(&libusb_async_cb), // callback - static_cast<void *>(&_msb_pool.back()->completed), // user_data - 0 // timeout - ); - - _all_luts.push_back(lut); + //initial release for all buffers + for (size_t i = 0; i < get_num_frames(); i++) + { + libusb_zero_copy_mb &mb = *(_mb_pool[i]); + if (is_recv) mb.release(); + else + { + mb.result.completed = 1; + _enqueued.push_back(&mb); + } } } - ~libusb_zero_copy_impl(void){ + ~libusb_zero_copy_single(void) + { libusb_context *ctx = libusb::session::get_global_session()->get_context(); //cancel all transfers - BOOST_FOREACH(libusb_transfer *lut, _all_luts){ + BOOST_FOREACH(libusb_transfer *lut, _all_luts) + { libusb_cancel_transfer(lut); } //process all transfers until timeout occurs - bool completed = false; + int completed = 0; wait_for_completion(ctx, 0.01, completed); //free all transfers - BOOST_FOREACH(libusb_transfer *lut, _all_luts){ + BOOST_FOREACH(libusb_transfer *lut, _all_luts) + { libusb_free_transfer(lut); } - } - managed_recv_buffer::sptr get_recv_buff(double timeout){ - if (_next_recv_buff_index == _num_recv_frames) _next_recv_buff_index = 0; - return _mrb_pool[_next_recv_buff_index]->get_new(timeout, _next_recv_buff_index); - } + template <typename buffer_type> + UHD_INLINE typename buffer_type::sptr get_buff(double timeout) + { + typename buffer_type::sptr buff; + libusb_zero_copy_mb *front = NULL; + { + boost::mutex::scoped_lock l(_mutex); + if (_enqueued.empty()) + { + _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); + } + if (_enqueued.empty()) return buff; + front = _enqueued.front(); + } - managed_send_buffer::sptr get_send_buff(double timeout){ - if (_next_send_buff_index == _num_send_frames) _next_send_buff_index = 0; - return _msb_pool[_next_send_buff_index]->get_new(timeout, _next_send_buff_index); - } + buff = front->get_new<buffer_type>(timeout); - size_t get_num_recv_frames(void) const { return _num_recv_frames; } - size_t get_num_send_frames(void) const { return _num_send_frames; } + boost::mutex::scoped_lock l(_mutex); + if (buff) _enqueued.pop_front(); + this->submit_what_we_can(); + return buff; + } - size_t get_recv_frame_size(void) const { return _recv_frame_size; } - size_t get_send_frame_size(void) const { return _send_frame_size; } + UHD_INLINE size_t get_num_frames(void) const { return _num_frames; } + UHD_INLINE size_t get_frame_size(void) const { return _frame_size; } private: libusb::device_handle::sptr _handle; - const size_t _recv_frame_size, _num_recv_frames; - const size_t _send_frame_size, _num_send_frames; + const size_t _num_frames, _frame_size; //! Storage for transfer related objects - buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; - std::vector<boost::shared_ptr<libusb_zero_copy_mrb> > _mrb_pool; - std::vector<boost::shared_ptr<libusb_zero_copy_msb> > _msb_pool; - size_t _next_recv_buff_index, _next_send_buff_index; + buffer_pool::sptr _buffer_pool; + std::vector<boost::shared_ptr<libusb_zero_copy_mb> > _mb_pool; + + boost::mutex _mutex; + boost::condition_variable _cond; + + //! why 2 queues? there is room in the future to have > N buffers but only N in flight + boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released; + + void enqueue_damn_buffer(libusb_zero_copy_mb *mb) + { + boost::mutex::scoped_lock l(_mutex); + _released.push_back(mb); + this->submit_what_we_can(); + l.unlock(); + _cond.notify_one(); + } + + void submit_what_we_can(void) + { + while (not _released.empty() and not _enqueued.full()) + { + _released.front()->submit(); + _enqueued.push_back(_released.front()); + _released.pop_front(); + } + } //! a list of all transfer structs we allocated std::list<libusb_transfer *> _all_luts; +}; + +/*********************************************************************** + * USB zero_copy device class + **********************************************************************/ +struct libusb_zero_copy_impl : usb_zero_copy +{ + libusb_zero_copy_impl( + libusb::device_handle::sptr handle, + const size_t recv_interface, + const size_t recv_endpoint, + const size_t send_interface, + const size_t send_endpoint, + const device_addr_t &hints + ){ + _recv_impl.reset(new libusb_zero_copy_single( + handle, recv_interface, (recv_endpoint & 0x7f) | 0x80, + size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS)), + size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE)))); + _send_impl.reset(new libusb_zero_copy_single( + handle, send_interface, (send_endpoint & 0x7f) | 0x00, + size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS)), + size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE)))); + } + + managed_recv_buffer::sptr get_recv_buff(double timeout) + { + boost::mutex::scoped_lock l(_recv_mutex); + return _recv_impl->get_buff<managed_recv_buffer>(timeout); + } + + managed_send_buffer::sptr get_send_buff(double timeout) + { + boost::mutex::scoped_lock l(_send_mutex); + return _send_impl->get_buff<managed_send_buffer>(timeout); + } + + size_t get_num_recv_frames(void) const { return _recv_impl->get_num_frames(); } + size_t get_num_send_frames(void) const { return _send_impl->get_num_frames(); } + size_t get_recv_frame_size(void) const { return _recv_impl->get_frame_size(); } + size_t get_send_frame_size(void) const { return _send_impl->get_frame_size(); } + boost::shared_ptr<libusb_zero_copy_single> _recv_impl, _send_impl; + boost::mutex _recv_mutex, _send_mutex; }; /*********************************************************************** diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5a75d5f0d..688228e49 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -63,6 +63,8 @@ static inline void handle_overflow_nop(void){} class recv_packet_handler{ public: typedef boost::function<managed_recv_buffer::sptr(double)> get_buff_type; + typedef boost::function<void(const size_t)> handle_flowctrl_type; + typedef boost::function<void(const stream_cmd_t&)> issue_stream_cmd_type; typedef void(*vrt_unpacker_type)(const boost::uint32_t *, vrt::if_packet_info_t &); //typedef boost::function<void(const boost::uint32_t *, vrt::if_packet_info_t &)> vrt_unpacker_type; @@ -139,6 +141,19 @@ public: _props.at(xport_chan).get_buff = get_buff; } + /*! + * Set the function to handle flow control + * \param xport_chan which transport channel + * \param handle_flowctrl the callback function + */ + void set_xport_handle_flowctrl(const size_t xport_chan, const handle_flowctrl_type &handle_flowctrl, const size_t update_window, const bool do_init = false) + { + _props.at(xport_chan).handle_flowctrl = handle_flowctrl; + //we need the window size to be within the 0xfff (max 12 bit seq) + _props.at(xport_chan).fc_update_window = std::min<size_t>(update_window, 0xfff); + if (do_init) handle_flowctrl(0); + } + //! Set the conversion routine for all channels void set_converter(const uhd::convert::id_type &id){ _num_outputs = id.num_outputs; @@ -158,6 +173,21 @@ public: _converter->set_scalar(scale_factor); } + //! Set the callback to issue stream commands + void set_issue_stream_cmd(const size_t xport_chan, const issue_stream_cmd_type &issue_stream_cmd) + { + _props.at(xport_chan).issue_stream_cmd = issue_stream_cmd; + } + + //! Overload call to issue stream commands + void issue_stream_cmd(const stream_cmd_t &stream_cmd) + { + for (size_t i = 0; i < _props.size(); i++) + { + if (_props[i].issue_stream_cmd) _props[i].issue_stream_cmd(stream_cmd); + } + } + /******************************************************************* * Receive: * The entry point for the fast-path receive calls. @@ -219,8 +249,11 @@ private: handle_overflow(&handle_overflow_nop) {} get_buff_type get_buff; + issue_stream_cmd_type issue_stream_cmd; size_t packet_count; handle_overflow_type handle_overflow; + handle_flowctrl_type handle_flowctrl; + size_t fc_update_window; }; std::vector<xport_chan_props_type> _props; size_t _num_outputs; @@ -302,6 +335,15 @@ private: info.time = time_spec_t::from_ticks(info.ifpi.tsf, _tick_rate); //assumes has_tsf is true info.copy_buff = reinterpret_cast<const char *>(info.vrt_hdr + info.ifpi.num_header_words32); + //handle flow control + if (_props[index].handle_flowctrl) + { + if ((info.ifpi.packet_count % _props[index].fc_update_window/2) == 0) + { + _props[index].handle_flowctrl(info.ifpi.packet_count); + } + } + //-------------------------------------------------------------- //-- Determine return conditions: //-- The order of these checks is HOLY. @@ -314,8 +356,9 @@ private: //2) check for sequence errors #ifndef SRPH_DONT_CHECK_SEQUENCE + const size_t seq_mask = (info.ifpi.link_type == vrt::if_packet_info_t::LINK_TYPE_NONE)? 0xf : 0xfff; const size_t expected_packet_count = _props[index].packet_count; - _props[index].packet_count = (info.ifpi.packet_count + 1)%16; + _props[index].packet_count = (info.ifpi.packet_count + 1) & seq_mask; if (expected_packet_count != info.ifpi.packet_count){ return PACKET_SEQUENCE_ERROR; } @@ -459,7 +502,7 @@ private: curr_info.metadata.start_of_burst = false; curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_OVERFLOW; - UHD_MSG(fastpath) << "O"; + UHD_MSG(fastpath) << "D"; return; } @@ -479,6 +522,7 @@ private: curr_info.metadata.start_of_burst = false; curr_info.metadata.end_of_burst = false; curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_ALIGNMENT; + _props[index].handle_overflow(); return; } @@ -622,6 +666,11 @@ public: return recv_packet_handler::recv(buffs, nsamps_per_buff, metadata, timeout, one_packet); } + void issue_stream_cmd(const stream_cmd_t &stream_cmd) + { + return recv_packet_handler::issue_stream_cmd(stream_cmd); + } + private: size_t _max_num_samps; }; diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 726742327..41f030ea6 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -47,6 +47,7 @@ namespace uhd{ namespace transport{ namespace sph{ class send_packet_handler{ public: typedef boost::function<managed_send_buffer::sptr(double)> get_buff_type; + typedef boost::function<bool(uhd::async_metadata_t &, const double)> async_receiver_type; typedef void(*vrt_packer_type)(boost::uint32_t *, vrt::if_packet_info_t &); //typedef boost::function<void(boost::uint32_t *, vrt::if_packet_info_t &)> vrt_packer_type; @@ -57,6 +58,7 @@ public: send_packet_handler(const size_t size = 1): _next_packet_seq(0) { + this->set_enable_trailer(true); this->resize(size); } @@ -96,6 +98,11 @@ public: _props.at(xport_chan).sid = sid; } + void set_enable_trailer(const bool enable) + { + _has_tlr = enable; + } + //! Set the rate of ticks per second void set_tick_rate(const double rate){ _tick_rate = rate; @@ -138,6 +145,21 @@ public: _converter->set_scalar(scale_factor); } + //! Set the callback to get async messages + void set_async_receiver(const async_receiver_type &async_receiver) + { + _async_receiver = async_receiver; + } + + //! Overload call to get async metadata + bool recv_async_msg( + uhd::async_metadata_t &async_metadata, double timeout = 0.1 + ){ + if (_async_receiver) return _async_receiver(async_metadata, timeout); + boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6))); + return false; + } + /******************************************************************* * Send: * The entry point for the fast-path send calls. @@ -154,7 +176,7 @@ public: if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; //if_packet_info.has_sid = false; //set per channel if_packet_info.has_cid = false; - if_packet_info.has_tlr = true; + if_packet_info.has_tlr = _has_tlr; if_packet_info.has_tsi = false; if_packet_info.has_tsf = metadata.has_time_spec; if_packet_info.tsf = metadata.time_spec.to_ticks(_tick_rate); @@ -165,9 +187,12 @@ public: //TODO remove this code when sample counts of zero are supported by hardware #ifndef SSPH_DONT_PAD_TO_ONE - if (nsamps_per_buff == 0) return send_one_packet( - _zero_buffs, 1, if_packet_info, timeout - ) & 0x0; + static const boost::uint64_t zero = 0; + _zero_buffs.resize(buffs.size(), &zero); + + if (nsamps_per_buff == 0) return send_one_packet( + _zero_buffs, 1, if_packet_info, timeout + ) & 0x0; #endif return send_one_packet(buffs, nsamps_per_buff, if_packet_info, timeout); @@ -228,6 +253,8 @@ private: size_t _max_samples_per_packet; std::vector<const void *> _zero_buffs; size_t _next_packet_seq; + bool _has_tlr; + async_receiver_type _async_receiver; /******************************************************************* * Send a single packet: @@ -337,6 +364,12 @@ public: return send_packet_handler::send(buffs, nsamps_per_buff, metadata, timeout); } + bool recv_async_msg( + uhd::async_metadata_t &async_metadata, double timeout = 0.1 + ){ + return send_packet_handler::recv_async_msg(async_metadata, timeout); + } + private: size_t _max_num_samps; }; diff --git a/host/lib/types/serial.cpp b/host/lib/types/serial.cpp index 9e9d32954..9b8336dd8 100644 --- a/host/lib/types/serial.cpp +++ b/host/lib/types/serial.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,6 +21,21 @@ using namespace uhd; +i2c_iface::~i2c_iface(void) +{ + //empty +} + +spi_iface::~spi_iface(void) +{ + //empty +} + +uart_iface::~uart_iface(void) +{ + //empty +} + spi_config_t::spi_config_t(edge_t edge): mosi_edge(edge), miso_edge(edge) @@ -29,8 +44,8 @@ spi_config_t::spi_config_t(edge_t edge): } void i2c_iface::write_eeprom( - boost::uint8_t addr, - boost::uint8_t offset, + boost::uint16_t addr, + boost::uint16_t offset, const byte_vector_t &bytes ){ for (size_t i = 0; i < bytes.size(); i++){ @@ -42,8 +57,8 @@ void i2c_iface::write_eeprom( } byte_vector_t i2c_iface::read_eeprom( - boost::uint8_t addr, - boost::uint8_t offset, + boost::uint16_t addr, + boost::uint16_t offset, size_t num_bytes ){ byte_vector_t bytes; @@ -55,6 +70,59 @@ byte_vector_t i2c_iface::read_eeprom( return bytes; } +struct eeprom16_impl : i2c_iface +{ + eeprom16_impl(i2c_iface* internal) + { + _internal = internal; + } + i2c_iface* _internal; + + byte_vector_t read_i2c( + boost::uint16_t addr, + size_t num_bytes + ){ + return _internal->read_i2c(addr, num_bytes); + } + + void write_i2c( + boost::uint16_t addr, + const byte_vector_t &bytes + ){ + return _internal->write_i2c(addr, bytes); + } + + byte_vector_t read_eeprom( + boost::uint16_t addr, + boost::uint16_t offset, + size_t num_bytes + ){ + byte_vector_t cmd = boost::assign::list_of(offset >> 8)(offset & 0xff); + this->write_i2c(addr, cmd); + return this->read_i2c(addr, num_bytes); + } + + void write_eeprom( + boost::uint16_t addr, + boost::uint16_t offset, + const byte_vector_t &bytes + ){ + for (size_t i = 0; i < bytes.size(); i++) + { + //write a byte at a time, its easy that way + boost::uint16_t offset_i = offset+i; + byte_vector_t cmd = boost::assign::list_of(offset_i >> 8)(offset_i & 0xff)(bytes[i]); + this->write_i2c(addr, cmd); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //worst case write + } + } +}; + +i2c_iface::sptr i2c_iface::eeprom16(void) +{ + return i2c_iface::sptr(new eeprom16_impl(this)); +} + boost::uint32_t spi_iface::read_spi( int which_slave, const spi_config_t &config, diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 8ae379f73..f8c817df5 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -37,3 +37,4 @@ INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) INCLUDE_SUBDIRECTORY(e100) +INCLUDE_SUBDIRECTORY(b200) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index d2c33b512..bcc5ac74d 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -31,5 +31,6 @@ IF(ENABLE_B100) ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp ) ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index c4d050242..19df3d6af 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012 Ettus Research LLC +// Copyright 2012-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -188,12 +188,27 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ ctrl_xport_args["send_frame_size"] = "512"; ctrl_xport_args["num_send_frames"] = "16"; - _ctrl_transport = usb_zero_copy::make( - handle, - 4, 8, //interface, endpoint - 3, 4, //interface, endpoint - ctrl_xport_args - ); + //try to open ctrl transport + //this could fail with libusb_submit_transfer under some conditions + try{ + _ctrl_transport = usb_zero_copy::make( + handle, + 4, 8, //interface, endpoint + 3, 4, //interface, endpoint + ctrl_xport_args + ); + } + //try reset once in the case of failure + catch(const uhd::exception &ex){ + if (initialization_count > 1) throw; + UHD_MSG(warning) << + "The control endpoint was left in a bad state.\n" + "Attempting endpoint re-enumeration...\n" << ex.what() << std::endl; + _fifo_ctrl.reset(); + _ctrl_transport.reset(); + _fx2_ctrl->usrp_fx2_reset(); + goto b100_impl_constructor_begin; + } this->enable_gpif(true); //////////////////////////////////////////////////////////////////// @@ -245,7 +260,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ const size_t rx_lut_size = size_t(data_xport_args.cast<double>("recv_frame_size", 0.0)); _fifo_ctrl->poke32(TOREG(SR_PADDER+0), rx_lut_size/sizeof(boost::uint32_t)); - _data_transport = usb_zero_copy::make_wrapper( + _data_transport = usb_zero_copy_make_wrapper( usb_zero_copy::make( handle, // identifier 2, 6, // IN interface, endpoint @@ -472,7 +487,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ } //initialize io handling - _recv_demuxer = recv_packet_demuxer::make(_data_transport, _rx_dsps.size(), B100_RX_SID_BASE); + _recv_demuxer.reset(new recv_packet_demuxer_3000(_data_transport)); //allocate streamer weak ptrs containers _rx_streamers.resize(_rx_dsps.size()); @@ -516,6 +531,7 @@ void b100_impl::check_fw_compat(void){ "%s" ) % B100_FW_COMPAT_NUM % fw_compat_num % print_images_error())); } + _tree->create<std::string>("/mboards/0/fw_version").set(str(boost::format("%u.0") % fw_compat_num)); } void b100_impl::check_fpga_compat(void){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 68d7043a1..f81aa3568 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ #include "time64_core_200.hpp" #include "fifo_ctrl_excelsior.hpp" #include "user_settings_core_200.hpp" -#include "recv_packet_demuxer.hpp" +#include "recv_packet_demuxer_3000.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> #include <uhd/types/dict.hpp> @@ -74,6 +74,24 @@ uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( b100_codec_ctrl::sptr codec ); +/*! + * Make a wrapper around a zero copy implementation. + * The wrapper performs the following functions: + * - Pad commits to the frame boundary + * - Extract multiple packets on recv + * + * When enable multiple receive packets is set to true, + * the implementation inspects the vita length on transfers, + * and may split a single transfer into multiple managed buffers. + * + * \param usb_zc a usb zero copy interface object + * \param usb_frame_boundary bytes per frame + * \return a new zero copy wrapper object + */ +uhd::transport::zero_copy_if::sptr usb_zero_copy_make_wrapper( + uhd::transport::zero_copy_if::sptr usb_zc, size_t usb_frame_boundary = 512 +); + //! Implementation guts class b100_impl : public uhd::device { public: @@ -105,7 +123,7 @@ private: //transports uhd::transport::zero_copy_if::sptr _ctrl_transport; uhd::transport::zero_copy_if::sptr _data_transport; - uhd::usrp::recv_packet_demuxer::sptr _recv_demuxer; + boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _recv_demuxer; //dboard stuff uhd::usrp::dboard_manager::sptr _dboard_manager; diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 25604da72..efbba1c4c 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -73,8 +73,8 @@ public: void set_gpio_debug(unit_t, int); boost::uint16_t read_gpio(unit_t); - void write_i2c(boost::uint8_t, const byte_vector_t &); - byte_vector_t read_i2c(boost::uint8_t, size_t); + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); void write_spi( unit_t unit, @@ -219,11 +219,11 @@ boost::uint32_t b100_dboard_iface::read_write_spi( /*********************************************************************** * I2C **********************************************************************/ -void b100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ +void b100_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){ return _i2c_iface->write_i2c(addr, bytes); } -byte_vector_t b100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ +byte_vector_t b100_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){ return _i2c_iface->read_i2c(addr, num_bytes); } diff --git a/host/lib/usrp/b100/io_impl.cpp b/host/lib/usrp/b100/io_impl.cpp index 723756dcc..86edb4ed6 100644 --- a/host/lib/usrp/b100/io_impl.cpp +++ b/host/lib/usrp/b100/io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -158,12 +158,15 @@ rx_streamer::sptr b100_impl::get_rx_stream(const uhd::stream_args_t &args_){ const size_t dsp = args.channels[chan_i]; _rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _rx_dsps[dsp]->setup(args); + _recv_demuxer->realloc_sid(B100_RX_SID_BASE + dsp); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( - &recv_packet_demuxer::get_recv_buff, _recv_demuxer, dsp, _1 + &recv_packet_demuxer_3000::get_recv_buff, _recv_demuxer, B100_RX_SID_BASE + dsp, _1 ), true /*flush*/); my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] )); + my_streamer->set_issue_stream_cmd(chan_i, boost::bind( + &rx_dsp_core_200::issue_stream_command, _rx_dsps[dsp], _1)); _rx_streamers[dsp] = my_streamer; //store weak pointer } @@ -217,6 +220,7 @@ tx_streamer::sptr b100_impl::get_tx_stream(const uhd::stream_args_t &args_){ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_send_buff, _data_transport, _1 )); + my_streamer->set_async_receiver(boost::bind(&fifo_ctrl_excelsior::pop_async_msg, _fifo_ctrl, _1, _2)); _tx_streamers[dsp] = my_streamer; //store weak pointer } diff --git a/host/lib/transport/usb_zero_copy_wrapper.cpp b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp index d04244ca9..2096e4ef4 100644 --- a/host/lib/transport/usb_zero_copy_wrapper.cpp +++ b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp @@ -81,7 +81,7 @@ private: **********************************************************************/ class usb_zero_copy_wrapper_msb : public managed_send_buffer{ public: - usb_zero_copy_wrapper_msb(const usb_zero_copy::sptr internal, const size_t fragmentation_size): + usb_zero_copy_wrapper_msb(const zero_copy_if::sptr internal, const size_t fragmentation_size): _internal(internal), _fragmentation_size(fragmentation_size) { _ok_to_auto_flush = false; @@ -131,7 +131,7 @@ public: } private: - usb_zero_copy::sptr _internal; + zero_copy_if::sptr _internal; const size_t _fragmentation_size; managed_send_buffer::sptr _last_send_buff; size_t _bytes_in_buffer; @@ -164,7 +164,7 @@ private: **********************************************************************/ class usb_zero_copy_wrapper : public usb_zero_copy{ public: - usb_zero_copy_wrapper(sptr usb_zc, const size_t frame_boundary): + usb_zero_copy_wrapper(zero_copy_if::sptr usb_zc, const size_t frame_boundary): _internal_zc(usb_zc), _frame_boundary(frame_boundary), _next_recv_buff_index(0) @@ -176,6 +176,14 @@ public: } managed_recv_buffer::sptr get_recv_buff(double timeout){ + //lazy flush mechanism - negative timeout + if (timeout < 0.0) + { + _last_recv_buff.reset(); + while (_internal_zc->get_recv_buff()){} + return managed_recv_buffer::sptr(); + } + //attempt to get a managed recv buffer if (not _last_recv_buff){ _last_recv_buff = _internal_zc->get_recv_buff(timeout); @@ -210,7 +218,7 @@ public: } private: - sptr _internal_zc; + zero_copy_if::sptr _internal_zc; size_t _frame_boundary; std::vector<boost::shared_ptr<usb_zero_copy_wrapper_mrb> > _mrb_pool; boost::shared_ptr<usb_zero_copy_wrapper_msb> _the_only_msb; @@ -224,8 +232,8 @@ private: /*********************************************************************** * USB zero copy wrapper factory function **********************************************************************/ -usb_zero_copy::sptr usb_zero_copy::make_wrapper( - sptr usb_zc, size_t usb_frame_boundary +zero_copy_if::sptr usb_zero_copy_make_wrapper( + zero_copy_if::sptr usb_zc, size_t usb_frame_boundary ){ - return sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary)); + return zero_copy_if::sptr(new usb_zero_copy_wrapper(usb_zc, usb_frame_boundary)); } diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt new file mode 100644 index 000000000..3d8aad052 --- /dev/null +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2012-2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the B100 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) + +IF(ENABLE_B200) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b200_uart.cpp + ) +ENDIF(ENABLE_B200) diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp new file mode 100644 index 000000000..f4a520374 --- /dev/null +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -0,0 +1,561 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "b200_iface.hpp" + +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/functional/hash.hpp> +#include <boost/thread/thread.hpp> +#include <boost/cstdint.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <fstream> +#include <string> +#include <vector> +#include <cstring> +#include <iomanip> +#include <libusb.h> + +using namespace uhd; +using namespace uhd::transport; + +static const bool load_img_msg = true; + +const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0; +const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_ENDPOINT_OUT); +const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_ENDPOINT_IN); +const static boost::uint8_t B200_VREQ_FPGA_START = 0x02; +const static boost::uint8_t B200_VREQ_FPGA_DATA = 0x12; +const static boost::uint8_t B200_VREQ_GET_COMPAT = 0x15; +const static boost::uint8_t B200_VREQ_SET_FPGA_HASH = 0x1C; +const static boost::uint8_t B200_VREQ_GET_FPGA_HASH = 0x1D; +const static boost::uint8_t B200_VREQ_SET_FW_HASH = 0x1E; +const static boost::uint8_t B200_VREQ_GET_FW_HASH = 0x1F; +const static boost::uint8_t B200_VREQ_LOOP = 0x22; +const static boost::uint8_t B200_VREQ_SPI_WRITE = 0x32; +const static boost::uint8_t B200_VREQ_SPI_READ = 0x42; +const static boost::uint8_t B200_VREQ_FPGA_RESET = 0x62; +const static boost::uint8_t B200_VREQ_GPIF_RESET = 0x72; +const static boost::uint8_t B200_VREQ_GET_USB = 0x80; +const static boost::uint8_t B200_VREQ_GET_STATUS = 0x83; +const static boost::uint8_t B200_VREQ_AD9361_CTRL_WRITE = 0x90; +const static boost::uint8_t B200_VREQ_AD9361_CTRL_READ = 0x91; +const static boost::uint8_t B200_VREQ_FX3_RESET = 0x99; +const static boost::uint8_t B200_VREQ_EEPROM_WRITE = 0xBA; +const static boost::uint8_t B200_VREQ_EEPROM_READ = 0xBB; + +const static boost::uint8_t FX3_STATE_FPGA_READY = 0x00; +const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x01; +const static boost::uint8_t FX3_STATE_BUSY = 0x02; +const static boost::uint8_t FX3_STATE_RUNNING = 0x03; + +typedef boost::uint32_t hash_type; + + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * Create a file hash + * The hash will be used to identify the loaded firmware and fpga image + * \param filename file used to generate hash value + * \return hash value in a size_t type + */ +static hash_type generate_hash(const char *filename) +{ + std::ifstream file(filename); + if (not file){ + throw uhd::io_error(std::string("cannot open input file ") + filename); + } + + size_t hash = 0; + + char ch; + long long count = 0; + while (file.get(ch)) { + count++; + boost::hash_combine(hash, ch); + } + + if (count == 0){ + throw uhd::io_error(std::string("empty input file ") + filename); + } + + if (not file.eof()){ + throw uhd::io_error(std::string("file error ") + filename); + } + + file.close(); + return hash_type(hash); +} + + +/*! + * Verify checksum of a Intel HEX record + * \param record a line from an Intel HEX file + * \return true if record is valid, false otherwise + */ +bool checksum(std::string *record) { + + size_t len = record->length(); + unsigned int i; + unsigned char sum = 0; + unsigned int val; + + for (i = 1; i < len; i += 2) { + std::istringstream(record->substr(i, 2)) >> std::hex >> val; + sum += val; + } + + if (sum == 0) + return true; + else + return false; +} + + +/*! + * Parse Intel HEX record + * + * \param record a line from an Intel HEX file + * \param len output length of record + * \param addr output address + * \param type output type + * \param data output data + * \return true if record is sucessfully read, false on error + */ +bool parse_record(std::string *record, boost::uint16_t &len, \ + boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) { + + unsigned int i; + std::string _data; + unsigned int val; + + if (record->substr(0, 1) != ":") + return false; + + std::istringstream(record->substr(1, 2)) >> std::hex >> len; + std::istringstream(record->substr(3, 4)) >> std::hex >> addr; + std::istringstream(record->substr(7, 2)) >> std::hex >> type; + + for (i = 0; i < len; i++) { + std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; + data[i] = (unsigned char) val; + } + + return true; +} + + +/*********************************************************************** + * The implementation class + **********************************************************************/ +class b200_iface_impl : public b200_iface{ +public: + + b200_iface_impl(usb_control::sptr usb_ctrl): + _usb_ctrl(usb_ctrl) + { + //NOP + } + + + int fx3_control_write(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length, + boost::int32_t timeout = 0) + { + return _usb_ctrl->submit(VRT_VENDOR_OUT, // bmReqeustType + request, // bRequest + value, // wValue + index, // wIndex + buff, // data + length, // wLength + timeout); // timeout + } + + + int fx3_control_read(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length, + boost::int32_t timeout = 0) + { + return _usb_ctrl->submit(VRT_VENDOR_IN, // bmReqeustType + request, // bRequest + value, // wValue + index, // wIndex + buff, // data + length, // wLength + timeout); // timeout + } + + + void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) + { + throw uhd::not_implemented_error("b200 write i2c"); + } + + + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes) + { + throw uhd::not_implemented_error("b200 read i2c"); + } + + void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, + const byte_vector_t &bytes) + { + fx3_control_write(B200_VREQ_EEPROM_WRITE, + 0, offset | (boost::uint16_t(addr) << 8), + (unsigned char *) &bytes[0], + bytes.size()); + } + + byte_vector_t read_eeprom( + boost::uint16_t addr, + boost::uint16_t offset, + size_t num_bytes + ){ + byte_vector_t recv_bytes(num_bytes); + fx3_control_read(B200_VREQ_EEPROM_READ, + 0, offset | (boost::uint16_t(addr) << 8), + (unsigned char*) &recv_bytes[0], + num_bytes); + return recv_bytes; + } + + void transact_spi( + unsigned char *tx_data, + size_t num_tx_bits, + unsigned char *rx_data, + size_t num_rx_bits + ){ + int ret = 0; + boost::uint16_t tx_length = num_tx_bits / 8; + + if(tx_data[0] & 0x80) { + ret = fx3_control_write(B200_VREQ_SPI_WRITE, 0x00, \ + 0x00, tx_data, tx_length); + } else { + ret = fx3_control_write(B200_VREQ_SPI_READ, 0x00, \ + 0x00, tx_data, tx_length); + } + + if(ret < 0) { + throw uhd::io_error("transact_spi: fx3_control_write failed!"); + } + + + if(num_rx_bits) { + boost::uint16_t total_length = num_rx_bits / 8; + + ret = fx3_control_read(B200_VREQ_LOOP, 0x00, \ + 0x00, rx_data, total_length); + + if(ret < 0) { + throw uhd::io_error("transact_spi: readback failed!"); + } + } + } + + void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) { + fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, 64); + int ret = 0; + for (size_t i = 0; i < 30; i++) + { + ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, 64, 1000); + if (ret == 64) return; + } + throw uhd::io_error(str(boost::format("ad9361_transact failed with usb error: %d") % ret)); + } + + + void load_firmware(const std::string filestring, bool force = false) + { + const char *filename = filestring.c_str(); + + /* Fields used in each USB control transfer. */ + boost::uint16_t len = 0; + boost::uint16_t type = 0; + boost::uint16_t lower_address_bits = 0x0000; + unsigned char data[512]; + + /* Can be set by the Intel HEX record 0x04, used for all 0x00 records + * thereafter. Note this field takes the place of the 'index' parameter in + * libusb calls, and is necessary for FX3's 32-bit addressing. */ + boost::uint16_t upper_address_bits = 0x0000; + + std::ifstream file; + file.open(filename, std::ifstream::in); + + if(!file.good()) { + throw uhd::io_error("fx3_load_firmware: cannot open firmware input file"); + } + + if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " \ + << filestring << "..." << std::flush; + + while (!file.eof()) { + boost::int32_t ret = 0; + std::string record; + file >> record; + + /* Check for valid Intel HEX record. */ + if (!checksum(&record) || !parse_record(&record, len, \ + lower_address_bits, type, data)) { + throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum"); + } + + /* Type 0x00: Data. */ + if (type == 0x00) { + ret = fx3_control_write(FX3_FIRMWARE_LOAD, \ + lower_address_bits, upper_address_bits, data, len); + + if (ret < 0) { + throw uhd::io_error("usrp_load_firmware: usrp_control_write failed"); + } + } + + /* Type 0x01: EOF. */ + else if (type == 0x01) { + if (lower_address_bits != 0x0000 || len != 0 ) { + throw uhd::io_error("fx3_load_firmware: For EOF record, address must be 0, length must be 0."); + } + + //TODO + //usrp_set_firmware_hash(hash); //set hash before reset + + /* Successful termination! */ + file.close(); + + /* Let the system settle. */ + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + return; + } + + /* Type 0x04: Extended Linear Address Record. */ + else if (type == 0x04) { + if (lower_address_bits != 0x0000 || len != 2 ) { + throw uhd::io_error("fx3_load_firmware: For ELA record, address must be 0, length must be 2."); + } + + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + } + + /* Type 0x05: Start Linear Address Record. */ + else if (type == 0x05) { + if (lower_address_bits != 0x0000 || len != 4 ) { + throw uhd::io_error("fx3_load_firmware: For SLA record, address must be 0, length must be 4."); + } + + /* The firmware load is complete. We now need to tell the CPU + * to jump to an execution address start point, now contained within + * the data field. Parse these address bits out, and then push the + * instruction. */ + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[3] & 0x00FF)); + + fx3_control_write(FX3_FIRMWARE_LOAD, lower_address_bits, \ + upper_address_bits, 0, 0); + + if (load_img_msg) UHD_MSG(status) << " done" << std::endl; + } + + /* If we receive an unknown record type, error out. */ + else { + throw uhd::io_error("fx3_load_firmware: unsupported record type."); + } + } + + /* There was no valid EOF. */ + throw uhd::io_error("fx3_load_firmware: No EOF record found."); + } + + + void reset_fx3(void) { + unsigned char data[4]; + memset(data, 0x00, sizeof(data)); + + fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, 4); + } + + void reset_gpif(void) { + unsigned char data[4]; + memset(data, 0x00, sizeof(data)); + + fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, 4); + } + + void set_fpga_reset_pin(const bool reset) + { + unsigned char data[4]; + memset(data, (reset)? 0xFF : 0x00, sizeof(data)); + + UHD_THROW_INVALID_CODE_PATH(); + + fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, 4); + } + + boost::uint8_t get_usb_speed(void) { + + unsigned char rx_data[1]; + + fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, 1); + + return boost::lexical_cast<boost::uint8_t>(rx_data[0]); + } + + + boost::uint8_t get_fx3_status(void) { + + unsigned char rx_data[1]; + + fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, rx_data, 1); + + return boost::lexical_cast<boost::uint8_t>(rx_data[0]); + } + + boost::uint16_t get_compat_num(void) { + + unsigned char rx_data[2]; + + fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, 2); + + boost::uint16_t compat = 0x0000; + compat |= (((uint16_t) rx_data[0]) << 8); + compat |= (rx_data[1] & 0x00FF); + + return compat; + } + + void usrp_get_firmware_hash(hash_type &hash) { + fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4, 500); + } + + void usrp_set_firmware_hash(hash_type hash) { + fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4); + } + + void usrp_get_fpga_hash(hash_type &hash) { + fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4, 500); + } + + void usrp_set_fpga_hash(hash_type hash) { + fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4); + } + + void load_fpga(const std::string filestring) { + + boost::uint8_t fx3_state = 0; + + const char *filename = filestring.c_str(); + + hash_type hash = generate_hash(filename); + hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); + if (hash == loaded_hash) return; + + size_t file_size = 0; + { + std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate); + file_size = file.tellg(); + } + + std::ifstream file; + file.open(filename, std::ios::in | std::ios::binary); + + if(!file.good()) { + throw uhd::io_error("load_fpga: cannot open FPGA input file."); + } + + do { + fx3_state = get_fx3_status(); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_FPGA_READY); + + if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " \ + << filestring << "..." << std::flush; + + unsigned char out_buff[64]; + memset(out_buff, 0x00, sizeof(out_buff)); + fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000); + + do { + fx3_state = get_fx3_status(); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_CONFIGURING_FPGA); + + + size_t bytes_sent = 0; + while(!file.eof()) { + file.read((char *) out_buff, sizeof(out_buff)); + const std::streamsize n = file.gcount(); + if(n == 0) continue; + + boost::uint16_t transfer_count = boost::uint16_t(n); + + /* Send the data to the device. */ + fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); + + if (load_img_msg) + { + if (bytes_sent == 0) UHD_MSG(status) << " 0%" << std::flush; + const size_t percent_before = size_t((bytes_sent*100)/file_size); + bytes_sent += transfer_count; + const size_t percent_after = size_t((bytes_sent*100)/file_size); + if (percent_before/10 != percent_after/10) + { + UHD_MSG(status) << "\b\b\b\b" << std::setw(3) << percent_after << "%" << std::flush; + } + } + } + + file.close(); + + do { + fx3_state = get_fx3_status(); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_RUNNING); + + usrp_set_fpga_hash(hash); + + if (load_img_msg) UHD_MSG(status) << "\b\b\b\b done" << std::endl; + } + +private: + usb_control::sptr _usb_ctrl; +}; + +/*********************************************************************** + * Make an instance of the implementation + **********************************************************************/ +b200_iface::sptr b200_iface::make(usb_control::sptr usb_ctrl) +{ + return sptr(new b200_iface_impl(usb_ctrl)); +} diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp new file mode 100644 index 000000000..6eb20a459 --- /dev/null +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B200_IFACE_HPP +#define INCLUDED_B200_IFACE_HPP + +#include <stdint.h> +#include <uhd/transport/usb_control.hpp> +#include <uhd/types/serial.hpp> //i2c iface +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "ad9361_ctrl.hpp" + +class b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, + public ad9361_ctrl_iface_type { +public: + typedef boost::shared_ptr<b200_iface> sptr; + + /*! + * Make a b200 interface object from a control transport + * \param usb_ctrl a USB control transport + * \return a new b200 interface object + */ + static sptr make(uhd::transport::usb_control::sptr usb_ctrl); + + //! query the device USB speed (2, 3) + virtual boost::uint8_t get_usb_speed(void) = 0; + + //! get the current status of the FX3 + virtual boost::uint8_t get_fx3_status(void) = 0; + + //! get the current status of the FX3 + virtual boost::uint16_t get_compat_num(void) = 0; + + //! load a firmware image + virtual void load_firmware(const std::string filestring, bool force=false) = 0; + + //! reset the FX3 + virtual void reset_fx3(void) = 0; + + //! reset the GPIF state machine + virtual void reset_gpif(void) = 0; + + //! set the FPGA_RESET line + virtual void set_fpga_reset_pin(const bool reset) = 0; + + //! load an FPGA image + virtual void load_fpga(const std::string filestring) = 0; + + //! send SPI through the FX3 + virtual void transact_spi( unsigned char *tx_data, size_t num_tx_bits, \ + unsigned char *rx_data, size_t num_rx_bits) = 0; +}; + + +#endif /* INCLUDED_B200_IFACE_HPP */ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp new file mode 100644 index 000000000..f7ed35e50 --- /dev/null +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -0,0 +1,887 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "b200_impl.hpp" +#include "b200_regs.hpp" +#include <uhd/transport/usb_control.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/images.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <boost/format.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/filesystem.hpp> +#include <boost/thread/thread.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/functional/hash.hpp> +#include <cstdio> +#include <ctime> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +const boost::uint16_t B200_VENDOR_ID = 0x2500; +const boost::uint16_t B200_PRODUCT_ID = 0x0020; +const boost::uint16_t INIT_PRODUCT_ID = 0x00f0; + +static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); + +//! mapping of frontend to radio perif index +static const size_t FE1 = 1; +static const size_t FE2 = 0; + +/*********************************************************************** + * Discovery + **********************************************************************/ +static device_addrs_t b200_find(const device_addr_t &hint) +{ + device_addrs_t b200_addrs; + + //return an empty list of addresses when type is set to non-b200 + if (hint.has_key("type") and hint["type"] != "b200") return b200_addrs; + + //Return an empty list of addresses when an address is specified, + //since an address is intended for a different, non-USB, device. + if (hint.has_key("addr")) return b200_addrs; + + unsigned int vid, pid; + + if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { + sscanf(hint.get("vid").c_str(), "%x", &vid); + sscanf(hint.get("pid").c_str(), "%x", &pid); + } else { + vid = B200_VENDOR_ID; + pid = B200_PRODUCT_ID; + } + + // Important note: + // The get device list calls are nested inside the for loop. + // This allows the usb guts to decontruct when not in use, + // so that re-enumeration after fw load can occur successfully. + // This requirement is a courtesy of libusb1.0 on windows. + + //find the usrps and load firmware + size_t found = 0; + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + //extract the firmware path for the b200 + std::string b200_fw_image; + try{ + b200_fw_image = find_image_path(hint.get("fw", B200_FW_FILE_NAME)); + } + catch(...){ + UHD_MSG(warning) << boost::format( + "Could not locate B200 firmware.\n" + "Please install the images package.\n" + ); + return b200_addrs; + } + UHD_LOG << "the firmware image: " << b200_fw_image << std::endl; + + usb_control::sptr control; + try{control = usb_control::make(handle, 0);} + catch(const uhd::exception &){continue;} //ignore claimed + + //check if fw was already loaded + if (handle->get_manufacturer() != "Ettus Research LLC") + { + b200_iface::make(control)->load_firmware(b200_fw_image); + } + + found++; + } + + const boost::system_time timeout_time = boost::get_system_time() + REENUMERATION_TIMEOUT_MS; + + //search for the device until found or timeout + while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0) + { + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) + { + usb_control::sptr control; + try{control = usb_control::make(handle, 0);} + catch(const uhd::exception &){continue;} //ignore claimed + + b200_iface::sptr iface = b200_iface::make(control); + const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*iface, "B200"); + + device_addr_t new_addr; + new_addr["type"] = "b200"; + new_addr["name"] = mb_eeprom["name"]; + new_addr["serial"] = handle->get_serial(); + //this is a found b200 when the hint serial and name match or blank + if ( + (not hint.has_key("name") or hint["name"] == new_addr["name"]) and + (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) + ){ + b200_addrs.push_back(new_addr); + } + } + } + + return b200_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr b200_make(const device_addr_t &device_addr) +{ + return device::sptr(new b200_impl(device_addr)); +} + +UHD_STATIC_BLOCK(register_b200_device) +{ + device::register_device(&b200_find, &b200_make); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +b200_impl::b200_impl(const device_addr_t &device_addr) +{ + _tree = property_tree::make(); + const fs_path mb_path = "/mboards/0"; + + //try to match the given device address with something on the USB bus + std::vector<usb_device_handle::sptr> device_list = + usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); + + //locate the matching handle in the device list + usb_device_handle::sptr handle; + BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { + if (dev_handle->get_serial() == device_addr["serial"]){ + handle = dev_handle; + break; + } + } + UHD_ASSERT_THROW(handle.get() != NULL); //better be found + + //create control objects + usb_control::sptr control = usb_control::make(handle, 0); + _iface = b200_iface::make(control); + this->check_fw_compat(); //check after making + + //////////////////////////////////////////////////////////////////// + // setup the mboard eeprom + //////////////////////////////////////////////////////////////////// + const mboard_eeprom_t mb_eeprom(*_iface, "B200"); + _tree->create<mboard_eeprom_t>(mb_path / "eeprom") + .set(mb_eeprom) + .subscribe(boost::bind(&b200_impl::set_mb_eeprom, this, _1)); + + //////////////////////////////////////////////////////////////////// + // Load the FPGA image, then reset GPIF + //////////////////////////////////////////////////////////////////// + std::string default_file_name; + std::string product_name = "B200?"; + if (not mb_eeprom["product"].empty()) + { + switch (boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"])) + { + case 0x0001: + case 0x7737: + product_name = "B200"; + default_file_name = B200_FPGA_FILE_NAME; + break; + case 0x7738: + case 0x0002: + product_name = "B210"; + default_file_name = B210_FPGA_FILE_NAME; + break; + default: throw uhd::runtime_error("b200 unknown product code: " + mb_eeprom["product"]); + } + } + if (default_file_name.empty()) + { + UHD_ASSERT_THROW(device_addr.has_key("fpga")); + } + + //extract the FPGA path for the B200 + std::string b200_fpga_image = find_image_path( + device_addr.has_key("fpga")? device_addr["fpga"] : default_file_name + ); + + _iface->load_fpga(b200_fpga_image); + _iface->reset_gpif(); + + //////////////////////////////////////////////////////////////////// + // Create control transport + //////////////////////////////////////////////////////////////////// + boost::uint8_t usb_speed = _iface->get_usb_speed(); + UHD_MSG(status) << "Operating over USB " << (int) usb_speed << "." << std::endl; + const std::string min_frame_size = (usb_speed == 3) ? "1024" : "512"; + + device_addr_t ctrl_xport_args; + ctrl_xport_args["recv_frame_size"] = min_frame_size; + ctrl_xport_args["num_recv_frames"] = "16"; + ctrl_xport_args["send_frame_size"] = min_frame_size; + ctrl_xport_args["num_send_frames"] = "16"; + + _ctrl_transport = usb_zero_copy::make( + handle, + 4, 8, //interface, endpoint + 3, 4, //interface, endpoint + ctrl_xport_args + ); + while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport + + //////////////////////////////////////////////////////////////////// + // Async task structure + //////////////////////////////////////////////////////////////////// + _async_task_data.reset(new AsyncTaskData()); + _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/)); + _async_task = uhd::task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); + + //////////////////////////////////////////////////////////////////// + // Local control endpoint + //////////////////////////////////////////////////////////////////// + _local_ctrl = radio_ctrl_core_3000::make(vrt::if_packet_info_t::LINK_TYPE_CHDR, _ctrl_transport, zero_copy_if::sptr()/*null*/, B200_LOCAL_CTRL_SID); + _local_ctrl->hold_task(_async_task); + _async_task_data->local_ctrl = _local_ctrl; //weak + this->check_fpga_compat(); + + /* Initialize the GPIOs, set the default bandsels to the lower range. Note + * that calling update_bandsel calls update_gpio_state(). */ + _gpio_state = gpio_state(); + update_bandsel("RX", 800e6); + update_bandsel("TX", 850e6); + + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); + _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); + _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation + + if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try + { + _gps = gps_ctrl::make(_async_task_data->gpsdo_uart); + } + catch(std::exception &e) + { + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_gps and _gps->gps_detected()) + { + //UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + } + } + else + { + UHD_MSG(status) << "not found" << std::endl; + _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE); + } + } + + //////////////////////////////////////////////////////////////////// + // Initialize the properties tree + //////////////////////////////////////////////////////////////////// + _tree->create<std::string>("/name").set("B-Series Device"); + _tree->create<std::string>(mb_path / "name").set(product_name); + _tree->create<std::string>(mb_path / "codename").set("Sasquatch"); + + //////////////////////////////////////////////////////////////////// + // Create data transport + // This happens after FPGA ctrl instantiated so any junk that might + // be in the FPGAs buffers doesn't get pulled into the transport + // before being cleared. + //////////////////////////////////////////////////////////////////// + device_addr_t data_xport_args; + data_xport_args["recv_frame_size"] = device_addr.get("recv_frame_size", "8192"); + data_xport_args["num_recv_frames"] = device_addr.get("num_recv_frames", "16"); + data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192"); + data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + + _data_transport = usb_zero_copy::make( + handle, // identifier + 2, 6, // IN interface, endpoint + 1, 2, // OUT interface, endpoint + data_xport_args // param hints + ); + while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport + _demux.reset(new recv_packet_demuxer_3000(_data_transport)); + + //////////////////////////////////////////////////////////////////// + // Init codec - turns on clocks + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Initialize CODEC control..." << std::endl; + _codec_ctrl = ad9361_ctrl::make(_iface); + this->reset_codec_dcm(); + + //////////////////////////////////////////////////////////////////// + // create codec control objects + //////////////////////////////////////////////////////////////////// + { + const fs_path codec_path = mb_path / ("rx_codecs") / "A"; + _tree->create<std::string>(codec_path / "name").set(product_name+" RX dual ADC"); + _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend + } + { + const fs_path codec_path = mb_path / ("tx_codecs") / "A"; + _tree->create<std::string>(codec_path / "name").set(product_name+" TX dual DAC"); + _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend + } + + //////////////////////////////////////////////////////////////////// + // create clock control objects + //////////////////////////////////////////////////////////////////// + _tree->create<double>(mb_path / "tick_rate") + .coerce(boost::bind(&b200_impl::set_tick_rate, this, _1)) + .publish(boost::bind(&b200_impl::get_tick_rate, this)) + .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1)); + _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + + //////////////////////////////////////////////////////////////////// + // and do the misc mboard sensors + //////////////////////////////////////////////////////////////////// + _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") + .publish(boost::bind(&b200_impl::get_ref_locked, this)); + + //////////////////////////////////////////////////////////////////// + // create frontend mapping + //////////////////////////////////////////////////////////////////// + _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") + .set(subdev_spec_t()) + .subscribe(boost::bind(&b200_impl::update_rx_subdev_spec, this, _1)); + _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") + .set(subdev_spec_t()) + .subscribe(boost::bind(&b200_impl::update_tx_subdev_spec, this, _1)); + + //////////////////////////////////////////////////////////////////// + // setup radio control + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Initialize Radio control..." << std::endl; + const size_t num_radio_chains = ((_local_ctrl->peek32(RB32_CORE_STATUS) >> 8) & 0xff); + UHD_ASSERT_THROW(num_radio_chains > 0); + UHD_ASSERT_THROW(num_radio_chains <= 2); + _radio_perifs.resize(num_radio_chains); + for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i); + + //now test each radio module's connection to the codec interface + _codec_ctrl->data_port_loopback(true); + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + this->codec_loopback_self_test(perif.ctrl); + } + _codec_ctrl->data_port_loopback(false); + + //////////////////////////////////////////////////////////////////// + // create time and clock control objects + //////////////////////////////////////////////////////////////////// + _spi_iface = spi_core_3000::make(_local_ctrl, TOREG(SR_CORE_SPI), RB32_CORE_SPI); + _spi_iface->set_divider(B200_BUS_CLOCK_RATE/ADF4001_SPI_RATE); + _adf4001_iface = boost::shared_ptr<adf4001_ctrl>(new adf4001_ctrl(_spi_iface, ADF4001_SLAVENO)); + + //register time now and pps onto available radio cores + _tree->create<time_spec_t>(mb_path / "time" / "now") + .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)); + _tree->create<time_spec_t>(mb_path / "time" / "pps") + .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)); + for (size_t i = 0; i < _radio_perifs.size(); i++) + { + _tree->access<time_spec_t>(mb_path / "time" / "now") + .subscribe(boost::bind(&time_core_3000::set_time_now, _radio_perifs[i].time64, _1)); + _tree->access<time_spec_t>(mb_path / "time" / "pps") + .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[i].time64, _1)); + } + + //setup time source props + _tree->create<std::string>(mb_path / "time_source" / "value") + .subscribe(boost::bind(&b200_impl::update_time_source, this, _1)); + static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); + //setup reference source props + _tree->create<std::string>(mb_path / "clock_source" / "value") + .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1)); + static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo"); + _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources); + + //////////////////////////////////////////////////////////////////// + // dboard eeproms but not really + //////////////////////////////////////////////////////////////////// + dboard_eeprom_t db_eeprom; + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom); + _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom); + + //////////////////////////////////////////////////////////////////// + // do some post-init tasks + //////////////////////////////////////////////////////////////////// + + //init the clock rate to something reasonable + _tree->access<double>(mb_path / "tick_rate").set( + device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE)); + + //subdev spec contains full width of selections + subdev_spec_t rx_spec, tx_spec; + BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends")) + { + rx_spec.push_back(subdev_spec_pair_t("A", fe)); + } + BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends")) + { + tx_spec.push_back(subdev_spec_pair_t("A", fe)); + } + _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); + _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); + + //init to internal clock and time source + _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); + _tree->access<std::string>(mb_path / "time_source/value").set("none"); + + //GPS installed: use external ref, time, and init time spec + if (_gps and _gps->gps_detected()) + { + UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; + _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); + _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); + UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; + const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1); + _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); + } + +} + +b200_impl::~b200_impl(void) +{ + UHD_SAFE_CALL + ( + _async_task.reset(); + ) +} + +/*********************************************************************** + * setup radio control objects + **********************************************************************/ + +void b200_impl::setup_radio(const size_t dspno) +{ + radio_perifs_t &perif = _radio_perifs[dspno]; + const fs_path mb_path = "/mboards/0"; + + //////////////////////////////////////////////////////////////////// + // radio control + //////////////////////////////////////////////////////////////////// + const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID; + perif.ctrl = radio_ctrl_core_3000::make(vrt::if_packet_info_t::LINK_TYPE_CHDR, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid); + perif.ctrl->hold_task(_async_task); + _async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak + _tree->access<time_spec_t>(mb_path / "time" / "cmd") + .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); + this->register_loopback_self_test(perif.ctrl); + perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); + + //////////////////////////////////////////////////////////////////// + // create rx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) + .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); + const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); + _tree->create<double>(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1)) + .set(1e6); + _tree->create<double>(rx_dsp_path / "freq" / "value") + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) + .set(0.0); + _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") + .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); + + //////////////////////////////////////////////////////////////////// + // create tx dsp control objects + //////////////////////////////////////////////////////////////////// + perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) + .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); + const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno); + _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); + _tree->create<double>(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1)) + .set(1e6); + _tree->create<double>(tx_dsp_path / "freq" / "value") + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) + .set(0.0); + _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") + .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = RB64_TIME_NOW; + time64_rb_bases.rb_pps = RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + + //////////////////////////////////////////////////////////////////// + // create RF frontend interfacing + //////////////////////////////////////////////////////////////////// + for(size_t direction = 0; direction < 2; direction++) + { + const std::string x = direction? "rx" : "tx"; + const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == FE1)? "1" : "2")); + const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A"); + + _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); + _tree->create<int>(rf_fe_path / "sensors"); //empty TODO + BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) + { + _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") + .set(ad9361_ctrl::get_gain_range(key)); + + _tree->create<double>(rf_fe_path / "gains" / name / "value") + .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) + .set(0.0); + } + _tree->create<std::string>(rf_fe_path / "connection").set("IQ"); + _tree->create<bool>(rf_fe_path / "enabled").set(true); + _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); + _tree->create<double>(rf_fe_path / "bandwidth" / "value") + .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) + .set(40e6); + _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") + .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); + _tree->create<double>(rf_fe_path / "freq" / "value") + .set(0.0) + .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1)); + _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") + .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + + //setup antenna stuff + if (key[0] == 'R') + { + static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); + _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); + _tree->create<std::string>(rf_fe_path / "antenna" / "value") + .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1)) + .set("RX2"); + } + if (key[0] == 'T') + { + static const std::vector<std::string> ants(1, "TX/RX"); + _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); + _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX"); + } + + } +} + +/*********************************************************************** + * loopback tests + **********************************************************************/ + +void b200_impl::register_loopback_self_test(wb_iface::sptr iface) +{ + bool test_fail = false; + UHD_MSG(status) << "Performing register loopback test... " << std::flush; + size_t hash = time(NULL); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + if (test_fail) break; //exit loop on any failure + } + UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl; +} + +void b200_impl::codec_loopback_self_test(wb_iface::sptr iface) +{ + bool test_fail = false; + UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; + size_t hash = size_t(time(NULL)); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; + iface->poke32(TOREG(SR_CODEC_IDLE), word32); + iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate + const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); + const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); + const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); + test_fail = word32 != rb_tx or word32 != rb_rx; + if (test_fail) break; //exit loop on any failure + } + UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl; + + /* Zero out the idle data. */ + iface->poke32(TOREG(SR_CODEC_IDLE), 0); +} + +/*********************************************************************** + * Sample and tick rate comprehension below + **********************************************************************/ +double b200_impl::set_tick_rate(const double rate) +{ + UHD_MSG(status) << "Asking for clock rate " << rate/1e6 << " MHz\n"; + _tick_rate = _codec_ctrl->set_clock_rate(rate); + UHD_MSG(status) << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n"; + + //reset after clock rate change + this->reset_codec_dcm(); + + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + perif.time64->set_tick_rate(_tick_rate); + perif.time64->self_test(); + } + return _tick_rate; +} + +/*********************************************************************** + * compat checks + **********************************************************************/ + +void b200_impl::check_fw_compat(void) +{ + boost::uint16_t compat_num = _iface->get_compat_num(); + boost::uint32_t compat_major = (boost::uint32_t) (compat_num >> 8); + boost::uint32_t compat_minor = (boost::uint32_t) (compat_num & 0xFF); + + if (compat_major != B200_FW_COMPAT_NUM_MAJOR){ + throw uhd::runtime_error(str(boost::format( + "Expected firmware compatibility number 0x%x, but got 0x%x.%x:\n" + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % int(B200_FW_COMPAT_NUM_MAJOR) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>("/mboards/0/fw_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +void b200_impl::check_fpga_compat(void) +{ + const boost::uint64_t compat = _local_ctrl->peek64(0); + const boost::uint32_t signature = boost::uint32_t(compat >> 32); + const boost::uint16_t compat_major = boost::uint16_t(compat >> 16); + const boost::uint16_t compat_minor = boost::uint16_t(compat & 0xffff); + if (signature != 0xACE0BA5E) throw uhd::runtime_error( + "b200::check_fpga_compat signature register readback failed"); + + if (compat_major != B200_FPGA_COMPAT_NUM){ + throw uhd::runtime_error(str(boost::format( + "Expected FPGA compatibility number 0x%x, but got 0x%x.%x:\n" + "The FPGA build is not compatible with the host code build.\n" + "%s" + ) % int(B200_FPGA_COMPAT_NUM) % compat_major % compat_minor + % print_images_error())); + } + _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") + % compat_major % compat_minor)); +} + +void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) +{ + mb_eeprom.commit(*_iface, "B200"); +} + + +/*********************************************************************** + * Reference time and clock + **********************************************************************/ + +void b200_impl::update_clock_source(const std::string &source) +{ + if (source == "internal"){ + _adf4001_iface->set_lock_to_ext_ref(false); + } + else if ((source == "external") + or (source == "gpsdo")){ + + _adf4001_iface->set_lock_to_ext_ref(true); + } else { + throw uhd::key_error("update_clock_source: unknown source: " + source); + } + + _gpio_state.ref_sel = (source == "gpsdo")? 1 : 0; + this->update_gpio_state(); +} + +void b200_impl::update_time_source(const std::string &source) +{ + if (source == "none"){} + else if (source == "external"){} + else if (source == "gpsdo"){} + else throw uhd::key_error("update_time_source: unknown source: " + source); + _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), (source == "external")? 1 : 0); +} + +/*********************************************************************** + * GPIO setup + **********************************************************************/ + +void b200_impl::update_bandsel(const std::string& which, double freq) +{ + if(which[0] == 'R') { + if(freq < 2.2e9) { + _gpio_state.rx_bandsel_a = 0; + _gpio_state.rx_bandsel_b = 0; + _gpio_state.rx_bandsel_c = 1; + } else if((freq >= 2.2e9) && (freq < 4e9)) { + _gpio_state.rx_bandsel_a = 0; + _gpio_state.rx_bandsel_b = 1; + _gpio_state.rx_bandsel_c = 0; + } else if((freq >= 4e9) && (freq <= 6e9)) { + _gpio_state.rx_bandsel_a = 1; + _gpio_state.rx_bandsel_b = 0; + _gpio_state.rx_bandsel_c = 0; + } else { + UHD_THROW_INVALID_CODE_PATH(); + } + } else if(which[0] == 'T') { + if(freq < 2.5e9) { + _gpio_state.tx_bandsel_a = 0; + _gpio_state.tx_bandsel_b = 1; + } else if((freq >= 2.5e9) && (freq <= 6e9)) { + _gpio_state.tx_bandsel_a = 1; + _gpio_state.tx_bandsel_b = 0; + } else { + UHD_THROW_INVALID_CODE_PATH(); + } + } else { + UHD_THROW_INVALID_CODE_PATH(); + } + + update_gpio_state(); +} + +void b200_impl::update_gpio_state(void) +{ + const boost::uint32_t misc_word = 0 + | (_gpio_state.tx_bandsel_a << 7) + | (_gpio_state.tx_bandsel_b << 6) + | (_gpio_state.rx_bandsel_a << 5) + | (_gpio_state.rx_bandsel_b << 4) + | (_gpio_state.rx_bandsel_c << 3) + | (_gpio_state.codec_arst << 2) + | (_gpio_state.mimo << 1) + | (_gpio_state.ref_sel << 0) + ; + + _local_ctrl->poke32(TOREG(RB32_CORE_MISC), misc_word); +} + +void b200_impl::reset_codec_dcm(void) +{ + _gpio_state.codec_arst = 1; + this->update_gpio_state(); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + _gpio_state.codec_arst = 0; + this->update_gpio_state(); +} + +void b200_impl::update_atrs(void) +{ + if (_radio_perifs.size() > FE1 and _radio_perifs[FE1].atr) + { + radio_perifs_t &perif = _radio_perifs[FE1]; + const bool enb_rx = bool(perif.rx_streamer.lock()); + const bool enb_tx = bool(perif.tx_streamer.lock()); + const bool is_rx2 = perif.ant_rx2; + const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX1_RX2 : STATE_RX1_TXRX) : STATE_OFF; + const size_t txonly = (enb_tx)? (STATE_TX1_TXRX) : STATE_OFF; + size_t fd = STATE_OFF; + if (enb_rx and enb_tx) fd = STATE_FDX1_TXRX; + if (enb_rx and not enb_tx) fd = rxonly; + if (not enb_rx and enb_tx) fd = txonly; + gpio_core_200_32wo::sptr atr = perif.atr; + atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); + atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); + atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); + atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); + } + if (_radio_perifs.size() > FE2 and _radio_perifs[FE2].atr) + { + radio_perifs_t &perif = _radio_perifs[FE2]; + const bool enb_rx = bool(perif.rx_streamer.lock()); + const bool enb_tx = bool(perif.tx_streamer.lock()); + const bool is_rx2 = perif.ant_rx2; + const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX2_RX2 : STATE_RX2_TXRX) : STATE_OFF; + const size_t txonly = (enb_tx)? (STATE_TX2_TXRX) : STATE_OFF; + size_t fd = STATE_OFF; + if (enb_rx and enb_tx) fd = STATE_FDX2_TXRX; + if (enb_rx and not enb_tx) fd = rxonly; + if (not enb_rx and enb_tx) fd = txonly; + gpio_core_200_32wo::sptr atr = perif.atr; + atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); + atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); + atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); + atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); + } +} + +void b200_impl::update_antenna_sel(const size_t which, const std::string &ant) +{ + if (ant != "TX/RX" and ant != "RX2") throw uhd::value_error("b200: unknown RX antenna option: " + ant); + _radio_perifs[which].ant_rx2 = (ant == "RX2"); + this->update_atrs(); +} + +void b200_impl::update_enables(void) +{ + //extract settings from state variables + const bool enb_tx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].tx_streamer.lock()); + const bool enb_rx1 = (_radio_perifs.size() > FE1) and bool(_radio_perifs[FE1].rx_streamer.lock()); + const bool enb_tx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].tx_streamer.lock()); + const bool enb_rx2 = (_radio_perifs.size() > FE2) and bool(_radio_perifs[FE2].rx_streamer.lock()); + const size_t num_rx = (enb_rx1?1:0) + (enb_rx2?1:0); + const size_t num_tx = (enb_tx1?1:0) + (enb_tx2?1:0); + const bool mimo = num_rx == 2 or num_tx == 2; + + //setup the active chains in the codec + _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2); + if ((num_rx + num_tx) == 0) _codec_ctrl->set_active_chains(true, false, true, false); //enable something + this->reset_codec_dcm(); //set_active_chains could cause a clock rate change - reset dcm + + //figure out if mimo is enabled based on new state + _gpio_state.mimo = (mimo)? 1 : 0; + update_gpio_state(); + + //atrs change based on enables + this->update_atrs(); +} + +sensor_value_t b200_impl::get_ref_locked(void) +{ + const bool lock = (_local_ctrl->peek32(RB32_CORE_MISC) & 0x1) == 0x1; + return sensor_value_t("Ref", lock, "locked", "unlocked"); +} diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp new file mode 100644 index 000000000..a59e0b977 --- /dev/null +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -0,0 +1,196 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B200_IMPL_HPP +#define INCLUDED_B200_IMPL_HPP + +#include "b200_iface.hpp" +#include "b200_uart.hpp" +#include "ad9361_ctrl.hpp" +#include "adf4001_ctrl.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "time_core_3000.hpp" +#include "gpio_core_200.hpp" +#include "radio_ctrl_core_3000.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include <uhd/device.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/utils/pimpl.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/transport/usb_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/weak_ptr.hpp> +#include "recv_packet_demuxer_3000.hpp" + +static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; +static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; +static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; +static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x02; +static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x02; +static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps) +static const double B200_BUS_CLOCK_RATE = 100e6; +static const double B200_DEFAULT_TICK_RATE = 32e6; +static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; + +#define FLIP_SID(sid) (((sid)<<16)|((sid)>>16)) + +static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010; +static const boost::uint32_t B200_RESP0_MSG_SID = FLIP_SID(B200_CTRL0_MSG_SID); + +static const boost::uint32_t B200_CTRL1_MSG_SID = 0x00000020; +static const boost::uint32_t B200_RESP1_MSG_SID = FLIP_SID(B200_CTRL1_MSG_SID); + +static const boost::uint32_t B200_TX_DATA0_SID = 0x00000050; +static const boost::uint32_t B200_TX_MSG0_SID = FLIP_SID(B200_TX_DATA0_SID); + +static const boost::uint32_t B200_TX_DATA1_SID = 0x00000060; +static const boost::uint32_t B200_TX_MSG1_SID = FLIP_SID(B200_TX_DATA1_SID); + +static const boost::uint32_t B200_RX_DATA0_SID = 0x000000A0; +static const boost::uint32_t B200_RX_DATA1_SID = 0x000000B0; + +static const boost::uint32_t B200_TX_GPS_UART_SID = 0x00000030; +static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SID); + +static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040; +static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID); + +/*********************************************************************** + * The B200 Capability Constants + **********************************************************************/ + +//! Implementation guts +struct b200_impl : public uhd::device +{ + //structors + b200_impl(const uhd::device_addr_t &); + ~b200_impl(void); + + //the io interface + uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); + bool recv_async_msg(uhd::async_metadata_t &, double); + + uhd::property_tree::sptr _tree; + + //controllers + b200_iface::sptr _iface; + radio_ctrl_core_3000::sptr _local_ctrl; + ad9361_ctrl::sptr _codec_ctrl; + spi_core_3000::sptr _spi_iface; + boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface; + uhd::gps_ctrl::sptr _gps; + + //transports + uhd::transport::zero_copy_if::sptr _data_transport; + uhd::transport::zero_copy_if::sptr _ctrl_transport; + boost::shared_ptr<uhd::usrp::recv_packet_demuxer_3000> _demux; + + //device properties interface + uhd::property_tree::sptr get_tree(void) const + { + return _tree; + } + + boost::weak_ptr<uhd::rx_streamer> _rx_streamer; + boost::weak_ptr<uhd::tx_streamer> _tx_streamer; + + //async ctrl + msgs + uhd::task::sptr _async_task; + typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + struct AsyncTaskData + { + boost::shared_ptr<async_md_type> async_md; + boost::weak_ptr<radio_ctrl_core_3000> local_ctrl; + boost::weak_ptr<radio_ctrl_core_3000> radio_ctrl[2]; + b200_uart::sptr gpsdo_uart; + }; + boost::shared_ptr<AsyncTaskData> _async_task_data; + void handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>); + + void register_loopback_self_test(wb_iface::sptr iface); + void codec_loopback_self_test(wb_iface::sptr iface); + void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); + void check_fw_compat(void); + void check_fpga_compat(void); + void update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); + void update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); + void update_time_source(const std::string &); + void update_clock_source(const std::string &); + void update_bandsel(const std::string& which, double freq); + void update_antenna_sel(const size_t which, const std::string &ant); + uhd::sensor_value_t get_ref_locked(void); + + //perifs in the radio core + struct radio_perifs_t + { + radio_ctrl_core_3000::sptr ctrl; + gpio_core_200_32wo::sptr atr; + time_core_3000::sptr time64; + rx_vita_core_3000::sptr framer; + rx_dsp_core_3000::sptr ddc; + tx_vita_core_3000::sptr deframer; + tx_dsp_core_3000::sptr duc; + boost::weak_ptr<uhd::rx_streamer> rx_streamer; + boost::weak_ptr<uhd::tx_streamer> tx_streamer; + bool ant_rx2; + }; + std::vector<radio_perifs_t> _radio_perifs; + void setup_radio(const size_t which_radio); + void handle_overflow(const size_t index); + + struct gpio_state { + boost::uint32_t tx_bandsel_a, tx_bandsel_b, rx_bandsel_a, rx_bandsel_b, rx_bandsel_c, codec_arst, mimo, ref_sel; + + gpio_state() { + tx_bandsel_a = 0; + tx_bandsel_b = 0; + rx_bandsel_a = 0; + rx_bandsel_b = 0; + rx_bandsel_c = 0; + codec_arst = 0; + mimo = 0; + ref_sel = 0; + } + } _gpio_state; + + void update_gpio_state(void); + void reset_codec_dcm(void); + + void update_enables(void); + void update_atrs(void); + + void update_tick_rate(const double); + void update_rx_samp_rate(const size_t, const double); + void update_tx_samp_rate(const size_t, const double); + + double _tick_rate; + double get_tick_rate(void){return _tick_rate;} + double set_tick_rate(const double rate); +}; + +#endif /* INCLUDED_B200_IMPL_HPP */ diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp new file mode 100644 index 000000000..5a588f902 --- /dev/null +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -0,0 +1,378 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "b200_regs.hpp" +#include "b200_impl.hpp" +#include "validate_subdev_spec.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "async_packet_handler.hpp" +#include <boost/bind.hpp> +#include <boost/make_shared.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/*********************************************************************** + * update streamer rates + **********************************************************************/ +void b200_impl::update_tick_rate(const double rate) +{ + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + perif.framer->set_tick_rate(_tick_rate); + } + BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) + { + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); + if (my_streamer) my_streamer->set_tick_rate(rate); + perif.deframer->set_tick_rate(_tick_rate); + } +} + +void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) +{ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) +{ + boost::shared_ptr<sph::send_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock()); + if (not my_streamer) return; + my_streamer->set_samp_rate(rate); + const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment(); + my_streamer->set_scale_factor(adj); +} + +/*********************************************************************** + * frontend selection + **********************************************************************/ +void b200_impl::update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ + //sanity checking + if (spec.size()) validate_subdev_spec(_tree, spec, "rx"); + UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size()); + + if (spec.size() > 0) + { + UHD_ASSERT_THROW(spec[0].db_name == "A"); + UHD_ASSERT_THROW(spec[0].sd_name == "A"); + } + if (spec.size() > 1) + { + //TODO we can support swapping at a later date, only this combo is supported + UHD_ASSERT_THROW(spec[1].db_name == "A"); + UHD_ASSERT_THROW(spec[1].sd_name == "B"); + } + + this->update_enables(); +} + +void b200_impl::update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ + //sanity checking + if (spec.size()) validate_subdev_spec(_tree, spec, "tx"); + UHD_ASSERT_THROW(spec.size() <= _radio_perifs.size()); + + if (spec.size() > 0) + { + UHD_ASSERT_THROW(spec[0].db_name == "A"); + UHD_ASSERT_THROW(spec[0].sd_name == "A"); + } + if (spec.size() > 1) + { + //TODO we can support swapping at a later date, only this combo is supported + UHD_ASSERT_THROW(spec[1].db_name == "A"); + UHD_ASSERT_THROW(spec[1].sd_name == "B"); + } + + this->update_enables(); +} + +static void b200_if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); +} + +static void b200_if_hdr_pack_le( + boost::uint32_t *packet_buff, + vrt::if_packet_info_t &if_packet_info +){ + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + return vrt::if_hdr_pack_le(packet_buff, if_packet_info); +} + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool b200_impl::recv_async_msg( + async_metadata_t &async_metadata, double timeout +){ + return _async_task_data->async_md->pop_with_timed_wait(async_metadata, timeout); +} + +void b200_impl::handle_async_task( + uhd::transport::zero_copy_if::sptr xport, + boost::shared_ptr<AsyncTaskData> data +) +{ + managed_recv_buffer::sptr buff = xport->get_recv_buff(); + if (not buff or buff->size() < 8) return; + const boost::uint32_t sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); + switch (sid) + { + + //if the packet is a control response + case B200_RESP0_MSG_SID: + case B200_RESP1_MSG_SID: + case B200_LOCAL_RESP_SID: + { + radio_ctrl_core_3000::sptr ctrl; + if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock(); + if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock(); + if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock(); + if (ctrl) ctrl->push_response(buff->cast<const boost::uint32_t *>()); + break; + } + + //if the packet is a uart message + case B200_RX_GPS_UART_SID: + { + data->gpsdo_uart->handle_uart_packet(buff); + break; + } + + //or maybe the packet is a TX async message + case B200_TX_MSG0_SID: + case B200_TX_MSG1_SID: + { + const size_t i = (sid == B200_TX_MSG0_SID)? 0 : 1; + + //extract packet info + vrt::if_packet_info_t if_packet_info; + if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + + //unpacking can fail + try + { + b200_if_hdr_unpack_le(packet_buff, if_packet_info); + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "Error parsing ctrl packet: " << ex.what() << std::endl; + break; + } + + //fill in the async metadata + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, if_packet_info, packet_buff, _tick_rate, i); + data->async_md->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + break; + } + + //doh! + default: + UHD_MSG(error) << "Got a ctrl packet with unknown SID " << sid << std::endl; + } +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) +{ + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("b200_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + boost::shared_ptr<sph::recv_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + radio_perifs_t &perif = _radio_perifs[chan]; + const boost::uint32_t sid = chan?B200_RX_DATA1_SID:B200_RX_DATA0_SID; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; + const size_t bpi = convert::get_bytes_per_item(args.otw_format); + size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + spp = std::min<size_t>(2041, spp); //magic maximum for framing at full rate + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + my_streamer->set_vrt_unpacker(&b200_if_hdr_unpack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.otw_format + "_item32_le"; + id.num_inputs = 1; + id.output_format = args.cpu_format; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.framer->clear(); + perif.framer->set_nsamps_per_packet(spp); + perif.framer->set_sid(sid); + perif.framer->setup(args); + perif.ddc->setup(args); + _demux->realloc_sid(sid); + my_streamer->set_xport_chan_get_buff(stream_i, boost::bind( + &recv_packet_demuxer_3000::get_recv_buff, _demux, sid, _1 + ), true /*flush*/); + my_streamer->set_overflow_handler(stream_i, boost::bind( + &b200_impl::handle_overflow, this, chan + )); + my_streamer->set_issue_stream_cmd(stream_i, boost::bind( + &rx_vita_core_3000::issue_stream_command, perif.framer, _1 + )); + perif.rx_streamer = my_streamer; //store weak pointer + + //sets all tick and samp rates on this streamer + this->update_tick_rate(this->get_tick_rate()); + _tree->access<double>(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % chan)).update(); + } + this->update_enables(); + + return my_streamer; +} + +void b200_impl::handle_overflow(const size_t i) +{ + boost::shared_ptr<sph::recv_packet_streamer> my_streamer = + boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[i].rx_streamer.lock()); + if (my_streamer->get_num_channels() == 2) //MIMO time + { + //find out if we were in continuous mode before stopping + const bool in_continuous_streaming_mode = _radio_perifs[i].framer->in_continuous_streaming_mode(); + //stop streaming + my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //flush demux + _demux->realloc_sid(B200_RX_DATA0_SID); + _demux->realloc_sid(B200_RX_DATA1_SID); + //flush actual transport + while (_data_transport->get_recv_buff(0.001)){} + //restart streaming + if (in_continuous_streaming_mode) + { + stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.stream_now = false; + stream_cmd.time_spec = _radio_perifs[i].time64->get_time_now() + time_spec_t(0.01); + my_streamer->issue_stream_cmd(stream_cmd); + } + } + else _radio_perifs[i].framer->handle_overflow(); +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_) +{ + stream_args_t args = args_; + + //setup defaults for unspecified values + if (not args.otw_format.empty() and args.otw_format != "sc16") + { + throw uhd::value_error("b200_impl::get_rx_stream only supports otw_format sc16"); + } + args.otw_format = "sc16"; + args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + + boost::shared_ptr<sph::send_packet_streamer> my_streamer; + for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) + { + const size_t chan = args.channels[stream_i]; + radio_perifs_t &perif = _radio_perifs[chan]; + + //calculate packet size + static const size_t hdr_size = 0 + + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) + //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + - sizeof(vrt::if_packet_info_t().cid) //no class id ever used + - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used + ; + static const size_t bpp = _data_transport->get_send_frame_size() - hdr_size; + const size_t spp = bpp/convert::get_bytes_per_item(args.otw_format); + + //make the new streamer given the samples per packet + if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); + my_streamer->resize(args.channels.size()); + + //init some streamer stuff + my_streamer->set_vrt_packer(&b200_if_hdr_pack_le); + + //set the converter + uhd::convert::id_type id; + id.input_format = args.cpu_format; + id.num_inputs = 1; + id.output_format = args.otw_format + "_item32_le"; + id.num_outputs = 1; + my_streamer->set_converter(id); + + perif.deframer->clear(); + perif.deframer->setup(args); + perif.duc->setup(args); + + my_streamer->set_xport_chan_get_buff(stream_i, boost::bind( + &zero_copy_if::get_send_buff, _data_transport, _1 + )); + my_streamer->set_async_receiver(boost::bind( + &async_md_type::pop_with_timed_wait, _async_task_data->async_md, _1, _2 + )); + my_streamer->set_xport_chan_sid(stream_i, true, chan?B200_TX_DATA1_SID:B200_TX_DATA0_SID); + my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet + perif.tx_streamer = my_streamer; //store weak pointer + + //sets all tick and samp rates on this streamer + this->update_tick_rate(this->get_tick_rate()); + _tree->access<double>(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % chan)).update(); + } + this->update_enables(); + + return my_streamer; +} diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp new file mode 100644 index 000000000..ae39b95b2 --- /dev/null +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -0,0 +1,120 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B200_REGS_HPP +#define INCLUDED_B200_REGS_HPP + +#include <boost/cstdint.hpp> + +#define TOREG(x) ((x)*4) + +#define localparam static const int + +localparam SR_CORE_SPI = 8; +localparam SR_CORE_MISC = 16; +localparam SR_CORE_COMPAT = 24; +localparam SR_CORE_GPSDO_ST = 40; +localparam SR_CORE_PPS_SEL = 48; +localparam RB32_CORE_SPI = 8; +localparam RB32_CORE_MISC = 16; +localparam RB32_CORE_STATUS = 20; + +localparam SR_SPI = 8; +localparam SR_ATR = 12; +localparam SR_TEST = 21; +localparam SR_CODEC_IDLE = 22; +localparam SR_READBACK = 32; +localparam SR_TX_CTRL = 64; +localparam SR_RX_CTRL = 96; +localparam SR_RX_DSP = 144; +localparam SR_TX_DSP = 184; +localparam SR_TIME = 128; + +localparam RB32_TEST = 0; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB64_CODEC_READBACK = 24; + +//pll constants +static const int ADF4001_SLAVENO = (1 << 1); +static const double ADF4001_SPI_RATE = 10e3; //slow for large time constant on spi lines + +/* ATR Control Bits */ +static const boost::uint32_t TX_ENABLE1 = (1 << 7); +static const boost::uint32_t SFDX1_RX = (1 << 6); +static const boost::uint32_t SFDX1_TX = (1 << 5); +static const boost::uint32_t SRX1_RX = (1 << 4); +static const boost::uint32_t SRX1_TX = (1 << 3); +static const boost::uint32_t LED_RX1 = (1 << 2); +static const boost::uint32_t LED_TXRX_RX1 = (1 << 1); +static const boost::uint32_t LED_TXRX_TX1 = (1 << 0); + +static const boost::uint32_t TX_ENABLE2 = (1 << 7); +static const boost::uint32_t SFDX2_RX = (1 << 6); +static const boost::uint32_t SFDX2_TX = (1 << 5); +static const boost::uint32_t SRX2_RX = (1 << 4); +static const boost::uint32_t SRX2_TX = (1 << 3); +static const boost::uint32_t LED_RX2 = (1 << 2); +static const boost::uint32_t LED_TXRX_RX2 = (1 << 1); +static const boost::uint32_t LED_TXRX_TX2 = (1 << 0); + + +/* ATR State Definitions. */ +static const boost::uint32_t STATE_OFF = 0x00; + +///////////////////////// side 1 /////////////////////////////////// +static const boost::uint32_t STATE_RX1_RX2 = (SFDX1_RX + | SFDX1_TX + | LED_RX1); + +static const boost::uint32_t STATE_RX1_TXRX = (SRX1_RX + | SRX1_TX + | LED_TXRX_RX1); + +static const boost::uint32_t STATE_FDX1_TXRX = (TX_ENABLE1 + | SFDX1_RX + | SFDX1_TX + | LED_TXRX_TX1 + | LED_RX1); + +static const boost::uint32_t STATE_TX1_TXRX = (TX_ENABLE1 + | SFDX1_RX + | SFDX1_TX + | LED_TXRX_TX1); + +///////////////////////// side 2 /////////////////////////////////// +static const boost::uint32_t STATE_RX2_RX2 = (SFDX2_RX + | SRX2_TX + | LED_RX2); + +static const boost::uint32_t STATE_RX2_TXRX = (SRX2_TX + | SRX2_RX + | LED_TXRX_RX2); + +static const boost::uint32_t STATE_FDX2_TXRX = (TX_ENABLE2 + | SFDX2_RX + | SFDX2_TX + | LED_TXRX_TX2 + | LED_RX2); + +static const boost::uint32_t STATE_TX2_TXRX = (TX_ENABLE2 + | SFDX2_RX + | SFDX2_TX + | LED_TXRX_TX2); + + +#endif /* INCLUDED_B200_REGS_HPP */ diff --git a/host/lib/usrp/b200/b200_uart.cpp b/host/lib/usrp/b200/b200_uart.cpp new file mode 100644 index 000000000..4682a79b9 --- /dev/null +++ b/host/lib/usrp/b200/b200_uart.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "b200_uart.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/exception.hpp> + +using namespace uhd; +using namespace uhd::transport; + +struct b200_uart_impl : b200_uart +{ + b200_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid): + _xport(xport), + _sid(sid), + _count(0), + _char_queue(4096) + { + //this default baud divider is over 9000 + this->set_baud_divider(9001); + } + + void send_char(const char ch) + { + managed_send_buffer::sptr buff = _xport->get_send_buff(); + UHD_ASSERT_THROW(bool(buff)); + + vrt::if_packet_info_t packet_info; + packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = _count++; + packet_info.sob = false; + packet_info.eob = false; + packet_info.sid = _sid; + packet_info.has_sid = true; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = false; + packet_info.has_tlr = false; + + boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>(); + vrt::if_hdr_pack_le(packet_buff, packet_info); + packet_buff[packet_info.num_header_words32+0] = uhd::htowx(boost::uint32_t(_baud_div)); + packet_buff[packet_info.num_header_words32+1] = uhd::htowx(boost::uint32_t(ch)); + buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t)); + } + + void write_uart(const std::string &buff) + { + for (size_t i = 0; i < buff.size(); i++) + { + if (buff[i] == '\n') this->send_char('\r'); + this->send_char(buff[i]); + } + } + + std::string read_uart(double timeout) + { + std::string line; + char ch = '\0'; + while (_char_queue.pop_with_timed_wait(ch, timeout)) + { + if (ch == '\r') continue; + line += std::string(&ch, 1); + if (ch == '\n') return line; + } + return line; + } + + void handle_uart_packet(managed_recv_buffer::sptr buff) + { + const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + vrt::if_hdr_unpack_le(packet_buff, packet_info); + const char ch = char(uhd::wtohx(packet_buff[packet_info.num_header_words32+1])); + _char_queue.push_with_pop_on_full(ch); + } + + void set_baud_divider(const double baud_div) + { + _baud_div = size_t(baud_div + 0.5); + } + + const zero_copy_if::sptr _xport; + const boost::uint32_t _sid; + size_t _count; + size_t _baud_div; + bounded_buffer<char> _char_queue; +}; + + +b200_uart::sptr b200_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid) +{ + return b200_uart::sptr(new b200_uart_impl(xport, sid)); +} diff --git a/host/lib/usrp/b200/b200_uart.hpp b/host/lib/usrp/b200/b200_uart.hpp new file mode 100644 index 000000000..1c8e44ddc --- /dev/null +++ b/host/lib/usrp/b200/b200_uart.hpp @@ -0,0 +1,36 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_B200_UART_HPP +#define INCLUDED_B200_UART_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> //uart iface +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class b200_uart: boost::noncopyable, public uhd::uart_iface +{ +public: + typedef boost::shared_ptr<b200_uart> sptr; + static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid); + virtual void handle_uart_packet(uhd::transport::managed_recv_buffer::sptr buff) = 0; + virtual void set_baud_divider(const double baud_div) = 0; +}; + + +#endif /* INCLUDED_B200_UART_HPP */ diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index fa07e3d1d..1728b63f9 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,6 +29,8 @@ ENDIF(ENABLE_USB) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp new file mode 100644 index 000000000..1afa2fbb7 --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -0,0 +1,173 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "ad9361_ctrl.hpp" +#include "ad9361_transaction.h" +#include <uhd/exception.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/format.hpp> +#include <cstring> + +//! compat strnlen for platforms that dont have it +static size_t my_strnlen(const char *str, size_t max) +{ + const char *end = (const char *)std::memchr((const void *)str, 0, max); + if (end == NULL) return max; + return (size_t)(end - str); +} + +using namespace uhd; + +struct ad9361_ctrl_impl : public ad9361_ctrl +{ + ad9361_ctrl_impl(ad9361_ctrl_iface_sptr iface): + _iface(iface), _seq(0) + { + ad9361_transaction_t request; + + request.action = AD9361_ACTION_ECHO; + this->do_transaction(request); + + request.action = AD9361_ACTION_INIT; + this->do_transaction(request); + } + + double set_gain(const std::string &which, const double value) + { + ad9361_transaction_t request; + + if (which == "RX1") request.action = AD9361_ACTION_SET_RX1_GAIN; + if (which == "RX2") request.action = AD9361_ACTION_SET_RX2_GAIN; + if (which == "TX1") request.action = AD9361_ACTION_SET_TX1_GAIN; + if (which == "TX2") request.action = AD9361_ACTION_SET_TX2_GAIN; + + ad9361_double_pack(value, request.value.gain); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.gain); + } + + //! set a new clock rate, return the exact value + double set_clock_rate(const double rate) + { + //warning for known trouble rates + if (rate > 56e6) UHD_MSG(warning) << boost::format( + "The requested clock rate %f MHz may cause slow configuration.\n" + "The driver recommends a master clock rate less than %f MHz.\n" + ) % (rate/1e6) % 56.0 << std::endl; + + //clip to known bounds + const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range(); + const double clipped_rate = clock_rate_range.clip(rate); + + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_CLOCK_RATE; + ad9361_double_pack(clipped_rate, request.value.rate); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.rate); + } + + //! set which RX and TX chains/antennas are active + void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) + { + boost::uint32_t mask = 0; + if (tx1) mask |= (1 << 0); + if (tx2) mask |= (1 << 1); + if (rx1) mask |= (1 << 2); + if (rx2) mask |= (1 << 3); + + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_ACTIVE_CHAINS; + request.value.enable_mask = mask; + this->do_transaction(request); + } + + //! tune the given frontend, return the exact value + double tune(const std::string &which, const double freq) + { + //clip to known bounds + const meta_range_t freq_range = ad9361_ctrl::get_rf_freq_range(); + const double clipped_freq = freq_range.clip(freq); + + ad9361_transaction_t request; + + if (which[0] == 'R') request.action = AD9361_ACTION_SET_RX_FREQ; + if (which[0] == 'T') request.action = AD9361_ACTION_SET_TX_FREQ; + + const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq); + ad9361_double_pack(value, request.value.freq); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.freq); + } + + //! turn on/off Catalina's data port loopback + void data_port_loopback(const bool on) + { + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_CODEC_LOOP; + request.value.codec_loop = on? 1 : 0; + this->do_transaction(request); + } + + ad9361_transaction_t do_transaction(const ad9361_transaction_t &request) + { + boost::mutex::scoped_lock lock(_mutex); + + //declare in/out buffers + unsigned char in_buff[64] = {}; + unsigned char out_buff[64] = {}; + + //copy the input transaction + std::memcpy(in_buff, &request, sizeof(request)); + + //fill in other goodies + ad9361_transaction_t *in = (ad9361_transaction_t *)in_buff; + in->version = AD9361_TRANSACTION_VERSION; + in->sequence = _seq++; + + //transact + _iface->ad9361_transact(in_buff, out_buff); + ad9361_transaction_t *out = (ad9361_transaction_t *)out_buff; + + //sanity checks + UHD_ASSERT_THROW(out->version == in->version); + UHD_ASSERT_THROW(out->sequence == in->sequence); + + //handle errors + const size_t len = my_strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG); + const std::string error_msg(out->error_msg, len); + if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg); + + //return result done! + return *out; + } + + ad9361_ctrl_iface_sptr _iface; + size_t _seq; + boost::mutex _mutex; + +}; + + +/*********************************************************************** + * Make an instance of the implementation + **********************************************************************/ +ad9361_ctrl::sptr ad9361_ctrl::make(ad9361_ctrl_iface_sptr iface) +{ + return sptr(new ad9361_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp new file mode 100644 index 000000000..fd8012764 --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -0,0 +1,128 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_CTRL_HPP +#define INCLUDED_AD9361_CTRL_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/function.hpp> +#include <vector> +#include <string> + + +struct ad9361_ctrl_iface_type +{ + virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0; +}; +typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr; + + +struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type +{ + ad9361_ctrl_over_zc(uhd::transport::zero_copy_if::sptr xport) + { + _xport = xport; + } + + void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) + { + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout"); + std::memcpy(buff->cast<void *>(), in_buff, 64); + buff->commit(64); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout"); + std::memcpy(out_buff, buff->cast<const void *>(), 64); + } + } + + uhd::transport::zero_copy_if::sptr _xport; +}; + + +class ad9361_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr<ad9361_ctrl> sptr; + + //! make a new codec control object + static sptr make(ad9361_ctrl_iface_sptr iface); + + //! Get a list of gain names for RX or TX + static std::vector<std::string> get_gain_names(const std::string &/*which*/) + { + return std::vector<std::string>(1, "PGA"); + } + + //! get the gain range for a particular gain element + static uhd::meta_range_t get_gain_range(const std::string &which) + { + if(which[0] == 'R') { + return uhd::meta_range_t(0.0, 73.0, 1.0); + } else { + return uhd::meta_range_t(0.0, 89.75, 0.25); + } + } + + //! get the freq range for the frontend which + static uhd::meta_range_t get_rf_freq_range(void) + { + return uhd::meta_range_t(50e6, 6e9); + } + + //! get the filter range for the frontend which + static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/) + { + return uhd::meta_range_t(200e3, 56e6); + } + + //! get the clock rate range for the frontend + static uhd::meta_range_t get_clock_rate_range(void) + { + //return uhd::meta_range_t(220e3, 61.44e6); + return uhd::meta_range_t(5e6, 61.44e6); //5 MHz DCM low end + } + + //! set the filter bandwidth for the frontend + double set_bw_filter(const std::string &/*which*/, const double /*bw*/) + { + return 56e6; //TODO + } + + //! set the gain for a particular gain element + virtual double set_gain(const std::string &which, const double value) = 0; + + //! set a new clock rate, return the exact value + virtual double set_clock_rate(const double rate) = 0; + + //! set which RX and TX chains/antennas are active + virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0; + + //! tune the given frontend, return the exact value + virtual double tune(const std::string &which, const double value) = 0; + + //! turn on/off Catalina's data port loopback + virtual void data_port_loopback(const bool on) = 0; +}; + +#endif /* INCLUDED_AD9361_CTRL_HPP */ diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h new file mode 100644 index 000000000..7b41b811f --- /dev/null +++ b/host/lib/usrp/common/ad9361_transaction.h @@ -0,0 +1,102 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_TRANSACTION_H +#define INCLUDED_AD9361_TRANSACTION_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +//various constants +#define AD9361_TRANSACTION_VERSION 0x4 +#define AD9361_TRANSACTION_MAX_ERROR_MSG 40 + +//action types +#define AD9361_ACTION_ECHO 0 +#define AD9361_ACTION_INIT 1 +#define AD9361_ACTION_SET_RX1_GAIN 2 +#define AD9361_ACTION_SET_TX1_GAIN 3 +#define AD9361_ACTION_SET_RX2_GAIN 4 +#define AD9361_ACTION_SET_TX2_GAIN 5 +#define AD9361_ACTION_SET_RX_FREQ 6 +#define AD9361_ACTION_SET_TX_FREQ 7 +#define AD9361_ACTION_SET_CODEC_LOOP 8 +#define AD9361_ACTION_SET_CLOCK_RATE 9 +#define AD9361_ACTION_SET_ACTIVE_CHAINS 10 + +static inline void ad9361_double_pack(const double input, uint32_t output[2]) +{ + const uint32_t *p = (const uint32_t *)&input; + output[0] = p[0]; + output[1] = p[1]; +} + +static inline double ad9361_double_unpack(const uint32_t input[2]) +{ + double output = 0.0; + uint32_t *p = (uint32_t *)&output; + p[0] = input[0]; + p[1] = input[1]; + return output; +} + +typedef struct +{ + //version is expected to be AD9361_TRANSACTION_VERSION + //check otherwise for compatibility + uint32_t version; + + //sequence number - increment every call for sanity + uint32_t sequence; + + //action tells us what to do, see AD9361_ACTION_* + uint32_t action; + + union + { + //enable mask for chains + uint32_t enable_mask; + + //true to enable codec internal loopback + uint32_t codec_loop; + + //freq holds request LO freq and result from tune + uint32_t freq[2]; + + //gain holds request gain and result from action + uint32_t gain[2]; + + //rate holds request clock rate and result from action + uint32_t rate[2]; + + } value; + + //error message comes back as a reply - + //set to null string for no error \0 + char error_msg[]; + +} ad9361_transaction_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_AD9361_TRANSACTION_H */ diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp new file mode 100644 index 000000000..46171c7ce --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +// Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "adf4001_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <iostream> +#include <iomanip> + +using namespace uhd; +using namespace uhd::usrp; + +adf4001_regs_t::adf4001_regs_t(void) { + ref_counter = 0; + n = 0; + charge_pump_current_1 = 0; + charge_pump_current_2 = 0; + anti_backlash_width = ANTI_BACKLASH_WIDTH_2_9NS; + lock_detect_precision = LOCK_DETECT_PRECISION_3CYC; + charge_pump_gain = CHARGE_PUMP_GAIN_1; + counter_reset = COUNTER_RESET_NORMAL; + power_down = POWER_DOWN_NORMAL; + muxout = MUXOUT_TRISTATE_OUT; + phase_detector_polarity = PHASE_DETECTOR_POLARITY_NEGATIVE; + charge_pump_mode = CHARGE_PUMP_TRISTATE; + fastlock_mode = FASTLOCK_MODE_DISABLED; + timer_counter_control = TIMEOUT_3CYC; +} + + +boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) { + boost::uint32_t reg = 0; + switch (addr) { + case 0: + reg |= (boost::uint32_t(ref_counter) & 0x003FFF) << 2; + reg |= (boost::uint32_t(anti_backlash_width) & 0x000003) << 16; + reg |= (boost::uint32_t(lock_detect_precision) & 0x000001) << 20; + break; + case 1: + reg |= (boost::uint32_t(n) & 0x001FFF) << 8; + reg |= (boost::uint32_t(charge_pump_gain) & 0x000001) << 21; + break; + case 2: + reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; + reg |= (boost::uint32_t(power_down) & 0x000001) << 3; + reg |= (boost::uint32_t(muxout) & 0x000007) << 4; + reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; + reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; + reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; + reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; + reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; + reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; + reg |= (boost::uint32_t(power_down) & 0x000002) << 21; + break; + case 3: + reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; + reg |= (boost::uint32_t(power_down) & 0x000001) << 3; + reg |= (boost::uint32_t(muxout) & 0x000007) << 4; + reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; + reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; + reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; + reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; + reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; + reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; + reg |= (boost::uint32_t(power_down) & 0x000002) << 21; + break; + default: + break; + } + + reg |= (boost::uint32_t(addr) & 0x03); + + return reg; +} + + +adf4001_ctrl::adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno): + spi_iface(_spi), + slaveno(slaveno) + { + + spi_config.mosi_edge = spi_config_t::EDGE_RISE; + + //set defaults + adf4001_regs.ref_counter = 1; + adf4001_regs.n = 4; + adf4001_regs.charge_pump_current_1 = 7; + adf4001_regs.charge_pump_current_2 = 7; + adf4001_regs.muxout = adf4001_regs_t::MUXOUT_DLD; + adf4001_regs.counter_reset = adf4001_regs_t::COUNTER_RESET_NORMAL; + adf4001_regs.phase_detector_polarity = adf4001_regs_t::PHASE_DETECTOR_POLARITY_POSITIVE; + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; + + //everything else should be defaults + + program_regs(); +} + +void adf4001_ctrl::set_lock_to_ext_ref(bool external) { + if(external) { + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_NORMAL; + } else { + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; + } + + program_regs(); +} + +void adf4001_ctrl::program_regs(void) { + //no control over CE, only LE, therefore we use the initialization latch method + write_reg(3); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + + //write R counter latch (0) + write_reg(0); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + + //write N counter latch (1) + write_reg(1); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); +} + + +void adf4001_ctrl::write_reg(boost::uint8_t addr) { + boost::uint32_t reg = adf4001_regs.get_reg(addr); //load the reg data + + spi_iface->transact_spi(slaveno, + spi_config, + reg, + 24, + false); +} diff --git a/host/lib/usrp/common/adf4001_ctrl.hpp b/host/lib/usrp/common/adf4001_ctrl.hpp new file mode 100644 index 000000000..a16cff3fa --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.hpp @@ -0,0 +1,142 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +// Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP + +#include "spi_core_3000.hpp" +#include <uhd/types/serial.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread/thread.hpp> + +namespace uhd { namespace usrp { + +class adf4001_regs_t { +public: + + /* Function prototypes */ + boost::uint32_t get_reg(boost::uint8_t addr); + adf4001_regs_t(void); + + /* Register values / addresses */ + boost::uint16_t ref_counter; //14 bits + boost::uint16_t n; //13 bits + boost::uint8_t charge_pump_current_1; //3 bits + boost::uint8_t charge_pump_current_2; //3 bits + + enum anti_backlash_width_t { + ANTI_BACKLASH_WIDTH_2_9NS = 0, + ANTI_BACKLASH_WIDTH_1_3NS = 1, + ANTI_BACKLASH_WIDTH_6_0NS = 2, + ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3 + }; + anti_backlash_width_t anti_backlash_width; + + enum lock_detect_precision_t { + LOCK_DETECT_PRECISION_3CYC = 0, + LOCK_DETECT_PRECISION_5CYC = 1 + }; + lock_detect_precision_t lock_detect_precision; + enum charge_pump_gain_t { + CHARGE_PUMP_GAIN_1 = 0, + CHARGE_PUMP_GAIN_2 = 1 + }; + charge_pump_gain_t charge_pump_gain; + enum counter_reset_t { + COUNTER_RESET_NORMAL = 0, + COUNTER_RESET_RESET = 1 + }; + counter_reset_t counter_reset; + enum power_down_t { + POWER_DOWN_NORMAL = 0, + POWER_DOWN_ASYNC = 1, + POWER_DOWN_SYNC = 3 + }; + power_down_t power_down; + enum muxout_t { + MUXOUT_TRISTATE_OUT = 0, + MUXOUT_DLD = 1, + MUXOUT_NDIV = 2, + MUXOUT_AVDD = 3, + MUXOUT_RDIV = 4, + MUXOUT_NCH_OD_ALD = 5, + MUXOUT_SDO = 6, + MUXOUT_GND = 7 + }; + muxout_t muxout; + enum phase_detector_polarity_t { + PHASE_DETECTOR_POLARITY_NEGATIVE = 0, + PHASE_DETECTOR_POLARITY_POSITIVE = 1 + }; + phase_detector_polarity_t phase_detector_polarity; + enum charge_pump_mode_t { + CHARGE_PUMP_NORMAL = 0, + CHARGE_PUMP_TRISTATE = 1 + }; + charge_pump_mode_t charge_pump_mode; + enum fastlock_mode_t { + FASTLOCK_MODE_DISABLED = 0, + FASTLOCK_MODE_1 = 1, + FASTLOCK_MODE_2 = 2 + }; + fastlock_mode_t fastlock_mode; + enum timer_counter_control_t { + TIMEOUT_3CYC = 0, + TIMEOUT_7CYC = 1, + TIMEOUT_11CYC = 2, + TIMEOUT_15CYC = 3, + TIMEOUT_19CYC = 4, + TIMEOUT_23CYC = 5, + TIMEOUT_27CYC = 6, + TIMEOUT_31CYC = 7, + TIMEOUT_35CYC = 8, + TIMEOUT_39CYC = 9, + TIMEOUT_43CYC = 10, + TIMEOUT_47CYC = 11, + TIMEOUT_51CYC = 12, + TIMEOUT_55CYC = 13, + TIMEOUT_59CYC = 14, + TIMEOUT_63CYC = 15, + }; + timer_counter_control_t timer_counter_control; +}; + + +class adf4001_ctrl { +public: + + adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno); + void set_lock_to_ext_ref(bool external); + +private: + spi_core_3000::sptr spi_iface; + int slaveno; + spi_config_t spi_config; + adf4001_regs_t adf4001_regs; + + void program_regs(void); + void write_reg(boost::uint8_t addr); +}; + +}} + +#endif diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 1f9cb84b3..6111efea9 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -411,8 +411,8 @@ public: } byte_vector_t read_eeprom( - boost::uint8_t addr, - boost::uint8_t offset, + boost::uint16_t addr, + boost::uint16_t offset, size_t num_bytes ){ this->write_i2c(addr, byte_vector_t(1, offset)); @@ -432,7 +432,7 @@ public: static const bool iface_debug = false; static const size_t max_i2c_data_bytes = 64; - void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) + void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); @@ -442,7 +442,7 @@ public: uhd::runtime_error("USRP: failed i2c write"); } - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes) { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); diff --git a/host/lib/usrp/common/recv_packet_demuxer.cpp b/host/lib/usrp/common/recv_packet_demuxer.cpp index f2cfe3bb0..fe606213c 100644 --- a/host/lib/usrp/common/recv_packet_demuxer.cpp +++ b/host/lib/usrp/common/recv_packet_demuxer.cpp @@ -19,6 +19,8 @@ #include <uhd/utils/msg.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/thread/mutex.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/types/metadata.hpp> #include <queue> #include <deque> #include <vector> @@ -27,6 +29,19 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; +struct recv_pkt_demux_mrb : public managed_recv_buffer +{ +public: + recv_pkt_demux_mrb(void){/*NOP*/} + + void release(void) + { + delete this; + } + + boost::uint32_t buff[10]; +}; + static UHD_INLINE boost::uint32_t extract_sid(managed_recv_buffer::sptr &buff){ //ASSUME that the data is in little endian format return uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); @@ -66,7 +81,20 @@ public: //otherwise queue and try again if (rx_index < _queues.size()) _queues[rx_index].wrapper.push(buff); - else UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl; + else + { + UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl; + recv_pkt_demux_mrb *mrb = new recv_pkt_demux_mrb(); + vrt::if_packet_info_t info; + info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + info.num_payload_words32 = 1; + info.num_payload_bytes = info.num_payload_words32*sizeof(boost::uint32_t); + info.has_sid = true; + info.sid = _sid_base + index; + vrt::if_hdr_pack_le(mrb->buff, info); + mrb->buff[info.num_header_words32] = rx_metadata_t::ERROR_CODE_OVERFLOW; + return mrb->make(mrb, mrb->buff, info.num_packet_words32*sizeof(boost::uint32_t)); + } } } diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp new file mode 100644 index 000000000..4fb6c4604 --- /dev/null +++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp @@ -0,0 +1,127 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/atomic.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <queue> +#include <map> + +namespace uhd{ namespace usrp{ + + struct recv_packet_demuxer_3000 + { + recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport): + _xport(xport) + {/*NOP*/} + + transport::managed_recv_buffer::sptr get_recv_buff(const boost::uint32_t sid, const double timeout) + { + const time_spec_t exit_time = time_spec_t(timeout) + time_spec_t::get_system_time(); + transport::managed_recv_buffer::sptr buff; + buff = _internal_get_recv_buff(sid, timeout); + while (not buff) //loop until timeout + { + const time_spec_t delta = exit_time - time_spec_t::get_system_time(); + const double new_timeout = delta.get_real_secs(); + if (new_timeout < 0.0) break; + buff = _internal_get_recv_buff(sid, new_timeout); + } + return buff; + } + + transport::managed_recv_buffer::sptr _internal_get_recv_buff(const boost::uint32_t sid, const double timeout) + { + transport::managed_recv_buffer::sptr buff; + + //---------------------------------------------------------- + //-- Check the queue to see if we already have a buffer + //---------------------------------------------------------- + { + boost::mutex::scoped_lock l(mutex); + queue_type_t &queue = _queues[sid]; + if (not queue.empty()) + { + buff = queue.front(); + queue.front().reset(); + queue.pop(); + return buff; + } + } + + //---------------------------------------------------------- + //-- Try to claim the transport or wait patiently + //---------------------------------------------------------- + if (_claimed.cas(1, 0)) + { + boost::mutex::scoped_lock l(mutex); + cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); + } + + //---------------------------------------------------------- + //-- Wait on the transport for input buffers + //---------------------------------------------------------- + else + { + buff = _xport->get_recv_buff(timeout); + if (buff) + { + const boost::uint32_t new_sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); + if (new_sid != sid) + { + boost::mutex::scoped_lock l(mutex); + if (_queues.count(new_sid) == 0) UHD_MSG(error) + << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec + << std::endl; + else _queues[new_sid].push(buff); + buff.reset(); + } + } + _claimed.write(0); + cond.notify_all(); + } + return buff; + } + + void realloc_sid(const boost::uint32_t sid) + { + boost::mutex::scoped_lock l(mutex); + while(not _queues[sid].empty()) //allocated and clears if already allocated + { + _queues[sid].pop(); + } + } + + typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t; + std::map<boost::uint32_t, queue_type_t> _queues; + transport::zero_copy_if::sptr _xport; + uhd::atomic_uint32_t _claimed; + boost::condition_variable cond; + boost::mutex mutex; + }; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */ diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index 3192b0774..f526319bc 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012 Ettus Research LLC +# Copyright 2011-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp @@ -32,4 +33,12 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tx_vita_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp ) diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index cdab70b8d..51c23aa4b 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -104,3 +104,35 @@ private: gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){ return sptr(new gpio_core_200_impl(iface, base, rb_addr)); } + +class gpio_core_200_32wo_impl : public gpio_core_200_32wo{ +public: + gpio_core_200_32wo_impl(wb_iface::sptr iface, const size_t base): + _iface(iface), _base(base) + { + _iface->poke32(REG_GPIO_DDR, 0xffffffff); + } + + void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ + if (atr == dboard_iface::ATR_REG_IDLE) _iface->poke32(REG_GPIO_IDLE, value); + if (atr == dboard_iface::ATR_REG_TX_ONLY) _iface->poke32(REG_GPIO_TX_ONLY, value); + if (atr == dboard_iface::ATR_REG_RX_ONLY) _iface->poke32(REG_GPIO_RX_ONLY, value); + if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value); + } + + void set_all_regs(const boost::uint32_t value){ + this->set_atr_reg(dboard_iface::ATR_REG_IDLE, value); + this->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, value); + this->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, value); + this->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value); + } + +private: + wb_iface::sptr _iface; + const size_t _base; + +}; + +gpio_core_200_32wo::sptr gpio_core_200_32wo::make(wb_iface::sptr iface, const size_t base){ + return sptr(new gpio_core_200_32wo_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 278575874..a3edf5454 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -49,4 +49,18 @@ public: }; +//! Simple wrapper for 32 bit write only +class gpio_core_200_32wo : boost::noncopyable{ +public: + typedef boost::shared_ptr<gpio_core_200_32wo> sptr; + + typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; + + static sptr make(wb_iface::sptr iface, const size_t); + + virtual void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value) = 0; + + virtual void set_all_regs(const boost::uint32_t value) = 0; +}; + #endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/i2c_core_100.cpp b/host/lib/usrp/cores/i2c_core_100.cpp index ceeb3f518..9e8a226f2 100644 --- a/host/lib/usrp/cores/i2c_core_100.cpp +++ b/host/lib/usrp/cores/i2c_core_100.cpp @@ -70,7 +70,7 @@ public: } void write_i2c( - boost::uint8_t addr, + boost::uint16_t addr, const byte_vector_t &bytes ){ _iface->poke16(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) @@ -93,7 +93,7 @@ public: } byte_vector_t read_i2c( - boost::uint8_t addr, + boost::uint16_t addr, size_t num_bytes ){ byte_vector_t bytes; diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.cpp b/host/lib/usrp/cores/i2c_core_100_wb32.cpp new file mode 100644 index 000000000..df6e6ff72 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100_wb32.cpp @@ -0,0 +1,152 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "i2c_core_100_wb32.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define REG_I2C_PRESCALER_LO _base + 0 +#define REG_I2C_PRESCALER_HI _base + 4 +#define REG_I2C_CTRL _base + 8 +#define REG_I2C_DATA _base + 12 +#define REG_I2C_CMD_STATUS _base + 16 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +using namespace uhd; + +class i2c_core_100_wb32_wb32_impl : public i2c_core_100_wb32{ +public: + i2c_core_100_wb32_wb32_impl(wb_iface::sptr iface, const size_t base): + _iface(iface), _base(base) + { + //init I2C FPGA interface. + _iface->poke32(REG_I2C_CTRL, 0x0000); + _iface->poke32(REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core + } + + void set_clock_rate(const double rate) + { + static const boost::uint32_t i2c_datarate = 400000; + boost::uint16_t prescaler = rate / (i2c_datarate*5) - 1; + _iface->poke32(REG_I2C_PRESCALER_LO, prescaler & 0xFF); + _iface->poke32(REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); + } + + void write_i2c( + boost::uint16_t addr, + const byte_vector_t &bytes + ){ + _iface->poke32(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if (not wait_chk_ack()) { + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + + for (size_t i = 0; i < bytes.size(); i++) { + _iface->poke32(REG_I2C_DATA, bytes[i]); + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c( + boost::uint16_t addr, + size_t num_bytes + ){ + byte_vector_t bytes; + if (num_bytes == 0) return bytes; + + while (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_BUSY){ + /* NOP */ + } + + _iface->poke32(REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if (not wait_chk_ack()) { + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_STOP); + } + for (size_t i = 0; i < num_bytes; i++) { + _iface->poke32(REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait(); + bytes.push_back(boost::uint8_t(_iface->peek32(REG_I2C_DATA))); + } + return bytes; + } + + //override read_eeprom so we can write once, read all N bytes + //the default implementation calls read i2c once per byte + byte_vector_t read_eeprom(boost::uint16_t addr, boost::uint16_t offset, size_t num_bytes) + { + this->write_i2c(addr, byte_vector_t(1, offset)); + return this->read_i2c(addr, num_bytes); + } + +private: + void i2c_wait(void) { + for (size_t i = 0; i < 10; i++) + { + if ((_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_TIP) == 0) return; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + UHD_MSG(error) << "i2c_core_100_wb32: i2c_wait timeout" << std::endl; + } + + bool wait_chk_ack(void){ + i2c_wait(); + return (_iface->peek32(REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; + } + + wb_iface::sptr _iface; + const size_t _base; +}; + +i2c_core_100_wb32::sptr i2c_core_100_wb32::make(wb_iface::sptr iface, const size_t base) +{ + return sptr(new i2c_core_100_wb32_wb32_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/usrp/cores/i2c_core_100_wb32.hpp new file mode 100644 index 000000000..f2ac98292 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100_wb32.hpp @@ -0,0 +1,37 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{ +public: + typedef boost::shared_ptr<i2c_core_100_wb32> sptr; + + //! makes a new i2c core from iface and slave base + static sptr make(wb_iface::sptr iface, const size_t base); + + virtual void set_clock_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP */ diff --git a/host/lib/usrp/cores/i2c_core_200.cpp b/host/lib/usrp/cores/i2c_core_200.cpp index 1b882c54a..6010ac5a2 100644 --- a/host/lib/usrp/cores/i2c_core_200.cpp +++ b/host/lib/usrp/cores/i2c_core_200.cpp @@ -73,7 +73,7 @@ public: } void write_i2c( - boost::uint8_t addr, + boost::uint16_t addr, const byte_vector_t &bytes ){ this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); //addr and read bit (0) @@ -96,7 +96,7 @@ public: } byte_vector_t read_i2c( - boost::uint8_t addr, + boost::uint16_t addr, size_t num_bytes ){ byte_vector_t bytes; diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp new file mode 100644 index 000000000..616903920 --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -0,0 +1,312 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "radio_ctrl_core_3000.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> +#include <queue> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const size_t SR_READBACK = 32; + +class radio_ctrl_core_3000_impl : public radio_ctrl_core_3000 +{ +public: + + radio_ctrl_core_3000_impl( + vrt::if_packet_info_t::link_type_t link_type, + uhd::transport::zero_copy_if::sptr ctrl_xport, + uhd::transport::zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, + const std::string &name + ): + _link_type(link_type), + _packet_type(vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), + _bige(link_type == vrt::if_packet_info_t::LINK_TYPE_VRLP), + _ctrl_xport(ctrl_xport), + _resp_xport(resp_xport), + _sid(sid), + _name(name), + _seq_out(0), + _timeout(ACK_TIMEOUT), + _resp_queue(128/*max response msgs*/), + _resp_queue_size(_resp_xport? _resp_xport->get_num_recv_frames() : 3) + { + UHD_LOG << "radio_ctrl_core_3000_impl() " << _name << std::endl; + if (resp_xport) + { + while (resp_xport->get_recv_buff(0.0)){} //flush + } + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + } + + ~radio_ctrl_core_3000_impl(void) + { + UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl; + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + _async_task.reset(); //now its ok to release the task + ) + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(const wb_addr_type addr, const boost::uint32_t data) + { + boost::mutex::scoped_lock lock(_mutex); + UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << " data 0x" << data << std::dec << std::endl; + + this->send_pkt(addr/4, data); + this->wait_for_ack(false); + } + + boost::uint32_t peek32(const wb_addr_type addr) + { + boost::mutex::scoped_lock lock(_mutex); + UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; + + this->send_pkt(SR_READBACK, addr/8); + this->wait_for_ack(false); + + this->send_pkt(0); + const boost::uint64_t res = this->wait_for_ack(true); + const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff); + const boost::uint32_t hi = boost::uint32_t(res >> 32); + return ((addr/4) & 0x1)? hi : lo; + } + + boost::uint64_t peek64(const wb_addr_type addr) + { + boost::mutex::scoped_lock lock(_mutex); + UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; + + this->send_pkt(SR_READBACK, addr/8); + this->wait_for_ack(false); + + this->send_pkt(0); + return this->wait_for_ack(true); + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time) + { + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate) + { + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0) + { + managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.link_type = _link_type; + packet_info.packet_type = _packet_type; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = _seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.sid = _sid; + packet_info.has_sid = true; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + if (_bige) vrt::if_hdr_pack_be(pkt, packet_info); + else vrt::if_hdr_pack_le(pkt, packet_info); + + //load payload + pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr); + pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data); + //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data; + + //send the buffer over the interface + _outstanding_seqs.push(_seq_out); + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + + _seq_out++; //inc seq for next call + } + + UHD_INLINE boost::uint64_t wait_for_ack(const bool readback) + { + while (readback or (_outstanding_seqs.size() >= _resp_queue_size)) + { + UHD_LOGV(always) << _name << " wait_for_ack: " << "readback = " << readback << " outstanding_seqs.size() " << _outstanding_seqs.size() << std::endl; + + //get seq to ack from outstanding packets list + UHD_ASSERT_THROW(not _outstanding_seqs.empty()); + const size_t seq_to_ack = _outstanding_seqs.front(); + _outstanding_seqs.pop(); + + //parse the packet + vrt::if_packet_info_t packet_info; + resp_buff_type resp_buff; + boost::uint32_t const *pkt = NULL; + managed_recv_buffer::sptr buff; + + //get buffer from response endpoint - or die in timeout + if (_resp_xport) + { + buff = _resp_xport->get_recv_buff(_timeout); + try + { + UHD_ASSERT_THROW(bool(buff)); + UHD_ASSERT_THROW(bool(buff->size())); + } + catch(const std::exception &ex) + { + throw uhd::io_error(str(boost::format("Radio ctrl (%s) no response packet - %s") % _name % ex.what())); + } + pkt = buff->cast<const boost::uint32_t *>(); + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + } + + //get buffer from response endpoint - or die in timeout + else + { + UHD_ASSERT_THROW(_resp_queue.pop_with_timed_wait(resp_buff, _timeout)); + pkt = resp_buff.data; + packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t); + } + + //parse the buffer + try + { + packet_info.link_type = _link_type; + if (_bige) vrt::if_hdr_unpack_be(pkt, packet_info); + else vrt::if_hdr_unpack_le(pkt, packet_info); + } + catch(const std::exception &ex) + { + UHD_MSG(error) << "Radio ctrl bad VITA packet: " << ex.what() << std::endl; + UHD_VAR(buff->size()); + UHD_MSG(status) << std::hex << pkt[0] << std::dec << std::endl; + UHD_MSG(status) << std::hex << pkt[1] << std::dec << std::endl; + UHD_MSG(status) << std::hex << pkt[2] << std::dec << std::endl; + UHD_MSG(status) << std::hex << pkt[3] << std::dec << std::endl; + } + + //check the buffer + try + { + UHD_ASSERT_THROW(packet_info.has_sid); + UHD_ASSERT_THROW(packet_info.sid == boost::uint32_t((_sid >> 16) | (_sid << 16))); + UHD_ASSERT_THROW(packet_info.packet_count == (seq_to_ack & 0xfff)); + UHD_ASSERT_THROW(packet_info.num_payload_words32 == 2); + UHD_ASSERT_THROW(packet_info.packet_type == _packet_type); + } + catch(const std::exception &ex) + { + throw uhd::io_error(str(boost::format("Radio ctrl (%s) packet parse error - %s") % _name % ex.what())); + } + + //return the readback value + if (readback and _outstanding_seqs.empty()) + { + const boost::uint64_t hi = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+0]) : uhd::wtohx(pkt[packet_info.num_header_words32+0]); + const boost::uint64_t lo = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+1]) : uhd::wtohx(pkt[packet_info.num_header_words32+1]); + return ((hi << 32) | lo); + } + } + return 0; + } + + void push_response(const boost::uint32_t *buff) + { + resp_buff_type resp_buff; + std::memcpy(resp_buff.data, buff, sizeof(resp_buff)); + _resp_queue.push_with_haste(resp_buff); + } + + void hold_task(boost::shared_ptr<void> task) + { + _async_task = task; + } + + const vrt::if_packet_info_t::link_type_t _link_type; + const vrt::if_packet_info_t::packet_type_t _packet_type; + const bool _bige; + const uhd::transport::zero_copy_if::sptr _ctrl_xport; + const uhd::transport::zero_copy_if::sptr _resp_xport; + boost::shared_ptr<void> _async_task; + const boost::uint32_t _sid; + const std::string _name; + boost::mutex _mutex; + size_t _seq_out; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + std::queue<size_t> _outstanding_seqs; + struct resp_buff_type + { + boost::uint32_t data[8]; + }; + bounded_buffer<resp_buff_type> _resp_queue; + const size_t _resp_queue_size; +}; + + +radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make( + vrt::if_packet_info_t::link_type_t link_type, + zero_copy_if::sptr ctrl_xport, + zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, + const std::string &name +) +{ + return sptr(new radio_ctrl_core_3000_impl(link_type, ctrl_xport, resp_xport, sid, name)); +} diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp new file mode 100644 index 000000000..6ef484296 --- /dev/null +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP +#define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + +/*! + * Provide access to peek, poke for the radio ctrl module + */ +class radio_ctrl_core_3000 : public wb_iface +{ +public: + typedef boost::shared_ptr<radio_ctrl_core_3000> sptr; + + //! Make a new control object + static sptr make( + uhd::transport::vrt::if_packet_info_t::link_type_t link_type, + uhd::transport::zero_copy_if::sptr ctrl_xport, + uhd::transport::zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, + const std::string &name = "0" + ); + + //! Hold a ref to a task thats feeding push response + virtual void hold_task(boost::shared_ptr<void> task) = 0; + + //! Push a response externall (resp_xport is NULL) + virtual void push_response(const boost::uint32_t *buff) = 0; + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp new file mode 100644 index 000000000..36d9af5bc --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -0,0 +1,204 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "rx_dsp_core_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/thread/thread.hpp> //thread sleep +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <algorithm> +#include <cmath> + +#define REG_DSP_RX_FREQ _dsp_base + 0 +#define REG_DSP_RX_SCALE_IQ _dsp_base + 4 +#define REG_DSP_RX_DECIM _dsp_base + 8 +#define REG_DSP_RX_MUX _dsp_base + 12 + +#define FLAG_DSP_RX_MUX_SWAP_IQ (1 << 0) +#define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) + +template <class T> T ceil_log2(T num){ + return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class rx_dsp_core_3000_impl : public rx_dsp_core_3000{ +public: + rx_dsp_core_3000_impl( + wb_iface::sptr iface, + const size_t dsp_base + ): + _iface(iface), _dsp_base(dsp_base) + { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + this->set_tick_rate(1.0); + } + + ~rx_dsp_core_3000_impl(void) + { + UHD_SAFE_CALL + ( + //NOP + ) + } + + void set_mux(const std::string &mode, const bool fe_swapped){ + static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of + ("IQ", 0) + ("QI", FLAG_DSP_RX_MUX_SWAP_IQ) + ("I", FLAG_DSP_RX_MUX_REAL_MODE) + ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE) + ; + _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] ^ (fe_swapped? FLAG_DSP_RX_MUX_SWAP_IQ : 0)); + } + + void set_tick_rate(const double rate){ + _tick_rate = rate; + } + + void set_link_rate(const double rate){ + //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s + _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) + } + + uhd::meta_range_t get_host_rates(void){ + meta_range_t range; + for (int rate = 512; rate > 256; rate -= 4){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 256; rate > 128; rate -= 2){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ + range.push_back(range_t(_tick_rate/rate)); + } + return range; + } + + double set_host_rate(const double rate){ + const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); + size_t decim = decim_rate; + + //determine which half-band filters are activated + int hb0 = 0, hb1 = 0; + if (decim % 2 == 0){ + hb0 = 1; + decim /= 2; + } + if (decim % 2 == 0){ + hb1 = 1; + decim /= 2; + } + + _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + + if (decim > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested decimation is odd; the user should expect CIC rolloff.\n" + "Select an even decimation to ensure that a halfband filter is enabled.\n" + "decimation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % decim_rate % (_tick_rate/1e6) % (rate/1e6); + } + + // Calculate CIC decimation (i.e., without halfband decimators) + // Calculate closest multiplier constant to reverse gain absent scale multipliers + const double rate_pow = std::pow(double(decim & 0xff), 4); + _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + this->update_scalar(); + + return _tick_rate/decim_rate; + } + + void update_scalar(void){ + const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); + const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small + _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); + } + + double get_scaling_adjustment(void){ + return _fxpt_scalar_correction*_host_extra_scaling/32767.; + } + + double set_freq(const double freq_){ + //correct for outside of rate (wrap around) + double freq = std::fmod(freq_, _tick_rate); + if (std::abs(freq) > _tick_rate/2.0) + freq -= boost::math::sign(freq)*_tick_rate; + + //calculate the freq register word (signed) + UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); + static const double scale_factor = std::pow(2.0, 32); + const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + + //update the actual frequency + const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + + _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); + + return actual_freq; + } + + uhd::meta_range_t get_freq_range(void){ + return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); + } + + void setup(const uhd::stream_args_t &stream_args){ + + //unsigned format_word = 0; + if (stream_args.otw_format == "sc16"){ + //format_word = 0; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; + } + /* + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = peak*256; + _dsp_extra_scaling = peak*256; + } + */ + else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format); + + _host_extra_scaling *= stream_args.args.cast<double>("fullscale", 1.0); + + this->update_scalar(); + } + +private: + wb_iface::sptr _iface; + const size_t _dsp_base; + double _tick_rate, _link_rate; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +}; + +rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base) +{ + return sptr(new rx_dsp_core_3000_impl(iface, dsp_base)); +} diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp new file mode 100644 index 000000000..23b12b9b7 --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class rx_dsp_core_3000 : boost::noncopyable{ +public: + typedef boost::shared_ptr<rx_dsp_core_3000> sptr; + + static sptr make( + wb_iface::sptr iface, + const size_t dsp_base + ); + + virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0; + + virtual void set_tick_rate(const double rate) = 0; + + virtual void set_link_rate(const double rate) = 0; + + virtual double set_host_rate(const double rate) = 0; + + virtual uhd::meta_range_t get_host_rates(void) = 0; + + virtual double get_scaling_adjustment(void) = 0; + + virtual uhd::meta_range_t get_freq_range(void) = 0; + + virtual double set_freq(const double freq) = 0; + + virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp new file mode 100644 index 000000000..aad137ea3 --- /dev/null +++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp @@ -0,0 +1,153 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "rx_vita_core_3000.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/tuple/tuple.hpp> + +#define REG_FRAMER_MAXLEN _base + 4*4 + 0 +#define REG_FRAMER_SID _base + 4*4 + 4 + +#define REG_CTRL_CMD _base + 0 +#define REG_CTRL_TIME_HI _base + 4 +#define REG_CTRL_TIME_LO _base + 8 + +#define REG_FC_WINDOW _base + 6*4 + 0 +#define REG_FC_ENABLE _base + 6*4 + 4 + +using namespace uhd; + +struct rx_vita_core_3000_impl : rx_vita_core_3000 +{ + rx_vita_core_3000_impl( + wb_iface::sptr iface, + const size_t base + ): + _iface(iface), + _base(base), + _continuous_streaming(false), + _is_setup(false) + { + this->set_tick_rate(1); //init to non zero + this->set_nsamps_per_packet(100); //init to non zero + this->clear(); + } + + ~rx_vita_core_3000_impl(void) + { + UHD_SAFE_CALL + ( + this->clear(); + ) + } + + void configure_flow_control(const size_t window_size) + { + _iface->poke32(REG_FC_WINDOW, window_size-1); + _iface->poke32(REG_FC_ENABLE, window_size?1:0); + } + + void clear(void) + { + this->configure_flow_control(0); //disable fc + } + + void set_nsamps_per_packet(const size_t nsamps) + { + _iface->poke32(REG_FRAMER_MAXLEN, nsamps); + } + + void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) + { + if (not _is_setup) + { + //UHD_MSG(warning) << "rx vita core 3000 issue stream command - not setup yet!"; + return; + } + UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); + _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + + //setup the mode to instruction flags + typedef boost::tuple<bool, bool, bool, bool> inst_t; + static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of + //reload, chain, samps, stop + (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false, false)) + (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false, true)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true, false)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true, false)) + ; + + //setup the instruction flag values + bool inst_reload, inst_chain, inst_samps, inst_stop; + boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; + + //calculate the word from flags and length + boost::uint32_t cmd_word = 0; + cmd_word |= boost::uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; + cmd_word |= boost::uint32_t((inst_chain)? 1 : 0) << 30; + cmd_word |= boost::uint32_t((inst_reload)? 1 : 0) << 29; + cmd_word |= boost::uint32_t((inst_stop)? 1 : 0) << 28; + cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); + + //issue the stream command + _iface->poke32(REG_CTRL_CMD, cmd_word); + const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate); + _iface->poke32(REG_CTRL_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //latches the command + } + + void set_tick_rate(const double rate) + { + _tick_rate = rate; + } + + void set_sid(const boost::uint32_t sid) + { + _iface->poke32(REG_FRAMER_SID, sid); + } + + void handle_overflow(void) + { + if (_continuous_streaming) this->issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + } + + void setup(const uhd::stream_args_t &) + { + _is_setup = true; + } + + bool in_continuous_streaming_mode(void) + { + return _continuous_streaming; + } + + wb_iface::sptr _iface; + const size_t _base; + double _tick_rate; + bool _continuous_streaming; + bool _is_setup; +}; + +rx_vita_core_3000::sptr rx_vita_core_3000::make( + wb_iface::sptr iface, + const size_t base +) +{ + return rx_vita_core_3000::sptr(new rx_vita_core_3000_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/usrp/cores/rx_vita_core_3000.hpp new file mode 100644 index 000000000..b011a7388 --- /dev/null +++ b/host/lib/usrp/cores/rx_vita_core_3000.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class rx_vita_core_3000 : boost::noncopyable +{ +public: + typedef boost::shared_ptr<rx_vita_core_3000> sptr; + + static sptr make( + wb_iface::sptr iface, + const size_t base + ); + + virtual void clear(void) = 0; + + virtual void set_nsamps_per_packet(const size_t nsamps) = 0; + + virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0; + + virtual void set_tick_rate(const double rate) = 0; + + virtual void set_sid(const boost::uint32_t sid) = 0; + + virtual void handle_overflow(void) = 0; + + virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void configure_flow_control(const size_t window_size) = 0; + + virtual bool in_continuous_streaming_mode(void) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp new file mode 100644 index 000000000..b7503064a --- /dev/null +++ b/host/lib/usrp/cores/spi_core_3000.cpp @@ -0,0 +1,96 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "spi_core_3000.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define SPI_DIV _base + 0 +#define SPI_CTRL _base + 4 +#define SPI_DATA _base + 8 + +using namespace uhd; + +class spi_core_3000_impl : public spi_core_3000 +{ +public: + spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): + _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0) + { + this->set_divider(30); + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3f) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word) + { + _iface->poke32(SPI_DIV, _div); + _iface->poke32(SPI_CTRL, ctrl_word); + _ctrl_word_cache = ctrl_word; + } + + //send data word + _iface->poke32(SPI_DATA, data_out); + + //conditional readback + if (readback) + { + return _iface->peek32(_readback); + } + + return 0; + } + + void set_divider(const double div) + { + _div = size_t((div/2) - 0.5); + } + +private: + + wb_iface::sptr _iface; + const size_t _base; + const size_t _readback; + boost::uint32_t _ctrl_word_cache; + boost::mutex _mutex; + size_t _div; +}; + +spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback) +{ + return sptr(new spi_core_3000_impl(iface, base, readback)); +} + diff --git a/host/lib/usrp/cores/spi_core_3000.hpp b/host/lib/usrp/cores/spi_core_3000.hpp new file mode 100644 index 000000000..995ad59db --- /dev/null +++ b/host/lib/usrp/cores/spi_core_3000.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class spi_core_3000 : boost::noncopyable, public uhd::spi_iface +{ +public: + typedef boost::shared_ptr<spi_core_3000> sptr; + + //! makes a new spi core from iface and slave base + static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); + + //! Set the spi clock divider to something usable + virtual void set_divider(const double div) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/time_core_3000.cpp b/host/lib/usrp/cores/time_core_3000.cpp new file mode 100644 index 000000000..45ff55271 --- /dev/null +++ b/host/lib/usrp/cores/time_core_3000.cpp @@ -0,0 +1,118 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "time_core_3000.hpp" +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> + +#define REG_TIME_HI _base + 0 +#define REG_TIME_LO _base + 4 +#define REG_TIME_CTRL _base + 8 + +#define CTRL_LATCH_TIME_PPS (1 << 1) +#define CTRL_LATCH_TIME_NOW (1 << 0) + +using namespace uhd; + +struct time_core_3000_impl : time_core_3000 +{ + time_core_3000_impl( + wb_iface::sptr iface, const size_t base, + const readback_bases_type &readback_bases + ): + _iface(iface), + _base(base), + _readback_bases(readback_bases) + { + this->set_tick_rate(1); //init to non zero + } + + ~time_core_3000_impl(void) + { + UHD_SAFE_CALL + ( + //NOP + ) + } + + void set_tick_rate(const double rate) + { + _tick_rate = rate; + } + + void self_test(void) + { + const size_t sleep_millis = 100; + UHD_MSG(status) << "Performing timer loopback test... " << std::flush; + const time_spec_t time0 = this->get_time_now(); + boost::this_thread::sleep(boost::posix_time::milliseconds(sleep_millis)); + const time_spec_t time1 = this->get_time_now(); + const double approx_secs = (time1 - time0).get_real_secs(); + const bool test_fail = (approx_secs > 0.15) or (approx_secs < 0.05); + UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; + + //useful warning for debugging actual rate + const size_t ticks_elapsed = _tick_rate*approx_secs; + const size_t appox_rate = ticks_elapsed/(sleep_millis/1e3); + if (test_fail) UHD_MSG(warning) + << "Expecting clock rate: " << (_tick_rate/1e6) << " MHz\n" + << "Appoximate clock rate: " << (appox_rate/1e6) << " MHz\n" + << std::endl; + } + + uhd::time_spec_t get_time_now(void) + { + const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_now); + return time_spec_t::from_ticks(ticks, _tick_rate); + } + + uhd::time_spec_t get_time_last_pps(void) + { + const boost::uint64_t ticks = _iface->peek64(_readback_bases.rb_pps); + return time_spec_t::from_ticks(ticks, _tick_rate); + } + + void set_time_now(const uhd::time_spec_t &time) + { + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0)); + _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_NOW); + } + + void set_time_next_pps(const uhd::time_spec_t &time) + { + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_TIME_LO, boost::uint32_t(ticks >> 0)); + _iface->poke32(REG_TIME_CTRL, CTRL_LATCH_TIME_PPS); + } + + wb_iface::sptr _iface; + const size_t _base; + const readback_bases_type _readback_bases; + double _tick_rate; +}; + +time_core_3000::sptr time_core_3000::make( + wb_iface::sptr iface, const size_t base, + const readback_bases_type &readback_bases +) +{ + return time_core_3000::sptr(new time_core_3000_impl(iface, base, readback_bases)); +} diff --git a/host/lib/usrp/cores/time_core_3000.hpp b/host/lib/usrp/cores/time_core_3000.hpp new file mode 100644 index 000000000..ffe2f4133 --- /dev/null +++ b/host/lib/usrp/cores/time_core_3000.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class time_core_3000 : boost::noncopyable +{ +public: + typedef boost::shared_ptr<time_core_3000> sptr; + + struct readback_bases_type + { + size_t rb_now; + size_t rb_pps; + }; + + //! makes a new time core from iface and slave base + static sptr make( + wb_iface::sptr iface, const size_t base, + const readback_bases_type &readback_bases + ); + + virtual void self_test(void) = 0; + + virtual void set_tick_rate(const double rate) = 0; + + virtual uhd::time_spec_t get_time_now(void) = 0; + + virtual uhd::time_spec_t get_time_last_pps(void) = 0; + + virtual void set_time_now(const uhd::time_spec_t &time) = 0; + + virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp new file mode 100644 index 000000000..ff4392a13 --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -0,0 +1,181 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "tx_dsp_core_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <algorithm> +#include <cmath> + +#define REG_DSP_TX_FREQ _dsp_base + 0 +#define REG_DSP_TX_SCALE_IQ _dsp_base + 4 +#define REG_DSP_TX_INTERP _dsp_base + 8 + +template <class T> T ceil_log2(T num){ + return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class tx_dsp_core_3000_impl : public tx_dsp_core_3000{ +public: + tx_dsp_core_3000_impl( + wb_iface::sptr iface, + const size_t dsp_base + ): + _iface(iface), _dsp_base(dsp_base) + { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + this->set_tick_rate(1.0); + } + + void set_tick_rate(const double rate){ + _tick_rate = rate; + } + + void set_link_rate(const double rate){ + //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s + _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) + } + + uhd::meta_range_t get_host_rates(void){ + meta_range_t range; + for (int rate = 512; rate > 256; rate -= 4){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 256; rate > 128; rate -= 2){ + range.push_back(range_t(_tick_rate/rate)); + } + for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ + range.push_back(range_t(_tick_rate/rate)); + } + return range; + } + + double set_host_rate(const double rate){ + const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); + size_t interp = interp_rate; + + //determine which half-band filters are activated + int hb0 = 0, hb1 = 0; + if (interp % 2 == 0){ + hb0 = 1; + interp /= 2; + } + if (interp % 2 == 0){ + hb1 = 1; + interp /= 2; + } + + _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff)); + + if (interp > 1 and hb0 == 0 and hb1 == 0) + { + UHD_MSG(warning) << boost::format( + "The requested interpolation is odd; the user should expect CIC rolloff.\n" + "Select an even interpolation to ensure that a halfband filter is enabled.\n" + "interpolation = dsp_rate/samp_rate -> %d = (%f MHz)/(%f MHz)\n" + ) % interp_rate % (_tick_rate/1e6) % (rate/1e6); + } + + // Calculate CIC interpolation (i.e., without halfband interpolators) + // Calculate closest multiplier constant to reverse gain absent scale multipliers + const double rate_pow = std::pow(double(interp & 0xff), 3); + _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + this->update_scalar(); + + return _tick_rate/interp_rate; + } + + void update_scalar(void){ + const double factor = 1.0 + std::max(ceil_log2(_scaling_adjustment), 0.0); + const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling/factor; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar*factor; //should be small + _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar); + } + + double get_scaling_adjustment(void){ + return _fxpt_scalar_correction*_host_extra_scaling*32767.; + } + + double set_freq(const double freq_){ + //correct for outside of rate (wrap around) + double freq = std::fmod(freq_, _tick_rate); + if (std::abs(freq) > _tick_rate/2.0) + freq -= boost::math::sign(freq)*_tick_rate; + + //calculate the freq register word (signed) + UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); + static const double scale_factor = std::pow(2.0, 32); + const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + + //update the actual frequency + const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + + _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); + + return actual_freq; + } + + uhd::meta_range_t get_freq_range(void){ + return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); + } + + void setup(const uhd::stream_args_t &stream_args){ + + //unsigned format_word = 0; + if (stream_args.otw_format == "sc16"){ + //format_word = 0; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; + } + /* + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = 1.0/peak/256; + _dsp_extra_scaling = 1.0/peak; + } + else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format); + */ + + _host_extra_scaling /= stream_args.args.cast<double>("fullscale", 1.0); + + this->update_scalar(); + } + +private: + wb_iface::sptr _iface; + const size_t _dsp_base; + double _tick_rate, _link_rate; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +}; + +tx_dsp_core_3000::sptr tx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base) +{ + return sptr(new tx_dsp_core_3000_impl(iface, dsp_base)); +} diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp new file mode 100644 index 000000000..eb5ffaf0f --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class tx_dsp_core_3000 : boost::noncopyable{ +public: + typedef boost::shared_ptr<tx_dsp_core_3000> sptr; + + static sptr make( + wb_iface::sptr iface, + const size_t dsp_base + ); + + virtual void set_tick_rate(const double rate) = 0; + + virtual void set_link_rate(const double rate) = 0; + + virtual double set_host_rate(const double rate) = 0; + + virtual uhd::meta_range_t get_host_rates(void) = 0; + + virtual double get_scaling_adjustment(void) = 0; + + virtual uhd::meta_range_t get_freq_range(void) = 0; + + virtual double set_freq(const double freq) = 0; + + virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp new file mode 100644 index 000000000..38eb6afb5 --- /dev/null +++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp @@ -0,0 +1,107 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "tx_vita_core_3000.hpp" +#include <uhd/utils/safe_call.hpp> + +#define REG_CTRL_ERROR_POLICY _base + 0 +#define REG_DEFRAMER_CYCLE_FC_UPS _base + 2*4 + 0 +#define REG_DEFRAMER_PACKET_FC_UPS _base + 2*4 + 4 + +using namespace uhd; + +struct tx_vita_core_3000_impl : tx_vita_core_3000 +{ + tx_vita_core_3000_impl( + wb_iface::sptr iface, + const size_t base + ): + _iface(iface), + _base(base) + { + this->set_tick_rate(1); //init to non zero + this->set_underflow_policy("next_packet"); + this->clear(); + } + + ~tx_vita_core_3000_impl(void) + { + UHD_SAFE_CALL + ( + this->clear(); + ) + } + + void clear(void) + { + this->configure_flow_control(0, 0); + this->set_underflow_policy(_policy); //clears the seq + } + + void set_tick_rate(const double rate) + { + _tick_rate = rate; + } + + void set_underflow_policy(const std::string &policy) + { + if (policy == "next_packet") + { + _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 1)); + } + else if (policy == "next_burst") + { + _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 2)); + } + else if (policy == "wait") + { + _iface->poke32(REG_CTRL_ERROR_POLICY, (1 << 0)); + } + else throw uhd::value_error("USRP TX cannot handle requested underflow policy: " + policy); + _policy = policy; + } + + void setup(const uhd::stream_args_t &stream_args) + { + if (stream_args.args.has_key("underflow_policy")) + { + this->set_underflow_policy(stream_args.args["underflow_policy"]); + } + } + + void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) + { + if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0); + else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff)); + + if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0); + else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff)); + } + + wb_iface::sptr _iface; + const size_t _base; + double _tick_rate; + std::string _policy; +}; + +tx_vita_core_3000::sptr tx_vita_core_3000::make( + wb_iface::sptr iface, + const size_t base +) +{ + return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp new file mode 100644 index 000000000..2070936ce --- /dev/null +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -0,0 +1,49 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class tx_vita_core_3000 : boost::noncopyable +{ +public: + typedef boost::shared_ptr<tx_vita_core_3000> sptr; + + static sptr make( + wb_iface::sptr iface, + const size_t base + ); + + virtual void clear(void) = 0; + + virtual void set_tick_rate(const double rate) = 0; + + virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/wb_iface.cpp b/host/lib/usrp/cores/wb_iface.cpp new file mode 100644 index 000000000..9aa6d18d4 --- /dev/null +++ b/host/lib/usrp/cores/wb_iface.cpp @@ -0,0 +1,51 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "wb_iface.hpp" +#include <uhd/exception.hpp> + +using namespace uhd; + +void wb_iface::poke64(const wb_iface::wb_addr_type, const boost::uint64_t) +{ + throw uhd::not_implemented_error("poke64 not implemented"); +} + +boost::uint64_t wb_iface::peek64(const wb_iface::wb_addr_type) +{ + throw uhd::not_implemented_error("peek64 not implemented"); +} + +void wb_iface::poke32(const wb_iface::wb_addr_type, const boost::uint32_t) +{ + throw uhd::not_implemented_error("poke32 not implemented"); +} + +boost::uint32_t wb_iface::peek32(const wb_iface::wb_addr_type) +{ + throw uhd::not_implemented_error("peek32 not implemented"); +} + +void wb_iface::poke16(const wb_iface::wb_addr_type, const boost::uint16_t) +{ + throw uhd::not_implemented_error("poke16 not implemented"); +} + +boost::uint16_t wb_iface::peek16(const wb_iface::wb_addr_type) +{ + throw uhd::not_implemented_error("peek16 not implemented"); +} diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/lib/usrp/cores/wb_iface.hpp index 982594b21..197788180 100644 --- a/host/lib/usrp/cores/wb_iface.hpp +++ b/host/lib/usrp/cores/wb_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,38 +22,53 @@ #include <boost/cstdint.hpp> #include <boost/shared_ptr.hpp> -class wb_iface{ +class /*UHD_API*/ wb_iface +{ public: typedef boost::shared_ptr<wb_iface> sptr; typedef boost::uint32_t wb_addr_type; /*! + * Write a register (64 bits) + * \param addr the address + * \param data the 64bit data + */ + virtual void poke64(const wb_addr_type addr, const boost::uint64_t data); + + /*! + * Read a register (64 bits) + * \param addr the address + * \return the 64bit data + */ + virtual boost::uint64_t peek64(const wb_addr_type addr); + + /*! * Write a register (32 bits) * \param addr the address * \param data the 32bit data */ - virtual void poke32(wb_addr_type addr, boost::uint32_t data) = 0; + virtual void poke32(const wb_addr_type addr, const boost::uint32_t data); /*! * Read a register (32 bits) * \param addr the address * \return the 32bit data */ - virtual boost::uint32_t peek32(wb_addr_type addr) = 0; + virtual boost::uint32_t peek32(const wb_addr_type addr); /*! * Write a register (16 bits) * \param addr the address * \param data the 16bit data */ - virtual void poke16(wb_addr_type addr, boost::uint16_t data) = 0; + virtual void poke16(const wb_addr_type addr, const boost::uint16_t data); /*! * Read a register (16 bits) * \param addr the address * \return the 16bit data */ - virtual boost::uint16_t peek16(wb_addr_type addr) = 0; + virtual boost::uint16_t peek16(const wb_addr_type addr); }; diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index b000c7f33..9e8653608 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -26,6 +26,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version3.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version4.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/db_cbx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp ${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp new file mode 100644 index 000000000..04399e64e --- /dev/null +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -0,0 +1,212 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + + +#include "max2870_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) { + //register the handle to our base CBX class + self_base = _self_sbx_xcvr; +} + + +sbx_xcvr::cbx::~cbx(void){ + /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { + UHD_LOGV(often) << boost::format( + "CBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //clip the input + target_freq = cbx_freq_range.clip(target_freq); + + //map mode setting to valid integer divider (N) values + static const uhd::range_t int_n_mode_div_range(16,4095,1); + static const uhd::range_t frac_n_mode_div_range(19,4091,1); + + //map rf divider select output dividers to enums + static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of + (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1) + (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2) + (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4) + (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8) + (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16) + (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32) + (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64) + (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128) + ; + + double actual_freq, pfd_freq; + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + max2870_regs_t::int_n_mode_t int_n_mode; + int R=0, BS=0, N=0, FRAC=0, MOD=4095; + int RFdiv = 1; + max2870_regs_t::reference_divide_by_2_t T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + max2870_regs_t::reference_doubler_t D = max2870_regs_t::REFERENCE_DOUBLER_DISABLED; + + //Reference doubler for 50% duty cycle + // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 + //NOTE: MAX2870 goes down to 10MHz ref vs. 12.5MHz on ADF4351 + if(ref_freq <= 10.0e6) D = max2870_regs_t::REFERENCE_DOUBLER_ENABLED; + + //increase RF divider until acceptable VCO frequency + double vco_freq = target_freq; + //NOTE: MIN freq for MAX2870 VCO is 3GHz vs. 2.2GHz on ADF4351 + while (vco_freq < 3e9) { + vco_freq *= 2; + RFdiv *= 2; + } + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exits when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(1+D)/(R*(1+T)); + + //keep the PFD frequency at or below 25MHz + if (pfd_freq > 25e6) continue; + + //ignore fractional part of tuning + N = int(vco_freq/pfd_freq); + + //Fractional-N calculation + FRAC = int((vco_freq/pfd_freq - N)*MOD); + + //are we in int-N or frac-N mode? + int_n_mode = (FRAC == 0) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N; + + //keep N within int divider requirements + if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) { + if(N < int_n_mode_div_range.start()) continue; + if(N > int_n_mode_div_range.stop()) continue; + } else { + if(N < frac_n_mode_div_range.start()) continue; + if(N > frac_n_mode_div_range.stop()) continue; + } + + //keep pfd freq low enough to achieve 50kHz BS clock + BS = std::ceil(pfd_freq / 50e3); + if(BS <= 1023) break; + } + + UHD_ASSERT_THROW(R <= 1023); + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0){ + T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; + R /= 2; + } + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + + UHD_LOGV(often) + << boost::format("CBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl + << boost::format("CBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl + << boost::format("CBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" + ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + + //load the register values + max2870_regs_t regs; + + if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) + regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM; + else + regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; + + //set frac/int CPL mode + max2870_regs_t::cpl_t cpl; + max2870_regs_t::ldf_t ldf; + max2870_regs_t::cpoc_t cpoc; + if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) { + cpl = max2870_regs_t::CPL_DISABLED; + cpoc = max2870_regs_t::CPOC_ENABLED; + ldf = max2870_regs_t::LDF_INT_N; + } else { + cpl = max2870_regs_t::CPL_ENABLED; + ldf = max2870_regs_t::LDF_FRAC_N; + cpoc = max2870_regs_t::CPOC_DISABLED; + } + + regs.frac_12_bit = FRAC; + regs.int_16_bit = N; + regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = (target_freq >= 3.0e9) ? max2870_regs_t::FEEDBACK_SELECT_DIVIDED : max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + regs.r_counter_10_bit = R; + regs.reference_divide_by_2 = T; + regs.reference_doubler = D; + regs.band_select_clock_div = BS; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); + regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + regs.int_n_mode = int_n_mode; + regs.cpl = cpl; + regs.ldf = ldf; + regs.cpoc = cpoc; + + //write the registers + //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) + int addr; + + for(addr=5; addr>=0; addr--){ + UHD_LOGV(often) << boost::format( + "CBX SPI Reg (0x%02x): 0x%08x" + ) % addr % regs.get_reg(addr) << std::endl; + self_base->get_iface()->write_spi( + unit, spi_config_t::EDGE_RISE, + regs.get_reg(addr), 32 + ); + } + + //return the actual frequency + UHD_LOGV(often) << boost::format( + "CBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 728cb17e9..9db29e65a 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -32,6 +32,7 @@ static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){ UHD_STATIC_BLOCK(reg_sbx_dboards){ dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX"); dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4"); + dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX"); } @@ -114,9 +115,15 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ switch(get_rx_id().to_uint16()) { case 0x054: db_actual = sbx_versionx_sptr(new sbx_version3(this)); + freq_range = sbx_freq_range; break; case 0x065: db_actual = sbx_versionx_sptr(new sbx_version4(this)); + freq_range = sbx_freq_range; + break; + case 0x067: + db_actual = sbx_versionx_sptr(new cbx(this)); + freq_range = cbx_freq_range; break; default: /* We didn't recognize the version of the board... */ @@ -128,7 +135,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); - else this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + else if(get_rx_id() == 0x067) this->get_rx_subtree()->create<std::string>("name").set("CBX RX"); + else this->get_rx_subtree()->create<std::string>("name").set("SBX/CBX RX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); @@ -141,8 +149,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ } this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) - .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); - this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range); + .set((freq_range.start() + freq_range.stop())/2.0); + this->get_rx_subtree()->create<meta_range_t>("freq/range").set(freq_range); this->get_rx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1)) .set("RX2"); @@ -159,8 +167,9 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); - else if(get_tx_id() == 0x067) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); - else this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + else if(get_tx_id() == 0x064) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); + else if(get_tx_id() == 0x066) this->get_tx_subtree()->create<std::string>("name").set("CBX TX"); + else this->get_tx_subtree()->create<std::string>("name").set("SBX/CBX TX"); this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); @@ -173,8 +182,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ } this->get_tx_subtree()->create<double>("freq/value") .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) - .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); - this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range); + .set((freq_range.start() + freq_range.stop())/2.0); + this->get_tx_subtree()->create<meta_range_t>("freq/range").set(freq_range); this->get_tx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1)) .set(sbx_tx_antennas.at(0)); diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 2a0e83115..4f3a2eeaa 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -100,6 +100,7 @@ using namespace boost::assign; * The SBX dboard constants **********************************************************************/ static const freq_range_t sbx_freq_range(400e6, 4.4e9); +static const freq_range_t cbx_freq_range(1200e6, 6.0e9); static const freq_range_t sbx_tx_lo_2dbm = list_of (range_t(0.35e9, 0.37e9)) @@ -213,6 +214,30 @@ protected: }; /*! + * CBX daughterboard + * + * The only driver difference between SBX and CBX is the MAX2870 vs. ADF435x. + * There is also no LO filter switching required, but the GPIO is left blank + * so we don't worry about it. + */ + class cbx : public sbx_versionx { + public: + cbx(sbx_xcvr *_self_sbx_xcvr); + ~cbx(void); + + double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + + /*! This is the registered instance of the wrapper class, sbx_base. */ + sbx_xcvr *self_base; + }; + + /*! + * Frequency range of the daughterboard; this is set in the constructor + * to correspond either to SBX or CBX. + */ + freq_range_t freq_range; + + /*! * Handle to the version-specific implementation of the SBX. * * Since many of this class's functions are dependent on the version of the diff --git a/host/lib/usrp/dboard_iface.cpp b/host/lib/usrp/dboard_iface.cpp index 5cc5ea470..6be50130a 100644 --- a/host/lib/usrp/dboard_iface.cpp +++ b/host/lib/usrp/dboard_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,6 +31,11 @@ dboard_iface::dboard_iface(void){ _impl = UHD_PIMPL_MAKE(impl, ()); } +dboard_iface::~dboard_iface(void) +{ + //empty +} + template <typename T> static T shadow_it(T &shadow, const T &value, const T &mask){ shadow = (shadow & ~mask) | (value & mask); diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index 532b2dc9e..07d0049c8 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -73,8 +73,8 @@ public: void set_gpio_debug(unit_t, int); boost::uint16_t read_gpio(unit_t); - void write_i2c(boost::uint8_t, const byte_vector_t &); - byte_vector_t read_i2c(boost::uint8_t, size_t); + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); void write_spi( unit_t unit, @@ -219,11 +219,11 @@ boost::uint32_t e100_dboard_iface::read_write_spi( /*********************************************************************** * I2C **********************************************************************/ -void e100_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ +void e100_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){ return _i2c_iface->write_i2c(addr, bytes); } -byte_vector_t e100_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ +byte_vector_t e100_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){ return _i2c_iface->read_i2c(addr, num_bytes); } diff --git a/host/lib/usrp/e100/e100_ctrl.cpp b/host/lib/usrp/e100/e100_ctrl.cpp index c9c86c8af..cdbbff6dd 100644 --- a/host/lib/usrp/e100/e100_ctrl.cpp +++ b/host/lib/usrp/e100/e100_ctrl.cpp @@ -144,7 +144,7 @@ public: ::close(_node_fd); } - void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ + void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){ byte_vector_t rw_bytes(bytes); //setup the message @@ -163,7 +163,7 @@ public: UHD_ASSERT_THROW(::ioctl(_node_fd, I2C_RDWR, &data) >= 0); } - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){ + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){ byte_vector_t bytes(num_bytes); //setup the message diff --git a/host/lib/usrp/e100/io_impl.cpp b/host/lib/usrp/e100/io_impl.cpp index e34620444..bf04a5871 100644 --- a/host/lib/usrp/e100/io_impl.cpp +++ b/host/lib/usrp/e100/io_impl.cpp @@ -166,6 +166,8 @@ rx_streamer::sptr e100_impl::get_rx_stream(const uhd::stream_args_t &args_){ my_streamer->set_overflow_handler(chan_i, boost::bind( &rx_dsp_core_200::handle_overflow, _rx_dsps[dsp] )); + my_streamer->set_issue_stream_cmd(chan_i, boost::bind( + &rx_dsp_core_200::issue_stream_command, _rx_dsps[dsp], _1)); _rx_streamers[dsp] = my_streamer; //store weak pointer } @@ -220,6 +222,7 @@ tx_streamer::sptr e100_impl::get_tx_stream(const uhd::stream_args_t &args_){ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_send_buff, _data_transport, _1 )); + my_streamer->set_async_receiver(boost::bind(&fifo_ctrl_excelsior::pop_async_msg, _fifo_ctrl, _1, _2)); _tx_streamers[dsp] = my_streamer; //store weak pointer } diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index f3bdded60..c3af75faa 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -26,6 +26,10 @@ #include <boost/thread/thread.hpp> #include <boost/tokenizer.hpp> #include <boost/format.hpp> +#include <boost/regex.hpp> + +#include "boost/tuple/tuple.hpp" +#include "boost/foreach.hpp" using namespace uhd; using namespace boost::gregorian; @@ -38,14 +42,69 @@ using namespace boost::this_thread; */ class gps_ctrl_impl : public gps_ctrl{ +private: + std::map<std::string, boost::tuple<std::string, boost::system_time, bool> > sensors; + + std::string get_cached_sensor(const std::string sensor, const int freshness, const bool once, const bool touch=true) { + boost::system_time time = boost::get_system_time(); + try { + // this is nasty ... + //std::cout << boost::format("Requested %s - seen? ") % sensor << sensors[sensor].get<2>() << " once? " << once << std::endl; + if(time - sensors[sensor].get<1>() < milliseconds(freshness) && (!once or !sensors[sensor].get<2>())) { + sensors[sensor] = boost::make_tuple(sensors[sensor].get<0>(), sensors[sensor].get<1>(), touch); + return sensors[sensor].get<0>(); + } else { + return update_cached_sensors(sensor); + } + } catch(std::exception &e) { + UHD_MSG(warning) << "get_cached_sensor: " << e.what(); + } + return std::string(); + } + + std::string update_cached_sensors(const std::string sensor) { + if(not gps_detected() || (gps_type != GPS_TYPE_JACKSON_LABS)) { + UHD_MSG(error) << "get_stat(): unsupported GPS or no GPS detected"; + return std::string(); + } + + std::string msg = _recv(); + static const boost::regex status_regex("\\d\\d-\\d\\d-\\d\\d"); + boost::system_time time = boost::get_system_time(); + if(msg.size() < 6) + return std::string(); + + std::string nmea = msg.substr(1,5); + const std::list<std::string> list = boost::assign::list_of("GPGGA")("GPRMC"); + BOOST_FOREACH(std::string key, list) { + // beginning matches one of the NMEA keys + if(!nmea.compare(key)) { + sensors[key] = boost::make_tuple(msg, time, !sensor.compare(key)); + // if this was what we're looking for return it + return (!sensor.compare(key))? msg : std::string(); + } + } + + //We're still here so it's not one of the NMEA strings from above + if(boost::regex_search(msg, status_regex, boost::regex_constants::match_continuous)) { + trim(msg); + sensors["SERVO"] = boost::make_tuple(msg, time, false); + if(!sensor.compare("SERVO")) + return msg; + else + return std::string(); + } + return std::string(); + } public: gps_ctrl_impl(uart_iface::sptr uart){ _uart = uart; + std::string reply; bool i_heard_some_nmea = false, i_heard_something_weird = false; gps_type = GPS_TYPE_NONE; - + //first we look for a Jackson Labs Firefly (since that's what we provide...) _flush(); //get whatever junk is in the rx buffer right now, and throw it away _send("HAAAY GUYYYYS\n"); //to elicit a response from the Firefly @@ -60,7 +119,7 @@ public: if(reply.find("Command Error") != std::string::npos) { gps_type = GPS_TYPE_JACKSON_LABS; break; - } + } else if(reply.substr(0, 3) == "$GP") i_heard_some_nmea = true; //but keep looking for that "Command Error" response else if(reply.length() != 0) i_heard_something_weird = true; //probably wrong baud rate sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); @@ -99,7 +158,8 @@ public: ("gps_gpgga") ("gps_gprmc") ("gps_time") - ("gps_locked"); + ("gps_locked") + ("gps_servo"); return ret; } @@ -108,7 +168,7 @@ public: or key == "gps_gprmc") { return sensor_value_t( boost::to_upper_copy(key), - get_nmea(boost::to_upper_copy(key.substr(4,8))), + get_cached_sensor(boost::to_upper_copy(key.substr(4,8)), GPS_NMEA_NORMAL_FRESHNESS, false, false), ""); } else if(key == "gps_time") { @@ -117,6 +177,9 @@ public: else if(key == "gps_locked") { return sensor_value_t("GPS lock status", locked(), "locked", "unlocked"); } + else if(key == "gps_servo") { + return sensor_value_t("GPS servo status", get_servo(), ""); + } else { throw uhd::value_error("gps ctrl get_sensor unknown key: " + key); } @@ -138,24 +201,25 @@ private: sleep(milliseconds(FIREFLY_STUPID_DELAY_MS)); _send("GPS:GPRMC 1\n"); sleep(milliseconds(FIREFLY_STUPID_DELAY_MS)); + _send("SERV:TRAC 0\n"); + sleep(milliseconds(FIREFLY_STUPID_DELAY_MS)); } - + //retrieve a raw NMEA sentence std::string get_nmea(std::string msgtype) { - msgtype.insert(0, "$"); std::string reply; - if(not gps_detected()) { - UHD_MSG(error) << "get_nmea(): unsupported GPS or no GPS detected"; - return std::string(); - } - - _flush(); //flush all input before waiting for a message const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); while(boost::get_system_time() < comm_timeout) { - reply = _recv(); - if(reply.substr(0, 6) == msgtype) - return reply; + if(!msgtype.compare("GPRMC")) { + reply = get_cached_sensor(msgtype, GPS_NMEA_FRESHNESS, true); + } + else { + reply = get_cached_sensor(msgtype, GPS_NMEA_LOW_FRESHNESS, false); + } + if(reply.size()) { + if(reply.substr(1, 5) == msgtype) return reply; + } boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); } throw uhd::value_error(str(boost::format("get_nmea(): no %s message found") % msgtype)); @@ -176,9 +240,10 @@ private: } ptime get_time(void) { + _flush(); int error_cnt = 0; ptime gps_time; - while(error_cnt < 3) { + while(error_cnt < 2) { try { std::string reply = get_nmea("GPRMC"); @@ -188,29 +253,30 @@ private: if(datestr.size() == 0 or timestr.size() == 0) { throw uhd::value_error(str(boost::format("Invalid response \"%s\"") % reply)); } - + //just trust me on this one - gps_time = ptime( date( + gps_time = ptime( date( greg_year(boost::lexical_cast<int>(datestr.substr(4, 2)) + 2000), - greg_month(boost::lexical_cast<int>(datestr.substr(2, 2))), - greg_day(boost::lexical_cast<int>(datestr.substr(0, 2))) + greg_month(boost::lexical_cast<int>(datestr.substr(2, 2))), + greg_day(boost::lexical_cast<int>(datestr.substr(0, 2))) ), hours( boost::lexical_cast<int>(timestr.substr(0, 2))) + minutes(boost::lexical_cast<int>(timestr.substr(2, 2))) + seconds(boost::lexical_cast<int>(timestr.substr(4, 2))) ); return gps_time; - + } catch(std::exception &e) { UHD_MSG(warning) << "get_time: " << e.what(); + _flush(); error_cnt++; } } throw uhd::value_error("Timeout after no valid message found"); - + return gps_time; //keep gcc from complaining } - + time_t get_epoch_time(void) { return (get_time() - from_time_t(0)).total_seconds(); } @@ -223,7 +289,7 @@ private: int error_cnt = 0; while(error_cnt < 3) { try { - std::string reply = get_nmea("GPGGA"); + std::string reply = get_cached_sensor("GPGGA", GPS_LOCK_FRESHNESS, false, false); if(reply.size() <= 1) return false; return (get_token(reply, 6) != "0"); @@ -236,6 +302,29 @@ private: return false; } + std::string get_servo(void) { + + //enable servo reporting + _send("SERV:TRAC 1\n"); + sleep(milliseconds(FIREFLY_STUPID_DELAY_MS)); + + std::string reply; + + const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); + while(boost::get_system_time() < comm_timeout) { + reply = get_cached_sensor("SERVO", GPS_NMEA_LOW_FRESHNESS, false); + if(reply.size()) + { + //disable it before leaving function + _send("SERV:TRAC 0\n"); + return reply; + } + boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); + } + throw uhd::value_error("get_stat(): no servo message found"); + return std::string(); + } + uart_iface::sptr _uart; void _flush(void){ @@ -258,7 +347,12 @@ private: GPS_TYPE_NONE } gps_type; - static const int GPS_COMM_TIMEOUT_MS = 1500; + static const int GPS_COMM_TIMEOUT_MS = 1300; + static const int GPS_NMEA_FRESHNESS = 10; + static const int GPS_NMEA_LOW_FRESHNESS = 2500; + static const int GPS_NMEA_NORMAL_FRESHNESS = 1000; + static const int GPS_SERVO_FRESHNESS = 2500; + static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; static const int FIREFLY_STUPID_DELAY_MS = 200; }; diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 1f4abc27e..dc25379f9 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -359,6 +359,69 @@ static void store_b100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ } /*********************************************************************** + * Implementation of B200 load/store + **********************************************************************/ +/* On the B200, this field indicates the slave address. From the FX3, this + * address is always 0. */ +static const boost::uint8_t B200_EEPROM_SLAVE_ADDR = 0x04; + +//use char array so we dont need to attribute packed +struct b200_eeprom_map{ + unsigned char _r[220]; + unsigned char revision[2]; + unsigned char product[2]; + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; +}; + +static void load_b200(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), 2) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), 2) + ); + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), NAME_MAX_LEN + )); +} + +static void store_b200(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ + //parse the revision number + if (mb_eeprom.has_key("revision")) iface.write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface.write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the serial + if (mb_eeprom.has_key("serial")) iface.write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface.write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} +/*********************************************************************** * Implementation of E100 load/store **********************************************************************/ static const boost::uint8_t E100_EEPROM_ADDR = 0x51; @@ -451,6 +514,7 @@ mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ if (which == "N100") load_n100(*this, iface); if (which == "B000") load_b000(*this, iface); if (which == "B100") load_b100(*this, iface); + if (which == "B200") load_b200(*this, iface); if (which == "E100") load_e100(*this, iface); } @@ -458,5 +522,6 @@ void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ if (which == "N100") store_n100(*this, iface); if (which == "B000") store_b000(*this, iface); if (which == "B100") store_b100(*this, iface); + if (which == "B200") store_b200(*this, iface); if (which == "E100") store_e100(*this, iface); } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index eba3157a9..26ce1ccdd 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #include <uhd/usrp/multi_usrp.hpp> #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> -#include <uhd/utils/msg.hpp> +#include <uhd/utils/log.hpp> #include <uhd/utils/gain_group.hpp> #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/mboard_eeprom.hpp> @@ -140,6 +140,14 @@ static tune_result_t tune_xx_subdev_and_dsp( } //------------------------------------------------------------------ + //-- poke the tune request args into the dboard + //------------------------------------------------------------------ + if (rf_fe_subtree->exists("tune_args")) + { + rf_fe_subtree->access<device_addr_t>("tune_args").set(tune_request.args); + } + + //------------------------------------------------------------------ //-- set the RF frequency depending upon the policy //------------------------------------------------------------------ double target_rf_freq = 0.0; @@ -537,8 +545,25 @@ public: } } - subdev_spec_t get_rx_subdev_spec(size_t mboard){ - return _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get(); + subdev_spec_t get_rx_subdev_spec(size_t mboard) + { + subdev_spec_t spec = _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").get(); + if (spec.empty()) + { + try + { + const std::string db_name = _tree->list(mb_root(mboard) / "dboards").at(0); + const std::string fe_name = _tree->list(mb_root(mboard) / "dboards" / db_name / "rx_frontends").at(0); + spec.push_back(subdev_spec_pair_t(db_name, fe_name)); + _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec); + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::get_rx_subdev_spec(%u) failed to make default spec - %s") % mboard % e.what())); + } + UHD_MSG(status) << "Selecting default RX front end spec: " << spec.to_pp_string() << std::endl; + } + return spec; } size_t get_rx_num_channels(void){ @@ -689,8 +714,25 @@ public: } } - subdev_spec_t get_tx_subdev_spec(size_t mboard){ - return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get(); + subdev_spec_t get_tx_subdev_spec(size_t mboard) + { + subdev_spec_t spec = _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get(); + if (spec.empty()) + { + try + { + const std::string db_name = _tree->list(mb_root(mboard) / "dboards").at(0); + const std::string fe_name = _tree->list(mb_root(mboard) / "dboards" / db_name / "tx_frontends").at(0); + spec.push_back(subdev_spec_pair_t(db_name, fe_name)); + _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec); + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::get_tx_subdev_spec(%u) failed to make default spec - %s") % mboard % e.what())); + } + UHD_MSG(status) << "Selecting default TX front end spec: " << spec.to_pp_string() << std::endl; + } + return spec; } size_t get_tx_num_channels(void){ @@ -835,6 +877,10 @@ private: if (mcp.chan < sss) break; mcp.chan -= sss; } + if (mcp.mboard >= get_num_mboards()) + { + throw uhd::index_error(str(boost::format("multi_usrp: RX channel %u out of range for configured RX frontends") % chan)); + } return mcp; } @@ -846,48 +892,108 @@ private: if (mcp.chan < sss) break; mcp.chan -= sss; } + if (mcp.mboard >= get_num_mboards()) + { + throw uhd::index_error(str(boost::format("multi_usrp: TX channel %u out of range for configured TX frontends") % chan)); + } return mcp; } - fs_path mb_root(const size_t mboard){ - const std::string name = _tree->list("/mboards").at(mboard); - return "/mboards/" + name; + fs_path mb_root(const size_t mboard) + { + try + { + const std::string name = _tree->list("/mboards").at(mboard); + return "/mboards/" + name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::mb_root(%u) - %s") % mboard % e.what())); + } } - fs_path rx_dsp_root(const size_t chan){ + fs_path rx_dsp_root(const size_t chan) + { mboard_chan_pair mcp = rx_chan_to_mcp(chan); - const std::string name = _tree->list(mb_root(mcp.mboard) / "rx_dsps").at(mcp.chan); - return mb_root(mcp.mboard) / "rx_dsps" / name; + try + { + const std::string name = _tree->list(mb_root(mcp.mboard) / "rx_dsps").at(mcp.chan); + return mb_root(mcp.mboard) / "rx_dsps" / name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::rx_dsp_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } - fs_path tx_dsp_root(const size_t chan){ + fs_path tx_dsp_root(const size_t chan) + { mboard_chan_pair mcp = tx_chan_to_mcp(chan); - const std::string name = _tree->list(mb_root(mcp.mboard) / "tx_dsps").at(mcp.chan); - return mb_root(mcp.mboard) / "tx_dsps" / name; + try + { + const std::string name = _tree->list(mb_root(mcp.mboard) / "tx_dsps").at(mcp.chan); + return mb_root(mcp.mboard) / "tx_dsps" / name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::tx_dsp_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } - fs_path rx_fe_root(const size_t chan){ + fs_path rx_fe_root(const size_t chan) + { mboard_chan_pair mcp = rx_chan_to_mcp(chan); - const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); - return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name; + try + { + const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "rx_frontends" / spec.db_name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::rx_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } - fs_path tx_fe_root(const size_t chan){ + fs_path tx_fe_root(const size_t chan) + { mboard_chan_pair mcp = tx_chan_to_mcp(chan); - const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); - return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name; + try + { + const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "tx_frontends" / spec.db_name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::tx_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } - fs_path rx_rf_fe_root(const size_t chan){ + fs_path rx_rf_fe_root(const size_t chan) + { mboard_chan_pair mcp = rx_chan_to_mcp(chan); - const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); - return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends" / spec.sd_name; + try + { + const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "dboards" / spec.db_name / "rx_frontends" / spec.sd_name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::rx_rf_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } - fs_path tx_rf_fe_root(const size_t chan){ + fs_path tx_rf_fe_root(const size_t chan) + { mboard_chan_pair mcp = tx_chan_to_mcp(chan); - const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); - return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends" / spec.sd_name; + try + { + const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); + return mb_root(mcp.mboard) / "dboards" / spec.db_name / "tx_frontends" / spec.sd_name; + } + catch(const std::exception &e) + { + throw uhd::index_error(str(boost::format("multi_usrp::tx_rf_fe_root(%u) - mcp(%u) - %s") % chan % mcp.chan % e.what())); + } } gain_group::sptr rx_gain_group(size_t chan){ @@ -921,5 +1027,6 @@ private: * The Make Function **********************************************************************/ multi_usrp::sptr multi_usrp::make(const device_addr_t &dev_addr){ + UHD_LOG << "multi_usrp::make with args " << dev_addr.to_pp_string() << std::endl; return sptr(new multi_usrp_impl(dev_addr)); } diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 39850d5d1..4c3141d9e 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -113,8 +113,8 @@ public: void set_gpio_debug(unit_t, int); boost::uint16_t read_gpio(unit_t); - void write_i2c(boost::uint8_t, const byte_vector_t &); - byte_vector_t read_i2c(boost::uint8_t, size_t); + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); void write_spi(unit_t unit, const spi_config_t &config, @@ -386,13 +386,13 @@ boost::uint32_t usrp1_dboard_iface::read_write_spi(unit_t unit, /*********************************************************************** * I2C **********************************************************************/ -void usrp1_dboard_iface::write_i2c(boost::uint8_t addr, +void usrp1_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) { return _iface->write_i2c(addr, bytes); } -byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint8_t addr, +byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes) { return _iface->read_i2c(addr, num_bytes); diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 8940a92bb..d384eb13f 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -356,6 +356,11 @@ public: return _stc->recv_post(metadata, num_samps_recvd); } + void issue_stream_cmd(const stream_cmd_t &stream_cmd) + { + _stc->issue_stream_cmd(stream_cmd); + } + private: size_t _max_num_samps; soft_time_ctrl::sptr _stc; @@ -410,6 +415,12 @@ public: return num_samps_sent; } + bool recv_async_msg( + async_metadata_t &async_metadata, double timeout = 0.1 + ){ + return _stc->get_async_queue().pop_with_timed_wait(async_metadata, timeout); + } + private: size_t _max_num_samps; soft_time_ctrl::sptr _stc; diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp index 16b747e45..9301721aa 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.cpp +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -104,11 +104,11 @@ public: /******************************************************************* * I2C ******************************************************************/ - void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ + void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){ return _ctrl_transport->write_i2c(addr, bytes); } - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){ + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){ return _ctrl_transport->read_i2c(addr, num_bytes); } diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index edd9ef242..8f2d0f0dc 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -60,8 +60,8 @@ public: void set_gpio_debug(unit_t, int); boost::uint16_t read_gpio(unit_t); - void write_i2c(boost::uint8_t, const byte_vector_t &); - byte_vector_t read_i2c(boost::uint8_t, size_t); + void write_i2c(boost::uint16_t, const byte_vector_t &); + byte_vector_t read_i2c(boost::uint16_t, size_t); void set_clock_rate(unit_t, double); double get_clock_rate(unit_t); @@ -229,11 +229,11 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( /*********************************************************************** * I2C **********************************************************************/ -void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ +void usrp2_dboard_iface::write_i2c(boost::uint16_t addr, const byte_vector_t &bytes){ return _i2c_iface->write_i2c(addr, bytes); } -byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ +byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint16_t addr, size_t num_bytes){ return _i2c_iface->read_i2c(addr, num_bytes); } diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index e06cf8f6f..9ee6abed0 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -467,6 +467,8 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); + my_streamer->set_issue_stream_cmd(chan_i, boost::bind( + &rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dsp], _1)); _mbc[mb].rx_streamers[dsp] = my_streamer; //store weak pointer break; } @@ -536,6 +538,7 @@ tx_streamer::sptr usrp2_impl::get_tx_stream(const uhd::stream_args_t &args_){ my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &usrp2_impl::io_impl::get_send_buff, _io_impl.get(), abs, _1 )); + my_streamer->set_async_receiver(boost::bind(&bounded_buffer<async_metadata_t>::pop_with_timed_wait, &(_io_impl->async_msg_fifo), _1, _2)); _mbc[mb].tx_streamers[dsp] = my_streamer; //store weak pointer break; } diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 8804433e7..3b230ca69 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -240,7 +240,7 @@ public: /*********************************************************************** * I2C **********************************************************************/ - void write_i2c(boost::uint8_t addr, const byte_vector_t &buf){ + void write_i2c(boost::uint16_t addr, const byte_vector_t &buf){ //setup the out data usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t(); out_data.id = htonl(USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO); @@ -258,7 +258,7 @@ public: UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE); } - byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){ + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes){ //setup the out data usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t(); out_data.id = htonl(USRP2_CTRL_ID_DO_AN_I2C_READ_FOR_ME_BRO); diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 95105f917..a28e1f9ef 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -115,14 +115,14 @@ SET_SOURCE_FILES_PROPERTIES( ######################################################################## # Define UHD_PKG_DATA_PATH for paths.cpp ######################################################################## -FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX}/${PKG_DATA_DIR} UHD_PKG_DATA_PATH) -STRING(REPLACE "\\" "\\\\" UHD_PKG_DATA_PATH ${UHD_PKG_DATA_PATH}) -MESSAGE(STATUS "Full package data directory: ${UHD_PKG_DATA_PATH}") +FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH) +STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH}) SET_SOURCE_FILES_PROPERTIES( ${CMAKE_CURRENT_SOURCE_DIR}/paths.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/images.cpp PROPERTIES COMPILE_DEFINITIONS - "UHD_PKG_DATA_PATH=\"${UHD_PKG_DATA_PATH}\"" + "UHD_PKG_PATH=\"${UHD_PKG_PATH}\";UHD_LIB_DIR=\"lib${LIB_SUFFIX}\"" ) ######################################################################## diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp index 251cadeaa..1ba2f81e6 100644 --- a/host/lib/utils/images.cpp +++ b/host/lib/utils/images.cpp @@ -42,7 +42,7 @@ std::string uhd::find_image_path(const std::string &image_name){ } std::string uhd::find_images_downloader(void){ - return fs::path((fs::path(get_pkg_data_path()) / "utils" / "uhd_images_downloader.py")).string(); + return fs::path(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "uhd_images_downloader.py").string(); } std::string uhd::print_images_error(void){ diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 26fa6d1c7..53055314b 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -71,20 +71,20 @@ static std::vector<fs::path> get_env_paths(const std::string &var_name){ /*********************************************************************** * Get a list of special purpose paths **********************************************************************/ -std::string uhd::get_pkg_data_path(void) +std::string uhd::get_pkg_path(void) { - return get_env_var("UHD_PKG_DATA_PATH", UHD_PKG_DATA_PATH); + return get_env_var("UHD_PKG_PATH", UHD_PKG_PATH); } std::vector<fs::path> get_image_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_IMAGE_PATH"); - paths.push_back(fs::path(uhd::get_pkg_data_path()) / "images"); + paths.push_back(fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "images"); return paths; } std::vector<fs::path> get_module_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH"); - paths.push_back(fs::path(uhd::get_pkg_data_path()) / "modules"); + paths.push_back(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "modules"); return paths; } diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index e9d1e5e92..2a40d0050 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -16,6 +16,11 @@ # ######################################################################## +# unit test support +######################################################################## +include(UHDUnitTest) + +######################################################################## # unit test suite ######################################################################## SET(test_sources @@ -39,12 +44,15 @@ SET(test_sources #turn each test cpp file into an executable with an int main() function ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) -#for each source: build an executable, register it as a test, and install +SET(UHD_TEST_TARGET_DEPS uhd) +SET(UHD_TEST_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) + +#for each source: build an executable, register it as a test FOREACH(test_source ${test_sources}) GET_FILENAME_COMPONENT(test_name ${test_source} NAME_WE) ADD_EXECUTABLE(${test_name} ${test_source}) TARGET_LINK_LIBRARIES(${test_name} uhd) - ADD_TEST(${test_name} ${test_name}) + UHD_ADD_TEST(${test_name} ${test_name}) UHD_INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) ENDFOREACH(test_source) diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 5a40029dc..9339a9739 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){ //simulate overflow if (i == NUM_PKTS_TO_TEST/2){ - ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_EXTENSION; + ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; ifpi.num_payload_words32 = 1; dummy_recv_xport.push_back_packet(ifpi, uhd::rx_metadata_t::ERROR_CODE_OVERFLOW); } diff --git a/host/tests/vrt_test.cpp b/host/tests/vrt_test.cpp index 066f1493b..225fa4f2b 100644 --- a/host/tests/vrt_test.cpp +++ b/host/tests/vrt_test.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,26 +17,39 @@ #include <boost/test/unit_test.hpp> #include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/format.hpp> #include <cstdlib> +#include <iostream> using namespace uhd::transport; static void pack_and_unpack( vrt::if_packet_info_t &if_packet_info_in ){ - boost::uint32_t header_buff[vrt::max_if_hdr_words32]; + if (if_packet_info_in.num_payload_bytes == 0) + { + if_packet_info_in.num_payload_bytes = if_packet_info_in.num_payload_words32 * sizeof(boost::uint32_t); + } + boost::uint32_t packet_buff[2048]; //pack metadata into a vrt header vrt::if_hdr_pack_be( - header_buff, if_packet_info_in + packet_buff, if_packet_info_in ); + std::cout << std::endl; + for (size_t i = 0; i < 5; i++) + { + std::cout << boost::format("packet_buff[%u] = 0x%.8x") % i % uhd::byteswap(packet_buff[i]) << std::endl; + } vrt::if_packet_info_t if_packet_info_out; + if_packet_info_out.link_type = if_packet_info_in.link_type; if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32; //unpack the vrt header back into metadata vrt::if_hdr_unpack_be( - header_buff, if_packet_info_out + packet_buff, if_packet_info_out ); //check the the unpacked metadata is the same @@ -91,7 +104,7 @@ BOOST_AUTO_TEST_CASE(test_with_sid){ if_packet_info.has_tsf = false; if_packet_info.has_tlr = false; if_packet_info.sid = std::rand(); - if_packet_info.num_payload_words32 = 1111; + if_packet_info.num_payload_words32 = 11; pack_and_unpack(if_packet_info); } @@ -106,7 +119,7 @@ BOOST_AUTO_TEST_CASE(test_with_cid){ if_packet_info.has_tsf = false; if_packet_info.has_tlr = false; if_packet_info.cid = std::rand(); - if_packet_info.num_payload_words32 = 2222; + if_packet_info.num_payload_words32 = 22; pack_and_unpack(if_packet_info); } @@ -120,7 +133,7 @@ BOOST_AUTO_TEST_CASE(test_with_time){ if_packet_info.has_tlr = false; if_packet_info.tsi = std::rand(); if_packet_info.tsf = std::rand(); - if_packet_info.num_payload_words32 = 33333; + if_packet_info.num_payload_words32 = 33; pack_and_unpack(if_packet_info); } @@ -136,6 +149,36 @@ BOOST_AUTO_TEST_CASE(test_with_all){ if_packet_info.cid = std::rand(); if_packet_info.tsi = std::rand(); if_packet_info.tsf = std::rand(); - if_packet_info.num_payload_words32 = 44444; + if_packet_info.num_payload_words32 = 44; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_vrlp){ + vrt::if_packet_info_t if_packet_info; + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_VRLP; + if_packet_info.packet_count = 3; + if_packet_info.has_sid = true; + if_packet_info.has_cid = false; + if_packet_info.has_tsi = false; + if_packet_info.has_tsf = true; + if_packet_info.has_tlr = true; + if_packet_info.tsi = std::rand(); + if_packet_info.tsf = std::rand(); + if_packet_info.num_payload_words32 = 42; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr){ + vrt::if_packet_info_t if_packet_info; + if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; + if_packet_info.packet_count = 7; + if_packet_info.has_sid = true; + if_packet_info.has_cid = false; + if_packet_info.has_tsi = false; + if_packet_info.has_tsf = true; + if_packet_info.has_tlr = true; + if_packet_info.tsi = std::rand(); + if_packet_info.tsf = std::rand(); + if_packet_info.num_payload_words32 = 24; pack_and_unpack(if_packet_info); } diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index b04d0e1b9..92df0b7cf 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -47,7 +47,9 @@ SET(util_share_sources IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp + b2xx_fx3_utils.cpp ) + INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS}) ENDIF(ENABLE_USB) IF(LINUX AND ENABLE_USB) diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp new file mode 100644 index 000000000..c99ce1e01 --- /dev/null +++ b/host/utils/b2xx_fx3_utils.cpp @@ -0,0 +1,746 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <iomanip> +#include <fstream> +#include <libusb.h> +#include <sstream> +#include <string> +#include <cmath> +#include <cstring> + +#include <boost/cstdint.hpp> +#include <boost/filesystem.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <boost/program_options.hpp> +#include <boost/thread/thread.hpp> +#include <boost/functional/hash.hpp> + +#include <uhd/config.hpp> + +const static boost::uint16_t FX3_VID = 0x04b4; +const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; +const static boost::uint16_t FX3_REENUM_PID = 0x00f0; +const static boost::uint16_t B2XX_VID = 0x2500; +const static boost::uint16_t B2XX_PID = 0x0020; + +const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_ENDPOINT_OUT); +const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR + | LIBUSB_ENDPOINT_IN); +const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0; + +const static boost::uint8_t B2XX_VREQ_FPGA_START = 0x02; +const static boost::uint8_t B2XX_VREQ_FPGA_DATA = 0x12; +const static boost::uint8_t B2XX_VREQ_SET_FPGA_HASH = 0x1C; +const static boost::uint8_t B2XX_VREQ_GET_FPGA_HASH = 0x1D; +const static boost::uint8_t B2XX_VREQ_FPGA_RESET = 0x62; +const static boost::uint8_t B2XX_VREQ_GET_USB = 0x80; +const static boost::uint8_t B2XX_VREQ_GET_STATUS = 0x83; +const static boost::uint8_t B2XX_VREQ_FX3_RESET = 0x99; +const static boost::uint8_t B2XX_VREQ_EEPROM_WRITE = 0xBA; + +const static boost::uint8_t FX3_STATE_FPGA_READY = 0x00; +const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x01; +const static boost::uint8_t FX3_STATE_BUSY = 0x02; +const static boost::uint8_t FX3_STATE_RUNNING = 0x03; + +typedef boost::uint32_t hash_type; +typedef std::vector<boost::uint8_t> byte_vector_t; + + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + + +//!used with lexical cast to parse a hex string +template <class T> struct to_hex{ + T value; + operator T() const {return value;} + friend std::istream& operator>>(std::istream& in, to_hex& out){ + in >> std::hex >> out.value; + return in; + } +}; + +//!parse hex-formatted ASCII text into an int +boost::uint16_t atoh(const std::string &string){ + if (string.substr(0, 2) == "0x"){ + return boost::lexical_cast<to_hex<boost::uint16_t> >(string); + } + return boost::lexical_cast<boost::uint16_t>(string); +} + + +/*! + * Create a file hash + * The hash will be used to identify the loaded firmware and fpga image + * \param filename file used to generate hash value + * \return hash value in a size_t type + */ +static hash_type generate_hash(const char *filename) +{ + std::ifstream file(filename); + if (not file){ + std::cerr << std::string("cannot open input file ") + filename + << std::endl; + } + + size_t hash = 0; + + char ch; + while (file.get(ch)) { + boost::hash_combine(hash, ch); + } + + if (not file.eof()){ + std::cerr << std::string("file error ") + filename << std::endl; + } + + file.close(); + return hash_type(hash); +} + +/*! + * Verify checksum of a Intel HEX record + * \param record a line from an Intel HEX file + * \return true if record is valid, false otherwise + */ +bool checksum(std::string *record) { + + size_t len = record->length(); + unsigned int i; + unsigned char sum = 0; + unsigned int val; + + for (i = 1; i < len; i += 2) { + std::istringstream(record->substr(i, 2)) >> std::hex >> val; + sum += val; + } + + if (sum == 0) + return true; + else + return false; +} + + +/*! + * Parse Intel HEX record + * + * \param record a line from an Intel HEX file + * \param len output length of record + * \param addr output address + * \param type output type + * \param data output data + * \return true if record is sucessfully read, false on error + */ +bool parse_record(std::string *record, boost::uint16_t &len, boost::uint16_t &addr, + uint16_t &type, unsigned char* data) { + + unsigned int i; + std::string _data; + unsigned int val; + + if (record->substr(0, 1) != ":") + return false; + + std::istringstream(record->substr(1, 2)) >> std::hex >> len; + std::istringstream(record->substr(3, 4)) >> std::hex >> addr; + std::istringstream(record->substr(7, 2)) >> std::hex >> type; + + for (i = 0; i < len; i++) { + std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; + data[i] = (unsigned char) val; + } + + return true; +} + + +/*! + * Write data to the FX3. + * + * \param dev_handle the libusb-1.0 device handle + * \param request the usb transfer request type + * \param value the USB bValue + * \param index the USB bIndex + * \param buff the data to write + * \param length the number of bytes to write + * \return the number of bytes written + */ +libusb_error fx3_control_write(libusb_device_handle *dev_handle, boost::uint8_t request, + boost::uint16_t value, boost::uint16_t index, unsigned char *buff, + boost::uint16_t length, boost::uint32_t timeout = 0) { + +#if 0 + if(DEBUG) { + std::cout << "Writing: <" << std::hex << std::setw(6) << std::showbase \ + << std::internal << std::setfill('0') << (int) request \ + << ", " << std::setw(6) << (int) VRT_VENDOR_OUT \ + << ", " << std::setw(6) << value \ + << ", " << std::setw(6) << index \ + << ", " << std::dec << std::setw(2) << length \ + << ">" << std::endl; + + std::cout << "\t\tData: 0x " << std::noshowbase; + + for(int count = 0; count < length; count++) { + std::cout << std::hex << std::setw(2) << (int) buff[count] << " "; + } + + std::cout << std::showbase << std::endl; + } +#endif + + return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_OUT, request, \ + value, index, buff, length, timeout); +} + + +/*! + * Read data from the FX3. + * + * \param dev_handle the libusb-1.0 device handle + * \param request the usb transfer request type + * \param value the USB bValue + * \param index the USB bIndex + * \param buff a buffer to store the read bytes to + * \param length the number of bytes to read + * \return the number of bytes read + */ +libusb_error fx3_control_read(libusb_device_handle *dev_handle, boost::uint8_t request, + boost::uint16_t value, boost::uint16_t index, unsigned char *buff, + boost::uint16_t length, boost::uint32_t timeout = 0) { + +#if 0 + if(DEBUG) { + std::cout << "Reading: <" << std::hex << std::setw(6) << std::showbase \ + << std::internal << std::setfill('0') << (int) request \ + << ", " << std::setw(6) << (int) VRT_VENDOR_IN \ + << ", " << std::setw(6) << value \ + << ", " << std::setw(6) << index \ + << ", " << std::dec << std::setw(2) << length \ + << ">" << std::endl << std::endl; + } +#endif + + return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_IN, request, \ + value, index, buff, length, timeout); +} + + +void write_eeprom(libusb_device_handle *dev_handle, boost::uint8_t addr, + boost::uint8_t offset, const byte_vector_t &bytes) { + fx3_control_write(dev_handle, B2XX_VREQ_EEPROM_WRITE, + 0, offset | (boost::uint16_t(addr) << 8), + (unsigned char *) &bytes[0], + bytes.size()); +} + + +boost::uint8_t get_fx3_status(libusb_device_handle *dev_handle) { + + unsigned char rx_data[1]; + + fx3_control_read(dev_handle, B2XX_VREQ_GET_STATUS, 0x00, 0x00, rx_data, 1); + + return boost::lexical_cast<boost::uint8_t>(rx_data[0]); +} + +void usrp_get_fpga_hash(libusb_device_handle *dev_handle, hash_type &hash) { + fx3_control_read(dev_handle, B2XX_VREQ_GET_FPGA_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4, 500); +} + +void usrp_set_fpga_hash(libusb_device_handle *dev_handle, hash_type hash) { + fx3_control_write(dev_handle, B2XX_VREQ_SET_FPGA_HASH, 0x00, 0x00, + (unsigned char*) &hash, 4); +} + +boost::int32_t load_fpga(libusb_device_handle *dev_handle, + const std::string filestring) { + + if (filestring.empty()) + { + std::cerr << "load_fpga: input file is empty." << std::endl; + exit(-1); + } + + boost::uint8_t fx3_state = 0; + + const char *filename = filestring.c_str(); + + hash_type hash = generate_hash(filename); + hash_type loaded_hash; usrp_get_fpga_hash(dev_handle, loaded_hash); + if (hash == loaded_hash) return 0; + + size_t file_size = 0; + { + std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate); + file_size = file.tellg(); + } + + std::ifstream file; + file.open(filename, std::ios::in | std::ios::binary); + + if(!file.good()) { + std::cerr << "load_fpga: cannot open FPGA input file." << std::endl; + exit(-1); + } + + do { + fx3_state = get_fx3_status(dev_handle); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_FPGA_READY); + + std::cout << "Loading FPGA image: " \ + << filestring << "..." << std::flush; + + unsigned char out_buff[64]; + memset(out_buff, 0x00, sizeof(out_buff)); + fx3_control_write(dev_handle, B2XX_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000); + + do { + fx3_state = get_fx3_status(dev_handle); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_CONFIGURING_FPGA); + + + size_t bytes_sent = 0; + while(!file.eof()) { + file.read((char *) out_buff, sizeof(out_buff)); + const std::streamsize n = file.gcount(); + if(n == 0) continue; + + boost::uint16_t transfer_count = boost::uint16_t(n); + + /* Send the data to the device. */ + fx3_control_write(dev_handle, B2XX_VREQ_FPGA_DATA, 0, 0, out_buff, + transfer_count, 5000); + + if (bytes_sent == 0) std::cout << " 0%" << std::flush; + const size_t percent_before = size_t((bytes_sent*100)/file_size); + bytes_sent += transfer_count; + const size_t percent_after = size_t((bytes_sent*100)/file_size); + if (percent_before/10 != percent_after/10) { + std::cout << "\b\b\b\b" << std::setw(3) << percent_after + << "%" << std::flush; + } + } + + file.close(); + + do { + fx3_state = get_fx3_status(dev_handle); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } while(fx3_state != FX3_STATE_RUNNING); + + usrp_set_fpga_hash(dev_handle, hash); + + std::cout << "\b\b\b\b done" << std::endl; + + return 0; +} + + +/*! + * Program the FX3 with a firmware file (Intel HEX format) + * + * \param dev_handle the libusb-1.0 device handle + * \param filestring the filename of the firmware file + * \return 0 for success, otherwise error code + */ +boost::int32_t fx3_load_firmware(libusb_device_handle *dev_handle, \ + std::string filestring) { + + if (filestring.empty()) + { + std::cerr << "fx3_load_firmware: input file is empty." << std::endl; + exit(-1); + } + + const char *filename = filestring.c_str(); + + /* Fields used in each USB control transfer. */ + boost::uint16_t len = 0; + boost::uint16_t type = 0; + boost::uint16_t lower_address_bits = 0x0000; + unsigned char data[512]; + + /* Can be set by the Intel HEX record 0x04, used for all 0x00 records + * thereafter. Note this field takes the place of the 'index' parameter in + * libusb calls, and is necessary for FX3's 32-bit addressing. */ + boost::uint16_t upper_address_bits = 0x0000; + + std::ifstream file; + file.open(filename, std::ifstream::in); + + if(!file.good()) { + std::cerr << "fx3_load_firmware: cannot open firmware input file" + << std::endl; + exit(-1); + } + + std::cout << "Loading firmware image: " \ + << filestring << "..." << std::flush; + + while (!file.eof()) { + boost::int32_t ret = 0; + std::string record; + file >> record; + + /* Check for valid Intel HEX record. */ + if (!checksum(&record) || !parse_record(&record, len, \ + lower_address_bits, type, data)) { + std::cerr << "fx3_load_firmware: bad intel hex record checksum" + << std::endl; + } + + /* Type 0x00: Data. */ + if (type == 0x00) { + ret = fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, \ + lower_address_bits, upper_address_bits, data, len); + + if (ret < 0) { + std::cerr << "usrp_load_firmware: usrp_control_write failed" + << std::endl; + } + } + + /* Type 0x01: EOF. */ + else if (type == 0x01) { + if (lower_address_bits != 0x0000 || len != 0 ) { + std::cerr << "fx3_load_firmware: For EOF record, address must be 0, length must be 0." << std::endl; + } + + /* Successful termination! */ + file.close(); + + /* Let the system settle. */ + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + return 0; + } + + /* Type 0x04: Extended Linear Address Record. */ + else if (type == 0x04) { + if (lower_address_bits != 0x0000 || len != 2 ) { + std::cerr << "fx3_load_firmware: For ELA record, address must be 0, length must be 2." << std::endl; + } + + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + } + + /* Type 0x05: Start Linear Address Record. */ + else if (type == 0x05) { + if (lower_address_bits != 0x0000 || len != 4 ) { + std::cerr << "fx3_load_firmware: For SLA record, address must be 0, length must be 4." << std::endl; + } + + /* The firmware load is complete. We now need to tell the CPU + * to jump to an execution address start point, now contained within + * the data field. Parse these address bits out, and then push the + * instruction. */ + upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[1] & 0x00FF)); + lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ + + ((boost::uint16_t)(data[3] & 0x00FF)); + + fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, lower_address_bits, \ + upper_address_bits, 0, 0); + + std::cout << " done" << std::endl; + } + + /* If we receive an unknown record type, error out. */ + else { + std::cerr << "fx3_load_firmware: unsupported record type." << std::endl; + } + } + + /* There was no valid EOF. */ + std::cerr << "fx3_load_firmware: No EOF record found." << std::cout; + return ~0; +} + + +boost::int32_t main(boost::int32_t argc, char *argv[]) { + boost::uint16_t vid, pid; + std::string pid_str, vid_str, fw_file, fpga_file; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "help message") + ("vid,v", po::value<std::string>(&vid_str)->default_value("0x2500"), + "Specify VID of device to use.") + ("pid,p", po::value<std::string>(&pid_str)->default_value("0x0020"), + "Specify PID of device to use.") + ("speed,S", "Read back the USB mode currently in use.") + ("reset-device,D", "Reset the B2xx Device.") + ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") + ("reset-usb,U", "Reset the USB subsystem on your host computer.") + ("init-device,I", "Initialize a B2xx device.") + ("load-fw,W", po::value<std::string>(&fw_file)->default_value(""), + "Load a firmware (hex) file into the FX3.") + ("load-fpga,L", po::value<std::string>(&fpga_file)->default_value(""), + "Load a FPGA (bin) file into the FPGA.") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("help")){ + std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; + return ~0; + } else if (vm.count("reset-usb")) { + /* Okay, first, we need to discover what the path is to the ehci and + * xhci device files. */ + std::set<fs::path> path_list; + path_list.insert("/sys/bus/pci/drivers/xhci-pci/"); + path_list.insert("/sys/bus/pci/drivers/ehci-pci/"); + path_list.insert("/sys/bus/pci/drivers/xhci_hcd/"); + path_list.insert("/sys/bus/pci/drivers/ehci_hcd/"); + + /* Check each of the possible paths above to find which ones this system + * uses. */ + for(std::set<fs::path>::iterator found = path_list.begin(); + found != path_list.end(); ++found) { + + if(fs::exists(*found)) { + + fs::path devpath = *found; + + std::set<fs::path> globbed; + + /* Now, glob all of the files in the directory. */ + fs::directory_iterator end_itr; + for(fs::directory_iterator itr(devpath); itr != end_itr; ++itr) { + globbed.insert((*itr).path()); + } + + /* Check each file path string to see if it is a device file. */ + for(std::set<fs::path>::iterator it = globbed.begin(); + it != globbed.end(); ++it) { + + std::string file = (*it).string(); + + if(file.compare(0, 5, "0000:") == 0) { + /* Un-bind the device. */ + std::fstream unbind((devpath.string() + "unbind").c_str(), + std::fstream::out); + unbind << file; + unbind.close(); + + /* Re-bind the device. */ + std::cout << "Re-binding: " << file << " in " + << devpath.string() << std::endl; + std::fstream bind((devpath.string() + "bind").c_str(), + std::fstream::out); + bind << file; + bind.close(); + } + } + } + } + + return 0; + } + + vid = atoh(vid_str); + pid = atoh(pid_str); + + /* Pointer to pointer of device, used to retrieve a list of devices. */ + libusb_device **devs; + libusb_device_handle *dev_handle; + libusb_context *ctx = NULL; + libusb_error error_code; + + libusb_init(&ctx); + libusb_set_debug(ctx, 3); + libusb_get_device_list(ctx, &devs); + + /* If we are initializing the device, the VID/PID will default to the + * Cypress VID/PID for the initial FW load. */ + if (vm.count("init-device")) { + dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, + FX3_DEFAULT_PID); + if(dev_handle == NULL) { + std::cerr << "Cannot open device with vid: " << vid << " and pid: " + << pid << std::endl; + return -1; + } else { std::cout << "Uninitialized B2xx detected..." << std::flush; } + libusb_free_device_list(devs, 1); + + /* Find out if kernel driver is attached, and if so, detach it. */ + if(libusb_kernel_driver_active(dev_handle, 0) == 1) { + std::cout << " Competing Driver Identified... " << std::flush; + + if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { + std::cout << " Competing Driver Destroyed!" << std::flush; + } + } + libusb_claim_interface(dev_handle, 0); + std::cout << " Control of B2xx granted..." << std::endl << std::endl; + + /* Load the FW. */ + error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); + if(error_code != 0) { + std::cerr << std::flush << "Error loading firmware. Error code: " + << error_code << std::endl; + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + return ~0; + } + + /* Let the device re-enumerate. */ + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, + FX3_REENUM_PID); + if(dev_handle == NULL) { + std::cerr << "Cannot open device with vid: " << vid << " and pid: " + << pid << std::endl; + return -1; + } else { + std::cout << "Detected in-progress init of B2xx..." << std::flush; + } + //libusb_free_device_list(devs, 1); + libusb_claim_interface(dev_handle, 0); + std::cout << " Reenumeration complete, Device claimed..." + << std::endl; + + /* Now, initialize the device. */ + byte_vector_t bytes(8); + bytes[0] = 0x43; + bytes[1] = 0x59; + bytes[2] = 0x14; + bytes[3] = 0xB2; + bytes[4] = (B2XX_PID & 0xff); + bytes[5] = (B2XX_PID >> 8); + bytes[6] = (B2XX_VID & 0xff); + bytes[7] = (B2XX_VID >> 8); + write_eeprom(dev_handle, 0x0, 0x0, bytes); + std::cout << "EEPROM initialized, resetting device..." + << std::endl << std::endl; + + /* Reset the device! */ + boost::uint8_t data_buffer[1]; + fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, + 0x00, 0x00, data_buffer, 1, 5000); + + std::cout << "Initialization Process Complete." + << std::endl << std::endl; + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + return 0; + } + + dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid); + if(dev_handle == NULL) { + std::cerr << "Cannot open device with vid: " << vid << " and pid: " + << pid << std::endl; + return -1; + } else { std::cout << "Reactor Core Online..." << std::flush; } + libusb_free_device_list(devs, 1); + + /* Find out if kernel driver is attached, and if so, detach it. */ + if(libusb_kernel_driver_active(dev_handle, 0) == 1) { + std::cout << " Competing Driver Identified... " << std::flush; + + if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { + std::cout << " Competing Driver Destroyed!" << std::flush; + } + } + + /* Claim interface 0 of device. */ + error_code = (libusb_error) libusb_claim_interface(dev_handle, 0); + std::cout << " All Systems Nominal..." << std::endl << std::endl; + + boost::uint8_t data_buffer[16]; + memset(data_buffer, 0x0, sizeof(data_buffer)); + + if (vm.count("speed")){ + error_code = fx3_control_read(dev_handle, B2XX_VREQ_GET_USB, + 0x00, 0x00, data_buffer, 1, 5000); + + boost::uint8_t speed = boost::lexical_cast<boost::uint8_t>(data_buffer[0]); + + std::cout << "Currently operating at USB " << (int) speed << std::endl; + + } else if (vm.count("reset-device")) { + error_code = fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, + 0x00, 0x00, data_buffer, 1, 5000); + + } else if (vm.count("reset-fpga")) { + error_code = fx3_control_write(dev_handle, B2XX_VREQ_FPGA_RESET, + 0x00, 0x00, data_buffer, 1, 5000); + + } else if (vm.count("load-fw")) { + error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); + + if(error_code != 0) { + std::cerr << std::flush << "Error loading firmware. Error code: " + << error_code << std::endl; + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + return ~0; + } + + std::cout << "Firmware load complete, releasing USB interface..." + << std::endl; + + } else if (vm.count("load-fpga")) { + error_code = (libusb_error) load_fpga(dev_handle, fpga_file); + + if(error_code != 0) { + std::cerr << std::flush << "Error loading FPGA. Error code: " + << error_code << std::endl; + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + return ~0; + } + + std::cout << "FPGA load complete, releasing USB interface..." + << std::endl; + + } else { + std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; + libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + return ~0; + } + + std::cout << std::endl << "Reactor Shutting Down..." << std::endl; + + error_code = (libusb_error) libusb_release_interface(dev_handle, 0); + libusb_close(dev_handle); + libusb_exit(ctx); + + return 0; +} + diff --git a/host/utils/fx2_init_eeprom.cpp b/host/utils/fx2_init_eeprom.cpp index c210ae575..701092a5d 100644 --- a/host/utils/fx2_init_eeprom.cpp +++ b/host/utils/fx2_init_eeprom.cpp @@ -41,12 +41,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); + po::notify(vm); //print the help message if (vm.count("help")){ std::cout << boost::format("USRP EEPROM initialization %s") % desc << std::endl; - return ~0; + return EXIT_FAILURE; } //cant find a uninitialized usrp with this mystery module in the way... @@ -76,7 +76,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (found_addrs.size() == 0){ std::cerr << "No USRP devices found" << std::endl; - return ~0; + return EXIT_FAILURE; } for (size_t i = 0; i < found_addrs.size(); i++){ @@ -89,5 +89,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "Power-cycle the usrp for the changes to take effect." << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp index 8e34bb3d5..91088112c 100644 --- a/host/utils/query_gpsdo_sensors.cpp +++ b/host/utils/query_gpsdo_sensors.cpp @@ -27,6 +27,7 @@ #include <boost/thread.hpp> #include <string> #include <cmath> +#include <ctime> #include <cstdlib> namespace po = boost::program_options; @@ -102,6 +103,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //Check PPS and compare UHD device time to GPS time boost::this_thread::sleep(boost::posix_time::seconds(1)); uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); + const time_t pc_clock_time = time(NULL); const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); if (last_pps_time.to_ticks(1.0) == gps_time.to_int()) { std::cout << boost::format("GPS and UHD Device time are aligned.\n"); @@ -114,6 +116,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); + std::cout << boost::format("PC Clock time: %.0f seconds\n") % pc_clock_time; //finished std::cout << boost::format("\nDone!\n\n"); diff --git a/host/utils/uhd-usrp.rules b/host/utils/uhd-usrp.rules index 56d9a8c43..2f5198d64 100644 --- a/host/utils/uhd-usrp.rules +++ b/host/utils/uhd-usrp.rules @@ -15,5 +15,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +#USRP1 SUBSYSTEMS=="usb", ATTRS{idVendor}=="fffe", ATTRS{idProduct}=="0002", MODE:="0666" + +#B100 SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0002", MODE:="0666" + +#B200 +SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0020", MODE:="0666" diff --git a/host/utils/uhd_cal_rx_iq_balance.cpp b/host/utils/uhd_cal_rx_iq_balance.cpp index 68d0443da..5fb494114 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -29,6 +29,7 @@ #include <complex> #include <cmath> #include <ctime> +#include <cstdlib> namespace po = boost::program_options; @@ -120,7 +121,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" << std::endl; - return ~0; + return EXIT_FAILURE; } //create a usrp device @@ -239,5 +240,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ store_results(usrp, results, "RX", "rx", "iq"); - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/uhd_cal_tx_dc_offset.cpp b/host/utils/uhd_cal_tx_dc_offset.cpp index 8f69b3ce1..c9cf757f4 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -123,7 +123,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" << std::endl; - return ~0; + return EXIT_FAILURE; } //create a usrp device @@ -237,5 +237,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ store_results(usrp, results, "TX", "tx", "dc"); - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/uhd_cal_tx_iq_balance.cpp b/host/utils/uhd_cal_tx_iq_balance.cpp index 5478b07e3..20d018edf 100644 --- a/host/utils/uhd_cal_tx_iq_balance.cpp +++ b/host/utils/uhd_cal_tx_iq_balance.cpp @@ -28,6 +28,7 @@ #include <iostream> #include <complex> #include <ctime> +#include <cstdlib> namespace po = boost::program_options; @@ -123,7 +124,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n" << std::endl; - return ~0; + return EXIT_FAILURE; } //create a usrp device @@ -242,5 +243,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ store_results(usrp, results, "TX", "tx", "iq"); - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/uhd_find_devices.cpp b/host/utils/uhd_find_devices.cpp index b778eeb68..c258c580e 100644 --- a/host/utils/uhd_find_devices.cpp +++ b/host/utils/uhd_find_devices.cpp @@ -20,6 +20,7 @@ #include <boost/program_options.hpp> #include <boost/format.hpp> #include <iostream> +#include <cstdlib> namespace po = boost::program_options; @@ -37,7 +38,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ std::cout << boost::format("UHD Find Devices %s") % desc << std::endl; - return ~0; + return EXIT_FAILURE; } //discover the usrps and print the results @@ -45,7 +46,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (device_addrs.size() == 0){ std::cerr << "No UHD Devices Found" << std::endl; - return ~0; + return EXIT_FAILURE; } for (size_t i = 0; i < device_addrs.size(); i++){ @@ -56,5 +57,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //uhd::device::make(device_addrs[i]); //test make } - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp index 5b3702fb4..98ed84850 100644 --- a/host/utils/uhd_usrp_probe.cpp +++ b/host/utils/uhd_usrp_probe.cpp @@ -30,6 +30,7 @@ #include <iostream> #include <sstream> #include <vector> +#include <cstdlib> namespace po = boost::program_options; using namespace uhd; @@ -114,13 +115,16 @@ static std::string get_dboard_pp_string(const std::string &type, property_tree:: ss << boost::format("%s Dboard: %s") % type % path.leaf() << std::endl; //ss << std::endl; const std::string prefix = (type == "RX")? "rx" : "tx"; - usrp::dboard_eeprom_t db_eeprom = tree->access<usrp::dboard_eeprom_t>(path / (prefix + "_eeprom")).get(); - if (db_eeprom.id != usrp::dboard_id_t::none()) ss << boost::format("ID: %s") % db_eeprom.id.to_pp_string() << std::endl; - if (not db_eeprom.serial.empty()) ss << boost::format("Serial: %s") % db_eeprom.serial << std::endl; - if (type == "TX"){ - usrp::dboard_eeprom_t gdb_eeprom = tree->access<usrp::dboard_eeprom_t>(path / "gdb_eeprom").get(); - if (gdb_eeprom.id != usrp::dboard_id_t::none()) ss << boost::format("ID: %s") % gdb_eeprom.id.to_pp_string() << std::endl; - if (not gdb_eeprom.serial.empty()) ss << boost::format("Serial: %s") % gdb_eeprom.serial << std::endl; + if (tree->exists(path / (prefix + "_eeprom"))) + { + usrp::dboard_eeprom_t db_eeprom = tree->access<usrp::dboard_eeprom_t>(path / (prefix + "_eeprom")).get(); + if (db_eeprom.id != usrp::dboard_id_t::none()) ss << boost::format("ID: %s") % db_eeprom.id.to_pp_string() << std::endl; + if (not db_eeprom.serial.empty()) ss << boost::format("Serial: %s") % db_eeprom.serial << std::endl; + if (type == "TX"){ + usrp::dboard_eeprom_t gdb_eeprom = tree->access<usrp::dboard_eeprom_t>(path / "gdb_eeprom").get(); + if (gdb_eeprom.id != usrp::dboard_id_t::none()) ss << boost::format("ID: %s") % gdb_eeprom.id.to_pp_string() << std::endl; + if (not gdb_eeprom.serial.empty()) ss << boost::format("Serial: %s") % gdb_eeprom.serial << std::endl; + } } BOOST_FOREACH(const std::string &name, tree->list(path / (prefix + "_frontends"))){ ss << make_border(get_frontend_pp_string(type, tree, path / (prefix + "_frontends") / name)); @@ -197,12 +201,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ std::cout << boost::format("UHD USRP Probe %s") % desc << std::endl; - return ~0; + return EXIT_FAILURE; } if (vm.count("version")){ std::cout << uhd::get_version_string() << std::endl; - return 0; + return EXIT_SUCCESS; } device::sptr dev = device::make(vm["args"].as<std::string>()); @@ -210,11 +214,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (vm.count("string")){ std::cout << tree->access<std::string>(vm["string"].as<std::string>()).get() << std::endl; - return 0; + return EXIT_SUCCESS; } if (vm.count("tree") != 0) print_tree("/", tree); else std::cout << make_border(get_device_pp_string(tree)) << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/usrp_burn_db_eeprom.cpp b/host/utils/usrp_burn_db_eeprom.cpp index b6b2dc4d6..3ca953115 100644 --- a/host/utils/usrp_burn_db_eeprom.cpp +++ b/host/utils/usrp_burn_db_eeprom.cpp @@ -58,7 +58,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ "Omit the ID argument to perform readback,\n" "Or specify a new ID to burn into the EEPROM.\n" ) << std::endl; - return ~0; + return EXIT_FAILURE; } //make the device and extract the dboard w/ property @@ -96,5 +96,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format(" Current revision: \"%s\"") % db_eeprom.revision << std::endl; std::cout << " Done" << std::endl << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/usrp_burn_mb_eeprom.cpp b/host/utils/usrp_burn_mb_eeprom.cpp index ca9a6c8ba..1b13fb615 100644 --- a/host/utils/usrp_burn_mb_eeprom.cpp +++ b/host/utils/usrp_burn_mb_eeprom.cpp @@ -47,7 +47,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ "Omit the value argument to perform a readback,\n" "Or specify a new value to burn into the EEPROM.\n" ) << std::endl; - return ~0; + return EXIT_FAILURE; } std::cout << "Creating USRP device from address: " + args << std::endl; @@ -60,7 +60,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::usrp::mboard_eeprom_t mb_eeprom = tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").get(); if (not mb_eeprom.has_key(key)){ std::cerr << boost::format("Cannot find value for EEPROM[%s]") % key << std::endl; - return ~0; + return EXIT_FAILURE; } std::cout << boost::format(" EEPROM [\"%s\"] is \"%s\"") % key % mb_eeprom[key] << std::endl; std::cout << std::endl; @@ -74,5 +74,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } std::cout << "Done" << std::endl; - return 0; + return EXIT_SUCCESS; } diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index bda6fc31b..bab6ddd91 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -77,6 +77,9 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ else if (tx_name.find("SBX") != std::string::npos){ usrp->set_tx_gain(0); } + else if (tx_name.find("CBX") != std::string::npos){ + usrp->set_tx_gain(0); + } else if (tx_name.find("RFX") != std::string::npos){ usrp->set_tx_gain(0); } @@ -92,6 +95,9 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ else if (rx_name.find("SBX") != std::string::npos){ usrp->set_rx_gain(25); } + else if (rx_name.find("CBX") != std::string::npos){ + usrp->set_rx_gain(25); + } else if (rx_name.find("RFX") != std::string::npos){ usrp->set_rx_gain(25); } diff --git a/images/Makefile b/images/Makefile index 562aa76c0..2e3bcfd26 100644 --- a/images/Makefile +++ b/images/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2013 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -52,6 +52,12 @@ ifeq ($(shell xtclsh -h > /dev/null 2>&1 && echo $$?),0) HAS_XTCLSH=1 endif +ifeq ($(shell avr-gcc --version > /dev/null 2>&1 && echo $$?),0) + ifeq ($(shell avr-objcopy --version > /dev/null 2>&1 && echo $$?),0) + HAS_AVR_UTILS=1 + endif +endif + ######################################################################## # USRP1 and B100 firmware ######################################################################## @@ -277,6 +283,22 @@ $(_usrp_e110_fpga_bin): $(GLOBAL_DEPS) endif ######################################################################## +# OctoClock firmware +######################################################################## + +ifdef HAS_AVR_UTILS + +_octoclock_fw_dir = $(TOP_FW_DIR)/octoclock +_octoclock_fw_bin = $(BUILT_IMAGES_DIR)/octoclock_fw.hex +IMAGES_LIST += $(_octoclock_fw_bin) + +$(_octoclock_fw_bin): $(GLOBAL_DEPS) + cd $(_octoclock_fw_dir) && make -f Makefile clean + cd $(_octoclock_fw_dir) && make -f Makefile all + cp $(TOP_FW_DIR)/octoclock/octoclock_fw.hex $@ +endif + +######################################################################## # Build rules ######################################################################## images: $(IMAGES_LIST) |