diff options
Diffstat (limited to 'firmware/usrp3')
31 files changed, 3237 insertions, 200 deletions
| 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..e5226489f 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" @@ -161,8 +161,8 @@ ge_write_mdio(const uint32_t base, const uint32_t address, const uint32_t port,  static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port)  { -    const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1; -    const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE; +    const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE; +    const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;      if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)      {          return xge_read_mdio(base, address, device, port); @@ -175,8 +175,8 @@ static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint3  static void write_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port, const uint32_t data)  { -    const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1; -    const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE; +    const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE; +    const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;      if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)      {          return xge_write_mdio(base, address, device, port, data); @@ -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,23 +322,24 @@ 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      }  } -void update_eth_state(const uint32_t eth) +void update_eth_state(const uint32_t eth, const uint32_t sfp_type)  {      const bool old_link_up = links_up[eth]; -    const uint32_t status_reg_addr = (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1; -    const bool is_10g = (wb_peek32(SR_ADDR(RB0_BASE, eth == 0 ? RB_ETH_TYPE0 : RB_ETH_TYPE1)) == 1); +    const uint32_t status_reg_addr = (eth==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS;      uint32_t sfpp_status = wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) & 0xFFFF;      if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {          //SFP+ pin state changed. Reinitialize PHY and MAC -        if (is_10g) { -            xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE); -            xge_phy_init(eth ,MDIO_PORT); +        if (sfp_type == RB_SFP_10G_ETH) { +            xge_mac_init((eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE); +            xge_phy_init(eth, MDIO_PORT);          } else {              //No-op for 1G          } @@ -347,7 +347,7 @@ void update_eth_state(const uint32_t eth)          int8_t timeout = 100;          bool link_up = false;          do { -            if (is_10g) { +            if (sfp_type == RB_SFP_10G_ETH) {                  link_up = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0;              } else {                  link_up = ((wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) >> 16) & 0x1) != 0; @@ -362,54 +362,66 @@ void update_eth_state(const uint32_t eth)      }      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");  } -void poll_sfpp_status(const uint32_t eth) +void poll_sfpp_status(const uint32_t sfp)  { -    uint32_t x; -    // Has MODDET/MODAbS changed since we last looked? -    x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); +    uint32_t type = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE)); +    uint32_t status = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS)); -    if (x & SFPP_STATUS_RXLOS_CHG) -        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (x & SFPP_STATUS_RXLOS)); -    if (x & SFPP_STATUS_TXFAULT_CHG) -        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((x & SFPP_STATUS_TXFAULT) >> 1)); -    if (x & SFPP_STATUS_MODABS_CHG) -        UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((x & SFPP_STATUS_MODABS) >> 2)); - -    //update the link up status -    if ((x & SFPP_STATUS_RXLOS_CHG) || (x & SFPP_STATUS_TXFAULT_CHG) || (x & SFPP_STATUS_MODABS_CHG)) -    { -        update_eth_state(eth); -    } - -    if (x & SFPP_STATUS_MODABS_CHG) { +    if (status & SFPP_STATUS_MODABS_CHG) {          // MODDET has changed state since last checked -        if (x & SFPP_STATUS_MODABS) { +        if (status & 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); +            UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", sfp);          } 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); -            xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1); +            UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", sfp); +            if (type == RB_SFP_10G_ETH) { +                xge_read_sfpp_type((sfp==0) ? I2C0_BASE : I2C2_BASE,1); +            }          }      } + +    if (status & SFPP_STATUS_RXLOS_CHG) { +        UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d RXLOS changed state: %d", sfp, (status & SFPP_STATUS_RXLOS)); +    } +    if (status & SFPP_STATUS_TXFAULT_CHG) { +        UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d TXFAULT changed state: %d", sfp, ((status & SFPP_STATUS_TXFAULT) >> 1)); +    } +    if (status & SFPP_STATUS_MODABS_CHG) { +        UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d MODABS changed state: %d", sfp, ((status & SFPP_STATUS_MODABS) >> 2)); +    } + +    //update the link up status +    const bool old_link_up = links_up[sfp]; +    if (type == RB_SFP_AURORA) { +        links_up[sfp] = ((wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS)) >> 16) & 0x1) != 0; +    } else { +        if ((status & SFPP_STATUS_RXLOS_CHG) || +            (status & SFPP_STATUS_TXFAULT_CHG) || +            (status & SFPP_STATUS_MODABS_CHG)) +        { +            update_eth_state(sfp, type); +        } +    } +    if (old_link_up != links_up[sfp]) { +        UHD_FW_TRACE_FSTR(INFO, "The link on SFP port %u is %s", sfp, links_up[sfp]?"up":"down"); +    }  } -void ethernet_init(const uint32_t eth) +void ethernet_init(const uint32_t sfp)  {  #ifdef UHD_FW_TRACE_LEVEL -    uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); -    UHD_FW_TRACE_FSTR(DEBUG, "eth%1d SFP initial state: RXLOS: %d  TXFAULT: %d  MODABS: %d", -        eth, +    uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS )); +    UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d SFP initial state: RXLOS: %d  TXFAULT: %d  MODABS: %d", +        sfp,          (x & SFPP_STATUS_RXLOS),          ((x & SFPP_STATUS_TXFAULT) >> 1),          ((x & SFPP_STATUS_MODABS) >> 2));  #endif -    links_up[eth] = false; -    update_eth_state(eth); +    update_eth_state(sfp, wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE)));  }  // 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_aurora_bist.py b/firmware/usrp3/x300/x300_aurora_bist.py new file mode 100755 index 000000000..f5e119b66 --- /dev/null +++ b/firmware/usrp3/x300/x300_aurora_bist.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python +# +# 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/>. +# + +from __future__ import print_function +from builtins import str +from builtins import range + +import x300_debug +import argparse +import time +import datetime +import math +import tqdm +import numpy + +######################################################################## +# constants +######################################################################## +SFP0_MAC_REG_BASE       = 0xC000 +SFP1_MAC_REG_BASE       = 0xD000 +SFP0_TYPE_REG_OFFSET    = 0xA000 + 16 +SFP1_TYPE_REG_OFFSET    = 0xA000 + 20 +SFP0_STATUS_REG_OFFSET  = 0xA000 + 32 +SFP1_STATUS_REG_OFFSET  = 0xA000 + 36 + +SFP_TYPE_AURORA         = 2 + +MAC_REG_CTRL            = 0 +MAC_REG_STATUS          = 0 +MAC_REG_OVERRUNS        = 4 +MAC_REG_BIST_SAMPS      = 8 +MAC_REG_BIST_ERRORS     = 12 + +MAC_STATUS_LINK_UP_MSK          = 0x00000001 +MAC_STATUS_HARD_ERR_MSK         = 0x00000002 +MAC_STATUS_SOFT_ERR_MSK         = 0x00000004 +MAC_STATUS_BIST_LOCKED_MSK      = 0x00000008 +MAC_STATUS_BIST_LATENCY_MSK     = 0x000FFFF0 +MAC_STATUS_BIST_LATENCY_OFFSET  = 4 +MAC_STATUS_CHECKSUM_ERRS_MSK    = 0xFFF00000 +MAC_STATUS_CHECKSUM_ERRS_OFFSET = 20 + +MAC_CTRL_BIST_CHECKER_EN    = 0x00000001 +MAC_CTRL_BIST_GEN_EN        = 0x00000002 +MAC_CTRL_BIST_LOOPBACK_EN   = 0x00000004 +MAC_CTRL_PHY_RESET          = 0x00000100 +MAC_CTRL_BIST_RATE_MSK      = 0x000000F8 +MAC_CTRL_BIST_RATE_OFFSET   = 3 + +AURORA_CLK_RATE         = 156.25e6 +BUS_CLK_RATE            = 166.66e6 +BIST_MAX_TIME_LIMIT     = math.floor(pow(2,48)/AURORA_CLK_RATE)-1 + +######################################################################## +# utils +######################################################################## +def get_aurora_info(ctrl): +    if (ctrl.peek(SFP0_TYPE_REG_OFFSET) == SFP_TYPE_AURORA): +        aur_port = 0 +    elif (ctrl.peek(SFP1_TYPE_REG_OFFSET) == SFP_TYPE_AURORA): +        aur_port = 1 +    else: +        aur_port = -1 +    link_up = False +    if aur_port != -1: +        mac_base = SFP0_MAC_REG_BASE if aur_port == 0 else SFP1_MAC_REG_BASE +        link_up = ((ctrl.peek(mac_base + MAC_REG_STATUS) & MAC_STATUS_LINK_UP_MSK) != 0) +    return (aur_port, link_up) + +def get_rate_setting(rate): +    for div in range(2,32): +        if (rate < 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/div)): +            return (div-1, 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/(div-1))) +    return (0, 8e-6 * BUS_CLK_RATE) + +def run_ber_loopback_bist(ctrls, duration, rate_tuple): +    (rate_sett, rate) = rate_tuple +    print('[INFO] Running BER Loopback BIST at %.0fMB/s for %.0fs...'%(rate,duration)) +    # Determine offsets +    (mst_port, link_up) = get_aurora_info(ctrls['master']) +    MST_MAC_REG_BASE = SFP0_MAC_REG_BASE if (mst_port == 0) else SFP1_MAC_REG_BASE +    if 'slave' in ctrls: +        (sla_port, link_up) = get_aurora_info(ctrls['slave']) +        SLA_MAC_REG_BASE = SFP0_MAC_REG_BASE if (sla_port == 0) else SFP1_MAC_REG_BASE +    # Reset both PHYS +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET) +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET) +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) +    time.sleep(1.5) +    # Put the slave in loopback mode and the master in BIST mode +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN) +    if rate_sett == 0: +        master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN +    else: +        master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, master_ctrl) +    start_time = datetime.datetime.now() +    # Wait and check if BIST locked +    time.sleep(0.5) +    mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS) +    if (not (mst_status & MAC_STATUS_BIST_LOCKED_MSK)): +        print('[ERROR] BIST engine did not lock onto a PRBS word!') +    # Wait for requested time +    try: +        for i in tqdm.tqdm(list(range(duration)), desc='[INFO] Progress'): +            time.sleep(1.0) +    except KeyboardInterrupt: +        print('[WARNING] Operation cancelled by user.') +    # Turn off the BIST generator and loopback +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +    stop_time = datetime.datetime.now() +    time.sleep(0.5) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) +    # Validate status and no overruns +    mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS) +    mst_overruns = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_OVERRUNS) +    if (mst_status & MAC_STATUS_HARD_ERR_MSK): +        print('[ERROR] Hard errors in master PHY') +    if (mst_overruns > 0): +        print('[ERROR] Buffer overruns in master PHY') +    if 'slave' in ctrls: +        sla_status = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_STATUS) +        sla_overruns = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_OVERRUNS) +        if (sla_status & MAC_STATUS_HARD_ERR_MSK): +            print('[ERROR] Hard errors in slave PHY') +        if (sla_overruns > 0): +            print('[ERROR] Buffer overruns in slave PHY') +    # Compure latency +    mst_samps = 65536.0*ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_BIST_SAMPS) +    mst_errors = 1.0*ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_BIST_ERRORS) +    if (mst_samps != 0): +        mst_latency_cyc = 16.0*((mst_status & MAC_STATUS_BIST_LATENCY_MSK) >> MAC_STATUS_BIST_LATENCY_OFFSET) +        time_diff = stop_time - start_time +        print('[INFO] BIST Complete!') +        print('- Elapsed Time              = ' + str(time_diff)) +        print('- Max BER (Bit Error Ratio) = %.4g (%d errors out of %d)'%((mst_errors+1)/mst_samps,mst_errors,mst_samps)) +        print('- Max Roundtrip Latency     = %.1fus'%(1e6*mst_latency_cyc/AURORA_CLK_RATE)) +        print('- Approx Throughput         = %.0fMB/s'%((8e-6*mst_samps)/time_diff.total_seconds())) +    else: +        print('[ERROR] BIST Failed!') +    # Drain and Cleanup +    print('[INFO] Cleaning up...') +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +    time.sleep(0.5) +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) + +def run_latency_loopback_bist(ctrls, duration, rate_tuple): +    (rate_sett, rate) = rate_tuple +    print('[INFO] Running Latency Loopback BIST at %.0fMB/s for %.0fs...'%(rate,duration)) +    # Determine offsets +    (mst_port, link_up) = get_aurora_info(ctrls['master']) +    MST_MAC_REG_BASE = SFP0_MAC_REG_BASE if (mst_port == 0) else SFP1_MAC_REG_BASE +    if 'slave' in ctrls: +        (sla_port, link_up) = get_aurora_info(ctrls['slave']) +        SLA_MAC_REG_BASE = SFP0_MAC_REG_BASE if (sla_port == 0) else SFP1_MAC_REG_BASE +    # Reset both PHYS +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET) +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET) +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) +    time.sleep(1.5) + +    # Put the slave in loopback mode and the master in BIST mode +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN) +    if rate_sett == 0: +        master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN +    else: +        master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN + +    start_time = datetime.datetime.now() +    latencies = [] +    mst_lock_errors = 0 +    mst_hard_errors = 0 +    mst_overruns = 0 +    try: +        for i in tqdm.tqdm(list(range(duration*10)), desc='[INFO] Progress'): +            ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, master_ctrl) +            # Wait and check if BIST locked +            time.sleep(0.05) +            mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS) +            if (not (mst_status & MAC_STATUS_BIST_LOCKED_MSK)): +                mst_lock_errors += 1 +            # Turn off the BIST generator +            ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +            # Validate status and no overruns +            mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS) +            mst_overruns += ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_OVERRUNS) +            if (mst_status & MAC_STATUS_HARD_ERR_MSK): +                mst_hard_errors += 1 +            time.sleep(0.05) +            ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) +            # Compure latency +            mst_latency_cyc = 16.0*((mst_status & MAC_STATUS_BIST_LATENCY_MSK) >> MAC_STATUS_BIST_LATENCY_OFFSET) +            mst_latency_us = 1e6*mst_latency_cyc/AURORA_CLK_RATE +            latencies.append(mst_latency_us) +    except KeyboardInterrupt: +        print('[WARNING] Operation cancelled by user.') +    stop_time = datetime.datetime.now() +    # Report +    if (mst_lock_errors > 0): +        print('[ERROR] BIST engine did not lock onto a PRBS word %d times!'%(mst_lock_errors)) +    if (mst_hard_errors > 0): +        print('[ERROR] There were %d hard errors in master PHY'%(mst_hard_errors)) +    if (mst_overruns > 0): +        print('[ERROR] There were %d buffer overruns in master PHY'%(mst_overruns)) + +    print('[INFO] BIST Complete!') +    print('- Elapsed Time               = ' + str(stop_time - start_time)) +    print('- Roundtrip Latency Mean     = %.2fus'%(numpy.mean(latencies))) +    print('- Roundtrip Latency Stdev    = %.2fus'%(numpy.std(latencies))) +    # Turn off BIST loopback     +    time.sleep(0.5) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) +    if 'slave' in ctrls: +        sla_status = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_STATUS) +        sla_overruns = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_OVERRUNS) +        if (sla_status & MAC_STATUS_HARD_ERR_MSK): +            print('[ERROR] Hard errors in slave PHY') +        if (sla_overruns > 0): +            print('[ERROR] Buffer overruns in slave PHY') +    # Drain and Cleanup +    print('[INFO] Cleaning up...') +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN) +    time.sleep(0.5) +    ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0) +    if 'slave' in ctrls: +        ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0) + +######################################################################## +# command line options +######################################################################## +def get_options(): +    parser = argparse.ArgumentParser(description='Controller for the USRP X3X0 Aurora BIST Engine') +    parser.add_argument('--master', type=str, default=None, required=True, help='IP Address of master USRP-X3X0 device') +    parser.add_argument('--slave', type=str, default=None, help='IP Address of slave USRP-X3X0 device') +    parser.add_argument('--test', type=str, default='ber', choices=['ber', 'latency'], help='Type of test to run') +    parser.add_argument('--duration', type=int, default=10, help='Duration of test in seconds') +    parser.add_argument('--rate', type=int, default=1245, help='BIST throughput in MB/s') +    return parser.parse_args() + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = get_options() + +    if (options.duration < 0 or options.duration > BIST_MAX_TIME_LIMIT): +        raise Exception('Invalid duration. Min = 0s and Max = %ds'%(BIST_MAX_TIME_LIMIT)) + +    ctrls = dict() +    ctrls['master'] = x300_debug.ctrl_socket(addr=options.master) +    if options.slave: +        ctrls['slave'] = x300_debug.ctrl_socket(addr=options.slave) + +    # Report device and core info +    links_up = True +    for node in ctrls: +        print('[INFO] ' + node.upper() + ':') +        ctrl = ctrls[node] +        (aur_port, link_up) = get_aurora_info(ctrl) +        if aur_port >= 0: +            status_str = str(aur_port) + (' UP' if link_up else ' DOWN') +        else: +            status_str = 'Not Detected!' +        print('- Mgmt IP Addr  : ' + (options.master if node == 'master' else options.slave)) +        print('- Aurora Status : Port ' + status_str) +        links_up = links_up & link_up + +    # Sanity check +    if not links_up: +        print('[ERROR] At least one of the links is down. Cannot proceed.') +        exit(1) + +    # Run BIST +    if options.test == 'ber': +        run_ber_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate)) +    else: +        run_latency_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate)) + diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py index c9bcbb138..e134a7275 100755 --- a/firmware/usrp3/x300/x300_debug.py +++ b/firmware/usrp3/x300/x300_debug.py @@ -1,6 +1,6 @@  #!/usr/bin/env python  # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -21,27 +21,36 @@ import math  import socket  import struct -  ########################################################################  # constants  ######################################################################## -B250_FW_COMMS_UDP_PORT = 49152 +X300_FW_COMMS_UDP_PORT = 49152 + +X300_FW_COMMS_FLAGS_ACK = 1 +X300_FW_COMMS_FLAGS_ERROR = 2 +X300_FW_COMMS_FLAGS_POKE32 = 4 +X300_FW_COMMS_FLAGS_PEEK32 = 8 + +X300_FIXED_PORTS = 5 -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_ZPU_MISC_SR_BUS_OFFSET = 0xA000 +X300_ZPU_XBAR_SR_BUS_OFFSET = 0xB000 + +# Settings register bus addresses (hangs off ZPU wishbone bus) +# Multiple by 4 as ZPU wishbone bus is word aligned +X300_SR_NUM_CE       = X300_ZPU_MISC_SR_BUS_OFFSET + 4*7 +X300_SR_RB_ADDR_XBAR = X300_ZPU_MISC_SR_BUS_OFFSET + 4*128 +# Readback addresses +X300_RB_CROSSBAR     = X300_ZPU_MISC_SR_BUS_OFFSET + 4*128  #UDP_CTRL_PORT = 49183  UDP_MAX_XFER_BYTES = 1024  UDP_TIMEOUT = 3 -#USRP2_FW_PROTO_VERSION = 11 #should be unused after r6  #REG_ARGS_FMT = '!LLLLLB15x'  #REG_IP_FMT = '!LLLL20x'  REG_PEEK_POKE_FMT = '!LLLL' -  _seq = -1  def seq():      global _seq @@ -66,7 +75,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 @@ -78,47 +87,71 @@ class ctrl_socket(object):          self._sock.send(pkt)          return self._sock.recv(UDP_MAX_XFER_BYTES) -    def read_router_stats(self): +    def read_router_stats(self, blocks=None, ignore=None): +        # Readback number of CEs +        num_ports = self.peek(X300_SR_NUM_CE) + X300_FIXED_PORTS +        ports = ['eth0', 'eth1', 'pcie', 'radio0', 'radio1'] + ["Block{num}".format(num=x) for x in range(num_ports-X300_FIXED_PORTS)] +        if blocks is None: +            print("\nNote: Using default CE port names (use --blocks to specify)\n") +        else: +            user_ports = [x.strip() for x in blocks.split(",") if len(x.strip())] +            for idx, user_port in enumerate(user_ports): +                ports[idx + X300_FIXED_PORTS] = user_port +        if ignore is None: +            ignore = [] +        else: +            ignore = [int(x.strip()) for x in ignore.split(",") if len(x.strip())] +        print("Egress Port "), +        # Write out xbar ports +        PORT_MAX_LEN = 12 +        for idx, in_prt in enumerate(ports): +            if idx in ignore: +                continue +            print "{spaces}{name}".format(spaces=(" "*max(0, PORT_MAX_LEN-len(in_prt))), name=in_prt),          print -        print("            "), -        ports = ['        eth0','        eth1','      radio0','      radio1','    compute0','    compute1','    compute2','        pcie'] -        for in_prt in ports: -            print("%s" % in_prt), -        print("   Egress Port") -        print("             "), -        for in_prt in range (0, 8): -            print("____________"), +        print(" "*(PORT_MAX_LEN+1)), +        for in_prt in range(num_ports-len(ignore)): +            print("_" * (PORT_MAX_LEN-1) + " "),          print -        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) -                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") +        for in_prt, port_name in enumerate(ports): +            if in_prt in ignore: +                continue +            print "{spaces}{name} |".format(spaces=(" "*max(0, PORT_MAX_LEN-len(port_name))), name=port_name), +            for out_prt in range(num_ports): +                if out_prt in ignore: +                    continue +                self.poke(X300_SR_RB_ADDR_XBAR,(in_prt*num_ports+out_prt)) +                data = self.peek(X300_RB_CROSSBAR)                  print("%10d  " % (data)),              print          print          print("Ingress Port")          print +    def peek_print(self,peek_addr): +        peek_data = self.peek(peek_addr) +        print("PEEK of address %d(0x%x) reads %d(0x%x)" % (peek_addr,peek_addr,peek_data,peek_data)) +        return peek_data      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)) -        print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data)) +        if flags & X300_FW_COMMS_FLAGS_ERROR == X300_FW_COMMS_FLAGS_ERROR: +            raise Exception("X300 peek of address %d returns error code" % (addr)) +        return data + +    def poke_print(self,poke_addr,poke_data): +        print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data)) +        return(self.poke(poke_addr,poke_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)) -        print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data)  ) +        if flags & X300_FW_COMMS_FLAGS_ERROR == X300_FW_COMMS_FLAGS_ERROR: +            raise Exception("X300 peek of address %d returns error code" % (addr)) +        return data  ######################################################################## @@ -126,14 +159,14 @@ 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("--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("--addr", type="string", help="USRP-X300 device address", default='') +    parser.add_option("--stats", action="store_true", help="Display RFNoC Crossbar Stats", 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("--blocks", help="List names of blocks (post-radio)", default=None) +    parser.add_option("--ignore", help="List of ports to ignore", default=None)      (options, args) = parser.parse_args() -      return options @@ -143,25 +176,18 @@ def get_options():  if __name__=='__main__':      options = get_options() - -    if options.list: -        print('Possible network devices:') -        print('  ' + '\n  '.join(enumerate_devices())) -        exit() -      if not options.addr: raise Exception('no address specified')      status = ctrl_socket(addr=options.addr)      if options.stats: -        status.read_router_stats() - +        status.read_router_stats(options.blocks, options.ignore)      if options.peek is not None:          addr = options.peek -        status.peek(addr) +        status.peek_print(addr)      if options.poke is not None and options.data is not None:          addr = options.poke          data = options.data -        status.poke(addr,data) +        status.poke_print(addr,data) diff --git a/firmware/usrp3/x300/x300_defs.h b/firmware/usrp3/x300/x300_defs.h index c4011bd12..e9e9f34fa 100644 --- a/firmware/usrp3/x300/x300_defs.h +++ b/firmware/usrp3/x300/x300_defs.h @@ -4,11 +4,11 @@  #ifndef INCLUDED_X300_DEFS_H  #define INCLUDED_X300_DEFS_H -#define CPU_CLOCK 166666667 +#define CPU_CLOCK  83333333  #define MAIN_RAM_BASE 0x0000  #define PKT_RAM0_BASE 0x8000 -#define XGE0_BASE 0xC000 -#define XGE1_BASE 0xD000 +#define SFP0_MAC_BASE 0xC000 +#define SFP1_MAC_BASE 0xD000  #define BOOT_LDR_BASE 0xFC00  #define UART0_BASE 0xfd00  #define UART0_BAUD 115200 @@ -21,9 +21,6 @@  #define RB0_BASE 0xa000 //same as set  #define SETXB_BASE 0xb000 -#define ETH1G -//#define ETH10G  -  //eeprom map for mboard addrs  #define MBOARD_EEPROM_ADDR 0x50 @@ -36,6 +33,7 @@ static const int SR_SFPP_CTRL  = 4;  static const int SR_SPI        = 32;  static const int SR_ETHINT0    = 40;  static const int SR_ETHINT1    = 56; +static const int SR_RB_ADDR    = 128;  //led shifts for SR_LEDS  static const int LED_ACT1 = (1 << 5); @@ -49,11 +47,12 @@ static const int LED_LINKACT = (1 << 0);  static const int RB_COUNTER      = 0;  static const int RB_SPI_RDY      = 1;  static const int RB_SPI_DATA     = 2; -static const int RB_ETH_TYPE0    = 4; -static const int RB_ETH_TYPE1    = 5; +static const int RB_SFP0_TYPE    = 4; +static const int RB_SFP1_TYPE    = 5;  static const int RB_FPGA_COMPAT  = 6; -static const int RB_SFPP_STATUS0 = 8; -static const int RB_SFPP_STATUS1 = 9; +static const int RB_SFP0_STATUS  = 8; +static const int RB_SFP1_STATUS  = 9; +static const int RB_XBAR         = 128;  // Bootloader Memory Map  static const int BL_ADDRESS     = 0; @@ -73,4 +72,9 @@ static const int BL_DATA        = 1;  #define ETH_FRAMER_DST_UDP_MAC 6  #define ETH_FRAMER_DST_MAC_LO 7 +// SFP type constants +#define RB_SFP_1G_ETH   0 +#define RB_SFP_10G_ETH  1 +#define RB_SFP_AURORA   2 +  #endif /* INCLUDED_X300_DEFS_H */ diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c index ef97412a2..57c1faa2c 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> @@ -84,8 +84,8 @@ static void init_network(void)      wb_i2c_read(I2C1_BASE, MBOARD_EEPROM_ADDR, (uint8_t *)(&eeprom_map), sizeof(eeprom_map));      //determine interface number -    const size_t eth0no = wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE0))? 2 : 0; -    const size_t eth1no = wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE1))? 3 : 1; +    const size_t eth0no = wb_peek32(SR_ADDR(RB0_BASE, RB_SFP0_TYPE))? 2 : 0; +    const size_t eth1no = wb_peek32(SR_ADDR(RB0_BASE, RB_SFP1_TYPE))? 3 : 1;      //pick the address from eeprom or default      const eth_mac_addr_t *my_mac0 = (const eth_mac_addr_t *)pick_inited_field(&eeprom_map.mac_addr0, &default_map.mac_addr0, 6); @@ -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); @@ -151,19 +159,23 @@ void x300_init(void)      wb_poke32(SR_ADDR(SET0_BASE, SR_SW_RST), 0);      //print network summary -    for (uint8_t e = 0; e < ethernet_ninterfaces(); e++) +    for (uint8_t sfp = 0; sfp < ethernet_ninterfaces(); sfp++)      { -        uint32_t offset = SR_ADDR(RB0_BASE, ((e==1)?RB_ETH_TYPE1:RB_ETH_TYPE0)); -        UHD_FW_TRACE_FSTR(INFO, "Ethernet Port %u:", (int)e); -        UHD_FW_TRACE_FSTR(INFO, "-- PHY:    %s", ((wb_peek32(offset)==1) ? "10Gbps" : "1Gbps")); -        UHD_FW_TRACE_FSTR(INFO, "-- MAC:    %s", mac_addr_to_str(u3_net_stack_get_mac_addr(e))); -        UHD_FW_TRACE_FSTR(INFO, "-- IP:     %s", ip_addr_to_str(u3_net_stack_get_ip_addr(e))); -        UHD_FW_TRACE_FSTR(INFO, "-- SUBNET: %s", ip_addr_to_str(u3_net_stack_get_subnet(e))); -        UHD_FW_TRACE_FSTR(INFO, "-- BCAST:  %s", ip_addr_to_str(u3_net_stack_get_bcast(e))); +        uint32_t sfp_type = wb_peek32(SR_ADDR(RB0_BASE, ((sfp==1) ? RB_SFP1_TYPE : RB_SFP0_TYPE))); +        UHD_FW_TRACE_FSTR(INFO, "SFP+ Port %u:", (int)sfp); +        if (sfp_type == RB_SFP_AURORA) { +            UHD_FW_TRACE     (INFO, "-- PHY:    10Gbps Aurora"); +        } else { +            UHD_FW_TRACE_FSTR(INFO, "-- PHY:    %s", (sfp_type == RB_SFP_10G_ETH) ? "10Gbps Ethernet" : "1Gbps Ethernet"); +            UHD_FW_TRACE_FSTR(INFO, "-- MAC:    %s", mac_addr_to_str(u3_net_stack_get_mac_addr(sfp))); +            UHD_FW_TRACE_FSTR(INFO, "-- IP:     %s", ip_addr_to_str(u3_net_stack_get_ip_addr(sfp))); +            UHD_FW_TRACE_FSTR(INFO, "-- SUBNET: %s", ip_addr_to_str(u3_net_stack_get_subnet(sfp))); +            UHD_FW_TRACE_FSTR(INFO, "-- BCAST:  %s", ip_addr_to_str(u3_net_stack_get_bcast(sfp))); +        }      }      // 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..42ee3248b 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> @@ -247,18 +246,16 @@ static void handle_claim(void)  /***********************************************************************   * LED blinky logic and support utilities   **********************************************************************/ -static uint32_t get_xbar_total(const uint8_t port) +static uint32_t get_xbar_total(const uint32_t port)  { -    #define get_xbar_stat(in_prt, out_prt) \ -        wb_peek32(RB0_BASE+256+(((in_prt)*8+(out_prt))*4)) +    static const uint32_t NUM_PORTS = 16;      uint32_t total = 0; -    for (size_t i = 0; i < 8; i++) +    for (uint32_t i = 0; i < NUM_PORTS; i++)      { -        total += get_xbar_stat(port, i); -    } -    for (size_t i = 0; i < 8; i++) -    { -        total += get_xbar_stat(i, port); +        wb_poke32(SET0_BASE + SR_RB_ADDR*4, (NUM_PORTS*port + i)); +        total += wb_peek32(RB0_BASE + RB_XBAR*4); +        wb_poke32(SET0_BASE + SR_RB_ADDR*4, (NUM_PORTS*i + port)); +        total += wb_peek32(RB0_BASE + RB_XBAR*4);      }      if (port < 2) //also netstack if applicable      { @@ -280,10 +277,10 @@ static size_t popcntll(uint64_t num)  static void update_leds(void)  {      //update activity status for all ports -    uint64_t activity_shreg[8]; -    for (size_t i = 0; i < 8; i++) +    uint64_t activity_shreg[16]; +    for (uint32_t i = 0; i < 16; i++)      { -        static uint32_t last_total[8]; +        static uint32_t last_total[16];          const uint32_t total = get_xbar_total(i);          activity_shreg[i] <<= 1;          activity_shreg[i] |= (total == last_total[i])? 0 : 1; @@ -321,8 +318,10 @@ static void garp(void)      count = 0;      for (size_t e = 0; e < ethernet_ninterfaces(); e++)      { -        if (!ethernet_get_link_up(e)) continue; -        u3_net_stack_send_arp_request(e, u3_net_stack_get_ip_addr(e)); +        if (wb_peek32(SR_ADDR(RB0_BASE, e == 0 ? RB_SFP0_TYPE : RB_SFP1_TYPE)) != RB_SFP_AURORA) { +            if (!ethernet_get_link_up(e)) continue; +            u3_net_stack_send_arp_request(e, u3_net_stack_get_ip_addr(e)); +        }      }  } @@ -447,7 +446,7 @@ int main(void)          {              poll_sfpp_status(0); // Every so often poll XGE Phy to look for SFP+ hotplug events.              poll_sfpp_status(1); // Every so often poll XGE Phy to look for SFP+ hotplug events. -            handle_link_state(); //deal with router table update +            //handle_link_state(); //deal with router table update              handle_claim(); //deal with the host claim register              update_leds(); //run the link and activity leds              garp(); //send periodic garps | 
