diff options
Diffstat (limited to 'firmware/usrp3')
33 files changed, 3181 insertions, 94 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/fw_comm_protocol.h b/firmware/usrp3/include/fw_comm_protocol.h new file mode 100644 index 000000000..5f0fc0768 --- /dev/null +++ b/firmware/usrp3/include/fw_comm_protocol.h @@ -0,0 +1,99 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_FW_COMM_PROTOCOL +#define INCLUDED_FW_COMM_PROTOCOL + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +/*! + * Structs and constants for communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define FW_COMM_PROTOCOL_SIGNATURE  0xACE3 +#define FW_COMM_PROTOCOL_VERSION    0 +#define FW_COMM_MAX_DATA_WORDS      16 +#define FW_COMM_PROTOCOL_MTU        256 + +#define FW_COMM_FLAGS_ACK           0x00000001 +#define FW_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define FW_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define FW_COMM_CMD_ECHO            0x00000000 +#define FW_COMM_CMD_POKE32          0x00000010 +#define FW_COMM_CMD_PEEK32          0x00000020 +#define FW_COMM_CMD_BLOCK_POKE32    0x00000030 +#define FW_COMM_CMD_BLOCK_PEEK32    0x00000040 + +#define FW_COMM_ERR_PKT_ERROR       0x80000000 +#define FW_COMM_ERR_CMD_ERROR       0x40000000 +#define FW_COMM_ERR_SIZE_ERROR      0x20000000 + +#define FW_COMM_GENERATE_ID(prod)   ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0)  | \ +                                     (((uint32_t) prod)                       << 16) | \ +                                     (((uint32_t) FW_COMM_PROTOCOL_VERSION)   << 24)) +#define FW_COMM_GET_PRODUCT_ID(id)   ((uint8_t)(id >> 16)) +#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24)) + +typedef struct +{ +    uint32_t id;            //Protocol and device identifier +    uint32_t flags;         //Holds commands and ack messages +    uint32_t sequence;      //Sequence number (specific to FW communication transactions) +    uint32_t data_words;    //Number of data words in payload +    uint32_t addr;          //Address field for the command in flags +    uint32_t data[FW_COMM_MAX_DATA_WORDS];  //Data field for the command in flags +} fw_comm_pkt_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +typedef void (*poke32_func)(const uint32_t addr, const uint32_t data); +typedef uint32_t (*peek32_func)(const uint32_t addr); + +/*! + * Process a firmware communication packet and compute a response. + * Args: + * - (in) request: Pointer to the request struct + * - (out) response: Pointer to the response struct + * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering) + * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke + * - return value: Send a response packet + */ +bool process_fw_comm_protocol_pkt( +    const fw_comm_pkt_t* request, +    fw_comm_pkt_t* response, +    uint8_t product_id, +    poke32_func poke_callback, +    peek32_func peek_callback +); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_FW_COMM_PROTOCOL */ 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..df23eed83 --- /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 + */ +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 + */ +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. + */ +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. + */ +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. + */ +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. + */ +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. + */ +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 + */ +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..743aedf68 100644 --- a/firmware/usrp3/lib/ethernet.c +++ b/firmware/usrp3/lib/ethernet.c @@ -21,7 +21,7 @@  #endif  #include "../x300/x300_defs.h"  #include "ethernet.h" -#include "mdelay.h" +#include "cron.h"  #include <trace.h>  #include "wb_i2c.h"  #include "wb_utils.h" @@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)    int x;   // Delay read of SFPP    if (delay_ms) -    mdelay(delay_ms); +    sleep_ms(delay_ms);    // Read ID code from SFP    x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);    // I2C Error? 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..cf13e7d22 --- /dev/null +++ b/firmware/usrp3/lib/fw_comm_protocol.c @@ -0,0 +1,102 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <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, +    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: { +                //Do nothing. +                UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()"); +            } 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..e473b2b6c --- /dev/null +++ b/firmware/usrp3/n230/CMakeLists.txt @@ -0,0 +1,35 @@ +# +# 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 +# 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 0x3fff) + +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..3f6bb3fcf --- /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][0:-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..41772168b --- /dev/null +++ b/firmware/usrp3/n230/n230_debug.py @@ -0,0 +1,402 @@ +#!/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_BASE_ADDR        = 0x4000 +N230_FW_LOADER_CHUNK_SIZE       = 16 +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) +                    (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): +        self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +        self._sock.settimeout(UDP_TIMEOUT) +        self._sock.connect((addr, N230_FW_COMMS_UDP_PORT)) +        self.set_callbacks(lambda *a: None, lambda *a: None) + +    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 read_time_stats(self): +        print +        regs = ['    ingress1','    ingress2','     egress1','     egress1'] +        for reg in range (0, 4): +            print("%s " % regs[reg]), +            data = self.peek64(0xA000 + 32 + (reg*8), fmt='i') +            print("%10d    " % (data)), +            print("%10f uS" % (data * 0.0217)) +        print + +    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:] +            for i in range(0, len(coe_words), N230_FW_LOADER_CHUNK_SIZE): +                data_chunk = coe_words[i:i+(N230_FW_LOADER_CHUNK_SIZE)] +                data = [0] * N230_FW_COMMS_MAX_DATA_WORDS +                for j in range(len(data_chunk)): +                    data[j] = int(data_chunk[j], 16) +                out_pkt = pack_fw_command( +                    N230_FW_COMMS_CMD_BLOCK_POKE32|N230_FW_COMMS_FLAGS_ACK, +                    seq(), len(data_chunk), N230_FW_LOADER_BASE_ADDR+(i*4), data) +                in_pkt = self.send_and_recv(out_pkt) +                (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt) +                draw_progress_bar(((i+N230_FW_LOADER_CHUNK_SIZE)*100)/len(coe_words)) +            print("\nBooting...") +            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 "dirty" +        fw_hash = self.peek(N230_FW_HASH_ADDR) +        fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "dirty" +        print("FPGA Git Hash is: %x (%s)" % (fpga_hash & 0xfffffff, fpga_status)) +        print("Firmware Git Hash is: %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("--time", action="store_true",help="Display CHDR timestamp Stats", 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) +                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(addr=options.addr) +    +    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.time: +        ctrl_sock.read_time_stats() + +    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..116f87985 --- /dev/null +++ b/firmware/usrp3/n230/n230_eeprom.c @@ -0,0 +1,195 @@ +// +// 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_eeprom.h" + +#include <trace.h> +#include <stddef.h> +#include <flash/spi_flash.h> +#include <flash/spif_spsn_s25flxx.h> +#include <string.h> //memcpy +#include "n230_fw_defs.h" +#include "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_eeprom.h b/firmware/usrp3/n230/n230_eeprom.h new file mode 100644 index 000000000..71c7d53fd --- /dev/null +++ b/firmware/usrp3/n230/n230_eeprom.h @@ -0,0 +1,122 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_EEPROM_H +#define INCLUDED_N230_EEPROM_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define N230_NUM_ETH_PORTS 1 +#define N230_MAX_NUM_ETH_PORTS 2 + +#if (N230_NUM_ETH_PORTS > N230_MAX_NUM_ETH_PORTS) +#error +#endif + +#define N230_EEPROM_VER_MAJOR     1 +#define N230_EEPROM_VER_MINOR     0 +#define N230_EEPROM_SERIAL_LEN    9 +#define N230_EEPROM_NAME_LEN      32 + +typedef struct +{ +    uint8_t  mac_addr[6]; +    uint8_t  _pad[2]; +    uint32_t subnet; +    uint32_t ip_addr; +} n230_eth_eeprom_map_t; + +typedef struct +{ +    //Data format version +    uint16_t data_version_major; +    uint16_t data_version_minor; + +    //HW identification info +    uint16_t hw_revision; +    uint16_t hw_product; +    uint8_t serial[N230_EEPROM_SERIAL_LEN]; +    uint8_t _pad0[20 - N230_EEPROM_SERIAL_LEN]; + +    //Ethernet specific +    uint32_t gateway; +    n230_eth_eeprom_map_t eth_info[N230_MAX_NUM_ETH_PORTS]; + +    //User specific +    uint8_t user_name[N230_EEPROM_NAME_LEN]; +} n230_eeprom_map_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +/*! + * Read the eeprom and update caches. + * Returns true if read was successful. + * If the read was not successful then the cache is initialized with + * default values and marked as dirty. + */ +bool read_n230_eeprom(); + +/*! + * Write the contents of the cache to the eeprom. + * Returns true if write was successful. + */ +bool write_n230_eeprom(); + +/*! + * Returns the dirty state of the cache. + */ +bool is_n230_eeprom_cache_dirty(); + +/*! + * Returns a const pointer to the EEPROM map. + */ +const n230_eeprom_map_t* get_n230_const_eeprom_map(); + +/*! + * Returns the settings for the the 'iface'th ethernet interface + */ +const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface); + +/*! + * Returns a non-const pointer to the EEPROM map. Will mark the cache as dirty. + */ +n230_eeprom_map_t* get_n230_eeprom_map(); + +/*! + * FPGA Image operations + */ +inline void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes); + +inline bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes); + +inline bool erase_n230_fpga_image_sector(uint32_t offset); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_N230_EEPROM_H */ diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c new file mode 100644 index 000000000..b69c35eb0 --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.c @@ -0,0 +1,235 @@ +// +// 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 <fw_comm_protocol.h> +#include <wb_utils.h> +#include <string.h> //memcmp +#include <u3_net_stack.h> +#include <print_addrs.h> +#include <trace.h> +#include "../n230/n230_fw_host_iface.h" +#include "n230_eeprom.h" +#include "n230_fw_defs.h" + +static n230_host_shared_mem_t* host_shared_mem_ptr; + +/*********************************************************************** + * 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, +            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); +} + diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h new file mode 100644 index 000000000..271c3a6de --- /dev/null +++ b/firmware/usrp3/n230/n230_eth_handlers.h @@ -0,0 +1,42 @@ +// +// 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 "../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(); + +#endif /* INCLUDED_N230_ETH_HANDLERS_H */ diff --git a/firmware/usrp3/n230/n230_fw_defs.h b/firmware/usrp3/n230/n230_fw_defs.h new file mode 100644 index 000000000..f30c6efe0 --- /dev/null +++ b/firmware/usrp3/n230/n230_fw_defs.h @@ -0,0 +1,133 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FW_DEFS_H +#define INCLUDED_N230_FW_DEFS_H + +#include <stdint.h> + +/*! + * Constants specific to N230 firmware. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + * However, if it is included from within the host code, + * it will be namespaced appropriately + */ +#ifdef __cplusplus +namespace uhd { +namespace usrp { +namespace n230 { +namespace fw { +#endif + +static inline uint32_t reg_addr(uint32_t base, uint32_t offset) { +    return ((base) + (offset)*4); +} + +/******************************************************************* + * Global + *******************************************************************/ +static const uint32_t CPU_CLOCK_FREQ            = 80000000; +static const uint32_t PER_MILLISEC_CRON_JOBID   = 0; +static const uint32_t PER_SECOND_CRON_JOBID     = 1; + +/******************************************************************* + * Wishbone slave addresses + *******************************************************************/ +static const uint32_t WB_MAIN_RAM_BASE  = 0x0000; +static const uint32_t WB_PKT_RAM_BASE   = 0x8000; +static const uint32_t WB_SBRB_BASE      = 0xa000; +static const uint32_t WB_SPI_FLASH_BASE = 0xb000; +static const uint32_t WB_ETH0_MAC_BASE  = 0xc000; +static const uint32_t WB_ETH1_MAC_BASE  = 0xd000; +static const uint32_t WB_ETH0_I2C_BASE  = 0xf600; +static const uint32_t WB_ETH1_I2C_BASE  = 0xf700; +static const uint32_t WB_DBG_UART_BASE  = 0xf900; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_ZPU_SW_RST     = 0; +static const uint32_t SR_ZPU_BOOT_DONE  = 1; +static const uint32_t SR_ZPU_LEDS       = 2; +static const uint32_t SR_ZPU_SFP_CTRL0  = 16; +static const uint32_t SR_ZPU_SFP_CTRL1  = 17; +static const uint32_t SR_ZPU_ETHINT0    = 64; +static const uint32_t SR_ZPU_ETHINT1    = 80; + +static const uint32_t SR_ZPU_SW_RST_NONE    = 0x0; +static const uint32_t SR_ZPU_SW_RST_PHY     = 0x1; +static const uint32_t SR_ZPU_SW_RST_RADIO   = 0x2; + +/******************************************************************* + * Readback addresses + *******************************************************************/ +static const uint32_t RB_ZPU_COMPAT         = 0; +static const uint32_t RB_ZPU_COUNTER        = 1; +static const uint32_t RB_ZPU_SFP_STATUS0    = 2; +static const uint32_t RB_ZPU_SFP_STATUS1    = 3; + +/******************************************************************* + * Ethernet + *******************************************************************/ +static const uint32_t WB_PKT_RAM_CTRL_OFFSET    = 0x1FFC; + +static const uint32_t SR_ZPU_ETHINT_FRAMER_BASE     = 0; +static const uint32_t SR_ZPU_ETHINT_DISPATCHER_BASE = 8; + +//Eth framer constants +static const uint32_t ETH_FRAMER_SRC_MAC_HI     = 0; +static const uint32_t ETH_FRAMER_SRC_MAC_LO     = 1; +static const uint32_t ETH_FRAMER_SRC_IP_ADDR    = 2; +static const uint32_t ETH_FRAMER_SRC_UDP_PORT   = 3; +static const uint32_t ETH_FRAMER_DST_RAM_ADDR   = 4; +static const uint32_t ETH_FRAMER_DST_IP_ADDR    = 5; +static const uint32_t ETH_FRAMER_DST_UDP_MAC    = 6; +static const uint32_t ETH_FRAMER_DST_MAC_LO     = 7; + +/******************************************************************* + * CODEC + *******************************************************************/ +static const uint32_t CODEC_SPI_CLOCK_FREQ      = 4000000;  //4MHz +static const uint32_t ADF4001_SPI_CLOCK_FREQ    = 200000;   //200kHz + +/******************************************************************* + * UART + *******************************************************************/ +static const uint32_t DBG_UART_BAUD     = 115200; + +/******************************************************************* + * Build Compatability Numbers + *******************************************************************/ +static const uint8_t PRODUCT_NUM = 0x01; +static const uint8_t COMPAT_MAJOR = 0x00; +static const uint16_t COMPAT_MINOR = 0x0000; + +static inline uint8_t get_prod_num(uint32_t compat_reg) { +    return (compat_reg >> 24) & 0xFF; +} +static inline uint8_t get_compat_major(uint32_t compat_reg) { +    return (compat_reg >> 16) & 0xFF; +} +static inline uint8_t get_compat_minor(uint32_t compat_reg) { +    return compat_reg & 0xFFFF; +} + +#ifdef __cplusplus +}}}} //namespace +#endif +#endif /* INCLUDED_N230_FW_DEFS_H */ diff --git a/firmware/usrp3/n230/n230_fw_host_iface.h b/firmware/usrp3/n230/n230_fw_host_iface.h new file mode 100644 index 000000000..2d510bf1f --- /dev/null +++ b/firmware/usrp3/n230/n230_fw_host_iface.h @@ -0,0 +1,124 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FW_HOST_IFACE_H +#define INCLUDED_N230_FW_HOST_IFACE_H + +#include <stdint.h> + +/*! + * Structs and constants for N230 communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------- +// Ethernet related +// +#define N230_DEFAULT_ETH0_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff} +#define N230_DEFAULT_ETH1_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33} +#define N230_DEFAULT_ETH0_IP     (192 << 24 | 168 << 16 | 10  << 8  | 2 << 0) +#define N230_DEFAULT_ETH1_IP     (192 << 24 | 168 << 16 | 20  << 8  | 2 << 0) +#define N230_DEFAULT_ETH0_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_ETH1_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_GATEWAY     (192 << 24 | 168 << 16 | 10  << 8  | 1 << 0) + +#define N230_FW_COMMS_UDP_PORT        49152 +#define N230_FW_COMMS_CVITA_PORT      49153 +#define N230_FW_COMMS_FLASH_PROG_PORT 49154 +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Memory shared with host +// +#define N230_FW_HOST_SHMEM_BASE_ADDR      0x10000 +#define N230_FW_HOST_SHMEM_RW_BASE_ADDR   0x1000C +#define N230_FW_HOST_SHMEM_NUM_WORDS      (sizeof(n230_host_shared_mem_data_t)/sizeof(uint32_t)) + +#define N230_FW_HOST_SHMEM_MAX_ADDR  \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((N230_FW_HOST_SHMEM_NUM_WORDS - 1) * sizeof(uint32_t))) + +#define N230_FW_HOST_SHMEM_OFFSET(member) \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((uint32_t)offsetof(n230_host_shared_mem_data_t, member))) + +//The shared memory block can only be accessed on 32-bit boundaries +typedef struct {    //All fields must be 32-bit wide to avoid packing directives +    //Read-Only fields (N230_FW_HOST_SHMEM_BASE_ADDR) +    uint32_t fw_compat_num;     //Compat number must be at offset 0 +    uint32_t fw_version_hash; +    uint32_t claim_status; + +    //Read-Write fields (N230_FW_HOST_SHMEM_RW_BASE_ADDR) +    uint32_t scratch; +    uint32_t claim_time; +    uint32_t claim_src; +} n230_host_shared_mem_data_t; + +typedef union +{ +    uint32_t                    buff[N230_FW_HOST_SHMEM_NUM_WORDS]; +    n230_host_shared_mem_data_t   data; +} n230_host_shared_mem_t; + +#define N230_FW_PRODUCT_ID        1 +#define N230_FW_COMPAT_NUM_MAJOR  3 +#define N230_FW_COMPAT_NUM_MINOR  0 +#define N230_FW_COMPAT_NUM        (((N230_FW_COMPAT_NUM_MAJOR & 0xFF) << 16) | (N230_FW_COMPAT_NUM_MINOR & 0xFFFF)) +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Flash read-write interface for host +// +#define N230_FLASH_COMM_FLAGS_ACK           0x00000001 +#define N230_FLASH_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define N230_FLASH_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define N230_FLASH_COMM_CMD_READ_NV_DATA    0x00000010 +#define N230_FLASH_COMM_CMD_WRITE_NV_DATA   0x00000020 +#define N230_FLASH_COMM_CMD_READ_FPGA       0x00000030 +#define N230_FLASH_COMM_CMD_WRITE_FPGA      0x00000040 +#define N230_FLASH_COMM_CMD_ERASE_FPGA      0x00000050 + +#define N230_FLASH_COMM_ERR_PKT_ERROR       0x80000000 +#define N230_FLASH_COMM_ERR_CMD_ERROR       0x40000000 +#define N230_FLASH_COMM_ERR_SIZE_ERROR      0x20000000 + +#define N230_FLASH_COMM_MAX_PAYLOAD_SIZE    128 + +typedef struct +{ +    uint32_t flags; +    uint32_t seq; +    uint32_t offset; +    uint32_t size; +    uint8_t data[N230_FLASH_COMM_MAX_PAYLOAD_SIZE]; +} n230_flash_prog_t; +// +//-------------------------------------------------- + +#define N230_CLAIMER_TIMEOUT_IN_MS        2000 + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_N230_FW_HOST_IFACE_H */ diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c new file mode 100644 index 000000000..4602f8338 --- /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 "n230_eeprom.h" +#include "n230_fw_defs.h" +#include "n230_init.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..0dc44095e --- /dev/null +++ b/firmware/usrp3/n230/n230_main.c @@ -0,0 +1,114 @@ +// +// 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 "n230_eth_handlers.h" +#include "n230_fw_defs.h" +#include "n230_fw_host_iface.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; +static const soft_reg_field_t LED_REG_BLINKY_FIELD = {.num_bits=4, .shift=0}; + +//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 +            soft_reg_write(&g_led_register, LED_REG_BLINKY_FIELD, heart_beat); +            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(); +        } + +        //run the network stack - poll and handle +        u3_net_stack_handle_one(); +    } +    return 0; +} + +// Watchdog timer for claimer +static void n230_handle_claim() +{ +    static uint32_t last_time = 0; +    static size_t timeout = 0; + +    if (g_host_shared_mem.data.claim_time == 0) { +        //If time is 0 if the claim was forfeit +        g_host_shared_mem.data.claim_status = 0; +    } else if (last_time != g_host_shared_mem.data.claim_time) { +        //If the time changes, reset timeout +        g_host_shared_mem.data.claim_status = 1; +        timeout = 0; +    } else { +        //Otherwise increment for timeout +        timeout++; +    } + +    //Always stash the last seen time +    last_time = g_host_shared_mem.data.claim_time; + +    //Timeout logic +    if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) { +        g_host_shared_mem.data.claim_time = 0; +    } +} + diff --git a/firmware/usrp3/utils/git-hash.sh b/firmware/usrp3/utils/git-hash.sh new file mode 100755 index 000000000..ff7ae5ecb --- /dev/null +++ b/firmware/usrp3/utils/git-hash.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ $(command -v git) = "" ]]; then +    short_hash="FFFFFFFF" +else +    if (git diff --quiet); then +        #Clean +        short_hash="0$(git rev-parse --verify HEAD --short)" +    else +        #Dirty +        short_hash="F$(git rev-parse --verify HEAD --short)" +    fi +fi +echo ${short_hash^^}
\ No newline at end of file diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py index c9bcbb138..c518ba4e0 100755 --- a/firmware/usrp3/x300/x300_debug.py +++ b/firmware/usrp3/x300/x300_debug.py @@ -25,12 +25,18 @@ import struct  ########################################################################  # constants  ######################################################################## -B250_FW_COMMS_UDP_PORT = 49152 +X300_FW_COMMS_UDP_PORT = 49152 -B250_FW_COMMS_FLAGS_ACK = 1 -B250_FW_COMMS_FLAGS_ERROR = 2 -B250_FW_COMMS_FLAGS_POKE32 = 4 -B250_FW_COMMS_FLAGS_PEEK32 = 8 +X300_FW_COMMS_FLAGS_ACK         = 0x00000001 +X300_FW_COMMS_FLAGS_POKE32      = 0x00000010 +X300_FW_COMMS_FLAGS_PEEK32      = 0x00000020 + +X300_FW_COMMS_ERR_PKT_ERROR     = 0x80000000 +X300_FW_COMMS_ERR_CMD_ERROR     = 0x40000000 +X300_FW_COMMS_ERR_SIZE_ERROR    = 0x20000000 + +X300_FW_COMMS_ID = 0x0000ACE3 +X300_FW_COMMS_MAX_DATA_WORDS    = 16  #UDP_CTRL_PORT = 49183  UDP_MAX_XFER_BYTES = 1024 @@ -39,7 +45,7 @@ UDP_TIMEOUT = 3  #REG_ARGS_FMT = '!LLLLLB15x'  #REG_IP_FMT = '!LLLL20x' -REG_PEEK_POKE_FMT = '!LLLL' +REG_PEEK_POKE_FMT = '!LLLLL'  _seq = -1 @@ -52,12 +58,33 @@ def seq():  ########################################################################  # helper functions  ######################################################################## - -def unpack_reg_peek_poke_fmt(s): -    return struct.unpack(REG_PEEK_POKE_FMT,s) #(flags, seq, addr, data) +def fw_check_error(flags): +    if flags & X300_FW_COMMS_ERR_PKT_ERROR == X300_FW_COMMS_ERR_PKT_ERROR: +        raise Exception("The fiwmware operation returned a packet error") +    if flags & X300_FW_COMMS_ERR_CMD_ERROR == X300_FW_COMMS_ERR_CMD_ERROR: +        raise Exception("The fiwmware operation returned a command error") +    if flags & X300_FW_COMMS_ERR_SIZE_ERROR == X300_FW_COMMS_ERR_SIZE_ERROR: +        raise Exception("The fiwmware operation returned a size error")  def pack_reg_peek_poke_fmt(flags, seq, addr, data): -    return struct.pack(REG_PEEK_POKE_FMT, flags, seq, addr, data); +    num_words = 1 +    data_arr = [data] +    buf = bytes() +    buf = struct.pack('!LLLLL', X300_FW_COMMS_ID, flags, seq, num_words, addr) +    for i in range(X300_FW_COMMS_MAX_DATA_WORDS): +        if (i < num_words): +            buf += struct.pack('!L', data_arr[i]) +        else: +            buf += struct.pack('!L', 0) +    return buf + +def unpack_reg_peek_poke_fmt(buf): +    (id, flags, seq, num_words, addr) = struct.unpack_from('!LLLLL', buf) +    fw_check_error(flags) +    data = [] +    for i in xrange(20, len(buf), 4): +        data.append(struct.unpack('!L', buf[i:i+4])[0]) +    return (flags, seq, addr, data[0])  ########################################################################  # Burner class, holds a socket and send/recv routines @@ -66,7 +93,7 @@ class ctrl_socket(object):      def __init__(self, addr):          self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)          self._sock.settimeout(UDP_TIMEOUT) -        self._sock.connect((addr, B250_FW_COMMS_UDP_PORT)) +        self._sock.connect((addr, X300_FW_COMMS_UDP_PORT))          self.set_callbacks(lambda *a: None, lambda *a: None)          #self.init_update() #check that the device is there @@ -92,11 +119,10 @@ class ctrl_socket(object):          for in_prt in range (0, 8):              print("%s |" % ports[in_prt]),              for out_prt in range (0, 8): -                out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0) +                out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)                  in_pkt = self.send_and_recv(out_pkt)                  (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -                if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -                    raise Exception("B250 peek returns error code") +                fw_check_error(flags)                  print("%10d  " % (data)),              print          print @@ -105,19 +131,17 @@ class ctrl_socket(object):      def peek(self,peek_addr): -        out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0) +        out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)          in_pkt = self.send_and_recv(out_pkt)          (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -        if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -            raise Exception("B250 peek of address %d returns error code" % (addr)) +        fw_check_error(flags)          print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data))      def poke(self,poke_addr,poke_data): -        out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_POKE32|B250_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data) +        out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_POKE32|X300_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)          in_pkt = self.send_and_recv(out_pkt)          (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt) -        if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR: -            raise Exception("B250 peek of address %d returns error code" % (addr)) +        fw_check_error(flags)          print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data)  ) @@ -126,12 +150,12 @@ class ctrl_socket(object):  ########################################################################  def get_options():      parser = optparse.OptionParser() -    parser.add_option("--addr", type="string",                 help="USRP-N2XX device address",       default='') -    parser.add_option("--list", action="store_true",           help="list possible network devices", default=False) +    parser.add_option("--addr", type="string",              help="USRP-X300 device address",       default='') +    parser.add_option("--list", action="store_true",        help="list possible network devices", default=False)      parser.add_option("--peek", type="int",                 help="Read from memory map",     default=None)      parser.add_option("--poke", type="int",                 help="Write to memory map",     default=None)      parser.add_option("--data", type="int",                 help="Data for poke",     default=None) -    parser.add_option("--stats", action="store_true",           help="Display SuperMIMO Network Stats", default=False) +    parser.add_option("--stats", action="store_true",        help="Display SuperMIMO Network Stats", default=False)      (options, args) = parser.parse_args()      return options diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c index ef97412a2..97b20032b 100644 --- a/firmware/usrp3/x300/x300_init.c +++ b/firmware/usrp3/x300/x300_init.c @@ -1,7 +1,7 @@  #include "x300_init.h"  #include "x300_defs.h"  #include "ethernet.h" -#include "mdelay.h" +#include "cron.h"  #include <wb_utils.h>  #include <wb_uart.h>  #include <wb_i2c.h> @@ -121,6 +121,11 @@ static void putc(void *p, char c)  #endif  } +static uint32_t get_counter_val() +{ +    return wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)); +} +  void x300_init(void)  {      //first - uart @@ -136,6 +141,9 @@ void x300_init(void)      UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF));      UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000)); +    //Initialize cron +    cron_init(get_counter_val, CPU_CLOCK); +      //i2c rate init      wb_i2c_init(I2C0_BASE, CPU_CLOCK);      wb_i2c_init(I2C1_BASE, CPU_CLOCK); @@ -163,7 +171,7 @@ void x300_init(void)      }      // For eth interfaces, initialize the PHY's -    mdelay(100); +    sleep_ms(100);      ethernet_init(0);      ethernet_init(1);  } diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c index 3b812a2c4..44ed10aa8 100644 --- a/firmware/usrp3/x300/x300_main.c +++ b/firmware/usrp3/x300/x300_main.c @@ -6,7 +6,6 @@  #include "xge_phy.h"  #include "ethernet.h"  #include "chinch.h" -#include "mdelay.h"  #include <wb_utils.h>  #include <wb_uart.h> | 
