//
// 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
#include "x300_fw_common.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "x300_regs.hpp"
#include
#include
using namespace uhd;
using namespace uhd::niusrprio;
class x300_ctrl_iface : public wb_iface
{
public:
enum {num_retries = 3};
x300_ctrl_iface(bool enable_errors = true) : errors(enable_errors)
{
/* NOP */
}
void flush(void)
{
boost::mutex::scoped_lock lock(reg_access);
__flush();
}
void poke32(const wb_addr_type addr, const boost::uint32_t data)
{
for (size_t i = 1; i <= num_retries; i++)
{
boost::mutex::scoped_lock lock(reg_access);
try
{
return this->__poke32(addr, data);
}
catch(const uhd::io_error &ex)
{
std::string error_msg = str(boost::format(
"x300 fw communication failure #%u\n%s") % i % ex.what());
if (errors) UHD_MSG(error) << error_msg << std::endl;
if (i == num_retries) throw uhd::io_error(error_msg);
}
}
}
boost::uint32_t peek32(const wb_addr_type addr)
{
for (size_t i = 1; i <= num_retries; i++)
{
boost::mutex::scoped_lock lock(reg_access);
try
{
boost::uint32_t data = this->__peek32(addr);
return data;
}
catch(const uhd::io_error &ex)
{
std::string error_msg = str(boost::format(
"x300 fw communication failure #%u\n%s") % i % ex.what());
if (errors) UHD_MSG(error) << error_msg << std::endl;
if (i == num_retries) throw uhd::io_error(error_msg);
}
}
return 0;
}
protected:
bool errors;
virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) = 0;
virtual boost::uint32_t __peek32(const wb_addr_type addr) = 0;
virtual void __flush() = 0;
boost::mutex reg_access;
};
//-----------------------------------------------------
// Ethernet impl
//-----------------------------------------------------
class x300_ctrl_iface_enet : public x300_ctrl_iface
{
public:
x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true):
x300_ctrl_iface(enable_errors), udp(udp), seq(0)
{
try
{
this->peek32(0);
}
catch(...){}
}
protected:
virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data)
{
//load request struct
x300_fw_comms_t request = x300_fw_comms_t();
request.flags = uhd::htonx(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_POKE32);
request.sequence = uhd::htonx(seq++);
request.addr = uhd::htonx(addr);
request.data = uhd::htonx(data);
//send request
__flush();
udp->send(boost::asio::buffer(&request, sizeof(request)));
//recv reply
x300_fw_comms_t reply = x300_fw_comms_t();
const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
if (nbytes == 0) throw uhd::io_error("x300 fw poke32 - reply timed out");
//sanity checks
const size_t flags = uhd::ntohx(reply.flags);
UHD_ASSERT_THROW(nbytes == sizeof(reply));
UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR));
UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_POKE32);
UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK);
UHD_ASSERT_THROW(reply.sequence == request.sequence);
UHD_ASSERT_THROW(reply.addr == request.addr);
UHD_ASSERT_THROW(reply.data == request.data);
}
virtual boost::uint32_t __peek32(const wb_addr_type addr)
{
//load request struct
x300_fw_comms_t request = x300_fw_comms_t();
request.flags = uhd::htonx(X300_FW_COMMS_FLAGS_ACK | X300_FW_COMMS_FLAGS_PEEK32);
request.sequence = uhd::htonx(seq++);
request.addr = uhd::htonx(addr);
request.data = 0;
//send request
__flush();
udp->send(boost::asio::buffer(&request, sizeof(request)));
//recv reply
x300_fw_comms_t reply = x300_fw_comms_t();
const size_t nbytes = udp->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0);
if (nbytes == 0) throw uhd::io_error("x300 fw peek32 - reply timed out");
//sanity checks
const size_t flags = uhd::ntohx(reply.flags);
UHD_ASSERT_THROW(nbytes == sizeof(reply));
UHD_ASSERT_THROW(not (flags & X300_FW_COMMS_FLAGS_ERROR));
UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_PEEK32);
UHD_ASSERT_THROW(flags & X300_FW_COMMS_FLAGS_ACK);
UHD_ASSERT_THROW(reply.sequence == request.sequence);
UHD_ASSERT_THROW(reply.addr == request.addr);
//return result!
return uhd::ntohx(reply.data);
}
virtual void __flush(void)
{
char buff[X300_FW_COMMS_MTU] = {};
while (udp->recv(boost::asio::buffer(buff), 0.0)){} //flush
}
private:
uhd::transport::udp_simple::sptr udp;
size_t seq;
};
//-----------------------------------------------------
// PCIe impl
//-----------------------------------------------------
class x300_ctrl_iface_pcie : public x300_ctrl_iface
{
public:
x300_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy, bool enable_errors = true):
x300_ctrl_iface(enable_errors), _drv_proxy(drv_proxy)
{
nirio_status status = 0;
nirio_status_chain(_drv_proxy->set_attribute(RIO_ADDRESS_SPACE, BUS_INTERFACE), status);
//Verify that the Ettus FPGA loaded in the device. This may not be true if the
//user is switching to UHD after using LabVIEW FPGA.
boost::uint32_t pcie_fpga_signature = 0;
_drv_proxy->peek(FPGA_PCIE_SIG_REG, pcie_fpga_signature);
if (pcie_fpga_signature != FPGA_X3xx_SIG_VALUE)
throw uhd::io_error("cannot create x300_ctrl_iface_pcie. incorrect/no fpga image");
//Also, poll on the ZPU_STATUS bit to ensure all the state machines in the FPGA are
//ready to accept register transaction requests.
boost::uint32_t reg_data = 0xffffffff;
boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
boost::posix_time::time_duration elapsed;
do {
boost::this_thread::sleep(boost::posix_time::microsec(500)); //Avoid flooding the bus
elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
nirio_status_chain(_drv_proxy->peek(PCIE_ZPU_STATUS_REG(0), reg_data), status);
} while (
nirio_status_not_fatal(status) &&
(reg_data & PCIE_ZPU_STATUS_SUSPENDED) &&
elapsed.total_milliseconds() < INIT_TIMEOUT_IN_MS);
nirio_status_to_exception(status, "Could not initialize x300_ctrl_iface_pcie.");
try
{
this->peek32(0);
}
catch(...){}
}
protected:
virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data)
{
nirio_status status = 0;
boost::uint32_t reg_data = 0xffffffff;
boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
boost::posix_time::time_duration elapsed;
nirio_status_chain(_drv_proxy->poke(PCIE_ZPU_DATA_REG(addr), data), status);
if (nirio_status_not_fatal(status)) {
do {
boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus
elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
nirio_status_chain(_drv_proxy->peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status);
} while (
nirio_status_not_fatal(status) &&
((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) &&
elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS);
}
if (nirio_status_fatal(status))
throw uhd::io_error("x300 fw poke32 - hardware IO error");
if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS)
throw uhd::io_error("x300 fw poke32 - operation timed out");
}
virtual boost::uint32_t __peek32(const wb_addr_type addr)
{
nirio_status status = 0;
boost::uint32_t reg_data = 0xffffffff;
boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
boost::posix_time::time_duration elapsed;
nirio_status_chain(_drv_proxy->poke(PCIE_ZPU_READ_REG(addr), PCIE_ZPU_READ_START), status);
if (nirio_status_not_fatal(status)) {
do {
boost::this_thread::sleep(boost::posix_time::microsec(50)); //Avoid flooding the bus
elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
nirio_status_chain(_drv_proxy->peek(PCIE_ZPU_STATUS_REG(addr), reg_data), status);
} while (
nirio_status_not_fatal(status) &&
((reg_data & (PCIE_ZPU_STATUS_BUSY | PCIE_ZPU_STATUS_SUSPENDED)) != 0) &&
elapsed.total_milliseconds() < READ_TIMEOUT_IN_MS);
}
nirio_status_chain(_drv_proxy->peek(PCIE_ZPU_DATA_REG(addr), reg_data), status);
if (nirio_status_fatal(status))
throw uhd::io_error("x300 fw peek32 - hardware IO error");
if (elapsed.total_milliseconds() > READ_TIMEOUT_IN_MS)
throw uhd::io_error("x300 fw peek32 - operation timed out");
return reg_data;
}
virtual void __flush(void)
{
__peek32(0);
}
private:
niriok_proxy::sptr _drv_proxy;
static const boost::uint32_t READ_TIMEOUT_IN_MS = 10;
static const boost::uint32_t INIT_TIMEOUT_IN_MS = 5000;
};
wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true)
{
return wb_iface::sptr(new x300_ctrl_iface_enet(udp, enable_errors));
}
wb_iface::sptr x300_make_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy, bool enable_errors = true)
{
return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy, enable_errors));
}