Vortex 2.0 changes:
+ Microarchitecture optimizations + 64-bit support + Xilinx FPGA support + LLVM-16 support + Refactoring and quality control fixes
This commit is contained in:
@@ -1,829 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
# Copyright © 2019-2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import xml.etree.ElementTree as ET
|
||||
import re
|
||||
import json
|
||||
import argparse
|
||||
import math
|
||||
|
||||
vl_include_re = re.compile(r"^\s*`include\s+\"(.+)\"")
|
||||
vl_define_re = re.compile(r"^\s*`define\s+(\w+)(\([\w\s,]*\))?(.*)")
|
||||
vl_ifdef_re = re.compile(r"^\s*`(ifdef|ifndef|elsif)\s+(\w+)\s*$")
|
||||
vl_endif_re = re.compile(r"^\s*`(endif|else)\s*$")
|
||||
vl_expand_re = re.compile(r"`([0-9a-zA-Z_]+)")
|
||||
vl_int_re = re.compile(r"\d+'s*h([\da-fA-F]+)")
|
||||
|
||||
exclude_files = []
|
||||
include_dirs = []
|
||||
macros = []
|
||||
br_stack = []
|
||||
def parse_vl_int(text):
|
||||
str_hex = re.sub(vl_int_re, r'\1', text)
|
||||
return int(str_hex, 16)
|
||||
|
||||
def translate_ternary(text):
|
||||
def source_loc(xml_doc, xml_loc):
|
||||
loc = xml_loc.split(",")
|
||||
file_id = loc[0]
|
||||
start_line = loc[1]
|
||||
start_col = loc[2]
|
||||
end_line = loc[3]
|
||||
end_col = loc[4]
|
||||
file = xml_doc.find(".//file/[@id='" + file_id + "']").get("filename")
|
||||
return file + " (" + start_line + ":" + start_col + "-" + end_line + ":" + end_col + ")"
|
||||
|
||||
def skip_space(text, i, ln, step):
|
||||
while (i >= 0) and (i < ln):
|
||||
c = text[i]
|
||||
if not c.isspace():
|
||||
break
|
||||
i += step
|
||||
return i
|
||||
|
||||
def skip_expr(text, i, ln, step):
|
||||
paren = 0
|
||||
checkparen = True
|
||||
while (i >= 0) and (i < ln):
|
||||
c = text[i]
|
||||
if checkparen and (((step < 0) and (c == ')')) or ((step > 0) and (c == '('))):
|
||||
paren += 1
|
||||
elif checkparen and (((step < 0) and (c == '(')) or ((step > 0) and (c == ')'))):
|
||||
if (0 == paren):
|
||||
break
|
||||
paren -= 1
|
||||
if (0 == paren):
|
||||
i = skip_space(text, i + step, ln, step)
|
||||
checkparen = False
|
||||
continue
|
||||
elif (0 == paren) and not (c.isalnum() or (c == '_')):
|
||||
break
|
||||
i += step
|
||||
return (i - step)
|
||||
|
||||
def parse_ternary(text):
|
||||
ternary = None
|
||||
ln = len(text)
|
||||
for i in range(1, ln):
|
||||
c = text[i]
|
||||
if not (c == '?'):
|
||||
continue
|
||||
# parse condition expression
|
||||
i0 = skip_space(text, i - 1, ln, -1)
|
||||
if (i < 0):
|
||||
raise Exception("invalid condition expression")
|
||||
i1 = skip_expr(text, i0, ln, -1)
|
||||
if (i1 > i0):
|
||||
raise Exception("invalid condition expression")
|
||||
# parse true expression
|
||||
i2 = skip_space(text, i + 1, ln, 1)
|
||||
if (i2 >= ln):
|
||||
raise Exception("invalid true expression")
|
||||
i3 = skip_expr(text, i2, ln, 1)
|
||||
if (i3 < i2):
|
||||
raise Exception("invalid true expression")
|
||||
# parse colon
|
||||
i4 = skip_space(text, i3 + 1, ln, 1)
|
||||
if (i4 >= ln):
|
||||
raise Exception("invalid colon")
|
||||
if not (text[i4] == ':'):
|
||||
raise Exception("missing colon")
|
||||
# parse false expression
|
||||
i5 = skip_space(text, i4 + 1, ln, 1)
|
||||
if (i5 >= ln):
|
||||
raise Exception("invalid false expression")
|
||||
i6 = skip_expr(text, i5, ln, 1)
|
||||
if (i6 < i5):
|
||||
raise Exception("invalid false expression")
|
||||
ternary = (i0, i1, i2, i3, i5, i6)
|
||||
break
|
||||
return ternary
|
||||
|
||||
while True:
|
||||
pos = parse_ternary(text)
|
||||
if pos is None:
|
||||
break
|
||||
# convert to python ternary
|
||||
newText = text[:pos[1]] + text[pos[2]:pos[3]+1] + " if " + text[pos[1]:pos[0]+1] + " else " + text[pos[4]:pos[5]+1] + text[pos[5]+1:]
|
||||
text = newText
|
||||
|
||||
return text
|
||||
|
||||
def parse_func_args(text):
|
||||
args = []
|
||||
arg = ''
|
||||
l = len(text)
|
||||
if text[0] != '(':
|
||||
raise Exception("missing leading parenthesis: " + text)
|
||||
paren = 1
|
||||
for i in range(1, l):
|
||||
c = text[i]
|
||||
if c == '(':
|
||||
paren += 1
|
||||
elif c == ')':
|
||||
if paren == 0:
|
||||
raise Exception("mismatched parenthesis: (" + i + ") " + text)
|
||||
paren -= 1
|
||||
if paren == 0:
|
||||
l = i
|
||||
break
|
||||
if c == ',' and paren == 1:
|
||||
if arg.strip():
|
||||
args.append(arg)
|
||||
arg = ''
|
||||
else:
|
||||
arg += c
|
||||
if paren != 0:
|
||||
raise Exception("missing closing parenthesis: " + text)
|
||||
if arg.strip():
|
||||
args.append(arg)
|
||||
|
||||
return (args, l)
|
||||
|
||||
def load_include_path(dir):
|
||||
if not dir in include_dirs:
|
||||
print("*** include path: " + dir)
|
||||
include_dirs.append(dir)
|
||||
|
||||
def resolve_include_path(filename, parent_dir):
|
||||
if os.path.basename(filename) in exclude_files:
|
||||
return None
|
||||
if os.path.isfile(filename):
|
||||
return os.path.abspath(filename)
|
||||
search_dirs = include_dirs
|
||||
if parent_dir:
|
||||
search_dirs.append(parent_dir)
|
||||
for dir in search_dirs:
|
||||
filepath = os.path.join(dir, filename)
|
||||
if os.path.isfile(filepath):
|
||||
return os.path.abspath(filepath)
|
||||
raise Exception("couldn't find include file: " + filename + " in " + parent_dir)
|
||||
|
||||
def remove_comments(text):
|
||||
text = re.sub(re.compile("/\*.*?\*/",re.DOTALL ), "", text) # multiline
|
||||
text = re.sub(re.compile("//.*?\n" ), "\n", text) # singleline
|
||||
return text
|
||||
|
||||
def add_macro(name, args, value):
|
||||
macro = (name, args, value)
|
||||
macros.append(macro)
|
||||
'''
|
||||
if not args is None:
|
||||
print("*** token: " + name + "(", end='')
|
||||
for i in range(len(args)):
|
||||
if i > 0:
|
||||
print(', ', end='')
|
||||
print(args[i], end='')
|
||||
print(")=" + value)
|
||||
def parse_dtype_width(xml_doc, dtype_id):
|
||||
xml_type = xml_doc.find(".//typetable/*[@id='" + dtype_id + "']")
|
||||
if xml_type.tag == "packarraydtype" or xml_type.tag == "unpackarraydtype":
|
||||
sub_dtype_id = xml_type.get("sub_dtype_id")
|
||||
base_width = parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
const = xml_type.iter("const")
|
||||
left = parse_vl_int(next(const).get("name"))
|
||||
right = parse_vl_int(next(const).get("name"))
|
||||
return base_width * (left - right + 1)
|
||||
elif xml_type.tag == "structdtype":
|
||||
width = 0
|
||||
for member in xml_type.iter("memberdtype"):
|
||||
sub_dtype_id = member.get("sub_dtype_id")
|
||||
width = width + parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
return width
|
||||
elif xml_type.tag == "uniondtype":
|
||||
width = 0
|
||||
for member in xml_type.iter("memberdtype"):
|
||||
sub_dtype_id = member.get("sub_dtype_id")
|
||||
width = max(width, parse_dtype_width(xml_doc, sub_dtype_id))
|
||||
return width
|
||||
else:
|
||||
print("*** token: " + name + "=" + value)
|
||||
'''
|
||||
|
||||
def find_macro(name):
|
||||
for macro in macros:
|
||||
if macro[0] == name:
|
||||
return macro
|
||||
return None
|
||||
|
||||
def expand_text(text, params):
|
||||
|
||||
def re_pattern_args(args):
|
||||
p = "(?<![0-9a-zA-Z_])("
|
||||
i = 0
|
||||
for arg in args:
|
||||
if i > 0:
|
||||
p += "|"
|
||||
p += arg
|
||||
i += 1
|
||||
p += ")(?![0-9a-zA-Z_])"
|
||||
return p
|
||||
|
||||
class DoReplParam(object):
|
||||
def __init__(self, params):
|
||||
self.params = params
|
||||
self.expanded = False
|
||||
def __call__(self, match):
|
||||
name = match.group(1)
|
||||
self.expanded = True
|
||||
return self.params[name]
|
||||
|
||||
class DoReplMacro(object):
|
||||
def __init__(self):
|
||||
self.expanded = False
|
||||
self.has_func = False
|
||||
def __call__(self, match):
|
||||
name = match.group(1)
|
||||
macro = find_macro(name)
|
||||
if macro:
|
||||
if not macro[1] is None:
|
||||
self.has_func = True
|
||||
else:
|
||||
self.expanded = True
|
||||
return macro[2]
|
||||
return "`" + name
|
||||
|
||||
def repl_func_macro(text):
|
||||
expanded = False
|
||||
match = re.search(vl_expand_re, text)
|
||||
if match:
|
||||
name = match.group(1)
|
||||
macro = find_macro(name)
|
||||
if macro:
|
||||
args = macro[1]
|
||||
value = macro[2]
|
||||
if not args is None:
|
||||
str_args = text[match.end():].strip()
|
||||
f_args = parse_func_args(str_args)
|
||||
if len(args) == 0:
|
||||
if len(f_args[0]) != 0:
|
||||
raise Exception("invalid argments for macro '" + name + "': value=" + text)
|
||||
else:
|
||||
if len(args) != len(f_args[0]):
|
||||
raise Exception("mismatch number of argments for macro '" + name + "': actual=" + len(f_args[0]) + ", expected=" + len(args))
|
||||
|
||||
pattern = re_pattern_args(args)
|
||||
params = {}
|
||||
for i in range(len(args)):
|
||||
params[args[i]] = f_args[0][i]
|
||||
dorepl = DoReplParam(params)
|
||||
value = re.sub(pattern, dorepl, value)
|
||||
|
||||
str_head = text[0:match.start()]
|
||||
str_tail = text[match.end() + f_args[1]+1:]
|
||||
text = str_head + value + str_tail
|
||||
expanded = True
|
||||
if expanded:
|
||||
return text
|
||||
return None
|
||||
|
||||
changed = False
|
||||
iter = 0
|
||||
|
||||
while True:
|
||||
if iter > 65536:
|
||||
raise Exception("Macro recursion!")
|
||||
has_func = False
|
||||
while True:
|
||||
params_updated = False
|
||||
if not params is None:
|
||||
do_repl = DoReplParam(params)
|
||||
pattern = re_pattern_args(params)
|
||||
new_text = re.sub(pattern, do_repl, text)
|
||||
if do_repl.expanded:
|
||||
text = new_text
|
||||
params_updated = True
|
||||
do_repl = DoReplMacro()
|
||||
new_text = re.sub(vl_expand_re, do_repl, text)
|
||||
has_func = do_repl.has_func
|
||||
if not (params_updated or do_repl.expanded):
|
||||
break
|
||||
text = new_text
|
||||
changed = True
|
||||
if not has_func:
|
||||
break
|
||||
expanded = repl_func_macro(text)
|
||||
if not expanded:
|
||||
break
|
||||
text = expanded
|
||||
changed = True
|
||||
iter += 1
|
||||
|
||||
if changed:
|
||||
return text
|
||||
return None
|
||||
|
||||
def parse_include(filename, nesting):
|
||||
print("*** parsing: " + filename + "...")
|
||||
if nesting > 99:
|
||||
raise Exception("include recursion!")
|
||||
#print("*** parsing '" + filename + "'...")
|
||||
content = None
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
# remove comments
|
||||
content = remove_comments(content)
|
||||
# parse content
|
||||
prev_line = None
|
||||
for line in content.splitlines(False):
|
||||
# skip empty lines
|
||||
if re.match(re.compile(r'^\s*$'), line):
|
||||
continue
|
||||
# merge multi-line lines
|
||||
if line.endswith('\\'):
|
||||
if prev_line:
|
||||
prev_line += line[:len(line) - 1]
|
||||
else:
|
||||
prev_line = line[:len(line) - 1]
|
||||
continue
|
||||
if prev_line:
|
||||
line = prev_line + line
|
||||
prev_line = None
|
||||
# parse ifdef
|
||||
m = re.match(vl_ifdef_re, line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
cond = m.group(2)
|
||||
taken = find_macro(cond) is not None
|
||||
if key == 'ifndef':
|
||||
taken = not taken
|
||||
elif key == '"elsif':
|
||||
br_stack.pop()
|
||||
br_stack.append(taken)
|
||||
#print("*** " + key + "(" + cond + ") => " + str(taken))
|
||||
continue
|
||||
# parse endif
|
||||
m = re.match(vl_endif_re, line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
top = br_stack.pop()
|
||||
if key == 'else':
|
||||
br_stack.append(not top)
|
||||
#print("*** " + key)
|
||||
continue
|
||||
# skip disabled blocks
|
||||
if not all(br_stack):
|
||||
continue
|
||||
sub_dtype_id = xml_type.get("sub_dtype_id")
|
||||
if sub_dtype_id != None:
|
||||
return parse_dtype_width(xml_doc, sub_dtype_id)
|
||||
left = xml_type.get("left")
|
||||
right = xml_type.get("right")
|
||||
if left != None and right != None:
|
||||
return int(left) - int(right) + 1
|
||||
return 1
|
||||
|
||||
# parse include
|
||||
m = re.match(vl_include_re, line)
|
||||
if m:
|
||||
include = m.group(1)
|
||||
include = resolve_include_path(include, os.path.dirname(filename))
|
||||
if include:
|
||||
parse_include(include, nesting + 1)
|
||||
continue
|
||||
# parse define
|
||||
m = re.match(vl_define_re, line)
|
||||
if m:
|
||||
name = m.group(1)
|
||||
args = m.group(2)
|
||||
if args:
|
||||
args = args[1:len(args)-1].strip()
|
||||
if args != '':
|
||||
args = args.split(',')
|
||||
for i in range(len(args)):
|
||||
args[i] = args[i].strip()
|
||||
else:
|
||||
args = []
|
||||
value = m.group(3)
|
||||
add_macro(name, args, value.strip())
|
||||
def parse_var_name(xml_doc, xml_node):
|
||||
if xml_node.tag == "varref":
|
||||
return xml_node.get("name")
|
||||
elif xml_node.tag == "varxref":
|
||||
name = xml_node.get("name")
|
||||
dotted = xml_node.get("dotted")
|
||||
return dotted + '.' + name
|
||||
else:
|
||||
raise ET.ParseError("invalid probe entry" + source_loc(xml_doc, xml_node.get("loc")))
|
||||
return name
|
||||
|
||||
def parse_sel_name(xml_doc, xml_node):
|
||||
name = parse_var_name(xml_doc, xml_node.find("*"))
|
||||
const = xml_node.iter("const")
|
||||
offset = parse_vl_int(next(const).get("name"))
|
||||
#size = parse_vl_int(next(const).get("name"))
|
||||
return name + '_' + str(offset)
|
||||
|
||||
def parse_array_name(xml_doc, xml_node):
|
||||
if xml_node.tag == "arraysel":
|
||||
name = parse_array_name(xml_doc, xml_node.find("*"))
|
||||
xml_size = xml_node.find("const").get("name")
|
||||
array_size = parse_vl_int(xml_size)
|
||||
name = name + '_' + str(array_size)
|
||||
else:
|
||||
name = parse_var_name(xml_doc, xml_node)
|
||||
return name
|
||||
|
||||
def parse_vl_port(xml_doc, xml_node, signals):
|
||||
total_width = 0
|
||||
if xml_node.tag == "concat":
|
||||
for xml_child in xml_node.findall("*"):
|
||||
total_width = total_width + parse_vl_port(xml_doc, xml_child, signals)
|
||||
elif xml_node.tag == "varref" or xml_node.tag == "varxref":
|
||||
name = parse_var_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
elif xml_node.tag == "sel":
|
||||
name = parse_sel_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
elif xml_node.tag == "arraysel":
|
||||
name = parse_array_name(xml_doc, xml_node)
|
||||
dtype_id = xml_node.get("dtype_id")
|
||||
signal_width = parse_dtype_width(xml_doc, dtype_id)
|
||||
signals.append([name, signal_width])
|
||||
total_width = total_width + signal_width
|
||||
else:
|
||||
raise ET.ParseError("invalid probe entry: " + source_loc(xml_doc, xml_node.get("loc")))
|
||||
return total_width
|
||||
|
||||
def parse_xml(filename, max_taps):
|
||||
xml_doc = ET.parse(filename)
|
||||
modules = {}
|
||||
xml_modules = xml_doc.findall(".//module/[@origName='VX_scope_tap']")
|
||||
for xml_module in xml_modules:
|
||||
scope_id = parse_vl_int(xml_module.find(".//var/[@name='SCOPE_ID']/const").get("name"))
|
||||
triggerw = parse_vl_int(xml_module.find(".//var/[@name='TRIGGERW']/const").get("name"))
|
||||
probew = parse_vl_int(xml_module.find(".//var/[@name='PROBEW']/const").get("name"))
|
||||
module_name = xml_module.get("name")
|
||||
modules[module_name] = [scope_id, triggerw, probew]
|
||||
|
||||
taps = []
|
||||
xml_instances = xml_doc.iter("instance")
|
||||
for xml_instance in xml_instances:
|
||||
if (max_taps != -1 and len(taps) >= max_taps):
|
||||
break
|
||||
defName = xml_instance.get("defName")
|
||||
module = modules.get(defName)
|
||||
if module is None:
|
||||
continue
|
||||
triggers = []
|
||||
probes = []
|
||||
w = parse_vl_port(xml_doc, xml_instance.find(".//port/[@name='triggers']/*"), triggers)
|
||||
if w != module[1]:
|
||||
raise ET.ParseError("invalid triggers width: actual=" + str(w) + ", expected=" + str(module[1]))
|
||||
w = parse_vl_port(xml_doc, xml_instance.find(".//port/[@name='probes']/*"), probes)
|
||||
if w != module[2]:
|
||||
raise ET.ParseError("invalid probes width: actual=" + str(w) + ", expected=" + str(module[2]))
|
||||
signals = probes
|
||||
for trigger in triggers:
|
||||
signals.append(trigger)
|
||||
loc = xml_instance.get("loc")
|
||||
hier = xml_doc.find(".//cell/[@loc='" + loc + "']").get("hier")
|
||||
path = hier.rsplit(".", 1)[0]
|
||||
taps.append({"id":module[0],
|
||||
"width":module[1] + module[2],
|
||||
"signals":signals,
|
||||
"path":path})
|
||||
|
||||
def parse_includes(includes):
|
||||
# change current directory to include directory
|
||||
old_dir = os.getcwd()
|
||||
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
os.chdir(script_dir)
|
||||
|
||||
for include in includes:
|
||||
parse_include(include, 0)
|
||||
load_include_path(os.path.dirname(include))
|
||||
|
||||
# restore current directory
|
||||
os.chdir(old_dir)
|
||||
|
||||
def load_defines(defines):
|
||||
for define in defines:
|
||||
key_value = define.split('=', 2)
|
||||
name = key_value[0]
|
||||
value = ''
|
||||
if len(key_value) == 2:
|
||||
value = key_value[1]
|
||||
add_macro(name, None, value)
|
||||
|
||||
def load_config(filename):
|
||||
with open(filename, "r") as f:
|
||||
config = json.load(f)
|
||||
print("condfig=", config)
|
||||
return config
|
||||
|
||||
def eval_node(text, params):
|
||||
def clog2(x):
|
||||
l2 = math.log2(x)
|
||||
cl = math.ceil(l2)
|
||||
return int(cl)
|
||||
|
||||
if not type(text) == str:
|
||||
return text
|
||||
|
||||
expanded = expand_text(text, params)
|
||||
if expanded:
|
||||
text = expanded
|
||||
|
||||
try:
|
||||
__text = text.replace('$clog2', '__clog2')
|
||||
__text = translate_ternary(__text)
|
||||
__text = __text.replace('||', 'or')
|
||||
__text = __text.replace('&&', 'and')
|
||||
e = eval(__text, {'__clog2': clog2})
|
||||
return e
|
||||
except (NameError, SyntaxError):
|
||||
return text
|
||||
|
||||
def gen_vl_header(file, modules, taps):
|
||||
|
||||
header = '''
|
||||
`ifndef VX_SCOPE_DEFS
|
||||
`define VX_SCOPE_DEFS
|
||||
'''
|
||||
footer = '`endif'
|
||||
|
||||
def signal_size(size, mn):
|
||||
if type(size) == int:
|
||||
if (size != mn):
|
||||
return "[" + str(size-1) + ":0]"
|
||||
else:
|
||||
return ""
|
||||
else:
|
||||
return "[" + size + "-1:0]"
|
||||
|
||||
def create_signal(key, ports):
|
||||
if not key in ports:
|
||||
ports[key] = []
|
||||
return ports[key]
|
||||
|
||||
def dic_insert(gdic, ldic, key, value, enabled):
|
||||
if enabled:
|
||||
ldic[key] = value
|
||||
if key in gdic:
|
||||
return False
|
||||
if enabled:
|
||||
gdic[key] = None
|
||||
return True
|
||||
|
||||
def trigger_name(name, size):
|
||||
if type(size) == int:
|
||||
if size != 1:
|
||||
return "(| " + name + ")"
|
||||
else:
|
||||
return name
|
||||
else:
|
||||
return "(| " + name + ")"
|
||||
|
||||
def trigger_subscripts(asize):
|
||||
def Q(arr, ss, asize, idx, N):
|
||||
a = asize[idx]
|
||||
if (a != 0):
|
||||
for i in range(a):
|
||||
tmp = ss + '[' + str(i) + ']'
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, asize, idx + 1, N)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
else:
|
||||
if (idx + 1) < N:
|
||||
Q(arr, ss, asize, idx + 1, N)
|
||||
else:
|
||||
arr.append(ss)
|
||||
|
||||
if asize is None:
|
||||
return [""]
|
||||
ln = len(asize)
|
||||
if (0 == ln):
|
||||
return [""]
|
||||
arr = []
|
||||
Q(arr, "", asize, 0, ln)
|
||||
return arr
|
||||
|
||||
|
||||
def visit_path(alltaps, ports, ntype, paths, modules, taps):
|
||||
curtaps = {}
|
||||
|
||||
if (len(paths) != 0):
|
||||
spath = paths.pop(0)
|
||||
snodes = modules[ntype]["submodules"]
|
||||
if not spath in snodes:
|
||||
raise Exception("invalid path: " + spath + " in " + ntype)
|
||||
|
||||
snode = snodes[spath]
|
||||
|
||||
stype = snode["type"]
|
||||
|
||||
enabled = True
|
||||
if "enabled" in snode:
|
||||
enabled = eval_node(snode["enabled"], None)
|
||||
|
||||
subtaps = visit_path(alltaps, ports, stype, paths, modules, taps)
|
||||
|
||||
scount = 0
|
||||
if "count" in snode:
|
||||
scount = eval_node(snode["count"], None)
|
||||
|
||||
params = None
|
||||
if "params" in snode:
|
||||
params = snode["params"]
|
||||
|
||||
new_staps = []
|
||||
|
||||
nn = "SCOPE_IO_" + ntype
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for key in subtaps:
|
||||
subtap = subtaps[key]
|
||||
s = subtap[0]
|
||||
a = subtap[1]
|
||||
t = subtap[2]
|
||||
|
||||
aa = [scount]
|
||||
sa = signal_size(scount, 0)
|
||||
if a:
|
||||
for i in a:
|
||||
x = eval_node(i, params)
|
||||
aa.append(x)
|
||||
sa += signal_size(x, 0)
|
||||
|
||||
if dic_insert(alltaps, curtaps, spath + '/' + key, (s, aa, t), enabled):
|
||||
skey = key.replace('/', '_')
|
||||
if enabled:
|
||||
pp.append("\toutput wire" + sa + signal_size(s, 1) + " scope_" + spath + '_' + skey + ',')
|
||||
new_staps.append(skey)
|
||||
|
||||
ports[nn] = pp
|
||||
|
||||
if (0 == scount):
|
||||
nn = "SCOPE_BIND_" + ntype + '_' + spath
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for st in new_staps:
|
||||
if enabled:
|
||||
pp.append("\t.scope_" + st + "(scope_" + spath + '_' + st + "),")
|
||||
else:
|
||||
pp.append("\t`UNUSED_PIN (scope_" + st + "),")
|
||||
|
||||
ports[nn] = pp
|
||||
else:
|
||||
nn = "SCOPE_BIND_" + ntype + '_' + spath + "(__i__)"
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for st in new_staps:
|
||||
if enabled:
|
||||
pp.append("\t.scope_" + st + "(scope_" + spath + '_' + st + "[__i__]),")
|
||||
else:
|
||||
pp.append("\t`UNUSED_PIN (scope_" + st + "),")
|
||||
|
||||
ports[nn] = pp
|
||||
else:
|
||||
nn = "SCOPE_IO_" + ntype
|
||||
pp = create_signal(nn, ports)
|
||||
|
||||
for tk in taps:
|
||||
trigger = 0
|
||||
name = tk
|
||||
size = eval_node(taps[tk], None)
|
||||
if name[0] == '!':
|
||||
name = name[1:]
|
||||
trigger = 1
|
||||
elif name[0] == '?':
|
||||
name = name[1:]
|
||||
trigger = 2
|
||||
if dic_insert(alltaps, curtaps, name, (size, None, trigger), True):
|
||||
pp.append("\toutput wire" + signal_size(size, 1) + " scope_" + name + ',')
|
||||
|
||||
ports[nn] = pp
|
||||
|
||||
return curtaps
|
||||
|
||||
toptaps = {}
|
||||
|
||||
with open(file, 'w') as f:
|
||||
|
||||
ports = {}
|
||||
alltaps = {}
|
||||
|
||||
for key in taps:
|
||||
skey_list = key.split(',')
|
||||
_taps = taps[key]
|
||||
for skey in skey_list:
|
||||
#print('*** processing node: ' + skey + ' ...')
|
||||
paths = skey.strip().split('/')
|
||||
ntype = paths.pop(0)
|
||||
curtaps = visit_path(alltaps, ports, ntype, paths, modules, _taps)
|
||||
for tk in curtaps:
|
||||
toptaps[tk] = curtaps[tk]
|
||||
|
||||
print(header, file=f)
|
||||
|
||||
for key in ports:
|
||||
print("`define " + key + ' \\', file=f)
|
||||
for port in ports[key]:
|
||||
print(port + ' \\', file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_DECL_SIGNALS \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
name = key.replace('/', '_')
|
||||
size = tap[0]
|
||||
asize = tap[1]
|
||||
sa = ""
|
||||
if asize:
|
||||
for a in asize:
|
||||
sa += signal_size(a, 0)
|
||||
if i > 0:
|
||||
print(" \\", file=f)
|
||||
print('\t wire' + sa + signal_size(size, 1) + " scope_" + name + ';', file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_DATA_LIST \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
trigger = tap[2]
|
||||
if trigger != 0:
|
||||
continue
|
||||
name = key.replace('/', '_')
|
||||
if i > 0:
|
||||
print(", \\", file=f)
|
||||
print("\t scope_" + name, file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_UPDATE_LIST \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
trigger = tap[2]
|
||||
if trigger == 0:
|
||||
continue
|
||||
name = key.replace('/', '_')
|
||||
if i > 0:
|
||||
print(", \\", file=f)
|
||||
print("\t scope_" + name, file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print("`define SCOPE_TRIGGER \\", file=f)
|
||||
i = 0
|
||||
for key in toptaps:
|
||||
tap = toptaps[key]
|
||||
if tap[2] != 2:
|
||||
continue
|
||||
size = tap[0]
|
||||
asize = tap[1]
|
||||
sus = trigger_subscripts(asize)
|
||||
for su in sus:
|
||||
if i > 0:
|
||||
print(" | \\", file=f)
|
||||
print("\t(", file=f, end='')
|
||||
name = trigger_name("scope_" + key.replace('/', '_') + su, size)
|
||||
print(name, file=f, end='')
|
||||
print(")", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("", file=f)
|
||||
|
||||
print(footer, file=f)
|
||||
|
||||
return toptaps
|
||||
|
||||
def gen_cc_header(file, taps):
|
||||
|
||||
header = '''
|
||||
#pragma once
|
||||
|
||||
struct scope_module_t {
|
||||
const char* name;
|
||||
int index;
|
||||
int parent;
|
||||
};
|
||||
|
||||
struct scope_tap_t {
|
||||
int width;
|
||||
const char* name;
|
||||
int module;
|
||||
};
|
||||
'''
|
||||
def flatten_path(paths, sizes):
|
||||
def Q(arr, ss, idx, N, paths, sizes):
|
||||
size = sizes[idx]
|
||||
if size != 0:
|
||||
for i in range(sizes[idx]):
|
||||
tmp = ss + ('/' if (ss != '') else '')
|
||||
tmp += paths[idx] + '_' + str(i)
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, idx + 1, N, paths, sizes)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
else:
|
||||
tmp = ss + ('/' if (ss != '') else '')
|
||||
tmp += paths[idx]
|
||||
if (idx + 1) < N:
|
||||
Q(arr, tmp, idx + 1, N, paths, sizes)
|
||||
else:
|
||||
arr.append(tmp)
|
||||
|
||||
arr = []
|
||||
Q(arr, "", 0, len(asize), paths, asize)
|
||||
return arr
|
||||
|
||||
# flatten the taps
|
||||
fdic = {}
|
||||
for key in taps:
|
||||
tap = taps[key]
|
||||
size = str(tap[0])
|
||||
trigger = tap[2]
|
||||
if (trigger != 0):
|
||||
continue
|
||||
paths = key.split('/')
|
||||
if (len(paths) > 1):
|
||||
name = paths.pop(-1)
|
||||
asize = tap[1]
|
||||
for ss in flatten_path(paths, asize):
|
||||
fdic[ss + '/' + name ] = [size, 0]
|
||||
else:
|
||||
fdic[key] = [size, 0]
|
||||
for key in taps:
|
||||
tap = taps[key]
|
||||
size = str(tap[0])
|
||||
trigger = tap[2]
|
||||
if (trigger == 0):
|
||||
continue
|
||||
paths = key.split('/')
|
||||
if (len(paths) > 1):
|
||||
name = paths.pop(-1)
|
||||
asize = tap[1]
|
||||
for ss in flatten_path(paths, asize):
|
||||
fdic[ss + '/' + name ] = [size, 0]
|
||||
else:
|
||||
fdic[key] = [size, 0]
|
||||
|
||||
# generate module dic
|
||||
mdic = {}
|
||||
mdic["*"] = ("*", 0, -1)
|
||||
for key in fdic:
|
||||
paths = key.split('/')
|
||||
if len(paths) == 1:
|
||||
continue
|
||||
paths.pop(-1)
|
||||
parent = 0
|
||||
mk = ""
|
||||
for path in paths:
|
||||
mk += '/' + path
|
||||
if not mk in mdic:
|
||||
index = len(mdic)
|
||||
mdic[mk] = (path, index, parent)
|
||||
parent = index
|
||||
else:
|
||||
parent = mdic[mk][1]
|
||||
fdic[key][1] = parent
|
||||
|
||||
with open(file, 'w') as f:
|
||||
print(header, file=f)
|
||||
|
||||
print("static constexpr scope_module_t scope_modules[] = {", file=f)
|
||||
i = 0
|
||||
for key in mdic:
|
||||
m = mdic[key]
|
||||
if i > 0:
|
||||
print(',', file=f)
|
||||
print("\t{\"" + m[0] + "\", " + str(m[1]) + ", " + str(m[2]) + "}", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("};", file=f)
|
||||
|
||||
print("", file=f)
|
||||
print("static constexpr scope_tap_t scope_taps[] = {", file=f)
|
||||
i = 0
|
||||
for key in fdic:
|
||||
size = fdic[key][0]
|
||||
parent = fdic[key][1]
|
||||
paths = key.split('/')
|
||||
if len(paths) > 1:
|
||||
name = paths.pop(-1)
|
||||
else:
|
||||
name = key
|
||||
if i > 0:
|
||||
print(',', file=f)
|
||||
print("\t{" + size + ", \"" + name + "\", " + str(parent) + "}", file=f, end='')
|
||||
i += 1
|
||||
print("", file=f)
|
||||
print("};", file=f)
|
||||
return {"version":"0.1.0", "taps":taps}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Scope headers generator.')
|
||||
parser.add_argument('-vl', nargs='?', default='scope-defs.vh', metavar='file', help='Output Verilog header')
|
||||
parser.add_argument('-cc', nargs='?', default='scope-defs.h', metavar='file', help='Output C++ header')
|
||||
parser.add_argument('-D', nargs='?', action='append', metavar='macro[=value]', help='define macro')
|
||||
parser.add_argument('-I', nargs='?', action='append', metavar='<includedir>', help='include directory')
|
||||
parser.add_argument('config', help='Json config file')
|
||||
parser.add_argument('-o', nargs='?', default='scope.json', metavar='o', help='Output JSON manifest')
|
||||
parser.add_argument('-n', nargs='?', default=-1, metavar='n', type=int, help='Maximum number of taps to read')
|
||||
parser.add_argument('xml', help='Design XML descriptor file')
|
||||
args = parser.parse_args()
|
||||
print("args=", args)
|
||||
|
||||
global exclude_files
|
||||
global include_dirs
|
||||
global macros
|
||||
global br_stack
|
||||
|
||||
if args.I:
|
||||
for dir in args.I:
|
||||
load_include_path(dir)
|
||||
|
||||
if args.D:
|
||||
load_defines(args.D)
|
||||
|
||||
config = load_config(args.config)
|
||||
|
||||
exclude_files.append(os.path.basename(args.vl))
|
||||
|
||||
if "include_paths" in config:
|
||||
for path in config["include_paths"]:
|
||||
load_include_path(path)
|
||||
|
||||
if "includes" in config:
|
||||
parse_includes(config["includes"])
|
||||
|
||||
taps = gen_vl_header(args.vl, config["modules"], config["taps"])
|
||||
gen_cc_header(args.cc, taps)
|
||||
#print("args=", args)
|
||||
scope_taps = parse_xml(args.xml, args.n)
|
||||
with open(args.o, "w") as f:
|
||||
json.dump(scope_taps, f, ensure_ascii=False, indent=4)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user