Py4Hw User Guide> 7.1 RTL Generation from Behavioural Descriptions¶
Next: 8.1 Dealing with unique instances, unique blocks and parametetric blocks¶
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)
First step, collecting information from the constructor¶
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 ?
Transform clock into a VerilogBody¶
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
Remove Prints and Asserts¶
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
Replace Conditionals¶
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
Replace Parameter Calls¶
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 Wire Calls¶
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
Propagate Constants¶
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
Replace Expressions¶
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
Replace Operators¶
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
Replace Wires and Variables¶
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
Replace Constants¶
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
Replace Assigns¶
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
Replace If Expressions¶
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
Replace Doc Strings¶
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
Others¶
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
Summary¶
- Verilog generation of behavioural circuits is based on transpilation, the analysis and modification of the Python AST of the models