diff options
168 files changed, 12326 insertions, 1346 deletions
diff --git a/.gitmodules b/.gitmodules index 473a21289..c85c089b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@  [submodule "fpga-src"]  	path = fpga-src  	url = https://github.com/EttusResearch/fpga.git -	branch = maint +	branch = master diff --git a/firmware/e300/battery/bq2419x.c b/firmware/e300/battery/bq2419x.c index 11b393622..0545fcf56 100644 --- a/firmware/e300/battery/bq2419x.c +++ b/firmware/e300/battery/bq2419x.c @@ -571,9 +571,9 @@ int8_t bq2419x_init(void)  	ir_comp &= ~BQ2419X_BAT_COMP_MASK;  	ir_comp |= (0x02 << BQ2419X_BAT_COMP_SHIFT); -	/* set thermal regulation to 60 C */ +	/* set thermal regulation to 120 C */  	ir_comp &= ~BQ2419X_TREG_MASK; -	ir_comp |= (0x00 << BQ2419X_TREG_SHIFT); +	ir_comp |= (0x03 << BQ2419X_TREG_SHIFT);  	ret = bq2419x_write(BQ2419X_REG_THERMAL_REG_CTRL, ir_comp);  	if (ret) diff --git a/firmware/e300/battery/mcu_settings.h b/firmware/e300/battery/mcu_settings.h index 175cb8f44..1472c183a 100644 --- a/firmware/e300/battery/mcu_settings.h +++ b/firmware/e300/battery/mcu_settings.h @@ -23,6 +23,6 @@  #define F_CPU 8000000UL  #define VERSION_MAJ 2 -#define VERSION_MIN 0 +#define VERSION_MIN 1  #endif /* MCU_SETTINGS_H */ diff --git a/firmware/e300/battery/pmu.c b/firmware/e300/battery/pmu.c index bd337783f..59ba171a3 100644 --- a/firmware/e300/battery/pmu.c +++ b/firmware/e300/battery/pmu.c @@ -58,11 +58,14 @@ static io_pin_t AVR_IRQ = IO_PD(5);  static io_pin_t PS_POR = IO_PD(6);  static io_pin_t PS_SRST = IO_PD(7);  static io_pin_t OVERTEMP = IO_PC(2); +static io_pin_t PANICn = IO_PC(1);  static uint16_t last_full_charge;  static uint16_t charge_on_last_unplug;  static bool battery_present_last; +static bool panic_last; +  static const uint8_t PMU_BLINK_ERROR_DELAY_MS = 250;  static const uint8_t PMU_BLINK_ERROR_TICKS_PER_BLINK = 10; @@ -224,6 +227,10 @@ int8_t pmu_init(void)  	state = OFF; +	/* make panic button an input */ +	io_input_pin(PANICn); +	panic_last = io_test_pin(PANICn); +  	/* make the LED outputs */  	io_output_pin(CHARGE);  	io_output_pin(POWER_LED); @@ -525,6 +532,7 @@ void pmu_handle_events(void)  	bool is_charging = false;  	bool is_full = false;  	bool overtemp = io_test_pin(OVERTEMP); +	bool panic = io_test_pin(PANICn);  	/* check if someone plugged the battery late,  	 * if so init gauge */ @@ -537,6 +545,10 @@ void pmu_handle_events(void)  	}  	battery_present_last = battery_present; +	if (panic != panic_last) +		pmu_power_down(); +	panic_last = panic; +  	if (overtemp) {  		fpga_set_gauge_status(BIT(6));  		pmu_error = PMU_ERROR_GLOBAL_TEMP; @@ -588,7 +600,6 @@ void pmu_handle_events(void)  				uint8_t health = pmu_battery_get_health(charger);  				switch (health) {  				case PMU_HEALTH_OVERHEAT: -					pmu_power_down();  					pmu_error = PMU_ERROR_CHARGER_TEMP;  					break;  				default: diff --git a/firmware/usrp3/CMakeLists.txt b/firmware/usrp3/CMakeLists.txt index f71b79b0c..66a43b6bd 100644 --- a/firmware/usrp3/CMakeLists.txt +++ b/firmware/usrp3/CMakeLists.txt @@ -25,6 +25,10 @@ SET(CMAKE_SYSTEM_NAME Generic)  CMAKE_FORCE_C_COMPILER(zpu-elf-gcc GNU)  PROJECT(USRP3_FW C) +SET(UHD_VERSION_HASH 0 CACHE INTEGER "UHD Version Hash") +EXECUTE_PROCESS(COMMAND ${CMAKE_SOURCE_DIR}/utils/git-hash.sh OUTPUT_VARIABLE UHD_VERSION_HASH) +ADD_DEFINITIONS(-DUHD_VERSION_HASH=0x${UHD_VERSION_HASH}) +  INCLUDE_DIRECTORIES(include)  find_package(PythonInterp) @@ -130,3 +134,4 @@ ENDMACRO(GEN_OUTPUTS)  ########################################################################  ADD_SUBDIRECTORY(lib)  ADD_SUBDIRECTORY(x300) +ADD_SUBDIRECTORY(n230) diff --git a/firmware/usrp3/include/cron.h b/firmware/usrp3/include/cron.h new file mode 100644 index 000000000..2d43d97f5 --- /dev/null +++ b/firmware/usrp3/include/cron.h @@ -0,0 +1,75 @@ +// +// 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_CRON_H +#define INCLUDED_CRON_H + +#include <stdint.h> +#include <stdbool.h> + +#define CRON_MAX_JOBS 4 + +typedef enum { +    SEC = 1, MILLISEC = 1000, MICROSEC = 1000000 +} cron_time_unit_t; + +typedef uint32_t (*cron_counter_fetcher_t)(); + +/*! + * \brief Initialize cron subsystem with a mechanism to fetch a counter and its frequency + */ +void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq); + +/*! + * \brief Get the hardware tick count + */ +uint32_t cron_get_ticks(); + +/*! + * \brief Get the time elapsed between start and stop in the specified units + */ +uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit); + +/*! + * \brief Sleep (spinloop) for about 'ticks' counter ticks + * Use only if simulating, _very_ short delay + */ +void sleep_ticks(uint32_t ticks); + +/*! + * \brief Sleep (spinloop) for about 'duration' microseconds + * Use only if simulating, _very_ short delay + */ +void sleep_us(uint32_t duration); + +/*! + * \brief Sleep (spinloop) for about 'duration' milliseconds + * Use only if simulating, _very_ short delay + */ +void sleep_ms(uint32_t duration); + +/*! + * \brief Initialize a unique cron job with 'job_id' and interval 'interval_ms' + */ +void cron_job_init(uint32_t job_id, uint32_t interval_ms); + +/*! + * \brief Check if cron job with 'job_id' is due for execution + */ +bool cron_job_run_due(uint32_t job_id); + +#endif /* INCLUDED_CRON_H */ diff --git a/firmware/usrp3/include/flash/spi_flash.h b/firmware/usrp3/include/flash/spi_flash.h new file mode 100644 index 000000000..8ed73f648 --- /dev/null +++ b/firmware/usrp3/include/flash/spi_flash.h @@ -0,0 +1,92 @@ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * 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_SPI_FLASH +#define INCLUDED_SPI_FLASH + +#include <wb_spi.h> + +//Device class that encapsulates the geometry and control +//interface for the flash chip +typedef struct { +    uint32_t page_size;     //in bytes +    uint32_t sector_size;   //in bytes +    uint32_t num_sectors; +    const wb_spi_slave_t* bus; +} spi_flash_dev_t; + +//Low level device specific operations +typedef uint16_t (*spif_read_id_fn_t)(const spi_flash_dev_t* flash); +typedef void (*spif_read_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes); +typedef bool (*spif_erase_sector_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset); +typedef bool (*spif_erase_sector_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset); +typedef bool (*spif_erase_sector_busy_fn_t)(const spi_flash_dev_t* flash); +typedef bool (*spif_write_page_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes); +typedef bool (*spif_write_page_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes); +typedef bool (*spif_write_page_busy_fn_t)(const spi_flash_dev_t* flash); + +//Interface struct for all low level device operations +typedef struct { +    spif_read_id_fn_t               read_id; +    spif_read_fn_t                  read; +    spif_erase_sector_dispatch_fn_t erase_sector_dispatch; +    spif_erase_sector_commit_fn_t   erase_sector_commit; +    spif_erase_sector_busy_fn_t     erase_sector_busy; +    spif_write_page_dispatch_fn_t   write_page_dispatch; +    spif_write_page_commit_fn_t     write_page_commit; +    spif_write_page_busy_fn_t       write_page_busy; +} spi_flash_ops_t; + +typedef enum { +    IDLE, WRITE_IN_PROGRESS, ERASE_IN_PROGRESS +} spi_flash_state_t; + +//A session struct that encapsulates everything about the flash +//in a device agnostic way +typedef struct { +    const spi_flash_dev_t*  device; +    const spi_flash_ops_t*  ops; +    spi_flash_state_t       state; +    uint32_t                last_offset; +    uint16_t                id; +} spi_flash_session_t; + +/*! + * Initialize the spi_flash_session_t object + */ +void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops); + +/*! + * Read "num_bytes" from "offset" in the flash into the buffer "buf". + * This call will block until all data is available. + */ +void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes); + +/*! + * Erase sector at "offset" in the flash. + * This call will block until the erase is complete. + */ +bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset); + +/*! + * Write "num_bytes" from buffer "buf" at "offset" in the flash. + * This call will block until the write is complete. + */ +bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes); + + +#endif /* INCLUDED_SPI_FLASH */ diff --git a/firmware/usrp3/include/flash/spif_spsn_s25flxx.h b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h new file mode 100644 index 000000000..1e6eededf --- /dev/null +++ b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * 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_SPIF_SPSN_S25FLXX_H +#define INCLUDED_SPIF_SPSN_S25FLXX_H + +#include <flash/spi_flash.h> + +const spi_flash_ops_t* spif_spsn_s25flxx_operations(); + +uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash); + +void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes); + +bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset); + +bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset); + +bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes); + +bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes); + +bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash); + +#endif /* INCLUDED_SPIF_SPSN_S25FLXX_H */ diff --git a/firmware/usrp3/include/mdelay.h b/firmware/usrp3/include/mdelay.h deleted file mode 100644 index 226bbb3f7..000000000 --- a/firmware/usrp3/include/mdelay.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- c -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * - * 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_MDELAY_H -#define INCLUDED_MDELAY_H - -/*! - * \brief Delay about ms milliseconds - * - * If simulating, _very_ short delay - */ -void mdelay(int ms); - -#endif /* INCLUDED_MDELAY_H */ diff --git a/firmware/usrp3/include/trace.h b/firmware/usrp3/include/trace.h index 0daa231fe..015ae9049 100644 --- a/firmware/usrp3/include/trace.h +++ b/firmware/usrp3/include/trace.h @@ -31,6 +31,7 @@   * An alternate way of defining the level is the "TRACE_LEVEL"   * variable in cmake. (eg. -DTRACE_LEVEL=13).   */ +  //#define UHD_FW_TRACE_LEVEL 13  typedef enum diff --git a/firmware/usrp3/include/wb_soft_reg.h b/firmware/usrp3/include/wb_soft_reg.h new file mode 100644 index 000000000..cbfc311bb --- /dev/null +++ b/firmware/usrp3/include/wb_soft_reg.h @@ -0,0 +1,135 @@ +// +// 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_SOFT_REG_H +#define INCLUDED_SOFT_REG_H + +#include <stdint.h> +#include <stdbool.h> +#include <wb_utils.h> + +/* Keeps track of all metadata associated with a soft register. + * Use this struct when you need to manage a hardware register that needs + * to be accessed from different sections of software. If a register contains + * several unrelated bitfields, this object can be used to ensure coherency. + * It is recommended that the client hold this as a global/static object. + */ +typedef struct +{ +    uint32_t wr_addr; +    uint32_t rd_addr; +    uint32_t soft_copy; +} soft_reg_t; + +/* A register field is defined as a tuple of the mask and the shift. + * It can be used to make read-modify-write operations more convenient + * For efficiency reasons, it is recommended to always use a constant + * of this type because it will get optimized out by the compiler and + * will result in zero memory overhead + */ +typedef struct +{ +    uint8_t num_bits; +    uint8_t shift; +} soft_reg_field_t; + + +/*! + * Initialize the soft_reg_t struct as a read-write register. + * Params: + * - reg: Pointer to the soft_reg struct + * - wr_addr: The address used to flush the register to HW + * - rd_addr: The address used to read the register from HW + */ +static inline void initialize_readwrite_soft_reg(soft_reg_t* reg, uint32_t wr_addr, uint32_t rd_addr) +{ +    reg->wr_addr = wr_addr; +    reg->rd_addr = rd_addr; +    reg->soft_copy = 0; +} + +/*! + * Initialize the soft_reg_t struct as a write-only register. + * Params: + * - reg: Pointer to the soft_reg struct + * - addr: The address used to flush the register to HW + */ +static inline void initialize_writeonly_soft_reg(soft_reg_t* reg, uint32_t addr) +{ +    reg->wr_addr = addr; +    reg->rd_addr = 0; +    reg->soft_copy = 0; +} + +/*! + * Update specified field in the soft-copy with the arg value. + * Performs a read-modify-write operation so all other field are preserved. + * NOTE: This does not write the value to hardware. + */ +static inline void soft_reg_set(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value) +{ +    const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift; +    reg->soft_copy = (reg->soft_copy & ~mask) | ((field_value << field.shift) & mask); +} + +/*! + * Write the contents of the soft-copy to hardware. + */ +static inline void soft_reg_flush(const soft_reg_t* reg) +{ +    wb_poke32(reg->wr_addr, reg->soft_copy); +} + +/*! + * Shortcut for a set and a flush. + */ +static inline void soft_reg_write(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value) +{ +    soft_reg_set(reg, field, field_value); +    soft_reg_flush(reg); +} + +/*! + * Get the value of the specified field from the soft-copy. + * NOTE: This does not read anything from hardware. + */ +static inline uint32_t soft_reg_get(const soft_reg_t* reg, const soft_reg_field_t field) +{ +    const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift; +    return (reg->soft_copy & mask) >> field.shift; +} + +/*! + * Read the contents of the register from hardware and update the soft copy. + */ +static inline void soft_reg_refresh(soft_reg_t* reg) +{ +    if (reg->rd_addr) { +        reg->soft_copy = wb_peek32(reg->rd_addr); +    } +} + +/*! + * Shortcut for refresh and get + */ +static inline uint32_t soft_reg_read(soft_reg_t* reg, const soft_reg_field_t field) +{ +    soft_reg_refresh(reg); +    return soft_reg_get(reg, field); +} + +#endif /* INCLUDED_SOFT_REG_H */ diff --git a/firmware/usrp3/include/wb_spi.h b/firmware/usrp3/include/wb_spi.h new file mode 100644 index 000000000..ebbb20b16 --- /dev/null +++ b/firmware/usrp3/include/wb_spi.h @@ -0,0 +1,55 @@ + +// Copyright 2012 Ettus Research LLC + +#ifndef INCLUDED_WB_SPI_H +#define INCLUDED_WB_SPI_H + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +typedef enum { +    WRITE, WRITE_READ +} wb_spi_rw_mode_t; + +typedef enum { +    RISING, FALLING +} wb_spi_edge_t; + +typedef struct { +    void*           base; +    uint32_t        slave_sel; +    uint32_t        clk_div; +    wb_spi_edge_t   mosi_edge; +    wb_spi_edge_t   miso_edge; +    bool            lsb_first; +} wb_spi_slave_t; + +/*! + * \brief Initialize SPI slave device + */ +void wb_spi_init(const wb_spi_slave_t* slave); + +/*! + * \brief Perform a SPI transaction in auto chip-select mode. + */ +inline void wb_spi_transact(const wb_spi_slave_t* slave, +    wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length); + +/*! + * \brief Perform a SPI transaction in manual chip-select mode. + */ +inline void wb_spi_transact_man_ss(const wb_spi_slave_t* slave, +    wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length); + +/*! + * \brief Select SPI slave + */ +void wb_spi_slave_select(const wb_spi_slave_t* slave); + +/*! + * \brief Deselect SPI slave + */ +void wb_spi_slave_deselect(const wb_spi_slave_t* slave); + +#endif /* INCLUDED_WB_SPI_H */ diff --git a/firmware/usrp3/lib/CMakeLists.txt b/firmware/usrp3/lib/CMakeLists.txt index 621b9b611..9d9ee3c6c 100644 --- a/firmware/usrp3/lib/CMakeLists.txt +++ b/firmware/usrp3/lib/CMakeLists.txt @@ -21,12 +21,16 @@ add_library(usrp3fw STATIC      udp_uart.c      wb_uart.c      wb_i2c.c +    wb_spi.c      printf.c      wb_pkt_iface64.c      u3_net_stack.c      ethernet.c -    mdelay.c      chinch.c      print_addrs.c      link_state_route_proto.c +    cron.c +    fw_comm_protocol.c +    flash/spi_flash.c +    flash/spif_spsn_s25flxx.c  ) diff --git a/firmware/usrp3/lib/cron.c b/firmware/usrp3/lib/cron.c new file mode 100644 index 000000000..24b8feb4e --- /dev/null +++ b/firmware/usrp3/lib/cron.c @@ -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 "cron.h" + +//Counter specific +static cron_counter_fetcher_t  cron_fetch_counter; +static uint32_t                cron_counter_freq; + +//Cron job specific +typedef struct { +    uint32_t tick_interval; +    uint32_t last_tick_count; +} cron_job_t; + +static cron_job_t  cron_job_table[CRON_MAX_JOBS]; + +void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq) +{ +    cron_fetch_counter = fetch_counter; +    cron_counter_freq = counter_freq; + +    for (int i = 0; i < CRON_MAX_JOBS; i++) { +        cron_job_table[i].tick_interval = 0; +    } +} + +uint32_t cron_get_ticks() +{ +    return cron_fetch_counter(); +} + +uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit) +{ +    return ((stop_ticks - start_ticks) / cron_counter_freq) * ((uint32_t)unit); +} + +void sleep_ticks(uint32_t ticks) +{ +    if (ticks == 0) return; //Handle the 0 delay case quickly + +    const uint32_t ticks_begin = cron_fetch_counter(); +    while(cron_fetch_counter() - ticks_begin < ticks) { +      /*NOP: Spinloop*/ +    } +} + +void sleep_us(uint32_t duration) +{ +    sleep_ticks((duration * (cron_counter_freq/1000000))); +} + +void sleep_ms(uint32_t duration) +{ +    sleep_ticks((duration * (cron_counter_freq/1000))); +} + +void cron_job_init(uint32_t job_id, uint32_t interval_ms) +{ +    cron_job_table[job_id].tick_interval = (interval_ms * (cron_counter_freq/1000)); +    cron_job_table[job_id].last_tick_count = 0; +} + +bool cron_job_run_due(uint32_t job_id) +{ +    uint32_t new_tick_count = cron_fetch_counter(); +    bool run_job = (new_tick_count - cron_job_table[job_id].last_tick_count) >= +        cron_job_table[job_id].tick_interval; + +    if (run_job) { +        //If the job is due to run, update the tick count for the next run +        //The assumption here is that the caller will actually run their job +        //when the return value is true. If not, the caller just missed this +        //iteration and will have to option to run the job in the next pass through. +        cron_job_table[job_id].last_tick_count = new_tick_count; +    } +    return run_job; +} diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c index 91efbfe1d..e9c18528d 100644 --- a/firmware/usrp3/lib/ethernet.c +++ b/firmware/usrp3/lib/ethernet.c @@ -21,7 +21,7 @@  #endif  #include "../x300/x300_defs.h"  #include "ethernet.h" -#include "mdelay.h" +#include "cron.h"  #include <trace.h>  #include "wb_i2c.h"  #include "wb_utils.h" @@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)    int x;   // Delay read of SFPP    if (delay_ms) -    mdelay(delay_ms); +    sleep_ms(delay_ms);    // Read ID code from SFP    x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);    // I2C Error? @@ -312,10 +312,9 @@ static void xge_mac_init(const uint32_t base)  }  // base is pointer to XGE MAC on Wishbone. -static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg) +static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port)  {      int x; -    uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg;      // Read LASI Ctrl register to capture state.      //y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);      UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence."); @@ -323,8 +322,10 @@ static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)      x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);      x = x | (1 << 15);      write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x); +    uint32_t loopCount = 0;      while(x&(1<<15)) {          x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port); +        if( loopCount++ > 200 ) break; // usually succeeds after 22 or 23 polls      }  } diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c new file mode 100644 index 000000000..b4257c96f --- /dev/null +++ b/firmware/usrp3/lib/flash/spi_flash.c @@ -0,0 +1,50 @@ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * 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 <flash/spi_flash.h> + +void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops) +{ +    flash->device = device; +    flash->ops = ops; +    flash->state = IDLE; +    flash->last_offset = 0; +    flash->id = ops->read_id(device); +} + +void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes) +{ +    flash->ops->read(flash->device, offset, buf, num_bytes); +} + +bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset) +{ +    if (flash->ops->erase_sector_dispatch(flash->device, offset)) { +        return flash->ops->erase_sector_commit(flash->device, offset); +    } else { +        return false; +    } +} + +bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes) +{ +    if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) { +        return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes); +    } else { +        return false; +    } +} diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c new file mode 100644 index 000000000..244115b6f --- /dev/null +++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c @@ -0,0 +1,238 @@ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <wb_spi.h> +#include <flash/spif_spsn_s25flxx.h> +#include <cron.h> +#include <trace.h> +#include <string.h> //for memset, memcpy + +#define S25FLXX_CMD_WIDTH       8 +#define S25FLXX_ADDR_WIDTH      24 + +/* S25FLxx-specific commands */ +#define S25FLXX_CMD_READID      0x90    /* Read Manufacturer and Device Identification */ +#define S25FLXX_CMD_READSIG     0xAB    /* Read Electronic Signature (Will release from Deep PD) */ +#define S25FLXX_CMD_READ        0x03    /* Read Data Bytes */ +#define S25FLXX_CMD_FAST_READ   0x0B    /* Read Data Bytes at Higher Speed */ + +#define S25FLXX_CMD_WREN        0x06    /* Write Enable */ +#define S25FLXX_CMD_WRDI        0x04    /* Write Disable */ + +#define S25FLXX_CMD_PP          0x02    /* Page Program */ +#define S25FLXX_CMD_SE          0xD8    /* Sector Erase */ +#define S25FLXX_CMD_BE          0xC7    /* Bulk Erase */ +#define S25FLXX_CMD_DP          0xB9    /* Deep Power-down */ + +#define S25FLXX_CMD_RDSR        0x05    /* Read Status Register */ +#define S25FLXX_CMD_WRSR        0x01    /* Write Status Register */ + +#define S25FLXX_STATUS_WIP      0x01    /* Write in Progress */ +#define S25FLXX_STATUS_E_ERR    0x20    /* Erase Error Occured */ +#define S25FLXX_STATUS_P_ERR    0x40    /* Programming Error Occured */ + +#define S25FLXX_SECTOR_ERASE_TIME_MS    750     //Spec: 650ms +#define S25FLXX_PAGE_WRITE_TIME_MS      1       //Spec: 750us + +#define S25FLXX_SMALL_SECTORS_PER_LOGICAL   16      //16 4-kB physical sectors per logical sector +#define S25FLXX_LARGE_SECTOR_BASE           0x20000 //Large physical sectors start at logical sector 2 + +inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash) +{ +    uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF; +    wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */); +    return status; +} + +inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms) +{ +    uint32_t start_ticks = cron_get_ticks(); +    do { +        if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) { +            return true; +        } +    } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms); + +    return false;  // Timed out +} + +inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled) +{ +    uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI; +    wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH); +} + +const spi_flash_ops_t spif_spsn_s25flxx_ops = +{ +    .read_id = spif_spsn_s25flxx_read_id, +    .read = spif_spsn_s25flxx_read, +    .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch, +    .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit, +    .erase_sector_busy = spif_spsn_s25flxx_device_busy, +    .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch, +    .write_page_commit = spif_spsn_s25flxx_write_page_commit, +    .write_page_busy = spif_spsn_s25flxx_device_busy +}; + +const spi_flash_ops_t* spif_spsn_s25flxx_operations() +{ +    return &spif_spsn_s25flxx_ops; +} + +uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash) +{ +    wb_spi_slave_select(flash->bus); +    uint32_t command = S25FLXX_CMD_READID << 24; +    wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32); +    uint16_t id = 0; +    wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16); +    wb_spi_slave_deselect(flash->bus); +    return id; +} + +void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes) +{ +    //We explicitly control the slave select here, so that we can +    //do the entire read operation as a single transaction from +    //device's point of view. (The most our SPI peripheral can transfer +    //in a single shot is 16 bytes.) + +    //Do the 5 byte instruction tranfer: +    //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0) +    uint8_t read_cmd[5]; +    read_cmd[4] = S25FLXX_CMD_FAST_READ; +    *((uint32_t*)(read_cmd + 3)) = (offset << 8); + +    wb_spi_slave_select(flash->bus); +    wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8); + +    //Read up to 4 bytes at a time until done +    uint8_t data_sw[16], data[16]; +    size_t xact_size = 16; +    unsigned char *bytes = (unsigned char *) buf; +    for (size_t i = 0; i < num_bytes; i += 16) { +        if (xact_size > num_bytes - i) xact_size = num_bytes - i; +        wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8); +        for (size_t k = 0; k < 4; k++) {    //Fix word level significance +            ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k]; +        } +        for (size_t j = 0; j < xact_size; j++) { +            *bytes = data[j]; +            bytes++; +        } +    } +    wb_spi_slave_deselect(flash->bus); +} + +bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset) +{ +    //Sanity check sector size +    if (offset % flash->sector_size) { +        UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size."); +        return false; +    } + +    if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) { +        UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset); +        return false; +    } +    _spi_flash_set_write_enabled(flash, true); + +    //Send sector erase command +    uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF); +    wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32); + +    return true; +} + +bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset) +{ +    //Poll status until write done +    uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1; +    bool status = false; +    for (uint8_t i = 0; i < phy_sector_count && !status; i++) { +        status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS); +    } +    if (!status) { +        UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset); +    } +    _spi_flash_set_write_enabled(flash, false); +    return status; +} + +bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes) +{ +    if (num_bytes == 0 || num_bytes > flash->page_size) { +        UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size."); +        return false; +    } +    if (num_bytes > (flash->sector_size * flash->num_sectors)) { +        UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary."); +        return false; +    } + +    //Wait until ready and enable write enabled +    if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) { +        UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset); +        return false; +    } +    _spi_flash_set_write_enabled(flash, true); + +    //We explicitly control the slave select here, so that we can +    //do the entire read operation as a single transaction from +    //device's point of view. (The most our SPI peripheral can transfer +    //in a single shot is 16 bytes.) + +    //Do the 4 byte instruction tranfer: +    //PP_CMD, ADDR2, ADDR1, ADDR0 +    uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF); + +    wb_spi_slave_select(flash->bus); +    wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32); + +    //Write the page 16 bytes at a time. +    uint8_t bytes_sw[16]; +    uint8_t* bytes = (uint8_t*) buf; +    for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) { +        const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16; +        for (size_t k = 0; k < 4; k++) {    //Fix word level significance +            ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k]; +        } +        wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8); +        bytes += xact_size; +    } +    wb_spi_slave_deselect(flash->bus); + +    return true; +} + +bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes) +{ +    //Wait until write done +    if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) { +        UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time."); +        return false; +    } +    _spi_flash_set_write_enabled(flash, false); +    return true; +} + +bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash) +{ +    return (_spif_read_status(flash) & S25FLXX_STATUS_WIP); +} + diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c new file mode 100644 index 000000000..0cc931a76 --- /dev/null +++ b/firmware/usrp3/lib/fw_comm_protocol.c @@ -0,0 +1,105 @@ +// +// 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 "../../../host/lib/usrp/common/fw_comm_protocol.h" + +#include <trace.h> +#include <string.h> //memcmp + +bool process_fw_comm_protocol_pkt( +    const fw_comm_pkt_t* request, +    fw_comm_pkt_t* response, +    uint8_t product_id, +    uint32_t iface_id, +    poke32_func poke_callback, +    peek32_func peek_callback) +{ +    bool send_response = false; + +    uint16_t signature = request->id; +    uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id); +    uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id); +    if (signature == FW_COMM_PROTOCOL_SIGNATURE &&  //Verify protocol +        version   <= FW_COMM_PROTOCOL_VERSION &&    //Verify protocol version (older versions supported) +        product   == product_id)                    //Verify device +    { +        //Request is valid. Copy it into the reply. +        memcpy(response, request, sizeof(fw_comm_pkt_t)); + +        //Start assuming no error +        response->flags &= ~FW_COMM_FLAGS_ERROR_MASK; + +        //Otherwise, run the command set by the flags +        switch (request->flags & FW_COMM_FLAGS_CMD_MASK) { +            case FW_COMM_CMD_ECHO: { +                UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()"); +                response->data_words = 1; +                response->data[0] = iface_id; +            } break; + +            case FW_COMM_CMD_POKE32: { +                UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x", +                    request->addr,*(request->data)); +                poke_callback(request->addr, *(request->data)); +            } break; + +            case FW_COMM_CMD_PEEK32: { +                *(response->data) = peek_callback(request->addr); +                UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x", +                    request->addr,*(response->data)); +            } break; + +            case FW_COMM_CMD_BLOCK_POKE32: { +                if (request->data_words > FW_COMM_MAX_DATA_WORDS) { +                    response->flags |= FW_COMM_ERR_SIZE_ERROR; +                    response->data_words = FW_COMM_MAX_DATA_WORDS; +                } else { +                    response->data_words = request->data_words; +                } +                UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words); +                for (uint32_t i = 0; i < response->data_words; i++) { +                    poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]); +                } +            } break; + +            case FW_COMM_CMD_BLOCK_PEEK32: { +                if (request->data_words > FW_COMM_MAX_DATA_WORDS) { +                    response->flags |= FW_COMM_ERR_SIZE_ERROR; +                    response->data_words = FW_COMM_MAX_DATA_WORDS; +                } else { +                    response->data_words = request->data_words; +                } +                for (uint32_t i = 0; i < response->data_words; i++) { +                    response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t))); +                } +                UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words); +            } break; + +            default: { +                UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command."); +                response->flags |= FW_COMM_ERR_CMD_ERROR; +            } +        } + +        //Send a reply if ack requested +        send_response = (request->flags & FW_COMM_FLAGS_ACK); +    } else {    //Size, protocol, product check failed +        UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request."); +        send_response = false; +    } +    return send_response; +} diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c deleted file mode 100644 index 6d2742206..000000000 --- a/firmware/usrp3/lib/mdelay.c +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- c -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * Copyright 2009 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 "mdelay.h" -#include "wb_utils.h" -#include "printf.h" -#include <stdint.h> -//IJB FIXME. -#include "../x300/x300_defs.h" - -void mdelay(int ms){ -  for(int i = 0; i < ms; i++){ -    static const uint32_t num_ticks = CPU_CLOCK/1000; -    const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)); -    //    printf("DEBUG: Counter is %d\n",ticks_begin); -    while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) { -      /*NOP*/ -    } -  } -} diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c new file mode 100644 index 000000000..04904feea --- /dev/null +++ b/firmware/usrp3/lib/wb_spi.c @@ -0,0 +1,206 @@ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <wb_spi.h> +#include <trace.h> + +typedef struct { +  volatile uint32_t data0; +  volatile uint32_t data1; +  volatile uint32_t data2; +  volatile uint32_t data3; +  volatile uint32_t ctrl_status; +  volatile uint32_t clkdiv; +  volatile uint32_t slavesel; +} wb_spi_regs_t; + +#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base) + +// Masks for different parts of CTRL reg +#define WB_SPI_CTRL_AUTO_SS     (1 << 13) +#define WB_SPI_CTRL_IE          (1 << 12) +#define WB_SPI_CTRL_LSB         (1 << 11) +#define WB_SPI_CTRL_TXNEG       (1 << 10) +#define WB_SPI_CTRL_RXNEG       (1 << 9) +#define WB_SPI_CTRL_GO_BSY      (1 << 8) +#define WB_SPI_CTRL_LENGTH(x)   (x & 0x7F) + +static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave) +{ +    uint32_t flags = 0; +    //If the SPI slave samples on the rising edge then shift +    //data out on the falling edge. +    if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG; +    //If the SPI slave drives on the rising edge then shift +    //data in on the falling edge. +    if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG; +    if (slave->lsb_first)           flags |= WB_SPI_CTRL_LSB; +    return flags; +} + +static inline void _wait_for_xfer(const wb_spi_slave_t* slave) +{ +    while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) { +        /*NOP*/ +    } +} + +void wb_spi_init(const wb_spi_slave_t* slave) +{ +    WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div; +    WB_SPI_REGS(slave->base)->slavesel = 0; + +    //Do a dummy transaction with no slave selected to prime the engine +    uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave); +    WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY; +    _wait_for_xfer(slave); +} + +void _wb_spi_transact_buf( +    const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode, +    const void* mosi_buf, void* miso_buf, uint32_t length, +    bool auto_slave_sel) +{ +    if (length == 0) return; + +    //Wait for previous transaction to finish +    _wait_for_xfer(slave); + +    //Write SPI data register(s) +    if (mosi_buf) { +        uint8_t* mosi_bytes = (uint8_t*) mosi_buf; +        uint8_t bits_left = length; +        for (uint32_t reg_index = 0; reg_index < 4; reg_index++) { +            uint32_t word = 0; +            if (bits_left < 32) { +                if (bits_left <= 8) { +                    word = (uint32_t) mosi_bytes[0]; +                } else if (bits_left <= 16) { +                    word = (((uint32_t) mosi_bytes[1]) << 0) | +                           (((uint32_t) mosi_bytes[0]) << 8); +                } else if (bits_left <= 24) { +                    word = (((uint32_t) mosi_bytes[2]) << 0) | +                           (((uint32_t) mosi_bytes[1]) << 8) | +                           (((uint32_t) mosi_bytes[0]) << 16); +                } else { +                    word = *((uint32_t*) mosi_bytes); +                } +                bits_left = 0; +            } else { +                word = *((uint32_t*) mosi_bytes); +                mosi_bytes += 4; +                bits_left -= 32; +            } + +            switch (reg_index) { +                case 0: WB_SPI_REGS(slave->base)->data0 = word; break; +                case 1: WB_SPI_REGS(slave->base)->data1 = word; break; +                case 2: WB_SPI_REGS(slave->base)->data2 = word; break; +                case 3: WB_SPI_REGS(slave->base)->data3 = word; break; +            } + +            if (bits_left == 0) break; +        } +    } + +    //Compute flags for slave and write control register +    uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave); +    if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS; +    WB_SPI_REGS(slave->base)->ctrl_status = ctrl; + +    // Tell it which SPI slave device to access +    WB_SPI_REGS(slave->base)->slavesel    = slave->slave_sel; + +    //Go go go! +    WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY; + +    if (rw_mode == WRITE_READ) { +        //Wait for SPI read operation to complete +        _wait_for_xfer(slave); + +        if (miso_buf) { +            //Read SPI data registers +            uint8_t* miso_bytes = (uint8_t*) miso_buf; +            uint8_t bits_left = length; +            for (uint32_t reg_index = 0; reg_index < 4; reg_index++) { +                uint32_t word = 0; +                switch (reg_index) { +                    case 0: word = WB_SPI_REGS(slave->base)->data0; break; +                    case 1: word = WB_SPI_REGS(slave->base)->data1; break; +                    case 2: word = WB_SPI_REGS(slave->base)->data2; break; +                    case 3: word = WB_SPI_REGS(slave->base)->data3; break; +                } + +                if (bits_left < 32) { +                    if (bits_left <= 8) { +                        miso_bytes[0] = word & 0xFF; +                    } else if (bits_left <= 16) { +                        miso_bytes[1] = word & 0xFF; +                        miso_bytes[0] = (word >> 8) & 0xFF; +                    } else if (bits_left <= 24) { +                        miso_bytes[2] = word & 0xFF; +                        miso_bytes[1] = (word >> 8) & 0xFF; +                        miso_bytes[0] = (word >> 16) & 0xFF; +                    } else { +                        *((uint32_t*) miso_bytes) = word; +                    } +                    bits_left = 0; +                } else { +                    *((uint32_t*) miso_bytes) = word; +                    miso_bytes += 4; +                    bits_left -= 32; +                } + +                if (bits_left == 0) break; +            } +        } +    } +} + +void wb_spi_transact( +    const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode, +    const void* mosi_buf, void* miso_buf, uint32_t length) +{ +    return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true); +} + +void wb_spi_transact_man_ss( +    const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode, +    const void* mosi_buf, void* miso_buf, uint32_t length) +{ +    return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false); +} + +void wb_spi_slave_select(const wb_spi_slave_t* slave) +{ +    //Wait for previous transactions to finish +    _wait_for_xfer(slave); +    //Disable auto slave select +    WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave); +    //Manually select slave +    WB_SPI_REGS(slave->base)->slavesel    = slave->slave_sel; +} + +void wb_spi_slave_deselect(const wb_spi_slave_t* slave) +{ +    //Wait for previous transactions to finish +    _wait_for_xfer(slave); +    //Disable auto slave select +    WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave); +    //Manually deselect slave +    WB_SPI_REGS(slave->base)->slavesel    = 0; +} diff --git a/firmware/usrp3/n230/CMakeLists.txt b/firmware/usrp3/n230/CMakeLists.txt new file mode 100644 index 000000000..6247477f0 --- /dev/null +++ b/firmware/usrp3/n230/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# Copyright 2010-2014,2016 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_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/../../host/lib/usrp/n230) + +list(APPEND n230_sources n230_eeprom.c n230_eth_handlers.c n230_init.c n230_main.c) + +######################################################################## +set(GEN_OUTPUTS_BIN_SIZE 0x7fff) + +add_executable(n230_main.elf ${n230_sources}) +target_link_libraries(n230_main.elf usrp3fw) +GEN_OUTPUTS(n230_main.elf n230) + +#INSTALL( +#    FILES ${CMAKE_CURRENT_BINARY_DIR}/n230_main.bin +#    DESTINATION share/uhd/images +#    RENAME usrp_n230_fw.bin +#) diff --git a/firmware/usrp3/n230/n230_burner.py b/firmware/usrp3/n230/n230_burner.py new file mode 100755 index 000000000..7b9920de7 --- /dev/null +++ b/firmware/usrp3/n230/n230_burner.py @@ -0,0 +1,359 @@ +#!/usr/bin/env python +# +# 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/>. +# + +import optparse +import math +import socket +import struct +import os.path +import sys +from array import array + +######################################################################## +# constants +######################################################################## +N230_FLASH_COMM_UDP_PORT          = 49154 +N230_FLASH_COMM_PAYLOAD_SIZE      = 128 +N230_FLASH_COMM_SECTOR_SIZE       = 65536 + +N230_FLASH_COMM_FLAGS_ACK         = 0x00000001 +N230_FLASH_COMM_FLAGS_CMD_MASK    = 0x00000FF0 +N230_FLASH_COMM_FLAGS_ERROR_MASK  = 0xFF000000 + +N230_FLASH_COMM_CMD_READ_NV_DATA  = 0x00000010 +N230_FLASH_COMM_CMD_WRITE_NV_DATA = 0x00000020 +N230_FLASH_COMM_CMD_READ_FPGA     = 0x00000030 +N230_FLASH_COMM_CMD_WRITE_FPGA    = 0x00000040 +N230_FLASH_COMM_CMD_ERASE_FPGA    = 0x00000050 + +N230_FLASH_COMM_ERR_PKT_ERROR     = 0x80000000 +N230_FLASH_COMM_ERR_CMD_ERROR     = 0x40000000 +N230_FLASH_COMM_ERR_SIZE_ERROR    = 0x20000000 + +N230_FLASH_COMM_SAFE_IMG_BASE     = 0x000000 +N230_FLASH_COMM_PROD_IMG_BASE     = 0x400000 +N230_FLASH_COMM_FPGA_IMG_MAX_SIZE = 0x400000 + +UDP_MAX_XFER_BYTES = 256 +UDP_TIMEOUT = 3 + +_seq = -1 +def next_seq(): +    global _seq +    _seq = _seq+1 +    return _seq + +def seq(): +    return _seq + +######################################################################## +# helper functions +######################################################################## + +short = struct.Struct('>H') +ulong = struct.Struct('>I') + +def unpack_flash_transaction(buf): +    (flags, seqno, offset, size) = struct.unpack_from('!LLLL', buf) +    check_error(flags) +    if (seqno != seq()): +        raise Exception("The flash transaction operation returned an incorrect sequence number") +    data = bytes() +    for i in xrange(16, len(buf), 1): +        data += buf[i] +    return (flags, offset, size, data) + +def pack_flash_transaction(flags, offset, size, data): +    buf = bytes() +    buf = struct.pack('!LLLL', flags, next_seq(), offset, size) +    for i in range(N230_FLASH_COMM_PAYLOAD_SIZE): +        if (i < size): +            buf += struct.pack('!B', data[i]) +        else: +            buf += struct.pack('!B', 0) +    return buf + +def check_error(flags): +    if flags & N230_FLASH_COMM_ERR_PKT_ERROR == N230_FLASH_COMM_ERR_PKT_ERROR: +        raise Exception("The flash transaction operation returned a packet error") +    if flags & N230_FLASH_COMM_ERR_CMD_ERROR == N230_FLASH_COMM_ERR_CMD_ERROR: +        raise Exception("The flash transaction operation returned a command error") +    if flags & N230_FLASH_COMM_ERR_SIZE_ERROR == N230_FLASH_COMM_ERR_SIZE_ERROR: +        raise Exception("The flash transaction operation returned a size error") + +def chunkify(stuff, n): +    return [stuff[i:i+n] for i in range(0, len(stuff), n)] + +def draw_progress_bar(percent, bar_len = 32): +    sys.stdout.write("\r") +    progress = "" +    for i in range(bar_len): +        if i < int((bar_len * percent) / 100): +            progress += "=" +        else: +            progress += "-" +    sys.stdout.write("[%s] %d%%" % (progress, percent)) +    sys.stdout.flush() + +######################################################################## +# Burner class, holds a socket and send/recv routines +######################################################################## +class ctrl_socket(object): +    def __init__(self, addr): +        self._safe_image = False +        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +        self._sock.settimeout(UDP_TIMEOUT) +        self._sock.connect((addr, N230_FLASH_COMM_UDP_PORT)) +        self.set_callbacks(lambda *a: None, lambda *a: None) + +    def set_safe_image(self, noprompt): +        confirm_msg = ('----------------------------------------------------------------------\n' +                       'WARNING!!! You are about to access the safe-image stored in the flash \n' +                       '----------------------------------------------------------------------\n' +                       'Writing a non-functional image will brick the device.\n' +                       'Are you sure you want to proceed?') +        if not noprompt: +            if raw_input("%s (y/N) " % confirm_msg).lower() == 'y': +                self._safe_image = True +            else: +                print 'Aborted by user' +                sys.exit(1) +        else: +            print '[WARNING] Operating on safe image without a prompt as requested' +            self._safe_image = True + +    def set_callbacks(self, progress_cb, status_cb): +        self._progress_cb = progress_cb +        self._status_cb = status_cb + +    def send_and_recv(self, pkt): +        self._sock.send(pkt) +        return self._sock.recv(UDP_MAX_XFER_BYTES) +     +    def compute_offset(self, offset): +        base = N230_FLASH_COMM_SAFE_IMG_BASE if (self._safe_image) else N230_FLASH_COMM_PROD_IMG_BASE +        return base + offset + +    def burn_fpga_to_flash(self, bitfile_path, noprompt): +        print '[BURN] Reading ' + bitfile_path + '...' +        with open(bitfile_path, 'rb') as bitfile: +            header = file_bytes = bitfile.read() +            if (self._safe_image != self.parse_bitfile_header(file_bytes)['safe-image']): +                confirm_msg = ('----------------------------------------------------------------------\n' +                               'WARNING!!! You are about to burn a safe image into a production slot  \n' +                               '           or a production image into a safe slot.                    \n' +                               '----------------------------------------------------------------------\n' +                               'This is dangerous and can cause the device to boot incorrectly.\n' +                               'Are you sure you want to proceed?') +                if not noprompt: +                    if raw_input("%s (y/N) " % confirm_msg).lower() != 'y': +                        print '[BURN] Aborted by user' +                        return +                else: +                    print '[WARNING] Burning image to the wrong slot without a prompt as requested' + +            print '[BURN] Writing to flash...' +            pkt_chunks = chunkify(file_bytes, N230_FLASH_COMM_PAYLOAD_SIZE) +            offset = 0 +            for pkt_data in pkt_chunks: +                pkt_data = array("B", pkt_data) +                size = N230_FLASH_COMM_PAYLOAD_SIZE if (len(pkt_data) >= N230_FLASH_COMM_PAYLOAD_SIZE) else len(pkt_data) +                #Erase sector +                if (offset % N230_FLASH_COMM_SECTOR_SIZE == 0): +                    flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA +                    out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data) +                    (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +                #Write data +                flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA +                out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data) +                (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +                #Increment +                offset += N230_FLASH_COMM_PAYLOAD_SIZE +                draw_progress_bar((((offset/N230_FLASH_COMM_PAYLOAD_SIZE)+1)*100)/len(pkt_chunks)) +            print('\n[BURN] DONE') + +    def parse_bitfile_header(self, header_bytes): +        xil_header = dict() +        n230_header = dict() +        n230_header['valid'] = False +        ptr = 0 +        #Field 1 +        if short.unpack(header_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(header_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0: +            #Headers +            ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 2 +            ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 1 +            #Fields a-d +            for keynum in range(0, 4): +                key = header_bytes[ptr] +                ptr += 1 +                val_len = short.unpack(header_bytes[ptr:ptr+2])[0] +                ptr += 2 +                val = header_bytes[ptr:ptr+val_len] +                ptr += val_len +                xil_header[key] = val +            #Field e +            ptr += 1 +            length = ulong.unpack(header_bytes[ptr:ptr+4])[0] +            xil_header['bl'] = length     #Bitstream length +            ptr += 4 +            xil_header['hl'] = ptr        #Header lengt + +            #Map Xilinx header field to N230 specific ones +            if xil_header and xil_header['a'].split(';')[0] == 'n230': +                n230_header['valid'] = True +                n230_header['user-id'] = int(xil_header['a'].split(';')[1].split('=')[1], 16) +                n230_header['safe-image'] = (n230_header['user-id'] >> 16 == 0x5AFE) +                n230_header['product'] = xil_header['b'] +                n230_header['timestamp'] = xil_header['c'] + ' ' + xil_header['d'] +                n230_header['filesize'] = xil_header['hl'] + xil_header['bl'] +        return n230_header + +    def read_bitfile_header_from_flash(self): +        max_header_size = 1024  #Should be enough +        header_bytes = bytes() +        for offset in range(0, max_header_size, N230_FLASH_COMM_PAYLOAD_SIZE): +            #Read data +            flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA +            out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) +            (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +            header_bytes += data +        return self.parse_bitfile_header(header_bytes) + +    def extract_fpga_from_flash(self, bitfile_path): +        header = self.read_bitfile_header_from_flash(); +        if not header['valid']: +            raise Exception("Could not detect a vaild Xilinx .bit burned into the flash") +        max_offset = header['filesize'] +        print '[EXTRACT] Writing ' + bitfile_path + '...' +        with open(bitfile_path, 'wb') as bitfile: +            for i in range(0, int(math.ceil(float(max_offset)/N230_FLASH_COMM_PAYLOAD_SIZE))): +                offset = i * N230_FLASH_COMM_PAYLOAD_SIZE +                size = N230_FLASH_COMM_PAYLOAD_SIZE if (max_offset - offset >= N230_FLASH_COMM_PAYLOAD_SIZE) else (max_offset - offset) +                #Read data +                flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA +                out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) +                (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +                bitfile.write(data[:size]) +                draw_progress_bar(((offset*100)/max_offset) + 1) +            print('\n[EXTRACT] DONE') + +    def erase_fpga_from_flash(self): +        print '[ERASE] Erasing image from flash...' +        for offset in range(0, N230_FLASH_COMM_FPGA_IMG_MAX_SIZE, N230_FLASH_COMM_SECTOR_SIZE): +            flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA +            out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE) +            (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +            draw_progress_bar(((offset+N230_FLASH_COMM_SECTOR_SIZE)*100)/N230_FLASH_COMM_FPGA_IMG_MAX_SIZE) +        print('\n[ERASE] DONE') + +    def wipe_user_data(self, noprompt): +        confirm_msg = ('-------------------------------------------------------------------\n' +                       'WARNING!!! You are about to erase all the user data from the flash \n' +                       '-------------------------------------------------------------------\n' +                       'This will cause the device to lose the following:\n' +                       ' * IP Address (Will default to 192.168.10.2)\n' +                       ' * Subnet Mask (Will default to 255.255.255.2)\n' +                       ' * MAC Address\n' +                       ' * Serial Number\n' +                       ' * Hardware Revision\n' +                       ' * ...and other identification info\n' +                       'Are you sure you want to proceed?') +        if not noprompt: +            if raw_input("%s (y/N) " % confirm_msg).lower() == 'y': +                wipe_ok = True +            else: +                print '[WIPE] Aborted by user' +                wipe_ok = False +        else: +            print '[WARNING] Wiping user data without prompt a as requested' +            wipe_ok = True + +        if wipe_ok: +            print '[WIPE] Erasing all user data from flash...' +            flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_NV_DATA +            out_pkt = pack_flash_transaction(flags, 0, N230_FLASH_COMM_PAYLOAD_SIZE, [0xFF]*N230_FLASH_COMM_PAYLOAD_SIZE) +            (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt)) +            print('[WIPE] DONE. Please power-cycle the device.') + +    def print_status(self): +        header = self.read_bitfile_header_from_flash(); +        if header['valid']: +            print('[STATUS] Detected a valid .bit header in the flash (Product = %s, Datestamp = %s%s)' % \ +                  (header['product'], header['timestamp'], ', Safe-Image' if header['safe-image'] else '')) +        else: +            print('[STATUS] No .bit header detected. Either the flash is uninitialized or the image is corrupt.') + + +######################################################################## +# command line options +######################################################################## +def get_options(): +    parser = optparse.OptionParser() +    parser.add_option("--addr", type="string",                 help="N230 device address", default='') +    parser.add_option("--status", action="store_true",         help="Print out the status of the burned image", default=False) +    parser.add_option("--erase", action="store_true",          help="Erase FPGA bitstream from flash", default=False) +    parser.add_option("--burn", type="string",                 help="Path to FPGA bitstream (.bit) to burn to flash", default=None) +    parser.add_option("--extract", type="string",              help="Destination bitfile to dump contents of the extracted image", default=None) +    parser.add_option("--safe_image", action="store_true",     help="Operate on the safe image. WARNING: This could be dangerous", default=False) +    parser.add_option("--wipe_user_data", action="store_true", help="Erase all user data like IP, MAC, S/N, etc from flash", default=False) +    parser.add_option("--no_prompt", action="store_true",      help="Suppress all warning prompts", default=False) +    (options, args) = parser.parse_args() +    return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() + +    if not options.addr: raise Exception('No address specified') + +    ctrl_sock = ctrl_socket(addr=options.addr) +     +    # Initialize safe image selector first +    if options.safe_image: +        ctrl_sock.set_safe_image(options.no_prompt) + +    if options.status: +        ctrl_sock.print_status() + +    # Order of operations: +    # 1. Extract (if specified) +    # 2. Erase (if specified) +    # 3. Burn (if specified) +     +    if options.extract is not None: +        file_path = options.extract +        ctrl_sock.print_status() +        ctrl_sock.extract_fpga_from_flash(file_path) + +    if options.erase: +        ctrl_sock.erase_fpga_from_flash() +        ctrl_sock.print_status() +     +    if options.burn is not None: +        file_path = options.burn +        extension = os.path.splitext(file_path)[1] +        if (extension.lower() == '.bit'): +            ctrl_sock.burn_fpga_to_flash(file_path, options.no_prompt) +            ctrl_sock.print_status() +        else: +            raise Exception("Unsupported FPGA bitfile format. You must use a .bit file.") + +    if options.wipe_user_data: +        ctrl_sock.wipe_user_data(options.no_prompt) diff --git a/firmware/usrp3/n230/n230_debug.py b/firmware/usrp3/n230/n230_debug.py new file mode 100755 index 000000000..f9ff64ab7 --- /dev/null +++ b/firmware/usrp3/n230/n230_debug.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python +# +# Copyright 2010-2011 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/>. +# + +import optparse +import math +import socket +import struct +import array +import os.path +import sys +import time +try: +    import fcntl +    N230_DEVICE_DISCOVERY_AVAILABLE = True +except: +    N230_DEVICE_DISCOVERY_AVAILABLE = False + +######################################################################## +# constants +######################################################################## +N230_FW_COMMS_UDP_PORT          = 49152 +N230_FW_COMMS_MAX_DATA_WORDS    = 16 + +N230_FW_COMMS_FLAGS_ACK         = 0x00000001 +N230_FW_COMMS_FLAGS_ERROR_MASK  = 0xF0000000 +N230_FW_COMMS_FLAGS_CMD_MASK    = 0x000000F0 + +N230_FW_COMMS_CMD_ECHO          = 0x00000000 +N230_FW_COMMS_CMD_POKE32        = 0x00000010 +N230_FW_COMMS_CMD_PEEK32        = 0x00000020 +N230_FW_COMMS_CMD_BLOCK_POKE32  = 0x00000030 +N230_FW_COMMS_CMD_BLOCK_PEEK32  = 0x00000040 + +N230_FW_COMMS_ERR_PKT_ERROR     = 0x80000000 +N230_FW_COMMS_ERR_CMD_ERROR     = 0x40000000 +N230_FW_COMMS_ERR_SIZE_ERROR    = 0x20000000 + +N230_FW_COMMS_ID                = 0x0001ACE3 + +N230_FW_LOADER_ADDR             = 0xfa00 +N230_FW_LOADER_DATA             = 0xfa04 +N230_FW_LOADER_NUM_WORDS        = 8192 +N230_FW_LOADER_BOOT_DONE_ADDR   = 0xA004 +N230_FW_LOADER_BOOT_TIMEOUT     = 5 + +N230_JESD204_TEST               = 0xA014 +N230_FPGA_HASH_ADDR             = 0xA010 +N230_FW_HASH_ADDR               = 0x10004 +N230_ICAP_ADDR                  = 0xF800 +#ICAP_DUMMY_WORD               = 0xFFFFFFFF +#ICAP_SYNC_WORD                = 0xAA995566 +#ICAP_TYPE1_NOP                = 0x20000000 +#ICAP_WRITE_WBSTAR             = 0x30020001 +#ICAP_WBSTAR_ADDR              = 0x00000000 +#ICAP_WRITE_CMD                = 0x30008001 +#ICAP_IPROG_CMD                = 0x0000000F +# Bit reversed values per Xilinx UG470 - Bits reversed within bytes. +ICAP_DUMMY_WORD               = 0xFFFFFFFF +ICAP_SYNC_WORD                = 0x5599AA66 +ICAP_TYPE1_NOP                = 0x04000000 +ICAP_WRITE_WBSTAR             = 0x0C400080 +ICAP_WBSTAR_ADDR              = 0x00000000 +ICAP_WRITE_CMD                = 0x0C000180 +ICAP_IPROG_CMD                = 0x000000F0 + + +UDP_MAX_XFER_BYTES  = 256 +UDP_TIMEOUT         = 3 +FPGA_LOAD_TIMEOUT   = 10 + +_seq = -1 +def seq(): +    global _seq +    _seq = _seq+1 +    return _seq + +######################################################################## +# helper functions +######################################################################## + +def pack_fw_command(flags, seq, num_words, addr, data_arr): +    if (num_words > N230_FW_COMMS_MAX_DATA_WORDS): +        raise Exception("Data size too large. Firmware supports a max 16 words per block." % (addr)) +    buf = bytes() +    buf = struct.pack('!IIIII', N230_FW_COMMS_ID, flags, seq, num_words, addr) +    for i in range(N230_FW_COMMS_MAX_DATA_WORDS): +        if (i < num_words): +            buf += struct.pack('!I', data_arr[i]) +        else: +            buf += struct.pack('!I', 0) +    return buf + +def unpack_fw_command(buf, fmt=None): +    (id, flags, seq, num_words, addr) = struct.unpack_from('!IIIII', buf) +    fw_check_error(flags) +    data = [] +    if fmt is None: +        fmt = 'I' +    for i in xrange(20, len(buf), 4): +        data.append(struct.unpack('!'+fmt, buf[i:i+4])[0]) +    return (flags, seq, num_words, addr, data) + +def fw_check_error(flags): +    if flags & N230_FW_COMMS_ERR_PKT_ERROR == N230_FW_COMMS_ERR_PKT_ERROR: +        raise Exception("The fiwmware operation returned a packet error") +    if flags & N230_FW_COMMS_ERR_CMD_ERROR == N230_FW_COMMS_ERR_CMD_ERROR: +        raise Exception("The fiwmware operation returned a command error") +    if flags & N230_FW_COMMS_ERR_SIZE_ERROR == N230_FW_COMMS_ERR_SIZE_ERROR: +        raise Exception("The fiwmware operation returned a size error") + +def chunkify(stuff, n): +    return [stuff[i:i+n] for i in range(0, len(stuff), n)] + +def draw_progress_bar(percent, bar_len = 32): +    sys.stdout.write("\r") +    progress = "" +    for i in range(bar_len): +        if i < int((bar_len * percent) / 100): +            progress += "=" +        else: +            progress += "-" +    sys.stdout.write("[%s] %d%%" % (progress, percent)) +    sys.stdout.flush() + +######################################################################## +# Discovery class +######################################################################## +class discovery_socket(object): +    def __init__(self): +        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) +        self._sock.settimeout(0.250) + +    def get_bcast_addrs(self): +        max_possible = 128  # arbitrary. raise if needed. +        num_bytes = max_possible * 32 +        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +        names = array.array('B', '\0' * num_bytes) +        outbytes = struct.unpack('iL', fcntl.ioctl( +            s.fileno(), +            0x8912,  # SIOCGIFCONF +            struct.pack('iL', num_bytes, names.buffer_info()[0]) +        ))[0] +        namestr = names.tostring() +        lst = [] +        for i in range(0, outbytes, 40): +            name = namestr[i:i+16].split('\0', 1)[0] +            ip   = map(ord, namestr[i+20:i+24]) +            mask = map(ord, fcntl.ioctl(s.fileno(), 0x891B, struct.pack('256s', name))[20:24]) +            bcast = [] +            for i in range(len(ip)): +                bcast.append((ip[i] | (~mask[i])) & 0xFF)  +            if (name != 'lo'): +                lst.append(str(bcast[0]) + '.' + str(bcast[1]) + '.' + str(bcast[2]) + '.' + str(bcast[3])) +        return lst + +    def discover(self): +        addrs = [] +        for bcast_addr in self.get_bcast_addrs(): +            out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 0, 0, [0]) +            self._sock.sendto(out_pkt, (bcast_addr, N230_FW_COMMS_UDP_PORT)) +            while 1: +                try: +                    (in_pkt, addr_pair) = self._sock.recvfrom(UDP_MAX_XFER_BYTES) +                    if len(in_pkt) < 20: +                        continue +                    (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) +                    addrs.append(addr_pair[0]) +                except socket.error: +                    break +        return addrs + + +######################################################################## +# Communications class, holds a socket and send/recv routine +######################################################################## +class ctrl_socket(object): +    def __init__(self, addr, port): +        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +        self._sock.settimeout(UDP_TIMEOUT) +        self._sock.connect((addr, port)) +        self.set_callbacks(lambda *a: None, lambda *a: None) +        self.peek(0)    #Dummy read + +    def set_callbacks(self, progress_cb, status_cb): +        self._progress_cb = progress_cb +        self._status_cb = status_cb + +    def send(self, pkt): +        self._sock.send(pkt) + +    def recv(self): +        return self._sock.recv(UDP_MAX_XFER_BYTES) + +    def send_and_recv(self, pkt): +        self.send(pkt) +        return self.recv() + +    def peek(self, peek_addr, fmt=None): +        out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, peek_addr, [0]) +        in_pkt = self.send_and_recv(out_pkt) +        (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt) +        return data[0] + +    def peek64(self, peek_addr, fmt=None): +        out_pkt = pack_fw_command(N230_FW_COMMS_CMD_BLOCK_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 2, peek_addr, [0,0]) +        in_pkt = self.send_and_recv(out_pkt) +        (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt) +        return (data[0] | (data[1] << 32))  + +    def poke(self, poke_addr, poke_data, ack=True): +        ack_flag = N230_FW_COMMS_FLAGS_ACK if ack else 0 +        out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32|ack_flag, seq(), 1, poke_addr, [poke_data]) +        self.send(out_pkt) +        if ack: +            in_pkt = self.recv() +            (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) + +    def live_load_firmware_bin(self, bin_path): +        raise Exception("live_load_firmware_bin not implemented yet!") + +    def live_load_firmware_coe(self, coe_path): +        with open(coe_path, 'r') as coe_file: +            print("Loading %s..." % coe_path) +            coe_lines = [line.strip(',;\n ') for line in coe_file] +            start_index = coe_lines.index("memory_initialization_vector=") + 1 +            coe_words = coe_lines[start_index:] +            if len(coe_words) != N230_FW_LOADER_NUM_WORDS: +                raise Exception("invalid COE file. Must contain 8192 words!") +            self.poke(N230_FW_LOADER_ADDR, 0)    #Load start address +            for i in range(0, len(coe_words)): +                self.poke(N230_FW_LOADER_DATA, int(coe_words[i],16), (i%10==0) and (i<len(coe_words)-1)) +                draw_progress_bar(((i+1)*100)/len(coe_words)) +            print("\nRebooting...") +            out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32, seq(), 1, N230_FW_LOADER_BOOT_DONE_ADDR, [1]) +            self._sock.send(out_pkt) +            self._sock.settimeout(1) +            out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0]) +            for i in range(N230_FW_LOADER_BOOT_TIMEOUT): +                try: +                    self._sock.send(out_pkt) +                    in_pkt = self._sock.recv(UDP_MAX_XFER_BYTES) +                    print("Firmware is alive!") +                    self._sock.settimeout(UDP_TIMEOUT) +                    return +                except socket.error: +                    pass +            print("Firmware boot FAILED!!!") +            self._sock.settimeout(UDP_TIMEOUT) + +    def read_hash(self): +        fpga_hash = self.peek(N230_FPGA_HASH_ADDR) +        fpga_status = "clean" if (fpga_hash & 0xf0000000 == 0x0) else "modified" +        fw_hash = self.peek(N230_FW_HASH_ADDR) +        fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "modified" +        print("FPGA Version     : %x (%s)" % (fpga_hash & 0xfffffff, fpga_status)) +        print("Firmware Version : %x (%s)" % (fw_hash & 0xfffffff, fw_status)) + +    def is_claimed(self): +        claimed = self.peek(0x10008) +        print("Claimed          : %s") % ('YES' if claimed else 'NO') + +    def reset_fpga(self): +        print("Reseting USRP...") +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_DUMMY_WORD) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_SYNC_WORD) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_WBSTAR) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WBSTAR_ADDR) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_CMD) +        ctrl_sock.poke(N230_ICAP_ADDR,ICAP_IPROG_CMD, False) +        print("Waiting for FPGA to load...") +        self._sock.settimeout(1) +        out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0]) +        for i in range(FPGA_LOAD_TIMEOUT): +            try: +                in_pkt = self.send_and_recv(out_pkt) +                (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) +                print("FPGA loaded successfully.") +                self._sock.settimeout(UDP_TIMEOUT) +                return +            except socket.error: +                pass +        print("FPGA load FAILED!!!") +        self._sock.settimeout(UDP_TIMEOUT) + +    def jesd204_test_connector(self): +        print("Testing JESD204 connectors. Molex cable #79576-2102 must be connected") +        ctrl_sock.poke(N230_JESD204_TEST,0x1) +        while True: +            jesd204_test_status = ctrl_sock.peek(N230_JESD204_TEST) +            if (jesd204_test_status & 0x10000 == 0x10000): +                break +        ctrl_sock.poke(N230_JESD204_TEST,0x0) +        if (jesd204_test_status & 0xFFFF != 0x0): +            print("JESD204 loopback test Failed!: Returned status is %4x" % (jesd204_test_status & 0xFFFF)) +        else: +            print("JESD204 loopback test Passed.") + +######################################################################## +# command line options +######################################################################## +def get_options(): +    parser = optparse.OptionParser() +    parser.add_option("--discover", action="store_true",help="Find all devices connected N230 devices", default=False) +    parser.add_option("--addr", type="string",      help="N230 device address", default='') +    parser.add_option("--peek", type="int",         help="Read from memory map", default=None) +    parser.add_option("--poke", type="int",         help="Write to memory map", default=None) +    parser.add_option("--data", type="int",         help="Data for poke", default=None) +    parser.add_option("--fw",   type="string",      help="Path to FW image to load", default=None) +    parser.add_option("--hash", action="store_true",help="Display FPGA git hash", default=False) +    parser.add_option("--reset", action="store_true",help="Reset and Reload USRP FPGA.", default=False) +    parser.add_option("--jesd204test", action="store_true",help="Test mini-SAS connectors with loopback cable..", default=False) + +    (options, args) = parser.parse_args() +    return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() + +    if options.discover: +        if N230_DEVICE_DISCOVERY_AVAILABLE: +            disc_sock = discovery_socket() +            for addr in disc_sock.discover(): +                print '==== FOUND ' + addr + ' ====' +                ctrl_sock = ctrl_socket(addr, N230_FW_COMMS_UDP_PORT) +                ctrl_sock.read_hash() +                ctrl_sock.is_claimed() +            sys.exit() +        else: +            raise Exception('Discovery is only supported on Linux.') + +    if not options.addr:  +        raise Exception('No address specified') + +    ctrl_sock = ctrl_socket(options.addr, N230_FW_COMMS_UDP_PORT) +    +    if options.fw is not None: +        file_path = options.fw +        extension = os.path.splitext(file_path)[1] +        if (extension.lower() == '.coe'): +            ctrl_sock.live_load_firmware_coe(file_path) +        elif (extension.lower() == '.bin'): +            ctrl_sock.live_load_firmware_bin(file_path) +        else: +            raise Exception("Unsupported firmware file format") + +    if options.hash: +        ctrl_sock.read_hash() + +    if options.peek is not None: +        addr = options.peek +        data = ctrl_sock.peek(addr) +        print("PEEK[0x%x (%d)] => 0x%x (%d)" % (addr,addr,data,data)) + +    if options.poke is not None and options.data is not None: +        addr = options.poke +        data = options.data +        ctrl_sock.poke(addr,data) +        print("POKE[0x%x (%d)] <= 0x%x (%d)" % (addr,addr,data,data)) + +    if options.reset: +        ctrl_sock.reset_fpga() + +    if options.jesd204test: +        ctrl_sock.jesd204_test_connector() diff --git a/firmware/usrp3/n230/n230_eeprom.c b/firmware/usrp3/n230/n230_eeprom.c new file mode 100644 index 000000000..8f756d41f --- /dev/null +++ b/firmware/usrp3/n230/n230_eeprom.c @@ -0,0 +1,196 @@ +// +// 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 "../../../host/lib/usrp/n230/n230_eeprom.h" + +#include <trace.h> +#include <stddef.h> +#include <flash/spi_flash.h> +#include <flash/spif_spsn_s25flxx.h> +#include <string.h> //memcpy + +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" + +static const wb_spi_slave_t flash_spi_slave = { +    .base      = (void*) 0xB000, +    .slave_sel = 0x0001, +    .clk_div   = 4, //80MHz/4 = 20MHz +    .mosi_edge = RISING, +    .miso_edge = FALLING, +    .lsb_first = false +}; + +static const spi_flash_dev_t spi_flash_device = { +    .page_size   = 256, +    .sector_size = 65536, +    .num_sectors = 254, +    .bus         = &flash_spi_slave +}; + +/*********************************************************************** + * Non-volatile device data + **********************************************************************/ +#define N230_FLASH_NV_DATA_OFFSET     0x800000 + +//Default values in case the EEPROM is not read, corrupt +const n230_eeprom_map_t default_eeprom = { +    .data_version_major = N230_EEPROM_VER_MAJOR, +    .data_version_minor = N230_EEPROM_VER_MINOR, +    .hw_revision = 0, +    .hw_product = 0x01, +    .gateway = N230_DEFAULT_GATEWAY, +    .eth_info = { +        {   //eth0 +            .mac_addr = N230_DEFAULT_ETH0_MAC, +            .subnet   = N230_DEFAULT_ETH0_MASK, +            .ip_addr  = N230_DEFAULT_ETH0_IP +        }, +        {   //eth1 +            .mac_addr = N230_DEFAULT_ETH1_MAC, +            .subnet   = N230_DEFAULT_ETH1_MASK, +            .ip_addr  = N230_DEFAULT_ETH1_IP +        } +    } +}; + +//EEPROM cache +static spi_flash_session_t flash_session = {.device = NULL}; +static n230_eeprom_map_t eeprom_cache; +static bool cache_dirty = true; + +bool read_n230_eeprom() +{ +    bool status = false; +    if (flash_session.device == NULL) { //Initialize flash session structure for the first time +        wb_spi_init(spi_flash_device.bus); +        spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations()); +    } +    spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); + +    //Verify data format +    status = (eeprom_cache.data_version_major == default_eeprom.data_version_major); +    //Sanity communication info +    if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF) +        eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr; +    if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF) +        eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr; +    if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF) +        eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet; +    if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF) +        eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet; + +    if (!status)  { +        UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map."); +        memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t)); +    } +    cache_dirty = !status; +    return status; +} + +bool write_n230_eeprom() +{ +    //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size +    //This function would need to be reimplemented if this assumption is no longer true +    if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) { +        UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size"); +        return false; +    } + +    bool status = true; +    if (cache_dirty) { +        n230_eeprom_map_t device_eeprom; +        spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t)); +        if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) { +            //Cache does not match read state. Write. +            UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash..."); +            status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET); +            if (status) { +                status = spif_write_page_sync( +                    &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t)); +            } +            if (!status) { +                UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!"); +            } +            cache_dirty = !status; +        } else { +            UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped."); +            //Cache matches read state. So mark as clean +            cache_dirty = false; +        } +    } +    return status; +} + +bool is_n230_eeprom_cache_dirty() +{ +    return cache_dirty; +} + +n230_eeprom_map_t* get_n230_eeprom_map() +{ +    cache_dirty = true; +    return &eeprom_cache; +} + +const n230_eeprom_map_t* get_n230_const_eeprom_map() +{ +    return &eeprom_cache; +} + +const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) { +    if (iface >= N230_NUM_ETH_PORTS) { +        UHD_FW_TRACE_FSTR(ERROR, +            "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!", +            iface, N230_NUM_ETH_PORTS); +    } +    return &(get_n230_const_eeprom_map()->eth_info[iface]); +} + + +/*********************************************************************** + * Storage for bootstrap FPGA Image + **********************************************************************/ +#define N230_FLASH_FPGA_IMAGE_OFFSET  0x000000 +#define N230_FLASH_FPGA_IMAGE_SIZE    0x400000 +#define N230_FLASH_NUM_FPGA_IMAGES    2 + +void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes) +{ +    if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { +        UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset); +    } +    spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); +} + +bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes) +{ +    if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { +        UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset); +        return false; +    } +    return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes); +} + +bool erase_n230_fpga_image_sector(uint32_t offset) +{ +    if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) { +        UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset); +        return false; +    } +    return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset); +} diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c new file mode 100644 index 000000000..b291bb39f --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.c @@ -0,0 +1,340 @@ +// +// 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 "n230_eth_handlers.h" + +#include <wb_utils.h> +#include <string.h> //memcmp +#include <u3_net_stack.h> +#include <print_addrs.h> +#include <trace.h> +#include "../../../host/lib/usrp/common/fw_comm_protocol.h" +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../n230/n230_fw_host_iface.h" +#include "../../../host/lib/usrp/n230/n230_eeprom.h" + +static n230_host_shared_mem_t* host_shared_mem_ptr; + +static const soft_reg_field_t LED_REG_FIELD_ETH_LINK2   = {.num_bits=1, .shift=0}; +static const soft_reg_field_t LED_REG_FIELD_ETH_LINK1   = {.num_bits=1, .shift=1}; +static const soft_reg_field_t LED_REG_FIELD_ETH_ACT2    = {.num_bits=1, .shift=2}; +static const soft_reg_field_t LED_REG_FIELD_ETH_ACT1    = {.num_bits=1, .shift=3}; + +/*********************************************************************** + * Handler for host <-> firmware communication + **********************************************************************/ + +static inline void n230_poke32(const uint32_t addr, const uint32_t data) +{ +    if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) { +        host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data; +    } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) { +        wb_poke32(addr, data); +    } +} + +static inline uint32_t n230_peek32(const uint32_t addr) +{ +    if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) { +        return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)]; +    } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) { +        return wb_peek32(addr); +    } else { +        return 0; +    } +} + +void n230_handle_udp_fw_comms( +    const uint8_t ethno, +    const struct ip_addr *src, const struct ip_addr *dst, +    const uint16_t src_port, const uint16_t dst_port, +    const void *buff, const size_t num_bytes) +{ +    if (buff == NULL) { +        UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR"); +        /* We got here from ICMP_DUR undeliverable packet */ +        /* Future space for hooks to tear down streaming radios etc */ +    } else if (num_bytes != sizeof(fw_comm_pkt_t)) { +        UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size)."); +    } else { +        const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff; +        fw_comm_pkt_t response; +        bool send_response = process_fw_comm_protocol_pkt( +            request, &response, +            N230_FW_PRODUCT_ID, +            (uint32_t)ethno, +            n230_poke32, n230_peek32); + +        if (send_response) { +            u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response)); +        } +    } +} + +void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr) +{ +    host_shared_mem_ptr = shared_mem_ptr; +    u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms); +} + + +/*********************************************************************** + * Handler for UDP framer program packets + **********************************************************************/ +void program_udp_framer( +    const uint8_t ethno, +    const uint32_t sid, +    const struct ip_addr *dst_ip, +    const uint16_t dst_port, +    const uint16_t src_port) +{ +    const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip); +    const size_t vdest = (sid >> 16) & 0xff; + +    uint32_t framer_base = +        ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE; + +    //setup source framer +    const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI), +        (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0)); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO), +        (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) | +        (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0)); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port); + +    //setup destination framer +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC), +        (((uint32_t)dst_port) << 16) | +        (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0)); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO), +        (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) | +        (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0)); +} + +void handle_udp_prog_framer( +    const uint8_t ethno, +    const struct ip_addr *src, const struct ip_addr *dst, +    const uint16_t src_port, const uint16_t dst_port, +    const void *buff, const size_t num_bytes) +{ +    if (buff == NULL) { +        /* We got here from ICMP_DUR undeliverable packet */ +        /* Future space for hooks to tear down streaming radios etc */ +    } else { +        const uint32_t sid = ((const uint32_t *)buff)[1]; +        program_udp_framer(ethno, sid, src, src_port, dst_port); +        UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d", +            ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port); +    } +} + +void n230_register_udp_prog_framer() +{ +    u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer); +} + + +/*********************************************************************** + * Handler for flash programming interface over UDP + **********************************************************************/ + +void n230_handle_flash_prog_comms( +    const uint8_t ethno, +    const struct ip_addr *src, const struct ip_addr *dst, +    const uint16_t src_port, const uint16_t dst_port, +    const void *buff, const size_t num_bytes) +{ +    if (buff == NULL) { +        UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR"); +        /* We got here from ICMP_DUR undeliverable packet */ +        /* Future space for hooks to tear down streaming radios etc */ +    } else if (num_bytes != sizeof(n230_flash_prog_t)) { +        UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size)."); +    } else { +        const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff; +        n230_flash_prog_t response; +        bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK; + +        //Request is valid. Copy it into the reply. +        memcpy(&response, request, sizeof(n230_flash_prog_t)); + +        switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) { +            case N230_FLASH_COMM_CMD_READ_NV_DATA: { +                UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()"); +                //Offset ignored because all non-volatile data fits in a packet. +                if (is_n230_eeprom_cache_dirty()) { +                    read_n230_eeprom(); +                } +                //EEPROM cache is up-to-date. Copy it into the packet. +                //Assumption: Cache size < 256. If this is no longer true, the offset field +                //will have to be used. +                memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t)); +                ack_requested = true; +            } break; + +            case N230_FLASH_COMM_CMD_WRITE_NV_DATA: { +                UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()"); +                //Offset ignored because all non-volatile data fits in a packet. +                memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t)); +                if (!write_n230_eeprom()) { +                    response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; +                } +            } break; + +            case N230_FLASH_COMM_CMD_READ_FPGA: { +                UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)", +                    request->offset, request->size); +                read_n230_fpga_image_page(request->offset, response.data, request->size); +                ack_requested = true; +            } break; + +            case N230_FLASH_COMM_CMD_WRITE_FPGA: { +                UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)", +                    request->offset, request->size); +                if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) { +                    response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; +                } +            } break; + +            case N230_FLASH_COMM_CMD_ERASE_FPGA: { +                UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)", +                    request->offset); +                if (!erase_n230_fpga_image_sector(request->offset)) { +                    response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR; +                } +            } break; + +            default :{ +                UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command."); +                response.flags |= FW_COMM_ERR_CMD_ERROR; +            } +        } +        //Send a reply if ack requested +        if (ack_requested) { +            u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response)); +        } +    } +} + +void n230_register_flash_comms_handler() +{ +    u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms); +} + +/*********************************************************************** + * Handler for SFP state changes + **********************************************************************/ +#define SFPP_STATUS_MODABS_CHG     (1 << 5)    // Has MODABS changed since last read? +#define SFPP_STATUS_TXFAULT_CHG    (1 << 4)    // Has TXFAULT changed since last read? +#define SFPP_STATUS_RXLOS_CHG      (1 << 3)    // Has RXLOS changed since last read? +#define SFPP_STATUS_MODABS         (1 << 2)    // MODABS state +#define SFPP_STATUS_TXFAULT        (1 << 1)    // TXFAULT state +#define SFPP_STATUS_RXLOS          (1 << 0)    // RXLOS state + +static bool     links_up[N230_MAX_NUM_ETH_PORTS] = {}; +static uint32_t packet_count[N230_MAX_NUM_ETH_PORTS] = {}; + +void n230_poll_sfp_status(const uint32_t eth, bool force, bool* state_updated) +{ +    // Has MODDET/MODAbS changed since we last looked? +    uint32_t rb = wb_peek32(SR_ADDR(WB_SBRB_BASE, (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1)); + +    if (rb & SFPP_STATUS_RXLOS_CHG) +        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (rb & SFPP_STATUS_RXLOS)); +    if (rb & SFPP_STATUS_TXFAULT_CHG) +        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((rb & SFPP_STATUS_TXFAULT) >> 1)); +    if (rb & SFPP_STATUS_MODABS_CHG) +        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((rb & SFPP_STATUS_MODABS) >> 2)); + +    //update the link up status +    if ((rb & SFPP_STATUS_RXLOS_CHG) || (rb & SFPP_STATUS_TXFAULT_CHG) || (rb & SFPP_STATUS_MODABS_CHG) || force) +    { +        const bool old_link_up = links_up[eth]; +        const uint32_t status_reg_addr = (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1; + +        uint32_t sfpp_status = wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) & 0xFFFF; +        if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) { +            int8_t timeout = 100; +            bool link_up = false; +            do { +                link_up = ((wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) >> 16) & 0x1) != 0; +            } while (!link_up && timeout-- > 0); + +            links_up[eth] = link_up; +        } else { +            links_up[eth] = false; +        } + +        if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth)); +        UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down"); +        if (rb & SFPP_STATUS_MODABS_CHG) { +            // MODDET has changed state since last checked +            if (rb & SFPP_STATUS_MODABS) { +                // MODDET is high, module currently removed. +                UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth); +            } else { +                // MODDET is low, module currently inserted. +                // Return status. +                UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth); +            } +        } +        *state_updated = true; +    } else { +        *state_updated = false; +    } +} + +void n230_update_link_act_state(soft_reg_t* led_reg) +{ +    static bool first_poll = 1; +    static uint32_t poll_cnt; + +    bool activity[N230_MAX_NUM_ETH_PORTS] = {}; +    for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { +        if (first_poll) { +            links_up[i] = 0; +            packet_count[i] = 0; +            poll_cnt = 0; +        } + +        //Check SFP status and update links_up +        bool link_state_from_sfp = false; +        n230_poll_sfp_status(i, first_poll, &link_state_from_sfp); + +        //Check packet counters less frequently to keep the LED on for a visible duration +        uint32_t cnt = wb_peek32(SR_ADDR(WB_SBRB_BASE, (i==0)?RB_ZPU_ETH0_PKT_CNT:RB_ZPU_ETH1_PKT_CNT)); +        activity[i] = (cnt != packet_count[i]); +        packet_count[i] = cnt; + +        //Update links_up if there is activity only if the SFP +        //handler has not updated it +        if (activity[i] && !link_state_from_sfp) links_up[i] = true; +    } + +    //TODO: Swap this when Ethernet port swap issues is fixed +    soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK2, links_up[0]?1:0); +    soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK1, links_up[1]?1:0); +    soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT2,  activity[0]?1:0); +    soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT1,  activity[1]?1:0); + +    first_poll = 0; +} + diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h new file mode 100644 index 000000000..67afbb246 --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.h @@ -0,0 +1,48 @@ +// +// 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_N230_ETH_HANDLERS_H +#define INCLUDED_N230_ETH_HANDLERS_H + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <lwip/ip_addr.h> +#include <wb_soft_reg.h> +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" + +/*! + * Registrar for host firmware communications handler. + */ +void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr); + +/*! + * Registrar for framer programmer handler. + */ +void n230_register_udp_prog_framer(); + +/*! + * Registrar for host firmware communications handler. + */ +void n230_register_flash_comms_handler(); + +/*! + * Handle SFP updates. + */ +void n230_update_link_act_state(soft_reg_t* led_reg); + +#endif /* INCLUDED_N230_ETH_HANDLERS_H */ diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c new file mode 100644 index 000000000..14f5ebd77 --- /dev/null +++ b/firmware/usrp3/n230/n230_init.c @@ -0,0 +1,125 @@ +// +// 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 <cron.h> +#include <printf.h> +#include <wb_utils.h> +#include <wb_uart.h> +#include <wb_i2c.h> +#include <wb_pkt_iface64.h> +#include <u3_net_stack.h> +#include <print_addrs.h> +#include <trace.h> +#include "../../../host/lib/usrp/n230/n230_eeprom.h" +#include "n230_init.h" +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" + +static wb_pkt_iface64_config_t pkt_config; + +static void putc(void *p, char c) +{ +//If FW_TRACE_LEVEL is defined, then the trace level is set +//to a non-zero number. Turn on the debug UART to enable tracing +#ifdef UHD_FW_TRACE_LEVEL +    wb_uart_putc(WB_DBG_UART_BASE, c); +#endif +} + +static uint32_t get_counter_val() +{ +    return wb_peek32(SR_ADDR(WB_SBRB_BASE, RB_ZPU_COUNTER)); +} + +void n230_init(void) +{ +    //TODO: We may need to remove the debug UART before we release. +    //Initialize the debug UART first. +    wb_uart_init(WB_DBG_UART_BASE, CPU_CLOCK_FREQ/DBG_UART_BAUD); +    init_printf(NULL, putc); + +    //Now we can init the rest with prints +    UHD_FW_TRACE_FSTR(INFO, "[ZPU Init Begin -- CPU CLOCK is %d MHz]", (CPU_CLOCK_FREQ/1000000)); + +    //Initialize cron and the per millisecond cron job +    UHD_FW_TRACE(INFO, "Initializing cron..."); +    cron_init(get_counter_val, CPU_CLOCK_FREQ); +    cron_job_init(PER_MILLISEC_CRON_JOBID, 1); +    cron_job_init(PER_SECOND_CRON_JOBID, 1000); + +    //Initialize rate for I2C cores +    UHD_FW_TRACE(INFO, "Initializing I2C..."); +    for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { +        wb_i2c_init((i==1)?WB_ETH1_I2C_BASE:WB_ETH0_I2C_BASE, CPU_CLOCK_FREQ); +    } + +    //Initialize eeprom +    read_n230_eeprom(); + +    UHD_FW_TRACE(INFO, "Initializing network stack..."); +    init_network_stack(); +} + +void init_network_stack(void) +{ +    //Hold Ethernet PHYs in reset +    wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_PHY); + +    //Initialize ethernet packet interface +    pkt_config = wb_pkt_iface64_init(WB_PKT_RAM_BASE, WB_PKT_RAM_CTRL_OFFSET); +    u3_net_stack_init(&pkt_config); + +    //Initialize MACs +    for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) { +        init_ethernet_mac(i); +    } + +    //Pull Ethernet PHYs out of reset +    wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_NONE); +} + +void init_ethernet_mac(uint32_t iface_num) +{ +    UHD_FW_TRACE_FSTR(INFO, "Initializing eth%d...", iface_num); + +    //Get interface info from the EEPROM (or defaults otherwise) +    const n230_eth_eeprom_map_t* eth_eeprom_map = get_n230_ethernet_info(iface_num); +    const eth_mac_addr_t *my_mac = (const eth_mac_addr_t *) &(eth_eeprom_map->mac_addr); +    const struct ip_addr *my_ip  = (const struct ip_addr *) &(eth_eeprom_map->ip_addr); +    const struct ip_addr *subnet = (const struct ip_addr *) &(eth_eeprom_map->subnet); + +    //Init software fields related to ethernet +    u3_net_stack_init_eth(iface_num, my_mac, my_ip, subnet); + +    uint32_t dispatcher_base = +        ((iface_num == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_DISPATCHER_BASE; + +    //Program dispatcher +    wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 0), +          (my_mac->addr[5] << 0) | (my_mac->addr[4] << 8) | (my_mac->addr[3] << 16) | (my_mac->addr[2] << 24)); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 1), (my_mac->addr[1] << 0) | (my_mac->addr[0] << 8)); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 2), my_ip->addr); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 4), 0/*nofwd*/); +    wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 5), (ICMP_IRQ << 8) | 0); //no fwd: type, code + +    //DEBUG: Print initialized info +    UHD_FW_TRACE_FSTR(INFO, "-- MAC%u:     %s", iface_num, mac_addr_to_str(u3_net_stack_get_mac_addr(iface_num))); +    UHD_FW_TRACE_FSTR(INFO, "-- IP%u:      %s", iface_num, ip_addr_to_str(u3_net_stack_get_ip_addr(iface_num))); +    UHD_FW_TRACE_FSTR(INFO, "-- SUBNET%u:  %s", iface_num, ip_addr_to_str(u3_net_stack_get_subnet(iface_num))); +    UHD_FW_TRACE_FSTR(INFO, "-- BCAST%u:   %s", iface_num, ip_addr_to_str(u3_net_stack_get_bcast(iface_num))); +} + + diff --git a/firmware/usrp3/n230/n230_init.h b/firmware/usrp3/n230/n230_init.h new file mode 100644 index 000000000..e2231909e --- /dev/null +++ b/firmware/usrp3/n230/n230_init.h @@ -0,0 +1,28 @@ +// +// 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_N230_INIT_H +#define INCLUDED_N230_INIT_H + +#include <stdint.h> +#include <stdbool.h> + +void n230_init(void); +void init_network_stack(void); +void init_ethernet_mac(uint32_t iface_num); + +#endif /* INCLUDED_B250_INIT_H */ diff --git a/firmware/usrp3/n230/n230_main.c b/firmware/usrp3/n230/n230_main.c new file mode 100644 index 000000000..a6c12e56d --- /dev/null +++ b/firmware/usrp3/n230/n230_main.c @@ -0,0 +1,113 @@ +// +// 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 <cron.h> +#include <wb_soft_reg.h> +#include <u3_net_stack.h> +#include <trace.h> +#include "../../../host/lib/usrp/n230/n230_fw_defs.h" +#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h" +#include "n230_eth_handlers.h" +#include "n230_init.h" + +//The version hash should come from a cmake build variable +//If it doesn't then the build system does not support the feature +//so just default to 0xFFFFFFFF +#ifndef UHD_VERSION_HASH +#define UHD_VERSION_HASH 0xFFFFFFFF +#endif + +//TODO: This is just for initial debugging. +static soft_reg_t g_led_register; + +//Shared memory +static n230_host_shared_mem_t g_host_shared_mem; + +//Functions +static void n230_handle_claim(); + +/*********************************************************************** + * Main loop runs all the handlers + **********************************************************************/ +int main(void) +{ +    //Initialize host shared mem +    g_host_shared_mem.data.fw_compat_num    = N230_FW_COMPAT_NUM; +    g_host_shared_mem.data.fw_version_hash  = UHD_VERSION_HASH; + +    //Main initialization function +    n230_init(); + +    //Initialize UDP Handlers +    n230_register_udp_fw_comms_handler(&g_host_shared_mem); +    n230_register_udp_prog_framer(); +    n230_register_flash_comms_handler(); + +    initialize_writeonly_soft_reg(&g_led_register, SR_ADDR(WB_SBRB_BASE, SR_ZPU_LEDS)); + +    uint32_t heart_beat = 0; +    while(true) +    { +        //TODO: This is just for initial debugging. Once the firmware +        //is somewhat stable we should delete this cron job +        if (cron_job_run_due(PER_SECOND_CRON_JOBID)) { +            //Everything in this block runs approx once per second +            if (heart_beat % 10 == 0) { +                UHD_FW_TRACE_FSTR(INFO, "0.1Hz Heartbeat (%u)", heart_beat); +            } +            heart_beat++; +        } + +        if (cron_job_run_due(PER_MILLISEC_CRON_JOBID)) { +            //Everything in this block runs approx once per millisecond +            n230_handle_claim(); +            n230_update_link_act_state(&g_led_register); +        } + +        //run the network stack - poll and handle +        u3_net_stack_handle_one(); +    } +    return 0; +} + +// Watchdog timer for claimer +static void n230_handle_claim() +{ +    static uint32_t last_time = 0; +    static size_t timeout = 0; + +    if (g_host_shared_mem.data.claim_time == 0) { +        //If time is 0 if the claim was forfeit +        g_host_shared_mem.data.claim_status = 0; +    } else if (last_time != g_host_shared_mem.data.claim_time) { +        //If the time changes, reset timeout +        g_host_shared_mem.data.claim_status = 1; +        timeout = 0; +    } else { +        //Otherwise increment for timeout +        timeout++; +    } + +    //Always stash the last seen time +    last_time = g_host_shared_mem.data.claim_time; + +    //Timeout logic +    if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) { +        g_host_shared_mem.data.claim_time = 0; +    } +} + diff --git a/firmware/usrp3/utils/git-hash.sh b/firmware/usrp3/utils/git-hash.sh new file mode 100755 index 000000000..ff7ae5ecb --- /dev/null +++ b/firmware/usrp3/utils/git-hash.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ $(command -v git) = "" ]]; then +    short_hash="FFFFFFFF" +else +    if (git diff --quiet); then +        #Clean +        short_hash="0$(git rev-parse --verify HEAD --short)" +    else +        #Dirty +        short_hash="F$(git rev-parse --verify HEAD --short)" +    fi +fi +echo ${short_hash^^}
\ No newline at end of file diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py index c9bcbb138..c518ba4e0 100755 --- a/firmware/usrp3/x300/x300_debug.py +++ b/firmware/usrp3/x300/x300_debug.py @@ -25,12 +25,18 @@ import struct  ########################################################################  # constants  ######################################################################## -B250_FW_COMMS_UDP_PORT = 49152 +X300_FW_COMMS_UDP_PORT = 49152 -B250_FW_COMMS_FLAGS_ACK = 1 -B250_FW_COMMS_FLAGS_ERROR = 2 -B250_FW_COMMS_FLAGS_POKE32 = 4 -B250_FW_COMMS_FLAGS_PEEK32 = 8 +X300_FW_COMMS_FLAGS_ACK         = 0x00000001 +X300_FW_COMMS_FLAGS_POKE32      = 0x00000010 +X300_FW_COMMS_FLAGS_PEEK32      = 0x00000020 + +X300_FW_COMMS_ERR_PKT_ERROR     = 0x80000000 +X300_FW_COMMS_ERR_CMD_ERROR     = 0x40000000 +X300_FW_COMMS_ERR_SIZE_ERROR    = 0x20000000 + +X300_FW_COMMS_ID = 0x0000ACE3 +X300_FW_COMMS_MAX_DATA_WORDS    = 16  #UDP_CTRL_PORT = 49183  UDP_MAX_XFER_BYTES = 1024 @@ -39,7 +45,7 @@ UDP_TIMEOUT = 3  #REG_ARGS_FMT = '!LLLLLB15x'  #REG_IP_FMT = '!LLLL20x' -REG_PEEK_POKE_FMT = '!LLLL' +REG_PEEK_POKE_FMT = '!LLLLL'  _seq = -1 @@ -52,12 +58,33 @@ def seq():  ########################################################################  # helper functions  ######################################################################## - -def unpack_reg_peek_poke_fmt(s): -    return struct.unpack(REG_PEEK_POKE_FMT,s) #(flags, seq, addr, data) +def fw_check_error(flags): +    if flags & X300_FW_COMMS_ERR_PKT_ERROR == X300_FW_COMMS_ERR_PKT_ERROR: +        raise Exception("The fiwmware operation returned a packet error") +    if flags & X300_FW_COMMS_ERR_CMD_ERROR == X300_FW_COMMS_ERR_CMD_ERROR: +        raise Exception("The fiwmware operation returned a command error") +    if flags & X300_FW_COMMS_ERR_SIZE_ERROR == X300_FW_COMMS_ERR_SIZE_ERROR: +        raise Exception("The fiwmware operation returned a size error")  def pack_reg_peek_poke_fmt(flags, seq, addr, data): -    return struct.pack(REG_PEEK_POKE_FMT, flags, seq, addr, data); +    num_words = 1 +    data_arr = [data] +    buf = bytes() +    buf = struct.pack('!LLLLL', X300_FW_COMMS_ID, flags, seq, num_words, addr) +    for i in range(X300_FW_COMMS_MAX_DATA_WORDS): +        if (i < num_words): +            buf += struct.pack('!L', data_arr[i]) +        else: +            buf += struct.pack('!L', 0) +    return buf + +def unpack_reg_peek_poke_fmt(buf): +    (id, flags, seq, num_words, addr) = struct.unpack_from('!LLLLL', buf) +    fw_check_error(flags) +    data = [] +    for i in xrange(20, len(buf), 4): +        data.append(struct.unpack('!L', buf[i:i+4])[0]) +    return (flags, seq, addr, data[0])  ########################################################################  # Burner class, holds a socket and send/recv routines @@ -66,7 +93,7 @@ class ctrl_socket(object):      def __init__(self, addr):          self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)          self._sock.settimeout(UDP_TIMEOUT) -        self._sock.connect((addr, B250_FW_COMMS_UDP_PORT)) +        self._sock.connect((addr, X300_FW_COMMS_UDP_PORT))          self.set_callbacks(lambda *a: None, lambda *a: None)          #self.init_update() #check that the device is there @@ -92,11 +119,10 @@ class ctrl_socket(object):          for in_prt in range (0, 8):              print("%s |" % ports[in_prt]),              for out_prt in range (0, 8): -                out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0) +                out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)                  in_pkt = self.send_and_recv(out_pkt)                  (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -                if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -                    raise Exception("B250 peek returns error code") +                fw_check_error(flags)                  print("%10d  " % (data)),              print          print @@ -105,19 +131,17 @@ class ctrl_socket(object):      def peek(self,peek_addr): -        out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0) +        out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)          in_pkt = self.send_and_recv(out_pkt)          (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -        if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -            raise Exception("B250 peek of address %d returns error code" % (addr)) +        fw_check_error(flags)          print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data))      def poke(self,poke_addr,poke_data): -        out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_POKE32|B250_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data) +        out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_POKE32|X300_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)          in_pkt = self.send_and_recv(out_pkt)          (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -        if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -            raise Exception("B250 peek of address %d returns error code" % (addr)) +        fw_check_error(flags)          print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data)  ) @@ -126,12 +150,12 @@ class ctrl_socket(object):  ########################################################################  def get_options():      parser = optparse.OptionParser() -    parser.add_option("--addr", type="string",                 help="USRP-N2XX device address",       default='') -    parser.add_option("--list", action="store_true",           help="list possible network devices", default=False) +    parser.add_option("--addr", type="string",              help="USRP-X300 device address",       default='') +    parser.add_option("--list", action="store_true",        help="list possible network devices", default=False)      parser.add_option("--peek", type="int",                 help="Read from memory map",     default=None)      parser.add_option("--poke", type="int",                 help="Write to memory map",     default=None)      parser.add_option("--data", type="int",                 help="Data for poke",     default=None) -    parser.add_option("--stats", action="store_true",           help="Display SuperMIMO Network Stats", default=False) +    parser.add_option("--stats", action="store_true",        help="Display SuperMIMO Network Stats", default=False)      (options, args) = parser.parse_args()      return options diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c index ef97412a2..97b20032b 100644 --- a/firmware/usrp3/x300/x300_init.c +++ b/firmware/usrp3/x300/x300_init.c @@ -1,7 +1,7 @@  #include "x300_init.h"  #include "x300_defs.h"  #include "ethernet.h" -#include "mdelay.h" +#include "cron.h"  #include <wb_utils.h>  #include <wb_uart.h>  #include <wb_i2c.h> @@ -121,6 +121,11 @@ static void putc(void *p, char c)  #endif  } +static uint32_t get_counter_val() +{ +    return wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)); +} +  void x300_init(void)  {      //first - uart @@ -136,6 +141,9 @@ void x300_init(void)      UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF));      UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000)); +    //Initialize cron +    cron_init(get_counter_val, CPU_CLOCK); +      //i2c rate init      wb_i2c_init(I2C0_BASE, CPU_CLOCK);      wb_i2c_init(I2C1_BASE, CPU_CLOCK); @@ -163,7 +171,7 @@ void x300_init(void)      }      // For eth interfaces, initialize the PHY's -    mdelay(100); +    sleep_ms(100);      ethernet_init(0);      ethernet_init(1);  } diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c index 3b812a2c4..44ed10aa8 100644 --- a/firmware/usrp3/x300/x300_main.c +++ b/firmware/usrp3/x300/x300_main.c @@ -6,7 +6,6 @@  #include "xge_phy.h"  #include "ethernet.h"  #include "chinch.h" -#include "mdelay.h"  #include <wb_utils.h>  #include <wb_uart.h> diff --git a/fpga-src b/fpga-src -Subproject b91465a5cef377d94e9d1a37c7ff2540b594ff6 +Subproject 9f2f5e95c6d7d3a17861bd876d68b7ac7e33bbb diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 6ac281ccc..f7a35fc8b 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -269,8 +269,8 @@ UHD_INSTALL(FILES  #{{{IMG_SECTION  # This section is written automatically by /images/create_imgs_package.py  # Any manual changes in here will be overwritten. -SET(UHD_IMAGES_MD5SUM "37081e1e73004bd9e74d7e5b6293fd39") -SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.009.002-release.zip") +SET(UHD_IMAGES_MD5SUM "cb53211ebf0420ea6b619f305c6ab2d6") +SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.010.git-118-g6c5d59cb.zip")  #}}}  ######################################################################## diff --git a/host/cmake/Modules/UHDBuildInfo.cmake b/host/cmake/Modules/UHDBuildInfo.cmake new file mode 100644 index 000000000..bc30b99ee --- /dev/null +++ b/host/cmake/Modules/UHDBuildInfo.cmake @@ -0,0 +1,60 @@ +# +# Copyright 2015 National Instruments Corp. +# +# 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/>. +# + +# +# We need this to be macro because GET_DIRECTORY_PROPERTY works with +# the current directory. +# +MACRO(UHD_LOAD_BUILD_INFO) +    MESSAGE(STATUS "") +    MESSAGE(STATUS "Loading build info.") + +    # Build date +    EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c +        "import time; print(time.strftime('%a, %d %b %Y %H:%M:%S', time.gmtime()))" +        OUTPUT_VARIABLE UHD_BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE +    ) + +    # Compiler name +    IF(MSVC) +        IF(MSVC10) +            SET(UHD_C_COMPILER "MSVC 2010") +            SET(UHD_CXX_COMPILER "MSVC 2010") +        ELSEIF(MSVC11) +            SET(UHD_C_COMPILER "MSVC 2012") +            SET(UHD_CXX_COMPILER "MSVC 2012") +        ELSEIF(MSVC12) +            SET(UHD_C_COMPILER "MSVC 2013") +            SET(UHD_CXX_COMPILER "MSVC 2013") +        ELSEIF(MSVC14) +            SET(UHD_C_COMPILER "MSVC 2015") +            SET(UHD_CXX_COMPILER "MSVC 2015") +        ELSE() +            # Go with the ugly string +            SET(UHD_C_COMPILER "MSVC ${CMAKE_C_COMPILER_VERSION}") +            SET(UHD_CXX_COMPILER "MSVC ${CMAKE_CXX_COMPILER_VERSION}") +        ENDIF(MSVC10) +    ELSE() +        SET(UHD_C_COMPILER "${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}") +        SET(UHD_CXX_COMPILER "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") +    ENDIF(MSVC) + +    # Compiler flags +    GET_DIRECTORY_PROPERTY(uhd_flags COMPILE_DEFINITIONS) +    SET(UHD_C_FLAGS "${uhd_flags}${CMAKE_C_FLAGS}") # CMAKE_C_FLAGS starts with a space +    SET(UHD_CXX_FLAGS "${uhd_flags}${CMAKE_CXX_FLAGS}") # CMAKE_CXX_FLAGS starts with a space +ENDMACRO(UHD_LOAD_BUILD_INFO) diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 58a9c7076..696817a96 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -27,9 +27,9 @@ FIND_PACKAGE(Git QUIET)  #  - set UHD_VERSION_DEVEL to true for master and development branches  ########################################################################  SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 009) -SET(UHD_VERSION_PATCH 002) -SET(UHD_VERSION_DEVEL FALSE) +SET(UHD_VERSION_MINOR 010) +SET(UHD_VERSION_PATCH git) +SET(UHD_VERSION_DEVEL TRUE)  ########################################################################  # Set up trimmed version numbers for DLL resource files and packages diff --git a/host/cmake/msvc/inttypes.h b/host/cmake/msvc/inttypes.h deleted file mode 100644 index 1c2baa82e..000000000 --- a/host/cmake/msvc/inttypes.h +++ /dev/null @@ -1,301 +0,0 @@ -// ISO C9x  compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124  -//  -//  Copyright (c) 2006 Alexander Chemeris -//  -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -//  -//   1. Redistributions of source code must retain the above copyright notice, -//      this list of conditions and the following disclaimer. -//  -//   2. Redistributions in binary form must reproduce the above copyright -//      notice, this list of conditions and the following disclaimer in the -//      documentation and/or other materials provided with the distribution. -//  -//   3. The name of the author may be used to endorse or promote products -//      derived from this software without specific prior written permission. -//  -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//  -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_INTTYPES_H_ // [ -#define _MSC_INTTYPES_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include <stdint.h> - -// 7.8 Format conversion of integer types - -typedef struct { -   intmax_t quot; -   intmax_t rem; -} imaxdiv_t; - -// 7.8.1 Macros for format specifiers - -// The fprintf macros for signed integers are: -#define PRId8       "d" -#define PRIi8       "i" -#define PRIdLEAST8  "d" -#define PRIiLEAST8  "i" -#define PRIdFAST8   "d" -#define PRIiFAST8   "i" - -#define PRId16       "hd" -#define PRIi16       "hi" -#define PRIdLEAST16  "hd" -#define PRIiLEAST16  "hi" -#define PRIdFAST16   "hd" -#define PRIiFAST16   "hi" - -#define PRId32       "I32d" -#define PRIi32       "I32i" -#define PRIdLEAST32  "I32d" -#define PRIiLEAST32  "I32i" -#define PRIdFAST32   "I32d" -#define PRIiFAST32   "I32i" - -#define PRId64       "I64d" -#define PRIi64       "I64i" -#define PRIdLEAST64  "I64d" -#define PRIiLEAST64  "I64i" -#define PRIdFAST64   "I64d" -#define PRIiFAST64   "I64i" - -#define PRIdMAX     "I64d" -#define PRIiMAX     "I64i" - -#define PRIdPTR     "Id" -#define PRIiPTR     "Ii" - -// The fprintf macros for unsigned integers are: -#define PRIo8       "o" -#define PRIu8       "u" -#define PRIx8       "x" -#define PRIX8       "X" -#define PRIoLEAST8  "o" -#define PRIuLEAST8  "u" -#define PRIxLEAST8  "x" -#define PRIXLEAST8  "X" -#define PRIoFAST8   "o" -#define PRIuFAST8   "u" -#define PRIxFAST8   "x" -#define PRIXFAST8   "X" - -#define PRIo16       "ho" -#define PRIu16       "hu" -#define PRIx16       "hx" -#define PRIX16       "hX" -#define PRIoLEAST16  "ho" -#define PRIuLEAST16  "hu" -#define PRIxLEAST16  "hx" -#define PRIXLEAST16  "hX" -#define PRIoFAST16   "ho" -#define PRIuFAST16   "hu" -#define PRIxFAST16   "hx" -#define PRIXFAST16   "hX" - -#define PRIo32       "I32o" -#define PRIu32       "I32u" -#define PRIx32       "I32x" -#define PRIX32       "I32X" -#define PRIoLEAST32  "I32o" -#define PRIuLEAST32  "I32u" -#define PRIxLEAST32  "I32x" -#define PRIXLEAST32  "I32X" -#define PRIoFAST32   "I32o" -#define PRIuFAST32   "I32u" -#define PRIxFAST32   "I32x" -#define PRIXFAST32   "I32X" - -#define PRIo64       "I64o" -#define PRIu64       "I64u" -#define PRIx64       "I64x" -#define PRIX64       "I64X" -#define PRIoLEAST64  "I64o" -#define PRIuLEAST64  "I64u" -#define PRIxLEAST64  "I64x" -#define PRIXLEAST64  "I64X" -#define PRIoFAST64   "I64o" -#define PRIuFAST64   "I64u" -#define PRIxFAST64   "I64x" -#define PRIXFAST64   "I64X" - -#define PRIoMAX     "I64o" -#define PRIuMAX     "I64u" -#define PRIxMAX     "I64x" -#define PRIXMAX     "I64X" - -#define PRIoPTR     "Io" -#define PRIuPTR     "Iu" -#define PRIxPTR     "Ix" -#define PRIXPTR     "IX" - -// The fscanf macros for signed integers are: -#define SCNd8       "d" -#define SCNi8       "i" -#define SCNdLEAST8  "d" -#define SCNiLEAST8  "i" -#define SCNdFAST8   "d" -#define SCNiFAST8   "i" - -#define SCNd16       "hd" -#define SCNi16       "hi" -#define SCNdLEAST16  "hd" -#define SCNiLEAST16  "hi" -#define SCNdFAST16   "hd" -#define SCNiFAST16   "hi" - -#define SCNd32       "ld" -#define SCNi32       "li" -#define SCNdLEAST32  "ld" -#define SCNiLEAST32  "li" -#define SCNdFAST32   "ld" -#define SCNiFAST32   "li" - -#define SCNd64       "I64d" -#define SCNi64       "I64i" -#define SCNdLEAST64  "I64d" -#define SCNiLEAST64  "I64i" -#define SCNdFAST64   "I64d" -#define SCNiFAST64   "I64i" - -#define SCNdMAX     "I64d" -#define SCNiMAX     "I64i" - -#ifdef _WIN64 // [ -#  define SCNdPTR     "I64d" -#  define SCNiPTR     "I64i" -#else  // _WIN64 ][ -#  define SCNdPTR     "ld" -#  define SCNiPTR     "li" -#endif  // _WIN64 ] - -// The fscanf macros for unsigned integers are: -#define SCNo8       "o" -#define SCNu8       "u" -#define SCNx8       "x" -#define SCNX8       "X" -#define SCNoLEAST8  "o" -#define SCNuLEAST8  "u" -#define SCNxLEAST8  "x" -#define SCNXLEAST8  "X" -#define SCNoFAST8   "o" -#define SCNuFAST8   "u" -#define SCNxFAST8   "x" -#define SCNXFAST8   "X" - -#define SCNo16       "ho" -#define SCNu16       "hu" -#define SCNx16       "hx" -#define SCNX16       "hX" -#define SCNoLEAST16  "ho" -#define SCNuLEAST16  "hu" -#define SCNxLEAST16  "hx" -#define SCNXLEAST16  "hX" -#define SCNoFAST16   "ho" -#define SCNuFAST16   "hu" -#define SCNxFAST16   "hx" -#define SCNXFAST16   "hX" - -#define SCNo32       "lo" -#define SCNu32       "lu" -#define SCNx32       "lx" -#define SCNX32       "lX" -#define SCNoLEAST32  "lo" -#define SCNuLEAST32  "lu" -#define SCNxLEAST32  "lx" -#define SCNXLEAST32  "lX" -#define SCNoFAST32   "lo" -#define SCNuFAST32   "lu" -#define SCNxFAST32   "lx" -#define SCNXFAST32   "lX" - -#define SCNo64       "I64o" -#define SCNu64       "I64u" -#define SCNx64       "I64x" -#define SCNX64       "I64X" -#define SCNoLEAST64  "I64o" -#define SCNuLEAST64  "I64u" -#define SCNxLEAST64  "I64x" -#define SCNXLEAST64  "I64X" -#define SCNoFAST64   "I64o" -#define SCNuFAST64   "I64u" -#define SCNxFAST64   "I64x" -#define SCNXFAST64   "I64X" - -#define SCNoMAX     "I64o" -#define SCNuMAX     "I64u" -#define SCNxMAX     "I64x" -#define SCNXMAX     "I64X" - -#ifdef _WIN64 // [ -#  define SCNoPTR     "I64o" -#  define SCNuPTR     "I64u" -#  define SCNxPTR     "I64x" -#  define SCNXPTR     "I64X" -#else  // _WIN64 ][ -#  define SCNoPTR     "lo" -#  define SCNuPTR     "lu" -#  define SCNxPTR     "lx" -#  define SCNXPTR     "lX" -#endif  // _WIN64 ] - -// 7.8.2 Functions for greatest-width integer types - -// 7.8.2.1 The imaxabs function -#define imaxabs _abs64 - -// 7.8.2.2 The imaxdiv function - -// This is modified version of div() function from Microsoft's div.c found -// in %MSVC.NET%\crt\src\div.c -#ifdef STATIC_IMAXDIV // [ -static -#else // STATIC_IMAXDIV ][ -_inline -#endif // STATIC_IMAXDIV ] -imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) -{ -   imaxdiv_t result; - -   result.quot = numer / denom; -   result.rem = numer % denom; - -   if (numer < 0 && result.rem > 0) { -      // did division wrong; must fix up -      ++result.quot; -      result.rem -= denom; -   } - -   return result; -} - -// 7.8.2.3 The strtoimax and strtoumax functions -#define strtoimax _strtoi64 -#define strtoumax _strtoui64 - -// 7.8.2.4 The wcstoimax and wcstoumax functions -#define wcstoimax _wcstoi64 -#define wcstoumax _wcstoui64 - - -#endif // _MSC_INTTYPES_H_ ] diff --git a/host/cmake/msvc/stdint.h b/host/cmake/msvc/stdint.h deleted file mode 100644 index 15333b467..000000000 --- a/host/cmake/msvc/stdint.h +++ /dev/null @@ -1,226 +0,0 @@ -// ISO C9x  compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124  -//  -//  Copyright (c) 2006 Alexander Chemeris -//  -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -//  -//   1. Redistributions of source code must retain the above copyright notice, -//      this list of conditions and the following disclaimer. -//  -//   2. Redistributions in binary form must reproduce the above copyright -//      notice, this list of conditions and the following disclaimer in the -//      documentation and/or other materials provided with the distribution. -//  -//   3. The name of the author may be used to endorse or promote products -//      derived from this software without specific prior written permission. -//  -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//  -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include <limits.h> - -// For Visual Studio 6 in C++ mode wrap <wchar.h> include with 'extern "C++" {}' -// or compiler give many errors like this: -//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#if (_MSC_VER < 1300) && defined(__cplusplus) -   extern "C++" { -#endif  -#     include <wchar.h> -#if (_MSC_VER < 1300) && defined(__cplusplus) -   } -#endif - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types -typedef __int8            int8_t; -typedef __int16           int16_t; -typedef __int32           int32_t; -typedef __int64           int64_t; -typedef unsigned __int8   uint8_t; -typedef unsigned __int16  uint16_t; -typedef unsigned __int32  uint32_t; -typedef unsigned __int64  uint64_t; - -// 7.18.1.2 Minimum-width integer types -typedef int8_t    int_least8_t; -typedef int16_t   int_least16_t; -typedef int32_t   int_least32_t; -typedef int64_t   int_least64_t; -typedef uint8_t   uint_least8_t; -typedef uint16_t  uint_least16_t; -typedef uint32_t  uint_least32_t; -typedef uint64_t  uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t    int_fast8_t; -typedef int16_t   int_fast16_t; -typedef int32_t   int_fast32_t; -typedef int64_t   int_fast64_t; -typedef uint8_t   uint_fast8_t; -typedef uint16_t  uint_fast16_t; -typedef uint32_t  uint_fast32_t; -typedef uint64_t  uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ -   typedef __int64           intptr_t; -   typedef unsigned __int64  uintptr_t; -#else // _WIN64 ][ -   typedef int               intptr_t; -   typedef unsigned int      uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t   intmax_t; -typedef uint64_t  uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN     ((int8_t)_I8_MIN) -#define INT8_MAX     _I8_MAX -#define INT16_MIN    ((int16_t)_I16_MIN) -#define INT16_MAX    _I16_MAX -#define INT32_MIN    ((int32_t)_I32_MIN) -#define INT32_MAX    _I32_MAX -#define INT64_MIN    ((int64_t)_I64_MIN) -#define INT64_MAX    _I64_MAX -#define UINT8_MAX    _UI8_MAX -#define UINT16_MAX   _UI16_MAX -#define UINT32_MAX   _UI32_MAX -#define UINT64_MAX   _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN    INT8_MIN -#define INT_LEAST8_MAX    INT8_MAX -#define INT_LEAST16_MIN   INT16_MIN -#define INT_LEAST16_MAX   INT16_MAX -#define INT_LEAST32_MIN   INT32_MIN -#define INT_LEAST32_MAX   INT32_MAX -#define INT_LEAST64_MIN   INT64_MIN -#define INT_LEAST64_MAX   INT64_MAX -#define UINT_LEAST8_MAX   UINT8_MAX -#define UINT_LEAST16_MAX  UINT16_MAX -#define UINT_LEAST32_MAX  UINT32_MAX -#define UINT_LEAST64_MAX  UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN    INT8_MIN -#define INT_FAST8_MAX    INT8_MAX -#define INT_FAST16_MIN   INT16_MIN -#define INT_FAST16_MAX   INT16_MAX -#define INT_FAST32_MIN   INT32_MIN -#define INT_FAST32_MAX   INT32_MAX -#define INT_FAST64_MIN   INT64_MIN -#define INT_FAST64_MAX   INT64_MAX -#define UINT_FAST8_MAX   UINT8_MAX -#define UINT_FAST16_MAX  UINT16_MAX -#define UINT_FAST32_MAX  UINT32_MAX -#define UINT_FAST64_MAX  UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -#  define INTPTR_MIN   INT64_MIN -#  define INTPTR_MAX   INT64_MAX -#  define UINTPTR_MAX  UINT64_MAX -#else // _WIN64 ][ -#  define INTPTR_MIN   INT32_MIN -#  define INTPTR_MAX   INT32_MAX -#  define UINTPTR_MAX  UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN   INT64_MIN -#define INTMAX_MAX   INT64_MAX -#define UINTMAX_MAX  UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -#  define PTRDIFF_MIN  _I64_MIN -#  define PTRDIFF_MAX  _I64_MAX -#else  // _WIN64 ][ -#  define PTRDIFF_MIN  _I32_MIN -#  define PTRDIFF_MAX  _I32_MAX -#endif  // _WIN64 ] - -#define SIG_ATOMIC_MIN  INT_MIN -#define SIG_ATOMIC_MAX  INT_MAX - -#ifndef SIZE_MAX // [ -#  ifdef _WIN64 // [ -#     define SIZE_MAX  _UI64_MAX -#  else // _WIN64 ][ -#     define SIZE_MAX  _UI32_MAX -#  endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> -#ifndef WCHAR_MIN // [ -#  define WCHAR_MIN  0 -#endif  // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -#  define WCHAR_MAX  _UI16_MAX -#endif  // WCHAR_MAX ] - -#define WINT_MIN  0 -#define WINT_MAX  _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val)  val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val)  val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#ifndef INTMAX_C -#define INTMAX_C   INT64_C -#endif -#ifndef UINTMAX_C -#define UINTMAX_C  UINT64_C -#endif - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 3090d3f80..ed462459c 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -63,7 +63,6 @@ ENDIF(ENABLE_MANUAL)  ########################################################################  # Setup API documentation (using Doxygen)  ######################################################################## -MESSAGE(STATUS "")  LIBUHD_REGISTER_COMPONENT("API/Doxygen" ENABLE_DOXYGEN ON "DOXYGEN_FOUND" OFF OFF)  OPTION(ENABLE_DOXYGEN_FULL "Use Doxygen to document the entire source tree (not just API)" OFF) @@ -90,7 +89,6 @@ ENDIF(ENABLE_DOXYGEN)  ########################################################################  # Run Doxygen (on code and/or manual, depending on CMake flags)  ######################################################################## -MESSAGE(STATUS "")  IF(ENABLE_MANUAL_OR_DOXYGEN)      #generate the doxygen configuration file      SET(CMAKE_CURRENT_BINARY_DIR_DOXYGEN ${CMAKE_CURRENT_BINARY_DIR}/doxygen) @@ -121,6 +119,7 @@ SET(man_page_sources      uhd_cal_rx_iq_balance.1      uhd_cal_tx_dc_offset.1      uhd_cal_tx_iq_balance.1 +    uhd_config_info.1      uhd_find_devices.1      uhd_image_loader.1      uhd_images_downloader.1 @@ -133,7 +132,6 @@ SET(man_page_sources  ########################################################################  # Setup man pages  ######################################################################## -MESSAGE(STATUS "")  FIND_PACKAGE(GZip)  # No elegant way in CMake to reverse a boolean diff --git a/host/docs/build.dox b/host/docs/build.dox index 05e4ff788..f6ff23b20 100644 --- a/host/docs/build.dox +++ b/host/docs/build.dox @@ -99,6 +99,10 @@ You can install all the dependencies through the package manager:      sudo yum -y install boost-devel libusb1-devel python-mako doxygen python-docutils cmake make gcc gcc-c++ +or + +    sudo dnf -y install boost-devel libusb1-devel python-mako doxygen python-docutils cmake make gcc gcc-c++ +  Your actual command may differ.  \section build_get_source Getting the source code diff --git a/host/docs/uhd_config_info.1 b/host/docs/uhd_config_info.1 new file mode 100644 index 000000000..edc1b7532 --- /dev/null +++ b/host/docs/uhd_config_info.1 @@ -0,0 +1,64 @@ +.TH "uhd_find_devices" 1 "3.9.1" UHD "User Commands" +.SH NAME +uhd_config_info \- USRP Hardware Driver Build Configuration Info +.SH DESCRIPTION +Print build information corresponding to this installation of the USRP +Hardware Driver (UHD). +.LP +The UHD package is the universal hardware driver for Ettus Research +products. The goal is to provide a host driver and API for +current and future Ettus Research products. Users will be able to use +the UHD driver standalone or with 3rd party applications. + +.SH SYNOPSIS +.B  uhd_config_info [OPTIONS] + +.SH OPTIONS +.IP "Print date this build was compiled:" +--boost-version +.IP "Print Boost version used:" +--build-date +.IP "Print C compiler used:" +--c-compiler +.IP "Print C++ compiler used:" +--cxx-compiler +.IP "Print C compile flags:" +--c-flags +.IP "Print C++ compile flags:" +--cxx-flags +.IP "Print UHD components included in this build (comma-delimited):" +--enabled-components +.IP "Print default install prefix for this build:" +--install-prefix +.IP "Print libusb version used" +--libusb-version +.IP "Print all information listed above:" +--print-all +.IP "Print UHD version:" +--version +.IP "This help information:" +--help + +.SH SEE ALSO +UHD documentation: +.B http://files.ettus.com/manual/ +.LP +GR-UHD documentation: +.B http://gnuradio.org/doc/doxygen/page_uhd.html + +.SH AUTHOR +This manual page was written by Nicholas Corgan +for the Debian project (but may be used by others). + +.SH COPYRIGHT +Copyright (c) 2015 National Instruments Corp. +.LP +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. +.LP +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. diff --git a/host/include/config.h.in b/host/include/config.h.in index bd690299e..83370f06f 100644 --- a/host/include/config.h.in +++ b/host/include/config.h.in @@ -21,4 +21,5 @@  #cmakedefine UHD_VERSION_MAJOR ${TRIMMED_VERSION_MAJOR}  #cmakedefine UHD_VERSION_MINOR ${TRIMMED_VERSION_MINOR}  #cmakedefine UHD_VERSION_PATCH ${TRIMMED_VERSION_PATCH} +#cmakedefine ENABLE_USB  #cmakedefine UHD_VERSION @UHD_VERSION_ADDED@ diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index 083ec4951..e55141549 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -27,6 +27,7 @@ CONFIGURE_FILE(  )  UHD_INSTALL(FILES +    build_info.hpp      config.hpp      convert.hpp      deprecated.hpp diff --git a/host/include/uhd/build_info.hpp b/host/include/uhd/build_info.hpp new file mode 100644 index 000000000..2e6571ad0 --- /dev/null +++ b/host/include/uhd/build_info.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2015 National Instruments Corp. +// +// 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_BUILD_INFO_HPP +#define INCLUDED_UHD_BUILD_INFO_HPP + +#include <uhd/config.hpp> + +#include <string> + +namespace uhd { namespace build_info { + +    //! Return the version of Boost this build was built with. +    UHD_API const std::string boost_version(); + +    //! Return the date and time (GMT) this UHD build was built. +    UHD_API const std::string build_date(); + +    //! Return the C compiler used for this build. +    UHD_API const std::string c_compiler(); + +    //! Return the C++ compiler used for this build. +    UHD_API const std::string cxx_compiler(); + +    //! Return the C flags passed into this build. +    UHD_API const std::string c_flags(); + +    //! Return the C++ flags passed into this build. +    UHD_API const std::string cxx_flags(); + +    //! Return the UHD components enabled for this build, comma-delimited. +    UHD_API const std::string enabled_components(); + +    //! Return the default CMake install prefix for this build. +    UHD_API const std::string install_prefix(); + +    //! Return the version of libusb this build was built with. +    UHD_API const std::string libusb_version(); +}} + +#endif /* INCLUDED_UHD_BUILD_INFO_HPP */ diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 8939cd773..ecd260675 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -53,6 +53,7 @@ typedef ptrdiff_t ssize_t;      #define UHD_EXPORT         __declspec(dllexport)      #define UHD_IMPORT         __declspec(dllimport)      #define UHD_INLINE         __forceinline +    #define UHD_FORCE_INLINE   __forceinline      #define UHD_DEPRECATED     __declspec(deprecated)      #define UHD_ALIGNED(x)     __declspec(align(x))      #define UHD_UNUSED(x)      x @@ -60,6 +61,7 @@ typedef ptrdiff_t ssize_t;      #define UHD_EXPORT         __declspec(dllexport)      #define UHD_IMPORT         __declspec(dllimport)      #define UHD_INLINE         inline +    #define UHD_FORCE_INLINE   inline      #define UHD_DEPRECATED     __declspec(deprecated)      #define UHD_ALIGNED(x)     __declspec(align(x))      #define UHD_UNUSED(x)      x __attribute__((unused)) @@ -67,6 +69,7 @@ typedef ptrdiff_t ssize_t;      #define UHD_EXPORT         __attribute__((visibility("default")))      #define UHD_IMPORT         __attribute__((visibility("default")))      #define UHD_INLINE         inline __attribute__((always_inline)) +    #define UHD_FORCE_INLINE   inline __attribute__((always_inline))      #define UHD_DEPRECATED     __attribute__((deprecated))      #define UHD_ALIGNED(x)     __attribute__((aligned(x)))      #define UHD_UNUSED(x)      x __attribute__((unused)) @@ -74,6 +77,7 @@ typedef ptrdiff_t ssize_t;      #define UHD_EXPORT      #define UHD_IMPORT      #define UHD_INLINE         inline +    #define UHD_FORCE_INLINE   inline      #define UHD_DEPRECATED      #define UHD_ALIGNED(x)      #define UHD_UNUSED(x)      x diff --git a/host/include/uhd/error.h b/host/include/uhd/error.h index f0ac41d1f..77216dc72 100644 --- a/host/include/uhd/error.h +++ b/host/include/uhd/error.h @@ -158,7 +158,7 @@ extern "C" {   * strings into a buffer that can be queried with this function. Functions that   * do take in UHD structs/handles will place their error strings in both locations.   */ -uhd_error uhd_get_last_error( +UHD_API uhd_error uhd_get_last_error(      char* error_out,      size_t strbuffer_len  ); diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index 3f34782e2..3af395eeb 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -33,6 +33,7 @@ UHD_INSTALL(FILES      sensors.hpp      serial.hpp      sid.hpp +    stdint.hpp      stream_cmd.hpp      time_spec.hpp      tune_request.hpp diff --git a/host/include/uhd/types/filters.hpp b/host/include/uhd/types/filters.hpp index 976ae233d..2c30c1007 100644 --- a/host/include/uhd/types/filters.hpp +++ b/host/include/uhd/types/filters.hpp @@ -55,12 +55,12 @@ namespace uhd{              //NOP          } -        inline virtual bool is_bypassed() +        UHD_INLINE virtual bool is_bypassed()          {              return _bypass;          } -        inline filter_type get_type() +        UHD_INLINE filter_type get_type()          {              return _type;          } @@ -98,7 +98,7 @@ namespace uhd{                  //NOP              } -            inline const std::string& get_analog_type() +            UHD_INLINE const std::string& get_analog_type()              {                  return _analog_type;              } @@ -128,17 +128,17 @@ namespace uhd{              //NOP          } -        inline double get_cutoff() +        UHD_INLINE double get_cutoff()          {              return _cutoff;          } -        inline double get_rolloff() +        UHD_INLINE double get_rolloff()          {              return _cutoff;          } -        inline void set_cutoff(const double cutoff) +        UHD_INLINE void set_cutoff(const double cutoff)          {              _cutoff = cutoff;          } @@ -181,32 +181,32 @@ namespace uhd{              //NOP          } -        inline double get_output_rate() +        UHD_INLINE double get_output_rate()          {              return (_bypass ? _rate : (_rate / _decimation * _interpolation));          } -        inline double get_input_rate() +        UHD_INLINE double get_input_rate()          {              return _rate;          } -        inline double get_interpolation() +        UHD_INLINE double get_interpolation()          {              return _interpolation;          } -        inline double get_decimation() +        UHD_INLINE double get_decimation()          {              return _decimation;          } -        inline double get_tap_full_scale() +        UHD_INLINE double get_tap_full_scale()          {              return _tap_full_scale;          } -        inline std::vector<tap_t>& get_taps() +        UHD_INLINE std::vector<tap_t>& get_taps()          {              return _taps;          } diff --git a/host/include/uhd/types/stdint.hpp b/host/include/uhd/types/stdint.hpp new file mode 100644 index 000000000..cccb6b157 --- /dev/null +++ b/host/include/uhd/types/stdint.hpp @@ -0,0 +1,53 @@ +// +// Copyright 2015 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_TYPES_STDINT_HPP +#define INCLUDED_UHD_TYPES_STDINT_HPP + +#include <boost/cstdint.hpp> + +using boost::int8_t; +using boost::uint8_t; +using boost::int16_t; +using boost::uint16_t; +using boost::int32_t; +using boost::uint32_t; +using boost::int64_t; +using boost::uint64_t; + +using boost::int_least8_t; +using boost::uint_least8_t; +using boost::int_least16_t; +using boost::uint_least16_t; +using boost::int_least32_t; +using boost::uint_least32_t; +using boost::int_least64_t; +using boost::uint_least64_t; + +using boost::int_fast8_t; +using boost::uint_fast8_t; +using boost::int_fast16_t; +using boost::uint_fast16_t; +using boost::int_fast32_t; +using boost::uint_fast32_t; +using boost::int_fast64_t; +using boost::uint_fast64_t; + +using boost::intptr_t; +using boost::uintptr_t; + +#endif /* INCLUDED_UHD_TYPES_STDINT_HPP */ diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index e974f808d..2d3717357 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -26,6 +26,7 @@ UHD_INSTALL(FILES      ### utilities ###      gps_ctrl.hpp +    gpio_defs.hpp      mboard_eeprom.hpp      subdev_spec.hpp diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index f8f318a40..c3a3a4e00 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -22,6 +22,7 @@  #include <uhd/utils/pimpl.hpp>  #include <uhd/types/serial.hpp>  #include <uhd/types/time_spec.hpp> +#include <uhd/usrp/gpio_defs.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/cstdint.hpp>  #include <string> @@ -67,14 +68,6 @@ public:          UNIT_TX = int('t')      }; -    //! possible atr registers -    enum atr_reg_t{ -        ATR_REG_IDLE        = int('i'), -        ATR_REG_TX_ONLY     = int('t'), -        ATR_REG_RX_ONLY     = int('r'), -        ATR_REG_FULL_DUPLEX = int('f') -    }; -      //! aux dac selection enums (per unit)      enum aux_dac_t{          AUX_DAC_A = int('a'), @@ -89,6 +82,8 @@ public:          AUX_ADC_B = int('b')      }; +    typedef uhd::usrp::gpio_atr::gpio_atr_reg_t atr_reg_t; +      /*!       * Get special properties information for this dboard slot.       * This call helps the dboard code to handle implementation diff --git a/host/include/uhd/usrp/gpio_defs.hpp b/host/include/uhd/usrp/gpio_defs.hpp new file mode 100644 index 000000000..c32f22f28 --- /dev/null +++ b/host/include/uhd/usrp/gpio_defs.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP + +#include <uhd/config.hpp> +#include <boost/assign.hpp> +#include <boost/utility.hpp> +#include <map> + +namespace uhd { namespace usrp { namespace gpio_atr { + +enum gpio_atr_reg_t { +    ATR_REG_IDLE        = int('i'), +    ATR_REG_TX_ONLY     = int('t'), +    ATR_REG_RX_ONLY     = int('r'), +    ATR_REG_FULL_DUPLEX = int('f') +}; + +enum gpio_atr_mode_t { +    MODE_ATR  = 0,   //Output driven by the auto-transmit-receive engine +    MODE_GPIO = 1    //Output value is static +}; + +enum gpio_ddr_t { +    DDR_INPUT   = 0, +    DDR_OUTPUT  = 1 +}; + +enum gpio_attr_t { +    GPIO_CTRL, +    GPIO_DDR, +    GPIO_OUT, +    GPIO_ATR_0X, +    GPIO_ATR_RX, +    GPIO_ATR_TX, +    GPIO_ATR_XX +}; + +typedef std::map<gpio_attr_t, std::string> gpio_attr_map_t; + +static const gpio_attr_map_t gpio_attr_map = +    boost::assign::map_list_of +        (GPIO_CTRL,   "CTRL") +        (GPIO_DDR,    "DDR") +        (GPIO_OUT,    "OUT") +        (GPIO_ATR_0X, "ATR_0X") +        (GPIO_ATR_RX, "ATR_RX") +        (GPIO_ATR_TX, "ATR_TX") +        (GPIO_ATR_XX, "ATR_XX") +; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP */ diff --git a/host/include/uhd/utils/algorithm.hpp b/host/include/uhd/utils/algorithm.hpp index 704d745d9..6c6cdf033 100644 --- a/host/include/uhd/utils/algorithm.hpp +++ b/host/include/uhd/utils/algorithm.hpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 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 @@ -39,7 +39,7 @@ namespace uhd{       * \param range the range of elements to be sorted       * \return a new range with the elements sorted       */ -    template<typename Range> inline Range sorted(const Range &range){ +    template<typename Range> UHD_INLINE Range sorted(const Range &range){          Range r(range); std::sort(boost::begin(r), boost::end(r)); return r;      } @@ -53,7 +53,7 @@ namespace uhd{       * \param range the range of elements to be reversed       * \return a new range with the elements reversed       */ -    template<typename Range> inline Range reversed(const Range &range){ +    template<typename Range> UHD_INLINE Range reversed(const Range &range){          Range r(range); std::reverse(boost::begin(r), boost::end(r)); return r;      } @@ -66,7 +66,7 @@ namespace uhd{       * \param value the match to look for in the range       * \return true when the value is found in the range       */ -    template<typename Range, typename T> inline +    template<typename Range, typename T> UHD_INLINE      bool has(const Range &range, const T &value){          return boost::end(range) != std::find(boost::begin(range), boost::end(range), value);      } @@ -78,7 +78,7 @@ namespace uhd{       * \param bound2 the upper or lower bound       * \return the value clipped at the bounds       */ -    template<typename T> inline T clip(const T &val, const T &bound1, const T &bound2){ +    template<typename T> UHD_INLINE T clip(const T &val, const T &bound1, const T &bound2){          const T minimum = std::min(bound1, bound2);          if (val < minimum) return minimum;          const T maximum = std::max(bound1, bound2); diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp index 9db92c526..869d53053 100644 --- a/host/include/uhd/utils/cast.hpp +++ b/host/include/uhd/utils/cast.hpp @@ -1,5 +1,5 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 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 @@ -28,7 +28,7 @@ namespace uhd{ namespace cast{      // Example:      //     boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF");      // Uses stringstream. -    template<typename T> inline T hexstr_cast(const std::string &in) +    template<typename T> UHD_INLINE T hexstr_cast(const std::string &in)      {          T x;          std::stringstream ss; diff --git a/host/include/uhd/utils/dirty_tracked.hpp b/host/include/uhd/utils/dirty_tracked.hpp index d228a9e65..561beec9b 100644 --- a/host/include/uhd/utils/dirty_tracked.hpp +++ b/host/include/uhd/utils/dirty_tracked.hpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 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 @@ -60,7 +60,7 @@ namespace uhd{          /*!           * Get underlying data           */ -        inline const data_t& get() const { +        UHD_INLINE const data_t& get() const {              return _data;          } @@ -68,21 +68,21 @@ namespace uhd{           * Has the underlying data changed since the last           * time it was cleaned?           */ -        inline bool is_dirty() const { +        UHD_INLINE bool is_dirty() const {              return _dirty;          }          /*!           * Mark the underlying data as clean           */ -        inline void mark_clean() { +        UHD_INLINE void mark_clean() {              _dirty = false;          }          /*!           * Mark the underlying data as dirty           */ -        inline void force_dirty() { +        UHD_INLINE void force_dirty() {              _dirty = true;          } @@ -91,7 +91,7 @@ namespace uhd{           * Store the specified value and mark it as dirty           * if it is not equal to the underlying data.           */ -        inline dirty_tracked& operator=(const data_t& value) +        UHD_INLINE dirty_tracked& operator=(const data_t& value)          {              if(!(_data == value)) {    //data_t must have an equality operator                  _dirty = true; @@ -107,7 +107,7 @@ namespace uhd{           * This exists to optimize out an implicit cast from dirty_tracked           * type to data type.           */ -        inline dirty_tracked& operator=(const dirty_tracked& source) { +        UHD_INLINE dirty_tracked& operator=(const dirty_tracked& source) {              if (!(_data == source._data)) {                  _dirty = true;                  _data = source._data; @@ -118,7 +118,7 @@ namespace uhd{          /*!           * Explicit conversion from this type to data_t           */ -        inline operator const data_t&() const { +        UHD_INLINE operator const data_t&() const {              return get();          } diff --git a/host/include/uhd/utils/msg_task.hpp b/host/include/uhd/utils/msg_task.hpp index d46fdd69e..8ae789d72 100644 --- a/host/include/uhd/utils/msg_task.hpp +++ b/host/include/uhd/utils/msg_task.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2014 Ettus Research LLC +// Copyright 2011-2015 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 @@ -42,7 +42,7 @@ namespace uhd{               */              virtual msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) = 0; -            inline static std::vector<boost::uint8_t> buff_to_vector(boost::uint8_t* p, size_t n) { +            UHD_INLINE static std::vector<boost::uint8_t> buff_to_vector(boost::uint8_t* p, size_t n) {                  if(p and n > 0){                      std::vector<boost::uint8_t> v(n);                      memcpy(&v.front(), p, n); diff --git a/host/include/uhd/utils/soft_register.hpp b/host/include/uhd/utils/soft_register.hpp index a2c34a4ec..a6184bbb9 100644 --- a/host/include/uhd/utils/soft_register.hpp +++ b/host/include/uhd/utils/soft_register.hpp @@ -57,7 +57,7 @@ namespace uhd {  //TODO: These hints were added to boost 1.53.  /** \brief hint for the branch prediction */ -inline bool likely(bool expr) +UHD_INLINE bool likely(bool expr)  {  #ifdef __GNUC__      return __builtin_expect(expr, true); @@ -67,7 +67,7 @@ inline bool likely(bool expr)      }  /** \brief hint for the branch prediction */ -inline bool unlikely(bool expr) +UHD_INLINE bool unlikely(bool expr)  {  #ifdef __GNUC__      return __builtin_expect(expr, false); @@ -86,16 +86,16 @@ inline bool unlikely(bool expr)  typedef boost::uint32_t soft_reg_field_t;  namespace soft_reg_field { -    inline size_t width(const soft_reg_field_t field) { +    UHD_INLINE size_t width(const soft_reg_field_t field) {          return (field & 0xFF);      } -    inline size_t shift(const soft_reg_field_t field) { +    UHD_INLINE size_t shift(const soft_reg_field_t field) {          return ((field >> 8) & 0xFF);      }      template<typename data_t> -    inline size_t mask(const soft_reg_field_t field) { +    UHD_INLINE size_t mask(const soft_reg_field_t field) {          static const data_t ONE = static_cast<data_t>(1);          //Behavior for the left shift operation is undefined in C++          //if the shift amount is >= bitwidth of the datatype @@ -122,7 +122,7 @@ public:       * Cast the soft_register generic reference to a more specific type       */      template <typename soft_reg_t> -    inline static soft_reg_t& cast(soft_register_base& reg) { +    UHD_INLINE static soft_reg_t& cast(soft_register_base& reg) {          soft_reg_t* ptr = dynamic_cast<soft_reg_t*>(®);          if (ptr) {              return *ptr; @@ -172,7 +172,7 @@ public:       * Can be optionally synced with hardware.       * NOTE: Memory management of the iface is up to the caller       */ -    inline void initialize(wb_iface& iface, bool sync = false) +    UHD_INLINE void initialize(wb_iface& iface, bool sync = false)      {          _iface = &iface; @@ -186,7 +186,7 @@ public:       * Performs a read-modify-write operation so all other field are preserved.       * NOTE: This does not write the value to hardware.       */ -    inline void set(const soft_reg_field_t field, const reg_data_t value) +    UHD_INLINE void set(const soft_reg_field_t field, const reg_data_t value)      {          _soft_copy = (_soft_copy & ~soft_reg_field::mask<reg_data_t>(field)) |                       ((value << soft_reg_field::shift(field)) & soft_reg_field::mask<reg_data_t>(field)); @@ -196,7 +196,7 @@ public:       * Get the value of the specified field from the soft-copy.       * NOTE: This does not read anything from hardware.       */ -    inline reg_data_t get(const soft_reg_field_t field) +    UHD_INLINE reg_data_t get(const soft_reg_field_t field)      {          return (_soft_copy & soft_reg_field::mask<reg_data_t>(field)) >> soft_reg_field::shift(field);      } @@ -204,7 +204,7 @@ public:      /*!       * Write the contents of the soft-copy to hardware.       */ -    inline void flush() +    UHD_INLINE void flush()      {          if (writable && _iface) {              //If optimized flush then poke only if soft copy is dirty @@ -223,14 +223,14 @@ public:                  _soft_copy.mark_clean();              }          } else { -            throw uhd::not_implemented_error("soft_register is not writable."); +            throw uhd::not_implemented_error("soft_register is not writable or uninitialized.");          }      }      /*!       * Read the contents of the register from hardware and update the soft copy.       */ -    inline void refresh() +    UHD_INLINE void refresh()      {          if (readable && _iface) {              if (get_bitwidth() <= 16) { @@ -244,14 +244,14 @@ public:              }              _soft_copy.mark_clean();          } else { -            throw uhd::not_implemented_error("soft_register is not readable."); +            throw uhd::not_implemented_error("soft_register is not readable or uninitialized.");          }      }      /*!       * Shortcut for a set and a flush.       */ -    inline void write(const soft_reg_field_t field, const reg_data_t value) +    UHD_INLINE void write(const soft_reg_field_t field, const reg_data_t value)      {          set(field, value);          flush(); @@ -260,7 +260,7 @@ public:      /*!       * Shortcut for refresh and get       */ -    inline reg_data_t read(const soft_reg_field_t field) +    UHD_INLINE reg_data_t read(const soft_reg_field_t field)      {          refresh();          return get(field); @@ -269,7 +269,7 @@ public:      /*!       * Get bitwidth for this register       */ -    inline size_t get_bitwidth() +    UHD_INLINE size_t get_bitwidth()      {          static const size_t BITS_IN_BYTE = 8;          return sizeof(reg_data_t) * BITS_IN_BYTE; @@ -278,7 +278,7 @@ public:      /*!       * Is the register readable?       */ -    inline bool is_readable() +    UHD_INLINE bool is_readable()      {          return readable;      } @@ -286,7 +286,7 @@ public:      /*!       * Is the register writable?       */ -    inline bool is_writable() +    UHD_INLINE bool is_writable()      {          return writable;      } @@ -321,43 +321,43 @@ public:          soft_register_t<reg_data_t, readable, writable>(addr, mode), _mutex()      {} -    inline void initialize(wb_iface& iface, bool sync = false) +    UHD_INLINE void initialize(wb_iface& iface, bool sync = false)      {          boost::lock_guard<boost::mutex> lock(_mutex);          soft_register_t<reg_data_t, readable, writable>::initialize(iface, sync);      } -    inline void set(const soft_reg_field_t field, const reg_data_t value) +    UHD_INLINE void set(const soft_reg_field_t field, const reg_data_t value)      {          boost::lock_guard<boost::mutex> lock(_mutex);          soft_register_t<reg_data_t, readable, writable>::set(field, value);      } -    inline reg_data_t get(const soft_reg_field_t field) +    UHD_INLINE reg_data_t get(const soft_reg_field_t field)      {          boost::lock_guard<boost::mutex> lock(_mutex);          return soft_register_t<reg_data_t, readable, writable>::get(field);      } -    inline void flush() +    UHD_INLINE void flush()      {          boost::lock_guard<boost::mutex> lock(_mutex);          soft_register_t<reg_data_t, readable, writable>::flush();      } -    inline void refresh() +    UHD_INLINE void refresh()      {          boost::lock_guard<boost::mutex> lock(_mutex);          soft_register_t<reg_data_t, readable, writable>::refresh();      } -    inline void write(const soft_reg_field_t field, const reg_data_t value) +    UHD_INLINE void write(const soft_reg_field_t field, const reg_data_t value)      {          boost::lock_guard<boost::mutex> lock(_mutex);          soft_register_t<reg_data_t, readable, writable>::write(field, value);      } -    inline reg_data_t read(const soft_reg_field_t field) +    UHD_INLINE reg_data_t read(const soft_reg_field_t field)      {          boost::lock_guard<boost::mutex> lock(_mutex);          return soft_register_t<reg_data_t, readable, writable>::read(field); @@ -469,7 +469,7 @@ public:      /*!       * Get the name of this register map       */ -    virtual inline const std::string& get_name() const { return _name; } +    virtual UHD_INLINE const std::string& get_name() const { return _name; }      /*!       * Initialize all registers in this register map using a bus. @@ -542,7 +542,7 @@ protected:      /*!       * Add a register to this map with an identifier "name" and visibility       */ -    inline void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) { +    UHD_INLINE void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) {          boost::lock_guard<boost::mutex> lock(_mutex);          if (visible == PUBLIC) {              //Only add to the map if this register is publicly visible diff --git a/host/include/uhd/version.hpp.in b/host/include/uhd/version.hpp.in index e2c64812d..bfa0b904a 100644 --- a/host/include/uhd/version.hpp.in +++ b/host/include/uhd/version.hpp.in @@ -1,5 +1,5 @@  // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -27,7 +27,7 @@   * The format is oldest API compatible release - ABI compat number.   * The compatibility number allows pre-release ABI to be versioned.   */ -#define UHD_VERSION_ABI_STRING "3.9.0-0" +#define UHD_VERSION_ABI_STRING "3.10.0-0"  /*!   * A macro to check UHD version at compile-time. diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index f74af1f29..21a979109 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -65,6 +65,26 @@ MACRO(INCLUDE_SUBDIRECTORY subdir)  ENDMACRO(INCLUDE_SUBDIRECTORY)  ######################################################################## +# Register lower level components +######################################################################## +MESSAGE(STATUS "") +# Dependencies +FIND_PACKAGE(USB1) +FIND_PACKAGE(GPSD) +LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF OFF) +LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) +# Devices +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) +LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) +LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF OFF) +LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) +LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("N230" ENABLE_N230 ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) + +########################################################################  # Include subdirectories (different than add)  ########################################################################  INCLUDE_SUBDIRECTORY(ic_reg_maps) @@ -76,6 +96,16 @@ INCLUDE_SUBDIRECTORY(usrp_clock)  INCLUDE_SUBDIRECTORY(utils)  ######################################################################## +# Build info +######################################################################## +INCLUDE(UHDBuildInfo) +UHD_LOAD_BUILD_INFO() +CONFIGURE_FILE( +    ${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp +    ${CMAKE_CURRENT_BINARY_DIR}/build_info.cpp +@ONLY) + +########################################################################  # Setup UHD_VERSION_STRING for version.cpp  ########################################################################  CONFIGURE_FILE( @@ -87,6 +117,7 @@ CONFIGURE_FILE(  # Append to the list of sources for lib uhd  ########################################################################  LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_BINARY_DIR}/build_info.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/image_loader.cpp diff --git a/host/lib/build_info.cpp b/host/lib/build_info.cpp new file mode 100644 index 000000000..5ccfd0268 --- /dev/null +++ b/host/lib/build_info.cpp @@ -0,0 +1,113 @@ +// +// Copyright 2015 National Instruments Corp. +// +// 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 <config.h> + +#include <uhd/build_info.hpp> + +#include <boost/format.hpp> +#include <boost/version.hpp> +#include <boost/algorithm/string.hpp> + +#ifdef ENABLE_USB +#include <libusb.h> +#endif + +namespace uhd { namespace build_info { + +    const std::string boost_version() { +        return boost::algorithm::replace_all_copy( +            std::string(BOOST_LIB_VERSION), "_", "." +        ); +    } + +    const std::string build_date() { +        return "@UHD_BUILD_DATE@"; +    } + +    const std::string c_compiler() { +        return "@UHD_C_COMPILER@"; +    } + +    const std::string cxx_compiler() { +        return "@UHD_CXX_COMPILER@"; +    } + +#ifdef _MSC_VER +    static const std::string define_flag = "/D "; +#else +    static const std::string define_flag = "-D"; +#endif + +    const std::string c_flags() { +        return boost::algorithm::replace_all_copy( +            (define_flag + std::string("@UHD_C_FLAGS@")), +            std::string(";"), (" " + define_flag) +        ); +    } + +    const std::string cxx_flags() { +        return boost::algorithm::replace_all_copy( +            (define_flag + std::string("@UHD_CXX_FLAGS@")), +            std::string(";"), (" " + define_flag) +        ); +    } + +    const std::string enabled_components() { +        return boost::algorithm::replace_all_copy( +            std::string("@_uhd_enabled_components@"), +            std::string(";"), std::string(", ") +        ); +    } + +    const std::string install_prefix() { +        return "@CMAKE_INSTALL_PREFIX@"; +    } + +    const std::string libusb_version() { +    #ifdef ENABLE_USB +        /* +         * Versions can only be queried from 1.0.13 onward. +         * Depending on if the commit came from libusbx or +         * libusb (now merged), the define might be different. +         */ +        #ifdef LIBUSB_API_VERSION /* 1.0.18 onward */ +            int major_version = LIBUSB_API_VERSION >> 24; +            int minor_version = (LIBUSB_API_VERSION & 0xFF0000) >> 16; +            int micro_version = ((LIBUSB_API_VERSION & 0xFFFF) - 0x100) + 18; + +            return str(boost::format("%d.%d.%d") +                          % major_version % minor_version % micro_version); +        #elif defined(LIBUSBX_API_VERSION) /* 1.0.13 - 1.0.17 */ +            switch(LIBUSBX_API_VERSION & 0xFF) { +                case 0x00: +                    return "1.0.13"; +                case 0x01: +                    return "1.0.15"; +                case 0xFF: +                    return "1.0.14"; +                default: +                    return "1.0.16 or 1.0.17"; +            } +        #else +            return "< 1.0.13"; +        #endif +    #else +        return "N/A"; +    #endif +    } +}} diff --git a/host/lib/convert/convert_item32.cpp b/host/lib/convert/convert_item32.cpp index 57bd64860..d52b47a1a 100644 --- a/host/lib/convert/convert_item32.cpp +++ b/host/lib/convert/convert_item32.cpp @@ -38,7 +38,10 @@      _DECLARE_ITEM32_CONVERTER(cpu_type, sc8) \      _DECLARE_ITEM32_CONVERTER(cpu_type, sc16) +/* Create sc16<->sc16,sc8(otw) */  DECLARE_ITEM32_CONVERTER(sc16) +/* Create fc32<->sc16,sc8(otw) */  DECLARE_ITEM32_CONVERTER(fc32) +/* Create fc64<->sc16,sc8(otw) */  DECLARE_ITEM32_CONVERTER(fc64)  _DECLARE_ITEM32_CONVERTER(sc8, sc8) diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index 4f9eeb747..5c62d51df 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -39,30 +39,37 @@ DECLARE_CONVERTER(item32, 1, item32, 1, PRIORITY_GENERAL) {  }  """ -TMPL_CONV_GEN2_ITEM32 = """ -DECLARE_CONVERTER(item32, 1, sc16_item32_{end}, 1, PRIORITY_GENERAL) {{ +# Some 32-bit types converters are also defined in convert_item32.cpp to +# take care of quirks such as I/Q ordering on the wire etc. +TMPL_CONV_ITEM32 = """ +DECLARE_CONVERTER({in_type}, 1, {out_type}, 1, PRIORITY_GENERAL) {{      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);      for (size_t i = 0; i < nsamps; i++) {{ -        output[i] = {to_wire}(input[i]); +        output[i] = {to_wire_or_host}(input[i]);      }}  }} +""" -DECLARE_CONVERTER(sc16_item32_{end}, 1, item32, 1, PRIORITY_GENERAL) {{ +# 64-bit data types are two consecutive item32 items +TMPL_CONV_ITEM64 = """ +DECLARE_CONVERTER({in_type}, 1, {out_type}, 1, PRIORITY_GENERAL) {{      const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]);      item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); -    for (size_t i = 0; i < nsamps; i++) {{ -        output[i] = {to_host}(input[i]); +    // An item64 is two item32_t's +    for (size_t i = 0; i < nsamps * 2; i++) {{ +        output[i] = {to_wire_or_host}(input[i]);      }}  }}  """ -TMPL_CONV_U8 = """ -DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{ -    const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); -    boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); + +TMPL_CONV_U8S8 = """ +DECLARE_CONVERTER({us8}, 1, {us8}_item32_{end}, 1, PRIORITY_GENERAL) {{ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);      // 1) Copy all the 4-byte tuples      size_t n_words = nsamps / 4; @@ -72,8 +79,8 @@ DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{      // 2) If nsamps was not a multiple of 4, copy the rest by hand      size_t bytes_left = nsamps % 4;      if (bytes_left) {{ -        const u8_t *last_input_word  = reinterpret_cast<const u8_t *>(&input[n_words]); -        u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); +        const {us8}_t *last_input_word  = reinterpret_cast<const {us8}_t *>(&input[n_words]); +        {us8}_t *last_output_word = reinterpret_cast<{us8}_t *>(&output[n_words]);          for (size_t k = 0; k < bytes_left; k++) {{              last_output_word[k] = last_input_word[k];          }} @@ -81,9 +88,9 @@ DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{      }}  }} -DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{ -    const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); -    boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); +DECLARE_CONVERTER({us8}_item32_{end}, 1, {us8}, 1, PRIORITY_GENERAL) {{ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]);      // 1) Copy all the 4-byte tuples      size_t n_words = nsamps / 4; @@ -93,9 +100,9 @@ DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{      // 2) If nsamps was not a multiple of 4, copy the rest by hand      size_t bytes_left = nsamps % 4;      if (bytes_left) {{ -        boost::uint32_t last_input_word = {to_host}(input[n_words]); -        const u8_t *last_input_word_ptr = reinterpret_cast<const u8_t *>(&last_input_word); -        u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); +        item32_t last_input_word = {to_host}(input[n_words]); +        const {us8}_t *last_input_word_ptr = reinterpret_cast<const {us8}_t *>(&last_input_word); +        {us8}_t *last_output_word = reinterpret_cast<{us8}_t *>(&output[n_words]);          for (size_t k = 0; k < bytes_left; k++) {{              last_output_word[k] = last_input_word_ptr[k];          }} @@ -103,6 +110,40 @@ DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{  }}  """ +TMPL_CONV_S16 = """ +DECLARE_CONVERTER(s16, 1, s16_item32_{end}, 1, PRIORITY_GENERAL) {{ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + +    // 1) Copy all the 4-byte tuples +    size_t n_words = nsamps / 2; +    for (size_t i = 0; i < n_words; i++) {{ +        output[i] = {to_wire}(input[i]); +    }} +    // 2) If nsamps was not a multiple of 2, copy the last one by hand +    if (nsamps % 2) {{ +        item32_t tmp = item32_t(*reinterpret_cast<const s16_t *>(&input[n_words])); +        output[n_words] = {to_wire}(tmp); +    }} +}} + +DECLARE_CONVERTER(s16_item32_{end}, 1, s16, 1, PRIORITY_GENERAL) {{ +    const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); +    item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + +    // 1) Copy all the 4-byte tuples +    size_t n_words = nsamps / 2; +    for (size_t i = 0; i < n_words; i++) {{ +        output[i] = {to_host}(input[i]); +    }} +    // 2) If nsamps was not a multiple of 2, copy the last one by hand +    if (nsamps % 2) {{ +        item32_t tmp = {to_host}(input[n_words]); +        *reinterpret_cast<s16_t *>(&output[n_words]) = s16_t(tmp); +    }} +}} +""" +  TMPL_CONV_USRP1_COMPLEX = """  DECLARE_CONVERTER(${cpu_type}, ${width}, sc16_item16_usrp1, 1, PRIORITY_GENERAL){      % for w in range(width): @@ -164,23 +205,52 @@ if __name__ == '__main__':      file = os.path.basename(__file__)      output = parse_tmpl(TMPL_HEADER, file=file) -    #generate complex converters for all gen2 platforms -    for end, to_host, to_wire in ( -        ('be', 'uhd::ntohx', 'uhd::htonx'), -        ('le', 'uhd::wtohx', 'uhd::htowx'), -    ): -        output += TMPL_CONV_GEN2_ITEM32.format( -                end=end, to_host=to_host, to_wire=to_wire -        ) -    #generate raw (u8) converters: +    ## Generate all data types that are exactly +    ## item32 or multiples thereof: +    for end in ('be', 'le'): +        host_to_wire = {'be': 'uhd::htonx', 'le': 'uhd::htowx'}[end] +        wire_to_host = {'be': 'uhd::ntohx', 'le': 'uhd::wtohx'}[end] +        # item32 types (sc16->sc16 is a special case because it defaults +        # to Q/I order on the wire: +        for in_type, out_type, to_wire_or_host in ( +                ('item32', 'sc16_item32_{end}', host_to_wire), +                ('sc16_item32_{end}', 'item32', wire_to_host), +                ('f32', 'f32_item32_{end}', host_to_wire), +                ('f32_item32_{end}', 'f32', wire_to_host), +        ): +            output += TMPL_CONV_ITEM32.format( +                    end=end, to_wire_or_host=to_wire_or_host, +                    in_type=in_type.format(end=end), out_type=out_type.format(end=end) +            ) +        # 2xitem32 types: +        for in_type, out_type in ( +                ('fc32', 'fc32_item32_{end}'), +                ('fc32_item32_{end}', 'fc32'), +        ): +            output += TMPL_CONV_ITEM64.format( +                    end=end, to_wire_or_host=to_wire_or_host, +                    in_type=in_type.format(end=end), out_type=out_type.format(end=end) +            ) + +    ## Real 16-Bit:      for end, to_host, to_wire in (          ('be', 'uhd::ntohx', 'uhd::htonx'),          ('le', 'uhd::wtohx', 'uhd::htowx'),      ): -        output += TMPL_CONV_U8.format( -                end=end, to_host=to_host, to_wire=to_wire +        output += TMPL_CONV_S16.format( +            end=end, to_host=to_host, to_wire=to_wire          ) +    ## Real 8-Bit Types: +    for us8 in ('u8', 's8'): +        for end, to_host, to_wire in ( +            ('be', 'uhd::ntohx', 'uhd::htonx'), +            ('le', 'uhd::wtohx', 'uhd::htowx'), +        ): +            output += TMPL_CONV_U8S8.format( +                    us8=us8, end=end, to_host=to_host, to_wire=to_wire +            ) +      #generate complex converters for usrp1 format (requires Cheetah)      for width in 1, 2, 4:          for cpu_type, do_scale in ( diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 6abc399b4..79c8a90b7 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -22,17 +22,15 @@  ########################################################################  # Include subdirectories (different than add)  ######################################################################## -INCLUDE_SUBDIRECTORY(nirio) +IF(ENABLE_X300) +    INCLUDE_SUBDIRECTORY(nirio) +ENDIF(ENABLE_X300)  ########################################################################  # Setup libusb  ######################################################################## -MESSAGE(STATUS "") -FIND_PACKAGE(USB1) - -LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF OFF) -  IF(ENABLE_USB) +    MESSAGE(STATUS "")      MESSAGE(STATUS "USB support enabled via libusb.")      INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS})      LIBUHD_APPEND_LIBS(${LIBUSB_LIBRARIES}) @@ -128,10 +126,15 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/buffer_pool.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/chdr.cpp  ) +IF(ENABLE_X300) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp +    ) +ENDIF(ENABLE_X300) +  # Verbose Debug output for send/recv  SET( UHD_TXRX_DEBUG_PRINTS OFF CACHE BOOL "Use verbose debug output for send/recv" )  OPTION( UHD_TXRX_DEBUG_PRINTS "Use verbose debug output for send/recv" "" ) diff --git a/host/lib/transport/nirio/lvbitx/CMakeLists.txt b/host/lib/transport/nirio/lvbitx/CMakeLists.txt index b9a2a9f15..5741a12f8 100644 --- a/host/lib/transport/nirio/lvbitx/CMakeLists.txt +++ b/host/lib/transport/nirio/lvbitx/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2013 Ettus Research LLC +# Copyright 2013,2015 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 @@ -30,8 +30,8 @@ MACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM lvbitx binfile)      SET(IMAGES_PATH_OPT --uhd-images-path=${UHD_IMAGES_DIR})      ADD_CUSTOM_COMMAND( -        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp           OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp +        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp          DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/process-lvbitx.py          DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.hpp          DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/template_lvbitx.cpp @@ -41,6 +41,7 @@ MACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM lvbitx binfile)      )      #make libuhd depend on the output file +    LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.hpp)      LIBUHD_APPEND_SOURCES(${CMAKE_CURRENT_BINARY_DIR}/${lvbitxprefix}_lvbitx.cpp)  ENDMACRO(LIBUHD_LVBITX_GEN_SOURCE_AND_BITSTREAM) diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 0f1f7ff3a..4a7ad8d26 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -127,7 +127,7 @@ public:       * \param threshold number of packets per channel       */      void set_alignment_failure_threshold(const size_t threshold){ -        _alignment_faulure_threshold = threshold*this->size(); +        _alignment_failure_threshold = threshold*this->size();      }      //! Set the rate of ticks per second @@ -203,6 +203,12 @@ public:      //! Overload call to issue stream commands      void issue_stream_cmd(const stream_cmd_t &stream_cmd)      { +        if (stream_cmd.stream_now +                and stream_cmd.stream_mode != stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS +                and _props.size() > 1) { +            throw uhd::runtime_error("Attempting to do multi-channel receive with stream_now == true will result in misaligned channels. Aborting."); +        } +          for (size_t i = 0; i < _props.size(); i++)          {              if (_props[i].issue_stream_cmd) _props[i].issue_stream_cmd(stream_cmd); @@ -269,7 +275,7 @@ private:      size_t _header_offset_words32;      double _tick_rate, _samp_rate;      bool _queue_error_for_next_call; -    size_t _alignment_faulure_threshold; +    size_t _alignment_failure_threshold;      rx_metadata_t _queue_metadata;      struct xport_chan_props_type{          xport_chan_props_type(void): @@ -587,7 +593,7 @@ private:              }              //too many iterations: detect alignment failure -            if (iterations++ > _alignment_faulure_threshold){ +            if (iterations++ > _alignment_failure_threshold){                  UHD_MSG(error) << boost::format(                      "The receive packet handler failed to time-align packets.\n"                      "%u received packets were processed by the handler.\n" diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 5c9592970..695f7f83d 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -18,8 +18,6 @@  ########################################################################  # This file included, use CMake directory variables  ######################################################################## -find_package(GPSD) -  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})  LIBUHD_APPEND_SOURCES( @@ -43,8 +41,6 @@ IF(ENABLE_C_API)      )  ENDIF(ENABLE_C_API) -LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) -  IF(ENABLE_GPSD)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp @@ -62,3 +58,4 @@ INCLUDE_SUBDIRECTORY(e100)  INCLUDE_SUBDIRECTORY(e300)  INCLUDE_SUBDIRECTORY(x300)  INCLUDE_SUBDIRECTORY(b200) +INCLUDE_SUBDIRECTORY(n230) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 1558cd974..66129458c 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the B100 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_B100)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index 76710dc65..4b9e2de55 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the B200 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_B200)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 38709bbb3..62690f09f 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -41,6 +41,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::transport;  static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); @@ -661,15 +662,15 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      ////////////////////////////////////////////////////////////////////      // front panel gpio      //////////////////////////////////////////////////////////////////// -    _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); +    _radio_perifs[0].fp_gpio = gpio_atr_3000::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);      BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      {              _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)              .set(0) -            .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); +            .subscribe(boost::bind(&gpio_atr_3000::set_gpio_attr, _radio_perifs[0].fp_gpio, attr.first, _1));      }      _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") -        .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); +        .publish(boost::bind(&gpio_atr_3000::read_gpio, _radio_perifs[0].fp_gpio));      ////////////////////////////////////////////////////////////////////      // dboard eeproms but not really @@ -682,10 +683,14 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      ////////////////////////////////////////////////////////////////////      // do some post-init tasks      //////////////////////////////////////////////////////////////////// - -    //init the clock rate to something reasonable -    double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE); +    // Init the clock rate and the auto mcr appropriately +    if (not device_addr.has_key("master_clock_rate")) { +        UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; +    } +    // We can automatically choose a master clock rate, but not if the user specifies one +    const double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE);      _tree->access<double>(mb_path / "tick_rate").set(default_tick_rate); +    _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate"));      //subdev spec contains full width of selections      subdev_spec_t rx_spec, tx_spec; @@ -709,12 +714,6 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s          _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM);          _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP);      } -    // We can automatically choose a master clock rate, but not if the user specifies one -    _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); -    if (not device_addr.has_key("master_clock_rate")) { -        UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; -    } -  }  b200_impl::~b200_impl(void) @@ -758,14 +757,15 @@ void b200_impl::setup_radio(const size_t dspno)      ////////////////////////////////////////////////////////////////////      // Set up peripherals      //////////////////////////////////////////////////////////////////// -    perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); +    perif.atr = gpio_atr_3000::make_write_only(perif.ctrl, TOREG(SR_ATR)); +    perif.atr->set_atr_mode(MODE_ATR, 0xFFFFFFFF);      // create rx dsp control objects      perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));      perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/);      perif.ddc->set_link_rate(10e9/8); //whatever      perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false);      perif.ddc->set_freq(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ); -    perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); +    perif.deframer = tx_vita_core_3000::make_no_radio_buff(perif.ctrl, TOREG(SR_TX_CTRL));      perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));      perif.duc->set_link_rate(10e9/8); //whatever      perif.duc->set_freq(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ); @@ -797,7 +797,6 @@ void b200_impl::setup_radio(const size_t dspno)      // create tx dsp control objects      ////////////////////////////////////////////////////////////////////      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))          .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));      const fs_path tx_dsp_path = mb_path / "tx_dsps" / dspno;      perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); @@ -969,27 +968,6 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)      mb_eeprom.commit(*_iface, "B200");  } - -boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL:   return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR:    return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT:    return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default:        UHD_THROW_INVALID_CODE_PATH(); -    } -} -  /***********************************************************************   * Reference time and clock   **********************************************************************/ @@ -1172,11 +1150,11 @@ void b200_impl::update_atrs(void)          if (enb_rx and enb_tx) fd = STATE_FDX1_TXRX;          if (enb_rx and not enb_tx) fd = rxonly;          if (not enb_rx and enb_tx) fd = txonly; -        gpio_core_200_32wo::sptr atr = perif.atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); +        gpio_atr_3000::sptr atr = perif.atr; +        atr->set_atr_reg(ATR_REG_IDLE, STATE_OFF); +        atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly); +        atr->set_atr_reg(ATR_REG_TX_ONLY, txonly); +        atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);      }      if (_radio_perifs.size() > _fe2 and _radio_perifs[_fe2].atr)      { @@ -1190,11 +1168,11 @@ void b200_impl::update_atrs(void)          if (enb_rx and enb_tx) fd = STATE_FDX2_TXRX;          if (enb_rx and not enb_tx) fd = rxonly;          if (not enb_rx and enb_tx) fd = txonly; -        gpio_core_200_32wo::sptr atr = perif.atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); +        gpio_atr_3000::sptr atr = perif.atr; +        atr->set_atr_reg(ATR_REG_IDLE, STATE_OFF); +        atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly); +        atr->set_atr_reg(ATR_REG_TX_ONLY, txonly); +        atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);      }  } diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 22dd231ce..251686b43 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -27,7 +27,7 @@  #include "rx_vita_core_3000.hpp"  #include "tx_vita_core_3000.hpp"  #include "time_core_3000.hpp" -#include "gpio_core_200.hpp" +#include "gpio_atr_3000.hpp"  #include "radio_ctrl_core_3000.hpp"  #include "rx_dsp_core_3000.hpp"  #include "tx_dsp_core_3000.hpp" @@ -49,8 +49,8 @@  #include "recv_packet_demuxer_3000.hpp"  static const boost::uint8_t  B200_FW_COMPAT_NUM_MAJOR = 8;  static const boost::uint8_t  B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 13; -static const boost::uint16_t B205_FPGA_COMPAT_NUM = 4; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 14; +static const boost::uint16_t B205_FPGA_COMPAT_NUM = 5;  static const double          B200_BUS_CLOCK_RATE = 100e6;  static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;  static const size_t B200_MAX_RATE_USB2              =  53248000; // bytes/s @@ -180,8 +180,8 @@ private:      struct radio_perifs_t      {          radio_ctrl_core_3000::sptr ctrl; -        gpio_core_200_32wo::sptr atr; -        gpio_core_200::sptr fp_gpio; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr atr; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio;          time_core_3000::sptr time64;          rx_vita_core_3000::sptr framer;          rx_dsp_core_3000::sptr ddc; @@ -229,9 +229,6 @@ private:      void update_enables(void);      void update_atrs(void); -    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); -    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); -      double _tick_rate;      double get_tick_rate(void){return _tick_rate;}      double set_tick_rate(const double rate); diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 5b0c4ba13..279901208 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -159,7 +159,6 @@ void b200_impl::update_tick_rate(const double new_tick_rate)          boost::shared_ptr<sph::send_packet_streamer> my_streamer =              boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());          if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); -        perif.deframer->set_tick_rate(new_tick_rate);      }  } diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9dabc4e0b..270314dcc 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -37,4 +37,5 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/usrp3_fw_ctrl_iface.cpp  ) diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index 5c438ee9c..8cd75d539 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -89,8 +89,10 @@ public:      //! get the clock rate range for the frontend      static uhd::meta_range_t get_clock_rate_range(void)      { -        //return uhd::meta_range_t(220e3, 61.44e6); -        return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end +        return uhd::meta_range_t( +                ad9361_device_t::AD9361_MIN_CLOCK_RATE, +                ad9361_device_t::AD9361_MAX_CLOCK_RATE +        );      }      //! set the filter bandwidth for the frontend's analog low pass diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index 0a8a61575..bb25379c0 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -91,6 +91,7 @@ int get_num_taps(int max_num_taps) {  }  const double ad9361_device_t::AD9361_MAX_GAIN        = 89.75; +const double ad9361_device_t::AD9361_MIN_CLOCK_RATE  = 220e3;  const double ad9361_device_t::AD9361_MAX_CLOCK_RATE  = 61.44e6;  const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6;  // Max bandwdith is due to filter rolloff in analog filter stage @@ -770,7 +771,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      size_t count = 0;      _io_iface->poke8(0x016, 0x02);      while (_io_iface->peek8(0x016) & 0x02) { -        if (count > 100) { +        if (count > 200) {              throw uhd::runtime_error("[ad9361_device_t] RF DC Offset Calibration Failure");              break;          } @@ -821,7 +822,7 @@ void ad9361_device_t::_calibrate_rx_quadrature()      size_t count = 0;      _io_iface->poke8(0x016, 0x20);      while (_io_iface->peek8(0x016) & 0x20) { -        if (count > 100) { +        if (count > 1000) {              throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure");              break;          } diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 66bc2e8b9..73b1d9a35 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -157,6 +157,7 @@ public:      //Constants      static const double AD9361_MAX_GAIN;      static const double AD9361_MAX_CLOCK_RATE; +    static const double AD9361_MIN_CLOCK_RATE;      static const double AD9361_CAL_VALID_WINDOW;      static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;      static const double DEFAULT_RX_FREQ; diff --git a/host/lib/usrp/common/constrained_device_args.hpp b/host/lib/usrp/common/constrained_device_args.hpp new file mode 100644 index 000000000..1bfd1df00 --- /dev/null +++ b/host/lib/usrp/common/constrained_device_args.hpp @@ -0,0 +1,283 @@ +// +// 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_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP + +#include <uhd/types/device_addr.hpp> +#include <uhd/exception.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign/list_of.hpp> +#include <vector> +#include <string> + +namespace uhd { +namespace usrp { + +    /*! +     * constrained_device_args_t provides a base and utilities to +     * map key=value pairs passed in through the device creation +     * args interface (device_addr_t). +     * +     * Inherit from this class to create typed device specific +     * arguments and use the base class methods to handle parsing +     * the device_addr or any key=value string to populate the args +     * +     * This file contains a library of different types of args the +     * the user can pass in. The library can be extended to support +     * non-intrinsic types by the client. +     * +     */ +    class constrained_device_args_t { +    public: //Types + +        /*! +         * Base argument type. All other arguments inherit from this. +         */ +        class generic_arg { +        public: +            generic_arg(const std::string& key): _key(key) {} +            inline const std::string& key() const { return _key; } +            inline virtual std::string to_string() const = 0; +        private: +            std::string _key; +        }; + +        /*! +         * String argument type. Can be case sensitive or insensitive +         */ +        template<bool case_sensitive> +        class str_arg : public generic_arg { +        public: +            str_arg(const std::string& name, const std::string& default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const std::string& value) { +                _value = case_sensitive ? value : boost::algorithm::to_lower_copy(value); +            } +            inline const std::string& get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                set(str_rep); +            } +            inline virtual std::string to_string() const { +                return key() + "=" + get(); +            } +            inline bool operator==(const std::string& rhs) const { +                return get() == boost::algorithm::to_lower_copy(rhs); +            } +        private: +            std::string _value; +        }; +        typedef str_arg<false>  str_ci_arg; +        typedef str_arg<true>   str_cs_arg; + +        /*! +         * Numeric argument type. The template type data_t allows the +         * client to constrain the type of the number. +         */ +        template<typename data_t> +        class num_arg : public generic_arg { +        public: +            num_arg(const std::string& name, const data_t default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const data_t value) { +                _value = value; +            } +            inline const data_t get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                try { +                    _value = boost::lexical_cast<data_t>(str_rep); +                } catch (std::exception& ex) { +                    throw uhd::value_error(str(boost::format( +                        "Error parsing numeric parameter %s: %s.") % +                        key() % ex.what() +                    )); +                } +            } +            inline virtual std::string to_string() const { +                return key() + "=" + boost::lexical_cast<std::string>(get()); +            } +        private: +            data_t _value; +        }; + +        /*! +         * Enumeration argument type. The template type enum_t allows the +         * client to use their own enum and specify a string mapping for +         * the values of the enum +         * +         * NOTE: The constraint on enum_t is that the values must start with +         * 0 and be sequential +         */ +        template<typename enum_t> +        class enum_arg : public generic_arg { +        public: +            enum_arg( +                const std::string& name, +                const enum_t default_value, +                const std::vector<std::string>& values) : +                    generic_arg(name), _str_values(values) +            { set(default_value); } + +            inline void set(const enum_t value) { +                _value = value; +            } +            inline const enum_t get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep, bool assert_invalid = true) { +                std::string valid_values_str; +                for (size_t i = 0; i < _str_values.size(); i++) { +                    if (boost::algorithm::to_lower_copy(str_rep) == +                        boost::algorithm::to_lower_copy(_str_values[i])) +                    { +                        valid_values_str += ((i==0)?"":", ") + _str_values[i]; +                        set(static_cast<enum_t>(static_cast<int>(i))); +                        return; +                    } +                } +                //If we reach here then, the string enum value was invalid +                if (assert_invalid) { +                    throw uhd::value_error(str(boost::format( +                        "Invalid device arg value: %s=%s (Valid: {%s})") % +                        key() % str_rep % valid_values_str +                    )); +                } +            } +            inline virtual std::string to_string() const { +                size_t index = static_cast<size_t>(static_cast<int>(_value)); +                UHD_ASSERT_THROW(index < _str_values.size()); +                return key() + "=" + _str_values[index]; +            } + +        private: +            enum_t                      _value; +            std::vector<std::string>    _str_values; +        }; + +        /*! +         * Boolean argument type. +         */ +        class bool_arg : public generic_arg { +        public: +            bool_arg(const std::string& name, const bool default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const bool value) { +                _value = value; +            } +            inline bool get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                try { +                    _value = (boost::lexical_cast<int>(str_rep) != 0); +                } catch (std::exception& ex) { +                    if (str_rep.empty()) { +                        //If str_rep is empty then the device_addr was set +                        //without a value which means that the user "set" the flag +                        _value = true; +                    } else if (boost::algorithm::to_lower_copy(str_rep) == "true" || +                        boost::algorithm::to_lower_copy(str_rep) == "yes" || +                        boost::algorithm::to_lower_copy(str_rep) == "y") { +                        _value = true; +                    } else if (boost::algorithm::to_lower_copy(str_rep) == "false" || +                            boost::algorithm::to_lower_copy(str_rep) == "no" || +                            boost::algorithm::to_lower_copy(str_rep) == "n") { +                        _value = false; +                    } else { +                        throw uhd::value_error(str(boost::format( +                            "Error parsing boolean parameter %s: %s.") % +                            key() % ex.what() +                        )); +                    } +                } +            } +            inline virtual std::string to_string() const { +                return key() + "=" + (get() ? "true" : "false"); +            } +        private: +            bool _value; +        }; + +    public: //Methods +        constrained_device_args_t() {} +        virtual ~constrained_device_args_t() {} + +        void parse(const std::string& str_args) { +            device_addr_t dev_args(str_args); +            _parse(dev_args); +        } + +        void parse(const device_addr_t& dev_args) { +            _parse(dev_args); +        } + +        inline virtual std::string to_string() const = 0; + +    protected:  //Methods +        //Override _parse to provide an implementation to parse all +        //client specific device args +        virtual void _parse(const device_addr_t& dev_args) = 0; + +        /*! +         * Utility: Ensure that the value of the device arg is between min and max +         */ +        template<typename num_data_t> +        static inline void _enforce_range(const num_arg<num_data_t>& arg, const num_data_t& min, const num_data_t& max) { +            if (arg.get() > max || arg.get() < min) { +                throw uhd::value_error(str(boost::format( +                    "Invalid device arg value: %s (Minimum: %s, Maximum: %s)") % +                    arg.to_string() % +                    boost::lexical_cast<std::string>(min) % boost::lexical_cast<std::string>(max))); +            } +        } + +        /*! +         * Utility: Ensure that the value of the device arg is is contained in valid_values +         */ +        template<typename arg_t, typename data_t> +        static inline void _enforce_discrete(const arg_t& arg, const std::vector<data_t>& valid_values) { +            bool match = false; +            BOOST_FOREACH(const data_t& val, valid_values) { +                if (val == arg.get()) { +                    match = true; +                    break; +                } +            } +            if (!match) { +                std::string valid_values_str; +                for (size_t i = 0; i < valid_values.size(); i++) { +                    valid_values_str += ((i==0)?"":", ") + boost::lexical_cast<std::string>(valid_values[i]); +                    throw uhd::value_error(str(boost::format( +                        "Invalid device arg value: %s (Valid: {%s})") % +                        arg.to_string() % valid_values_str +                    )); +                } +            } +        } +    }; +}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP */ diff --git a/host/lib/usrp/common/fw_comm_protocol.h b/host/lib/usrp/common/fw_comm_protocol.h new file mode 100644 index 000000000..14adb33a9 --- /dev/null +++ b/host/lib/usrp/common/fw_comm_protocol.h @@ -0,0 +1,102 @@ +// +// 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_FW_COMM_PROTOCOL +#define INCLUDED_FW_COMM_PROTOCOL + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +/*! + * Structs and constants for communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define FW_COMM_PROTOCOL_SIGNATURE  0xACE3 +#define FW_COMM_PROTOCOL_VERSION    0 +#define FW_COMM_MAX_DATA_WORDS      16 +#define FW_COMM_PROTOCOL_MTU        256 + +#define FW_COMM_FLAGS_ACK           0x00000001 +#define FW_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define FW_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define FW_COMM_CMD_ECHO            0x00000000 +#define FW_COMM_CMD_POKE32          0x00000010 +#define FW_COMM_CMD_PEEK32          0x00000020 +#define FW_COMM_CMD_BLOCK_POKE32    0x00000030 +#define FW_COMM_CMD_BLOCK_PEEK32    0x00000040 + +#define FW_COMM_ERR_PKT_ERROR       0x80000000 +#define FW_COMM_ERR_CMD_ERROR       0x40000000 +#define FW_COMM_ERR_SIZE_ERROR      0x20000000 + +#define FW_COMM_GENERATE_ID(prod)   ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0)  | \ +                                     (((uint32_t) prod)                       << 16) | \ +                                     (((uint32_t) FW_COMM_PROTOCOL_VERSION)   << 24)) + +#define FW_COMM_GET_PROTOCOL_SIG(id) ((uint16_t)(id & 0xFFFF)) +#define FW_COMM_GET_PRODUCT_ID(id)   ((uint8_t)(id >> 16)) +#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24)) + +typedef struct +{ +    uint32_t id;            //Protocol and device identifier +    uint32_t flags;         //Holds commands and ack messages +    uint32_t sequence;      //Sequence number (specific to FW communication transactions) +    uint32_t data_words;    //Number of data words in payload +    uint32_t addr;          //Address field for the command in flags +    uint32_t data[FW_COMM_MAX_DATA_WORDS];  //Data field for the command in flags +} fw_comm_pkt_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +typedef void (*poke32_func)(const uint32_t addr, const uint32_t data); +typedef uint32_t (*peek32_func)(const uint32_t addr); + +/*! + * Process a firmware communication packet and compute a response. + * Args: + * - (in) request: Pointer to the request struct + * - (out) response: Pointer to the response struct + * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering) + * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke + * - return value: Send a response packet + */ +bool process_fw_comm_protocol_pkt( +    const fw_comm_pkt_t* request, +    fw_comm_pkt_t* response, +    uint8_t product_id, +    uint32_t iface_id, +    poke32_func poke_callback, +    peek32_func peek_callback +); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_FW_COMM_PROTOCOL */ diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp new file mode 100644 index 000000000..ef541e37f --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "usrp3_fw_ctrl_iface.hpp" + +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include <boost/foreach.hpp> +#include "fw_comm_protocol.h" + +namespace uhd { namespace usrp { namespace usrp3 { + +//---------------------------------------------------------- +// Factory method +//---------------------------------------------------------- +uhd::wb_iface::sptr usrp3_fw_ctrl_iface::make( +    uhd::transport::udp_simple::sptr udp_xport, +    const boost::uint16_t product_id, +    const bool verbose) +{ +    return wb_iface::sptr(new usrp3_fw_ctrl_iface(udp_xport, product_id, verbose)); +} + +//---------------------------------------------------------- +// udp_fw_ctrl_iface +//---------------------------------------------------------- + +usrp3_fw_ctrl_iface::usrp3_fw_ctrl_iface( +    uhd::transport::udp_simple::sptr udp_xport, +    const boost::uint16_t product_id, +    const bool verbose) : +    _product_id(product_id), _verbose(verbose), _udp_xport(udp_xport), +    _seq_num(0) +{ +    flush(); +    peek32(0); +} + +usrp3_fw_ctrl_iface::~usrp3_fw_ctrl_iface() +{ +    flush(); +} + +void usrp3_fw_ctrl_iface::flush() +{ +    boost::mutex::scoped_lock lock(_mutex); +    _flush(); +} + +void usrp3_fw_ctrl_iface::poke32(const wb_addr_type addr, const boost::uint32_t data) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    for (size_t i = 1; i <= NUM_RETRIES; i++) { +        try { +            _poke32(addr, data); +            return; +        } catch(const std::exception &ex) { +            const std::string error_msg = str(boost::format( +                "udp fw poke32 failure #%u\n%s") % i % ex.what()); +            if (_verbose) UHD_MSG(warning) << error_msg << std::endl; +            if (i == NUM_RETRIES) throw uhd::io_error(error_msg); +        } +    } +} + +boost::uint32_t usrp3_fw_ctrl_iface::peek32(const wb_addr_type addr) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    for (size_t i = 1; i <= NUM_RETRIES; i++) { +        try { +            return _peek32(addr); +        } catch(const std::exception &ex) { +            const std::string error_msg = str(boost::format( +                "udp fw peek32 failure #%u\n%s") % i % ex.what()); +            if (_verbose) UHD_MSG(warning) << error_msg << std::endl; +            if (i == NUM_RETRIES) throw uhd::io_error(error_msg); +        } +    } +    return 0; +} + +void usrp3_fw_ctrl_iface::_poke32(const wb_addr_type addr, const boost::uint32_t data) +{ +    //Load request struct +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_POKE32); +    request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); +    request.addr = uhd::htonx(addr); +    request.data_words = 1; +    request.data[0] = uhd::htonx(data); + +    //Send request +    _flush(); +    _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //Recv reply +    fw_comm_pkt_t reply; +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); +    if (nbytes == 0) throw uhd::io_error("udp fw poke32 - reply timed out"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(reply)); +    UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); +    UHD_ASSERT_THROW(flags & FW_COMM_CMD_POKE32); +    UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); +    UHD_ASSERT_THROW(reply.sequence == request.sequence); +    UHD_ASSERT_THROW(reply.addr == request.addr); +    UHD_ASSERT_THROW(reply.data[0] == request.data[0]); +} + +boost::uint32_t usrp3_fw_ctrl_iface::_peek32(const wb_addr_type addr) +{ +    //Load request struct +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_PEEK32); +    request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); +    request.addr = uhd::htonx(addr); +    request.data_words = 1; +    request.data[0] = 0; + +    //Send request +    _flush(); +    _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //Recv reply +    fw_comm_pkt_t reply; +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); +    if (nbytes == 0) throw uhd::io_error("udp fw peek32 - reply timed out"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(reply)); +    UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); +    UHD_ASSERT_THROW(flags & FW_COMM_CMD_PEEK32); +    UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); +    UHD_ASSERT_THROW(reply.sequence == request.sequence); +    UHD_ASSERT_THROW(reply.addr == request.addr); + +    //return result! +    return uhd::ntohx<boost::uint32_t>(reply.data[0]); +} + +void usrp3_fw_ctrl_iface::_flush(void) +{ +    char buff[FW_COMM_PROTOCOL_MTU] = {}; +    while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) { +        /*NOP*/ +    } +} + +std::vector<std::string> usrp3_fw_ctrl_iface::discover_devices( +    const std::string& addr_hint, const std::string& port, +    boost::uint16_t product_id) +{ +    std::vector<std::string> addrs; + +    //Create a UDP transport to communicate: +    //Some devices will cause a throw when opened for a broadcast address. +    //We print and recover so the caller can loop through all bcast addrs. +    uhd::transport::udp_simple::sptr udp_bcast_xport; +    try { +        udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port); +    } catch(const std::exception &e) { +        UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s") +        % addr_hint % e.what() << std::endl; +        return addrs; +    } + +    //Send dummy request +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); +    request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); +    udp_bcast_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //loop for replies until timeout +    while (true) { +        char buff[FW_COMM_PROTOCOL_MTU] = {}; +        const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050); +        if (nbytes != sizeof(fw_comm_pkt_t)) break; //No more responses or responses are invalid + +        const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; +        if (request.id       == reply->id && +            request.flags    == reply->flags && +            request.sequence == reply->sequence) +        { +            addrs.push_back(udp_bcast_xport->get_recv_addr()); +        } +    } + +    return addrs; +} + +boost::uint32_t usrp3_fw_ctrl_iface::get_iface_id( +    const std::string& addr, const std::string& port, +    boost::uint16_t product_id) +{ +    uhd::transport::udp_simple::sptr udp_xport = +        uhd::transport::udp_simple::make_connected(addr, port); + +    //Send dummy request +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); +    request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); +    udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //loop for replies until timeout +    char buff[FW_COMM_PROTOCOL_MTU] = {}; +    const size_t nbytes = udp_xport->recv(boost::asio::buffer(buff), 1.0); + +    const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; +    if (nbytes            >  0 && +        request.id        == reply->id && +        request.flags     == reply->flags && +        request.sequence  == reply->sequence) +    { +        return uhd::ntohx<boost::uint32_t>(reply->data[0]); +    } else { +        throw uhd::io_error("udp get_iface_id - bad response"); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp new file mode 100644 index 000000000..33286861b --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp @@ -0,0 +1,72 @@ +// +// 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_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP +#define INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread/mutex.hpp> +#include <vector> + +namespace uhd { namespace usrp { namespace usrp3 { + +class usrp3_fw_ctrl_iface : public uhd::wb_iface +{ +public: +    usrp3_fw_ctrl_iface( +        uhd::transport::udp_simple::sptr udp_xport, +        const boost::uint16_t product_id, +        const bool verbose); +    virtual ~usrp3_fw_ctrl_iface(); + +    // -- uhd::wb_iface -- +    void poke32(const wb_addr_type addr, const boost::uint32_t data); +    boost::uint32_t peek32(const wb_addr_type addr); +    void flush(); + +    static uhd::wb_iface::sptr make( +        uhd::transport::udp_simple::sptr udp_xport, +        const boost::uint16_t product_id, +        const bool verbose = true); +    // -- uhd::wb_iface -- + +    static std::vector<std::string> discover_devices( +        const std::string& addr_hint, const std::string& port, +        boost::uint16_t product_id); + +    static boost::uint32_t get_iface_id( +        const std::string& addr, const std::string& port, +        boost::uint16_t product_id); + +private: +    void _poke32(const wb_addr_type addr, const boost::uint32_t data); +    boost::uint32_t _peek32(const wb_addr_type addr); +    void _flush(void); + +    const boost::uint16_t               _product_id; +    const bool                          _verbose; +    uhd::transport::udp_simple::sptr    _udp_xport; +    boost::uint32_t                     _seq_num; +    boost::mutex                        _mutex; + +    static const size_t NUM_RETRIES = 3; +}; + +}}} //namespace + +#endif //INCLUDED_LIBUHD_USRP_USRP3_USRP3_UDP_FW_CTRL_HPP diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index f28ae040f..720f1d957 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -40,4 +40,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/gpio_atr_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_3000.cpp  ) diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp new file mode 100644 index 000000000..1a9d5dd5c --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp @@ -0,0 +1,397 @@ +// +// Copyright 2015 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 "dma_fifo_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <uhd/utils/soft_register.hpp> +#include <uhd/utils/msg.hpp> + +using namespace uhd; + +#define SR_DRAM_BIST_BASE 16 + +dma_fifo_core_3000::~dma_fifo_core_3000(void) { +    /* NOP */ +} + +class dma_fifo_core_3000_impl : public dma_fifo_core_3000 +{ +protected: +    class rb_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR, /*width*/ 3, /*shift*/ 0);  //[2:0] + +        static const boost::uint32_t RB_FIFO_STATUS     = 0; +        static const boost::uint32_t RB_BIST_STATUS     = 1; +        static const boost::uint32_t RB_BIST_XFER_CNT   = 2; +        static const boost::uint32_t RB_BIST_CYC_CNT    = 3; + +        rb_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 0) +        { +            //Initial values +            set(ADDR, RB_FIFO_STATUS); +        } +    }; + +    class fifo_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(CLEAR_FIFO,           /*width*/  1, /*shift*/  0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_EN,       /*width*/  1, /*shift*/  1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(BURST_TIMEOUT,        /*width*/ 12, /*shift*/  4);  //[15:4] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_THRESH,   /*width*/ 16, /*shift*/ 16);  //[31:16] + +        fifo_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 4) +        { +            //Initial values +            set(CLEAR_FIFO, 1); +            set(RD_SUPPRESS_EN, 0); +            set(BURST_TIMEOUT, 256); +            set(RD_SUPPRESS_THRESH, 0); +        } +    }; + +    class base_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(BASE_ADDR, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        base_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 8) +        { +            //Initial values +            set(BASE_ADDR, 0x00000000); +        } +    }; + +    class addr_mask_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR_MASK, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        addr_mask_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 12) +        { +            //Initial values +            set(ADDR_MASK, 0xFF000000); +        } +    }; + +    class bist_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(GO,               /*width*/ 1, /*shift*/ 0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(CONTINUOUS_MODE,  /*width*/ 1, /*shift*/ 1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(TEST_PATT,        /*width*/ 2, /*shift*/ 4);  //[5:4] + +        static const boost::uint32_t TEST_PATT_ZERO_ONE     = 0; +        static const boost::uint32_t TEST_PATT_CHECKERBOARD = 1; +        static const boost::uint32_t TEST_PATT_COUNT        = 2; +        static const boost::uint32_t TEST_PATT_COUNT_INV    = 3; + +        bist_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 16) +        { +            //Initial values +            set(GO, 0); +            set(CONTINUOUS_MODE, 0); +            set(TEST_PATT, TEST_PATT_ZERO_ONE); +        } +    }; + +    class bist_cfg_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKTS,         /*width*/ 18, /*shift*/ 0);  //[17:0] +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKT_SIZE,     /*width*/ 13, /*shift*/ 18); //[30:18] +        UHD_DEFINE_SOFT_REG_FIELD(PKT_SIZE_RAMP,    /*width*/ 1,  /*shift*/ 31); //[31] + +        bist_cfg_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 20) +        { +            //Initial values +            set(MAX_PKTS, 0); +            set(MAX_PKT_SIZE, 0); +            set(PKT_SIZE_RAMP, 0); +        } +    }; + +    class bist_delay_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(TX_PKT_DELAY,     /*width*/ 16, /*shift*/ 0);  //[15:0] +        UHD_DEFINE_SOFT_REG_FIELD(RX_SAMP_DELAY,    /*width*/  8, /*shift*/ 16); //[23:16] + +        bist_delay_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 24) +        { +            //Initial values +            set(TX_PKT_DELAY, 0); +            set(RX_SAMP_DELAY, 0); +        } +    }; + +    class bist_sid_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SID,     /*width*/ 32, /*shift*/ 0);  //[31:0] + +        bist_sid_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 28) +        { +            //Initial values +            set(SID, 0); +        } +    }; + +public: +    class fifo_readback { +    public: +        fifo_readback(wb_iface::sptr iface,  const size_t base, const size_t rb_addr) : +            _iface(iface), _addr_reg(base), _rb_addr(rb_addr) +        { +            _addr_reg.initialize(*iface, true); +        } + +        bool is_fifo_instantiated() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        boost::uint32_t get_occupied_cnt() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x7FFFFFF; +        } + +        boost::uint32_t is_fifo_busy() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x40000000; +        } + +        struct bist_status_t { +            bool running; +            bool finished; +            boost::uint8_t error; +        }; + +        bist_status_t get_bist_status() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            boost::uint32_t st32 = _iface->peek32(_rb_addr) & 0xF; +            bist_status_t status; +            status.running = st32 & 0x1; +            status.finished = st32 & 0x2; +            status.error = static_cast<boost::uint8_t>((st32>>2) & 0x3); +            return status; +        } + +        bool is_ext_bist_supported() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        double get_xfer_ratio() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            boost::uint32_t xfer_cnt = 0, cyc_cnt = 0; +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_XFER_CNT); +            xfer_cnt = _iface->peek32(_rb_addr); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_CYC_CNT); +            cyc_cnt = _iface->peek32(_rb_addr); +            return (static_cast<double>(xfer_cnt)/cyc_cnt); +        } + +    private: +        wb_iface::sptr  _iface; +        rb_addr_reg_t   _addr_reg; +        const size_t    _rb_addr; +        boost::mutex    _mutex; +    }; + +public: +    dma_fifo_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): +        _iface(iface), _base(base), _fifo_readback(iface, base, readback), +        _fifo_ctrl_reg(base), _base_addr_reg(base), _addr_mask_reg(base), +        _bist_ctrl_reg(base), _bist_cfg_reg(base), _bist_delay_reg(base), _bist_sid_reg(base) +    { +        _fifo_ctrl_reg.initialize(*iface, true); +        _base_addr_reg.initialize(*iface, true); +        _addr_mask_reg.initialize(*iface, true); +        _bist_ctrl_reg.initialize(*iface, true); +        _bist_cfg_reg.initialize(*iface, true); +        _has_ext_bist = _fifo_readback.is_ext_bist_supported(); +        if (_has_ext_bist) { +            _bist_delay_reg.initialize(*iface, true); +            _bist_sid_reg.initialize(*iface, true); +        } +        flush(); +    } + +    virtual void flush() { +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Re-arm the FIFO +        _wait_for_fifo_empty(); +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0); +    } + +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) { +        //Validate parameters +        if (size < 8192) throw uhd::runtime_error("DMA FIFO must be larger than 8KiB"); +        boost::uint32_t size_mask = size - 1; +        if (size & size_mask) throw uhd::runtime_error("DMA FIFO size must be a power of 2"); + +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Write base address and mask +        _base_addr_reg.write(base_addr_reg_t::BASE_ADDR, base_addr); +        _addr_mask_reg.write(addr_mask_reg_t::ADDR_MASK, ~size_mask); + +        //Re-arm the FIFO +        flush(); +    } + +    virtual boost::uint32_t get_bytes_occupied() { +        return _fifo_readback.get_occupied_cnt() * 8; +    } + +    virtual bool ext_bist_supported() { +        return _fifo_readback.is_ext_bist_supported(); +    } + +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) { +        return run_ext_bist(finite, 0, 0, 0, timeout_ms); +    } + +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500 +    ) { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        _wait_for_bist_done(timeout_ms, true);          //Stop previous BIST and wait (if running) +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0);   //Reset + +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKTS, (2^18)-1); +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKT_SIZE, 8000); +        _bist_cfg_reg.set(bist_cfg_reg_t::PKT_SIZE_RAMP, 0); +        _bist_cfg_reg.flush(); + +        if (_has_ext_bist) { +            _bist_delay_reg.set(bist_delay_reg_t::RX_SAMP_DELAY, rx_samp_delay); +            _bist_delay_reg.set(bist_delay_reg_t::TX_PKT_DELAY, tx_pkt_delay); +            _bist_delay_reg.flush(); + +            _bist_sid_reg.write(bist_sid_reg_t::SID, sid); +        } else { +            if (rx_samp_delay != 0 || tx_pkt_delay != 0 || sid != 0) { +                throw uhd::not_implemented_error( +                    "dma_fifo_core_3000: Runtime delay and SID support only available on FPGA images with extended BIST enabled"); +            } +        } + +        _bist_ctrl_reg.set(bist_ctrl_reg_t::TEST_PATT, bist_ctrl_reg_t::TEST_PATT_COUNT); +        _bist_ctrl_reg.set(bist_ctrl_reg_t::CONTINUOUS_MODE, finite ? 0 : 1); +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 1); + +        if (!finite) { +            boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms)); +        } + +        _wait_for_bist_done(timeout_ms, !finite); +        if (!_fifo_readback.get_bist_status().finished) { +            throw uhd::runtime_error("dma_fifo_core_3000: DRAM BIST state machine is in a bad state."); +        } + +        return _fifo_readback.get_bist_status().error; +    } + +    virtual double get_bist_throughput(double fifo_clock_rate) { +        if (_has_ext_bist) { +            _wait_for_bist_done(1000); +            static const double BYTES_PER_CYC = 8; +            return _fifo_readback.get_xfer_ratio() * fifo_clock_rate * BYTES_PER_CYC; +        } else { +            throw uhd::not_implemented_error( +                "dma_fifo_core_3000: Throughput counter only available on FPGA images with extended BIST enabled"); +        } +    } + +private: +    void _wait_for_fifo_empty() +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.is_fifo_busy()) { +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > 100) break; +        } +    } + +    void _wait_for_bist_done(boost::uint32_t timeout_ms, bool force_stop = false) +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.get_bist_status().running) { +            if (force_stop) { +                _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0); +                force_stop = false; +            } +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > timeout_ms) break; +        } +    } + +private: +    wb_iface::sptr  _iface; +    const size_t    _base; +    boost::mutex    _mutex; +    bool            _has_ext_bist; + +    fifo_readback       _fifo_readback; +    fifo_ctrl_reg_t     _fifo_ctrl_reg; +    base_addr_reg_t     _base_addr_reg; +    addr_mask_reg_t     _addr_mask_reg; +    bist_ctrl_reg_t     _bist_ctrl_reg; +    bist_cfg_reg_t      _bist_cfg_reg; +    bist_delay_reg_t    _bist_delay_reg; +    bist_sid_reg_t      _bist_sid_reg; +}; + +// +// Static make function +// +dma_fifo_core_3000::sptr dma_fifo_core_3000::make(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    if (check(iface, set_base, rb_addr)) { +        return sptr(new dma_fifo_core_3000_impl(iface, set_base, rb_addr)); +    } else { +        throw uhd::runtime_error(""); +    } +} + +bool dma_fifo_core_3000::check(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    dma_fifo_core_3000_impl::fifo_readback fifo_rb(iface, set_base, rb_addr); +    return fifo_rb.is_fifo_instantiated(); +} diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/usrp/cores/dma_fifo_core_3000.hpp new file mode 100644 index 000000000..41430e5c3 --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.hpp @@ -0,0 +1,86 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <uhd/types/wb_iface.hpp> + + +class dma_fifo_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<dma_fifo_core_3000> sptr; +    virtual ~dma_fifo_core_3000(void) = 0; + +    /*! +     * Create a DMA FIFO controller using the given bus, settings and readback base +     * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA +     */ +    static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Check if a DMA FIFO is instantiated in the FPGA +     */ +    static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Flush the DMA FIFO. Will clear all contents. +     */ +    virtual void flush() = 0; + +    /*! +     * Resize and rebase the DMA FIFO. Will clear all contents. +     */ +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) = 0; + +    /*! +     * Get the (approx) number of bytes currently in the DMA FIFO +     */ +    virtual boost::uint32_t get_bytes_occupied() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO +     */ +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Is extended BIST supported +     */ +    virtual bool ext_bist_supported() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO (extended BIST only) +     */ +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Get the throughput measured from the last invocation of the BIST (extended BIST only) +     */ +    virtual double get_bist_throughput(double fifo_clock_rate) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_atr_3000.cpp b/host/lib/usrp/cores/gpio_atr_3000.cpp new file mode 100644 index 000000000..3e0aa1f03 --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.cpp @@ -0,0 +1,297 @@ +// +// Copyright 2011,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 "gpio_atr_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/utils/soft_register.hpp> + +using namespace uhd; +using namespace usrp; + +//------------------------------------------------------------- +// gpio_atr_3000 +//------------------------------------------------------------- + +#define REG_ATR_IDLE_OFFSET     (base + 0) +#define REG_ATR_RX_OFFSET       (base + 4) +#define REG_ATR_TX_OFFSET       (base + 8) +#define REG_ATR_FDX_OFFSET      (base + 12) +#define REG_DDR_OFFSET          (base + 16) +#define REG_ATR_DISABLE_OFFSET  (base + 20) + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000_impl : public gpio_atr_3000{ +public: +    gpio_atr_3000_impl( +        wb_iface::sptr iface, +        const wb_iface::wb_addr_type base, +        const wb_iface::wb_addr_type rb_addr = READBACK_DISABLED +    ): +        _iface(iface), _rb_addr(rb_addr), +        _atr_idle_reg(REG_ATR_IDLE_OFFSET, _atr_disable_reg), +        _atr_rx_reg(REG_ATR_RX_OFFSET), +        _atr_tx_reg(REG_ATR_TX_OFFSET), +        _atr_fdx_reg(REG_ATR_FDX_OFFSET), +        _ddr_reg(REG_DDR_OFFSET), +        _atr_disable_reg(REG_ATR_DISABLE_OFFSET) +    { +        _atr_idle_reg.initialize(*_iface, true); +        _atr_rx_reg.initialize(*_iface, true); +        _atr_tx_reg.initialize(*_iface, true); +        _atr_fdx_reg.initialize(*_iface, true); +        _ddr_reg.initialize(*_iface, true); +        _atr_disable_reg.initialize(*_iface, true); +    } + +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) +    { +        //Each bit in the "ATR Disable" register determines whether the respective bit in the GPIO +        //output bus is driven by the ATR engine or a static register. +        //For each bit position, a 1 means that the bit is static and 0 means that the bit +        //is driven by the ATR state machine. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _atr_disable_reg.set_with_mask((mode==MODE_ATR) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _atr_disable_reg.flush(); +    } + +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) +    { +        //Each bit in the "DDR" register determines whether the respective bit in the GPIO +        //bus is an input or an output. +        //For each bit position, a 1 means that the bit is an output and 0 means that the bit +        //is an input. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _ddr_reg.set_with_mask((dir==DDR_INPUT) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _ddr_reg.flush(); +    } + +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) +    { +        //Set the value of the specified ATR register. For bits with ATR Disable set to 1, +        //the IDLE register will hold the output state +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        masked_reg_t* reg = NULL; +        switch (atr) { +            case ATR_REG_IDLE:          reg = &_atr_idle_reg; break; +            case ATR_REG_RX_ONLY:       reg = &_atr_rx_reg;   break; +            case ATR_REG_TX_ONLY:       reg = &_atr_tx_reg;   break; +            case ATR_REG_FULL_DUPLEX:   reg = &_atr_fdx_reg;  break; +            default:                    reg = &_atr_idle_reg; break; +        } +        //For protection we only write to bits that have the mode ATR by masking the user +        //specified "mask" with ~atr_disable. +        reg->set_with_mask(value, mask); +        reg->flush(); +    } + +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) { +        //Set the value of the specified GPIO output register. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. + +        //For protection we only write to bits that have the mode GPIO by masking the user +        //specified "mask" with atr_disable. +        _atr_idle_reg.set_gpio_out_with_mask(value, mask); +        _atr_idle_reg.flush(); +    } + +    virtual boost::uint32_t read_gpio() +    { +        //Read the state of the GPIO pins +        //If a pin is configured as an input, reads the actual value of the pin +        //If a pin is configured as an output, reads the last value written to the pin +        if (_rb_addr != READBACK_DISABLED) { +            return _iface->peek32(_rb_addr); +        } else { +            throw uhd::runtime_error("read_gpio not supported for write-only interface."); +        } +    } + +    inline virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) +    { +        //An attribute based API to configure all settings for the GPIO bus in one function +        //call. This API does not have a mask so it configures all bits at the same time. +        switch (attr) +        { +        case GPIO_CTRL: +            set_atr_mode(MODE_ATR, value);   //Configure mode=ATR for all bits that are set +            set_atr_mode(MODE_GPIO, ~value); //Configure mode=GPIO for all bits that are unset +            break; +        case GPIO_DDR: +            set_gpio_ddr(DDR_OUTPUT, value); //Configure as output for all bits that are set +            set_gpio_ddr(DDR_INPUT, ~value); //Configure as input for all bits that are unset +            break; +        case GPIO_OUT: +            //Only set bits that are driven statically +            set_gpio_out(value); +            break; +        case GPIO_ATR_0X: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_IDLE, value); +            break; +        case GPIO_ATR_RX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_RX_ONLY, value); +            break; +        case GPIO_ATR_TX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_TX_ONLY, value); +            break; +        case GPIO_ATR_XX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_FULL_DUPLEX, value); +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +private: +    //Special RB addr value to indicate no readback +    //This value is invalid as a real address because it is not a multiple of 4 +    static const wb_iface::wb_addr_type READBACK_DISABLED = 0xFFFFFFFF; + +    class masked_reg_t : public uhd::soft_reg32_wo_t { +    public: +        masked_reg_t(const wb_iface::wb_addr_type offset): uhd::soft_reg32_wo_t(offset) { +            set(REGISTER, 0); +        } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            set(REGISTER, (value&mask)|(get(REGISTER)&(~mask))); +        } + +        virtual void flush() { +            uhd::soft_reg32_wo_t::flush(); +        } +    }; + +    class atr_idle_reg_t : public masked_reg_t { +    public: +        atr_idle_reg_t(const wb_iface::wb_addr_type offset, masked_reg_t& atr_disable_reg): +            masked_reg_t(offset), +            _atr_idle_cache(0), _gpio_out_cache(0), +            _atr_disable_reg(atr_disable_reg) +        { } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _atr_idle_cache = (value&mask)|(_atr_idle_cache&(~mask)); +        } + +        void set_gpio_out_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _gpio_out_cache = (value&mask)|(_gpio_out_cache&(~mask)); +        } + +        virtual void flush() { +            set(REGISTER, +                (_atr_idle_cache & (~_atr_disable_reg.get(REGISTER))) | +                (_gpio_out_cache & _atr_disable_reg.get(REGISTER)) +            ); +            masked_reg_t::flush(); +        } + +    private: +        boost::uint32_t _atr_idle_cache; +        boost::uint32_t _gpio_out_cache; +        masked_reg_t&   _atr_disable_reg; +    }; + +    wb_iface::sptr          _iface; +    wb_iface::wb_addr_type  _rb_addr; +    atr_idle_reg_t          _atr_idle_reg; +    masked_reg_t            _atr_rx_reg; +    masked_reg_t            _atr_tx_reg; +    masked_reg_t            _atr_fdx_reg; +    masked_reg_t            _ddr_reg; +    masked_reg_t            _atr_disable_reg; +}; + +gpio_atr_3000::sptr gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new gpio_atr_3000_impl(iface, base, rb_addr)); +} + +gpio_atr_3000::sptr gpio_atr_3000::make_write_only( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base +) { +    gpio_atr_3000::sptr gpio_iface(new gpio_atr_3000_impl(iface, base)); +    gpio_iface->set_gpio_ddr(DDR_OUTPUT, MASK_SET_ALL); +    return gpio_iface; +} + +//------------------------------------------------------------- +// db_gpio_atr_3000 +//------------------------------------------------------------- + +class db_gpio_atr_3000_impl : public gpio_atr_3000_impl, public db_gpio_atr_3000 { +public: +    db_gpio_atr_3000_impl(wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr): +        gpio_atr_3000_impl(iface, base, rb_addr) { /* NOP */ } + +    inline void set_pin_ctrl(const db_unit_t unit, const boost::uint16_t value) +    { +        gpio_atr_3000_impl::set_atr_mode(MODE_ATR,  compute_mask(unit, value)); +        gpio_atr_3000_impl::set_atr_mode(MODE_GPIO, compute_mask(unit, ~value)); +    } + +    inline void set_gpio_ddr(const db_unit_t unit, const boost::uint16_t value) +    { +        gpio_atr_3000_impl::set_gpio_ddr(DDR_OUTPUT, compute_mask(unit, value)); +        gpio_atr_3000_impl::set_gpio_ddr(DDR_INPUT,  compute_mask(unit, ~value)); +    } + +    inline void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint16_t value) +    { +        gpio_atr_3000_impl::set_atr_reg(atr, +            static_cast<boost::uint32_t>(value) << compute_shift(unit), +            compute_mask(unit, 0xFFFF)); +    } + +    inline void set_gpio_out(const db_unit_t unit, const boost::uint16_t value) +    { +        gpio_atr_3000_impl::set_gpio_out( +            static_cast<boost::uint32_t>(value) << compute_shift(unit), +            compute_mask(unit, 0xFFFF)); +    } + +    inline boost::uint16_t read_gpio(const db_unit_t unit) +    { +        return boost::uint16_t(gpio_atr_3000_impl::read_gpio() >> compute_shift(unit)); +    } + +private: +    inline boost::uint32_t compute_shift(const db_unit_t unit) { +        return (unit == dboard_iface::UNIT_RX) ? 0 : 16; +    } + +    inline boost::uint32_t compute_mask(const db_unit_t unit, const boost::uint16_t mask) { +        return static_cast<boost::uint32_t>(mask) << (compute_shift(unit)); +    } +}; + +db_gpio_atr_3000::sptr db_gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new db_gpio_atr_3000_impl(iface, base, rb_addr)); +} + +}}} diff --git a/host/lib/usrp/cores/gpio_atr_3000.hpp b/host/lib/usrp/cores/gpio_atr_3000.hpp new file mode 100644 index 000000000..b30cd3b85 --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.hpp @@ -0,0 +1,175 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000 : boost::noncopyable { +public: +    typedef boost::shared_ptr<gpio_atr_3000> sptr; + +    static const boost::uint32_t MASK_SET_ALL = 0xFFFFFFFF; + +    virtual ~gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Create a write-only GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     */ +    static sptr make_write_only( +        uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base); + +    /*! +     * Select the ATR mode for all bits in the mask +     * +     * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) = 0; + +    /*! +     * Select the data direction for all bits in the mask +     * +     * \param dir the direction {OUTPUT, INPUT} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) = 0; + +    /*! +     * Write the specified (masked) value to the ATR register +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Write to a static GPIO output +     * +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \return the value read back +     */ +    virtual boost::uint32_t read_gpio() = 0; + +    /*! +     * Set a GPIO attribute +     * +     * \param attr the attribute to set +     * \param value the value to write to the attribute +     */ +    virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) = 0; +}; + +class db_gpio_atr_3000 { +public: +    typedef boost::shared_ptr<db_gpio_atr_3000> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t db_unit_t; + +    virtual ~db_gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object for a daughterboard connector +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Configure the GPIO mode for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode +     */ +    virtual void set_pin_ctrl(const db_unit_t unit, const boost::uint16_t value) = 0; + +    /*! +     * Configure the direction for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input +     */ +    virtual void set_gpio_ddr(const db_unit_t unit, const boost::uint16_t value) = 0; + +    /*! +     * Write the specified value to the ATR register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value the value to write +     */ +    virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint16_t value) = 0; + +    /*! +     * Write the specified value to the GPIO register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     */ +    virtual void set_gpio_out(const db_unit_t unit, const boost::uint16_t value) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \return the value read back +     */ +    virtual boost::uint16_t read_gpio(const db_unit_t unit) = 0; +}; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 4f1c25a0b..05a689845 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -77,10 +77,10 @@ private:      }      void update(void){ -        this->update(dboard_iface::ATR_REG_IDLE, REG_GPIO_IDLE); -        this->update(dboard_iface::ATR_REG_TX_ONLY, REG_GPIO_TX_ONLY); -        this->update(dboard_iface::ATR_REG_RX_ONLY, REG_GPIO_RX_ONLY); -        this->update(dboard_iface::ATR_REG_FULL_DUPLEX, REG_GPIO_BOTH); +        this->update(gpio_atr::ATR_REG_IDLE, REG_GPIO_IDLE); +        this->update(gpio_atr::ATR_REG_TX_ONLY, REG_GPIO_TX_ONLY); +        this->update(gpio_atr::ATR_REG_RX_ONLY, REG_GPIO_RX_ONLY); +        this->update(gpio_atr::ATR_REG_FULL_DUPLEX, REG_GPIO_BOTH);      }      void update(const atr_reg_t atr, const size_t addr){ @@ -122,17 +122,17 @@ public:      }      void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ -        if (atr == dboard_iface::ATR_REG_IDLE)        _iface->poke32(REG_GPIO_IDLE, value); -        if (atr == dboard_iface::ATR_REG_TX_ONLY)     _iface->poke32(REG_GPIO_TX_ONLY, value); -        if (atr == dboard_iface::ATR_REG_RX_ONLY)     _iface->poke32(REG_GPIO_RX_ONLY, value); -        if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value); +        if (atr == gpio_atr::ATR_REG_IDLE)        _iface->poke32(REG_GPIO_IDLE, value); +        if (atr == gpio_atr::ATR_REG_TX_ONLY)     _iface->poke32(REG_GPIO_TX_ONLY, value); +        if (atr == gpio_atr::ATR_REG_RX_ONLY)     _iface->poke32(REG_GPIO_RX_ONLY, value); +        if (atr == gpio_atr::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value);      }      void set_all_regs(const boost::uint32_t value){ -        this->set_atr_reg(dboard_iface::ATR_REG_IDLE,        value); -        this->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY,     value); -        this->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY,     value); -        this->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value); +        this->set_atr_reg(gpio_atr::ATR_REG_IDLE,        value); +        this->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value); +        this->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value); +        this->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);      }  private: diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index e22834fd9..c60507792 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp>  #include <boost/assign.hpp>  #include <boost/cstdint.hpp>  #include <boost/utility.hpp> @@ -27,28 +28,6 @@  #include <uhd/types/wb_iface.hpp>  #include <map> -typedef enum { -    GPIO_CTRL, -    GPIO_DDR, -    GPIO_OUT, -    GPIO_ATR_0X, -    GPIO_ATR_RX, -    GPIO_ATR_TX, -    GPIO_ATR_XX -} gpio_attr_t; - -typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; -static const gpio_attr_map_t gpio_attr_map = -    boost::assign::map_list_of -        (GPIO_CTRL,   "CTRL") -        (GPIO_DDR,    "DDR") -        (GPIO_OUT,    "OUT") -        (GPIO_ATR_0X, "ATR_0X") -        (GPIO_ATR_RX, "ATR_RX") -        (GPIO_ATR_TX, "ATR_TX") -        (GPIO_ATR_XX, "ATR_XX") -; -  class gpio_core_200 : boost::noncopyable{  public:      typedef boost::shared_ptr<gpio_core_200> sptr; diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp index 71a2b7e21..c76b384d9 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.cpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp @@ -18,9 +18,11 @@  #include "tx_vita_core_3000.hpp"  #include <uhd/utils/safe_call.hpp> -#define REG_CTRL_ERROR_POLICY           _base + 0 -#define REG_DEFRAMER_CYCLE_FC_UPS       _base + 2*4 + 0 -#define REG_DEFRAMER_PACKET_FC_UPS      _base + 2*4 + 4 +#define REG_CTRL_ERROR_POLICY       (_base + 0) +#define REG_FC_PRE_RADIO_RESP_BASE  (_base + 2*4) +#define REG_FC_PRE_FIFO_RESP_BASE   (_base + 4*4) +#define REG_CTRL_FC_CYCLE_OFFSET    (0*4) +#define REG_CTRL_FC_PACKET_OFFSET   (1*4)  using namespace uhd; @@ -32,12 +34,22 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000  {      tx_vita_core_3000_impl(          wb_iface::sptr iface, -        const size_t base +        const size_t base, +        fc_monitor_loc fc_location      ):          _iface(iface), -        _base(base) +        _base(base), +        _fc_base((fc_location==FC_PRE_RADIO or fc_location==FC_DEFAULT) ? +                    REG_FC_PRE_RADIO_RESP_BASE : REG_FC_PRE_FIFO_RESP_BASE), +        _fc_location(fc_location)      { -        this->set_tick_rate(1); //init to non zero +        if (fc_location != FC_DEFAULT) { +            //Turn off the other FC monitoring module +            const size_t other_fc_base = (fc_location==FC_PRE_RADIO) ? +                    REG_FC_PRE_FIFO_RESP_BASE : REG_FC_PRE_RADIO_RESP_BASE; +            _iface->poke32(other_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +            _iface->poke32(other_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        }          this->set_underflow_policy("next_packet");          this->clear();      } @@ -56,11 +68,6 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000          this->set_underflow_policy(_policy); //clears the seq      } -    void set_tick_rate(const double rate) -    { -        _tick_rate = rate; -    } -      void set_underflow_policy(const std::string &policy)      {          if (policy == "next_packet") @@ -89,23 +96,35 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000      void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up)      { -        if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0); -        else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff)); +        if (cycs_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, (1 << 31) | ((cycs_per_up) & 0xffffff)); -        if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0); -        else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff)); +        if (pkts_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, (1 << 31) | ((pkts_per_up) & 0xffff));      } -    wb_iface::sptr _iface; -    const size_t _base; -    double _tick_rate; -    std::string _policy; +    wb_iface::sptr  _iface; +    const size_t    _base; +    const size_t    _fc_base; +    std::string     _policy; +    fc_monitor_loc  _fc_location; +  };  tx_vita_core_3000::sptr tx_vita_core_3000::make(      wb_iface::sptr iface, +    const size_t base, +    fc_monitor_loc fc_location +) +{ +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, fc_location)); +} + +tx_vita_core_3000::sptr tx_vita_core_3000::make_no_radio_buff( +    wb_iface::sptr iface,      const size_t base  )  { -    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base)); +    //No internal radio buffer so only pre-radio monitoring is supported. +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, FC_DEFAULT));  } diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp index 4c0052d4f..bd0f20ba4 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.hpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -32,17 +32,27 @@ class tx_vita_core_3000 : boost::noncopyable  public:      typedef boost::shared_ptr<tx_vita_core_3000> sptr; +    enum fc_monitor_loc { +        FC_DEFAULT, +        FC_PRE_RADIO, +        FC_PRE_FIFO +    }; +      virtual ~tx_vita_core_3000(void) = 0;      static sptr make(          uhd::wb_iface::sptr iface, +        const size_t base, +        fc_monitor_loc fc_location = FC_PRE_RADIO +    ); + +    static sptr make_no_radio_buff( +        uhd::wb_iface::sptr iface,          const size_t base      );      virtual void clear(void) = 0; -    virtual void set_tick_rate(const double rate) = 0; -      virtual void setup(const uhd::stream_args_t &stream_args) = 0;      virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0; diff --git a/host/lib/usrp/cores/user_settings_core_3000.cpp b/host/lib/usrp/cores/user_settings_core_3000.cpp new file mode 100644 index 000000000..549264f57 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.cpp @@ -0,0 +1,85 @@ +// +// 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 "user_settings_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; + +#define REG_USER_SR_ADDR   _sr_base_addr + 0 +#define REG_USER_SR_DATA   _sr_base_addr + 4 +#define REG_USER_RB_ADDR   _sr_base_addr + 8 + +class user_settings_core_3000_impl : public user_settings_core_3000 { +public: +    user_settings_core_3000_impl( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr): +        _iface(iface), _sr_base_addr(sr_base_addr), _rb_reg_addr(rb_reg_addr) +    { +    } + +    void poke64(const wb_addr_type offset, const boost::uint64_t value) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("poke64: Incorrect address alignment"); +        poke32(offset, static_cast<boost::uint32_t>(value)); +        poke32(offset + 4, static_cast<boost::uint32_t>(value >> 32)); +    } + +    boost::uint64_t peek64(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("peek64: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_RB_ADDR, offset >> 3);  //Translate byte offset to 64-bit offset +        return _iface->peek64(_rb_reg_addr); +    } + +    void poke32(const wb_addr_type offset, const boost::uint32_t value) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("poke32: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_SR_ADDR, offset >> 2);   //Translate byte offset to 64-bit offset +        _iface->poke32(REG_USER_SR_DATA, value); +    } + +    boost::uint32_t peek32(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("peek32: Incorrect address alignment"); + +        boost::uint64_t value = peek64((offset >> 3) << 3); +        if ((offset & 0x7) == 0) { +            return static_cast<boost::uint32_t>(value); +        } else { +            return static_cast<boost::uint32_t>(value >> 32); +        } +    } + +private: +    wb_iface::sptr      _iface; +    const wb_addr_type  _sr_base_addr; +    const wb_addr_type  _rb_reg_addr; +    boost::mutex        _mutex; +}; + +wb_iface::sptr user_settings_core_3000::make(wb_iface::sptr iface, +    const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr) +{ +    return sptr(new user_settings_core_3000_impl(iface, sr_base_addr, rb_reg_addr)); +} diff --git a/host/lib/usrp/cores/user_settings_core_3000.hpp b/host/lib/usrp/cores/user_settings_core_3000.hpp new file mode 100644 index 000000000..6891b9e81 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.hpp @@ -0,0 +1,35 @@ +// +// 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/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class user_settings_core_3000 : public uhd::wb_iface { +public: +    virtual ~user_settings_core_3000() {} + +    static sptr make( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr); +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */ diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 1342c913d..3e7df9a39 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -248,15 +248,15 @@ rfx_xcvr::rfx_xcvr(      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, output_enables);      //setup the tx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | ANT_RX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | ANT_RX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);      //setup the rx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB);  }  rfx_xcvr::~rfx_xcvr(void){ @@ -272,14 +272,14 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){      //set the rx atr regs that change with antenna setting      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TXRX  | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX  | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | ANT_TXRX ); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TXRX  | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX  | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | ANT_TXRX );      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB |              ((ant == "TX/RX")? ANT_TXRX : ANT_RX2));      } @@ -292,12 +292,12 @@ void rfx_xcvr::set_tx_ant(const std::string &ant){      //set the tx atr regs that change with antenna setting      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_RX | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_RX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX | MIXER_ENB);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);      }  } diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index ce5166c4c..c575bba01 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -237,8 +237,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    //flash LEDs -    flash_leds(); +    //Initialize ATR registers after direction and pin ctrl configuration +    update_atr();      UHD_LOGV(often) << boost::format(          "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" @@ -265,39 +265,39 @@ void sbx_xcvr::update_atr(void){      //setup the tx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, 0 | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_IDLE, 0 | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS);      //setup the rx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_IDLE, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);      //set the RX atr regs that change with antenna setting      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_RX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \              | ((_rx_ant != "RX2")? ANT_TXRX : ANT_RX2));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_TX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_DIS \              | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_FULL_DUPLEX, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \              | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));      //set the TX atr regs that change with antenna setting      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, 0 | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_RX_ONLY, 0 | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS \              | ((_rx_ant != "RX2")? ANT_RX : ANT_TX));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, tx_pga0_iobits | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_TX_ONLY, tx_pga0_iobits | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \              | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, tx_pga0_iobits | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_FULL_DUPLEX, tx_pga0_iobits | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \              | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));  } @@ -352,45 +352,3 @@ sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) {      return sensor_value_t("LO", locked, "locked", "unlocked");  } - - -void sbx_xcvr::flash_leds(void) { -    //Remove LED gpios from ATR control temporarily and set to outputs -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, \ -            TX_LED_TXRX|TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, \ -            RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    //Put LED gpios back in ATR control and update atr -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -} - diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 7cb4b2d6b..db9f21f43 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -26,6 +26,7 @@  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/utils/assert_has.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <uhd/utils/static.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/shared_ptr.hpp> @@ -318,14 +319,14 @@ public:          write_gpio();          // Configure ATR -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, _tx_gpio_reg.atr_idle); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _tx_gpio_reg.atr_tx); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, _tx_gpio_reg.atr_rx); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _tx_gpio_reg.atr_full_duplex); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, _rx_gpio_reg.atr_idle); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _rx_gpio_reg.atr_tx); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _rx_gpio_reg.atr_rx); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _rx_gpio_reg.atr_full_duplex); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE, _tx_gpio_reg.atr_idle); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY, _tx_gpio_reg.atr_tx); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY, _tx_gpio_reg.atr_rx); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _tx_gpio_reg.atr_full_duplex); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, _rx_gpio_reg.atr_idle); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY, _rx_gpio_reg.atr_tx); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY, _rx_gpio_reg.atr_rx); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _rx_gpio_reg.atr_full_duplex);          // Engage ATR control (1 is ATR control, 0 is manual control)          _iface->set_pin_ctrl(dboard_iface::UNIT_TX, _tx_gpio_reg.atr_mask); @@ -683,6 +684,20 @@ private:          device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();          is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer");          UHD_LOGV(rarely) << boost::format("UBX TX: the requested frequency is %f MHz") % (freq/1e6) << std::endl; +        double target_pfd_freq = _tx_target_pfd_freq; +        if (is_int_n and tune_args.has_key("int_n_step")) +        { +            target_pfd_freq = tune_args.cast<double>("int_n_step", _tx_target_pfd_freq); +            if (target_pfd_freq > _tx_target_pfd_freq) +            { +                UHD_MSG(warning) +                    << boost::format("Requested int_n_step of %f MHz too large, clipping to %f MHz") +                    % (target_pfd_freq/1e6) +                    % (_tx_target_pfd_freq/1e6) +                    << std::endl; +                target_pfd_freq = _tx_target_pfd_freq; +            } +        }          // Clip the frequency to the valid range          freq = ubx_freq_range.clip(freq); @@ -704,10 +719,10 @@ private:              set_cpld_field(TXLB_SEL, 1);              set_cpld_field(TXHB_SEL, 0);              // Set LO1 to IF of 2100 MHz (offset from RX IF to reduce leakage) -            freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _txlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= (500*fMHz)) && (freq <= (800*fMHz))) @@ -717,7 +732,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (800*fMHz)) && (freq <= (1000*fMHz))) @@ -727,7 +742,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq > (1000*fMHz)) && (freq <= (2200*fMHz))) @@ -737,7 +752,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2200*fMHz)) && (freq <= (2500*fMHz))) @@ -747,7 +762,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2500*fMHz)) && (freq <= (6000*fMHz))) @@ -757,7 +772,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          } @@ -825,6 +840,20 @@ private:          property_tree::sptr subtree = this->get_rx_subtree();          device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();          is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +        double target_pfd_freq = _rx_target_pfd_freq; +        if (is_int_n and tune_args.has_key("int_n_step")) +        { +            target_pfd_freq = tune_args.cast<double>("int_n_step", _rx_target_pfd_freq); +            if (target_pfd_freq > _rx_target_pfd_freq) +            { +                UHD_MSG(warning) +                    << boost::format("Requested int_n_step of %f Mhz too large, clipping to %f MHz") +                    % (target_pfd_freq/1e6) +                    % (_rx_target_pfd_freq/1e6) +                    << std::endl; +                target_pfd_freq = _rx_target_pfd_freq; +            } +        }          // Clip the frequency to the valid range          freq = ubx_freq_range.clip(freq); @@ -848,10 +877,10 @@ private:              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0);              // Set LO1 to IF of 2380 MHz (2440 MHz filter center minus 60 MHz offset to minimize LO leakage) -            freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 100*fMHz) && (freq < 500*fMHz)) @@ -864,10 +893,10 @@ private:              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0);              // Set LO1 to IF of 2440 (center of filter) -            freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 500*fMHz) && (freq < 800*fMHz)) @@ -879,7 +908,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 800*fMHz) && (freq < 1000*fMHz)) @@ -891,7 +920,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq >= 1000*fMHz) && (freq < 1500*fMHz)) @@ -903,7 +932,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 1500*fMHz) && (freq < 2200*fMHz)) @@ -915,7 +944,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2200*fMHz) && (freq < 2500*fMHz)) @@ -927,7 +956,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2500*fMHz) && (freq <= 6000*fMHz)) @@ -939,7 +968,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          } diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index c8f2be155..dda2def95 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -112,14 +112,14 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO);      //setup ATR for the antenna switches (constant) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        ANT_RX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     ANT_RX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); - -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        ANT_TXRX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        ANT_RX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     ANT_RX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); + +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        ANT_TXRX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO);  }  wbx_simple::~wbx_simple(void){ @@ -138,14 +138,14 @@ void wbx_simple::set_rx_ant(const std::string &ant){      //write the new antenna setting to atr regs      if (_rx_ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TXRX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TXRX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     ANT_TXRX, ANTSW_IO);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2), ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY, ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2), ANTSW_IO);      }  } @@ -154,11 +154,11 @@ void wbx_simple::set_tx_ant(const std::string &ant){      //write the new antenna setting to atr regs      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX, ANTSW_IO);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO);      }  } diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 93047fb7a..78b5b2871 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -117,15 +117,15 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF435X_CE|RXBB_PDB|ADF435X_PDBRF|RX_ATTN_MASK);      //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); - -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  }  wbx_base::wbx_version2::~wbx_version2(void){ diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index 6927ae4e4..a5821ffc2 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -129,29 +129,29 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      //slip between bursts).  set TX gain iobits to min gain (max attenuation)      //when RX_ONLY or IDLE to suppress LO leakage      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, v3_tx_mod, \ +            gpio_atr::ATR_REG_IDLE, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, v3_tx_mod, \ +            gpio_atr::ATR_REG_RX_ONLY, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, v3_tx_mod, \ +            gpio_atr::ATR_REG_TX_ONLY, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, \ +            gpio_atr::ATR_REG_IDLE, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, \ +            gpio_atr::ATR_REG_TX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, \ +            gpio_atr::ATR_REG_RX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  } @@ -181,8 +181,8 @@ double wbx_base::wbx_version3::set_tx_gain(double gain, const std::string &name)          //write the new gain to tx gpio outputs          //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);      }      else UHD_THROW_INVALID_CODE_PATH();      return self_base->_tx_gains[name]; //shadow diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 81cdaefac..327ae675b 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -136,29 +136,29 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      //between bursts) set TX gain iobits to min gain (max attenuation) when      //RX_ONLY or IDLE to suppress LO leakage      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, v4_tx_mod, \ +            gpio_atr::ATR_REG_IDLE, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, v4_tx_mod, \ +            gpio_atr::ATR_REG_RX_ONLY, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, v4_tx_mod, \ +            gpio_atr::ATR_REG_TX_ONLY, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, v4_tx_mod, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, \ +            gpio_atr::ATR_REG_IDLE, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, \ +            gpio_atr::ATR_REG_TX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, \ +            gpio_atr::ATR_REG_RX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  } @@ -188,8 +188,8 @@ double wbx_base::wbx_version4::set_tx_gain(double gain, const std::string &name)          //write the new gain to tx gpio outputs          //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);      }      else UHD_THROW_INVALID_CODE_PATH(); diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 50c67991a..092f84548 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -315,12 +315,12 @@ xcvr2450::~xcvr2450(void){  void xcvr2450::spi_reset(void){      //spi reset mode: global enable = off, tx and rx enable = on -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, TX_ENB_TXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_ENB_RXIO | POWER_DOWN_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE, TX_ENB_TXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, RX_ENB_RXIO | POWER_DOWN_RXIO);      boost::this_thread::sleep(boost::posix_time::milliseconds(10));      //take it back out of spi reset mode and wait a bit -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_DIS_RXIO | POWER_UP_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, RX_DIS_RXIO | POWER_UP_RXIO);      boost::this_thread::sleep(boost::posix_time::milliseconds(10));  } @@ -337,16 +337,16 @@ void xcvr2450::update_atr(void){      int ad9515div  = (_ad9515div == 3)? AD9515DIV_3_TXIO : AD9515DIV_2_TXIO;      //set the tx registers -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        band_sel | ad9515div | TX_DIS_TXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     band_sel | ad9515div | TX_DIS_TXIO | rx_ant_sel); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     band_sel | ad9515div | TX_ENB_TXIO | tx_ant_sel); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, band_sel | ad9515div | TX_ENB_TXIO | xx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        band_sel | ad9515div | TX_DIS_TXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     band_sel | ad9515div | TX_DIS_TXIO | rx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     band_sel | ad9515div | TX_ENB_TXIO | tx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, band_sel | ad9515div | TX_ENB_TXIO | xx_ant_sel);      //set the rx registers -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO);  }  /*********************************************************************** diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt index 2a1e14eab..da77b85dc 100644 --- a/host/lib/usrp/e100/CMakeLists.txt +++ b/host/lib/usrp/e100/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP-E100 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF OFF) -  IF(ENABLE_E100)      INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt index 9c8aa29b9..68c3520e4 100644 --- a/host/lib/usrp/e300/CMakeLists.txt +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -24,8 +24,6 @@  ########################################################################  find_package(UDev) -LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_E300)      LIST(APPEND E300_SOURCES          ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index 594461518..471376337 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga {  static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 14; +static const boost::uint32_t COMPAT_MAJOR = 15;  static const boost::uint32_t COMPAT_MINOR = 0;  }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index a57c86c1d..fae09ba63 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -48,6 +48,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::transport;  namespace fs = boost::filesystem;  namespace asio = boost::asio; @@ -517,15 +518,15 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      ////////////////////////////////////////////////////////////////////      // internal gpios      //////////////////////////////////////////////////////////////////// -    gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); +    gpio_atr_3000::sptr fp_gpio = gpio_atr_3000::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO);      BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      {          _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) -            .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1)) +            .subscribe(boost::bind(&gpio_atr_3000::set_gpio_attr, fp_gpio, attr.first, _1))              .set(0);      }      _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") -        .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio)); +        .publish(boost::bind(&gpio_atr_3000::read_gpio, fp_gpio));      //////////////////////////////////////////////////////////////////// @@ -631,37 +632,6 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);  } -boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void e300_impl::_set_internal_gpio( -    gpio_core_200::sptr gpio, -    const gpio_attr_t attr, -    const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL: -        return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR: -        return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT: -        return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default: -        UHD_THROW_INVALID_CODE_PATH(); -    } -} -  uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx)  {      const boost::uint32_t st = @@ -1001,7 +971,8 @@ void e300_impl::_setup_radio(const size_t dspno)      ////////////////////////////////////////////////////////////////////      // Set up peripherals      //////////////////////////////////////////////////////////////////// -    perif.atr = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::GPIO)); +    perif.atr = gpio_atr_3000::make_write_only(perif.ctrl, radio::sr_addr(radio::GPIO)); +    perif.atr->set_atr_mode(MODE_ATR, 0xFFFFFFFF);      perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT));      perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);      perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); @@ -1050,7 +1021,6 @@ void e300_impl::_setup_radio(const size_t dspno)      // create tx dsp control objects      ////////////////////////////////////////////////////////////////////      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))          .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));      const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno);      perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); @@ -1315,11 +1285,11 @@ void e300_impl::_update_atrs(void)          if (enb_tx)              fd_reg |= tx_enables | xx_leds; -        gpio_core_200_32wo::sptr atr = _radio_perifs[instance].atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, oo_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rx_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd_reg); +        gpio_atr_3000::sptr atr = _radio_perifs[instance].atr; +        atr->set_atr_reg(ATR_REG_IDLE, oo_reg); +        atr->set_atr_reg(ATR_REG_RX_ONLY, rx_reg); +        atr->set_atr_reg(ATR_REG_TX_ONLY, tx_reg); +        atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd_reg);      }  } diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 595b42679..e9a0b4b9a 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -41,7 +41,7 @@  #include "tx_dsp_core_3000.hpp"  #include "ad9361_ctrl.hpp"  #include "ad936x_manager.hpp" -#include "gpio_core_200.hpp" +#include "gpio_atr_3000.hpp"  #include "e300_global_regs.hpp"  #include "e300_i2c.hpp" @@ -147,7 +147,7 @@ private: // types      struct radio_perifs_t      {          radio_ctrl_core_3000::sptr ctrl; -        gpio_core_200_32wo::sptr atr; +        gpio_atr::gpio_atr_3000::sptr atr;          time_core_3000::sptr time64;          rx_vita_core_3000::sptr framer;          rx_dsp_core_3000::sptr ddc; @@ -283,14 +283,6 @@ private: // methods      // get frontend lock sensor      uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); -    // internal gpios -    boost::uint8_t _get_internal_gpio(gpio_core_200::sptr); - -    void _set_internal_gpio( -        gpio_core_200::sptr gpio, -        const gpio_attr_t attr, -        const boost::uint32_t value); -  private: // members      uhd::device_addr_t                     _device_addr;      xport_t                                _xport_path; diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp index 29d250c8f..209a73077 100644 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -87,7 +87,6 @@ void e300_impl::_update_tick_rate(const double rate)              boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());          if (my_streamer)              my_streamer->set_tick_rate(rate); -        perif.deframer->set_tick_rate(_tick_rate);      }  } diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp index 846c759a4..74e45df00 100644 --- a/host/lib/usrp/e300/e300_regs.hpp +++ b/host/lib/usrp/e300/e300_regs.hpp @@ -41,7 +41,7 @@ static const uint32_t TIME       = 128;  static const uint32_t RX_DSP     = 144;  static const uint32_t TX_DSP     = 184;  static const uint32_t LEDS       = 195; -static const uint32_t FP_GPIO    = 200; +static const uint32_t FP_GPIO    = 201;  static const uint32_t RX_FRONT   = 208;  static const uint32_t TX_FRONT   = 216;  static const uint32_t CODEC_IDLE = 250; diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 396237e24..dbc0ebed2 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -438,8 +438,10 @@ public:       ******************************************************************/      void set_master_clock_rate(double rate, size_t mboard){          if (mboard != ALL_MBOARDS){ -            if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { +            if (_tree->exists(mb_root(mboard) / "auto_tick_rate") +                    and _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").get()) {                  _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); +                UHD_MSG(status) << "Setting master clock rate selection to 'manual'." << std::endl;              }              _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);              return; @@ -1346,10 +1348,10 @@ public:              if (attr == "CTRL") iface->set_pin_ctrl(unit, boost::uint16_t(value), boost::uint16_t(mask));              if (attr == "DDR") iface->set_gpio_ddr(unit, boost::uint16_t(value), boost::uint16_t(mask));              if (attr == "OUT") iface->set_gpio_out(unit, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_0X") iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_RX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_TX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_XX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_0X") iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_RX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_TX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_XX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask));          }      } @@ -1367,10 +1369,10 @@ public:              if (attr == "CTRL") return iface->get_pin_ctrl(unit);              if (attr == "DDR") return iface->get_gpio_ddr(unit);              if (attr == "OUT") return iface->get_gpio_out(unit); -            if (attr == "ATR_0X") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_IDLE); -            if (attr == "ATR_RX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY); -            if (attr == "ATR_TX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY); -            if (attr == "ATR_XX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX); +            if (attr == "ATR_0X") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_IDLE); +            if (attr == "ATR_RX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY); +            if (attr == "ATR_TX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY); +            if (attr == "ATR_XX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX);              if (attr == "READBACK") return iface->read_gpio(unit);          }          return 0; diff --git a/host/lib/usrp/n230/CMakeLists.txt b/host/lib/usrp/n230/CMakeLists.txt new file mode 100644 index 000000000..9eaccffba --- /dev/null +++ b/host/lib/usrp/n230/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the N230 support +######################################################################## +IF(ENABLE_N230) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_cores.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_resource_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_eeprom_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_stream_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_clk_pps_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_frontend_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_uart.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_image_loader.cpp +   ) +ENDIF(ENABLE_N230) diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp new file mode 100644 index 000000000..9d704b702 --- /dev/null +++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2013-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 "n230_clk_pps_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <stdexcept> +#include <cmath> +#include <cstdlib> + +namespace uhd { namespace usrp { namespace n230 { + +class n230_clk_pps_ctrl_impl : public n230_clk_pps_ctrl +{ +public: +    n230_clk_pps_ctrl_impl( +        ad9361_ctrl::sptr codec_ctrl, +        n230_ref_pll_ctrl::sptr ref_pll_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        fpga::core_pps_sel_reg_t& core_pps_sel, +        fpga::core_status_reg_t& core_status_reg, +        const std::vector<time_core_3000::sptr>& time_cores +    ): _codec_ctrl(codec_ctrl), +       _ref_pll_ctrl(ref_pll_ctrl), +       _core_misc_reg(core_misc_reg), +       _core_pps_sel_reg(core_pps_sel), +       _core_status_reg(core_status_reg), +       _time_cores(time_cores), +       _tick_rate(0.0), +       _clock_source("<undefined>"), +       _time_source("<undefined>") +    { +    } + +    virtual ~n230_clk_pps_ctrl_impl() +    { +    } + +    double set_tick_rate(const double rate) +    { +        UHD_MSG(status) << "Configuring a tick rate of " << rate/1e6 << " MHz... "; +        _tick_rate = _codec_ctrl->set_clock_rate(rate); +        UHD_MSG(status) << "got " << _tick_rate/1e6 << " MHz\n"; + +        BOOST_FOREACH(time_core_3000::sptr& time_core, _time_cores) { +            time_core->set_tick_rate(_tick_rate); +            time_core->self_test(); +        } + +        return _tick_rate; +    } + +    double get_tick_rate() +    { +        return _tick_rate; +    } + +    void set_clock_source(const std::string &source) +    { +        if (_clock_source == source) return; + +        if (source == "internal") { +            _ref_pll_ctrl->set_lock_to_ext_ref(false); +        } else if (source == "external" || source == "gpsdo") { +            _ref_pll_ctrl->set_lock_to_ext_ref(true); +        } else { +            throw uhd::key_error("set_clock_source: unknown source: " + source); +        } +        _core_misc_reg.write(fpga::core_misc_reg_t::REF_SEL, (source == "gpsdo") ? 1 : 0); + +        _clock_source = source; +    } + +    const std::string& get_clock_source() +    { +        return _clock_source; +    } + +    uhd::sensor_value_t get_ref_locked() +    { +        bool locked = false; +        if (_clock_source == "external" || _clock_source == "gpsdo") { +            locked = (_core_status_reg.read(fpga::core_status_reg_t::REF_LOCKED) == 1); +        } else { +            //If the source is internal, the charge pump on the ADF4001 is tristated which +            //means that the 40MHz VCTXXO is free running i.e. always "locked" +            locked = true; +        } +        return sensor_value_t("Ref", locked, "locked", "unlocked"); +    } + +    void set_pps_source(const std::string &source) +    { +        if (_time_source == source) return; + +        if (source == "none" or source == "gpsdo") { +            _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 0); +        } else if (source == "external") { +            _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 1); +        } else { +            throw uhd::key_error("update_time_source: unknown source: " + source); +        } + +        _time_source = source; +    } + +    const std::string& get_pps_source() +    { +        return _time_source; +    } + +private: +    ad9361_ctrl::sptr                   _codec_ctrl; +    n230_ref_pll_ctrl::sptr             _ref_pll_ctrl; +    fpga::core_misc_reg_t&              _core_misc_reg; +    fpga::core_pps_sel_reg_t&           _core_pps_sel_reg; +    fpga::core_status_reg_t&            _core_status_reg; +    std::vector<time_core_3000::sptr>   _time_cores; +    double                              _tick_rate; +    std::string                         _clock_source; +    std::string                         _time_source; +}; + +}}} //namespace + +using namespace uhd::usrp::n230; +using namespace uhd::usrp; + +n230_clk_pps_ctrl::sptr n230_clk_pps_ctrl::make( +    ad9361_ctrl::sptr codec_ctrl, +    n230_ref_pll_ctrl::sptr ref_pll_ctrl, +    fpga::core_misc_reg_t& core_misc_reg, +    fpga::core_pps_sel_reg_t& core_pps_sel_reg, +    fpga::core_status_reg_t& core_status_reg, +    const std::vector<time_core_3000::sptr>& time_cores) +{ +    return sptr(new n230_clk_pps_ctrl_impl( +        codec_ctrl, ref_pll_ctrl, core_misc_reg, core_pps_sel_reg, core_status_reg, time_cores)); +} + diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp new file mode 100644 index 000000000..3e0a21e04 --- /dev/null +++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp @@ -0,0 +1,89 @@ +// +// Copyright 2013-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_N230_CLK_PPS_CTRL_HPP +#define INCLUDED_N230_CLK_PPS_CTRL_HPP + +#include "time_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include <uhd/types/sensors.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> +#include "n230_cores.hpp" +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_clk_pps_ctrl : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<n230_clk_pps_ctrl> sptr; + +    static sptr make( +        ad9361_ctrl::sptr codec_ctrl, +        n230_ref_pll_ctrl::sptr ref_pll_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        fpga::core_pps_sel_reg_t& core_pps_sel_reg, +        fpga::core_status_reg_t& core_status_reg, +        const std::vector<time_core_3000::sptr>& time_cores); + +    virtual ~n230_clk_pps_ctrl() {} + +    /*********************************************************************** +     * Tick Rate +     **********************************************************************/ +    /*! Set the master clock rate of the device. +     * \return the clock frequency in Hz +     */ +    virtual double set_tick_rate(const double rate) = 0; + +    /*! Get the master clock rate of the device. +     * \return the clock frequency in Hz +     */ +    virtual double get_tick_rate() = 0; + +    /*********************************************************************** +     * Reference clock +     **********************************************************************/ +    /*! Set the reference clock source of the device. +     */ +    virtual void set_clock_source(const std::string &source) = 0; + +    /*! Get the reference clock source of the device. +     */ +    virtual const std::string& get_clock_source() = 0; + +    /*! Get the reference clock lock status. +     */ +    virtual uhd::sensor_value_t get_ref_locked() = 0; + +    /*********************************************************************** +     * Time source +     **********************************************************************/ +    /*! Set the time source of the device. +     */ +    virtual void set_pps_source(const std::string &source) = 0; + +    /*! Get the reference clock source of the device. +     */ +    virtual const std::string& get_pps_source() = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_CLK_PPS_CTRL_HPP */ diff --git a/host/lib/usrp/n230/n230_cores.cpp b/host/lib/usrp/n230/n230_cores.cpp new file mode 100644 index 000000000..58c702ec1 --- /dev/null +++ b/host/lib/usrp/n230/n230_cores.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2013-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 "n230_cores.hpp" +#include "n230_fpga_defs.h" +#include "n230_fw_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +n230_core_spi_core::n230_core_spi_core( +    uhd::wb_iface::sptr iface, +    perif_t default_perif) : +    _spi_core(spi_core_3000::make(iface, +                                  fpga::sr_addr(fpga::SR_CORE_SPI), +                                  fpga::rb_addr(fpga::RB_CORE_SPI))), +    _current_perif(default_perif), +    _last_perif(default_perif) +{ +    change_perif(default_perif); +} + +boost::uint32_t n230_core_spi_core::transact_spi( +    int which_slave, +    const spi_config_t &config, +    boost::uint32_t data, +    size_t num_bits, +    bool readback) +{ +    boost::mutex::scoped_lock lock(_mutex); +    return _spi_core->transact_spi(which_slave, config, data, num_bits, readback); +} + +void n230_core_spi_core::change_perif(perif_t perif) +{ +    boost::mutex::scoped_lock lock(_mutex); +    _last_perif = _current_perif; +    _current_perif = perif; + +    switch (_current_perif) { +        case CODEC: +            _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::CODEC_SPI_CLOCK_FREQ); +            break; +        case PLL: +            _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::ADF4001_SPI_CLOCK_FREQ); +            break; +    } +} + +void n230_core_spi_core::restore_perif() +{ +    change_perif(_last_perif); +} + +n230_ref_pll_ctrl::n230_ref_pll_ctrl(n230_core_spi_core::sptr spi) : +    adf4001_ctrl(spi, fpga::ADF4001_SPI_SLAVE_NUM), +    _spi(spi) +{ +} + +void n230_ref_pll_ctrl::set_lock_to_ext_ref(bool external) +{ +    _spi->change_perif(n230_core_spi_core::PLL); +    adf4001_ctrl::set_lock_to_ext_ref(external); +    _spi->restore_perif(); +} + +}}} //namespace + +using namespace uhd::usrp::n230; +using namespace uhd::usrp; + +n230_core_spi_core::sptr n230_core_spi_core::make( +    uhd::wb_iface::sptr iface, n230_core_spi_core::perif_t default_perif) +{ +    return sptr(new n230_core_spi_core(iface, default_perif)); +} + diff --git a/host/lib/usrp/n230/n230_cores.hpp b/host/lib/usrp/n230/n230_cores.hpp new file mode 100644 index 000000000..3f56c1889 --- /dev/null +++ b/host/lib/usrp/n230/n230_cores.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2013-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_N230_CORES_HPP +#define INCLUDED_N230_CORES_HPP + +#include "spi_core_3000.hpp" +#include "adf4001_ctrl.hpp" +#include <boost/thread/mutex.hpp> + +namespace uhd { namespace usrp { namespace n230 { + +class n230_core_spi_core : boost::noncopyable, public uhd::spi_iface { + +public: +    typedef boost::shared_ptr<n230_core_spi_core> sptr; + +    enum perif_t { +        CODEC, PLL +    }; + +    n230_core_spi_core(uhd::wb_iface::sptr iface, perif_t default_perif); + +    virtual boost::uint32_t transact_spi( +        int which_slave, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits, +        bool readback); + +    void change_perif(perif_t perif); +    void restore_perif(); + +    static sptr make(uhd::wb_iface::sptr iface, perif_t default_perif = CODEC); + +private: +    spi_core_3000::sptr     _spi_core; +    perif_t                 _current_perif; +    perif_t                 _last_perif; +    boost::mutex            _mutex; +}; + +class n230_ref_pll_ctrl : public adf4001_ctrl { +public: +    typedef boost::shared_ptr<n230_ref_pll_ctrl> sptr; + +    n230_ref_pll_ctrl(n230_core_spi_core::sptr spi); +    void set_lock_to_ext_ref(bool external); + +private: +    n230_core_spi_core::sptr _spi; +}; + + +}}} //namespace + +#endif /* INCLUDED_N230_CORES_HPP */ diff --git a/host/lib/usrp/n230/n230_defaults.h b/host/lib/usrp/n230/n230_defaults.h new file mode 100644 index 000000000..a25978585 --- /dev/null +++ b/host/lib/usrp/n230/n230_defaults.h @@ -0,0 +1,65 @@ +// +// 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_N230_DEFAULTS_H +#define INCLUDED_N230_DEFAULTS_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <uhd/transport/udp_constants.hpp> + +namespace uhd { +namespace usrp { +namespace n230 { + +static const double DEFAULT_TICK_RATE       = 46.08e6; +static const double MAX_TICK_RATE           = 50e6; +static const double MIN_TICK_RATE           = 1e6; + +static const double DEFAULT_TX_SAMP_RATE    = 1.0e6; +static const double DEFAULT_RX_SAMP_RATE    = 1.0e6; +static const double DEFAULT_DDC_FREQ        = 0.0; +static const double DEFAULT_DUC_FREQ        = 0.0; + +static const double DEFAULT_FE_GAIN         = 0.0; +static const double DEFAULT_FE_FREQ         = 1.0e9; +static const double DEFAULT_FE_BW           = 56e6; + +static const std::string DEFAULT_TIME_SRC   = "none"; +static const std::string DEFAULT_CLOCK_SRC  = "internal"; + +static const size_t DEFAULT_FRAME_SIZE      = 1500 - 20 - 8; //default ipv4 mtu - ipv4 header - udp header +static const size_t MAX_FRAME_SIZE          = 8000; +static const size_t MIN_FRAME_SIZE          = IP_PROTOCOL_MIN_MTU_SIZE; + +static const size_t DEFAULT_NUM_FRAMES      = 32; + +//A 1MiB SRAM is shared between two radios so we allocate each +//radio 0.5MiB minus 8 packets worth of buffering to ensure +//that the FIFO does not overflow +static const size_t DEFAULT_SEND_BUFF_SIZE  = 500*1024; +#if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +static const size_t DEFAULT_RECV_BUFF_SIZE  = 0x100000; //1Mib +#elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) +static const size_t DEFAULT_RECV_BUFF_SIZE  = 0x2000000;//32MiB +#endif + +}}}    //namespace + +#endif /* INCLUDED_N230_DEFAULTS_H */ diff --git a/host/lib/usrp/n230/n230_device_args.hpp b/host/lib/usrp/n230/n230_device_args.hpp new file mode 100644 index 000000000..014a6cd14 --- /dev/null +++ b/host/lib/usrp/n230/n230_device_args.hpp @@ -0,0 +1,128 @@ +// +// 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_N230_DEV_ARGS_HPP +#define INCLUDED_N230_DEV_ARGS_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread/mutex.hpp> +#include "../common/constrained_device_args.hpp" +#include "n230_defaults.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_device_args_t : public constrained_device_args_t +{ +public: +    enum loopback_mode_t { LOOPBACK_OFF=0, LOOPBACK_RADIO=1, LOOPBACK_CODEC=2 }; + +    n230_device_args_t(): +        _master_clock_rate("master_clock_rate", n230::DEFAULT_TICK_RATE), +        _send_frame_size("send_frame_size", n230::DEFAULT_FRAME_SIZE), +        _recv_frame_size("recv_frame_size", n230::DEFAULT_FRAME_SIZE), +        _num_send_frames("num_send_frames", n230::DEFAULT_NUM_FRAMES), +        _num_recv_frames("num_recv_frames", n230::DEFAULT_NUM_FRAMES), +        _send_buff_size("send_buff_size", n230::DEFAULT_SEND_BUFF_SIZE), +        _recv_buff_size("recv_buff_size", n230::DEFAULT_RECV_BUFF_SIZE), +        _safe_mode("safe_mode", false), +        _loopback_mode("loopback_mode", LOOPBACK_OFF, boost::assign::list_of("off")("radio")("codec")) +    {} + +    double get_master_clock_rate() const { +        return _master_clock_rate.get(); +    } +    size_t get_send_frame_size() const { +        return _send_frame_size.get(); +    } +    size_t get_recv_frame_size() const { +        return _recv_frame_size.get(); +    } +    size_t get_num_send_frames() const { +        return _num_send_frames.get(); +    } +    size_t get_num_recv_frames() const { +        return _num_recv_frames.get(); +    } +    size_t get_send_buff_size() const { +        return _send_buff_size.get(); +    } +    size_t get_recv_buff_size() const { +        return _recv_buff_size.get(); +    } +    bool get_safe_mode() const { +        return _safe_mode.get(); +    } +    loopback_mode_t get_loopback_mode() const { +        return _loopback_mode.get(); +    } + +    inline virtual std::string to_string() const { +        return  _master_clock_rate.to_string() + ", " + +                _send_frame_size.to_string() + ", " + +                _recv_frame_size.to_string() + ", " + +                _num_send_frames.to_string() + ", " + +                _num_recv_frames.to_string() + ", " + +                _send_buff_size.to_string() + ", " + +                _recv_buff_size.to_string() + ", " + +                _safe_mode.to_string() + ", " + +                _loopback_mode.to_string(); +    } +private: +    virtual void _parse(const device_addr_t& dev_args) { +        //Extract parameters from dev_args +        if (dev_args.has_key(_master_clock_rate.key())) +            _master_clock_rate.parse(dev_args[_master_clock_rate.key()]); +        if (dev_args.has_key(_send_frame_size.key())) +            _send_frame_size.parse(dev_args[_send_frame_size.key()]); +        if (dev_args.has_key(_recv_frame_size.key())) +            _recv_frame_size.parse(dev_args[_recv_frame_size.key()]); +        if (dev_args.has_key(_num_send_frames.key())) +            _num_send_frames.parse(dev_args[_num_send_frames.key()]); +        if (dev_args.has_key(_num_recv_frames.key())) +            _num_recv_frames.parse(dev_args[_num_recv_frames.key()]); +        if (dev_args.has_key(_send_buff_size.key())) +            _send_buff_size.parse(dev_args[_send_buff_size.key()]); +        if (dev_args.has_key(_recv_buff_size.key())) +            _recv_buff_size.parse(dev_args[_recv_buff_size.key()]); +        if (dev_args.has_key(_safe_mode.key())) +            _safe_mode.parse(dev_args[_safe_mode.key()]); +        if (dev_args.has_key(_loopback_mode.key())) +            _loopback_mode.parse(dev_args[_loopback_mode.key()], false /* assert invalid */); + +        //Sanity check params +        _enforce_range(_master_clock_rate, MIN_TICK_RATE, MAX_TICK_RATE); +        _enforce_range(_send_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE); +        _enforce_range(_recv_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE); +        _enforce_range(_num_send_frames, (size_t)2, (size_t)UINT_MAX); +        _enforce_range(_num_recv_frames, (size_t)2, (size_t)UINT_MAX); +    } + +    constrained_device_args_t::num_arg<double>           _master_clock_rate; +    constrained_device_args_t::num_arg<size_t>           _send_frame_size; +    constrained_device_args_t::num_arg<size_t>           _recv_frame_size; +    constrained_device_args_t::num_arg<size_t>           _num_send_frames; +    constrained_device_args_t::num_arg<size_t>           _num_recv_frames; +    constrained_device_args_t::num_arg<size_t>           _send_buff_size; +    constrained_device_args_t::num_arg<size_t>           _recv_buff_size; +    constrained_device_args_t::bool_arg                  _safe_mode; +    constrained_device_args_t::enum_arg<loopback_mode_t> _loopback_mode; +}; + +}}} //namespace + +#endif //INCLUDED_N230_DEV_ARGS_HPP diff --git a/host/lib/usrp/n230/n230_eeprom.h b/host/lib/usrp/n230/n230_eeprom.h new file mode 100644 index 000000000..b6c2a0c76 --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom.h @@ -0,0 +1,124 @@ +// +// 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_N230_EEPROM_H +#define INCLUDED_N230_EEPROM_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define N230_NUM_ETH_PORTS 2 +#define N230_MAX_NUM_ETH_PORTS 2 + +#if (N230_NUM_ETH_PORTS > N230_MAX_NUM_ETH_PORTS) +#error +#endif + +#define N230_EEPROM_VER_MAJOR     1 +#define N230_EEPROM_VER_MINOR     1 +#define N230_EEPROM_SERIAL_LEN    9 +#define N230_EEPROM_NAME_LEN      32 + +typedef struct +{ +    uint8_t  mac_addr[6]; +    uint8_t  _pad[2]; +    uint32_t subnet; +    uint32_t ip_addr; +} n230_eth_eeprom_map_t; + +typedef struct +{ +    //Data format version +    uint16_t data_version_major; +    uint16_t data_version_minor; + +    //HW identification info +    uint16_t hw_revision; +    uint16_t hw_product; +    uint8_t serial[N230_EEPROM_SERIAL_LEN]; +    uint8_t _pad_serial; +    uint16_t hw_revision_compat; +    uint8_t _pad0[18 - (N230_EEPROM_SERIAL_LEN + 1)]; + +    //Ethernet specific +    uint32_t gateway; +    n230_eth_eeprom_map_t eth_info[N230_MAX_NUM_ETH_PORTS]; + +    //User specific +    uint8_t user_name[N230_EEPROM_NAME_LEN]; +} n230_eeprom_map_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +/*! + * Read the eeprom and update caches. + * Returns true if read was successful. + * If the read was not successful then the cache is initialized with + * default values and marked as dirty. + */ +bool read_n230_eeprom(); + +/*! + * Write the contents of the cache to the eeprom. + * Returns true if write was successful. + */ +bool write_n230_eeprom(); + +/*! + * Returns the dirty state of the cache. + */ +bool is_n230_eeprom_cache_dirty(); + +/*! + * Returns a const pointer to the EEPROM map. + */ +const n230_eeprom_map_t* get_n230_const_eeprom_map(); + +/*! + * Returns the settings for the the 'iface'th ethernet interface + */ +const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface); + +/*! + * Returns a non-const pointer to the EEPROM map. Will mark the cache as dirty. + */ +n230_eeprom_map_t* get_n230_eeprom_map(); + +/*! + * FPGA Image operations + */ +inline void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes); + +inline bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes); + +inline bool erase_n230_fpga_image_sector(uint32_t offset); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_N230_EEPROM_H */ diff --git a/host/lib/usrp/n230/n230_eeprom_manager.cpp b/host/lib/usrp/n230/n230_eeprom_manager.cpp new file mode 100644 index 000000000..b19deb23a --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom_manager.cpp @@ -0,0 +1,207 @@ +// +// Copyright 2013-2014,2016 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 "n230_eeprom.h" +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/mac_addr.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include "n230_eeprom_manager.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +const double n230_eeprom_manager::UDP_TIMEOUT_IN_SEC = 2.0; + +n230_eeprom_manager::n230_eeprom_manager(const std::string& addr): +    _seq_num(0) +{ +    _udp_xport = transport::udp_simple::make_connected( +        addr, BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT)); +    read_mb_eeprom(); +} + +static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len) +{ +    std::string out; +    for (size_t i = 0; i < max_len; i++) { +        if (bytes[i] < 32 or bytes[i] > 127) return out; +        out += bytes[i]; +    } +    return out; +} + +static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer) +{ +    byte_vector_t bytes; +    const size_t len = std::min(string.size(), max_len); +    for (size_t i = 0; i < len; i++){ +        buffer[i] = string[i]; +    } +    if (len < max_len - 1) buffer[len] = '\0'; +} + +const mboard_eeprom_t& n230_eeprom_manager::read_mb_eeprom() +{ +    boost::mutex::scoped_lock lock(_mutex); + +    //Read EEPROM from device +    _transact(N230_FLASH_COMM_CMD_READ_NV_DATA); +    const n230_eeprom_map_t* map_ptr = reinterpret_cast<const n230_eeprom_map_t*>(_response.data); +    const n230_eeprom_map_t& map = *map_ptr; + +    uint16_t ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major); +    uint16_t ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor); + +    _mb_eeprom["product"] = boost::lexical_cast<std::string>( +        uhd::htonx<boost::uint16_t>(map.hw_product)); +    _mb_eeprom["revision"] = boost::lexical_cast<std::string>( +        uhd::htonx<boost::uint16_t>(map.hw_revision)); +    //The revision_compat field does not exist in version 1.0 +    //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set +    //revision_compat = revision +    if (ver_major == 1 and ver_minor == 0) { +        _mb_eeprom["revision_compat"] = _mb_eeprom["revision"]; +    } else { +        _mb_eeprom["revision_compat"] = boost::lexical_cast<std::string>( +            uhd::htonx<boost::uint16_t>(map.hw_revision_compat)); +    } +    _mb_eeprom["serial"] = _bytes_to_string( +        map.serial, N230_EEPROM_SERIAL_LEN); + +    //Extract ethernet info +    _mb_eeprom["gateway"] = boost::asio::ip::address_v4( +        uhd::htonx<boost::uint32_t>(map.gateway)).to_string(); +    for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) { +        const std::string n(1, i+'0'); +        _mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4( +            uhd::htonx<boost::uint32_t>(map.eth_info[i].ip_addr)).to_string(); +        _mb_eeprom["subnet"+n] = boost::asio::ip::address_v4( +            uhd::htonx<boost::uint32_t>(map.eth_info[i].subnet)).to_string(); +        byte_vector_t mac_addr(map.eth_info[i].mac_addr, map.eth_info[i].mac_addr + 6); +        _mb_eeprom["mac-addr"+n] = mac_addr_t::from_bytes(mac_addr).to_string(); +    } + +    _mb_eeprom["name"] = _bytes_to_string( +        map.user_name, N230_EEPROM_NAME_LEN); + +    return _mb_eeprom; +} + +void n230_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    _mb_eeprom = eeprom; + +    n230_eeprom_map_t* map_ptr = reinterpret_cast<n230_eeprom_map_t*>(_request.data); +    memset(map_ptr, 0xff, sizeof(n230_eeprom_map_t)); //Initialize to erased state +    //Read EEPROM from device +    _transact(N230_FLASH_COMM_CMD_READ_NV_DATA); +    memcpy(map_ptr, _response.data, sizeof(n230_eeprom_map_t)); +    n230_eeprom_map_t& map = *map_ptr; + +    // Automatic version upgrade handling +    uint16_t old_ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major); +    uint16_t old_ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor); + +    //The revision_compat field does not exist for version 1.0 so force write it +    //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set +    //revision_compat = revision for the upgrade +    bool force_write_version_compat = (old_ver_major == 1 and old_ver_minor == 0); + +    map.data_version_major = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MAJOR); +    map.data_version_minor = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MINOR); + +    if (_mb_eeprom.has_key("product")) { +        map.hw_product = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["product"])); +    } +    if (_mb_eeprom.has_key("revision")) { +        map.hw_revision = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision"])); +    } +    if (_mb_eeprom.has_key("revision_compat")) { +        map.hw_revision_compat = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision_compat"])); +    } else if (force_write_version_compat) { +        map.hw_revision_compat = map.hw_revision; +    } +    if (_mb_eeprom.has_key("serial")) { +        _string_to_bytes(_mb_eeprom["serial"], N230_EEPROM_SERIAL_LEN, map.serial); +    } + +    //Push ethernet info +    if (_mb_eeprom.has_key("gateway")){ +        map.gateway = uhd::htonx<boost::uint32_t>( +            boost::asio::ip::address_v4::from_string(_mb_eeprom["gateway"]).to_ulong()); +    } +    for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) { +        const std::string n(1, i+'0'); +        if (_mb_eeprom.has_key("ip-addr"+n)){ +            map.eth_info[i].ip_addr = uhd::htonx<boost::uint32_t>( +                boost::asio::ip::address_v4::from_string(_mb_eeprom["ip-addr"+n]).to_ulong()); +        } +        if (_mb_eeprom.has_key("subnet"+n)){ +            map.eth_info[i].subnet = uhd::htonx<boost::uint32_t>( +                boost::asio::ip::address_v4::from_string(_mb_eeprom["subnet"+n]).to_ulong()); +        } +        if (_mb_eeprom.has_key("mac-addr"+n)) { +            byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"+n]).to_bytes(); +            std::copy(mac_addr.begin(), mac_addr.end(), map.eth_info[i].mac_addr); +        } +    } +    //store the name +    if (_mb_eeprom.has_key("name")) { +        _string_to_bytes(_mb_eeprom["name"], N230_EEPROM_NAME_LEN, map.user_name); +    } + +    //Write EEPROM to device +    _transact(N230_FLASH_COMM_CMD_WRITE_NV_DATA); +} + +void n230_eeprom_manager::_transact(const boost::uint32_t command) +{ +    //Load request struct +    _request.flags = uhd::htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK | command); +    _request.seq = uhd::htonx<boost::uint32_t>(_seq_num++); + +    //Send request +    _flush_xport(); +    _udp_xport->send(boost::asio::buffer(&_request, sizeof(_request))); + +    //Recv reply +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&_response, sizeof(_response)), UDP_TIMEOUT_IN_SEC); +    if (nbytes == 0) throw uhd::io_error("n230_eeprom_manager::_transact failure"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(_response.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(_response)); +    UHD_ASSERT_THROW(_response.seq == _request.seq); +    UHD_ASSERT_THROW(flags & command); +} + +void n230_eeprom_manager::_flush_xport() +{ +    char buff[sizeof(n230_flash_prog_t)] = {}; +    while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) { +        /*NOP*/ +    } +} + +}}};    //namespace diff --git a/host/lib/usrp/n230/n230_eeprom_manager.hpp b/host/lib/usrp/n230/n230_eeprom_manager.hpp new file mode 100644 index 000000000..cc5aee9f3 --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom_manager.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2013-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_N230_EEPROM_MANAGER_HPP +#define INCLUDED_N230_EEPROM_MANAGER_HPP + +#include <boost/thread/mutex.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include "n230_fw_host_iface.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_eeprom_manager : boost::noncopyable +{ +public: +    n230_eeprom_manager(const std::string& addr); + +    const mboard_eeprom_t& read_mb_eeprom(); +    void write_mb_eeprom(const mboard_eeprom_t& eeprom); + +    inline const mboard_eeprom_t& get_mb_eeprom() { +        return _mb_eeprom; +    } + +private:    //Functions +    void _transact(const boost::uint32_t command); +    void _flush_xport(); + +private:    //Members +    mboard_eeprom_t             _mb_eeprom; +    transport::udp_simple::sptr _udp_xport; +    n230_flash_prog_t           _request; +    n230_flash_prog_t           _response; +    boost::uint32_t             _seq_num; +    boost::mutex                _mutex; + +    static const double UDP_TIMEOUT_IN_SEC; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_EEPROM_MANAGER_HPP */ diff --git a/host/lib/usrp/n230/n230_fpga_defs.h b/host/lib/usrp/n230/n230_fpga_defs.h new file mode 100644 index 000000000..3aa96643f --- /dev/null +++ b/host/lib/usrp/n230/n230_fpga_defs.h @@ -0,0 +1,207 @@ +// +// 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_N230_FPGA_DEFS_H +#define INCLUDED_N230_FPGA_DEFS_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <uhd/utils/soft_register.hpp> + +namespace uhd { +namespace usrp { +namespace n230 { +namespace fpga { + +static inline uint32_t sr_addr(uint32_t offset) { +    return (offset*4); +} + +static inline uint32_t rb_addr(uint32_t offset) { +    return (offset*8); +} + +static const size_t NUM_RADIOS = 2; +static const double BUS_CLK_RATE = 80e6; + +/******************************************************************* + * CVITA Routing + *******************************************************************/ +static const uint32_t CVITA_UDP_PORT    = 49153; +static const bool CVITA_BIG_ENDIAN      = true; + +enum xb_endpoint_t { +    N230_XB_DST_E0    = 0, +    N230_XB_DST_E1    = 1, +    N230_XB_DST_R0    = 2, +    N230_XB_DST_R1    = 3, +    N230_XB_DST_GCTRL = 4, +    N230_XB_DST_UART  = 5 +}; + +static const boost::uint8_t RADIO_CTRL_SUFFIX = 0x00; +static const boost::uint8_t RADIO_FC_SUFFIX   = 0x01; +static const boost::uint8_t RADIO_DATA_SUFFIX = 0x02; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_CORE_RADIO_CONTROL = 3; +static const uint32_t SR_CORE_LOOPBACK      = 4; +static const uint32_t SR_CORE_BIST1         = 5; +static const uint32_t SR_CORE_BIST2         = 6; +static const uint32_t SR_CORE_SPI           = 8; +static const uint32_t SR_CORE_MISC          = 16; +static const uint32_t SR_CORE_DATA_DELAY    = 17; +static const uint32_t SR_CORE_CLK_DELAY     = 18; +static const uint32_t SR_CORE_COMPAT        = 24; +static const uint32_t SR_CORE_READBACK      = 32; +static const uint32_t SR_CORE_GPSDO_ST      = 40; +static const uint32_t SR_CORE_PPS_SEL       = 48; +static const uint32_t SR_CORE_MS0_GPIO      = 50; +static const uint32_t SR_CORE_MS1_GPIO      = 58; + +static const uint32_t RB_CORE_SIGNATUE      = 0; +static const uint32_t RB_CORE_SPI           = 1; +static const uint32_t RB_CORE_STATUS        = 2; +static const uint32_t RB_CORE_BIST          = 3; +static const uint32_t RB_CORE_VERSION_HASH  = 4; +static const uint32_t RB_CORE_MS0_GPIO      = 5; +static const uint32_t RB_CORE_MS1_GPIO      = 6; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_RADIO_SPI          = 8; +static const uint32_t SR_RADIO_ATR          = 12; +static const uint32_t SR_RADIO_SW_RST       = 20; +static const uint32_t SR_RADIO_TEST         = 21; +static const uint32_t SR_RADIO_CODEC_IDLE   = 22; +static const uint32_t SR_RADIO_READBACK     = 32; +static const uint32_t SR_RADIO_TX_CTRL      = 64; +static const uint32_t SR_RADIO_RX_CTRL      = 96; +static const uint32_t SR_RADIO_RX_DSP       = 144; +static const uint32_t SR_RADIO_TX_DSP       = 184; +static const uint32_t SR_RADIO_TIME         = 128; +static const uint32_t SR_RADIO_RX_FMT       = 136; +static const uint32_t SR_RADIO_TX_FMT       = 138; +static const uint32_t SR_RADIO_USER_SR      = 253; + +static const uint32_t RB_RADIO_TEST         = 0; +static const uint32_t RB_RADIO_TIME_NOW     = 1; +static const uint32_t RB_RADIO_TIME_PPS     = 2; +static const uint32_t RB_RADIO_CODEC_DATA   = 3; +static const uint32_t RB_RADIO_DEBUG        = 4; +static const uint32_t RB_RADIO_FRAMER       = 5; +static const uint32_t SR_RADIO_USER_RB      = 7; + +static const uint32_t AD9361_SPI_SLAVE_NUM  = 0x1; +static const uint32_t ADF4001_SPI_SLAVE_NUM = 0x2; + +static const uint32_t RB_N230_PRODUCT_ID    = 1; +static const uint32_t RB_N230_COMPAT_MAJOR  = 0x20; +static const uint32_t RB_N230_COMPAT_SAFE   = 0xC0; + +/******************************************************************* + * Codec Interface Specific + *******************************************************************/ + +// Matches delay setting of 0x00 in AD9361 register 0x006 +static const uint32_t CODEC_DATA_DELAY      = 0; +static const uint32_t CODEC_CLK_DELAY       = 16; + +//This number must be < 46.08MHz to make sure we don't +//violate timing for radio_clk. It is only used during +//initialization so the exact value does not matter. +static const double CODEC_DEFAULT_CLK_RATE  = 40e6; + +/******************************************************************* + * Link Specific + *******************************************************************/ +static const double N230_LINK_RATE_BPS      = 1e9/8; + +/******************************************************************* + * GPSDO + *******************************************************************/ +static const uint32_t GPSDO_UART_BAUDRATE   = 115200; +static const uint32_t GPSDO_ST_ABSENT       = 0x83; +/******************************************************************* + * Register Objects + *******************************************************************/ +class core_radio_ctrl_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(MIMO,         /*width*/ 1, /*shift*/ 0);  //[0] +    UHD_DEFINE_SOFT_REG_FIELD(CODEC_ARST,   /*width*/ 1, /*shift*/ 1);  //[1] + +    core_radio_ctrl_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_RADIO_CONTROL)) +    { +        //Initial values +        set(CODEC_ARST, 0); +        set(MIMO, 1);   //MIMO always ON for now +    } +}; + +class core_misc_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(REF_SEL,      /*width*/ 1, /*shift*/ 0);  //[0] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_C, /*width*/ 1, /*shift*/ 1);  //[1] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_B, /*width*/ 1, /*shift*/ 2);  //[2] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_A, /*width*/ 1, /*shift*/ 3);  //[3] +    UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_B, /*width*/ 1, /*shift*/ 4);  //[4] +    UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_A, /*width*/ 1, /*shift*/ 5);  //[5] + +    core_misc_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_MISC)) +    { +        //Initial values +        set(REF_SEL, 0); +        set(RX_BANDSEL_C, 0); +        set(RX_BANDSEL_B, 0); +        set(RX_BANDSEL_A, 0); +        set(TX_BANDSEL_B, 0); +        set(TX_BANDSEL_A, 0); +    } +}; + +class core_pps_sel_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(EXT_PPS_EN,   /*width*/ 1, /*shift*/ 0);  //[0] + +    core_pps_sel_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_PPS_SEL)) +    { +        //Initial values +        set(EXT_PPS_EN, 0); +    } +}; + +class core_status_reg_t : public soft_reg64_ro_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(REF_LOCKED,     /*width*/ 1, /*shift*/ 0);    //[0] +    UHD_DEFINE_SOFT_REG_FIELD(GPSDO_STATUS,   /*width*/ 8, /*shift*/ 32);   //[32:39] + +    core_status_reg_t(): +        soft_reg64_ro_t(fpga::rb_addr(fpga::RB_CORE_STATUS)) +    { } +}; + +}}}}    //namespace + +#endif /* INCLUDED_N230_FPGA_DEFS_H */ diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.cpp b/host/lib/usrp/n230/n230_frontend_ctrl.cpp new file mode 100644 index 000000000..e0820d9b2 --- /dev/null +++ b/host/lib/usrp/n230/n230_frontend_ctrl.cpp @@ -0,0 +1,243 @@ +// +// Copyright 2013-2014,2016 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 "n230_frontend_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +/* ATR Control Bits */ +static const boost::uint32_t TX_ENABLE      = (1 << 7); +static const boost::uint32_t SFDX_RX        = (1 << 6); +static const boost::uint32_t SFDX_TX        = (1 << 5); +static const boost::uint32_t SRX_RX         = (1 << 4); +static const boost::uint32_t SRX_TX         = (1 << 3); +static const boost::uint32_t LED_RX         = (1 << 2); +static const boost::uint32_t LED_TXRX_RX    = (1 << 1); +static const boost::uint32_t LED_TXRX_TX    = (1 << 0); + +/* ATR State Definitions. */ +static const boost::uint32_t STATE_OFF      = 0x00; +static const boost::uint32_t STATE_RX_RX2   = (SFDX_RX +                                                | SFDX_TX +                                                | LED_RX); +static const boost::uint32_t STATE_RX_TXRX  = (SRX_RX +                                                | SRX_TX +                                                | LED_TXRX_RX); +static const boost::uint32_t STATE_FDX_TXRX = (TX_ENABLE +                                                | SFDX_RX +                                                | SFDX_TX +                                                | LED_TXRX_TX +                                                | LED_RX); +static const boost::uint32_t STATE_TX_TXRX  = (TX_ENABLE +                                                | SFDX_RX +                                                | SFDX_TX +                                                | LED_TXRX_TX); + +using namespace uhd::usrp; + +class n230_frontend_ctrl_impl : public n230_frontend_ctrl +{ +public: +    n230_frontend_ctrl_impl( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores +    ): _core_ctrl(core_ctrl), +       _codec_ctrl(codec_ctrl), +       _gpio_cores(gpio_cores), +       _core_misc_reg(core_misc_reg) +    { +    } + +    virtual ~n230_frontend_ctrl_impl() +    { +    } + +    void set_antenna_sel(const size_t which, const std::string &ant) +    { +        if (ant != "TX/RX" and ant != "RX2") +            throw uhd::value_error("n230: unknown RX antenna option: " + ant); + +        _fe_states[which].rx_ant = ant; +        _flush_atr_state(); +    } + +    void set_stream_state(const fe_state_t fe0_state_, const fe_state_t fe1_state_) +    { +        //Update soft-state +        _fe_states[0].state = fe0_state_; +        _fe_states[1].state = fe1_state_; + +        const fe_state_t fe0_state = _fe_states[0].state; +        const fe_state_t fe1_state = (_gpio_cores.size() > 1) ? _fe_states[1].state : NONE_STREAMING; + +        const size_t num_tx = (_is_tx(fe0_state) ? 1 : 0) + (_is_tx(fe1_state) ? 1 : 0); +        const size_t num_rx = (_is_rx(fe0_state) ? 1 : 0) + (_is_rx(fe1_state) ? 1 : 0); + +        //setup the active chains in the codec +        if ((num_rx + num_tx) == 0) { +            _codec_ctrl->set_active_chains( +                true, false, +                true, false); //enable something +        } else { +            _codec_ctrl->set_active_chains( +                _is_tx(fe0_state), _is_tx(fe1_state), +                _is_rx(fe0_state), _is_rx(fe1_state)); +        } + +        _core_misc_reg.flush(); +        //atrs change based on enables +        _flush_atr_state(); +    } + + +    void set_stream_state(const size_t which, const fe_state_t state) +    { +        if (which == 0) { +            set_stream_state(state, _fe_states[1].state); +        } else if (which == 1) { +            set_stream_state(_fe_states[0].state, state); +        } else { +            throw uhd::value_error( +                str(boost::format("n230: unknown stream index option: %d") % which) +            ); +        } +    } + +    void set_bandsel(const std::string& which, double freq) +    { +        using namespace n230::fpga; + +        if(which[0] == 'R') { +            if(freq < 2.2e9) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 1); +            } else if((freq >= 2.2e9) && (freq < 4e9)) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 1); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0); +            } else if((freq >= 4e9) && (freq <= 6e9)) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 1); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0); +            } else { +                UHD_THROW_INVALID_CODE_PATH(); +            } +        } else if(which[0] == 'T') { +            if(freq < 2.5e9) { +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 1); +            } else if((freq >= 2.5e9) && (freq <= 6e9)) { +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 1); +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 0); +            } else { +                UHD_THROW_INVALID_CODE_PATH(); +            } +        } else { +            UHD_THROW_INVALID_CODE_PATH(); +        } + +        _core_misc_reg.flush(); +    } + +    void set_self_test_mode(self_test_mode_t mode) +    { +        switch (mode) { +            case LOOPBACK_RADIO: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x1); +            } break; +            case LOOPBACK_CODEC: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0); +                _codec_ctrl->data_port_loopback(true); +            } break; +            //Default = disable +            default: +            case LOOPBACK_DISABLED: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0); +                _codec_ctrl->data_port_loopback(false); +            } break; +        } +    } + +private: +    void _flush_atr_state() +    { +        for (size_t i = 0; i < _gpio_cores.size(); i++) { +            const fe_state_cache_t& fe_state_cache = _fe_states[i]; +            const bool enb_rx = _is_rx(fe_state_cache.state); +            const bool enb_tx = _is_tx(fe_state_cache.state); +            const bool is_rx2 = (fe_state_cache.rx_ant == "RX2"); +            const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX_RX2 : STATE_RX_TXRX) : STATE_OFF; +            const size_t txonly = (enb_tx)? (STATE_TX_TXRX) : STATE_OFF; +            size_t fd = STATE_OFF; +            if (enb_rx and enb_tx) fd = STATE_FDX_TXRX; +            if (enb_rx and not enb_tx) fd = rxonly; +            if (not enb_rx and enb_tx) fd = txonly; +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_IDLE, STATE_OFF); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, rxonly); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, txonly); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, fd); +        } +    } + +    inline static bool _is_tx(const fe_state_t state) +    { +        return state == TX_STREAMING || state == TXRX_STREAMING; +    } + +    inline static bool _is_rx(const fe_state_t state) +    { +        return state == RX_STREAMING || state == TXRX_STREAMING; +    } + +private: +    struct fe_state_cache_t { +        fe_state_cache_t() : state(NONE_STREAMING), rx_ant("RX2") +        {} +        fe_state_t state; +        std::string rx_ant; +    }; + +    radio_ctrl_core_3000::sptr              _core_ctrl; +    ad9361_ctrl::sptr                       _codec_ctrl; +    std::vector<gpio_atr::gpio_atr_3000::sptr>   _gpio_cores; +    fpga::core_misc_reg_t&                  _core_misc_reg; +    uhd::dict<size_t, fe_state_cache_t>     _fe_states; +}; + +}}} //namespace + +using namespace uhd::usrp::n230; + +n230_frontend_ctrl::sptr n230_frontend_ctrl::make( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores) +{ +    return sptr(new n230_frontend_ctrl_impl(core_ctrl, core_misc_reg, codec_ctrl, gpio_cores)); +} + diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.hpp b/host/lib/usrp/n230/n230_frontend_ctrl.hpp new file mode 100644 index 000000000..377d23ba8 --- /dev/null +++ b/host/lib/usrp/n230/n230_frontend_ctrl.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2013-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_N230_FRONTEND_CTRL_HPP +#define INCLUDED_N230_FRONTEND_CTRL_HPP + +#include "radio_ctrl_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include "gpio_atr_3000.hpp" +#include <uhd/types/sensors.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +enum fe_state_t { +    NONE_STREAMING, TX_STREAMING, RX_STREAMING, TXRX_STREAMING +}; + +enum self_test_mode_t { +    LOOPBACK_DISABLED, LOOPBACK_RADIO, LOOPBACK_CODEC +}; + + +class n230_frontend_ctrl : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<n230_frontend_ctrl> sptr; + +    static sptr make( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores); + +    virtual ~n230_frontend_ctrl() {} + +    virtual void set_antenna_sel( +        const size_t which, +        const std::string &ant) = 0; + +    virtual void set_stream_state( +        const size_t which, +        const fe_state_t state) = 0; + +    virtual void set_stream_state( +        const fe_state_t fe0_state, +        const fe_state_t fe1_state) = 0; + +    virtual void set_bandsel( +        const std::string& which, +        double freq) = 0; + +    virtual void set_self_test_mode( +        self_test_mode_t mode) = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_FRONTEND_CTRL_HPP */ diff --git a/host/lib/usrp/n230/n230_fw_defs.h b/host/lib/usrp/n230/n230_fw_defs.h new file mode 100644 index 000000000..fbdc67ebb --- /dev/null +++ b/host/lib/usrp/n230/n230_fw_defs.h @@ -0,0 +1,137 @@ +// +// 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_N230_FW_DEFS_H +#define INCLUDED_N230_FW_DEFS_H + +#include <stdint.h> + +/*! + * Constants specific to N230 firmware. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + * However, if it is included from within the host code, + * it will be namespaced appropriately + */ +#ifdef __cplusplus +namespace uhd { +namespace usrp { +namespace n230 { +namespace fw { +#endif + +static inline uint32_t reg_addr(uint32_t base, uint32_t offset) { +    return ((base) + (offset)*4); +} + +/******************************************************************* + * Global + *******************************************************************/ +static const uint32_t CPU_CLOCK_FREQ            = 80000000; +static const uint32_t PER_MILLISEC_CRON_JOBID   = 0; +static const uint32_t PER_SECOND_CRON_JOBID     = 1; + +/******************************************************************* + * Wishbone slave addresses + *******************************************************************/ +static const uint32_t WB_MAIN_RAM_BASE  = 0x0000; +static const uint32_t WB_PKT_RAM_BASE   = 0x8000; +static const uint32_t WB_SBRB_BASE      = 0xa000; +static const uint32_t WB_SPI_FLASH_BASE = 0xb000; +static const uint32_t WB_ETH0_MAC_BASE  = 0xc000; +static const uint32_t WB_ETH1_MAC_BASE  = 0xd000; +static const uint32_t WB_XB_SBRB_BASE   = 0xe000; +static const uint32_t WB_ETH0_I2C_BASE  = 0xf600; +static const uint32_t WB_ETH1_I2C_BASE  = 0xf700; +static const uint32_t WB_DBG_UART_BASE  = 0xf900; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_ZPU_SW_RST     = 0; +static const uint32_t SR_ZPU_BOOT_DONE  = 1; +static const uint32_t SR_ZPU_LEDS       = 2; +static const uint32_t SR_ZPU_XB_LOCAL   = 4; +static const uint32_t SR_ZPU_SFP_CTRL0  = 16; +static const uint32_t SR_ZPU_SFP_CTRL1  = 17; +static const uint32_t SR_ZPU_ETHINT0    = 64; +static const uint32_t SR_ZPU_ETHINT1    = 80; + +static const uint32_t SR_ZPU_SW_RST_NONE    = 0x0; +static const uint32_t SR_ZPU_SW_RST_PHY     = 0x1; +static const uint32_t SR_ZPU_SW_RST_RADIO   = 0x2; + +/******************************************************************* + * Readback addresses + *******************************************************************/ +static const uint32_t RB_ZPU_COMPAT         = 0; +static const uint32_t RB_ZPU_COUNTER        = 1; +static const uint32_t RB_ZPU_SFP_STATUS0    = 2; +static const uint32_t RB_ZPU_SFP_STATUS1    = 3; +static const uint32_t RB_ZPU_ETH0_PKT_CNT   = 6; +static const uint32_t RB_ZPU_ETH1_PKT_CNT   = 7; + +/******************************************************************* + * Ethernet + *******************************************************************/ +static const uint32_t WB_PKT_RAM_CTRL_OFFSET    = 0x1FFC; + +static const uint32_t SR_ZPU_ETHINT_FRAMER_BASE     = 0; +static const uint32_t SR_ZPU_ETHINT_DISPATCHER_BASE = 8; + +//Eth framer constants +static const uint32_t ETH_FRAMER_SRC_MAC_HI     = 0; +static const uint32_t ETH_FRAMER_SRC_MAC_LO     = 1; +static const uint32_t ETH_FRAMER_SRC_IP_ADDR    = 2; +static const uint32_t ETH_FRAMER_SRC_UDP_PORT   = 3; +static const uint32_t ETH_FRAMER_DST_RAM_ADDR   = 4; +static const uint32_t ETH_FRAMER_DST_IP_ADDR    = 5; +static const uint32_t ETH_FRAMER_DST_UDP_MAC    = 6; +static const uint32_t ETH_FRAMER_DST_MAC_LO     = 7; + +/******************************************************************* + * CODEC + *******************************************************************/ +static const uint32_t CODEC_SPI_CLOCK_FREQ      = 4000000;  //4MHz +static const uint32_t ADF4001_SPI_CLOCK_FREQ    = 200000;   //200kHz + +/******************************************************************* + * UART + *******************************************************************/ +static const uint32_t DBG_UART_BAUD     = 115200; + +/******************************************************************* + * Build Compatability Numbers + *******************************************************************/ +static const uint8_t PRODUCT_NUM = 0x01; +static const uint8_t COMPAT_MAJOR = 0x00; +static const uint16_t COMPAT_MINOR = 0x0000; + +static inline uint8_t get_prod_num(uint32_t compat_reg) { +    return (compat_reg >> 24) & 0xFF; +} +static inline uint8_t get_compat_major(uint32_t compat_reg) { +    return (compat_reg >> 16) & 0xFF; +} +static inline uint8_t get_compat_minor(uint32_t compat_reg) { +    return compat_reg & 0xFFFF; +} + +#ifdef __cplusplus +}}}} //namespace +#endif +#endif /* INCLUDED_N230_FW_DEFS_H */ diff --git a/host/lib/usrp/n230/n230_fw_host_iface.h b/host/lib/usrp/n230/n230_fw_host_iface.h new file mode 100644 index 000000000..0391af0d9 --- /dev/null +++ b/host/lib/usrp/n230/n230_fw_host_iface.h @@ -0,0 +1,128 @@ +// +// 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_N230_FW_HOST_IFACE_H +#define INCLUDED_N230_FW_HOST_IFACE_H + +#include <stdint.h> + +/*! + * Structs and constants for N230 communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------- +// Ethernet related +// +#define N230_DEFAULT_ETH0_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff} +#define N230_DEFAULT_ETH1_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33} +#define N230_DEFAULT_ETH0_IP     (192 << 24 | 168 << 16 | 10  << 8  | 2 << 0) +#define N230_DEFAULT_ETH1_IP     (192 << 24 | 168 << 16 | 20  << 8  | 2 << 0) +#define N230_DEFAULT_ETH0_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_ETH1_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_GATEWAY     (192 << 24 | 168 << 16 | 10  << 8  | 1 << 0) + +#define N230_FW_COMMS_UDP_PORT        49152 +#define N230_FW_COMMS_CVITA_PORT      49153 +#define N230_FW_COMMS_FLASH_PROG_PORT 49154 +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Memory shared with host +// +#define N230_FW_HOST_SHMEM_BASE_ADDR      0x10000 +#define N230_FW_HOST_SHMEM_RW_BASE_ADDR   0x1000C +#define N230_FW_HOST_SHMEM_NUM_WORDS      (sizeof(n230_host_shared_mem_data_t)/sizeof(uint32_t)) + +#define N230_FW_HOST_SHMEM_MAX_ADDR  \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((N230_FW_HOST_SHMEM_NUM_WORDS - 1) * sizeof(uint32_t))) + +#define N230_FW_HOST_SHMEM_OFFSET(member) \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((uint32_t)offsetof(n230_host_shared_mem_data_t, member))) + +//The shared memory block can only be accessed on 32-bit boundaries +typedef struct {    //All fields must be 32-bit wide to avoid packing directives +    //Read-Only fields (N230_FW_HOST_SHMEM_BASE_ADDR) +    uint32_t fw_compat_num;     //Compat number must be at offset 0 +    uint32_t fw_version_hash; +    uint32_t claim_status; + +    //Read-Write fields (N230_FW_HOST_SHMEM_RW_BASE_ADDR) +    uint32_t scratch; +    uint32_t claim_time; +    uint32_t claim_src; +} n230_host_shared_mem_data_t; + +typedef union +{ +    uint32_t                    buff[N230_FW_HOST_SHMEM_NUM_WORDS]; +    n230_host_shared_mem_data_t   data; +} n230_host_shared_mem_t; + +#define N230_FW_PRODUCT_ID        1 +#define N230_FW_COMPAT_NUM_MAJOR  32 +#define N230_FW_COMPAT_NUM_MINOR  0 +#define N230_FW_COMPAT_NUM        (((N230_FW_COMPAT_NUM_MAJOR & 0xFF) << 16) | (N230_FW_COMPAT_NUM_MINOR & 0xFFFF)) +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Flash read-write interface for host +// +#define N230_FLASH_COMM_FLAGS_ACK           0x00000001 +#define N230_FLASH_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define N230_FLASH_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define N230_FLASH_COMM_CMD_READ_NV_DATA    0x00000010 +#define N230_FLASH_COMM_CMD_WRITE_NV_DATA   0x00000020 +#define N230_FLASH_COMM_CMD_READ_FPGA       0x00000030 +#define N230_FLASH_COMM_CMD_WRITE_FPGA      0x00000040 +#define N230_FLASH_COMM_CMD_ERASE_FPGA      0x00000050 + +#define N230_FLASH_COMM_ERR_PKT_ERROR       0x80000000 +#define N230_FLASH_COMM_ERR_CMD_ERROR       0x40000000 +#define N230_FLASH_COMM_ERR_SIZE_ERROR      0x20000000 + +#define N230_FLASH_COMM_MAX_PAYLOAD_SIZE    128 + +typedef struct +{ +    uint32_t flags; +    uint32_t seq; +    uint32_t offset; +    uint32_t size; +    uint8_t data[N230_FLASH_COMM_MAX_PAYLOAD_SIZE]; +} n230_flash_prog_t; +// +//-------------------------------------------------- + +#define N230_HW_REVISION_COMPAT 1 +#define N230_HW_REVISION_MIN    1 + + +#define N230_CLAIMER_TIMEOUT_IN_MS        2000 + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_N230_FW_HOST_IFACE_H */ diff --git a/host/lib/usrp/n230/n230_image_loader.cpp b/host/lib/usrp/n230/n230_image_loader.cpp new file mode 100644 index 000000000..9dd4a252d --- /dev/null +++ b/host/lib/usrp/n230/n230_image_loader.cpp @@ -0,0 +1,209 @@ +// +// Copyright 2016 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 <fstream> +#include <algorithm> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include "n230_fw_host_iface.h" +#include "n230_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +struct xil_bitfile_hdr_t { +    xil_bitfile_hdr_t(): +        valid(false), userid(0), product(""), +        fpga(""), timestamp(""), filesize(0) +    {} + +    bool            valid; +    boost::uint32_t userid; +    std::string     product; +    std::string     fpga; +    std::string     timestamp; +    boost::uint32_t filesize; +}; + +static inline boost::uint16_t _to_uint16(boost::uint8_t* buf) { +    return (static_cast<boost::uint16_t>(buf[0]) << 8) | +           (static_cast<boost::uint16_t>(buf[1]) << 0); +} + +static inline boost::uint32_t _to_uint32(boost::uint8_t* buf) { +    return (static_cast<boost::uint32_t>(buf[0]) << 24) | +           (static_cast<boost::uint32_t>(buf[1]) << 16) | +           (static_cast<boost::uint32_t>(buf[2]) << 8)  | +           (static_cast<boost::uint32_t>(buf[3]) << 0); +} + +static void _parse_bitfile_header(const std::string& filepath, xil_bitfile_hdr_t& hdr) { +    // Read header into memory +    std::ifstream img_file(filepath.c_str(), std::ios::binary); +    static const size_t MAX_HDR_SIZE = 1024; +    boost::scoped_array<char> hdr_buf(new char[MAX_HDR_SIZE]); +    img_file.seekg(0, std::ios::beg); +    img_file.read(hdr_buf.get(), MAX_HDR_SIZE); +    img_file.close(); + +    //Parse header +    size_t ptr = 0; +    boost::uint8_t* buf = reinterpret_cast<boost::uint8_t*>(hdr_buf.get());  //Shortcut + +    boost::uint8_t signature[10] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0}; +    if (memcmp(buf, signature, 10) == 0) {  //Validate signature +        ptr += _to_uint16(buf + ptr) + 2; +        ptr += _to_uint16(buf + ptr) + 1; + +        std::string fields[4]; +        for (size_t i = 0; i < 4; i++) { +            size_t key = buf[ptr++] - 'a'; +            boost::uint16_t len = _to_uint16(buf + ptr); ptr += 2; +            fields[key] = std::string(reinterpret_cast<char*>(buf + ptr), size_t(len)); ptr += len; +        } + +        hdr.filesize = _to_uint32(buf + ++ptr); ptr += 4; +        hdr.fpga = fields[1]; +        hdr.timestamp = fields[2] + std::string(" ") + fields[3]; + +        std::vector<std::string> tokens; +        boost::split(tokens, fields[0], boost::is_any_of(";")); +        if (tokens.size() == 3) { +            hdr.product = tokens[0]; +            std::vector<std::string> uidtokens; +            boost::split(uidtokens, tokens[1], boost::is_any_of("=")); +            if (uidtokens.size() == 2 and uidtokens[0] == "UserID") { +                std::stringstream stream; +                stream << uidtokens[1]; +                stream >> std::hex >> hdr.userid; +                hdr.valid = true; +            } +        } +    } +} + +static size_t _send_and_recv( +    udp_simple::sptr xport, +    n230_flash_prog_t& out, n230_flash_prog_t& in) +{ +    static boost::uint32_t seqno = 0; +    out.seq = htonx<boost::uint32_t>(++seqno); +    xport->send(boost::asio::buffer(&out, sizeof(n230_flash_prog_t))); +    size_t len = xport->recv(boost::asio::buffer(&in, udp_simple::mtu), 0.5); +    if (len != sizeof(n230_flash_prog_t) or ntohx<boost::uint32_t>(in.seq) != seqno) { +        throw uhd::io_error("Error communicating with the device."); +    } +    return len; +} + + +static bool n230_image_loader(const image_loader::image_loader_args_t &loader_args){ +    // Run discovery routine and ensure that exactly one N230 is specified +    device_addrs_t devs = usrp::n230::n230_impl::n230_find(loader_args.args); +    if (devs.size() == 0 or !loader_args.load_fpga) return false; +    if (devs.size() > 1) { +        throw uhd::runtime_error("Multiple devices match the specified args. To avoid accidentally updating the " +                                 "wrong device, please narrow the search by specifying a unique \"addr\" argument."); +    } +    device_addr_t dev = devs[0]; + +    // Sanity check the specified bitfile +    std::string fpga_img_path = loader_args.fpga_path; +    bool fpga_path_specified = !loader_args.fpga_path.empty(); +    if (not fpga_path_specified) { +        fpga_img_path = ( +            fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "images" / "usrp_n230_fpga.bit" +        ).string(); +    } + +    if (not boost::filesystem::exists(fpga_img_path)) { +        if (fpga_path_specified) { +            throw uhd::runtime_error(str(boost::format("The file \"%s\" does not exist.") % fpga_img_path)); +        } else { +            throw uhd::runtime_error(str(boost::format( +                "Could not find the default FPGA image: %s.\n" +                "Either specify the --fpga-path argument or download the latest prebuilt images:\n" +                "%s\n") +            % fpga_img_path % print_utility_error("uhd_images_downloader.py"))); +        } +    } +    xil_bitfile_hdr_t hdr; +    _parse_bitfile_header(fpga_img_path, hdr); + +    // Create a UDP communication link +    udp_simple::sptr udp_xport = +        udp_simple::make_connected(dev["addr"],BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT)); + +    if (hdr.valid and hdr.product == "n230") { +        if (hdr.userid != 0x5AFE0000) { +            std::cout << boost::format("Unit: USRP N230 (%s, %s)\n-- FPGA Image: %s\n") +                         % dev["addr"] % dev["serial"] % fpga_img_path; + +            // Write image +            std::ifstream image(fpga_img_path.c_str(), std::ios::binary); +            size_t image_size = boost::filesystem::file_size(fpga_img_path); + +            static const size_t SECTOR_SIZE = 65536; +            static const size_t IMAGE_BASE  = 0x400000; + +            n230_flash_prog_t out, in; +            size_t bytes_written = 0; +            while (bytes_written < image_size) { +                size_t payload_size = std::min<size_t>(image_size - bytes_written, N230_FLASH_COMM_MAX_PAYLOAD_SIZE); +                if (bytes_written % SECTOR_SIZE == 0) { +                    out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA); +                    out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE); +                    out.size = htonx<boost::uint32_t>(payload_size); +                    _send_and_recv(udp_xport, out, in); +                } +                out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA); +                out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE); +                out.size = htonx<boost::uint32_t>(payload_size); +                image.read((char*)out.data, payload_size); +                _send_and_recv(udp_xport, out, in); +                bytes_written += ntohx<boost::uint32_t>(in.size); +                std::cout << boost::format("\r-- Loading FPGA image: %d%%") +                             % (int(double(bytes_written) / double(image_size) * 100.0)) +                         << std::flush; +            } +            std::cout << std::endl << "FPGA image loaded successfully." << std::endl; +            std::cout << std::endl << "Power-cycle the device to run the image." << std::endl; +            return true; +        } else { +            throw uhd::runtime_error("This utility cannot burn a failsafe image!"); +        } +    } else { +        throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid USRP N230 FPGA image.") +                                     % fpga_img_path)); +    } +} + +UHD_STATIC_BLOCK(register_n230_image_loader){ +    std::string recovery_instructions = "Aborting. Your USRP N230 device will likely boot in safe mode.\n" +                                        "Please re-run this command with the additional \"safe_mode\" device argument\n" +                                        "to recover your device."; + +    image_loader::register_image_loader("n230", n230_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/n230/n230_impl.cpp b/host/lib/usrp/n230/n230_impl.cpp new file mode 100644 index 000000000..b005182cf --- /dev/null +++ b/host/lib/usrp/n230/n230_impl.cpp @@ -0,0 +1,591 @@ +// +// 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 "n230_impl.hpp" + +#include "usrp3_fw_ctrl_iface.hpp" +#include "validate_subdev_spec.hpp" +#include <uhd/utils/static.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/direction.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/asio/ip/address_v4.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include <boost/make_shared.hpp> + +#include "../common/fw_comm_protocol.h" +#include "n230_defaults.h" +#include "n230_fpga_defs.h" +#include "n230_fw_defs.h" +#include "n230_fw_host_iface.h" + +namespace uhd { namespace usrp { namespace n230 { + +using namespace uhd::transport; +namespace asio = boost::asio; + +//---------------------------------------------------------- +// Static device registration with framework +//---------------------------------------------------------- +UHD_STATIC_BLOCK(register_n230_device) +{ +    device::register_device(&n230_impl::n230_find, &n230_impl::n230_make, device::USRP); +} + +//---------------------------------------------------------- +// Device discovery +//---------------------------------------------------------- +uhd::device_addrs_t n230_impl::n230_find(const uhd::device_addr_t &multi_dev_hint) +{ +    //handle the multi-device discovery +    device_addrs_t hints = separate_device_addr(multi_dev_hint); +    if (hints.size() > 1){ +        device_addrs_t found_devices; +        std::string error_msg; +        BOOST_FOREACH(const device_addr_t &hint_i, hints){ +            device_addrs_t found_devices_i = n230_find(hint_i); +            if (found_devices_i.size() != 1) error_msg += str(boost::format( +                "Could not resolve device hint \"%s\" to a single device." +            ) % hint_i.to_string()); +            else found_devices.push_back(found_devices_i[0]); +        } +        if (found_devices.empty()) return device_addrs_t(); +        if (not error_msg.empty()) throw uhd::value_error(error_msg); +        return device_addrs_t(1, combine_device_addrs(found_devices)); +    } + +    //initialize the hint for a single device case +    UHD_ASSERT_THROW(hints.size() <= 1); +    hints.resize(1); //in case it was empty +    device_addr_t hint = hints[0]; +    device_addrs_t n230_addrs; + +    //return an empty list of addresses when type is set to non-n230 +    if (hint.has_key("type") and hint["type"] != "n230") return n230_addrs; + +    //Return an empty list of addresses when a resource is specified, +    //since a resource is intended for a different, non-networked, device. +    if (hint.has_key("resource")) return n230_addrs; + +    //if no address was specified, send a broadcast on each interface +    if (not hint.has_key("addr")) { +        BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) { +            //avoid the loopback device +            if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue; + +            //create a new hint with this broadcast address +            device_addr_t new_hint = hint; +            new_hint["addr"] = if_addrs.bcast; + +            //call discover with the new hint and append results +            device_addrs_t new_n230_addrs = n230_find(new_hint); +            n230_addrs.insert(n230_addrs.begin(), +                new_n230_addrs.begin(), new_n230_addrs.end() +            ); +        } +        return n230_addrs; +    } + +    std::vector<std::string> discovered_addrs = +        usrp3::usrp3_fw_ctrl_iface::discover_devices( +            hint["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID); + +    BOOST_FOREACH(const std::string& addr, discovered_addrs) +    { +        device_addr_t new_addr; +        new_addr["type"] = "n230"; +        new_addr["addr"] = addr; + +        //Attempt a simple 2-way communication with a connected socket. +        //Reason: Although the USRP will respond the broadcast above, +        //we may not be able to communicate directly (non-broadcast). +        udp_simple::sptr ctrl_xport = udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)); + +        //Corner case: If two devices have the same IP but different MAC +        //addresses and are used back-to-back it takes a while for ARP tables +        //on the host to update in which period brodcasts will respond but +        //connected communication can fail. Retry the following call to allow +        //the stack to update +        size_t first_conn_retries = 10; +        usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl; +        while (first_conn_retries > 0) { +            try { +                fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make(ctrl_xport, N230_FW_PRODUCT_ID, false /*verbose*/); +                break; +            } catch (uhd::io_error& ex) { +                boost::this_thread::sleep(boost::posix_time::milliseconds(500)); +                first_conn_retries--; +            } +        } +        if (first_conn_retries > 0) { +            uint32_t compat_reg = fw_ctrl->peek32(fw::reg_addr(fw::WB_SBRB_BASE, fw::RB_ZPU_COMPAT)); +            if (fw::get_prod_num(compat_reg) == fw::PRODUCT_NUM) { +                if (!n230_resource_manager::is_device_claimed(fw_ctrl)) { +                    //Not claimed by another process or host +                    try { +                        //Try to read the EEPROM to get the name and serial +                        n230_eeprom_manager eeprom_mgr(new_addr["addr"]); +                        const mboard_eeprom_t& eeprom = eeprom_mgr.get_mb_eeprom(); +                        new_addr["name"] = eeprom["name"]; +                        new_addr["serial"] = eeprom["serial"]; +                    } +                    catch(const std::exception &) +                    { +                        //set these values as empty string so the device may still be found +                        //and the filter's below can still operate on the discovered device +                        new_addr["name"] = ""; +                        new_addr["serial"] = ""; +                    } +                    //filter the discovered device below by matching optional keys +                    if ((not hint.has_key("name")    or hint["name"]    == new_addr["name"]) and +                        (not hint.has_key("serial")  or hint["serial"]  == new_addr["serial"])) +                    { +                        n230_addrs.push_back(new_addr); +                    } +                } +            } +        } +    } + +    return n230_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +device::sptr n230_impl::n230_make(const device_addr_t &device_addr) +{ +    return device::sptr(new n230_impl(device_addr)); +} + +/*********************************************************************** + * n230_impl::ctor + **********************************************************************/ +n230_impl::n230_impl(const uhd::device_addr_t& dev_addr) +{ +    UHD_MSG(status) << "N230 initialization sequence..." << std::endl; +    _dev_args.parse(dev_addr); +    _tree = uhd::property_tree::make(); + +    //TODO: Only supports one motherboard per device class. +    const fs_path mb_path = "/mboards/0"; + +    //Initialize addresses +    std::vector<std::string> ip_addrs(1, dev_addr["addr"]); +    if (dev_addr.has_key("secondary-addr")) { +        ip_addrs.push_back(dev_addr["secondary-addr"]); +    } + +    //Read EEPROM and perform version checks before talking to HW +    _eeprom_mgr = boost::make_shared<n230_eeprom_manager>(ip_addrs[0]); +    const mboard_eeprom_t& mb_eeprom = _eeprom_mgr->get_mb_eeprom(); +    bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); +    if (recover_mb_eeprom) { +        UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version " +                            "checks.\nOperating in this mode may cause hardware damage and unstable " +                            "radio performance!"<< std::endl; +    } +    boost::uint16_t hw_rev = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision"]); +    boost::uint16_t hw_rev_compat = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision_compat"]); +    if (not recover_mb_eeprom) { +        if (hw_rev_compat > N230_HW_REVISION_COMPAT) { +            throw uhd::runtime_error(str(boost::format( +                "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.") +                % hw_rev)); +        } +    } + +    //Initialize all subsystems +    _resource_mgr = boost::make_shared<n230_resource_manager>(ip_addrs, _dev_args.get_safe_mode()); +    _stream_mgr = boost::make_shared<n230_stream_manager>(_dev_args, _resource_mgr, _tree); + +    //Build property tree +    _initialize_property_tree(mb_path); + +    //Debug loopback mode +    switch(_dev_args.get_loopback_mode()) { +    case n230_device_args_t::LOOPBACK_RADIO: +        UHD_MSG(status) << "DEBUG: Running in TX->RX Radio loopback mode.\n"; +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_RADIO); +        break; +    case n230_device_args_t::LOOPBACK_CODEC: +        UHD_MSG(status) << "DEBUG: Running in TX->RX CODEC loopback mode.\n"; +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_CODEC); +        break; +    default: +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_DISABLED); +        break; +    } +} + +/*********************************************************************** + * n230_impl::dtor + **********************************************************************/ +n230_impl::~n230_impl() +{ +    _stream_mgr.reset(); +    _eeprom_mgr.reset(); +    _resource_mgr.reset(); +    _tree.reset(); +} + +/*********************************************************************** + * n230_impl::get_rx_stream + **********************************************************************/ +rx_streamer::sptr n230_impl::get_rx_stream(const uhd::stream_args_t &args) +{ +    return _stream_mgr->get_rx_stream(args); +} + +/*********************************************************************** + * n230_impl::get_tx_stream + **********************************************************************/ +tx_streamer::sptr n230_impl::get_tx_stream(const uhd::stream_args_t &args) +{ +    return _stream_mgr->get_tx_stream(args); +} + +/*********************************************************************** + * n230_impl::recv_async_msg + **********************************************************************/ +bool n230_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) +{ +    return _stream_mgr->recv_async_msg(async_metadata, timeout); +} + +/*********************************************************************** + * _initialize_property_tree + **********************************************************************/ +void n230_impl::_initialize_property_tree(const fs_path& mb_path) +{ +    //------------------------------------------------------------------ +    // General info +    //------------------------------------------------------------------ +    _tree->create<std::string>("/name").set("N230 Device"); + +    _tree->create<std::string>(mb_path / "name").set("N230"); +    _tree->create<std::string>(mb_path / "codename").set("N230"); +    _tree->create<std::string>(mb_path / "dboards").set("none");    //No dboards. + +    _tree->create<std::string>(mb_path / "fw_version").set(str(boost::format("%u.%u") +        % _resource_mgr->get_version(FIRMWARE, COMPAT_MAJOR) +        % _resource_mgr->get_version(FIRMWARE, COMPAT_MINOR))); +    _tree->create<std::string>(mb_path / "fw_version_hash").set(str(boost::format("%s") +        % _resource_mgr->get_version_hash(FIRMWARE))); +    _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") +        % _resource_mgr->get_version(FPGA, COMPAT_MAJOR) +        % _resource_mgr->get_version(FPGA, COMPAT_MINOR))); +    _tree->create<std::string>(mb_path / "fpga_version_hash").set(str(boost::format("%s") +        % _resource_mgr->get_version_hash(FPGA))); + +    _tree->create<double>(mb_path / "link_max_rate").set(_resource_mgr->get_max_link_rate()); + +    //------------------------------------------------------------------ +    // EEPROM +    //------------------------------------------------------------------ +    _tree->create<mboard_eeprom_t>(mb_path / "eeprom") +        .set(_eeprom_mgr->get_mb_eeprom())  //Set first... +        .subscribe(boost::bind(&n230_eeprom_manager::write_mb_eeprom, _eeprom_mgr, _1));  //..then enable writer + +    //------------------------------------------------------------------ +    // Create codec nodes +    //------------------------------------------------------------------ +    const fs_path rx_codec_path = mb_path / ("rx_codecs") / "A"; +    _tree->create<std::string>(rx_codec_path / "name") +        .set("N230 RX dual ADC"); +    _tree->create<int>(rx_codec_path / "gains");    //Empty because gains are in frontend + +    const fs_path tx_codec_path = mb_path / ("tx_codecs") / "A"; +    _tree->create<std::string>(tx_codec_path / "name") +        .set("N230 TX dual DAC"); +    _tree->create<int>(tx_codec_path / "gains");    //Empty because gains are in frontend + +    //------------------------------------------------------------------ +    // Create clock and time control nodes +    //------------------------------------------------------------------ +    _tree->create<double>(mb_path / "tick_rate") +        .coerce(boost::bind(&n230_clk_pps_ctrl::set_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .publish(boost::bind(&n230_clk_pps_ctrl::get_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr())) +        .subscribe(boost::bind(&n230_stream_manager::update_tick_rate, _stream_mgr, _1)); + +    //Register time now and pps onto available radio cores +    //radio0 is the master +    _tree->create<time_spec_t>(mb_path / "time" / "cmd"); +    _tree->create<time_spec_t>(mb_path / "time" / "now") +        .publish(boost::bind(&time_core_3000::get_time_now, _resource_mgr->get_radio(0).time)); +    _tree->create<time_spec_t>(mb_path / "time" / "pps") +        .publish(boost::bind(&time_core_3000::get_time_last_pps, _resource_mgr->get_radio(0).time)); + +    //Setup time source props +    _tree->create<std::string>(mb_path / "time_source" / "value") +        .subscribe(boost::bind(&n230_impl::_check_time_source, this, _1)) +        .subscribe(boost::bind(&n230_clk_pps_ctrl::set_pps_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .set(n230::DEFAULT_TIME_SRC); +    static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo"); +    _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options") +        .set(time_sources); + +    //Setup reference source props +    _tree->create<std::string>(mb_path / "clock_source" / "value") +        .subscribe(boost::bind(&n230_impl::_check_clock_source, this, _1)) +        .subscribe(boost::bind(&n230_clk_pps_ctrl::set_clock_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .set(n230::DEFAULT_CLOCK_SRC); +    static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo"); +    _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options") +        .set(clock_sources); +    _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") +        .publish(boost::bind(&n230_clk_pps_ctrl::get_ref_locked, _resource_mgr->get_clk_pps_ctrl_sptr())); + +    //------------------------------------------------------------------ +    // Create frontend mapping +    //------------------------------------------------------------------ +    _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") +        .set(subdev_spec_t()) +        .subscribe(boost::bind(&n230_impl::_update_rx_subdev_spec, this, _1)); +    _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") +        .set(subdev_spec_t()) +        .subscribe(boost::bind(&n230_impl::_update_tx_subdev_spec, this, _1)); + +    //------------------------------------------------------------------ +    // Create a fake dboard to put frontends in +    //------------------------------------------------------------------ +    //For completeness we give it a fake EEPROM as well +    dboard_eeprom_t db_eeprom;  //Default state: ID is 0xffff, Version and serial empty +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom); +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom); +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom); + +    //------------------------------------------------------------------ +    // Create radio specific nodes +    //------------------------------------------------------------------ +    for (size_t radio_instance = 0; radio_instance < fpga::NUM_RADIOS; radio_instance++) { +        _initialize_radio_properties(mb_path, radio_instance); +    } +    //Update tick rate on newly created radio objects +    _tree->access<double>(mb_path / "tick_rate").set(_dev_args.get_master_clock_rate()); + +    //------------------------------------------------------------------ +    // Initialize subdev specs +    //------------------------------------------------------------------ +    subdev_spec_t rx_spec, tx_spec; +    BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends")) +    { +        rx_spec.push_back(subdev_spec_pair_t("A", fe)); +    } +    BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends")) +    { +        tx_spec.push_back(subdev_spec_pair_t("A", fe)); +    } +    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); +    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); + +    //------------------------------------------------------------------ +    // MiniSAS GPIO +    //------------------------------------------------------------------ +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "DDR") +        .set(0) +        .subscribe(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_DDR, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "DDR") +        .set(0) +        .subscribe(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_DDR, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "OUT") +        .set(0) +        .subscribe(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_OUT, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "OUT") +        .set(0) +        .subscribe(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_OUT, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") +        .publish(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(0))); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "READBACK") +        .publish(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(1))); + +    //------------------------------------------------------------------ +    // GPSDO sensors +    //------------------------------------------------------------------ +    if (_resource_mgr->is_gpsdo_present()) { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        BOOST_FOREACH(const std::string &name, gps_ctrl->get_sensors()) +        { +            _tree->create<sensor_value_t>(mb_path / "sensors" / name) +                .publish(boost::bind(&gps_ctrl::get_sensor, gps_ctrl, name)); +        } +    } +} + +/*********************************************************************** + * _initialize_radio_properties + **********************************************************************/ +void n230_impl::_initialize_radio_properties(const fs_path& mb_path, size_t instance) +{ +    radio_resource_t& perif = _resource_mgr->get_radio(instance); + +    //Time +    _tree->access<time_spec_t>(mb_path / "time" / "cmd") +        .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); +    _tree->access<double>(mb_path / "tick_rate") +        .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); +    _tree->access<time_spec_t>(mb_path / "time" / "now") +        .subscribe(boost::bind(&time_core_3000::set_time_now, perif.time, _1)); +    _tree->access<time_spec_t>(mb_path / "time" / "pps") +        .subscribe(boost::bind(&time_core_3000::set_time_next_pps, perif.time, _1)); + +    //RX DSP +    _tree->access<double>(mb_path / "tick_rate") +        .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) +        .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); +    const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % instance); +    _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") +        .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); +    _tree->create<double>(rx_dsp_path / "rate" / "value") +        .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) +        .subscribe(boost::bind(&n230_stream_manager::update_rx_samp_rate, _stream_mgr, instance, _1)) +        .set(n230::DEFAULT_RX_SAMP_RATE); +    _tree->create<double>(rx_dsp_path / "freq" / "value") +        .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) +        .set(n230::DEFAULT_DDC_FREQ); +    _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") +        .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); +    _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") +        .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); + +    //TX DSP +    _tree->access<double>(mb_path / "tick_rate") +        .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); +    const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % instance); +    _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") +        .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); +    _tree->create<double>(tx_dsp_path / "rate" / "value") +        .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) +        .subscribe(boost::bind(&n230_stream_manager::update_tx_samp_rate, _stream_mgr, instance, _1)) +        .set(n230::DEFAULT_TX_SAMP_RATE); +    _tree->create<double>(tx_dsp_path / "freq" / "value") +        .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) +        .set(n230::DEFAULT_DUC_FREQ); +    _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") +        .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); + +    //RF Frontend Interfacing +    static const std::vector<direction_t> data_directions = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); +    BOOST_FOREACH(direction_t direction, data_directions) { +        const std::string dir_str = (direction == RX_DIRECTION) ? "rx" : "tx"; +        const std::string key = boost::to_upper_copy(dir_str) + str(boost::format("%u") % (instance + 1)); +        const fs_path rf_fe_path = mb_path / "dboards" / "A" / (dir_str + "_frontends") / ((instance==0)?"A":"B"); + +        //CODEC subtree +        _resource_mgr->get_codec_mgr().populate_frontend_subtree(_tree->subtree(rf_fe_path), key, direction); + +        //User settings +        _tree->create<uhd::wb_iface::sptr>(rf_fe_path / "user_settings" / "iface") +            .set(perif.user_settings); + +        //Setup antenna stuff +        if (key[0] == 'R') { +            static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); +            _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options") +                .set(ants); +            _tree->create<std::string>(rf_fe_path / "antenna" / "value") +                .subscribe(boost::bind(&n230_frontend_ctrl::set_antenna_sel, _resource_mgr->get_frontend_ctrl_sptr(), instance, _1)) +                .set("RX2"); +        } +        if (key[0] == 'T') { +            static const std::vector<std::string> ants(1, "TX/RX"); +            _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options") +                .set(ants); +            _tree->create<std::string>(rf_fe_path / "antenna" / "value") +                .set("TX/RX"); +        } +    } +} + +void n230_impl::_update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ +    //sanity checking +    if (spec.size()) validate_subdev_spec(_tree, spec, "rx"); +    UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS); + +    if (spec.size() > 0) { +        UHD_ASSERT_THROW(spec[0].db_name == "A"); +        UHD_ASSERT_THROW(spec[0].sd_name == "A"); +    } +    if (spec.size() > 1) { +        //TODO we can support swapping at a later date, only this combo is supported +        UHD_ASSERT_THROW(spec[1].db_name == "A"); +        UHD_ASSERT_THROW(spec[1].sd_name == "B"); +    } + +    _stream_mgr->update_stream_states(); +} + +void n230_impl::_update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ +    //sanity checking +    if (spec.size()) validate_subdev_spec(_tree, spec, "tx"); +    UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS); + +    if (spec.size() > 0) { +        UHD_ASSERT_THROW(spec[0].db_name == "A"); +        UHD_ASSERT_THROW(spec[0].sd_name == "A"); +    } +    if (spec.size() > 1) { +        //TODO we can support swapping at a later date, only this combo is supported +        UHD_ASSERT_THROW(spec[1].db_name == "A"); +        UHD_ASSERT_THROW(spec[1].sd_name == "B"); +    } + +    _stream_mgr->update_stream_states(); +} + +void n230_impl::_check_time_source(std::string source) +{ +    if (source == "gpsdo") +    { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        if (not (gps_ctrl and gps_ctrl->gps_detected())) +            throw uhd::runtime_error("GPSDO time source not available"); +    } +} + +void n230_impl::_check_clock_source(std::string source) +{ +    if (source == "gpsdo") +    { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        if (not (gps_ctrl and gps_ctrl->gps_detected())) +            throw uhd::runtime_error("GPSDO clock source not available"); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_impl.hpp b/host/lib/usrp/n230/n230_impl.hpp new file mode 100644 index 000000000..b644dd8a3 --- /dev/null +++ b/host/lib/usrp/n230/n230_impl.hpp @@ -0,0 +1,81 @@ +// +// 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_N230_IMPL_HPP +#define INCLUDED_N230_IMPL_HPP + +#include <uhd/property_tree.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/subdev_spec.hpp> + +#include "n230_device_args.hpp" +#include "n230_eeprom_manager.hpp" +#include "n230_resource_manager.hpp" +#include "n230_stream_manager.hpp" +#include "recv_packet_demuxer_3000.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_impl : public uhd::device +{ +public: //Functions +    // ctor and dtor +    n230_impl(const uhd::device_addr_t& device_addr); +    virtual ~n230_impl(void); + +    //--------------------------------------------------------------------- +    // uhd::device interface +    // +    static sptr make(const uhd::device_addr_t &hint, size_t which = 0); + +    //! Make a new receive streamer from the streamer arguments +    virtual uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + +    //! Make a new transmit streamer from the streamer arguments +    virtual uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); + +    //!Receive and asynchronous message from the device. +    virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout = 0.1); + +    //!Registration methods the discovery and factory system. +    //[static void register_device(const find_t &find, const make_t &make)] +    static uhd::device_addrs_t n230_find(const uhd::device_addr_t &hint); +    static uhd::device::sptr n230_make(const uhd::device_addr_t &device_addr); +    // +    //--------------------------------------------------------------------- + +    typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + +private:    //Functions +    void _initialize_property_tree(const fs_path& mb_path); +    void _initialize_radio_properties(const fs_path& mb_path, size_t instance); + +    void _update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); +    void _update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); +    void _check_time_source(std::string); +    void _check_clock_source(std::string); + +private:    //Classes and Members +    n230_device_args_t                        _dev_args; +    boost::shared_ptr<n230_resource_manager>  _resource_mgr; +    boost::shared_ptr<n230_eeprom_manager>    _eeprom_mgr; +    boost::shared_ptr<n230_stream_manager>    _stream_mgr; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_IMPL_HPP */ diff --git a/host/lib/usrp/n230/n230_resource_manager.cpp b/host/lib/usrp/n230/n230_resource_manager.cpp new file mode 100644 index 000000000..f13dd0b33 --- /dev/null +++ b/host/lib/usrp/n230/n230_resource_manager.cpp @@ -0,0 +1,569 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_resource_manager.hpp" + +#include "usrp3_fw_ctrl_iface.hpp" +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/platform.hpp> +#include <uhd/utils/paths.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/functional/hash.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/make_shared.hpp> +#include "n230_fw_defs.h" +#include "n230_fw_host_iface.h" + +#define IF_DATA_I_MASK  0xFFF00000 +#define IF_DATA_Q_MASK  0x0000FFF0 + +namespace uhd { namespace usrp { namespace n230 { + +//Constants +static const uint8_t N230_HOST_SRC_ADDR_ETH0 = 0; +static const uint8_t N230_HOST_SRC_ADDR_ETH1 = 1; +static const uint8_t N230_HOST_DEST_ADDR     = 2; + +static const uint8_t N230_ETH0_IFACE_ID  = 0; +static const uint8_t N230_ETH1_IFACE_ID  = 1; + +class n230_ad9361_client_t : public ad9361_params { +public: +    ~n230_ad9361_client_t() {} +    double get_band_edge(frequency_band_t band) { +        switch (band) { +            case AD9361_RX_BAND0:   return 2.2e9; +            case AD9361_RX_BAND1:   return 4.0e9; +            case AD9361_TX_BAND0:   return 2.5e9; +            default:                return 0; +        } +    } +    clocking_mode_t get_clocking_mode() { +        return AD9361_XTAL_N_CLK_PATH; +    } +    digital_interface_mode_t get_digital_interface_mode() { +        return AD9361_DDR_FDD_LVDS; +    } +    digital_interface_delays_t get_digital_interface_timing() { +        digital_interface_delays_t delays; +        delays.rx_clk_delay = 0; +        delays.rx_data_delay = 0; +        delays.tx_clk_delay = 0; +        delays.tx_data_delay = 2; +        return delays; +    } +}; + +n230_resource_manager::n230_resource_manager( +    const std::vector<std::string> ip_addrs, +    const bool safe_mode +) : +    _safe_mode(safe_mode), +    _last_host_enpoint(0) +{ +    if (_safe_mode) UHD_MSG(warning) << "Initializing device in safe mode\n"; +    UHD_MSG(status) << "Setup basic communication...\n"; + +    //Discover ethernet interfaces +    bool dual_eth_expected = (ip_addrs.size() > 1); +    BOOST_FOREACH(const std::string& addr, ip_addrs) { +        n230_eth_conn_t conn_iface; +        conn_iface.ip_addr = addr; + +        boost::uint32_t iface_id = 0xFFFFFFFF; +        try { +            iface_id = usrp3::usrp3_fw_ctrl_iface::get_iface_id( +                conn_iface.ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID); +        } catch (uhd::io_error&) { +            throw uhd::io_error(str(boost::format( +                "Could not communicate with the device over address %s") % +                conn_iface.ip_addr)); +        } +        switch (iface_id) { +            case N230_ETH0_IFACE_ID: conn_iface.type = ETH0; break; +            case N230_ETH1_IFACE_ID: conn_iface.type = ETH1; break; +            default: { +                if (dual_eth_expected) { +                    throw uhd::runtime_error("N230 Initialization Error: Could not detect ethernet port number."); +                } else { +                    //For backwards compatibility, if only one port is specified, assume that a detection +                    //failure means that the device does not support dual-ethernet behavior. +                    conn_iface.type = ETH0; break; +                } +            } +        } +        _eth_conns.push_back(conn_iface); +    } +    if (_eth_conns.size() < 1) { +        throw uhd::runtime_error("N230 Initialization Error: No eth interfaces specified.)"); +    } + +    //Create firmware communication interface +    _fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make( +        transport::udp_simple::make_connected( +            _get_conn(PRI_ETH).ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)), N230_FW_PRODUCT_ID); +    if (_fw_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create n230_ctrl_iface.)"); +    } +    _check_fw_compat(); + +    //Start the device claimer +    _claimer_task = uhd::task::make(boost::bind(&n230_resource_manager::_claimer_loop, this)); + +    //Create common settings interface +    const sid_t core_sid = _generate_sid(CORE, _get_conn(PRI_ETH).type); + +    transport::udp_zero_copy::buff_params dummy_out_params; +    transport::zero_copy_if::sptr core_xport = +        _create_transport(_get_conn(PRI_ETH), core_sid, device_addr_t(), dummy_out_params); +    if (core_xport.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create settings transport.)"); +    } +    _core_ctrl = radio_ctrl_core_3000::make( +        fpga::CVITA_BIG_ENDIAN, core_xport, core_xport, core_sid.get()); +    if (_core_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create settings ctrl.)"); +    } +    _check_fpga_compat(); + +    UHD_MSG(status) << boost::format("Version signatures... Firmware:%s FPGA:%s...\n") +        % _fw_version.get_hash_str() % _fpga_version.get_hash_str(); + +    _core_radio_ctrl_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_misc_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_pps_sel_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_status_reg.initialize(*_core_ctrl); + +    //Create common SPI interface +    _core_spi_ctrl = n230_core_spi_core::make(_core_ctrl); +    if (_core_spi_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create SPI ctrl.)"); +    } + +    //Create AD9361 interface +    UHD_MSG(status) << "Initializing CODEC...\n"; +    _codec_ctrl = ad9361_ctrl::make_spi( +        boost::make_shared<n230_ad9361_client_t>(), _core_spi_ctrl, fpga::AD9361_SPI_SLAVE_NUM); +    if (_codec_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create Catalina ctrl.)"); +    } +    _codec_ctrl->set_clock_rate(fpga::CODEC_DEFAULT_CLK_RATE); +    _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS); +    _codec_mgr->init_codec(); + +    //Create AD4001 interface +    _ref_pll_ctrl = boost::make_shared<n230_ref_pll_ctrl>(_core_spi_ctrl); +    if (_ref_pll_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create ADF4001 ctrl.)"); +    } + +    //Reset SERDES interface and synchronize to frame sync from AD9361 +    _reset_codec_digital_interface(); + +    std::vector<time_core_3000::sptr> time_cores; +    std::vector<gpio_atr::gpio_atr_3000::sptr> gpio_cores; +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        _initialize_radio(i); +        time_cores.push_back(_radios[i].time); +        gpio_cores.push_back(_radios[i].gpio_atr); +    } + +    //Create clock and PPS control interface +    _clk_pps_ctrl = n230_clk_pps_ctrl::make( +        _codec_ctrl, _ref_pll_ctrl, _core_misc_reg, _core_pps_sel_reg, _core_status_reg, time_cores); +    if (_clk_pps_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create clock and PPS ctrl.)"); +    } + +    //Create front-end control interface +    _frontend_ctrl = n230_frontend_ctrl::make(_core_ctrl, _core_misc_reg, _codec_ctrl, gpio_cores); +    if (_frontend_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create front-end ctrl.)"); +    } + +    //Create miniSAS GPIO interfaces +    _ms0_gpio = gpio_atr::gpio_atr_3000::make( +        _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS0_GPIO), fpga::rb_addr(fpga::RB_CORE_MS0_GPIO)); +    _ms0_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL); +    _ms1_gpio = gpio_atr::gpio_atr_3000::make( +        _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS1_GPIO), fpga::rb_addr(fpga::RB_CORE_MS1_GPIO)); +    _ms1_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL); + +    //Create GPSDO interface +    if (_core_status_reg.read(fpga::core_status_reg_t::GPSDO_STATUS) != fpga::GPSDO_ST_ABSENT) { +        UHD_MSG(status) << "Detecting GPSDO.... " << std::flush; +        try { +            const sid_t gps_uart_sid = _generate_sid(GPS_UART, _get_conn(PRI_ETH).type); +            transport::zero_copy_if::sptr gps_uart_xport = +                _create_transport(_get_conn(PRI_ETH), gps_uart_sid, device_addr_t(), dummy_out_params); +            _gps_uart = n230_uart::make(gps_uart_xport, uhd::htonx(gps_uart_sid.get())); +            _gps_uart->set_baud_divider(fpga::BUS_CLK_RATE/fpga::GPSDO_UART_BAUDRATE); +            _gps_uart->write_uart("\n"); //cause the baud and response to be setup +            boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation +            _gps_ctrl = gps_ctrl::make(_gps_uart); +        } catch(std::exception &e) { +            UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; +        } +        if (not is_gpsdo_present()) { +            _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_GPSDO_ST), fpga::GPSDO_ST_ABSENT); +        } +    } + +    //Perform data self-tests +    _frontend_ctrl->set_stream_state(TXRX_STREAMING, TXRX_STREAMING); +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        _frontend_ctrl->set_self_test_mode(LOOPBACK_RADIO); +        bool radio_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl); +        if (!radio_selftest_pass) { +            throw uhd::runtime_error("N230 Initialization Error: Data loopback test failed.)"); +        } + +        _frontend_ctrl->set_self_test_mode(LOOPBACK_CODEC); +        bool codec_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl); +        if (!codec_selftest_pass) { +            throw uhd::runtime_error("N230 Initialization Error: Codec loopback test failed.)"); +        } +    } +    _frontend_ctrl->set_self_test_mode(LOOPBACK_DISABLED); +    _frontend_ctrl->set_stream_state(NONE_STREAMING, NONE_STREAMING); +} + +n230_resource_manager::~n230_resource_manager() +{ +    _claimer_task.reset(); +    {   //Critical section +        boost::mutex::scoped_lock(_claimer_mutex); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), 0); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), 0); +    } +} + +transport::zero_copy_if::sptr n230_resource_manager::create_transport( +    n230_data_dir_t direction, +    size_t radio_instance, +    const device_addr_t ¶ms, +    sid_t& sid_pair, +    transport::udp_zero_copy::buff_params& buff_out_params) +{ +    const n230_eth_conn_t& conn = _get_conn((radio_instance==1)?SEC_ETH:PRI_ETH); +    const sid_t temp_sid_pair = +        _generate_sid(direction==RX_DATA?RADIO_RX_DATA:RADIO_TX_DATA, conn.type, radio_instance); +    transport::zero_copy_if::sptr xport = _create_transport(conn, temp_sid_pair, params, buff_out_params); +    if (xport.get() == NULL) { +        throw uhd::runtime_error("N230 Create Data Transport: Could not create data transport.)"); +    } else { +        sid_pair = temp_sid_pair; +    } +    return xport; +} + +bool n230_resource_manager::is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl) +{ +    boost::mutex::scoped_lock(_claimer_mutex); + +    //If timed out then device is definitely unclaimed +    if (fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_status)) == 0) +        return false; + +    //otherwise check claim src to determine if another thread with the same src has claimed the device +    return fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_src)) != get_process_hash(); +} + +void n230_resource_manager::_claimer_loop() +{ +    {   //Critical section +        boost::mutex::scoped_lock(_claimer_mutex); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), time(NULL)); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), get_process_hash()); +    } +    boost::this_thread::sleep(boost::posix_time::milliseconds(N230_CLAIMER_TIMEOUT_IN_MS / 2)); +} + +void n230_resource_manager::_initialize_radio(size_t instance) +{ +    radio_resource_t& radio = _radios[instance]; + +    //Create common settings interface +    const sid_t ctrl_sid = _generate_sid(RADIO_CONTROL, _get_conn(PRI_ETH).type, instance); +    transport::udp_zero_copy::buff_params buff_out_params; +    transport::zero_copy_if::sptr ctrl_xport = +        _create_transport(_get_conn(PRI_ETH), ctrl_sid, device_addr_t(), buff_out_params); +    if (ctrl_xport.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create radio transport.)"); +    } +    radio.ctrl = radio_ctrl_core_3000::make( +        fpga::CVITA_BIG_ENDIAN, ctrl_xport, ctrl_xport, ctrl_sid.get()); +    if (radio.ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create radio ctrl.)"); +    } + +    //Perform register loopback test to verify the radio clock +    bool reg_selftest_pass = _radio_register_loopback_self_test(radio.ctrl); +    if (!reg_selftest_pass) { +        throw uhd::runtime_error("N230 Initialization Error: Register loopback test failed.)"); +    } + +    //Write-only ATR interface +    radio.gpio_atr = gpio_atr::gpio_atr_3000::make_write_only(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_ATR)); +    radio.gpio_atr->set_atr_mode(gpio_atr::MODE_ATR,gpio_atr::gpio_atr_3000::MASK_SET_ALL); + +    //Core VITA time interface +    time_core_3000::readback_bases_type time_bases; +    time_bases.rb_now = fpga::rb_addr(fpga::RB_RADIO_TIME_NOW); +    time_bases.rb_pps = fpga::rb_addr(fpga::RB_RADIO_TIME_PPS); +    radio.time = time_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TIME), time_bases); +    if (radio.time.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create time core.)"); +    } + +    //RX DSP +    radio.framer = rx_vita_core_3000::make( +        radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_CTRL)); +    radio.ddc = rx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_DSP), true /*old DDC?*/); +    if (radio.framer.get() == NULL || radio.ddc.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)"); +    } +    radio.ddc->set_link_rate(fpga::N230_LINK_RATE_BPS); + +    //TX DSP +    radio.deframer = tx_vita_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_CTRL)); +    radio.duc = tx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_DSP)); +    if (radio.deframer.get() == NULL || radio.duc.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)"); +    } +    radio.duc->set_link_rate(fpga::N230_LINK_RATE_BPS); + +    //User settings +    radio.user_settings = user_settings_core_3000::make(radio.ctrl, +        fpga::sr_addr(fpga::SR_RADIO_USER_SR), fpga::rb_addr(fpga::SR_RADIO_USER_RB)); +    if (radio.user_settings.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create user settings bus.)"); +    } +} + +boost::uint8_t xb_ep_to_sid(fpga::xb_endpoint_t ep) { +    return static_cast<boost::uint8_t>(ep) << 4; +} + +const sid_t n230_resource_manager::_generate_sid(const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance) +{ +    fpga::xb_endpoint_t xb_dest_ep; +    boost::uint8_t sid_dest_ep = 0; +    fpga::xb_endpoint_t xb_ret_ep = (xport == ETH1) ? fpga::N230_XB_DST_E1 : fpga::N230_XB_DST_E0; +    boost::uint8_t sid_ret_addr = (xport == ETH1) ? N230_HOST_SRC_ADDR_ETH1 : N230_HOST_SRC_ADDR_ETH0; + +    if (type == CORE or type == GPS_UART) { +        //Non-radio endpoints +        xb_dest_ep = (type == CORE) ? fpga::N230_XB_DST_GCTRL : fpga::N230_XB_DST_UART; +        sid_dest_ep = xb_ep_to_sid(xb_dest_ep); +    } else { +        //Radio endpoints +        xb_dest_ep = (instance == 1) ? fpga::N230_XB_DST_R1 : fpga::N230_XB_DST_R0; +        sid_dest_ep = xb_ep_to_sid(xb_dest_ep); +        switch (type) { +        case RADIO_TX_DATA: +            sid_dest_ep |= fpga::RADIO_DATA_SUFFIX; +            break; +        case RADIO_RX_DATA: +            sid_dest_ep |= fpga::RADIO_FC_SUFFIX; +            break; +        default: +            sid_dest_ep |= fpga::RADIO_CTRL_SUFFIX; +            break; +        } +    } + +    //Increment last host logical endpoint +    sid_t sid(sid_ret_addr, ++_last_host_enpoint, N230_HOST_DEST_ADDR, sid_dest_ep); + +    //Program the crossbar addr +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, fw::SR_ZPU_XB_LOCAL), sid.get_dst_addr()); +    // Program CAM entry for returning packets to us +    // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, sid.get_src_addr()), static_cast<boost::uint32_t>(xb_ret_ep)); +    // Program CAM entry for outgoing packets matching a N230 resource (for example a Radio) +    // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, 256 + sid.get_dst_endpoint()), static_cast<boost::uint32_t>(xb_dest_ep)); + +    return sid; +} + +transport::zero_copy_if::sptr n230_resource_manager::_create_transport( +    const n230_eth_conn_t& eth_conn, +    const sid_t& sid, const device_addr_t &buff_params, +    transport::udp_zero_copy::buff_params& buff_params_out) +{ +    transport::zero_copy_xport_params default_buff_args; +    default_buff_args.recv_frame_size = transport::udp_simple::mtu; +    default_buff_args.send_frame_size = transport::udp_simple::mtu; +    default_buff_args.num_recv_frames = 32; +    default_buff_args.num_send_frames = 32; + +    transport::zero_copy_if::sptr xport = transport::udp_zero_copy::make( +        eth_conn.ip_addr, boost::lexical_cast<std::string>(fpga::CVITA_UDP_PORT), +        default_buff_args, buff_params_out, buff_params); + +    if (xport.get()) { +        _program_dispatcher(*xport, eth_conn.type, sid); +    } +    return xport; +} + +void n230_resource_manager::_program_dispatcher( +    transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid) +{ +    //Send a mini packet with SID into the ZPU +    //ZPU will reprogram the ethernet framer +    transport::managed_send_buffer::sptr buff = xport.get_send_buff(); +    buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0 +    buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid.get()); +    buff->commit(8); +    buff.reset(); + +    //reprogram the ethernet dispatcher's udp port (should be safe to always set) +    uint32_t disp_base_offset = +        ((port == ETH1) ? fw::SR_ZPU_ETHINT1 : fw::SR_ZPU_ETHINT0) + fw::SR_ZPU_ETHINT_DISPATCHER_BASE; +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, disp_base_offset + fw::ETH_FRAMER_SRC_UDP_PORT), fpga::CVITA_UDP_PORT); + +    //Do a peek to an arbitrary address to guarantee that the +    //ethernet framer has been programmed before we return. +    _fw_ctrl->peek32(0); +} + +void n230_resource_manager::_reset_codec_digital_interface() +{ +    //Set timing registers +    _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_DATA_DELAY), fpga::CODEC_DATA_DELAY); +    _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_CLK_DELAY), fpga::CODEC_CLK_DELAY); + +    _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 1); +    boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +    _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 0); +} + +bool n230_resource_manager::_radio_register_loopback_self_test(wb_iface::sptr iface) +{ +    bool test_fail = false; +    size_t hash = static_cast<size_t>(time(NULL)); +    for (size_t i = 0; i < 100; i++) { +        boost::hash_combine(hash, i); +        iface->poke32(fpga::sr_addr(fpga::SR_RADIO_TEST), boost::uint32_t(hash)); +        test_fail = iface->peek32(fpga::rb_addr(fpga::RB_RADIO_TEST)) != boost::uint32_t(hash); +        if (test_fail) break; //exit loop on any failure +    } +    return !test_fail; +} + +bool n230_resource_manager::_radio_data_loopback_self_test(wb_iface::sptr iface) +{ +   bool test_fail = false; +    size_t hash = size_t(time(NULL)); +    for (size_t i = 0; i < 100; i++) { +        boost::hash_combine(hash, i); +        const boost::uint32_t word32 = boost::uint32_t(hash) & (IF_DATA_I_MASK | IF_DATA_Q_MASK); +        iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), word32); +        iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); //block until request completes +        boost::this_thread::sleep(boost::posix_time::microseconds(100)); //wait for loopback to propagate through codec +        const boost::uint64_t rb_word64 = iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); +        const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); +        const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); +        test_fail = word32 != rb_tx or word32 != rb_rx; +        if (test_fail) +            UHD_MSG(fastpath) << boost::format("mismatch (exp:%x, got:%x and %x)... ") % word32 % rb_tx % rb_rx; +            break; //exit loop on any failure +        } + +    /* Zero out the idle data. */ +    iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), 0); +    return !test_fail; +} + +std::string n230_resource_manager::_get_fpga_upgrade_msg() { +    std::string img_loader_path = +        (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + +    return str(boost::format( +            "\nDownload the appropriate FPGA images for this version of UHD.\n" +            "%s\n\n" +            "Then burn a new image to the on-board flash storage of your\n" +            "USRP N230 device using the image loader utility. Use this command:\n" +            "\n \"%s\" --args=\"type=n230,addr=%s\"\n") +        % print_utility_error("uhd_images_downloader.py") +        % img_loader_path % _get_conn(PRI_ETH).ip_addr); + +} + +void n230_resource_manager::_check_fw_compat() +{ +    boost::uint32_t compat_num = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_compat_num)); +    _fw_version.compat_major = compat_num >> 16; +    _fw_version.compat_minor = compat_num; +    _fw_version.version_hash = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_version_hash)); + +    if (_fw_version.compat_major != N230_FW_COMPAT_NUM_MAJOR){ +        throw uhd::runtime_error(str(boost::format( +            "Expected firmware compatibility number %d.x, but got %d.%d\n" +            "The firmware build is not compatible with the host code build.\n" +            "%s" +            ) % static_cast<boost::uint32_t>(N230_FW_COMPAT_NUM_MAJOR) +              % static_cast<boost::uint32_t>(_fw_version.compat_major) +              % static_cast<boost::uint32_t>(_fw_version.compat_minor) +              % _get_fpga_upgrade_msg())); +    } +} + +void n230_resource_manager::_check_fpga_compat() +{ +    const boost::uint64_t compat = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_SIGNATUE)); +    const boost::uint32_t signature = boost::uint32_t(compat >> 32); +    const boost::uint16_t product_id = boost::uint8_t(compat >> 24); +    _fpga_version.compat_major = static_cast<boost::uint8_t>(compat >> 16); +    _fpga_version.compat_minor = static_cast<boost::uint16_t>(compat); + +    const boost::uint64_t version_hash = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_VERSION_HASH)); +    _fpga_version.version_hash = boost::uint32_t(version_hash); + +    if (signature != 0x0ACE0BA5E || product_id != fpga::RB_N230_PRODUCT_ID) +        throw uhd::runtime_error("Signature check failed. Please contact support."); + +    bool is_safe_image = (_fpga_version.compat_major > fpga::RB_N230_COMPAT_SAFE); + +    if (is_safe_image && !_safe_mode) { +        throw uhd::runtime_error( +            "The device appears to have the failsafe FPGA image loaded\n" +            "This could have happened because the production FPGA image in the flash was either corrupt or non-existent\n" +            "To remedy this error, please burn a valid FPGA image to the flash.\n" +            "To continue using the failsafe image with UHD, create the UHD device with the \"safe_mode\" device arg.\n" +            "Radio functionality/performance not guaranteed when operating in safe mode.\n"); +    } else if (_fpga_version.compat_major != fpga::RB_N230_COMPAT_MAJOR && !is_safe_image) { +        throw uhd::runtime_error(str(boost::format( +            "Expected FPGA compatibility number %d.x, but got %d.%d:\n" +            "The FPGA build is not compatible with the host code build.\n" +            "%s" +            ) % static_cast<boost::uint32_t>(fpga::RB_N230_COMPAT_MAJOR) +              % static_cast<boost::uint32_t>(_fpga_version.compat_major) +              % static_cast<boost::uint32_t>(_fpga_version.compat_minor) +              % _get_fpga_upgrade_msg())); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_resource_manager.hpp b/host/lib/usrp/n230/n230_resource_manager.hpp new file mode 100644 index 000000000..0a1178bd2 --- /dev/null +++ b/host/lib/usrp/n230/n230_resource_manager.hpp @@ -0,0 +1,318 @@ +// +// 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_N230_RESOURCE_MANAGER_HPP +#define INCLUDED_N230_RESOURCE_MANAGER_HPP + +#include "radio_ctrl_core_3000.hpp" +#include "spi_core_3000.hpp" +#include "gpio_atr_3000.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "time_core_3000.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include "user_settings_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" +#include <uhd/utils/tasks.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/soft_register.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/usrp/gps_ctrl.hpp> + +#include "usrp3_fw_ctrl_iface.hpp" +#include "n230_clk_pps_ctrl.hpp" +#include "n230_cores.hpp" +#include "n230_fpga_defs.h" +#include "n230_frontend_ctrl.hpp" +#include "n230_uart.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +enum n230_eth_port_t { +    ETH0, +    ETH1 +}; + +enum n230_eth_pref_t { +    PRI_ETH, +    SEC_ETH +}; + +enum n230_endpoint_t { +    RADIO_TX_DATA, +    RADIO_RX_DATA, +    RADIO_CONTROL, +    CORE, +    GPS_UART +}; + +enum n230_ver_src_t { +    SOFTWARE, +    FIRMWARE, +    FPGA +}; + +enum n230_version_t { +    COMPAT_MAJOR, +    COMPAT_MINOR +}; + +enum n230_data_dir_t { +    RX_DATA, TX_DATA +}; + +//Radio resources +class radio_resource_t : public boost::noncopyable { +public: +    radio_ctrl_core_3000::sptr      ctrl; +    gpio_atr::gpio_atr_3000::sptr   gpio_atr; +    time_core_3000::sptr            time; +    rx_vita_core_3000::sptr         framer; +    rx_dsp_core_3000::sptr          ddc; +    tx_vita_core_3000::sptr         deframer; +    tx_dsp_core_3000::sptr          duc; +    user_settings_core_3000::sptr   user_settings; +}; + +class n230_resource_manager : public boost::noncopyable +{ +public:     //Methods +    n230_resource_manager(const std::vector<std::string> ip_addrs, const bool safe_mode); +    virtual ~n230_resource_manager(); + +    static bool is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl); + +    inline bool is_device_claimed() { +        if (_fw_ctrl.get()) { +            return is_device_claimed(_fw_ctrl); +        } else { +            return false; +        } +    } + +    inline boost::uint32_t get_version(n230_ver_src_t src, n230_version_t type) { +        switch (src) { +            case FPGA:      return _fpga_version.get(type); +            case FIRMWARE:  return _fw_version.get(type); +            default:        return 0; +        } +    } + +    inline const std::string get_version_hash(n230_ver_src_t src) { +        switch (src) { +            case FPGA:      return _fpga_version.get_hash_str(); +            case FIRMWARE:  return _fw_version.get_hash_str(); +            default:        return ""; +        } +    } + +    //Firmware control interface +    inline wb_iface& get_fw_ctrl() const { +        return *_fw_ctrl; +    } +    inline wb_iface::sptr get_fw_ctrl_sptr() { +        return _fw_ctrl; +    } + +    //Core settings control interface +    inline radio_ctrl_core_3000& get_core_ctrl() const { +        return *_core_ctrl; +    } +    inline radio_ctrl_core_3000::sptr get_core_ctrl_sptr() { +        return _core_ctrl; +    } + +    //AD931 control interface +    inline ad9361_ctrl& get_codec_ctrl() const { +        return *_codec_ctrl; +    } +    inline ad9361_ctrl::sptr get_codec_ctrl_sptr() { +        return _codec_ctrl; +    } +    inline uhd::usrp::ad936x_manager& get_codec_mgr() const { +        return *_codec_mgr; +    } + +    //Clock PPS controls +    inline n230_ref_pll_ctrl& get_ref_pll_ctrl() const { +        return *_ref_pll_ctrl; +    } +    inline n230_ref_pll_ctrl::sptr get_ref_pll_ctrl_sptr() { +        return _ref_pll_ctrl; +    } + +    //Clock PPS controls +    inline n230_clk_pps_ctrl& get_clk_pps_ctrl() const { +        return *_clk_pps_ctrl; +    } +    inline n230_clk_pps_ctrl::sptr get_clk_pps_ctrl_sptr() { +        return _clk_pps_ctrl; +    } + +    //Front-end control +    inline n230_frontend_ctrl& get_frontend_ctrl() const { +        return *_frontend_ctrl; +    } +    inline n230_frontend_ctrl::sptr get_frontend_ctrl_sptr() { +        return _frontend_ctrl; +    } + +    //MiniSAS GPIO control +    inline gpio_atr::gpio_atr_3000::sptr get_minisas_gpio_ctrl_sptr(size_t idx) { +        return idx == 0 ? _ms0_gpio : _ms1_gpio; +    } + +    inline gpio_atr::gpio_atr_3000& get_minisas_gpio_ctrl(size_t idx) { +        return *get_minisas_gpio_ctrl_sptr(idx); +    } + +    //GPSDO control +    inline bool is_gpsdo_present() { +        return _gps_ctrl.get() and _gps_ctrl->gps_detected(); +    } + +    inline uhd::gps_ctrl::sptr get_gps_ctrl(void) { +        return _gps_ctrl; +    } + +    inline radio_resource_t& get_radio(size_t instance) { +        return _radios[instance]; +    } + +    //Transport to stream data +    transport::zero_copy_if::sptr create_transport( +        n230_data_dir_t direction, size_t radio_instance, +        const device_addr_t ¶ms, sid_t& sid, +        transport::udp_zero_copy::buff_params& buff_out_params); + +    //Misc +    inline double get_max_link_rate() { +        return fpga::N230_LINK_RATE_BPS * _eth_conns.size(); +    } + +private: +    struct ver_info_t { +        boost::uint8_t  compat_major; +        boost::uint16_t compat_minor; +        boost::uint32_t version_hash; + +        boost::uint32_t get(n230_version_t type) { +            switch (type) { +                case COMPAT_MAJOR: return compat_major; +                case COMPAT_MINOR: return compat_minor; +                default:           return 0; +            } +        } + +        const std::string get_hash_str() { +            return (str(boost::format("%07x%s") +                % (version_hash & 0x0FFFFFFF) +                % ((version_hash & 0xF0000000) ? "(modified)" : ""))); +        } +    }; + +    struct n230_eth_conn_t { +        std::string ip_addr; +        n230_eth_port_t type; +    }; + +    //-- Functions -- + +    void _claimer_loop(); + +    void _initialize_radio(size_t instance); + +    std::string _get_fpga_upgrade_msg(); +    void _check_fw_compat(); +    void _check_fpga_compat(); + +    const sid_t _generate_sid( +        const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance = 0); + +    transport::zero_copy_if::sptr _create_transport( +        const n230_eth_conn_t& eth_conn, +        const sid_t& sid, const device_addr_t &buff_params, +        transport::udp_zero_copy::buff_params& buff_params_out); + +    void _program_dispatcher( +        transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid); + +    void _reset_codec_digital_interface(); + +    bool _radio_register_loopback_self_test(wb_iface::sptr iface); + +    bool _radio_data_loopback_self_test(wb_iface::sptr iface); + +    inline const n230_eth_conn_t& _get_conn(const n230_eth_pref_t pref) { +        if (_eth_conns.size() == 1) +            return _eth_conns[0]; +        else +            return _eth_conns[(pref==PRI_ETH)?0:1]; +    } + +    //-- Members -- + +    std::vector<n230_eth_conn_t>    _eth_conns; +    const bool                      _safe_mode; +    ver_info_t                      _fw_version; +    ver_info_t                      _fpga_version; + +    //Firmware register interface +    uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr   _fw_ctrl; +    uhd::task::sptr                 _claimer_task; +    static boost::mutex             _claimer_mutex;  //All claims and checks in this process are serialized + +    //Transport +    boost::uint8_t                  _last_host_enpoint; + +    //Radio settings interface +    radio_ctrl_core_3000::sptr      _core_ctrl; +    n230_core_spi_core::sptr        _core_spi_ctrl; +    ad9361_ctrl::sptr               _codec_ctrl; +    uhd::usrp::ad936x_manager::sptr _codec_mgr; + +    //Core Registers +    fpga::core_radio_ctrl_reg_t     _core_radio_ctrl_reg; +    fpga::core_misc_reg_t           _core_misc_reg; +    fpga::core_pps_sel_reg_t        _core_pps_sel_reg; +    fpga::core_status_reg_t         _core_status_reg; + +    //Radio peripherals +    radio_resource_t                _radios[fpga::NUM_RADIOS]; + +    //Misc IO peripherals +    n230_ref_pll_ctrl::sptr         _ref_pll_ctrl; +    n230_clk_pps_ctrl::sptr         _clk_pps_ctrl; +    n230_frontend_ctrl::sptr        _frontend_ctrl; + +    //miniSAS GPIO +    gpio_atr::gpio_atr_3000::sptr   _ms0_gpio; +    gpio_atr::gpio_atr_3000::sptr   _ms1_gpio; + +    //GPSDO +    n230_uart::sptr                 _gps_uart; +    uhd::gps_ctrl::sptr             _gps_ctrl; + +}; + +}}} //namespace + +#endif //INCLUDED_N230_RESOURCE_MANAGER_HPP diff --git a/host/lib/usrp/n230/n230_stream_manager.cpp b/host/lib/usrp/n230/n230_stream_manager.cpp new file mode 100644 index 000000000..e7624ecd6 --- /dev/null +++ b/host/lib/usrp/n230/n230_stream_manager.cpp @@ -0,0 +1,562 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_stream_manager.hpp" + +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "async_packet_handler.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/log.hpp> +#include <boost/foreach.hpp> +#include <boost/make_shared.hpp> + +static const double N230_RX_SW_BUFF_FULL_FACTOR   = 0.90;     //Buffer should ideally be 90% full. +static const size_t N230_RX_FC_REQUEST_FREQ       = 32;       //per flow-control window +static const size_t N230_TX_MAX_ASYNC_MESSAGES    = 1000; +static const size_t N230_TX_MAX_SPP               = 4092; +static const size_t N230_TX_FC_RESPONSE_FREQ      = 10;       //per flow-control window + +static const boost::uint32_t N230_EVENT_CODE_FLOW_CTRL = 0; + +namespace uhd { namespace usrp { namespace n230 { + +using namespace uhd::transport; + +n230_stream_manager::~n230_stream_manager() +{ +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +n230_stream_manager::n230_stream_manager( +    const n230_device_args_t& dev_args, +    boost::shared_ptr<n230_resource_manager> resource_mgr, +    boost::weak_ptr<property_tree> prop_tree +) : +    _dev_args(dev_args), +    _resource_mgr(resource_mgr), +    _tree(prop_tree) +{ +    _async_md_queue.reset(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES)); +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +rx_streamer::sptr n230_stream_manager::get_rx_stream(const uhd::stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_stream_setup_mutex); + +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    if (args.otw_format.empty()) args.otw_format = "sc16"; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer; +    for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) +    { +        const size_t chan = args.channels[stream_i]; +        radio_resource_t& perif = _resource_mgr->get_radio(chan); + +        //setup transport hints (default to a large recv buff) +        //TODO: Propagate the device_args class into streamer in the future +        device_addr_t device_addr = args.args; +        if (not device_addr.has_key("recv_buff_size")) { +            device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_buff_size()); +        } +        if (not device_addr.has_key("recv_frame_size")) { +            device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_frame_size()); +        } +        if (not device_addr.has_key("num_recv_frames")) { +            device_addr["num_recv_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_recv_frames()); +        } + +        transport::udp_zero_copy::buff_params buff_params_out; +        sid_t sid; +        zero_copy_if::sptr xport = _resource_mgr->create_transport( +            RX_DATA, chan, device_addr, sid, buff_params_out); + +        //calculate packet size +        static const size_t hdr_size = 0 +            + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +            //+ sizeof(vrt::if_packet_info_t().tlr) //no longer using 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->get_recv_frame_size() - hdr_size; +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); +        size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); +        spp = std::min<size_t>(N230_TX_MAX_SPP, spp); //FPGA FIFO maximum for framing at full rate + +        //make the new streamer given the samples per packet +        if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); +        my_streamer->resize(args.channels.size()); + +        //init some streamer stuff +        my_streamer->set_vrt_unpacker(&n230_stream_manager::_cvita_hdr_unpack); + +        //set the converter +        uhd::convert::id_type id; +        id.input_format = args.otw_format + "_item32_be"; +        id.num_inputs = 1; +        id.output_format = args.cpu_format; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        perif.framer->clear(); +        perif.framer->set_nsamps_per_packet(spp); +        perif.framer->set_sid(sid.reversed().get()); +        perif.framer->setup(args); +        perif.ddc->setup(args); + +        //Give the streamer a functor to get the recv_buffer +        //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&zero_copy_if::get_recv_buff, xport, _1), +            true /*flush*/ +        ); + +        my_streamer->set_overflow_handler(stream_i, boost::bind( +            &n230_stream_manager::_handle_overflow, this, chan +        )); + +        my_streamer->set_issue_stream_cmd(stream_i, boost::bind( +            &rx_vita_core_3000::issue_stream_command, perif.framer, _1 +        )); + +        const size_t fc_window = _get_rx_flow_control_window( +            xport->get_recv_frame_size(), buff_params_out.recv_buff_size); +        const size_t fc_handle_window = std::max<size_t>(1, fc_window / N230_RX_FC_REQUEST_FREQ); + +        perif.framer->configure_flow_control(fc_window); + +        //Give the streamer a functor to send flow control messages +        //handle_rx_flowctrl is static and has no lifetime issues +        boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); +        my_streamer->set_xport_handle_flowctrl( +            stream_i, boost::bind(&n230_stream_manager::_handle_rx_flowctrl, sid.get(), xport, fc_cache, _1), +            fc_handle_window, +            true/*init*/ +        ); + +        //Store a weak pointer to prevent a streamer->manager->streamer circular dependency +        _rx_streamers[chan] = my_streamer; //store weak pointer +        _rx_stream_cached_args[chan] = args; + +        //Sets tick and samp rates on all streamer +        update_tick_rate(_get_tick_rate()); + +        //TODO: Find a way to remove this dependency +        property_tree::sptr prop_tree = _tree.lock(); +        if (prop_tree) { +            //TODO: Update this to support multiple motherboards +            const fs_path mb_path = "/mboards/0"; +            prop_tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update(); +        } +    } +    update_stream_states(); + +    return my_streamer; +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr n230_stream_manager::get_tx_stream(const uhd::stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_stream_setup_mutex); + +    uhd::stream_args_t args = args_; + +    //setup defaults for unspecified values +    if (not args.otw_format.empty() and args.otw_format != "sc16") { +        throw uhd::value_error("n230_impl::get_tx_stream only supports otw_format sc16"); +    } +    args.otw_format = "sc16"; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    //shared async queue for all channels in streamer +    boost::shared_ptr<async_md_queue_t> async_md(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES)); + +    boost::shared_ptr<sph::send_packet_streamer> my_streamer; +    for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) +    { +        const size_t chan = args.channels[stream_i]; +        radio_resource_t& perif = _resource_mgr->get_radio(chan); + +        //setup transport hints (default to a large recv buff) +        //TODO: Propagate the device_args class into streamer in the future +        device_addr_t device_addr = args.args; +        if (not device_addr.has_key("send_buff_size")) { +            device_addr["send_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_buff_size()); +        } +        if (not device_addr.has_key("send_frame_size")) { +            device_addr["send_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_frame_size()); +        } +        if (not device_addr.has_key("num_send_frames")) { +            device_addr["num_send_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_send_frames()); +        } + +        transport::udp_zero_copy::buff_params buff_params_out; +        sid_t sid; +        zero_copy_if::sptr xport = _resource_mgr->create_transport( +            TX_DATA, chan, device_addr, sid, buff_params_out); + +        //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->get_send_frame_size() - hdr_size; +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); +        const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + +        //make the new streamer given the samples per packet +        if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); +        my_streamer->resize(args.channels.size()); +        my_streamer->set_vrt_packer(&n230_stream_manager::_cvita_hdr_pack); + +        //set the converter +        uhd::convert::id_type id; +        id.input_format = args.cpu_format; +        id.num_inputs = 1; +        id.output_format = args.otw_format + "_item32_be"; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        perif.deframer->clear(); +        perif.deframer->setup(args); +        perif.duc->setup(args); + +        //flow control setup +        size_t fc_window = _get_tx_flow_control_window( +            bpp, device_addr.cast<size_t>("send_buff_size", _dev_args.get_send_buff_size())); +        //In packets +        const size_t fc_handle_window = (fc_window / N230_TX_FC_RESPONSE_FREQ); + +        perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window); +        boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t()); +        fc_cache->stream_channel = stream_i; +        fc_cache->device_channel = chan; +        fc_cache->async_queue = async_md; +        fc_cache->old_async_queue = _async_md_queue; + +        tick_rate_retriever_t get_tick_rate_fn = boost::bind(&n230_stream_manager::_get_tick_rate, this); +        task::sptr task = task::make( +            boost::bind(&n230_stream_manager::_handle_tx_async_msgs, +                fc_cache, xport, get_tick_rate_fn)); + +        //Give the streamer a functor to get the send buffer +        //get_tx_buff_with_flowctrl is static so bind has no lifetime issues +        //xport.send (sptr) is required to add streamer->data-transport lifetime dependency +        //task (sptr) is required to add  a streamer->async-handler lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&n230_stream_manager::_get_tx_buff_with_flowctrl, task, fc_cache, xport, fc_window, _1) +        ); +        //Give the streamer a functor handled received async messages +        my_streamer->set_async_receiver( +            boost::bind(&async_md_queue_t::pop_with_timed_wait, async_md, _1, _2) +        ); +        my_streamer->set_xport_chan_sid(stream_i, true, sid.get()); +        my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet + +        //Store a weak pointer to prevent a streamer->manager->streamer circular dependency +        _tx_streamers[chan] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); +        _tx_stream_cached_args[chan] = args; + +        //Sets tick and samp rates on all streamer +        update_tick_rate(_get_tick_rate()); + +        //TODO: Find a way to remove this dependency +        property_tree::sptr prop_tree = _tree.lock(); +        if (prop_tree) { +            //TODO: Update this to support multiple motherboards +            const fs_path mb_path = "/mboards/0"; +            prop_tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update(); +        } +    } +    update_stream_states(); + +    return my_streamer; +} + +/*********************************************************************** + * Async Message Receiver + **********************************************************************/ +bool n230_stream_manager::recv_async_msg(async_metadata_t &async_metadata, double timeout) +{ +    return _async_md_queue->pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Sample Rate Updaters + **********************************************************************/ +void n230_stream_manager::update_rx_samp_rate(const size_t dspno, const double rate) +{ +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock()); +    if (not my_streamer) return; +    my_streamer->set_samp_rate(rate); +    const double adj = _resource_mgr->get_radio(dspno).ddc->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj); +} + +void n230_stream_manager::update_tx_samp_rate(const size_t dspno, const double rate) +{ +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock()); +    if (not my_streamer) return; +    my_streamer->set_samp_rate(rate); +    const double adj = _resource_mgr->get_radio(dspno).duc->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj); +} + +/*********************************************************************** + * Tick Rate Updater + **********************************************************************/ +void n230_stream_manager::update_tick_rate(const double rate) +{ +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        radio_resource_t& perif = _resource_mgr->get_radio(i); + +        boost::shared_ptr<sph::recv_packet_streamer> my_rx_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +        if (my_rx_streamer) my_rx_streamer->set_tick_rate(rate); +        perif.framer->set_tick_rate(rate); + +        boost::shared_ptr<sph::send_packet_streamer> my_tx_streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); +        if (my_tx_streamer) my_tx_streamer->set_tick_rate(rate); +    } +} + +/*********************************************************************** + * Stream State Updater + **********************************************************************/ +void n230_stream_manager::update_stream_states() +{ +    //extract settings from state variables +    const bool enb_tx0 = bool(_tx_streamers[0].lock()); +    const bool enb_rx0 = bool(_rx_streamers[0].lock()); +    const bool enb_tx1 = bool(_tx_streamers[1].lock()); +    const bool enb_rx1 = bool(_rx_streamers[1].lock()); + +    fe_state_t fe0_state = NONE_STREAMING; +    if (enb_tx0 && enb_rx0) fe0_state = TXRX_STREAMING; +    else if (enb_tx0)       fe0_state = TX_STREAMING; +    else if (enb_rx0)       fe0_state = RX_STREAMING; + +    fe_state_t fe1_state = NONE_STREAMING; +    if (enb_tx1 && enb_rx1) fe1_state = TXRX_STREAMING; +    else if (enb_tx1)       fe1_state = TX_STREAMING; +    else if (enb_rx1)       fe1_state = RX_STREAMING; + +    _resource_mgr->get_frontend_ctrl().set_stream_state(fe0_state, fe1_state); +} + +size_t n230_stream_manager::_get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size) +{ +    double sw_buff_max = sw_buff_size * N230_RX_SW_BUFF_FULL_FACTOR; +    size_t window_in_pkts = (static_cast<size_t>(sw_buff_max) / frame_size); +    if (window_in_pkts == 0) { +        throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); +    } +    return window_in_pkts; +} + +void n230_stream_manager::_handle_overflow(const size_t i) +{ +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +    if (my_streamer->get_num_channels() == 2) { +        //MIMO +        //find out if we were in continuous mode before stopping +        const bool in_continuous_streaming_mode = _resource_mgr->get_radio(i).framer->in_continuous_streaming_mode(); +        //stop streaming +        my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +        //restart streaming +        if (in_continuous_streaming_mode) { +            stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +            stream_cmd.stream_now = false; +            stream_cmd.time_spec = _resource_mgr->get_radio(i).time->get_time_now() + time_spec_t(0.01); +            my_streamer->issue_stream_cmd(stream_cmd); +        } +    } else { +        _resource_mgr->get_radio(i).framer->handle_overflow(); +    } +} + +void n230_stream_manager::_handle_rx_flowctrl( +    const sid_t& sid, +    zero_copy_if::sptr xport, +    boost::shared_ptr<rx_fc_cache_t> fc_cache, +    const size_t last_seq) +{ +    static const size_t RXFC_PACKET_LEN_IN_WORDS    = 2; +    static const size_t RXFC_CMD_CODE_OFFSET        = 0; +    static const size_t RXFC_SEQ_NUM_OFFSET         = 1; + +    managed_send_buffer::sptr buff = xport->get_send_buff(0.0); +    if (not buff) { +        throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); +    } +    boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + +    //recover seq32 +    size_t& seq_sw = fc_cache->last_seq_in; +    const size_t seq_hw = seq_sw & HW_SEQ_NUM_MASK; +    if (last_seq < seq_hw) seq_sw += (HW_SEQ_NUM_MASK + 1); +    seq_sw &= ~HW_SEQ_NUM_MASK; +    seq_sw |= last_seq; + +    //load packet info +    vrt::if_packet_info_t packet_info; +    packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; +    packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS; +    packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +    packet_info.packet_count = seq_sw; +    packet_info.sob = false; +    packet_info.eob = false; +    packet_info.sid = sid.get(); +    packet_info.has_sid = true; +    packet_info.has_cid = false; +    packet_info.has_tsi = false; +    packet_info.has_tsf = false; +    packet_info.has_tlr = false; + +    //load header +    _cvita_hdr_pack(pkt, packet_info); + +    //load payload +    pkt[packet_info.num_header_words32 + RXFC_CMD_CODE_OFFSET] = uhd::htonx<boost::uint32_t>(N230_EVENT_CODE_FLOW_CTRL); +    pkt[packet_info.num_header_words32 + RXFC_SEQ_NUM_OFFSET] = uhd::htonx<boost::uint32_t>(seq_sw); + +    //send the buffer over the interface +    buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + +void n230_stream_manager::_handle_tx_async_msgs( +    boost::shared_ptr<tx_fc_cache_t> fc_cache, +    zero_copy_if::sptr xport, +    tick_rate_retriever_t get_tick_rate) +{ +    managed_recv_buffer::sptr buff = xport->get_recv_buff(); +    if (not buff) return; + +    //extract packet info +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +    const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + +    //unpacking can fail +    uint32_t (*endian_conv)(uint32_t) = uhd::ntohx; +    try { +        _cvita_hdr_unpack(packet_buff, if_packet_info); +        endian_conv = uhd::ntohx; +    } catch(const std::exception &ex) { +        UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; +        return; +    } + +    //fill in the async metadata +    async_metadata_t metadata; +    load_metadata_from_buff( +        endian_conv, metadata, if_packet_info, packet_buff, +        get_tick_rate(), fc_cache->stream_channel); + +    //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 == N230_EVENT_CODE_FLOW_CTRL or +        metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK +    ) { +        const size_t seq = metadata.user_payload[0]; +        fc_cache->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 != N230_EVENT_CODE_FLOW_CTRL) { +        fc_cache->async_queue->push_with_pop_on_full(metadata); +        metadata.channel = fc_cache->device_channel; +        fc_cache->old_async_queue->push_with_pop_on_full(metadata); +        standard_async_msg_prints(metadata); +    } +} + +managed_send_buffer::sptr n230_stream_manager::_get_tx_buff_with_flowctrl( +    task::sptr /*holds ref*/, +    boost::shared_ptr<tx_fc_cache_t> fc_cache, +    zero_copy_if::sptr xport, +    size_t fc_pkt_window, +    const double timeout) +{ +    while (true) +    { +        const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK); +        if ((delta & HW_SEQ_NUM_MASK) <= fc_pkt_window) break; + +        const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->last_seq_ack, timeout); +        if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control +    } + +    managed_send_buffer::sptr buff = xport->get_send_buff(timeout); +    if (buff) fc_cache->last_seq_out++; //update seq, this will actually be a send +    return buff; +} + +size_t n230_stream_manager::_get_tx_flow_control_window( +    size_t payload_size, +    size_t hw_buff_size) +{ +    size_t window_in_pkts = hw_buff_size / payload_size; +    if (window_in_pkts == 0) { +        throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); +    } +    return window_in_pkts; +} + +double n230_stream_manager::_get_tick_rate() +{ +    return _resource_mgr->get_clk_pps_ctrl().get_tick_rate(); +} + +void n230_stream_manager::_cvita_hdr_unpack( +    const boost::uint32_t *packet_buff, +    vrt::if_packet_info_t &if_packet_info) +{ +    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +    return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); +} + +void n230_stream_manager::_cvita_hdr_pack( +    boost::uint32_t *packet_buff, +    vrt::if_packet_info_t &if_packet_info) +{ +    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +    return vrt::if_hdr_pack_be(packet_buff, if_packet_info); +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_stream_manager.hpp b/host/lib/usrp/n230/n230_stream_manager.hpp new file mode 100644 index 000000000..7a496c4e9 --- /dev/null +++ b/host/lib/usrp/n230/n230_stream_manager.hpp @@ -0,0 +1,151 @@ +// +// 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_N230_STREAM_MANAGER_HPP +#define INCLUDED_N230_STREAM_MANAGER_HPP + +#include "time_core_3000.hpp" +#include "rx_vita_core_3000.hpp" +#include <uhd/types/sid.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/utils/tasks.hpp> +#include <boost/smart_ptr.hpp> +#include "n230_device_args.hpp" +#include "n230_resource_manager.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_stream_manager : public boost::noncopyable +{ +public:     //Methods +    n230_stream_manager( +        const n230_device_args_t& dev_args, +        boost::shared_ptr<n230_resource_manager> resource_mgr, +        boost::weak_ptr<property_tree> prop_tree); +    virtual ~n230_stream_manager(); + +    rx_streamer::sptr get_rx_stream( +        const uhd::stream_args_t &args); + +    tx_streamer::sptr get_tx_stream( +        const uhd::stream_args_t &args_); + +    bool recv_async_msg( +        async_metadata_t &async_metadata, +        double timeout); + +    void update_stream_states(); + +    void update_rx_samp_rate( +        const size_t dspno, const double rate); + +    void update_tx_samp_rate( +        const size_t dspno, const double rate); + +    void update_tick_rate( +        const double rate); + +private: +    typedef transport::bounded_buffer<async_metadata_t> async_md_queue_t; + +    struct rx_fc_cache_t +    { +        rx_fc_cache_t(): +            last_seq_in(0){} +        size_t last_seq_in; +    }; + +    struct tx_fc_cache_t +    { +        tx_fc_cache_t(): +            stream_channel(0), +            device_channel(0), +            last_seq_out(0), +            last_seq_ack(0), +            seq_queue(1){} +        size_t stream_channel; +        size_t device_channel; +        size_t last_seq_out; +        size_t last_seq_ack; +        transport::bounded_buffer<size_t> seq_queue; +        boost::shared_ptr<async_md_queue_t> async_queue; +        boost::shared_ptr<async_md_queue_t> old_async_queue; +    }; + +    typedef boost::function<double(void)> tick_rate_retriever_t; + +    void _handle_overflow(const size_t i); + +    double _get_tick_rate(); + +    static size_t _get_rx_flow_control_window( +        size_t frame_size, size_t sw_buff_size); + +    static void _handle_rx_flowctrl( +        const sid_t& sid, +        transport::zero_copy_if::sptr xport, +        boost::shared_ptr<rx_fc_cache_t> fc_cache, +        const size_t last_seq); + +    static void _handle_tx_async_msgs( +        boost::shared_ptr<tx_fc_cache_t> guts, +        transport::zero_copy_if::sptr xport, +        tick_rate_retriever_t get_tick_rate); + +    static transport::managed_send_buffer::sptr _get_tx_buff_with_flowctrl( +        task::sptr /*holds ref*/, +        boost::shared_ptr<tx_fc_cache_t> guts, +        transport::zero_copy_if::sptr xport, +        size_t fc_pkt_window, +        const double timeout); + +    static size_t _get_tx_flow_control_window( +        size_t payload_size, +        size_t hw_buff_size); + +    static void _cvita_hdr_unpack( +        const boost::uint32_t *packet_buff, +        transport::vrt::if_packet_info_t &if_packet_info); + +    static void _cvita_hdr_pack( +        boost::uint32_t *packet_buff, +        transport::vrt::if_packet_info_t &if_packet_info); + +    const n230_device_args_t                  _dev_args; +    boost::shared_ptr<n230_resource_manager>  _resource_mgr; +    //TODO: Find a way to remove this dependency +    boost::weak_ptr<property_tree>          _tree; + +    boost::mutex                            _stream_setup_mutex; +    uhd::msg_task::sptr                     _async_task; +    boost::shared_ptr<async_md_queue_t>     _async_md_queue; +    boost::weak_ptr<uhd::tx_streamer>       _tx_streamers[fpga::NUM_RADIOS]; +    boost::weak_ptr<uhd::rx_streamer>       _rx_streamers[fpga::NUM_RADIOS]; +    stream_args_t                           _tx_stream_cached_args[fpga::NUM_RADIOS]; +    stream_args_t                           _rx_stream_cached_args[fpga::NUM_RADIOS]; + +    static const boost::uint32_t HW_SEQ_NUM_MASK    = 0xFFF; +}; + +}}} //namespace + +#endif //INCLUDED_N230_STREAM_MANAGER_HPP diff --git a/host/lib/usrp/n230/n230_uart.cpp b/host/lib/usrp/n230/n230_uart.cpp new file mode 100644 index 000000000..20936c303 --- /dev/null +++ b/host/lib/usrp/n230/n230_uart.cpp @@ -0,0 +1,131 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_uart.hpp" + +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/exception.hpp> + +using namespace uhd; +using namespace uhd::transport; + +namespace uhd { namespace usrp { namespace n230 { + +struct n230_uart_impl : n230_uart +{ +    n230_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid): +        _xport(xport), +        _sid(sid), +        _count(0), +        _char_queue(4096) +    { +        //this default baud divider is over 9000 +        this->set_baud_divider(9001); + +        //create a task to handle incoming packets +        _recv_task = uhd::task::make(boost::bind(&n230_uart_impl::handle_recv, this)); +    } + +    void send_char(const char ch) +    { +        managed_send_buffer::sptr buff = _xport->get_send_buff(); +        UHD_ASSERT_THROW(bool(buff)); + +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +        packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; +        packet_info.num_payload_words32 = 2; +        packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +        packet_info.packet_count = _count++; +        packet_info.sob = false; +        packet_info.eob = false; +        packet_info.sid = _sid; +        packet_info.has_sid = true; +        packet_info.has_cid = false; +        packet_info.has_tsi = false; +        packet_info.has_tsf = false; +        packet_info.has_tlr = false; + +        boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>(); +        vrt::if_hdr_pack_le(packet_buff, packet_info); +        packet_buff[packet_info.num_header_words32+0] = uhd::htonx(boost::uint32_t(_baud_div)); +        packet_buff[packet_info.num_header_words32+1] = uhd::htonx(boost::uint32_t(ch)); +        buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t)); +    } + +    void write_uart(const std::string &buff) +    { +        static bool r_sent = false; +        for (size_t i = 0; i < buff.size(); i++) +        { +            if (buff[i] == '\n' and not r_sent) this->send_char('\r'); +            this->send_char(buff[i]); +            r_sent = (buff[i] == '\r'); +        } +    } + +    std::string read_uart(double timeout) +    { +        std::string line; +        char ch = '\0'; +        while (_char_queue.pop_with_timed_wait(ch, timeout)) +        { +            if (ch == '\r') continue; +            line += std::string(&ch, 1); +            if (ch == '\n') return line; +        } +        return line; +    } + +    void handle_recv(void) +    { +        managed_recv_buffer::sptr buff = _xport->get_recv_buff(); +        if (not buff) +            return; +        const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +        packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +        vrt::if_hdr_unpack_be(packet_buff, packet_info); +        const char ch = char(uhd::ntohx(packet_buff[packet_info.num_header_words32+1])); +        _char_queue.push_with_pop_on_full(ch); +    } + +    void set_baud_divider(const double baud_div) +    { +        _baud_div = size_t(baud_div + 0.5); +    } + +    const zero_copy_if::sptr _xport; +    const boost::uint32_t _sid; +    size_t _count; +    size_t _baud_div; +    bounded_buffer<char> _char_queue; +    uhd::task::sptr _recv_task; +}; + + +n230_uart::sptr n230_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid) +{ +    return n230_uart::sptr(new n230_uart_impl(xport, sid)); +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_uart.hpp b/host/lib/usrp/n230/n230_uart.hpp new file mode 100644 index 000000000..0bde12ab2 --- /dev/null +++ b/host/lib/usrp/n230/n230_uart.hpp @@ -0,0 +1,38 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_UART_HPP +#define INCLUDED_N230_UART_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> //uart iface +#include <uhd/utils/tasks.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +namespace uhd { namespace usrp { namespace n230 { + +class n230_uart: boost::noncopyable, public uhd::uart_iface +{ +public: +    typedef boost::shared_ptr<n230_uart> sptr; +    static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid); +    virtual void set_baud_divider(const double baud_div) = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_UART_HPP */ diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 47344e841..6924ba3b0 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP1 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_USRP1)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 4c3141d9e..502d0fbe3 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -63,6 +63,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace boost::assign;  static const dboard_id_t tvrx_id(0x0040); diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index d9894adaf..edf77a654 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP2 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_USRP2)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index 3d6348eec..f8b129f89 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the X300 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_X300)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 549fc9dfa..6039ee376 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -33,7 +33,7 @@ extern "C" {  #define X300_REVISION_MIN    2  #define X300_FW_COMPAT_MAJOR 4  #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 19 +#define X300_FPGA_COMPAT_MAJOR 20  //shared memory sections - in between the stack and the program space  #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index ebb9bf3a6..9a31ac98e 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -47,23 +47,38 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport;  using namespace uhd::niusrprio; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::usrp::x300;  namespace asio = boost::asio; -/*********************************************************************** - * Discovery over the udp and pcie transport - **********************************************************************/ +static bool has_dram_buff(wb_iface::sptr zpu_ctrl) { +    bool dramR0 = dma_fifo_core_3000::check( +        zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO0), SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO0)); +    bool dramR1 = dma_fifo_core_3000::check( +        zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO1), SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO1)); +    return (dramR0 and dramR1); +} +  static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) { -    //1G = {0:1G, 1:1G} w/ DRAM, HG = {0:1G, 1:10G} w/ DRAM, XG = {0:10G, 1:10G} w/ DRAM -    //HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM +    //Possible options: +    //1G  = {0:1G, 1:1G} w/ DRAM, HG  = {0:1G, 1:10G} w/ DRAM, XG  = {0:10G, 1:10G} w/ DRAM +    //1GS = {0:1G, 1:1G} w/ SRAM, HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM -    //In the default configuration, UHD does not support the HG and XG images so -    //they are never autodetected. +    std::string option;      bool eth0XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE0)) == 0x1);      bool eth1XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE1)) == 0x1); -    return (eth0XG && eth1XG) ? "XGS" : (eth1XG ? "HGS" : "1G"); +    option = (eth0XG && eth1XG) ? "XG" : (eth1XG ? "HG" : "1G"); + +    if (not has_dram_buff(zpu_ctrl)) { +        option += "S"; +    } +    return option;  } +/*********************************************************************** + * Discovery over the udp and pcie transport + **********************************************************************/ +  //@TODO: Refactor the find functions to collapse common code for ethernet and PCIe  static device_addrs_t x300_find_with_addr(const device_addr_t &hint)  { @@ -348,6 +363,8 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_nam          if ((i & 0x1fff) == 0) UHD_MSG(status) << "." << std::flush;      } +    //Wait for fimrware to reboot. 3s is an upper bound +    boost::this_thread::sleep(boost::posix_time::milliseconds(3000));      UHD_MSG(status) << " done!" << std::endl;  } @@ -729,6 +746,36 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      }      //////////////////////////////////////////////////////////////////// +    // DRAM FIFO initialization +    //////////////////////////////////////////////////////////////////// +    mb.has_dram_buff = has_dram_buff(mb.zpu_ctrl); +    if (mb.has_dram_buff) { +        for (size_t i = 0; i < mboard_members_t::NUM_RADIOS; i++) { +            static const size_t NUM_REGS = 8; +            mb.dram_buff_ctrl[i] = dma_fifo_core_3000::make( +                mb.zpu_ctrl, +                SR_ADDR(SET0_BASE, ZPU_SR_DRAM_FIFO0+(i*NUM_REGS)), +                SR_ADDR(SET0_BASE, ZPU_RB_DRAM_FIFO0+i)); +            mb.dram_buff_ctrl[i]->resize(X300_DRAM_FIFO_SIZE * i, X300_DRAM_FIFO_SIZE); + +            if (mb.dram_buff_ctrl[i]->ext_bist_supported()) { +                UHD_MSG(status) << boost::format("Running BIST for DRAM FIFO %d... ") % i; +                boost::uint32_t bisterr = mb.dram_buff_ctrl[i]->run_bist(); +                if (bisterr != 0) { +                    throw uhd::runtime_error(str(boost::format("DRAM FIFO BIST failed! (code: %d)\n") % bisterr)); +                } else { +                    double throughput = mb.dram_buff_ctrl[i]->get_bist_throughput(X300_BUS_CLOCK_RATE); +                    UHD_MSG(status) << (boost::format("pass (Throughput: %.1fMB/s)") % (throughput/1e6)) << std::endl; +                } +            } else { +                if (mb.dram_buff_ctrl[i]->run_bist() != 0) { +                    throw uhd::runtime_error(str(boost::format("DRAM FIFO %d BIST failed!\n") % i)); +                } +            } +        } +    } + +    ////////////////////////////////////////////////////////////////////      // setup radios      ////////////////////////////////////////////////////////////////////      this->setup_radio(mb_i, "A", dev_addr); @@ -749,15 +796,15 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      ////////////////////////////////////////////////////////////////////      // front panel gpio      //////////////////////////////////////////////////////////////////// -    mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); +    mb.fp_gpio = gpio_atr_3000::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO);      BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      {          _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)              .set(0) -            .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1)); +            .subscribe(boost::bind(&gpio_atr_3000::set_gpio_attr, mb.fp_gpio, attr.first, _1));      }      _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") -        .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio)); +        .publish(boost::bind(&gpio_atr_3000::read_gpio, mb.fp_gpio));      ////////////////////////////////////////////////////////////////////      // register the time keepers - only one can be the highlander @@ -930,7 +977,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, con      perif.spi = spi_core_3000::make(perif.ctrl, radio::sr_addr(radio::SPI), radio::RB32_SPI);      perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN);      perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); -    perif.leds = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::LEDS)); +    perif.leds = gpio_atr_3000::make_write_only(perif.ctrl, radio::sr_addr(radio::LEDS)); +    perif.leds->set_atr_mode(MODE_ATR, 0xFFFFFFFF);      perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT));      perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);      perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); @@ -940,7 +988,10 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, con      perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL));      perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP));      perif.ddc->set_link_rate(10e9/8); //whatever -    perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL)); +    //The DRAM FIFO is treated as in internal radio FIFO for flow control purposes +    tx_vita_core_3000::fc_monitor_loc fc_loc = +        mb.has_dram_buff ? tx_vita_core_3000::FC_PRE_FIFO : tx_vita_core_3000::FC_PRE_RADIO; +    perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL), fc_loc);      perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP));      perif.duc->set_link_rate(10e9/8); //whatever @@ -1014,7 +1065,7 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, con      //create a new dboard interface      x300_dboard_iface_config_t db_config; -    db_config.gpio = gpio_core_200::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO); +    db_config.gpio = db_gpio_atr_3000::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO);      db_config.spi = perif.spi;      db_config.rx_spi_slaveno = DB_RX_SEN;      db_config.tx_spi_slaveno = DB_TX_SEN; @@ -1143,7 +1194,7 @@ x300_impl::both_xports_t x300_impl::make_transport(           * connection type.*/          size_t eth_data_rec_frame_size = 0; -        if (mb.loaded_fpga_image == "HGS") { +        if (mb.loaded_fpga_image.substr(0,2) == "HG") {              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); @@ -1151,7 +1202,7 @@ x300_impl::both_xports_t x300_impl::make_transport(                  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") { +        } else if (mb.loaded_fpga_image.substr(0,2) == "XG") {              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);          } @@ -1295,16 +1346,16 @@ boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t      return sid;  } -void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string &rx_ant) +void x300_impl::update_atr_leds(gpio_atr_3000::sptr leds, const std::string &rx_ant)  {      const bool is_txrx = (rx_ant == "TX/RX");      const int rx_led = (1 << 2);      const int tx_led = (1 << 1);      const int txrx_led = (1 << 0); -    leds->set_atr_reg(dboard_iface::ATR_REG_IDLE, 0); -    leds->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); -    leds->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_led); -    leds->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, rx_led | tx_led); +    leds->set_atr_reg(ATR_REG_IDLE, 0); +    leds->set_atr_reg(ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); +    leds->set_atr_reg(ATR_REG_TX_ONLY, tx_led); +    leds->set_atr_reg(ATR_REG_FULL_DUPLEX, rx_led | tx_led);  }  void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) @@ -1314,7 +1365,6 @@ void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate)          perif.time64->set_tick_rate(rate);          perif.framer->set_tick_rate(rate);          perif.ddc->set_tick_rate(rate); -        perif.deframer->set_tick_rate(rate);          perif.duc->set_tick_rate(rate);      }  } @@ -1517,30 +1567,6 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep  }  /*********************************************************************** - * front-panel GPIO - **********************************************************************/ - -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL:   return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR:    return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT:    return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default:        UHD_THROW_INVALID_CODE_PATH(); -    } -} - -/***********************************************************************   * claimer logic   **********************************************************************/ diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 67afa77ee..4de0344bf 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -41,7 +41,8 @@  #include "radio_ctrl_core_3000.hpp"  #include "rx_frontend_core_200.hpp"  #include "tx_frontend_core_200.hpp" -#include "gpio_core_200.hpp" +#include "gpio_atr_3000.hpp" +#include "dma_fifo_core_3000.hpp"  #include <boost/weak_ptr.hpp>  #include <uhd/usrp/gps_ctrl.hpp>  #include <uhd/usrp/mboard_eeprom.hpp> @@ -56,8 +57,11 @@ static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin";  static const double X300_DEFAULT_TICK_RATE      = 200e6;        //Hz  static const double X300_BUS_CLOCK_RATE         = 166.666667e6; //Hz -static const size_t X300_TX_HW_BUFF_SIZE        = 520*1024;      //512K SRAM buffer + 8K 2Clk FIFO -static const size_t X300_TX_FC_RESPONSE_FREQ    = 8;            //per flow-control window +static const size_t X300_TX_HW_BUFF_SIZE_SRAM       = 520*1024;      //512K SRAM buffer + 8K 2Clk FIFO +static const size_t X300_TX_FC_RESPONSE_FREQ_SRAM   = 8;             //per flow-control window +static const size_t X300_TX_HW_BUFF_SIZE_DRAM       = 128*1024; +static const size_t X300_TX_FC_RESPONSE_FREQ_DRAM   = 32; +static const boost::uint32_t X300_DRAM_FIFO_SIZE    = 32*1024*1024;  static const size_t X300_RX_SW_BUFF_SIZE_ETH        = 0x2000000;//32MiB    For an ~8k frame size any size >32MiB is just wasted buffer space  static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS  = 0x100000; //1Mib @@ -123,7 +127,7 @@ enum  struct x300_dboard_iface_config_t  { -    gpio_core_200::sptr gpio; +    uhd::usrp::gpio_atr::db_gpio_atr_3000::sptr gpio;      spi_core_3000::sptr spi;      size_t rx_spi_slaveno;      size_t tx_spi_slaveno; @@ -185,7 +189,7 @@ private:          rx_dsp_core_3000::sptr ddc;          tx_vita_core_3000::sptr deframer;          tx_dsp_core_3000::sptr duc; -        gpio_core_200_32wo::sptr leds; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds;          rx_frontend_core_200::sptr rx_fe;          tx_frontend_core_200::sptr tx_fe;          //Registers @@ -226,10 +230,14 @@ private:               return slot_name == "A" ? 0 : 1;          } +        bool has_dram_buff; +        dma_fifo_core_3000::sptr dram_buff_ctrl[NUM_RADIOS]; + +          //other perifs on mboard          x300_clock_ctrl::sptr clock;          uhd::gps_ctrl::sptr gps; -        gpio_core_200::sptr fp_gpio; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio;          uhd::usrp::x300::fw_regmap_t::sptr fw_regmap; @@ -366,9 +374,7 @@ private:      void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);      void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members); -    void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); -    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); -    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); +    void update_atr_leds(uhd::usrp::gpio_atr::gpio_atr_3000::sptr, const std::string &ant);      void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false);      double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index e3515af0c..1356daec5 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -216,9 +216,10 @@ struct x300_tx_fc_guts_t   * FC credit we have is C = F + M - N (i.e. we can send C more packets   * before getting another ack).   */ -static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) +static size_t get_tx_flow_control_window(size_t frame_size, const bool dram_buff, const device_addr_t& tx_args)  { -    double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); +    double default_buff_size = dram_buff ? X300_TX_HW_BUFF_SIZE_DRAM : X300_TX_HW_BUFF_SIZE_SRAM; +    double hw_buff_size = tx_args.cast<double>("send_buff_size", default_buff_size);      size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / frame_size);      if (window_in_pkts == 0) {          throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); @@ -580,8 +581,9 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)          perif.duc->setup(args);          //flow control setup -        size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), device_addr);  //In packets -        const size_t fc_handle_window = std::max<size_t>(1, fc_window/X300_TX_FC_RESPONSE_FREQ); +        size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), mb.has_dram_buff, device_addr);  //In packets +        const size_t fc_handle_window = std::max<size_t>(1, +            fc_window/ (mb.has_dram_buff ? X300_TX_FC_RESPONSE_FREQ_DRAM : X300_TX_FC_RESPONSE_FREQ_SRAM));          UHD_LOG << "TX Flow Control Window = " << fc_window << ", TX Flow Control Handler Window = " << fc_handle_window << std::endl; diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index 3e0966c83..de3a3161a 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -42,7 +42,7 @@ static const uint32_t TIME       = 128;  static const uint32_t RX_DSP     = 144;  static const uint32_t TX_DSP     = 184;  static const uint32_t LEDS       = 195; -static const uint32_t FP_GPIO    = 200; +static const uint32_t FP_GPIO    = 201;  static const uint32_t RX_FRONT   = 208;  static const uint32_t TX_FRONT   = 216; @@ -77,6 +77,8 @@ localparam ZPU_SR_XB_LOCAL   = 03;  localparam ZPU_SR_SPI        = 32;  localparam ZPU_SR_ETHINT0    = 40;  localparam ZPU_SR_ETHINT1    = 56; +localparam ZPU_SR_DRAM_FIFO0 = 72; +localparam ZPU_SR_DRAM_FIFO1 = 80;  //reset bits  #define ZPU_SR_SW_RST_ETH_PHY           (1<<0) @@ -89,6 +91,8 @@ localparam ZPU_RB_CLK_STATUS = 3;  localparam ZPU_RB_COMPAT_NUM = 6;  localparam ZPU_RB_ETH_TYPE0  = 4;  localparam ZPU_RB_ETH_TYPE1  = 5; +localparam ZPU_RB_DRAM_FIFO0 = 10; +localparam ZPU_RB_DRAM_FIFO1 = 11;  //spi slaves on radio  #define DB_DAC_SEN      (1 << 7) diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt index a54d27c52..d2b70e356 100644 --- a/host/lib/usrp_clock/octoclock/CMakeLists.txt +++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt @@ -18,8 +18,6 @@  ########################################################################  # Conditionally configure the OctoClock support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_OCTOCLOCK)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp diff --git a/host/lib/utils/msg.cpp b/host/lib/utils/msg.cpp index de98ada64..95879a116 100644 --- a/host/lib/utils/msg.cpp +++ b/host/lib/utils/msg.cpp @@ -79,6 +79,8 @@ void uhd::msg::register_handler(const handler_t &handler){  }  static void default_msg_handler(uhd::msg::type_t type, const std::string &msg){ +    static boost::mutex msg_mutex; +    boost::mutex::scoped_lock lock(msg_mutex);      switch(type){      case uhd::msg::fastpath:          std::cerr << msg << std::flush; diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index ebc18fe93..8f586ddf4 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -17,7 +17,6 @@  #include <uhd/config.hpp>  #include <uhd/exception.hpp> -#include <uhd/transport/nirio/nifpga_lvbitx.h>  #include <uhd/utils/paths.hpp>  #include <boost/algorithm/string.hpp> diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 8b12c961f..d5d15ede8 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -77,3 +77,5 @@ IF(MSVC OR APPLE OR LINUX)      ADD_LIBRARY(module_test MODULE module_test.cpp)      TARGET_LINK_LIBRARIES(module_test uhd)  ENDIF() + +ADD_SUBDIRECTORY(devtest) diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index d71d756dd..8d359d2e2 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -417,16 +417,16 @@ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_and_sc8){  }  /*********************************************************************** - * Test short conversion + * Test u8 conversion   **********************************************************************/  static void test_convert_types_u8(      size_t nsamps, convert::id_type &id  ){      //fill the input samples      std::vector<boost::uint8_t> input(nsamps), output(nsamps); -    //BOOST_FOREACH(boost::uint8_t &in, input) in = boost::uint8_t(std::rand() & 0xFF); -    boost::uint32_t d = 48; -    BOOST_FOREACH(boost::uint8_t &in, input) in = d++; +    BOOST_FOREACH(boost::uint8_t &in, input) in = boost::uint8_t(std::rand() & 0xFF); +    //boost::uint32_t d = 48; +    //BOOST_FOREACH(boost::uint8_t &in, input) in = d++;      //run the loopback and test      convert::id_type in_id = id; @@ -455,3 +455,158 @@ BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8){          test_convert_types_u8(nsamps, id);      }  } + +/*********************************************************************** + * Test s8 conversion + **********************************************************************/ +static void test_convert_types_s8( +    size_t nsamps, convert::id_type &id +){ +    //fill the input samples +    std::vector<boost::int8_t> input(nsamps), output(nsamps); +    BOOST_FOREACH(boost::int8_t &in, input) in = boost::int8_t(std::rand() & 0xFF); + +    //run the loopback and test +    convert::id_type in_id = id; +    convert::id_type out_id = id; +    std::swap(out_id.input_format, out_id.output_format); +    std::swap(out_id.num_inputs, out_id.num_outputs); +    loopback(nsamps, in_id, out_id, input, output); +    BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(test_convert_types_s8_and_s8){ +    convert::id_type id; +    id.input_format = "s8"; +    id.num_inputs = 1; +    id.num_outputs = 1; + +    //try various lengths to test edge cases +    id.output_format = "s8_item32_le"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_s8(nsamps, id); +    } + +    //try various lengths to test edge cases +    id.output_format = "s8_item32_be"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_s8(nsamps, id); +    } +} + +/*********************************************************************** + * Test s16 conversion + **********************************************************************/ +static void test_convert_types_s16( +    size_t nsamps, convert::id_type &id +){ +    //fill the input samples +    std::vector<boost::int16_t> input(nsamps), output(nsamps); +    BOOST_FOREACH(boost::int16_t &in, input) in = boost::int16_t(std::rand() & 0xFFFF); + +    //run the loopback and test +    convert::id_type in_id = id; +    convert::id_type out_id = id; +    std::swap(out_id.input_format, out_id.output_format); +    std::swap(out_id.num_inputs, out_id.num_outputs); +    loopback(nsamps, in_id, out_id, input, output); +    BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(test_convert_types_s16_and_s16){ +    convert::id_type id; +    id.input_format = "s16"; +    id.num_inputs = 1; +    id.num_outputs = 1; + +    //try various lengths to test edge cases +    id.output_format = "s16_item32_le"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_s16(nsamps, id); +    } + +    //try various lengths to test edge cases +    id.output_format = "s16_item32_be"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_s16(nsamps, id); +    } +} + +/*********************************************************************** + * Test fc32 -> fc32 conversion + **********************************************************************/ +static void test_convert_types_fc32( +    size_t nsamps, convert::id_type &id +){ +    //fill the input samples +    std::vector< std::complex<float> > input(nsamps), output(nsamps); +    BOOST_FOREACH(fc32_t &in, input) in = fc32_t( +        (std::rand()/float(RAND_MAX/2)) - 1, +        (std::rand()/float(RAND_MAX/2)) - 1 +    ); + +    //run the loopback and test +    convert::id_type in_id = id; +    convert::id_type out_id = id; +    std::swap(out_id.input_format, out_id.output_format); +    std::swap(out_id.num_inputs, out_id.num_outputs); +    loopback(nsamps, in_id, out_id, input, output); +    BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_fc32){ +    convert::id_type id; +    id.input_format = "fc32"; +    id.num_inputs = 1; +    id.num_outputs = 1; + +    //try various lengths to test edge cases +    id.output_format = "fc32_item32_le"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_fc32(nsamps, id); +    } + +    //try various lengths to test edge cases +    id.output_format = "fc32_item32_be"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_fc32(nsamps, id); +    } +} + +/*********************************************************************** + * Test f32 -> f32 conversion + **********************************************************************/ +static void test_convert_types_f32( +    size_t nsamps, convert::id_type &id +){ +    //fill the input samples +    std::vector<float> input(nsamps), output(nsamps); +    BOOST_FOREACH(float &in, input) in = float((std::rand()/float(RAND_MAX/2)) - 1); + +    //run the loopback and test +    convert::id_type in_id = id; +    convert::id_type out_id = id; +    std::swap(out_id.input_format, out_id.output_format); +    std::swap(out_id.num_inputs, out_id.num_outputs); +    loopback(nsamps, in_id, out_id, input, output); +    BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(test_convert_types_f32_and_f32){ +    convert::id_type id; +    id.input_format = "f32"; +    id.num_inputs = 1; +    id.num_outputs = 1; + +    //try various lengths to test edge cases +    id.output_format = "f32_item32_le"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_f32(nsamps, id); +    } + +    //try various lengths to test edge cases +    id.output_format = "f32_item32_be"; +    for (size_t nsamps = 1; nsamps < 16; nsamps++){ +        test_convert_types_f32(nsamps, id); +    } +} diff --git a/host/tests/devtest/CMakeLists.txt b/host/tests/devtest/CMakeLists.txt new file mode 100644 index 000000000..6fa921bbd --- /dev/null +++ b/host/tests/devtest/CMakeLists.txt @@ -0,0 +1,58 @@ +# +# Copyright 2015 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/>. +# + +# Formatting +MESSAGE(STATUS "") + +# All devtest files get installed: +FILE(GLOB py_devtest_files "*.py") +UHD_INSTALL(PROGRAMS +    ${py_devtest_files} +    DESTINATION ${PKG_LIB_DIR}/tests/devtest +    COMPONENT tests +) + +# Arguments: +# - pattern: This will be used to identify which devtest_*.py is to be executed. +# - filter: Will be used in args strings as "type=<filter>". +# - devtype: A descriptive string. Is only used for CMake output. +MACRO(ADD_DEVTEST pattern filter devtype) +    MESSAGE(STATUS "Adding ${devtype} device test target") +    ADD_CUSTOM_TARGET("test_${pattern}" +        ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_testsuite.py +        "--src-dir" "${CMAKE_CURRENT_SOURCE_DIR}" +        "--devtest-pattern" "${pattern}" +        "--device-filter" "${filter}" +        "--build-type" "${CMAKE_BUILD_TYPE}" +        "--build-dir" "${CMAKE_BINARY_DIR}" +        COMMENT "Running device test on all connected ${devtype} devices:" +        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +    ) +ENDMACRO(ADD_DEVTEST) + +IF(ENABLE_B200) +    ADD_DEVTEST("b2xx" "b200" "B2XX") +ENDIF(ENABLE_B200) +IF(ENABLE_X300) +    ADD_DEVTEST("x3x0" "x300" "X3x0") +ENDIF(ENABLE_X300) +IF(ENABLE_E300) +    ADD_DEVTEST("e3xx" "e3x0" "E3XX") +ENDIF(ENABLE_E300) + +# Formatting +MESSAGE(STATUS "") diff --git a/host/tests/devtest/README.md b/host/tests/devtest/README.md new file mode 100644 index 000000000..ee1ff3c9f --- /dev/null +++ b/host/tests/devtest/README.md @@ -0,0 +1,28 @@ +# Device Tests + +These are a set of tests to be run with one or more attached devices. +None of these tests require special configuration; e.g., the X3x0 test +will work regardless of attached daughterboards, FPGIO wiring etc. + +## Adding new tests + +To add new tests, add new files with classes that derive from unittest.TestCase. +Most of the time, you'll want to derive from `uhd_test_case` or +`uhd_example_test_case`. + +## Adding new devices + +To add new devices, follow these steps: + +1) Add an entry to the CMakeLists.txt file in this directory using the +   `ADD_DEVTEST()` macro. +2) Add a `devtest_pattern.py` file to this directory, where `pattern` is +   the same pattern used in the `ADD_DEVTEST()` macro. +3) Edit this devtest file to import all the tests you want to run. Some +   may require parameterization. + +The devtest file is 'executed' using Python's unittest module, so it doesn't +require any actual commands. If the device needs special initialization, +commands inside this file will be executed *if* they are *not* in a +`if __name__ == "__main__"` conditional. + diff --git a/host/tests/devtest/benchmark_rate_test.py b/host/tests/devtest/benchmark_rate_test.py new file mode 100755 index 000000000..2602e1771 --- /dev/null +++ b/host/tests/devtest/benchmark_rate_test.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Test using benchmark_rate. """ + +import re +from uhd_test_base import shell_application, uhd_example_test_case + +class uhd_benchmark_rate_test(uhd_example_test_case): +    """ +    Run benchmark_rate in various configurations. +    """ +    tests = {} + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = uhd_benchmark_rate_test.tests + +    def run_test(self, test_name, test_args): +        """ +        Runs benchmark_rate with the given parameters. Parses output and writes +        results to file. +        """ +        self.log.info('Running test {n}, Channel = {c}, Sample Rate = {r}'.format( +            n=test_name, c=test_args.get('chan', '0'), r=test_args.get('rate', 1e6), +        )) +        args = [ +            self.create_addr_args_str(), +            '--duration', str(test_args.get('duration', 1)), +            '--channels', str(test_args.get('chan', '0')), +        ] +        if 'tx' in test_args['direction']: +            args.append('--tx_rate') +            args.append(str(test_args.get('rate', 1e6))) +        if 'rx' in test_args['direction']: +            args.append('--rx_rate') +            args.append(str(test_args.get('rate'))) +        (app, run_results) = self.run_example('benchmark_rate', args) +        match = re.search(r'(Num received samples):\s*(.*)', app.stdout) +        run_results['num_rx_samples'] = int(match.group(2)) if match else -1 +        match = re.search(r'(Num dropped samples):\s*(.*)', app.stdout) +        run_results['num_rx_dropped'] = int(match.group(2)) if match else -1 +        match = re.search(r'(Num overflows detected):\s*(.*)', app.stdout) +        run_results['num_rx_overruns'] = int(match.group(2)) if match else -1 +        match = re.search(r'(Num transmitted samples):\s*(.*)', app.stdout) +        run_results['num_tx_samples'] = int(match.group(2)) if match else -1 +        match = re.search(r'(Num sequence errors):\s*(.*)', app.stdout) +        run_results['num_tx_seqerrs'] = int(match.group(2)) if match else -1 +        match = re.search(r'(Num underflows detected):\s*(.*)', app.stdout) +        run_results['num_tx_underruns'] = int(match.group(2)) if match else -1 +        run_results['passed'] = all([ +            run_results['return_code'] == 0, +            run_results['num_rx_dropped'] == 0, +            run_results['num_tx_seqerrs'] == 0, +            run_results['num_tx_underruns'] <= test_args.get('acceptable-underruns', 0), +        ]) +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/devtest_b2xx.py b/host/tests/devtest/devtest_b2xx.py new file mode 100755 index 000000000..da358d08e --- /dev/null +++ b/host/tests/devtest/devtest_b2xx.py @@ -0,0 +1,76 @@ +# +# Copyright 2015 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/>. +# +""" +Run device tests for the B2xx series. +""" +from usrp_probe_test import uhd_usrp_probe_test +from benchmark_rate_test import uhd_benchmark_rate_test +uhd_benchmark_rate_test.tests = { +    #'mimo': { +        #'duration': 1, +        #'directions': ['tx,rx',], +        #'channels': ['0,1',], +        #'sample-rates': [1e6, 30e6], +        #'products': ['B210',], +        #'acceptable-underruns': 500, +    #}, +    'siso_chan0_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0', +        'rate': 1e6, +        'acceptable-underruns': 50, +    }, +    'siso_chan0_fast': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0', +        'rate': 40e6, +        'acceptable-underruns': 500, +    }, +    'siso_chan1_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '1', +        'rate': 1e6, +        'acceptable-underruns': 50, +        'products': ['B210',], +    }, +    'siso_chan1_fast': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '1', +        'rate': 40e6, +        'acceptable-underruns': 500, +        'products': ['B210',], +    }, +} + +from rx_samples_to_file_test import rx_samples_to_file_test +rx_samples_to_file_test.tests = { +    'default': { +        'duration': 1, +        'subdev': 'A:A', +        'rate': 5e6, +        'products': ['B210', 'B200',], +    }, +} + +from tx_bursts_test import uhd_tx_bursts_test +from test_pps_test import uhd_test_pps_test +from gpio_test import gpio_test + diff --git a/host/tests/devtest/devtest_e3xx.py b/host/tests/devtest/devtest_e3xx.py new file mode 100755 index 000000000..1cab44184 --- /dev/null +++ b/host/tests/devtest/devtest_e3xx.py @@ -0,0 +1,58 @@ +# +# Copyright 2015 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/>. +# +""" +Run device tests for the E3XX series. +""" +from usrp_probe_test import uhd_usrp_probe_test +from benchmark_rate_test import uhd_benchmark_rate_test +uhd_benchmark_rate_test.tests = { +    'mimo': { +        'duration': 1, +        'direction': 'tx,rx', +        'channels': '0,1', +        'rate': 1e6, +        'acceptable-underruns': 500, +    }, +    'siso_chan0_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0', +        'rate': 1e6, +        'acceptable-underruns': 50, +    }, +    'siso_chan1_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '1', +        'rate': 1e6, +        'acceptable-underruns': 50, +        'products': ['B210',], +    }, +} + +from rx_samples_to_file_test import rx_samples_to_file_test +rx_samples_to_file_test.tests = { +    'default': { +        'duration': 1, +        'subdev': 'A:A', +        'rate': 5e6, +    }, +} + +from tx_bursts_test import uhd_tx_bursts_test +from test_pps_test import uhd_test_pps_test + diff --git a/host/tests/devtest/devtest_x3x0.py b/host/tests/devtest/devtest_x3x0.py new file mode 100755 index 000000000..7ad6b21b6 --- /dev/null +++ b/host/tests/devtest/devtest_x3x0.py @@ -0,0 +1,57 @@ +# +# Copyright 2015 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/>. +# +""" +Run device tests for the X3x0 series. +""" + +from benchmark_rate_test import uhd_benchmark_rate_test +uhd_benchmark_rate_test.tests = { +    'mimo_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0,1', +        'rate': 1e6, +        'acceptable-underruns': 500, +    }, +    'mimo_fast': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0,1', +        'rate': 12.5e6, +        'acceptable-underruns': 500, +    }, +    'siso_chan0_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '0', +        'rate': 1e6, +        'acceptable-underruns': 0, +    }, +    'siso_chan1_slow': { +        'duration': 1, +        'direction': 'tx,rx', +        'chan': '1', +        'rate': 1e6, +        'acceptable-underruns': 0, +    }, +} + +#from rx_samples_to_file_test import rx_samples_to_file_test +from tx_bursts_test import uhd_tx_bursts_test +from test_pps_test import uhd_test_pps_test +from gpio_test import gpio_test + diff --git a/host/tests/devtest/gpio_test.py b/host/tests/devtest/gpio_test.py new file mode 100755 index 000000000..d764a8d96 --- /dev/null +++ b/host/tests/devtest/gpio_test.py @@ -0,0 +1,47 @@ +# +# Copyright 2015 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/>. +# +""" Test for test_pps_input. """ + +import re +from uhd_test_base import uhd_example_test_case + +class gpio_test(uhd_example_test_case): +    """ Run gpio. """ +    tests = {'default': {},} + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = gpio_test.tests + +    def run_test(self, test_name, test_args): +        """ Run the app and scrape for the success message. """ +        self.log.info('Running test {n}'.format(n=test_name,)) +        # Run example: +        args = [ +            self.create_addr_args_str(), +        ] +        (app, run_results) = self.run_example('gpio', args) +        # Evaluate pass/fail: +        run_results['passed'] = all([ +            app.returncode == 0, +            re.search('All tests passed!', app.stdout) is not None, +        ]) +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/run_testsuite.py b/host/tests/devtest/run_testsuite.py new file mode 100755 index 000000000..30601c8bd --- /dev/null +++ b/host/tests/devtest/run_testsuite.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" +Device test runner. +""" + +import os +import sys +import subprocess +import argparse +import logging +import time +from threading  import Thread +try: +    from Queue import Queue, Empty +except ImportError: +    from queue import Queue, Empty  # Py3k +from usrp_probe import get_usrp_list + +ANI = ('.', 'o', 'O', '0', 'O', 'o') + +def setup_parser(): +    """ Set up argparser """ +    parser = argparse.ArgumentParser(description="Test utility for UHD/USRP.") +    parser.add_argument('--devtest-pattern', '-p', default='*', help='e.g. b2xx') +    parser.add_argument('--device-filter', '-f', default=None, required=True, help='b200, x300, ...') +    parser.add_argument('--log-dir', '-l', default='.') +    parser.add_argument('--src-dir', default='.', help='Directory where the test sources are stored') +    parser.add_argument('--build-dir', default=None, help='Build dir (where examples/ and utils/ are)') +    parser.add_argument('--build-type', default='Release') +    return parser + +def setup_env(args): +    def setup_env_win(env, build_dir, build_type): +        env['PATH'] = "{build_dir}/lib/{build_type};{build_dir}/examples/{build_type};{build_dir}/utils/{build_type};{path}".format( +            build_dir=build_dir, build_type=build_type, path=env.get('PATH', '') +        ) +        env['LIBPATH'] = "{build_dir}/lib/{build_type};{path}".format( +            build_dir=build_dir, build_type=build_type, path=env.get('LIBPATH', '') +        ) +        env['LIB'] = "{build_dir}/lib/{build_type};{path}".format( +            build_dir=build_dir, build_type=build_type, path=env.get('LIB', '') +        ) +        return env +    def setup_env_unix(env, build_dir): +        env['PATH'] = "{build_dir}/examples:{build_dir}/utils:{path}".format( +            build_dir=build_dir, path=env.get('PATH', '') +        ) +        env['LD_LIBRARY_PATH'] = "{build_dir}/lib:{path}".format( +            build_dir=build_dir, path=env.get('LD_LIBRARY_PATH', '') +        ) +        return env +    def setup_env_osx(env, build_dir): +        env['PATH'] = "{build_dir}/examples:{build_dir}/utils:{path}".format( +                build_dir=build_dir, path=env.get('PATH', '') +        ) +        env['DYLD_LIBRARY_PATH'] = "{build_dir}/lib:{path}".format( +                build_dir=build_dir, path=env.get('DYLD_LIBRARY_PATH', '') +        ) +        return env +    ### Go +    env = os.environ +    if sys.platform.startswith('linux'): +        env = setup_env_unix(env, args.build_dir) +    elif sys.platform.startswith('win'): +        env = setup_env_win(env, args.build_dir, args.build_type) +    elif sys.platform.startswith('darwin'): +        env = setup_env_osx(env, args.build_dir) +    else: +        print("Devtest not supported on this platform ({0}).".format(sys.platform)) +        exit(1) +    return env + +def main(): +    """ +    Go, go, go! +    """ +    args = setup_parser().parse_args() +    devtest_pattern = "devtest_{p}.py".format(p=args.devtest_pattern) +    uhd_args_list = get_usrp_list("type=" + args.device_filter) +    if len(uhd_args_list) == 0: +        print("No devices found. Exiting.") +        exit(1) +    tests_passed = True +    for uhd_idx, uhd_info in enumerate(uhd_args_list): +        print('--- Running all tests for device {dev} ({prod}, Serial: {ser}).'.format( +            dev=uhd_idx, +            prod=uhd_info.get('product', 'USRP'), +            ser=uhd_info.get('serial') +        )) +        print('--- This will take some time. Better grab a cup of tea.') +        env = setup_env(args) +        args_str = uhd_info['args'] +        env['_UHD_TEST_ARGS_STR'] = args_str +        logfile_name = "log{}.log".format( +            args_str.replace('type=', '_').replace('serial=', '_').replace(',', '') +        ) +        resultsfile_name = "results{}.log".format( +            args_str.replace('type=', '_').replace('serial=', '_').replace(',', '') +        ) +        env['_UHD_TEST_LOGFILE'] = os.path.join(args.log_dir, logfile_name) +        env['_UHD_TEST_RESULTSFILE'] = os.path.join(args.log_dir, resultsfile_name) +        env['_UHD_TEST_LOG_LEVEL'] = str(logging.INFO) +        env['_UHD_TEST_PRINT_LEVEL'] = str(logging.WARNING) +        p = subprocess.Popen( +            [ +                "python", "-m", "unittest", "discover", "-v", +                "-s", args.src_dir, +                "-p", devtest_pattern, +            ], +            env=env, +            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, +        ) +        print(p.communicate()[0]) +        if p.returncode != 0: +            tests_passed = False +    print('--- Done testing all attached devices.') +    return tests_passed + +if __name__ == "__main__": +    if not main(): +        exit(1) + diff --git a/host/tests/devtest/rx_samples_to_file_test.py b/host/tests/devtest/rx_samples_to_file_test.py new file mode 100755 index 000000000..bac6ac256 --- /dev/null +++ b/host/tests/devtest/rx_samples_to_file_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Test the rx_samples_to_file example. """ + +from uhd_test_base import uhd_example_test_case + +class rx_samples_to_file_test(uhd_example_test_case): +    """ +    Run rx_samples_to_file and check output. +    """ +    tests = { +        'default': { +            'duration': 1, +            'rate': 5e6, +        }, +    } + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = rx_samples_to_file_test.tests + +    def run_test(self, test_name, test_args): +        """ +        Test launcher. Runs the example. +        """ +        self.log.info('Running test {n}, Subdev = {subdev}, Sample Rate = {rate}'.format( +            n=test_name, subdev=test_args.get('subdev'), rate=test_args.get('rate'), +        )) +        # Run example: +        args = [ +            self.create_addr_args_str(), +            '--null', +            '--stats', +            '--duration', str(test_args['duration']), +            '--rate', str(test_args.get('rate', 1e6)), +            '--wirefmt', test_args.get('wirefmt', 'sc16'), +        ] +        if test_args.has_key('subdev'): +            args.append('--subdev') +            args.append(test_args['subdev']) +        (app, run_results) = self.run_example('rx_samples_to_file', args) +        # Evaluate pass/fail: +        run_results['passed'] = all([ +            not run_results['has_D'], +            not run_results['has_S'], +            run_results['return_code'] == 0, +        ]) +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/test_messages_test.py b/host/tests/devtest/test_messages_test.py new file mode 100644 index 000000000..496765c75 --- /dev/null +++ b/host/tests/devtest/test_messages_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Test the test_messages example. """ + +import re +from uhd_test_base import uhd_example_test_case + +class uhd_test_messages_test(uhd_example_test_case): +    """ +    Run test_messages and check output. +    """ +    tests = {'default': {},} + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = uhd_test_messages_test.tests + +    def run_test(self, test_name, test_args): +        """ Run the app and scrape for the failure messages. """ +        self.log.info('Running test {n}'.format(n=test_name,)) +        # Run example: +        args = [ +            self.create_addr_args_str(), +        ] +        if test_args.has_key('ntests'): +            args.append('--ntests') +            args.append(test_args['ntests']) +        (app, run_results) = self.run_example('test_messages', args) +        # Evaluate pass/fail: +        succ_fail_re = re.compile(r'(?P<test>.*)->\s+(?P<succ>\d+) successes,\s+(?P<fail>\d+) +failures') +        for mo in succ_fail_re.finditer(app.stdout): +            key = mo.group("test").strip().replace(' ', '_').lower() +            successes = int(mo.group("succ")) +            failures = int(mo.group("fail")) +            run_results[key] = "{}/{}".format(successes, successes+failures) +            run_results['passed'] = bool(failures) + +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/test_pps_test.py b/host/tests/devtest/test_pps_test.py new file mode 100755 index 000000000..1e5b36e2c --- /dev/null +++ b/host/tests/devtest/test_pps_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Test for test_pps_input. """ + +import re +from uhd_test_base import uhd_example_test_case + +class uhd_test_pps_test(uhd_example_test_case): +    """ Run test_pps_input. """ +    tests = {'default': {},} + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = uhd_test_pps_test.tests + +    def run_test(self, test_name, test_args): +        """ Run the app and scrape for the success message. """ +        self.log.info('Running test {n}'.format(n=test_name,)) +        # Run example: +        args = [ +            self.create_addr_args_str(), +        ] +        if test_args.has_key('source'): +            args.append('--source') +            args.append(test_args['source']) +        (app, run_results) = self.run_example('test_pps_input', args) +        # Evaluate pass/fail: +        run_results['passed'] = all([ +            app.returncode == 0, +            re.search('Success!', app.stdout) is not None, +        ]) +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/tx_bursts_test.py b/host/tests/devtest/tx_bursts_test.py new file mode 100755 index 000000000..863f35fe1 --- /dev/null +++ b/host/tests/devtest/tx_bursts_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Run the test for tx_burst """ + +import re +from uhd_test_base import uhd_example_test_case + +class uhd_tx_bursts_test(uhd_example_test_case): +    """ Run test_messages. """ +    tests = { +        'default': { +            'nsamps': 10000, +            'rate': 5e6, +            'channels': '0', +        }, +    } + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = uhd_tx_bursts_test.tests + +    def run_test(self, test_name, test_args): +        """ Run the app and scrape for the failure messages. """ +        self.log.info('Running test {name}, Channel = {channel}, Sample Rate = {rate}'.format( +            name=test_name, channel=test_args.get('channel'), rate=test_args.get('rate'), +        )) +        # Run example: +        args = [ +            self.create_addr_args_str(), +            '--nsamps', str(test_args['nsamps']), +            '--channels', str(test_args['channels']), +            '--rate', str(test_args.get('rate', 1e6)), +        ] +        if test_args.has_key('subdev'): +            args.append('--subdev') +            args.append(test_args['subdev']) +        (app, run_results) = self.run_example('tx_bursts', args) +        # Evaluate pass/fail: +        run_results['passed'] = all([ +            app.returncode == 0, +            not run_results['has_S'], +        ]) +        run_results['async_burst_ack_found'] = re.search('success', app.stdout) is not None +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/tests/devtest/uhd_test_base.py b/host/tests/devtest/uhd_test_base.py new file mode 100755 index 000000000..046e6fb47 --- /dev/null +++ b/host/tests/devtest/uhd_test_base.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python + +import os +import sys +import yaml +import unittest +import re +import time +import logging +from subprocess import Popen, PIPE, STDOUT +from usrp_probe import get_usrp_list + +#-------------------------------------------------------------------------- +# Application +#-------------------------------------------------------------------------- +class shell_application(object): +    """ +    Wrapper for applications that are in $PATH. +    Note: The CMake infrastructure makes sure all examples and utils are in $PATH. +    """ +    def __init__(self, name): +        self.name = name +        self.stdout = '' +        self.stderr = '' +        self.returncode = None +        self.exec_time = None + +    def run(self, args = []): +        cmd_line = [self.name] +        cmd_line.extend(args) +        start_time = time.time() +        p = Popen(cmd_line, stdout=PIPE, stderr=PIPE, close_fds=True) +        self.stdout, self.stderr = p.communicate() +        self.returncode = p.returncode +        self.exec_time = time.time() - start_time + +#-------------------------------------------------------------------------- +# Test case base +#-------------------------------------------------------------------------- +class uhd_test_case(unittest.TestCase): +    """ +    Base class for UHD test cases. +    """ +    test_name = '--TEST--' + +    def set_up(self): +        """ +        Override this to add own setup code per test. +        """ +        pass + +    def setUp(self): +        self.name = self.__class__.__name__ +        self.test_id = self.id().split('.')[-1] +        self.results = {} +        self.results_file = os.getenv('_UHD_TEST_RESULTSFILE', "") +        if self.results_file and os.path.isfile(self.results_file): +            self.results = yaml.safe_load(open(self.results_file).read()) or {} +        self.args_str = os.getenv('_UHD_TEST_ARGS_STR', "") +        self.usrp_info = get_usrp_list(self.args_str)[0] +        if not self.results.has_key(self.usrp_info['serial']): +            self.results[self.usrp_info['serial']] = {} +        if not self.results[self.usrp_info['serial']].has_key(self.name): +            self.results[self.usrp_info['serial']][self.name] = {} +        self.setup_logger() +        self.set_up() + +    def setup_logger(self): +        " Add logging infrastructure " +        self.log = logging.getLogger("devtest.{name}".format(name=self.name)) +        self.log_file = os.getenv('_UHD_TEST_LOGFILE', "devtest.log") +        #self.log_level = int(os.getenv('_UHD_TEST_LOG_LEVEL', logging.DEBUG)) +        #self.print_level = int(os.getenv('_UHD_TEST_PRINT_LEVEL', logging.WARNING)) +        self.log_level = logging.DEBUG +        self.print_level = logging.WARNING +        file_handler = logging.FileHandler(self.log_file) +        file_handler.setLevel(self.log_level) +        console_handler = logging.StreamHandler() +        console_handler.setLevel(self.print_level) +        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +        file_handler.setFormatter(formatter) +        console_handler.setFormatter(formatter) +        self.log.setLevel(logging.DEBUG) +        self.log.addHandler(file_handler) +        self.log.addHandler(console_handler) +        self.log.info("Starting test with device: {dev}".format(dev=self.args_str)) + +    def tear_down(self): +        pass + +    def tearDown(self): +        self.tear_down() +        if self.results_file: +            open(self.results_file, 'w').write(yaml.dump(self.results, default_flow_style=False)) + +    def report_result(self, testname, key, value): +        """ Store a result as a key/value pair. +        After completion, all results for one test are written to the results file. +        """ +        if not self.results[self.usrp_info['serial']][self.name].has_key(testname): +            self.results[self.usrp_info['serial']][self.name][testname] = {} +        self.results[self.usrp_info['serial']][self.name][testname][key] = value + +    def create_addr_args_str(self, argname="args"): +        """ Returns an args string, usually '--args "type=XXX,serial=YYY" """ +        if len(self.args_str) == 0: +            return '' +        return '--{}={}'.format(argname, self.args_str) + +    def filter_warnings(self, errstr): +        """ Searches errstr for UHD warnings, removes them, and puts them into a separate string. +        Returns (errstr, warnstr), where errstr no longer has warning. """ +        warn_re = re.compile("UHD Warning:\n(?:    .*\n)+") +        warnstr = "\n".join(warn_re.findall(errstr)).strip() +        errstr = warn_re.sub('', errstr).strip() +        return (errstr, warnstr) + +    def filter_stderr(self, stderr, run_results={}): +        """ Filters the output to stderr. run_results[] is a dictionary. +        This function will: +        - Remove UUUUU... strings, since they are generally not a problem. +        - Remove all DDDD and SSSS strings, and add run_results['has_S'] = True +          and run_results['has_D'] = True. +        - Remove warnings and put them in run_results['warnings'] +        - Put the filtered error string into run_results['errors'] and returns the dictionary +        """ +        errstr, run_results['warnings'] = self.filter_warnings(stderr) +        # Scan for underruns and sequence errors / dropped packets  not detected in the counter +        errstr = re.sub('UU+', '', errstr) +        (errstr, n_subs) = re.subn('SS+', '', errstr) +        if n_subs: +            run_results['has_S'] = True +        (errstr, n_subs) = re.subn('DD+', '', errstr) +        if n_subs: +            run_results['has_D'] = True +        errstr = re.sub("\n\n+", "\n", errstr) +        run_results['errors'] = errstr.strip() +        return run_results + +class uhd_example_test_case(uhd_test_case): +    """ +    A test case that runs an example. +    """ + +    def setup_example(self): +        """ +        Override this to add specific setup code. +        """ +        pass + +    def set_up(self): +        """ +        """ +        self.setup_example() + +    def run_test(self, test_name, test_args): +        """ +        Override this to run the actual example. + +        Needs to return either a boolean or a dict with key 'passed' to determine +        pass/fail. +        """ +        raise NotImplementedError + +    def run_example(self, example, args): +        """ +        Run `example' (which has to be a UHD example or utility) with `args'. +        Return results and the app object. +        """ +        self.log.info("Running example: `{example} {args}'".format(example=example, args=" ".join(args))) +        app = shell_application(example) +        app.run(args) +        run_results = { +            'return_code': app.returncode, +            'passed': False, +            'has_D': False, +            'has_S': False, +        } +        run_results = self.filter_stderr(app.stderr, run_results) +        self.log.info('STDERR Output:') +        self.log.info(str(app.stderr)) +        return (app, run_results) + + +    def report_example_results(self, test_name, run_results): +        for key in sorted(run_results): +            self.log.info('{key} = {val}'.format(key=key, val=run_results[key])) +            self.report_result( +                test_name, +                key, run_results[key] +            ) +        if run_results.has_key('passed'): +            self.report_result( +                test_name, +                'status', +                'Passed' if run_results['passed'] else 'Failed', +            ) +        if run_results.has_key('errors'): +            self.report_result( +                test_name, +                'errors', +                'Yes' if run_results['errors'] else 'No', +            ) + +    def test_all(self): +        """ +        Hook for test runner. Needs to be a class method that starts with 'test'. +        Calls run_test(). +        """ +        for test_name, test_args in self.test_params.iteritems(): +            if not test_args.has_key('product') or (self.usrp_info['product'] in test_args.get('products', [])): +                run_results = self.run_test(test_name, test_args) +                passed = bool(run_results) +                if isinstance(run_results, dict): +                    passed = run_results['passed'] +                self.assertTrue( +                    passed, +                    msg="Errors occurred during test `{t}'. Check log file for details.\nRun results:\n{r}".format( +                        t=test_name, r=yaml.dump(run_results, default_flow_style=False) +                    ) +                ) + diff --git a/host/tests/devtest/usrp_probe.py b/host/tests/devtest/usrp_probe.py new file mode 100644 index 000000000..ba3c645e4 --- /dev/null +++ b/host/tests/devtest/usrp_probe.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Run uhd_find_devices and parse the output. """ + +import re +import subprocess + +def get_usrp_list(device_filter=None): +    """ Returns a list of dicts that contain USRP info """ +    try: +        if device_filter is not None: +            output = subprocess.check_output(['uhd_find_devices', '--args', device_filter]) +        else: +            output = subprocess.check_output('uhd_find_devices') +    except subprocess.CalledProcessError: +        return [] +    split_re = "\n*-+\n-- .*\n-+\n" +    uhd_strings = re.split(split_re, output) +    result = [] +    for uhd_string in uhd_strings: +        if not re.match("Device Address", uhd_string): +            continue +        this_result = {k: v for k, v in re.findall("    ([a-z]+): (.*)", uhd_string)} +        args_string = "" +        try: +            args_string = "type={},serial={}".format(this_result['type'], this_result['serial']) +        except KeyError: +            continue +        this_result['args'] = args_string +        result.append(this_result) +    return result + +if __name__ == "__main__": +    print get_usrp_list() +    print get_usrp_list('type=x300') diff --git a/host/tests/devtest/usrp_probe_test.py b/host/tests/devtest/usrp_probe_test.py new file mode 100755 index 000000000..a136a2af7 --- /dev/null +++ b/host/tests/devtest/usrp_probe_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" Run the test for tx_burst """ + +import re +from uhd_test_base import uhd_example_test_case + +class uhd_usrp_probe_test(uhd_example_test_case): +    """ Run uhd_usrp_probe """ +    tests = { +        'default': { +            'init-only': False, +        }, +    } + +    def setup_example(self): +        """ +        Set args. +        """ +        self.test_params = uhd_usrp_probe_test.tests + +    def run_test(self, test_name, test_args): +        """ Run the app and scrape for the failure messages. """ +        self.log.info('Running test {name}'.format(name=test_name)) +        # Run example: +        args = [ +            self.create_addr_args_str(), +        ] +        if test_args.get('init-only'): +            args.append('--init-only') +        (app, run_results) = self.run_example('uhd_usrp_probe', args) +        # Evaluate pass/fail: +        run_results['passed'] = all([ +            app.returncode == 0, +        ]) +        self.report_example_results(test_name, run_results) +        return run_results + diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 28fecc895..bf8d88799 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -19,6 +19,7 @@  # Utilities that get installed into the runtime path  ########################################################################  SET(util_runtime_sources +    uhd_config_info.cpp      uhd_find_devices.cpp      uhd_usrp_probe.cpp      uhd_image_loader.cpp @@ -30,7 +31,7 @@ SET(util_runtime_sources  SET(x3xx_burner_sources      usrp_x3xx_fpga_burner.cpp -    cdecode.c +    ${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/x300/cdecode.c  )  find_package(UDev) @@ -48,18 +49,25 @@ FOREACH(util_source ${util_runtime_sources})      UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities)  ENDFOREACH(util_source) -ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) -TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) -UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) +IF(ENABLE_X300) +    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp/x300) +    ADD_EXECUTABLE(usrp_x3xx_fpga_burner ${x3xx_burner_sources}) +    TARGET_LINK_LIBRARIES(usrp_x3xx_fpga_burner uhd ${Boost_LIBRARIES}) +    UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) +ENDIF(ENABLE_X300)  ########################################################################  # Utilities that get installed into the share path  ########################################################################  SET(util_share_sources +    converter_benchmark.cpp      query_gpsdo_sensors.cpp      usrp_burn_db_eeprom.cpp      usrp_burn_mb_eeprom.cpp  ) +SET(util_share_sources_py +    converter_benchmark.py +)  IF(ENABLE_USB)      LIST(APPEND util_share_sources          fx2_init_eeprom.cpp @@ -108,9 +116,20 @@ FOREACH(util_source ${util_share_sources})      TARGET_LINK_LIBRARIES(${util_name} uhd ${Boost_LIBRARIES})      UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities)  ENDFOREACH(util_source) +FOREACH(util_source ${util_share_sources_py}) +    UHD_INSTALL(PROGRAMS +        ${CMAKE_CURRENT_SOURCE_DIR}/${util_source} +        DESTINATION ${PKG_LIB_DIR}/utils +        COMPONENT utilities +    ) +ENDFOREACH(util_source) -UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) -UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +IF(ENABLE_USRP2) +    UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +ENDIF(ENABLE_USRP2) +IF(ENABLE_X300) +    UHD_INSTALL(TARGETS usrp_x3xx_fpga_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +ENDIF(ENABLE_X300)  #UHD images downloader configuration  CONFIGURE_FILE( diff --git a/host/utils/cdecode.c b/host/utils/cdecode.c deleted file mode 100644 index 1d09cbe22..000000000 --- a/host/utils/cdecode.c +++ /dev/null @@ -1,80 +0,0 @@ -/* -cdecoder.c - c source to a base64 decoding algorithm implementation - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#include "cdecode.h" - -int base64_decode_value(char value_in){ -    static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; -    static const char decoding_size = sizeof(decoding); -    value_in -= 43; -    if ((signed char)value_in < 0 || value_in > decoding_size) return -1; -    return decoding[(int)value_in]; -} - -void base64_init_decodestate(base64_decodestate* state_in){ -    state_in->step = step_a; -    state_in->plainchar = 0; -} - -size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){ -    const char* codechar = code_in; -    char* plainchar = plaintext_out; -    char fragment; -     -    *plainchar = state_in->plainchar; -     -    switch (state_in->step){ -        while (1){ -            case step_a: -                do{ -                    if (codechar == code_in+length_in){ -                        state_in->step = step_a; -                        state_in->plainchar = *plainchar; -                        return plainchar - plaintext_out; -                    } -                    fragment = (char)base64_decode_value(*codechar++); -                } while ((signed char)fragment < 0); -                *plainchar = (fragment & 0x03f) << 2; - -            case step_b: -                do{ -                    if (codechar == code_in+length_in){ -                        state_in->step = step_b; -                        state_in->plainchar = *plainchar; -                        return plainchar - plaintext_out; -                    } -                    fragment = (char)base64_decode_value(*codechar++); -                } while ((signed char)fragment < 0); -                *plainchar++ |= (fragment & 0x030) >> 4; -                *plainchar    = (fragment & 0x00f) << 4; -            case step_c: -                do{ -                    if (codechar == code_in+length_in) -                    { -                        state_in->step = step_c; -                        state_in->plainchar = *plainchar; -                        return plainchar - plaintext_out; -                    } -                    fragment = (char)base64_decode_value(*codechar++); -                } while ((signed char)fragment < 0); -                *plainchar++ |= (fragment & 0x03c) >> 2; -                *plainchar    = (fragment & 0x003) << 6; -            case step_d: -                do{ -                    if (codechar == code_in+length_in){ -                        state_in->step = step_d; -                        state_in->plainchar = *plainchar; -                        return plainchar - plaintext_out; -                    } -                    fragment = (char)base64_decode_value(*codechar++); -                } while ((signed char)fragment < 0); -                *plainchar++   |= (fragment & 0x03f); -        } -    } -    /* control should not reach here */ -    return plainchar - plaintext_out; -} diff --git a/host/utils/cdecode.h b/host/utils/cdecode.h deleted file mode 100644 index e1eee301f..000000000 --- a/host/utils/cdecode.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -cdecode.h - c header for a base64 decoding algorithm - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#ifndef BASE64_CDECODE_H -#define BASE64_CDECODE_H - -#include <stddef.h> - -typedef enum{ -    step_a, step_b, step_c, step_d -} base64_decodestep; - -typedef struct{ -    base64_decodestep step; -    char plainchar; -} base64_decodestate; - -void base64_init_decodestate(base64_decodestate* state_in); - -int base64_decode_value(char value_in); - -size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); - -#endif /* BASE64_CDECODE_H */ diff --git a/host/utils/converter_benchmark.cpp b/host/utils/converter_benchmark.cpp new file mode 100644 index 000000000..0f38e8518 --- /dev/null +++ b/host/utils/converter_benchmark.cpp @@ -0,0 +1,433 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/safe_main.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/convert.hpp> +#include <uhd/exception.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/timer.hpp> +#include <boost/algorithm/string.hpp> +#include <iostream> +#include <iomanip> +#include <map> +#include <complex> + +namespace po = boost::program_options; +using namespace uhd::convert; + +enum buf_init_t { +    RANDOM, INC +}; + +// Convert `sc16_item32_le' -> `sc16' +// Finds the first _ in format and returns the string +// until then. Returns the entire string if no _ is found. +std::string format_to_type(const std::string &format) +{ +    std::string ret_val = ""; +    for (size_t i = 0; i < format.length(); i++) { +        if (format[i] == '_') { +            return ret_val; +        } +        ret_val.append(1, format[i]); +    } + +    return ret_val; +} + +void configure_conv( +        converter::sptr conv, +        const std::string &in_type, +        const std::string &out_type +) { +    if (in_type == "sc16") { +        if (out_type == "fc32") { +            std::cout << "Setting scalar to 32767." << std::endl; +            conv->set_scalar(32767.); +            return; +        } +    } + +    if (in_type == "fc32") { +        if (out_type == "sc16") { +            std::cout << "Setting scalar to 32767." << std::endl; +            conv->set_scalar(32767.); +            return; +        } +    } + +    std::cout << "No configuration required." << std::endl; +} + +template <typename T> +void init_random_vector_complex_float(std::vector<char> &buf_ptr, const size_t n_items) +{ +    std::complex<T> * const buf = reinterpret_cast<std::complex<T> * const>(&buf_ptr[0]); +    for (size_t i = 0; i < n_items; i++) { +        buf[i] = std::complex<T>( +            T((std::rand()/double(RAND_MAX/2)) - 1), +            T((std::rand()/double(RAND_MAX/2)) - 1) +        ); +    } +} + +template <typename T> +void init_random_vector_complex_int(std::vector<char> &buf_ptr, const size_t n_items) +{ +    std::complex<T> * const buf = reinterpret_cast<std::complex<T> * const>(&buf_ptr[0]); +    for (size_t i = 0; i < n_items; i++) { +        buf[i] = std::complex<T>(T(std::rand()), T(std::rand())); +    } +} + +template <typename T> +void init_random_vector_real_int(std::vector<char> &buf_ptr, size_t n_items) +{ +    T * const buf = reinterpret_cast<T * const>(&buf_ptr[0]); +    for (size_t i = 0; i < n_items; i++) { +        buf[i] = T(std::rand()); +    } +} + +// Fill a buffer with increasing numbers +template <typename T> +void init_inc_vector(std::vector<char> &buf_ptr, size_t n_items) +{ +    T * const buf = reinterpret_cast<T * const>(&buf_ptr[0]); +    for (size_t i = 0; i < n_items; i++) { +        buf[i] = T(i); +    } +} + +void init_buffers( +        std::vector< std::vector<char> > &buf, +        const std::string &type, +        size_t bytes_per_item, +        buf_init_t buf_seed_mode +) { +    if (buf.empty()) { +        return; +    } +    size_t n_items = buf[0].size() / bytes_per_item; + +    /// Fill with incrementing integers +    if (buf_seed_mode == INC) { +        for (size_t i = 0; i < buf.size(); i++) { +            if (type == "sc8") { +                init_inc_vector< std::complex<boost::int8_t> >(buf[i], n_items); +            } else if (type == "sc16") { +                init_inc_vector< std::complex<boost::int16_t> >(buf[i], n_items); +            } else if (type == "sc32") { +                init_inc_vector< std::complex<boost::int32_t> >(buf[i], n_items); +            } else if (type == "fc32") { +                init_inc_vector< std::complex<float> >(buf[i], n_items); +            } else if (type == "fc64") { +                init_inc_vector< std::complex<double> >(buf[i], n_items); +            } else if (type == "s8") { +                init_inc_vector< boost::int8_t >(buf[i], n_items); +            } else if (type == "s16") { +                init_inc_vector< boost::int16_t >(buf[i], n_items); +            } else if (type == "item32") { +                init_inc_vector< boost::uint32_t >(buf[i], n_items); +                init_random_vector_real_int<boost::uint32_t>(buf[i], n_items); +            } else { +                throw uhd::runtime_error(str( +                            boost::format("Cannot handle data type: %s") % type +                )); +            } +        } + +        return; +    } + +    assert(buf_seed_mode == RANDOM); + +    /// Fill with random data +    for (size_t i = 0; i < buf.size(); i++) { +        if (type == "sc8") { +            init_random_vector_complex_int<boost::int8_t>(buf[i], n_items); +        } else if (type == "sc16") { +            init_random_vector_complex_int<boost::int16_t>(buf[i], n_items); +        } else if (type == "sc32") { +            init_random_vector_complex_int<boost::int32_t>(buf[i], n_items); +        } else if (type == "fc32") { +            init_random_vector_complex_float<float>(buf[i], n_items); +        } else if (type == "fc64") { +            init_random_vector_complex_float<double>(buf[i], n_items); +        } else if (type == "s8") { +            init_random_vector_real_int<boost::int8_t>(buf[i], n_items); +        } else if (type == "s16") { +            init_random_vector_real_int<boost::int16_t>(buf[i], n_items); +        } else if (type == "item32") { +            init_random_vector_real_int<boost::uint32_t>(buf[i], n_items); +        } else { +            throw uhd::runtime_error(str( +                boost::format("Cannot handle data type: %s") % type +            )); +        } +    } +} + +// Returns time elapsed +double run_benchmark( +        converter::sptr conv, +        const std::vector<const void *> &input_buf_refs, +        const std::vector<void *> &output_buf_refs, +        size_t n_items, +        size_t iterations +) { +    boost::timer benchmark_timer; +    for (size_t i = 0; i < iterations; i++) { +        conv->conv(input_buf_refs, output_buf_refs, n_items); +    } +    return benchmark_timer.elapsed(); +} + +template <typename T> +std::string void_ptr_to_hexstring(const void *v_ptr, size_t index) +{ +    const T *ptr = reinterpret_cast<const T *>(v_ptr); +    return str(boost::format("%X") % ptr[index]); +} + +std::string item_to_hexstring( +    const void *v_ptr, +    size_t index, +    const std::string &type +) { +    if (type == "fc32") { +        return void_ptr_to_hexstring<uint64_t>(v_ptr, index); +    } +    else if (type == "sc16" || type == "item32") { +        return void_ptr_to_hexstring<uint32_t>(v_ptr, index); +    } +    else if (type == "sc8" || type == "s16") { +        return void_ptr_to_hexstring<uint16_t>(v_ptr, index); +    } +    else if (type == "u8") { +        return void_ptr_to_hexstring<uint8_t>(v_ptr, index); +    } +    else { +        return str(boost::format("<unhandled data type: %s>") % type); +    } +} + +std::string item_to_string( +    const void *v_ptr, +    size_t index, +    const std::string &type, +    const bool print_hex +) { +    if (print_hex) { +        return item_to_hexstring(v_ptr, index, type); +    } + +    if (type == "sc16") { +        const std::complex<boost::int16_t> *ptr = reinterpret_cast<const std::complex<boost::int16_t> *>(v_ptr); +        return boost::lexical_cast<std::string>(ptr[index]); +    } +    else if (type == "sc8") { +        const std::complex<boost::int8_t> *ptr = reinterpret_cast<const std::complex<boost::int8_t> *>(v_ptr); +        return boost::lexical_cast<std::string>(ptr[index]); +    } +    else if (type == "fc32") { +        const std::complex<float> *ptr = reinterpret_cast<const std::complex<float> *>(v_ptr); +        return boost::lexical_cast<std::string>(ptr[index]); +    } +    else if (type == "item32") { +        const boost::uint32_t *ptr = reinterpret_cast<const boost::uint32_t *>(v_ptr); +        return boost::lexical_cast<std::string>(ptr[index]); +    } +    else if (type == "s16") { +        const boost::int16_t *ptr = reinterpret_cast<const boost::int16_t *>(v_ptr); +        return boost::lexical_cast<std::string>(ptr[index]); +    } +    else { +        return str(boost::format("<unhandled data type: %s>") % type); +    } +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ +    std::string in_format, out_format; +    std::string priorities; +    std::string seed_mode; +    priority_type prio = -1, max_prio; +    size_t iterations, n_samples; +    size_t n_inputs, n_outputs; +    buf_init_t buf_seed_mode = RANDOM; + +    /// Command line arguments +    po::options_description desc("Converter benchmark options:"); +    desc.add_options() +        ("help", "help message") +        ("in",  po::value<std::string>(&in_format), "Input format (e.g. 'sc16')") +        ("out", po::value<std::string>(&out_format), "Output format (e.g. 'sc16')") +        ("samples",  po::value<size_t>(&n_samples)->default_value(1000000), "Number of samples per iteration") +        ("iterations",  po::value<size_t>(&iterations)->default_value(10000), "Number of iterations per benchmark") +        ("priorities", po::value<std::string>(&priorities)->default_value("default"), "Converter priorities. Can be 'default', 'all', or a comma-separated list of priorities.") +        ("max-prio", po::value<priority_type>(&max_prio)->default_value(4), "Largest available priority (advanced feature)") +        ("n-inputs",   po::value<size_t>(&n_inputs)->default_value(1),  "Number of input vectors") +        ("n-outputs",  po::value<size_t>(&n_outputs)->default_value(1), "Number of output vectors") +        ("debug-converter", "Skip benchmark and print conversion results. Implies iterations==1 and will only run on a single converter.") +        ("seed-mode", po::value<std::string>(&seed_mode)->default_value("random"), "How to initialize the data: random, incremental") +        ("hex", "When using debug mode, dump memory in hex") +    ; +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    //print the help message +    if (vm.count("help")){ +        std::cout << boost::format("UHD Converter Benchmark Tool %s") % desc << std::endl << std::endl; +        std::cout << "  Use this to benchmark or debug converters." << std::endl +                  << "  When using as a benchmark tool, it will output the execution time\n" +                     "  for every conversion run in CSV format to stdout. Every line between\n" +                     "  the output delimiters {{{ }}} is of the format: <PRIO>,<TIME IN MILLISECONDS>\n" +                     "  When using for converter debugging, every line is formatted as\n" +                     "  <INPUT_VALUE>,<OUTPUT_VALUE>\n" << std::endl; +        return EXIT_FAILURE; +    } + +    // Parse more arguments +    if (seed_mode == "incremental") { +        buf_seed_mode = INC; +    } else if (seed_mode == "random") { +        buf_seed_mode = RANDOM; +    } else { +        std::cout << "Invalid argument: --seed-mode must be either 'incremental' or 'random'." << std::endl; +    } + +    bool debug_mode = bool(vm.count("debug-converter")); +    if (debug_mode) { +        iterations = 1; +    } + +    /// Create the converter(s) ////////////////////////////////////////////// +    id_type converter_id; +    converter_id.input_format  = in_format; +    converter_id.output_format = out_format; +    converter_id.num_inputs    = n_inputs; +    converter_id.num_outputs   = n_outputs; +    std::cout << "Requested converter format: " << converter_id.to_string() +              << std::endl; +    uhd::dict<priority_type, converter::sptr> conv_list; +    if (priorities == "default" or priorities.empty()) { +        try { +            conv_list[prio] = get_converter(converter_id, prio)(); // Can throw a uhd::key_error +        } catch(const uhd::key_error &e) { +            std::cout << "No converters found." << std::endl; +            return EXIT_FAILURE; +        } +    } else if (priorities == "all") { +        for (priority_type i = 0; i < max_prio; i++) { +            try { +                // get_converter() returns a factory function, execute that immediately: +                converter::sptr conv_for_prio = get_converter(converter_id, i)(); // Can throw a uhd::key_error +                conv_list[i] = conv_for_prio; +            } catch (...) { +                continue; +            } +        } +    } else { // Assume that priorities contains a list of prios (e.g. 0,2,3) +        std::vector<std::string> prios_in_list; +        boost::split( +                prios_in_list, +                priorities, +                boost::is_any_of(","), // Split at , +                boost::token_compress_on // Avoid empty results +        ); +        BOOST_FOREACH(const std::string &this_prio, prios_in_list) { +            size_t prio_index = boost::lexical_cast<size_t>(this_prio); +            converter::sptr conv_for_prio = get_converter(converter_id, prio_index)(); // Can throw a uhd::key_error +            conv_list[prio_index] = conv_for_prio; +        } +    } +    std::cout << "Found " << conv_list.size() << " converter(s)." << std::endl; + +    /// Create input and output buffers /////////////////////////////////////// +    // First, convert the types to plain types (e.g. sc16_item32_le -> sc16) +    const std::string in_type  = format_to_type(in_format); +    const std::string out_type = format_to_type(out_format); +    const size_t in_size  = get_bytes_per_item(in_type); +    const size_t out_size = get_bytes_per_item(out_type); +    // Create the buffers and fill them with random data & zeros, respectively +    std::vector< std::vector<char> > input_buffers(n_inputs, std::vector<char>(in_size * n_samples, 0)); +    std::vector< std::vector<char> > output_buffers(n_outputs, std::vector<char>(out_size * n_samples, 0)); +    init_buffers(input_buffers, in_type, in_size, buf_seed_mode); +    // Create ref vectors for the converter: +    std::vector<const void *>  input_buf_refs(n_inputs); +    std::vector<void *> output_buf_refs(n_outputs); +    for (size_t i = 0; i < n_inputs; i++) { +        input_buf_refs[i] = reinterpret_cast<const void *>(&input_buffers[i][0]); +    } +    for (size_t i = 0; i < n_outputs; i++) { +        output_buf_refs[i] = reinterpret_cast<void *>(&output_buffers[i][0]); +    } + +    /// Final configurations to the converter: +    std::cout << "Configuring converters:" << std::endl; +    BOOST_FOREACH(priority_type prio_i, conv_list.keys()) { +        std::cout << "* [" << prio_i << "]: "; +        configure_conv(conv_list[prio_i], in_type, out_type); +    } + +    /// Run the benchmark for every converter //////////////////////////////// +    std::cout << "{{{" << std::endl; +    if (not debug_mode) { +        std::cout << "prio,duration_ms,avg_duration_ms,n_samples,iterations" << std::endl; +        BOOST_FOREACH(priority_type prio_i, conv_list.keys()) { +            double duration = run_benchmark( +                    conv_list[prio_i], +                    input_buf_refs, +                    output_buf_refs, +                    n_samples, +                    iterations +            ); +            std::cout << boost::format("%i,%d,%d,%d,%d") +                % prio_i +                % (duration * 1000) +                % (duration * 1000.0 / iterations) +                % n_samples +                % iterations +                << std::endl; +        } +    } + +    /// Or run debug mode, which runs one conversion and prints the results //// +    if (debug_mode) { +        // Only run on the first converter: +        run_benchmark( +            conv_list[conv_list.keys().at(0)], +            input_buf_refs, +            output_buf_refs, +            n_samples, +            iterations +        ); +        for (size_t i = 0; i < n_samples; i++) { +            std::cout << item_to_string(input_buf_refs[0], i, in_type, vm.count("hex")) +                      << ";" +                      << item_to_string(reinterpret_cast< const void * >(output_buf_refs[0]), i, out_type, vm.count("hex")) +                      << std::endl; +        } +    } +    std::cout << "}}}" << std::endl; + +    return EXIT_SUCCESS; +} diff --git a/host/utils/converter_benchmark.py b/host/utils/converter_benchmark.py new file mode 100644 index 000000000..c3cab8753 --- /dev/null +++ b/host/utils/converter_benchmark.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# +# Copyright 2015 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/>. +# +""" +Wrap the converter_benchmark tool and produce prettier results. +""" + +from __future__ import print_function +import argparse +import csv +import subprocess + +INTRO_SETUP = { +    'n_samples': { +        'title': 'Samples per iteration', +    }, +    'iterations': { +        'title': 'Number of iterations' +    }, +} + +TABLE_SETUP = { +    'prio': { +        'title': 'Priority', +    }, +    'duration_ms': { +        'title': 'Total Duration (ms)', +    }, +    'avg_duration_ms': { +        'title': 'Avg. Duration (ms)', +    }, +} + +def run_benchmark(args): +    """ Run the tool with the given arguments, return the section in the {{{ }}} brackets """ +    call_args = ['./converter_benchmark',] +    for k, v in args.__dict__.iteritems(): +        k = k.replace('_', '-') +        if v is None: +            continue +        if k in ('debug-converter', 'hex'): +            if v: +                call_args.append('--{0}'.format(k)) +            continue +        call_args.append('--{0}'.format(k)) +        call_args.append(str(v)) +    print(call_args) +    try: +        output = subprocess.check_output(call_args) +    except subprocess.CalledProcessError as ex: +        print(ex.output) +        exit(ex.returncode) +    header_out, csv_output = output.split('{{{', 1) +    csv_output = csv_output.split('}}}', 1) +    assert len(csv_output) == 2 and csv_output[1].strip() == '' +    return header_out, csv_output[0] + +def print_stats_table(args, csv_output): +    """ +    Print stats. +    """ +    reader = csv.reader(csv_output.strip().split('\n'), delimiter=',') +    title_row = reader.next() +    row_widths = [0,] * len(TABLE_SETUP) +    for idx, row in enumerate(reader): +        if idx == 0: +            # Print intro: +            for k, v in INTRO_SETUP.iteritems(): +                print("{title}: {value}".format( +                    title=v['title'], +                    value=row[title_row.index(k)], +                )) +            print("") +            # Print table header +            for idx, item in enumerate(TABLE_SETUP): +                print(" {title} ".format(title=TABLE_SETUP[item]['title']), end='') +                row_widths[idx] = len(TABLE_SETUP[item]['title']) +                if idx < len(TABLE_SETUP) - 1: +                    print("|", end='') +            print("") +            for idx, item in enumerate(TABLE_SETUP): +                print("-" * (row_widths[idx] + 2), end='') +                if idx < len(TABLE_SETUP) - 1: +                    print("+", end='') +            print("") +        # Print actual row data +        for idx, item in enumerate(TABLE_SETUP): +            format_str = " {{item:>{n}}} ".format(n=row_widths[idx]) +            print(format_str.format(item=row[title_row.index(item)]), end='') +            if idx < len(TABLE_SETUP) - 1: +                print("|", end='') +        print("") + +def print_debug_table(args, csv_output): +    """ +    Print debug output. +    """ +    reader = csv.reader(csv_output.strip().split('\n'), delimiter=';') +    print_widths_hex = { +        'u8': 2, +        'sc16': 8, +        'fc32': 16, +        's16': 4, +    } +    if args.hex: +        format_str = "{{0[0]:0>{n_in}}} => {{0[1]:0>{n_out}}}".format( +            n_in=print_widths_hex[getattr(args, 'in').split('_', 1)[0]], +            n_out=print_widths_hex[args.out.split('_', 1)[0]] +        ) +    else: +        format_str = "{0[0]}\t=>\t{0[1]}" +    for row in reader: +        print(format_str.format(row)) + +def setup_argparse(): +    """ Configure arg parser. """ +    parser = argparse.ArgumentParser( +        description="UHD Converter Benchmark + Debugging Utility.", +    ) +    parser.add_argument( +        "-i", "--in", required=True, +        help="Input format  (e.g. 'sc16')" +    ) +    parser.add_argument( +        "-o", "--out", required=True, +        help="Output format  (e.g. 'sc16')" +    ) +    parser.add_argument( +        "-s", "--samples", type=int, +        help="Number of samples per iteration" +    ) +    parser.add_argument( +        "-N", "--iterations", type=int, +        help="Number of iterations per benchmark", +    ) +    parser.add_argument( +        "-p", "--priorities", +        help="Converter priorities. Can be 'default', 'all', or a comma-separated list of priorities.", +    ) +    parser.add_argument( +        "--max-prio", type=int, +        help="Largest available priority (advanced feature)", +    ) +    parser.add_argument( +        "--n-inputs", type=int, +        help="Number of input vectors", +    ) +    parser.add_argument( +        "--n-outputs", type=int, +        help="Number of output vectors", +    ) +    parser.add_argument( +        "--seed-mode", choices=('random', 'incremental'), +        help="How to initialize the data: random, incremental", +    ) +    parser.add_argument( +        "--debug-converter", action='store_true', +        help="Skip benchmark and print conversion results. Implies iterations==1 and will only run on a single converter.", +    ) +    parser.add_argument( +        "--hex", action='store_true', +        help="In debug mode, display data as hex values.", +    ) +    return parser + +def main(): +    """ Go, go, go! """ +    args = setup_argparse().parse_args() +    print("Running converter benchmark...") +    header_out, csv_output = run_benchmark(args) +    print(header_out) +    if args.debug_converter: +        print_debug_table(args, csv_output) +    else: +        print_stats_table(args, csv_output) + +if __name__ == "__main__": +    main() + diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp index 3b98a634c..4c17c6044 100644 --- a/host/utils/query_gpsdo_sensors.cpp +++ b/host/utils/query_gpsdo_sensors.cpp @@ -1,5 +1,5 @@  // -// Copyright 2012,2014 Ettus Research LLC +// Copyright 2012,2014,2015 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 @@ -38,7 +38,6 @@ void print_notes(void) {    std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n");    std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n");    std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); -  std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n");    std::cout << boost::format("****************************************************************************************************************\n");  } @@ -51,7 +50,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){    po::options_description desc("Allowed options");    desc.add_options()      ("help", "help message") -    ("args", po::value<std::string>(&args)->default_value(""), "Specify a single USRP.") +    ("args", po::value<std::string>(&args)->default_value(""), "Device address arguments specifying a single USRP")      ;    po::variables_map vm;    po::store(po::parse_command_line(argc, argv, desc), vm); @@ -59,8 +58,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){    //Print the help message    if (vm.count("help")) { -    std::cout << boost::format("Query GPSDO Sensors %s") % desc << std::endl; -    return EXIT_FAILURE; +      std::cout << boost::format("Query GPSDO Sensors, try to lock the reference oscillator to the GPS disciplined clock, and set the device time to GPS time") +          << std::endl +          << std::endl +          << desc; +      return EXIT_FAILURE;    }    //Create a USRP device @@ -68,9 +70,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);    std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); -  print_notes(); - -    //Verify GPS sensors are present (i.e. EEPROM has been burnt)    std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); @@ -82,38 +81,96 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      exit(EXIT_FAILURE);    } +  // Explicitly set time source to gpsdo +  boost::this_thread::sleep(boost::posix_time::seconds(1)); +  try { +      usrp->set_time_source("gpsdo"); +  } catch (uhd::value_error &e) { +      std::cout << "could not set the time source to \"gpsdo\"; error was:" <<std::endl; +      std::cout << e.what() << std::endl; +      std::cout << "trying \"external\"..." <<std::endl; +      try { +          usrp->set_time_source("external"); +      } catch (uhd::value_error &e) { +          std::cout << "\"external\" failed, too." << std::endl; +      } +  } +  std::cout<< std::endl << "Time source is now " << usrp->get_time_source(0) << std::endl; +    //Check for GPS lock    uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked",0);    if(not gps_locked.to_bool()) { -    std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); -    std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); -  } else -      std::cout << boost::format("GPS Locked\n"); +      std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); +      std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); +  } else { +      std::cout << boost::format("GPS Locked"); +  } + +  std::cout << "\nSetting the reference clock source to \"gpsdo\"...\n"; +  try { +      usrp->set_clock_source("gpsdo"); +  } catch (uhd::value_error &e) { +      std::cout << "could not set the clock source to \"gpsdo\"; error was:" <<std::endl; +      std::cout << e.what() << std::endl; +      std::cout << "trying \"external\"..." <<std::endl; +      try{ +          usrp->set_clock_source("external"); +      } catch (uhd::value_error &e) { +          std::cout << "\"external\" failed, too." << std::endl; +      } +  } +  std::cout<< std::endl << "Clock source is now " << usrp->get_clock_source(0) << std::endl; + +  print_notes(); +    //Check for 10 MHz lock    if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) { -    uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); -    if(not gps_locked.to_bool()) { -      std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); -      std::cout << boost::format("Double check installation instructions (N2X0/E1X0 only): https://www.ettus.com/content/files/gpsdo-kit_4.pdf\n\n"); -    } else -        std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); -  }else -    std::cout << boost::format("ref_locked sensor not present on this board.\n"); - -  // Explicitly set time source to gpsdo -  usrp->set_time_source("gpsdo"); +      uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); +      if(not gps_locked.to_bool()) { +          std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); +          std::cout << boost::format("Double check installation instructions (N2X0/E1X0 only): https://www.ettus.com/content/files/gpsdo-kit_4.pdf\n\n"); +          std::cout << boost::format("Locking the internal reference to the GPSDO might take a second to reach stability. Retrying in 10 s...\n\n"); +          boost::this_thread::sleep(boost::posix_time::seconds(10)); +      } +      if(usrp->get_mboard_sensor("ref_locked",0).to_bool()) { +          std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); +      } +  } else { +      std::cout << boost::format("ref_locked sensor not present on this board.\n"); +  }    //Check PPS and compare UHD device time to GPS time    boost::this_thread::sleep(boost::posix_time::seconds(1));    uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); -  const time_t pc_clock_time = time(NULL); -  const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); +  uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + +  //we only care about the full seconds +  signed gps_seconds = gps_time.to_int(); +  long long pps_seconds = last_pps_time.to_ticks(1.0); + +  if(pps_seconds != gps_seconds) { +      std::cout << boost::format("\nGPS and UHD Device time are NOT aligned;\nlast_pps: %ld vs gps: %ld. Trying to set the device time to GPS time...") +                % pps_seconds % gps_seconds +                << std::endl; +      //full next after next second +      uhd::time_spec_t next_pps_time(gps_seconds + 2.0); +      //instruct the USRP to wait for the next PPS edge, then set the new time on the following PPS +      usrp->set_time_unknown_pps(next_pps_time); +      //allow some time to make sure the PPS has come… +      boost::this_thread::sleep(boost::posix_time::seconds(1.1)); +      //…then ask +      gps_seconds = usrp->get_mboard_sensor("gps_time").to_int(); +      pps_seconds = usrp->get_time_last_pps().to_ticks(1.0); +  } -  if (last_pps_time.to_ticks(1.0) == gps_time.to_int()) { +  std::cout << boost::format("last_pps: %ld vs gps: %ld.") +            % pps_seconds % gps_seconds +            << std::endl; +  if (pps_seconds == gps_seconds) {        std::cout << boost::format("GPS and UHD Device time are aligned.\n");    } else { -      std::cout << boost::format("\nGPS and UHD Device time are NOT aligned last_pps: %ld vs gps: %ld. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n") % last_pps_time.to_ticks(1.0) % gps_time.to_int() << std::endl; +      std::cout << boost::format("Could not align UHD Device time to GPS time. Giving up.\n");    }    //print NMEA strings @@ -121,12 +178,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){        uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga");        uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc");        std::cout << boost::format("Printing available NMEA strings:\n"); -      std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); -  } catch (std::exception &) { +      std::cout << boost::format("%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string(); +  } catch (uhd::lookup_error &e) {        std::cout << "NMEA strings not implemented for this device." << std::endl;    } -  std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); -  std::cout << boost::format("PC Clock time: %.0f seconds\n") % pc_clock_time; +  std::cout << boost::format("GPS Epoch time at last PPS: %.5f seconds\n") % usrp->get_mboard_sensor("gps_time").to_real(); +  std::cout << boost::format("UHD Device time last PPS:   %.5f seconds\n") % (usrp->get_time_last_pps().get_real_secs()); +  std::cout << boost::format("UHD Device time right now:  %.5f seconds\n") % (usrp->get_time_now().get_real_secs()); +  std::cout << boost::format("PC Clock time:              %.5f seconds\n") % time(NULL);    //finished    std::cout << boost::format("\nDone!\n\n"); diff --git a/host/utils/uhd_config_info.cpp b/host/utils/uhd_config_info.cpp new file mode 100644 index 000000000..4279c325d --- /dev/null +++ b/host/utils/uhd_config_info.cpp @@ -0,0 +1,89 @@ +// +// Copyright 2015 National Instruments Corp. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/build_info.hpp> +#include <uhd/version.hpp> +#include <uhd/utils/safe_main.hpp> + +#include <boost/format.hpp> +#include <boost/program_options.hpp> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char* argv[]) { +    // Program Options +    po::options_description desc("Allowed Options"); +    desc.add_options() +        ("build-date",         "Print build date") +        ("c-compiler",         "Print C compiler") +        ("cxx-compiler",       "Print C++ compiler") +        ("c-flags",            "Print C compiler flags") +        ("cxx-flags",          "Print C++ compiler flags") +        ("enabled-components", "Print built-time enabled components") +        ("install-prefix",     "Print install prefix") +        ("libusb-version",     "Print libusb version") +        ("print-all",          "Print everything") +        ("version",            "Print this UHD build's version") +        ("help",               "Print help message") +    ; + +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    // Print the help message +    if(vm.count("help") > 0) { +        std::cout << boost::format("UHD Config Info - %s") % desc << std::endl; +        return EXIT_FAILURE; +    } + +    bool print_all = (vm.count("print-all") > 0); + +    if(vm.count("version") > 0 or print_all) { +        std::cout << "UHD " << uhd::get_version_string() << std::endl; +    } +    if(vm.count("build-date") > 0 or print_all) { +        std::cout << "Build date: " << uhd::build_info::build_date() << std::endl; +    } +    if(vm.count("c-compiler") > 0 or print_all) { +        std::cout << "C compiler: " << uhd::build_info::c_compiler() << std::endl; +    } +    if(vm.count("cxx-compiler") > 0 or print_all) { +        std::cout << "C++ compiler: " << uhd::build_info::cxx_compiler() << std::endl; +    } +    if(vm.count("c-flags") > 0 or print_all) { +        std::cout << "C flags: " << uhd::build_info::c_flags() << std::endl; +    } +    if(vm.count("cxx-flags") > 0 or print_all) { +        std::cout << "C++ flags: " << uhd::build_info::cxx_flags() << std::endl; +    } +    if(vm.count("enabled-components") > 0 or print_all) { +        std::cout << "Enabled components: " << uhd::build_info::enabled_components() << std::endl; +    } +    if(vm.count("install-prefix") > 0 or print_all) { +        std::cout << "Install prefix: " << uhd::build_info::install_prefix() << std::endl; +    } +    if(vm.count("boost-version") > 0 or print_all) { +        std::cout << "Boost version: " << uhd::build_info::boost_version() << std::endl; +    } +    if(vm.count("libusb-version") > 0 or print_all) { +        std::string _libusb_version = uhd::build_info::libusb_version(); +        std::cout << "Libusb version: " << (_libusb_version.empty() ? "N/A" : _libusb_version) << std::endl; +    } + +    return EXIT_SUCCESS; +} diff --git a/tools/chdr-dissector/packet-chdr.c b/tools/chdr-dissector/packet-chdr.c index cce46bb84..079e6bb3b 100644 --- a/tools/chdr-dissector/packet-chdr.c +++ b/tools/chdr-dissector/packet-chdr.c @@ -1,5 +1,5 @@  /* - * Dissector for UHD CHDR packets + * Dissector for UHD CVITA (CHDR) packets   *   * Copyright 2010-2014 Ettus Research LLC   * @@ -37,22 +37,45 @@ const unsigned int CHDR_PORT = X300_VITA_UDP_PORT;  static int proto_chdr = -1;  static int hf_chdr_hdr = -1; -static int hf_chdr_is_extension = -1; -static int hf_chdr_reserved = -1; +static int hf_chdr_type = -1;  static int hf_chdr_has_time = -1;  static int hf_chdr_eob = -1; +static int hf_chdr_error = -1;  static int hf_chdr_sequence = -1;  static int hf_chdr_packet_size = -1;  static int hf_chdr_stream_id = -1;  static int hf_chdr_src_dev = -1;  static int hf_chdr_src_ep = -1; +static int hf_chdr_src_blockport = -1;  static int hf_chdr_dst_dev = -1;  static int hf_chdr_dst_ep = -1; +static int hf_chdr_dst_blockport = -1;  static int hf_chdr_timestamp = -1;  static int hf_chdr_payload = -1;  static int hf_chdr_ext_response = -1;  static int hf_chdr_ext_status_code = -1;  static int hf_chdr_ext_seq_num = -1; +static int hf_chdr_cmd = -1; +static int hf_chdr_cmd_address = -1; +static int hf_chdr_cmd_value = -1; + +static const value_string CHDR_PACKET_TYPES[] = { +    { 0, "Data" }, +    { 1, "Data (End-of-Burst)" }, +    { 4, "Flow Control" }, +    { 8, "Command" }, +    { 12, "Response" }, +    { 13, "Error Response" }, +}; + +static const value_string CHDR_PACKET_TYPES_SHORT[] = { +    { 0, "data" }, +    { 1, "data" }, +    { 4, "fc" }, +    { 8, "cmd" }, +    { 12, "resp" }, +    { 13, "resp" }, +};  /* the heuristic dissector is called on every packet with payload.   * The warning printed for this should only be printed once. @@ -64,6 +87,7 @@ static gint ett_chdr = -1;  static gint ett_chdr_header = -1;  static gint ett_chdr_id = -1;  static gint ett_chdr_response = -1; +static gint ett_chdr_cmd = -1;  /* Forward-declare the dissector functions */  void proto_register_chdr(void); @@ -71,7 +95,7 @@ void proto_reg_handoff_chdr(void);  static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);  /* heuristic dissector call. Will always return. */ -static gboolean heur_dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +static gboolean heur_dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* whatislove)  {      if(heur_warning_printed < 1){          printf(LOG_HEADER"heuristic dissector always returns true!\n"); @@ -132,11 +156,21 @@ static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)      proto_tree *stream_tree;      proto_item *response_item;      proto_tree *response_tree; +    proto_item *cmd_item; +    proto_tree *cmd_tree;      gint len;      gint flag_offset;      guint8 *bytes; -    gboolean flag_has_time; +    guint8 hdr_bits = 0; +    guint8 pkt_type = 0; +    gboolean flag_has_time = 0; +    gboolean flag_is_data = 0; +    gboolean flag_is_fc = 0; +    gboolean flag_is_cmd = 0; +    gboolean flag_is_resp = 0; +    gboolean flag_is_eob = 0; +    gboolean flag_is_error = 0;      unsigned long long timestamp;      gboolean is_network;      gint endianness; @@ -169,8 +203,16 @@ static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)          if (len >= 4){              chdr_size = 8; -            bytes = tvb_get_string(tvb, 0, 4); -            flag_has_time = bytes[flag_offset] & 0x20; +            bytes = tvb_get_string(wmem_packet_scope(), tvb, 0, 4); +	    hdr_bits = (bytes[flag_offset] & 0xF0) >> 4; +	    pkt_type = hdr_bits >> 2; +	    flag_is_data = (pkt_type == 0); +	    flag_is_fc = (pkt_type == 1); +	    flag_is_cmd = (pkt_type == 2); +	    flag_is_resp = (pkt_type == 3); +	    flag_is_eob = flag_is_data && (hdr_bits & 0x1); +	    flag_is_error = flag_is_resp && (hdr_bits & 0x1); +            flag_has_time = hdr_bits & 0x2;              if (flag_has_time)                  chdr_size += 8; // 64-bit timestamp          } @@ -178,17 +220,28 @@ static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)          /* Start with a top-level item to add everything else to */          item = proto_tree_add_item(tree, proto_chdr, tvb, 0, min(len, chdr_size), ENC_NA); -        if (len >= 4){ +        if (len >= 4) {              chdr_tree = proto_item_add_subtree(item, ett_chdr); +            /* Header info. First, a top-level header tree item: */              header_item = proto_tree_add_item(chdr_tree, hf_chdr_hdr, tvb, flag_offset, 1, endianness);              header_tree = proto_item_add_subtree(header_item, ett_chdr_header); - -            /* These lines add flag info to tree */ -            proto_tree_add_item(header_tree, hf_chdr_is_extension, tvb, flag_offset, 1, ENC_NA); -            proto_tree_add_item(header_tree, hf_chdr_reserved, tvb, flag_offset, 1, ENC_NA); -            proto_tree_add_item(header_tree, hf_chdr_has_time, tvb, flag_offset, 1, ENC_NA); -            proto_tree_add_item(header_tree, hf_chdr_eob, tvb, flag_offset, 1, ENC_NA); +            proto_item_append_text(header_item, ", Packet type: %s", +                val_to_str(hdr_bits & 0xD, CHDR_PACKET_TYPES, "Unknown (0x%x)") +            ); +            /* Let us query hdr.type */ +            proto_tree_add_string( +                header_tree, hf_chdr_type, tvb, flag_offset, 1, +                val_to_str(hdr_bits & 0xD, CHDR_PACKET_TYPES_SHORT, "invalid") +            ); +            /* And other flags */ +            proto_tree_add_boolean(header_tree, hf_chdr_has_time, tvb, flag_offset, 1, flag_has_time); +            if (flag_is_data) { +                proto_tree_add_boolean(header_tree, hf_chdr_eob, tvb, flag_offset, 1, flag_is_eob); +            } +            if (flag_is_resp) { +                proto_tree_add_boolean(header_tree, hf_chdr_error, tvb, flag_offset, 1, flag_is_error); +            }              /* These lines add sequence, packet_size and stream ID */              proto_tree_add_item(chdr_tree, hf_chdr_sequence, tvb, (is_network ? 0:2), 2, endianness); @@ -199,16 +252,33 @@ static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)                  stream_item = proto_tree_add_item(chdr_tree, hf_chdr_stream_id, tvb, 4, 4, endianness);                  stream_tree = proto_item_add_subtree(stream_item, ett_chdr_id);                  proto_tree_add_item(stream_tree, hf_chdr_src_dev, tvb, id_pos[0], 1, ENC_NA); -                proto_tree_add_item(stream_tree, hf_chdr_src_ep, tvb, id_pos[1], 1, ENC_NA); +                proto_tree_add_item(stream_tree, hf_chdr_src_ep,  tvb, id_pos[1], 1, ENC_NA);                  proto_tree_add_item(stream_tree, hf_chdr_dst_dev, tvb, id_pos[2], 1, ENC_NA); -                proto_tree_add_item(stream_tree, hf_chdr_dst_ep, tvb, id_pos[3], 1, ENC_NA); +                proto_tree_add_item(stream_tree, hf_chdr_dst_ep,  tvb, id_pos[3], 1, ENC_NA); + +                /* Block ports (only add them if address points to a device) */ +                bytes = tvb_get_string(wmem_packet_scope(), tvb, 0, 8); +		if (bytes[id_pos[0]] != 0) { +                    proto_tree_add_item(stream_tree, hf_chdr_src_blockport, tvb, id_pos[1], 1, ENC_NA); +		} +		if (bytes[id_pos[2]] != 0) { +                    proto_tree_add_item(stream_tree, hf_chdr_dst_blockport, tvb, id_pos[3], 1, ENC_NA); +		} + +		/* Append SID in sid_t hex format */ +                proto_item_append_text(stream_item, " (%02X:%02X>%02X:%02X)", +                    bytes[id_pos[0]], +                    bytes[id_pos[1]], +                    bytes[id_pos[2]], +                    bytes[id_pos[3]] +                );                  /* if has_time flag is present interpret timestamp */                  if ((flag_has_time) && (len >= 16)){                      if (is_network)                          item = proto_tree_add_item(chdr_tree, hf_chdr_timestamp, tvb, 8, 8, endianness);                      else{ -                        bytes = (guint8*) tvb_get_string(tvb, 8, sizeof(unsigned long long)); +                        bytes = (guint8*) tvb_get_string(wmem_packet_scope(), tvb, 8, sizeof(unsigned long long));                          timestamp = get_timestamp(bytes, sizeof(unsigned long long));                          proto_tree_add_uint64(chdr_tree, hf_chdr_timestamp, tvb, 8, 8, timestamp);                      } @@ -217,19 +287,21 @@ static void dissect_chdr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)                  int remaining_bytes = (len - chdr_size);                  int show_raw_payload = (remaining_bytes > 0); -                if (hf_chdr_is_extension){ -                    if (remaining_bytes == 8){  // Interpret this as a response packet -                        response_item = proto_tree_add_item(chdr_tree, hf_chdr_ext_response, tvb, chdr_size, 8, endianness); -                        response_tree = proto_item_add_subtree(response_item, ett_chdr_response); - -                        proto_tree_add_item(response_tree, hf_chdr_ext_status_code, tvb, chdr_size, 4, endianness); -                        /* This will show the 12-bits of sequence ID in the last 2 bytes */ -                        proto_tree_add_item(response_tree, hf_chdr_ext_seq_num, tvb, (chdr_size + 4 + (is_network ? 2 : 0)), 2, endianness); -                    } -                } - -                if (show_raw_payload) +                if (flag_is_cmd && remaining_bytes == 8) { +                    cmd_item = proto_tree_add_item(chdr_tree, hf_chdr_cmd, tvb, chdr_size, 8, endianness); +                    cmd_tree = proto_item_add_subtree(cmd_item, ett_chdr_cmd); +                    proto_tree_add_item(cmd_tree, hf_chdr_cmd_address, tvb, chdr_size,     4, endianness); +                    proto_tree_add_item(cmd_tree, hf_chdr_cmd_value,   tvb, chdr_size + 4, 4, endianness); +                } else if (flag_is_resp) { +                    response_item = proto_tree_add_item(chdr_tree, hf_chdr_ext_response, tvb, chdr_size, 8, endianness); +                    response_tree = proto_item_add_subtree(response_item, ett_chdr_response); + +                    proto_tree_add_item(response_tree, hf_chdr_ext_status_code, tvb, chdr_size, 4, endianness); +                    /* This will show the 12-bits of sequence ID in the last 2 bytes */ +                    proto_tree_add_item(response_tree, hf_chdr_ext_seq_num, tvb, (chdr_size + 4 + (is_network ? 2 : 0)), 2, endianness); +                } else if (show_raw_payload) {                      proto_tree_add_item(chdr_tree, hf_chdr_payload, tvb, chdr_size, -1, ENC_NA); +                }              }          }      } @@ -244,17 +316,11 @@ void proto_register_chdr(void)                  NULL, 0xF0,                  NULL, HFILL }          }, -        { &hf_chdr_is_extension, -            { "Extension context packet", "chdr.hdr.ext", -                FT_BOOLEAN, BASE_NONE, -                NULL, 0x80, -                NULL, HFILL } -        }, -        { &hf_chdr_reserved, -            { "Reserved bit", "chdr.hdr.reserved", -                FT_BOOLEAN, BASE_NONE, -                NULL, 0x40, -                NULL, HFILL } +        { &hf_chdr_type, +            { "Packet Type", "chdr.hdr.type", +                FT_STRINGZ, BASE_NONE, +                NULL, 0x00, +                "Packet Type", HFILL }          },          { &hf_chdr_has_time,              { "Time present", "chdr.hdr.has_time", @@ -268,6 +334,12 @@ void proto_register_chdr(void)                  NULL, 0x10,                  NULL, HFILL }          }, +        { &hf_chdr_error, +            { "Error Flag", "chdr.hdr.error", +                FT_BOOLEAN, BASE_NONE, +                NULL, 0x10, +                NULL, HFILL } +        },          { &hf_chdr_sequence,              { "Sequence ID", "chdr.seq",                  FT_UINT16, BASE_DEC, @@ -298,6 +370,12 @@ void proto_register_chdr(void)                  NULL, 0x0,                  NULL, HFILL }          }, +        { &hf_chdr_src_blockport, +            { "Source block port", "chdr.src_bp", +                FT_UINT8, BASE_DEC, +                NULL, 0xF, +                NULL, HFILL } +        },          { &hf_chdr_dst_dev,              { "Destination device", "chdr.dst_dev",                  FT_UINT8, BASE_DEC, @@ -310,6 +388,12 @@ void proto_register_chdr(void)                  NULL, 0x0,                  NULL, HFILL }          }, +        { &hf_chdr_dst_blockport, +            { "Destination block port", "chdr.dst_bp", +                FT_UINT8, BASE_DEC, +                NULL, 0xF, +                NULL, HFILL } +        },          { &hf_chdr_timestamp,              { "Time", "chdr.time",                  FT_UINT64, BASE_DEC, @@ -341,13 +425,32 @@ void proto_register_chdr(void)                  NULL, 0x0FFF,                  NULL, HFILL }          }, +        { &hf_chdr_cmd, +            { "Command", "chdr.cmd", +                FT_BYTES, BASE_NONE, +                NULL, 0x0, +                NULL, HFILL } +        }, +        { &hf_chdr_cmd_address, +            { "Register Address", "chdr.cmd.addr", +                FT_UINT32, BASE_DEC, +                NULL, 0x0, +                NULL, HFILL } +        }, +        { &hf_chdr_cmd_value, +            { "Command Value", "chdr.cmd.val", +                FT_UINT32, BASE_HEX, +                NULL, 0x0, +                NULL, HFILL } +        },      };      static gint *ett[] = {          &ett_chdr,          &ett_chdr_header,          &ett_chdr_id, -        &ett_chdr_response +        &ett_chdr_response, +        &ett_chdr_cmd      };      proto_chdr = proto_register_protocol("UHD CHDR", "CHDR", "chdr");  | 
