fafb770ca5
Because a node ref is at root doesn't make it valid, check that soc is valid otherwise we return garbage. Change-Id: I6e5befc959dc670ab39a87484e87af6d90be7726
1721 lines
48 KiB
C++
1721 lines
48 KiB
C++
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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 <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>
|
|
#include <sstream>
|
|
#include <limits>
|
|
|
|
namespace soc_desc
|
|
{
|
|
|
|
/**
|
|
* Parser
|
|
*/
|
|
|
|
#define XML_CHAR_TO_CHAR(s) ((const char *)(s))
|
|
|
|
#define BEGIN_ATTR_MATCH(attr) \
|
|
for(xmlAttr *a = attr; a; a = a->next) { \
|
|
bool used = false;
|
|
|
|
#define MATCH_UNIQUE_ATTR(attr_name, val, has, parse_fn, ctx) \
|
|
if(strcmp(XML_CHAR_TO_CHAR(a->name), attr_name) == 0) { \
|
|
if(has) \
|
|
return parse_not_unique_attr_error(a, ctx); \
|
|
has = true; \
|
|
xmlChar *str = NULL; \
|
|
if(!parse_text_attr_internal(a, str, ctx) || !parse_fn(a, val, str, ctx)) \
|
|
ret = false; \
|
|
used = true; \
|
|
}
|
|
|
|
#define MATCH_UNUSED_ATTR(parse_fn, ctx) \
|
|
if(!used) { \
|
|
ret = ret && parse_fn(a, ctx); \
|
|
}
|
|
|
|
#define END_ATTR_MATCH() \
|
|
}
|
|
|
|
#define BEGIN_NODE_MATCH(node) \
|
|
for(xmlNode *sub = node; sub; sub = sub->next) { \
|
|
bool used = false; \
|
|
|
|
#define MATCH_ELEM_NODE(node_name, array, parse_fn, ctx) \
|
|
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
|
|
array.resize(array.size() + 1); \
|
|
if(!parse_fn(sub, array.back(), ctx)) \
|
|
ret = false; \
|
|
array.back().id = array.size(); \
|
|
used = true; \
|
|
}
|
|
|
|
#define MATCH_TEXT_NODE(node_name, array, parse_fn, ctx) \
|
|
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
|
|
if(!is_real_text_node(sub)) \
|
|
return parse_not_text_error(sub, ctx); \
|
|
xmlChar *content = xmlNodeGetContent(sub); \
|
|
array.resize(array.size() + 1); \
|
|
ret = ret && parse_fn(sub, array.back(), content, ctx); \
|
|
xmlFree(content); \
|
|
used = true; \
|
|
}
|
|
|
|
#define MATCH_UNIQUE_ELEM_NODE(node_name, val, has, parse_fn, ctx) \
|
|
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
|
|
if(has) \
|
|
return parse_not_unique_error(sub, ctx); \
|
|
has = true; \
|
|
if(!parse_fn(sub, val, ctx)) \
|
|
ret = false; \
|
|
used = true; \
|
|
}
|
|
|
|
#define MATCH_UNIQUE_TEXT_NODE(node_name, val, has, parse_fn, ctx) \
|
|
if(sub->type == XML_ELEMENT_NODE && strcmp(XML_CHAR_TO_CHAR(sub->name), node_name) == 0) { \
|
|
if(has) \
|
|
return parse_not_unique_error(sub, ctx); \
|
|
if(!is_real_text_node(sub)) \
|
|
return parse_not_text_error(sub, ctx); \
|
|
has = true; \
|
|
xmlChar *content = xmlNodeGetContent(sub); \
|
|
ret = ret && parse_fn(sub, val, content, ctx); \
|
|
xmlFree(content); \
|
|
used = true; \
|
|
}
|
|
|
|
#define MATCH_UNUSED_NODE(parse_fn, ctx) \
|
|
if(!used) { \
|
|
ret = ret && parse_fn(sub, ctx); \
|
|
}
|
|
|
|
#define END_NODE_MATCH() \
|
|
}
|
|
|
|
#define CHECK_HAS(node, node_name, has, ctx) \
|
|
if(!has) \
|
|
ret = ret && parse_missing_error(node, node_name, ctx);
|
|
|
|
#define CHECK_HAS_ATTR(node, attr_name, has, ctx) \
|
|
if(!has) \
|
|
ret = ret && parse_missing_attr_error(node, attr_name, ctx);
|
|
|
|
namespace
|
|
{
|
|
|
|
bool is_real_text_node(xmlNode *node)
|
|
{
|
|
for(xmlNode *sub = node->children; sub; sub = sub->next)
|
|
if(sub->type != XML_TEXT_NODE && sub->type != XML_ENTITY_REF_NODE)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
std::string xml_loc(xmlNode *node)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "line " << node->line;
|
|
return oss.str();
|
|
}
|
|
|
|
std::string xml_loc(xmlAttr *attr)
|
|
{
|
|
return xml_loc(attr->parent);
|
|
}
|
|
|
|
template<typename T>
|
|
bool add_error(error_context_t& ctx, err_t::level_t lvl, T *node,
|
|
const std::string& msg)
|
|
{
|
|
ctx.add(err_t(lvl, xml_loc(node), msg));
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
bool add_fatal(error_context_t& ctx, T *node, const std::string& msg)
|
|
{
|
|
return add_error(ctx, err_t::FATAL, node, msg);
|
|
}
|
|
|
|
template<typename T>
|
|
bool add_warning(error_context_t& ctx, T *node, const std::string& msg)
|
|
{
|
|
return add_error(ctx, err_t::WARNING, node, msg);
|
|
}
|
|
|
|
bool parse_wrong_version_error(xmlNode *node, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "unknown version, only version " << MAJOR_VERSION << " is supported";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_not_unique_error(xmlNode *node, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "there must be a unique <" << XML_CHAR_TO_CHAR(node->name) << "> element";
|
|
if(node->parent->name)
|
|
oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
|
|
else
|
|
oss << " at root level";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_not_unique_attr_error(xmlAttr *attr, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "there must be a unique " << XML_CHAR_TO_CHAR(attr->name) << " attribute";
|
|
oss << " in <" << XML_CHAR_TO_CHAR(attr->parent->name) << ">";
|
|
return add_fatal(ctx, attr, oss.str());
|
|
}
|
|
|
|
bool parse_missing_error(xmlNode *node, const char *name, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "missing <" << name << "> element";
|
|
if(node->parent->name)
|
|
oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
|
|
else
|
|
oss << " at root level";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_missing_attr_error(xmlNode *node, const char *name, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "missing " << name << " attribute";
|
|
oss << " in <" << XML_CHAR_TO_CHAR(node->name) << ">";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_conflict_error(xmlNode *node, const char *name1, const char *name2,
|
|
error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "conflicting <" << name1 << "> and <" << name2 << "> elements";
|
|
if(node->parent->name)
|
|
oss << " in <" << XML_CHAR_TO_CHAR(node->parent->name) << ">";
|
|
else
|
|
oss << " at root level";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_not_text_error(xmlNode *node, error_context_t& ctx)
|
|
{
|
|
return add_fatal(ctx, node, "this is not a text element");
|
|
}
|
|
|
|
bool parse_not_text_attr_error(xmlAttr *attr, error_context_t& ctx)
|
|
{
|
|
return add_fatal(ctx, attr, "this is not a text attribute");
|
|
}
|
|
|
|
bool parse_text_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
name = XML_CHAR_TO_CHAR(content);
|
|
return true;
|
|
}
|
|
|
|
bool parse_name_elem(xmlNode *node, std::string& name, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
name = XML_CHAR_TO_CHAR(content);
|
|
if(name.size() == 0)
|
|
return add_fatal(ctx, node, "name cannot be empty");
|
|
for(size_t i = 0; i < name.size(); i++)
|
|
if(!isalnum(name[i]) && name[i] != '_')
|
|
return add_fatal(ctx, node, "name must only contain alphanumeric characters or _");
|
|
return true;
|
|
}
|
|
|
|
bool parse_unknown_elem(xmlNode *node, error_context_t& ctx)
|
|
{
|
|
/* ignore blank nodes */
|
|
if(xmlIsBlankNode(node))
|
|
return true;
|
|
std::ostringstream oss;
|
|
oss << "unknown <" << XML_CHAR_TO_CHAR(node->name) << "> element";
|
|
return add_fatal(ctx, node, oss.str());
|
|
}
|
|
|
|
bool parse_access_elem(xmlNode *node, access_t& acc, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
const char *text = XML_CHAR_TO_CHAR(content);
|
|
if(strcmp(text, "read-only") == 0)
|
|
acc = READ_ONLY;
|
|
else if(strcmp(text, "read-write") == 0)
|
|
acc = READ_WRITE;
|
|
else if(strcmp(text, "write-only") == 0)
|
|
acc = WRITE_ONLY;
|
|
else
|
|
return add_fatal(ctx, node, "unknown access type " + std::string(text));
|
|
return true;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
bool parse_unsigned_text(U *node, T& res, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
char *end;
|
|
unsigned long uns = strtoul(XML_CHAR_TO_CHAR(content), &end, 0);
|
|
if(*end != 0)
|
|
return add_fatal(ctx, node, "content must be an unsigned integer");
|
|
res = uns;
|
|
if(res != uns)
|
|
return add_fatal(ctx, node, "value does not fit into allowed range");
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
return parse_unsigned_text(node, res, content, ctx);
|
|
}
|
|
|
|
template<typename T>
|
|
bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
return parse_unsigned_text(attr, res, content, ctx);
|
|
}
|
|
|
|
bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx)
|
|
{
|
|
if(attr->children != attr->last)
|
|
return false;
|
|
if(attr->children->type != XML_TEXT_NODE)
|
|
return parse_not_text_attr_error(attr, ctx);
|
|
res = attr->children->content;
|
|
return true;
|
|
}
|
|
|
|
bool parse_text_attr(xmlAttr *attr, std::string& res, xmlChar *content, error_context_t& ctx)
|
|
{
|
|
res = XML_CHAR_TO_CHAR(content);
|
|
return true;
|
|
}
|
|
|
|
bool parse_unknown_attr(xmlAttr *attr, error_context_t& ctx)
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "unknown '" << XML_CHAR_TO_CHAR(attr->name) << "' attribute";
|
|
return add_fatal(ctx, attr, oss.str());
|
|
}
|
|
|
|
|
|
bool parse_enum_elem(xmlNode *node, enum_t& reg, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_name = false, has_value = false, has_desc = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("name", reg.name, has_name, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("value", reg.value, has_value, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "name", has_name, ctx)
|
|
CHECK_HAS(node, "value", has_value, ctx)
|
|
return ret;
|
|
}
|
|
|
|
bool parse_field_elem(xmlNode *node, field_t& field, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_name = false, has_pos = false, has_desc = false, has_width = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("name", field.name, has_name, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("position", field.pos, has_pos, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("width", field.width, has_width, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", field.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_ELEM_NODE("enum", field.enum_, parse_enum_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "name", has_name, ctx)
|
|
CHECK_HAS(node, "position", has_pos, ctx)
|
|
if(!has_width)
|
|
field.width = 1;
|
|
return ret;
|
|
}
|
|
|
|
bool parse_variant_elem(xmlNode *node, variant_t& variant, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_type = false, has_offset = false, has_access = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("type", variant.type, has_type, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("offset", variant.offset, has_offset, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("access", variant.access, has_access, parse_access_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "type", has_type, ctx)
|
|
CHECK_HAS(node, "offset", has_offset, ctx)
|
|
if(!has_access)
|
|
variant.access = UNSPECIFIED;
|
|
return ret;
|
|
}
|
|
|
|
bool parse_register_elem(xmlNode *node, register_t& reg, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_width = false, has_desc = false, has_access = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", reg.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("width", reg.width, has_width, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("access", reg.access, has_access, parse_access_elem, ctx)
|
|
MATCH_ELEM_NODE("field", reg.field, parse_field_elem, ctx)
|
|
MATCH_ELEM_NODE("variant", reg.variant, parse_variant_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
if(!has_width)
|
|
reg.width = 32;
|
|
if(!has_access)
|
|
reg.access = UNSPECIFIED;
|
|
return ret;
|
|
}
|
|
|
|
bool parse_formula_elem(xmlNode *node, range_t& range, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_var = false;
|
|
BEGIN_ATTR_MATCH(node->properties)
|
|
MATCH_UNIQUE_ATTR("variable", range.variable, has_var, parse_text_attr, ctx)
|
|
MATCH_UNUSED_ATTR(parse_unknown_attr, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS_ATTR(node, "variable", has_var, ctx)
|
|
return ret;
|
|
}
|
|
|
|
bool parse_range_elem(xmlNode *node, range_t& range, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_first = false, has_count = false, has_stride = false, has_base = false;
|
|
bool has_formula = false, has_formula_attr = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("first", range.first, has_first, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("count", range.count, has_count, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("base", range.base, has_base, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("stride", range.stride, has_stride, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_ELEM_NODE("formula", range, has_formula_attr, parse_formula_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("formula", range.formula, has_formula, parse_text_elem, ctx)
|
|
MATCH_TEXT_NODE("address", range.list, parse_unsigned_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "first", has_first, ctx)
|
|
if(range.list.size() == 0)
|
|
{
|
|
CHECK_HAS(node, "count", has_count, ctx)
|
|
if(!has_base && !has_formula)
|
|
ret = ret && parse_missing_error(node, "base> or <formula", ctx);
|
|
if(has_base && has_formula)
|
|
return parse_conflict_error(node, "base", "formula", ctx);
|
|
if(has_base)
|
|
CHECK_HAS(node, "stride", has_stride, ctx)
|
|
if(has_stride && !has_base)
|
|
ret = ret && parse_conflict_error(node, "stride", "formula", ctx);
|
|
if(has_stride)
|
|
range.type = range_t::STRIDE;
|
|
else
|
|
range.type = range_t::FORMULA;
|
|
}
|
|
else
|
|
{
|
|
if(has_base)
|
|
ret = ret && parse_conflict_error(node, "base", "addr", ctx);
|
|
if(has_count)
|
|
ret = ret && parse_conflict_error(node, "count", "addr", ctx);
|
|
if(has_formula)
|
|
ret = ret && parse_conflict_error(node, "formula", "addr", ctx);
|
|
if(has_stride)
|
|
ret = ret && parse_conflict_error(node, "stride", "addr", ctx);
|
|
range.type = range_t::LIST;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool parse_instance_elem(xmlNode *node, instance_t& inst, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_name = false, has_title = false, has_desc = false, has_range = false;
|
|
bool has_address = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("name", inst.name, has_name, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("title", inst.title, has_title, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", inst.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("address", inst.addr, has_address, parse_unsigned_elem, ctx)
|
|
MATCH_UNIQUE_ELEM_NODE("range", inst.range, has_range, parse_range_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "name", has_name, ctx)
|
|
if(!has_address && !has_range)
|
|
ret = ret && parse_missing_error(node, "address> or <range", ctx);
|
|
if(has_address && has_range)
|
|
ret = ret && parse_conflict_error(node, "address", "range", ctx);
|
|
if(has_address)
|
|
inst.type = instance_t::SINGLE;
|
|
else
|
|
inst.type = instance_t::RANGE;
|
|
return ret;
|
|
}
|
|
|
|
bool parse_node_elem(xmlNode *node_, node_t& node, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
register_t reg;
|
|
bool has_title = false, has_desc = false, has_register = false, has_name = false;
|
|
BEGIN_NODE_MATCH(node_->children)
|
|
MATCH_UNIQUE_TEXT_NODE("name", node.name, has_name, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("title", node.title, has_title, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", node.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_ELEM_NODE("register", reg, has_register, parse_register_elem, ctx)
|
|
MATCH_ELEM_NODE("node", node.node, parse_node_elem, ctx)
|
|
MATCH_ELEM_NODE("instance", node.instance, parse_instance_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node_, "name", has_name, ctx)
|
|
if(has_register)
|
|
node.register_.push_back(reg);
|
|
return ret;
|
|
}
|
|
|
|
bool parse_soc_elem(xmlNode *node, soc_t& soc, error_context_t& ctx)
|
|
{
|
|
bool ret = true;
|
|
bool has_name = false, has_title = false, has_desc = false, has_version = false;
|
|
bool has_isa = false;
|
|
BEGIN_NODE_MATCH(node->children)
|
|
MATCH_UNIQUE_TEXT_NODE("name", soc.name, has_name, parse_name_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("title", soc.title, has_title, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("desc", soc.desc, has_desc, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("version", soc.version, has_version, parse_text_elem, ctx)
|
|
MATCH_UNIQUE_TEXT_NODE("isa", soc.isa, has_isa, parse_text_elem, ctx)
|
|
MATCH_TEXT_NODE("author", soc.author, parse_text_elem, ctx)
|
|
MATCH_ELEM_NODE("node", soc.node, parse_node_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "name", has_name, ctx)
|
|
return ret;
|
|
}
|
|
|
|
bool parse_root_elem(xmlNode *node, soc_t& soc, error_context_t& ctx)
|
|
{
|
|
size_t ver = 0;
|
|
bool ret = true;
|
|
bool has_soc = false, has_version = false;
|
|
BEGIN_ATTR_MATCH(node->properties)
|
|
MATCH_UNIQUE_ATTR("version", ver, has_version, parse_unsigned_attr, ctx)
|
|
MATCH_UNUSED_ATTR(parse_unknown_attr, ctx)
|
|
END_ATTR_MATCH()
|
|
if(!has_version)
|
|
{
|
|
ctx.add(err_t(err_t::FATAL, xml_loc(node), "no version attribute, is this a v1 file ?"));
|
|
return false;
|
|
}
|
|
if(ver != MAJOR_VERSION)
|
|
return parse_wrong_version_error(node, ctx);
|
|
BEGIN_NODE_MATCH(node)
|
|
MATCH_UNIQUE_ELEM_NODE("soc", soc, has_soc, parse_soc_elem, ctx)
|
|
MATCH_UNUSED_NODE(parse_unknown_elem, ctx)
|
|
END_NODE_MATCH()
|
|
CHECK_HAS(node, "soc", has_soc, ctx)
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
bool parse_xml(const std::string& filename, soc_t& soc,
|
|
error_context_t& error_ctx)
|
|
{
|
|
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, soc, error_ctx);
|
|
|
|
xmlFreeDoc(doc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Normalizer
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
|
|
struct soc_sorter
|
|
{
|
|
/* returns the lowest address of an instance, or 0 if none
|
|
* and 0xffffffff if cannot evaluate */
|
|
soc_addr_t first_addr(const instance_t& inst) const
|
|
{
|
|
if(inst.type == instance_t::SINGLE)
|
|
return inst.addr;
|
|
/* sanity check */
|
|
if(inst.type != instance_t::RANGE)
|
|
{
|
|
printf("Warning: unknown instance type %d\n", inst.type);
|
|
return 0;
|
|
}
|
|
if(inst.range.type == range_t::STRIDE)
|
|
return inst.range.base; /* assume positive stride */
|
|
if(inst.range.type == range_t::LIST)
|
|
{
|
|
soc_addr_t min = 0xffffffff;
|
|
for(size_t i = 0; i < inst.range.list.size(); i++)
|
|
if(inst.range.list[i] < min)
|
|
min = inst.range.list[i];
|
|
return min;
|
|
}
|
|
/* sanity check */
|
|
if(inst.range.type != range_t::FORMULA)
|
|
{
|
|
printf("Warning: unknown range type %d\n", inst.range.type);
|
|
return 0;
|
|
}
|
|
soc_addr_t min = 0xffffffff;
|
|
std::map< std::string, soc_word_t > vars;
|
|
for(size_t i = 0; i < inst.range.count; i++)
|
|
{
|
|
soc_word_t res;
|
|
vars[inst.range.variable] = inst.range.first;
|
|
error_context_t ctx;
|
|
if(evaluate_formula(inst.range.formula, vars, res, "", ctx) && res < min)
|
|
min = res;
|
|
}
|
|
return min;
|
|
}
|
|
|
|
/* return smallest address among all instances */
|
|
soc_addr_t first_addr(const node_t& node) const
|
|
{
|
|
soc_addr_t min = 0xffffffff;
|
|
for(size_t i = 0; i < node.instance.size(); i++)
|
|
min = std::min(min, first_addr(node.instance[i]));
|
|
return min;
|
|
}
|
|
|
|
/* sort instances by first address */
|
|
bool operator()(const instance_t& a, const instance_t& b) const
|
|
{
|
|
return first_addr(a) < first_addr(b);
|
|
}
|
|
|
|
/* sort nodes by first address of first instance (which is the lowest of
|
|
* any instance if instances are sorted) */
|
|
bool operator()(const node_t& a, const node_t& b) const
|
|
{
|
|
soc_addr_t addr_a = first_addr(a);
|
|
soc_addr_t addr_b = first_addr(b);
|
|
/* It may happen that two nodes have the same first instance address,
|
|
* for example if one logically splits a block into two blocks with
|
|
* the same base. In this case, sort by name */
|
|
if(addr_a == addr_b)
|
|
return a.name < b.name;
|
|
return addr_a < addr_b;
|
|
}
|
|
|
|
/* sort fields by decreasing position */
|
|
bool operator()(const field_t& a, const field_t& b) const
|
|
{
|
|
/* in the unlikely case where two fields have the same position, use name */
|
|
if(a.pos == b.pos)
|
|
return a.name < b.name;
|
|
return a.pos > b.pos;
|
|
}
|
|
|
|
/* sort enum values by value, then by name */
|
|
bool operator()(const enum_t& a, const enum_t& b) const
|
|
{
|
|
if(a.value == b.value)
|
|
return a.name < b.name;
|
|
return a.value < b.value;
|
|
}
|
|
};
|
|
|
|
void normalize(field_t& field)
|
|
{
|
|
std::sort(field.enum_.begin(), field.enum_.end(), soc_sorter());
|
|
}
|
|
|
|
void normalize(register_t& reg)
|
|
{
|
|
for(size_t i = 0; i < reg.field.size(); i++)
|
|
normalize(reg.field[i]);
|
|
std::sort(reg.field.begin(), reg.field.end(), soc_sorter());
|
|
}
|
|
|
|
void normalize(node_t& node)
|
|
{
|
|
for(size_t i = 0; i < node.register_.size(); i++)
|
|
normalize(node.register_[i]);
|
|
for(size_t i = 0; i < node.node.size(); i++)
|
|
normalize(node.node[i]);
|
|
std::sort(node.node.begin(), node.node.end(), soc_sorter());
|
|
std::sort(node.instance.begin(), node.instance.end(), soc_sorter());
|
|
}
|
|
|
|
} /* namespace */
|
|
|
|
void normalize(soc_t& soc)
|
|
{
|
|
for(size_t i = 0; i < soc.node.size(); i++)
|
|
normalize(soc.node[i]);
|
|
std::sort(soc.node.begin(), soc.node.end(), soc_sorter());
|
|
}
|
|
|
|
/**
|
|
* Producer
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
|
|
#define SAFE(x) \
|
|
do{ \
|
|
if((x) < 0) { \
|
|
std::ostringstream oss; \
|
|
oss << __FILE__ << ":" << __LINE__; \
|
|
ctx.add(err_t(err_t::FATAL, oss.str(), "write error")); \
|
|
return -1; \
|
|
} \
|
|
}while(0)
|
|
|
|
int produce_range(xmlTextWriterPtr writer, const range_t& range, error_context_t& ctx)
|
|
{
|
|
/* <range> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range"));
|
|
/* <first/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first));
|
|
if(range.type == range_t::STRIDE)
|
|
{
|
|
/* <count/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count));
|
|
/* <base/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base));
|
|
/* <stride/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride));
|
|
}
|
|
/* <formula> */
|
|
else if(range.type == range_t::FORMULA)
|
|
{
|
|
/* <count/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count));
|
|
/* <formula> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula"));
|
|
/* variable */
|
|
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "variable", BAD_CAST range.variable.c_str()));
|
|
/* content */
|
|
SAFE(xmlTextWriterWriteString(writer, BAD_CAST range.formula.c_str()));
|
|
/* </formula> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
}
|
|
else if(range.type == range_t::LIST)
|
|
{
|
|
for(size_t i = 0; i < range.list.size(); i++)
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", range.list[i]));
|
|
}
|
|
/* </range> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int produce_instance(xmlTextWriterPtr writer, const instance_t& inst, error_context_t& ctx)
|
|
{
|
|
/* <instance> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "instance"));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST inst.name.c_str()));
|
|
/* <title/> */
|
|
if(!inst.title.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST inst.title.c_str()));
|
|
/* <desc/> */
|
|
if(!inst.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST inst.desc.c_str()));
|
|
/* <address/> */
|
|
if(inst.type == instance_t::SINGLE)
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", inst.addr));
|
|
/* <range/> */
|
|
else if(inst.type == instance_t::RANGE)
|
|
SAFE(produce_range(writer, inst.range, ctx));
|
|
/* </instance> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
int produce_enum(xmlTextWriterPtr writer, const enum_t& enum_, error_context_t& ctx)
|
|
{
|
|
/* <enum> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "enum"));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST enum_.name.c_str()));
|
|
/* <desc/> */
|
|
if(!enum_.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST enum_.desc.c_str()));
|
|
/* <value/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "value", "0x%x", enum_.value));
|
|
/* </enum> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t& ctx)
|
|
{
|
|
/* <field> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field"));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST field.name.c_str()));
|
|
/* <desc/> */
|
|
if(!field.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str()));
|
|
/* <position/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "position", "%lu", field.pos));
|
|
/* <width/> */
|
|
if(field.width != 1)
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", field.width));
|
|
/* enums */
|
|
for(size_t i = 0; i < field.enum_.size(); i++)
|
|
SAFE(produce_enum(writer, field.enum_[i], ctx));
|
|
/* </field> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
const char *access_string(access_t acc)
|
|
{
|
|
switch(acc)
|
|
{
|
|
case READ_ONLY: return "read-only";
|
|
case READ_WRITE: return "read-write";
|
|
case WRITE_ONLY: return "write-only";
|
|
default: return "bug-invalid-access";
|
|
}
|
|
}
|
|
|
|
int produce_variant(xmlTextWriterPtr writer, const variant_t& variant, error_context_t& ctx)
|
|
{
|
|
/* <variant> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "variant"));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "type", BAD_CAST variant.type.c_str()));
|
|
/* <position/> */
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "offset", "%lu", (unsigned long)variant.offset));
|
|
/* <access/> */
|
|
if(variant.access != UNSPECIFIED)
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "access", BAD_CAST access_string(variant.access)));
|
|
/* </variant> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
int produce_register(xmlTextWriterPtr writer, const register_t& reg, error_context_t& ctx)
|
|
{
|
|
/* <register> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "register"));
|
|
/* <width/> */
|
|
if(reg.width != 32)
|
|
SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", reg.width));
|
|
/* <desc/> */
|
|
if(!reg.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str()));
|
|
/* <access/> */
|
|
if(reg.access != UNSPECIFIED)
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "access", BAD_CAST access_string(reg.access)));
|
|
/* fields */
|
|
for(size_t i = 0; i < reg.field.size(); i++)
|
|
SAFE(produce_field(writer, reg.field[i], ctx));
|
|
/* variants */
|
|
for(size_t i = 0; i < reg.variant.size(); i++)
|
|
SAFE(produce_variant(writer, reg.variant[i], ctx));
|
|
/* </register> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
int produce_node(xmlTextWriterPtr writer, const node_t& node, error_context_t& ctx)
|
|
{
|
|
/* <node> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "node"));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST node.name.c_str()));
|
|
/* <title/> */
|
|
if(!node.title.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST node.title.c_str()));
|
|
/* <desc/> */
|
|
if(!node.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST node.desc.c_str()));
|
|
/* instances */
|
|
for(size_t i = 0; i < node.instance.size(); i++)
|
|
SAFE(produce_instance(writer, node.instance[i], ctx));
|
|
/* register */
|
|
for(size_t i = 0; i < node.register_.size(); i++)
|
|
SAFE(produce_register(writer, node.register_[i], ctx));
|
|
/* nodes */
|
|
for(size_t i = 0; i < node.node.size(); i++)
|
|
SAFE(produce_node(writer, node.node[i], ctx));
|
|
/* </node> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
return 0;
|
|
}
|
|
|
|
#undef SAFE
|
|
|
|
}
|
|
|
|
bool produce_xml(const std::string& filename, const soc_t& soc, error_context_t& ctx)
|
|
{
|
|
LIBXML_TEST_VERSION
|
|
|
|
std::ostringstream oss;
|
|
xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0);
|
|
if(writer == NULL)
|
|
return false;
|
|
#define SAFE(x) do{if((x) < 0) goto Lerr;}while(0)
|
|
SAFE(xmlTextWriterSetIndent(writer, 1));
|
|
SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " "));
|
|
/* <xml> */
|
|
SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL));
|
|
/* <soc> */
|
|
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc"));
|
|
/* version */
|
|
oss << MAJOR_VERSION;
|
|
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST oss.str().c_str()));
|
|
/* <name/> */
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST soc.name.c_str()));
|
|
/* <title/> */
|
|
if(!soc.title.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST soc.title.c_str()));
|
|
/* <desc/> */
|
|
if(!soc.desc.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str()));
|
|
/* <author/> */
|
|
for(size_t i = 0; i < soc.author.size(); i++)
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "author", BAD_CAST soc.author[i].c_str()));
|
|
/* <isa/> */
|
|
if(!soc.isa.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "isa", BAD_CAST soc.isa.c_str()));
|
|
/* <version/> */
|
|
if(!soc.version.empty())
|
|
SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "version", BAD_CAST soc.version.c_str()));
|
|
/* nodes */
|
|
for(size_t i = 0; i < soc.node.size(); i++)
|
|
SAFE(produce_node(writer, soc.node[i], ctx));
|
|
/* </soc> */
|
|
SAFE(xmlTextWriterEndElement(writer));
|
|
/* </xml> */
|
|
SAFE(xmlTextWriterEndDocument(writer));
|
|
xmlFreeTextWriter(writer);
|
|
return true;
|
|
#undef SAFE
|
|
Lerr:
|
|
xmlFreeTextWriter(writer);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* utils
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
|
|
template< typename T >
|
|
soc_id_t gen_fresh_id(const std::vector< T >& list)
|
|
{
|
|
soc_id_t id = 0;
|
|
for(size_t i = 0; i < list.size(); i++)
|
|
id = std::max(id, list[i].id);
|
|
return id + 1;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* soc_ref_t
|
|
*/
|
|
|
|
soc_ref_t::soc_ref_t():m_soc(0)
|
|
{
|
|
}
|
|
|
|
soc_ref_t::soc_ref_t(soc_t *soc):m_soc(soc)
|
|
{
|
|
}
|
|
|
|
bool soc_ref_t::valid() const
|
|
{
|
|
return get() != 0;
|
|
}
|
|
|
|
soc_t *soc_ref_t::get() const
|
|
{
|
|
return m_soc;
|
|
}
|
|
|
|
bool soc_ref_t::operator==(const soc_ref_t& ref) const
|
|
{
|
|
return m_soc == ref.m_soc;
|
|
}
|
|
|
|
node_ref_t soc_ref_t::root() const
|
|
{
|
|
return node_ref_t(*this);
|
|
}
|
|
|
|
node_inst_t soc_ref_t::root_inst() const
|
|
{
|
|
return node_inst_t(*this);
|
|
}
|
|
|
|
void soc_ref_t::reset()
|
|
{
|
|
m_soc = 0;
|
|
}
|
|
|
|
/**
|
|
* node_ref_t */
|
|
|
|
node_ref_t::node_ref_t(soc_ref_t soc):m_soc(soc)
|
|
{
|
|
}
|
|
|
|
node_ref_t::node_ref_t(soc_ref_t soc, const std::vector< soc_id_t >& path)
|
|
:m_soc(soc), m_path(path)
|
|
{
|
|
}
|
|
|
|
node_ref_t::node_ref_t()
|
|
{
|
|
}
|
|
|
|
bool node_ref_t::valid() const
|
|
{
|
|
return (m_soc.valid() && is_root()) || get() != 0;
|
|
}
|
|
|
|
bool node_ref_t::is_root() const
|
|
{
|
|
return m_path.empty();
|
|
}
|
|
|
|
void node_ref_t::reset()
|
|
{
|
|
m_soc.reset();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
std::vector< node_t > *get_children(node_ref_t node)
|
|
{
|
|
if(node.is_root())
|
|
return node.soc().valid() ? &node.soc().get()->node : 0;
|
|
node_t *n = node.get();
|
|
return n == 0 ? 0 : &n->node;
|
|
}
|
|
|
|
node_t *get_child(std::vector< node_t > *nodes, soc_id_t id)
|
|
{
|
|
if(nodes == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < nodes->size(); i++)
|
|
if((*nodes)[i].id == id)
|
|
return &(*nodes)[i];
|
|
return 0;
|
|
}
|
|
|
|
node_t *get_child(std::vector< node_t > *nodes, const std::string& name)
|
|
{
|
|
if(nodes == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < nodes->size(); i++)
|
|
if((*nodes)[i].name == name)
|
|
return &(*nodes)[i];
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
/* NOTE: valid() is implemented using get() != 0, so don't use it in get() ! */
|
|
node_t *node_ref_t::get() const
|
|
{
|
|
if(!soc().valid())
|
|
return 0;
|
|
/* we could do it recursively but it would make plenty of copies */
|
|
node_t *n = 0;
|
|
std::vector< node_t > *nodes = &soc().get()->node;
|
|
for(size_t i = 0; i < m_path.size(); i++)
|
|
{
|
|
n = get_child(nodes, m_path[i]);
|
|
if(n == 0)
|
|
return 0;
|
|
nodes = &n->node;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
soc_ref_t node_ref_t::soc() const
|
|
{
|
|
return m_soc;
|
|
}
|
|
|
|
node_ref_t node_ref_t::parent(unsigned level) const
|
|
{
|
|
if(level > depth())
|
|
return node_ref_t();
|
|
std::vector< soc_id_t > path = m_path;
|
|
path.resize(depth() - level);
|
|
return node_ref_t(m_soc, path);
|
|
}
|
|
|
|
unsigned node_ref_t::depth() const
|
|
{
|
|
return m_path.size();
|
|
}
|
|
|
|
register_ref_t node_ref_t::reg() const
|
|
{
|
|
node_t *n = get();
|
|
if(n == 0)
|
|
return register_ref_t();
|
|
if(n->register_.empty())
|
|
return parent().reg();
|
|
else
|
|
return register_ref_t(*this);
|
|
}
|
|
|
|
register_ref_t node_ref_t::create_reg(size_t width) const
|
|
{
|
|
node_t *n = get();
|
|
if(n == 0)
|
|
return register_ref_t();
|
|
if(!n->register_.empty())
|
|
return register_ref_t();
|
|
n->register_.resize(1);
|
|
n->register_[0].width = width;
|
|
return register_ref_t(*this);
|
|
}
|
|
|
|
node_ref_t node_ref_t::child(const std::string& name) const
|
|
{
|
|
/* check the node exists */
|
|
node_t *n = get_child(get_children(*this), name);
|
|
if(n == 0)
|
|
return node_ref_t();
|
|
std::vector< soc_id_t > path = m_path;
|
|
path.push_back(n->id);
|
|
return node_ref_t(m_soc, path);
|
|
}
|
|
|
|
std::vector< node_ref_t > node_ref_t::children() const
|
|
{
|
|
std::vector< node_ref_t > nodes;
|
|
std::vector< node_t > *children = get_children(*this);
|
|
if(children == 0)
|
|
return nodes;
|
|
for(size_t i = 0; i < children->size(); i++)
|
|
{
|
|
std::vector< soc_id_t > path = m_path;
|
|
path.push_back((*children)[i].id);
|
|
nodes.push_back(node_ref_t(m_soc, path));
|
|
}
|
|
return nodes;
|
|
}
|
|
|
|
std::vector< std::string > node_ref_t::path() const
|
|
{
|
|
std::vector< std::string > path;
|
|
if(!soc().valid())
|
|
return path;
|
|
/* we could do it recursively but this is more efficient */
|
|
node_t *n = 0;
|
|
std::vector< node_t > *nodes = &soc().get()->node;
|
|
for(size_t i = 0; i < m_path.size(); i++)
|
|
{
|
|
n = get_child(nodes, m_path[i]);
|
|
if(n == 0)
|
|
{
|
|
path.clear();
|
|
return path;
|
|
}
|
|
path.push_back(n->name);
|
|
nodes = &n->node;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
std::string node_ref_t::name() const
|
|
{
|
|
node_t *n = get();
|
|
return n == 0 ? "" : n->name;
|
|
}
|
|
|
|
bool node_ref_t::operator==(const node_ref_t& ref) const
|
|
{
|
|
return m_soc == ref.m_soc && m_path == ref.m_path;
|
|
}
|
|
|
|
void node_ref_t::remove()
|
|
{
|
|
if(is_root())
|
|
{
|
|
soc_t *s = soc().get();
|
|
if(s)
|
|
s->node.clear();
|
|
}
|
|
else
|
|
{
|
|
std::vector< node_t > *list = get_children(parent());
|
|
if(list == 0)
|
|
return;
|
|
for(size_t i = 0; i < list->size(); i++)
|
|
if((*list)[i].id == m_path.back())
|
|
{
|
|
list->erase(list->begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
node_ref_t node_ref_t::create() const
|
|
{
|
|
std::vector< node_t > *list = get_children(*this);
|
|
if(list == 0)
|
|
return node_ref_t();
|
|
node_t n;
|
|
n.id = gen_fresh_id(*list);
|
|
list->push_back(n);
|
|
std::vector< soc_id_t > path = m_path;
|
|
path.push_back(n.id);
|
|
return node_ref_t(soc(), path);
|
|
}
|
|
|
|
/**
|
|
* register_ref_t
|
|
*/
|
|
|
|
register_ref_t::register_ref_t(node_ref_t node)
|
|
:m_node(node)
|
|
{
|
|
}
|
|
|
|
register_ref_t::register_ref_t()
|
|
{
|
|
}
|
|
|
|
bool register_ref_t::valid() const
|
|
{
|
|
return get() != 0;
|
|
}
|
|
|
|
void register_ref_t::reset()
|
|
{
|
|
m_node.reset();
|
|
}
|
|
|
|
register_t *register_ref_t::get() const
|
|
{
|
|
node_t *n = m_node.get();
|
|
if(n == 0 || n->register_.empty())
|
|
return 0;
|
|
return &n->register_[0];
|
|
}
|
|
|
|
node_ref_t register_ref_t::node() const
|
|
{
|
|
return m_node;
|
|
}
|
|
|
|
std::vector< field_ref_t > register_ref_t::fields() const
|
|
{
|
|
std::vector< field_ref_t > fields;
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return fields;
|
|
for(size_t i = 0; i < r->field.size(); i++)
|
|
fields.push_back(field_ref_t(*this, r->field[i].id));
|
|
return fields;
|
|
}
|
|
|
|
std::vector< variant_ref_t > register_ref_t::variants() const
|
|
{
|
|
std::vector< variant_ref_t > variants;
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return variants;
|
|
for(size_t i = 0; i < r->variant.size(); i++)
|
|
variants.push_back(variant_ref_t(*this, r->variant[i].id));
|
|
return variants;
|
|
}
|
|
|
|
field_ref_t register_ref_t::field(const std::string& name) const
|
|
{
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return field_ref_t();
|
|
for(size_t i = 0; i < r->field.size(); i++)
|
|
if(r->field[i].name == name)
|
|
return field_ref_t(*this, r->field[i].id);
|
|
return field_ref_t();
|
|
}
|
|
|
|
variant_ref_t register_ref_t::variant(const std::string& type) const
|
|
{
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return variant_ref_t();
|
|
for(size_t i = 0; i < r->variant.size(); i++)
|
|
if(r->variant[i].type == type)
|
|
return variant_ref_t(*this, r->variant[i].id);
|
|
return variant_ref_t();
|
|
}
|
|
|
|
void register_ref_t::remove()
|
|
{
|
|
node_t *n = node().get();
|
|
if(n)
|
|
n->register_.clear();
|
|
}
|
|
|
|
field_ref_t register_ref_t::create_field() const
|
|
{
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return field_ref_t();
|
|
field_t f;
|
|
f.id = gen_fresh_id(r->field);
|
|
r->field.push_back(f);
|
|
return field_ref_t(*this, f.id);
|
|
}
|
|
|
|
variant_ref_t register_ref_t::create_variant() const
|
|
{
|
|
register_t *r = get();
|
|
if(r == 0)
|
|
return variant_ref_t();
|
|
variant_t v;
|
|
v.id = gen_fresh_id(r->variant);
|
|
r->variant.push_back(v);
|
|
return variant_ref_t(*this, v.id);
|
|
}
|
|
|
|
/**
|
|
* field_ref_t
|
|
*/
|
|
|
|
field_ref_t::field_ref_t(register_ref_t reg, soc_id_t id)
|
|
:m_reg(reg), m_id(id)
|
|
{
|
|
}
|
|
|
|
field_ref_t::field_ref_t()
|
|
{
|
|
}
|
|
|
|
bool field_ref_t::valid() const
|
|
{
|
|
return get() != 0;
|
|
}
|
|
|
|
void field_ref_t::reset()
|
|
{
|
|
m_reg.reset();
|
|
}
|
|
|
|
field_t *field_ref_t::get() const
|
|
{
|
|
register_t *reg = m_reg.get();
|
|
if(reg == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < reg->field.size(); i++)
|
|
if(reg->field[i].id == m_id)
|
|
return ®->field[i];
|
|
return 0;
|
|
}
|
|
|
|
std::vector< enum_ref_t > field_ref_t::enums() const
|
|
{
|
|
std::vector< enum_ref_t > enums;
|
|
field_t *f = get();
|
|
if(f == 0)
|
|
return enums;
|
|
for(size_t i = 0; i < f->enum_.size(); i++)
|
|
enums.push_back(enum_ref_t(*this, f->enum_[i].id));
|
|
return enums;
|
|
}
|
|
|
|
register_ref_t field_ref_t::reg() const
|
|
{
|
|
return m_reg;
|
|
}
|
|
|
|
enum_ref_t field_ref_t::create_enum() const
|
|
{
|
|
field_t *f = get();
|
|
if(f == 0)
|
|
return enum_ref_t();
|
|
enum_t e;
|
|
e.id = gen_fresh_id(f->enum_);
|
|
f->enum_.push_back(e);
|
|
return enum_ref_t(*this, e.id);
|
|
}
|
|
|
|
/**
|
|
* enum_ref_t
|
|
*/
|
|
|
|
enum_ref_t::enum_ref_t(field_ref_t field, soc_id_t id)
|
|
:m_field(field), m_id(id)
|
|
{
|
|
}
|
|
|
|
enum_ref_t::enum_ref_t()
|
|
{
|
|
}
|
|
|
|
bool enum_ref_t::valid() const
|
|
{
|
|
return get() != 0;
|
|
}
|
|
|
|
void enum_ref_t::reset()
|
|
{
|
|
m_field.reset();
|
|
}
|
|
|
|
enum_t *enum_ref_t::get() const
|
|
{
|
|
field_t *field = m_field.get();
|
|
if(field == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < field->enum_.size(); i++)
|
|
if(field->enum_[i].id == m_id)
|
|
return &field->enum_[i];
|
|
return 0;
|
|
}
|
|
|
|
field_ref_t enum_ref_t::field() const
|
|
{
|
|
return m_field;
|
|
}
|
|
|
|
/**
|
|
* variant_ref_t
|
|
*/
|
|
|
|
variant_ref_t::variant_ref_t(register_ref_t reg, soc_id_t id)
|
|
:m_reg(reg), m_id(id)
|
|
{
|
|
}
|
|
|
|
variant_ref_t::variant_ref_t()
|
|
{
|
|
}
|
|
|
|
bool variant_ref_t::valid() const
|
|
{
|
|
return get() != 0;
|
|
}
|
|
|
|
void variant_ref_t::reset()
|
|
{
|
|
m_reg.reset();
|
|
}
|
|
|
|
variant_t *variant_ref_t::get() const
|
|
{
|
|
register_t *reg = m_reg.get();
|
|
if(reg == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < reg->variant.size(); i++)
|
|
if(reg->variant[i].id == m_id)
|
|
return ®->variant[i];
|
|
return 0;
|
|
}
|
|
|
|
register_ref_t variant_ref_t::reg() const
|
|
{
|
|
return m_reg;
|
|
}
|
|
|
|
std::string variant_ref_t::type() const
|
|
{
|
|
variant_t *v = get();
|
|
return v ? v->type : std::string();
|
|
}
|
|
|
|
soc_word_t variant_ref_t::offset() const
|
|
{
|
|
variant_t *v = get();
|
|
return v ? v->offset : 0;
|
|
}
|
|
|
|
/**
|
|
* node_inst_t
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
|
|
const size_t INST_NO_INDEX = std::numeric_limits<std::size_t>::max();
|
|
|
|
bool get_inst_addr(range_t& range, size_t index, soc_addr_t& addr)
|
|
{
|
|
if(index < range.first || index >= range.first + range.size())
|
|
return false;
|
|
switch(range.type)
|
|
{
|
|
case range_t::STRIDE:
|
|
addr += range.base + (index - range.first) * range.stride;
|
|
return true;
|
|
case range_t::FORMULA:
|
|
{
|
|
soc_word_t res;
|
|
std::map< std::string, soc_word_t > vars;
|
|
vars[range.variable] = index;
|
|
error_context_t ctx;
|
|
if(!evaluate_formula(range.formula, vars, res, "", ctx))
|
|
return false;
|
|
addr += res;
|
|
return true;
|
|
}
|
|
case range_t::LIST:
|
|
addr += range.list[index - range.first];
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool get_inst_addr(instance_t *inst, size_t index, soc_addr_t& addr)
|
|
{
|
|
if(inst == 0)
|
|
return false;
|
|
switch(inst->type)
|
|
{
|
|
case instance_t::SINGLE:
|
|
if(index != INST_NO_INDEX)
|
|
return false;
|
|
addr += inst->addr;
|
|
return true;
|
|
case instance_t::RANGE:
|
|
if(index == INST_NO_INDEX)
|
|
return false;
|
|
return get_inst_addr(inst->range, index, addr);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
node_inst_t::node_inst_t(soc_ref_t soc)
|
|
:m_node(soc.root())
|
|
{
|
|
}
|
|
|
|
node_inst_t::node_inst_t(node_ref_t node, const std::vector< soc_id_t >& ids,
|
|
const std::vector< size_t >& indexes)
|
|
:m_node(node), m_id_path(ids), m_index_path(indexes)
|
|
{
|
|
}
|
|
|
|
node_inst_t::node_inst_t()
|
|
{
|
|
}
|
|
|
|
bool node_inst_t::valid() const
|
|
{
|
|
return (is_root() && node().valid()) || get() != 0;
|
|
}
|
|
|
|
void node_inst_t::reset()
|
|
{
|
|
m_node.reset();
|
|
}
|
|
|
|
node_ref_t node_inst_t::node() const
|
|
{
|
|
return m_node;
|
|
}
|
|
|
|
soc_ref_t node_inst_t::soc() const
|
|
{
|
|
return m_node.soc();
|
|
}
|
|
|
|
bool node_inst_t::is_root() const
|
|
{
|
|
return m_node.is_root();
|
|
}
|
|
|
|
node_inst_t node_inst_t::parent(unsigned level) const
|
|
{
|
|
if(level > depth())
|
|
return node_inst_t();
|
|
std::vector< soc_id_t > ids = m_id_path;
|
|
std::vector< size_t > indexes = m_index_path;
|
|
ids.resize(depth() - level);
|
|
indexes.resize(depth() - level);
|
|
return node_inst_t(m_node.parent(level), ids, indexes);
|
|
}
|
|
|
|
unsigned node_inst_t::depth() const
|
|
{
|
|
return m_id_path.size();
|
|
}
|
|
|
|
instance_t *node_inst_t::get() const
|
|
{
|
|
node_t *n = m_node.get();
|
|
if(n == 0)
|
|
return 0;
|
|
for(size_t i = 0; i < n->instance.size(); i++)
|
|
if(n->instance[i].id == m_id_path.back())
|
|
return &n->instance[i];
|
|
return 0;
|
|
}
|
|
|
|
soc_addr_t node_inst_t::addr() const
|
|
{
|
|
if(!valid() || is_root())
|
|
return 0;
|
|
soc_addr_t addr = parent().addr();
|
|
if(!get_inst_addr(get(), m_index_path.back(), addr))
|
|
return 0;
|
|
return addr;
|
|
}
|
|
|
|
node_inst_t node_inst_t::child(const std::string& name) const
|
|
{
|
|
return child(name, INST_NO_INDEX);
|
|
}
|
|
|
|
node_inst_t node_inst_t::child(const std::string& name, size_t index) const
|
|
{
|
|
std::vector< node_t > *nodes = get_children(m_node);
|
|
if(nodes == 0)
|
|
return node_inst_t();
|
|
node_ref_t child_node = m_node;
|
|
for(size_t i = 0; i < nodes->size(); i++)
|
|
{
|
|
node_t& node = (*nodes)[i];
|
|
child_node.m_path.push_back(node.id);
|
|
for(size_t j = 0; j < node.instance.size(); j++)
|
|
{
|
|
if(node.instance[j].name != name)
|
|
continue;
|
|
std::vector< soc_id_t > ids = m_id_path;
|
|
std::vector< size_t > indexes = m_index_path;
|
|
ids.push_back(node.instance[j].id);
|
|
indexes.push_back(index);
|
|
return node_inst_t(child_node, ids, indexes);
|
|
}
|
|
child_node.m_path.pop_back();
|
|
}
|
|
return node_inst_t();
|
|
}
|
|
|
|
std::vector< node_inst_t > node_inst_t::children() const
|
|
{
|
|
std::vector< node_inst_t > list;
|
|
std::vector< node_t > *nodes = get_children(m_node);
|
|
std::vector< soc_id_t > n_path = m_id_path;
|
|
std::vector< size_t > i_path = m_index_path;
|
|
if(nodes == 0)
|
|
return list;
|
|
node_ref_t child_node = m_node;
|
|
for(size_t i = 0; i < nodes->size(); i++)
|
|
{
|
|
node_t& node = (*nodes)[i];
|
|
child_node.m_path.push_back(node.id);
|
|
for(size_t j = 0; j < node.instance.size(); j++)
|
|
{
|
|
instance_t& inst = node.instance[j];
|
|
n_path.push_back(inst.id);
|
|
switch(inst.type)
|
|
{
|
|
case instance_t::SINGLE:
|
|
i_path.push_back(INST_NO_INDEX);
|
|
list.push_back(node_inst_t(child_node, n_path, i_path));
|
|
i_path.pop_back();
|
|
break;
|
|
case instance_t::RANGE:
|
|
for(size_t i = 0; i < inst.range.size(); i++)
|
|
{
|
|
i_path.push_back(inst.range.first + i);
|
|
list.push_back(node_inst_t(child_node, n_path, i_path));
|
|
i_path.pop_back();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
n_path.pop_back();
|
|
}
|
|
child_node.m_path.pop_back();
|
|
}
|
|
return list;
|
|
}
|
|
|
|
std::string node_inst_t::name() const
|
|
{
|
|
instance_t *inst = get();
|
|
return inst == 0 ? "" : inst->name;
|
|
}
|
|
|
|
bool node_inst_t:: is_indexed() const
|
|
{
|
|
return !m_index_path.empty() && m_index_path.back() != INST_NO_INDEX;
|
|
}
|
|
|
|
size_t node_inst_t::index() const
|
|
{
|
|
return m_index_path.empty() ? INST_NO_INDEX : m_index_path.back();
|
|
}
|
|
|
|
/** WARNING we need to call xmlInitParser() to init libxml2 but it needs to
|
|
* 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
|
|
|