//
// Copyright 2013-2017 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 .
//
#ifdef E300_NATIVE
#include
#include
#include
// constants coded into the fpga parameters
static const size_t ZF_CONFIG_BASE = 0x40000000;
static const size_t ZF_PAGE_WIDTH = 10;
static const size_t H2S_STREAMS_WIDTH = 4;
static const size_t H2S_CMDFIFO_DEPTH = 5;
static const size_t S2H_STREAMS_WIDTH = 4;
static const size_t S2H_CMDFIFO_DEPTH = 5;
// calculate more useful constants for this module
static const size_t ZF_PAGE_SIZE(1 << ZF_PAGE_WIDTH);
static const size_t H2S_NUM_STREAMS(1 << H2S_STREAMS_WIDTH);
static const size_t H2S_NUM_CMDS(1 << H2S_CMDFIFO_DEPTH);
static const size_t S2H_NUM_STREAMS(1 << S2H_STREAMS_WIDTH);
static const size_t S2H_NUM_CMDS(1 << S2H_CMDFIFO_DEPTH);
//offsetsinto the arbiter memory map
static const size_t ARBITER_WR_CLEAR = 0;
static const size_t ARBITER_RD_SIG = 0;
static const size_t ARBITER_WR_ADDR = 4;
static const size_t ARBITER_WR_SIZE = 8;
static const size_t ARBITER_WR_STS_RDY = 12;
static const size_t ARBITER_WR_STS = 16;
static const size_t ARBITER_RB_STATUS = 16;
static const size_t ARBITER_RB_STATUS_OCC = 20;
static const size_t ARBITER_RB_ADDR_SPACE = 24;
static const size_t ARBITER_RB_SIZE_SPACE = 28;
// registers for the wb32_iface
static const size_t SR_CORE_READBACK = 0;
static UHD_INLINE size_t S2H_BASE(const size_t base)
{
return base + ZF_PAGE_SIZE * 0;
}
static UHD_INLINE size_t H2S_BASE(const size_t base)
{
return base + ZF_PAGE_SIZE * 1;
}
static UHD_INLINE size_t REG_BASE(const size_t base)
{
return base + ZF_PAGE_SIZE * 2;
}
static UHD_INLINE size_t DST_BASE(const size_t base)
{
return base + ZF_PAGE_SIZE * 3;
}
static UHD_INLINE size_t ZF_STREAM_OFF(const size_t which)
{
return which * 32;
}
#include "e300_fifo_config.hpp"
#include //mmap
#include //open, close
#include //poll
#include
#include
#include //sleep
#include //timeout
#include
#include
//locking stuff for shared irq
#include
#include
struct e300_fifo_poll_waiter
{
e300_fifo_poll_waiter(const int fd):
_fd(fd),
_poll_claimed(false)
{
//NOP
}
/*!
* Waits until the file descriptor fd has data to read.
* Access to the file descriptor is thread safe.
*/
void wait(const double timeout)
{
if (timeout == 0) {
return;
}
boost::mutex::scoped_lock l(_mutex);
if (_poll_claimed)
{
_cond.timed_wait(l, boost::posix_time::microseconds(timeout*1000000));
}
else
{
_poll_claimed = true;
l.unlock();
struct pollfd fds[1];
fds[0].fd = _fd;
fds[0].events = POLLIN;
::poll(fds, 1, long(timeout*1000));
if (fds[0].revents & POLLIN)
::read(_fd, NULL, 0);
l.lock();
_poll_claimed = 0;
_cond.notify_all();
}
}
boost::condition_variable _cond;
boost::mutex _mutex;
int _fd;
bool _poll_claimed;
};
static const size_t DEFAULT_FRAME_SIZE = 2048;
static const size_t DEFAULT_NUM_FRAMES = 32;
using namespace uhd;
using namespace uhd::transport;
struct __mem_addrz_t
{
size_t which, phys, data, ctrl;
};
/***********************************************************************
* peek n' poke mmapped space
**********************************************************************/
UHD_INLINE void zf_poke32(const uint32_t addr, const uint32_t data)
{
volatile uint32_t *p = reinterpret_cast(addr);
*p = data;
}
UHD_INLINE uint32_t zf_peek32(const uint32_t addr)
{
volatile const uint32_t *p = reinterpret_cast(addr);
return *p;
}
/***********************************************************************
* managed buffer
**********************************************************************/
struct e300_fifo_mb : managed_buffer
{
e300_fifo_mb(const __mem_addrz_t &addrs, const size_t len):
ctrl_base(addrs.ctrl), phys_mem(addrs.phys), mem((void *)addrs.data), len(len){}
void release(void)
{
UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_ADDR_SPACE) > 0);
UHD_ASSERT_THROW(zf_peek32(ctrl_base+ARBITER_RB_SIZE_SPACE) > 0);
zf_poke32(ctrl_base + ARBITER_WR_ADDR, phys_mem);
zf_poke32(ctrl_base + ARBITER_WR_SIZE, this->size());
}
template
UHD_INLINE typename T::sptr get_new(void)
{
return make(reinterpret_cast(this), mem, len);
}
const size_t ctrl_base;
const size_t phys_mem;
void *const mem;
const size_t len;
};
/***********************************************************************
* transport
**********************************************************************/
class e300_transport : public zero_copy_if
{
public:
e300_transport(
boost::shared_ptr allocator,
const __mem_addrz_t &addrs,
const size_t num_frames,
const size_t frame_size,
e300_fifo_poll_waiter *waiter,
const bool auto_release
):
_allocator(allocator),
_addrs(addrs),
_num_frames(num_frames),
_frame_size(frame_size),
_index(0),
_waiter(waiter)
{
//UHD_LOGGER_INFO("E300") << boost::format("phys 0x%x") % addrs.phys ;
//UHD_LOGGER_INFO("E300") << boost::format("data 0x%x") % addrs.data ;
//UHD_LOGGER_INFO("E300") << boost::format("ctrl 0x%x") % addrs.ctrl ;
const uint32_t sig = zf_peek32(_addrs.ctrl + ARBITER_RD_SIG);
UHD_ASSERT_THROW((sig >> 16) == 0xACE0);
zf_poke32(_addrs.ctrl + ARBITER_WR_CLEAR, 1);
for (size_t i = 0; i < num_frames; i++)
{
//create a managed buffer at the given offset
__mem_addrz_t mb_addrs = addrs;
mb_addrs.phys += (i*frame_size);
mb_addrs.data += (i*frame_size);
boost::shared_ptr mb(new e300_fifo_mb(mb_addrs, frame_size));
//setup the buffers so they are "positioned for use"
const size_t sts_good = (1 << 7) | (_addrs.which & 0xf);
if (auto_release) mb->get_new(); //release for read
else zf_poke32(_addrs.ctrl + ARBITER_WR_STS, sts_good); //poke an ok into the sts fifo
_buffs.push_back(mb);
}
}
~e300_transport(void)
{
//NOP
}
template
UHD_INLINE typename T::sptr get_buff(const double timeout)
{
const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(timeout);
while (1)
{
if (zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS_OCC))
{
const uint32_t sts = zf_peek32(_addrs.ctrl + ARBITER_RB_STATUS);
UHD_ASSERT_THROW((sts >> 7) & 0x1); //assert OK
UHD_ASSERT_THROW((sts & 0xf) == _addrs.which); //expected tag
zf_poke32(_addrs.ctrl + ARBITER_WR_STS_RDY, 1); //pop from sts fifo
if (_index == _num_frames)
_index = 0;
return _buffs[_index++]->get_new();
}
if (time_spec_t::get_system_time() > exit_time) {
break;
}
_waiter->wait(timeout);
//boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
return typename T::sptr();
}
managed_recv_buffer::sptr get_recv_buff(const double timeout)
{
return this->get_buff(timeout);
}
size_t get_num_recv_frames(void) const
{
return _num_frames;
}
size_t get_recv_frame_size(void) const
{
return _frame_size;
}
managed_send_buffer::sptr get_send_buff(const double timeout)
{
return this->get_buff(timeout);
}
size_t get_num_send_frames(void) const
{
return _num_frames;
}
size_t get_send_frame_size(void) const
{
return _frame_size;
}
private:
boost::shared_ptr _allocator;
const __mem_addrz_t _addrs;
const size_t _num_frames;
const size_t _frame_size;
size_t _index;
e300_fifo_poll_waiter *_waiter;
std::vector > _buffs;
};
/***********************************************************************
* memory mapping
**********************************************************************/
class e300_fifo_interface_impl : public virtual e300_fifo_interface
{
public:
e300_fifo_interface_impl(const e300_fifo_config_t &config):
_config(config),
_bytes_in_use(0),
_recv_entries_in_use(std::vector(S2H_NUM_STREAMS, 0)),
_send_entries_in_use(std::vector(H2S_NUM_STREAMS, 0))
{
//open the file descriptor to our kernel module
const std::string dev = "/dev/axi_fpga";
_fd = ::open(dev.c_str(), O_RDWR|O_SYNC);
if (_fd < 0)
{
throw uhd::runtime_error("e300: failed to open " + dev);
}
//mmap the control and data regions into virtual space
//UHD_VAR(_config.ctrl_length);
//UHD_VAR(_config.buff_length);
//UHD_VAR(_config.phys_addr);
_buff = ::mmap(NULL, _config.ctrl_length + _config.buff_length, PROT_READ|PROT_WRITE, MAP_SHARED, _fd, 0);
if (_buff == MAP_FAILED)
{
::close(_fd);
throw uhd::runtime_error("e300: failed to mmap " + dev);
}
//segment the memory according to zynq fifo arbiter
_ctrl_space = size_t(_buff);
_data_space = size_t(_buff) + _config.ctrl_length;
//zero out the data region
std::memset((void *)_data_space, 0, _config.buff_length);
//create a poll _waiter for the transports
_waiter = new e300_fifo_poll_waiter(_fd);
}
virtual ~e300_fifo_interface_impl(void)
{
delete _waiter;
UHD_LOGGER_TRACE("E300")<< "cleanup: munmap" ;
::munmap(_buff, _config.ctrl_length + _config.buff_length);
::close(_fd);
}
uhd::transport::zero_copy_if::sptr make_recv_xport(
const size_t which_stream,
const uhd::transport::zero_copy_xport_params ¶ms)
{
return this->_make_xport(which_stream, params, true);
}
uhd::transport::zero_copy_if::sptr make_send_xport(
const size_t which_stream,
const uhd::transport::zero_copy_xport_params ¶ms)
{
return this->_make_xport(which_stream, params, false);
}
size_t get_global_regs_base() const
{
return REG_BASE(_ctrl_space);
}
private:
uhd::transport::zero_copy_if::sptr _make_xport(
const size_t which_stream,
const uhd::transport::zero_copy_xport_params ¶ms,
const bool is_recv)
{
boost::mutex::scoped_lock lock(_setup_mutex);
const size_t frame_size = is_recv ? params.recv_frame_size : params.send_frame_size;
const size_t num_frames = is_recv ? params.num_recv_frames : params.num_send_frames;
size_t &entries_in_use = (is_recv)? _recv_entries_in_use.at(which_stream)
: _send_entries_in_use.at(which_stream);
__mem_addrz_t addrs;
addrs.which = which_stream;
addrs.phys = _config.phys_addr + _bytes_in_use;
addrs.data = _data_space + _bytes_in_use;
addrs.ctrl = ((is_recv)? S2H_BASE(_ctrl_space) : H2S_BASE(_ctrl_space)) + ZF_STREAM_OFF(which_stream);
uhd::transport::zero_copy_if::sptr xport;
if (is_recv) xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv));
else xport.reset(new e300_transport(shared_from_this(), addrs, num_frames, frame_size, _waiter, is_recv));
_bytes_in_use += num_frames*frame_size;
entries_in_use += num_frames;
UHD_ASSERT_THROW(_recv_entries_in_use.at(which_stream) <= S2H_NUM_CMDS);
UHD_ASSERT_THROW(_send_entries_in_use.at(which_stream) <= H2S_NUM_CMDS);
UHD_ASSERT_THROW(_bytes_in_use <= _config.buff_length);
return xport;
}
e300_fifo_config_t _config;
e300_fifo_poll_waiter *_waiter;
size_t _bytes_in_use;
int _fd;
void *_buff;
size_t _ctrl_space;
size_t _data_space;
std::vector _recv_entries_in_use;
std::vector _send_entries_in_use;
boost::mutex _setup_mutex;
};
e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &config)
{
return e300_fifo_interface::sptr(new e300_fifo_interface_impl(config));
}
#else //E300_NATIVE
#include "e300_fifo_config.hpp"
#include
e300_fifo_interface::sptr e300_fifo_interface::make(const e300_fifo_config_t &)
{
throw uhd::assertion_error("e300_fifo_interface::make() !E300_NATIVE");
}
#endif //E300_NATIVE