diff --git a/doc/args_class_diagram.pdf b/doc/args_class_diagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4d2515387c209b2793e91fc11286ce33b965757f Binary files /dev/null and b/doc/args_class_diagram.pdf differ diff --git a/gen_bus.py b/gen_bus.py index 1e0b8143b708bc13a64c664dbff4e1083cadd48a..1a01c0e9aa5ea3dbe88459fe8afa994cfef9006a 100755 --- a/gen_bus.py +++ b/gen_bus.py @@ -12,12 +12,13 @@ import os import logging -import common as cm +#import common as cm import collections from py_args_lib import * from fpga import FPGA import numpy as np from gen_slave import tab_aligned + logger = logging.getLogger('main.gen_bus') @@ -32,7 +33,7 @@ class Bus(object): def __init__(self, fpga): self.fpga = fpga self.root_dir = os.path.expandvars('$ARGS_GEAR') - self.out_dir = os.path.expandvars('$ARGS_BUILD_DIR/{}/hdl'.format(self.fpga.fpga_name)) + self.out_dir = check_outdir(self.fpga.board_name, self.fpga.fpga_name, 'hdl') self.tmpl_mstr_port_full = os.path.join(self.root_dir, 'templates/template_bus_master_axi_full.vho') self.tmpl_mstr_port_lite = os.path.join(self.root_dir, 'templates/template_bus_master_axi_lite.vho') self.tmpl_mstr_port_cast = os.path.join(self.root_dir, 'templates/template_bus_master_port_casting.vho') @@ -92,11 +93,13 @@ class Bus(object): i = -1 # for i, slave_attr in enumerate(self.fpga.address_map.values()): for slave_attr in self.fpga.address_map.values(): + logger.debug('slave_attr=%s', str(slave_attr)) if slave_attr['port_index'] == last_port and slave_attr['type'] == last_prot: continue # skip port indexes already dealt with i = i+1 last_port = slave_attr['port_index'] last_prot = slave_attr['type'] + logger.debug('indexes=%s', str(self.indexes)) if np.mod(i, 15) == 0 and self.indexes[i][1] == 0 and i != 0: lines.append(get_cmd(input[line_num+4])) # daisy chain interconnects lines.append(get_cmd(input[line_num+5]).format(self.indexes[i][0]-1, self.indexes[i][0])) @@ -122,9 +125,9 @@ class Bus(object): last_prot = slave_attr['type'] if isinstance(slave_attr['slave'], Register): if not getattr(slave_attr['slave'], 'isIP', False): - span = cm.ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096)) + span = ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096)) else : - span = cm.ceil_pow2(max(slave_attr['slave'].address_length(), 4096)) + span = ceil_pow2(max(slave_attr['slave'].address_length(), 4096)) else : span = slave_attr['span'] _line = get_cmd(line).replace('<range>', '4') # 4k is minimum settable size for vivado @@ -153,9 +156,9 @@ class Bus(object): last_prot = slave_attr['type'] if isinstance(slave_attr['slave'], Register): if not getattr(slave_attr['slave'], 'isIP', False): - span = cm.ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096)) + span = ceil_pow2(max(slave_attr['peripheral'].reg_len, 4096)) else : - span = cm.ceil_pow2(max(slave_attr['slave'].address_length(), 4096)) + span = ceil_pow2(max(slave_attr['slave'].address_length(), 4096)) else : span = slave_attr['span'] lines.append('# interconnect[{}] <{}> base: 0x{:08x} span: 0x{:06x}\n'.format( @@ -298,6 +301,8 @@ class Bus(object): """ Calculate interconnects and local slave port numbers for all slaves """ + logger.debug("enter calc_indexes()") + logger.debug("nof_slaves=%d", self.nof_slaves) index_list = [] for i in range(self.nof_slaves): if np.floor(i/15) < self.nof_interconnects: @@ -312,5 +317,6 @@ class Bus(object): i, interconnect_num, local_slave_num, self.nof_interconnects)) index_list.append((interconnect_num, local_slave_num)) + return index_list diff --git a/gen_fpgamap_py.py b/gen_fpgamap_py.py index 218fa6c83a0fb5cd0deefc220fedddf8c85cdc18..bcf84ac016b804e161106b388560629bbc625bdd 100755 --- a/gen_fpgamap_py.py +++ b/gen_fpgamap_py.py @@ -38,7 +38,7 @@ import pprint # import code -def genPython(fpga, fpgaName, readable): +def genPython(fpga, fpgaName, addr_size=4, readable=False): slavePorts = {} print("Including slave ports for {}:".format(fpgaName)) for slavePortName, slavePortInfo in fpga.address_map.items(): @@ -46,11 +46,11 @@ def genPython(fpga, fpgaName, readable): continue peripheral = slavePortInfo['peripheral'] slave = slavePortInfo['slave'] - base = int(slavePortInfo['base']/addr_size) # Convert from AXI byte address to register address + base = int(slavePortInfo['base'] / addr_size) # Convert from AXI byte address to register address if peripheral.name() not in slavePorts: - slavePorts[peripheral.name()] = {'slaves': {}, 'start': base, 'span': int(slavePortInfo['span']/4), 'count': peripheral.number_of_peripherals()} + slavePorts[peripheral.name()] = {'slaves': {}, 'start': base, 'span': int(slavePortInfo['span'] / addr_size), 'count': peripheral.number_of_peripherals()} else: - slavePorts[peripheral.name()]['span'] += int(slavePortInfo['span']/addr_size) + slavePorts[peripheral.name()]['span'] += int(slavePortInfo['span'] / addr_size) slaves = slavePorts[peripheral.name()]['slaves'] slaveOffset = base - slavePorts[peripheral.name()]['start'] if isinstance(slave, RAM): @@ -197,4 +197,4 @@ if __name__ == '__main__': fpga_lib = FPGALibrary(root_dir=libRootDir, use_avalon_base_addr=useAvalon) fpga = fpga_lib.get_fpga(fpgaName) - genPython(fpga, fpgaName, readable) + genPython(fpga, fpgaName, addr_size, readable) diff --git a/gen_hdl.py b/gen_hdl.py new file mode 100755 index 0000000000000000000000000000000000000000..bc063c9a0b7afdfa644e8d186062133cdb8339ad --- /dev/null +++ b/gen_hdl.py @@ -0,0 +1,193 @@ +#! /usr/bin/env python3 + +############################################################################### +# +# Copyright (C) 2016 +# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Author Date +# PD apr 2020 Original +# +############################################################################### + +import os +import sys +import traceback +import argparse +import subprocess + +from args_logger import MyLogger + +from py_args_lib import * + +import gen_slave +import gen_bus +import gen_doc +import gen_fpgamap_py + + +def main(): + + # root directory to find hdl.cfg and yaml files + lib_root_dir = os.path.expandvars('$ARGS_WORK') + + + # Find and parse all *.fpga.yaml YAML files under root directory for RADIOHDL + # For each FPGA file it extracts a list of the peripherals that are used by that FPGA + # Here we select the FPGA YAML that matches up with the supplied fpga command line argument + addr_size = 1 if use_avalon is True else 4 + + fpga_lib = FPGALibrary(root_dir=lib_root_dir, use_avalon_base_addr=use_avalon) + fpga = fpga_lib.get_fpga(fpga_name) + + # create the summary file in the output directory + out_dir = check_outdir(fpga.board_name.replace('uniboard', 'unb'), fpga_name, 'hdl') + + hdl_gen = HDLGen(out_dir, fpga, addr_size) + + +class HDLGen(object): + """ + """ + def __init__(self, outdir, fpga, addr_size): + self.outdir = outdir + self.fpga = fpga + self.addr_size = addr_size + self.hdl_lib_name = self.fpga.hdl_library_name + self.fpga_name = self.fpga.fpga_name + self.board_name = self.fpga.board_name + + self.args_generated = [] + + self.handle_args_lib_references(use_name=self.fpga_name, is_fpga=True) + + # for lib_name in self.fpga.peripherals: + # # library of peripherals that should have names unique from self.periph_lib_names + # # generate files and append to lib dict's synth_files and vivado_tcl_files + # self.handle_args_lib_references(use_name=lib_name) + + def handle_args_lib_references(self, use_name, is_fpga=False): + """ + """ + logger.debug('handle_args_lib_reference use_name = %s', str(use_name)) + use_name_keep = use_name + use_name_split = use_name.split('/') + lib_name = use_name_split[0] + periph_name = None if len(use_name_split) == 1 else use_name_split[1] + args_lib_name = None if periph_name is None else '_'.join(use_name_split) # if peripheral is specified in fpga.yaml + + self.gen_args_lib_dict(lib_name, periph_name, args_lib_name, is_fpga) + + return + + def gen_args_lib_dict(self, args_lib_name, periph_select=None, lib_name_custom=None, is_fpga=False): # to support peripheral.py and fpga.py + + final_name = args_lib_name if lib_name_custom is None else lib_name_custom + if (final_name in self.args_generated or lib_name_custom is not None) and is_fpga is False : + logger.warning("ARGS has already generated firmware and documentation for the library %s, will not repeat.", + args_lib_name) # have to tolerate when part of fpga + return None + logger.debug("gen_args_lib_dict hdl_lib_dict '%s' args_lib_name '%s' lib_name_custom '%s'", + final_name, args_lib_name, lib_name_custom) + + peripherals = self.fpga.peripherals if is_fpga else self.periph_libs[args_lib_name]['peripherals'] + + if is_fpga: + fpga_bus = gen_bus.Bus(self.fpga) + gen_fpgamap_py.genPython(fpga=self.fpga, fpgaName=self.fpga_name, addr_size=self.addr_size, readable=True) + fpga_bus.gen_firmware() + out_dir = check_outdir(self.board_name, self.fpga_name, 'hdl') + else: + out_dir = check_outdir(self.board_name, self.fpga_name, 'hdl') + + for component_name, peripheral in peripherals.items(): + # if periph_select != 'None' and periph['peripheral_name'] != periph_select: + # logger.warning("peripheral name: {}".format(component_name)) + if periph_select is not None and component_name != periph_select: + continue + if peripheral.evaluated is False: + peripheral.eval_peripheral() + periph_slave = gen_slave.Slave(peripheral, self.hdl_lib_name, args_lib_name if is_fpga else None) + periph_slave.generate_regs(out_dir) + for key in peripheral.rams: + periph_slave.generate_mem(out_dir, peripheral.rams[key], 'ram') + for key in peripheral.fifos: + periph_slave.generate_mem(out_dir, peripheral.fifos[key], 'fifo') + + self.args_gen_doc(final_name, is_fpga) + if periph_select is None: + self.args_generated.append(final_name) + + return + + def args_gen_doc(self, lib_name, is_fpga): # , output_files): + #devnull = open(os.devnull, 'w') + try: + subprocess.call("pdflatex -version", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True) + except FileNotFoundError: + logger.warning("No latex distribution detected, skipping ARGS documentation generation") + return + + logger.debug('Generating PDF documentation for library %s...', lib_name) + if is_fpga: + doc = gen_doc.FPGADocumentation(lib_name, self.fpga) + doc.fill() + doc.make_pdf() + del doc + else: + doc = gen_doc.PeripheralDocumentation(lib_name, self.periph_libs[lib_name]) + doc.fill() + doc.make_pdf() + del doc + return + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description='ARGS tool script to generate hdl code') + parser.add_argument('-f', '--fpga', required=True, help='ARGS fpga_name, example="unb2b_minimal"') + parser.add_argument('-a', '--avalon', action='store_true', default=False, help='use qsys/sopc config file for base addresses') + parser.add_argument('-v', '--verbosity', default='INFO', help='stdout log level, can be [ERROR | WARNING | INFO | DEBUG]') + args = parser.parse_args() + + fpga_name = args.fpga + use_avalon = args.avalon + + if use_avalon: + sys.exit("-a, --avalon option is not working in this AXI version of software, it is for future development") + + # setup first log system before importing other user libraries + program_name = __file__.split('/')[-1].split('.')[0] + out_dir = os.path.join(os.getenv('ARGS_GEAR'), 'log') + my_logger = MyLogger(log_path=out_dir, file_name=program_name) + my_logger.set_file_log_level('DEBUG') + my_logger.set_stdout_log_level(args.verbosity) + + logger = my_logger.get_logger() + logger.debug("Used arguments: {}".format(args)) + + try: + main() + except (ARGSNameError, ARGSModeError, ARGSYamlError): + handle_args_error(sys.exc_info()[0]) + except: + logger.error('Program fault, reporting and cleanup') + logger.error('Caught %s', str(sys.exc_info()[0])) + logger.error(str(sys.exc_info()[1])) + logger.error('TRACEBACK:\n%s', traceback.format_exc()) + logger.error('Aborting NOW') + sys.exit("ERROR") diff --git a/gen_slave.py b/gen_slave.py index 8eb32e786bc74f83952f3e50e9d017292c446cd3..f0fedc0bd46e9b9b5a4663ad19cf30fe593c0883 100755 --- a/gen_slave.py +++ b/gen_slave.py @@ -7,7 +7,6 @@ import subprocess import traceback import shutil import numpy as np -from common import ceil_log2, ceil_pow2 from constants import * from peripheral_lib import * import peripheral @@ -26,7 +25,7 @@ logger = logging.getLogger('main.gen_slave') def word_wise(byte_address): - return int(byte_address/WIDTH_IN_BYTES) + return max(1, int(byte_address / WIDTH_IN_BYTES)) def tab_aligned(input_strings): @@ -74,19 +73,21 @@ def vector_len(width): class Slave(object): """Generate VHDL and IP source for memory mapped slaves""" - def __init__(self, peripheral, fpga_name=None): - self.rootDir = os.path.expandvars('$RADIOHDL/tools/args') # ('$RADIOHDL/tools/prestudy/YAML') + def __init__(self, peripheral, hdl_lib_name, fpga_name=None): + self.tmpl_dir = os.path.join(os.getenv('ARGS_GEAR'), 'templates') + self.out_dir = None self.nof_dat = 0 self.replaceDict = {} self.periph_name = peripheral.name() - self.periph_lib = peripheral.lib + self.periph_lib = hdl_lib_name self.slaves = peripheral.slaves - self.prefix = ((peripheral.lib + '_') if peripheral.lib != peripheral.name() else '') + peripheral.name() + self.prefix = ((hdl_lib_name + '_') if hdl_lib_name != peripheral.name() else '') + peripheral.name() self.output_files = [] self.fpga_name = fpga_name - def generate_mem(self, settings, slave_type): + def generate_mem(self, outdir, settings, slave_type): """ Generate a VHDL instantiation file and a TCL file to create and customise the IP """ + self.out_dir = outdir lines = [] # fix memory to fit Xilinx IP width/depth combinations # self.xilinxConstraints() @@ -94,10 +95,9 @@ class Slave(object): self.gen_file(settings, slave_type, 'vhd') self.gen_file(settings, slave_type, 'tcl') - def generate_regs(self, peripheral): + def generate_regs(self, outdir): """ Generate a set of entity, pkg and instantiation files for all reg slaves """ - self.periph_name = peripheral.name() - self.periph_lib = peripheral.lib + self.out_dir = outdir self.nof_dat = 0 for slave in self.slaves: if not isinstance(slave, Register) or (isinstance(slave, Register) and getattr(slave, 'isIP', False)): @@ -113,23 +113,13 @@ class Slave(object): self.gen_file(None, 'reg', 'vho') def write_file(self, lines, file_name): - out_dir = os.path.expandvars('$HDL_BUILD_DIR/ARGS') - if self.fpga_name is not None: - out_dir = os.path.join(out_dir, self.fpga_name) - try: - os.stat(out_dir) - except: - os.mkdir(out_dir) - out_dir = os.path.join(out_dir, self.periph_lib) - try: - os.stat(out_dir) - except: - os.mkdir(out_dir) - out_dir = os.path.join(out_dir, self.periph_name) + out_dir = self.out_dir + out_dir = os.path.join(out_dir, self.periph_lib, self.periph_name) try: - os.stat(out_dir) - except: - os.mkdir(out_dir) + os.stat(out_dir) # Check that the output directory exists + except FileNotFoundError: + os.makedirs(out_dir) # if not make it + file_name = os.path.join(out_dir, file_name) with open(file_name, 'w') as outFile: for line in lines: @@ -141,9 +131,7 @@ class Slave(object): return self.output_files def gen_pkg(self): - tmplFile = os.path.join(self.rootDir, "templates/template_reg_pkg.vhd") - # outDir = os.path.join(self.rootDir, 'outputs') - # pkgFile = os.path.join(outDir, self.periph_lib + '_' + self.periph_name + "_reg_pkg.vhd") + tmplFile = os.path.join(self.tmpl_dir, "template_reg_pkg.vhd") file_name = self.prefix + "_reg_pkg.vhd" lines = [] # fields_dict = regGroup.fields @@ -189,7 +177,7 @@ class Slave(object): def gen_vho(self, slaveSettings, slave_type): lines = [] - tmplFile = os.path.join(self.rootDir, "templates/template_reg_axi4.vho") + tmplFile = os.path.join(self.tmpl_dir, "template_reg_axi4.vho") # fields_dict = slaveSettings.fields with open(tmplFile, 'r') as infile: for line in infile: @@ -222,7 +210,7 @@ class Slave(object): def gen_vhdl(self, slaveSettings, slave_type): lines = [] slave_type = slave_type.lower() - tmplFile = os.path.join(self.rootDir, "templates/template_" + slave_type + "_axi4.vhd") + tmplFile = os.path.join(self.tmpl_dir, "template_{}_axi4.vhd".format(slave_type)) removePort = {} replace_dicts = {} if slave_type == 'reg': @@ -319,10 +307,10 @@ class Slave(object): lines.extend(self.instantiate_rams()) sublines = [] if slave.get_kv("dual_clock"): - tmpl_file = os.path.join(self.rootDir, 'templates/template_common_reg_r_w_dc.vho') + tmpl_file = os.path.join(self.tmpl_dir, 'template_common_reg_r_w_dc.vho') # Need to add support for peripherals with different kinds of dual clock slaves else: - tmpl_file = os.path.join(self.rootDir, 'templates/template_common_reg_r_w.vho') + tmpl_file = os.path.join(self.tmpl_dir, 'template_common_reg_r_w.vho') with open(tmpl_file, 'r') as inst_file: sublines = [(subline.replace('<reg_name>', regGroup.name())) for subline in inst_file] @@ -351,7 +339,7 @@ class Slave(object): def gen_tcl(self, settings, slave_type): lines = [] - tmplFile = os.path.join(self.rootDir, "templates/template_" + slave_type + "_axi4.tcl") + tmplFile = os.path.join(self.tmpl_dir, "template_{}_axi4.tcl".format(slave_type)) # replaceDict = {'<entity>': settings.name, '<FW>' : settings.access_mode() == 'FW', '<init_sl>' : settings.default(), '<bDefault>':settings.default() is not None, # '<dat_w>' : settings.width(), '<FR>' : settings.access_mode() == 'FR', '<nof_dat>' : settings.address_length(), '<FTHRESHOLD>' : settings.address_length() - 5, '<ETHRESHOLD>' : 2 } default = settings.reset_value() if settings.reset_value() is not None else 0 @@ -412,7 +400,7 @@ class Slave(object): def set_clr_mask(self, regGroup): field_list = regGroup.fields - temp_vector = [0]*word_wise(regGroup.address_length())*self.dat_w + temp_vector = [0]*(word_wise(regGroup.address_length())*self.dat_w) bMask = 0 padding = '' for field in field_list: @@ -420,6 +408,7 @@ class Slave(object): bMask = 1 lowestBit = None if word_wise(field.address_offset())*self.dat_w+field.bit_offset() == 0 else word_wise(field.address_offset())*self.dat_w+field.bit_offset() - 1 temp_vector[word_wise(field.address_offset())*self.dat_w+field.bit_offset() + field.width()-1 :lowestBit:-1] = [1]*field.width() # list(format(field.default(), '0' + str(field.width()) + 'b')) + temp_string = ''.join(map(str, temp_vector[::-1])) hexValue = hex(int(temp_string, 2))[2:] if len(hexValue) < int(word_wise(regGroup.address_length())*self.dat_w/4): @@ -687,7 +676,7 @@ class Slave(object): sublines.append(('\t<>_adr <= wr_adr(c_mm_<>_ram.adr_w-1 downto 0) WHEN <>_wr_en ' + ('= \'1\'' if self.regGroup.number_of_slaves() == 1 else '/= (<>_wr_en\'range => \'0\')')+' ELSE\n\t\t\t\trd_adr(c_mm_<>_ram.adr_w-1 downto 0);\n\n ').replace('<>', self.regGroup.name() + '_' + ram.name())) if self.regGroup.number_of_slaves() > 1: sublines.append(('\t<>_' + ram.name() + '_gen: FOR i in 0 to c_mm_<>_reg.nof_slaves-1 GENERATE\n').replace('<>', self.regGroup.name())) - with open(os.path.join(self.rootDir, 'templates/template_common_ram_rw_rw.vho'), 'r') as inst_file: + with open(os.path.join(self.tmpl_dir, 'template_common_ram_rw_rw.vho'), 'r') as inst_file: for subline in inst_file: for tag, replace_string in {'<field_name>' : self.regGroup.name() + '_' + ram.name(), '<FIELD_NAME>': (self.regGroup.name() + '_' + ram.name()).upper(), '<reg_name>': self.regGroup.name(), '(i)' : '' if self.regGroup.number_of_slaves() == 1 else '(i)', '+ i' : '' if self.regGroup.number_of_slaves() == 1 else '+ i'}.items(): subline = subline.replace(tag, replace_string) @@ -696,7 +685,7 @@ class Slave(object): sublines.append(subline) if self.regGroup.number_of_slaves() > 1: sublines.append('\tEND GENERATE;\n\n') - with open(os.path.join(self.rootDir, 'templates/template_common_pipeline.vho'), 'r') as inst_file: + with open(os.path.join(self.tmpl_dir, 'template_common_pipeline.vho'), 'r') as inst_file: # sublines.extend(subline.replace('<field_name>', regGroup.name() + '_' + ram.name()) for subline in inst_file) for subline in inst_file: if '<nof_slaves>' in subline: @@ -711,7 +700,7 @@ class Slave(object): def c_mm_reg(self, replace_dicts): lines = [] - tmpl_file = os.path.join(self.rootDir, 'templates/template_c_mm_reg.vho') + tmpl_file = os.path.join(self.tmpl_dir, 'template_c_mm_reg.vho') for ram in self.regGroup.rams: sublines = [] with open(tmpl_file, 'r') as inst_file: diff --git a/py_args_lib/fpga.py b/py_args_lib/fpga.py index 16247afbbdffba6179bb6aca00e306a54ae07eeb..3637587fd0cf12975008f725d5ac7ff9070e0eb6 100644 --- a/py_args_lib/fpga.py +++ b/py_args_lib/fpga.py @@ -52,7 +52,8 @@ class FPGA(object): self.use_avalon_base_addr = False if use_avalon_base_addr is None else use_avalon_base_addr - self.fpga_name = "" + self.hdl_library_name = "" + self.fpga_name = "" self.fpga_description = "" self.parameters = {} # self.peripherals = {} @@ -97,6 +98,7 @@ class FPGA(object): logger.debug("Creating FPGA") logger.debug("Instantiating the peripherals from the peripheral Library") # self.fpga_name = self.fpga_config['hdl_library_name'] + self.hdl_library_name = self.fpga_config['hdl_library_name'] self.fpga_name = self.fpga_config['fpga_name'] if "fpga_description" in self.fpga_config: @@ -513,12 +515,10 @@ class FPGALibrary(object): if root_dir is not None: for root, dirs, files in os.walk(self.root_dir, topdown=True): if 'tools' in root: - # skip tools dir - continue + continue # skip tools dir for name in files: if not name.endswith(self.file_extension): - # skip, no matching file extension - continue + continue # skip, no matching file extension try : # try to read yaml file library_config = yaml.load(open(os.path.join(root, name), 'r')) @@ -530,7 +530,7 @@ class FPGALibrary(object): #logger.error('ERROR:\n' + sys.exc_info()[1]) raise ARGSYamlError continue - #sys.exit() + if not isinstance(library_config, dict): logger.warning('File %s is not readable as a dictionary, it will not be' diff --git a/py_args_lib/peripheral.py b/py_args_lib/peripheral.py index cc411fa9908cd28107d5fc9e9d38eb9f34645f54..e99ea51eae029e496d9bfd533dc8adc501f0c360 100644 --- a/py_args_lib/peripheral.py +++ b/py_args_lib/peripheral.py @@ -57,7 +57,7 @@ class Peripheral(BaseObject): The Peripheral evaluates the nof_inst and parameters to set the dimensions of the MM slaves. """ - def __init__(self, hdl_library_name, library_config): + def __init__(self, library_config): super().__init__() self._config = {} # read config from file self._parameters = {} # all used parameters @@ -66,7 +66,6 @@ class Peripheral(BaseObject): self.fifos = {} # all used fifos self.slaves = [] self._component_name = library_config['peripheral_name'] - self.hdl_library_name = hdl_library_name self.name(self._component_name) self._valid_keys = ['number_of_peripherals'] self._args.update({'number_of_peripherals' : DEFAULT_NUMBER_OF_PERIPHERALS}) @@ -190,7 +189,7 @@ class Peripheral(BaseObject): slave_ports = deepcopy(self._config['slave_ports']) if not isinstance(slave_ports, list): - logger.error("slave_ports not a list in %s.peripheral.yaml", self.hdl_library_name) + logger.error("slave_ports not a list in *.peripheral.yaml") sys.exit() for slave_nr, slave_info in enumerate(slave_ports): @@ -198,12 +197,11 @@ class Peripheral(BaseObject): slave_name = slave_info['slave_name'] if slave_name is None: - logger.error("Peripheral '%s': 'slave_name' key missing value in %s.peripheral.yaml", self.name(), self.hdl_library_name) + logger.error("Peripheral '%s': 'slave_name' key missing value in *.peripheral.yaml", self.name()) sys.exit() i = 0 - _slave_type = slave_info.get('slave_type', '').upper() - if _slave_type in VALID_SLAVE_TYPES: + if slave_info.get('slave_type','').upper() in VALID_SLAVE_TYPES: number_of_slaves = slave_info.get('number_of_slaves', DEFAULT_NUMBER_OF_SLAVES) fields = [] @@ -232,8 +230,7 @@ class Peripheral(BaseObject): defaults = field_info['field_defaults'] if any([key.lower() not in VALID_DEFAULT_KEYS for key in defaults.keys()]) : defaults = {} - logger.error("%s.peripheral.yaml: Invalid key set in defaults for field group %s. Valid keys are %s", - self.hdl_library_name, group_label, str(VALID_DEFAULT_KEYS)) + logger.error("{}.peripheral.yaml: Invalid key set in defaults for field group {}. Valid keys are {}".format(self.lib, group_label, VALID_DEFAULT_KEYS)) sys.exit() continue @@ -244,38 +241,41 @@ class Peripheral(BaseObject): # field = Field(field_name, _slave_type) field = Field(field_name, field_info) except ARGSNameError: - logger.error("Invalid name '%s' for field in %s.peripheral.yaml", field_name, self.hdl_library_name) + logger.error("Invalid name '%s' for field in *.peripheral.yaml", field_name) sys.exit() if group_label is not None : field.group_name(group_label) + if field_name == "args_map_build": + logger.info("args_map_build = {}".format(Peripheral.__timestamp)) + field_info['reset_value'] = Peripheral.__timestamp for key, val in field_info.items(): if val is not None: if key == 'field_name': continue if key.lower() in VALID_FIELD_KEYS: # if valid attribute key, apply value to attribute eval("field.{}(val)".format(key.lower())) - # logger.debug("%s.peripheral.yaml: field %s %s is %s", - # self.hdl_library_name, field.name(), key, str(eval("field.{}()".format(key.lower())))) + # logger.debug("*.peripheral.yaml: field %s %s is %s", + # field.name(), key, str(eval("field.{}()".format(key.lower())))) else: - logger.error("Unknown key %s in %s.peripheral.yaml", key, self.hdl_library_name) + logger.error("Unknown key %s in *.peripheral.yaml", key) sys.exit() else: - logger.error("Peripheral '%s': Slave '%s': '%s' key missing value in %s.peripheral.yaml", - self.name(), slave_name, key, self.hdl_library_name) + logger.error("Peripheral '%s': Slave '%s': '%s' key missing value in *.peripheral.yaml", + self.name(), slave_name, key) sys.exit() for key, val in defaults.items(): if field_info.get(key, None) is None: # if key doesn't exist in config file, apply defaults eval("field.{}(val)".format(key.lower())) - logger.debug("%s.peripheral.yaml: Setting field %s key %s to default %s", - self.hdl_library_name, field_info['field_name'], str(key), str(val)) + logger.debug("*.peripheral.yaml: Setting field %s key %s to default %s", + field_info['field_name'], str(key), str(val)) if field.success: # fields[field_name] = deepcopy(field) fields.append(deepcopy(field)) else: - logger.error("%s.peripheral.yaml: field '%s' not succesfully added to fields", self.hdl_library_name, field_name) + logger.error("*.peripheral.yaml: field '%s' not succesfully added to fields", field_name) sys.exit() if slave_info['slave_type'].upper() in ['RAM', 'FIFO']: @@ -294,8 +294,8 @@ class Peripheral(BaseObject): self.slaves[-1].update_args({'dual_clock': slave_info['dual_clock']}) else : - logger.error("Peripheral '%s': Slave '%s': Invalid value %s for 'slave_type' key in %s.peripheral.yaml", - self.name(), slave_name, slave_info.get('slave_type', 'None'), self.hdl_library_name) + logger.error("Peripheral '%s': Slave '%s': Invalid value %s for 'slave_type' key in *.peripheral.yaml", + self.name(), slave_name, slave_info.get('slave_type', 'None')) sys.exit() if 'peripheral_description' in self._config: @@ -323,7 +323,7 @@ class Peripheral(BaseObject): _val = _val.replace(key1, str(val1)) # logger.debug("_val={}".format(_val)) if val is None: - logger.error("key set to invalid value %s in %s.peripheral.yaml", _val, self.hdl_library_name) + logger.error("key set to invalid value %s in *.peripheral.yaml", _val) sys.exit() if '.coe' in _val: @@ -333,12 +333,12 @@ class Peripheral(BaseObject): if isinstance(result, float): result = int(result) except SyntaxError: - logger.error("Key set to invalid value '%s' in %s.peripheral.yaml", _val, self.hdl_library_name) + logger.error("Key set to invalid value '%s' in *.peripheral.yaml", _val) sys.exit() except NameError: result = _val if val_type == int: - logger.error("Key set to invalid value '%s' in %s.peripheral.yaml", _val, self.hdl_library_name) + logger.error("Key set to invalid value '%s' in *.peripheral.yaml", _val) logger.error("Is parameter defined?") sys.exit() @@ -362,7 +362,7 @@ class Peripheral(BaseObject): if protocol is not None and protocol.upper() in ['LITE', 'FULL']: register.protocol = protocol.upper() # else : - # logger.error("{}.peripheral.yaml: Invalid user setting {} for slave {}".format(self.hdl_library_name, protocol, name)) + # logger.error("*.peripheral.yaml: Invalid user setting {} for slave {}".format(protocol, name)) # sys.exit() self.registers['slave_{}'.format(slave_nr)] = register self.slaves.append(register) @@ -392,10 +392,10 @@ class Peripheral(BaseObject): try : slave = eval(add_slave) except ARGSNameError: - logger.error("Invalid slave_name '%s' for %s in %s.peripheral.yaml", name, slave_type, self.hdl_library_name) + logger.error("Invalid slave_name '%s' for %s in *.peripheral.yaml", name, slave_type) sys.exit() except ARGSModeError: - logger.error("Invalid access mode for %s '%s' in %s.peripheral.yaml", slave_type, name, self.hdl_library_name) + logger.error("Invalid access mode for %s '%s' in *.peripheral.yaml", slave_type, name) sys.exit() return slave @@ -405,10 +405,10 @@ class Peripheral(BaseObject): # 'address_offset', 'reset_value', 'software_value', 'radix', 'field_description'] for field in fields: # .values(): - logger.debug("eval_fields= %s", field.name()) + logger.debug("eval field %s", field.name()) if [(field.name() == _field.name() and field.group_name() == _field.group_name())for _field in fields].count(True) > 1: - logger.error("Field name '%s' group_name '%s' is not unique within slave field list in %s.peripheral.yaml", - field.name(), field.group_name(), self.hdl_library_name) + logger.error("Field name '%s' group_name '%s' is not unique within slave field list in *.peripheral.yaml", + field.name(), field.group_name()) sys.exit() if field.group_name() is not None: field.name(field.group_name() + '_' + field.name()) @@ -559,7 +559,7 @@ class Peripheral(BaseObject): for field in fields_eval: if field.address_offset() != UNASSIGNED_ADDRESS: occupied_addresses.append(field.address_offset()) - # logger.debug("{}.peripheral.yaml: field {} has manually set address {}".format(self.hdl_library_name, field.name(), str(hex(field.address_offset())))) + # logger.debug("*.peripheral.yaml: field {} has manually set address {}".format(field.name(), str(hex(field.address_offset())))) # 2nd pass for automatic address and bit offset assignment lowest_free_addr = 0 @@ -583,8 +583,8 @@ class Peripheral(BaseObject): lowest_free_addr = lowest_free_addr + WIDTH_IN_BYTES if len(set(occupied_bits)) < len(occupied_bits) or any([bit > (DATA_WIDTH-1) or bit < 0 for bit in occupied_bits]): - logger.error('%s.peripheral.yaml: Manually assigned bits for field %s is outside of data width or contains bit collisions', - self.hdl_library_name, field.name() if field.group_name() is None else "group " + field.group_name()) + logger.error('*.peripheral.yaml: Manually assigned bits for field %s is outside of data width or contains bit collisions', + field.name() if field.group_name() is None else "group " + field.group_name()) logger.error("{}".format(str(occupied_bits))) sys.exit() # track beginning of group address @@ -598,8 +598,8 @@ class Peripheral(BaseObject): if len(group_addresses) == 1: group_address = group_addresses[0] elif len(group_addresses) > 1: - logger.error('%s.peripheral.yaml: Manually set addresses within field group %s %s are conflicting, please change in configuration file', - self.hdl_library_name, field.group_name(), type(field.group_name())) + logger.error('*.peripheral.yaml: Manually set addresses within field group %s %s are conflicting, please change in configuration file', + field.group_name(), type(field.group_name())) sys.exit() else: # address not assigned group_address = UNASSIGNED_ADDRESS @@ -613,13 +613,13 @@ class Peripheral(BaseObject): while any([i in occupied_bits for i in range(free_bit, free_bit + field.width()+1)]): # bit is occupied free_bit = free_bit + 1 # try next bit if free_bit == DEFAULT_WIDTH: # 31 didn't work - logger.error('%s.peripheral.yaml: No suitable gap available for automatic bit offset assignment of field%s', - self.hdl_library_name, field.name()) + logger.error('*.peripheral.yaml: No suitable gap available for automatic bit offset assignment of field%s', + field.name()) logger.error("Check peripheral.yaml file. Field group may be overstuffed or manual bit assignment may have precluded space for other fields") break field.bit_offset(free_bit) occupied_bits = occupied_bits + list(range(field.bit_offset(), field.bit_offset() + field.width())) - # logger.warning("{}.peripheral.yaml: Final field {} addr {} [{}-{}]".format(self.hdl_library_name, field.name(), str(field.address_offset()), str(field.bit_offset()+field.width()-1),str(field.bit_offset()))) + # logger.warning("*.peripheral.yaml: Final field {} addr {} [{}-{}]".format(field.name(), str(field.address_offset()), str(field.bit_offset()+field.width()-1),str(field.bit_offset()))) # re-sort fields to be ordered by address and bit offsets fields_eval.sort(key=lambda x: x.address_offset()) @@ -689,8 +689,7 @@ class Peripheral(BaseObject): # if field.number_of_fields() > 1: # logger.debug(" number_of_fields=%s", str(field.number_of_fields())) logger.debug(" width=%-2s number_of_fields=%s", - str(field.width()), - str(field.number_of_fields())) + str(field.width()), str(field.number_of_fields())) # for reg in self.registers.values(): for reg in self.slaves: @@ -710,12 +709,9 @@ class Peripheral(BaseObject): # logger.debug(" number_of_fields=%s", str(field.number_of_fields())) logger.debug(" width=%-2s address_offset=0x%02x access_mode=%-4s reset_value=%-4s radix=%s", - str(field.width()), field.address_offset(), - field.access_mode(), str(field.reset_value()), - field.radix()) + str(field.width()), field.address_offset(), field.access_mode(), str(field.reset_value()), field.radix()) logger.debug(" bit_offset=%-2s number_of_fields=%-4s side_effect=%-4s software_value=%-4s", - str(field.bit_offset()), str(field.number_of_fields()), - field.side_effect(), str(field.software_value())) + str(field.bit_offset()), str(field.number_of_fields()), field.side_effect(), str(field.software_value())) logger.debug(" parameters:") for param_key, param_val in self._parameters.items(): @@ -797,16 +793,15 @@ class PeripheralLibrary(object): for fpn in file_path_names: logger.debug("Load peripheral(s) from '%s'", fpn) library_config = yaml.load(open(fpn, 'r')) - lib = library_config['hdl_library_name'] for peripheral_config in library_config['peripherals']: try : - peripheral = deepcopy(Peripheral(lib, peripheral_config)) + peripheral = deepcopy(Peripheral(peripheral_config)) except ARGSNameError: - logger.error("Invalid peripheral_name '%s' in %s.peripheral.yaml", - peripheral_config['peripheral_name'], lib) + logger.error("Invalid peripheral_name '%s' in *.peripheral.yaml", + peripheral_config['peripheral_name'], library_config['hdl_library_name']) sys.exit() logger.debug(" read peripheral '%s'" % peripheral.component_name()) - self.library[lib]['peripherals'].update({peripheral.component_name(): peripheral}) + self.library[library_config['hdl_library_name']]['peripherals'].update({peripheral.component_name(): peripheral}) self.nof_peripherals = self.nof_peripherals + 1 # self.nof_peripherals = len(self.peripherals) # number of peripherals return @@ -835,7 +830,7 @@ class PeripheralLibrary(object): if matches > 1: if fpga_library is not None and fpga_library in matching_libs: logger.debug("Multiple peripherals named %s found under libs %s, limiting peripheral search to local design library", - periph_name, ' '.join(matching_libs).upper(), fpga_library) + periph_name, ' '.join(matching_libs).upper(), fpga_library) return self.library[fpga_library]['peripherals'].get(periph_name, None) else: print(' '.join(matching_libs)) @@ -844,7 +839,7 @@ class PeripheralLibrary(object): sys.exit() elif matches == 1: logger.debug("Peripheral %s found under library %s", - periph_name, matching_libs[0]) + periph_name, matching_libs[0]) return self.library[matching_libs[0]]['peripherals'][periph_name] else: logger.error("No matching peripherals for '%s' found under %s", diff --git a/py_args_lib/peripheral_lib/common_func.py b/py_args_lib/peripheral_lib/common_func.py index bec8a1cb0b51091b705ef65495d799d61443ba48..12bafe574b189c271bd6d63ad2793d6e4141e3d7 100644 --- a/py_args_lib/peripheral_lib/common_func.py +++ b/py_args_lib/peripheral_lib/common_func.py @@ -26,6 +26,7 @@ ################################################################################ # System imports +import os import math # do not use numpy in common, to avoid making common to elaborate import re @@ -61,3 +62,14 @@ def unique(in_list): def path_string(dir): joined_dir = ''.join(re.split('[/\\\\]+',dir)) return joined_dir.lower() + + +def check_outdir(boardname, fpganame, args_dir=None): + out_dir = os.path.join(os.getenv('ARGS_BUILD_DIR'), boardname.replace('uniboard', 'unb'), 'args', fpganame) + if args_dir is not None: + out_dir = os.path.join(out_dir, args_dir) + try: + os.stat(out_dir) # Check that the output directory exists + except FileNotFoundError: + os.makedirs(out_dir) # if not make it + return out_dir