HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
wavedata_table_editor.cpp
Go to the documentation of this file.
2 #include "hal_core/netlist/net.h"
6 #include <QHeaderView>
7 #include <QDebug>
8 #include <QDir>
9 
10 namespace hal {
12  : QTableWidget(parent), mMaxTime(0), mDisableCellParser(false), mDisplayHexValues(true), mCurrentTime(0), mCurrentRow(0)
13  {;}
14 
15  void WavedataTableEditor::setup(const std::vector<NetlistSimulatorController::InputColumnHeader> &inpColHeads, bool omitClock)
16  {
17  for (NetlistSimulatorController::InputColumnHeader ich : inpColHeads)
18  {
19  if (omitClock && ich.is_clock) continue;
20  mInputColumnHeader.append(ich);
21  }
22 
23  setupHeader();
24 
25  setRowCount(2);
26  int n = mInputColumnHeader.size()+1;
27  for (int i=0; i<n; i++) setValueCell(0,i,0);
28 
29  // Must not change start time t=0
30  item(0,0)->setFlags(item(0,0)->flags() & (~Qt::ItemIsEditable));
31  connect(this, &QTableWidget::itemChanged, this, &WavedataTableEditor::handleItemChanged);
32  }
33 
34  void WavedataTableEditor::setupHeader()
35  {
36  QStringList headerLabel;
37  for (NetlistSimulatorController::InputColumnHeader ich : mInputColumnHeader)
38  {
39  QString colName = QString::fromStdString(ich.name);
40  if (ich.nets.size() > 1) colName += QString("[%1:0]").arg(ich.nets.size()-1);
41  headerLabel << colName;
42  }
43  int n = mInputColumnHeader.size()+1;
44  setColumnCount(n);
45  headerLabel.prepend("Time");
46  setHorizontalHeaderLabels(headerLabel);
47  }
48 
49  void WavedataTableEditor::setValueCell(int irow, int icol, int val)
50  {
51  QTableWidgetItem* valItem = new QTableWidgetItem(val > 9 && mDisplayHexValues
52  ? "0x" + QString::number(val,16)
53  : QString::number(val));
55  setItem (irow, icol, valItem);
56  }
57 
58  void WavedataTableEditor::loadWaveData(const QString& saleaDirectoryFile)
59  {
60  mDisableCellParser = true;
61  SaleaeParser parser(saleaDirectoryFile.toStdString());
62 
63  mCurrentTime = 0;
64  mCurrentRow = 0;
65  int nrows = 1;
66  int ncols = columnCount() - 1; // columns with values
67  mCurrentValue = new int[ncols];
68  memset(mCurrentValue,0,ncols*sizeof(int));
69 
70  for (int icol = 0; icol < ncols; icol++)
71  {
72  const NetlistSimulatorController::InputColumnHeader& ich = mInputColumnHeader.at(icol);
73 
74  int nBits = ich.nets.size();
75 
76  for (int i=0; i<nBits; i++)
77  {
78  int mask = 1 << i;
79 
80  const Net* n = ich.nets.at(i);
81 
82  parser.register_callback(n->get_name(), n->get_id(), [icol,mask,ncols,this](const void* obj, uint64_t t, int val) {
83  if (t != mCurrentTime)
84  {
85  mCurrentRow++;
86  if (mCurrentRow+1 >= rowCount()) setRowCount(mCurrentRow+2);
87  QTableWidgetItem* tItem = new QTableWidgetItem(QString::number(t));
88  tItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
89  setItem(mCurrentRow,0,tItem);
90  mCurrentTime = t;
91  for (int i=0; i<ncols; i++)
92  setValueCell(mCurrentRow-1, i+1, mCurrentValue[i]);
93  }
94 
95  if (val)
96  mCurrentValue[icol] |= mask;
97  else
98  mCurrentValue[icol] &= (~mask);
99  }, nullptr);
100  }
101  }
102 
103  clear();
104  setupHeader();
105  setRowCount(2);
106  QTableWidgetItem* zeroItem = new QTableWidgetItem("0");
108  zeroItem->setFlags(zeroItem->flags() & (~Qt::ItemIsEditable));
109  setItem(0,0,zeroItem);
110 
111  while (parser.next_event()) {;} // loop over entries calling callback
112 
113  for (int i=0; i<ncols; i++)
114  setValueCell(mCurrentRow, i+1, mCurrentValue[i]);
115 
116  delete [] mCurrentValue;
117 
118  mDisableCellParser = false;
119  }
120 
121  void WavedataTableEditor::handleItemChanged(QTableWidgetItem* changedItem)
122  {
123  if (mDisableCellParser) return;
124 
125  mDisableCellParser = true;
126  int cur = rowCount() - 1;
127  if (cur > 0 && !changedItem->column() && changedItem->row() == cur)
128  {
129  setRowCount(cur+2);
130  for (int icol=1; icol < columnCount(); icol++)
131  {
132  QTableWidgetItem* testItem = item(cur, icol);
133  QTableWidgetItem* lastItem = item(cur-1, icol);
134  if ((!testItem || testItem->text().isEmpty()) && lastItem)
135  {
136  QTableWidgetItem* twi = lastItem->clone();
138  setItem(cur, icol, twi);
139  }
140  }
141  }
142  if (!changedItem->column())
143  {
144  // time column
145  int irow = changedItem->row();
146  if (cur > 0 && irow == cur)
147  {
148  setRowCount(cur+2);
149  for (int icol=1; icol < columnCount(); icol++)
150  {
151  QTableWidgetItem* testItem = item(cur, icol);
152  QTableWidgetItem* lastItem = item(cur-1, icol);
153  if ((!testItem || testItem->text().isEmpty()) && lastItem)
154  {
155  QTableWidgetItem* twi = lastItem->clone();
157  setItem(cur,icol, twi);
158  }
159  }
160  Q_EMIT lineAdded();
161  }
162  bool numericOk = false;
163  qlonglong thisVal = changedItem->text().toLongLong(&numericOk);
164  qlonglong prevVal = irow ? item(irow-1,0)->text().toLongLong() : 0;
165  qlonglong nextVal = 0;
166  if (rowCount() > irow+1 && item(irow+1,0))
167  {
168  qlonglong tempNextVal = item(irow+1,0)->text().toLongLong(&numericOk);
169  if (numericOk && tempNextVal >= prevVal+2) nextVal = tempNextVal;
170  }
171  if (nextVal)
172  {
173  // changed time value has successor
174  if (!numericOk || thisVal <= prevVal || thisVal >= nextVal)
175  changedItem->setText(QString::number((prevVal+nextVal)/2));
176  }
177  else
178  {
179  // changed last time value
180  if (!numericOk || thisVal <= prevVal)
181  changedItem->setText(QString::number(prevVal+1000));
182  }
183  }
184  else
185  {
186  // wave value column
187  int val = intCellValue(changedItem->row(),changedItem->column());
188  if (val == sIllegalValue)
189  changedItem->setText("0");
190  else
191  {
192  if (val > 9 && mDisplayHexValues)
193  changedItem->setText("0x" + QString::number(val,16));
194  else
195  changedItem->setText(QString::number(val,10));
196  }
197  }
199  mDisableCellParser = false;
200  }
201 
202  int WavedataTableEditor::validLines() const
203  {
204  int retval = 0;
205  qlonglong tlast = -1;
206  bool numericOk = false;
207  for (int irow = 0; irow < rowCount(); irow++)
208  {
209  QTableWidgetItem* timeCell = item(irow,0);
210  if (!timeCell) continue;
211  qlonglong t = timeCell->text().toLongLong(&numericOk);
212  if (!numericOk) continue;
213  if (t <= tlast) continue;
214  bool incomplete = false;
215  for (int icol=1; icol<columnCount(); icol++)
216  {
217  QTableWidgetItem* testItem = item(irow,icol);
218  if (!testItem || testItem->text().isEmpty())
219  {
220  incomplete = true;
221  break;
222  }
223  }
224  if (incomplete) continue;
225  ++retval;
226  tlast = t;
227  }
228  return retval;
229  }
230 
231  int WavedataTableEditor::intCellValue(int irow, int icol) const
232  {
233  QString txt;
234  int nBits = mInputColumnHeader.at(icol-1).nets.size();
235  int upperLimit = 1 << nBits;
236 
237  if (item(irow,icol))
238  txt = item(irow,icol)->text();
239  if (txt.isEmpty())
240  return sIllegalValue;
241 
242  bool numberOk = true;
243  int tempValue = -1;
244 
245  // most common input 0,1,x,z
246  if (txt.size() == 1)
247  {
248  switch (txt.at(0).toUpper().unicode())
249  {
250  case 'Z':
251  return -2;
252  case 'X':
253  return -1;
254  default:
255  // legal values 0-9
256  tempValue = txt.toInt(&numberOk);
257  if (!numberOk || tempValue >= upperLimit) return sIllegalValue;
258  return tempValue;
259  }
260  }
261 
262  // txt size at least 2
263  if (txt.at(0) == '"')
264  {
265  // enter ASCII character, eg. "A -> 0x41 (65)
266  tempValue = txt.at(1).unicode();
267  }
268  else
269  {
270  int apos = txt.indexOf('\'');
271  if (apos < 0)
272  {
273  if (txt.at(0) == '0')
274  {
275  switch (txt.at(1).toUpper().unicode())
276  {
277  case 'X':
278  tempValue = txt.mid(2).toInt(&numberOk, 16);
279  break;
280  case 'O':
281  tempValue = txt.mid(2).toInt(&numberOk, 8);
282  break;
283  case 'B':
284  tempValue = txt.mid(2).toInt(&numberOk, 2);
285  break;
286  default:
287  tempValue = txt.toInt(&numberOk, 10);
288  break;
289  }
290  }
291  else
292  tempValue = txt.toInt(&numberOk, 10);
293  }
294  else
295  {
296  if (apos + 2 >= txt.size())
297  return sIllegalValue;
298  switch (txt.at(apos+1).toUpper().unicode())
299  {
300  case 'H':
301  tempValue = txt.mid(apos+2).toInt(&numberOk, 16);
302  break;
303  case 'O':
304  tempValue = txt.mid(apos+2).toInt(&numberOk, 8);
305  break;
306  case 'B':
307  tempValue = txt.mid(apos+2).toInt(&numberOk, 2);
308  break;
309  case 'D':
310  tempValue = txt.mid(apos+2).toInt(&numberOk, 10);
311  break;
312  default:
313  return sIllegalValue;
314  }
315  }
316  }
317 
318  if (!numberOk || tempValue >= upperLimit)
319  return sIllegalValue;
320 
321  return tempValue;
322  }
323 
324  void WavedataTableEditor::generateSimulationInput(const QString& workdir)
325  {
326  QDir saleaeDir = QDir(workdir).absoluteFilePath("saleae");
327  QString saleaeDirectoryFilename = saleaeDir.absoluteFilePath("saleae.json");
328  SaleaeWriter* writer = new SaleaeWriter(saleaeDirectoryFilename.toStdString());
329  int iTableCol = 1;
330  QTableWidgetItem* timeCell = item(0,0);
331  mMaxTime = timeCell ? timeCell->text().toULongLong() : 0;
332  int sofIndex = 0;
333  int maxIndex = 0;
334  for (auto it = mInputColumnHeader.constBegin(); it != mInputColumnHeader.constEnd(); ++it)
335  maxIndex += it->nets.size();
336 
337  SaleaeOutputFile** sofArray;
338  int* valArray;
339  sofArray = new SaleaeOutputFile*[maxIndex];
340  valArray = new int[maxIndex];
341 
342  for (auto it = mInputColumnHeader.constBegin(); it != mInputColumnHeader.constEnd(); ++it)
343  {
344  int tableVal = intCellValue(0,iTableCol);
345  if (tableVal == WavedataTableEditor::sIllegalValue)
346  tableVal = 0;
347  int mask = 1;
348  int iBit = 0;
349  for (const Net* n : it->nets)
350  {
351  SaleaeOutputFile* sof = writer->add_or_replace_waveform(n->get_name(),n->get_id());
352  int netVal = tableVal & mask;
353  sof->writeTimeValue(mMaxTime,netVal);
354  sofArray[sofIndex + iBit] = sof;
355  valArray[sofIndex + iBit] = netVal;
356  mask <<= 1;
357  ++iBit;
358  }
359  ++iTableCol;
360  sofIndex += it->nets.size();
361  }
362 
363  for (int irow = 1; irow < rowCount(); irow++)
364  {
365  sofIndex = 0;
366  timeCell = item(irow,0);
367  if (!timeCell) continue;
368  uint64_t tval = timeCell->text().toULongLong();
369  if (tval <= mMaxTime) continue;
370  mMaxTime = tval;
371  iTableCol = 1;
372  for (auto it = mInputColumnHeader.constBegin(); it != mInputColumnHeader.constEnd(); ++it)
373  {
374  int tableVal = intCellValue(irow,iTableCol);
375  if (tableVal == WavedataTableEditor::sIllegalValue)
376  tableVal = 0;
377  int mask = 1;
378 
379  for (int iBit = 0; iBit < it->nets.size(); ++iBit)
380  {
381  int netVal = tableVal & mask;
382  if (netVal != valArray[sofIndex+iBit])
383  {
384  SaleaeOutputFile* sof = sofArray[sofIndex + iBit];
385  Q_ASSERT(sof);
386  sof->writeTimeValue(mMaxTime,netVal);
387  valArray[sofIndex + iBit] = netVal;
388  }
389  mask <<= 1;
390  }
391  ++iTableCol;
392  sofIndex += it->nets.size();
393  }
394  }
395 
396  delete writer;
397  delete [] sofArray;
398  delete [] valArray;
399  }
400 
401  void WavedataTableEditor::setDisplayHexValues(bool hex)
402  {
403  if (hex == mDisplayHexValues) return; // nothing to do;
404 
405  mDisableCellParser = true;
406  mDisplayHexValues = hex;
407 
408  for (int icol = 1; icol < columnCount(); icol++)
409  for (int irow = 0; irow < rowCount(); irow++)
410  {
411  QTableWidgetItem* cell = item(irow,icol);
412  if (!cell) continue;
413  int val = intCellValue(irow,icol);
414  QString txt = QString::number(val, mDisplayHexValues ? 16 : 10);
415  if (mDisplayHexValues && val > 9) txt.prepend("0x");
416  cell->setText(txt);
417  }
418 
419  mDisableCellParser = false;
420  }
421 
422 }
Definition: net.h:58
u32 get_id() const
Definition: net.cpp:88
const std::string & get_name() const
Definition: net.cpp:98
void writeTimeValue(uint64_t t, int32_t val)
Write single data tuple to disk.
The SaleaeParser class is an engine which allows to parse any number of SALEAE files into a sequence ...
Definition: saleae_parser.h:74
bool register_callback(const std::string &name, uint32_t id, std::function< void(void *, uint64_t, int)> callback, void *obj)
Registers callback which gets executed upon next_event()
The SaleaeWriter class is a helper class which provides utility methods when importing data to SALEAE...
Definition: saleae_writer.h:42
SaleaeOutputFile * add_or_replace_waveform(const std::string &name, uint32_t id)
Create new empty output file. Will delete existing binary file if directory links already a data file...
WavedataTableEditor(QWidget *parent=nullptr)
void loadWaveData(const QString &saleaDirectoryFile)
void setup(const std::vector< NetlistSimulatorController::InputColumnHeader > &inpColHeads, bool omitClock)
Definition: defines.h:45
QChar toUpper() const const
ushort unicode() const const
QString absoluteFilePath(const QString &fileName) const const
void prepend(const T &value)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QChar at(int position) const const
QString fromStdString(const std::string &str)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(int position, int n) const const
QString number(int n, int base)
QString & prepend(QChar ch)
int size() const const
int toInt(bool *ok, int base) const const
qlonglong toLongLong(bool *ok, int base) const const
std::string toStdString() const const
qulonglong toULongLong(bool *ok, int base) const const
AlignRight
ItemIsEditable
QTableWidgetItem * item(int row, int column) const const
void itemChanged(QTableWidgetItem *item)
void setColumnCount(int columns)
void setHorizontalHeaderLabels(const QStringList &labels)
void setItem(int row, int column, QTableWidgetItem *item)
void setRowCount(int rows)
virtual QTableWidgetItem * clone() const const
int column() const const
Qt::ItemFlags flags() const const
int row() const const
void setFlags(Qt::ItemFlags flags)
void setText(const QString &text)
void setTextAlignment(int alignment)
QString text() const const
QRegion mask() const const