########################################################################################################################
# @file FindXilinx.cmake
# @author Andrew D. Zonenberg
# @brief Xilinx ISE toolchain CMake module
########################################################################################################################
########################################################################################################################
# Autodetect Xilinx paths (very hacky for now)
# 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_file(FUSE NAMES fuse PATHS "${XILINX}/ISE_DS/ISE/bin/${XILINX_ARCH}/")
if(FUSE STREQUAL "FUSE-NOTFOUND")
message(FATAL_ERROR "No Xilinx fuse installation found")
endif()
message(STATUS "Found Xilinx fuse... ${FUSE}")
########################################################################################################################
# Argument parsing helper
macro(xilinx_parse_args _top_level _sources)
set(${_top_level} FALSE)
set(${_sources})
set(_found_sources 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(${_found_sources})
list(APPEND ${_sources} ${arg})
elseif(${_found_top_level})
if(${_top_level})
message(FATAL_ERROR "Multiple top-level files specified in xilinx_parse_args")
else()
set(${_top_level} ${arg})
endif()
else()
message(FATAL_ERROR "Unrecognized command ${arg} in xilinx_parse_args")
endif()
endforeach()
endmacro()
########################################################################################################################
# ISim executable generation
function(add_isim_executable OUTPUT_FILE )
# Parse args
xilinx_parse_args(TOP_LEVEL 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")
# Main compile rule
# TODO: tweak this
add_custom_target(
${OUTPUT_FILE} ALL
COMMAND ${FUSE} ${FUSE_FLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE} -prj ${PRJ_FILE}
work.${TOPLEVEL_BASENAME} work.glbl > ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}_build.log
2> ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE}_err.log
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")
add_custom_command(TARGET ${OUTPUT_FILE} POST_BUILD 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(TOP_LEVEL 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()