Dev/matterhorn server funcs (#1469)
Build on RHEL9 docker image / build (push) Successful in 4m15s
Build on RHEL8 docker image / build (push) Successful in 5m21s
Build and Deploy on local RHEL9 / build (push) Successful in 2m12s
Build and Deploy on local RHEL8 / build (push) Successful in 5m12s
Run Simulator Tests on local RHEL9 / build (push) Successful in 18m55s
Run Simulator Tests on local RHEL8 / build (push) Successful in 22m19s

* added fetch fmt server library

* added first draft of matterhorn

* added cpp TCP Interface to slsDetectorServer

* bug: added std::signal for proper handling of ctr+c

* added compile option to set log level

* WIP

* dont use c project settings when building matterhornserver

* updated logger

* WIP

* WIP

* linked fmt to slsProjectOptions

* solved merge conflict

* some refactoring

* cleaned up logs

* WIP

* generated register defs from csv file

* oops given in hex

* properly added fmt as a dependency

* some format changes

* WIP

* used CRTP for virtual detector

* WIP

* added udp functions to matterhornserver

* fixed build

* added Server class usable for all detectors

* removed stopserver

* added some more functions

* wrong overload

* porper cleanup of matterhorn app

* PR Review

* refactored directory structure

* added shared memory for aqcuisition status

* implemented bus read and write

* added Matterhorn data class

* modified memory model

* added SPI communication

* moved memory for Bus communication to BucCommunication class

* WIP added HelperFunction for mask conversion

* completed HelperFunctions

* added tests for MatterHorn Helper Functions

* removed shared memory in destructor

* fixed SPI communication

* moved SharedMemory to slsSupportLib

* added toolchain file for cross compilation

* fix constructor for HarwareSPICommunication is public

* update receiver parameters function

* updated mac address

* uploaded matterhorn server binary tracked as lfs

* small fixes from tests on board

* automatically update matterhorn api version when cross compiling binaries

* fix bug in SPI read

* copy to server_directory/bin when cross compiling

* fixed cmake list

* update generate_registerdefs.py

* fixed copy of server binary

* always return error message

* add error log when opening dev/mem

* code review

* fixed eiger, mythen tests for dynamic range

* second Review

* member function getDerived for better readability

* split code into implementation class and actual class doing tcp communication

* added slsWarnings and c++17 compiler option

* added git lfs to workflow

* fix gitea workflows

* bug: add extra clock register

* implement initial checks

* bug: client excpetcs 4 bytes

* refactoring directory structure and rename to .hpp

* check if detector already set up

* missed .h to .hpp

* Code Review 3

* use temmplate value for stopserver

* use internal CMAKE_CROSSCOMPILING variable

* moved boolean stop server flag to DetectorImpl - moving template parameter up

* missed a file

* replace baseclass:: with this

* add Matterhorn server in github workflow

* whatever workflow bug- lets see

* fixed receiving trim bits changed to int64_t
This commit is contained in:
2026-07-16 10:10:14 +02:00
committed by GitHub
parent c3ad3d2e73
commit 7db947e4d2
65 changed files with 2935 additions and 613 deletions
+2 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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
+17 -4
View File
@@ -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
+30 -6
View File
@@ -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
"""
+17 -16
View File
@@ -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)
@@ -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
@@ -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=<VirtualMatterhornServer.hpp>
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=<MatterhornServer.hpp>
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?
#)
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4bfbe17a6b0efbdcd3da78eafb1ac6c212eb81434f36267640f75fe743dc0aea
size 316776
@@ -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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
/// @brief Base class for Matterhorn Server, can be used to implement a virtual
/// server for testing and actual server
template <typename DerivedServer>
class BaseMatterhornServer
: public DetectorServer<BaseMatterhornServer<DerivedServer>> {
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<BaseMatterhornServer<DerivedServer>>(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 <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::processFunction(const detFuncs function_id,
ServerInterface &socket) {
switch (function_id) {
default:
throw RuntimeError(
fmt::format("Function {} not implemented",
getFunctionNameFromEnum((enum detFuncs)function_id)));
}
}
template <typename DerivedServer>
ReturnCode BaseMatterhornServer<DerivedServer>::get_num_udp_interfaces(
ServerInterface &socket) const {
return static_cast<ReturnCode>(
socket.sendResult(static_cast<int>(numUDPInterfaces)));
}
template <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::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<ReturnCode>(socket.sendResult(
version_cstr)); // TODO: check what would be possible return codes!!!
}
template <typename DerivedServer>
ReturnCode BaseMatterhornServer<DerivedServer>::get_detector_type(
ServerInterface &socket) {
int detectortype = slsDetectorDefs::detectorType::MATTERHORN;
return static_cast<ReturnCode>(socket.sendResult(detectortype));
}
template <typename DerivedServer>
std::string BaseMatterhornServer<DerivedServer>::getMatterhornServerVersion() {
return APIMATTERHORN;
}
template <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::initial_checks(ServerInterface &socket) {
return static_cast<DerivedServer *>(this)->initial_checks(socket);
}
} // namespace sls
@@ -1,27 +0,0 @@
#pragma once
#include "BaseMatterhornServer.h"
#include "TCPInterface.h"
#include "sls/sls_detector_defs.h"
#include <array>
#include <memory>
namespace sls {
class MatterhornServer : public BaseMatterhornServer<MatterhornServer> {
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
@@ -1,29 +0,0 @@
#include <cstdint>
#include <string_view>
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
@@ -1,24 +0,0 @@
#include "BaseMatterhornServer.h"
namespace sls {
class VirtualMatterhornServer
: public BaseMatterhornServer<VirtualMatterhornServer> {
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
@@ -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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
/// @brief Base class for Matterhorn Server, can be used to implement a virtual
/// server for testing and actual server
template <typename DerivedServer>
class BaseMatterhornServer
: public DetectorServer<BaseMatterhornServer<DerivedServer>> {
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<is_stop_server<DerivedServer>::value>>
impl,
uint16_t port = DEFAULT_TCP_CNTRL_PORTNO)
: DetectorServer<BaseMatterhornServer<DerivedServer>>(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<DerivedServer *>(this); }
const DerivedServer *getDerived() const {
return static_cast<const DerivedServer *>(this);
}
};
template <typename DerivedServer>
ProcessedResult
BaseMatterhornServer<DerivedServer>::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 <typename DerivedServer>
ProcessedResult
BaseMatterhornServer<DerivedServer>::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 <typename DerivedServer>
ProcessedResult BaseMatterhornServer<DerivedServer>::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
@@ -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 <cstdint>
#include <string>
namespace sls {
template <bool isStopServer>
class VirtualMatterhornServerImpl; // forward declare
template <typename DerivedMatterhornServerImpl>
class BaseMatterhornServerImpl
: public DetectorServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::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<DerivedMatterhornServerImpl,
VirtualMatterhornServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::value>>,
VirtualMemoryModel<uint32_t>,
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<MatterhornDefs::MatterHornIPCores, MemoryModel>
busCommunication{};
using SPICommunicationClass = std::conditional_t<
std::is_same_v<DerivedMatterhornServerImpl,
VirtualMatterhornServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::value>>,
VirtualSPICommunication<MatterhornDefs::MatterhornSPIRegisters>,
HardwareSPICommunication>;
SPICommunicationClass spiCommunication{};
private:
static constexpr uint8_t numUDPInterfaces =
1; // only one udp per module for now
};
template <typename DerivedMatterhornServerImpl>
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 <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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 <typename DerivedMatterhornServerImpl>
std::string
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_server_version()
const {
return APIMATTERHORN;
}
template <typename DerivedMatterhornServerImpl>
uint8_t BaseMatterhornServerImpl<
DerivedMatterhornServerImpl>::get_num_udp_interfaces() {
return numUDPInterfaces;
}
template <typename DerivedMatterhornServerImpl>
uint8_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_detector_type() {
return slsDetectorDefs::detectorType::MATTERHORN;
}
template <typename DerivedMatterhornServerImpl>
uint64_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_num_frames() const {
try {
uint32_t num_frames =
busCommunication.readRegister(Reg::MH_SM_Frames_Reg);
return static_cast<uint64_t>(num_frames);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to read number of frames from register: "
<< e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::set_num_frames(
const uint64_t num_frames) {
try {
busCommunication.writeRegister(Reg::MH_SM_Frames_Reg,
static_cast<uint32_t>(num_frames));
auto written_num_frames = busCommunication.readRegister(
Reg::MH_SM_Frames_Reg); // check if write was successful
if (num_frames != static_cast<uint64_t>(written_num_frames)) {
throw std::runtime_error(
fmt::format("Requested {} frames, but set {}", num_frames,
static_cast<uint64_t>(written_num_frames)));
}
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set number of frames: " << e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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 <typename DerivedMatterhornServerImpl>
uint32_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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 <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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 <typename DerivedMatterhornServerImpl>
uint32_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_counter_mask()
const {
std::vector<std::byte> 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 <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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 <typename DerivedMatterhornServerImpl>
slsDetectorDefs::rxParameters
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::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
@@ -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 <semaphore.h>
#include <csignal>
#include <fmt/format.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -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<true> 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";
@@ -1,24 +0,0 @@
#include "MatterhornServer.h"
namespace sls {
MatterhornServer::MatterhornServer(uint16_t port)
: BaseMatterhornServer<MatterhornServer>(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<ReturnCode>(socket.sendResult(initial_checks_passed));
}
} // namespace sls
@@ -0,0 +1,41 @@
#pragma once
#include "BaseMatterhornServer.hpp"
#include "MatterhornServerImpl.hpp"
#include "TCPInterface.hpp"
#include "sls/sls_detector_defs.h"
#include <array>
#include <memory>
namespace sls {
template <bool isStopServer = false>
class MatterhornServer
: public BaseMatterhornServer<MatterhornServer<isStopServer>> {
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 <bool isStopServer>
MatterhornServer<isStopServer>::MatterhornServer(uint16_t port)
: BaseMatterhornServer<MatterhornServer<isStopServer>>(
std::make_unique<MatterhornServerImpl<isStopServer>>(), 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
@@ -0,0 +1,70 @@
#pragma once
#include "BaseMatterhornServerImpl.hpp"
namespace sls {
template <bool isStopServer = false>
class MatterhornServerImpl
: public BaseMatterhornServerImpl<MatterhornServerImpl<isStopServer>> {
public:
MatterhornServerImpl() = default;
~MatterhornServerImpl() = default;
slsDetectorDefs::runStatus get_run_status() const; // TODO: impement
void set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info);
void set_source_udp_mac([[maybe_unused]] const uint64_t src_mac);
};
template <bool isStopServer>
slsDetectorDefs::runStatus
MatterhornServerImpl<isStopServer>::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 <bool isStopServer>
void MatterhornServerImpl<isStopServer>::
set_module_position_and_update_srcudpmac(
const std::array<int, 2> &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 <bool isStopServer>
void MatterhornServerImpl<isStopServer>::set_source_udp_mac(
const uint64_t src_mac) {
throw RuntimeError(
"Cannot overwrite vendor specific source UDP MAC address.");
}
} // end namespace sls
@@ -1,24 +0,0 @@
#include "VirtualMatterhornServer.h"
namespace sls {
VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port)
: BaseMatterhornServer<VirtualMatterhornServer>(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<ReturnCode>(socket.sendResult(initial_checks_passed));
}
} // namespace sls
@@ -0,0 +1,39 @@
#include "BaseMatterhornServer.hpp"
#include "VirtualMatterhornServerImpl.hpp"
namespace sls {
template <bool isStopServer = false>
class VirtualMatterhornServer
: public BaseMatterhornServer<VirtualMatterhornServer<isStopServer>> {
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 <bool isStopServer>
VirtualMatterhornServer<isStopServer>::VirtualMatterhornServer(uint16_t port)
: BaseMatterhornServer<VirtualMatterhornServer<isStopServer>>(
std::make_unique<VirtualMatterhornServerImpl<isStopServer>>(), 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
@@ -0,0 +1,87 @@
#pragma once
#include "BaseMatterhornServerImpl.hpp"
#include "sls/ToString.h"
namespace sls {
template <bool isStopServer = false>
class VirtualMatterhornServerImpl
: public BaseMatterhornServerImpl<
VirtualMatterhornServerImpl<isStopServer>> {
public:
VirtualMatterhornServerImpl();
~VirtualMatterhornServerImpl() = default;
slsDetectorDefs::runStatus get_run_status() const;
void set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info);
void set_source_udp_mac(const uint64_t newsrcudpMac);
};
template <bool isStopServer>
VirtualMatterhornServerImpl<isStopServer>::VirtualMatterhornServerImpl() {
this->set_source_udp_ip(LOCALHOSTIP_INT);
}
template <bool isStopServer>
slsDetectorDefs::runStatus
VirtualMatterhornServerImpl<isStopServer>::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 <bool isStopServer>
void VirtualMatterhornServerImpl<isStopServer>::
set_module_position_and_update_srcudpmac(
const std::array<int, 2> &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 <bool isStopServer>
void VirtualMatterhornServerImpl<isStopServer>::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
@@ -0,0 +1,120 @@
#include "SPICommunication.hpp"
#include <algorithm>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <unistd.h>
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<std::byte>
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<size_t>::max()) {
throw RuntimeError("SPI read size overflow");
}
std::vector<std::byte> 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<std::byte>(((chip_id & 0xF) << 4) | (register_id & 0xF));
// allocate data buffer to read out data into
std::vector<std::byte> 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<std::uintptr_t>(dummy_data.data());
send_cmd.rx_buf = reinterpret_cast<std::uintptr_t>(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<std::byte> 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<std::byte> &data) {
const size_t n_bytes = data.size();
if (n_bytes == std::numeric_limits<size_t>::max()) {
throw RuntimeError("SPI read size overflow");
}
// First byte of the message is 4 bits chip_id then 4 bits register_id
std::vector<std::byte> write_data(n_bytes + 1); // +1 for the command byte
write_data[0] =
static_cast<std::byte>(((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<std::uintptr_t>(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
@@ -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 <map>
#include <vector>
namespace sls {
/// @brief abstract base class for SPI communication
template <typename DerivedSPIModel> class SPICommunication {
public:
SPICommunication() = default;
~SPICommunication() = default;
std::vector<std::byte> 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<std::byte> &data);
void open_spi();
private:
DerivedSPIModel *getDerived() {
return static_cast<DerivedSPIModel *>(this);
}
};
template <typename DerivedSPIModel>
void SPICommunication<DerivedSPIModel>::open_spi() {
getDerived()->open_spi();
}
template <typename DerivedSPIModel>
std::vector<std::byte>
SPICommunication<DerivedSPIModel>::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<const DerivedSPIModel *>(this)->spi_read(
spi_reg.n_bytes, chip_id, spi_reg.spi_register_id);
}
template <typename DerivedSPIModel>
void SPICommunication<DerivedSPIModel>::SPIwrite(
const SPIRegister &spi_reg, const uint8_t chip_id,
const std::vector<std::byte> &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>{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<HardwareSPICommunication> {
public:
HardwareSPICommunication() = default;
~HardwareSPICommunication();
void open_spi();
void spi_write(const uint8_t chip_id, const uint8_t register_id,
const std::vector<std::byte> &data);
std::vector<std::byte> 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 <SPIRegister... SPIRegisters, uint8_t NUM_CHIPS_PER_MODULE> // non
// type template parameters only for c++20
template <typename SPIRegisters> // TODO add a type trait to ensure it stores
// all fields
class VirtualSPICommunication
: public SPICommunication<VirtualSPICommunication<SPIRegisters>> {
public:
VirtualSPICommunication() {
// TODO should it be in the constructor?
/*
(virtual_registers.emplace(
SPIRegisters.spi_register_id,
VirtualMemoryModel<std::byte>{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 &reg : SPIRegisters::spiregisters) {
virtual_registers.emplace(
reg.spi_register_id,
VirtualMemoryModel<std::byte>{
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<std::byte> 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<std::byte> 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<std::byte> &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<uint16_t, VirtualMemoryModel<std::byte>> virtual_registers{};
};
} // namespace sls
@@ -0,0 +1,87 @@
#pragma once
#include "fmt/format.h"
#include <cstdint>
#include <vector>
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<std::byte> &register_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<std::byte> &register_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<uint32_t>((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
@@ -0,0 +1,50 @@
#pragma once
#include "RegisterDefs.hpp"
#include "SPIRegisterDefs.hpp"
#include <array>
#include <cstdint>
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<SPIRegister, 12> 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<IPCore, 5> 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
@@ -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
@@ -0,0 +1,51 @@
#pragma once
#include "SPIRegisterHelperStructs.hpp"
#include <cstdint>
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
@@ -0,0 +1,75 @@
#include "HelperFunctions.hpp"
#include <stdexcept>
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
@@ -0,0 +1,26 @@
/**
* @file HelperFunctions.hpp
* @short contains helper functions for the Matterhorn server implementation
* e.g. for processing of specific commands
*/
#include <cstdint>
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
@@ -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
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src/utils>")
@@ -0,0 +1,48 @@
#include "HelperFunctions.hpp"
#include "catch.hpp"
#include <iostream>
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
@@ -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)
@@ -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_OBJECTS:slsServerObject>)
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_OBJECTS:slsServerObject>)
target_link_libraries(slsServerStatic PUBLIC slsServerObject)
target_link_libraries(slsServerStatic PUBLIC slsServerObject PRIVATE slsProjectWarnings)
set_target_properties(slsServerStatic PROPERTIES
ARCHIVE_OUTPUT_NAME SlsServerStatic
@@ -0,0 +1,87 @@
#pragma once
#include "RegisterHelperStructs.hpp"
#include "fmt/format.h"
#include <cstdint>
#include <fcntl.h>
#include <map>
#include <memory>
#include <stdexcept>
#include <sys/mman.h>
#include <vector>
// TODO: maybe should be templated on address type (e.g. uint32_t register or
// uint64_t register) for more flexibility?
namespace sls {
template <typename IPCores, typename MemoryModel> class BusCommunication {
using IPCoreEnumType = typename IPCores::ipcore_enum_type;
public:
BusCommunication();
void mapToMemory();
uint32_t readRegister(const Register &register_) const;
void writeRegister(const Register &register_, 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<IPCoreEnumType, MemoryModel> ipcoreregisterblocks{};
};
template <typename IPCores, typename MemoryModel>
BusCommunication<IPCores, MemoryModel>::BusCommunication() {
for (const auto &ip_core : IPCores::ipcores) {
ipcoreregisterblocks.emplace(ip_core,
MemoryModel{static_cast<uint32_t>(ip_core),
IPCores::ip_core_block_size});
}
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::mapToMemory() {
for (auto &map_elem : ipcoreregisterblocks) {
map_elem.second.mapToMemory();
}
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::readRegister(
const Register &register_) const {
return bus_r(register_.offset_in_bytes, register_.ip_core);
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::writeRegister(
const Register &register_, const uint32_t data) {
bus_w(register_.offset_in_bytes, register_.ip_core, data);
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::bus_r(
const uint32_t offset, const IPCoreEnumType baseadress) const {
auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() +
offset / (sizeof(uint32_t));
return *ptr1;
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::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
@@ -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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
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 <typename DerivedDetectorServer> 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> tcpInterface;
std::array<UDPInfo, 1>
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 <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(uint16_t port) {
validatePortNumber(port);
udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO;
udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO;
std::function<ReturnCode(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::processFunction(
const detFuncs function_id, ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_GET_SERVER_VERSION:
return static_cast<DerivedDetectorServer *>(this)->get_version(socket);
case detFuncs::F_GET_DETECTOR_TYPE:
return static_cast<DerivedDetectorServer *>(this)->get_detector_type(
socket);
case detFuncs::F_INITIAL_CHECKS:
return static_cast<DerivedDetectorServer *>(this)->initial_checks(
socket);
case detFuncs::F_GET_NUM_INTERFACES:
return static_cast<DerivedDetectorServer *>(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<DerivedDetectorServer *>(this)->processFunction(function_id,
socket);
}
return ReturnCode::FAIL;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_update_mode(
ServerInterface &socket) const {
return static_cast<ReturnCode>(
socket.sendResult(static_cast<int>(updateMode)));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
int ret = socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
int ret = socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstport));
};
} // namespace sls
@@ -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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
using ReturnCode = slsDetectorDefs::ReturnCode;
template <typename DerivedDetectorServer> 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<is_stop_server<DerivedDetectorServer>::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> tcpInterface;
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl;
auto *getImpl() {
return static_cast<typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
const auto *getImpl() const {
return static_cast<const typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
private:
/// @brief get derived class
DerivedDetectorServer *getDerived() {
return static_cast<DerivedDetectorServer *>(this);
}
const DerivedDetectorServer *getDerived() const {
return static_cast<const DerivedDetectorServer *>(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 <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl_,
uint16_t port)
: impl(std::move(impl_)) {
validatePortNumber(port);
std::function<ProcessedResult(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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<uint32_t>(updateMode));
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
auto srcUdpMac = impl->get_source_udp_mac();
return send_result(socket, srcUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
(void)socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
uint32_t src_UdpIp = impl->get_source_udp_ip();
return send_result(socket, src_UdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
(void)socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
auto dstUdpMac = impl->get_destination_udp_mac();
return send_result(socket, dstUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
uint32_t dstUdpIp = impl->get_destination_udp_ip();
return send_result(socket, dstUdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
uint16_t dstUdpPort = impl->get_destination_udp_port();
return send_result(socket, dstUdpPort);
};
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_triggers(
ServerInterface &socket) const {
uint64_t num_triggers{};
try {
num_triggers = static_cast<uint64_t>(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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_udp_interfaces(
ServerInterface &socket) const {
int num_udp_interfaces = getImpl()->get_num_udp_interfaces();
return send_result(socket, num_udp_interfaces);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_detector_type(
ServerInterface &socket) const {
uint32_t detectortype = getImpl()->get_detector_type();
return send_result(socket, detectortype);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_receiver_parameters(
ServerInterface &socket) const {
slsDetectorDefs::rxParameters rx_params =
getImpl()->get_receiver_parameters();
return send_result(socket, rx_params);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_run_status(
ServerInterface &socket) const {
slsDetectorDefs::runStatus status = getImpl()->get_run_status();
return send_result(socket, status);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::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<bool>(socket, true);
}
}
template <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::set_module_position_and_update_srcudpmac(
ServerInterface &socket) {
std::array<int, 2> 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
@@ -0,0 +1,109 @@
#pragma once
#include "sls/SharedMemory.h"
#include <array>
#include <atomic>
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<slsDetectorDefs::runStatus> scanStatus{
slsDetectorDefs::runStatus::IDLE}; // idle, running or error
std::atomic<bool> scanStop{false};
// TODO: only neccessary for virtual, maybe have two shared memory
// structures, one for virtual
std::atomic<slsDetectorDefs::runStatus> status{
slsDetectorDefs::runStatus::IDLE};
std::atomic<bool> stop{false};
};
template <bool isStopServer> 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<UDPInfo, 1>
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<acquisitionStatus> 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
@@ -0,0 +1,68 @@
#pragma once
#include "fmt/format.h"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <cstdint>
#include <vector>
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 <typename DataType> 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<DataType> mapped_memory{};
/// @brief size mapped memory region [bytes]
const size_t size_memory_space{0};
};
} // namespace sls
@@ -0,0 +1,62 @@
#pragma once
#include <cstdint>
#include <exception>
#include <fmt/format.h>
#include <string_view>
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 <typename T>
void setRegisterField(uint32_t &registervalue, const RegisterField &reg_field,
T field_value) {
if (field_value > static_cast<T>(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<uint32_t>(field_value) & reg_field.bitmask)
<< reg_field.bit_position;
}
template <typename T>
T getRegisterField(const uint32_t &registervalue,
const RegisterField &reg_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<T>(field_value);
}
} // namespace sls
@@ -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<std::string>(error_message)};
}
inline ProcessedResult send_ok(ServerInterface &socket) {
return ProcessedResult{
static_cast<ReturnCode>(socket.Send(ReturnCode::OK))};
}
template <typename T>
inline ProcessedResult send_result(ServerInterface &socket, const T &value) {
return ProcessedResult{static_cast<ReturnCode>(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<slsDetectorDefs::ReturnCode(
const detFuncs &, ServerInterface &)> &processFunction_,
const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO);
TCPInterface(
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
&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<slsDetectorDefs::ReturnCode(const detFuncs &,
ServerInterface &)>
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
processFunction;
/// @brief TCP/IP port number for the detector server
@@ -0,0 +1,19 @@
/** @file Defs.hpp
* @brief this file contains some definitions used in the slsDetectorServer_cpp
* project.
*/
#pragma once
#include <cstdint>
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
@@ -0,0 +1,64 @@
#pragma once
#include "Defs.hpp"
#include "DetectorServerImpl.hpp"
#include "sls/SharedMemory.h"
#include "sls/sls_detector_defs.h"
#include <cstdint>
#include <cstdlib>
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<uint64_t>(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<acquisitionStatus> shm(0, -1, "server");
if (shm.exists()) {
shm.removeSharedMemory();
}
}
} // namespace sls
@@ -0,0 +1,57 @@
#pragma once
#include <type_traits>
namespace sls {
// forward declares
template <bool isStopServer> class MatterhornServer;
template <bool isStopServer> class VirtualMatterhornServer;
template <bool isStopServer> class MatterhornServerImpl;
template <bool isStopServer> class VirtualMatterhornServerImpl;
template <typename DerivedServer> class BaseMatterhornServer;
// type trait to get implementation type
template <typename DetectorServer> struct implementation_type_trait;
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<MatterhornServer<isStopServer>>> {
using ImplType = MatterhornServerImpl<isStopServer>;
};
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<VirtualMatterhornServer<isStopServer>>> {
using ImplType = VirtualMatterhornServerImpl<isStopServer>;
};
// type trait to get stop server flag from Detector Server
template <typename DetectorServerImpl>
struct is_stop_server : std::false_type {};
template <>
struct is_stop_server<VirtualMatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<MatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<VirtualMatterhornServer<true>> : std::true_type {};
template <> struct is_stop_server<MatterhornServer<true>> : std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<VirtualMatterhornServer<true>>>
: std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<MatterhornServer<true>>>
: std::true_type {};
} // namespace sls
@@ -1,4 +1,4 @@
#include "CommandLineOptions.h"
#include "CommandLineOptions.hpp"
#include "sls/ToString.h"
#include "sls/sls_detector_exceptions.h"
@@ -0,0 +1,117 @@
#include "DetectorServerImpl.hpp"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <fmt/format.h>
namespace sls {
template class DetectorServerImpl<true>; // forward declare
template class DetectorServerImpl<false>; // forward declare
template <bool isStopServer>
DetectorServerImpl<isStopServer>::DetectorServerImpl() {
udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO;
udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO;
createSharedMemory();
}
template <bool isStopServer>
DetectorServerImpl<isStopServer>::~DetectorServerImpl() {
shm.removeSharedMemory();
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::createSharedMemory() {
shm = SharedMemory<acquisitionStatus>(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 <bool isStopServer>
void DetectorServerImpl<isStopServer>::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 isStopServer>
bool DetectorServerImpl<isStopServer>::get_update_mode() const {
return updateMode;
}
template <bool isStopServer>
uint64_t DetectorServerImpl<isStopServer>::get_source_udp_mac() const {
return udpDetails[0].srcmac;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_source_udp_ip(const uint32_t srcip) {
udpDetails[0].srcip = srcip;
}
template <bool isStopServer>
uint32_t DetectorServerImpl<isStopServer>::get_source_udp_ip() const {
return udpDetails[0].srcip;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_ip(
const uint32_t dstip) {
udpDetails[0].dstip = dstip;
}
template <bool isStopServer>
uint32_t DetectorServerImpl<isStopServer>::get_destination_udp_ip() const {
return udpDetails[0].dstip;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_mac(
const uint64_t dstmac) {
// TODO: configuremac, check unicast address
udpDetails[0].dstmac = dstmac;
}
template <bool isStopServer>
uint64_t DetectorServerImpl<isStopServer>::get_destination_udp_mac() const {
return udpDetails[0].dstmac;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_port(
const uint16_t dstport) {
udpDetails[0].dstport = dstport;
}
template <bool isStopServer>
uint16_t DetectorServerImpl<isStopServer>::get_destination_udp_port() const {
return udpDetails[0].dstport;
}
template <bool isStopServer>
detector_setup_status
DetectorServerImpl<isStopServer>::get_detector_setup_status() const {
return detectorSetupStatus;
}
} // namespace sls
@@ -0,0 +1,62 @@
#include "MemoryModel.hpp"
#include <fcntl.h>
#include <memory>
#include <stdexcept>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
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<volatile uint32_t *>(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<void *>(
const_cast<uint32_t *>(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
@@ -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<slsDetectorDefs::ReturnCode(
const detFuncs &, ServerInterface &)> &processFunction_,
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
&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<detFuncs>(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
+1 -1
View File
@@ -1,6 +1,6 @@
#include "CtbConfig.h"
#include "SharedMemory.h"
#include "sls/SharedMemory.h"
#include "sls/ToString.h"
#include "sls/string_utils.h"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -4,7 +4,7 @@
#include <stdlib.h>
#include "CtbConfig.h"
#include "SharedMemory.h"
#include "sls/SharedMemory.h"
#include <fstream>
#include <set>
+1 -1
View File
@@ -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 {
@@ -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 <filesystem>
+15 -1
View File
@@ -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;
+60
View File
@@ -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() {
+11 -2
View File
@@ -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();
}
+3
View File
@@ -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)
@@ -92,7 +92,7 @@ template <typename T> 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 <typename T> 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) {
@@ -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);
+1 -1
View File
@@ -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"
@@ -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)