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
This commit is contained in:
parent
c8d3638b9e
commit
1cada1f833
22 changed files with 4039 additions and 941 deletions
|
@ -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)
|
||||
|
|
|
@ -28,11 +28,14 @@
|
|||
#include <readline/history.h>
|
||||
#include <lua.hpp>
|
||||
#include <unistd.h>
|
||||
#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: <reg table> */
|
||||
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: <field table> <reg table> */
|
||||
|
||||
lua_pushstring(g_lua, field.name.c_str());
|
||||
/** create various characteristics */
|
||||
lua_pushstring(g_lua, f->name.c_str());
|
||||
/* lua stack: <name> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "name");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
lua_pushunsigned(g_lua, addr);
|
||||
/* lua stack: <addr> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "addr");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
lua_pushboolean(g_lua, sct);
|
||||
lua_setfield(g_lua, -2, "sct");
|
||||
lua_pushunsigned(g_lua, f->pos);
|
||||
/* lua stack: <pos> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "pos");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
lua_pushunsigned(g_lua, field.first_bit);
|
||||
lua_setfield(g_lua, -2, "first_bit");
|
||||
lua_pushunsigned(g_lua, f->width);
|
||||
/* lua stack: <width> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "width");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
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: <bm> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "bitmask");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
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: <local_bm> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "local_bitmask");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
/** 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: <local_bm> <pos> <addr> <field table> ... */
|
||||
lua_pushcclosure(g_lua, my_lua_read_field, 3);
|
||||
/* lua stack: <my_lua_read_field> <field table> ... */
|
||||
lua_setfield(g_lua, -2, "read");
|
||||
/* lua stack: <field table> ... */
|
||||
|
||||
/** 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: <local_bm> <pos> <addr> <field table> ... */
|
||||
lua_pushvalue(g_lua, -4);
|
||||
/* lua stack: <field table> <local_bm> <pos> <addr> <field table> ... */
|
||||
lua_pushunsigned(g_lua, arg[i]);
|
||||
/* lua stack: <'wsct'> <field table> <local_bm> <pos> <addr> <field table> ... */
|
||||
lua_pushcclosure(g_lua, my_lua_write_field, 5);
|
||||
/* lua stack: <my_lua_write_field> <field table> ... */
|
||||
lua_setfield(g_lua, -2, name[i]);
|
||||
/* lua stack: <field table> ... */
|
||||
}
|
||||
|
||||
/** create values */
|
||||
for(size_t i = 0; i < f->enum_.size(); i++)
|
||||
{
|
||||
lua_pushunsigned(g_lua, f->enum_[i].value);
|
||||
/* lua stack: <value> <field table> ... */
|
||||
lua_setfield(g_lua, -2, f->enum_[i].name.c_str());
|
||||
/* lua stack: <field table> ... */
|
||||
}
|
||||
|
||||
/** register field */
|
||||
lua_setfield(g_lua, -2, f->name.c_str());
|
||||
/* lua stack: <reg table> */
|
||||
}
|
||||
|
||||
/* lua stack on entry/exit: <inst table> */
|
||||
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: <addr> <inst table> */
|
||||
lua_pushcclosure(g_lua, my_lua_read_reg, 1);
|
||||
/* lua stack: <my_lua_read_reg> <inst table> */
|
||||
lua_setfield(g_lua, -2, "read");
|
||||
/* lua stack: <inst table> */
|
||||
|
||||
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: <addr> <inst table> */
|
||||
lua_pushcclosure(g_lua, my_lua_write_reg, 1);
|
||||
/* lua stack: <my_lua_read_reg> <inst table> */
|
||||
lua_setfield(g_lua, -2, "write");
|
||||
/* lua stack: <inst table> */
|
||||
|
||||
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: <addr> <inst table> */
|
||||
lua_pushunsigned(g_lua, arg[i]);
|
||||
/* lua stack: <'s'/'c'/'t'> <addr> <inst table> */
|
||||
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: <my_lua_sct_reg> <inst table> */
|
||||
lua_setfield(g_lua, -2, name[i]);
|
||||
/* lua stack: <inst table> */
|
||||
}
|
||||
|
||||
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: <parent table> */
|
||||
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: <index table> <parent table> */
|
||||
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: <index table> <parent table> */
|
||||
lua_pushvalue(g_lua, -1);
|
||||
/* lua stack: <index table> <index table> <parent table> */
|
||||
lua_setfield(g_lua, -3, inst[i].name().c_str());
|
||||
/* lua stack: <index table> <parent table> */
|
||||
}
|
||||
lua_setfield(g_lua, -3, dev.reg[i].addr[k].name.c_str());
|
||||
lua_pushinteger(g_lua, inst[i].index());
|
||||
/* lua stack: <index> <index table> <parent table> */
|
||||
}
|
||||
|
||||
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: <instance table> [<index> <index table>] <parent table> */
|
||||
|
||||
/** create name and desc fields */
|
||||
lua_pushstring(g_lua, inst[i].node().get()->name.c_str());
|
||||
/* lua stack: <node name> <instance table> ... */
|
||||
lua_setfield(g_lua, -2, "name");
|
||||
/* lua stack: <instance table> ... */
|
||||
|
||||
lua_pushstring(g_lua, inst[i].node().get()->desc.c_str());
|
||||
/* lua stack: <node desc> <instance table> ... */
|
||||
lua_setfield(g_lua, -2, "desc");
|
||||
/* lua stack: <instance table> ... */
|
||||
|
||||
lua_pushstring(g_lua, inst[i].node().get()->title.c_str());
|
||||
/* lua stack: <node title> <instance table> ... */
|
||||
lua_setfield(g_lua, -2, "title");
|
||||
/* lua stack: <instance table> ... */
|
||||
|
||||
lua_pushunsigned(g_lua, inst[i].addr());
|
||||
/* lua stack: <node addr> <instance table> ... */
|
||||
lua_setfield(g_lua, -2, "addr");
|
||||
/* lua stack: <instance table> ... */
|
||||
|
||||
/** create register */
|
||||
my_lua_create_reg(inst[i].addr(), inst[i].node().reg());
|
||||
|
||||
/** create subinstances */
|
||||
my_lua_create_instances(inst[i].children());
|
||||
/* lua stack: <instance table> [<index> <index table>] <parent table> */
|
||||
|
||||
if(inst[i].is_indexed())
|
||||
{
|
||||
/* lua stack: <instance table> <index> <index table> <parent table> */
|
||||
lua_settable(g_lua, -3);
|
||||
/* lua stack: <index table> <parent table> */
|
||||
lua_pop(g_lua, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* lua stack: <instance table> <parent table> */
|
||||
lua_setfield(g_lua, -2, inst[i].name().c_str());
|
||||
}
|
||||
/* lua stack: <parent table> */
|
||||
}
|
||||
}
|
||||
|
||||
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: <hwstub table> */
|
||||
lua_getfield(g_lua, -1, "soc");
|
||||
/* lua stack: <hwstub.soc table> <hwstub table> */
|
||||
|
||||
/** create a new table for the soc */
|
||||
lua_newtable(g_lua);
|
||||
/* lua stack: <soc table> <hwstub.soc table> <hwstub table> */
|
||||
|
||||
/** create name and desc fields */
|
||||
lua_pushstring(g_lua, soc.name.c_str());
|
||||
/* lua stack: <soc name> <soc table> <hwstub.soc table> <hwstub table> */
|
||||
lua_setfield(g_lua, -2, "name");
|
||||
/* lua stack: <soc table> <hwstub.soc table> <hwstub table> */
|
||||
|
||||
lua_pushstring(g_lua, soc.desc.c_str());
|
||||
/* lua stack: <soc desc> <soc table> <hwstub.soc table> <hwstub table> */
|
||||
lua_setfield(g_lua, -2, "desc");
|
||||
/* lua stack: <soc table> <hwstub.soc table> <hwstub table> */
|
||||
|
||||
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: <soc table> <hwstub.soc table> <hwstub table> */
|
||||
|
||||
/** put soc table at hwstub.soc.<soc name> */
|
||||
lua_setfield(g_lua, -2, soc.name.c_str());
|
||||
/* lua stack: <hwstub.soc table> <hwstub table> */
|
||||
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
91
utils/regtools/desc/regs-example-v1.xml
Normal file
91
utils/regtools/desc/regs-example-v1.xml
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0"?>
|
||||
<soc name="imx233" desc="i.MX233">
|
||||
<dev name="APBH" long_name="APHB DMA" desc="AHB-to-APBH Bridge with DMA" version="3.2.0">
|
||||
<addr name="APBH" addr="0x80004000"/>
|
||||
<reg name="CTRL0" desc="" sct="yes">
|
||||
<addr name="CTRL0" addr="0x0"/>
|
||||
<field name="SFTRST" desc="" bitrange="31:31"/>
|
||||
<field name="CLKGATE" desc="" bitrange="30:30"/>
|
||||
<field name="AHB_BURST8_EN" desc="" bitrange="29:29"/>
|
||||
<field name="APB_BURST4_EN" desc="" bitrange="28:28"/>
|
||||
<field name="RSVD0" desc="" bitrange="27:24"/>
|
||||
<field name="RESET_CHANNEL" desc="" bitrange="23:16">
|
||||
<value name="SSP1" value="0x2" desc=""/>
|
||||
<value name="SSP2" value="0x4" desc=""/>
|
||||
<value name="ATA" value="0x10" desc=""/>
|
||||
<value name="NAND0" value="0x10" desc=""/>
|
||||
<value name="NAND1" value="0x20" desc=""/>
|
||||
<value name="NAND2" value="0x40" desc=""/>
|
||||
<value name="NAND3" value="0x80" desc=""/>
|
||||
</field>
|
||||
<field name="CLKGATE_CHANNEL" desc="" bitrange="15:8">
|
||||
<value name="SSP1" value="0x2" desc=""/>
|
||||
<value name="SSP2" value="0x4" desc=""/>
|
||||
<value name="ATA" value="0x10" desc=""/>
|
||||
<value name="NAND0" value="0x10" desc=""/>
|
||||
<value name="NAND1" value="0x20" desc=""/>
|
||||
<value name="NAND2" value="0x40" desc=""/>
|
||||
<value name="NAND3" value="0x80" desc=""/>
|
||||
</field>
|
||||
<field name="FREEZE_CHANNEL" desc="" bitrange="7:0">
|
||||
<value name="SSP1" value="0x2" desc=""/>
|
||||
<value name="SSP2" value="0x4" desc=""/>
|
||||
<value name="ATA" value="0x10" desc=""/>
|
||||
<value name="NAND0" value="0x10" desc=""/>
|
||||
<value name="NAND1" value="0x20" desc=""/>
|
||||
<value name="NAND2" value="0x40" desc=""/>
|
||||
<value name="NAND3" value="0x80" desc=""/>
|
||||
</field>
|
||||
</reg>
|
||||
<reg name="CTRL1" desc="" sct="yes">
|
||||
<addr name="CTRL1" addr="0x10"/>
|
||||
<field name="RSVD1" desc="" bitrange="31:24"/>
|
||||
<field name="CH_CMDCMPLT_IRQ_EN" desc="" bitrange="23:16"/>
|
||||
<field name="RSVD0" desc="" bitrange="15:8"/>
|
||||
<field name="CH_CMDCMPLT_IRQ" desc="" bitrange="7:0"/>
|
||||
</reg>
|
||||
<reg name="CHn_CMD" desc="">
|
||||
<formula string="0x60+n*0x70"/>
|
||||
<addr name="CH0_CMD" addr="0x60"/>
|
||||
<addr name="CH1_CMD" addr="0xd0"/>
|
||||
<addr name="CH2_CMD" addr="0x140"/>
|
||||
<addr name="CH3_CMD" addr="0x1b0"/>
|
||||
<addr name="CH4_CMD" addr="0x220"/>
|
||||
<addr name="CH5_CMD" addr="0x290"/>
|
||||
<addr name="CH6_CMD" addr="0x300"/>
|
||||
<addr name="CH7_CMD" addr="0x370"/>
|
||||
<field name="XFER_COUNT" desc="" bitrange="31:16"/>
|
||||
<field name="CMDWORDS" desc="" bitrange="15:12"/>
|
||||
<field name="RSVD1" desc="" bitrange="11:9"/>
|
||||
<field name="HALTONTERMINATE" desc="" bitrange="8:8"/>
|
||||
<field name="WAIT4ENDCMD" desc="" bitrange="7:7"/>
|
||||
<field name="SEMAPHORE" desc="" bitrange="6:6"/>
|
||||
<field name="NANDWAIT4READY" desc="" bitrange="5:5"/>
|
||||
<field name="NANDLOCK" desc="" bitrange="4:4"/>
|
||||
<field name="IRQONCMPLT" desc="" bitrange="3:3"/>
|
||||
<field name="CHAIN" desc="" bitrange="2:2"/>
|
||||
<field name="COMMAND" desc="" bitrange="1:0">
|
||||
<value name="NO_DMA_XFER" value="0x0" desc=""/>
|
||||
<value name="DMA_WRITE" value="0x1" desc=""/>
|
||||
<value name="DMA_READ" value="0x2" desc=""/>
|
||||
<value name="DMA_SENSE" value="0x3" desc=""/>
|
||||
</field>
|
||||
</reg>
|
||||
</dev>
|
||||
<dev name="SAIF" long_name="Sync Audio Interface" desc="Sync Audio Interface (SAIF)" version="3.2.0">
|
||||
<addr name="SAIF1" addr="0x80042000"/>
|
||||
<addr name="SAIF2" addr="0x80046000"/>
|
||||
<reg name="DATA" desc="" sct="yes">
|
||||
<addr name="DATA" addr="0x20"/>
|
||||
<field name="PCM_RIGHT" desc="" bitrange="31:16"/>
|
||||
<field name="PCM_LEFT" desc="" bitrange="15:0"/>
|
||||
</reg>
|
||||
<reg name="VERSION" desc="">
|
||||
<addr name="VERSION" addr="0x30"/>
|
||||
<field name="MAJOR" desc="" bitrange="31:24"/>
|
||||
<field name="MINOR" desc="" bitrange="23:16"/>
|
||||
<field name="STEP" desc="" bitrange="15:0"/>
|
||||
</reg>
|
||||
</dev>
|
||||
</soc>
|
||||
|
153
utils/regtools/desc/regs-example.xml
Normal file
153
utils/regtools/desc/regs-example.xml
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?xml version="1.0"?>
|
||||
<soc version="2">
|
||||
<name>vsoc</name>
|
||||
<title>Virtual SOC</title>
|
||||
<desc>Virtual SoC is a nice and powerful chip.</desc>
|
||||
<author>Amaury Pouly</author>
|
||||
<isa>ARM</isa>
|
||||
<version>0.5</version>
|
||||
<node>
|
||||
<name>int</name>
|
||||
<title>Interrupt Collector</title>
|
||||
<desc>The interrupt collector controls the routing of interrupt to the processor</desc>
|
||||
<instance>
|
||||
<name>ICOLL</name>
|
||||
<title>Interrupt collector</title>
|
||||
<address>0x80000000</address>
|
||||
</instance>
|
||||
<node>
|
||||
<name>status</name>
|
||||
<access>read-only</access>
|
||||
<title>Interrupt status register</title>
|
||||
<instance>
|
||||
<name>STATUS</name>
|
||||
<address>0x4</address>
|
||||
</instance>
|
||||
<register>
|
||||
<width>8</width>
|
||||
<field>
|
||||
<name>VDDIO_BO</name>
|
||||
<desc>VDDIO brownout interrupt status</desc>
|
||||
<position>0</position>
|
||||
</field>
|
||||
</register>
|
||||
</node>
|
||||
<node>
|
||||
<name>enable</name>
|
||||
<title>Interrupt enable register</title>
|
||||
<instance>
|
||||
<name>ENABLE</name>
|
||||
<address>0x8</address>
|
||||
</instance>
|
||||
<register>
|
||||
<width>16</width>
|
||||
<field>
|
||||
<name>VDDIO_BO</name>
|
||||
<desc>VDDIO brownout interrupt enable</desc>
|
||||
<position>0</position>
|
||||
<width>2</width>
|
||||
<enum>
|
||||
<name>DISABLED</name>
|
||||
<desc>Interrupt is disabled</desc>
|
||||
<value>0</value>
|
||||
</enum>
|
||||
<enum>
|
||||
<name>ENABLED</name>
|
||||
<desc>Interrupt is enabled</desc>
|
||||
<value>1</value>
|
||||
</enum>
|
||||
<enum>
|
||||
<name>NMI</name>
|
||||
<desc>Interrupt is non-maskable</desc>
|
||||
<value>2</value>
|
||||
</enum>
|
||||
</field>
|
||||
</register>
|
||||
<variant>
|
||||
<type>set</type>
|
||||
<offset>4</offset>
|
||||
</variant>
|
||||
<variant>
|
||||
<type>clr</type>
|
||||
<offset>8</offset>
|
||||
</variant>
|
||||
</node>
|
||||
</node>
|
||||
<node>
|
||||
<name>gpio</name>
|
||||
<title>GPIO controller</title>
|
||||
<desc>A GPIO controller manages several ports</desc>
|
||||
<instance>
|
||||
<name>CPU_GPIO</name>
|
||||
<title>CPU GPIO controller 1 through 3</title>
|
||||
<range>
|
||||
<first>1</first>
|
||||
<count>3</count>
|
||||
<formula variable="n">0x80001000+(n-1)*0x1000</formula>
|
||||
</range>
|
||||
</instance>
|
||||
<instance>
|
||||
<name>COP_GPIO</name>
|
||||
<title>Companion processor GPIO controller</title>
|
||||
<desc>Although the companion processor GPIO controller is accessible from the CPU, it incurs an extra penalty on the bus</desc>
|
||||
<address>0x90000000</address>
|
||||
</instance>
|
||||
<node>
|
||||
<name>port</name>
|
||||
<title>GPIO port</title>
|
||||
<instance>
|
||||
<name>PORT</name>
|
||||
<range>
|
||||
<first>0</first>
|
||||
<count>4</count>
|
||||
<base>0</base>
|
||||
<stride>0x100</stride>
|
||||
</range>
|
||||
</instance>
|
||||
<node>
|
||||
<name>input</name>
|
||||
<title>Input register</title>
|
||||
<instance>
|
||||
<name>IN</name>
|
||||
<address>0</address>
|
||||
</instance>
|
||||
<register>
|
||||
<width>8</width>
|
||||
<field>
|
||||
<name>VALUE</name>
|
||||
<position>0</position>
|
||||
<width>8</width>
|
||||
</field>
|
||||
</register>
|
||||
</node>
|
||||
<node>
|
||||
<name>output_enable</name>
|
||||
<title>Output enable register</title>
|
||||
<instance>
|
||||
<name>OE</name>
|
||||
<address>0x10</address>
|
||||
</instance>
|
||||
<register>
|
||||
<width>8</width>
|
||||
<field>
|
||||
<name>ENABLE</name>
|
||||
<position>0</position>
|
||||
<width>8</width>
|
||||
</field>
|
||||
</register>
|
||||
<variant>
|
||||
<type>set</type>
|
||||
<address>4</address>
|
||||
</variant>
|
||||
<variant>
|
||||
<type>clr</type>
|
||||
<address>8</address>
|
||||
</variant>
|
||||
<variant>
|
||||
<type>mask</type>
|
||||
<address>12</address>
|
||||
</variant>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</soc>
|
373
utils/regtools/desc/spec-2.0.txt
Normal file
373
utils/regtools/desc/spec-2.0.txt
Normal file
|
@ -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:
|
||||
|
||||
<node>
|
||||
<name>N</name>
|
||||
<instance>
|
||||
<name>A</name>
|
||||
<address>X</address>
|
||||
</instance>
|
||||
<instance>
|
||||
<name>B</name>
|
||||
<address>Y</address>
|
||||
</instance>
|
||||
<!-- HERE -->
|
||||
</node>
|
||||
|
||||
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:
|
||||
|
||||
<!-- HERE -->
|
||||
<node>
|
||||
<name>S_N</name>
|
||||
<instance>
|
||||
<name>C</name>
|
||||
<address>U</address>
|
||||
</instance>
|
||||
<instance>
|
||||
<name>D</name>
|
||||
<address>V</address>
|
||||
</instance>
|
||||
</node>
|
||||
|
||||
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:
|
||||
|
||||
<node>
|
||||
<name>N</name>
|
||||
<instance>
|
||||
<name>A</name>
|
||||
<range>
|
||||
<first>1</first>
|
||||
<count>5</count>
|
||||
<base>0x1000</base>
|
||||
<stride>0x100</stride>
|
||||
</range>
|
||||
</instance>
|
||||
<node>
|
||||
<name>NN</name>
|
||||
<instance>
|
||||
<name>E</name>
|
||||
<address>0x4</address>
|
||||
</instance>
|
||||
</node>
|
||||
</node>
|
||||
|
||||
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:
|
||||
|
||||
<node>
|
||||
<name>N</name>
|
||||
<instance>
|
||||
<name>F</name>
|
||||
<range>
|
||||
<first>0</first>
|
||||
<count>4</count>
|
||||
<formula variable="n">0x50+(n/2)*0x100+(n%2)*0x10</formula>
|
||||
</range>
|
||||
</instance>
|
||||
</node>
|
||||
|
||||
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:
|
||||
|
||||
<node>
|
||||
<name>icoll</name>
|
||||
<title>DMA Controller</title>
|
||||
<desc>The DMA controller provides uniform DMA facilities to transfer data from
|
||||
and to peripherals. It uses memory-mapped tables and support chained
|
||||
transfers.</desc>
|
||||
<instance>
|
||||
<name>AHB_DMAC</name>
|
||||
<address>0x80000000</address>
|
||||
<title>AHB DMA Controller</title>
|
||||
<desc>The AHB DMA controller provides DMA facilities for the peripherals
|
||||
on the AHB bus like the SSP and PIX engines.</desc>
|
||||
</instance>
|
||||
<instance>
|
||||
<name>APB_DMAC</name>
|
||||
<address>0x8001000</address>
|
||||
<title>APB DMA Controller</title>
|
||||
<desc>The APB DMA controller provides DMA facilities for the peripherals
|
||||
on the APB bus like the I2C and PCM engines.</desc>
|
||||
</instance>
|
||||
</node>
|
||||
|
||||
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:
|
||||
|
||||
<register>
|
||||
<width>8</width>
|
||||
<field>
|
||||
<name>MODE</name>
|
||||
<desc>Interrupt mode</desc>
|
||||
<position>0</position>
|
||||
<width>2</width>
|
||||
<enum>
|
||||
<name>DISABLED</name>
|
||||
<desc>Interrupt is disabled</desc>
|
||||
<value>0</value>
|
||||
</enum>
|
||||
<enum>
|
||||
<name>ENABLED</name>
|
||||
<desc>Interrupt is enabled</desc>
|
||||
<value>1</value>
|
||||
</enum>
|
||||
<enum>
|
||||
<name>NMI</name>
|
||||
<desc>Interrupt is non-maskable</desc>
|
||||
<value>2</value>
|
||||
</enum>
|
||||
</field>
|
||||
<field>
|
||||
<name>PRIORITY</name>
|
||||
<desc>Interrupt priority, lower values are more prioritized.</desc>
|
||||
<position>2</position>
|
||||
<width>2</width>
|
||||
</field>
|
||||
<field>
|
||||
<name>ARM_MODE</name>
|
||||
<desc>Select between ARM's FIQ and IRQ mode</desc>
|
||||
<position>4</position>
|
||||
<width>1</width>
|
||||
<enum>
|
||||
<name>IRQ</name>
|
||||
<value>0</value>
|
||||
</enum>
|
||||
<enum>
|
||||
<name>FIQ</name>
|
||||
<value>1</value>
|
||||
</enum>
|
||||
</field>
|
||||
</register>
|
||||
|
||||
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):
|
||||
|
||||
<node>
|
||||
<name>dma</name>
|
||||
<instance><name>DMAC</name><address>0x80000000</address></instance>
|
||||
<node>
|
||||
<instance><name>PCM_CHAN</name><address>0x0</address></instance>
|
||||
<instance><name>I2C_CHAN</name><address>0x10</address></instance>
|
||||
<register><!--- blabla --></register>
|
||||
<node>
|
||||
<name>sct</name>
|
||||
<instance><name>SET</name><address>0x4</address></instance>
|
||||
<instance><name>CLR</name><address>0x8</address></instance>
|
||||
<instance><name>TOG</name><address>0xC</address></instance>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
||||
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.
|
||||
|
||||
<soc>
|
||||
<name>vsoc</name>
|
||||
<title>Virtual SOC</title>
|
||||
<desc>Virtual SoC is a nice and powerful chip.</desc>
|
||||
<author>Amaury Pouly</author>
|
||||
<isa>ARM</isa>
|
||||
<version>0.5</version>
|
||||
<!-- put nodes below -->
|
||||
</soc>
|
||||
|
||||
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:
|
||||
<?xml version="1.0"?>
|
||||
<!-- desc -->
|
||||
</xml>
|
||||
|
||||
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
|
|
@ -18,7 +18,7 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "soc_desc.hpp"
|
||||
#include "soc_desc_v1.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
@ -28,6 +28,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
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;
|
384
utils/regtools/include/soc_desc.hpp
Normal file
384
utils/regtools/include/soc_desc.hpp
Normal file
|
@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
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 <soc>. */
|
||||
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__ */
|
|
@ -18,8 +18,8 @@
|
|||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __SOC_DESC__
|
||||
#define __SOC_DESC__
|
||||
#ifndef __SOC_DESC_V1__
|
||||
#define __SOC_DESC_V1__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
@ -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 <soc>. */
|
||||
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__ */
|
||||
} // soc_desc_v1
|
||||
|
||||
#endif /* __SOC_DESC_V1__ */
|
|
@ -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 $@ $<
|
||||
|
||||
|
|
214
utils/regtools/lib/formula.cpp
Normal file
214
utils/regtools/lib/formula.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
#include "soc_desc.hpp"
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
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
|
File diff suppressed because it is too large
Load diff
990
utils/regtools/lib/soc_desc_v1.cpp
Normal file
990
utils/regtools/lib/soc_desc_v1.cpp
Normal file
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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\\\"
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
612
utils/regtools/swiss_knife.cpp
Normal file
612
utils/regtools/swiss_knife.cpp
Normal file
|
@ -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;
|
||||
}
|
|
@ -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);
|
Loading…
Reference in a new issue