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