File utils.h
File List > midas_fe > utils.h
Go to the documentation of this file
#include "FEBSlowcontrolInterface.h"
#include "mutrig_config.h"
#include "Mutrig3Config.h"
#include "bits_utils.h"
uint32_t getODBIdx(midas::odb odb, std::string name) {
uint32_t idx = 0;
for (midas::odb& subkey : odb) {
if (false)
std::cout << subkey.get_name() << " = " << subkey << " name = " << name << std::endl;
if (subkey.get_name() == name)
break;
idx++;
}
return idx;
}
uint32_t getOffset(midas::odb odb, std::string name) {
uint32_t offset = 0;
for (midas::odb& subkey : odb) {
if (false)
std::cout << subkey.get_name() << " = " << subkey << " name = " << name << std::endl;
if (subkey.get_name() == name)
break;
offset += (uint32_t)subkey;
}
return offset;
}
void get_DACs_from_odb(midas::odb m_nbits, midas::odb m_config, uint8_t* bitpattern_w,
uint32_t asicIDx, std::string firstWord, std::string lastWord,
bool inverted) {
// NOTE: we assume here that the order of Nbits will not be changes
// TOOD: make nbits a struct and not something of the ODB
uint32_t idx = getODBIdx(m_nbits, firstWord);
uint32_t offset = getOffset(m_nbits, firstWord);
bool foundFirstWord = false;
for (midas::odb& subkey : m_nbits) {
if (subkey.get_name() == firstWord || foundFirstWord) {
foundFirstWord = true;
if (false)
std::cout << subkey.get_name() << " nbits:" << subkey
<< " value:" << m_config[subkey.get_name()][asicIDx]
<< " offset:" << offset << " idx:" << idx << std::endl;
offset = setParameter(bitpattern_w, m_config[subkey.get_name()][asicIDx], offset,
subkey, inverted);
}
if (subkey.get_name() == lastWord)
break;
}
}
void get_BiasDACs_from_odb(midas::odb m_config, uint8_t* bitpattern_w, uint32_t asicIDx) {
// TOOD: set first ("VNTimerDel") and last word ("Bandgap_on") as a const
get_DACs_from_odb(m_config["Nbits"], m_config["BIASDACS"], bitpattern_w, asicIDx, "VNTimerDel",
"Bandgap_on", true);
}
void get_ConfDACs_from_odb(midas::odb m_config, uint8_t* bitpattern_w, uint32_t asicIDx) {
// TOOD: set first ("SelFast") and last word ("ckdivend") as a const
get_DACs_from_odb(m_config["Nbits"], m_config["CONFDACS"], bitpattern_w, asicIDx, "SelFast",
"ckdivend", false);
}
void get_VDACs_from_odb(midas::odb m_config, uint8_t* bitpattern_w, uint32_t asicIDx) {
// TOOD: set first ("VCAL") and last word ("ref_Vss") as a const
get_DACs_from_odb(m_config["Nbits"], m_config["VDACS"], bitpattern_w, asicIDx, "VCAL",
"ref_Vss", false);
}
int InitFEBs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
// set FEB enable regs
feb_sc.FEBEnable();
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (FEBsIsMutrig) {
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_DP_REGISTER_W, 0x0FFFFFFF);
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_DUMMY_REGISTER_W, 0x0);
}
if (!FEBActive)
continue;
// set FPGA ID
feb_sc.FEB_write(febIDx, FPGA_ID_REGISTER_RW, febIDx);
vector<uint32_t> data(1);
feb_sc.FEB_read(febIDx, FPGA_ID_REGISTER_RW, data);
if ((febIDx & 0xffff) == (data[0] & 0xffff))
cm_msg1(MINFO, "quads", "InitFEBs()", "Successfully set FEBID of FEB %i to ID %i", febIDx,
febIDx);
feb_sc.FEB_write(febIDx, MP_LVDS_LINK_MASK_REGISTER_W,
(uint32_t)m_settings["DAQ"]["Links"]["LVDSLinkMask"][febIDx]);
feb_sc.FEB_write(
febIDx, MP_LVDS_LINK_MASK2_REGISTER_W,
(uint32_t)(((uint64_t)m_settings["DAQ"]["Links"]["LVDSLinkMask"][febIDx]) >> 32));
feb_sc.FEB_write(febIDx, MP_LVDS_INVERT_0_REGISTER_W,
(uint32_t)m_settings["DAQ"]["Links"]["LVDSLinkInvert"][febIDx]);
feb_sc.FEB_write(
febIDx, MP_LVDS_INVERT_1_REGISTER_W,
(uint32_t)(((uint64_t)m_settings["DAQ"]["Links"]["LVDSLinkInvert"][febIDx]) >> 32));
feb_sc.FEB_write(febIDx, MP_CTRL_SPI_ENABLE_REGISTER_W, 0x00000000);
feb_sc.FEB_write(febIDx, MP_CTRL_DIRECT_SPI_ENABLE_REGISTER_W, 0x00000000);
feb_sc.FEB_write(febIDx, MP_CTRL_SLOW_DOWN_REGISTER_W, 0x0000001F);
uint32_t delay = m_settings["Readout"]["Sorter Delay"][febIDx];
feb_sc.FEB_write(febIDx, SORTER_COUNTER_REGISTER_R + SORTER_INDEX_DELAY, delay);
// chip mapping
cm_msg1(MINFO, "quads", "InitFEBs()", "Set global ASIC ID mapping");
for ( uint32_t chipID = febIDx * N_CHIPS; chipID < (febIDx + 1) * N_CHIPS; chipID++ ) {
uint16_t globalChipID = (uint16_t) m_settings["DAQ"]["Links"]["Mapping"][chipID];
uint16_t localChipID = chipID % N_CHIPS;
cm_msg1(MINFO, "quads", "InitFEBs()", "Setup globalChipID-%i -> localASIC-%i on FEB-%i", globalChipID, localChipID, febIDx);
uint32_t command = (globalChipID << 9) | (0x1 << 7) | (febIDx << 4) | localChipID;
feb_sc.write_register(SWB_LOOKUP_CTRL_REGISTER_W, command);
//cout << hex << command << " i:" << hex << i << " v:" << hex << (uint16_t) asic_global_odb[i] << "FEB " << feb << endl;
}
}
feb_sc.write_register(SWB_LOOKUP_CTRL_REGISTER_W, 0x0);
return FE_SUCCESS;
}
int resetASICs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
cm_msg1(MINFO, "quads", "resetASICs()", "Reset all ASICs");
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
if (!FEBActive)
continue;
feb_sc.FEB_write(febIDx, MP_CTRL_RESET_REGISTER_W, 0x00000001);
sleep(2);
feb_sc.FEB_write(febIDx, MP_CTRL_RESET_REGISTER_W, 0x00000000);
}
return FE_SUCCESS;
}
int ConfigureASICs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings, uint8_t* bitpattern_w) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
uint16_t ASICMask = m_settings["DAQ"]["Links"]["ASICMask"][febIDx];
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsQuads = m_settings["DAQ"]["Links"]["FEBsQuads"][febIDx];
if (!FEBActive || !FEBsIsQuads)
continue;
for (uint32_t asicMaskIDx = febIDx * N_CHIPS; asicMaskIDx < (febIDx + 1) * N_CHIPS;
asicMaskIDx++) {
if (!((ASICMask >> (asicMaskIDx % N_CHIPS)) & 0x1))
continue;
cm_msg1(MINFO, "quads", "ConfigureASICs()",
"/Settings/Config/ -> globalASIC-%i -> localASIC-%i on FEB-%i", asicMaskIDx,
asicMaskIDx % N_CHIPS, febIDx);
get_BiasDACs_from_odb(m_settings["Config"], bitpattern_w, asicMaskIDx);
get_ConfDACs_from_odb(m_settings["Config"], bitpattern_w, asicMaskIDx);
get_VDACs_from_odb(m_settings["Config"], bitpattern_w, asicMaskIDx);
if (false)
for (int i = 0; i < 48; i++) printf("i: %i-v: %i\n", i, bitpattern_w[i]);
// get payload for configuration
std::vector<uint32_t> payload;
const uint32_t* bitpattern_ptr = reinterpret_cast<const uint32_t*>(bitpattern_w);
for (uint32_t i = 0; i < length_32bits; ++i) {
uint32_t reversed_word = 0;
for (int j = 0; j < 32; ++j)
reversed_word |= ((bitpattern_ptr[i] >> j) & 0b1) << (31 - j);
payload.push_back(reversed_word);
}
if (false)
for (int i = 0; i < payload.size(); i++) printf("i: %i-v: %i\n", i, payload[i]);
status = feb_sc.FEB_write(
febIDx, MP_CTRL_COMBINED_START_REGISTER_W + (asicMaskIDx % N_CHIPS), payload, true);
}
}
return status;
}
int ConfigureMuTRiGASICs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings, uint8_t* bitpattern_w) {
int status = FE_SUCCESS;
mutrig::Mutrig3Config config;
//Note: febIDx is global, corresponding to the QSFP port
//Note: febSSIDx is subsystem-centric and starts from zero, corresponding to an increasing number of FEBs for the subsystem without any offset.
//For the subsystem, i.e. indices of channels and asics within MutrigConfig, feb is used - so all channel numbers within mutrig start from zero
//
int32_t febSSIDx=-1;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
const uint16_t asicMask = m_settings["DAQ"]["Links"]["ASICMask"][febIDx];
if (!febIsMutrig) {
continue;
}
febSSIDx++;
if (!febActive) {
continue;
}
for (uint32_t localAsic = 0; localAsic < N_MUTRIGS_PER_FEB; ++localAsic) {
const uint32_t globalAsic = febSSIDx * N_MUTRIGS_PER_FEB + localAsic;
if (((asicMask >> localAsic) & 0x1u) == 0u) {
cm_msg1(MINFO, "quads", "ConfigureMuTRiGASICs",
"/Settings/ConfigMuTRiG -> globalASIC-%u -> localASIC-%u on FEB-%u --> SKIPPED",
globalAsic, localAsic, febIDx);
continue;
}
cm_msg1(MINFO, "quads", "ConfigureMuTRiGASICs",
"/Settings/ConfigMuTRiG -> globalASIC-%u -> localASIC-%u on FEB-%u",
globalAsic, localAsic, febIDx);
config.MapConfigFromDB(m_settings["ConfigMuTRiG"], globalAsic);
vector<vector<uint32_t>> payload;
payload.push_back(vector<uint32_t>(reinterpret_cast<uint32_t*>(config.bitpattern_w),reinterpret_cast<uint32_t*>(config.bitpattern_w)+config.length_32bits));
auto _status = feb_sc.FEBsc_NiosRPC(febIDx, CMD_MUTRIG_ASIC_CFG | (localAsic), payload );
if (_status != FE_SUCCESS) {
cm_msg1(MERROR, "quads", "ConfigureMuTRiGASICs",
"Failed to configure ASIC %u on FEB %u", localAsic, febIDx);
continue;
}
}
}
return status;
}
// int ConfigureMuTRiGASICs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings, uint8_t* bitpattern_w) {
// int status = FE_SUCCESS;
// // bind the generator to the ODB tree
// mutrig::ODBConfigGenerator::BoundGenerator cfg(m_settings["ConfigMuTRiG"]);
// // sanity check against constants.h
// if (!cfg.validate()) {
// cm_msg1(MERROR, "quads", "ConfigureMuTRiGASICs",
// "MuTRiG layout mismatch: ODB layout gives %zu bits / %zu bytes, "
// "but constants.h expects %u bits / %u bytes",
// cfg.total_bits(), cfg.total_bytes(),
// N_BITS_MUTRIG, N_BYTES_MUTRIG);
// return FE_ERR_HW;
// }
// for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
// const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
// const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
// const uint16_t asicMask = m_settings["DAQ"]["Links"]["ASICMask"][febIDx];
// if (!febActive || !febIsMutrig) {
// continue;
// }
// for (uint32_t localAsic = 0; localAsic < N_MUTRIGS_PER_FEB; ++localAsic) {
// if (((asicMask >> localAsic) & 0x1u) == 0u) {
// continue;
// }
// const uint32_t globalAsic = febIDx * N_MUTRIGS_PER_FEB + localAsic;
// cm_msg1(MINFO, "quads", "ConfigureMuTRiGASICs",
// "/Settings/ConfigMuTRiG -> globalASIC-%u -> localASIC-%u on FEB-%u",
// globalAsic, localAsic, febIDx);
// // build full MuTRiG config from ODB
// const auto bytes = cfg.generate(globalAsic);
// if (bytes.size() != N_BYTES_MUTRIG) {
// cm_msg1(MERROR, "quads", "ConfigureMuTRiGASICs",
// "Generated config size mismatch for ASIC %u: got %zu bytes, expected %u",
// globalAsic, bytes.size(), N_BYTES_MUTRIG);
// return FE_ERR_HW;
// }
// std::memcpy(bitpattern_w, bytes.data(), bytes.size());
// // Optional debug output
// if (false) {
// const std::string json = cfg.test_json(globalAsic);
// std::cout << json << std::endl;
// }
// // generate payload and send it to the mutrig
// vector<vector<uint32_t>> payload;
// payload.push_back(vector<uint32_t>(reinterpret_cast<uint32_t*>(bitpattern_w),reinterpret_cast<uint32_t*>(bitpattern_w)+bytes.size()/4));
// auto _status = feb_sc.FEBsc_NiosRPC(febIDx, CMD_MUTRIG_ASIC_CFG | (localAsic), payload );
// if (_status != FE_SUCCESS) {
// cm_msg1(MERROR, "quads", "ConfigureMuTRiGASICs",
// "Failed to configure ASIC %u on FEB %u", localAsic, febIDx);
// return status;
// }
// }
// }
// return status;
// }
int MuTRiG_reset_datapath(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!febIsMutrig || !febActive) {
continue;
}
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x2);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x0);
}
return status;
}
int MuTRiG_reset_asics(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!febIsMutrig || !febActive) {
continue;
}
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x1);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x0);
}
return status;
}
int MuTRiG_reset_lvds(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!febIsMutrig || !febActive) {
continue;
}
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x4);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x0);
}
return status;
}
int MuTRiG_reset_counters(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
const bool febActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
const bool febIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!febIsMutrig || !febActive) {
continue;
}
auto rpc_ret = feb_sc.FEBsc_NiosRPC(febIDx, CMD_MUTRIG_CNT_RESET, {});
}
return status;
}
int read_tdac_file(vector<uint32_t>& vec, std::string path) {
std::ifstream file;
file.open(path);
if (file.fail()) {
cm_msg1(MERROR, "quads", "read_tdac_file", "Could not find tdac file %s", path.c_str());
vec.clear();
for (int i = 0; i < 256 * 64; i++) {
vec.push_back(0x47474747); // has to be 47 as it send directly to the chip
return FE_SUCCESS;
}
} else {
vec.resize(256 * 64);
file.read(reinterpret_cast<char*>(&vec[0]), 256 * 64 * sizeof(uint32_t));
const uint32_t mask = 0x07070707;
for (int col = 0; col < 256; ++col) {
for (int row = 0; row < 62; ++row) {
uint32_t val = vec[col * 64 + row];
vec[col * 64 + row] = val ^ mask;
}
uint32_t val2 = vec[col * 64 + 62];
vec[col * 64 + 62] = val2 ^ 0x0707;
vec[col * 64 + 63] = 0xda00da00 | ((col & 0xff) << 16);
; // if read from file the last three values need to be inverted.
}
}
file.close();
return FE_SUCCESS;
}
int ConfigureTDACs(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
// reset ASICs before writing
resetASICs(feb_sc, m_settings);
// read tdac files
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
uint16_t ASICMask = m_settings["DAQ"]["Links"]["ASICMask"][febIDx];
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsQuads = m_settings["DAQ"]["Links"]["FEBsQuads"][febIDx];
if (!FEBActive || !FEBsIsQuads)
continue;
std::vector<std::vector<uint32_t>> tdac_page_this_feb;
for (uint32_t asicMaskIDx = febIDx * N_CHIPS; asicMaskIDx < (febIDx + 1) * N_CHIPS;
asicMaskIDx++) {
if (!((ASICMask >> (asicMaskIDx % N_CHIPS)) & 0x1))
continue;
cm_msg1(MINFO, "quads", "WriteTDACs()",
"/Settings/Config/ -> globalASIC-%i -> localASIC-%i on FEB-%i", asicMaskIDx,
asicMaskIDx % N_CHIPS, febIDx);
// read TDAC file
// TODO: we only read the first TDAC file, this is hardcoded change me
std::string path = m_settings["Config"]["TDACS"]["TDACFILE"][asicMaskIDx];
std::vector<uint32_t> tdac_chip(64 * 256);
read_tdac_file(tdac_chip, "/home/mu3e/musip/online/userfiles/maskfiles/" + path);
// write TDAC
uint32_t N_DCOLS_PER_PAGE = 8;
uint32_t PAGESIZE2 = 128 * N_DCOLS_PER_PAGE;
uint8_t N_PAGES_PER_CHIP = 128 / N_DCOLS_PER_PAGE;
uint8_t pages_remaining = N_PAGES_PER_CHIP;
while (pages_remaining > 0) {
std::vector<uint32_t> free_pages = {0};
feb_sc.FEB_read(febIDx, MP_CTRL_N_FREE_PAGES_REGISTER_R, free_pages);
// printf("free_pages: %d %d\n", free_pages[0], N_PAGES_PER_CHIP-pages_remaining);
if (free_pages[0] == 0)
continue;
std::vector<uint32_t> tdac_page(PAGESIZE2);
uint8_t current_page = N_PAGES_PER_CHIP - pages_remaining;
tdac_page =
std::vector<uint32_t>(tdac_chip.begin() + current_page * PAGESIZE2,
tdac_chip.begin() + (current_page + 1) * PAGESIZE2);
feb_sc.FEB_write(febIDx, MP_CTRL_TDAC_START_REGISTER_W + (asicMaskIDx % N_CHIPS),
tdac_page, true, false);
pages_remaining--;
}
}
}
cm_msg1(MINFO, "quads", "ConfigureTDACs", "tdac write completed");
return FE_SUCCESS;
}
int ConfigureInjectASICs(FEBSlowcontrolInterface& feb_sc, const std::vector<uint8_t>& inj_col,
const std::vector<uint8_t>& inj_row) {
// Commands vector, same command to all asics
vector<uint32_t> commands(2 * N_FEBS_QUAD * N_CHIPS_MAX);
const uint32_t command_wr_test = 0b101100; // Write Command Test register
const uint32_t command_ld_test = 0b001101; // Load Command Test register
const uint32_t command_inject = 0b001000; // Command to inject ONCE
// Generic Command word variable
uint64_t ScCommand = 0xf;
uint32_t highBits = (ScCommand >> 32);
uint32_t lowBits = (ScCommand & 0xffffffff);
const uint8_t col_register_size = 7; // 7 bit
const uint8_t col_register_mask = 0x7F; // 7 bit
vector<uint8_t> TEST_register(128, 0); // all ZEROES
// From trial and error the bit order seems to get reversed somewhere along the chain. So
// "Enable Injection Column" (InjEn in internal Note 0052) is bit 1 and "Enable Injection
// Row<0>", "Enable Injection Row<1>" (EnInjRow<0>, EnInjRow<1> in Note 0052) are bits 6 and 5
// respectively (counting from zero).
for (auto icol : inj_col) {
TEST_register.at(icol / 2) = TEST_register.at(icol / 2) | (0x1 << 1);
}
// Convert Adresses to Enable Signals
for (auto irow : inj_row) {
if ((irow / 2) < TEST_register.size()) {
if (irow % 2 == 0)
TEST_register.at(irow / 2) = TEST_register.at(irow / 2) | (0x1 << 6);
else if (irow % 2 == 1)
TEST_register.at(irow / 2) = TEST_register.at(irow / 2) | (0x1 << 5);
}
}
// Convert Test register to payload for Commands
uint8_t register_counter = 0;
const uint8_t payload_size = 54;
const uint32_t total_register_size = 896; // 7 bits * 128 columns;
// Create the payload vector the correct size and fill with zeros
std::vector<uint64_t> TEST_register_payloads((total_register_size - 1) / payload_size + 1, 0);
// register length and payload quantisation do not need to align --> zero padding at the
// beginning might be needed
uint32_t zero_padding_size = payload_size - (total_register_size % payload_size);
// ugly bit gymnastics ahead
for (int32_t i = TEST_register.size() - 1; i >= 0; --i, ++register_counter) {
const unsigned total_bit_position =
col_register_size * register_counter + zero_padding_size;
const unsigned bit_position_this_payload = total_bit_position % payload_size;
const unsigned payload_counter = total_bit_position / payload_size;
TEST_register_payloads.at(payload_counter) |=
(uint64_t(TEST_register.at(i) & col_register_mask) << bit_position_this_payload);
if ((bit_position_this_payload + col_register_size) > payload_size) {
// This register stretches in to the next payload, so add the those left over bits now.
const unsigned bit_overflow_count =
bit_position_this_payload + col_register_size - payload_size;
TEST_register_payloads.at(payload_counter + 1) |=
(TEST_register.at(i) >> (col_register_size - bit_overflow_count));
// Also mask away the extra bits we wrote into the high bits of this payload. Not
// strictly necessary (they'll be shifted away when the payload gets sent) but it's
// cleaner for comparison checks.
TEST_register_payloads.at(payload_counter) &=
0x003FFFFFFFFFFFFF; // Only keep the first payload_size bits
}
}
// Sending Test register Payload and Load register
// Write Register
for (auto p : TEST_register_payloads) {
ScCommand = (p << 10) | ((command_wr_test & 0x3F) << 4);
highBits = (ScCommand >> 32);
lowBits = (ScCommand & 0xffffffff);
for (int i = 0; i < 2 * N_FEBS_QUAD * N_CHIPS_MAX; i += 2) {
commands[i] = lowBits;
commands[i + 1] = highBits;
}
feb_sc.FEB_broadcast(MP_CTRL_EXT_CMD_START_REGISTER_W, commands);
}
// Load Register
ScCommand = (0x300 << 10) | ((command_ld_test & 0x3F) << 4);
highBits = (ScCommand >> 32);
lowBits = (ScCommand & 0xffffffff);
for (int i = 0; i < 2 * N_FEBS_QUAD * N_CHIPS_MAX; i += 2) {
commands[i] = lowBits;
commands[i + 1] = highBits;
}
feb_sc.FEB_broadcast(MP_CTRL_EXT_CMD_START_REGISTER_W, commands);
return FE_SUCCESS;
}
int InjectASICs(FEBSlowcontrolInterface& feb_sc, uint32_t injection_pulse_duration) {
// Commands vector, same command to all asics
std::vector<uint32_t> commands(2 * N_FEBS_QUAD * N_CHIPS_MAX);
const uint32_t command_wr_test = 0b101100; // Write Command Test register
const uint32_t command_ld_test = 0b001101; // Load Command Test register
const uint32_t command_inject = 0b001000; // Command to inject ONCE
const uint64_t ScCommand =
((injection_pulse_duration & 0x3FF) << 10) | ((command_inject & 0x3F) << 4);
const uint32_t highBits = (ScCommand >> 32);
const uint32_t lowBits = (ScCommand & 0xffffffff);
for (int i = 0; i < 2 * N_FEBS_QUAD * N_CHIPS_MAX; i += 2) {
commands[i] = lowBits;
commands[i + 1] = highBits;
}
feb_sc.FEB_broadcast(MP_CTRL_EXT_CMD_START_REGISTER_W, commands);
return FE_SUCCESS;
}
int InjectASICsInLoop(FEBSlowcontrolInterface& feb_sc, uint32_t injection_pulse_duration,
uint32_t num_repetitions, uint32_t wait_between_pulses) {
const auto pulseWaitTime = std::chrono::milliseconds(wait_between_pulses);
for (uint32_t i = 0; i < num_repetitions; i++) {
// This method can lock the frontend for quite a long time, depending on the parameters
// passed. So Print a status report every now and then.
// if((i % 10) == 9) cm_msg1(MINFO, "quads", "InjectASICsInLoop" , "Sending injection trigger %u of
// %u\n", i + 1, num_repetitions);
int result = InjectASICs(feb_sc, injection_pulse_duration);
if (result != FE_SUCCESS)
return result;
// Here we sleep because the injection circuit has to recharge (10 ms seems to work)
std::this_thread::sleep_for(pulseWaitTime);
}
return FE_SUCCESS;
}
int FullChipInjection(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings, uint8_t min_columns,
uint8_t max_columns, uint8_t min_rows, uint8_t max_rows,
uint32_t injection_pulse_duration, uint32_t num_repetitions,
uint32_t wait_between_pulses) {
std::vector<uint8_t> columns(2);
std::vector<uint8_t> rows(3);
constexpr auto waitTimeAfterConfigure = std::chrono::milliseconds(1);
// --- Remainder (overshoot) handling ---
// length of the scanned ranges
const uint8_t col_range = max_columns - min_columns;
const uint8_t row_range = max_rows - min_rows;
// Double modulo to stick with uint8
uint8_t r_column = ((col_range % 4) + 1) % 4;
uint8_t r_row = ((row_range % 3) + 1) % 3;
// --- Main regular scan (no overshoot) ---
// loop will break if next update will overshoot
for (uint8_t c = min_columns; c <= max_columns - 3 && c <= 255 - 3; c += 4) {
for (uint8_t r = min_rows; r <= max_rows - 2 && r <= 249 - 2; r += 3) {
// ---- Fill columns ----
for (uint8_t index = 0; index < columns.size(); ++index) columns[index] = c + 2 * index;
// ---- Fill rows ----
for (uint8_t index = 0; index < rows.size(); ++index) rows[index] = r + index;
// ---- PRINT BEFORE CONFIGURE ----
uint8_t col_start = columns.front();
uint8_t col_end = columns.back();
uint8_t row_start = rows.front();
uint8_t row_end = rows.back();
// ---- Actual injection ----
ConfigureInjectASICs(feb_sc, columns, rows);
std::this_thread::sleep_for(waitTimeAfterConfigure);
InjectASICsInLoop(feb_sc, injection_pulse_duration, num_repetitions,
wait_between_pulses);
if (r >= 247)
break;
}
if (c >= 252)
break;
if (!m_settings["DAQ"]["Commands"]["Full chip Injection"]) {
return FE_SUCCESS;
}
}
// Computes the tail columns
std::vector<uint8_t> columns_tail;
if (r_column == 1) {
columns_tail = {max_columns};
}
if (r_column == 2) {
columns_tail = {static_cast<unsigned char>(max_columns - 1)};
}
else if (r_column == 2) {
columns_tail = {static_cast<uint8_t>(max_columns - 2), max_columns};
}
// Computes the tail rows
std::vector<uint8_t> rows_tail;
if (r_row == 1) {
rows_tail = {max_rows};
} else if (r_row == 2) {
rows_tail = {static_cast<uint8_t>(max_rows - 1), max_rows};
}
// ---------- 1) Right strip: leftover columns, full row blocks ----------
if (r_column != 0) {
for (uint8_t r = min_rows; r <= max_rows - 2 && r <= 249 - 2; r += 3) {
std::vector<uint8_t> rows_block{r, static_cast<uint8_t>(r + 1),
static_cast<uint8_t>(r + 2)};
ConfigureInjectASICs(feb_sc, columns_tail, rows_block);
std::this_thread::sleep_for(waitTimeAfterConfigure);
InjectASICsInLoop(feb_sc, injection_pulse_duration, num_repetitions,
wait_between_pulses);
if (r >= 247)
break;
}
}
// ---------- 2) Bottom strip: leftover rows, full column blocks ----------
if (r_row != 0) {
for (uint8_t c = min_columns; c <= max_columns - 3 && c <= 255 - 3; c += 4) {
std::vector<uint8_t> columns_block{c, static_cast<uint8_t>(c + 2)};
ConfigureInjectASICs(feb_sc, columns_block, rows_tail);
std::this_thread::sleep_for(waitTimeAfterConfigure);
InjectASICsInLoop(feb_sc, injection_pulse_duration, num_repetitions,
wait_between_pulses);
if (c >= 252)
break;
}
}
// ---------- 3) Bottom-right corner: leftover rows AND columns ----------
if (r_column != 0 && r_row != 0) {
ConfigureInjectASICs(feb_sc, columns_tail, rows_tail);
std::this_thread::sleep_for(waitTimeAfterConfigure);
InjectASICsInLoop(feb_sc, injection_pulse_duration, num_repetitions, wait_between_pulses);
}
return FE_SUCCESS;
}
uint64_t calculateADCCommand(ADC_Command adcCommand, uint16_t adcDivisionFactor, ADC_Mode adcMode) {
constexpr uint64_t SteerADC = 0b100000;
return ((static_cast<uint64_t>(adcMode) & 0xf) << 24) |
(static_cast<uint64_t>(adcDivisionFactor & 0x03ff) << 14) |
((static_cast<uint64_t>(adcCommand) & 0xf) << 10) | (SteerADC << 4);
}
void sendCommand(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings, uint64_t command) {
uint32_t highBits = (command >> 32);
uint32_t lowBits = (command & 0xffffffff);
vector<uint32_t> commands(2 * N_FEBS_QUAD * N_CHIPS_MAX);
for (int i = 0; i < 2 * N_FEBS_QUAD * N_CHIPS_MAX; i += 2) {
commands[i] = lowBits;
commands[i + 1] = highBits;
}
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsQuads = m_settings["DAQ"]["Links"]["FEBsQuads"][febIDx];
if (!FEBActive || !FEBsIsQuads)
continue;
feb_sc.FEB_write(febIDx, MP_CTRL_EXT_CMD_START_REGISTER_W, commands);
}
// TODO: Test how short this can be, maybe even remove completely
constexpr auto waitTimeBetweenWrites = std::chrono::milliseconds(10);
std::this_thread::sleep_for(waitTimeBetweenWrites);
}
void adcContinuousReadout(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
sendCommand(feb_sc, m_settings, calculateADCCommand(ADC_Command::Reset, 0x3f0, ADC_Mode::All));
sendCommand(feb_sc, m_settings,
calculateADCCommand(ADC_Command::Configure, 0x3f0, ADC_Mode::All));
sendCommand(feb_sc, m_settings,
calculateADCCommand(ADC_Command::Measure, 0x3f0, ADC_Mode::All));
}
uint32_t generate_random_pixel_hit_swb(uint32_t time_stamp) {
uint32_t tot = rand() % 32; // 0 to 31
uint32_t chipID = rand() % 3; // 0 to 2
uint32_t col = rand() % 256; // 0 to 256
uint32_t row = rand() % 250; // 0 to 250
uint32_t hit = (time_stamp << 28) | (chipID << 22) | (row << 14) | (col << 6) | (tot << 1);
return hit;
}
int create_dummy_event(uint32_t* dma_buf_dummy, size_t eventSize, int nEvents, int serial_number) {
for (int i = 0; i < nEvents; i++) {
// event header
dma_buf_dummy[0 + i * eventSize] = 0x00000001; // Trigger Mask & Event ID
dma_buf_dummy[1 + i * eventSize] = serial_number++; // Serial number
dma_buf_dummy[2 + i * eventSize] = ss_time(); // time
dma_buf_dummy[3 + i * eventSize] = eventSize * 4 - 4 * 4; // event size
dma_buf_dummy[4 + i * eventSize] = eventSize * 4 - 6 * 4; // all bank size
dma_buf_dummy[5 + i * eventSize] = 0x31; // flags
// bank DHPS -- hits
dma_buf_dummy[6 + i * eventSize] =
'D' << 0 | 'H' << 8 | 'P' << 16 | 'S' << 24; // bank name
dma_buf_dummy[7 + i * eventSize] = 0x06; // bank type TID_DWORD
dma_buf_dummy[8 + i * eventSize] = 10 * 4; // data size
dma_buf_dummy[9 + i * eventSize] = 0x0; // reserved
dma_buf_dummy[10 + i * eventSize] = 0x00000000; // hit0
dma_buf_dummy[11 + i * eventSize] = generate_random_pixel_hit_swb(ss_time()); // hit0
dma_buf_dummy[12 + i * eventSize] = 0x00000000; // hit1
dma_buf_dummy[13 + i * eventSize] = generate_random_pixel_hit_swb(ss_time()); // hit1
dma_buf_dummy[14 + i * eventSize] = 0x00000000; // hit2
dma_buf_dummy[15 + i * eventSize] = generate_random_pixel_hit_swb(ss_time()); // hit2
dma_buf_dummy[16 + i * eventSize] = 0x00000000; // hit3
dma_buf_dummy[17 + i * eventSize] = generate_random_pixel_hit_swb(ss_time()); // hit3
dma_buf_dummy[18 + i * eventSize] = 0x00000000; // hit4
dma_buf_dummy[19 + i * eventSize] = generate_random_pixel_hit_swb(ss_time()); // hit5
// bank DSIN second FEB
dma_buf_dummy[20 + i * eventSize] =
'D' << 0 | 'S' << 8 | 'I' << 16 | 'N' << 24; // bank name
dma_buf_dummy[21 + i * eventSize] = 0x6; // bank type TID_DWORD
dma_buf_dummy[22 + i * eventSize] = 8 * 4; // data size
dma_buf_dummy[23 + i * eventSize] = 0x0; // reserved
dma_buf_dummy[24 + i * eventSize] = 0xE80001BC; // preamble
dma_buf_dummy[25 + i * eventSize] = serial_number++; // TS0
dma_buf_dummy[26 + i * eventSize] = 0x0000 & serial_number & 0xFFFF; // TS1
dma_buf_dummy[27 + i * eventSize] = 0xFC000000; // DS0
dma_buf_dummy[28 + i * eventSize] = 0xFC000000; // DS1
dma_buf_dummy[29 + i * eventSize] = 0x00000000; // stuff
dma_buf_dummy[30 + i * eventSize] = 0xAFFEAFFE; // PADDING
dma_buf_dummy[31 + i * eventSize] = 0xAFFEAFFE; // PADDING
}
sleep(1);
return serial_number;
}
int ChangeTDCTest(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
bool setting = m_settings["DAQ"]["Commands"]["MuTRiG"]["TestPulsesTDC"];
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!FEBActive || !FEBsIsMutrig)
continue;
uint16_t command = CMD_TILE_INJECTION_SETTING | setting;
auto _status = feb_sc.FEBsc_NiosRPC(febIDx, command, {});
char reportStr[255];
sprintf(reportStr,
"%s Injection Setting of FEB:%i: to 0x%16.16lx",
(_status == FEBSlowcontrolInterface::ERRCODES::OK ? "Successfully updated" : "Failed to update"),
febIDx,
static_cast<unsigned long>(setting)
);
cm_msg1(MINFO, "quads", "ChangeTDCTest()", "%s", reportStr);
if(_status != FEBSlowcontrolInterface::ERRCODES::OK) status = _status;
}
return status;
}
int TBinit(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!FEBActive || !FEBsIsMutrig)
continue;
status = feb_sc.FEBsc_NiosRPC(febIDx, CMD_TILE_TMB_INIT, {});
if(status != FEB_REPLY_SUCCESS) {
cm_msg1(
MERROR,
"Quads",
"TMBinit()",
"Failed to initialize Testboards on FEB:%i\n",
febIDx
);
continue;
}
char reportStr[255];
sprintf(reportStr,
"%s initialized TMB of FEB:%i",
(status == FEB_REPLY_SUCCESS? "Successfully " : "Failed to "),
febIDx
);
cm_msg1(MINFO, "quads", "TMBinit()", "%s", reportStr);
}
//Update Inject as well to stay consistent with ODB
ChangeTDCTest(feb_sc, m_settings);
return status;
}
int UpdatePowerOverride(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
int febIDx = m_settings["DAQ"]["Commands"]["MuTRiG"]["override_power_moduleid"];
uint32_t m_override_dig_power_mask = m_settings["DAQ"]["Commands"]["MuTRiG"]["override_dig_power_mask"];
uint32_t m_override_ana_power_mask = m_settings["DAQ"]["Commands"]["MuTRiG"]["override_ana_power_mask"];
if(febIDx < 0 || febIDx > N_FEBS ){
cm_msg1(
MINFO,
"Quads",
"UpdatePowerOverride()",
"ID out of bounds (0 <= %i < %i)",
febIDx,
N_FEBS
);
return status;
}
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!FEBActive || !FEBsIsMutrig) {
cm_msg1(
MERROR,
"Quads",
"UpdatePowerOverride()",
"Failed to update power ASIC setting on TMB of FEB:%i active:%i mutrig:%i",
febIDx,
FEBActive,
FEBsIsMutrig
);
return status;
}
status = feb_sc.FEBsc_NiosRPC(febIDx, CMD_TILE_ASIC_PWROR, { { m_override_ana_power_mask, m_override_dig_power_mask } });
if(status != FEB_REPLY_SUCCESS) {
cm_msg1(
MERROR,
"Quads",
"UpdatePowerOverride()",
"Failed to update power ASIC setting on TMB of FEB:%i to %12.12x || %12.12x",
febIDx,
m_override_ana_power_mask,
m_override_dig_power_mask
);
} else {
cm_msg1(
MINFO,
"Quads",
"UpdatePowerOverride()",
"Update power ASIC setting on TMB of FEB:%i to %12.12x || %12.12x",
febIDx,
m_override_ana_power_mask,
m_override_dig_power_mask
);
}
return status;
}
int UpdatePower(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
//Note: Indices below correspond to subdetector module numbers starting from zero
//uint32_t m_override_dig_power_mask = m_settings["DAQ"]["Commands"]["MuTRiG"]["override_dig_power_mask"];
//uint32_t m_override_ana_power_mask = m_settings["DAQ"]["Commands"]["MuTRiG"]["override_ana_power_mask"];
std::vector<bool> m_module_power_mask = m_settings["DAQ"]["Commands"]["MuTRiG"]["module_power_mask"];
bool m_module_power = m_settings["DAQ"]["Commands"]["MuTRiG"]["module_power"];
//Note: febIDx is global, corresponding to the QSFP port
//Note: febSSIDx is subsystem-centric and starts from zero, corresponding to an increasing number of FEBs for the subsystem without any offset.
//For the subsystem, i.e. indices of channels and asics within MutrigConfig, feb is used - so all channel numbers within mutrig start from zero
//
int32_t febSSIDx=-1;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
uint16_t ASICMask = m_settings["DAQ"]["Links"]["ASICMask"][febIDx];
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!FEBsIsMutrig)
continue;
febSSIDx++;
if (!FEBActive)
continue;
if(m_module_power == false)
ASICMask = 0;
if(m_module_power_mask[febSSIDx] == false)
ASICMask = 0;
status = feb_sc.FEBsc_NiosRPC(febIDx, CMD_TILE_ASIC_PWR, { { ASICMask } });
if(status != FEB_REPLY_SUCCESS) {
cm_msg1(
MERROR,
"Quads",
"UpdatePower()",
"Failed to update power ASIC setting on TMB of FEB:%i to %12.12x",
febIDx,
ASICMask
);
} else {
cm_msg1(
MINFO,
"Quads",
"UpdatePower()",
"Update power ASIC setting on TMB of FEB:%i to %12.12x",
febIDx,
ASICMask
);
}
}
return status;
}
int MuTRiGResetLVDSAddr(FEBSlowcontrolInterface& feb_sc, midas::odb m_settings) {
int status = FE_SUCCESS;
for (uint32_t febIDx = 0; febIDx < m_settings["DAQ"]["Links"]["FEBsActive"].size(); febIDx++) {
bool FEBActive = m_settings["DAQ"]["Links"]["FEBsActive"][febIDx];
bool FEBsIsMutrig = m_settings["DAQ"]["Links"]["FEBsMutrig"][febIDx];
if (!FEBActive || !FEBsIsMutrig)
continue;
status = feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x20);
status = feb_sc.FEB_write(febIDx, MUTRIG_CTRL_RESET_REGISTER_W, 0x0);
}
return status;
}
int to_signed_12b(uint32_t i){ if ((i & 0x800 ) != 0){ i = 0x7ff & (~i); return -i;} else return 0x7ff&i;}
int to_signed_16b(uint32_t i){ if ((i & 0x8000) != 0){ i = 0x7fff & (~i); return -i-1;} else return 0x7fff&i;}