aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristofer Jonsson <kristofer.jonsson@arm.com>2021-10-14 10:09:08 +0200
committerKristofer Jonsson <kristofer.jonsson@arm.com>2021-10-28 16:20:07 +0200
commit6d80c42364d58f9aac6e97b8bf057dfb44b2921b (patch)
tree19b15087eca2c790f32a6ab167a76e9e11e686e5
parente446b42e711e56974d07f242e18129c335966604 (diff)
downloadethos-u-linux-driver-stack-6d80c42364d58f9aac6e97b8bf057dfb44b2921b.tar.gz
Add utility to print firmware log
By default the logd will try to fetch the address of the print buffer from the device tree entry, assuming there is a device tree entry named 'print_queue'. Change-Id: Ic4750fe793f450152ba537820adc794731aaacaf
-rw-r--r--utils/CMakeLists.txt3
-rw-r--r--utils/ethosu_logd/CMakeLists.txt23
-rw-r--r--utils/ethosu_logd/dev_mem.cpp133
-rw-r--r--utils/ethosu_logd/dev_mem.hpp101
-rw-r--r--utils/ethosu_logd/main.cpp290
5 files changed, 549 insertions, 1 deletions
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index cd50866..6581048 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2020 Arm Limited. All rights reserved.
+# Copyright (c) 2020-2021 Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
@@ -16,4 +16,5 @@
# limitations under the License.
#
+add_subdirectory(ethosu_logd)
add_subdirectory(inference_runner)
diff --git a/utils/ethosu_logd/CMakeLists.txt b/utils/ethosu_logd/CMakeLists.txt
new file mode 100644
index 0000000..552d6ae
--- /dev/null
+++ b/utils/ethosu_logd/CMakeLists.txt
@@ -0,0 +1,23 @@
+#
+# Copyright (c) 2021 Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed 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
+#
+# 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.
+#
+
+# Build executable
+add_executable(ethosu_logd main.cpp dev_mem.cpp)
+
+# Install target
+install(TARGETS ethosu_logd DESTINATION "bin")
diff --git a/utils/ethosu_logd/dev_mem.cpp b/utils/ethosu_logd/dev_mem.cpp
new file mode 100644
index 0000000..053cdd3
--- /dev/null
+++ b/utils/ethosu_logd/dev_mem.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2021 Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed 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
+ *
+ * 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 "dev_mem.hpp"
+
+#include <cstddef>
+#include <iostream>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace EthosU {
+namespace DevMem {
+
+/****************************************************************************
+ * Exception
+ ****************************************************************************/
+
+Exception::Exception(const char *msg) : msg(msg) {}
+
+Exception::Exception(const std::string &msg) : msg(msg) {}
+
+Exception::~Exception() throw() {}
+
+const char *Exception::what() const throw() {
+ return msg.c_str();
+}
+
+/****************************************************************************
+ * DevMem
+ ****************************************************************************/
+
+DevMem::DevMem(uintptr_t address, size_t size) :
+ base(nullptr), pageMask(sysconf(_SC_PAGESIZE) - 1), pageOffset(address & pageMask), size(size) {
+ int fd = ::open("/dev/mem", O_RDWR | O_SYNC);
+ if (fd < 0) {
+ throw Exception("Failed to open device");
+ }
+
+ base = reinterpret_cast<char *>(
+ ::mmap(nullptr, pageOffset + size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, address & ~pageMask));
+ if (base == MAP_FAILED) {
+ throw Exception("MMap failed");
+ }
+
+ ::close(fd);
+}
+
+DevMem::~DevMem() {
+ ::munmap(base, pageOffset + size);
+}
+
+void DevMem::read(char *dst, size_t length, size_t offset) {
+ if (offset + length > size) {
+ throw Exception("Read failed");
+ }
+
+ // TODO Why do not std::copy() or memcpy work?
+ for (size_t i = 0; i < length; i++) {
+ dst[i] = base[pageOffset + offset + i];
+ }
+}
+
+void DevMem::write(char *src, size_t length, size_t offset) {
+ if (offset + length > size) {
+ throw Exception("Write failed");
+ }
+
+ // TODO Why do not std::copy() or memcpy work?
+ for (size_t i = 0; i < length; i++) {
+ base[pageOffset + offset + i] = src[i];
+ }
+}
+
+/****************************************************************************
+ * Log
+ ****************************************************************************/
+
+Log::Log(uintptr_t address, size_t size) : DevMem(address, size) {}
+
+void Log::clear() {
+ LogHeader header;
+ read(header, 0);
+
+ uint32_t rpos = header.write;
+ write(rpos, offsetof(LogHeader, read));
+}
+
+void Log::print() {
+ LogHeader header;
+ read(header, 0);
+
+ if (header.size < LOG_SIZE_MIN || header.size > LOG_SIZE_MAX) {
+ std::string msg = "Incorrect ring buffer values. size=" + std::to_string(header.size) +
+ ", read=" + std::to_string(header.read) + ", write=" + std::to_string(header.write);
+ throw Exception(msg.c_str());
+ }
+
+ size_t rpos = header.read;
+
+ // Skip forward if read is more than 'size' behind
+ if (rpos + header.size < header.write) {
+ rpos = header.write - header.size;
+ }
+
+ while (rpos < header.write) {
+ char c;
+ size_t offset = rpos++ % header.size + sizeof(header);
+ read(c, offset);
+ std::cout << c;
+ }
+}
+
+} // namespace DevMem
+} // namespace EthosU
diff --git a/utils/ethosu_logd/dev_mem.hpp b/utils/ethosu_logd/dev_mem.hpp
new file mode 100644
index 0000000..9d3f0a8
--- /dev/null
+++ b/utils/ethosu_logd/dev_mem.hpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2021 Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed 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
+ *
+ * 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.
+ */
+
+#ifndef DEV_MEM_HPP
+#define DEV_MEM_HPP
+
+#include <exception>
+#include <string>
+
+namespace EthosU {
+namespace DevMem {
+
+/****************************************************************************
+ * Exception
+ ****************************************************************************/
+
+class Exception : public std::exception {
+public:
+ Exception(const char *msg);
+ Exception(const std::string &msg);
+ virtual ~Exception() throw();
+ virtual const char *what() const throw();
+
+private:
+ std::string msg;
+};
+
+/****************************************************************************
+ * DevMem
+ ****************************************************************************/
+
+class DevMem {
+public:
+ DevMem(uintptr_t address, size_t size);
+ virtual ~DevMem();
+
+ void read(char *dst, size_t length, size_t offset);
+
+ template <typename T>
+ void read(T &dst, size_t offset) {
+ read(reinterpret_cast<char *>(&dst), sizeof(dst), offset);
+ }
+
+ void write(char *src, size_t length, size_t offset);
+
+ template <typename T>
+ void write(T &src, size_t offset) {
+ write(reinterpret_cast<char *>(&src), sizeof(src), offset);
+ }
+
+private:
+ char *base;
+ const uintptr_t pageMask;
+ const size_t pageOffset;
+ const size_t size;
+};
+
+/****************************************************************************
+ * Log
+ ****************************************************************************/
+
+class Log : public DevMem {
+public:
+ Log(uintptr_t address, size_t size = LOG_SIZE_MAX);
+
+ void clear();
+ void print();
+
+private:
+ struct LogHeader {
+ uint32_t size;
+ uint32_t read;
+ uint32_t pad[6];
+ uint32_t write;
+ };
+
+ static const size_t LOG_SIZE_MIN = 1024;
+ static const size_t LOG_SIZE_MAX = 1024 * 1024;
+
+ static uintptr_t getAddress();
+};
+
+} // namespace DevMem
+} // namespace EthosU
+
+#endif /* DEV_MEM_HPP */
diff --git a/utils/ethosu_logd/main.cpp b/utils/ethosu_logd/main.cpp
new file mode 100644
index 0000000..d7ff1eb
--- /dev/null
+++ b/utils/ethosu_logd/main.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2021 Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed 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
+ *
+ * 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 "dev_mem.hpp"
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+
+namespace {
+
+void help(const char *prog) {
+ std::cerr << "USAGE: " << prog << " [-h] [--address ADDRESS] [-c] [-C]" << std::endl << std::endl;
+ std::cerr << "positional argument:" << std::endl;
+ std::cerr << " -h, --help Show help message and exit" << std::endl;
+ std::cerr << " --address ADDRESS Address of ring buffer" << std::endl;
+ std::cerr << " -C Clear the ring buffer" << std::endl;
+ std::cerr << " -c Read and clear the ring buffer" << std::endl;
+}
+
+class Path {
+public:
+ Path(const std::string path) : path(path) {}
+
+ Path(const char *path) : path(path) {}
+
+ std::string string() const {
+ return path;
+ }
+
+ const char *c_str() const {
+ return path.c_str();
+ }
+
+ Path operator/(const std::string &other) const {
+ if (other.substr(0, 1) == "/") {
+ return Path(other);
+ } else {
+ return Path(path + "/" + other);
+ }
+ }
+
+ bool exists() const {
+ std::ifstream f = std::ifstream(path);
+ return !!f;
+ }
+
+ std::vector<Path> find(const std::string &name) const {
+ std::vector<Path> files;
+ find(*this, name, files);
+ return files;
+ }
+
+ Path parent() const {
+ return Path(path.substr(0, path.rfind("/")));
+ }
+
+private:
+ const std::string path;
+
+ static void find(const Path &path, const std::string &name, std::vector<Path> &files) {
+ DIR *dir = opendir(path.c_str());
+ if (dir == nullptr) {
+ throw EthosU::DevMem::Exception(std::string("Failed to open ") + path.string());
+ }
+
+ const dirent *dentry;
+ while ((dentry = readdir(dir)) != nullptr) {
+ const std::string dname = dentry->d_name;
+ const Path pathname = path / dname;
+
+ // Add path to list if it matches 'name'
+ if (dname == name) {
+ files.push_back(pathname);
+ }
+
+ // Call 'find' recursively for directories
+ if (dname != "." && dname != ".." && dentry->d_type == DT_DIR) {
+ find(pathname, name, files);
+ }
+ }
+
+ closedir(dir);
+ }
+};
+
+bool grep(const Path &path, const std::string &match) {
+ std::ifstream ifs(path.c_str(), std::ios_base::binary);
+ std::string content = std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
+ return content.find(match) != std::string::npos;
+}
+
+class DTS {
+public:
+ DTS(const Path &path) : path(path) {}
+
+ void getRegByName(const std::string &name, uintptr_t &address, size_t &size) const {
+ size_t index = 0;
+ for (const auto &n : getString("reg-names")) {
+ if (n == name) {
+ getRegByIndex(index, address, size);
+ return;
+ }
+
+ index++;
+ }
+
+ throw EthosU::DevMem::Exception(std::string("Failed to find 'reg-name' ") + name);
+ }
+
+ void getRegByIndex(const size_t index, uintptr_t &address, size_t &size) const {
+ size_t addressCell = getAddressCells();
+ size_t sizeCell = getSizeCells();
+ size_t offset = index * (addressCell + sizeCell) * 4;
+
+ address = getInt("reg", offset, addressCell * 4);
+ size = getInt("reg", offset + addressCell * 4, sizeCell * 4);
+ }
+
+private:
+ const Path path;
+
+ size_t getAddressCells() const {
+ const Path p = path / "#address-cells";
+ if (!p.exists()) {
+ return 2;
+ }
+
+ return getInt(p.string(), 0, 4);
+ }
+
+ size_t getSizeCells() const {
+ const Path p = path / "#size-cells";
+ if (!p.exists()) {
+ return 2;
+ }
+
+ return getInt(p.string(), 0, 4);
+ }
+
+ std::vector<char> getProperty(const std::string &name) const {
+ const Path propertyPath = path / name;
+ std::ifstream ifs(propertyPath.c_str(), std::ios_base::binary);
+ std::vector<char> content =
+ std::vector<char>(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
+ return content;
+ }
+
+ std::vector<std::string> getString(const std::string &name) const {
+ std::vector<char> property = getProperty(name);
+ std::vector<std::string> names;
+
+ for (std::vector<char>::iterator end, it = property.begin();
+ (end = std::find(it, property.end(), '\0')) != property.end();
+ it = end + 1) {
+ names.push_back(std::string(it, end));
+ }
+
+ return names;
+ }
+
+ uint64_t getInt(const std::string &name, const size_t offset, const size_t size) const {
+ const std::vector<char> property = getProperty(name);
+
+ switch (size) {
+ case 1:
+ return toCpu<uint8_t>(&property[offset]);
+ case 2:
+ return toCpu<uint16_t>(&property[offset]);
+ case 4:
+ return toCpu<uint32_t>(&property[offset]);
+ case 8:
+ return toCpu<uint64_t>(&property[offset]);
+ default:
+ throw EthosU::DevMem::Exception("Illegal integer size");
+ }
+ }
+
+ static constexpr bool isBigEndian() {
+ int d = 0x12345678;
+ return (d & 0xf) == 1;
+ }
+
+ template <typename T>
+ static T toCpu(const char *src) {
+ T dst;
+ char *d = reinterpret_cast<char *>(&dst);
+
+ if (isBigEndian()) {
+ for (size_t i = 0; i < sizeof(T); i++) {
+ d[i] = src[i];
+ }
+ } else {
+ for (size_t i = 0; i < sizeof(T); i++) {
+ d[i] = src[sizeof(T) - 1 - i];
+ }
+ }
+
+ return dst;
+ }
+};
+
+void getAddressSizeFromDtb(uintptr_t &address, size_t &size) {
+ // This is the file system path to the device tree
+ const Path devtree = "/sys/firmware/devicetree/base";
+
+ // Search device tree for <path>/**/compatible
+ for (const auto &path : devtree.find("compatible")) {
+ // Grep for "ethosu" in 'compatible'
+ if (grep(path, "arm,ethosu")) {
+ DTS dts(path.parent());
+
+ dts.getRegByName("print_queue", address, size);
+ return;
+ }
+ }
+
+ throw EthosU::DevMem::Exception("Could not find Ethos-U device tree entry with reg-name 'print_queue'");
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ try {
+ uintptr_t address = 0;
+ size_t size = 0;
+ bool clearBefore = false;
+ bool clearAfter = false;
+
+ for (int i = 1; i < argc; i++) {
+ std::string arg = argv[i];
+
+ if (arg == "--address") {
+ address = std::stol(argv[++i], nullptr, 0);
+ } else if (arg == "-c") {
+ clearAfter = true;
+ } else if (arg == "-C") {
+ clearBefore = true;
+ } else if (arg == "-h" || arg == "--help") {
+ help(argv[0]);
+ ::exit(0);
+ } else {
+ std::cerr << "Illegal argument '" + arg + "'" << std::endl;
+ help(argv[0]);
+ ::exit(1);
+ }
+ }
+
+ if (address == 0) {
+ getAddressSizeFromDtb(address, size);
+ }
+
+ EthosU::DevMem::Log log(address, size);
+
+ if (clearBefore) {
+ log.clear();
+ }
+
+ log.print();
+
+ if (clearAfter) {
+ log.clear();
+ }
+ } catch (std::exception &e) {
+ printf("Error: %s\n", e.what());
+ return 1;
+ }
+
+ return 0;
+}