aboutsummaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/usrp3/CMakeLists.txt5
-rw-r--r--firmware/usrp3/include/cron.h75
-rw-r--r--firmware/usrp3/include/flash/spi_flash.h92
-rw-r--r--firmware/usrp3/include/flash/spif_spsn_s25flxx.h39
-rw-r--r--firmware/usrp3/include/mdelay.h29
-rw-r--r--firmware/usrp3/include/trace.h1
-rw-r--r--firmware/usrp3/include/wb_soft_reg.h135
-rw-r--r--firmware/usrp3/include/wb_spi.h55
-rw-r--r--firmware/usrp3/lib/CMakeLists.txt6
-rw-r--r--firmware/usrp3/lib/cron.c92
-rw-r--r--firmware/usrp3/lib/ethernet.c100
-rw-r--r--firmware/usrp3/lib/flash/spi_flash.c50
-rw-r--r--firmware/usrp3/lib/flash/spif_spsn_s25flxx.c238
-rw-r--r--firmware/usrp3/lib/fw_comm_protocol.c105
-rw-r--r--firmware/usrp3/lib/mdelay.c36
-rw-r--r--firmware/usrp3/lib/wb_spi.c206
-rw-r--r--firmware/usrp3/n230/CMakeLists.txt35
-rwxr-xr-xfirmware/usrp3/n230/n230_burner.py359
-rwxr-xr-xfirmware/usrp3/n230/n230_debug.py387
-rw-r--r--firmware/usrp3/n230/n230_eeprom.c196
-rw-r--r--firmware/usrp3/n230/n230_eth_handlers.c340
-rw-r--r--firmware/usrp3/n230/n230_eth_handlers.h48
-rw-r--r--firmware/usrp3/n230/n230_init.c125
-rw-r--r--firmware/usrp3/n230/n230_init.h28
-rw-r--r--firmware/usrp3/n230/n230_main.c113
-rwxr-xr-xfirmware/usrp3/utils/git-hash.sh14
-rwxr-xr-xfirmware/usrp3/x300/x300_aurora_bist.py307
-rwxr-xr-xfirmware/usrp3/x300/x300_debug.py130
-rw-r--r--firmware/usrp3/x300/x300_defs.h24
-rw-r--r--firmware/usrp3/x300/x300_init.c36
-rw-r--r--firmware/usrp3/x300/x300_main.c31
31 files changed, 3237 insertions, 200 deletions
diff --git a/firmware/usrp3/CMakeLists.txt b/firmware/usrp3/CMakeLists.txt
index f71b79b0c..66a43b6bd 100644
--- a/firmware/usrp3/CMakeLists.txt
+++ b/firmware/usrp3/CMakeLists.txt
@@ -25,6 +25,10 @@ SET(CMAKE_SYSTEM_NAME Generic)
CMAKE_FORCE_C_COMPILER(zpu-elf-gcc GNU)
PROJECT(USRP3_FW C)
+SET(UHD_VERSION_HASH 0 CACHE INTEGER "UHD Version Hash")
+EXECUTE_PROCESS(COMMAND ${CMAKE_SOURCE_DIR}/utils/git-hash.sh OUTPUT_VARIABLE UHD_VERSION_HASH)
+ADD_DEFINITIONS(-DUHD_VERSION_HASH=0x${UHD_VERSION_HASH})
+
INCLUDE_DIRECTORIES(include)
find_package(PythonInterp)
@@ -130,3 +134,4 @@ ENDMACRO(GEN_OUTPUTS)
########################################################################
ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(x300)
+ADD_SUBDIRECTORY(n230)
diff --git a/firmware/usrp3/include/cron.h b/firmware/usrp3/include/cron.h
new file mode 100644
index 000000000..2d43d97f5
--- /dev/null
+++ b/firmware/usrp3/include/cron.h
@@ -0,0 +1,75 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_CRON_H
+#define INCLUDED_CRON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define CRON_MAX_JOBS 4
+
+typedef enum {
+ SEC = 1, MILLISEC = 1000, MICROSEC = 1000000
+} cron_time_unit_t;
+
+typedef uint32_t (*cron_counter_fetcher_t)();
+
+/*!
+ * \brief Initialize cron subsystem with a mechanism to fetch a counter and its frequency
+ */
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq);
+
+/*!
+ * \brief Get the hardware tick count
+ */
+uint32_t cron_get_ticks();
+
+/*!
+ * \brief Get the time elapsed between start and stop in the specified units
+ */
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit);
+
+/*!
+ * \brief Sleep (spinloop) for about 'ticks' counter ticks
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_ticks(uint32_t ticks);
+
+/*!
+ * \brief Sleep (spinloop) for about 'duration' microseconds
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_us(uint32_t duration);
+
+/*!
+ * \brief Sleep (spinloop) for about 'duration' milliseconds
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_ms(uint32_t duration);
+
+/*!
+ * \brief Initialize a unique cron job with 'job_id' and interval 'interval_ms'
+ */
+void cron_job_init(uint32_t job_id, uint32_t interval_ms);
+
+/*!
+ * \brief Check if cron job with 'job_id' is due for execution
+ */
+bool cron_job_run_due(uint32_t job_id);
+
+#endif /* INCLUDED_CRON_H */
diff --git a/firmware/usrp3/include/flash/spi_flash.h b/firmware/usrp3/include/flash/spi_flash.h
new file mode 100644
index 000000000..8ed73f648
--- /dev/null
+++ b/firmware/usrp3/include/flash/spi_flash.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_SPI_FLASH
+#define INCLUDED_SPI_FLASH
+
+#include <wb_spi.h>
+
+//Device class that encapsulates the geometry and control
+//interface for the flash chip
+typedef struct {
+ uint32_t page_size; //in bytes
+ uint32_t sector_size; //in bytes
+ uint32_t num_sectors;
+ const wb_spi_slave_t* bus;
+} spi_flash_dev_t;
+
+//Low level device specific operations
+typedef uint16_t (*spif_read_id_fn_t)(const spi_flash_dev_t* flash);
+typedef void (*spif_read_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+typedef bool (*spif_erase_sector_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset);
+typedef bool (*spif_erase_sector_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset);
+typedef bool (*spif_erase_sector_busy_fn_t)(const spi_flash_dev_t* flash);
+typedef bool (*spif_write_page_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+typedef bool (*spif_write_page_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+typedef bool (*spif_write_page_busy_fn_t)(const spi_flash_dev_t* flash);
+
+//Interface struct for all low level device operations
+typedef struct {
+ spif_read_id_fn_t read_id;
+ spif_read_fn_t read;
+ spif_erase_sector_dispatch_fn_t erase_sector_dispatch;
+ spif_erase_sector_commit_fn_t erase_sector_commit;
+ spif_erase_sector_busy_fn_t erase_sector_busy;
+ spif_write_page_dispatch_fn_t write_page_dispatch;
+ spif_write_page_commit_fn_t write_page_commit;
+ spif_write_page_busy_fn_t write_page_busy;
+} spi_flash_ops_t;
+
+typedef enum {
+ IDLE, WRITE_IN_PROGRESS, ERASE_IN_PROGRESS
+} spi_flash_state_t;
+
+//A session struct that encapsulates everything about the flash
+//in a device agnostic way
+typedef struct {
+ const spi_flash_dev_t* device;
+ const spi_flash_ops_t* ops;
+ spi_flash_state_t state;
+ uint32_t last_offset;
+ uint16_t id;
+} spi_flash_session_t;
+
+/*!
+ * Initialize the spi_flash_session_t object
+ */
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops);
+
+/*!
+ * Read "num_bytes" from "offset" in the flash into the buffer "buf".
+ * This call will block until all data is available.
+ */
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+
+/*!
+ * Erase sector at "offset" in the flash.
+ * This call will block until the erase is complete.
+ */
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset);
+
+/*!
+ * Write "num_bytes" from buffer "buf" at "offset" in the flash.
+ * This call will block until the write is complete.
+ */
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+
+#endif /* INCLUDED_SPI_FLASH */
diff --git a/firmware/usrp3/include/flash/spif_spsn_s25flxx.h b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h
new file mode 100644
index 000000000..1e6eededf
--- /dev/null
+++ b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_SPIF_SPSN_S25FLXX_H
+#define INCLUDED_SPIF_SPSN_S25FLXX_H
+
+#include <flash/spi_flash.h>
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations();
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash);
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset);
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset);
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash);
+
+#endif /* INCLUDED_SPIF_SPSN_S25FLXX_H */
diff --git a/firmware/usrp3/include/mdelay.h b/firmware/usrp3/include/mdelay.h
deleted file mode 100644
index 226bbb3f7..000000000
--- a/firmware/usrp3/include/mdelay.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef INCLUDED_MDELAY_H
-#define INCLUDED_MDELAY_H
-
-/*!
- * \brief Delay about ms milliseconds
- *
- * If simulating, _very_ short delay
- */
-void mdelay(int ms);
-
-#endif /* INCLUDED_MDELAY_H */
diff --git a/firmware/usrp3/include/trace.h b/firmware/usrp3/include/trace.h
index 0daa231fe..015ae9049 100644
--- a/firmware/usrp3/include/trace.h
+++ b/firmware/usrp3/include/trace.h
@@ -31,6 +31,7 @@
* An alternate way of defining the level is the "TRACE_LEVEL"
* variable in cmake. (eg. -DTRACE_LEVEL=13).
*/
+
//#define UHD_FW_TRACE_LEVEL 13
typedef enum
diff --git a/firmware/usrp3/include/wb_soft_reg.h b/firmware/usrp3/include/wb_soft_reg.h
new file mode 100644
index 000000000..cbfc311bb
--- /dev/null
+++ b/firmware/usrp3/include/wb_soft_reg.h
@@ -0,0 +1,135 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_SOFT_REG_H
+#define INCLUDED_SOFT_REG_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <wb_utils.h>
+
+/* Keeps track of all metadata associated with a soft register.
+ * Use this struct when you need to manage a hardware register that needs
+ * to be accessed from different sections of software. If a register contains
+ * several unrelated bitfields, this object can be used to ensure coherency.
+ * It is recommended that the client hold this as a global/static object.
+ */
+typedef struct
+{
+ uint32_t wr_addr;
+ uint32_t rd_addr;
+ uint32_t soft_copy;
+} soft_reg_t;
+
+/* A register field is defined as a tuple of the mask and the shift.
+ * It can be used to make read-modify-write operations more convenient
+ * For efficiency reasons, it is recommended to always use a constant
+ * of this type because it will get optimized out by the compiler and
+ * will result in zero memory overhead
+ */
+typedef struct
+{
+ uint8_t num_bits;
+ uint8_t shift;
+} soft_reg_field_t;
+
+
+/*!
+ * Initialize the soft_reg_t struct as a read-write register.
+ * Params:
+ * - reg: Pointer to the soft_reg struct
+ * - wr_addr: The address used to flush the register to HW
+ * - rd_addr: The address used to read the register from HW
+ */
+static inline void initialize_readwrite_soft_reg(soft_reg_t* reg, uint32_t wr_addr, uint32_t rd_addr)
+{
+ reg->wr_addr = wr_addr;
+ reg->rd_addr = rd_addr;
+ reg->soft_copy = 0;
+}
+
+/*!
+ * Initialize the soft_reg_t struct as a write-only register.
+ * Params:
+ * - reg: Pointer to the soft_reg struct
+ * - addr: The address used to flush the register to HW
+ */
+static inline void initialize_writeonly_soft_reg(soft_reg_t* reg, uint32_t addr)
+{
+ reg->wr_addr = addr;
+ reg->rd_addr = 0;
+ reg->soft_copy = 0;
+}
+
+/*!
+ * Update specified field in the soft-copy with the arg value.
+ * Performs a read-modify-write operation so all other field are preserved.
+ * NOTE: This does not write the value to hardware.
+ */
+static inline void soft_reg_set(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value)
+{
+ const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift;
+ reg->soft_copy = (reg->soft_copy & ~mask) | ((field_value << field.shift) & mask);
+}
+
+/*!
+ * Write the contents of the soft-copy to hardware.
+ */
+static inline void soft_reg_flush(const soft_reg_t* reg)
+{
+ wb_poke32(reg->wr_addr, reg->soft_copy);
+}
+
+/*!
+ * Shortcut for a set and a flush.
+ */
+static inline void soft_reg_write(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value)
+{
+ soft_reg_set(reg, field, field_value);
+ soft_reg_flush(reg);
+}
+
+/*!
+ * Get the value of the specified field from the soft-copy.
+ * NOTE: This does not read anything from hardware.
+ */
+static inline uint32_t soft_reg_get(const soft_reg_t* reg, const soft_reg_field_t field)
+{
+ const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift;
+ return (reg->soft_copy & mask) >> field.shift;
+}
+
+/*!
+ * Read the contents of the register from hardware and update the soft copy.
+ */
+static inline void soft_reg_refresh(soft_reg_t* reg)
+{
+ if (reg->rd_addr) {
+ reg->soft_copy = wb_peek32(reg->rd_addr);
+ }
+}
+
+/*!
+ * Shortcut for refresh and get
+ */
+static inline uint32_t soft_reg_read(soft_reg_t* reg, const soft_reg_field_t field)
+{
+ soft_reg_refresh(reg);
+ return soft_reg_get(reg, field);
+}
+
+#endif /* INCLUDED_SOFT_REG_H */
diff --git a/firmware/usrp3/include/wb_spi.h b/firmware/usrp3/include/wb_spi.h
new file mode 100644
index 000000000..ebbb20b16
--- /dev/null
+++ b/firmware/usrp3/include/wb_spi.h
@@ -0,0 +1,55 @@
+
+// Copyright 2012 Ettus Research LLC
+
+#ifndef INCLUDED_WB_SPI_H
+#define INCLUDED_WB_SPI_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+typedef enum {
+ WRITE, WRITE_READ
+} wb_spi_rw_mode_t;
+
+typedef enum {
+ RISING, FALLING
+} wb_spi_edge_t;
+
+typedef struct {
+ void* base;
+ uint32_t slave_sel;
+ uint32_t clk_div;
+ wb_spi_edge_t mosi_edge;
+ wb_spi_edge_t miso_edge;
+ bool lsb_first;
+} wb_spi_slave_t;
+
+/*!
+ * \brief Initialize SPI slave device
+ */
+void wb_spi_init(const wb_spi_slave_t* slave);
+
+/*!
+ * \brief Perform a SPI transaction in auto chip-select mode.
+ */
+inline void wb_spi_transact(const wb_spi_slave_t* slave,
+ wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length);
+
+/*!
+ * \brief Perform a SPI transaction in manual chip-select mode.
+ */
+inline void wb_spi_transact_man_ss(const wb_spi_slave_t* slave,
+ wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length);
+
+/*!
+ * \brief Select SPI slave
+ */
+void wb_spi_slave_select(const wb_spi_slave_t* slave);
+
+/*!
+ * \brief Deselect SPI slave
+ */
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave);
+
+#endif /* INCLUDED_WB_SPI_H */
diff --git a/firmware/usrp3/lib/CMakeLists.txt b/firmware/usrp3/lib/CMakeLists.txt
index 621b9b611..9d9ee3c6c 100644
--- a/firmware/usrp3/lib/CMakeLists.txt
+++ b/firmware/usrp3/lib/CMakeLists.txt
@@ -21,12 +21,16 @@ add_library(usrp3fw STATIC
udp_uart.c
wb_uart.c
wb_i2c.c
+ wb_spi.c
printf.c
wb_pkt_iface64.c
u3_net_stack.c
ethernet.c
- mdelay.c
chinch.c
print_addrs.c
link_state_route_proto.c
+ cron.c
+ fw_comm_protocol.c
+ flash/spi_flash.c
+ flash/spif_spsn_s25flxx.c
)
diff --git a/firmware/usrp3/lib/cron.c b/firmware/usrp3/lib/cron.c
new file mode 100644
index 000000000..24b8feb4e
--- /dev/null
+++ b/firmware/usrp3/lib/cron.c
@@ -0,0 +1,92 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "cron.h"
+
+//Counter specific
+static cron_counter_fetcher_t cron_fetch_counter;
+static uint32_t cron_counter_freq;
+
+//Cron job specific
+typedef struct {
+ uint32_t tick_interval;
+ uint32_t last_tick_count;
+} cron_job_t;
+
+static cron_job_t cron_job_table[CRON_MAX_JOBS];
+
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq)
+{
+ cron_fetch_counter = fetch_counter;
+ cron_counter_freq = counter_freq;
+
+ for (int i = 0; i < CRON_MAX_JOBS; i++) {
+ cron_job_table[i].tick_interval = 0;
+ }
+}
+
+uint32_t cron_get_ticks()
+{
+ return cron_fetch_counter();
+}
+
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit)
+{
+ return ((stop_ticks - start_ticks) / cron_counter_freq) * ((uint32_t)unit);
+}
+
+void sleep_ticks(uint32_t ticks)
+{
+ if (ticks == 0) return; //Handle the 0 delay case quickly
+
+ const uint32_t ticks_begin = cron_fetch_counter();
+ while(cron_fetch_counter() - ticks_begin < ticks) {
+ /*NOP: Spinloop*/
+ }
+}
+
+void sleep_us(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000000)));
+}
+
+void sleep_ms(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000)));
+}
+
+void cron_job_init(uint32_t job_id, uint32_t interval_ms)
+{
+ cron_job_table[job_id].tick_interval = (interval_ms * (cron_counter_freq/1000));
+ cron_job_table[job_id].last_tick_count = 0;
+}
+
+bool cron_job_run_due(uint32_t job_id)
+{
+ uint32_t new_tick_count = cron_fetch_counter();
+ bool run_job = (new_tick_count - cron_job_table[job_id].last_tick_count) >=
+ cron_job_table[job_id].tick_interval;
+
+ if (run_job) {
+ //If the job is due to run, update the tick count for the next run
+ //The assumption here is that the caller will actually run their job
+ //when the return value is true. If not, the caller just missed this
+ //iteration and will have to option to run the job in the next pass through.
+ cron_job_table[job_id].last_tick_count = new_tick_count;
+ }
+ return run_job;
+}
diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c
index 91efbfe1d..e5226489f 100644
--- a/firmware/usrp3/lib/ethernet.c
+++ b/firmware/usrp3/lib/ethernet.c
@@ -21,7 +21,7 @@
#endif
#include "../x300/x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <trace.h>
#include "wb_i2c.h"
#include "wb_utils.h"
@@ -161,8 +161,8 @@ ge_write_mdio(const uint32_t base, const uint32_t address, const uint32_t port,
static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port)
{
- const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
- const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE;
+ const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;
if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
{
return xge_read_mdio(base, address, device, port);
@@ -175,8 +175,8 @@ static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint3
static void write_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port, const uint32_t data)
{
- const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
- const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE;
+ const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;
if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
{
return xge_write_mdio(base, address, device, port, data);
@@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
int x;
// Delay read of SFPP
if (delay_ms)
- mdelay(delay_ms);
+ sleep_ms(delay_ms);
// Read ID code from SFP
x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
// I2C Error?
@@ -312,10 +312,9 @@ static void xge_mac_init(const uint32_t base)
}
// base is pointer to XGE MAC on Wishbone.
-static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
+static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port)
{
int x;
- uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg;
// Read LASI Ctrl register to capture state.
//y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);
UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence.");
@@ -323,23 +322,24 @@ static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);
x = x | (1 << 15);
write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x);
+ uint32_t loopCount = 0;
while(x&(1<<15)) {
x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port);
+ if( loopCount++ > 200 ) break; // usually succeeds after 22 or 23 polls
}
}
-void update_eth_state(const uint32_t eth)
+void update_eth_state(const uint32_t eth, const uint32_t sfp_type)
{
const bool old_link_up = links_up[eth];
- const uint32_t status_reg_addr = (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1;
- const bool is_10g = (wb_peek32(SR_ADDR(RB0_BASE, eth == 0 ? RB_ETH_TYPE0 : RB_ETH_TYPE1)) == 1);
+ const uint32_t status_reg_addr = (eth==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS;
uint32_t sfpp_status = wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) & 0xFFFF;
if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
//SFP+ pin state changed. Reinitialize PHY and MAC
- if (is_10g) {
- xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE);
- xge_phy_init(eth ,MDIO_PORT);
+ if (sfp_type == RB_SFP_10G_ETH) {
+ xge_mac_init((eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE);
+ xge_phy_init(eth, MDIO_PORT);
} else {
//No-op for 1G
}
@@ -347,7 +347,7 @@ void update_eth_state(const uint32_t eth)
int8_t timeout = 100;
bool link_up = false;
do {
- if (is_10g) {
+ if (sfp_type == RB_SFP_10G_ETH) {
link_up = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0;
} else {
link_up = ((wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) >> 16) & 0x1) != 0;
@@ -362,54 +362,66 @@ void update_eth_state(const uint32_t eth)
}
if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
- UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down");
}
-void poll_sfpp_status(const uint32_t eth)
+void poll_sfpp_status(const uint32_t sfp)
{
- uint32_t x;
- // Has MODDET/MODAbS changed since we last looked?
- x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
+ uint32_t type = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE));
+ uint32_t status = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS));
- if (x & SFPP_STATUS_RXLOS_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (x & SFPP_STATUS_RXLOS));
- if (x & SFPP_STATUS_TXFAULT_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((x & SFPP_STATUS_TXFAULT) >> 1));
- if (x & SFPP_STATUS_MODABS_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((x & SFPP_STATUS_MODABS) >> 2));
-
- //update the link up status
- if ((x & SFPP_STATUS_RXLOS_CHG) || (x & SFPP_STATUS_TXFAULT_CHG) || (x & SFPP_STATUS_MODABS_CHG))
- {
- update_eth_state(eth);
- }
-
- if (x & SFPP_STATUS_MODABS_CHG) {
+ if (status & SFPP_STATUS_MODABS_CHG) {
// MODDET has changed state since last checked
- if (x & SFPP_STATUS_MODABS) {
+ if (status & SFPP_STATUS_MODABS) {
// MODDET is high, module currently removed.
- UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth);
+ UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", sfp);
} else {
// MODDET is low, module currently inserted.
// Return status.
- UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth);
- xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1);
+ UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", sfp);
+ if (type == RB_SFP_10G_ETH) {
+ xge_read_sfpp_type((sfp==0) ? I2C0_BASE : I2C2_BASE,1);
+ }
}
}
+
+ if (status & SFPP_STATUS_RXLOS_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d RXLOS changed state: %d", sfp, (status & SFPP_STATUS_RXLOS));
+ }
+ if (status & SFPP_STATUS_TXFAULT_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d TXFAULT changed state: %d", sfp, ((status & SFPP_STATUS_TXFAULT) >> 1));
+ }
+ if (status & SFPP_STATUS_MODABS_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d MODABS changed state: %d", sfp, ((status & SFPP_STATUS_MODABS) >> 2));
+ }
+
+ //update the link up status
+ const bool old_link_up = links_up[sfp];
+ if (type == RB_SFP_AURORA) {
+ links_up[sfp] = ((wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS)) >> 16) & 0x1) != 0;
+ } else {
+ if ((status & SFPP_STATUS_RXLOS_CHG) ||
+ (status & SFPP_STATUS_TXFAULT_CHG) ||
+ (status & SFPP_STATUS_MODABS_CHG))
+ {
+ update_eth_state(sfp, type);
+ }
+ }
+ if (old_link_up != links_up[sfp]) {
+ UHD_FW_TRACE_FSTR(INFO, "The link on SFP port %u is %s", sfp, links_up[sfp]?"up":"down");
+ }
}
-void ethernet_init(const uint32_t eth)
+void ethernet_init(const uint32_t sfp)
{
#ifdef UHD_FW_TRACE_LEVEL
- uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d",
- eth,
+ uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS ));
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d",
+ sfp,
(x & SFPP_STATUS_RXLOS),
((x & SFPP_STATUS_TXFAULT) >> 1),
((x & SFPP_STATUS_MODABS) >> 2));
#endif
- links_up[eth] = false;
- update_eth_state(eth);
+ update_eth_state(sfp, wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE)));
}
//
diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c
new file mode 100644
index 000000000..b4257c96f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spi_flash.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <flash/spi_flash.h>
+
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops)
+{
+ flash->device = device;
+ flash->ops = ops;
+ flash->state = IDLE;
+ flash->last_offset = 0;
+ flash->id = ops->read_id(device);
+}
+
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ flash->ops->read(flash->device, offset, buf, num_bytes);
+}
+
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset)
+{
+ if (flash->ops->erase_sector_dispatch(flash->device, offset)) {
+ return flash->ops->erase_sector_commit(flash->device, offset);
+ } else {
+ return false;
+ }
+}
+
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) {
+ return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes);
+ } else {
+ return false;
+ }
+}
diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
new file mode 100644
index 000000000..244115b6f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <cron.h>
+#include <trace.h>
+#include <string.h> //for memset, memcpy
+
+#define S25FLXX_CMD_WIDTH 8
+#define S25FLXX_ADDR_WIDTH 24
+
+/* S25FLxx-specific commands */
+#define S25FLXX_CMD_READID 0x90 /* Read Manufacturer and Device Identification */
+#define S25FLXX_CMD_READSIG 0xAB /* Read Electronic Signature (Will release from Deep PD) */
+#define S25FLXX_CMD_READ 0x03 /* Read Data Bytes */
+#define S25FLXX_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */
+
+#define S25FLXX_CMD_WREN 0x06 /* Write Enable */
+#define S25FLXX_CMD_WRDI 0x04 /* Write Disable */
+
+#define S25FLXX_CMD_PP 0x02 /* Page Program */
+#define S25FLXX_CMD_SE 0xD8 /* Sector Erase */
+#define S25FLXX_CMD_BE 0xC7 /* Bulk Erase */
+#define S25FLXX_CMD_DP 0xB9 /* Deep Power-down */
+
+#define S25FLXX_CMD_RDSR 0x05 /* Read Status Register */
+#define S25FLXX_CMD_WRSR 0x01 /* Write Status Register */
+
+#define S25FLXX_STATUS_WIP 0x01 /* Write in Progress */
+#define S25FLXX_STATUS_E_ERR 0x20 /* Erase Error Occured */
+#define S25FLXX_STATUS_P_ERR 0x40 /* Programming Error Occured */
+
+#define S25FLXX_SECTOR_ERASE_TIME_MS 750 //Spec: 650ms
+#define S25FLXX_PAGE_WRITE_TIME_MS 1 //Spec: 750us
+
+#define S25FLXX_SMALL_SECTORS_PER_LOGICAL 16 //16 4-kB physical sectors per logical sector
+#define S25FLXX_LARGE_SECTOR_BASE 0x20000 //Large physical sectors start at logical sector 2
+
+inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash)
+{
+ uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF;
+ wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */);
+ return status;
+}
+
+inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms)
+{
+ uint32_t start_ticks = cron_get_ticks();
+ do {
+ if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) {
+ return true;
+ }
+ } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms);
+
+ return false; // Timed out
+}
+
+inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled)
+{
+ uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI;
+ wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH);
+}
+
+const spi_flash_ops_t spif_spsn_s25flxx_ops =
+{
+ .read_id = spif_spsn_s25flxx_read_id,
+ .read = spif_spsn_s25flxx_read,
+ .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch,
+ .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit,
+ .erase_sector_busy = spif_spsn_s25flxx_device_busy,
+ .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch,
+ .write_page_commit = spif_spsn_s25flxx_write_page_commit,
+ .write_page_busy = spif_spsn_s25flxx_device_busy
+};
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations()
+{
+ return &spif_spsn_s25flxx_ops;
+}
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash)
+{
+ wb_spi_slave_select(flash->bus);
+ uint32_t command = S25FLXX_CMD_READID << 24;
+ wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32);
+ uint16_t id = 0;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16);
+ wb_spi_slave_deselect(flash->bus);
+ return id;
+}
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 5 byte instruction tranfer:
+ //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0)
+ uint8_t read_cmd[5];
+ read_cmd[4] = S25FLXX_CMD_FAST_READ;
+ *((uint32_t*)(read_cmd + 3)) = (offset << 8);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8);
+
+ //Read up to 4 bytes at a time until done
+ uint8_t data_sw[16], data[16];
+ size_t xact_size = 16;
+ unsigned char *bytes = (unsigned char *) buf;
+ for (size_t i = 0; i < num_bytes; i += 16) {
+ if (xact_size > num_bytes - i) xact_size = num_bytes - i;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8);
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k];
+ }
+ for (size_t j = 0; j < xact_size; j++) {
+ *bytes = data[j];
+ bytes++;
+ }
+ }
+ wb_spi_slave_deselect(flash->bus);
+}
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Sanity check sector size
+ if (offset % flash->sector_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size.");
+ return false;
+ }
+
+ if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //Send sector erase command
+ uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF);
+ wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Poll status until write done
+ uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1;
+ bool status = false;
+ for (uint8_t i = 0; i < phy_sector_count && !status; i++) {
+ status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS);
+ }
+ if (!status) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset);
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return status;
+}
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (num_bytes == 0 || num_bytes > flash->page_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size.");
+ return false;
+ }
+ if (num_bytes > (flash->sector_size * flash->num_sectors)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary.");
+ return false;
+ }
+
+ //Wait until ready and enable write enabled
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 4 byte instruction tranfer:
+ //PP_CMD, ADDR2, ADDR1, ADDR0
+ uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32);
+
+ //Write the page 16 bytes at a time.
+ uint8_t bytes_sw[16];
+ uint8_t* bytes = (uint8_t*) buf;
+ for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) {
+ const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16;
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k];
+ }
+ wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8);
+ bytes += xact_size;
+ }
+ wb_spi_slave_deselect(flash->bus);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ //Wait until write done
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time.");
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return true;
+}
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash)
+{
+ return (_spif_read_status(flash) & S25FLXX_STATUS_WIP);
+}
+
diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c
new file mode 100644
index 000000000..0cc931a76
--- /dev/null
+++ b/firmware/usrp3/lib/fw_comm_protocol.c
@@ -0,0 +1,105 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+
+#include <trace.h>
+#include <string.h> //memcmp
+
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ uint32_t iface_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback)
+{
+ bool send_response = false;
+
+ uint16_t signature = request->id;
+ uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
+ uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
+ if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
+ version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
+ product == product_id) //Verify device
+ {
+ //Request is valid. Copy it into the reply.
+ memcpy(response, request, sizeof(fw_comm_pkt_t));
+
+ //Start assuming no error
+ response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
+
+ //Otherwise, run the command set by the flags
+ switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
+ case FW_COMM_CMD_ECHO: {
+ UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
+ response->data_words = 1;
+ response->data[0] = iface_id;
+ } break;
+
+ case FW_COMM_CMD_POKE32: {
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
+ request->addr,*(request->data));
+ poke_callback(request->addr, *(request->data));
+ } break;
+
+ case FW_COMM_CMD_PEEK32: {
+ *(response->data) = peek_callback(request->addr);
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
+ request->addr,*(response->data));
+ } break;
+
+ case FW_COMM_CMD_BLOCK_POKE32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
+ }
+ } break;
+
+ case FW_COMM_CMD_BLOCK_PEEK32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
+ } break;
+
+ default: {
+ UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
+ response->flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+
+ //Send a reply if ack requested
+ send_response = (request->flags & FW_COMM_FLAGS_ACK);
+ } else { //Size, protocol, product check failed
+ UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
+ send_response = false;
+ }
+ return send_response;
+}
diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c
deleted file mode 100644
index 6d2742206..000000000
--- a/firmware/usrp3/lib/mdelay.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- * Copyright 2009 Ettus Research LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mdelay.h"
-#include "wb_utils.h"
-#include "printf.h"
-#include <stdint.h>
-//IJB FIXME.
-#include "../x300/x300_defs.h"
-
-void mdelay(int ms){
- for(int i = 0; i < ms; i++){
- static const uint32_t num_ticks = CPU_CLOCK/1000;
- const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
- // printf("DEBUG: Counter is %d\n",ticks_begin);
- while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
- /*NOP*/
- }
- }
-}
diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c
new file mode 100644
index 000000000..04904feea
--- /dev/null
+++ b/firmware/usrp3/lib/wb_spi.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <trace.h>
+
+typedef struct {
+ volatile uint32_t data0;
+ volatile uint32_t data1;
+ volatile uint32_t data2;
+ volatile uint32_t data3;
+ volatile uint32_t ctrl_status;
+ volatile uint32_t clkdiv;
+ volatile uint32_t slavesel;
+} wb_spi_regs_t;
+
+#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base)
+
+// Masks for different parts of CTRL reg
+#define WB_SPI_CTRL_AUTO_SS (1 << 13)
+#define WB_SPI_CTRL_IE (1 << 12)
+#define WB_SPI_CTRL_LSB (1 << 11)
+#define WB_SPI_CTRL_TXNEG (1 << 10)
+#define WB_SPI_CTRL_RXNEG (1 << 9)
+#define WB_SPI_CTRL_GO_BSY (1 << 8)
+#define WB_SPI_CTRL_LENGTH(x) (x & 0x7F)
+
+static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave)
+{
+ uint32_t flags = 0;
+ //If the SPI slave samples on the rising edge then shift
+ //data out on the falling edge.
+ if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG;
+ //If the SPI slave drives on the rising edge then shift
+ //data in on the falling edge.
+ if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG;
+ if (slave->lsb_first) flags |= WB_SPI_CTRL_LSB;
+ return flags;
+}
+
+static inline void _wait_for_xfer(const wb_spi_slave_t* slave)
+{
+ while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) {
+ /*NOP*/
+ }
+}
+
+void wb_spi_init(const wb_spi_slave_t* slave)
+{
+ WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div;
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+
+ //Do a dummy transaction with no slave selected to prime the engine
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave);
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+ _wait_for_xfer(slave);
+}
+
+void _wb_spi_transact_buf(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length,
+ bool auto_slave_sel)
+{
+ if (length == 0) return;
+
+ //Wait for previous transaction to finish
+ _wait_for_xfer(slave);
+
+ //Write SPI data register(s)
+ if (mosi_buf) {
+ uint8_t* mosi_bytes = (uint8_t*) mosi_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ word = (uint32_t) mosi_bytes[0];
+ } else if (bits_left <= 16) {
+ word = (((uint32_t) mosi_bytes[1]) << 0) |
+ (((uint32_t) mosi_bytes[0]) << 8);
+ } else if (bits_left <= 24) {
+ word = (((uint32_t) mosi_bytes[2]) << 0) |
+ (((uint32_t) mosi_bytes[1]) << 8) |
+ (((uint32_t) mosi_bytes[0]) << 16);
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ }
+ bits_left = 0;
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ mosi_bytes += 4;
+ bits_left -= 32;
+ }
+
+ switch (reg_index) {
+ case 0: WB_SPI_REGS(slave->base)->data0 = word; break;
+ case 1: WB_SPI_REGS(slave->base)->data1 = word; break;
+ case 2: WB_SPI_REGS(slave->base)->data2 = word; break;
+ case 3: WB_SPI_REGS(slave->base)->data3 = word; break;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+
+ //Compute flags for slave and write control register
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave);
+ if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS;
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl;
+
+ // Tell it which SPI slave device to access
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+
+ //Go go go!
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+
+ if (rw_mode == WRITE_READ) {
+ //Wait for SPI read operation to complete
+ _wait_for_xfer(slave);
+
+ if (miso_buf) {
+ //Read SPI data registers
+ uint8_t* miso_bytes = (uint8_t*) miso_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ switch (reg_index) {
+ case 0: word = WB_SPI_REGS(slave->base)->data0; break;
+ case 1: word = WB_SPI_REGS(slave->base)->data1; break;
+ case 2: word = WB_SPI_REGS(slave->base)->data2; break;
+ case 3: word = WB_SPI_REGS(slave->base)->data3; break;
+ }
+
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ miso_bytes[0] = word & 0xFF;
+ } else if (bits_left <= 16) {
+ miso_bytes[1] = word & 0xFF;
+ miso_bytes[0] = (word >> 8) & 0xFF;
+ } else if (bits_left <= 24) {
+ miso_bytes[2] = word & 0xFF;
+ miso_bytes[1] = (word >> 8) & 0xFF;
+ miso_bytes[0] = (word >> 16) & 0xFF;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ }
+ bits_left = 0;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ miso_bytes += 4;
+ bits_left -= 32;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+ }
+}
+
+void wb_spi_transact(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true);
+}
+
+void wb_spi_transact_man_ss(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false);
+}
+
+void wb_spi_slave_select(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually select slave
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+}
+
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually deselect slave
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+}
diff --git a/firmware/usrp3/n230/CMakeLists.txt b/firmware/usrp3/n230/CMakeLists.txt
new file mode 100644
index 000000000..6247477f0
--- /dev/null
+++ b/firmware/usrp3/n230/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# Copyright 2010-2014,2016 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/../../host/lib/usrp/n230)
+
+list(APPEND n230_sources n230_eeprom.c n230_eth_handlers.c n230_init.c n230_main.c)
+
+########################################################################
+set(GEN_OUTPUTS_BIN_SIZE 0x7fff)
+
+add_executable(n230_main.elf ${n230_sources})
+target_link_libraries(n230_main.elf usrp3fw)
+GEN_OUTPUTS(n230_main.elf n230)
+
+#INSTALL(
+# FILES ${CMAKE_CURRENT_BINARY_DIR}/n230_main.bin
+# DESTINATION share/uhd/images
+# RENAME usrp_n230_fw.bin
+#)
diff --git a/firmware/usrp3/n230/n230_burner.py b/firmware/usrp3/n230/n230_burner.py
new file mode 100755
index 000000000..7b9920de7
--- /dev/null
+++ b/firmware/usrp3/n230/n230_burner.py
@@ -0,0 +1,359 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import os.path
+import sys
+from array import array
+
+########################################################################
+# constants
+########################################################################
+N230_FLASH_COMM_UDP_PORT = 49154
+N230_FLASH_COMM_PAYLOAD_SIZE = 128
+N230_FLASH_COMM_SECTOR_SIZE = 65536
+
+N230_FLASH_COMM_FLAGS_ACK = 0x00000001
+N230_FLASH_COMM_FLAGS_CMD_MASK = 0x00000FF0
+N230_FLASH_COMM_FLAGS_ERROR_MASK = 0xFF000000
+
+N230_FLASH_COMM_CMD_READ_NV_DATA = 0x00000010
+N230_FLASH_COMM_CMD_WRITE_NV_DATA = 0x00000020
+N230_FLASH_COMM_CMD_READ_FPGA = 0x00000030
+N230_FLASH_COMM_CMD_WRITE_FPGA = 0x00000040
+N230_FLASH_COMM_CMD_ERASE_FPGA = 0x00000050
+
+N230_FLASH_COMM_ERR_PKT_ERROR = 0x80000000
+N230_FLASH_COMM_ERR_CMD_ERROR = 0x40000000
+N230_FLASH_COMM_ERR_SIZE_ERROR = 0x20000000
+
+N230_FLASH_COMM_SAFE_IMG_BASE = 0x000000
+N230_FLASH_COMM_PROD_IMG_BASE = 0x400000
+N230_FLASH_COMM_FPGA_IMG_MAX_SIZE = 0x400000
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+
+_seq = -1
+def next_seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+def seq():
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+short = struct.Struct('>H')
+ulong = struct.Struct('>I')
+
+def unpack_flash_transaction(buf):
+ (flags, seqno, offset, size) = struct.unpack_from('!LLLL', buf)
+ check_error(flags)
+ if (seqno != seq()):
+ raise Exception("The flash transaction operation returned an incorrect sequence number")
+ data = bytes()
+ for i in xrange(16, len(buf), 1):
+ data += buf[i]
+ return (flags, offset, size, data)
+
+def pack_flash_transaction(flags, offset, size, data):
+ buf = bytes()
+ buf = struct.pack('!LLLL', flags, next_seq(), offset, size)
+ for i in range(N230_FLASH_COMM_PAYLOAD_SIZE):
+ if (i < size):
+ buf += struct.pack('!B', data[i])
+ else:
+ buf += struct.pack('!B', 0)
+ return buf
+
+def check_error(flags):
+ if flags & N230_FLASH_COMM_ERR_PKT_ERROR == N230_FLASH_COMM_ERR_PKT_ERROR:
+ raise Exception("The flash transaction operation returned a packet error")
+ if flags & N230_FLASH_COMM_ERR_CMD_ERROR == N230_FLASH_COMM_ERR_CMD_ERROR:
+ raise Exception("The flash transaction operation returned a command error")
+ if flags & N230_FLASH_COMM_ERR_SIZE_ERROR == N230_FLASH_COMM_ERR_SIZE_ERROR:
+ raise Exception("The flash transaction operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Burner class, holds a socket and send/recv routines
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr):
+ self._safe_image = False
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, N230_FLASH_COMM_UDP_PORT))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+
+ def set_safe_image(self, noprompt):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to access the safe-image stored in the flash \n'
+ '----------------------------------------------------------------------\n'
+ 'Writing a non-functional image will brick the device.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ self._safe_image = True
+ else:
+ print 'Aborted by user'
+ sys.exit(1)
+ else:
+ print '[WARNING] Operating on safe image without a prompt as requested'
+ self._safe_image = True
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send_and_recv(self, pkt):
+ self._sock.send(pkt)
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def compute_offset(self, offset):
+ base = N230_FLASH_COMM_SAFE_IMG_BASE if (self._safe_image) else N230_FLASH_COMM_PROD_IMG_BASE
+ return base + offset
+
+ def burn_fpga_to_flash(self, bitfile_path, noprompt):
+ print '[BURN] Reading ' + bitfile_path + '...'
+ with open(bitfile_path, 'rb') as bitfile:
+ header = file_bytes = bitfile.read()
+ if (self._safe_image != self.parse_bitfile_header(file_bytes)['safe-image']):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to burn a safe image into a production slot \n'
+ ' or a production image into a safe slot. \n'
+ '----------------------------------------------------------------------\n'
+ 'This is dangerous and can cause the device to boot incorrectly.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() != 'y':
+ print '[BURN] Aborted by user'
+ return
+ else:
+ print '[WARNING] Burning image to the wrong slot without a prompt as requested'
+
+ print '[BURN] Writing to flash...'
+ pkt_chunks = chunkify(file_bytes, N230_FLASH_COMM_PAYLOAD_SIZE)
+ offset = 0
+ for pkt_data in pkt_chunks:
+ pkt_data = array("B", pkt_data)
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (len(pkt_data) >= N230_FLASH_COMM_PAYLOAD_SIZE) else len(pkt_data)
+ #Erase sector
+ if (offset % N230_FLASH_COMM_SECTOR_SIZE == 0):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Write data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Increment
+ offset += N230_FLASH_COMM_PAYLOAD_SIZE
+ draw_progress_bar((((offset/N230_FLASH_COMM_PAYLOAD_SIZE)+1)*100)/len(pkt_chunks))
+ print('\n[BURN] DONE')
+
+ def parse_bitfile_header(self, header_bytes):
+ xil_header = dict()
+ n230_header = dict()
+ n230_header['valid'] = False
+ ptr = 0
+ #Field 1
+ if short.unpack(header_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(header_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
+ #Headers
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 2
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 1
+ #Fields a-d
+ for keynum in range(0, 4):
+ key = header_bytes[ptr]
+ ptr += 1
+ val_len = short.unpack(header_bytes[ptr:ptr+2])[0]
+ ptr += 2
+ val = header_bytes[ptr:ptr+val_len]
+ ptr += val_len
+ xil_header[key] = val
+ #Field e
+ ptr += 1
+ length = ulong.unpack(header_bytes[ptr:ptr+4])[0]
+ xil_header['bl'] = length #Bitstream length
+ ptr += 4
+ xil_header['hl'] = ptr #Header lengt
+
+ #Map Xilinx header field to N230 specific ones
+ if xil_header and xil_header['a'].split(';')[0] == 'n230':
+ n230_header['valid'] = True
+ n230_header['user-id'] = int(xil_header['a'].split(';')[1].split('=')[1], 16)
+ n230_header['safe-image'] = (n230_header['user-id'] >> 16 == 0x5AFE)
+ n230_header['product'] = xil_header['b']
+ n230_header['timestamp'] = xil_header['c'] + ' ' + xil_header['d']
+ n230_header['filesize'] = xil_header['hl'] + xil_header['bl']
+ return n230_header
+
+ def read_bitfile_header_from_flash(self):
+ max_header_size = 1024 #Should be enough
+ header_bytes = bytes()
+ for offset in range(0, max_header_size, N230_FLASH_COMM_PAYLOAD_SIZE):
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ header_bytes += data
+ return self.parse_bitfile_header(header_bytes)
+
+ def extract_fpga_from_flash(self, bitfile_path):
+ header = self.read_bitfile_header_from_flash();
+ if not header['valid']:
+ raise Exception("Could not detect a vaild Xilinx .bit burned into the flash")
+ max_offset = header['filesize']
+ print '[EXTRACT] Writing ' + bitfile_path + '...'
+ with open(bitfile_path, 'wb') as bitfile:
+ for i in range(0, int(math.ceil(float(max_offset)/N230_FLASH_COMM_PAYLOAD_SIZE))):
+ offset = i * N230_FLASH_COMM_PAYLOAD_SIZE
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (max_offset - offset >= N230_FLASH_COMM_PAYLOAD_SIZE) else (max_offset - offset)
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ bitfile.write(data[:size])
+ draw_progress_bar(((offset*100)/max_offset) + 1)
+ print('\n[EXTRACT] DONE')
+
+ def erase_fpga_from_flash(self):
+ print '[ERASE] Erasing image from flash...'
+ for offset in range(0, N230_FLASH_COMM_FPGA_IMG_MAX_SIZE, N230_FLASH_COMM_SECTOR_SIZE):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ draw_progress_bar(((offset+N230_FLASH_COMM_SECTOR_SIZE)*100)/N230_FLASH_COMM_FPGA_IMG_MAX_SIZE)
+ print('\n[ERASE] DONE')
+
+ def wipe_user_data(self, noprompt):
+ confirm_msg = ('-------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to erase all the user data from the flash \n'
+ '-------------------------------------------------------------------\n'
+ 'This will cause the device to lose the following:\n'
+ ' * IP Address (Will default to 192.168.10.2)\n'
+ ' * Subnet Mask (Will default to 255.255.255.2)\n'
+ ' * MAC Address\n'
+ ' * Serial Number\n'
+ ' * Hardware Revision\n'
+ ' * ...and other identification info\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ wipe_ok = True
+ else:
+ print '[WIPE] Aborted by user'
+ wipe_ok = False
+ else:
+ print '[WARNING] Wiping user data without prompt a as requested'
+ wipe_ok = True
+
+ if wipe_ok:
+ print '[WIPE] Erasing all user data from flash...'
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_NV_DATA
+ out_pkt = pack_flash_transaction(flags, 0, N230_FLASH_COMM_PAYLOAD_SIZE, [0xFF]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ print('[WIPE] DONE. Please power-cycle the device.')
+
+ def print_status(self):
+ header = self.read_bitfile_header_from_flash();
+ if header['valid']:
+ print('[STATUS] Detected a valid .bit header in the flash (Product = %s, Datestamp = %s%s)' % \
+ (header['product'], header['timestamp'], ', Safe-Image' if header['safe-image'] else ''))
+ else:
+ print('[STATUS] No .bit header detected. Either the flash is uninitialized or the image is corrupt.')
+
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--status", action="store_true", help="Print out the status of the burned image", default=False)
+ parser.add_option("--erase", action="store_true", help="Erase FPGA bitstream from flash", default=False)
+ parser.add_option("--burn", type="string", help="Path to FPGA bitstream (.bit) to burn to flash", default=None)
+ parser.add_option("--extract", type="string", help="Destination bitfile to dump contents of the extracted image", default=None)
+ parser.add_option("--safe_image", action="store_true", help="Operate on the safe image. WARNING: This could be dangerous", default=False)
+ parser.add_option("--wipe_user_data", action="store_true", help="Erase all user data like IP, MAC, S/N, etc from flash", default=False)
+ parser.add_option("--no_prompt", action="store_true", help="Suppress all warning prompts", default=False)
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if not options.addr: raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(addr=options.addr)
+
+ # Initialize safe image selector first
+ if options.safe_image:
+ ctrl_sock.set_safe_image(options.no_prompt)
+
+ if options.status:
+ ctrl_sock.print_status()
+
+ # Order of operations:
+ # 1. Extract (if specified)
+ # 2. Erase (if specified)
+ # 3. Burn (if specified)
+
+ if options.extract is not None:
+ file_path = options.extract
+ ctrl_sock.print_status()
+ ctrl_sock.extract_fpga_from_flash(file_path)
+
+ if options.erase:
+ ctrl_sock.erase_fpga_from_flash()
+ ctrl_sock.print_status()
+
+ if options.burn is not None:
+ file_path = options.burn
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.bit'):
+ ctrl_sock.burn_fpga_to_flash(file_path, options.no_prompt)
+ ctrl_sock.print_status()
+ else:
+ raise Exception("Unsupported FPGA bitfile format. You must use a .bit file.")
+
+ if options.wipe_user_data:
+ ctrl_sock.wipe_user_data(options.no_prompt)
diff --git a/firmware/usrp3/n230/n230_debug.py b/firmware/usrp3/n230/n230_debug.py
new file mode 100755
index 000000000..f9ff64ab7
--- /dev/null
+++ b/firmware/usrp3/n230/n230_debug.py
@@ -0,0 +1,387 @@
+#!/usr/bin/env python
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import array
+import os.path
+import sys
+import time
+try:
+ import fcntl
+ N230_DEVICE_DISCOVERY_AVAILABLE = True
+except:
+ N230_DEVICE_DISCOVERY_AVAILABLE = False
+
+########################################################################
+# constants
+########################################################################
+N230_FW_COMMS_UDP_PORT = 49152
+N230_FW_COMMS_MAX_DATA_WORDS = 16
+
+N230_FW_COMMS_FLAGS_ACK = 0x00000001
+N230_FW_COMMS_FLAGS_ERROR_MASK = 0xF0000000
+N230_FW_COMMS_FLAGS_CMD_MASK = 0x000000F0
+
+N230_FW_COMMS_CMD_ECHO = 0x00000000
+N230_FW_COMMS_CMD_POKE32 = 0x00000010
+N230_FW_COMMS_CMD_PEEK32 = 0x00000020
+N230_FW_COMMS_CMD_BLOCK_POKE32 = 0x00000030
+N230_FW_COMMS_CMD_BLOCK_PEEK32 = 0x00000040
+
+N230_FW_COMMS_ERR_PKT_ERROR = 0x80000000
+N230_FW_COMMS_ERR_CMD_ERROR = 0x40000000
+N230_FW_COMMS_ERR_SIZE_ERROR = 0x20000000
+
+N230_FW_COMMS_ID = 0x0001ACE3
+
+N230_FW_LOADER_ADDR = 0xfa00
+N230_FW_LOADER_DATA = 0xfa04
+N230_FW_LOADER_NUM_WORDS = 8192
+N230_FW_LOADER_BOOT_DONE_ADDR = 0xA004
+N230_FW_LOADER_BOOT_TIMEOUT = 5
+
+N230_JESD204_TEST = 0xA014
+N230_FPGA_HASH_ADDR = 0xA010
+N230_FW_HASH_ADDR = 0x10004
+N230_ICAP_ADDR = 0xF800
+#ICAP_DUMMY_WORD = 0xFFFFFFFF
+#ICAP_SYNC_WORD = 0xAA995566
+#ICAP_TYPE1_NOP = 0x20000000
+#ICAP_WRITE_WBSTAR = 0x30020001
+#ICAP_WBSTAR_ADDR = 0x00000000
+#ICAP_WRITE_CMD = 0x30008001
+#ICAP_IPROG_CMD = 0x0000000F
+# Bit reversed values per Xilinx UG470 - Bits reversed within bytes.
+ICAP_DUMMY_WORD = 0xFFFFFFFF
+ICAP_SYNC_WORD = 0x5599AA66
+ICAP_TYPE1_NOP = 0x04000000
+ICAP_WRITE_WBSTAR = 0x0C400080
+ICAP_WBSTAR_ADDR = 0x00000000
+ICAP_WRITE_CMD = 0x0C000180
+ICAP_IPROG_CMD = 0x000000F0
+
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+FPGA_LOAD_TIMEOUT = 10
+
+_seq = -1
+def seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+def pack_fw_command(flags, seq, num_words, addr, data_arr):
+ if (num_words > N230_FW_COMMS_MAX_DATA_WORDS):
+ raise Exception("Data size too large. Firmware supports a max 16 words per block." % (addr))
+ buf = bytes()
+ buf = struct.pack('!IIIII', N230_FW_COMMS_ID, flags, seq, num_words, addr)
+ for i in range(N230_FW_COMMS_MAX_DATA_WORDS):
+ if (i < num_words):
+ buf += struct.pack('!I', data_arr[i])
+ else:
+ buf += struct.pack('!I', 0)
+ return buf
+
+def unpack_fw_command(buf, fmt=None):
+ (id, flags, seq, num_words, addr) = struct.unpack_from('!IIIII', buf)
+ fw_check_error(flags)
+ data = []
+ if fmt is None:
+ fmt = 'I'
+ for i in xrange(20, len(buf), 4):
+ data.append(struct.unpack('!'+fmt, buf[i:i+4])[0])
+ return (flags, seq, num_words, addr, data)
+
+def fw_check_error(flags):
+ if flags & N230_FW_COMMS_ERR_PKT_ERROR == N230_FW_COMMS_ERR_PKT_ERROR:
+ raise Exception("The fiwmware operation returned a packet error")
+ if flags & N230_FW_COMMS_ERR_CMD_ERROR == N230_FW_COMMS_ERR_CMD_ERROR:
+ raise Exception("The fiwmware operation returned a command error")
+ if flags & N230_FW_COMMS_ERR_SIZE_ERROR == N230_FW_COMMS_ERR_SIZE_ERROR:
+ raise Exception("The fiwmware operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Discovery class
+########################################################################
+class discovery_socket(object):
+ def __init__(self):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ self._sock.settimeout(0.250)
+
+ def get_bcast_addrs(self):
+ max_possible = 128 # arbitrary. raise if needed.
+ num_bytes = max_possible * 32
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ names = array.array('B', '\0' * num_bytes)
+ outbytes = struct.unpack('iL', fcntl.ioctl(
+ s.fileno(),
+ 0x8912, # SIOCGIFCONF
+ struct.pack('iL', num_bytes, names.buffer_info()[0])
+ ))[0]
+ namestr = names.tostring()
+ lst = []
+ for i in range(0, outbytes, 40):
+ name = namestr[i:i+16].split('\0', 1)[0]
+ ip = map(ord, namestr[i+20:i+24])
+ mask = map(ord, fcntl.ioctl(s.fileno(), 0x891B, struct.pack('256s', name))[20:24])
+ bcast = []
+ for i in range(len(ip)):
+ bcast.append((ip[i] | (~mask[i])) & 0xFF)
+ if (name != 'lo'):
+ lst.append(str(bcast[0]) + '.' + str(bcast[1]) + '.' + str(bcast[2]) + '.' + str(bcast[3]))
+ return lst
+
+ def discover(self):
+ addrs = []
+ for bcast_addr in self.get_bcast_addrs():
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 0, 0, [0])
+ self._sock.sendto(out_pkt, (bcast_addr, N230_FW_COMMS_UDP_PORT))
+ while 1:
+ try:
+ (in_pkt, addr_pair) = self._sock.recvfrom(UDP_MAX_XFER_BYTES)
+ if len(in_pkt) < 20:
+ continue
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ addrs.append(addr_pair[0])
+ except socket.error:
+ break
+ return addrs
+
+
+########################################################################
+# Communications class, holds a socket and send/recv routine
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr, port):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, port))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+ self.peek(0) #Dummy read
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send(self, pkt):
+ self._sock.send(pkt)
+
+ def recv(self):
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def send_and_recv(self, pkt):
+ self.send(pkt)
+ return self.recv()
+
+ def peek(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, peek_addr, [0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return data[0]
+
+ def peek64(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_BLOCK_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 2, peek_addr, [0,0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return (data[0] | (data[1] << 32))
+
+ def poke(self, poke_addr, poke_data, ack=True):
+ ack_flag = N230_FW_COMMS_FLAGS_ACK if ack else 0
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32|ack_flag, seq(), 1, poke_addr, [poke_data])
+ self.send(out_pkt)
+ if ack:
+ in_pkt = self.recv()
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+
+ def live_load_firmware_bin(self, bin_path):
+ raise Exception("live_load_firmware_bin not implemented yet!")
+
+ def live_load_firmware_coe(self, coe_path):
+ with open(coe_path, 'r') as coe_file:
+ print("Loading %s..." % coe_path)
+ coe_lines = [line.strip(',;\n ') for line in coe_file]
+ start_index = coe_lines.index("memory_initialization_vector=") + 1
+ coe_words = coe_lines[start_index:]
+ if len(coe_words) != N230_FW_LOADER_NUM_WORDS:
+ raise Exception("invalid COE file. Must contain 8192 words!")
+ self.poke(N230_FW_LOADER_ADDR, 0) #Load start address
+ for i in range(0, len(coe_words)):
+ self.poke(N230_FW_LOADER_DATA, int(coe_words[i],16), (i%10==0) and (i<len(coe_words)-1))
+ draw_progress_bar(((i+1)*100)/len(coe_words))
+ print("\nRebooting...")
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32, seq(), 1, N230_FW_LOADER_BOOT_DONE_ADDR, [1])
+ self._sock.send(out_pkt)
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(N230_FW_LOADER_BOOT_TIMEOUT):
+ try:
+ self._sock.send(out_pkt)
+ in_pkt = self._sock.recv(UDP_MAX_XFER_BYTES)
+ print("Firmware is alive!")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("Firmware boot FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def read_hash(self):
+ fpga_hash = self.peek(N230_FPGA_HASH_ADDR)
+ fpga_status = "clean" if (fpga_hash & 0xf0000000 == 0x0) else "modified"
+ fw_hash = self.peek(N230_FW_HASH_ADDR)
+ fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "modified"
+ print("FPGA Version : %x (%s)" % (fpga_hash & 0xfffffff, fpga_status))
+ print("Firmware Version : %x (%s)" % (fw_hash & 0xfffffff, fw_status))
+
+ def is_claimed(self):
+ claimed = self.peek(0x10008)
+ print("Claimed : %s") % ('YES' if claimed else 'NO')
+
+ def reset_fpga(self):
+ print("Reseting USRP...")
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_DUMMY_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_SYNC_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_WBSTAR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WBSTAR_ADDR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_CMD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_IPROG_CMD, False)
+ print("Waiting for FPGA to load...")
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(FPGA_LOAD_TIMEOUT):
+ try:
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ print("FPGA loaded successfully.")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("FPGA load FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def jesd204_test_connector(self):
+ print("Testing JESD204 connectors. Molex cable #79576-2102 must be connected")
+ ctrl_sock.poke(N230_JESD204_TEST,0x1)
+ while True:
+ jesd204_test_status = ctrl_sock.peek(N230_JESD204_TEST)
+ if (jesd204_test_status & 0x10000 == 0x10000):
+ break
+ ctrl_sock.poke(N230_JESD204_TEST,0x0)
+ if (jesd204_test_status & 0xFFFF != 0x0):
+ print("JESD204 loopback test Failed!: Returned status is %4x" % (jesd204_test_status & 0xFFFF))
+ else:
+ print("JESD204 loopback test Passed.")
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--discover", action="store_true",help="Find all devices connected N230 devices", default=False)
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--peek", type="int", help="Read from memory map", default=None)
+ parser.add_option("--poke", type="int", help="Write to memory map", default=None)
+ parser.add_option("--data", type="int", help="Data for poke", default=None)
+ parser.add_option("--fw", type="string", help="Path to FW image to load", default=None)
+ parser.add_option("--hash", action="store_true",help="Display FPGA git hash", default=False)
+ parser.add_option("--reset", action="store_true",help="Reset and Reload USRP FPGA.", default=False)
+ parser.add_option("--jesd204test", action="store_true",help="Test mini-SAS connectors with loopback cable..", default=False)
+
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if options.discover:
+ if N230_DEVICE_DISCOVERY_AVAILABLE:
+ disc_sock = discovery_socket()
+ for addr in disc_sock.discover():
+ print '==== FOUND ' + addr + ' ===='
+ ctrl_sock = ctrl_socket(addr, N230_FW_COMMS_UDP_PORT)
+ ctrl_sock.read_hash()
+ ctrl_sock.is_claimed()
+ sys.exit()
+ else:
+ raise Exception('Discovery is only supported on Linux.')
+
+ if not options.addr:
+ raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(options.addr, N230_FW_COMMS_UDP_PORT)
+
+ if options.fw is not None:
+ file_path = options.fw
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.coe'):
+ ctrl_sock.live_load_firmware_coe(file_path)
+ elif (extension.lower() == '.bin'):
+ ctrl_sock.live_load_firmware_bin(file_path)
+ else:
+ raise Exception("Unsupported firmware file format")
+
+ if options.hash:
+ ctrl_sock.read_hash()
+
+ if options.peek is not None:
+ addr = options.peek
+ data = ctrl_sock.peek(addr)
+ print("PEEK[0x%x (%d)] => 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.poke is not None and options.data is not None:
+ addr = options.poke
+ data = options.data
+ ctrl_sock.poke(addr,data)
+ print("POKE[0x%x (%d)] <= 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.reset:
+ ctrl_sock.reset_fpga()
+
+ if options.jesd204test:
+ ctrl_sock.jesd204_test_connector()
diff --git a/firmware/usrp3/n230/n230_eeprom.c b/firmware/usrp3/n230/n230_eeprom.c
new file mode 100644
index 000000000..8f756d41f
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eeprom.c
@@ -0,0 +1,196 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+
+#include <trace.h>
+#include <stddef.h>
+#include <flash/spi_flash.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <string.h> //memcpy
+
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+
+static const wb_spi_slave_t flash_spi_slave = {
+ .base = (void*) 0xB000,
+ .slave_sel = 0x0001,
+ .clk_div = 4, //80MHz/4 = 20MHz
+ .mosi_edge = RISING,
+ .miso_edge = FALLING,
+ .lsb_first = false
+};
+
+static const spi_flash_dev_t spi_flash_device = {
+ .page_size = 256,
+ .sector_size = 65536,
+ .num_sectors = 254,
+ .bus = &flash_spi_slave
+};
+
+/***********************************************************************
+ * Non-volatile device data
+ **********************************************************************/
+#define N230_FLASH_NV_DATA_OFFSET 0x800000
+
+//Default values in case the EEPROM is not read, corrupt
+const n230_eeprom_map_t default_eeprom = {
+ .data_version_major = N230_EEPROM_VER_MAJOR,
+ .data_version_minor = N230_EEPROM_VER_MINOR,
+ .hw_revision = 0,
+ .hw_product = 0x01,
+ .gateway = N230_DEFAULT_GATEWAY,
+ .eth_info = {
+ { //eth0
+ .mac_addr = N230_DEFAULT_ETH0_MAC,
+ .subnet = N230_DEFAULT_ETH0_MASK,
+ .ip_addr = N230_DEFAULT_ETH0_IP
+ },
+ { //eth1
+ .mac_addr = N230_DEFAULT_ETH1_MAC,
+ .subnet = N230_DEFAULT_ETH1_MASK,
+ .ip_addr = N230_DEFAULT_ETH1_IP
+ }
+ }
+};
+
+//EEPROM cache
+static spi_flash_session_t flash_session = {.device = NULL};
+static n230_eeprom_map_t eeprom_cache;
+static bool cache_dirty = true;
+
+bool read_n230_eeprom()
+{
+ bool status = false;
+ if (flash_session.device == NULL) { //Initialize flash session structure for the first time
+ wb_spi_init(spi_flash_device.bus);
+ spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations());
+ }
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+
+ //Verify data format
+ status = (eeprom_cache.data_version_major == default_eeprom.data_version_major);
+ //Sanity communication info
+ if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr;
+ if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr;
+ if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet;
+ if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet;
+
+ if (!status) {
+ UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map.");
+ memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t));
+ }
+ cache_dirty = !status;
+ return status;
+}
+
+bool write_n230_eeprom()
+{
+ //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size
+ //This function would need to be reimplemented if this assumption is no longer true
+ if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size");
+ return false;
+ }
+
+ bool status = true;
+ if (cache_dirty) {
+ n230_eeprom_map_t device_eeprom;
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t));
+ if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) {
+ //Cache does not match read state. Write.
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash...");
+ status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET);
+ if (status) {
+ status = spif_write_page_sync(
+ &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+ }
+ if (!status) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!");
+ }
+ cache_dirty = !status;
+ } else {
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped.");
+ //Cache matches read state. So mark as clean
+ cache_dirty = false;
+ }
+ }
+ return status;
+}
+
+bool is_n230_eeprom_cache_dirty()
+{
+ return cache_dirty;
+}
+
+n230_eeprom_map_t* get_n230_eeprom_map()
+{
+ cache_dirty = true;
+ return &eeprom_cache;
+}
+
+const n230_eeprom_map_t* get_n230_const_eeprom_map()
+{
+ return &eeprom_cache;
+}
+
+const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) {
+ if (iface >= N230_NUM_ETH_PORTS) {
+ UHD_FW_TRACE_FSTR(ERROR,
+ "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!",
+ iface, N230_NUM_ETH_PORTS);
+ }
+ return &(get_n230_const_eeprom_map()->eth_info[iface]);
+}
+
+
+/***********************************************************************
+ * Storage for bootstrap FPGA Image
+ **********************************************************************/
+#define N230_FLASH_FPGA_IMAGE_OFFSET 0x000000
+#define N230_FLASH_FPGA_IMAGE_SIZE 0x400000
+#define N230_FLASH_NUM_FPGA_IMAGES 2
+
+void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ }
+ spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool erase_n230_fpga_image_sector(uint32_t offset)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset);
+}
diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c
new file mode 100644
index 000000000..b291bb39f
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.c
@@ -0,0 +1,340 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_eth_handlers.h"
+
+#include <wb_utils.h>
+#include <string.h> //memcmp
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../n230/n230_fw_host_iface.h"
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+
+static n230_host_shared_mem_t* host_shared_mem_ptr;
+
+static const soft_reg_field_t LED_REG_FIELD_ETH_LINK2 = {.num_bits=1, .shift=0};
+static const soft_reg_field_t LED_REG_FIELD_ETH_LINK1 = {.num_bits=1, .shift=1};
+static const soft_reg_field_t LED_REG_FIELD_ETH_ACT2 = {.num_bits=1, .shift=2};
+static const soft_reg_field_t LED_REG_FIELD_ETH_ACT1 = {.num_bits=1, .shift=3};
+
+/***********************************************************************
+ * Handler for host <-> firmware communication
+ **********************************************************************/
+
+static inline void n230_poke32(const uint32_t addr, const uint32_t data)
+{
+ if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data;
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ wb_poke32(addr, data);
+ }
+}
+
+static inline uint32_t n230_peek32(const uint32_t addr)
+{
+ if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)];
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ return wb_peek32(addr);
+ } else {
+ return 0;
+ }
+}
+
+void n230_handle_udp_fw_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(fw_comm_pkt_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size).");
+ } else {
+ const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff;
+ fw_comm_pkt_t response;
+ bool send_response = process_fw_comm_protocol_pkt(
+ request, &response,
+ N230_FW_PRODUCT_ID,
+ (uint32_t)ethno,
+ n230_poke32, n230_peek32);
+
+ if (send_response) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr)
+{
+ host_shared_mem_ptr = shared_mem_ptr;
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms);
+}
+
+
+/***********************************************************************
+ * Handler for UDP framer program packets
+ **********************************************************************/
+void program_udp_framer(
+ const uint8_t ethno,
+ const uint32_t sid,
+ const struct ip_addr *dst_ip,
+ const uint16_t dst_port,
+ const uint16_t src_port)
+{
+ const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip);
+ const size_t vdest = (sid >> 16) & 0xff;
+
+ uint32_t framer_base =
+ ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE;
+
+ //setup source framer
+ const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI),
+ (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO),
+ (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) |
+ (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port);
+
+ //setup destination framer
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC),
+ (((uint32_t)dst_port) << 16) |
+ (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO),
+ (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) |
+ (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0));
+}
+
+void handle_udp_prog_framer(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else {
+ const uint32_t sid = ((const uint32_t *)buff)[1];
+ program_udp_framer(ethno, sid, src, src_port, dst_port);
+ UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d",
+ ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port);
+ }
+}
+
+void n230_register_udp_prog_framer()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer);
+}
+
+
+/***********************************************************************
+ * Handler for flash programming interface over UDP
+ **********************************************************************/
+
+void n230_handle_flash_prog_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(n230_flash_prog_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size).");
+ } else {
+ const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff;
+ n230_flash_prog_t response;
+ bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK;
+
+ //Request is valid. Copy it into the reply.
+ memcpy(&response, request, sizeof(n230_flash_prog_t));
+
+ switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) {
+ case N230_FLASH_COMM_CMD_READ_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ if (is_n230_eeprom_cache_dirty()) {
+ read_n230_eeprom();
+ }
+ //EEPROM cache is up-to-date. Copy it into the packet.
+ //Assumption: Cache size < 256. If this is no longer true, the offset field
+ //will have to be used.
+ memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t));
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t));
+ if (!write_n230_eeprom()) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_READ_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ read_n230_fpga_image_page(request->offset, response.data, request->size);
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_ERASE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)",
+ request->offset);
+ if (!erase_n230_fpga_image_sector(request->offset)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ default :{
+ UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command.");
+ response.flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+ //Send a reply if ack requested
+ if (ack_requested) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_flash_comms_handler()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms);
+}
+
+/***********************************************************************
+ * Handler for SFP state changes
+ **********************************************************************/
+#define SFPP_STATUS_MODABS_CHG (1 << 5) // Has MODABS changed since last read?
+#define SFPP_STATUS_TXFAULT_CHG (1 << 4) // Has TXFAULT changed since last read?
+#define SFPP_STATUS_RXLOS_CHG (1 << 3) // Has RXLOS changed since last read?
+#define SFPP_STATUS_MODABS (1 << 2) // MODABS state
+#define SFPP_STATUS_TXFAULT (1 << 1) // TXFAULT state
+#define SFPP_STATUS_RXLOS (1 << 0) // RXLOS state
+
+static bool links_up[N230_MAX_NUM_ETH_PORTS] = {};
+static uint32_t packet_count[N230_MAX_NUM_ETH_PORTS] = {};
+
+void n230_poll_sfp_status(const uint32_t eth, bool force, bool* state_updated)
+{
+ // Has MODDET/MODAbS changed since we last looked?
+ uint32_t rb = wb_peek32(SR_ADDR(WB_SBRB_BASE, (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1));
+
+ if (rb & SFPP_STATUS_RXLOS_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (rb & SFPP_STATUS_RXLOS));
+ if (rb & SFPP_STATUS_TXFAULT_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((rb & SFPP_STATUS_TXFAULT) >> 1));
+ if (rb & SFPP_STATUS_MODABS_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((rb & SFPP_STATUS_MODABS) >> 2));
+
+ //update the link up status
+ if ((rb & SFPP_STATUS_RXLOS_CHG) || (rb & SFPP_STATUS_TXFAULT_CHG) || (rb & SFPP_STATUS_MODABS_CHG) || force)
+ {
+ const bool old_link_up = links_up[eth];
+ const uint32_t status_reg_addr = (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1;
+
+ uint32_t sfpp_status = wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) & 0xFFFF;
+ if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
+ int8_t timeout = 100;
+ bool link_up = false;
+ do {
+ link_up = ((wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) >> 16) & 0x1) != 0;
+ } while (!link_up && timeout-- > 0);
+
+ links_up[eth] = link_up;
+ } else {
+ links_up[eth] = false;
+ }
+
+ if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
+ UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down");
+ if (rb & SFPP_STATUS_MODABS_CHG) {
+ // MODDET has changed state since last checked
+ if (rb & SFPP_STATUS_MODABS) {
+ // MODDET is high, module currently removed.
+ UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth);
+ } else {
+ // MODDET is low, module currently inserted.
+ // Return status.
+ UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth);
+ }
+ }
+ *state_updated = true;
+ } else {
+ *state_updated = false;
+ }
+}
+
+void n230_update_link_act_state(soft_reg_t* led_reg)
+{
+ static bool first_poll = 1;
+ static uint32_t poll_cnt;
+
+ bool activity[N230_MAX_NUM_ETH_PORTS] = {};
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ if (first_poll) {
+ links_up[i] = 0;
+ packet_count[i] = 0;
+ poll_cnt = 0;
+ }
+
+ //Check SFP status and update links_up
+ bool link_state_from_sfp = false;
+ n230_poll_sfp_status(i, first_poll, &link_state_from_sfp);
+
+ //Check packet counters less frequently to keep the LED on for a visible duration
+ uint32_t cnt = wb_peek32(SR_ADDR(WB_SBRB_BASE, (i==0)?RB_ZPU_ETH0_PKT_CNT:RB_ZPU_ETH1_PKT_CNT));
+ activity[i] = (cnt != packet_count[i]);
+ packet_count[i] = cnt;
+
+ //Update links_up if there is activity only if the SFP
+ //handler has not updated it
+ if (activity[i] && !link_state_from_sfp) links_up[i] = true;
+ }
+
+ //TODO: Swap this when Ethernet port swap issues is fixed
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK2, links_up[0]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK1, links_up[1]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT2, activity[0]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT1, activity[1]?1:0);
+
+ first_poll = 0;
+}
+
diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h
new file mode 100644
index 000000000..67afbb246
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.h
@@ -0,0 +1,48 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_ETH_HANDLERS_H
+#define INCLUDED_N230_ETH_HANDLERS_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <lwip/ip_addr.h>
+#include <wb_soft_reg.h>
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr);
+
+/*!
+ * Registrar for framer programmer handler.
+ */
+void n230_register_udp_prog_framer();
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_flash_comms_handler();
+
+/*!
+ * Handle SFP updates.
+ */
+void n230_update_link_act_state(soft_reg_t* led_reg);
+
+#endif /* INCLUDED_N230_ETH_HANDLERS_H */
diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c
new file mode 100644
index 000000000..14f5ebd77
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.c
@@ -0,0 +1,125 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <printf.h>
+#include <wb_utils.h>
+#include <wb_uart.h>
+#include <wb_i2c.h>
+#include <wb_pkt_iface64.h>
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+#include "n230_init.h"
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+
+static wb_pkt_iface64_config_t pkt_config;
+
+static void putc(void *p, char c)
+{
+//If FW_TRACE_LEVEL is defined, then the trace level is set
+//to a non-zero number. Turn on the debug UART to enable tracing
+#ifdef UHD_FW_TRACE_LEVEL
+ wb_uart_putc(WB_DBG_UART_BASE, c);
+#endif
+}
+
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(WB_SBRB_BASE, RB_ZPU_COUNTER));
+}
+
+void n230_init(void)
+{
+ //TODO: We may need to remove the debug UART before we release.
+ //Initialize the debug UART first.
+ wb_uart_init(WB_DBG_UART_BASE, CPU_CLOCK_FREQ/DBG_UART_BAUD);
+ init_printf(NULL, putc);
+
+ //Now we can init the rest with prints
+ UHD_FW_TRACE_FSTR(INFO, "[ZPU Init Begin -- CPU CLOCK is %d MHz]", (CPU_CLOCK_FREQ/1000000));
+
+ //Initialize cron and the per millisecond cron job
+ UHD_FW_TRACE(INFO, "Initializing cron...");
+ cron_init(get_counter_val, CPU_CLOCK_FREQ);
+ cron_job_init(PER_MILLISEC_CRON_JOBID, 1);
+ cron_job_init(PER_SECOND_CRON_JOBID, 1000);
+
+ //Initialize rate for I2C cores
+ UHD_FW_TRACE(INFO, "Initializing I2C...");
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ wb_i2c_init((i==1)?WB_ETH1_I2C_BASE:WB_ETH0_I2C_BASE, CPU_CLOCK_FREQ);
+ }
+
+ //Initialize eeprom
+ read_n230_eeprom();
+
+ UHD_FW_TRACE(INFO, "Initializing network stack...");
+ init_network_stack();
+}
+
+void init_network_stack(void)
+{
+ //Hold Ethernet PHYs in reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_PHY);
+
+ //Initialize ethernet packet interface
+ pkt_config = wb_pkt_iface64_init(WB_PKT_RAM_BASE, WB_PKT_RAM_CTRL_OFFSET);
+ u3_net_stack_init(&pkt_config);
+
+ //Initialize MACs
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ init_ethernet_mac(i);
+ }
+
+ //Pull Ethernet PHYs out of reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_NONE);
+}
+
+void init_ethernet_mac(uint32_t iface_num)
+{
+ UHD_FW_TRACE_FSTR(INFO, "Initializing eth%d...", iface_num);
+
+ //Get interface info from the EEPROM (or defaults otherwise)
+ const n230_eth_eeprom_map_t* eth_eeprom_map = get_n230_ethernet_info(iface_num);
+ const eth_mac_addr_t *my_mac = (const eth_mac_addr_t *) &(eth_eeprom_map->mac_addr);
+ const struct ip_addr *my_ip = (const struct ip_addr *) &(eth_eeprom_map->ip_addr);
+ const struct ip_addr *subnet = (const struct ip_addr *) &(eth_eeprom_map->subnet);
+
+ //Init software fields related to ethernet
+ u3_net_stack_init_eth(iface_num, my_mac, my_ip, subnet);
+
+ uint32_t dispatcher_base =
+ ((iface_num == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_DISPATCHER_BASE;
+
+ //Program dispatcher
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 0),
+ (my_mac->addr[5] << 0) | (my_mac->addr[4] << 8) | (my_mac->addr[3] << 16) | (my_mac->addr[2] << 24));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 1), (my_mac->addr[1] << 0) | (my_mac->addr[0] << 8));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 2), my_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 4), 0/*nofwd*/);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 5), (ICMP_IRQ << 8) | 0); //no fwd: type, code
+
+ //DEBUG: Print initialized info
+ UHD_FW_TRACE_FSTR(INFO, "-- MAC%u: %s", iface_num, mac_addr_to_str(u3_net_stack_get_mac_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- IP%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_ip_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- SUBNET%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_subnet(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- BCAST%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_bcast(iface_num)));
+}
+
+
diff --git a/firmware/usrp3/n230/n230_init.h b/firmware/usrp3/n230/n230_init.h
new file mode 100644
index 000000000..e2231909e
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.h
@@ -0,0 +1,28 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_INIT_H
+#define INCLUDED_N230_INIT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void n230_init(void);
+void init_network_stack(void);
+void init_ethernet_mac(uint32_t iface_num);
+
+#endif /* INCLUDED_B250_INIT_H */
diff --git a/firmware/usrp3/n230/n230_main.c b/firmware/usrp3/n230/n230_main.c
new file mode 100644
index 000000000..a6c12e56d
--- /dev/null
+++ b/firmware/usrp3/n230/n230_main.c
@@ -0,0 +1,113 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <wb_soft_reg.h>
+#include <u3_net_stack.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+#include "n230_eth_handlers.h"
+#include "n230_init.h"
+
+//The version hash should come from a cmake build variable
+//If it doesn't then the build system does not support the feature
+//so just default to 0xFFFFFFFF
+#ifndef UHD_VERSION_HASH
+#define UHD_VERSION_HASH 0xFFFFFFFF
+#endif
+
+//TODO: This is just for initial debugging.
+static soft_reg_t g_led_register;
+
+//Shared memory
+static n230_host_shared_mem_t g_host_shared_mem;
+
+//Functions
+static void n230_handle_claim();
+
+/***********************************************************************
+ * Main loop runs all the handlers
+ **********************************************************************/
+int main(void)
+{
+ //Initialize host shared mem
+ g_host_shared_mem.data.fw_compat_num = N230_FW_COMPAT_NUM;
+ g_host_shared_mem.data.fw_version_hash = UHD_VERSION_HASH;
+
+ //Main initialization function
+ n230_init();
+
+ //Initialize UDP Handlers
+ n230_register_udp_fw_comms_handler(&g_host_shared_mem);
+ n230_register_udp_prog_framer();
+ n230_register_flash_comms_handler();
+
+ initialize_writeonly_soft_reg(&g_led_register, SR_ADDR(WB_SBRB_BASE, SR_ZPU_LEDS));
+
+ uint32_t heart_beat = 0;
+ while(true)
+ {
+ //TODO: This is just for initial debugging. Once the firmware
+ //is somewhat stable we should delete this cron job
+ if (cron_job_run_due(PER_SECOND_CRON_JOBID)) {
+ //Everything in this block runs approx once per second
+ if (heart_beat % 10 == 0) {
+ UHD_FW_TRACE_FSTR(INFO, "0.1Hz Heartbeat (%u)", heart_beat);
+ }
+ heart_beat++;
+ }
+
+ if (cron_job_run_due(PER_MILLISEC_CRON_JOBID)) {
+ //Everything in this block runs approx once per millisecond
+ n230_handle_claim();
+ n230_update_link_act_state(&g_led_register);
+ }
+
+ //run the network stack - poll and handle
+ u3_net_stack_handle_one();
+ }
+ return 0;
+}
+
+// Watchdog timer for claimer
+static void n230_handle_claim()
+{
+ static uint32_t last_time = 0;
+ static size_t timeout = 0;
+
+ if (g_host_shared_mem.data.claim_time == 0) {
+ //If time is 0 if the claim was forfeit
+ g_host_shared_mem.data.claim_status = 0;
+ } else if (last_time != g_host_shared_mem.data.claim_time) {
+ //If the time changes, reset timeout
+ g_host_shared_mem.data.claim_status = 1;
+ timeout = 0;
+ } else {
+ //Otherwise increment for timeout
+ timeout++;
+ }
+
+ //Always stash the last seen time
+ last_time = g_host_shared_mem.data.claim_time;
+
+ //Timeout logic
+ if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) {
+ g_host_shared_mem.data.claim_time = 0;
+ }
+}
+
diff --git a/firmware/usrp3/utils/git-hash.sh b/firmware/usrp3/utils/git-hash.sh
new file mode 100755
index 000000000..ff7ae5ecb
--- /dev/null
+++ b/firmware/usrp3/utils/git-hash.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [[ $(command -v git) = "" ]]; then
+ short_hash="FFFFFFFF"
+else
+ if (git diff --quiet); then
+ #Clean
+ short_hash="0$(git rev-parse --verify HEAD --short)"
+ else
+ #Dirty
+ short_hash="F$(git rev-parse --verify HEAD --short)"
+ fi
+fi
+echo ${short_hash^^} \ No newline at end of file
diff --git a/firmware/usrp3/x300/x300_aurora_bist.py b/firmware/usrp3/x300/x300_aurora_bist.py
new file mode 100755
index 000000000..f5e119b66
--- /dev/null
+++ b/firmware/usrp3/x300/x300_aurora_bist.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+#
+# Copyright 2016 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+from builtins import str
+from builtins import range
+
+import x300_debug
+import argparse
+import time
+import datetime
+import math
+import tqdm
+import numpy
+
+########################################################################
+# constants
+########################################################################
+SFP0_MAC_REG_BASE = 0xC000
+SFP1_MAC_REG_BASE = 0xD000
+SFP0_TYPE_REG_OFFSET = 0xA000 + 16
+SFP1_TYPE_REG_OFFSET = 0xA000 + 20
+SFP0_STATUS_REG_OFFSET = 0xA000 + 32
+SFP1_STATUS_REG_OFFSET = 0xA000 + 36
+
+SFP_TYPE_AURORA = 2
+
+MAC_REG_CTRL = 0
+MAC_REG_STATUS = 0
+MAC_REG_OVERRUNS = 4
+MAC_REG_BIST_SAMPS = 8
+MAC_REG_BIST_ERRORS = 12
+
+MAC_STATUS_LINK_UP_MSK = 0x00000001
+MAC_STATUS_HARD_ERR_MSK = 0x00000002
+MAC_STATUS_SOFT_ERR_MSK = 0x00000004
+MAC_STATUS_BIST_LOCKED_MSK = 0x00000008
+MAC_STATUS_BIST_LATENCY_MSK = 0x000FFFF0
+MAC_STATUS_BIST_LATENCY_OFFSET = 4
+MAC_STATUS_CHECKSUM_ERRS_MSK = 0xFFF00000
+MAC_STATUS_CHECKSUM_ERRS_OFFSET = 20
+
+MAC_CTRL_BIST_CHECKER_EN = 0x00000001
+MAC_CTRL_BIST_GEN_EN = 0x00000002
+MAC_CTRL_BIST_LOOPBACK_EN = 0x00000004
+MAC_CTRL_PHY_RESET = 0x00000100
+MAC_CTRL_BIST_RATE_MSK = 0x000000F8
+MAC_CTRL_BIST_RATE_OFFSET = 3
+
+AURORA_CLK_RATE = 156.25e6
+BUS_CLK_RATE = 166.66e6
+BIST_MAX_TIME_LIMIT = math.floor(pow(2,48)/AURORA_CLK_RATE)-1
+
+########################################################################
+# utils
+########################################################################
+def get_aurora_info(ctrl):
+ if (ctrl.peek(SFP0_TYPE_REG_OFFSET) == SFP_TYPE_AURORA):
+ aur_port = 0
+ elif (ctrl.peek(SFP1_TYPE_REG_OFFSET) == SFP_TYPE_AURORA):
+ aur_port = 1
+ else:
+ aur_port = -1
+ link_up = False
+ if aur_port != -1:
+ mac_base = SFP0_MAC_REG_BASE if aur_port == 0 else SFP1_MAC_REG_BASE
+ link_up = ((ctrl.peek(mac_base + MAC_REG_STATUS) & MAC_STATUS_LINK_UP_MSK) != 0)
+ return (aur_port, link_up)
+
+def get_rate_setting(rate):
+ for div in range(2,32):
+ if (rate < 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/div)):
+ return (div-1, 8e-6 * BUS_CLK_RATE * (1.0 - 1.0/(div-1)))
+ return (0, 8e-6 * BUS_CLK_RATE)
+
+def run_ber_loopback_bist(ctrls, duration, rate_tuple):
+ (rate_sett, rate) = rate_tuple
+ print('[INFO] Running BER Loopback BIST at %.0fMB/s for %.0fs...'%(rate,duration))
+ # Determine offsets
+ (mst_port, link_up) = get_aurora_info(ctrls['master'])
+ MST_MAC_REG_BASE = SFP0_MAC_REG_BASE if (mst_port == 0) else SFP1_MAC_REG_BASE
+ if 'slave' in ctrls:
+ (sla_port, link_up) = get_aurora_info(ctrls['slave'])
+ SLA_MAC_REG_BASE = SFP0_MAC_REG_BASE if (sla_port == 0) else SFP1_MAC_REG_BASE
+ # Reset both PHYS
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET)
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET)
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ time.sleep(1.5)
+ # Put the slave in loopback mode and the master in BIST mode
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN)
+ if rate_sett == 0:
+ master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN
+ else:
+ master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, master_ctrl)
+ start_time = datetime.datetime.now()
+ # Wait and check if BIST locked
+ time.sleep(0.5)
+ mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS)
+ if (not (mst_status & MAC_STATUS_BIST_LOCKED_MSK)):
+ print('[ERROR] BIST engine did not lock onto a PRBS word!')
+ # Wait for requested time
+ try:
+ for i in tqdm.tqdm(list(range(duration)), desc='[INFO] Progress'):
+ time.sleep(1.0)
+ except KeyboardInterrupt:
+ print('[WARNING] Operation cancelled by user.')
+ # Turn off the BIST generator and loopback
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ stop_time = datetime.datetime.now()
+ time.sleep(0.5)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ # Validate status and no overruns
+ mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS)
+ mst_overruns = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_OVERRUNS)
+ if (mst_status & MAC_STATUS_HARD_ERR_MSK):
+ print('[ERROR] Hard errors in master PHY')
+ if (mst_overruns > 0):
+ print('[ERROR] Buffer overruns in master PHY')
+ if 'slave' in ctrls:
+ sla_status = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_STATUS)
+ sla_overruns = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_OVERRUNS)
+ if (sla_status & MAC_STATUS_HARD_ERR_MSK):
+ print('[ERROR] Hard errors in slave PHY')
+ if (sla_overruns > 0):
+ print('[ERROR] Buffer overruns in slave PHY')
+ # Compure latency
+ mst_samps = 65536.0*ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_BIST_SAMPS)
+ mst_errors = 1.0*ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_BIST_ERRORS)
+ if (mst_samps != 0):
+ mst_latency_cyc = 16.0*((mst_status & MAC_STATUS_BIST_LATENCY_MSK) >> MAC_STATUS_BIST_LATENCY_OFFSET)
+ time_diff = stop_time - start_time
+ print('[INFO] BIST Complete!')
+ print('- Elapsed Time = ' + str(time_diff))
+ print('- Max BER (Bit Error Ratio) = %.4g (%d errors out of %d)'%((mst_errors+1)/mst_samps,mst_errors,mst_samps))
+ print('- Max Roundtrip Latency = %.1fus'%(1e6*mst_latency_cyc/AURORA_CLK_RATE))
+ print('- Approx Throughput = %.0fMB/s'%((8e-6*mst_samps)/time_diff.total_seconds()))
+ else:
+ print('[ERROR] BIST Failed!')
+ # Drain and Cleanup
+ print('[INFO] Cleaning up...')
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ time.sleep(0.5)
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+
+def run_latency_loopback_bist(ctrls, duration, rate_tuple):
+ (rate_sett, rate) = rate_tuple
+ print('[INFO] Running Latency Loopback BIST at %.0fMB/s for %.0fs...'%(rate,duration))
+ # Determine offsets
+ (mst_port, link_up) = get_aurora_info(ctrls['master'])
+ MST_MAC_REG_BASE = SFP0_MAC_REG_BASE if (mst_port == 0) else SFP1_MAC_REG_BASE
+ if 'slave' in ctrls:
+ (sla_port, link_up) = get_aurora_info(ctrls['slave'])
+ SLA_MAC_REG_BASE = SFP0_MAC_REG_BASE if (sla_port == 0) else SFP1_MAC_REG_BASE
+ # Reset both PHYS
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET)
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_PHY_RESET)
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ time.sleep(1.5)
+
+ # Put the slave in loopback mode and the master in BIST mode
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_LOOPBACK_EN)
+ if rate_sett == 0:
+ master_ctrl = MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN
+ else:
+ master_ctrl = ((rate_sett - 1) << MAC_CTRL_BIST_RATE_OFFSET)|MAC_CTRL_BIST_GEN_EN|MAC_CTRL_BIST_CHECKER_EN
+
+ start_time = datetime.datetime.now()
+ latencies = []
+ mst_lock_errors = 0
+ mst_hard_errors = 0
+ mst_overruns = 0
+ try:
+ for i in tqdm.tqdm(list(range(duration*10)), desc='[INFO] Progress'):
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, master_ctrl)
+ # Wait and check if BIST locked
+ time.sleep(0.05)
+ mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS)
+ if (not (mst_status & MAC_STATUS_BIST_LOCKED_MSK)):
+ mst_lock_errors += 1
+ # Turn off the BIST generator
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ # Validate status and no overruns
+ mst_status = ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_STATUS)
+ mst_overruns += ctrls['master'].peek(MST_MAC_REG_BASE + MAC_REG_OVERRUNS)
+ if (mst_status & MAC_STATUS_HARD_ERR_MSK):
+ mst_hard_errors += 1
+ time.sleep(0.05)
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ # Compure latency
+ mst_latency_cyc = 16.0*((mst_status & MAC_STATUS_BIST_LATENCY_MSK) >> MAC_STATUS_BIST_LATENCY_OFFSET)
+ mst_latency_us = 1e6*mst_latency_cyc/AURORA_CLK_RATE
+ latencies.append(mst_latency_us)
+ except KeyboardInterrupt:
+ print('[WARNING] Operation cancelled by user.')
+ stop_time = datetime.datetime.now()
+ # Report
+ if (mst_lock_errors > 0):
+ print('[ERROR] BIST engine did not lock onto a PRBS word %d times!'%(mst_lock_errors))
+ if (mst_hard_errors > 0):
+ print('[ERROR] There were %d hard errors in master PHY'%(mst_hard_errors))
+ if (mst_overruns > 0):
+ print('[ERROR] There were %d buffer overruns in master PHY'%(mst_overruns))
+
+ print('[INFO] BIST Complete!')
+ print('- Elapsed Time = ' + str(stop_time - start_time))
+ print('- Roundtrip Latency Mean = %.2fus'%(numpy.mean(latencies)))
+ print('- Roundtrip Latency Stdev = %.2fus'%(numpy.std(latencies)))
+ # Turn off BIST loopback
+ time.sleep(0.5)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ if 'slave' in ctrls:
+ sla_status = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_STATUS)
+ sla_overruns = ctrls['slave'].peek(SLA_MAC_REG_BASE + MAC_REG_OVERRUNS)
+ if (sla_status & MAC_STATUS_HARD_ERR_MSK):
+ print('[ERROR] Hard errors in slave PHY')
+ if (sla_overruns > 0):
+ print('[ERROR] Buffer overruns in slave PHY')
+ # Drain and Cleanup
+ print('[INFO] Cleaning up...')
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, MAC_CTRL_BIST_CHECKER_EN)
+ time.sleep(0.5)
+ ctrls['master'].poke(MST_MAC_REG_BASE + MAC_REG_CTRL, 0)
+ if 'slave' in ctrls:
+ ctrls['slave'].poke(SLA_MAC_REG_BASE + MAC_REG_CTRL, 0)
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = argparse.ArgumentParser(description='Controller for the USRP X3X0 Aurora BIST Engine')
+ parser.add_argument('--master', type=str, default=None, required=True, help='IP Address of master USRP-X3X0 device')
+ parser.add_argument('--slave', type=str, default=None, help='IP Address of slave USRP-X3X0 device')
+ parser.add_argument('--test', type=str, default='ber', choices=['ber', 'latency'], help='Type of test to run')
+ parser.add_argument('--duration', type=int, default=10, help='Duration of test in seconds')
+ parser.add_argument('--rate', type=int, default=1245, help='BIST throughput in MB/s')
+ return parser.parse_args()
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if (options.duration < 0 or options.duration > BIST_MAX_TIME_LIMIT):
+ raise Exception('Invalid duration. Min = 0s and Max = %ds'%(BIST_MAX_TIME_LIMIT))
+
+ ctrls = dict()
+ ctrls['master'] = x300_debug.ctrl_socket(addr=options.master)
+ if options.slave:
+ ctrls['slave'] = x300_debug.ctrl_socket(addr=options.slave)
+
+ # Report device and core info
+ links_up = True
+ for node in ctrls:
+ print('[INFO] ' + node.upper() + ':')
+ ctrl = ctrls[node]
+ (aur_port, link_up) = get_aurora_info(ctrl)
+ if aur_port >= 0:
+ status_str = str(aur_port) + (' UP' if link_up else ' DOWN')
+ else:
+ status_str = 'Not Detected!'
+ print('- Mgmt IP Addr : ' + (options.master if node == 'master' else options.slave))
+ print('- Aurora Status : Port ' + status_str)
+ links_up = links_up & link_up
+
+ # Sanity check
+ if not links_up:
+ print('[ERROR] At least one of the links is down. Cannot proceed.')
+ exit(1)
+
+ # Run BIST
+ if options.test == 'ber':
+ run_ber_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate))
+ else:
+ run_latency_loopback_bist(ctrls, options.duration, get_rate_setting(options.rate))
+
diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py
index c9bcbb138..e134a7275 100755
--- a/firmware/usrp3/x300/x300_debug.py
+++ b/firmware/usrp3/x300/x300_debug.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2010-2011 Ettus Research LLC
+# Copyright 2010-2014 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,27 +21,36 @@ import math
import socket
import struct
-
########################################################################
# constants
########################################################################
-B250_FW_COMMS_UDP_PORT = 49152
+X300_FW_COMMS_UDP_PORT = 49152
+
+X300_FW_COMMS_FLAGS_ACK = 1
+X300_FW_COMMS_FLAGS_ERROR = 2
+X300_FW_COMMS_FLAGS_POKE32 = 4
+X300_FW_COMMS_FLAGS_PEEK32 = 8
+
+X300_FIXED_PORTS = 5
-B250_FW_COMMS_FLAGS_ACK = 1
-B250_FW_COMMS_FLAGS_ERROR = 2
-B250_FW_COMMS_FLAGS_POKE32 = 4
-B250_FW_COMMS_FLAGS_PEEK32 = 8
+X300_ZPU_MISC_SR_BUS_OFFSET = 0xA000
+X300_ZPU_XBAR_SR_BUS_OFFSET = 0xB000
+
+# Settings register bus addresses (hangs off ZPU wishbone bus)
+# Multiple by 4 as ZPU wishbone bus is word aligned
+X300_SR_NUM_CE = X300_ZPU_MISC_SR_BUS_OFFSET + 4*7
+X300_SR_RB_ADDR_XBAR = X300_ZPU_MISC_SR_BUS_OFFSET + 4*128
+# Readback addresses
+X300_RB_CROSSBAR = X300_ZPU_MISC_SR_BUS_OFFSET + 4*128
#UDP_CTRL_PORT = 49183
UDP_MAX_XFER_BYTES = 1024
UDP_TIMEOUT = 3
-#USRP2_FW_PROTO_VERSION = 11 #should be unused after r6
#REG_ARGS_FMT = '!LLLLLB15x'
#REG_IP_FMT = '!LLLL20x'
REG_PEEK_POKE_FMT = '!LLLL'
-
_seq = -1
def seq():
global _seq
@@ -66,7 +75,7 @@ class ctrl_socket(object):
def __init__(self, addr):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.settimeout(UDP_TIMEOUT)
- self._sock.connect((addr, B250_FW_COMMS_UDP_PORT))
+ self._sock.connect((addr, X300_FW_COMMS_UDP_PORT))
self.set_callbacks(lambda *a: None, lambda *a: None)
#self.init_update() #check that the device is there
@@ -78,47 +87,71 @@ class ctrl_socket(object):
self._sock.send(pkt)
return self._sock.recv(UDP_MAX_XFER_BYTES)
- def read_router_stats(self):
+ def read_router_stats(self, blocks=None, ignore=None):
+ # Readback number of CEs
+ num_ports = self.peek(X300_SR_NUM_CE) + X300_FIXED_PORTS
+ ports = ['eth0', 'eth1', 'pcie', 'radio0', 'radio1'] + ["Block{num}".format(num=x) for x in range(num_ports-X300_FIXED_PORTS)]
+ if blocks is None:
+ print("\nNote: Using default CE port names (use --blocks to specify)\n")
+ else:
+ user_ports = [x.strip() for x in blocks.split(",") if len(x.strip())]
+ for idx, user_port in enumerate(user_ports):
+ ports[idx + X300_FIXED_PORTS] = user_port
+ if ignore is None:
+ ignore = []
+ else:
+ ignore = [int(x.strip()) for x in ignore.split(",") if len(x.strip())]
+ print("Egress Port "),
+ # Write out xbar ports
+ PORT_MAX_LEN = 12
+ for idx, in_prt in enumerate(ports):
+ if idx in ignore:
+ continue
+ print "{spaces}{name}".format(spaces=(" "*max(0, PORT_MAX_LEN-len(in_prt))), name=in_prt),
print
- print(" "),
- ports = [' eth0',' eth1',' radio0',' radio1',' compute0',' compute1',' compute2',' pcie']
- for in_prt in ports:
- print("%s" % in_prt),
- print(" Egress Port")
- print(" "),
- for in_prt in range (0, 8):
- print("____________"),
+ print(" "*(PORT_MAX_LEN+1)),
+ for in_prt in range(num_ports-len(ignore)):
+ print("_" * (PORT_MAX_LEN-1) + " "),
print
- for in_prt in range (0, 8):
- print("%s |" % ports[in_prt]),
- for out_prt in range (0, 8):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)
- in_pkt = self.send_and_recv(out_pkt)
- (flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek returns error code")
+ for in_prt, port_name in enumerate(ports):
+ if in_prt in ignore:
+ continue
+ print "{spaces}{name} |".format(spaces=(" "*max(0, PORT_MAX_LEN-len(port_name))), name=port_name),
+ for out_prt in range(num_ports):
+ if out_prt in ignore:
+ continue
+ self.poke(X300_SR_RB_ADDR_XBAR,(in_prt*num_ports+out_prt))
+ data = self.peek(X300_RB_CROSSBAR)
print("%10d " % (data)),
print
print
print("Ingress Port")
print
+ def peek_print(self,peek_addr):
+ peek_data = self.peek(peek_addr)
+ print("PEEK of address %d(0x%x) reads %d(0x%x)" % (peek_addr,peek_addr,peek_data,peek_data))
+ return peek_data
def peek(self,peek_addr):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
- print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data))
+ if flags & X300_FW_COMMS_FLAGS_ERROR == X300_FW_COMMS_FLAGS_ERROR:
+ raise Exception("X300 peek of address %d returns error code" % (addr))
+ return data
+
+ def poke_print(self,poke_addr,poke_data):
+ print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data))
+ return(self.poke(poke_addr,poke_data))
def poke(self,poke_addr,poke_data):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_POKE32|B250_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_POKE32|X300_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
- print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data) )
+ if flags & X300_FW_COMMS_FLAGS_ERROR == X300_FW_COMMS_FLAGS_ERROR:
+ raise Exception("X300 peek of address %d returns error code" % (addr))
+ return data
########################################################################
@@ -126,14 +159,14 @@ class ctrl_socket(object):
########################################################################
def get_options():
parser = optparse.OptionParser()
- parser.add_option("--addr", type="string", help="USRP-N2XX device address", default='')
- parser.add_option("--list", action="store_true", help="list possible network devices", default=False)
- parser.add_option("--peek", type="int", help="Read from memory map", default=None)
- parser.add_option("--poke", type="int", help="Write to memory map", default=None)
- parser.add_option("--data", type="int", help="Data for poke", default=None)
- parser.add_option("--stats", action="store_true", help="Display SuperMIMO Network Stats", default=False)
+ parser.add_option("--addr", type="string", help="USRP-X300 device address", default='')
+ parser.add_option("--stats", action="store_true", help="Display RFNoC Crossbar Stats", default=False)
+ parser.add_option("--peek", type="int", help="Read from memory map", default=None)
+ parser.add_option("--poke", type="int", help="Write to memory map", default=None)
+ parser.add_option("--data", type="int", help="Data for poke", default=None)
+ parser.add_option("--blocks", help="List names of blocks (post-radio)", default=None)
+ parser.add_option("--ignore", help="List of ports to ignore", default=None)
(options, args) = parser.parse_args()
-
return options
@@ -143,25 +176,18 @@ def get_options():
if __name__=='__main__':
options = get_options()
-
- if options.list:
- print('Possible network devices:')
- print(' ' + '\n '.join(enumerate_devices()))
- exit()
-
if not options.addr: raise Exception('no address specified')
status = ctrl_socket(addr=options.addr)
if options.stats:
- status.read_router_stats()
-
+ status.read_router_stats(options.blocks, options.ignore)
if options.peek is not None:
addr = options.peek
- status.peek(addr)
+ status.peek_print(addr)
if options.poke is not None and options.data is not None:
addr = options.poke
data = options.data
- status.poke(addr,data)
+ status.poke_print(addr,data)
diff --git a/firmware/usrp3/x300/x300_defs.h b/firmware/usrp3/x300/x300_defs.h
index c4011bd12..e9e9f34fa 100644
--- a/firmware/usrp3/x300/x300_defs.h
+++ b/firmware/usrp3/x300/x300_defs.h
@@ -4,11 +4,11 @@
#ifndef INCLUDED_X300_DEFS_H
#define INCLUDED_X300_DEFS_H
-#define CPU_CLOCK 166666667
+#define CPU_CLOCK 83333333
#define MAIN_RAM_BASE 0x0000
#define PKT_RAM0_BASE 0x8000
-#define XGE0_BASE 0xC000
-#define XGE1_BASE 0xD000
+#define SFP0_MAC_BASE 0xC000
+#define SFP1_MAC_BASE 0xD000
#define BOOT_LDR_BASE 0xFC00
#define UART0_BASE 0xfd00
#define UART0_BAUD 115200
@@ -21,9 +21,6 @@
#define RB0_BASE 0xa000 //same as set
#define SETXB_BASE 0xb000
-#define ETH1G
-//#define ETH10G
-
//eeprom map for mboard addrs
#define MBOARD_EEPROM_ADDR 0x50
@@ -36,6 +33,7 @@ static const int SR_SFPP_CTRL = 4;
static const int SR_SPI = 32;
static const int SR_ETHINT0 = 40;
static const int SR_ETHINT1 = 56;
+static const int SR_RB_ADDR = 128;
//led shifts for SR_LEDS
static const int LED_ACT1 = (1 << 5);
@@ -49,11 +47,12 @@ static const int LED_LINKACT = (1 << 0);
static const int RB_COUNTER = 0;
static const int RB_SPI_RDY = 1;
static const int RB_SPI_DATA = 2;
-static const int RB_ETH_TYPE0 = 4;
-static const int RB_ETH_TYPE1 = 5;
+static const int RB_SFP0_TYPE = 4;
+static const int RB_SFP1_TYPE = 5;
static const int RB_FPGA_COMPAT = 6;
-static const int RB_SFPP_STATUS0 = 8;
-static const int RB_SFPP_STATUS1 = 9;
+static const int RB_SFP0_STATUS = 8;
+static const int RB_SFP1_STATUS = 9;
+static const int RB_XBAR = 128;
// Bootloader Memory Map
static const int BL_ADDRESS = 0;
@@ -73,4 +72,9 @@ static const int BL_DATA = 1;
#define ETH_FRAMER_DST_UDP_MAC 6
#define ETH_FRAMER_DST_MAC_LO 7
+// SFP type constants
+#define RB_SFP_1G_ETH 0
+#define RB_SFP_10G_ETH 1
+#define RB_SFP_AURORA 2
+
#endif /* INCLUDED_X300_DEFS_H */
diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c
index ef97412a2..57c1faa2c 100644
--- a/firmware/usrp3/x300/x300_init.c
+++ b/firmware/usrp3/x300/x300_init.c
@@ -1,7 +1,7 @@
#include "x300_init.h"
#include "x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <wb_utils.h>
#include <wb_uart.h>
#include <wb_i2c.h>
@@ -84,8 +84,8 @@ static void init_network(void)
wb_i2c_read(I2C1_BASE, MBOARD_EEPROM_ADDR, (uint8_t *)(&eeprom_map), sizeof(eeprom_map));
//determine interface number
- const size_t eth0no = wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE0))? 2 : 0;
- const size_t eth1no = wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE1))? 3 : 1;
+ const size_t eth0no = wb_peek32(SR_ADDR(RB0_BASE, RB_SFP0_TYPE))? 2 : 0;
+ const size_t eth1no = wb_peek32(SR_ADDR(RB0_BASE, RB_SFP1_TYPE))? 3 : 1;
//pick the address from eeprom or default
const eth_mac_addr_t *my_mac0 = (const eth_mac_addr_t *)pick_inited_field(&eeprom_map.mac_addr0, &default_map.mac_addr0, 6);
@@ -121,6 +121,11 @@ static void putc(void *p, char c)
#endif
}
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
+}
+
void x300_init(void)
{
//first - uart
@@ -136,6 +141,9 @@ void x300_init(void)
UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF));
UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000));
+ //Initialize cron
+ cron_init(get_counter_val, CPU_CLOCK);
+
//i2c rate init
wb_i2c_init(I2C0_BASE, CPU_CLOCK);
wb_i2c_init(I2C1_BASE, CPU_CLOCK);
@@ -151,19 +159,23 @@ void x300_init(void)
wb_poke32(SR_ADDR(SET0_BASE, SR_SW_RST), 0);
//print network summary
- for (uint8_t e = 0; e < ethernet_ninterfaces(); e++)
+ for (uint8_t sfp = 0; sfp < ethernet_ninterfaces(); sfp++)
{
- uint32_t offset = SR_ADDR(RB0_BASE, ((e==1)?RB_ETH_TYPE1:RB_ETH_TYPE0));
- UHD_FW_TRACE_FSTR(INFO, "Ethernet Port %u:", (int)e);
- UHD_FW_TRACE_FSTR(INFO, "-- PHY: %s", ((wb_peek32(offset)==1) ? "10Gbps" : "1Gbps"));
- UHD_FW_TRACE_FSTR(INFO, "-- MAC: %s", mac_addr_to_str(u3_net_stack_get_mac_addr(e)));
- UHD_FW_TRACE_FSTR(INFO, "-- IP: %s", ip_addr_to_str(u3_net_stack_get_ip_addr(e)));
- UHD_FW_TRACE_FSTR(INFO, "-- SUBNET: %s", ip_addr_to_str(u3_net_stack_get_subnet(e)));
- UHD_FW_TRACE_FSTR(INFO, "-- BCAST: %s", ip_addr_to_str(u3_net_stack_get_bcast(e)));
+ uint32_t sfp_type = wb_peek32(SR_ADDR(RB0_BASE, ((sfp==1) ? RB_SFP1_TYPE : RB_SFP0_TYPE)));
+ UHD_FW_TRACE_FSTR(INFO, "SFP+ Port %u:", (int)sfp);
+ if (sfp_type == RB_SFP_AURORA) {
+ UHD_FW_TRACE (INFO, "-- PHY: 10Gbps Aurora");
+ } else {
+ UHD_FW_TRACE_FSTR(INFO, "-- PHY: %s", (sfp_type == RB_SFP_10G_ETH) ? "10Gbps Ethernet" : "1Gbps Ethernet");
+ UHD_FW_TRACE_FSTR(INFO, "-- MAC: %s", mac_addr_to_str(u3_net_stack_get_mac_addr(sfp)));
+ UHD_FW_TRACE_FSTR(INFO, "-- IP: %s", ip_addr_to_str(u3_net_stack_get_ip_addr(sfp)));
+ UHD_FW_TRACE_FSTR(INFO, "-- SUBNET: %s", ip_addr_to_str(u3_net_stack_get_subnet(sfp)));
+ UHD_FW_TRACE_FSTR(INFO, "-- BCAST: %s", ip_addr_to_str(u3_net_stack_get_bcast(sfp)));
+ }
}
// For eth interfaces, initialize the PHY's
- mdelay(100);
+ sleep_ms(100);
ethernet_init(0);
ethernet_init(1);
}
diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c
index 125459583..f255fe96c 100644
--- a/firmware/usrp3/x300/x300_main.c
+++ b/firmware/usrp3/x300/x300_main.c
@@ -6,7 +6,6 @@
#include "xge_phy.h"
#include "ethernet.h"
#include "chinch.h"
-#include "mdelay.h"
#include <wb_utils.h>
#include <wb_uart.h>
@@ -247,18 +246,16 @@ static void handle_claim(void)
/***********************************************************************
* LED blinky logic and support utilities
**********************************************************************/
-static uint32_t get_xbar_total(const uint8_t port)
+static uint32_t get_xbar_total(const uint32_t port)
{
- #define get_xbar_stat(in_prt, out_prt) \
- wb_peek32(RB0_BASE+256+(((in_prt)*8+(out_prt))*4))
+ static const uint32_t NUM_PORTS = 16;
uint32_t total = 0;
- for (size_t i = 0; i < 8; i++)
+ for (uint32_t i = 0; i < NUM_PORTS; i++)
{
- total += get_xbar_stat(port, i);
- }
- for (size_t i = 0; i < 8; i++)
- {
- total += get_xbar_stat(i, port);
+ wb_poke32(SET0_BASE + SR_RB_ADDR*4, (NUM_PORTS*port + i));
+ total += wb_peek32(RB0_BASE + RB_XBAR*4);
+ wb_poke32(SET0_BASE + SR_RB_ADDR*4, (NUM_PORTS*i + port));
+ total += wb_peek32(RB0_BASE + RB_XBAR*4);
}
if (port < 2) //also netstack if applicable
{
@@ -280,10 +277,10 @@ static size_t popcntll(uint64_t num)
static void update_leds(void)
{
//update activity status for all ports
- uint64_t activity_shreg[8];
- for (size_t i = 0; i < 8; i++)
+ uint64_t activity_shreg[16];
+ for (uint32_t i = 0; i < 16; i++)
{
- static uint32_t last_total[8];
+ static uint32_t last_total[16];
const uint32_t total = get_xbar_total(i);
activity_shreg[i] <<= 1;
activity_shreg[i] |= (total == last_total[i])? 0 : 1;
@@ -321,8 +318,10 @@ static void garp(void)
count = 0;
for (size_t e = 0; e < ethernet_ninterfaces(); e++)
{
- if (!ethernet_get_link_up(e)) continue;
- u3_net_stack_send_arp_request(e, u3_net_stack_get_ip_addr(e));
+ if (wb_peek32(SR_ADDR(RB0_BASE, e == 0 ? RB_SFP0_TYPE : RB_SFP1_TYPE)) != RB_SFP_AURORA) {
+ if (!ethernet_get_link_up(e)) continue;
+ u3_net_stack_send_arp_request(e, u3_net_stack_get_ip_addr(e));
+ }
}
}
@@ -447,7 +446,7 @@ int main(void)
{
poll_sfpp_status(0); // Every so often poll XGE Phy to look for SFP+ hotplug events.
poll_sfpp_status(1); // Every so often poll XGE Phy to look for SFP+ hotplug events.
- handle_link_state(); //deal with router table update
+ //handle_link_state(); //deal with router table update
handle_claim(); //deal with the host claim register
update_leds(); //run the link and activity leds
garp(); //send periodic garps