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