1
0
Fork 0
forked from len0rd/rockbox

regtools: completely rework qeditor, improve soc desc library and tools

The graphical editor can now display and editor description files.
The library has been improved to provide more useful function.
The XML format has been slightly changed: only one soc is allowed per file
(this is was already de facto the case since <soc> was the root tag).
Also introduce a DTD to validate the files.

Change-Id: If70ba35b6dc0242bdb87411cf4baee9597798aac
This commit is contained in:
Amaury Pouly 2014-04-07 11:28:04 +02:00
parent 3754624edc
commit 4356666101
22 changed files with 4390 additions and 614 deletions

View file

@ -9,6 +9,11 @@ Example:
<!-- desc -->
</xml>
Root Element: root
------------------
The root element can either be "soc" tag if the file contains a single description,
or "root" with no properties and one or more "soc" tags as children.
Element: soc
------------
The XML can contain one or more SoC description. Each description is enclosed in

View file

@ -0,0 +1,28 @@
<!-- Format specification of the 1.0 register description files. Files are
considered to be version 1.0 if no version field is specified -->
<!ELEMENT soc (dev)*>
<!ATTLIST soc name CDATA #REQUIRED>
<!ATTLIST soc desc CDATA #IMPLIED>
<!ELEMENT dev (addr|reg)*>
<!ATTLIST dev name CDATA #REQUIRED>
<!ATTLIST dev long_name CDATA #IMPLIED>
<!ATTLIST dev desc CDATA #IMPLIED>
<!ATTLIST dev version CDATA #IMPLIED>
<!ELEMENT addr EMPTY>
<!ATTLIST addr name CDATA #REQUIRED>
<!ATTLIST addr addr CDATA #REQUIRED>
<!ELEMENT reg (addr|field|formula)*>
<!ATTLIST reg name CDATA #REQUIRED>
<!ATTLIST reg addr CDATA #IMPLIED>
<!ATTLIST reg desc CDATA #IMPLIED>
<!ATTLIST reg sct (yes|no) "no">
<!ELEMENT formula EMPTY>
<!ATTLIST formula string CDATA #IMPLIED>
<!ELEMENT field (value)*>
<!ATTLIST field name CDATA #REQUIRED>
<!ATTLIST field desc CDATA #IMPLIED>
<!ATTLIST field bitrange CDATA #REQUIRED>
<!ELEMENT value EMPTY>
<!ATTLIST value name CDATA #REQUIRED>
<!ATTLIST value value CDATA #REQUIRED>
<!ATTLIST value desc CDATA #IMPLIED>

View file

@ -574,11 +574,15 @@ int main(int argc, char **argv)
std::vector< soc_t > socs;
for(int i = optind; i < argc - 1; i++)
if(!soc_desc_parse_xml(argv[i], socs))
{
soc_t s;
if(!soc_desc_parse_xml(argv[i], s))
{
printf("Cannot parse %s\n", argv[i]);
return 1;
}
socs.push_back(s);
}
g_gen_selector = force_selector || socs.size() > 1;

View file

@ -21,8 +21,12 @@
#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>
#define XML_CHAR_TO_CHAR(s) ((const char *)(s))
@ -78,6 +82,9 @@
#define END_NODE_MATCH() \
}
namespace
{
bool validate_string_hook(const std::string& str, std::string& s)
{
s = str;
@ -137,6 +144,7 @@ bool parse_value_elem(xmlNode *node, soc_reg_field_value_t& value)
BEGIN_ATTR_MATCH(node->properties)
MATCH_TEXT_ATTR("name", value.name)
MATCH_UINT32_ATTR("value", value.value)
MATCH_TEXT_ATTR("desc", value.desc)
END_ATTR_MATCH()
return true;
@ -256,24 +264,32 @@ bool parse_soc_elem(xmlNode *node, soc_t& soc)
return true;
}
bool parse_root_elem(xmlNode *node, std::vector< soc_t >& soc)
bool parse_root_elem(xmlNode *node, soc_t& soc)
{
std::vector< soc_t > socs;
BEGIN_NODE_MATCH(node)
MATCH_ELEM_NODE("soc", soc, parse_soc_elem)
MATCH_ELEM_NODE("soc", socs, parse_soc_elem)
END_NODE_MATCH()
if(socs.size() != 1)
{
fprintf(stderr, "A description file must contain exactly one soc element\n");
return false;
}
soc = socs[0];
return true;
}
bool soc_desc_parse_xml(const std::string& filename, std::vector< soc_t >& socs)
}
bool soc_desc_parse_xml(const std::string& filename, soc_t& socs)
{
LIBXML_TEST_VERSION
xmlDoc *doc = xmlReadFile(filename.c_str(), NULL, 0);
xmlDocPtr doc = xmlReadFile(filename.c_str(), NULL, 0);
if(doc == NULL)
return false;
xmlNode *root_element = xmlDocGetRootElement(doc);
xmlNodePtr root_element = xmlDocGetRootElement(doc);
bool ret = parse_root_elem(root_element, socs);
xmlFreeDoc(doc);
@ -281,3 +297,673 @@ bool soc_desc_parse_xml(const std::string& filename, std::vector< soc_t >& socs)
return ret;
}
namespace
{
int produce_field(xmlTextWriterPtr writer, const soc_reg_field_t& field)
{
#define SAFE(x) if((x) < 0) return -1;
/* <field> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "field"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.name.c_str()));
/* desc */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.desc.c_str()));
/* bitrange */
SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "bitrange", "%d:%d",
field.last_bit, field.first_bit));
/* values */
for(size_t i = 0; i < field.value.size(); i++)
{
/* <value> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "value"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST field.value[i].name.c_str()));
/* value */
SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "value", "0x%x", field.value[i].value));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST field.value[i].desc.c_str()));
/* </value> */
SAFE(xmlTextWriterEndElement(writer));
}
/* </field> */
SAFE(xmlTextWriterEndElement(writer));
#undef SAFE
return 0;
}
int produce_reg(xmlTextWriterPtr writer, const soc_reg_t& reg)
{
#define SAFE(x) if((x) < 0) return -1;
/* <reg> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "reg"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.name.c_str()));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST reg.desc.c_str()));
/* flags */
if(reg.flags & REG_HAS_SCT)
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "sct", BAD_CAST "yes"));
/* formula */
if(reg.formula.type != REG_FORMULA_NONE)
{
/* <formula> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "formula"));
switch(reg.formula.type)
{
case REG_FORMULA_STRING:
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "string",
BAD_CAST reg.formula.string.c_str()));
break;
default:
break;
}
/* </formula> */
SAFE(xmlTextWriterEndElement(writer));
}
/* addresses */
for(size_t i = 0; i < reg.addr.size(); i++)
{
/* <addr> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST reg.addr[i].name.c_str()));
/* addr */
SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", reg.addr[i].addr));
/* </addr> */
SAFE(xmlTextWriterEndElement(writer));
}
/* fields */
for(size_t i = 0; i < reg.field.size(); i++)
produce_field(writer, reg.field[i]);
/* </reg> */
SAFE(xmlTextWriterEndElement(writer));
#undef SAFE
return 0;
}
int produce_dev(xmlTextWriterPtr writer, const soc_dev_t& dev)
{
#define SAFE(x) if((x) < 0) return -1;
/* <dev> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "dev"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.name.c_str()));
/* long_name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "long_name", BAD_CAST dev.long_name.c_str()));
/* desc */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST dev.desc.c_str()));
/* version */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "version", BAD_CAST dev.version.c_str()));
/* addresses */
for(size_t i = 0; i < dev.addr.size(); i++)
{
/* <addr> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "addr"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST dev.addr[i].name.c_str()));
/* addr */
SAFE(xmlTextWriterWriteFormatAttribute(writer, BAD_CAST "addr", "0x%x", dev.addr[i].addr));
/* </addr> */
SAFE(xmlTextWriterEndElement(writer));
}
/* registers */
for(size_t i = 0; i < dev.reg.size(); i++)
produce_reg(writer, dev.reg[i]);
/* </dev> */
SAFE(xmlTextWriterEndElement(writer));
#undef SAFE
return 0;
}
}
bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc)
{
LIBXML_TEST_VERSION
xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0);
if(writer == NULL)
return false;
#define SAFE(x) if((x) < 0) goto Lerr
SAFE(xmlTextWriterSetIndent(writer, 1));
SAFE(xmlTextWriterSetIndentString(writer, BAD_CAST " "));
/* <xml> */
SAFE(xmlTextWriterStartDocument(writer, NULL, NULL, NULL));
/* <soc> */
SAFE(xmlTextWriterStartElement(writer, BAD_CAST "soc"));
/* name */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "name", BAD_CAST soc.name.c_str()));
/* desc */
SAFE(xmlTextWriterWriteAttribute(writer, BAD_CAST "desc", BAD_CAST soc.desc.c_str()));
/* devices */
for(size_t i = 0; i < soc.dev.size(); i++)
SAFE(produce_dev(writer, soc.dev[i]));
/* end <soc> */
SAFE(xmlTextWriterEndElement(writer));
/* </xml> */
SAFE(xmlTextWriterEndDocument(writer));
xmlFreeTextWriter(writer);
return true;
#undef SAFE
Lerr:
xmlFreeTextWriter(writer);
return false;
}
namespace
{
struct soc_sorter
{
bool operator()(const soc_dev_t& a, const soc_dev_t& b) const
{
return a.name < b.name;
}
bool operator()(const soc_dev_addr_t& a, const soc_dev_addr_t& b) const
{
return a.name < b.name;
}
bool operator()(const soc_reg_t& a, const soc_reg_t& b) const
{
soc_addr_t aa = a.addr.size() > 0 ? a.addr[0].addr : 0;
soc_addr_t ab = b.addr.size() > 0 ? b.addr[0].addr : 0;
return aa < ab;
}
bool operator()(const soc_reg_addr_t& a, const soc_reg_addr_t& b) const
{
return a.addr < b.addr;
}
bool operator()(const soc_reg_field_t& a, const soc_reg_field_t& b) const
{
return a.last_bit > b.last_bit;
}
bool operator()(const soc_reg_field_value_t a, const soc_reg_field_value_t& b) const
{
return a.value < b.value;
}
};
void normalize(soc_reg_field_t& field)
{
std::sort(field.value.begin(), field.value.end(), soc_sorter());
}
void normalize(soc_reg_t& reg)
{
std::sort(reg.addr.begin(), reg.addr.end(), soc_sorter());
std::sort(reg.field.begin(), reg.field.end(), soc_sorter());
for(size_t i = 0; i < reg.field.size(); i++)
normalize(reg.field[i]);
}
void normalize(soc_dev_t& dev)
{
std::sort(dev.addr.begin(), dev.addr.end(), soc_sorter());
std::sort(dev.reg.begin(), dev.reg.end(), soc_sorter());
for(size_t i = 0; i < dev.reg.size(); i++)
normalize(dev.reg[i]);
}
}
void soc_desc_normalize(soc_t& soc)
{
std::sort(soc.dev.begin(), soc.dev.end(), soc_sorter());
for(size_t i = 0; i < soc.dev.size(); i++)
normalize(soc.dev[i]);
}
namespace
{
soc_error_t make_error(soc_error_level_t lvl, std::string at, std::string what)
{
soc_error_t err;
err.level = lvl;
err.location = at;
err.message = what;
return err;
}
soc_error_t make_warning(std::string at, std::string what)
{
return make_error(SOC_ERROR_WARNING, at, what);
}
soc_error_t make_fatal(std::string at, std::string what)
{
return make_error(SOC_ERROR_FATAL, at, what);
}
soc_error_t prefix(soc_error_t err, const std::string& prefix_at)
{
err.location = prefix_at + "." + err.location;
return err;
}
void add_errors(std::vector< soc_error_t >& errors,
const std::vector< soc_error_t >& new_errors, const std::string& prefix_at)
{
for(size_t i = 0; i < new_errors.size(); i++)
errors.push_back(prefix(new_errors[i], prefix_at));
}
std::vector< soc_error_t > no_error()
{
std::vector< soc_error_t > s;
return s;
}
std::vector< soc_error_t > one_error(const soc_error_t& err)
{
std::vector< soc_error_t > s;
s.push_back(err);
return s;
}
bool name_valid(char c)
{
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') || c == '_';
}
bool name_valid(const std::string& s)
{
for(size_t i = 0; i < s.size(); i++)
if(!name_valid(s[i]))
return false;
return true;
}
}
std::vector< soc_error_t > soc_reg_field_value_t::errors(bool recursive)
{
(void) recursive;
if(name.size() == 0)
return one_error(make_fatal(name, "empty name"));
else if(!name_valid(name))
return one_error(make_fatal(name, "invalid name"));
else
return no_error();
}
std::vector< soc_error_t > soc_reg_field_t::errors(bool recursive)
{
std::vector< soc_error_t > err;
std::string at(name);
if(name.size() == 0)
err.push_back(make_fatal(at, "empty name"));
else if(!name_valid(name))
err.push_back(make_fatal(at, "invalid name"));
if(last_bit > 31)
err.push_back(make_fatal(at, "last bit is greater than 31"));
if(first_bit > last_bit)
err.push_back(make_fatal(at, "last bit is greater than first bit"));
for(size_t i = 0; i < value.size(); i++)
{
for(size_t j = 0; j < value.size(); j++)
{
if(i == j)
continue;
if(value[i].name == value[j].name)
err.push_back(prefix(make_fatal(value[i].name,
"there are several values with the same name"), at));
if(value[i].value == value[j].value)
err.push_back(prefix(make_warning(value[i].name,
"there are several values with the same value"), at));
}
if(value[i].value > (bitmask() >> first_bit))
err.push_back(prefix(make_warning(at, "value doesn't fit into the field"), value[i].name));
if(recursive)
add_errors(err, value[i].errors(true), at);
}
return err;
}
std::vector< soc_error_t > soc_reg_addr_t::errors(bool recursive)
{
(void) recursive;
if(name.size() == 0)
return one_error(make_fatal("", "empty name"));
else if(!name_valid(name))
return one_error(make_fatal(name, "invalid name"));
else
return no_error();
}
std::vector< soc_error_t > soc_reg_formula_t::errors(bool recursive)
{
(void) recursive;
if(type == REG_FORMULA_STRING && string.size() == 0)
return one_error(make_fatal("", "empty string formula"));
else
return no_error();
}
namespace
{
bool field_overlap(const soc_reg_field_t& a, const soc_reg_field_t& b)
{
return !(a.first_bit > b.last_bit || b.first_bit > a.last_bit);
}
}
std::vector< soc_error_t > soc_reg_t::errors(bool recursive)
{
std::vector< soc_error_t > err;
std::string at(name);
if(name.size() == 0)
err.push_back(make_fatal(at, "empty name"));
else if(!name_valid(name))
err.push_back(make_fatal(at, "invalid name"));
for(size_t i = 0; i < addr.size(); i++)
{
for(size_t j = 0; j < addr.size(); j++)
{
if(i == j)
continue;
if(addr[i].name == addr[j].name)
err.push_back(prefix(make_fatal(addr[i].name,
"there are several instances with the same name"), at));
if(addr[i].addr == addr[j].addr)
err.push_back(prefix(make_fatal(addr[i].name,
"there are several instances with the same address"), at));
}
if(recursive)
add_errors(err, addr[i].errors(true), at);
}
if(recursive)
add_errors(err, formula.errors(true), at);
for(size_t i = 0; i < field.size(); i++)
{
for(size_t j = 0; j < field.size(); j++)
{
if(i == j)
continue;
if(field[i].name == field[j].name)
err.push_back(prefix(make_fatal(field[i].name,
"there are several fields with the same name"), at));
if(field_overlap(field[i], field[j]))
err.push_back(prefix(make_fatal(field[i].name,
"there are overlapping fields"), at));
}
if(recursive)
add_errors(err, field[i].errors(true), at);
}
return err;
}
std::vector< soc_error_t > soc_dev_addr_t::errors(bool recursive)
{
(void) recursive;
if(name.size() == 0)
return one_error(make_fatal("", "empty name"));
else if(!name_valid(name))
return one_error(make_fatal(name, "invalid name"));
else
return no_error();
}
std::vector< soc_error_t > soc_dev_t::errors(bool recursive)
{
std::vector< soc_error_t > err;
std::string at(name);
if(name.size() == 0)
err.push_back(make_fatal(at, "empty name"));
else if(!name_valid(name))
err.push_back(make_fatal(at, "invalid name"));
for(size_t i = 0; i < addr.size(); i++)
{
for(size_t j = 0; j < addr.size(); j++)
{
if(i == j)
continue;
if(addr[i].name == addr[j].name)
err.push_back(prefix(make_fatal(addr[i].name,
"there are several instances with the same name"), at));
if(addr[i].addr == addr[j].addr)
err.push_back(prefix(make_fatal(addr[i].name,
"there are several instances with the same address"), at));
}
if(recursive)
add_errors(err, addr[i].errors(true), at);
}
for(size_t i = 0; i < reg.size(); i++)
{
for(size_t j = 0; j < reg.size(); j++)
{
if(i == j)
continue;
if(reg[i].name == reg[j].name)
err.push_back(prefix(make_fatal(reg[i].name,
"there are several registers with the same name"), at));
}
if(recursive)
add_errors(err, reg[i].errors(true), at);
}
return err;
}
std::vector< soc_error_t > soc_t::errors(bool recursive)
{
std::vector< soc_error_t > err;
std::string at(name);
for(size_t i = 0; i < dev.size(); i++)
{
for(size_t j = 0; j < dev.size(); j++)
{
if(i == j)
continue;
if(dev[i].name == dev[j].name)
err.push_back(prefix(make_fatal(dev[i].name,
"there are several devices with the same name"), at));
}
if(recursive)
add_errors(err, dev[i].errors(true), at);
}
return err;
}
namespace
{
struct formula_evaluator
{
std::string formula;
size_t pos;
std::string error;
bool err(const char *fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vsnprintf(buffer,sizeof(buffer), fmt, args);
va_end(args);
error = buffer;
return false;
}
formula_evaluator(const std::string& s):pos(0)
{
for(size_t i = 0; i < s.size(); i++)
if(!isspace(s[i]))
formula.push_back(s[i]);
}
void adv()
{
pos++;
}
char cur()
{
return end() ? 0 : formula[pos];
}
bool end()
{
return pos >= formula.size();
}
bool parse_digit(char c, int basis, soc_word_t& res)
{
c = tolower(c);
if(isdigit(c))
{
res = c - '0';
return true;
}
if(basis == 16 && isxdigit(c))
{
res = c + 10 - 'a';
return true;
}
return err("invalid digit '%c'", c);
}
bool parse_signed(soc_word_t& res)
{
char op = cur();
if(op == '+' || op == '-')
{
adv();
if(!parse_signed(res))
return false;
if(op == '-')
res *= -1;
return true;
}
else if(op == '(')
{
adv();
if(!parse_expression(res))
return false;
if(cur() != ')')
return err("expected ')', got '%c'", cur());
adv();
return true;
}
else if(isdigit(op))
{
res = op - '0';
adv();
int basis = 10;
if(op == '0' && cur() == 'x')
{
basis = 16;
adv();
}
soc_word_t digit = 0;
while(parse_digit(cur(), basis, digit))
{
res = res * basis + digit;
adv();
}
return true;
}
else if(isalpha(op) || op == '_')
{
std::string name;
while(isalnum(cur()) || cur() == '_')
{
name.push_back(cur());
adv();
}
return get_variable(name, res);
}
else
return err("express signed expression, got '%c'", op);
}
bool parse_term(soc_word_t& res)
{
if(!parse_signed(res))
return false;
while(cur() == '*' || cur() == '/' || cur() == '%')
{
char op = cur();
adv();
soc_word_t tmp;
if(!parse_signed(tmp))
return false;
if(op == '*')
res *= tmp;
else if(tmp != 0)
res = op == '/' ? res / tmp : res % tmp;
else
return err("division by 0");
}
return true;
}
bool parse_expression(soc_word_t& res)
{
if(!parse_term(res))
return false;
while(!end() && (cur() == '+' || cur() == '-'))
{
char op = cur();
adv();
soc_word_t tmp;
if(!parse_term(tmp))
return false;
if(op == '+')
res += tmp;
else
res -= tmp;
}
return true;
}
bool parse(soc_word_t& res, std::string& _error)
{
bool ok = parse_expression(res);
if(ok && !end())
err("unexpected character '%c'", cur());
_error = error;
return ok && end();
}
virtual bool get_variable(std::string name, soc_word_t& res)
{
return err("unknown variable '%s'", name.c_str());
}
};
struct my_evaluator : public formula_evaluator
{
const std::map< std::string, soc_word_t>& var;
my_evaluator(const std::string& formula, const std::map< std::string, soc_word_t>& _var)
:formula_evaluator(formula), var(_var) {}
virtual bool get_variable(std::string name, soc_word_t& res)
{
std::map< std::string, soc_word_t>::const_iterator it = var.find(name);
if(it == var.end())
return formula_evaluator::get_variable(name, res);
else
{
res = it->second;
return true;
}
}
};
}
bool soc_desc_evaluate_formula(const std::string& formula,
const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error)
{
my_evaluator e(formula, var);
return e.parse(result, error);
}

View file

@ -25,6 +25,7 @@
#include <vector>
#include <list>
#include <string>
#include <map>
/**
* These data structures represent the SoC register in a convenient way.
@ -50,6 +51,21 @@ typedef uint32_t soc_addr_t;
typedef uint32_t soc_word_t;
typedef uint32_t soc_reg_flags_t;
/** SoC error gravity level */
enum soc_error_level_t
{
SOC_ERROR_WARNING,
SOC_ERROR_FATAL,
};
/** SoC description error */
struct soc_error_t
{
soc_error_level_t level; /// level (warning, fatal, ...)
std::string location; /// human description of the location
std::string message; /// message
};
/** SoC register generic formula */
enum soc_reg_formula_type_t
{
@ -66,6 +82,8 @@ struct soc_reg_field_value_t
std::string name; /// name of the value
soc_word_t value; /// numeric value
std::string desc; /// human description
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC register field */
@ -90,6 +108,8 @@ struct soc_reg_field_t
}
std::vector< soc_reg_field_value_t > value;
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC register address */
@ -97,6 +117,8 @@ struct soc_reg_addr_t
{
std::string name; /// actual register name
soc_addr_t addr; /// actual register address (relative to device)
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC register formula */
@ -104,6 +126,8 @@ struct soc_reg_formula_t
{
enum soc_reg_formula_type_t type;
std::string string; /// for STRING
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC register */
@ -116,6 +140,8 @@ struct soc_reg_t
soc_reg_flags_t flags; /// ORed value
std::vector< soc_reg_field_t > field;
std::vector< soc_error_t > errors(bool recursive);
};
/** Soc device address */
@ -123,6 +149,8 @@ struct soc_dev_addr_t
{
std::string name; /// actual device name
soc_addr_t addr;
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC device */
@ -135,6 +163,8 @@ struct soc_dev_t
std::vector< soc_dev_addr_t > addr;
std::vector< soc_reg_t > reg;
std::vector< soc_error_t > errors(bool recursive);
};
/** SoC */
@ -144,10 +174,23 @@ struct soc_t
std::string desc; /// SoC name
std::vector< soc_dev_t > dev;
std::vector< soc_error_t > errors(bool recursive);
};
/** Parse a SoC description from a XML file, append it to <soc>. A file
* can contain multiple SoC descriptions */
bool soc_desc_parse_xml(const std::string& filename, std::vector< soc_t >& soc);
/** Parse a SoC description from a XML file, append it to <soc>. */
bool soc_desc_parse_xml(const std::string& filename, soc_t& soc);
/** Write a SoC description to a XML file, overwriting it. A file can contain
* multiple Soc descriptions */
bool soc_desc_produce_xml(const std::string& filename, const soc_t& soc);
/** Normalise a soc description by reordering elemnts so that:
* - devices are sorted by first name
* - registers are sorted by first address
* - fields are sorted by last bit
* - values are sorted by value */
void soc_desc_normalize(soc_t& soc);
/** Formula parser: try to parse and evaluate a formula to a specific value of 'n' */
bool soc_desc_evaluate_formula(const std::string& formula,
const std::map< std::string, soc_word_t>& var, soc_word_t& result, std::string& error);
#endif /* __SOC_DESC__ */

View file

@ -5,13 +5,14 @@
#include <QVector>
#include <QString>
#include "backend.h"
#include "regtab.h"
class Analyser : public QObject
class Analyser : public RegTabPanel
{
Q_OBJECT
public:
Analyser(const SocRef& soc, IoBackend *backend);
virtual ~Analyser();
virtual void AllowWrite(bool en) { Q_UNUSED(en); }
virtual QWidget *GetWidget() = 0;
protected:

View file

@ -0,0 +1,746 @@
#include "aux.h"
#include <QFontMetrics>
#include <QPainter>
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include <QHeaderView>
#include <QDebug>
#include <QElapsedTimer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QTextBlock>
/**
* SocBitRangeValidator
*/
SocBitRangeValidator::SocBitRangeValidator(QObject *parent)
:QValidator(parent)
{
}
void SocBitRangeValidator::fixup(QString& input) const
{
input = input.trimmed();
}
QValidator::State SocBitRangeValidator::validate(QString& input, int& pos) const
{
Q_UNUSED(pos);
int first, last;
State state = parse(input, last, first);
return state;
}
QValidator::State SocBitRangeValidator::parse(const QString& input, int& last, int& first) const
{
// the empty string is always intermediate
if(input.size() == 0)
return Intermediate;
// check if there is ':'
int pos = input.indexOf(':');
if(pos == -1)
pos = input.size();
// if field start with ':', the last bit is implicit and is 31
if(pos > 0)
{
// parse last bit and check it's between 0 and 31
bool ok = false;
last = input.left(pos).toInt(&ok);
if(!ok || last < 0 || last >= 32)
return Invalid;
}
else
last = 31;
// parse first bit
if(pos < input.size() - 1)
{
bool ok = false;
first = input.mid(pos + 1).toInt(&ok);
if(!ok || first < 0 || first > last)
return Invalid;
}
// if input ends with ':', first bit is implicit and is 0
else if(pos == input.size() - 1)
first = 0;
// if there no ':', first=last
else
first = last;
return Acceptable;
}
/**
* SocFieldValidator
*/
SocFieldValidator::SocFieldValidator(QObject *parent)
:QValidator(parent)
{
m_field.first_bit = 0;
m_field.last_bit = 31;
}
SocFieldValidator::SocFieldValidator(const soc_reg_field_t& field, QObject *parent)
:QValidator(parent), m_field(field)
{
}
void SocFieldValidator::fixup(QString& input) const
{
input = input.trimmed();
}
QValidator::State SocFieldValidator::validate(QString& input, int& pos) const
{
Q_UNUSED(pos);
soc_word_t val;
State state = parse(input, val);
return state;
}
QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const
{
// the empty string is always intermediate
if(input.size() == 0)
return Intermediate;
// first check named values
State state = Invalid;
foreach(const soc_reg_field_value_t& value, m_field.value)
{
QString name = QString::fromLocal8Bit(value.name.c_str());
// cannot be a substring if too long or empty
if(input.size() > name.size())
continue;
// check equal string
if(input == name)
{
state = Acceptable;
val = value.value;
break;
}
// check substring
if(name.startsWith(input))
state = Intermediate;
}
// early return for exact match
if(state == Acceptable)
return state;
// do a few special cases for convenience
if(input.compare("0x", Qt::CaseInsensitive) == 0 ||
input.compare("0b", Qt::CaseInsensitive) == 0)
return Intermediate;
// try by parsing
unsigned basis, pos;
if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive))
{
basis = 16;
pos = 2;
}
else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive))
{
basis = 2;
pos = 2;
}
else if(input.size() >= 2 && input.startsWith("0"))
{
basis = 8;
pos = 1;
}
else
{
basis = 10;
pos = 0;
}
bool ok = false;
unsigned long v = input.mid(pos).toULong(&ok, basis);
// if not ok, return result of name parsing
if(!ok)
return state;
// if ok, check if it fits in the number of bits
unsigned nr_bits = m_field.last_bit - m_field.first_bit + 1;
unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1;
if(v <= max)
{
val = v;
return Acceptable;
}
return state;
}
/**
* RegLineEdit
*/
RegLineEdit::RegLineEdit(QWidget *parent)
:QWidget(parent)
{
m_layout = new QHBoxLayout(this);
m_button = new QToolButton(this);
m_button->setCursor(Qt::ArrowCursor);
m_button->setStyleSheet("QToolButton { font-weight: bold; color: white; background: black; }");
m_button->setPopupMode(QToolButton::InstantPopup);
m_edit = new QLineEdit(this);
m_layout->addWidget(m_button);
m_layout->addWidget(m_edit);
m_menu = new QMenu(this);
connect(m_menu->addAction("Write"), SIGNAL(triggered()), this, SLOT(OnWriteAct()));
connect(m_menu->addAction("Set"), SIGNAL(triggered()), this, SLOT(OnSetAct()));
connect(m_menu->addAction("Clear"), SIGNAL(triggered()), this, SLOT(OnClearAct()));
connect(m_menu->addAction("Toggle"), SIGNAL(triggered()), this, SLOT(OnToggleAct()));
EnableSCT(false);
SetReadOnly(false);
ShowMode(true);
SetMode(Write);
}
void RegLineEdit::SetReadOnly(bool ro)
{
m_edit->setReadOnly(ro);
m_readonly = ro;
ShowMode(!ro);
}
void RegLineEdit::EnableSCT(bool en)
{
m_has_sct = en;
if(!m_has_sct)
{
m_button->setMenu(0);
SetMode(Write);
}
else
m_button->setMenu(m_menu);
}
RegLineEdit::~RegLineEdit()
{
}
QLineEdit *RegLineEdit::GetLineEdit()
{
return m_edit;
}
void RegLineEdit::ShowMode(bool show)
{
if(show)
m_button->show();
else
m_button->hide();
}
void RegLineEdit::OnWriteAct()
{
SetMode(Write);
}
void RegLineEdit::OnSetAct()
{
SetMode(Set);
}
void RegLineEdit::OnClearAct()
{
SetMode(Clear);
}
void RegLineEdit::OnToggleAct()
{
SetMode(Toggle);
}
void RegLineEdit::SetMode(EditMode mode)
{
m_mode = mode;
switch(m_mode)
{
case Write: m_button->setText("WR"); break;
case Set: m_button->setText("SET"); break;
case Clear: m_button->setText("CLR"); break;
case Toggle: m_button->setText("TOG"); break;
default: break;
}
}
RegLineEdit::EditMode RegLineEdit::GetMode()
{
return m_mode;
}
void RegLineEdit::setText(const QString& text)
{
m_edit->setText(text);
}
QString RegLineEdit::text() const
{
return m_edit->text();
}
/**
* SocFieldItemDelegate
*/
QString SocFieldItemDelegate::displayText(const QVariant& value, const QLocale& locale) const
{
if(value.type() == QVariant::UInt)
return QString("0x%1").arg(value.toUInt(), (m_bitcount + 3) / 4, 16, QChar('0'));
else
return QStyledItemDelegate::displayText(value, locale);
}
/**
* SocFieldEditor
*/
SocFieldEditor::SocFieldEditor(const soc_reg_field_t& field, QWidget *parent)
:QLineEdit(parent), m_reg_field(field)
{
m_validator = new SocFieldValidator(field);
setValidator(m_validator);
}
SocFieldEditor::~SocFieldEditor()
{
delete m_validator;
}
uint SocFieldEditor::field() const
{
soc_word_t v;
/* in case validator fails to parse, return old value */
if(m_validator->parse(text(), v) == QValidator::Acceptable)
return v;
else
return m_field;
}
void SocFieldEditor::setField(uint field)
{
m_field = field;
int digits = (m_reg_field.last_bit - m_reg_field.first_bit + 4) / 4;
setText(QString("0x%1").arg(field, digits, 16, QChar('0')));
}
/**
* SocFieldEditorCreator
*/
QWidget *SocFieldEditorCreator::createWidget(QWidget *parent) const
{
return new SocFieldEditor(m_field, parent);
}
QByteArray SocFieldEditorCreator::valuePropertyName() const
{
return QByteArray("field");
}
/**
* RegSexyDisplay
*/
RegSexyDisplay::RegSexyDisplay(const SocRegRef& reg, QWidget *parent)
:QWidget(parent), m_reg(reg)
{
m_size = QSize();
}
int RegSexyDisplay::separatorSize() const
{
return 1;
}
int RegSexyDisplay::marginSize() const
{
return fontMetrics().height() / 3;
}
int RegSexyDisplay::textSep() const
{
return marginSize() / 2;
}
int RegSexyDisplay::headerHeight() const
{
return 2 * marginSize() + textSep() + 2 * fontMetrics().height();
}
int RegSexyDisplay::columnWidth() const
{
return 2 * marginSize() + fontMetrics().height();
}
int RegSexyDisplay::maxContentHeight() const
{
int max = 0;
QFontMetrics metrics = fontMetrics();
for(size_t i = 0; i < m_reg.GetReg().field.size(); i++)
{
QString s = QString::fromStdString(m_reg.GetReg().field[i].name);
// add extra spaces arounds
s = " " + s + " ";
max = qMax(max, metrics.boundingRect(s).width());
}
return 2 * marginSize() + max;
}
int RegSexyDisplay::gapHeight() const
{
return marginSize() / 2;
}
QSize RegSexyDisplay::minimumSizeHint() const
{
/* cache computation because it's expensive */
if(m_size.isValid())
return m_size;
/* width: display 32 columns + 33 vertical separators */
m_size.setWidth(32 * columnWidth() + 33 * separatorSize());
/* height: one separator + two digits + one separator + margin + separator
* + names + separator */
m_size.setHeight(4 * separatorSize() + headerHeight() + gapHeight() + maxContentHeight());
return m_size;
}
QSize RegSexyDisplay::sizeHint() const
{
return minimumSizeHint();
}
void RegSexyDisplay::paintEvent(QPaintEvent *event)
{
// FIXME could be optimised with QStaticText
Q_UNUSED(event);
int txt_h = fontMetrics().height();
int sep_sz = separatorSize();
int w = width();
int h = height() - 1;
int col_w = (w - 33 * sep_sz) / 32;
int hdr_h = headerHeight();
int gap_h = gapHeight();
int tot_w = 33 * sep_sz + 32 * col_w;
int margin = marginSize();
int txt_sep = textSep();
int tot_hdr_sz = 2 * sep_sz + hdr_h;
// computer xshift
int x_shift = (w - tot_w) / 2;
#define ith_col_x(i) (x_shift + (i) * (sep_sz + col_w))
QPainter painter(this);
QBrush back_brush = palette().base();
QBrush line_brush = palette().dark();
// fill interesting zone with base
painter.fillRect(x_shift, 0, tot_w, h, back_brush);
// draw top and bottom lines
painter.setPen(QPen(palette().dark(), sep_sz));
painter.fillRect(x_shift, 0, tot_w, sep_sz, line_brush);
painter.fillRect(x_shift, h - sep_sz, tot_w, sep_sz, line_brush);
// draw intemediate lines
for(int i = 0; i <= 32; i++)
painter.fillRect(ith_col_x(i), 0, sep_sz, 2 * sep_sz + hdr_h, line_brush);
// draw bottom header lines
painter.fillRect(ith_col_x(0), sep_sz + hdr_h, tot_w, sep_sz, line_brush);
painter.fillRect(ith_col_x(0), tot_hdr_sz + gap_h, tot_w, sep_sz, line_brush);
// redraw some lines but wider
for(int i = 4; i < 32; i += 4)
painter.fillRect(ith_col_x(i) - sep_sz, 0, 3 * sep_sz, tot_hdr_sz, line_brush);
// draw numbers in the header
painter.setPen(palette().brush(QPalette::ButtonText).color());
for(int i = 0; i < 32; i++)
{
QRect r(ith_col_x(i), sep_sz + margin, col_w, txt_h);
painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) / 10));
r.translate(0, txt_h + txt_sep);
painter.drawText(r, Qt::AlignCenter, QString("%1").arg((31 - i) % 10));
}
// display content
for(size_t i = 0; i < m_reg.GetReg().field.size(); i++)
{
const soc_reg_field_t& field = m_reg.GetReg().field[i];
QRect r(QPoint(ith_col_x(31 - field.last_bit) + sep_sz, tot_hdr_sz),
QPoint(ith_col_x(32 - field.first_bit), h - sep_sz));
painter.fillRect(r.x() - sep_sz, r.y(), sep_sz, r.height(), line_brush);
painter.fillRect(r.right(), r.y(), sep_sz, r.height(), line_brush);
r.setY(r.y() + gap_h + sep_sz);
// draw rotated text
painter.save();
painter.translate(r.bottomLeft());
painter.rotate(-90);
//painter.fillRect(QRect(0, 0, r.height(), r.width()), QBrush(Qt::red));
QRect r2(0, 0, r.height(), r.width());
painter.drawText(r2, Qt::AlignCenter, QString::fromStdString(field.name));
painter.restore();
}
#undef ith_col_x
}
/**
* GrowingTextEdit
*/
GrowingTextEdit::GrowingTextEdit(QWidget *parent)
:QTextEdit(parent)
{
connect(this, SIGNAL(textChanged()), this, SLOT(TextChanged()));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
void GrowingTextEdit::TextChanged()
{
int content_size = document()->documentLayout()->documentSize().height();
content_size = qMax(content_size, fontMetrics().height());
setFixedHeight(content_size + contentsMargins().top() + contentsMargins().bottom());
}
/**
* GrowingTableWidget
*/
GrowingTableWidget::GrowingTableWidget(QWidget *parent)
:QTableWidget(parent)
{
connect(model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
this, SLOT(DataChanged(const QModelIndex&, const QModelIndex&)));
}
void GrowingTableWidget::DataChanged(const QModelIndex& tl, const QModelIndex& br)
{
Q_UNUSED(tl);
Q_UNUSED(br);
resizeRowsToContents();
resizeColumnsToContents();
int h = contentsMargins().top() + contentsMargins().bottom();
h += horizontalHeader()->height();
for(int i = 0; i < rowCount(); i++)
h += rowHeight(i);
setMinimumHeight(h);
}
/**
* MyTextEditor
*/
MyTextEditor::MyTextEditor(QWidget *parent)
:QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout;
m_toolbar = new QToolBar(this);
m_edit = new QTextEdit(this);
layout->addWidget(m_toolbar, 0);
layout->addWidget(m_edit, 1);
setLayout(layout);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_edit->setAcceptRichText(false);
m_edit->setAutoFormatting(QTextEdit::AutoAll);
m_bold_button = new QToolButton(this);
m_bold_button->setIcon(QIcon::fromTheme("format-text-bold"));
m_bold_button->setText("bold");
m_bold_button->setCheckable(true);
m_italic_button = new QToolButton(this);
m_italic_button->setIcon(QIcon::fromTheme("format-text-italic"));
m_italic_button->setText("italic");
m_italic_button->setCheckable(true);
m_underline_button = new QToolButton(this);
m_underline_button->setIcon(QIcon::fromTheme("format-text-underline"));
m_underline_button->setText("underline");
m_underline_button->setCheckable(true);
m_toolbar->addWidget(m_bold_button);
m_toolbar->addWidget(m_italic_button);
m_toolbar->addWidget(m_underline_button);
connect(m_bold_button, SIGNAL(toggled(bool)), this, SLOT(OnTextBold(bool)));
connect(m_italic_button, SIGNAL(toggled(bool)), this, SLOT(OnTextItalic(bool)));
connect(m_underline_button, SIGNAL(toggled(bool)), this, SLOT(OnTextUnderline(bool)));
connect(m_edit, SIGNAL(textChanged()), this, SLOT(OnInternalTextChanged()));
connect(m_edit, SIGNAL(currentCharFormatChanged(const QTextCharFormat&)),
this, SLOT(OnCharFormatChanged(const QTextCharFormat&)));
SetGrowingMode(false);
SetReadOnly(false);
}
void MyTextEditor::SetReadOnly(bool en)
{
m_read_only = en;
if(en)
m_toolbar->hide();
else
m_toolbar->hide();
m_edit->setReadOnly(en);
}
void MyTextEditor::SetGrowingMode(bool en)
{
m_growing_mode = en;
if(en)
{
m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
OnTextChanged();
}
else
{
m_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_edit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
}
void MyTextEditor::OnInternalTextChanged()
{
if(m_growing_mode)
{
int content_size = m_edit->document()->documentLayout()->documentSize().height();
content_size = qMax(content_size, m_edit->fontMetrics().height());
m_edit->setMinimumHeight(content_size + m_edit->contentsMargins().top() +
m_edit->contentsMargins().bottom());
}
emit OnTextChanged();
}
void MyTextEditor::OnTextBold(bool checked)
{
QTextCursor cursor = m_edit->textCursor();
QTextCharFormat fmt = cursor.charFormat();
fmt.setFontWeight(checked ? QFont::Bold : QFont::Normal);
cursor.setCharFormat(fmt);
m_edit->setTextCursor(cursor);
}
void MyTextEditor::OnTextItalic(bool checked)
{
QTextCursor cursor = m_edit->textCursor();
QTextCharFormat fmt = cursor.charFormat();
fmt.setFontItalic(checked);
cursor.setCharFormat(fmt);
m_edit->setTextCursor(cursor);
}
void MyTextEditor::OnTextUnderline(bool checked)
{
QTextCursor cursor = m_edit->textCursor();
QTextCharFormat fmt = cursor.charFormat();
fmt.setFontUnderline(checked);
cursor.setCharFormat(fmt);
m_edit->setTextCursor(cursor);
}
void MyTextEditor::OnCharFormatChanged(const QTextCharFormat& fmt)
{
/* NOTE: changing the button states programmaticaly doesn't trigger
* the toggled() signals, otherwise it would result in a loop
* between this function and OnText{Bold,Italic,Underline,...} */
m_bold_button->setChecked(fmt.fontWeight() > QFont::Normal);
m_italic_button->setChecked(fmt.fontItalic());
m_underline_button->setChecked(fmt.fontUnderline());
}
void MyTextEditor::SetTextHtml(const QString& text)
{
m_edit->setHtml(text);
}
QString MyTextEditor::GetTextHtml()
{
return m_edit->toPlainText();
}
bool MyTextEditor::IsModified()
{
return m_edit->document()->isModified();
}
/**
* MySwitchableTextEditor
*/
MySwitchableTextEditor::MySwitchableTextEditor(QWidget *parent)
:QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
m_edit = new MyTextEditor(this);
m_label = new QLabel(this);
m_label->setTextFormat(Qt::RichText);
m_label->setAlignment(Qt::AlignTop);
m_line = new QLineEdit(this);
layout->addWidget(m_label);
layout->addWidget(m_edit);
layout->addWidget(m_line);
setLayout(layout);
m_editor_mode = false;
m_line_mode = false;
UpdateVisibility();
}
void MySwitchableTextEditor::SetEditorMode(bool edit)
{
if(edit == m_editor_mode)
return;
QString text = GetTextHtml();
m_editor_mode = edit;
UpdateVisibility();
SetTextHtml(text);
}
QString MySwitchableTextEditor::GetTextHtml()
{
if(m_editor_mode)
return m_line_mode ? m_line->text() : m_edit->GetTextHtml();
else
return m_label->text();
}
void MySwitchableTextEditor::SetTextHtml(const QString& text)
{
if(m_editor_mode)
{
if(m_line_mode)
m_line->setText(text);
else
m_edit->SetTextHtml(text);
}
else
m_label->setText(text);
}
MyTextEditor *MySwitchableTextEditor::GetEditor()
{
return m_edit;
}
void MySwitchableTextEditor::SetLineMode(bool en)
{
if(m_line_mode == en)
return;
QString text = GetTextHtml();
m_line_mode = en;
SetTextHtml(text);
UpdateVisibility();
}
QLineEdit *MySwitchableTextEditor::GetLineEdit()
{
return m_line;
}
void MySwitchableTextEditor::UpdateVisibility()
{
m_label->setVisible(!m_editor_mode);
m_edit->setVisible(m_editor_mode && !m_line_mode);
m_line->setVisible(m_editor_mode && m_line_mode);
}
QLabel *MySwitchableTextEditor::GetLabel()
{
return m_label;
}
bool MySwitchableTextEditor::IsModified()
{
if(!m_editor_mode)
return false;
return m_line_mode ? m_line->isModified() : m_edit->IsModified();
}

View file

@ -0,0 +1,227 @@
#ifndef AUX_H
#define AUX_H
#include <QEvent>
#include <QPaintEvent>
#include <QLineEdit>
#include <QValidator>
#include <QToolButton>
#include <QMenu>
#include <QHBoxLayout>
#include <QTextEdit>
#include <QTableWidget>
#include <QToolBar>
#include <QLabel>
#include <QHBoxLayout>
#include <QItemEditorCreatorBase>
#include <QStyledItemDelegate>
#include "backend.h"
class SocBitRangeValidator : public QValidator
{
Q_OBJECT
public:
SocBitRangeValidator(QObject *parent = 0);
virtual void fixup(QString& input) const;
virtual State validate(QString& input, int& pos) const;
/* validate and return the interpreted value */
State parse(const QString& input, int& last_bit, int& first_bit) const;
};
class SocFieldValidator : public QValidator
{
Q_OBJECT
public:
SocFieldValidator(QObject *parent = 0);
SocFieldValidator(const soc_reg_field_t& field, QObject *parent = 0);
virtual void fixup(QString& input) const;
virtual State validate(QString& input, int& pos) const;
/* validate and return the interpreted value */
State parse(const QString& input, soc_word_t& val) const;
protected:
soc_reg_field_t m_field;
};
class RegLineEdit : public QWidget
{
Q_OBJECT
public:
enum EditMode
{
Write, Set, Clear, Toggle
};
RegLineEdit(QWidget *parent = 0);
~RegLineEdit();
void SetReadOnly(bool ro);
void EnableSCT(bool en);
void SetMode(EditMode mode);
EditMode GetMode();
QLineEdit *GetLineEdit();
void setText(const QString& text);
QString text() const;
Q_PROPERTY(QString text READ text WRITE setText USER true)
protected slots:
void OnWriteAct();
void OnSetAct();
void OnClearAct();
void OnToggleAct();
protected:
void ShowMode(bool show);
void DoAutoHide();
QHBoxLayout *m_layout;
QToolButton *m_button;
QLineEdit *m_edit;
EditMode m_mode;
bool m_has_sct;
bool m_readonly;
QMenu *m_menu;
};
class SocFieldItemDelegate : public QStyledItemDelegate
{
public:
SocFieldItemDelegate(QObject *parent = 0):QStyledItemDelegate(parent), m_bitcount(32) {}
SocFieldItemDelegate(const soc_reg_field_t& field, QObject *parent = 0)
:QStyledItemDelegate(parent), m_bitcount(field.last_bit - field.first_bit + 1) {}
virtual QString displayText(const QVariant& value, const QLocale& locale) const;
protected:
int m_bitcount;
};
class SocFieldEditor : public QLineEdit
{
Q_OBJECT
Q_PROPERTY(uint field READ field WRITE setField USER true)
public:
SocFieldEditor(const soc_reg_field_t& field, QWidget *parent = 0);
virtual ~SocFieldEditor();
uint field() const;
void setField(uint field);
protected:
SocFieldValidator *m_validator;
uint m_field;
soc_reg_field_t m_reg_field;
};
class SocFieldEditorCreator : public QItemEditorCreatorBase
{
public:
SocFieldEditorCreator() { m_field.first_bit = 0; m_field.last_bit = 31; }
SocFieldEditorCreator(const soc_reg_field_t& field):m_field(field) {}
virtual QWidget *createWidget(QWidget *parent) const;
virtual QByteArray valuePropertyName() const;
protected:
soc_reg_field_t m_field;
};
class RegSexyDisplay : public QWidget
{
Q_OBJECT
public:
RegSexyDisplay(const SocRegRef& reg, QWidget *parent = 0);
QSize minimumSizeHint() const;
QSize sizeHint() const;
protected:
int marginSize() const;
int separatorSize() const;
int columnWidth() const;
int headerHeight() const;
int gapHeight() const;
int maxContentHeight() const;
int textSep() const;
void paintEvent(QPaintEvent *event);
private:
SocRegRef m_reg;
mutable QSize m_size;
};
class GrowingTextEdit : public QTextEdit
{
Q_OBJECT
public:
GrowingTextEdit(QWidget *parent = 0);
protected slots:
void TextChanged();
};
class GrowingTableWidget : public QTableWidget
{
Q_OBJECT
public:
GrowingTableWidget(QWidget *parent = 0);
protected slots:
void DataChanged(const QModelIndex& tl, const QModelIndex& br);
};
class MyTextEditor : public QWidget
{
Q_OBJECT
public:
MyTextEditor(QWidget *parent = 0);
void SetGrowingMode(bool en);
void SetReadOnly(bool ro);
void SetTextHtml(const QString& text);
QString GetTextHtml();
bool IsModified();
signals:
void OnTextChanged();
protected slots:
void OnInternalTextChanged();
void OnTextBold(bool checked);
void OnTextItalic(bool checked);
void OnTextUnderline(bool checked);
void OnCharFormatChanged(const QTextCharFormat& fmt);
protected:
bool m_growing_mode;
bool m_read_only;
QToolBar *m_toolbar;
QTextEdit *m_edit;
QToolButton *m_bold_button;
QToolButton *m_italic_button;
QToolButton *m_underline_button;
};
class MySwitchableTextEditor : public QWidget
{
Q_OBJECT
public:
MySwitchableTextEditor(QWidget *parent = 0);
QString GetTextHtml();
void SetTextHtml(const QString& text);
void SetEditorMode(bool en);
MyTextEditor *GetEditor();
QLineEdit *GetLineEdit();
QLabel *GetLabel();
void SetLineMode(bool en);
bool IsModified();
protected:
void UpdateVisibility();
bool m_editor_mode;
bool m_line_mode;
QLabel *m_label;
MyTextEditor *m_edit;
QLineEdit *m_line;
};
#endif /* AUX_H */

View file

@ -4,6 +4,36 @@
#include <QFileInfo>
#include "backend.h"
/**
* SocFile
*/
SocFile::SocFile()
:m_valid(false)
{
}
SocFile::SocFile(const QString& filename)
:m_filename(filename)
{
m_valid = soc_desc_parse_xml(filename.toStdString(), m_soc);
soc_desc_normalize(m_soc);
}
bool SocFile::IsValid()
{
return m_valid;
}
SocRef SocFile::GetSocRef()
{
return SocRef(this);
}
QString SocFile::GetFilename()
{
return m_filename;
}
/**
* Backend
*/
@ -12,33 +42,31 @@ Backend::Backend()
{
}
QStringList Backend::GetSocNameList()
QList< SocFileRef > Backend::GetSocFileList()
{
QStringList sl;
foreach(const soc_t& soc, m_socs)
sl.append(QString(soc.name.c_str()));
return sl;
QList< SocFileRef > list;
for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
list.append(SocFileRef(&(*it)));
return list;
}
bool Backend::GetSocByName(const QString& name, SocRef& s)
QList< SocRef > Backend::GetSocList()
{
for(std::list< soc_t >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
if(it->name == name.toStdString())
{
s = SocRef(&(*it));
return true;
}
return false;
QList< SocRef > list;
for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
list.append(it->GetSocRef());
return list;
}
bool Backend::LoadSocDesc(const QString& filename)
{
std::vector< soc_t > new_socs;
bool ret = soc_desc_parse_xml(filename.toStdString(), new_socs);
for(size_t i = 0; i < new_socs.size(); i++)
m_socs.push_back(new_socs[i]);
SocFile f(filename);
if(!f.IsValid())
return false;
m_socs.push_back(f);
emit OnSocListChanged();
return ret;
return true;
}
IoBackend *Backend::CreateFileIoBackend(const QString& filename)
@ -321,7 +349,7 @@ HWStubBackendHelper::HWStubBackendHelper()
if(m_hotplug)
{
m_hotplug = LIBUSB_SUCCESS == libusb_hotplug_register_callback(
NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
NULL, (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
LIBUSB_HOTPLUG_ENUMERATE, HWSTUB_USB_VID, HWSTUB_USB_PID, HWSTUB_CLASS,
&HWStubBackendHelper::HotPlugCallback, reinterpret_cast< void* >(this), &m_hotplug_handle);
}
@ -364,6 +392,7 @@ void HWStubBackendHelper::OnHotPlug(bool arrived, struct libusb_device *dev)
int HWStubBackendHelper::HotPlugCallback(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event, void *user_data)
{
Q_UNUSED(ctx);
HWStubBackendHelper *helper = reinterpret_cast< HWStubBackendHelper* >(user_data);
switch(event)
{

View file

@ -5,10 +5,11 @@
#include <QStringList>
#include <QMap>
#include <QVector>
#include "soc_desc.hpp"
#include <QMetaType>
#ifdef HAVE_HWSTUB
#include "hwstub.h"
#endif
#include "soc.h"
class IoBackend : public QObject
{
@ -62,18 +63,18 @@ class DummyIoBackend : public IoBackend
public:
DummyIoBackend() {}
virtual bool SupportAccess(AccessType type) { (void) type; return false; }
virtual bool SupportAccess(AccessType type) { Q_UNUSED(type); return false; }
virtual QString GetSocName() { return ""; }
virtual bool ReadRegister(const QString& name, soc_word_t& value)
{ (void) name; (void) value; return false; }
{ Q_UNUSED(name); Q_UNUSED(value); return false; }
virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value)
{ (void) addr; (void) value; return false; }
{ Q_UNUSED(addr); Q_UNUSED(value); return false; }
virtual bool Reload() { return false; }
virtual bool IsReadOnly() { return true; }
virtual bool WriteRegister(const QString& name, soc_word_t value, WriteMode mode)
{ (void) name; (void) value; (void) mode; return false; }
{ Q_UNUSED(name); Q_UNUSED(value); Q_UNUSED(mode); return false; }
virtual bool WriteRegister(soc_addr_t addr, soc_word_t value, WriteMode mode)
{ (void) addr; (void) value; (void) mode; return false; }
{ Q_UNUSED(addr); Q_UNUSED(value); Q_UNUSED(mode); return false; }
virtual bool IsDirty() { return false; }
virtual bool Commit() { return false; }
};
@ -90,12 +91,12 @@ public:
virtual QString GetSocName();
virtual bool ReadRegister(const QString& name, soc_word_t& value);
virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value)
{ (void) addr; (void) value; return false; }
{ Q_UNUSED(addr); Q_UNUSED(value); return false; }
virtual bool Reload();
virtual bool IsReadOnly() { return m_readonly; }
virtual bool WriteRegister(const QString& name, soc_word_t value, WriteMode mode);
virtual bool WriteRegister(soc_addr_t addr, soc_word_t value, WriteMode mode)
{ (void) addr; (void) value; (void) mode; return false; }
{ Q_UNUSED(addr); Q_UNUSED(value); Q_UNUSED(mode); return false; }
virtual bool IsDirty() { return m_dirty; }
virtual bool Commit();
@ -149,12 +150,12 @@ public:
virtual bool SupportAccess(AccessType type) { return type == ByAddress; }
virtual QString GetSocName();
virtual bool ReadRegister(const QString& name, soc_word_t& value)
{ (void) name; (void) value; return false; }
{ Q_UNUSED(name); Q_UNUSED(value); return false; }
virtual bool ReadRegister(soc_addr_t addr, soc_word_t& value);
virtual bool Reload();
virtual bool IsReadOnly() { return false; }
virtual bool WriteRegister(const QString& name, soc_word_t value, WriteMode mode)
{ (void) name; (void) value; (void) mode; return false; }
{ Q_UNUSED(name); Q_UNUSED(value); Q_UNUSED(mode); return false; }
virtual bool WriteRegister(soc_addr_t addr, soc_word_t value, WriteMode mode);
virtual bool IsDirty() { return false; }
virtual bool Commit() { return true; }
@ -191,16 +192,48 @@ protected:
};
#endif
class SocRef
class SocRef;
class SocFile
{
public:
SocRef():m_soc(0) {}
SocRef(const soc_t *soc):m_soc(soc) {}
const soc_t& GetSoc() const { return *m_soc; }
SocFile();
SocFile(const QString& filename);
bool IsValid();
SocRef GetSocRef();
QString GetFilename();
soc_t& GetSoc() { return m_soc; }
protected:
const soc_t *m_soc;
bool m_valid;
QString m_filename;
soc_t m_soc;
};
class SocFileRef
{
public:
SocFileRef():m_socfile(0) {}
SocFileRef(SocFile *file):m_socfile(file) {}
SocFile *GetSocFile() const { return m_socfile; }
protected:
SocFile *m_socfile;
};
Q_DECLARE_METATYPE(SocFileRef)
class SocRef : public SocFileRef
{
public:
SocRef() {}
SocRef(SocFile *file):SocFileRef(file) {}
soc_t& GetSoc() const { return GetSocFile()->GetSoc(); }
};
Q_DECLARE_METATYPE(SocRef)
class SocDevRef : public SocRef
{
public:
@ -208,9 +241,9 @@ public:
SocDevRef(const SocRef& soc, int dev_idx, int dev_addr_idx)
:SocRef(soc), m_dev_idx(dev_idx), m_dev_addr_idx(dev_addr_idx) {}
int GetDevIndex() const { return m_dev_idx; }
const soc_dev_t& GetDev() const { return GetSoc().dev[GetDevIndex()]; }
soc_dev_t& GetDev() const { return GetSoc().dev[GetDevIndex()]; }
int GetDevAddrIndex() const { return m_dev_addr_idx; }
const soc_dev_addr_t& GetDevAddr() const { return GetDev().addr[GetDevAddrIndex()]; }
soc_dev_addr_t& GetDevAddr() const { return GetDev().addr[GetDevAddrIndex()]; }
protected:
int m_dev_idx, m_dev_addr_idx;
};
@ -222,9 +255,9 @@ public:
SocRegRef(const SocDevRef& dev, int reg_idx, int reg_addr_idx)
:SocDevRef(dev), m_reg_idx(reg_idx), m_reg_addr_idx(reg_addr_idx) {}
int GetRegIndex() const { return m_reg_idx; }
const soc_reg_t& GetReg() const { return GetDev().reg[GetRegIndex()]; }
soc_reg_t& GetReg() const { return GetDev().reg[GetRegIndex()]; }
int GetRegAddrIndex() const { return m_reg_addr_idx; }
const soc_reg_addr_t& GetRegAddr() const { return GetReg().addr[GetRegAddrIndex()]; }
soc_reg_addr_t& GetRegAddr() const { return GetReg().addr[GetRegAddrIndex()]; }
protected:
int m_reg_idx, m_reg_addr_idx;
};
@ -236,7 +269,7 @@ public:
SocFieldRef(const SocRegRef& reg, int field_idx)
:SocRegRef(reg), m_field_idx(field_idx) {}
int GetFieldIndex() const { return m_field_idx; }
const soc_reg_field_t& GetField() const { return GetReg().field[GetFieldIndex()]; }
soc_reg_field_t& GetField() const { return GetReg().field[GetFieldIndex()]; }
protected:
int m_field_idx;
};
@ -247,9 +280,9 @@ class Backend : public QObject
public:
Backend();
QStringList GetSocNameList();
QList< SocFileRef > GetSocFileList();
QList< SocRef > GetSocList();
bool LoadSocDesc(const QString& filename);
bool GetSocByName(const QString& name, SocRef& s);
IoBackend *CreateDummyIoBackend();
IoBackend *CreateFileIoBackend(const QString& filename);
#ifdef HAVE_HWSTUB
@ -259,7 +292,7 @@ public:
signals:
void OnSocListChanged();
private:
std::list< soc_t > m_socs;
std::list< SocFile > m_socs;
};
class BackendHelper

View file

@ -12,6 +12,7 @@
#include "mainwindow.h"
#include "regtab.h"
#include "regedit.h"
MyTabWidget::MyTabWidget()
{
@ -20,23 +21,37 @@ MyTabWidget::MyTabWidget()
connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(OnCloseTab(int)));
}
void MyTabWidget::OnCloseTab(int index)
bool MyTabWidget::CloseTab(int index)
{
QWidget *w = this->widget(index);
DocumentTab *doc = dynamic_cast< DocumentTab* >(w);
if(doc->Quit())
{
removeTab(index);
delete w;
return true;
}
else
return false;
}
void MyTabWidget::OnCloseTab(int index)
{
CloseTab(index);
}
MainWindow::MainWindow(Backend *backend)
:m_backend(backend)
{
QAction *new_regtab_act = new QAction(QIcon::fromTheme("document-new"), tr("&Register Tab"), this);
QAction *new_regtab_act = new QAction(QIcon::fromTheme("document-new"), tr("Register &Tab"), this);
QAction *new_regedit_act = new QAction(QIcon::fromTheme("document-edit"), tr("Register &Editor"), this);
QAction *load_desc_act = new QAction(QIcon::fromTheme("document-open"), tr("&Soc Description"), this);
QAction *quit_act = new QAction(QIcon::fromTheme("application-exit"), tr("&Quit"), this);
QAction *about_act = new QAction(QIcon::fromTheme("help-about"), tr("&About"), this);
QAction *about_qt_act = new QAction(QIcon::fromTheme("help-about"), tr("About &Qt"), this);
connect(new_regtab_act, SIGNAL(triggered()), this, SLOT(OnNewRegTab()));
connect(new_regedit_act, SIGNAL(triggered()), this, SLOT(OnNewRegEdit()));
connect(load_desc_act, SIGNAL(triggered()), this, SLOT(OnLoadDesc()));
connect(quit_act, SIGNAL(triggered()), this, SLOT(OnQuit()));
connect(about_act, SIGNAL(triggered()), this, SLOT(OnAbout()));
@ -48,6 +63,7 @@ MainWindow::MainWindow(Backend *backend)
file_menu->addAction(quit_act);
new_submenu->addAction(new_regtab_act);
new_submenu->addAction(new_regedit_act);
load_submenu->addAction(load_desc_act);
@ -91,6 +107,8 @@ void MainWindow::OnAboutQt()
void MainWindow::closeEvent(QCloseEvent *event)
{
if(!Quit())
return event->ignore();
WriteSettings();
event->accept();
}
@ -100,7 +118,7 @@ void MainWindow::OnLoadDesc()
QFileDialog *fd = new QFileDialog(this);
fd->setFilter("XML files (*.xml);;All files (*)");
fd->setFileMode(QFileDialog::ExistingFiles);
fd->setDirectory(Settings::Get()->value("mainwindow/loaddescdir", QDir::currentPath()).toString());
fd->setDirectory(Settings::Get()->value("loaddescdir", QDir::currentPath()).toString());
if(fd->exec())
{
QStringList filenames = fd->selectedFiles();
@ -111,11 +129,42 @@ void MainWindow::OnLoadDesc()
msg.setText(QString("Cannot load ") + filenames[i]);
msg.exec();
}
Settings::Get()->setValue("mainwindow/loaddescdir", fd->directory().absolutePath());
Settings::Get()->setValue("loaddescdir", fd->directory().absolutePath());
}
}
void MainWindow::OnTabModified(bool modified)
{
QWidget *sender = qobject_cast< QWidget* >(QObject::sender());
int index = m_tab->indexOf(sender);
if(modified)
m_tab->setTabIcon(index, QIcon::fromTheme("document-save"));
else
m_tab->setTabIcon(index, QIcon());
}
void MainWindow::AddTab(QWidget *tab, const QString& title)
{
connect(tab, SIGNAL(OnModified(bool)), this, SLOT(OnTabModified(bool)));
m_tab->setCurrentIndex(m_tab->addTab(tab, title));
}
void MainWindow::OnNewRegTab()
{
m_tab->addTab(new RegTab(m_backend), "Register Tab");
AddTab(new RegTab(m_backend, this), "Register Tab");
}
void MainWindow::OnNewRegEdit()
{
AddTab(new RegEdit(m_backend, this), "Register Editor");
}
bool MainWindow::Quit()
{
while(m_tab->count() > 0)
{
if(!m_tab->CloseTab(0))
return false;
}
return true;
}

View file

@ -7,11 +7,19 @@
#include "backend.h"
#include "settings.h"
class DocumentTab
{
public:
virtual bool Quit() = 0;
virtual void OnModified(bool modified) = 0;
};
class MyTabWidget : public QTabWidget
{
Q_OBJECT
public:
MyTabWidget();
bool CloseTab(int index);
private slots:
void OnCloseTab(int index);
@ -30,15 +38,21 @@ public:
private:
void closeEvent(QCloseEvent *event);
protected:
void AddTab(QWidget *tab, const QString& title);
bool Quit();
private slots:
void OnQuit();
void OnAbout();
void OnAboutQt();
void OnLoadDesc();
void OnNewRegTab();
void OnNewRegEdit();
void OnTabModified(bool modified);
private:
QTabWidget *m_tab;
MyTabWidget *m_tab;
Backend *m_backend;
};

View file

@ -1,7 +1,9 @@
QT += widgets
HEADERS += mainwindow.h backend.h regtab.h analyser.h settings.h std_analysers.h
SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp std_analysers.cpp settings.cpp
HEADERS += mainwindow.h backend.h regtab.h analyser.h settings.h \
std_analysers.h aux.h regdisplaypanel.h regedit.h
SOURCES += main.cpp mainwindow.cpp regtab.cpp backend.cpp analyser.cpp \
std_analysers.cpp settings.cpp aux.cpp regdisplaypanel.cpp regedit.cpp
LIBS += -L../lib/ -lsocdesc -lxml2
INCLUDEPATH += ../lib/ ../../hwstub/lib

View file

@ -0,0 +1,314 @@
#include "regdisplaypanel.h"
#include <QHeaderView>
#include <QDebug>
/**
* RegItemEditorCreator
*/
QWidget *RegItemEditorCreator::createWidget(QWidget * parent) const
{
return new RegLineEdit(parent);
}
QByteArray RegItemEditorCreator::valuePropertyName () const
{
return QByteArray("text");
}
/**
* DevDisplayPanel
*/
DevDisplayPanel::DevDisplayPanel(QWidget *parent, const SocDevRef& dev_ref)
:QGroupBox(parent), m_dev(dev_ref), m_reg_font(font())
{
QVBoxLayout *right_layout = new QVBoxLayout;
const soc_dev_addr_t& dev_addr = m_dev.GetDevAddr();
m_reg_font.setWeight(100);
m_reg_font.setKerning(false);
QString dev_name;
dev_name.sprintf("HW_%s_BASE", dev_addr.name.c_str());
QLabel *label_names = new QLabel("<b>" + dev_name + "</b>");
label_names->setTextFormat(Qt::RichText);
QLabel *label_addr = new QLabel("<b>" + QString().sprintf("0x%03x", dev_addr.addr) + "</b>");
label_addr->setTextFormat(Qt::RichText);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addStretch();
top_layout->addWidget(label_names);
top_layout->addWidget(label_addr);
top_layout->addStretch();
m_name = new QLabel(this);
m_name->setTextFormat(Qt::RichText);
m_name->setText("<h1>" + QString::fromStdString(m_dev.GetDev().long_name) + "</h1>");
m_desc = new QLabel(this);
m_name->setTextFormat(Qt::RichText);
m_desc->setText(QString::fromStdString(m_dev.GetDev().desc));
right_layout->addLayout(top_layout, 0);
right_layout->addWidget(m_name, 0);
right_layout->addWidget(m_desc, 0);
right_layout->addStretch(1);
setTitle("Device Description");
setLayout(right_layout);
}
void DevDisplayPanel::AllowWrite(bool en)
{
Q_UNUSED(en);
}
QWidget *DevDisplayPanel::GetWidget()
{
return this;
}
/**
* RegDisplayPanel
*/
RegDisplayPanel::RegDisplayPanel(QWidget *parent, IoBackend *io_backend, const SocRegRef& reg_ref)
:QGroupBox(parent), m_io_backend(io_backend), m_reg(reg_ref), m_reg_font(font())
{
bool read_only = m_io_backend->IsReadOnly();
QVBoxLayout *right_layout = new QVBoxLayout;
const soc_dev_addr_t& dev_addr = m_reg.GetDevAddr();
const soc_reg_t& reg = m_reg.GetReg();
const soc_reg_addr_t& reg_addr = m_reg.GetRegAddr();
m_reg_font.setWeight(100);
m_reg_font.setKerning(false);
QString reg_name;
reg_name.sprintf("HW_%s_%s", dev_addr.name.c_str(), reg_addr.name.c_str());
QStringList names;
QVector< soc_addr_t > addresses;
names.append(reg_name);
addresses.append(reg_addr.addr);
if(reg.flags & REG_HAS_SCT)
{
names.append(reg_name + "_SET");
names.append(reg_name + "_CLR");
names.append(reg_name + "_TOG");
addresses.append(reg_addr.addr + 4);
addresses.append(reg_addr.addr + 8);
addresses.append(reg_addr.addr + 12);
}
QString str;
str += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str += "<tr><td><b>" + names[i] + "</b></td></tr>";
str += "</table>";
QLabel *label_names = new QLabel;
label_names->setTextFormat(Qt::RichText);
label_names->setText(str);
QString str_addr;
str_addr += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str_addr += "<tr><td><b>" + QString().sprintf("0x%03x", addresses[i]) + "</b></td></tr>";
str_addr += "</table>";
QLabel *label_addr = new QLabel;
label_addr->setTextFormat(Qt::RichText);
label_addr->setText(str_addr);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addStretch();
top_layout->addWidget(label_names);
top_layout->addWidget(label_addr);
top_layout->addStretch();
m_raw_val_name = new QLabel;
m_raw_val_name->setText("Raw value:");
m_raw_val_edit = new RegLineEdit;
m_raw_val_edit->SetReadOnly(read_only);
m_raw_val_edit->GetLineEdit()->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
m_raw_val_edit->GetLineEdit()->setValidator(new SocFieldValidator(m_raw_val_edit));
m_raw_val_edit->EnableSCT(!!(reg.flags & REG_HAS_SCT));
m_raw_val_edit->GetLineEdit()->setFont(m_reg_font);
connect(m_raw_val_edit->GetLineEdit(), SIGNAL(returnPressed()), this, SLOT(OnRawRegValueReturnPressed()));
QHBoxLayout *raw_val_layout = new QHBoxLayout;
raw_val_layout->addStretch();
raw_val_layout->addWidget(m_raw_val_name);
raw_val_layout->addWidget(m_raw_val_edit);
raw_val_layout->addStretch();
m_value_table = new GrowingTableWidget;
m_value_table->setRowCount(reg.field.size());
m_value_table->setColumnCount(5);
for(size_t row = 0; row < reg.field.size(); row++)
{
const soc_reg_field_t& field = reg.field[row];
QString bits_str;
if(field.first_bit == field.last_bit)
bits_str.sprintf("%d", field.first_bit);
else
bits_str.sprintf("%d:%d", field.last_bit, field.first_bit);
QTableWidgetItem *item = new QTableWidgetItem(bits_str);
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
m_value_table->setItem(row, 0, item);
item = new QTableWidgetItem(QString(field.name.c_str()));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
m_value_table->setItem(row, 1, item);
item = new QTableWidgetItem(QString(field.desc.c_str()));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
m_value_table->setItem(row, 4, item);
}
m_value_table->setHorizontalHeaderItem(0, new QTableWidgetItem("Bits"));
m_value_table->setHorizontalHeaderItem(1, new QTableWidgetItem("Name"));
m_value_table->setHorizontalHeaderItem(2, new QTableWidgetItem("Value"));
m_value_table->setHorizontalHeaderItem(3, new QTableWidgetItem("Meaning"));
m_value_table->setHorizontalHeaderItem(4, new QTableWidgetItem("Description"));
m_value_table->verticalHeader()->setVisible(false);
m_value_table->resizeColumnsToContents();
m_value_table->horizontalHeader()->setStretchLastSection(true);
m_value_table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_table_delegate = new QStyledItemDelegate(this);
m_table_edit_factory = new QItemEditorFactory();
m_regedit_creator = new RegItemEditorCreator();
m_table_edit_factory->registerEditor(QVariant::String, m_regedit_creator);
m_table_delegate->setItemEditorFactory(m_table_edit_factory);
m_value_table->setItemDelegate(m_table_delegate);
m_sexy_display = new RegSexyDisplay(reg_ref, this);
m_sexy_display->setFont(m_reg_font);
m_desc = new QLabel(this);
m_desc->setTextFormat(Qt::RichText);
m_desc->setText(QString::fromStdString(m_reg.GetReg().desc));
right_layout->addWidget(m_desc);
right_layout->addLayout(top_layout);
if(raw_val_layout)
right_layout->addLayout(raw_val_layout);
right_layout->addWidget(m_sexy_display);
right_layout->addWidget(m_value_table);
setTitle("Register Description");
m_viewport = new QWidget;
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_viewport->setLayout(right_layout);
m_scroll = new QScrollArea;
m_scroll->setWidget(m_viewport);
m_scroll->setWidgetResizable(true);
m_scroll->setFrameShape(QFrame::NoFrame);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(m_scroll, 1);
setLayout(layout);
AllowWrite(false);
// load data
Reload();
}
RegDisplayPanel::~RegDisplayPanel()
{
delete m_table_edit_factory;
}
void RegDisplayPanel::Reload()
{
const soc_dev_addr_t& dev_addr = m_reg.GetDevAddr();
const soc_reg_t& reg = m_reg.GetReg();
const soc_reg_addr_t& reg_addr = m_reg.GetRegAddr();
soc_word_t value;
BackendHelper helper(m_io_backend, m_reg);
bool has_value = helper.ReadRegister(dev_addr.name.c_str(), reg_addr.name.c_str(), value);
if(has_value)
{
m_raw_val_name->show();
m_raw_val_edit->show();
m_raw_val_edit->GetLineEdit()->setText(QString().sprintf("0x%08x", value));
}
else
{
m_raw_val_name->hide();
m_raw_val_edit->hide();
}
int row = 0;
foreach(const soc_reg_field_t& field, reg.field)
{
QTableWidgetItem *item = new QTableWidgetItem();
QTableWidgetItem *desc_item = new QTableWidgetItem();
if(has_value)
{
soc_word_t v = (value & field.bitmask()) >> field.first_bit;
QString value_name;
foreach(const soc_reg_field_value_t& rval, field.value)
if(v == rval.value)
value_name = rval.name.c_str();
const char *fmt = "%lu";
// heuristic
if((field.last_bit - field.first_bit + 1) > 16)
fmt = "0x%lx";
item->setText(QString().sprintf(fmt, (unsigned long)v));
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
if(value_name.size() != 0)
{
desc_item->setText(value_name);
desc_item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
}
}
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
desc_item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
m_value_table->setItem(row, 2, item);
m_value_table->setItem(row, 3, desc_item);
row++;
}
}
void RegDisplayPanel::AllowWrite(bool en)
{
m_allow_write = en;
if(m_raw_val_edit)
m_raw_val_edit->SetReadOnly(m_io_backend->IsReadOnly() || !m_allow_write);
}
IoBackend::WriteMode RegDisplayPanel::EditModeToWriteMode(RegLineEdit::EditMode mode)
{
switch(mode)
{
case RegLineEdit::Write: return IoBackend::Write;
case RegLineEdit::Set: return IoBackend::Set;
case RegLineEdit::Clear: return IoBackend::Clear;
case RegLineEdit::Toggle: return IoBackend::Toggle;
default: return IoBackend::Write;
}
}
void RegDisplayPanel::OnRawRegValueReturnPressed()
{
soc_word_t val;
QLineEdit *edit = m_raw_val_edit->GetLineEdit();
const SocFieldValidator *validator = dynamic_cast< const SocFieldValidator *>(edit->validator());
QValidator::State state = validator->parse(edit->text(), val);
if(state != QValidator::Acceptable)
return;
IoBackend::WriteMode mode = EditModeToWriteMode(m_raw_val_edit->GetMode());
BackendHelper helper(m_io_backend, m_reg);
helper.WriteRegister(m_reg.GetDevAddr().name.c_str(), m_reg.GetRegAddr().name.c_str(),
val, mode);
// FIXME: we should notify the UI to read value back because it has changed
Reload();
}
QWidget *RegDisplayPanel::GetWidget()
{
return this;
}

View file

@ -0,0 +1,77 @@
#ifndef REGDISPLAYPANEL_H
#define REGDISPLAYPANEL_H
#include <QVBoxLayout>
#include <QLabel>
#include <QGroupBox>
#include <QTableWidget>
#include <QStyledItemDelegate>
#include <QItemEditorCreatorBase>
#include <QTextEdit>
#include <QScrollArea>
#include <soc_desc.hpp>
#include "backend.h"
#include "settings.h"
#include "aux.h"
#include "regtab.h"
class RegItemEditorCreator : public QItemEditorCreatorBase
{
public:
RegItemEditorCreator() {}
virtual QWidget *createWidget(QWidget * parent) const;
virtual QByteArray valuePropertyName () const;
};
class DevDisplayPanel : public QGroupBox, public RegTabPanel
{
Q_OBJECT
public:
DevDisplayPanel(QWidget *parent, const SocDevRef& reg);
void Reload();
void AllowWrite(bool en);
QWidget *GetWidget();
bool Quit();
protected:
const SocDevRef& m_dev;
QFont m_reg_font;
QLabel *m_name;
QLabel *m_desc;
};
class RegDisplayPanel : public QGroupBox, public RegTabPanel
{
Q_OBJECT
public:
RegDisplayPanel(QWidget *parent, IoBackend *io_backend, const SocRegRef& reg);
~RegDisplayPanel();
void AllowWrite(bool en);
void Reload();
QWidget *GetWidget();
bool Quit();
protected:
IoBackend::WriteMode EditModeToWriteMode(RegLineEdit::EditMode mode);
IoBackend *m_io_backend;
const SocRegRef& m_reg;
bool m_allow_write;
RegLineEdit *m_raw_val_edit;
RegSexyDisplay *m_sexy_display;
GrowingTableWidget *m_value_table;
QStyledItemDelegate *m_table_delegate;
QItemEditorFactory *m_table_edit_factory;
RegItemEditorCreator *m_regedit_creator;
QLabel *m_raw_val_name;
QFont m_reg_font;
QLabel *m_desc;
QWidget *m_viewport;
QScrollArea *m_scroll;
private slots:
void OnRawRegValueReturnPressed();
};
#endif /* REGDISPLAYPANEL_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
#ifndef REGEDIT_H
#define REGEDIT_H
#include <QComboBox>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <QTabWidget>
#include <QSplitter>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QListWidget>
#include <QGroupBox>
#include <QToolButton>
#include <QMenu>
#include <QCheckBox>
#include <QRadioButton>
#include <QButtonGroup>
#include <QDebug>
#include <QScrollArea>
#include "backend.h"
#include "settings.h"
#include "mainwindow.h"
#include "aux.h"
class AbstractRegEditPanel
{
public:
AbstractRegEditPanel() {}
virtual ~AbstractRegEditPanel() {}
virtual void OnModified(bool mod) = 0;
};
class EmptyEditPanel : public QWidget, public AbstractRegEditPanel
{
Q_OBJECT
public:
EmptyEditPanel(QWidget *parent);
signals:
void OnModified(bool mod);
protected:
};
class SocEditPanel : public QWidget, public AbstractRegEditPanel
{
Q_OBJECT
public:
SocEditPanel(SocRef ref, QWidget *parent = 0);
signals:
void OnModified(bool mod);
protected slots:
void OnTextEdited();
void OnNameEdited(const QString& text);
protected:
SocRef m_ref;
QGroupBox *m_name_group;
QLineEdit *m_name_edit;
QGroupBox *m_desc_group;
MyTextEditor *m_desc_edit;
};
class DevEditPanel : public QWidget, public AbstractRegEditPanel
{
Q_OBJECT
public:
DevEditPanel(SocDevRef ref, QWidget *parent = 0);
signals:
void OnModified(bool mod);
protected slots:
void OnInstActivated(int row, int column);
void OnInstChanged(int row, int column);
void OnNameEdited(const QString& text);
void OnLongNameEdited(const QString& text);
void OnVersionEdited(const QString& text);
void OnDescEdited();
protected:
void FillRow(int row, const soc_dev_addr_t& addr);
void CreateNewRow(int row);
enum
{
DevInstDeleteType = QTableWidgetItem::UserType,
DevInstNewType
};
enum
{
DevInstIconColumn = 0,
DevInstNameColumn = 1,
DevInstAddrColumn = 2,
};
SocDevRef m_ref;
QGroupBox *m_name_group;
QLineEdit *m_name_edit;
QGroupBox *m_long_name_group;
QLineEdit *m_long_name_edit;
QGroupBox *m_version_group;
QLineEdit *m_version_edit;
QGroupBox *m_instances_group;
QTableWidget *m_instances_table;
QGroupBox *m_desc_group;
MyTextEditor *m_desc_edit;
};
class RegEditPanel : public QWidget, public AbstractRegEditPanel
{
Q_OBJECT
public:
RegEditPanel(SocRegRef ref, QWidget *parent = 0);
signals:
void OnModified(bool mod);
protected slots:
void OnInstActivated(int row, int column);
void OnInstChanged(int row, int column);
void OnNameEdited(const QString& text);
void OnDescEdited();
void OnSctEdited(int state);
void OnFormulaChanged(int index);
void OnFormulaStringChanged(const QString& text);
void OnFormulaGenerate(bool checked);
protected:
void CreateNewAddrRow(int row);
void FillRow(int row, const soc_reg_addr_t& addr);
void UpdateFormula();
void UpdateWarning(int row);
enum
{
RegInstDeleteType = QTableWidgetItem::UserType,
RegInstNewType
};
enum
{
RegInstIconColumn = 0,
RegInstNameColumn,
RegInstAddrColumn,
RegInstNrColumns,
};
SocRegRef m_ref;
QGroupBox *m_name_group;
QLineEdit *m_name_edit;
QGroupBox *m_instances_group;
QTableWidget *m_instances_table;
QGroupBox *m_desc_group;
QGroupBox *m_flags_group;
QCheckBox *m_sct_check;
QFont m_reg_font;
QGroupBox *m_formula_group;
QButtonGroup *m_formula_radio_group;
QLabel *m_formula_type_label;
QComboBox *m_formula_combo;
QLineEdit *m_formula_string_edit;
QPushButton *m_formula_string_gen;
RegSexyDisplay *m_sexy_display;
MyTextEditor *m_desc_edit;
QGroupBox *m_field_group;
QTableWidget *m_field_table;
};
class FieldEditPanel : public QWidget, public AbstractRegEditPanel
{
Q_OBJECT
public:
FieldEditPanel(SocFieldRef ref, QWidget *parent = 0);
signals:
void OnModified(bool mod);
protected slots:
void OnDescEdited();
void OnNameEdited(const QString& text);
void OnBitRangeEdited(const QString& string);
void OnValueActivated(int row, int column);
void OnValueChanged(int row, int column);
protected:
void CreateNewRow(int row);
void FillRow(int row, const soc_reg_field_value_t& val);
void UpdateWarning(int row);
void UpdateDelegates();
enum
{
FieldValueDeleteType = QTableWidgetItem::UserType,
FieldValueNewType,
};
enum
{
FieldValueIconColumn = 0,
FieldValueNameColumn,
FieldValueValueColumn,
FieldValueDescColumn,
FieldValueNrColumns,
};
SocFieldRef m_ref;
QGroupBox *m_name_group;
QLineEdit *m_name_edit;
QGroupBox *m_bitrange_group;
QLineEdit *m_bitrange_edit;
QGroupBox *m_desc_group;
MyTextEditor *m_desc_edit;
QGroupBox *m_value_group;
QTableWidget *m_value_table;
};
class RegEdit : public QWidget, public DocumentTab
{
Q_OBJECT
public:
RegEdit(Backend *backend, QWidget *parent = 0);
~RegEdit();
virtual bool Quit();
signals:
void OnModified(bool mod);
protected slots:
void OnSocItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void OnSocItemActivated(QTreeWidgetItem *current, int column);
void OnOpen();
void OnSave();
void OnSaveAs();
void OnSocModified(bool modified);
void OnNew();
protected:
void LoadSocFile(const QString& filename);
void UpdateSocFile();
void FillSocTree();
void FillSocTreeItem(QTreeWidgetItem *_item);
void FillDevTreeItem(QTreeWidgetItem *_item);
void FillRegTreeItem(QTreeWidgetItem *_item);
void SetPanel(QWidget *panel);
void DisplaySoc(SocRef ref);
void DisplayDev(SocDevRef ref);
void DisplayReg(SocRegRef ref);
void DisplayField(SocFieldRef ref);
bool CloseSoc();
bool SaveSoc();
bool SaveSocAs();
bool SaveSocFile(const QString& filename);
bool GetFilename(QString& filename, bool save);
void SetModified(bool add, bool mod);
void FixupEmptyItem(QTreeWidgetItem *item);
void MakeItalic(QTreeWidgetItem *item, bool it);
void AddDevice(QTreeWidgetItem *item);
void AddRegister(QTreeWidgetItem *_item);
void UpdateName(QTreeWidgetItem *current);
void AddField(QTreeWidgetItem *_item);
void CreateNewDeviceItem(QTreeWidgetItem *parent);
void CreateNewRegisterItem(QTreeWidgetItem *parent);
void CreateNewFieldItem(QTreeWidgetItem *parent);
QGroupBox *m_file_group;
QToolButton *m_file_open;
QToolButton *m_file_save;
QLineEdit *m_file_edit;
QSplitter *m_splitter;
QTreeWidget *m_soc_tree;
Backend *m_backend;
bool m_modified;
SocFile m_cur_socfile;
QWidget *m_right_panel;
};
#endif /* REGEDIT_H */

View file

@ -1,406 +1,75 @@
#include "regtab.h"
#include <QSplitter>
#include <QVBoxLayout>
#include <QAbstractListModel>
#include <QMessageBox>
#include <QSizePolicy>
#include <QHBoxLayout>
#include <QStringBuilder>
#include <QLabel>
#include <QGridLayout>
#include <QTableWidget>
#include <QHeaderView>
#include <QFileDialog>
#include <QDebug>
#include <QStyle>
#include "backend.h"
#include "analyser.h"
#include "regdisplaypanel.h"
/**
* SocFieldValidator
*/
SocFieldValidator::SocFieldValidator(QObject *parent)
:QValidator(parent)
namespace
{
m_field.first_bit = 0;
m_field.last_bit = 31;
}
SocFieldValidator::SocFieldValidator(const soc_reg_field_t& field, QObject *parent)
:QValidator(parent), m_field(field)
enum
{
}
RegTreeDevType = QTreeWidgetItem::UserType,
RegTreeRegType
};
void SocFieldValidator::fixup(QString& input) const
class DevTreeItem : public QTreeWidgetItem
{
input = input.trimmed();
}
public:
DevTreeItem(const QString& string, const SocDevRef& ref)
:QTreeWidgetItem(QStringList(string), RegTreeDevType), m_ref(ref) {}
QValidator::State SocFieldValidator::validate(QString& input, int& pos) const
const SocDevRef& GetRef() { return m_ref; }
private:
SocDevRef m_ref;
};
class RegTreeItem : public QTreeWidgetItem
{
(void) pos;
soc_word_t val;
State state = parse(input, val);
return state;
}
public:
RegTreeItem(const QString& string, const SocRegRef& ref)
:QTreeWidgetItem(QStringList(string), RegTreeRegType), m_ref(ref) {}
QValidator::State SocFieldValidator::parse(const QString& input, soc_word_t& val) const
{
// the empty string is all alwats intermediate
if(input.size() == 0)
return Intermediate;
// first check named values
State state = Invalid;
foreach(const soc_reg_field_value_t& value, m_field.value)
{
QString name = QString::fromLocal8Bit(value.name.c_str());
// cannot be a substring if too long or empty
if(input.size() > name.size())
continue;
// check equal string
if(input == name)
{
state = Acceptable;
val = value.value;
break;
}
// check substring
if(name.startsWith(input))
state = Intermediate;
}
// early return for exact match
if(state == Acceptable)
return state;
// do a few special cases for convenience
if(input.compare("0x", Qt::CaseInsensitive) == 0 ||
input.compare("0b", Qt::CaseInsensitive) == 0)
return Intermediate;
// try by parsing
unsigned basis, pos;
if(input.size() >= 2 && input.startsWith("0x", Qt::CaseInsensitive))
{
basis = 16;
pos = 2;
}
else if(input.size() >= 2 && input.startsWith("0b", Qt::CaseInsensitive))
{
basis = 2;
pos = 2;
}
else if(input.size() >= 2 && input.startsWith("0"))
{
basis = 8;
pos = 1;
}
else
{
basis = 10;
pos = 0;
}
bool ok = false;
unsigned long v = input.mid(pos).toULong(&ok, basis);
// if not ok, return result of name parsing
if(!ok)
return state;
// if ok, check if it fits in the number of bits
unsigned nr_bits = m_field.last_bit - m_field.first_bit + 1;
unsigned long max = nr_bits == 32 ? 0xffffffff : (1 << nr_bits) - 1;
if(v <= max)
{
val = v;
return Acceptable;
}
const SocRegRef& GetRef() { return m_ref; }
private:
SocRegRef m_ref;
};
return state;
}
/**
* RegLineEdit
* EmptyRegTabPanel
*/
RegLineEdit::RegLineEdit(QWidget *parent)
EmptyRegTabPanel::EmptyRegTabPanel(QWidget *parent)
:QWidget(parent)
{
m_layout = new QHBoxLayout(this);
m_button = new QToolButton(this);
m_button->setCursor(Qt::ArrowCursor);
m_button->setStyleSheet("QToolButton { font-weight: bold; color: white; background: black; }");
m_button->setPopupMode(QToolButton::InstantPopup);
m_edit = new QLineEdit(this);
m_layout->addWidget(m_button);
m_layout->addWidget(m_edit);
m_menu = new QMenu(this);
connect(m_menu->addAction("Write"), SIGNAL(triggered()), this, SLOT(OnWriteAct()));
connect(m_menu->addAction("Set"), SIGNAL(triggered()), this, SLOT(OnSetAct()));
connect(m_menu->addAction("Clear"), SIGNAL(triggered()), this, SLOT(OnClearAct()));
connect(m_menu->addAction("Toggle"), SIGNAL(triggered()), this, SLOT(OnToggleAct()));
EnableSCT(false);
SetReadOnly(false);
ShowMode(true);
SetMode(Write);
QVBoxLayout *l = new QVBoxLayout;
l->addStretch();
setLayout(l);
}
void RegLineEdit::SetReadOnly(bool ro)
void EmptyRegTabPanel::AllowWrite(bool en)
{
m_edit->setReadOnly(ro);
m_readonly = ro;
ShowMode(!ro);
Q_UNUSED(en);
}
void RegLineEdit::EnableSCT(bool en)
QWidget *EmptyRegTabPanel::GetWidget()
{
m_has_sct = en;
if(!m_has_sct)
{
m_button->setMenu(0);
SetMode(Write);
}
else
m_button->setMenu(m_menu);
return this;
}
RegLineEdit::~RegLineEdit()
{
}
QLineEdit *RegLineEdit::GetLineEdit()
{
return m_edit;
}
void RegLineEdit::ShowMode(bool show)
{
if(show)
m_button->show();
else
m_button->hide();
}
void RegLineEdit::OnWriteAct()
{
SetMode(Write);
}
void RegLineEdit::OnSetAct()
{
SetMode(Set);
}
void RegLineEdit::OnClearAct()
{
SetMode(Clear);
}
void RegLineEdit::OnToggleAct()
{
SetMode(Toggle);
}
void RegLineEdit::SetMode(EditMode mode)
{
m_mode = mode;
switch(m_mode)
{
case Write: m_button->setText("WR"); break;
case Set: m_button->setText("SET"); break;
case Clear: m_button->setText("CLR"); break;
case Toggle: m_button->setText("TOG"); break;
default: break;
}
}
RegLineEdit::EditMode RegLineEdit::GetMode()
{
return m_mode;
}
/**
* RegDisplayPanel
*/
RegDisplayPanel::RegDisplayPanel(QWidget *parent, IoBackend *io_backend, const SocRegRef& reg_ref)
:QGroupBox(parent), m_io_backend(io_backend), m_reg(reg_ref)
{
bool read_only = m_io_backend->IsReadOnly();
QVBoxLayout *right_layout = new QVBoxLayout;
const soc_dev_addr_t& dev_addr = m_reg.GetDevAddr();
const soc_reg_t& reg = m_reg.GetReg();
const soc_reg_addr_t& reg_addr = m_reg.GetRegAddr();
QString reg_name;
reg_name.sprintf("HW_%s_%s", dev_addr.name.c_str(), reg_addr.name.c_str());
QStringList names;
QVector< soc_addr_t > addresses;
names.append(reg_name);
addresses.append(reg_addr.addr);
if(reg.flags & REG_HAS_SCT)
{
names.append(reg_name + "_SET");
names.append(reg_name + "_CLR");
names.append(reg_name + "_TOG");
addresses.append(reg_addr.addr + 4);
addresses.append(reg_addr.addr + 8);
addresses.append(reg_addr.addr + 12);
}
QString str;
str += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str += "<tr><td><b>" + names[i] + "</b></td></tr>";
str += "</table>";
QLabel *label_names = new QLabel;
label_names->setTextFormat(Qt::RichText);
label_names->setText(str);
QString str_addr;
str_addr += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str_addr += "<tr><td><b>" + QString().sprintf("0x%03x", addresses[i]) + "</b></td></tr>";
str_addr += "</table>";
QLabel *label_addr = new QLabel;
label_addr->setTextFormat(Qt::RichText);
label_addr->setText(str_addr);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addStretch();
top_layout->addWidget(label_names);
top_layout->addWidget(label_addr);
top_layout->addStretch();
soc_word_t value;
BackendHelper helper(m_io_backend, m_reg);
bool has_value = helper.ReadRegister(dev_addr.name.c_str(), reg_addr.name.c_str(), value);
QHBoxLayout *raw_val_layout = 0;
if(has_value)
{
QLabel *raw_val_name = new QLabel;
raw_val_name->setText("Raw value:");
m_raw_val_edit = new RegLineEdit;
m_raw_val_edit->SetReadOnly(read_only);
m_raw_val_edit->GetLineEdit()->setText(QString().sprintf("0x%08x", value));
m_raw_val_edit->GetLineEdit()->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
m_raw_val_edit->GetLineEdit()->setValidator(new SocFieldValidator(m_raw_val_edit));
m_raw_val_edit->EnableSCT(!!(reg.flags & REG_HAS_SCT));
connect(m_raw_val_edit->GetLineEdit(), SIGNAL(returnPressed()), this, SLOT(OnRawRegValueReturnPressed()));
raw_val_layout = new QHBoxLayout;
raw_val_layout->addStretch();
raw_val_layout->addWidget(raw_val_name);
raw_val_layout->addWidget(m_raw_val_edit);
raw_val_layout->addStretch();
}
else
m_raw_val_edit = 0;
QTableWidget *value_table = new QTableWidget;
value_table->setRowCount(reg.field.size());
value_table->setColumnCount(4);
int row = 0;
foreach(const soc_reg_field_t& field, reg.field)
{
QString bits_str;
if(field.first_bit == field.last_bit)
bits_str.sprintf("%d", field.first_bit);
else
bits_str.sprintf("%d:%d", field.last_bit, field.first_bit);
QTableWidgetItem *item = new QTableWidgetItem(bits_str);
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(row, 0, item);
item = new QTableWidgetItem(QString(field.name.c_str()));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(row, 1, item);
item = new QTableWidgetItem();
if(has_value)
{
soc_word_t v = (value & field.bitmask()) >> field.first_bit;
QString value_name;
foreach(const soc_reg_field_value_t& rval, field.value)
if(v == rval.value)
value_name = rval.name.c_str();
const char *fmt = "%lu";
// heuristic
if((field.last_bit - field.first_bit + 1) > 16)
fmt = "0x%lx";
item->setText(QString().sprintf(fmt, (unsigned long)v));
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
if(value_name.size() != 0)
{
QTableWidgetItem *t = new QTableWidgetItem(value_name);
t->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
t->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(row, 3, t);
}
}
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(row, 2, item);
row++;
}
value_table->setHorizontalHeaderItem(0, new QTableWidgetItem("Bits"));
value_table->setHorizontalHeaderItem(1, new QTableWidgetItem("Name"));
value_table->setHorizontalHeaderItem(2, new QTableWidgetItem("Value"));
value_table->setHorizontalHeaderItem(3, new QTableWidgetItem("Meaning"));
value_table->verticalHeader()->setVisible(false);
value_table->resizeColumnsToContents();
value_table->horizontalHeader()->setStretchLastSection(true);
value_table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
right_layout->addLayout(top_layout);
if(raw_val_layout)
right_layout->addLayout(raw_val_layout);
//right_layout->addWidget(bits_label);
right_layout->addWidget(value_table);
//right_layout->addStretch();
setTitle("Register Description");
setLayout(right_layout);
AllowWrite(false);
}
void RegDisplayPanel::AllowWrite(bool en)
{
m_allow_write = en;
if(m_raw_val_edit)
m_raw_val_edit->SetReadOnly(m_io_backend->IsReadOnly() || !m_allow_write);
}
IoBackend::WriteMode RegDisplayPanel::EditModeToWriteMode(RegLineEdit::EditMode mode)
{
switch(mode)
{
case RegLineEdit::Write: return IoBackend::Write;
case RegLineEdit::Set: return IoBackend::Set;
case RegLineEdit::Clear: return IoBackend::Clear;
case RegLineEdit::Toggle: return IoBackend::Toggle;
default: return IoBackend::Write;
}
}
void RegDisplayPanel::OnRawRegValueReturnPressed()
{
soc_word_t val;
QLineEdit *edit = m_raw_val_edit->GetLineEdit();
const SocFieldValidator *validator = dynamic_cast< const SocFieldValidator *>(edit->validator());
QValidator::State state = validator->parse(edit->text(), val);
if(state != QValidator::Acceptable)
return;
IoBackend::WriteMode mode = EditModeToWriteMode(m_raw_val_edit->GetMode());
BackendHelper helper(m_io_backend, m_reg);
helper.WriteRegister(m_reg.GetDevAddr().name.c_str(), m_reg.GetRegAddr().name.c_str(),
val, mode);
// FIXME: we should notify the UI to read value back because it has changed
}
/**
* RegTab
*/
RegTab::RegTab(Backend *backend)
:m_backend(backend)
RegTab::RegTab(Backend *backend, QWidget *parent)
:QSplitter(parent), m_backend(backend)
{
QWidget *left = new QWidget;
this->addWidget(left);
@ -432,7 +101,7 @@ RegTab::RegTab(Backend *backend)
QGroupBox *data_sel_group = new QGroupBox("Data selection");
QHBoxLayout *data_sel_layout = new QHBoxLayout;
m_data_selector = new QComboBox;
m_data_selector->addItem(QIcon::fromTheme("face-sad"), "None", QVariant(DataSelNothing));
m_data_selector->addItem(QIcon::fromTheme("text-x-generic"), "Explore", QVariant(DataSelNothing));
m_data_selector->addItem(QIcon::fromTheme("document-open"), "File...", QVariant(DataSelFile));
#ifdef HAVE_HWSTUB
m_data_selector->addItem(QIcon::fromTheme("multimedia-player"), "Device...", QVariant(DataSelDevice));
@ -446,7 +115,8 @@ RegTab::RegTab(Backend *backend)
data_sel_reload->setIcon(QIcon::fromTheme("view-refresh"));
data_sel_reload->setToolTip("Reload data");
data_sel_layout->addWidget(m_data_selector);
data_sel_layout->addWidget(m_data_sel_edit);
data_sel_layout->addWidget(m_data_sel_edit, 1);
data_sel_layout->addStretch(0);
#ifdef HAVE_HWSTUB
m_dev_selector = new QComboBox;
data_sel_layout->addWidget(m_dev_selector, 1);
@ -457,12 +127,9 @@ RegTab::RegTab(Backend *backend)
data_sel_group->setLayout(data_sel_layout);
m_data_soc_label->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
m_right_panel->addWidget(data_sel_group);
m_right_content = new QWidget;
QVBoxLayout *l = new QVBoxLayout;
l->addStretch();
m_right_content->setLayout(l);
m_right_panel->addWidget(m_right_content);
m_right_panel->addWidget(data_sel_group, 0);
m_right_content = 0;
SetPanel(new EmptyRegTabPanel);
QWidget *w = new QWidget;
w->setLayout(m_right_panel);
this->addWidget(w);
@ -470,21 +137,17 @@ RegTab::RegTab(Backend *backend)
m_io_backend = m_backend->CreateDummyIoBackend();
connect(m_soc_selector, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(OnSocChanged(const QString&)));
connect(m_soc_selector, SIGNAL(currentIndexChanged(int)),
this, SLOT(OnSocChanged(int)));
connect(m_backend, SIGNAL(OnSocListChanged()), this, SLOT(OnSocListChanged()));
connect(m_reg_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
this, SLOT(OnRegItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
connect(m_reg_tree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
SLOT(OnRegItemClicked(QTreeWidgetItem *, int)));
connect(m_data_selector, SIGNAL(activated(int)),
this, SLOT(OnDataSelChanged(int)));
connect(m_data_soc_label, SIGNAL(linkActivated(const QString&)), this,
SLOT(OnDataSocActivated(const QString&)));
connect(m_analysers_list, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
this, SLOT(OnAnalyserChanged(QListWidgetItem *, QListWidgetItem *)));
connect(m_analysers_list, SIGNAL(itemClicked(QListWidgetItem *)), this,
SLOT(OnAnalyserClicked(QListWidgetItem *)));
#ifdef HAVE_HWSTUB
connect(m_dev_selector, SIGNAL(currentIndexChanged(int)),
this, SLOT(OnDevChanged(int)));
@ -492,14 +155,22 @@ RegTab::RegTab(Backend *backend)
connect(m_readonly_check, SIGNAL(clicked(bool)), this, SLOT(OnReadOnlyClicked(bool)));
OnSocListChanged();
OnDataSelChanged(DataSelNothing);
OnDataSelChanged(0);
}
RegTab::~RegTab()
{
#ifdef HAVE_HWSTUB
ClearDevList();
#endif
delete m_io_backend;
}
bool RegTab::Quit()
{
return true;
}
void RegTab::SetDataSocName(const QString& socname)
{
if(socname.size() != 0)
@ -533,9 +204,10 @@ void RegTab::OnDataSelChanged(int index)
#ifdef HAVE_HWSTUB
m_dev_selector->hide();
#endif
m_readonly_check->show();
QFileDialog *fd = new QFileDialog(m_data_selector);
fd->setFilter("Textual files (*.txt);;All files (*)");
fd->setDirectory(Settings::Get()->value("regtab/loaddatadir", QDir::currentPath()).toString());
fd->setDirectory(Settings::Get()->value("loaddatadir", QDir::currentPath()).toString());
if(fd->exec())
{
QStringList filenames = fd->selectedFiles();
@ -545,13 +217,14 @@ void RegTab::OnDataSelChanged(int index)
SetDataSocName(m_io_backend->GetSocName());
OnDataSocActivated(m_io_backend->GetSocName());
}
Settings::Get()->setValue("regtab/loaddatadir", fd->directory().absolutePath());
Settings::Get()->setValue("loaddatadir", fd->directory().absolutePath());
SetReadOnlyIndicator();
}
#ifdef HAVE_HWSTUB
else if(var == DataSelDevice)
{
m_data_sel_edit->hide();
m_readonly_check->show();
m_dev_selector->show();
OnDevListChanged();
}
@ -562,13 +235,31 @@ void RegTab::OnDataSelChanged(int index)
#ifdef HAVE_HWSTUB
m_dev_selector->hide();
#endif
m_readonly_check->hide();
delete m_io_backend;
m_io_backend = m_backend->CreateDummyIoBackend();
m_readonly_check->setCheckState(Qt::Checked);
SetDataSocName("");
UpdateSocFilename();
}
OnDataChanged();
}
void RegTab::UpdateSocFilename()
{
int index = m_data_selector->currentIndex();
if(index == -1)
return;
if(m_data_selector->itemData(index) != DataSelNothing)
return;
index = m_soc_selector->currentIndex();
if(index == -1)
return;
SocRef ref = m_soc_selector->itemData(index).value< SocRef >();
m_data_sel_edit->setText(ref.GetSocFile()->GetFilename());
}
void RegTab::SetReadOnlyIndicator()
{
if(m_io_backend->IsReadOnly())
@ -582,23 +273,30 @@ void RegTab::OnDataChanged()
void RegTab::OnRegItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
(void) previous;
Q_UNUSED(previous);
OnRegItemClicked(current, 0);
}
void RegTab::OnRegItemClicked(QTreeWidgetItem *current, int col)
{
(void) col;
if(current == 0 || current->type() != RegTreeRegType)
Q_UNUSED(col);
if(current == 0)
return;
if(current->type() == RegTreeRegType)
{
RegTreeItem *item = dynamic_cast< RegTreeItem * >(current);
DisplayRegister(item->GetRef());
}
else if(current->type() == RegTreeDevType)
{
DevTreeItem *item = dynamic_cast< DevTreeItem * >(current);
DisplayDevice(item->GetRef());
}
}
void RegTab::OnAnalyserChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
(void) previous;
Q_UNUSED(previous);
OnAnalyserClicked(current);
}
@ -606,33 +304,44 @@ void RegTab::OnAnalyserClicked(QListWidgetItem *current)
{
if(current == 0)
return;
delete m_right_content;
AnalyserFactory *ana = AnalyserFactory::GetAnalyserByName(current->text());
m_right_content = ana->Create(m_cur_soc, m_io_backend)->GetWidget();
m_right_panel->addWidget(m_right_content, 1);
SetPanel(ana->Create(m_cur_soc, m_io_backend));
}
void RegTab::DisplayRegister(const SocRegRef& ref)
{
SetPanel(new RegDisplayPanel(this, m_io_backend, ref));
}
void RegTab::DisplayDevice(const SocDevRef& ref)
{
SetPanel(new DevDisplayPanel(this, ref));
}
void RegTab::SetPanel(RegTabPanel *panel)
{
delete m_right_content;
RegDisplayPanel *panel = new RegDisplayPanel(this, m_io_backend, ref);
panel->AllowWrite(m_readonly_check->checkState() == Qt::Unchecked);
m_right_content = panel;
m_right_panel->addWidget(m_right_content);
m_right_content->AllowWrite(m_readonly_check->checkState() == Qt::Unchecked);
m_right_panel->addWidget(m_right_content->GetWidget(), 1);
}
void RegTab::OnSocListChanged()
{
m_soc_selector->clear();
QStringList socs = m_backend->GetSocNameList();
QList< SocRef > socs = m_backend->GetSocList();
for(int i = 0; i < socs.size(); i++)
m_soc_selector->addItem(socs[i]);
{
QVariant v;
v.setValue(socs[i]);
m_soc_selector->addItem(QString::fromStdString(socs[i].GetSoc().name), v);
}
}
#ifdef HAVE_HWSTUB
void RegTab::OnDevListChanged()
{
m_dev_selector->clear();
ClearDevList();
QList< HWStubDevice* > list = m_hwstub_helper.GetDevList();
foreach(HWStubDevice *dev, list)
{
@ -659,10 +368,21 @@ void RegTab::OnDevChanged(int index)
OnDataSocActivated(m_io_backend->GetSocName());
OnDataChanged();
}
void RegTab::ClearDevList()
{
while(m_dev_selector->count() > 0)
{
HWStubDevice *dev = reinterpret_cast< HWStubDevice* >(m_dev_selector->itemData(0).value< void* >());
delete dev;
m_dev_selector->removeItem(0);
}
}
#endif
void RegTab::FillDevSubTree(DevTreeItem *item)
void RegTab::FillDevSubTree(QTreeWidgetItem *_item)
{
DevTreeItem *item = dynamic_cast< DevTreeItem* >(_item);
const soc_dev_t& dev = item->GetRef().GetDev();
for(size_t i = 0; i < dev.reg.size(); i++)
{
@ -697,23 +417,21 @@ void RegTab::FillAnalyserList()
m_analysers_list->addItems(AnalyserFactory::GetAnalysersForSoc(m_cur_soc.GetSoc().name.c_str()));
}
void RegTab::OnSocChanged(const QString& soc)
void RegTab::OnSocChanged(int index)
{
m_reg_tree->clear();
if(!m_backend->GetSocByName(soc, m_cur_soc))
if(index == -1)
return;
m_reg_tree->clear();
m_cur_soc = m_soc_selector->itemData(index).value< SocRef >();
FillRegTree();
FillAnalyserList();
UpdateSocFilename();
}
void RegTab::OnReadOnlyClicked(bool checked)
{
if(m_io_backend->IsReadOnly())
return SetReadOnlyIndicator();
if(m_right_content == 0)
return;
RegDisplayPanel *panel = dynamic_cast< RegDisplayPanel* >(m_right_content);
if(panel == 0)
return;
panel->AllowWrite(!checked);
m_right_content->AllowWrite(!checked);
UpdateSocFilename();
}

View file

@ -2,7 +2,6 @@
#define REGTAB_H
#include <QComboBox>
#include <QEvent>
#include <QTreeWidget>
#include <QVBoxLayout>
#include <QTabWidget>
@ -11,118 +10,41 @@
#include <QPushButton>
#include <QLabel>
#include <QListWidget>
#include <QValidator>
#include <QGroupBox>
#include <QToolButton>
#include <QMenu>
#include <QCheckBox>
#include <soc_desc.hpp>
#include "backend.h"
#include "settings.h"
#include "mainwindow.h"
enum
{
RegTreeDevType = QTreeWidgetItem::UserType,
RegTreeRegType
};
class DevTreeItem : public QTreeWidgetItem
class RegTabPanel
{
public:
DevTreeItem(const QString& string, const SocDevRef& ref)
:QTreeWidgetItem(QStringList(string), RegTreeDevType), m_ref(ref) {}
const SocDevRef& GetRef() { return m_ref; }
private:
SocDevRef m_ref;
RegTabPanel() {}
virtual ~RegTabPanel() {}
virtual void AllowWrite(bool en) = 0;
virtual QWidget *GetWidget() = 0;
};
class RegTreeItem : public QTreeWidgetItem
class EmptyRegTabPanel : public QWidget, public RegTabPanel
{
public:
RegTreeItem(const QString& string, const SocRegRef& ref)
:QTreeWidgetItem(QStringList(string), RegTreeRegType), m_ref(ref) {}
const SocRegRef& GetRef() { return m_ref; }
private:
SocRegRef m_ref;
};
class SocFieldValidator : public QValidator
{
Q_OBJECT
public:
SocFieldValidator(QObject *parent = 0);
SocFieldValidator(const soc_reg_field_t& field, QObject *parent = 0);
virtual void fixup(QString& input) const;
virtual State validate(QString& input, int& pos) const;
/* validate and return the interpreted value */
State parse(const QString& input, soc_word_t& val) const;
protected:
soc_reg_field_t m_field;
};
class RegLineEdit : public QWidget
{
Q_OBJECT
public:
enum EditMode
{
Write, Set, Clear, Toggle
};
RegLineEdit(QWidget *parent = 0);
~RegLineEdit();
void SetReadOnly(bool ro);
void EnableSCT(bool en);
void SetMode(EditMode mode);
EditMode GetMode();
QLineEdit *GetLineEdit();
protected slots:
void OnWriteAct();
void OnSetAct();
void OnClearAct();
void OnToggleAct();
protected:
void ShowMode(bool show);
QHBoxLayout *m_layout;
QToolButton *m_button;
QLineEdit *m_edit;
EditMode m_mode;
bool m_has_sct;
bool m_readonly;
QMenu *m_menu;
};
class RegDisplayPanel : public QGroupBox
{
Q_OBJECT
public:
RegDisplayPanel(QWidget *parent, IoBackend *io_backend, const SocRegRef& reg);
EmptyRegTabPanel(QWidget *parent = 0);
void AllowWrite(bool en);
protected:
IoBackend::WriteMode EditModeToWriteMode(RegLineEdit::EditMode mode);
IoBackend *m_io_backend;
const SocRegRef& m_reg;
bool m_allow_write;
RegLineEdit *m_raw_val_edit;
private slots:
void OnRawRegValueReturnPressed();
QWidget *GetWidget();
};
class RegTab : public QSplitter
class RegTab : public QSplitter, public DocumentTab
{
Q_OBJECT
public:
RegTab(Backend *backend);
RegTab(Backend *backend, QWidget *parent = 0);
~RegTab();
virtual bool Quit();
signals:
void OnModified(bool modified);
protected:
enum
@ -134,12 +56,16 @@ protected:
#endif
};
void FillDevSubTree(DevTreeItem *item);
void FillDevSubTree(QTreeWidgetItem *item);
void FillRegTree();
void FillAnalyserList();
void UpdateSocList();
void DisplayRegister(const SocRegRef& ref);
void DisplayDevice(const SocDevRef& ref);
void SetDataSocName(const QString& socname);
void SetPanel(RegTabPanel *panel);
void UpdateSocFilename();
QComboBox *m_soc_selector;
#ifdef HAVE_HWSTUB
QComboBox *m_dev_selector;
@ -149,7 +75,7 @@ protected:
QTreeWidget *m_reg_tree;
SocRef m_cur_soc;
QVBoxLayout *m_right_panel;
QWidget *m_right_content;
RegTabPanel *m_right_content;
QLineEdit *m_data_sel_edit;
QCheckBox *m_readonly_check;
QLabel *m_data_soc_label;
@ -163,9 +89,10 @@ private slots:
#ifdef HAVE_HWSTUB
void OnDevListChanged();
void OnDevChanged(int index);
void ClearDevList();
#endif
void SetReadOnlyIndicator();
void OnSocChanged(const QString& text);
void OnSocChanged(int index);
void OnSocListChanged();
void OnRegItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void OnRegItemClicked(QTreeWidgetItem *clicked, int col);

View file

@ -23,6 +23,7 @@ ClockAnalyser::ClockAnalyser(const SocRef& soc, IoBackend *backend)
ClockAnalyser::~ClockAnalyser()
{
delete m_group;
}
QWidget *ClockAnalyser::GetWidget()
@ -287,9 +288,9 @@ void ClockAnalyser::FillTree()
else
AddClock(ref_xtal, "clk_rtc32k", INVALID);
(void) clk_x;
(void) clk_gpmi;
(void) clk_h;
Q_UNUSED(clk_x);
Q_UNUSED(clk_gpmi);
Q_UNUSED(clk_h);
m_tree_widget->expandAll();
m_tree_widget->resizeColumnToContents(0);
@ -334,6 +335,7 @@ EmiAnalyser::EmiAnalyser(const SocRef& soc, IoBackend *backend)
EmiAnalyser::~EmiAnalyser()
{
delete m_group;
}
QWidget *EmiAnalyser::GetWidget()
@ -671,6 +673,7 @@ PinAnalyser::PinAnalyser(const SocRef& soc, IoBackend *backend)
PinAnalyser::~PinAnalyser()
{
delete m_group;
}
QWidget *PinAnalyser::GetWidget()

View file

@ -21,7 +21,6 @@
class ClockAnalyser : public Analyser
{
Q_OBJECT
public:
ClockAnalyser(const SocRef& soc, IoBackend *backend);
virtual ~ClockAnalyser();
@ -50,7 +49,7 @@ private:
/**
* EMI analyser
*/
class EmiAnalyser : public Analyser
class EmiAnalyser : public QObject, public Analyser
{
Q_OBJECT
public:
@ -96,7 +95,6 @@ private:
*/
class PinAnalyser : public Analyser
{
Q_OBJECT
public:
PinAnalyser(const SocRef& soc, IoBackend *backend);
virtual ~PinAnalyser();

View file

@ -21,77 +21,343 @@
#include "soc_desc.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <map>
#include <cstring>
void print_value_desc(const soc_reg_field_value_t& value)
template< typename T >
bool build_map(const char *type, const std::vector< T >& vec,
std::map< std::string, size_t >& map)
{
printf(" VALUE %s (%#x)\n", value.name.c_str(), value.value);
for(size_t i = 0; i < vec.size(); i++)
{
if(map.find(vec[i].name) != map.end())
{
printf("soc has duplicate %s '%s'\n", type, vec[i].name.c_str());
return false;
}
map[vec[i].name] = i;
}
return true;
}
void print_field_desc(const soc_reg_field_t& field)
template< typename T >
bool build_map(const char *type, const std::vector< T >& a, const std::vector< T >& b,
std::vector< std::pair< size_t, size_t > >& m)
{
printf(" FIELD %s (%d:%d)\n", field.name.c_str(), field.last_bit,
field.first_bit);
for(size_t i = 0; i < field.value.size(); i++)
print_value_desc(field.value[i]);
std::map< std::string, size_t > ma, mb;
if(!build_map(type, a, ma) || !build_map(type, b, mb))
return false;
std::map< std::string, size_t >::iterator it;
for(it = ma.begin(); it != ma.end(); ++it)
{
if(mb.find(it->first) == mb.end())
{
printf("%s '%s' exists in only one file\n", type, it->first.c_str());
return false;
}
m.push_back(std::make_pair(it->second, mb[it->first]));
}
for(it = mb.begin(); it != mb.end(); ++it)
{
if(ma.find(it->first) == ma.end())
{
printf("%s '%s' exists in only one file\n", type, it->first.c_str());
return false;
}
}
return true;
}
std::string compute_sct(soc_reg_flags_t f)
bool compare_value(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg,
const soc_reg_field_t& field, const soc_reg_field_value_t& a, const soc_reg_field_value_t& b)
{
if(f & REG_HAS_SCT) return "SCT";
else return "";
if(a.value != b.value)
{
printf("register field value '%s.%s.%s.%s.%s' have different values\n", soc.name.c_str(),
dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str());
return false;
}
if(a.desc != b.desc)
{
printf("register field value '%s.%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(),
dev.name.c_str(), reg.name.c_str(), field.name.c_str(), a.name.c_str());
return false;
}
return true;
}
void print_reg_addr_desc(const soc_reg_addr_t& reg)
bool compare_field(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg,
const soc_reg_field_t& a, const soc_reg_field_t& b)
{
printf(" ADDR %s %#x\n", reg.name.c_str(), reg.addr);
if(a.first_bit != b.first_bit || a.last_bit != b.last_bit)
{
printf("register address '%s.%s.%s.%s' have different bit ranges\n", soc.name.c_str(),
dev.name.c_str(), reg.name.c_str(), a.name.c_str());
return false;
}
if(a.desc != b.desc)
{
printf("register address '%s.%s.%s.%s' have different descriptions\n", soc.name.c_str(),
dev.name.c_str(), reg.name.c_str(), a.name.c_str());
return false;
}
/* values */
std::vector< std::pair< size_t, size_t > > map;
if(!build_map("field value", a.value, b.value, map))
return false;
for(size_t i = 0; i < map.size(); i++)
if(!compare_value(soc, dev, reg, a, a.value[map[i].first], b.value[map[i].second]))
return false;
return true;
}
void print_reg_desc(const soc_reg_t& reg)
bool compare_reg_addr(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& reg,
const soc_reg_addr_t& a, const soc_reg_addr_t& b)
{
std::string sct = compute_sct(reg.flags);
printf(" REG %s %s\n", reg.name.c_str(), sct.c_str());
for(size_t i = 0; i < reg.addr.size(); i++)
print_reg_addr_desc(reg.addr[i]);
for(size_t i = 0; i < reg.field.size(); i++)
print_field_desc(reg.field[i]);
if(a.addr != b.addr)
{
printf("register address '%s.%s.%s.%s' have different values\n", soc.name.c_str(),
dev.name.c_str(), reg.name.c_str(), a.name.c_str());
return false;
}
else
return true;
}
void print_dev_addr_desc(const soc_dev_addr_t& dev)
bool compare_reg(const soc_t& soc, const soc_dev_t& dev, const soc_reg_t& a,
const soc_reg_t& b)
{
printf(" ADDR %s %#x\n", dev.name.c_str(), dev.addr);
if(a.desc != b.desc)
{
printf("register '%s.%s.%s' have different descriptions\n", soc.name.c_str(),
dev.name.c_str(), a.name.c_str());
return false;
}
if(a.flags != b.flags)
{
printf("device '%s.%s.%s' have different flags\n", soc.name.c_str(),
dev.name.c_str(), a.name.c_str());
return false;
}
if(a.formula.type != b.formula.type)
{
printf("device '%s.%s.%s' have different formula types\n", soc.name.c_str(),
dev.name.c_str(), a.name.c_str());
return false;
}
if(a.formula.string != b.formula.string)
{
printf("device '%s.%s.%s' have different formula string\n", soc.name.c_str(),
dev.name.c_str(), a.name.c_str());
return false;
}
/* addresses */
std::vector< std::pair< size_t, size_t > > map;
if(!build_map("register address", a.addr, b.addr, map))
return false;
for(size_t i = 0; i < map.size(); i++)
if(!compare_reg_addr(soc, dev, a, a.addr[map[i].first], b.addr[map[i].second]))
return false;
/* field */
map.clear();
if(!build_map("field", a.field, b.field, map))
return false;
for(size_t i = 0; i < map.size(); i++)
if(!compare_field(soc, dev, a, a.field[map[i].first], b.field[map[i].second]))
return false;
return true;
}
void print_dev_desc(const soc_dev_t& dev)
bool compare_dev_addr(const soc_t& soc, const soc_dev_t& dev, const soc_dev_addr_t& a,
const soc_dev_addr_t& b)
{
printf(" DEV %s\n", dev.name.c_str());
for(size_t i = 0; i < dev.addr.size(); i++)
print_dev_addr_desc(dev.addr[i]);
for(size_t i = 0; i < dev.reg.size(); i++)
print_reg_desc(dev.reg[i]);
if(a.addr != b.addr)
{
printf("device address '%s.%s.%s' have different values\n", soc.name.c_str(),
dev.name.c_str(), a.name.c_str());
return false;
}
else
return true;
}
void print_soc_desc(const soc_t& soc)
bool compare_dev(const soc_t& soc, const soc_dev_t& a, const soc_dev_t& b)
{
printf("SOC %s (%s)\n", soc.name.c_str(), soc.desc.c_str());
for(size_t i = 0; i < soc.dev.size(); i++)
print_dev_desc(soc.dev[i]);
if(a.long_name != b.long_name)
{
printf("device '%s.%s' have different long names\n", soc.name.c_str(),
a.name.c_str());
return false;
}
if(a.desc != b.desc)
{
printf("device '%s.%s' have different descriptions\n", soc.name.c_str(),
a.name.c_str());
return false;
}
if(a.version != b.version)
{
printf("device '%s.%s' have different versions\n", soc.name.c_str(),
a.name.c_str());
return false;
}
/* addresses */
std::vector< std::pair< size_t, size_t > > map;
if(!build_map("device address", a.addr, b.addr, map))
return false;
for(size_t i = 0; i < map.size(); i++)
if(!compare_dev_addr(soc, a, a.addr[map[i].first], b.addr[map[i].second]))
return false;
/* reg */
map.clear();
if(!build_map("register", a.reg, b.reg, map))
return false;
for(size_t i = 0; i < map.size(); i++)
if(!compare_reg(soc, a, a.reg[map[i].first], b.reg[map[i].second]))
return false;
return true;
}
bool compare_soc(const soc_t& a, const soc_t& b)
{
if(a.name != b.name)
{
return printf("soc have different names\n");
return false;
}
if(a.desc != b.desc)
{
printf("soc '%s' have different descriptions\n", a.name.c_str());
return false;
}
std::vector< std::pair< size_t, size_t > > map;
if(!build_map("device", a.dev, b.dev, map))
return false;
for(size_t i = 0; i< map.size(); i++)
if(!compare_dev(a, a.dev[map[i].first], b.dev[map[i].second]))
return false;
return true;
}
int do_compare(int argc, char **argv)
{
if(argc != 2)
return printf("compare mode expects two arguments\n");
soc_t soc[2];
if(!soc_desc_parse_xml(argv[0], soc[0]))
return printf("cannot read file '%s'\n", argv[0]);
if(!soc_desc_parse_xml(argv[1], soc[1]))
return printf("cannot read file '%s'\n", argv[1]);
if(compare_soc(soc[0], soc[1]))
printf("Files are identical.\n");
return 0;
}
int do_write(int argc, char **argv)
{
if(argc != 2)
return printf("write mode expects two arguments\n");
soc_t soc;
if(!soc_desc_parse_xml(argv[0], soc))
return printf("cannot read file '%s'\n", argv[0]);
if(!soc_desc_produce_xml(argv[1], soc))
return printf("cannot write file '%s'\n", argv[1]);
return 0;
}
int do_check(int argc, char **argv)
{
for(int i = 0; i < argc; i++)
{
soc_t soc;
if(!soc_desc_parse_xml(argv[i], soc))
{
printf("cannot read file '%s'\n", argv[i]);
continue;
}
printf("[%s]\n", argv[i]);
std::vector< soc_error_t > errs = soc.errors(true);
for(size_t i = 0; i < errs.size(); i++)
{
const soc_error_t& e = errs[i];
switch(e.level)
{
case SOC_ERROR_WARNING: printf("[WARN ] "); break;
case SOC_ERROR_FATAL: printf("[FATAL] "); break;
default: printf("[ UNK ] "); break;
}
printf("%s: %s\n", e.location.c_str(), e.message.c_str());
}
}
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 error;
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;
}
if(!soc_desc_evaluate_formula(formula, map, result, error))
printf("error: %s\n", error.c_str());
else
printf("result: %lu (%#lx)\n", (unsigned long)result, (unsigned long)result);
}
return 0;
}
void usage()
{
printf("usage: tester <desc file>\n");
printf("usage: tester <mode> [options]\n");
printf("modes:\n");
printf(" compare <desc file> <desc file>\n");
printf(" write <read file> <write file>\n");
printf(" check <files...>\n");
printf(" eval [<formula>|--var <name>=<val>]...\n");
exit(1);
}
int main(int argc, char **argv)
{
if(argc != 2)
if(argc < 2)
usage();
std::string mode = argv[1];
if(mode == "compare")
return do_compare(argc - 2, argv + 2);
else if(mode == "write")
return do_write(argc - 2, argv + 2);
else if(mode == "check")
return do_check(argc - 2, argv + 2);
else if(mode == "eval")
return do_eval(argc - 2, argv + 2);
else
usage();
std::vector< soc_t > socs;
bool ret = soc_desc_parse_xml(argv[1], socs);
printf("parse result: %d\n", ret);
if(ret)
for(size_t i = 0; i < socs.size(); i++)
print_soc_desc(socs[i]);
return 0;
}