cve2/scripts/ri5cly-manage.py
2016-12-29 22:26:42 +01:00

227 lines
9.4 KiB
Python
Executable file

#!/bin/python3
# Copyright 2016 ETH Zurich and University of Bologna.
# Copyright and related rights are licensed under the Solderpad Hardware
# License, Version 0.51 (the “License”); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
# or agreed to in writing, software, hardware and materials distributed under
# this 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.
################################################################################
## Engineer: Markus Wegmann - markus.wegmann@technokrat.ch ##
## ##
## Project Name: littleRISCV ##
## Language: Python 3.6 ##
## ##
## Description: This program can overwrite the littleRISCV config and ##
## export a clean version of the core. The macro switches ##
## and all unnecessary code is removed in the root .sv files. ##
## ##
################################################################################
import os
import shutil
import argparse
import re
import zipfile
def zipdir(path, ziph):
# ziph is zipfile handle
for root, dirs, files in os.walk(path):
for file in files:
ziph.write(os.path.relpath(os.path.join(root, file), os.path.join(path, '..')))
def main():
littleRISCV_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/..") # Path to littleRISCV folder
print(littleRISCV_path)
parser = argparse.ArgumentParser(description="Can overwrite config and export clean version of littleRISCV")
parser.add_argument('-i', dest="new_riscv_config", metavar='.../new_riscv_config.sv',
help='path to a new config file to overwrite littleRISCV config')
parser.add_argument('-o', dest='export_folder_path', metavar='.../export_folder_path',
help='path to a folder to export clean version of littleRISCV without preprocessor switches')
parser.add_argument('-z', dest='zip', action='store_true',
help='zip the export into a tar.gz')
args = parser.parse_args()
action_taken = False
if args.new_riscv_config is not None:
overwriteConfig(args["new_riscv_config.sv"], littleRISCV_path)
action_taken = True
if args.export_folder_path is not None:
exportCleanVersion(args.export_folder_path, littleRISCV_path, zip=args.zip)
action_taken = True
if action_taken == False:
print("No action taken. Please see the help below for more information:")
parser.print_help()
def overwriteConfig(new_config_path, littleRISCV_path):
print("Overwriting current config (include/riscv_config.sv) with new one.")
shutil.move(os.path.abspath(littleRISCV_path + "/include/riscv_config.sv"), os.path.abspath(littleRISCV_path + "/include/riscv_config.sv.bak")) # Backup
shutil.copy(os.path.abspath(new_config_path), os.path.abspath(littleRISCV_path + "/include/riscv_config.sv")) # Copy new config to littleRISCV
def parseConfig(littleRISCV_path):
print("Trying to parse configuration.")
definitions = []
config = os.path.abspath(littleRISCV_path + "/include/riscv_config.sv")
with open(config, encoding="utf8") as f:
nesting_counter = 0 # If we enter a not-enabled section, keep track when we leave the section again
content = f.readlines()
while len(content) > 0:
line = content.pop(0)
config_pattern = re.compile("^//.*CONFIG:\s(\w*)$") # Test for config declaration
m = config_pattern.match(line)
if m is not None and nesting_counter == 0:
content.pop(0) # Pop description
line = content.pop(0)
config_value_pattern = re.compile("^`define\s(\w*)$") # Check if config enabled and extract name
m = config_value_pattern.match(line)
if m is not None:
definitions.append(m.group(1))
else:
ifdef_pattern = re.compile("^`ifdef\s(\w*)$") # Check if we have a dependant config
m = ifdef_pattern.match(line)
if m is not None:
if m.group(1) in definitions:
pass
else:
nesting_counter += 1
ifndef_pattern = re.compile("^`ifndef\s(\w*)$") # Check if we have a dependant config
m = ifndef_pattern.match(line)
if m is not None:
if not (m.group(1) in definitions):
pass
else:
nesting_counter += 1
endif_pattern = re.compile("^`endif.*$") # Check if we ended a block
m = endif_pattern.match(line)
if m is not None:
nesting_counter -= 1
if nesting_counter < 0:
nesting_counter = 0
print("Enabled CONFIG definitions: {}".format(definitions))
return definitions
def processSystemVerilog(filename, folderpath, definitions):
print("Processing: {}".format(filename))
content = []
new_content = []
with open(os.path.abspath(folderpath+"/"+filename), encoding="utf8") as f:
content = f.readlines()
nesting_counter = 0 # If we enter a not-enabled section, keep track when we leave the section again
is_else_true = False # at first occurence of missing declarations
while len(content) > 0:
is_codeline = True
line = content.pop(0)
config_pattern = re.compile("^\s*//.*CONFIG_REGION:\s(\w*)$") # Test for config region declaration
m = config_pattern.match(line)
if m is not None:
is_codeline = False
line = content.pop(0)
ifdef_pattern = re.compile("^\s*`ifdef\s(\w*)$") # Check if we have an ifdef
m = ifdef_pattern.match(line)
if m is not None:
if m.group(1) in definitions:
if nesting_counter == 0:
is_else_true = False
else:
nesting_counter += 1
else:
if nesting_counter == 0:
is_else_true = True
nesting_counter += 1
ifndef_pattern = re.compile("^\s*`ifndef\s(\w*)$") # Check if we have an ifndef
m = ifndef_pattern.match(line)
if m is not None:
if not (m.group(1) in definitions):
if nesting_counter == 0:
is_else_true = False
else:
nesting_counter += 1
else:
if nesting_counter == 0:
is_else_true = True
nesting_counter += 1
else_pattern = re.compile("^\s*`else.*$") # Check if we have an else
m = else_pattern.match(line)
if m is not None:
is_codeline = False
if nesting_counter == 1 and is_else_true:
nesting_counter -= 1
if (not is_else_true) and nesting_counter == 0:
nesting_counter += 1
endif_pattern = re.compile("^\s*`endif.*$") # Check if we have an endif
m = endif_pattern.match(line)
if m is not None:
is_codeline = False
nesting_counter -= 1
if nesting_counter < 0:
nesting_counter = 0
if is_codeline and nesting_counter == 0:
new_content.append(line)
os.remove(os.path.abspath(folderpath+"/"+filename))
with open(os.path.abspath(folderpath+"/"+filename), 'w', encoding="utf8") as f:
f.writelines(new_content)
def exportCleanVersion(build_path, littleRISCV_path, zip=False):
print("Exporting clean version of littleRISCV without preprocessor switches to defined output folder.")
definitions = parseConfig(littleRISCV_path)
shutil.rmtree(os.path.abspath(build_path), ignore_errors=True)
shutil.copytree(os.path.abspath(littleRISCV_path), os.path.abspath(build_path), ignore=shutil.ignore_patterns("*.git", "scripts", "docs"))
for filename in os.listdir(os.path.abspath(build_path)):
sv_p = re.compile("^.*\.sv")
m = sv_p.match(filename)
if m is not None:
processSystemVerilog(filename, build_path, definitions)
with open(os.path.abspath(build_path+"/THIS_CORE_IS_AUTOMATICALLY_GENERATATED!!!.txt"), 'w', encoding="utf8") as f:
f.write("This core export was automatically generated by ri5cly-manage.py\n\n")
f.write("Following settings were enabled: {}".format(definitions))
if zip is True:
print("Zip exported version.")
zipf = zipfile.ZipFile(os.path.abspath(build_path)+".zip", 'w', zipfile.ZIP_DEFLATED)
zipdir(os.path.abspath(build_path), zipf)
zipf.close()
shutil.rmtree(os.path.abspath(build_path), ignore_errors=True)
if __name__ == "__main__":
main()