otsdaq_demo  v2_05_02_indev
FEOtsEthernetProgramInterface_interface.cc
1 #include <dirent.h> /*DIR and dirent*/
2 #include <iostream>
3 #include <set>
4 #include "otsdaq-demo/FEInterfaces/FEOtsEthernetProgramInterface.h"
5 #include "otsdaq/Macros/CoutMacros.h"
6 #include "otsdaq/Macros/InterfacePluginMacros.h"
7 #include "otsdaq/MessageFacility/MessageFacility.h"
8 
9 using namespace ots;
10 
11 #undef __MF_SUBJECT__
12 #define __MF_SUBJECT__ "FE-FEOtsEthernetProgramInterface"
13 
14 #define PROGRAM_FILE_PATH std::string(__ENV__("OTS_FIRMWARE_PROGRAM_FILE_PATH")) + "/"
15 
17 // ADDRESS SPACE
18 // http://otsdaq.fnal.gov/docs/oei_address_space.html
19 // field size (bits)
20 #define UDP_CORE_BLOCK_ADDRESS ((uint64_t)(0x2) << 32)
21 #define FLASH_COMMAND_BASE 0
22 // 0 -- START_FLASH_COMMAND_TRIGGER
23 // 1 -- FLASH_BASE_ADDRESS
24 // 2 -- FLASH_DATA_SIZE
25 // 3 -- FLASH_WRITE_MODE
26 #define FLASH_WRITE_DATA 4
27 #define FLASH_WRITE_STATUS 9
28 
29 // end ADDRESS SPACE
31 
32 //==============================================================================
33 FEOtsEthernetProgramInterface::FEOtsEthernetProgramInterface(
34  const std::string& interfaceUID,
35  const ConfigurationTree& theXDAQContextConfigTree,
36  const std::string& interfaceConfigurationPath)
37  : Socket(theXDAQContextConfigTree.getNode(interfaceConfigurationPath)
38  .getNode("HostIPAddress")
39  .getValue<std::string>(),
40  theXDAQContextConfigTree.getNode(interfaceConfigurationPath)
41  .getNode("HostPort")
42  .getValue<unsigned int>())
43  , FEOtsUDPTemplateInterface(
44  interfaceUID, theXDAQContextConfigTree, interfaceConfigurationPath)
45 {
46  // register FE Macro Functions
47  registerFEMacroFunction(
48  "getListOfProgramFiles" /*feMacroName*/,
49  static_cast<FEVInterface::frontEndMacroFunction_t>(
50  &FEOtsEthernetProgramInterface::getListOfProgramFiles) /*feMacroFunction*/,
51  std::vector<std::string>{} /*namesOfInputArgs*/,
52  std::vector<std::string>{"listOfProgramFiles"} /*namesOfOutputArgs*/,
53  10 /*requiredUserPermissions*/);
54 
55  // for testing FE Macro Functions
56  std::vector<frontEndMacroArg_t> argsIn;
57  // argsIn.push_back(frontEndMacroInArg_t("arg1","val1"));
58  // argsIn.push_back(frontEndMacroInArg_t("arg2","val2"));
59  // argsIn.push_back(frontEndMacroInArg_t("arg3","val3"));
60 
61  __COUT__ << std::endl;
62  __COUT__ << std::endl;
63  __COUT__ << __COUT_HDR_P__ << "# of args = " << argsIn.size() << std::endl;
64  for(auto& argIn : argsIn)
65  __COUT__ << argIn.first << ": " << argIn.second << std::endl;
66 
67  std::vector<std::string> returnStrings;
68  std::vector<frontEndMacroArg_t> argsOut;
69 
70  std::string outputArgs = "listOfProgramFiles"; //"oarg1,oarg2,";
71  {
72  std::istringstream inputStream(outputArgs);
73  std::string argName;
74  while(getline(inputStream, argName, ','))
75  {
76  __COUT__ << "argName " << argName << std::endl;
77 
78  returnStrings.push_back(std::string("test"));
79  argsOut.push_back(FEVInterface::frontEndMacroArg_t(
80  argName, returnStrings[returnStrings.size() - 1]));
81  //
82  // __COUT__ << argsOut[argsOut.size()-1].first << std::endl;
83  // __COUT__ << argsOut[argsOut.size()-1].second << std::endl;
84  }
85  }
86 
87  auto mapOfFEMacroIt = mapOfFEMacroFunctions_.find("getListOfProgramFiles");
88  if(mapOfFEMacroIt != mapOfFEMacroFunctions_.end())
89  {
90  (this->*(mapOfFEMacroIt->second.macroFunction_))(
91  mapOfFEMacroIt->second, argsIn, argsOut);
92  __COUT__ << "Made it " << std::endl;
93  for(auto& arg : argsOut)
94  __COUT__ << arg.first << ": " << arg.second << std::endl;
95  }
96 }
97 
98 //==============================================================================
99 FEOtsEthernetProgramInterface::~FEOtsEthernetProgramInterface(void) {}
100 
101 //==============================================================================
102 void FEOtsEthernetProgramInterface::configure(void)
103 {
104  FEOtsUDPTemplateInterface::configure();
105  // __COUT__ << "configure" << std::endl;
106  // __COUT__ << "Clearing receive socket buffer: " <<
107  // OtsUDPHardware::clearReadSocket() << " packets cleared." << std::endl;
108  //
109  // std::string sendBuffer;
110  // std::string recvBuffer;
111  //
112  // __COUT__ << "Setting Destination IP: " <<
113  // theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode("StreamToIPAddress").getValue<std::string>()
114  // << std::endl;
115  // __COUT__ << "And Destination Port: " <<
116  // theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode("StreamToPort").getValue<unsigned
117  // int>()
118  // << std::endl;
119  //
120  // sendBuffer.resize(0);
121  // OtsUDPFirmwareCore::setupBurstDestination(sendBuffer,
122  // theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode("StreamToIPAddress").getValue<std::string>(),
123  // theXDAQContextConfigTree_.getNode(theConfigurationPath_).getNode("StreamToPort").getValue<uint64_t>()
124  // );
125  // OtsUDPHardware::write(sendBuffer);
126  //
127  // //
128  // //
129  // __COUT__ << "Reading back burst dest MAC/IP/Port: " << std::endl;
130  // sendBuffer.resize(0);
131  // OtsUDPFirmwareCore::readBurstDestinationMAC(sendBuffer);
132  // OtsUDPHardware::read(sendBuffer,recvBuffer);
133  // sendBuffer.resize(0);
134  // OtsUDPFirmwareCore::readBurstDestinationIP(sendBuffer);
135  // OtsUDPHardware::read(sendBuffer,recvBuffer);
136  // sendBuffer.resize(0);
137  // OtsUDPFirmwareCore::readBurstDestinationPort(sendBuffer);
138  // OtsUDPHardware::read(sendBuffer,recvBuffer);
139  //
140  //
141  // sendBuffer.resize(0);
142  // OtsUDPFirmwareCore::read(sendBuffer,0x5);
143  // OtsUDPHardware::read(sendBuffer,recvBuffer);
144  //
145  // //Run Configure Sequence Commands
146  // runSequenceOfCommands("LinkToConfigureSequence");
147  //
148  __COUT__ << "Done with configuring." << std::endl;
149 }
150 
151 //==============================================================================
152 // void FEOtsEthernetProgramInterface::configureDetector(const DACStream& theDACStream)
153 //{
154 // __COUT__ << "\tconfigureDetector" << std::endl;
155 //}
156 
158 // void FEOtsEthernetProgramInterface::halt(void)
159 //{
160 // __COUT__ << "\tHalt" << std::endl;
161 // stop();
162 //}
163 //
165 // void FEOtsEthernetProgramInterface::pause(void)
166 //{
167 // __COUT__ << "\tPause" << std::endl;
168 // stop();
169 //}
170 //
172 // void FEOtsEthernetProgramInterface::resume(void)
173 //{
174 // __COUT__ << "\tResume" << std::endl;
175 // start("");
176 //}
177 //
179 // void FEOtsEthernetProgramInterface::start(std::string )//runNumber)
180 //{
181 // __COUT__ << "\tStart" << std::endl;
182 //
183 //
184 // //Run Start Sequence Commands
185 // runSequenceOfCommands("LinkToStartSequence");
186 //
187 // OtsUDPHardware::write(OtsUDPFirmwareCore::startBurst());
188 //}
189 //
191 // void FEOtsEthernetProgramInterface::stop(void)
192 //{
193 // __COUT__ << "\tStop" << std::endl;
194 //
195 // //Run Stop Sequence Commands
196 //
197 // runSequenceOfCommands("LinkToStopSequence");
198 //
199 // OtsUDPHardware::write(OtsUDPFirmwareCore::stopBurst());
200 //}
201 //
203 // bool FEOtsEthernetProgramInterface::running(void)
204 //{
205 // __COUT__ << "\running" << std::endl;
206 //
207 // // //example!
208 // // //play with array of 8 LEDs at address 0x1003
209 //
210 // //
211 // // bool flashLEDsWhileRunning = false;
212 // // if(flashLEDsWhileRunning)
213 // // {
214 // // std::string sendBuffer;
215 // // int state = -1;
216 // // while(WorkLoop::continueWorkLoop_)
217 // // {
218 // // //while running
219 // // //play with the LEDs at address 0x1003
220 // //
221 // // ++state;
222 // // if(state < 8)
223 // // {
224 // // sendBuffer.resize(0);
225 // // OtsUDPFirmwareCore::writeAdvanced(sendBuffer, 0x1003,1<<state);
226 // // OtsUDPHardware::write(sendBuffer);
227 // // }
228 // // else if(state%2 == 1 && state < 11)
229 // // {
230 // // sendBuffer.resize(0);
231 // // OtsUDPFirmwareCore::writeAdvanced(sendBuffer, 0x1003, 0xFF);
232 // // OtsUDPHardware::write(sendBuffer);
233 // // }
234 // // else if(state%2 == 0 && state < 11)
235 // // {
236 // // sendBuffer.resize(0);
237 // // OtsUDPFirmwareCore::writeAdvanced(sendBuffer, 0x1003,0);
238 // // OtsUDPHardware::write(sendBuffer);
239 // // }
240 // // else
241 // // state = -1;
242 // //
243 // // sleep(1);
244 // // }
245 // // }
246 //
247 // return false;
248 //}
249 
250 //==============================================================================
251 // getListOfProgramFiles
252 // 0 args in
253 // 1 args out
254 // listOfProgramFiles = comma-separated list of programmable file names
255 //
256 // Note: path is from environment variable OTS_FIRMWARE_PROGRAM_FILE_PATH
257 void FEOtsEthernetProgramInterface::getListOfProgramFiles(__ARGS__)
258 {
259  std::string dirpath = PROGRAM_FILE_PATH;
260  DIR* pDIR;
261  struct dirent* entry;
262  bool isDir;
263  std::set<std::string> listOfProgramFiles;
264 
265  if((pDIR = opendir(dirpath.c_str())))
266  {
267  // add every program file to return set
268  // valid program files are *.bin
269 
270  while((entry = readdir(pDIR)))
271  {
272  //__COUT__ << int(entry->d_type) << " " << entry->d_name << "\n" << std::endl;
273  if(entry->d_name[0] != '.' &&
274  (entry->d_type == 0 || // 0 == UNKNOWN (which can happen - seen in SL7 VM)
275  entry->d_type == 4 || entry->d_type == 8))
276  {
277  __COUT__ << int(entry->d_type) << " " << entry->d_name << " "
278  << std::string(entry->d_name).find(".bin") << " "
279  << strlen(entry->d_name) - 4 << "\n"
280  << std::endl;
281 
282  isDir = false;
283 
284  if(entry->d_type == 0)
285  {
286  // unknown type .. determine if directory
287  DIR* pTmpDIR = opendir((dirpath + entry->d_name).c_str());
288  if(pTmpDIR)
289  {
290  isDir = true;
291  closedir(pTmpDIR);
292  }
293  // else //assume file
294  }
295  else if(entry->d_type == 4)
296  isDir = true;
297 
298  if(!isDir && // isBinFile
299  std::string(entry->d_name).find(".bin") == strlen(entry->d_name) - 4)
300  listOfProgramFiles.insert(entry->d_name);
301  }
302  }
303 
304  closedir(pDIR);
305  }
306  else
307  {
308  __COUT__ << "Failed to access directory contents!" << std::endl;
309  }
310 
311  auto& returnString = getFEMacroArgument(argsOut, "listOfProgramFiles");
312  returnString = "";
313  for(const auto& name : listOfProgramFiles)
314  {
315  if(returnString.size())
316  returnString += ",";
317  __COUT__ << "name " << name << std::endl;
318  returnString += name;
319  }
320 }
321 
322 //==============================================================================
323 // loadProgramFile
324 // 1 args in
325 // programFile = filename of programmable file
326 // 1 args out
327 // listOfProgramFiles = comma-separated list of programmable file names
328 void FEOtsEthernetProgramInterface::loadProgramFile(__ARGS__)
329 {
330  // Steps:
331  // - open file
332  //
333 
334  std::string file = "";
335  // remove special characters
336  {
337  bool prevWasDot = false;
338  std::string sourceStr = getFEMacroConstArgument(argsIn, "programFile");
339  for(const auto& c : sourceStr)
340  {
341  if(c == '.')
342  {
343  if(!prevWasDot && // previous source char was not dot
344  file.size() && // there are other chars in filename
345  file[file.size() - 1] !=
346  '.') // the prev char in filename is not a '.'
347  file += c;
348  // else skip (prevent multiple dots)
349 
350  prevWasDot = true;
351  }
352  else if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
353  (c >= '0' && c <= '9') || c == '_' || c == '-')
354  file += c;
355  // else skip (non-alphanumeric - _ .)
356  }
357  }
358  std::string path = PROGRAM_FILE_PATH + file;
359 
360  __COUT__ << "Programmable file path: " << path << std::endl;
361 
362  FILE* bitstream = fopen(path.c_str(), "rb");
363  if(!bitstream)
364  {
365  __SS__ << "Failed to read bitsream";
366  __COUT_ERR__ << "\n" << ss.str();
367  __SS_THROW__;
368  }
369 
370  // keep as placeholder for potentially handling mcs files (in addition to bin files)
371  /*if (path.Find(".mcs") != -1) {
372  //if the path has .mcs in it assume it is an mcs file and generate a bin file
373  FILE * binfile = fopen("generated_from_mcs.bin", "w");
374  char line[100];
375  char lineoutput[16];
376  fgets(line, 100, bitstream);//skip first line
377  int i;
378  bool has_d;
379  //char dstr[100];
380  while (fgets(line, 100, bitstream)) {
381  i = 9;
382  has_d = 0;
383  while (i < 41) {
384  lineoutput[(i - 9) / 2] = hextoint(line[i]) * 16 + hextoint(line[i + 1]);
385  if (hextoint(line[i + 1]) == 13) has_d = 1;
386  i += 2;
387  }
388  if (has_d) {
389  LineOut(line);
390  break;
391  }
392  fwrite(lineoutput, sizeof(char), 16, binfile);
393  }
394  fclose(bitstream);
395  fclose(binfile);
396  //update the bitsream variable for the rest of the function to the .bin file
397  bitstream = fopen("generated_from_mcs.bin", "r");
398  }*/
399 
400  fseek(bitstream, 0, SEEK_END);
401  uint64_t bSize = ftell(bitstream) / 8; // divide by 8 to get number of qwds
402  rewind(bitstream);
403 
404  __COUT__ << "Programmable file size [quad-words]: " << bSize << std::endl;
405 
406  std::string sendBuffer;
407  std::string recvBuffer;
408  uint64_t recvQuadWord;
409 
410  // send reset command to Ethernet block which will reset flash block
411  OtsUDPFirmwareCore::softEthernetReset(sendBuffer);
412  OtsUDPHardware::write(sendBuffer);
413  OtsUDPFirmwareCore::clearEthernetReset(sendBuffer);
414  OtsUDPHardware::write(sendBuffer);
415 
416  // sleep to allow reset to complete
417  // sleep(2); //seconds
418 
419  // setup address and flash write mode
420  {
421  std::vector<uint64_t> dataVec = {
422  0, // first (ie at addr 0) quadwrod all 0 because it is the start-command
423  // trigger
424  0, // write address(second qwd)
425  bSize, // size
426  3 // mode is erase then write
427  };
428 
429  sendBuffer.resize(0);
430  OtsUDPFirmwareCore::writeAdvanced(
431  sendBuffer,
432  UDP_CORE_BLOCK_ADDRESS /*block*/ | FLASH_COMMAND_BASE /*addr*/,
433  dataVec /*data*/);
434  OtsUDPHardware::write(sendBuffer);
435  }
436 
437  // now actually send bitstream data
438  const size_t page_size = 512;
439  //const size_t MAX_TCP_SIZE = 5000;
440 
441  char page_data[page_size];
442  //unsigned char next_page_recv_buff[MAX_TCP_SIZE];
443  //int next_page_recv_len;
444  int page = 0;
445 
446  // int num_page_ready_returns = 0;
447  // int bytesoff = 0;
448  // int thisbytesoff, returnaddr;
449  size_t result;
450  while(1)
451  {
452  if(page % 5120 == 0) // debug/status print out
453  {
454  char str[100];
455  sprintf(str, "Place: %lX Page: %d", ftell(bitstream), page);
456  // sprintf(str, "Place: %X Page: %d Returns: %d",
457  // ftell(bitstream),page,num_page_ready_returns);
458  __COUT__ << str << std::endl;
459  ;
460  }
461 
462  // if (page == 512) break;
463 
464  // get block of data from file
465  result = fread(page_data, 1, page_size, bitstream);
466  if(result != page_size) // if block of data is not full page size, handle it.
467  {
468  if(result == 0)
469  {
470  __COUT__ << "Done" << std::endl;
471  break;
472  }
473  else // 0-fill
474  {
475  for(unsigned int i = result; i < page_size; i++)
476  page_data[i] = 0;
477  }
478  }
479 
480  // at this point have block of data
481  // check if ready to send
482 
483  if(page > 1) // if 3rd page and beyond, handle differently
484  {
485  // after writing first two pages, now waits to write next page
486  // until gets response from FPGA indicating to send next page
487 
488  // check status
489  sendBuffer.resize(0);
490  OtsUDPFirmwareCore::read(
491  sendBuffer,
492  UDP_CORE_BLOCK_ADDRESS /*block*/ | FLASH_WRITE_STATUS /*addr*/);
493 
494  try
495  {
496  OtsUDPHardware::read(sendBuffer, recvQuadWord);
497  }
498  catch(...)
499  {
500  // close file before re-throwing
501  std::fclose(bitstream);
502  throw;
503  }
504 
505  if(recvQuadWord != 0x01)
506  {
507  // no reply or incorrect reply
508  if(recvQuadWord == 3)
509  __COUT_ERR__ << "No active command" << std::endl;
510  else
511  __COUT_ERR__ << "Error in writing multiple pages" << std::endl;
512 
513  break;
514  }
515  // else {
516  // num_page_ready_returns++;
517  // returnaddr = 0 | (int)next_page_recv_buff[21]<<16 |
518  //(int)next_page_recv_buff[20]<<8 | (int)next_page_recv_buff[19];
519  // thisbytesoff = ftell(bitstream) - returnaddr;
520  // if (thisbytesoff != bytesoff) {
521  // sprintf(str, "New offset: %X", thisbytesoff);
522  // __COUT__ << str << std::endl;
523  // bytesoff = thisbytesoff;
524  // }
525  // }
526 
527  //__COUT__ << "Got next page response" << std::endl;
528  }
529 
530  // ready, so send data
531  sendBuffer.resize(0);
532  OtsUDPFirmwareCore::writeAdvanced(
533  sendBuffer,
534  UDP_CORE_BLOCK_ADDRESS /*block*/ | FLASH_WRITE_DATA /*addr*/,
535  page_data /*data*/,
536  page_size / 8 /*size in quadwords*/,
537  OtsUDPFirmwareCore::FIFO_ADDRESS_CMD_TYPE /*type option*/);
538  OtsUDPHardware::write(sendBuffer);
539 
540  // int counter = 0;
541  // while (counter < page_size) {
542  // fprintf(outfile, "%8.8X ", ftell(bitstream)-0x200+counter);
543  // while (1) {
544  // fprintf(outfile, "%2.2hhX ", page_data[counter]);
545  // counter++;
546  // if((counter%16)==0){
547  // break;
548  // }
549  // }
550  // fprintf(outfile, "\n");
551  // }
552 
553  // send go command after first page written
554  if(page == 0)
555  {
556  sendBuffer.resize(0);
557  OtsUDPFirmwareCore::writeAdvanced(
558  sendBuffer,
559  UDP_CORE_BLOCK_ADDRESS /*block*/ | FLASH_COMMAND_BASE /*addr*/,
560  1 /*data*/);
561  OtsUDPHardware::write(sendBuffer);
562 
563  __COUT__ << "Sending \"GO\" command..." << std::endl;
564  }
565 
566  page++;
567  }
568 
569  // fclose(outfile);
570  std::fclose(bitstream);
571 }
572 
573 DEFINE_OTS_INTERFACE(FEOtsEthernetProgramInterface)