diff options
70 files changed, 8426 insertions, 1014 deletions
diff --git a/firmware/README.md b/firmware/README.md index 08340603d..c8ad9df16 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -16,6 +16,18 @@ __Build Instructions:__ 3. cmake `<source directory>` 4. make +## fx3/ + +__Description:__ This is the firmware for the FX3 USB PHY, and the AD9361 RFIC. + +__Devices:__ USRP B200 and USRP B210 + +__Tools:__ Cypress FX3 SDK + +__Build Instructions:__ + +Please see the `fx3/README.md` file for additional instructions. + ## octoclock/ __Description:__ Firmware for the Octoclock device. diff --git a/firmware/fx3/README.md b/firmware/fx3/README.md new file mode 100644 index 000000000..e2e8a13d4 --- /dev/null +++ b/firmware/fx3/README.md @@ -0,0 +1,84 @@ +INSTRUCTIONS +================================ + +# Building the B2xx FX3 Firmware + +The USRP B200 and B210 each use the Cypress FX3 USB3 PHY for USB3 connectivity. +This device has an ARM core on it, which is programmed in C. This README will +show you how to build our firmware source + +**A brief "Theory of Operations":** +The host sends commands to the FX3, our USB3 PHY, which has an on-board ARM +which runs the FX3 firmware code (hex file). That code translates commands into +SPI commands to/from the AD9361. The SPI lines run through the FPGA (bin or bit +file), where they are level-translated, and then head to the AD9361. Note that +the FPGA takes no action on these SPI lines. They are passive pass-throughs. + +## Setting up the Cypress SDK + +In order to compile the USRP B200 and B210 firmware, you will need the FX3 SDK +distributed by the FX3 manufacturer, Cypress Semiconductor. You can download the +[FX3 SDK from here](http://www.cypress.com/?rID=57990). + +Once you have downloaded it, extract the ARM cross-compiler sub-directory from +the zip file and put it somewhere useful. The highest level directory you need +is `arm-2011.03/`. + +Now that you have extracted the cross compilation toolchain, you need to set up +some environment variables to tell the B2xx `makefile` where to look for the +tools. These variables are: + +``` + $ export ARMGCC_INSTALL_PATH=<your path>/arm-2011.03 + $ export ARMGCC_VERSION=4.5.2 +``` + +Now, you'll need to set-up the Cypress SDK, as well. In the SDK, navigate to +the `firmware` directory, and copy the following sub-directories into +`uhd.git/firmware/fx3`: `common/`, `lpp_source/`, `u3p_firmware/`. + +Your directory structure should now look like: + +``` +uhd.git/ + | + --firmware/ + | + --fx3/ + | + --ad9361/ # From UHD + --b200/ # From UHD + --common/ # From Cypress SDK + --gpif2_designer/ # From UHD + --lpp_source/ # From Cypress SDK + --u3p_firmware/ # From Cypress SDK + --README.md # From UHD +``` + + +## Applying the Patch to the Toolchain + +Now, you'll need to apply a patch to a couple of files in the Cypress SDK. Head +into the `common/` directory you just copied from the Cypress SDK, and apply the +patch `b200/fx3_mem_map.patch`. + +``` + # cd uhd.git/firmware/common/ + $ patch -p2 < ../b200/fx3_mem_map.patch +``` + +If you don't see any errors print on the screen, then the patch was successful. + +## Building the Firmware + +Now, you should be able to head into the `b200/` directory and simply build the +firmware: + +``` + $ cd uhd.git/firmware/fx3/b200 + $ make +``` + +It will generate a `usrp_b200_fw.hex` file, which you can then give to UHD to +program your USRP B200 or USRP B210. + diff --git a/firmware/fx3/ad9361/include/ad9361_dispatch.h b/firmware/fx3/ad9361/include/ad9361_dispatch.h new file mode 100644 index 000000000..e89a4e0b0 --- /dev/null +++ b/firmware/fx3/ad9361/include/ad9361_dispatch.h @@ -0,0 +1,16 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef INCLUDED_AD9361_DISPATCH_H +#define INCLUDED_AD9361_DISPATCH_H + +#include <ad9361_transaction.h> + +extern void ad9361_dispatch(const char* request, char* response); + +typedef void (*msgfn)(const char*, ...); + +extern void ad9361_set_msgfn(msgfn pfn); + +#endif /* INCLUDED_AD9361_DISPATCH_H */ diff --git a/firmware/fx3/ad9361/include/ad9361_transaction.h b/firmware/fx3/ad9361/include/ad9361_transaction.h new file mode 100644 index 000000000..2349a5d3d --- /dev/null +++ b/firmware/fx3/ad9361/include/ad9361_transaction.h @@ -0,0 +1,90 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#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_DISPATCH_PACKET_SIZE 64 + +//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; + +#define AD9361_TRANSACTION_MAX_ERROR_MSG (AD9361_DISPATCH_PACKET_SIZE - (sizeof(ad9361_transaction_t)-4)-1) // -4 for 'error_msg' alignment padding, -1 for terminating \0 + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_AD9361_TRANSACTION_H */ diff --git a/firmware/fx3/ad9361/lib/ad9361_filter_taps.h b/firmware/fx3/ad9361/lib/ad9361_filter_taps.h new file mode 100644 index 000000000..afbe27630 --- /dev/null +++ b/firmware/fx3/ad9361/lib/ad9361_filter_taps.h @@ -0,0 +1,47 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef INCLUDED_AD9361_FILTER_TAPS_HPP +#define INCLUDED_AD9361_FILTER_TAPS_HPP + +/* A default 128-tap filter that can be used for generic circumstances. */ +static uint16_t default_128tap_coeffs[] = { + 0x0001,0xfff1,0xffcf,0xffc0,0xffe8,0x0020,0x001a,0xffe3, + 0xffe1,0x001f,0x0028,0xffdf,0xffcc,0x0024,0x0043,0xffdb, + 0xffac,0x0026,0x0068,0xffdb,0xff80,0x0022,0x009a,0xffe2, + 0xff47,0x0017,0x00db,0xfff3,0xfeff,0xffff,0x012b,0x0013, + 0xfea5,0xffd7,0x0190,0x0046,0xfe35,0xff97,0x020e,0x0095, + 0xfda7,0xff36,0x02ae,0x010d,0xfcf0,0xfea1,0x0383,0x01c6, + 0xfbf3,0xfdb6,0x04b7,0x02f8,0xfa6d,0xfc1a,0x06be,0x0541, + 0xf787,0xf898,0x0b60,0x0b6d,0xee88,0xea40,0x2786,0x7209 +}; + + +/* The below pair of filters is optimized for a 10MHz LTE application. */ +/* +static uint16_t lte10mhz_rx_coeffs[] = { + 0xffe2,0x0042,0x0024,0x0095,0x0056,0x004d,0xffcf,0xffb7, + 0xffb1,0x0019,0x0059,0x006a,0x0004,0xff9d,0xff72,0xffd4, + 0x0063,0x00b7,0x0062,0xffac,0xff21,0xff59,0x0032,0x0101, + 0x00f8,0x0008,0xfeea,0xfeac,0xffa3,0x0117,0x01b5,0x00d0, + 0xff05,0xfdea,0xfe9e,0x00ba,0x026f,0x0215,0xffb5,0xfd4a, + 0xfd18,0xffa0,0x02de,0x03dc,0x0155,0xfd2a,0xfb0d,0xfd54, + 0x0287,0x062f,0x048a,0xfe37,0xf862,0xf8c1,0x004d,0x0963, + 0x0b88,0x02a4,0xf3e7,0xebdd,0xf5f8,0x1366,0x3830,0x518b +}; + +static uint16_t lte10mhz_tx_coeffs[] = { + 0xfffb,0x0000,0x0004,0x0017,0x0024,0x0028,0x0013,0xfff3, + 0xffdc,0xffe5,0x000b,0x0030,0x002e,0xfffe,0xffc4,0xffb8, + 0xfff0,0x0045,0x0068,0x002b,0xffb6,0xff72,0xffad,0x0047, + 0x00b8,0x0088,0xffc8,0xff1c,0xff33,0x001a,0x0110,0x0124, + 0x0019,0xfec8,0xfe74,0xff9a,0x0156,0x0208,0x00d3,0xfe9b, + 0xfd68,0xfe96,0x015d,0x033f,0x0236,0xfecd,0xfc00,0xfcb5, + 0x00d7,0x04e5,0x04cc,0xffd5,0xf9fe,0xf8fb,0xfef2,0x078c, + 0x0aae,0x036d,0xf5c0,0xed89,0xf685,0x12af,0x36a4,0x4faa +}; +*/ + + +#endif // INCLUDED_AD9361_FILTER_TAPS_HPP diff --git a/firmware/fx3/ad9361/lib/ad9361_gain_tables.h b/firmware/fx3/ad9361/lib/ad9361_gain_tables.h new file mode 100644 index 000000000..58dcbeb65 --- /dev/null +++ b/firmware/fx3/ad9361/lib/ad9361_gain_tables.h @@ -0,0 +1,95 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef INCLUDED_AD9361_GAIN_TABLES_HPP +#define INCLUDED_AD9361_GAIN_TABLES_HPP + +uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, + {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, + {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, + {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, + {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, + {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, + {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, + {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, + {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, + {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, + {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, + {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, + {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, + {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, + {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, + {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, + {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, + {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, + {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, + {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, + {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, + {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, + {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, + {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, + {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, + {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, + {76,0x6F,0x38,0x20,1}}; + + +uint8_t gain_table_1300mhz_to_4000mhz[77][5] = { {0,0x00,0x00,0x20,1}, + {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, + {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, + {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, + {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, + {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, + {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, + {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, + {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, + {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, + {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, + {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, + {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, + {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, + {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, + {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, + {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, + {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, + {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, + {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, + {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, + {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, + {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, + {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, + {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, + {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, + {76,0x6F,0x38,0x20,1}}; + + +uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, + {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, + {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, + {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, + {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, + {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, + {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, + {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, + {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, + {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, + {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, + {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, + {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, + {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, + {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, + {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, + {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, + {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, + {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, + {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, + {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, + {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, + {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, + {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, + {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, + {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, + {76,0x6F,0x38,0x20,1}}; + + +#endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/firmware/fx3/ad9361/lib/ad9361_impl.c b/firmware/fx3/ad9361/lib/ad9361_impl.c new file mode 100644 index 000000000..61512d2c8 --- /dev/null +++ b/firmware/fx3/ad9361/lib/ad9361_impl.c @@ -0,0 +1,1918 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +/* This file implements b200 vendor requests handler + * It handles ad9361 setup and configuration + */ + +#include <stdarg.h> +#include <stdio.h> +#include <math.h> + +#include <ad9361_transaction.h> +#include "ad9361_filter_taps.h" +#include "ad9361_gain_tables.h" +#include "ad9361_synth_lut.h" +#include "ad9361_dispatch.h" + +//////////////////////////////////////////////////////////// + +static void fake_msg(const char* str, ...) +{ +} + +static msgfn _msgfn = fake_msg; + +//extern void msg(const char* str, ...); External object must provide this symbol +#define msg (_msgfn) + +void ad9361_set_msgfn(msgfn pfn) +{ + _msgfn = pfn; +} + +//////////////////////////////////////////////////////////// +#define AD9361_MAX_GAIN 89.75 + +#define DOUBLE_PI 3.14159265359 +#define DOUBLE_LN_2 0.693147181 + +#define RX_TYPE 0 +#define TX_TYPE 1 + +#ifndef AD9361_CLOCKING_MODE +#error define a AD9361_CLOCKING_MODE +#endif + +#ifndef AD9361_RX_BAND_EDGE0 +#error define a AD9361_RX_BAND_EDGE0 +#endif + +#ifndef AD9361_RX_BAND_EDGE1 +#error define a AD9361_RX_BAND_EDGE1 +#endif + +#ifndef AD9361_TX_BAND_EDGE +#error define a AD9361_TX_BAND_EDGE +#endif + +//////////////////////////////////////////////////////////// +// the following macros evaluate to a compile time constant +// macros By Tom Torfs - donated to the public domain + +/* turn a numeric literal into a hex constant +(avoids problems with leading zeroes) +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) + +/* *** user macros *** */ + +/* for upto 8-bit binary constants */ +#define B8(d) ((unsigned char)B8__(HEX__(d))) + +//////////////////////////////////////////////////////////// +// shadow registers +static uint8_t reg_vcodivs; +static uint8_t reg_inputsel; +static uint8_t reg_rxfilt; +static uint8_t reg_txfilt; +static uint8_t reg_bbpll; +static uint8_t reg_bbftune_config; +static uint8_t reg_bbftune_mode; + +//////////////////////////////////////////////////////////// +// other private data fields for VRQ handler +static double _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; +static double _baseband_bw, _bbpll_freq, _adcclock_freq; +static double _req_clock_rate, _req_coreclk; +static uint16_t _rx_bbf_tunediv; +static uint8_t _curr_gain_table; +static uint32_t _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain; +static int _tfir_factor; + +double set_gain(int which, int n, const double value); +void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2); +/*********************************************************************** + * Placeholders, unused, or test functions + **********************************************************************/ +static char *tmp_req_buffer; + +void post_err_msg(const char* error) +{ + msg("[AD9361 error] %s", error); + + if (!tmp_req_buffer) + return; + + ad9361_transaction_t *request = (ad9361_transaction_t *)tmp_req_buffer; + strncpy(request->error_msg, error, (AD9361_TRANSACTION_MAX_ERROR_MSG + 1)); // '+ 1' as length excludes terminating NUL + request->error_msg[AD9361_TRANSACTION_MAX_ERROR_MSG] = '\0'; // If string was too long, NUL will not be copied, so force one just in case +} + +void write_ad9361_reg(uint32_t reg, uint8_t val) +{ + ad9361_transact_spi((reg << 8) | val | (1 << 23)); +} + +uint8_t read_ad9361_reg(uint32_t reg) +{ + return ad9361_transact_spi((reg << 8)) & 0xff; +} + +//shortcuts for double packer/unpacker function +#define double_pack ad9361_double_pack +#define double_unpack ad9361_double_unpack + +/* Make Catalina output its test tone. */ +void output_test_tone(void) { + /* Output a 480 kHz tone at 800 MHz */ + write_ad9361_reg(0x3F4, 0x0B); + write_ad9361_reg(0x3FC, 0xFF); + write_ad9361_reg(0x3FD, 0xFF); + write_ad9361_reg(0x3FE, 0x3F); +} + +/* Turn on/off Catalina's TX port --> RX port loopback. */ +void data_port_loopback(const int on) { + msg("[data_port_loopback] Enabled: %d", on); + write_ad9361_reg(0x3F5, (on ? 0x01 : 0x00)); +} + +/* This is a simple comparison for very large double-precision floating + * point numbers. It is used to prevent re-tunes for frequencies that are + * the same but not 'exactly' because of data precision issues. */ +// TODO: see if we can avoid the need for this function +int freq_is_nearly_equal(double a, double b) { + return AD9361_MAX(a,b) - AD9361_MIN(a,b) < 1; +} + +/*********************************************************************** + * Filter functions + **********************************************************************/ + +/* This function takes in the calculated maximum number of FIR taps, and + * returns a number of taps that makes Catalina happy. */ +int get_num_taps(int max_num_taps) { + + int num_taps = 0; + int num_taps_list[] = {16, 32, 48, 64, 80, 96, 112, 128}; + int i; + for(i = 1; i < 8; i++) { + if(max_num_taps >= num_taps_list[i]) { + continue; + } else { + num_taps = num_taps_list[i - 1]; + break; + } + } if(num_taps == 0) { num_taps = 128; } + + return num_taps; +} + +/* Program either the RX or TX FIR filter. + * + * The process is the same for both filters, but the function must be told + * how many taps are in the filter, and given a vector of the taps + * themselves. Note that the filters are symmetric, so value of 'num_taps' + * should actually be twice the length of the tap vector. */ +void program_fir_filter(int which, int num_taps, \ + uint16_t *coeffs) { + + uint16_t base; + if(which == RX_TYPE) { + base = 0x0f0; + write_ad9361_reg(base+6, 0x02); //filter gain + } else { + base = 0x060; + } + + /* Write the filter configuration. */ + uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + + /* Turn on the filter clock. */ + write_ad9361_reg(base+5, reg_numtaps | 0x1a); + ad9361_msleep(1); + + int num_unique_coeffs = (num_taps / 2); + + /* The filters are symmetric, so iterate over the tap vector, + * programming each index, and then iterate backwards, repeating the + * process. */ + int addr; + for(addr=0; addr < num_unique_coeffs; addr++) { + write_ad9361_reg(base+0, addr); + write_ad9361_reg(base+1, (coeffs[addr]) & 0xff); + write_ad9361_reg(base+2, (coeffs[addr] >> 8) & 0xff); + write_ad9361_reg(base+5, 0xfe); + write_ad9361_reg(base+4, 0x00); + write_ad9361_reg(base+4, 0x00); + } + + for(addr=0; addr < num_unique_coeffs; addr++) { + write_ad9361_reg(base+0, addr+num_unique_coeffs); + write_ad9361_reg(base+1, (coeffs[num_unique_coeffs-1-addr]) & 0xff); + write_ad9361_reg(base+2, (coeffs[num_unique_coeffs-1-addr] >> 8) & 0xff); + write_ad9361_reg(base+5, 0xfe); + write_ad9361_reg(base+4, 0x00); + write_ad9361_reg(base+4, 0x00); + } + + /* Disable the filter clock. */ + write_ad9361_reg(base+5, 0xf8); +} + +/* Program the RX FIR Filter. */ +void setup_rx_fir(int total_num_taps) { + int num_taps = total_num_taps / 2; + uint16_t coeffs[num_taps]; + int i; + for(i = 0; i < num_taps; i++) { + coeffs[num_taps - 1 - i] = default_128tap_coeffs[63 - i]; + } + + program_fir_filter(RX_TYPE, total_num_taps, coeffs); +} + +/* Program the TX FIR Filter. */ +void setup_tx_fir(int total_num_taps) { + int num_taps = total_num_taps / 2; + uint16_t coeffs[num_taps]; + int i; + for(i = 0; i < num_taps; i++) { + coeffs[num_taps - 1 - i] = default_128tap_coeffs[63 - i]; + } + + program_fir_filter(TX_TYPE, total_num_taps, coeffs); +} + +/*********************************************************************** + * Calibration functions + ***********************************************************************/ + +/* Calibrate and lock the BBPLL. + * + * This function should be called anytime the BBPLL is tuned. */ +void calibrate_lock_bbpll() { + write_ad9361_reg(0x03F, 0x05); // Start the BBPLL calibration + write_ad9361_reg(0x03F, 0x01); // Clear the 'start' bit + + /* Increase BBPLL KV and phase margin. */ + write_ad9361_reg(0x04c, 0x86); + write_ad9361_reg(0x04d, 0x01); + write_ad9361_reg(0x04d, 0x05); + + /* Wait for BBPLL lock. */ + int count = 0; + while(!(read_ad9361_reg(0x05e) & 0x80)) { + if(count > 1000) { + post_err_msg("BBPLL not locked"); + break; + } + + count++; + ad9361_msleep(2); + } +} + +/* Calibrate the synthesizer charge pumps. + * + * Technically, this calibration only needs to be done once, at device + * initialization. */ +void calibrate_synth_charge_pumps() { + /* If this function ever gets called, and the ENSM isn't already in the + * ALERT state, then something has gone horribly wrong. */ + if((read_ad9361_reg(0x017) & 0x0F) != 5) { + post_err_msg("Catalina not in ALERT during cal"); + } + + /* Calibrate the RX synthesizer charge pump. */ + int count = 0; + write_ad9361_reg(0x23d, 0x04); + while(!(read_ad9361_reg(0x244) & 0x80)) { + if(count > 5) { + post_err_msg("RX charge pump cal failure"); + break; + } + + count++; + ad9361_msleep(1); + } + write_ad9361_reg(0x23d, 0x00); + + /* Calibrate the TX synthesizer charge pump. */ + count = 0; + write_ad9361_reg(0x27d, 0x04); + while(!(read_ad9361_reg(0x284) & 0x80)) { + if(count > 5) { + post_err_msg("TX charge pump cal failure"); + break; + } + + count++; + ad9361_msleep(1); + } + write_ad9361_reg(0x27d, 0x00); +} + +/* Calibrate the analog BB RX filter. + * + * Note that the filter calibration depends heavily on the baseband + * bandwidth, so this must be re-done after any change to the RX sample + * rate. */ +double calibrate_baseband_rx_analog_filter() { + /* For filter tuning, baseband BW is half the complex BW, and must be + * between 28e6 and 0.2e6. */ + double bbbw = _baseband_bw / 2.0; + if(bbbw > 28e6) { + bbbw = 28e6; + } else if (bbbw < 0.20e6) { + bbbw = 0.20e6; + } + + double rxtune_clk = ((1.4 * bbbw * 2 * + DOUBLE_PI) / DOUBLE_LN_2); + + _rx_bbf_tunediv = AD9361_MIN(511, AD9361_CEIL_INT(_bbpll_freq / rxtune_clk)); + + reg_bbftune_config = (reg_bbftune_config & 0xFE) \ + | ((_rx_bbf_tunediv >> 8) & 0x0001); + + double bbbw_mhz = bbbw / 1e6; + + double temp = ((bbbw_mhz - AD9361_FLOOR_INT(bbbw_mhz)) * 1000) / 7.8125; + uint8_t bbbw_khz = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(temp + 0.5))); + + /* Set corner frequencies and dividers. */ + write_ad9361_reg(0x1fb, (uint8_t)(bbbw_mhz)); + write_ad9361_reg(0x1fc, bbbw_khz); + write_ad9361_reg(0x1f8, (_rx_bbf_tunediv & 0x00FF)); + write_ad9361_reg(0x1f9, reg_bbftune_config); + + /* RX Mix Voltage settings - only change with apps engineer help. */ + write_ad9361_reg(0x1d5, 0x3f); + write_ad9361_reg(0x1c0, 0x03); + + /* Enable RX1 & RX2 filter tuners. */ + write_ad9361_reg(0x1e2, 0x02); + write_ad9361_reg(0x1e3, 0x02); + + /* Run the calibration! */ + int count = 0; + write_ad9361_reg(0x016, 0x80); + while(read_ad9361_reg(0x016) & 0x80) { + if(count > 100) { + post_err_msg("RX baseband filter cal FAILURE"); + break; + } + + count++; + ad9361_msleep(1); + } + + /* Disable RX1 & RX2 filter tuners. */ + write_ad9361_reg(0x1e2, 0x03); + write_ad9361_reg(0x1e3, 0x03); + + return bbbw; +} + +/* Calibrate the analog BB TX filter. + * + * Note that the filter calibration depends heavily on the baseband + * bandwidth, so this must be re-done after any change to the TX sample + * rate. */ +double calibrate_baseband_tx_analog_filter() { + /* For filter tuning, baseband BW is half the complex BW, and must be + * between 28e6 and 0.2e6. */ + double bbbw = _baseband_bw / 2.0; + if(bbbw > 20e6) { + bbbw = 20e6; + } else if (bbbw < 0.625e6) { + bbbw = 0.625e6; + } + + double txtune_clk = ((1.6 * bbbw * 2 * + DOUBLE_PI) / DOUBLE_LN_2); + + uint16_t txbbfdiv = AD9361_MIN(511, (AD9361_CEIL_INT(_bbpll_freq / txtune_clk))); + + reg_bbftune_mode = (reg_bbftune_mode & 0xFE) \ + | ((txbbfdiv >> 8) & 0x0001); + + /* Program the divider values. */ + write_ad9361_reg(0x0d6, (txbbfdiv & 0x00FF)); + write_ad9361_reg(0x0d7, reg_bbftune_mode); + + /* Enable the filter tuner. */ + write_ad9361_reg(0x0ca, 0x22); + + /* Calibrate! */ + int count = 0; + write_ad9361_reg(0x016, 0x40); + while(read_ad9361_reg(0x016) & 0x40) { + if(count > 100) { + post_err_msg("TX baseband filter cal FAILURE"); + break; + } + + count++; + ad9361_msleep(1); + } + + /* Disable the filter tuner. */ + write_ad9361_reg(0x0ca, 0x26); + + return bbbw; +} + +/* Calibrate the secondary TX filter. + * + * This filter also depends on the TX sample rate, so if a rate change is + * made, the previous calibration will no longer be valid. */ +void calibrate_secondary_tx_filter() { + /* For filter tuning, baseband BW is half the complex BW, and must be + * between 20e6 and 0.53e6. */ + double bbbw = _baseband_bw / 2.0; + if(bbbw > 20e6) { + bbbw = 20e6; + } else if (bbbw < 0.53e6) { + bbbw = 0.53e6; + } + + double bbbw_mhz = bbbw / 1e6; + + /* Start with a resistor value of 100 Ohms. */ + int res = 100; + + /* Calculate target corner frequency. */ + double corner_freq = 5 * bbbw_mhz * 2 * DOUBLE_PI; + + /* Iterate through RC values to determine correct combination. */ + int cap = 0; + int i; + for(i = 0; i <= 3; i++) { + cap = (AD9361_FLOOR_INT(0.5 + (( 1 / ((corner_freq * res) * 1e6)) * 1e12))) - 12; + + if(cap <= 63) { + break; + } + + res = res * 2; + } + if(cap > 63) { + cap = 63; + } + + uint8_t reg0d0, reg0d1, reg0d2; + + /* Translate baseband bandwidths to register settings. */ + if((bbbw_mhz * 2) <= 9) { + reg0d0 = 0x59; + } else if(((bbbw_mhz * 2) > 9) && ((bbbw_mhz * 2) <= 24)) { + reg0d0 = 0x56; + } else if((bbbw_mhz * 2) > 24) { + reg0d0 = 0x57; + } else { + post_err_msg("Cal2ndTxFil: INVALID_CODE_PATH bad bbbw_mhz"); + reg0d0 = 0x00; + } + + /* Translate resistor values to register settings. */ + if(res == 100) { + reg0d1 = 0x0c; + } else if(res == 200) { + reg0d1 = 0x04; + } else if(res == 400) { + reg0d1 = 0x03; + } else if(res == 800) { + reg0d1 = 0x01; + } else { + reg0d1 = 0x0c; + } + + reg0d2 = cap; + + /* Program the above-calculated values. Sweet. */ + write_ad9361_reg(0x0d2, reg0d2); + write_ad9361_reg(0x0d1, reg0d1); + write_ad9361_reg(0x0d0, reg0d0); +} + +/* Calibrate the RX TIAs. + * + * Note that the values in the TIA register, after calibration, vary with + * the RX gain settings. */ +void calibrate_rx_TIAs() { + + uint8_t reg1eb = read_ad9361_reg(0x1eb) & 0x3F; + uint8_t reg1ec = read_ad9361_reg(0x1ec) & 0x7F; + uint8_t reg1e6 = read_ad9361_reg(0x1e6) & 0x07; + uint8_t reg1db = 0x00; + uint8_t reg1dc = 0x00; + uint8_t reg1dd = 0x00; + uint8_t reg1de = 0x00; + uint8_t reg1df = 0x00; + + /* For calibration, baseband BW is half the complex BW, and must be + * between 28e6 and 0.2e6. */ + double bbbw = _baseband_bw / 2.0; + if(bbbw > 20e6) { + bbbw = 20e6; + } else if (bbbw < 0.20e6) { + bbbw = 0.20e6; + } + double ceil_bbbw_mhz = AD9361_CEIL_INT(bbbw / 1e6); + + /* Do some crazy resistor and capacitor math. */ + int Cbbf = (reg1eb * 160) + (reg1ec * 10) + 140; + int R2346 = 18300 * (reg1e6 & 0x07); + double CTIA_fF = (Cbbf * R2346 * 0.56) / 3500; + + /* Translate baseband BW to register settings. */ + if(ceil_bbbw_mhz <= 3) { + reg1db = 0xe0; + } else if((ceil_bbbw_mhz > 3) && (ceil_bbbw_mhz <= 10)) { + reg1db = 0x60; + } else if(ceil_bbbw_mhz > 10) { + reg1db = 0x20; + } else { + post_err_msg("CalRxTias: INVALID_CODE_PATH bad bbbw_mhz"); + } + + if(CTIA_fF > 2920) { + reg1dc = 0x40; + reg1de = 0x40; + + uint8_t temp = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(0.5 + ((CTIA_fF - 400.0) / 320.0)))); + reg1dd = temp; + reg1df = temp; + } else { + uint8_t temp = (uint8_t) AD9361_FLOOR_INT(0.5 + ((CTIA_fF - 400.0) / 40.0)) + 0x40; + reg1dc = temp; + reg1de = temp; + reg1dd = 0; + reg1df = 0; + } + + /* w00t. Settings calculated. Program them and roll out. */ + write_ad9361_reg(0x1db, reg1db); + write_ad9361_reg(0x1dd, reg1dd); + write_ad9361_reg(0x1df, reg1df); + write_ad9361_reg(0x1dc, reg1dc); + write_ad9361_reg(0x1de, reg1de); +} + +/* Setup the Catalina ADC. + * + * There are 40 registers that control the ADC's operation, most of the + * values of which must be derived mathematically, dependent on the current + * setting of the BBPLL. Note that the order of calculation is critical, as + * some of the 40 registers depend on the values in others. */ +void setup_adc() { + double bbbw_mhz = (((_bbpll_freq / 1e6) / _rx_bbf_tunediv) * DOUBLE_LN_2) \ + / (1.4 * 2 * DOUBLE_PI); + + /* For calibration, baseband BW is half the complex BW, and must be + * between 28e6 and 0.2e6. */ + if(bbbw_mhz > 28) { + bbbw_mhz = 28; + } else if (bbbw_mhz < 0.20) { + bbbw_mhz = 0.20; + } + + uint8_t rxbbf_c3_msb = read_ad9361_reg(0x1eb) & 0x3F; + uint8_t rxbbf_c3_lsb = read_ad9361_reg(0x1ec) & 0x7F; + uint8_t rxbbf_r2346 = read_ad9361_reg(0x1e6) & 0x07; + + double fsadc = _adcclock_freq / 1e6; + + /* Sort out the RC time constant for our baseband bandwidth... */ + double rc_timeconst = 0.0; + if(bbbw_mhz < 18) { + rc_timeconst = (1 / ((1.4 * 2 * DOUBLE_PI) \ + * (18300 * rxbbf_r2346) + * ((160e-15 * rxbbf_c3_msb) + + (10e-15 * rxbbf_c3_lsb) + 140e-15) + * (bbbw_mhz * 1e6))); + } else { + rc_timeconst = (1 / ((1.4 * 2 * DOUBLE_PI) \ + * (18300 * rxbbf_r2346) + * ((160e-15 * rxbbf_c3_msb) + + (10e-15 * rxbbf_c3_lsb) + 140e-15) + * (bbbw_mhz * 1e6) * (1 + (0.01 * (bbbw_mhz - 18))))); + } + + double scale_res = ad9361_sqrt(1 / rc_timeconst); + double scale_cap = ad9361_sqrt(1 / rc_timeconst); + + double scale_snr = (_adcclock_freq < 80e6) ? 1.0 : 1.584893192; + double maxsnr = 640 / 160; + + /* Calculate the values for all 40 settings registers. + * + * DO NOT TOUCH THIS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING. kthx.*/ + uint8_t data[40]; + data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0x24; + data[4] = 0x24; data[5] = 0; data[6] = 0; + data[7] = (uint8_t) AD9361_MIN(124, (AD9361_FLOOR_INT(-0.5 + + (80.0 * scale_snr * scale_res + * AD9361_MIN(1.0, ad9361_sqrt(maxsnr * fsadc / 640.0)))))); + double data007 = data[7]; + data[8] = (uint8_t) AD9361_MIN(255, (AD9361_FLOOR_INT(0.5 + + ((20.0 * (640.0 / fsadc) * ((data007 / 80.0)) + / (scale_res * scale_cap)))))); + data[10] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(-0.5 + (77.0 * scale_res + * AD9361_MIN(1.0, ad9361_sqrt(maxsnr * fsadc / 640.0)))))); + double data010 = data[10]; + data[9] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(0.8 * data010))); + data[11] = (uint8_t) AD9361_MIN(255, (AD9361_FLOOR_INT(0.5 + + (20.0 * (640.0 / fsadc) * ((data010 / 77.0) + / (scale_res * scale_cap)))))); + data[12] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(-0.5 + + (80.0 * scale_res * AD9361_MIN(1.0, + ad9361_sqrt(maxsnr * fsadc / 640.0)))))); + double data012 = data[12]; + data[13] = (uint8_t) AD9361_MIN(255, (AD9361_FLOOR_INT(-1.5 + + (20.0 * (640.0 / fsadc) * ((data012 / 80.0) + / (scale_res * scale_cap)))))); + data[14] = 21 * (uint8_t)(AD9361_FLOOR_INT(0.1 * 640.0 / fsadc)); + data[15] = (uint8_t) AD9361_MIN(127, (1.025 * data007)); + double data015 = data[15]; + data[16] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT((data015 + * (0.98 + (0.02 * AD9361_MAX(1.0, + (640.0 / fsadc) / maxsnr))))))); + data[17] = data[15]; + data[18] = (uint8_t) AD9361_MIN(127, (0.975 * (data010))); + double data018 = data[18]; + data[19] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT((data018 + * (0.98 + (0.02 * AD9361_MAX(1.0, + (640.0 / fsadc) / maxsnr))))))); + data[20] = data[18]; + data[21] = (uint8_t) AD9361_MIN(127, (0.975 * data012)); + double data021 = data[21]; + data[22] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT((data021 + * (0.98 + (0.02 * AD9361_MAX(1.0, + (640.0 / fsadc) / maxsnr))))))); + data[23] = data[21]; + data[24] = 0x2e; + data[25] = (uint8_t)(AD9361_FLOOR_INT(128.0 + AD9361_MIN(63.0, + 63.0 * (fsadc / 640.0)))); + data[26] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, 63.0 * (fsadc / 640.0) + * (0.92 + (0.08 * (640.0 / fsadc)))))); + data[27] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, + 32.0 * ad9361_sqrt(fsadc / 640.0)))); + data[28] = (uint8_t)(AD9361_FLOOR_INT(128.0 + AD9361_MIN(63.0, + 63.0 * (fsadc / 640.0)))); + data[29] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, + 63.0 * (fsadc / 640.0) + * (0.92 + (0.08 * (640.0 / fsadc)))))); + data[30] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, + 32.0 * ad9361_sqrt(fsadc / 640.0)))); + data[31] = (uint8_t)(AD9361_FLOOR_INT(128.0 + AD9361_MIN(63.0, + 63.0 * (fsadc / 640.0)))); + data[32] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, + 63.0 * (fsadc / 640.0) * (0.92 + + (0.08 * (640.0 / fsadc)))))); + data[33] = (uint8_t)(AD9361_FLOOR_INT(AD9361_MIN(63.0, + 63.0 * ad9361_sqrt(fsadc / 640.0)))); + data[34] = (uint8_t) AD9361_MIN(127, (AD9361_FLOOR_INT(64.0 + * ad9361_sqrt(fsadc / 640.0)))); + data[35] = 0x40; + data[36] = 0x40; + data[37] = 0x2c; + data[38] = 0x00; + data[39] = 0x00; + + /* Program the registers! */ + int i; + for(i=0; i<40; i++) { + write_ad9361_reg(0x200+i, data[i]); + } + +} + +/* Calibrate the baseband DC offset. + * + * Note that this function is called from within the TX quadrature + * calibration function! */ +void calibrate_baseband_dc_offset() { + write_ad9361_reg(0x193, 0x3f); // Calibration settings + write_ad9361_reg(0x190, 0x0f); // Set tracking coefficient + //write_ad9361_reg(0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift + write_ad9361_reg(0x194, 0x01); // More calibration settings + + /* Start that calibration, baby. */ + int count = 0; + write_ad9361_reg(0x016, 0x01); + while(read_ad9361_reg(0x016) & 0x01) { + if(count > 100) { + post_err_msg("Baseband DC Offset Calibration Failure"); + break; + } + + count++; + ad9361_msleep(5); + } +} + +/* Calibrate the RF DC offset. + * + * Note that this function is called from within the TX quadrature + * calibration function. */ +void calibrate_rf_dc_offset() { + /* Some settings are frequency-dependent. */ + if(_rx_freq < 4e9) { + write_ad9361_reg(0x186, 0x32); // RF DC Offset count + write_ad9361_reg(0x187, 0x24); + write_ad9361_reg(0x188, 0x05); + } else { + write_ad9361_reg(0x186, 0x28); // RF DC Offset count + write_ad9361_reg(0x187, 0x34); + write_ad9361_reg(0x188, 0x06); + } + + write_ad9361_reg(0x185, 0x20); // RF DC Offset wait count + write_ad9361_reg(0x18b, 0x83); + write_ad9361_reg(0x189, 0x30); + + /* Run the calibration! */ + int count = 0; + write_ad9361_reg(0x016, 0x02); + while(read_ad9361_reg(0x016) & 0x02) { + if(count > 100) { + post_err_msg("RF DC Offset Calibration Failure"); + break; + } + + count++; + ad9361_msleep(50); + } +} + +/* Start the RX quadrature calibration. + * + * Note that we are using Catalina's 'tracking' feature for RX quadrature + * calibration, so once it starts it continues to free-run during operation. + * It should be re-run for large frequency changes. */ +void calibrate_rx_quadrature(void) { + /* Configure RX Quadrature calibration settings. */ + write_ad9361_reg(0x168, 0x03); // Set tone level for cal + write_ad9361_reg(0x16e, 0x25); // RX Gain index to use for cal + write_ad9361_reg(0x16a, 0x75); // Set Kexp phase + write_ad9361_reg(0x16b, 0x15); // Set Kexp amplitude + write_ad9361_reg(0x169, 0xcf); // Continuous tracking mode + write_ad9361_reg(0x18b, 0xad); +} + +/* TX quadtrature calibration routine. + * + * The TX quadrature needs to be done twice, once for each TX chain, with + * only one register change in between. Thus, this function enacts the + * calibrations, and it is called from calibrate_tx_quadrature. */ +void tx_quadrature_cal_routine(void) { + + /* This is a weird process, but here is how it works: + * 1) Read the calibrated NCO frequency bits out of 0A3. + * 2) Write the two bits to the RX NCO freq part of 0A0. + * 3) Re-read 0A3 to get bits [5:0] because maybe they changed? + * 4) Update only the TX NCO freq bits in 0A3. + * 5) Profit (I hope). */ + uint8_t reg0a3 = read_ad9361_reg(0x0a3); + uint8_t nco_freq = (reg0a3 & 0xC0); + write_ad9361_reg(0x0a0, 0x15 | (nco_freq >> 1)); + reg0a3 = read_ad9361_reg(0x0a3); + write_ad9361_reg(0x0a3, (reg0a3 & 0x3F) | nco_freq); + + /* It is possible to reach a configuration that won't operate correctly, + * where the two test tones used for quadrature calibration are outside + * of the RX BBF, and therefore don't make it to the ADC. We will check + * for that scenario here. */ + double max_cal_freq = (((_baseband_bw * _tfir_factor) * ((nco_freq >> 6) + 1)) / 32) * 2; + double bbbw = _baseband_bw / 2.0; // bbbw represents the one-sided BW + if(bbbw > 28e6) { + bbbw = 28e6; + } else if (bbbw < 0.20e6) { + bbbw = 0.20e6; + } + if (max_cal_freq > bbbw ) + post_err_msg("max_cal_freq > bbbw"); + + write_ad9361_reg(0x0a1, 0x7B); // Set tracking coefficient + write_ad9361_reg(0x0a9, 0xff); // Cal count + write_ad9361_reg(0x0a2, 0x7f); // Cal Kexp + write_ad9361_reg(0x0a5, 0x01); // Cal magnitude threshold VVVV + write_ad9361_reg(0x0a6, 0x01); + + /* The gain table index used for calibration must be adjusted for the + * mid-table to get a TIA index = 1 and LPF index = 0. */ + if((_rx_freq >= 1300e6) && (_rx_freq < 4000e6)) { + write_ad9361_reg(0x0aa, 0x22); // Cal gain table index + } else { + write_ad9361_reg(0x0aa, 0x25); // Cal gain table index + } + + write_ad9361_reg(0x0a4, 0xf0); // Cal setting conut + write_ad9361_reg(0x0ae, 0x00); // Cal LPF gain index (split mode) + + /* First, calibrate the baseband DC offset. */ + calibrate_baseband_dc_offset(); + + /* Second, calibrate the RF DC offset. */ + calibrate_rf_dc_offset(); + + /* Now, calibrate the TX quadrature! */ + int count = 0; + write_ad9361_reg(0x016, 0x10); + while(read_ad9361_reg(0x016) & 0x10) { + if(count > 100) { + post_err_msg("TX Quadrature Calibration Failure"); + break; + } + + count++; + ad9361_msleep(10); + } +} + +/* Run the TX quadrature calibration. + * + * Note that from within this function we are also triggering the baseband + * and RF DC calibrations. */ +void calibrate_tx_quadrature(void) { + /* Make sure we are, in fact, in the ALERT state. If not, something is + * terribly wrong in the driver execution flow. */ + if((read_ad9361_reg(0x017) & 0x0F) != 5) { + post_err_msg("TX Quad Cal started, but not in ALERT"); + } + + /* Turn off free-running and continuous calibrations. Note that this + * will get turned back on at the end of the RX calibration routine. */ + write_ad9361_reg(0x169, 0xc0); + + /* This calibration must be done in a certain order, and for both TX_A + * and TX_B, separately. Store the original setting so that we can + * restore it later. */ + uint8_t orig_reg_inputsel = reg_inputsel; + + /*********************************************************************** + * TX1/2-A Calibration + **********************************************************************/ + reg_inputsel = reg_inputsel & 0xBF; + write_ad9361_reg(0x004, reg_inputsel); + + tx_quadrature_cal_routine(); + + /*********************************************************************** + * TX1/2-B Calibration + **********************************************************************/ + reg_inputsel = reg_inputsel | 0x40; + write_ad9361_reg(0x004, reg_inputsel); + + tx_quadrature_cal_routine(); + + /*********************************************************************** + * fin + **********************************************************************/ + reg_inputsel = orig_reg_inputsel; + write_ad9361_reg(0x004, orig_reg_inputsel); +} + + +/*********************************************************************** + * Other Misc Setup Functions + ***********************************************************************/ + +/* Program the mixer gain table. + * + * Note that this table is fixed for all frequency settings. */ +void program_mixer_gm_subtable() { + uint8_t gain[] = {0x78, 0x74, 0x70, 0x6C, 0x68, 0x64, 0x60, 0x5C, 0x58, + 0x54, 0x50, 0x4C, 0x48, 0x30, 0x18, 0x00}; + uint8_t gm[] = {0x00, 0x0D, 0x15, 0x1B, 0x21, 0x25, 0x29, 0x2C, 0x2F, + 0x31, 0x33, 0x34, 0x35, 0x3A, 0x3D, 0x3E}; + + /* Start the clock. */ + write_ad9361_reg(0x13f, 0x02); + + /* Program the GM Sub-table. */ + int i; + for(i = 15; i >= 0; i--) { + write_ad9361_reg(0x138, i); + write_ad9361_reg(0x139, gain[(15 - i)]); + write_ad9361_reg(0x13A, 0x00); + write_ad9361_reg(0x13B, gm[(15 - i)]); + write_ad9361_reg(0x13F, 0x06); + write_ad9361_reg(0x13C, 0x00); + write_ad9361_reg(0x13C, 0x00); + } + + /* Clear write bit and stop clock. */ + write_ad9361_reg(0x13f, 0x02); + write_ad9361_reg(0x13C, 0x00); + write_ad9361_reg(0x13C, 0x00); + write_ad9361_reg(0x13f, 0x00); +} + +/* Program the gain table. + * + * There are three different gain tables for different frequency ranges! */ +void program_gain_table() { + + /* Figure out which gain table we should be using for our current + * frequency band. */ + uint8_t (*gain_table)[5] = NULL; + uint8_t new_gain_table; + if(_rx_freq < 1300e6) { + gain_table = gain_table_sub_1300mhz; + new_gain_table = 1; + } else if(_rx_freq < 4e9) { + gain_table = gain_table_1300mhz_to_4000mhz; + new_gain_table = 2; + } else if(_rx_freq <= 6e9) { + gain_table = gain_table_4000mhz_to_6000mhz; + new_gain_table = 3; + } else { + post_err_msg("Wrong _rx_freq value"); + new_gain_table = 1; + } + + /* Only re-program the gain table if there has been a band change. */ + if(_curr_gain_table == new_gain_table) { + return; + } else { + _curr_gain_table = new_gain_table; + } + + /* Okay, we have to program a new gain table. Sucks, brah. Start the + * gain table clock. */ + write_ad9361_reg(0x137, 0x1A); + + /* IT'S PROGRAMMING TIME. */ + uint8_t index = 0; + for(; index < 77; index++) { + write_ad9361_reg(0x130, index); + write_ad9361_reg(0x131, gain_table[index][1]); + write_ad9361_reg(0x132, gain_table[index][2]); + write_ad9361_reg(0x133, gain_table[index][3]); + write_ad9361_reg(0x137, 0x1E); + write_ad9361_reg(0x134, 0x00); + write_ad9361_reg(0x134, 0x00); + } + + /* Everything above the 77th index is zero. */ + for(; index < 91; index++) { + write_ad9361_reg(0x130, index); + write_ad9361_reg(0x131, 0x00); + write_ad9361_reg(0x132, 0x00); + write_ad9361_reg(0x133, 0x00); + write_ad9361_reg(0x137, 0x1E); + write_ad9361_reg(0x134, 0x00); + write_ad9361_reg(0x134, 0x00); + } + + /* Clear the write bit and stop the gain clock. */ + write_ad9361_reg(0x137, 0x1A); + write_ad9361_reg(0x134, 0x00); + write_ad9361_reg(0x134, 0x00); + write_ad9361_reg(0x137, 0x00); +} + +/* Setup gain control registers. + * + * This really only needs to be done once, at initialization. */ +void setup_gain_control() { + write_ad9361_reg(0x0FA, 0xE0); // Gain Control Mode Select + write_ad9361_reg(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + write_ad9361_reg(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + write_ad9361_reg(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + write_ad9361_reg(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + write_ad9361_reg(0x100, 0x6F); // Max Digital Gain + write_ad9361_reg(0x104, 0x2F); // ADC Small Overload Threshold + write_ad9361_reg(0x105, 0x3A); // ADC Large Overload Threshold + write_ad9361_reg(0x107, 0x31); // Large LMT Overload Threshold + write_ad9361_reg(0x108, 0x39); // Small LMT Overload Threshold + write_ad9361_reg(0x109, 0x23); // Rx1 Full/LMT Gain Index + write_ad9361_reg(0x10A, 0x58); // Rx1 LPF Gain Index + write_ad9361_reg(0x10B, 0x00); // Rx1 Digital Gain Index + write_ad9361_reg(0x10C, 0x23); // Rx2 Full/LMT Gain Index + write_ad9361_reg(0x10D, 0x18); // Rx2 LPF Gain Index + write_ad9361_reg(0x10E, 0x00); // Rx2 Digital Gain Index + write_ad9361_reg(0x114, 0x30); // Low Power Threshold + write_ad9361_reg(0x11A, 0x27); // Initial LMT Gain Limit + write_ad9361_reg(0x081, 0x00); // Tx Symbol Gain Control +} + +/* Setup the RX or TX synthesizers. + * + * This setup depends on a fixed look-up table, which is stored in an + * included header file. The table is indexed based on the passed VCO rate. + */ +void setup_synth(int which, double vcorate) { + /* The vcorates in the vco_index array represent lower boundaries for + * rates. Once we find a match, we use that index to look-up the rest of + * the register values in the LUT. */ + int vcoindex = 0; + int i; + for(i = 0; i < 53; i++) { + vcoindex = i; + if(vcorate > vco_index[i]) { + break; + } + } + + if (vcoindex > 53) + post_err_msg("vcoindex > 53"); + + /* Parse the values out of the LUT based on our calculated index... */ + uint8_t vco_output_level = synth_cal_lut[vcoindex][0]; + uint8_t vco_varactor = synth_cal_lut[vcoindex][1]; + uint8_t vco_bias_ref = synth_cal_lut[vcoindex][2]; + uint8_t vco_bias_tcf = synth_cal_lut[vcoindex][3]; + uint8_t vco_cal_offset = synth_cal_lut[vcoindex][4]; + uint8_t vco_varactor_ref = synth_cal_lut[vcoindex][5]; + uint8_t charge_pump_curr = synth_cal_lut[vcoindex][6]; + uint8_t loop_filter_c2 = synth_cal_lut[vcoindex][7]; + uint8_t loop_filter_c1 = synth_cal_lut[vcoindex][8]; + uint8_t loop_filter_r1 = synth_cal_lut[vcoindex][9]; + uint8_t loop_filter_c3 = synth_cal_lut[vcoindex][10]; + uint8_t loop_filter_r3 = synth_cal_lut[vcoindex][11]; + + /* ... annnd program! */ + if(which == RX_TYPE) { + write_ad9361_reg(0x23a, 0x40 | vco_output_level); + write_ad9361_reg(0x239, 0xC0 | vco_varactor); + write_ad9361_reg(0x242, vco_bias_ref | (vco_bias_tcf << 3)); + write_ad9361_reg(0x238, (vco_cal_offset << 3)); + write_ad9361_reg(0x245, 0x00); + write_ad9361_reg(0x251, vco_varactor_ref); + write_ad9361_reg(0x250, 0x70); + write_ad9361_reg(0x23b, 0x80 | charge_pump_curr); + write_ad9361_reg(0x23e, loop_filter_c1 | (loop_filter_c2 << 4)); + write_ad9361_reg(0x23f, loop_filter_c3 | (loop_filter_r1 << 4)); + write_ad9361_reg(0x240, loop_filter_r3); + } else if(which == TX_TYPE) { + write_ad9361_reg(0x27a, 0x40 | vco_output_level); + write_ad9361_reg(0x279, 0xC0 | vco_varactor); + write_ad9361_reg(0x282, vco_bias_ref | (vco_bias_tcf << 3)); + write_ad9361_reg(0x278, (vco_cal_offset << 3)); + write_ad9361_reg(0x285, 0x00); + write_ad9361_reg(0x291, vco_varactor_ref); + write_ad9361_reg(0x290, 0x70); + write_ad9361_reg(0x27b, 0x80 | charge_pump_curr); + write_ad9361_reg(0x27e, loop_filter_c1 | (loop_filter_c2 << 4)); + write_ad9361_reg(0x27f, loop_filter_c3 | (loop_filter_r1 << 4)); + write_ad9361_reg(0x280, loop_filter_r3); + } else { + post_err_msg("[setup_synth] INVALID_CODE_PATH"); + } +} + + +/* Tune the baseband VCO. + * + * This clock signal is what gets fed to the ADCs and DACs. This function is + * not exported outside of this file, and is invoked based on the rate + * fed to the public set_clock_rate function. */ +double tune_bbvco(const double rate) { + msg("[tune_bbvco] rate=%.10f", rate); + + /* Let's not re-tune to the same frequency over and over... */ + if(freq_is_nearly_equal(rate, _req_coreclk)) { + return _adcclock_freq; + } + + _req_coreclk = rate; + + const double fref = 40e6; + const int modulus = 2088960; + const double vcomax = 1430e6; + const double vcomin = 672e6; + double vcorate; + int vcodiv; + + /* Iterate over VCO dividers until appropriate divider is found. */ + int i = 1; + for(; i <= 6; i++) { + vcodiv = 1 << i; + vcorate = rate * vcodiv; + + if(vcorate >= vcomin && vcorate <= vcomax) break; + } + if(i == 7) + post_err_msg("[tune_bbvco] wrong vcorate"); + + msg("[tune_bbvco] vcodiv=%d vcorate=%.10f", vcodiv, vcorate); + + /* Fo = Fref * (Nint + Nfrac / mod) */ + int nint = vcorate / fref; + msg("[tune_bbvco] (nint)=%.10f", (vcorate / fref)); + int nfrac = lround(((vcorate / fref) - (double)nint) * (double)modulus); + msg("[tune_bbvco] (nfrac)=%.10f", (((vcorate / fref) - (double)nint) * (double)modulus)); + msg("[tune_bbvco] nint=%d nfrac=%d", nint, nfrac); + double actual_vcorate = fref * ((double)nint + ((double)nfrac / (double)modulus)); + + /* Scale CP current according to VCO rate */ + const double icp_baseline = 150e-6; + const double freq_baseline = 1280e6; + double icp = icp_baseline * (actual_vcorate / freq_baseline); + int icp_reg = (icp / 25e-6) - 1; + + write_ad9361_reg(0x045, 0x00); // REFCLK / 1 to BBPLL + write_ad9361_reg(0x046, icp_reg & 0x3F); // CP current + write_ad9361_reg(0x048, 0xe8); // BBPLL loop filters + write_ad9361_reg(0x049, 0x5b); // BBPLL loop filters + write_ad9361_reg(0x04a, 0x35); // BBPLL loop filters + + write_ad9361_reg(0x04b, 0xe0); + write_ad9361_reg(0x04e, 0x10); // Max accuracy + + write_ad9361_reg(0x043, nfrac & 0xFF); // Nfrac[7:0] + write_ad9361_reg(0x042, (nfrac >> 8) & 0xFF); // Nfrac[15:8] + write_ad9361_reg(0x041, (nfrac >> 16) & 0xFF); // Nfrac[23:16] + write_ad9361_reg(0x044, nint); // Nint + + calibrate_lock_bbpll(); + + reg_bbpll = (reg_bbpll & 0xF8) | i; + + _bbpll_freq = actual_vcorate; + _adcclock_freq = (actual_vcorate / vcodiv); + + return _adcclock_freq; +} + +/* This function re-programs all of the gains in the system. + * + * Because the gain values match to different gain indices based on the + * current operating band, this function can be called to update all gain + * settings to the appropriate index after a re-tune. */ +void program_gains() { + set_gain(RX_TYPE,1, _rx1_gain); + set_gain(RX_TYPE,2, _rx2_gain); + set_gain(TX_TYPE,1, _tx1_gain); + set_gain(TX_TYPE,2, _tx2_gain); +} + +/* This is the internal tune function, not available for a host call. + * + * Calculate the VCO settings for the requested frquency, and then either + * tune the RX or TX VCO. */ +double tune_helper(int which, const double value) { + + /* The RFPLL runs from 6 GHz - 12 GHz */ + const double fref = 80e6; + const int modulus = 8388593; + const double vcomax = 12e9; + const double vcomin = 6e9; + double vcorate; + int vcodiv; + + /* Iterate over VCO dividers until appropriate divider is found. */ + int i; + for(i = 0; i <= 6; i++) { + vcodiv = 2 << i; + vcorate = value * vcodiv; + if(vcorate >= vcomin && vcorate <= vcomax) break; + } + if(i == 7) + post_err_msg("RFVCO can't find valid VCO rate!"); + + int nint = vcorate / fref; + int nfrac = ((vcorate / fref) - nint) * modulus; + + double actual_vcorate = fref * (nint + (double)(nfrac)/modulus); + double actual_lo = actual_vcorate / vcodiv; + + // UHD_VAR(actual_lo); // TODO: + + if(which == RX_TYPE) { + + _req_rx_freq = value; + + /* Set band-specific settings. */ + if(value < AD9361_RX_BAND_EDGE0) { + reg_inputsel = (reg_inputsel & 0xC0) | 0x30; + } else if((value >= AD9361_RX_BAND_EDGE0) && (value < AD9361_RX_BAND_EDGE1)) { + reg_inputsel = (reg_inputsel & 0xC0) | 0x0C; + } else if((value >= AD9361_RX_BAND_EDGE1) && (value <= 6e9)) { + reg_inputsel = (reg_inputsel & 0xC0) | 0x03; + } else { + post_err_msg("[tune_helper] INVALID_CODE_PATH"); + } + + write_ad9361_reg(0x004, reg_inputsel); + + /* Store vcodiv setting. */ + reg_vcodivs = (reg_vcodivs & 0xF0) | (i & 0x0F); + + /* Setup the synthesizer. */ + setup_synth(RX_TYPE, actual_vcorate); + + /* Tune!!!! */ + write_ad9361_reg(0x233, nfrac & 0xFF); + write_ad9361_reg(0x234, (nfrac >> 8) & 0xFF); + write_ad9361_reg(0x235, (nfrac >> 16) & 0xFF); + write_ad9361_reg(0x232, (nint >> 8) & 0xFF); + write_ad9361_reg(0x231, nint & 0xFF); + write_ad9361_reg(0x005, reg_vcodivs); + + /* Lock the PLL! */ + ad9361_msleep(2); + if((read_ad9361_reg(0x247) & 0x02) == 0) { + post_err_msg("RX PLL NOT LOCKED"); + } + + _rx_freq = actual_lo; + + return actual_lo; + + } else { + + _req_tx_freq = value; + + /* Set band-specific settings. */ + if(value < AD9361_TX_BAND_EDGE) { + reg_inputsel = reg_inputsel | 0x40; + } else if((value >= AD9361_TX_BAND_EDGE) && (value <= 6e9)) { + reg_inputsel = reg_inputsel & 0xBF; + } else { + post_err_msg("[tune_helper] INVALID_CODE_PATH"); + } + + write_ad9361_reg(0x004, reg_inputsel); + + /* Store vcodiv setting. */ + reg_vcodivs = (reg_vcodivs & 0x0F) | ((i & 0x0F) << 4); + + /* Setup the synthesizer. */ + setup_synth(TX_TYPE, actual_vcorate); + + /* Tune it, homey. */ + write_ad9361_reg(0x273, nfrac & 0xFF); + write_ad9361_reg(0x274, (nfrac >> 8) & 0xFF); + write_ad9361_reg(0x275, (nfrac >> 16) & 0xFF); + write_ad9361_reg(0x272, (nint >> 8) & 0xFF); + write_ad9361_reg(0x271, nint & 0xFF); + write_ad9361_reg(0x005, reg_vcodivs); + + /* Lock the PLL! */ + ad9361_msleep(2); + if((read_ad9361_reg(0x287) & 0x02) == 0) { + post_err_msg("TX PLL NOT LOCKED"); + } + + _tx_freq = actual_lo; + + return actual_lo; + } +} + +/* Configure the various clock / sample rates in the RX and TX chains. + * + * Functionally, this function configures Catalina's RX and TX rates. For + * a requested TX & RX rate, it sets the interpolation & decimation filters, + * and tunes the VCO that feeds the ADCs and DACs. + */ +double setup_rates(const double rate) { + + /* If we make it into this function, then we are tuning to a new rate. + * Store the new rate. */ + _req_clock_rate = rate; + + /* Set the decimation and interpolation values in the RX and TX chains. + * This also switches filters in / out. Note that all transmitters and + * receivers have to be turned on for the calibration portion of + * bring-up, and then they will be switched out to reflect the actual + * user-requested antenna selections. */ + int divfactor = 0; + _tfir_factor = 0; + if(rate < 0.33e6) { + // RX1 + RX2 enabled, 3, 2, 2, 4 + reg_rxfilt = B8( 11101111 ) ; + + // TX1 + TX2 enabled, 3, 2, 2, 4 + reg_txfilt = B8( 11101111 ) ; + + divfactor = 48; + _tfir_factor = 2; + } else if(rate < 0.66e6) { + // RX1 + RX2 enabled, 2, 2, 2, 4 + reg_rxfilt = B8( 11011111 ) ; + + // TX1 + TX2 enabled, 2, 2, 2, 4 + reg_txfilt = B8( 11011111 ) ; + + divfactor = 32; + _tfir_factor = 2; + } else if(rate <= 20e6) { + // RX1 + RX2 enabled, 2, 2, 2, 2 + reg_rxfilt = B8( 11011110 ) ; + + // TX1 + TX2 enabled, 2, 2, 2, 2 + reg_txfilt = B8( 11011110 ) ; + + divfactor = 16; + _tfir_factor = 2; + } else if((rate > 20e6) && (rate < 23e6)) { + // RX1 + RX2 enabled, 3, 2, 2, 2 + reg_rxfilt = B8( 11101110 ) ; + + // TX1 + TX2 enabled, 3, 1, 2, 2 + reg_txfilt = B8( 11100110 ) ; + + divfactor = 24; + _tfir_factor = 2; + } else if((rate >= 23e6) && (rate < 41e6)) { + // RX1 + RX2 enabled, 2, 2, 2, 2 + reg_rxfilt = B8( 11011110 ) ; + + // TX1 + TX2 enabled, 1, 2, 2, 2 + reg_txfilt = B8( 11001110 ) ; + + divfactor = 16; + _tfir_factor = 2; + } else if((rate >= 41e6) && (rate <= 56e6)) { + // RX1 + RX2 enabled, 3, 1, 2, 2 + reg_rxfilt = B8( 11100110 ) ; + + // TX1 + TX2 enabled, 3, 1, 1, 2 + reg_txfilt = B8( 11100010 ) ; + + divfactor = 12; + _tfir_factor = 2; + } else if((rate > 56e6) && (rate <= 61.44e6)) { + // RX1 + RX2 enabled, 3, 1, 1, 2 + reg_rxfilt = B8( 11100010 ) ; + + // TX1 + TX2 enabled, 3, 1, 1, 1 + reg_txfilt = B8( 11100001 ) ; + + divfactor = 6; + _tfir_factor = 1; + } else { + // should never get in here + post_err_msg("[setup_rates] INVALID_CODE_PATH"); + } + + msg("[setup_rates] divfactor=%d", divfactor); + + /* Tune the BBPLL to get the ADC and DAC clocks. */ + const double adcclk = tune_bbvco(rate * divfactor); + double dacclk = adcclk; + + /* The DAC clock must be <= 336e6, and is either the ADC clock or 1/2 the + * ADC clock.*/ + if(adcclk > 336e6) { + /* Make the DAC clock = ADC/2, and bypass the TXFIR. */ + reg_bbpll = reg_bbpll | 0x08; + dacclk = adcclk / 2.0; + } else { + reg_bbpll = reg_bbpll & 0xF7; + } + + /* Set the dividers / interpolators in Catalina. */ + write_ad9361_reg(0x002, reg_txfilt); + write_ad9361_reg(0x003, reg_rxfilt); + write_ad9361_reg(0x004, reg_inputsel); + write_ad9361_reg(0x00A, reg_bbpll); + + msg("[setup_rates] adcclk=%f", adcclk); + _baseband_bw = (adcclk / divfactor); + + /* Setup the RX and TX FIR filters. Scale the number of taps based on + * the clock speed. */ + const int max_tx_taps = 16 * AD9361_MIN((int)((dacclk / rate) + 0.5), \ + AD9361_MIN(4 * (1 << _tfir_factor), 8)); + const int max_rx_taps = AD9361_MIN((16 * (int)(adcclk / rate)), 128); + + const int num_tx_taps = get_num_taps(max_tx_taps); + const int num_rx_taps = get_num_taps(max_rx_taps); + + setup_tx_fir(num_tx_taps); + setup_rx_fir(num_rx_taps); + + return _baseband_bw; +} + +/*********************************************************************** + * Publicly exported functions to host calls + **********************************************************************/ +void init_ad9361(void) { + + /* Initialize shadow registers. */ + reg_vcodivs = 0x00; + reg_inputsel = 0x30; + reg_rxfilt = 0x00; + reg_txfilt = 0x00; + reg_bbpll = 0x02; + reg_bbftune_config = 0x1e; + reg_bbftune_mode = 0x1e; + + /* Initialize private VRQ fields. */ + _rx_freq = 0.0; + _tx_freq = 0.0; + _req_rx_freq = 0.0; + _req_tx_freq = 0.0; + _baseband_bw = 0.0; + _req_clock_rate = 0.0; + _req_coreclk = 0.0; + _bbpll_freq = 0.0; + _adcclock_freq = 0.0; + _rx_bbf_tunediv = 0; + _curr_gain_table = 0; + _rx1_gain = 0; + _rx2_gain = 0; + _tx1_gain = 0; + _tx2_gain = 0; + + /* Reset the device. */ + write_ad9361_reg(0x000,0x01); + write_ad9361_reg(0x000,0x00); + ad9361_msleep(20); + + /* There is not a WAT big enough for this. */ + write_ad9361_reg(0x3df, 0x01); + + write_ad9361_reg(0x2a6, 0x0e); // Enable master bias + write_ad9361_reg(0x2a8, 0x0e); // Set bandgap trim + + /* Set RFPLL ref clock scale to REFCLK * 2 */ + write_ad9361_reg(0x2ab, 0x07); + write_ad9361_reg(0x2ac, 0xff); + + /* Enable clocks. */ + if (AD9361_CLOCKING_MODE == 0) + { + write_ad9361_reg(0x009, 0x17); + } + if (AD9361_CLOCKING_MODE == 1) + { + write_ad9361_reg(0x009, 0x07); + write_ad9361_reg(0x292, 0x08); + write_ad9361_reg(0x293, 0x80); + write_ad9361_reg(0x294, 0x00); + write_ad9361_reg(0x295, 0x14); + } + ad9361_msleep(20); + + /* Tune the BBPLL, write TX and RX FIRS. */ + setup_rates(50e6); + + /* Setup data ports (FDD dual port DDR CMOS): + * FDD dual port DDR CMOS no swap. + * Force TX on one port, RX on the other. */ + write_ad9361_reg(0x010, 0xc8); + write_ad9361_reg(0x011, 0x00); + write_ad9361_reg(0x012, 0x02); + + /* Data delay for TX and RX data clocks */ + write_ad9361_reg(0x006, 0x0F); + write_ad9361_reg(0x007, 0x0F); + + /* Setup AuxDAC */ + write_ad9361_reg(0x018, 0x00); // AuxDAC1 Word[9:2] + write_ad9361_reg(0x019, 0x00); // AuxDAC2 Word[9:2] + write_ad9361_reg(0x01A, 0x00); // AuxDAC1 Config and Word[1:0] + write_ad9361_reg(0x01B, 0x00); // AuxDAC2 Config and Word[1:0] + write_ad9361_reg(0x023, 0xFF); // AuxDAC Manaul/Auto Control + write_ad9361_reg(0x026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select + write_ad9361_reg(0x030, 0x00); // AuxDAC1 Rx Delay + write_ad9361_reg(0x031, 0x00); // AuxDAC1 Tx Delay + write_ad9361_reg(0x032, 0x00); // AuxDAC2 Rx Delay + write_ad9361_reg(0x033, 0x00); // AuxDAC2 Tx Delay + + /* Setup AuxADC */ + write_ad9361_reg(0x00B, 0x00); // Temp Sensor Setup (Offset) + write_ad9361_reg(0x00C, 0x00); // Temp Sensor Setup (Temp Window) + write_ad9361_reg(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) + write_ad9361_reg(0x00F, 0x04); // Temp Sensor Setup (Decimation) + write_ad9361_reg(0x01C, 0x10); // AuxADC Setup (Clock Div) + write_ad9361_reg(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) + + /* Setup control outputs. */ + write_ad9361_reg(0x035, 0x07); + write_ad9361_reg(0x036, 0xFF); + + /* Setup GPO */ + write_ad9361_reg(0x03a, 0x27); //set delay register + write_ad9361_reg(0x020, 0x00); // GPO Auto Enable Setup in RX and TX + write_ad9361_reg(0x027, 0x03); // GPO Manual and GPO auto value in ALERT + write_ad9361_reg(0x028, 0x00); // GPO_0 RX Delay + write_ad9361_reg(0x029, 0x00); // GPO_1 RX Delay + write_ad9361_reg(0x02A, 0x00); // GPO_2 RX Delay + write_ad9361_reg(0x02B, 0x00); // GPO_3 RX Delay + write_ad9361_reg(0x02C, 0x00); // GPO_0 TX Delay + write_ad9361_reg(0x02D, 0x00); // GPO_1 TX Delay + write_ad9361_reg(0x02E, 0x00); // GPO_2 TX Delay + write_ad9361_reg(0x02F, 0x00); // GPO_3 TX Delay + + write_ad9361_reg(0x261, 0x00); // RX LO power + write_ad9361_reg(0x2a1, 0x00); // TX LO power + write_ad9361_reg(0x248, 0x0b); // en RX VCO LDO + write_ad9361_reg(0x288, 0x0b); // en TX VCO LDO + write_ad9361_reg(0x246, 0x02); // pd RX cal Tcf + write_ad9361_reg(0x286, 0x02); // pd TX cal Tcf + write_ad9361_reg(0x249, 0x8e); // rx vco cal length + write_ad9361_reg(0x289, 0x8e); // rx vco cal length + write_ad9361_reg(0x23b, 0x80); // set RX MSB?, FIXME 0x89 magic cp + write_ad9361_reg(0x27b, 0x80); // "" TX //FIXME 0x88 see above + write_ad9361_reg(0x243, 0x0d); // set rx prescaler bias + write_ad9361_reg(0x283, 0x0d); // "" TX + + write_ad9361_reg(0x23d, 0x00); // Clear half VCO cal clock setting + write_ad9361_reg(0x27d, 0x00); // Clear half VCO cal clock setting + + /* The order of the following process is EXTREMELY important. If the + * below functions are modified at all, device initialization and + * calibration might be broken in the process! */ + + write_ad9361_reg(0x015, 0x04); // dual synth mode, synth en ctrl en + write_ad9361_reg(0x014, 0x05); // use SPI for TXNRX ctrl, to ALERT, TX on + write_ad9361_reg(0x013, 0x01); // enable ENSM + ad9361_msleep(1); + + calibrate_synth_charge_pumps(); + + tune_helper(RX_TYPE, 800e6); + tune_helper(TX_TYPE, 850e6); + + program_mixer_gm_subtable(); + program_gain_table(); + setup_gain_control(); + + calibrate_baseband_rx_analog_filter(); + calibrate_baseband_tx_analog_filter(); + calibrate_rx_TIAs(); + calibrate_secondary_tx_filter(); + + setup_adc(); + + calibrate_tx_quadrature(); + calibrate_rx_quadrature(); + + write_ad9361_reg(0x012, 0x02); // cals done, set PPORT config + write_ad9361_reg(0x013, 0x01); // Set ENSM FDD bit + write_ad9361_reg(0x015, 0x04); // dual synth mode, synth en ctrl en + + /* Default TX attentuation to 10dB on both TX1 and TX2 */ + write_ad9361_reg(0x073, 0x00); + write_ad9361_reg(0x074, 0x00); + write_ad9361_reg(0x075, 0x00); + write_ad9361_reg(0x076, 0x00); + + /* Setup RSSI Measurements */ + write_ad9361_reg(0x150, 0x0E); // RSSI Measurement Duration 0, 1 + write_ad9361_reg(0x151, 0x00); // RSSI Measurement Duration 2, 3 + write_ad9361_reg(0x152, 0xFF); // RSSI Weighted Multiplier 0 + write_ad9361_reg(0x153, 0x00); // RSSI Weighted Multiplier 1 + write_ad9361_reg(0x154, 0x00); // RSSI Weighted Multiplier 2 + write_ad9361_reg(0x155, 0x00); // RSSI Weighted Multiplier 3 + write_ad9361_reg(0x156, 0x00); // RSSI Delay + write_ad9361_reg(0x157, 0x00); // RSSI Wait + write_ad9361_reg(0x158, 0x0D); // RSSI Mode Select + write_ad9361_reg(0x15C, 0x67); // Power Measurement Duration + + /* Turn on the default RX & TX chains. */ + set_active_chains(true, false, false, false); + + /* Set TXers & RXers on (only works in FDD mode) */ + write_ad9361_reg(0x014, 0x21); +} + + +/* This function sets the RX / TX rate between Catalina and the FPGA, and + * thus determines the interpolation / decimation required in the FPGA to + * achieve the user's requested rate. + * + * This is the only clock setting function that is exposed to the outside. */ +double set_clock_rate(const double req_rate) { + if(req_rate > 61.44e6) { + post_err_msg("Requested master clock rate outside range"); + } + + msg("[set_clock_rate] req_rate=%.10f", req_rate); + + /* UHD has a habit of requesting the same rate like four times when it + * starts up. This prevents that, and any bugs in user code that request + * the same rate over and over. */ + if(freq_is_nearly_equal(req_rate, _req_clock_rate)) { + return _baseband_bw; + } + + /* We must be in the SLEEP / WAIT state to do this. If we aren't already + * there, transition the ENSM to State 0. */ + uint8_t current_state = read_ad9361_reg(0x017) & 0x0F; + switch(current_state) { + case 0x05: + /* We are in the ALERT state. */ + write_ad9361_reg(0x014, 0x21); + ad9361_msleep(5); + write_ad9361_reg(0x014, 0x00); + break; + + case 0x0A: + /* We are in the FDD state. */ + write_ad9361_reg(0x014, 0x00); + break; + + default: + post_err_msg("[set_clock_rate:1] AD9361 in unknown state"); + break; + }; + + /* Store the current chain / antenna selections so that we can restore + * them at the end of this routine; all chains will be enabled from + * within setup_rates for calibration purposes. */ + uint8_t orig_tx_chains = reg_txfilt & 0xC0; + uint8_t orig_rx_chains = reg_rxfilt & 0xC0; + + /* Call into the clock configuration / settings function. This is where + * all the hard work gets done. */ + double rate = setup_rates(req_rate); + + msg("[set_clock_rate] rate=%.10f", rate); + + /* Transition to the ALERT state and calibrate everything. */ + write_ad9361_reg(0x015, 0x04); //dual synth mode, synth en ctrl en + write_ad9361_reg(0x014, 0x05); //use SPI for TXNRX ctrl, to ALERT, TX on + write_ad9361_reg(0x013, 0x01); //enable ENSM + ad9361_msleep(1); + + calibrate_synth_charge_pumps(); + + tune_helper(RX_TYPE, _rx_freq); + tune_helper(TX_TYPE, _tx_freq); + + program_mixer_gm_subtable(); + program_gain_table(); + setup_gain_control(); + program_gains(); + + calibrate_baseband_rx_analog_filter(); + calibrate_baseband_tx_analog_filter(); + calibrate_rx_TIAs(); + calibrate_secondary_tx_filter(); + + setup_adc(); + + calibrate_tx_quadrature(); + calibrate_rx_quadrature(); + + write_ad9361_reg(0x012, 0x02); // cals done, set PPORT config + write_ad9361_reg(0x013, 0x01); // Set ENSM FDD bit + write_ad9361_reg(0x015, 0x04); // dual synth mode, synth en ctrl en + + /* End the function in the same state as the entry state. */ + switch(current_state) { + case 0x05: + /* We are already in ALERT. */ + break; + + case 0x0A: + /* Transition back to FDD, and restore the original antenna + * / chain selections. */ + reg_txfilt = (reg_txfilt & 0x3F) | orig_tx_chains; + reg_rxfilt = (reg_rxfilt & 0x3F) | orig_rx_chains; + + write_ad9361_reg(0x002, reg_txfilt); + write_ad9361_reg(0x003, reg_rxfilt); + write_ad9361_reg(0x014, 0x21); + break; + + default: + post_err_msg("[set_clock_rate:2] AD9361 in unknown state"); + break; + }; + + return rate; +} + + +/* Set which of the four TX / RX chains provided by Catalina are active. + * + * Catalina provides two sets of chains, Side A and Side B. Each side + * provides one TX antenna, and one RX antenna. The B200 maintains the USRP + * standard of providing one antenna connection that is both TX & RX, and + * one that is RX-only - for each chain. Thus, the possible antenna and + * chain selections are: + * + * B200 Antenna Catalina Side Catalina Chain + * ------------------------------------------------------------------- + * TX / RX1 Side A TX1 (when switched to TX) + * TX / RX1 Side A RX1 (when switched to RX) + * RX1 Side A RX1 + * + * TX / RX2 Side B TX2 (when switched to TX) + * TX / RX2 Side B RX2 (when switched to RX) + * RX2 Side B RX2 + */ +void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) { + /* Clear out the current active chain settings. */ + reg_txfilt = reg_txfilt & 0x3F; + reg_rxfilt = reg_rxfilt & 0x3F; + + /* Turn on the different chains based on the passed parameters. */ + if(tx1) { reg_txfilt = reg_txfilt | 0x40; } + if(tx2) { reg_txfilt = reg_txfilt | 0x80; } + if(rx1) { reg_rxfilt = reg_rxfilt | 0x40; } + if(rx2) { reg_rxfilt = reg_rxfilt | 0x80; } + + /* Turn on / off the chains. */ + write_ad9361_reg(0x002, reg_txfilt); + write_ad9361_reg(0x003, reg_rxfilt); +} + +/* Tune the RX or TX frequency. + * + * This is the publicly-accessible tune function. It makes sure the tune + * isn't a redundant request, and if not, passes it on to the class's + * internal tune function. + * + * After tuning, it runs any appropriate calibrations. */ +double tune(int which, const double value) { + + if(which == RX_TYPE) { + if(freq_is_nearly_equal(value, _req_rx_freq)) { + return _rx_freq; + } + + } else if(which == TX_TYPE) { + if(freq_is_nearly_equal(value, _req_tx_freq)) { + return _tx_freq; + } + + } else { + post_err_msg("[tune] INVALID_CODE_PATH"); + } + + /* If we aren't already in the ALERT state, we will need to return to + * the FDD state after tuning. */ + int not_in_alert = 0; + if((read_ad9361_reg(0x017) & 0x0F) != 5) { + /* Force the device into the ALERT state. */ + not_in_alert = 1; + write_ad9361_reg(0x014, 0x01); + } + + /* Tune the RF VCO! */ + double tune_freq = tune_helper(which, value); + + /* Run any necessary calibrations / setups */ + if(which == RX_TYPE) { + program_gain_table(); + } + + /* Update the gain settings. */ + program_gains(); + + /* Run the calibration algorithms. */ + calibrate_tx_quadrature(); + calibrate_rx_quadrature(); + + /* If we were in the FDD state, return it now. */ + if(not_in_alert) { + write_ad9361_reg(0x014, 0x21); + } + + return tune_freq; +} + +/* Set the gain of RX1, RX2, TX1, or TX2. + * + * Note that the 'value' passed to this function is the actual gain value, + * _not_ the gain index. This is the opposite of the eval software's GUI! + * Also note that the RX chains are done in terms of gain, and the TX chains + * are done in terms of attenuation. */ +double set_gain(int which, int n, const double value) { + + if(which == RX_TYPE) { + /* Indexing the gain tables requires an offset from the requested + * amount of total gain in dB: + * < 1300MHz: dB + 5 + * >= 1300MHz and < 4000MHz: dB + 3 + * >= 4000MHz and <= 6000MHz: dB + 14 + */ + int gain_offset = 0; + if(_rx_freq < 1300e6) { + gain_offset = 5; + } else if(_rx_freq < 4000e6) { + gain_offset = 3; + } else { + gain_offset = 14; + } + + int gain_index = value + gain_offset; + + /* Clip the gain values to the proper min/max gain values. */ + if(gain_index > 76) gain_index = 76; + if(gain_index < 0) gain_index = 0; + + if(n == 1) { + _rx1_gain = value; + write_ad9361_reg(0x109, gain_index); + } else { + _rx2_gain = value; + write_ad9361_reg(0x10c, gain_index); + } + + return gain_index - gain_offset; + } else { + /* Setting the below bits causes a change in the TX attenuation word + * to immediately take effect. */ + write_ad9361_reg(0x077, 0x40); + write_ad9361_reg(0x07c, 0x40); + + /* Each gain step is -0.25dB. Calculate the attenuation necessary + * for the requested gain, convert it into gain steps, then write + * the attenuation word. Max gain (so zero attenuation) is 89.75. */ + double atten = AD9361_MAX_GAIN - value; + int attenreg = atten * 4; + if(n == 1) { + _tx1_gain = value; + write_ad9361_reg(0x073, attenreg & 0xFF); + write_ad9361_reg(0x074, (attenreg >> 8) & 0x01); + } else { + _tx2_gain = value; + write_ad9361_reg(0x075, attenreg & 0xFF); + write_ad9361_reg(0x076, (attenreg >> 8) & 0x01); + } + return AD9361_MAX_GAIN - ((double)(attenreg)/ 4); + } +} + +/* This function is responsible to dispatch the vendor request call + * to the proper handler + */ +void ad9361_dispatch(const char* vrb, char* vrb_out) { + memcpy(vrb_out, vrb, AD9361_DISPATCH_PACKET_SIZE); // Copy request to response memory + tmp_req_buffer = vrb_out; // Set this to enable 'post_err_msg' + + ////////////////////////////////////////////// + + double ret_val = 0.0; + int mask = 0; + + const ad9361_transaction_t *request = (const ad9361_transaction_t *)vrb; + ad9361_transaction_t *response = (ad9361_transaction_t *)vrb_out; + response->error_msg[0] = '\0'; // Ensure error is cleared + + //msg("[ad9361_dispatch] action=%d", request->action); + + switch (request->action) { + case AD9361_ACTION_ECHO: + break; // nothing to do + case AD9361_ACTION_INIT: + init_ad9361(); + break; + case AD9361_ACTION_SET_RX1_GAIN: + ret_val = set_gain(RX_TYPE,1,double_unpack(request->value.gain)); + double_pack(ret_val, response->value.gain); + break; + case AD9361_ACTION_SET_TX1_GAIN: + ret_val = set_gain(TX_TYPE,1,double_unpack(request->value.gain)); + double_pack(ret_val, response->value.gain); + break; + case AD9361_ACTION_SET_RX2_GAIN: + ret_val = set_gain(RX_TYPE,2,double_unpack(request->value.gain)); + double_pack(ret_val, response->value.gain); + break; + case AD9361_ACTION_SET_TX2_GAIN: + ret_val = set_gain(TX_TYPE,2,double_unpack(request->value.gain)); + double_pack(ret_val, response->value.gain); + break; + case AD9361_ACTION_SET_RX_FREQ: + ret_val = tune(RX_TYPE, double_unpack(request->value.freq)); + double_pack(ret_val, response->value.freq); + break; + case AD9361_ACTION_SET_TX_FREQ: + ret_val = tune(TX_TYPE, double_unpack(request->value.freq)); + double_pack(ret_val, response->value.freq); + break; + case AD9361_ACTION_SET_CODEC_LOOP: + data_port_loopback(request->value.codec_loop != 0); + break; + case AD9361_ACTION_SET_CLOCK_RATE: + ret_val = set_clock_rate(double_unpack(request->value.rate)); + double_pack(ret_val, response->value.rate); + break; + case AD9361_ACTION_SET_ACTIVE_CHAINS: + mask = request->value.enable_mask; + set_active_chains(mask & 1, mask & 2, mask & 4, mask & 8); + break; + default: + post_err_msg("[ad9361_dispatch] NOT IMPLEMENTED"); + break; + } +} diff --git a/firmware/fx3/ad9361/lib/ad9361_synth_lut.h b/firmware/fx3/ad9361/lib/ad9361_synth_lut.h new file mode 100644 index 000000000..79214526d --- /dev/null +++ b/firmware/fx3/ad9361/lib/ad9361_synth_lut.h @@ -0,0 +1,135 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef INCLUDED_AD9361_SYNTH_LUT_HPP +#define INCLUDED_AD9361_SYNTH_LUT_HPP + + +double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, + 11288000000, 11007000000, 10742000000, 10492000000, + 10258000000, 10036000000, 9827800000, 9631100000, + 9445300000, 9269800000, 9103600000, 8946300000, + 8797000000, 8655300000, 8520600000, 8392300000, + 8269900000, 8153100000, 8041400000, 7934400000, + 7831800000, 7733200000, 7638400000, 7547100000, + 7459000000, 7374000000, 7291900000, 7212400000, + 7135500000, 7061000000, 6988700000, 6918600000, + 6850600000, 6784600000, 6720500000, 6658200000, + 6597800000, 6539200000, 6482300000, 6427000000, + 6373400000, 6321400000, 6270900000, 6222000000, + 6174500000, 6128400000, 6083600000, 6040100000, + 5997700000}; + +int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 15, 8, 10, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 15, 8, 11, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 15, 8, 11, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 14, 8, 12, 13, 4, 13, 15, 9}, + {10, 0, 4, 0, 14, 8, 13, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 14, 9, 13, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 14, 9, 14, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 14, 9, 15, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 14, 9, 15, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 13, 9, 16, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 13, 9, 17, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 13, 9, 18, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 13, 9, 18, 13, 4, 13, 15, 9}, + {10, 0, 5, 1, 13, 9, 19, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 14, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 14, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 15, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 15, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 16, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 16, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 17, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 17, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 18, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 18, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 19, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 19, 13, 4, 13, 15, 9}, + {10, 1, 6, 1, 15, 11, 20, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 12, 20, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 12, 21, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 12, 21, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 22, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 22, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 23, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 23, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 24, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 24, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 25, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 25, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 26, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 26, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 27, 13, 4, 13, 15, 9}, + {10, 1, 7, 2, 15, 14, 27, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 18, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 18, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 18, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 19, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 19, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 19, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 19, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 20, 13, 4, 13, 15, 9}, + {10, 3, 7, 3, 15, 12, 20, 13, 4, 13, 15, 9}}; + + +#if 0 /* This is the table for a 40MHz RFPLL Reference */ +int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 15, 8, 9, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 15, 8, 9, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 15, 8, 10, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 15, 8, 11, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 15, 8, 11, 12, 3, 14, 15, 11}, + {10, 0, 4, 0, 14, 8, 12, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 13, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 13, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 14, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 15, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 15, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 16, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 17, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 17, 12, 3, 14, 15, 11}, + {10, 0, 5, 1, 14, 9, 18, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 13, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 14, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 14, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 15, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 15, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 16, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 16, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 17, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 17, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 17, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 18, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 18, 12, 3, 14, 15, 11}, + {10, 1, 6, 1, 15, 11, 19, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 12, 19, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 12, 20, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 12, 20, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 21, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 21, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 22, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 22, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 23, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 23, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 24, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 24, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 25, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 25, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 26, 12, 3, 14, 15, 11}, + {10, 1, 7, 2, 15, 14, 26, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 17, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 17, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 17, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 18, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 18, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 18, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 18, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 19, 12, 3, 14, 15, 11}, + {10, 3, 7, 3, 15, 12, 19, 12, 3, 14, 15, 11} }; +#endif + +#endif /* INCLUDED_AD9361_SYNTH_LUT_HPP */ diff --git a/firmware/fx3/b200/.gitignore b/firmware/fx3/b200/.gitignore new file mode 100644 index 000000000..13c187886 --- /dev/null +++ b/firmware/fx3/b200/.gitignore @@ -0,0 +1,4 @@ +*.o +*.elf +*.hex +*.map diff --git a/firmware/fx3/b200/b200_ad9361.c b/firmware/fx3/b200/b200_ad9361.c new file mode 100644 index 000000000..ebb0dda70 --- /dev/null +++ b/firmware/fx3/b200/b200_ad9361.c @@ -0,0 +1,57 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#include "cyu3error.h" +#include "cyu3i2c.h" +#include "cyu3spi.h" +#include "cyu3os.h" +#include "cyu3pib.h" +#include "cyu3system.h" +#include "cyu3usb.h" +#include "cyu3utils.h" +#include "pib_regs.h" +#include "b200_vrq.h" +#include <stdint.h> + +#define true CyTrue +#define false CyFalse + +typedef CyBool_t bool; + +/* Fast sqrt() - precision can be improved by increasing + * the number of iterations + */ +float ad9361_sqrt(const float number) +{ + uint32_t i; + float x2, y; + + x2 = number * 0.5F; + y = number; + i = *(uint32_t *) &y; + i = 0x5f3759df - ( i >> 1 ); + y = *(float *) &i; + y = y * (1.5F - (x2 * y * y)); + + return number * y; +} + +void ad9361_msleep(const unsigned millis) +{ + CyU3PThreadSleep(millis); +} + +#define AD9361_MIN(a, b) CY_U3P_MIN(a, b) +#define AD9361_MAX(a, b) CY_U3P_MAX(a, b) + +#define AD9361_CEIL_INT(a) ((int)(a+1)) +#define AD9361_FLOOR_INT(a) ((int)(a)) + +#define AD9361_CLOCKING_MODE 0 + +#define AD9361_RX_BAND_EDGE0 2.2e9 +#define AD9361_RX_BAND_EDGE1 4e9 +#define AD9361_TX_BAND_EDGE 2.5e9 + +#include "../ad9361/lib/ad9361_impl.c" diff --git a/firmware/fx3/b200/b200_gpifconfig.h b/firmware/fx3/b200/b200_gpifconfig.h new file mode 100644 index 000000000..58836fac8 --- /dev/null +++ b/firmware/fx3/b200/b200_gpifconfig.h @@ -0,0 +1,178 @@ +//
+// Copyright 2013-2014 Ettus Research LLC
+//
+
+/*
+ * Project Name: b200_v2.cyfx
+ * Time : 01/17/2013 12:50:08
+ * Device Type: FX3
+ * Project Type: GPIF2
+ *
+ *
+ *
+ *
+ * This is a generated file and should not be modified
+ * This file need to be included only once in the firmware
+ * This file is generated by Gpif2 designer tool version - 1.0.715.0
+ *
+ */
+
+#ifndef _INCLUDED_CYFXGPIF2CONFIG_
+#define _INCLUDED_CYFXGPIF2CONFIG_
+#include "cyu3types.h"
+#include "cyu3gpif.h"
+
+/* Summary
+ Number of states in the state machine
+ */
+#define CY_NUMBER_OF_STATES 6
+
+/* Summary
+ Mapping of user defined state names to state indices
+ */
+#define RESET 0
+#define IDLE 1
+#define READ 2
+#define WRITE 3
+#define SHORT_PKT 4
+#define ZLP 5
+
+
+/* Summary
+ Initial value of early outputs from the state machine.
+ */
+#define ALPHA_RESET 0x8
+
+
+/* Summary
+ Transition function values used in the state machine.
+ */
+uint16_t CyFxGpifTransition[] = {
+ 0x0000, 0x8080, 0x2222, 0x5555, 0x7F7F, 0x1F1F, 0x8888
+};
+
+/* Summary
+ Table containing the transition information for various states.
+ This table has to be stored in the WAVEFORM Registers.
+ This array consists of non-replicated waveform descriptors and acts as a
+ waveform table.
+ */
+CyU3PGpifWaveData CyFxGpifWavedata[] = {
+ {{0x1E086001,0x000100C4,0x80000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x4E080302,0x00000200,0x80000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x1E086001,0x000100C4,0x80000000},{0x4E040704,0x20000200,0xC0100000}},
+ {{0x00000000,0x00000000,0x00000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x00000000,0x00000000,0x00000000},{0x3E738705,0x00000200,0xC0100000}},
+ {{0x00000000,0x00000000,0x00000000},{0x5E002703,0x2001020C,0x80000000}},
+ {{0x00000000,0x00000000,0x00000000},{0x4E040704,0x20000200,0xC0100000}}
+};
+
+/* Summary
+ Table that maps state indices to the descriptor table indices.
+ */
+uint8_t CyFxGpifWavedataPosition[] = {
+ 0,1,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,4,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,5,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,6,0,2,0,0
+};
+
+/* Summary
+ GPIF II configuration register values.
+ */
+uint32_t CyFxGpifRegValue[] = {
+ 0x80000380, /* CY_U3P_PIB_GPIF_CONFIG */
+ 0x000010AC, /* CY_U3P_PIB_GPIF_BUS_CONFIG */
+ 0x01070002, /* CY_U3P_PIB_GPIF_BUS_CONFIG2 */
+ 0x00000044, /* CY_U3P_PIB_GPIF_AD_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_STATUS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INTR */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INTR_MASK */
+ 0x00000082, /* CY_U3P_PIB_GPIF_SERIAL_IN_CONFIG */
+ 0x00000782, /* CY_U3P_PIB_GPIF_SERIAL_OUT_CONFIG */
+ 0x00000500, /* CY_U3P_PIB_GPIF_CTRL_BUS_DIRECTION */
+ 0x0000FFCF, /* CY_U3P_PIB_GPIF_CTRL_BUS_DEFAULT */
+ 0x000000BF, /* CY_U3P_PIB_GPIF_CTRL_BUS_POLARITY */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_TOGGLE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000018, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000019, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000006, /* CY_U3P_PIB_GPIF_CTRL_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_CTRL_COUNT_LIMIT */
+ 0x0000010A, /* CY_U3P_PIB_GPIF_ADDR_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_ADDR_COUNT_LIMIT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_STATE_COUNT_CONFIG */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_STATE_COUNT_LIMIT */
+ 0x0000010A, /* CY_U3P_PIB_GPIF_DATA_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_DATA_COUNT_LIMIT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_CTRL */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x80010400, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010401, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010402, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010403, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_LAMBDA_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ALPHA_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_BETA_STAT */
+ 0x00080000, /* CY_U3P_PIB_GPIF_WAVEFORM_CTRL_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH */
+ 0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH_TIMEOUT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CRC_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CRC_DATA */
+ 0xFFFFFFF1 /* CY_U3P_PIB_GPIF_BETA_DEASSERT */
+};
+
+/* Summary
+ This structure holds all the configuration inputs for the GPIF II.
+ */
+const CyU3PGpifConfig_t CyFxGpifConfig = {
+ (uint16_t)(sizeof(CyFxGpifWavedataPosition)/sizeof(uint8_t)),
+ CyFxGpifWavedata,
+ CyFxGpifWavedataPosition,
+ (uint16_t)(sizeof(CyFxGpifTransition)/sizeof(uint16_t)),
+ CyFxGpifTransition,
+ (uint16_t)(sizeof(CyFxGpifRegValue)/sizeof(uint32_t)),
+ CyFxGpifRegValue
+};
+
+#endif /* _INCLUDED_CYFXGPIF2CONFIG_ */
diff --git a/firmware/fx3/b200/b200_i2c.c b/firmware/fx3/b200/b200_i2c.c new file mode 100644 index 000000000..c6fa67c77 --- /dev/null +++ b/firmware/fx3/b200/b200_i2c.c @@ -0,0 +1,82 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#include "b200_i2c.h" + +#include "cyu3i2c.h" + +/* I2c initialization for EEPROM programming. */ +void CyFxI2cInit (uint16_t pageLen) { + CyU3PI2cConfig_t i2cConfig; + + /* Initialize and configure the I2C master module. */ + CyU3PI2cInit (); + + /* Start the I2C master block. The bit rate is set at 100KHz. + * The data transfer is done via DMA. */ + CyU3PMemSet ((uint8_t *)&i2cConfig, 0, sizeof(i2cConfig)); + i2cConfig.bitRate = CY_FX_USBI2C_I2C_BITRATE; + i2cConfig.busTimeout = 0xFFFFFFFF; + i2cConfig.dmaTimeout = 0xFFFF; + i2cConfig.isDma = CyFalse; + + CyU3PI2cSetConfig (&i2cConfig, NULL); + glI2cPageSize = pageLen; +} + +/* I2C read / write for programmer application. */ +void CyFxUsbI2cTransfer ( + uint16_t byteAddress, + uint8_t devAddr, + uint16_t byteCount, + uint8_t *buffer, + CyBool_t isRead) +{ + CyU3PI2cPreamble_t preamble; + uint16_t pageCount = (byteCount / glI2cPageSize); + uint16_t resCount = glI2cPageSize; + + if (byteCount == 0) { + return; + } + + if ((byteCount % glI2cPageSize) != 0) { + pageCount ++; + resCount = byteCount % glI2cPageSize; + } + + while (pageCount != 0) { + if (isRead) { + /* Update the preamble information. */ + preamble.length = 4; + preamble.buffer[0] = devAddr; + preamble.buffer[1] = (uint8_t)(byteAddress >> 8); + preamble.buffer[2] = (uint8_t)(byteAddress & 0xFF); + preamble.buffer[3] = (devAddr | 0x01); + preamble.ctrlMask = 0x0004; + + CyU3PI2cReceiveBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0); + } else { + /* Write. Update the preamble information. */ + preamble.length = 3; + preamble.buffer[0] = devAddr; + preamble.buffer[1] = (uint8_t)(byteAddress >> 8); + preamble.buffer[2] = (uint8_t)(byteAddress & 0xFF); + preamble.ctrlMask = 0x0000; + + CyU3PI2cTransmitBytes (&preamble, buffer, (pageCount == 1) ? resCount : glI2cPageSize, 0); + /* Wait for the write to complete. */ + preamble.length = 1; + CyU3PI2cWaitForAck(&preamble, 200); + } + + /* An additional delay seems to be required after receiving an ACK. */ + CyU3PThreadSleep (1); + + /* Update the parameters */ + byteAddress += glI2cPageSize; + buffer += glI2cPageSize; + pageCount --; + } +} diff --git a/firmware/fx3/b200/b200_i2c.h b/firmware/fx3/b200/b200_i2c.h new file mode 100644 index 000000000..c5c781946 --- /dev/null +++ b/firmware/fx3/b200/b200_i2c.h @@ -0,0 +1,40 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef _B200_I2C_H +#define _B200_I2C_H + +#include "cyu3externcstart.h" + +#include "cyu3usbconst.h" +#include "cyu3types.h" + +/* Following two definitions made in b200_main.h for consistency. */ +/* define B200_VREQ_EEPROM_WRITE (uint8_t)(0xBA) */ +/* define B200_VREQ_EEPROM_READ (uint8_t)(0xBB) */ + +static uint16_t glI2cPageSize = 0x40; /* I2C Page size to be used for transfers. */ + +/* This application uses EEPROM as the slave I2C device. The I2C EEPROM + * part number used is 24LC256. The capacity of the EEPROM is 256K bits */ +#define CY_FX_USBI2C_I2C_MAX_CAPACITY (32 * 1024) /* Capacity in bytes */ + +/* The following constant is defined based on the page size that the I2C + * device support. 24LC256 support 64 byte page write access. */ +#define CY_FX_USBI2C_I2C_PAGE_SIZE (64) + +/* I2C Data rate */ +#define CY_FX_USBI2C_I2C_BITRATE (100000) + +/* Give a timeout value of 5s for any programming. */ +#define CY_FX_USB_I2C_TIMEOUT (5000) + +/* Function forward-declerations. */ +void CyFxI2cInit (uint16_t pageLen); +void CyFxUsbI2cTransfer (uint16_t byteAddress, uint8_t devAddr, + uint16_t byteCount, uint8_t *buffer, CyBool_t isRead); + +#include "cyu3externcend.h" + +#endif /* _B200_I2C_H */ diff --git a/firmware/fx3/b200/b200_main.c b/firmware/fx3/b200/b200_main.c new file mode 100644 index 000000000..38af9ed4e --- /dev/null +++ b/firmware/fx3/b200/b200_main.c @@ -0,0 +1,3160 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +/* This file defines the application that runs on the Cypress FX3 device, and + * enables the user to program the FPGA with an FPGA image. Since the FPGA + * doesn't yet have a clock, the image must be bit-banged into the FPGA. + */ + +#include <stdarg.h> +#include <stdio.h> + +#include "b200_main.h" +#include "b200_gpifconfig.h" +#include "b200_vrq.h" +#include "b200_i2c.h" + +#include "cyu3dma.h" +#include "cyu3error.h" +#include "cyu3gpif.h" +#include "cyu3gpio.h" +#include "cyu3spi.h" +#include "cyu3os.h" +#include "cyu3pib.h" +#include "cyu3system.h" +#include "cyu3usb.h" +#include "cyu3utils.h" +#include "cyfxversion.h" +#include "pib_regs.h" + +#include <ad9361_transaction.h> +#include <ad9361_dispatch.h> + +#define STATIC_SAVER static // Save stack space for variables in a non-re-entrant function (e.g. USB setup callback) + +/* + * WARNING: Before you enable any of the features below, please read the comments on the same line for that feature! + * Indented features must have the parent feature enabled as well. + */ + +//#define HAS_HEAP // This requires memory to be set aside for the heap (e.g. required for printing floating-point numbers). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to create one. +//#define ENABLE_MSG // This will cause the compiled code to exceed the default text memory area (SYS_MEM). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to resize the memory map so it will fit. +//#define ENABLE_AD9361_LOGGING // When enabling this, you *must* enable the heap with HAS_HEAP (and apply the accompanying memory map patch 'fx3_mem_map.patch') otherwise the FW will crash when printing a floating-point number (as there is no heap for _sbrk by default) +//#define ENABLE_MANUAL_DMA_XFER +//#define ENABLE_MANUAL_DMA_XFER_FROM_HOST +//#define ENABLE_MANUAL_DMA_XFER_TO_HOST +//#define ENABLE_DMA_BUFFER_PACKET_DEBUG +//#define ENABLE_FPGA_SB // Be careful: this will add an ever-so-slight delay to some operations (e.g. AD3961 tune) +#define ENABLE_RE_ENUM_THREAD +#define ENABLE_USB_EVENT_LOGGING +//#define PREVENT_LOW_POWER_MODE +//#define ENABLE_INIT_B_WORKAROUND // This should only be enabled if you have a board where the FPGA INIT_B line is broken, but the FPGA is known to work +//#define ENABLE_DONE_WORKAROUND // This should only be enabled if you have a board where the FPGA DONE line is broken, but the FPGA is known to work + +#define WATCHDOG_TIMEOUT 1500 +#define CHECK_POWER_STATE_SLEEP_TIME 500 // Should be less than WATCHDOG_TIMEOUT + +#define FPGA_PROGRAMMING_POLL_SLEEP 10 // ticks +#define FPGA_PROGRAMMING_BITSTREAM_START_POLL_COUNT 250 // ~2.5 secs +#define FPGA_PROGRAMMING_INITB_POLL_COUNT 100 // ~1 sec +#define FPGA_PROGRAMMING_DONE_POLL_COUNT 250 // ~2.5 secs // This is the interval *after* no FPGA programming activity has been detected + +#define FPGA_RESET_SETTLING_TIME (1*10) // ~10ms (for SB to initialise) + +#define RE_ENUM_THREAD_SLEEP_TIME 100 +#define KEEP_ALIVE_LOOP_COUNT 200 + +#pragma message "----------------------" + +#ifdef ENABLE_MSG +#pragma message "msg enabled" + +#ifdef ENABLE_AD9361_LOGGING +#pragma message " AD9361 logging enabled" +#else +#pragma message " AD9361 logging disabled" +#endif // ENABLE_AD9361_LOGGING + +#else +#pragma message "msg disabled" +#endif // ENABLE_MSG + +#ifdef ENABLE_MANUAL_DMA_XFER +#pragma message "Manual DMA transfers" + +#ifdef ENABLE_MANUAL_DMA_XFER_FROM_HOST +#pragma message " -> From host" +#endif // ENABLE_MANUAL_DMA_XFER_FROM_HOST + +#ifdef ENABLE_MANUAL_DMA_XFER_TO_HOST +#pragma message " <- To host" +#endif // ENABLE_MANUAL_DMA_XFER_TO_HOST + +#ifdef ENABLE_DMA_BUFFER_PACKET_DEBUG +#pragma message " Packet debugging enabled" +#endif // ENABLE_DMA_BUFFER_PACKET_DEBUG + +#else +#pragma message "Auto DMA transfers" +#endif // ENABLE_MANUAL_DMA_XFER + +#ifdef ENABLE_FPGA_SB +#pragma message "FPGA Settings Bus enabled" +#else +#pragma message "FPGA Settings Bus disabled" +#endif // ENABLE_FPGA_SB + +#ifdef ENABLE_RE_ENUM_THREAD +#pragma message "Re-enumeration & statistics thread enabled" +#else +#pragma message "Re-enumeration & statistics thread disabled" +#endif // ENABLE_RE_ENUM_THREAD + +#ifdef ENABLE_USB_EVENT_LOGGING +#pragma message "USB event logging enabled" +#else +#pragma message "USB event logging disabled" +#endif // ENABLE_USB_EVENT_LOGGING + +#ifdef PREVENT_LOW_POWER_MODE +#pragma message "Preventing Low Power Mode" +#else +#pragma message "Allowing Low Power Mode" +#endif // PREVENT_LOW_POWER_MODE + +#ifdef HAS_HEAP +#pragma message "Heap enabled" +#else +#pragma message "Heap disabled" +#endif // HAS_HEAP + +#ifdef ENABLE_INIT_B_WORKAROUND +#pragma message "INIT_B workaround enabled" +#else +#pragma message "INIT_B workaround disabled" +#endif // ENABLE_INIT_B_WORKAROUND + +#ifdef ENABLE_DONE_WORKAROUND +#pragma message "DONE workaround enabled" +#else +#pragma message "DONE workaround disabled" +#endif // ENABLE_DONE_WORKAROUND + +#pragma message "----------------------" + +/* Declare global & static fields for our bit-bang application. */ +static CyU3PDmaChannel data_cons_to_prod_chan_handle; +static CyU3PDmaChannel data_prod_to_cons_chan_handle; + +static CyU3PDmaChannel ctrl_cons_to_prod_chan_handle; +static CyU3PDmaChannel ctrl_prod_to_cons_chan_handle; + +static CyU3PEvent g_event_usb_config; +static CyU3PThread thread_main_app; +static CyU3PThread thread_fpga_config; +#ifdef ENABLE_RE_ENUM_THREAD +static CyU3PThread thread_re_enum; +#endif // ENABLE_RE_ENUM_THREAD +static CyU3PThread thread_ad9361; + +static CyBool_t g_app_running = CyFalse; +static uint8_t g_fx3_state = STATE_UNDEFINED; + +//#define AD9361_DISPATCH_PACKET_SIZE 64 // Must fit into smallest VREQ +#define USB2_VREQ_BUF_SIZE 64 +#define USB3_VREQ_BUF_SIZE 512 +#define MIN_VREQ_BUF_SIZE USB2_VREQ_BUF_SIZE +#define MAX_VREQ_BUF_SIZE USB3_VREQ_BUF_SIZE + +#if AD9361_DISPATCH_PACKET_SIZE > MIN_VREQ_BUF_SIZE +#error "AD9361_DISPATCH_PACKET_SIZE must be less than MIN_VREQ_BUF_SIZE" +#endif + +static uint16_t g_vendor_req_buff_size = MIN_VREQ_BUF_SIZE; +static uint8_t g_vendor_req_buffer[MAX_VREQ_BUF_SIZE] __attribute__ ((aligned (32))); +static uint16_t g_vendor_req_read_count = 0; + +static uint8_t fpga_hash[4] __attribute__ ((aligned (32))); +static uint8_t fw_hash[4] __attribute__ ((aligned (32))); +static uint8_t compat_num[2]; +static uint32_t g_fpga_programming_write_count = 0; + +static char g_ad9361_response[AD9361_DISPATCH_PACKET_SIZE]; + +#define COUNTER_MAGIC 0x10024001 +#define LOG_BUFFER_SIZE /*MAX_VREQ_BUF_SIZE*/1024 // [Max vreq @ USB3 (64 @ USB2)] Can be larger +static char log_buffer[LOG_BUFFER_SIZE]; +static char log_contiguous_buffer[LOG_BUFFER_SIZE]; +static int log_buffer_idx = 0, log_buffer_len = 0; +#ifdef ENABLE_MSG +static int log_count = 0; +#endif // ENABLE_MSG + +#define USB_EVENT_LOG_SIZE 64 +static uint8_t g_usb_event_log[USB_EVENT_LOG_SIZE]; +static uint16_t g_last_usb_event_log_index = 0; +static uint8_t g_usb_event_log_contiguous_buf[USB_EVENT_LOG_SIZE]; + +#ifdef ENABLE_FPGA_SB +static CyBool_t g_fpga_sb_enabled = CyFalse; +static uint16_t g_fpga_sb_uart_div = 434*2; +static uint16_t g_fpga_sb_last_usb_event_log_index = 0; +static CyU3PThread thread_fpga_sb_poll; +static CyU3PMutex g_suart_lock; +#endif // ENABLE_FPGA_SB + +static CyU3PMutex g_log_lock, g_counters_lock, g_counters_dma_from_host_lock, g_counters_dma_to_host_lock; + +#define FPGA_SB_UART_ADDR_BASE 0x00 + +enum UARTRegs +{ + SUART_CLKDIV, + SUART_TXLEVEL, + SUART_RXLEVEL, + SUART_TXCHAR, + SUART_RXCHAR +}; + +enum UARTPacketType +{ + UPT_NONE = '\0', + UPT_MSG = ' ', + UPT_COUNTERS = 'C', + UPT_USB_EVENTS = 'U', +}; + +enum ConfigFlags { + CF_NONE = 0, + CF_TX_SWING = 1 << 0, + CF_TX_DEEMPHASIS = 1 << 1, + CF_DISABLE_USB2 = 1 << 2, + CF_ENABLE_AS_SUPERSPEED = 1 << 3, + CF_PPORT_DRIVE_STRENGTH = 1 << 4, + CF_DMA_BUFFER_SIZE = 1 << 5, + CF_DMA_BUFFER_COUNT = 1 << 6, + CF_MANUAL_DMA = 1 << 7, + + CF_RE_ENUM = 1 << 31 +}; + +typedef struct Config { + int tx_swing; // [90] [65] 45 + int tx_deemphasis; // 0x11 + int disable_usb2; // 0 + int enable_as_superspeed; // 1 + int pport_drive_strength; // CY_U3P_DS_THREE_QUARTER_STRENGTH + int dma_buffer_size; // [USB3] (max) + int dma_buffer_count; // [USB3] 1 + int manual_dma; // 0 + int sb_baud_div; // 434*2 +} CONFIG, *PCONFIG; + +typedef struct ConfigMod { + int flags; + CONFIG config; +} CONFIG_MOD, *PCONFIG_MOD; + +static CONFIG g_config; +static CONFIG_MOD g_config_mod; + +#define REG_LNK_PHY_ERROR_STATUS 0xE0033044 + +enum PhyErrors { + PHYERR_PHY_LOCK_EV = 1 << 8, + PHYERR_TRAINING_ERROR_EV = 1 << 7, + PHYERR_RX_ERROR_CRC32_EV = 1 << 6, + PHYERR_RX_ERROR_CRC16_EV = 1 << 5, + PHYERR_RX_ERROR_CRC5_EV = 1 << 4, + PHYERR_PHY_ERROR_DISPARITY_EV = 1 << 3, + PHYERR_PHY_ERROR_EB_UND_EV = 1 << 2, + PHYERR_PHY_ERROR_EB_OVR_EV = 1 << 1, + PHYERR_PHY_ERROR_DECODE_EV = 1 << 0, + + PHYERR_MAX = PHYERR_PHY_LOCK_EV, + PHYERR_MASK = (PHYERR_MAX << 1) - 1 +}; + +typedef struct USBErrorCounters { + int phy_error_count; + int link_error_count; + + int PHY_LOCK_EV; + int TRAINING_ERROR_EV; + int RX_ERROR_CRC32_EV; + int RX_ERROR_CRC16_EV; + int RX_ERROR_CRC5_EV; + int PHY_ERROR_DISPARITY_EV; + int PHY_ERROR_EB_UND_EV; + int PHY_ERROR_EB_OVR_EV; + int PHY_ERROR_DECODE_EV; +} USB_ERROR_COUNTERS, *PUSB_ERROR_COUNTERS; + +typedef struct DMACounters { + int XFER_CPLT; + int SEND_CPLT; + int RECV_CPLT; + int PROD_EVENT; + int CONS_EVENT; + int ABORTED; + int ERROR; + int PROD_SUSP; + int CONS_SUSP; + + int BUFFER_MARKER; + int BUFFER_EOP; + int BUFFER_ERROR; + int BUFFER_OCCUPIED; + + int last_count; + int last_size; + + int last_sid; + int bad_sid_count; +} DMA_COUNTERS, *PDMA_COUNTERS; + +typedef struct Counters { + int magic; + + DMA_COUNTERS dma_to_host; + DMA_COUNTERS dma_from_host; + + int log_overrun_count; + + int usb_error_update_count; + USB_ERROR_COUNTERS usb_error_counters; + + int usb_ep_underrun_count; + + int heap_size; + + int resume_count; +} COUNTERS, *PCOUNTERS; + +volatile static COUNTERS g_counters; + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif // min + +#define LOCKP(p) CyU3PMutexGet(p, CYU3P_WAIT_FOREVER) +#define UNLOCKP(p) CyU3PMutexPut(p) +#define LOCK(p) LOCKP(&p) +#define UNLOCK(p) UNLOCKP(&p) + +//////////////////////////////////////////////////////////////////////////////// + +char *heap_end = 0; +caddr_t _sbrk(int incr) +{ +#ifdef HAS_HEAP + extern char __heap_start; + extern char __heap_end; + char *prev_heap_end; + + if (heap_end == 0) + { + heap_end = (char *)&__heap_start; + } + prev_heap_end = heap_end; + + if (heap_end + incr > &__heap_end) + { + return (caddr_t) 0; + } + heap_end += incr; + g_counters.heap_size += incr; // Not sync'd + + return (caddr_t) prev_heap_end; +#else + return (caddr_t) -1; +#endif // HAS_HEAP +} + +//////////////////////////////////////////////////////////////////////////////// + +void b200_start_fpga_sb_gpio(void); +void sb_write(uint8_t reg, uint32_t val); +void _sb_write_string(const char* msg); + +void msg(const char* str, ...) { +#define msg_CHECK_USE_LOCK +//void _msgv(int use_lock, const char* str, va_list args) { +//#define msg_CHECK_USE_LOCK if (use_lock) +#ifdef ENABLE_MSG + va_list args; + static char buf[LOG_BUFFER_SIZE]; + int idx = 0; + + msg_CHECK_USE_LOCK + LOCK(g_log_lock); + + ++log_count; + log_count %= 10000; + + va_start(args, str); + + if (1) { // FIXME: Optional + uint32_t time_now = CyU3PGetTime(); + idx += sprintf(buf, "%08X %04i ", (uint)time_now, log_count); + } + else + idx += sprintf(buf, "%04i ", log_count); + idx += vsnprintf(buf + idx, LOG_BUFFER_SIZE - idx, str, args); + + va_end(args); + + if ((LOG_BUFFER_SIZE - log_buffer_len) < (idx + 1 + 1)) { + msg_CHECK_USE_LOCK + LOCK(g_counters_lock); + ++g_counters.log_overrun_count; + msg_CHECK_USE_LOCK + UNLOCK(g_counters_lock); + + goto msg_exit; + } + + // Circular buffer if we need it later, but currently won't wrap due to above condition + memcpy(log_buffer + log_buffer_len, buf, min(idx + 1, LOG_BUFFER_SIZE - log_buffer_len)); + if ((idx + 1) > (LOG_BUFFER_SIZE - log_buffer_len)) + { + memcpy(log_buffer, buf + (LOG_BUFFER_SIZE - log_buffer_len), (idx + 1) - (LOG_BUFFER_SIZE - log_buffer_len)); + log_buffer[(idx + 1) - (LOG_BUFFER_SIZE - log_buffer_len)] = '\0'; + } + else + log_buffer[log_buffer_len + idx + 1] = '\0'; + + log_buffer_len += (idx + 1); +msg_exit: + msg_CHECK_USE_LOCK + UNLOCK(g_log_lock); +#ifdef ENABLE_FPGA_SB + LOCK(g_suart_lock); + sb_write(SUART_TXCHAR, UPT_MSG); + _sb_write_string(buf); + _sb_write_string("\r\n"); + UNLOCK(g_suart_lock); +#endif // ENABLE_FPGA_SB +#endif // ENABLE_MSG +} +/* +void msg(const char* str, ...) +{ + va_list args; + va_start(args, str); + _msgv(1, str, args); + va_end(args); +} + +void msg_nl(const char* str, ...) +{ + va_list args; + va_start(args, str); + _msgv(0, str, args); + va_end(args); +} +*/ +void log_reset(void) { + //LOCK(g_log_lock); + + log_buffer_idx = 0; + log_buffer_len = 0; + log_buffer[0] = '\0'; + + //UNLOCK(g_log_lock); +} + +void counters_auto_reset(void) { + //LOCK(g_counters_lock); + + g_counters.log_overrun_count = 0; + + //UNLOCK(g_counters_lock); +} + +void counters_dma_reset(void) { + LOCK(g_counters_lock); + + LOCK(g_counters_dma_to_host_lock); + memset((void*)&g_counters.dma_to_host, 0x00, sizeof(DMA_COUNTERS)); + UNLOCK(g_counters_dma_to_host_lock); + + LOCK(g_counters_dma_from_host_lock); + memset((void*)&g_counters.dma_from_host, 0x00, sizeof(DMA_COUNTERS)); + UNLOCK(g_counters_dma_from_host_lock); + + UNLOCK(g_counters_lock); +} + +void counters_reset_usb_errors(void) { + LOCK(g_counters_lock); + + g_counters.usb_error_update_count = 0; + memset((void*)&g_counters.usb_error_counters, 0x00, sizeof(g_counters.usb_error_counters)); + + UNLOCK(g_counters_lock); +} + +#ifdef ENABLE_MANUAL_DMA_XFER +/* Callback funtion for the DMA event notification. */ +void dma_callback ( + CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */ + CyU3PDmaCbType_t type, /* Callback type. */ + CyU3PDmaCBInput_t *input, /* Callback status. */ + int from_host) +{ + CyU3PReturnStatus_t status = CY_U3P_SUCCESS; + + PDMA_COUNTERS cnt = (PDMA_COUNTERS)(from_host ? &g_counters.dma_from_host : &g_counters.dma_to_host); + CyU3PMutex* lock = (from_host ? &g_counters_dma_from_host_lock : &g_counters_dma_to_host_lock); + + uint16_t buffer_status = (input->buffer_p.status & CY_U3P_DMA_BUFFER_STATUS_MASK); + if (buffer_status & CY_U3P_DMA_BUFFER_MARKER) + { + cnt->BUFFER_MARKER++; + } + if (buffer_status & CY_U3P_DMA_BUFFER_EOP) + { + cnt->BUFFER_EOP++; + } + if (buffer_status & CY_U3P_DMA_BUFFER_ERROR) + { + cnt->BUFFER_ERROR++; + } + if (buffer_status & CY_U3P_DMA_BUFFER_OCCUPIED) + { + cnt->BUFFER_OCCUPIED++; + } + + if (type == CY_U3P_DMA_CB_PROD_EVENT) + { +#ifdef ENABLE_DMA_BUFFER_PACKET_DEBUG + LOCKP(lock); + int prod_cnt = cnt->PROD_EVENT++; + UNLOCKP(lock); + + if (cnt->last_count != input->buffer_p.count) + msg("[DMA %05d] buffer.count (%d) != last_count (%d)", prod_cnt, input->buffer_p.count, cnt->last_count); + cnt->last_count = input->buffer_p.count; + + if (cnt->last_size != input->buffer_p.size) + msg("[DMA %05d] buffer.size (%d) != last_size (%d)", prod_cnt, input->buffer_p.size, cnt->last_size); + cnt->last_size = input->buffer_p.size; + + uint32_t* p32 = input->buffer_p.buffer; + uint32_t sid = p32[1]; + cnt->last_sid = (int)sid; + if ((sid != 0xa0) && (sid != 0xb0)) + { + cnt->bad_sid_count++; + msg("[DMA %05d] Bad SID: 0x%08x", prod_cnt, sid); + } + + uint16_t* p16 = input->buffer_p.buffer; + + if (p32[0] & (((uint32_t)1) << 31)) + { + msg("[DMA %05d] Error code: 0x%x (packet len: %d)", prod_cnt, p32[4], p16[0]); // Status + + //msg("[DMA] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); + } + else + { + if (p16[1] & (((uint16_t)1) << 12)) + { + msg("[DMA %05d] EOB", prod_cnt); // Comes with one sample + } + + if ((p16[0] != input->buffer_p.count) && + ((p16[0] + 4) != input->buffer_p.count)) + { + msg("[DMA %05d] Packet len (%d) != buffer count (%d)", prod_cnt, p16[0], input->buffer_p.count); + } + + //msg("[DMA] 0x%04x 0x%04x 0x%04x 0x%04x", p16[0], p16[1], p16[2], p16[3]); + + if (p16[1] & (((uint16_t)1) << 12)) + msg("[DMA %05d] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", prod_cnt, p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); + } +#endif // ENABLE_DMA_BUFFER_PACKET_DEBUG + status = CyU3PDmaChannelCommitBuffer (chHandle, input->buffer_p.count, 0); +#ifndef ENABLE_DMA_BUFFER_PACKET_DEBUG + LOCKP(lock); + cnt->PROD_EVENT++; + UNLOCKP(lock); +#endif // !ENABLE_DMA_BUFFER_PACKET_DEBUG + } + else if (type == CY_U3P_DMA_CB_CONS_EVENT) + { + LOCKP(lock); + cnt->CONS_EVENT++; + UNLOCKP(lock); + } + else if (type == CY_U3P_DMA_CB_XFER_CPLT) + { + LOCKP(lock); + cnt->XFER_CPLT++; + UNLOCKP(lock); + } + else if (type == CY_U3P_DMA_CB_SEND_CPLT) + { + LOCKP(lock); + cnt->SEND_CPLT++; + UNLOCKP(lock); + } + else if (type == CY_U3P_DMA_CB_RECV_CPLT) + { + LOCKP(lock); + cnt->RECV_CPLT++; + UNLOCKP(lock); + } + else if (type == CY_U3P_DMA_CB_ABORTED) + { + LOCKP(lock); + cnt->ABORTED++; + UNLOCKP(lock); + + msg("! Aborted %i", from_host); + } + else if (type == CY_U3P_DMA_CB_ERROR) + { + LOCKP(lock); + cnt->ERROR++; + UNLOCKP(lock); + + msg("! Error %i", from_host); + } + else if (type == CY_U3P_DMA_CB_PROD_SUSP) + { + LOCKP(lock); + cnt->PROD_SUSP++; + UNLOCKP(lock); + + msg("! Prod suspend %i", from_host); + } + else if (type == CY_U3P_DMA_CB_CONS_SUSP) + { + LOCKP(lock); + cnt->CONS_SUSP++; + UNLOCKP(lock); + + msg("! Cons suspend %i", from_host); + } +} + +void from_host_dma_callback ( + CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */ + CyU3PDmaCbType_t type, /* Callback type. */ + CyU3PDmaCBInput_t *input) /* Callback status. */ +{ + return dma_callback(chHandle, type, input, 1); +} + +void to_host_dma_callback ( + CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */ + CyU3PDmaCbType_t type, /* Callback type. */ + CyU3PDmaCBInput_t *input) /* Callback status. */ +{ + return dma_callback(chHandle, type, input, 0); +} +#endif // ENABLE_MANUAL_DMA_XFER + +/*! Interrupt callback for GPIOs. + * + * This function is invoked by the GPIO interrupt handler when pins configured + * as inputs with interrupts are triggered. */ +void gpio_interrupt_callback(uint8_t gpio_id) { + CyBool_t gpio_value; + + if ((gpio_id == GPIO_DONE) && (g_fx3_state == STATE_CONFIGURING_FPGA)) { // Only proceed if in the correct FX3 state + CyU3PGpioGetValue(gpio_id, &gpio_value); + + if(gpio_value == CyTrue) { + //msg("DONE HIGH"); + CyU3PEventSet(&g_event_usb_config, EVENT_GPIO_DONE_HIGH, CYU3P_EVENT_OR); + } + } else if ((gpio_id == GPIO_INIT_B) && (g_fx3_state == STATE_FPGA_READY)) { // Only proceed if in the correct FX3 state + CyU3PGpioGetValue(gpio_id, &gpio_value); + + if(gpio_value == CyTrue) { + //msg("INITB_RISE"); + CyU3PEventSet(&g_event_usb_config, EVENT_GPIO_INITB_RISE, CYU3P_EVENT_OR); + } + } +} + + +// The following two functions are intended to replace write_spi_to_ad9361 +// and read_spi_from_ad9361 after the code porting is complete +/*! Perform a register write to the ad9361 chip. + * A pointer to the register address followed by data will be provided as + * parameter + */ +static void write_ad9361_reg(uint16_t reg, uint8_t val) { + + CyBool_t gpio_value; + uint8_t write_buff[3]; + MAKE_AD9361_WRITE(write_buff, reg, val) + + // Number of bytes we are writing. + uint8_t num_bytes = 3; //register address = 2 bytes, data = 1 byte + + CyU3PGpioSetValue(GPIO_FX3_CE, 0); + + // Clock the data out to AD9361 over SPI. + int8_t bit_count, byte_count; + for(byte_count = 0; byte_count < num_bytes; byte_count++) { + + uint8_t miso = 0x00; + uint8_t data = write_buff[byte_count]; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + CyU3PGpioSetValue(GPIO_FX3_MOSI, ((data >> bit_count) & 0x01)); + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + miso |= (1 << bit_count); + } + } + // FIXME: Determine what to do with miso value; + } + + CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); + CyU3PGpioSetValue(GPIO_FX3_CE, 1); +} + +/*! Perform a register read from to the ad9361 chip. + * A pointer to register address will be provided as parameter + * The function returns the value read from the register + */ +static uint8_t read_ad9361_reg(uint16_t reg) { + + CyBool_t gpio_value; + uint8_t write_buff[2]; + MAKE_AD9361_READ(write_buff, reg) + + // Each 9361 register read returns 1 byte + + CyU3PGpioSetValue(GPIO_FX3_CE, 0); + + // Write the two register address bytes. + int8_t bit_count, byte_count; + for(byte_count = 0; byte_count < 2; byte_count++) { + + uint8_t miso = 0x00; + uint8_t data = write_buff[byte_count]; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + CyU3PGpioSetValue(GPIO_FX3_MOSI, ((data >> bit_count) & 0x01)); + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + miso |= (1 << bit_count); + } + } + // FIXME: Determine what to do with miso value; + } + + CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); + + // Read the response data from the chip. + uint8_t data = 0x00; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + data |= (1 << bit_count); + } + + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + } + CyU3PGpioSetValue(GPIO_FX3_CE, 1); + return data; +} + +/*! Perform a register write to the ad9361 chip. + * + * This function will take data received over EP0, as a vendor request, and + * perform a SPI write to ad9361. This requires that the FPGA be passing these + * SPI lines through to the ad9361 chip. */ +void write_spi_to_ad9361(void) { + + CyBool_t gpio_value; + + /* Pull out the number of bytes we are writing. */ + uint8_t num_bytes = ((g_vendor_req_buffer[0] & 0x70) >> 4) + 1; + + CyU3PGpioSetValue(GPIO_FX3_CE, 0); + + /* Clock the data out to AD9361 over SPI. */ + int8_t bit_count, byte_count; + for(byte_count = 0; byte_count < (num_bytes + 2); byte_count++) { + + uint8_t miso = 0x00; + uint8_t data = g_vendor_req_buffer[byte_count]; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + CyU3PGpioSetValue(GPIO_FX3_MOSI, ((data >> bit_count) & 0x01)); + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + miso |= (1 << bit_count); + } + } + + g_vendor_req_buffer[byte_count] = miso; + } + + CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); + CyU3PGpioSetValue(GPIO_FX3_CE, 1); +} + + +/*! Perform a register read from the ad9361 chip. + * + * This function will write a command to the ad9361 chip, performing a register + * read, and store the returned data in the vendor request buffer. This data can + * then be retrieved with another vendor request from the host. + * + * This requires that the FPGA be passing these SPI lines through to the + * ad9361 chip. */ +void read_spi_from_ad9361(void) { + + CyBool_t gpio_value; + + /* Pull out the number of bytes we are reading. */ + uint8_t num_bytes = ((g_vendor_req_buffer[0] & 0x70) >> 4) + 1; + + CyU3PGpioSetValue(GPIO_FX3_CE, 0); + + /* Write the two instruction bytes. */ + int8_t bit_count, byte_count; + for(byte_count = 0; byte_count < 2; byte_count++) { + + uint8_t miso = 0x00; + uint8_t data = g_vendor_req_buffer[byte_count]; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + CyU3PGpioSetValue(GPIO_FX3_MOSI, ((data >> bit_count) & 0x01)); + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + miso |= (1 << bit_count); + } + } + + g_vendor_req_buffer[byte_count] = miso; + } + + CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); + + /* Read the response data from the chip. */ + for(byte_count = 0; byte_count < num_bytes; byte_count++) { + + uint8_t data = 0x00; + + for(bit_count = 7; bit_count >= 0; bit_count--) { + CyU3PGpioSetValue(GPIO_FX3_SCLK, 1); + + CyU3PGpioGetValue(GPIO_FX3_MISO, &gpio_value); + if(gpio_value) { + data |= (1 << bit_count); + } + + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + } + + g_vendor_req_buffer[byte_count + 2] = data; + } + + CyU3PGpioSetValue(GPIO_FX3_CE, 1); +} + + +uint32_t ad9361_transact_spi(const uint32_t bits) { + // FIXME: Could make this more sane + if ((bits >> 23) & 0x1) + { + write_ad9361_reg(bits >> 8, bits & 0xff); + return 0; + } + return read_ad9361_reg(bits >> 8); +} + + +/*! Stops the application, and destroys transport data structures. + * + * This function is essentially a destructor for all transport configurations. + * It ensures that if the USB configuration is reset without a power reboot, + * everything will come back up properly. */ +void b200_fw_stop(void) { + msg("b200_fw_stop"); + + CyU3PEpConfig_t usb_endpoint_config; + + /* Update the flag. */ + g_app_running = CyFalse; + + /* Flush the endpoint memory */ + CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); + + /* Reset the DMA channels */ + // SDK 1.3 known issue #1 - probably not necessary since Destroy is next, but just in case + CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); + CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + CyU3PDmaChannelReset(&ctrl_cons_to_prod_chan_handle); + CyU3PDmaChannelReset(&ctrl_prod_to_cons_chan_handle); + + /* Destroy the DMA channels */ + CyU3PDmaChannelDestroy(&data_cons_to_prod_chan_handle); + CyU3PDmaChannelDestroy(&data_prod_to_cons_chan_handle); + CyU3PDmaChannelDestroy(&ctrl_cons_to_prod_chan_handle); + CyU3PDmaChannelDestroy(&ctrl_prod_to_cons_chan_handle); + + /* Disable endpoints. */ + CyU3PMemSet((uint8_t *) &usb_endpoint_config, 0, \ + sizeof(usb_endpoint_config)); + usb_endpoint_config.enable = CyFalse; + + CyU3PSetEpConfig(DATA_ENDPOINT_PRODUCER, &usb_endpoint_config); + CyU3PSetEpConfig(DATA_ENDPOINT_CONSUMER, &usb_endpoint_config); + CyU3PSetEpConfig(CTRL_ENDPOINT_PRODUCER, &usb_endpoint_config); + CyU3PSetEpConfig(CTRL_ENDPOINT_CONSUMER, &usb_endpoint_config); +} + + +void reset_gpif(void) { + g_fx3_state = STATE_BUSY; + + // Put the FPGA into RESET + CyU3PGpioSetValue(GPIO_FPGA_RESET, CyTrue); + + // Bring down GPIF + CyU3PGpifDisable(CyTrue); + + /* Reset the DMA channels */ + CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); + CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + CyU3PDmaChannelReset(&ctrl_cons_to_prod_chan_handle); + CyU3PDmaChannelReset(&ctrl_prod_to_cons_chan_handle); + + /* Reset the DMA transfers */ + CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, \ + DMA_SIZE_INFINITE); + + CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, \ + DMA_SIZE_INFINITE); + + CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, \ + DMA_SIZE_INFINITE); + + CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, \ + DMA_SIZE_INFINITE); + + /* Flush the USB endpoints */ + CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); + + /* Load the GPIF configuration for Slave FIFO sync mode. */ + CyU3PGpifLoad(&CyFxGpifConfig); + + /* Start the state machine. */ + CyU3PGpifSMStart(RESET, ALPHA_RESET); + + /* Configure the watermarks for the slfifo-write buffers. */ + CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1); + CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1); + CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1); + CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1); + + CyU3PGpioSetValue(GPIO_FPGA_RESET, CyFalse); + + CyU3PThreadSleep(FPGA_RESET_SETTLING_TIME); + + b200_start_fpga_sb_gpio(); + + g_fx3_state = STATE_RUNNING; +} + + +CyU3PReturnStatus_t b200_set_io_matrix(CyBool_t fpga_config_mode) { + CyU3PIoMatrixConfig_t io_config_matrix; + CyU3PReturnStatus_t res; + + /* Configure the IO peripherals on the FX3. The gpioSimpleEn arrays are + * bitmaps, where each bit represents the GPIO of the matching index - the + * second array is index + 32. */ + CyU3PMemSet((uint8_t *) &io_config_matrix, 0, sizeof(io_config_matrix)); + io_config_matrix.isDQ32Bit = (fpga_config_mode == CyFalse); + io_config_matrix.lppMode = CY_U3P_IO_MATRIX_LPP_DEFAULT; + io_config_matrix.gpioSimpleEn[0] = 0 | MASK_GPIO_FPGA_SB_SCL | MASK_GPIO_FPGA_SB_SDA; + io_config_matrix.gpioSimpleEn[1] = MASK_GPIO_PROGRAM_B \ + | MASK_GPIO_INIT_B \ + | (fpga_config_mode ? 0 : \ + // Used once FPGA config is done to bit-bang SPI, etc. + MASK_GPIO_SHDN_SW \ + | MASK_GPIO_AUX_PWR_ON \ + | MASK_GPIO_FX3_SCLK \ + | MASK_GPIO_FX3_CE \ + | MASK_GPIO_FX3_MISO \ + | MASK_GPIO_FX3_MOSI); + io_config_matrix.gpioComplexEn[0] = 0; + io_config_matrix.gpioComplexEn[1] = 0; + io_config_matrix.useUart = CyFalse; + io_config_matrix.useI2C = CyTrue; + io_config_matrix.useI2S = CyFalse; + io_config_matrix.useSpi = fpga_config_mode; + + res = CyU3PDeviceConfigureIOMatrix(&io_config_matrix); + if (res != CY_U3P_SUCCESS) + msg("! ConfigureIOMatrix"); + + return res; +} + + +CyU3PReturnStatus_t b200_gpio_init(CyBool_t set_callback) { + CyU3PGpioClock_t gpio_clock_config; + CyU3PReturnStatus_t res; + + /* Since we are only using FX3's 'simple GPIO' functionality, these values + * must *NOT* change. Cypress says changing them will break stuff. */ + CyU3PMemSet((uint8_t *) &gpio_clock_config, 0, \ + sizeof(gpio_clock_config)); + gpio_clock_config.fastClkDiv = 2; + gpio_clock_config.slowClkDiv = 0; + gpio_clock_config.simpleDiv = CY_U3P_GPIO_SIMPLE_DIV_BY_2; + gpio_clock_config.clkSrc = CY_U3P_SYS_CLK; + gpio_clock_config.halfDiv = 0; + + res = CyU3PGpioInit(&gpio_clock_config, (set_callback ? gpio_interrupt_callback : NULL)); + if (res != CY_U3P_SUCCESS) + msg("! CyU3PGpioInit"); + + return res; +} + + +void sb_write(uint8_t reg, uint32_t val) { +#ifdef ENABLE_FPGA_SB + const int len = 32; + int i; + + if (g_fpga_sb_enabled == CyFalse) + return; + + reg += FPGA_SB_UART_ADDR_BASE; + + //CyU3PBusyWait(1); // Can be used after each SetValue to slow down bit changes + + // START + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // Should already be 1 + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 0); + + // ADDR[8] + for (i = 7; i >= 0; i--) { + uint8_t bit = ((reg & (0x1 << i)) ? 0x01 : 0x00); + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, bit); + + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // FPGA reads bit + } + + // DATA[32] + for (i = (len-1); i >= 0; i--) { + uint8_t bit = ((val & (0x1 << i)) ? 0x01 : 0x00); + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, bit); + + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // FPGA reads bit + } + + // STOP + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 0); + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // Actual stop + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 1); // Xact occurs +#endif // ENABLE_FPGA_SB +} + + +void _sb_write_string(const char* msg) { +#ifdef ENABLE_FPGA_SB + while (*msg) { + sb_write(SUART_TXCHAR, (uint8_t)(*(msg++))); + } +#endif // ENABLE_FPGA_SB +} + + +void sb_write_string(const char* msg) { +#ifdef ENABLE_FPGA_SB + LOCK(g_suart_lock); + _sb_write_string(msg); + UNLOCK(g_suart_lock); +#endif // ENABLE_FPGA_SB +} + + +void b200_enable_fpga_sb_gpio(CyBool_t enable) { +#ifdef ENABLE_FPGA_SB + CyU3PGpioSimpleConfig_t gpio_config; + CyU3PReturnStatus_t res; + + if (enable == CyFalse) { + g_fpga_sb_enabled = CyFalse; + + return; + } + + gpio_config.outValue = CyFalse; + gpio_config.driveLowEn = CyTrue; + gpio_config.driveHighEn = CyTrue; + gpio_config.inputEn = CyFalse; + gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; + + res = CyU3PGpioSetSimpleConfig(GPIO_FPGA_SB_SCL, &gpio_config); + if (res != CY_U3P_SUCCESS) { + msg("! GpioSetSimpleConfig GPIO_FPGA_SB_SCL"); + } + res = CyU3PGpioSetSimpleConfig(GPIO_FPGA_SB_SDA, &gpio_config); + if (res != CY_U3P_SUCCESS) { + msg("! GpioSetSimpleConfig GPIO_FPGA_SB_SDA"); + } + + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); + CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 1); + + g_fpga_sb_enabled = CyTrue; + + msg("Debug SB OK"); +#endif // ENABLE_FPGA_SB +} + + +void b200_start_fpga_sb_gpio(void) { +#ifdef ENABLE_FPGA_SB + LOCK(g_suart_lock); + sb_write(SUART_CLKDIV, g_fpga_sb_uart_div); // 16-bit reg, master clock = 100 MHz (434*2x = 230400/2) + _sb_write_string("\r\n B2x0 FPGA reset\r\n"); + UNLOCK(g_suart_lock); + + msg("Compat: %d.%d", FX3_COMPAT_MAJOR, FX3_COMPAT_MINOR); + msg("FX3 SDK: %d.%d.%d (build %d)", CYFX_VERSION_MAJOR, CYFX_VERSION_MINOR, CYFX_VERSION_PATCH, CYFX_VERSION_BUILD); +#endif // ENABLE_FPGA_SB +} + + +/*! Initialize and configure the GPIO module for FPGA programming. + * + * This function initializes the FX3 GPIO module, creating a configuration that + * allows us to program the FPGA. After the FPGA has been programmed, the + * application thread will re-configure some of the pins. */ +void b200_gpios_pre_fpga_config(void) { + CyU3PGpioSimpleConfig_t gpio_config; + + //b200_enable_fpga_sb_gpio(CyFalse); + + //CyU3PGpioDeInit(); + + b200_set_io_matrix(CyTrue); + + //b200_gpio_init(CyTrue); // This now done once during startup + + //////////////////////////////////// + + /* GPIO[0:32] must be set with the DeviceOverride function, instead of the + * SimpleEn array configuration. */ + CyU3PDeviceGpioOverride(GPIO_FPGA_RESET, CyTrue); + CyU3PDeviceGpioOverride(GPIO_DONE, CyTrue); + + /* Configure GPIOs: + * Outputs: + * driveLowEn = True + * driveHighEn = True + * inputEn = False + * Inputs: + * driveLowEn = False + * driveHighEn = False + * outValue = Ignored + */ + gpio_config.outValue = CyFalse; + gpio_config.driveLowEn = CyTrue; + gpio_config.driveHighEn = CyTrue; + gpio_config.inputEn = CyFalse; + gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; + + CyU3PGpioSetSimpleConfig(GPIO_FPGA_RESET, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_PROGRAM_B, &gpio_config); + + /* Reconfigure the GPIO configure struct for inputs that DO require + * interrupts attached to them. */ + gpio_config.outValue = CyTrue; + gpio_config.inputEn = CyTrue; + gpio_config.driveLowEn = CyFalse; + gpio_config.driveHighEn = CyFalse; + gpio_config.intrMode = CY_U3P_GPIO_INTR_POS_EDGE; + + CyU3PGpioSetSimpleConfig(GPIO_DONE, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_INIT_B, &gpio_config); + + /* Initialize GPIO output values. */ + CyU3PGpioSetValue(GPIO_FPGA_RESET, 0); + CyU3PGpioSetValue(GPIO_PROGRAM_B, 1); + + b200_enable_fpga_sb_gpio(CyTrue); // So SCL/SDA are already high when SB state machine activates +} + + +void b200_slfifo_mode_gpio_config(void) { + CyU3PGpioSimpleConfig_t gpio_config; + + //b200_enable_fpga_sb_gpio(CyFalse); + + //CyU3PGpioDeInit(); + + b200_set_io_matrix(CyFalse); + + //b200_gpio_init(CyFalse); // This now done once during startup + + //////////////////////////////////// + + /* GPIO[0:32] must be set with the DeviceOverride function, instead of the + * SimpleEn array configuration. */ + CyU3PDeviceGpioOverride(GPIO_FPGA_RESET, CyTrue); + CyU3PDeviceGpioOverride(GPIO_DONE, CyTrue); + CyU3PDeviceGpioOverride(GPIO_FX3_SCLK, CyTrue); + CyU3PDeviceGpioOverride(GPIO_FX3_CE, CyTrue); + CyU3PDeviceGpioOverride(GPIO_FX3_MISO, CyTrue); + CyU3PDeviceGpioOverride(GPIO_FX3_MOSI, CyTrue); + + /* Configure GPIOs: + * Outputs: + * driveLowEn = True + * driveHighEn = True + * inputEn = False + * Inputs: + * driveLowEn = False + * driveHighEn = False + * outValue = Ignored + */ + gpio_config.outValue = CyFalse; + gpio_config.driveLowEn = CyTrue; + gpio_config.driveHighEn = CyTrue; + gpio_config.inputEn = CyFalse; + gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; + + CyU3PGpioSetSimpleConfig(GPIO_FPGA_RESET, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_SHDN_SW, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_FX3_SCLK, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_FX3_CE, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_FX3_MOSI, &gpio_config); + + /* Reconfigure the GPIO configure struct for inputs that do NOT require + * interrupts attached to them. */ + gpio_config.outValue = CyFalse; + gpio_config.inputEn = CyTrue; + gpio_config.driveLowEn = CyFalse; + gpio_config.driveHighEn = CyFalse; + gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; + + CyU3PGpioSetSimpleConfig(GPIO_FX3_MISO, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_AUX_PWR_ON, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_PROGRAM_B, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_INIT_B, &gpio_config); + CyU3PGpioSetSimpleConfig(GPIO_DONE, &gpio_config); + + /* Initialize GPIO output values. */ + CyU3PGpioSetValue(GPIO_FPGA_RESET, 0); + CyU3PGpioSetValue(GPIO_SHDN_SW, 1); + CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); + CyU3PGpioSetValue(GPIO_FX3_CE, 1); + CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); + + // Disabled here as only useful once FPGA has been programmed + //b200_enable_fpga_sb_gpio(CyTrue); + //b200_start_fpga_sb_gpio(); // Set set up SB USART +} + + +/*! Initializes and configures USB, and DMA. + * + * This function creates and connects the USB endpoints, and sets up the DMA + * channels. After this is done, everything is 'running' on the FX3 chip, and + * ready to receive data from the host. */ +void b200_fw_start(void) { + msg("b200_fw_start"); + + CyU3PDmaChannelConfig_t dma_channel_config; + CyU3PEpConfig_t usb_endpoint_config; + CyU3PUSBSpeed_t usb_speed; + uint16_t max_packet_size = 0; + uint16_t data_buffer_count = 0; + uint16_t data_buffer_size = 0; + uint16_t data_buffer_size_to_host = 0; + uint16_t data_buffer_size_from_host = 0; + uint8_t num_packets_per_burst = 0; + CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS; + + /* Based on the USB bus speed, configure the endpoint packet size + * and the DMA buffer size */ + usb_speed = CyU3PUsbGetSpeed(); + switch(usb_speed) { + case CY_U3P_FULL_SPEED: + case CY_U3P_HIGH_SPEED: + max_packet_size = 512; + data_buffer_count = 16; + data_buffer_size = 512; + g_vendor_req_buff_size = USB2_VREQ_BUF_SIZE; // Max 64 + num_packets_per_burst = USB2_PACKETS_PER_BURST; // 1 + + data_buffer_size_to_host = data_buffer_size_from_host = data_buffer_size; + + break; + + case CY_U3P_SUPER_SPEED: +//#ifdef PREVENT_LOW_POWER_MODE + apiRetStatus = CyU3PUsbLPMDisable(); // This still allows my laptop to sleep + + if (apiRetStatus != CY_U3P_SUCCESS) + msg("! LPMDisable failed (%d)", apiRetStatus); + else + msg("LPMDisable OK"); +//#endif // PREVENT_LOW_POWER_MODE + max_packet_size = 1024; // Per USB3 spec + + // SDK ver: total available buffer memory + // 1.2.3: 204KB + // 1.3.1: 188KB + + // These options should be ignored - data_buffer_count *MUST* be 1 + // They follow is kept for future testing + + // 1K + //data_buffer_count = 64; + //data_buffer_size = 1024; + + // 4K + //data_buffer_count = 8; + //data_buffer_size = 4096; + + // 16K + //data_buffer_count = 2*2; + //data_buffer_size = 16384; // Default 16K + + // 32K + //data_buffer_count = 2; + //data_buffer_size = 16384*2; + + data_buffer_count = 1; + data_buffer_size = ((1 << 16) - 1); + data_buffer_size -= (data_buffer_size % 1024); // Align to 1K boundary + + data_buffer_size_to_host = data_buffer_size; + data_buffer_size_from_host = data_buffer_size; + + g_vendor_req_buff_size = USB3_VREQ_BUF_SIZE; // Max 512 + num_packets_per_burst = USB3_PACKETS_PER_BURST; // 16 + break; + + case CY_U3P_NOT_CONNECTED: + msg("! CY_U3P_NOT_CONNECTED"); + return; + + default: + return; + } + + msg("[DMA] to host: %d, from host: %d, depth: %d, burst size: %d", data_buffer_size_to_host, data_buffer_size_from_host, data_buffer_count, num_packets_per_burst); + + /************************************************************************* + * Slave FIFO Data DMA Channel Configuration + *************************************************************************/ + + /* Wipe out any old config. */ + CyU3PMemSet((uint8_t *) &usb_endpoint_config, 0, \ + sizeof(usb_endpoint_config)); + + /* This is the configuration for the USB Producer and Consumer endpoints. + * + * The Producer endpoint is actually the endpoint on the FX3 that is + * sending data BACK to the host. This endpoint enumerates as the + * 'BULK IN' endpoint. + + * The Consumer endpoint is the endpoint on the FX3 that is + * receiving data from the host. This endpoint enumerates as the + * 'BULK OUT' endpoint. + * + * Note that this is opposite of what you might expect!. */ + usb_endpoint_config.enable = CyTrue; + usb_endpoint_config.epType = CY_U3P_USB_EP_BULK; + usb_endpoint_config.burstLen = num_packets_per_burst; + usb_endpoint_config.streams = 0; + usb_endpoint_config.pcktSize = max_packet_size; + + /* Configure the endpoints that we are using for slave FIFO transfers. */ + CyU3PSetEpConfig(DATA_ENDPOINT_PRODUCER, &usb_endpoint_config); + CyU3PSetEpConfig(DATA_ENDPOINT_CONSUMER, &usb_endpoint_config); + + /* Create a DMA AUTO channel for U2P transfer. + * DMA size is set based on the USB speed. */ + //dma_channel_config.size = data_buffer_size; + dma_channel_config.size = data_buffer_size_from_host; + dma_channel_config.count = data_buffer_count; + dma_channel_config.prodSckId = PRODUCER_DATA_SOCKET; + dma_channel_config.consSckId = DATA_TX_PPORT_SOCKET; + dma_channel_config.dmaMode = CY_U3P_DMA_MODE_BYTE; + dma_channel_config.notification = 0 | +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_FROM_HOST) +CY_U3P_DMA_CB_XFER_CPLT | +CY_U3P_DMA_CB_SEND_CPLT | +CY_U3P_DMA_CB_RECV_CPLT | +CY_U3P_DMA_CB_PROD_EVENT | +CY_U3P_DMA_CB_CONS_EVENT | +CY_U3P_DMA_CB_ABORTED | +CY_U3P_DMA_CB_ERROR | +CY_U3P_DMA_CB_PROD_SUSP | +CY_U3P_DMA_CB_CONS_SUSP | +#endif // ENABLE_MANUAL_DMA_XFER + 0; + dma_channel_config.cb = +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_FROM_HOST) + from_host_dma_callback; +#else + NULL; +#endif // ENABLE_MANUAL_DMA_XFER + dma_channel_config.prodHeader = 0; + dma_channel_config.prodFooter = 0; + dma_channel_config.consHeader = 0; + dma_channel_config.prodAvailCount = 0; + + CyU3PDmaChannelCreate (&data_cons_to_prod_chan_handle, +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_FROM_HOST) + /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL, +#else + CY_U3P_DMA_TYPE_AUTO, +#endif // ENABLE_MANUAL_DMA_XFER + &dma_channel_config); + + // By default these will adopt 'usb_endpoint_config.pcktSize' + //CyU3PSetEpPacketSize(DATA_ENDPOINT_PRODUCER, 16384); + //CyU3PSetEpPacketSize(DATA_ENDPOINT_CONSUMER, 16384); + + /* Create a DMA AUTO channel for P2U transfer. */ + dma_channel_config.size = data_buffer_size_to_host; + dma_channel_config.prodSckId = DATA_RX_PPORT_SOCKET; + dma_channel_config.consSckId = CONSUMER_DATA_SOCKET; + dma_channel_config.notification = 0 | +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) +CY_U3P_DMA_CB_XFER_CPLT | +CY_U3P_DMA_CB_SEND_CPLT | +CY_U3P_DMA_CB_RECV_CPLT | +CY_U3P_DMA_CB_PROD_EVENT | +CY_U3P_DMA_CB_CONS_EVENT | +CY_U3P_DMA_CB_ABORTED | +CY_U3P_DMA_CB_ERROR | +CY_U3P_DMA_CB_PROD_SUSP | +CY_U3P_DMA_CB_CONS_SUSP | +#endif // ENABLE_MANUAL_DMA_XFER + 0; + dma_channel_config.cb = +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) + to_host_dma_callback; +#else + NULL; +#endif // ENABLE_MANUAL_DMA_XFER + CyU3PDmaChannelCreate (&data_prod_to_cons_chan_handle, +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) + /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL, +#else + CY_U3P_DMA_TYPE_AUTO, +#endif // ENABLE_MANUAL_DMA_XFER + &dma_channel_config); + + /* Flush the Endpoint memory */ + CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + + /* Set DMA channel transfer size. */ + CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); + + + /************************************************************************* + * Slave FIFO Control DMA Channel Configuration + *************************************************************************/ + + /* Wipe out any old config. */ + CyU3PMemSet((uint8_t *) &usb_endpoint_config, 0, \ + sizeof(usb_endpoint_config)); + + /* This is the configuration for the USB Producer and Consumer endpoints. + * + * The Producer endpoint is actually the endpoint on the FX3 that is + * sending data BACK to the host. This endpoint enumerates as the + * 'BULK IN' endpoint. + + * The Consumer endpoint is the endpoint on the FX3 that is + * receiving data from the host. This endpoint enumerates as the + * 'BULK OUT' endpoint. + * + * Note that this is opposite of what you might expect!. */ + usb_endpoint_config.enable = CyTrue; + usb_endpoint_config.epType = CY_U3P_USB_EP_BULK; + usb_endpoint_config.burstLen = num_packets_per_burst; + usb_endpoint_config.streams = 0; + usb_endpoint_config.pcktSize = max_packet_size; + + /* Configure the endpoints that we are using for slave FIFO transfers. */ + CyU3PSetEpConfig(CTRL_ENDPOINT_PRODUCER, &usb_endpoint_config); + CyU3PSetEpConfig(CTRL_ENDPOINT_CONSUMER, &usb_endpoint_config); + + /* Create a DMA AUTO channel for U2P transfer. + * DMA size is set based on the USB speed. */ + dma_channel_config.size = max_packet_size; + dma_channel_config.count = 2; + dma_channel_config.prodSckId = PRODUCER_CTRL_SOCKET; + dma_channel_config.consSckId = CTRL_COMM_PPORT_SOCKET; + dma_channel_config.dmaMode = CY_U3P_DMA_MODE_BYTE; + dma_channel_config.notification = 0; + dma_channel_config.cb = NULL; + dma_channel_config.prodHeader = 0; + dma_channel_config.prodFooter = 0; + dma_channel_config.consHeader = 0; + dma_channel_config.prodAvailCount = 0; + + CyU3PDmaChannelCreate (&ctrl_cons_to_prod_chan_handle, + CY_U3P_DMA_TYPE_AUTO, &dma_channel_config); + + /* Create a DMA AUTO channel for P2U transfer. */ + dma_channel_config.prodSckId = CTRL_RESP_PPORT_SOCKET; + dma_channel_config.consSckId = CONSUMER_CTRL_SOCKET; + dma_channel_config.cb = NULL; + CyU3PDmaChannelCreate (&ctrl_prod_to_cons_chan_handle, + CY_U3P_DMA_TYPE_AUTO, &dma_channel_config); + + /* Flush the Endpoint memory */ + CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); + + /* Set DMA channel transfer size. */ + CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); + + //CyU3PUsbEnableEPPrefetch(); // To address USB_EVENT_EP_UNDERRUN on EP 0x86 (didn't fix it though) + + /* Update the application status flag. */ + g_app_running = CyTrue; +} + + +/*! This callback is invoked when the FX3 detects a USB event. + * + * We currently handle SETCONF, RESET, and DISCONNECT. + * + * We are _not_ handling SUSPEND or CONNECT. + */ +void event_usb_callback (CyU3PUsbEventType_t event_type, uint16_t event_data) { + + switch(event_type) { + case CY_U3P_USB_EVENT_SETCONF: + msg("USB_EVENT_SETCONF (#%d)", event_data); //evData provides the configuration number that is selected by the host. + if(g_app_running) { + b200_fw_stop(); + } + + b200_fw_start(); + break; + + case CY_U3P_USB_EVENT_RESET: + case CY_U3P_USB_EVENT_DISCONNECT: + if (event_type == CY_U3P_USB_EVENT_RESET) + msg("USB_EVENT_RESET"); + else + msg("USB_EVENT_DISCONNECT"); + if(g_app_running) { + b200_fw_stop(); + } + break; + + case CY_U3P_USB_EVENT_CONNECT: + msg("USB_EVENT_CONNECT"); + break; + + case CY_U3P_USB_EVENT_SUSPEND: + msg("USB_EVENT_SUSPEND"); + break; + + case CY_U3P_USB_EVENT_RESUME: // Known issue: this is called repeatedly after a resume + //msg("USB_EVENT_RESUME"); + g_counters.resume_count++; // Not locked + break; + + case CY_U3P_USB_EVENT_SPEED: + msg("USB_EVENT_SPEED"); + break; + + case CY_U3P_USB_EVENT_SETINTF: + msg("USB_EVENT_SETINTF"); + break; + + case CY_U3P_USB_EVENT_SET_SEL: + msg("USB_EVENT_SET_SEL"); + break; + + case CY_U3P_USB_EVENT_SOF_ITP: // CyU3PUsbEnableITPEvent + //msg("USB_EVENT_SOF_ITP"); + break; + + case CY_U3P_USB_EVENT_EP0_STAT_CPLT: + //msg("USB_EVENT_EP0_STAT_CPLT"); // Occurs each time there's a control transfer + break; + + case CY_U3P_USB_EVENT_VBUS_VALID: + msg("USB_EVENT_VBUS_VALID"); + break; + + case CY_U3P_USB_EVENT_VBUS_REMOVED: + msg("USB_EVENT_VBUS_REMOVED"); + break; + + case CY_U3P_USB_EVENT_HOST_CONNECT: + msg("USB_EVENT_HOST_CONNECT"); + break; + + case CY_U3P_USB_EVENT_HOST_DISCONNECT: + msg("USB_EVENT_HOST_DISCONNECT"); + break; + + case CY_U3P_USB_EVENT_OTG_CHANGE: + msg("USB_EVENT_OTG_CHANGE"); + break; + + case CY_U3P_USB_EVENT_OTG_VBUS_CHG: + msg("USB_EVENT_OTG_VBUS_CHG"); + break; + + case CY_U3P_USB_EVENT_OTG_SRP: + msg("USB_EVENT_OTG_SRP"); + break; + + case CY_U3P_USB_EVENT_EP_UNDERRUN: // See SDK 1.3 known issues 17 if this happens (can probably ignore first logged occurence) + LOCK(g_counters_lock); + ++g_counters.usb_ep_underrun_count; + UNLOCK(g_counters_lock); + + msg("! USB_EVENT_EP_UNDERRUN on EP 0x%02x", event_data); + break; + + case CY_U3P_USB_EVENT_LNK_RECOVERY: + msg("USB_EVENT_LNK_RECOVERY"); + break; +#if (CYFX_VERSION_MAJOR >= 1) && (CYFX_VERSION_MINOR >= 3) + case CY_U3P_USB_EVENT_USB3_LNKFAIL: + msg("USB_EVENT_USB3_LNKFAIL"); + break; + + case CY_U3P_USB_EVENT_SS_COMP_ENTRY: + msg("USB_EVENT_SS_COMP_ENTRY"); + break; + + case CY_U3P_USB_EVENT_SS_COMP_EXIT: + msg("USB_EVENT_SS_COMP_EXIT"); + break; +#endif // (CYFX_VERSION_MAJOR >= 1) && (CYFX_VERSION_MINOR >= 3) + + default: + msg("! Unhandled USB event"); + break; + } +} + + +/*! Callback function that is invoked when a USB setup event occurs. + * + * We aren't actually handling the USB setup ourselves, but rather letting the + * USB driver take care of it since the default options work fine. The purpose + * of this function is to register that the event happened at all, so that the + * application thread knows it can proceed. + * + * This function is also responsible for receiving vendor requests, and trigging + * the appropriate RTOS event to wake up the vendor request handler thread. + */ +CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { + STATIC_SAVER uint8_t bRequestType, bRequest, bType, bTarget, i2cAddr; + STATIC_SAVER uint16_t wValue, wIndex, wLength; + + CyBool_t handled = CyFalse; + + /* Decode the fields from the setup request. */ + bRequestType = (uint8_t)(data0 & CY_U3P_USB_REQUEST_TYPE_MASK); + bType = (uint8_t)(bRequestType & CY_U3P_USB_TYPE_MASK); + bTarget = (uint8_t)(bRequestType & CY_U3P_USB_TARGET_MASK); + bRequest = (uint8_t)((data0 & CY_U3P_USB_REQUEST_MASK) >> CY_U3P_USB_REQUEST_POS); + wValue = (uint16_t)((data0 & CY_U3P_USB_VALUE_MASK) >> CY_U3P_USB_VALUE_POS); + wIndex = (uint16_t)((data1 & CY_U3P_USB_INDEX_MASK) >> CY_U3P_USB_INDEX_POS); + wLength = (uint16_t)((data1 & CY_U3P_USB_LENGTH_MASK) >> CY_U3P_USB_LENGTH_POS); + + if(bType == CY_U3P_USB_STANDARD_RQT) { + /* Handle SET_FEATURE(FUNCTION_SUSPEND) and CLEAR_FEATURE(FUNCTION_SUSPEND) + * requests here. It should be allowed to pass if the device is in configured + * state and failed otherwise. */ + if((bTarget == CY_U3P_USB_TARGET_INTF) \ + && ((bRequest == CY_U3P_USB_SC_SET_FEATURE) \ + || (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE)) && (wValue == 0)) { + + if(g_app_running) { + CyU3PUsbAckSetup(); + msg("ACK set/clear"); + } else { + CyU3PUsbStall(0, CyTrue, CyFalse); + msg("! STALL set/clear"); + } + + handled = CyTrue; + } + + /* Handle Microsoft OS String Descriptor request. */ + if((bTarget == CY_U3P_USB_TARGET_DEVICE) \ + && (bRequest == CY_U3P_USB_SC_GET_DESCRIPTOR) \ + && (wValue == ((CY_U3P_USB_STRING_DESCR << 8) | 0xEE))) { + /* Make sure we do not send more data than requested. */ + if(wLength > b200_usb_product_desc[0]) { + wLength = b200_usb_product_desc[0]; + } + + //msg("MS string desc"); + + CyU3PUsbSendEP0Data(wLength, ((uint8_t *) b200_usb_product_desc)); + handled = CyTrue; + } + + /* CLEAR_FEATURE request for endpoint is always passed to the setup callback + * regardless of the enumeration model used. When a clear feature is received, + * the previous transfer has to be flushed and cleaned up. This is done at the + * protocol level. Since this is just a loopback operation, there is no higher + * level protocol. So flush the EP memory and reset the DMA channel associated + * with it. If there are more than one EP associated with the channel reset both + * the EPs. The endpoint stall and toggle / sequence number is also expected to be + * reset. Return CyFalse to make the library clear the stall and reset the endpoint + * toggle. Or invoke the CyU3PUsbStall (ep, CyFalse, CyTrue) and return CyTrue. + * Here we are clearing the stall. */ + if((bTarget == CY_U3P_USB_TARGET_ENDPT) \ + && (bRequest == CY_U3P_USB_SC_CLEAR_FEATURE) + && (wValue == CY_U3P_USBX_FS_EP_HALT)) { + if(g_app_running) { + if(wIndex == DATA_ENDPOINT_PRODUCER) { + CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); + CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbResetEp(DATA_ENDPOINT_PRODUCER); + CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, \ + DMA_SIZE_INFINITE); + CyU3PUsbStall(wIndex, CyFalse, CyTrue); + handled = CyTrue; + CyU3PUsbAckSetup(); + + msg("Clear DATA_ENDPOINT_PRODUCER"); + } + + if(wIndex == DATA_ENDPOINT_CONSUMER) { + CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); + CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, \ + DMA_SIZE_INFINITE); + CyU3PUsbStall(wIndex, CyFalse, CyTrue); + handled = CyTrue; + CyU3PUsbAckSetup(); + + msg("Clear DATA_ENDPOINT_CONSUMER"); + } + + if(wIndex == CTRL_ENDPOINT_PRODUCER) { + CyU3PDmaChannelReset(&ctrl_cons_to_prod_chan_handle); + CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); + CyU3PUsbResetEp(CTRL_ENDPOINT_PRODUCER); + CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, \ + DMA_SIZE_INFINITE); + CyU3PUsbStall(wIndex, CyFalse, CyTrue); + handled = CyTrue; + CyU3PUsbAckSetup(); + + msg("Clear CTRL_ENDPOINT_PRODUCER"); + } + + if(wIndex == CTRL_ENDPOINT_CONSUMER) { + CyU3PDmaChannelReset(&ctrl_prod_to_cons_chan_handle); + CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); + CyU3PUsbResetEp(CTRL_ENDPOINT_CONSUMER); + CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, \ + DMA_SIZE_INFINITE); + CyU3PUsbStall(wIndex, CyFalse, CyTrue); + handled = CyTrue; + CyU3PUsbAckSetup(); + + msg("Clear CTRL_ENDPOINT_CONSUMER"); + } + } + } + } + /* This must be & and not == so that we catch VREQs that are both 'IN' and + * 'OUT' in direction. */ + else if(bRequestType & CY_U3P_USB_VENDOR_RQT) { + + handled = CyTrue; + uint16_t read_count = 0; + + switch(bRequest) { + case B200_VREQ_BITSTREAM_START: { + CyU3PUsbGetEP0Data(1, g_vendor_req_buffer, &read_count); + + g_fpga_programming_write_count = 0; + + CyU3PEventSet(&g_event_usb_config, EVENT_BITSTREAM_START, \ + CYU3P_EVENT_OR); + break; + } + + case B200_VREQ_BITSTREAM_DATA: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, \ + &read_count); + + if (g_fx3_state == STATE_CONFIGURING_FPGA) { + ++g_fpga_programming_write_count; + CyU3PSpiTransmitWords(g_vendor_req_buffer, read_count); + CyU3PThreadSleep(1); // Newer controllers don't have an issue when this short sleep here + } + break; + } + + case B200_VREQ_BITSTREAM_DATA_FILL: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &g_vendor_req_read_count); + break; + } + + case B200_VREQ_BITSTREAM_DATA_COMMIT: { + /*CyU3PReturnStatus_t*/int spi_result = -1; + if (g_fx3_state == STATE_CONFIGURING_FPGA) { + ++g_fpga_programming_write_count; + spi_result = CyU3PSpiTransmitWords(g_vendor_req_buffer, g_vendor_req_read_count); + CyU3PThreadSleep(1); // 20 MHz, 512 bytes + } + CyU3PUsbSendEP0Data(sizeof(spi_result), (uint8_t*)&spi_result); + break; + } + + case B200_VREQ_FPGA_CONFIG: { + CyU3PUsbGetEP0Data(1, g_vendor_req_buffer, &read_count); + + CyU3PEventSet(&g_event_usb_config, EVENT_FPGA_CONFIG, CYU3P_EVENT_OR); + break; + } + + case B200_VREQ_GET_COMPAT: { + CyU3PUsbSendEP0Data(/*2*/sizeof(compat_num), compat_num); + break; + } + + case B200_VREQ_SET_FPGA_HASH: { + CyU3PUsbGetEP0Data(4, fpga_hash, &read_count); + break; + } + + case B200_VREQ_GET_FPGA_HASH: { + CyU3PUsbSendEP0Data(/*4*/sizeof(fpga_hash), fpga_hash); + break; + } + + case B200_VREQ_SET_FW_HASH: { + CyU3PUsbGetEP0Data(4, fw_hash, &read_count); + break; + } + + case B200_VREQ_GET_FW_HASH: { + CyU3PUsbSendEP0Data(/*4*/sizeof(fw_hash), fw_hash); + break; + } + + case B200_VREQ_SPI_WRITE_AD9361: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, \ + &read_count); + + write_spi_to_ad9361(); // FIXME: Should have g_vendor_req_buffer & read_count passed in as args + break; + } + + case B200_VREQ_SPI_READ_AD9361: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, \ + &read_count); + + read_spi_from_ad9361(); // FIXME: Should have g_vendor_req_buffer & read_count passed in as args + break; + } + + case B200_VREQ_LOOP_CODE: { + CyU3PUsbSendEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer); + break; + } + + case B200_VREQ_GET_LOG: { + LOCK(g_log_lock); + + if (log_buffer_idx == 0) + CyU3PUsbSendEP0Data(log_buffer_len, (uint8_t*)log_buffer); + else { + int len1 = min(LOG_BUFFER_SIZE - log_buffer_idx, log_buffer_len); + memcpy(log_contiguous_buffer, log_buffer + log_buffer_idx, len1); + //if ((log_buffer_idx + log_buffer_len) > LOG_BUFFER_SIZE) + if (len1 < log_buffer_len) + memcpy(log_contiguous_buffer + len1, log_buffer, log_buffer_len - len1); + CyU3PUsbSendEP0Data(log_buffer_len, (uint8_t*)log_contiguous_buffer); + } + + // FIXME: Necessary? Not used in the other ones + //CyU3PUsbSendEP0Data(0, NULL); // Send ZLP since previous send has resulted in an integral # of packets + + log_reset(); + + UNLOCK(g_log_lock); + + //log_reset(); + + break; + } + + case B200_VREQ_GET_COUNTERS: { + LOCK(g_counters_lock); + + CyU3PUsbSendEP0Data(sizeof(COUNTERS), (uint8_t*)&g_counters); + + counters_auto_reset(); + + UNLOCK(g_counters_lock); + + //counters_auto_reset(); + + break; + } + + case B200_VREQ_CLEAR_COUNTERS: { + CyU3PUsbAckSetup(); + //CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &read_count); // Dummy + + counters_dma_reset(); + + break; + } + + case B200_VREQ_GET_USB_EVENT_LOG: { + uint16_t idx = CyU3PUsbGetEventLogIndex(); // Current *write* pointer + if (idx > (USB_EVENT_LOG_SIZE-1)) { + msg("! USB event log idx = %i", (int)idx); + break; + } + // Assuming logging won't wrap around between get calls (i.e. buffer should be long enough) + uint16_t len = 0; + if (idx < g_last_usb_event_log_index) { + uint16_t len1 = (USB_EVENT_LOG_SIZE - g_last_usb_event_log_index); + if (len1 > (USB_EVENT_LOG_SIZE-1)) { + msg("! USB event log len 2.1 = %i", (int)len1); + break; + } + len = len1 + idx; + if (len > (USB_EVENT_LOG_SIZE-1)) { + msg("! USB event log len 2.2 = %i", (int)len); + break; + } + memcpy(g_usb_event_log_contiguous_buf, g_usb_event_log + g_last_usb_event_log_index, len1); + memcpy(g_usb_event_log_contiguous_buf + len1, g_usb_event_log, idx); + //msg("USB event log [2] %i %i", (int)len1, (int)len); + } else { + len = idx - g_last_usb_event_log_index; + if (len > (USB_EVENT_LOG_SIZE-1)) { + msg("! USB event log len 1 = %i", (int)len); + break; + } + if (len > 0) { // ZLP should be OK + memcpy(g_usb_event_log_contiguous_buf, g_usb_event_log + g_last_usb_event_log_index, len); + //msg("USB event log [1] %i", (int)len); + } + } + + //if (len > 0) // Send a ZLP, otherwise it'll timeout + CyU3PUsbSendEP0Data(len, g_usb_event_log_contiguous_buf); + + g_last_usb_event_log_index = idx; + break; + } + + case B200_VREQ_SET_CONFIG: { + CyU3PUsbGetEP0Data(sizeof(CONFIG_MOD), (uint8_t*)g_vendor_req_buffer, &read_count); + if (read_count == sizeof(CONFIG_MOD)) { + memcpy(&g_config_mod, g_vendor_req_buffer, sizeof(CONFIG_MOD)); + CyU3PEventSet(&g_event_usb_config, EVENT_RE_ENUM, CYU3P_EVENT_OR); + } + break; + } + + case B200_VREQ_GET_CONFIG: { + CyU3PUsbSendEP0Data(sizeof(g_config), (uint8_t*)&g_config); + break; + } + + case B200_VREQ_WRITE_SB: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, (uint8_t*)g_vendor_req_buffer, &read_count); +#ifdef ENABLE_FPGA_SB + uint16_t i; + LOCK(g_suart_lock); + for (i = 0; i < read_count; ++i) + sb_write(SUART_TXCHAR, g_vendor_req_buffer[i]); + UNLOCK(g_suart_lock); + + msg("Wrote %d SB chars", read_count); +#else + msg("SB is disabled"); +#endif // ENABLE_FPGA_SB + break; + } + + case B200_VREQ_SET_SB_BAUD_DIV: { + uint16_t div; + CyU3PUsbGetEP0Data(sizeof(div), (uint8_t*)&div, &read_count); + + if (read_count == sizeof(div)) { +#ifdef ENABLE_FPGA_SB + LOCK(g_suart_lock); + sb_write(SUART_CLKDIV, div); + UNLOCK(g_suart_lock); + msg("SUART_CLKDIV = %d", div); + g_fpga_sb_uart_div = div; // Store for GPIF (FPGA) reset +#else + msg("SB is disabled"); +#endif // ENABLE_FPGA_SB + } + else + msg("! SUART_CLKDIV received %d bytes", read_count); + + break; + } + + case B200_VREQ_FLUSH_DATA_EPS: { + //msg("Flushing data EPs..."); + + CyU3PUsbAckSetup(); + + // From host + //CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); + //CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + //CyU3PUsbResetEp(DATA_ENDPOINT_PRODUCER); + //CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + + //CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); + CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + //CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + //CyU3PUsbResetEp(DATA_ENDPOINT_PRODUCER); + CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); + //CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); + + // To host + //CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + //CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); + //CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); + //CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); + + break; + } + + case B200_VREQ_EEPROM_WRITE: { + i2cAddr = 0xA0 | ((wValue & 0x0007) << 1); + CyU3PUsbGetEP0Data(((wLength + 15) & 0xFFF0), g_vendor_req_buffer, NULL); + + CyFxUsbI2cTransfer (wIndex, i2cAddr, wLength, + g_vendor_req_buffer, CyFalse); + break; + } + + case B200_VREQ_EEPROM_READ: { + i2cAddr = 0xA0 | ((wValue & 0x0007) << 1); + CyU3PMemSet (g_vendor_req_buffer, 0, sizeof (g_vendor_req_buffer)); + CyFxUsbI2cTransfer (wIndex, i2cAddr, wLength, + g_vendor_req_buffer, CyTrue); + + CyU3PUsbSendEP0Data(wLength, g_vendor_req_buffer); + break; + } + + case B200_VREQ_TOGGLE_FPGA_RESET: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, \ + &read_count); + + /* CyBool_t value = (g_vendor_req_buffer[0] & 0x01) ? CyTrue : CyFalse; + CyU3PGpioSetValue(GPIO_FPGA_RESET, value); */ + break; + } + + case B200_VREQ_TOGGLE_GPIF_RESET: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, \ + &read_count); + + reset_gpif(); + break; + } + + case B200_VREQ_RESET_DEVICE: { + CyU3PUsbGetEP0Data(4, g_vendor_req_buffer, &read_count); + + CyU3PDeviceReset(CyFalse); // FIXME: If CyTrue, this will *not* call static initialisers for global variables - must do this manually + break; + } + + case B200_VREQ_GET_USB_SPEED: { + CyU3PUSBSpeed_t usb_speed = CyU3PUsbGetSpeed(); + switch(usb_speed) { + case CY_U3P_SUPER_SPEED: + g_vendor_req_buffer[0] = 3; + break; + + case CY_U3P_FULL_SPEED: + case CY_U3P_HIGH_SPEED: + g_vendor_req_buffer[0] = 2; + break; + + default: + g_vendor_req_buffer[0] = 1; + break; + } + + CyU3PUsbSendEP0Data(1, g_vendor_req_buffer); + break; + } + + case B200_VREQ_GET_STATUS: { + g_vendor_req_buffer[0] = g_fx3_state; + CyU3PUsbSendEP0Data(1, g_vendor_req_buffer); + break; + } + + case B200_VREQ_AD9361_CTRL_READ: { + CyU3PUsbSendEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer); + /* + * This is where vrb gets sent back to the host + */ + break; + } + + case B200_VREQ_AD9361_CTRL_WRITE: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &read_count); + CyU3PEventSet(&g_event_usb_config, EVENT_AD9361_XACT_INIT, CYU3P_EVENT_OR); + + uint32_t event_flag; + CyU3PEventGet(&g_event_usb_config, EVENT_AD9361_XACT_DONE, CYU3P_EVENT_AND_CLEAR, &event_flag, CYU3P_WAIT_FOREVER); + + memcpy(g_vendor_req_buffer, g_ad9361_response, AD9361_DISPATCH_PACKET_SIZE); + break; + } + + case B200_VREQ_AD9361_LOOPBACK: { + CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &read_count); + + if (read_count > 0) { + ad9361_transaction_t xact; + memset(&xact, 0x00, sizeof(xact)); + + xact.version = AD9361_TRANSACTION_VERSION; + xact.action = AD9361_ACTION_SET_CODEC_LOOP; + xact.sequence = 0; + xact.value.codec_loop = g_vendor_req_buffer[0]; + + memcpy(g_vendor_req_buffer, &xact, sizeof(xact)); + + CyU3PEventSet(&g_event_usb_config, EVENT_AD9361_XACT_INIT, CYU3P_EVENT_OR); + + uint32_t event_flag; + CyU3PEventGet(&g_event_usb_config, EVENT_AD9361_XACT_DONE, CYU3P_EVENT_AND_CLEAR, &event_flag, CYU3P_WAIT_FOREVER); + + memcpy(g_vendor_req_buffer, g_ad9361_response, AD9361_DISPATCH_PACKET_SIZE); + + if (xact.value.codec_loop) + msg("Codec loopback ON"); + else + msg("Codec loopback OFF"); + } + + break; + } + + default: + msg("! Unknown VREQ %02X", (uint32_t)bRequest); + handled = CyFalse; + } + + /* After processing the vendor request, flush the endpoints. */ + CyU3PUsbFlushEp(VREQ_ENDPOINT_PRODUCER); + CyU3PUsbFlushEp(VREQ_ENDPOINT_CONSUMER); + } + + return handled; +} + + +/* Callback function to handle LPM requests from the USB 3.0 host. This function + * is invoked by the API whenever a state change from U0 -> U1 or U0 -> U2 + * happens. + * + * If we return CyTrue from this function, the FX3 device is retained + * in the low power state. If we return CyFalse, the FX3 device immediately + * tries to trigger an exit back to U0. + */ +CyBool_t lpm_request_callback(CyU3PUsbLinkPowerMode link_mode) { + msg("! lpm_request_callback = %i", link_mode); + return +//#ifdef PREVENT_LOW_POWER_MODE + CyFalse; // This still allows my laptop to sleep +//#else +// CyTrue; +//#endif // PREVENT_LOW_POWER_MODE +} + + +/*! Initialize and start the GPIF state machine. + * + * This function starts the GPIF Slave FIFO state machine on the FX3. Because on + * of the GPIF pins is used for FPGA configuration, this cannot be done until + * after FPGA configuration is complete. */ +void b200_gpif_init(void) { + msg("b200_gpif_init"); + + CyU3PPibClock_t pib_clock_config; + + /* Initialize the p-port block; disable DLL for sync GPIF. */ + pib_clock_config.clkDiv = 2; + pib_clock_config.clkSrc = CY_U3P_SYS_CLK; + pib_clock_config.isHalfDiv = CyFalse; + pib_clock_config.isDllEnable = CyFalse; + CyU3PPibInit(CyTrue, &pib_clock_config); + + /* Load the GPIF configuration for Slave FIFO sync mode. */ + CyU3PGpifLoad(&CyFxGpifConfig); + + /* Start the state machine. */ + CyU3PGpifSMStart(RESET, ALPHA_RESET); + + /* Configure the watermarks for the slfifo-write buffers. */ + CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1); + CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1); + CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1); + CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1); +} + + +/*! Start and configure the FX3's SPI module. + * + * This module is used for programming the FPGA. After the FPGA is configured, + * the SPI module is disabled, as it cannot be used while we are using GPIF + * 32-bit mode. */ +CyU3PReturnStatus_t b200_spi_init(void) { + msg("b200_spi_init"); + + CyU3PSpiConfig_t spiConfig; + + /* Start the SPI module and configure the master. */ + CyU3PSpiInit(); + + /* Start the SPI master block. Run the SPI clock at 8MHz + * and configure the word length to 8 bits. Also configure + * the slave select using FW. */ + CyU3PMemSet ((uint8_t *)&spiConfig, 0, sizeof(spiConfig)); + spiConfig.isLsbFirst = CyFalse; + spiConfig.cpol = CyFalse; + spiConfig.cpha = CyFalse; + spiConfig.ssnPol = CyTrue; + spiConfig.leadTime = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK; + spiConfig.lagTime = CY_U3P_SPI_SSN_LAG_LEAD_HALF_CLK; + spiConfig.ssnCtrl = CY_U3P_SPI_SSN_CTRL_FW; + spiConfig.clock = 20000000; + spiConfig.wordLen = 8; + + CyU3PReturnStatus_t res = CyU3PSpiSetConfig(&spiConfig, NULL); + + if (res != CY_U3P_SUCCESS) + msg("! CyU3PSpiSetConfig"); + + return res; +} + + +/*! Initialize the USB module of the FX3 chip. + * + * This function handles USB initialization, re-enumeration (and thus coming up + * as a USRP B200 device), configures USB endpoints and the DMA module. + */ +void b200_usb_init(void) { + //msg("b200_usb_init"); + + /* Initialize the I2C interface for the EEPROM of page size 64 bytes. */ + CyFxI2cInit(CY_FX_USBI2C_I2C_PAGE_SIZE); + + /* Start the USB system! */ + CyU3PUsbStart(); + + /* Register our USB Setup callback. The boolean parameter indicates whether + * or not we are using FX3's 'Fast Enumeration' mode, which relies on the + * USB driver auto-detecting the connection speed and setting the correct + * descriptors. */ + CyU3PUsbRegisterSetupCallback(usb_setup_callback, CyTrue); + + CyU3PUsbRegisterEventCallback(event_usb_callback); + + CyU3PUsbRegisterLPMRequestCallback(lpm_request_callback); + + /* Check to see if a VID/PID is in the EEPROM that we should use. */ + uint8_t valid[4]; + CyU3PMemSet(valid, 0, 4); + CyFxUsbI2cTransfer(0x0, 0xA0, 4, valid, CyTrue); + if(*((uint32_t *) &(valid[0])) == 0xB2145943) { + + /* Pull the programmed device serial out of the i2c EEPROM, and copy the + * characters into the device serial string, which is then advertised as + * part of the USB descriptors. */ + uint8_t vidpid[4]; + CyU3PMemSet(vidpid, 0, 4); + CyFxUsbI2cTransfer(0x4, 0xA0, 4, vidpid, CyTrue); + b200_usb2_dev_desc[8] = vidpid[2]; + b200_usb2_dev_desc[9] = vidpid[3]; + b200_usb2_dev_desc[10] = vidpid[0]; + b200_usb2_dev_desc[11] = vidpid[1]; + + b200_usb3_dev_desc[8] = vidpid[2]; + b200_usb3_dev_desc[9] = vidpid[3]; + b200_usb3_dev_desc[10] = vidpid[0]; + b200_usb3_dev_desc[11] = vidpid[1]; + } + + uint8_t ascii_serial[9]; + CyU3PMemSet(ascii_serial, 0, 9); + CyFxUsbI2cTransfer(0x4f7, 0xA0, 9, ascii_serial, CyTrue); + uint8_t count; + dev_serial[0] = 2; + for(count = 0; count < 9; count++) { + uint8_t byte = ascii_serial[count]; + if (byte < 32 || byte > 127) break; + dev_serial[2 + (count * 2)] = byte; + // FIXME: Set count*2 + 1 = 0x00 ? + dev_serial[0] += 2; + } + + /* Set our USB enumeration descriptors! Note that there are different + * function calls for each USB speed: FS, HS, SS. */ + + /* Device descriptors */ + CyU3PUsbSetDesc(CY_U3P_USB_SET_HS_DEVICE_DESCR, 0, + (uint8_t *) b200_usb2_dev_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_DEVICE_DESCR, 0, + (uint8_t *) b200_usb3_dev_desc); + + /* Device qualifier descriptors */ + CyU3PUsbSetDesc(CY_U3P_USB_SET_DEVQUAL_DESCR, 0, + (uint8_t *) b200_dev_qual_desc); + + /* Configuration descriptors */ + CyU3PUsbSetDesc(CY_U3P_USB_SET_HS_CONFIG_DESCR, 0, + (uint8_t *) b200_usb_hs_config_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_FS_CONFIG_DESCR, 0, + (uint8_t *) b200_usb_fs_config_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_CONFIG_DESCR, 0, + (uint8_t *) b200_usb_ss_config_desc); + + /* BOS Descriptor */ + CyU3PUsbSetDesc(CY_U3P_USB_SET_SS_BOS_DESCR, 0, + (uint8_t *) b200_usb_bos_desc); + + /* String descriptors */ + CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 0, + (uint8_t *) b200_string_lang_id_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 1, + (uint8_t *) b200_usb_manufacture_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 2, + (uint8_t *) b200_usb_product_desc); + + CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 3, + (uint8_t *) dev_serial); + + //////////////////////////////////////////////////////// + + // FIXME: CyU3PUsbSetTxDeemphasis(0x11); <0x1F // Shouldn't need to change this + + uint32_t tx_swing = /*65*/45; // 65 & 45 are OK, 120 causes much link recovery. <128. 1.2V is USB3 limit. + if (CyU3PUsbSetTxSwing(tx_swing) == CY_U3P_SUCCESS) + msg("CyU3PUsbSetTxSwing %d", tx_swing); + else + msg("! CyU3PUsbSetTxSwing %d", tx_swing); + + //////////////////////////////////////////////////////// + + /* Connect the USB pins, and enable SuperSpeed (USB 3.0). */ + CyU3PConnectState(CyTrue, CyTrue); // connect, ssEnable +} + + +void b200_restore_gpio_for_fpga_config(void) { + CyU3PDeviceGpioRestore(GPIO_FPGA_RESET); + CyU3PDeviceGpioRestore(GPIO_DONE); + + CyU3PDeviceGpioRestore(GPIO_FX3_SCLK); + CyU3PDeviceGpioRestore(GPIO_FX3_CE); + CyU3PDeviceGpioRestore(GPIO_FX3_MISO); + CyU3PDeviceGpioRestore(GPIO_FX3_MOSI); + + //CyU3PGpioDeInit(); // Moved to just before init +} + +void thread_fpga_config_entry(uint32_t input) { + uint32_t event_flag; + + //msg("thread_fpga_config_entry"); + + for(;;) { + + // Event is set through VREQ + if(CyU3PEventGet(&g_event_usb_config, \ + (EVENT_FPGA_CONFIG), CYU3P_EVENT_AND_CLEAR, \ + &event_flag, CYU3P_WAIT_FOREVER) == CY_U3P_SUCCESS) { + + //uint8_t old_state = g_fx3_state; + uint32_t old_fpga_programming_write_count = 0; + + if(g_fx3_state == STATE_ERROR) { + CyU3PThreadRelinquish(); + continue; + } + + if(g_fx3_state == STATE_RUNNING) { + /* The FX3 is currently configured for SLFIFO mode. We need to tear down + * this configuration and re-configure to program the FPGA. */ + b200_restore_gpio_for_fpga_config(); + CyU3PGpifDisable(CyTrue); + } + + CyU3PSysWatchDogClear(); + + g_fx3_state = STATE_BUSY; + + /* Configure the device GPIOs for FPGA programming. */ + b200_gpios_pre_fpga_config(); + + CyU3PSysWatchDogClear(); + + /* Initialize the SPI module that will be used for FPGA programming. */ + b200_spi_init(); // This must be done *after* 'b200_gpios_pre_fpga_config' + + CyU3PSysWatchDogClear(); + + /* Wait for the signal from the host that the bitstream is starting. */ + uint32_t wait_count = 0; + + /* We can now begin configuring the FPGA. */ + g_fx3_state = STATE_FPGA_READY; + + msg("Begin FPGA"); + + // Event is set through VREQ + while(CyU3PEventGet(&g_event_usb_config, \ + (EVENT_BITSTREAM_START), CYU3P_EVENT_AND_CLEAR, \ + &event_flag, CYU3P_NO_WAIT) != CY_U3P_SUCCESS) { + + if(wait_count >= FPGA_PROGRAMMING_BITSTREAM_START_POLL_COUNT) { + msg("! Bitstream didn't start"); + g_fx3_state = STATE_UNCONFIGURED; // Since IO configuration has changed, leave it in the unconfigured state (rather than the previous one, which might have been running) + CyU3PThreadRelinquish(); + break; + } + + wait_count++; + CyU3PThreadSleep(FPGA_PROGRAMMING_POLL_SLEEP); + CyU3PSysWatchDogClear(); + } + + if (wait_count >= FPGA_PROGRAMMING_BITSTREAM_START_POLL_COUNT) + continue; + + /* Pull PROGRAM_B low and then release it. */ + CyU3PGpioSetValue(GPIO_PROGRAM_B, 0); + CyU3PThreadSleep(20); + CyU3PGpioSetValue(GPIO_PROGRAM_B, 1); + + /* Wait for INIT_B to fall and rise. */ + wait_count = 0; + + msg("Wait FPGA"); + + while(CyU3PEventGet(&g_event_usb_config, \ + (EVENT_GPIO_INITB_RISE), CYU3P_EVENT_AND_CLEAR, \ + &event_flag, CYU3P_NO_WAIT) != CY_U3P_SUCCESS) { + + if(wait_count >= FPGA_PROGRAMMING_INITB_POLL_COUNT) { + msg("! INITB didn't rise"); + g_fx3_state = STATE_UNCONFIGURED; // Safer to call it unconfigured than the previous state + CyU3PThreadRelinquish(); + break; + } + + wait_count++; + CyU3PThreadSleep(FPGA_PROGRAMMING_POLL_SLEEP); + CyU3PSysWatchDogClear(); + } +#ifdef ENABLE_INIT_B_WORKAROUND + if (wait_count >= FPGA_PROGRAMMING_INITB_POLL_COUNT) + { + CyBool_t gpio_init_b; + CyU3PGpioGetValue(GPIO_INIT_B, &gpio_init_b); + if (gpio_init_b == CyTrue) + { + wait_count = 0; + } + else + { + msg("! INIT_B still not high"); + } + } +#endif // ENABLE_INIT_B_WORKAROUND + if (wait_count >= FPGA_PROGRAMMING_INITB_POLL_COUNT) + continue; + + /* We are ready to accept the FPGA bitstream! */ + wait_count = 0; + g_fx3_state = STATE_CONFIGURING_FPGA; + + msg("Configuring FPGA"); + + // g_fpga_programming_write_count is zero'd by VREQ triggering EVENT_BITSTREAM_START + + while(CyU3PEventGet(&g_event_usb_config, \ + (EVENT_GPIO_DONE_HIGH), CYU3P_EVENT_AND_CLEAR, \ + &event_flag, CYU3P_NO_WAIT) != CY_U3P_SUCCESS) { + + /* Wait for the configuration to complete, which will be indicated + * by the DONE pin going high and triggering the associated + * interrupt. */ + + if(wait_count >= FPGA_PROGRAMMING_DONE_POLL_COUNT) { + msg("! DONE didn't go high"); + g_fx3_state = STATE_UNCONFIGURED; + CyU3PThreadRelinquish(); + break; + } + + if (old_fpga_programming_write_count == g_fpga_programming_write_count) // Only increment wait count if we haven't written anything + wait_count++; + else { + wait_count = 0; + old_fpga_programming_write_count = g_fpga_programming_write_count; + } + + CyU3PThreadSleep(FPGA_PROGRAMMING_POLL_SLEEP); + CyU3PSysWatchDogClear(); + } +#ifdef ENABLE_DONE_WORKAROUND + if (wait_count >= FPGA_PROGRAMMING_DONE_POLL_COUNT) + { + CyBool_t gpio_done; + CyU3PGpioGetValue(GPIO_DONE, &gpio_done); + if (gpio_done == CyTrue) + { + wait_count = 0; + } + else + { + msg("! DONE still not high"); + } + } +#endif // ENABLE_DONE_WORKAROUND + if (wait_count >= FPGA_PROGRAMMING_DONE_POLL_COUNT) + continue; + + msg("FPGA done"); + + /* Tell the host that we are ignoring it for a while. */ + g_fx3_state = STATE_BUSY; + + CyU3PSysWatchDogClear(); + + /* Now that the FPGA is configured, we need to tear down the current SPI and + * GPIO configs, and re-config for GPIF & bit-banged SPI operation. */ + CyU3PSpiDeInit(); + b200_restore_gpio_for_fpga_config(); + + CyU3PSysWatchDogClear(); + + /* Load the GPIO configuration for normal SLFIFO use. */ + b200_slfifo_mode_gpio_config(); + + /* Tone down the drive strength on the P-port. */ + //CyU3PSetPportDriveStrength(CY_U3P_DS_HALF_STRENGTH); + + CyU3PSysWatchDogClear(); + + /* FPGA configuration is complete! Time to get the GPIF state machine + * running for Slave FIFO. */ + b200_gpif_init(); + + CyU3PThreadSleep(1); + b200_start_fpga_sb_gpio(); // Moved here to give SB time to init + + /* RUN, BABY, RUN! */ + g_fx3_state = STATE_RUNNING; + + msg("Running"); + } + + CyU3PThreadRelinquish(); + } +} + + +/*! The primary program thread. + * + * This is the primary application thread running on the FX3 device. It is + * responsible for initializing much of the chip, and then bit-banging the FPGA + * image, as it is sent from the host, into the FPGA. It then re-configures the + * FX3 for slave-fifo, and enters an infinite loop where it simply updates the + * watchdog timer and does some minor power management state checking. + */ +void thread_main_app_entry(uint32_t input) { + //msg("thread_main_app_entry"); + + /* In your spectrum, stealing your Hz. */ + for(;;) { + CyU3PSysWatchDogClear(); + CyU3PThreadSleep(CHECK_POWER_STATE_SLEEP_TIME); +#ifdef PREVENT_LOW_POWER_MODE + /* Once data transfer has started, we keep trying to get the USB + * link to stay in U0. If this is done + * before data transfers have started, there is a likelihood of + * failing the TD 9.24 U1/U2 test. */ + { + CyU3PUsbLinkPowerMode current_state; + + if((CyU3PUsbGetSpeed () == CY_U3P_SUPER_SPEED)) { + + /* If the link is in U1/U2 states, try to get back to U0. */ + CyU3PUsbGetLinkPowerState(¤t_state); + + if (current_state > CyU3PUsbLPM_U3) + msg("Power state %i", current_state); + + while((current_state >= CyU3PUsbLPM_U1) \ + && (current_state <= CyU3PUsbLPM_U3)) { + + msg("! LPS = %i", current_state); + + CyU3PUsbSetLinkPowerState(CyU3PUsbLPM_U0); // This will wake up the host if it's trying to sleep + CyU3PThreadSleep(1); + + if (CyU3PUsbGetSpeed () != CY_U3P_SUPER_SPEED) + break; + + CyU3PUsbGetLinkPowerState (¤t_state); + } + } + } +#endif // PREVENT_LOW_POWER_MODE + } +} + + +void thread_ad9361_entry(uint32_t input) { + uint32_t event_flag; + + //msg("thread_ad9361_entry"); + + while (1) { + if (CyU3PEventGet(&g_event_usb_config, \ + EVENT_AD9361_XACT_INIT, CYU3P_EVENT_AND_CLEAR, \ + &event_flag, CYU3P_WAIT_FOREVER) == CY_U3P_SUCCESS) { + ad9361_dispatch((const char*)g_vendor_req_buffer, g_ad9361_response); + + CyU3PEventSet(&g_event_usb_config, EVENT_AD9361_XACT_DONE, CYU3P_EVENT_OR); + } + } +} + +static uint16_t g_poll_last_phy_error_count = 0, g_poll_last_link_error_count = 0; +static uint32_t g_poll_last_phy_error_status = 0; + +void update_error_counters(void) { + if (CyU3PUsbGetSpeed () != CY_U3P_SUPER_SPEED) + return; + + uvint32_t reg = REG_LNK_PHY_ERROR_STATUS; + uint32_t val = 0; + if (CyU3PReadDeviceRegisters((uvint32_t*)reg, 1, &val) == CY_U3P_SUCCESS) { + g_poll_last_phy_error_status |= (val & PHYERR_MASK); + + // Reset after read + uint32_t zero = PHYERR_MASK; + if (CyU3PWriteDeviceRegisters((uvint32_t*)reg, 1, &zero) != CY_U3P_SUCCESS) + msg("! CyU3PWriteDeviceRegisters"); + } + else { + // FIXME: Log once + msg("! Reg read fail"); + } + + // Equivalent code: + //uint32_t* p = (uint32_t*)REG_LNK_PHY_ERROR_STATUS; + //val = (*p); + //(*p) = PHYERR_MASK; + + uint16_t phy_error_count = 0, link_error_count = 0; + if (CyU3PUsbGetErrorCounts(&phy_error_count, &link_error_count) == CY_U3P_SUCCESS) { // Resets internal counters after call + g_poll_last_phy_error_count += phy_error_count; + g_poll_last_link_error_count += link_error_count; + } + else { + // FIXME: Log once + msg("! CyU3PUsbGetErrorCounts"); + } + + LOCK(g_counters_lock); + g_counters.usb_error_update_count++; + g_counters.usb_error_counters.phy_error_count += phy_error_count; + g_counters.usb_error_counters.link_error_count += link_error_count; + if (val & PHYERR_MASK) { + if (val & PHYERR_PHY_LOCK_EV) g_counters.usb_error_counters.PHY_LOCK_EV++; + if (val & PHYERR_TRAINING_ERROR_EV) g_counters.usb_error_counters.TRAINING_ERROR_EV++; + if (val & PHYERR_RX_ERROR_CRC32_EV) g_counters.usb_error_counters.RX_ERROR_CRC32_EV++; + if (val & PHYERR_RX_ERROR_CRC16_EV) g_counters.usb_error_counters.RX_ERROR_CRC16_EV++; + if (val & PHYERR_RX_ERROR_CRC5_EV) g_counters.usb_error_counters.RX_ERROR_CRC5_EV++; + if (val & PHYERR_PHY_ERROR_DISPARITY_EV)g_counters.usb_error_counters.PHY_ERROR_DISPARITY_EV++; + if (val & PHYERR_PHY_ERROR_EB_UND_EV) g_counters.usb_error_counters.PHY_ERROR_EB_UND_EV++; + if (val & PHYERR_PHY_ERROR_EB_OVR_EV) g_counters.usb_error_counters.PHY_ERROR_EB_OVR_EV++; + if (val & PHYERR_PHY_ERROR_DECODE_EV) g_counters.usb_error_counters.PHY_ERROR_DECODE_EV++; + } + UNLOCK(g_counters_lock); // FIXME: Read/write regs +} + + +void thread_re_enum_entry(uint32_t input) { + uint32_t event_flag; + + //msg("thread_re_enum_entry"); + + int keep_alive = 0; + + while (1) { + if (CyU3PEventGet(&g_event_usb_config, \ + (EVENT_RE_ENUM), CYU3P_EVENT_AND_CLEAR, \ + &event_flag, RE_ENUM_THREAD_SLEEP_TIME) == CY_U3P_SUCCESS) { + msg("Re-config"); + + // FIXME: This section is not finished + + // Not locking this since we only expect one write in VREQ and read afterward here + + int re_enum = g_config_mod.flags & (CF_RE_ENUM | CF_TX_SWING | CF_TX_DEEMPHASIS); + + CyU3PThreadSleep(100); // Wait for EP0 xaction to complete + + //b200_fw_stop(); + + if (re_enum) { + msg("Link down"); + CyU3PConnectState(CyFalse, CyTrue); + } + + if (g_config_mod.flags & CF_TX_DEEMPHASIS) { + //g_config_mod.config.tx_deemphasis + //CyU3PUsbSetTxDeemphasis(0x11); <0x1F + } + if (g_config_mod.flags & CF_TX_SWING) { + //CyU3PUsbSetTxSwing(90); <128 + } + + //CyU3PUsbControlUsb2Support(); + + //b200_fw_start() + + /* Connect the USB pins, and enable SuperSpeed (USB 3.0). */ + if (re_enum) { + msg("Link up"); + CyU3PConnectState(CyTrue, CyTrue); // CHECK: Assuming all other important state will persist + } + + counters_reset_usb_errors(); + } + else { + if (++keep_alive == KEEP_ALIVE_LOOP_COUNT) { + msg("Keep-alive"); + keep_alive = 0; + } +#ifndef ENABLE_FPGA_SB + update_error_counters(); +#endif // !ENABLE_FPGA_SB + } + + CyU3PThreadRelinquish(); + } +} + + +void base16_encode(uint8_t v, char out[2], char first) { + out[0] = first + (v >> 4); + out[1] = first + (v & 0x0F); +} + + +#ifdef ENABLE_FPGA_SB +void thread_fpga_sb_poll_entry(uint32_t input) { + //msg("thread_fpga_sb_poll_entry"); + + while (1) { + uint16_t i; + uint8_t has_change = 0; + + update_error_counters(); + + /*if (g_poll_last_phy_error_count > 0) + has_change = 1; + if (g_poll_last_link_error_count > 0) + has_change = 1;*/ + if (g_poll_last_phy_error_status != 0) + has_change = 1; + + uint16_t idx = CyU3PUsbGetEventLogIndex(); // Current *write* pointer + if (idx > (USB_EVENT_LOG_SIZE-1)) { + msg("! USB event log idx = %i", (int)idx); + break; + } + + uint8_t has_usb_events = 0; + // Assuming logging won't wrap around between get calls (i.e. buffer should be long enough) + if (g_fpga_sb_last_usb_event_log_index != idx) { + if (idx < g_fpga_sb_last_usb_event_log_index) { + for (i = g_fpga_sb_last_usb_event_log_index; i < USB_EVENT_LOG_SIZE; i++) { + if (g_usb_event_log[i] != 0x14 && g_usb_event_log[i] != 0x15 && g_usb_event_log[i] != 0x16) { // CTRL, STATUS, ACKSETUP + has_usb_events = 1; + break; + } + } + + if (has_usb_events == 0) { + for (i = 0; i < idx; i++) { + if (g_usb_event_log[i] != 0x14 && g_usb_event_log[i] != 0x15 && g_usb_event_log[i] != 0x16) { // CTRL, STATUS, ACKSETUP + has_usb_events = 1; + break; + } + } + } + } + else { + for (i = g_fpga_sb_last_usb_event_log_index; i < idx; i++) { + if (g_usb_event_log[i] != 0x14 && g_usb_event_log[i] != 0x15 && g_usb_event_log[i] != 0x16) { // CTRL, STATUS, ACKSETUP + has_usb_events = 1; + break; + } + } + } + } + + if (has_change || has_usb_events) { + LOCK(g_suart_lock); + + sb_write(SUART_TXCHAR, UPT_USB_EVENTS); + + char out[3]; + out[2] = '\0'; + + if (has_usb_events) { + if (idx < g_fpga_sb_last_usb_event_log_index) { + for (i = g_fpga_sb_last_usb_event_log_index; i < USB_EVENT_LOG_SIZE; i++) { + if (g_usb_event_log[i] == 0x14 || g_usb_event_log[i] == 0x15 || g_usb_event_log[i] == 0x16) // CTRL, STATUS, ACKSETUP + continue; + base16_encode(g_usb_event_log[i], out, 'A'); + _sb_write_string(out); + } + + for (i = 0; i < idx; i++) { + if (g_usb_event_log[i] == 0x14 || g_usb_event_log[i] == 0x15 || g_usb_event_log[i] == 0x16) // CTRL, STATUS, ACKSETUP + continue; + base16_encode(g_usb_event_log[i], out, 'A'); + _sb_write_string(out); + } + } + else { + for (i = g_fpga_sb_last_usb_event_log_index; i < idx; i++) { + if (g_usb_event_log[i] == 0x14 || g_usb_event_log[i] == 0x15 || g_usb_event_log[i] == 0x16) // CTRL, STATUS, ACKSETUP + continue; + base16_encode(g_usb_event_log[i], out, 'A'); + _sb_write_string(out); + } + } + } + + // USB events: A-P,A-P + // PHY error status: a,a-i + + if (g_poll_last_phy_error_status != 0) { + uint32_t mask; + size_t offset; + for (mask = PHYERR_MAX, offset = 0; mask != 0; mask >>= 1, ++offset) { + if ((g_poll_last_phy_error_status & mask) != 0) { + sb_write(SUART_TXCHAR, 'a'); + sb_write(SUART_TXCHAR, 'a' + offset); + } + } + } + + /*char buf[6]; + + if (g_poll_last_phy_error_count > 0) { + sb_write(SUART_TXCHAR, 'b'); + snprintf(buf, sizeof(buf)-1, "%d", g_poll_last_phy_error_count); + _sb_write_string(buf); + } + + if (g_poll_last_link_error_count > 0) { + sb_write(SUART_TXCHAR, 'c'); + snprintf(buf, sizeof(buf)-1, "%d", g_poll_last_link_error_count); + _sb_write_string(buf); + }*/ + + _sb_write_string("\r\n"); + + UNLOCK(g_suart_lock); + } + + g_poll_last_phy_error_count = 0; + g_poll_last_link_error_count = 0; + g_poll_last_phy_error_status = 0; + + g_fpga_sb_last_usb_event_log_index = idx; + + CyU3PThreadRelinquish(); + } +} +#endif // ENABLE_FPGA_SB + +/*! Application define function which creates the threads. + * + * The name of this application cannot be changed, as it is called from the + * tx_application _define function, referenced in the rest of the FX3 build + * system. + * + * If thread creation fails, lock the system and force a power reset. + */ +void CyFxApplicationDefine(void) { + void *app_thread_ptr, *fpga_thread_ptr, *ad9361_thread_ptr; +#ifdef ENABLE_RE_ENUM_THREAD + void *re_enum_thread_ptr; +#endif // ENABLE_RE_ENUM_THREAD +#ifdef ENABLE_FPGA_SB + void *fpga_sb_poll_thread_ptr; +#endif // ENABLE_FPGA_SB + + g_counters.magic = COUNTER_MAGIC; +#ifdef ENABLE_AD9361_LOGGING + ad9361_set_msgfn(msg); +#endif // ENABLE_AD9361_LOGGING + memset(&g_config, 0xFF, sizeof(g_config)); // Initialise to -1 + + CyU3PMutexCreate(&g_log_lock, CYU3P_NO_INHERIT); + CyU3PMutexCreate(&g_counters_lock, CYU3P_NO_INHERIT); + CyU3PMutexCreate(&g_counters_dma_from_host_lock, CYU3P_NO_INHERIT); + CyU3PMutexCreate(&g_counters_dma_to_host_lock, CYU3P_NO_INHERIT); +#ifdef ENABLE_FPGA_SB + CyU3PMutexCreate(&g_suart_lock, CYU3P_NO_INHERIT); +#endif // ENABLE_FPGA_SB +#ifdef ENABLE_USB_EVENT_LOGGING + CyU3PUsbInitEventLog(g_usb_event_log, USB_EVENT_LOG_SIZE); +#endif // ENABLE_USB_EVENT_LOGGING + + //////////////////////////////////////////////////////// + + /* Tell the host that we are ignoring it for a while. */ + g_fx3_state = STATE_BUSY; + + /* Set the FX3 compatibility number. */ + compat_num[0] = FX3_COMPAT_MAJOR; + compat_num[1] = FX3_COMPAT_MINOR; + + /* Initialize the USB system. */ + b200_usb_init(); + + /* Turn on the Watchdog Timer. */ + CyU3PSysWatchDogConfigure(CyTrue, WATCHDOG_TIMEOUT); + + /* Go do something. Probably not useful, because you aren't configured. */ + g_fx3_state = STATE_UNCONFIGURED; + + //////////////////////////////////////////////////////// + + b200_gpio_init(CyTrue); + + b200_enable_fpga_sb_gpio(CyTrue); + + msg("Compat: %d.%d", FX3_COMPAT_MAJOR, FX3_COMPAT_MINOR); + msg("FX3 SDK: %d.%d.%d (build %d)", CYFX_VERSION_MAJOR, CYFX_VERSION_MINOR, CYFX_VERSION_PATCH, CYFX_VERSION_BUILD); + + //////////////////////////////////////////////////////// + + /* Create the USB event group that we will use to track USB events from the + * application thread. */ + CyU3PEventCreate(&g_event_usb_config); + + /* Allocate memory for the application thread. */ + app_thread_ptr = CyU3PMemAlloc(APP_THREAD_STACK_SIZE); + + /* Allocate memory for the FPGA configuration thread. */ + fpga_thread_ptr = CyU3PMemAlloc(APP_THREAD_STACK_SIZE); +#ifdef ENABLE_RE_ENUM_THREAD + re_enum_thread_ptr = CyU3PMemAlloc(APP_THREAD_STACK_SIZE); +#endif // ENABLE_RE_ENUM_THREAD + ad9361_thread_ptr = CyU3PMemAlloc(APP_THREAD_STACK_SIZE); +#ifdef ENABLE_FPGA_SB + fpga_sb_poll_thread_ptr = CyU3PMemAlloc(APP_THREAD_STACK_SIZE); +#endif // ENABLE_FPGA_SB + //////////////////////////////////////////////////////// + + /* Create the thread for the application */ + if (app_thread_ptr != NULL) + CyU3PThreadCreate(&thread_main_app, + "200:B200 Main", + thread_main_app_entry, + 0, + app_thread_ptr, + APP_THREAD_STACK_SIZE, + THREAD_PRIORITY, + THREAD_PRIORITY, + CYU3P_NO_TIME_SLICE, + CYU3P_AUTO_START); + + /* Create the thread for FPGA configuration. */ + if (fpga_thread_ptr != NULL) + CyU3PThreadCreate(&thread_fpga_config, + "300:B200 FPGA", + thread_fpga_config_entry, + 0, + fpga_thread_ptr, + APP_THREAD_STACK_SIZE, + THREAD_PRIORITY, + THREAD_PRIORITY, + CYU3P_NO_TIME_SLICE, + CYU3P_AUTO_START); +#ifdef ENABLE_RE_ENUM_THREAD + /* Create the thread for stats collection and re-enumeration/configuration */ + if (re_enum_thread_ptr != NULL) + CyU3PThreadCreate(&thread_re_enum, + "400:B200 Re-enum", + thread_re_enum_entry, + 0, + re_enum_thread_ptr, + APP_THREAD_STACK_SIZE, + THREAD_PRIORITY, + THREAD_PRIORITY, + CYU3P_NO_TIME_SLICE, + CYU3P_AUTO_START); +#endif // ENABLE_RE_ENUM_THREAD + /* Create thread to handle AD9361 transactions */ + if (ad9361_thread_ptr != NULL) + CyU3PThreadCreate(&thread_ad9361, + "500:B200 AD9361", + thread_ad9361_entry, + 0, + ad9361_thread_ptr, + APP_THREAD_STACK_SIZE, + THREAD_PRIORITY, + THREAD_PRIORITY, + CYU3P_NO_TIME_SLICE, + CYU3P_AUTO_START); +#ifdef ENABLE_FPGA_SB + /* Create thread to handling Settings Bus logging/transactions */ + if (fpga_sb_poll_thread_ptr != NULL) + CyU3PThreadCreate(&thread_fpga_sb_poll, + "600:B200 FPGA SB poll", + thread_fpga_sb_poll_entry, + 0, + fpga_sb_poll_thread_ptr, + APP_THREAD_STACK_SIZE, + THREAD_PRIORITY, + THREAD_PRIORITY, + CYU3P_NO_TIME_SLICE, + CYU3P_AUTO_START); +#endif // ENABLE_FPGA_SB +} + + +int main(void) { + CyU3PReturnStatus_t status = CY_U3P_SUCCESS; + CyU3PSysClockConfig_t clock_config; + + /* Configure the FX3 Clocking scheme: + * CPU Divider: 2 (~200 MHz) + * DMA Divider: 2 (~100 MHz) + * MMIO Divider: 2 (~100 MHz) + * 32 kHz Standby Clock: Disabled + * System Clock Divider: 1 */ + clock_config.cpuClkDiv = 2; + clock_config.dmaClkDiv = 2; + clock_config.mmioClkDiv = 2; + clock_config.useStandbyClk = CyFalse; + clock_config.clkSrc = CY_U3P_SYS_CLK; + clock_config.setSysClk400 = CyTrue; + + status = CyU3PDeviceInit(&clock_config); + if(status != CY_U3P_SUCCESS) + goto handle_fatal_error; + + /* Initialize the caches. Enable instruction cache and keep data cache disabled. + * The data cache is useful only when there is a large amount of CPU based memory + * accesses. When used in simple cases, it can decrease performance due to large + * number of cache flushes and cleans and also it adds to the complexity of the + * code. */ + status = CyU3PDeviceCacheControl(CyTrue, CyFalse, CyFalse); // Icache, Dcache, DMAcache + if (status != CY_U3P_SUCCESS) + goto handle_fatal_error; + + /* Configure the IO peripherals on the FX3. The gpioSimpleEn arrays are + * bitmaps, where each bit represents the GPIO of the matching index - the + * second array is index + 32. */ + status = b200_set_io_matrix(CyTrue); + if(status != CY_U3P_SUCCESS) + goto handle_fatal_error; + + /* This function calls starts the RTOS kernel. + * + * ABANDON ALL HOPE, YE WHO ENTER HERE */ + CyU3PKernelEntry(); + + /* Although we will never make it here, this has to be here to make the + * compiler happy. */ + return 0; + + /* If an error occurs before the launch of the kernel, it is unrecoverable. + * Once you go down this hole, you aren't coming back out without a power + * reset. */ + handle_fatal_error: + while(1); +} diff --git a/firmware/fx3/b200/b200_main.h b/firmware/fx3/b200/b200_main.h new file mode 100644 index 000000000..7971c1625 --- /dev/null +++ b/firmware/fx3/b200/b200_main.h @@ -0,0 +1,143 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +#ifndef _B200_MAIN_H +#define _B200_MAIN_H + +#include "cyu3externcstart.h" + +#include "cyu3types.h" +#include "cyu3usbconst.h" + +#define FX3_COMPAT_MAJOR (uint8_t)(4) +#define FX3_COMPAT_MINOR (uint8_t)(0) + +/* GPIO Pins */ +#define GPIO_FPGA_RESET (uint32_t)(26) // CTL[9] +#define GPIO_DONE (uint32_t)(27) +#define GPIO_PROGRAM_B (uint32_t)(45) +#define GPIO_INIT_B (uint32_t)(50) +#define GPIO_AUX_PWR_ON (uint32_t)(51) +#define GPIO_SHDN_SW (uint32_t)(52) +#define GPIO_FX3_SCLK (uint32_t)(53) +#define GPIO_FX3_CE (uint32_t)(54) +#define GPIO_FX3_MISO (uint32_t)(55) +#define GPIO_FX3_MOSI (uint32_t)(56) +#define GPIO_FPGA_SB_SCL (uint32_t)(25) // CTL[8] +#define GPIO_FPGA_SB_SDA (uint32_t)(23) // CTL[6] + +/* Create the bit-shifts that define the above GPIOs for bitmaps. The bitshifts + * are relative to 32-bit masks, so shifts > 32 are adjusted accordingly. Note + * that GPIOs < 32 are configured without the use of masks. */ +#define MASK_GPIO_PROGRAM_B (uint32_t)(1 << (GPIO_PROGRAM_B - 32)) +#define MASK_GPIO_INIT_B (uint32_t)(1 << (GPIO_INIT_B - 32)) +#define MASK_GPIO_AUX_PWR_ON (uint32_t)(1 << (GPIO_FX3_SCLK - 32)) +#define MASK_GPIO_SHDN_SW (uint32_t)(1 << (GPIO_FX3_SCLK - 32)) +#define MASK_GPIO_FX3_SCLK (uint32_t)(1 << (GPIO_FX3_SCLK - 32)) +#define MASK_GPIO_FX3_CE (uint32_t)(1 << (GPIO_FX3_CE - 32)) +#define MASK_GPIO_FX3_MISO (uint32_t)(1 << (GPIO_FX3_MISO - 32)) +#define MASK_GPIO_FX3_MOSI (uint32_t)(1 << (GPIO_FX3_MOSI - 32)) +#define MASK_GPIO_FPGA_SB_SCL (uint32_t)(1 << (GPIO_FPGA_SB_SCL - 0)) +#define MASK_GPIO_FPGA_SB_SDA (uint32_t)(1 << (GPIO_FPGA_SB_SDA - 0)) + +#define USB3_PACKETS_PER_BURST (16) +#define USB2_PACKETS_PER_BURST (1) +#define DMA_SIZE_INFINITE (0) + +#define APP_THREAD_STACK_SIZE (0x0800) +#define THREAD_PRIORITY (8) + +#define B200_VREQ_BITSTREAM_START (uint8_t)(0x02) +#define B200_VREQ_BITSTREAM_DATA (uint8_t)(0x12) +#define B200_VREQ_BITSTREAM_DATA_FILL (uint8_t)(0x13) +#define B200_VREQ_BITSTREAM_DATA_COMMIT (uint8_t)(0x14) +#define B200_VREQ_GET_COMPAT (uint8_t)(0x15) +#define B200_VREQ_SET_FPGA_HASH (uint8_t)(0x1C) +#define B200_VREQ_GET_FPGA_HASH (uint8_t)(0x1D) +#define B200_VREQ_SET_FW_HASH (uint8_t)(0x1E) +#define B200_VREQ_GET_FW_HASH (uint8_t)(0x1F) +#define B200_VREQ_LOOP_CODE (uint8_t)(0x22) +#define B200_VREQ_GET_LOG (uint8_t)(0x23) +#define B200_VREQ_GET_COUNTERS (uint8_t)(0x24) +#define B200_VREQ_CLEAR_COUNTERS (uint8_t)(0x25) +#define B200_VREQ_GET_USB_EVENT_LOG (uint8_t)(0x26) +#define B200_VREQ_SET_CONFIG (uint8_t)(0x27) +#define B200_VREQ_GET_CONFIG (uint8_t)(0x28) +#define B200_VREQ_WRITE_SB (uint8_t)(0x29) +#define B200_VREQ_SET_SB_BAUD_DIV (uint8_t)(0x30) +#define B200_VREQ_FLUSH_DATA_EPS (uint8_t)(0x31) +#define B200_VREQ_SPI_WRITE_AD9361 (uint8_t)(0x32) +#define B200_VREQ_SPI_READ_AD9361 (uint8_t)(0x42) +#define B200_VREQ_FPGA_CONFIG (uint8_t)(0x55) +#define B200_VREQ_TOGGLE_FPGA_RESET (uint8_t)(0x62) +#define B200_VREQ_TOGGLE_GPIF_RESET (uint8_t)(0x72) +#define B200_VREQ_GET_USB_SPEED (uint8_t)(0x80) +#define B200_VREQ_GET_STATUS (uint8_t)(0x83) +#define B200_VREQ_AD9361_CTRL_WRITE (uint8_t)(0x90) +#define B200_VREQ_AD9361_CTRL_READ (uint8_t)(0x91) +#define B200_VREQ_AD9361_LOOPBACK (uint8_t)(0x92) +#define B200_VREQ_RESET_DEVICE (uint8_t)(0x99) +#define B200_VREQ_EEPROM_WRITE (uint8_t)(0xBA) +#define B200_VREQ_EEPROM_READ (uint8_t)(0xBB) + +#define EVENT_BITSTREAM_START (1 << 1) +#define EVENT_GPIO_DONE_HIGH (1 << 2) +#define EVENT_GPIO_INITB_RISE (1 << 3) +#define EVENT_FPGA_CONFIG (1 << 4) +#define EVENT_RE_ENUM (1 << 5) +#define EVENT_AD9361_XACT_INIT (1 << 6) +#define EVENT_AD9361_XACT_DONE (1 << 7) + + +/* FX3 States */ +#define STATE_UNDEFINED (0) +#define STATE_FPGA_READY (1) +#define STATE_CONFIGURING_FPGA (2) +#define STATE_BUSY (3) +#define STATE_RUNNING (4) +#define STATE_UNCONFIGURED (5) +#define STATE_ERROR (6) + + +/* Define the USB endpoints, sockets, and directions. The LSB is the socket + * number, and the MSB is the direction. For USB 2.0, sockets are mapped + * one-to-one since they must be uni-directional. */ +#define VREQ_ENDPOINT_PRODUCER 0x00 // OUT (host -> FX3) +#define VREQ_ENDPOINT_CONSUMER 0x80 // IN (FX3 -> host) + +#define DATA_ENDPOINT_PRODUCER 0x02 // OUT (host -> FX3), produces for FPGA +#define DATA_ENDPOINT_CONSUMER 0x86 // IN (FX3 -> host), consumes from FPGA + +#define CTRL_ENDPOINT_PRODUCER 0x04 // OUT (host -> FX3), produces for FPGA +#define CTRL_ENDPOINT_CONSUMER 0x88 // IN (FX3 -> host), consumes from FPGA + +#define PRODUCER_DATA_SOCKET CY_U3P_UIB_SOCKET_PROD_2 +#define CONSUMER_DATA_SOCKET CY_U3P_UIB_SOCKET_CONS_6 + +#define PRODUCER_CTRL_SOCKET CY_U3P_UIB_SOCKET_PROD_4 +#define CONSUMER_CTRL_SOCKET CY_U3P_UIB_SOCKET_CONS_8 + +#define DATA_TX_PPORT_SOCKET CY_U3P_PIB_SOCKET_0 +#define DATA_RX_PPORT_SOCKET CY_U3P_PIB_SOCKET_1 +#define CTRL_COMM_PPORT_SOCKET CY_U3P_PIB_SOCKET_2 +#define CTRL_RESP_PPORT_SOCKET CY_U3P_PIB_SOCKET_3 + + +/* Descriptor definitions for USB enumerations. */ +extern uint8_t b200_usb2_dev_desc[]; +extern uint8_t b200_usb3_dev_desc[]; +extern const uint8_t b200_dev_qual_desc[]; +extern const uint8_t b200_usb_fs_config_desc[]; +extern const uint8_t b200_usb_hs_config_desc[]; +extern const uint8_t b200_usb_bos_desc[]; +extern const uint8_t b200_usb_ss_config_desc[]; +extern const uint8_t b200_string_lang_id_desc[]; +extern const uint8_t b200_usb_manufacture_desc[]; +extern const uint8_t b200_usb_product_desc[]; +extern uint8_t dev_serial[]; + + +#include "cyu3externcend.h" + +#endif /* _B200_MAIN_H */ diff --git a/firmware/fx3/b200/b200_usb_descriptors.c b/firmware/fx3/b200/b200_usb_descriptors.c new file mode 100644 index 000000000..e8a765b24 --- /dev/null +++ b/firmware/fx3/b200/b200_usb_descriptors.c @@ -0,0 +1,510 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +/* Define the USB 2.0 and USB 3.0 enumeration descriptions for the USRP B200 + * device. */ + + +#include "b200_main.h" + + +/* Standard Device Descriptor for USB 2.0 */ +uint8_t b200_usb2_dev_desc[] __attribute__ ((aligned (32))) = +{ + 0x12, /* Descriptor size */ + CY_U3P_USB_DEVICE_DESCR, /* Device descriptor type */ + 0x10,0x02, /* USB 2.10 */ + 0xFF, /* Device class */ + 0x00, /* Device sub-class */ + 0x00, /* Device protocol */ + 0x40, /* Maxpacket size for EP0 : 64 bytes */ + 0xB4,0x04, /* Vendor ID */ + 0xF0,0x00, /* Product ID */ + 0x00,0x00, /* Device release number */ + 0x01, /* Manufacture string index */ + 0x02, /* Product string index */ + 0x03, /* Serial number string index */ + 0x01 /* Number of configurations */ +}; + + +/* Standard Device Descriptor for USB 3.0 */ +uint8_t b200_usb3_dev_desc[] __attribute__ ((aligned (32))) = +{ + 0x12, /* Descriptor size */ + CY_U3P_USB_DEVICE_DESCR, /* Device descriptor type */ + 0x00,0x03, /* USB 3.0 */ + 0xFF, /* Device class */ + 0x00, /* Device sub-class */ + 0x00, /* Device protocol */ + 0x09, /* Maxpacket size for EP0 : 2^9 */ + 0xB4,0x04, /* Vendor ID */ + 0xF0,0x00, /* Product ID */ + 0x00,0x00, /* Device release number */ + 0x01, /* Manufacture string index */ + 0x02, /* Product string index */ + 0x03, /* Serial number string index */ + 0x01 /* Number of configurations */ +}; + + +/* Binary Device Object Store Descriptor */ +const uint8_t b200_usb_bos_desc[] __attribute__ ((aligned (32))) = +{ + 0x05, /* Descriptor size */ + CY_U3P_BOS_DESCR, /* Device descriptor type */ + 0x16,0x00, /* Length of this descriptor and all sub descriptors */ + 0x02, /* Number of device capability descriptors */ + + /* USB 2.0 extension */ + 0x07, /* Descriptor size */ + CY_U3P_DEVICE_CAPB_DESCR, /* Device capability type descriptor */ + CY_U3P_USB2_EXTN_CAPB_TYPE, /* USB 2.0 extension capability type */ + 0x02,0x00,0x00,0x00, /* Supported device level features: LPM support */ + + /* SuperSpeed device capability */ + 0x0A, /* Descriptor size */ + CY_U3P_DEVICE_CAPB_DESCR, /* Device capability type descriptor */ + CY_U3P_SS_USB_CAPB_TYPE, /* SuperSpeed device capability type */ + 0x00, /* Supported device level features */ + 0x0E,0x00, /* Speeds supported by the device : SS, HS and FS */ + 0x03, /* Functionality support */ + 0x00, /* U1 Device Exit latency */ + 0x00,0x00 /* U2 Device Exit latency */ +}; + + +/* Standard Device Qualifier Descriptor */ +const uint8_t b200_dev_qual_desc[] __attribute__ ((aligned (32))) = +{ + 0x0A, /* Descriptor size */ + CY_U3P_USB_DEVQUAL_DESCR, /* Device qualifier descriptor type */ + 0x00,0x02, /* USB 2.0 */ + 0xFF, /* Device class */ + 0x00, /* Device sub-class */ + 0x00, /* Device protocol */ + 0x40, /* Maxpacket size for EP0 : 64 bytes */ + 0x01, /* Number of configurations */ + 0x00 /* Reserved */ +}; + + +/* Standard Full Speed Configuration Descriptor */ +const uint8_t b200_usb_fs_config_desc[] __attribute__ ((aligned (32))) = +{ + /* Configuration descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_CONFIG_DESCR, /* Configuration descriptor type */ + 0x52,0x00, /* Length of this descriptor and all sub descriptors */ + 0x05, /* Number of interfaces */ + 0x01, /* Configuration number */ + 0x00, /* Configuration string index */ + 0x80, /* Config characteristics - bus powered */ + 0x01, /* Lie about the max power consumption (in 2mA unit) : 2mA */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface descriptor type */ + 0x00, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x00, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface descriptor type */ + 0x01, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x40,0x00, /* Max packet size = 64 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface descriptor type */ + 0x02, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x40,0x00, /* Max packet size = 64 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface descriptor type */ + 0x03, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x40,0x00, /* Max packet size = 64 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface descriptor type */ + 0x04, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x40,0x00, /* Max packet size = 64 bytes */ + 0x00 /* Servicing interval for data transfers : 0 for bulk */ +}; + + +/* Standard High Speed Configuration Descriptor */ +const uint8_t b200_usb_hs_config_desc[] __attribute__ ((aligned (32))) = +{ + /* Configuration descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_CONFIG_DESCR, /* Configuration descriptor type */ + 0x52,0x00, /* Length of this descriptor and all sub descriptors */ + 0x05, /* Number of interfaces */ + 0x01, /* Configuration number */ + 0x00, /* COnfiguration string index */ + 0x80, /* Config characteristics - bus powered */ + 0x01, /* Lie about the max power consumption (in 2mA unit) : 2mA */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x00, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x00, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x01, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x02, /* Max packet size = 512 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x02, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x02, /* Max packet size = 512 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x03, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x02, /* Max packet size = 512 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x04, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of endpoints */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x02, /* Max packet size = 512 bytes */ + 0x00 /* Servicing interval for data transfers : 0 for bulk */ +}; + + +/* Standard Super Speed Configuration Descriptor */ +const uint8_t b200_usb_ss_config_desc[] __attribute__ ((aligned (32))) = +{ + /* Configuration descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_CONFIG_DESCR, /* Configuration descriptor type */ + 0x6A,0x00, /* Length of this descriptor and all sub descriptors */ + 0x05, /* Number of interfaces */ + 0x01, /* Configuration number */ + 0x00, /* COnfiguration string index */ + 0x80, /* Config characteristics - D6: Self power; D5: Remote wakeup */ + 0x01, /* Lie about the max power consumption (in 8mA unit) : 8mA */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x00, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x00, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x01, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Super speed endpoint companion descriptor for producer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x02, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for Bulk */ + + /* Super speed endpoint companion descriptor for consumer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x03, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Super speed endpoint companion descriptor for producer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x04, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x01, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for Bulk */ + + /* Super speed endpoint companion descriptor for consumer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00 /* Service interval for the EP : 0 for bulk */ +}; + + +/* Standard Language ID String Descriptor */ +const uint8_t b200_string_lang_id_desc[] __attribute__ ((aligned (32))) = + { + 0x04, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 0x09,0x04 /* Language ID supported */ + }; + + +/* Standard Manufacturer String Descriptor */ +const uint8_t b200_usb_manufacture_desc[] __attribute__ ((aligned (32))) = + { + 0x26, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'E',0x00, + 't',0x00, + 't',0x00, + 'u',0x00, + 's',0x00, + ' ',0x00, + 'R',0x00, + 'e',0x00, + 's',0x00, + 'e',0x00, + 'a',0x00, + 'r',0x00, + 'c',0x00, + 'h',0x00, + ' ',0x00, + 'L',0x00, + 'L',0x00, + 'C',0x00 + }; + + +/* Standard Product String Descriptor */ +const uint8_t b200_usb_product_desc[] __attribute__ ((aligned (32))) = + { + 0x14, /* Descriptor Size */ + CY_U3P_USB_STRING_DESCR, /* Device Descriptor Type */ + 'U',0x00, + 'S',0x00, + 'R',0x00, + 'P',0x00, + ' ',0x00, + 'B',0x00, + '2',0x00, + '0',0x00, + '0',0x00 + }; + +/* Microsoft OS Descriptor. */ +const uint8_t CyFxUsbOSDscr[] __attribute__ ((aligned (32))) = +{ + 0x10, + CY_U3P_USB_STRING_DESCR, + 'O', 0x00, + 'S', 0x00, + ' ', 0x00, + 'D', 0x00, + 'e', 0x00, + 's', 0x00, + 'c', 0x00 +}; + +uint8_t dev_serial[20] __attribute__ ((aligned (32))) = +{ + 0x14, + CY_U3P_USB_STRING_DESCR, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00, + '0', 0x00 +}; + +/* Place this buffer as the last buffer so that no other variable / code shares + * the same cache line. Do not add any other variables / arrays in this file. + * This will lead to variables sharing the same cache line. */ +const uint8_t CyFxUsbDscrAlignBuffer[32] __attribute__ ((aligned (32))); diff --git a/firmware/fx3/b200/b200_vrq.h b/firmware/fx3/b200/b200_vrq.h new file mode 100644 index 000000000..d1f79f0ad --- /dev/null +++ b/firmware/fx3/b200/b200_vrq.h @@ -0,0 +1,21 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// + +/* This file defines b200 vendor requests handlers, version 1 + */ +#ifndef B200_VRQ_H +#define B200_VRQ_H + +uint32_t ad9361_transact_spi(const uint32_t bits); + +// note: for a write instruction bit 7 from byte 0 is set to 1 +#define MAKE_AD9361_WRITE(dest, reg, val) {dest[0] = 0x80 | ((reg >> 8) & 0x3F); \ + dest[1] = reg & 0xFF; \ + dest[2] = val;} +#define MAKE_AD9361_READ(dest, reg) {dest[0] = (reg >> 8) & 0x3F; \ + dest[1] = reg & 0xFF;} + +#endif //B200_VRQ_H + + diff --git a/firmware/fx3/b200/fx3_mem_map.patch b/firmware/fx3/b200/fx3_mem_map.patch new file mode 100644 index 000000000..37d704ace --- /dev/null +++ b/firmware/fx3/b200/fx3_mem_map.patch @@ -0,0 +1,68 @@ +diff -ur 1.2.3-orig/common/cyfxtx.c 1.2.3/common/cyfxtx.c +--- 1.2.3-orig/common/cyfxtx.c 2013-02-07 17:16:54.000000000 -0800 ++++ 1.2.3/common/cyfxtx.c 2014-03-25 16:56:12.484602382 -0700 +@@ -33,7 +33,7 @@ + such as thread stacks and memory for message queues. The Cypress FX3
+ libraries require a Mem heap size of at least 32 KB.
+ */
+-#define CY_U3P_MEM_HEAP_BASE ((uint8_t *)0x40038000)
++#define CY_U3P_MEM_HEAP_BASE ((uint8_t *)0x40044000)
+ #define CY_U3P_MEM_HEAP_SIZE (0x8000)
+
+ /* The last 32 KB of RAM is reserved for 2-stage boot operation. This value can be changed to
+diff -ur 1.2.3-orig/common/fx3.ld 1.2.3/common/fx3.ld +--- 1.2.3-orig/common/fx3.ld 2013-02-07 17:16:54.000000000 -0800 ++++ 1.2.3/common/fx3.ld 2014-03-25 16:59:40.872240377 -0700 +@@ -26,10 +26,11 @@ + The default memory map used for FX3 applications is as follows:
+
+ Descriptor area Base: 0x40000000 Size: 12KB
+- Code area Base: 0x40003000 Size: 180KB
+- Data area Base: 0x40030000 Size: 32KB
+- Driver heap Base: 0x40038000 Size: 32KB (Update cyfxtx.c to change this.)
+- Buffer area Base: 0x40040000 Size: 256KB (Update cyfxtx.c to change this.)
++ Code area Base: 0x40003000 Size: 212KB
++ Data area Base: 0x40038000 Size: 32KB
++ Heap Base: 0x40040000 Size: 16KB
++ Driver heap Base: 0x40044000 Size: 32KB (Update cyfxtx.c to change this.)
++ Buffer area Base: 0x4004C000 Size: 208KB (Update cyfxtx.c to change this.)
+
+ Interrupt handlers to be placed in I-TCM (16KB).
+ The first 256 bytes of ITCM are reserved for Exception Vectors.
+@@ -52,8 +53,8 @@ + MEMORY
+ {
+ I-TCM : ORIGIN = 0x100, LENGTH = 0x3F00
+- SYS_MEM : ORIGIN = 0x40003000 LENGTH = 0x2D000
+- DATA : ORIGIN = 0x40030000 LENGTH = 0x8000
++ SYS_MEM : ORIGIN = 0x40003000 LENGTH = 0x35000
++ DATA : ORIGIN = 0x40038000 LENGTH = 0x8000
+ }
+
+ SECTIONS
+@@ -75,7 +76,7 @@ + _etext = .;
+ } > SYS_MEM
+
+- . = 0x40030000;
++ . = 0x40038000;
+ .data :
+ {
+ _data = .;
+@@ -104,5 +105,16 @@ + } > DATA
+ __exidx_end = .;
+
++ PROVIDE(__exidx_end = __exidx_end);
++
++ . = ALIGN(4);
++ __heap_start = 0x40040000;
++ PROVIDE(__heap_start = __heap_start);
++
++ . = ALIGN(4);
++ __heap_end = 0x40044000;
++ PROVIDE(__heap_end = __heap_end);
++
++ PROVIDE(__heap_size = __heap_end - __heap_start);
+ }
+
diff --git a/firmware/fx3/b200/makefile b/firmware/fx3/b200/makefile new file mode 100644 index 000000000..d693db076 --- /dev/null +++ b/firmware/fx3/b200/makefile @@ -0,0 +1,55 @@ +# +# Copyright 2013-2014 Ettus Research LLC +# + +HEX_OUT = usrp_b200_fw.hex + +all:$(HEX_OUT) + +# Pull in the Cypress SDK files to build the firmware +FX3FWROOT=.. +FX3PFWROOT=../u3p_firmware +include $(FX3FWROOT)/common/fx3_build_config.mak + +ifndef OC + OC = arm-none-eabi-objcopy +endif + +MODULE = b200_main + +SOURCE += $(MODULE).c +SOURCE += b200_usb_descriptors.c +SOURCE += b200_ad9361.c +SOURCE += b200_i2c.c + +INCLUDES = b200_main.h b200_vrq.h b200_gpifconfig.h b200_i2c.h +INCLUDES += ../ad9361/include/ad9361_transaction.h + +INCFLAGS = -I ../ad9361/include + +LDLIBS += \ + "$$ARMGCC_INSTALL_PATH"/arm-none-eabi/lib/libm.a + +C_OBJECT=$(SOURCE:%.c=./%.o) +A_OBJECT=$(SOURCE_ASM:%.S=./%.o) + +EXES = $(MODULE).$(EXEEXT) + +$(MODULE).$(EXEEXT): $(A_OBJECT) $(C_OBJECT) + $(LINK) $(LINKFLAGS) + +$(C_OBJECT) : %.o : %.c $(INCLUDES) + $(COMPILE) $(INCFLAGS) + +$(A_OBJECT) : %.o : %.S + $(ASSEMBLE) + +clean: + rm -f ./$(MODULE).$(EXEEXT) + rm -f ./$(MODULE).map + rm -f ./*.o + +$(HEX_OUT): $(C_OBJECT) $(A_OBJECT) $(EXES) + $(OC) -O ihex $(EXES) $@ + +#[]# diff --git a/firmware/fx3/gpif2_designer/b200_v2.cydsn/b200_v2.cyfx b/firmware/fx3/gpif2_designer/b200_v2.cydsn/b200_v2.cyfx new file mode 100644 index 000000000..3e6eb0719 --- /dev/null +++ b/firmware/fx3/gpif2_designer/b200_v2.cydsn/b200_v2.cyfx @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="us-ascii"?>
+<CyXmlSerializer>
+<!--This file is machine generated and read. It is not intended to be edited by hand.-->
+<!--Due to this, there is no schema for this file.-->
+<CyGuid_7d237aff-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtGpif2exe" version="2">
+<CyGuid_7d237b00-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtProject" version="1">
+<ProjectDocs>
+<CyGuid_7d237b03-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtItem" name="gpif2model.xml" persistent="./projectfiles/gpif2model.xml" target="7d237b02-d944-11da-aaba-00164119d63b">
+<Hidden v="False" />
+</CyGuid_7d237b03-d944-11da-aaba-00164119d63b>
+<CyGuid_7d237b03-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtItem" name="gpif2view.xml" persistent="./projectfiles/gpif2view.xml" target="7d237b01-d944-11da-aaba-00164119d63b">
+<Hidden v="False" />
+</CyGuid_7d237b03-d944-11da-aaba-00164119d63b>
+<CyGuid_7d237b03-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtItem" name="gpif2timingsimulation.xml" persistent="./projectfiles/gpif2timingsimulation.xml" target="3ad448c6-d155-4f76-a7fb-e760cd8e6feb">
+<Hidden v="False" />
+</CyGuid_7d237b03-d944-11da-aaba-00164119d63b>
+</ProjectDocs>
+<OutputDocs>
+<CyGuid_7d237b03-d944-11da-aaba-00164119d63b type_name="CyGpif2Designer.Common.PrjMgmt.Model.CyPrjMgmtItem" name="cyfxgpif2config.h" persistent="C:\Users\bhilburn\Documents\GPIF II Designer\b200_v2.cydsn\cyfxgpif2config.h" target="7d237afd-d944-11da-aaba-00164119d63b">
+<Hidden v="False" />
+</CyGuid_7d237b03-d944-11da-aaba-00164119d63b>
+</OutputDocs>
+</CyGuid_7d237b00-d944-11da-aaba-00164119d63b>
+<Settings>
+<Setting name="GPIF2_OutputName" value="cyfxgpif2config" />
+<Setting name="GPIF2_OutputLocation" value="C:\Users\bhilburn\Documents\GPIF II Designer\b200_v2.cydsn" />
+<Setting name="GPIF2_Template" value="C:\Program Files\Cypress\GPIFII Designer\inputs\outputtemplates\cygpif2cheadertemplate.tpl" />
+</Settings>
+</CyGuid_7d237aff-d944-11da-aaba-00164119d63b>
+</CyXmlSerializer>
\ No newline at end of file diff --git a/firmware/fx3/gpif2_designer/b200_v2.cydsn/cyfxgpif2config.h b/firmware/fx3/gpif2_designer/b200_v2.cydsn/cyfxgpif2config.h new file mode 100644 index 000000000..d16cdf038 --- /dev/null +++ b/firmware/fx3/gpif2_designer/b200_v2.cydsn/cyfxgpif2config.h @@ -0,0 +1,174 @@ +/*
+ * Project Name: b200_v2.cyfx
+ * Time : 10/23/2013 12:03:48
+ * Device Type: FX3
+ * Project Type: GPIF2
+ *
+ *
+ *
+ *
+ * This is a generated file and should not be modified
+ * This file need to be included only once in the firmware
+ * This file is generated by Gpif2 designer tool version - 1.0.715.0
+ *
+ */
+
+#ifndef _INCLUDED_CYFXGPIF2CONFIG_
+#define _INCLUDED_CYFXGPIF2CONFIG_
+#include "cyu3types.h"
+#include "cyu3gpif.h"
+
+/* Summary
+ Number of states in the state machine
+ */
+#define CY_NUMBER_OF_STATES 6
+
+/* Summary
+ Mapping of user defined state names to state indices
+ */
+#define RESET 0
+#define IDLE 1
+#define READ 2
+#define WRITE 3
+#define SHORT_PKT 4
+#define ZLP 5
+
+
+/* Summary
+ Initial value of early outputs from the state machine.
+ */
+#define ALPHA_RESET 0x8
+
+
+/* Summary
+ Transition function values used in the state machine.
+ */
+uint16_t CyFxGpifTransition[] = {
+ 0x0000, 0x8080, 0x2222, 0x5555, 0x7F7F, 0x1F1F, 0x8888
+};
+
+/* Summary
+ Table containing the transition information for various states.
+ This table has to be stored in the WAVEFORM Registers.
+ This array consists of non-replicated waveform descriptors and acts as a
+ waveform table.
+ */
+CyU3PGpifWaveData CyFxGpifWavedata[] = {
+ {{0x1E086001,0x000100C4,0x80000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x4E080302,0x00000200,0x80000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x1E086001,0x000100C4,0x80000000},{0x4E040704,0x20000200,0xC0100000}},
+ {{0x00000000,0x00000000,0x00000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x00000000,0x00000000,0x00000000},{0x3E738705,0x00000200,0xC0100000}},
+ {{0x00000000,0x00000000,0x00000000},{0x5E002703,0x2001020C,0x80000000}},
+ {{0x00000000,0x00000000,0x00000000},{0x4E040704,0x20000200,0xC0100000}}
+};
+
+/* Summary
+ Table that maps state indices to the descriptor table indices.
+ */
+uint8_t CyFxGpifWavedataPosition[] = {
+ 0,1,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,4,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,5,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 0,6,0,2,0,0
+};
+
+/* Summary
+ GPIF II configuration register values.
+ */
+uint32_t CyFxGpifRegValue[] = {
+ 0x80000380, /* CY_U3P_PIB_GPIF_CONFIG */
+ 0x000010AC, /* CY_U3P_PIB_GPIF_BUS_CONFIG */
+ 0x01070002, /* CY_U3P_PIB_GPIF_BUS_CONFIG2 */
+ 0x00000044, /* CY_U3P_PIB_GPIF_AD_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_STATUS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INTR */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INTR_MASK */
+ 0x00000082, /* CY_U3P_PIB_GPIF_SERIAL_IN_CONFIG */
+ 0x00000782, /* CY_U3P_PIB_GPIF_SERIAL_OUT_CONFIG */
+ 0x00000500, /* CY_U3P_PIB_GPIF_CTRL_BUS_DIRECTION */
+ 0x0000FFCF, /* CY_U3P_PIB_GPIF_CTRL_BUS_DEFAULT */
+ 0x000000BF, /* CY_U3P_PIB_GPIF_CTRL_BUS_POLARITY */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_TOGGLE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000018, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000019, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_BUS_SELECT */
+ 0x00000006, /* CY_U3P_PIB_GPIF_CTRL_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_CTRL_COUNT_LIMIT */
+ 0x0000010A, /* CY_U3P_PIB_GPIF_ADDR_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_ADDR_COUNT_LIMIT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_STATE_COUNT_CONFIG */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_STATE_COUNT_LIMIT */
+ 0x0000010A, /* CY_U3P_PIB_GPIF_DATA_COUNT_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COUNT_RESET */
+ 0x0000FFFF, /* CY_U3P_PIB_GPIF_DATA_COUNT_LIMIT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CTRL_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COMP_VALUE */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ADDR_COMP_MASK */
+ 0x00000000, /* CY_U3P_PIB_GPIF_DATA_CTRL */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_DATA */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_INGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x00000000, /* CY_U3P_PIB_GPIF_EGRESS_ADDRESS */
+ 0x80010400, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010401, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010402, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x80010403, /* CY_U3P_PIB_GPIF_THREAD_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_LAMBDA_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_ALPHA_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_BETA_STAT */
+ 0x00080000, /* CY_U3P_PIB_GPIF_WAVEFORM_CTRL_STAT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH */
+ 0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH_TIMEOUT */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CRC_CONFIG */
+ 0x00000000, /* CY_U3P_PIB_GPIF_CRC_DATA */
+ 0xFFFFFFF1 /* CY_U3P_PIB_GPIF_BETA_DEASSERT */
+};
+
+/* Summary
+ This structure holds all the configuration inputs for the GPIF II.
+ */
+const CyU3PGpifConfig_t CyFxGpifConfig = {
+ (uint16_t)(sizeof(CyFxGpifWavedataPosition)/sizeof(uint8_t)),
+ CyFxGpifWavedata,
+ CyFxGpifWavedataPosition,
+ (uint16_t)(sizeof(CyFxGpifTransition)/sizeof(uint16_t)),
+ CyFxGpifTransition,
+ (uint16_t)(sizeof(CyFxGpifRegValue)/sizeof(uint32_t)),
+ CyFxGpifRegValue
+};
+
+#endif /* _INCLUDED_CYFXGPIF2CONFIG_ */
diff --git a/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2model.xml b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2model.xml new file mode 100644 index 000000000..477bad9e7 --- /dev/null +++ b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2model.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<GPIFIIModel version="3">
+ <InterfaceDefination>
+ <InterfaceSetting>
+ <I2SEnabled>False</I2SEnabled>
+ <I2CEnabled>False</I2CEnabled>
+ <SPIEnabled>False</SPIEnabled>
+ <I2SEnabled>False</I2SEnabled>
+ <ADMuxedEnabled>False</ADMuxedEnabled>
+ <InterfaceType>Slave</InterfaceType>
+ <CommunicationType>Synchronous</CommunicationType>
+ <ClockSource>External</ClockSource>
+ <ClockEdge>Positive</ClockEdge>
+ <Endianness>LittleEndian</Endianness>
+ <DataBusWidth>Bit32</DataBusWidth>
+ <AddressBuswidth>2</AddressBuswidth>
+ </InterfaceSetting>
+ </InterfaceDefination>
+ <Signals>
+ <Signal ElementId="INPUT0" SignalType="Input" SpecialFunction="OE">
+ <DisplayName>SLOE</DisplayName>
+ <GPIOPinNumber>GPIO_19</GPIOPinNumber>
+ <Polarity>ActiveLow</Polarity>
+ </Signal>
+ <Signal ElementId="INPUT1" SignalType="Input" SpecialFunction="None">
+ <DisplayName>SLCS</DisplayName>
+ <GPIOPinNumber>GPIO_17</GPIOPinNumber>
+ <Polarity>ActiveLow</Polarity>
+ </Signal>
+ <Signal ElementId="INPUT2" SignalType="Input" SpecialFunction="None">
+ <DisplayName>SLWR</DisplayName>
+ <GPIOPinNumber>GPIO_18</GPIOPinNumber>
+ <Polarity>ActiveLow</Polarity>
+ </Signal>
+ <Signal ElementId="INPUT3" SignalType="Input" SpecialFunction="None">
+ <DisplayName>SLRD</DisplayName>
+ <GPIOPinNumber>GPIO_20</GPIOPinNumber>
+ <Polarity>ActiveLow</Polarity>
+ </Signal>
+ <Signal ElementId="INPUT4" SignalType="Input" SpecialFunction="None">
+ <DisplayName>PKEND</DisplayName>
+ <GPIOPinNumber>GPIO_24</GPIOPinNumber>
+ <Polarity>ActiveLow</Polarity>
+ </Signal>
+ <Signal ElementId="FLAG0" SignalType="Flags" SpecialFunction="None">
+ <DisplayName>FLAG0</DisplayName>
+ <GPIOPinNumber>GPIO_21</GPIOPinNumber>
+ <IntialValue>Low</IntialValue>
+ <Polarity>ActiveLow</Polarity>
+ <Flags>Current_Thread_DMA_Ready</Flags>
+ </Signal>
+ <Signal ElementId="FLAG1" SignalType="Flags" SpecialFunction="None">
+ <DisplayName>FLAG1</DisplayName>
+ <GPIOPinNumber>GPIO_22</GPIOPinNumber>
+ <IntialValue>Low</IntialValue>
+ <Polarity>ActiveLow</Polarity>
+ <Flags>Current_Thread_DMA_WaterMark</Flags>
+ </Signal>
+ </Signals>
+ <StateMachine>
+ <AddressCounter />
+ <DataCounter />
+ <ControlCounter />
+ <AddressComparator />
+ <DataComparator />
+ <ControlComparator />
+ <DRQ />
+ <AddrData />
+ <State ElementId="STARTSTATE1" StateType="StartState">
+ <DisplayName>RESET</DisplayName>
+ <RepeatUntillNextTransition>True</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ </State>
+ <State ElementId="STATE1" StateType="NormalState">
+ <DisplayName>IDLE</DisplayName>
+ <RepeatUntillNextTransition>True</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ <Action ElementId="IN_ADDR0" ActionType="IN_ADDR">
+ <SampleAddressType>ThreadSelection</SampleAddressType>
+ <A7Override>DMAAccessAndRegisterAccess</A7Override>
+ </Action>
+ </State>
+ <State ElementId="STATE2" StateType="NormalState">
+ <DisplayName>READ</DisplayName>
+ <RepeatUntillNextTransition>True</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ <Action ElementId="DR_DATA0" ActionType="DR_DATA">
+ <IsDataCounterConnected>False</IsDataCounterConnected>
+ <DataSourceSink>Socket</DataSourceSink>
+ <ThreadNumber>Thread0</ThreadNumber>
+ <SyncBurstMode>Enable</SyncBurstMode>
+ <DriveNewData>DriveNewData</DriveNewData>
+ <UpdateSource>True</UpdateSource>
+ </Action>
+ </State>
+ <State ElementId="STATE3" StateType="NormalState">
+ <DisplayName>WRITE</DisplayName>
+ <RepeatUntillNextTransition>True</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ <Action ElementId="IN_DATA0" ActionType="IN_DATA">
+ <DataSourceSink>Socket</DataSourceSink>
+ <ThreadNumber>Thread0</ThreadNumber>
+ <SampleData>True</SampleData>
+ <WriteDataIntoDataSink>True</WriteDataIntoDataSink>
+ </Action>
+ </State>
+ <State ElementId="STATE4" StateType="NormalState">
+ <DisplayName>SHORT_PKT</DisplayName>
+ <RepeatUntillNextTransition>False</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ <Action ElementId="COMMIT0" ActionType="COMMIT">
+ <ThreadNumber>Thread0</ThreadNumber>
+ </Action>
+ <Action ElementId="IN_DATA0" ActionType="IN_DATA">
+ <DataSourceSink>Socket</DataSourceSink>
+ <ThreadNumber>Thread0</ThreadNumber>
+ <SampleData>True</SampleData>
+ <WriteDataIntoDataSink>True</WriteDataIntoDataSink>
+ </Action>
+ </State>
+ <State ElementId="STATE5" StateType="NormalState">
+ <DisplayName>ZLP</DisplayName>
+ <RepeatUntillNextTransition>False</RepeatUntillNextTransition>
+ <RepeatCount>0</RepeatCount>
+ <Action ElementId="COMMIT0" ActionType="COMMIT">
+ <ThreadNumber>Thread0</ThreadNumber>
+ </Action>
+ </State>
+ <Transition ElementId="TRANSITION1" SourceState="STARTSTATE1" DestinationState="STATE1" Equation="LOGIC_ONE" />
+ <Transition ElementId="TRANSITION2" SourceState="STATE1" DestinationState="STATE2" Equation="SLWR&!SLCS&PKEND&!SLRD&!SLOE" />
+ <Transition ElementId="TRANSITION3" SourceState="STATE1" DestinationState="STATE3" Equation="!SLWR&!SLCS&PKEND&SLRD" />
+ <Transition ElementId="TRANSITION4" SourceState="STATE1" DestinationState="STATE4" Equation="!SLWR&!SLCS&!PKEND&SLRD" />
+ <Transition ElementId="TRANSITION5" SourceState="STATE1" DestinationState="STATE5" Equation="SLWR&!SLCS&!PKEND&SLRD" />
+ <Transition ElementId="TRANSITION6" SourceState="STATE5" DestinationState="STATE1" Equation="PKEND" />
+ <Transition ElementId="TRANSITION7" SourceState="STATE2" DestinationState="STATE1" Equation="SLRD|SLCS|SLOE" />
+ <Transition ElementId="TRANSITION8" SourceState="STATE3" DestinationState="STATE1" Equation="(PKEND&SLWR)|SLCS" />
+ <Transition ElementId="TRANSITION9" SourceState="STATE3" DestinationState="STATE4" Equation="!SLWR&!PKEND" />
+ <Transition ElementId="TRANSITION10" SourceState="STATE4" DestinationState="STATE1" Equation="PKEND|SLCS|SLWR" />
+ </StateMachine>
+</GPIFIIModel>
\ No newline at end of file diff --git a/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2timingsimulation.xml b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2timingsimulation.xml new file mode 100644 index 000000000..e6b10027b --- /dev/null +++ b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2timingsimulation.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<GPIFIITimingSimulation version="1">
+ <Clock>100</Clock>
+ <BufferSize>512</BufferSize>
+ <WaterMark>0</WaterMark>
+ <Scenario Name="Read" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE2" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+ <Scenario Name="Write" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+ <Scenario Name="BurstRead" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE2" WaitNumber="0" />
+ <State StateId="STATE2" WaitNumber="0" />
+ <State StateId="STATE2" WaitNumber="0" />
+ <State StateId="STATE2" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+ <Scenario Name="BurstWrite" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE3" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+ <Scenario Name="ShortPkt" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE4" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+ <Scenario Name="ZLP" CurrentThread="Thread0">
+ <State StateId="STARTSTATE1" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ <State StateId="STATE5" WaitNumber="0" />
+ <State StateId="STATE1" WaitNumber="0" />
+ </Scenario>
+</GPIFIITimingSimulation>
\ No newline at end of file diff --git a/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2view.xml b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2view.xml new file mode 100644 index 000000000..730be04ab --- /dev/null +++ b/firmware/fx3/gpif2_designer/b200_v2.cydsn/projectfiles/gpif2view.xml @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<Root version="4">
+ <CyStates>
+ <CyNormalState>
+ <Left>363</Left>
+ <Top>96.4466666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STATE1</Name>
+ <DisplayName>IDLE</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyNormalState>
+ <CyNormalState>
+ <Left>237</Left>
+ <Top>390.446666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STATE2</Name>
+ <DisplayName>READ</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyNormalState>
+ <CyNormalState>
+ <Left>551</Left>
+ <Top>379.446666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STATE3</Name>
+ <DisplayName>WRITE</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyNormalState>
+ <CyNormalState>
+ <Left>773</Left>
+ <Top>233.446666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STATE4</Name>
+ <DisplayName>SHORT_PKT</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyNormalState>
+ <CyNormalState>
+ <Left>11</Left>
+ <Top>196.446666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STATE5</Name>
+ <DisplayName>ZLP</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyNormalState>
+ <CyStartState>
+ <Left>29</Left>
+ <Top>18.4466666666667</Top>
+ <Width>83</Width>
+ <Height>70</Height>
+ <Name>STARTSTATE1</Name>
+ <DisplayName>RESET</DisplayName>
+ <zIndex>1</zIndex>
+ <IsGroup>False</IsGroup>
+ <ParentID>00000000-0000-0000-0000-000000000000</ParentID>
+ </CyStartState>
+ </CyStates>
+ <CyTransitions>
+ <CyTransition>
+ <Name>TRANSITION1</Name>
+ <TransitionEquation>LOGIC_ONE</TransitionEquation>
+ <SourceName>STARTSTATE1</SourceName>
+ <SinkName>STATE1</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION2</Name>
+ <TransitionEquation>SLWR&!SLCS&PKEND&!SLRD&!SLOE</TransitionEquation>
+ <SourceName>STATE1</SourceName>
+ <SinkName>STATE2</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION3</Name>
+ <TransitionEquation>!SLWR&!SLCS&PKEND&SLRD</TransitionEquation>
+ <SourceName>STATE1</SourceName>
+ <SinkName>STATE3</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION4</Name>
+ <TransitionEquation>!SLWR&!SLCS&!PKEND&SLRD</TransitionEquation>
+ <SourceName>STATE1</SourceName>
+ <SinkName>STATE4</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION5</Name>
+ <TransitionEquation>SLWR&!SLCS&!PKEND&SLRD</TransitionEquation>
+ <SourceName>STATE1</SourceName>
+ <SinkName>STATE5</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION6</Name>
+ <TransitionEquation>PKEND</TransitionEquation>
+ <SourceName>STATE5</SourceName>
+ <SinkName>STATE1</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION7</Name>
+ <TransitionEquation>SLRD|SLCS|SLOE</TransitionEquation>
+ <SourceName>STATE2</SourceName>
+ <SinkName>STATE1</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION8</Name>
+ <TransitionEquation>(PKEND&SLWR)|SLCS</TransitionEquation>
+ <SourceName>STATE3</SourceName>
+ <SinkName>STATE1</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION9</Name>
+ <TransitionEquation>!SLWR&!PKEND</TransitionEquation>
+ <SourceName>STATE3</SourceName>
+ <SinkName>STATE4</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ <CyTransition>
+ <Name>TRANSITION10</Name>
+ <TransitionEquation>PKEND|SLCS|SLWR</TransitionEquation>
+ <SourceName>STATE4</SourceName>
+ <SinkName>STATE1</SinkName>
+ <SourceConnectorName>Connector</SourceConnectorName>
+ <SinkConnectorName>Connector</SinkConnectorName>
+ <SourceArrowSymbol>None</SourceArrowSymbol>
+ <SinkArrowSymbol>Arrow</SinkArrowSymbol>
+ <zIndex>0</zIndex>
+ </CyTransition>
+ </CyTransitions>
+</Root>
\ No newline at end of file diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 45d0cd940..c45058562 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -210,8 +210,8 @@ UHD_INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_MD5SUM "00784ebb5243b0abb15db305f557e230") -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.000-48-ge1c32905.zip") +SET(UHD_IMAGES_MD5SUM "02291061d617c7b398a0dbdcc11ca6b6") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.007.000-70-gfcc85c95.zip") ######################################################################## # Register top level components diff --git a/host/LICENSE b/host/LICENSE index 9aa03b39b..b91233b22 100644 --- a/host/LICENSE +++ b/host/LICENSE @@ -1,3 +1,7 @@ +This LICENSE file applies only to this directory and all subdirectories. Other +top-level directories in the UHD(tm) Software distribution are not necessarily +covered by this license. + 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 diff --git a/host/docs/usrp_x3x0.rst b/host/docs/usrp_x3x0.rst index 3b7e9914e..7dd322dbf 100644 --- a/host/docs/usrp_x3x0.rst +++ b/host/docs/usrp_x3x0.rst @@ -65,15 +65,18 @@ in order not to damage sensitive electronics through static discharge! Network Connectivity ^^^^^^^^^^^^^^^^^^^^ -The next step is to make sure your computer can talk to the USRP. An otherwise unconfigured -USRP device will have the IP address 192.168.10.2 when using 1GigE. -It is recommended to directly connect your USRP to the computer at first, -and to set the IP address on your machine to 192.168.10.1. -See Section `Setup the host interface`_ on details how to change your machine's IP address. +The next step is to make sure your computer can talk to the USRP. An otherwise +unconfigured USRP device will have the IP address 192.168.10.2 when using +1GigE. It is recommended to directly connect your USRP to the computer at +first, and to set the IP address on your machine to 192.168.10.1. -**Note**: If you are running an automatic IP configuration service such as Network Manager, make -sure it is either deactivated or configured to not change the network device! This can, in extreme cases, -lead to you bricking the USRP! +See the `system configuration manual <./usrp_x3x0_config.html>`_ on details how +to change your machine's IP address. + +**Note**: If you are running an automatic IP configuration service such as +Network Manager, make sure it is either deactivated or configured to not manage +the network interface! This can, in extreme cases, lead to you bricking the +USRP! If your network configuration is correct, running ``uhd_find_devices`` will find your USRP and print some information about it. You will also be able to ping the USRP by running:: @@ -86,31 +89,32 @@ on the command line. At this point, you should also run:: to make sure all of your components (daughterboards, GPSDO) are correctly detected and usable. -^^^^^^^^^^^^^^^^^^^^^ -Updating the firmware -^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^ +Updating the FPGA Image +^^^^^^^^^^^^^^^^^^^^^^^^^ -If the output from ``uhd_find_devices`` and ``uhd_usrp_probe`` didn't show any warnings, you -can skip this step. However, if there were warnings regarding version incompatibility, you will -have to upate the FPGA image before you can start using your USRP. +If the output from ``uhd_find_devices`` and ``uhd_usrp_probe`` didn't show any +warnings, you can skip this step. However, if there were errors regarding the +FPGA version compatibility number (compat number), you will have to upate the +FPGA image before you can start using your USRP. 1. Download the current UHD images. You can use the ``uhd_images_downloader`` script provided with UHD (see also `FPGA Image Flavors`_). 2. Use the ``usrp_x3xx_fpga_burner`` utility to update the FPGA image. On the command line, run:: - usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS # Since we are using 1GigE, type is HGS + usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS If you have installed the images to a non-standard location, you might need to run (change the filename according to your device):: usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga-path <path_to_images>/usrp_x310_fpga_HGS.bit - The process of updating the firmware will take several minutes. Make sure the process of flashing the image does not get interrupted. + The process of updating the FPGA image will take several minutes. Make sure the process of flashing the image does not get interrupted. See `Load the Images onto the On-board Flash`_ for more details. -When your firmware is up to date, power-cycle the device and re-run ``uhd_usrp_probe``. There should -be no more warnings at this point, and all components should be correctly detected. Your USRP is now -ready for development! +When your FPGA image is up to date, power-cycle the device and re-run +``uhd_usrp_probe``. There should be no errors at this point, and all components +should be correctly detected. Your USRP is now ready for development! -------------- Hardware Setup @@ -386,63 +390,21 @@ The default IP address for the USRP X300/X310 device depends on the Ethernet Por You must configure the host Ethernet interface with a static IP address on the same subnet as the connected device to enable communication, as shown in the following table: -+---------------+-------------------------+----------------+----------------+---------------+ -|  Ethernet | USRP |  Default USRP |  Host Static  | Host Static | -| Interface | Ethernet Port | IP Address | IP Address | Subnet Mask | -+===============+=========================+================+================+===============+ -|  Gigabit |  Port 0 (HGS Image) |  192.168.10.2 | 192.168.10.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ -|  Ten Gigabit |  Port 1 (HGS/XGS Image) |  192.168.40.2 | 192.168.40.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ -|  Ten Gigabit |  Port 0 (XGS Image) |  192.168.30.2 | 192.168.30.1 | 255.255.255.0 | -+---------------+-------------------------+----------------+----------------+---------------+ - - -On a Linux system, you can add a static IP address very easily by using the -'ip' command - -:: - - sudo ip addr add 192.168.10.1/24 dev <interface> - -Note that **<interface>** is usually something like **eth0**. You can discover the -names of the network interfaces in your computer by running: - -:: - - ip addr show - -**Note:** -When using UHD software, if an IP address for the USRP-X Series device is not specified, -the software will use UDP broadcast packets to locate the USRP-X Series device. -On some systems, the firewall will block UDP broadcast packets. -It is recommended that you change or disable your firewall settings. - -On many Linux distributions, NetworkManager or similar tools may control the network interface. -It is important to deactivate these tools for your device before continuing! - -^^^^^^^^^^^^^^^ -Setting the MTU -^^^^^^^^^^^^^^^ -As UHD by default uses receive and transmit frames larger than the standard MTU of 1500 Bytes, -the NIC needs to be configured to use a larger MTU when used with the USRP X series devices. - -:: - - sudo ip link set mtu 8192 dev <interface> - -Upon initialization UHD will probe for the maximum possible path MTU along the path between the USRP X series device -and the host, both in receive and transmit direction. - -If the network hardware does not support MTUs as large as 8000 Bytes, passing the **send_frame_size** and **receive_frame_size** -arguments will make UHD use smaller MTUs: - -:: - - uhd_usrp_probe --args='send_frame_size=<max send MTU>, recv_frame_size=<max receive MTU>' ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +|  Ethernet | USRP |  Default USRP |  Host Static  | Host Static | Address | +| Interface | Ethernet Port | IP Address | IP Address | Subnet Mask | EEPROM key | ++===============+=========================+================+================+===============+===============+ +|  Gigabit |  Port 0 (HGS Image) |  192.168.10.2 | 192.168.10.1 | 255.255.255.0 | ``ip-addr0`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +|  Ten Gigabit |  Port 0 (XGS Image) |  192.168.30.2 | 192.168.30.1 | 255.255.255.0 | ``ip-addr2`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ +|  Ten Gigabit |  Port 1 (HGS/XGS Image) |  192.168.40.2 | 192.168.40.1 | 255.255.255.0 | ``ip-addr3`` | ++---------------+-------------------------+----------------+----------------+---------------+---------------+ -**Note:** This will most likely have a severe performance penalty. +As you can see, the X300/X310 actually stores different IP addresses, which all address the device differently: Each combination of Ethernet port and interface type (i.e., Gigabit or Ten Gigabit) has its own IP address. As an example, when addressing the device through 1 Gigabit Ethernet on its first port (Port 0), the relevant IP address is the one stored in the EEPROM with key ``ip-addr0``, or 192.168.10.2 by default. +See the `system configuration manual <./usrp_x3x0_config.html>`_ on details +how to change your machine's IP address and MTU size to work well with the X300. ^^^^^^^^^^^^^^^^^^^^^^^^^ Multiple devices per host @@ -477,6 +439,7 @@ You may need to change the USRP's IP address for several reasons: To change the USRP's IP address, you must know the current address of the USRP, and the network must be setup properly as described above. +You must also know which IP address of the X300 you want to change, as identified by their address EEPROM key (e.g. ``ip-addr0``, see the table above). Run the following commands: **UNIX:** @@ -484,14 +447,14 @@ Run the following commands: :: cd <install-path>/lib/uhd/utils - ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr --val=192.168.10.3 + ./usrp_burn_mb_eeprom --args=<optional device args> --key=ip-addr0 --val=192.168.10.3 **Windows:** :: cd <install-path>\lib\uhd\utils - usrp_burn_mb_eeprom.exe --args=<optional device args> --key=ip-addr --val=192.168.10.3 + usrp_burn_mb_eeprom.exe --args=<optional device args> --key=ip-addr0 --val=192.168.10.3 --------------------- Addressing the Device diff --git a/host/docs/usrp_x3x0_config.rst b/host/docs/usrp_x3x0_config.rst index 22ef8c595..4be247b04 100644 --- a/host/docs/usrp_x3x0_config.rst +++ b/host/docs/usrp_x3x0_config.rst @@ -46,6 +46,23 @@ You should open your NetworkManager configuration and tell it to ignore the network interface you are using. **This is not the same as simply setting a static IP address.** You *must* tell NetworkManager to ignore the interface. +Changing the host's IP address +------------------------------------- + +On a Linux system, you can add a static IP address very easily by using the +'ip' command: + +:: + + sudo ip addr add 192.168.10.1/24 dev <interface> + +Note that **<interface>** is usually something like **eth0**. You can discover the +names of the network interfaces in your computer by running: + +:: + + ip addr show + Configuring the Socket Buffers ------------------------------------- It is necessary to increase the maximum size of the socket buffers to avoid @@ -92,6 +109,11 @@ Firewall will often interfere with your ability to communicate with your USRP. You should configure your firewall to "trust" the interface you are using. Setting this properly depends on your OS and firewall configuration method. +When using UHD software, if an IP address for the USRP-X Series device is not specified, +the software will use UDP broadcast packets to locate the USRP-X Series device. +On some systems, the firewall will block UDP broadcast packets. +It is therefore recommended that you change or disable your firewall settings. + Interface Configuration File (Fedora) ------------------------------------- On Fedora systems, you can configure the network interface mostly from one diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index ea49d48d9..9e9aa67e9 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -98,7 +98,7 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/rx_multi_samples.cpp b/host/examples/rx_multi_samples.cpp index 9e5970978..a50b5f0e0 100644 --- a/host/examples/rx_multi_samples.cpp +++ b/host/examples/rx_multi_samples.cpp @@ -172,8 +172,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 0d42404d3..de3640794 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -101,18 +101,15 @@ template<typename samp_type> void recv_to_file( continue; } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ - std::string error = str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code); - - if (continue_on_bad_packet){ - std::cerr << error << std::endl; - continue; - } - else - throw std::runtime_error(error); + std::string error = str(boost::format("Receiver error: %s") % md.strerror()); + 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()) diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index cc9216cb7..30535907f 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -130,8 +130,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) break; if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } if(verbose) std::cout << boost::format( diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp index 86c59d9d7..e23390506 100644 --- a/host/examples/test_dboard_coercion.cpp +++ b/host/examples/test_dboard_coercion.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012 Ettus Research LLC +// Copyright 2012,2014 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 @@ -24,81 +24,95 @@ #include <boost/math/special_functions/round.hpp> #include <iostream> #include <complex> +#include <utility> #include <vector> +#define SAMP_RATE 1e6 + namespace po = boost::program_options; +typedef std::pair<double, double> double_pair; //BOOST_FOREACH doesn't like commas +typedef std::vector<std::pair<double, double> > pair_vector; + /************************************************************************ * Misc functions ************************************************************************/ -std::string return_MHz_string(double freq){ +std::string MHz_str(double freq){ std::string nice_string = std::string(str(boost::format("%5.2f MHz") % (freq / 1e6))); return nice_string; } -std::string return_USRP_config_string(uhd::usrp::multi_usrp::sptr usrp, bool test_tx, bool test_rx){ - uhd::dict<std::string, std::string> tx_info = usrp->get_usrp_tx_info(); - uhd::dict<std::string, std::string> rx_info = usrp->get_usrp_rx_info(); +std::string return_usrp_config_string(uhd::usrp::multi_usrp::sptr usrp, int chan, bool test_tx, bool test_rx, bool is_b2xx){ + uhd::dict<std::string, std::string> tx_info = usrp->get_usrp_tx_info(chan); + uhd::dict<std::string, std::string> rx_info = usrp->get_usrp_rx_info(chan); std::string info_string; std::string mboard_id, mboard_serial; std::string tx_serial, tx_subdev_name, tx_subdev_spec; std::string rx_serial, rx_subdev_name, rx_subdev_spec; mboard_id = tx_info.get("mboard_id"); - if(tx_info.get("mboard_serial") != "") mboard_serial = tx_info.get("mboard_serial"); - else mboard_serial = "no serial"; + if(tx_info.get("mboard_serial") == "") mboard_serial = "no serial"; + else mboard_serial = tx_info.get("mboard_serial"); - info_string = std::string(str(boost::format("Motherboard: %s (%s)\n") % mboard_id % mboard_serial)); + info_string = str(boost::format("Motherboard: %s (%s)\n") % mboard_id % mboard_serial); if(test_tx){ - if(tx_info.get("tx_serial") != "") tx_serial = tx_info.get("tx_serial"); - else tx_serial = "no serial"; + if(tx_info.get("tx_serial") == "") tx_serial = "no serial"; + else tx_serial = tx_info.get("tx_serial"); tx_subdev_name = tx_info.get("tx_subdev_name"); tx_subdev_spec = tx_info.get("tx_subdev_spec"); - info_string += std::string(str(boost::format("TX: %s (%s, %s)") % tx_subdev_name % tx_serial % tx_subdev_spec)); + info_string += is_b2xx ? str(boost::format("TX: %s (%s)") + % tx_subdev_name % tx_subdev_spec) + : str(boost::format("TX: %s (%s, %s)") + % tx_subdev_name % tx_serial % tx_subdev_spec); } if(test_tx and test_rx) info_string += "\n"; if(test_rx){ - if(rx_info.get("rx_serial") != "") rx_serial = rx_info.get("rx_serial"); - else rx_serial = "no serial"; + if(rx_info.get("rx_serial") == "") rx_serial = "no serial"; + else rx_serial = rx_info.get("rx_serial"); rx_subdev_name = rx_info.get("rx_subdev_name"); rx_subdev_spec = rx_info.get("rx_subdev_spec"); - info_string += std::string(str(boost::format("RX: %s (%s, %s)") % rx_subdev_name % rx_serial % rx_subdev_spec)); + info_string += is_b2xx ? str(boost::format("RX: %s (%s)") + % rx_subdev_name % rx_subdev_spec) + : str(boost::format("RX: %s (%s, %s)") + % rx_subdev_name % rx_serial % rx_subdev_spec); } return info_string; } -/************************************************************************ - * TX Frequency/Gain Coercion -************************************************************************/ +std::string coercion_test(uhd::usrp::multi_usrp::sptr usrp, std::string type, int chan, + bool test_gain, double freq_step, double gain_step, bool verbose){ -std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ + //Getting USRP info + uhd::dict<std::string, std::string> usrp_info = (type == "TX") ? usrp->get_usrp_tx_info(chan) + : usrp->get_usrp_rx_info(chan); + std::string subdev_name = (type == "TX") ? usrp_info.get("tx_subdev_name") + : usrp_info.get("rx_subdev_name"); + std::string subdev_spec = (type == "TX") ? usrp_info.get("tx_subdev_spec") + : usrp_info.get("rx_subdev_spec"); //Establish frequency range - std::vector<double> freqs; - std::vector<double> xcvr_freqs; + std::vector<double> xcvr_freqs; //XCVR2450 has two ranges + uhd::freq_range_t freq_ranges = (type == "TX") ? usrp->get_fe_tx_freq_range(chan) + : usrp->get_fe_rx_freq_range(chan); + + std::cout << boost::format("\nTesting %s coercion...") % type << std::endl; - BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_tx_freq_range()){ + BOOST_FOREACH(const uhd::range_t &range, freq_ranges){ double freq_begin = range.start(); double freq_end = range.stop(); - double freq_step; - if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ + if(subdev_name.find("XCVR2450") == 0){ xcvr_freqs.push_back(freq_begin); xcvr_freqs.push_back(freq_end); } - if(freq_end - freq_begin > 1000e6) freq_step = 100e6; - else if(freq_end - freq_begin < 300e6) freq_step = 10e6; - else freq_step = 50e6; - double current_freq = freq_begin; - while(current_freq < freq_end){ freqs.push_back(current_freq); current_freq += freq_step; @@ -109,55 +123,66 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo std::vector<double> gains; if(test_gain){ - //Establish gain range + uhd::gain_range_t gain_range = (type == "TX") ? usrp->get_tx_gain_range(chan) + : usrp->get_rx_gain_range(chan); - double gain_begin = usrp->get_tx_gain_range().start(); + double gain_begin = gain_range.start(); + //Start gain at 0 if range begins negative if(gain_begin < 0.0) gain_begin = 0.0; - double gain_end = usrp->get_tx_gain_range().stop(); + + double gain_end = gain_range.stop(); double current_gain = gain_begin; while(current_gain < gain_end){ gains.push_back(current_gain); - current_gain++; + current_gain += gain_step; } gains.push_back(gain_end); - } //Establish error-storing variables - std::vector<double> bad_tune_freqs; std::vector<double> no_lock_freqs; - std::vector< std::vector< double > > bad_gain_vals; - std::vector<std::string> dboard_sensor_names = usrp->get_tx_sensor_names(); + pair_vector bad_gain_vals; + + //Sensor names + std::vector<std::string> dboard_sensor_names = (type == "TX") ? usrp->get_tx_sensor_names(chan) + : usrp->get_rx_sensor_names(chan); std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); + bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); - for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ + BOOST_FOREACH(double freq, freqs){ //Testing for successful frequency tune + if(type == "TX") usrp->set_tx_freq(freq,chan); + else usrp->set_rx_freq(freq,chan); - usrp->set_tx_freq(*f); boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); + double actual_freq = (type == "TX") ? usrp->get_tx_freq(chan) + : usrp->get_rx_freq(chan); - double actual_freq = usrp->get_tx_freq(); - - if(*f == 0.0){ + if(freq == 0.0){ if(floor(actual_freq + 0.5) == 0.0){ - if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("\n%s frequency successfully tuned to %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("\n%s frequency tuned to %s instead of %s.") + % type % MHz_str(actual_freq) % MHz_str(freq) << std::endl; + bad_tune_freqs.push_back(freq); } } else{ - if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ - if(verbose) std::cout << boost::format("\nTX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; + if((freq / actual_freq > 0.9999) and (freq / actual_freq < 1.0001)){ + if(verbose) std::cout << boost::format("\n%s frequency successfully tuned to %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("\nTX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - bad_tune_freqs.push_back(*f); + if(verbose) std::cout << boost::format("\n%s frequency tuned to %s instead of %s.") + % type % MHz_str(actual_freq) % MHz_str(freq) << std::endl; + bad_tune_freqs.push_back(freq); } } @@ -173,11 +198,13 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo } } if(is_locked){ - if(verbose) std::cout << boost::format("LO successfully locked at TX frequency %s.") % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("LO successfully locked at %s frequency %s.") + % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("LO did not successfully lock at TX frequency %s.") % return_MHz_string(*f) << std::endl; - no_lock_freqs.push_back(*f); + if(verbose) std::cout << boost::format("LO did not successfully lock at %s frequency %s.") + % type % MHz_str(freq) << std::endl; + no_lock_freqs.push_back(freq); } } @@ -185,275 +212,101 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo //Testing for successful gain tune - for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ - usrp->set_tx_gain(*g); + BOOST_FOREACH(double gain, gains){ + if(type == "TX") usrp->set_tx_gain(gain,chan); + else usrp->set_rx_gain(gain,chan); + boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - double actual_gain = usrp->get_tx_gain(); + double actual_gain = (type == "TX") ? usrp->get_tx_gain(chan) + : usrp->get_rx_gain(chan); - if(*g == 0.0){ + if(gain == 0.0){ if(actual_gain == 0.0){ - if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + if(verbose) std::cout << boost::format("Gain successfully set to %5.2f at %s frequency %s.") + % gain % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); + if(verbose) std::cout << boost::format("Gain set to %5.2f instead of %5.2f at %s frequency %s.") + % actual_gain % gain % type % MHz_str(freq) << std::endl; + bad_gain_vals.push_back(std::make_pair(freq, gain)); } } else{ - if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ - if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; + if((gain / actual_gain) > 0.9999 and (gain / actual_gain) < 1.0001){ + if(verbose) std::cout << boost::format("Gain successfully set to %5.2f at %s frequency %s.") + % gain % type % MHz_str(freq) << std::endl; } else{ - if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); + if(verbose) std::cout << boost::format("Gain set to %5.2f instead of %5.2f at %s frequency %s.") + % actual_gain % gain % type % MHz_str(freq) << std::endl; + bad_gain_vals.push_back(std::make_pair(freq, gain)); } } } } } - std::string tx_results = "TX Summary:\n"; - if(usrp->get_usrp_tx_info().get("tx_subdev_name") == "XCVR2450 TX"){ - tx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % - return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); + std::string results = str(boost::format("%s Summary:\n") % type); + if(subdev_name.find("XCVR2450") == 0){ + results += str(boost::format("Frequency Range: %s - %s, %s - %s\n") + % MHz_str(xcvr_freqs[0]) % MHz_str(xcvr_freqs[1]) + % MHz_str(xcvr_freqs[2]) % MHz_str(xcvr_freqs[3])); + } + else results += str(boost::format("Frequency Range: %s - %s (Step: %s)\n") + % MHz_str(freqs.front()) % MHz_str(freqs.back()) % MHz_str(freq_step)); + if(test_gain) results += str(boost::format("Gain Range:%5.2f - %5.2f (Step:%5.2f)\n") + % gains.front() % gains.back() % gain_step); + + if(bad_tune_freqs.empty()) results += "USRP successfully tuned to all frequencies."; + else if(bad_tune_freqs.size() > 10 and not verbose){ + //If tuning fails at many values, don't print them all + results += str(boost::format("USRP did not successfully tune at %d frequencies.") + % bad_tune_freqs.size()); } - else tx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); - if(test_gain) tx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); - - if(bad_tune_freqs.empty()) tx_results += "USRP successfully tuned to all frequencies."; else{ - tx_results += "USRP did not successfully tune to the following frequencies: "; - for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ - if(i != bad_tune_freqs.begin()) tx_results += ", "; - tx_results += return_MHz_string(*i); + results += "USRP did not successfully tune to the following frequencies: "; + BOOST_FOREACH(double bad_freq, bad_tune_freqs){ + if(bad_freq != *bad_tune_freqs.begin()) results += ", "; + results += MHz_str(bad_freq); } } if(has_sensor){ - tx_results += "\n"; - if(no_lock_freqs.empty()) tx_results += "LO successfully locked at all frequencies."; - else{ - tx_results += "LO did not lock at the following frequencies: "; - for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ - if(i != no_lock_freqs.begin()) tx_results += ", "; - tx_results += return_MHz_string(*i); - } + results += "\n"; + if(no_lock_freqs.empty()) results += "LO successfully locked at all frequencies."; + else if(no_lock_freqs.size() > 10 and not verbose){ + //If locking fails at many values, don't print them all + results += str(boost::format("USRP did not successfully lock at %d frequencies.") + % no_lock_freqs.size()); } - } - if(test_gain){ - tx_results += "\n"; - if(bad_gain_vals.empty()) tx_results += "USRP successfully set all specified gain values at all frequencies."; else{ - tx_results += "USRP did not successfully set gain under the following circumstances:"; - for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ - std::vector<double> bad_pair = *i; - double bad_freq = bad_pair.front(); - double bad_gain = bad_pair.back(); - tx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + results += "LO did not lock at the following frequencies: "; + BOOST_FOREACH(double bad_freq, no_lock_freqs){ + if(bad_freq != *no_lock_freqs.begin()) results += ", "; + results += MHz_str(bad_freq); } } } - - return tx_results; -} - -/************************************************************************ - * RX Frequency/Gain Coercion -************************************************************************/ - -std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbose){ - - //Establish frequency range - - std::vector<double> freqs; - std::vector<double> xcvr_freqs; - - BOOST_FOREACH(const uhd::range_t &range, usrp->get_fe_rx_freq_range()){ - double freq_begin = range.start(); - double freq_end = range.stop(); - - if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ - xcvr_freqs.push_back(freq_begin); - xcvr_freqs.push_back(freq_end); - } - - double freq_step; - - if(freq_end - freq_begin > 1000e6) freq_step = 100e6; - else if(freq_end - freq_begin < 300e6) freq_step = 10e6; - else freq_step = 50e6; - - double current_freq = freq_begin; - - while(current_freq < freq_end){ - freqs.push_back(current_freq); - current_freq += freq_step; - } - } - - std::vector<double> gains; - if(test_gain){ - - //Establish gain range - - double gain_begin = usrp->get_rx_gain_range().start(); - if(gain_begin < 0.0) gain_begin = 0.0; - double gain_end = usrp->get_rx_gain_range().stop(); - - double current_gain = gain_begin; - while(current_gain < gain_end){ - gains.push_back(current_gain); - current_gain++; - } - gains.push_back(gain_end); - - } - - //Establish error-storing variables - - std::vector<double> bad_tune_freqs; - std::vector<double> no_lock_freqs; - std::vector< std::vector< double > > bad_gain_vals; - std::vector<std::string> dboard_sensor_names = usrp->get_rx_sensor_names(); - std::vector<std::string> mboard_sensor_names = usrp->get_mboard_sensor_names(); - bool has_sensor = (std::find(dboard_sensor_names.begin(), dboard_sensor_names.end(), "lo_locked")) != dboard_sensor_names.end(); - - for(std::vector<double>::iterator f = freqs.begin(); f != freqs.end(); ++f){ - - //Testing for successful frequency tune - - usrp->set_rx_freq(*f); - boost::this_thread::sleep(boost::posix_time::microseconds(long(1000))); - - double actual_freq = usrp->get_rx_freq(); - - if(*f == 0.0){ - if(floor(actual_freq + 0.5) == 0.0){ - if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - } + results += "\n"; + if(bad_gain_vals.empty()) results += "USRP successfully set all specified gain values at all frequencies."; + else if(bad_gain_vals.size() > 10 and not verbose){ + //If gain fails at many values, don't print them all + results += str(boost::format("USRP did not successfully set gain at %d values.") + % bad_gain_vals.size()); } else{ - if((*f / actual_freq > 0.9999) and (*f / actual_freq < 1.0001)){ - if(verbose) std::cout << boost::format("\nRX frequency successfully tuned to %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("\nRX frequency tuned to %s instead of %s.") % return_MHz_string(actual_freq) % return_MHz_string(*f) << std::endl; - bad_tune_freqs.push_back(*f); - } - } - - //Testing for successful lock - - if(has_sensor){ - bool is_locked = false; - for(int i = 0; i < 1000; i++){ - boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - if(usrp->get_rx_sensor("lo_locked",0).to_bool()){ - is_locked = true; - break; - } - } - if(is_locked){ - if(verbose) std::cout << boost::format("LO successfully locked at RX frequency %s.") % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("LO did not successfully lock at RX frequency %s.") % return_MHz_string(*f) << std::endl; - no_lock_freqs.push_back(*f); - } - } - - if(test_gain){ - - //Testing for successful gain tune - - for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ - usrp->set_rx_gain(*g); - boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - - double actual_gain = usrp->get_rx_gain(); - - if(*g == 0.0){ - if(actual_gain == 0.0){ - if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); - } - } - else{ - if((*g / actual_gain) > 0.9 and (*g / actual_gain) < 1.1){ - if(verbose) std::cout << boost::format("RX gain successfully set to %5.2f at RX frequency %s.") % *g % return_MHz_string(*f) << std::endl; - } - else{ - if(verbose) std::cout << boost::format("RX gain set to %5.2f instead of %5.2f at RX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; - std::vector<double> bad_gain_freq; - bad_gain_freq.push_back(*f); - bad_gain_freq.push_back(*g); - bad_gain_vals.push_back(bad_gain_freq); - } - } - } - } - } - - std::string rx_results = "RX Summary:\n"; - if(usrp->get_usrp_rx_info().get("rx_subdev_name") == "XCVR2450 RX"){ - rx_results += std::string(str(boost::format("Frequency Range: %s - %s, %s - %s\n") % return_MHz_string(xcvr_freqs.at(0)) % return_MHz_string(xcvr_freqs.at(1)) % - return_MHz_string(xcvr_freqs.at(2)) % return_MHz_string(xcvr_freqs.at(3)))); - } - else rx_results += std::string(str(boost::format("Frequency Range: %s - %s\n") % return_MHz_string(freqs.front()) % return_MHz_string(freqs.back()))); - if(test_gain) rx_results += std::string(str(boost::format("Gain Range: %5.2f - %5.2f\n") % gains.front() % gains.back())); - - if(bad_tune_freqs.empty()) rx_results += "USRP successfully tuned to all frequencies."; - else{ - rx_results += "USRP did not successfully tune to the following frequencies: "; - for(std::vector<double>::iterator i = bad_tune_freqs.begin(); i != bad_tune_freqs.end(); ++i){ - if(i != bad_tune_freqs.begin()) rx_results += ", "; - rx_results += return_MHz_string(*i); - } - } - if(has_sensor){ - - rx_results += "\n"; - if(no_lock_freqs.empty()) rx_results += "LO successfully locked at all frequencies."; - else{ - rx_results += "LO did not successfully lock at the following frequencies: "; - for(std::vector<double>::iterator i = no_lock_freqs.begin(); i != no_lock_freqs.end(); ++i){ - if( i != no_lock_freqs.begin()) rx_results += ", "; - rx_results += return_MHz_string(*i); - } - } - } - if(test_gain){ - rx_results += "\n"; - if(bad_gain_vals.empty()) rx_results += "USRP successfully set all specified gain values at all frequencies."; - else{ - rx_results += "USRP did not successfully set gain under the following circumstances:"; - for(std::vector< std::vector<double> >::iterator i = bad_gain_vals.begin(); i != bad_gain_vals.end(); ++i){ - std::vector<double> bad_pair = *i; - double bad_freq = bad_pair.front(); - double bad_gain = bad_pair.back(); - rx_results += std::string(str(boost::format("\nFrequency: %s, Gain: %5.2f") % return_MHz_string(bad_freq) % bad_gain)); + results += "USRP did not successfully set gain under the following circumstances:"; + BOOST_FOREACH(double_pair bad_pair, bad_gain_vals){ + double bad_freq = bad_pair.first; + double bad_gain = bad_pair.second; + results += str(boost::format("\nFrequency: %s, Gain: %5.2f") % MHz_str(bad_freq) % bad_gain); } } } - return rx_results; + return results; } /************************************************************************ @@ -463,8 +316,9 @@ std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo int UHD_SAFE_MAIN(int argc, char *argv[]){ //Variables + int chan; std::string args; - double gain_step; + double freq_step, gain_step; std::string ref; std::string tx_results; std::string rx_results; @@ -475,34 +329,20 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "Specify the UHD device") - ("gain_step", po::value<double>(&gain_step)->default_value(1.0), "Specify the delta between gain scans") + ("chan", po::value<int>(&chan)->default_value(0), "Specify multi_usrp channel") + ("freq-step", po::value<double>(&freq_step)->default_value(100e6), "Specify the delta between frequency scans") + ("gain-step", po::value<double>(&gain_step)->default_value(1.0), "Specify the delta between gain scans") ("tx", "Specify to test TX frequency and gain coercion") ("rx", "Specify to test RX frequency and gain coercion") ("ref", po::value<std::string>(&ref)->default_value("internal"), "Waveform type: internal, external, or mimo") - ("no_tx_gain", "Do not test TX gain") - ("no_rx_gain", "Do not test RX gain") + ("no-tx-gain", "Do not test TX gain") + ("no-rx-gain", "Do not test RX gain") ("verbose", "Output every frequency and gain check instead of just final summary") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Create a USRP device - std::cout << std::endl; - uhd::device_addrs_t device_addrs = uhd::device::find(args); - std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << std::endl << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - usrp->set_tx_rate(1e6); - usrp->set_rx_rate(1e6); - - //Boolean variables based on command line input - bool test_tx = vm.count("tx") > 0; - bool test_rx = vm.count("rx") > 0; - bool test_tx_gain = !(vm.count("no_tx_gain") > 0) and (usrp->get_tx_gain_range().stop() > 0); - bool test_rx_gain = !(vm.count("no_rx_gain") > 0) and (usrp->get_rx_gain_range().stop() > 0); - bool verbose = vm.count("verbose") > 0; - //Help messages, errors if(vm.count("help") > 0){ std::cout << "UHD Daughterboard Coercion Test\n" @@ -510,42 +350,72 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ "make sure that they can successfully tune to all\n" "frequencies and gains in their advertised ranges.\n\n"; std::cout << desc << std::endl; - return ~0; - } - - if(ref != "internal" and ref != "external" and ref != "mimo"){ - std::cout << desc << std::endl; - std::cout << "REF must equal internal, external, or mimo." << std::endl; - return ~0; + return EXIT_SUCCESS; } if(vm.count("tx") + vm.count("rx") == 0){ std::cout << desc << std::endl; std::cout << "Specify --tx to test for TX frequency coercion\n" "Specify --rx to test for RX frequency coercion\n"; - return ~0; + return EXIT_FAILURE; } - if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Basic RX (0x0001)"){ - std::cout << desc << std::endl; - std::cout << "This test does not work with the Basic RX daughterboard." << std::endl; - return ~0; - } - else if(test_rx and usrp->get_usrp_rx_info().get("rx_id") == "Unknown (0xffff)"){ + //Create a USRP device + std::cout << std::endl; + uhd::device_addrs_t device_addrs = uhd::device::find(args); + std::cout << boost::format("Creating the USRP device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << std::endl << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + usrp->set_tx_rate(SAMP_RATE); + usrp->set_rx_rate(SAMP_RATE); + + //Boolean variables based on command line input + bool test_tx = vm.count("tx") > 0; + bool test_rx = vm.count("rx") > 0; + bool test_tx_gain = !(vm.count("no-tx-gain") > 0) and (usrp->get_tx_gain_range().stop() > 0); + bool test_rx_gain = !(vm.count("no-rx-gain") > 0) and (usrp->get_rx_gain_range().stop() > 0); + bool verbose = vm.count("verbose") > 0; + + if(ref != "internal" and ref != "external" and ref != "mimo"){ std::cout << desc << std::endl; - std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; - return ~0; + std::cout << "REF must equal internal, external, or mimo." << std::endl; + return EXIT_FAILURE; } - if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Basic TX (0x0000)"){ - std::cout << desc << std::endl; - std::cout << "This test does not work with the Basic TX daughterboard." << std::endl; - return ~0; + //Use TX mboard ID to determine if this is a B2xx, will still return value if there is no TX + std::string tx_mboard_id = usrp->get_usrp_tx_info(chan).get("mboard_id"); + bool is_b2xx = (tx_mboard_id == "B200" or tx_mboard_id == "B210"); + + //Don't perform daughterboard validity checks for B200/B210 + if((not is_b2xx) and test_tx){ + std::string tx_dboard_name = usrp->get_usrp_tx_info(chan).get("tx_id"); + if(tx_dboard_name == "Basic TX (0x0000)" or tx_dboard_name == "LF TX (0x000e)"){ + std::cout << desc << std::endl; + std::cout << boost::format("This test does not work with the %s daughterboard.") + % tx_dboard_name << std::endl; + return EXIT_FAILURE; + } + else if(tx_dboard_name == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no TX daughterboard." << std::endl; + return EXIT_FAILURE; + } } - else if(test_tx and usrp->get_usrp_tx_info().get("tx_id") == "Unknown (0xffff)"){ - std::cout << desc << std::endl; - std::cout << "This daughterboard is unrecognized, or there is no TX daughterboard." << std::endl; - return ~0; + + //Don't perform daughterboard validity checks for B200/B210 + if((not is_b2xx) and test_rx){ + std::string rx_dboard_name = usrp->get_usrp_rx_info(chan).get("rx_id"); + if(rx_dboard_name == "Basic RX (0x0001)" or rx_dboard_name == "LF RX (0x000f)"){ + std::cout << desc << std::endl; + std::cout << boost::format("This test does not work with the %s daughterboard.") + % rx_dboard_name << std::endl; + return EXIT_FAILURE; + } + else if(rx_dboard_name == "Unknown (0xffff)"){ + std::cout << desc << std::endl; + std::cout << "This daughterboard is unrecognized, or there is no RX daughterboard." << std::endl; + return EXIT_FAILURE; + } } //Setting clock source @@ -563,12 +433,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Checking REF lock: %s ...") % ref_locked.to_pp_string() << std::endl; UHD_ASSERT_THROW(ref_locked.to_bool()); } - usrp_config = return_USRP_config_string(usrp, test_tx, test_rx); - if(test_tx) tx_results = tx_test(usrp, test_tx_gain, verbose); - if(test_rx) rx_results = rx_test(usrp, test_rx_gain, verbose); + usrp_config = return_usrp_config_string(usrp, chan, test_tx, test_rx, is_b2xx); + if(test_tx) tx_results = coercion_test(usrp, "TX", chan, test_tx_gain, freq_step, gain_step, verbose); + if(test_rx) rx_results = coercion_test(usrp, "RX", chan, test_rx_gain, freq_step, gain_step, verbose); - if(verbose) std::cout << std::endl; - std::cout << usrp_config << std::endl << std::endl; + std::cout << std::endl << usrp_config << std::endl << std::endl; if(test_tx) std::cout << tx_results << std::endl; if(test_tx and test_rx) std::cout << std::endl; if(test_rx) std::cout << rx_results << std::endl; diff --git a/host/examples/test_timed_commands.cpp b/host/examples/test_timed_commands.cpp index 8c6011c68..3da4bc707 100644 --- a/host/examples/test_timed_commands.cpp +++ b/host/examples/test_timed_commands.cpp @@ -139,8 +139,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ const size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 1.0); if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } std::cout << boost::format( " Received packet: %u samples, %u full secs, %f frac secs" diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp index 4b949e5bd..3f233b2a5 100644 --- a/host/examples/transport_hammer.cpp +++ b/host/examples/transport_hammer.cpp @@ -88,7 +88,7 @@ void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd: break; default: - std::cerr << "Error code: " << md.error_code << std::endl; + std::cerr << "Receiver error: " << md.strerror() << std::endl; std::cerr << "Unexpected error on recv, continuing..." << std::endl; break; } diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index 3d3cf1dfc..a62ccd7b2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -181,8 +181,8 @@ template<typename samp_type> void recv_to_file( } if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error %s" + ) % md.strerror())); } num_total_samps += num_rx_samps; diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index 1090c243c..b54ffc5f7 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -77,10 +77,18 @@ public: */ static sptr make(const device_addr_t &hint, size_t which = 0); - //! Make a new receive streamer from the streamer arguments + /*! \brief Make a new receive streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_rx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; - //! Make a new transmit streamer from the streamer arguments + /*! \brief Make a new transmit streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_tx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; //! Get access to the underlying property structure diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index f7abb396f..fc1de245d 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -104,16 +104,20 @@ public: uint32_t& num_remaining); private: //Methods - bool _is_initialized(); datatype_info_t _get_datatype_info(); nirio_status _get_transfer_count(uint64_t& transfer_count); nirio_status _ensure_transfer_completed(uint32_t timeout_ms); private: //Members + enum fifo_state_t { + UNMAPPED, MAPPED, STARTED + }; + std::string _name; fifo_direction_t _fifo_direction; uint32_t _fifo_channel; datatype_info_t _datatype_info; + fifo_state_t _state; size_t _acquired_pending; nirio_driver_iface::rio_mmap_t _mem_map; boost::recursive_mutex _mutex; diff --git a/host/include/uhd/transport/nirio/nirio_fifo.ipp b/host/include/uhd/transport/nirio/nirio_fifo.ipp index 80a0c2a89..437e3a1fc 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.ipp +++ b/host/include/uhd/transport/nirio/nirio_fifo.ipp @@ -31,6 +31,7 @@ nirio_fifo<data_t>::nirio_fifo( _fifo_direction(direction), _fifo_channel(fifo_instance), _datatype_info(_get_datatype_info()), + _state(UNMAPPED), _acquired_pending(0), _mem_map(), _riok_proxy_ptr(&riok_proxy), @@ -61,28 +62,37 @@ nirio_status nirio_fifo<data_t>::initialize( if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == UNMAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - //Forcefully stop the fifo if it is running - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + //Forcefully stop the fifo if it is running + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.params.fifo.channel = _fifo_channel; + _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); //Cleanup operation. Ignore status. - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + //Configure the FIFO now that we know it is stopped + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::CONFIGURE; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); + in.params.fifo.op.config.requiresActuals = 1; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.config.requestedDepth = static_cast<uint32_t>(requested_depth); - in.params.fifo.op.config.requiresActuals = 1; + if (nirio_status_fatal(status)) return status; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_fatal(status)) return status; + actual_depth = out.params.fifo.op.config.actualDepth; + actual_size = out.params.fifo.op.config.actualSize; - actual_depth = out.params.fifo.op.config.actualDepth; - actual_size = out.params.fifo.op.config.actualSize; + status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); - status = _riok_proxy_ptr->map_fifo_memory(_fifo_channel, actual_size, _mem_map); + if (nirio_status_not_fatal(status)) { + _state = MAPPED; + } + } else { + status = NiRio_Status_SoftwareFault; + } return status; } @@ -90,9 +100,13 @@ template <typename data_t> void nirio_fifo<data_t>::finalize() { boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (!_mem_map.is_null()) { - stop(); + + //If the FIFO is started, the stop will change the state to MAPPED. + stop(); + + if (_state == MAPPED) { _riok_proxy_ptr->unmap_fifo_memory(_mem_map); + _state = UNMAPPED; //Assume teardown succeeded } } @@ -104,16 +118,25 @@ nirio_status nirio_fifo<data_t>::start() boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + //Do nothing. Already started. + } else if (_state == MAPPED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::START; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status)) { - _acquired_pending = 0; - _expected_xfer_count = 0; + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + if (nirio_status_not_fatal(status)) { + _state = STARTED; + _acquired_pending = 0; + _expected_xfer_count = 0; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; } @@ -125,15 +148,22 @@ nirio_status nirio_fifo<data_t>::stop() if (!_riok_proxy_ptr) return NiRio_Status_ResourceNotInitialized; boost::unique_lock<boost::recursive_mutex> lock(_mutex); - if (_acquired_pending > 0) release(_acquired_pending); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + if (_acquired_pending > 0) release(_acquired_pending); + + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::STOP; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + in.params.fifo.channel = _fifo_channel; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + _state = MAPPED; //Assume teardown succeeded + } return status; } @@ -151,36 +181,40 @@ nirio_status nirio_fifo<data_t>::acquire( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - uint32_t stuffed[2]; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, stuffed, sizeof(stuffed)); - - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; - - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); - in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; - in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; - in.params.fifo.op.wait.timeout = timeout; - - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - - if (nirio_status_not_fatal(status)) { - elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); - elements_acquired = stuffed[0]; - elements_remaining = stuffed[1]; - _acquired_pending = elements_acquired; - - if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && - _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && - get_direction() == INPUT_FIFO - ) { - _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); - status = _ensure_transfer_completed(timeout); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + uint32_t stuffed[2]; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, stuffed, sizeof(stuffed)); + + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WAIT; + + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.wait.elementsRequested = static_cast<uint32_t>(elements_requested); + in.params.fifo.op.wait.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.wait.bitWidth = _datatype_info.width * 8; + in.params.fifo.op.wait.output = _fifo_direction == OUTPUT_FIFO; + in.params.fifo.op.wait.timeout = timeout; + + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + + if (nirio_status_not_fatal(status)) { + elements = static_cast<data_t*>(out.params.fifo.op.wait.elements.pointer); + elements_acquired = stuffed[0]; + elements_remaining = stuffed[1]; + _acquired_pending = elements_acquired; + + if (UHD_NIRIO_RX_FIFO_XFER_CHECK_EN && + _riok_proxy_ptr->get_rio_quirks().rx_fifo_xfer_check_en() && + get_direction() == INPUT_FIFO + ) { + _expected_xfer_count += static_cast<uint64_t>(elements_requested * sizeof(data_t)); + status = _ensure_transfer_completed(timeout); + } } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -194,17 +228,21 @@ nirio_status nirio_fifo<data_t>::release(const size_t elements) boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::GRANT; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.grant.elements = static_cast<uint32_t>(elements); - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - _acquired_pending = 0; + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + _acquired_pending = 0; + } else { + status = NiRio_Status_ResourceNotInitialized; + } return status; } @@ -222,23 +260,27 @@ nirio_status nirio_fifo<data_t>::read( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - nirio_driver_iface::nirio_syncop_out_params_t out = {}; - init_syncop_out_params(out, buf, num_elements * _datatype_info.width); + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + nirio_driver_iface::nirio_syncop_out_params_t out = {}; + init_syncop_out_params(out, buf, num_elements * _datatype_info.width); - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::READ; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.readWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.readWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_read = out.params.fifo.op.read.numberRead; - num_remaining = out.params.fifo.op.read.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_read = out.params.fifo.op.read.numberRead; + num_remaining = out.params.fifo.op.read.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; @@ -256,22 +298,26 @@ nirio_status nirio_fifo<data_t>::write( boost::unique_lock<boost::recursive_mutex> lock(_mutex); - nirio_driver_iface::nirio_syncop_in_params_t in = {}; - init_syncop_in_params(in, buf, num_elements * _datatype_info.width); - nirio_driver_iface::nirio_syncop_out_params_t out = {}; + if (_state == STARTED) { + nirio_driver_iface::nirio_syncop_in_params_t in = {}; + init_syncop_in_params(in, buf, num_elements * _datatype_info.width); + nirio_driver_iface::nirio_syncop_out_params_t out = {}; - in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; - in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; + in.function = nirio_driver_iface::NIRIO_FUNC::FIFO; + in.subfunction = nirio_driver_iface::NIRIO_FIFO::WRITE; - in.params.fifo.channel = _fifo_channel; - in.params.fifo.op.writeWithDataType.timeout = timeout; - in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); - in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; + in.params.fifo.channel = _fifo_channel; + in.params.fifo.op.writeWithDataType.timeout = timeout; + in.params.fifo.op.readWithDataType.scalarType = static_cast<uint32_t>(_datatype_info.scalar_type); + in.params.fifo.op.readWithDataType.bitWidth = _datatype_info.width * 8; - status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); + status = _riok_proxy_ptr->sync_operation(&in, sizeof(in), &out, sizeof(out)); - if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { - num_remaining = out.params.fifo.op.write.numberRemaining; + if (nirio_status_not_fatal(status) || status == NiRio_Status_FifoTimeout) { + num_remaining = out.params.fifo.op.write.numberRemaining; + } + } else { + status = NiRio_Status_ResourceNotInitialized; } return status; diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h index 326eeeb8c..ed4f72e7f 100644 --- a/host/include/uhd/transport/nirio/nirio_quirks.h +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -24,8 +24,8 @@ //Quirk#1: We need to verify RX zero-copy data transfers from the RIO // driver if we are in full duplex mode. -// This option allows disabling this quirk by compiling it out. -#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 1 +// This option allows enabling this quirk. +#define UHD_NIRIO_RX_FIFO_XFER_CHECK_EN 0 namespace uhd { namespace niusrprio { diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 6a79720d0..51a2b7c43 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 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 @@ -117,6 +117,20 @@ namespace uhd{ //! Out of sequence. The transport has either dropped a packet or received data out of order. bool out_of_sequence; + + /*! + * Convert a rx_metadata_t into a pretty print string. + * + * \param compact Set to false for a more verbose output. + * \return a printable string representing the metadata. + */ + std::string to_pp_string(bool compact=true) const; + + /*! + * Similar to C's strerror() function, creates a std::string describing the error code. + * \return a printable string representing the error. + */ + std::string strerror(void) const; }; /*! diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 5b4991202..883e4da3d 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -118,15 +118,11 @@ public: */ virtual device::sptr get_device(void) = 0; - //! Convenience method to get a RX streamer - rx_streamer::sptr get_rx_stream(const stream_args_t &args){ - return this->get_device()->get_rx_stream(args); - } + //! Convenience method to get a RX streamer. See also uhd::device::get_rx_stream(). + virtual rx_streamer::sptr get_rx_stream(const stream_args_t &args) = 0; - //! Convenience method to get a TX streamer - tx_streamer::sptr get_tx_stream(const stream_args_t &args){ - return this->get_device()->get_tx_stream(args); - } + //! Convenience method to get a TX streamer. See also uhd::device::get_rx_stream(). + virtual tx_streamer::sptr get_tx_stream(const stream_args_t &args) = 0; /*! * Returns identifying information about this USRP's configuration. diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index c0991b3ce..e9633286f 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -22,6 +22,7 @@ UHD_INSTALL(FILES atomic.hpp byteswap.hpp byteswap.ipp + cast.hpp csv.hpp gain_group.hpp images.hpp diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp new file mode 100644 index 000000000..9db92c526 --- /dev/null +++ b/host/include/uhd/utils/cast.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2014 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_UHD_UTILS_CAST_HPP +#define INCLUDED_UHD_UTILS_CAST_HPP + +#include <uhd/config.hpp> +#include <string> +#include <sstream> + +namespace uhd{ namespace cast{ + //! Convert a hexadecimal string into a value. + // + // Example: + // boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF"); + // Uses stringstream. + template<typename T> inline T hexstr_cast(const std::string &in) + { + T x; + std::stringstream ss; + ss << std::hex << in; + ss >> x; + return x; + } + +}} //namespace uhd::cast + +#endif /* INCLUDED_UHD_UTILS_CAST_HPP */ + diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index dc7f8f9dc..c7907ed83 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -134,6 +134,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("sc64", sizeof(std::complex<boost::int64_t>)); convert::register_bytes_per_item("sc32", sizeof(std::complex<boost::int32_t>)); convert::register_bytes_per_item("sc16", sizeof(std::complex<boost::int16_t>)); + convert::register_bytes_per_item("sc12", 3 * sizeof(std::complex<boost::int8_t>)); convert::register_bytes_per_item("sc8", sizeof(std::complex<boost::int8_t>)); //register standard real types diff --git a/host/lib/transport/nirio/nifpga_lvbitx.cpp b/host/lib/transport/nirio/nifpga_lvbitx.cpp index 289a44d4a..b87d87a8d 100644 --- a/host/lib/transport/nirio/nifpga_lvbitx.cpp +++ b/host/lib/transport/nirio/nifpga_lvbitx.cpp @@ -16,6 +16,7 @@ // #include <uhd/transport/nirio/nifpga_lvbitx.h> +#include <cstdlib> #include <string> #include <iostream> #include <fstream> @@ -109,6 +110,21 @@ std::string nifpga_lvbitx::_get_fpga_images_dir(const std::string search_paths) std::vector<std::string> search_path_vtr; boost::split(search_path_vtr, search_paths, boost::is_any_of(",")); + // + // Add the value of the UHD_IMAGES_DIR environment variable to the list of + // directories searched for a LVBITX image. + // + char* uhd_images_dir; +#ifdef UHD_PLATFORM_WIN32 + size_t len; + errno_t err = _dupenv_s(&uhd_images_dir, &len, "UHD_IMAGES_DIR"); + if(not err and uhd_images_dir != NULL) search_path_vtr.push_back(std::string(uhd_images_dir)); + free(uhd_images_dir); +#else + uhd_images_dir = getenv("UHD_IMAGES_DIR"); + if(uhd_images_dir != NULL) search_path_vtr.push_back(std::string(uhd_images_dir)); +#endif + std::string lvbitx_dir; //Traverse through the list of search paths. Priority: lexical BOOST_FOREACH(std::string& search_path, search_path_vtr) { diff --git a/host/lib/transport/nirio_zero_copy.cpp b/host/lib/transport/nirio_zero_copy.cpp index 7b1e32fe0..c3c8a9368 100644 --- a/host/lib/transport/nirio_zero_copy.cpp +++ b/host/lib/transport/nirio_zero_copy.cpp @@ -18,7 +18,6 @@ #include <uhd/transport/nirio_zero_copy.hpp> #include <stdio.h> #include <uhd/transport/nirio/nirio_fifo.h> -#include <uhd/transport/nirio/nirio_fifo.h> #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> @@ -261,19 +260,27 @@ private: UHD_INLINE void _flush_rx_buff() { - nirio_status flush_status = 0; - while (nirio_status_not_fatal(flush_status)) { - static const size_t NUM_ELEMS_TO_FLUSH = 1; - static const uint32_t FLUSH_TIMEOUT_IN_MS = 0; - - fifo_data_t* flush_data_ptr = NULL; - size_t flush_elems_acquired = 0, flush_elems_remaining = 0; - flush_status = _recv_fifo->acquire( - flush_data_ptr, NUM_ELEMS_TO_FLUSH, FLUSH_TIMEOUT_IN_MS, - flush_elems_acquired, flush_elems_remaining); - if (nirio_status_not_fatal(flush_status)) { - _recv_fifo->release(flush_elems_acquired); - } + // acquire is called with 0 elements requested first to + // get the number of elements in the buffer and then + // repeatedly with the number of remaining elements + // until the buffer is empty + fifo_data_t* elems_buffer; + for (size_t num_elems_requested = 0, + num_elems_acquired = 0, + num_elems_remaining = 1; + num_elems_remaining; + num_elems_requested = num_elems_remaining) + { + nirio_status status = _recv_fifo->acquire( + elems_buffer, + num_elems_requested, + 0, // timeout + num_elems_acquired, + num_elems_remaining); + // throw excetption if status is fatal + nirio_status_to_exception(status, + "NI-RIO PCIe data transfer failed during flush."); + _recv_fifo->release(num_elems_acquired); } } diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index b69c8e487..7fc6bdd94 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -82,6 +82,7 @@ SET_SOURCE_FILES_PROPERTIES( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/device_addr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mac_addr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp diff --git a/host/lib/types/metadata.cpp b/host/lib/types/metadata.cpp new file mode 100644 index 000000000..fec2ac564 --- /dev/null +++ b/host/lib/types/metadata.cpp @@ -0,0 +1,92 @@ +// +// Copyright 2014 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 <string> +#include <sstream> +#include <boost/format.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/time_spec.hpp> + +using namespace uhd; + +std::string rx_metadata_t::to_pp_string(bool compact) const +{ + std::stringstream ss; + + if (compact) { + if (has_time_spec) { + ss << "Time: " << time_spec.get_real_secs() << " s\n"; + } + if (more_fragments) { + ss << "Fragmentation offset: " << fragment_offset << "\n"; + } + if (start_of_burst) { + ss << "Start of burst.\n" << fragment_offset; + } + if (end_of_burst) { + ss << "End of burst.\n" << fragment_offset; + } + if (error_code != ERROR_CODE_NONE) { + ss << strerror() << "\n"; + } + } else { + ss << "Has timespec: " << (has_time_spec ? "Yes" : "No") + << "\tTime of first sample: " << time_spec.get_real_secs() + << "\nFragmented: " << (more_fragments ? "Yes" : "No") + << " Fragmentation offset: " << fragment_offset + << "\nStart of burst: " << (start_of_burst ? "Yes" : "No") + << "\tEnd of burst: " << (end_of_burst ? "Yes" : "No") + << "\nError Code: " << strerror() + << "\tOut of sequence: " << (out_of_sequence ? "Yes" : "No"); + } + + return ss.str(); +} + +std::string rx_metadata_t::strerror() const +{ + std::string errstr = ""; + switch(this->error_code) { + case ERROR_CODE_NONE: + errstr = "ERROR_CODE_NONE"; + break; + case ERROR_CODE_TIMEOUT: + errstr = "ERROR_CODE_TIMEOUT"; + break; + case ERROR_CODE_LATE_COMMAND: + errstr = "ERROR_CODE_LATE_COMMAND"; + break; + case ERROR_CODE_BROKEN_CHAIN: + errstr = "ERROR_CODE_BROKEN_CHAIN (Expected another stream command)"; + break; + case ERROR_CODE_OVERFLOW: + errstr = "ERROR_CODE_OVERFLOW "; + errstr += (this->out_of_sequence ? "(Out of sequence error)" : "(Overflow)"); + break; + case ERROR_CODE_ALIGNMENT: + errstr = "ERROR_CODE_ALIGNMENT (Multi-channel alignment failed)"; + break; + case ERROR_CODE_BAD_PACKET: + errstr = "ERROR_CODE_BAD_PACKET"; + break; + default: + errstr = std::string(str(boost::format("Unknown error code: 0x%x") % error_code)); + } + + return errstr; +} diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index a47856b07..baf2b6ae3 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -20,6 +20,7 @@ #include "b100_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -56,11 +57,11 @@ static device_addrs_t b100_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b100_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b100") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B100_VENDOR_ID; pid = B100_PRODUCT_ID; @@ -515,6 +516,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(subdev_spec_t("A:" + _tree->list(mb_path / "dboards/A/tx_frontends").at(0))); _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->create<double>(mb_path / "link_max_rate").set(B100_MAX_RATE_USB2); } b100_impl::~b100_impl(void){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 7d71d5ec3..b6752681e 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -54,6 +54,7 @@ static const boost::uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; static const std::string B100_EEPROM_MAP_KEY = "B100"; +static const size_t B100_MAX_RATE_USB2 = 32000000; // bytes/s #define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) #define I2C_ADDR_RX_A (I2C_DEV_EEPROM | 0x5) diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 9dd3a424d..98141dbaa 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -19,6 +19,7 @@ #include "b200_regs.hpp" #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -58,11 +59,11 @@ static device_addrs_t b200_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return b200_addrs; - unsigned int vid, pid; + boost::uint16_t 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); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = B200_VENDOR_ID; pid = B200_PRODUCT_ID; @@ -160,9 +161,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; if (device_addr.has_key("vid")) - sscanf(device_addr.get("vid").c_str(), "%hx", &vid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); if (device_addr.has_key("pid")) - sscanf(device_addr.get("pid").c_str(), "%hx", &pid); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid, pid); @@ -250,6 +251,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport + _tree->create<double>(mb_path / "link_max_rate").set((usb_speed == 3) ? B200_MAX_RATE_USB3 : B200_MAX_RATE_USB2); //////////////////////////////////////////////////////////////////// // Async task structure diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 7d98a8f8d..c3508c550 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -47,11 +47,13 @@ static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x04; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x03; -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; +static const size_t B200_MAX_RATE_USB2 = 32000000; // bytes/s +static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s + #define FLIP_SID(sid) (((sid)<<16)|((sid)>>16)) static const boost::uint32_t B200_CTRL0_MSG_SID = 0x00000010; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 86846667f..02c24b4bb 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -91,8 +91,10 @@ public: uhd::meta_range_t get_host_rates(void){ meta_range_t range; - for (int rate = 1024; rate > 512; rate -= 8){ - range.push_back(range_t(_tick_rate/rate)); + if (!_is_b200) { + for (int rate = 1024; rate > 512; rate -= 8){ + range.push_back(range_t(_tick_rate/rate)); + } } for (int rate = 512; rate > 256; rate -= 4){ range.push_back(range_t(_tick_rate/rate)); diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index c593c5437..c74c64471 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010,2012-2013 Ettus Research LLC +// Copyright 2010,2012-2014 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 @@ -1005,8 +1005,8 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ _freq_scalar = (4*16.0e6)/(this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX)); } else if (ref_clock == 100e6) { - - this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV8); + + this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, REFCLOCK_DIV6); UHD_LOGV(often) << boost::format( "TVRX2 (%s): Dividing Refclock by 6" diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index f08709669..4883b2410 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -24,6 +24,7 @@ #include <uhd/usrp/dboard_id.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/convert.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> @@ -103,6 +104,8 @@ static meta_range_t make_overall_tune_range( return range; } + + /*********************************************************************** * Gain helper functions **********************************************************************/ @@ -589,6 +592,11 @@ public: /******************************************************************* * RX methods ******************************************************************/ + rx_streamer::sptr get_rx_stream(const stream_args_t &args) { + _check_link_rate(args, false); + return this->get_device()->get_rx_stream(args); + } + void set_rx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "rx_subdev_spec").set(spec); @@ -770,6 +778,11 @@ public: /******************************************************************* * TX methods ******************************************************************/ + tx_streamer::sptr get_tx_stream(const stream_args_t &args) { + _check_link_rate(args, true); + return this->get_device()->get_tx_stream(args); + } + void set_tx_subdev_spec(const subdev_spec_t &spec, size_t mboard){ if (mboard != ALL_MBOARDS){ _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").set(spec); @@ -1178,6 +1191,34 @@ private: } return gg; } + + //! \param is_tx True for tx + // Assumption is that all mboards use the same link + bool _check_link_rate(const stream_args_t &args, bool is_tx) { + bool link_rate_is_ok = true; + size_t bytes_per_sample = convert::get_bytes_per_item(args.otw_format); + double max_link_rate = 0; + double sum_rate = 0; + BOOST_FOREACH(const size_t chan, args.channels) { + mboard_chan_pair mcp = is_tx ? tx_chan_to_mcp(chan) : rx_chan_to_mcp(chan); + if (_tree->exists(mb_root(mcp.mboard) / "link_max_rate")) { + max_link_rate = std::max( + max_link_rate, + _tree->access<double>(mb_root(mcp.mboard) / "link_max_rate").get() + ); + } + sum_rate += is_tx ? get_tx_rate(chan) : get_rx_rate(chan); + } + if (max_link_rate > 0 and (max_link_rate / bytes_per_sample) < sum_rate) { + UHD_MSG(warning) << boost::format( + "The total sum of rates (%f MSps on %u channels) exceeds the maximum capacity of the connection.\n" + "This can cause %s." + ) % (sum_rate/1e6) % args.channels.size() % (is_tx ? "underruns (U)" : "overflows (O)") << std::endl; + link_rate_is_ok = false; + } + + return link_rate_is_ok; + } }; /*********************************************************************** diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 3b902b343..0ba2e1e4a 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -20,6 +20,7 @@ #include <uhd/utils/safe_call.hpp> #include <uhd/transport/usb_control.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/cast.hpp> #include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/images.hpp> @@ -59,11 +60,11 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) //since an address and resource is intended for a different, non-USB, device. if (hint.has_key("addr") || hint.has_key("resource")) return usrp1_addrs; - unsigned int vid, pid; + boost::uint16_t vid, pid; if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "usrp1") { - sscanf(hint.get("vid").c_str(), "%x", &vid); - sscanf(hint.get("pid").c_str(), "%x", &pid); + vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); + pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); } else { vid = USRP1_VENDOR_ID; pid = USRP1_PRODUCT_ID; @@ -409,7 +410,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + _tree->create<double>(mb_path / "link_max_rate").set(USRP1_MAX_RATE_USB2); } usrp1_impl::~usrp1_impl(void){ diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index da9fe8b16..012bc0794 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -39,6 +39,7 @@ #define INCLUDED_USRP1_IMPL_HPP static const std::string USRP1_EEPROM_MAP_KEY = "B000"; +static const size_t USRP1_MAX_RATE_USB2 = 32000000; // bytes/s #define FR_RB_CAPS 3 #define FR_MODE 13 diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 16d9b9a54..918f3e892 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -442,6 +442,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].spiface = _mbc[mb].iface; break; } + _tree->create<double>(mb_path / "link_max_rate").set(USRP2_LINK_RATE_BPS); //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -655,12 +656,14 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1)); //setup time source props _tree->create<std::string>(mb_path / "time_source/value") - .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)); + .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)) + .set("none"); _tree->create<std::vector<std::string> >(mb_path / "time_source/options") .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64)); //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") - .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); + .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)) + .set("internal"); std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index e492b2238..f5e53678c 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -392,6 +392,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; mb.rio_fpga_interface->get_kernel_proxy().get_rio_quirks().register_tx_streams(tx_data_fifos); + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } BOOST_FOREACH(const std::string &key, dev_addr.keys()) @@ -456,6 +458,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) << "UHD will use the auto-detected max frame size for this connection." << std::endl; } + + _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE); } //create basic communication @@ -1133,11 +1137,14 @@ x300_impl::both_xports_t x300_impl::make_transport( if (mb.loaded_fpga_image == "HGS") { if (mb.router_dst_here == X300_XB_DST_E0) { eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); } else if (mb.router_dst_here == X300_XB_DST_E1) { eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } } else if (mb.loaded_fpga_image == "XGS") { - eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; + _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); } if (eth_data_rec_frame_size == 0) { diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 259ea253d..4b3efc845 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -76,6 +76,19 @@ static const size_t X300_ETH_MSG_NUM_FRAMES = 32; static const size_t X300_ETH_DATA_NUM_FRAMES = 32; static const double X300_DEFAULT_SYSREF_RATE = 10e6; +static const size_t X300_TX_MAX_HDR_LEN = // bytes + sizeof(boost::uint32_t) // Header + + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp +static const size_t X300_RX_MAX_HDR_LEN = // bytes + sizeof(boost::uint32_t) // Header + + sizeof(uhd::transport::vrt::if_packet_info_t().sid) // SID + + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp + +static const size_t X300_MAX_RATE_PCIE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_10GIGE = 800000000; // bytes/s +static const size_t X300_MAX_RATE_1GIGE = 100000000; // bytes/s + #define X300_RADIO_DEST_PREFIX_TX 0 #define X300_RADIO_DEST_PREFIX_CTRL 1 #define X300_RADIO_DEST_PREFIX_RX 2 diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 85de34a54..9263c9b44 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -242,6 +242,8 @@ struct x300_tx_fc_guts_t boost::shared_ptr<x300_impl::async_md_type> old_async_queue; }; +#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 + static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) { double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); @@ -283,23 +285,28 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero return; } - //catch the flow control packets and react - if (endian_conv(packet_buff[if_packet_info.num_header_words32+0]) == 0) - { - const size_t seq = endian_conv(packet_buff[if_packet_info.num_header_words32+1]); - guts->seq_queue.push_with_haste(seq); - return; - } - //fill in the async metadata async_metadata_t metadata; load_metadata_from_buff( endian_conv, metadata, if_packet_info, packet_buff, clock->get_master_clock_rate(), guts->stream_channel); - guts->async_queue->push_with_pop_on_full(metadata); - metadata.channel = guts->device_channel; - guts->old_async_queue->push_with_pop_on_full(metadata); - standard_async_msg_prints(metadata); + + //The FC response and the burst ack are two indicators that the radio + //consumed packets. Use them to update the FC metadata + if (metadata.event_code == X300_ASYNC_EVENT_CODE_FLOW_CTRL or + metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK + ) { + const size_t seq = metadata.user_payload[0]; + guts->seq_queue.push_with_pop_on_full(seq); + } + + //FC responses don't propagate up to the user so filter them here + if (metadata.event_code != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { + guts->async_queue->push_with_pop_on_full(metadata); + metadata.channel = guts->device_channel; + guts->old_async_queue->push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } } static managed_send_buffer::sptr get_tx_buff_with_flowctrl( @@ -319,7 +326,9 @@ static managed_send_buffer::sptr get_tx_buff_with_flowctrl( } managed_send_buffer::sptr buff = xport->get_send_buff(timeout); - if (buff) guts->last_seq_out++; //update seq, this will actually be a send + if (buff) { + guts->last_seq_out++; //update seq, this will actually be a send + } return buff; } @@ -399,15 +408,9 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid); UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl; - //calculate packet size - static const size_t hdr_size = 0 - + vrt::num_vrl_words32*sizeof(boost::uint32_t) - + 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 = xport.recv->get_recv_frame_size() - hdr_size; // bytes per packet + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.recv->get_recv_frame_size() - X300_RX_MAX_HDR_LEN; // bytes per packet const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); // samples per packet @@ -568,15 +571,9 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid); UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl; - //calculate packet size - static const size_t hdr_size = 0 - + vrt::num_vrl_words32*sizeof(boost::uint32_t) - + 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 = xport.send->get_send_frame_size() - hdr_size; + // To calculate the max number of samples per packet, we assume the maximum header length + // to avoid fragmentation should the entire header be used. + const size_t bpp = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN; const size_t bpi = convert::get_bytes_per_item(args.otw_format); const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 2a40d0050..7c4815004 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -28,6 +28,7 @@ SET(test_sources buffer_test.cpp byteswap_test.cpp convert_test.cpp + cast_test.cpp dict_test.cpp error_test.cpp gain_group_test.cpp diff --git a/host/tests/cast_test.cpp b/host/tests/cast_test.cpp new file mode 100644 index 000000000..6b8a4c527 --- /dev/null +++ b/host/tests/cast_test.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2014 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 <iostream> +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/cast.hpp> + +BOOST_AUTO_TEST_CASE(test_mac_addr){ + std::string in = "0x0100"; + boost::uint16_t correct_result = 256; + boost::uint16_t x = uhd::cast::hexstr_cast<boost::uint16_t>(in); + //boost::uint16_t x = uhd::cast::hexstr_cast(in); + std::cout + << "Testing hex -> uint16_t conversion. " + << in << " == " << std::hex << x << "?" << std::endl; + BOOST_CHECK_EQUAL(x, correct_result); +} + diff --git a/host/utils/uhd_cal_rx_iq_balance.cpp b/host/utils/uhd_cal_rx_iq_balance.cpp index 12ae7ff46..3188e02a0 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -127,32 +127,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "RX", "rx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_dc_offset.cpp b/host/utils/uhd_cal_tx_dc_offset.cpp index c9eab9177..b5c5293f0 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -129,32 +129,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/uhd_cal_tx_iq_balance.cpp b/host/utils/uhd_cal_tx_iq_balance.cpp index 27e6356bb..6461b3d71 100644 --- a/host/utils/uhd_cal_tx_iq_balance.cpp +++ b/host/utils/uhd_cal_tx_iq_balance.cpp @@ -18,12 +18,7 @@ #include "usrp_cal_utils.hpp" #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> -#include <uhd/utils/paths.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/usrp/multi_usrp.hpp> #include <boost/program_options.hpp> -#include <boost/format.hpp> #include <boost/thread/thread.hpp> #include <boost/math/special_functions/round.hpp> #include <iostream> @@ -130,32 +125,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return EXIT_FAILURE; } - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - - // Configure subdev - if (vm.count("subdev")) { - usrp->set_tx_subdev_spec(subdev); - usrp->set_rx_subdev_spec(subdev); - } - UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; - serial = get_serial(usrp, "tx"); - UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; - - //set the antennas to cal - if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ - throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); - } - usrp->set_rx_antenna("CAL"); - usrp->set_tx_antenna("CAL"); - - //fail if daughterboard has no serial - check_for_empty_serial(usrp, "TX", "tx", args); - - //set optimum defaults - set_optimum_defaults(usrp); + // Create a USRP device + uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial); //create a receive streamer uhd::stream_args_t stream_args("fc32"); //complex floats diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index 5aff5e22f..9e7f4c469 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -20,6 +20,8 @@ #include <uhd/usrp/multi_usrp.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/utils/paths.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> #include <boost/filesystem.hpp> #include <boost/format.hpp> #include <iostream> @@ -50,6 +52,8 @@ static const size_t default_num_samps = 10000; **********************************************************************/ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path mb_path = "/mboards/0"; const std::string mb_name = tree->access<std::string>(mb_path / "name").get(); @@ -69,7 +73,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; + const uhd::fs_path tx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/tx_frontends/0"; const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get(); if (tx_name.find("WBX") != std::string::npos){ usrp->set_tx_gain(0); @@ -87,7 +91,7 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ throw std::runtime_error("self-calibration is not supported for this hardware"); } - const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/rx_frontends/0"; + const uhd::fs_path rx_fe_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); if (rx_name.find("WBX") != std::string::npos){ usrp->set_rx_gain(25); @@ -110,24 +114,19 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ /*********************************************************************** * Check for empty serial **********************************************************************/ - void check_for_empty_serial( - uhd::usrp::multi_usrp::sptr usrp, - std::string XX, - std::string xx, - std::string uhd_args + uhd::usrp::multi_usrp::sptr usrp ){ + // Will work on 1st subdev, top-level must make sure it's the right one + uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); //extract eeprom uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); - const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom"; + // This only works with transceiver boards, so we can always check rx side + const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/rx_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); - std::string args_str = ""; - if(uhd_args != "") args_str = str(boost::format(" --args=%s") % uhd_args); - - std::string error_string = str(boost::format("This %s dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this.") % XX); - + std::string error_string = "This dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this."; if (db_eeprom.serial.empty()) throw std::runtime_error(error_string); } @@ -188,6 +187,7 @@ static std::string get_serial( const std::string &tx_rx ){ uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + // Will work on 1st subdev, top-level must make sure it's the right one uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec(); const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/" + tx_rx + "_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); @@ -257,8 +257,8 @@ static void capture_samples( //validate the received data if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){ throw std::runtime_error(str(boost::format( - "Unexpected error code 0x%x" - ) % md.error_code)); + "Receiver error: %s" + ) % md.strerror())); } //we can live if all the data didnt come in if (num_rx_samps > buff.size()/2){ @@ -270,3 +270,37 @@ static void capture_samples( } } +/*********************************************************************** + * Setup function + **********************************************************************/ +static uhd::usrp::multi_usrp::sptr setup_usrp_for_cal(std::string &args, std::string &subdev, std::string &serial) +{ + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + + // Configure subdev + if (!subdev.empty()) { + usrp->set_tx_subdev_spec(subdev); + usrp->set_rx_subdev_spec(subdev); + } + UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl; + serial = get_serial(usrp, "tx"); + UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl; + + //set the antennas to cal + if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){ + throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate."); + } + usrp->set_rx_antenna("CAL"); + usrp->set_tx_antenna("CAL"); + + //fail if daughterboard has no serial + check_for_empty_serial(usrp); + + //set optimum defaults + set_optimum_defaults(usrp); + + return usrp; +} + diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 277e807d9..cecac5588 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -17,13 +17,13 @@ #include <csignal> #include <iostream> -#include <map> #include <fstream> #include <time.h> #include <vector> #include <boost/foreach.hpp> #include <boost/asio.hpp> +#include <boost/filesystem.hpp> #include <boost/program_options.hpp> #include <boost/assign.hpp> #include <boost/assign/list_of.hpp> @@ -32,21 +32,97 @@ #include <boost/filesystem.hpp> #include <boost/thread/thread.hpp> -#include "usrp_simple_burner_utils.hpp" #include <uhd/exception.hpp> #include <uhd/property_tree.hpp> #include <uhd/transport/if_addrs.hpp> #include <uhd/transport/udp_simple.hpp> +#include <uhd/types/dict.hpp> #include <uhd/utils/byteswap.hpp> #include <uhd/utils/images.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/safe_call.hpp> +namespace fs = boost::filesystem; namespace po = boost::program_options; using namespace boost::algorithm; using namespace uhd; using namespace uhd::transport; +#define UDP_FW_UPDATE_PORT 49154 +#define UDP_MAX_XFER_BYTES 1024 +#define UDP_TIMEOUT 3 +#define UDP_POLL_INTERVAL 0.10 //in seconds +#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 +#define USRP2_UDP_UPDATE_PORT 49154 +#define FLASH_DATA_PACKET_SIZE 256 +#define FPGA_IMAGE_SIZE_BYTES 1572864 +#define FW_IMAGE_SIZE_BYTES 31744 +#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 +#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 +#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 +#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 + +typedef enum { + UNKNOWN = ' ', + + USRP2_QUERY = 'a', + USRP2_ACK = 'A', + + GET_FLASH_INFO_CMD = 'f', + GET_FLASH_INFO_ACK = 'F', + + ERASE_FLASH_CMD = 'e', + ERASE_FLASH_ACK = 'E', + + CHECK_ERASING_DONE_CMD = 'd', + DONE_ERASING_ACK = 'D', + NOT_DONE_ERASING_ACK = 'B', + + WRITE_FLASH_CMD = 'w', + WRITE_FLASH_ACK = 'W', + + READ_FLASH_CMD = 'r', + READ_FLASH_ACK = 'R', + + RESET_USRP_CMD = 's', + RESET_USRP_ACK = 'S', + + GET_HW_REV_CMD = 'v', + GET_HW_REV_ACK = 'V', + +} usrp2_fw_update_id_t; + +typedef struct { + uint32_t proto_ver; + uint32_t id; + uint32_t seq; + union { + uint32_t ip_addr; + uint32_t hw_rev; + struct { + uint32_t flash_addr; + uint32_t length; + uint8_t data[256]; + } flash_args; + struct { + uint32_t sector_size_bytes; + uint32_t memory_size_bytes; + } flash_info_args; + } data; +} usrp2_fw_update_data_t; + +//Mapping revision numbers to filenames +uhd::dict<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of + (0xa, "n200_r3") + (0x100a, "n200_r4") + (0x10a, "n210_r3") + (0x110a, "n210_r4") +; + +boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; +boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; +boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; + /*********************************************************************** * Signal handlers **********************************************************************/ @@ -66,59 +142,94 @@ void sig_int_handler(int){ } } -//Mapping revision numbers to filenames -std::map<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of - (0xa, "n200_r3") - (0x100a, "n200_r4") - (0x10a, "n210_r3") - (0x110a, "n210_r4") -; +/*********************************************************************** + * List all connected USRP N2XX devices + **********************************************************************/ +void list_usrps(){ + udp_simple::sptr udp_bc_transport; + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + boost::uint32_t hw_rev; -//Images and image sizes, to be populated as necessary -boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; -boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; -int fpga_image_size = 0; -int fw_image_size = 0; + usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); + usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_QUERY); + + std::cout << "Available USRP N2XX devices:" << std::endl; -//For non-standard images not covered by uhd::find_image_path() -bool does_image_exist(std::string image_filepath){ + //Send UDP packets to all broadcast addresses + BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ + //Avoid the loopback device + if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; + udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - std::ifstream ifile((char*)image_filepath.c_str()); - return ifile; + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_ACK){ + usrp2_ack_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); + + size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + } + + std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; + } + } +} + +/*********************************************************************** + * Find USRP N2XX with specified IP address and return type + **********************************************************************/ +boost::uint32_t find_usrp(udp_simple::sptr udp_transport){ + boost::uint32_t hw_rev; + bool found_it = false; + + const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); + usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); + hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); + hw_info_pkt.id = htonx<boost::uint32_t>(GET_HW_REV_CMD); + udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); + + //Loop and receive until the timeout + size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_HW_REV_ACK){ + hw_rev = ntohl(update_data_in->data.hw_rev); + if(filename_map.has_key(hw_rev)){ + std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; + found_it = true; + } + else throw std::runtime_error("Invalid revision found."); + } + if(not found_it) throw std::runtime_error("No USRP N2XX found."); + + return hw_rev; } /*********************************************************************** * Custom filename validation functions **********************************************************************/ -void validate_custom_fpga_file(std::string rev_str, std::string fpga_path){ +void validate_custom_fpga_file(std::string rev_str, std::string& fpga_path){ //Check for existence of file - if(!does_image_exist(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); + if(not fs::exists(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fpga_path)); //Check to find rev_str in filename uhd::fs_path custom_fpga_path(fpga_path); - if(custom_fpga_path.leaf().find("fw") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename indicates that this is a firmware image.") - % fpga_path)); - } if(custom_fpga_path.leaf().find(rev_str) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fpga_path % rev_str)); } } -void validate_custom_fw_file(std::string rev_str, std::string fw_path){ +void validate_custom_fw_file(std::string rev_str, std::string& fw_path){ //Check for existence of file - if(!does_image_exist(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); + if(not fs::exists(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path)); //Check to find truncated rev_str in filename uhd::fs_path custom_fw_path(fw_path); - if(custom_fw_path.leaf().find("fpga") != std::string::npos){ - throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename indicates that this is an FPGA image.") - % fw_path)); - } if(custom_fw_path.leaf().find(erase_tail_copy(rev_str,3)) == std::string::npos){ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") % fw_path % erase_tail_copy(rev_str,3))); @@ -126,89 +237,91 @@ void validate_custom_fw_file(std::string rev_str, std::string fw_path){ } /*********************************************************************** - * Grabbing and validating image binaries + * Reading and validating image binaries **********************************************************************/ -int grab_fpga_image(std::string fpga_path){ +int read_fpga_image(std::string& fpga_path){ - //Reading FPGA image from file - std::ifstream to_read_fpga((char*)fpga_path.c_str(), std::ios::binary); - to_read_fpga.seekg(0, std::ios::end); - fpga_image_size = to_read_fpga.tellg(); - to_read_fpga.seekg(0, std::ios::beg); - char fpga_read[FPGA_IMAGE_SIZE_BYTES]; - to_read_fpga.read(fpga_read,fpga_image_size); - to_read_fpga.close(); - for(int i = 0; i < fpga_image_size; i++) fpga_image[i] = (boost::uint8_t)fpga_read[i]; - - //Checking validity of image + //Check size of given image + std::ifstream fpga_file(fpga_path.c_str(), std::ios::binary); + fpga_file.seekg(0, std::ios::end); + int fpga_image_size = fpga_file.tellg(); if(fpga_image_size > FPGA_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") + % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); } - //Check sequence of bytes in image + //Check sequence of bytes in image before reading + boost::uint8_t fpga_test_bytes[63]; + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_test_bytes,63); bool is_good = false; for(int i = 0; i < 63; i++){ - if((boost::uint8_t)fpga_image[i] == 255) continue; - else if((boost::uint8_t)fpga_image[i] == 170 and - (boost::uint8_t)fpga_image[i+1] == 153){ + if(fpga_test_bytes[i] == 255) continue; + else if(fpga_test_bytes[i] == 170 and + fpga_test_bytes[i+1] == 153){ is_good = true; break; } } + if(not is_good) throw std::runtime_error("Not a valid FPGA image."); - if(!is_good) throw std::runtime_error("Not a valid FPGA image."); + //With image validated, read into utility + fpga_file.seekg(0, std::ios::beg); + fpga_file.read((char*)fpga_image,fpga_image_size); + fpga_file.close(); //Return image size return fpga_image_size; } -int grab_fw_image(std::string fw_path){ - - //Reading firmware image from file - std::ifstream to_read_fw((char*)fw_path.c_str(), std::ios::binary); - to_read_fw.seekg(0, std::ios::end); - fw_image_size = to_read_fw.tellg(); - to_read_fw.seekg(0, std::ios::beg); - char fw_read[FW_IMAGE_SIZE_BYTES]; - to_read_fw.read(fw_read,fw_image_size); - to_read_fw.close(); - for(int i = 0; i < fw_image_size; i++) fw_image[i] = (boost::uint8_t)fw_read[i]; +int read_fw_image(std::string& fw_path){ - //Checking validity of image + //Check size of given image + std::ifstream fw_file(fw_path.c_str(), std::ios::binary); + fw_file.seekg(0, std::ios::end); + int fw_image_size = fw_file.tellg(); if(fw_image_size > FW_IMAGE_SIZE_BYTES){ - throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") % fw_image_size % FW_IMAGE_SIZE_BYTES)); + throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") + % fw_image_size % FW_IMAGE_SIZE_BYTES)); } - //Check first four bytes of image - for(int i = 0; i < 4; i++) if((boost::uint8_t)fw_image[i] != 11) throw std::runtime_error("Not a valid firmware image."); + //Check sequence of bytes in image before reading + boost::uint8_t fw_test_bytes[4]; + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_test_bytes,4); + for(int i = 0; i < 4; i++) if(fw_test_bytes[i] != 11) throw std::runtime_error("Not a valid firmware image."); + + //With image validated, read into utility + fw_file.seekg(0, std::ios::beg); + fw_file.read((char*)fw_image,fw_image_size); + fw_file.close(); - //Return image size return fw_image_size; } -boost::uint32_t* get_flash_info(std::string ip_addr){ +boost::uint32_t* get_flash_info(std::string& ip_addr){ boost::uint32_t *flash_info = new boost::uint32_t[2]; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); usrp2_fw_update_data_t get_flash_info_pkt = usrp2_fw_update_data_t(); get_flash_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - get_flash_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL); + get_flash_info_pkt.id = htonx<boost::uint32_t>(GET_FLASH_INFO_CMD); udp_transport->send(boost::asio::buffer(&get_flash_info_pkt, sizeof(get_flash_info_pkt))); //Loop and receive until the timeout size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == GET_FLASH_INFO_ACK){ flash_info[0] = ntohl(update_data_in->data.flash_info_args.sector_size_bytes); flash_info[1] = ntohl(update_data_in->data.flash_info_args.memory_size_bytes); } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != GET_FLASH_INFO_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } - + return flash_info; } @@ -218,102 +331,100 @@ boost::uint32_t* get_flash_info(std::string ip_addr){ void erase_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint32_t memory_size){ + boost::uint32_t image_location_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t image_size = is_fw ? FW_IMAGE_SIZE_BYTES + : FPGA_IMAGE_SIZE_BYTES; + //Making sure this won't attempt to erase past end of device - if(is_fw){ - if(PROD_FW_IMAGE_LOCATION_ADDR+FW_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } - else{ - if(PROD_FPGA_IMAGE_LOCATION_ADDR+FPGA_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device."); - } + if((image_location_addr+image_size) > memory_size) throw std::runtime_error("Cannot erase past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t erase_pkt = usrp2_fw_update_data_t(); - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL); + erase_pkt.id = htonx<boost::uint32_t>(ERASE_FLASH_CMD); erase_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - if(is_fw){ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FW_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FW_IMAGE_SIZE_BYTES); - } - else{ - erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FPGA_IMAGE_LOCATION_ADDR); - erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FPGA_IMAGE_SIZE_BYTES); - } + erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(image_location_addr); + erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(image_size); //Begin erasing udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == ERASE_FLASH_ACK){ if(is_fw) std::cout << "Erasing firmware image." << std::endl; else std::cout << "Erasing FPGA image." << std::endl; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != ERASE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } //Check for erase completion - erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL); + erase_pkt.id = htonx<boost::uint32_t>(CHECK_ERASING_DONE_CMD); while(true){ udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG){ - if(is_fw) std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FW_IMAGE_SIZE_BYTES % PROD_FW_IMAGE_LOCATION_ADDR; - else std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FPGA_IMAGE_SIZE_BYTES % PROD_FPGA_IMAGE_LOCATION_ADDR; + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == DONE_ERASING_ACK){ + std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") + % image_size % image_location_addr; break; } - else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG){ - throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + else if(ntohl(update_data_in->id) != NOT_DONE_ERASING_ACK){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(update_data_in->id))); } } } void write_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Making sure this won't attempt to write past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot write past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t write_pkt = usrp2_fw_update_data_t(); - write_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL); + write_pkt.id = htonx<boost::uint32_t>(WRITE_FLASH_CMD); write_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); write_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Write image - if(is_fw) std::cout << "Writing firmware image." << std::endl; - else std::cout << "Writing FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rWriting " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + write_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); std::copy(image+(i*FLASH_DATA_PACKET_SIZE), image+((i+1)*FLASH_DATA_PACKET_SIZE), write_pkt.data.flash_args.data); udp_transport->send(boost::asio::buffer(&write_pkt, sizeof(write_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != WRITE_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } current_addr += FLASH_DATA_PACKET_SIZE; } + std::cout << std::flush << "\rWriting " << type << " image (100%)." << std::endl; std::cout << boost::format(" * Successfully wrote %d bytes.\n") % image_size; } void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){ int current_index = 0; - boost::uint32_t current_addr; - if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR; - else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t begin_addr = is_fw ? PROD_FW_IMAGE_LOCATION_ADDR + : PROD_FPGA_IMAGE_LOCATION_ADDR; + boost::uint32_t current_addr = begin_addr; + std::string type = is_fw ? "firmware" : "FPGA"; //Array size needs to be known at runtime, this constant is guaranteed to be larger than any firmware or FPGA image boost::uint8_t from_usrp[FPGA_IMAGE_SIZE_BYTES]; @@ -321,27 +432,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im //Making sure this won't attempt to read past end of device if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot read past end of device."); - //Setting up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; + //UDP receive buffer const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Setting up UDP packet usrp2_fw_update_data_t verify_pkt = usrp2_fw_update_data_t(); - verify_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL); + verify_pkt.id = htonx<boost::uint32_t>(READ_FLASH_CMD); verify_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); verify_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE); - //Verify image - if(is_fw) std::cout << "Verifying firmware image." << std::endl; - else std::cout << "Verifying FPGA image." << std::endl; - for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){ + //Print progress + std::cout << "\rVerifying " << type << " image (" + << int((double(current_addr-begin_addr)/double(image_size))*100) << "%)." << std::flush; + verify_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); udp_transport->send(boost::asio::buffer(&verify_pkt, sizeof(verify_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG){ - throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id))); + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != READ_FLASH_ACK){ + throw std::runtime_error(str(boost::format("Invalid reply %d from device.") + % ntohl(update_data_in->id))); } for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j]; @@ -350,27 +461,27 @@ void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* im } for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed."); + std::cout << std::flush << "\rVerifying " << type << " image (100%)." << std::endl; std::cout << " * Successful." << std::endl; } void reset_usrp(udp_simple::sptr udp_transport){ //Set up UDP transport - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); //Set up UDP packet usrp2_fw_update_data_t reset_pkt = usrp2_fw_update_data_t(); - reset_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL); + reset_pkt.id = htonx<boost::uint32_t>(RESET_USRP_CMD); reset_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); //Reset USRP udp_transport->send(boost::asio::buffer(&reset_pkt, sizeof(reset_pkt))); size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG){ + if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == RESET_USRP_ACK){ throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet } - else std::cout << "Resetting USRP." << std::endl; + else std::cout << std::endl << "Resetting USRP." << std::endl; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -386,125 +497,88 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("addr", po::value<std::string>(&ip_addr)->default_value("192.168.10.2"), "Specify an IP address.") ("fw", po::value<std::string>(&fw_path), "Specify a filepath for a custom firmware image.") ("fpga", po::value<std::string>(&fpga_path), "Specify a filepath for a custom FPGA image.") - ("no_fw", "Do not burn a firmware image.") - ("no_fpga", "Do not burn an FPGA image.") - ("auto_reboot", "Automatically reboot N2XX without prompting.") + ("no-fw", "Do not burn a firmware image.") + ("no_fw", "Do not burn a firmware image (DEPRECATED).") + ("no-fpga", "Do not burn an FPGA image.") + ("no_fpga", "Do not burn an FPGA image (DEPRECATED).") + ("auto-reboot", "Automatically reboot N2XX without prompting.") + ("auto_reboot", "Automatically reboot N2XX without prompting (DEPRECATED).") ("list", "List available N2XX USRP devices.") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); - //Apply options + //Print help message if(vm.count("help") > 0){ std::cout << boost::format("N2XX Simple Net Burner\n"); std::cout << boost::format("Automatically detects and burns standard firmware and FPGA images onto USRP N2XX devices.\n"); std::cout << boost::format("Can optionally take user input for custom images.\n\n"); std::cout << desc << std::endl; - return EXIT_FAILURE; + return EXIT_SUCCESS; } - bool burn_fpga = (vm.count("no_fpga") == 0); - bool burn_fw = (vm.count("no_fw") == 0); + //List option + if(vm.count("list")){ + list_usrps(); + return EXIT_SUCCESS; + } + + //Process user options + bool burn_fpga = (vm.count("no-fpga") == 0) and (vm.count("no_fpga") == 0); + bool burn_fw = (vm.count("no-fw") == 0) and (vm.count("no_fw") == 0); bool use_custom_fpga = (vm.count("fpga") > 0); bool use_custom_fw = (vm.count("fw") > 0); - bool list_usrps = (vm.count("list") > 0); - bool auto_reboot = (vm.count("auto_reboot") > 0); + bool auto_reboot = (vm.count("auto-reboot") > 0) or (vm.count("auto_reboot") > 0); + int fpga_image_size = 0; + int fw_image_size = 0; - if(!burn_fpga && !burn_fw){ + if(not burn_fpga && not burn_fw){ std::cout << "No images will be burned." << std::endl; return EXIT_FAILURE; } - if(!burn_fw && use_custom_fw) std::cout << boost::format("Conflicting firmware options presented. Will not burn a firmware image.\n\n"); - if(!burn_fpga && use_custom_fpga) std::cout << boost::format("Conflicting FPGA options presented. Will not burn an FPGA image.\n\n"); - - //Variables not from options - boost::uint32_t hw_rev; - bool found_it = false; - boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu]; - const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem); - - //List option - if(list_usrps){ - udp_simple::sptr udp_bc_transport; - usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t(); - usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_OHAI_LOL); - - std::cout << "Available USRP N2XX devices:" << std::endl; - - //Send UDP packets to all broadcast addresses - BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){ - //Avoid the loopback device - if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue; - udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_OHAI_OMG){ - usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt))); - - size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - } + //Print deprecation messages if necessary + if(vm.count("no_fpga") > 0) std::cout << "WARNING: --no_fpga option is deprecated! Use --no-fpga instead." << std::endl << std::endl; + if(vm.count("no_fw") > 0) std::cout << "WARNING: --no_fw option is deprecated! Use --no-fw instead." << std::endl << std::endl; + if(vm.count("auto_reboot") > 0) std::cout << "WARNING: --auto_reboot option is deprecated! Use --auto-reboot instead." << std::endl << std::endl; - std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev]; - } - - } - return EXIT_FAILURE; - } + //Find USRP and establish connection std::cout << boost::format("Searching for USRP N2XX with IP address %s.\n") % ip_addr; - - //Address specified udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT)); - usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t(); - hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION); - hw_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL); - udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt))); - - //Loop and receive until the timeout - size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT); - if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){ - hw_rev = ntohl(update_data_in->data.hw_rev); - if(filename_map.find(hw_rev) != filename_map.end()){ - std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev]; - found_it = true; - } - else throw std::runtime_error("Invalid revision found."); - } - if(!found_it) throw std::runtime_error("No USRP N2XX found."); - - //Determining default image filenames for validation - std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); - std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); - std::string default_fw_filepath = ""; - std::string default_fpga_filepath = ""; + boost::uint32_t hw_rev = find_usrp(udp_transport); //Check validity of file locations and binaries before attempting burn std::cout << "Searching for specified images." << std::endl << std::endl; if(burn_fpga){ - if(!use_custom_fpga) fpga_path = find_image_path(default_fpga_filename); - else{ - //Replace ~ with home directory - if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + if(use_custom_fpga){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fpga_file(filename_map[hw_rev], fpga_path); } + else{ + std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]); + fpga_path = find_image_path(default_fpga_filename); + } - grab_fpga_image(fpga_path); + fpga_image_size = read_fpga_image(fpga_path); } if(burn_fw){ - if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); - else{ - //Replace ~ with home directory - if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + if(use_custom_fw){ + //Expand tilde usage if applicable + #ifndef UHD_PLATFORM_WIN32 + if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + #endif validate_custom_fw_file(filename_map[hw_rev], fw_path); } + else{ + std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3)); + fw_path = find_image_path(default_fw_filename); + } - grab_fw_image(fw_path); + fw_image_size = read_fw_image(fw_path); } std::cout << "Will burn the following images:" << std::endl; @@ -547,7 +621,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << std::endl; //Formatting } if(reset) reset_usrp(udp_transport); - else return EXIT_SUCCESS; return EXIT_SUCCESS; } diff --git a/host/utils/usrp_simple_burner_utils.hpp b/host/utils/usrp_simple_burner_utils.hpp deleted file mode 100644 index f386c3620..000000000 --- a/host/utils/usrp_simple_burner_utils.hpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright 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 <iostream> -#include <math.h> -#include <stdint.h> - -#include <boost/foreach.hpp> -#include <boost/asio.hpp> -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include <uhd/transport/if_addrs.hpp> -#include <uhd/transport/udp_simple.hpp> -#include <uhd/types/device_addr.hpp> -#include <uhd/utils/msg.hpp> - -#define UDP_FW_UPDATE_PORT 49154 -#define UDP_MAX_XFER_BYTES 1024 -#define UDP_TIMEOUT 3 -#define UDP_POLL_INTERVAL 0.10 //in seconds -#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6 -#define USRP2_UDP_UPDATE_PORT 49154 -#define FLASH_DATA_PACKET_SIZE 256 -#define FPGA_IMAGE_SIZE_BYTES 1572864 -#define FW_IMAGE_SIZE_BYTES 31744 -#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000 -#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000 -#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000 -#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000 - -using namespace uhd; -using namespace uhd::transport; -namespace asio = boost::asio; - -typedef enum { - USRP2_FW_UPDATE_ID_WAT = ' ', - - USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', - USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', - - USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', - USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', - - USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', - USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', - - USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', - USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', - USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', - - USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', - USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', - - USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', - USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', - - USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', - USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', - - USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL = 'v', - USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG = 'V', - - USRP2_FW_UPDATE_ID_KTHXBAI = '~' - -} usrp2_fw_update_id_t; - -typedef struct { - uint32_t proto_ver; - uint32_t id; - uint32_t seq; - union { - uint32_t ip_addr; - uint32_t hw_rev; - struct { - uint32_t flash_addr; - uint32_t length; - uint8_t data[256]; - } flash_args; - struct { - uint32_t sector_size_bytes; - uint32_t memory_size_bytes; - } flash_info_args; - } data; -} usrp2_fw_update_data_t; |