From 1cada1f8339d6b5f8506277f80e62aaef77ab774 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sun, 14 Dec 2014 11:53:55 +0100 Subject: soc_desc: new version of the desc file format Fix qeditor to use the old soc_desc_v1. Port hwstub_shell to the new description format. Change-Id: I9fefbff534bfaa5c3603bb3dd8307a2b76e88cfc --- utils/hwstub/tools/Makefile | 5 +- utils/hwstub/tools/hwstub_shell.cpp | 374 ++++---- utils/regtools/Makefile | 5 +- utils/regtools/desc/XML.txt | 140 --- utils/regtools/desc/regs-example-v1.xml | 91 ++ utils/regtools/desc/regs-example.xml | 153 ++++ utils/regtools/desc/spec-1.0.txt | 140 +++ utils/regtools/desc/spec-2.0.txt | 373 ++++++++ utils/regtools/headergen.cpp | 749 --------------- utils/regtools/headergen_v1.cpp | 751 +++++++++++++++ utils/regtools/include/soc_desc.hpp | 384 ++++++++ utils/regtools/include/soc_desc_v1.hpp | 230 +++++ utils/regtools/lib/Makefile | 9 +- utils/regtools/lib/formula.cpp | 214 +++++ utils/regtools/lib/soc_desc.cpp | 1530 +++++++++++++++++-------------- utils/regtools/lib/soc_desc.hpp | 228 ----- utils/regtools/lib/soc_desc_v1.cpp | 990 ++++++++++++++++++++ utils/regtools/qeditor/backend.cpp | 4 +- utils/regtools/qeditor/backend.h | 6 +- utils/regtools/qeditor/mainwindow.cpp | 4 +- utils/regtools/qeditor/qeditor.pro | 4 +- utils/regtools/qeditor/regedit.cpp | 6 +- utils/regtools/qeditor/utils.cpp | 2 +- utils/regtools/swiss_knife.cpp | 612 +++++++++++++ utils/regtools/tester.cpp | 363 -------- utils/regtools/tester_v1.cpp | 365 ++++++++ 26 files changed, 5415 insertions(+), 2317 deletions(-) delete mode 100644 utils/regtools/desc/XML.txt create mode 100644 utils/regtools/desc/regs-example-v1.xml create mode 100644 utils/regtools/desc/regs-example.xml create mode 100644 utils/regtools/desc/spec-1.0.txt create mode 100644 utils/regtools/desc/spec-2.0.txt delete mode 100644 utils/regtools/headergen.cpp create mode 100644 utils/regtools/headergen_v1.cpp create mode 100644 utils/regtools/include/soc_desc.hpp create mode 100644 utils/regtools/include/soc_desc_v1.hpp create mode 100644 utils/regtools/lib/formula.cpp delete mode 100644 utils/regtools/lib/soc_desc.hpp create mode 100644 utils/regtools/lib/soc_desc_v1.cpp create mode 100644 utils/regtools/swiss_knife.cpp delete mode 100644 utils/regtools/tester.cpp create mode 100644 utils/regtools/tester_v1.cpp diff --git a/utils/hwstub/tools/Makefile b/utils/hwstub/tools/Makefile index f718300623..868ddcca79 100644 --- a/utils/hwstub/tools/Makefile +++ b/utils/hwstub/tools/Makefile @@ -2,9 +2,10 @@ CC=gcc CXX=g++ LD=g++ HWSTUB_LIB_DIR=../lib +REGTOOLS_INCLUDE_DIR=../../regtools/include REGTOOLS_LIB_DIR=../../regtools/lib -CFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` -CXXFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_LIB_DIR) `pkg-config --cflags lua5.2` +CFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -std=c99 -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_INCLUDE_DIR) `pkg-config --cflags lua5.2` +CXXFLAGS=-Wall -O2 `pkg-config --cflags libusb-1.0` -g -I$(HWSTUB_LIB_DIR) -I$(REGTOOLS_INCLUDE_DIR) `pkg-config --cflags lua5.2` LDFLAGS=`pkg-config --libs libusb-1.0` `pkg-config --libs lua5.2` -lreadline -L$(HWSTUB_LIB_DIR) -L$(REGTOOLS_LIB_DIR) -lsocdesc -lhwstub `xml2-config --libs` EXEC=hwstub_shell hwstub_load SRC=$(wildcard *.c) diff --git a/utils/hwstub/tools/hwstub_shell.cpp b/utils/hwstub/tools/hwstub_shell.cpp index f59ca8b82a..dd5c20b471 100644 --- a/utils/hwstub/tools/hwstub_shell.cpp +++ b/utils/hwstub/tools/hwstub_shell.cpp @@ -28,11 +28,14 @@ #include #include #include +#include "soc_desc_v1.hpp" #include "soc_desc.hpp" extern "C" { #include "prompt.h" } +using namespace soc_desc_v1; + #if LUA_VERSION_NUM < 502 #warning You need at least lua 5.2 #endif @@ -50,6 +53,44 @@ struct hwstub_stmp_desc_t g_hwdev_stmp; struct hwstub_pp_desc_t g_hwdev_pp; lua_State *g_lua; +/** + * debug + */ + +void print_context(const std::string& file, const soc_desc::error_context_t& ctx) +{ + for(size_t j = 0; j < ctx.count(); j++) + { + soc_desc::error_t e = ctx.get(j); + switch(e.level()) + { + case soc_desc::error_t::INFO: printf("[INFO]"); break; + case soc_desc::error_t::WARNING: printf("[WARN]"); break; + case soc_desc::error_t::FATAL: printf("[FATAL]"); break; + default: printf("[UNK]"); break; + } + if(e.location().size() != 0) + printf(" (%s) %s:", file.c_str(), e.location().c_str()); + printf(" %s\n", e.message().c_str()); + } +} + +void my_lua_print_stack(lua_State *state = 0, int up_to = 0) +{ + if(state == 0) + state = g_lua; + up_to = lua_gettop(state) - up_to; + printf("stack:"); + for(int i = -1; i >= -up_to; i--) + { + if(lua_isstring(state, i)) + printf(" <%s>", lua_tostring(state, i)); + else + printf(" [%s]", lua_typename(state, lua_type(state, i))); + } + printf("\n"); +} + /** * hw specific */ @@ -449,7 +490,7 @@ int my_lua_write_field(lua_State *state) soc_addr_t addr = lua_tounsigned(state, lua_upvalueindex(1)); soc_word_t shift = lua_tounsigned(state, lua_upvalueindex(2)); soc_word_t mask = lua_tounsigned(state, lua_upvalueindex(3)); - bool is_sct = lua_toboolean(state, lua_upvalueindex(5)); + char op = lua_tounsigned(state, lua_upvalueindex(5)); soc_word_t value = mask; if(n == 1) @@ -469,10 +510,17 @@ int my_lua_write_field(lua_State *state) value &= mask; } - if(!is_sct) - value = value << shift | (hw_read32(state, addr) & ~(mask << shift)); + soc_word_t old_value = hw_read32(state, addr); + if(op == 'w') + value = value << shift | (old_value & ~(mask << shift)); + else if(op == 's') + value = old_value | value << shift; + else if(op == 'c') + value = old_value & ~(value << shift); + else if(op == 't') + value = old_value ^ (value << shift); else - value <<= shift; + luaL_error(state, "write_field() internal error"); hw_write32(state, addr, value); return 0; @@ -501,222 +549,240 @@ int my_lua_sct_reg(lua_State *state) return 0; } -void my_lua_create_field(soc_addr_t addr, const soc_reg_field_t& field, bool sct) +/* lua stack on entry/exit: */ +void my_lua_create_field(soc_addr_t addr, soc_desc::field_ref_t field) { + soc_desc::field_t *f = field.get(); + /** create field table */ lua_newtable(g_lua); + /* lua stack: */ - lua_pushstring(g_lua, field.name.c_str()); + /** create various characteristics */ + lua_pushstring(g_lua, f->name.c_str()); + /* lua stack: ... */ lua_setfield(g_lua, -2, "name"); + /* lua stack: ... */ lua_pushunsigned(g_lua, addr); + /* lua stack: ... */ lua_setfield(g_lua, -2, "addr"); + /* lua stack: ... */ - lua_pushboolean(g_lua, sct); - lua_setfield(g_lua, -2, "sct"); - - lua_pushunsigned(g_lua, field.first_bit); - lua_setfield(g_lua, -2, "first_bit"); + lua_pushunsigned(g_lua, f->pos); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "pos"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, field.last_bit); - lua_setfield(g_lua, -2, "last_bit"); + lua_pushunsigned(g_lua, f->width); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "width"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, field.bitmask()); + lua_pushunsigned(g_lua, f->bitmask()); + /* lua stack: ... */ lua_setfield(g_lua, -2, "bitmask"); + /* lua stack: ... */ - soc_word_t local_bitmask = field.bitmask() >> field.first_bit; + soc_word_t local_bitmask = f->bitmask() >> f->pos; lua_pushunsigned(g_lua, local_bitmask); + /* lua stack: ... */ lua_setfield(g_lua, -2, "local_bitmask"); + /* lua stack: ... */ + /** create read routine */ lua_pushunsigned(g_lua, addr); - lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, f->pos); lua_pushunsigned(g_lua, local_bitmask); + /* lua stack: ... */ lua_pushcclosure(g_lua, my_lua_read_field, 3); + /* lua stack: ... */ lua_setfield(g_lua, -2, "read"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, addr); - lua_pushunsigned(g_lua, field.first_bit); - lua_pushunsigned(g_lua, local_bitmask); - lua_pushvalue(g_lua, -4); - lua_pushboolean(g_lua, false); - lua_pushcclosure(g_lua, my_lua_write_field, 5); - lua_setfield(g_lua, -2, "write"); - - if(sct) + /** create write/set/clr/tog routines */ + static const char *name[] = {"write", "set", "clr", "tog"}; + static const char arg[] = {'w', 's', 'c', 't'}; + for(int i = 0; i < 4; i++) { - lua_pushunsigned(g_lua, addr + 4); - lua_pushunsigned(g_lua, field.first_bit); + lua_pushunsigned(g_lua, addr); + lua_pushunsigned(g_lua, f->pos); lua_pushunsigned(g_lua, local_bitmask); + /* lua stack: ... */ lua_pushvalue(g_lua, -4); - lua_pushboolean(g_lua, true); + /* lua stack: ... */ + lua_pushunsigned(g_lua, arg[i]); + /* lua stack: <'wsct'> ... */ lua_pushcclosure(g_lua, my_lua_write_field, 5); - lua_setfield(g_lua, -2, "set"); - - lua_pushunsigned(g_lua, addr + 8); - lua_pushunsigned(g_lua, field.first_bit); - lua_pushunsigned(g_lua, local_bitmask); - lua_pushvalue(g_lua, -4); - lua_pushboolean(g_lua, true); - lua_pushcclosure(g_lua, my_lua_write_field, 5); - lua_setfield(g_lua, -2, "clr"); - - lua_pushunsigned(g_lua, addr + 12); - lua_pushunsigned(g_lua, field.first_bit); - lua_pushunsigned(g_lua, local_bitmask); - lua_pushvalue(g_lua, -4); - lua_pushboolean(g_lua, true); - lua_pushcclosure(g_lua, my_lua_write_field, 5); - lua_setfield(g_lua, -2, "tog"); + /* lua stack: ... */ + lua_setfield(g_lua, -2, name[i]); + /* lua stack: ... */ } - for(size_t i = 0; i < field.value.size(); i++) + /** create values */ + for(size_t i = 0; i < f->enum_.size(); i++) { - lua_pushunsigned(g_lua, field.value[i].value); - lua_setfield(g_lua, -2, field.value[i].name.c_str()); + lua_pushunsigned(g_lua, f->enum_[i].value); + /* lua stack: ... */ + lua_setfield(g_lua, -2, f->enum_[i].name.c_str()); + /* lua stack: ... */ } + + /** register field */ + lua_setfield(g_lua, -2, f->name.c_str()); + /* lua stack: */ } -void my_lua_create_reg(soc_addr_t addr, size_t index, const soc_reg_t& reg) +/* lua stack on entry/exit: */ +void my_lua_create_reg(soc_addr_t addr, soc_desc::register_ref_t reg) { - lua_newtable(g_lua); - - lua_pushstring(g_lua, reg.addr[index].name.c_str()); - lua_setfield(g_lua, -2, "name"); - - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); - lua_setfield(g_lua, -2, "addr"); - - lua_pushboolean(g_lua, !!(reg.flags & REG_HAS_SCT)); - lua_setfield(g_lua, -2, "sct"); - - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); + if(!reg.valid()) + return; + /** create read/write routine */ + lua_pushunsigned(g_lua, addr); + /* lua stack: */ lua_pushcclosure(g_lua, my_lua_read_reg, 1); + /* lua stack: */ lua_setfield(g_lua, -2, "read"); + /* lua stack: */ - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); + lua_pushunsigned(g_lua, addr); + /* lua stack: */ lua_pushcclosure(g_lua, my_lua_write_reg, 1); + /* lua stack: */ lua_setfield(g_lua, -2, "write"); + /* lua stack: */ - if(reg.flags & REG_HAS_SCT) + /** create set/clr/tog helpers */ + static const char *name[] = {"set", "clr", "tog"}; + static const char arg[] = {'s', 'c', 't'}; + for(int i = 0; i < 3; i++) { - lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 4); - lua_pushcclosure(g_lua, my_lua_write_reg, 1); - lua_setfield(g_lua, -2, "set"); + lua_pushunsigned(g_lua, addr); + /* lua stack: */ + lua_pushunsigned(g_lua, arg[i]); + /* lua stack: <'s'/'c'/'t'> */ + lua_pushcclosure(g_lua, my_lua_sct_reg, 2); + /* lua stack: */ + lua_setfield(g_lua, -2, name[i]); + /* lua stack: */ + } - lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 8); - lua_pushcclosure(g_lua, my_lua_write_reg, 1); - lua_setfield(g_lua, -2, "clr"); + /** create fields */ + std::vector< soc_desc::field_ref_t > fields = reg.fields(); + for(size_t i = 0; i < fields.size(); i++) + my_lua_create_field(addr, fields[i]); +} - lua_pushunsigned(g_lua, addr + reg.addr[index].addr + 12); - lua_pushcclosure(g_lua, my_lua_write_reg, 1); - lua_setfield(g_lua, -2, "tog"); - } - else +/* lua stack on entry/exit: */ +void my_lua_create_instances(const std::vector< soc_desc::node_inst_t >& inst) +{ + for(size_t i = 0; i < inst.size(); i++) { - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); - lua_pushunsigned(g_lua, 's'); - lua_pushcclosure(g_lua, my_lua_sct_reg, 2); - lua_setfield(g_lua, -2, "set"); + /** if the instance is indexed, find the instance table, otherwise create it */ + if(inst[i].is_indexed()) + { + /** try to get the instance table, otherwise create it */ + lua_getfield(g_lua, -1, inst[i].name().c_str()); + /* lua stack: */ + if(lua_isnil(g_lua, -1)) + { + lua_pop(g_lua, 1); + lua_newtable(g_lua); + /* lua stack: */ + lua_pushvalue(g_lua, -1); + /* lua stack: */ + lua_setfield(g_lua, -3, inst[i].name().c_str()); + /* lua stack: */ + } + lua_pushinteger(g_lua, inst[i].index()); + /* lua stack: */ + } - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); - lua_pushunsigned(g_lua, 'c'); - lua_pushcclosure(g_lua, my_lua_sct_reg, 2); - lua_setfield(g_lua, -2, "clr"); + /** create a new table for the instance */ + lua_newtable(g_lua); + /* lua stack: [ ] */ - lua_pushunsigned(g_lua, addr + reg.addr[index].addr); - lua_pushunsigned(g_lua, 't'); - lua_pushcclosure(g_lua, my_lua_sct_reg, 2); - lua_setfield(g_lua, -2, "tog"); - } + /** create name and desc fields */ + lua_pushstring(g_lua, inst[i].node().get()->name.c_str()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "name"); + /* lua stack: ... */ - for(size_t i = 0; i < reg.field.size(); i++) - { - my_lua_create_field(addr + reg.addr[index].addr, reg.field[i], - reg.flags & REG_HAS_SCT); - lua_setfield(g_lua, -2, reg.field[i].name.c_str()); - } -} + lua_pushstring(g_lua, inst[i].node().get()->desc.c_str()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "desc"); + /* lua stack: ... */ -void my_lua_create_dev(size_t index, const soc_dev_t& dev) -{ - lua_newtable(g_lua); + lua_pushstring(g_lua, inst[i].node().get()->title.c_str()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "title"); + /* lua stack: ... */ - lua_pushstring(g_lua, dev.addr[index].name.c_str()); - lua_setfield(g_lua, -2, "name"); + lua_pushunsigned(g_lua, inst[i].addr()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "addr"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, dev.addr[index].addr); - lua_setfield(g_lua, -2, "addr"); + /** create register */ + my_lua_create_reg(inst[i].addr(), inst[i].node().reg()); - for(size_t i = 0; i < dev.reg.size(); i++) - { - bool table = dev.reg[i].addr.size() > 1; - if(table) - lua_newtable(g_lua); - else - lua_pushnil(g_lua); + /** create subinstances */ + my_lua_create_instances(inst[i].children()); + /* lua stack: [ ] */ - for(size_t k = 0; k < dev.reg[i].addr.size(); k++) + if(inst[i].is_indexed()) { - my_lua_create_reg(dev.addr[index].addr, k, dev.reg[i]); - if(table) - { - lua_pushinteger(g_lua, k); - lua_pushvalue(g_lua, -2); - lua_settable(g_lua, -4); - } - lua_setfield(g_lua, -3, dev.reg[i].addr[k].name.c_str()); + /* lua stack: */ + lua_settable(g_lua, -3); + /* lua stack: */ + lua_pop(g_lua, 1); } - - if(table) - lua_setfield(g_lua, -2, dev.reg[i].name.c_str()); else - lua_pop(g_lua, 1); + { + /* lua stack: */ + lua_setfield(g_lua, -2, inst[i].name().c_str()); + } + /* lua stack: */ } } -bool my_lua_import_soc(const soc_t& soc) +bool my_lua_import_soc(soc_desc::soc_t& soc) { + /** remember old stack index to check for unbalanced stack at the end */ int oldtop = lua_gettop(g_lua); + /** find hwstub.soc table */ lua_getglobal(g_lua, "hwstub"); + /* lua stack: */ lua_getfield(g_lua, -1, "soc"); + /* lua stack: */ + /** create a new table for the soc */ lua_newtable(g_lua); + /* lua stack: */ + /** create name and desc fields */ lua_pushstring(g_lua, soc.name.c_str()); + /* lua stack: */ lua_setfield(g_lua, -2, "name"); + /* lua stack: */ lua_pushstring(g_lua, soc.desc.c_str()); + /* lua stack: */ lua_setfield(g_lua, -2, "desc"); + /* lua stack: */ - for(size_t i = 0; i < soc.dev.size(); i++) - { - bool table = soc.dev[i].addr.size() > 1; - if(table) - lua_newtable(g_lua); - else - lua_pushnil(g_lua); - - for(size_t k = 0; k < soc.dev[i].addr.size(); k++) - { - my_lua_create_dev(k, soc.dev[i]); - if(table) - { - lua_pushinteger(g_lua, k + 1); - lua_pushvalue(g_lua, -2); - lua_settable(g_lua, -4); - } - lua_setfield(g_lua, -3, soc.dev[i].addr[k].name.c_str()); - } - - if(table) - lua_setfield(g_lua, -2, soc.dev[i].name.c_str()); - else - lua_pop(g_lua, 1); - } + /** create instances */ + soc_desc::soc_ref_t rsoc(&soc); + my_lua_create_instances(rsoc.root_inst().children()); + /* lua stack: */ + /** put soc table at hwstub.soc. */ lua_setfield(g_lua, -2, soc.name.c_str()); + /* lua stack: */ lua_pop(g_lua, 2); + /* lua stack: <> */ if(lua_gettop(g_lua) != oldtop) { @@ -726,7 +792,7 @@ bool my_lua_import_soc(const soc_t& soc) return true; } -bool my_lua_import_soc(const std::vector< soc_t >& socs) +bool my_lua_import_soc(std::vector< soc_desc::soc_t >& socs) { for(size_t i = 0; i < socs.size(); i++) { @@ -803,15 +869,17 @@ int main(int argc, char **argv) } // load register descriptions - std::vector< soc_t > socs; + std::vector< soc_desc::soc_t > socs; for(int i = optind; i < argc; i++) { - socs.push_back(soc_t()); - if(!soc_desc_parse_xml(argv[i], socs[socs.size() - 1])) + socs.push_back(soc_desc::soc_t()); + soc_desc::error_context_t ctx; + if(!soc_desc::parse_xml(argv[i], socs[socs.size() - 1], ctx)) { - printf("Cannot load description '%s'\n", argv[i]); - return 2; + printf("Cannot load description file '%s'\n", argv[i]); + socs.pop_back(); } + print_context(argv[i], ctx); } // create usb context diff --git a/utils/regtools/Makefile b/utils/regtools/Makefile index 5e3feafd01..fed5d8c8e5 100644 --- a/utils/regtools/Makefile +++ b/utils/regtools/Makefile @@ -2,8 +2,9 @@ DEFINES= CC?=gcc CXX?=g++ LD?=g++ -CFLAGS=-g -std=c99 -Wall $(DEFINES) -Ilib -CXXFLAGS=-g -Wall $(DEFINES) -Ilib +INCLUDE=-Iinclude/ +CFLAGS=-g -std=c99 -Wall $(DEFINES) $(INCLUDE) +CXXFLAGS=-g -Wall $(DEFINES) $(INCLUDE) LDFLAGS=-Llib -lsocdesc `xml2-config --libs` SRC=$(wildcard *.c) SRCXX=$(wildcard *.cpp) diff --git a/utils/regtools/desc/XML.txt b/utils/regtools/desc/XML.txt deleted file mode 100644 index 3c28154a1f..0000000000 --- a/utils/regtools/desc/XML.txt +++ /dev/null @@ -1,140 +0,0 @@ -This file describes the format of the register map based on XML. - -Root ----- -As any XML document, the content of the file should be enclosed in a "xml" tag. - -Example: - - - - -Root Element: root ------------------- -The root element can either be "soc" tag if the file contains a single description, -or "root" with no properties and one or more "soc" tags as children. - -Element: soc ------------- -The XML can contain one or more SoC description. Each description is enclosed in -a "soc" tag. The following properties are defined: -- "name" (mandatory,string): the mnemonic of the SoC. -- "desc" (optional,string): a textual description of the SoC. - -Example: - - - - -Element: soc.dev ----------------- -Each SoC can contain any number of devices. Each device is enclosed in a "dev" -tag. A device is logical group of registers and doesn't have a precise meaning. -If a SoC has several copies of the same device at different addresses (SSP1, SSP2 -for example), then only one copy has to be described since a device can have -several address (see soc.dev.addr section). The following properties are defined: -- "name" (mandatory,string): the mnemonic of the device. -- "long_name" (optional,string): a short description of the device. -- "desc" (optional,string): a long description of the SoC. -- "version" (optional,string): version of the register description for this device. - -Example: - - - - -The address shortcut has the following standard translation: - - - -is equivalent to: - - - - - -Element: soc.dev.reg.addr -------------------------- -Each device can have one or more addresses associated with it. Each address -is enclosed in a "addr" tag. This allows to describe several register at once on -SoCs where a similar register is replicated several times. -The following properties are defined: -- "name" (mandatory,string): unique name of this instance of the register. -- "addr" (mandatory,integer): base address of this instance of the register. - -Example: - - - - -Element: soc.dev.reg.formula ----------------------------- -In the special case where the addresses of the register follow a pattern, an -explicit formula can be described as part of the format. There is no specific -requirement on the formula except that the register addresses be indexed by -a variable "n" which range from 0 to N-1 where N is the number of address. -The formula is not used for anything except header generation where it is -handy to have a formula rather a long list of conditionals. -The following properties are defined: -- "string" (mandatory,string): the equation describing the addresses. - -Example: - - -Element: soc.dev.reg.field --------------------------- -Each register can be further divided into disjoints fields. Each field -is enclosed in a "field" tag. A field is defined as a contiguous set -of bits in the register. The following properties are defined: -- "name" (mandatory,string): the mnemonic of field -- "bitrange" (mandatory,string): the bit range of the field written as - "n-m" where n and m are integers giving the most (resp. least) significant - bit of the field. - -Example: - - - - -Element: soc.dev.reg.field.value --------------------------------- -Each field can describe a list of named values in cases where this is appropriate. -Each value is enclosed in a "value" tag. The following properties are defined: -- "name" (mandatory,string): the mnemonic of the value. -- "value" (mandatory,integer): the associated value. - -Example: - - diff --git a/utils/regtools/desc/regs-example-v1.xml b/utils/regtools/desc/regs-example-v1.xml new file mode 100644 index 0000000000..4f3cf81ff2 --- /dev/null +++ b/utils/regtools/desc/regs-example-v1.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/regtools/desc/regs-example.xml b/utils/regtools/desc/regs-example.xml new file mode 100644 index 0000000000..6fb8f759fe --- /dev/null +++ b/utils/regtools/desc/regs-example.xml @@ -0,0 +1,153 @@ + + + vsoc + Virtual SOC + Virtual SoC is a nice and powerful chip. + Amaury Pouly + ARM + 0.5 + + int + Interrupt Collector + The interrupt collector controls the routing of interrupt to the processor + + ICOLL + Interrupt collector +
0x80000000
+
+ + status + read-only + Interrupt status register + + STATUS +
0x4
+
+ + 8 + + VDDIO_BO + VDDIO brownout interrupt status + 0 + + +
+ + enable + Interrupt enable register + + ENABLE +
0x8
+
+ + 16 + + VDDIO_BO + VDDIO brownout interrupt enable + 0 + 2 + + DISABLED + Interrupt is disabled + 0 + + + ENABLED + Interrupt is enabled + 1 + + + NMI + Interrupt is non-maskable + 2 + + + + + set + 4 + + + clr + 8 + +
+
+ + gpio + GPIO controller + A GPIO controller manages several ports + + CPU_GPIO + CPU GPIO controller 1 through 3 + + 1 + 3 + 0x80001000+(n-1)*0x1000 + + + + COP_GPIO + Companion processor GPIO controller + Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus +
0x90000000
+
+ + port + GPIO port + + PORT + + 0 + 4 + 0 + 0x100 + + + + input + Input register + + IN +
0
+
+ + 8 + + VALUE + 0 + 8 + + +
+ + output_enable + Output enable register + + OE +
0x10
+
+ + 8 + + ENABLE + 0 + 8 + + + + set +
4
+
+ + clr +
8
+
+ + mask +
12
+
+
+
+
+
\ No newline at end of file diff --git a/utils/regtools/desc/spec-1.0.txt b/utils/regtools/desc/spec-1.0.txt new file mode 100644 index 0000000000..3c28154a1f --- /dev/null +++ b/utils/regtools/desc/spec-1.0.txt @@ -0,0 +1,140 @@ +This file describes the format of the register map based on XML. + +Root +---- +As any XML document, the content of the file should be enclosed in a "xml" tag. + +Example: + + + + +Root Element: root +------------------ +The root element can either be "soc" tag if the file contains a single description, +or "root" with no properties and one or more "soc" tags as children. + +Element: soc +------------ +The XML can contain one or more SoC description. Each description is enclosed in +a "soc" tag. The following properties are defined: +- "name" (mandatory,string): the mnemonic of the SoC. +- "desc" (optional,string): a textual description of the SoC. + +Example: + + + + +Element: soc.dev +---------------- +Each SoC can contain any number of devices. Each device is enclosed in a "dev" +tag. A device is logical group of registers and doesn't have a precise meaning. +If a SoC has several copies of the same device at different addresses (SSP1, SSP2 +for example), then only one copy has to be described since a device can have +several address (see soc.dev.addr section). The following properties are defined: +- "name" (mandatory,string): the mnemonic of the device. +- "long_name" (optional,string): a short description of the device. +- "desc" (optional,string): a long description of the SoC. +- "version" (optional,string): version of the register description for this device. + +Example: + + +
+ +The address shortcut has the following standard translation: + + + +is equivalent to: + + + + + +Element: soc.dev.reg.addr +------------------------- +Each device can have one or more addresses associated with it. Each address +is enclosed in a "addr" tag. This allows to describe several register at once on +SoCs where a similar register is replicated several times. +The following properties are defined: +- "name" (mandatory,string): unique name of this instance of the register. +- "addr" (mandatory,integer): base address of this instance of the register. + +Example: + + + + +Element: soc.dev.reg.formula +---------------------------- +In the special case where the addresses of the register follow a pattern, an +explicit formula can be described as part of the format. There is no specific +requirement on the formula except that the register addresses be indexed by +a variable "n" which range from 0 to N-1 where N is the number of address. +The formula is not used for anything except header generation where it is +handy to have a formula rather a long list of conditionals. +The following properties are defined: +- "string" (mandatory,string): the equation describing the addresses. + +Example: + + +Element: soc.dev.reg.field +-------------------------- +Each register can be further divided into disjoints fields. Each field +is enclosed in a "field" tag. A field is defined as a contiguous set +of bits in the register. The following properties are defined: +- "name" (mandatory,string): the mnemonic of field +- "bitrange" (mandatory,string): the bit range of the field written as + "n-m" where n and m are integers giving the most (resp. least) significant + bit of the field. + +Example: + + + + +Element: soc.dev.reg.field.value +-------------------------------- +Each field can describe a list of named values in cases where this is appropriate. +Each value is enclosed in a "value" tag. The following properties are defined: +- "name" (mandatory,string): the mnemonic of the value. +- "value" (mandatory,integer): the associated value. + +Example: + + diff --git a/utils/regtools/desc/spec-2.0.txt b/utils/regtools/desc/spec-2.0.txt new file mode 100644 index 0000000000..79b9f6be44 --- /dev/null +++ b/utils/regtools/desc/spec-2.0.txt @@ -0,0 +1,373 @@ +This file describes the format of the register map based on XML, version 2.0. + +1) Overview +----------- + +1.1) Nodes and instances +------------------------ + +This specification is based on the concept of "nodes". Nodes are containers +which can contain other nodes and/or a register. Each node can have one or more +addresses (addresses are always relative to the parent node). The idea is that +this hierarchy of nodes generates a number of addresses recursively. The example +below outlines this idea: + + + N + + A +
X
+
+ + B +
Y
+
+ +
+ +This example creates one node named N and two instances named A and B, +at respective addresses X and Y. This means that all subnodes of this node will +have two copies: one relative to X, which path will be prefixed by "A", and +one relative to Y, which path will be prefixed by "B". +This example below explores this idea in details: + + + + S_N + + C +
U
+
+ + D +
V
+
+
+ +In this example, N generates two copies of the sub-node S_N. +The sub-node S_N generates two instances C and D. The whole hierarchy thus generates +four instances: +- A.C at X+U +- A.D at X+V +- B.C at Y+U +- B.D at Y+V + +As a note for later, notice that there really are two hierarchies in parallel: +- the node hierarchy: it is composed of N and N.S_N +- the instance hierarchy: it is made up of A, B, A.C, A.D, B.C and B.D + +1.2) Ranges +----------- + +To make things more useful, in particular in case of multiple copies of a register, +we introduce the concept of range addresses with an example: + + + N + + A + + 1 + 5 + 0x1000 + 0x100 + + + + NN + + E +
0x4
+
+
+
+ +A range describes a contiguous set of adresses, indexed by a number. One can +specify the first number in the range, and the number of copies. In the case +of a regular pattern (base + n * stride), we can specify a stride +to compute the address of the next copy. In this example, the top-level +nodes generates five copies which path is A[1], A[2], ..., A[5] +and which addresses are 0x1000+1*0x100, 0x1000+2*0x100, ..., 0x1000+5*0x100. +If we add the sub-node to the picture, for each copy we create a instance E +we offset 0x4 from the parent. Overall this generates 5 instances: +- A[1].E at 0x1000+1*0x100+0x4 = 0x1104 +- A[2].E at 0x1000+2*0x100+0x4 = 0x1204 +- A[3].E at 0x1000+3*0x100+0x4 = 0x1304 +- A[4].E at 0x1000+4*0x100+0x4 = 0x1404 +- A[5].E at 0x1000+5*0x100+0x4 = 0x1504 +Note that the intermediate path also define instances, so there are 5 additional +instances in reality: +- A[1] at 0x1100 +- A[2] at 0x1200 +- A[3] at 0x1300 +- A[4] at 0x1400 +- A[5] at 0x1500 + +For the record, there is a more general way of specifying a range when it does +not follow a nice regular pattern. One can specify a formula where the parameter +is the index. There are no restrictions on the formula except that it must use +usual arithmetic operators. The example below illustrate such a use: + + + N + + F + + 0 + 4 + 0x50+(n/2)*0x100+(n%2)*0x10 + + + + +In this example we generate four nodes F[0], ..., F[3] with a formula. Here "/" +is the euclidian division and "%" is the modulo operator. Note the use of an +attribute to specify which variable represents the index. The generated addresses +are: +- F[0] at 0x50+(0/2)*0x100+(0%2)*0x10 = 0x50 +- F[1] at 0x50+(1/2)*0x100+(1%2)*0x10 = 0x50+0x10 = 0x60 +- F[2] at 0x50+(2/2)*0x100+(2%2)*0x10 = 0x50+0x100 = 0x150 +- F[3] at 0x50+(3/2)*0x100+(3%2)*0x10 = 0x50+0x100+0x10 = 0x160 + +1.3) Node description +--------------------- + +For documentation purposes, node can of course carry some description, as well +as instances. More precisely, nodes can have a title, that is a short description +very much like a chapter title, and a description, this is a free form and potentially +lengthy description of the node. Instances too can have a title and a description. +The following example illustrates this: + + + icoll + DMA Controller + The DMA controller provides uniform DMA facilities to transfer data from + and to peripherals. It uses memory-mapped tables and support chained + transfers. + + AHB_DMAC +
0x80000000
+ AHB DMA Controller + The AHB DMA controller provides DMA facilities for the peripherals + on the AHB bus like the SSP and PIX engines. +
+ + APB_DMAC +
0x8001000
+ APB DMA Controller + The APB DMA controller provides DMA facilities for the peripherals + on the APB bus like the I2C and PCM engines. +
+
+ +1.4) Register description +-------------------------- + +The goal of the register description is of course to describe registers! +To see how registers relate to the node hierarchy, see 1.5, this section focuses +only the description only. + +A register carries a lot of information, which is organise logically. A register +can have a width, in bits. By default registers are assumed to be 32-bit wide. +The most useful feature of register description is to describe the fields of +the registers. Each field has a name, a start position and a width. Fields +can also carry a description. Finally, each field can specify enumerated values, +that is named values, for convenience. Enumerated values have a name, a value +and an optional description. The example below illustrates all these concepts: + + + 8 + + MODE + Interrupt mode + 0 + 2 + + DISABLED + Interrupt is disabled + 0 + + + ENABLED + Interrupt is enabled + 1 + + + NMI + Interrupt is non-maskable + 2 + + + + PRIORITY + Interrupt priority, lower values are more prioritized. + 2 + 2 + + + ARM_MODE + Select between ARM's FIQ and IRQ mode + 4 + 1 + + IRQ + 0 + + + FIQ + 1 + + + + +In this example, the 8-bit registers has three fields: +- MODE(1:0): it has three named values DISABLED(0), ENABLED(1) and NMI(2) +- PRIORITY(2:1): it has no named values +- ARM_MODE(3): it has two named values IRQ(0) and FIQ(1) + +1.5) Register inheritance +------------------------- + +The node hierarchy specifies instances, that is pairs of (path,address), +and the register description describes the internal of a register. The placement +of the register descriptions in the node hierarchy will specify which registers +can be found at each address. More precisely, if a node contains a register +description, it means that this node's and all sub-nodes' instances are registers +following the description. It is forbidden for a node to contain a register +description if one of its parents already contains one. The example below +will make this concept clearer (for readability, we omitted some of the tags): + + + dma + DMAC
0x80000000
+ + PCM_CHAN
0x0
+ I2C_CHAN
0x10
+ + + sct + SET
0x4
+ CLR
0x8
+ TOG
0xC
+
+
+
+ +This example describes one register (let's call it blabla) and 9 instances: +- DMAC at 0x80000000, no register +- DMAC.PCM_CHAN at 0x80000000, register blabla +- DMAC.PCM_CHAN.SET at 0x80000004, register blabla +- DMAC.PCM_CHAN.CLR at 0x80000008, register blabla +- DMAC.PCM_CHAN.TOG at 0x8000000C, register blabla +- DMAC.I2C_CHAN at 0x80000010, register blabla +- DMAC.I2C_CHAN.SET at 0x80000014, register blabla +- DMAC.I2C_CHAN.CLR at 0x80000018, register blabla +- DMAC.I2C_CHAN.TOG at 0x8000001C, register blabla + +1.6) Soc description +-------------------- + +The description file must also specify some information about the system-on-chip +itself. The entire description, including nodes, is contained in a "soc" tag +which must at least specify the soc name. It can optionally specify the title +and description, as well as the author(s) of the description, the ISA and +the version. + + + vsoc + Virtual SOC + Virtual SoC is a nice and powerful chip. + Amaury Pouly + ARM + 0.5 + + + +2) Specification +---------------- + +Root +---- +As any XML document, the content of the file should be enclosed in a "xml" tag. +The root element must be "soc" tag. + +Example: + + + + +Element: soc +------------ +It must contain the following tags: +- name: name of soc, only made of alphanumerical characters +It can contain at most one of each of the following tags: +- title: one line description of the soc +- desc: free form description of the soc +- isa: instruction set assembly +- version: version of the description +It can contain any number of the following tags: +- author: author of the description +- node: node description + +Element: node +------------- +It must contain the following tags: +- name: name of node, only made of alphanumerical characters +It can contain at most one of each of the following tags: +- title: one line description of the node +- desc: free form description of the node +- register: register description +It can contain any number of the following tags: +- instance: author of the description +- node: node description + +Element: instance +----------------- +It must contain the following tags: +- name: name of instance, only made of alphanumerical characters +It can contain at most one of each of the following tags: +- title: one line description of the instance +- desc: free form description of the instance +- address: address for a single instance (non-negative number) +- range: address range for multiple instances +Note that address and range are mutually exclusive, and at least one of them +must exists. + +Element: range +-------------- +It must contain the following tags: +- first: index of the first instance (non-negative number) +- count: number of instances (positive number) +It can contain at most one of each of the following tags: +- base: base address (non-negative number) +- stride: stride (number) +- formula: free-form formula, must have a "variable" attribute +Note that stride and formula are mutually exclusive, and at least one of them +must exists. If stride is specified and base is omitted, it is taken to be 0. + +Element: register +----------------- +It can contain at most one of each of the following tags: +- width: width in bits (positive number) +It can contain any number of the following tags: +- field: field description + +Element: field +-------------- +It must contain the following tags: +- name: name of field, only made of alphanumerical characters +- position: least significant bit +It can contain at most one of each of the following tags: +- desc: free form description of the instance +- width: width in bits +It can contain any number of the following tags: +- enum: enumerated value +If the width is not specified, it is assumed to be 1. + +Element: enum +------------- +It must contain the following tags: +- name: name of field, only made of alphanumerical characters +- value: value (non-negative, must fit into the field's width) +It can contain at most one of each of the following tags: +- desc: free form description of the instance \ No newline at end of file diff --git a/utils/regtools/headergen.cpp b/utils/regtools/headergen.cpp deleted file mode 100644 index 046e8b2fd8..0000000000 --- a/utils/regtools/headergen.cpp +++ /dev/null @@ -1,749 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2013 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 -#include -#include -#include -#include -#include -#include -#include - -#define HEADERGEN_VERSION "2.1.8" - -#define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0) - -bool g_gen_selector = false; - -std::string g_macro_filename; - -std::string g_soc_name; -std::string g_soc_dev; -std::string g_soc_reg; -std::string g_soc_field; -std::string g_soc_dev_regs_base; - -namespace { -std::string tolower(const std::string s) -{ - std::string res = s; - std::transform(res.begin(), res.end(), res.begin(), ::tolower); - return res; -} - -std::string toupper(const std::string& s) -{ - std::string res = s; - std::transform(res.begin(), res.end(), res.begin(), ::toupper); - return res; -} -} - -template< typename T > -std::string to_str(const T& v) -{ - std::ostringstream oss; - oss << v; - return oss.str(); -} - -template< typename T > -std::string to_hex(const T& v) -{ - std::ostringstream oss; - oss << std::hex << v; - return oss.str(); -} - -typedef std::pair< std::string, std::string > xml_ver_t; - -void fprint_copyright(FILE *f, const std::vector< xml_ver_t >& versions, char const *author) -{ - std::ostringstream ver; - time_t t = time(NULL); - struct tm tm = *localtime(&t); - - for(size_t i = 0; i < versions.size(); i++) - ver << " " << versions[i].first << ":" << versions[i].second; - - fprintf(f,"\ -/***************************************************************************\n\ - * __________ __ ___.\n\ - * Open \\______ \\ ____ ____ | | _\\_ |__ _______ ___\n\ - * Source | _// _ \\_/ ___\\| |/ /| __ \\ / _ \\ \\/ /\n\ - * Jukebox | | ( <_> ) \\___| < | \\_\\ ( <_> > < <\n\ - * Firmware |____|_ /\\____/ \\___ >__|_ \\|___ /\\____/__/\\_ \\\n\ - * \\/ \\/ \\/ \\/ \\/\n\ - * This file was automatically generated by headergen, DO NOT EDIT it.\n\ - * headergen version: " HEADERGEN_VERSION "\n"); - if(versions.size() > 0) - fprintf(f, " * XML versions:%s\n", ver.str().c_str()); - fprintf(f,"\ - *\n\ - * Copyright (C) "); - fprintf(f, "%d by %s\n", 1900+tm.tm_year, author); - fprintf(f,"\ - *\n\ - * This program is free software; you can redistribute it and/or\n\ - * modify it under the terms of the GNU General Public License\n\ - * as published by the Free Software Foundation; either version 2\n\ - * of the License, or (at your option) any later version.\n\ - *\n\ - * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\n\ - * KIND, either express or implied.\n\ - *\n\ - ****************************************************************************/\n"); -} - -void fprint_copyright(FILE *f, const xml_ver_t& version, char const *author) -{ - fprint_copyright(f, std::vector< xml_ver_t >(1, version), author); -} - -void fprint_copyright(FILE *f, char const *author) -{ - fprint_copyright(f, std::vector< xml_ver_t >(), author); -} - -void fprint_include_guard_ex(FILE *f, bool begin, const std::string& name) -{ - if(begin) - { - fprintf(f, "#ifndef %s\n", name.c_str()); - fprintf(f, "#define %s\n", name.c_str()); - } - else - fprintf(f, "#endif /* %s */\n", name.c_str()); -} - -void fprint_include_guard(FILE *f, bool begin) -{ - std::string name = "__HEADERGEN__" + toupper(g_soc_name) + "__" + toupper(g_soc_dev) - + "__H__"; - fprint_include_guard_ex(f, begin, name); -} - -struct limited_column_context_t -{ - limited_column_context_t(size_t nr_col = 80) - :m_nr_col(nr_col), m_prevent_wordcut(true) {} - void set_prefix(const std::string& prefix) { m_prefix = prefix; } - void add(const std::string& text) - { - for(size_t i = 0; i < text.size();) - { - size_t offset = 0; - if(m_cur_line.size() == 0) - m_cur_line = m_prefix; - size_t len = std::min(text.size() - i, m_nr_col - m_cur_line.size()); - // prevent word cut - if(m_prevent_wordcut && !isspace(text[i + len - 1]) && - i + len < text.size() && !isspace(text[i + len])) - { - size_t pos = text.find_last_of(" \t\n\v\r\f", i + len - 1); - if(pos == std::string::npos || pos < i) - len = 0; - else - len = pos - i + 1; - } - size_t pos = text.find('\n', i); - if(pos != std::string::npos && pos <= i + len) - { - offset = 1; - len = pos - i; - } - m_cur_line += text.substr(i, len); - // len == 0 means we need a new line - if(m_cur_line.size() == m_nr_col || len == 0) - { - m_lines.push_back(m_cur_line); - m_cur_line = ""; - } - i += len + offset; - } - } - - std::string to_string() - { - std::string str; - for(size_t i = 0; i < m_lines.size(); i++) - str += m_lines[i] + "\n"; - if(m_cur_line.size() != 0) - str += m_cur_line + "\n"; - return str; - } - - void print(FILE *f) - { - fprintf(f, "%s", to_string().c_str()); - } - - std::vector< std::string > m_lines; - std::string m_cur_line; - std::string m_prefix; - size_t m_nr_col; - bool m_prevent_wordcut; -}; - -struct define_align_context_t -{ - define_align_context_t():m_max_name(0) {} - void add(const std::string& name, const std::string& val) - { - m_lines.push_back(std::make_pair(name, val)); - m_max_name = std::max(m_max_name, name.size()); - } - - void add_raw(const std::string& line) - { - m_lines.push_back(std::make_pair("", line)); - } - - void print(FILE *f) - { - std::string define = "#define "; - size_t align = define.size() + m_max_name + 1; - align = ((align + 3) / 4) * 4; - - for(size_t i = 0; i < m_lines.size(); i++) - { - std::string name = m_lines[i].first; - // raw entry ? - if(name.size() == 0) - { - fprintf(f, "%s", m_lines[i].second.c_str()); - continue; - } - name.insert(name.end(), align - define.size() - name.size(), ' '); - fprintf(f, "%s%s%s\n", define.c_str(), name.c_str(), m_lines[i].second.c_str()); - } - } - - size_t m_max_name; - std::vector< std::pair< std::string, std::string > > m_lines; -}; - -limited_column_context_t print_description(const std::string& desc, const std::string& prefix) -{ - limited_column_context_t ctx; - if(desc.size() == 0) - return ctx; - ctx.set_prefix(prefix); - ctx.add(desc); - return ctx; -} - -void fprint_description(FILE *f, const std::string& desc, const std::string& prefix) -{ - limited_column_context_t ctx = print_description(desc, prefix); - ctx.print(f); -} - -void fprint_description(define_align_context_t& ctx, const std::string& desc, const std::string& prefix) -{ - limited_column_context_t ctx2 = print_description(desc, prefix); - ctx.add_raw(ctx2.to_string()); -} - -void gen_soc_field(define_align_context_t& ctx, bool multidev, bool multireg, const soc_reg_field_t& field) -{ - if(field.desc.size() != 0) - { - ctx.add_raw("/* Field: " + field.name + "\n"); - fprint_description(ctx, "Description: " + field.desc + " */\n", " * "); - } - - std::string prefix = g_soc_dev + "_" + g_soc_reg + "_" + g_soc_field; - ctx.add("BP_" + prefix, to_str(field.first_bit)); - ctx.add("BM_" + prefix, "0x" + to_hex(field.bitmask())); - - for(size_t i = 0; i < field.value.size(); i++) - ctx.add("BV_" + prefix + "__" + field.value[i].name, "0x" + to_hex(field.value[i].value)); - - ctx.add("BF_" + prefix + "(v)", "(((v) << " + to_str(field.first_bit) + ") & 0x" + to_hex(field.bitmask()) + ")"); - - if(field.value.size() > 0) - ctx.add("BF_" + prefix + "_V(v)", "((BV_" + prefix + "__##v" + " << " + to_str(field.first_bit) + ") & 0x" + to_hex(field.bitmask()) + ")"); -} - -void gen_soc_reg(FILE *f, bool multidev, const soc_reg_t& reg, char const *author, char const *hwprefix) -{ - bool multireg = reg.addr.size() > 1; - - static const char *suffix[] = {"", "_SET", "_CLR", "_TOG"}; - bool sct = !!(reg.flags & REG_HAS_SCT); - - // comment generator - fprintf(f, "/**\n"); - fprintf(f, " * Register: %s%s_%s\n", hwprefix, g_soc_dev.c_str(), g_soc_reg.c_str()); - fprintf(f, " * Address:"); - if(multireg && reg.formula.type == REG_FORMULA_STRING) - { - fprintf(f, " %s\n", reg.formula.string.c_str()); - } - else - { - for(size_t i = 0; i < reg.addr.size(); i++) - fprintf(f, " %#x", reg.addr[i].addr); - fprintf(f, "\n"); - } - fprintf(f, " * SCT: %s\n", sct ? "yes" : "no"); - if(reg.desc.size() != 0) - fprint_description(f, "Description: " + reg.desc, " * "); - fprintf(f, " */\n"); - - define_align_context_t ctx; - - if (multireg && reg.formula.type != REG_FORMULA_STRING) - { - for(int i=0; i < (int)reg.addr.size(); i++) - { - std::ostringstream name, value; - name << hwprefix << g_soc_dev << "_" << reg.addr[i].name; - if (multidev) - { - name << "(d)"; - } - value << "(*(volatile unsigned long *)(" << g_soc_dev_regs_base; - if (multidev) - { - value << "(d)"; - } - value << " + 0x" << std::hex << reg.addr[i].addr << "))"; - - ctx.add(name.str(), value.str()); - } - } - else - { - for(int i = 0; i < (sct ? 4 : 1); i++) - { - std::ostringstream name; - name << hwprefix << g_soc_dev << "_" << g_soc_reg << suffix[i]; - if(multidev || multireg) - { - name << "("; - if(multidev) - name << "d"; - if(multidev && multireg) - name << ","; - if(multireg) - name << "n"; - name << ")"; - } - std::ostringstream value; - value << "(*(volatile unsigned long *)(" << g_soc_dev_regs_base; - if(multidev) - value << "(d)"; - value << " + "; - if(multireg) - { - std::string formula = reg.formula.string.c_str(); - size_t pos = formula.find("n"); - while(pos != std::string::npos) - { - formula.replace(pos, 1, "(n)"); - pos = formula.find("n", pos + 2); - } - value << formula; - } - else - value << "0x" << std::hex << reg.addr[0].addr; - - if(sct) - value << " + 0x" << std::hex << (i * 4); - value << "))"; - - ctx.add(name.str(), value.str()); - } - } - - for(size_t i = 0; i < reg.field.size(); i++) - { - g_soc_field = reg.field[i].name; - gen_soc_field(ctx, multidev, multireg, reg.field[i]); - } - - ctx.print(f); - - fprintf(f, "\n"); -} - -void gen_soc_dev_header(const std::string& filename, const xml_ver_t& ver, const soc_dev_t& dev, - char const *author, char const *hwprefix) -{ - /* - printf("Generate headers for soc %s, dev %s: use file %s\n", g_soc_name.c_str(), - g_soc_dev.c_str(), filename.c_str()); - */ - FILE *f = fopen(filename.c_str(), "w"); - if(f == NULL) - { - printf("Cannot open %s for writing: %m\n", filename.c_str()); - return; - } - fprint_copyright(f, ver, author); - fprint_include_guard(f, true); - if(g_macro_filename.size() > 0) - fprintf(f, "#include \"%s\"\n", g_macro_filename.c_str()); - - /* print base */ - fprintf(f, "\n"); - g_soc_dev_regs_base = "REGS_" + g_soc_dev + "_BASE"; - fprintf(f, "#define %s", g_soc_dev_regs_base.c_str()); - - if(dev.addr.size() > 1) - fprintf(f, "(i)"); - fprintf(f, " ("); - - for(size_t i = 0; i < dev.addr.size() - 1; i++) - fprintf(f, "(i) == %d ? %#x : ", (int)i + 1, dev.addr[i].addr); - - fprintf(f, "%#x)\n", dev.addr[dev.addr.size() - 1].addr); - - fprintf(f, "\n"); - - /* print version */ - fprintf(f, "#define REGS_%s_VERSION \"%s\"\n\n", g_soc_dev.c_str(), dev.version.c_str()); - - for(size_t i = 0; i < dev.reg.size(); i++) - { - g_soc_reg = dev.reg[i].name; - gen_soc_reg(f, dev.addr.size() > 1, dev.reg[i], author, hwprefix); - } - - fprint_include_guard(f, false); - fclose(f); -} - -void gen_soc_headers(const std::string& prefix, const soc_t& soc, char const *author, char const *hwprefix) -{ - printf("Generate headers for soc %s: use directory %s\n", soc.name.c_str(), - prefix.c_str()); - mkdir(prefix.c_str(), 0770); - - for(size_t i = 0; i < soc.dev.size(); i++) - { - g_soc_dev = soc.dev[i].name; - xml_ver_t ver(soc.name, soc.dev[i].version); - gen_soc_dev_header(prefix + "/regs-" + tolower(g_soc_dev.c_str()) + ".h", ver, soc.dev[i], author, hwprefix); - } -} - -void gen_headers(const std::string& prefix, const std::vector< soc_t >& socs, char const *author, char const *hwprefix) -{ - for(size_t i = 0; i < socs.size(); i++) - { - g_soc_name = socs[i].name; - std::string dir = prefix; - if(g_gen_selector) - dir += "/" + socs[i].name; - gen_soc_headers(dir, socs[i], author, hwprefix); - } -} - -typedef std::map< std::string, std::vector< std::pair< size_t, size_t > > > general_dev_list_t; -general_dev_list_t build_general_dev_list(const std::vector< soc_t >& socs) -{ - general_dev_list_t map; - for(size_t i = 0; i < socs.size(); i++) - { - for(size_t j = 0; j < socs[i].dev.size(); j++) - map[tolower(socs[i].dev[j].name)].push_back(std::make_pair(i,j)); - } - return map; -} - -void gen_select_header(const std::string& filename, const std::string& dev, - const std::vector< std::string >& socs, const std::vector< xml_ver_t >& ver, char const *author) -{ - std::string guard = "__SELECT__" + toupper(dev) + "__H__"; - FILE *f = fopen(filename.c_str(), "w"); - if(f == NULL) - error("Cannot open file %s\n", filename.c_str()); - fprint_copyright(f, ver, author); - fprint_include_guard_ex(f, true, guard); - if(g_macro_filename.size() > 0) - fprintf(f, "#include \"%s\"\n", g_macro_filename.c_str()); - fprintf(f, "\n"); - - for(size_t i = 0; i < socs.size(); i++) - { - fprintf(f, "#define %s_INCLUDE \"%s/regs-%s.h\"\n", - toupper(socs[i]).c_str(), tolower(socs[i]).c_str(), - tolower(dev).c_str()); - } - fprintf(f, "\n#include \"regs-select.h\"\n\n"); - for(size_t i = 0; i < socs.size(); i++) - { - fprintf(f, "#undef %s_INCLUDE\n", toupper(socs[i]).c_str()); - } - fprintf(f, "\n"); - fprint_include_guard_ex(f, false, guard); - fclose(f); -} - -void gen_selectors(const std::string& prefix, const std::vector< soc_t >& socs, char const *author) -{ - printf("Generate select headers: use directory %s\n", prefix.c_str()); - general_dev_list_t map = build_general_dev_list(socs); - for(general_dev_list_t::iterator it = map.begin(); it != map.end(); ++it) - { - std::vector< xml_ver_t > ver; - std::vector< std::string > names; - for(size_t i = 0; i < it->second.size(); i++) - { - size_t soc_nr = it->second[i].first; - size_t dev_in_soc_nr = it->second[i].second; - ver.push_back(std::make_pair(socs[soc_nr].name, socs[soc_nr].dev[dev_in_soc_nr].version)); - names.push_back(socs[soc_nr].name); - } - gen_select_header(prefix + "/regs-" + it->first + ".h", it->first, names, ver, author); - } -} - -void gen_macro_list(FILE *f, const std::string& prefix, int count, int nr_digits, - const std::string& sep, int max_per_line = 1000, const std::string& align = "") -{ - for(int i = 1; i <= count;) - { - for(int j = i; j <= std::min(count, i + max_per_line - 1); j++) - { - fprintf(f, "%s%0*d", prefix.c_str(), nr_digits, j); - if(j < count) - fprintf(f, "%s", sep.c_str()); - } - i += max_per_line; - if(i <= count) - fprintf(f, "\\\n%s", align.c_str()); - } -} - -void gen_macro(const std::string& filename, bool variadic, char const *author, char const *hwprefix) -{ - printf("Generate %smacro header: use %s\n", variadic ? "": "non-variadic ", - filename.c_str()); - std::string guard = "__REGS__MACRO__H__"; - FILE *f = fopen(filename.c_str(), "w"); - if(f == NULL) - error("Cannot open file %s\n", filename.c_str()); - fprint_copyright(f, author); - fprint_include_guard_ex(f, true, guard); - fprintf(f, "\n"); - -#define REG_WRITE "REG_WRITE" - fprintf(f, "#ifndef %s\n", REG_WRITE); - fprintf(f, "#define %s(var,value) ((var) = (value))\n", REG_WRITE); - fprintf(f, "#endif /* %s */\n", REG_WRITE); - fprintf(f, "\n"); - -#define REG_READ "REG_READ" - fprintf(f, "#ifndef %s\n", REG_READ); - fprintf(f, "#define %s(var) (var)\n", REG_READ); - fprintf(f, "#endif /* %s */\n", REG_READ); - fprintf(f, "\n"); - - const int MAX_NARGS = 32; - - fprintf(f, "\ -#define BF_SET(reg, field) "REG_WRITE"(%1$s##reg##_SET, BM_##reg##_##field)\n\ -#define BF_CLR(reg, field) "REG_WRITE"(%1$s##reg##_CLR, BM_##reg##_##field)\n\ -#define BF_TOG(reg, field) "REG_WRITE"(%1$s##reg##_TOG, BM_##reg##_##field)\n\ -\n\ -#define BF_SETV(reg, field, v) "REG_WRITE"(%1$s##reg##_SET, BF_##reg##_##field(v))\n\ -#define BF_CLRV(reg, field, v) "REG_WRITE"(%1$s##reg##_CLR, BF_##reg##_##field(v))\n\ -#define BF_TOGV(reg, field, v) "REG_WRITE"(%1$s##reg##_TOG, BF_##reg##_##field(v))\n\ -\n\ -#define BF_RDX(val, reg, field) (("REG_READ"(val) & BM_##reg##_##field) >> BP_##reg##_##field)\n\ -#define BF_RD(reg, field) BF_RDX("REG_READ"(%1$s##reg), reg, field)\n\ -#define BF_WRX(val, reg, field, v) "REG_WRITE"(val, ("REG_READ"(val) & ~BM_##reg##_##field) | (((v) << BP_##reg##_##field) & BM_##reg##_##field))\n\ -#define BF_WR(reg, field, v) BF_WRX(%1$s##reg, reg, field, v)\n\ -#define BF_WR_V(reg, field, sy) BF_WR(reg, field, BV_##reg##_##field##__##sy)\n\ -#define BF_WR_VX(val, reg, field, sy) BF_WRX(val, reg, field, BV_##reg##_##field##__##sy)\n\ -\n\ -#define BF_SETn(reg, n, field) "REG_WRITE"(%1$s##reg##_SET(n), BM_##reg##_##field)\n\ -#define BF_CLRn(reg, n, field) "REG_WRITE"(%1$s##reg##_CLR(n), BM_##reg##_##field)\n\ -#define BF_TOGn(reg, n, field) "REG_WRITE"(%1$s##reg##_TOG(n), BM_##reg##_##field)\n\ -\n\ -#define BF_SETVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_SET(n), BF_##reg##_##field(v))\n\ -#define BF_CLRVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_CLR(n), BF_##reg##_##field(v))\n\ -#define BF_TOGVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_TOG(n), BF_##reg##_##field(v))\n\ -\n\ -#define BF_RDn(reg, n, field) BF_RDX(%1$s##reg(n), reg, field)\n\ -#define BF_WRn(reg, n, field, v) BF_WRX(%1$s##reg(n), reg, field, v)\n\ -#define BF_WRn_V(reg, n, field, sy) BF_WRn(reg, n, field, BV_##reg##_##field##__##sy)\n\ -\n", hwprefix); - - for(int nargs = 1; nargs <= MAX_NARGS; nargs++) - { - fprintf(f, "#define BM_OR%d(reg, ", nargs); - gen_macro_list(f, "f", nargs, 2, ", ", 10, " "); - fprintf(f, ") \\\n ("); - gen_macro_list(f, "BM_##reg##_##f", nargs, 2, " | ", 4, " "); - fprintf(f, ")\n"); - } - fprintf(f, "\n"); - - for(int nargs = 1; nargs <= MAX_NARGS; nargs++) - { - fprintf(f, "#define BF_OR%d(reg, ", nargs); - gen_macro_list(f, "f", nargs, 2, ", ", 10, " "); - fprintf(f, ") \\\n ("); - gen_macro_list(f, "BF_##reg##_##f", nargs, 2, " | ", 4, " "); - fprintf(f, ")\n"); - } - fprintf(f, "\n"); - - if(variadic) - { - fprintf(f, "#define REG_NARG(...) REG_NARGS_(__VA_ARGS__"); - for(int i = MAX_NARGS; i >= 1; i--) - fprintf(f, ", %d", i); - fprintf(f, ")\n"); - fprintf(f, "#define REG_NARGS_("); - gen_macro_list(f, "_", MAX_NARGS, 1, ", "); - fprintf(f, ", N, ...) N\n\n"); - - fprintf(f, "#define REG_VARIADIC(macro, reg, ...) REG_VARIADIC_(macro, REG_NARG(__VA_ARGS__), reg, __VA_ARGS__)\n"); - fprintf(f, "#define REG_VARIADIC_(macro, cnt, reg, ...) REG_VARIADIC__(macro, cnt, reg, __VA_ARGS__)\n"); - fprintf(f, "#define REG_VARIADIC__(macro, cnt, reg, ...) REG_VARIADIC___(macro##cnt, reg, __VA_ARGS__)\n"); - fprintf(f, "#define REG_VARIADIC___(macro, reg, ...) macro(reg, __VA_ARGS__)\n\n"); - - fprintf(f, "#define BM_OR(reg, ...) REG_VARIADIC(BM_OR, reg, __VA_ARGS__)\n"); - fprintf(f, "#define BF_OR(reg, ...) REG_VARIADIC(BF_OR, reg, __VA_ARGS__)\n"); - } - - fprint_include_guard_ex(f, false, guard); - fclose(f); -} - -void usage() -{ - printf("usage: headergen [options] \n"); - printf("options:\n"); - printf(" -?/--help Dispaly this help\n"); - printf(" -s/--selector Always produce selector files\n"); - printf(" -m/--no-macro Do not generate a macro file with helpers\n"); - printf(" -i/--no-include Do not include the macro file in the headers\n"); - printf(" -v/--no-variadic Do not generate variadic macros\n"); - printf(" -p/--reg-prefix \"prefix\" Prefix register names\n"); - printf(" -a/--author \"Author\" Set author name in preamble\n"); - printf("\n"); - printf("Default option is to generate a macro file with variadic macros.\n"); - printf("Default option is to include the macro file in the headers.\n"); - printf("Default option is to generate selector files only for two or more socs.\n"); - printf("Default option is to create one subdirectory per soc, except if no\n"); - printf("selector files are needed. The subdirectories will be created if necessary.\n"); - printf("Default option is to not prefix register names\n"); - printf("Default option is \"Unknown Author\" for author in preamble\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - bool force_selector = false; - bool no_variadic = false; - bool no_macro = false; - bool no_include = false; - char const *author = "Unknown Author"; - char const *hwprefix = ""; - - if(argc <= 1) - usage(); - - while(1) - { - static struct option long_options[] = - { - {"help", no_argument, 0, '?'}, - {"selector", no_argument, 0, 's'}, - {"no-macro", no_argument, 0, 'm'}, - {"no-include", no_argument, 0, 'i'}, - {"no-variadic", no_argument, 0, 'v'}, - {"reg-prefix", required_argument, 0, 'p'}, - {"author", required_argument, 0, 'a'}, - {0, 0, 0, 0} - }; - - int c = getopt_long(argc, argv, "?smivp:a:", long_options, NULL); - if(c == -1) - break; - switch(c) - { - case -1: - break; - case '?': - usage(); - break; - case 's': - force_selector = true; - break; - case 'm': - no_macro = true; - break; - case 'i': - no_include = true; - break; - case 'v': - no_variadic = true; - break; - case 'a' : - author = optarg; - break; - case 'p' : - hwprefix = optarg; - break; - default: - abort(); - } - } - - std::vector< soc_t > socs; - for(int i = optind; i < argc - 1; i++) - { - soc_t s; - if(!soc_desc_parse_xml(argv[i], s)) - { - printf("Cannot parse %s\n", argv[i]); - return 1; - } - socs.push_back(s); - } - - g_gen_selector = force_selector || socs.size() > 1; - - if(!no_macro) - { - g_macro_filename = std::string(argv[argc - 1]) + "/regs-macro.h"; - gen_macro(g_macro_filename, !no_variadic, author, hwprefix); - g_macro_filename = "regs-macro.h"; - if(no_include) - g_macro_filename.clear(); - } - if(g_gen_selector) - { - gen_selectors(argv[argc - 1], socs, author); - g_macro_filename.clear(); - } - gen_headers(argv[argc - 1], socs, author, hwprefix); - return 0; -} diff --git a/utils/regtools/headergen_v1.cpp b/utils/regtools/headergen_v1.cpp new file mode 100644 index 0000000000..7b38366d5d --- /dev/null +++ b/utils/regtools/headergen_v1.cpp @@ -0,0 +1,751 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2013 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_v1.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace soc_desc_v1; + +#define HEADERGEN_VERSION "2.1.8" + +#define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0) + +bool g_gen_selector = false; + +std::string g_macro_filename; + +std::string g_soc_name; +std::string g_soc_dev; +std::string g_soc_reg; +std::string g_soc_field; +std::string g_soc_dev_regs_base; + +namespace { +std::string tolower(const std::string s) +{ + std::string res = s; + std::transform(res.begin(), res.end(), res.begin(), ::tolower); + return res; +} + +std::string toupper(const std::string& s) +{ + std::string res = s; + std::transform(res.begin(), res.end(), res.begin(), ::toupper); + return res; +} +} + +template< typename T > +std::string to_str(const T& v) +{ + std::ostringstream oss; + oss << v; + return oss.str(); +} + +template< typename T > +std::string to_hex(const T& v) +{ + std::ostringstream oss; + oss << std::hex << v; + return oss.str(); +} + +typedef std::pair< std::string, std::string > xml_ver_t; + +void fprint_copyright(FILE *f, const std::vector< xml_ver_t >& versions, char const *author) +{ + std::ostringstream ver; + time_t t = time(NULL); + struct tm tm = *localtime(&t); + + for(size_t i = 0; i < versions.size(); i++) + ver << " " << versions[i].first << ":" << versions[i].second; + + fprintf(f,"\ +/***************************************************************************\n\ + * __________ __ ___.\n\ + * Open \\______ \\ ____ ____ | | _\\_ |__ _______ ___\n\ + * Source | _// _ \\_/ ___\\| |/ /| __ \\ / _ \\ \\/ /\n\ + * Jukebox | | ( <_> ) \\___| < | \\_\\ ( <_> > < <\n\ + * Firmware |____|_ /\\____/ \\___ >__|_ \\|___ /\\____/__/\\_ \\\n\ + * \\/ \\/ \\/ \\/ \\/\n\ + * This file was automatically generated by headergen, DO NOT EDIT it.\n\ + * headergen version: " HEADERGEN_VERSION "\n"); + if(versions.size() > 0) + fprintf(f, " * XML versions:%s\n", ver.str().c_str()); + fprintf(f,"\ + *\n\ + * Copyright (C) "); + fprintf(f, "%d by %s\n", 1900+tm.tm_year, author); + fprintf(f,"\ + *\n\ + * This program is free software; you can redistribute it and/or\n\ + * modify it under the terms of the GNU General Public License\n\ + * as published by the Free Software Foundation; either version 2\n\ + * of the License, or (at your option) any later version.\n\ + *\n\ + * This software is distributed on an \"AS IS\" basis, WITHOUT WARRANTY OF ANY\n\ + * KIND, either express or implied.\n\ + *\n\ + ****************************************************************************/\n"); +} + +void fprint_copyright(FILE *f, const xml_ver_t& version, char const *author) +{ + fprint_copyright(f, std::vector< xml_ver_t >(1, version), author); +} + +void fprint_copyright(FILE *f, char const *author) +{ + fprint_copyright(f, std::vector< xml_ver_t >(), author); +} + +void fprint_include_guard_ex(FILE *f, bool begin, const std::string& name) +{ + if(begin) + { + fprintf(f, "#ifndef %s\n", name.c_str()); + fprintf(f, "#define %s\n", name.c_str()); + } + else + fprintf(f, "#endif /* %s */\n", name.c_str()); +} + +void fprint_include_guard(FILE *f, bool begin) +{ + std::string name = "__HEADERGEN__" + toupper(g_soc_name) + "__" + toupper(g_soc_dev) + + "__H__"; + fprint_include_guard_ex(f, begin, name); +} + +struct limited_column_context_t +{ + limited_column_context_t(size_t nr_col = 80) + :m_nr_col(nr_col), m_prevent_wordcut(true) {} + void set_prefix(const std::string& prefix) { m_prefix = prefix; } + void add(const std::string& text) + { + for(size_t i = 0; i < text.size();) + { + size_t offset = 0; + if(m_cur_line.size() == 0) + m_cur_line = m_prefix; + size_t len = std::min(text.size() - i, m_nr_col - m_cur_line.size()); + // prevent word cut + if(m_prevent_wordcut && !isspace(text[i + len - 1]) && + i + len < text.size() && !isspace(text[i + len])) + { + size_t pos = text.find_last_of(" \t\n\v\r\f", i + len - 1); + if(pos == std::string::npos || pos < i) + len = 0; + else + len = pos - i + 1; + } + size_t pos = text.find('\n', i); + if(pos != std::string::npos && pos <= i + len) + { + offset = 1; + len = pos - i; + } + m_cur_line += text.substr(i, len); + // len == 0 means we need a new line + if(m_cur_line.size() == m_nr_col || len == 0) + { + m_lines.push_back(m_cur_line); + m_cur_line = ""; + } + i += len + offset; + } + } + + std::string to_string() + { + std::string str; + for(size_t i = 0; i < m_lines.size(); i++) + str += m_lines[i] + "\n"; + if(m_cur_line.size() != 0) + str += m_cur_line + "\n"; + return str; + } + + void print(FILE *f) + { + fprintf(f, "%s", to_string().c_str()); + } + + std::vector< std::string > m_lines; + std::string m_cur_line; + std::string m_prefix; + size_t m_nr_col; + bool m_prevent_wordcut; +}; + +struct define_align_context_t +{ + define_align_context_t():m_max_name(0) {} + void add(const std::string& name, const std::string& val) + { + m_lines.push_back(std::make_pair(name, val)); + m_max_name = std::max(m_max_name, name.size()); + } + + void add_raw(const std::string& line) + { + m_lines.push_back(std::make_pair("", line)); + } + + void print(FILE *f) + { + std::string define = "#define "; + size_t align = define.size() + m_max_name + 1; + align = ((align + 3) / 4) * 4; + + for(size_t i = 0; i < m_lines.size(); i++) + { + std::string name = m_lines[i].first; + // raw entry ? + if(name.size() == 0) + { + fprintf(f, "%s", m_lines[i].second.c_str()); + continue; + } + name.insert(name.end(), align - define.size() - name.size(), ' '); + fprintf(f, "%s%s%s\n", define.c_str(), name.c_str(), m_lines[i].second.c_str()); + } + } + + size_t m_max_name; + std::vector< std::pair< std::string, std::string > > m_lines; +}; + +limited_column_context_t print_description(const std::string& desc, const std::string& prefix) +{ + limited_column_context_t ctx; + if(desc.size() == 0) + return ctx; + ctx.set_prefix(prefix); + ctx.add(desc); + return ctx; +} + +void fprint_description(FILE *f, const std::string& desc, const std::string& prefix) +{ + limited_column_context_t ctx = print_description(desc, prefix); + ctx.print(f); +} + +void fprint_description(define_align_context_t& ctx, const std::string& desc, const std::string& prefix) +{ + limited_column_context_t ctx2 = print_description(desc, prefix); + ctx.add_raw(ctx2.to_string()); +} + +void gen_soc_field(define_align_context_t& ctx, bool multidev, bool multireg, const soc_reg_field_t& field) +{ + if(field.desc.size() != 0) + { + ctx.add_raw("/* Field: " + field.name + "\n"); + fprint_description(ctx, "Description: " + field.desc + " */\n", " * "); + } + + std::string prefix = g_soc_dev + "_" + g_soc_reg + "_" + g_soc_field; + ctx.add("BP_" + prefix, to_str(field.first_bit)); + ctx.add("BM_" + prefix, "0x" + to_hex(field.bitmask())); + + for(size_t i = 0; i < field.value.size(); i++) + ctx.add("BV_" + prefix + "__" + field.value[i].name, "0x" + to_hex(field.value[i].value)); + + ctx.add("BF_" + prefix + "(v)", "(((v) << " + to_str(field.first_bit) + ") & 0x" + to_hex(field.bitmask()) + ")"); + + if(field.value.size() > 0) + ctx.add("BF_" + prefix + "_V(v)", "((BV_" + prefix + "__##v" + " << " + to_str(field.first_bit) + ") & 0x" + to_hex(field.bitmask()) + ")"); +} + +void gen_soc_reg(FILE *f, bool multidev, const soc_reg_t& reg, char const *author, char const *hwprefix) +{ + bool multireg = reg.addr.size() > 1; + + static const char *suffix[] = {"", "_SET", "_CLR", "_TOG"}; + bool sct = !!(reg.flags & REG_HAS_SCT); + + // comment generator + fprintf(f, "/**\n"); + fprintf(f, " * Register: %s%s_%s\n", hwprefix, g_soc_dev.c_str(), g_soc_reg.c_str()); + fprintf(f, " * Address:"); + if(multireg && reg.formula.type == REG_FORMULA_STRING) + { + fprintf(f, " %s\n", reg.formula.string.c_str()); + } + else + { + for(size_t i = 0; i < reg.addr.size(); i++) + fprintf(f, " %#x", reg.addr[i].addr); + fprintf(f, "\n"); + } + fprintf(f, " * SCT: %s\n", sct ? "yes" : "no"); + if(reg.desc.size() != 0) + fprint_description(f, "Description: " + reg.desc, " * "); + fprintf(f, " */\n"); + + define_align_context_t ctx; + + if (multireg && reg.formula.type != REG_FORMULA_STRING) + { + for(int i=0; i < (int)reg.addr.size(); i++) + { + std::ostringstream name, value; + name << hwprefix << g_soc_dev << "_" << reg.addr[i].name; + if (multidev) + { + name << "(d)"; + } + value << "(*(volatile unsigned long *)(" << g_soc_dev_regs_base; + if (multidev) + { + value << "(d)"; + } + value << " + 0x" << std::hex << reg.addr[i].addr << "))"; + + ctx.add(name.str(), value.str()); + } + } + else + { + for(int i = 0; i < (sct ? 4 : 1); i++) + { + std::ostringstream name; + name << hwprefix << g_soc_dev << "_" << g_soc_reg << suffix[i]; + if(multidev || multireg) + { + name << "("; + if(multidev) + name << "d"; + if(multidev && multireg) + name << ","; + if(multireg) + name << "n"; + name << ")"; + } + std::ostringstream value; + value << "(*(volatile unsigned long *)(" << g_soc_dev_regs_base; + if(multidev) + value << "(d)"; + value << " + "; + if(multireg) + { + std::string formula = reg.formula.string.c_str(); + size_t pos = formula.find("n"); + while(pos != std::string::npos) + { + formula.replace(pos, 1, "(n)"); + pos = formula.find("n", pos + 2); + } + value << formula; + } + else + value << "0x" << std::hex << reg.addr[0].addr; + + if(sct) + value << " + 0x" << std::hex << (i * 4); + value << "))"; + + ctx.add(name.str(), value.str()); + } + } + + for(size_t i = 0; i < reg.field.size(); i++) + { + g_soc_field = reg.field[i].name; + gen_soc_field(ctx, multidev, multireg, reg.field[i]); + } + + ctx.print(f); + + fprintf(f, "\n"); +} + +void gen_soc_dev_header(const std::string& filename, const xml_ver_t& ver, const soc_dev_t& dev, + char const *author, char const *hwprefix) +{ + /* + printf("Generate headers for soc %s, dev %s: use file %s\n", g_soc_name.c_str(), + g_soc_dev.c_str(), filename.c_str()); + */ + FILE *f = fopen(filename.c_str(), "w"); + if(f == NULL) + { + printf("Cannot open %s for writing: %m\n", filename.c_str()); + return; + } + fprint_copyright(f, ver, author); + fprint_include_guard(f, true); + if(g_macro_filename.size() > 0) + fprintf(f, "#include \"%s\"\n", g_macro_filename.c_str()); + + /* print base */ + fprintf(f, "\n"); + g_soc_dev_regs_base = "REGS_" + g_soc_dev + "_BASE"; + fprintf(f, "#define %s", g_soc_dev_regs_base.c_str()); + + if(dev.addr.size() > 1) + fprintf(f, "(i)"); + fprintf(f, " ("); + + for(size_t i = 0; i < dev.addr.size() - 1; i++) + fprintf(f, "(i) == %d ? %#x : ", (int)i + 1, dev.addr[i].addr); + + fprintf(f, "%#x)\n", dev.addr[dev.addr.size() - 1].addr); + + fprintf(f, "\n"); + + /* print version */ + fprintf(f, "#define REGS_%s_VERSION \"%s\"\n\n", g_soc_dev.c_str(), dev.version.c_str()); + + for(size_t i = 0; i < dev.reg.size(); i++) + { + g_soc_reg = dev.reg[i].name; + gen_soc_reg(f, dev.addr.size() > 1, dev.reg[i], author, hwprefix); + } + + fprint_include_guard(f, false); + fclose(f); +} + +void gen_soc_headers(const std::string& prefix, const soc_t& soc, char const *author, char const *hwprefix) +{ + printf("Generate headers for soc %s: use directory %s\n", soc.name.c_str(), + prefix.c_str()); + mkdir(prefix.c_str(), 0770); + + for(size_t i = 0; i < soc.dev.size(); i++) + { + g_soc_dev = soc.dev[i].name; + xml_ver_t ver(soc.name, soc.dev[i].version); + gen_soc_dev_header(prefix + "/regs-" + tolower(g_soc_dev.c_str()) + ".h", ver, soc.dev[i], author, hwprefix); + } +} + +void gen_headers(const std::string& prefix, const std::vector< soc_t >& socs, char const *author, char const *hwprefix) +{ + for(size_t i = 0; i < socs.size(); i++) + { + g_soc_name = socs[i].name; + std::string dir = prefix; + if(g_gen_selector) + dir += "/" + socs[i].name; + gen_soc_headers(dir, socs[i], author, hwprefix); + } +} + +typedef std::map< std::string, std::vector< std::pair< size_t, size_t > > > general_dev_list_t; +general_dev_list_t build_general_dev_list(const std::vector< soc_t >& socs) +{ + general_dev_list_t map; + for(size_t i = 0; i < socs.size(); i++) + { + for(size_t j = 0; j < socs[i].dev.size(); j++) + map[tolower(socs[i].dev[j].name)].push_back(std::make_pair(i,j)); + } + return map; +} + +void gen_select_header(const std::string& filename, const std::string& dev, + const std::vector< std::string >& socs, const std::vector< xml_ver_t >& ver, char const *author) +{ + std::string guard = "__SELECT__" + toupper(dev) + "__H__"; + FILE *f = fopen(filename.c_str(), "w"); + if(f == NULL) + error("Cannot open file %s\n", filename.c_str()); + fprint_copyright(f, ver, author); + fprint_include_guard_ex(f, true, guard); + if(g_macro_filename.size() > 0) + fprintf(f, "#include \"%s\"\n", g_macro_filename.c_str()); + fprintf(f, "\n"); + + for(size_t i = 0; i < socs.size(); i++) + { + fprintf(f, "#define %s_INCLUDE \"%s/regs-%s.h\"\n", + toupper(socs[i]).c_str(), tolower(socs[i]).c_str(), + tolower(dev).c_str()); + } + fprintf(f, "\n#include \"regs-select.h\"\n\n"); + for(size_t i = 0; i < socs.size(); i++) + { + fprintf(f, "#undef %s_INCLUDE\n", toupper(socs[i]).c_str()); + } + fprintf(f, "\n"); + fprint_include_guard_ex(f, false, guard); + fclose(f); +} + +void gen_selectors(const std::string& prefix, const std::vector< soc_t >& socs, char const *author) +{ + printf("Generate select headers: use directory %s\n", prefix.c_str()); + general_dev_list_t map = build_general_dev_list(socs); + for(general_dev_list_t::iterator it = map.begin(); it != map.end(); ++it) + { + std::vector< xml_ver_t > ver; + std::vector< std::string > names; + for(size_t i = 0; i < it->second.size(); i++) + { + size_t soc_nr = it->second[i].first; + size_t dev_in_soc_nr = it->second[i].second; + ver.push_back(std::make_pair(socs[soc_nr].name, socs[soc_nr].dev[dev_in_soc_nr].version)); + names.push_back(socs[soc_nr].name); + } + gen_select_header(prefix + "/regs-" + it->first + ".h", it->first, names, ver, author); + } +} + +void gen_macro_list(FILE *f, const std::string& prefix, int count, int nr_digits, + const std::string& sep, int max_per_line = 1000, const std::string& align = "") +{ + for(int i = 1; i <= count;) + { + for(int j = i; j <= std::min(count, i + max_per_line - 1); j++) + { + fprintf(f, "%s%0*d", prefix.c_str(), nr_digits, j); + if(j < count) + fprintf(f, "%s", sep.c_str()); + } + i += max_per_line; + if(i <= count) + fprintf(f, "\\\n%s", align.c_str()); + } +} + +void gen_macro(const std::string& filename, bool variadic, char const *author, char const *hwprefix) +{ + printf("Generate %smacro header: use %s\n", variadic ? "": "non-variadic ", + filename.c_str()); + std::string guard = "__REGS__MACRO__H__"; + FILE *f = fopen(filename.c_str(), "w"); + if(f == NULL) + error("Cannot open file %s\n", filename.c_str()); + fprint_copyright(f, author); + fprint_include_guard_ex(f, true, guard); + fprintf(f, "\n"); + +#define REG_WRITE "REG_WRITE" + fprintf(f, "#ifndef %s\n", REG_WRITE); + fprintf(f, "#define %s(var,value) ((var) = (value))\n", REG_WRITE); + fprintf(f, "#endif /* %s */\n", REG_WRITE); + fprintf(f, "\n"); + +#define REG_READ "REG_READ" + fprintf(f, "#ifndef %s\n", REG_READ); + fprintf(f, "#define %s(var) (var)\n", REG_READ); + fprintf(f, "#endif /* %s */\n", REG_READ); + fprintf(f, "\n"); + + const int MAX_NARGS = 32; + + fprintf(f, "\ +#define BF_SET(reg, field) "REG_WRITE"(%1$s##reg##_SET, BM_##reg##_##field)\n\ +#define BF_CLR(reg, field) "REG_WRITE"(%1$s##reg##_CLR, BM_##reg##_##field)\n\ +#define BF_TOG(reg, field) "REG_WRITE"(%1$s##reg##_TOG, BM_##reg##_##field)\n\ +\n\ +#define BF_SETV(reg, field, v) "REG_WRITE"(%1$s##reg##_SET, BF_##reg##_##field(v))\n\ +#define BF_CLRV(reg, field, v) "REG_WRITE"(%1$s##reg##_CLR, BF_##reg##_##field(v))\n\ +#define BF_TOGV(reg, field, v) "REG_WRITE"(%1$s##reg##_TOG, BF_##reg##_##field(v))\n\ +\n\ +#define BF_RDX(val, reg, field) (("REG_READ"(val) & BM_##reg##_##field) >> BP_##reg##_##field)\n\ +#define BF_RD(reg, field) BF_RDX("REG_READ"(%1$s##reg), reg, field)\n\ +#define BF_WRX(val, reg, field, v) "REG_WRITE"(val, ("REG_READ"(val) & ~BM_##reg##_##field) | (((v) << BP_##reg##_##field) & BM_##reg##_##field))\n\ +#define BF_WR(reg, field, v) BF_WRX(%1$s##reg, reg, field, v)\n\ +#define BF_WR_V(reg, field, sy) BF_WR(reg, field, BV_##reg##_##field##__##sy)\n\ +#define BF_WR_VX(val, reg, field, sy) BF_WRX(val, reg, field, BV_##reg##_##field##__##sy)\n\ +\n\ +#define BF_SETn(reg, n, field) "REG_WRITE"(%1$s##reg##_SET(n), BM_##reg##_##field)\n\ +#define BF_CLRn(reg, n, field) "REG_WRITE"(%1$s##reg##_CLR(n), BM_##reg##_##field)\n\ +#define BF_TOGn(reg, n, field) "REG_WRITE"(%1$s##reg##_TOG(n), BM_##reg##_##field)\n\ +\n\ +#define BF_SETVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_SET(n), BF_##reg##_##field(v))\n\ +#define BF_CLRVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_CLR(n), BF_##reg##_##field(v))\n\ +#define BF_TOGVn(reg, n, field, v) "REG_WRITE"(%1$s##reg##_TOG(n), BF_##reg##_##field(v))\n\ +\n\ +#define BF_RDn(reg, n, field) BF_RDX(%1$s##reg(n), reg, field)\n\ +#define BF_WRn(reg, n, field, v) BF_WRX(%1$s##reg(n), reg, field, v)\n\ +#define BF_WRn_V(reg, n, field, sy) BF_WRn(reg, n, field, BV_##reg##_##field##__##sy)\n\ +\n", hwprefix); + + for(int nargs = 1; nargs <= MAX_NARGS; nargs++) + { + fprintf(f, "#define BM_OR%d(reg, ", nargs); + gen_macro_list(f, "f", nargs, 2, ", ", 10, " "); + fprintf(f, ") \\\n ("); + gen_macro_list(f, "BM_##reg##_##f", nargs, 2, " | ", 4, " "); + fprintf(f, ")\n"); + } + fprintf(f, "\n"); + + for(int nargs = 1; nargs <= MAX_NARGS; nargs++) + { + fprintf(f, "#define BF_OR%d(reg, ", nargs); + gen_macro_list(f, "f", nargs, 2, ", ", 10, " "); + fprintf(f, ") \\\n ("); + gen_macro_list(f, "BF_##reg##_##f", nargs, 2, " | ", 4, " "); + fprintf(f, ")\n"); + } + fprintf(f, "\n"); + + if(variadic) + { + fprintf(f, "#define REG_NARG(...) REG_NARGS_(__VA_ARGS__"); + for(int i = MAX_NARGS; i >= 1; i--) + fprintf(f, ", %d", i); + fprintf(f, ")\n"); + fprintf(f, "#define REG_NARGS_("); + gen_macro_list(f, "_", MAX_NARGS, 1, ", "); + fprintf(f, ", N, ...) N\n\n"); + + fprintf(f, "#define REG_VARIADIC(macro, reg, ...) REG_VARIADIC_(macro, REG_NARG(__VA_ARGS__), reg, __VA_ARGS__)\n"); + fprintf(f, "#define REG_VARIADIC_(macro, cnt, reg, ...) REG_VARIADIC__(macro, cnt, reg, __VA_ARGS__)\n"); + fprintf(f, "#define REG_VARIADIC__(macro, cnt, reg, ...) REG_VARIADIC___(macro##cnt, reg, __VA_ARGS__)\n"); + fprintf(f, "#define REG_VARIADIC___(macro, reg, ...) macro(reg, __VA_ARGS__)\n\n"); + + fprintf(f, "#define BM_OR(reg, ...) REG_VARIADIC(BM_OR, reg, __VA_ARGS__)\n"); + fprintf(f, "#define BF_OR(reg, ...) REG_VARIADIC(BF_OR, reg, __VA_ARGS__)\n"); + } + + fprint_include_guard_ex(f, false, guard); + fclose(f); +} + +void usage() +{ + printf("usage: headergen [options] \n"); + printf("options:\n"); + printf(" -?/--help Dispaly this help\n"); + printf(" -s/--selector Always produce selector files\n"); + printf(" -m/--no-macro Do not generate a macro file with helpers\n"); + printf(" -i/--no-include Do not include the macro file in the headers\n"); + printf(" -v/--no-variadic Do not generate variadic macros\n"); + printf(" -p/--reg-prefix \"prefix\" Prefix register names\n"); + printf(" -a/--author \"Author\" Set author name in preamble\n"); + printf("\n"); + printf("Default option is to generate a macro file with variadic macros.\n"); + printf("Default option is to include the macro file in the headers.\n"); + printf("Default option is to generate selector files only for two or more socs.\n"); + printf("Default option is to create one subdirectory per soc, except if no\n"); + printf("selector files are needed. The subdirectories will be created if necessary.\n"); + printf("Default option is to not prefix register names\n"); + printf("Default option is \"Unknown Author\" for author in preamble\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + bool force_selector = false; + bool no_variadic = false; + bool no_macro = false; + bool no_include = false; + char const *author = "Unknown Author"; + char const *hwprefix = ""; + + if(argc <= 1) + usage(); + + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"selector", no_argument, 0, 's'}, + {"no-macro", no_argument, 0, 'm'}, + {"no-include", no_argument, 0, 'i'}, + {"no-variadic", no_argument, 0, 'v'}, + {"reg-prefix", required_argument, 0, 'p'}, + {"author", required_argument, 0, 'a'}, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?smivp:a:", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case '?': + usage(); + break; + case 's': + force_selector = true; + break; + case 'm': + no_macro = true; + break; + case 'i': + no_include = true; + break; + case 'v': + no_variadic = true; + break; + case 'a' : + author = optarg; + break; + case 'p' : + hwprefix = optarg; + break; + default: + abort(); + } + } + + std::vector< soc_t > socs; + for(int i = optind; i < argc - 1; i++) + { + soc_t s; + if(!parse_xml(argv[i], s)) + { + printf("Cannot parse %s\n", argv[i]); + return 1; + } + socs.push_back(s); + } + + g_gen_selector = force_selector || socs.size() > 1; + + if(!no_macro) + { + g_macro_filename = std::string(argv[argc - 1]) + "/regs-macro.h"; + gen_macro(g_macro_filename, !no_variadic, author, hwprefix); + g_macro_filename = "regs-macro.h"; + if(no_include) + g_macro_filename.clear(); + } + if(g_gen_selector) + { + gen_selectors(argv[argc - 1], socs, author); + g_macro_filename.clear(); + } + gen_headers(argv[argc - 1], socs, author, hwprefix); + return 0; +} diff --git a/utils/regtools/include/soc_desc.hpp b/utils/regtools/include/soc_desc.hpp new file mode 100644 index 0000000000..66f2e3b6e1 --- /dev/null +++ b/utils/regtools/include/soc_desc.hpp @@ -0,0 +1,384 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 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. + * + ****************************************************************************/ +#ifndef __SOC_DESC__ +#define __SOC_DESC__ + +#include +#include +#include +#include +#include + +namespace soc_desc +{ + +const size_t MAJOR_VERSION = 2; +const size_t MINOR_VERSION = 0; +const size_t REVISION_VERSION = 0; + +/** Typedef for SoC types: word, address and flags */ +typedef uint32_t soc_addr_t; +typedef uint32_t soc_word_t; +typedef int soc_id_t; + +/** Error class */ +class error_t +{ +public: + enum level_t + { + INFO, + WARNING, + FATAL + }; + error_t(level_t lvl, const std::string& loc, const std::string& msg) + :m_level(lvl), m_loc(loc), m_msg(msg) {} + level_t level() const { return m_level; } + std::string location() const { return m_loc; } + std::string message() const { return m_msg; } +protected: + level_t m_level; + std::string m_loc, m_msg; +}; + +/** Error context to log errors */ +class error_context_t +{ +public: + void add(const error_t& err) { m_list.push_back(err); } + size_t count() const { return m_list.size(); } + error_t get(size_t i) const { return m_list[i]; } +protected: + std::vector< error_t > m_list; +}; + +/** + * Bare representation of the format + */ + +/** Enumerated value (aka named value), represents a special value for a field */ +struct enum_t +{ + soc_id_t id; /** ID (must be unique among field enums) */ + std::string name; /** Name (must be unique among field enums) */ + std::string desc; /** Optional description of the meaning of this value */ + soc_word_t value; /** Value of the field */ +}; + +/** Register field information */ +struct field_t +{ + soc_id_t id; /** ID (must be unique among register fields) */ + std::string name; /** Name (must be unique among register fields) */ + std::string desc; /** Optional description of the field */ + size_t pos; /** Position of the least significant bit */ + size_t width; /** Width of the field in bits */ + std::vector< enum_t > enum_; /** List of special values */ + + /** Returns the bit mask of the field within the register */ + soc_word_t bitmask() const + { + // WARNING beware of the case where width is 32 + if(width == 32) + return 0xffffffff; + else + return ((1 << width) - 1) << pos; + } + + /** Extract field value from register value */ + soc_word_t extract(soc_word_t reg_val) const + { + return (reg_val & bitmask()) >> pos; + } + + /** Replace the field value in a register value */ + soc_word_t replace(soc_word_t reg_val, soc_word_t field_val) const + { + return (reg_val & ~bitmask()) | ((field_val << pos) & bitmask()); + } + + /** Return field value index, or -1 if none */ + int find_value(soc_word_t v) const + { + for(size_t i = 0; i < enum_.size(); i++) + if(enum_[i].value == v) + return i; + return -1; + } +}; + +/** Register information */ +struct register_t +{ + size_t width; /** Size in bits */ + std::vector< field_t > field; /** List of fields */ +}; + +/** Node address range information */ +struct range_t +{ + enum type_t + { + STRIDE, /** Addresses are given by a base address and a stride */ + FORMULA /** Addresses are given by a formula */ + }; + + type_t type; /** Range type */ + size_t first; /** First index in the range */ + size_t count; /** Number of indexes in the range */ + soc_word_t base; /** Base address (for STRIDE) */ + soc_word_t stride; /** Stride value (for STRIDE) */ + std::string formula; /** Formula (for FORMULA) */ + std::string variable; /** Formula variable name (for FORMULA) */ +}; + +/** Node instance information */ +struct instance_t +{ + enum type_t + { + SINGLE, /** There is a single instance at a specified address */ + RANGE /** There are multiple addresses forming a range */ + }; + + soc_id_t id; /** ID (must be unique among node instances) */ + std::string name; /** Name (must be unique among node instances) */ + std::string title; /** Optional instance human name */ + std::string desc; /** Optional description of the instance */ + type_t type; /** Instance type */ + soc_word_t addr; /** Address (for SINGLE) */ + range_t range; /** Range (for RANGE) */ +}; + +/** Node information */ +struct node_t +{ + soc_id_t id; /** ID (must be unique among nodes) */ + std::string name; /** Name (must be unique for the among nodes) */ + std::string title; /** Optional node human name */ + std::string desc; /** Optional description of the node */ + std::vector< register_t> register_; /** Optional register */ + std::vector< instance_t> instance; /** List of instances */ + std::vector< node_t > node; /** List of sub-nodes */ +}; + +/** System-on-chip information */ +struct soc_t +{ + std::string name; /** Codename of the SoC */ + std::string title; /** Human name of the SoC */ + std::string desc; /** Optional description of the SoC */ + std::string isa; /** Instruction Set Assembly */ + std::string version; /** Description version */ + std::vector< std::string > author; /** List of authors of the description */ + std::vector< node_t > node; /** List of nodes */ +}; + +/** Parse a SoC description from a XML file, put it into . */ +bool parse_xml(const std::string& filename, soc_t& soc, error_context_t& error_ctx); +/** Write a SoC description to a XML file, overwriting it. A file can contain + * multiple Soc descriptions */ +bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& error_ctx); +/** Formula parser: try to parse and evaluate a formula with some variables */ +bool evaluate_formula(const std::string& formula, + const std::map< std::string, soc_word_t>& var, soc_word_t& result, + const std::string& loc, error_context_t& error_ctx); + +/** + * Convenience API to manipulate the format + * + * The idea is that *_ref_t objects are stable pointers: they stay valid even + * when the underlying soc changes. In particular: + * - modifying any structure data (except id fields) preserves all references + * - removing a structure invalidates all references pointing to this structure + * and its children + * - adding any structure preserves all references + * These references can be used to get pointers to the actual data + * of the representation when it needs to be read or write. + */ + +class soc_ref_t; +class node_ref_t; +class register_ref_t; +class field_ref_t; +class node_inst_t; + +/** SoC reference */ +class soc_ref_t +{ + soc_t *m_soc; /* pointer to the soc */ +public: + /** Builds an invalid reference */ + soc_ref_t(); + /** Builds a reference to a soc */ + soc_ref_t(soc_t *soc); + /** Checks whether this reference is valid */ + bool valid() const; + /** Returns a pointer to the soc */ + soc_t *get() const; + /** Returns a reference to the root node */ + node_ref_t root() const; + /** Returns a reference to the root node instance */ + node_inst_t root_inst() const; + /** Compare this reference to another */ + bool operator==(const soc_ref_t& r) const; + inline bool operator!=(const soc_ref_t& r) const { return !operator==(r); } +}; + +/** SoC node reference + * NOTE: the root soc node is presented as a node with empty path */ +class node_ref_t +{ + friend class soc_ref_t; + friend class node_inst_t; + soc_ref_t m_soc; /* reference to the soc */ + std::vector< soc_id_t > m_path; /* path from the root */ + + node_ref_t(soc_ref_t soc); + node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path); +public: + /** Builds an invalid reference */ + node_ref_t(); + /** Check whether this reference is valid */ + bool valid() const; + /** Check whether this reference is the root node */ + bool is_root() const; + /** Returns a pointer to the node, or 0 if invalid or root */ + node_t *get() const; + /** Returns a reference to the soc */ + soc_ref_t soc() const; + /** Returns a reference to the parent node */ + node_ref_t parent() const; + /** Returns a reference to the register (which may be on a parent node) */ + register_ref_t reg() const; + /** Returns a list of references to the sub-nodes */ + std::vector< node_ref_t > children() const; + /** Returns a reference to a specific child */ + node_ref_t child(const std::string& name) const; + /** Returns the path of the node, as the list of node names from the root */ + std::vector< std::string > path() const; + /** Returns the name of the node */ + std::string name() const; + /** Compare this reference to another */ + bool operator==(const node_ref_t& r) const; + inline bool operator!=(const node_ref_t& r) const { return !operator==(r); } +}; + +/** SoC register reference */ +class register_ref_t +{ + friend class node_ref_t; + node_ref_t m_node; /* reference to the node owning the register */ + + register_ref_t(node_ref_t node); +public: + /** Builds an invalid reference */ + register_ref_t(); + /** Check whether this reference is valid/exists */ + bool valid() const; + /** Returns a pointer to the register, or 0 */ + register_t *get() const; + /** Returns a reference to the node containing the register */ + node_ref_t node() const; + /** Returns a list of references to the fields of the register */ + std::vector< field_ref_t > fields() const; + /** Returns a reference to a particular field */ + field_ref_t field(const std::string& name) const; + /** Compare this reference to another */ + bool operator==(const register_ref_t& r) const; + inline bool operator!=(const register_ref_t& r) const { return !operator==(r); } +}; + +/** SoC register field reference */ +class field_ref_t +{ + friend class register_ref_t; + register_ref_t m_reg; /* reference to the register */ + soc_id_t m_id; /* field name */ + + field_ref_t(register_ref_t reg, soc_id_t id); +public: + /** Builds an invalid reference */ + field_ref_t(); + /** Check whether this reference is valid/exists */ + bool valid() const; + /** Returns a pointer to the field, or 0 */ + field_t *get() const; + /** Returns a reference to the register containing the field */ + register_ref_t reg() const; + /** Compare this reference to another */ + bool operator==(const field_ref_t& r) const; + inline bool operator!=(const field_ref_t& r) const { return !operator==(r); } +}; + +/** SoC node instance + * NOTE: the root soc node is presented as a node with a single instance at 0 */ +class node_inst_t +{ + friend class node_ref_t; + friend class soc_ref_t; + node_ref_t m_node; /* reference to the node */ + std::vector< soc_id_t > m_id_path; /* list of instance IDs */ + std::vector< size_t > m_index_path; /* list of instance indexes */ + + node_inst_t(soc_ref_t soc); + node_inst_t(node_ref_t soc, const std::vector< soc_id_t >& path, + const std::vector< size_t >& indexes); +public: + /** Builds an invalid reference */ + node_inst_t(); + /** Check whether this instance is valid/exists */ + bool valid() const; + /** Returns a reference to the soc */ + soc_ref_t soc() const; + /** Returns a reference to the node */ + node_ref_t node() const; + /** Check whether this reference is the root node instance */ + bool is_root() const; + /** Returns a reference to the parent instance */ + node_inst_t parent() const; + /** Returns a pointer to the instance of the node, or 0 */ + instance_t *get() const; + /** Returns the address of this instance */ + soc_addr_t addr() const; + /** Returns an instance to a child of this node's instance. If the subnode + * instance is a range, the returned reference is invalid */ + node_inst_t child(const std::string& name) const; + /** Returns an instance to a child of this node's instance with a range index. + * If the subnode is not not a range or if the index is out of bounds, + * the returned reference is invalid */ + node_inst_t child(const std::string& name, size_t index) const; + /** Returns a list of all instances of subnodes of this node's instance */ + std::vector< node_inst_t > children() const; + /** Returns the name of the instance */ + std::string name() const; + /** Checks whether this instance is indexed */ + bool is_indexed() const; + /** Returns the index of the instance */ + size_t index() const; + /** Compare this reference to another */ + bool operator==(const node_inst_t& r) const; + inline bool operator!=(const node_inst_t& r) const { return !operator==(r); } +}; + +} // soc_desc + +#endif /* __SOC_DESC__ */ diff --git a/utils/regtools/include/soc_desc_v1.hpp b/utils/regtools/include/soc_desc_v1.hpp new file mode 100644 index 0000000000..33368e88d4 --- /dev/null +++ b/utils/regtools/include/soc_desc_v1.hpp @@ -0,0 +1,230 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 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. + * + ****************************************************************************/ +#ifndef __SOC_DESC_V1__ +#define __SOC_DESC_V1__ + +#include +#include +#include +#include +#include + +/** + * These data structures represent the SoC register in a convenient way. + * The basic structure is the following: + * - each SoC has several devices + * - each device has a generic name, a list of {name,address} and several registers + * - each register has a generic name, a list of {name,address}, flags, + * several fields + * - each field has a name, a first and last bit position, can apply either + * to all addresses of a register or be specific to one only and has several values + * - each field value has a name and a value + * + * All addresses, values and names are relative to the parents. For example a field + * value BV_LCDIF_CTRL_WORD_LENGTH_18_BIT is represented has: + * - device LCDIF, register CTRL, field WORD_LENGTH, value 16_BIT + * The address of CTRL is related to the address of LCDIF, the value of 16_BIT + * ignores the position of the WORD_LENGTH field in the register. + */ + +namespace soc_desc_v1 +{ + +const size_t MAJOR_VERSION = 1; +const size_t MINOR_VERSION = 4; +const size_t REVISION_VERSION = 1; + +/** + * Typedef for SoC types: word, address and flags */ +typedef uint32_t soc_addr_t; +typedef uint32_t soc_word_t; +typedef uint32_t soc_reg_flags_t; + +/** SoC error gravity level */ +enum soc_error_level_t +{ + SOC_ERROR_WARNING, + SOC_ERROR_FATAL, +}; + +/** SoC description error */ +struct soc_error_t +{ + soc_error_level_t level; /// level (warning, fatal, ...) + std::string location; /// human description of the location + std::string message; /// message +}; + +/** SoC register generic formula */ +enum soc_reg_formula_type_t +{ + REG_FORMULA_NONE, /// register has no generic formula + REG_FORMULA_STRING, /// register has a generic formula represented by a string +}; + +/** . values */ +const soc_reg_flags_t REG_HAS_SCT = 1 << 0; /// register SCT variants + +/** SoC register field named value */ +struct soc_reg_field_value_t +{ + std::string name; /// name of the value + soc_word_t value; /// numeric value + std::string desc; /// human description + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC register field */ +struct soc_reg_field_t +{ + std::string name; /// name of the field + std::string desc; /// human description + unsigned first_bit, last_bit; /// bit range of the field + + soc_reg_field_t():first_bit(0), last_bit(31) {} + + /** Return field bitmask in register */ + soc_word_t bitmask() const + { + // WARNING beware of the case where first_bit=0 and last_bit=31 + if(first_bit == 0 && last_bit == 31) + return 0xffffffff; + else + return ((1 << (last_bit - first_bit + 1)) - 1) << first_bit; + } + + /** Extract field value from register value */ + soc_word_t extract(soc_word_t reg_val) const + { + return (reg_val & bitmask()) >> first_bit; + } + + /** Replace the field value in a register value */ + soc_word_t replace(soc_word_t reg_val, soc_word_t field_val) const + { + return (reg_val & ~bitmask()) | ((field_val << first_bit) & bitmask()); + } + + bool is_reserved() const + { + return name.substr(0, 4) == "RSVD" || name.substr(0, 5) == "RSRVD"; + } + + /** Return field value index, or -1 if none */ + int find_value(soc_word_t v) const + { + for(size_t i = 0; i < value.size(); i++) + if(value[i].value == v) + return i; + return -1; + } + + std::vector< soc_reg_field_value_t > value; + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC register address */ +struct soc_reg_addr_t +{ + std::string name; /// actual register name + soc_addr_t addr; /// actual register address (relative to device) + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC register formula */ +struct soc_reg_formula_t +{ + enum soc_reg_formula_type_t type; + std::string string; /// for STRING + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC register */ +struct soc_reg_t +{ + std::string name; /// generic name (for multi registers) or actual name + std::string desc; /// human description + std::vector< soc_reg_addr_t > addr; /// instances of the registers + soc_reg_formula_t formula; /// formula for the instance addresses + soc_reg_flags_t flags; /// ORed value + + std::vector< soc_reg_field_t > field; + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** Soc device address */ +struct soc_dev_addr_t +{ + std::string name; /// actual device name + soc_addr_t addr; + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC device */ +struct soc_dev_t +{ + std::string name; /// generic name (of multi devices) or actual name + std::string long_name; /// human friendly name + std::string desc; /// human description + std::string version; /// description version + std::vector< soc_dev_addr_t > addr; + + std::vector< soc_reg_t > reg; + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** SoC */ +struct soc_t +{ + std::string name; /// codename (rockbox) + std::string desc; /// SoC name + + std::vector< soc_dev_t > dev; + + std::vector< soc_error_t > errors(bool recursive); +}; + +/** Parse a SoC description from a XML file, append it to . */ +bool parse_xml(const std::string& filename, soc_t& soc); +/** Write a SoC description to a XML file, overwriting it. A file can contain + * multiple Soc descriptions */ +bool produce_xml(const std::string& filename, const soc_t& soc); +/** Normalise a soc description by reordering elemnts so that: + * - devices are sorted by first name + * - registers are sorted by first address + * - fields are sorted by last bit + * - values are sorted by value */ +void normalize(soc_t& soc); +/** Formula parser: try to parse and evaluate a formula with some variables */ +bool evaluate_formula(const std::string& formula, + const std::map< std::string, soc_word_t>& var, soc_word_t& result, + std::string& error); + +} // soc_desc_v1 + +#endif /* __SOC_DESC_V1__ */ \ No newline at end of file diff --git a/utils/regtools/lib/Makefile b/utils/regtools/lib/Makefile index 08021ef941..d7d6c1b07a 100644 --- a/utils/regtools/lib/Makefile +++ b/utils/regtools/lib/Makefile @@ -1,18 +1,15 @@ CC?=gcc CXX?=g++ AR?=ar -CFLAGS=-Wall -O2 `xml2-config --cflags` -std=c99 -g -fPIC -CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC +INCLUDE=../include/ +CXXFLAGS=-Wall -O2 `xml2-config --cflags` -g -fPIC -I$(INCLUDE) LIB=libsocdesc.a SRC=$(wildcard *.c) SRCXX=$(wildcard *.cpp) -OBJ=$(SRC:.c=.o) $(SRCXX:.cpp=.o) +OBJ=$(SRCXX:.cpp=.o) all: $(LIB) $(EXEC) -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - %.o: %.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/utils/regtools/lib/formula.cpp b/utils/regtools/lib/formula.cpp new file mode 100644 index 0000000000..1a6b16c773 --- /dev/null +++ b/utils/regtools/lib/formula.cpp @@ -0,0 +1,214 @@ +#include "soc_desc.hpp" +#include +#include + +using namespace soc_desc; + +namespace soc_desc +{ + +namespace +{ + +struct formula_evaluator +{ + std::string formula; + size_t pos; + error_context_t& ctx; + std::string m_loc; + + bool err(const char *fmt, ...) + { + char buffer[256]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer,sizeof(buffer), fmt, args); + va_end(args); + ctx.add(error_t(error_t::FATAL, m_loc, buffer)); + return false; + } + + formula_evaluator(const std::string& s, error_context_t& ctx):pos(0),ctx(ctx) + { + for(size_t i = 0; i < s.size(); i++) + if(!isspace(s[i])) + formula.push_back(s[i]); + } + + void set_location(const std::string& loc) + { + m_loc = loc; + } + + void adv() + { + pos++; + } + + char cur() + { + return end() ? 0 : formula[pos]; + } + + bool end() + { + return pos >= formula.size(); + } + + bool parse_digit(char c, int basis, soc_word_t& res) + { + c = tolower(c); + if(isdigit(c)) + { + res = c - '0'; + return true; + } + if(basis == 16 && isxdigit(c)) + { + res = c + 10 - 'a'; + return true; + } + return false; + } + + bool parse_signed(soc_word_t& res) + { + char op = cur(); + if(op == '+' || op == '-') + { + adv(); + if(!parse_signed(res)) + return false; + if(op == '-') + res *= -1; + return true; + } + else if(op == '(') + { + adv(); + if(!parse_expression(res)) + return false; + if(cur() != ')') + return err("expected ')', got '%c'", cur()); + adv(); + return true; + } + else if(isdigit(op)) + { + res = op - '0'; + adv(); + int basis = 10; + if(op == '0' && cur() == 'x') + { + basis = 16; + adv(); + } + soc_word_t digit = 0; + while(parse_digit(cur(), basis, digit)) + { + res = res * basis + digit; + adv(); + } + return true; + } + else if(isalpha(op) || op == '_') + { + std::string name; + while(isalnum(cur()) || cur() == '_') + { + name.push_back(cur()); + adv(); + } + return get_variable(name, res); + } + else + return err("express signed expression, got '%c'", op); + } + + bool parse_term(soc_word_t& res) + { + if(!parse_signed(res)) + return false; + while(cur() == '*' || cur() == '/' || cur() == '%') + { + char op = cur(); + adv(); + soc_word_t tmp; + if(!parse_signed(tmp)) + return false; + if(op == '*') + res *= tmp; + else if(tmp != 0) + res = op == '/' ? res / tmp : res % tmp; + else + return err("division by 0"); + } + return true; + } + + bool parse_expression(soc_word_t& res) + { + if(!parse_term(res)) + return false; + while(!end() && (cur() == '+' || cur() == '-')) + { + char op = cur(); + adv(); + soc_word_t tmp; + if(!parse_term(tmp)) + return false; + if(op == '+') + res += tmp; + else + res -= tmp; + } + return true; + } + + bool parse(soc_word_t& res) + { + bool ok = parse_expression(res); + if(ok && !end()) + err("unexpected character '%c'", cur()); + return ok && end(); + } + + virtual bool get_variable(std::string name, soc_word_t& res) + { + return err("unknown variable '%s'", name.c_str()); + } +}; + +struct my_evaluator : public formula_evaluator +{ + const std::map< std::string, soc_word_t>& var; + + my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var, + error_context_t& ctx) + :formula_evaluator(formula, ctx), var(_var) {} + + virtual bool get_variable(std::string name, soc_word_t& res) + { + std::map< std::string, soc_word_t>::const_iterator it = var.find(name); + if(it == var.end()) + return formula_evaluator::get_variable(name, res); + else + { + res = it->second; + return true; + } + } +}; + +} + +bool evaluate_formula(const std::string& formula, + const std::map< std::string, soc_word_t>& var, soc_word_t& result, + const std::string& loc, error_context_t& error) +{ + my_evaluator e(formula, var, error); + e.set_location(loc); + return e.parse(result); +} + +} // soc_desc diff --git a/utils/regtools/lib/soc_desc.cpp b/utils/regtools/lib/soc_desc.cpp index 90e4e0eb81..4e0e46f00e 100644 --- a/utils/regtools/lib/soc_desc.cpp +++ b/utils/regtools/lib/soc_desc.cpp @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2012 by Amaury Pouly + * Copyright (C) 2014 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 @@ -27,261 +27,429 @@ #include #include #include +#include +#include + +namespace soc_desc +{ + +/** + * Parser + */ #define XML_CHAR_TO_CHAR(s) ((const char *)(s)) #define BEGIN_ATTR_MATCH(attr) \ for(xmlAttr *a = attr; a; a = a->next) { -#define MATCH_X_ATTR(attr_name, hook, ...) \ +#define MATCH_UNIQUE_ATTR(attr_name, val, has, parse_fn, ctx) \ if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ - std::string s; \ - if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \ - return false; \ + if(has) \ + return parse_not_unique_attr_error(a, ctx); \ + has = true; \ + xmlChar *str = NULL; \ + if(!parse_text_attr_internal(a, str, ctx) || !parse_fn(a, val, str, ctx)) \ + ret = false; \ } -#define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \ - if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ - std::string s; \ - if(parse_text_attr(a, s)) \ - hook(s, __VA_ARGS__); \ - } - -#define SOFT_MATCH_SCT_ATTR(attr_name, var) \ - SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var) - -#define MATCH_TEXT_ATTR(attr_name, var) \ - MATCH_X_ATTR(attr_name, validate_string_hook, var) - -#define MATCH_UINT32_ATTR(attr_name, var) \ - MATCH_X_ATTR(attr_name, validate_uint32_hook, var) - -#define MATCH_BITRANGE_ATTR(attr_name, first, last) \ - MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last) - #define END_ATTR_MATCH() \ } #define BEGIN_NODE_MATCH(node) \ for(xmlNode *sub = node; sub; sub = sub->next) { -#define MATCH_ELEM_NODE(node_name, array, parse_fn) \ +#define MATCH_ELEM_NODE(node_name, array, parse_fn, ctx) \ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ array.resize(array.size() + 1); \ - if(!parse_fn(sub, array.back())) \ - return false; \ + if(!parse_fn(sub, array.back(), ctx)) \ + ret = false; \ + array.back().id = array.size(); \ } -#define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \ +#define MATCH_TEXT_NODE(node_name, array, parse_fn, ctx) \ if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ + if(!is_real_text_node(sub)) \ + return parse_not_text_error(sub, ctx); \ + xmlChar *content = xmlNodeGetContent(sub); \ array.resize(array.size() + 1); \ - if(!parse_fn(sub, array.back())) \ - array.pop_back(); \ + ret = ret && parse_fn(sub, array.back(), content, ctx); \ + xmlFree(content); \ + } + +#define MATCH_UNIQUE_ELEM_NODE(node_name, val, has, parse_fn, ctx) \ + if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ + if(has) \ + return parse_not_unique_error(sub, ctx); \ + has = true; \ + if(!parse_fn(sub, val, ctx)) \ + ret = false; \ + } + +#define MATCH_UNIQUE_TEXT_NODE(node_name, val, has, parse_fn, ctx) \ + if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ + if(has) \ + return parse_not_unique_error(sub, ctx); \ + if(!is_real_text_node(sub)) \ + return parse_not_text_error(sub, ctx); \ + has = true; \ + xmlChar *content = xmlNodeGetContent(sub); \ + ret = ret && parse_fn(sub, val, content, ctx); \ + xmlFree(content); \ } #define END_NODE_MATCH() \ } +#define CHECK_HAS(node, node_name, has, ctx) \ + if(!has) \ + ret = ret && parse_missing_error(node, node_name, ctx); + +#define CHECK_HAS_ATTR(node, attr_name, has, ctx) \ + if(!has) \ + ret = ret && parse_missing_attr_error(node, attr_name, ctx); + namespace { -bool validate_string_hook(const std::string& str, std::string& s) +bool is_real_text_node(xmlNode *node) { - s = str; + for(xmlNode *sub = node->children; sub; sub = sub->next) + if(sub->type != XML_TEXT_NODE && sub->type != XML_ENTITY_REF_NODE) + return false; return true; } -bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags) +std::string xml_loc(xmlNode *node) { - if(str == "yes") flags |= REG_HAS_SCT; - else if(str != "no") return false; - return true; + std::ostringstream oss; + oss << "line " << node->line; + return oss.str(); } -bool validate_unsigned_long_hook(const std::string& str, unsigned long& s) +std::string xml_loc(xmlAttr *attr) { - char *end; - s = strtoul(str.c_str(), &end, 0); - return *end == 0; + return xml_loc(attr->parent); } -bool validate_uint32_hook(const std::string& str, uint32_t& s) +template +bool add_error(error_context_t& ctx, error_t::level_t lvl, T *node, + const std::string& msg) { - unsigned long u; - if(!validate_unsigned_long_hook(str, u)) return false; -#if ULONG_MAX > 0xffffffff - if(u > 0xffffffff) return false; -#endif - s = u; - return true; + ctx.add(error_t(lvl, xml_loc(node), msg)); + return false; } -bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last) +template +bool add_fatal(error_context_t& ctx, T *node, const std::string& msg) { - unsigned long a, b; - size_t sep = str.find(':'); - if(sep == std::string::npos) return false; - if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false; - if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false; - if(a > 31 || b > 31 || a < b) return false; - first = b; - last = a; - return true; + return add_error(ctx, error_t::FATAL, node, msg); } -bool parse_text_attr(xmlAttr *attr, std::string& s) +template +bool add_warning(error_context_t& ctx, T *node, const std::string& msg) { - if(attr->children != attr->last) - return false; - if(attr->children->type != XML_TEXT_NODE) - return false; - s = XML_CHAR_TO_CHAR(attr->children->content); - return true; + return add_error(ctx, error_t::WARNING, node, msg); } -bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value) +bool parse_wrong_version_error(xmlNode *node, error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", value.name) - MATCH_UINT32_ATTR("value", value.value) - MATCH_TEXT_ATTR("desc", value.desc) - END_ATTR_MATCH() + std::ostringstream oss; + oss << "unknown version, only version " << MAJOR_VERSION << " is supported"; + return add_fatal(ctx, node, oss.str()); +} - return true; +bool parse_not_unique_error(xmlNode *node, error_context_t& ctx) +{ + std::ostringstream oss; + oss << "there must be a unique <" << XML_CHAR_TO_CHAR(node->name) << "> element"; + if(node->parent->name) + oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; + else + oss << " at root level"; + return add_fatal(ctx, node, oss.str()); } -bool parse_field_elem(xmlNode *node, soc_reg_field_t& field) +bool parse_not_unique_attr_error(xmlAttr *attr, error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", field.name) - MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit) - MATCH_TEXT_ATTR("desc", field.desc) - END_ATTR_MATCH() + std::ostringstream oss; + oss << "there must be a unique " << XML_CHAR_TO_CHAR(attr->name) << " attribute"; + oss << " in <" << XML_CHAR_TO_CHAR(attr->parent->name) << ">"; + return add_fatal(ctx, attr, oss.str()); +} - BEGIN_NODE_MATCH(node->children) - SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem) - END_NODE_MATCH() +bool parse_missing_error(xmlNode *node, const char *name, error_context_t& ctx) +{ + std::ostringstream oss; + oss << "missing <" << name << "> element"; + if(node->parent->name) + oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; + else + oss << " at root level"; + return add_fatal(ctx, node, oss.str()); +} - return true; +bool parse_missing_attr_error(xmlNode *node, const char *name, error_context_t& ctx) +{ + std::ostringstream oss; + oss << "missing " << name << " attribute"; + oss << " in <" << XML_CHAR_TO_CHAR(node->name) << ">"; + return add_fatal(ctx, node, oss.str()); } -bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr) +bool parse_conflict_error(xmlNode *node, const char *name1, const char *name2, + error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", addr.name) - MATCH_UINT32_ATTR("addr", addr.addr) - END_ATTR_MATCH() + std::ostringstream oss; + oss << "conflicting <" << name1 << "> and <" << name2 << "> elements"; + if(node->parent->name) + oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">"; + else + oss << " at root level"; + return add_fatal(ctx, node, oss.str()); +} - return true; +bool parse_not_text_error(xmlNode *node, error_context_t& ctx) +{ + return add_fatal(ctx, node, "this is not a text element"); } -bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula) +bool parse_not_text_attr_error(xmlAttr *attr, error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("string", formula.string) - END_ATTR_MATCH() + return add_fatal(ctx, attr, "this is not a text attribute"); +} - formula.type = REG_FORMULA_STRING; +bool parse_text_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx) +{ + name = XML_CHAR_TO_CHAR(content); + return true; +} +bool parse_name_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx) +{ + name = XML_CHAR_TO_CHAR(content); + if(name.size() == 0) + return add_fatal(ctx, node, "name cannot be empty"); + for(size_t i = 0; i < name.size(); i++) + if(!isalnum(name[i]) && name[i] != '_') + return add_fatal(ctx, node, "name must only contain alphanumeric characters or _"); return true; } -bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg) +template +bool parse_unsigned_text(U *node, T& res, xmlChar *content, error_context_t& ctx) { - soc_reg_addr_t a; - a.name = reg.name; - if(!validate_uint32_hook(str, a.addr)) - return false; - reg.addr.push_back(a); + char *end; + unsigned long uns = strtoul(XML_CHAR_TO_CHAR(content), &end, 0); + if(*end != 0) + return add_fatal(ctx, node, "content must be an unsigned integer"); + res = uns; + if(res != uns) + return add_fatal(ctx, node, "value does not fit into allowed range"); return true; } -bool parse_reg_elem(xmlNode *node, soc_reg_t& reg) +template +bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx) { - std::list< soc_reg_formula_t > formulas; - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", reg.name) - SOFT_MATCH_SCT_ATTR("sct", reg.flags) - SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg) - MATCH_TEXT_ATTR("desc", reg.desc) - END_ATTR_MATCH() + return parse_unsigned_text(node, res, content, ctx); +} - BEGIN_NODE_MATCH(node->children) - MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem) - MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem) - MATCH_ELEM_NODE("field", reg.field, parse_field_elem) - END_NODE_MATCH() +template +bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx) +{ + return parse_unsigned_text(attr, res, content, ctx); +} - if(formulas.size() > 1) - { - fprintf(stderr, "Only one formula is allowed per register\n"); +bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx) +{ + if(attr->children != attr->last) return false; - } - if(formulas.size() == 1) - reg.formula = formulas.front(); - + if(attr->children->type != XML_TEXT_NODE) + return parse_not_text_attr_error(attr, ctx); + res = attr->children->content; return true; } -bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr) +bool parse_text_attr(xmlAttr *attr, std::string& res, xmlChar *content, error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", addr.name) - MATCH_UINT32_ATTR("addr", addr.addr) - END_ATTR_MATCH() - + res = XML_CHAR_TO_CHAR(content); return true; } -bool parse_dev_elem(xmlNode *node, soc_dev_t& dev) +bool parse_enum_elem(xmlNode *node, enum_t& reg, error_context_t& ctx) { - BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", dev.name) - MATCH_TEXT_ATTR("long_name", dev.long_name) - MATCH_TEXT_ATTR("desc", dev.desc) - MATCH_TEXT_ATTR("version", dev.version) - END_ATTR_MATCH() + bool ret = true; + bool has_name = false, has_value = false, has_desc = false; + BEGIN_NODE_MATCH(node->children) + MATCH_UNIQUE_TEXT_NODE("name", reg.name, has_name, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("value", reg.value, has_value, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx) + END_NODE_MATCH() + CHECK_HAS(node, "name", has_name, ctx) + CHECK_HAS(node, "value", has_value, ctx) + return ret; +} +bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx) +{ + bool ret = true; + bool has_name = false, has_pos = false, has_desc = false, has_width = false; BEGIN_NODE_MATCH(node->children) - MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem) - MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem) + MATCH_UNIQUE_TEXT_NODE("name", field.name, has_name, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("position", field.pos, has_pos, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("width", field.width, has_width, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("desc", field.desc, has_desc, parse_text_elem, ctx) + MATCH_ELEM_NODE("enum", field.enum_, parse_enum_elem, ctx) END_NODE_MATCH() + CHECK_HAS(node, "name", has_name, ctx) + CHECK_HAS(node, "position", has_pos, ctx) + if(!has_width) + field.width = 1; + return ret; +} - return true; +bool parse_register_elem(xmlNode *node, register_t& reg, error_context_t& ctx) +{ + bool ret = true; + bool has_width = false; + BEGIN_NODE_MATCH(node->children) + MATCH_UNIQUE_TEXT_NODE("width", reg.width, has_width, parse_unsigned_elem, ctx) + MATCH_ELEM_NODE("field", reg.field, parse_field_elem, ctx) + END_NODE_MATCH() + if(!has_width) + reg.width = 32; + return ret; } -bool parse_soc_elem(xmlNode *node, soc_t& soc) +bool parse_formula_elem(xmlNode *node, range_t& range, error_context_t& ctx) { + bool ret = true; + bool has_var = false; BEGIN_ATTR_MATCH(node->properties) - MATCH_TEXT_ATTR("name", soc.name) - MATCH_TEXT_ATTR("desc", soc.desc) - END_ATTR_MATCH() + MATCH_UNIQUE_ATTR("variable", range.variable, has_var, parse_text_attr, ctx) + END_NODE_MATCH() + CHECK_HAS_ATTR(node, "variable", has_var, ctx) + return ret; +} +bool parse_range_elem(xmlNode *node, range_t& range, error_context_t& ctx) +{ + bool ret = true; + bool has_first = false, has_count = false, has_stride = false, has_base = false; + bool has_formula = false, has_formula_attr = false; BEGIN_NODE_MATCH(node->children) - MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem) + MATCH_UNIQUE_TEXT_NODE("first", range.first, has_first, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("count", range.count, has_count, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("base", range.base, has_base, parse_unsigned_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("stride", range.stride, has_stride, parse_unsigned_elem, ctx) + MATCH_UNIQUE_ELEM_NODE("formula", range, has_formula_attr, parse_formula_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("formula", range.formula, has_formula, parse_text_elem, ctx) END_NODE_MATCH() + CHECK_HAS(node, "first", has_first, ctx) + CHECK_HAS(node, "count", has_count, ctx) + if(!has_base && !has_formula) + ret = ret && parse_missing_error(node, "base> or children) + MATCH_UNIQUE_TEXT_NODE("name", inst.name, has_name, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("title", inst.title, has_title, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("desc", inst.desc, has_desc, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("address", inst.addr, has_address, parse_unsigned_elem, ctx) + MATCH_UNIQUE_ELEM_NODE("range", inst.range, has_range, parse_range_elem, ctx) + END_NODE_MATCH() + CHECK_HAS(node, "name", has_name, ctx) + if(!has_address && !has_range) + ret = ret && parse_missing_error(node, "address> or socs; - BEGIN_NODE_MATCH(node) - MATCH_ELEM_NODE("soc", socs, parse_soc_elem) + bool ret = true; + register_t reg; + bool has_title = false, has_desc = false, has_register = false, has_name = false; + BEGIN_NODE_MATCH(node_->children) + MATCH_UNIQUE_TEXT_NODE("name", node.name, has_name, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("title", node.title, has_title, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("desc", node.desc, has_desc, parse_text_elem, ctx) + MATCH_UNIQUE_ELEM_NODE("register", reg, has_register, parse_register_elem, ctx) + MATCH_ELEM_NODE("node", node.node, parse_node_elem, ctx) + MATCH_ELEM_NODE("instance", node.instance, parse_instance_elem, ctx) END_NODE_MATCH() - if(socs.size() != 1) + CHECK_HAS(node_, "name", has_name, ctx) + CHECK_HAS(node_, "instance", !node.instance.empty(), ctx) + if(has_register) + node.register_.push_back(reg); + return ret; +} + +bool parse_soc_elem(xmlNode *node, soc_t& soc, error_context_t& ctx) +{ + bool ret = true; + bool has_name = false, has_title = false, has_desc = false, has_version = false; + bool has_isa = false; + BEGIN_NODE_MATCH(node->children) + MATCH_UNIQUE_TEXT_NODE("name", soc.name, has_name, parse_name_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("title", soc.title, has_title, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("desc", soc.desc, has_desc, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("version", soc.version, has_version, parse_text_elem, ctx) + MATCH_UNIQUE_TEXT_NODE("isa", soc.isa, has_isa, parse_text_elem, ctx) + MATCH_TEXT_NODE("author", soc.author, parse_text_elem, ctx) + MATCH_ELEM_NODE("node", soc.node, parse_node_elem, ctx) + END_NODE_MATCH() + CHECK_HAS(node, "name", has_name, ctx) + return ret; +} + +bool parse_root_elem(xmlNode *node, soc_t& soc, error_context_t& ctx) +{ + size_t ver = 0; + bool ret = true; + bool has_soc = false, has_version = false; + BEGIN_ATTR_MATCH(node->properties) + MATCH_UNIQUE_ATTR("version", ver, has_version, parse_unsigned_attr, ctx) + END_ATTR_MATCH() + if(!has_version) { - fprintf(stderr, "A description file must contain exactly one soc element\n"); + ctx.add(error_t(error_t::FATAL, xml_loc(node), "no version attribute, is this a v1 file ?")); return false; } - soc = socs[0]; - return true; + if(ver != MAJOR_VERSION) + return parse_wrong_version_error(node, ctx); + BEGIN_NODE_MATCH(node) + MATCH_UNIQUE_ELEM_NODE("soc", soc, has_soc, parse_soc_elem, ctx) + END_NODE_MATCH() + CHECK_HAS(node, "soc", has_soc, ctx) + return ret; } } -bool soc_desc_parse_xml(const std::string& filename, soc_t& socs) +bool parse_xml(const std::string& filename, soc_t& soc, + error_context_t& error_ctx) { LIBXML_TEST_VERSION @@ -290,156 +458,207 @@ bool soc_desc_parse_xml(const std::string& filename, soc_t& socs) return false; xmlNodePtr root_element = xmlDocGetRootElement(doc); - bool ret = parse_root_elem(root_element, socs); + bool ret = parse_root_elem(root_element, soc, error_ctx); xmlFreeDoc(doc); return ret; } +/** + * Producer + */ + namespace { -int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field) +#define SAFE(x) \ + do{ \ + if((x) < 0) { \ + std::ostringstream oss; \ + oss << __FILE__ << ":" << __LINE__; \ + ctx.add(error_t(error_t::FATAL, oss.str(), "write error")); \ + return -1; \ + } \ + }while(0) + +int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t& ctx) { -#define SAFE(x) if((x) < 0) return -1; - /* */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); - /* desc */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); - /* bitrange */ - SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d", - field.last_bit, field.first_bit)); - /* values */ - for(size_t i = 0; i < field.value.size(); i++) + /* */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range")); + /* */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first)); + /* */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); + /* */ + if(range.type == range_t::STRIDE) { - /* */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str())); - /* value */ - SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value)); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str())); - /* */ - SAFE(xmlTextWriterEndElement(writer)); + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base)); + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride)); } - /* */ - SAFE(xmlTextWriterEndElement(writer)); -#undef SAFE - return 0; -} - -int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg) -{ -#define SAFE(x) if((x) < 0) return -1; - /* */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str())); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str())); - /* flags */ - if(reg.flags & REG_HAS_SCT) - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes")); - /* formula */ - if(reg.formula.type != REG_FORMULA_NONE) + /* */ + else if(range.type == range_t::FORMULA) { /* */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); - switch(reg.formula.type) - { - case REG_FORMULA_STRING: - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string", - BAD_CAST reg.formula.string.c_str())); - break; - default: - break; - } + /* variable */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "variable", BAD_CAST range.variable.c_str())); + /* content */ + SAFE(xmlTextWriterWriteString(writer, BAD_CAST range.formula.c_str())); /* */ SAFE(xmlTextWriterEndElement(writer)); } - /* addresses */ - for(size_t i = 0; i < reg.addr.size(); i++) - { - /* */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str())); - /* addr */ - SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr)); - /* */ - SAFE(xmlTextWriterEndElement(writer)); - } + /* */ + SAFE(xmlTextWriterEndElement(writer)); + + return 0; +} + +int produce_instance(xmlTextWriterPtr writer, const instance_t& inst, error_context_t& ctx) +{ + /* */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "instance")); + /* */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST inst.name.c_str())); + /* */ + if(!inst.title.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST inst.title.c_str())); + /* <desc/> */ + if(!inst.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST inst.desc.c_str())); + /* <address/> */ + if(inst.type == instance_t::SINGLE) + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", inst.addr)); + /* <range/> */ + else if(inst.type == instance_t::RANGE) + SAFE(produce_range(writer, inst.range, ctx)); + /* </instance> */ + SAFE(xmlTextWriterEndElement(writer)); + return 0; +} + +int produce_enum(xmlTextWriterPtr writer, const enum_t& enum_, error_context_t& ctx) +{ + /* <enum> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "enum")); + /* <name/> */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST enum_.name.c_str())); + /* <desc/> */ + if(!enum_.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST enum_.desc.c_str())); + /* <value/> */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "value", "0x%x", enum_.value)); + /* </enum> */ + SAFE(xmlTextWriterEndElement(writer)); + return 0; +} + +int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t& ctx) +{ + /* <field> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); + /* <name/> */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); + /* <desc/> */ + if(!field.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); + /* <position/> */ + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "position", "%lu", field.pos)); + /* <width/> */ + if(field.width != 1) + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", field.width)); + /* enums */ + for(size_t i = 0; i < field.enum_.size(); i++) + SAFE(produce_enum(writer, field.enum_[i], ctx)); + /* </field> */ + SAFE(xmlTextWriterEndElement(writer)); + return 0; +} + +int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_context_t& ctx) +{ + /* <register> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "register")); + /* <width/> */ + if(reg.width != 32) + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", reg.width)); /* fields */ for(size_t i = 0; i < reg.field.size(); i++) - produce_field(writer, reg.field[i]); - /* </reg> */ + SAFE(produce_field(writer, reg.field[i], ctx)); + /* </register> */ SAFE(xmlTextWriterEndElement(writer)); -#undef SAFE return 0; } -int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev) +int produce_node(xmlTextWriterPtr writer, const node_t& node, error_context_t& ctx) { -#define SAFE(x) if((x) < 0) return -1; - /* <dev> */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str())); - /* long_name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str())); - /* desc */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str())); - /* version */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str())); - /* addresses */ - for(size_t i = 0; i < dev.addr.size(); i++) - { - /* <addr> */ - SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str())); - /* addr */ - SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr)); - /* </addr> */ - SAFE(xmlTextWriterEndElement(writer)); - } - /* registers */ - for(size_t i = 0; i < dev.reg.size(); i++) - produce_reg(writer, dev.reg[i]); - /* </dev> */ + /* <node> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "node")); + /* <name/> */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST node.name.c_str())); + /* <title/> */ + if(!node.title.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST node.title.c_str())); + /* <desc/> */ + if(!node.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST node.desc.c_str())); + /* instances */ + for(size_t i = 0; i < node.instance.size(); i++) + SAFE(produce_instance(writer, node.instance[i], ctx)); + /* register */ + for(size_t i = 0; i < node.register_.size(); i++) + SAFE(produce_register(writer, node.register_[i], ctx)); + /* nodes */ + for(size_t i = 0; i < node.node.size(); i++) + SAFE(produce_node(writer, node.node[i], ctx)); + /* </node> */ SAFE(xmlTextWriterEndElement(writer)); -#undef SAFE return 0; } +#undef SAFE + } -bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc) +bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& ctx) { LIBXML_TEST_VERSION + std::ostringstream oss; xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0); if(writer == NULL) return false; -#define SAFE(x) if((x) < 0) goto Lerr +#define SAFE(x) do{if((x) < 0) goto Lerr;}while(0) SAFE(xmlTextWriterSetIndent(writer, 1)); - SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); + SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); /* <xml> */ SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL)); /* <soc> */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc")); - /* name */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); - /* desc */ - SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); - /* devices */ - for(size_t i = 0; i < soc.dev.size(); i++) - SAFE(produce_dev(writer, soc.dev[i])); - /* end <soc> */ + /* version */ + oss << MAJOR_VERSION; + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST oss.str().c_str())); + /* <name/> */ + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); + /* <title/> */ + if(!soc.title.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST soc.title.c_str())); + /* <desc/> */ + if(!soc.desc.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); + /* <author/> */ + for(size_t i = 0; i < soc.author.size(); i++) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "author", BAD_CAST soc.author[i].c_str())); + /* <isa/> */ + if(!soc.isa.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "isa", BAD_CAST soc.isa.c_str())); + /* <version/> */ + if(!soc.version.empty()) + SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "version", BAD_CAST soc.version.c_str())); + /* nodes */ + for(size_t i = 0; i < soc.node.size(); i++) + SAFE(produce_node(writer, soc.node[i], ctx)); + /* </soc> */ SAFE(xmlTextWriterEndElement(writer)); /* </xml> */ SAFE(xmlTextWriterEndDocument(writer)); @@ -451,520 +670,500 @@ Lerr: return false; } -namespace +/** + * soc_ref_t + */ + +soc_ref_t::soc_ref_t():m_soc(0) { +} -struct soc_sorter +soc_ref_t::soc_ref_t(soc_t *soc):m_soc(soc) { - bool operator()(const soc_dev_t& a, const soc_dev_t& b) const - { - return a.name < b.name; - } +} - bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const - { - return a.name < b.name; - } +bool soc_ref_t::valid() const +{ + return get() != 0; +} - bool operator()(const soc_reg_t& a, const soc_reg_t& b) const - { - soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0; - soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0; - return aa < ab; - } +soc_t *soc_ref_t::get() const +{ + return m_soc; +} - bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const - { - return a.addr < b.addr; - } +bool soc_ref_t::operator==(const soc_ref_t& ref) const +{ + return m_soc == ref.m_soc; +} - bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const - { - return a.last_bit > b.last_bit; - } +node_ref_t soc_ref_t::root() const +{ + return node_ref_t(*this); +} - bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const - { - return a.value < b.value; - } -}; +node_inst_t soc_ref_t::root_inst() const +{ + return node_inst_t(*this); +} -void normalize(soc_reg_field_t& field) +/** + * node_ref_t */ + +node_ref_t::node_ref_t(soc_ref_t soc):m_soc(soc) { - std::sort(field.value.begin(), field.value.end(), soc_sorter()); } -void normalize(soc_reg_t& reg) +node_ref_t::node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path) + :m_soc(soc), m_path(path) { - std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter()); - std::sort(reg.field.begin(), reg.field.end(), soc_sorter()); - for(size_t i = 0; i < reg.field.size(); i++) - normalize(reg.field[i]); } -void normalize(soc_dev_t& dev) +node_ref_t::node_ref_t() { - std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter()); - std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter()); - for(size_t i = 0; i < dev.reg.size(); i++) - normalize(dev.reg[i]); } +bool node_ref_t::valid() const +{ + return (m_soc.valid() && is_root()) || get() != 0; } -void soc_desc_normalize(soc_t& soc) +bool node_ref_t::is_root() const { - std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter()); - for(size_t i = 0; i < soc.dev.size(); i++) - normalize(soc.dev[i]); + return m_path.empty(); } namespace { - soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what) - { - soc_error_t err; - err.level = lvl; - err.location = at; - err.message = what; - return err; - } - soc_error_t make_warning(std::string at, std::string what) - { - return make_error(SOC_ERROR_WARNING, at, what); - } - - soc_error_t make_fatal(std::string at, std::string what) - { - return make_error(SOC_ERROR_FATAL, at, what); - } +std::vector< node_t > *get_children(node_ref_t node) +{ + if(node.is_root()) + return &node.soc().get()->node; + node_t *n = node.get(); + return n == 0 ? 0 : &n->node; +} - soc_error_t prefix(soc_error_t err, const std::string& prefix_at) - { - err.location = prefix_at + "." + err.location; - return err; - } +node_t *get_child(std::vector< node_t > *nodes, soc_id_t id) +{ + if(nodes == 0) + return 0; + for(size_t i = 0; i < nodes->size(); i++) + if((*nodes)[i].id == id) + return &(*nodes)[i]; + return 0; +} - void add_errors(std::vector< soc_error_t >& errors, - const std::vector< soc_error_t >& new_errors, const std::string& prefix_at) - { - for(size_t i = 0; i < new_errors.size(); i++) - errors.push_back(prefix(new_errors[i], prefix_at)); - } +node_t *get_child(std::vector< node_t > *nodes, const std::string& name) +{ + if(nodes == 0) + return 0; + for(size_t i = 0; i < nodes->size(); i++) + if((*nodes)[i].name == name) + return &(*nodes)[i]; + return 0; +} - std::vector< soc_error_t > no_error() - { - std::vector< soc_error_t > s; - return s; - } +} - std::vector< soc_error_t > one_error(const soc_error_t& err) +/* NOTE: valid() is implemented using get() != 0, so don't use it in get() ! */ +node_t *node_ref_t::get() const +{ + if(!soc().valid()) + return 0; + /* we could do it recursively but it would make plenty of copies */ + node_t *n = 0; + std::vector< node_t > *nodes = &soc().get()->node; + for(size_t i = 0; i < m_path.size(); i++) { - std::vector< soc_error_t > s; - s.push_back(err); - return s; + n = get_child(nodes, m_path[i]); + if(n == 0) + return 0; + nodes = &n->node; } + return n; +} - bool name_valid(char c) - { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || c == '_'; - } +soc_ref_t node_ref_t::soc() const +{ + return m_soc; +} - bool name_valid(const std::string& s) - { - for(size_t i = 0; i < s.size(); i++) - if(!name_valid(s[i])) - return false; - return true; - } +node_ref_t node_ref_t::parent() const +{ + std::vector< soc_id_t > path = m_path; + if(!path.empty()) + path.pop_back(); + return node_ref_t(m_soc, path); } -std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive) +register_ref_t node_ref_t::reg() const { - (void) recursive; - if(name.size() == 0) - return one_error(make_fatal(name, "empty name")); - else if(!name_valid(name)) - return one_error(make_fatal(name, "invalid name")); + node_t *n = get(); + if(n == 0) + return register_ref_t(); + if(n->register_.empty()) + return parent().reg(); else - return no_error(); + return register_ref_t(*this); } -std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive) +node_ref_t node_ref_t::child(const std::string& name) const { - std::vector< soc_error_t > err; - std::string at(name); - if(name.size() == 0) - err.push_back(make_fatal(at, "empty name")); - else if(!name_valid(name)) - err.push_back(make_fatal(at, "invalid name")); - if(last_bit > 31) - err.push_back(make_fatal(at, "last bit is greater than 31")); - if(first_bit > last_bit) - err.push_back(make_fatal(at, "last bit is greater than first bit")); - for(size_t i = 0; i < value.size(); i++) + /* check the node exists */ + node_t *n = get_child(get_children(*this), name); + if(n == 0) + return node_ref_t(); + std::vector< soc_id_t > path = m_path; + path.push_back(n->id); + return node_ref_t(m_soc, path); +} + +std::vector< node_ref_t > node_ref_t::children() const +{ + std::vector< node_ref_t > nodes; + std::vector< node_t > *children = get_children(*this); + if(children == 0) + return nodes; + for(size_t i = 0; i < children->size(); i++) { - for(size_t j = 0; j < value.size(); j++) + std::vector< soc_id_t > path = m_path; + path.push_back((*children)[i].id); + nodes.push_back(node_ref_t(m_soc, path)); + } + return nodes; +} + +std::vector< std::string > node_ref_t::path() const +{ + std::vector< std::string > path; + if(!soc().valid()) + return path; + /* we could do it recursively but this is more efficient */ + node_t *n = 0; + std::vector< node_t > *nodes = &soc().get()->node; + for(size_t i = 0; i < m_path.size(); i++) + { + n = get_child(nodes, m_path[i]); + if(n == 0) { - if(i == j) - continue; - if(value[i].name == value[j].name) - err.push_back(prefix(make_fatal(value[i].name, - "there are several values with the same name"), at)); - if(value[i].value == value[j].value) - err.push_back(prefix(make_warning(value[i].name, - "there are several values with the same value"), at)); + path.clear(); + return path; } - if(value[i].value > (bitmask() >> first_bit)) - err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name)); - if(recursive) - add_errors(err, value[i].errors(true), at); + path.push_back(n->name); + nodes = &n->node; } - return err; + return path; } -std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive) +std::string node_ref_t::name() const { - (void) recursive; - if(name.size() == 0) - return one_error(make_fatal("", "empty name")); - else if(!name_valid(name)) - return one_error(make_fatal(name, "invalid name")); - else - return no_error(); + node_t *n = get(); + return n == 0 ? "" : n->name; } -std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive) +bool node_ref_t::operator==(const node_ref_t& ref) const { - (void) recursive; - if(type == REG_FORMULA_STRING && string.size() == 0) - return one_error(make_fatal("", "empty string formula")); - else - return no_error(); + return m_soc == ref.m_soc && m_path == ref.m_path; } -namespace +/** + * register_ref_t + */ + +register_ref_t::register_ref_t(node_ref_t node) + :m_node(node) { +} -bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b) +register_ref_t::register_ref_t() { - return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit); } +bool register_ref_t::valid() const +{ + return get() != 0; } -std::vector< soc_error_t > soc_reg_t::errors(bool recursive) +register_t *register_ref_t::get() const { - std::vector< soc_error_t > err; - std::string at(name); - if(name.size() == 0) - err.push_back(make_fatal(at, "empty name")); - else if(!name_valid(name)) - err.push_back(make_fatal(at, "invalid name")); - for(size_t i = 0; i < addr.size(); i++) - { - for(size_t j = 0; j < addr.size(); j++) - { - if(i == j) - continue; - if(addr[i].name == addr[j].name) - err.push_back(prefix(make_fatal(addr[i].name, - "there are several instances with the same name"), at)); - if(addr[i].addr == addr[j].addr) - err.push_back(prefix(make_fatal(addr[i].name, - "there are several instances with the same address"), at)); - } - if(recursive) - add_errors(err, addr[i].errors(true), at); - } - if(recursive) - add_errors(err, formula.errors(true), at); - for(size_t i = 0; i < field.size(); i++) - { - for(size_t j = 0; j < field.size(); j++) - { - if(i == j) - continue; - if(field[i].name == field[j].name) - err.push_back(prefix(make_fatal(field[i].name, - "there are several fields with the same name"), at)); - if(field_overlap(field[i], field[j])) - err.push_back(prefix(make_fatal(field[i].name, - "there are overlapping fields"), at)); - } - if(recursive) - add_errors(err, field[i].errors(true), at); - } - return err; + node_t *n = m_node.get(); + if(n == 0 || n->register_.empty()) + return 0; + return &n->register_[0]; } -std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive) +node_ref_t register_ref_t::node() const { - (void) recursive; - if(name.size() == 0) - return one_error(make_fatal("", "empty name")); - else if(!name_valid(name)) - return one_error(make_fatal(name, "invalid name")); - else - return no_error(); + return m_node; } -std::vector< soc_error_t > soc_dev_t::errors(bool recursive) +std::vector< field_ref_t > register_ref_t::fields() const { - std::vector< soc_error_t > err; - std::string at(name); - if(name.size() == 0) - err.push_back(make_fatal(at, "empty name")); - else if(!name_valid(name)) - err.push_back(make_fatal(at, "invalid name")); - for(size_t i = 0; i < addr.size(); i++) - { - for(size_t j = 0; j < addr.size(); j++) - { - if(i == j) - continue; - if(addr[i].name == addr[j].name) - err.push_back(prefix(make_fatal(addr[i].name, - "there are several instances with the same name"), at)); - if(addr[i].addr == addr[j].addr) - err.push_back(prefix(make_fatal(addr[i].name, - "there are several instances with the same address"), at)); - } - if(recursive) - add_errors(err, addr[i].errors(true), at); - } - for(size_t i = 0; i < reg.size(); i++) - { - for(size_t j = 0; j < reg.size(); j++) - { - if(i == j) - continue; - if(reg[i].name == reg[j].name) - err.push_back(prefix(make_fatal(reg[i].name, - "there are several registers with the same name"), at)); - } - if(recursive) - add_errors(err, reg[i].errors(true), at); - } - return err; + std::vector< field_ref_t > fields; + register_t *r = get(); + if(r == 0) + return fields; + for(size_t i = 0; i < r->field.size(); i++) + fields.push_back(field_ref_t(*this, r->field[i].id)); + return fields; } -std::vector< soc_error_t > soc_t::errors(bool recursive) +field_ref_t register_ref_t::field(const std::string& name) const { - std::vector< soc_error_t > err; - std::string at(name); - for(size_t i = 0; i < dev.size(); i++) - { - for(size_t j = 0; j < dev.size(); j++) - { - if(i == j) - continue; - if(dev[i].name == dev[j].name) - err.push_back(prefix(make_fatal(dev[i].name, - "there are several devices with the same name"), at)); - } - if(recursive) - add_errors(err, dev[i].errors(true), at); - } - return err; + register_t *r = get(); + if(r == 0) + return field_ref_t(); + for(size_t i = 0; i < r->field.size(); i++) + if(r->field[i].name == name) + return field_ref_t(*this, r->field[i].id); + return field_ref_t(); } -namespace +/** + * field_ref_t + */ + +field_ref_t::field_ref_t(register_ref_t reg, soc_id_t id) + :m_reg(reg), m_id(id) { +} -struct formula_evaluator +field_ref_t::field_ref_t() { - std::string formula; - size_t pos; - std::string error; +} - bool err(const char *fmt, ...) - { - char buffer[256]; - va_list args; - va_start(args, fmt); - vsnprintf(buffer,sizeof(buffer), fmt, args); - va_end(args); - error = buffer; - return false; - } +bool field_ref_t::valid() const +{ + return get() != 0; +} - formula_evaluator(const std::string& s):pos(0) - { - for(size_t i = 0; i < s.size(); i++) - if(!isspace(s[i])) - formula.push_back(s[i]); - } +field_t *field_ref_t::get() const +{ + register_t *reg = m_reg.get(); + if(reg == 0) + return 0; + for(size_t i = 0; i < reg->field.size(); i++) + if(reg->field[i].id == m_id) + return ®->field[i]; + return 0; +} - void adv() - { - pos++; - } +register_ref_t field_ref_t::reg() const +{ + return m_reg; +} - char cur() - { - return end() ? 0 : formula[pos]; - } +/** + * node_inst_t + */ - bool end() - { - return pos >= formula.size(); - } +namespace +{ + +const size_t INST_NO_INDEX = std::numeric_limits<std::size_t>::max(); - bool parse_digit(char c, int basis, soc_word_t& res) +bool get_inst_addr(range_t& range, size_t index, soc_addr_t& addr) +{ + if(index < range.first || index >= range.first + range.count) + return false; + switch(range.type) { - c = tolower(c); - if(isdigit(c)) - { - res = c - '0'; + case range_t::STRIDE: + addr += range.base + (index - range.first) * range.stride; return true; - } - if(basis == 16 && isxdigit(c)) + case range_t::FORMULA: { - res = c + 10 - 'a'; + soc_word_t res; + std::map< std::string, soc_word_t > vars; + vars[range.variable] = index; + error_context_t ctx; + if(!evaluate_formula(range.formula, vars, res, "", ctx)) + return false; + addr += res; return true; } - return err("invalid digit '%c'", c); + default: + return false; } +} - bool parse_signed(soc_word_t& res) +bool get_inst_addr(instance_t *inst, size_t index, soc_addr_t& addr) +{ + if(inst == 0) + return false; + switch(inst->type) { - char op = cur(); - if(op == '+' || op == '-') - { - adv(); - if(!parse_signed(res)) + case instance_t::SINGLE: + if(index != INST_NO_INDEX) return false; - if(op == '-') - res *= -1; + addr += inst->addr; return true; - } - else if(op == '(') - { - adv(); - if(!parse_expression(res)) + case instance_t::RANGE: + if(index == INST_NO_INDEX) return false; - if(cur() != ')') - return err("expected ')', got '%c'", cur()); - adv(); - return true; - } - else if(isdigit(op)) - { - res = op - '0'; - adv(); - int basis = 10; - if(op == '0' && cur() == 'x') - { - basis = 16; - adv(); - } - soc_word_t digit = 0; - while(parse_digit(cur(), basis, digit)) - { - res = res * basis + digit; - adv(); - } - return true; - } - else if(isalpha(op) || op == '_') - { - std::string name; - while(isalnum(cur()) || cur() == '_') - { - name.push_back(cur()); - adv(); - } - return get_variable(name, res); - } - else - return err("express signed expression, got '%c'", op); - } - - bool parse_term(soc_word_t& res) - { - if(!parse_signed(res)) + return get_inst_addr(inst->range, index, addr); + default: return false; - while(cur() == '*' || cur() == '/' || cur() == '%') - { - char op = cur(); - adv(); - soc_word_t tmp; - if(!parse_signed(tmp)) - return false; - if(op == '*') - res *= tmp; - else if(tmp != 0) - res = op == '/' ? res / tmp : res % tmp; - else - return err("division by 0"); - } - return true; } +} - bool parse_expression(soc_word_t& res) - { - if(!parse_term(res)) - return false; - while(!end() && (cur() == '+' || cur() == '-')) - { - char op = cur(); - adv(); - soc_word_t tmp; - if(!parse_term(tmp)) - return false; - if(op == '+') - res += tmp; - else - res -= tmp; - } - return true; - } +} - bool parse(soc_word_t& res, std::string& _error) - { - bool ok = parse_expression(res); - if(ok && !end()) - err("unexpected character '%c'", cur()); - _error = error; - return ok && end(); - } +node_inst_t::node_inst_t(soc_ref_t soc) + :m_node(soc.root()) +{ +} - virtual bool get_variable(std::string name, soc_word_t& res) - { - return err("unknown variable '%s'", name.c_str()); - } -}; +node_inst_t::node_inst_t(node_ref_t node, const std::vector< soc_id_t >& ids, + const std::vector< size_t >& indexes) + :m_node(node), m_id_path(ids), m_index_path(indexes) +{ +} -struct my_evaluator : public formula_evaluator +node_inst_t::node_inst_t() { - const std::map< std::string, soc_word_t>& var; +} - my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var) - :formula_evaluator(formula), var(_var) {} +bool node_inst_t::valid() const +{ + return is_root() || get() != 0; +} - virtual bool get_variable(std::string name, soc_word_t& res) +node_ref_t node_inst_t::node() const +{ + return m_node; +} + +soc_ref_t node_inst_t::soc() const +{ + return m_node.soc(); +} + +bool node_inst_t::is_root() const +{ + return m_node.is_root(); +} + +node_inst_t node_inst_t::parent() const +{ + std::vector< soc_id_t > ids = m_id_path; + std::vector< size_t > indexes = m_index_path; + if(!ids.empty()) + ids.pop_back(); + if(!indexes.empty()) + indexes.pop_back(); + return node_inst_t(m_node.parent(), ids, indexes); +} + +instance_t *node_inst_t::get() const +{ + node_t *n = m_node.get(); + if(n == 0) + return 0; + for(size_t i = 0; i < n->instance.size(); i++) + if(n->instance[i].id == m_id_path.back()) + return &n->instance[i]; + return 0; +} + +soc_addr_t node_inst_t::addr() const +{ + if(is_root()) + return 0; + soc_addr_t addr = parent().addr(); + if(!get_inst_addr(get(), m_index_path.back(), addr)) + return 0; + return addr; +} + +node_inst_t node_inst_t::child(const std::string& name) const +{ + return child(name, INST_NO_INDEX); +} + + +node_inst_t node_inst_t::child(const std::string& name, size_t index) const +{ + std::vector< node_t > *nodes = get_children(m_node); + if(nodes == 0) + return node_inst_t(); + node_ref_t child_node = m_node; + for(size_t i = 0; i < nodes->size(); i++) { - std::map< std::string, soc_word_t>::const_iterator it = var.find(name); - if(it == var.end()) - return formula_evaluator::get_variable(name, res); - else + node_t& node = (*nodes)[i]; + child_node.m_path.push_back(node.id); + for(size_t j = 0; j < node.instance.size(); j++) { - res = it->second; - return true; + if(node.instance[j].name != name) + continue; + std::vector< soc_id_t > ids = m_id_path; + std::vector< size_t > indexes = m_index_path; + ids.push_back(node.instance[j].id); + ids.push_back(index); + return node_inst_t(child_node, ids, indexes); } + child_node.m_path.pop_back(); } -}; + return node_inst_t(); +} + +std::vector< node_inst_t > node_inst_t::children() const +{ + std::vector< node_inst_t > list; + std::vector< node_t > *nodes = get_children(m_node); + std::vector< soc_id_t > n_path = m_id_path; + std::vector< size_t > i_path = m_index_path; + if(nodes == 0) + return list; + node_ref_t child_node = m_node; + for(size_t i = 0; i < nodes->size(); i++) + { + node_t& node = (*nodes)[i]; + child_node.m_path.push_back(node.id); + for(size_t j = 0; j < node.instance.size(); j++) + { + instance_t& inst = node.instance[j]; + n_path.push_back(inst.id); + switch(inst.type) + { + case instance_t::SINGLE: + i_path.push_back(INST_NO_INDEX); + list.push_back(node_inst_t(child_node, n_path, i_path)); + i_path.pop_back(); + break; + case instance_t::RANGE: + for(size_t i = 0; i < inst.range.count; i++) + { + i_path.push_back(inst.range.first + i); + list.push_back(node_inst_t(child_node, n_path, i_path)); + i_path.pop_back(); + } + break; + default: + break; + } + n_path.pop_back(); + } + child_node.m_path.pop_back(); + } + return list; +} + +std::string node_inst_t::name() const +{ + instance_t *inst = get(); + return inst == 0 ? "" : inst->name; +} +bool node_inst_t:: is_indexed() const +{ + return !m_index_path.empty() && m_index_path.back() != INST_NO_INDEX; } -bool soc_desc_evaluate_formula(const std::string& formula, - const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error) +size_t node_inst_t::index() const { - my_evaluator e(formula, var); - return e.parse(result, error); + return m_index_path.empty() ? INST_NO_INDEX : m_index_path.back(); } /** WARNING we need to call xmlInitParser() to init libxml2 but it needs to @@ -982,4 +1181,7 @@ public: }; xml_parser_init __xml_parser_init; -} \ No newline at end of file +} + +} // soc_desc_v1 + diff --git a/utils/regtools/lib/soc_desc.hpp b/utils/regtools/lib/soc_desc.hpp deleted file mode 100644 index bb8eb4ba8d..0000000000 --- a/utils/regtools/lib/soc_desc.hpp +++ /dev/null @@ -1,228 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2012 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. - * - ****************************************************************************/ -#ifndef __SOC_DESC__ -#define __SOC_DESC__ - -#include <stdint.h> -#include <vector> -#include <list> -#include <string> -#include <map> - -/** - * These data structures represent the SoC register in a convenient way. - * The basic structure is the following: - * - each SoC has several devices - * - each device has a generic name, a list of {name,address} and several registers - * - each register has a generic name, a list of {name,address}, flags, - * several fields - * - each field has a name, a first and last bit position, can apply either - * to all addresses of a register or be specific to one only and has several values - * - each field value has a name and a value - * - * All addresses, values and names are relative to the parents. For example a field - * value BV_LCDIF_CTRL_WORD_LENGTH_18_BIT is represented has: - * - device LCDIF, register CTRL, field WORD_LENGTH, value 16_BIT - * The address of CTRL is related to the address of LCDIF, the value of 16_BIT - * ignores the position of the WORD_LENGTH field in the register. - */ - -#define SOCDESC_VERSION_MAJOR 1 -#define SOCDESC_VERSION_MINOR 4 -#define SOCDESC_VERSION_REV 1 - -#define SOCDESC_VERSION__(maj,min,rev) #maj"."#min"."#rev -#define SOCDESC_VERSION_(maj,min,rev) SOCDESC_VERSION__(maj,min,rev) -#define SOCDESC_VERSION SOCDESC_VERSION_(SOCDESC_VERSION_MAJOR,SOCDESC_VERSION_MINOR,SOCDESC_VERSION_REV) - -/** - * Typedef for SoC types: word, address and flags */ -typedef uint32_t soc_addr_t; -typedef uint32_t soc_word_t; -typedef uint32_t soc_reg_flags_t; - -/** SoC error gravity level */ -enum soc_error_level_t -{ - SOC_ERROR_WARNING, - SOC_ERROR_FATAL, -}; - -/** SoC description error */ -struct soc_error_t -{ - soc_error_level_t level; /// level (warning, fatal, ...) - std::string location; /// human description of the location - std::string message; /// message -}; - -/** SoC register generic formula */ -enum soc_reg_formula_type_t -{ - REG_FORMULA_NONE, /// register has no generic formula - REG_FORMULA_STRING, /// register has a generic formula represented by a string -}; - -/** <soc_reg_t>.<flags> values */ -const soc_reg_flags_t REG_HAS_SCT = 1 << 0; /// register SCT variants - -/** SoC register field named value */ -struct soc_reg_field_value_t -{ - std::string name; /// name of the value - soc_word_t value; /// numeric value - std::string desc; /// human description - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC register field */ -struct soc_reg_field_t -{ - std::string name; /// name of the field - std::string desc; /// human description - unsigned first_bit, last_bit; /// bit range of the field - - soc_reg_field_t():first_bit(0), last_bit(31) {} - - /** Return field bitmask in register */ - soc_word_t bitmask() const - { - // WARNING beware of the case where first_bit=0 and last_bit=31 - if(first_bit == 0 && last_bit == 31) - return 0xffffffff; - else - return ((1 << (last_bit - first_bit + 1)) - 1) << first_bit; - } - - /** Extract field value from register value */ - soc_word_t extract(soc_word_t reg_val) const - { - return (reg_val & bitmask()) >> first_bit; - } - - /** Replace the field value in a register value */ - soc_word_t replace(soc_word_t reg_val, soc_word_t field_val) const - { - return (reg_val & ~bitmask()) | ((field_val << first_bit) & bitmask()); - } - - bool is_reserved() const - { - return name.substr(0, 4) == "RSVD" || name.substr(0, 5) == "RSRVD"; - } - - /** Return field value index, or -1 if none */ - int find_value(soc_word_t v) const - { - for(size_t i = 0; i < value.size(); i++) - if(value[i].value == v) - return i; - return -1; - } - - std::vector< soc_reg_field_value_t > value; - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC register address */ -struct soc_reg_addr_t -{ - std::string name; /// actual register name - soc_addr_t addr; /// actual register address (relative to device) - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC register formula */ -struct soc_reg_formula_t -{ - enum soc_reg_formula_type_t type; - std::string string; /// for STRING - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC register */ -struct soc_reg_t -{ - std::string name; /// generic name (for multi registers) or actual name - std::string desc; /// human description - std::vector< soc_reg_addr_t > addr; /// instances of the registers - soc_reg_formula_t formula; /// formula for the instance addresses - soc_reg_flags_t flags; /// ORed value - - std::vector< soc_reg_field_t > field; - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** Soc device address */ -struct soc_dev_addr_t -{ - std::string name; /// actual device name - soc_addr_t addr; - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC device */ -struct soc_dev_t -{ - std::string name; /// generic name (of multi devices) or actual name - std::string long_name; /// human friendly name - std::string desc; /// human description - std::string version; /// description version - std::vector< soc_dev_addr_t > addr; - - std::vector< soc_reg_t > reg; - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** SoC */ -struct soc_t -{ - std::string name; /// codename (rockbox) - std::string desc; /// SoC name - - std::vector< soc_dev_t > dev; - - std::vector< soc_error_t > errors(bool recursive); -}; - -/** Parse a SoC description from a XML file, append it to <soc>. */ -bool soc_desc_parse_xml(const std::string& filename, soc_t& soc); -/** Write a SoC description to a XML file, overwriting it. A file can contain - * multiple Soc descriptions */ -bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc); -/** Normalise a soc description by reordering elemnts so that: - * - devices are sorted by first name - * - registers are sorted by first address - * - fields are sorted by last bit - * - values are sorted by value */ -void soc_desc_normalize(soc_t& soc); -/** Formula parser: try to parse and evaluate a formula to a specific value of 'n' */ -bool soc_desc_evaluate_formula(const std::string& formula, - const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error); - -#endif /* __SOC_DESC__ */ \ No newline at end of file diff --git a/utils/regtools/lib/soc_desc_v1.cpp b/utils/regtools/lib/soc_desc_v1.cpp new file mode 100644 index 0000000000..d585485493 --- /dev/null +++ b/utils/regtools/lib/soc_desc_v1.cpp @@ -0,0 +1,990 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 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_v1.hpp" +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xmlsave.h> +#include <libxml/xmlwriter.h> +#include <stdio.h> +#include <string.h> +#include <algorithm> +#include <cctype> + +namespace soc_desc_v1 +{ + +#define XML_CHAR_TO_CHAR(s) ((const char *)(s)) + +#define BEGIN_ATTR_MATCH(attr) \ + for(xmlAttr *a = attr; a; a = a->next) { + +#define MATCH_X_ATTR(attr_name, hook, ...) \ + if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ + std::string s; \ + if(!parse_text_attr(a, s) || !hook(s, __VA_ARGS__)) \ + return false; \ + } + +#define SOFT_MATCH_X_ATTR(attr_name, hook, ...) \ + if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \ + std::string s; \ + if(parse_text_attr(a, s)) \ + hook(s, __VA_ARGS__); \ + } + +#define SOFT_MATCH_SCT_ATTR(attr_name, var) \ + SOFT_MATCH_X_ATTR(attr_name, validate_sct_hook, var) + +#define MATCH_TEXT_ATTR(attr_name, var) \ + MATCH_X_ATTR(attr_name, validate_string_hook, var) + +#define MATCH_UINT32_ATTR(attr_name, var) \ + MATCH_X_ATTR(attr_name, validate_uint32_hook, var) + +#define MATCH_BITRANGE_ATTR(attr_name, first, last) \ + MATCH_X_ATTR(attr_name, validate_bitrange_hook, first, last) + +#define END_ATTR_MATCH() \ + } + +#define BEGIN_NODE_MATCH(node) \ + for(xmlNode *sub = node; sub; sub = sub->next) { + +#define MATCH_ELEM_NODE(node_name, array, parse_fn) \ + if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ + array.resize(array.size() + 1); \ + if(!parse_fn(sub, array.back())) \ + return false; \ + } + +#define SOFT_MATCH_ELEM_NODE(node_name, array, parse_fn) \ + if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \ + array.resize(array.size() + 1); \ + if(!parse_fn(sub, array.back())) \ + array.pop_back(); \ + } + +#define END_NODE_MATCH() \ + } + +namespace +{ + +bool validate_string_hook(const std::string& str, std::string& s) +{ + s = str; + return true; +} + +bool validate_sct_hook(const std::string& str, soc_reg_flags_t& flags) +{ + if(str == "yes") flags |= REG_HAS_SCT; + else if(str != "no") return false; + return true; +} + +bool validate_unsigned_long_hook(const std::string& str, unsigned long& s) +{ + char *end; + s = strtoul(str.c_str(), &end, 0); + return *end == 0; +} + +bool validate_uint32_hook(const std::string& str, uint32_t& s) +{ + unsigned long u; + if(!validate_unsigned_long_hook(str, u)) return false; +#if ULONG_MAX > 0xffffffff + if(u > 0xffffffff) return false; +#endif + s = u; + return true; +} + +bool validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last) +{ + unsigned long a, b; + size_t sep = str.find(':'); + if(sep == std::string::npos) return false; + if(!validate_unsigned_long_hook(str.substr(0, sep), a)) return false; + if(!validate_unsigned_long_hook(str.substr(sep + 1), b)) return false; + if(a > 31 || b > 31 || a < b) return false; + first = b; + last = a; + return true; +} + +bool parse_text_attr(xmlAttr *attr, std::string& s) +{ + if(attr->children != attr->last) + return false; + if(attr->children->type != XML_TEXT_NODE) + return false; + s = XML_CHAR_TO_CHAR(attr->children->content); + return true; +} + +bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", value.name) + MATCH_UINT32_ATTR("value", value.value) + MATCH_TEXT_ATTR("desc", value.desc) + END_ATTR_MATCH() + + return true; +} + +bool parse_field_elem(xmlNode *node, soc_reg_field_t& field) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", field.name) + MATCH_BITRANGE_ATTR("bitrange", field.first_bit, field.last_bit) + MATCH_TEXT_ATTR("desc", field.desc) + END_ATTR_MATCH() + + BEGIN_NODE_MATCH(node->children) + SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem) + END_NODE_MATCH() + + return true; +} + +bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", addr.name) + MATCH_UINT32_ATTR("addr", addr.addr) + END_ATTR_MATCH() + + return true; +} + +bool parse_reg_formula_elem(xmlNode *node, soc_reg_formula_t& formula) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("string", formula.string) + END_ATTR_MATCH() + + formula.type = REG_FORMULA_STRING; + + return true; +} + +bool parse_add_trivial_addr(const std::string& str, soc_reg_t& reg) +{ + soc_reg_addr_t a; + a.name = reg.name; + if(!validate_uint32_hook(str, a.addr)) + return false; + reg.addr.push_back(a); + return true; +} + +bool parse_reg_elem(xmlNode *node, soc_reg_t& reg) +{ + std::list< soc_reg_formula_t > formulas; + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", reg.name) + SOFT_MATCH_SCT_ATTR("sct", reg.flags) + SOFT_MATCH_X_ATTR("addr", parse_add_trivial_addr, reg) + MATCH_TEXT_ATTR("desc", reg.desc) + END_ATTR_MATCH() + + BEGIN_NODE_MATCH(node->children) + MATCH_ELEM_NODE("addr", reg.addr, parse_reg_addr_elem) + MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem) + MATCH_ELEM_NODE("field", reg.field, parse_field_elem) + END_NODE_MATCH() + + if(formulas.size() > 1) + { + fprintf(stderr, "Only one formula is allowed per register\n"); + return false; + } + if(formulas.size() == 1) + reg.formula = formulas.front(); + + return true; +} + +bool parse_dev_addr_elem(xmlNode *node, soc_dev_addr_t& addr) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", addr.name) + MATCH_UINT32_ATTR("addr", addr.addr) + END_ATTR_MATCH() + + return true; +} + +bool parse_dev_elem(xmlNode *node, soc_dev_t& dev) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", dev.name) + MATCH_TEXT_ATTR("long_name", dev.long_name) + MATCH_TEXT_ATTR("desc", dev.desc) + MATCH_TEXT_ATTR("version", dev.version) + END_ATTR_MATCH() + + BEGIN_NODE_MATCH(node->children) + MATCH_ELEM_NODE("addr", dev.addr, parse_dev_addr_elem) + MATCH_ELEM_NODE("reg", dev.reg, parse_reg_elem) + END_NODE_MATCH() + + return true; +} + +bool parse_soc_elem(xmlNode *node, soc_t& soc) +{ + BEGIN_ATTR_MATCH(node->properties) + MATCH_TEXT_ATTR("name", soc.name) + MATCH_TEXT_ATTR("desc", soc.desc) + END_ATTR_MATCH() + + BEGIN_NODE_MATCH(node->children) + MATCH_ELEM_NODE("dev", soc.dev, parse_dev_elem) + END_NODE_MATCH() + + return true; +} + +bool parse_root_elem(xmlNode *node, soc_t& soc) +{ + std::vector< soc_t > socs; + BEGIN_NODE_MATCH(node) + MATCH_ELEM_NODE("soc", socs, parse_soc_elem) + END_NODE_MATCH() + if(socs.size() != 1) + { + fprintf(stderr, "A description file must contain exactly one soc element\n"); + return false; + } + soc = socs[0]; + return true; +} + +} + +bool parse_xml(const std::string& filename, soc_t& socs) +{ + LIBXML_TEST_VERSION + + xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0); + if(doc == NULL) + return false; + + xmlNodePtr root_element = xmlDocGetRootElement(doc); + bool ret = parse_root_elem(root_element, socs); + + xmlFreeDoc(doc); + + return ret; +} + +namespace +{ + +int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field) +{ +#define SAFE(x) if((x) < 0) return -1; + /* <field> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); + /* desc */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); + /* bitrange */ + SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d", + field.last_bit, field.first_bit)); + /* values */ + for(size_t i = 0; i < field.value.size(); i++) + { + /* <value> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str())); + /* value */ + SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value)); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str())); + /* </value> */ + SAFE(xmlTextWriterEndElement(writer)); + } + /* </field> */ + SAFE(xmlTextWriterEndElement(writer)); +#undef SAFE + return 0; +} + +int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg) +{ +#define SAFE(x) if((x) < 0) return -1; + /* <reg> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str())); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str())); + /* flags */ + if(reg.flags & REG_HAS_SCT) + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes")); + /* formula */ + if(reg.formula.type != REG_FORMULA_NONE) + { + /* <formula> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula")); + switch(reg.formula.type) + { + case REG_FORMULA_STRING: + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string", + BAD_CAST reg.formula.string.c_str())); + break; + default: + break; + } + /* </formula> */ + SAFE(xmlTextWriterEndElement(writer)); + } + /* addresses */ + for(size_t i = 0; i < reg.addr.size(); i++) + { + /* <addr> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str())); + /* addr */ + SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr)); + /* </addr> */ + SAFE(xmlTextWriterEndElement(writer)); + } + /* fields */ + for(size_t i = 0; i < reg.field.size(); i++) + produce_field(writer, reg.field[i]); + /* </reg> */ + SAFE(xmlTextWriterEndElement(writer)); +#undef SAFE + return 0; +} + +int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev) +{ +#define SAFE(x) if((x) < 0) return -1; + /* <dev> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str())); + /* long_name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str())); + /* desc */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str())); + /* version */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str())); + /* addresses */ + for(size_t i = 0; i < dev.addr.size(); i++) + { + /* <addr> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str())); + /* addr */ + SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr)); + /* </addr> */ + SAFE(xmlTextWriterEndElement(writer)); + } + /* registers */ + for(size_t i = 0; i < dev.reg.size(); i++) + produce_reg(writer, dev.reg[i]); + /* </dev> */ + SAFE(xmlTextWriterEndElement(writer)); +#undef SAFE + return 0; +} + +} + +bool produce_xml(const std::string& filename, const soc_t& soc) +{ + LIBXML_TEST_VERSION + + xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0); + if(writer == NULL) + return false; +#define SAFE(x) if((x) < 0) goto Lerr + SAFE(xmlTextWriterSetIndent(writer, 1)); + SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " ")); + /* <xml> */ + SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL)); + /* <soc> */ + SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc")); + /* name */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str())); + /* desc */ + SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str())); + /* devices */ + for(size_t i = 0; i < soc.dev.size(); i++) + SAFE(produce_dev(writer, soc.dev[i])); + /* end <soc> */ + SAFE(xmlTextWriterEndElement(writer)); + /* </xml> */ + SAFE(xmlTextWriterEndDocument(writer)); + xmlFreeTextWriter(writer); + return true; +#undef SAFE +Lerr: + xmlFreeTextWriter(writer); + return false; +} + +namespace +{ + +struct soc_sorter +{ + bool operator()(const soc_dev_t& a, const soc_dev_t& b) const + { + return a.name < b.name; + } + + bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const + { + return a.name < b.name; + } + + bool operator()(const soc_reg_t& a, const soc_reg_t& b) const + { + soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0; + soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0; + return aa < ab; + } + + bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const + { + return a.addr < b.addr; + } + + bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const + { + return a.last_bit > b.last_bit; + } + + bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const + { + return a.value < b.value; + } +}; + +void normalize(soc_reg_field_t& field) +{ + std::sort(field.value.begin(), field.value.end(), soc_sorter()); +} + +void normalize(soc_reg_t& reg) +{ + std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter()); + std::sort(reg.field.begin(), reg.field.end(), soc_sorter()); + for(size_t i = 0; i < reg.field.size(); i++) + normalize(reg.field[i]); +} + +void normalize(soc_dev_t& dev) +{ + std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter()); + std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter()); + for(size_t i = 0; i < dev.reg.size(); i++) + normalize(dev.reg[i]); +} + +} + +void normalize(soc_t& soc) +{ + std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter()); + for(size_t i = 0; i < soc.dev.size(); i++) + normalize(soc.dev[i]); +} + +namespace +{ + soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what) + { + soc_error_t err; + err.level = lvl; + err.location = at; + err.message = what; + return err; + } + + soc_error_t make_warning(std::string at, std::string what) + { + return make_error(SOC_ERROR_WARNING, at, what); + } + + soc_error_t make_fatal(std::string at, std::string what) + { + return make_error(SOC_ERROR_FATAL, at, what); + } + + soc_error_t prefix(soc_error_t err, const std::string& prefix_at) + { + err.location = prefix_at + "." + err.location; + return err; + } + + void add_errors(std::vector< soc_error_t >& errors, + const std::vector< soc_error_t >& new_errors, const std::string& prefix_at) + { + for(size_t i = 0; i < new_errors.size(); i++) + errors.push_back(prefix(new_errors[i], prefix_at)); + } + + std::vector< soc_error_t > no_error() + { + std::vector< soc_error_t > s; + return s; + } + + std::vector< soc_error_t > one_error(const soc_error_t& err) + { + std::vector< soc_error_t > s; + s.push_back(err); + return s; + } + + bool name_valid(char c) + { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || c == '_'; + } + + bool name_valid(const std::string& s) + { + for(size_t i = 0; i < s.size(); i++) + if(!name_valid(s[i])) + return false; + return true; + } +} + +std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive) +{ + (void) recursive; + if(name.size() == 0) + return one_error(make_fatal(name, "empty name")); + else if(!name_valid(name)) + return one_error(make_fatal(name, "invalid name")); + else + return no_error(); +} + +std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive) +{ + std::vector< soc_error_t > err; + std::string at(name); + if(name.size() == 0) + err.push_back(make_fatal(at, "empty name")); + else if(!name_valid(name)) + err.push_back(make_fatal(at, "invalid name")); + if(last_bit > 31) + err.push_back(make_fatal(at, "last bit is greater than 31")); + if(first_bit > last_bit) + err.push_back(make_fatal(at, "last bit is greater than first bit")); + for(size_t i = 0; i < value.size(); i++) + { + for(size_t j = 0; j < value.size(); j++) + { + if(i == j) + continue; + if(value[i].name == value[j].name) + err.push_back(prefix(make_fatal(value[i].name, + "there are several values with the same name"), at)); + if(value[i].value == value[j].value) + err.push_back(prefix(make_warning(value[i].name, + "there are several values with the same value"), at)); + } + if(value[i].value > (bitmask() >> first_bit)) + err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name)); + if(recursive) + add_errors(err, value[i].errors(true), at); + } + return err; +} + +std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive) +{ + (void) recursive; + if(name.size() == 0) + return one_error(make_fatal("", "empty name")); + else if(!name_valid(name)) + return one_error(make_fatal(name, "invalid name")); + else + return no_error(); +} + +std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive) +{ + (void) recursive; + if(type == REG_FORMULA_STRING && string.size() == 0) + return one_error(make_fatal("", "empty string formula")); + else + return no_error(); +} + +namespace +{ + +bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b) +{ + return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit); +} + +} + +std::vector< soc_error_t > soc_reg_t::errors(bool recursive) +{ + std::vector< soc_error_t > err; + std::string at(name); + if(name.size() == 0) + err.push_back(make_fatal(at, "empty name")); + else if(!name_valid(name)) + err.push_back(make_fatal(at, "invalid name")); + for(size_t i = 0; i < addr.size(); i++) + { + for(size_t j = 0; j < addr.size(); j++) + { + if(i == j) + continue; + if(addr[i].name == addr[j].name) + err.push_back(prefix(make_fatal(addr[i].name, + "there are several instances with the same name"), at)); + if(addr[i].addr == addr[j].addr) + err.push_back(prefix(make_fatal(addr[i].name, + "there are several instances with the same address"), at)); + } + if(recursive) + add_errors(err, addr[i].errors(true), at); + } + if(recursive) + add_errors(err, formula.errors(true), at); + for(size_t i = 0; i < field.size(); i++) + { + for(size_t j = 0; j < field.size(); j++) + { + if(i == j) + continue; + if(field[i].name == field[j].name) + err.push_back(prefix(make_fatal(field[i].name, + "there are several fields with the same name"), at)); + if(field_overlap(field[i], field[j])) + err.push_back(prefix(make_fatal(field[i].name, + "there are overlapping fields"), at)); + } + if(recursive) + add_errors(err, field[i].errors(true), at); + } + return err; +} + +std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive) +{ + (void) recursive; + if(name.size() == 0) + return one_error(make_fatal("", "empty name")); + else if(!name_valid(name)) + return one_error(make_fatal(name, "invalid name")); + else + return no_error(); +} + +std::vector< soc_error_t > soc_dev_t::errors(bool recursive) +{ + std::vector< soc_error_t > err; + std::string at(name); + if(name.size() == 0) + err.push_back(make_fatal(at, "empty name")); + else if(!name_valid(name)) + err.push_back(make_fatal(at, "invalid name")); + for(size_t i = 0; i < addr.size(); i++) + { + for(size_t j = 0; j < addr.size(); j++) + { + if(i == j) + continue; + if(addr[i].name == addr[j].name) + err.push_back(prefix(make_fatal(addr[i].name, + "there are several instances with the same name"), at)); + if(addr[i].addr == addr[j].addr) + err.push_back(prefix(make_fatal(addr[i].name, + "there are several instances with the same address"), at)); + } + if(recursive) + add_errors(err, addr[i].errors(true), at); + } + for(size_t i = 0; i < reg.size(); i++) + { + for(size_t j = 0; j < reg.size(); j++) + { + if(i == j) + continue; + if(reg[i].name == reg[j].name) + err.push_back(prefix(make_fatal(reg[i].name, + "there are several registers with the same name"), at)); + } + if(recursive) + add_errors(err, reg[i].errors(true), at); + } + return err; +} + +std::vector< soc_error_t > soc_t::errors(bool recursive) +{ + std::vector< soc_error_t > err; + std::string at(name); + for(size_t i = 0; i < dev.size(); i++) + { + for(size_t j = 0; j < dev.size(); j++) + { + if(i == j) + continue; + if(dev[i].name == dev[j].name) + err.push_back(prefix(make_fatal(dev[i].name, + "there are several devices with the same name"), at)); + } + if(recursive) + add_errors(err, dev[i].errors(true), at); + } + return err; +} + +namespace +{ + +struct formula_evaluator +{ + std::string formula; + size_t pos; + std::string error; + + bool err(const char *fmt, ...) + { + char buffer[256]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer,sizeof(buffer), fmt, args); + va_end(args); + error = buffer; + return false; + } + + formula_evaluator(const std::string& s):pos(0) + { + for(size_t i = 0; i < s.size(); i++) + if(!isspace(s[i])) + formula.push_back(s[i]); + } + + void adv() + { + pos++; + } + + char cur() + { + return end() ? 0 : formula[pos]; + } + + bool end() + { + return pos >= formula.size(); + } + + bool parse_digit(char c, int basis, soc_word_t& res) + { + c = tolower(c); + if(isdigit(c)) + { + res = c - '0'; + return true; + } + if(basis == 16 && isxdigit(c)) + { + res = c + 10 - 'a'; + return true; + } + return err("invalid digit '%c'", c); + } + + bool parse_signed(soc_word_t& res) + { + char op = cur(); + if(op == '+' || op == '-') + { + adv(); + if(!parse_signed(res)) + return false; + if(op == '-') + res *= -1; + return true; + } + else if(op == '(') + { + adv(); + if(!parse_expression(res)) + return false; + if(cur() != ')') + return err("expected ')', got '%c'", cur()); + adv(); + return true; + } + else if(isdigit(op)) + { + res = op - '0'; + adv(); + int basis = 10; + if(op == '0' && cur() == 'x') + { + basis = 16; + adv(); + } + soc_word_t digit = 0; + while(parse_digit(cur(), basis, digit)) + { + res = res * basis + digit; + adv(); + } + return true; + } + else if(isalpha(op) || op == '_') + { + std::string name; + while(isalnum(cur()) || cur() == '_') + { + name.push_back(cur()); + adv(); + } + return get_variable(name, res); + } + else + return err("express signed expression, got '%c'", op); + } + + bool parse_term(soc_word_t& res) + { + if(!parse_signed(res)) + return false; + while(cur() == '*' || cur() == '/' || cur() == '%') + { + char op = cur(); + adv(); + soc_word_t tmp; + if(!parse_signed(tmp)) + return false; + if(op == '*') + res *= tmp; + else if(tmp != 0) + res = op == '/' ? res / tmp : res % tmp; + else + return err("division by 0"); + } + return true; + } + + bool parse_expression(soc_word_t& res) + { + if(!parse_term(res)) + return false; + while(!end() && (cur() == '+' || cur() == '-')) + { + char op = cur(); + adv(); + soc_word_t tmp; + if(!parse_term(tmp)) + return false; + if(op == '+') + res += tmp; + else + res -= tmp; + } + return true; + } + + bool parse(soc_word_t& res, std::string& _error) + { + bool ok = parse_expression(res); + if(ok && !end()) + err("unexpected character '%c'", cur()); + _error = error; + return ok && end(); + } + + virtual bool get_variable(std::string name, soc_word_t& res) + { + return err("unknown variable '%s'", name.c_str()); + } +}; + +struct my_evaluator : public formula_evaluator +{ + const std::map< std::string, soc_word_t>& var; + + my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var) + :formula_evaluator(formula), var(_var) {} + + virtual bool get_variable(std::string name, soc_word_t& res) + { + std::map< std::string, soc_word_t>::const_iterator it = var.find(name); + if(it == var.end()) + return formula_evaluator::get_variable(name, res); + else + { + res = it->second; + return true; + } + } +}; + +} + +bool evaluate_formula(const std::string& formula, + const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error) +{ + my_evaluator e(formula, var); + return e.parse(result, error); +} + +/** WARNING we need to call xmlInitParser() to init libxml2 but it needs to + * called from the main thread, which is a super strong requirement, so do it + * using a static constructor */ +namespace +{ +class xml_parser_init +{ +public: + xml_parser_init() + { + xmlInitParser(); + } +}; + +xml_parser_init __xml_parser_init; +} + +} // soc_desc_v1 diff --git a/utils/regtools/qeditor/backend.cpp b/utils/regtools/qeditor/backend.cpp index 7091a0c77f..cbaff7370d 100644 --- a/utils/regtools/qeditor/backend.cpp +++ b/utils/regtools/qeditor/backend.cpp @@ -35,8 +35,8 @@ SocFile::SocFile() SocFile::SocFile(const QString& filename) :m_filename(filename) { - m_valid = soc_desc_parse_xml(filename.toStdString(), m_soc); - soc_desc_normalize(m_soc); + m_valid = parse_xml(filename.toStdString(), m_soc); + normalize(m_soc); } bool SocFile::IsValid() diff --git a/utils/regtools/qeditor/backend.h b/utils/regtools/qeditor/backend.h index b2f53c9ed5..934c1c359b 100644 --- a/utils/regtools/qeditor/backend.h +++ b/utils/regtools/qeditor/backend.h @@ -29,7 +29,11 @@ #ifdef HAVE_HWSTUB #include "hwstub.h" #endif -#include "soc_desc.hpp" +#include "soc_desc_v1.hpp" + +/* we don't want to import the entire soc_desc except for a few selected + * pieces */ +using namespace soc_desc_v1; class IoBackend : public QObject { diff --git a/utils/regtools/qeditor/mainwindow.cpp b/utils/regtools/qeditor/mainwindow.cpp index 9cea728d6e..b44306d701 100644 --- a/utils/regtools/qeditor/mainwindow.cpp +++ b/utils/regtools/qeditor/mainwindow.cpp @@ -161,12 +161,14 @@ void MainWindow::OnQuit() void MainWindow::OnAbout() { + QString soc_desc_ver = QString("%1.%2.%3").arg(MAJOR_VERSION) + .arg(MINOR_VERSION).arg(REVISION_VERSION); QMessageBox::about(this, "About", "<h1>QEditor</h1>" "<h2>Version "APP_VERSION"</h2>" "<p>Written by Amaury Pouly</p>" "<p>Libraries:</p>" - "<ul><li>soc_desc: "SOCDESC_VERSION"</li>" + "<ul><li>soc_desc: " + soc_desc_ver + "</li>" #ifdef HAVE_HWSTUB "<li>hwstub: "HWSTUB_VERSION"</li>" #else diff --git a/utils/regtools/qeditor/qeditor.pro b/utils/regtools/qeditor/qeditor.pro index eecc0aba3f..265cc93171 100644 --- a/utils/regtools/qeditor/qeditor.pro +++ b/utils/regtools/qeditor/qeditor.pro @@ -5,14 +5,14 @@ HEADERS += mainwindow.h backend.h regtab.h analyser.h settings.h \ SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp \ std_analysers.cpp settings.cpp utils.cpp regdisplaypanel.cpp regedit.cpp LIBS += -L../lib/ -lsocdesc -lxml2 -INCLUDEPATH += ../lib/ ../../hwstub/lib +INCLUDEPATH += ../include/ ../../hwstub/lib DEPENDPATH += ../ libsocdesc.commands = cd ../lib && make QMAKE_EXTRA_TARGETS += libsocdesc PRE_TARGETDEPS += libsocdesc -VERSION = 2.0.4 +VERSION = 2.1.0 DEFINES += APP_VERSION=\\\"$$VERSION\\\" diff --git a/utils/regtools/qeditor/regedit.cpp b/utils/regtools/qeditor/regedit.cpp index 851e054eb0..8b4bfb7c49 100644 --- a/utils/regtools/qeditor/regedit.cpp +++ b/utils/regtools/qeditor/regedit.cpp @@ -421,7 +421,7 @@ void RegEditPanel::OnFormulaGenerate(bool checked) map["n"] = n; std::string err; soc_word_t res; - if(!soc_desc_evaluate_formula(formula, map, res, err)) + if(!evaluate_formula(formula, map, res, err)) { qDebug() << "Cannot evaluator " << QString::fromStdString(formula) << "for n=" << n << ": " << QString::fromStdString(err); @@ -1051,8 +1051,8 @@ void RegEdit::OnNew() bool RegEdit::SaveSocFile(const QString& filename) { - soc_desc_normalize(m_cur_socfile.GetSoc()); - if(!soc_desc_produce_xml(filename.toStdString(), m_cur_socfile.GetSoc())) + normalize(m_cur_socfile.GetSoc()); + if(!produce_xml(filename.toStdString(), m_cur_socfile.GetSoc())) { QMessageBox::warning(this, "The description was not saved", "There was an error when saving the file"); diff --git a/utils/regtools/qeditor/utils.cpp b/utils/regtools/qeditor/utils.cpp index e4a872ed49..2ee1bc6db5 100644 --- a/utils/regtools/qeditor/utils.cpp +++ b/utils/regtools/qeditor/utils.cpp @@ -1004,7 +1004,7 @@ void MyTextEditor::SetReadOnly(bool en) if(en) m_toolbar->hide(); else - m_toolbar->hide(); + m_toolbar->show(); m_edit->setReadOnly(en); } diff --git a/utils/regtools/swiss_knife.cpp b/utils/regtools/swiss_knife.cpp new file mode 100644 index 0000000000..eaa2519a27 --- /dev/null +++ b/utils/regtools/swiss_knife.cpp @@ -0,0 +1,612 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2014 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 <stdio.h> +#include <stdlib.h> +#include <map> +#include <set> +#include <cstring> + +using namespace soc_desc; + +void print_context(const error_context_t& ctx) +{ + for(size_t j = 0; j < ctx.count(); j++) + { + error_t e = ctx.get(j); + switch(e.level()) + { + case error_t::INFO: printf("[INFO]"); break; + case error_t::WARNING: printf("[WARN]"); break; + case error_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()); + } +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_value_t& in, enum_t& out, error_context_t& ctx) +{ + out.name = in.name; + out.desc = in.desc; + out.value = in.value; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_reg_field_t& in, field_t& out, error_context_t& ctx) +{ + out.name = in.name; + out.desc = in.desc; + out.pos = in.first_bit; + out.width = in.last_bit - in.first_bit + 1; + out.enum_.resize(in.value.size()); + for(size_t i = 0; i < in.value.size(); i++) + if(!convert_v1_to_v2(in.value[i], out.enum_[i], ctx)) + return false; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_reg_addr_t& in, instance_t& out, error_context_t& ctx) +{ + out.name = in.name; + out.type = instance_t::SINGLE; + out.addr = in.addr; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_reg_formula_t& in, range_t& out, error_context_t& ctx) +{ + out.type = range_t::FORMULA; + out.formula = in.string; + out.variable = "n"; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_reg_t& in, node_t& out, error_context_t& ctx, + std::string _loc) +{ + std::string loc = _loc + "." + in.name; + out.name = in.name; + out.desc = in.desc; + if(in.formula.type == soc_desc_v1::REG_FORMULA_NONE) + { + out.instance.resize(in.addr.size()); + for(size_t i = 0; i < in.addr.size(); i++) + if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx)) + return false; + } + else + { + out.instance.resize(1); + out.instance[0].name = in.name; + out.instance[0].type = instance_t::RANGE; + out.instance[0].range.first = 0; + out.instance[0].range.count = in.addr.size(); + /* check if formula is base/stride */ + bool is_stride = true; + soc_word_t base = 0, stride = 0; + if(in.addr.size() <= 1) + { + ctx.add(error_t(error_t::WARNING, loc, + "register uses a formula but has only one instance")); + is_stride = false; + } + else + { + base = in.addr[0].addr; + stride = in.addr[1].addr - base; + for(size_t i = 0; i < in.addr.size(); i++) + if(base + i * stride != in.addr[i].addr) + is_stride = false; + } + + if(is_stride) + { + ctx.add(error_t(error_t::INFO, loc, "promoted formula to base/stride")); + out.instance[0].range.type = range_t::STRIDE; + out.instance[0].range.base = base; + out.instance[0].range.stride = stride; + } + else if(!convert_v1_to_v2(in.formula, out.instance[0].range, ctx)) + return false; + } + out.register_.resize(1); + out.register_[0].width = 32; + out.register_[0].field.resize(in.field.size()); + for(size_t i = 0; i < in.field.size(); i++) + if(!convert_v1_to_v2(in.field[i], out.register_[0].field[i], ctx)) + return false; + /* sct */ + if(in.flags & soc_desc_v1::REG_HAS_SCT) + { + out.node.resize(1); + out.node[0].name = "SCT"; + out.node[0].instance.resize(3); + const char *names[3] = {"SET", "CLR", "TOG"}; + for(size_t i = 0; i < 3; i++) + { + out.node[0].instance[i].name = names[i]; + out.node[0].instance[i].type = instance_t::SINGLE; + out.node[0].instance[i].addr = 4 + i *4; + } + } + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_dev_addr_t& in, instance_t& out, error_context_t& ctx) +{ + out.name = in.name; + out.type = instance_t::SINGLE; + out.addr = in.addr; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_dev_t& in, node_t& out, error_context_t& ctx, + std::string _loc) +{ + std::string loc = _loc + "." + in.name; + if(!in.version.empty()) + ctx.add(error_t(error_t::INFO, loc, "dropped version")); + out.name = in.name; + out.title = in.long_name; + out.desc = in.desc; + out.instance.resize(in.addr.size()); + for(size_t i = 0; i < in.addr.size(); i++) + if(!convert_v1_to_v2(in.addr[i], out.instance[i], ctx)) + return false; + out.node.resize(in.reg.size()); + for(size_t i = 0; i < in.reg.size(); i++) + if(!convert_v1_to_v2(in.reg[i], out.node[i], ctx, loc)) + return false; + return true; +} + +bool convert_v1_to_v2(const soc_desc_v1::soc_t& in, soc_t& out, error_context_t& ctx) +{ + out.name = in.name; + out.title = in.desc; + out.node.resize(in.dev.size()); + for(size_t i = 0; i < in.dev.size(); i++) + if(!convert_v1_to_v2(in.dev[i], out.node[i], ctx, in.name)) + return false; + return true; +} + +int do_convert(int argc, char **argv) +{ + if(argc != 2) + return printf("convert mode expects two arguments\n"); + soc_desc_v1::soc_t soc; + if(!soc_desc_v1::parse_xml(argv[0], soc)) + return printf("cannot read file '%s'\n", argv[0]); + error_context_t ctx; + soc_t new_soc; + if(!convert_v1_to_v2(soc, new_soc, ctx)) + { + print_context(ctx); + return printf("cannot convert from v1 to v2\n"); + } + if(!produce_xml(argv[1], new_soc, ctx)) + { + print_context(ctx); + return printf("cannot write file '%s'\n", argv[1]); + } + print_context(ctx); + return 0; +} + +int do_read(int argc, char **argv) +{ + for(int i = 0; i < argc; i++) + { + error_context_t ctx; + soc_t soc; + bool ret = parse_xml(argv[i], soc, ctx); + if(ctx.count() != 0) + printf("In file %s:\n", argv[i]); + print_context(ctx); + if(!ret) + { + printf("cannot parse file '%s'\n", argv[i]); + continue; + } + } + return 0; +} + +int do_eval(int argc, char **argv) +{ + std::map< std::string, soc_word_t > map; + for(int i = 0; i < argc; i++) + { + std::string formula(argv[i]); + soc_word_t result; + if(strcmp(argv[i], "--var") == 0) + { + if(i + 1 >= argc) + break; + i++; + std::string str(argv[i]); + size_t pos = str.find('='); + if(pos == std::string::npos) + { + printf("invalid variable string '%s'\n", str.c_str()); + continue; + } + std::string name = str.substr(0, pos); + std::string val = str.substr(pos + 1); + char *end; + soc_word_t v = strtoul(val.c_str(), &end, 0); + if(*end) + { + printf("invalid variable string '%s'\n", str.c_str()); + continue; + } + printf("%s = %#lx\n", name.c_str(), (unsigned long)v); + map[name] = v; + continue; + } + error_context_t ctx; + if(!evaluate_formula(formula, map, result, "", ctx)) + { + print_context(ctx); + printf("cannot parse '%s'\n", formula.c_str()); + } + else + printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); + } + return 0; +} + +int do_write(int argc, char **argv) +{ + if(argc != 2) + return printf("write mode expects two arguments\n"); + soc_t soc; + error_context_t ctx; + if(!parse_xml(argv[0], soc, ctx)) + { + print_context(ctx); + return printf("cannot read file '%s'\n", argv[0]); + } + if(!produce_xml(argv[1], soc, ctx)) + { + print_context(ctx); + return printf("cannot write file '%s'\n", argv[1]); + } + print_context(ctx); + return 0; +} + +void check_name(const std::string& path, const std::string& name, error_context_t& ctx) +{ + if(name.empty()) + ctx.add(error_t(error_t::FATAL, path, "name is empty")); + for(size_t i = 0; i < name.size(); i++) + if(!isalnum(name[i]) && name[i] != '_') + ctx.add(error_t(error_t::FATAL, path, "name '" + name + + "' must only contain alphanumeric characters or '_'")); +} + +void check_instance(const std::string& _path, const instance_t& inst, error_context_t& ctx) +{ + std::string path = _path + "." + inst.name; + check_name(path, inst.name, ctx); + if(inst.type == instance_t::RANGE) + { + if(inst.range.type == range_t::FORMULA) + { + check_name(path + ".<formula variable>", inst.range.variable, ctx); + /* try to parse formula */ + std::map< std::string, soc_word_t> var; + var[inst.range.variable] = inst.range.first; + soc_word_t res; + if(!evaluate_formula(inst.range.formula, var, res, path + ".<formula>", ctx)) + ctx.add(error_t(error_t::FATAL, path + ".<formula>", + "cannot evaluate formula")); + } + } +} + +void check_field(const std::string& _path, const field_t& field, error_context_t& ctx) +{ + std::string path = _path + "." + field.name; + check_name(path, field.name, ctx); + if(field.width == 0) + ctx.add(error_t(error_t::WARNING, path, "field has width 0")); + soc_word_t max = field.bitmask() >> field.pos; + std::set< std::string > names; + std::map< soc_word_t, std::string > map; + for(size_t i = 0; i < field.enum_.size(); i++) + { + soc_word_t v = field.enum_[i].value; + std::string n = field.enum_[i].name; + std::string path_ = path + "." + n; + check_name(path_, n, ctx); + if(v > max) + ctx.add(error_t(error_t::FATAL, path_, "value does not fit into the field")); + if(names.find(n) != names.end()) + ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in enums")); + names.insert(n); + if(map.find(v) != map.end()) + ctx.add(error_t(error_t::WARNING, path, "'" + n + "' and '" + map[v] + "' have the same value")); + map[v] = n; + } +} + +void check_register(const std::string& _path, const soc_desc::register_t& reg, error_context_t& ctx) +{ + std::string path = _path + ".<register>"; + if(reg.width != 8 && reg.width != 16 && reg.width != 32) + ctx.add(error_t(error_t::WARNING, path, "width is not 8, 16 or 32")); + for(size_t i = 0; i < reg.field.size(); i++) + check_field(path, reg.field[i], ctx); + std::set< std::string > names; + soc_word_t bitmap = 0; + for(size_t i = 0; i < reg.field.size(); i++) + { + std::string n = reg.field[i].name; + if(names.find(n) != names.end()) + ctx.add(error_t(error_t::FATAL, path, "duplicate name '" + n + "' in fields")); + if(reg.field[i].pos + reg.field[i].width > reg.width) + ctx.add(error_t(error_t::FATAL, path, "field '" + n + "' does not fit into the register")); + names.insert(n); + if(bitmap & reg.field[i].bitmask()) + { + /* find the duplicate to ease debugging */ + for(size_t j = 0; j < i; j++) + if(reg.field[j].bitmask() & reg.field[i].bitmask()) + ctx.add(error_t(error_t::FATAL, path, "overlap between fields '" + + reg.field[j].name + "' and '" + n + "'")); + } + bitmap |= reg.field[i].bitmask(); + } +} + +void check_nodes(const std::string& path, const std::vector< node_t >& nodes, + error_context_t& ctx); + +void check_node(const std::string& _path, const node_t& node, error_context_t& ctx) +{ + std::string path = _path + "." + node.name; + check_name(_path, node.name, ctx); + if(node.instance.empty()) + ctx.add(error_t(error_t::FATAL, path, "subnode with no instances")); + for(size_t j = 0; j < node.instance.size(); j++) + check_instance(path, node.instance[j], ctx); + for(size_t i = 0; i < node.register_.size(); i++) + check_register(path, node.register_[i], ctx); + check_nodes(path, node.node, ctx); +} + +void check_nodes(const std::string& path, const std::vector< node_t >& nodes, + error_context_t& ctx) +{ + for(size_t i = 0; i < nodes.size(); i++) + check_node(path, nodes[i], ctx); + /* gather all instance names */ + std::set< std::string > names; + for(size_t i = 0; i < nodes.size(); i++) + for(size_t j = 0; j < nodes[i].instance.size(); j++) + { + std::string n = nodes[i].instance[j].name; + if(names.find(n) != names.end()) + ctx.add(error_t(error_t::FATAL, path, "duplicate instance name '" + + n + "' in subnodes")); + names.insert(n); + } + /* gather all node names */ + names.clear(); + for(size_t i = 0; i < nodes.size(); i++) + { + std::string n = nodes[i].name; + if(names.find(n) != names.end()) + ctx.add(error_t(error_t::FATAL, path, "duplicate node name '" + n + + "' in subnodes")); + names.insert(n); + } +} + +void do_check(soc_t& soc, error_context_t& ctx) +{ + check_name(soc.name, soc.name, ctx); + check_nodes(soc.name, soc.node, ctx); +} + +int do_check(int argc, char **argv) +{ + for(int i = 0; i < argc; i++) + { + error_context_t ctx; + soc_t soc; + bool ret = parse_xml(argv[i], soc, ctx); + if(ret) + do_check(soc, ctx); + if(ctx.count() != 0) + printf("In file %s:\n", argv[i]); + print_context(ctx); + if(!ret) + { + printf("cannot parse file '%s'\n", argv[i]); + continue; + } + } + return 0; +} + +const unsigned DUMP_NODES = 1 << 0; +const unsigned DUMP_INSTANCES = 1 << 1; +const unsigned DUMP_VERBOSE = 1 << 2; +const unsigned DUMP_REGISTERS = 1 << 3; + +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"); +} + +void print_inst(node_inst_t inst, bool end = 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(end) + printf(" @ %#x\n", inst.addr()); +} + +void print_reg(register_ref_t reg, unsigned flags) +{ + if(!(flags & DUMP_REGISTERS)) + return; + node_ref_t node = reg.node(); + soc_desc::register_t *r = reg.get(); + print_path(node, false); + printf(":width=%u\n", (unsigned)r->width); + std::vector< field_ref_t > fields = reg.fields(); + for(size_t i = 0; i < fields.size(); i++) + { + field_t *f = fields[i].get(); + print_path(node, false); + if(f->width == 1) + printf(":[%u]=", (unsigned)f->pos); + else + printf(":[%u-%u]=", (unsigned)(f->pos + f->width - 1), (unsigned)f->pos); + printf("%s\n", f->name.c_str()); + } +} + +void do_dump(node_ref_t node, unsigned flags) +{ + print_path(node); + if(node.reg().node() == node) + print_reg(node.reg(), flags); + std::vector< node_ref_t > children = node.children(); + for(size_t i = 0; i < children.size(); i++) + do_dump(children[i], flags); +} + +void do_dump(node_inst_t inst, unsigned flags) +{ + print_inst(inst); + std::vector< node_inst_t > children = inst.children(); + for(size_t i = 0; i < children.size(); i++) + do_dump(children[i], flags); +} + +void do_dump(soc_t& soc, unsigned flags) +{ + soc_ref_t ref(&soc); + if(flags & DUMP_NODES) + do_dump(ref.root(), flags); + if(flags & DUMP_INSTANCES) + do_dump(ref.root_inst(), flags); +} + +int do_dump(int argc, char **argv) +{ + unsigned flags = 0; + int i = 0; + for(; i < argc; i++) + { + if(strcmp(argv[i], "--nodes") == 0) + flags |= DUMP_NODES; + else if(strcmp(argv[i], "--instances") == 0) + flags |= DUMP_INSTANCES; + else if(strcmp(argv[i], "--verbose") == 0) + flags |= DUMP_VERBOSE; + else if(strcmp(argv[i], "--registers") == 0) + flags |= DUMP_REGISTERS; + else + break; + } + if(i == argc) + { + printf("you must specify at least one file\n"); + return 1; + } + for(; i < argc; i++) + { + error_context_t ctx; + soc_t soc; + bool ret = parse_xml(argv[i], soc, ctx); + if(ret) + do_dump(soc, flags); + if(ctx.count() != 0) + printf("In file %s:\n", argv[i]); + print_context(ctx); + if(!ret) + { + printf("cannot parse file '%s'\n", argv[i]); + continue; + } + } + return 0; +} + +void usage() +{ + printf("usage: swiss_knife <mode> [options]\n"); + printf("modes:\n"); + printf(" read <files...>\n"); + printf(" write <read file> <write file>\n"); + printf(" eval [<formula>|--var <name>=<val>]...\n"); + printf(" convert <input file> <output file>\n"); + printf(" check <files...>\n"); + printf(" dump [--nodes] [--instances] [--registers] [--verbose] <files...>\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + if(argc < 2) + usage(); + std::string mode = argv[1]; + if(mode == "read") + return do_read(argc - 2, argv + 2); + else if(mode == "write") + return do_write(argc - 2, argv + 2); + else if(mode == "eval") + return do_eval(argc - 2, argv + 2); + else if(mode == "convert") + return do_convert(argc - 2, argv + 2); + else if(mode == "check") + return do_check(argc - 2, argv + 2); + else if(mode == "dump") + return do_dump(argc - 2, argv + 2); + else + usage(); + return 0; +} \ No newline at end of file diff --git a/utils/regtools/tester.cpp b/utils/regtools/tester.cpp deleted file mode 100644 index 1beba5fe9b..0000000000 --- a/utils/regtools/tester.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2012 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 <stdio.h> -#include <stdlib.h> -#include <map> -#include <cstring> - -template< typename T > -bool build_map(const char *type, const std::vector< T >& vec, - std::map< std::string, size_t >& map) -{ - for(size_t i = 0; i < vec.size(); i++) - { - if(map.find(vec[i].name) != map.end()) - { - printf("soc has duplicate %s '%s'\n", type, vec[i].name.c_str()); - return false; - } - map[vec[i].name] = i; - } - return true; -} - -template< typename T > -bool build_map(const char *type, const std::vector< T >& a, const std::vector< T >& b, - std::vector< std::pair< size_t, size_t > >& m) -{ - std::map< std::string, size_t > ma, mb; - if(!build_map(type, a, ma) || !build_map(type, b, mb)) - return false; - std::map< std::string, size_t >::iterator it; - for(it = ma.begin(); it != ma.end(); ++it) - { - if(mb.find(it->first) == mb.end()) - { - printf("%s '%s' exists in only one file\n", type, it->first.c_str()); - return false; - } - m.push_back(std::make_pair(it->second, mb[it->first])); - } - for(it = mb.begin(); it != mb.end(); ++it) - { - if(ma.find(it->first) == ma.end()) - { - printf("%s '%s' exists in only one file\n", type, it->first.c_str()); - return false; - } - } - return true; -} - -bool compare_value(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, - const soc_reg_field_t& field, const soc_reg_field_value_t& a, const soc_reg_field_value_t& b) -{ - if(a.value != b.value) - { - printf("register field value '%s.%s.%s.%s.%s' have different values\n", soc.name.c_str(), - dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str()); - return false; - } - if(a.desc != b.desc) - { - printf("register field value '%s.%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(), - dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str()); - return false; - } - return true; -} - -bool compare_field(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, - const soc_reg_field_t& a, const soc_reg_field_t& b) -{ - if(a.first_bit != b.first_bit || a.last_bit != b.last_bit) - { - printf("register address '%s.%s.%s.%s' have different bit ranges\n", soc.name.c_str(), - dev.name.c_str(), reg.name.c_str(), a.name.c_str()); - return false; - } - if(a.desc != b.desc) - { - printf("register address '%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(), - dev.name.c_str(), reg.name.c_str(), a.name.c_str()); - return false; - } - /* values */ - std::vector< std::pair< size_t, size_t > > map; - if(!build_map("field value", a.value, b.value, map)) - return false; - for(size_t i = 0; i < map.size(); i++) - if(!compare_value(soc, dev, reg, a, a.value[map[i].first], b.value[map[i].second])) - return false; - return true; -} - -bool compare_reg_addr(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, - const soc_reg_addr_t& a, const soc_reg_addr_t& b) -{ - if(a.addr != b.addr) - { - printf("register address '%s.%s.%s.%s' have different values\n", soc.name.c_str(), - dev.name.c_str(), reg.name.c_str(), a.name.c_str()); - return false; - } - else - return true; -} - -bool compare_reg(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& a, - const soc_reg_t& b) -{ - if(a.desc != b.desc) - { - printf("register '%s.%s.%s' have different descriptions\n", soc.name.c_str(), - dev.name.c_str(), a.name.c_str()); - return false; - } - if(a.flags != b.flags) - { - printf("device '%s.%s.%s' have different flags\n", soc.name.c_str(), - dev.name.c_str(), a.name.c_str()); - return false; - } - if(a.formula.type != b.formula.type) - { - printf("device '%s.%s.%s' have different formula types\n", soc.name.c_str(), - dev.name.c_str(), a.name.c_str()); - return false; - } - if(a.formula.string != b.formula.string) - { - printf("device '%s.%s.%s' have different formula string\n", soc.name.c_str(), - dev.name.c_str(), a.name.c_str()); - return false; - } - /* addresses */ - std::vector< std::pair< size_t, size_t > > map; - if(!build_map("register address", a.addr, b.addr, map)) - return false; - for(size_t i = 0; i < map.size(); i++) - if(!compare_reg_addr(soc, dev, a, a.addr[map[i].first], b.addr[map[i].second])) - return false; - /* field */ - map.clear(); - if(!build_map("field", a.field, b.field, map)) - return false; - for(size_t i = 0; i < map.size(); i++) - if(!compare_field(soc, dev, a, a.field[map[i].first], b.field[map[i].second])) - return false; - return true; -} - -bool compare_dev_addr(const soc_t& soc, const soc_dev_t& dev, const soc_dev_addr_t& a, - const soc_dev_addr_t& b) -{ - if(a.addr != b.addr) - { - printf("device address '%s.%s.%s' have different values\n", soc.name.c_str(), - dev.name.c_str(), a.name.c_str()); - return false; - } - else - return true; -} - -bool compare_dev(const soc_t& soc, const soc_dev_t& a, const soc_dev_t& b) -{ - if(a.long_name != b.long_name) - { - printf("device '%s.%s' have different long names\n", soc.name.c_str(), - a.name.c_str()); - return false; - } - if(a.desc != b.desc) - { - printf("device '%s.%s' have different descriptions\n", soc.name.c_str(), - a.name.c_str()); - return false; - } - if(a.version != b.version) - { - printf("device '%s.%s' have different versions\n", soc.name.c_str(), - a.name.c_str()); - return false; - } - /* addresses */ - std::vector< std::pair< size_t, size_t > > map; - if(!build_map("device address", a.addr, b.addr, map)) - return false; - for(size_t i = 0; i < map.size(); i++) - if(!compare_dev_addr(soc, a, a.addr[map[i].first], b.addr[map[i].second])) - return false; - /* reg */ - map.clear(); - if(!build_map("register", a.reg, b.reg, map)) - return false; - for(size_t i = 0; i < map.size(); i++) - if(!compare_reg(soc, a, a.reg[map[i].first], b.reg[map[i].second])) - return false; - return true; -} - -bool compare_soc(const soc_t& a, const soc_t& b) -{ - if(a.name != b.name) - { - return printf("soc have different names\n"); - return false; - } - if(a.desc != b.desc) - { - printf("soc '%s' have different descriptions\n", a.name.c_str()); - return false; - } - std::vector< std::pair< size_t, size_t > > map; - if(!build_map("device", a.dev, b.dev, map)) - return false; - for(size_t i = 0; i< map.size(); i++) - if(!compare_dev(a, a.dev[map[i].first], b.dev[map[i].second])) - return false; - return true; -} - -int do_compare(int argc, char **argv) -{ - if(argc != 2) - return printf("compare mode expects two arguments\n"); - soc_t soc[2]; - if(!soc_desc_parse_xml(argv[0], soc[0])) - return printf("cannot read file '%s'\n", argv[0]); - if(!soc_desc_parse_xml(argv[1], soc[1])) - return printf("cannot read file '%s'\n", argv[1]); - if(compare_soc(soc[0], soc[1])) - printf("Files are identical.\n"); - return 0; -} - -int do_write(int argc, char **argv) -{ - if(argc != 2) - return printf("write mode expects two arguments\n"); - soc_t soc; - if(!soc_desc_parse_xml(argv[0], soc)) - return printf("cannot read file '%s'\n", argv[0]); - if(!soc_desc_produce_xml(argv[1], soc)) - return printf("cannot write file '%s'\n", argv[1]); - return 0; -} - -int do_check(int argc, char **argv) -{ - for(int i = 0; i < argc; i++) - { - soc_t soc; - if(!soc_desc_parse_xml(argv[i], soc)) - { - printf("cannot read file '%s'\n", argv[i]); - continue; - } - printf("[%s]\n", argv[i]); - std::vector< soc_error_t > errs = soc.errors(true); - for(size_t i = 0; i < errs.size(); i++) - { - const soc_error_t& e = errs[i]; - switch(e.level) - { - case SOC_ERROR_WARNING: printf("[WARN ] "); break; - case SOC_ERROR_FATAL: printf("[FATAL] "); break; - default: printf("[ UNK ] "); break; - } - printf("%s: %s\n", e.location.c_str(), e.message.c_str()); - } - } - return 0; -} - -int do_eval(int argc, char **argv) -{ - std::map< std::string, soc_word_t > map; - for(int i = 0; i < argc; i++) - { - std::string error; - std::string formula(argv[i]); - soc_word_t result; - if(strcmp(argv[i], "--var") == 0) - { - if(i + 1 >= argc) - break; - i++; - std::string str(argv[i]); - size_t pos = str.find('='); - if(pos == std::string::npos) - { - printf("invalid variable string '%s'\n", str.c_str()); - continue; - } - std::string name = str.substr(0, pos); - std::string val = str.substr(pos + 1); - char *end; - soc_word_t v = strtoul(val.c_str(), &end, 0); - if(*end) - { - printf("invalid variable string '%s'\n", str.c_str()); - continue; - } - printf("%s = %#lx\n", name.c_str(), (unsigned long)v); - map[name] = v; - continue; - } - if(!soc_desc_evaluate_formula(formula, map, result, error)) - printf("error: %s\n", error.c_str()); - else - printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); - } - return 0; -} - -void usage() -{ - printf("usage: tester <mode> [options]\n"); - printf("modes:\n"); - printf(" compare <desc file> <desc file>\n"); - printf(" write <read file> <write file>\n"); - printf(" check <files...>\n"); - printf(" eval [<formula>|--var <name>=<val>]...\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - if(argc < 2) - usage(); - std::string mode = argv[1]; - if(mode == "compare") - return do_compare(argc - 2, argv + 2); - else if(mode == "write") - return do_write(argc - 2, argv + 2); - else if(mode == "check") - return do_check(argc - 2, argv + 2); - else if(mode == "eval") - return do_eval(argc - 2, argv + 2); - else - usage(); - return 0; -} \ No newline at end of file diff --git a/utils/regtools/tester_v1.cpp b/utils/regtools/tester_v1.cpp new file mode 100644 index 0000000000..3ece431531 --- /dev/null +++ b/utils/regtools/tester_v1.cpp @@ -0,0 +1,365 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 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_v1.hpp" +#include <stdio.h> +#include <stdlib.h> +#include <map> +#include <cstring> + +using namespace soc_desc_v1; + +template< typename T > +bool build_map(const char *type, const std::vector< T >& vec, + std::map< std::string, size_t >& map) +{ + for(size_t i = 0; i < vec.size(); i++) + { + if(map.find(vec[i].name) != map.end()) + { + printf("soc has duplicate %s '%s'\n", type, vec[i].name.c_str()); + return false; + } + map[vec[i].name] = i; + } + return true; +} + +template< typename T > +bool build_map(const char *type, const std::vector< T >& a, const std::vector< T >& b, + std::vector< std::pair< size_t, size_t > >& m) +{ + std::map< std::string, size_t > ma, mb; + if(!build_map(type, a, ma) || !build_map(type, b, mb)) + return false; + std::map< std::string, size_t >::iterator it; + for(it = ma.begin(); it != ma.end(); ++it) + { + if(mb.find(it->first) == mb.end()) + { + printf("%s '%s' exists in only one file\n", type, it->first.c_str()); + return false; + } + m.push_back(std::make_pair(it->second, mb[it->first])); + } + for(it = mb.begin(); it != mb.end(); ++it) + { + if(ma.find(it->first) == ma.end()) + { + printf("%s '%s' exists in only one file\n", type, it->first.c_str()); + return false; + } + } + return true; +} + +bool compare_value(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, + const soc_reg_field_t& field, const soc_reg_field_value_t& a, const soc_reg_field_value_t& b) +{ + if(a.value != b.value) + { + printf("register field value '%s.%s.%s.%s.%s' have different values\n", soc.name.c_str(), + dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str()); + return false; + } + if(a.desc != b.desc) + { + printf("register field value '%s.%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(), + dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str()); + return false; + } + return true; +} + +bool compare_field(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, + const soc_reg_field_t& a, const soc_reg_field_t& b) +{ + if(a.first_bit != b.first_bit || a.last_bit != b.last_bit) + { + printf("register address '%s.%s.%s.%s' have different bit ranges\n", soc.name.c_str(), + dev.name.c_str(), reg.name.c_str(), a.name.c_str()); + return false; + } + if(a.desc != b.desc) + { + printf("register address '%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(), + dev.name.c_str(), reg.name.c_str(), a.name.c_str()); + return false; + } + /* values */ + std::vector< std::pair< size_t, size_t > > map; + if(!build_map("field value", a.value, b.value, map)) + return false; + for(size_t i = 0; i < map.size(); i++) + if(!compare_value(soc, dev, reg, a, a.value[map[i].first], b.value[map[i].second])) + return false; + return true; +} + +bool compare_reg_addr(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg, + const soc_reg_addr_t& a, const soc_reg_addr_t& b) +{ + if(a.addr != b.addr) + { + printf("register address '%s.%s.%s.%s' have different values\n", soc.name.c_str(), + dev.name.c_str(), reg.name.c_str(), a.name.c_str()); + return false; + } + else + return true; +} + +bool compare_reg(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& a, + const soc_reg_t& b) +{ + if(a.desc != b.desc) + { + printf("register '%s.%s.%s' have different descriptions\n", soc.name.c_str(), + dev.name.c_str(), a.name.c_str()); + return false; + } + if(a.flags != b.flags) + { + printf("device '%s.%s.%s' have different flags\n", soc.name.c_str(), + dev.name.c_str(), a.name.c_str()); + return false; + } + if(a.formula.type != b.formula.type) + { + printf("device '%s.%s.%s' have different formula types\n", soc.name.c_str(), + dev.name.c_str(), a.name.c_str()); + return false; + } + if(a.formula.string != b.formula.string) + { + printf("device '%s.%s.%s' have different formula string\n", soc.name.c_str(), + dev.name.c_str(), a.name.c_str()); + return false; + } + /* addresses */ + std::vector< std::pair< size_t, size_t > > map; + if(!build_map("register address", a.addr, b.addr, map)) + return false; + for(size_t i = 0; i < map.size(); i++) + if(!compare_reg_addr(soc, dev, a, a.addr[map[i].first], b.addr[map[i].second])) + return false; + /* field */ + map.clear(); + if(!build_map("field", a.field, b.field, map)) + return false; + for(size_t i = 0; i < map.size(); i++) + if(!compare_field(soc, dev, a, a.field[map[i].first], b.field[map[i].second])) + return false; + return true; +} + +bool compare_dev_addr(const soc_t& soc, const soc_dev_t& dev, const soc_dev_addr_t& a, + const soc_dev_addr_t& b) +{ + if(a.addr != b.addr) + { + printf("device address '%s.%s.%s' have different values\n", soc.name.c_str(), + dev.name.c_str(), a.name.c_str()); + return false; + } + else + return true; +} + +bool compare_dev(const soc_t& soc, const soc_dev_t& a, const soc_dev_t& b) +{ + if(a.long_name != b.long_name) + { + printf("device '%s.%s' have different long names\n", soc.name.c_str(), + a.name.c_str()); + return false; + } + if(a.desc != b.desc) + { + printf("device '%s.%s' have different descriptions\n", soc.name.c_str(), + a.name.c_str()); + return false; + } + if(a.version != b.version) + { + printf("device '%s.%s' have different versions\n", soc.name.c_str(), + a.name.c_str()); + return false; + } + /* addresses */ + std::vector< std::pair< size_t, size_t > > map; + if(!build_map("device address", a.addr, b.addr, map)) + return false; + for(size_t i = 0; i < map.size(); i++) + if(!compare_dev_addr(soc, a, a.addr[map[i].first], b.addr[map[i].second])) + return false; + /* reg */ + map.clear(); + if(!build_map("register", a.reg, b.reg, map)) + return false; + for(size_t i = 0; i < map.size(); i++) + if(!compare_reg(soc, a, a.reg[map[i].first], b.reg[map[i].second])) + return false; + return true; +} + +bool compare_soc(const soc_t& a, const soc_t& b) +{ + if(a.name != b.name) + { + return printf("soc have different names\n"); + return false; + } + if(a.desc != b.desc) + { + printf("soc '%s' have different descriptions\n", a.name.c_str()); + return false; + } + std::vector< std::pair< size_t, size_t > > map; + if(!build_map("device", a.dev, b.dev, map)) + return false; + for(size_t i = 0; i< map.size(); i++) + if(!compare_dev(a, a.dev[map[i].first], b.dev[map[i].second])) + return false; + return true; +} + +int do_compare(int argc, char **argv) +{ + if(argc != 2) + return printf("compare mode expects two arguments\n"); + soc_t soc[2]; + if(!parse_xml(argv[0], soc[0])) + return printf("cannot read file '%s'\n", argv[0]); + if(!parse_xml(argv[1], soc[1])) + return printf("cannot read file '%s'\n", argv[1]); + if(compare_soc(soc[0], soc[1])) + printf("Files are identical.\n"); + return 0; +} + +int do_write(int argc, char **argv) +{ + if(argc != 2) + return printf("write mode expects two arguments\n"); + soc_t soc; + if(!parse_xml(argv[0], soc)) + return printf("cannot read file '%s'\n", argv[0]); + if(!produce_xml(argv[1], soc)) + return printf("cannot write file '%s'\n", argv[1]); + return 0; +} + +int do_check(int argc, char **argv) +{ + for(int i = 0; i < argc; i++) + { + soc_t soc; + if(!parse_xml(argv[i], soc)) + { + printf("cannot read file '%s'\n", argv[i]); + continue; + } + printf("[%s]\n", argv[i]); + std::vector< soc_error_t > errs = soc.errors(true); + for(size_t i = 0; i < errs.size(); i++) + { + const soc_error_t& e = errs[i]; + switch(e.level) + { + case SOC_ERROR_WARNING: printf("[WARN ] "); break; + case SOC_ERROR_FATAL: printf("[FATAL] "); break; + default: printf("[ UNK ] "); break; + } + printf("%s: %s\n", e.location.c_str(), e.message.c_str()); + } + } + return 0; +} + +int do_eval(int argc, char **argv) +{ + std::map< std::string, soc_word_t > map; + for(int i = 0; i < argc; i++) + { + std::string error; + std::string formula(argv[i]); + soc_word_t result; + if(strcmp(argv[i], "--var") == 0) + { + if(i + 1 >= argc) + break; + i++; + std::string str(argv[i]); + size_t pos = str.find('='); + if(pos == std::string::npos) + { + printf("invalid variable string '%s'\n", str.c_str()); + continue; + } + std::string name = str.substr(0, pos); + std::string val = str.substr(pos + 1); + char *end; + soc_word_t v = strtoul(val.c_str(), &end, 0); + if(*end) + { + printf("invalid variable string '%s'\n", str.c_str()); + continue; + } + printf("%s = %#lx\n", name.c_str(), (unsigned long)v); + map[name] = v; + continue; + } + if(!evaluate_formula(formula, map, result, error)) + printf("error: %s\n", error.c_str()); + else + printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result); + } + return 0; +} + +void usage() +{ + printf("usage: tester <mode> [options]\n"); + printf("modes:\n"); + printf(" compare <desc file> <desc file>\n"); + printf(" write <read file> <write file>\n"); + printf(" check <files...>\n"); + printf(" eval [<formula>|--var <name>=<val>]...\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + if(argc < 2) + usage(); + std::string mode = argv[1]; + if(mode == "compare") + return do_compare(argc - 2, argv + 2); + else if(mode == "write") + return do_write(argc - 2, argv + 2); + else if(mode == "check") + return do_check(argc - 2, argv + 2); + else if(mode == "eval") + return do_eval(argc - 2, argv + 2); + else + usage(); + return 0; +} \ No newline at end of file -- cgit v1.2.3