1 /*
2 * Copyright (c) Qualcomm Innovation Center, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9 #include <executorch/backends/qualcomm/runtime/Logging.h>
10 #include <executorch/backends/qualcomm/runtime/backends/htpbackend/HtpDevice.h>
11
12 #include "HTP/QnnHtpCommon.h"
13 #include "Saver/QnnSaverCommon.h"
14
15 namespace executorch {
16 namespace backends {
17 namespace qnn {
18
19 using executorch::runtime::Error;
20
21 // constexpr config values
22 constexpr const int kSleepMinLatency = 40;
23 constexpr const int kSleepLowLatency = 100;
24 constexpr const int kSleepMediumLatency = 1000;
25 constexpr const int kSleepHighLatency = 2000;
26 constexpr const int kDcvsDisable = 0;
27 constexpr const int kDcvsEnable = 1;
28
29 // default rpc control latency - 100 us
30 constexpr const int kRpcControlLatency = 100;
31 // default rpc polling time for high power modes - 9999 us
32 constexpr const int kRpcPollingTimeHighPower = 9999;
33 // default rpc polling time for low power modes - 0 us
34 constexpr const int kRpcPollingTimeLowPower = 0;
35
36 // the number of Rpc Polling config
37 constexpr const int kNumRpcPollingPowerConfigs = 2;
38
39 namespace {
40 template <typename... Args>
HtpPerfInfraStubForSaver(Args...args)41 Qnn_ErrorHandle_t HtpPerfInfraStubForSaver(Args... args) {
42 return QNN_SUCCESS;
43 }
44
GetPerfInfra(const QnnInterface & qnn_interface,QnnHtpDevice_PerfInfrastructure_t * p_out)45 Error GetPerfInfra(
46 const QnnInterface& qnn_interface,
47 QnnHtpDevice_PerfInfrastructure_t* p_out) {
48 if (qnn_interface.GetBackendId() == QNN_BACKEND_ID_SAVER) {
49 p_out->createPowerConfigId = HtpPerfInfraStubForSaver;
50 p_out->destroyPowerConfigId = HtpPerfInfraStubForSaver;
51 p_out->setPowerConfig = HtpPerfInfraStubForSaver;
52 p_out->setMemoryConfig = HtpPerfInfraStubForSaver;
53 return Error::Ok;
54 }
55
56 QnnDevice_Infrastructure_t device_infra = nullptr;
57 Qnn_ErrorHandle_t error =
58 qnn_interface.qnn_device_get_infrastructure(&device_infra);
59
60 if (error != QNN_SUCCESS) {
61 QNN_EXECUTORCH_LOG_ERROR(
62 "HTP backend perf_infrastructure "
63 "creation failed. Error %d",
64 QNN_GET_ERROR_CODE(error));
65 return Error::Internal;
66 }
67
68 auto* htp_infra = static_cast<QnnHtpDevice_Infrastructure_t*>(device_infra);
69 if (htp_infra->infraType != QNN_HTP_DEVICE_INFRASTRUCTURE_TYPE_PERF) {
70 QNN_EXECUTORCH_LOG_ERROR(
71 "HTP infra type = %d, which is "
72 "not perf infra type.",
73 htp_infra->infraType);
74 return Error::Internal;
75 }
76
77 *p_out = htp_infra->perfInfra;
78 return Error::Ok;
79 }
80
SetVotePowerConfig(const std::uint32_t power_config_id,const QnnExecuTorchHtpPerformanceMode perf_mode,const HtpDevice::PerformanceModeVoteType vote_type)81 std::vector<QnnHtpPerfInfrastructure_PowerConfig_t> SetVotePowerConfig(
82 const std::uint32_t power_config_id,
83 const QnnExecuTorchHtpPerformanceMode perf_mode,
84 const HtpDevice::PerformanceModeVoteType vote_type) {
85 constexpr const int kNumConfigs = 1;
86 std::vector<QnnHtpPerfInfrastructure_PowerConfig_t> power_configs(
87 kNumConfigs);
88
89 QnnHtpPerfInfrastructure_PowerConfig_t& dcvs_config = power_configs[0];
90
91 dcvs_config.option = QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_DCVS_V3;
92 QnnHtpPerfInfrastructure_DcvsV3_t& dcvs_v3 = dcvs_config.dcvsV3Config;
93 dcvs_v3.contextId = power_config_id;
94
95 // Check DownVote before performance mode
96 if (vote_type == HtpDevice::PerformanceModeVoteType::kDownVote) {
97 dcvs_v3.setSleepDisable = 0; // false
98 dcvs_v3.sleepDisable = 0;
99
100 dcvs_v3.setDcvsEnable = 1; // true
101 dcvs_v3.dcvsEnable = kDcvsEnable;
102
103 dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_POWER_SAVER_MODE;
104
105 dcvs_v3.setSleepLatency = 1; // true
106 dcvs_v3.sleepLatency = kSleepHighLatency;
107
108 dcvs_v3.setBusParams = 1;
109 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2;
110 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS2;
111 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS2;
112
113 dcvs_v3.setCoreParams = 1;
114 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2;
115 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS2;
116 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS2;
117
118 return power_configs;
119 }
120
121 // Upvote
122 dcvs_v3.setSleepDisable = 0;
123 dcvs_v3.sleepDisable = 0;
124
125 dcvs_v3.setDcvsEnable = 1;
126 dcvs_v3.dcvsEnable = kDcvsDisable;
127
128 dcvs_v3.powerMode = QNN_HTP_PERF_INFRASTRUCTURE_POWERMODE_PERFORMANCE_MODE;
129
130 // choose performance mode
131 switch (perf_mode) {
132 case QnnExecuTorchHtpPerformanceMode::kHtpBurst:
133 dcvs_v3.setSleepLatency = 1; // true
134 dcvs_v3.sleepLatency = kSleepMinLatency;
135
136 dcvs_v3.setBusParams = 1;
137 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
138 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
139 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
140
141 dcvs_v3.setCoreParams = 1;
142 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
143 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
144 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_MAX_VOLTAGE_CORNER;
145 break;
146 case QnnExecuTorchHtpPerformanceMode::kHtpSustainedHighPerformance:
147 case QnnExecuTorchHtpPerformanceMode::kHtpHighPerformance:
148 dcvs_v3.setSleepLatency = 1; // true
149 dcvs_v3.sleepLatency = kSleepLowLatency;
150
151 dcvs_v3.setBusParams = 1;
152 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_TURBO;
153 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_TURBO;
154 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_TURBO;
155
156 dcvs_v3.setCoreParams = 1;
157 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_TURBO;
158 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_TURBO;
159 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_TURBO;
160 break;
161 case QnnExecuTorchHtpPerformanceMode::kHtpPowerSaver:
162 dcvs_v3.setSleepLatency = 1; // true
163 dcvs_v3.sleepLatency = kSleepMediumLatency;
164
165 dcvs_v3.setBusParams = 1;
166 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS;
167 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS;
168 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS;
169
170 dcvs_v3.setCoreParams = 1;
171 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS;
172 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS;
173 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS;
174 break;
175 case QnnExecuTorchHtpPerformanceMode::kHtpLowPowerSaver:
176 dcvs_v3.setSleepLatency = 1; // true
177 dcvs_v3.sleepLatency = kSleepMediumLatency;
178
179 dcvs_v3.setBusParams = 1;
180 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2;
181 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS2;
182 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS2;
183
184 dcvs_v3.setCoreParams = 1;
185 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS2;
186 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS2;
187 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS2;
188 break;
189 case QnnExecuTorchHtpPerformanceMode::kHtpHighPowerSaver:
190 dcvs_v3.setSleepLatency = 1; // true
191 dcvs_v3.sleepLatency = kSleepMediumLatency;
192
193 dcvs_v3.setBusParams = 1;
194 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
195 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
196 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
197
198 dcvs_v3.setCoreParams = 1;
199 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
200 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
201 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_SVS_PLUS;
202 break;
203 case QnnExecuTorchHtpPerformanceMode::kHtpLowBalanced:
204 dcvs_v3.setSleepLatency = 1; // true
205 dcvs_v3.sleepLatency = kSleepMediumLatency;
206
207 dcvs_v3.setBusParams = 1;
208 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_NOM;
209 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_NOM;
210 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_NOM;
211
212 dcvs_v3.setCoreParams = 1;
213 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_NOM;
214 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_NOM;
215 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_NOM;
216 break;
217 case QnnExecuTorchHtpPerformanceMode::kHtpBalanced:
218 dcvs_v3.setSleepLatency = 1; // true
219 dcvs_v3.sleepLatency = kSleepMediumLatency;
220
221 dcvs_v3.setBusParams = 1;
222 dcvs_v3.busVoltageCornerMin = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
223 dcvs_v3.busVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
224 dcvs_v3.busVoltageCornerMax = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
225
226 dcvs_v3.setCoreParams = 1;
227 dcvs_v3.coreVoltageCornerMin = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
228 dcvs_v3.coreVoltageCornerTarget = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
229 dcvs_v3.coreVoltageCornerMax = DCVS_VOLTAGE_VCORNER_NOM_PLUS;
230 break;
231 default:
232 QNN_EXECUTORCH_LOG_ERROR(
233 "Invalid performance profile "
234 "%d to set power configs",
235 perf_mode);
236 break;
237 }
238
239 return power_configs;
240 }
241
SetRpcPollingPowerConfig(QnnExecuTorchHtpPerformanceMode perf_mode)242 std::vector<QnnHtpPerfInfrastructure_PowerConfig_t> SetRpcPollingPowerConfig(
243 QnnExecuTorchHtpPerformanceMode perf_mode) {
244 std::vector<QnnHtpPerfInfrastructure_PowerConfig_t> power_configs(
245 kNumRpcPollingPowerConfigs);
246
247 QnnHtpPerfInfrastructure_PowerConfig_t& rpc_control_latency =
248 power_configs[0];
249 QnnHtpPerfInfrastructure_PowerConfig_t& rpc_polling_time = power_configs[1];
250
251 // configs
252 rpc_control_latency.option =
253 QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_CONTROL_LATENCY;
254 rpc_polling_time.option =
255 QNN_HTP_PERF_INFRASTRUCTURE_POWER_CONFIGOPTION_RPC_POLLING_TIME;
256
257 rpc_control_latency.rpcControlLatencyConfig = kRpcControlLatency;
258 switch (perf_mode) {
259 case QnnExecuTorchHtpPerformanceMode::kHtpBurst:
260 case QnnExecuTorchHtpPerformanceMode::kHtpSustainedHighPerformance:
261 case QnnExecuTorchHtpPerformanceMode::kHtpHighPerformance:
262 rpc_polling_time.rpcPollingTimeConfig = kRpcPollingTimeHighPower;
263 break;
264 case QnnExecuTorchHtpPerformanceMode::kHtpPowerSaver:
265 case QnnExecuTorchHtpPerformanceMode::kHtpLowPowerSaver:
266 case QnnExecuTorchHtpPerformanceMode::kHtpHighPowerSaver:
267 case QnnExecuTorchHtpPerformanceMode::kHtpLowBalanced:
268 case QnnExecuTorchHtpPerformanceMode::kHtpBalanced:
269 case QnnExecuTorchHtpPerformanceMode::kHtpDefault:
270 rpc_polling_time.rpcPollingTimeConfig = kRpcPollingTimeLowPower;
271 break;
272 default:
273 QNN_EXECUTORCH_LOG_ERROR(
274 "Invalid performance profile "
275 "%d to set power configs",
276 perf_mode);
277 break;
278 }
279 return power_configs;
280 }
281
282 } // namespace
283
~HtpDevice()284 HtpDevice::~HtpDevice() {
285 if (htp_perf_infra_ != nullptr && powerconfig_client_id_ != 0 &&
286 !down_vote_power_configs_ptr_.empty()) {
287 htp_perf_infra_->setPowerConfig(
288 powerconfig_client_id_, down_vote_power_configs_ptr_.data());
289 htp_perf_infra_->destroyPowerConfigId(powerconfig_client_id_);
290 } else if (htp_perf_infra_ != nullptr && powerconfig_client_id_ != 0) {
291 htp_perf_infra_->destroyPowerConfigId(powerconfig_client_id_);
292 }
293 }
294
MakeConfig(std::vector<const QnnDevice_Config_t * > & config)295 Error HtpDevice::MakeConfig(std::vector<const QnnDevice_Config_t*>& config) {
296 std::vector<QnnDevice_CustomConfig_t> device_custom_config =
297 htp_device_custom_config_->CreateDeviceCustomConfig(
298 qcom_target_soc_info_);
299 QnnHtpDevice_CustomConfig_t* p_custom_config = nullptr;
300
301 if (QNN_HTP_API_VERSION_MAJOR <= QNN_HTP_DEPRECATED_HTP_ARCH_VERSION_MAJOR &&
302 QNN_HTP_API_VERSION_MINOR <= QNN_HTP_DEPRECATED_HTP_ARCH_VERSION_MINOR) {
303 p_custom_config = htp_device_custom_config_->AllocDeviceCustomConfig();
304 p_custom_config->option = QNN_HTP_DEVICE_CONFIG_OPTION_ARCH;
305 p_custom_config->arch.deviceId = 0;
306 p_custom_config->arch.arch = static_cast<QnnHtpDevice_Arch_t>(
307 qcom_target_soc_info_->htp_info()->htp_arch());
308 device_custom_config.push_back(
309 static_cast<QnnDevice_CustomConfig_t>(p_custom_config));
310 }
311
312 switch (htp_options_->pd_session()) {
313 case QnnExecuTorchHtpPdSession::kHtpSignedPd:
314 p_custom_config = htp_device_custom_config_->AllocDeviceCustomConfig();
315 p_custom_config->option = QNN_HTP_DEVICE_CONFIG_OPTION_SIGNEDPD;
316 p_custom_config->useSignedProcessDomain.useSignedProcessDomain = true;
317 p_custom_config->useSignedProcessDomain.deviceId = 0;
318 device_custom_config.push_back(
319 static_cast<QnnDevice_CustomConfig_t>(p_custom_config));
320 break;
321 case QnnExecuTorchHtpPdSession::kHtpUnsignedPd:
322 default:
323 break;
324 }
325
326 const std::vector<QnnDevice_PlatformInfo_t*>& device_platform_info =
327 htp_device_platform_info_config_->CreateDevicePlatformInfo(
328 qcom_target_soc_info_);
329
330 uint32_t num_custom_configs =
331 device_platform_info.size() + device_custom_config.size();
332 device_config_.resize(num_custom_configs);
333 // +1 for null terminated
334 config.reserve(num_custom_configs + 1);
335
336 for (std::size_t i = 0; i < device_custom_config.size(); ++i) {
337 device_config_[i].option = QNN_DEVICE_CONFIG_OPTION_CUSTOM;
338 device_config_[i].customConfig = device_custom_config[i];
339 config.push_back(&device_config_[i]);
340 }
341
342 if (!device_platform_info.empty()) {
343 // Below codes use `Device_config_[device_custom_config.size()]` which imply
344 // the length of platform info can only be 1.
345 ET_CHECK_OR_RETURN_ERROR(
346 device_platform_info.size() == 1u,
347 Internal,
348 "Error! Device platform info size != 1, got %zu",
349 device_platform_info.size());
350 device_config_[device_custom_config.size()].option =
351 QNN_DEVICE_CONFIG_OPTION_PLATFORM_INFO;
352 device_config_[device_custom_config.size()].hardwareInfo =
353 device_platform_info.back();
354 config.push_back(&device_config_[device_custom_config.size()]);
355 }
356
357 // null terminated
358 config.push_back(nullptr);
359
360 return Error::Ok;
361 }
362
PerformanceVote()363 void HtpDevice::PerformanceVote() {
364 if (IsPerfModeEnabled()) {
365 htp_perf_infra_->setPowerConfig(
366 powerconfig_client_id_, perf_power_configs_ptr_.data());
367 }
368 };
369
ReleasePerformanceVote()370 void HtpDevice::ReleasePerformanceVote() {
371 if (IsPerfModeEnabled()) {
372 htp_perf_infra_->setPowerConfig(
373 powerconfig_client_id_, down_vote_power_configs_ptr_.data());
374 }
375 };
376
AfterCreateDevice()377 Error HtpDevice::AfterCreateDevice() {
378 if (IsPerfModeEnabled()) {
379 const QnnInterface& qnn_interface = implementation_.GetQnnInterface();
380 Qnn_ErrorHandle_t error = QNN_SUCCESS;
381
382 // Get htp_perf_infra
383 htp_perf_infra_ = &owned_htp_perf_infra_;
384 if (GetPerfInfra(qnn_interface, htp_perf_infra_) != Error::Ok) {
385 return Error::Internal;
386 }
387
388 // Get power client id
389 error = htp_perf_infra_->createPowerConfigId(
390 /*device_id=*/0, /*core_id=*/0, &powerconfig_client_id_);
391
392 if (error != QNN_SUCCESS) {
393 QNN_EXECUTORCH_LOG_ERROR(
394 "HTP backend unable to create "
395 "power config. Error %d",
396 QNN_GET_ERROR_CODE(error));
397 return Error::Internal;
398 }
399
400 // Set vector of PowerConfigs and map it to a vector of pointers.
401 perf_power_configs_ = SetVotePowerConfig(
402 powerconfig_client_id_,
403 htp_options_->performance_mode(),
404 PerformanceModeVoteType::kUpVote);
405 perf_power_configs_ptr_ = ObtainNullTermPtrVector(perf_power_configs_);
406
407 down_vote_power_configs_ = SetVotePowerConfig(
408 powerconfig_client_id_,
409 QnnExecuTorchHtpPerformanceMode::kHtpDefault,
410 PerformanceModeVoteType::kDownVote);
411 down_vote_power_configs_ptr_ =
412 ObtainNullTermPtrVector(down_vote_power_configs_);
413
414 // vote immediately
415 PerformanceVote();
416
417 // Set Rpc polling mode
418 rpc_power_configs_ =
419 SetRpcPollingPowerConfig(htp_options_->performance_mode());
420 rpc_power_configs_ptr_ = ObtainNullTermPtrVector(rpc_power_configs_);
421
422 htp_perf_infra_->setPowerConfig(
423 powerconfig_client_id_, rpc_power_configs_ptr_.data());
424 }
425
426 return Error::Ok;
427 }
428
429 } // namespace qnn
430 } // namespace backends
431 } // namespace executorch
432