#include "fgpaprogrammer.h" #include #include #include #include #include #include #include #include #include #include #include "mydebug.h" #include #include #include #include "fpgaflashengine.h" #include "FpgaBridgeTftp.h" #include "fileutils.h" #include "fpgabeammeupstring.h" static unsigned char dummy_buffer[1024]; class my_exception: public std::exception { public: int code; my_exception(int c): code(c) {} }; enum UserEventId { evn_start=QEvent::User+100, evn_timeout, evn_write_completed, evn_read_completed, evn_break, evn_abort, evn_error, evn_erase, evn_reset, evn_readFlashID, evn_readStatusSPI, evn_terminate }; struct event_data_t { int type; unsigned int len; char data[256]; event_data_t(QEvent::Type type_=QEvent::User, const void* data_=0, unsigned int len_=0): type(type_), len(len_) { (void)data_; } }; class MyEvent: public QEvent { public: MyEvent(int type): QEvent(QEvent::Type(type)), data(QEvent::Type(type)) { } event_data_t data; }; class FgpaProgrammer::FpgaProgrammerImplementation: public QThread, public FpgaFlashInterface { public: //int tId; QTimer* pTimer; FgpaProgrammer* tFpgaProg; FpgaBridgeTftp* tftp; bool simulate; bool SRIOMemory; //0 for address BASE_ADDRESS_IF //=!0 for address BASE_ADDRESS_MEM FpgaFlashEngine engine; FpgaFlashProfile profile; QElapsedTimer elapsedTimer; //FpgaFlashInterface virtual unsigned long timeNowMs() override { return elapsedTimer.elapsed(); } //*********************************************************************** // rtgWrite // Input parameters: address = address to write // data = buffer to write // len = buffer's length // mess = message to write into debug log // Returns: true, no error, operation completed // false, generic error into write function //*********************************************************************** virtual bool rtgWrite(uint32_t address, const void* data, unsigned int len=256) override { if (tftp && !simulate) { QString remoteFile = ""; if (SRIOMemory) { remoteFile =QString("$SRIO:%1/0x%2+%3").arg(profile.slotAddress).arg(BASE_ADDRESS_MEM+address, 0, 16).arg(len); } else { remoteFile =QString("$SRIO:%1/0x%2+%3").arg(profile.slotAddress).arg(BASE_ADDRESS_IF+address, 0, 16).arg(len); } MyDebug.logMsg( type_log_info, origin_msg_srio , QString(STR_COMMAND_WRITE).arg(remoteFile)); Q_EMIT tFpgaProg->writeRequest(remoteFile, data, len); bool ok=waitTxEvent(); int countRepeat = NUM_REPEAT_WRITE; //repeatwrite until is ok or error while ((!ok) && (countRepeat>0)) { MyDebug.logMsg( type_log_error, origin_msg_tftp , STR_TRY_TO_REPEAT_WRITE); Q_EMIT tFpgaProg->writeRequest(remoteFile, data, len); ok=waitTxEvent(); if (WAIT_TIME_AFTER_WR>0) waitEventTimeout(WAIT_TIME_AFTER_WR); countRepeat--; } if (countRepeat==0) { MyDebug.logMsg( type_log_error, origin_msg_tftp , STR_TRY_TOO_WRITE); return false; } if (WAIT_TIME_AFTER_WR>0) waitEventTimeout(WAIT_TIME_AFTER_WR); return ok; } if (simulate) { engine.simulateWrite(address, data, len); } if (WAIT_TIME_AFTER_WR>0) waitEventTimeout(WAIT_TIME_AFTER_WR); return true; } //*********************************************************************** // rtgRead // Input parameters: address = address to read // data = ouput buffer // len = buffer's length // // Returns: true, no error, operation completed // false, generic error into read function //*********************************************************************** virtual bool rtgRead(uint32_t address, void* data, unsigned int len=256) override { if (tftp && !simulate) { QString remoteFile= ""; if (SRIOMemory) { remoteFile=QString("$SRIO:%1/0x%2+%3").arg(profile.slotAddress).arg(BASE_ADDRESS_MEM+address, 0, 16).arg(len); } else { remoteFile=QString("$SRIO:%1/0x%2+%3").arg(profile.slotAddress).arg(BASE_ADDRESS_IF+address, 0, 16).arg(len); } MyDebug.logMsg( type_log_info, origin_msg_srio , QString(STR_COMMAND_READ).arg(remoteFile)); Q_EMIT tFpgaProg->readRequest(remoteFile, data, len); bool ok=waitRxEvent(); int countRepeat = NUM_REPEAT_READ; //repeatwrite until is ok or error while ((!ok) && (countRepeat>0)) { MyDebug.logMsg( type_log_error, origin_msg_tftp , STR_TRY_TO_REPEAT_READ); Q_EMIT tFpgaProg->readRequest(remoteFile, data, len); ok=waitTxEvent(); if (WAIT_TIME_AFTER_RD>0) waitEventTimeout(WAIT_TIME_AFTER_RD); countRepeat--; } if (countRepeat==0) { MyDebug.logMsg( type_log_error, origin_msg_tftp , STR_TRY_TOO_READ ); return false; } if (WAIT_TIME_AFTER_RD>0) waitEventTimeout(WAIT_TIME_AFTER_RD); return ok; } if (simulate) { engine.simulateRead(address, data, len); } if (WAIT_TIME_AFTER_RD>0) waitEventTimeout(WAIT_TIME_AFTER_RD); return true; } //*********************************************************************** // rtgRead Direct // Input parameters: address = address to read // data = ouput buffer // len = buffer's length // // Returns: true, no error, operation completed // false, generic error into read function //*********************************************************************** virtual bool rtgReadDirect(uint32_t address, void* data, unsigned int len=256) override { if (tftp && !simulate) { QString remoteFile= ""; remoteFile=QString("$SRIO:%1/0x%2+%3").arg(profile.slotAddress).arg(address, 0, 16).arg(len); if (LOG_LEVEL>log_level_min) { MyDebug.logMsg( type_log_info, origin_msg_srio , QString(STR_COMMAND_READ_DIRECT).arg(remoteFile)); } Q_EMIT tFpgaProg->readRequest(remoteFile, data, len); return waitRxEvent(); } waitEventTimeout(100); return true; } #if 0 virtual bool rtgErase(uint32_t address, void* data, unsigned int len=256) override { engine.simulateErase(address, data, len); waitEventTimeout(100); return true; } #endif virtual bool rtgWaitEvent(unsigned int timeout_ms=5000) { if (timeout_ms==0) return true; /*event_data_t tmp=*/waitEvent(timeout_ms); return true; } virtual void notify(FpgaFlashInterface::step_t sts, int progress, const char* msg, ...) override { va_list args; va_start(args, msg); notifyStatus(sts, progress, QString::vasprintf(msg, args)); MyDebug.logMsg( type_log_info, origin_msg_engine , QString::vasprintf(msg, args)); va_end(args); } virtual void log(type_log_t level, const char* msg, ...) override //level<0: error, ==0: info, >0: success { va_list args; va_start(args, msg); MyDebug.logMsg(level, origin_msg_engine, QString::vasprintf(msg, args)); va_end(args); } struct state_t { FpgaProgrammerDriver* driver; int timer_pending; int state; }; state_t s; volatile unsigned sectorSize; volatile unsigned int baseAddress; FpgaProgrammerImplementation(): //tId(0), tFpgaProg(0), tftp(0), simulate(false) { //pTimer.setSingleShot(true); } //*********************************************************************** // Set profile of the selected FPGA-flash // Input parameters: _profile = parameters of selected fpga/flash // // Returns: //*********************************************************************** void setProfile(const FpgaFlashProfile& _profile) { profile = _profile; profile.BaseOffset = profile.address_start_area; // profile.SectorSize_KBytes = FLASH_SECTOR_DIM; sectorSize = FLASH_SECTOR_DIM; } //*********************************************************************** // Inizialize programmer with default parameters // Input parameters: _profile = parameters of selected fpga/flash // // Returns: //*********************************************************************** void initialize(unsigned int /*write_size*/, int sector_size, int base_address) { sectorSize=sector_size; baseAddress=base_address; profile.reset(); profile.SectorSize_KBytes = sectorSize; profile.BaseAddress = baseAddress; profile.BaseOffset = 0; } void waitTxCompleted() { waitEventTimeout(); } void waitSpiBusy() { event_data_t evn=waitEventTimeout(); if (evn.type!=evn_read_completed) throw my_exception(1); } void waitSpiRdy() { event_data_t evn=waitEventTimeout(); if (evn.type!=evn_read_completed) throw my_exception(1); } enum states_t { s_reset_wait=1, s_id_request, s_progr_enable, s_erase_block, s_program_block }; void stateReset() { } void state_RequestId() { } void timerExpired() { //QCoreApplication::postEvent(this, new MyEvent(evn_timeout)); } QQueue qevn; bool waitTxEvent(unsigned int tm_ms=0) { event_data_t e=waitEvent(tm_ms); return e.type==evn_write_completed; } bool waitRxEvent(unsigned int tm_ms=0) { event_data_t e=waitEvent(tm_ms); return e.type==evn_read_completed; } event_data_t waitEventTimeout(unsigned int tm_ms=5000) { if (tm_ms==0) { static event_data_t myeven; myeven.type=evn_timeout; return myeven; } return waitEvent(tm_ms); } event_data_t waitEvent(unsigned int tm_ms=0) { if (pTimer->isActive()) { pTimer->stop(); } #if 0 if (tId) { killTimer(tId); tId=0; } #endif #if 0 if (!qevn.isEmpty()) { event_data_t evn=qevn.takeFirst(); //if (evn.type==evn_abort) // throw(std::system_error(1)); MyDebug()<<"Event pending:"<waitStart(tm_ms); pTimer->start(tm_ms); } retry_: QEventLoop::ProcessEventsFlags flags=QEventLoop::WaitForMoreEvents; bool processed=this->eventDispatcher()->processEvents(flags); int count_event = 0; if (processed) { if (!qevn.isEmpty()) { count_event = qevn.count(); #if 0 if (tId) { killTimer(tId); tId=0; } #endif pTimer->stop(); event_data_t evn=qevn.takeFirst(); if (evn.type==evn_break) { MyDebug.logMsg(type_log_error, origin_msg_generic, STR_EXCP_BREAK); throw my_exception(3); } switch (evn.type) { case evn_timeout: MyDebug.logMsg(type_log_info, origin_msg_event, QString(STR_TIMEOUT_QUEUE).arg(count_event)); break; case evn_write_completed: MyDebug.logMsg(type_log_info, origin_msg_event, QString(STR_WRITE_COMPLETED_QUEUE).arg(count_event)); break; case evn_read_completed: MyDebug.logMsg(type_log_info, origin_msg_event, QString(STR_READ_COMPLETED_QUEUE).arg(count_event)); break; default: MyDebug.logMsg(type_log_info, origin_msg_event, QString(STR_QUEUE_EVENT).arg(evn.type).arg(count_event)); break; } return evn; } else { MyDebug.logMsg(type_log_info, origin_msg_event, STR_QUEUE_EVENT_EMPTY); } } goto retry_; return event_data_t(QEvent::User, 0, 0); } void customEvent(QEvent* evn) override { if (evn->type()>=QEvent::User) { MyEvent* me=reinterpret_cast(evn); if (me) qevn.append(me->data); } } void timerEvent(QTimerEvent* evn) { if (evn->timerId()==pTimer->timerId()) //tId) { //tId=0; //killTimer(tId); pTimer->stop(); static event_data_t data; data.type=evn_timeout; qevn.append(data); } else QThread::timerEvent(evn); } void run() { pTimer=new QTimer(this); pTimer->setSingleShot(true); pTimer->moveToThread(this); connect(pTimer, &QTimer::timeout, this, [this]() { QCoreApplication::postEvent(this, new MyEvent(evn_timeout), Qt::HighEventPriority); }); execute(); } void notifyStatus(int sts, int progress, const QString& msg) { Q_EMIT tFpgaProg->statusChanged(sts, progress, msg); } //*********************************************************************** // Start program flash with erase first // Input parameters: // // Returns: //*********************************************************************** void startProgram() { //setto il profilo di fpga da processare engine.setProfile(profile); event_data_t evn; //reset dummy_buffer for(unsigned int i=0; idata, DIM_FILE_WRITE); #else ok_fpgaProgram = engine.fpgaProgram(binary_data->data, binary_data->size); #endif } for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(0, -1, STR_STOP_PROGRAM); break; } if (evn.type==evn_timeout) { QString msg = QString("%1").arg(MyDebug.getLastError()); notifyStatus(0, -1, msg); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } } //*********************************************************************** // Start esare flash // Input parameters: // // Returns: //*********************************************************************** void startErase() { //setto il profilo di fpga da processare //FpgaFlashProfile profile={sectorSize, 0, baseAddress, 1}; engine.setProfile(profile); //notifico lo stato di start notifyStatus(s_erase, -1, STR_START_ERASE); elapsedTimer.start(); //inizio la cancellazione engine.fpgaErase(sizeof dummy_buffer); event_data_t evn; for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(s_erase, -1, STR_STOP_ERASE); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } } //*********************************************************************** // Start reset flash communication with erase first // Input parameters: // // Returns: //*********************************************************************** void startReset() { //setto il profilo di fpga da processare //FpgaFlashProfile profile={sectorSize, 0, baseAddress, 1}; engine.setProfile(profile); //notifico lo stato di start notifyStatus(s_reset, -1, STR_START_RESET); elapsedTimer.start(); //inizio la cancellazione engine.fpgaReset(); event_data_t evn; for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(s_reset, -1, STR_STOP_RESET); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } } //*********************************************************************** // Start read flash ID // Input parameters: // // Returns: //*********************************************************************** void startReadFlashID() { //setto il profilo di fpga da processare //FpgaFlashProfile profile={sectorSize, 0, baseAddress, 1}; engine.setProfile(profile); //notifico lo stato di start notifyStatus(s_read_id, -1, STR_START_READ_FLASH_ID); elapsedTimer.start(); //inizio la cancellazione engine.readFlashID(); event_data_t evn; for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(s_read_id, -1, STR_STOP_READ_FLASH_ID); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } } //*********************************************************************** // Return spi status // Input parameters: // // Returns: //*********************************************************************** void startStatusSPI() { //setto il profilo di fpga da processare //FpgaFlashProfile profile={sectorSize, 0, baseAddress, 1}; engine.setProfile(profile); //notifico lo stato di start notifyStatus(s_status, -1, STR_START_READ_SPI_STATUS); elapsedTimer.start(); //inizio la cancellazione engine.readStatusSPI(); event_data_t evn; for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(s_status, -1, STR_STOP_READ_SPI_STATUS); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } } //*********************************************************************** // terminate connection // Input parameters: // // Returns: //*********************************************************************** /*void startTerminate() { //setto il profilo di fpga da processare //FpgaFlashProfile profile={sectorSize, 0, baseAddress, 1}; engine.setProfile(profile); //notifico lo stato di start notifyStatus(s_end, -1, STR_START_TERMINATE); elapsedTimer.start(); //inizio la cancellazione engine.fpgaTerminate(); event_data_t evn; for(;;) { evn=waitEvent(5000); if (evn.type==evn_break) { //se l'evento è break, vuol dire che ha finito di processare notifyStatus(s_end, -1, STR_STOP_TERMINATE); break; } //verifico se è stato richiesto un interrupt if (isInterruptionRequested()) { return; } } }*/ //*********************************************************************** // main control for all operations // Input parameters: // // Returns: //*********************************************************************** void execute() { engine.setInterface(this); for(;;) { try { //attendo il prossimo evento, verificare il funzionamento event_data_t evn=waitEvent(); //5000); //se l'evento da processare non è l'evento di start, allora rimango in stato di idle. if (evn.type!=evn_start && evn.type!=evn_erase && evn.type!=evn_reset && evn.type!=evn_readFlashID && evn.type!=evn_readStatusSPI && evn.type!=evn_terminate ) { notifyStatus(0, -1, "idle"); if (evn.type==evn_error) { notifyStatus(0, -1, QString(STR_ENGINE_ERROR).arg(MyDebug.getLastError())); break; } continue; } try { if (evn.type==evn_start) { startProgram(); } else if (evn.type==evn_erase) { startErase(); } else if (evn.type==evn_reset) { startReset(); } else if (evn.type==evn_readFlashID) { startReadFlashID(); } else if (evn.type==evn_readStatusSPI) { startStatusSPI(); } } catch(my_exception& e) { //notifico eccezione durante la programmazione notifyStatus(0, -1, QString(STR_EXCEPTION).arg(e.code)); } } catch(my_exception& e) { notifyStatus(0, -1, QString(STR_EXCEPTION).arg(e.code)); } } } void pushEvent(QEvent* evn) { QApplication::sendEvent(this, evn); } }; FgpaProgrammer::FgpaProgrammer(QObject *parent) : QObject(parent), p_(*new FpgaProgrammerImplementation) { p_.tFpgaProg=this; p_.initialize(0, 1, 0); p_.moveToThread(&p_); p_.start(); connect(this, &FgpaProgrammer::writeRequest, this, &FgpaProgrammer::onWriteRequest, Qt::QueuedConnection); connect(this, &FgpaProgrammer::readRequest, this, &FgpaProgrammer::onReadRequest, Qt::QueuedConnection); } //*********************************************************************** // Set simulate flag for simulate write/read info // Input parameters: // // Returns: //*********************************************************************** void FgpaProgrammer::setSimulate(bool enable) { p_.simulate=enable; } //*********************************************************************** // Set SRIOMemory flag for select BASE_ADDRESS_IF(0x47000000) or BASE_ADDRESS_MEM(0xA0000000) // Input parameters: // // Returns: //*********************************************************************** void FgpaProgrammer::setSRIOMemory(bool enable) { p_.SRIOMemory=enable; } //*********************************************************************** // Connect programmer with tftp instance // Input parameters: // // Returns: //*********************************************************************** void FgpaProgrammer::setTftp(FpgaBridgeTftp *tftp) { if (tftp!=p_.tftp) { p_.tftp=tftp; connect(p_.tftp, &FpgaBridgeTftp::sendTerminated, this, [this]() { if (p_.engine.getEnableMsgBlk()) QMessageBox::information(0, STR_TFTP_WRITE_COMPLETED, STR_TFTP_WRITE_COMPLETED); this->writeCompleted(true); }); connect(p_.tftp, &FpgaBridgeTftp::sendError, this, [this]() { MyDebug.logMsg(type_log_error, origin_msg_tftp, STR_TFTP_SEND_ERROR); QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); }); connect(p_.tftp, &FpgaBridgeTftp::transferError, this, [this](int, const QString&) { MyDebug.logMsg(type_log_error, origin_msg_tftp, STR_TFTP_TRANFER_ERROR); QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); }); connect(p_.tftp, &FpgaBridgeTftp::receiveTerminated, this, [this](int, const QHostAddress) { MyDebug.logMsg(type_log_info, origin_msg_tftp, STR_TFTP_RECEIVE_COMPLETED); QCoreApplication::postEvent(&p_, new MyEvent(evn_read_completed), Qt::HighEventPriority); }); } } void FgpaProgrammer::setDriver(FpgaProgrammerDriver* driver) { p_.s.driver=driver; } void FgpaProgrammer::start(unsigned long write_size, unsigned int sectorSize, unsigned int baseOffset) { MyDebug.logMsg(type_log_info, origin_msg_generic, QString(STR_START_REQUEST_SIZE).arg(write_size)); p_.initialize(write_size, sectorSize, baseOffset); if (write_size==0) QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); else QCoreApplication::postEvent(&p_, new MyEvent(evn_start), Qt::HighEventPriority); } void FgpaProgrammer::start(const FpgaFlashProfile& _profile) { MyDebug.logMsg(type_log_info, origin_msg_generic, QString(STR_START_REQUEST_FROM).arg(_profile.address_start_area)); p_.setProfile(_profile); QCoreApplication::postEvent(&p_, new MyEvent(evn_start), Qt::HighEventPriority); } void FgpaProgrammer::erase(unsigned long erase_size, unsigned int sectorSize, unsigned int baseOffset) { MyDebug.logMsg(type_log_info, origin_msg_generic, QString(STR_START_ERASE_SIZE).arg(erase_size)); p_.initialize(erase_size, sectorSize, baseOffset); //TODO: agigungere la parte di codice per chiamare la funzione erase QCoreApplication::postEvent(&p_, new MyEvent(evn_erase), Qt::HighEventPriority); } void FgpaProgrammer::reset(const FpgaFlashProfile& _profile) { MyDebug.logMsg(type_log_info, origin_msg_generic, STR_START_RESET); p_.setProfile(_profile); QCoreApplication::postEvent(&p_, new MyEvent(evn_reset), Qt::HighEventPriority); } void FgpaProgrammer::readFlashID(const FpgaFlashProfile& _profile) { MyDebug.logMsg(type_log_info, origin_msg_generic, STR_START_READ_FLASH_ID); p_.setProfile(_profile); QCoreApplication::postEvent(&p_, new MyEvent(evn_readFlashID), Qt::HighEventPriority); } void FgpaProgrammer::readStatusSPI(const FpgaFlashProfile& _profile) { MyDebug.logMsg(type_log_info, origin_msg_generic, STR_START_READ_SPI_STATUS); p_.setProfile(_profile); QCoreApplication::postEvent(&p_, new MyEvent(evn_readStatusSPI), Qt::HighEventPriority); } void FgpaProgrammer::terminate(const FpgaFlashProfile& _profile) { MyDebug.logMsg(type_log_info, origin_msg_generic, STR_START_TERMINATE); p_.setProfile(_profile); QCoreApplication::postEvent(&p_, new MyEvent(evn_terminate), Qt::HighEventPriority); } void FgpaProgrammer::abort(const FpgaFlashProfile& _profile) { p_.setProfile(_profile); } void FgpaProgrammer::writeCompleted(bool ok) { if (!ok) QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); else QCoreApplication::postEvent(&p_, new MyEvent(evn_write_completed), Qt::HighEventPriority); } void FgpaProgrammer::readCompleted(bool ok) { if (!ok) QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); else QCoreApplication::postEvent(&p_, new MyEvent(evn_read_completed), Qt::HighEventPriority); } #if 0 void FgpaProgrammer::eraseCompleted(bool ok, const void* data, unsigned int len) { QCoreApplication::postEvent(&p_, new MyEvent(evn_break), Qt::HighEventPriority); } #endif void FgpaProgrammer::onWriteRequest(const QString& a, const void* src, unsigned int len) { p_.tftp->sendMemory((void*)src, len, a); } void FgpaProgrammer::onReadRequest(const QString& a, void* dst, unsigned int len) { p_.tftp->receiveMemory(dst, len, a); }