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