From 1cada1f8339d6b5f8506277f80e62aaef77ab774 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Sun, 14 Dec 2014 11:53:55 +0100 Subject: [PATCH] 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 | 402 ++-- utils/regtools/Makefile | 5 +- utils/regtools/desc/regs-example-v1.xml | 91 + utils/regtools/desc/regs-example.xml | 153 ++ utils/regtools/desc/{XML.txt => spec-1.0.txt} | 0 utils/regtools/desc/spec-2.0.txt | 373 ++++ .../{headergen.cpp => headergen_v1.cpp} | 6 +- utils/regtools/include/soc_desc.hpp | 384 ++++ .../soc_desc.hpp => include/soc_desc_v1.hpp} | 32 +- utils/regtools/lib/Makefile | 9 +- utils/regtools/lib/formula.cpp | 214 +++ utils/regtools/lib/soc_desc.cpp | 1662 +++++++++-------- 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 => tester_v1.cpp} | 16 +- 22 files changed, 4039 insertions(+), 941 deletions(-) create mode 100644 utils/regtools/desc/regs-example-v1.xml create mode 100644 utils/regtools/desc/regs-example.xml rename utils/regtools/desc/{XML.txt => spec-1.0.txt} (100%) create mode 100644 utils/regtools/desc/spec-2.0.txt rename utils/regtools/{headergen.cpp => headergen_v1.cpp} (99%) create mode 100644 utils/regtools/include/soc_desc.hpp rename utils/regtools/{lib/soc_desc.hpp => include/soc_desc_v1.hpp} (89%) create mode 100644 utils/regtools/lib/formula.cpp create mode 100644 utils/regtools/lib/soc_desc_v1.cpp create mode 100644 utils/regtools/swiss_knife.cpp rename utils/regtools/{tester.cpp => tester_v1.cpp} (97%) 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, f->pos); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "pos"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, field.first_bit); - lua_setfield(g_lua, -2, "first_bit"); + lua_pushunsigned(g_lua, f->width); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "width"); + /* lua stack: ... */ - lua_pushunsigned(g_lua, field.last_bit); - lua_setfield(g_lua, -2, "last_bit"); - - 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: ... */ + + /** 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); + lua_pushunsigned(g_lua, f->pos); + lua_pushunsigned(g_lua, local_bitmask); + /* lua stack: ... */ + lua_pushvalue(g_lua, -4); + /* lua stack: ... */ + lua_pushunsigned(g_lua, arg[i]); + /* lua stack: <'wsct'> ... */ + lua_pushcclosure(g_lua, my_lua_write_field, 5); + /* lua stack: ... */ + lua_setfield(g_lua, -2, name[i]); + /* lua stack: ... */ + } + + /** create values */ + for(size_t i = 0; i < f->enum_.size(); i++) + { + 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: */ +} + +/* lua stack on entry/exit: */ +void my_lua_create_reg(soc_addr_t addr, soc_desc::register_ref_t reg) +{ + 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); - 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) - { - lua_pushunsigned(g_lua, addr + 4); - 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, "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"); - } - - for(size_t i = 0; i < field.value.size(); i++) - { - lua_pushunsigned(g_lua, field.value[i].value); - lua_setfield(g_lua, -2, field.value[i].name.c_str()); - } -} - -void my_lua_create_reg(soc_addr_t addr, size_t index, const soc_reg_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); - lua_pushcclosure(g_lua, my_lua_read_reg, 1); - lua_setfield(g_lua, -2, "read"); - - lua_pushunsigned(g_lua, addr + reg.addr[index].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 + reg.addr[index].addr + 8); - lua_pushcclosure(g_lua, my_lua_write_reg, 1); - lua_setfield(g_lua, -2, "clr"); - - 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_pushunsigned(g_lua, addr + reg.addr[index].addr); - lua_pushunsigned(g_lua, 's'); + 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_setfield(g_lua, -2, "set"); - - 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"); - - 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"); + /* lua stack: */ + lua_setfield(g_lua, -2, name[i]); + /* 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()); - } + /** 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]); } -void my_lua_create_dev(size_t index, const soc_dev_t& dev) +/* lua stack on entry/exit: */ +void my_lua_create_instances(const std::vector< soc_desc::node_inst_t >& inst) { - lua_newtable(g_lua); - - lua_pushstring(g_lua, dev.addr[index].name.c_str()); - lua_setfield(g_lua, -2, "name"); - - lua_pushunsigned(g_lua, dev.addr[index].addr); - lua_setfield(g_lua, -2, "addr"); - - for(size_t i = 0; i < dev.reg.size(); i++) + for(size_t i = 0; i < inst.size(); i++) { - bool table = dev.reg[i].addr.size() > 1; - if(table) - lua_newtable(g_lua); - else - lua_pushnil(g_lua); - - for(size_t k = 0; k < dev.reg[i].addr.size(); k++) + /** if the instance is indexed, find the instance table, otherwise create it */ + if(inst[i].is_indexed()) { - my_lua_create_reg(dev.addr[index].addr, k, dev.reg[i]); - if(table) + /** 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_pushinteger(g_lua, k); - lua_pushvalue(g_lua, -2); - lua_settable(g_lua, -4); + 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_setfield(g_lua, -3, dev.reg[i].addr[k].name.c_str()); + lua_pushinteger(g_lua, inst[i].index()); + /* lua stack: */ } - if(table) - lua_setfield(g_lua, -2, dev.reg[i].name.c_str()); - else + /** create a new table for the instance */ + lua_newtable(g_lua); + /* lua stack: [ ] */ + + /** 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: ... */ + + lua_pushstring(g_lua, inst[i].node().get()->desc.c_str()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "desc"); + /* lua stack: ... */ + + lua_pushstring(g_lua, inst[i].node().get()->title.c_str()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "title"); + /* lua stack: ... */ + + lua_pushunsigned(g_lua, inst[i].addr()); + /* lua stack: ... */ + lua_setfield(g_lua, -2, "addr"); + /* lua stack: ... */ + + /** create register */ + my_lua_create_reg(inst[i].addr(), inst[i].node().reg()); + + /** create subinstances */ + my_lua_create_instances(inst[i].children()); + /* lua stack: [ ] */ + + if(inst[i].is_indexed()) + { + /* lua stack: */ + lua_settable(g_lua, -3); + /* lua stack: */ lua_pop(g_lua, 1); + } + else + { + /* 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/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/XML.txt b/utils/regtools/desc/spec-1.0.txt similarity index 100% rename from utils/regtools/desc/XML.txt rename to utils/regtools/desc/spec-1.0.txt 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_v1.cpp similarity index 99% rename from utils/regtools/headergen.cpp rename to utils/regtools/headergen_v1.cpp index 046e8b2fd8..7b38366d5d 100644 --- a/utils/regtools/headergen.cpp +++ b/utils/regtools/headergen_v1.cpp @@ -18,7 +18,7 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "soc_desc.hpp" +#include "soc_desc_v1.hpp" #include #include #include @@ -28,6 +28,8 @@ #include #include +using namespace soc_desc_v1; + #define HEADERGEN_VERSION "2.1.8" #define error(...) do{ fprintf(stderr, __VA_ARGS__); exit(1); } while(0) @@ -721,7 +723,7 @@ int main(int argc, char **argv) for(int i = optind; i < argc - 1; i++) { soc_t s; - if(!soc_desc_parse_xml(argv[i], s)) + if(!parse_xml(argv[i], s)) { printf("Cannot parse %s\n", argv[i]); return 1; 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/lib/soc_desc.hpp b/utils/regtools/include/soc_desc_v1.hpp similarity index 89% rename from utils/regtools/lib/soc_desc.hpp rename to utils/regtools/include/soc_desc_v1.hpp index bb8eb4ba8d..33368e88d4 100644 --- a/utils/regtools/lib/soc_desc.hpp +++ b/utils/regtools/include/soc_desc_v1.hpp @@ -18,8 +18,8 @@ * KIND, either express or implied. * ****************************************************************************/ -#ifndef __SOC_DESC__ -#define __SOC_DESC__ +#ifndef __SOC_DESC_V1__ +#define __SOC_DESC_V1__ #include #include @@ -45,13 +45,12 @@ * 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 +namespace soc_desc_v1 +{ -#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) +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 */ @@ -211,18 +210,21 @@ struct soc_t }; /** Parse a SoC description from a XML file, append it to . */ -bool soc_desc_parse_xml(const std::string& filename, soc_t& soc); +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 soc_desc_produce_xml(const std::string& filename, const soc_t& soc); +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 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); +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); -#endif /* __SOC_DESC__ */ \ No newline at end of file +} // 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; + std::ostringstream oss; + oss << "line " << node->line; + return oss.str(); +} + +std::string xml_loc(xmlAttr *attr) +{ + return xml_loc(attr->parent); +} + +template +bool add_error(error_context_t& ctx, error_t::level_t lvl, T *node, + const std::string& msg) +{ + ctx.add(error_t(lvl, xml_loc(node), msg)); + return false; +} + +template +bool add_fatal(error_context_t& ctx, T *node, const std::string& msg) +{ + return add_error(ctx, error_t::FATAL, node, msg); +} + +template +bool add_warning(error_context_t& ctx, T *node, const std::string& msg) +{ + return add_error(ctx, error_t::WARNING, node, msg); +} + +bool parse_wrong_version_error(xmlNode *node, error_context_t& ctx) +{ + std::ostringstream oss; + oss << "unknown version, only version " << MAJOR_VERSION << " is supported"; + return add_fatal(ctx, node, oss.str()); +} + +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_not_unique_attr_error(xmlAttr *attr, error_context_t& ctx) +{ + 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()); +} + +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()); +} + +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_conflict_error(xmlNode *node, const char *name1, const char *name2, + error_context_t& ctx) +{ + 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()); +} + +bool parse_not_text_error(xmlNode *node, error_context_t& ctx) +{ + return add_fatal(ctx, node, "this is not a text element"); +} + +bool parse_not_text_attr_error(xmlAttr *attr, error_context_t& ctx) +{ + return add_fatal(ctx, attr, "this is not a text attribute"); +} + +bool parse_text_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx) +{ + name = XML_CHAR_TO_CHAR(content); return true; } -bool validate_unsigned_long_hook(const std::string& str, unsigned long& s) +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; +} + +template +bool parse_unsigned_text(U *node, T& res, xmlChar *content, error_context_t& ctx) { 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; + 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 validate_bitrange_hook(const std::string& str, unsigned& first, unsigned& last) +template +bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx) { - 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 parse_unsigned_text(node, res, content, ctx); } -bool parse_text_attr(xmlAttr *attr, std::string& s) +template +bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx) +{ + return parse_unsigned_text(attr, res, content, ctx); +} + +bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx) { 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 parse_not_text_attr_error(attr, ctx); + res = attr->children->content; return true; } -bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value) +bool parse_text_attr(xmlAttr *attr, std::string& res, xmlChar *content, 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() - + res = XML_CHAR_TO_CHAR(content); return true; } -bool parse_field_elem(xmlNode *node, soc_reg_field_t& field) +bool parse_enum_elem(xmlNode *node, enum_t& reg, 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() - + bool ret = true; + bool has_name = false, has_value = false, has_desc = false; BEGIN_NODE_MATCH(node->children) - SOFT_MATCH_ELEM_NODE("value", field.value, parse_value_elem) + 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() - - return true; + CHECK_HAS(node, "name", has_name, ctx) + CHECK_HAS(node, "value", has_value, ctx) + return ret; } -bool parse_reg_addr_elem(xmlNode *node, soc_reg_addr_t& addr) +bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx) { - 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() - + 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", reg.addr, parse_reg_addr_elem) - MATCH_ELEM_NODE("formula", formulas, parse_reg_formula_elem) - MATCH_ELEM_NODE("field", reg.field, parse_field_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; +} - if(formulas.size() > 1) +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_formula_elem(xmlNode *node, range_t& range, error_context_t& ctx) +{ + bool ret = true; + bool has_var = false; + BEGIN_ATTR_MATCH(node->properties) + 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_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 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() + 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, "Only one formula is allowed per register\n"); + ctx.add(error_t(error_t::FATAL, xml_loc(node), "no version attribute, is this a v1 file ?")); 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; + if(ver != MAJOR_VERSION) + return parse_wrong_version_error(node, ctx); BEGIN_NODE_MATCH(node) - MATCH_ELEM_NODE("soc", socs, parse_soc_elem) + MATCH_UNIQUE_ELEM_NODE("soc", soc, has_soc, parse_soc_elem, ctx) 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; + 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) 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 "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(xmlTextWriterEndElement(writer)); -#undef SAFE - return 0; -} +#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_reg(xmlTextWriterPtr writer, const soc_reg_t& reg) +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 "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) + /* */ + 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(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base)); + SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride)); + } + /* */ + 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 -{ - 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) +soc_ref_t::soc_ref_t(soc_t *soc):m_soc(soc) { - 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) +bool soc_ref_t::valid() const { - 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]); + return get() != 0; } +soc_t *soc_ref_t::get() const +{ + return m_soc; } -void soc_desc_normalize(soc_t& soc) +bool soc_ref_t::operator==(const soc_ref_t& ref) 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_soc == ref.m_soc; } -namespace +node_ref_t soc_ref_t::root() const { - 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; - } + return node_ref_t(*this); } -std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive) +node_inst_t soc_ref_t::root_inst() 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")); - else - return no_error(); + return node_inst_t(*this); } -std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive) +/** + * node_ref_t */ + +node_ref_t::node_ref_t(soc_ref_t soc):m_soc(soc) { - 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) +node_ref_t::node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path) + :m_soc(soc), m_path(path) { - (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) +node_ref_t::node_ref_t() { - (void) recursive; - if(type == REG_FORMULA_STRING && string.size() == 0) - return one_error(make_fatal("", "empty string formula")); - else - return no_error(); +} + +bool node_ref_t::valid() const +{ + return (m_soc.valid() && is_root()) || get() != 0; +} + +bool node_ref_t::is_root() const +{ + return m_path.empty(); } namespace { -bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b) +std::vector< node_t > *get_children(node_ref_t node) { - return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit); + if(node.is_root()) + return &node.soc().get()->node; + node_t *n = node.get(); + return n == 0 ? 0 : &n->node; +} + +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; +} + +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 > soc_reg_t::errors(bool recursive) +/* NOTE: valid() is implemented using get() != 0, so don't use it in get() ! */ +node_t *node_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++) + 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++) { - 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); + n = get_child(nodes, m_path[i]); + if(n == 0) + return 0; + nodes = &n->node; } - 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; + return n; } -std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive) +soc_ref_t node_ref_t::soc() 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")); + return m_soc; +} + +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); +} + +register_ref_t node_ref_t::reg() const +{ + 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_dev_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")); - 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; + /* 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< soc_error_t > soc_t::errors(bool recursive) +std::vector< node_ref_t > node_ref_t::children() const { - std::vector< soc_error_t > err; - std::string at(name); - for(size_t i = 0; i < dev.size(); i++) + 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 < 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); + std::vector< soc_id_t > path = m_path; + path.push_back((*children)[i].id); + nodes.push_back(node_ref_t(m_soc, path)); } - return err; + 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) + { + path.clear(); + return path; + } + path.push_back(n->name); + nodes = &n->node; + } + return path; +} + +std::string node_ref_t::name() const +{ + node_t *n = get(); + return n == 0 ? "" : n->name; +} + +bool node_ref_t::operator==(const node_ref_t& ref) const +{ + return m_soc == ref.m_soc && m_path == ref.m_path; +} + +/** + * register_ref_t + */ + +register_ref_t::register_ref_t(node_ref_t node) + :m_node(node) +{ +} + +register_ref_t::register_ref_t() +{ +} + +bool register_ref_t::valid() const +{ + return get() != 0; +} + +register_t *register_ref_t::get() const +{ + node_t *n = m_node.get(); + if(n == 0 || n->register_.empty()) + return 0; + return &n->register_[0]; +} + +node_ref_t register_ref_t::node() const +{ + return m_node; +} + +std::vector< field_ref_t > register_ref_t::fields() const +{ + 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; +} + +field_ref_t register_ref_t::field(const std::string& name) const +{ + 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(); +} + +/** + * field_ref_t + */ + +field_ref_t::field_ref_t(register_ref_t reg, soc_id_t id) + :m_reg(reg), m_id(id) +{ +} + +field_ref_t::field_ref_t() +{ +} + +bool field_ref_t::valid() const +{ + return get() != 0; +} + +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; +} + +register_ref_t field_ref_t::reg() const +{ + return m_reg; +} + +/** + * node_inst_t + */ + namespace { -struct formula_evaluator -{ - std::string formula; - size_t pos; - std::string error; +const size_t INST_NO_INDEX = std::numeric_limits<std::size_t>::max(); - 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; +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; - } - - formula_evaluator(const std::string& s):pos(0) + switch(range.type) { - 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'; + 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'; - 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)) + 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; - if(op == '-') - res *= -1; + addr += res; 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)) + 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(); - } - - 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 +bool get_inst_addr(instance_t *inst, size_t index, soc_addr_t& addr) { - 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) + if(inst == 0) + return false; + switch(inst->type) { - 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; + case instance_t::SINGLE: + if(index != INST_NO_INDEX) + return false; + addr += inst->addr; return true; - } + case instance_t::RANGE: + if(index == INST_NO_INDEX) + return false; + return get_inst_addr(inst->range, index, addr); + default: + return false; } -}; +} } -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) +node_inst_t::node_inst_t(soc_ref_t soc) + :m_node(soc.root()) { - my_evaluator e(formula, var); - return e.parse(result, error); +} + +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) +{ +} + +node_inst_t::node_inst_t() +{ +} + +bool node_inst_t::valid() const +{ + return is_root() || get() != 0; +} + +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++) + { + node_t& node = (*nodes)[i]; + child_node.m_path.push_back(node.id); + for(size_t j = 0; j < node.instance.size(); j++) + { + 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; +} + +size_t node_inst_t::index() const +{ + 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_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_v1.cpp similarity index 97% rename from utils/regtools/tester.cpp rename to utils/regtools/tester_v1.cpp index 1beba5fe9b..3ece431531 100644 --- a/utils/regtools/tester.cpp +++ b/utils/regtools/tester_v1.cpp @@ -18,12 +18,14 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "soc_desc.hpp" +#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) @@ -244,9 +246,9 @@ 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])) + if(!parse_xml(argv[0], soc[0])) return printf("cannot read file '%s'\n", argv[0]); - if(!soc_desc_parse_xml(argv[1], soc[1])) + 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"); @@ -258,9 +260,9 @@ 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)) + if(!parse_xml(argv[0], soc)) return printf("cannot read file '%s'\n", argv[0]); - if(!soc_desc_produce_xml(argv[1], soc)) + if(!produce_xml(argv[1], soc)) return printf("cannot write file '%s'\n", argv[1]); return 0; } @@ -270,7 +272,7 @@ 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)) + if(!parse_xml(argv[i], soc)) { printf("cannot read file '%s'\n", argv[i]); continue; @@ -325,7 +327,7 @@ int do_eval(int argc, char **argv) map[name] = v; continue; } - if(!soc_desc_evaluate_formula(formula, map, result, error)) + 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);