aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/usrp3/lib
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/usrp3/lib')
-rw-r--r--firmware/usrp3/lib/CMakeLists.txt6
-rw-r--r--firmware/usrp3/lib/cron.c92
-rw-r--r--firmware/usrp3/lib/ethernet.c100
-rw-r--r--firmware/usrp3/lib/flash/spi_flash.c50
-rw-r--r--firmware/usrp3/lib/flash/spif_spsn_s25flxx.c238
-rw-r--r--firmware/usrp3/lib/fw_comm_protocol.c105
-rw-r--r--firmware/usrp3/lib/mdelay.c36
-rw-r--r--firmware/usrp3/lib/wb_spi.c206
8 files changed, 752 insertions, 81 deletions
diff --git a/firmware/usrp3/lib/CMakeLists.txt b/firmware/usrp3/lib/CMakeLists.txt
index 621b9b611..9d9ee3c6c 100644
--- a/firmware/usrp3/lib/CMakeLists.txt
+++ b/firmware/usrp3/lib/CMakeLists.txt
@@ -21,12 +21,16 @@ add_library(usrp3fw STATIC
udp_uart.c
wb_uart.c
wb_i2c.c
+ wb_spi.c
printf.c
wb_pkt_iface64.c
u3_net_stack.c
ethernet.c
- mdelay.c
chinch.c
print_addrs.c
link_state_route_proto.c
+ cron.c
+ fw_comm_protocol.c
+ flash/spi_flash.c
+ flash/spif_spsn_s25flxx.c
)
diff --git a/firmware/usrp3/lib/cron.c b/firmware/usrp3/lib/cron.c
new file mode 100644
index 000000000..24b8feb4e
--- /dev/null
+++ b/firmware/usrp3/lib/cron.c
@@ -0,0 +1,92 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "cron.h"
+
+//Counter specific
+static cron_counter_fetcher_t cron_fetch_counter;
+static uint32_t cron_counter_freq;
+
+//Cron job specific
+typedef struct {
+ uint32_t tick_interval;
+ uint32_t last_tick_count;
+} cron_job_t;
+
+static cron_job_t cron_job_table[CRON_MAX_JOBS];
+
+void cron_init(const cron_counter_fetcher_t fetch_counter, uint32_t counter_freq)
+{
+ cron_fetch_counter = fetch_counter;
+ cron_counter_freq = counter_freq;
+
+ for (int i = 0; i < CRON_MAX_JOBS; i++) {
+ cron_job_table[i].tick_interval = 0;
+ }
+}
+
+uint32_t cron_get_ticks()
+{
+ return cron_fetch_counter();
+}
+
+uint32_t get_elapsed_time(uint32_t start_ticks, uint32_t stop_ticks, cron_time_unit_t unit)
+{
+ return ((stop_ticks - start_ticks) / cron_counter_freq) * ((uint32_t)unit);
+}
+
+void sleep_ticks(uint32_t ticks)
+{
+ if (ticks == 0) return; //Handle the 0 delay case quickly
+
+ const uint32_t ticks_begin = cron_fetch_counter();
+ while(cron_fetch_counter() - ticks_begin < ticks) {
+ /*NOP: Spinloop*/
+ }
+}
+
+void sleep_us(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000000)));
+}
+
+void sleep_ms(uint32_t duration)
+{
+ sleep_ticks((duration * (cron_counter_freq/1000)));
+}
+
+void cron_job_init(uint32_t job_id, uint32_t interval_ms)
+{
+ cron_job_table[job_id].tick_interval = (interval_ms * (cron_counter_freq/1000));
+ cron_job_table[job_id].last_tick_count = 0;
+}
+
+bool cron_job_run_due(uint32_t job_id)
+{
+ uint32_t new_tick_count = cron_fetch_counter();
+ bool run_job = (new_tick_count - cron_job_table[job_id].last_tick_count) >=
+ cron_job_table[job_id].tick_interval;
+
+ if (run_job) {
+ //If the job is due to run, update the tick count for the next run
+ //The assumption here is that the caller will actually run their job
+ //when the return value is true. If not, the caller just missed this
+ //iteration and will have to option to run the job in the next pass through.
+ cron_job_table[job_id].last_tick_count = new_tick_count;
+ }
+ return run_job;
+}
diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c
index 91efbfe1d..e5226489f 100644
--- a/firmware/usrp3/lib/ethernet.c
+++ b/firmware/usrp3/lib/ethernet.c
@@ -21,7 +21,7 @@
#endif
#include "../x300/x300_defs.h"
#include "ethernet.h"
-#include "mdelay.h"
+#include "cron.h"
#include <trace.h>
#include "wb_i2c.h"
#include "wb_utils.h"
@@ -161,8 +161,8 @@ ge_write_mdio(const uint32_t base, const uint32_t address, const uint32_t port,
static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port)
{
- const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
- const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE;
+ const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;
if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
{
return xge_read_mdio(base, address, device, port);
@@ -175,8 +175,8 @@ static uint32_t read_mdio(const uint8_t eth, const uint32_t address, const uint3
static void write_mdio(const uint8_t eth, const uint32_t address, const uint32_t device, const uint32_t port, const uint32_t data)
{
- const uint32_t rb_addr = (eth==0) ? RB_ETH_TYPE0 : RB_ETH_TYPE1;
- const uint32_t base = (eth==0) ? XGE0_BASE : XGE1_BASE;
+ const uint32_t rb_addr = (eth==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE;
+ const uint32_t base = (eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE;
if (wb_peek32(SR_ADDR(RB0_BASE, rb_addr)) != 0)
{
return xge_write_mdio(base, address, device, port, data);
@@ -220,7 +220,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms)
int x;
// Delay read of SFPP
if (delay_ms)
- mdelay(delay_ms);
+ sleep_ms(delay_ms);
// Read ID code from SFP
x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3);
// I2C Error?
@@ -312,10 +312,9 @@ static void xge_mac_init(const uint32_t base)
}
// base is pointer to XGE MAC on Wishbone.
-static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
+static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port)
{
int x;
- uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg;
// Read LASI Ctrl register to capture state.
//y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A);
UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence.");
@@ -323,23 +322,24 @@ static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg)
x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port);
x = x | (1 << 15);
write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x);
+ uint32_t loopCount = 0;
while(x&(1<<15)) {
x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port);
+ if( loopCount++ > 200 ) break; // usually succeeds after 22 or 23 polls
}
}
-void update_eth_state(const uint32_t eth)
+void update_eth_state(const uint32_t eth, const uint32_t sfp_type)
{
const bool old_link_up = links_up[eth];
- const uint32_t status_reg_addr = (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1;
- const bool is_10g = (wb_peek32(SR_ADDR(RB0_BASE, eth == 0 ? RB_ETH_TYPE0 : RB_ETH_TYPE1)) == 1);
+ const uint32_t status_reg_addr = (eth==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS;
uint32_t sfpp_status = wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) & 0xFFFF;
if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) {
//SFP+ pin state changed. Reinitialize PHY and MAC
- if (is_10g) {
- xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE);
- xge_phy_init(eth ,MDIO_PORT);
+ if (sfp_type == RB_SFP_10G_ETH) {
+ xge_mac_init((eth==0) ? SFP0_MAC_BASE : SFP1_MAC_BASE);
+ xge_phy_init(eth, MDIO_PORT);
} else {
//No-op for 1G
}
@@ -347,7 +347,7 @@ void update_eth_state(const uint32_t eth)
int8_t timeout = 100;
bool link_up = false;
do {
- if (is_10g) {
+ if (sfp_type == RB_SFP_10G_ETH) {
link_up = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0;
} else {
link_up = ((wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) >> 16) & 0x1) != 0;
@@ -362,54 +362,66 @@ void update_eth_state(const uint32_t eth)
}
if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth));
- UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down");
}
-void poll_sfpp_status(const uint32_t eth)
+void poll_sfpp_status(const uint32_t sfp)
{
- uint32_t x;
- // Has MODDET/MODAbS changed since we last looked?
- x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
+ uint32_t type = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE));
+ uint32_t status = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS));
- if (x & SFPP_STATUS_RXLOS_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (x & SFPP_STATUS_RXLOS));
- if (x & SFPP_STATUS_TXFAULT_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((x & SFPP_STATUS_TXFAULT) >> 1));
- if (x & SFPP_STATUS_MODABS_CHG)
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((x & SFPP_STATUS_MODABS) >> 2));
-
- //update the link up status
- if ((x & SFPP_STATUS_RXLOS_CHG) || (x & SFPP_STATUS_TXFAULT_CHG) || (x & SFPP_STATUS_MODABS_CHG))
- {
- update_eth_state(eth);
- }
-
- if (x & SFPP_STATUS_MODABS_CHG) {
+ if (status & SFPP_STATUS_MODABS_CHG) {
// MODDET has changed state since last checked
- if (x & SFPP_STATUS_MODABS) {
+ if (status & SFPP_STATUS_MODABS) {
// MODDET is high, module currently removed.
- UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth);
+ UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", sfp);
} else {
// MODDET is low, module currently inserted.
// Return status.
- UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth);
- xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1);
+ UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", sfp);
+ if (type == RB_SFP_10G_ETH) {
+ xge_read_sfpp_type((sfp==0) ? I2C0_BASE : I2C2_BASE,1);
+ }
}
}
+
+ if (status & SFPP_STATUS_RXLOS_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d RXLOS changed state: %d", sfp, (status & SFPP_STATUS_RXLOS));
+ }
+ if (status & SFPP_STATUS_TXFAULT_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d TXFAULT changed state: %d", sfp, ((status & SFPP_STATUS_TXFAULT) >> 1));
+ }
+ if (status & SFPP_STATUS_MODABS_CHG) {
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d MODABS changed state: %d", sfp, ((status & SFPP_STATUS_MODABS) >> 2));
+ }
+
+ //update the link up status
+ const bool old_link_up = links_up[sfp];
+ if (type == RB_SFP_AURORA) {
+ links_up[sfp] = ((wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS)) >> 16) & 0x1) != 0;
+ } else {
+ if ((status & SFPP_STATUS_RXLOS_CHG) ||
+ (status & SFPP_STATUS_TXFAULT_CHG) ||
+ (status & SFPP_STATUS_MODABS_CHG))
+ {
+ update_eth_state(sfp, type);
+ }
+ }
+ if (old_link_up != links_up[sfp]) {
+ UHD_FW_TRACE_FSTR(INFO, "The link on SFP port %u is %s", sfp, links_up[sfp]?"up":"down");
+ }
}
-void ethernet_init(const uint32_t eth)
+void ethernet_init(const uint32_t sfp)
{
#ifdef UHD_FW_TRACE_LEVEL
- uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 ));
- UHD_FW_TRACE_FSTR(DEBUG, "eth%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d",
- eth,
+ uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_STATUS : RB_SFP1_STATUS ));
+ UHD_FW_TRACE_FSTR(DEBUG, "SFP%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d",
+ sfp,
(x & SFPP_STATUS_RXLOS),
((x & SFPP_STATUS_TXFAULT) >> 1),
((x & SFPP_STATUS_MODABS) >> 2));
#endif
- links_up[eth] = false;
- update_eth_state(eth);
+ update_eth_state(sfp, wb_peek32(SR_ADDR(RB0_BASE, (sfp==0) ? RB_SFP0_TYPE : RB_SFP1_TYPE)));
}
//
diff --git a/firmware/usrp3/lib/flash/spi_flash.c b/firmware/usrp3/lib/flash/spi_flash.c
new file mode 100644
index 000000000..b4257c96f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spi_flash.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <flash/spi_flash.h>
+
+void spif_init(spi_flash_session_t* flash, const spi_flash_dev_t* device, const spi_flash_ops_t* ops)
+{
+ flash->device = device;
+ flash->ops = ops;
+ flash->state = IDLE;
+ flash->last_offset = 0;
+ flash->id = ops->read_id(device);
+}
+
+void spif_read_sync(const spi_flash_session_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ flash->ops->read(flash->device, offset, buf, num_bytes);
+}
+
+bool spif_erase_sector_sync(const spi_flash_session_t* flash, uint32_t offset)
+{
+ if (flash->ops->erase_sector_dispatch(flash->device, offset)) {
+ return flash->ops->erase_sector_commit(flash->device, offset);
+ } else {
+ return false;
+ }
+}
+
+bool spif_write_page_sync(const spi_flash_session_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (flash->ops->write_page_dispatch(flash->device, offset, buf, num_bytes)) {
+ return flash->ops->write_page_commit(flash->device, offset, buf, num_bytes);
+ } else {
+ return false;
+ }
+}
diff --git a/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
new file mode 100644
index 000000000..244115b6f
--- /dev/null
+++ b/firmware/usrp3/lib/flash/spif_spsn_s25flxx.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <flash/spif_spsn_s25flxx.h>
+#include <cron.h>
+#include <trace.h>
+#include <string.h> //for memset, memcpy
+
+#define S25FLXX_CMD_WIDTH 8
+#define S25FLXX_ADDR_WIDTH 24
+
+/* S25FLxx-specific commands */
+#define S25FLXX_CMD_READID 0x90 /* Read Manufacturer and Device Identification */
+#define S25FLXX_CMD_READSIG 0xAB /* Read Electronic Signature (Will release from Deep PD) */
+#define S25FLXX_CMD_READ 0x03 /* Read Data Bytes */
+#define S25FLXX_CMD_FAST_READ 0x0B /* Read Data Bytes at Higher Speed */
+
+#define S25FLXX_CMD_WREN 0x06 /* Write Enable */
+#define S25FLXX_CMD_WRDI 0x04 /* Write Disable */
+
+#define S25FLXX_CMD_PP 0x02 /* Page Program */
+#define S25FLXX_CMD_SE 0xD8 /* Sector Erase */
+#define S25FLXX_CMD_BE 0xC7 /* Bulk Erase */
+#define S25FLXX_CMD_DP 0xB9 /* Deep Power-down */
+
+#define S25FLXX_CMD_RDSR 0x05 /* Read Status Register */
+#define S25FLXX_CMD_WRSR 0x01 /* Write Status Register */
+
+#define S25FLXX_STATUS_WIP 0x01 /* Write in Progress */
+#define S25FLXX_STATUS_E_ERR 0x20 /* Erase Error Occured */
+#define S25FLXX_STATUS_P_ERR 0x40 /* Programming Error Occured */
+
+#define S25FLXX_SECTOR_ERASE_TIME_MS 750 //Spec: 650ms
+#define S25FLXX_PAGE_WRITE_TIME_MS 1 //Spec: 750us
+
+#define S25FLXX_SMALL_SECTORS_PER_LOGICAL 16 //16 4-kB physical sectors per logical sector
+#define S25FLXX_LARGE_SECTOR_BASE 0x20000 //Large physical sectors start at logical sector 2
+
+inline static uint8_t _spif_read_status(const spi_flash_dev_t* flash)
+{
+ uint16_t cmd = S25FLXX_CMD_RDSR << 8, status = 0xFFFF;
+ wb_spi_transact(flash->bus, WRITE_READ, &cmd, &status, S25FLXX_CMD_WIDTH + 8 /* 8 bits of status */);
+ return status;
+}
+
+inline static bool _spif_wait_ready(const spi_flash_dev_t* flash, uint32_t timeout_ms)
+{
+ uint32_t start_ticks = cron_get_ticks();
+ do {
+ if ((_spif_read_status(flash) & S25FLXX_STATUS_WIP) == 0) {
+ return true;
+ }
+ } while (get_elapsed_time(start_ticks, cron_get_ticks(), MILLISEC) < timeout_ms);
+
+ return false; // Timed out
+}
+
+inline static void _spi_flash_set_write_enabled(const spi_flash_dev_t* flash, bool enabled)
+{
+ uint8_t cmd = enabled ? S25FLXX_CMD_WREN : S25FLXX_CMD_WRDI;
+ wb_spi_transact(flash->bus, WRITE, &cmd, NULL, S25FLXX_CMD_WIDTH);
+}
+
+const spi_flash_ops_t spif_spsn_s25flxx_ops =
+{
+ .read_id = spif_spsn_s25flxx_read_id,
+ .read = spif_spsn_s25flxx_read,
+ .erase_sector_dispatch = spif_spsn_s25flxx_erase_sector_dispatch,
+ .erase_sector_commit = spif_spsn_s25flxx_erase_sector_commit,
+ .erase_sector_busy = spif_spsn_s25flxx_device_busy,
+ .write_page_dispatch = spif_spsn_s25flxx_write_page_dispatch,
+ .write_page_commit = spif_spsn_s25flxx_write_page_commit,
+ .write_page_busy = spif_spsn_s25flxx_device_busy
+};
+
+const spi_flash_ops_t* spif_spsn_s25flxx_operations()
+{
+ return &spif_spsn_s25flxx_ops;
+}
+
+uint16_t spif_spsn_s25flxx_read_id(const spi_flash_dev_t* flash)
+{
+ wb_spi_slave_select(flash->bus);
+ uint32_t command = S25FLXX_CMD_READID << 24;
+ wb_spi_transact_man_ss(flash->bus, WRITE, &command, NULL, 32);
+ uint16_t id = 0;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, &id, 16);
+ wb_spi_slave_deselect(flash->bus);
+ return id;
+}
+
+void spif_spsn_s25flxx_read(const spi_flash_dev_t* flash, uint32_t offset, void *buf, uint32_t num_bytes)
+{
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 5 byte instruction tranfer:
+ //FAST_READ_CMD, ADDR2, ADDR1, ADDR0, DUMMY (0)
+ uint8_t read_cmd[5];
+ read_cmd[4] = S25FLXX_CMD_FAST_READ;
+ *((uint32_t*)(read_cmd + 3)) = (offset << 8);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, read_cmd, NULL, 5*8);
+
+ //Read up to 4 bytes at a time until done
+ uint8_t data_sw[16], data[16];
+ size_t xact_size = 16;
+ unsigned char *bytes = (unsigned char *) buf;
+ for (size_t i = 0; i < num_bytes; i += 16) {
+ if (xact_size > num_bytes - i) xact_size = num_bytes - i;
+ wb_spi_transact_man_ss(flash->bus, WRITE_READ, NULL, data_sw, xact_size*8);
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)data)[k] = ((uint32_t*)data_sw)[3-k];
+ }
+ for (size_t j = 0; j < xact_size; j++) {
+ *bytes = data[j];
+ bytes++;
+ }
+ }
+ wb_spi_slave_deselect(flash->bus);
+}
+
+bool spif_spsn_s25flxx_erase_sector_dispatch(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Sanity check sector size
+ if (offset % flash->sector_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_erase_sector: Erase offset not a multiple of sector size.");
+ return false;
+ }
+
+ if (!_spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector: Timeout. Sector at 0x%X was not ready for erase.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //Send sector erase command
+ uint32_t command = (S25FLXX_CMD_SE << 24) | (offset & 0x00FFFFFF);
+ wb_spi_transact(flash->bus, WRITE_READ, &command, NULL, 32);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_erase_sector_commit(const spi_flash_dev_t* flash, uint32_t offset)
+{
+ //Poll status until write done
+ uint8_t phy_sector_count = (offset < S25FLXX_LARGE_SECTOR_BASE) ? S25FLXX_SMALL_SECTORS_PER_LOGICAL : 1;
+ bool status = false;
+ for (uint8_t i = 0; i < phy_sector_count && !status; i++) {
+ status = _spif_wait_ready(flash, S25FLXX_SECTOR_ERASE_TIME_MS);
+ }
+ if (!status) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_erase_sector_commit: Timeout. Sector at 0x%X did not finish erasing in time.", offset);
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return status;
+}
+
+bool spif_spsn_s25flxx_write_page_dispatch(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ if (num_bytes == 0 || num_bytes > flash->page_size) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Invalid size. Must be > 0 and <= Page Size.");
+ return false;
+ }
+ if (num_bytes > (flash->sector_size * flash->num_sectors)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_write_page: Cannot write past flash boundary.");
+ return false;
+ }
+
+ //Wait until ready and enable write enabled
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE_FSTR(ERROR, "spif_spsn_s25flxx_write_page: Timeout. Page at 0x%X was not ready for write.", offset);
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, true);
+
+ //We explicitly control the slave select here, so that we can
+ //do the entire read operation as a single transaction from
+ //device's point of view. (The most our SPI peripheral can transfer
+ //in a single shot is 16 bytes.)
+
+ //Do the 4 byte instruction tranfer:
+ //PP_CMD, ADDR2, ADDR1, ADDR0
+ uint32_t write_cmd = (S25FLXX_CMD_PP << 24) | (offset & 0x00FFFFFF);
+
+ wb_spi_slave_select(flash->bus);
+ wb_spi_transact_man_ss(flash->bus, WRITE, &write_cmd, NULL, 32);
+
+ //Write the page 16 bytes at a time.
+ uint8_t bytes_sw[16];
+ uint8_t* bytes = (uint8_t*) buf;
+ for (int32_t bytes_left = num_bytes; bytes_left > 0; bytes_left -= 16) {
+ const uint32_t xact_size = (bytes_left < 16) ? bytes_left : 16;
+ for (size_t k = 0; k < 4; k++) { //Fix word level significance
+ ((uint32_t*)bytes_sw)[k] = ((uint32_t*)bytes)[3-k];
+ }
+ wb_spi_transact_man_ss(flash->bus, WRITE, bytes_sw, NULL, xact_size * 8);
+ bytes += xact_size;
+ }
+ wb_spi_slave_deselect(flash->bus);
+
+ return true;
+}
+
+bool spif_spsn_s25flxx_write_page_commit(const spi_flash_dev_t* flash, uint32_t offset, const void *buf, uint32_t num_bytes)
+{
+ //Wait until write done
+ if (!_spif_wait_ready(flash, S25FLXX_PAGE_WRITE_TIME_MS)) {
+ UHD_FW_TRACE(ERROR, "spif_spsn_s25flxx_commit_write: Timeout. Page did not finish writing in time.");
+ return false;
+ }
+ _spi_flash_set_write_enabled(flash, false);
+ return true;
+}
+
+bool spif_spsn_s25flxx_device_busy(const spi_flash_dev_t* flash)
+{
+ return (_spif_read_status(flash) & S25FLXX_STATUS_WIP);
+}
+
diff --git a/firmware/usrp3/lib/fw_comm_protocol.c b/firmware/usrp3/lib/fw_comm_protocol.c
new file mode 100644
index 000000000..0cc931a76
--- /dev/null
+++ b/firmware/usrp3/lib/fw_comm_protocol.c
@@ -0,0 +1,105 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../../host/lib/usrp/common/fw_comm_protocol.h"
+
+#include <trace.h>
+#include <string.h> //memcmp
+
+bool process_fw_comm_protocol_pkt(
+ const fw_comm_pkt_t* request,
+ fw_comm_pkt_t* response,
+ uint8_t product_id,
+ uint32_t iface_id,
+ poke32_func poke_callback,
+ peek32_func peek_callback)
+{
+ bool send_response = false;
+
+ uint16_t signature = request->id;
+ uint8_t version = FW_COMM_GET_PROTOCOL_VER(request->id);
+ uint8_t product = FW_COMM_GET_PRODUCT_ID(request->id);
+ if (signature == FW_COMM_PROTOCOL_SIGNATURE && //Verify protocol
+ version <= FW_COMM_PROTOCOL_VERSION && //Verify protocol version (older versions supported)
+ product == product_id) //Verify device
+ {
+ //Request is valid. Copy it into the reply.
+ memcpy(response, request, sizeof(fw_comm_pkt_t));
+
+ //Start assuming no error
+ response->flags &= ~FW_COMM_FLAGS_ERROR_MASK;
+
+ //Otherwise, run the command set by the flags
+ switch (request->flags & FW_COMM_FLAGS_CMD_MASK) {
+ case FW_COMM_CMD_ECHO: {
+ UHD_FW_TRACE(DEBUG, "fw_comm_protocol::echo()");
+ response->data_words = 1;
+ response->data[0] = iface_id;
+ } break;
+
+ case FW_COMM_CMD_POKE32: {
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::poke32(0x%x)=0x%x",
+ request->addr,*(request->data));
+ poke_callback(request->addr, *(request->data));
+ } break;
+
+ case FW_COMM_CMD_PEEK32: {
+ *(response->data) = peek_callback(request->addr);
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::peek32(0x%x)=0x%x",
+ request->addr,*(response->data));
+ } break;
+
+ case FW_COMM_CMD_BLOCK_POKE32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_poke32(0x%x,%d)",request->addr,response->data_words);
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ poke_callback(request->addr + (i * sizeof(uint32_t)), request->data[i]);
+ }
+ } break;
+
+ case FW_COMM_CMD_BLOCK_PEEK32: {
+ if (request->data_words > FW_COMM_MAX_DATA_WORDS) {
+ response->flags |= FW_COMM_ERR_SIZE_ERROR;
+ response->data_words = FW_COMM_MAX_DATA_WORDS;
+ } else {
+ response->data_words = request->data_words;
+ }
+ for (uint32_t i = 0; i < response->data_words; i++) {
+ response->data[i] = peek_callback(request->addr + (i * sizeof(uint32_t)));
+ }
+ UHD_FW_TRACE_FSTR(DEBUG, "fw_comm_protocol::block_peek32(0x%x,%d)",request->addr,response->data_words);
+ } break;
+
+ default: {
+ UHD_FW_TRACE(ERROR, "fw_comm_protocol got an invalid command.");
+ response->flags |= FW_COMM_ERR_CMD_ERROR;
+ }
+ }
+
+ //Send a reply if ack requested
+ send_response = (request->flags & FW_COMM_FLAGS_ACK);
+ } else { //Size, protocol, product check failed
+ UHD_FW_TRACE(WARN, "fw_comm_protocol ignored an unknown request.");
+ send_response = false;
+ }
+ return send_response;
+}
diff --git a/firmware/usrp3/lib/mdelay.c b/firmware/usrp3/lib/mdelay.c
deleted file mode 100644
index 6d2742206..000000000
--- a/firmware/usrp3/lib/mdelay.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- c -*- */
-/*
- * Copyright 2007 Free Software Foundation, Inc.
- * Copyright 2009 Ettus Research LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mdelay.h"
-#include "wb_utils.h"
-#include "printf.h"
-#include <stdint.h>
-//IJB FIXME.
-#include "../x300/x300_defs.h"
-
-void mdelay(int ms){
- for(int i = 0; i < ms; i++){
- static const uint32_t num_ticks = CPU_CLOCK/1000;
- const uint32_t ticks_begin = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER));
- // printf("DEBUG: Counter is %d\n",ticks_begin);
- while((wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)) - ticks_begin) < num_ticks) {
- /*NOP*/
- }
- }
-}
diff --git a/firmware/usrp3/lib/wb_spi.c b/firmware/usrp3/lib/wb_spi.c
new file mode 100644
index 000000000..04904feea
--- /dev/null
+++ b/firmware/usrp3/lib/wb_spi.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2014 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wb_spi.h>
+#include <trace.h>
+
+typedef struct {
+ volatile uint32_t data0;
+ volatile uint32_t data1;
+ volatile uint32_t data2;
+ volatile uint32_t data3;
+ volatile uint32_t ctrl_status;
+ volatile uint32_t clkdiv;
+ volatile uint32_t slavesel;
+} wb_spi_regs_t;
+
+#define WB_SPI_REGS(base) ((wb_spi_regs_t *) base)
+
+// Masks for different parts of CTRL reg
+#define WB_SPI_CTRL_AUTO_SS (1 << 13)
+#define WB_SPI_CTRL_IE (1 << 12)
+#define WB_SPI_CTRL_LSB (1 << 11)
+#define WB_SPI_CTRL_TXNEG (1 << 10)
+#define WB_SPI_CTRL_RXNEG (1 << 9)
+#define WB_SPI_CTRL_GO_BSY (1 << 8)
+#define WB_SPI_CTRL_LENGTH(x) (x & 0x7F)
+
+static inline uint32_t _wb_spi_get_flags(const wb_spi_slave_t* slave)
+{
+ uint32_t flags = 0;
+ //If the SPI slave samples on the rising edge then shift
+ //data out on the falling edge.
+ if (slave->mosi_edge == RISING) flags |= WB_SPI_CTRL_TXNEG;
+ //If the SPI slave drives on the rising edge then shift
+ //data in on the falling edge.
+ if (slave->miso_edge == RISING) flags |= WB_SPI_CTRL_RXNEG;
+ if (slave->lsb_first) flags |= WB_SPI_CTRL_LSB;
+ return flags;
+}
+
+static inline void _wait_for_xfer(const wb_spi_slave_t* slave)
+{
+ while (WB_SPI_REGS(slave->base)->ctrl_status & WB_SPI_CTRL_GO_BSY) {
+ /*NOP*/
+ }
+}
+
+void wb_spi_init(const wb_spi_slave_t* slave)
+{
+ WB_SPI_REGS(slave->base)->clkdiv = slave->clk_div;
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+
+ //Do a dummy transaction with no slave selected to prime the engine
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(8) | _wb_spi_get_flags(slave);
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+ _wait_for_xfer(slave);
+}
+
+void _wb_spi_transact_buf(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length,
+ bool auto_slave_sel)
+{
+ if (length == 0) return;
+
+ //Wait for previous transaction to finish
+ _wait_for_xfer(slave);
+
+ //Write SPI data register(s)
+ if (mosi_buf) {
+ uint8_t* mosi_bytes = (uint8_t*) mosi_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ word = (uint32_t) mosi_bytes[0];
+ } else if (bits_left <= 16) {
+ word = (((uint32_t) mosi_bytes[1]) << 0) |
+ (((uint32_t) mosi_bytes[0]) << 8);
+ } else if (bits_left <= 24) {
+ word = (((uint32_t) mosi_bytes[2]) << 0) |
+ (((uint32_t) mosi_bytes[1]) << 8) |
+ (((uint32_t) mosi_bytes[0]) << 16);
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ }
+ bits_left = 0;
+ } else {
+ word = *((uint32_t*) mosi_bytes);
+ mosi_bytes += 4;
+ bits_left -= 32;
+ }
+
+ switch (reg_index) {
+ case 0: WB_SPI_REGS(slave->base)->data0 = word; break;
+ case 1: WB_SPI_REGS(slave->base)->data1 = word; break;
+ case 2: WB_SPI_REGS(slave->base)->data2 = word; break;
+ case 3: WB_SPI_REGS(slave->base)->data3 = word; break;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+
+ //Compute flags for slave and write control register
+ uint32_t ctrl = WB_SPI_CTRL_LENGTH(length) | _wb_spi_get_flags(slave);
+ if (auto_slave_sel) ctrl |= WB_SPI_CTRL_AUTO_SS;
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl;
+
+ // Tell it which SPI slave device to access
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+
+ //Go go go!
+ WB_SPI_REGS(slave->base)->ctrl_status = ctrl | WB_SPI_CTRL_GO_BSY;
+
+ if (rw_mode == WRITE_READ) {
+ //Wait for SPI read operation to complete
+ _wait_for_xfer(slave);
+
+ if (miso_buf) {
+ //Read SPI data registers
+ uint8_t* miso_bytes = (uint8_t*) miso_buf;
+ uint8_t bits_left = length;
+ for (uint32_t reg_index = 0; reg_index < 4; reg_index++) {
+ uint32_t word = 0;
+ switch (reg_index) {
+ case 0: word = WB_SPI_REGS(slave->base)->data0; break;
+ case 1: word = WB_SPI_REGS(slave->base)->data1; break;
+ case 2: word = WB_SPI_REGS(slave->base)->data2; break;
+ case 3: word = WB_SPI_REGS(slave->base)->data3; break;
+ }
+
+ if (bits_left < 32) {
+ if (bits_left <= 8) {
+ miso_bytes[0] = word & 0xFF;
+ } else if (bits_left <= 16) {
+ miso_bytes[1] = word & 0xFF;
+ miso_bytes[0] = (word >> 8) & 0xFF;
+ } else if (bits_left <= 24) {
+ miso_bytes[2] = word & 0xFF;
+ miso_bytes[1] = (word >> 8) & 0xFF;
+ miso_bytes[0] = (word >> 16) & 0xFF;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ }
+ bits_left = 0;
+ } else {
+ *((uint32_t*) miso_bytes) = word;
+ miso_bytes += 4;
+ bits_left -= 32;
+ }
+
+ if (bits_left == 0) break;
+ }
+ }
+ }
+}
+
+void wb_spi_transact(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, true);
+}
+
+void wb_spi_transact_man_ss(
+ const wb_spi_slave_t* slave, wb_spi_rw_mode_t rw_mode,
+ const void* mosi_buf, void* miso_buf, uint32_t length)
+{
+ return _wb_spi_transact_buf(slave, rw_mode, mosi_buf, miso_buf, length, false);
+}
+
+void wb_spi_slave_select(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually select slave
+ WB_SPI_REGS(slave->base)->slavesel = slave->slave_sel;
+}
+
+void wb_spi_slave_deselect(const wb_spi_slave_t* slave)
+{
+ //Wait for previous transactions to finish
+ _wait_for_xfer(slave);
+ //Disable auto slave select
+ WB_SPI_REGS(slave->base)->ctrl_status = _wb_spi_get_flags(slave);
+ //Manually deselect slave
+ WB_SPI_REGS(slave->base)->slavesel = 0;
+}