2014-12-14 10:53:55 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* 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"
|
2016-02-06 15:01:24 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
2014-12-14 10:53:55 +00:00
|
|
|
#include <map>
|
|
|
|
#include <set>
|
|
|
|
#include <cstring>
|
2016-02-06 15:01:24 +00:00
|
|
|
#include <fstream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <cstring>
|
2014-12-14 10:53:55 +00:00
|
|
|
|
|
|
|
using namespace soc_desc;
|
|
|
|
|
|
|
|
void print_context(const error_context_t& ctx)
|
|
|
|
{
|
|
|
|
for(size_t j = 0; j < ctx.count(); j++)
|
|
|
|
{
|
2016-09-09 12:09:46 +00:00
|
|
|
err_t e = ctx.get(j);
|
2014-12-14 10:53:55 +00:00
|
|
|
switch(e.level())
|
|
|
|
{
|
2016-09-09 12:09:46 +00:00
|
|
|
case err_t::INFO: printf("[INFO]"); break;
|
|
|
|
case err_t::WARNING: printf("[WARN]"); break;
|
|
|
|
case err_t::FATAL: printf("[FATAL]"); break;
|
2014-12-14 10:53:55 +00:00
|
|
|
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;
|
|
|
|
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)
|
|
|
|
{
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::WARNING, loc,
|
2014-12-14 10:53:55 +00:00
|
|
|
"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)
|
|
|
|
{
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::INFO, loc, "promoted formula to base/stride"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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;
|
2016-02-06 15:01:24 +00:00
|
|
|
out.register_[0].desc = in.desc;
|
2014-12-14 10:53:55 +00:00
|
|
|
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)
|
|
|
|
{
|
2016-02-06 15:01:24 +00:00
|
|
|
out.register_[0].variant.resize(3);
|
|
|
|
const char *names[3] = {"set", "clr", "tog"};
|
2014-12-14 10:53:55 +00:00
|
|
|
for(size_t i = 0; i < 3; i++)
|
|
|
|
{
|
2016-02-06 15:01:24 +00:00
|
|
|
out.register_[0].variant[i].type = names[i];
|
|
|
|
out.register_[0].variant[i].offset = 4 + i *4;
|
2014-12-14 10:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::INFO, loc, "dropped version"));
|
2014-12-14 10:53:55 +00:00
|
|
|
out.name = in.name;
|
|
|
|
out.title = in.long_name;
|
|
|
|
out.desc = in.desc;
|
2016-02-06 15:01:24 +00:00
|
|
|
out.instance.resize(1);
|
|
|
|
if(in.addr.size() == 1)
|
|
|
|
{
|
|
|
|
out.instance[0].type = instance_t::SINGLE;
|
|
|
|
out.instance[0].name = in.addr[0].name;
|
|
|
|
out.instance[0].addr = in.addr[0].addr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
out.instance[0].type = instance_t::RANGE;
|
|
|
|
out.instance[0].name = in.name;
|
|
|
|
out.instance[0].range.type = range_t::LIST;
|
|
|
|
out.instance[0].range.first = 1;
|
|
|
|
out.instance[0].range.list.resize(in.addr.size());
|
|
|
|
for(size_t i = 0; i < in.addr.size(); i++)
|
|
|
|
out.instance[0].range.list[i] = in.addr[i].addr;
|
|
|
|
}
|
2014-12-14 10:53:55 +00:00
|
|
|
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)
|
|
|
|
{
|
2016-02-06 15:01:24 +00:00
|
|
|
std::vector< std::string > authors;
|
|
|
|
std::string version;
|
|
|
|
while(argc >= 2)
|
|
|
|
{
|
|
|
|
if(strcmp(argv[0], "--author") == 0)
|
|
|
|
authors.push_back(argv[1]);
|
|
|
|
else if(strcmp(argv[0], "--version") == 0)
|
|
|
|
version = argv[1];
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
argc -= 2;
|
|
|
|
argv += 2;
|
|
|
|
}
|
|
|
|
if(argc < 2)
|
|
|
|
return printf("convert mode expects at least one description file and an output file\n");
|
2014-12-14 10:53:55 +00:00
|
|
|
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");
|
|
|
|
}
|
2016-02-06 15:01:24 +00:00
|
|
|
new_soc.author = authors;
|
|
|
|
new_soc.version = version;
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "name is empty"));
|
2014-12-14 10:53:55 +00:00
|
|
|
for(size_t i = 0; i < name.size(); i++)
|
|
|
|
if(!isalnum(name[i]) && name[i] != '_')
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "name '" + name +
|
2014-12-14 10:53:55 +00:00
|
|
|
"' 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))
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path + ".<formula>",
|
2014-12-14 10:53:55 +00:00
|
|
|
"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)
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::WARNING, path, "field has width 0"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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)
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path_, "value does not fit into the field"));
|
2014-12-14 10:53:55 +00:00
|
|
|
if(names.find(n) != names.end())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "duplicate name '" + n + "' in enums"));
|
2014-12-14 10:53:55 +00:00
|
|
|
names.insert(n);
|
|
|
|
if(map.find(v) != map.end())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::WARNING, path, "'" + n + "' and '" + map[v] + "' have the same value"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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)
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::WARNING, path, "width is not 8, 16 or 32"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "duplicate name '" + n + "' in fields"));
|
2014-12-14 10:53:55 +00:00
|
|
|
if(reg.field[i].pos + reg.field[i].width > reg.width)
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "field '" + n + "' does not fit into the register"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "overlap between fields '" +
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::WARNING, path, "subnode with no instances"));
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "duplicate instance name '" +
|
2014-12-14 10:53:55 +00:00
|
|
|
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())
|
2016-09-09 12:09:46 +00:00
|
|
|
ctx.add(err_t(err_t::FATAL, path, "duplicate node name '" + n +
|
2014-12-14 10:53:55 +00:00
|
|
|
"' 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());
|
|
|
|
}
|
2016-02-06 15:01:24 +00:00
|
|
|
std::vector< variant_ref_t > variants = reg.variants();
|
|
|
|
for(size_t i = 0; i < variants.size(); i++)
|
|
|
|
{
|
|
|
|
print_path(node, false);
|
|
|
|
printf(":%s@+0x%x\n", variants[i].type().c_str(), variants[i].offset());
|
|
|
|
}
|
2014-12-14 10:53:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-06 15:01:24 +00:00
|
|
|
std::string trim(const std::string& s)
|
|
|
|
{
|
|
|
|
std::string ss = s.substr(s.find_first_not_of(" \t"));
|
|
|
|
return ss.substr(0, ss.find_last_not_of(" \t") + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parse_key(const std::string& key, std::string& dev, std::string& reg)
|
|
|
|
{
|
|
|
|
if(key.substr(0, 3) != "HW.")
|
|
|
|
return false;
|
|
|
|
std::string s = key.substr(3);
|
|
|
|
size_t idx = s.find('.');
|
|
|
|
if(idx == std::string::npos)
|
|
|
|
return false;
|
|
|
|
dev = s.substr(0, idx);
|
|
|
|
reg = s.substr(idx + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool find_addr(const soc_desc_v1::soc_dev_t& dev,
|
|
|
|
const std::string& reg, soc_desc_v1::soc_addr_t& addr)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < dev.reg.size(); i++)
|
|
|
|
for(size_t j = 0; j < dev.reg[i].addr.size(); j++)
|
|
|
|
if(dev.reg[i].addr[j].name == reg)
|
|
|
|
{
|
|
|
|
addr += dev.reg[i].addr[j].addr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool find_addr(const soc_desc_v1::soc_t& soc, const std::string& dev,
|
|
|
|
const std::string& reg, soc_desc_v1::soc_addr_t& addr)
|
|
|
|
{
|
|
|
|
addr = 0;
|
|
|
|
for(size_t i = 0; i < soc.dev.size(); i++)
|
|
|
|
for(size_t j = 0; j < soc.dev[i].addr.size(); j++)
|
|
|
|
if(soc.dev[i].addr[j].name == dev)
|
|
|
|
{
|
|
|
|
addr += soc.dev[i].addr[j].addr;
|
|
|
|
return find_addr(soc.dev[i], reg, addr);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int convert_dump(const std::map< std::string, std::string >& entries,
|
|
|
|
const soc_desc_v1::soc_t& soc, std::ofstream& fout)
|
|
|
|
{
|
|
|
|
std::map< std::string, std::string >::const_iterator it = entries.begin();
|
|
|
|
for(; it != entries.end(); ++it)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
soc_desc_v1::soc_word_t v = strtoul(it->second.c_str(), &end, 0);
|
|
|
|
if(*end != 0)
|
|
|
|
{
|
|
|
|
printf("because of invalid value '%s': ignore key '%s'\n",
|
|
|
|
it->second.c_str(), it->first.c_str());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::string dev, reg;
|
|
|
|
if(!parse_key(it->first, dev, reg))
|
|
|
|
{
|
|
|
|
printf("invalid key format, ignore key '%s'\n", it->first.c_str());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
soc_desc_v1::soc_addr_t addr;
|
|
|
|
if(!find_addr(soc, dev, reg, addr))
|
|
|
|
{
|
|
|
|
printf("cannot find register in description, ignore key '%s'\n",
|
|
|
|
it->first.c_str());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fout << "0x" << std::hex << addr << " = 0x" << std::hex << v << "\n";
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_convertdump(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if(argc < 3)
|
|
|
|
{
|
|
|
|
printf("you must specify at least one description file, one input file and one output file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::vector< soc_desc_v1::soc_t > socs;
|
|
|
|
for(int i = 0; i < argc - 2; i++)
|
|
|
|
{
|
|
|
|
socs.resize(socs.size() + 1);
|
|
|
|
if(!parse_xml(argv[i], socs.back()))
|
|
|
|
{
|
|
|
|
socs.pop_back();
|
|
|
|
printf("cannot parse description file '%s'\n", argv[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::ifstream fin(argv[argc - 2]);
|
|
|
|
if(!fin)
|
|
|
|
{
|
|
|
|
printf("cannot open input file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::map< std::string, std::string > entries;
|
|
|
|
std::string line;
|
|
|
|
while(std::getline(fin, line))
|
|
|
|
{
|
|
|
|
size_t idx = line.find('=');
|
|
|
|
if(idx == std::string::npos)
|
|
|
|
{
|
|
|
|
printf("ignore invalid line '%s'\n", line.c_str());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
std::string key = trim(line.substr(0, idx));
|
|
|
|
std::string value = trim(line.substr(idx + 1));
|
|
|
|
entries[key] = value;
|
|
|
|
}
|
|
|
|
if(entries.find("HW") == entries.end())
|
|
|
|
{
|
|
|
|
printf("invalid dump file: missing HW key\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
std::string soc = entries["HW"];
|
|
|
|
soc_desc_v1::soc_t *psoc = 0;
|
|
|
|
for(size_t i = 0; i < socs.size(); i++)
|
|
|
|
if(socs[i].name == soc)
|
|
|
|
psoc = &socs[i];
|
|
|
|
if(psoc == 0)
|
|
|
|
{
|
|
|
|
printf("cannot convert dump: please provide the description file for the soc '%s'\n", soc.c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
entries.erase(entries.find("HW"));
|
|
|
|
std::ofstream fout(argv[argc - 1]);
|
|
|
|
if(!fout)
|
|
|
|
{
|
|
|
|
printf("cannot open output file\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
fout << "soc = " << soc << "\n";
|
|
|
|
return convert_dump(entries, *psoc, fout);
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_normalize(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if(argc != 2)
|
|
|
|
{
|
|
|
|
printf("normalize takes two arguments\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
error_context_t ctx;
|
|
|
|
soc_t soc;
|
|
|
|
bool ret = parse_xml(argv[0], soc, ctx);
|
|
|
|
if(ctx.count() != 0)
|
|
|
|
printf("In file %s:\n", argv[0]);
|
|
|
|
print_context(ctx);
|
|
|
|
if(!ret)
|
|
|
|
{
|
|
|
|
printf("cannot parse file '%s'\n", argv[1]);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
normalize(soc);
|
|
|
|
ret = produce_xml(argv[1], soc, ctx);
|
|
|
|
if(ctx.count() != 0)
|
|
|
|
printf("In file %s:\n", argv[1]);
|
|
|
|
print_context(ctx);
|
|
|
|
if(!ret)
|
|
|
|
{
|
|
|
|
printf("cannot write file '%s'\n", argv[1]);
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-14 10:53:55 +00:00
|
|
|
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");
|
2016-02-06 15:01:24 +00:00
|
|
|
printf(" convert [--author <auth>] [--version <ver>] <input file> <output file>\n");
|
2014-12-14 10:53:55 +00:00
|
|
|
printf(" check <files...>\n");
|
|
|
|
printf(" dump [--nodes] [--instances] [--registers] [--verbose] <files...>\n");
|
2016-02-06 15:01:24 +00:00
|
|
|
printf(" convertdump <desc file> ... <desc file> <input dump file> <output dump file>\n");
|
|
|
|
printf(" normalize <desc file> <output desc file>\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("The following operations are performed in each mode:\n");
|
|
|
|
printf("* read: open and parse the files, reports any obvious errors\n");
|
|
|
|
printf("* write: open, parse a file and write it back, checks the parser/generator match\n");
|
|
|
|
printf("* eval: evaluate a formula with the formula parser\n");
|
|
|
|
printf("* convert: convert a description file from version 1 to version 2\n");
|
|
|
|
printf("* check: performs deep checks on description files\n");
|
|
|
|
printf("* dump: debug tool to dump internal structures\n");
|
|
|
|
printf("* convertdump: convert a register dump from version 1 to version 2\n");
|
|
|
|
printf(" NOTE: description file must be a v1 file\n");
|
|
|
|
printf("* normalize: normalise a description file\n");
|
2014-12-14 10:53:55 +00:00
|
|
|
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);
|
2016-02-06 15:01:24 +00:00
|
|
|
else if(mode == "convertdump")
|
|
|
|
return do_convertdump(argc - 2, argv + 2);
|
|
|
|
else if(mode == "normalize")
|
|
|
|
return do_normalize(argc - 2, argv + 2);
|
2014-12-14 10:53:55 +00:00
|
|
|
else
|
|
|
|
usage();
|
|
|
|
return 0;
|
2016-09-09 12:09:46 +00:00
|
|
|
}
|