InterruptLatency.hpp 3.25 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/**
 *  @file	InterruptLatency.hpp
 *  @brief	Measures the software overhead, i.e., the roundtrip time that is
 *              added by the TPC software layers. This is done via the 'Counter'
 *              IP cores, which simply provide a cycle-accurate countdown timer.
 *              The default design should provide at least one instance of the
 *              timer, which accepts a cycle count as first argument. The design
 *              should run at 100 Mhz (assumption of timing calculations).
 *  @author	J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
 **/
Jens Korinth's avatar
Jens Korinth committed
11
12
#ifndef INTERRUPT_LATENCY_HPP__
#define INTERRUPT_LATENCY_HPP__
13
14
15
16
17
18
19
20
21
22

#include <atomic>
#include <thread>
#include <future>
#include <vector>
#include <sstream>
#include <chrono>
#include <cmath>
#include <unistd.h>
#include <ncurses.h>
23
#include <tapasco_api.hpp>
24
25
26

using namespace std;
using namespace std::chrono;
27
using namespace tapasco;
28
29
30
31
32
33

/**
 * Measures interrupt latency added by software layers in TPC.
 **/
class InterruptLatency {
public:
34
  static tapasco_func_id_t const COUNTER_ID = 14;
35

36
37
  InterruptLatency(Tapasco& tapasco) : tapasco(tapasco) {
    if (tapasco.func_instance_count(COUNTER_ID) == 0)
38
39
40
41
42
43
44
45
      throw "need at least one instance of 'Counter' (14) in bitstream";
  }
  virtual ~InterruptLatency() {}

  static constexpr long OP_ALLOCFREE = 0;
  static constexpr long OP_COPYFROM  = 1;
  static constexpr long OP_COPYTO    = 2;

46
  double atcycles(uint32_t const clock_cycles, size_t const min_runs = 100, double *min = NULL, double *max = NULL) {
Jens Korinth's avatar
Jens Korinth committed
47
48
49
50
51
52
    CumulativeAverage<double> cavg { 0 };
    bool stop = false;
    int x, y, maxx, maxy;
    getyx(stdscr, y, x);
    getmaxyx(stdscr, maxy, maxx);
    future<void> f = async(launch::async, [&]() { trigger(stop, clock_cycles, cavg); });
53
    auto c = getch();
Jens Korinth's avatar
Jens Korinth committed
54
55
56
57
58
59
    do {
      move(y, 0);
      clrtoeol();
      mvprintw(y, x, "Runtime: %12zu cc, Latency: % 12.1f, Min: % 12.1f, Max: % 12.1f, Count: %zu/%zu",
        clock_cycles, cavg(), cavg.min(), cavg.max(), cavg.size(), min_runs);
      refresh();
60
      usleep(1000000);
61
62
63
64
      // exit gracefully on ctrl+c
      c = getch();
      if (c == 3) { endwin(); exit(3); }
    } while (c == ERR && (fabs(cavg.delta()) > 0.01 || cavg.size() < min_runs));
Jens Korinth's avatar
Jens Korinth committed
65
66
67
68
    stop = true;
    f.get();

    move((y+1) % maxy, 0);
69
70
    if (min) *min = cavg.min();
    if (max) *max = cavg.max();
Jens Korinth's avatar
Jens Korinth committed
71
72
73
    return cavg();
  }

74
75
private:
  void trigger(volatile bool& stop, uint32_t const clock_cycles, CumulativeAverage<double>& cavg) {
76
    tapasco_res_t res;
77
    while (! stop) {
78
      auto tstart = steady_clock::now();
79
80
      // if 0, use 1us - 100ms interval (clock period is 10ns)
      uint32_t cc = clock_cycles > 0 ? clock_cycles : (rand() % (10000000 - 100) + 100);
81
82
      if ((res = tapasco.launch_no_return(COUNTER_ID, cc)) != TAPASCO_SUCCESS)
        throw Tapasco::tapasco_error(res);
83
      microseconds const d = duration_cast<microseconds>(steady_clock::now() - tstart);
84
85
86
87
88
89
90
91
92
93
94
      cavg.update(d.count() - cc / 100);
    }
  }

  static const std::string maskToString(long const opmask) {
    stringstream tmp;
    tmp << (opmask & OP_COPYFROM ? "r" : " ") 
        << (opmask & OP_COPYTO   ? "w" : " ");
    return tmp.str();
  }

95
  Tapasco& tapasco;
96
97
};

Jens Korinth's avatar
Jens Korinth committed
98
#endif /* INTERRUPT_LATENCY_HPP__ */
99
/* vim: set foldmarker=@{,@} foldlevel=0 foldmethod=marker : */