Last active
August 29, 2020 16:04
-
-
Save gaurishg/b788d4c13cbc02b324270e9b55f5d74d to your computer and use it in GitHub Desktop.
Python3 script for counting number of NAND gates used in implementation of various logic gates and circuites in nand2tetris course.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import sys, os | |
import typing | |
args = sys.argv | |
# print(args) | |
# This dict will contain data of gate | |
GateDict = {"Nand": [1, {"Nand": 1}], | |
"DFF": [4, {"Nand": 4}], | |
"Screen": [0, {"Nand": 0}]} | |
HDLs: typing.Dict[str, str] = {} # Dict of Gates and their HDL files | |
dirs = ['01', '02', os.path.join('03', 'a'), os.path.join('03', 'b'), | |
'04', '05', '06', '07', '08', '09', '10', '11', '12', '13', ] | |
for folder in dirs: | |
folder = os.path.join('.', folder) | |
for f in os.listdir(folder): | |
if os.path.isfile(os.path.join(folder, f)): | |
# print("{} is file".format(f)) | |
if f.endswith('.hdl'): | |
chip = f[:f.find('.hdl')] | |
if chip not in HDLs: | |
HDLs[chip] = os.path.join(folder, f) | |
# print(HDLs) | |
# Get chip name | |
# Get all intermediate chip names | |
# Repeat this procedure for all intermediate chips | |
def chipCount(chipName): | |
chipsUsed = {} | |
Nands = 0 | |
if chipName in GateDict: | |
return GateDict[chipName] | |
if chipName in HDLs: | |
filename = HDLs[chipName] | |
with open(filename) as hdl_file: | |
comment = False | |
for line in hdl_file: | |
line = line.strip() | |
# Handle multiline comments | |
if comment: | |
if line.endswith("*/"): | |
comment = False | |
continue | |
else: | |
if line.startswith("/*"): | |
comment = True | |
continue | |
# Multiline comments handling ended | |
if len(line) and line[0].isupper() and line.find('(') != -1: | |
subChipName = line[:line.find('(')] | |
if subChipName in chipsUsed: | |
chipsUsed[subChipName] += 1 | |
else: | |
chipsUsed[subChipName] = 1 | |
for subChip in chipsUsed: | |
Nands_in_chip, _ = chipCount(subChip) | |
Nands += Nands_in_chip * chipsUsed[subChip] | |
GateDict[chipName] = [Nands, chipsUsed] | |
return Nands, chipsUsed | |
else: | |
print(chipName, "not found") | |
def getAllChips(chipName): | |
if chipName == "Nand": | |
return ["Nand"] | |
if chipName not in GateDict: | |
chipCount(chipName) | |
chips = [chipName] | |
chips += [chip for chip in GateDict[chipName][1]] | |
for chip in chips[1:]: | |
subchips = getAllChips(chip) | |
for subchip in subchips: | |
if subchip not in chips: | |
chips.append(subchip) | |
chips.sort(key=lambda x: GateDict[x][0], reverse=True) | |
return chips | |
def print_detailed_one(chipName): | |
chipsUsed = 0 | |
if chipName not in GateDict: | |
chipCount(chipName) | |
gates = GateDict[chipName][1] | |
print(f"{chipName}".center(60, "=")) | |
print(f"Chip".ljust(25) + "NANDs/chip".ljust(15) + "Total NANDs".rjust(20)) | |
print("-" * 60) | |
for gate in gates: | |
chipsUsed+=gates[gate] | |
print(f"{gate}({gates[gate]})".ljust(25) + f"{GateDict[gate][0]} NANDs/chip".ljust(15) + f"{gates[gate] * GateDict[gate][0]}".rjust(20)) | |
print("-" * 60) | |
print(f"Total {chipsUsed} chip{'s' if chipsUsed > 1 else ''} used".ljust(50) + f"{GateDict[chipName][0]:10}") | |
def print_detailed_full(chipName): | |
if chipName not in GateDict: | |
chipCount(chipName) | |
gates = getAllChips(chipName) | |
for gate in gates: | |
print_detailed_one(gate) | |
print() | |
def print_detailed_one_table(chipName): | |
gates = getAllChips(chipName) | |
for gate in gates: | |
print("{:20} {:>15} NANDs".format(gate, GateDict[gate][0])) | |
if len(args) > 1: | |
if len(args) == 2: | |
if args[1] in HDLs: | |
chipCount(args[1]) | |
print("{:10} {:>20} NANDs".format(args[1], GateDict[args[1]][0])) | |
else: | |
print("{}.hdl not found!!".format(args[1])) | |
if len(args) > 2: | |
if args[-1] == "--full-detail": | |
for arg in args[1:-1]: | |
if arg in HDLs: | |
chipCount(arg) | |
print("PRINTING DETAILED OUTPUT FOR {}".format(arg)) | |
print_detailed_full(arg) | |
else: | |
print("{}.hdl not found!!".format(arg)) | |
print("X" * 60) | |
print("\n") | |
elif args[-1] == "--one-table": | |
for arg in args[1:-1]: | |
if arg in HDLs: | |
chipCount(arg) | |
print("REPORT FOR {}".format(arg)) | |
print_detailed_one_table(arg) | |
else: | |
print("{}.hdl not found!!".format(arg)) | |
print("X" * 60) | |
print("\n") | |
elif args[-1] == "--detailed": | |
for arg in args[1:-1]: | |
if arg in HDLs: | |
chipCount(arg) | |
print("PRINTING OUTPUT FOR {}".format(arg)) | |
print_detailed_one(arg) | |
else: | |
print("{}.hdl not found!!") | |
print("X" * 60) | |
print("\n") | |
else: | |
for arg in args[1:]: | |
if arg in HDLs: | |
chipCount(arg) | |
print("{:10} {:>20} NANDs".format(arg, GateDict[arg][0])) | |
else: | |
print("{}.hdl not found".format(arg)) | |
else: | |
print("Usage:") | |
print("Windows: python .\\NANDcounter_improved.py <Gate1> [<Gate2> ... <GateN>] [options]") | |
print("Linux/Mac: python3 ./NANDcounter_improved.py <Gate1> [<Gate2>...<GateN>] [options]") | |
print("Options:") | |
print("\t1. --full-detail:\t Recursively give NAND gate used in chips") | |
print("\t2. --one-table:\t\t All Gates used in your implementation (upto NAND itself)") | |
print("\t3. --detailed:\t\t Prints only first table of --full-detail") | |
print() | |
print("Example:") | |
print("\tLinux:\t python3 ./NANDcounter_improved.py Xor ALU Nand HalfAdder --one-table") | |
print("\tWindows:\t python .\\NANDcounter_improved.py Xor ALU Nand HalfAdder --one-table") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment