Python_Project---PYCPLD

36365 ワード

Introduction
GithubにはオープンソースのプロジェクトPYCPLDがあります.その機能はpythonスクリプト言語でCPLDを統合するエンジニアリングです.This project is to use the python script to integrate different CPLD IP into target board system. Currently the Altera MAX-II EP570P is supported with Quartus-15.1 version. and the Arduio compatible CPLD board is added in reference design.
そのテンプレートに従ってIPモジュールを追加するだけで、他の統合IPモジュールがシステムに組み込まれ、対応するQuartusエンジニアリングを生成することでpythonスクリプトを実行して自動的に生成することができます.
本文は主にそのコードを解析し,その実現原理を述べる.
githubでソースコードを検索して勉強することができます.何か質問があれば、コメントにメッセージを残して、できるだけ早く答えます.
Auto_Generate.py
python./Auto_Generate.py twrkv58f220m_SDK_2_0_Enc.yml形式で指定されたymlファイルが存在する場合、g.generate(boardyml)を実行してファイルを生成します.
g = ips.generator、だから私たちはipsに重点を置いています.generator.
'''import             ,              。
  import                :
1、python      
2、python      
3、     '''
import sys, os
import ips.generator

__PATH__ = os.path.dirname(os.path.abspath(__file__)).replace('\\','/')

if __name__ == '__main__':
    print (" Verilog Code Generator Tool")
    print (" -----------------------------------
"
) g = ips.generator if len(sys.argv)== 1: boardyml = raw_input("Please input yaml file path
>>"
) else: boardyml = sys.argv[1] if os.path.exists(boardyml) is True: g.generate(boardyml) else: print ("error path: %s"%boardyml) sys.stdout.flush() os.system("pause")

ips.generator
ips.generatorはipsフォルダの下にあるgeneratorモジュールです.
def generate(boardyml):
  ## context data
  context = {
#    'MODULE_TEXT': 'module top(clk,rst_n,rs232_rx,rs232_tx, %s, led);',
    'MODULE_TEXT': 'module top(clk,rst_n,rs232_rx, %s, led);',
    'INOUT_TEXT': '',
    'WIRE_TEXT': '',
    'REG_TEXT': '',
    'ASSIGN_TEXT': '',
    'IP_TEXT': '',
    'INIT_REG_TEXT': '',
    'CMD_CASE_TEXT': '',
    'RST_REG_TEXT': '',
    'DFT_REG_TEXT': ''
  }

  ret = analysis_yml(boardyml, context)

  if ret is None:
    return None
  ## render template with context data
  print "rend data*******************"
  topv = engine.render(VERILOG_TEMPLATE_PATH, context)

  save_template_to_file(VERILOG_OUTPUT_PATH, 'top', topv)
  print VERILOG_OUTPUT_PATH

ret = analysis_yml(boardyml, context)
解析yml
#analyze the boardyml and update the context for tempale
def analysis_yml(boardyml, context):
  io_dic, bus_scope = analysis.analysis_context(boardyml)
  print "io_dic"
  print io_dic
  print "bus_scope"
  print bus_scope
  if io_dic is None:
    return None
  IO_DIC = io_dic
  INIT_REG_TEXT, CMD_CASE_TEXT, RST_REG_TEXT, DFT_REG_TEXT = cpld.Code_verilog_reg(io_dic)  
  context['INIT_REG_TEXT'] = as_escaped(INIT_REG_TEXT)
  context['CMD_CASE_TEXT'] = as_escaped(CMD_CASE_TEXT)
  context['RST_REG_TEXT'] = as_escaped(RST_REG_TEXT)
  context['DFT_REG_TEXT'] = as_escaped(DFT_REG_TEXT)
  MODULE_TEXT = cpld.Module_header(bus_scope)
  context['MODULE_TEXT'] = as_escaped(MODULE_TEXT)
  INOUT_TEXT = cpld.inout(bus_scope)
  context['INOUT_TEXT'] = as_escaped(INOUT_TEXT)
  WIRE_TEXT = cpld.wire(io_dic)
  context['WIRE_TEXT'] = as_escaped(WIRE_TEXT)
  REG_TEXT = cpld.reg(io_dic)
  context['REG_TEXT'] = as_escaped(REG_TEXT)
  ASSIGN_TEXT = cpld.assign(io_dic, bus_scope)
  context['ASSIGN_TEXT'] = ASSIGN_TEXT
  IP_TEXT = cpld.ip_caller(io_dic)
  context['IP_TEXT'] = IP_TEXT
  return True

io_dic, bus_scope = analysis.analysis_context(boardyml)
analysisモジュールのanalysisを呼び出す_context関数.IOポートの辞書とbusを返します.scope神馬東東の.
##################################################################
#AUTO-GENERATE-API
##################################################################
def analysis_context(boardyml):
  global CPLD_IO_TABLE
  global BOARD_CPLD_IO_PATH
  global CPLD_QSF_TEMPL_PATH
  global CPLD_TCL_TEMPL_PATH

  file_name = os.path.basename(boardyml)
  dir_name  = os.path.dirname(boardyml)
  ##########################################################################
  #Choose cpld. According cpld versio and type, load cpld io and template path
  ##########################################################################
  #TYPE: TWR CPLD
  if  re.search("twr", file_name):
    #TWR CPLD V2
    if re.search("v2", dir_name):
      cpld_io_path  = BOARD_CPLD_IO_PATH['TWR_CPLD_V2']
      CPLD_QSF_TEMPL_PATH = BOARD_CPLD_QSF_TEMPLATE['TWR_CPLD_V2']
      CPLD_TCL_TEMPL_PATH = BOARD_CPLD_TCL_TEMPLATE['TWR_CPLD_V2']
    #TWR CPLD V1
    else:
      cpld_io_path  = BOARD_CPLD_IO_PATH['TWR_CPLD_V1']
      CPLD_QSF_TEMPL_PATH = BOARD_CPLD_QSF_TEMPLATE['TWR_CPLD_V1']
      CPLD_TCL_TEMPL_PATH = BOARD_CPLD_TCL_TEMPLATE['TWR_CPLD_V1']

  #TYPE: FRDM CPLD
  elif re.search("frdm", file_name):
    #FRDM CPLD V2
    if re.search("v2", dir_name):
      cpld_io_path  = BOARD_CPLD_IO_PATH['FRDM_CPLD_V2']
      CPLD_QSF_TEMPL_PATH = BOARD_CPLD_QSF_TEMPLATE['FRDM_CPLD_V2']
      CPLD_TCL_TEMPL_PATH = BOARD_CPLD_TCL_TEMPLATE['FRDM_CPLD_V2']


    #FRDM CPLD V1
    else:
      cpld_io_path  = BOARD_CPLD_IO_PATH['FRDM_CPLD_V1']
      CPLD_QSF_TEMPL_PATH = BOARD_CPLD_QSF_TEMPLATE['FRDM_CPLD_V1']
      CPLD_TCL_TEMPL_PATH = BOARD_CPLD_TCL_TEMPLATE['FRDM_CPLD_V1']

  #TYPE: CPLD EP570
  elif re.search("ep570", dir_name):
    cpld_io_path  = BOARD_CPLD_IO_PATH['EP570_CPLD']
    CPLD_QSF_TEMPL_PATH = BOARD_CPLD_QSF_TEMPLATE['EP570_CPLD']
    CPLD_TCL_TEMPL_PATH = BOARD_CPLD_TCL_TEMPLATE['EP570_CPLD']


  else:
    print "Error: Unknown CPLD Version!"
    return

  #load yml files
  modules = file_load_yml( boardyml )
  CPLD_IO_TABLE = file_load_yml( cpld_io_path )

  if modules is None or CPLD_IO_TABLE is None:
    print ("Error: Load file error.")
    return None

  #map cpld pins with boards(target & assitant), return a dic
  io_dic = map_io(modules)
  print io_dic
  if io_dic is None:
    return None, None


  bus_scope, Used_io = cpld_io_analyze(io_dic)

  if Used_io is None:
    print ("Error: Bus Definition!")
    return None, None

  #Generate  my_uart_top.v
  #---------------------------------------------
  #---------------------------------------------
  return io_dic, bus_scope
  #generate_top_v_file(internal_io_dic, external_io_dic, bus_scope)

cpld_のロードioとqsf、tclファイルのファイルパス
osを通るpath.义齿path.dirnameはboardymlの完全なパスとファイル名を取得します.
reを通るsearch(「twr」、file_name)とre.search(「v 2」,dir_name)が正則的に一致し、対応するCPLDボードを選択します.CPLDボードのバージョンに応じて、対応するcpld_をロードします.ioファイルパスとquartusエンジニアリングに必要なqsf tclファイルのファイルパス
tclファイル:tclのフルネームはTool command languageで、文字列に基づくコマンド言語であり、tcl言語は解釈言語であり、コンパイルと結合を必要とせず、他のshell言語のように、直接各文を順次解釈して実行する.FPGAのアプリケーションではtclファイルにtcl言語を用いてピンを構成し,tclファイルにはピンの構成情報のみが含まれている.
qsfファイル:qsfのフルネームはQuartus Settings Fileの略です.エンジニアリング情報、デバイス情報、ピンコンストレイント、コンパイルコンストレイント、Classic TimingAnalyzer用のタイミングコンストレイントなど、Quartusエンジニアリングのすべてのコンストレイントが含まれています.
(Quartus Settings Fileはpycpld/ips/templateディレクトリの下にあります).
modules = file_load_yml( boardyml )
boardymlファイルを開きyamlを使用します.loadをロードします.
yaml.loadの役割はyaml形式のドキュメントをpythonオブジェクトに変換することです.
##################################################################
#FILE OPERATIONS SECTION
##################################################################

def file_load_yml(fp):
  try:
    f = open(fp,"r")
  except IOError:
    print ("Error: No such file %s"%fp)
    return None

  c = {}
  try:
    c = yaml.load(f)
  except yaml.parser.ParserError:
    print ("Error: There may be some format errors exist in Yaml File.
%s"
%fp) return None f.close() return c

以下は節選のtwrkv 58 f 220 m_SDK_2_0_test.ymlファイル:
#Descriptions:
#CMD: cpld command
#A: Assitant Board
#T: Target Board
#FUNC0: the function of this pin connection
#DIRECTION: A2T  T2A  T2T

#SINGLE: default 0, if the pin header is single on FRDM-Board, this should be set 1
SINGLE: 0

PMH:
    CMD: PMH
    SW2:
        T_PIN: CPLD_IO4
        A_PIN: B58
        DIRECTION: A2T
        NOTE: SW2-1 fly wire to J8-1


ENC:
    IP: __ENC
    CMD: ENC
    pha_out:
        PIN: A34
        DIRECTION: out
    phb_out:
        PIN: A33
        DIRECTION: out
    index_out:
        PIN: CPLD_IO50
        DIRECTION: out

主に2つのケースがあります.1つ目は簡単な配線です.2つ目はipモジュールの出力入力構成です(ENC直交符号化モジュール)
生成されたdictの構造は次のとおりです.
  : [(ioin0,inout0,comments),...(ioin,ioout,comments), ]

  Struct io_dic:
  {
    : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
    : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
    : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
    ...
  }

CPLD_IO_TABLE = file_load_yml( cpld_io_path )
cpld_を読み込むioというIOピン情報のymlファイルは、同じくCPLD_について返されますIO情報の辞書.
次は、セクションで選択したcpld_です.io.xxx.ymlファイル:
1:
    TYPE: NONE
    FUNC: L4
    PINS: CPLD_IO1
    #used by L4 with J7 2-3.
2:
    TYPE: NONE
    FUNC: NONE
    PINS: CPLD_IO2
21:
    TYPE: A
    FUNC: SPI1_CLK_K70
    PINS: B7_K70
# 22:   //   cpld TDI TDO TCK TMS         GND IO
#     TYPE: NONE
#     FUNC: NONE
#     PINS: X
# 23:
#     TYPE: NONE
#     FUNC: NONE
#     PINS: X
# 24:
#     TYPE: NONE
#     FUNC: NONE
#     PINS: X
# 25:
#     TYPE: NONE
#     FUNC: NONE
#     PINS: X
26:
    TYPE: T
    FUNC: NONE
    PINS: B7

上はtwrv 2_cpld_io.ymlの一節.TYPEには3つの定義があります.
  • NONEは、このIOポートがプレートのjumperに接続され、ELEPプレートに接続されず、特定の接続補助プレートK 70のIOもしないことを表す.
  • Aは、このIOポートを代表してプレートのjumperに接続され、特定の接続補助プレートK 70のIOとして機能する.
  • TはTarget Boardの意味で、これらのcpldのio口はELEPボードの
  • につながっています.
    注意CPLD_IO_TABLEはグローバル変数です
    io_dic = map_io(modules)
    前述のmodules(ユーザ定義のIO配線のymlファイル生成によるdict)マッチング(定義されたCPLDボードのIOポート説明生成によるdict)により、マッチングされたIOポート配線のdict:io_dic
    CPLD_IO_TABLEはグローバル変数なので、ここでmap_ioはmodulesというパラメータしか持っていません.map_io—>look_up—>parser_pin_in_out—>look_up_table—>global CPLD_IO_TABLE
    次はポイントです.ディレクトリを最高にアップグレードします.
    map_io
    def map_io(modules):
      '''
      : [(ioin0,inout0,comments),...(ioin,ioout,comments), ]
    
      Struct io_dic:
      {
        : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
        : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
        : [(ioin0,inout0,comments),...(ioin,ioout,comments), module_name],
        ...
      }
    
      '''
      SingF  = 0
      switch = False
    
      try:
        SingF = modules["SINGLE"]
        del modules["SINGLE"]
      except KeyError:
        pass
    
      if SingF == 1: switch = True
    
      io_dic = {}
    
      #look up io in cpld for each module
      for module_name in modules:
        module = modules[module_name]
        CMD = module["CMD"]
    
        if ( "IP" in module):
          io_dic[CMD] = look_up_ip(module, switch)
          if io_dic[CMD] is None:
            print ("Error: Pin error, Please check your yml file at module: \'%s\'."%module_name)
            return None
          io_dic[CMD].append(module_name)
        else:
          io_dic[CMD] = look_up(module, switch)
          if io_dic[CMD] is None:
            print ("Error: Pin error, Please check your yml file at module: \'%s\'."%module_name)
            return None
          io_dic[CMD].append(module_name)
      build_in_module = {'IP': '__UART', 'CMD': 'BIM'}
      io_dic['UART'] = look_up_ip(build_in_module, switch)
      return io_dic

    エントリパラメータmodulesは次の辞書です.
    {'ENC': {'phb_out': {'DIRECTION': 'out', 'PIN': 'A33'}, 'IP': '__ENC', 'CMD': 'ENC', 'pha_out': {'DIRECTION': 'out', 'PIN': 'A34'}, 'index_out': {'DIRECTION': 'out', 'PIN': 'CPLD_IO50'}}, 'PMH': {'CMD': 'PMH', 'SW2': {'NOTE': 'SW2-1 fly wire to J8-1', 'DIRECTION': 'A2T', 'A_PIN': 'B58', 'T_PIN': 'CPLD_IO4'}}}
    
      for module_name in modules:
        module = modules[module_name]
        CMD = module["CMD"]
    dict = {"b":"2", "a":"1",
            "c":"3", "e":"5", "d":"4"}
    
    for key,value in dict.items():
        print(key,value)
    
    # -- OUTPUT --
    # a 1
    # c 3
    # b 2
    # e 5
    # d 4
    '''
                         ,        ,Python          ,         ,
    '''

    moduleは
    {'phb_out': {'DIRECTION': 'out', 'PIN': 'A33'}, 'IP': '__ENC', 'CMD': 'ENC', 'pha_out': {'DIRECTION': 'out', 'PIN': 'A34'}, 'index_out': {'DIRECTION': 'out', 'PIN': 'CPLD_IO50'}}

    CMDが手に入れたのは『ENC』
    内蔵IPモジュールの場合はlook_を呼び出しますup_ip(module, switch)
    接続されている場合はlook_を呼び出します.up(module, switch)
    上の2つのステップで生成されたio_dicにUARTというデフォルトモジュールを追加します(各エンジニアリングではCMDを受信するためにUARTのIPが必要です)
      build_in_module = {'IP': '__UART', 'CMD': 'BIM'}
      io_dic['UART'] = look_up_ip(build_in_module, switch)

    look_up_ip
    {‘phb_out’: {‘DIRECTION’: ‘out’, ‘PIN’: ‘A33’}, ‘IP’: ‘__ENC’, ‘CMD’: ‘ENC’, ‘pha_out’: {‘DIRECTION’: ‘out’, ‘PIN’: ‘A34’}, ‘index_out’: {‘DIRECTION’: ‘out’, ‘PIN’: ‘CPLD_IO50’}}
    #look up io in cpld_io dic for internal IPs
    #return:  [(CpldIOIn0,CpldIoOut0,Comments),....]    type[list]
    #switch: single row need to remap, this value set True
    def look_up_ip(module, switch):
      global IP_DICT
      mname = module["CMD"]
      print "module name: " + mname
      #Python   (Dictionary) keys()      list      dict    key。
      confuncs = module.keys()
      #remove()                    。
      confuncs.remove("CMD")
    
      #search the IP package and find the matching IPs
    
    '''
    pkgutil.iter_modules(path=None, prefix='')
    Yields (module_finder, name, ispkg) for all submodules on path, or, if path is None, all top-level modules on sys.path.
    
    path should be either None or a list of paths to look for modules in.
    
    prefix is a string to output on the front of every module name on output.
    '''
      pkgpath = os.path.dirname(ip.__file__)
      cips = [name for _, name, _ in pkgutil.iter_modules([pkgpath])]
    
    '''
    
    ['base_ip', 'enc', 'i2c_master', 'i2c_slave', 'i2c_slave_for_case', 'pwm_capture', 'pwm_out', 'qdec', 'spi_master', 'spi_slave', 'spi_slave_new', 'sw_pulse', 'uart', 'uart7bit', 'uart8bit']
    
    '''
      ip_name = None
      for cip in cips:
        print "checking " + cip
        #print_classes()
        #func = getattr(ip, cip + '.get_ip_name')
        sub_module = getattr(ip, cip)
        #
    
        func = getattr(sub_module, 'get_ip_name')
        #
    
        sub_module_class = getattr(sub_module, func())
        #ips.ip.enc.enc_partial.ENC
    
    
        sub_module_class_instance = sub_module_class("")
        #
    
        if sub_module_class_instance.matched_id(module['IP']) is True:
          print "ID matched for " + module['IP']
          ip_name = sub_module_class_instance.__class__.__name__
          if sub_module_class_instance.__class__.__name__ not in IP_DICT['class']:
            IP_DICT['inst'].append(sub_module_class_instance)
            IP_DICT['class'].append(sub_module_class_instance.__class__.__name__)
            if "ALT" in module:
              print "*****************************"
              print "set inst alt %s"%(module["ALT"])
              sub_module_class_instance.set_alt(module["ALT"])
              sub_module_class_instance.ALT_CMD = mname
          elif "ALT" in module:
            print "*****************************"
            print "set inst alt %s"%(module["ALT"])
            IP_DICT['inst'].append(sub_module_class_instance)
            IP_DICT['class'].append(sub_module_class_instance.__class__.__name__)
            sub_module_class_instance.set_alt(module["ALT"])
            sub_module_class_instance.ALT_CMD = mname
          break
    
      if ip_name is None:
        print "No matching IP found for " + module['IP']
        return None
      confuncs.remove("IP")
      if "ALT" in confuncs:
        confuncs.remove("ALT")
      confuncs.sort()
    
      look_result = []
    
      if mname == "BIM":
        look_result.append(('','',"//build in %s"%module['IP'], 'BIM', ip_name))
        return look_result
    
      for conname in confuncs:
        connection = module[conname]
        CPinIN, CPinOUT, comments, ip_ping_wire = parser_pin_ip(connection, conname)
    
        if CPinIN is not None or CPinOUT is not None:
          look_result.append((CPinIN,CPinOUT,comments, ip_ping_wire, ip_name))
        else:
          return None
    
      return look_result

    look_up
    #look up io in cpld_io dic
    #return:  [(CpldIOIn0,CpldIoOut0,Comments),....]    type[list]
    #switch: single row need to remap, this value set True
    def look_up(module, switch):
      mname = module["CMD"]
    
      confuncs = module.keys()
      confuncs.remove("CMD")
      confuncs.sort()
    
      look_result = []
    
      for conname in confuncs:
        connection = module[conname]
        CPinIN, CPinOUT, comments = parser_pin_in_out(connection, switch)
    
        if CPinIN is not None and CPinOUT is not None:
          look_result.append((CPinIN,CPinOUT,comments))
        else:
          return None
    
      return look_result

    parser_pin_in_out
    look_up_table