High Level Synthesis in py4hw refers to the process of creating hardware from python source code. The result of this process is not a Verilog code, but a py4hw structural model, which can then be translated to verilog.
Thus, this is a source-to-source transformation in the python language domain.
Let's create this circuit to compute the square root of an integer value using the babylonian method. Note that is is a combinational circuit, because all the behavior takes place in the propagate method.
import py4hw
class SquareRoot(py4hw.Logic):
def __init__(self, parent, name, a, r):
super().__init__(parent, name)
self.a = self.addIn('a', a)
self.r = self.addOut('r', r)
def propagate(self):
maxv = 1 << self.a.getWidth()
maxiter = int(math.log2(math.log2(maxv))) + 4
#print('max iters:', maxiter)
n = self.a.get()
if n == 0:
r = 0
elif n == 1:
r = 1
else:
guess = n
done = False
for _ in range(maxiter):
# For a combinational circuit loops must be fully unrollable,
# otherwise no synthesys is possible
if (done):
pass
else:
next_guess = (guess + (n // guess)) // 2 # Integer division
if next_guess >= guess: # Convergence condition: guess stops decreasing
done = True
else:
guess = next_guess
r = guess
self.r.put(r)
we want to create an structural circuit from this behavioral description. This is what we expect from High Level Synthesis workflows. However, typical HLS workflows will create Verilog from high-level language. We can follow a different approach, generate a py4hw structural model (in Python) from a py4hw behavioral model (also in Python, of course).
from py4hw.transpilation.python2structural import Python2StructuralCode
coder = Python2StructuralCode(SquareRoot)
print(coder.getCode())
--------------------------------------------------------------------------- OSError Traceback (most recent call last) Cell In[4], line 3 1 coder = Python2StructuralCode(SquareRoot) ----> 3 print(coder.getCode()) File C:\Projects\Research\INT_Py4hw\py4hw\py4hw\transpilation\python2structural.py:20, in Python2StructuralCode.getCode(self) 17 def getCode(self): 18 ret = '# This file was automatically created by Python2StructuralCode\n' ---> 20 class_ast = get_class_ast(self.clazz) 22 renamer = ClassRenamer(self.clazz.__name__, self.clazz.__name__ + 'Structural') 23 class_ast = renamer.visit(class_ast) File C:\Projects\Research\INT_Py4hw\py4hw\py4hw\transpilation\astutils.py:103, in get_class_ast(clazz) 100 raise ValueError("Input must be a class") 102 # Get the full class source and parse it --> 103 source = textwrap.dedent(inspect.getsource(clazz)) 104 module_node = ast.parse(source) 106 # Find the class definition node File ~\AppData\Local\anaconda3\Lib\inspect.py:1262, in getsource(object) 1256 def getsource(object): 1257 """Return the text of the source code for an object. 1258 1259 The argument may be a module, class, method, function, traceback, frame, 1260 or code object. The source code is returned as a single string. An 1261 OSError is raised if the source code cannot be retrieved.""" -> 1262 lines, lnum = getsourcelines(object) 1263 return ''.join(lines) File ~\AppData\Local\anaconda3\Lib\inspect.py:1244, in getsourcelines(object) 1236 """Return a list of source lines and starting line number for an object. 1237 1238 The argument may be a module, class, method, function, traceback, frame, (...) 1241 original source file the first line of code was found. An OSError is 1242 raised if the source code cannot be retrieved.""" 1243 object = unwrap(object) -> 1244 lines, lnum = findsource(object) 1246 if istraceback(object): 1247 object = object.tb_frame File ~\AppData\Local\anaconda3\Lib\inspect.py:1063, in findsource(object) 1055 def findsource(object): 1056 """Return the entire source file and starting line number for an object. 1057 1058 The argument may be a module, class, method, function, traceback, frame, 1059 or code object. The source code is returned as a list of all the lines 1060 in the file and the line number indexes a line in that list. An OSError 1061 is raised if the source code cannot be retrieved.""" -> 1063 file = getsourcefile(object) 1064 if file: 1065 # Invalidate cache if needed. 1066 linecache.checkcache(file) File ~\AppData\Local\anaconda3\Lib\inspect.py:940, in getsourcefile(object) 936 def getsourcefile(object): 937 """Return the filename that can be used to locate an object's source. 938 Return None if no way can be identified to get the source. 939 """ --> 940 filename = getfile(object) 941 all_bytecode_suffixes = importlib.machinery.DEBUG_BYTECODE_SUFFIXES[:] 942 all_bytecode_suffixes += importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES[:] File ~\AppData\Local\anaconda3\Lib\inspect.py:908, in getfile(object) 906 return module.__file__ 907 if object.__module__ == '__main__': --> 908 raise OSError('source code not available') 909 raise TypeError('{!r} is a built-in class'.format(object)) 910 if ismethod(object): OSError: source code not available
coder.clazz.__name__
'SquareRoot'