core_timing.cpp
1 // Copyright 2016 Dolphin Emulator Project / 2017 Dolphin Emulator Project 2 // Licensed under GPLv2+ 3 // Refer to the license.txt file included. 4 5 #include <catch2/catch_test_macros.hpp> 6 7 #include <array> 8 #include <bitset> 9 #include <string> 10 #include "common/file_util.h" 11 #include "core/core.h" 12 #include "core/core_timing.h" 13 14 // Numbers are chosen randomly to make sure the correct one is given. 15 static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; 16 static constexpr int MAX_SLICE_LENGTH = 17 BASE_CLOCK_RATE_ARM11 / 234; // Copied from CoreTiming internals 18 19 static std::bitset<CB_IDS.size()> callbacks_ran_flags; 20 static u64 expected_callback = 0; 21 static s64 lateness = 0; 22 23 template <unsigned int IDX> 24 void CallbackTemplate(std::uintptr_t user_data, s64 cycles_late) { 25 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 26 callbacks_ran_flags.set(IDX); 27 REQUIRE(CB_IDS[IDX] == user_data); 28 REQUIRE(CB_IDS[IDX] == expected_callback); 29 REQUIRE(lateness == cycles_late); 30 } 31 32 static void AdvanceAndCheck(Core::Timing& timing, u32 idx, int downcount, int expected_lateness = 0, 33 int cpu_downcount = 0) { 34 callbacks_ran_flags = 0; 35 expected_callback = CB_IDS[idx]; 36 lateness = expected_lateness; 37 38 timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount() - 39 cpu_downcount); // Pretend we executed X cycles of instructions. 40 41 timing.GetTimer(0)->Advance(); 42 timing.GetTimer(0)->SetNextSlice(); 43 44 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags); 45 REQUIRE(downcount == timing.GetTimer(0)->GetDowncount()); 46 } 47 48 TEST_CASE("CoreTiming[BasicOrder]", "[core]") { 49 Core::Timing timing(1, 100); 50 51 Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); 52 Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); 53 Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>); 54 Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", CallbackTemplate<3>); 55 Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", CallbackTemplate<4>); 56 57 // Enter slice 0 58 timing.GetTimer(0)->Advance(); 59 timing.GetTimer(0)->SetNextSlice(); 60 61 // D -> B -> C -> A -> E 62 timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0); 63 REQUIRE(1000 == timing.GetTimer(0)->GetDowncount()); 64 timing.ScheduleEvent(500, cb_b, CB_IDS[1], 0); 65 REQUIRE(500 == timing.GetTimer(0)->GetDowncount()); 66 timing.ScheduleEvent(800, cb_c, CB_IDS[2], 0); 67 REQUIRE(500 == timing.GetTimer(0)->GetDowncount()); 68 timing.ScheduleEvent(100, cb_d, CB_IDS[3], 0); 69 REQUIRE(100 == timing.GetTimer(0)->GetDowncount()); 70 timing.ScheduleEvent(1200, cb_e, CB_IDS[4], 0); 71 REQUIRE(100 == timing.GetTimer(0)->GetDowncount()); 72 73 AdvanceAndCheck(timing, 3, 400); 74 AdvanceAndCheck(timing, 1, 300); 75 AdvanceAndCheck(timing, 2, 200); 76 AdvanceAndCheck(timing, 0, 200); 77 AdvanceAndCheck(timing, 4, MAX_SLICE_LENGTH); 78 } 79 80 namespace SharedSlotTest { 81 static unsigned int counter = 0; 82 83 template <unsigned int ID> 84 void FifoCallback(std::uintptr_t user_data, s64 cycles_late) { 85 static_assert(ID < CB_IDS.size(), "ID out of range"); 86 callbacks_ran_flags.set(ID); 87 REQUIRE(CB_IDS[ID] == user_data); 88 REQUIRE(ID == counter); 89 REQUIRE(lateness == cycles_late); 90 ++counter; 91 } 92 } // namespace SharedSlotTest 93 94 TEST_CASE("CoreTiming[SharedSlot]", "[core]") { 95 using namespace SharedSlotTest; 96 97 Core::Timing timing(1, 100); 98 99 Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", FifoCallback<0>); 100 Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", FifoCallback<1>); 101 Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", FifoCallback<2>); 102 Core::TimingEventType* cb_d = timing.RegisterEvent("callbackD", FifoCallback<3>); 103 Core::TimingEventType* cb_e = timing.RegisterEvent("callbackE", FifoCallback<4>); 104 105 timing.ScheduleEvent(1000, cb_a, CB_IDS[0], 0); 106 timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0); 107 timing.ScheduleEvent(1000, cb_c, CB_IDS[2], 0); 108 timing.ScheduleEvent(1000, cb_d, CB_IDS[3], 0); 109 timing.ScheduleEvent(1000, cb_e, CB_IDS[4], 0); 110 111 // Enter slice 0 112 timing.GetTimer(0)->Advance(); 113 timing.GetTimer(0)->SetNextSlice(); 114 REQUIRE(1000 == timing.GetTimer(0)->GetDowncount()); 115 116 callbacks_ran_flags = 0; 117 counter = 0; 118 lateness = 0; 119 timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); 120 timing.GetTimer(0)->Advance(); 121 timing.GetTimer(0)->SetNextSlice(); 122 REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount()); 123 REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong()); 124 } 125 126 TEST_CASE("CoreTiming[PredictableLateness]", "[core]") { 127 Core::Timing timing(1, 100); 128 129 Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); 130 Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); 131 132 // Enter slice 0 133 timing.GetTimer(0)->Advance(); 134 timing.GetTimer(0)->SetNextSlice(); 135 136 timing.ScheduleEvent(100, cb_a, CB_IDS[0], 0); 137 timing.ScheduleEvent(200, cb_b, CB_IDS[1], 0); 138 139 AdvanceAndCheck(timing, 0, 90, 10, -10); // (100 - 10) 140 AdvanceAndCheck(timing, 1, MAX_SLICE_LENGTH, 50, -50); 141 } 142 143 namespace ChainSchedulingTest { 144 static int reschedules = 0; 145 146 static void RescheduleCallback(Core::Timing& timing, std::uintptr_t user_data, s64 cycles_late) { 147 --reschedules; 148 REQUIRE(reschedules >= 0); 149 REQUIRE(lateness == cycles_late); 150 151 if (reschedules > 0) 152 timing.ScheduleEvent(1000, reinterpret_cast<Core::TimingEventType*>(user_data), user_data); 153 } 154 } // namespace ChainSchedulingTest 155 156 TEST_CASE("CoreTiming[ChainScheduling]", "[core]") { 157 using namespace ChainSchedulingTest; 158 159 Core::Timing timing(1, 100); 160 161 Core::TimingEventType* cb_a = timing.RegisterEvent("callbackA", CallbackTemplate<0>); 162 Core::TimingEventType* cb_b = timing.RegisterEvent("callbackB", CallbackTemplate<1>); 163 Core::TimingEventType* cb_c = timing.RegisterEvent("callbackC", CallbackTemplate<2>); 164 Core::TimingEventType* cb_rs = timing.RegisterEvent( 165 "callbackReschedule", [&timing](std::uintptr_t user_data, s64 cycles_late) { 166 RescheduleCallback(timing, user_data, cycles_late); 167 }); 168 169 // Enter slice 0 170 timing.GetTimer(0)->Advance(); 171 timing.GetTimer(0)->SetNextSlice(); 172 173 timing.ScheduleEvent(800, cb_a, CB_IDS[0], 0); 174 timing.ScheduleEvent(1000, cb_b, CB_IDS[1], 0); 175 timing.ScheduleEvent(2200, cb_c, CB_IDS[2], 0); 176 timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs), 0); 177 REQUIRE(800 == timing.GetTimer(0)->GetDowncount()); 178 179 reschedules = 3; 180 AdvanceAndCheck(timing, 0, 200); // cb_a 181 AdvanceAndCheck(timing, 1, 1000); // cb_b, cb_rs 182 REQUIRE(2 == reschedules); 183 184 timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); 185 timing.GetTimer(0)->Advance(); 186 timing.GetTimer(0)->SetNextSlice(); // cb_rs 187 REQUIRE(1 == reschedules); 188 REQUIRE(200 == timing.GetTimer(0)->GetDowncount()); 189 190 AdvanceAndCheck(timing, 2, 800); // cb_c 191 192 timing.GetTimer(0)->AddTicks(timing.GetTimer(0)->GetDowncount()); 193 timing.GetTimer(0)->Advance(); 194 timing.GetTimer(0)->SetNextSlice(); // cb_rs 195 REQUIRE(0 == reschedules); 196 REQUIRE(MAX_SLICE_LENGTH == timing.GetTimer(0)->GetDowncount()); 197 } 198 199 // TODO: Add tests for multiple timers