blob: b1079d80a84a7c133dcd3287ae83209f927c77db [file] [log] [blame]
Zane Shelley11b89942019-11-07 11:07:28 -06001#pragma once
2
3#include <hei_main.hpp>
4
Zane Shelley3a02e242020-05-08 16:25:36 -05005#include <algorithm>
6#include <map>
7#include <vector>
8
Zane Shelley11b89942019-11-07 11:07:28 -06009#include "gtest/gtest.h"
10
11namespace libhei
12{
13
14/**
15 * @brief Contains simulated chip objects and register contents used during
16 * isolation. Also contains the expected signatures to compare after
17 * isolation.
18 */
19class SimulatorData
20{
21 private: // This class cannot be instantiated. Use getSingleton() instead.
22 /** @brief Default constructor. */
23 SimulatorData() = default;
24
25 /** @brief Destructor. */
26 ~SimulatorData() = default;
27
28 /** @brief Copy constructor. */
29 SimulatorData(const SimulatorData&) = delete;
30
31 /** @brief Assignment operator. */
32 SimulatorData& operator=(const SimulatorData&) = delete;
33
34 public:
35 /** @brief Provides access to a singleton instance of this object. */
36 static SimulatorData& getSingleton()
37 {
38 static SimulatorData theSimData{};
39 return theSimData;
40 }
41
Zane Shelley1be4c3c2020-04-17 15:55:07 -050042 public:
43 /** The list of supported chip types for the simulator. */
Zane Shelley8c093d82020-05-04 22:06:52 -050044 enum SimChipType
Zane Shelley1be4c3c2020-04-17 15:55:07 -050045 {
Patrick Williams2f7537d2023-05-10 07:51:39 -050046 SAMPLE = 0xdeadbeef,
Zane Shelleyd6826e52020-11-10 17:39:00 -060047 EXPLORER_11 = 0x60d20011,
48 EXPLORER_20 = 0x60d20020,
Caleb Palmere4ad4e32024-08-07 09:48:14 -050049 ODYSSEY_10 = 0x60c00010,
Patrick Williams2f7537d2023-05-10 07:51:39 -050050 P10_10 = 0x20da0010,
51 P10_20 = 0x20da0020,
Zane Shelley1be4c3c2020-04-17 15:55:07 -050052 };
53
Zane Shelley11b89942019-11-07 11:07:28 -060054 private:
Zane Shelley1be4c3c2020-04-17 15:55:07 -050055 /** The Chip Data file paths for each support chip type. */
Zane Shelley8c093d82020-05-04 22:06:52 -050056 static const std::map<SimChipType, const char*> cv_chipPath;
Zane Shelley1be4c3c2020-04-17 15:55:07 -050057
Zane Shelley11b89942019-11-07 11:07:28 -060058 /** The list of configured chips used throughout a test case. */
59 std::vector<Chip> iv_chipList;
60
Zane Shelley8c093d82020-05-04 22:06:52 -050061 /** The list of configured chip types used throughout a test case. */
62 std::vector<ChipType_t> iv_typeList;
63
Zane Shelley11b89942019-11-07 11:07:28 -060064 /** The contents of all the SCOM registers used for an iteration of
65 * isolation. */
66 std::map<Chip, std::map<uint32_t, uint64_t>> iv_scomRegData;
67
68 /** The contents of all the Indirect SCOM registers used for an iteration of
69 * isolation. */
70 std::map<Chip, std::map<uint64_t, uint64_t>> iv_idScomRegData;
71
72 /** The list of expected signatures during an iteration of isolation. */
73 std::vector<Signature> iv_expSigList;
74
75 public:
76 /**
77 * @brief Adds a chip to the list of configured chips. Also, calls the main
78 * initialize() API which will initialize the isolator with the Chip
79 * Data File associated with this chip.
80 */
Caleb Palmer446da092025-01-16 10:22:02 -060081 void addChip(const Chip& i_chip, const char* i_chipPath = "");
Zane Shelley11b89942019-11-07 11:07:28 -060082
Paul Greenwoodc0919342019-12-10 15:36:17 -060083 /** @brief Retrieve ScomReg from map and return its value */
84 uint64_t getScomReg(const Chip& i_chip, uint32_t i_address)
85 {
86 return iv_scomRegData[i_chip][i_address];
87 }
88
89 /** @breif Retrieve idScomReg from map and return its value */
90 uint64_t getIdScomReg(const Chip& i_chip, uint64_t i_address)
91 {
92 return iv_idScomRegData[i_chip][i_address];
93 }
94
Zane Shelley11b89942019-11-07 11:07:28 -060095 /** @brief Adds a SCOM register to iv_scomRegData. */
96 void addScomReg(const Chip& i_chip, uint32_t i_address, uint64_t i_value)
97 {
98 // First check if this entry already exists.
99 auto chip_itr = iv_scomRegData.find(i_chip);
100 if (iv_scomRegData.end() != chip_itr)
101 {
102 auto addr_itr = chip_itr->second.find(i_address);
103 ASSERT_EQ(chip_itr->second.end(), addr_itr);
104 }
105
106 // Add the new entry.
107 iv_scomRegData[i_chip][i_address] = i_value;
108 }
109
110 /** @brief Adds a SCOM register to iv_idScomRegData. */
111 void addIdScomReg(const Chip& i_chip, uint64_t i_address, uint64_t i_value)
112 {
113 // First check if this entry already exists.
114 auto chip_itr = iv_idScomRegData.find(i_chip);
115 if (iv_idScomRegData.end() != chip_itr)
116 {
117 auto addr_itr = chip_itr->second.find(i_address);
118 ASSERT_EQ(chip_itr->second.end(), addr_itr);
119 }
120
121 // Add the new entry.
122 iv_idScomRegData[i_chip][i_address] = i_value;
123 }
124
125 /** @brief Adds a Signature to iv_expSigList. */
126 void addSignature(const Signature& i_signature)
127 {
128 // First check if this entry already exists.
Patrick Williams8db65db2024-08-16 15:22:30 -0400129 auto itr =
130 std::find(iv_expSigList.begin(), iv_expSigList.end(), i_signature);
Zane Shelley11b89942019-11-07 11:07:28 -0600131 ASSERT_EQ(iv_expSigList.end(), itr);
132
133 // Add the new entry.
134 iv_expSigList.push_back(i_signature);
135 }
136
137 /**
138 * @brief Flushes register and expected signature lists used for a single
139 * isolation.
140 */
141 void flushIterationData()
142 {
143 iv_scomRegData.clear();
144 iv_idScomRegData.clear();
145 iv_expSigList.clear();
146 }
147
148 /** @brief Flushes all simulation data. */
149 void flushAll()
150 {
151 flushIterationData();
152 iv_chipList.clear();
Zane Shelleydcf902b2021-07-15 22:18:35 -0500153 iv_typeList.clear();
Zane Shelley11b89942019-11-07 11:07:28 -0600154 }
155
156 /**
Caleb Palmer446da092025-01-16 10:22:02 -0600157 * @brief Runs the simulation for isolation and verifies the expected
158 * signatures.
159 */
160 void simIsolate(IsolationData& o_isoData);
161
162 /**
Zane Shelley11b89942019-11-07 11:07:28 -0600163 * @brief After an iteration is set up with registers and expected
164 * signatures, this is called to run the simulation and verify the
Caleb Palmer446da092025-01-16 10:22:02 -0600165 * expected signatures and will then flush the iteration data.
Zane Shelley11b89942019-11-07 11:07:28 -0600166 */
167 void endIteration();
168};
169
170} // end namespace libhei
171
172//------------------------------------------------------------------------------
173
174// clang-format off
175
176// The following macros can be used to simplify commonly used function for
177// simulation test cases. At the core of each test case is a Google Test (i.e.
178// gtest), which will do most of the error checking. Just like in gtest, a test
179// case file can contain more than one test. Also, remember that this is all C++
180// code. While it not likely to be used much, you can combine these macros with
181// C++ code to do more advanced test cases. For example, you can put the
182// iteration macros in a loop to walk through each bit of a register.
183
184/**
185 * This is the beginning of a test case. The NAME parameter must be valid C++
186 * identifier and must not contain any underscores (per gtest requirement). To
187 * end the test case use END_TEST_CASE. All contents of the test case must be
188 * contain in between these two macros.
189 */
190#define START_TEST_CASE(NAME) \
191 TEST(Simulator, NAME) \
192 { \
193 libhei::SimulatorData& simData = \
194 libhei::SimulatorData::getSingleton(); \
Zane Shelley1be4c3c2020-04-17 15:55:07 -0500195 simData.flushAll(); \
196 libhei::ChipType_t chipType;
Zane Shelley11b89942019-11-07 11:07:28 -0600197
198/**
199 * Use this to configure a chip object for the test case. There should be an
200 * instance of this macro for each chip required for the test case. Note that
201 * this will also call libhei::initialize() for each new chip type. The CHIP
202 * parameter must be valid C++ identifier because it will be used as the name of
203 * the chip variable. This same identifier will be re-used in several other
204 * macros.
205 */
206#define CHIP(CHIP, TYPE) \
Zane Shelley1be4c3c2020-04-17 15:55:07 -0500207 chipType = static_cast<libhei::ChipType_t>(libhei::SimulatorData::TYPE); \
208 libhei::Chip CHIP{#CHIP, chipType}; \
Zane Shelley11b89942019-11-07 11:07:28 -0600209 simData.addChip(CHIP);
210
211/**
212 * Once all of the chips have been configured, there can be one or more
213 * iterations defined in the test case. Use END_ITERATION to end the iteration.
214 * Note that register and signature information will be reset for each
215 * iteration, however, the same set of configure chips will be used for all
216 * iterations within the test case.
217 */
218#define START_ITERATION \
219 { \
220 simData.flushIterationData();
221
222/** This will add a SCOM register to the current iteration. */
223#define REG_SCOM(CHIP, ADDR, VAL) \
224 simData.addScomReg(CHIP, static_cast<uint32_t>(ADDR), \
225 static_cast<uint64_t>(VAL));
226
227/** This will add an Indirect SCOM register to the current iteration. */
228#define REG_IDSCOM(CHIP, ADDR, VAL) \
229 simData.addIdScomReg(CHIP, static_cast<uint64_t>(ADDR), \
230 static_cast<uint64_t>(VAL));
231
232/** This will add an expected signature to the current iteration. */
233#define EXP_SIG(CHIP, ID, INST, BIT, TYPE) \
234 simData.addSignature(libhei::Signature{ \
235 CHIP, static_cast<libhei::RegisterId_t>(ID), \
Zane Shelley13b182b2020-05-07 20:23:45 -0500236 static_cast<libhei::Instance_t>(INST), \
237 static_cast<libhei::BitPosition_t>(BIT), libhei::ATTN_TYPE_##TYPE});
Zane Shelley11b89942019-11-07 11:07:28 -0600238
239/**
240 * This is the end of an iteration that began with START_ITERATION. All of the
241 * register contents and expected signatures will have been stored in the
242 * simulation data. So, this will call libhei::isolate() with the list of
243 * configured chips. Using the register contents in the simulation data,
244 * libhei::isolate() will return a list of signatures (active attentions). That
245 * list will be compared against the expected list of signatures stored in the
246 * simulation data for test case verification.
247 *
248 * You will see that there are two gtest checks for failures:
249 * - The first check will look to see if any of the previous functions to add
250 * chips, registers, or signatures to the simulation data failed.
251 * - The second check will determine if isolation completed successfully and if
252 * all expected signatures have been verified.
253 * If either check fails, the test case will be aborted regardless if there are
254 * additional iterations in that test case. Note that failure in a test case
255 * will not have any impact on subsequent test cases. Therefore, all test cases
256 * in a file will at least be attempted even if there is a failure.
257 */
258#define END_ITERATION \
259 if (HasFailure()) { simData.flushAll(); return; } \
260 simData.endIteration(); \
261 if (HasFailure()) { simData.flushAll(); return; } \
262 }
263
264/**
265 * This is the end of the test case that started with START_TEST_CASE. It will
266 * call libhei::uninitialize() and clean up the simulation data.
267 */
268#define END_TEST_CASE \
269 libhei::uninitialize(); \
270 simData.flushAll(); \
271 }
272
273// clang-format on