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