aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/usrp3
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usrp3')
-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.c9
-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_debug.py70
-rw-r--r--firmware/usrp3/x300/x300_init.c12
-rw-r--r--firmware/usrp3/x300/x300_main.c1
29 files changed, 2805 insertions, 96 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..e9c18528d 100644
--- a/firmware/usrp3/lib/ethernet.c
+++ b/firmware/usrp3/lib/ethernet.c
@@ -21,7 +21,7 @@
#endif
#include "../x300/x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <trace.h>
#include "wb_i2c.h"
#include "wb_utils.h"
@@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
int x;
// Delay read of SFPP
if (delay_ms)
- mdelay(delay_ms);
+ sleep_ms(delay_ms);
// Read ID code from SFP
x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
// I2C Error?
@@ -312,10 +312,9 @@ static void xge_mac_init(const uint32_t base)
}
// base is pointer to XGE MAC on Wishbone.
-static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
+static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port)
{
int x;
- uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg;
// Read LASI Ctrl register to capture state.
//y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);
UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence.");
@@ -323,8 +322,10 @@ static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);
x = x | (1 << 15);
write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x);
+ uint32_t loopCount = 0;
while(x&(1<<15)) {
x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port);
+ if( loopCount++ > 200 ) break; // usually succeeds after 22 or 23 polls
}
}
diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c
new file mode 100644
index 000000000..b4257c96f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spi_flash.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <flash/spi_flash.h>
+
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops)
+{
+ flash->device = device;
+ flash->ops = ops;
+ flash->state = IDLE;
+ flash->last_offset = 0;
+ flash->id = ops->read_id(device);
+}
+
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ flash->ops->read(flash->device, offset, buf, num_bytes);
+}
+
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset)
+{
+ if (flash->ops->erase_sector_dispatch(flash->device, offset)) {
+ return flash->ops->erase_sector_commit(flash->device, offset);
+ } else {
+ return false;
+ }
+}
+
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) {
+ return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes);
+ } else {
+ return false;
+ }
+}
diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
new file mode 100644
index 000000000..244115b6f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <cron.h>
+#include <trace.h>
+#include <string.h> //for memset, memcpy
+
+#define S25FLXX_CMD_WIDTH 8
+#define S25FLXX_ADDR_WIDTH 24
+
+/* S25FLxx-specific commands */
+#define S25FLXX_CMD_READID 0x90 /* Read Manufacturer and Device Identification */
+#define S25FLXX_CMD_READSIG 0xAB /* Read Electronic Signature (Will release from Deep PD) */
+#define S25FLXX_CMD_READ 0x03 /* Read Data Bytes */
+#define S25FLXX_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */
+
+#define S25FLXX_CMD_WREN 0x06 /* Write Enable */
+#define S25FLXX_CMD_WRDI 0x04 /* Write Disable */
+
+#define S25FLXX_CMD_PP 0x02 /* Page Program */
+#define S25FLXX_CMD_SE 0xD8 /* Sector Erase */
+#define S25FLXX_CMD_BE 0xC7 /* Bulk Erase */
+#define S25FLXX_CMD_DP 0xB9 /* Deep Power-down */
+
+#define S25FLXX_CMD_RDSR 0x05 /* Read Status Register */
+#define S25FLXX_CMD_WRSR 0x01 /* Write Status Register */
+
+#define S25FLXX_STATUS_WIP 0x01 /* Write in Progress */
+#define S25FLXX_STATUS_E_ERR 0x20 /* Erase Error Occured */
+#define S25FLXX_STATUS_P_ERR 0x40 /* Programming Error Occured */
+
+#define S25FLXX_SECTOR_ERASE_TIME_MS 750 //Spec: 650ms
+#define S25FLXX_PAGE_WRITE_TIME_MS 1 //Spec: 750us
+
+#define S25FLXX_SMALL_SECTORS_PER_LOGICAL 16 //16 4-kB physical sectors per logical sector
+#define S25FLXX_LARGE_SECTOR_BASE 0x20000 //Large physical sectors start at logical sector 2
+
+inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash)
+{
+ uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF;
+ wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */);
+ return status;
+}
+
+inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms)
+{
+ uint32_t start_ticks = cron_get_ticks();
+ do {
+ if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) {
+ return true;
+ }
+ } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms);
+
+ return false; // Timed out
+}
+
+inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled)
+{
+ uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI;
+ wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH);
+}
+
+const spi_flash_ops_t spif_spsn_s25flxx_ops =
+{
+ .read_id = spif_spsn_s25flxx_read_id,
+ .read = spif_spsn_s25flxx_read,
+ .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch,
+ .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit,
+ .erase_sector_busy = spif_spsn_s25flxx_device_busy,
+ .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch,
+ .write_page_commit = spif_spsn_s25flxx_write_page_commit,
+ .write_page_busy = spif_spsn_s25flxx_device_busy
+};
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations()
+{
+ return &spif_spsn_s25flxx_ops;
+}
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash)
+{
+ wb_spi_slave_select(flash->bus);
+ uint32_t command = S25FLXX_CMD_READID << 24;
+ wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32);
+ uint16_t id = 0;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16);
+ wb_spi_slave_deselect(flash->bus);
+ return id;
+}
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 5 byte instruction tranfer:
+ //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0)
+ uint8_t read_cmd[5];
+ read_cmd[4] = S25FLXX_CMD_FAST_READ;
+ *((uint32_t*)(read_cmd + 3)) = (offset << 8);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8);
+
+ //Read up to 4 bytes at a time until done
+ uint8_t data_sw[16], data[16];
+ size_t xact_size = 16;
+ unsigned char *bytes = (unsigned char *) buf;
+ for (size_t i = 0; i < num_bytes; i += 16) {
+ if (xact_size > num_bytes - i) xact_size = num_bytes - i;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8);
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k];
+ }
+ for (size_t j = 0; j < xact_size; j++) {
+ *bytes = data[j];
+ bytes++;
+ }
+ }
+ wb_spi_slave_deselect(flash->bus);
+}
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Sanity check sector size
+ if (offset % flash->sector_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size.");
+ return false;
+ }
+
+ if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //Send sector erase command
+ uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF);
+ wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Poll status until write done
+ uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1;
+ bool status = false;
+ for (uint8_t i = 0; i < phy_sector_count && !status; i++) {
+ status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS);
+ }
+ if (!status) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset);
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return status;
+}
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (num_bytes == 0 || num_bytes > flash->page_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size.");
+ return false;
+ }
+ if (num_bytes > (flash->sector_size * flash->num_sectors)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary.");
+ return false;
+ }
+
+ //Wait until ready and enable write enabled
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 4 byte instruction tranfer:
+ //PP_CMD, ADDR2, ADDR1, ADDR0
+ uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32);
+
+ //Write the page 16 bytes at a time.
+ uint8_t bytes_sw[16];
+ uint8_t* bytes = (uint8_t*) buf;
+ for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) {
+ const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16;
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k];
+ }
+ wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8);
+ bytes += xact_size;
+ }
+ wb_spi_slave_deselect(flash->bus);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ //Wait until write done
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time.");
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return true;
+}
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash)
+{
+ return (_spif_read_status(flash) & S25FLXX_STATUS_WIP);
+}
+
diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c
new file mode 100644
index 000000000..0cc931a76
--- /dev/null
+++ b/firmware/usrp3/lib/fw_comm_protocol.c
@@ -0,0 +1,105 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+
+#include <trace.h>
+#include <string.h> //memcmp
+
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ uint32_t iface_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback)
+{
+ bool send_response = false;
+
+ uint16_t signature = request->id;
+ uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
+ uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
+ if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
+ version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
+ product == product_id) //Verify device
+ {
+ //Request is valid. Copy it into the reply.
+ memcpy(response, request, sizeof(fw_comm_pkt_t));
+
+ //Start assuming no error
+ response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
+
+ //Otherwise, run the command set by the flags
+ switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
+ case FW_COMM_CMD_ECHO: {
+ UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
+ response->data_words = 1;
+ response->data[0] = iface_id;
+ } break;
+
+ case FW_COMM_CMD_POKE32: {
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
+ request->addr,*(request->data));
+ poke_callback(request->addr, *(request->data));
+ } break;
+
+ case FW_COMM_CMD_PEEK32: {
+ *(response->data) = peek_callback(request->addr);
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
+ request->addr,*(response->data));
+ } break;
+
+ case FW_COMM_CMD_BLOCK_POKE32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
+ }
+ } break;
+
+ case FW_COMM_CMD_BLOCK_PEEK32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
+ } break;
+
+ default: {
+ UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
+ response->flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+
+ //Send a reply if ack requested
+ send_response = (request->flags & FW_COMM_FLAGS_ACK);
+ } else { //Size, protocol, product check failed
+ UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
+ send_response = false;
+ }
+ return send_response;
+}
diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c
deleted file mode 100644
index 6d2742206..000000000
--- a/firmware/usrp3/lib/mdelay.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- * Copyright 2009 Ettus Research LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mdelay.h"
-#include "wb_utils.h"
-#include "printf.h"
-#include <stdint.h>
-//IJB FIXME.
-#include "../x300/x300_defs.h"
-
-void mdelay(int ms){
- for(int i = 0; i < ms; i++){
- static const uint32_t num_ticks = CPU_CLOCK/1000;
- const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
- // printf("DEBUG: Counter is %d\n",ticks_begin);
- while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
- /*NOP*/
- }
- }
-}
diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c
new file mode 100644
index 000000000..04904feea
--- /dev/null
+++ b/firmware/usrp3/lib/wb_spi.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <trace.h>
+
+typedef struct {
+ volatile uint32_t data0;
+ volatile uint32_t data1;
+ volatile uint32_t data2;
+ volatile uint32_t data3;
+ volatile uint32_t ctrl_status;
+ volatile uint32_t clkdiv;
+ volatile uint32_t slavesel;
+} wb_spi_regs_t;
+
+#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base)
+
+// Masks for different parts of CTRL reg
+#define WB_SPI_CTRL_AUTO_SS (1 << 13)
+#define WB_SPI_CTRL_IE (1 << 12)
+#define WB_SPI_CTRL_LSB (1 << 11)
+#define WB_SPI_CTRL_TXNEG (1 << 10)
+#define WB_SPI_CTRL_RXNEG (1 << 9)
+#define WB_SPI_CTRL_GO_BSY (1 << 8)
+#define WB_SPI_CTRL_LENGTH(x) (x & 0x7F)
+
+static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave)
+{
+ uint32_t flags = 0;
+ //If the SPI slave samples on the rising edge then shift
+ //data out on the falling edge.
+ if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG;
+ //If the SPI slave drives on the rising edge then shift
+ //data in on the falling edge.
+ if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG;
+ if (slave->lsb_first) flags |= WB_SPI_CTRL_LSB;
+ return flags;
+}
+
+static inline void _wait_for_xfer(const wb_spi_slave_t* slave)
+{
+ while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) {
+ /*NOP*/
+ }
+}
+
+void wb_spi_init(const wb_spi_slave_t* slave)
+{
+ WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div;
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+
+ //Do a dummy transaction with no slave selected to prime the engine
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave);
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+ _wait_for_xfer(slave);
+}
+
+void _wb_spi_transact_buf(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length,
+ bool auto_slave_sel)
+{
+ if (length == 0) return;
+
+ //Wait for previous transaction to finish
+ _wait_for_xfer(slave);
+
+ //Write SPI data register(s)
+ if (mosi_buf) {
+ uint8_t* mosi_bytes = (uint8_t*) mosi_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ word = (uint32_t) mosi_bytes[0];
+ } else if (bits_left <= 16) {
+ word = (((uint32_t) mosi_bytes[1]) << 0) |
+ (((uint32_t) mosi_bytes[0]) << 8);
+ } else if (bits_left <= 24) {
+ word = (((uint32_t) mosi_bytes[2]) << 0) |
+ (((uint32_t) mosi_bytes[1]) << 8) |
+ (((uint32_t) mosi_bytes[0]) << 16);
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ }
+ bits_left = 0;
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ mosi_bytes += 4;
+ bits_left -= 32;
+ }
+
+ switch (reg_index) {
+ case 0: WB_SPI_REGS(slave->base)->data0 = word; break;
+ case 1: WB_SPI_REGS(slave->base)->data1 = word; break;
+ case 2: WB_SPI_REGS(slave->base)->data2 = word; break;
+ case 3: WB_SPI_REGS(slave->base)->data3 = word; break;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+
+ //Compute flags for slave and write control register
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave);
+ if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS;
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl;
+
+ // Tell it which SPI slave device to access
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+
+ //Go go go!
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+
+ if (rw_mode == WRITE_READ) {
+ //Wait for SPI read operation to complete
+ _wait_for_xfer(slave);
+
+ if (miso_buf) {
+ //Read SPI data registers
+ uint8_t* miso_bytes = (uint8_t*) miso_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ switch (reg_index) {
+ case 0: word = WB_SPI_REGS(slave->base)->data0; break;
+ case 1: word = WB_SPI_REGS(slave->base)->data1; break;
+ case 2: word = WB_SPI_REGS(slave->base)->data2; break;
+ case 3: word = WB_SPI_REGS(slave->base)->data3; break;
+ }
+
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ miso_bytes[0] = word & 0xFF;
+ } else if (bits_left <= 16) {
+ miso_bytes[1] = word & 0xFF;
+ miso_bytes[0] = (word >> 8) & 0xFF;
+ } else if (bits_left <= 24) {
+ miso_bytes[2] = word & 0xFF;
+ miso_bytes[1] = (word >> 8) & 0xFF;
+ miso_bytes[0] = (word >> 16) & 0xFF;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ }
+ bits_left = 0;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ miso_bytes += 4;
+ bits_left -= 32;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+ }
+}
+
+void wb_spi_transact(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true);
+}
+
+void wb_spi_transact_man_ss(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false);
+}
+
+void wb_spi_slave_select(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually select slave
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+}
+
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually deselect slave
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+}
diff --git a/firmware/usrp3/n230/CMakeLists.txt b/firmware/usrp3/n230/CMakeLists.txt
new file mode 100644
index 000000000..6247477f0
--- /dev/null
+++ b/firmware/usrp3/n230/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# Copyright 2010-2014,2016 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/../../host/lib/usrp/n230)
+
+list(APPEND n230_sources n230_eeprom.c n230_eth_handlers.c n230_init.c n230_main.c)
+
+########################################################################
+set(GEN_OUTPUTS_BIN_SIZE 0x7fff)
+
+add_executable(n230_main.elf ${n230_sources})
+target_link_libraries(n230_main.elf usrp3fw)
+GEN_OUTPUTS(n230_main.elf n230)
+
+#INSTALL(
+# FILES ${CMAKE_CURRENT_BINARY_DIR}/n230_main.bin
+# DESTINATION share/uhd/images
+# RENAME usrp_n230_fw.bin
+#)
diff --git a/firmware/usrp3/n230/n230_burner.py b/firmware/usrp3/n230/n230_burner.py
new file mode 100755
index 000000000..7b9920de7
--- /dev/null
+++ b/firmware/usrp3/n230/n230_burner.py
@@ -0,0 +1,359 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import os.path
+import sys
+from array import array
+
+########################################################################
+# constants
+########################################################################
+N230_FLASH_COMM_UDP_PORT = 49154
+N230_FLASH_COMM_PAYLOAD_SIZE = 128
+N230_FLASH_COMM_SECTOR_SIZE = 65536
+
+N230_FLASH_COMM_FLAGS_ACK = 0x00000001
+N230_FLASH_COMM_FLAGS_CMD_MASK = 0x00000FF0
+N230_FLASH_COMM_FLAGS_ERROR_MASK = 0xFF000000
+
+N230_FLASH_COMM_CMD_READ_NV_DATA = 0x00000010
+N230_FLASH_COMM_CMD_WRITE_NV_DATA = 0x00000020
+N230_FLASH_COMM_CMD_READ_FPGA = 0x00000030
+N230_FLASH_COMM_CMD_WRITE_FPGA = 0x00000040
+N230_FLASH_COMM_CMD_ERASE_FPGA = 0x00000050
+
+N230_FLASH_COMM_ERR_PKT_ERROR = 0x80000000
+N230_FLASH_COMM_ERR_CMD_ERROR = 0x40000000
+N230_FLASH_COMM_ERR_SIZE_ERROR = 0x20000000
+
+N230_FLASH_COMM_SAFE_IMG_BASE = 0x000000
+N230_FLASH_COMM_PROD_IMG_BASE = 0x400000
+N230_FLASH_COMM_FPGA_IMG_MAX_SIZE = 0x400000
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+
+_seq = -1
+def next_seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+def seq():
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+short = struct.Struct('>H')
+ulong = struct.Struct('>I')
+
+def unpack_flash_transaction(buf):
+ (flags, seqno, offset, size) = struct.unpack_from('!LLLL', buf)
+ check_error(flags)
+ if (seqno != seq()):
+ raise Exception("The flash transaction operation returned an incorrect sequence number")
+ data = bytes()
+ for i in xrange(16, len(buf), 1):
+ data += buf[i]
+ return (flags, offset, size, data)
+
+def pack_flash_transaction(flags, offset, size, data):
+ buf = bytes()
+ buf = struct.pack('!LLLL', flags, next_seq(), offset, size)
+ for i in range(N230_FLASH_COMM_PAYLOAD_SIZE):
+ if (i < size):
+ buf += struct.pack('!B', data[i])
+ else:
+ buf += struct.pack('!B', 0)
+ return buf
+
+def check_error(flags):
+ if flags & N230_FLASH_COMM_ERR_PKT_ERROR == N230_FLASH_COMM_ERR_PKT_ERROR:
+ raise Exception("The flash transaction operation returned a packet error")
+ if flags & N230_FLASH_COMM_ERR_CMD_ERROR == N230_FLASH_COMM_ERR_CMD_ERROR:
+ raise Exception("The flash transaction operation returned a command error")
+ if flags & N230_FLASH_COMM_ERR_SIZE_ERROR == N230_FLASH_COMM_ERR_SIZE_ERROR:
+ raise Exception("The flash transaction operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Burner class, holds a socket and send/recv routines
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr):
+ self._safe_image = False
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, N230_FLASH_COMM_UDP_PORT))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+
+ def set_safe_image(self, noprompt):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to access the safe-image stored in the flash \n'
+ '----------------------------------------------------------------------\n'
+ 'Writing a non-functional image will brick the device.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ self._safe_image = True
+ else:
+ print 'Aborted by user'
+ sys.exit(1)
+ else:
+ print '[WARNING] Operating on safe image without a prompt as requested'
+ self._safe_image = True
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send_and_recv(self, pkt):
+ self._sock.send(pkt)
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def compute_offset(self, offset):
+ base = N230_FLASH_COMM_SAFE_IMG_BASE if (self._safe_image) else N230_FLASH_COMM_PROD_IMG_BASE
+ return base + offset
+
+ def burn_fpga_to_flash(self, bitfile_path, noprompt):
+ print '[BURN] Reading ' + bitfile_path + '...'
+ with open(bitfile_path, 'rb') as bitfile:
+ header = file_bytes = bitfile.read()
+ if (self._safe_image != self.parse_bitfile_header(file_bytes)['safe-image']):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to burn a safe image into a production slot \n'
+ ' or a production image into a safe slot. \n'
+ '----------------------------------------------------------------------\n'
+ 'This is dangerous and can cause the device to boot incorrectly.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() != 'y':
+ print '[BURN] Aborted by user'
+ return
+ else:
+ print '[WARNING] Burning image to the wrong slot without a prompt as requested'
+
+ print '[BURN] Writing to flash...'
+ pkt_chunks = chunkify(file_bytes, N230_FLASH_COMM_PAYLOAD_SIZE)
+ offset = 0
+ for pkt_data in pkt_chunks:
+ pkt_data = array("B", pkt_data)
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (len(pkt_data) >= N230_FLASH_COMM_PAYLOAD_SIZE) else len(pkt_data)
+ #Erase sector
+ if (offset % N230_FLASH_COMM_SECTOR_SIZE == 0):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Write data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Increment
+ offset += N230_FLASH_COMM_PAYLOAD_SIZE
+ draw_progress_bar((((offset/N230_FLASH_COMM_PAYLOAD_SIZE)+1)*100)/len(pkt_chunks))
+ print('\n[BURN] DONE')
+
+ def parse_bitfile_header(self, header_bytes):
+ xil_header = dict()
+ n230_header = dict()
+ n230_header['valid'] = False
+ ptr = 0
+ #Field 1
+ if short.unpack(header_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(header_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
+ #Headers
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 2
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 1
+ #Fields a-d
+ for keynum in range(0, 4):
+ key = header_bytes[ptr]
+ ptr += 1
+ val_len = short.unpack(header_bytes[ptr:ptr+2])[0]
+ ptr += 2
+ val = header_bytes[ptr:ptr+val_len]
+ ptr += val_len
+ xil_header[key] = val
+ #Field e
+ ptr += 1
+ length = ulong.unpack(header_bytes[ptr:ptr+4])[0]
+ xil_header['bl'] = length #Bitstream length
+ ptr += 4
+ xil_header['hl'] = ptr #Header lengt
+
+ #Map Xilinx header field to N230 specific ones
+ if xil_header and xil_header['a'].split(';')[0] == 'n230':
+ n230_header['valid'] = True
+ n230_header['user-id'] = int(xil_header['a'].split(';')[1].split('=')[1], 16)
+ n230_header['safe-image'] = (n230_header['user-id'] >> 16 == 0x5AFE)
+ n230_header['product'] = xil_header['b']
+ n230_header['timestamp'] = xil_header['c'] + ' ' + xil_header['d']
+ n230_header['filesize'] = xil_header['hl'] + xil_header['bl']
+ return n230_header
+
+ def read_bitfile_header_from_flash(self):
+ max_header_size = 1024 #Should be enough
+ header_bytes = bytes()
+ for offset in range(0, max_header_size, N230_FLASH_COMM_PAYLOAD_SIZE):
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ header_bytes += data
+ return self.parse_bitfile_header(header_bytes)
+
+ def extract_fpga_from_flash(self, bitfile_path):
+ header = self.read_bitfile_header_from_flash();
+ if not header['valid']:
+ raise Exception("Could not detect a vaild Xilinx .bit burned into the flash")
+ max_offset = header['filesize']
+ print '[EXTRACT] Writing ' + bitfile_path + '...'
+ with open(bitfile_path, 'wb') as bitfile:
+ for i in range(0, int(math.ceil(float(max_offset)/N230_FLASH_COMM_PAYLOAD_SIZE))):
+ offset = i * N230_FLASH_COMM_PAYLOAD_SIZE
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (max_offset - offset >= N230_FLASH_COMM_PAYLOAD_SIZE) else (max_offset - offset)
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ bitfile.write(data[:size])
+ draw_progress_bar(((offset*100)/max_offset) + 1)
+ print('\n[EXTRACT] DONE')
+
+ def erase_fpga_from_flash(self):
+ print '[ERASE] Erasing image from flash...'
+ for offset in range(0, N230_FLASH_COMM_FPGA_IMG_MAX_SIZE, N230_FLASH_COMM_SECTOR_SIZE):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ draw_progress_bar(((offset+N230_FLASH_COMM_SECTOR_SIZE)*100)/N230_FLASH_COMM_FPGA_IMG_MAX_SIZE)
+ print('\n[ERASE] DONE')
+
+ def wipe_user_data(self, noprompt):
+ confirm_msg = ('-------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to erase all the user data from the flash \n'
+ '-------------------------------------------------------------------\n'
+ 'This will cause the device to lose the following:\n'
+ ' * IP Address (Will default to 192.168.10.2)\n'
+ ' * Subnet Mask (Will default to 255.255.255.2)\n'
+ ' * MAC Address\n'
+ ' * Serial Number\n'
+ ' * Hardware Revision\n'
+ ' * ...and other identification info\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ wipe_ok = True
+ else:
+ print '[WIPE] Aborted by user'
+ wipe_ok = False
+ else:
+ print '[WARNING] Wiping user data without prompt a as requested'
+ wipe_ok = True
+
+ if wipe_ok:
+ print '[WIPE] Erasing all user data from flash...'
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_NV_DATA
+ out_pkt = pack_flash_transaction(flags, 0, N230_FLASH_COMM_PAYLOAD_SIZE, [0xFF]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ print('[WIPE] DONE. Please power-cycle the device.')
+
+ def print_status(self):
+ header = self.read_bitfile_header_from_flash();
+ if header['valid']:
+ print('[STATUS] Detected a valid .bit header in the flash (Product = %s, Datestamp = %s%s)' % \
+ (header['product'], header['timestamp'], ', Safe-Image' if header['safe-image'] else ''))
+ else:
+ print('[STATUS] No .bit header detected. Either the flash is uninitialized or the image is corrupt.')
+
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--status", action="store_true", help="Print out the status of the burned image", default=False)
+ parser.add_option("--erase", action="store_true", help="Erase FPGA bitstream from flash", default=False)
+ parser.add_option("--burn", type="string", help="Path to FPGA bitstream (.bit) to burn to flash", default=None)
+ parser.add_option("--extract", type="string", help="Destination bitfile to dump contents of the extracted image", default=None)
+ parser.add_option("--safe_image", action="store_true", help="Operate on the safe image. WARNING: This could be dangerous", default=False)
+ parser.add_option("--wipe_user_data", action="store_true", help="Erase all user data like IP, MAC, S/N, etc from flash", default=False)
+ parser.add_option("--no_prompt", action="store_true", help="Suppress all warning prompts", default=False)
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if not options.addr: raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(addr=options.addr)
+
+ # Initialize safe image selector first
+ if options.safe_image:
+ ctrl_sock.set_safe_image(options.no_prompt)
+
+ if options.status:
+ ctrl_sock.print_status()
+
+ # Order of operations:
+ # 1. Extract (if specified)
+ # 2. Erase (if specified)
+ # 3. Burn (if specified)
+
+ if options.extract is not None:
+ file_path = options.extract
+ ctrl_sock.print_status()
+ ctrl_sock.extract_fpga_from_flash(file_path)
+
+ if options.erase:
+ ctrl_sock.erase_fpga_from_flash()
+ ctrl_sock.print_status()
+
+ if options.burn is not None:
+ file_path = options.burn
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.bit'):
+ ctrl_sock.burn_fpga_to_flash(file_path, options.no_prompt)
+ ctrl_sock.print_status()
+ else:
+ raise Exception("Unsupported FPGA bitfile format. You must use a .bit file.")
+
+ if options.wipe_user_data:
+ ctrl_sock.wipe_user_data(options.no_prompt)
diff --git a/firmware/usrp3/n230/n230_debug.py b/firmware/usrp3/n230/n230_debug.py
new file mode 100755
index 000000000..f9ff64ab7
--- /dev/null
+++ b/firmware/usrp3/n230/n230_debug.py
@@ -0,0 +1,387 @@
+#!/usr/bin/env python
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import array
+import os.path
+import sys
+import time
+try:
+ import fcntl
+ N230_DEVICE_DISCOVERY_AVAILABLE = True
+except:
+ N230_DEVICE_DISCOVERY_AVAILABLE = False
+
+########################################################################
+# constants
+########################################################################
+N230_FW_COMMS_UDP_PORT = 49152
+N230_FW_COMMS_MAX_DATA_WORDS = 16
+
+N230_FW_COMMS_FLAGS_ACK = 0x00000001
+N230_FW_COMMS_FLAGS_ERROR_MASK = 0xF0000000
+N230_FW_COMMS_FLAGS_CMD_MASK = 0x000000F0
+
+N230_FW_COMMS_CMD_ECHO = 0x00000000
+N230_FW_COMMS_CMD_POKE32 = 0x00000010
+N230_FW_COMMS_CMD_PEEK32 = 0x00000020
+N230_FW_COMMS_CMD_BLOCK_POKE32 = 0x00000030
+N230_FW_COMMS_CMD_BLOCK_PEEK32 = 0x00000040
+
+N230_FW_COMMS_ERR_PKT_ERROR = 0x80000000
+N230_FW_COMMS_ERR_CMD_ERROR = 0x40000000
+N230_FW_COMMS_ERR_SIZE_ERROR = 0x20000000
+
+N230_FW_COMMS_ID = 0x0001ACE3
+
+N230_FW_LOADER_ADDR = 0xfa00
+N230_FW_LOADER_DATA = 0xfa04
+N230_FW_LOADER_NUM_WORDS = 8192
+N230_FW_LOADER_BOOT_DONE_ADDR = 0xA004
+N230_FW_LOADER_BOOT_TIMEOUT = 5
+
+N230_JESD204_TEST = 0xA014
+N230_FPGA_HASH_ADDR = 0xA010
+N230_FW_HASH_ADDR = 0x10004
+N230_ICAP_ADDR = 0xF800
+#ICAP_DUMMY_WORD = 0xFFFFFFFF
+#ICAP_SYNC_WORD = 0xAA995566
+#ICAP_TYPE1_NOP = 0x20000000
+#ICAP_WRITE_WBSTAR = 0x30020001
+#ICAP_WBSTAR_ADDR = 0x00000000
+#ICAP_WRITE_CMD = 0x30008001
+#ICAP_IPROG_CMD = 0x0000000F
+# Bit reversed values per Xilinx UG470 - Bits reversed within bytes.
+ICAP_DUMMY_WORD = 0xFFFFFFFF
+ICAP_SYNC_WORD = 0x5599AA66
+ICAP_TYPE1_NOP = 0x04000000
+ICAP_WRITE_WBSTAR = 0x0C400080
+ICAP_WBSTAR_ADDR = 0x00000000
+ICAP_WRITE_CMD = 0x0C000180
+ICAP_IPROG_CMD = 0x000000F0
+
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+FPGA_LOAD_TIMEOUT = 10
+
+_seq = -1
+def seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+def pack_fw_command(flags, seq, num_words, addr, data_arr):
+ if (num_words > N230_FW_COMMS_MAX_DATA_WORDS):
+ raise Exception("Data size too large. Firmware supports a max 16 words per block." % (addr))
+ buf = bytes()
+ buf = struct.pack('!IIIII', N230_FW_COMMS_ID, flags, seq, num_words, addr)
+ for i in range(N230_FW_COMMS_MAX_DATA_WORDS):
+ if (i < num_words):
+ buf += struct.pack('!I', data_arr[i])
+ else:
+ buf += struct.pack('!I', 0)
+ return buf
+
+def unpack_fw_command(buf, fmt=None):
+ (id, flags, seq, num_words, addr) = struct.unpack_from('!IIIII', buf)
+ fw_check_error(flags)
+ data = []
+ if fmt is None:
+ fmt = 'I'
+ for i in xrange(20, len(buf), 4):
+ data.append(struct.unpack('!'+fmt, buf[i:i+4])[0])
+ return (flags, seq, num_words, addr, data)
+
+def fw_check_error(flags):
+ if flags & N230_FW_COMMS_ERR_PKT_ERROR == N230_FW_COMMS_ERR_PKT_ERROR:
+ raise Exception("The fiwmware operation returned a packet error")
+ if flags & N230_FW_COMMS_ERR_CMD_ERROR == N230_FW_COMMS_ERR_CMD_ERROR:
+ raise Exception("The fiwmware operation returned a command error")
+ if flags & N230_FW_COMMS_ERR_SIZE_ERROR == N230_FW_COMMS_ERR_SIZE_ERROR:
+ raise Exception("The fiwmware operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Discovery class
+########################################################################
+class discovery_socket(object):
+ def __init__(self):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ self._sock.settimeout(0.250)
+
+ def get_bcast_addrs(self):
+ max_possible = 128 # arbitrary. raise if needed.
+ num_bytes = max_possible * 32
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ names = array.array('B', '\0' * num_bytes)
+ outbytes = struct.unpack('iL', fcntl.ioctl(
+ s.fileno(),
+ 0x8912, # SIOCGIFCONF
+ struct.pack('iL', num_bytes, names.buffer_info()[0])
+ ))[0]
+ namestr = names.tostring()
+ lst = []
+ for i in range(0, outbytes, 40):
+ name = namestr[i:i+16].split('\0', 1)[0]
+ ip = map(ord, namestr[i+20:i+24])
+ mask = map(ord, fcntl.ioctl(s.fileno(), 0x891B, struct.pack('256s', name))[20:24])
+ bcast = []
+ for i in range(len(ip)):
+ bcast.append((ip[i] | (~mask[i])) & 0xFF)
+ if (name != 'lo'):
+ lst.append(str(bcast[0]) + '.' + str(bcast[1]) + '.' + str(bcast[2]) + '.' + str(bcast[3]))
+ return lst
+
+ def discover(self):
+ addrs = []
+ for bcast_addr in self.get_bcast_addrs():
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 0, 0, [0])
+ self._sock.sendto(out_pkt, (bcast_addr, N230_FW_COMMS_UDP_PORT))
+ while 1:
+ try:
+ (in_pkt, addr_pair) = self._sock.recvfrom(UDP_MAX_XFER_BYTES)
+ if len(in_pkt) < 20:
+ continue
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ addrs.append(addr_pair[0])
+ except socket.error:
+ break
+ return addrs
+
+
+########################################################################
+# Communications class, holds a socket and send/recv routine
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr, port):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, port))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+ self.peek(0) #Dummy read
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send(self, pkt):
+ self._sock.send(pkt)
+
+ def recv(self):
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def send_and_recv(self, pkt):
+ self.send(pkt)
+ return self.recv()
+
+ def peek(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, peek_addr, [0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return data[0]
+
+ def peek64(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_BLOCK_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 2, peek_addr, [0,0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return (data[0] | (data[1] << 32))
+
+ def poke(self, poke_addr, poke_data, ack=True):
+ ack_flag = N230_FW_COMMS_FLAGS_ACK if ack else 0
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32|ack_flag, seq(), 1, poke_addr, [poke_data])
+ self.send(out_pkt)
+ if ack:
+ in_pkt = self.recv()
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+
+ def live_load_firmware_bin(self, bin_path):
+ raise Exception("live_load_firmware_bin not implemented yet!")
+
+ def live_load_firmware_coe(self, coe_path):
+ with open(coe_path, 'r') as coe_file:
+ print("Loading %s..." % coe_path)
+ coe_lines = [line.strip(',;\n ') for line in coe_file]
+ start_index = coe_lines.index("memory_initialization_vector=") + 1
+ coe_words = coe_lines[start_index:]
+ if len(coe_words) != N230_FW_LOADER_NUM_WORDS:
+ raise Exception("invalid COE file. Must contain 8192 words!")
+ self.poke(N230_FW_LOADER_ADDR, 0) #Load start address
+ for i in range(0, len(coe_words)):
+ self.poke(N230_FW_LOADER_DATA, int(coe_words[i],16), (i%10==0) and (i<len(coe_words)-1))
+ draw_progress_bar(((i+1)*100)/len(coe_words))
+ print("\nRebooting...")
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32, seq(), 1, N230_FW_LOADER_BOOT_DONE_ADDR, [1])
+ self._sock.send(out_pkt)
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(N230_FW_LOADER_BOOT_TIMEOUT):
+ try:
+ self._sock.send(out_pkt)
+ in_pkt = self._sock.recv(UDP_MAX_XFER_BYTES)
+ print("Firmware is alive!")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("Firmware boot FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def read_hash(self):
+ fpga_hash = self.peek(N230_FPGA_HASH_ADDR)
+ fpga_status = "clean" if (fpga_hash & 0xf0000000 == 0x0) else "modified"
+ fw_hash = self.peek(N230_FW_HASH_ADDR)
+ fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "modified"
+ print("FPGA Version : %x (%s)" % (fpga_hash & 0xfffffff, fpga_status))
+ print("Firmware Version : %x (%s)" % (fw_hash & 0xfffffff, fw_status))
+
+ def is_claimed(self):
+ claimed = self.peek(0x10008)
+ print("Claimed : %s") % ('YES' if claimed else 'NO')
+
+ def reset_fpga(self):
+ print("Reseting USRP...")
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_DUMMY_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_SYNC_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_WBSTAR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WBSTAR_ADDR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_CMD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_IPROG_CMD, False)
+ print("Waiting for FPGA to load...")
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(FPGA_LOAD_TIMEOUT):
+ try:
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ print("FPGA loaded successfully.")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("FPGA load FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def jesd204_test_connector(self):
+ print("Testing JESD204 connectors. Molex cable #79576-2102 must be connected")
+ ctrl_sock.poke(N230_JESD204_TEST,0x1)
+ while True:
+ jesd204_test_status = ctrl_sock.peek(N230_JESD204_TEST)
+ if (jesd204_test_status & 0x10000 == 0x10000):
+ break
+ ctrl_sock.poke(N230_JESD204_TEST,0x0)
+ if (jesd204_test_status & 0xFFFF != 0x0):
+ print("JESD204 loopback test Failed!: Returned status is %4x" % (jesd204_test_status & 0xFFFF))
+ else:
+ print("JESD204 loopback test Passed.")
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--discover", action="store_true",help="Find all devices connected N230 devices", default=False)
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--peek", type="int", help="Read from memory map", default=None)
+ parser.add_option("--poke", type="int", help="Write to memory map", default=None)
+ parser.add_option("--data", type="int", help="Data for poke", default=None)
+ parser.add_option("--fw", type="string", help="Path to FW image to load", default=None)
+ parser.add_option("--hash", action="store_true",help="Display FPGA git hash", default=False)
+ parser.add_option("--reset", action="store_true",help="Reset and Reload USRP FPGA.", default=False)
+ parser.add_option("--jesd204test", action="store_true",help="Test mini-SAS connectors with loopback cable..", default=False)
+
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if options.discover:
+ if N230_DEVICE_DISCOVERY_AVAILABLE:
+ disc_sock = discovery_socket()
+ for addr in disc_sock.discover():
+ print '==== FOUND ' + addr + ' ===='
+ ctrl_sock = ctrl_socket(addr, N230_FW_COMMS_UDP_PORT)
+ ctrl_sock.read_hash()
+ ctrl_sock.is_claimed()
+ sys.exit()
+ else:
+ raise Exception('Discovery is only supported on Linux.')
+
+ if not options.addr:
+ raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(options.addr, N230_FW_COMMS_UDP_PORT)
+
+ if options.fw is not None:
+ file_path = options.fw
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.coe'):
+ ctrl_sock.live_load_firmware_coe(file_path)
+ elif (extension.lower() == '.bin'):
+ ctrl_sock.live_load_firmware_bin(file_path)
+ else:
+ raise Exception("Unsupported firmware file format")
+
+ if options.hash:
+ ctrl_sock.read_hash()
+
+ if options.peek is not None:
+ addr = options.peek
+ data = ctrl_sock.peek(addr)
+ print("PEEK[0x%x (%d)] => 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.poke is not None and options.data is not None:
+ addr = options.poke
+ data = options.data
+ ctrl_sock.poke(addr,data)
+ print("POKE[0x%x (%d)] <= 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.reset:
+ ctrl_sock.reset_fpga()
+
+ if options.jesd204test:
+ ctrl_sock.jesd204_test_connector()
diff --git a/firmware/usrp3/n230/n230_eeprom.c b/firmware/usrp3/n230/n230_eeprom.c
new file mode 100644
index 000000000..8f756d41f
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eeprom.c
@@ -0,0 +1,196 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+
+#include <trace.h>
+#include <stddef.h>
+#include <flash/spi_flash.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <string.h> //memcpy
+
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+
+static const wb_spi_slave_t flash_spi_slave = {
+ .base = (void*) 0xB000,
+ .slave_sel = 0x0001,
+ .clk_div = 4, //80MHz/4 = 20MHz
+ .mosi_edge = RISING,
+ .miso_edge = FALLING,
+ .lsb_first = false
+};
+
+static const spi_flash_dev_t spi_flash_device = {
+ .page_size = 256,
+ .sector_size = 65536,
+ .num_sectors = 254,
+ .bus = &flash_spi_slave
+};
+
+/***********************************************************************
+ * Non-volatile device data
+ **********************************************************************/
+#define N230_FLASH_NV_DATA_OFFSET 0x800000
+
+//Default values in case the EEPROM is not read, corrupt
+const n230_eeprom_map_t default_eeprom = {
+ .data_version_major = N230_EEPROM_VER_MAJOR,
+ .data_version_minor = N230_EEPROM_VER_MINOR,
+ .hw_revision = 0,
+ .hw_product = 0x01,
+ .gateway = N230_DEFAULT_GATEWAY,
+ .eth_info = {
+ { //eth0
+ .mac_addr = N230_DEFAULT_ETH0_MAC,
+ .subnet = N230_DEFAULT_ETH0_MASK,
+ .ip_addr = N230_DEFAULT_ETH0_IP
+ },
+ { //eth1
+ .mac_addr = N230_DEFAULT_ETH1_MAC,
+ .subnet = N230_DEFAULT_ETH1_MASK,
+ .ip_addr = N230_DEFAULT_ETH1_IP
+ }
+ }
+};
+
+//EEPROM cache
+static spi_flash_session_t flash_session = {.device = NULL};
+static n230_eeprom_map_t eeprom_cache;
+static bool cache_dirty = true;
+
+bool read_n230_eeprom()
+{
+ bool status = false;
+ if (flash_session.device == NULL) { //Initialize flash session structure for the first time
+ wb_spi_init(spi_flash_device.bus);
+ spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations());
+ }
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+
+ //Verify data format
+ status = (eeprom_cache.data_version_major == default_eeprom.data_version_major);
+ //Sanity communication info
+ if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr;
+ if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr;
+ if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet;
+ if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet;
+
+ if (!status) {
+ UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map.");
+ memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t));
+ }
+ cache_dirty = !status;
+ return status;
+}
+
+bool write_n230_eeprom()
+{
+ //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size
+ //This function would need to be reimplemented if this assumption is no longer true
+ if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size");
+ return false;
+ }
+
+ bool status = true;
+ if (cache_dirty) {
+ n230_eeprom_map_t device_eeprom;
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t));
+ if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) {
+ //Cache does not match read state. Write.
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash...");
+ status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET);
+ if (status) {
+ status = spif_write_page_sync(
+ &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+ }
+ if (!status) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!");
+ }
+ cache_dirty = !status;
+ } else {
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped.");
+ //Cache matches read state. So mark as clean
+ cache_dirty = false;
+ }
+ }
+ return status;
+}
+
+bool is_n230_eeprom_cache_dirty()
+{
+ return cache_dirty;
+}
+
+n230_eeprom_map_t* get_n230_eeprom_map()
+{
+ cache_dirty = true;
+ return &eeprom_cache;
+}
+
+const n230_eeprom_map_t* get_n230_const_eeprom_map()
+{
+ return &eeprom_cache;
+}
+
+const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) {
+ if (iface >= N230_NUM_ETH_PORTS) {
+ UHD_FW_TRACE_FSTR(ERROR,
+ "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!",
+ iface, N230_NUM_ETH_PORTS);
+ }
+ return &(get_n230_const_eeprom_map()->eth_info[iface]);
+}
+
+
+/***********************************************************************
+ * Storage for bootstrap FPGA Image
+ **********************************************************************/
+#define N230_FLASH_FPGA_IMAGE_OFFSET 0x000000
+#define N230_FLASH_FPGA_IMAGE_SIZE 0x400000
+#define N230_FLASH_NUM_FPGA_IMAGES 2
+
+void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ }
+ spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool erase_n230_fpga_image_sector(uint32_t offset)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset);
+}
diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c
new file mode 100644
index 000000000..b291bb39f
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.c
@@ -0,0 +1,340 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_eth_handlers.h"
+
+#include <wb_utils.h>
+#include <string.h> //memcmp
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../n230/n230_fw_host_iface.h"
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+
+static n230_host_shared_mem_t* host_shared_mem_ptr;
+
+static const soft_reg_field_t LED_REG_FIELD_ETH_LINK2 = {.num_bits=1, .shift=0};
+static const soft_reg_field_t LED_REG_FIELD_ETH_LINK1 = {.num_bits=1, .shift=1};
+static const soft_reg_field_t LED_REG_FIELD_ETH_ACT2 = {.num_bits=1, .shift=2};
+static const soft_reg_field_t LED_REG_FIELD_ETH_ACT1 = {.num_bits=1, .shift=3};
+
+/***********************************************************************
+ * Handler for host <-> firmware communication
+ **********************************************************************/
+
+static inline void n230_poke32(const uint32_t addr, const uint32_t data)
+{
+ if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data;
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ wb_poke32(addr, data);
+ }
+}
+
+static inline uint32_t n230_peek32(const uint32_t addr)
+{
+ if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)];
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ return wb_peek32(addr);
+ } else {
+ return 0;
+ }
+}
+
+void n230_handle_udp_fw_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(fw_comm_pkt_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size).");
+ } else {
+ const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff;
+ fw_comm_pkt_t response;
+ bool send_response = process_fw_comm_protocol_pkt(
+ request, &response,
+ N230_FW_PRODUCT_ID,
+ (uint32_t)ethno,
+ n230_poke32, n230_peek32);
+
+ if (send_response) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr)
+{
+ host_shared_mem_ptr = shared_mem_ptr;
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms);
+}
+
+
+/***********************************************************************
+ * Handler for UDP framer program packets
+ **********************************************************************/
+void program_udp_framer(
+ const uint8_t ethno,
+ const uint32_t sid,
+ const struct ip_addr *dst_ip,
+ const uint16_t dst_port,
+ const uint16_t src_port)
+{
+ const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip);
+ const size_t vdest = (sid >> 16) & 0xff;
+
+ uint32_t framer_base =
+ ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE;
+
+ //setup source framer
+ const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI),
+ (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO),
+ (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) |
+ (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port);
+
+ //setup destination framer
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC),
+ (((uint32_t)dst_port) << 16) |
+ (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO),
+ (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) |
+ (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0));
+}
+
+void handle_udp_prog_framer(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else {
+ const uint32_t sid = ((const uint32_t *)buff)[1];
+ program_udp_framer(ethno, sid, src, src_port, dst_port);
+ UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d",
+ ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port);
+ }
+}
+
+void n230_register_udp_prog_framer()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer);
+}
+
+
+/***********************************************************************
+ * Handler for flash programming interface over UDP
+ **********************************************************************/
+
+void n230_handle_flash_prog_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(n230_flash_prog_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size).");
+ } else {
+ const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff;
+ n230_flash_prog_t response;
+ bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK;
+
+ //Request is valid. Copy it into the reply.
+ memcpy(&response, request, sizeof(n230_flash_prog_t));
+
+ switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) {
+ case N230_FLASH_COMM_CMD_READ_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ if (is_n230_eeprom_cache_dirty()) {
+ read_n230_eeprom();
+ }
+ //EEPROM cache is up-to-date. Copy it into the packet.
+ //Assumption: Cache size < 256. If this is no longer true, the offset field
+ //will have to be used.
+ memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t));
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t));
+ if (!write_n230_eeprom()) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_READ_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ read_n230_fpga_image_page(request->offset, response.data, request->size);
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_ERASE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)",
+ request->offset);
+ if (!erase_n230_fpga_image_sector(request->offset)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ default :{
+ UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command.");
+ response.flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+ //Send a reply if ack requested
+ if (ack_requested) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_flash_comms_handler()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms);
+}
+
+/***********************************************************************
+ * Handler for SFP state changes
+ **********************************************************************/
+#define SFPP_STATUS_MODABS_CHG (1 << 5) // Has MODABS changed since last read?
+#define SFPP_STATUS_TXFAULT_CHG (1 << 4) // Has TXFAULT changed since last read?
+#define SFPP_STATUS_RXLOS_CHG (1 << 3) // Has RXLOS changed since last read?
+#define SFPP_STATUS_MODABS (1 << 2) // MODABS state
+#define SFPP_STATUS_TXFAULT (1 << 1) // TXFAULT state
+#define SFPP_STATUS_RXLOS (1 << 0) // RXLOS state
+
+static bool links_up[N230_MAX_NUM_ETH_PORTS] = {};
+static uint32_t packet_count[N230_MAX_NUM_ETH_PORTS] = {};
+
+void n230_poll_sfp_status(const uint32_t eth, bool force, bool* state_updated)
+{
+ // Has MODDET/MODAbS changed since we last looked?
+ uint32_t rb = wb_peek32(SR_ADDR(WB_SBRB_BASE, (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1));
+
+ if (rb & SFPP_STATUS_RXLOS_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (rb & SFPP_STATUS_RXLOS));
+ if (rb & SFPP_STATUS_TXFAULT_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((rb & SFPP_STATUS_TXFAULT) >> 1));
+ if (rb & SFPP_STATUS_MODABS_CHG)
+ UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((rb & SFPP_STATUS_MODABS) >> 2));
+
+ //update the link up status
+ if ((rb & SFPP_STATUS_RXLOS_CHG) || (rb & SFPP_STATUS_TXFAULT_CHG) || (rb & SFPP_STATUS_MODABS_CHG) || force)
+ {
+ const bool old_link_up = links_up[eth];
+ const uint32_t status_reg_addr = (eth==0) ? RB_ZPU_SFP_STATUS0 : RB_ZPU_SFP_STATUS1;
+
+ uint32_t sfpp_status = wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) & 0xFFFF;
+ if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
+ int8_t timeout = 100;
+ bool link_up = false;
+ do {
+ link_up = ((wb_peek32(SR_ADDR(WB_SBRB_BASE, status_reg_addr)) >> 16) & 0x1) != 0;
+ } while (!link_up && timeout-- > 0);
+
+ links_up[eth] = link_up;
+ } else {
+ links_up[eth] = false;
+ }
+
+ if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
+ UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down");
+ if (rb & SFPP_STATUS_MODABS_CHG) {
+ // MODDET has changed state since last checked
+ if (rb & SFPP_STATUS_MODABS) {
+ // MODDET is high, module currently removed.
+ UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth);
+ } else {
+ // MODDET is low, module currently inserted.
+ // Return status.
+ UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth);
+ }
+ }
+ *state_updated = true;
+ } else {
+ *state_updated = false;
+ }
+}
+
+void n230_update_link_act_state(soft_reg_t* led_reg)
+{
+ static bool first_poll = 1;
+ static uint32_t poll_cnt;
+
+ bool activity[N230_MAX_NUM_ETH_PORTS] = {};
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ if (first_poll) {
+ links_up[i] = 0;
+ packet_count[i] = 0;
+ poll_cnt = 0;
+ }
+
+ //Check SFP status and update links_up
+ bool link_state_from_sfp = false;
+ n230_poll_sfp_status(i, first_poll, &link_state_from_sfp);
+
+ //Check packet counters less frequently to keep the LED on for a visible duration
+ uint32_t cnt = wb_peek32(SR_ADDR(WB_SBRB_BASE, (i==0)?RB_ZPU_ETH0_PKT_CNT:RB_ZPU_ETH1_PKT_CNT));
+ activity[i] = (cnt != packet_count[i]);
+ packet_count[i] = cnt;
+
+ //Update links_up if there is activity only if the SFP
+ //handler has not updated it
+ if (activity[i] && !link_state_from_sfp) links_up[i] = true;
+ }
+
+ //TODO: Swap this when Ethernet port swap issues is fixed
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK2, links_up[0]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_LINK1, links_up[1]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT2, activity[0]?1:0);
+ soft_reg_write(led_reg, LED_REG_FIELD_ETH_ACT1, activity[1]?1:0);
+
+ first_poll = 0;
+}
+
diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h
new file mode 100644
index 000000000..67afbb246
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.h
@@ -0,0 +1,48 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_ETH_HANDLERS_H
+#define INCLUDED_N230_ETH_HANDLERS_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <lwip/ip_addr.h>
+#include <wb_soft_reg.h>
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr);
+
+/*!
+ * Registrar for framer programmer handler.
+ */
+void n230_register_udp_prog_framer();
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_flash_comms_handler();
+
+/*!
+ * Handle SFP updates.
+ */
+void n230_update_link_act_state(soft_reg_t* led_reg);
+
+#endif /* INCLUDED_N230_ETH_HANDLERS_H */
diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c
new file mode 100644
index 000000000..14f5ebd77
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.c
@@ -0,0 +1,125 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <printf.h>
+#include <wb_utils.h>
+#include <wb_uart.h>
+#include <wb_i2c.h>
+#include <wb_pkt_iface64.h>
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/n230/n230_eeprom.h"
+#include "n230_init.h"
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+
+static wb_pkt_iface64_config_t pkt_config;
+
+static void putc(void *p, char c)
+{
+//If FW_TRACE_LEVEL is defined, then the trace level is set
+//to a non-zero number. Turn on the debug UART to enable tracing
+#ifdef UHD_FW_TRACE_LEVEL
+ wb_uart_putc(WB_DBG_UART_BASE, c);
+#endif
+}
+
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(WB_SBRB_BASE, RB_ZPU_COUNTER));
+}
+
+void n230_init(void)
+{
+ //TODO: We may need to remove the debug UART before we release.
+ //Initialize the debug UART first.
+ wb_uart_init(WB_DBG_UART_BASE, CPU_CLOCK_FREQ/DBG_UART_BAUD);
+ init_printf(NULL, putc);
+
+ //Now we can init the rest with prints
+ UHD_FW_TRACE_FSTR(INFO, "[ZPU Init Begin -- CPU CLOCK is %d MHz]", (CPU_CLOCK_FREQ/1000000));
+
+ //Initialize cron and the per millisecond cron job
+ UHD_FW_TRACE(INFO, "Initializing cron...");
+ cron_init(get_counter_val, CPU_CLOCK_FREQ);
+ cron_job_init(PER_MILLISEC_CRON_JOBID, 1);
+ cron_job_init(PER_SECOND_CRON_JOBID, 1000);
+
+ //Initialize rate for I2C cores
+ UHD_FW_TRACE(INFO, "Initializing I2C...");
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ wb_i2c_init((i==1)?WB_ETH1_I2C_BASE:WB_ETH0_I2C_BASE, CPU_CLOCK_FREQ);
+ }
+
+ //Initialize eeprom
+ read_n230_eeprom();
+
+ UHD_FW_TRACE(INFO, "Initializing network stack...");
+ init_network_stack();
+}
+
+void init_network_stack(void)
+{
+ //Hold Ethernet PHYs in reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_PHY);
+
+ //Initialize ethernet packet interface
+ pkt_config = wb_pkt_iface64_init(WB_PKT_RAM_BASE, WB_PKT_RAM_CTRL_OFFSET);
+ u3_net_stack_init(&pkt_config);
+
+ //Initialize MACs
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ init_ethernet_mac(i);
+ }
+
+ //Pull Ethernet PHYs out of reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_NONE);
+}
+
+void init_ethernet_mac(uint32_t iface_num)
+{
+ UHD_FW_TRACE_FSTR(INFO, "Initializing eth%d...", iface_num);
+
+ //Get interface info from the EEPROM (or defaults otherwise)
+ const n230_eth_eeprom_map_t* eth_eeprom_map = get_n230_ethernet_info(iface_num);
+ const eth_mac_addr_t *my_mac = (const eth_mac_addr_t *) &(eth_eeprom_map->mac_addr);
+ const struct ip_addr *my_ip = (const struct ip_addr *) &(eth_eeprom_map->ip_addr);
+ const struct ip_addr *subnet = (const struct ip_addr *) &(eth_eeprom_map->subnet);
+
+ //Init software fields related to ethernet
+ u3_net_stack_init_eth(iface_num, my_mac, my_ip, subnet);
+
+ uint32_t dispatcher_base =
+ ((iface_num == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_DISPATCHER_BASE;
+
+ //Program dispatcher
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 0),
+ (my_mac->addr[5] << 0) | (my_mac->addr[4] << 8) | (my_mac->addr[3] << 16) | (my_mac->addr[2] << 24));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 1), (my_mac->addr[1] << 0) | (my_mac->addr[0] << 8));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 2), my_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 4), 0/*nofwd*/);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 5), (ICMP_IRQ << 8) | 0); //no fwd: type, code
+
+ //DEBUG: Print initialized info
+ UHD_FW_TRACE_FSTR(INFO, "-- MAC%u: %s", iface_num, mac_addr_to_str(u3_net_stack_get_mac_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- IP%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_ip_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- SUBNET%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_subnet(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- BCAST%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_bcast(iface_num)));
+}
+
+
diff --git a/firmware/usrp3/n230/n230_init.h b/firmware/usrp3/n230/n230_init.h
new file mode 100644
index 000000000..e2231909e
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.h
@@ -0,0 +1,28 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_INIT_H
+#define INCLUDED_N230_INIT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void n230_init(void);
+void init_network_stack(void);
+void init_ethernet_mac(uint32_t iface_num);
+
+#endif /* INCLUDED_B250_INIT_H */
diff --git a/firmware/usrp3/n230/n230_main.c b/firmware/usrp3/n230/n230_main.c
new file mode 100644
index 000000000..a6c12e56d
--- /dev/null
+++ b/firmware/usrp3/n230/n230_main.c
@@ -0,0 +1,113 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <wb_soft_reg.h>
+#include <u3_net_stack.h>
+#include <trace.h>
+#include "../../../host/lib/usrp/n230/n230_fw_defs.h"
+#include "../../../host/lib/usrp/n230/n230_fw_host_iface.h"
+#include "n230_eth_handlers.h"
+#include "n230_init.h"
+
+//The version hash should come from a cmake build variable
+//If it doesn't then the build system does not support the feature
+//so just default to 0xFFFFFFFF
+#ifndef UHD_VERSION_HASH
+#define UHD_VERSION_HASH 0xFFFFFFFF
+#endif
+
+//TODO: This is just for initial debugging.
+static soft_reg_t g_led_register;
+
+//Shared memory
+static n230_host_shared_mem_t g_host_shared_mem;
+
+//Functions
+static void n230_handle_claim();
+
+/***********************************************************************
+ * Main loop runs all the handlers
+ **********************************************************************/
+int main(void)
+{
+ //Initialize host shared mem
+ g_host_shared_mem.data.fw_compat_num = N230_FW_COMPAT_NUM;
+ g_host_shared_mem.data.fw_version_hash = UHD_VERSION_HASH;
+
+ //Main initialization function
+ n230_init();
+
+ //Initialize UDP Handlers
+ n230_register_udp_fw_comms_handler(&g_host_shared_mem);
+ n230_register_udp_prog_framer();
+ n230_register_flash_comms_handler();
+
+ initialize_writeonly_soft_reg(&g_led_register, SR_ADDR(WB_SBRB_BASE, SR_ZPU_LEDS));
+
+ uint32_t heart_beat = 0;
+ while(true)
+ {
+ //TODO: This is just for initial debugging. Once the firmware
+ //is somewhat stable we should delete this cron job
+ if (cron_job_run_due(PER_SECOND_CRON_JOBID)) {
+ //Everything in this block runs approx once per second
+ if (heart_beat % 10 == 0) {
+ UHD_FW_TRACE_FSTR(INFO, "0.1Hz Heartbeat (%u)", heart_beat);
+ }
+ heart_beat++;
+ }
+
+ if (cron_job_run_due(PER_MILLISEC_CRON_JOBID)) {
+ //Everything in this block runs approx once per millisecond
+ n230_handle_claim();
+ n230_update_link_act_state(&g_led_register);
+ }
+
+ //run the network stack - poll and handle
+ u3_net_stack_handle_one();
+ }
+ return 0;
+}
+
+// Watchdog timer for claimer
+static void n230_handle_claim()
+{
+ static uint32_t last_time = 0;
+ static size_t timeout = 0;
+
+ if (g_host_shared_mem.data.claim_time == 0) {
+ //If time is 0 if the claim was forfeit
+ g_host_shared_mem.data.claim_status = 0;
+ } else if (last_time != g_host_shared_mem.data.claim_time) {
+ //If the time changes, reset timeout
+ g_host_shared_mem.data.claim_status = 1;
+ timeout = 0;
+ } else {
+ //Otherwise increment for timeout
+ timeout++;
+ }
+
+ //Always stash the last seen time
+ last_time = g_host_shared_mem.data.claim_time;
+
+ //Timeout logic
+ if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) {
+ g_host_shared_mem.data.claim_time = 0;
+ }
+}
+
diff --git a/firmware/usrp3/utils/git-hash.sh b/firmware/usrp3/utils/git-hash.sh
new file mode 100755
index 000000000..ff7ae5ecb
--- /dev/null
+++ b/firmware/usrp3/utils/git-hash.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [[ $(command -v git) = "" ]]; then
+ short_hash="FFFFFFFF"
+else
+ if (git diff --quiet); then
+ #Clean
+ short_hash="0$(git rev-parse --verify HEAD --short)"
+ else
+ #Dirty
+ short_hash="F$(git rev-parse --verify HEAD --short)"
+ fi
+fi
+echo ${short_hash^^} \ No newline at end of file
diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py
index c9bcbb138..c518ba4e0 100755
--- a/firmware/usrp3/x300/x300_debug.py
+++ b/firmware/usrp3/x300/x300_debug.py
@@ -25,12 +25,18 @@ import struct
########################################################################
# constants
########################################################################
-B250_FW_COMMS_UDP_PORT = 49152
+X300_FW_COMMS_UDP_PORT = 49152
-B250_FW_COMMS_FLAGS_ACK = 1
-B250_FW_COMMS_FLAGS_ERROR = 2
-B250_FW_COMMS_FLAGS_POKE32 = 4
-B250_FW_COMMS_FLAGS_PEEK32 = 8
+X300_FW_COMMS_FLAGS_ACK = 0x00000001
+X300_FW_COMMS_FLAGS_POKE32 = 0x00000010
+X300_FW_COMMS_FLAGS_PEEK32 = 0x00000020
+
+X300_FW_COMMS_ERR_PKT_ERROR = 0x80000000
+X300_FW_COMMS_ERR_CMD_ERROR = 0x40000000
+X300_FW_COMMS_ERR_SIZE_ERROR = 0x20000000
+
+X300_FW_COMMS_ID = 0x0000ACE3
+X300_FW_COMMS_MAX_DATA_WORDS = 16
#UDP_CTRL_PORT = 49183
UDP_MAX_XFER_BYTES = 1024
@@ -39,7 +45,7 @@ UDP_TIMEOUT = 3
#REG_ARGS_FMT = '!LLLLLB15x'
#REG_IP_FMT = '!LLLL20x'
-REG_PEEK_POKE_FMT = '!LLLL'
+REG_PEEK_POKE_FMT = '!LLLLL'
_seq = -1
@@ -52,12 +58,33 @@ def seq():
########################################################################
# helper functions
########################################################################
-
-def unpack_reg_peek_poke_fmt(s):
- return struct.unpack(REG_PEEK_POKE_FMT,s) #(flags, seq, addr, data)
+def fw_check_error(flags):
+ if flags & X300_FW_COMMS_ERR_PKT_ERROR == X300_FW_COMMS_ERR_PKT_ERROR:
+ raise Exception("The fiwmware operation returned a packet error")
+ if flags & X300_FW_COMMS_ERR_CMD_ERROR == X300_FW_COMMS_ERR_CMD_ERROR:
+ raise Exception("The fiwmware operation returned a command error")
+ if flags & X300_FW_COMMS_ERR_SIZE_ERROR == X300_FW_COMMS_ERR_SIZE_ERROR:
+ raise Exception("The fiwmware operation returned a size error")
def pack_reg_peek_poke_fmt(flags, seq, addr, data):
- return struct.pack(REG_PEEK_POKE_FMT, flags, seq, addr, data);
+ num_words = 1
+ data_arr = [data]
+ buf = bytes()
+ buf = struct.pack('!LLLLL', X300_FW_COMMS_ID, flags, seq, num_words, addr)
+ for i in range(X300_FW_COMMS_MAX_DATA_WORDS):
+ if (i < num_words):
+ buf += struct.pack('!L', data_arr[i])
+ else:
+ buf += struct.pack('!L', 0)
+ return buf
+
+def unpack_reg_peek_poke_fmt(buf):
+ (id, flags, seq, num_words, addr) = struct.unpack_from('!LLLLL', buf)
+ fw_check_error(flags)
+ data = []
+ for i in xrange(20, len(buf), 4):
+ data.append(struct.unpack('!L', buf[i:i+4])[0])
+ return (flags, seq, addr, data[0])
########################################################################
# Burner class, holds a socket and send/recv routines
@@ -66,7 +93,7 @@ class ctrl_socket(object):
def __init__(self, addr):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.settimeout(UDP_TIMEOUT)
- self._sock.connect((addr, B250_FW_COMMS_UDP_PORT))
+ self._sock.connect((addr, X300_FW_COMMS_UDP_PORT))
self.set_callbacks(lambda *a: None, lambda *a: None)
#self.init_update() #check that the device is there
@@ -92,11 +119,10 @@ class ctrl_socket(object):
for in_prt in range (0, 8):
print("%s |" % ports[in_prt]),
for out_prt in range (0, 8):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek returns error code")
+ fw_check_error(flags)
print("%10d " % (data)),
print
print
@@ -105,19 +131,17 @@ class ctrl_socket(object):
def peek(self,peek_addr):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
+ fw_check_error(flags)
print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data))
def poke(self,poke_addr,poke_data):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_POKE32|B250_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_POKE32|X300_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
+ fw_check_error(flags)
print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data) )
@@ -126,12 +150,12 @@ class ctrl_socket(object):
########################################################################
def get_options():
parser = optparse.OptionParser()
- parser.add_option("--addr", type="string", help="USRP-N2XX device address", default='')
- parser.add_option("--list", action="store_true", help="list possible network devices", default=False)
+ parser.add_option("--addr", type="string", help="USRP-X300 device address", default='')
+ parser.add_option("--list", action="store_true", help="list possible network devices", default=False)
parser.add_option("--peek", type="int", help="Read from memory map", default=None)
parser.add_option("--poke", type="int", help="Write to memory map", default=None)
parser.add_option("--data", type="int", help="Data for poke", default=None)
- parser.add_option("--stats", action="store_true", help="Display SuperMIMO Network Stats", default=False)
+ parser.add_option("--stats", action="store_true", help="Display SuperMIMO Network Stats", default=False)
(options, args) = parser.parse_args()
return options
diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c
index ef97412a2..97b20032b 100644
--- a/firmware/usrp3/x300/x300_init.c
+++ b/firmware/usrp3/x300/x300_init.c
@@ -1,7 +1,7 @@
#include "x300_init.h"
#include "x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <wb_utils.h>
#include <wb_uart.h>
#include <wb_i2c.h>
@@ -121,6 +121,11 @@ static void putc(void *p, char c)
#endif
}
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
+}
+
void x300_init(void)
{
//first - uart
@@ -136,6 +141,9 @@ void x300_init(void)
UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF));
UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000));
+ //Initialize cron
+ cron_init(get_counter_val, CPU_CLOCK);
+
//i2c rate init
wb_i2c_init(I2C0_BASE, CPU_CLOCK);
wb_i2c_init(I2C1_BASE, CPU_CLOCK);
@@ -163,7 +171,7 @@ void x300_init(void)
}
// For eth interfaces, initialize the PHY's
- mdelay(100);
+ sleep_ms(100);
ethernet_init(0);
ethernet_init(1);
}
diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c
index 3b812a2c4..44ed10aa8 100644
--- a/firmware/usrp3/x300/x300_main.c
+++ b/firmware/usrp3/x300/x300_main.c
@@ -6,7 +6,6 @@
#include "xge_phy.h"
#include "ethernet.h"
#include "chinch.h"
-#include "mdelay.h"
#include <wb_utils.h>
#include <wb_uart.h>