Monday, December 3, 2012

CMake, CTest, and CDash for Xilinx FPGAs, part 2

This is a follow-up to my post from yesterday. I've made major progress and, if I knew things would go this fast, wouldn't have written that post until today :)

The current version of the script is able to compile HDL designs to both FPGA bitstreams and ISim test cases, as well as running the simulation executable in the form of a unit test. There's no direct support for CPLDs yet (which will pretty much involve refactoring the code to call xst out into a function and adding some code to call cpldfit) but that will come soon.

Also on the to-do list:
  • Support for invoking PlanAhead in both pre-synthesis and post-PAR modes
  • Support for programming bitstreams to FPGAs and CPLDs using iMPACT via a "make program" type target
  • Support for indirect programming (need to generate ROM files etc)
  • Support for programming bitstreams to FPGAs and CPLDs using my JTAG toolchain (uses libftdi and the Digilent API as back ends, so I can integrate FT2232-based debug/program modules into my boards and not rely on the Xilinx platform cable)
  • Support for more command-line flags for the toolchain. Right now all of the ngdbuild/map/par/trce/bitgen flags are hard-coded and only about half of the default xst flags are changeable.
  • Support for mixed hardware/ISim/C++ cosimulation (using pipes and $fread/$fwrite to bridge to ISim and JTAG to bridge to real hardware)
Without further ado, here's a usage example for the major new feature:

########################################################################################################################
# Global synthesis flags

set(XILINX_FILTER_FILE ${CMAKE_CURRENT_SOURCE_DIR}/filter.filter)

set(XST_KEEP_HIERARCHY Soft)
set(XST_NETLIST_HIERARCHY Rebuilt)

########################################################################################################################
# Current top-level module
add_fpga_target(
 OUTPUT
  JtagTest
 TOP_LEVEL
  ${CMAKE_CURRENT_SOURCE_DIR}/JtagTest.v
 CONSTRAINTS
  ${CMAKE_SOURCE_DIR}/ucf/JtagTest.ucf
 DEVICE
  xc6slx45-3-csg324
 SOURCES
  ${CMAKE_CURRENT_SOURCE_DIR}/debug/JtagDebugController.v
  ${CMAKE_CURRENT_SOURCE_DIR}/noc/common/NOCArbiter.v
  ${CMAKE_CURRENT_SOURCE_DIR}/noc/common/NOCRouterCore.v
  ${CMAKE_CURRENT_SOURCE_DIR}/noc/common/NOCRouterMux.v
  ${CMAKE_CURRENT_SOURCE_DIR}/noc/rpc/RPCRouter.v
  ${CMAKE_CURRENT_SOURCE_DIR}/noc/rpc/RPCRouterExitQueue.v
  ${CMAKE_CURRENT_SOURCE_DIR}/peripherals/NetworkedButtonArray.v
  ${CMAKE_CURRENT_SOURCE_DIR}/peripherals/NetworkedLEDBank.v
  ${CMAKE_CURRENT_SOURCE_DIR}/util/MediumBlockRamFifo.v
  ${CMAKE_CURRENT_SOURCE_DIR}/util/SwitchDebouncer.v
  ${CMAKE_CURRENT_SOURCE_DIR}/util/SwitchDebouncerBlock.v
  ${CMAKE_CURRENT_SOURCE_DIR}/util/ThreeStageSynchronizer.v
 )

The add_fpga_target function uses the OUTPUT parameter as the base name for all of the temporary files created during compilation.

The TOP_LEVEL parameter specifies the top-level source file for the module. For now the base name of the TOP_LEVEL file is used as the top-level module name; in the future I may make the TOP_LEVEL parameter specify the module name and then add that file (along with all the others) to the SOURCES section.

DEVICE and SOURCES should be self-explanatory. Note that the Xilinx toolchain expects the part numbers in a specific format - there's a dash between the speed grade and the package (unlike the actual part numbers) and the temperature range is not specified.

Full source for this monster is below. Now that it's reached the point of basic usability I won't be blogging on it anymore except to announce the stable release on Google Code once I've worked out the rest of the kinks and bugs.

########################################################################################################################
# @file FindXilinx.cmake
# @author Andrew D. Zonenberg
# @brief Xilinx ISE toolchain CMake module
########################################################################################################################

########################################################################################################################
# Autodetect Xilinx paths (very hacky for now)

# TODO: Print messages only when configuring

# Find /opt/Xilinx or similar
find_file(XILINX_PARENT NAMES Xilinx PATHS /opt)
if(XILINX_PARENT STREQUAL "XILINX_PARENT-NOTFOUND")
 message(FATAL_ERROR "No Xilinx toolchain installation found")
endif()

# Find /opt/Xilinx/VERSION
# TODO: Figure out a better way of doing this
find_file(XILINX NAMES 14.3 PATHS ${XILINX_PARENT})
if(XILINX STREQUAL "XILINX-NOTFOUND")
 message(FATAL_ERROR "No ISE 14.3 installation found")
endif()
#message(STATUS "Found Xilinx toolchain... ${XILINX}")

# Set current OS architecture (TODO: autodetect)
set(XILINX_ARCH lin64)

# Find fuse
find_program(FUSE names fuse PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/" NO_DEFAULT_PATH)
if(FUSE STREQUAL "FUSE-NOTFOUND")
 message(FATAL_ERROR "No Xilinx fuse installation found")
endif()
#message(STATUS "Found Xilinx fuse... ${FUSE}")

# Find xst
find_file(XST NAMES xst PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(XST STREQUAL "XST-NOTFOUND")
 message(FATAL_ERROR "No Xilinx xst installation found")
endif()
#message(STATUS "Found Xilinx xst... ${XST}")

# Find ngdbuild
find_file(NGDBUILD NAMES ngdbuild PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(NGDBUILD STREQUAL "NGDBUILD-NOTFOUND")
 message(FATAL_ERROR "No Xilinx ngdbuild installation found")
endif()
#message(STATUS "Found Xilinx ngdbuild... ${NGDBUILD}")

# Find map
find_file(MAP NAMES map PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(MAP STREQUAL "MAP-NOTFOUND")
 message(FATAL_ERROR "No Xilinx map installation found")
endif()
#message(STATUS "Found Xilinx map... ${MAP}")

# Find par
find_file(PAR NAMES par PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(PAR STREQUAL "PAR-NOTFOUND")
 message(FATAL_ERROR "No Xilinx par installation found")
endif()
#message(STATUS "Found Xilinx par... ${PAR}")

# Find trce
find_file(TRCE NAMES trce PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(TRCE STREQUAL "TRCE-NOTFOUND")
 message(FATAL_ERROR "No Xilinx trce installation found")
endif()
#message(STATUS "Found Xilinx trce... ${TRCE}")

# Find bitgen
find_file(BITGEN NAMES bitgen PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(BITGEN STREQUAL "BITGEN-NOTFOUND")
 message(FATAL_ERROR "No Xilinx bitgen installation found")
endif()
#message(STATUS "Found Xilinx bitgen... ${BITGEN}")

########################################################################################################################
# Argument parsing helper

macro(xilinx_parse_args _output _top_level _ucf _device _sources)
 set(${_top_level} FALSE)
 set(${_output} FALSE)
 set(${_ucf} FALSE)
 set(${_device} FALSE)
 set(${_sources})
 set(_found_sources FALSE)
 set(_found_device FALSE)
 set(_found_output FALSE)
 set(_found_ucf FALSE)
 set(_found_top_level FALSE)
 foreach(arg ${ARGN})
  if(${arg} STREQUAL "TOP_LEVEL")
   set(_found_top_level TRUE)
  elseif(${arg} STREQUAL "SOURCES")
   set(_found_sources TRUE)
  elseif(${arg} STREQUAL "CONSTRAINTS")
   set(_found_ucf TRUE)
  elseif(${arg} STREQUAL "DEVICE")
   set(_found_device TRUE)
  elseif(${arg} STREQUAL "OUTPUT")
   set(_found_output TRUE)
  elseif(${_found_sources})
   list(APPEND ${_sources} ${arg})
  elseif(${_found_device})
   if(${_device})
    message(FATAL_ERROR "Multiple devices specified in xilinx_parse_args")
   else()
    set(${_device} ${arg})    
   endif()
  elseif(${_found_ucf})
   if(${_ucf})
    message(FATAL_ERROR "Multiple constraint files specified in xilinx_parse_args")
   else()
    set(${_ucf} ${arg})    
   endif()
  elseif(${_found_top_level})
   if(${_top_level})
    message(FATAL_ERROR "Multiple top-level files specified in xilinx_parse_args (${_top_level})")
   else()
    set(${_top_level} ${arg})    
   endif()
  elseif(${_found_output})
   if(${_output})
    message(FATAL_ERROR "Multiple outputs specified in xilinx_parse_args")
   else()
    set(${_output} ${arg})    
   endif()
  else()
   message(FATAL_ERROR "Unrecognized command ${arg} in xilinx_parse_args")
  endif()
 endforeach()
endmacro()

########################################################################################################################
# Default flags for fuse
set(FUSE_FLAGS "-intstyle ise -incremental -lib unisims_ver -lib unimacro_ver -lib xilinxcorelib_ver -lib secureip")

########################################################################################################################
# ISim executable generation

function(add_isim_executable OUTPUT_FILE )
  
 # Parse args
 xilinx_parse_args(OUTFNAME TOP_LEVEL UCF DEVICE SOURCES ${ARGN})
 
 # Get base name without extension of the top-level module
 get_filename_component(TOPLEVEL_BASENAME ${TOP_LEVEL} NAME_WE )
 
 # Write the .prj file
 set(PRJ_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}.prj")
 file(WRITE ${PRJ_FILE} "verilog work \"${TOP_LEVEL}\"\n")
 foreach(f ${SOURCES})
  file(APPEND ${PRJ_FILE} "verilog work \"${f}\"\n")
 endforeach()
 file(APPEND ${PRJ_FILE} "verilog work \"${XILINX}/ISE_DS/ISE/verilog/src/glbl.v\"\n")
 
 # Write the run-fuse wrapper script
 set(FUSE_ERR_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}_err.log")
 set(FUSE_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}_build.log")
 set(FUSE_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/runfuse${OUTPUT_FILE}.sh")
 file(WRITE ${FUSE_WRAPPER} "#!/bin/bash\n")
 file(APPEND ${FUSE_WRAPPER} "cd ${CMAKE_CURRENT_BINARY_DIR}\n")
 #file(APPEND ${FUSE_WRAPPER} "source ${XILINX}/ISE_DS/settings64.sh\n")
 file(APPEND ${FUSE_WRAPPER} "${FUSE} ${FUSE_FLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE} -prj ${PRJ_FILE}")
 file(APPEND ${FUSE_WRAPPER} "   work.${TOPLEVEL_BASENAME} work.glbl > ${FUSE_LOG} 2> ${FUSE_ERR_LOG}\n")
 file(APPEND ${FUSE_WRAPPER} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${FUSE_WRAPPER} "    cat ${FUSE_ERR_LOG} | grep \"ERROR\"\n")
 file(APPEND ${FUSE_WRAPPER} "    exit 1;\n")
 file(APPEND ${FUSE_WRAPPER} "fi\n")
 file(APPEND ${FUSE_WRAPPER} "exit 0;\n")
 execute_process(COMMAND chmod +x ${FUSE_WRAPPER})
 
 # Main compile rule
 # TODO: tweak this
 add_custom_target(
  ${OUTPUT_FILE} ALL
  COMMAND ${FUSE_WRAPPER}
  DEPENDS ${SOURCES} ${TOP_LEVEL}
  COMMENT "Building ISim executable ${OUTPUT_FILE}..."
 )
 
 # Write the tcl script
 set(TCL_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}.tcl")
 file(WRITE ${TCL_FILE} "onerror {resume}\n")
 file(APPEND ${TCL_FILE} "wave add /\n")
 file(APPEND ${TCL_FILE} "run 1000 ns;\n")
 file(APPEND ${TCL_FILE} "exit;\n")
 
 # Write the run-test wrapper script
 set(TEST_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/run${OUTPUT_FILE}.sh")
 file(WRITE ${TEST_WRAPPER} "#!/bin/bash\n")
 file(APPEND ${TEST_WRAPPER} "cd ${CMAKE_CURRENT_BINARY_DIR}\n")
 file(APPEND ${TEST_WRAPPER} "source ${XILINX}/ISE_DS/settings64.sh\n")
 file(APPEND ${TEST_WRAPPER} "./${OUTPUT_FILE} -tclbatch ${TCL_FILE} -intstyle silent -vcdfile ${OUTPUT_FILE}.vcd -vcdunit ps || exit 1\n")
 file(APPEND ${TEST_WRAPPER} "cat isim.log | grep -q FAIL\n")
 file(APPEND ${TEST_WRAPPER} "if [ \"$?\" != \"1\" ]; then\n")
 file(APPEND ${TEST_WRAPPER} "    exit 1;\n")
 file(APPEND ${TEST_WRAPPER} "fi\n")
 execute_process(COMMAND chmod +x ${TEST_WRAPPER})
 
endfunction()

########################################################################################################################
# Test generation
#
# Usage:
# add_isim_test(NandGate
# TOP_LEVEL
#  ${CMAKE_CURRENT_SOURCE_DIR}/testNandGate.v
# SOURCES 
#  ${CMAKE_SOURCE_DIR}/hdl/NandGate.v
# )

function(add_isim_test TEST_NAME)

 # Parse args
 xilinx_parse_args(OUTPUT TOP_LEVEL UCF DEVICE SOURCES ${ARGN})

 # Add the sim executable
 add_isim_executable(test${TEST_NAME}
  TOP_LEVEL
   ${TOP_LEVEL}
  SOURCES 
   ${SOURCES}
  )

 add_test(${TEST_NAME}
  "${CMAKE_CURRENT_BINARY_DIR}/runtest${TEST_NAME}.sh")
 set_property(TEST ${TEST_NAME} APPEND PROPERTY DEPENDS test${TEST_NAME})


endfunction()

########################################################################################################################
# Default flags for Xilinx toolchain

# Compiler flags
set(XST_MAX_FANOUT 100000)
set(XST_OPT_MODE Speed)
set(XST_OPT_LEVEL 1)
set(XST_KEEP_HIERARCHY No)
set(XST_NETLIST_HIERARCHY As_Optimized)
set(XST_RESOURCE_SHARING Yes)
set(XST_RAM_EXTRACT Yes)
set(XST_SHREG_MIN_SIZE 2)
set(XST_REGISTER_BALANCING No)

set(XILINX_FILTER_FILE FALSE)

########################################################################################################################
# Xilinx FPGA bitstream generation

function(add_fpga_target)
 
 # Parse args
 xilinx_parse_args(OUTFNAME TOP_LEVEL UCF DEVICE SOURCES ${ARGN})
 
 # Get base name without extension of the top-level module
 get_filename_component(TOPLEVEL_BASENAME ${TOP_LEVEL} NAME_WE )
 
 # Set the filter flag
 SET(XILINX_FILTER_FLAG "")
 if(XILINX_FILTER_FILE)
  SET(XILINX_FILTER_FLAG "-filter ${XILINX_FILTER_FILE}")
 ENDIF()
 
 # Write the .prj file
 set(PRJ_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.prj")
 file(WRITE ${PRJ_FILE} "verilog work \"${TOP_LEVEL}\"\n")
 foreach(f ${SOURCES})
  file(APPEND ${PRJ_FILE} "verilog work \"${f}\"\n")
 endforeach()
 file(APPEND ${PRJ_FILE} "verilog work \"${XILINX}/ISE_DS/ISE/verilog/src/glbl.v\"\n")
 
 # Create the XST input script
 set(XST_DIR "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_xst")
 file(MAKE_DIRECTORY ${XST_DIR})
 set(XST_TMPDIR "${XST_DIR}/projnav.tmp")
 file(MAKE_DIRECTORY ${XST_TMPDIR})
 set(XST_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.xst")
 set(XST_SYR_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.syr")
 file(WRITE  ${XST_SCRIPT_FILE} "set -tmpdir \"${XST_TMPDIR}\"\n")
 file(APPEND ${XST_SCRIPT_FILE} "set -xsthdpdir ${XST_DIR}\n")
 file(APPEND ${XST_SCRIPT_FILE} "run\n")
 file(APPEND ${XST_SCRIPT_FILE} "-ifn ${PRJ_FILE}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-ofn ${OUTFNAME}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-ofmt NGC\n")
 file(APPEND ${XST_SCRIPT_FILE} "-p ${DEVICE}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-top ${TOPLEVEL_BASENAME}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-slice_utilization_ratio 100\n")
 file(APPEND ${XST_SCRIPT_FILE} "-bram_utilization_ratio 100\n")
 file(APPEND ${XST_SCRIPT_FILE} "-dsp_utilization_ratio 100\n")
 file(APPEND ${XST_SCRIPT_FILE} "-bufg 16\n")
 file(APPEND ${XST_SCRIPT_FILE} "-hierarchy_separator /\n")
 file(APPEND ${XST_SCRIPT_FILE} "-bus_delimiter <>\n")
 file(APPEND ${XST_SCRIPT_FILE} "-case Maintain\n")
 file(APPEND ${XST_SCRIPT_FILE} "-max_fanout ${XST_MAX_FANOUT}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-opt_mode ${XST_OPT_MODE}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-opt_level ${XST_OPT_LEVEL}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-keep_hierarchy ${XST_KEEP_HIERARCHY}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-netlist_hierarchy ${XST_NETLIST_HIERARCHY}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-resource_sharing ${XST_RESOURCE_SHARING}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-ram_extract ${XST_RAM_EXTRACT}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-shreg_min_size ${XST_SHREG_MIN_SIZE}\n")
 file(APPEND ${XST_SCRIPT_FILE} "-register_balancing ${XST_REGISTER_BALANCING}\n")
 
 #-power NO
 #-iuc NO
 #-rtlview Yes
 #-glob_opt AllClockNets
 #-read_cores YES
 #-write_timing_constraints NO
 #-cross_clock_analysis NO
 #-lc Auto
 #-reduce_control_sets Auto
 #-fsm_extract YES -fsm_encoding Auto
 #-safe_implementation No
 #-fsm_style LUT
 #-ram_style Auto
 #-rom_extract Yes
 #-shreg_extract YES
 #-rom_style Auto
 #-auto_bram_packing NO
 #-async_to_sync NO
 #-use_dsp48 Auto
 #-iobuf YES
 #-register_duplication YES
 #-optimize_primitives NO
 #-use_clock_enable Auto
 #-use_sync_set Auto
 #-use_sync_reset Auto
 #-iob Auto
 #-equivalent_register_removal YES
 #-slice_utilization_ratio_maxmargin 5

 # Create the run-XST script
 set(XST_BUILD_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_xst.log")
 set(XST_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runXST_${OUTFNAME}.sh")
 file(WRITE  ${XST_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${XST_RUN_SCRIPT} "${XST} -intstyle xflow ${XILINX_FILTER_FLAG} -ifn ${XST_SCRIPT_FILE} -ofn ${XST_SYR_FILE} > ${XST_BUILD_LOG}\n")
 file(APPEND ${XST_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${XST_RUN_SCRIPT} "    cat ${XST_BUILD_LOG} | grep \"ERROR\"\n")
 file(APPEND ${XST_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${XST_RUN_SCRIPT} "fi\n")
 file(APPEND ${XST_RUN_SCRIPT} "cat ${XST_SYR_FILE} | grep \"WARNING\"\n")
 file(APPEND ${XST_RUN_SCRIPT} "exit 0;\n")
 execute_process(COMMAND chmod +x ${XST_RUN_SCRIPT})
  
 # Synthesize
 set(NGC_FILE "${OUTFNAME}.ngc")
 add_custom_command(
  OUTPUT ${NGC_FILE}
  COMMAND ${XST_RUN_SCRIPT}
  DEPENDS ${SOURCES} ${TOP_LEVEL} ${UCF}
  COMMENT "Synthesizing NGC object ${NGC_FILE}"
 )
 
 # Create the run-NGDBUILD script
 set(NGDBUILD_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_ngdbuild.log")
 set(NGDBUILD_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runNGDBUILD_${OUTFNAME}.sh")
 set(NGDBUILD_BLD_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.bld")
 file(WRITE  ${NGDBUILD_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "${NGDBUILD} -intstyle ise ${XILINX_FILTER_FLAG} -dd _ngo -nt timestamp -uc ${UCF} -p ${DEVICE} ${NGC_FILE} ${NGD_FILE} > ${NGDBUILD_LOG}\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "    cat ${NGDBUILD_LOG} | grep \"ERROR\"\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "fi\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "cat ${NGDBUILD_BLD_FILE} | grep \"WARNING\"\n")
 file(APPEND ${NGDBUILD_RUN_SCRIPT} "exit 0;\n")
 execute_process(COMMAND chmod +x ${NGDBUILD_RUN_SCRIPT})
 
 # Translate
 set(NGD_FILE "${OUTFNAME}.ngd")
 set(PCF_FILE "${OUTFNAME}.pcf")
 add_custom_command(
  OUTPUT ${NGD_FILE}
  COMMAND ${NGDBUILD_RUN_SCRIPT}
  DEPENDS ${UCF} ${NGC_FILE}
  COMMENT "Translating NGD object ${NGD_FILE}"
 )
 
 # Create the run-MAP script
 set(MAP_NCD_FILE "${OUTFNAME}_map.ncd")
 set(MAP_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_map.log")
 set(MAP_MRP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_map.mrp")
 set(MAP_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runMAP_${OUTFNAME}.sh")
 file(WRITE  ${MAP_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${MAP_RUN_SCRIPT} "${MAP} -intstyle ise -p ${DEVICE} -w ${XILINX_FILTER_FLAG} -logic_opt off -ol high -t 1 -xt 0 -register_duplication off -r 4 -global_opt off -mt 2 -ir off -pr off -lc off -power off -o ${MAP_NCD_FILE} ${NGD_FILE} ${PCF_FILE} > ${MAP_LOG}\n")
 file(APPEND ${MAP_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${MAP_RUN_SCRIPT} "    cat ${MAP_LOG} | grep \"ERROR\"\n")
 file(APPEND ${MAP_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${MAP_RUN_SCRIPT} "fi\n")
 file(APPEND ${MAP_RUN_SCRIPT} "cat ${MAP_MRP_FILE} | grep \"WARNING\"\n")
 file(APPEND ${MAP_RUN_SCRIPT} "exit 0;\n")
 execute_process(COMMAND chmod +x ${MAP_RUN_SCRIPT})
 
 # Map
 add_custom_command(
  OUTPUT ${MAP_NCD_FILE}
  COMMAND ${MAP_RUN_SCRIPT}
  DEPENDS ${UCF} ${NGD_FILE}
  COMMENT "Mapping native circuit description ${MAP_NCD_FILE}"
 )
 
 # Create the run-PAR script
 set(NCD_FILE "${OUTFNAME}.ncd")
 set(PAR_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_par.log")
 set(PAR_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runPAR_${OUTFNAME}.sh")
 set(PAR_PAR_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.par")
 file(WRITE  ${PAR_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${PAR_RUN_SCRIPT} "${PAR} -w -intstyle ise ${XILINX_FILTER_FLAG}  -ol high -mt 4 ${MAP_NCD_FILE} ${NCD_FILE} ${PCF_FILE} > ${PAR_LOG}\n")
 file(APPEND ${PAR_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${PAR_RUN_SCRIPT} "    cat ${PAR_LOG} | grep \"ERROR\"\n")
 file(APPEND ${PAR_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${PAR_RUN_SCRIPT} "fi\n")
 file(APPEND ${PAR_RUN_SCRIPT} "cat ${PAR_PAR_FILE} | grep \"WARNING\"\n")
 file(APPEND ${PAR_RUN_SCRIPT} "exit 0;\n")
 execute_process(COMMAND chmod +x ${PAR_RUN_SCRIPT})
 
 # PAR
 add_custom_command(
  OUTPUT ${NCD_FILE}
  COMMAND ${PAR_RUN_SCRIPT}
  DEPENDS ${UCF} ${MAP_NCD_FILE}
  COMMENT "Place and route native circuit description ${NCD_FILE}"
 )
 
 # Create the run-trce script
 set(TWX_FILE "${OUTFNAME}.twx")
 set(TWR_FILE "${OUTFNAME}.twr")
 set(TRCE_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_trce.log")
 set(TRCE_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runTRCE_${OUTFNAME}.sh")
 file(WRITE  ${TRCE_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "${TRCE} -intstyle ise -v 3 -s 2 -n 3 ${XILINX_FILTER_FLAG}  -fastpaths -xml ${TWX_FILE} ${NCD_FILE} -o ${TWR_FILE} ${PCF_FILE} -ucf ${UCF} > ${TRCE_LOG}\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    cat ${TRCE_LOG} | grep \"ERROR\"\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "fi\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "cat ${TWR_FILE} | grep \"0 timing errors detected\" > /dev/null\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    cat ${TWR_FILE} | grep \"paths analyzed\"\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    cat ${TWR_FILE} | grep \"timing errors detected\"\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    cat ${TWR_FILE} | grep \"Minimum period is\"\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    cat ${TWR_FILE} | grep \"Score\"\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${TRCE_RUN_SCRIPT} "fi\n")
 execute_process(COMMAND chmod +x ${TRCE_RUN_SCRIPT})
 
 # TRCE
 add_custom_command(
  OUTPUT ${TWR_FILE}
  COMMAND ${TRCE_RUN_SCRIPT}
  DEPENDS ${UCF} ${NCD_FILE}
  COMMENT "Generate static timing analysis ${TWR_FILE}"
 )

 # Create the bitgen input script
 set(BITGEN_SCRIPT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.ut")
 set(BIT_FILE "${OUTFNAME}.bit")
 file(WRITE  ${BITGEN_SCRIPT_FILE} "-w\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g DebugBitstream:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g Binary:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g CRC:Enable\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g Reset_on_err:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g ConfigRate:2\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g ProgPin:PullUp\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g TckPin:PullUp\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g TdiPin:PullUp\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g TdoPin:PullUp\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g TmsPin:PullUp\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g UnusedPin:PullDown\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g UserID:0xFFFFFFFF\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g ExtMasterCclk_en:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g SPI_buswidth:1\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g TIMER_CFG:0xFFFF\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g multipin_wakeup:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g StartUpClk:CClk\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g DONE_cycle:4\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g GTS_cycle:5\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g GWE_cycle:6\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g LCK_cycle:NoWait\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g Security:None\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g DonePipe:Yes\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g DriveDone:Yes\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g en_sw_gsr:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g drive_awake:No\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g sw_clk:Startupclk\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g sw_gwe_cycle:5\n")
 file(APPEND ${BITGEN_SCRIPT_FILE} "-g sw_gts_cycle:4\n")
 
 # Create the run-bitgen script
 set(BITGEN_LOG "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_bitgen.log")
 set(BITGEN_RUN_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/runBITGEN_${OUTFNAME}.sh")
 set(BITGEN_BGN_FILE "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.bgn")
 file(WRITE  ${BITGEN_RUN_SCRIPT} "#!/bin/bash\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "${BITGEN} -intstyle ise ${XILINX_FILTER_FLAG} -f ${BITGEN_SCRIPT_FILE} ${NCD_FILE} > ${BITGEN_LOG}\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "if [ \"$?\" != \"0\" ]; then\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "    cat ${BITGEN_LOG} | grep \"ERROR\"\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "    exit 1;\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "fi\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "cat ${BITGEN_BGN_FILE} | grep \"WARNING\"\n")
 file(APPEND ${BITGEN_RUN_SCRIPT} "exit 0;\n")
 execute_process(COMMAND chmod +x ${BITGEN_RUN_SCRIPT})
 
 # BITGEN
 # Must depend on trce in order for timing failure to prevent bitgen from running
 add_custom_target(
  ${OUTFNAME} ALL
  COMMAND ${BITGEN_RUN_SCRIPT}
  DEPENDS ${NCD_FILE} ${TWR_FILE}
  COMMENT "Generate FPGA bitstream ${BIT_FILE}"
  SOURCES ${NCD_FILE} ${TWR_FILE}
 )
 
 # Add additional make-clean files
 # Do not delete run scripts or toolchain input files, only outputs
 set_property(
  DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
  ${XST_SYR_FILE}
  ${XST_BUILD_LOG}
  ${NGDBUILD_LOG}
  ${NGDBUILD_BLD_FILE}
  ${PCF_FILE}
  ${MAP_LOG}
  ${MAP_MRP_FILE}
  ${PAR_LOG}
  ${PAR_PAR_FILE}
  ${TWX_FILE}
  ${TRCE_LOG}
  ${BITGEN_LOG}
  ${BIT_FILE}
  ${BITGEN_BGN_FILE}
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.lso"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.map"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_map.map"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_map.ngm"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_map.xrpt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_ngdbuild.xrpt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.ngm"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.pad"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_pad.csv"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_pad.txt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_par.xrpt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.ptwx"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_summary.xml"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.unroutes"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_usage.xml"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.xpi"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_xst.xrpt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}_bitgen.xwbt"
  "${CMAKE_CURRENT_BINARY_DIR}/${OUTFNAME}.drc"
  "${CMAKE_CURRENT_BINARY_DIR}/usage_statistics_webtalk.html"
  "${CMAKE_CURRENT_BINARY_DIR}/webtalk.log"
  "${CMAKE_CURRENT_BINARY_DIR}/par_usage_statistics.html"
  )

endfunction()

# TODO: planAhead
#planAhead -ise yes -m64 -log planAhead.log -journal planAhead.jou -source pa.fromNcd.tcl

#pa.fromHdl.tcl (pre-synthesis)
#create_project -name lx9-lvds-ioexpander -dir "/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/planAhead_run_1" -part xc6slx9tqg144-2
#set_param project.pinAheadLayout yes
#set srcset [get_property srcset [current_run -impl]]
#set_property target_constrs_file "TopLevel.ucf" [current_fileset -constrset]
#set hdlfile [add_files [list {TopLevel.v}]]
#set_property file_type Verilog $hdlfile
#set_property library work $hdlfile
#set_property top TopLevel $srcset
#add_files [list {TopLevel.ucf}] -fileset [get_property constrset [current_run]]
#open_rtl_design -part xc6slx9tqg144-2

#pa.fromNcd.tcl contents (post-PAR)
#create_project -name lx9-lvds-ioexpander -dir "/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/planAhead_run_1" -part xc6slx9tqg144-2
#set srcset [get_property srcset [current_run -impl]]
#set_property design_mode GateLvl $srcset
#set_property edif_top_file "/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/TopLevel.ngc" [ get_property srcset [ current_run ] ]
#add_files -norecurse { {/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander} }
#set_property target_constrs_file "TopLevel.ucf" [current_fileset -constrset]
#add_files [list {TopLevel.ucf}] -fileset [get_property constrset [current_run]]
#link_design
#read_xdl -file "/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/TopLevel.ncd"
#if {[catch {read_twx -name results_1 -file "/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/TopLevel.twx"} eInfo]} {
#   puts "WARNING: there was a problem importing \"/home/azonenberg/native/programming/verilogpractice/lx9-lvds-ioexpander/TopLevel.twx\": $eInfo"
#}

No comments:

Post a Comment