blob: a21d2ada62031f13e829938724db23c8f970b49a [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright 2020 The SkyWater PDK Authors.
#
# Use of this source code is governed by the Apache 2.0
# license that can be found in the LICENSE file or at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
''' This is a cell VCD waveform generation script.
'''
import csv
import json
import os
import sys
import argparse
import pathlib
import glob
import subprocess
import textwrap
import re
def write_vcd (cellpath, define_data, use_power_pins=False):
''' Generates vcd for a given cell.
Args:
cellpath - path to a cell [str of pathlib.Path]
define_data - cell data from json [dic]
use_power_pins - include power pins toggling in simulation [bool]
'''
# collect power port names
pp = []
for p in define_data['ports']:
if len(p)>2 and p[0]=='power':
pp.append(p[1])
# define output file(s)
ppsuffix = '.pp' if use_power_pins else ''
outfile = os.path.join(cellpath, define_data['file_prefix'] + ppsuffix + '.vcd')
vppfile = os.path.join(cellpath, define_data['file_prefix'] + '.vpp.tmp')
tmptestbed = os.path.join(cellpath, define_data['file_prefix'] + '.tb.v.tmp')
# find and patch Verilog testbed file
testbedfile = os.path.join(cellpath, define_data['file_prefix'] + '.tb.v')
assert os.path.exists(testbedfile), testbedfile
insertppdefine = use_power_pins
insertdumpvars = True
insertfinish = True
prvline=''
with open(tmptestbed,'w') as ttb:
with open(testbedfile,'r') as tbf:
for line in tbf:
# add use_power_pins define
if insertppdefine and line.startswith('`include'):
line = '`define USE_POWER_PINS\n' + line
insertppdefine = False
# add dumpfile define
if insertdumpvars and prvline.strip(' \n\r')=='begin':
line = line[:-len(line.lstrip())] + \
'$dumpfile("' + outfile + '");\n' + \
line[:-len(line.lstrip())] + \
'$dumpvars(1,top);\n' + \
line
insertdumpvars = False
# add finish command, to stop paraller threads
if insertfinish and line.strip(' \n\r')=='end' and not '$finish' in prvline:
line = prvline[:-len(prvline.lstrip())] + '$finish;\n' + line
insertfinish = False
# remove power pins from reg - optinal, but makes output more readable
if not use_power_pins:
for p in pp:
if re.search( 'reg\s+'+p, line ) is not None or \
re.search( p+'\s+\=', line ) is not None :
line=''
break
# remove power pins from dut
if not use_power_pins and define_data['file_prefix']+' dut' in line:
for p in pp:
line = line.replace(f'.{p}({p}),','')
line = line.replace(f'.{p}({p}))',')')
prvline = line
ttb.write(line)
# generate vpp code and vcd recording
if subprocess.call(['iverilog', '-o', vppfile, tmptestbed], cwd=cellpath):
raise ChildProcessError("Icarus Verilog compilation failed")
if subprocess.call(['vvp', vppfile], cwd=cellpath):
raise ChildProcessError("Icarus Verilog runtime failed")
# remove temporary files
os.remove(tmptestbed)
os.remove(vppfile)
def process(cellpath):
''' Processes cell indicated by path.
Opens cell definiton and calls further processing
Args:
cellpath - path to a cell [str of pathlib.Path]
'''
print()
print(cellpath)
define_json = os.path.join(cellpath, 'definition.json')
if not os.path.exists(define_json):
print("No definition.json in", cellpath)
assert os.path.exists(define_json), define_json
define_data = json.load(open(define_json))
if define_data['type'] == 'cell':
write_vcd(cellpath, define_data, use_power_pins = False)
write_vcd(cellpath, define_data, use_power_pins = True)
return
def main():
''' Generates VCD waveform for cell.'''
prereq_txt = ''
output_txt = 'output:\n generates [fullcellname].vcd'
allcellpath = '../../../libraries/*/latest/cells/*'
parser = argparse.ArgumentParser(
description = main.__doc__,
epilog = prereq_txt +'\n\n'+ output_txt,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"--all_libs",
help="process all cells in "+allcellpath,
action="store_true")
parser.add_argument(
"cell_dir",
help="path to the cell directory",
type=pathlib.Path,
nargs="*")
args = parser.parse_args()
if args.all_libs:
path = pathlib.Path(allcellpath).expanduser()
parts = path.parts[1:] if path.is_absolute() else path.parts
paths = pathlib.Path(path.root).glob(str(pathlib.Path("").joinpath(*parts)))
args.cell_dir = list(paths)
cell_dirs = [d.resolve() for d in args.cell_dir if d.is_dir()]
errors = 0
for d in cell_dirs:
try:
process(d)
except KeyboardInterrupt:
sys.exit(1)
except (AssertionError, FileNotFoundError, ChildProcessError) as ex:
print (f'Error: {type(ex).__name__}')
print (f'{ex.args}')
errors +=1
print (f'\n{len(cell_dirs)} files processed, {errors} errors.')
return 0 if errors else 1
if __name__ == "__main__":
sys.exit(main())