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