| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| #include "hexagon_vtcm_pool.h" |
| |
| #include <tvm/runtime/logging.h> |
| |
| #include "HAP_compute_res.h" |
| #include "hexagon_common.h" |
| |
| namespace tvm { |
| namespace runtime { |
| namespace hexagon { |
| |
| HexagonVtcmPool::HexagonVtcmPool() { |
| compute_res_attr_t res_info; |
| HEXAGON_SAFE_CALL(HAP_compute_res_attr_init(&res_info)); |
| |
| unsigned int avail_block_size; |
| compute_res_vtcm_page_t total_block_layout; |
| compute_res_vtcm_page_t avail_block_layout; |
| |
| HEXAGON_SAFE_CALL(compute_resource_query_VTCM(/* application_id = */ 0, &vtcm_device_size_, |
| &total_block_layout, &avail_block_size, |
| &avail_block_layout)); |
| DLOG(INFO) << "HexagonVtcmPool total " << vtcm_device_size_ << " avail " << avail_block_size; |
| TVM_FFI_ICHECK(avail_block_size >= (1024 * 1024)) << "Less than 1MB VTCM available"; |
| |
| // allocate nbytes of vtcm on a single page |
| HEXAGON_SAFE_CALL(HAP_compute_res_attr_set_vtcm_param_v2(&res_info, |
| /*vtcm_size = */ vtcm_device_size_, |
| /*min_page_size = */ 1, |
| /*min_vtcm_size = */ avail_block_size)); |
| |
| // TODO(HWE): Investigate why a non-zero timeout results in |
| // hanging, both in the simulator and on hardware. |
| context_id_ = HAP_compute_res_acquire(&res_info, /*timeout = */ 0); |
| TVM_FFI_ICHECK(context_id_) |
| << "HAP_compute_res_acquire failed to acquire requested VTCM resource."; |
| HEXAGON_SAFE_CALL( |
| HAP_compute_res_attr_get_vtcm_ptr_v2(&res_info, &vtcm_data_, &vtcm_allocated_size_)); |
| TVM_FFI_ICHECK(vtcm_data_ != nullptr) |
| << "HAP_compute_res_acquire returned nullptr when allocating VTCM."; |
| TVM_FFI_ICHECK(vtcm_allocated_size_ >= avail_block_size) |
| << "HAP_compute_res_acquire failed to allocate minimum amount of VTCM"; |
| free_.emplace_back( |
| std::pair<char*, size_t>(static_cast<char*>(vtcm_data_), vtcm_allocated_size_)); |
| // DebugDump(); |
| } |
| |
| HexagonVtcmPool::~HexagonVtcmPool() { HEXAGON_SAFE_CALL(HAP_compute_res_release(context_id_)); } |
| |
| void* HexagonVtcmPool::Allocate(size_t nbytes) { |
| std::lock_guard<std::mutex> lock(mutex_); |
| |
| TVM_FFI_ICHECK(!free_.empty()) << "No free VTCM"; |
| TVM_FFI_ICHECK(nbytes >= 0x80) << "Minimum VTCM alloation must be 128 bytes - nbytes " << nbytes; |
| |
| // If this is not aligned on a 2k block, allocate from the end to avoid fragmentation |
| if (nbytes & size_t(0x7FF)) { |
| DLOG(INFO) << "VTCM nbytes requested: " << nbytes << " allocate from the end"; |
| auto last_free_entry = free_.end(); |
| last_free_entry--; |
| TVM_FFI_ICHECK(last_free_entry->second >= nbytes) |
| << "Not enough contiguous VTCM space at the end to allocate"; |
| char* ptr = last_free_entry->first + (last_free_entry->second - nbytes); |
| allocations_.emplace_back(std::pair<char*, size_t>(ptr, nbytes)); |
| last_free_entry->second -= nbytes; |
| if (last_free_entry->second == 0) { |
| free_.erase(last_free_entry); |
| } |
| // DebugDump(); |
| return ptr; |
| } |
| |
| auto entry_to_allocate = free_.begin(); |
| for (auto it = free_.begin(); it != free_.end(); it++) { |
| if ((entry_to_allocate->second < nbytes || it->second < entry_to_allocate->second) && |
| it->second >= nbytes) { |
| entry_to_allocate = it; |
| if (entry_to_allocate->second == nbytes) { |
| break; |
| } |
| } |
| } |
| TVM_FFI_ICHECK(entry_to_allocate->second >= nbytes) |
| << "Not enough contiguous VTCM space to allocate"; |
| char* ptr = entry_to_allocate->first; |
| allocations_.emplace(allocations_.end(), std::pair<char*, size_t>(ptr, nbytes)); |
| |
| if (entry_to_allocate->second == nbytes) { |
| free_.erase(entry_to_allocate); |
| } else { |
| entry_to_allocate->first = entry_to_allocate->first + nbytes; |
| entry_to_allocate->second = entry_to_allocate->second - nbytes; |
| } |
| // DebugDump(); |
| return ptr; |
| } |
| |
| void HexagonVtcmPool::Free(void* ptr, size_t nbytes) { |
| char* ptr_to_free = static_cast<char*>(ptr); |
| std::lock_guard<std::mutex> lock(mutex_); |
| |
| auto it = std::find_if(allocations_.begin(), allocations_.end(), |
| [&](auto entry) { return entry.first == ptr_to_free; }); |
| TVM_FFI_ICHECK(it != allocations_.end()) |
| << "Attempted to free a pointer that had not been allocated"; |
| TVM_FFI_ICHECK(it->second == nbytes) << "Attempted to free a different size than was allocated"; |
| allocations_.erase(it); |
| |
| it = std::lower_bound(free_.begin(), free_.end(), std::pair<char*, size_t>(ptr_to_free, nbytes), |
| [](auto p, auto q) { return p.first <= q.first; }); |
| if (it == free_.end()) { |
| // Insert an entry at the end |
| it = free_.emplace(it, std::pair<char*, size_t>(ptr_to_free, nbytes)); |
| } else { |
| TVM_FFI_ICHECK(ptr_to_free != it->first) |
| << "Attempting to free a pointer that was already free"; |
| TVM_FFI_ICHECK(ptr_to_free + nbytes <= it->first) |
| << "free_ is in an inconsistent state, freed block overlaps with next"; |
| if (ptr_to_free + nbytes == it->first) { |
| // Make this entry bigger |
| it->first = ptr_to_free; |
| it->second += nbytes; |
| } else { |
| // Insert an entry before this |
| it = free_.emplace(it, std::pair<char*, size_t>(ptr_to_free, nbytes)); |
| } |
| } |
| |
| // Check for overlap with the previous entry |
| if (it != free_.begin()) { |
| auto it_prev = it; |
| it_prev--; |
| TVM_FFI_ICHECK(it_prev->first + it_prev->second <= ptr_to_free) |
| << "free_ is in an inconsistent state, freed block overlaps with previous"; |
| if (it_prev->first + it_prev->second == ptr_to_free) { |
| it_prev->second += it->second; |
| free_.erase(it); |
| } |
| } |
| // DebugDump(); |
| } |
| |
| void HexagonVtcmPool::DebugDump() { |
| LOG(INFO) << "VTCM list state"; |
| for (auto entry : allocations_) { |
| LOG(INFO) << "VTCM alloc: " << static_cast<void*>(entry.first) << " " << entry.second; |
| } |
| for (auto entry : free_) { |
| LOG(INFO) << "VTCM free: " << static_cast<void*>(entry.first) << " " << entry.second; |
| } |
| } |
| |
| } // namespace hexagon |
| } // namespace runtime |
| } // namespace tvm |