diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2015-12-23 10:59:07 -0800 |
---|---|---|
committer | Ashish Chaudhari <ashish@ettus.com> | 2016-01-05 17:35:14 -0800 |
commit | e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4 (patch) | |
tree | 12d441822342db2e9e11b5cbe7c0fd156f0cba13 /firmware | |
parent | f45b4eb386a4c4cf5e7a463936fb4591d1bf8854 (diff) | |
download | uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.gz uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.bz2 uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.zip |
n230: Initial checkin of firmware files
Diffstat (limited to 'firmware')
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> |