aboutsummaryrefslogtreecommitdiffstats
path: root/parse.py
diff options
context:
space:
mode:
authorbryan newbold <bnewbold@leaflabs.com>2014-02-26 19:42:51 -0500
committerbryan newbold <bnewbold@leaflabs.com>2014-02-26 19:42:51 -0500
commit6cc0f5ff9e4de203572ef6a66df82aca97bb6544 (patch)
tree398f13f7a2d3a35af8a91f497c6dc96f88b7adcd /parse.py
parent37e46745e248b0136b16aa40bed9ea20df16769d (diff)
downloadaxi-lite-gen-6cc0f5ff9e4de203572ef6a66df82aca97bb6544.tar.gz
axi-lite-gen-6cc0f5ff9e4de203572ef6a66df82aca97bb6544.zip
commit day's work
Diffstat (limited to 'parse.py')
-rwxr-xr-xparse.py381
1 files changed, 244 insertions, 137 deletions
diff --git a/parse.py b/parse.py
index c404d97..7541dde 100755
--- a/parse.py
+++ b/parse.py
@@ -17,8 +17,13 @@ import os
import jinja2
-AXI_ADDR_BITS = 16
-WORD_BITS = 32
+AXI_DATA_WIDTH = 32
+AXI_ADDR_WIDTH = 16
+AXI_ADDR_MSB = AXI_ADDR_WIDTH-1
+AXI_ADDR_LSB = 2
+
+required_fields = ('word_index', 'bits', 'mode', 'section', 'slug',
+ 'default', 'description')
def parse_slug(s):
pre = s.split('[')[0]
@@ -32,6 +37,7 @@ def parse_slug(s):
assert(post >= 0)
return (pre, post)
+
def str2val(s, bits):
"""
Strip '_' characters (eg, 0x1111_2222).
@@ -50,6 +56,7 @@ def str2val(s, bits):
assert(v >= 0 and v < 2**bits)
return v
+
class Value():
index = None
bits = None
@@ -61,8 +68,9 @@ class Value():
description = None
mode = None
addr = None
+ signed = False
- def offset(self, offset):
+ def set_offset(self, offset):
self.addr = offset + (4 * self.index)
def addr_pp(self):
@@ -73,7 +81,7 @@ class Value():
# TODO: input validation/transforms
self.index = int(word_index)
assert(self.index >= 0)
- assert(self.index <= (2**AXI_ADDR_BITS - 1))
+ assert(self.index <= (2**AXI_ADDR_WIDTH - 1))
if bits in [None, '']:
raise ValueError("Bits not defined")
@@ -101,18 +109,69 @@ class Value():
def __str__(self):
return "<Value: %s>" % str(self.__dict__)
+ def hdlwidth(self):
+ if self.bits == 1:
+ return "[0]"
+ else:
+ return "[%d:0] " % (self.bits - 1)
+
+ def pphdlwidth(self):
+ if self.bits == 1:
+ return ""
+ else:
+ return "[%d:0] " % (self.bits - 1)
+
+ def ppdefault(self):
+ return "%d'h%X" % (self.bits, self.default)
+
+ def word_list(self):
+ l = []
+ b = self.bits
+ bottom = 0
+ a = self.index
+ span = None
+ while b > 0:
+ if b < 32:
+ if (self.bits == 1):
+ span = ""
+ else:
+ span = "[%d:%d]" % (bottom+b-1, bottom)
+ l.append((a, "{%d'd0, %s%s}" % (32-b, self.slug, span), span))
+ else:
+ if (self.bits == 1):
+ span = ""
+ else:
+ span = "[%d:%d]" % (bottom+31, bottom)
+ l.append((a, "%s%s" % (self.slug, span), span))
+ a += 1
+ b -= 32
+ bottom += 32
+ return l
+
+ def ctype(self):
+ if self.bits <= 32:
+ return self.signed and "int32_t" or "uint32_t"
+ elif self.bits <= 64:
+ return self.signed and "int64_t" or "uint64_t"
+ else:
+ raise ValueError("Can't represent %d bits in C... ?" % self.bits)
+
+
class Register(Value):
read = False
write = False
+
class Parameter(Value):
- pass
+ def ppslug(self):
+ return self.slug.upper()
+
def check_overlaps(l):
rangelist = []
for val in l:
# TODO: also handle larger ranges
- this = (val.index, val.index + ((val.bits-1)/WORD_BITS))
+ this = (val.index, val.index + ((val.bits-1)/AXI_DATA_WIDTH))
inserted = False
for i in range(len(rangelist)):
that = rangelist[i]
@@ -127,7 +186,12 @@ def check_overlaps(l):
if not inserted:
rangelist.append(this)
+
def check_names(l):
+ """
+ Checks that all section+slug combinations are unique (no duplicates)
+ 'l' should be the set of all values, in any order.
+ """
names = []
n = None
for val in l:
@@ -139,142 +203,185 @@ def check_names(l):
raise ValueError("Dupliate name: %s" % n)
names.append(n)
-def error(s="unspecified"):
- sys.stderr.write(str(s) + '\n')
- sys.exit(-1)
-class Repeated():
- section = None
-
- def __init__(self, word_index, slug):
- self.index = int(word_index)
- assert(self.index >= 0)
- assert(self.index <= (2**AXI_ADDR_BITS - 1))
+def check_gaps(l):
+ """
+ Checks for gaps between memory map locations within a section.
+ Assumes 'l' is a list of values in a section, already sorted by index.
+ """
+ n = None
+ for v in l:
+ if n is not None:
+ if v.index != n:
+ raise Exception("Gap between values! Oh no! At: %s.%s (n=%d)"
+ % (v.section, v.slug, n))
+ n = v.index + 1 + (v.bits-1)/32
- if self.section is '':
- self.section = ''
- self.section_index = None
- else:
- (self.section, self.section_index) = parse_slug(section)
-
-
-req = ('word_index', 'bits', 'mode', 'section', 'slug', 'default',
- 'description')
-
-print("------- START READ")
-f = open('example.csv', 'r')
-reader = csv.DictReader(f)
-
-registers = []
-parameters = []
-mode = None
-
-for line in reader:
- if reader.line_num is 0:
- # validate fields just once
- for field in req:
- if not field in reader.fields:
- error("Missing column: %s" % field)
-
- # skip lines w/o
- if line['word_index'] in [None, '']:
- print("Skipping line %d (no index)" % reader.line_num)
- continue
-
- mode = line['mode']
- try:
- if mode.lower() == 'p':
- p = Parameter(**line)
- parameters.append(p)
- elif mode.lower() == 'r':
- r = Register(**line)
- r.read = True
- registers.append(r)
- else:
- #error("Unknown mode: %s" % mode)
- print("Skipping line %d (unknown mode %s)" % (reader.line_num,
- mode))
- pass
- except (AttributeError, TypeError, ValueError), e:
- error("Syntax error parsing line %d: %s" % (reader.line_num, e))
- sys.stdout.write(".")
-print('')
-f.close()
-
-print("Registers:\t%d" % len(registers))
-print("Parameters:\t%d" % len(parameters))
-
-offset = 0x0
-for r in registers:
- r.offset(offset)
-for p in parameters:
- p.offset(offset)
-
-check_overlaps(registers + parameters)
-check_names(registers + parameters)
-sections = {}
-for val in (registers + parameters):
- if not val.section in sections.keys():
- sections[val.section] = []
- sections[val.section].append(val)
-
-for key, sec in sections.iteritems():
- sections[key] = sorted(sec, key=lambda x: x.index)
-
-print("------- END READ")
-
-# TODO: process into sections; sort; apply offsets
-
-context = dict(registers=registers,
- parameters=parameters,
- name="example",
- now=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()),
- attribution="Generated by AXI-Lite Generator",
- whoami=os.getenv('USER'),
- sections=sections)
-
-# TODO:
-# jinja2.ChoiceLoader
-# jinja2.PackageLoader
-env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'))
-#print("------- START PYTHON")
-"""
-params: single helper to dump them all
-registers:
- helper get/set by string (eg, get("meta.magic"))
- module cmd to dump them all
- module+slug cmd to get/set
- <section>.<slug> getter/setter functions
-"""
-#print("------- END PYTHON")
-#print("------- START HDL")
-"""
-wrapper stub also.
-params: passed all around
-registers: just one place
-"""
-#print("------- END HDL")
+def error(s="unspecified"):
+ sys.stderr.write(str(s) + '\n')
+ sys.exit(-1)
-#print("------- START C_HEADER")
-"""
-just structs for parameters/registers
-"""
-#print("------- END C_HEADER")
-print("------- START HTML")
-t = env.get_template('minimal.html.tmpl')
-out_f = open('output/example.html', 'w')
-out_f.write(t.render(**context))
-out_f.close()
-print("------- END HTML")
+def parse():
+ print("------- START READ")
+ f = open('example.csv', 'r')
+ reader = csv.DictReader(f)
-print("------- START RST")
-t = env.get_template('minimal.rst.tmpl')
-out_f = open('output/example.rst', 'w')
-out_f.write(t.render(**context))
-out_f.close()
-print("------- END RST")
+ registers = []
+ parameters = []
+ mode = None
-print("------- DONE!")
+ for line in reader:
+ if reader.line_num is 0:
+ # validate fields just once
+ for field in req:
+ if not field in reader.fields:
+ error("Missing column: %s" % field)
+
+ # skip lines w/o
+ if line['word_index'] in [None, '']:
+ print("Skipping line %d (no index)" % reader.line_num)
+ continue
+
+ mode = line['mode'].lower()
+ try:
+ if mode == 'p':
+ p = Parameter(**line)
+ parameters.append(p)
+ elif mode in ['r', 'w', 'rw', 'wr']:
+ r = Register(**line)
+ r.read = 'r' in mode
+ r.write = 'w' in mode
+ registers.append(r)
+ else:
+ #error("Unknown mode: %s" % mode)
+ print("Skipping line %d (unknown mode %s)" % (reader.line_num,
+ mode))
+ pass
+ except (AttributeError, TypeError, ValueError), e:
+ error("Syntax error parsing line %d: %s" % (reader.line_num, e))
+ sys.stdout.write(".")
+ print('')
+ f.close()
+
+ print("Registers:\t%d" % len(registers))
+ print("Parameters:\t%d" % len(parameters))
+
+ offset = 0x0
+ for r in registers:
+ r.set_offset(offset)
+ for p in parameters:
+ p.set_offset(offset)
+
+ check_overlaps(registers + parameters)
+ check_names(registers + parameters)
+ sections = {}
+ for val in (registers + parameters):
+ if not val.section in sections.keys():
+ sections[val.section] = []
+ sections[val.section].append(val)
+
+ for key, sec in sections.iteritems():
+ sections[key] = sorted(sec, key=lambda x: x.index)
+ check_gaps(sections[key])
+
+ print("------- END READ")
+ return registers, parameters, sections
+
+
+def output(registers, parameters, sections):
+ settings = {
+ 'stub_axi_nets': True,
+ 'stub_nets': True,
+ }
+ context = dict(registers=registers,
+ parameters=parameters,
+ name="example",
+ now=time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime()),
+ attribution="Generated by AXI-Lite Generator",
+ whoami=os.getenv('USER'),
+ sections=sections,
+ AXI_DATA_WIDTH=AXI_DATA_WIDTH,
+ AXI_ADDR_WIDTH=AXI_ADDR_WIDTH,
+ AXI_ADDR_MSB=AXI_ADDR_MSB,
+ AXI_ADDR_LSB=AXI_ADDR_LSB,
+ settings=settings)
+
+ def guess_autoescape(template_name):
+ """Only auto-escape HTML documents"""
+ if template_name is None:
+ return False
+ if 'html' in template_name.lower():
+ return True
+ else:
+ return False
+
+ # TODO:
+ # jinja2.ChoiceLoader
+ # jinja2.PackageLoader
+ env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'),
+ lstrip_blocks=True,
+ trim_blocks=True,
+ autoescape=guess_autoescape,
+ extensions=['jinja2.ext.autoescape'])
+ #print("------- START PYTHON")
+ """
+ params: single helper to dump them all
+ registers:
+ helper get/set by string (eg, get("meta.magic"))
+ module cmd to dump them all
+ module+slug cmd to get/set
+ <section>.<slug> getter/setter functions
+ """
+ #print("------- END PYTHON")
+ print("------- START HDL")
+ """
+ wrapper stub also.
+ params: passed all around
+ registers: just one place
+ """
+ t = env.get_template('partial_axi_lite_slave.v.tmpl')
+ out_f = open('output/axi_lite_slave_%s.v' % context['name'], 'w')
+ out_f.write(t.render(**context))
+ out_f.close()
+
+ t = env.get_template('stub.v.tmpl')
+ out_f = open('output/%s_stub.v' % context['name'], 'w')
+ out_f.write(t.render(**context))
+ out_f.close()
+ print("------- END HDL")
+
+ #print("------- START C_HEADER")
+ """
+ just structs for parameters/registers
+ """
+ t = env.get_template('headers.h.tmpl')
+ out_f = open('output/%s_headers.h' % context['name'], 'w')
+ out_f.write(t.render(**context))
+ out_f.close()
+ #print("------- END C_HEADER")
+
+ print("------- START HTML")
+ t = env.get_template('minimal.html.tmpl')
+ out_f = open('output/%s.html' % context['name'], 'w')
+ out_f.write(t.render(**context))
+ out_f.close()
+ print("------- END HTML")
+
+ print("------- START RST")
+ t = env.get_template('minimal.rst.tmpl')
+ out_f = open('output/%s.rst' % context['name'], 'w')
+ out_f.write(t.render(**context))
+ out_f.close()
+ print("------- END RST")
+
+ print("------- DONE!")
+
+def main():
+ r, p, s = parse()
+ output(r,p,s)
+
+if __name__=="__main__":
+ main()