Zane Shelley | 11b8994 | 2019-11-07 11:07:28 -0600 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | #include <hei_main.hpp> |
| 4 | |
| 5 | #include "gtest/gtest.h" |
| 6 | |
| 7 | namespace 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 | */ |
| 15 | class 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 Greenwood | c091934 | 2019-12-10 15:36:17 -0600 | [diff] [blame] | 61 | /** @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 Shelley | 11b8994 | 2019-11-07 11:07:28 -0600 | [diff] [blame] | 73 | /** @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 |