mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 13:57:19 -04:00
412 lines
17 KiB
Python
Executable file
412 lines
17 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
|
|
import subprocess
|
|
|
|
|
|
class bcolors:
|
|
HEADER = '\033[95m'
|
|
OKBLUE = '\033[94m'
|
|
OKGREEN = '\033[92m'
|
|
WARNING = '\033[93m'
|
|
FAIL = '\033[91m'
|
|
ENDC = '\033[0m'
|
|
BOLD = '\033[1m'
|
|
UNDERLINE = '\033[4m'
|
|
|
|
|
|
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')
|
|
parser.add_argument('--synthesize', dest='synthesize', action='store_true',
|
|
help='will synthesize the current or new configuration')
|
|
parser.add_argument('--report', dest='report', action='store_true',
|
|
help='will report custom synthesized design')
|
|
parser.add_argument('--synthesize_all', dest='synthesize_all', action='store_true',
|
|
help='will synthesize all sample configs in the scripts/example_configs folder with Synopsys')
|
|
parser.add_argument('--report_all', dest='report_all', action='store_true',
|
|
help='will report all sample configs which have been synthesized')
|
|
parser.add_argument('--test_all', dest='test_all', action='store_true',
|
|
help='will run some basic RTL simulations test, like helloworld on all sample configs in the scripts/example_configs folder')
|
|
parser.add_argument('--test', dest='test', action='store_true',
|
|
help='will run some basic RTL simulations test, like helloworld')
|
|
args = parser.parse_args()
|
|
|
|
action_taken = False
|
|
|
|
if args.new_riscv_config is not None:
|
|
overwriteConfig(args.new_riscv_config, 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 args.synthesize == True:
|
|
synthesize(littleRISCV_path)
|
|
report(littleRISCV_path)
|
|
action_taken = True
|
|
|
|
elif args.report == True:
|
|
report(littleRISCV_path)
|
|
action_taken = True
|
|
|
|
if args.synthesize_all == True:
|
|
synthesizeAll(littleRISCV_path)
|
|
reportAll(littleRISCV_path)
|
|
action_taken = True
|
|
elif args.report_all == True:
|
|
reportAll(littleRISCV_path)
|
|
action_taken = True
|
|
|
|
if args.test_all == True:
|
|
testAll(littleRISCV_path)
|
|
action_taken = True
|
|
|
|
if args.test == True:
|
|
test(littleRISCV_path)
|
|
action_taken = True
|
|
|
|
if action_taken == False:
|
|
print("No action taken. Please see the help below for more information:")
|
|
parser.print_help()
|
|
|
|
|
|
def backupConfig(new_config_path, littleRISCV_path):
|
|
print("Backing up current config (include/riscv_config.sv) to (include/riscv_config.sv.bak)".format(new_config_path))
|
|
shutil.copy(os.path.abspath(littleRISCV_path + "/include/riscv_config.sv"), os.path.abspath(littleRISCV_path + "/include/riscv_config.sv.bak")) # Backup
|
|
|
|
def restoreConfig(new_config_path, littleRISCV_path):
|
|
print("Restoring backup config (include/riscv_config.sv.bak).")
|
|
shutil.copy(os.path.abspath(littleRISCV_path + "/include/riscv_config.sv"), os.path.abspath(littleRISCV_path + "/include/riscv_config.sv.bak")) # Backup
|
|
|
|
def overwriteConfig(new_config_path, littleRISCV_path, backup=True):
|
|
print("Overwriting current config (include/riscv_config.sv) with new one ({})".format(new_config_path))
|
|
if backup:
|
|
backupConfig(new_config_path, littleRISCV_path)
|
|
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)
|
|
|
|
|
|
def synthesizeAll(littleRISCV_path):
|
|
if not os.path.exists(os.path.abspath(littleRISCV_path+"/../../../synopsys/start_synopsys_synth.py")):
|
|
print("littleRISCV repository not contained in Imperio/Pulpino project! Canceling.")
|
|
return
|
|
|
|
if not os.path.isdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/")):
|
|
os.mkdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/"))
|
|
|
|
backupConfig()
|
|
|
|
print("Synthsizing all configs in (/scripts/example_configs/).")
|
|
|
|
for filename in os.listdir(os.path.abspath(littleRISCV_path + "/scripts/example_configs")):
|
|
print("Synthesizing {}".format(filename))
|
|
overwriteConfig(os.path.abspath(littleRISCV_path + "/scripts/example_configs/" + filename), littleRISCV_path, backup=False)
|
|
p = subprocess.Popen([os.path.abspath(littleRISCV_path+"/../../../synopsys/start_synopsys_synth.py")], cwd=os.path.abspath(littleRISCV_path+"/../../../synopsys/"))
|
|
p.wait()
|
|
|
|
# Get clock of synopsys setup configuration
|
|
with open(os.path.abspath(littleRISCV_path+"/../../../synopsys/scripts/setup/setup.tcl"), encoding="utf8") as f:
|
|
content = f.readlines()
|
|
|
|
clock_p = re.compile("^set\sCLOCK_SLOW\s(\d+);\.*$")
|
|
m = clock_p.match()
|
|
|
|
if m is not None:
|
|
clock = str(m.group(1))
|
|
else:
|
|
clock = "undefined"
|
|
|
|
shutil.rmtree(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/" + filename + "_{}".format(clock)), ignore_errors=True)
|
|
shutil.copytree(os.path.abspath(littleRISCV_path + "/../../../synopsys"), os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/" + filename + "_{}".format(clock)))
|
|
print("Synthesized {}".format(filename + "_{}".format(clock)))
|
|
|
|
restoreConfig()
|
|
print("Synthesized all configurations! Bye.")
|
|
|
|
def synthesize(littleRISCV_path):
|
|
if not os.path.exists(os.path.abspath(littleRISCV_path+"/../../../synopsys/start_synopsys_synth.py")):
|
|
print("littleRISCV repository not contained in Imperio/Pulpino project! Canceling.")
|
|
|
|
if not os.path.isdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/")):
|
|
os.mkdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/"))
|
|
|
|
p = subprocess.Popen([os.path.abspath(littleRISCV_path+"/../../../synopsys/start_synopsys_synth.py")], cwd=os.path.abspath(littleRISCV_path+"/../../../synopsys/"))
|
|
p.wait()
|
|
|
|
# Get clock of synopsys setup configuration
|
|
with open(os.path.abspath(littleRISCV_path+"/../../../synopsys/scripts/setup/setup.tcl"), encoding="utf8") as f:
|
|
content = f.readlines()
|
|
|
|
clock_p = re.compile("^set\sCLOCK_SLOW\s(\d+);\.*$")
|
|
m = clock_p.match()
|
|
|
|
if m is not None:
|
|
clock = str(m.group(1))
|
|
else:
|
|
clock = "undefined"
|
|
|
|
shutil.rmtree(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/custom" + "_{}".format(clock)), ignore_errors=True)
|
|
shutil.copytree(os.path.abspath(littleRISCV_path + "/../../../synopsys"), os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/custom" + "_{}".format(clock)))
|
|
|
|
print("Synthesized Imperio! Results are in little-riscv/scripts/synthesis_results/custom_{}. Bye.".format(clock))
|
|
|
|
|
|
def report_specific(config_name, littleRISCV_path):
|
|
process = os.popen("cat " + os.path.abspath(littleRISCV_path + "/scripts/synthesis_results/" + config_name + "/reports/imperio_*_area* | grep 'pulpino_i/core_region_i/RISCV_CORE' "))
|
|
area = process.read()
|
|
process.close()
|
|
area_pattern = re.compile("pulpino_i/core_region_i/RISCV_CORE\s*(\d+\.?\d*)\s*.*")
|
|
m = area_pattern.match(area)
|
|
area = m.group(1)
|
|
area = float(area)
|
|
area /= 1.44 * 1000.0 # 1k GE (gate equivalent)
|
|
|
|
clock_p = re.compile("^.*_(\d+)$")
|
|
m = clock_p.match(config_name)
|
|
|
|
if m is not None:
|
|
clock = m.group(1)
|
|
else:
|
|
clock = "undefined"
|
|
|
|
return "{}\t\t{}".format(config_name,area,clock)
|
|
|
|
def report(littleRISCV_path):
|
|
print("Config\t\tArea (kGE)\t\tFrequency (MHz)")
|
|
print(report_specific("custom", littleRISCV_path))
|
|
|
|
|
|
def reportAll(littleRISCV_path):
|
|
print("Config\t\tArea (kGE)\t\tFrequency (MHz)")
|
|
for filename in os.listdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results")):
|
|
print(report_specific(filename, littleRISCV_path))
|
|
|
|
def testAll(littleRISCV_path):
|
|
backupConfig()
|
|
|
|
for filename in os.listdir(os.path.abspath(littleRISCV_path + "/scripts/synthesis_results")):
|
|
print("Testing RTL of {}".format(filename))
|
|
overwriteConfig(os.path.abspath(littleRISCV_path + "/scripts/example_configs/" + filename), littleRISCV_path, backup=False)
|
|
test(littleRISCV_path)
|
|
|
|
restoreConfig()
|
|
print("Tested all configurations! Bye.")
|
|
|
|
|
|
|
|
def test(littleRISCV_path):
|
|
if not os.path.exists(os.path.abspath(littleRISCV_path+"/../../sw/build")):
|
|
print("littleRISCV repository not contained in Imperio/Pulpino project, or you did not generate make files with CMake in pulpino/sw/build. Canceling.")
|
|
return
|
|
|
|
print("Testing current configuration.")
|
|
print("Compiling design")
|
|
p = subprocess.Popen(["make vcompile"], cwd=os.path.abspath(littleRISCV_path+"/../../sw/build"))
|
|
p.wait()
|
|
print("Running helloworld")
|
|
p = subprocess.Popen(["make helloworld.vsimc"], cwd=os.path.abspath(littleRISCV_path+"/../../sw/build"))
|
|
p.wait()
|
|
|
|
print("Testing finished.")
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|