aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAshish Chaudhari <ashish@ettus.com>2015-12-23 10:59:07 -0800
committerAshish Chaudhari <ashish@ettus.com>2016-01-05 17:35:14 -0800
commite92913f81a8a44def64dfcb7c0001d9ca7d2d2c4 (patch)
tree12d441822342db2e9e11b5cbe7c0fd156f0cba13
parentf45b4eb386a4c4cf5e7a463936fb4591d1bf8854 (diff)
downloaduhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.gz
uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.bz2
uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.zip
n230: Initial checkin of firmware files
-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/fw_comm_protocol.h99
-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.c4
-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.c102
-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.py402
-rw-r--r--firmware/usrp3/n230/n230_eeprom.c195
-rw-r--r--firmware/usrp3/n230/n230_eeprom.h122
-rw-r--r--firmware/usrp3/n230/n230_eth_handlers.c235
-rw-r--r--firmware/usrp3/n230/n230_eth_handlers.h42
-rw-r--r--firmware/usrp3/n230/n230_fw_defs.h133
-rw-r--r--firmware/usrp3/n230/n230_fw_host_iface.h124
-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.c114
-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
33 files changed, 3181 insertions, 94 deletions
diff --git a/firmware/usrp3/CMakeLists.txt b/firmware/usrp3/CMakeLists.txt
index f71b79b0c..66a43b6bd 100644
--- a/firmware/usrp3/CMakeLists.txt
+++ b/firmware/usrp3/CMakeLists.txt
@@ -25,6 +25,10 @@ SET(CMAKE_SYSTEM_NAME Generic)
CMAKE_FORCE_C_COMPILER(zpu-elf-gcc GNU)
PROJECT(USRP3_FW C)
+SET(UHD_VERSION_HASH 0 CACHE INTEGER "UHD Version Hash")
+EXECUTE_PROCESS(COMMAND ${CMAKE_SOURCE_DIR}/utils/git-hash.sh OUTPUT_VARIABLE UHD_VERSION_HASH)
+ADD_DEFINITIONS(-DUHD_VERSION_HASH=0x${UHD_VERSION_HASH})
+
INCLUDE_DIRECTORIES(include)
find_package(PythonInterp)
@@ -130,3 +134,4 @@ ENDMACRO(GEN_OUTPUTS)
########################################################################
ADD_SUBDIRECTORY(lib)
ADD_SUBDIRECTORY(x300)
+ADD_SUBDIRECTORY(n230)
diff --git a/firmware/usrp3/include/cron.h b/firmware/usrp3/include/cron.h
new file mode 100644
index 000000000..2d43d97f5
--- /dev/null
+++ b/firmware/usrp3/include/cron.h
@@ -0,0 +1,75 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_CRON_H
+#define INCLUDED_CRON_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define CRON_MAX_JOBS 4
+
+typedef enum {
+ SEC = 1, MILLISEC = 1000, MICROSEC = 1000000
+} cron_time_unit_t;
+
+typedef uint32_t (*cron_counter_fetcher_t)();
+
+/*!
+ * \brief Initialize cron subsystem with a mechanism to fetch a counter and its frequency
+ */
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq);
+
+/*!
+ * \brief Get the hardware tick count
+ */
+uint32_t cron_get_ticks();
+
+/*!
+ * \brief Get the time elapsed between start and stop in the specified units
+ */
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit);
+
+/*!
+ * \brief Sleep (spinloop) for about 'ticks' counter ticks
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_ticks(uint32_t ticks);
+
+/*!
+ * \brief Sleep (spinloop) for about 'duration' microseconds
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_us(uint32_t duration);
+
+/*!
+ * \brief Sleep (spinloop) for about 'duration' milliseconds
+ * Use only if simulating, _very_ short delay
+ */
+void sleep_ms(uint32_t duration);
+
+/*!
+ * \brief Initialize a unique cron job with 'job_id' and interval 'interval_ms'
+ */
+void cron_job_init(uint32_t job_id, uint32_t interval_ms);
+
+/*!
+ * \brief Check if cron job with 'job_id' is due for execution
+ */
+bool cron_job_run_due(uint32_t job_id);
+
+#endif /* INCLUDED_CRON_H */
diff --git a/firmware/usrp3/include/flash/spi_flash.h b/firmware/usrp3/include/flash/spi_flash.h
new file mode 100644
index 000000000..8ed73f648
--- /dev/null
+++ b/firmware/usrp3/include/flash/spi_flash.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_SPI_FLASH
+#define INCLUDED_SPI_FLASH
+
+#include <wb_spi.h>
+
+//Device class that encapsulates the geometry and control
+//interface for the flash chip
+typedef struct {
+ uint32_t page_size; //in bytes
+ uint32_t sector_size; //in bytes
+ uint32_t num_sectors;
+ const wb_spi_slave_t* bus;
+} spi_flash_dev_t;
+
+//Low level device specific operations
+typedef uint16_t (*spif_read_id_fn_t)(const spi_flash_dev_t* flash);
+typedef void (*spif_read_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+typedef bool (*spif_erase_sector_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset);
+typedef bool (*spif_erase_sector_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset);
+typedef bool (*spif_erase_sector_busy_fn_t)(const spi_flash_dev_t* flash);
+typedef bool (*spif_write_page_dispatch_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+typedef bool (*spif_write_page_commit_fn_t)(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+typedef bool (*spif_write_page_busy_fn_t)(const spi_flash_dev_t* flash);
+
+//Interface struct for all low level device operations
+typedef struct {
+ spif_read_id_fn_t read_id;
+ spif_read_fn_t read;
+ spif_erase_sector_dispatch_fn_t erase_sector_dispatch;
+ spif_erase_sector_commit_fn_t erase_sector_commit;
+ spif_erase_sector_busy_fn_t erase_sector_busy;
+ spif_write_page_dispatch_fn_t write_page_dispatch;
+ spif_write_page_commit_fn_t write_page_commit;
+ spif_write_page_busy_fn_t write_page_busy;
+} spi_flash_ops_t;
+
+typedef enum {
+ IDLE, WRITE_IN_PROGRESS, ERASE_IN_PROGRESS
+} spi_flash_state_t;
+
+//A session struct that encapsulates everything about the flash
+//in a device agnostic way
+typedef struct {
+ const spi_flash_dev_t* device;
+ const spi_flash_ops_t* ops;
+ spi_flash_state_t state;
+ uint32_t last_offset;
+ uint16_t id;
+} spi_flash_session_t;
+
+/*!
+ * Initialize the spi_flash_session_t object
+ */
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops);
+
+/*!
+ * Read "num_bytes" from "offset" in the flash into the buffer "buf".
+ * This call will block until all data is available.
+ */
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+
+/*!
+ * Erase sector at "offset" in the flash.
+ * This call will block until the erase is complete.
+ */
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset);
+
+/*!
+ * Write "num_bytes" from buffer "buf" at "offset" in the flash.
+ * This call will block until the write is complete.
+ */
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+
+#endif /* INCLUDED_SPI_FLASH */
diff --git a/firmware/usrp3/include/flash/spif_spsn_s25flxx.h b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h
new file mode 100644
index 000000000..1e6eededf
--- /dev/null
+++ b/firmware/usrp3/include/flash/spif_spsn_s25flxx.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_SPIF_SPSN_S25FLXX_H
+#define INCLUDED_SPIF_SPSN_S25FLXX_H
+
+#include <flash/spi_flash.h>
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations();
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash);
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset);
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset);
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes);
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash);
+
+#endif /* INCLUDED_SPIF_SPSN_S25FLXX_H */
diff --git a/firmware/usrp3/include/fw_comm_protocol.h b/firmware/usrp3/include/fw_comm_protocol.h
new file mode 100644
index 000000000..5f0fc0768
--- /dev/null
+++ b/firmware/usrp3/include/fw_comm_protocol.h
@@ -0,0 +1,99 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_FW_COMM_PROTOCOL
+#define INCLUDED_FW_COMM_PROTOCOL
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+/*!
+ * Structs and constants for communication between firmware and host.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FW_COMM_PROTOCOL_SIGNATURE 0xACE3
+#define FW_COMM_PROTOCOL_VERSION 0
+#define FW_COMM_MAX_DATA_WORDS 16
+#define FW_COMM_PROTOCOL_MTU 256
+
+#define FW_COMM_FLAGS_ACK 0x00000001
+#define FW_COMM_FLAGS_CMD_MASK 0x00000FF0
+#define FW_COMM_FLAGS_ERROR_MASK 0xFF000000
+
+#define FW_COMM_CMD_ECHO 0x00000000
+#define FW_COMM_CMD_POKE32 0x00000010
+#define FW_COMM_CMD_PEEK32 0x00000020
+#define FW_COMM_CMD_BLOCK_POKE32 0x00000030
+#define FW_COMM_CMD_BLOCK_PEEK32 0x00000040
+
+#define FW_COMM_ERR_PKT_ERROR 0x80000000
+#define FW_COMM_ERR_CMD_ERROR 0x40000000
+#define FW_COMM_ERR_SIZE_ERROR 0x20000000
+
+#define FW_COMM_GENERATE_ID(prod) ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0) | \
+ (((uint32_t) prod) << 16) | \
+ (((uint32_t) FW_COMM_PROTOCOL_VERSION) << 24))
+#define FW_COMM_GET_PRODUCT_ID(id) ((uint8_t)(id >> 16))
+#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24))
+
+typedef struct
+{
+ uint32_t id; //Protocol and device identifier
+ uint32_t flags; //Holds commands and ack messages
+ uint32_t sequence; //Sequence number (specific to FW communication transactions)
+ uint32_t data_words; //Number of data words in payload
+ uint32_t addr; //Address field for the command in flags
+ uint32_t data[FW_COMM_MAX_DATA_WORDS]; //Data field for the command in flags
+} fw_comm_pkt_t;
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+// The following definitions are only useful in firmware. Exclude in host code.
+#ifndef __cplusplus
+
+typedef void (*poke32_func)(const uint32_t addr, const uint32_t data);
+typedef uint32_t (*peek32_func)(const uint32_t addr);
+
+/*!
+ * Process a firmware communication packet and compute a response.
+ * Args:
+ * - (in) request: Pointer to the request struct
+ * - (out) response: Pointer to the response struct
+ * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering)
+ * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke
+ * - return value: Send a response packet
+ */
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback
+);
+
+#endif //ifdef __cplusplus
+
+#endif /* INCLUDED_FW_COMM_PROTOCOL */
diff --git a/firmware/usrp3/include/mdelay.h b/firmware/usrp3/include/mdelay.h
deleted file mode 100644
index 226bbb3f7..000000000
--- a/firmware/usrp3/include/mdelay.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef INCLUDED_MDELAY_H
-#define INCLUDED_MDELAY_H
-
-/*!
- * \brief Delay about ms milliseconds
- *
- * If simulating, _very_ short delay
- */
-void mdelay(int ms);
-
-#endif /* INCLUDED_MDELAY_H */
diff --git a/firmware/usrp3/include/trace.h b/firmware/usrp3/include/trace.h
index 0daa231fe..015ae9049 100644
--- a/firmware/usrp3/include/trace.h
+++ b/firmware/usrp3/include/trace.h
@@ -31,6 +31,7 @@
* An alternate way of defining the level is the "TRACE_LEVEL"
* variable in cmake. (eg. -DTRACE_LEVEL=13).
*/
+
//#define UHD_FW_TRACE_LEVEL 13
typedef enum
diff --git a/firmware/usrp3/include/wb_soft_reg.h b/firmware/usrp3/include/wb_soft_reg.h
new file mode 100644
index 000000000..df23eed83
--- /dev/null
+++ b/firmware/usrp3/include/wb_soft_reg.h
@@ -0,0 +1,135 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_SOFT_REG_H
+#define INCLUDED_SOFT_REG_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <wb_utils.h>
+
+/* Keeps track of all metadata associated with a soft register.
+ * Use this struct when you need to manage a hardware register that needs
+ * to be accessed from different sections of software. If a register contains
+ * several unrelated bitfields, this object can be used to ensure coherency.
+ * It is recommended that the client hold this as a global/static object.
+ */
+typedef struct
+{
+ uint32_t wr_addr;
+ uint32_t rd_addr;
+ uint32_t soft_copy;
+} soft_reg_t;
+
+/* A register field is defined as a tuple of the mask and the shift.
+ * It can be used to make read-modify-write operations more convenient
+ * For efficiency reasons, it is recommended to always use a constant
+ * of this type because it will get optimized out by the compiler and
+ * will result in zero memory overhead
+ */
+typedef struct
+{
+ uint8_t num_bits;
+ uint8_t shift;
+} soft_reg_field_t;
+
+
+/*!
+ * Initialize the soft_reg_t struct as a read-write register.
+ * Params:
+ * - reg: Pointer to the soft_reg struct
+ * - wr_addr: The address used to flush the register to HW
+ * - rd_addr: The address used to read the register from HW
+ */
+inline void initialize_readwrite_soft_reg(soft_reg_t* reg, uint32_t wr_addr, uint32_t rd_addr)
+{
+ reg->wr_addr = wr_addr;
+ reg->rd_addr = rd_addr;
+ reg->soft_copy = 0;
+}
+
+/*!
+ * Initialize the soft_reg_t struct as a write-only register.
+ * Params:
+ * - reg: Pointer to the soft_reg struct
+ * - addr: The address used to flush the register to HW
+ */
+inline void initialize_writeonly_soft_reg(soft_reg_t* reg, uint32_t addr)
+{
+ reg->wr_addr = addr;
+ reg->rd_addr = 0;
+ reg->soft_copy = 0;
+}
+
+/*!
+ * Update specified field in the soft-copy with the arg value.
+ * Performs a read-modify-write operation so all other field are preserved.
+ * NOTE: This does not write the value to hardware.
+ */
+inline void soft_reg_set(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value)
+{
+ const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift;
+ reg->soft_copy = (reg->soft_copy & ~mask) | ((field_value << field.shift) & mask);
+}
+
+/*!
+ * Write the contents of the soft-copy to hardware.
+ */
+inline void soft_reg_flush(const soft_reg_t* reg)
+{
+ wb_poke32(reg->wr_addr, reg->soft_copy);
+}
+
+/*!
+ * Shortcut for a set and a flush.
+ */
+inline void soft_reg_write(soft_reg_t* reg, const soft_reg_field_t field, const uint32_t field_value)
+{
+ soft_reg_set(reg, field, field_value);
+ soft_reg_flush(reg);
+}
+
+/*!
+ * Get the value of the specified field from the soft-copy.
+ * NOTE: This does not read anything from hardware.
+ */
+inline uint32_t soft_reg_get(const soft_reg_t* reg, const soft_reg_field_t field)
+{
+ const uint32_t mask = ((1<<field.num_bits)-1)<<field.shift;
+ return (reg->soft_copy & mask) >> field.shift;
+}
+
+/*!
+ * Read the contents of the register from hardware and update the soft copy.
+ */
+inline void soft_reg_refresh(soft_reg_t* reg)
+{
+ if (reg->rd_addr) {
+ reg->soft_copy = wb_peek32(reg->rd_addr);
+ }
+}
+
+/*!
+ * Shortcut for refresh and get
+ */
+inline uint32_t soft_reg_read(soft_reg_t* reg, const soft_reg_field_t field)
+{
+ soft_reg_refresh(reg);
+ return soft_reg_get(reg, field);
+}
+
+#endif /* INCLUDED_SOFT_REG_H */
diff --git a/firmware/usrp3/include/wb_spi.h b/firmware/usrp3/include/wb_spi.h
new file mode 100644
index 000000000..ebbb20b16
--- /dev/null
+++ b/firmware/usrp3/include/wb_spi.h
@@ -0,0 +1,55 @@
+
+// Copyright 2012 Ettus Research LLC
+
+#ifndef INCLUDED_WB_SPI_H
+#define INCLUDED_WB_SPI_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+typedef enum {
+ WRITE, WRITE_READ
+} wb_spi_rw_mode_t;
+
+typedef enum {
+ RISING, FALLING
+} wb_spi_edge_t;
+
+typedef struct {
+ void* base;
+ uint32_t slave_sel;
+ uint32_t clk_div;
+ wb_spi_edge_t mosi_edge;
+ wb_spi_edge_t miso_edge;
+ bool lsb_first;
+} wb_spi_slave_t;
+
+/*!
+ * \brief Initialize SPI slave device
+ */
+void wb_spi_init(const wb_spi_slave_t* slave);
+
+/*!
+ * \brief Perform a SPI transaction in auto chip-select mode.
+ */
+inline void wb_spi_transact(const wb_spi_slave_t* slave,
+ wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length);
+
+/*!
+ * \brief Perform a SPI transaction in manual chip-select mode.
+ */
+inline void wb_spi_transact_man_ss(const wb_spi_slave_t* slave,
+ wb_spi_rw_mode_t rw_mode, const void* mosi_buf, void* miso_buf, uint32_t length);
+
+/*!
+ * \brief Select SPI slave
+ */
+void wb_spi_slave_select(const wb_spi_slave_t* slave);
+
+/*!
+ * \brief Deselect SPI slave
+ */
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave);
+
+#endif /* INCLUDED_WB_SPI_H */
diff --git a/firmware/usrp3/lib/CMakeLists.txt b/firmware/usrp3/lib/CMakeLists.txt
index 621b9b611..9d9ee3c6c 100644
--- a/firmware/usrp3/lib/CMakeLists.txt
+++ b/firmware/usrp3/lib/CMakeLists.txt
@@ -21,12 +21,16 @@ add_library(usrp3fw STATIC
udp_uart.c
wb_uart.c
wb_i2c.c
+ wb_spi.c
printf.c
wb_pkt_iface64.c
u3_net_stack.c
ethernet.c
- mdelay.c
chinch.c
print_addrs.c
link_state_route_proto.c
+ cron.c
+ fw_comm_protocol.c
+ flash/spi_flash.c
+ flash/spif_spsn_s25flxx.c
)
diff --git a/firmware/usrp3/lib/cron.c b/firmware/usrp3/lib/cron.c
new file mode 100644
index 000000000..24b8feb4e
--- /dev/null
+++ b/firmware/usrp3/lib/cron.c
@@ -0,0 +1,92 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "cron.h"
+
+//Counter specific
+static cron_counter_fetcher_t cron_fetch_counter;
+static uint32_t cron_counter_freq;
+
+//Cron job specific
+typedef struct {
+ uint32_t tick_interval;
+ uint32_t last_tick_count;
+} cron_job_t;
+
+static cron_job_t cron_job_table[CRON_MAX_JOBS];
+
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq)
+{
+ cron_fetch_counter = fetch_counter;
+ cron_counter_freq = counter_freq;
+
+ for (int i = 0; i < CRON_MAX_JOBS; i++) {
+ cron_job_table[i].tick_interval = 0;
+ }
+}
+
+uint32_t cron_get_ticks()
+{
+ return cron_fetch_counter();
+}
+
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit)
+{
+ return ((stop_ticks - start_ticks) / cron_counter_freq) * ((uint32_t)unit);
+}
+
+void sleep_ticks(uint32_t ticks)
+{
+ if (ticks == 0) return; //Handle the 0 delay case quickly
+
+ const uint32_t ticks_begin = cron_fetch_counter();
+ while(cron_fetch_counter() - ticks_begin < ticks) {
+ /*NOP: Spinloop*/
+ }
+}
+
+void sleep_us(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000000)));
+}
+
+void sleep_ms(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000)));
+}
+
+void cron_job_init(uint32_t job_id, uint32_t interval_ms)
+{
+ cron_job_table[job_id].tick_interval = (interval_ms * (cron_counter_freq/1000));
+ cron_job_table[job_id].last_tick_count = 0;
+}
+
+bool cron_job_run_due(uint32_t job_id)
+{
+ uint32_t new_tick_count = cron_fetch_counter();
+ bool run_job = (new_tick_count - cron_job_table[job_id].last_tick_count) >=
+ cron_job_table[job_id].tick_interval;
+
+ if (run_job) {
+ //If the job is due to run, update the tick count for the next run
+ //The assumption here is that the caller will actually run their job
+ //when the return value is true. If not, the caller just missed this
+ //iteration and will have to option to run the job in the next pass through.
+ cron_job_table[job_id].last_tick_count = new_tick_count;
+ }
+ return run_job;
+}
diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c
index 91efbfe1d..743aedf68 100644
--- a/firmware/usrp3/lib/ethernet.c
+++ b/firmware/usrp3/lib/ethernet.c
@@ -21,7 +21,7 @@
#endif
#include "../x300/x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <trace.h>
#include "wb_i2c.h"
#include "wb_utils.h"
@@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
int x;
// Delay read of SFPP
if (delay_ms)
- mdelay(delay_ms);
+ sleep_ms(delay_ms);
// Read ID code from SFP
x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
// I2C Error?
diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c
new file mode 100644
index 000000000..b4257c96f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spi_flash.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <flash/spi_flash.h>
+
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops)
+{
+ flash->device = device;
+ flash->ops = ops;
+ flash->state = IDLE;
+ flash->last_offset = 0;
+ flash->id = ops->read_id(device);
+}
+
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ flash->ops->read(flash->device, offset, buf, num_bytes);
+}
+
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset)
+{
+ if (flash->ops->erase_sector_dispatch(flash->device, offset)) {
+ return flash->ops->erase_sector_commit(flash->device, offset);
+ } else {
+ return false;
+ }
+}
+
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) {
+ return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes);
+ } else {
+ return false;
+ }
+}
diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
new file mode 100644
index 000000000..244115b6f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <cron.h>
+#include <trace.h>
+#include <string.h> //for memset, memcpy
+
+#define S25FLXX_CMD_WIDTH 8
+#define S25FLXX_ADDR_WIDTH 24
+
+/* S25FLxx-specific commands */
+#define S25FLXX_CMD_READID 0x90 /* Read Manufacturer and Device Identification */
+#define S25FLXX_CMD_READSIG 0xAB /* Read Electronic Signature (Will release from Deep PD) */
+#define S25FLXX_CMD_READ 0x03 /* Read Data Bytes */
+#define S25FLXX_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */
+
+#define S25FLXX_CMD_WREN 0x06 /* Write Enable */
+#define S25FLXX_CMD_WRDI 0x04 /* Write Disable */
+
+#define S25FLXX_CMD_PP 0x02 /* Page Program */
+#define S25FLXX_CMD_SE 0xD8 /* Sector Erase */
+#define S25FLXX_CMD_BE 0xC7 /* Bulk Erase */
+#define S25FLXX_CMD_DP 0xB9 /* Deep Power-down */
+
+#define S25FLXX_CMD_RDSR 0x05 /* Read Status Register */
+#define S25FLXX_CMD_WRSR 0x01 /* Write Status Register */
+
+#define S25FLXX_STATUS_WIP 0x01 /* Write in Progress */
+#define S25FLXX_STATUS_E_ERR 0x20 /* Erase Error Occured */
+#define S25FLXX_STATUS_P_ERR 0x40 /* Programming Error Occured */
+
+#define S25FLXX_SECTOR_ERASE_TIME_MS 750 //Spec: 650ms
+#define S25FLXX_PAGE_WRITE_TIME_MS 1 //Spec: 750us
+
+#define S25FLXX_SMALL_SECTORS_PER_LOGICAL 16 //16 4-kB physical sectors per logical sector
+#define S25FLXX_LARGE_SECTOR_BASE 0x20000 //Large physical sectors start at logical sector 2
+
+inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash)
+{
+ uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF;
+ wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */);
+ return status;
+}
+
+inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms)
+{
+ uint32_t start_ticks = cron_get_ticks();
+ do {
+ if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) {
+ return true;
+ }
+ } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms);
+
+ return false; // Timed out
+}
+
+inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled)
+{
+ uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI;
+ wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH);
+}
+
+const spi_flash_ops_t spif_spsn_s25flxx_ops =
+{
+ .read_id = spif_spsn_s25flxx_read_id,
+ .read = spif_spsn_s25flxx_read,
+ .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch,
+ .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit,
+ .erase_sector_busy = spif_spsn_s25flxx_device_busy,
+ .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch,
+ .write_page_commit = spif_spsn_s25flxx_write_page_commit,
+ .write_page_busy = spif_spsn_s25flxx_device_busy
+};
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations()
+{
+ return &spif_spsn_s25flxx_ops;
+}
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash)
+{
+ wb_spi_slave_select(flash->bus);
+ uint32_t command = S25FLXX_CMD_READID << 24;
+ wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32);
+ uint16_t id = 0;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16);
+ wb_spi_slave_deselect(flash->bus);
+ return id;
+}
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 5 byte instruction tranfer:
+ //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0)
+ uint8_t read_cmd[5];
+ read_cmd[4] = S25FLXX_CMD_FAST_READ;
+ *((uint32_t*)(read_cmd + 3)) = (offset << 8);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8);
+
+ //Read up to 4 bytes at a time until done
+ uint8_t data_sw[16], data[16];
+ size_t xact_size = 16;
+ unsigned char *bytes = (unsigned char *) buf;
+ for (size_t i = 0; i < num_bytes; i += 16) {
+ if (xact_size > num_bytes - i) xact_size = num_bytes - i;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8);
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k];
+ }
+ for (size_t j = 0; j < xact_size; j++) {
+ *bytes = data[j];
+ bytes++;
+ }
+ }
+ wb_spi_slave_deselect(flash->bus);
+}
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Sanity check sector size
+ if (offset % flash->sector_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size.");
+ return false;
+ }
+
+ if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //Send sector erase command
+ uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF);
+ wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Poll status until write done
+ uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1;
+ bool status = false;
+ for (uint8_t i = 0; i < phy_sector_count && !status; i++) {
+ status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS);
+ }
+ if (!status) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset);
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return status;
+}
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (num_bytes == 0 || num_bytes > flash->page_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size.");
+ return false;
+ }
+ if (num_bytes > (flash->sector_size * flash->num_sectors)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary.");
+ return false;
+ }
+
+ //Wait until ready and enable write enabled
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 4 byte instruction tranfer:
+ //PP_CMD, ADDR2, ADDR1, ADDR0
+ uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32);
+
+ //Write the page 16 bytes at a time.
+ uint8_t bytes_sw[16];
+ uint8_t* bytes = (uint8_t*) buf;
+ for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) {
+ const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16;
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k];
+ }
+ wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8);
+ bytes += xact_size;
+ }
+ wb_spi_slave_deselect(flash->bus);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ //Wait until write done
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time.");
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return true;
+}
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash)
+{
+ return (_spif_read_status(flash) & S25FLXX_STATUS_WIP);
+}
+
diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c
new file mode 100644
index 000000000..cf13e7d22
--- /dev/null
+++ b/firmware/usrp3/lib/fw_comm_protocol.c
@@ -0,0 +1,102 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <fw_comm_protocol.h>
+#include <trace.h>
+#include <string.h> //memcmp
+
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback)
+{
+ bool send_response = false;
+
+ uint16_t signature = request->id;
+ uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
+ uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
+ if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
+ version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
+ product == product_id) //Verify device
+ {
+ //Request is valid. Copy it into the reply.
+ memcpy(response, request, sizeof(fw_comm_pkt_t));
+
+ //Start assuming no error
+ response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
+
+ //Otherwise, run the command set by the flags
+ switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
+ case FW_COMM_CMD_ECHO: {
+ //Do nothing.
+ UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
+ } break;
+
+ case FW_COMM_CMD_POKE32: {
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
+ request->addr,*(request->data));
+ poke_callback(request->addr, *(request->data));
+ } break;
+
+ case FW_COMM_CMD_PEEK32: {
+ *(response->data) = peek_callback(request->addr);
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
+ request->addr,*(response->data));
+ } break;
+
+ case FW_COMM_CMD_BLOCK_POKE32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
+ }
+ } break;
+
+ case FW_COMM_CMD_BLOCK_PEEK32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
+ } break;
+
+ default: {
+ UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
+ response->flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+
+ //Send a reply if ack requested
+ send_response = (request->flags & FW_COMM_FLAGS_ACK);
+ } else { //Size, protocol, product check failed
+ UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
+ send_response = false;
+ }
+ return send_response;
+}
diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c
deleted file mode 100644
index 6d2742206..000000000
--- a/firmware/usrp3/lib/mdelay.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- * Copyright 2009 Ettus Research LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mdelay.h"
-#include "wb_utils.h"
-#include "printf.h"
-#include <stdint.h>
-//IJB FIXME.
-#include "../x300/x300_defs.h"
-
-void mdelay(int ms){
- for(int i = 0; i < ms; i++){
- static const uint32_t num_ticks = CPU_CLOCK/1000;
- const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
- // printf("DEBUG: Counter is %d\n",ticks_begin);
- while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
- /*NOP*/
- }
- }
-}
diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c
new file mode 100644
index 000000000..04904feea
--- /dev/null
+++ b/firmware/usrp3/lib/wb_spi.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <trace.h>
+
+typedef struct {
+ volatile uint32_t data0;
+ volatile uint32_t data1;
+ volatile uint32_t data2;
+ volatile uint32_t data3;
+ volatile uint32_t ctrl_status;
+ volatile uint32_t clkdiv;
+ volatile uint32_t slavesel;
+} wb_spi_regs_t;
+
+#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base)
+
+// Masks for different parts of CTRL reg
+#define WB_SPI_CTRL_AUTO_SS (1 << 13)
+#define WB_SPI_CTRL_IE (1 << 12)
+#define WB_SPI_CTRL_LSB (1 << 11)
+#define WB_SPI_CTRL_TXNEG (1 << 10)
+#define WB_SPI_CTRL_RXNEG (1 << 9)
+#define WB_SPI_CTRL_GO_BSY (1 << 8)
+#define WB_SPI_CTRL_LENGTH(x) (x & 0x7F)
+
+static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave)
+{
+ uint32_t flags = 0;
+ //If the SPI slave samples on the rising edge then shift
+ //data out on the falling edge.
+ if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG;
+ //If the SPI slave drives on the rising edge then shift
+ //data in on the falling edge.
+ if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG;
+ if (slave->lsb_first) flags |= WB_SPI_CTRL_LSB;
+ return flags;
+}
+
+static inline void _wait_for_xfer(const wb_spi_slave_t* slave)
+{
+ while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) {
+ /*NOP*/
+ }
+}
+
+void wb_spi_init(const wb_spi_slave_t* slave)
+{
+ WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div;
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+
+ //Do a dummy transaction with no slave selected to prime the engine
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave);
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+ _wait_for_xfer(slave);
+}
+
+void _wb_spi_transact_buf(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length,
+ bool auto_slave_sel)
+{
+ if (length == 0) return;
+
+ //Wait for previous transaction to finish
+ _wait_for_xfer(slave);
+
+ //Write SPI data register(s)
+ if (mosi_buf) {
+ uint8_t* mosi_bytes = (uint8_t*) mosi_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ word = (uint32_t) mosi_bytes[0];
+ } else if (bits_left <= 16) {
+ word = (((uint32_t) mosi_bytes[1]) << 0) |
+ (((uint32_t) mosi_bytes[0]) << 8);
+ } else if (bits_left <= 24) {
+ word = (((uint32_t) mosi_bytes[2]) << 0) |
+ (((uint32_t) mosi_bytes[1]) << 8) |
+ (((uint32_t) mosi_bytes[0]) << 16);
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ }
+ bits_left = 0;
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ mosi_bytes += 4;
+ bits_left -= 32;
+ }
+
+ switch (reg_index) {
+ case 0: WB_SPI_REGS(slave->base)->data0 = word; break;
+ case 1: WB_SPI_REGS(slave->base)->data1 = word; break;
+ case 2: WB_SPI_REGS(slave->base)->data2 = word; break;
+ case 3: WB_SPI_REGS(slave->base)->data3 = word; break;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+
+ //Compute flags for slave and write control register
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave);
+ if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS;
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl;
+
+ // Tell it which SPI slave device to access
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+
+ //Go go go!
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+
+ if (rw_mode == WRITE_READ) {
+ //Wait for SPI read operation to complete
+ _wait_for_xfer(slave);
+
+ if (miso_buf) {
+ //Read SPI data registers
+ uint8_t* miso_bytes = (uint8_t*) miso_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ switch (reg_index) {
+ case 0: word = WB_SPI_REGS(slave->base)->data0; break;
+ case 1: word = WB_SPI_REGS(slave->base)->data1; break;
+ case 2: word = WB_SPI_REGS(slave->base)->data2; break;
+ case 3: word = WB_SPI_REGS(slave->base)->data3; break;
+ }
+
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ miso_bytes[0] = word & 0xFF;
+ } else if (bits_left <= 16) {
+ miso_bytes[1] = word & 0xFF;
+ miso_bytes[0] = (word >> 8) & 0xFF;
+ } else if (bits_left <= 24) {
+ miso_bytes[2] = word & 0xFF;
+ miso_bytes[1] = (word >> 8) & 0xFF;
+ miso_bytes[0] = (word >> 16) & 0xFF;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ }
+ bits_left = 0;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ miso_bytes += 4;
+ bits_left -= 32;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+ }
+}
+
+void wb_spi_transact(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true);
+}
+
+void wb_spi_transact_man_ss(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false);
+}
+
+void wb_spi_slave_select(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually select slave
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+}
+
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually deselect slave
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+}
diff --git a/firmware/usrp3/n230/CMakeLists.txt b/firmware/usrp3/n230/CMakeLists.txt
new file mode 100644
index 000000000..e473b2b6c
--- /dev/null
+++ b/firmware/usrp3/n230/CMakeLists.txt
@@ -0,0 +1,35 @@
+#
+# Copyright 2010-2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/../../host/lib/usrp/n230)
+
+list(APPEND n230_sources n230_eeprom.c n230_eth_handlers.c n230_init.c n230_main.c)
+
+########################################################################
+set(GEN_OUTPUTS_BIN_SIZE 0x3fff)
+
+add_executable(n230_main.elf ${n230_sources})
+target_link_libraries(n230_main.elf usrp3fw)
+GEN_OUTPUTS(n230_main.elf n230)
+
+#INSTALL(
+# FILES ${CMAKE_CURRENT_BINARY_DIR}/n230_main.bin
+# DESTINATION share/uhd/images
+# RENAME usrp_n230_fw.bin
+#)
diff --git a/firmware/usrp3/n230/n230_burner.py b/firmware/usrp3/n230/n230_burner.py
new file mode 100755
index 000000000..3f6bb3fcf
--- /dev/null
+++ b/firmware/usrp3/n230/n230_burner.py
@@ -0,0 +1,359 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import os.path
+import sys
+from array import array
+
+########################################################################
+# constants
+########################################################################
+N230_FLASH_COMM_UDP_PORT = 49154
+N230_FLASH_COMM_PAYLOAD_SIZE = 128
+N230_FLASH_COMM_SECTOR_SIZE = 65536
+
+N230_FLASH_COMM_FLAGS_ACK = 0x00000001
+N230_FLASH_COMM_FLAGS_CMD_MASK = 0x00000FF0
+N230_FLASH_COMM_FLAGS_ERROR_MASK = 0xFF000000
+
+N230_FLASH_COMM_CMD_READ_NV_DATA = 0x00000010
+N230_FLASH_COMM_CMD_WRITE_NV_DATA = 0x00000020
+N230_FLASH_COMM_CMD_READ_FPGA = 0x00000030
+N230_FLASH_COMM_CMD_WRITE_FPGA = 0x00000040
+N230_FLASH_COMM_CMD_ERASE_FPGA = 0x00000050
+
+N230_FLASH_COMM_ERR_PKT_ERROR = 0x80000000
+N230_FLASH_COMM_ERR_CMD_ERROR = 0x40000000
+N230_FLASH_COMM_ERR_SIZE_ERROR = 0x20000000
+
+N230_FLASH_COMM_SAFE_IMG_BASE = 0x000000
+N230_FLASH_COMM_PROD_IMG_BASE = 0x400000
+N230_FLASH_COMM_FPGA_IMG_MAX_SIZE = 0x400000
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+
+_seq = -1
+def next_seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+def seq():
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+short = struct.Struct('>H')
+ulong = struct.Struct('>I')
+
+def unpack_flash_transaction(buf):
+ (flags, seqno, offset, size) = struct.unpack_from('!LLLL', buf)
+ check_error(flags)
+ if (seqno != seq()):
+ raise Exception("The flash transaction operation returned an incorrect sequence number")
+ data = bytes()
+ for i in xrange(16, len(buf), 1):
+ data += buf[i]
+ return (flags, offset, size, data)
+
+def pack_flash_transaction(flags, offset, size, data):
+ buf = bytes()
+ buf = struct.pack('!LLLL', flags, next_seq(), offset, size)
+ for i in range(N230_FLASH_COMM_PAYLOAD_SIZE):
+ if (i < size):
+ buf += struct.pack('!B', data[i])
+ else:
+ buf += struct.pack('!B', 0)
+ return buf
+
+def check_error(flags):
+ if flags & N230_FLASH_COMM_ERR_PKT_ERROR == N230_FLASH_COMM_ERR_PKT_ERROR:
+ raise Exception("The flash transaction operation returned a packet error")
+ if flags & N230_FLASH_COMM_ERR_CMD_ERROR == N230_FLASH_COMM_ERR_CMD_ERROR:
+ raise Exception("The flash transaction operation returned a command error")
+ if flags & N230_FLASH_COMM_ERR_SIZE_ERROR == N230_FLASH_COMM_ERR_SIZE_ERROR:
+ raise Exception("The flash transaction operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Burner class, holds a socket and send/recv routines
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr):
+ self._safe_image = False
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, N230_FLASH_COMM_UDP_PORT))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+
+ def set_safe_image(self, noprompt):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to access the safe-image stored in the flash \n'
+ '----------------------------------------------------------------------\n'
+ 'Writing a non-functional image will brick the device.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ self._safe_image = True
+ else:
+ print 'Aborted by user'
+ sys.exit(1)
+ else:
+ print '[WARNING] Operating on safe image without a prompt as requested'
+ self._safe_image = True
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send_and_recv(self, pkt):
+ self._sock.send(pkt)
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def compute_offset(self, offset):
+ base = N230_FLASH_COMM_SAFE_IMG_BASE if (self._safe_image) else N230_FLASH_COMM_PROD_IMG_BASE
+ return base + offset
+
+ def burn_fpga_to_flash(self, bitfile_path, noprompt):
+ print '[BURN] Reading ' + bitfile_path + '...'
+ with open(bitfile_path, 'rb') as bitfile:
+ header = file_bytes = bitfile.read()
+ if (self._safe_image != self.parse_bitfile_header(file_bytes)['safe-image']):
+ confirm_msg = ('----------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to burn a safe image into a production slot \n'
+ ' or a production image into a safe slot. \n'
+ '----------------------------------------------------------------------\n'
+ 'This is dangerous and can cause the device to boot incorrectly.\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() != 'y':
+ print '[BURN] Aborted by user'
+ return
+ else:
+ print '[WARNING] Burning image to the wrong slot without a prompt as requested'
+
+ print '[BURN] Writing to flash...'
+ pkt_chunks = chunkify(file_bytes, N230_FLASH_COMM_PAYLOAD_SIZE)
+ offset = 0
+ for pkt_data in pkt_chunks:
+ pkt_data = array("B", pkt_data)
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (len(pkt_data) >= N230_FLASH_COMM_PAYLOAD_SIZE) else len(pkt_data)
+ #Erase sector
+ if (offset % N230_FLASH_COMM_SECTOR_SIZE == 0):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Write data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, pkt_data)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ #Increment
+ offset += N230_FLASH_COMM_PAYLOAD_SIZE
+ draw_progress_bar((((offset/N230_FLASH_COMM_PAYLOAD_SIZE)+1)*100)/len(pkt_chunks))
+ print('\n[BURN] DONE')
+
+ def parse_bitfile_header(self, header_bytes):
+ xil_header = dict()
+ n230_header = dict()
+ n230_header['valid'] = False
+ ptr = 0
+ #Field 1
+ if short.unpack(header_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(header_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
+ #Headers
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 2
+ ptr += short.unpack(header_bytes[ptr:ptr+2])[0] + 1
+ #Fields a-d
+ for keynum in range(0, 4):
+ key = header_bytes[ptr]
+ ptr += 1
+ val_len = short.unpack(header_bytes[ptr:ptr+2])[0]
+ ptr += 2
+ val = header_bytes[ptr:ptr+val_len]
+ ptr += val_len
+ xil_header[key] = val
+ #Field e
+ ptr += 1
+ length = ulong.unpack(header_bytes[ptr:ptr+4])[0]
+ xil_header['bl'] = length #Bitstream length
+ ptr += 4
+ xil_header['hl'] = ptr #Header lengt
+
+ #Map Xilinx header field to N230 specific ones
+ if xil_header and xil_header['a'].split(';')[0] == 'n230':
+ n230_header['valid'] = True
+ n230_header['user-id'] = int(xil_header['a'].split(';')[1].split('=')[1][0:-1], 16)
+ n230_header['safe-image'] = n230_header['user-id'] >> 16 == 0x5AFE
+ n230_header['product'] = xil_header['b']
+ n230_header['timestamp'] = xil_header['c'] + ' ' + xil_header['d']
+ n230_header['filesize'] = xil_header['hl'] + xil_header['bl']
+ return n230_header
+
+ def read_bitfile_header_from_flash(self):
+ max_header_size = 1024 #Should be enough
+ header_bytes = bytes()
+ for offset in range(0, max_header_size, N230_FLASH_COMM_PAYLOAD_SIZE):
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ header_bytes += data
+ return self.parse_bitfile_header(header_bytes)
+
+ def extract_fpga_from_flash(self, bitfile_path):
+ header = self.read_bitfile_header_from_flash();
+ if not header['valid']:
+ raise Exception("Could not detect a vaild Xilinx .bit burned into the flash")
+ max_offset = header['filesize']
+ print '[EXTRACT] Writing ' + bitfile_path + '...'
+ with open(bitfile_path, 'wb') as bitfile:
+ for i in range(0, int(math.ceil(float(max_offset)/N230_FLASH_COMM_PAYLOAD_SIZE))):
+ offset = i * N230_FLASH_COMM_PAYLOAD_SIZE
+ size = N230_FLASH_COMM_PAYLOAD_SIZE if (max_offset - offset >= N230_FLASH_COMM_PAYLOAD_SIZE) else (max_offset - offset)
+ #Read data
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_READ_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), size, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ bitfile.write(data[:size])
+ draw_progress_bar(((offset*100)/max_offset) + 1)
+ print('\n[EXTRACT] DONE')
+
+ def erase_fpga_from_flash(self):
+ print '[ERASE] Erasing image from flash...'
+ for offset in range(0, N230_FLASH_COMM_FPGA_IMG_MAX_SIZE, N230_FLASH_COMM_SECTOR_SIZE):
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA
+ out_pkt = pack_flash_transaction(flags, self.compute_offset(offset), N230_FLASH_COMM_PAYLOAD_SIZE, [0]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ draw_progress_bar(((offset+N230_FLASH_COMM_SECTOR_SIZE)*100)/N230_FLASH_COMM_FPGA_IMG_MAX_SIZE)
+ print('\n[ERASE] DONE')
+
+ def wipe_user_data(self, noprompt):
+ confirm_msg = ('-------------------------------------------------------------------\n'
+ 'WARNING!!! You are about to erase all the user data from the flash \n'
+ '-------------------------------------------------------------------\n'
+ 'This will cause the device to lose the following:\n'
+ ' * IP Address (Will default to 192.168.10.2)\n'
+ ' * Subnet Mask (Will default to 255.255.255.2)\n'
+ ' * MAC Address\n'
+ ' * Serial Number\n'
+ ' * Hardware Revision\n'
+ ' * ...and other identification info\n'
+ 'Are you sure you want to proceed?')
+ if not noprompt:
+ if raw_input("%s (y/N) " % confirm_msg).lower() == 'y':
+ wipe_ok = True
+ else:
+ print '[WIPE] Aborted by user'
+ wipe_ok = False
+ else:
+ print '[WARNING] Wiping user data without prompt a as requested'
+ wipe_ok = True
+
+ if wipe_ok:
+ print '[WIPE] Erasing all user data from flash...'
+ flags = N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_NV_DATA
+ out_pkt = pack_flash_transaction(flags, 0, N230_FLASH_COMM_PAYLOAD_SIZE, [0xFF]*N230_FLASH_COMM_PAYLOAD_SIZE)
+ (flags, real_offset, size, data) = unpack_flash_transaction(self.send_and_recv(out_pkt))
+ print('[WIPE] DONE. Please power-cycle the device.')
+
+ def print_status(self):
+ header = self.read_bitfile_header_from_flash();
+ if header['valid']:
+ print('[STATUS] Detected a valid .bit header in the flash (Product = %s, Datestamp = %s%s)' % \
+ (header['product'], header['timestamp'], ', Safe-Image' if header['safe-image'] else ''))
+ else:
+ print('[STATUS] No .bit header detected. Either the flash is uninitialized or the image is corrupt.')
+
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--status", action="store_true", help="Print out the status of the burned image", default=False)
+ parser.add_option("--erase", action="store_true", help="Erase FPGA bitstream from flash", default=False)
+ parser.add_option("--burn", type="string", help="Path to FPGA bitstream (.bit) to burn to flash", default=None)
+ parser.add_option("--extract", type="string", help="Destination bitfile to dump contents of the extracted image", default=None)
+ parser.add_option("--safe_image", action="store_true", help="Operate on the safe image. WARNING: This could be dangerous", default=False)
+ parser.add_option("--wipe_user_data", action="store_true", help="Erase all user data like IP, MAC, S/N, etc from flash", default=False)
+ parser.add_option("--no_prompt", action="store_true", help="Suppress all warning prompts", default=False)
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if not options.addr: raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(addr=options.addr)
+
+ # Initialize safe image selector first
+ if options.safe_image:
+ ctrl_sock.set_safe_image(options.no_prompt)
+
+ if options.status:
+ ctrl_sock.print_status()
+
+ # Order of operations:
+ # 1. Extract (if specified)
+ # 2. Erase (if specified)
+ # 3. Burn (if specified)
+
+ if options.extract is not None:
+ file_path = options.extract
+ ctrl_sock.print_status()
+ ctrl_sock.extract_fpga_from_flash(file_path)
+
+ if options.erase:
+ ctrl_sock.erase_fpga_from_flash()
+ ctrl_sock.print_status()
+
+ if options.burn is not None:
+ file_path = options.burn
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.bit'):
+ ctrl_sock.burn_fpga_to_flash(file_path, options.no_prompt)
+ ctrl_sock.print_status()
+ else:
+ raise Exception("Unsupported FPGA bitfile format. You must use a .bit file.")
+
+ if options.wipe_user_data:
+ ctrl_sock.wipe_user_data(options.no_prompt)
diff --git a/firmware/usrp3/n230/n230_debug.py b/firmware/usrp3/n230/n230_debug.py
new file mode 100755
index 000000000..41772168b
--- /dev/null
+++ b/firmware/usrp3/n230/n230_debug.py
@@ -0,0 +1,402 @@
+#!/usr/bin/env python
+#
+# Copyright 2010-2011 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+import math
+import socket
+import struct
+import array
+import os.path
+import sys
+import time
+try:
+ import fcntl
+ N230_DEVICE_DISCOVERY_AVAILABLE = True
+except:
+ N230_DEVICE_DISCOVERY_AVAILABLE = False
+
+########################################################################
+# constants
+########################################################################
+N230_FW_COMMS_UDP_PORT = 49152
+N230_FW_COMMS_MAX_DATA_WORDS = 16
+
+N230_FW_COMMS_FLAGS_ACK = 0x00000001
+N230_FW_COMMS_FLAGS_ERROR_MASK = 0xF0000000
+N230_FW_COMMS_FLAGS_CMD_MASK = 0x000000F0
+
+N230_FW_COMMS_CMD_ECHO = 0x00000000
+N230_FW_COMMS_CMD_POKE32 = 0x00000010
+N230_FW_COMMS_CMD_PEEK32 = 0x00000020
+N230_FW_COMMS_CMD_BLOCK_POKE32 = 0x00000030
+N230_FW_COMMS_CMD_BLOCK_PEEK32 = 0x00000040
+
+N230_FW_COMMS_ERR_PKT_ERROR = 0x80000000
+N230_FW_COMMS_ERR_CMD_ERROR = 0x40000000
+N230_FW_COMMS_ERR_SIZE_ERROR = 0x20000000
+
+N230_FW_COMMS_ID = 0x0001ACE3
+
+N230_FW_LOADER_BASE_ADDR = 0x4000
+N230_FW_LOADER_CHUNK_SIZE = 16
+N230_FW_LOADER_BOOT_DONE_ADDR = 0xA004
+N230_FW_LOADER_BOOT_TIMEOUT = 5
+
+N230_JESD204_TEST = 0xA014
+N230_FPGA_HASH_ADDR = 0xA010
+N230_FW_HASH_ADDR = 0x10004
+N230_ICAP_ADDR = 0xF800
+#ICAP_DUMMY_WORD = 0xFFFFFFFF
+#ICAP_SYNC_WORD = 0xAA995566
+#ICAP_TYPE1_NOP = 0x20000000
+#ICAP_WRITE_WBSTAR = 0x30020001
+#ICAP_WBSTAR_ADDR = 0x00000000
+#ICAP_WRITE_CMD = 0x30008001
+#ICAP_IPROG_CMD = 0x0000000F
+# Bit reversed values per Xilinx UG470 - Bits reversed within bytes.
+ICAP_DUMMY_WORD = 0xFFFFFFFF
+ICAP_SYNC_WORD = 0x5599AA66
+ICAP_TYPE1_NOP = 0x04000000
+ICAP_WRITE_WBSTAR = 0x0C400080
+ICAP_WBSTAR_ADDR = 0x00000000
+ICAP_WRITE_CMD = 0x0C000180
+ICAP_IPROG_CMD = 0x000000F0
+
+
+UDP_MAX_XFER_BYTES = 256
+UDP_TIMEOUT = 3
+FPGA_LOAD_TIMEOUT = 10
+
+_seq = -1
+def seq():
+ global _seq
+ _seq = _seq+1
+ return _seq
+
+########################################################################
+# helper functions
+########################################################################
+
+def pack_fw_command(flags, seq, num_words, addr, data_arr):
+ if (num_words > N230_FW_COMMS_MAX_DATA_WORDS):
+ raise Exception("Data size too large. Firmware supports a max 16 words per block." % (addr))
+ buf = bytes()
+ buf = struct.pack('!IIIII', N230_FW_COMMS_ID, flags, seq, num_words, addr)
+ for i in range(N230_FW_COMMS_MAX_DATA_WORDS):
+ if (i < num_words):
+ buf += struct.pack('!I', data_arr[i])
+ else:
+ buf += struct.pack('!I', 0)
+ return buf
+
+def unpack_fw_command(buf, fmt=None):
+ (id, flags, seq, num_words, addr) = struct.unpack_from('!IIIII', buf)
+ fw_check_error(flags)
+ data = []
+ if fmt is None:
+ fmt = 'I'
+ for i in xrange(20, len(buf), 4):
+ data.append(struct.unpack('!'+fmt, buf[i:i+4])[0])
+ return (flags, seq, num_words, addr, data)
+
+def fw_check_error(flags):
+ if flags & N230_FW_COMMS_ERR_PKT_ERROR == N230_FW_COMMS_ERR_PKT_ERROR:
+ raise Exception("The fiwmware operation returned a packet error")
+ if flags & N230_FW_COMMS_ERR_CMD_ERROR == N230_FW_COMMS_ERR_CMD_ERROR:
+ raise Exception("The fiwmware operation returned a command error")
+ if flags & N230_FW_COMMS_ERR_SIZE_ERROR == N230_FW_COMMS_ERR_SIZE_ERROR:
+ raise Exception("The fiwmware operation returned a size error")
+
+def chunkify(stuff, n):
+ return [stuff[i:i+n] for i in range(0, len(stuff), n)]
+
+def draw_progress_bar(percent, bar_len = 32):
+ sys.stdout.write("\r")
+ progress = ""
+ for i in range(bar_len):
+ if i < int((bar_len * percent) / 100):
+ progress += "="
+ else:
+ progress += "-"
+ sys.stdout.write("[%s] %d%%" % (progress, percent))
+ sys.stdout.flush()
+
+########################################################################
+# Discovery class
+########################################################################
+class discovery_socket(object):
+ def __init__(self):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ self._sock.settimeout(0.250)
+
+ def get_bcast_addrs(self):
+ max_possible = 128 # arbitrary. raise if needed.
+ num_bytes = max_possible * 32
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ names = array.array('B', '\0' * num_bytes)
+ outbytes = struct.unpack('iL', fcntl.ioctl(
+ s.fileno(),
+ 0x8912, # SIOCGIFCONF
+ struct.pack('iL', num_bytes, names.buffer_info()[0])
+ ))[0]
+ namestr = names.tostring()
+ lst = []
+ for i in range(0, outbytes, 40):
+ name = namestr[i:i+16].split('\0', 1)[0]
+ ip = map(ord, namestr[i+20:i+24])
+ mask = map(ord, fcntl.ioctl(s.fileno(), 0x891B, struct.pack('256s', name))[20:24])
+ bcast = []
+ for i in range(len(ip)):
+ bcast.append((ip[i] | (~mask[i])) & 0xFF)
+ if (name != 'lo'):
+ lst.append(str(bcast[0]) + '.' + str(bcast[1]) + '.' + str(bcast[2]) + '.' + str(bcast[3]))
+ return lst
+
+ def discover(self):
+ addrs = []
+ for bcast_addr in self.get_bcast_addrs():
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 0, 0, [0])
+ self._sock.sendto(out_pkt, (bcast_addr, N230_FW_COMMS_UDP_PORT))
+ while 1:
+ try:
+ (in_pkt, addr_pair) = self._sock.recvfrom(UDP_MAX_XFER_BYTES)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ addrs.append(addr_pair[0])
+ except socket.error:
+ break
+ return addrs
+
+
+########################################################################
+# Communications class, holds a socket and send/recv routine
+########################################################################
+class ctrl_socket(object):
+ def __init__(self, addr):
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self._sock.settimeout(UDP_TIMEOUT)
+ self._sock.connect((addr, N230_FW_COMMS_UDP_PORT))
+ self.set_callbacks(lambda *a: None, lambda *a: None)
+
+ def set_callbacks(self, progress_cb, status_cb):
+ self._progress_cb = progress_cb
+ self._status_cb = status_cb
+
+ def send(self, pkt):
+ self._sock.send(pkt)
+
+ def recv(self):
+ return self._sock.recv(UDP_MAX_XFER_BYTES)
+
+ def send_and_recv(self, pkt):
+ self.send(pkt)
+ return self.recv()
+
+ def read_time_stats(self):
+ print
+ regs = [' ingress1',' ingress2',' egress1',' egress1']
+ for reg in range (0, 4):
+ print("%s " % regs[reg]),
+ data = self.peek64(0xA000 + 32 + (reg*8), fmt='i')
+ print("%10d " % (data)),
+ print("%10f uS" % (data * 0.0217))
+ print
+
+ def peek(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, peek_addr, [0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return data[0]
+
+ def peek64(self, peek_addr, fmt=None):
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_BLOCK_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 2, peek_addr, [0,0])
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt, fmt)
+ return (data[0] | (data[1] << 32))
+
+ def poke(self, poke_addr, poke_data, ack=True):
+ ack_flag = N230_FW_COMMS_FLAGS_ACK if ack else 0
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32|ack_flag, seq(), 1, poke_addr, [poke_data])
+ self.send(out_pkt)
+ if ack:
+ in_pkt = self.recv()
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+
+ def live_load_firmware_bin(self, bin_path):
+ raise Exception("live_load_firmware_bin not implemented yet!")
+
+ def live_load_firmware_coe(self, coe_path):
+ with open(coe_path, 'r') as coe_file:
+ print("Loading %s..." % coe_path)
+ coe_lines = [line.strip(',;\n ') for line in coe_file]
+ start_index = coe_lines.index("memory_initialization_vector=") + 1
+ coe_words = coe_lines[start_index:]
+ for i in range(0, len(coe_words), N230_FW_LOADER_CHUNK_SIZE):
+ data_chunk = coe_words[i:i+(N230_FW_LOADER_CHUNK_SIZE)]
+ data = [0] * N230_FW_COMMS_MAX_DATA_WORDS
+ for j in range(len(data_chunk)):
+ data[j] = int(data_chunk[j], 16)
+ out_pkt = pack_fw_command(
+ N230_FW_COMMS_CMD_BLOCK_POKE32|N230_FW_COMMS_FLAGS_ACK,
+ seq(), len(data_chunk), N230_FW_LOADER_BASE_ADDR+(i*4), data)
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ draw_progress_bar(((i+N230_FW_LOADER_CHUNK_SIZE)*100)/len(coe_words))
+ print("\nBooting...")
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_POKE32, seq(), 1, N230_FW_LOADER_BOOT_DONE_ADDR, [1])
+ self._sock.send(out_pkt)
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_PEEK32|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(N230_FW_LOADER_BOOT_TIMEOUT):
+ try:
+ self._sock.send(out_pkt)
+ in_pkt = self._sock.recv(UDP_MAX_XFER_BYTES)
+ print("Firmware is alive!")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("Firmware boot FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def read_hash(self):
+ fpga_hash = self.peek(N230_FPGA_HASH_ADDR)
+ fpga_status = "clean" if (fpga_hash & 0xf0000000 == 0x0) else "dirty"
+ fw_hash = self.peek(N230_FW_HASH_ADDR)
+ fw_status = "clean" if (fw_hash & 0xf0000000 == 0x0) else "dirty"
+ print("FPGA Git Hash is: %x (%s)" % (fpga_hash & 0xfffffff, fpga_status))
+ print("Firmware Git Hash is: %x (%s)" % (fw_hash & 0xfffffff, fw_status))
+
+ def is_claimed(self):
+ claimed = self.peek(0x10008)
+ print("Claimed: %s") % ('Yes' if claimed else 'No')
+
+ def reset_fpga(self):
+ print("Reseting USRP...")
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_DUMMY_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_SYNC_WORD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_WBSTAR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WBSTAR_ADDR)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_TYPE1_NOP)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_WRITE_CMD)
+ ctrl_sock.poke(N230_ICAP_ADDR,ICAP_IPROG_CMD, False)
+ print("Waiting for FPGA to load...")
+ self._sock.settimeout(1)
+ out_pkt = pack_fw_command(N230_FW_COMMS_CMD_ECHO|N230_FW_COMMS_FLAGS_ACK, seq(), 1, 0, [0])
+ for i in range(FPGA_LOAD_TIMEOUT):
+ try:
+ in_pkt = self.send_and_recv(out_pkt)
+ (flags, ack_seq, block_size, addr, data) = unpack_fw_command(in_pkt)
+ print("FPGA loaded successfully.")
+ self._sock.settimeout(UDP_TIMEOUT)
+ return
+ except socket.error:
+ pass
+ print("FPGA load FAILED!!!")
+ self._sock.settimeout(UDP_TIMEOUT)
+
+ def jesd204_test_connector(self):
+ print("Testing JESD204 connectors. Molex cable #79576-2102 must be connected")
+ ctrl_sock.poke(N230_JESD204_TEST,0x1)
+ while True:
+ jesd204_test_status = ctrl_sock.peek(N230_JESD204_TEST)
+ if (jesd204_test_status & 0x10000 == 0x10000):
+ break
+ ctrl_sock.poke(N230_JESD204_TEST,0x0)
+ if (jesd204_test_status & 0xFFFF != 0x0):
+ print("JESD204 loopback test Failed!: Returned status is %4x" % (jesd204_test_status & 0xFFFF))
+ else:
+ print("JESD204 loopback test Passed.")
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--discover", action="store_true",help="Find all devices connected N230 devices", default=False)
+ parser.add_option("--addr", type="string", help="N230 device address", default='')
+ parser.add_option("--peek", type="int", help="Read from memory map", default=None)
+ parser.add_option("--poke", type="int", help="Write to memory map", default=None)
+ parser.add_option("--data", type="int", help="Data for poke", default=None)
+ parser.add_option("--fw", type="string", help="Path to FW image to load", default=None)
+ parser.add_option("--hash", action="store_true",help="Display FPGA git hash", default=False)
+ parser.add_option("--time", action="store_true",help="Display CHDR timestamp Stats", default=False)
+ parser.add_option("--reset", action="store_true",help="Reset and Reload USRP FPGA.", default=False)
+ parser.add_option("--jesd204test", action="store_true",help="Test mini-SAS connectors with loopback cable..", default=False)
+
+ (options, args) = parser.parse_args()
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+
+ if options.discover:
+ if N230_DEVICE_DISCOVERY_AVAILABLE:
+ disc_sock = discovery_socket()
+ for addr in disc_sock.discover():
+ print '==== FOUND ' + addr + ' ===='
+ ctrl_sock = ctrl_socket(addr)
+ ctrl_sock.read_hash()
+ ctrl_sock.is_claimed()
+ sys.exit()
+ else:
+ raise Exception('Discovery is only supported on Linux.')
+
+ if not options.addr:
+ raise Exception('No address specified')
+
+ ctrl_sock = ctrl_socket(addr=options.addr)
+
+ if options.fw is not None:
+ file_path = options.fw
+ extension = os.path.splitext(file_path)[1]
+ if (extension.lower() == '.coe'):
+ ctrl_sock.live_load_firmware_coe(file_path)
+ elif (extension.lower() == '.bin'):
+ ctrl_sock.live_load_firmware_bin(file_path)
+ else:
+ raise Exception("Unsupported firmware file format")
+
+ if options.hash:
+ ctrl_sock.read_hash()
+
+ if options.peek is not None:
+ addr = options.peek
+ data = ctrl_sock.peek(addr)
+ print("PEEK[0x%x (%d)] => 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.poke is not None and options.data is not None:
+ addr = options.poke
+ data = options.data
+ ctrl_sock.poke(addr,data)
+ print("POKE[0x%x (%d)] <= 0x%x (%d)" % (addr,addr,data,data))
+
+ if options.time:
+ ctrl_sock.read_time_stats()
+
+ if options.reset:
+ ctrl_sock.reset_fpga()
+
+ if options.jesd204test:
+ ctrl_sock.jesd204_test_connector()
diff --git a/firmware/usrp3/n230/n230_eeprom.c b/firmware/usrp3/n230/n230_eeprom.c
new file mode 100644
index 000000000..116f87985
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eeprom.c
@@ -0,0 +1,195 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_eeprom.h"
+
+#include <trace.h>
+#include <stddef.h>
+#include <flash/spi_flash.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <string.h> //memcpy
+#include "n230_fw_defs.h"
+#include "n230_fw_host_iface.h"
+
+static const wb_spi_slave_t flash_spi_slave = {
+ .base = (void*) 0xB000,
+ .slave_sel = 0x0001,
+ .clk_div = 4, //80MHz/4 = 20MHz
+ .mosi_edge = RISING,
+ .miso_edge = FALLING,
+ .lsb_first = false
+};
+
+static const spi_flash_dev_t spi_flash_device = {
+ .page_size = 256,
+ .sector_size = 65536,
+ .num_sectors = 254,
+ .bus = &flash_spi_slave
+};
+
+/***********************************************************************
+ * Non-volatile device data
+ **********************************************************************/
+#define N230_FLASH_NV_DATA_OFFSET 0x800000
+
+//Default values in case the EEPROM is not read, corrupt
+const n230_eeprom_map_t default_eeprom = {
+ .data_version_major = N230_EEPROM_VER_MAJOR,
+ .data_version_minor = N230_EEPROM_VER_MINOR,
+ .hw_revision = 0,
+ .hw_product = 0x01,
+ .gateway = N230_DEFAULT_GATEWAY,
+ .eth_info = {
+ { //eth0
+ .mac_addr = N230_DEFAULT_ETH0_MAC,
+ .subnet = N230_DEFAULT_ETH0_MASK,
+ .ip_addr = N230_DEFAULT_ETH0_IP
+ },
+ { //eth1
+ .mac_addr = N230_DEFAULT_ETH1_MAC,
+ .subnet = N230_DEFAULT_ETH1_MASK,
+ .ip_addr = N230_DEFAULT_ETH1_IP
+ }
+ }
+};
+
+//EEPROM cache
+static spi_flash_session_t flash_session = {.device = NULL};
+static n230_eeprom_map_t eeprom_cache;
+static bool cache_dirty = true;
+
+bool read_n230_eeprom()
+{
+ bool status = false;
+ if (flash_session.device == NULL) { //Initialize flash session structure for the first time
+ wb_spi_init(spi_flash_device.bus);
+ spif_init(&flash_session, &spi_flash_device, spif_spsn_s25flxx_operations());
+ }
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+
+ //Verify data format
+ status = (eeprom_cache.data_version_major == default_eeprom.data_version_major);
+ //Sanity communication info
+ if (eeprom_cache.eth_info[0].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].ip_addr = default_eeprom.eth_info[0].ip_addr;
+ if (eeprom_cache.eth_info[1].ip_addr == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].ip_addr = default_eeprom.eth_info[1].ip_addr;
+ if (eeprom_cache.eth_info[0].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[0].subnet = default_eeprom.eth_info[0].subnet;
+ if (eeprom_cache.eth_info[1].subnet == 0xFFFFFFFF)
+ eeprom_cache.eth_info[1].subnet = default_eeprom.eth_info[1].subnet;
+
+ if (!status) {
+ UHD_FW_TRACE(WARN, "read_n230_eeprom: Initialized cache to the default map.");
+ memcpy(&eeprom_cache, &default_eeprom, sizeof(n230_eeprom_map_t));
+ }
+ cache_dirty = !status;
+ return status;
+}
+
+bool write_n230_eeprom()
+{
+ //Assumption: sizeof(n230_eeprom_map_t) <= flash_page_size
+ //This function would need to be reimplemented if this assumption is no longer true
+ if (sizeof(n230_eeprom_map_t) > flash_session.device->page_size) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: sizeof(n230_eeprom_map_t) > flash_page_size");
+ return false;
+ }
+
+ bool status = true;
+ if (cache_dirty) {
+ n230_eeprom_map_t device_eeprom;
+ spif_read_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET, &device_eeprom, sizeof(n230_eeprom_map_t));
+ if (memcmp(&eeprom_cache, &device_eeprom, sizeof(n230_eeprom_map_t)) != 0) {
+ //Cache does not match read state. Write.
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: Writing data to flash...");
+ status = spif_erase_sector_sync(&flash_session, N230_FLASH_NV_DATA_OFFSET);
+ if (status) {
+ status = spif_write_page_sync(
+ &flash_session, N230_FLASH_NV_DATA_OFFSET, &eeprom_cache, sizeof(n230_eeprom_map_t));
+ }
+ if (!status) {
+ UHD_FW_TRACE(ERROR, "write_n230_eeprom: Operation failed!");
+ }
+ cache_dirty = !status;
+ } else {
+ UHD_FW_TRACE(DEBUG, "write_n230_eeprom: No new data. Write skipped.");
+ //Cache matches read state. So mark as clean
+ cache_dirty = false;
+ }
+ }
+ return status;
+}
+
+bool is_n230_eeprom_cache_dirty()
+{
+ return cache_dirty;
+}
+
+n230_eeprom_map_t* get_n230_eeprom_map()
+{
+ cache_dirty = true;
+ return &eeprom_cache;
+}
+
+const n230_eeprom_map_t* get_n230_const_eeprom_map()
+{
+ return &eeprom_cache;
+}
+
+const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface) {
+ if (iface >= N230_NUM_ETH_PORTS) {
+ UHD_FW_TRACE_FSTR(ERROR,
+ "get_n230_ethernet_info called with iface=%d when there are only %d ports!!!",
+ iface, N230_NUM_ETH_PORTS);
+ }
+ return &(get_n230_const_eeprom_map()->eth_info[iface]);
+}
+
+
+/***********************************************************************
+ * Storage for bootstrap FPGA Image
+ **********************************************************************/
+#define N230_FLASH_FPGA_IMAGE_OFFSET 0x000000
+#define N230_FLASH_FPGA_IMAGE_SIZE 0x400000
+#define N230_FLASH_NUM_FPGA_IMAGES 2
+
+void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "read_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ }
+ spif_read_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "write_n230_fpga_image_page: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_write_page_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset, buf, num_bytes);
+}
+
+bool erase_n230_fpga_image_sector(uint32_t offset)
+{
+ if (offset >= (N230_FLASH_NUM_FPGA_IMAGES * N230_FLASH_FPGA_IMAGE_SIZE)) {
+ UHD_FW_TRACE_FSTR(ERROR, "erase_n230_fpga_image_sector: Offset 0x%x out of bounds", offset);
+ return false;
+ }
+ return spif_erase_sector_sync(&flash_session, N230_FLASH_FPGA_IMAGE_OFFSET + offset);
+}
diff --git a/firmware/usrp3/n230/n230_eeprom.h b/firmware/usrp3/n230/n230_eeprom.h
new file mode 100644
index 000000000..71c7d53fd
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eeprom.h
@@ -0,0 +1,122 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_EEPROM_H
+#define INCLUDED_N230_EEPROM_H
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define N230_NUM_ETH_PORTS 1
+#define N230_MAX_NUM_ETH_PORTS 2
+
+#if (N230_NUM_ETH_PORTS > N230_MAX_NUM_ETH_PORTS)
+#error
+#endif
+
+#define N230_EEPROM_VER_MAJOR 1
+#define N230_EEPROM_VER_MINOR 0
+#define N230_EEPROM_SERIAL_LEN 9
+#define N230_EEPROM_NAME_LEN 32
+
+typedef struct
+{
+ uint8_t mac_addr[6];
+ uint8_t _pad[2];
+ uint32_t subnet;
+ uint32_t ip_addr;
+} n230_eth_eeprom_map_t;
+
+typedef struct
+{
+ //Data format version
+ uint16_t data_version_major;
+ uint16_t data_version_minor;
+
+ //HW identification info
+ uint16_t hw_revision;
+ uint16_t hw_product;
+ uint8_t serial[N230_EEPROM_SERIAL_LEN];
+ uint8_t _pad0[20 - N230_EEPROM_SERIAL_LEN];
+
+ //Ethernet specific
+ uint32_t gateway;
+ n230_eth_eeprom_map_t eth_info[N230_MAX_NUM_ETH_PORTS];
+
+ //User specific
+ uint8_t user_name[N230_EEPROM_NAME_LEN];
+} n230_eeprom_map_t;
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+// The following definitions are only useful in firmware. Exclude in host code.
+#ifndef __cplusplus
+
+/*!
+ * Read the eeprom and update caches.
+ * Returns true if read was successful.
+ * If the read was not successful then the cache is initialized with
+ * default values and marked as dirty.
+ */
+bool read_n230_eeprom();
+
+/*!
+ * Write the contents of the cache to the eeprom.
+ * Returns true if write was successful.
+ */
+bool write_n230_eeprom();
+
+/*!
+ * Returns the dirty state of the cache.
+ */
+bool is_n230_eeprom_cache_dirty();
+
+/*!
+ * Returns a const pointer to the EEPROM map.
+ */
+const n230_eeprom_map_t* get_n230_const_eeprom_map();
+
+/*!
+ * Returns the settings for the the 'iface'th ethernet interface
+ */
+const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface);
+
+/*!
+ * Returns a non-const pointer to the EEPROM map. Will mark the cache as dirty.
+ */
+n230_eeprom_map_t* get_n230_eeprom_map();
+
+/*!
+ * FPGA Image operations
+ */
+inline void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes);
+
+inline bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes);
+
+inline bool erase_n230_fpga_image_sector(uint32_t offset);
+
+#endif //ifdef __cplusplus
+
+#endif /* INCLUDED_N230_EEPROM_H */
diff --git a/firmware/usrp3/n230/n230_eth_handlers.c b/firmware/usrp3/n230/n230_eth_handlers.c
new file mode 100644
index 000000000..b69c35eb0
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.c
@@ -0,0 +1,235 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_eth_handlers.h"
+
+#include <fw_comm_protocol.h>
+#include <wb_utils.h>
+#include <string.h> //memcmp
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "../n230/n230_fw_host_iface.h"
+#include "n230_eeprom.h"
+#include "n230_fw_defs.h"
+
+static n230_host_shared_mem_t* host_shared_mem_ptr;
+
+/***********************************************************************
+ * Handler for host <-> firmware communication
+ **********************************************************************/
+
+static inline void n230_poke32(const uint32_t addr, const uint32_t data)
+{
+ if (addr >= N230_FW_HOST_SHMEM_RW_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)] = data;
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ wb_poke32(addr, data);
+ }
+}
+
+static inline uint32_t n230_peek32(const uint32_t addr)
+{
+ if (addr >= N230_FW_HOST_SHMEM_BASE_ADDR && addr <= N230_FW_HOST_SHMEM_MAX_ADDR) {
+ return host_shared_mem_ptr->buff[(addr - N230_FW_HOST_SHMEM_BASE_ADDR)/sizeof(uint32_t)];
+ } else if (addr < N230_FW_HOST_SHMEM_BASE_ADDR) {
+ return wb_peek32(addr);
+ } else {
+ return 0;
+ }
+}
+
+void n230_handle_udp_fw_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(fw_comm_pkt_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_udp_fw_comms got an unknown request (bad size).");
+ } else {
+ const fw_comm_pkt_t *request = (const fw_comm_pkt_t *)buff;
+ fw_comm_pkt_t response;
+ bool send_response = process_fw_comm_protocol_pkt(
+ request, &response,
+ N230_FW_PRODUCT_ID,
+ n230_poke32, n230_peek32);
+
+ if (send_response) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr)
+{
+ host_shared_mem_ptr = shared_mem_ptr;
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_UDP_PORT, &n230_handle_udp_fw_comms);
+}
+
+
+/***********************************************************************
+ * Handler for UDP framer program packets
+ **********************************************************************/
+void program_udp_framer(
+ const uint8_t ethno,
+ const uint32_t sid,
+ const struct ip_addr *dst_ip,
+ const uint16_t dst_port,
+ const uint16_t src_port)
+{
+ const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip);
+ const size_t vdest = (sid >> 16) & 0xff;
+
+ uint32_t framer_base =
+ ((ethno == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_FRAMER_BASE;
+
+ //setup source framer
+ const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_HI),
+ (((uint32_t)src_mac->addr[0]) << 8) | (((uint32_t)src_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_MAC_LO),
+ (((uint32_t)src_mac->addr[2]) << 24) | (((uint32_t)src_mac->addr[3]) << 16) |
+ (((uint32_t)src_mac->addr[4]) << 8) | (((uint32_t)src_mac->addr[5]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_IP_ADDR), u3_net_stack_get_ip_addr(ethno)->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_SRC_UDP_PORT), src_port);
+
+ //setup destination framer
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_RAM_ADDR), vdest);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_IP_ADDR), dst_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_UDP_MAC),
+ (((uint32_t)dst_port) << 16) |
+ (((uint32_t)dst_mac->addr[0]) << 8) | (((uint32_t)dst_mac->addr[1]) << 0));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, framer_base + ETH_FRAMER_DST_MAC_LO),
+ (((uint32_t)dst_mac->addr[2]) << 24) | (((uint32_t)dst_mac->addr[3]) << 16) |
+ (((uint32_t)dst_mac->addr[4]) << 8) | (((uint32_t)dst_mac->addr[5]) << 0));
+}
+
+void handle_udp_prog_framer(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else {
+ const uint32_t sid = ((const uint32_t *)buff)[1];
+ program_udp_framer(ethno, sid, src, src_port, dst_port);
+ UHD_FW_TRACE_FSTR(INFO, "Reprogrammed eth%d framer. Src=%s:%d, Dest=%s:%d",
+ ethno,ip_addr_to_str(src),src_port,ip_addr_to_str(dst),dst_port);
+ }
+}
+
+void n230_register_udp_prog_framer()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_CVITA_PORT, &handle_udp_prog_framer);
+}
+
+
+/***********************************************************************
+ * Handler for flash programming interface over UDP
+ **********************************************************************/
+
+void n230_handle_flash_prog_comms(
+ const uint8_t ethno,
+ const struct ip_addr *src, const struct ip_addr *dst,
+ const uint16_t src_port, const uint16_t dst_port,
+ const void *buff, const size_t num_bytes)
+{
+ if (buff == NULL) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an ICMP_DUR");
+ /* We got here from ICMP_DUR undeliverable packet */
+ /* Future space for hooks to tear down streaming radios etc */
+ } else if (num_bytes != sizeof(n230_flash_prog_t)) {
+ UHD_FW_TRACE(WARN, "n230_handle_flash_prog_comms got an unknown request (bad size).");
+ } else {
+ const n230_flash_prog_t *request = (const n230_flash_prog_t *)buff;
+ n230_flash_prog_t response;
+ bool ack_requested = request->flags & N230_FLASH_COMM_FLAGS_ACK;
+
+ //Request is valid. Copy it into the reply.
+ memcpy(&response, request, sizeof(n230_flash_prog_t));
+
+ switch (request->flags & N230_FLASH_COMM_FLAGS_CMD_MASK) {
+ case N230_FLASH_COMM_CMD_READ_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::read_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ if (is_n230_eeprom_cache_dirty()) {
+ read_n230_eeprom();
+ }
+ //EEPROM cache is up-to-date. Copy it into the packet.
+ //Assumption: Cache size < 256. If this is no longer true, the offset field
+ //will have to be used.
+ memcpy(response.data, get_n230_const_eeprom_map(), sizeof(n230_eeprom_map_t));
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_NV_DATA: {
+ UHD_FW_TRACE(DEBUG, "n230_handle_flash_prog_comms::write_nv_data()");
+ //Offset ignored because all non-volatile data fits in a packet.
+ memcpy(get_n230_eeprom_map(), request->data, sizeof(n230_eeprom_map_t));
+ if (!write_n230_eeprom()) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_READ_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::read_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ read_n230_fpga_image_page(request->offset, response.data, request->size);
+ ack_requested = true;
+ } break;
+
+ case N230_FLASH_COMM_CMD_WRITE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::write_fpga_page(offset=0x%x, size=%d)",
+ request->offset, request->size);
+ if (!write_n230_fpga_image_page(request->offset, request->data, request->size)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ case N230_FLASH_COMM_CMD_ERASE_FPGA: {
+ UHD_FW_TRACE_FSTR(DEBUG, "n230_handle_flash_prog_comms::erase_fpga_sector(offset=0x%x)",
+ request->offset);
+ if (!erase_n230_fpga_image_sector(request->offset)) {
+ response.flags |= N230_FLASH_COMM_ERR_CMD_ERROR;
+ }
+ } break;
+
+ default :{
+ UHD_FW_TRACE(ERROR, "n230_handle_flash_prog_comms got an invalid command.");
+ response.flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+ //Send a reply if ack requested
+ if (ack_requested) {
+ u3_net_stack_send_udp_pkt(ethno, src, dst_port, src_port, &response, sizeof(response));
+ }
+ }
+}
+
+void n230_register_flash_comms_handler()
+{
+ u3_net_stack_register_udp_handler(N230_FW_COMMS_FLASH_PROG_PORT, &n230_handle_flash_prog_comms);
+}
+
diff --git a/firmware/usrp3/n230/n230_eth_handlers.h b/firmware/usrp3/n230/n230_eth_handlers.h
new file mode 100644
index 000000000..271c3a6de
--- /dev/null
+++ b/firmware/usrp3/n230/n230_eth_handlers.h
@@ -0,0 +1,42 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_ETH_HANDLERS_H
+#define INCLUDED_N230_ETH_HANDLERS_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <lwip/ip_addr.h>
+#include "../n230/n230_fw_host_iface.h"
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_udp_fw_comms_handler(n230_host_shared_mem_t* shared_mem_ptr);
+
+/*!
+ * Registrar for framer programmer handler.
+ */
+void n230_register_udp_prog_framer();
+
+/*!
+ * Registrar for host firmware communications handler.
+ */
+void n230_register_flash_comms_handler();
+
+#endif /* INCLUDED_N230_ETH_HANDLERS_H */
diff --git a/firmware/usrp3/n230/n230_fw_defs.h b/firmware/usrp3/n230/n230_fw_defs.h
new file mode 100644
index 000000000..f30c6efe0
--- /dev/null
+++ b/firmware/usrp3/n230/n230_fw_defs.h
@@ -0,0 +1,133 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FW_DEFS_H
+#define INCLUDED_N230_FW_DEFS_H
+
+#include <stdint.h>
+
+/*!
+ * Constants specific to N230 firmware.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ * However, if it is included from within the host code,
+ * it will be namespaced appropriately
+ */
+#ifdef __cplusplus
+namespace uhd {
+namespace usrp {
+namespace n230 {
+namespace fw {
+#endif
+
+static inline uint32_t reg_addr(uint32_t base, uint32_t offset) {
+ return ((base) + (offset)*4);
+}
+
+/*******************************************************************
+ * Global
+ *******************************************************************/
+static const uint32_t CPU_CLOCK_FREQ = 80000000;
+static const uint32_t PER_MILLISEC_CRON_JOBID = 0;
+static const uint32_t PER_SECOND_CRON_JOBID = 1;
+
+/*******************************************************************
+ * Wishbone slave addresses
+ *******************************************************************/
+static const uint32_t WB_MAIN_RAM_BASE = 0x0000;
+static const uint32_t WB_PKT_RAM_BASE = 0x8000;
+static const uint32_t WB_SBRB_BASE = 0xa000;
+static const uint32_t WB_SPI_FLASH_BASE = 0xb000;
+static const uint32_t WB_ETH0_MAC_BASE = 0xc000;
+static const uint32_t WB_ETH1_MAC_BASE = 0xd000;
+static const uint32_t WB_ETH0_I2C_BASE = 0xf600;
+static const uint32_t WB_ETH1_I2C_BASE = 0xf700;
+static const uint32_t WB_DBG_UART_BASE = 0xf900;
+
+/*******************************************************************
+ * Seting Register Base addresses
+ *******************************************************************/
+static const uint32_t SR_ZPU_SW_RST = 0;
+static const uint32_t SR_ZPU_BOOT_DONE = 1;
+static const uint32_t SR_ZPU_LEDS = 2;
+static const uint32_t SR_ZPU_SFP_CTRL0 = 16;
+static const uint32_t SR_ZPU_SFP_CTRL1 = 17;
+static const uint32_t SR_ZPU_ETHINT0 = 64;
+static const uint32_t SR_ZPU_ETHINT1 = 80;
+
+static const uint32_t SR_ZPU_SW_RST_NONE = 0x0;
+static const uint32_t SR_ZPU_SW_RST_PHY = 0x1;
+static const uint32_t SR_ZPU_SW_RST_RADIO = 0x2;
+
+/*******************************************************************
+ * Readback addresses
+ *******************************************************************/
+static const uint32_t RB_ZPU_COMPAT = 0;
+static const uint32_t RB_ZPU_COUNTER = 1;
+static const uint32_t RB_ZPU_SFP_STATUS0 = 2;
+static const uint32_t RB_ZPU_SFP_STATUS1 = 3;
+
+/*******************************************************************
+ * Ethernet
+ *******************************************************************/
+static const uint32_t WB_PKT_RAM_CTRL_OFFSET = 0x1FFC;
+
+static const uint32_t SR_ZPU_ETHINT_FRAMER_BASE = 0;
+static const uint32_t SR_ZPU_ETHINT_DISPATCHER_BASE = 8;
+
+//Eth framer constants
+static const uint32_t ETH_FRAMER_SRC_MAC_HI = 0;
+static const uint32_t ETH_FRAMER_SRC_MAC_LO = 1;
+static const uint32_t ETH_FRAMER_SRC_IP_ADDR = 2;
+static const uint32_t ETH_FRAMER_SRC_UDP_PORT = 3;
+static const uint32_t ETH_FRAMER_DST_RAM_ADDR = 4;
+static const uint32_t ETH_FRAMER_DST_IP_ADDR = 5;
+static const uint32_t ETH_FRAMER_DST_UDP_MAC = 6;
+static const uint32_t ETH_FRAMER_DST_MAC_LO = 7;
+
+/*******************************************************************
+ * CODEC
+ *******************************************************************/
+static const uint32_t CODEC_SPI_CLOCK_FREQ = 4000000; //4MHz
+static const uint32_t ADF4001_SPI_CLOCK_FREQ = 200000; //200kHz
+
+/*******************************************************************
+ * UART
+ *******************************************************************/
+static const uint32_t DBG_UART_BAUD = 115200;
+
+/*******************************************************************
+ * Build Compatability Numbers
+ *******************************************************************/
+static const uint8_t PRODUCT_NUM = 0x01;
+static const uint8_t COMPAT_MAJOR = 0x00;
+static const uint16_t COMPAT_MINOR = 0x0000;
+
+static inline uint8_t get_prod_num(uint32_t compat_reg) {
+ return (compat_reg >> 24) & 0xFF;
+}
+static inline uint8_t get_compat_major(uint32_t compat_reg) {
+ return (compat_reg >> 16) & 0xFF;
+}
+static inline uint8_t get_compat_minor(uint32_t compat_reg) {
+ return compat_reg & 0xFFFF;
+}
+
+#ifdef __cplusplus
+}}}} //namespace
+#endif
+#endif /* INCLUDED_N230_FW_DEFS_H */
diff --git a/firmware/usrp3/n230/n230_fw_host_iface.h b/firmware/usrp3/n230/n230_fw_host_iface.h
new file mode 100644
index 000000000..2d510bf1f
--- /dev/null
+++ b/firmware/usrp3/n230/n230_fw_host_iface.h
@@ -0,0 +1,124 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FW_HOST_IFACE_H
+#define INCLUDED_N230_FW_HOST_IFACE_H
+
+#include <stdint.h>
+
+/*!
+ * Structs and constants for N230 communication between firmware and host.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------
+// Ethernet related
+//
+#define N230_DEFAULT_ETH0_MAC {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff}
+#define N230_DEFAULT_ETH1_MAC {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33}
+#define N230_DEFAULT_ETH0_IP (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0)
+#define N230_DEFAULT_ETH1_IP (192 << 24 | 168 << 16 | 20 << 8 | 2 << 0)
+#define N230_DEFAULT_ETH0_MASK (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)
+#define N230_DEFAULT_ETH1_MASK (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)
+#define N230_DEFAULT_GATEWAY (192 << 24 | 168 << 16 | 10 << 8 | 1 << 0)
+
+#define N230_FW_COMMS_UDP_PORT 49152
+#define N230_FW_COMMS_CVITA_PORT 49153
+#define N230_FW_COMMS_FLASH_PROG_PORT 49154
+//
+//--------------------------------------------------
+
+//--------------------------------------------------
+// Memory shared with host
+//
+#define N230_FW_HOST_SHMEM_BASE_ADDR 0x10000
+#define N230_FW_HOST_SHMEM_RW_BASE_ADDR 0x1000C
+#define N230_FW_HOST_SHMEM_NUM_WORDS (sizeof(n230_host_shared_mem_data_t)/sizeof(uint32_t))
+
+#define N230_FW_HOST_SHMEM_MAX_ADDR \
+ (N230_FW_HOST_SHMEM_BASE_ADDR + ((N230_FW_HOST_SHMEM_NUM_WORDS - 1) * sizeof(uint32_t)))
+
+#define N230_FW_HOST_SHMEM_OFFSET(member) \
+ (N230_FW_HOST_SHMEM_BASE_ADDR + ((uint32_t)offsetof(n230_host_shared_mem_data_t, member)))
+
+//The shared memory block can only be accessed on 32-bit boundaries
+typedef struct { //All fields must be 32-bit wide to avoid packing directives
+ //Read-Only fields (N230_FW_HOST_SHMEM_BASE_ADDR)
+ uint32_t fw_compat_num; //Compat number must be at offset 0
+ uint32_t fw_version_hash;
+ uint32_t claim_status;
+
+ //Read-Write fields (N230_FW_HOST_SHMEM_RW_BASE_ADDR)
+ uint32_t scratch;
+ uint32_t claim_time;
+ uint32_t claim_src;
+} n230_host_shared_mem_data_t;
+
+typedef union
+{
+ uint32_t buff[N230_FW_HOST_SHMEM_NUM_WORDS];
+ n230_host_shared_mem_data_t data;
+} n230_host_shared_mem_t;
+
+#define N230_FW_PRODUCT_ID 1
+#define N230_FW_COMPAT_NUM_MAJOR 3
+#define N230_FW_COMPAT_NUM_MINOR 0
+#define N230_FW_COMPAT_NUM (((N230_FW_COMPAT_NUM_MAJOR & 0xFF) << 16) | (N230_FW_COMPAT_NUM_MINOR & 0xFFFF))
+//
+//--------------------------------------------------
+
+//--------------------------------------------------
+// Flash read-write interface for host
+//
+#define N230_FLASH_COMM_FLAGS_ACK 0x00000001
+#define N230_FLASH_COMM_FLAGS_CMD_MASK 0x00000FF0
+#define N230_FLASH_COMM_FLAGS_ERROR_MASK 0xFF000000
+
+#define N230_FLASH_COMM_CMD_READ_NV_DATA 0x00000010
+#define N230_FLASH_COMM_CMD_WRITE_NV_DATA 0x00000020
+#define N230_FLASH_COMM_CMD_READ_FPGA 0x00000030
+#define N230_FLASH_COMM_CMD_WRITE_FPGA 0x00000040
+#define N230_FLASH_COMM_CMD_ERASE_FPGA 0x00000050
+
+#define N230_FLASH_COMM_ERR_PKT_ERROR 0x80000000
+#define N230_FLASH_COMM_ERR_CMD_ERROR 0x40000000
+#define N230_FLASH_COMM_ERR_SIZE_ERROR 0x20000000
+
+#define N230_FLASH_COMM_MAX_PAYLOAD_SIZE 128
+
+typedef struct
+{
+ uint32_t flags;
+ uint32_t seq;
+ uint32_t offset;
+ uint32_t size;
+ uint8_t data[N230_FLASH_COMM_MAX_PAYLOAD_SIZE];
+} n230_flash_prog_t;
+//
+//--------------------------------------------------
+
+#define N230_CLAIMER_TIMEOUT_IN_MS 2000
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_N230_FW_HOST_IFACE_H */
diff --git a/firmware/usrp3/n230/n230_init.c b/firmware/usrp3/n230/n230_init.c
new file mode 100644
index 000000000..4602f8338
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.c
@@ -0,0 +1,125 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <printf.h>
+#include <wb_utils.h>
+#include <wb_uart.h>
+#include <wb_i2c.h>
+#include <wb_pkt_iface64.h>
+#include <u3_net_stack.h>
+#include <print_addrs.h>
+#include <trace.h>
+#include "n230_eeprom.h"
+#include "n230_fw_defs.h"
+#include "n230_init.h"
+
+static wb_pkt_iface64_config_t pkt_config;
+
+static void putc(void *p, char c)
+{
+//If FW_TRACE_LEVEL is defined, then the trace level is set
+//to a non-zero number. Turn on the debug UART to enable tracing
+#ifdef UHD_FW_TRACE_LEVEL
+ wb_uart_putc(WB_DBG_UART_BASE, c);
+#endif
+}
+
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(WB_SBRB_BASE, RB_ZPU_COUNTER));
+}
+
+void n230_init(void)
+{
+ //TODO: We may need to remove the debug UART before we release.
+ //Initialize the debug UART first.
+ wb_uart_init(WB_DBG_UART_BASE, CPU_CLOCK_FREQ/DBG_UART_BAUD);
+ init_printf(NULL, putc);
+
+ //Now we can init the rest with prints
+ UHD_FW_TRACE_FSTR(INFO, "[ZPU Init Begin -- CPU CLOCK is %d MHz]", (CPU_CLOCK_FREQ/1000000));
+
+ //Initialize cron and the per millisecond cron job
+ UHD_FW_TRACE(INFO, "Initializing cron...");
+ cron_init(get_counter_val, CPU_CLOCK_FREQ);
+ cron_job_init(PER_MILLISEC_CRON_JOBID, 1);
+ cron_job_init(PER_SECOND_CRON_JOBID, 1000);
+
+ //Initialize rate for I2C cores
+ UHD_FW_TRACE(INFO, "Initializing I2C...");
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ wb_i2c_init((i==1)?WB_ETH1_I2C_BASE:WB_ETH0_I2C_BASE, CPU_CLOCK_FREQ);
+ }
+
+ //Initialize eeprom
+ read_n230_eeprom();
+
+ UHD_FW_TRACE(INFO, "Initializing network stack...");
+ init_network_stack();
+}
+
+void init_network_stack(void)
+{
+ //Hold Ethernet PHYs in reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_PHY);
+
+ //Initialize ethernet packet interface
+ pkt_config = wb_pkt_iface64_init(WB_PKT_RAM_BASE, WB_PKT_RAM_CTRL_OFFSET);
+ u3_net_stack_init(&pkt_config);
+
+ //Initialize MACs
+ for (uint32_t i = 0; i < N230_NUM_ETH_PORTS; i++) {
+ init_ethernet_mac(i);
+ }
+
+ //Pull Ethernet PHYs out of reset
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, SR_ZPU_SW_RST), SR_ZPU_SW_RST_NONE);
+}
+
+void init_ethernet_mac(uint32_t iface_num)
+{
+ UHD_FW_TRACE_FSTR(INFO, "Initializing eth%d...", iface_num);
+
+ //Get interface info from the EEPROM (or defaults otherwise)
+ const n230_eth_eeprom_map_t* eth_eeprom_map = get_n230_ethernet_info(iface_num);
+ const eth_mac_addr_t *my_mac = (const eth_mac_addr_t *) &(eth_eeprom_map->mac_addr);
+ const struct ip_addr *my_ip = (const struct ip_addr *) &(eth_eeprom_map->ip_addr);
+ const struct ip_addr *subnet = (const struct ip_addr *) &(eth_eeprom_map->subnet);
+
+ //Init software fields related to ethernet
+ u3_net_stack_init_eth(iface_num, my_mac, my_ip, subnet);
+
+ uint32_t dispatcher_base =
+ ((iface_num == 1) ? SR_ZPU_ETHINT1 : SR_ZPU_ETHINT0) + SR_ZPU_ETHINT_DISPATCHER_BASE;
+
+ //Program dispatcher
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 0),
+ (my_mac->addr[5] << 0) | (my_mac->addr[4] << 8) | (my_mac->addr[3] << 16) | (my_mac->addr[2] << 24));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 1), (my_mac->addr[1] << 0) | (my_mac->addr[0] << 8));
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 2), my_ip->addr);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 4), 0/*nofwd*/);
+ wb_poke32(SR_ADDR(WB_SBRB_BASE, dispatcher_base + 5), (ICMP_IRQ << 8) | 0); //no fwd: type, code
+
+ //DEBUG: Print initialized info
+ UHD_FW_TRACE_FSTR(INFO, "-- MAC%u: %s", iface_num, mac_addr_to_str(u3_net_stack_get_mac_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- IP%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_ip_addr(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- SUBNET%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_subnet(iface_num)));
+ UHD_FW_TRACE_FSTR(INFO, "-- BCAST%u: %s", iface_num, ip_addr_to_str(u3_net_stack_get_bcast(iface_num)));
+}
+
+
diff --git a/firmware/usrp3/n230/n230_init.h b/firmware/usrp3/n230/n230_init.h
new file mode 100644
index 000000000..e2231909e
--- /dev/null
+++ b/firmware/usrp3/n230/n230_init.h
@@ -0,0 +1,28 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_INIT_H
+#define INCLUDED_N230_INIT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void n230_init(void);
+void init_network_stack(void);
+void init_ethernet_mac(uint32_t iface_num);
+
+#endif /* INCLUDED_B250_INIT_H */
diff --git a/firmware/usrp3/n230/n230_main.c b/firmware/usrp3/n230/n230_main.c
new file mode 100644
index 000000000..0dc44095e
--- /dev/null
+++ b/firmware/usrp3/n230/n230_main.c
@@ -0,0 +1,114 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <cron.h>
+#include <wb_soft_reg.h>
+#include <u3_net_stack.h>
+#include <trace.h>
+#include "n230_eth_handlers.h"
+#include "n230_fw_defs.h"
+#include "n230_fw_host_iface.h"
+#include "n230_init.h"
+
+//The version hash should come from a cmake build variable
+//If it doesn't then the build system does not support the feature
+//so just default to 0xFFFFFFFF
+#ifndef UHD_VERSION_HASH
+#define UHD_VERSION_HASH 0xFFFFFFFF
+#endif
+
+//TODO: This is just for initial debugging.
+static soft_reg_t g_led_register;
+static const soft_reg_field_t LED_REG_BLINKY_FIELD = {.num_bits=4, .shift=0};
+
+//Shared memory
+static n230_host_shared_mem_t g_host_shared_mem;
+
+//Functions
+static void n230_handle_claim();
+
+/***********************************************************************
+ * Main loop runs all the handlers
+ **********************************************************************/
+int main(void)
+{
+ //Initialize host shared mem
+ g_host_shared_mem.data.fw_compat_num = N230_FW_COMPAT_NUM;
+ g_host_shared_mem.data.fw_version_hash = UHD_VERSION_HASH;
+
+ //Main initialization function
+ n230_init();
+
+ //Initialize UDP Handlers
+ n230_register_udp_fw_comms_handler(&g_host_shared_mem);
+ n230_register_udp_prog_framer();
+ n230_register_flash_comms_handler();
+
+ initialize_writeonly_soft_reg(&g_led_register, SR_ADDR(WB_SBRB_BASE, SR_ZPU_LEDS));
+
+ uint32_t heart_beat = 0;
+ while(true)
+ {
+ //TODO: This is just for initial debugging. Once the firmware
+ //is somewhat stable we should delete this cron job
+ if (cron_job_run_due(PER_SECOND_CRON_JOBID)) {
+ //Everything in this block runs approx once per second
+ soft_reg_write(&g_led_register, LED_REG_BLINKY_FIELD, heart_beat);
+ if (heart_beat % 10 == 0) {
+ UHD_FW_TRACE_FSTR(INFO, "0.1Hz Heartbeat (%u)", heart_beat);
+ }
+ heart_beat++;
+ }
+
+ if (cron_job_run_due(PER_MILLISEC_CRON_JOBID)) {
+ //Everything in this block runs approx once per millisecond
+ n230_handle_claim();
+ }
+
+ //run the network stack - poll and handle
+ u3_net_stack_handle_one();
+ }
+ return 0;
+}
+
+// Watchdog timer for claimer
+static void n230_handle_claim()
+{
+ static uint32_t last_time = 0;
+ static size_t timeout = 0;
+
+ if (g_host_shared_mem.data.claim_time == 0) {
+ //If time is 0 if the claim was forfeit
+ g_host_shared_mem.data.claim_status = 0;
+ } else if (last_time != g_host_shared_mem.data.claim_time) {
+ //If the time changes, reset timeout
+ g_host_shared_mem.data.claim_status = 1;
+ timeout = 0;
+ } else {
+ //Otherwise increment for timeout
+ timeout++;
+ }
+
+ //Always stash the last seen time
+ last_time = g_host_shared_mem.data.claim_time;
+
+ //Timeout logic
+ if (timeout > N230_CLAIMER_TIMEOUT_IN_MS) {
+ g_host_shared_mem.data.claim_time = 0;
+ }
+}
+
diff --git a/firmware/usrp3/utils/git-hash.sh b/firmware/usrp3/utils/git-hash.sh
new file mode 100755
index 000000000..ff7ae5ecb
--- /dev/null
+++ b/firmware/usrp3/utils/git-hash.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+if [[ $(command -v git) = "" ]]; then
+ short_hash="FFFFFFFF"
+else
+ if (git diff --quiet); then
+ #Clean
+ short_hash="0$(git rev-parse --verify HEAD --short)"
+ else
+ #Dirty
+ short_hash="F$(git rev-parse --verify HEAD --short)"
+ fi
+fi
+echo ${short_hash^^} \ No newline at end of file
diff --git a/firmware/usrp3/x300/x300_debug.py b/firmware/usrp3/x300/x300_debug.py
index c9bcbb138..c518ba4e0 100755
--- a/firmware/usrp3/x300/x300_debug.py
+++ b/firmware/usrp3/x300/x300_debug.py
@@ -25,12 +25,18 @@ import struct
########################################################################
# constants
########################################################################
-B250_FW_COMMS_UDP_PORT = 49152
+X300_FW_COMMS_UDP_PORT = 49152
-B250_FW_COMMS_FLAGS_ACK = 1
-B250_FW_COMMS_FLAGS_ERROR = 2
-B250_FW_COMMS_FLAGS_POKE32 = 4
-B250_FW_COMMS_FLAGS_PEEK32 = 8
+X300_FW_COMMS_FLAGS_ACK = 0x00000001
+X300_FW_COMMS_FLAGS_POKE32 = 0x00000010
+X300_FW_COMMS_FLAGS_PEEK32 = 0x00000020
+
+X300_FW_COMMS_ERR_PKT_ERROR = 0x80000000
+X300_FW_COMMS_ERR_CMD_ERROR = 0x40000000
+X300_FW_COMMS_ERR_SIZE_ERROR = 0x20000000
+
+X300_FW_COMMS_ID = 0x0000ACE3
+X300_FW_COMMS_MAX_DATA_WORDS = 16
#UDP_CTRL_PORT = 49183
UDP_MAX_XFER_BYTES = 1024
@@ -39,7 +45,7 @@ UDP_TIMEOUT = 3
#REG_ARGS_FMT = '!LLLLLB15x'
#REG_IP_FMT = '!LLLL20x'
-REG_PEEK_POKE_FMT = '!LLLL'
+REG_PEEK_POKE_FMT = '!LLLLL'
_seq = -1
@@ -52,12 +58,33 @@ def seq():
########################################################################
# helper functions
########################################################################
-
-def unpack_reg_peek_poke_fmt(s):
- return struct.unpack(REG_PEEK_POKE_FMT,s) #(flags, seq, addr, data)
+def fw_check_error(flags):
+ if flags & X300_FW_COMMS_ERR_PKT_ERROR == X300_FW_COMMS_ERR_PKT_ERROR:
+ raise Exception("The fiwmware operation returned a packet error")
+ if flags & X300_FW_COMMS_ERR_CMD_ERROR == X300_FW_COMMS_ERR_CMD_ERROR:
+ raise Exception("The fiwmware operation returned a command error")
+ if flags & X300_FW_COMMS_ERR_SIZE_ERROR == X300_FW_COMMS_ERR_SIZE_ERROR:
+ raise Exception("The fiwmware operation returned a size error")
def pack_reg_peek_poke_fmt(flags, seq, addr, data):
- return struct.pack(REG_PEEK_POKE_FMT, flags, seq, addr, data);
+ num_words = 1
+ data_arr = [data]
+ buf = bytes()
+ buf = struct.pack('!LLLLL', X300_FW_COMMS_ID, flags, seq, num_words, addr)
+ for i in range(X300_FW_COMMS_MAX_DATA_WORDS):
+ if (i < num_words):
+ buf += struct.pack('!L', data_arr[i])
+ else:
+ buf += struct.pack('!L', 0)
+ return buf
+
+def unpack_reg_peek_poke_fmt(buf):
+ (id, flags, seq, num_words, addr) = struct.unpack_from('!LLLLL', buf)
+ fw_check_error(flags)
+ data = []
+ for i in xrange(20, len(buf), 4):
+ data.append(struct.unpack('!L', buf[i:i+4])[0])
+ return (flags, seq, addr, data[0])
########################################################################
# Burner class, holds a socket and send/recv routines
@@ -66,7 +93,7 @@ class ctrl_socket(object):
def __init__(self, addr):
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.settimeout(UDP_TIMEOUT)
- self._sock.connect((addr, B250_FW_COMMS_UDP_PORT))
+ self._sock.connect((addr, X300_FW_COMMS_UDP_PORT))
self.set_callbacks(lambda *a: None, lambda *a: None)
#self.init_update() #check that the device is there
@@ -92,11 +119,10 @@ class ctrl_socket(object):
for in_prt in range (0, 8):
print("%s |" % ports[in_prt]),
for out_prt in range (0, 8):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), 0xA000+256+((in_prt*8+out_prt)*4), 0)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek returns error code")
+ fw_check_error(flags)
print("%10d " % (data)),
print
print
@@ -105,19 +131,17 @@ class ctrl_socket(object):
def peek(self,peek_addr):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_PEEK32|B250_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_PEEK32|X300_FW_COMMS_FLAGS_ACK, seq(), peek_addr, 0)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
+ fw_check_error(flags)
print("PEEK of address %d(0x%x) reads %d(0x%x)" % (addr,addr,data,data))
def poke(self,poke_addr,poke_data):
- out_pkt = pack_reg_peek_poke_fmt(B250_FW_COMMS_FLAGS_POKE32|B250_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
+ out_pkt = pack_reg_peek_poke_fmt(X300_FW_COMMS_FLAGS_POKE32|X300_FW_COMMS_FLAGS_ACK, seq(), poke_addr, poke_data)
in_pkt = self.send_and_recv(out_pkt)
(flags, rxseq, addr, data) = unpack_reg_peek_poke_fmt(in_pkt)
- if flags & B250_FW_COMMS_FLAGS_ERROR == B250_FW_COMMS_FLAGS_ERROR:
- raise Exception("B250 peek of address %d returns error code" % (addr))
+ fw_check_error(flags)
print("POKE of address %d(0x%x) with %d(0x%x)" % (poke_addr,poke_addr,poke_data,poke_data) )
@@ -126,12 +150,12 @@ class ctrl_socket(object):
########################################################################
def get_options():
parser = optparse.OptionParser()
- parser.add_option("--addr", type="string", help="USRP-N2XX device address", default='')
- parser.add_option("--list", action="store_true", help="list possible network devices", default=False)
+ parser.add_option("--addr", type="string", help="USRP-X300 device address", default='')
+ parser.add_option("--list", action="store_true", help="list possible network devices", default=False)
parser.add_option("--peek", type="int", help="Read from memory map", default=None)
parser.add_option("--poke", type="int", help="Write to memory map", default=None)
parser.add_option("--data", type="int", help="Data for poke", default=None)
- parser.add_option("--stats", action="store_true", help="Display SuperMIMO Network Stats", default=False)
+ parser.add_option("--stats", action="store_true", help="Display SuperMIMO Network Stats", default=False)
(options, args) = parser.parse_args()
return options
diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c
index ef97412a2..97b20032b 100644
--- a/firmware/usrp3/x300/x300_init.c
+++ b/firmware/usrp3/x300/x300_init.c
@@ -1,7 +1,7 @@
#include "x300_init.h"
#include "x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <wb_utils.h>
#include <wb_uart.h>
#include <wb_i2c.h>
@@ -121,6 +121,11 @@ static void putc(void *p, char c)
#endif
}
+static uint32_t get_counter_val()
+{
+ return wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
+}
+
void x300_init(void)
{
//first - uart
@@ -136,6 +141,9 @@ void x300_init(void)
UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF));
UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000));
+ //Initialize cron
+ cron_init(get_counter_val, CPU_CLOCK);
+
//i2c rate init
wb_i2c_init(I2C0_BASE, CPU_CLOCK);
wb_i2c_init(I2C1_BASE, CPU_CLOCK);
@@ -163,7 +171,7 @@ void x300_init(void)
}
// For eth interfaces, initialize the PHY's
- mdelay(100);
+ sleep_ms(100);
ethernet_init(0);
ethernet_init(1);
}
diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c
index 3b812a2c4..44ed10aa8 100644
--- a/firmware/usrp3/x300/x300_main.c
+++ b/firmware/usrp3/x300/x300_main.c
@@ -6,7 +6,6 @@
#include "xge_phy.h"
#include "ethernet.h"
#include "chinch.h"
-#include "mdelay.h"
#include <wb_utils.h>
#include <wb_uart.h>