ChimeraTK-DeviceAccess  03.18.00
testDoubleBufferingHW.cc
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 
4 #define BOOST_TEST_DYN_LINK
5 #define BOOST_TEST_MODULE SubdeviceBackendUnifiedTest
6 #include <boost/test/unit_test.hpp>
7 using namespace boost::unit_test_framework;
8 
9 #include "Device.h"
10 
11 #include <boost/thread/barrier.hpp>
12 
13 #include <fstream>
14 
15 using namespace ChimeraTK;
16 
17 BOOST_AUTO_TEST_SUITE(DoubleBufferingBackendTestSuite)
18 
19 /**********************************************************************************************************************/
20 
21 // We need a test that is not run as unit test and which is used as long-running data consistency check
22 // - perhalps call CGs python scripts as init for hw?
23 // firmware provides double buffering region with continuously changing data (counter signal)
26  ScalarRegisterAccessor<uint32_t> doubleBufferingEnabled, writingBufferNum, fifoStatus;
27  std::map<int16_t, unsigned> jumpHist; // for a historgram jump -> count in corrupt data
28 
30  setDMapFilePath("doubleBufferHW.dmap");
31  d.open("logicalDev");
32  doubleBufferingEnabled.replace(d.getScalarRegisterAccessor<uint32_t>("enableDoubleBuffering"));
33  writingBufferNum.replace(d.getScalarRegisterAccessor<uint32_t>("currentBufferNumber"));
34  fifoStatus.replace(d.getScalarRegisterAccessor<uint32_t>("fifoStatus"));
35  }
36 
37  // try several times (with random sleeps inbetween) to read signal from reg and return
38  // the number of times signal was detected as corrupted
39  unsigned checkDataCorruption(std::string reg, unsigned tries) {
40  unsigned short goodStep = 100;
41  bool writeCorruptData = true;
42  unsigned dataCorruptionCount = 0;
43  auto accessorA = d.getOneDRegisterAccessor<int16_t>(reg);
44 
45  for(unsigned t = 0; t < tries; t++) {
46  accessorA.readLatest();
47  fifoStatus.readLatest();
48 
49  unsigned i = 0;
50  int16_t previousVal = 0;
51  for(int16_t val : accessorA) {
52  // val is allowed to increase by goodStep or wrap-around
53  if(previousVal != 0 && val != (int16_t)(previousVal + goodStep)) {
54  std::cout << "found data corruption at index " << i << ": step from " << previousVal << " to " << val
55  << " while DAQ fifoStatus=" << fifoStatus << std::endl;
56  if(writeCorruptData) {
57  char fname[100];
58  snprintf(fname, sizeof(fname), "corruptData%03i.dat", i);
59  std::cout << "writing corrupt data to " << fname << std::endl;
60  std::ofstream f(fname);
61  for(int16_t val1 : accessorA) {
62  f << val1 << std::endl;
63  }
64  f.close();
65  }
66  dataCorruptionCount++;
67  jumpHist[(int16_t)(val - previousVal)]++;
68  }
69  previousVal = val;
70  i++;
71  }
72  // random sleep of [0..0.1] seconds
73  boost::this_thread::sleep_for(boost::chrono::milliseconds(std::rand() % 100));
74  };
75  return dataCorruptionCount;
76  }
77  void enableDoubleBuf(bool ena) {
78  doubleBufferingEnabled = ena;
79  doubleBufferingEnabled.write();
80  }
81  unsigned getActiveBufferNo() {
82  writingBufferNum.readLatest();
83  return writingBufferNum;
84  }
85  void printHist() {
86  std::cout << "histogram of wrong count-up values in data:" << std::endl;
87  for(auto p : jumpHist) {
88  std::cout << " distance=" << p.first << " : " << p.second << " times" << std::endl;
89  }
90  }
91 };
92 
93 /**********************************************************************************************************************/
94 
96  /*
97  * Here we check that we can actually detect corrupted data, if double buffer feature is off
98  * This is a prerequesite for following test to make sense.
99  * Note, we would even expect corrup data when double buffering still on, if we read to currently written
100  * buffer, only less often.
101  */
102  enableDoubleBuf(false);
103  unsigned bufferNo = getActiveBufferNo();
104  // read from the buffer which is currently written to
105  unsigned dataCorruptionCount = checkDataCorruption(bufferNo == 0 ? "channel10buf0" : "channel10buf1", 200);
106  printHist();
107  BOOST_CHECK(dataCorruptionCount > 0);
108 }
109 
110 /**********************************************************************************************************************/
111 
113  /*
114  * Here we look for data corruption when firmware uses double buffering.
115  * This test is long-running.
116  *
117  * Note, this test will probably fail even with double-buffering enabled.
118  * This happens if data loss appears due to congestion in dma controller. This must be solved on the firmware side.
119  * For discussion see https://redmine.msktools.desy.de/issues/10522
120  */
121  unsigned dataCorruptionCount = checkDataCorruption("channel10", 1000);
122  printHist();
123  BOOST_CHECK(dataCorruptionCount == 0);
124 }
125 
126 /**********************************************************************************************************************/
127 
128 BOOST_AUTO_TEST_SUITE_END()
ChimeraTK::TransferElementAbstractor::readLatest
bool readLatest()
Read the latest value, discarding any other update since the last read if present.
Definition: TransferElementAbstractor.h:77
DeviceFixture_HW::enableDoubleBuf
void enableDoubleBuf(bool ena)
Definition: testDoubleBufferingHW.cc:77
ChimeraTK::Device::getOneDRegisterAccessor
OneDRegisterAccessor< UserType > getOneDRegisterAccessor(const RegisterPath &registerPathName, size_t numberOfWords=0, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a OneDRegisterAccessor object for the given register.
Definition: Device.h:273
DeviceFixture_HW::DeviceFixture_HW
DeviceFixture_HW()
Definition: testDoubleBufferingHW.cc:29
DeviceFixture_HW::checkDataCorruption
unsigned checkDataCorruption(std::string reg, unsigned tries)
Definition: testDoubleBufferingHW.cc:39
ChimeraTK::ScalarRegisterAccessor< uint32_t >
DeviceFixture_HW::getActiveBufferNo
unsigned getActiveBufferNo()
Definition: testDoubleBufferingHW.cc:81
ChimeraTK::NDRegisterAccessorAbstractor::replace
void replace(const NDRegisterAccessorAbstractor< UserType > &newAccessor)
Assign a new accessor to this NDRegisterAccessorAbstractor.
Definition: NDRegisterAccessorAbstractor.h:67
Device.h
DeviceFixture_HW::writingBufferNum
ScalarRegisterAccessor< uint32_t > writingBufferNum
Definition: testDoubleBufferingHW.cc:26
ChimeraTK::Device
Class allows to read/write registers from device.
Definition: Device.h:39
ChimeraTK::Device::open
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Definition: Device.cc:58
ChimeraTK::Device::getScalarRegisterAccessor
ScalarRegisterAccessor< UserType > getScalarRegisterAccessor(const RegisterPath &registerPathName, size_t wordOffsetInRegister=0, const AccessModeFlags &flags=AccessModeFlags({})) const
Get a ScalarRegisterObject object for the given register.
Definition: Device.h:263
DeviceFixture_HW::jumpHist
std::map< int16_t, unsigned > jumpHist
Definition: testDoubleBufferingHW.cc:27
BOOST_FIXTURE_TEST_CASE
BOOST_FIXTURE_TEST_CASE(testWithHardware0, DeviceFixture_HW)
Definition: testDoubleBufferingHW.cc:95
ChimeraTK::TransferElementAbstractor::write
bool write(ChimeraTK::VersionNumber versionNumber={})
Write the data to device.
Definition: TransferElementAbstractor.h:89
DeviceFixture_HW
Definition: testDoubleBufferingHW.cc:24
ChimeraTK::setDMapFilePath
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.
Definition: Utilities.cpp:327
ChimeraTK
Definition: DummyBackend.h:16
DeviceFixture_HW::d
Device d
Definition: testDoubleBufferingHW.cc:25
DeviceFixture_HW::printHist
void printHist()
Definition: testDoubleBufferingHW.cc:85