diff --git a/.gitea/workflows/docker-rh8-build-test.yml b/.gitea/workflows/docker-rh8-build-test.yml index 3d070f71e..9bb09949b 100644 --- a/.gitea/workflows/docker-rh8-build-test.yml +++ b/.gitea/workflows/docker-rh8-build-test.yml @@ -15,13 +15,14 @@ jobs: steps: - name: Clone repository run: | + git lfs install --skip-smudge echo Cloning ${{ github.ref_name }} git clone https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git --branch=${{ github.ref_name }} . - name: Build library run: | mkdir build && cd build - cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON + cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON -DSLS_USE_MATTERHORN=ON make -j 2 - name: C++ unit tests diff --git a/.gitea/workflows/docker-rh9-build-test.yml b/.gitea/workflows/docker-rh9-build-test.yml index 2783d69ec..7d2fbb400 100644 --- a/.gitea/workflows/docker-rh9-build-test.yml +++ b/.gitea/workflows/docker-rh9-build-test.yml @@ -19,7 +19,7 @@ jobs: - name: Build library run: | mkdir build && cd build - cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON + cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON -DSLS_USE_MATTERHORN=ON make -j 2 - name: C++ unit tests diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml index f0c274c67..b90214279 100644 --- a/.github/workflows/cmake.yaml +++ b/.github/workflows/cmake.yaml @@ -39,7 +39,8 @@ jobs: -DSLS_USE_HDF5=ON \ -DSLS_USE_GUI=ON \ -DSLS_USE_MOENCH=ON \ - -DSLS_TREAT_WARNINGS_AS_ERRORS=ON + -DSLS_TREAT_WARNINGS_AS_ERRORS=ON \ + -DSLS_USE_MATTERHORN=ON - name: Build # Build your program with the given configuration diff --git a/CMakeLists.txt b/CMakeLists.txt index d29e4782b..5ea532cee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,18 @@ if(SLS_BUILD_ONLY_MOENCH) set(SLS_USE_MOENCH ON CACHE BOOL "Enable" FORCE) endif() +option(SLS_BUILD_ONLY_MATTERHORN "compile only Matterhorn" OFF) +if(SLS_BUILD_ONLY_MATTERHORN) + message(STATUS "Build MATTERHORN server only!") + set(SLS_BUILD_SHARED_LIBRARIES OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE) + set(SLS_USE_TEXTCLIENT OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE) + set(SLS_USE_DETECTOR ON CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE) + set(SLS_USE_RECEIVER OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE) + set(SLS_USE_RECEIVER_BINARIES OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE) + set(SLS_USE_SERVER ON CACHE BOOL "Enable building cpp server" FORCE) + set(SLS_USE_MATTERHORN ON CACHE BOOL "Enable Matterhorn" FORCE) +endif() + #Convenience option to switch off defaults when building Jungfrau binaries only option(SLS_BUILD_ONLY_JUNGFRAU "compile only Jungfrau" OFF) if(SLS_BUILD_ONLY_JUNGFRAU) @@ -440,14 +452,15 @@ if (SLS_USE_GUI) add_subdirectory(slsDetectorGui) endif (SLS_USE_GUI) -if (SLS_USE_MATTERHORN) - add_subdirectory(slsDetectorServers/matterhornServer) -endif() - if (SLS_USE_SIMULATOR) add_subdirectory(slsDetectorServers) endif (SLS_USE_SIMULATOR) +# cant add_subdirectory twice +if (SLS_USE_MATTERHORN AND !SLS_USE_SIMULATOR) + add_subdirectory(slsDetectorServers/matterhornServer) +endif() + if (SLS_USE_PYTHON) find_package (Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED) set(PYBIND11_FINDPYTHON ON) # Needed for RH8 diff --git a/etc/generate_registerdefs.py b/etc/generate_registerdefs.py index 91178b0e3..aaaaa9720 100644 --- a/etc/generate_registerdefs.py +++ b/etc/generate_registerdefs.py @@ -32,23 +32,47 @@ def argument_parser(): # TODO: should be configurable header = r""" +#pragma once // clang-format off #include "RegisterHelperStructs.hpp" namespace sls { + + +namespace Reg { + /// @brief Enum for IP cores, value are adresses enum class IPCore : uint32_t { - MH_RO_SM_AXI = 0, // dummy adresses for now - FHDR_AXI = 1, - AURORA_STATUS = 2, - AURORA_STATUS2 = 3, - PACKETIZERREG = 4, - UNKNOWN = 5 + MH_RO_SM_AXI = 0xB0010000, + FHDR_AXI = 0xB0011000, + AURORA_STATUS = 0xB0014000, + AURORA_STATUS2 = 0xB0015000, + PACKETIZERREG = 0x00000000, // TODO: fill in correct address + UNKNOWN = 0x00000000 // dont know yet }; + +constexpr size_t IPCORE_REGISTER_BLOCK_SIZE = + 0x1000; // size of each IP core address space in bytes // TODO: maybe add in + // other file definitions + +// clang-format off + """ postpend = r""" +constexpr RegisterField ModuleRow{ + Frame_HDR_ModCoord_LSB_Reg, 0, 0xffff}; + +constexpr RegisterField ModuleCol{ + Frame_HDR_ModCoord_LSB_Reg, 16, 0xffff}; + +constexpr RegisterField ModuleCoordz{ + Frame_HDR_ModCoord_MSB_Reg, 0, 0xffff}; + +constexpr RegisterField ModuleIndex{ + Frame_HDR_ModCoord_MSB_Reg, 16, 0xffff}; +} // namespace Reg } // namespace sls // clang-format on """ diff --git a/etc/updateAPIVersion.py b/etc/updateAPIVersion.py index 84b160384..74c1289ec 100644 --- a/etc/updateAPIVersion.py +++ b/etc/updateAPIVersion.py @@ -20,8 +20,8 @@ API_FILE = ROOT_DIR / "slsSupportLib/include/sls/versionAPI.h" VERSION_FILE = ROOT_DIR / "VERSION" parser = argparse.ArgumentParser(description = 'updates API version') -parser.add_argument('api_module_name', choices=["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3"], help = 'module name to change api version options are: ["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3"]') -parser.add_argument('api_dir', help = 'Relative or absolute path to the module code') +parser.add_argument('api_module_name', choices=["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3", "APIMATTERHORN"], help = 'module name to change api version options are: ["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3", "APIMATTERHORN"]') +parser.add_argument('api_dirs', nargs="+", help = 'Relative or absolute paths to the module code') def update_api_file(new_api : str, api_module_name : str, api_file_name : str): @@ -36,21 +36,22 @@ def update_api_file(new_api : str, api_module_name : str, api_file_name : str): else: api_file.write(line) -def get_latest_modification_date(directory : str): +def get_latest_modification_date(directories : list[str]): latest_time = 0 latest_date = None - for root, dirs, files in os.walk(directory): - for file in files: - if file.endswith(".o"): - continue - full_path = os.path.join(root, file) - try: - mtime = os.path.getmtime(full_path) - if mtime > latest_time: - latest_time = mtime - except FileNotFoundError: - continue + for directory in directories: + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith(".o"): + continue + full_path = os.path.join(root, file) + try: + mtime = os.path.getmtime(full_path) + if mtime > latest_time: + latest_time = mtime + except FileNotFoundError: + continue latest_date = datetime.fromtimestamp(latest_time).strftime("%y%m%d") @@ -74,9 +75,9 @@ if __name__ == "__main__": args = parser.parse_args() - api_dir = ROOT_DIR / args.api_dir + api_dirs = [ROOT_DIR / api_dir for api_dir in args.api_dirs] - update_api_version(args.api_module_name, api_dir) + update_api_version(args.api_module_name, api_dirs) diff --git a/slsDetectorServers/matterhornServer/.gitattributes b/slsDetectorServers/matterhornServer/.gitattributes new file mode 100644 index 000000000..087a700fc --- /dev/null +++ b/slsDetectorServers/matterhornServer/.gitattributes @@ -0,0 +1,3 @@ +bin/ filter=lfs diff=lfs merge=lfs -text +bin/** filter=lfs diff=lfs merge=lfs -text +bin/matterhornDetectorServer filter=lfs diff=lfs merge=lfs -text diff --git a/slsDetectorServers/matterhornServer/CMakeLists.txt b/slsDetectorServers/matterhornServer/CMakeLists.txt index e4d237e41..34d539b08 100644 --- a/slsDetectorServers/matterhornServer/CMakeLists.txt +++ b/slsDetectorServers/matterhornServer/CMakeLists.txt @@ -2,22 +2,40 @@ set(MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornApp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/communication/SPICommunication.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/HelperFunctions.cpp +) + +set(MATTERHORN_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/communication + ${CMAKE_CURRENT_SOURCE_DIR}/src/utils + ${CMAKE_CURRENT_SOURCE_DIR}/src/defs + ${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include + ${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include ) if(SLS_USE_SIMULATOR) - list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/VirtualMatterhornServer.cpp) + set(MATTERHORN_SERVER_HEADER "VirtualMatterhornServer.hpp") add_executable(matterhornDetectorServer_virtual ${MATTERHORN_SOURCES}) + target_compile_definitions(matterhornDetectorServer_virtual PRIVATE + MATTERHORN_SERVER_HEADER= + MATTERHORN_SERVER_CLASS=VirtualMatterhornServer + ) + target_include_directories(matterhornDetectorServer_virtual - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include - ${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include) + PRIVATE ${MATTERHORN_INCLUDE_DIRS}) target_link_libraries(matterhornDetectorServer_virtual PUBLIC slsSupportStatic - slsServerStatic) + slsServerStatic + slsProjectOptions + PRIVATE + slsProjectWarnings + ) set_target_properties(matterhornDetectorServer_virtual PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin @@ -26,37 +44,61 @@ if(SLS_USE_SIMULATOR) install(TARGETS matterhornDetectorServer_virtual RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) -else() - list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornServer.cpp) +endif() # maybe better to have a else if build with simulators on one always uses the virtual server in MatterhornApp + +if(SLS_USE_MATTERHORN) + + + if(CMAKE_CROSSCOMPILING) # only update version if cross compiling binaries + find_package(Python3 REQUIRED COMPONENTS Interpreter REQUIRED) + add_custom_target(update_server_version ALL + COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/etc/updateAPIVersion.py APIMATTERHORN ${CMAKE_SOURCE_DIR}/slsDetectorServers/matterhornServer ${CMAKE_SOURCE_DIR}/slsDetectorServers/slsDetectorServer_cpp + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Updating APIMATTERHORN version" + VERBATIM) + endif() add_executable(matterhornDetectorServer ${MATTERHORN_SOURCES}) + if(CMAKE_CROSSCOMPILING) # only update version if cross compiling binaries + add_dependencies(matterhornDetectorServer update_server_version) + endif() + + target_compile_definitions(matterhornDetectorServer PRIVATE + MATTERHORN_SERVER_HEADER= + MATTERHORN_SERVER_CLASS=MatterhornServer + ) + target_include_directories(matterhornDetectorServer - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include - ${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include) - + PRIVATE ${MATTERHORN_INCLUDE_DIRS}) + target_link_libraries(matterhornDetectorServer PUBLIC slsSupportStatic - #slsDetectorStatic - slsServerStatic) + slsServerStatic + slsProjectOptions + PRIVATE + slsProjectWarnings + ) + + if(CMAKE_CROSSCOMPILING) # change output directory to stay consitent with c server binaries when cross compiling + set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) + else() + set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + endif() set_target_properties(matterhornDetectorServer PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + RUNTIME_OUTPUT_DIRECTORY ${RUNTIME_OUTPUT_DIRECTORY} ) install(TARGETS matterhornDetectorServer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) +endif() +if(SLS_USE_TESTS) + add_subdirectory(tests) endif() -#target_compile_definitions(matterhornDetectorServer_virtual -# PUBLIC VIRTUAL STOP_SERVER #what is this stop server should we really have a generic ServerAPP and pass compile options to create server e.g. MatterHorn? -#) - - - diff --git a/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer b/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer new file mode 100755 index 000000000..74a8fc366 --- /dev/null +++ b/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bfbe17a6b0efbdcd3da78eafb1ac6c212eb81434f36267640f75fe743dc0aea +size 316776 diff --git a/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h b/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h deleted file mode 100644 index 4ca5e46cc..000000000 --- a/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -#include "DetectorServer.h" -#include "TCPInterface.h" -// #include "communication_funcs.h" -#include "fmt/format.h" -#include "sls/logger.h" -#include "sls/network_utils.h" -#include "sls/sls_detector_defs.h" -#include "sls/versionAPI.h" -#include -#include -#include -#include -#include -#include - -namespace sls { - -/// @brief Base class for Matterhorn Server, can be used to implement a virtual -/// server for testing and actual server -template -class BaseMatterhornServer - : public DetectorServer> { - - public: - /** - * Constructor - * Starts up a Matterhorn server. - * Assembles a Matterhorn server using TCP and UDP detector interfaces - * throws an exception in case of failure - * @param port TCP/IP port number - */ - explicit BaseMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO) - : DetectorServer>(port) {} - - ~BaseMatterhornServer() = default; - - ReturnCode get_version(ServerInterface &socket); - - ReturnCode get_detector_type(ServerInterface &socket); - - ReturnCode initial_checks(ServerInterface &socket); - - ReturnCode get_num_udp_interfaces(ServerInterface &socket) const; - - /** - * @brief call function corresponding to the function ID received from the - * client and send back the result - * @param function_id the function ID received from the client - * @param socket the socket to send the result back to the client - */ - ReturnCode processFunction(const detFuncs function_id, - ServerInterface &socket); - - private: - static std::string getMatterhornServerVersion(); - - static constexpr uint8_t numUDPInterfaces = - 1; // only one udp per module for now -}; - -template -ReturnCode -BaseMatterhornServer::processFunction(const detFuncs function_id, - ServerInterface &socket) { - - switch (function_id) { - default: - throw RuntimeError( - fmt::format("Function {} not implemented", - getFunctionNameFromEnum((enum detFuncs)function_id))); - } -} - -template -ReturnCode BaseMatterhornServer::get_num_udp_interfaces( - ServerInterface &socket) const { - return static_cast( - socket.sendResult(static_cast(numUDPInterfaces))); -} - -template -ReturnCode -BaseMatterhornServer::get_version(ServerInterface &socket) { - - auto version = getMatterhornServerVersion(); - char version_cstr[MAX_STR_LENGTH]{}; - strncpy(version_cstr, version.c_str(), version.size()); - LOG(TLogLevel::logDEBUG) << "Matterhorn Server Version: " << version; - return static_cast(socket.sendResult( - version_cstr)); // TODO: check what would be possible return codes!!! -} - -template -ReturnCode BaseMatterhornServer::get_detector_type( - ServerInterface &socket) { - int detectortype = slsDetectorDefs::detectorType::MATTERHORN; - return static_cast(socket.sendResult(detectortype)); -} - -template -std::string BaseMatterhornServer::getMatterhornServerVersion() { - return APIMATTERHORN; -} - -template -ReturnCode -BaseMatterhornServer::initial_checks(ServerInterface &socket) { - - return static_cast(this)->initial_checks(socket); -} - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/MatterhornServer.h b/slsDetectorServers/matterhornServer/include/MatterhornServer.h deleted file mode 100644 index 586818b71..000000000 --- a/slsDetectorServers/matterhornServer/include/MatterhornServer.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "BaseMatterhornServer.h" -#include "TCPInterface.h" -#include "sls/sls_detector_defs.h" -#include -#include - -namespace sls { - -class MatterhornServer : public BaseMatterhornServer { - - public: - /** - * Constructor - * Starts up a Matterhorn server. - * Assembles a Matterhorn server using TCP and UDP detector interfaces - * throws an exception in case of failure - * @param port TCP/IP port number - */ - explicit MatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); - - ~MatterhornServer() = default; - - ReturnCode initial_checks(ServerInterface &socket); -}; - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp b/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp deleted file mode 100644 index 5f561a285..000000000 --- a/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -namespace sls { - -enum class IPCore : uint32_t; // forward declaration of IPCore enum class - -struct Register { - /// @brief IP core address space - const IPCore ip_core{}; // TODO replace by enum type - - /// @brief Offset of the register in bytes from the base address of the IP - /// core - const uint32_t offset_in_bytes{}; -}; - -struct RegisterField { - /// @brief Register to which the field belongs - const Register register_{}; - - /// @brief Bit position of the least significant bit of the field in the - /// register - const uint32_t bit_position{}; - - /// @brief Bitmask for the field - const uint32_t bitmask{}; -}; - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h b/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h deleted file mode 100644 index 61cb31bda..000000000 --- a/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h +++ /dev/null @@ -1,24 +0,0 @@ - -#include "BaseMatterhornServer.h" - -namespace sls { - -class VirtualMatterhornServer - : public BaseMatterhornServer { - - public: - /** - * Constructor - * Starts up a virtual Matterhorn server. - * Assembles a virtual Matterhorn server using TCP and UDP detector - * interfaces throws an exception in case of failure - * @param port TCP/IP port number - */ - explicit VirtualMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); - - ~VirtualMatterhornServer() = default; - - ReturnCode initial_checks(ServerInterface &socket); -}; - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp new file mode 100644 index 000000000..79d1c1883 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp @@ -0,0 +1,116 @@ +#pragma once +#include "DetectorServer.hpp" +#include "TCPInterface.hpp" +#include "fmt/format.h" +#include "helpers/type_traits.hpp" +#include "sls/logger.h" +#include "sls/network_utils.h" +#include "sls/sls_detector_defs.h" +#include +#include +#include +#include +#include +#include + +namespace sls { + +/// @brief Base class for Matterhorn Server, can be used to implement a virtual +/// server for testing and actual server +template +class BaseMatterhornServer + : public DetectorServer> { + + public: + /** + * Constructor + * Starts up a Matterhorn server. + * Assembles a Matterhorn server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit BaseMatterhornServer( + std::unique_ptr< + DetectorServerImpl::value>> + impl, + uint16_t port = DEFAULT_TCP_CNTRL_PORTNO) + : DetectorServer>(std::move(impl), + port) {} + + ~BaseMatterhornServer() = default; + + ProcessedResult set_counter_mask(ServerInterface &socket); + + ProcessedResult get_counter_mask(ServerInterface &socket) const; + + /** + * @brief call function corresponding to the function ID received from the + * client and send back the result + * @param function_id the function ID received from the client + * @param socket the socket to send the result back to the client + */ + ProcessedResult processFunction(const detFuncs function_id, + ServerInterface &socket); + + private: + DerivedServer *getDerived() { return static_cast(this); } + + const DerivedServer *getDerived() const { + return static_cast(this); + } +}; + +template +ProcessedResult +BaseMatterhornServer::processFunction(const detFuncs function_id, + ServerInterface &socket) { + + switch (function_id) { + case detFuncs::F_SET_COUNTER_MASK: + return set_counter_mask(socket); + case detFuncs::F_GET_COUNTER_MASK: + return get_counter_mask(socket); + default: + throw RuntimeError( + fmt::format("Function {} not implemented", + getFunctionNameFromEnum((enum detFuncs)function_id))); + } +} + +template +ProcessedResult +BaseMatterhornServer::set_counter_mask(ServerInterface &socket) { + + uint32_t counter_mask{}; + try { + (void)socket.Receive(counter_mask); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive counter mask: " << e.what(); + return_fail("Failed to receive counter mask: " + std::string(e.what())); + } + + try { + this->getImpl()->set_counter_mask(counter_mask); + } catch (const std::exception &e) { + return_fail("Failed to set counter mask: " + std::string(e.what())); + } + + return send_ok(socket); +} + +template +ProcessedResult BaseMatterhornServer::get_counter_mask( + ServerInterface &socket) const { + + uint32_t counter_mask{}; + + try { + counter_mask = this->getImpl()->get_counter_mask(); + } catch (const std::exception &e) { + return_fail("Failed to get counter mask: " + std::string(e.what())); + } + + return send_result(socket, counter_mask); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp new file mode 100644 index 000000000..9d0e45d22 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp @@ -0,0 +1,358 @@ +#pragma once +#include "ArmBusCommunication.hpp" +#include "DetectorServerImpl.hpp" +#include "MemoryModel.hpp" +#include "communication/SPICommunication.hpp" +#include "defs/MatterhornDefs.hpp" +#include "defs/RegisterDefs.hpp" +#include "helpers/type_traits.hpp" +#include "sls/versionAPI.h" +#include "utils/HelperFunctions.hpp" +#include +#include + +namespace sls { + +template +class VirtualMatterhornServerImpl; // forward declare + +template +class BaseMatterhornServerImpl + : public DetectorServerImpl< + is_stop_server::value> { + public: + BaseMatterhornServerImpl(); + ~BaseMatterhornServerImpl() = default; + + // TODO: probably virtaul server specific details, can be moved to derived + // class + /// @brief initial setup of detector + void setupDetector(); + + /// @brief get matterhorn server version + std::string get_server_version() const; + + static uint8_t get_detector_type(); + + static uint8_t get_num_udp_interfaces(); + + uint64_t get_num_frames() const; + void set_num_frames(const uint64_t num_frames); + + uint32_t get_num_triggers() const; + void set_num_triggers(const uint32_t num_triggers); + + uint32_t get_counter_mask() const; + void set_counter_mask(const uint32_t counter_mask); + + void set_module_position(const size_t module_row, const size_t module_col, + const size_t module_index); + + slsDetectorDefs::rxParameters get_receiver_parameters() const; + + protected: + using MemoryModel = std::conditional_t< + std::is_same_v::value>>, + VirtualMemoryModel, + HardwareMemoryModel>; // 32 bit registers + + // TODO: for now in MatterhornServer and not generic Server but can be + // templated on different IPCore types for each detector + BusCommunication + busCommunication{}; + + using SPICommunicationClass = std::conditional_t< + std::is_same_v::value>>, + VirtualSPICommunication, + HardwareSPICommunication>; + + SPICommunicationClass spiCommunication{}; + + private: + static constexpr uint8_t numUDPInterfaces = + 1; // only one udp per module for now +}; + +template +BaseMatterhornServerImpl< + DerivedMatterhornServerImpl>::BaseMatterhornServerImpl() { + + // map the IP core base addresses to memory + busCommunication.mapToMemory(); // TODO: should this happen in constructor? + + // TODO: need to check if chip is attached + spiCommunication.open_spi(); // TODO: should this happen in constructor? +} + +template +void BaseMatterhornServerImpl::setupDetector() { + // TODO: extend + try { + // stop server does not talk to the board + if constexpr (!this->stop_server) { + set_num_frames(1); + set_num_triggers(1); + set_counter_mask(0xF); // enable counter all counters by default + } + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to setup detector: " << e.what(); + this->detectorSetupStatus.error_message = std::string(e.what()); + this->detectorSetupStatus.setup_status = + detector_setup_status::SETUP_STATUS::FAILED_SETUP; + } + + this->detectorSetupStatus.setup_status = + detector_setup_status::SETUP_STATUS::SUCCESSFUL_SETUP; +} + +template +std::string +BaseMatterhornServerImpl::get_server_version() + const { + + return APIMATTERHORN; +} + +template +uint8_t BaseMatterhornServerImpl< + DerivedMatterhornServerImpl>::get_num_udp_interfaces() { + return numUDPInterfaces; +} + +template +uint8_t +BaseMatterhornServerImpl::get_detector_type() { + return slsDetectorDefs::detectorType::MATTERHORN; +} + +template +uint64_t +BaseMatterhornServerImpl::get_num_frames() const { + + try { + uint32_t num_frames = + busCommunication.readRegister(Reg::MH_SM_Frames_Reg); + return static_cast(num_frames); + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to read number of frames from register: " + << e.what(); + throw; + } +} + +template +void BaseMatterhornServerImpl::set_num_frames( + const uint64_t num_frames) { + + try { + busCommunication.writeRegister(Reg::MH_SM_Frames_Reg, + static_cast(num_frames)); + auto written_num_frames = busCommunication.readRegister( + Reg::MH_SM_Frames_Reg); // check if write was successful + + if (num_frames != static_cast(written_num_frames)) { + throw std::runtime_error( + fmt::format("Requested {} frames, but set {}", num_frames, + static_cast(written_num_frames))); + } + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to set number of frames: " << e.what(); + throw; + } +} + +template +void BaseMatterhornServerImpl::set_num_triggers( + const uint32_t num_triggers) { + + try { + busCommunication.writeRegister(Reg::MH_SM_Triggers_Reg, num_triggers); + auto written_num_triggers = busCommunication.readRegister( + Reg::MH_SM_Triggers_Reg); // check if write was successful + if (num_triggers != written_num_triggers) { + throw std::runtime_error( + fmt::format("Requested {} triggers, but set {}", num_triggers, + written_num_triggers)); + } + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to set number of triggers: " << e.what(); + throw; + } +} + +template +uint32_t +BaseMatterhornServerImpl::get_num_triggers() + const { + + try { + uint32_t num_triggers = + busCommunication.readRegister(Reg::MH_SM_Triggers_Reg); + return num_triggers; + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to read number of triggers from register: " + << e.what(); + throw; + } +} + +template +void BaseMatterhornServerImpl::set_counter_mask( + const uint32_t counter_mask) { + + // counter mask update to num consecutive counters and starting_counter + uint32_t spi_counter_mask{}; + try { + spi_counter_mask = convertCounterMaskToSPICounterMask(counter_mask); + } catch (const std::invalid_argument &e) { + LOG(logERROR) << "Failed to convert counter mask to SPI counter mask: " + << e.what(); + + throw std::invalid_argument( + "Failed to convert counter mask to SPI counter mask: " + + std::string(e.what())); + } + + try { + auto reg_value = spiCommunication.SPIread( + SPIRegisters::NUM_COUNTERS.register_, + 0); // TODO: how to handle different chip ids -> e.g. broadcast do + // we want it to be configurable for different chip ids? - + // Command overload for some of the SPI registers + + setSPIRegisterField(reg_value, SPIRegisters::NUM_COUNTERS, + spi_counter_mask); + + spiCommunication.SPIwrite(SPIRegisters::NUM_COUNTERS.register_, 0, + reg_value); + } catch (const std::exception &e) { + throw RuntimeError("Failed to set counter mask: " + + std::string(e.what())); + } +} + +template +uint32_t +BaseMatterhornServerImpl::get_counter_mask() + const { + + std::vector reg_value{}; + try { + reg_value = spiCommunication.SPIread( + SPIRegisters::NUM_COUNTERS.register_, + 0); // TODO: how to handle different chip ids - + } catch (const std::exception &e) { + throw sls::RuntimeError( + "Failed to read counter mask from SPI register: " + + std::string(e.what())); + } + + // stores num_counters and starting_counter 0b0000 -> counter 0 enabled, + // 0b0001 -> counter 1 enabled, 0b0010 + uint32_t spi_counter_mask = + getSPIRegisterField(reg_value, SPIRegisters::NUM_COUNTERS); + + uint32_t actual_counter_mask = + convertSPICounterMaskToCounterMask(spi_counter_mask); + + return actual_counter_mask; +} + +template +void BaseMatterhornServerImpl::set_module_position( + const size_t module_row, const size_t module_col, + const size_t module_index) { + + // write to register + uint32_t register_value_LSB{}; + uint32_t register_value_MSB{}; + + try { + register_value_LSB = + busCommunication.readRegister(Reg::Frame_HDR_ModCoord_LSB_Reg); + register_value_MSB = + busCommunication.readRegister(Reg::Frame_HDR_ModCoord_MSB_Reg); + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to read module position register: " + << e.what(); + throw; + } + + try { + setRegisterField(register_value_LSB, Reg::ModuleRow, module_row); + setRegisterField(register_value_LSB, Reg::ModuleCol, module_col); + setRegisterField(register_value_MSB, Reg::ModuleCoordz, 0); + setRegisterField(register_value_MSB, Reg::ModuleIndex, module_index); + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to set module position register fields: " + << e.what(); + throw; + } + + try { + busCommunication.writeRegister(Reg::Frame_HDR_ModCoord_LSB_Reg, + register_value_LSB); + + auto written_register_value_LSB = busCommunication.readRegister( + Reg::Frame_HDR_ModCoord_LSB_Reg); // check if write was successful + + busCommunication.writeRegister(Reg::Frame_HDR_ModCoord_MSB_Reg, + register_value_MSB); + + auto written_register_value_MSB = busCommunication.readRegister( + Reg::Frame_HDR_ModCoord_MSB_Reg); // check if write was successful + + if (register_value_LSB != written_register_value_LSB || + register_value_MSB != written_register_value_MSB) { + throw std::runtime_error( + fmt::format("LSB: requested {}, but set {}. " + "MSB: requested {}, but set {}", + register_value_LSB, written_register_value_LSB, + register_value_MSB, written_register_value_MSB)); + } + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to write module position register: " + << e.what(); + throw; + } +} + +template +slsDetectorDefs::rxParameters +BaseMatterhornServerImpl::get_receiver_parameters() + const { + + slsDetectorDefs::rxParameters rx_params{}; + + rx_params.udpInterfaces = numUDPInterfaces; + + rx_params.udp_dstip = this->udpDetails[0].dstip; + + rx_params.udp_dstport = this->udpDetails[0].dstport; + + rx_params.udp_dstmac = this->udpDetails[0].dstmac; + + rx_params.frames = get_num_frames(); + + rx_params.triggers = get_num_triggers(); + + // TODO: extend + + // rx_params.expTimeNs = 0; + + // rx_params.periodNs = 0; + + // rx_params.dynamicRange = 0; + + // rx_params.timMode = AUTO_TIMING; + + // rx_params.counterMask = 0; + + return rx_params; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp index 8c37df196..6551bf2f8 100644 --- a/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp +++ b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp @@ -1,11 +1,13 @@ -#include "CommandLineOptions.h" -#include "VirtualMatterhornServer.h" +#include "CommandLineOptions.hpp" +#include MATTERHORN_SERVER_HEADER +#include "helpers/Helpers.hpp" #include "sls/logger.h" #include "sls/sls_detector_exceptions.h" #include "sls/versionAPI.h" #include #include +#include #include #include #include @@ -62,6 +64,9 @@ int main(int argc, char *argv[]) { LOG(TLogLevel::logINFOMAGENTA) << cli.printOptions(); + // free shared memory from previous run (not removed if detector crashed) + freeSharedMemory(); + // Register Ctrl+C handler std::signal(SIGINT, sigInterruptHandler); @@ -75,11 +80,12 @@ int main(int argc, char *argv[]) { LOG(TLogLevel::logINFOBLUE) << "Stop Server [" << opts.port + 1 << "]"; try { - VirtualMatterhornServer stopServer(opts.port + 1); + MATTERHORN_SERVER_CLASS stopServer(opts.port + 1); while (!interruption) { pause(); // wait for signal to exit } } catch (...) { + LOG(logERROR) << "Some Error occured in Stop Server, exiting"; kill(getppid(), SIGINT); // tell parent to exit // TODO: should then // also return EXIT_FAILURE } @@ -93,13 +99,14 @@ int main(int argc, char *argv[]) { LOG(TLogLevel::logINFOBLUE) << "Control Server [" << opts.port << "]\n"; try { - VirtualMatterhornServer server( + MATTERHORN_SERVER_CLASS server( opts.port); // TODO use virtual if compiled with virtual // simulators on while (!interruption) { pause(); // wait for signal to exit } } catch (...) { + LOG(logERROR) << "Some Error occured in Control Server, exiting"; LOG(sls::logINFOBLUE) << "Exiting Control Server [ Tid: " << gettid() << " ]"; LOG(sls::logINFO) << "Exiting Detector Server"; diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp deleted file mode 100644 index 522d5b486..000000000 --- a/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "MatterhornServer.h" - -namespace sls { - -MatterhornServer::MatterhornServer(uint16_t port) - : BaseMatterhornServer(port) { - - // TODO: when do i set the udp mac and ip ? - - // should maybe be part of the constructor? - tcpInterface->startTCPServer(); - - // need a function to setup detector - e.g. set all registers etc. -} - -ReturnCode MatterhornServer::initial_checks(ServerInterface &socket) { - - // TODO: add more checks here, for now just return true to be able to test - // the should check firmware -client compatibility - bool initial_checks_passed = true; - return static_cast(socket.sendResult(initial_checks_passed)); -} - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp new file mode 100644 index 000000000..84ba7f96b --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "BaseMatterhornServer.hpp" +#include "MatterhornServerImpl.hpp" +#include "TCPInterface.hpp" +#include "sls/sls_detector_defs.h" +#include +#include + +namespace sls { + +template +class MatterhornServer + : public BaseMatterhornServer> { + + public: + /** + * Constructor + * Starts up a Matterhorn server. + * Assembles a Matterhorn server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit MatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + ~MatterhornServer() = default; +}; + +template +MatterhornServer::MatterhornServer(uint16_t port) + : BaseMatterhornServer>( + std::make_unique>(), port) { + + // should maybe be part of the constructor? + this->tcpInterface->startTCPServer(); + + // TODO: no init_server function for now is it neccessary to set the init + // flag + this->getImpl()->setupDetector(); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp new file mode 100644 index 000000000..9640a273c --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp @@ -0,0 +1,70 @@ +#pragma once +#include "BaseMatterhornServerImpl.hpp" + +namespace sls { + +template +class MatterhornServerImpl + : public BaseMatterhornServerImpl> { + + public: + MatterhornServerImpl() = default; + ~MatterhornServerImpl() = default; + + slsDetectorDefs::runStatus get_run_status() const; // TODO: impement + + void set_module_position_and_update_srcudpmac( + const std::array &position_info); + + void set_source_udp_mac([[maybe_unused]] const uint64_t src_mac); +}; + +template +slsDetectorDefs::runStatus +MatterhornServerImpl::get_run_status() const { + + // TODO: will also have a scanStatus - scanStatus should be in base + // implementation and shared between virtual and actual detector - split + // this function into two. + return slsDetectorDefs::runStatus::IDLE; // TODO: implement +} + +template +void MatterhornServerImpl:: + set_module_position_and_update_srcudpmac( + const std::array &position_info) { + + // position_info = [num_modules_in_y, module_index] + + const size_t module_row = position_info[1] % position_info[0]; + if (position_info[0] <= 0) { + throw RuntimeError("Number of modules in y direction cannot be 0."); + } + const size_t module_col = position_info[1] / position_info[0]; + + try { + this->set_module_position(module_row, module_col, position_info[1]); + } catch (const std::exception &e) { + throw RuntimeError("Failed to set module position: " + + std::string(e.what())); + } + + // TODO: update + if (this->udpDetails[0].srcmac == + 0) { // only configure if source mac address is not set already + uint64_t newSrcMac = + 0x000000000000; // TODO: vendor address will be on SOM memory/ + // different for 10G/100G + this->updateSrcMacAddress(newSrcMac); + } +} + +template +void MatterhornServerImpl::set_source_udp_mac( + const uint64_t src_mac) { + + throw RuntimeError( + "Cannot overwrite vendor specific source UDP MAC address."); +} + +} // end namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp deleted file mode 100644 index 8c82da20c..000000000 --- a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "VirtualMatterhornServer.h" - -namespace sls { - -VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port) - : BaseMatterhornServer(port) { - - udpDetails[0].srcip = LOCALHOSTIP_INT; - - // should maybe be part of the constructor? - tcpInterface->startTCPServer(); - - // need a function to setup detector - e.g. set all registers etc. -} - -ReturnCode VirtualMatterhornServer::initial_checks(ServerInterface &socket) { - - // TODO: add more checks here, for now just return true to be able to test - // the should check firmware -client compatibility - bool initial_checks_passed = true; - return static_cast(socket.sendResult(initial_checks_passed)); -} - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp new file mode 100644 index 000000000..a3c86123e --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp @@ -0,0 +1,39 @@ + +#include "BaseMatterhornServer.hpp" +#include "VirtualMatterhornServerImpl.hpp" + +namespace sls { + +template +class VirtualMatterhornServer + : public BaseMatterhornServer> { + + public: + /** + * Constructor + * Starts up a virtual Matterhorn server. + * Assembles a virtual Matterhorn server using TCP and UDP detector + * interfaces throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit VirtualMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + ~VirtualMatterhornServer() = default; +}; + +template +VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port) + : BaseMatterhornServer>( + std::make_unique>(), port) { + + LOG(logDEBUG) << "Initializing virtual Matterhorn server on port " << port; + + // should maybe be part of the constructor? + this->tcpInterface->startTCPServer(); + + // TODO: no init_server function for now is it neccessary to set the init + // flag + this->getImpl()->setupDetector(); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp new file mode 100644 index 000000000..e442abf6b --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "BaseMatterhornServerImpl.hpp" +#include "sls/ToString.h" + +namespace sls { + +template +class VirtualMatterhornServerImpl + : public BaseMatterhornServerImpl< + VirtualMatterhornServerImpl> { + + public: + VirtualMatterhornServerImpl(); + ~VirtualMatterhornServerImpl() = default; + + slsDetectorDefs::runStatus get_run_status() const; + + void set_module_position_and_update_srcudpmac( + const std::array &position_info); + + void set_source_udp_mac(const uint64_t newsrcudpMac); +}; + +template +VirtualMatterhornServerImpl::VirtualMatterhornServerImpl() { + this->set_source_udp_ip(LOCALHOSTIP_INT); +} + +template +slsDetectorDefs::runStatus +VirtualMatterhornServerImpl::get_run_status() const { + + slsDetectorDefs::runStatus scanstatus{}; + slsDetectorDefs::runStatus status{}; + + scanstatus = this->shm()->scanStatus; + status = this->shm()->status; + + // TODO: why only error and running? what about other states? + if (scanstatus == slsDetectorDefs::runStatus::ERROR || + scanstatus == slsDetectorDefs::runStatus::RUNNING) { + LOG(logINFO) << fmt::format("Scan status: {}\n", ToString(scanstatus)); + return scanstatus; + } + + LOG(logINFO) << fmt::format("Status: {}\n", ToString(status)); + return status; +} + +template +void VirtualMatterhornServerImpl:: + set_module_position_and_update_srcudpmac( + const std::array &position_info) { + + const size_t module_row = position_info[1] % position_info[0]; + const size_t module_col = position_info[1] / position_info[0]; + + try { + this->set_module_position(module_row, module_col, position_info[1]); + } catch (const std::exception &e) { + throw RuntimeError("Failed to set module position: " + + std::string(e.what())); + } + + // configure mac address based on module position + if (this->udpDetails[0].srcmac == + 0) { // only configure if source mac address is not set already + uint64_t newSrcMac = + generateMacAddressfromModulePosition(module_row, module_col); + + this->updateSrcMacAddress(newSrcMac); + } +} + +template +void VirtualMatterhornServerImpl::set_source_udp_mac( + const uint64_t newsrcudpMac) { + + if (!isValidMac(newsrcudpMac)) { + throw RuntimeError("Invalid source MAC address: unicast bit or local " + "administration bit is not set"); + } + + this->updateSrcMacAddress(newsrcudpMac); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp new file mode 100644 index 000000000..8feaffa07 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp @@ -0,0 +1,120 @@ +#include "SPICommunication.hpp" +#include +#include +#include +#include +#include + +namespace sls { + +void HardwareSPICommunication::open_spi() { + + // TODO device can change + spi_filedescriptor = open("/dev/spidev2.0", O_RDWR); // TODO use O_SYNC? + + if (spi_filedescriptor < 0) { + throw RuntimeError("Could not open /dev/spidev2.0"); + } + + LOG(logINFO) << fmt::format("SPI Read: opened spidev2.0 with fd={}", + spi_filedescriptor); +} + +void HardwareSPICommunication::close_spi() { + if (spi_filedescriptor >= 0) { + close(spi_filedescriptor); + LOG(logINFO) << "SPI Read: closed spidev2.0"; + spi_filedescriptor = -1; + } +} + +HardwareSPICommunication::~HardwareSPICommunication() { close_spi(); } + +std::vector +HardwareSPICommunication::spi_read(const size_t n_bytes, const uint8_t chip_id, + const uint8_t register_id) const { + + // allocate dummy data to shift out the data (first byte is command byte) + if (n_bytes == std::numeric_limits::max()) { + throw RuntimeError("SPI read size overflow"); + } + + std::vector dummy_data( + n_bytes + 1, std::byte{0x00}); // +1 for the command byte + + // First byte of the message is 4 bits chip_id then 4 bits register_id + dummy_data[0] = + static_cast(((chip_id & 0xF) << 4) | (register_id & 0xF)); + + // allocate data buffer to read out data into + std::vector read_data_buffer(n_bytes + 1, std::byte{0x00}); + + spi_ioc_transfer send_cmd{}; + send_cmd.len = n_bytes + 1; // +1 for the command byte + send_cmd.tx_buf = reinterpret_cast(dummy_data.data()); + send_cmd.rx_buf = reinterpret_cast(read_data_buffer.data()); + + // 0 - Normal operation, 1 - CSN remains zero after operation + // We use cs_change = 1 to not close the SPI transaction and + // allow for shifting the read out data back in to restore the + // register + send_cmd.cs_change = 1; + + // transfer here + if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) { + + throw RuntimeError( + fmt::format("SPI write failed with {}:{}", errno, strerror(errno))); + } + + // copy data to output buffer + std::vector output_data(n_bytes); + std::memcpy(output_data.data(), read_data_buffer.data() + 1, n_bytes); + + // copy the read out data back to the dummy data buffer to shift it back in + send_cmd.tx_buf = send_cmd.rx_buf; + + send_cmd.cs_change = + 0; // end the SPI transaction after shifting back in the data + + if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) { + throw RuntimeError( + fmt::format("SPI write failed with {}:{}", errno, strerror(errno))); + } + + return output_data; +} + +void HardwareSPICommunication::spi_write(const uint8_t chip_id, + const uint8_t register_id, + const std::vector &data) { + + const size_t n_bytes = data.size(); + + if (n_bytes == std::numeric_limits::max()) { + throw RuntimeError("SPI read size overflow"); + } + + // First byte of the message is 4 bits chip_id then 4 bits register_id + std::vector write_data(n_bytes + 1); // +1 for the command byte + + write_data[0] = + static_cast(((chip_id & 0xF) << 4) | (register_id & 0xF)); + + std::memcpy(write_data.data() + 1, data.data(), n_bytes); + + spi_ioc_transfer send_cmd{}; + send_cmd.len = n_bytes + 1; // +1 for the command byte + send_cmd.tx_buf = reinterpret_cast(write_data.data()); + + send_cmd.cs_change = + 0; // end the SPI transaction after the write (we dont need to shift + // back in data here since we are not doing a read) + + if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) { + throw RuntimeError( + fmt::format("SPI write failed with {}:{}", errno, strerror(errno))); + } +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp new file mode 100644 index 000000000..f74805db9 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp @@ -0,0 +1,205 @@ +#include "MemoryModel.hpp" +#include "SPIRegisterHelperStructs.hpp" +#include "defs/MatterhornDefs.hpp" +#include "defs/SPIRegisterDefs.hpp" +#include "fmt/format.h" +#include "sls/logger.h" +#include "sls/sls_detector_exceptions.h" +#include +#include + +namespace sls { + +/// @brief abstract base class for SPI communication +template class SPICommunication { + + public: + SPICommunication() = default; + + ~SPICommunication() = default; + + std::vector SPIread(const SPIRegister &spi_reg, + const uint8_t chip_id) const; + + void SPIwrite(const SPIRegister &spi_reg, const uint8_t chip_id, + const std::vector &data); + + void open_spi(); + + private: + DerivedSPIModel *getDerived() { + return static_cast(this); + } +}; + +template +void SPICommunication::open_spi() { + getDerived()->open_spi(); +} + +template +std::vector +SPICommunication::SPIread(const SPIRegister &spi_reg, + const uint8_t chip_id) const { + + if (chip_id >= MatterhornDefs::NUM_CHIPS_PER_MODULE) { + throw RuntimeError( + fmt::format("Chip id {} is out of range (0-{})", chip_id, + MatterhornDefs::NUM_CHIPS_PER_MODULE - 1)); + } + return static_cast(this)->spi_read( + spi_reg.n_bytes, chip_id, spi_reg.spi_register_id); +} + +template +void SPICommunication::SPIwrite( + const SPIRegister &spi_reg, const uint8_t chip_id, + const std::vector &data) { + + if (chip_id >= MatterhornDefs::NUM_CHIPS_PER_MODULE) { + throw RuntimeError( + fmt::format("Chip id {} is out of range (0-{})", chip_id, + MatterhornDefs::NUM_CHIPS_PER_MODULE - 1)); + } + + if (data.size() != spi_reg.n_bytes) { + throw RuntimeError(fmt::format("Data size {} does not match number of " + "bytes {} for SPI register {}", + data.size(), spi_reg.n_bytes, + spi_reg.spi_register_id)); + } + + getDerived()->spi_write(chip_id, spi_reg.spi_register_id, data); + + getDerived()->spi_write(chip_id, + SPIRegisters::SPI_REG_ExtraClocks.spi_register_id, + std::vector{std::byte{ + 0x00}}); // extra clock trigger to actually load + // the new value into the register +} + +/** + * Non destructive read from SPI register. Read n_bytes by shifting in + * dummy data while keeping csn 0 after the operation. Shift the read out + * data back in to restore the register. + */ +class HardwareSPICommunication + : public SPICommunication { + + public: + HardwareSPICommunication() = default; + + ~HardwareSPICommunication(); + + void open_spi(); + + void spi_write(const uint8_t chip_id, const uint8_t register_id, + const std::vector &data); + + std::vector spi_read(const size_t n_bytes, const uint8_t chip_id, + const uint8_t register_id) const; + + private: + int spi_filedescriptor = -1; + + void close_spi(); +}; + +// template // non +// type template parameters only for c++20 +template // TODO add a type trait to ensure it stores + // all fields +class VirtualSPICommunication + : public SPICommunication> { + + public: + VirtualSPICommunication() { + // TODO should it be in the constructor? + /* + (virtual_registers.emplace( + SPIRegisters.spi_register_id, + VirtualMemoryModel{SPIRegisters.spi_register_id, + SPIRegisters.n_bytes * + NUM_CHIPS_PER_MODULE}), + ...); + */ + + LOG(logDEBUG) << fmt::format( + "Initializing virtual SPI communication with {} registers for {} " + "chips per module", + SPIRegisters::spiregisters.size(), + SPIRegisters::NUM_CHIPS_PER_MODULE); + + for (const auto ® : SPIRegisters::spiregisters) { + virtual_registers.emplace( + reg.spi_register_id, + VirtualMemoryModel{ + reg.spi_register_id, + reg.n_bytes * SPIRegisters::NUM_CHIPS_PER_MODULE}); + } + + LOG(logDEBUG) << fmt::format( + "Initialized virtual SPI communication with {} registers for {} " + "chips per module", + virtual_registers.size(), SPIRegisters::NUM_CHIPS_PER_MODULE); + + for (const auto &[register_id, register_memory] : virtual_registers) { + LOG(logDEBUG) << fmt::format( + "Virtual SPI register with id {} has virtual", register_id); + } + } + + ~VirtualSPICommunication() = default; + + void open_spi() { + + // resize the virtual register memory to the correct size based on + // the defined SPI registers + for (auto &[register_id, register_memory] : virtual_registers) { + LOG(logDEBUG) << fmt::format("Mapping virtual SPI register with id " + "{} to virtual memory", + register_id); + register_memory.mapToMemory(); + } + } + + std::vector spi_read(const size_t n_bytes, const uint8_t chip_id, + const uint8_t register_id) const { + + auto mapped_register = + virtual_registers.at(register_id).getMappedMemoryPtr(); + + mapped_register += + chip_id * n_bytes; // TODO: how to handle different chip ids -> + // e.g. broadcast do we want it to be + // configurable for different chip ids? + + // TODO: should I emulate the shifting in of dummy data and shifting + // out of the register data here to be more realistic? + std::vector output_data(n_bytes); + std::memcpy(output_data.data(), mapped_register, n_bytes); + + return output_data; + } + + void spi_write(const uint8_t chip_id, const uint8_t register_id, + const std::vector &data) { + + auto mapped_register = + virtual_registers.at(register_id).getMappedMemoryPtr(); + + mapped_register += + chip_id * data.size(); // TODO: how to handle different + // chip ids -> e.g. broadcast do + + // TODO: should I emulate the shifting in of dummy data and shifting + // out of + std::memcpy(mapped_register, data.data(), data.size()); + } + + private: + /// @brief map of register id to virtual memory model for each register + std::map> virtual_registers{}; +}; + +} // namespace sls diff --git a/slsDetectorServers/matterhornServer/src/communication/SPIRegisterHelperStructs.hpp b/slsDetectorServers/matterhornServer/src/communication/SPIRegisterHelperStructs.hpp new file mode 100644 index 000000000..a0dc85afb --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/communication/SPIRegisterHelperStructs.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "fmt/format.h" +#include +#include + +namespace sls { + +struct SPIRegister { + /// @brief SPI register ID (0-15) + uint16_t spi_register_id{}; + + /// @brief number of bytes in the register + uint64_t n_bytes{}; +}; + +struct SPIRegisterField { + /// @brief SPI register to which teh field belongs + SPIRegister register_{}; + + /// @brief least significant bit position of the field in the register + uint64_t lsb_position{}; + + /// @brief number of bits in the field + /// TODO: can it be larger? + uint32_t num_bits{}; +}; + +// TODO: maybe change uint32_t but max field size is 32 bits so should be fine +// for now +void inline setSPIRegisterField(std::vector ®ister_value, + const SPIRegisterField &field, + uint32_t field_value) { + + // check that the field value can fit in the bitmask + if ((field_value >> field.num_bits) != 0) { + throw std::invalid_argument(fmt::format( + "Value {} cannot fit in field {}", field_value, field.num_bits)); + } + + constexpr uint8_t bits_per_byte = 8; + + // TODO: mmh doesnt feel very modern - maybe better to cast to uint32_t, + // alignment issues? + for (std::size_t i = 0; i < field.num_bits; ++i) { + std::size_t offset = field.lsb_position + i; + std::size_t byte_index = offset / bits_per_byte; + std::size_t bit_index = offset % bits_per_byte; + + std::byte mask = std::byte(1) << bit_index; + + const bool bit = (field_value >> i) & 0x1; + + if (bit) { + register_value[byte_index] |= + mask; // set the bit in the register value + } else { + register_value[byte_index] &= + ~mask; // clear the bit in the register value + } + } +} + +uint32_t inline getSPIRegisterField( + const std::vector ®ister_value, + const SPIRegisterField &field) { + + uint32_t field_value = 0; + constexpr uint8_t bits_per_byte = 8; + + for (std::size_t i = 0; i < field.num_bits; ++i) { + std::size_t offset = field.lsb_position + i; + std::size_t byte_index = offset / bits_per_byte; + std::size_t bit_index = offset % bits_per_byte; + + std::byte mask = std::byte(1) << bit_index; + + field_value |= + (static_cast((register_value[byte_index] & mask) >> + bit_index) + << i); // extract the field value bit from the register value and + // set it in the correct position in the field value + } + + return field_value; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/defs/MatterhornDefs.hpp b/slsDetectorServers/matterhornServer/src/defs/MatterhornDefs.hpp new file mode 100644 index 000000000..d49115d52 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/defs/MatterhornDefs.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "RegisterDefs.hpp" +#include "SPIRegisterDefs.hpp" +#include +#include + +namespace sls { + +namespace MatterhornDefs { + +constexpr uint8_t NUM_CHIPS_PER_MODULE = 8; + +// TODO: should probably be a specialized template struct + +/// @brief list of Matterhorn SPI registers +struct MatterhornSPIRegisters { + + constexpr static std::array spiregisters{ + SPIRegisters::SPI_REG_ConfigUnit, + SPIRegisters::SPI_REG_ConfigCML, + SPIRegisters::SPI_REG_ManualSelector, + SPIRegisters::SPI_REG_CoreRSTUnit, + SPIRegisters::SPI_REG_StoreRSTUnit, + SPIRegisters::SPI_REG_Trimbits, + SPIRegisters::SPI_REG_McGyver, + SPIRegisters::SPI_REG_McGyver_par_load, + SPIRegisters::SPI_REG_ActionReg, + SPIRegisters::SPI_REG_InternalDACs, + SPIRegisters::SPI_REG_ChecksumReg, + SPIRegisters::SPI_REG_ExtraClocks}; + + constexpr static uint8_t NUM_CHIPS_PER_MODULE = + MatterhornDefs::NUM_CHIPS_PER_MODULE; +}; + +/// @brief list of Matterhorn IP cores +struct MatterHornIPCores { + + using ipcore_enum_type = IPCore; + + constexpr static std::array ipcores{ + IPCore::MH_RO_SM_AXI, IPCore::FHDR_AXI, IPCore::AURORA_STATUS, + IPCore::AURORA_STATUS2, IPCore::PACKETIZERREG}; + + constexpr static size_t ip_core_block_size = IPCORE_REGISTER_BLOCK_SIZE; +}; + +} // namespace MatterhornDefs + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/RegisterDefs.hpp b/slsDetectorServers/matterhornServer/src/defs/RegisterDefs.hpp similarity index 80% rename from slsDetectorServers/matterhornServer/include/RegisterDefs.hpp rename to slsDetectorServers/matterhornServer/src/defs/RegisterDefs.hpp index 60335ccb4..51297d2f3 100644 --- a/slsDetectorServers/matterhornServer/include/RegisterDefs.hpp +++ b/slsDetectorServers/matterhornServer/src/defs/RegisterDefs.hpp @@ -1,19 +1,27 @@ // clang-format off +#pragma once #include "RegisterHelperStructs.hpp" namespace sls { /// @brief Enum for IP cores, value are adresses -constexpr enum class IPCore : uint32_t { - MH_RO_SM_AXI = 0, // dummy adresses for now - FHDR_AXI = 1, - AURORA_STATUS = 2, - AURORA_STATUS2 = 3, - PACKETIZERREG = 4, - UNKNOWN = 5 +enum class IPCore : uint32_t { + MH_RO_SM_AXI = 0xB0010000, + FHDR_AXI = 0xB0011000, + AURORA_STATUS = 0xB0014000, + AURORA_STATUS2 = 0xB0015000, + PACKETIZERREG = 0x00000000, // TODO: need to update with actual address + UNKNOWN = 0x00000000 // dont know yet }; +constexpr size_t IPCORE_REGISTER_BLOCK_SIZE = + 0x1000; // size of each IP core address space in bytes // TODO: maybe add in + // other file definitions + +// clang-format off +namespace Reg { + // Register definitions constexpr Register CTRL_Reg{IPCore::UNKNOWN, 0x0}; @@ -22,7 +30,7 @@ constexpr Register Status_Reg{IPCore::UNKNOWN, 0x4}; constexpr Register FPGAVersionReg{IPCore::UNKNOWN, 0x8}; -constexpr Register FPGA_GIT_HEAD{IPCore::UNKNOWN, 0xc}; +constexpr Register FPGA_GIT_HEADReg{IPCore::UNKNOWN, 0xc}; constexpr Register FixedPatternReg{IPCore::UNKNOWN, 0x10}; @@ -42,6 +50,8 @@ constexpr Register MH_SM_StoreLength_Reg{IPCore::MH_RO_SM_AXI, 0x10}; constexpr Register MH_SM_ResetMHLength_Reg{IPCore::MH_RO_SM_AXI, 0x14}; +constexpr Register MH_SM_Triggers_Reg{IPCore::MH_RO_SM_AXI, 0x18}; + constexpr Register Frame_HDR_Set_Reg{IPCore::FHDR_AXI, 0x0}; constexpr Register Frame_HDR_FrameNumLSB_Reg{IPCore::FHDR_AXI, 0x4}; @@ -116,7 +126,7 @@ constexpr RegisterField FPGADetType{ FPGAVersionReg, 24, 0xff}; constexpr RegisterField FPGA_GIT_HEAD{ - FPGA_GIT_HEAD, 0, 0xffffffff}; + FPGA_GIT_HEADReg, 0, 0xffffffff}; constexpr RegisterField FixedPattern{ FixedPatternReg, 0, 0xffffffff}; @@ -136,6 +146,24 @@ constexpr RegisterField Start_Acquistion{ constexpr RegisterField Stop_Acquistion{ MH_SM_Ctrl_Reg, 1, 0x1}; +constexpr RegisterField External_Counter_Enable{ + MH_SM_Ctrl_Reg, 2, 0x1}; + +constexpr RegisterField Parallel_RO{ + MH_SM_Ctrl_Reg, 3, 0x1}; + +constexpr RegisterField Trigger_Mode{ + MH_SM_Ctrl_Reg, 4, 0x3}; + +constexpr RegisterField HW_Trigger_Polarity{ + MH_SM_Ctrl_Reg, 6, 0x1}; + +constexpr RegisterField SW_Trigger{ + MH_SM_Ctrl_Reg, 7, 0x1}; + +constexpr RegisterField Reset_Readout_SM{ + MH_SM_Ctrl_Reg, 8, 0x1}; + constexpr RegisterField MH_Readout_Exposure_Time{ MH_SM_Exposure_Reg, 0, 0xffffffff}; @@ -151,6 +179,9 @@ constexpr RegisterField MH_SM_StoreLength{ constexpr RegisterField MH_SM_ResetMHLength{ MH_SM_ResetMHLength_Reg, 0, 0xffffffff}; +constexpr RegisterField MH_SM_Triggers{ + MH_SM_Triggers_Reg, 0, 0xffffffff}; + constexpr RegisterField Frame_Hdr_Set_Framenumber{ Frame_HDR_Set_Reg, 0, 0x1}; @@ -236,5 +267,17 @@ constexpr RegisterField Coordz{ PktCoordReg2, 0, 0xffff}; +constexpr RegisterField ModuleRow{ + Frame_HDR_ModCoord_LSB_Reg, 0, 0xffff}; + +constexpr RegisterField ModuleCol{ + Frame_HDR_ModCoord_LSB_Reg, 16, 0xffff}; + +constexpr RegisterField ModuleCoordz{ + Frame_HDR_ModCoord_MSB_Reg, 0, 0xffff}; + +constexpr RegisterField ModuleIndex{ + Frame_HDR_ModCoord_MSB_Reg, 16, 0xffff}; +} // namespace Reg } // namespace sls // clang-format on diff --git a/slsDetectorServers/matterhornServer/src/defs/SPIRegisterDefs.hpp b/slsDetectorServers/matterhornServer/src/defs/SPIRegisterDefs.hpp new file mode 100644 index 000000000..7f29f3b63 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/defs/SPIRegisterDefs.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "SPIRegisterHelperStructs.hpp" +#include + +namespace sls { + +namespace SPIRegisters { + +// SPI registers +constexpr SPIRegister SPI_REG_ConfigUnit{0, 8}; + +constexpr SPIRegister SPI_REG_ConfigCML{1, 1}; + +constexpr SPIRegister SPI_REG_ManualSelector{2, 2}; + +constexpr SPIRegister SPI_REG_CoreRSTUnit{3, 4}; + +constexpr SPIRegister SPI_REG_StoreRSTUnit{4, 2}; + +constexpr SPIRegister SPI_REG_Trimbits{5, 256}; + +constexpr SPIRegister SPI_REG_McGyver{6, 512}; + +constexpr SPIRegister SPI_REG_McGyver_par_load{7, 512}; + +constexpr SPIRegister SPI_REG_ActionReg{11, 1}; + +constexpr SPIRegister SPI_REG_InternalDACs{13, 4}; + +constexpr SPIRegister SPI_REG_ChecksumReg{14, 32}; + +// Used to generate extra clocks after writing to trigger the load of the new +// value +constexpr SPIRegister SPI_REG_ExtraClocks{12, + 1}; // TODO: dont know what size is + +// SPI register fields +constexpr SPIRegisterField OUTPUT_MODE{SPI_REG_ConfigUnit, 4, 3}; + +/// @brief first two bits starting counter, second two bits number of counters +/// to read e.g. 0b0100 -> read counter 0 and 1, 0b0001 -> read counter 1 +constexpr SPIRegisterField NUM_COUNTERS{SPI_REG_ConfigUnit, 8, 4}; + +/// @brief 00-> 16 bit, 01 -> 8 bit, 10 -> 4 bit, 11 -> reserved 16 bit +constexpr SPIRegisterField DYNAMIC_RANGE{SPI_REG_ConfigUnit, 14, 2}; + +// TODO: continue defining the rest of the fields as needed + +} // namespace SPIRegisters + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.cpp b/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.cpp new file mode 100644 index 000000000..336517f3d --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.cpp @@ -0,0 +1,75 @@ +#include "HelperFunctions.hpp" +#include + +namespace sls { + +uint32_t convertCounterMaskToSPICounterMask(const uint32_t counter_mask) { + uint32_t spi_counter_mask = 0; + + switch (counter_mask) { + case 0b1: + spi_counter_mask = 0b0000; // counter 0 enabled + break; + case 0b10: + spi_counter_mask = 0b0001; // counter 1 enabled + break; + case 0b100: + spi_counter_mask = 0b0010; // counter 2 enabled + break; + case 0b1000: + spi_counter_mask = 0b0011; // counter 3 enabled + break; + case 0b11: + spi_counter_mask = 0b0100; // counter 0 and 1 enabled + break; + case 0b110: + spi_counter_mask = 0b0101; // counter 1 and 2 enabled + break; + case 0b1100: + spi_counter_mask = 0b0110; // counter 2 and 3 enabled + break; + case 0b1001: + spi_counter_mask = 0b0111; // counter 0 and 3 enabled + break; + case 0b111: + spi_counter_mask = 0b1000; // counter 0, 1 and 2 enabled + break; + case 0b1110: + spi_counter_mask = 0b1001; // counter 1, 2 and 3 enabled + break; + case 0b1101: + spi_counter_mask = 0b1010; // counter 0, 2 and 3 enabled + break; + case 0b1011: + spi_counter_mask = 0b1011; // counter 0, 1 and 3 enabled + break; + case 0b1111: + spi_counter_mask = 0b1100; // counter 0, 1, 2 and 3 enabled + break; + default: + throw std::invalid_argument( + "Invalid counter mask: Only contiguous counters are enabled " + "(including wrap around)"); + } + + return spi_counter_mask; +} + +uint32_t convertSPICounterMaskToCounterMask(const uint32_t spi_counter_mask) { + + uint32_t counter_mask{}; + + uint8_t start_counter = spi_counter_mask & 0b11; // extract starting counter + uint8_t num_counters = + ((spi_counter_mask >> 2) & 0b11) + 1; // extract number of counters + + constexpr uint8_t max_counters = 4; + + for (uint8_t i = 0; i < num_counters; ++i) { + counter_mask |= (1 << ((start_counter + i) % max_counters)); + } + + return counter_mask; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.hpp b/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.hpp new file mode 100644 index 000000000..550a8bd8a --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/utils/HelperFunctions.hpp @@ -0,0 +1,26 @@ +/** + * @file HelperFunctions.hpp + * @short contains helper functions for the Matterhorn server implementation + * e.g. for processing of specific commands + */ +#include + +namespace sls { + +/** + * @brief converts the counter mask received from the client to the actual + * counter mask to be written to the SPI register based on the starting counter + * and number of counters to read + * @return actual counter mask to be written to the SPI register + */ +uint32_t convertCounterMaskToSPICounterMask(const uint32_t counter_mask); + +/** + * @brief converts the actual counter mask read from the SPI register to the + * counter mask to be sent to the client e.g. bit set to 1 if counter + * enabled + * @return counter mask to be sent to the client + */ +uint32_t convertSPICounterMaskToCounterMask(const uint32_t spi_counter_mask); + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/tests/CMakeLists.txt b/slsDetectorServers/matterhornServer/tests/CMakeLists.txt new file mode 100644 index 000000000..e4b6e4cfb --- /dev/null +++ b/slsDetectorServers/matterhornServer/tests/CMakeLists.txt @@ -0,0 +1,9 @@ + +target_sources(tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/test-HelperFunctions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../src/utils/HelperFunctions.cpp +) + +target_include_directories(tests + PUBLIC + "$") diff --git a/slsDetectorServers/matterhornServer/tests/test-HelperFunctions.cpp b/slsDetectorServers/matterhornServer/tests/test-HelperFunctions.cpp new file mode 100644 index 000000000..0cbf93e13 --- /dev/null +++ b/slsDetectorServers/matterhornServer/tests/test-HelperFunctions.cpp @@ -0,0 +1,48 @@ +#include "HelperFunctions.hpp" +#include "catch.hpp" +#include + +namespace sls { + +auto get_test_parameters_countermaskspiconversion() { + return GENERATE(std::make_tuple(uint32_t{0x1}, uint32_t{0b0000}), + std::make_tuple(uint32_t{0x2}, uint32_t{0b0001}), + std::make_tuple(uint32_t{0x4}, uint32_t{0b0010}), + std::make_tuple(uint32_t{0x3}, uint32_t{0b0100}), + std::make_tuple(uint32_t{0x6}, uint32_t{0b0101}), + std::make_tuple(uint32_t{0x7}, uint32_t{0b1000}), + std::make_tuple(uint32_t{0x8}, uint32_t{0b0011}), + std::make_tuple(uint32_t{0x9}, uint32_t{0b0111}), + std::make_tuple(uint32_t{0xB}, uint32_t{0b1011}), + std::make_tuple(uint32_t{0xC}, uint32_t{0b0110}), + std::make_tuple(uint32_t{0xD}, uint32_t{0b1010}), + std::make_tuple(uint32_t{0xE}, uint32_t{0b1001}), + std::make_tuple(uint32_t{0xF}, uint32_t{0b1100}), + std::make_tuple(uint32_t{0xB}, uint32_t{0b1011})); +} + +TEST_CASE("Convert Counter Mask to SPI Counter Mask", + "[MatterHorn][HelperFunctions]") { + auto [counter_mask, expected_spi_counter_mask] = + get_test_parameters_countermaskspiconversion(); + + REQUIRE(convertCounterMaskToSPICounterMask(counter_mask) == + expected_spi_counter_mask); + + REQUIRE_THROWS( + convertCounterMaskToSPICounterMask(0xA)); // invalid counter mask + + REQUIRE_THROWS( + convertCounterMaskToSPICounterMask(0x5)); // invalid counter mask +} + +TEST_CASE("Convert SPI Counter Mask to Counter Mask", + "[MatterHorn][HelperFunctions]") { + auto [counter_mask, spi_counter_mask] = + get_test_parameters_countermaskspiconversion(); + + REQUIRE(convertSPICounterMaskToCounterMask(spi_counter_mask) == + counter_mask); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/toolchain.cmake b/slsDetectorServers/matterhornServer/toolchain.cmake new file mode 100644 index 000000000..d9b669799 --- /dev/null +++ b/slsDetectorServers/matterhornServer/toolchain.cmake @@ -0,0 +1,17 @@ +#Usage: cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DSLS_BUILD_ONLY_MATTERHORN=ON + +set(SLS_ARM_COMPILER "/psi.ch/group/detector/firmware/arm64_linux/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/" CACHE PATH "Path to the ARM cross compiler") + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(CMAKE_C_COMPILER "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-gcc") +set(CMAKE_CXX_COMPILER "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-g++") + +set(CMAKE_AR "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-ar") +set(CMAKE_RANLIB "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-ranlib") + +set(CMAKE_FIND_ROOT_PATH "${SLS_ARM_COMPILER}") +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt b/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt index 9060b99f2..4d9e50eeb 100644 --- a/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt +++ b/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt @@ -1,6 +1,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/TCPInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/CommandLineOptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/MemoryModel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorServerImpl.cpp ) add_library(slsServerObject OBJECT @@ -23,13 +25,13 @@ target_link_libraries(slsServerObject set(DETECTOR_LIBRARY_TARGETS slsServerObject) set(PUBLICHEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/include/TCPInterface.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/TCPInterface.hpp ) #Shared library if(SLS_BUILD_SHARED_LIBRARIES) add_library(slsServerShared SHARED $) - target_link_libraries(slsServerShared PUBLIC slsServerObject) + target_link_libraries(slsServerShared PUBLIC slsServerObject PRIVATE slsProjectWarnings) set_target_properties(slsServerShared PROPERTIES VERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH} SOVERSION ${PACKAGE_VERSION_MAJOR} @@ -42,7 +44,7 @@ endif(SLS_BUILD_SHARED_LIBRARIES) #Static library add_library(slsServerStatic STATIC $) -target_link_libraries(slsServerStatic PUBLIC slsServerObject) +target_link_libraries(slsServerStatic PUBLIC slsServerObject PRIVATE slsProjectWarnings) set_target_properties(slsServerStatic PROPERTIES ARCHIVE_OUTPUT_NAME SlsServerStatic diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/ArmBusCommunication.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/ArmBusCommunication.hpp new file mode 100644 index 000000000..5b43bf16f --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/ArmBusCommunication.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "RegisterHelperStructs.hpp" +#include "fmt/format.h" +#include +#include +#include +#include +#include +#include +#include + +// TODO: maybe should be templated on address type (e.g. uint32_t register or +// uint64_t register) for more flexibility? + +namespace sls { + +template class BusCommunication { + + using IPCoreEnumType = typename IPCores::ipcore_enum_type; + + public: + BusCommunication(); + + void mapToMemory(); + + uint32_t readRegister(const Register ®ister_) const; + void writeRegister(const Register ®ister_, const uint32_t data); + + private: + void bus_w(const uint32_t offset, IPCoreEnumType baseadress, + const uint32_t data); + + uint32_t bus_r(const uint32_t offset, IPCoreEnumType baseadress) const; + + /// @brief map from id of IP Core to memory model for the register block of + /// the IP core + std::map ipcoreregisterblocks{}; +}; + +template +BusCommunication::BusCommunication() { + + for (const auto &ip_core : IPCores::ipcores) { + ipcoreregisterblocks.emplace(ip_core, + MemoryModel{static_cast(ip_core), + IPCores::ip_core_block_size}); + } +} + +template +void BusCommunication::mapToMemory() { + + for (auto &map_elem : ipcoreregisterblocks) { + map_elem.second.mapToMemory(); + } +} + +template +uint32_t BusCommunication::readRegister( + const Register ®ister_) const { + return bus_r(register_.offset_in_bytes, register_.ip_core); +} + +template +void BusCommunication::writeRegister( + const Register ®ister_, const uint32_t data) { + bus_w(register_.offset_in_bytes, register_.ip_core, data); +} + +template +uint32_t BusCommunication::bus_r( + const uint32_t offset, const IPCoreEnumType baseadress) const { + auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() + + offset / (sizeof(uint32_t)); + return *ptr1; +} + +template +void BusCommunication::bus_w( + const uint32_t offset, const IPCoreEnumType baseadress, + const uint32_t data) { + auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() + + offset / (sizeof(uint32_t)); + *ptr1 = data; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.h b/slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.hpp similarity index 100% rename from slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.h rename to slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.hpp diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h deleted file mode 100644 index 6773f17ba..000000000 --- a/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once -#include "TCPInterface.h" -// #include "communication_funcs.h" -#include "sls/logger.h" -#include "sls/network_utils.h" -#include "sls/sls_detector_defs.h" -#include "sls/versionAPI.h" -#include -#include -#include -#include -#include -#include - -namespace sls { - -// TODO move to defs? -/// @brief struct saving udp details (one UDP port per module) -struct UDPInfo { - uint16_t srcport{}; - uint16_t dstport{}; - uint64_t srcmac{}; - uint64_t dstmac{}; - uint32_t srcip{}; - uint32_t dstip{}; -}; - -using ReturnCode = slsDetectorDefs::ReturnCode; - -template class DetectorServer { - - public: - /** - * Constructor - * Creates a detector server. - * Assembles a detector server using TCP and UDP detector interfaces - * throws an exception in case of failure - * @param port TCP/IP port number - */ - explicit DetectorServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); - - protected: - /// @brief TCP/IP interface for communication with the client - std::unique_ptr tcpInterface; - - std::array - udpDetails{}; // TODO: for now only one receiver per module - - /// @brief TODO what is this? - bool updateMode{true}; - - private: - ReturnCode processFunction(const detFuncs function_id, - ServerInterface &socket); - - // TODO dont know what this does? - ReturnCode get_update_mode(ServerInterface &socket) const; - - ReturnCode get_source_udp_mac(ServerInterface &socket) const; - - ReturnCode set_source_udp_mac(ServerInterface &socket); - - ReturnCode get_source_udp_ip(ServerInterface &socket) const; - - ReturnCode set_source_udp_ip(ServerInterface &socket); - - ReturnCode get_source_udp_port(ServerInterface &socket) const; - - ReturnCode set_destination_udp_mac(ServerInterface &socket); - - ReturnCode get_destination_udp_mac(ServerInterface &socket) const; - - ReturnCode set_destination_udp_ip(ServerInterface &socket); - - ReturnCode get_destination_udp_ip(ServerInterface &socket) const; - - ReturnCode set_destination_udp_port(ServerInterface &socket); - - ReturnCode get_destination_udp_port(ServerInterface &socket) const; -}; - -template -DetectorServer::DetectorServer(uint16_t port) { - validatePortNumber(port); - - udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO; - udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO; - - std::function fn = - [this](const detFuncs &function_id, ServerInterface &socket) { - return this->processFunction(function_id, socket); - }; - tcpInterface = std::make_unique(fn, port); -} - -template -ReturnCode DetectorServer::processFunction( - const detFuncs function_id, ServerInterface &socket) { - - switch (function_id) { - case detFuncs::F_GET_SERVER_VERSION: - return static_cast(this)->get_version(socket); - case detFuncs::F_GET_DETECTOR_TYPE: - return static_cast(this)->get_detector_type( - socket); - case detFuncs::F_INITIAL_CHECKS: - return static_cast(this)->initial_checks( - socket); - case detFuncs::F_GET_NUM_INTERFACES: - return static_cast(this) - ->get_num_udp_interfaces(socket); - case detFuncs::F_GET_UPDATE_MODE: - return get_update_mode(socket); - case detFuncs::F_SET_SOURCE_UDP_MAC: - return set_source_udp_mac(socket); - case detFuncs::F_GET_SOURCE_UDP_MAC: - return get_source_udp_mac(socket); - case detFuncs::F_SET_SOURCE_UDP_IP: - return set_source_udp_ip(socket); - case detFuncs::F_GET_SOURCE_UDP_IP: - return get_source_udp_ip(socket); - case detFuncs::F_SET_DEST_UDP_MAC: - return set_destination_udp_mac(socket); - case detFuncs::F_GET_DEST_UDP_MAC: - return get_destination_udp_mac(socket); - case detFuncs::F_SET_DEST_UDP_IP: - return set_destination_udp_ip(socket); - case detFuncs::F_GET_DEST_UDP_IP: - return get_destination_udp_ip(socket); - case detFuncs::F_SET_DEST_UDP_PORT: - return set_destination_udp_port(socket); - case detFuncs::F_GET_DEST_UDP_PORT: - return get_destination_udp_port(socket); - - default: - LOG(logDEBUG) << "Checking specific server functions for function ID: " - << function_id; - // process detector specific functions - static_cast(this)->processFunction(function_id, - socket); - } - - return ReturnCode::FAIL; -} - -template -ReturnCode DetectorServer::get_update_mode( - ServerInterface &socket) const { - - return static_cast( - socket.sendResult(static_cast(updateMode))); -} - -template -ReturnCode DetectorServer::set_source_udp_mac( - ServerInterface &socket) { - uint64_t newsrcudpMac; - - try { - int ret = socket.Receive(newsrcudpMac); - } catch (const SocketError &e) { - LOG(logERROR) << "Failed to receive new source UDP MAC address: " - << e.what(); - return ReturnCode::FAIL; - } - - udpDetails[0].srcmac = newsrcudpMac; - // TODO: configuremac, check unicast address - return ReturnCode::OK; -} - -template -ReturnCode DetectorServer::get_source_udp_mac( - ServerInterface &socket) const { - return static_cast(socket.sendResult(udpDetails[0].srcmac)); -} - -template -ReturnCode DetectorServer::set_source_udp_ip( - ServerInterface &socket) { - uint32_t newSrcIp; - - try { - int ret = socket.Receive(newSrcIp); - } catch (const SocketError &e) { - LOG(logERROR) << "Failed to receive new source UDP IP address: " - << e.what(); - return ReturnCode::FAIL; - } - - udpDetails[0].srcip = newSrcIp; - return ReturnCode::OK; -} - -template -ReturnCode DetectorServer::get_source_udp_ip( - ServerInterface &socket) const { - return static_cast(socket.sendResult(udpDetails[0].srcip)); -} - -template -ReturnCode DetectorServer::set_destination_udp_mac( - ServerInterface &socket) { - uint64_t newDstMac; - - try { - int ret = socket.Receive(newDstMac); - } catch (const SocketError &e) { - LOG(logERROR) << "Failed to receive new destination UDP MAC address: " - << e.what(); - return ReturnCode::FAIL; - } - - udpDetails[0].dstmac = newDstMac; - // TODO: configuremac, check unicast address - return ReturnCode::OK; -} - -template -ReturnCode DetectorServer::get_destination_udp_mac( - ServerInterface &socket) const { - return static_cast(socket.sendResult(udpDetails[0].dstmac)); -} - -template -ReturnCode DetectorServer::set_destination_udp_ip( - ServerInterface &socket) { - uint32_t newDstIp; - - try { - int ret = socket.Receive(newDstIp); - } catch (const SocketError &e) { - LOG(logERROR) << "Failed to receive new destination UDP IP address: " - << e.what(); - return ReturnCode::FAIL; - } - - udpDetails[0].dstip = newDstIp; - return ReturnCode::OK; -} - -template -ReturnCode DetectorServer::get_destination_udp_ip( - ServerInterface &socket) const { - return static_cast(socket.sendResult(udpDetails[0].dstip)); -} - -template -ReturnCode DetectorServer::set_destination_udp_port( - ServerInterface &socket) { - uint16_t newDstPort; - - try { - int ret = socket.Receive(newDstPort); - } catch (const SocketError &e) { - LOG(logERROR) << "Failed to receive new destination UDP port number: " - << e.what(); - return ReturnCode::FAIL; - } - - udpDetails[0].dstport = newDstPort; - return ReturnCode::OK; -} - -template -ReturnCode DetectorServer::get_destination_udp_port( - ServerInterface &socket) const { - return static_cast(socket.sendResult(udpDetails[0].dstport)); -}; - -} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.hpp new file mode 100644 index 000000000..9039bd080 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.hpp @@ -0,0 +1,523 @@ +#pragma once +#include "DetectorServerImpl.hpp" +#include "TCPInterface.hpp" +#include "helpers/Helpers.hpp" +#include "helpers/type_traits.hpp" +#include "sls/logger.h" +#include "sls/network_utils.h" +#include "sls/sls_detector_defs.h" +#include "sls/versionAPI.h" +#include +#include +#include +#include +#include +#include + +namespace sls { + +using ReturnCode = slsDetectorDefs::ReturnCode; + +template class DetectorServer { + + public: + /** + * Constructor + * Creates a detector server. + * Assembles a detector server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit DetectorServer( + std::unique_ptr< + DetectorServerImpl::value>> + impl_, + uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + ~DetectorServer() = default; + + protected: + /// @brief TCP/IP interface for communication with the client + std::unique_ptr tcpInterface; + + std::unique_ptr< + DetectorServerImpl::value>> + impl; + + auto *getImpl() { + return static_cast::ImplType *>(impl.get()); + } + + const auto *getImpl() const { + return static_cast::ImplType *>(impl.get()); + } + + private: + /// @brief get derived class + DerivedDetectorServer *getDerived() { + return static_cast(this); + } + + const DerivedDetectorServer *getDerived() const { + return static_cast(this); + } + + ProcessedResult processFunction(const detFuncs function_id, + ServerInterface &socket); + + // TODO dont know what this does? + ProcessedResult get_update_mode(ServerInterface &socket) const; + + ProcessedResult get_source_udp_mac(ServerInterface &socket) const; + + ProcessedResult set_source_udp_mac(ServerInterface &socket); + + ProcessedResult get_source_udp_ip(ServerInterface &socket) const; + + ProcessedResult set_source_udp_ip(ServerInterface &socket); + + ProcessedResult get_source_udp_port(ServerInterface &socket) const; + + ProcessedResult set_destination_udp_mac(ServerInterface &socket); + + ProcessedResult get_destination_udp_mac(ServerInterface &socket) const; + + ProcessedResult set_destination_udp_ip(ServerInterface &socket); + + ProcessedResult get_destination_udp_ip(ServerInterface &socket) const; + + ProcessedResult set_destination_udp_port(ServerInterface &socket); + + ProcessedResult get_destination_udp_port(ServerInterface &socket) const; + + ProcessedResult get_num_frames(ServerInterface &socket) const; + + ProcessedResult set_num_frames(ServerInterface &socket); + + ProcessedResult get_num_triggers(ServerInterface &socket) const; + + ProcessedResult set_num_triggers(ServerInterface &socket); + + ProcessedResult get_version(ServerInterface &socket) const; + + ProcessedResult get_num_udp_interfaces(ServerInterface &socket) const; + + ProcessedResult get_detector_type(ServerInterface &socket) const; + + ProcessedResult get_receiver_parameters(ServerInterface &socket) const; + + ProcessedResult get_run_status(ServerInterface &socket) const; + + ProcessedResult initial_checks(ServerInterface &socket) const; + + ProcessedResult + set_module_position_and_update_srcudpmac(ServerInterface &socket); +}; + +template +DetectorServer::DetectorServer( + std::unique_ptr< + DetectorServerImpl::value>> + impl_, + uint16_t port) + : impl(std::move(impl_)) { + validatePortNumber(port); + + std::function fn = + [this](const detFuncs &function_id, ServerInterface &socket) { + return this->processFunction(function_id, socket); + }; + + tcpInterface = std::make_unique(fn, port); +} + +template +ProcessedResult DetectorServer::processFunction( + const detFuncs function_id, ServerInterface &socket) { + + switch (function_id) { + case detFuncs::F_GET_SERVER_VERSION: + return get_version(socket); + case detFuncs::F_GET_DETECTOR_TYPE: + return get_detector_type(socket); + case detFuncs::F_INITIAL_CHECKS: + return initial_checks(socket); + case detFuncs::F_GET_NUM_INTERFACES: + return get_num_udp_interfaces(socket); + case detFuncs::F_GET_UPDATE_MODE: + return get_update_mode(socket); + case detFuncs::F_SET_SOURCE_UDP_MAC: + return set_source_udp_mac(socket); + case detFuncs::F_GET_SOURCE_UDP_MAC: + return get_source_udp_mac(socket); + case detFuncs::F_SET_SOURCE_UDP_IP: + return set_source_udp_ip(socket); + case detFuncs::F_GET_SOURCE_UDP_IP: + return get_source_udp_ip(socket); + case detFuncs::F_SET_DEST_UDP_MAC: + return set_destination_udp_mac(socket); + case detFuncs::F_GET_DEST_UDP_MAC: + return get_destination_udp_mac(socket); + case detFuncs::F_SET_DEST_UDP_IP: + return set_destination_udp_ip(socket); + case detFuncs::F_GET_DEST_UDP_IP: + return get_destination_udp_ip(socket); + case detFuncs::F_SET_DEST_UDP_PORT: + return set_destination_udp_port(socket); + case detFuncs::F_GET_DEST_UDP_PORT: + return get_destination_udp_port(socket); + case detFuncs::F_GET_RUN_STATUS: + return get_run_status(socket); + case detFuncs::F_GET_NUM_FRAMES: + return get_num_frames(socket); + case detFuncs::F_SET_NUM_FRAMES: + return set_num_frames(socket); + case detFuncs::F_GET_NUM_TRIGGERS: + return get_num_triggers(socket); + case detFuncs::F_SET_NUM_TRIGGERS: + return set_num_triggers(socket); + case detFuncs::F_GET_RECEIVER_PARAMETERS: + return get_receiver_parameters(socket); + case detFuncs::F_SET_POSITION: + return set_module_position_and_update_srcudpmac(socket); + default: + LOG(logDEBUG) << "Checking specific server functions for function ID: " + << function_id; + // process detector specific functions + return getDerived()->processFunction(function_id, socket); + } + + return return_fail("Function not implemented"); +} + +template +ProcessedResult DetectorServer::get_update_mode( + ServerInterface &socket) const { + + const bool updateMode = impl->get_update_mode(); + + // TODO: catch the socket error during Send and add error message to the + // ProcessedResult but DatSocket shared with receiver - some refactoring + return send_result(socket, static_cast(updateMode)); +} + +template +ProcessedResult DetectorServer::get_source_udp_mac( + ServerInterface &socket) const { + auto srcUdpMac = impl->get_source_udp_mac(); + + return send_result(socket, srcUdpMac); +} + +template +ProcessedResult DetectorServer::set_source_udp_mac( + ServerInterface &socket) { + + uint64_t newsrcudpMac; + + try { + (void)socket.Receive(newsrcudpMac); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new source UDP MAC address: " + << e.what(); + return return_fail("Failed to receive new source UDP MAC address: " + + std::string(e.what())); + } + + try { + getImpl()->set_source_udp_mac(newsrcudpMac); + } catch (const std::exception &e) { + LOG(logERROR) << "Failed to set source UDP MAC address: " << e.what(); + return_fail("Failed to set source UDP MAC address: " + + std::string(e.what())); + } + + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::set_source_udp_ip( + ServerInterface &socket) { + + uint32_t newSrcIp; + + try { + (void)socket.Receive(newSrcIp); + } catch (const SocketError &e) { + auto error_message = "Failed to receive new source UDP IP address: " + + std::string(e.what()); + LOG(logERROR) << error_message; + + return return_fail(error_message); + } + + impl->set_source_udp_ip(newSrcIp); + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_source_udp_ip( + ServerInterface &socket) const { + uint32_t src_UdpIp = impl->get_source_udp_ip(); + return send_result(socket, src_UdpIp); +} + +template +ProcessedResult DetectorServer::set_destination_udp_mac( + ServerInterface &socket) { + uint64_t newDstMac; + + try { + (void)socket.Receive(newDstMac); + } catch (const SocketError &e) { + auto error_message = + "Failed to receive new destination UDP MAC address: " + + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + + impl->set_destination_udp_mac(newDstMac); + + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_destination_udp_mac( + ServerInterface &socket) const { + + auto dstUdpMac = impl->get_destination_udp_mac(); + return send_result(socket, dstUdpMac); +} + +template +ProcessedResult DetectorServer::set_destination_udp_ip( + ServerInterface &socket) { + uint32_t newDstIp; + + try { + (void)socket.Receive(newDstIp); + } catch (const SocketError &e) { + auto error_message = + "Failed to receive new destination UDP IP address: " + + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + + impl->set_destination_udp_ip(newDstIp); + + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_destination_udp_ip( + ServerInterface &socket) const { + + uint32_t dstUdpIp = impl->get_destination_udp_ip(); + return send_result(socket, dstUdpIp); +} + +template +ProcessedResult DetectorServer::set_destination_udp_port( + ServerInterface &socket) { + uint16_t newDstPort; + + try { + (void)socket.Receive(newDstPort); + } catch (const SocketError &e) { + auto error_message = + "Failed to receive new destination UDP port number: " + + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + + impl->set_destination_udp_port(newDstPort); + + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_destination_udp_port( + ServerInterface &socket) const { + uint16_t dstUdpPort = impl->get_destination_udp_port(); + return send_result(socket, dstUdpPort); +}; + +template +ProcessedResult DetectorServer::get_num_frames( + ServerInterface &socket) const { + uint64_t num_frames{}; + try { + num_frames = getImpl()->get_num_frames(); + } catch (const std::exception &e) { + auto error_message = + "Failed to get number of frames: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + return send_result(socket, num_frames); +} + +template +ProcessedResult +DetectorServer::set_num_frames(ServerInterface &socket) { + int64_t num_frames{}; + try { + (void)socket.Receive(num_frames); + } catch (const SocketError &e) { + auto error_message = + "Failed to receive number of frames: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + try { + getImpl()->set_num_frames(num_frames); + } catch (const std::exception &e) { + auto error_message = + "Failed to set number of frames: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_num_triggers( + ServerInterface &socket) const { + uint64_t num_triggers{}; + try { + num_triggers = static_cast(getImpl()->get_num_triggers()); + } catch (const std::exception &e) { + auto error_message = + "Failed to get number of triggers: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + return send_result(socket, num_triggers); +} + +template +ProcessedResult DetectorServer::set_num_triggers( + ServerInterface &socket) { + int64_t num_triggers{}; + try { + (void)socket.Receive(num_triggers); + } catch (const SocketError &e) { + auto error_message = + "Failed to receive number of triggers: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + try { + getImpl()->set_num_triggers(num_triggers); + } catch (const std::exception &e) { + auto error_message = + "Failed to set number of triggers: " + std::string(e.what()); + LOG(logERROR) << error_message; + return return_fail(error_message); + } + return send_ok(socket); +} + +template +ProcessedResult DetectorServer::get_version( + ServerInterface &socket) const { + + auto version = + getImpl()->get_server_version(); // TODO: get Impl from derived server + + char version_cstr[MAX_STR_LENGTH]{}; + std::snprintf(version_cstr, sizeof(version_cstr), "%s", + version.c_str()); // ensures temination + LOG(TLogLevel::logDEBUG) << "Server Version: " << version; + return send_result( + socket, + version_cstr); // TODO: check what would be possible return codes!!! +} + +template +ProcessedResult DetectorServer::get_num_udp_interfaces( + ServerInterface &socket) const { + int num_udp_interfaces = getImpl()->get_num_udp_interfaces(); + + return send_result(socket, num_udp_interfaces); +} + +template +ProcessedResult DetectorServer::get_detector_type( + ServerInterface &socket) const { + uint32_t detectortype = getImpl()->get_detector_type(); + return send_result(socket, detectortype); +} + +template +ProcessedResult DetectorServer::get_receiver_parameters( + ServerInterface &socket) const { + + slsDetectorDefs::rxParameters rx_params = + getImpl()->get_receiver_parameters(); + + return send_result(socket, rx_params); +} + +template +ProcessedResult DetectorServer::get_run_status( + ServerInterface &socket) const { + + slsDetectorDefs::runStatus status = getImpl()->get_run_status(); + + return send_result(socket, status); +} + +template +ProcessedResult DetectorServer::initial_checks( + ServerInterface &socket) const { + auto detectorsetupstatus = getImpl()->get_detector_setup_status(); + + // TODO: should there be a time limit? + while (detectorsetupstatus.setup_status == + detector_setup_status::NOT_SETUP) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + detectorsetupstatus = getImpl()->get_detector_setup_status(); + } + if (detectorsetupstatus.setup_status == + detector_setup_status::FAILED_SETUP) { + return return_fail("Initial checks failed: " + + detectorsetupstatus.error_message); + } else { + return send_result(socket, true); + } +} + +template +ProcessedResult +DetectorServer::set_module_position_and_update_srcudpmac( + ServerInterface &socket) { + + std::array position_info{}; // [num_modules_in_y, module_index] + try { + (void)socket.Receive(position_info.data(), + position_info.size() * sizeof(int)); + } catch (const SocketError &e) { + LOG(logERROR) + << "Failed to receive num modules in y dimension and module index: " + << e.what(); + return_fail( + "Failed to receive num modules in y dimension and module index: " + + std::string(e.what())); + } + + try { + getImpl()->set_module_position_and_update_srcudpmac(position_info); + } catch (const std::exception &e) { + return_fail("Failed to set module position: " + std::string(e.what())); + } + + return send_ok(socket); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServerImpl.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServerImpl.hpp new file mode 100644 index 000000000..8c4c29adf --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServerImpl.hpp @@ -0,0 +1,109 @@ +#pragma once +#include "sls/SharedMemory.h" +#include +#include + +namespace sls { + +// TODO move to defs? +/// @brief struct saving udp details (one UDP port per module) +struct UDPInfo { + uint16_t srcport{}; + uint16_t dstport{}; + uint64_t srcmac{}; + uint64_t dstmac{}; + uint32_t srcip{}; + uint32_t dstip{}; +}; + +/// @brief struct to store detector setup status +struct detector_setup_status { + + enum SETUP_STATUS : uint8_t { + FAILED_SETUP = 0, + SUCCESSFUL_SETUP = 1, + NOT_SETUP = 2 + }; + + /// @brief true if setupDetector() was successful, false otherwise + SETUP_STATUS setup_status{NOT_SETUP}; + /// @brief error message if setupDetector() failed, empty otherwise + std::string error_message{}; +}; + +/// @brief Shared memory structure for stop server to store run status +struct acquisitionStatus { + + /* FIXED PATTERN FOR STATIC FUNCTIONS. DO NOT CHANGE, ONLY APPEND ------*/ + int shmversion; + + bool isValid{true}; // false if freed to block access from python or c++ api + + std::atomic scanStatus{ + slsDetectorDefs::runStatus::IDLE}; // idle, running or error + std::atomic scanStop{false}; + + // TODO: only neccessary for virtual, maybe have two shared memory + // structures, one for virtual + std::atomic status{ + slsDetectorDefs::runStatus::IDLE}; + std::atomic stop{false}; +}; + +template class DetectorServerImpl { + + public: + DetectorServerImpl(); + ~DetectorServerImpl(); + + bool get_update_mode() const; + + uint64_t get_source_udp_mac() const; + + void set_source_udp_ip(const uint32_t srcip); + + uint32_t get_source_udp_ip() const; + + void set_destination_udp_ip(const uint32_t dstip); + + uint32_t get_destination_udp_ip() const; + + void set_destination_udp_mac(const uint64_t dstmac); + + uint64_t get_destination_udp_mac() const; + + void set_destination_udp_port(const uint16_t dstport); + + uint16_t get_destination_udp_port() const; + + detector_setup_status get_detector_setup_status() const; + + protected: + std::array + udpDetails{}; // TODO: for now only one receiver per module + + /// @brief TODO what is this? + bool updateMode{ + false}; // what should the default be - can update module size etc. + + /// @brief shared mempory with aquisition status + mutable SharedMemory shm{ + 0, 0}; // TODO: is mutable really neccessary? + + /// @brief sets source UDP MAC address in udpDetails and updates udp + /// header + /// @param srcmac + void updateSrcMacAddress(const uint64_t srcmac); + + /// @brief true if setupDetector() was successful, false otherwise + detector_setup_status detectorSetupStatus{}; + + /// @brief true if the derived server is a stop server, false otherwise + static constexpr bool stop_server = isStopServer; + + private: + /// @brief creates and maps shared memory + void createSharedMemory(); +}; + +} // namespace sls diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/MemoryModel.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/MemoryModel.hpp new file mode 100644 index 000000000..8acc16a4e --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/MemoryModel.hpp @@ -0,0 +1,68 @@ +#pragma once +#include "fmt/format.h" +#include "sls/logger.h" +#include "sls/sls_detector_exceptions.h" +#include +#include + +namespace sls { + +/// @brief class to handle memory mapping and access for hardware IP cores +class HardwareMemoryModel { + + public: + HardwareMemoryModel(const uint32_t IPcore_base_address, + const size_t size_memory_space_); + + ~HardwareMemoryModel(); + + void mapToMemory(); + + void unmapMemory(); + + volatile uint32_t *getMappedMemoryPtr() const; + + private: + volatile uint32_t *mapped_memory_ptr{nullptr}; + + /// @brief offset of the IP core base address in the memory space, used for + /// mapping + const size_t IPCore_base_address{0}; + + /// @brief size mapped memory region [bytes] + const size_t size_memory_space{0}; +}; + +/// @brief class to handle memory mapping and access for virtual IP cores (e.g. +/// use software implementation of memory) +template class VirtualMemoryModel { + + public: + // IPcore_base_address is not used for virtual memory model but kept for + // compatibility with HardwareMemoryModel interface + VirtualMemoryModel(const uint32_t IPcore_base_address, + const size_t size_memory_space_) + : size_memory_space(size_memory_space_) { + (void)IPcore_base_address; // suppress unused parameter warning + } + + ~VirtualMemoryModel() = default; + + void mapToMemory() { + mapped_memory.resize( + size_memory_space / + sizeof(DataType)); // TODO: should it be zero initialized? + } + + DataType *getMappedMemoryPtr() { return mapped_memory.data(); } + + const DataType *getMappedMemoryPtr() const { return mapped_memory.data(); } + + private: + std::vector mapped_memory{}; + + /// @brief size mapped memory region [bytes] + const size_t size_memory_space{0}; +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/RegisterHelperStructs.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/RegisterHelperStructs.hpp new file mode 100644 index 000000000..2c3265075 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/RegisterHelperStructs.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include +#include + +namespace sls { + +enum class IPCore : uint32_t; // forward declaration of IPCore enum class + +/// @brief struct representing 32 bit register +struct Register { + /// @brief IP core address space + const IPCore ip_core{}; // TODO replace by enum type + + /// @brief Offset of the register in bytes from the base address of the IP + /// core + const uint32_t offset_in_bytes{}; +}; + +struct RegisterField { + /// @brief Register to which the field belongs + const Register register_{}; + + /// @brief Bit position of the least significant bit of the field in the + /// register + const uint32_t bit_position{}; + + /// @brief Bitmask for the field + const uint32_t bitmask{}; +}; + +// TODO: maybe static member function of RegisterField? +template +void setRegisterField(uint32_t ®istervalue, const RegisterField ®_field, + T field_value) { + + if (field_value > static_cast(reg_field.bitmask)) { + throw std::invalid_argument( + fmt::format("Value {} cannot fit in field with bitmask {}", + field_value, reg_field.bitmask)); + } + // Clear the bits corresponding to the field + registervalue &= ~(reg_field.bitmask << reg_field.bit_position); + // Set the new value for the field + registervalue |= (static_cast(field_value) & reg_field.bitmask) + << reg_field.bit_position; +} + +template +T getRegisterField(const uint32_t ®istervalue, + const RegisterField ®_field) { + // Extract the bits corresponding to the field and shift them to get the + // value + + auto field_value = + (registervalue >> reg_field.bit_position) & reg_field.bitmask; + + return static_cast(field_value); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h b/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.hpp similarity index 50% rename from slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h rename to slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.hpp index 6d3ea2c29..deb337e62 100644 --- a/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h +++ b/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.hpp @@ -10,6 +10,31 @@ namespace sls { +using ReturnCode = slsDetectorDefs::ReturnCode; + +struct ProcessedResult { + /// @brief return code of the processed command + slsDetectorDefs::ReturnCode returnCode{}; + /// @brief error message to be sent to client in case of failure + std::string error_message{}; +}; + +// communication helpers +inline ProcessedResult return_fail(std::string_view error_message) { + return ProcessedResult{ReturnCode::FAIL, + static_cast(error_message)}; +} + +inline ProcessedResult send_ok(ServerInterface &socket) { + return ProcessedResult{ + static_cast(socket.Send(ReturnCode::OK))}; +} + +template +inline ProcessedResult send_result(ServerInterface &socket, const T &value) { + return ProcessedResult{static_cast(socket.sendResult(value))}; +} + /** * @brief TCPInterface class handles communication and processing of commands * from Client to Server. @@ -19,9 +44,10 @@ class TCPInterface { public: ~TCPInterface(); - TCPInterface(std::function &processFunction_, - const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO); + TCPInterface( + std::function + &processFunction_, + const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO); /// @brief creates tcp thread void startTCPServer(); @@ -40,12 +66,11 @@ class TCPInterface { * @param function_id The ID of the function recived by the server and to * be executed */ - slsDetectorDefs::ReturnCode processReceivedData(const detFuncs function_id, - ServerInterface &socket); + ProcessedResult processReceivedData(const detFuncs function_id, + ServerInterface &socket); /// @brief map of function IDs and corresponding functions - std::function + std::function processFunction; /// @brief TCP/IP port number for the detector server diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Defs.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Defs.hpp new file mode 100644 index 000000000..442a1df1d --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Defs.hpp @@ -0,0 +1,19 @@ +/** @file Defs.hpp + * @brief this file contains some definitions used in the slsDetectorServer_cpp + * project. + */ +#pragma once +#include + +namespace sls { + +/// @brief Individual/Group bit offset in a 48 bit MAC address - 0 indicates +/// unicast mac address +constexpr uint8_t INDIVIDUAL_GROUP_BIT_OFFSET = 40; // 1000 0000 + +/// @brief Universal/Local bit offset in a 48 bit MAC address - 1 indicates +/// locally administered mac address, 0 indicates universally administered mac +/// address +constexpr uint8_t UNIVERSAL_LOCAL_BIT_OFFSET = 41; // 0100 0000 + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Helpers.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Helpers.hpp new file mode 100644 index 000000000..b17207003 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/Helpers.hpp @@ -0,0 +1,64 @@ +#pragma once +#include "Defs.hpp" +#include "DetectorServerImpl.hpp" +#include "sls/SharedMemory.h" +#include "sls/sls_detector_defs.h" +#include +#include + +namespace sls { + +constexpr uint64_t mac_mask = 0xffffffffffff0000; +constexpr uint8_t offset_row_position_in_mac = 8; // given in bits +constexpr uint8_t offset_col_position_in_mac = 0; // given in bits + +/// @brief generates a random locally administered unicast MAC address for the +/// source UDP +/// @return generated MAC address +inline uint64_t generateRandomMacAddress() { + uint64_t mac = + 0xAA0000000000; // locally administered unicast address (0xA: 0b1010) // + // TODO maybe 0x02000000000 better? + for (int i = 2; i < 5; ++i) { + mac |= (static_cast(std::rand() % 256) << (i * 8)); + } + return mac; +} + +/// @brief generates a MAC address based on the module's row and column +/// position, last 32 bits of the MAC address are set to module_row and +/// module_col +/// @param module_row +/// @param module_col +/// @return generated MAC address +inline uint64_t generateMacAddressfromModulePosition(const uint8_t module_row, + const uint8_t module_col) { + + uint64_t newSrcMac = generateRandomMacAddress(); + newSrcMac = (newSrcMac & mac_mask) | + (module_row << offset_row_position_in_mac) | + (module_col << offset_col_position_in_mac); + + return newSrcMac; +} + +/// @brief check that mac is unicast and locally administered +/// @param mac +/// @return true if mac is valid, false otherwise +inline bool isValidMac(const uint64_t mac) { + if ((mac << INDIVIDUAL_GROUP_BIT_OFFSET) == 0 && + (mac << UNIVERSAL_LOCAL_BIT_OFFSET) == 1) { + return true; + } + return false; +} + +inline void freeSharedMemory() { + SharedMemory shm(0, -1, "server"); + + if (shm.exists()) { + shm.removeSharedMemory(); + } +} + +} // namespace sls diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/helpers/type_traits.hpp b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/type_traits.hpp new file mode 100644 index 000000000..97910cce1 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/helpers/type_traits.hpp @@ -0,0 +1,57 @@ +#pragma once +#include + +namespace sls { + +// forward declares +template class MatterhornServer; + +template class VirtualMatterhornServer; + +template class MatterhornServerImpl; + +template class VirtualMatterhornServerImpl; + +template class BaseMatterhornServer; + +// type trait to get implementation type + +template struct implementation_type_trait; + +template +struct implementation_type_trait< + BaseMatterhornServer>> { + using ImplType = MatterhornServerImpl; +}; + +template +struct implementation_type_trait< + BaseMatterhornServer>> { + using ImplType = VirtualMatterhornServerImpl; +}; + +// type trait to get stop server flag from Detector Server + +template +struct is_stop_server : std::false_type {}; + +template <> +struct is_stop_server> : std::true_type {}; + +template <> +struct is_stop_server> : std::true_type {}; + +template <> +struct is_stop_server> : std::true_type {}; + +template <> struct is_stop_server> : std::true_type {}; + +template <> +struct is_stop_server>> + : std::true_type {}; + +template <> +struct is_stop_server>> + : std::true_type {}; + +} // namespace sls diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp index 49d6a016b..1b0429c0a 100644 --- a/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp +++ b/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp @@ -1,4 +1,4 @@ -#include "CommandLineOptions.h" +#include "CommandLineOptions.hpp" #include "sls/ToString.h" #include "sls/sls_detector_exceptions.h" diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/DetectorServerImpl.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/DetectorServerImpl.cpp new file mode 100644 index 000000000..959fa2080 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/src/DetectorServerImpl.cpp @@ -0,0 +1,117 @@ +#include "DetectorServerImpl.hpp" +#include "sls/logger.h" +#include "sls/sls_detector_exceptions.h" +#include + +namespace sls { + +template class DetectorServerImpl; // forward declare +template class DetectorServerImpl; // forward declare + +template +DetectorServerImpl::DetectorServerImpl() { + udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO; + udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO; + + createSharedMemory(); +} + +template +DetectorServerImpl::~DetectorServerImpl() { + shm.removeSharedMemory(); +} + +template +void DetectorServerImpl::createSharedMemory() { + shm = SharedMemory(0, -1, "server"); + + if (shm.exists()) { + shm.openSharedMemory(true); // stop server + } else { + LOG(logINFOBLUE) << "Creating shared memory for acquisition status"; + try { + shm.createSharedMemory(); + } catch (const SharedMemoryAlreadyExistsError &e) { + shm.openSharedMemory(true); // potential race conditions between + // stop and control server + } + } +} + +template +void DetectorServerImpl::updateSrcMacAddress( + const uint64_t srcmac) { + LOG(logINFO) << "Updating source MAC address to: " + << fmt::format("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + (srcmac >> 40) & 0xff, (srcmac >> 32) & 0xff, + (srcmac >> 24) & 0xff, (srcmac >> 16) & 0xff, + (srcmac >> 8) & 0xff, srcmac & 0xff); + + udpDetails[0].srcmac = srcmac; + + // TODO: update UDP header with new source MAC address + + // TODO: do i need to keep track of the configured member ? +} + +template +bool DetectorServerImpl::get_update_mode() const { + return updateMode; +} + +template +uint64_t DetectorServerImpl::get_source_udp_mac() const { + return udpDetails[0].srcmac; +} + +template +void DetectorServerImpl::set_source_udp_ip(const uint32_t srcip) { + udpDetails[0].srcip = srcip; +} + +template +uint32_t DetectorServerImpl::get_source_udp_ip() const { + return udpDetails[0].srcip; +} + +template +void DetectorServerImpl::set_destination_udp_ip( + const uint32_t dstip) { + udpDetails[0].dstip = dstip; +} + +template +uint32_t DetectorServerImpl::get_destination_udp_ip() const { + return udpDetails[0].dstip; +} + +template +void DetectorServerImpl::set_destination_udp_mac( + const uint64_t dstmac) { + // TODO: configuremac, check unicast address + udpDetails[0].dstmac = dstmac; +} + +template +uint64_t DetectorServerImpl::get_destination_udp_mac() const { + return udpDetails[0].dstmac; +} + +template +void DetectorServerImpl::set_destination_udp_port( + const uint16_t dstport) { + udpDetails[0].dstport = dstport; +} + +template +uint16_t DetectorServerImpl::get_destination_udp_port() const { + return udpDetails[0].dstport; +} + +template +detector_setup_status +DetectorServerImpl::get_detector_setup_status() const { + return detectorSetupStatus; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/MemoryModel.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/MemoryModel.cpp new file mode 100644 index 000000000..273002f2d --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/src/MemoryModel.cpp @@ -0,0 +1,62 @@ +#include "MemoryModel.hpp" +#include +#include +#include +#include +#include +#include + +namespace sls { + +HardwareMemoryModel::HardwareMemoryModel(const uint32_t IPcore_base_address, + const size_t size_memory_space_) + : IPCore_base_address(IPcore_base_address), + size_memory_space(size_memory_space_) {} + +void HardwareMemoryModel::mapToMemory() { + + int fd = open("/dev/mem", O_RDWR | O_SYNC, 0); + + if (fd == -1) { + throw RuntimeError("Can't find /dev/mem"); + } + + auto void_mmap_ptr = + mmap(nullptr, size_memory_space, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + IPCore_base_address); + + if (void_mmap_ptr == MAP_FAILED) { + throw RuntimeError( + fmt::format("Failed to map base address: {}", + IPCore_base_address)); // TODO: needs ToString + } + + mapped_memory_ptr = reinterpret_cast(void_mmap_ptr); + + close(fd); +} + +volatile uint32_t *HardwareMemoryModel::getMappedMemoryPtr() const { + return mapped_memory_ptr; +} + +void HardwareMemoryModel::unmapMemory() { + + if (mapped_memory_ptr != nullptr) { + if (munmap(reinterpret_cast( + const_cast(mapped_memory_ptr)), + size_memory_space) < 0) { + LOG(logWARNING) + << fmt::format("Failed to unmap memory for IP core: {}", + IPCore_base_address); // TODO: needs ToString + } + mapped_memory_ptr = nullptr; + } +} + +HardwareMemoryModel::~HardwareMemoryModel() { + LOG(logDEBUG1) << "HardwareMemoryModel destructor called, unmapping memory"; + unmapMemory(); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp index ea54d2f5a..2e7468060 100644 --- a/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp +++ b/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp @@ -1,4 +1,4 @@ -#include "TCPInterface.h" +#include "TCPInterface.hpp" #include "fmt/format.h" #include "sls/logger.h" @@ -8,8 +8,8 @@ namespace sls { TCPInterface::TCPInterface( - std::function &processFunction_, + std::function + &processFunction_, const uint16_t portNumber_) : processFunction(processFunction_), portNumber(portNumber_), server(portNumber_) { @@ -44,19 +44,23 @@ void TCPInterface::startTCPServerClientConnection() { auto socket = server.accept(); try { - socket.Receive(function_id); + (void)socket.Receive(function_id); if (function_id < 0 || function_id >= NUM_DET_FUNCTIONS) { throw RuntimeError(fmt::format( "{}:{}", UNRECOGNIZED_FNUM_ENUM, getFunctionNameFromEnum((enum detFuncs)function_id))); } - auto returncode = processReceivedData( + auto processedResult = processReceivedData( static_cast(function_id), socket); - if (returncode == slsDetectorDefs::ReturnCode::FAIL) { + // TODO: should technically fail before + if (processedResult.returnCode == + slsDetectorDefs::ReturnCode::FAIL) { throw RuntimeError(fmt::format( - "Error processing command with fnum: {}", - getFunctionNameFromEnum((enum detFuncs)function_id))); + "Error while processing command with fnum: {}, Error: " + "{}", + getFunctionNameFromEnum((enum detFuncs)function_id), + processedResult.error_message)); } } catch (const RuntimeError &e) { @@ -76,22 +80,20 @@ void TCPInterface::startTCPServerClientConnection() { LOG(logINFOBLUE) << "Exiting TCP Server"; } -slsDetectorDefs::ReturnCode -TCPInterface::processReceivedData(const detFuncs function_id, - ServerInterface &socket) { +ProcessedResult TCPInterface::processReceivedData(const detFuncs function_id, + ServerInterface &socket) { LOG(logDEBUG1) << "calling function fnum: " << function_id << " (" << getFunctionNameFromEnum((enum detFuncs)function_id) << ")"; - slsDetectorDefs::ReturnCode returncode = - processFunction(function_id, socket); + ProcessedResult processedResult = processFunction(function_id, socket); LOG(logDEBUG1) << "Function " << getFunctionNameFromEnum((enum detFuncs)function_id) << " finished"; - return returncode; + return processedResult; } } // namespace sls \ No newline at end of file diff --git a/slsDetectorSoftware/src/CtbConfig.cpp b/slsDetectorSoftware/src/CtbConfig.cpp index b1117e9ca..2bdb0dafe 100644 --- a/slsDetectorSoftware/src/CtbConfig.cpp +++ b/slsDetectorSoftware/src/CtbConfig.cpp @@ -1,6 +1,6 @@ #include "CtbConfig.h" -#include "SharedMemory.h" +#include "sls/SharedMemory.h" #include "sls/ToString.h" #include "sls/string_utils.h" diff --git a/slsDetectorSoftware/src/DetectorImpl.cpp b/slsDetectorSoftware/src/DetectorImpl.cpp index 954275a05..fd08b8bf5 100644 --- a/slsDetectorSoftware/src/DetectorImpl.cpp +++ b/slsDetectorSoftware/src/DetectorImpl.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #include "DetectorImpl.h" #include "Module.h" -#include "SharedMemory.h" +#include "sls/SharedMemory.h" #include "sls/ZmqSocket.h" #include "sls/detectorData.h" #include "sls/file_utils.h" diff --git a/slsDetectorSoftware/src/DetectorImpl.h b/slsDetectorSoftware/src/DetectorImpl.h index aad1b6b71..b29dd7716 100644 --- a/slsDetectorSoftware/src/DetectorImpl.h +++ b/slsDetectorSoftware/src/DetectorImpl.h @@ -3,8 +3,8 @@ #pragma once #include "CtbConfig.h" -#include "SharedMemory.h" #include "sls/Result.h" +#include "sls/SharedMemory.h" #include "sls/ZmqSocket.h" #include "sls/logger.h" #include "sls/sls_detector_defs.h" diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 898d046c5..64be1d6e7 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Module.h" -#include "SharedMemory.h" #include "sls/ClientSocket.h" +#include "sls/SharedMemory.h" #include "sls/ToString.h" #include "sls/Version.h" #include "sls/bit_utils.h" diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index f4db5c6cd..1f5097530 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once -#include "SharedMemory.h" #include "sls/ClientSocket.h" #include "sls/Pattern.h" +#include "sls/SharedMemory.h" #include "sls/StaticVector.h" #include "sls/bit_utils.h" #include "sls/logger.h" diff --git a/slsDetectorSoftware/tests/test-CtbConfig.cpp b/slsDetectorSoftware/tests/test-CtbConfig.cpp index 5aaffeb3e..84c904c5e 100644 --- a/slsDetectorSoftware/tests/test-CtbConfig.cpp +++ b/slsDetectorSoftware/tests/test-CtbConfig.cpp @@ -4,7 +4,7 @@ #include #include "CtbConfig.h" -#include "SharedMemory.h" +#include "sls/SharedMemory.h" #include #include diff --git a/slsDetectorSoftware/tests/test-Module.cpp b/slsDetectorSoftware/tests/test-Module.cpp index 39d9ef568..779f3ea34 100644 --- a/slsDetectorSoftware/tests/test-Module.cpp +++ b/slsDetectorSoftware/tests/test-Module.cpp @@ -2,8 +2,8 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Detector.h" #include "Module.h" -#include "SharedMemory.h" #include "catch.hpp" +#include "sls/SharedMemory.h" namespace sls { diff --git a/slsDetectorSoftware/tests/test-SharedMemory.cpp b/slsDetectorSoftware/tests/test-SharedMemory.cpp index d8a1efb16..324205acf 100644 --- a/slsDetectorSoftware/tests/test-SharedMemory.cpp +++ b/slsDetectorSoftware/tests/test-SharedMemory.cpp @@ -2,8 +2,8 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #define DISABLE_STATIC_ASSERT // to be able to test obsolete shm without isValid -#include "SharedMemory.h" #include "catch.hpp" +#include "sls/SharedMemory.h" #include "sls/string_utils.h" #include diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index 668f94e55..4b14004c7 100644 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -402,7 +402,7 @@ int ClientInterface::setup_receiver(Interface &socket) { } impl()->setThresholdEnergy(val); } - if (detType == EIGER || detType == MYTHEN3) { + if (detType == EIGER || detType == MYTHEN3 || detType == MATTERHORN) { impl()->setDynamicRange(arg.dynamicRange); } impl()->setTimingMode(arg.timMode); @@ -433,6 +433,10 @@ int ClientInterface::setup_receiver(Interface &socket) { impl()->setGateDelay3(std::chrono::nanoseconds(arg.gateDelay3Ns)); impl()->setNumberOfGates(arg.gates); } + if (detType == MATTERHORN) { + impl()->setCounterMask(arg.countermask); + } + LOG(logDEBUG) << "set counter mask to " << arg.countermask; if (detType == GOTTHARD2) { impl()->setBurstMode(arg.burstType); } @@ -454,6 +458,7 @@ void ClientInterface::setDetectorType(detectorType arg) { case MOENCH: case MYTHEN3: case GOTTHARD2: + case MATTERHORN: break; default: throw RuntimeError("Unknown detector type: " + std::to_string(arg)); @@ -670,12 +675,21 @@ int ClientInterface::set_dynamic_range(Interface &socket) { break; */ case 4: + if (detType == MATTERHORN || detType == EIGER) { + exists = true; + } + break; case 12: if (detType == EIGER) { exists = true; } break; case 8: + if (detType == MATTERHORN || detType == EIGER || + detType == MYTHEN3) { + exists = true; + } + break; case 32: if (detType == EIGER || detType == MYTHEN3) { exists = true; diff --git a/slsReceiverSoftware/src/GeneralData.h b/slsReceiverSoftware/src/GeneralData.h index e2cf34eae..4b61fdf56 100644 --- a/slsReceiverSoftware/src/GeneralData.h +++ b/slsReceiverSoftware/src/GeneralData.h @@ -472,6 +472,66 @@ class Mythen3Data : public GeneralData { }; }; +class MatterhornData : public GeneralData { + + public: + MatterhornData() { + detType = slsDetectorDefs::MATTERHORN; + headerSizeinPacket = sizeof(slsDetectorDefs::sls_detector_header); + framesPerFile = MATTERHORN_MAX_FRAMES_PER_FILE; // TODO: dummy value + fifoDepth = 50000; // TODO: dummy value + standardheader = true; + dataSize = 8192; + packetSize = headerSizeinPacket + dataSize; + // udpSocketBufferSize = 0; // TODO: dummy value + dynamicRange = 16; // default + SetCounterMask(0xf); // default all 4 counters enabled + UpdateImageSize(); + }; + + void SetDynamicRange(int dr) { + dynamicRange = dr; + UpdateImageSize(); + }; + + void SetCounterMask(const int mask) { + int n = __builtin_popcount(mask); + if (n < 1 || n > 4) { + throw RuntimeError("Invalid number of counters " + + std::to_string(n) + ". Expected 1-4."); + } + counterMask = mask; + ncounters = n; + UpdateImageSize(); + }; + + void CalculatefifoDepth() { fifoDepth = max_memory_allocated / imageSize; } + + private: + void UpdateImageSize() { + nPixelsX = nChannelsX; + nPixelsY = nChannelsY * ncounters; + + imageSize = nPixelsX * nPixelsY * GetPixelDepth(); + LOG(logINFO) << "imageSize: " << imageSize; + actualImageSize = imageSize; + + CalculatefifoDepth(); + + packetsPerFrame = imageSize / dataSize; + + LOG(logINFO) << "Packets Per Frame: " << packetsPerFrame; + }; + + private: + int ncounters{0}; + static constexpr int nChannelsX{1024}; + static constexpr int nChannelsY{512}; + + constexpr static int max_memory_allocated = + 100000; // TODO: dummy value for now +}; + class Gotthard2Data : public GeneralData { public: Gotthard2Data() { diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index a8c2d2213..e0a61199b 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -118,6 +118,7 @@ void Implementation::setDetectorType(const detectorType d) { case XILINX_CHIPTESTBOARD: case MYTHEN3: case GOTTHARD2: + case MATTERHORN: LOG(logINFO) << " ***** " << ToString(d) << " Receiver *****"; break; default: @@ -151,6 +152,9 @@ void Implementation::setDetectorType(const detectorType d) { case GOTTHARD2: generalData = new Gotthard2Data(); break; + case MATTERHORN: + generalData = new MatterhornData(); + break; default: break; } @@ -265,7 +269,11 @@ void Implementation::setModulePositionId(const int id) { xy portGeometry = GetPortGeometry(); streamingPort = DEFAULT_ZMQ_RX_PORTNO + modulePos * portGeometry.x; - assert(numModules.y != 0); + if (numModules.y == 0) { + throw RuntimeError("Number of modules in y direction is 0. Cannot set " + "module position."); + } + for (unsigned int i = 0; i < listener.size(); ++i) { uint16_t row = 0, col = 0; row = (modulePos % numModules.y) * portGeometry.y; @@ -1606,7 +1614,8 @@ uint32_t Implementation::getDynamicRange() const { void Implementation::setDynamicRange(const uint32_t i) { if (generalData->dynamicRange != i) { - if (generalData->detType == EIGER || generalData->detType == MYTHEN3) { + if (generalData->detType == EIGER || generalData->detType == MYTHEN3 || + generalData->detType == MATTERHORN) { generalData->SetDynamicRange(i); SetupFifoStructure(); } diff --git a/slsReceiverSoftware/src/receiver_defs.h b/slsReceiverSoftware/src/receiver_defs.h index 5e2d5f53c..c045f8ec6 100644 --- a/slsReceiverSoftware/src/receiver_defs.h +++ b/slsReceiverSoftware/src/receiver_defs.h @@ -31,6 +31,9 @@ namespace sls { #define XILINX_CTB_MAX_FRAMES_PER_FILE 20000 #define MYTHEN3_MAX_FRAMES_PER_FILE 10000 #define GOTTHARD2_MAX_FRAMES_PER_FILE 20000 +#define MATTERHORN_MAX_FRAMES_PER_FILE \ + 20000 // dummy value for now - maybe even specialized receiver for + // matterhorn? #define STATISTIC_FRAMENUMBER_INFINITE (20000) diff --git a/slsDetectorSoftware/src/SharedMemory.h b/slsSupportLib/include/sls/SharedMemory.h similarity index 97% rename from slsDetectorSoftware/src/SharedMemory.h rename to slsSupportLib/include/sls/SharedMemory.h index bbdcfdd66..006cfbcc9 100644 --- a/slsDetectorSoftware/src/SharedMemory.h +++ b/slsSupportLib/include/sls/SharedMemory.h @@ -92,7 +92,7 @@ template class SharedMemory { ~SharedMemory() { if (shared_struct) - unmapSharedMemory(); + unmapSharedMemory(); // only unmapped as resued in command line } /** memory is valid if it has the IsValid flag and is true */ @@ -168,7 +168,11 @@ template class SharedMemory { if (fd < 0) { std::string msg = "Create shared memory " + name + " failed: " + strerror(errno); - throw SharedMemoryError(msg); + if (errno == EEXIST) { + throw SharedMemoryAlreadyExistsError(msg); + } else { + throw SharedMemoryError(msg); + } } if (ftruncate(fd, sizeof(T)) < 0) { diff --git a/slsSupportLib/include/sls/sls_detector_exceptions.h b/slsSupportLib/include/sls/sls_detector_exceptions.h index 421b60549..38bc43ebf 100644 --- a/slsSupportLib/include/sls/sls_detector_exceptions.h +++ b/slsSupportLib/include/sls/sls_detector_exceptions.h @@ -17,6 +17,11 @@ class SharedMemoryError : public RuntimeError { explicit SharedMemoryError(const std::string &msg); }; +class SharedMemoryAlreadyExistsError : public SharedMemoryError { + public: + explicit SharedMemoryAlreadyExistsError(const std::string &msg); +}; + class SocketError : public RuntimeError { public: explicit SocketError(const std::string &msg); diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index 32fba195b..4da99e33a 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -10,4 +10,4 @@ #define APIXILINXCTB "0.0.0 0x260506" #define APIJUNGFRAU "0.0.0 0x260424" #define APIMYTHEN3 "0.0.0 0x260506" -#define APIMATTERHORN "0.0.0 0x260212" +#define APIMATTERHORN "0.0.0 0x260529" diff --git a/slsSupportLib/src/sls_detector_exceptions.cpp b/slsSupportLib/src/sls_detector_exceptions.cpp index d5a6ca645..369c19db8 100644 --- a/slsSupportLib/src/sls_detector_exceptions.cpp +++ b/slsSupportLib/src/sls_detector_exceptions.cpp @@ -14,6 +14,11 @@ RuntimeError::RuntimeError(const char *msg) : runtime_error(msg) { } SharedMemoryError::SharedMemoryError(const std::string &msg) : RuntimeError(msg) {} + +SharedMemoryAlreadyExistsError::SharedMemoryAlreadyExistsError( + const std::string &msg) + : SharedMemoryError(msg) {} + SocketError::SocketError(const std::string &msg) : RuntimeError(msg) {} ZmqSocketError::ZmqSocketError(const std::string &msg) : RuntimeError(msg) {} NotImplementedError::NotImplementedError(const std::string &msg)