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.

In [1]:
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)
In [2]:
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.

In [3]:
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)

In [4]:
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'))
In [5]:
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

In [6]:
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

In [7]:
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

In [8]:
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

In [9]:
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

In [10]:
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¶

In [11]:
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¶

In [12]:
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

In [13]:
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¶

In [14]:
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¶

In [15]:
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¶

In [16]:
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¶

In [17]:
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¶

In [18]:
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¶

In [19]:
node.wires.variables = list(wiresAndVars.variables.values())

print(node.wires.variables)
[]
In [20]:
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