diff options
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; +} | 
