/*************************************************************************** * __________ __ ___. * 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 #include #include #include #include #include #include #include #include #include namespace soc_desc { /** * Parser */ #define XML_CHAR_TO_CHAR(s) ((const char *)(s)) #define BEGIN_ATTR_MATCH(attr) \ for(xmlAttr *a = attr; a; a = a->next) { \ 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 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 bool add_fatal(error_context_t& ctx, T *node, const std::string& msg) { return add_error(ctx, err_t::FATAL, node, msg); } template 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 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 bool parse_unsigned_elem(xmlNode *node, T& res, xmlChar *content, error_context_t& ctx) { return parse_unsigned_text(node, res, content, ctx); } template bool parse_unsigned_attr(xmlAttr *attr, T& res, xmlChar *content, error_context_t& ctx) { return parse_unsigned_text(attr, res, content, ctx); } bool parse_text_attr_internal(xmlAttr *attr, xmlChar*& res, error_context_t& ctx) { if(attr->children != attr->last) return false; if(attr->children->type != XML_TEXT_NODE) return 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 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 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) { /* */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "range")); /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "first", "%lu", range.first)); if(range.type == range_t::STRIDE) { /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "base", "0x%x", range.base)); /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "0x%x", range.stride)); } /* */ else if(range.type == range_t::FORMULA) { /* */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "count", "%lu", range.count)); /* */ 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())); /* */ 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])); } /* */ SAFE(xmlTextWriterEndElement(writer)); return 0; } int produce_instance(xmlTextWriterPtr writer, const instance_t& inst, error_context_t& ctx) { /* */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "instance")); /* */ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST inst.name.c_str())); /* */ if(!inst.title.empty()) SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "title", BAD_CAST inst.title.c_str())); /* <desc/> */ if(!inst.desc.empty()) SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST inst.desc.c_str())); /* <address/> */ if(inst.type == instance_t::SINGLE) SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "address", "0x%x", inst.addr)); /* <range/> */ else if(inst.type == instance_t::RANGE) SAFE(produce_range(writer, inst.range, ctx)); /* </instance> */ SAFE(xmlTextWriterEndElement(writer)); return 0; } int produce_enum(xmlTextWriterPtr writer, const enum_t& enum_, error_context_t& ctx) { /* <enum> */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "enum")); /* <name/> */ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST enum_.name.c_str())); /* <desc/> */ if(!enum_.desc.empty()) SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST enum_.desc.c_str())); /* <value/> */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "value", "0x%x", enum_.value)); /* </enum> */ SAFE(xmlTextWriterEndElement(writer)); return 0; } int produce_field(xmlTextWriterPtr writer, const field_t& field, error_context_t& ctx) { /* <field> */ SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field")); /* <name/> */ SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "name", BAD_CAST field.name.c_str())); /* <desc/> */ if(!field.desc.empty()) SAFE(xmlTextWriterWriteElement(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str())); /* <position/> */ SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "position", "%lu", field.pos)); /* <width/> */ if(field.width != 1) SAFE(xmlTextWriterWriteFormatElement(writer, BAD_CAST "width", "%lu", field.width)); /* enums */ for(size_t i = 0; i < field.enum_.size(); i++) SAFE(produce_enum(writer, field.enum_[i], ctx)); /* </field> */ SAFE(xmlTextWriterEndElement(writer)); return 0; } 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