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:
Amaury Pouly 2014-12-14 11:53:55 +01:00
parent c8d3638b9e
commit 1cada1f833
22 changed files with 4039 additions and 941 deletions

View file

@ -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)

View file

@ -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

View file

@ -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)

View 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>

View 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>

View 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

View file

@ -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;

View 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__ */

View file

@ -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__ */

View file

@ -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 $@ $<

View 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

View 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

View file

@ -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()

View file

@ -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
{

View file

@ -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

View file

@ -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\\\"

View file

@ -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");

View 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);
}

View 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;
}

View file

@ -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);