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