aboutsummaryrefslogtreecommitdiff
path: root/riscv/triggers.h
blob: 6f00122c6a613ad12f8d7ea045832e9efa31911e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#ifndef _RISCV_TRIGGERS_H
#define _RISCV_TRIGGERS_H

#include <vector>
#include <optional>

#include "decode.h"

namespace triggers {

typedef enum {
  OPERATION_EXECUTE,
  OPERATION_STORE,
  OPERATION_LOAD,
} operation_t;

typedef enum
{
  ACTION_DEBUG_EXCEPTION = MCONTROL_ACTION_DEBUG_EXCEPTION,
  ACTION_DEBUG_MODE = MCONTROL_ACTION_DEBUG_MODE,
  ACTION_TRACE_START = MCONTROL_ACTION_TRACE_START,
  ACTION_TRACE_STOP = MCONTROL_ACTION_TRACE_STOP,
  ACTION_TRACE_EMIT = MCONTROL_ACTION_TRACE_EMIT,
  ACTION_MAXVAL = MCONTROL_ACTION_TRACE_EMIT
} action_t;

typedef enum {
  TIMING_BEFORE = 0,
  TIMING_AFTER = 1
} timing_t;

typedef enum {
  SSELECT_IGNORE = 0,
  SSELECT_SCONTEXT = 1,
  SSELECT_ASID = 2,
  SSELECT_MAXVAL = 2
} sselect_t;

typedef enum {
  MHSELECT_MODE_IGNORE,
  MHSELECT_MODE_MCONTEXT,
  MHSELECT_MODE_VMID,
} mhselect_mode_t;

struct match_result_t {
  match_result_t(const timing_t t=TIMING_BEFORE, const action_t a=ACTION_DEBUG_EXCEPTION) {
    timing = t;
    action = a;
  }
  timing_t timing;
  action_t action;
};

class matched_t
{
  public:
    matched_t(triggers::operation_t operation, reg_t address, action_t action, bool gva) :
      operation(operation), address(address), action(action), gva(gva) {}

    triggers::operation_t operation;
    reg_t address;
    action_t action;
    bool gva;
};

class trigger_t {
public:
  virtual ~trigger_t() {};

  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept = 0;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept = 0;
  reg_t tdata2_read(const processor_t * const proc) const noexcept;
  void tdata2_write(processor_t * const proc, const reg_t val) noexcept;
  reg_t tdata3_read(const processor_t * const proc) const noexcept;
  void tdata3_write(processor_t * const proc, const reg_t val) noexcept;

  virtual bool get_dmode() const = 0;
  virtual bool get_chain() const { return false; }
  virtual bool get_execute() const { return false; }
  virtual bool get_store() const { return false; }
  virtual bool get_load() const { return false; }
  virtual action_t get_action() const { return ACTION_DEBUG_EXCEPTION; }
  virtual bool icount_check_needed() const { return false; }
  virtual void stash_read_values() {}

  virtual std::optional<match_result_t> detect_memory_access_match(processor_t UNUSED * const proc,
      operation_t UNUSED operation, reg_t UNUSED address, std::optional<reg_t> UNUSED data) noexcept { return std::nullopt; }
  virtual std::optional<match_result_t> detect_icount_fire(processor_t UNUSED * const proc) { return std::nullopt; }
  virtual void detect_icount_decrement(processor_t UNUSED * const proc) {}
  virtual std::optional<match_result_t> detect_trap_match(processor_t UNUSED * const proc, const trap_t UNUSED & t) noexcept { return std::nullopt; }

protected:
  static action_t legalize_action(reg_t val, reg_t action_mask, reg_t dmode_mask) noexcept;
  bool common_match(processor_t * const proc, bool use_prev_prv = false) const noexcept;
  bool allow_action(const state_t * const state) const;
  reg_t tdata2;

  bool vs = false;
  bool vu = false;
  bool m = false;
  bool s = false;
  bool u = false;

private:
  unsigned legalize_mhselect(bool h_enabled) const noexcept;
  bool mode_match(reg_t prv, bool v) const noexcept;
  bool textra_match(processor_t * const proc) const noexcept;

  struct mhselect_interpretation {
    const unsigned mhselect;
    const mhselect_mode_t mode;
    const std::optional<bool> shift_mhvalue;
    unsigned compare_val(const unsigned mhvalue) const {
      return shift_mhvalue.value() ? (mhvalue << 1 | mhselect >> 2) : mhvalue;
    };
  };

  mhselect_interpretation interpret_mhselect(bool h_enabled) const noexcept {
    static unsigned warlize_if_h[8] = { 0, 1, 2, 0, 4, 5, 6, 4 };  // 3,7 downgrade
    static unsigned warlize_no_h[8] = { 0, 0, 0, 0, 4, 4, 4, 4 };  // only 0,4 legal
    static std::optional<mhselect_interpretation> table[8] = {
      mhselect_interpretation{ 0, MHSELECT_MODE_IGNORE, std::nullopt },
      mhselect_interpretation{ 1, MHSELECT_MODE_MCONTEXT, true },
      mhselect_interpretation{ 2, MHSELECT_MODE_VMID, true },
      std::nullopt,
      mhselect_interpretation{ 4, MHSELECT_MODE_MCONTEXT, false },
      mhselect_interpretation{ 5, MHSELECT_MODE_MCONTEXT, true },
      mhselect_interpretation{ 6, MHSELECT_MODE_VMID, true },
      std::nullopt
    };
    assert(mhselect < 8);
    unsigned legal = h_enabled ? warlize_if_h[mhselect] : warlize_no_h[mhselect];
    assert(legal < 8);
    return table[legal].value();
  }

  sselect_t sselect;
  unsigned svalue;
  unsigned sbytemask;
  unsigned mhselect;
  unsigned mhvalue;
};

class disabled_trigger_t : public trigger_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;

  virtual bool get_dmode() const override { return dmode; }

private:
  bool dmode;
};

class trap_common_t : public trigger_t {
public:
  bool get_dmode() const override { return dmode; }
  virtual action_t get_action() const override { return action; }

  virtual std::optional<match_result_t> detect_trap_match(processor_t * const proc, const trap_t& t) noexcept override;

private:
  virtual bool simple_match(bool interrupt, reg_t bit) const = 0;

protected:
  bool dmode;
  bool hit;
  action_t action;
};

class itrigger_t : public trap_common_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;

private:
  virtual bool simple_match(bool interrupt, reg_t bit) const override;
  bool nmi;
};

class etrigger_t : public trap_common_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;

private:
  virtual bool simple_match(bool interrupt, reg_t bit) const override;
};

class mcontrol_common_t : public trigger_t {
public:
  typedef enum
  {
    MATCH_EQUAL = MCONTROL_MATCH_EQUAL,
    MATCH_NAPOT = MCONTROL_MATCH_NAPOT,
    MATCH_GE = MCONTROL_MATCH_GE,
    MATCH_LT = MCONTROL_MATCH_LT,
    MATCH_MASK_LOW = MCONTROL_MATCH_MASK_LOW,
    MATCH_MASK_HIGH = MCONTROL_MATCH_MASK_HIGH
  } match_t;

  virtual bool get_dmode() const override { return dmode; }
  virtual bool get_chain() const override { return chain; }
  virtual bool get_execute() const override { return execute; }
  virtual bool get_store() const override { return store; }
  virtual bool get_load() const override { return load; }
  virtual action_t get_action() const override { return action; }

  virtual std::optional<match_result_t> detect_memory_access_match(processor_t * const proc,
      operation_t operation, reg_t address, std::optional<reg_t> data) noexcept override;

private:
  bool simple_match(unsigned xlen, reg_t value) const;

protected:
  static match_t legalize_match(reg_t val) noexcept;
  static bool legalize_timing(reg_t val, reg_t timing_mask, reg_t select_mask, reg_t execute_mask, reg_t load_mask) noexcept;
  bool dmode = false;
  action_t action = ACTION_DEBUG_EXCEPTION;
  bool hit = false;
  bool select = false;
  bool timing = false;
  bool chain = false;
  match_t match = MATCH_EQUAL;
  bool execute = false;
  bool store = false;
  bool load = false;
};

class mcontrol_t : public mcontrol_common_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
};

class mcontrol6_t : public mcontrol_common_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;
};

class icount_t : public trigger_t {
public:
  virtual reg_t tdata1_read(const processor_t * const proc) const noexcept override;
  virtual void tdata1_write(processor_t * const proc, const reg_t val, const bool allow_chain) noexcept override;

  bool get_dmode() const override { return dmode; }
  virtual action_t get_action() const override { return action; }
  virtual bool icount_check_needed() const override { return count > 0 || pending; }
  virtual void stash_read_values() override;

  virtual std::optional<match_result_t> detect_icount_fire(processor_t * const proc) noexcept override;
  virtual void detect_icount_decrement(processor_t * const proc) noexcept override;

private:
  bool dmode = false;
  bool hit = false;
  unsigned count = 1, count_read_value = 1;
  bool pending = false, pending_read_value = false;
  action_t action = (action_t)0;
};

class module_t {
public:
  module_t(unsigned count);
  ~module_t();

  reg_t tdata1_read(unsigned index) const noexcept;
  bool tdata1_write(unsigned index, const reg_t val) noexcept;
  reg_t tdata2_read(unsigned index) const noexcept;
  bool tdata2_write(unsigned index, const reg_t val) noexcept;
  reg_t tdata3_read(unsigned index) const noexcept;
  bool tdata3_write(unsigned index, const reg_t val) noexcept;
  reg_t tinfo_read(unsigned index) const noexcept;

  unsigned count() const { return triggers.size(); }

  std::optional<match_result_t> detect_memory_access_match(operation_t operation, reg_t address, std::optional<reg_t> data) noexcept;
  std::optional<match_result_t> detect_icount_match() noexcept;
  std::optional<match_result_t> detect_trap_match(const trap_t& t) noexcept;

  processor_t *proc;
private:
  std::vector<trigger_t *> triggers;
};

};

#endif