The idea of Verilog generation from Python code is to transform the code from the python descriptions. There is some complexity because the dynamic nature of python makes the circuit not only dependent on the class declaration but also in the instantiation parameters.
We will base our description on the following simple circuit of a counter, described behaviourarly.
First, we declare the class, then we instantiate it. It will be the instantiated object that we can translate into verilog, not the class definition.
import py4hw
import ast
from py4hw.transpilation.ast2src import Py4hwUnparser
class CounterBehavioural(py4hw.Logic):
def __init__(self, parent, name, inc, q):
super().__init__(parent, name)
self.inc = self.addIn('inc', inc)
self.q = self.addOut('q', q)
def clock(self):
if (self.inc.get() == 1):
self.q.prepare(self.q.get()+1)
sys = py4hw.HWSystem()
q = sys.wire('q', 32)
inc = sys.wire('inc')
py4hw.Constant(sys, 'inc', 1, inc)
block = CounterBehavioural(sys, 'counter', inc, q)
We need to collect information from the constructor.
import py4hw.transpilation.ast2xml as ast2xml
tr = py4hw.Python2VerilogTranspiler(block, None)
module = tr.getMethodAST('__init__')
node = module
Py4hwUnparser(node)
print()
def __init__(self, parent, name, inc, q):
super().__init__(parent, name)
self.inc = self.addIn('inc', inc)
self.q = self.addOut('q', q)
node = py4hw.createVerilogBody(module.body)
if (False): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
if (False):
xml = ast2xml.visit_node(node)
txt = ast2xml.renderXml(xml)
print(txt.decode('UTF-8'))
initExtracter = py4hw.ExtractInitializers(tr.obj)
init = initExtracter.visit(node)
if (False): Py4hwUnparser(init)
if (False): print(ast.dump(init, indent=' '))
if (False):
xml = ast2xml.visit_node(init)
txt = ast2xml.renderXml(xml)
print(txt.decode('UTF-8'))
#Py4hwUnparser(init)
what information was collected ?
We assign the init section body we collected before
tr = py4hw.Python2VerilogTranspiler(block, None)
module = tr.getMethodAST('clock')
clkname = py4hw.getObjectClockDriver(tr.obj).name
print('Clock name:', clkname)
node = py4hw.createVerilogBody(module.body, 'posedge {}'.format(clkname))
node.init.body = init.init.body
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
Clock name: clk
always @(posedge clk)
begin
if (self.inc.get() == 1):
self.q.prepare((self.q.get() + 1))
end
prints and asserts are for debugging, so we remove them
node = py4hw.RemovePrints().visit(node)
node = py4hw.RemoveAssert().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
if (self.inc.get() == 1):
self.q.prepare((self.q.get() + 1))
end
We change match and if instructions for verilog equivalents
node = py4hw.ReplaceMatch().visit(node)
node = py4hw.ReplaceIf().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (self.inc.get() == 1):
self.q.prepare((self.q.get() + 1))
end
TBD
node = py4hw.ReplaceParameterCalls().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (self.inc.get() == 1):
self.q.prepare((self.q.get() + 1))
end
Replace put, get and prepare calls with Verilog assigns
node = py4hw.ReplaceWireCalls().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.PropagateConstants().process(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.ReplaceExpr().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
We do it several times
node = py4hw.ReplaceOperators().visit(node)
node = py4hw.ReplaceOperators().visit(node) # repeat to handle Compare
node = py4hw.ReplaceOperators().visit(node) # repeat to handle Compare
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
wiresAndVars = py4hw.ReplaceWiresAndVariables(initExtracter.ports, initExtracter.variables, initExtracter.arguments)
node = wiresAndVars.visit(node)
print(wiresAndVars.variables.values())
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
dict_values([])
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.ReplaceConstant().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.ReplaceAssign().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.ReplaceIfExp().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node = py4hw.ReplaceDocStrings().visit(node)
if (True): Py4hwUnparser(node)
if (False): print(ast.dump(node, indent=' '))
always @(posedge clk)
begin
v_if (inc == 1):
q v_= (q + 1)
end
node.wires.variables = list(wiresAndVars.variables.values())
print(node.wires.variables)
[]
rtl = py4hw.VerilogGenerator(block)
print( rtl.getVerilogForHierarchy())
transpiling sequential /HWSystem[HWSystem]/CounterBehavioural[counter]
// This file was automatically created by py4hw Verilog generator
module CounterBehavioural (
input clk,
input inc,
output reg [31:0] q);
// Code generated from clock method
// wire/variable declaration
// initial
initial
begin
end
// process
always @(posedge clk)
begin
if (inc==1)
begin
q<=q+1;
end
end
endmodule