#!/usr/bin/env python # encoding: utf-8 # waf example, builds a Xilinx FPGA bitstream __copyright__ = '(c) Jérôme Carretero 2012' """ This script builds an FPGA bitstream in an automated fashion. The Xilinx ISE IDE does the same thing, but needs mouse interaction. Notes: - this is quite sad, but the Xilinx toolchain tools want to operate in the source folder, so top==out - Xilinx toolchain tools generate file with timestamps, so an unsignificant change can still trigger domino cascade of compilations. - a "xilinx" wrapper is used; this file performs set up of the PATH for Xilinx tools (not done yet by the wscript) TODO: - make a tool - remove hard-coded .xst / .ut data (ISE generates that from the .xise) - CPLD toolchain (only works for FPGA) """ top = out = "." # mandatory import os import shutil import waflib from lxml import etree def options(opt): pass def configure(cfg): pass def build(bld): if not os.path.exists("xst/projnav.tmp"): os.makedirs("xst/projnav.tmp") nsmap={"pn": "http://www.xilinx.com/XMLSchema"} xise = "waf_demo.xise" fn = "waf_demo" xml = etree.parse(xise) def get(txt): try: return xml.xpath('//pn:property[@pn:name = "%s"]/@pn:value' % txt, namespaces=nsmap)[0] except: pass device = get("Device") # or "xc3s1500" package = get("Package") # or "fg456" speed = get("Speed Grade") # or "-4" # Set .prj file contents and collect HDL sources hdl = [] prj = [] for x in xml.xpath('//pn:files/pn:file[@pn:type = "FILE_VHDL"]/@pn:name', namespaces=nsmap): prj.append('vhdl work "%s"' % x) hdl.append(x) for x in xml.xpath('//pn:files/pn:file[@pn:type = "FILE_VERILOG"]/@pn:name', namespaces=nsmap): prj.append('verilog work "%s"' % x) hdl.append(x) ucf = xml.xpath('//pn:files/pn:file[@pn:type = "FILE_UCF"]/@pn:name', namespaces=nsmap)[0] or "src/pci_7seg.ucf" def make_prj(self): self.outputs[0].write("\n".join(prj)) def make_xst(self): self.outputs[0].write(""" set -tmpdir "xst/projnav.tmp" set -xsthdpdir "xst" run -ifn %(fn)s.prj -ifmt mixed -ofn %(fn)s -ofmt NGC -p %(device)s%(speed)s-%(package)s -top %(fn)s -opt_mode Speed -opt_level 1 -iuc NO -keep_hierarchy No -netlist_hierarchy As_Optimized -rtlview Yes -glob_opt AllClockNets -read_cores YES -write_timing_constraints NO -cross_clock_analysis NO -hierarchy_separator / -bus_delimiter <> -case Maintain -slice_utilization_ratio 100 -bram_utilization_ratio 100 -verilog2001 YES -fsm_extract YES -fsm_encoding Auto -safe_implementation No -fsm_style LUT -ram_extract Yes -ram_style Auto -rom_extract Yes -mux_style Auto -decoder_extract YES -priority_extract Yes -shreg_extract YES -shift_extract YES -xor_collapse YES -rom_style Auto -auto_bram_packing NO -mux_extract Yes -resource_sharing YES -async_to_sync NO -mult_style Auto -iobuf YES -max_fanout 500 -bufg 8 -register_duplication YES -register_balancing No -slice_packing YES -optimize_primitives NO -use_clock_enable Yes -use_sync_set Yes -use_sync_reset Yes -iob Auto -equivalent_register_removal YES -slice_utilization_ratio_maxmargin 5 """ % locals()) def make_ut(self): self.outputs[0].write(""" -w -g DebugBitstream:No -g Binary:no -g CRC:Enable -g ConfigRate:6 -g CclkPin:PullUp -g M0Pin:PullUp -g M1Pin:PullUp -g M2Pin:PullUp -g ProgPin:PullUp -g DonePin:PullUp -g HswapenPin:PullUp -g TckPin:PullUp -g TdiPin:PullUp -g TdoPin:PullUp -g TmsPin:PullUp -g UnusedPin:PullDown -g UserID:0xFFFFFFFF -g DCMShutdown:Disable -g DCIUpdateMode:AsRequired -g StartUpClk:CClk -g DONE_cycle:4 -g GTS_cycle:5 -g GWE_cycle:6 -g LCK_cycle:NoWait -g Match_cycle:Auto -g Security:None -g DonePipe:No -g DriveDone:No""") bld( name='prj', target="%s.prj" % fn, rule=make_prj, source=[xise], ) bld( name='xst', target='%s.xst' % fn, rule=make_xst, source=[xise], ) bld( name='ut', target='%s.ut' % fn, rule=make_ut, source=[xise], ) bld( name='synth', target=['%s%s' % (fn, ext) for ext in ('.syr', '.ngc', '.ngr', '.lso', '_xst.xrpt') ], rule='xilinx xst -intstyle ise -ifn ${SRC[0].abspath()} -ofn ${TGT[0].abspath()}; true', source=['%s.xst' % fn, '%s.prj' % fn] + hdl, ) bld( name='ngdbuild', target=['%s%s' % (fn, ext) for ext in ('.ngd', '_ngdbuild.xrpt') ], rule='xilinx ngdbuild -intstyle ise -dd _ngo -nt timestamp -uc ${SRC[1].abspath()} -p %(device)s-%(package)s%(speed)s ${SRC[0].bldpath()} ${TGT[0].bldpath()}' % locals(), source=['%s.ngc' % fn, ucf], ) bld( name='map', target=['%s%s' % (fn, ext) for ext in ('_map.ncd', '.pcf', '_map.map', '_map.mrp', '_map.ngm', '_map.xrpt', '.bld') ], rule='xilinx map -intstyle ise -p %(device)s-%(package)s%(speed)s -cm area -ir off -pr b -c 100 -o ${TGT[0].bldpath()} ${SRC[0].bldpath()} ${TGT[1].bldpath()}' % locals(), source=['%s.ngd' % fn], ) bld( name='par', target=['%s%s' % (fn, ext) for ext in ('.ncd', '.pad', '.par', '.ptwx', '.unroutes', '.xpi', '_pad.csv', '_pad.txt', '_par.xrpt') ], rule='xilinx par -w -intstyle ise -ol high -t 1 ${SRC[0].bldpath()} ${TGT[0].bldpath()} ${SRC[1].bldpath()}', source=['%s_map.ncd' % fn, '%s.pcf' % fn], ) bld( name='trce', target=['%s%s' % (fn, ext) for ext in ('.twx', '.twr') ], rule='xilinx trce -intstyle ise -e 3 -s 4 -n 3 -xml ${TGT[0].bldpath()} ${SRC[0].bldpath()} -o ${TGT[1].bldpath()} ${SRC[1].bldpath()}; true', source=['%s_map.ncd' % fn, '%s.pcf' % fn], ) bld( name='bitgen', target=['%s%s' % (fn, ext) for ext in ('.bit', '.bgn', '.drc', '_bitgen.xwbt', '_summary.xml', '_usage.xml') ], rule='xilinx bitgen -intstyle ise -f ${SRC[1].bldpath()} ${SRC[0].bldpath()} ${TGT[0].bldpath()}', source=['%s.ncd' % fn, '%s.ut' % fn], ) if bld.cmd == 'clean': for tgen in bld.get_all_task_gen(): for tgt in waflib.Utils.to_list(tgen.target): if os.path.exists(tgt): os.remove(tgt) for x in ( 'usage_statistics_webtalk.html', 'webtalk_pn.xml', 'webtalk.log', ): if os.path.exists(x): os.remove(x) for x in ( '_ngo', '_xmsgs', 'iseconfig', 'xlnx_auto_0_xdb', 'xst', ): try: shutil.rmtree(x) except: pass def distclean(ctx): import os, shutil from waflib import Context for fn in os.listdir('.'): if fn.startswith(('.conf_check_', ".lock-w")) \ or fn in (Context.DBFILE, 'config.log') \ or fn == 'c4che': if os.path.isdir(fn): shutil.rmtree(fn) else: os.remove(fn)