Skip to content

Commit

Permalink
allow add_ip short form, resolves #42
Browse files Browse the repository at this point in the history
  • Loading branch information
Risto97 committed Sep 14, 2024
1 parent 11d80a6 commit 1921916
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 18 deletions.
132 changes: 116 additions & 16 deletions cmake/hwip.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,33 @@ include("${CMAKE_CURRENT_LIST_DIR}/utils/safe_get_target_property.cmake")
# This function is a wrapper around the cmake built-in
# `add_library() <https://cmake.org/cmake/help/latest/command/add_library.html>`_ function.
# It generates the library name using the vendor, library, name, and version (VLNV) information passed
# in arguments (see get_ipname()). It creates two alias libraries to the default <vendor>__<library>__<name>__<version>:
#
# in arguments (see create_ip_vlnv()). It creates two alias libraries to the default <vendor>__<library>__<name>__<version>:
#
# * <vendor>::<library>::<name>::<version> ('__' replaced by '::')
# * <vendor>::<library>::<name> (short name without the version)
#
# This function can be used in FULL and SHORT form:
# Full form:
# ```
# add_ip(ip
# VENDOR vendor
# LIBRARY lib
# VERSION 1.2.3
# )
# ```
# In full form it is possible to ommit VENDOR, LIBRARY and VERSION, although it is not recommended.
#
# Ommiting them all would have following signature:
# ```
# add_ip(ip2)
# ```
#
# Short form:
# ```
# add_ip(vendor2::lib2::ip2::1.2.2)
# ```
# In short form only the full VLNV format is accepted
#
# :param IP_NAME: The name of the IP.
# :type IP_NAME: string
#
Expand All @@ -27,24 +49,33 @@ include("${CMAKE_CURRENT_LIST_DIR}/utils/safe_get_target_property.cmake")
# :type VERSION: string
#]]
function(add_ip IP_NAME)
cmake_parse_arguments(ARG "" "VERSION;DESCRIPTION;VENDOR;LIBRARY" "" ${ARGN})
cmake_parse_arguments(ARG "" "VENDOR;LIBRARY;VERSION;DESCRIPTION" "" ${ARGN})

# Vendor and library arguments are expected at the minimum
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument "
"${ARG_UNPARSED_ARGUMENTS}")
endif()

# If none of optional arguments VENDOR, LIBRARY, VERSION are passed expect to receive VLNV format in IP_NAME like vendor::lib::ip::0.0.1
if(NOT ARG_VENDOR AND NOT ARG_LIBRARY AND NOT ARG_VERSION)
unset(ARG_VENDOR)
parse_ip_vlnv(${IP_NAME} VENDOR LIBRARY IP_NAME VERSION)
set(ARG_VENDOR ${VENDOR})
set(ARG_LIBRARY ${LIBRARY})
set(ARG_VERSION ${VERSION})
endif()
# Issue a warning if one of the VLNV information is not passed (this triggers an error later anyway)
if((NOT ARG_VERSION OR NOT ARG_VENDOR OR NOT ARG_LIBRARY) AND NOT SOCMAKE_NOWARN_VLNV)
message(WARNING "Consider using full VLNV format\n\
IP block: ${IP_NAME}\n\
VENDOR: ${ARG_VENDOR}\n\
LIBRARY: ${ARG_LIBRARY}\n\
VERSION: ${ARG_VERSION}")
message(WARNING "Consider using full VLNV format\n\
IP block: ${IP_NAME}\n\
VENDOR: ${ARG_VENDOR}\n\
LIBRARY: ${ARG_LIBRARY}\n\
VERSION: ${ARG_VERSION}")
endif()
# Create the IP unique name using VLNV information
get_ipname(IP_LIB ${IP_NAME} VENDOR "${ARG_VENDOR}" LIBRARY "${ARG_LIBRARY}" VERSION "${ARG_VERSION}")

create_ip_vlnv(IP_LIB ${IP_NAME} VENDOR "${ARG_VENDOR}" LIBRARY "${ARG_LIBRARY}" VERSION "${ARG_VERSION}")
if(NOT TARGET ${IP_LIB})
add_library(${IP_LIB} INTERFACE)

Expand All @@ -54,13 +85,20 @@ function(add_ip IP_NAME)
endif()

# TODO Maybe delete short name without version
get_ipname(IP_LIB_SHORT ${IP_NAME} VENDOR "${ARG_VENDOR}" LIBRARY "${ARG_LIBRARY}" VERSION "")
string(REPLACE "__" "::" ALIAS_NAME_SHORT "${IP_LIB_SHORT}")
if(NOT "${IP_LIB}" STREQUAL "${ALIAS_NAME_SHORT}")
add_library(${ALIAS_NAME_SHORT} ALIAS ${IP_LIB})
if(ARG_VERSION)
create_ip_vlnv(IP_LIB_SHORT ${IP_NAME} VENDOR "${ARG_VENDOR}" LIBRARY "${ARG_LIBRARY}" VERSION "")
string(REPLACE "__" "::" ALIAS_NAME_SHORT "${IP_LIB_SHORT}")
if(NOT "${IP_LIB}" STREQUAL "${ALIAS_NAME_SHORT}")
add_library(${ALIAS_NAME_SHORT} ALIAS ${IP_LIB})
endif()
endif()
endif()

# Unset the parent variables that might have been set by previous add_ip() call
unset(IP_VENDOR PARENT_SCOPE)
unset(IP_LIBRARY PARENT_SCOPE)
unset(IP_NAME PARENT_SCOPE)
unset(IP_VERSION PARENT_SCOPE)
if(ARG_VENDOR)
set(IP_VENDOR ${ARG_VENDOR} PARENT_SCOPE)
set_target_properties(${IP_LIB} PROPERTIES VENDOR ${ARG_VENDOR})
Expand Down Expand Up @@ -101,7 +139,7 @@ endfunction()
# :keyword VERSION: Version of the IP following a three-part version number (Major.Minor.Patch, e.g., 1.0.13).
# :type VERSION: string
#]]
function(get_ipname OUTVAR IP_NAME)
function(create_ip_vlnv OUTVAR IP_NAME)
cmake_parse_arguments(ARG "" "VENDOR;LIBRARY;VERSION" "" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}")
Expand All @@ -121,11 +159,73 @@ function(get_ipname OUTVAR IP_NAME)
set(${OUTVAR} ${LIB_NAME} PARENT_SCOPE)
endfunction()

#[[[
# This function parses IP name from the VLNV format e.g. (vendor::lib::ip::0.0.1)
#
# This functions appends the vendor, library, name, and version (VLNV) information separated by '__'
# to create a unique string representing an IP name. This string is used as the library name when
# when calling the cmake built-in
# `add_library() <https://cmake.org/cmake/help/latest/command/add_library.html>`_ function (see add_ip()).
#
# :param OUTVAR: The generate IP name.
# :type OUTVAR: string
# :param IP_NAME: The name of the IP.
# :type IP_NAME: string
#
# **Keyword Arguments**
#
# :keyword VENDOR: Name of the IP vendor.
# :type VENDOR: string
# :keyword LIBRARY: Name of the IP library.
# :type LIBRARY: string
# :keyword VERSION: Version of the IP following a three-part version number (Major.Minor.Patch, e.g., 1.0.13).
# :type VERSION: string
#]]
function(parse_ip_vlnv IP_VLNV VENDOR LIBRARY IP_NAME VERSION)
message("IP_VLNV: ${IP_VLNV}")
cmake_parse_arguments(ARG "" "" "" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} passed unrecognized argument " "${ARG_UNPARSED_ARGUMENTS}")
endif()

# Convert IP_VLNV into a list of tokens by replacing :: with ;
string(REPLACE "::" ";" IP_TOKENS ${IP_VLNV})
# Remove empty list elements in case something like vendor::::ip::1.2.3 is passed
list(REMOVE_ITEM IP_TOKENS "")

# Raise an error if there are different than 4 tokens provided (`add_ip(vendor::lib::ip::0.0.1)`), unless its only 1 (`add_ip(ip)`)
list(LENGTH IP_TOKENS TOKEN_CNT)

# Its alowed for IP_VLNV to have 4 tokens (FULL) `add_ip(vendor::lib::ip::0.0.1)`
if(TOKEN_CNT EQUAL 4)
# Get elements of the list
list(GET IP_TOKENS 0 VENDOR)
list(GET IP_TOKENS 1 LIBRARY)
list(GET IP_TOKENS 2 IP_NAME)
list(GET IP_TOKENS 3 VERSION)
# Its alowed for IP_VLNV to have 1 token (SHORT) `add_ip(ip)`
elseif(TOKEN_CNT EQUAL 1)
set(IP_NAME ${IP_VLNV})
unset(VENDOR)
unset(LIBRARY)
unset(VERSION)
# Anything else is not allowed and will throw an error
else()
message(FATAL_ERROR "Please specify full VLNV format for IP: ${IP_VLNV}")
endif()

# Set output variables
set(VENDOR ${VENDOR} PARENT_SCOPE)
set(LIBRARY ${LIBRARY} PARENT_SCOPE)
set(IP_NAME ${IP_NAME} PARENT_SCOPE)
set(VERSION ${VERSION} PARENT_SCOPE)
endfunction()

# IS THIS REALLY NECESSARY?
# If only IP name is given without full VLNV, assume rest from the project variables
function(ip_assume_last VLNV IP_NAME) # TODO check SOURCE DIR if its the same as current
if(NOT TARGET ${IP_NAME})
get_ipname(IP_LIB ${IP_NAME} VENDOR "${IP_VENDOR}" LIBRARY "${IP_LIBRARY}" VERSION "${IP_VERSION}")
create_ip_vlnv(IP_LIB ${IP_NAME} VENDOR "${IP_VENDOR}" LIBRARY "${IP_LIBRARY}" VERSION "${IP_VERSION}")
endif()
alias_dereference(IP_LIB ${IP_LIB})
set(${VLNV} ${IP_LIB} PARENT_SCOPE)
Expand Down
3 changes: 1 addition & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

add_custom_target(check
COMMAND ctest
COMMAND ctest $(JOBS)
)

add_subdirectory(iverilog)
Expand Down
21 changes: 21 additions & 0 deletions tests/add_ip/add_ip.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set(TEST_NAME add_ip)

ct_add_test(NAME ${TEST_NAME})
function(${${TEST_NAME}})
## Test full add_ip() call
add_ip(ip
VENDOR vendor
LIBRARY lib
Expand All @@ -18,4 +19,24 @@ function(${${TEST_NAME}})
ct_assert_equal(IP_LIBRARY "lib")
ct_assert_equal(IP_VERSION "1.2.3")
ct_assert_target_exists(${IP_VENDOR}__${IP_LIBRARY}__${IP_NAME}__${IP_VERSION})

add_ip(ip4
VERSION 1.1.1
)
ct_assert_target_exists(ip4::1.1.1)
ct_assert_target_exists(ip4__1.1.1)

## Test shortened add_ip() call
add_ip(vendor2::lib2::ip2::1.2.2)

ct_assert_target_exists(vendor2::lib2::ip2::1.2.2)
ct_assert_target_exists(vendor2__lib2__ip2__1.2.2)

ct_assert_equal(IP vendor2__lib2__ip2__1.2.2)
ct_assert_equal(IP_NAME "ip2")
ct_assert_equal(IP_VENDOR "vendor2")
ct_assert_equal(IP_LIBRARY "lib2")
ct_assert_equal(IP_VERSION "1.2.2")
ct_assert_target_exists(${IP_VENDOR}__${IP_LIBRARY}__${IP_NAME}__${IP_VERSION})

endfunction()
32 changes: 32 additions & 0 deletions tests/add_ip/add_ip_1_token.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This test will succed because its allowed to have short notation in `ip_link(ip)` call
include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt")

set(TEST_NAME add_ip_1_token)

ct_add_test(NAME ${TEST_NAME})
function(${${TEST_NAME}})
add_ip(vendor::lib::ip1::0.0.1)
ct_assert_target_exists(vendor::lib::ip1::0.0.1)
ct_assert_target_exists(vendor__lib__ip1__0.0.1)
ct_assert_equal(IP vendor__lib__ip1__0.0.1)
ct_assert_equal(IP_VENDOR vendor)
ct_assert_equal(IP_LIBRARY lib)
ct_assert_equal(IP_VERSION 0.0.1)

add_ip(ip2
VENDOR vendor
LIBRARY lib
)
ct_assert_target_exists(vendor::lib::ip2)
ct_assert_equal(IP vendor__lib__ip2)
ct_assert_equal(IP_VENDOR vendor)
ct_assert_equal(IP_LIBRARY lib)
ct_assert_not_defined(IP_VERSION)

add_ip(ip3)
ct_assert_target_exists(ip3)
ct_assert_equal(IP ip3)
ct_assert_not_defined(IP_VENDOR)
ct_assert_not_defined(IP_LIBRARY)
ct_assert_not_defined(IP_VERSION)
endfunction()
45 changes: 45 additions & 0 deletions tests/add_ip/add_ip_fail_2_tokens.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This tests should fail because its not allowed to have different than 4 or 1 tokens in `ip_link()` call
include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt")

set(TEST_NAME add_ip_fail_2_tokens_name_version)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(ip1::0.0.1)
endfunction()

set(TEST_NAME add_ip_fail_2_tokens_vendor_name)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::ip1)
endfunction()

set(TEST_NAME add_ip_fail_2_tokens_vendor_lib)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib)
endfunction()

set(TEST_NAME add_ip_fail_2_tokens_vendor_version)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::0.0.1)
endfunction()

set(TEST_NAME add_ip_fail_2_tokens_lib_version)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(lib::0.0.1)
endfunction()


set(TEST_NAME add_ip_fail_2_tokens_empty_token_1)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(lib::)
endfunction()

set(TEST_NAME add_ip_fail_2_tokens_empty_token_2)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::lib)
endfunction()
51 changes: 51 additions & 0 deletions tests/add_ip/add_ip_fail_3_tokens.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This tests should fail because its not allowed to have different than 4 or 1 tokens in `ip_link()` call
include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt")

set(TEST_NAME add_ip_fail_3_tokens_vendor_lib_ip)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib::ip)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_vendor_lib_version)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib::0.0.1)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_lib_ip_version)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(lib::ip::0.0.1)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_empty_token_1)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::::vendor)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_empty_token_2)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::vendor::lib)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_empty_token_3)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::::lib)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_empty_token_4)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib::)
endfunction()

set(TEST_NAME add_ip_fail_3_tokens_empty_token_5)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::::)
endfunction()

34 changes: 34 additions & 0 deletions tests/add_ip/add_ip_fail_4_tokens.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This tests should fail because its not allowed to have different than 4 or 1 tokens in `ip_link()` call
include("${CMAKE_CURRENT_LIST_DIR}/../../CMakeLists.txt")

set(TEST_NAME add_ip_fail_4_tokens_empty_token_1)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::::::vendor)
endfunction()

set(TEST_NAME add_ip_fail_4_tokens_empty_token_2)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::::vendor::)
endfunction()

set(TEST_NAME add_ip_fail_4_tokens_empty_token_3)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(::vendor::lib::)
endfunction()

set(TEST_NAME add_ip_fail_4_tokens_empty_token_4)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib::::0.0.1)
endfunction()

set(TEST_NAME add_ip_fail_4_tokens_empty_token_5)
ct_add_test(NAME ${TEST_NAME} EXPECTFAIL)
function(${${TEST_NAME}})
add_ip(vendor::lib::ip::)
endfunction()


Loading

0 comments on commit 1921916

Please sign in to comment.