diff --git a/SoCMakeConfig.cmake b/SoCMakeConfig.cmake index a200e0e..5770d8d 100644 --- a/SoCMakeConfig.cmake +++ b/SoCMakeConfig.cmake @@ -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 ============= diff --git a/cmake/firmware/fw_utils.cmake b/cmake/firmware/fw_utils.cmake index 6b5f61d..9a0f957 100644 --- a/cmake/firmware/fw_utils.cmake +++ b/cmake/firmware/fw_utils.cmake @@ -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) @@ -82,15 +96,19 @@ 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}) @@ -98,41 +116,33 @@ function(gen_hex_files EXE) 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() -# diff --git a/cmake/firmware/linker_script/gen_lds.cmake b/cmake/firmware/linker_script/gen_lds.cmake index 6485e87..eccc505 100644 --- a/cmake/firmware/linker_script/gen_lds.cmake +++ b/cmake/firmware/linker_script/gen_lds.cmake @@ -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} @@ -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") diff --git a/cmake/firmware/linker_script/src/gen_linker_script.py b/cmake/firmware/linker_script/src/gen_linker_script.py index b1f8414..36618b4 100644 --- a/cmake/firmware/linker_script/src/gen_linker_script.py +++ b/cmake/firmware/linker_script/src/gen_linker_script.py @@ -1,11 +1,35 @@ -from systemrdl import RDLCompiler, RDLCompileError, RDLListener, RDLWalker -from systemrdl.node import FieldNode, MemNode, Node, RootNode, AddressableNode, RegNode, SignalNode from typing import List import sys, os import jinja2 import argparse +from systemrdl import RDLCompiler, RDLCompileError, RDLListener, RDLWalker +from systemrdl.node import MemNode, Node + class RDL2LdsExporter(RDLListener): + """Export SystemRDL to Linker Description Script (LDS). + + Class methods: + + - :func:`enter_Mem` + - :func:`enter_Reg` + - :func:`isSwEx` + - :func:`isSwWr` + - :func:`isBoot` + - :func:`isStack` + - :func:`isData` + - :func:`isBss` + - :func:`get_sections_prop` + - :func:`getStackMem` + - :func:`getProgramMem` + - :func:`getDataMem` + - :func:`getBssMem` + - :func:`getBootMem` + - :func:`getSwAcc` + - :func:`write_sections` + - :func:`export` + - :func:`process_template`` + """ def __init__(self): self.memories = [] self.regs = [] @@ -16,51 +40,68 @@ def enter_Mem(self, node): def enter_Reg(self, node): if node.get_property("linker_symbol", default=False): - assert not any(reg.inst_name == node.inst_name for reg in self.regs), f"Only one register with linker_symbol property and the same instance name can exist, you probably instantiated \"{node.parent.orig_type_name}\" Addrmap multiple times" + assert not any(reg.inst_name == node.inst_name for reg in self.regs), \ + f"Only one register with linker_symbol property and the same \ + instance name can exist, you probably instantiated \ + {node.parent.orig_type_name} Addrmap multiple times" self.regs.append(node) def isSwEx(self, mem : MemNode) -> bool: + """Returns True if the section contains executable code.""" sections = self.get_sections_prop(mem) if "text" in sections: return True return False def isSwWr(self, mem : MemNode) -> bool: + """Returns True if section is writable by software.""" sections = self.get_sections_prop(mem) if any(item in sections for item in ["data", "bss", "stack"]): return True return False def isBoot(self, mem : MemNode) -> bool: + """Returns True if section is contains the bootloader.""" sections = self.get_sections_prop(mem) if "boot" in sections: return True return False def isStack(self, mem : MemNode) -> bool: + """Returns True if section contains the stack.""" sections = self.get_sections_prop(mem) if "stack" in sections: return True return False + def isHeap(self, mem : MemNode) -> bool: + """Returns True if section contains the heap.""" + sections = self.get_sections_prop(mem) + if "heap" in sections: + return True + return False + def isData(self, mem : MemNode) -> bool: + """Returns True if section contains data.""" sections = self.get_sections_prop(mem) if "data" in sections: return True return False def isBss(self, mem : MemNode) -> bool: + """Returns True if section contains the bss (uninitialized global or static variables).""" sections = self.get_sections_prop(mem) if "bss" in sections: return True return False - def get_sections_prop(self, m : MemNode): + def get_sections_prop(self, m : MemNode) -> List[str]: + """Returns a list of string corresponding to the section properties.""" sections = m.get_property('sections').split('|') for s in sections: assert_m = f"Illegal property for sections: {s} of memory: {m.inst_name} in addrmap: {m.parent.inst_name}" - assert s in ['text', 'data', 'bss', 'boot', 'stack'], assert_m + assert s in ['text', 'data', 'bss', 'boot', 'stack', 'heap'], assert_m return sections @@ -73,8 +114,18 @@ def getStackMem(self, mems: List[MemNode]) -> MemNode: return stack_mems[0] + def getHeapMem(self, mems: List[MemNode]) -> MemNode: + heap_mems = [] + for m in mems: + if self.isHeap(m): + heap_mems.append(m) + assert len(heap_mems) == 1, f"Exactly 1 memory with section heap is allowed and required {heap_mems}" # TODO + + return heap_mems[0] + def getProgramMem(self, mems: List[MemNode]) -> MemNode: + """Returns the program/instruction/text MemNode.""" prog_mems = [] for m in mems: if self.isSwEx(m) and not self.isBoot(m): @@ -84,6 +135,7 @@ def getProgramMem(self, mems: List[MemNode]) -> MemNode: return prog_mems[0] def getDataMem(self, mems: List[MemNode]) -> MemNode: + """Returns the data MemNode.""" data_mems = [] for m in mems: if self.isData(m): @@ -93,6 +145,7 @@ def getDataMem(self, mems: List[MemNode]) -> MemNode: return data_mems[0] def getBssMem(self, mems: List[MemNode]) -> MemNode: + """Returns the bss MemNode.""" data_mems = [] for m in mems: if self.isData(m): @@ -102,6 +155,7 @@ def getBssMem(self, mems: List[MemNode]) -> MemNode: return data_mems[0] def getBootMem(self, mems: List[MemNode]) -> "MemNode | None": + """Returns the boot MemNode.""" boot_mems = [] for m in mems: if self.isBoot(m): @@ -111,6 +165,7 @@ def getBootMem(self, mems: List[MemNode]) -> "MemNode | None": return boot_mems[0] if len(boot_mems) > 0 else None def getSwAcc(self, mem : MemNode) -> str: + """Returns the software access rights to the MemNode.""" out = "r" if self.isSwWr(mem): out = out + "w" @@ -118,24 +173,19 @@ def getSwAcc(self, mem : MemNode) -> str: out = out + "x" return out - def write_sections(self, mems : List[MemNode]) -> str: - out = "" - - return out - def export(self, node: Node, outfile : str, debug : bool = True, ): - # self.memories = self.find_memories(node) context = {'mems' : self.memories, 'debug' : debug, 'regs' : self.regs } - text = self.process_template(context, "lds.j2") + # text = self.process_template(context, "lds.j2") + text = self.process_template(context, "linker.lds.j2") # assert(node.type_name is not None) # out_file = os.path.join(outfile, node.type_name + ".lds") @@ -174,7 +224,7 @@ def main(): parser.add_argument('--rdlfiles', nargs="+", help='RDL input files') parser.add_argument('--outfile', required=True, help='Output lds file') - parser.add_argument('--debug', type=bool, default=False, help='Include debug section in the lds or discard it') + parser.add_argument('--debug', default=False, action="store_true", help='Include debug section in the lds or discard it') parser.add_argument('-p', '--param', nargs='+', help="Parameter to overwrite on top RDL module in the format 'PARAM=VALUE'") args = parser.parse_args() @@ -195,6 +245,7 @@ def main(): sys.exit(1) top_gen = root.children(unroll=True) + # Is this really needed? top = None for top in top_gen: top = top @@ -215,5 +266,3 @@ def main(): if __name__ == "__main__": main() - - diff --git a/cmake/firmware/linker_script/src/template/lds.j2 b/cmake/firmware/linker_script/src/template/linker.lds.j2 similarity index 55% rename from cmake/firmware/linker_script/src/template/lds.j2 rename to cmake/firmware/linker_script/src/template/linker.lds.j2 index 6229a2e..3ff1fc1 100644 --- a/cmake/firmware/linker_script/src/template/lds.j2 +++ b/cmake/firmware/linker_script/src/template/linker.lds.j2 @@ -1,4 +1,10 @@ -OUTPUT_ARCH(riscv) /* TODO allow other architectures */ +/* Copyright (c) 2024 CERN */ +/* SPDX-License-Identifier: Apache-2.0 */ + +OUTPUT_FORMAT("elf32-littleriscv") +OUTPUT_ARCH(riscv) + +ENTRY(_entry) MEMORY { {% for m in mems %} @@ -6,99 +12,108 @@ MEMORY { {% endfor %} } -{% set stack_mem = mems|getStackMem %} +{# {% set stack_mem = mems|getStackMem %} +{% set heap_mem = mems|getHeapMem %} #} {% set prog_mem = mems|getProgramMem %} {% set boot_mem = mems|getBootMem %} {% set data_mem = mems|getDataMem %} {% set bss_mem = mems|getBssMem %} -PROVIDE(_estack = ORIGIN( {{ stack_mem.parent.inst_name }}) + LENGTH( {{ stack_mem.parent.inst_name }} )); - -PROVIDE(_stext = ORIGIN( {{ prog_mem.parent.inst_name }} )); -PROVIDE(_start = ORIGIN( {{ prog_mem.parent.inst_name }} )); -PROVIDE(_program_memory = ORIGIN( {{ prog_mem.parent.inst_name }} )); - -PROVIDE (__executable_start = SEGMENT_START("text-segment", _start)); . = SEGMENT_START("text-segment", _start) + SIZEOF_HEADERS; - {% for reg in regs %} PROVIDE({{reg.inst_name}} = 0x{{ '%08x' % reg.absolute_address }}); {% endfor %} +SECTIONS +{ + /* we want a fixed boot point */ + PROVIDE(__boot_address = ORIGIN( {{ boot_mem.parent.inst_name }} )); -SECTIONS { + {% if boot_mem is not none %} + .bootloader : { + *bootloader.S.o*(*); + *bootloader.cpp.o*(*); + . = ALIGN(4); + } > {{ boot_mem.parent.inst_name }} + {% endif %} - {% if boot_mem is not none %} - .bootloader : { - *bootloader.S.o(.vectors); - *bootloader.S.o*(*); - *bootloader.cpp.o*(*); - . = ALIGN(4); - } > {{ boot_mem.parent.inst_name }} - {% endif %} + /* we want a fixed entry point */ + PROVIDE(__entry_address = ORIGIN( {{ prog_mem.parent.inst_name }} ) + 0x180); - .text : - { - . = ALIGN(4); - *crt0.S.o(.vectors); - *crt0.S.o(.start_section); - *crt0.S.o(.text); + /* stack and heap related settings */ + __stack_size = DEFINED(__stack_size) ? __stack_size : 0x800; + PROVIDE(__stack_size = __stack_size); + __heap_size = DEFINED(__heap_size) ? __heap_size : 0x800; - /* TODO maybe more relaxed garbage collection */ - KEEP (*crt0.S.o(*)) + /***************************************/ + /* PROGRAM / TEXT / READ-ONLY SECTIONS */ + /***************************************/ - KEEP (*(SORT_NONE(.init))) + /* interrupt vectors */ + .vectors (ORIGIN( {{ prog_mem.parent.inst_name }} )): + { + PROVIDE(__vector_start = .); + KEEP(*(.vectors)); + } > {{ prog_mem.parent.inst_name }} - *(.text.unlikely .text.*_unlikely .text.unlikely.*) - *(.text.exit .text.exit.*) - *(.text.startup .text.startup.*) - *(.text.hot .text.hot.*) - *(.text .stub .text.* .gnu.linkonce.t.*) - /* .gnu.warning sections are handled specially by elf32.em. */ - *(.gnu.warning) + /* crt0 init code */ + .init (__entry_address): + { + /* The _enter symbol is placed in the .text.entry section + * and must be placed at the beginning of the program */ + KEEP (*(.text.entry)) + KEEP (*(SORT_NONE(.init))) + /* Align on 4 bytes to avoid errors during hex generation */ + FILL(0xDEAD) + . = ALIGN(4); + } > {{ prog_mem.parent.inst_name }} + /* not used by RISC-V */ + .fini : + { KEEP (*(SORT_NONE(.fini))) + /* Align on 4 bytes to avoid errors during hex generation */ + FILL(0xDEAD) + . = ALIGN(4); } > {{ prog_mem.parent.inst_name }} - PROVIDE (__etext = .); - PROVIDE (_etext = .); - PROVIDE (etext = .); - - .rodata : + .text : { - *(.rodata .rodata.* .gnu.linkonce.r.*) - *(.rodata1) - *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) - } > {{ data_mem.parent.inst_name }} + *(.text.unlikely .text.unlikely.*) + *(.text.startup .text.startup.*) + *(.text .text.*) + *(.gnu.linkonce.t.*) + } > {{ prog_mem.parent.inst_name }} - .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } > {{ data_mem.parent.inst_name }} - + /*****************/ + /* DATA SECTIONS */ + /*****************/ - /* Adjust the address for the data segment. We want to adjust up to - the same address within the page on the next page up. */ - . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); - /* Exception handling */ - .preinit_array : + /* initialization and termination routines */ + .preinit_array : ALIGN(8) { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array)) PROVIDE_HIDDEN (__preinit_array_end = .); - } > {{ data_mem.parent.inst_name }} - .init_array : - { + } > {{ data_mem.parent.inst_name }} + + .init_array : ALIGN(8) + { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) PROVIDE_HIDDEN (__init_array_end = .); - } > {{ data_mem.parent.inst_name }} - .fini_array : - { + } > {{ data_mem.parent.inst_name }} + + .fini_array : ALIGN(8) + { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) PROVIDE_HIDDEN (__fini_array_end = .); - } > {{ data_mem.parent.inst_name }} - .ctors : - { + } > {{ data_mem.parent.inst_name }} + + .ctors : + { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it @@ -107,7 +122,7 @@ SECTIONS { linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o - is in. */ + is in. */ KEEP (*crtbegin.o(.ctors)) KEEP (*crtbegin?.o(.ctors)) /* We don't want to include the .ctor section from @@ -117,67 +132,94 @@ SECTIONS { KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) - } > {{ data_mem.parent.inst_name }} + } > {{ data_mem.parent.inst_name }} + .dtors : - { + { KEEP (*crtbegin.o(.dtors)) KEEP (*crtbegin?.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) - } > {{ data_mem.parent.inst_name }} - .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } > {{ data_mem.parent.inst_name }} - .dynamic : { *(.dynamic) } > {{ data_mem.parent.inst_name }} - . = DATA_SEGMENT_RELRO_END (0, .); - .data : - { - __DATA_BEGIN__ = .; - *(.data .data.* .gnu.linkonce.d.*) + } > {{ data_mem.parent.inst_name }} + + .rodata : { + *(.rdata) + *(.rodata .rodata.*) + *(.gnu.linkonce.r.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + } > {{ data_mem.parent.inst_name }} + + /* data sections for initalized data */ + .data : ALIGN(8) + { + *(.data .data.*) + *(.gnu.linkonce.d.*) SORT(CONSTRUCTORS) - *(.data1) - *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) - *(.sdata .sdata.* .gnu.linkonce.s.*) - . = ALIGN(4); - } > {{ data_mem.parent.inst_name }} - _edata = .; PROVIDE (edata = .); - - - - - __bss_start = .; - __BSS_START__ = .; - _bss_start = .; - .sbss : - { - *(.dynsbss) - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - } > {{ bss_mem.parent.inst_name }} - .bss : ALIGN(4) - { - *(.dynbss) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - /* Align here to ensure that the .bss section occupies space up to - _end. Align after .bss to ensure correct alignment even if the - .bss section disappears because there are no input sections. - FIXME: Why do we need it? When there is no .bss section, we don't - pad the .data section. */ - . = ALIGN(. != 0 ? 32 / 8 : 1); - } > {{ bss_mem.parent.inst_name }} - . = ALIGN(32 / 8); - . = SEGMENT_START("ldata-segment", .); - . = ALIGN(32 / 8); - __BSS_END__ = .; - _bss_end = .; - _align_begin = .; - .align_mem0 : { - . = ALIGN(3); - } > {{ bss_mem.parent.inst_name }} - __global_pointer$ = 0x{{ '%08x' % data_mem.absolute_address }} + 0x{{ '%08x' % (data_mem.size/2)|int }}; - _end = .; PROVIDE (end = .); - . = DATA_SEGMENT_END (.); - + /* The compiler uses this to access data in the .sdata, .data, .sbss and .bss + sections with fewer instructions (relaxation). This reduces code size. + */ + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.gnu.linkonce.s.*) + } > {{ data_mem.parent.inst_name }} + + /* Thread Local Storage sections */ + .tdata : ALIGN(8) + { + PROVIDE_HIDDEN ( __tdata_start = . ); + *(.tdata .tdata.*) + *(.gnu.linkonce.td.*) + } > {{ data_mem.parent.inst_name }} + + PROVIDE ( __edata = . ); + + /* zero initialized sections */ + .tbss : ALIGN(8) + { + PROVIDE( __bss_start = . ); + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + } > {{ data_mem.parent.inst_name }} + + .bss (NOLOAD): ALIGN(8) { + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss .bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + PROVIDE( __bss_end = . ); + } > {{ data_mem.parent.inst_name }} + + /* second level sbss and sdata, not needed for now */ + /* .sdata2 : {*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)} */ + /* .sbss2 : {*(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } */ + + .stack (NOLOAD) : ALIGN(16) /* This is a requirement of the ABI */ + { + PROVIDE( __stack_start = . ); + . = __stack_size; + PROVIDE( _sp = . ); + PROVIDE( __stack_end = . ); + } > {{ data_mem.parent.inst_name }} + + .heap (NOLOAD) : ALIGN(8) + { + PROVIDE( __end = . ); + PROVIDE( __heap_start = . ); + . = __heap_size; + PROVIDE( __heap_end = . ); + } > {{ data_mem.parent.inst_name }} + + /******************/ + /* DEBUG SECTIONS */ + /******************/ + {% if debug %} /* Stabs debugging sections. */ .stab 0 : { *(.stab) } > {{ data_mem.parent.inst_name }} @@ -221,5 +263,6 @@ SECTIONS { .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } > {{ data_mem.parent.inst_name }} {% endif %} /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(*) } > {{ data_mem.parent.inst_name }} + }; diff --git a/cmake/firmware/toolchains/ibex_toolchain.cmake b/cmake/firmware/toolchains/ibex_toolchain.cmake deleted file mode 100644 index 53e3692..0000000 --- a/cmake/firmware/toolchains/ibex_toolchain.cmake +++ /dev/null @@ -1,92 +0,0 @@ -if(ENV{RISCV_TOOLCHAIN}) - set(RISCV_GNU_PATH $ENV{RISCV_TOOLCHAIN}) -elseif(RISCV_TOOLCHAIN) - set(RISCV_GNU_PATH ${RISCV_TOOLCHAIN}) -else() - CPMAddPackage( - NAME toolchain - URL "https://github.com/lowRISC/lowrisc-toolchains/releases/download/20230427-1/lowrisc-toolchain-gcc-rv32imcb-20230427-1.tar.xz" - ) - set(RISCV_GNU_PATH ${toolchain_SOURCE_DIR}) -endif() - -set(CMAKE_SYSTEM_PROCESSOR riscv32) -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_ABI elf) - -set(TOOLCHAIN_PREFIX "${CMAKE_SYSTEM_PROCESSOR}-unknown-${CMAKE_SYSTEM_ABI}-") - -find_program(RISCV_C_COMPILER ${TOOLCHAIN_PREFIX}gcc HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++ HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_AR ${TOOLCHAIN_PREFIX}ar HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_ASM ${TOOLCHAIN_PREFIX}g++ HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_LINKER ${TOOLCHAIN_PREFIX}ld HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_OBJDUMP ${TOOLCHAIN_PREFIX}objdump HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_RANLIB ${TOOLCHAIN_PREFIX}ranlib HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_SIZE ${TOOLCHAIN_PREFIX}size HINTS ${RISCV_GNU_PATH}/bin) -find_program(RISCV_STRIP ${TOOLCHAIN_PREFIX}strip HINTS ${RISCV_GNU_PATH}/bin) - -set(CMAKE_C_COMPILER ${RISCV_C_COMPILER}) -set(CMAKE_CXX_COMPILER ${RISCV_CXX_COMPILER}) -set(CMAKE_ASM ${RISCV_ASM}) -set(CMAKE_AR ${RISCV_AR}) -set(CMAKE_LINKER ${RISCV_LINKER}) -set(CMAKE_OBJCOPY ${RISCV_OBJCOPY}) -set(CMAKE_OBJDUMP ${RISCV_OBJDUMP}) -set(CMAKE_RANLIB ${RISCV_RANLIB}) -set(CMAKE_SIZE ${RISCV_SIZE}) -set(CMAKE_STRIP ${RISCV_STRIP}) - - -get_filename_component(RISCV_TOOLCHAIN_PATH ${RISCV_CXX_COMPILER} DIRECTORY CACHE) -set(RISCV_TOOLCHAIN_PREFIX ${TOOLCHAIN_PREFIX} CACHE STRING "") - -# set(CMAKE_C_FLAGS ) # TODO write based on CXX FLAGS - -set(CMAKE_CXX_STANDARD 17) -set(CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(CMAKE_CXX_FLAGS "") -set(CMAKE_C_FLAGS "") -set(CMAKE_EXE_LINKER_FLAGS "") - -string(APPEND CMAKE_CXX_FLAGS " -mabi=ilp32") # int and pointers are 32bit, long 64bit, char 8bit, short 16bit -string(APPEND CMAKE_CXX_FLAGS " -march=rv32ic") # RV32 Integer, Compressed instruction set -if(DEBUG) - string(APPEND CMAKE_CXX_FLAGS " -g -O0") # Debug flags - string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-g") -else() - string(APPEND CMAKE_CXX_FLAGS " -Os") # Optimize for code size TODO move to release - string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--strip-debug") # https://web.archive.org/web/20220530212919/https://linux.die.net/man/1/ld -endif() -string(APPEND CMAKE_CXX_FLAGS " -fno-delete-null-pointer-checks") # Avoid gcc delete NULL pointer accesses, because something might be at 0 address on CPU -string(APPEND CMAKE_CXX_FLAGS " -fverbose-asm") -string(APPEND CMAKE_CXX_FLAGS " -fno-rtti") -string(APPEND CMAKE_CXX_FLAGS " -save-temps") -string(APPEND CMAKE_CXX_FLAGS " -fdevirtualize") -string(APPEND CMAKE_CXX_FLAGS " -fstack-usage") # Create stack usage reports .su files -string(APPEND CMAKE_CXX_FLAGS " -Wstack-usage=128") # Create warning if a function is using more than 128 bytes -string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra -Wshadow -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Wredundant-decls -pedantic") -string(APPEND CMAKE_CXX_FLAGS " -ffreestanding") # Standard library may not exist and program startup may not be at main, do not assume that standard function use usual definitions -string(APPEND CMAKE_CXX_FLAGS " -nostdlib") # Do not use the standard system startup files or libraries when linking https://cs107e.github.io/guides/gcc/ -string(APPEND CMAKE_CXX_FLAGS " -nodefaultlibs") # Do not use the standard system libraries when linking -# string(APPEND CMAKE_CXX_FLAGS " -fomit-frame-pointer -fno-exceptions -fno-asynchronous-unwind-tables -fno-unwind-tables") # Remove eh_frame sections and rest - - -string(APPEND CMAKE_EXE_LINKER_FLAGS " -mabi=ilp32 -march=rv32ic -ffreestanding -nostdlib -lgcc") -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--build-id=none") # Don't include build-id in the output (should be disabled by default?), saves 160bits https://interrupt.memfault.com/blog/gnu-build-id-for-firmware -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Bstatic") # Use only static libraries https://web.archive.org/web/20220530212919/https://linux.die.net/man/1/ld -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--print-memory-usage") # Print memory usage -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Map=map_file.map") - -# set(CMAKE_C_FLAGS_DEBUG ) # TODO -# set(CMAKE_C_FLAGS_RELEASE ) -# set(CMAKE_CXX_FLAGS_DEBUG ${CXX_FLAGS}) -# set(CMAKE_CXX_FLAGS_RELEASE ${CXX_FLAGS}) -set(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS}) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/firmware/toolchains/ibex_toolchain_base.cmake b/cmake/firmware/toolchains/ibex_toolchain_base.cmake deleted file mode 100644 index b1d99d9..0000000 --- a/cmake/firmware/toolchains/ibex_toolchain_base.cmake +++ /dev/null @@ -1,31 +0,0 @@ -set(RISCV_GNU_PATH $ENV{RISCV_TOOLCHAIN}) - -set(CMAKE_SYSTEM_PROCESSOR riscv32) -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_ABI elf) - -set(TOOLCHAIN_PREFIX "${CMAKE_SYSTEM_PROCESSOR}-unknown-${CMAKE_SYSTEM_ABI}-") - -set(CMAKE_AR "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}ar") -set(CMAKE_ASM "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}g++") -set(CMAKE_C_COMPILER "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}gcc") -set(CMAKE_CXX_COMPILER "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}g++") -set(CMAKE_LINKER "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}ld") -set(CMAKE_OBJCOPY "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}objcopy") -set(CMAKE_OBJDUMP "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}objdump") -set(CMAKE_RANLIB "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}ranlib") -set(CMAKE_SIZE "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}size") -set(CMAKE_STRIP "${RISCV_GNU_PATH}/bin/${TOOLCHAIN_PREFIX}strip") - -set(CMAKE_CXX_STANDARD 17) -set(CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - - -set(CMAKE_CXX_FLAGS "") -set(CMAKE_C_FLAGS "") -set(CMAKE_EXE_LINKER_FLAGS "") - -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--print-memory-usage") # Print memory usage -string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Map=map_file.map") - diff --git a/cmake/firmware/toolchains/riscv_toolchain.cmake b/cmake/firmware/toolchains/riscv_toolchain.cmake new file mode 100644 index 0000000..f490b49 --- /dev/null +++ b/cmake/firmware/toolchains/riscv_toolchain.cmake @@ -0,0 +1,127 @@ + +# GET THE RISCV TOOLCHAIN +if(ENV{RISCV_TOOLCHAIN}) + set(RISCV_GNU_PATH $ENV{RISCV_TOOLCHAIN}) +elseif(RISCV_TOOLCHAIN) + set(RISCV_GNU_PATH ${RISCV_TOOLCHAIN}) +else() + CPMAddPackage( + NAME toolchain + URL "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz" + ) + set(RISCV_GNU_PATH ${toolchain_SOURCE_DIR}) +endif() + +set(CMAKE_SYSTEM_PROCESSOR riscv) +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_ABI elf) + +set(TOOLCHAIN_PREFIX "${CMAKE_SYSTEM_PROCESSOR}-none-${CMAKE_SYSTEM_ABI}") + +find_program(RISCV_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++ HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_AR ${TOOLCHAIN_PREFIX}-ar HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_ASM ${TOOLCHAIN_PREFIX}-as HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_LINKER ${TOOLCHAIN_PREFIX}-ld HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_OBJCOPY ${TOOLCHAIN_PREFIX}-objcopy HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_OBJDUMP ${TOOLCHAIN_PREFIX}-objdump HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_RANLIB ${TOOLCHAIN_PREFIX}-ranlib HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_SIZE ${TOOLCHAIN_PREFIX}-size HINTS ${RISCV_GNU_PATH}/bin) +find_program(RISCV_STRIP ${TOOLCHAIN_PREFIX}-strip HINTS ${RISCV_GNU_PATH}/bin) + +set(CMAKE_C_COMPILER ${RISCV_C_COMPILER}) +set(CMAKE_CXX_COMPILER ${RISCV_CXX_COMPILER}) +set(CMAKE_ASM ${RISCV_ASM}) +set(CMAKE_AR ${RISCV_AR}) +set(CMAKE_LINKER ${RISCV_LINKER}) +set(CMAKE_OBJCOPY ${RISCV_OBJCOPY}) +set(CMAKE_OBJDUMP ${RISCV_OBJDUMP}) +set(CMAKE_RANLIB ${RISCV_RANLIB}) +set(CMAKE_SIZE ${RISCV_SIZE}) +set(CMAKE_STRIP ${RISCV_STRIP}) + + +get_filename_component(RISCV_TOOLCHAIN_PATH ${RISCV_CXX_COMPILER} DIRECTORY CACHE) +set(RISCV_TOOLCHAIN_PREFIX "${TOOLCHAIN_PREFIX}-" CACHE STRING "") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_STANDARD 17) + +set(CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "") +set(CMAKE_EXE_LINKER_FLAGS "") + +############################# +# Machine-Dependent Options # +############################# +# RV32 +# i : Integer +# m : Integer Multiplication and Division +# a : Atomic instructions +# c : Compressed instructions +# zicsr : CSR Instructions (explicitely required with latest specs) +string(APPEND CMAKE_C_FLAGS " -march=rv32imac_zicsr") +# int and pointers are 32bit, long 64bit, char 8bit, short 16bit +string(APPEND CMAKE_C_FLAGS " -mabi=ilp32") + +################################ +# Options for Directory Search # +################################ +# Add the directory dir to the list of directories to be searched for header files during preprocessing +# string(APPEND CMAKE_C_FLAGS " -I${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/include/") + +##################################### +# Options that Control Optimization # +##################################### +# Place each function or data item into its own section in the output file +# if the target supports arbitrary sections. The name of the function or +# the name of the data item determines the section’s name in the output file. +# string(APPEND CMAKE_C_FLAGS " -ffunction-sections") +# string(APPEND CMAKE_C_FLAGS " -fdata-sections") + +# Optimize for size by default +string(APPEND CMAKE_C_FLAGS " -Os") + +# Pass common flags for c++ compilation flow +set(CMAKE_CXX_FLAGS ${CMAKE_C_FLAGS}) + +####################### +# Options for Linking # +####################### +# Do not use the standard system startup +string(APPEND CMAKE_EXE_LINKER_FLAGS " -nostartfiles") +# Prevents linking with the shared libraries +string(APPEND CMAKE_EXE_LINKER_FLAGS " -static") + +# Print memory usage +string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--print-memory-usage") +# Generate executable map file +string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-Map=map_file.map") + +########################################## +# Options Controlling the Kind of Output # +########################################## +# Use embedded class libnano_c +string(APPEND CMAKE_EXE_LINKER_FLAGS " -specs=nano.specs") + +################################ +# Options for Directory Search # +################################ +# Add directory dir to the list of directories to be searched for -l +# string(APPEND CMAKE_EXE_LINKER_FLAGS " -L${RISCV_GNU_PATH}/${TOOLCHAIN_PREFIX}/lib") + +# Search the library named library when linking. +# string(APPEND CMAKE_EXE_LINKER_FLAGS " -lc -lgcc -lm") + + +# set(CMAKE_C_FLAGS_DEBUG ) # TODO +# set(CMAKE_C_FLAGS_RELEASE ) +# set(CMAKE_CXX_FLAGS_DEBUG ${CXX_FLAGS}) +# set(CMAKE_CXX_FLAGS_RELEASE ${CXX_FLAGS}) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/peakrdl/peakrdl_halcpp.cmake b/cmake/peakrdl/peakrdl_halcpp.cmake index 17e1d83..4fce40e 100644 --- a/cmake/peakrdl/peakrdl_halcpp.cmake +++ b/cmake/peakrdl/peakrdl_halcpp.cmake @@ -39,7 +39,7 @@ # :type OUTDIR: string path #]] function(peakrdl_halcpp IP_LIB) - cmake_parse_arguments(ARG "" "OUTDIR" "PARAMETERS" ${ARGN}) + cmake_parse_arguments(ARG "SKIP_BUSES" "OUTDIR" "PARAMETERS" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") endif() @@ -72,6 +72,10 @@ function(peakrdl_halcpp IP_LIB) set(EXT_ARG --ext ${libs}) endif() + if(ARG_SKIP_BUSES) + set(SKIB_BUSES_ARG --skip-buses) + endif() + if(NOT RDL_FILES) message(FATAL_ERROR "Library ${IP_LIB} does not have RDL_FILES property set, unable to run ${CMAKE_CURRENT_FUNCTION}") @@ -79,7 +83,7 @@ function(peakrdl_halcpp IP_LIB) find_python3() set(__CMD ${Python3_EXECUTABLE} -m peakrdl halcpp - ${RDL_FILES} ${EXT_ARG} -o ${OUTDIR} ${OVERWRITTEN_PARAMETERS} + ${RDL_FILES} ${EXT_ARG} ${SKIB_BUSES_ARG} -o ${OUTDIR} ${OVERWRITTEN_PARAMETERS} ) target_include_directories(${IP_LIB} INTERFACE ${OUTDIR} ${OUTDIR}/include) diff --git a/cmake/peakrdl/peakrdl_regblock.cmake b/cmake/peakrdl/peakrdl_regblock.cmake index 29e88aa..0624194 100644 --- a/cmake/peakrdl/peakrdl_regblock.cmake +++ b/cmake/peakrdl/peakrdl_regblock.cmake @@ -35,7 +35,7 @@ #]] function(peakrdl_regblock IP_LIB) # Parse keyword arguments - cmake_parse_arguments(ARG "" "OUTDIR;RENAME;INTF" "" ${ARGN}) + cmake_parse_arguments(ARG "" "OUTDIR;RENAME;INTF;RESET" "" ${ARGN}) # Check for any unknown argument if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " @@ -71,6 +71,12 @@ function(peakrdl_regblock IP_LIB) if(ARG_INTF) set(INTF_ARG --cpuif ${ARG_INTF}) endif() + + # The default reset is active-high and synchronous + if(ARG_RESET) + set(RESET_ARG --default-reset ${ARG_RESET}) + endif() + # Get the SystemRDL sources to generate the register block # This function gets the IP sources and the deps get_ip_sources(RDL_SOURCES ${IP_LIB} SYSTEMRDL) @@ -84,6 +90,7 @@ function(peakrdl_regblock IP_LIB) set(__CMD ${Python3_EXECUTABLE} -m peakrdl regblock --rename ${REGBLOCK_NAME} ${INTF_ARG} + ${RESET_ARG} -o ${OUTDIR} ${RDL_SOURCES} ) diff --git a/cmake/peakrdl/peakrdl_regblock_wrap.cmake b/cmake/peakrdl/peakrdl_regblock_wrap.cmake index 58fbe9b..0fb14d5 100644 --- a/cmake/peakrdl/peakrdl_regblock_wrap.cmake +++ b/cmake/peakrdl/peakrdl_regblock_wrap.cmake @@ -1,6 +1,6 @@ function(peakrdl_regblock_wrap IP_LIB) # Parse keyword arguments - cmake_parse_arguments(ARG "TMR" "OUTDIR;RENAME;INTF" "" ${ARGN}) + cmake_parse_arguments(ARG "TMR" "OUTDIR;RENAME;INTF;RESET" "" ${ARGN}) # Check for any unknown argument if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " @@ -35,6 +35,29 @@ function(peakrdl_regblock_wrap IP_LIB) if(ARG_INTF) set(INTF_ARG --cpuif ${ARG_INTF}) endif() + + # The default reset is active-high and synchronous + if(ARG_RESET) + set(RESET_ARG --default-reset ${ARG_RESET}) + endif() + + # Activate the triplication if TMR option is passed + if(ARG_TMR) + set(TMR_OPT "--tmr") + endif() + + if(NOT ARG_RENAME) + # The default name is the IP name + get_target_property(REGBLOCK_NAME ${IP_LIB} IP_NAME) + if(NOT REGBLOCK_NAME) + message(FATAL_ERROR "IP_NAME not set for ${IP_LIB}, check if the IP was added with + add_ip function from SoCMake") + endif() + set(REGBLOCK_NAME ${REGBLOCK_NAME}_regblock) + else() + set(REGBLOCK_NAME ${ARG_RENAME}) + endif() + # Get the SystemRDL sources to generate the register block # This function gets the IP sources and the deps get_ip_sources(RDL_SOURCES ${IP_LIB} SYSTEMRDL) @@ -46,21 +69,30 @@ function(peakrdl_regblock_wrap IP_LIB) # Generate the regblock and wrapper find_python3() - if(ARG_TMR) - set(TMR_OPT "--tmr") - endif() + + # Create the reblog_wrap python command set(__CMD ${Python3_EXECUTABLE} -m peakrdl regblock_wrap + --rename ${REGBLOCK_NAME} + ${INTF_ARG} + ${RESET_ARG} ${TMR_OPT} - # --rename ${IP_NAME} - # ${INTF_ARG} -o ${OUTDIR} ${RDL_SOURCES} ) - set(REGBLOCK_SV_GEN ${OUTDIR}/${IP_NAME}_regblock_pkg.sv ${OUTDIR}/${IP_NAME}_regblock.sv) + set(REGBLOCK_SV_GEN ${OUTDIR}/${IP_NAME}_pkg.sv ${OUTDIR}/${IP_NAME}.sv) set(WRAP_SV_GEN ${OUTDIR}/${IP_NAME}.sv) set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_${CMAKE_CURRENT_FUNCTION}.stamp") + # Regblock generated files (pkg + logic) + set(REGBLOCK_SV_GEN + ${OUTDIR}/${REGBLOCK_NAME}_pkg.sv + ${OUTDIR}/${REGBLOCK_NAME}.sv + ) + # Top module wraper file generate + set(WRAP_SV_GEN ${OUTDIR}/${IP_NAME}.sv) + + # Add the custom command to call the peakrdl regblock_wrap plugin add_custom_command( # The output files are automtically marked as GENERATED (deleted by make clean among other things) OUTPUT ${REGBLOCK_SV_GEN} ${WRAP_SV_GEN} ${STAMP_FILE} @@ -70,12 +102,14 @@ function(peakrdl_regblock_wrap IP_LIB) COMMENT "Running ${CMAKE_CURRENT_FUNCTION} on ${IP_LIB}" ) - # This target triggers the systemverilog register block generation using peakRDL regblock tool (_CMD) + # This target triggers the custom command generating the top wrapper and the register file block set(TNAME ${IP_LIB}_regblock_wrap) add_custom_target( ${TNAME} DEPENDS ${REGBLOCK_SV_GEN} ${WRAP_SV_GEN} ${STAMP_FILE} ) + + # Add the generated sources to the IP sources ip_sources(${IP_LIB} SYSTEMVERILOG PREPEND ${REGBLOCK_SV_GEN} ${WRAP_SV_GEN}) if(ARG_TMR) diff --git a/cmake/peakrdl/peakrdl_socgen/common/socgen_props.rdl b/cmake/peakrdl/peakrdl_socgen/common/socgen_props.rdl index 6a7ad85..6f838f2 100644 --- a/cmake/peakrdl/peakrdl_socgen/common/socgen_props.rdl +++ b/cmake/peakrdl/peakrdl_socgen/common/socgen_props.rdl @@ -1,33 +1,30 @@ `ifndef COMMON_RDL `define COMMON_RDL -enum SignalType { - output = 0; - input = 1; - bidir = 2; - tri = 3; - clk = 4; - rst = 5; - wire = 6; - blank = 7; -}; - +// input or output property is needed to be detected by socgen property output { type = boolean; component = signal; }; + property input { type = boolean; component = signal; }; -property datatype { - type = string; +property clock { + type = boolean; component = signal; }; -property signal_type { - type = SignalType; +// reset property is a a built-in SystemRDL property but not for signals +property reset_signal { + type = boolean; + component = signal; +}; + +property datatype { + type = string; component = signal; }; @@ -61,12 +58,6 @@ property path { component = signal; }; -property propagate { - type = boolean; - component = signal; -}; - - struct intc { string name; string slv_ports[]; @@ -78,7 +69,6 @@ property intc_l { component = addrmap; }; - // desc = "Slave select signal, should not be shared between slaves"; property ss { type = boolean; @@ -100,25 +90,28 @@ property miso { component = signal; }; -signal clk { +signal clk { + input; + clock; signalwidth=1; desc = "Input clock"; - signal_type = SignalType::clk; - }; +}; signal rstn { + input; + reset_signal; signalwidth=1; desc = "Input reset, active low"; activelow = true; - signal_type = SignalType::rst; - }; +}; signal rst { + input; + reset_signal; signalwidth=1; desc = "Input reset, active high"; activehigh = true; - signal_type = SignalType::rst; - }; +}; property intf { type = boolean; @@ -150,8 +143,6 @@ struct addr_intf : data_intf { longint unsigned ADDR_WIDTH; }; - - property intf_inst{ component = addrmap; type = base_intf; diff --git a/cmake/peakrdl/peakrdl_socgen/peakrdl_socgen.cmake b/cmake/peakrdl/peakrdl_socgen/peakrdl_socgen.cmake index 89f3d74..54d7526 100644 --- a/cmake/peakrdl/peakrdl_socgen/peakrdl_socgen.cmake +++ b/cmake/peakrdl/peakrdl_socgen/peakrdl_socgen.cmake @@ -148,7 +148,7 @@ function(peakrdl_socgen IP_LIB) endif() set_source_files_properties(${V_GEN} PROPERTIES GENERATED TRUE) - ip_sources(${IP_LIB} VERILOG ${V_GEN}) + ip_sources(${IP_LIB} SYSTEMVERILOG ${V_GEN}) set(STAMP_FILE "${BINARY_DIR}/${IP_LIB}_${CMAKE_CURRENT_FUNCTION}.stamp") add_custom_command( diff --git a/cmake/sim/cadence/xcelium.cmake b/cmake/sim/cadence/xcelium.cmake index d39bf2b..6854625 100644 --- a/cmake/sim/cadence/xcelium.cmake +++ b/cmake/sim/cadence/xcelium.cmake @@ -55,13 +55,15 @@ function(xcelium IP_LIB) add_custom_target( run_${IP_LIB}_${CMAKE_CURRENT_FUNCTION} COMMAND xrun - ${V_FILES} + # Enable parameters without default value + -setenv CADENCE_ENABLE_AVSREQ_44905_PHASE_1=1 -setenv CADENCE_ENABLE_AVSREQ_63188_PHASE_1=1 + -define COMMON_CELLS_ASSERTS_OFF + ${SOURCES} ${ARG_INCDIRS} ${CMP_DEFS_ARG} ${ARG_GUI} - DEPENDS ${V_FILES} COMMENT "Running ${CMAKE_CURRENT_FUNCTION} on ${IP_LIB}" - DEPENDS ${V_FILES} ${IP_LIB} + DEPENDS ${SOURCES} ${IP_LIB} ) # add_dependencies(${IP_LIB}_${CMAKE_CURRENT_FUNCTION} ${IP_LIB}) diff --git a/cmake/sim/verilator/verilate.cmake b/cmake/sim/verilator/verilate.cmake index 810900f..3b7c65c 100644 --- a/cmake/sim/verilator/verilate.cmake +++ b/cmake/sim/verilator/verilate.cmake @@ -3,7 +3,7 @@ function(verilate IP_LIB) set(ONE_PARAM_ARGS "PREFIX;TOP_MODULE;THREADS;TRACE_THREADS;DIRECTORY;EXECUTABLE_NAME") set(MULTI_PARAM_ARGS "VERILATOR_ARGS;OPT_SLOW;OPT_FAST;OPT_GLOBAL") - cmake_parse_arguments(ARG + cmake_parse_arguments(ARG "${OPTIONS}" "${ONE_PARAM_ARGS}" "${MULTI_PARAM_ARGS}" @@ -161,11 +161,11 @@ function(verilate IP_LIB) INSTALL_COMMAND "" DEPENDS ${IP_LIB} EXCLUDE_FROM_ALL 1 - ) + ) set_property( TARGET ${VERILATE_TARGET} - APPEND PROPERTY ADDITIONAL_CLEAN_FILES + APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${DIRECTORY} ${EXECUTABLE_PATH} ) diff --git a/cmake/synth/sv2v.cmake b/cmake/synth/sv2v.cmake index dbfaddd..22a88e4 100644 --- a/cmake/synth/sv2v.cmake +++ b/cmake/synth/sv2v.cmake @@ -24,7 +24,7 @@ function(get_sv2v_sources OUT_VAR IP_LIB) endfunction() function(sv2v IP_LIB) - cmake_parse_arguments(ARG "REPLACE;TMR;HWIF_WIRE" "OUTDIR" "" ${ARGN}) + cmake_parse_arguments(ARG "REPLACE;TMR;HWIF_WIRE" "OUTDIR;REGBLOCK_OUTDIR" "" ${ARGN}) if(ARG_UNPARSED_ARGUMENTS) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}") endif() @@ -39,6 +39,18 @@ function(sv2v IP_LIB) endif() execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTDIR}) + # Default regblock output directory is regblock/ + if(NOT ARG_REGBLOCK_OUTDIR) + if(NOT ARG_TMR) + set(REGBLOCK_OUTDIR_ARG regblock) + else() + set(REGBLOCK_OUTDIR_ARG regblock_tmr) + endif() + else() + set(REGBLOCK_OUTDIR_ARG ${ARG_REGBLOCK_OUTDIR}) + endif() + + get_sv2v_sources(SV2V_SRC ${IP_LIB}) foreach(vfile ${SV2V_SRC}) get_filename_component(V_SOURCE_WO_EXT ${vfile} NAME_WE) @@ -66,7 +78,7 @@ function(sv2v IP_LIB) if(ARG_HWIF_WIRE) get_target_property(TOP_MODULE ${IP_LIB} IP_NAME) set(SED_COMMAND - COMMAND ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/hwif_sed.sh ${OUTDIR}/${TOP_MODULE}_regblock.v ${OUTDIR}/../regblock/${TOP_MODULE}.sv + COMMAND ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/hwif_sed.sh ${OUTDIR}/${TOP_MODULE}_regblock.v ${OUTDIR}/../${REGBLOCK_OUTDIR_ARG}/${TOP_MODULE}.sv ) endif()