From 6f0f1193e549245f3b4ac77c8875a40652286d1e Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Thu, 26 Jan 2017 15:26:10 +0100 Subject: regtools: add new tool list/find/describe registers Change-Id: I2d93d24bd421e1a2ea6d27b8f7cfd17311e6d458 --- utils/regtools/regtool.cpp | 630 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 utils/regtools/regtool.cpp (limited to 'utils/regtools/regtool.cpp') diff --git a/utils/regtools/regtool.cpp b/utils/regtools/regtool.cpp new file mode 100644 index 0000000000..2f7f04b956 --- /dev/null +++ b/utils/regtools/regtool.cpp @@ -0,0 +1,630 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "soc_desc.hpp" +#include "soc_desc_v1.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace soc_desc; +int g_verbose = 0; +bool g_inline = false; +bool g_print_zero = false; +bool g_regex_mode = false; +std::regex_constants::syntax_option_type g_regex_flags; + +std::regex_constants::syntax_option_type parse_regex_mode(const std::string& mode) +{ + std::regex_constants::syntax_option_type flags; + size_t index = 0; + while(index < mode.size()) + { + size_t end = mode.find(',', index); + if(end == std::string::npos) + end = mode.size(); + std::string opt = mode.substr(index, end - index); + if(opt == "icase") + flags |= std::regex_constants::icase; + else if(opt == "ECMAScript") + flags |= std::regex_constants::ECMAScript; + else if(opt == "basic") + flags |= std::regex_constants::basic; + else if(opt == "extended") + flags |= std::regex_constants::extended; + else if(opt == "awk") + flags |= std::regex_constants::awk; + else if(opt == "grep") + flags |= std::regex_constants::grep; + else if(opt == "egrep") + flags |= std::regex_constants::egrep; + else + { + fprintf(stderr, "Invalid regex option '%s'\n", opt.c_str()); + exit(1); + } + index = end + 1; + } + return flags; +} + +void print_path(node_ref_t node, bool nl = true) +{ + printf("%s", node.soc().get()->name.c_str()); + std::vector< std::string > path = node.path(); + for(size_t i = 0; i < path.size(); i++) + printf(".%s", path[i].c_str()); + if(nl) + printf("\n"); +} + +template +std::string to_str(const T& t) +{ + std::ostringstream oss; + oss << t; + return oss.str(); +} + +std::string get_path(node_inst_t inst) +{ + if(!inst.is_root()) + { + std::string path = get_path(inst.parent()) + "." + inst.name(); + if(inst.is_indexed()) + path += "[" + to_str(inst.index()) + "]"; + return path; + } + else + return inst.soc().get()->name; +} + +void print_inst(node_inst_t inst, bool addr = true, bool nl = true) +{ + if(!inst.is_root()) + { + print_inst(inst.parent(), false); + printf(".%s", inst.name().c_str()); + if(inst.is_indexed()) + printf("[%u]", (unsigned)inst.index()); + } + else + printf("%s", inst.soc().get()->name.c_str()); + if(addr) + { + printf(" @ %#x", inst.addr()); + if(nl) + printf("\n"); + } +} + +void find_insts(std::vector< node_inst_t >& list, node_inst_t inst, soc_addr_t addr) +{ + if(inst.addr() == addr) + { + /* only keep matches that are registers */ + if(inst.node().reg().valid()) + list.push_back(inst); + } + std::vector< node_inst_t > children = inst.children(); + for(size_t i = 0; i < children.size(); i++) + find_insts(list, children[i], addr); +} + +void find_insts(std::vector< node_inst_t >& insts, + std::vector< soc_t >& soc_list, soc_addr_t addr) +{ + for(size_t i = 0; i < soc_list.size(); i++) + find_insts(insts, soc_ref_t(&soc_list[i]).root_inst(), addr); +} + +const size_t NO_INDEX = (size_t)-1; +/* index is set to NO_INDEX if there is no index */ +bool parse_name(std::string& name, std::string& component, size_t& index) +{ + size_t i = 0; + /* name must be of the form [a-zA-Z0-9_]+ */ + while(i < name.size() && (isalnum(name[i]) || name[i] == '_')) + i++; + if(i == 0) + return false; + component = name.substr(0, i); + /* must stop at the end, or on '.' or on '[' */ + if(name.size() == i) + { + index = NO_INDEX; + name = name.substr(i); + return true; + } + else if(name[i] == '.') + { + index = NO_INDEX; + name = name.substr(i + 1); + return true; + } + else if(name[i] == '[') + { + /* parse index */ + char *end; + index = strtoul(name.c_str() + i + 1, &end, 0); + /* must stop on ']'. Also strtoul is happy with an empty string, check for this */ + if(*end != ']' || end == name.c_str() + i + 1) + return false; + i = end + 1 - name.c_str(); + /* check if we have a '.' after that, or the end */ + if(i < name.size() && name[i] != '.') + return false; + name = name.substr(i + 1); + return true; + } + else + return false; +} + +int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::regex& regex) +{ + /* only keep matches that are registers */ + if(inst.node().reg().valid()) + { + std::string path = get_path(inst); + if(regex_match(path, regex)) + matches.push_back(inst); + } + std::vector< node_inst_t > children = inst.children(); + for(size_t i = 0; i < children.size(); i++) + find_insts(matches, children[i], regex); + return 0; +} + +int find_insts_regex(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list, std::string str) +{ + auto flags = g_regex_flags | std::regex_constants::optimize; /* we will match a lot */ + /* any error during construction or mayching will throw exception, and print + * an error message so don't catch them */ + std::regex regex(str, flags); + for(size_t i = 0; i < soc_list.size(); i++) + if(find_insts(matches, soc_ref_t(&soc_list[i]).root_inst(), regex) != 0) + return 1; + return 0; +} + +int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::string name) +{ + if(name.empty()) + { + if(inst.node().reg().valid()) + matches.push_back(inst); + return 0; + } + std::string component; + size_t index; + bool ok = parse_name(name, component, index); + if(!ok) + { + fprintf(stderr, "invalid name '%s'\n", name.c_str()); + return 1; + } + if(index == NO_INDEX) + inst = inst.child(component); + else + inst = inst.child(component, index); + if(inst.valid()) + return find_insts(matches, inst, name); + else + return 0; +} + +int find_insts(std::vector< node_inst_t >& matches, soc_t& soc, std::string name) +{ + return find_insts(matches, soc_ref_t(&soc).root_inst(), name); +} + +int find_insts(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list, + std::string name) +{ + /* regex mode is special */ + if(g_regex_mode) + return find_insts_regex(matches, soc_list, name); + /* if name is an integer, parse it */ + char *end; + unsigned long addr = strtoul(name.c_str(), &end, 0); + if(*end == 0) + { + find_insts(matches, soc_list, addr); + return 0; + } + /* else assume it's a name */ + std::string name_copy = name; + std::string component; + size_t index; + bool ok = parse_name(name_copy, component, index); + if(!ok) + { + fprintf(stderr, "invalid name '%s'\n", name.c_str()); + return 1; + } + /* a soc cannot be indexed */ + for(size_t i = 0; i < soc_list.size(); i++) + { + int ret; + if(index == NO_INDEX && soc_list[i].name == component) + ret = find_insts(matches, soc_list[i], name_copy); + else + ret = find_insts(matches, soc_list[i], name); + if(ret != 0) + return ret; + } + return 0; +} + +void do_describe(node_inst_t inst, soc_word_t *decode_val) +{ + if(decode_val) + printf(" = %#lx", (unsigned long)*decode_val); + if(g_inline) + printf(" {"); + else + printf("\n"); + std::vector< field_ref_t > fields = inst.node().reg().fields(); + bool first = true; + soc_word_t mask = 0; /* mask of all decoded bits */ + /* special index fields.size() means "undecoded bits" */ + for(size_t i = 0; i <= fields.size(); i++) + { + unsigned long val = 0; + bool dont_print = false; + field_t f; + if(i == fields.size()) + { + /* create fake field */ + f.name = "undecoded bits"; + f.pos = 0; + f.width = inst.node().reg().get()->width; + val = decode_val ? *decode_val & ~mask : 0; + /* only print if decoding and something was left */ + dont_print = (val == 0); + } + else + { + f = *fields[i].get(); + val = decode_val ? f.extract(*decode_val) : 0; + /* don't print zero fields unless asked */ + dont_print = decode_val && !g_print_zero && val == 0; + } + if(dont_print) + continue; + if(g_inline) + printf("%s", first ? " " : ", "); + else + printf(" "); + printf("%s", f.name.c_str()); + first = false; + if(f.width == 1) + printf("[%zu]", f.pos); + else + printf("[%zu:%zu]", f.pos + f.width - 1, f.pos); + /* decode if needed */ + if(decode_val) + { + /* track what we decoded */ + mask |= ~f.bitmask(); + printf(" = %#lx", val); + } + /* newline if need */ + if(!g_inline) + printf("\n"); + } + if(g_inline) + printf(" }\n"); +} + +int do_find(std::vector< soc_t >& soc_list, int argc, char **argv, bool describe, + soc_word_t *decode_val = nullptr) +{ + if(argc != 1) + { + fprintf(stderr, "action 'find' takes on argument: the name or address of a register\n"); + return 1; + } + std::vector< node_inst_t > matches; + int ret = find_insts(matches, soc_list, argv[0]); + if(ret != 0) + return 0; + /* print matches */ + if(matches.size() > 0) + { + for(size_t i = 0; i < matches.size(); i++) + { + print_inst(matches[i], true, !describe); + if(describe) + do_describe(matches[i], decode_val); + } + return 0; + } + else + { + fprintf(stderr, "No matches\n"); + return 1; + } + return 0; +} + +int do_decode(std::vector< soc_t >& soc_list, int argc, char **argv) +{ + if(argc != 2) + { + fprintf(stderr, "action 'decode' takes two arguments: the register and the value\n"); + return 1; + } + char *end; + soc_word_t val = strtoul(argv[1], &end, 0); + if(*end) + { + fprintf(stderr, "invalid value '%s'\n", argv[1]); + return 1; + } + return do_find(soc_list, argc - 1, argv, true, &val); +} + +void print_context(const error_context_t& ctx) +{ + for(size_t j = 0; j < ctx.count(); j++) + { + err_t e = ctx.get(j); + switch(e.level()) + { + case err_t::INFO: printf("[INFO]"); break; + case err_t::WARNING: printf("[WARN]"); break; + case err_t::FATAL: printf("[FATAL]"); break; + default: printf("[UNK]"); break; + } + if(e.location().size() != 0) + printf(" %s:", e.location().c_str()); + printf(" %s\n", e.message().c_str()); + } +} + +int usage() +{ + printf("usage: regtool [options] [args]\n"); + printf("options:\n"); + printf(" -h Display this help\n"); + printf(" -f Load a register file\n"); + printf(" -d Specify a directory where to look for register files\n"); + printf(" -v Increase verbosity level\n"); + printf(" -s Limit search to a one or more socs (comma separated)\n"); + printf(" -i Describe/decode in one line\n"); + printf(" -z Print fields even when the value is zero\n"); + printf(" -r Enable regex mode\n"); + printf("\n"); + printf("actions:\n"); + printf(" find Find all registers that match this address\n"); + printf(" find Find the registers that match this name\n"); + printf(" describe Describe a register (either found by address or name)\n"); + printf(" decode Decode a register value\n"); + printf("By default, regtool will look for register files in desc/, but if\n"); + printf("any file or directory is specified, regtool will ignore the default directory\n"); + printf("Adresses can be in decimal or hexadecimal (using 0x prefix).\n"); + printf("Names can be fully qualified with a soc name or left ambiguous.\n"); + printf(" ::= . | \n"); + printf(" ::= (.)*\n"); + printf(" ::= | []\n"); + printf("In regex mode, all commands expect a regular expression that is matched\n"); + printf("against the full path (.) of every register. The regex must\n"); + printf("follow the C++11 standard and accepts the following, comma-separated options:\n"); + printf(" icase Case insensitive match\n"); + printf(" ECMAScript Use ECMAScript grammar\n"); + printf(" basic Use basic POSIX grammar\n"); + printf(" extended Use extended basic grammar\n"); + printf(" awk Use awk grammar\n"); + printf(" grep Use grep grammar\n"); + printf(" egrep Use egrep grammar\n"); + printf("Examples:\n"); + printf(" regtool -d desc/ -s stmp3700,imx233 find 0x8001c310\n"); + printf(" regtool find DIGCTL.CHIPID\n"); + printf(" regtool describe imx233.DIGCTL.CHIPID\n"); + printf(" regtool -f desc/regs-stmp3780.xml decode 0x8001c310 0x37800001\n"); + printf(" regtool -r awk find 'imx233\\.LCDIF\\..*'\n"); + return 1; +} + +std::string my_dirname(const std::string& filename) +{ + size_t idx = filename.find_last_of("/\\"); + if(idx == std::string::npos) + return filename; + else + return filename.substr(0, idx); +} + +/* warn controls whether we warn about error at lowest verbosity: we warn + * for files explicitely loaded but not the one listed in directories */ +bool load_file(std::vector< soc_t >& soc_list, const std::string& file, bool user_load) +{ + if(g_verbose >= 2) + fprintf(stderr, "Loading file '%s'...\n", file.c_str()); + error_context_t ctx; + soc_t s; + bool ret = parse_xml(file.c_str(), s, ctx); + if(g_verbose >= 1 || user_load) + { + if(ctx.count() != 0) + fprintf(stderr, "In file %s:\n", file.c_str()); + print_context(ctx); + } + if(!ret) + { + if(g_verbose >= 1 || user_load) + fprintf(stderr, "Cannot parse file '%s'\n", file.c_str()); + return !user_load; /* error only if user loaded */ + } + soc_list.push_back(s); + return true; +} + +bool load_dir(std::vector< soc_t >& soc_list, const std::string& dirname, bool user_load) +{ + if(g_verbose >= 2) + fprintf(stderr, "Loading directory '%s'...\n", dirname.c_str()); + DIR *dir = opendir(dirname.c_str()); + if(dir == NULL) + { + if(g_verbose >= 1 || user_load) + fprintf(stderr, "Warning: cannot open directory '%s'\n", dirname.c_str()); + return !user_load; /* error only if user loaded */ + } + struct dirent *ent; + while((ent = readdir(dir))) + { + std::string name(ent->d_name); + /* only list *.xml */ + if(name.size() < 4 || name.substr(name.size() - 4) != ".xml") + continue; + if(!load_file(soc_list, dirname + "/" + name, false)) + { + closedir(dir); + return false; + } + } + closedir(dir); + return true; +} + +void add_socs_to_list(std::set< std::string >& soc_list, const std::string& list) +{ + size_t idx = 0; + while(idx < list.size()) + { + size_t next = list.find(',', idx); + if(next == std::string::npos) + next = list.size(); + soc_list.insert(list.substr(idx, next - idx)); + idx = next + 1; + } +} + +int main(int argc, char **argv) +{ + std::vector< std::string > g_soc_dir; + std::vector< std::string > g_soc_files; + std::vector< soc_t > g_soc_list; + std::set< std::string> g_allowed_soc; + + if(argc <= 1) + return usage(); + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "hf:d:vs:izr:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'h': + return usage(); + case 's': + add_socs_to_list(g_allowed_soc, optarg); + break; + case 'd': + g_soc_dir.push_back(std::string(optarg)); + break; + case 'f': + g_soc_files.push_back(std::string(optarg)); + break; + case 'v': + g_verbose++; + break; + case 'i': + g_inline = true; + break; + case 'z': + g_print_zero = true; + break; + case 'r': + g_regex_mode = true; + g_regex_flags = parse_regex_mode(optarg); + break; + default: + abort(); + } + } + if(argc == optind) + { + fprintf(stderr, "You need at least one action\n"); + return 2; + } + /* if no file or directory, add default */ + if(g_soc_files.empty() && g_soc_dir.empty()) + load_dir(g_soc_list, my_dirname(argv[0]) + "/desc", false); + /* load directories */ + for(size_t i = 0; i < g_soc_dir.size(); i++) + load_dir(g_soc_list, g_soc_dir[i], true); + /* load files */ + for(size_t i = 0; i < g_soc_files.size(); i++) + load_file(g_soc_list, g_soc_files[i], true); + /* filter soc list */ + if(g_allowed_soc.size() > 0) + { + for(size_t i = 0; i < g_soc_list.size(); i++) + { + if(g_allowed_soc.find(g_soc_list[i].name) == g_allowed_soc.end()) + { + std::swap(g_soc_list[i], g_soc_list.back()); + g_soc_list.pop_back(); + i--; + } + } + } + /* print */ + if(g_verbose >= 1) + { + fprintf(stderr, "Available socs after filtering:"); + for(size_t i = 0; i < g_soc_list.size(); i++) + fprintf(stderr, " %s", g_soc_list[i].name.c_str()); + fprintf(stderr, "\n"); + } + + std::string action = argv[optind]; + argc -= optind + 1; + argv += optind + 1; + if(action == "find") + return do_find(g_soc_list, argc, argv, false); + if(action == "describe") + return do_find(g_soc_list, argc, argv, true); + if(action == "decode") + return do_decode(g_soc_list, argc, argv); + fprintf(stderr, "unknown action '%s'\n", action.c_str()); + return 1; +} -- cgit v1.2.3