diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2015-12-23 10:59:07 -0800 |
---|---|---|
committer | Ashish Chaudhari <ashish@ettus.com> | 2016-01-05 17:35:14 -0800 |
commit | e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4 (patch) | |
tree | 12d441822342db2e9e11b5cbe7c0fd156f0cba13 /firmware/usrp3/lib | |
parent | f45b4eb386a4c4cf5e7a463936fb4591d1bf8854 (diff) | |
download | uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.gz uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.tar.bz2 uhd-e92913f81a8a44def64dfcb7c0001d9ca7d2d2c4.zip |
n230: Initial checkin of firmware files
Diffstat (limited to 'firmware/usrp3/lib')
-rw-r--r-- | firmware/usrp3/lib/CMakeLists.txt | 6 | ||||
-rw-r--r-- | firmware/usrp3/lib/cron.c | 92 | ||||
-rw-r--r-- | firmware/usrp3/lib/ethernet.c | 4 | ||||
-rw-r--r-- | firmware/usrp3/lib/flash/spi_flash.c | 50 | ||||
-rw-r--r-- | firmware/usrp3/lib/flash/spif_spsn_s25flxx.c | 238 | ||||
-rw-r--r-- | firmware/usrp3/lib/fw_comm_protocol.c | 102 | ||||
-rw-r--r-- | firmware/usrp3/lib/mdelay.c | 36 | ||||
-rw-r--r-- | firmware/usrp3/lib/wb_spi.c | 206 |
8 files changed, 695 insertions, 39 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..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; +} |