mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2026-07-06 12:09:31 +02:00
added SPI communication
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
set(MATTERHORN_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornApp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/SPICommunication.cpp
|
||||
)
|
||||
|
||||
if(SLS_USE_SIMULATOR)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "DetectorServer.h"
|
||||
#include "MemoryModel.hpp"
|
||||
#include "RegisterDefs.hpp"
|
||||
#include "SPICommunication.h"
|
||||
#include "SpecializedTemplates.h"
|
||||
#include "TCPInterface.h"
|
||||
#include "fmt/format.h"
|
||||
@@ -55,6 +56,10 @@ class BaseMatterhornServer
|
||||
|
||||
ReturnCode set_module_position(ServerInterface &socket);
|
||||
|
||||
ReturnCode set_counter_mask(ServerInterface &socket);
|
||||
|
||||
ReturnCode get_counter_mask(ServerInterface &socket) const;
|
||||
|
||||
uint64_t getNumFrames() const;
|
||||
|
||||
void setNumFrames(const uint64_t num_frames);
|
||||
@@ -75,12 +80,19 @@ class BaseMatterhornServer
|
||||
protected:
|
||||
using MemoryModel = std::conditional_t<
|
||||
std::is_same_v<DerivedServer, VirtualMatterhornServer>,
|
||||
VirtualMemoryModel, HardwareMemoryModel>;
|
||||
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<IPCore, MemoryModel> busCommunication{};
|
||||
|
||||
using SPICommunicationClass = std::conditional_t<
|
||||
std::is_same_v<DerivedServer, VirtualMatterhornServer>,
|
||||
SPICommunication<VirtualSPICommunication<MatterhornSPIRegisters>>,
|
||||
SPICommunication<HardwareSPICommunication>>;
|
||||
|
||||
SPICommunicationClass spiCommunication{};
|
||||
|
||||
// TODO: probably virtaul server specific details, can be moved to derived
|
||||
// class
|
||||
/// @brief initial setup of detector
|
||||
@@ -99,6 +111,10 @@ 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",
|
||||
@@ -314,4 +330,61 @@ ReturnCode BaseMatterhornServer<DerivedServer>::set_module_position(
|
||||
return static_cast<ReturnCode>(socket.Send(ReturnCode::OK));
|
||||
}
|
||||
|
||||
template <typename DerivedServer>
|
||||
ReturnCode
|
||||
BaseMatterhornServer<DerivedServer>::set_counter_mask(ServerInterface &socket) {
|
||||
|
||||
// TODO: update properly
|
||||
|
||||
uint32_t counter_mask{};
|
||||
try {
|
||||
int ret = socket.Receive(counter_mask);
|
||||
} catch (const SocketError &e) {
|
||||
LOG(logERROR) << "Failed to receive counter mask: " << e.what();
|
||||
return ReturnCode::FAIL;
|
||||
}
|
||||
|
||||
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,
|
||||
counter_mask);
|
||||
|
||||
spiCommunication.SPIwrite(SPIRegisters::NUM_COUNTERS.register_, 0,
|
||||
reg_value);
|
||||
} catch (const std::exception &e) {
|
||||
LOG(logERROR) << "Failed to set counter mask: " << e.what();
|
||||
return ReturnCode::FAIL;
|
||||
}
|
||||
|
||||
return static_cast<ReturnCode>(socket.Send(ReturnCode::OK));
|
||||
}
|
||||
|
||||
template <typename DerivedServer>
|
||||
ReturnCode BaseMatterhornServer<DerivedServer>::get_counter_mask(
|
||||
ServerInterface &socket) const {
|
||||
|
||||
// TODO: update properly
|
||||
|
||||
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) {
|
||||
LOG(logERROR) << "Failed to read counter mask from SPI register: "
|
||||
<< e.what();
|
||||
return ReturnCode::FAIL;
|
||||
}
|
||||
|
||||
uint32_t counter_mask =
|
||||
getSPIRegisterField(reg_value, SPIRegisters::NUM_COUNTERS);
|
||||
|
||||
return static_cast<ReturnCode>(socket.sendResult(counter_mask));
|
||||
}
|
||||
|
||||
} // namespace sls
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace sls {
|
||||
|
||||
namespace MatterhornDefs {
|
||||
|
||||
constexpr uint8_t NUM_CHIPS_PER_MODULE = 8;
|
||||
|
||||
struct MatterhornSPIRegisters {
|
||||
|
||||
constexpr static std::array<SPIRegister, 10> spiregisters{
|
||||
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};
|
||||
|
||||
constexpr static uint8_t NUM_CHIPS_PER_MODULE =
|
||||
MatterhornDefs::NUM_CHIPS_PER_MODULE;
|
||||
};
|
||||
|
||||
} // namespace MatterhornDefs
|
||||
|
||||
} // namespace sls
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "MemoryModel.hpp"
|
||||
#include "SPIRegisterDefs.hpp"
|
||||
#include "SPIRegisterHelperStructs.hpp"
|
||||
#include "fmt/format.h"
|
||||
#include "sls/logger.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace sls {
|
||||
|
||||
/// @brief abstract base class for SPI communication
|
||||
template <typename DerivedSPIModel> class SPICommunication {
|
||||
|
||||
public:
|
||||
SPICommunication();
|
||||
|
||||
~SPICommunication();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
template <typename DerivedSPIModel>
|
||||
SPICommunication<DerivedSPIModel>::SPICommunication() {
|
||||
static_cast<DerivedSPIModel *>(this)->map_to_memory();
|
||||
}
|
||||
|
||||
template <typename DerivedSPIModel>
|
||||
SPICommunication<DerivedSPIModel>::~SPICommunication() {
|
||||
static_cast<DerivedSPIModel *>(this)->unmap_memory();
|
||||
}
|
||||
|
||||
template <typename DerivedSPIModel>
|
||||
std::vector<std::byte>
|
||||
SPICommunication<DerivedSPIModel>::SPIread(const SPIRegister &spi_reg,
|
||||
const uint8_t chip_id) const {
|
||||
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 (data.size() != spi_reg.n_bytes) {
|
||||
LOG(logERROR) << fmt::format("Data size {} does not match number of "
|
||||
"bytes {} for SPI register {}",
|
||||
data.size(), spi_reg.n_bytes,
|
||||
spi_reg.spi_register_id);
|
||||
throw std::runtime_error(
|
||||
fmt::format("Data size {} does not match number of bytes {} for "
|
||||
"SPI register {}",
|
||||
data.size(), spi_reg.n_bytes, spi_reg.spi_register_id));
|
||||
}
|
||||
|
||||
static_cast<DerivedSPIModel *>(this)->spi_write(
|
||||
chip_id, spi_reg.spi_register_id, data);
|
||||
|
||||
static_cast<DerivedSPIModel *>(this)->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:
|
||||
void map_to_memory();
|
||||
|
||||
void unmap_memory();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// 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}),
|
||||
...);
|
||||
*/
|
||||
|
||||
for (const auto ® : SPIRegisters::spiregisters) {
|
||||
virtual_registers.emplace(
|
||||
reg.spi_register_id,
|
||||
VirtualMemoryModel<std::byte>{
|
||||
reg.spi_register_id,
|
||||
reg.n_bytes * SPIRegisters::NUM_CHIPS_PER_MODULE});
|
||||
}
|
||||
}
|
||||
|
||||
void map_to_memory() {
|
||||
|
||||
// resize the virtual register memory to the correct size based on
|
||||
// the defined SPI registers
|
||||
for (auto &[register_id, register_memory] : virtual_registers) {
|
||||
register_memory.mapToMemory();
|
||||
}
|
||||
}
|
||||
|
||||
void unmap_memory() { // do nothing
|
||||
}
|
||||
|
||||
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,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. 0001 -> read counter 0 and 1, 0100 -> 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,84 @@
|
||||
#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> ®ister_value,
|
||||
const SPIRegisterField &field,
|
||||
uint32_t field_value) {
|
||||
|
||||
// check that the field value can fit in the bitmask
|
||||
if (field_value > field.num_bits) {
|
||||
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;
|
||||
|
||||
register_value[byte_index] &=
|
||||
~mask; // clear the bit in the register value
|
||||
|
||||
register_value[byte_index] |= std::byte(
|
||||
((field_value >> i) & 0x1)
|
||||
<< bit_index); // set the new field value bit in the register value
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t inline getSPIRegisterField(
|
||||
const std::vector<std::byte> ®ister_value,
|
||||
const SPIRegisterField &field) {
|
||||
|
||||
uint32_t field_value = 0;
|
||||
constexpr uint8_t bits_per_byte = 8;
|
||||
|
||||
for (std::size_t i = 0; i < field.num_bits; ++i) {
|
||||
std::size_t offset = field.lsb_position + i;
|
||||
std::size_t byte_index = offset / bits_per_byte;
|
||||
std::size_t bit_index = offset % bits_per_byte;
|
||||
|
||||
std::byte mask = std::byte(1) << bit_index;
|
||||
|
||||
field_value |=
|
||||
(static_cast<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
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "ArmBusCommunication.hpp"
|
||||
#include "MemoryModel.hpp"
|
||||
#include "RegisterDefs.hpp"
|
||||
|
||||
/**
|
||||
@@ -7,7 +8,6 @@
|
||||
* @short contains specializations of the template classes for the Matterhorn
|
||||
* server implementation
|
||||
*/
|
||||
|
||||
namespace sls {
|
||||
|
||||
template <typename MemoryModel>
|
||||
@@ -20,7 +20,7 @@ struct IpCoreRegisterBlock<IPCore, MemoryModel> {
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<IPCore, MemoryModel> memoryblocks_{
|
||||
std::map<IPCore, MemoryModel> memoryblocks_{
|
||||
{IPCore::MH_RO_SM_AXI,
|
||||
MemoryModel{static_cast<uint32_t>(IPCore::MH_RO_SM_AXI),
|
||||
IPCORE_REGISTER_BLOCK_SIZE}},
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "SPICommunication.h"
|
||||
#include <fcntl.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace sls {
|
||||
|
||||
void HardwareSPICommunication::map_to_memory() {
|
||||
|
||||
// TODO device can change
|
||||
int spi_filedescriptor = open("/dev/spidev2.0", O_RDWR); // TODO use O_SYNC?
|
||||
|
||||
LOG(logINFO) << fmt::format("SPI Read: opened spidev2.0 with fd={}",
|
||||
spi_filedescriptor);
|
||||
|
||||
if (spi_filedescriptor < 0) {
|
||||
LOG(logERROR) << "Could not open /dev/spidev2.0";
|
||||
throw std::runtime_error("Could not open /dev/spidev2.0");
|
||||
}
|
||||
}
|
||||
|
||||
void HardwareSPICommunication::unmap_memory() {
|
||||
if (spi_filedescriptor >= 0) {
|
||||
close(spi_filedescriptor);
|
||||
LOG(logINFO) << "SPI Read: closed spidev2.0";
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
std::vector<std::byte> dummy_data(n_bytes + 1);
|
||||
|
||||
// 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}); // TODO: is it neccessary to initialize?
|
||||
|
||||
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
|
||||
// regitster
|
||||
send_cmd.cs_change = 1;
|
||||
|
||||
// transfer here
|
||||
if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) {
|
||||
|
||||
LOG(logERROR) << fmt::format("SPI write failed with {}:{}", errno,
|
||||
strerror(errno));
|
||||
throw std::runtime_error(
|
||||
fmt::format("SPI write failed with {}:{}", errno, strerror(errno)));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
LOG(logERROR) << fmt::format("SPI write failed with {}:{}", errno,
|
||||
strerror(errno));
|
||||
throw std::runtime_error(
|
||||
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(),
|
||||
reinterpret_cast<std::byte *>(send_cmd.tx_buf) + 1, n_bytes);
|
||||
|
||||
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();
|
||||
|
||||
// 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);
|
||||
|
||||
std::vector<std::byte> read_back_buffer(n_bytes +
|
||||
1); // TODO: do we need this?
|
||||
|
||||
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.rx_buf = reinterpret_cast<std::uintptr_t>(read_back_buffer.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) {
|
||||
|
||||
LOG(logERROR) << fmt::format("SPI write failed with {}:{}", errno,
|
||||
strerror(errno));
|
||||
throw std::runtime_error(
|
||||
fmt::format("SPI write failed with {}:{}", errno, strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sls
|
||||
@@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#include "fmt/format.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
@@ -30,22 +31,28 @@ class HardwareMemoryModel {
|
||||
|
||||
/// @brief class to handle memory mapping and access for virtual IP cores (e.g.
|
||||
/// use software implementation of memory)
|
||||
class VirtualMemoryModel {
|
||||
template <typename DataType> class VirtualMemoryModel {
|
||||
|
||||
public:
|
||||
VirtualMemoryModel(const uint32_t IPcore_base_address,
|
||||
const size_t size_memory_space_);
|
||||
const size_t size_memory_space_)
|
||||
: IPCore_base_address(IPcore_base_address),
|
||||
size_memory_space(size_memory_space_) {}
|
||||
|
||||
~VirtualMemoryModel() = default;
|
||||
|
||||
void mapToMemory();
|
||||
void mapToMemory() {
|
||||
mapped_memory.resize(
|
||||
size_memory_space /
|
||||
sizeof(DataType)); // TODO: should it be zero initialized?
|
||||
}
|
||||
|
||||
uint32_t *getMappedMemoryPtr();
|
||||
DataType *getMappedMemoryPtr() { return mapped_memory.data(); }
|
||||
|
||||
const uint32_t *getMappedMemoryPtr() const;
|
||||
const DataType *getMappedMemoryPtr() const { return mapped_memory.data(); }
|
||||
|
||||
private:
|
||||
std::vector<uint32_t> mapped_memory{};
|
||||
std::vector<DataType> mapped_memory{};
|
||||
|
||||
/// @brief offset of the IP core base address in the memory space, used for
|
||||
/// mapping
|
||||
|
||||
@@ -51,23 +51,3 @@ void HardwareMemoryModel::unmapMemory() {
|
||||
}
|
||||
|
||||
HardwareMemoryModel::~HardwareMemoryModel() { unmapMemory(); }
|
||||
|
||||
VirtualMemoryModel::VirtualMemoryModel(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 VirtualMemoryModel::mapToMemory() {
|
||||
|
||||
mapped_memory.resize(
|
||||
size_memory_space /
|
||||
sizeof(uint32_t)); // TODO: should it be zero initialized?
|
||||
}
|
||||
|
||||
uint32_t *VirtualMemoryModel::getMappedMemoryPtr() {
|
||||
return mapped_memory.data();
|
||||
}
|
||||
|
||||
const uint32_t *VirtualMemoryModel::getMappedMemoryPtr() const {
|
||||
return mapped_memory.data();
|
||||
}
|
||||
Reference in New Issue
Block a user