xref: /aosp_15_r20/external/executorch/backends/qualcomm/runtime/backends/htpbackend/HtpDevice.cpp (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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