Skip to content

Commit

Permalink
Merge pull request #4 from HEP-SoC/gen_lds_update
Browse files Browse the repository at this point in the history
Linker generation update
  • Loading branch information
benoitdenkinger authored Jun 4, 2024
2 parents 3bb1aa9 + 0077cf1 commit 68bb1e0
Show file tree
Hide file tree
Showing 16 changed files with 560 additions and 381 deletions.
4 changes: 2 additions & 2 deletions SoCMakeConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ include("${CMAKE_CURRENT_LIST_DIR}/cmake/tmrg/tmrv/tmrv.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/fw_utils.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/add_tests.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/linker_script/gen_lds.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/linker_script/src/lds_props.cmake")

set(IBEX_TOOLCHAIN "${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/toolchains/ibex_toolchain.cmake" CACHE INTERNAL "IBEX_TOOLCHAIN")
set(IBEX_TOOLCHAIN_BASE "${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/toolchains/ibex_toolchain_base.cmake" CACHE INTERNAL "IBEX_TOOLCHAIN_BASE")
set(IBEX_TOOLCHAIN "${CMAKE_CURRENT_LIST_DIR}/cmake/firmware/toolchains/riscv_toolchain.cmake" CACHE INTERNAL "IBEX_TOOLCHAIN")

# ====================================
# ======== Documentation =============
Expand Down
152 changes: 81 additions & 71 deletions cmake/firmware/fw_utils.cmake
Original file line number Diff line number Diff line change
@@ -1,79 +1,93 @@
function(print_link_map EXE)
get_target_property(BINARY_DIR ${EXE} BINARY_DIR)

add_custom_command(TARGET ${EXE}
POST_BUILD
COMMAND cat ${BINARY_DIR}/map_file.map # TODO find where it is
COMMAND ${CMAKE_SIZE} "${BINARY_DIR}/${EXE}"
COMMENT "Printing the Map file from the linker for ${EXE}"
)

endfunction()

#[[[
# This function disassembles the given executable target to generate an assembly file.
#
# The function retrieves the binary directory of the target, sets the output assembly file name,
# and adds a custom command to disassemble the executable and store the result in the specified file.
#
# :param EXE: The executable target.
# :type EXE: string
#]]
function(disassemble EXE)
# Retrieve the binary directory of the target
get_target_property(BINARY_DIR ${EXE} BINARY_DIR)

# Set the paths for the executable and output assembly file
set(EXECUTABLE ${BINARY_DIR}/${EXE})
set(OUT_ASM_FILE "${BINARY_DIR}/${EXE}_asm.S")
set(OUT_ASM_FILE "${BINARY_DIR}/${EXE}_disasm.S")

# Add a custom command to disassemble the executable and generate the assembly file
add_custom_command(TARGET ${EXE}
POST_BUILD
BYPRODUCTS ${OUT_ASM_FILE}
COMMAND ${CMAKE_OBJDUMP} -DgrwCS ${EXECUTABLE} > ${OUT_ASM_FILE}
COMMENT "Dumping assembly from ${EXE}"
)
)

# Set a property on the target to store the path of the generated assembly file
set_property(TARGET ${EXE}
PROPERTY DISASM_FILE
${OUT_ASM_FILE}
)
endfunction()

function(gen_bin EXE)
get_target_property(BINARY_DIR ${EXE} BINARY_DIR)
set(EXECUTABLE ${BINARY_DIR}/${EXE})
set(BIN_FILE "${BINARY_DIR}/${EXE}.bin")
set(BIN_TEXT_FILE "${BINARY_DIR}/${EXE}_text.bin")
set(BIN_DATA_FILE "${BINARY_DIR}/${EXE}_data.bin")

get_target_property(BOOTLOADER ${EXE} BOOTLOADER)
if(BOOTLOADER)
set(TEXT_SECTION .bootloader)
else()
set(TEXT_SECTION .text)
endif()
add_custom_command(TARGET ${EXE}
POST_BUILD
BYPRODUCTS ${BIN_FILE} ${BIN_TEXT_FILE} ${BIN_DATA_FILE}
COMMAND ${CMAKE_OBJCOPY} -O binary ${EXECUTABLE} ${BIN_FILE}
COMMAND ${CMAKE_OBJCOPY} -O binary --only-section=${TEXT_SECTION} ${EXECUTABLE} ${BIN_TEXT_FILE}
COMMAND ${CMAKE_OBJCOPY} -O binary --remove-section=${TEXT_SECTION} ${EXECUTABLE} ${BIN_DATA_FILE}
COMMENT "Generating bin file from ${EXE}"
)

set_property(TARGET ${EXE} PROPERTY BIN_FILE ${BIN_FILE})
set_property(TARGET ${EXE} PROPERTY BIN_TEXT_FILE ${BIN_TEXT_FILE})
set_property(TARGET ${EXE} PROPERTY BIN_DATA_FILE ${BIN_DATA_FILE})
)
endfunction()

#[[[
# This function generates hex files for the given executable target.
#
# The function retrieves the binary directory of the target, parses the width arguments,
# and includes a utility script to find Python. It then retrieves binary file properties
# from the target, sets the appropriate sections for the bootloader, and generates hex files
# for each specified width.
#
# :param EXE: The executable target.
# :type EXE: string
#]]
function(gen_hex_files EXE)
# Retrieve the binary directory of the target
get_target_property(BINARY_DIR ${EXE} BINARY_DIR)

# Parse the width arguments
cmake_parse_arguments(ARG "" "" "WIDTHS" ${ARGN})

# Include the utility script to find Python
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../utils/find_python.cmake")
find_python3()

# Set the path for the executable
set(EXECUTABLE ${BINARY_DIR}/${EXE})
set(SOCMAKE_MAKEHEX_TOOL "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/scripts/makehex.py" CACHE STRING "Makehex tool")
gen_bin(${EXE})

# Retrieve binary file properties from the target
get_property(BIN_FILE TARGET ${EXE} PROPERTY BIN_FILE)
get_property(BIN_TEXT_FILE TARGET ${EXE} PROPERTY BIN_TEXT_FILE)
get_property(BIN_DATA_FILE TARGET ${EXE} PROPERTY BIN_DATA_FILE)

# Set default width to 32 if not specified
if(NOT ARG_WIDTHS)
set(ARG_WIDTHS 32)
endif()

# Determine the sections based on whether the target is a bootloader
get_target_property(BOOTLOADER ${EXE} BOOTLOADER)
if(BOOTLOADER)
set(TEXT_SECTION --only-section=.bootloader)
else()
set(TEXT_SECTION
--only-section=.vectors
--only-section=.init
--only-section=.fini
--only-section=.text
)
endif()

# Set the sections to be excluded for the data section
set(DATA_SECTION
--remove-section=.bootloader
--remove-section=.vectors
--remove-section=.init
--remove-section=.fini
--remove-section=.text
)

# Define allowed widths and iterate over specified widths to generate hex files
set(ALLOWED_WIDTHS 8 16 32 64)
foreach(width ${ARG_WIDTHS})
list(FIND ALLOWED_WIDTHS ${width} WIDTH_FIND)
Expand All @@ -82,57 +96,53 @@ function(gen_hex_files EXE)
set(HEX_FILE "${BINARY_DIR}/${EXE}_${width}bit.hex")
set(HEX_TEXT_FILE "${BINARY_DIR}/${EXE}_text_${width}bit.hex")
set(HEX_DATA_FILE "${BINARY_DIR}/${EXE}_data_${width}bit.hex")

# Add custom commands to generate hex files for the specified width
add_custom_command(TARGET ${EXE}
POST_BUILD
BYPRODUCTS ${HEX_FILE} ${HEX_TEXT_FILE} ${HEX_DATA_FILE}
COMMAND ${Python3_EXECUTABLE} ${SOCMAKE_MAKEHEX_TOOL} --width ${width} ${BIN_FILE} ${HEX_FILE} # TODO this is slowing down
COMMAND ${Python3_EXECUTABLE} ${SOCMAKE_MAKEHEX_TOOL} --width ${width} ${BIN_TEXT_FILE} ${HEX_TEXT_FILE}
COMMAND ${Python3_EXECUTABLE} ${SOCMAKE_MAKEHEX_TOOL} --width ${width} ${BIN_DATA_FILE} ${HEX_DATA_FILE}
COMMENT "Generating ${width} bit hex file file for ${EXE}"
)
COMMAND ${CMAKE_OBJCOPY} -O verilog --verilog-data-width=4 ${EXECUTABLE} ${HEX_FILE}
COMMAND ${CMAKE_OBJCOPY} -O verilog --verilog-data-width=4 --gap-fill 0x0000 ${TEXT_SECTION} ${EXECUTABLE} ${HEX_TEXT_FILE}
# TODO: find an automatic way to 'correct' the VMA for loading during simulation
COMMAND ${CMAKE_OBJCOPY} -O verilog --verilog-data-width=4 --gap-fill 0x0000 --adjust-vma=-0x10000000 ${DATA_SECTION} ${EXECUTABLE} ${HEX_DATA_FILE}
COMMENT "Generating ${width} bit hex file for ${EXE}"
)

# Set properties on the target to store the paths of the generated hex files
set_property(TARGET ${EXE} PROPERTY HEX_${width}bit_FILE ${HEX_FILE})
set_property(TARGET ${EXE} PROPERTY HEX_TEXT_${width}bit_FILE ${HEX_TEXT_FILE})
set_property(TARGET ${EXE} PROPERTY HEX_DATA_${width}bit_FILE ${HEX_DATA_FILE})
else()
message(FATAL_ERROR "\nWidth ${width} not allowed in gen_hex_files(), allowed values ${ALLOWED_WIDTHS}\n")
endif()
endforeach()

endfunction()

#[[[
# This function sets linker scripts for the given executable target.
#
# The function parses the linker script arguments and sets them as link options for the target.
# It also sets a property on the target to store the list of linker scripts.
#
# :param EXE: The executable target.
# :type EXE: string
#]]
function(set_linker_scripts EXE)
# Parse the linker script arguments
cmake_parse_arguments(ARG "" "" "LDS" ${ARGN})

# Ensure linker scripts are provided
if(NOT ARG_LDS)
message(FATAL_ERROR "Must provide one or more linker_scripts: LDS [fn,...]")
endif()

set(LINKER_SCRIPT_ARG "-Wl")
# Iterate over linker scripts and set them as link options for the target
foreach(lds ${ARG_LDS})
target_link_options(${PROJECT_NAME} PUBLIC
-T${lds}
)
)
endforeach()

# Set a property on the target to store the list of linker scripts
set_property(TARGET ${EXE} PROPERTY LINK_DEPENDS ${ARG_LDS})
endfunction()

# function(static_stack_analysis EXE)
# set(SSA_TOOL "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/scripts/ssa.py")
# get_target_property(BINARY_DIR ${EXE} BINARY_DIR)
# set(CFLOW_CALLSTACK "${BINARY_DIR}/${EXE}_cflow_callstack.txt")
#
# get_target_property(CPP_SOURCES ${EXE} SOURCES)
# list(FILTER CPP_SOURCES EXCLUDE REGEX "\\.[S|s]$") # Exclude asm files
#
# add_custom_command(TARGET ${EXE}
# POST_BUILD
# BYPRODUCTS ${CFLOW_CALLSTACK}
# COMMAND cflow ${CPP_SOURCES} > ${CFLOW_CALLSTACK} || (exit 0)
# COMMAND ${Python3_EXECUTABLE} ${SSA_TOOL} -f ${CFLOW_CALLSTACK} -d ${BINARY_DIR}/CMakeFiles/${EXE}.dir/ || (exit 0)
# COMMENT "Running static stack analysis on ${EXE}, error on this command can be ignored"
# )
#
# endfunction()
#
31 changes: 27 additions & 4 deletions cmake/firmware/linker_script/gen_lds.cmake
Original file line number Diff line number Diff line change
@@ -1,52 +1,76 @@
#[[[
# This function generates linker script (LDS) file for the specified IP library.
#
# :param IP_LIB: The name of the IP library target.
# :type IP_LIB: str
#]]
function(gen_lds IP_LIB)
# Parse the arguments passed to the function
cmake_parse_arguments(ARG "NODEBUG" "OUTDIR" "PARAMETERS" ${ARGN})
# Check for any unrecognized arguments
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}")
endif()

# Include necessary CMake files
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../hwip.cmake")
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../fw_utils.cmake")
include("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../utils/find_python.cmake")
find_python3()

# Define the path to the linker script generation tool
set(LDS_GEN_TOOL "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/src/gen_linker_script.py")

# Set the IP library to the last specified
ip_assume_last(IP_LIB ${IP_LIB})
# Get the binary directory of the IP library
get_target_property(BINARY_DIR ${IP_LIB} BINARY_DIR)

# Set the output directory for the linker script
if(NOT ARG_OUTDIR)
set(OUTDIR ${BINARY_DIR}/lds)
else()
set(OUTDIR ${ARG_OUTDIR})
endif()

# Set the debug flag if NODEBUG is not specified
if(ARG_NODEBUG)
set(ARG_NODEBUG)
else()
set(ARG_NODEBUG --debug)
endif()

# Used to overwrite the top level parameters
# Initialize string to store overwritten parameters
set(OVERWRITTEN_PARAMETERS "")
# Process any specified parameters to overwrite
if(ARG_PARAMETERS)
foreach(PARAM ${ARG_PARAMETERS})
string(APPEND OVERWRITTEN_PARAMETERS "-p${PARAM}")
endforeach()
endif()

# Create the output directory if it does not exist
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTDIR})

# Get the system RDL files for the IP library
get_ip_sources(RDL_FILES ${IP_LIB} SYSTEMRDL)

# Check if system RDL files exist
if(NOT RDL_FILES)
message(FATAL_ERROR "Library ${IP_LIB} does not have RDL_FILES property set, unable to run ${CMAKE_CURRENT_FUNCTION}")
endif()

# Set the path to the generated linker script file
set(LDS_FILE "${OUTDIR}/${IP_LIB}.lds")

# Mark the linker script file as generated
set_source_files_properties(${LDS_FILE} PROPERTIES GENERATED TRUE)
# Set linker script file as IP library source
ip_sources(${IP_LIB} LINKER_SCRIPT ${LDS_FILE})

# Set the stamp file path
set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_${CMAKE_CURRENT_FUNCTION}.stamp")
# Add custom command to generate linker script
add_custom_command(
OUTPUT ${LDS_FILE} ${STAMP_FILE}
COMMAND ${Python3_EXECUTABLE} ${LDS_GEN_TOOL}
Expand All @@ -60,13 +84,12 @@ function(gen_lds IP_LIB)
COMMENT "Running ${CMAKE_CURRENT_FUNCTION} on ${IP_LIB}"
)

# Add custom target for generating linker script
add_custom_target(
${IP_LIB}_lds
DEPENDS ${RDL_FILES} ${STAMP_FILE}
)

# Add dependency on the custom target for the IP library
add_dependencies(${IP_LIB} ${IP_LIB}_lds)

endfunction()

include("${CMAKE_CURRENT_LIST_DIR}/src/lds_props.cmake")
Loading

0 comments on commit 68bb1e0

Please sign in to comment.