Repository: Xilinx/XRT
Pull request #8846: Add API in xrt::run to get control scratchpad memory
Author: login: rbramand-xilinx
Created: 2025-04-01T09:13:08Z | Merged: 2025-04-03T17:20:27Z

<!-- Thanks for sending a pull request! Please fill out below, remove sections that don't apply for your pull request.  -->
#### Problem solved by the commit
Added new API to get control scratchpad memory.
This memory is used by NPU to store control state information of a model. ELF is parsed to look for this section and a xrt::bo object is created corresponding to it.
This PR provides API  to get this buffer object so that users can read/write to this memory.

#### Bug / issue (if any) fixed, which PR introduced the bug, how it was discovered
#### How problem was solved, alternative solutions (if any) and why they were rejected
Provided required function with xrt::run class as each running instance of a model will have its own control scratchpad memory

#### Risks (if any) associated the changes in the commit
Low

#### What has been tested and how, request additional testing if necessary
Tested with Preemption application on strix linux and things work as expected

#### Documentation impact (if any)
Added comments with the API declaration.


Diff:
diff --git a/src/runtime_src/core/common/api/module_int.h b/src/runtime_src/core/common/api/module_int.h
index 98f849452bd..d47cc2f41e6 100644
--- a/src/runtime_src/core/common/api/module_int.h
+++ b/src/runtime_src/core/common/api/module_int.h
@@ -77,6 +77,12 @@ get_kernel_info(const xrt::module& module);
 void
 dump_dtrace_buffer(const xrt::module& module);
 
+// Returns buffer object associated with control scratchpad memory.
+// This memory is created using ELF associated with run object.
+// Throws if ELF doesn't contain scratchpad memory
+xrt::bo
+get_ctrl_scratchpad_bo(const xrt::module& module);
+
 } // xrt_core::module_int
 
 #endif
diff --git a/src/runtime_src/core/common/api/xrt_elf.cpp b/src/runtime_src/core/common/api/xrt_elf.cpp
index 700d875be22..34fdad8a5fc 100644
--- a/src/runtime_src/core/common/api/xrt_elf.cpp
+++ b/src/runtime_src/core/common/api/xrt_elf.cpp
@@ -90,7 +90,7 @@ class elf_impl
     if (auto section = m_elf.sections[".note.xrt.configuration"])
       return std::stoul(get_note(section, 0));
 
-    throw std::runtime_error("ELF is missing xrt configuration info");  
+    throw std::runtime_error("ELF is missing xrt configuration info");
   }
 };
 
diff --git a/src/runtime_src/core/common/api/xrt_kernel.cpp b/src/runtime_src/core/common/api/xrt_kernel.cpp
index fc1c6bb7ecd..faa4d5b0545 100644
--- a/src/runtime_src/core/common/api/xrt_kernel.cpp
+++ b/src/runtime_src/core/common/api/xrt_kernel.cpp
@@ -2593,6 +2593,15 @@ class run_impl
   {
     return cmd->get_ert_packet();
   }
+
+  xrt::bo
+  get_ctrl_scratchpad_bo() const
+  {
+    if (!m_module)
+      throw xrt_core::error("No module associated with run object");
+  
+    return xrt_core::module_int::get_ctrl_scratchpad_bo(m_module);
+  }
 };
 
 // class mailbox_impl - Extension of run_impl for mailbox support
@@ -3911,6 +3920,15 @@ submit_signal(const xrt::fence& fence)
   });
 }
 
+xrt::bo
+run::
+get_ctrl_scratchpad_bo() const
+{
+  return xdp::native::profiling_wrapper("xrt::run::get_ctrl_scratchpad_bo", [this]{
+    return handle->get_ctrl_scratchpad_bo();
+  });
+}
+
 run::
 ~run()
 {}
diff --git a/src/runtime_src/core/common/api/xrt_module.cpp b/src/runtime_src/core/common/api/xrt_module.cpp
index bf090faf408..59d9d5f98b1 100644
--- a/src/runtime_src/core/common/api/xrt_module.cpp
+++ b/src/runtime_src/core/common/api/xrt_module.cpp
@@ -69,9 +69,10 @@ static constexpr uint8_t Elf_Amd_Aie2p_config = 70;
 // using max bd words as 9 to cover all cases
 static constexpr size_t max_bd_words = 9;
 
-static const char* Scratch_Pad_Mem_Symbol = "scratch-pad-mem";
-static const char* Control_Packet_Symbol = "control-packet";
-static const char* Control_Code_Symbol = "control-code";
+static const char* const Scratch_Pad_Mem_Symbol = "scratch-pad-mem";
+static const char* const Control_ScratchPad_Symbol = "scratch-pad-ctrl";
+static const char* const Control_Packet_Symbol = "control-packet";
+static const char* const Control_Code_Symbol = "control-code";
 
 struct buf
 {
@@ -513,6 +514,12 @@ class module_impl
     throw std::runtime_error("Not supported");
   }
 
+  virtual size_t
+  get_ctrl_scratch_pad_mem_size() const
+  {
+    throw std::runtime_error("Not supported");
+  }
+
   virtual std::pair<uint32_t, const control_packet&>
   get_ctrlpkt(uint32_t /*index*/ = 0) const
   {
@@ -564,20 +571,6 @@ class module_impl
     throw std::runtime_error("Not supported");
   }
 
-  // Patch ctrlcode buffer object for global argument
-  //
-  // @param bo_ctrlcode - bo containing ctrlcode
-  // @param symbol - symbol name
-  // @param index - argument index
-  // @param bo - global argument to patch into ctrlcode
-  // @param buf_type - whether it is control-code, control-packet, preempt-save or preempt-restore
-  // @param sec_index - index of section to be patched
-  virtual void
-  patch_instr(xrt::bo&, const std::string&, size_t, const xrt::bo&, patcher::buf_type, uint32_t)
-  {
-    throw std::runtime_error("Not supported ");
-  }
-
   // Patch ctrlcode buffer object for global argument
   //
   // @param argname - argument name
@@ -834,6 +827,7 @@ class module_elf_aie2p : public module_elf
   bool m_restore_buf_exist = false;
   
   size_t m_scratch_pad_mem_size = 0;
+  size_t m_ctrl_scratch_pad_mem_size = 0;
   uint32_t m_partition_size = UINT32_MAX;
 
   std::set<std::string> m_ctrlpkt_pm_dynsyms; // preemption dynsyms in elf
@@ -1093,6 +1087,9 @@ class module_elf_aie2p : public module_elf
       if (!m_scratch_pad_mem_size && (strcmp(symname, Scratch_Pad_Mem_Symbol) == 0)) {
         m_scratch_pad_mem_size = static_cast<size_t>(sym->st_size);
       }
+      else if (!m_ctrl_scratch_pad_mem_size && (strcmp(symname, Control_ScratchPad_Symbol) == 0)) {
+        m_ctrl_scratch_pad_mem_size = static_cast<size_t>(sym->st_size);
+      }
 
       static constexpr const char* ctrlpkt_pm_dynsym = "ctrlpkt-pm";
       if (std::string(symname).find(ctrlpkt_pm_dynsym) != std::string::npos) {
@@ -1239,6 +1236,12 @@ class module_elf_aie2p : public module_elf
     return m_scratch_pad_mem_size;
   }
 
+  size_t
+  get_ctrl_scratch_pad_mem_size() const override
+  {
+    return m_ctrl_scratch_pad_mem_size;
+  }
+
   std::pair<uint32_t, const buf&>
   get_preempt_save() const override
   {
@@ -1542,6 +1545,7 @@ class module_sram : public module_impl
   xrt::bo m_instr_bo;
   xrt::bo m_ctrlpkt_bo;
   xrt::bo m_scratch_pad_mem;
+  xrt::bo m_ctrl_scratch_pad_mem;
   xrt::bo m_preempt_save_bo;
   xrt::bo m_preempt_restore_bo;
 
@@ -1758,6 +1762,17 @@ class module_sram : public module_impl
       }
     }
 
+    // create ctrl scratchpad buffer and patch it if symbol is present
+    try {
+      auto size = m_parent->get_ctrl_scratch_pad_mem_size();
+      if (size > 0) {
+        m_ctrl_scratch_pad_mem = xrt::bo{ m_hwctx, size, xrt::bo::flags::cacheable, 1 /* fix me */ };
+        patch_instr(m_instr_bo, Control_ScratchPad_Symbol, 0, m_ctrl_scratch_pad_mem,
+                    patcher::buf_type::ctrltext, m_instr_sec_idx);
+      }
+    }
+    catch (...) { /*Do Nothing*/ };
+
     // patch all pdi addresses
     auto pdi_symbols = parent->get_patch_pdis(m_index);
     for (const auto& symbol : pdi_symbols) {
@@ -1845,9 +1860,11 @@ class module_sram : public module_impl
     fill_instruction_buffer(data);
   }
 
+  // Patch the instruction buffer with global argument(xrt::bo)
+  // The symbol to be patched is identified using argnm/index
   void
   patch_instr(xrt::bo& bo_ctrlcode, const std::string& argnm, size_t index, const xrt::bo& bo,
-              patcher::buf_type type, uint32_t sec_idx) override
+              patcher::buf_type type, uint32_t sec_idx)
   {
     patch_instr_value(bo_ctrlcode, argnm, index, bo.address(), type, sec_idx);
   }
@@ -2279,6 +2296,17 @@ class module_sram : public module_impl
                               std::string{"[dtrace] : dtrace buffer dump failed, "} + e.what());
     }
   }
+
+  xrt::bo
+  get_ctrl_scratchpad_bo()
+  {
+    if (!m_ctrl_scratch_pad_mem)
+      throw std::runtime_error("Control scratchpad memory is not present\n");
+  
+    // sync bo data before returning
+    m_ctrl_scratch_pad_mem.sync(XCL_BO_SYNC_BO_FROM_DEVICE);
+    return m_ctrl_scratch_pad_mem;
+  }
 };
 
 } // namespace xrt
@@ -2391,6 +2419,15 @@ dump_dtrace_buffer(const xrt::module& module)
   module_sram->dump_dtrace_buffer();
 }
 
+xrt::bo
+get_ctrl_scratchpad_bo(const xrt::module& module)
+{
+  auto module_sram = std::dynamic_pointer_cast<xrt::module_sram>(module.get_handle());
+  if (!module_sram)
+    throw std::runtime_error("Getting module_sram failed, wrong module object passed\n");
+
+  return module_sram->get_ctrl_scratchpad_bo();
+}
 } // xrt_core::module_int
 
 namespace {
diff --git a/src/runtime_src/core/include/xrt/experimental/xrt_aie.h b/src/runtime_src/core/include/xrt/experimental/xrt_aie.h
index 068dc28f808..ee2992c06f3 100644
--- a/src/runtime_src/core/include/xrt/experimental/xrt_aie.h
+++ b/src/runtime_src/core/include/xrt/experimental/xrt_aie.h
@@ -78,7 +78,7 @@ class program : public xrt::elf
   size_type
   get_partition_size() const;
 };
-  
+
 } // namespace xrt::aie
 
 #endif // __cplusplus
diff --git a/src/runtime_src/core/include/xrt/xrt_kernel.h b/src/runtime_src/core/include/xrt/xrt_kernel.h
index dc1f905e89f..d0210f33c3c 100644
--- a/src/runtime_src/core/include/xrt/xrt_kernel.h
+++ b/src/runtime_src/core/include/xrt/xrt_kernel.h
@@ -614,6 +614,21 @@ class run
     start(count);
   }
 
+  /**
+   * get_ctrl_scratchpad_bo() - Get the ctrl scratchpad bo object
+   * 
+   * NPU uses ctrl scratchpad memory to store control state data.
+   * This memory is created by XRT based on ELF used to create xrt::kernel
+   * The API returns the buffer object (bo) created by XRT allowing
+   * applications to read from or write to it.
+   * This API is only valid for run objects associated with an ELF.
+   * 
+   * Throws if control scratchpad section is not absent in ELF or
+   * if any error occurs while retrieving the bo
+   */
+  xrt::bo
+  get_ctrl_scratchpad_bo() const;
+
 public:
   /// @cond
   const std::shared_ptr<run_impl>&


Reviews:
user: login: github-actions[bot]
body: clang-tidy made some suggestions
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T09:18:32Z
user: login: stsoe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T15:38:12Z
user: login: stsoe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T15:52:35Z
user: login: stsoe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-01T16:04:19Z
user: login: rbramand-xilinx
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T10:52:13Z
user: login: github-actions[bot]
body: clang-tidy made some suggestions
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T12:01:09Z
user: login: stsoe
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-02T20:08:00Z
user: login: larry9523
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-02T20:15:28Z

Review comments:
diff_hunk: @@ -25,6 +25,19 @@ class elf_impl
 {
   ELFIO::elfio m_elf;
 
+  // Structure to hold symbol information of an
+  // entry in .dynsym section
+  struct symbol_info
path: src/runtime_src/core/common/api/xrt_elf.cpp
user: login: github-actions[bot]
body: warning: constructor does not initialize these fields: value, size, bind, type, section_index, other [[cppcoreguidelines-pro-type-member-init](https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-member-init.html)]

src/runtime_src/core/common/api/xrt_elf.cpp:32:
```diff
-     ELFIO::Elf64_Addr value;
-     ELFIO::Elf_Xword size;
-     unsigned char bind;
-     unsigned char type;
-     ELFIO::Elf_Half section_index;
-     unsigned char other;
+     ELFIO::Elf64_Addr value{};
+     ELFIO::Elf_Xword size{};
+     unsigned char bind{};
+     unsigned char type{};
+     ELFIO::Elf_Half section_index{};
+     unsigned char other{};
```

created_at: 2025-04-01T09:18:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -86,11 +99,42 @@ class elf_impl
   uint32_t
   get_partition_size() const
   {
-    // Partition size is stored in as note 0 in .note.xrt.configuration section 
+    // Partition size is stored in as note 0 in .note.xrt.configuration section
     if (auto section = m_elf.sections[".note.xrt.configuration"])
       return std::stoul(get_note(section, 0));
 
-    throw std::runtime_error("ELF is missing xrt configuration info");  
+    throw std::runtime_error("ELF is missing xrt configuration info");
+  }
+
+  symbol_info
+  get_symbol(const ELFIO::section* section, const std::string& symbol_name) const
+  {
+    const ELFIO::symbol_section_accessor symbols(m_elf, const_cast<ELFIO::section*>(section));
path: src/runtime_src/core/common/api/xrt_elf.cpp
user: login: github-actions[bot]
body: warning: do not use const_cast to remove const qualifier [[cppcoreguidelines-pro-type-const-cast](https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/pro-type-const-cast.html)]
```cpp
    const ELFIO::symbol_section_accessor symbols(m_elf, const_cast<ELFIO::section*>(section));
                                                        ^
```

created_at: 2025-04-01T09:18:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,6 +70,7 @@ static constexpr uint8_t Elf_Amd_Aie2p_config = 70;
 static constexpr size_t max_bd_words = 9;
 
 static const char* Scratch_Pad_Mem_Symbol = "scratch-pad-mem";
+static const char* Control_ScratchPad_Symbol = "scratch-pad-ctrl";
path: src/runtime_src/core/common/api/xrt_module.cpp
user: login: github-actions[bot]
body: warning: variable 'Control_ScratchPad_Symbol' is non-const and globally accessible, consider making it const [[cppcoreguidelines-avoid-non-const-global-variables](https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables.html)]
```cpp
static const char* Control_ScratchPad_Symbol = "scratch-pad-ctrl";
                   ^
```

created_at: 2025-04-01T09:18:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1758,6 +1776,17 @@ class module_sram : public module_impl
       }
     }
 
+    // create ctrl scratchpad buffer and patch it if symbol is present
+    try {
+      auto size = m_parent->get_ctrl_scratch_pad_mem_size();
+      if (size > 0) {
+        m_ctrl_scratch_pad_mem = xrt::bo{ m_hwctx, size, xrt::bo::flags::cacheable, 1 /* fix me */ };
+        patch_instr(m_instr_bo, Control_ScratchPad_Symbol, 0, m_ctrl_scratch_pad_mem,
path: src/runtime_src/core/common/api/xrt_module.cpp
user: login: github-actions[bot]
body: warning: Call to virtual method 'module_sram::patch_instr' during construction bypasses virtual dispatch [[clang-analyzer-optin.cplusplus.VirtualCall](https://clang.llvm.org/extra/clang-tidy/checks/clang-analyzer/optin.cplusplus.VirtualCall.html)]
```cpp
        patch_instr(m_instr_bo, Control_ScratchPad_Symbol, 0, m_ctrl_scratch_pad_mem,
        ^
```
<details>
<summary>Additional context</summary>

**src/runtime_src/core/common/api/xrt_module.cpp:2528:** Calling 'make_shared<xrt::module_sram, const std::shared_ptr<xrt::module_impl> &, const xrt::hw_context &, unsigned int &>'
```cpp
: detail::pimpl<module_impl>{ std::make_shared<module_sram>(parent.handle, hwctx, ctrl_code_idx) }
                              ^
```
**/usr/include/c++/13/bits/shared_ptr.h:1008:** Calling constructor for 'shared_ptr<xrt::module_sram>'
```cpp
      return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
             ^
```
**/usr/include/c++/13/bits/shared_ptr.h:463:** Calling constructor for '__shared_ptr<xrt::module_sram, __gnu_cxx::_S_atomic>'
```cpp
	: __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
   ^
```
**/usr/include/c++/13/bits/shared_ptr_base.h:1711:** Calling constructor for '__shared_count<__gnu_cxx::_S_atomic>'
```cpp
	: _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...)
             ^
```
**/usr/include/c++/13/bits/shared_ptr_base.h:971:** Calling constructor for '_Sp_counted_ptr_inplace<xrt::module_sram, std::allocator<void>, __gnu_cxx::_S_atomic>'
```cpp
	    _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
     ^
```
**/usr/include/c++/13/bits/shared_ptr_base.h:603:** Calling 'allocator_traits::construct'
```cpp
	  allocator_traits<_Alloc>::construct(__a, _M_ptr(),
   ^
```
**/usr/include/c++/13/bits/alloc_traits.h:660:** Calling '_Construct<xrt::module_sram, const std::shared_ptr<xrt::module_impl> &, const xrt::hw_context &, unsigned int &>'
```cpp
	{ std::_Construct(__p, std::forward<_Args>(__args)...); }
   ^
```
**/usr/include/c++/13/bits/stl_construct.h:118:** Calling constructor for 'module_sram'
```cpp
      ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
                        ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:2179:** Assuming the condition is false
```cpp
    if (xrt_core::config::get_xrt_debug()) {
        ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:2179:** Taking false branch
```cpp
    if (xrt_core::config::get_xrt_debug()) {
    ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:2189:** Assuming 'os_abi' is equal to 'Elf_Amd_Aie2p'
```cpp
    if (os_abi == Elf_Amd_Aie2p || os_abi == Elf_Amd_Aie2p_config) {
        ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:2189:** Left side of '||' is true
```cpp
    if (os_abi == Elf_Amd_Aie2p || os_abi == Elf_Amd_Aie2p_config) {
                                ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:2194:** Calling 'module_sram::create_instr_buf'
```cpp
      create_instr_buf(m_parent.get());
      ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1716:** Assuming 'sz' is not equal to 0
```cpp
    if (sz == 0)
        ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1716:** Taking false branch
```cpp
    if (sz == 0)
    ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1725:** Taking false branch
```cpp
    if (is_dump_control_codes()) {
    ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1740:** Assuming 'preempt_save_data_size' is <= 0
```cpp
    if ((preempt_save_data_size > 0) && (preempt_restore_data_size > 0)) {
         ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1740:** Left side of '&&' is false
```cpp
    if ((preempt_save_data_size > 0) && (preempt_restore_data_size > 0)) {
                                     ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1764:** 'preempt_save_data_size' is <= 0
```cpp
    if ((preempt_save_data_size > 0) && (preempt_restore_data_size > 0)) {
         ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1764:** Left side of '&&' is false
```cpp
    if ((preempt_save_data_size > 0) && (preempt_restore_data_size > 0)) {
                                     ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1781:** Assuming 'size' is > 0
```cpp
      if (size > 0) {
          ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1781:** Taking true branch
```cpp
      if (size > 0) {
      ^
```
**src/runtime_src/core/common/api/xrt_module.cpp:1783:** Call to virtual method 'module_sram::patch_instr' during construction bypasses virtual dispatch
```cpp
        patch_instr(m_instr_bo, Control_ScratchPad_Symbol, 0, m_ctrl_scratch_pad_mem,
        ^
```

</details>

created_at: 2025-04-01T09:18:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2593,6 +2593,24 @@ class run_impl
   {
     return cmd->get_ert_packet();
   }
+
+  [[nodiscard]] std::vector<char>
path: src/runtime_src/core/common/api/xrt_kernel.cpp
user: login: stsoe
body: There is no harm in discarding this return value.   I suggest you remove [[nodiscard]].
created_at: 2025-04-01T15:33:14Z
author_association: COLLABORATOR
diff_hunk: @@ -614,6 +614,43 @@ class run
     start(count);
   }
 
+  /**
+   * read_ctrl_scratchpad() - Read control scratchpad memory
+   * 
+   * @param offset
+   *  Offset in control scratchpad memory
+   * @param size
+   *  Number of bytes to read
+   * @return
+   *  Data read from control scratchpad memory
+   * 
+   * This API is valid only for run objects that are associated with an ELF.
+   * Use this API to read data from control scratchpad memory.
+   * This memory is created by XRT based on ELF used to create xrt::kernel.
+   * 
+   * This function throws if the read fails.
+   */
+  std::vector<char>
path: src/runtime_src/core/include/xrt/xrt_kernel.h
user: login: stsoe
body: Should we make the type `uint8_t` ?

I am not excited about std::vector as return type.   Reason is that doesn't handle a case where application controls the data storage, say a user pointer.  Using a std::vector could lead to unnecessary memcpy.

Thinking out loud here, I wonder if the exchange should be through an xrt::bo?  Obviously not a BO that is created by driver, but a type of BO that holds just system memory, either allocated when the BO is created or as a userptr passed by application?
created_at: 2025-04-01T15:37:35Z
author_association: COLLABORATOR
diff_hunk: @@ -614,6 +614,43 @@ class run
     start(count);
   }
 
+  /**
+   * read_ctrl_scratchpad() - Read control scratchpad memory
+   * 
+   * @param offset
+   *  Offset in control scratchpad memory
+   * @param size
+   *  Number of bytes to read
+   * @return
+   *  Data read from control scratchpad memory
+   * 
+   * This API is valid only for run objects that are associated with an ELF.
+   * Use this API to read data from control scratchpad memory.
+   * This memory is created by XRT based on ELF used to create xrt::kernel.
+   * 
+   * This function throws if the read fails.
+   */
+  std::vector<char>
path: src/runtime_src/core/include/xrt/xrt_kernel.h
user: login: stsoe
body: I don't think a BO makes sense.  The data is not managed by a BO.  Once written the connection to the BO is lost, I think.  
created_at: 2025-04-01T15:52:35Z
author_association: COLLABORATOR
diff_hunk: @@ -614,6 +614,43 @@ class run
     start(count);
   }
 
+  /**
+   * read_ctrl_scratchpad() - Read control scratchpad memory
+   * 
+   * @param offset
+   *  Offset in control scratchpad memory
+   * @param size
+   *  Number of bytes to read
+   * @return
+   *  Data read from control scratchpad memory
+   * 
+   * This API is valid only for run objects that are associated with an ELF.
+   * Use this API to read data from control scratchpad memory.
+   * This memory is created by XRT based on ELF used to create xrt::kernel.
+   * 
+   * This function throws if the read fails.
+   */
+  std::vector<char>
path: src/runtime_src/core/include/xrt/xrt_kernel.h
user: login: stsoe
body: Another thought. Looking at your PR description comment:

> This memory is used by NPU to store control state information of a model. ELF is parsed to look for this section and a xrt::bo object is created corresponding to it.

There is indeed a xrt::bo created.  Could this bo be exchanged with the application directly.  It would save the memcpy I see in xrt_module.cpp:2318-2320.   One could return a sub-bo if necessary.   Or simply allow the application to manage the BO directly (get_scratchpad_bo()??) returns the scratch pad bo, now the application can read and write and sync as it wants.
created_at: 2025-04-01T16:04:19Z
author_association: COLLABORATOR
diff_hunk: @@ -614,6 +614,43 @@ class run
     start(count);
   }
 
+  /**
+   * read_ctrl_scratchpad() - Read control scratchpad memory
+   * 
+   * @param offset
+   *  Offset in control scratchpad memory
+   * @param size
+   *  Number of bytes to read
+   * @return
+   *  Data read from control scratchpad memory
+   * 
+   * This API is valid only for run objects that are associated with an ELF.
+   * Use this API to read data from control scratchpad memory.
+   * This memory is created by XRT based on ELF used to create xrt::kernel.
+   * 
+   * This function throws if the read fails.
+   */
+  std::vector<char>
path: src/runtime_src/core/include/xrt/xrt_kernel.h
user: login: rbramand-xilinx
body: Yes Soren, I agree this approach is simple and let user manage bo however he wants. Thanks for the suggestion.
I will rewrite the code accordingly.
created_at: 2025-04-02T10:52:12Z
author_association: COLLABORATOR
diff_hunk: @@ -2391,6 +2419,15 @@ dump_dtrace_buffer(const xrt::module& module)
   module_sram->dump_dtrace_buffer();
 }
 
+xrt::bo
+get_ctrl_scratchpad_bo()
+{
+  auto module_sram = std::dynamic_pointer_cast<xrt::module_sram>(module.get_handle());
path: src/runtime_src/core/common/api/xrt_module.cpp
user: login: github-actions[bot]
body: warning: use of undeclared identifier 'module' [clang-diagnostic-error]
```cpp
  auto module_sram = std::dynamic_pointer_cast<xrt::module_sram>(module.get_handle());
                                                                 ^
```

created_at: 2025-04-02T12:01:09Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-01T11:51:07Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-02T12:08:41Z
body: clang-tidy review says "All clean, LGTM! :+1:"
author_association: CONTRIBUTOR