An example of nvdla ephon used to generate state machine code automatically

In NVIDIA Deep Learning Accelerator, a python script epython is used. Source code address:  . The full name of ephon is embedded python utility, which is simply used to preprocess python scripts embedded in verilog files. Using the simplicity of python syntax, we can automatically generate some repetitive and regular code.


Raise questions

When we write Verilog code, the state machine code is generally defined by parameter, as follows:

​localparam IDLE  = 3'd0;
localparam START = 3'd1;
localparam START = 3'd7;

If you need to insert a new state in the middle, you may need to modify both parts. One is that the bit width may be increased by 1, and the other is that all the States after the new insertion need to be increased by 1.

Binary, decimal and hexadecimal, which can be recoded by Vim or Emacs column editing, data filling and formatting. It would be a lot of trouble if it was gray code or single hot code.

So is there a simpler way?


An application of epython

We know that python can embed python script in verilog, so we can write a script to do this.

Suppose we have several States, which are described by python's list:


Can we automatically generate the definition of localparam based on this list? Our goal is to look like this:


My method steps are as follows:

1. Calculate the list length, that is, the number of States

2. Calculate the bit width of the state code, log2(stat_num), and then round it up

3. Convert 0,..., stat_num-1 to binary, decimal, hexadecimal, gray, onehot, etc

4. Format output

Isn't it simple?

For the simplicity of embedding python in verilog, I put the main implementation process in python module, and then import it. The final handwritten verilog code is as follows:

​//:| import vloglib
//:| st = ['IDLE', 'FULL', 'WIN', 'PROT', 'WR_FULL', 'WR_WIN']
//:| vloglib.stat_gen(st, 'bin')
//:) epython: generated_beg
//:) epython: generated_end

After executing ephon, it is as follows:

​//:| import vloglib
//:| st = ['IDLE', 'START', 'NEW', 'DO_SOMETHING', 'DONE']
//:| vloglib.stat_gen(st, 'bin')
//:) epython: generated_beg (DO NOT EDIT BELOW)
  localparam IDLE         = 3'b000;
  localparam START        = 3'b001;
  localparam NEW          = 3'b010;
  localparam DO_SOMETHING = 3'b011;
  localparam DONE         = 3'b100;
//:) epython: generated_end (DO NOT EDIT ABOVE)

The effect of gray code is as follows:

//:| import vloglib
//:| st = ['IDLE', 'START', 'NEW', 'DO_SOMETHING', 'DONE']
//:| vloglib.stat_gen(st, 'gray')
//:) epython: generated_beg (DO NOT EDIT BELOW)
  localparam IDLE         = 3'b000;
  localparam START        = 3'b001;
  localparam NEW          = 3'b011;
  localparam DO_SOMETHING = 3'b010;
  localparam DONE         = 3'b110;
//:) epython: generated_end (DO NOT EDIT ABOVE)

The effect of single hot code is as follows:

//:| import vloglib
//:| st = ['IDLE', 'START', 'NEW', 'DO_SOMETHING', 'DONE']
//:| vloglib.stat_gen(st, 'onehot')
//:) epython: generated_beg (DO NOT EDIT BELOW)
  localparam IDLE         = 5'b00001;
  localparam START        = 5'b00010;
  localparam NEW          = 5'b00100;
  localparam DO_SOMETHING = 5'b01000;
  localparam DONE         = 5'b10000;
//:) epython: generated_end (DO NOT EDIT ABOVE)

Is the effect OK? code analysis

The code is as follows:

import os
import sys
import re
import math
import bin2gray2bin

def stat_gen(l_stat, code='dec'):
    #calc bit width
    stat_num = len(l_stat)
    bit_width = math.ceil(math.log2(stat_num))

    #get the max lenght of strings in list
    l = [len(i) for i in l_stat]
    maxlenth = max(l)

    #print verilog code
    for i in range(len(l_stat)):
        n = '  localparam {}'.format(l_stat[i]).ljust(13 + maxlenth)
        c = ''

        if code == 'bin':
            t = '{:b}'.format(i)
            t = t.rjust(bit_width, '0')
            c = ' = {}\'b{};'.format(bit_width, t)

        elif code == 'dec':
            c = ' = {}\'d{};'.format(bit_width, i)

        elif code == 'hex':
            t = '{:x}'.format(i)
            t = t.rjust(math.ceil(bit_width/4), '0')
            c = ' = {}\'h{};'.format(bit_width, t)

        elif code == 'gray':
            t = '{:b}'.format(i)
            t = t.rjust(bit_width, '0')
            t = bin2gray2bin.bin2gray(t, bit_width)
            c = ' = {}\'b{};'.format(bit_width, t)

        elif code == 'onehot':
            if i == 0:
                t = '0' * (stat_num - 1) + '1'
                t = t[1:] + '0'
            c = ' = {}\'b{};'.format(stat_num, t)

        print(n + c)

Main points:

  • Math is a math library, which uses two functions log2() and ceil() to calculate the bit width

  • ljust(len) of string is the specified width len, which is left aligned. If it is insufficient, it is filled with spaces. Len is determined by the maximum length of the status string.

  • 2, X. hexadecimal can be formatted with sting.format(), then right aligned with rjust(len), and filled with '0'

  • Gray code calls a third-party library bin2gray2bin

  • Single hot code uses string sequence splicing to achieve the effect of left shift

Source download

Download address:


git clone


Published 6 original articles, won praise 11, visited 149
Private letter follow

Tags: Python Verilog github git

Posted on Thu, 05 Mar 2020 00:26:09 -0500 by Amit Rathi