diff options
Diffstat (limited to 'python/openvino/runtime/coredla_device/mmd/system_console')
3 files changed, 401 insertions, 0 deletions
diff --git a/python/openvino/runtime/coredla_device/mmd/system_console/CMakeLists.txt b/python/openvino/runtime/coredla_device/mmd/system_console/CMakeLists.txt new file mode 100644 index 0000000..d8be216 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/system_console/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_library(system_console_mmd INTERFACE) diff --git a/python/openvino/runtime/coredla_device/mmd/system_console/mmd_wrapper.cpp b/python/openvino/runtime/coredla_device/mmd/system_console/mmd_wrapper.cpp new file mode 100644 index 0000000..64c6631 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/system_console/mmd_wrapper.cpp @@ -0,0 +1,320 @@ +// Copyright 2020-2024 Intel Corporation. +// +// This software and the related documents are Intel copyrighted materials, +// and your use of them is governed by the express license under which they +// were provided to you ("License"). Unless the License provides otherwise, +// you may not use, modify, copy, publish, distribute, disclose or transmit +// this software or the related documents without Intel's prior written +// permission. +// +// This software and the related documents are provided as is, with no express +// or implied warranties, other than those that are expressly stated in the +// License. + +#include "mmd_wrapper.h" +#include "dla_dma_constants.h" // DLA_DMA_CSR_OFFSET_*** + +#include <cassert> // assert +#include <cstddef> // size_t +#include <iostream> // std::cerr +#include <stdexcept> // std::runtime_error +#include <string> // std::string + +#include <boost/process.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/filesystem/fstream.hpp> +#include <boost/process/environment.hpp> +#include <string> +#include <iostream> +#include <string> +#include <cstdio> +#include <sstream> +#include <ostream> + +#define xstr(s) _str(s) +#define _str(s) #s + +// All board variants must obey the CoreDLA CSR spec, which says that all access must be +// - 32 bits in size +// - address must be 4 byte aligned +// - within the address range, CSR size is 2048 bytes +constexpr uint64_t DLA_CSR_ALIGNMENT = 4; +constexpr uint64_t DLA_CSR_SIZE = 2048; +namespace bp = boost::process; //we will assume this for all further examples + +constexpr auto max_size = std::numeric_limits<std::streamsize>::max(); + +static const boost::filesystem::path system_console_path("/home/pmclean/intelfpga_pro/23.4/qprogrammer/syscon/bin/system-console"); +static boost::filesystem::path temp_file_path; +static boost::filesystem::path tcl_file_path; +static boost::filesystem::path sof_file_path; +static uint32_t enable_pmon; +static bool preserve_temp_files; + +const uint32_t DLA_CSR_BASE_ADDRESS = 0x80000000; +const uint32_t DLA_DDR_BASE_ADDRESS = 0x0; + + +static bp::opstream in; +static bp::ipstream out; +static bp::child subprocess; + +static int capture_till_prompt(bp::ipstream& out, std::ostream& capture) +{ + std::array<char, 4096> line_buffer; + if (out.fail()) { + std::cout << "EOF" << std::endl; + return 1; + } + + do { + out.clear(); + out.getline(&line_buffer[0], (std::streamsize)line_buffer.size(), '%'); + capture.write(&line_buffer[0], out.gcount()); + // If out.getline fills the line buffer without encountering the delimiter + // then the failbit of out will be set, causing out.fail() to return true. + // bp::ipstream indirectly inherits std::ios_base::iostate, which defines failbit/badbit + } while (out.fail() && (static_cast<long unsigned int> (out.gcount()) == line_buffer.size()-1)); + + if (out.fail()) { + std::cout << "EOF" << std::endl; + return 1; + } + return 0; +} + +static int wait_for_prompt(bp::ipstream& out) +{ + return capture_till_prompt(out, std::cout); +} + +std::string remove_non_alphanumeric(const std::string& input) { + std::string result = input; + result.erase(std::remove_if(result.begin(), result.end(), [](unsigned char c) { + return !std::isalnum(c); + }), result.end()); + return result; +} + +static void send_command(bp::opstream& in, std::string command) +{ + in << command << "\n"; + in.flush(); +} + +static void write_to_csr(bp::opstream& in, bp::ipstream& out, uint32_t addr, uint32_t data) { + addr += DLA_CSR_BASE_ADDRESS; + send_command(in, "master_write_32 $::g_dla_csr_service " + str( boost::format("0x%|08x| 0x%|08x|") % addr % data)); + if (0 != wait_for_prompt(out)) + { + throw std::runtime_error("Unexpected EOF"); + } +} + +static uint32_t read_from_csr(bp::opstream& in, bp::ipstream& out, uint32_t addr) { + if (addr == DLA_DMA_CSR_OFFSET_INTERRUPT_MASK) + { + return 3; + } + if (addr == DLA_DMA_CSR_OFFSET_LICENSE_FLAG) + { + return 1; + } + addr += DLA_CSR_BASE_ADDRESS; + send_command(in, "master_read_32 $::g_dla_csr_service " + str( boost::format("0x%|08x|") % addr ) + " 1"); + std::basic_stringstream<char> s1; + std::string captured; + do { + if (0 != capture_till_prompt(out, s1)) + { + throw std::runtime_error("Unexpected EOF"); + } + captured = s1.str(); + } while (std::all_of(captured.begin(), captured.end(), [](unsigned char c){return (std::isspace(c) || std::iscntrl(c));})); + std::string trimmed = remove_non_alphanumeric(captured); + + uint32_t data = std::stoul(trimmed, nullptr, 16); + + return data; +} + +static void read_from_ddr(bp::opstream& in, bp::ipstream& out, uint64_t addr, uint64_t length, void* data) +{ + if (data == nullptr) + { + throw std::runtime_error("null data"); + } + boost::filesystem::path temp_file_name = boost::filesystem::unique_path(); + boost::filesystem::path temppath = temp_file_path / temp_file_name; + send_command(in, "master_read_to_file $::g_emif_ddr_service " + temppath.generic_string() + str( boost::format(" 0x%|08x| 0x%|08x|") % addr % length ) ); + if (0 != wait_for_prompt(out)) { + throw std::runtime_error("Unexpected EOF"); + } + boost::filesystem::ifstream ifs(temppath, std::ios::in | std::ios::binary); + ifs.read(static_cast<char *>(data), length); + ifs.close(); + + if (!preserve_temp_files) { + try { + boost::filesystem::remove(temppath); + } catch (const boost::filesystem::filesystem_error& ex) { + std::cerr << "Error removing file: " << ex.what() << std::endl; + } + } +} + +static void write_to_ddr(bp::opstream& in, bp::ipstream& out, uint64_t addr, uint64_t length, const void* data) +{ + boost::filesystem::path temp_file_name = boost::filesystem::unique_path(); + boost::filesystem::path temppath = temp_file_path / temp_file_name; + boost::filesystem::ofstream ofs(temppath, std::ios::out | std::ios::binary); + if (ofs.fail()) { + throw std::runtime_error("Failed to access the temporary file " + temppath.generic_string()); + } + ofs.write(static_cast<const char *>(data), length); + ofs.close(); + send_command(in, "master_write_from_file $::g_emif_ddr_service " + temppath.generic_string() + str( boost::format(" 0x%|08x|") % addr ) ); + if (0 != wait_for_prompt(out)) + { + throw std::runtime_error("Unexpected EOF"); + } + + if (!preserve_temp_files) { + try { + boost::filesystem::remove(temppath); + } catch (const boost::filesystem::filesystem_error& ex) { + std::cerr << "Error removing file: " << ex.what() << std::endl; + } + } +} + +MmdWrapper::MmdWrapper() { + // Check for the envrionment variable + auto env = boost::this_process::environment(); + tcl_file_path = env.find("DLA_SYSCON_SOURCE_FILE") != env.end() ? + boost::filesystem::path(env["DLA_SYSCON_SOURCE_FILE"].to_string()) : + boost::filesystem::path(xstr(DLA_SYSCON_SOURCE_ROOT)) / "system_console_script.tcl"; + if (!boost::filesystem::exists(tcl_file_path)) { + throw std::runtime_error("Cannot locate " + tcl_file_path.generic_string() + ". Please specify the path of the Tcl setup script by defining the environment variable DLA_SYSCON_SOURCE_FILE\n"); + } else { + std::cout <<"Using the Tcl setup script at "<<tcl_file_path.generic_string()<<std::endl; + } + + temp_file_path = env.find("DLA_TEMP_DIR") != env.end() ? + boost::filesystem::path(env["DLA_TEMP_DIR"].to_string()) : + boost::filesystem::current_path(); + if (!boost::filesystem::exists(temp_file_path)) { + throw std::runtime_error("The temporary file storage directory specified via the environment variable DLA_TEMP_DIR does not exist.\n"); + } else { + std::cout <<"Saving temporary files to "<<temp_file_path.generic_string()<<std::endl; + } + + sof_file_path = env.find("DLA_SOF_PATH") != env.end() ? + boost::filesystem::path(env["DLA_SOF_PATH"].to_string()): + boost::filesystem::current_path() / "top.sof"; + if (!boost::filesystem::exists(sof_file_path)) { + throw std::runtime_error("Cannot find the FPGA bitstream (.sof). Please specify its location via the environment variable DLA_SOF_PATH,"\ + " or copy it as top.sof to the current working directory.\n"); + } else { + std::cout <<"Using the FPGA bitstream at "<<sof_file_path.generic_string()<<" to configure the JTAG connection"<<std::endl; + } + + boost::filesystem::path system_console_path = bp::search_path("system-console"); + if (system_console_path.empty()) { + throw std::runtime_error("Cannot find system-console in system PATH!\n"); + + } + enable_pmon = env.find("DLA_ENABLE_PMON") != env.end() ? 1 : 0; + + preserve_temp_files = env.find("DLA_PRESERVE_TEMP_FILES") != env.end() ? true : false; + + subprocess = bp::child(system_console_path, "-cli", bp::std_out > out, bp::std_in < in); + if (wait_for_prompt(out)) + { + throw std::runtime_error("Could not find initial prompt"); + } + send_command(in, "set ::cl(sof) " + sof_file_path.generic_string()); + if (enable_pmon == 1) { + send_command(in, "set ::cl(enable_pmon) 1"); + } + send_command(in, "source " + tcl_file_path.generic_string()); + std::basic_stringstream<char> s1; + if (0 != capture_till_prompt(out, s1)) + { + throw std::runtime_error("Could not find prompt after source"); + } + std::string captured(s1.str()); + + // Reset the IP + write_to_csr(in, out, DLA_DMA_CSR_OFFSET_IP_RESET, 1); + // Constants of the design + maxInstances_ = 1; + ddrSizePerInstance_ = 0x80000000; + // Need to change the frequencies below when their counterparts in the Qsys system are modified + coreDlaClockFreq_ = 200; + ddrClockFreq_ = 200; + // Initialize the handle_ object to a dummy value. It is not relevant to this MMD + handle_ = 0; +} + +MmdWrapper::~MmdWrapper() { + send_command(in, "close_services"); + if (wait_for_prompt(out)) + { + std::cout << "Could not find prompt after attempting to close system console services\n"; + } + send_command(in, "exit"); + try { + subprocess.terminate(); + std::cout << "Successfully closed JTAG services.\n"; + } catch (const boost::process::process_error& e) { + std::cerr << "Failed to terminate the system-console process due to reason: " << e.what() << std::endl; + } +} + +void MmdWrapper::RegisterISR(interrupt_service_routine_signature func, void *data) const { + throw std::runtime_error("System Console plugin requires polling"); +} + +void MmdWrapper::WriteToCsr(int instance, uint32_t addr, uint32_t data) const { + write_to_csr(in, out, addr, data); +} + +uint32_t MmdWrapper::ReadFromCsr(int instance, uint32_t addr) const { + return read_from_csr(in, out, addr); +} + +void MmdWrapper::WriteToDDR(int instance, uint64_t addr, uint64_t length, const void *data) const { + write_to_ddr(in, out, addr, length, data); +} + +void MmdWrapper::ReadFromDDR(int instance, uint64_t addr, uint64_t length, void *data) const { + read_from_ddr(in, out, addr, length, data); +} + +#ifndef STREAM_CONTROLLER_ACCESS +// Stream controller access is not supported by the platform abstraction +bool MmdWrapper::bIsStreamControllerValid(int instance) const { return false; } + +// 32-bit handshake with each Stream Controller CSR +void MmdWrapper::WriteToStreamController(int instance, uint32_t addr, uint64_t length, const void *data) const { + assert(false); +} + +void MmdWrapper::ReadFromStreamController(int instance, uint32_t addr, uint64_t length, void *data) const { + assert(false); +} +#else +// If the mmd layer supports accesses to the Stream Controller +bool MmdWrapper::bIsStreamControllerValid(int instance) const { + return false; +} + +// 32-bit handshake with each Stream Controller CSR +void MmdWrapper::WriteToStreamController(int instance, uint32_t addr, uint64_t length, const void *data) const { +} + +void MmdWrapper::ReadFromStreamController(int instance, uint32_t addr, uint64_t length, void *data) const { +} +#endif diff --git a/python/openvino/runtime/coredla_device/mmd/system_console/system_console_script.tcl b/python/openvino/runtime/coredla_device/mmd/system_console/system_console_script.tcl new file mode 100644 index 0000000..9e0e386 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/system_console/system_console_script.tcl @@ -0,0 +1,79 @@ +# Author: linqiaol +# Purpose: Perform write-read tests on external memory and CoreDLA CSR to make sure the registers can be accessed from host. + +# Declare and initialize CL arguments +if {![info exists ::cl(sof)]} { + set ::cl(sof) "top.sof" +} + +if {![info exists ::cl(enable_pmon)]} { + set ::cl(enable_pmon) 0 +} + +# Declare global variables +set ::g_emif_calip_service "" +set ::g_emif_ddr_service "" +set ::g_dla_csr_service "" +set ::g_pmon_service "" + +# Declare some contants +set ::g_const_master_offset_emif 0x0 +set ::g_const_master_range_emif 0x080000000 +set ::g_const_master_offset_dla 0x080000000 +set ::g_const_master_range_dla 0x000001000 + +#{{{ load_sof +proc load_sof {} { + puts "loading sof: $::cl(sof)" + design_load $::cl(sof) +} +#}}} + +#{{{claim_emif_ddr_service +proc claim_emif_ddr_service {} { + set all_master_paths [get_service_paths master] + set path [lindex $all_master_paths [lsearch -glob $all_master_paths *jtag*master*]] + set service [claim_service master $path {} "\{${::g_const_master_offset_emif} ${::g_const_master_range_emif} EXCLUSIVE\}"] + return $service +} +#}}} + +#{{{claim_dla_csr_service +proc claim_dla_csr_service {} { + set all_master_paths [get_service_paths master] + set path [lindex $all_master_paths [lsearch -glob $all_master_paths *jtag*master*]] + set service [claim_service master $path {} "\{${::g_const_master_offset_dla} ${::g_const_master_range_dla} EXCLUSIVE\}"] + return $service +} +#}}} + +#{{{claim_pmon_service +proc claim_pmon_service {} { + set all_master_paths [get_service_paths master] + set path [lindex $all_master_paths [lsearch -glob $all_master_paths *pmon*master*]] + set service [claim_service master $path {} {{0x0 0x00001000 EXCLUSIVE}}] + return $service +} +#}}} + +proc initialization {} { + load_sof + puts "Claim required services" + set ::g_dla_csr_service [claim_dla_csr_service] + set ::g_emif_ddr_service [claim_emif_ddr_service] + if {$::cl(enable_pmon) == 1} { + puts "Claiming JTAG service to the AXI4 performance monitor" + set ::g_pmon_service [claim_pmon_service] + } +} + +proc close_services {} { + close_service master $::g_dla_csr_service + if {$::cl(enable_pmon) == 1} { + close_service master $::g_pmon_service + } + close_service master $::g_emif_ddr_service + puts "Closed DLA JTAG services" +} + +initialization
\ No newline at end of file |
