diff options
Diffstat (limited to 'utils/reggen-ng/reggen-ng.py')
-rwxr-xr-x | utils/reggen-ng/reggen-ng.py | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/utils/reggen-ng/reggen-ng.py b/utils/reggen-ng/reggen-ng.py new file mode 100755 index 0000000000..09549d6fa4 --- /dev/null +++ b/utils/reggen-ng/reggen-ng.py @@ -0,0 +1,465 @@ +#!/usr/bin/python3 +# +# Tool to generate XML files for Rockbox regtools. The syntax is pretty simple +# and much less verbose than XML; see the '.reggen' files next to this script. +# +# Currently this tool expects a 'reggen' file on standard input and writes the +# equivalent XML to standard output. +# +# TODO: document this better and improve the command line usage + +from lxml import etree as ET +from lxml.builder import E +import functools + +class Soc: + def __init__(self): + self.name = None + self.title = None + self.desc = None + self.isa = None + self.version = None + self.authors = [] + self.nodes = [] + + def gen(self): + node = E("soc", version="2") + if self.name is not None: + node.append(E("name", self.name)) + if self.desc is not None: + node.append(E("desc", self.desc)) + if self.isa is not None: + node.append(E("isa", self.isa)) + if self.isa is not None: + node.append(E("version", self.version)) + for a in self.authors: + node.append(E("author", a)) + for n in self.nodes: + node.append(n.gen()) + return node + +class Node: + def __init__(self, name): + self.name = name + self.title = None + self.desc = None + self.instances = [] + self.children = [] + self.register_width = None + self.fields = [] + + def gen(self): + node = E("node", E("name", self.name)) + + if self.title is not None: + node.append(E("title", self.title)) + + if self.desc is not None: + node.append(E("desc", self.desc)) + + for i in self.instances: + node.append(i.gen()) + + for c in self.children: + node.append(c.gen()) + + if self.register_width is not None: + reg = E("register", + E("width", self.register_width)) + for f in self.fields: + reg.append(f.gen()) + node.append(reg) + + return node + +class Instance: + def __init__(self, name, addr, stride=None, count=None): + self.name = name + self.address = addr + self.stride = stride + self.count = count + + def gen(self): + node = E("instance", E("name", self.name)) + + if self.stride is not None: + node.append(E("range", + E("first", "0"), + E("count", self.count), + E("base", self.address), + E("stride", self.stride))) + else: + node.append(E("address", self.address)) + + return node + +class Field: + def __init__(self, name, msb, lsb): + self.name = name + self.desc = None + self.msb = int(msb) + self.lsb = int(lsb) + self.enums = [] + + def gen(self): + node = E("field", + E("name", self.name), + E("position", "%d" % self.lsb)) + + if self.desc is not None: + node.append(E("desc", self.desc)) + + if self.msb != self.lsb: + node.append(E("width", str(self.msb - self.lsb + 1))) + + for n, v in self.enums: + node.append(E("enum", E("name", n), E("value", v))) + + return node + +class Variant: + def __init__(self, ty, offset): + self.type = ty + self.offset = offset + + def gen(self): + return E("variant", + E("type", self.type), + E("offset", self.offset)) + +class Parser: + def __init__(self, f): + self.input = f + self.line = 1 + self.eolstop = False + self.next() + + def parse(self): + self.skipws() + results = self.parse_block_inner({ + 'name': (self.parse_string, 1), + 'title': (self.parse_string, 1), + 'desc': (self.parse_string, 1), + 'isa': (self.parse_string, 1), + 'version': (self.parse_string, 1), + 'author': self.parse_string, + 'node': self.parse_node, + 'reg': self.parse_register, + }) + + ret = Soc() + + if 'name' in results: + ret.name = results['name'][0] + else: + self.err('missing "name" statement at toplevel') + + if 'title' in results: + ret.title = results['title'][0] + if 'desc' in results: + ret.desc = results['desc'][0] + if 'isa' in results: + ret.isa = results['isa'][0] + if 'version' in results: + ret.version = results['version'][0] + if 'author' in results: + ret.authors += results['author'] + if 'node' in results: + ret.nodes += results['node'] + if 'reg' in results: + ret.nodes += results['reg'] + + return ret + + def parse_node(self): + name = self.parse_ident() + ret, results = self.parse_node_block(name, { + 'title': (self.parse_string, 1), + 'node': self.parse_node, + 'reg': self.parse_register, + }) + + if 'title' in results: + ret.title = results['title'][0] + if 'node' in results: + ret.children += results['node'] + if 'reg' in results: + ret.children += results['reg'] + + return ret + + def parse_register(self): + words, has_block = self.parse_wordline(True) + name = words[0] + width = "32" + addr = None + + if len(words) == 1: + # reg NAME + pass + elif len(words) == 2: + # reg NAME ADDR + addr = words[1] + elif len(words) == 3: + # reg WIDTH NAME ADDR + name = words[1] + width = words[0] + addr = words[2] + else: + self.err('malformed register statement') + + if has_block: + ret, results = self.parse_node_block(name, { + 'field': self.parse_field, + 'fld': self.parse_field, + 'bit': self.parse_field, + 'variant': self.parse_variant, + }) + + if 'field' in results: + ret.fields += results['field'] + if 'fld' in results: + ret.fields += results['fld'] + if 'bit' in results: + ret.fields += results['bit'] + if 'variant' in results: + ret.fields += results['variant'] + else: + ret = Node(name) + + if len(ret.instances) == 0: + if addr is None: + self.err("no address specified for register") + ret.instances.append(Instance(ret.name, addr)) + elif addr: + self.err("duplicate address specification for register") + + ret.register_width = width + return ret + + def parse_node_block(self, name, extra_parsers): + parsers = { + 'desc': (self.parse_string, 1), + 'addr': (self.parse_printable, 1), + 'instance': functools.partial(self.parse_instance, name), + } + + parsers.update(extra_parsers) + results = self.parse_block(parsers) + ret = Node(name) + + if 'desc' in results: + ret.desc = results['desc'][0] + if 'addr' in results: + ret.instances.append(Instance(ret.name, results['addr'][0])) + if 'instance' in results: + if 'addr' in results: + self.err('instance statement not allowed when addr is given') + + ret.instances += results['instance'] + + return ret, results + + def parse_instance(self, default_name): + words = self.parse_wordline(False)[0] + + if len(words) == 1: + # instance ADDR + return Instance(default_name, words[0]) + elif len(words) == 2: + # instance NAME ADDR + return Instance(words[0], words[1]) + elif len(words) == 3: + # instance ADDR STRIDE COUNT + return Instance(default_name, words[0], words[1], words[2]) + elif len(words) == 4: + # instance NAME ADDR STRIDE COUNT + return Instance(words[0], words[1], words[2], words[3]) + else: + self.err('malformed instance statement') + + def parse_field(self): + words, has_block = self.parse_wordline(True) + name = None + msb = None + lsb = None + + if len(words) == 2: + # field BIT NAME + lsb = msb = words[0] + name = words[1] + elif len(words) == 3: + # field MSB LSB NAME + msb = words[0] + lsb = words[1] + name = words[2] + else: + self.err('malformed field statement') + + try: + int(msb) + int(lsb) + except: + self.err('field MSB/LSB must be integers') + + ret = Field(name, msb, lsb) + if has_block: + results = self.parse_block({ + 'desc': self.parse_string, + 'enum': self.parse_enum, + }) + + if 'desc' in results: + if len(results['desc']) > 1: + self.err("only one description allowed") + ret.desc = results['desc'][0] + + if 'enum' in results: + ret.enums += results['enum'] + + return ret + + def parse_wordline(self, allow_block=False): + words = [] + self.eolstop = True + while(self.chr != '{' and self.chr != '}' and + self.chr != '\n' and self.chr != ';'): + words.append(self.parse_printable()) + self.eolstop = False + + if len(words) == 0: + self.err('this type of statement cannot be empty') + + if self.chr == '{': + if not allow_block: + self.err('this type of statement does not accept blocks') + has_block = True + else: + has_block = False + self.skipws() + + return words, has_block + + def parse_variant(self): + ty = self.parse_printable() + off = self.parse_printable() + return Variant(ty, off) + + def parse_enum(self): + name = self.parse_printable() + value = self.parse_printable() + return (name, value) + + def parse_block(self, parsers): + if self.chr != '{': + self.err("expected '{'") + self.next() + self.skipws() + + ret = self.parse_block_inner(parsers) + + assert self.chr == '}' + self.next() + self.skipws() + return ret + + def parse_block_inner(self, parsers): + ret = {} + cnt = {} + while self.chr != '}' and self.chr != '\0': + kwd = self.parse_ident() + if kwd not in parsers: + self.err("invalid keyword for block") + + if kwd not in ret: + ret[kwd] = [] + cnt[kwd] = 0 + + pentry = parsers[kwd] + if type(pentry) is tuple and len(pentry) == 2: + pfunc = pentry[0] + max_cnt = pentry[1] + else: + pfunc = pentry + max_cnt = 0 + + if max_cnt > 0 and cnt[kwd] == max_cnt: + if max_cnt == 1: + self.err("at most one '%s' statement allowed in this block" % kwd) + else: + self.err("at most %d '%s' statements allowed in this block" % (max_cnt, kwd)) + + ret[kwd].append(pfunc()) + cnt[kwd] += 1 + + return ret + + def parse_ident(self): + ident = '' + while self.chr.isalnum() or self.chr == '_': + ident += self.chr + self.next() + + if len(ident) == 0: + self.err("expected identifier") + + self.skipws() + return ident + + def parse_string(self): + if self.chr != '"': + self.err("expected string") + self.next() + + s = '' + while self.chr != '"': + s += self.chr + self.next() + self.next() + + self.skipws() + return s + + def parse_printable(self): + s = '' + while not self.chr.isspace() and self.chr != ';': + s += self.chr + self.next() + + self.skipws() + return s + + def skipws(self): + while True: + if self.chr == '#': + while self.chr != '\n': + self.next() + if self.eolstop: + break + self.next() + elif self.chr == '\n' or self.chr == ';': + if self.eolstop: + break + self.next() + elif self.chr.isspace(): + self.next() + else: + break + + def next(self): + self.chr = self.input.read(1) + if len(self.chr) == 0: + self.chr = '\0' + if self.chr == '\n': + self.line += 1 + return self.chr + + def err(self, msg): + raise ValueError("on line %d: %s" % (self.line, msg)) + +if __name__ == '__main__': + import sys + p = Parser(sys.stdin) + r = p.parse() + print('<?xml version="1.0"?>') + ET.dump(r.gen()) |