1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-22 16:08:39 -05:00

Windows build: new build system for LimeSuite

This commit is contained in:
f4exb 2017-09-21 20:29:57 +02:00
parent c44981eecc
commit 0a9d70126b
16 changed files with 16 additions and 8193 deletions

View File

@ -11,8 +11,8 @@ TARGET = liblimesuite
DEFINES += ENOLINK=21
CONFIG(MINGW32):QMAKE_CXXFLAGS += -fpermissive
CONFIG(MINGW32):QMAKE_CXXFLAGS += -std=c++11
QMAKE_CXXFLAGS += -fpermissive
QMAKE_CXXFLAGS += -std=c++11
CONFIG(MINGW32):LIBLIMESUITESRC = "D:\softs\LimeSuite"
CONFIG(MINGW64):LIBLIMESUITESRC = "D:\softs\LimeSuite"
@ -23,7 +23,6 @@ CONFIG(MINGW64):INCLUDEPATH += "D:\softs\libusb-1.0.20\include"
CONFIG(MINGW32):INCLUDEPATH += "..\libsqlite3\src"
CONFIG(MINGW64):INCLUDEPATH += "..\libsqlite3\src"
INCLUDEPATH += srcmw
INCLUDEPATH += $$LIBLIMESUITESRC/src
INCLUDEPATH += $$LIBLIMESUITESRC/src/ADF4002
INCLUDEPATH += $$LIBLIMESUITESRC/src/ConnectionRegistry
@ -43,13 +42,13 @@ SOURCES = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.cpp\
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionHandle.cpp\
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionRegistry.cpp\
$$LIBLIMESUITESRC/src/ConnectionRegistry/IConnection.cpp\
srcmw/ConnectionSTREAM/ConnectionSTREAM.cpp\
srcmw/ConnectionSTREAM/ConnectionSTREAMImages.cpp\
srcmw/ConnectionSTREAM/ConnectionSTREAMing.cpp\
srcmw/ConnectionSTREAM/ConnectionSTREAMEntry.cpp\
srcmw/Connection_uLimeSDR/Connection_uLimeSDR.cpp\
srcmw/Connection_uLimeSDR/Connection_uLimeSDRing.cpp\
srcmw/Connection_uLimeSDR/Connection_uLimeSDREntry.cpp\
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAM.cpp\
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAMImages.cpp\
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAMing.cpp\
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAMEntry.cpp\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/Connection_uLimeSDR.cpp\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/Connection_uLimeSDRing.cpp\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/Connection_uLimeSDREntry.cpp\
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybus.cpp\
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybusEntry.cpp\
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybusing.cpp\
@ -63,18 +62,18 @@ SOURCES = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.cpp\
$$LIBLIMESUITESRC/src/lms7002m/CalibrationCache.cpp\
$$LIBLIMESUITESRC/src/lms7002m/goert.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_BaseCalibrations.cpp\
srcmw/lms7002m/LMS7002M.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_filtersCalibration.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_gainCalibrations.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_parameters.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RegistersMap.cpp\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RxTxCalibrations.cpp\
$$LIBLIMESUITESRC/src/lms7002m/mcu_dc_iq_calibration.cpp\
srcmw/lms7002m_mcu/MCU_BD.cpp\
$$LIBLIMESUITESRC/src/lms7002m_mcu/MCU_BD.cpp\
$$LIBLIMESUITESRC/src/protocols/ILimeSDRStreaming.cpp\
$$LIBLIMESUITESRC/src/protocols/LMS64CProtocol.cpp\
$$LIBLIMESUITESRC/src/Si5351C/Si5351C.cpp\
srcmw/ErrorReporting.cpp\
$$LIBLIMESUITESRC/src/ErrorReporting.cpp\
$$LIBLIMESUITESRC/src/Logger.cpp\
src/SystemResources.cpp\
src/VersionInfo.cpp
@ -85,8 +84,8 @@ HEADERS = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.h\
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionHandle.h\
$$LIBLIMESUITESRC/src/ConnectionRegistry/ConnectionRegistry.h\
$$LIBLIMESUITESRC/src/ConnectionRegistry/IConnection.h\
srcmw/ConnectionSTREAM/ConnectionSTREAM.h\
srcmw/Connection_uLimeSDR/Connection_uLimeSDR.h\
$$LIBLIMESUITESRC/src/ConnectionSTREAM/ConnectionSTREAM.h\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/Connection_uLimeSDR.h\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/DRV_DriverInterface.h\
$$LIBLIMESUITESRC/src/Connection_uLimeSDR/FTD3XXLibrary/FTD3XX.h\
$$LIBLIMESUITESRC/src/ConnectionXillybus/ConnectionXillybus.h\
@ -103,7 +102,7 @@ HEADERS = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.h\
$$LIBLIMESUITESRC/src/lms7002m/LMS7002M_RegistersMap.h\
$$LIBLIMESUITESRC/src/lms7002m/mcu_programs.h\
$$LIBLIMESUITESRC/src/lms7002m_mcu/MCU_BD.h\
srcmw/lms7002m_mcu/MCU_File.h\
$$LIBLIMESUITESRC/src/lms7002m_mcu/MCU_File.h\
$$LIBLIMESUITESRC/src/protocols/ADCUnits.h\
$$LIBLIMESUITESRC/src/protocols/dataTypes.h\
$$LIBLIMESUITESRC/src/protocols/fifo.h\
@ -112,7 +111,7 @@ HEADERS = $$LIBLIMESUITESRC/src/ADF4002/ADF4002.h\
$$LIBLIMESUITESRC/src/protocols/LMS64CProtocol.h\
$$LIBLIMESUITESRC/src/protocols/LMSBoards.h\
$$LIBLIMESUITESRC/src/Si5351C/Si5351C.h\
srcmw/ErrorReporting.h\
$$LIBLIMESUITESRC/src/ErrorReporting.h\
$$LIBLIMESUITESRC/src/Logger.h\
$$LIBLIMESUITESRC/src/SystemResources.h\
$$LIBLIMESUITESRC/src/VersionInfo.h\

View File

@ -1,945 +0,0 @@
/**
@file ConnectionSTREAM.cpp
@author Lime Microsystems
@brief Implementation of STREAM board connection.
*/
#include "ConnectionSTREAM.h"
#include "ErrorReporting.h"
#include <cstring>
#include "Si5351C.h"
#include "FPGA_common.h"
#include "LMS7002M.h"
#include "Logger.h"
#include <ciso646>
#include <fstream>
#include <thread>
#include <chrono>
using namespace std;
#define CTR_W_REQCODE 0xC1
#define CTR_W_VALUE 0x0000
#define CTR_W_INDEX 0x0000
#define CTR_R_REQCODE 0xC0
#define CTR_R_VALUE 0x0000
#define CTR_R_INDEX 0x0000
using namespace lime;
const uint8_t ConnectionSTREAM::ctrlBulkOutAddr = 0x0F;
const uint8_t ConnectionSTREAM::ctrlBulkInAddr = 0x8F;
//control commands to be send via bulk port for boards v1.1 and earlier
const std::set<uint8_t> ConnectionSTREAM::commandsToBulkCtrlHw1 =
{
CMD_BRDSPI_WR, CMD_BRDSPI_RD,
CMD_LMS7002_WR, CMD_LMS7002_RD,
CMD_LMS7002_RST,
};
//control commands to be send via bulk port for boards v1.2 and later
const std::set<uint8_t> ConnectionSTREAM::commandsToBulkCtrlHw2 =
{
CMD_BRDSPI_WR, CMD_BRDSPI_RD,
CMD_LMS7002_WR, CMD_LMS7002_RD,
CMD_ANALOG_VAL_WR, CMD_ANALOG_VAL_RD,
CMD_ADF4002_WR,
CMD_LMS7002_RST,
CMD_GPIO_DIR_WR, CMD_GPIO_DIR_RD,
CMD_GPIO_WR, CMD_GPIO_RD,
};
/** @brief Initializes port type and object necessary to communicate to usb device.
*/
ConnectionSTREAM::ConnectionSTREAM(void *arg, const std::string &vidpid, const std::string &serial, const unsigned index)
{
bulkCtrlAvailable = false;
bulkCtrlInProgress = false;
RxLoopFunction = bind(&ConnectionSTREAM::ReceivePacketsLoop, this, std::placeholders::_1);
TxLoopFunction = bind(&ConnectionSTREAM::TransmitPacketsLoop, this, std::placeholders::_1);
isConnected = false;
#ifndef __unix__
if(arg == nullptr)
USBDevicePrimary = new CCyFX3Device();
else
USBDevicePrimary = new CCyFX3Device(*(CCyFX3Device*)arg);
InCtrlEndPt3 = nullptr;
OutCtrlEndPt3 = nullptr;
InCtrlBulkEndPt = nullptr;
OutCtrlBulkEndPt = nullptr;
for (int i = 0; i < MAX_EP_CNT; i++)
InEndPt[i] = OutEndPt[i] = nullptr;
#else
dev_handle = nullptr;
ctx = (libusb_context *)arg;
#endif
if (this->Open(vidpid, serial, index) != 0)
lime::error(GetLastErrorMessage());
commandsToBulkCtrl = commandsToBulkCtrlHw2;
LMSinfo info = this->GetInfo();
if (info.hardware <= 1)
{
commandsToBulkCtrl = commandsToBulkCtrlHw1;
}
this->VersionCheck();
if (info.device == LMS_DEV_LIMESDR || info.device == LMS_DEV_LIMESDR_USB_SP || info.device == LMS_DEV_LMS7002M_ULTIMATE_EVB)
DetectRefClk();
GetChipVersion();
//must configure synthesizer before using LimeSDR
if (info.device == LMS_DEV_LIMESDR && info.hardware < 4)
{
std::shared_ptr<Si5351C> si5351module(new Si5351C());
si5351module->Initialize(this);
si5351module->SetPLL(0, 25000000, 0);
si5351module->SetPLL(1, 25000000, 0);
si5351module->SetClock(0, 27000000, true, false);
si5351module->SetClock(1, 27000000, true, false);
for (int i = 2; i < 8; ++i)
si5351module->SetClock(i, 27000000, false, false);
Si5351C::Status status = si5351module->ConfigureClocks();
if (status != Si5351C::SUCCESS)
{
lime::warning("Failed to configure Si5351C");
return;
}
status = si5351module->UploadConfiguration();
if (status != Si5351C::SUCCESS)
lime::warning("Failed to upload Si5351C configuration");
std::this_thread::sleep_for(std::chrono::milliseconds(10)); //some settle time
}
}
double ConnectionSTREAM::DetectRefClk(void)
{
const double fx3Clk = 100e6 * 1.008; //fx3 clock 100MHz (adjusted to 100.8 MHz based on measurement on multiple boards)
const double fx3Cnt = 16777210; //fixed fx3 counter in FPGA
const double clkTbl[] = { 30.72e6, 38.4e6, 40e6, 52e6 };
const uint32_t addr[] = { 0x61, 0x63 };
const uint32_t vals[] = { 0x0, 0x0 };
if (this->WriteRegisters(addr, vals, 2) != 0)
{
return -1;
}
auto start = std::chrono::steady_clock::now();
if (this->WriteRegister(0x61, 0x4) != 0)
{
return -1;
}
while (1) //wait for test to finish
{
unsigned completed;
if (this->ReadRegister(0x65, completed) != 0)
{
return -1;
}
if (completed & 0x4)
break;
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
if (elapsed_seconds.count() > 0.5) //timeout
{
return -1;
}
}
const uint32_t addr2[] = { 0x72, 0x73 };
uint32_t vals2[2];
if (this->ReadRegisters(addr2, vals2, 2) != 0)
{
return -1;
}
double count = (vals2[0] | (vals2[1] << 16)); //cock counter
count *= fx3Clk / fx3Cnt; //estimate ref clock based on FX3 Clock
lime::info("Estimated reference clock %1.4f MHz", count/1e6);
unsigned i = 0;
double delta = 100e6;
while (i < sizeof(clkTbl) / sizeof(*clkTbl))
if (delta < fabs(count - clkTbl[i]))
break;
else
delta = fabs(count - clkTbl[i++]);
this->SetReferenceClockRate(clkTbl[i-1]);
lime::info("Selected reference clock %1.3f MHz", clkTbl[i - 1] / 1e6);
return clkTbl[i - 1];
}
/** @brief Closes connection to chip and deallocates used memory.
*/
ConnectionSTREAM::~ConnectionSTREAM()
{
Close();
#ifndef __unix__
delete USBDevicePrimary;
#endif
}
/** @brief Tries to open connected USB device and find communication endpoints.
@return Returns 0-Success, other-EndPoints not found or device didn't connect.
*/
int ConnectionSTREAM::Open(const std::string &vidpid, const std::string &serial, const unsigned index)
{
bulkCtrlAvailable = false;
#ifndef __unix__
if(index > USBDevicePrimary->DeviceCount())
return ReportError(ERANGE, "ConnectionSTREAM: Device index out of range");
if(USBDevicePrimary->Open(index) == false)
return ReportError(-1, "ConnectionSTREAM: Failed to open device");
if (InCtrlEndPt3)
{
delete InCtrlEndPt3;
InCtrlEndPt3 = nullptr;
}
InCtrlEndPt3 = new CCyControlEndPoint(*USBDevicePrimary->ControlEndPt);
if (OutCtrlEndPt3)
{
delete OutCtrlEndPt3;
OutCtrlEndPt3 = nullptr;
}
OutCtrlEndPt3 = new CCyControlEndPoint(*USBDevicePrimary->ControlEndPt);
InCtrlEndPt3->ReqCode = CTR_R_REQCODE;
InCtrlEndPt3->Value = CTR_R_VALUE;
InCtrlEndPt3->Index = CTR_R_INDEX;
InCtrlEndPt3->TimeOut = 3000;
OutCtrlEndPt3->ReqCode = CTR_W_REQCODE;
OutCtrlEndPt3->Value = CTR_W_VALUE;
OutCtrlEndPt3->Index = CTR_W_INDEX;
OutCtrlEndPt3->TimeOut = 3000;
for (int i = 0; i < USBDevicePrimary->EndPointCount(); i++)
{
auto adr = USBDevicePrimary->EndPoints[i]->Address;
if (adr < ctrlBulkOutAddr)
{
OutEndPt[adr] = USBDevicePrimary->EndPoints[i];
long len = OutEndPt[adr]->MaxPktSize * 64;
OutEndPt[adr]->SetXferSize(len);
}
else if (adr < ctrlBulkInAddr)
{
adr &= 0xF;
InEndPt[adr] = USBDevicePrimary->EndPoints[i];
long len = InEndPt[adr]->MaxPktSize * 64;
InEndPt[adr]->SetXferSize(len);
}
}
InCtrlBulkEndPt = nullptr;
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
if(USBDevicePrimary->EndPoints[i]->Address == ctrlBulkInAddr)
{
InCtrlBulkEndPt = USBDevicePrimary->EndPoints[i];
InCtrlBulkEndPt->TimeOut = 1000;
bulkCtrlAvailable = true;
break;
}
OutCtrlBulkEndPt = nullptr;
for (int i=0; i<USBDevicePrimary->EndPointCount(); i++)
if(USBDevicePrimary->EndPoints[i]->Address == ctrlBulkOutAddr)
{
OutCtrlBulkEndPt = USBDevicePrimary->EndPoints[i];
OutCtrlBulkEndPt->TimeOut = 1000;
bulkCtrlAvailable = true;
break;
}
isConnected = true;
return 0;
#else
const auto splitPos = vidpid.find(":");
const auto vid = std::stoi(vidpid.substr(0, splitPos), nullptr, 16);
const auto pid = std::stoi(vidpid.substr(splitPos+1), nullptr, 16);
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
int usbDeviceCount = libusb_get_device_list(ctx, &devs);
if (usbDeviceCount < 0) {
return ReportError(-1, "ConnectionSTREAM: libusb_get_device_list failed: %s", libusb_strerror(libusb_error(usbDeviceCount)));
}
for(int i=0; i<usbDeviceCount; ++i)
{
libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(devs[i], &desc);
if(r<0) {
lime::error("failed to get device description");
continue;
}
if (desc.idProduct != pid) continue;
if (desc.idVendor != vid) continue;
if(libusb_open(devs[i], &dev_handle) != 0) continue;
std::string foundSerial;
if (desc.iSerialNumber > 0)
{
char data[255];
r = libusb_get_string_descriptor_ascii(dev_handle,desc.iSerialNumber,(unsigned char*)data, sizeof(data));
if(r<0)
lime::error("failed to get serial number");
else
foundSerial = std::string(data, size_t(r));
}
if (serial == foundSerial) break; //found it
libusb_close(dev_handle);
dev_handle = nullptr;
}
libusb_free_device_list(devs, 1);
if(dev_handle == nullptr)
return ReportError(-1, "ConnectionSTREAM: libusb_open failed");
if(libusb_kernel_driver_active(dev_handle, 0) == 1) //find out if kernel driver is attached
{
lime::info("Kernel Driver Active");
if(libusb_detach_kernel_driver(dev_handle, 0) == 0) //detach it
lime::info("Kernel Driver Detached!");
}
int r = libusb_claim_interface(dev_handle, 0); //claim interface 0 (the first) of device
if(r < 0)
return ReportError(-1, "ConnectionSTREAM: Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
libusb_device* device = libusb_get_device(dev_handle);
libusb_config_descriptor* descriptor = nullptr;
if(libusb_get_active_config_descriptor(device, &descriptor) < 0)
{
lime::error("failed to get config descriptor");
}
//check for 0x0F and 0x8F endpoints
if(descriptor->bNumInterfaces > 0)
{
libusb_interface_descriptor iface = descriptor->interface[0].altsetting[0];
for(int i=0; i<iface.bNumEndpoints; ++i)
if(iface.endpoint[i].bEndpointAddress == ctrlBulkOutAddr ||
iface.endpoint[i].bEndpointAddress == ctrlBulkInAddr)
{
bulkCtrlAvailable = true;
break;
}
}
libusb_free_config_descriptor(descriptor);
isConnected = true;
if(bulkCtrlAvailable)
{
LMS64CProtocol::GenericPacket ctrPkt;
ctrPkt.cmd = CMD_USB_FIFO_RST;
ctrPkt.outBuffer.push_back(0x01); //reset bulk endpoints
if(TransferPacket(ctrPkt) != 0)
lime::error("Failed to reset USB bulk endpoints");
}
return 0;
#endif
}
/** @brief Closes communication to device.
*/
void ConnectionSTREAM::Close()
{
#ifndef __unix__
USBDevicePrimary->Close();
for (int i = 0; i < MAX_EP_CNT; i++)
InEndPt[i] = OutEndPt[i] = nullptr;
InCtrlBulkEndPt = nullptr;
OutCtrlBulkEndPt = nullptr;
if (InCtrlEndPt3)
{
delete InCtrlEndPt3;
InCtrlEndPt3 = nullptr;
}
if (OutCtrlEndPt3)
{
delete OutCtrlEndPt3;
OutCtrlEndPt3 = nullptr;
}
#else
if(dev_handle != 0)
{
libusb_release_interface(dev_handle, 0);
libusb_close(dev_handle);
dev_handle = 0;
}
#endif
isConnected = false;
}
/** @brief Returns connection status
@return 1-connection open, 0-connection closed.
*/
bool ConnectionSTREAM::IsOpen()
{
#ifndef __unix__
return USBDevicePrimary->IsOpen() && isConnected;
#else
return isConnected;
#endif
}
/** @brief Sends given data buffer to chip through USB port.
@param buffer data buffer, must not be longer than 64 bytes.
@param length given buffer size.
@param timeout_ms timeout limit for operation in milliseconds
@return number of bytes sent.
*/
int ConnectionSTREAM::Write(const unsigned char *buffer, const int length, int timeout_ms)
{
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
long len = length;
if(IsOpen() == false)
return 0;
unsigned char* wbuffer = new unsigned char[length];
memcpy(wbuffer, buffer, length);
bulkCtrlInProgress = false;
#ifndef __unix__
if(bulkCtrlAvailable
&& commandsToBulkCtrl.find(buffer[0]) != commandsToBulkCtrl.end())
{
bulkCtrlInProgress = true;
OutCtrlBulkEndPt->XferData(wbuffer, len);
}
else if(OutCtrlEndPt3)
OutCtrlEndPt3->Write(wbuffer, len);
else
len = 0;
#else
if(bulkCtrlAvailable
&& commandsToBulkCtrl.find(buffer[0]) != commandsToBulkCtrl.end())
{
bulkCtrlInProgress = true;
int actual = 0;
libusb_bulk_transfer(dev_handle, ctrlBulkOutAddr, wbuffer, length, &actual, timeout_ms);
len = actual;
}
else
len = libusb_control_transfer(dev_handle, LIBUSB_REQUEST_TYPE_VENDOR,CTR_W_REQCODE ,CTR_W_VALUE, CTR_W_INDEX, wbuffer, length, timeout_ms);
#endif
delete[] wbuffer;
return len;
}
/** @brief Reads data coming from the chip through USB port.
@param buffer pointer to array where received data will be copied, array must be
big enough to fit received data.
@param length number of bytes to read from chip.
@param timeout_ms timeout limit for operation in milliseconds
@return number of bytes received.
*/
int ConnectionSTREAM::Read(unsigned char *buffer, const int length, int timeout_ms)
{
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
long len = length;
if(IsOpen() == false)
return 0;
#ifndef __unix__
if(bulkCtrlAvailable && bulkCtrlInProgress)
{
InCtrlBulkEndPt->XferData(buffer, len);
bulkCtrlInProgress = false;
}
else if(InCtrlEndPt3)
InCtrlEndPt3->Read(buffer, len);
else
len = 0;
#else
if(bulkCtrlAvailable && bulkCtrlInProgress)
{
int actual = 0;
libusb_bulk_transfer(dev_handle, ctrlBulkInAddr, buffer, len, &actual, timeout_ms);
len = actual;
bulkCtrlInProgress = false;
}
else
len = libusb_control_transfer(dev_handle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN ,CTR_R_REQCODE ,CTR_R_VALUE, CTR_R_INDEX, buffer, len, timeout_ms);
#endif
return len;
}
#ifdef __unix__
/** @brief Function for handling libusb callbacks
*/
void callback_libusbtransfer(libusb_transfer *trans)
{
USBTransferContext *context = reinterpret_cast<USBTransferContext*>(trans->user_data);
std::unique_lock<std::mutex> lck(context->transferLock);
switch(trans->status)
{
case LIBUSB_TRANSFER_CANCELLED:
//lime::error("Transfer %i canceled", context->id);
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
//context->reset();
break;
case LIBUSB_TRANSFER_COMPLETED:
//if(trans->actual_length == context->bytesExpected)
{
context->bytesXfered = trans->actual_length;
context->done.store(true);
}
break;
case LIBUSB_TRANSFER_ERROR:
lime::error("TRANSFER ERRROR");
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
break;
case LIBUSB_TRANSFER_TIMED_OUT:
//lime::error("transfer timed out %i", context->id);
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
break;
case LIBUSB_TRANSFER_OVERFLOW:
lime::error("transfer overflow");
break;
case LIBUSB_TRANSFER_STALL:
lime::error("transfer stalled");
break;
case LIBUSB_TRANSFER_NO_DEVICE:
lime::error("transfer no device");
break;
}
lck.unlock();
context->cv.notify_one();
}
#endif
/**
@brief Starts asynchronous data reading from board
@param *buffer buffer where to store received data
@param length number of bytes to read
@param streamBulkInAddr endpoint index?
@return handle of transfer context
*/
int ConnectionSTREAM::BeginDataReading(char *buffer, uint32_t length, const uint8_t streamBulkInAddr)
{
int i = 0;
bool contextFound = false;
//find not used context
for(i = 0; i<USB_MAX_CONTEXTS; i++)
{
if(!contexts[i].used)
{
contextFound = true;
break;
}
}
if(!contextFound)
{
lime::error("No contexts left for reading data");
return -1;
}
contexts[i].used = true;
#ifndef __unix__
if (InEndPt[streamBulkInAddr & 0xF])
{
contexts[i].EndPt = InEndPt[streamBulkInAddr & 0xF];
contexts[i].context = contexts[i].EndPt->BeginDataXfer((unsigned char*)buffer, length, contexts[i].inOvLap);
}
return i;
#else
unsigned int Timeout = 500;
libusb_transfer *tr = contexts[i].transfer;
libusb_fill_bulk_transfer(tr, dev_handle, streamBulkInAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contexts[i], Timeout);
contexts[i].done = false;
contexts[i].bytesXfered = 0;
contexts[i].bytesExpected = length;
int status = libusb_submit_transfer(tr);
if(status != 0)
{
lime::error("BEGIN DATA READING %s", libusb_error_name(status));
contexts[i].used = false;
return -1;
}
#endif
return i;
}
/**
@brief Waits for asynchronous data reception
@param contextHandle handle of which context data to wait
@param timeout_ms number of miliseconds to wait
@return 1-data received, 0-data not received
*/
int ConnectionSTREAM::WaitForReading(int contextHandle, unsigned int timeout_ms)
{
if(contextHandle >= 0 && contexts[contextHandle].used == true)
{
#ifndef __unix__
int status = 0;
status = contexts[contextHandle].EndPt->WaitForXfer(contexts[contextHandle].inOvLap, timeout_ms);
return status;
#else
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
std::unique_lock<std::mutex> lck(contexts[contextHandle].transferLock);
while(contexts[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
{
//blocking not to waste CPU
contexts[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
t2 = chrono::high_resolution_clock::now();
}
return contexts[contextHandle].done.load() == true;
#endif
}
else
return 0;
}
/**
@brief Finishes asynchronous data reading from board
@param buffer array where to store received data
@param length number of bytes to read
@param contextHandle handle of which context to finish
@return negative values failure, positive number of bytes received
*/
int ConnectionSTREAM::FinishDataReading(char *buffer, uint32_t length, int contextHandle)
{
if(contextHandle >= 0 && contexts[contextHandle].used == true)
{
#ifndef __unix__
int status = 0;
long len = length;
status = contexts[contextHandle].EndPt->FinishDataXfer((unsigned char*)buffer, len, contexts[contextHandle].inOvLap, contexts[contextHandle].context);
contexts[contextHandle].used = false;
contexts[contextHandle].reset();
return len;
#else
length = contexts[contextHandle].bytesXfered;
contexts[contextHandle].used = false;
contexts[contextHandle].reset();
return length;
#endif
}
else
return 0;
}
/**
@brief Aborts reading operations
*/
void ConnectionSTREAM::AbortReading(int ep)
{
#ifndef __unix__
for (int i = 0; i < MAX_EP_CNT; i++)
if (InEndPt[i] && InEndPt[i]->Address == ep)
InEndPt[i]->Abort();
#else
for(int i=0; i<USB_MAX_CONTEXTS; ++i)
{
if(contexts[i].used && contexts[i].transfer->endpoint == ep)
libusb_cancel_transfer( contexts[i].transfer );
}
#endif
}
/**
@brief Starts asynchronous data Sending to board
@param *buffer buffer to send
@param length number of bytes to send
@param streamBulkOutAddr endpoint index?
@return handle of transfer context
*/
int ConnectionSTREAM::BeginDataSending(const char *buffer, uint32_t length, const uint8_t streamBulkOutAddr)
{
int i = 0;
//find not used context
bool contextFound = false;
for(i = 0; i<USB_MAX_CONTEXTS; i++)
{
if(!contextsToSend[i].used)
{
contextFound = true;
break;
}
}
if(!contextFound)
return -1;
contextsToSend[i].used = true;
#ifndef __unix__
if (OutEndPt[streamBulkOutAddr])
{
contextsToSend[i].EndPt = OutEndPt[streamBulkOutAddr];
contextsToSend[i].context = contextsToSend[i].EndPt->BeginDataXfer((unsigned char*)buffer, length, contextsToSend[i].inOvLap);
}
return i;
#else
unsigned int Timeout = 500;
libusb_transfer *tr = contextsToSend[i].transfer;
libusb_fill_bulk_transfer(tr, dev_handle, streamBulkOutAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contextsToSend[i], Timeout);
contextsToSend[i].done = false;
contextsToSend[i].bytesXfered = 0;
contextsToSend[i].bytesExpected = length;
int status = libusb_submit_transfer(tr);
if(status != 0)
{
lime::error("BEGIN DATA SENDING %s", libusb_error_name(status));
contextsToSend[i].used = false;
return -1;
}
#endif
return i;
}
/**
@brief Waits for asynchronous data sending
@param contextHandle handle of which context data to wait
@param timeout_ms number of miliseconds to wait
@return 1-data received, 0-data not received
*/
int ConnectionSTREAM::WaitForSending(int contextHandle, unsigned int timeout_ms)
{
if( contextsToSend[contextHandle].used == true )
{
# ifndef __unix__
int status = 0;
status = contextsToSend[contextHandle].EndPt->WaitForXfer(contextsToSend[contextHandle].inOvLap, timeout_ms);
return status;
# else
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
std::unique_lock<std::mutex> lck(contextsToSend[contextHandle].transferLock);
while(contextsToSend[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
{
//blocking not to waste CPU
contextsToSend[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
t2 = chrono::high_resolution_clock::now();
}
return contextsToSend[contextHandle].done == true;
# endif
}
else
return 0;
}
/**
@brief Finishes asynchronous data sending to board
@param buffer array where to store received data
@param length number of bytes to read, function changes this value to number of bytes acctually received
@param contextHandle handle of which context to finish
@return false failure, true number of bytes sent
*/
int ConnectionSTREAM::FinishDataSending(const char *buffer, uint32_t length, int contextHandle)
{
if( contextsToSend[contextHandle].used == true)
{
#ifndef __unix__
long len = length;
contextsToSend[contextHandle].EndPt->FinishDataXfer((unsigned char*)buffer, len, contextsToSend[contextHandle].inOvLap, contextsToSend[contextHandle].context);
contextsToSend[contextHandle].used = false;
contextsToSend[contextHandle].reset();
return len;
#else
length = contextsToSend[contextHandle].bytesXfered;
contextsToSend[contextHandle].used = false;
contextsToSend[contextHandle].reset();
return length;
#endif
}
else
return 0;
}
/**
@brief Aborts sending operations
*/
void ConnectionSTREAM::AbortSending(int ep)
{
#ifndef __unix__
for (int i = 0; i < MAX_EP_CNT; i++)
if (OutEndPt[i] && OutEndPt[i]->Address == ep)
OutEndPt[i]->Abort();
#else
for (int i = 0; i<USB_MAX_CONTEXTS; ++i)
{
if(contextsToSend[i].used && contextsToSend[i].transfer->endpoint == ep)
libusb_cancel_transfer(contextsToSend[i].transfer);
}
#endif
}
int ConnectionSTREAM::SendData(const char* buffer, int length, int epIndex, int timeout)
{
const unsigned char ep = 0x01;
int context = BeginDataSending((char*)buffer, length, ep);
if (WaitForSending(context, timeout)==false)
AbortSending(ep);
return FinishDataSending((char*)buffer, length , context);
}
int ConnectionSTREAM::ReceiveData(char* buffer, int length, int epIndex, int timeout)
{
const unsigned char ep = 0x81;
int context = BeginDataReading(buffer, length, ep);
if (WaitForReading(context, timeout) == false)
AbortReading(ep);
return FinishDataReading(buffer, length, context);
}
int ConnectionSTREAM::ProgramWrite(const char *buffer, const size_t length, const int programmingMode, const int device, ProgrammingCallback callback)
{
if (device == LMS64CProtocol::FX3 && programmingMode == 1)
{
#ifdef __unix__
libusb_device_descriptor desc;
int ret = libusb_get_device_descriptor(libusb_get_device(dev_handle), &desc);
if(ret<0)
lime::error("failed to get device description");
else if (desc.idProduct == 243)
#else
if (USBDevicePrimary->ProductID == 243)
#endif
{
#ifdef __unix__
return fx3_usbboot_download((unsigned char*)buffer,length);
#else
char* filename = "fx3fw_image_tmp.img";
int ret = 0;
std::ofstream myfile(filename, ios::out | ios::binary | ios::trunc);
if (!myfile.is_open())
{
ReportError("FX3 FW:Unable to create temporary file");
return -1;
}
myfile.write(buffer,length);
if (myfile.fail())
{
ReportError("FX3 FW:Unable to write to temporary file");
ret = -1;
}
myfile.close();
if (ret != -1)
{
if ((ret=USBDevicePrimary->DownloadFw(filename, FX3_FWDWNLOAD_MEDIA_TYPE::RAM))!=0)
ReportError("FX3: Failed to upload FW to RAM");
}
std::remove(filename);
return ret;
#endif
}
else
{
ReportError("FX3 bootloader NOT detected");
return -1;
}
}
return LMS64CProtocol::ProgramWrite(buffer,length,programmingMode,device,callback);
}
#ifdef __unix__
#define MAX_FWIMG_SIZE (512 * 1024) // Maximum size of the firmware binary.
#define GET_LSW(v) ((unsigned short)((v) & 0xFFFF))
#define GET_MSW(v) ((unsigned short)((v) >> 16))
#define VENDORCMD_TIMEOUT (5000) // Timeout for each vendor command is set to 5 seconds.
int ConnectionSTREAM::ram_write(unsigned char *buf, unsigned int ramAddress, int len)
{
const int MAX_WRITE_SIZE = (2 * 1024); // Max. size of data that can be written through one vendor command.
int r;
int index = 0;
int size;
while ( len > 0 )
{
size = (len > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : len;
r = libusb_control_transfer(dev_handle, 0x40, 0xA0, GET_LSW(ramAddress), GET_MSW(ramAddress),&buf[index], size, VENDORCMD_TIMEOUT);
if ( r != size )
{
lime::error("Vendor write to FX3 RAM failed");
return -1;
}
ramAddress += size;
index += size;
len -= size;
}
return 0;
}
int ConnectionSTREAM::fx3_usbboot_download(unsigned char *fwBuf, int filesize)
{
unsigned int *data_p;
unsigned int i, checksum;
unsigned int address, length;
int r, index;
if ( filesize > MAX_FWIMG_SIZE ) {
ReportError("File size exceeds maximum firmware image size\n");
return -2;
}
if ( strncmp((char *)fwBuf,"CY",2) ) {
ReportError("Image does not have 'CY' at start. aborting\n");
return -4;
}
if ( fwBuf[2] & 0x01 ) {
ReportError("Image does not contain executable code\n");
return -5;
}
if ( !(fwBuf[3] == 0xB0) ) {
ReportError("Not a normal FW binary with checksum\n");
return -6;
}
// Run through each section of code, and use vendor commands to download them to RAM.
index = 4;
checksum = 0;
while ( index < filesize )
{
data_p = (unsigned int *)(fwBuf + index);
length = data_p[0];
address = data_p[1];
if (length != 0)
{
for (i = 0; i < length; i++)
checksum += data_p[2 + i];
r = ram_write(fwBuf + index + 8, address, length * 4);
if (r != 0)
{
ReportError("Failed to download data to FX3 RAM\n");
return -3;
}
}
else
{
if (checksum != data_p[2]) {
ReportError ("Checksum error in firmware binary\n");
return -4;
}
r = libusb_control_transfer(dev_handle, 0x40, 0xA0, GET_LSW(address), GET_MSW(address), NULL,0, VENDORCMD_TIMEOUT);
if ( r != 0 )
lime::error("Ignored error in control transfer: %d", r);
break;
}
index += (8 + length * 4);
}
return 0;
}
#endif

View File

@ -1,193 +0,0 @@
/**
@file ConnectionSTREAM.h
@author Lime Microsystems
@brief Implementation of STREAM board connection.
*/
#pragma once
#include <ConnectionRegistry.h>
#include <IConnection.h>
#include <ILimeSDRStreaming.h>
#include <vector>
#include <set>
#include <string>
#include <atomic>
#include <memory>
#include <thread>
#include "fifo.h"
#include <ciso646>
#define __unix__
#include "windows.h"
#ifndef __unix__
#include "windows.h"
#include "CyAPI.h"
#else
#include <libusb-1.0/libusb.h>
#include <mutex>
#include <condition_variable>
#include <chrono>
#endif
namespace lime
{
#define USB_MAX_CONTEXTS 64 //maximum number of contexts for asynchronous transfers
/** @brief Wrapper class for holding USB asynchronous transfers contexts
*/
class USBTransferContext
{
public:
USBTransferContext() : used(false)
{
id = idCounter++;
#ifndef __unix__
inOvLap = new OVERLAPPED;
memset(inOvLap, 0, sizeof(OVERLAPPED));
inOvLap->hEvent = CreateEvent(NULL, false, false, NULL);
context = NULL;
EndPt = nullptr;
#else
transfer = libusb_alloc_transfer(0);
bytesXfered = 0;
bytesExpected = 0;
done = 0;
#endif
}
~USBTransferContext()
{
#ifndef __unix__
CloseHandle(inOvLap->hEvent);
delete inOvLap;
#else
libusb_free_transfer(transfer);
#endif
}
bool reset()
{
if(used)
return false;
#ifndef __unix__
CloseHandle(inOvLap->hEvent);
memset(inOvLap, 0, sizeof(OVERLAPPED));
inOvLap->hEvent = CreateEvent(NULL, false, false, NULL);
#endif
return true;
}
bool used;
int id;
static int idCounter;
#ifndef __unix__
PUCHAR context;
CCyUSBEndPoint* EndPt;
OVERLAPPED* inOvLap;
#else
libusb_transfer* transfer;
long bytesXfered;
long bytesExpected;
std::atomic<bool> done;
std::mutex transferLock;
std::condition_variable cv;
#endif
};
class ConnectionSTREAM : public ILimeSDRStreaming
{
public:
ConnectionSTREAM(void* arg, const std::string &vidpid, const std::string &serial, const unsigned index);
~ConnectionSTREAM(void);
void VersionCheck(void);
int Open(const std::string &vidpid, const std::string &serial, const unsigned index);
void Close();
bool IsOpen();
int GetOpenedIndex();
virtual int Write(const unsigned char* buffer, int length, int timeout_ms = 100) override;
virtual int Read(unsigned char* buffer, int length, int timeout_ms = 100) override;
//hooks to update FPGA plls when baseband interface data rate is changed
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate) override;
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase) override;
virtual int ProgramWrite(const char *buffer, const size_t length, const int programmingMode, const int device, ProgrammingCallback callback) override;
int ProgramUpdate(const bool download, ProgrammingCallback callback);
int ReadRawStreamData(char* buffer, unsigned length, int epIndex, int timeout_ms = 100)override;
protected:
virtual void ReceivePacketsLoop(Streamer* args) override;
virtual void TransmitPacketsLoop(Streamer* args) override;
int SendData(const char* buffer, int length, int epIndex = 0, int timeout = 100)override;
int ReceiveData(char* buffer, int length, int epIndex = 0, int timeout = 100)override;
virtual int BeginDataReading(char* buffer, uint32_t length, const uint8_t streamBulkInAddr = 0x81);
virtual int WaitForReading(int contextHandle, unsigned int timeout_ms);
virtual int FinishDataReading(char* buffer, uint32_t length, int contextHandle);
virtual void AbortReading(int ep);
virtual int BeginDataSending(const char* buffer, uint32_t length, const uint8_t streamBulkOutAddr = 0x01);
virtual int WaitForSending(int contextHandle, uint32_t timeout_ms);
virtual int FinishDataSending(const char* buffer, uint32_t length, int contextHandle);
virtual void AbortSending(int ep);
int ResetStreamBuffers() override;
eConnectionType GetType(void) {return USB_PORT;}
double DetectRefClk(void);
USBTransferContext contexts[USB_MAX_CONTEXTS];
USBTransferContext contextsToSend[USB_MAX_CONTEXTS];
bool isConnected;
#ifndef __unix__
static const int MAX_EP_CNT = 16;
CCyFX3Device* USBDevicePrimary;
//control endpoints
CCyControlEndPoint* InCtrlEndPt3;
CCyControlEndPoint* OutCtrlEndPt3;
//end points for samples reading and writing
CCyUSBEndPoint* InEndPt[MAX_EP_CNT];
CCyUSBEndPoint* OutEndPt[MAX_EP_CNT];
CCyUSBEndPoint* InCtrlBulkEndPt;
CCyUSBEndPoint* OutCtrlBulkEndPt;
#else
libusb_device_handle* dev_handle; //a device handle
libusb_context* ctx; //a libusb session
int read_firmware_image(unsigned char *buf, int len);
int fx3_usbboot_download(unsigned char *buf, int len);
int ram_write(unsigned char *buf, unsigned int ramAddress, int len);
#endif
static const uint8_t ctrlBulkOutAddr;
static const uint8_t ctrlBulkInAddr;
static const std::set<uint8_t> commandsToBulkCtrlHw1;
static const std::set<uint8_t> commandsToBulkCtrlHw2;
std::set<uint8_t> commandsToBulkCtrl;
bool bulkCtrlInProgress;
bool bulkCtrlAvailable;
std::mutex mExtraUsbMutex;
};
class ConnectionSTREAMEntry : public ConnectionRegistryEntry
{
public:
ConnectionSTREAMEntry(void);
ConnectionSTREAMEntry(const std::string entryName);
virtual ~ConnectionSTREAMEntry(void);
virtual std::vector<ConnectionHandle> enumerate(const ConnectionHandle& hint);
virtual IConnection* make(const ConnectionHandle& handle);
protected:
#ifndef __unix__
std::string DeviceName(unsigned int index);
void *ctx; //not used, just for mirroring unix
#else
libusb_context* ctx; //a libusb session
std::thread mUSBProcessingThread;
void handle_libusb_events();
std::atomic<bool> mProcessUSBEvents;
#endif
};
}

View File

@ -1,201 +0,0 @@
/**
@file ConnectionSTREAMEntry.cpp
@author Lime Microsystems
@brief Implementation of STREAM board connection.
*/
#include "ConnectionSTREAM.h"
#include "Logger.h"
using namespace lime;
#ifdef __unix__
void ConnectionSTREAMEntry::handle_libusb_events()
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 250000;
while(mProcessUSBEvents.load() == true)
{
int r = libusb_handle_events_timeout_completed(ctx, &tv, NULL);
if(r != 0) lime::error("error libusb_handle_events %s", libusb_strerror(libusb_error(r)));
}
}
#endif // __UNIX__
//! make a static-initialized entry in the registry
void __loadConnectionSTREAMEntry(void) //TODO fixme replace with LoadLibrary/dlopen
{
static ConnectionSTREAMEntry STREAMEntry;
}
int USBTransferContext::idCounter = 0;
ConnectionSTREAMEntry::ConnectionSTREAMEntry(void):
ConnectionRegistryEntry("STREAM")
{
#ifdef __unix__
int r = libusb_init(&ctx); //initialize the library for the session we just declared
if(r < 0)
lime::error("Init Error %i", r); //there was an error
libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation
mProcessUSBEvents.store(true);
mUSBProcessingThread = std::thread(&ConnectionSTREAMEntry::handle_libusb_events, this);
#endif
}
ConnectionSTREAMEntry::ConnectionSTREAMEntry(const std::string entryName):
ConnectionRegistryEntry(entryName)
{
#ifdef __unix__
int r = libusb_init(&ctx); //initialize the library for the session we just declared
if(r < 0)
lime::error("Init Error %i", r); //there was an error
libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation
mProcessUSBEvents.store(true);
mUSBProcessingThread = std::thread(&ConnectionSTREAMEntry::handle_libusb_events, this);
#endif
}
ConnectionSTREAMEntry::~ConnectionSTREAMEntry(void)
{
#ifdef __unix__
mProcessUSBEvents.store(false);
mUSBProcessingThread.join();
libusb_exit(ctx);
#endif
}
#ifndef __unix__
/** @return name of usb device as string.
@param index device index in list
*/
std::string ConnectionSTREAMEntry::DeviceName(unsigned int index)
{
std::string name;
char tempName[USB_STRING_MAXLEN];
CCyUSBDevice device;
if (index >= device.DeviceCount())
return "";
for (int i = 0; i < USB_STRING_MAXLEN; ++i)
tempName[i] = device.DeviceName[i];
if (device.bSuperSpeed == true)
name = "USB 3.0";
else if (device.bHighSpeed == true)
name = "USB 2.0";
else
name = "USB";
name += " (";
name += tempName;
name += ")";
return name;
}
#endif
std::vector<ConnectionHandle> ConnectionSTREAMEntry::enumerate(const ConnectionHandle &hint)
{
std::vector<ConnectionHandle> handles;
#ifndef __unix__
CCyUSBDevice device;
if (device.DeviceCount())
{
for (int i = 0; i<device.DeviceCount(); ++i)
{
if (hint.index >= 0 && hint.index != i)
continue;
if (device.IsOpen())
device.Close();
device.Open(i);
ConnectionHandle handle;
handle.media = "USB";
handle.name = DeviceName(i);
handle.index = i;
std::wstring ws(device.SerialNumber);
handle.serial = std::string(ws.begin(),ws.end());
if (hint.serial.empty() or hint.serial == handle.serial)
{
handles.push_back(handle); //filter on serial
}
device.Close();
}
}
#else
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
int usbDeviceCount = libusb_get_device_list(ctx, &devs);
if (usbDeviceCount < 0) {
lime::error("failed to get libusb device list: %s", libusb_strerror(libusb_error(usbDeviceCount)));
return handles;
}
for(int i=0; i<usbDeviceCount; ++i)
{
libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(devs[i], &desc);
if(r<0)
lime::error("failed to get device description");
int pid = desc.idProduct;
int vid = desc.idVendor;
if(vid == 1204 && pid == 34323)
{
ConnectionHandle handle;
handle.media = "USB";
handle.name = "DigiGreen";
handle.addr = std::to_string(int(pid))+":"+std::to_string(int(vid));
handles.push_back(handle);
}
else if((vid == 1204 && pid == 241) || (vid == 1204 && pid == 243) || (vid == 7504 && pid == 24840))
{
libusb_device_handle *tempDev_handle(nullptr);
if(libusb_open(devs[i], &tempDev_handle) != 0 || tempDev_handle == nullptr)
continue;
ConnectionHandle handle;
//check operating speed
int speed = libusb_get_device_speed(devs[i]);
if(speed == LIBUSB_SPEED_HIGH)
handle.media = "USB 2.0";
else if(speed == LIBUSB_SPEED_SUPER)
handle.media = "USB 3.0";
else
handle.media = "USB";
//read device name
char data[255];
r = libusb_get_string_descriptor_ascii(tempDev_handle, LIBUSB_CLASS_COMM, (unsigned char*)data, sizeof(data));
if(r > 0) handle.name = std::string(data, size_t(r));
r = std::sprintf(data, "%.4x:%.4x", int(vid), int(pid));
if (r > 0) handle.addr = std::string(data, size_t(r));
if (desc.iSerialNumber > 0)
{
r = libusb_get_string_descriptor_ascii(tempDev_handle,desc.iSerialNumber,(unsigned char*)data, sizeof(data));
if(r<0)
lime::error("failed to get serial number");
else
handle.serial = std::string(data, size_t(r));
}
libusb_close(tempDev_handle);
//add handle conditionally, filter by serial number
if (hint.serial.empty() or hint.serial == handle.serial)
{
handles.push_back(handle);
}
}
}
libusb_free_device_list(devs, 1);
#endif
return handles;
}
IConnection *ConnectionSTREAMEntry::make(const ConnectionHandle &handle)
{
return new ConnectionSTREAM(ctx, handle.addr, handle.serial, handle.index);
}

View File

@ -1,184 +0,0 @@
/**
@file ConnectionSTREAMImages.cpp
@author Lime Microsystems
@brief Image updating and version checking
*/
#include "ConnectionSTREAM.h"
#include "ErrorReporting.h"
#include "SystemResources.h"
#include "LMS64CProtocol.h"
#include "LMSBoards.h"
#include "Logger.h"
#include <fstream>
#include <ciso646>
using namespace lime;
/*!
* The entry structure that describes a board revision and its fw/gw images
*/
struct ConnectionSTREAMImageEntry
{
eLMS_DEV dev;
int hw_rev;
int fw_ver;
const char *fw_img;
int gw_ver;
int gw_rev;
const char *gw_rbf;
};
/*!
* Lookup the board information given hardware type and revision.
* Edit each entry for supported hardware and image updates.
*/
static const ConnectionSTREAMImageEntry &lookupImageEntry(const LMS64CProtocol::LMSinfo &info)
{
static const std::vector<ConnectionSTREAMImageEntry> imageEntries = {
ConnectionSTREAMImageEntry({LMS_DEV_UNKNOWN, -1, -1, "Unknown-USB.img", -1, -1, "Unknown-USB.rbf"}),
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 4, 3, "LimeSDR-USB_HW_1.3_r3.0.img", 2, 8, "LimeSDR-USB_HW_1.4_r2.8.rbf"}),
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 3, 3, "LimeSDR-USB_HW_1.3_r3.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 2, 3, "LimeSDR-USB_HW_1.2_r3.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
ConnectionSTREAMImageEntry({LMS_DEV_LIMESDR, 1, 7, "LimeSDR-USB_HW_1.1_r7.0.img", 1, 20, "LimeSDR-USB_HW_1.1_r1.20.rbf"}),
ConnectionSTREAMImageEntry({LMS_DEV_STREAM, 3, 8, "STREAM-USB_HW_1.1_r8.0.img", 1, 2, "STREAM-USB_HW_1.3_r1.2.rbf"})};
for(const auto &iter : imageEntries)
{
if (info.device == iter.dev and info.hardware == iter.hw_rev)
{
return iter;
}
}
return imageEntries.front(); //the -1 unknown entry
}
void ConnectionSTREAM::VersionCheck(void)
{
const auto info = this->GetInfo();
const auto &entry = lookupImageEntry(info);
//an entry match was not found
if (entry.dev == LMS_DEV_UNKNOWN)
{
lime::error("Unsupported hardware connected: %s[HW=%d]", GetDeviceName(info.device), info.hardware);
return;
}
//check and warn about firmware mismatch problems
if (info.firmware != entry.fw_ver) lime::warning(
"Firmware version mismatch!\n"
" Expected firmware version %d, but found version %d\n"
" Follow the FW and FPGA upgrade instructions:\n"
" http://wiki.myriadrf.org/Lime_Suite#Flashing_images\n"
" Or run update on the command line: LimeUtil --update\n",
entry.fw_ver, info.firmware);
//check and warn about gateware mismatch problems
const auto fpgaInfo = this->GetFPGAInfo();
if (fpgaInfo.gatewareVersion != entry.gw_ver
|| fpgaInfo.gatewareRevision != entry.gw_rev) lime::warning(
"Gateware version mismatch!\n"
" Expected gateware version %d, revision %d\n"
" But found version %d, revision %d\n"
" Follow the FW and FPGA upgrade instructions:\n"
" http://wiki.myriadrf.org/Lime_Suite#Flashing_images\n"
" Or run update on the command line: LimeUtil --update\n",
entry.gw_ver, entry.gw_rev, fpgaInfo.gatewareVersion, fpgaInfo.gatewareRevision);
}
static bool programmingCallbackStream(
int bsent, int btotal, const char* progressMsg,
const std::string &image,
IConnection::ProgrammingCallback callback)
{
const auto msg = std::string(progressMsg) + " (" + image + ")";
return callback(bsent, btotal, msg.c_str());
}
int ConnectionSTREAM::ProgramUpdate(const bool download, IConnection::ProgrammingCallback callback)
{
const auto info = this->GetInfo();
const auto &entry = lookupImageEntry(info);
//an entry match was not found
if (entry.dev == LMS_DEV_UNKNOWN)
{
return lime::ReportError("Unsupported hardware connected: %s[HW=%d]", GetDeviceName(info.device), info.hardware);
}
//download images when missing
if (download)
{
const std::vector<std::string> images = {entry.fw_img, entry.gw_rbf};
for (const auto &image : images)
{
if (not lime::locateImageResource(image).empty()) continue;
const std::string msg("Downloading: " + image);
if (callback) callback(0, 1, msg.c_str());
int ret = lime::downloadImageResource(image);
if (ret != 0) return ret; //error set by download call
if (callback) callback(1, 1, "Done!");
}
}
//load firmware into flash
{
//open file
std::ifstream file;
const auto path = lime::locateImageResource(entry.fw_img);
file.open(path.c_str(), std::ios::in | std::ios::binary);
if (not file.good()) return lime::ReportError("Error opening %s", path.c_str());
//read file
std::streampos fileSize;
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> progData(fileSize, 0);
file.read(progData.data(), fileSize);
int device = LMS64CProtocol::FX3; //FX3
int progMode = 2; //Firmware to FLASH
using namespace std::placeholders;
const auto cb = std::bind(&programmingCallbackStream, _1, _2, _3, path, callback);
auto status = this->ProgramWrite(progData.data(), progData.size(), progMode, device, cb);
if (status != 0) return status;
}
//load gateware into flash
{
//open file
std::ifstream file;
const auto path = lime::locateImageResource(entry.gw_rbf);
file.open(path.c_str(), std::ios::in | std::ios::binary);
if (not file.good()) return lime::ReportError("Error opening %s", path.c_str());
//read file
std::streampos fileSize;
file.seekg(0, std::ios::end);
fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> progData(fileSize, 0);
file.read(progData.data(), fileSize);
int device = LMS64CProtocol::FPGA; //Altera FPGA
int progMode = 1; //Bitstream to FLASH
using namespace std::placeholders;
const auto cb = std::bind(&programmingCallbackStream, _1, _2, _3, path, callback);
auto status = this->ProgramWrite(progData.data(), progData.size(), progMode, device, cb);
if (status != 0) return status;
}
//Reset FX3, FPGA should be reloaded on boot
{
int device = LMS64CProtocol::FX3; //FX3
auto status = this->ProgramWrite(nullptr, 0, 0, device, nullptr);
if (status != 0) return status;
}
return 0;
}

View File

@ -1,541 +0,0 @@
/**
@file ConnectionSTREAMing.cpp
@author Lime Microsystems
@brief Implementation of STREAM board connection (streaming API)
*/
#include "ConnectionSTREAM.h"
#include "fifo.h"
#include <LMS7002M.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <algorithm>
#include <complex>
#include <ciso646>
#include <FPGA_common.h>
#include "ErrorReporting.h"
#include "Logger.h"
using namespace lime;
using namespace std;
/** @brief Configures FPGA PLLs to LimeLight interface frequency
*/
int ConnectionSTREAM::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz, const double txPhase, const double rxPhase)
{
lime::fpga::FPGA_PLL_clock clocks[2];
if (channel == 2)
{
clocks[0].index = 0;
clocks[0].outFrequency = rxRate_Hz;
clocks[1].index = 1;
clocks[1].outFrequency = txRate_Hz;
return lime::fpga::SetPllFrequency(this, 4, 30.72e6, clocks, 2);
}
const float txInterfaceClk = 2 * txRate_Hz;
const float rxInterfaceClk = 2 * rxRate_Hz;
mExpectedSampleRate = rxRate_Hz;
const int pll_ind = (channel == 1) ? 2 : 0;
clocks[0].index = 0;
clocks[0].outFrequency = rxInterfaceClk;
clocks[1].index = 1;
clocks[1].outFrequency = rxInterfaceClk;
clocks[1].phaseShift_deg = rxPhase;
if (lime::fpga::SetPllFrequency(this, pll_ind+1, rxInterfaceClk, clocks, 2)!=0)
return -1;
clocks[0].index = 0;
clocks[0].outFrequency = txInterfaceClk;
clocks[1].index = 1;
clocks[1].outFrequency = txInterfaceClk;
clocks[1].phaseShift_deg = txPhase;
if (lime::fpga::SetPllFrequency(this, pll_ind, txInterfaceClk, clocks, 2)!=0)
return -1;
return 0;
}
/** @brief Configures FPGA PLLs to LimeLight interface frequency
*/
int ConnectionSTREAM::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz)
{
const float txInterfaceClk = 2 * txRate_Hz;
const float rxInterfaceClk = 2 * rxRate_Hz;
const int pll_ind = (channel == 1) ? 2 : 0;
int status = 0;
uint32_t reg20;
const double rxPhC1[] = { 91.08, 89.46 };
const double rxPhC2[] = { -1 / 6e6, 1.24e-6 };
const double txPhC1[] = { 89.75, 89.61 };
const double txPhC2[] = { -3.0e-7, 2.71e-7 };
const std::vector<uint32_t> spiAddr = { 0x021, 0x022, 0x023, 0x024, 0x027, 0x02A,
0x400, 0x40C, 0x40B, 0x400, 0x40B, 0x400};
const int bakRegCnt = spiAddr.size() - 4;
auto info = GetDeviceInfo();
bool phaseSearch = false;
if (!(mStreamers.size() > channel && (mStreamers[channel]->rxRunning || mStreamers[channel]->txRunning)))
if (this->chipVersion == 0x3841 && stoi(info.gatewareRevision) >= 7 && stoi(info.gatewareVersion) >= 2) //0x3840 LMS7002Mr2, 0x3841 LMS7002Mr3
if(rxInterfaceClk >= 5e6 || txInterfaceClk >= 5e6)
phaseSearch = true;
mExpectedSampleRate = rxRate_Hz;
std::vector<uint32_t> dataWr;
std::vector<uint32_t> dataRd;
if (phaseSearch)
{
dataWr.resize(spiAddr.size());
dataRd.resize(spiAddr.size());
//backup registers
dataWr[0] = (uint32_t(0x0020) << 16);
ReadLMS7002MSPI(dataWr.data(), &reg20, 1, channel);
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //msbit 1=SPI write
WriteLMS7002MSPI(dataWr.data(), 1, channel);
for (int i = 0; i < bakRegCnt; ++i)
dataWr[i] = (spiAddr[i] << 16);
ReadLMS7002MSPI(dataWr.data(),dataRd.data(), bakRegCnt, channel);
}
if(rxInterfaceClk >= 5e6)
{
if (phaseSearch)
{
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4, 0xE4E4, 0x0086,
0x028D, 0x00FF, 0x5555, 0x02CD, 0xAAAA, 0x02ED};
//Load test config
const int setRegCnt = spiData.size();
for (int i = 0; i < setRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
WriteLMS7002MSPI(dataWr.data(), setRegCnt, channel);
}
lime::fpga::FPGA_PLL_clock clocks[2];
clocks[0].index = 0;
clocks[0].outFrequency = rxInterfaceClk;
clocks[1].index = 1;
clocks[1].outFrequency = rxInterfaceClk;
if (this->chipVersion == 0x3841)
clocks[1].phaseShift_deg = rxPhC1[1] + rxPhC2[1] * rxInterfaceClk;
else
clocks[1].phaseShift_deg = rxPhC1[0] + rxPhC2[0] * rxInterfaceClk;
if (phaseSearch)
clocks[1].findPhase = true;
status = lime::fpga::SetPllFrequency(this, pll_ind+1, rxInterfaceClk, clocks, 2);
}
else
status = lime::fpga::SetDirectClocking(this, pll_ind+1, rxInterfaceClk, 90);
if(txInterfaceClk >= 5e6)
{
if (phaseSearch)
{
const std::vector<uint32_t> spiData = {0x0E9F, 0x07FF, 0x5550, 0xE4E4, 0xE4E4, 0x0484};
WriteRegister(0x000A, 0x0000);
//Load test config
const int setRegCnt = spiData.size();
for (int i = 0; i < setRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
WriteLMS7002MSPI(dataWr.data(), setRegCnt, channel);
}
lime::fpga::FPGA_PLL_clock clocks[2];
clocks[0].index = 0;
clocks[0].outFrequency = txInterfaceClk;
clocks[0].phaseShift_deg = 0;
clocks[1].index = 1;
clocks[1].outFrequency = txInterfaceClk;
if (this->chipVersion == 0x3841)
clocks[1].phaseShift_deg = txPhC1[1] + txPhC2[1] * txInterfaceClk;
else
clocks[1].phaseShift_deg = txPhC1[0] + txPhC2[0] * txInterfaceClk;
if (phaseSearch)
{
clocks[1].findPhase = true;
WriteRegister(0x000A, 0x0200);
}
status = lime::fpga::SetPllFrequency(this, pll_ind, txInterfaceClk, clocks, 2);
}
else
status = lime::fpga::SetDirectClocking(this, pll_ind, txInterfaceClk, 90);
if (phaseSearch)
{
//Restore registers
for (int i = 0; i < bakRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | dataRd[i]; //msbit 1=SPI write
WriteLMS7002MSPI(dataWr.data(), bakRegCnt, channel);
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | reg20; //msbit 1=SPI write
WriteLMS7002MSPI(dataWr.data(), 1, channel);
WriteRegister(0x000A, 0);
}
return status;
}
int ConnectionSTREAM::ResetStreamBuffers()
{
//USB FIFO reset
LMS64CProtocol::GenericPacket ctrPkt;
ctrPkt.cmd = CMD_USB_FIFO_RST;
ctrPkt.outBuffer.push_back(0x00);
return TransferPacket(ctrPkt);
}
int ConnectionSTREAM::ReadRawStreamData(char* buffer, unsigned length, int epIndex, int timeout_ms)
{
const unsigned char ep = 0x81;
fpga::StopStreaming(this, epIndex);
ResetStreamBuffers();
WriteRegister(0x0008, 0x0100 | 0x2);
WriteRegister(0x0007, 1);
fpga::StartStreaming(this, epIndex);
int totalBytesReceived = ReceiveData(buffer,length, epIndex, timeout_ms);
fpga::StopStreaming(this, epIndex);
AbortReading(ep);
return totalBytesReceived;
}
/** @brief Function dedicated for receiving data samples from board
@param stream a pointer to an active receiver stream
*/
void ConnectionSTREAM::ReceivePacketsLoop(Streamer* stream)
{
//at this point FPGA has to be already configured to output samples
const uint8_t chCount = stream->mRxStreams.size();
const auto link = stream->mRxStreams[0]->config.linkFormat;
const uint32_t samplesInPacket = (link == StreamConfig::STREAM_12_BIT_COMPRESSED ? 1360 : 1020)/chCount;
const unsigned char ep = 0x81;
const int chipID = stream->mChipID;
float latency=0;
for (int i = 0; i < chCount; i++)
latency += stream->mRxStreams[i]->config.performanceLatency/chCount;
const unsigned tmp_cnt = (latency * 6)+0.5;
const uint8_t packetsToBatch = (1<<tmp_cnt);
const uint32_t bufferSize = packetsToBatch*sizeof(FPGA_DataPacket);
const uint8_t buffersCount = 16;
vector<int> handles(buffersCount, 0);
vector<char>buffers(buffersCount*bufferSize, 0);
vector<StreamChannel::Frame> chFrames;
try
{
chFrames.resize(chCount);
}
catch (const std::bad_alloc &ex)
{
ReportError("Error allocating Rx buffers, not enough memory");
return;
}
int activeTransfers = 0;
for (int i = 0; i<buffersCount; ++i)
{
handles[i] = this->BeginDataReading(&buffers[i*bufferSize], bufferSize, ep);
++activeTransfers;
}
int bi = 0;
unsigned long totalBytesReceived = 0; //for data rate calculation
auto t1 = chrono::high_resolution_clock::now();
auto t2 = t1;
std::mutex txFlagsLock;
condition_variable resetTxFlags;
//worker thread for reseting late Tx packet flags
std::thread txReset([](ILimeSDRStreaming* port,
atomic<bool> *terminate,
mutex *spiLock,
condition_variable *doWork)
{
uint32_t reg9;
port->ReadRegister(0x0009, reg9);
const uint32_t addr[] = {0x0009, 0x0009};
const uint32_t data[] = {reg9 | (5 << 1), reg9 & ~(5 << 1)};
while (not terminate->load())
{
std::unique_lock<std::mutex> lck(*spiLock);
doWork->wait(lck);
port->WriteRegisters(addr, data, 2);
}
}, this, &stream->terminateRx, &txFlagsLock, &resetTxFlags);
int resetFlagsDelay = 128;
uint64_t prevTs = 0;
while (stream->terminateRx.load() == false)
{
if(stream->generateData.load())
{
if(activeTransfers == 0) //stop FPGA when last transfer completes
fpga::StopStreaming(this, chipID);
stream->safeToConfigInterface.notify_all(); //notify that it's safe to change chip config
const int batchSize = (this->mExpectedSampleRate/chFrames[0].samplesCount)/10;
IStreamChannel::Metadata meta;
for(int i=0; i<batchSize; ++i)
{
for(int ch=0; ch<chCount; ++ch)
{
meta.timestamp = chFrames[ch].timestamp;
for(int j=0; j<chFrames[ch].samplesCount; ++j)
{
chFrames[ch].samples[j].i = 0;
chFrames[ch].samples[j].q = 0;
}
uint32_t samplesPushed = stream->mRxStreams[ch]->Write((const void*)chFrames[ch].samples, chFrames[ch].samplesCount, &meta);
if(samplesPushed != chFrames[ch].samplesCount)
lime::warning("Rx samples pushed %i/%i", samplesPushed, chFrames[ch].samplesCount);
}
}
this_thread::sleep_for(chrono::milliseconds(100));
}
int32_t bytesReceived = 0;
if(handles[bi] >= 0)
{
if (this->WaitForReading(handles[bi], 1000) == true)
bytesReceived = this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
--activeTransfers;
totalBytesReceived += bytesReceived;
if (bytesReceived != int32_t(bufferSize)) //data should come in full sized packets
for(auto value: stream->mRxStreams)
value->underflow++;
}
bool txLate=false;
for (uint8_t pktIndex = 0; pktIndex < bytesReceived / sizeof(FPGA_DataPacket); ++pktIndex)
{
const FPGA_DataPacket* pkt = (FPGA_DataPacket*)&buffers[bi*bufferSize];
const uint8_t byte0 = pkt[pktIndex].reserved[0];
if ((byte0 & (1 << 3)) != 0 && !txLate) //report only once per batch
{
txLate = true;
if(resetFlagsDelay > 0)
--resetFlagsDelay;
else
{
lime::info("L %llu", (unsigned long long)pkt[pktIndex].counter);
resetTxFlags.notify_one();
resetFlagsDelay = packetsToBatch*buffersCount;
stream->txLastLateTime.store(pkt[pktIndex].counter);
for(auto value: stream->mTxStreams)
value->pktLost++;
}
}
uint8_t* pktStart = (uint8_t*)pkt[pktIndex].data;
if(pkt[pktIndex].counter - prevTs != samplesInPacket && pkt[pktIndex].counter != prevTs)
{
int packetLoss = ((pkt[pktIndex].counter - prevTs)/samplesInPacket)-1;
#ifndef NDEBUG
printf("\tRx pktLoss: ts diff: %li pktLoss: %i\n", pkt[pktIndex].counter - prevTs, packetLoss);
#endif
for(auto value: stream->mRxStreams)
value->pktLost += packetLoss;
}
prevTs = pkt[pktIndex].counter;
stream->rxLastTimestamp.store(prevTs);
//parse samples
vector<complex16_t*> dest(chCount);
for(uint8_t c=0; c<chCount; ++c)
dest[c] = (chFrames[c].samples);
size_t samplesCount = 0;
fpga::FPGAPacketPayload2Samples(pktStart, 4080, chCount, link, dest.data(), &samplesCount);
for(int ch=0; ch<chCount; ++ch)
{
IStreamChannel::Metadata meta;
meta.timestamp = pkt[pktIndex].counter;
meta.flags = RingFIFO::OVERWRITE_OLD;
uint32_t samplesPushed = stream->mRxStreams[ch]->Write((const void*)chFrames[ch].samples, samplesCount, &meta, 100);
if(samplesPushed != samplesCount)
stream->mRxStreams[ch]->overflow++;
}
}
// Re-submit this request to keep the queue full
if(not stream->generateData.load())
{
if(activeTransfers == 0) //reactivate FPGA and USB transfers
fpga::StartStreaming(this, chipID);
for(int i=0; i<buffersCount-activeTransfers; ++i)
{
handles[bi] = this->BeginDataReading(&buffers[bi*bufferSize], bufferSize, ep);
bi = (bi + 1) & (buffersCount-1);
++activeTransfers;
}
}
else
{
handles[bi] = -1;
bi = (bi + 1) & (buffersCount-1);
}
t2 = chrono::high_resolution_clock::now();
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (timePeriod >= 1000)
{
t1 = t2;
//total number of bytes sent per second
double dataRate = 1000.0*totalBytesReceived / timePeriod;
#ifndef NDEBUG
printf("Rx: %.3f MB/s\n", dataRate / 1000000.0);
#endif
totalBytesReceived = 0;
stream->rxDataRate_Bps.store((uint32_t)dataRate);
}
}
AbortReading(ep);
for (int j = 0; j<buffersCount; j++)
{
if(handles[bi] >= 0)
{
this->WaitForReading(handles[bi], 1000);
this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
}
bi = (bi + 1) & (buffersCount-1);
}
resetTxFlags.notify_one();
txReset.join();
stream->rxDataRate_Bps.store(0);
}
/** @brief Functions dedicated for transmitting packets to board
@param stream an active transmit stream
*/
void ConnectionSTREAM::TransmitPacketsLoop(Streamer* stream)
{
//at this point FPGA has to be already configured to output samples
const uint8_t maxChannelCount = 2;
const uint8_t chCount = stream->mTxStreams.size();
const auto link = stream->mTxStreams[0]->config.linkFormat;
const unsigned char ep = 0x01;
double latency=0;
for (int i = 0; i < chCount; i++)
latency += stream->mTxStreams[i]->config.performanceLatency/chCount;
const unsigned tmp_cnt = (latency * 6)+0.5;
const uint8_t buffersCount = 16; // must be power of 2
const uint8_t packetsToBatch = (1<<tmp_cnt); //packets in single USB transfer
const uint32_t bufferSize = packetsToBatch*4096;
const uint32_t popTimeout_ms = 100;
const int maxSamplesBatch = (link==StreamConfig::STREAM_12_BIT_COMPRESSED?1360:1020)/chCount;
vector<int> handles(buffersCount, 0);
vector<bool> bufferUsed(buffersCount, 0);
vector<complex16_t> samples[maxChannelCount];
vector<char> buffers;
try
{
for(int i=0; i<chCount; ++i)
samples[i].resize(maxSamplesBatch);
buffers.resize(buffersCount*bufferSize, 0);
}
catch (const std::bad_alloc& ex) //not enough memory for buffers
{
return lime::error("Error allocating Tx buffers, not enough memory");
}
long totalBytesSent = 0;
auto t1 = chrono::high_resolution_clock::now();
auto t2 = t1;
uint8_t bi = 0; //buffer index
while (stream->terminateTx.load() != true)
{
if (bufferUsed[bi])
{
unsigned bytesSent = 0;
if (this->WaitForSending(handles[bi], 1000) == true) {
bytesSent = this->FinishDataSending(&buffers[bi*bufferSize], bufferSize, handles[bi]);
}
if (bytesSent != bufferSize) {
for (auto value : stream->mTxStreams) {
value->overflow++;
}
}
else {
totalBytesSent += bytesSent;
}
bufferUsed[bi] = false;
}
int i=0;
while(i<packetsToBatch && stream->terminateTx.load() != true)
{
IStreamChannel::Metadata meta;
FPGA_DataPacket* pkt = reinterpret_cast<FPGA_DataPacket*>(&buffers[bi*bufferSize]);
bool badSamples = false;
for(int ch=0; ch<chCount; ++ch)
{
int samplesPopped = stream->mTxStreams[ch]->Read(samples[ch].data(), maxSamplesBatch, &meta, popTimeout_ms);
if (samplesPopped != maxSamplesBatch)
{
badSamples = true;
stream->mTxStreams[ch]->underflow++;
stream->txDataRate_Bps.store(0);
#ifndef NDEBUG
printf("popping from TX, samples popped %i/%i\n", samplesPopped, maxSamplesBatch);
#endif
break;
}
}
if (badSamples)
continue;
if(stream->terminateTx.load() == true) //early termination
break;
pkt[i].counter = meta.timestamp;
pkt[i].reserved[0] = 0;
//by default ignore timestamps
const int ignoreTimestamp = !(meta.flags & IStreamChannel::Metadata::SYNC_TIMESTAMP);
pkt[i].reserved[0] |= ((int)ignoreTimestamp << 4); //ignore timestamp
vector<complex16_t*> src(chCount);
for(uint8_t c=0; c<chCount; ++c)
src[c] = (samples[c].data());
uint8_t* const dataStart = (uint8_t*)pkt[i].data;
fpga::Samples2FPGAPacketPayload(src.data(), maxSamplesBatch, chCount, link, dataStart, nullptr);
++i;
}
handles[bi] = this->BeginDataSending(&buffers[bi*bufferSize], bufferSize, ep);
bufferUsed[bi] = true;
t2 = chrono::high_resolution_clock::now();
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (timePeriod >= 1000)
{
//total number of bytes sent per second
float dataRate = 1000.0*totalBytesSent / timePeriod;
stream->txDataRate_Bps.store(dataRate);
totalBytesSent = 0;
t1 = t2;
#ifndef NDEBUG
printf("Tx: %.3f MB/s\n", dataRate / 1000000.0);
#endif
}
bi = (bi + 1) & (buffersCount-1);
}
// Wait for all the queued requests to be cancelled
AbortSending(ep);
for (int j = 0; j<buffersCount; j++)
{
if (bufferUsed[bi])
{
this->WaitForSending(handles[bi], 1000);
this->FinishDataSending(&buffers[bi*bufferSize], bufferSize, handles[bi]);
}
bi = (bi + 1) & (buffersCount-1);
}
stream->txDataRate_Bps.store(0);
}

View File

@ -1,699 +0,0 @@
/**
@file Connection_uLimeSDR.cpp
@author Lime Microsystems
@brief Implementation of uLimeSDR board connection.
*/
#include "Connection_uLimeSDR.h"
#include "ErrorReporting.h"
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <FPGA_common.h>
#include <LMS7002M.h>
#include <ciso646>
using namespace std;
using namespace lime;
#define __unix__
Connection_uLimeSDR::Connection_uLimeSDR(void *arg)
{
RxLoopFunction = bind(&Connection_uLimeSDR::ReceivePacketsLoop, this, std::placeholders::_1);
TxLoopFunction = bind(&Connection_uLimeSDR::TransmitPacketsLoop, this, std::placeholders::_1);
isConnected = false;
mStreamWrEndPtAddr = 0x03;
mStreamRdEndPtAddr = 0x83;
isConnected = false;
txSize = 0;
rxSize = 0;
#ifndef __unix__
mFTHandle = NULL;
#else
dev_handle = 0;
devs = 0;
mUsbCounter = 0;
ctx = (libusb_context *)arg;
#endif
}
/** @brief Initializes port type and object necessary to communicate to usb device.
*/
Connection_uLimeSDR::Connection_uLimeSDR(void *arg, const unsigned index, const int vid, const int pid)
{
RxLoopFunction = bind(&Connection_uLimeSDR::ReceivePacketsLoop, this, std::placeholders::_1);
TxLoopFunction = bind(&Connection_uLimeSDR::TransmitPacketsLoop, this, std::placeholders::_1);
mExpectedSampleRate = 0;
isConnected = false;
mStreamWrEndPtAddr = 0x03;
mStreamRdEndPtAddr = 0x83;
isConnected = false;
txSize = 0;
rxSize = 0;
#ifndef __unix__
mFTHandle = NULL;
#else
dev_handle = 0;
devs = 0;
mUsbCounter = 0;
ctx = (libusb_context *)arg;
#endif
if (this->Open(index, vid, pid) != 0)
std::cerr << GetLastErrorMessage() << std::endl;
this->SetReferenceClockRate(52e6);
GetChipVersion();
}
/** @brief Closes connection to chip and deallocates used memory.
*/
Connection_uLimeSDR::~Connection_uLimeSDR()
{
Close();
}
#ifdef __unix__
int Connection_uLimeSDR::FT_FlushPipe(unsigned char ep)
{
int actual = 0;
unsigned char wbuffer[20]={0};
mUsbCounter++;
wbuffer[0] = (mUsbCounter)&0xFF;
wbuffer[1] = (mUsbCounter>>8)&0xFF;
wbuffer[2] = (mUsbCounter>>16)&0xFF;
wbuffer[3] = (mUsbCounter>>24)&0xFF;
wbuffer[4] = ep;
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
if (actual != 20)
return -1;
mUsbCounter++;
wbuffer[0] = (mUsbCounter)&0xFF;
wbuffer[1] = (mUsbCounter>>8)&0xFF;
wbuffer[2] = (mUsbCounter>>16)&0xFF;
wbuffer[3] = (mUsbCounter>>24)&0xFF;
wbuffer[4] = ep;
wbuffer[5] = 0x03;
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
if (actual != 20)
return -1;
return 0;
}
int Connection_uLimeSDR::FT_SetStreamPipe(unsigned char ep, size_t size)
{
int actual = 0;
unsigned char wbuffer[20]={0};
mUsbCounter++;
wbuffer[0] = (mUsbCounter)&0xFF;
wbuffer[1] = (mUsbCounter>>8)&0xFF;
wbuffer[2] = (mUsbCounter>>16)&0xFF;
wbuffer[3] = (mUsbCounter>>24)&0xFF;
wbuffer[4] = ep;
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
if (actual != 20)
return -1;
mUsbCounter++;
wbuffer[0] = (mUsbCounter)&0xFF;
wbuffer[1] = (mUsbCounter>>8)&0xFF;
wbuffer[2] = (mUsbCounter>>16)&0xFF;
wbuffer[3] = (mUsbCounter>>24)&0xFF;
wbuffer[5] = 0x02;
wbuffer[8] = (size)&0xFF;
wbuffer[9] = (size>>8)&0xFF;
wbuffer[10] = (size>>16)&0xFF;
wbuffer[11] = (size>>24)&0xFF;
libusb_bulk_transfer(dev_handle, 0x01, wbuffer, 20, &actual, 1000);
if (actual != 20)
return -1;
return 0;
}
#endif
/** @brief Tries to open connected USB device and find communication endpoints.
@return Returns 0-Success, other-EndPoints not found or device didn't connect.
*/
int Connection_uLimeSDR::Open(const unsigned index, const int vid, const int pid)
{
#ifndef __unix__
DWORD devCount;
FT_STATUS ftStatus = FT_OK;
DWORD dwNumDevices = 0;
// Open a device
ftStatus = FT_Create(0, FT_OPEN_BY_INDEX, &mFTHandle);
if (FT_FAILED(ftStatus))
{
ReportError(ENODEV, "Failed to list USB Devices");
return -1;
}
FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr);
FT_AbortPipe(mFTHandle, 0x82);
FT_AbortPipe(mFTHandle, 0x02);
FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr);
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, 0x82, 64);
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, 0x02, 64);
FT_SetPipeTimeout(mFTHandle, 0x02, 500);
FT_SetPipeTimeout(mFTHandle, 0x82, 500);
isConnected = true;
return 0;
#else
dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
if(dev_handle == nullptr)
return ReportError(ENODEV, "libusb_open failed");
libusb_reset_device(dev_handle);
if(libusb_kernel_driver_active(dev_handle, 1) == 1) //find out if kernel driver is attached
{
printf("Kernel Driver Active\n");
if(libusb_detach_kernel_driver(dev_handle, 1) == 0) //detach it
printf("Kernel Driver Detached!\n");
}
int r = libusb_claim_interface(dev_handle, 1); //claim interface 0 (the first) of device
if(r < 0)
{
printf("Cannot Claim Interface\n");
return ReportError(-1, "Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
}
r = libusb_claim_interface(dev_handle, 1); //claim interface 0 (the first) of device
if(r < 0)
{
printf("Cannot Claim Interface\n");
return ReportError(-1, "Cannot claim interface - %s", libusb_strerror(libusb_error(r)));
}
printf("Claimed Interface\n");
FT_SetStreamPipe(0x82,64);
FT_SetStreamPipe(0x02,64);
isConnected = true;
return 0;
#endif
}
/** @brief Closes communication to device.
*/
void Connection_uLimeSDR::Close()
{
#ifndef __unix__
FT_Close(mFTHandle);
#else
if(dev_handle != 0)
{
FT_FlushPipe(mStreamRdEndPtAddr);
FT_FlushPipe(0x82);
libusb_release_interface(dev_handle, 1);
libusb_close(dev_handle);
dev_handle = 0;
}
#endif
isConnected = false;
}
/** @brief Returns connection status
@return 1-connection open, 0-connection closed.
*/
bool Connection_uLimeSDR::IsOpen()
{
return isConnected;
}
#ifndef __unix__
int Connection_uLimeSDR::ReinitPipe(unsigned char ep)
{
FT_AbortPipe(mFTHandle, ep);
FT_FlushPipe(mFTHandle, ep);
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, ep, 64);
return 0;
}
#endif
/** @brief Sends given data buffer to chip through USB port.
@param buffer data buffer, must not be longer than 64 bytes.
@param length given buffer size.
@param timeout_ms timeout limit for operation in milliseconds
@return number of bytes sent.
*/
int Connection_uLimeSDR::Write(const unsigned char *buffer, const int length, int timeout_ms)
{
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
long len = 0;
if (IsOpen() == false)
return 0;
#ifndef __unix__
// Write to channel 1 ep 0x02
ULONG ulBytesWrite = 0;
FT_STATUS ftStatus = FT_OK;
OVERLAPPED vOverlapped = { 0 };
FT_InitializeOverlapped(mFTHandle, &vOverlapped);
ftStatus = FT_WritePipe(mFTHandle, 0x02, (unsigned char*)buffer, length, &ulBytesWrite, &vOverlapped);
if (ftStatus != FT_IO_PENDING)
{
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
ReinitPipe(0x02);
return -1;
}
DWORD dwRet = WaitForSingleObject(vOverlapped.hEvent, timeout_ms);
if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT)
{
if (GetOverlappedResult(mFTHandle, &vOverlapped, &ulBytesWrite, FALSE) == FALSE)
{
ReinitPipe(0x02);
ulBytesWrite = -1;
}
}
else
{
ReinitPipe(0x02);
ulBytesWrite = -1;
}
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
return ulBytesWrite;
#else
unsigned char* wbuffer = new unsigned char[length];
memcpy(wbuffer, buffer, length);
int actual = 0;
libusb_bulk_transfer(dev_handle, 0x02, wbuffer, length, &actual, timeout_ms);
len = actual;
delete[] wbuffer;
return len;
#endif
}
/** @brief Reads data coming from the chip through USB port.
@param buffer pointer to array where received data will be copied, array must be
big enough to fit received data.
@param length number of bytes to read from chip.
@param timeout_ms timeout limit for operation in milliseconds
@return number of bytes received.
*/
int Connection_uLimeSDR::Read(unsigned char *buffer, const int length, int timeout_ms)
{
std::lock_guard<std::mutex> lock(mExtraUsbMutex);
long len = length;
if(IsOpen() == false)
return 0;
#ifndef __unix__
//
// Read from channel 1 ep 0x82
//
ULONG ulBytesRead = 0;
FT_STATUS ftStatus = FT_OK;
OVERLAPPED vOverlapped = { 0 };
FT_InitializeOverlapped(mFTHandle, &vOverlapped);
ftStatus = FT_ReadPipe(mFTHandle, 0x82, buffer, length, &ulBytesRead, &vOverlapped);
if (ftStatus != FT_IO_PENDING)
{
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
ReinitPipe(0x82);
return -1;;
}
DWORD dwRet = WaitForSingleObject(vOverlapped.hEvent, timeout_ms);
if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT)
{
if (GetOverlappedResult(mFTHandle, &vOverlapped, &ulBytesRead, FALSE)==FALSE)
{
ReinitPipe(0x82);
ulBytesRead = -1;
}
}
else
{
ReinitPipe(0x82);
ulBytesRead = -1;
}
FT_ReleaseOverlapped(mFTHandle, &vOverlapped);
return ulBytesRead;
#else
int actual = 0;
libusb_bulk_transfer(dev_handle, 0x82, buffer, len, &actual, timeout_ms);
len = actual;
#endif
return len;
}
#ifdef __unix__
/** @brief Function for handling libusb callbacks
*/
static void callback_libusbtransfer(libusb_transfer *trans)
{
Connection_uLimeSDR::USBTransferContext *context = reinterpret_cast<Connection_uLimeSDR::USBTransferContext*>(trans->user_data);
std::unique_lock<std::mutex> lck(context->transferLock);
switch(trans->status)
{
case LIBUSB_TRANSFER_CANCELLED:
//printf("Transfer %i canceled\n", context->id);
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
//context->reset();
break;
case LIBUSB_TRANSFER_COMPLETED:
//if(trans->actual_length == context->bytesExpected)
{
context->bytesXfered = trans->actual_length;
context->done.store(true);
}
break;
case LIBUSB_TRANSFER_ERROR:
printf("TRANSFER ERRRO\n");
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
break;
case LIBUSB_TRANSFER_TIMED_OUT:
//printf("transfer timed out %i\n", context->id);
context->bytesXfered = trans->actual_length;
context->done.store(true);
//context->used = false;
break;
case LIBUSB_TRANSFER_OVERFLOW:
printf("transfer overflow\n");
break;
case LIBUSB_TRANSFER_STALL:
printf("transfer stalled\n");
break;
case LIBUSB_TRANSFER_NO_DEVICE:
printf("transfer no device\n");
break;
}
lck.unlock();
context->cv.notify_one();
}
#endif
/**
@brief Starts asynchronous data reading from board
@param *buffer buffer where to store received data
@param length number of bytes to read
@return handle of transfer context
*/
int Connection_uLimeSDR::BeginDataReading(char *buffer, uint32_t length)
{
int i = 0;
bool contextFound = false;
//find not used context
for(i = 0; i<USB_MAX_CONTEXTS; i++)
{
if(!contexts[i].used)
{
contextFound = true;
break;
}
}
if(!contextFound)
{
printf("No contexts left for reading data\n");
return -1;
}
contexts[i].used = true;
#ifndef __unix__
if (length != rxSize)
{
rxSize = length;
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, mStreamRdEndPtAddr, rxSize);
}
FT_InitializeOverlapped(mFTHandle, &contexts[i].inOvLap);
ULONG ulActual;
FT_STATUS ftStatus = FT_OK;
ftStatus = FT_ReadPipe(mFTHandle, mStreamRdEndPtAddr, (unsigned char*)buffer, length, &ulActual, &contexts[i].inOvLap);
if (ftStatus != FT_IO_PENDING)
return -1;
#else
if (length != rxSize)
{
rxSize = length;
FT_SetStreamPipe(mStreamRdEndPtAddr,rxSize);
}
unsigned int Timeout = 500;
libusb_transfer *tr = contexts[i].transfer;
libusb_fill_bulk_transfer(tr, dev_handle, mStreamRdEndPtAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contexts[i], Timeout);
contexts[i].done = false;
contexts[i].bytesXfered = 0;
contexts[i].bytesExpected = length;
int status = libusb_submit_transfer(tr);
if(status != 0)
{
printf("ERROR BEGIN DATA READING %s\n", libusb_error_name(status));
contexts[i].used = false;
return -1;
}
#endif
return i;
}
/**
@brief Waits for asynchronous data reception
@param contextHandle handle of which context data to wait
@param timeout_ms number of miliseconds to wait
@return 1-data received, 0-data not received
*/
int Connection_uLimeSDR::WaitForReading(int contextHandle, unsigned int timeout_ms)
{
if(contextHandle >= 0 && contexts[contextHandle].used == true)
{
#ifndef __unix__
DWORD dwRet = WaitForSingleObject(contexts[contextHandle].inOvLap.hEvent, timeout_ms);
if (dwRet == WAIT_OBJECT_0)
return 1;
#else
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
std::unique_lock<std::mutex> lck(contexts[contextHandle].transferLock);
while(contexts[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
{
//blocking not to waste CPU
contexts[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
t2 = chrono::high_resolution_clock::now();
}
return contexts[contextHandle].done.load() == true;
#endif
}
return 0;
}
/**
@brief Finishes asynchronous data reading from board
@param buffer array where to store received data
@param length number of bytes to read
@param contextHandle handle of which context to finish
@return false failure, true number of bytes received
*/
int Connection_uLimeSDR::FinishDataReading(char *buffer, uint32_t length, int contextHandle)
{
if(contextHandle >= 0 && contexts[contextHandle].used == true)
{
#ifndef __unix__
ULONG ulActualBytesTransferred;
FT_STATUS ftStatus = FT_OK;
ftStatus = FT_GetOverlappedResult(mFTHandle, &contexts[contextHandle].inOvLap, &ulActualBytesTransferred, FALSE);
if (ftStatus != FT_OK)
length = 0;
else
length = ulActualBytesTransferred;
FT_ReleaseOverlapped(mFTHandle, &contexts[contextHandle].inOvLap);
contexts[contextHandle].used = false;
return length;
#else
length = contexts[contextHandle].bytesXfered;
contexts[contextHandle].used = false;
contexts[contextHandle].reset();
return length;
#endif
}
else
return 0;
}
/**
@brief Aborts reading operations
*/
void Connection_uLimeSDR::AbortReading()
{
#ifndef __unix__
FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr);
for (int i = 0; i < USB_MAX_CONTEXTS; ++i)
{
if (contexts[i].used == true)
{
FT_ReleaseOverlapped(mFTHandle, &contexts[i].inOvLap);
contexts[i].used = false;
}
}
FT_FlushPipe(mFTHandle, mStreamRdEndPtAddr);
rxSize = 0;
#else
for(int i = 0; i<USB_MAX_CONTEXTS; ++i)
{
if(contexts[i].used)
libusb_cancel_transfer(contexts[i].transfer);
}
FT_FlushPipe(mStreamRdEndPtAddr);
rxSize = 0;
#endif
}
/**
@brief Starts asynchronous data Sending to board
@param *buffer buffer to send
@param length number of bytes to send
@return handle of transfer context
*/
int Connection_uLimeSDR::BeginDataSending(const char *buffer, uint32_t length)
{
int i = 0;
//find not used context
bool contextFound = false;
for(i = 0; i<USB_MAX_CONTEXTS; i++)
{
if(!contextsToSend[i].used)
{
contextFound = true;
break;
}
}
if(!contextFound)
return -1;
contextsToSend[i].used = true;
#ifndef __unix__
FT_STATUS ftStatus = FT_OK;
ULONG ulActualBytesSend;
if (length != txSize)
{
txSize = length;
FT_SetStreamPipe(mFTHandle, FALSE, FALSE, mStreamWrEndPtAddr, txSize);
}
FT_InitializeOverlapped(mFTHandle, &contextsToSend[i].inOvLap);
ftStatus = FT_WritePipe(mFTHandle, mStreamWrEndPtAddr, (unsigned char*)buffer, length, &ulActualBytesSend, &contextsToSend[i].inOvLap);
if (ftStatus != FT_IO_PENDING)
return -1;
#else
if (length != txSize)
{
txSize = length;
FT_SetStreamPipe(mStreamWrEndPtAddr,txSize);
}
unsigned int Timeout = 500;
libusb_transfer *tr = contextsToSend[i].transfer;
libusb_fill_bulk_transfer(tr, dev_handle, mStreamWrEndPtAddr, (unsigned char*)buffer, length, callback_libusbtransfer, &contextsToSend[i], Timeout);
contextsToSend[i].done = false;
contextsToSend[i].bytesXfered = 0;
contextsToSend[i].bytesExpected = length;
int status = libusb_submit_transfer(tr);
if(status != 0)
{
printf("ERROR BEGIN DATA SENDING %s\n", libusb_error_name(status));
contextsToSend[i].used = false;
return -1;
}
#endif
return i;
}
/**
@brief Waits for asynchronous data sending
@param contextHandle handle of which context data to wait
@param timeout_ms number of miliseconds to wait
@return 1-data received, 0-data not received
*/
int Connection_uLimeSDR::WaitForSending(int contextHandle, unsigned int timeout_ms)
{
if(contextsToSend[contextHandle].used == true)
{
#ifndef __unix__
DWORD dwRet = WaitForSingleObject(contextsToSend[contextHandle].inOvLap.hEvent, timeout_ms);
if (dwRet == WAIT_OBJECT_0)
return 1;
#else
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
std::unique_lock<std::mutex> lck(contextsToSend[contextHandle].transferLock);
while(contextsToSend[contextHandle].done.load() == false && std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() < timeout_ms)
{
//blocking not to waste CPU
contextsToSend[contextHandle].cv.wait_for(lck, chrono::milliseconds(timeout_ms));
t2 = chrono::high_resolution_clock::now();
}
return contextsToSend[contextHandle].done == true;
#endif
}
return 0;
}
/**
@brief Finishes asynchronous data sending to board
@param buffer array where to store received data
@param length number of bytes to read
@param contextHandle handle of which context to finish
@return false failure, true number of bytes sent
*/
int Connection_uLimeSDR::FinishDataSending(const char *buffer, uint32_t length, int contextHandle)
{
if(contextsToSend[contextHandle].used == true)
{
#ifndef __unix__
ULONG ulActualBytesTransferred ;
FT_STATUS ftStatus = FT_OK;
ftStatus = FT_GetOverlappedResult(mFTHandle, &contextsToSend[contextHandle].inOvLap, &ulActualBytesTransferred, FALSE);
if (ftStatus != FT_OK)
length = 0;
else
length = ulActualBytesTransferred;
FT_ReleaseOverlapped(mFTHandle, &contextsToSend[contextHandle].inOvLap);
contextsToSend[contextHandle].used = false;
return length;
#else
length = contextsToSend[contextHandle].bytesXfered;
contextsToSend[contextHandle].used = false;
contextsToSend[contextHandle].reset();
return length;
#endif
}
else
return 0;
}
/**
@brief Aborts sending operations
*/
void Connection_uLimeSDR::AbortSending()
{
#ifndef __unix__
FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr);
for (int i = 0; i < USB_MAX_CONTEXTS; ++i)
{
if (contextsToSend[i].used == true)
{
FT_ReleaseOverlapped(mFTHandle, &contextsToSend[i].inOvLap);
contextsToSend[i].used = false;
}
}
txSize = 0;
#else
for(int i = 0; i<USB_MAX_CONTEXTS; ++i)
{
if(contextsToSend[i].used)
libusb_cancel_transfer(contextsToSend[i].transfer);
}
FT_FlushPipe(mStreamWrEndPtAddr);
txSize = 0;
#endif
}

View File

@ -1,165 +0,0 @@
/**
@file Connection_uLimeSDR.h
@author Lime Microsystems
@brief Implementation of STREAM board connection.
*/
#pragma once
#include <ConnectionRegistry.h>
#include <IConnection.h>
#include <ILimeSDRStreaming.h>
#include <vector>
#include <string>
#include <atomic>
#include <memory>
#include <thread>
#include "fifo.h"
#define __unix__
#include "windows.h"
#ifndef __unix__
#include "windows.h"
#include "FTD3XXLibrary/FTD3XX.h"
#else
#include <libusb-1.0/libusb.h>
#include <mutex>
#include <condition_variable>
#include <chrono>
#endif
namespace lime{
#define USB_MAX_CONTEXTS 64 //maximum number of contexts for asynchronous transfers
class Connection_uLimeSDR : public ILimeSDRStreaming
{
public:
/** @brief Wrapper class for holding USB asynchronous transfers contexts
*/
class USBTransferContext
{
public:
USBTransferContext() : used(false)
{
id = idCounter++;
#ifndef __unix__
context = NULL;
#else
transfer = libusb_alloc_transfer(0);
bytesXfered = 0;
bytesExpected = 0;
done = 0;
#endif
}
~USBTransferContext()
{
#ifdef __unix__
libusb_free_transfer(transfer);
#endif
}
bool reset()
{
if(used)
return false;
return true;
}
bool used;
int id;
static int idCounter;
#ifndef __unix__
PUCHAR context;
OVERLAPPED inOvLap;
#else
libusb_transfer* transfer;
long bytesXfered;
long bytesExpected;
std::atomic<bool> done;
std::mutex transferLock;
std::condition_variable cv;
#endif
};
Connection_uLimeSDR(void *arg);
Connection_uLimeSDR(void *ctx, const unsigned index, const int vid = -1, const int pid = -1);
virtual ~Connection_uLimeSDR(void);
int Open(const unsigned index, const int vid, const int pid);
void Close();
bool IsOpen();
int GetOpenedIndex();
virtual int Write(const unsigned char *buffer, int length, int timeout_ms = 100) override;
virtual int Read(unsigned char *buffer, int length, int timeout_ms = 100) override;
//hooks to update FPGA plls when baseband interface data rate is changed
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase)override;
virtual int UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate) override;
int ReadRawStreamData(char* buffer, unsigned length, int epIndex, int timeout_ms = 100)override;
protected:
virtual void ReceivePacketsLoop(Streamer* args) override;
virtual void TransmitPacketsLoop(Streamer* args) override;
virtual int BeginDataReading(char* buffer, uint32_t length);
virtual int WaitForReading(int contextHandle, unsigned int timeout_ms);
virtual int FinishDataReading(char* buffer, uint32_t length, int contextHandle);
virtual void AbortReading();
virtual int BeginDataSending(const char* buffer, uint32_t length);
virtual int WaitForSending(int contextHandle, uint32_t timeout_ms);
virtual int FinishDataSending(const char* buffer, uint32_t length, int contextHandle);
virtual void AbortSending();
int ResetStreamBuffers() override;
eConnectionType GetType(void) {return USB_PORT;}
USBTransferContext contexts[USB_MAX_CONTEXTS];
USBTransferContext contextsToSend[USB_MAX_CONTEXTS];
bool isConnected;
int mCtrlWrEndPtAddr;
int mCtrlRdEndPtAddr;
int mStreamWrEndPtAddr;
int mStreamRdEndPtAddr;
uint32_t txSize;
uint32_t rxSize;
#ifndef __unix__
FT_HANDLE mFTHandle;
int ReinitPipe(unsigned char ep);
#else
int FT_SetStreamPipe(unsigned char ep, size_t size);
int FT_FlushPipe(unsigned char ep);
uint32_t mUsbCounter;
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
libusb_device_handle *dev_handle; //a device handle
libusb_context *ctx; //a libusb session
#endif
std::mutex mExtraUsbMutex;
};
class Connection_uLimeSDREntry : public ConnectionRegistryEntry
{
public:
Connection_uLimeSDREntry(void);
~Connection_uLimeSDREntry(void);
std::vector<ConnectionHandle> enumerate(const ConnectionHandle &hint);
IConnection *make(const ConnectionHandle &handle);
private:
#ifndef __unix__
FT_HANDLE* mFTHandle;
#else
libusb_context *ctx; //a libusb session
std::thread mUSBProcessingThread;
void handle_libusb_events();
std::atomic<bool> mProcessUSBEvents;
#endif
};
}

View File

@ -1,167 +0,0 @@
/**
@file Connection_uLimeSDREntry.cpp
@author Lime Microsystems
@brief Implementation of uLimeSDR board connection.
*/
#include "Connection_uLimeSDR.h"
using namespace lime;
#ifdef __unix__
void Connection_uLimeSDREntry::handle_libusb_events()
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 250000;
while(mProcessUSBEvents.load() == true)
{
int r = libusb_handle_events_timeout_completed(ctx, &tv, NULL);
if(r != 0) printf("error libusb_handle_events %s\n", libusb_strerror(libusb_error(r)));
}
}
#endif // __UNIX__
int Connection_uLimeSDR::USBTransferContext::idCounter=0;
//! make a static-initialized entry in the registry
void __loadConnection_uLimeSDREntry(void) //TODO fixme replace with LoadLibrary/dlopen
{
static Connection_uLimeSDREntry uLimeSDREntry;
}
Connection_uLimeSDREntry::Connection_uLimeSDREntry(void):
ConnectionRegistryEntry("uLimeSDR")
{
#ifndef __unix__
//m_pDriver = new CDriverInterface();
#else
int r = libusb_init(&ctx); //initialize the library for the session we just declared
if(r < 0)
printf("Init Error %i\n", r); //there was an error
libusb_set_debug(ctx, 3); //set verbosity level to 3, as suggested in the documentation
mProcessUSBEvents.store(true);
mUSBProcessingThread = std::thread(&Connection_uLimeSDREntry::handle_libusb_events, this);
#endif
}
Connection_uLimeSDREntry::~Connection_uLimeSDREntry(void)
{
#ifndef __unix__
//delete m_pDriver;
#else
mProcessUSBEvents.store(false);
mUSBProcessingThread.join();
libusb_exit(ctx);
#endif
}
std::vector<ConnectionHandle> Connection_uLimeSDREntry::enumerate(const ConnectionHandle &hint)
{
std::vector<ConnectionHandle> handles;
#ifndef __unix__
DWORD devCount = 0;
FT_STATUS ftStatus = FT_OK;
ftStatus = FT_ListDevices(&devCount, NULL, FT_LIST_NUMBER_ONLY);
if(FT_FAILED(ftStatus))
return handles;
if (devCount > 0)
{
for(int i = 0; i<devCount; ++i)
{
ConnectionHandle handle;
handle.media = "USB";
handle.name = "uLimeSDR";
handle.index = i;
handles.push_back(handle);
}
}
#else
libusb_device **devs; //pointer to pointer of device, used to retrieve a list of devices
int usbDeviceCount = libusb_get_device_list(ctx, &devs);
if (usbDeviceCount < 0) {
printf("failed to get libusb device list: %s\n", libusb_strerror(libusb_error(usbDeviceCount)));
return handles;
}
libusb_device_descriptor desc;
for(int i=0; i<usbDeviceCount; ++i)
{
int r = libusb_get_device_descriptor(devs[i], &desc);
if(r<0)
printf("failed to get device description\n");
int pid = desc.idProduct;
int vid = desc.idVendor;
if( vid == 0x0403)
{
if(pid == 0x601F)
{
libusb_device_handle *tempDev_handle;
tempDev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid);
if(libusb_kernel_driver_active(tempDev_handle, 0) == 1) //find out if kernel driver is attached
{
if(libusb_detach_kernel_driver(tempDev_handle, 0) == 0) //detach it
printf("Kernel Driver Detached!\n");
}
if(libusb_claim_interface(tempDev_handle, 0) < 0) //claim interface 0 (the first) of device
{
printf("Cannot Claim Interface\n");
}
ConnectionHandle handle;
//check operating speed
int speed = libusb_get_device_speed(devs[i]);
if(speed == LIBUSB_SPEED_HIGH)
handle.media = "USB 2.0";
else if(speed == LIBUSB_SPEED_SUPER)
handle.media = "USB 3.0";
else
handle.media = "USB";
//read device name
char data[255];
memset(data, 0, 255);
int st = libusb_get_string_descriptor_ascii(tempDev_handle, 2, (unsigned char*)data, 255);
if(st < 0)
printf("Error getting usb descriptor\n");
if(strlen(data) > 0)
handle.name = std::string(data, size_t(st));
handle.addr = std::to_string(int(pid))+":"+std::to_string(int(vid));
if (desc.iSerialNumber > 0)
{
r = libusb_get_string_descriptor_ascii(tempDev_handle,desc.iSerialNumber,(unsigned char*)data, sizeof(data));
if(r<0)
printf("failed to get serial number\n");
else
handle.serial = std::string(data, size_t(r));
}
libusb_close(tempDev_handle);
//add handle conditionally, filter by serial number
if (hint.serial.empty() or hint.serial == handle.serial)
{
handles.push_back(handle);
}
}
}
}
libusb_free_device_list(devs, 1);
#endif
return handles;
}
IConnection *Connection_uLimeSDREntry::make(const ConnectionHandle &handle)
{
#ifndef __unix__
return new Connection_uLimeSDR(mFTHandle, handle.index);
#else
const auto pidvid = handle.addr;
const auto splitPos = pidvid.find(":");
const auto pid = std::stoi(pidvid.substr(0, splitPos));
const auto vid = std::stoi(pidvid.substr(splitPos+1));
return new Connection_uLimeSDR(ctx, handle.index, vid, pid);
#endif
}

View File

@ -1,577 +0,0 @@
/**
@file Connection_uLimeSDRing.cpp
@author Lime Microsystems
@brief Implementation of uLimeSDR board connection (streaming API)
*/
#include "Connection_uLimeSDR.h"
#include "fifo.h"
#include <LMS7002M.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <algorithm>
#include <complex>
#include <ciso646>
#include <vector>
#include <FPGA_common.h>
#include "ErrorReporting.h"
using namespace lime;
using namespace std;
#define __unix__
/** @brief Configures FPGA PLLs to LimeLight interface frequency
*/
int Connection_uLimeSDR::UpdateExternalDataRate(const size_t channel, const double txRate, const double rxRate, const double txPhase, const double rxPhase)
{
const float txInterfaceClk = 2 * txRate;
const float rxInterfaceClk = 2 * rxRate;
int status = 0;
mExpectedSampleRate = rxRate;
lime::fpga::FPGA_PLL_clock clocks[4];
clocks[0].bypass = false;
clocks[0].index = 0;
clocks[0].outFrequency = txInterfaceClk;
clocks[0].phaseShift_deg = 0;
clocks[0].findPhase = false;
clocks[1].bypass = false;
clocks[1].index = 1;
clocks[1].outFrequency = txInterfaceClk;
clocks[1].findPhase = false;
clocks[1].phaseShift_deg = txPhase;
clocks[2].bypass = false;
clocks[2].index = 2;
clocks[2].outFrequency = rxInterfaceClk;
clocks[2].phaseShift_deg = 0;
clocks[2].findPhase = false;
clocks[3].bypass = false;
clocks[3].index = 3;
clocks[3].outFrequency = rxInterfaceClk;
clocks[3].findPhase = false;
clocks[3].phaseShift_deg = rxPhase;
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
return status;
}
/** @brief Configures FPGA PLLs to LimeLight interface frequency
*/
int Connection_uLimeSDR::UpdateExternalDataRate(const size_t channel, const double txRate_Hz, const double rxRate_Hz)
{
const float txInterfaceClk = 2 * txRate_Hz;
const float rxInterfaceClk = 2 * rxRate_Hz;
int status = 0;
uint32_t reg20;
const double rxPhC1[] = { 91.08, 89.46 };
const double rxPhC2[] = { -1 / 6e6, 1.24e-6 };
const double txPhC1[] = { 89.75, 89.61 };
const double txPhC2[] = { -3.0e-7, 2.71e-7 };
const std::vector<uint32_t> spiAddr = { 0x0021, 0x0022, 0x0023, 0x0024,
0x0027, 0x002A, 0x0400, 0x040C,
0x040B, 0x0400, 0x040B, 0x0400 };
const int bakRegCnt = spiAddr.size() - 4;
auto info = GetDeviceInfo();
const int addrLMS7002M = info.addrsLMS7002M.at(0);
bool phaseSearch = false;
//if (this->chipVersion == 0x3841) //0x3840 LMS7002Mr2, 0x3841 LMS7002Mr3
/*if (rxInterfaceClk >= 5e6 || txInterfaceClk >= 5e6)
phaseSearch = true;*/
mExpectedSampleRate = rxRate_Hz;
std::vector<uint32_t> dataWr;
std::vector<uint32_t> dataRd;
if (phaseSearch)
{
dataWr.resize(spiAddr.size());
dataRd.resize(spiAddr.size());
//backup registers
dataWr[0] = (uint32_t(0x0020) << 16);
TransactSPI(addrLMS7002M, dataWr.data(), &reg20, 1);
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | 0xFFFD; //msbit 1=SPI write
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
for (int i = 0; i < bakRegCnt; ++i)
dataWr[i] = (spiAddr[i] << 16);
TransactSPI(addrLMS7002M, dataWr.data(), dataRd.data(), bakRegCnt);
}
if ((txInterfaceClk >= 5e6) && (rxInterfaceClk >= 5e6))
{
lime::fpga::FPGA_PLL_clock clocks[4];
clocks[0].bypass = false;
clocks[0].index = 0;
clocks[0].outFrequency = txInterfaceClk;
clocks[0].phaseShift_deg = 0;
clocks[0].findPhase = false;
clocks[1].bypass = false;
clocks[1].index = 1;
clocks[1].outFrequency = txInterfaceClk;
clocks[1].findPhase = false;
if (this->chipVersion == 0x3841)
clocks[1].phaseShift_deg = txPhC1[1] + txPhC2[1] * txInterfaceClk;
else
clocks[1].phaseShift_deg = txPhC1[0] + txPhC2[0] * txInterfaceClk;
clocks[2].bypass = false;
clocks[2].index = 2;
clocks[2].outFrequency = rxInterfaceClk;
clocks[2].phaseShift_deg = 0;
clocks[2].findPhase = false;
clocks[3].bypass = false;
clocks[3].index = 3;
clocks[3].outFrequency = rxInterfaceClk;
clocks[3].findPhase = false;
if (this->chipVersion == 0x3841)
clocks[3].phaseShift_deg = rxPhC1[1] + rxPhC2[1] * rxInterfaceClk;
else
clocks[3].phaseShift_deg = rxPhC1[0] + rxPhC2[0] * rxInterfaceClk;
if (phaseSearch)
{
{
#ifndef NDEBUG
printf("RX phase config:\n");
#endif
clocks[3].findPhase = true;
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4,
0xE4E4, 0x0086, 0x028D, 0x00FF, 0x5555, 0x02CD, 0xAAAA, 0x02ED };
//Load test config
const int setRegCnt = spiData.size();
for (int i = 0; i < setRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
}
{
#ifndef NDEBUG
printf("TX phase config:\n");
#endif
clocks[3].findPhase = false;
const std::vector<uint32_t> spiData = { 0x0E9F, 0x07FF, 0x5550, 0xE4E4, 0xE4E4, 0x0484 };
WriteRegister(0x000A, 0x0000);
//Load test config
const int setRegCnt = spiData.size();
for (int i = 0; i < setRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | spiData[i]; //msbit 1=SPI write
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, setRegCnt);
clocks[1].findPhase = true;
WriteRegister(0x000A, 0x0200);
}
}
status = lime::fpga::SetPllFrequency(this, 0, rxInterfaceClk, clocks, 4);
}
else
{
status = lime::fpga::SetDirectClocking(this, 0, rxInterfaceClk, 90);
if (status == 0)
status = lime::fpga::SetDirectClocking(this, 1, rxInterfaceClk, 90);
}
if (phaseSearch)
{
//Restore registers
for (int i = 0; i < bakRegCnt; ++i)
dataWr[i] = (1 << 31) | (uint32_t(spiAddr[i]) << 16) | dataRd[i]; //msbit 1=SPI write
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, bakRegCnt);
dataWr[0] = (1 << 31) | (uint32_t(0x0020) << 16) | reg20; //msbit 1=SPI write
TransactSPI(addrLMS7002M, dataWr.data(), nullptr, 1);
WriteRegister(0x000A, 0);
}
return status;
}
int Connection_uLimeSDR::ReadRawStreamData(char* buffer, unsigned length, int epIndex, int timeout_ms)
{
int totalBytesReceived = 0;
fpga::StopStreaming(this, epIndex);
//ResetStreamBuffers();
WriteRegister(0x0008, 0x0100 | 0x2);
WriteRegister(0x0007, 1);
fpga::StartStreaming(this, epIndex);
int handle = BeginDataReading(buffer, length);
if (WaitForReading(handle, timeout_ms))
totalBytesReceived = FinishDataReading(buffer, length, handle);
AbortReading();
fpga::StopStreaming(this, epIndex);
return totalBytesReceived;
}
int Connection_uLimeSDR::ResetStreamBuffers()
{
rxSize = 0;
txSize = 0;
#ifndef __unix__
if (FT_AbortPipe(mFTHandle, mStreamRdEndPtAddr)!=FT_OK)
return -1;
if (FT_AbortPipe(mFTHandle, mStreamWrEndPtAddr)!=FT_OK)
return -1;
if (FT_FlushPipe(mFTHandle, mStreamRdEndPtAddr)!=FT_OK)
return -1;
#else
return FT_FlushPipe(mStreamRdEndPtAddr);
#endif
return 0;
}
/** @brief Function dedicated for receiving data samples from board
@param rxFIFO FIFO to store received data
@param terminate periodically pooled flag to terminate thread
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
*/
void Connection_uLimeSDR::ReceivePacketsLoop(Connection_uLimeSDR::Streamer* stream)
{
//at this point FPGA has to be already configured to output samples
const uint8_t chCount = stream->mRxStreams.size();
const auto link =stream->mRxStreams[0]->config.linkFormat;
const uint32_t samplesInPacket = (link == StreamConfig::STREAM_12_BIT_COMPRESSED ? 1360 : 1020)/chCount;
const int chipID = stream->mChipID;
double latency=0;
for (int i = 0; i < chCount; i++)
{
latency += stream->mRxStreams[i]->config.performanceLatency/chCount;
}
const unsigned tmp_cnt = (latency * 4)+0.5;
const uint8_t packetsToBatch = (1<<tmp_cnt);
const uint32_t bufferSize = packetsToBatch*sizeof(FPGA_DataPacket);
const uint8_t buffersCount = 16; // must be power of 2
vector<int> handles(buffersCount, 0);
vector<char>buffers(buffersCount*bufferSize, 0);
vector<StreamChannel::Frame> chFrames;
try
{
chFrames.resize(chCount);
}
catch (const std::bad_alloc &ex)
{
ReportError("Error allocating Rx buffers, not enough memory");
return;
}
uint8_t activeTransfers = 0;
for (int i = 0; i<buffersCount; ++i)
{
handles[i] = this->BeginDataReading(&buffers[i*bufferSize], bufferSize);
++activeTransfers;
}
int bi = 0;
unsigned long totalBytesReceived = 0; //for data rate calculation
int m_bufferFailures = 0;
int32_t droppedSamples = 0;
int32_t packetLoss = 0;
vector<uint32_t> samplesCollected(chCount, 0);
vector<uint32_t> samplesReceived(chCount, 0);
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
std::mutex txFlagsLock;
condition_variable resetTxFlags;
//worker thread for reseting late Tx packet flags
std::thread txReset([](ILimeSDRStreaming* port,
atomic<bool> *terminate,
mutex *spiLock,
condition_variable *doWork)
{
uint32_t reg9;
port->ReadRegister(0x0009, reg9);
const uint32_t addr[] = {0x0009, 0x0009};
const uint32_t data[] = {reg9 | (1 << 1), reg9 & ~(1 << 1)};
while (not terminate->load())
{
std::unique_lock<std::mutex> lck(*spiLock);
doWork->wait(lck);
port->WriteRegisters(addr, data, 2);
}
}, this, &stream->terminateRx, &txFlagsLock, &resetTxFlags);
int resetFlagsDelay = 128;
uint64_t prevTs = 0;
while (stream->terminateRx.load() == false)
{
if(stream->generateData.load())
{
if(activeTransfers == 0) //stop FPGA when last transfer completes
fpga::StopStreaming(this, chipID);
stream->safeToConfigInterface.notify_all(); //notify that it's safe to change chip config
const int batchSize = (this->mExpectedSampleRate/chFrames[0].samplesCount)/10;
IStreamChannel::Metadata meta;
for(int i=0; i<batchSize; ++i)
{
for(int ch=0; ch<chCount; ++ch)
{
meta.timestamp = chFrames[ch].timestamp;
for(int j=0; j<chFrames[ch].samplesCount; ++j)
{
chFrames[ch].samples[j].i = 0;
chFrames[ch].samples[j].q = 0;
}
uint32_t samplesPushed = stream->mRxStreams[ch]->Write((const void*)chFrames[ch].samples, chFrames[ch].samplesCount, &meta);
samplesReceived[ch] += chFrames[ch].samplesCount;
if(samplesPushed != chFrames[ch].samplesCount)
printf("Rx samples pushed %i/%i\n", samplesPushed, chFrames[ch].samplesCount);
}
}
this_thread::sleep_for(chrono::milliseconds(100));
}
int32_t bytesReceived = 0;
if(handles[bi] >= 0)
{
if (this->WaitForReading(handles[bi], 1000) == false)
++m_bufferFailures;
bytesReceived = this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
--activeTransfers;
totalBytesReceived += bytesReceived;
if (bytesReceived != int32_t(bufferSize)) //data should come in full sized packets
++m_bufferFailures;
}
bool txLate=false;
for (uint8_t pktIndex = 0; pktIndex < bytesReceived / sizeof(FPGA_DataPacket); ++pktIndex)
{
const FPGA_DataPacket* pkt = (FPGA_DataPacket*)&buffers[bi*bufferSize];
const uint8_t byte0 = pkt[pktIndex].reserved[0];
if ((byte0 & (1 << 3)) != 0 && !txLate) //report only once per batch
{
txLate = true;
if(resetFlagsDelay > 0)
--resetFlagsDelay;
else
{
printf("L");
resetTxFlags.notify_one();
resetFlagsDelay = packetsToBatch*buffersCount;
stream->txLastLateTime.store(pkt[pktIndex].counter);
}
}
uint8_t* pktStart = (uint8_t*)pkt[pktIndex].data;
if(pkt[pktIndex].counter - prevTs != samplesInPacket && pkt[pktIndex].counter != prevTs)
{
#ifndef NDEBUG
printf("\tRx pktLoss ts diff %lli\n", (long long)pkt[pktIndex].counter - prevTs);
#endif
packetLoss += (pkt[pktIndex].counter - prevTs)/samplesInPacket;
}
prevTs = pkt[pktIndex].counter;
stream->rxLastTimestamp.store(pkt[pktIndex].counter);
//parse samples
vector<complex16_t*> dest(chCount);
for(uint8_t c=0; c<chCount; ++c)
dest[c] = (chFrames[c].samples);
size_t samplesCount = 0;
fpga::FPGAPacketPayload2Samples(pktStart, 4080, chCount, link, dest.data(), &samplesCount);
for(int ch=0; ch<chCount; ++ch)
{
IStreamChannel::Metadata meta;
meta.timestamp = pkt[pktIndex].counter;
meta.flags = RingFIFO::OVERWRITE_OLD;
uint32_t samplesPushed = stream->mRxStreams[ch]->Write((const void*)chFrames[ch].samples, samplesCount, &meta, 100);
if(samplesPushed != samplesCount)
droppedSamples += samplesCount-samplesPushed;
}
}
// Re-submit this request to keep the queue full
if(not stream->generateData.load())
{
if(activeTransfers == 0) //reactivate FPGA and USB transfers
fpga::StartStreaming(this, chipID);
for(int i=0; i<buffersCount-activeTransfers; ++i)
{
handles[bi] = this->BeginDataReading(&buffers[bi*bufferSize], bufferSize);
bi = (bi + 1) & (buffersCount-1);
++activeTransfers;
}
}
else
{
handles[bi] = -1;
bi = (bi + 1) & (buffersCount-1);
}
t2 = chrono::high_resolution_clock::now();
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (timePeriod >= 1000)
{
t1 = t2;
//total number of bytes sent per second
double dataRate = 1000.0*totalBytesReceived / timePeriod;
#ifndef NDEBUG
//each channel sample rate
float samplingRate = 1000.0*samplesReceived[0] / timePeriod;
printf("Rx: %.3f MB/s, Fs: %.3f MHz, overrun: %i, loss: %i \n", dataRate / 1000000.0, samplingRate / 1000000.0, droppedSamples, packetLoss);
#endif
samplesReceived[0] = 0;
totalBytesReceived = 0;
m_bufferFailures = 0;
droppedSamples = 0;
packetLoss = 0;
stream->rxDataRate_Bps.store((uint32_t)dataRate);
}
}
this->AbortReading();
for (int j = 0; j<buffersCount; j++)
{
if(handles[bi] >= 0)
{
this->WaitForReading(handles[bi], 1000);
this->FinishDataReading(&buffers[bi*bufferSize], bufferSize, handles[bi]);
}
bi = (bi + 1) & (buffersCount-1);
}
resetTxFlags.notify_one();
txReset.join();
stream->rxDataRate_Bps.store(0);
}
/** @brief Functions dedicated for transmitting packets to board
@param txFIFO data source FIFO
@param terminate periodically pooled flag to terminate thread
@param dataRate_Bps (optional) if not NULL periodically returns data rate in bytes per second
*/
void Connection_uLimeSDR::TransmitPacketsLoop(Streamer* stream)
{
//at this point FPGA has to be already configured to output samples
const uint8_t maxChannelCount = 2;
const uint8_t chCount = stream->mTxStreams.size();
const auto link = stream->mTxStreams[0]->config.linkFormat;
double latency=0;
for (int i = 0; i < chCount; i++)
{
latency += stream->mTxStreams[i]->config.performanceLatency/chCount;
}
const unsigned tmp_cnt = (latency * 4)+0.5;
const uint8_t buffersCount = 16; // must be power of 2
assert(buffersCount % 2 == 0);
const uint8_t packetsToBatch = (1<<tmp_cnt); //packets in single USB transfer
const uint32_t bufferSize = packetsToBatch*4096;
const uint32_t popTimeout_ms = 100;
const int maxSamplesBatch = (link==StreamConfig::STREAM_12_BIT_COMPRESSED?1360:1020)/chCount;
vector<int> handles(buffersCount, 0);
vector<bool> bufferUsed(buffersCount, 0);
vector<uint32_t> bytesToSend(buffersCount, 0);
vector<complex16_t> samples[maxChannelCount];
vector<char> buffers;
try
{
for(int i=0; i<chCount; ++i)
samples[i].resize(maxSamplesBatch);
buffers.resize(buffersCount*bufferSize, 0);
}
catch (const std::bad_alloc& ex) //not enough memory for buffers
{
printf("Error allocating Tx buffers, not enough memory\n");
return;
}
int m_bufferFailures = 0;
long totalBytesSent = 0;
uint32_t samplesSent = 0;
auto t1 = chrono::high_resolution_clock::now();
auto t2 = chrono::high_resolution_clock::now();
uint8_t bi = 0; //buffer index
while (stream->terminateTx.load() != true)
{
if (bufferUsed[bi])
{
if (this->WaitForSending(handles[bi], 1000) == false)
++m_bufferFailures;
uint32_t bytesSent = this->FinishDataSending(&buffers[bi*bufferSize], bytesToSend[bi], handles[bi]);
totalBytesSent += bytesSent;
if (bytesSent != bytesToSend[bi])
++m_bufferFailures;
bufferUsed[bi] = false;
}
int i=0;
while(i<packetsToBatch && stream->terminateTx.load() != true)
{
IStreamChannel::Metadata meta;
FPGA_DataPacket* pkt = reinterpret_cast<FPGA_DataPacket*>(&buffers[bi*bufferSize]);
for(int ch=0; ch<chCount; ++ch)
{
int samplesPopped = stream->mTxStreams[ch]->Read(samples[ch].data(), maxSamplesBatch, &meta, popTimeout_ms);
if (samplesPopped != maxSamplesBatch)
{
#ifndef NDEBUG
printf("Warning popping from TX, samples popped %i/%i\n", samplesPopped, maxSamplesBatch);
#endif
}
}
if(stream->terminateTx.load() == true) //early termination
break;
pkt[i].counter = meta.timestamp;
pkt[i].reserved[0] = 0;
//by default ignore timestamps
const int ignoreTimestamp = !(meta.flags & IStreamChannel::Metadata::SYNC_TIMESTAMP);
pkt[i].reserved[0] |= ((int)ignoreTimestamp << 4); //ignore timestamp
vector<complex16_t*> src(chCount);
for(uint8_t c=0; c<chCount; ++c)
src[c] = (samples[c].data());
uint8_t* const dataStart = (uint8_t*)pkt[i].data;
fpga::Samples2FPGAPacketPayload(src.data(), maxSamplesBatch, chCount, link, dataStart, nullptr);
samplesSent += maxSamplesBatch;
++i;
}
bytesToSend[bi] = bufferSize;
handles[bi] = this->BeginDataSending(&buffers[bi*bufferSize], bytesToSend[bi]);
bufferUsed[bi] = true;
t2 = chrono::high_resolution_clock::now();
auto timePeriod = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (timePeriod >= 1000)
{
//total number of bytes sent per second
float dataRate = 1000.0*totalBytesSent / timePeriod;
stream->txDataRate_Bps.store(dataRate);
m_bufferFailures = 0;
samplesSent = 0;
totalBytesSent = 0;
t1 = t2;
#ifndef NDEBUG
//total number of samples from all channels per second
float sampleRate = 1000.0*samplesSent / timePeriod;
printf("Tx: %.3f MB/s, Fs: %.3f MHz, failures: %i\n", dataRate / 1000000.0, sampleRate / 1000000.0, m_bufferFailures);
#endif
}
bi = (bi + 1) & (buffersCount-1);
}
// Wait for all the queued requests to be cancelled
this->AbortSending();
for (int j = 0; j<buffersCount; j++)
{
if (bufferUsed[bi])
{
this->WaitForSending(handles[bi], 1000);
this->FinishDataSending(&buffers[bi*bufferSize], bufferSize, handles[bi]);
}
bi = (bi + 1) & (buffersCount-1);
}
stream->txDataRate_Bps.store(0);
}

View File

@ -1,63 +0,0 @@
/**
@file ErrorReporting.cpp
@author Lime Microsystems
@brief API for reporting error codes and error messages.
*/
#include "ErrorReporting.h"
#include <cstring> //strerror
#include <cstdio>
#ifdef _MSC_VER
#define thread_local __declspec( thread )
#include <Windows.h>
#endif
#ifdef __APPLE__
#define thread_local __thread
#endif
#define MAX_MSG_LEN 1024
thread_local int _reportedErrorCode;
thread_local char _reportedErrorMessage[MAX_MSG_LEN];
static const char *errToStr(const int errnum)
{
thread_local static char buff[MAX_MSG_LEN];
#ifdef _MSC_VER
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buff, sizeof(buff), NULL);
return buff;
#else
//http://linux.die.net/man/3/strerror_r
#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) || __APPLE__
strerror_r(errnum, buff, sizeof(buff));
#else
//this version may decide to use its own internal string
//return strerror_r(errnum, buff, sizeof(buff));
return buff;
#endif
return buff;
#endif
}
int lime::GetLastError(void)
{
return _reportedErrorCode;
}
const char *lime::GetLastErrorMessage(void)
{
return _reportedErrorMessage;
}
int lime::ReportError(const int errnum)
{
return lime::ReportError(errnum, errToStr(errnum));
}
int lime::ReportError(const int errnum, const char *format, va_list argList)
{
_reportedErrorCode = errnum;
vsnprintf(_reportedErrorMessage, MAX_MSG_LEN, format, argList);
return errnum;
}

View File

@ -1,85 +0,0 @@
/**
@file ErrorReporting.h
@author Lime Microsystems
@brief API for reporting error codes and error messages.
All calls are thread-safe using thread-local storage.
Code returning with an error should use:
return lime::ReportError(code, message, ...);
*/
#ifndef LIMESUITE_ERROR_REPORTING_H
#define LIMESUITE_ERROR_REPORTING_H
#include <LimeSuiteConfig.h>
#include <cerrno>
#include <string>
#include <stdexcept>
#include <cstdarg>
namespace lime
{
/*!
* Get the error code reported.
*/
LIME_API int GetLastError(void);
/*!
* Get the error code to string + any optional message reported.
*/
LIME_API const char *GetLastErrorMessage(void);
/*!
* Report a typical errno style error.
* The resulting error message comes from strerror().
* \param errnum a recognized error code
* \return a non-zero status code to return
*/
LIME_API int ReportError(const int errnum);
/*!
* Report an error as an integer code and a formatted message string.
* \param errnum a recognized error code
* \param format a format string followed by args
* \return a non-zero status code to return
*/
inline int ReportError(const int errnum, const char *format, ...);
/*!
* Report an error as a formatted message string.
* The reported errnum is 0 - no relevant error code.
* \param format a format string followed by args
* \return a non-zero status code to return
*/
inline int ReportError(const char *format, ...);
/*!
* Report an error as an integer code and message format arguments
* \param errnum a recognized error code
* \param format a printf-style format string
* \param argList the format string args as a va_list
* \return a non-zero status code to return
*/
LIME_API int ReportError(const int errnum, const char *format, va_list argList);
}
inline int lime::ReportError(const int errnum, const char *format, ...)
{
va_list argList;
va_start(argList, format);
int status = lime::ReportError(errnum, format, argList);
va_end(argList);
return status;
}
inline int lime::ReportError(const char *format, ...)
{
va_list argList;
va_start(argList, format);
int status = lime::ReportError(-1, format, argList);
va_end(argList);
return status;
}
#endif //LIMESUITE_ERROR_REPORTING_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,535 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <vector>
#include <iostream>
#if !(defined(max)) && _MSC_VER
// VC fix
#define max __max
#endif
#define MYMAX(a, b) (a) > (b) ? (a) : (b)
using namespace std;
class MemBlock
{
public:
unsigned long m_startAddress;
vector<unsigned char> m_bytes;
};
class MCU_File
{
public:
explicit MCU_File(const char *fileName, const char *mode)
{
m_file = fopen(fileName, mode);
if (m_file != NULL)
{
return;
}
cout << "Error opening";
//string errorStr = "Error opening ";
//errorStr += fileName;
//errorStr += "\n";
//throw errorStr;
}
~MCU_File()
{
if (m_file)
fclose(m_file);
}
bool FileOpened()
{
return m_file != NULL;
}
// Read binary file
void ReadBin(unsigned long limit)
{
m_top = 0;
m_chunks.push_back(MemBlock());
m_chunks.back().m_startAddress = 0;
cout << "Reading binary file\n";
int tmp = fgetc(m_file);
while (!feof(m_file))
{
m_chunks.back().m_bytes.push_back(tmp);
if (m_chunks.back().m_bytes.size() > limit + 1)
{
m_chunks.back().m_bytes.pop_back();
m_top = m_chunks.back().m_bytes.size() - 1;
cout << "Ignoring data above address space!\n";
cout << " Limit: " << limit << "\n";
return;
}
tmp = fgetc(m_file);
}
m_top = m_chunks.back().m_bytes.size() - 1;
if (!m_chunks.back().m_bytes.size())
{
cout << "No data!\n";
m_chunks.pop_back();
}
}
// Read hex file
void ReadHex(unsigned long limit)
{
char szLine[1024];
bool formatDetected = false;
bool intel = false;
bool endSeen = false;
bool linear = true; // Only used for intel hex
unsigned long addressBase = 0; // Only used for intel hex
unsigned long dataRecords = 0; // Only used for s-record
while (!feof(m_file))
{
if (fgets(szLine, 1024, m_file) == 0)
{
if (ferror(m_file))
{
throw "Error reading input!\n";
}
continue;
}
if (szLine[strlen(szLine) - 1] == 0xA || szLine[strlen(szLine) - 1] == 0xD)
{
szLine[strlen(szLine) - 1] = 0;
}
if (szLine[strlen(szLine) - 1] == 0xA || szLine[strlen(szLine) - 1] == 0xD)
{
szLine[strlen(szLine) - 1] = 0;
}
if (strlen(szLine) == 1023)
{
throw "Hex file lines to long!\n";
}
// Ignore blank lines
if (szLine[0] == '\n')
{
continue;
}
// Detect format and warn if garbage lines are found
if (!formatDetected)
{
if (szLine[0] != ':' && szLine[0] != 'S')
{
cout << "Ignoring garbage line!\n";
continue;
}
if (szLine[0] == 'S')
{
intel = false;
cout << "Detected S-Record\n";
}
else
{
intel = true;
cout << "Detected intel hex file\n";
}
formatDetected = true;
}
else if ((intel && szLine[0] != ':') ||
(!intel && szLine[0] != 'S'))
{
cout << "Ignoring garbage line!\n";
continue;
}
if (endSeen)
{
throw "Hex line after end of file record!\n";
}
if (intel)
{
unsigned long dataBytes;
unsigned long startAddress;
unsigned long type;
if (sscanf(&szLine[1], "%2lx%4lx%2lx", &dataBytes, &startAddress, &type) != 3)
{
throw "Hex line beginning corrupt!\n";
}
// Check line length
if (szLine[11 + dataBytes * 2] != '\n' && szLine[11 + dataBytes * 2] != 0)
{
throw "Hex line length incorrect!\n";
}
// Check line checksum
unsigned char checkSum = 0;
unsigned long tmp;
for (unsigned int i = 0; i <= dataBytes + 4; ++i)
{
if (sscanf(&szLine[1 + i * 2], "%2lx", &tmp) != 1)
{
throw "Hex line data corrupt!\n";
}
checkSum += tmp;
}
if (checkSum != 0)
{
throw "Hex line checksum error!\n";
}
switch (type)
{
case 0:
// Data record
if (!linear)
{
// Segmented
unsigned long test = startAddress;
test += dataBytes;
if (test > 0xffff)
{
throw "Can't handle wrapped segments!\n";
}
}
if (!m_chunks.size() ||
m_chunks.back().m_startAddress + m_chunks.back().m_bytes.size() !=
addressBase + startAddress)
{
m_chunks.push_back(MemBlock());
m_chunks.back().m_startAddress = addressBase + startAddress;
}
{
unsigned char i = 0;
for (i = 0; i < dataBytes; ++i)
{
sscanf(&szLine[9 + i * 2], "%2lx", &tmp);
if (addressBase + startAddress + i > limit)
{
cout << "Ignoring data above address space!\n";
cout << "Data address: " << addressBase + startAddress + i;
cout << " Limit: " << limit << "\n";
if (!m_chunks.back().m_bytes.size())
{
m_chunks.pop_back();
}
continue;
}
m_chunks.back().m_bytes.push_back(tmp);
}
}
break;
case 1:
// End-of-file record
if (dataBytes != 0)
{
cout << "Warning: End of file record not zero length!\n";
}
if (startAddress != 0)
{
cout << "Warning: End of file record address not zero!\n";
}
endSeen = true;
break;
case 2:
// Extended segment address record
if (dataBytes != 2)
{
throw "Length field must be 2 in extended segment address record!\n";
}
if (startAddress != 0)
{
throw "Address field must be zero in extended segment address record!\n";
}
sscanf(&szLine[9], "%4lx", &startAddress);
addressBase = startAddress << 4;
linear = false;
break;
case 3:
// Start segment address record
if (dataBytes != 4)
{
cout << "Warning: Length field must be 4 in start segment address record!\n";
}
if (startAddress != 0)
{
cout << "Warning: Address field must be zero in start segment address record!\n";
}
if (dataBytes == 4)
{
unsigned long ssa;
char ssaStr[16];
sscanf(&szLine[9], "%8lx", &ssa);
sprintf(ssaStr, "%08lX\n", ssa);
cout << "Segment start address (CS/IP): ";
cout << ssaStr;
}
break;
case 4:
// Extended linear address record
if (dataBytes != 2)
{
throw "Length field must be 2 in extended linear address record!\n";
}
if (startAddress != 0)
{
throw "Address field must be zero in extended linear address record!\n";
}
sscanf(&szLine[9], "%4lx", &startAddress);
addressBase = ((unsigned long)startAddress) << 16;
linear = true;
break;
case 5:
// Start linear address record
if (dataBytes != 4)
{
cout << "Warning: Length field must be 4 in start linear address record!\n";
}
if (startAddress != 0)
{
cout << "Warning: Address field must be zero in start linear address record!\n";
}
if (dataBytes == 4)
{
unsigned long lsa;
char lsaStr[16];
sscanf(&szLine[9], "%8lx", &lsa);
sprintf(lsaStr, "%08lX\n", lsa);
cout << "Linear start address: ";
cout << lsaStr;
}
break;
default:
cout << "Waring: Unknown record found!\n";
}
}
else
{
// S-record
unsigned long count;
char type;
if (sscanf(&szLine[1], "%c%2lx", &type, &count) != 2)
{
throw "Hex line beginning corrupt!\n";
}
// Check line length
if (szLine[4 + count * 2] != '\n' && szLine[4 + count * 2] != 0)
{
throw "Hex line length incorrect!\n";
}
// Check line checksum
unsigned char checkSum = 0;
unsigned long tmp;
for (unsigned int i = 0; i < count + 1; ++i)
{
if (sscanf(&szLine[2 + i * 2], "%2lx", &tmp) != 1)
{
throw "Hex line data corrupt!\n";
}
checkSum += tmp;
}
if (checkSum != 255)
{
throw "Hex line checksum error!\n";
}
switch (type)
{
case '0':
// Header record
{
char header[256];
uint8_t i = 0;
for (i = 0; uint8_t(i + 3) < count; ++i)
{
sscanf(&szLine[8 + i * 2], "%2lx", &tmp);
header[i] = tmp;
}
header[i] = 0;
if (i > 0)
{
cout << "Module name: " << header << "\n";
}
}
break;
case '1':
case '2':
case '3':
// Data record
{
dataRecords++;
unsigned long startAddress;
if (type == '1')
{
sscanf(&szLine[4], "%4lx", &startAddress);
}
else if (type == '2')
{
sscanf(&szLine[4], "%6lx", &startAddress);
}
else
{
sscanf(&szLine[4], "%8lx", &startAddress);
}
if (!m_chunks.size() ||
m_chunks.back().m_startAddress + m_chunks.back().m_bytes.size() !=
startAddress)
{
m_chunks.push_back(MemBlock());
m_chunks.back().m_startAddress = startAddress;
}
unsigned char i = 0;
for (i = uint8_t(type - '1'); uint8_t(i + 3) < count; ++i)
{
sscanf(&szLine[8 + i * 2], "%2lx", &tmp);
if (startAddress + i > limit)
{
cout << "Ignoring data above address space!\n";
cout << "Data address: " << startAddress + i;
cout << " Limit: " << limit << "\n";
if (!m_chunks.back().m_bytes.size())
{
m_chunks.pop_back();
}
continue;
}
m_chunks.back().m_bytes.push_back(tmp);
}
}
break;
case '5':
// Count record
{
unsigned long address;
sscanf(&szLine[4], "%4lx", &address);
if (address != dataRecords)
{
throw "Wrong number of data records!\n";
}
}
break;
case '7':
case '8':
case '9':
// Start address record
cout << "Ignoring start address record!\n";
break;
default:
cout << "Unknown record found!\n";
}
}
}
if (intel && !endSeen)
{
cout << "No end of file record!\n";
}
if (!m_chunks.size())
{
throw "No data in file!\n";
}
vector<MemBlock>::iterator vi;
m_top = 0;
for (vi = m_chunks.begin(); vi < m_chunks.end(); vi++)
{
//m_top = max(m_top, vi->m_startAddress + vi->m_bytes.size() - 1);
m_top = MYMAX(m_top, vi->m_startAddress + vi->m_bytes.size() - 1);
}
}
// Rather inefficient this one, fix sometime
bool GetByte(const unsigned long address, unsigned char &chr)
{
vector<MemBlock>::iterator vi;
for (vi = m_chunks.begin(); vi < m_chunks.end(); vi++)
{
if (vi->m_startAddress + vi->m_bytes.size() > address && vi->m_startAddress <= address)
{
break;
}
}
if (vi == m_chunks.end())
{
return false;
}
chr = vi->m_bytes[address - vi->m_startAddress];
return true;
}
bool BitString(const unsigned long address, const unsigned char bits, const bool lEndian, string &str)
{
bool ok = false;
long i;
unsigned char chr;
unsigned long data = 0;
unsigned long tmp;
if (lEndian)
{
for (i = 0; i < (bits + 7) / 8; ++i)
{
ok |= GetByte(address + i, chr);
tmp = chr;
data |= tmp << (8 * i);
}
}
else
{
for (i = 0; i < (bits + 7) / 8; ++i)
{
ok |= GetByte(address + i, chr);
tmp = chr;
data |= tmp << (8 * ((bits + 7) / 8 - i - 1));
}
}
if (!ok)
{
return false;
}
unsigned long mask = 1;
str = "";
for (i = 0; i < bits; i++)
{
if (data & mask)
{
str.insert(0,"1");
}
else
{
str.insert(0,"0");
}
mask <<= 1;
}
return true;
}
FILE *Handle() { return m_file; };
vector<MemBlock> m_chunks;
unsigned long m_top;
private:
FILE *m_file;
};

View File

@ -1,38 +0,0 @@
/*
* vasprintf.h
*
* Created on: 9 May 2017
* Author: egriffiths
*/
#ifndef LIBLIMESUITE_SRCMW_VASPRINTF_H_
#define LIBLIMESUITE_SRCMW_VASPRINTF_H_
#define _GNU_SOURCE
#define __CRT__NO_INLINE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int my_vasprintf(char ** __restrict__ ret,
const char * __restrict__ format,
va_list ap) {
int len;
/* Get Length */
len = _vsnprintf(NULL,0,format,ap);
if (len < 0) return -1;
/* +1 for \0 terminator. */
*ret = (char *) malloc(len + 1);
/* Check malloc fail*/
if (!*ret) return -1;
/* Write String */
_vsnprintf(*ret,len+1,format,ap);
/* Terminate explicitly */
(*ret)[len] = '\0';
return len;
}
#endif /* LIBLIMESUITE_SRCMW_VASPRINTF_H_ */