├── README.md ├── LICENSE └── verilog2spice.py /README.md: -------------------------------------------------------------------------------- 1 | # Verilog to Spice in Python 2 | 3 | Simple structured VERILOG netlist to SPICE netlist translator 4 | 5 | usage example assuming : 6 | * the verilog netlist to be converted called : final.v 7 | * a reference stdcells library spice netlist : stdcells.cdl 8 | * a reference memory block spice netlist : memory.cdl 9 | * the target spice netlist output : final.sp 10 | 11 | _**python verilog2spice.py -spice stdcells.cdl -spice memory.cdl -verilog final.v -output final.sp -pos_pwr VDD -neg_pwr VSS -delimiter**_ 12 | 13 | * under Linux, the command python can be avoided or replaced by python3 14 | * if pos_pwr and neg_pwr are not specified, they are by default VDD and VSS 15 | * if -delimiter is used, the busses delimiter will be changed from [:] in the verilog netlist to <:> in the spice netlist 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, laurentc2 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /verilog2spice.py: -------------------------------------------------------------------------------- 1 | #! usr/bin/python 2 | 3 | # 4 | # Simple structured VERILOG netlist to SPICE netlist translator 5 | # 6 | # usage example : assuming a verilog netlist called final.v 7 | # based on a stdcells library and a memory : 8 | # 9 | # python verilog2spice.py -spice stdcells.cdl -spice memory.cdl -verilog final.v -output final.sp -pos_pwr VDD -neg_pwr VSS -delimiter 10 | # 11 | # if pos_pwr and neg_pwr are not specified, they are by default VDD and VSS 12 | # 13 | # if -delimiter is used the busses delimiter will be changed 14 | # from [:] in the verilog netlist to <:> in the spice netlist 15 | # 16 | # distributed under GNU GPLv3 17 | ############################################################################## 18 | 19 | import sys 20 | import re 21 | from datetime import datetime 22 | 23 | spi_files = [] 24 | ver_file = '' 25 | out_file = '' 26 | pos_pwr = 'VDD' 27 | neg_pwr = 'VSS' 28 | del_on = False 29 | 30 | for arg in range(1,len(sys.argv)) : # parse all arguments 31 | if sys.argv[arg] == '-spice' : 32 | spi_files.append(sys.argv[arg+1]) 33 | elif sys.argv[arg] == '-verilog' : 34 | if ver_file != "" : 35 | print ('ERROR : only one structured Verilog netlist can be specified !') 36 | else : 37 | ver_file = sys.argv[arg+1] 38 | elif sys.argv[arg] == '-output' : 39 | if out_file != "" : 40 | print ('ERROR : only one output Spice netlist can be specified !') 41 | else : 42 | out_file = sys.argv[arg+1] 43 | if sys.argv[arg] == '-pos_pwr' : 44 | pos_pwr = sys.argv[arg+1] 45 | if sys.argv[arg] == '-neg_pwr' : 46 | neg_pwr = sys.argv[arg+1] 47 | elif sys.argv[arg] == '-delimiter' : 48 | del_on = True 49 | 50 | if len(spi_files) == 0 : 51 | sys.exit("Spice library netlist not specified") 52 | if ver_file == "" : 53 | sys.exit("Verilog netlist not specified") 54 | if out_file == "" : 55 | sys.exit("Output Spice netlist not specified") 56 | if del_on : 57 | print ('The positive power supply is : ' + pos_pwr + ' The negative one : ' + neg_pwr + ' Busses are delimited by <:>') 58 | else : 59 | print ('The positive power supply is : ' + pos_pwr + ' The negative one : ' + neg_pwr + ' Busses are delimited by [:]') 60 | 61 | nb_subckt = 0 # number of cells in the spice netlist 62 | cells = [] # list of cell of the spice netlist 63 | cell_num = 0 #same as nb_subckt + 1 64 | inst_on = False 65 | subckt_on = False 66 | spi_inc = "" 67 | 68 | # parse the SPICE cells library file : 69 | ###################################### 70 | for spi_file in spi_files : 71 | spifl = open(spi_file,'r') # open a SPICE library file 72 | if spi_file.find('\\') != -1 : # remove any path from the reference SPICE netlist 73 | spi_file = spi_file[spi_file.rfind('\\')+1:] 74 | if spi_file.find('/') != -1 : # remove any path from the reference SPICE netlist 75 | spi_file = spi_file[spi_file.rfind('/')+1:] 76 | spi_inc = spi_inc + spi_file + ' ' 77 | for line1 in spifl: 78 | words = line1.upper().rstrip('\r\n').strip().split() 79 | if len(words) > 0: 80 | if words[0].find('SUBCKT') == 1 : 81 | subckt_on = True 82 | nb_subckt += 1 83 | words.pop(0) 84 | cells.append(words) 85 | elif subckt_on and words[0] == '+' : # case of .SUBCKT defined on several lines 86 | cells[cell_num].extend(words) # store each cell_name and pins in a list 87 | else : 88 | subckt_on = False 89 | if words[0].find('ENDS') == 1 : # end of SUBCKT 90 | #print (cells[cell_num]) 91 | cell_num += 1 92 | spifl.close() 93 | 94 | if nb_subckt == 0 : 95 | sys.exit('\nERROR : NO subckt found in the Spice netlist !\n') 96 | else : 97 | print ('... end of SPICE netlist parsing : ' + str(nb_subckt) + ' cells found in the SPICE netist.\n') 98 | 99 | # parse the VERILOG netlist : 100 | ############################# 101 | verfl = open(ver_file,'r') # open VERILOG file to translate 102 | outfl = open(out_file,'w') # open the output SPICE netlist 103 | 104 | nb_subckt = 0 105 | nb_pins = 0 106 | outfl.write('*\n* ' +out_file + ' : SPICE netlist translated from the VERILOG netlist : ' + ver_file + '\n') 107 | outfl.write('*'+ ' '* (len(out_file) + 5 ) + 'on the ' + str(datetime.now())+ '\n*\n') 108 | outfl.write('*' * (len(out_file) + len(ver_file) + 60) + '\n\n') 109 | outfl.write('.INCLUDE ' + spi_inc + '\n\n') 110 | 111 | for line1 in verfl: 112 | words = line1.upper().rstrip('\r\n').strip().split() 113 | if len(words) > 0: 114 | if words[0].find('MODULE') == 0 : #first build the toplevel subckt 115 | subckt_name = words[1] 116 | subckt = '.SUBCKT ' + subckt_name + ' ' 117 | if words[0].startswith('INPUT') or words[0].startswith('OUTPUT') or words[0].startswith('INOUT') : 118 | subckt_on = True 119 | if line1.find('[') == -1 : # pins that are not a bus 120 | subckt += line1[line1.upper().find(words[0])+6:].strip() + ' ' 121 | subckt = subckt.replace(',','') 122 | subckt = subckt.replace(';','') 123 | 124 | else : # busses treatment 125 | lsb = min(int(line1[line1.find('[')+1 : line1.find(':')]) , int(line1[line1.find(':')+1 : line1.find(']')])) 126 | msb = max(int(line1[line1.find('[')+1 : line1.find(':')]) , int(line1[line1.find(':')+1 : line1.find(']')])) 127 | words = re.split(', *', line1[line1.find(']')+1:].rstrip('\r\n').strip().replace(';','')) 128 | for word in words: 129 | for i in range(lsb,msb+1): # spread each bit of each bus 130 | subckt += word + '[' + str(i) + '] ' 131 | 132 | if subckt_on and line1.find('(')>0 : # first cell detected : write the toplevel .SUBCKT 133 | subckt_on = False 134 | if del_on : # change the busses delimiter 135 | subckt = subckt.replace('[','<').replace(']','>') 136 | outfl.write('.GLOBAL ' + pos_pwr + ' ' + neg_pwr + '\n\n' + subckt.upper() + '\n\n') 137 | 138 | if (not subckt_on) and (not inst_on) and re.search('\(\s*\.',line1) and words[0].find('MODULE') != 0 and line1.strip()[0:2].find('//') != 0 : 139 | words = line1.upper().rstrip('\r\n').strip().split() 140 | if words[1][0] == 'X' : # avoid double XX at the beginning of the instance name 141 | instance = words[1] 142 | else : 143 | instance = 'X' + words[1] 144 | subckt = words[0] 145 | inst_on = True 146 | line2 = line1[line1.find('(')+1:] 147 | elif (not subckt_on) and inst_on : # store all the instance description into line2 148 | line2 = line2 + line1.upper() 149 | 150 | if inst_on and line1.find(';')>0 : # end of the cell description 151 | inst_on = False 152 | if del_on : # change the busses delimiter 153 | line2 = line2.replace('\[','\<').replace('\]','\>') 154 | pins=[] # list of pins 155 | nodes=[] # list of netlist nodes 156 | words = line2.upper().rstrip('\r\n').strip().split('.') 157 | all_pins = ' ' 158 | for word in words : 159 | pins.append(word[:word.find('(')]) 160 | nodes.append(word[word.find('(')+1:word.find(')')]) 161 | i = 0 162 | while i < len(cells) and subckt != cells[i][0] : # search for the cell on the list of cells stored with the SPICE 163 | i += 1 164 | if i == len(cells) : 165 | print ('ERROR : subckt ' + subckt + ' not found in the Spice netlist !') 166 | nb_subckt += 1 167 | else : 168 | inst_name = instance 169 | for pin in range(1,len(cells[i])) : # search for the pins of the SPICE subckt 170 | if cells[i][pin] == pos_pwr : 171 | instance = instance + ' ' + pos_pwr 172 | elif cells[i][pin] == neg_pwr : 173 | instance = instance + ' ' + neg_pwr 174 | else : 175 | j = 0 176 | while j < len(pins) and cells[i][pin] != pins[j] : # if the verilog pin name = spice pin name 177 | j += 1 178 | if j == len(nodes) : 179 | print ( 'ERROR : pin ' + cells[i][pin] + ' of the Spice netlist not found for the cell ' + inst_name + ' of the Verilog netlist !') 180 | nb_pins += 1 181 | else : 182 | instance = instance + ' ' + nodes[j] 183 | # print (instance + ' ' + subckt) 184 | outfl.write(instance + ' ' + subckt + '\n') 185 | 186 | outfl.write('\n' + '.ENDS ' + subckt_name ) 187 | 188 | if nb_subckt > 0 : 189 | print ('\nERROR : during the translation : ' + str(nb_subckt) + ' cells from the VERILOG netlist not found in the SPICE netlist !\n') 190 | if nb_pins > 0 : 191 | print ('\nERROR : during the translation : ' + str(nb_pins) + ' pins from the VERILOG netlist not found in the SPICE netlist !\n') 192 | if nb_subckt + nb_pins == 0 : 193 | print (ver_file + ' : VERILOG netlist successfully translated to the SPICE netlist : ' + out_file + '\n') 194 | 195 | verfl.close() 196 | outfl.close() 197 | 198 | exit(0) # Successful exit 199 | --------------------------------------------------------------------------------