//
// Copyright 2013 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 .
//
#include "x300_impl.hpp"
#include
#include "x300_regs.hpp"
#include
#include
#include
#include
#include
using namespace uhd;
struct x300_uart_iface : uart_iface
{
x300_uart_iface(wb_iface::sptr iface):
_iface(iface),
rxoffset(0),
txword32(0),
_last_device_rxoffset(0)
{
txoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX));
rxpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_ADDR));
txpool = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_ADDR));
poolsize = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_WORDS32));
_rxcache.resize(poolsize);
//this->write_uart("HELLO UART\n");
//this->read_uart(0.1);
}
void putchar(const char ch)
{
const int shift = ((txoffset%4) * 8);
if (shift == 0) txword32 = 0;
txword32 |= uint32_t(ch) << shift;
// Write out full 32 bit words or whatever we have if end of string
if (txoffset % 4 == 3 or ch == '\n')
{
_iface->poke32(SR_ADDR(txpool, txoffset/4), txword32);
}
txoffset = (txoffset + 1) % (poolsize*4);
if (ch == '\n')
{
// Tell the X300 to write the string
_iface->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_TX_INDEX), txoffset);
}
}
void write_uart(const std::string &buff)
{
boost::mutex::scoped_lock(_write_mutex);
for(const char ch: buff)
{
this->putchar(ch);
}
}
int getchar(void)
{
if (rxoffset == _last_device_rxoffset)
return -1;
int ret = static_cast(_rxcache[((rxoffset)/4) % poolsize] >> ((rxoffset%4)*8) & 0xFF);
rxoffset++;
return ret;
}
void update_cache(void)
{
uint32_t device_rxoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX));
uint32_t delta = device_rxoffset - rxoffset;
while (delta)
{
if (delta >= poolsize*4)
{
// all the data is new - reload the entire cache
for (uint32_t i = 0; i < poolsize; i++)
{
_rxcache[i] = _iface->peek32(SR_ADDR(rxpool, i));
}
// set the head to the same character as the current device
// offset (tail) one loop earlier
rxoffset = device_rxoffset - (poolsize*4);
// set the tail to the current device offset
_last_device_rxoffset = device_rxoffset;
// the string at the head is a partial, so skip it
for (int c = getchar(); c != '\n' and c != -1; c = getchar()) {}
// clear the partial string in the buffer, if any
_rxbuff.clear();
}
else if (rxoffset == _last_device_rxoffset)
{
// new data was added - refresh the portion of the cache that was updated
for (uint32_t i = (_last_device_rxoffset/4) % poolsize;
i != ((device_rxoffset/4)+1) % poolsize;
i = (i+1) % poolsize)
{
_rxcache[i] = _iface->peek32(SR_ADDR(rxpool, i));
}
// set the tail to the current device offset
_last_device_rxoffset = device_rxoffset;
}
else
{
// there is new data, but we aren't done with what we have - check back later
break;
}
// check again to see if anything changed while we were updating the cache
device_rxoffset = _iface->peek32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_UART_RX_INDEX));
delta = device_rxoffset - rxoffset;
}
}
std::string read_uart(double timeout)
{
boost::mutex::scoped_lock(_read_mutex);
const boost::system_time exit_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1e6));
std::string buff;
while (true)
{
// Update cache
this->update_cache();
// Get available characters
for (int ch = this->getchar(); ch != -1; ch = this->getchar())
{
// store character to buffer
_rxbuff.append(1, ch);
// newline found - return string
if (ch == '\n')
{
buff.swap(_rxbuff);
return buff;
}
}
// no more characters - check time
if (boost::get_system_time() > exit_time)
break;
}
return buff;
}
wb_iface::sptr _iface;
uint32_t rxoffset, txoffset, txword32, rxpool, txpool, poolsize;
uint32_t _last_device_rxoffset;
std::vector _rxcache;
std::string _rxbuff;
boost::mutex _read_mutex;
boost::mutex _write_mutex;
};
uart_iface::sptr x300_make_uart_iface(wb_iface::sptr iface)
{
return uart_iface::sptr(new x300_uart_iface(iface));
}