Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
vmm_config.cpp 14.22 KiB
#include "vmm_config.h"
#include <sstream>
#include <bitset>
#include <algorithm>
#include <chrono>
#include <thread>
#include <array>

#include "FrontEndBase.h"

void delayMilliseconds(int milliseconds) {
  std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}

FENConfigModule::FENConfigModule(FrontEndBase& frontend) : pFEN(&frontend) {
}

FENConfigModule::~FENConfigModule() {
}

void FENConfigModule::acquire(bool acquire) {
  std::string message;
  std::vector<std::string> linkStatus;
  if (acquire) {
    enableAcquisition(false);
    sendAll(true);
    enableAcquisition(true);
    checkLinkStatus(message, linkStatus);
  } else {
    enableAcquisition(false);
    checkLinkStatus(message, linkStatus);
  }
}

void FENConfigModule::sendAll(bool useConfigCheck) {
  configFEN();
  for(int hybrid = 0; hybrid <= HYBRIDS_PER_FEN; hybrid++) {
    if (isHybridEnabled(hybrid)) {
      configHybrid(hybrid);

      for(int vmm = 0; vmm < VMMS_PER_HYBRID; vmm++) {
        configVMM(hybrid, vmm, useConfigCheck);
      }
    }
  }
}

void FENConfigModule::enableAcquisition(bool enabled) {
  pFEN->userRegWrite("app_acq_enable", enabled);
  pFEN->userRegWrite("sc_acq_on_off", 0x00000000);
  pFEN->userRegWrite("sc_acq_on_off", 0x00000001);
  pFEN->userRegWrite("sc_acq_on_off", 0x00000000);
}

void FENConfigModule::checkLinkStatus(std::string& message, std::vector<std::string>& linkStatus) {
  linkStatus.clear();
  for (int n = 0; n < HYBRIDS_PER_FEN; n++) linkStatus.push_back("0");

  uint32_t result = readWriteRegs("sc_app_link_status", 0, "app_link_status");

  std::stringstream ss;

  for (int n = 0; n < HYBRIDS_PER_FEN; n++) {
    if (isHybridEnabled(n)) {
      linkStatus[n] = std::to_string(result);
      ss << "hybr. " + std::to_string(n) + ": " + linkStatus[n] << std::endl;
    }
  }
  message = ss.str();
  std::cout << "Message: " << message << std::endl;
}

void FENConfigModule::configFEN() {
  pFEN->userRegWrite("app_debug_data_format", 0);
  pFEN->userRegWrite("app_latency_reset", 27);
  pFEN->userRegWrite("app_latency_data_max", 1024);
  pFEN->userRegWrite("app_latency_data_jitter", 0);
  pFEN->userRegWrite("app_tp_offset_first", 100);
  pFEN->userRegWrite("app_tp_offset", 1000);
  pFEN->userRegWrite("app_tp_latency", 46);
  pFEN->userRegWrite("app_tp_number", 1);
  pFEN->userRegWrite("app_chmask", getChMap());
  pFEN->userRegWrite("sc_cfg_app", 0x00000000);
  pFEN->userRegWrite("sc_cfg_app", 0x00000001);
  pFEN->userRegWrite("sc_cfg_app", 0x00000000);
}

uint16_t FENConfigModule::getChMap() {
  std::string chMapString = "0000000000000000";
  for (int hybrid = 0; hybrid < HYBRIDS_PER_FEN; hybrid++) {
    if (isHybridEnabled(hybrid)) {
      for (int vmm = 0; vmm < VMMS_PER_HYBRID; vmm++) {
        chMapString.replace(15 - (hybrid * 2 + vmm), 1, "1");
      }
    }
  }
  uint16_t chMap = std::stoi(chMapString, nullptr, 2);
  return chMap;
}

bool FENConfigModule::enableHybrid(int hybrid, bool onOff) {
  if(hybrid < HYBRIDS_PER_FEN) {
    enabled_hybrids[hybrid] = onOff;
    return true;
  } else return false;
}

bool FENConfigModule::isHybridEnabled(int hybrid) {
  if(hybrid < HYBRIDS_PER_FEN) return enabled_hybrids[hybrid];
  else return false;
}

Hybrid& FENConfigModule::getHybrid(int index) {
  if (index < 0 || index >= HYBRIDS_PER_FEN)
    throw std::out_of_range("Invalid hybrid index");
  return hybrids[index];
}

void FENConfigModule::configHybrid(int hybrid_index) {
  std::array<std::string, 3> registers = {"hyb_tp_skew0", "hyb_tp_width0", "hyb_tp_polarity0"};

  for (const auto& reg : registers) {
    unsigned short result = 0;
    hybrids[hybrid_index].getRegister(reg, result);
    pFEN->userRegWrite(reg + std::to_string(hybrid_index), result);
  }

  uint32_t value = (1 << hybrid_index);
  pFEN->userRegWrite("sc_cfg_hyb", 0x0);
  pFEN->userRegWrite("sc_cfg_hyb", value);
  pFEN->userRegWrite("sc_cfg_hyb", 0x0);
}

bool FENConfigModule::configVMM(int hybrid_index, int vmm_index, bool enableConfigCheck)
{
  bool result = true;
  bool ok;

  std::vector<std::string> globalRegisters;
  globalRegisters.clear();
  fillGlobalRegisters(globalRegisters, hybrid_index,  vmm_index);
  
  std::cout << "Global Registers:" << std::endl;
    for (const auto& reg : globalRegisters) {
      std::cout << reg << std::endl;
    }
  
  if(globalRegisters.size()!=3){
    std::cout << "ERROR Global SPI does not have 3 words" << std::endl;
    return -1;
  }

  std::vector<std::string> channelRegisters;
  channelRegisters.clear();
  fillChRegisters(channelRegisters, hybrid_index,  vmm_index);
  if(channelRegisters.size() != 64){
    std::cout << "ERROR Channel registers do not have 64 values" << std::endl;
    return -1;
  }

  std::vector<std::string> globalRegisters2;
  globalRegisters2.clear();
  fillGlobalRegisters2(globalRegisters2, hybrid_index,  vmm_index);
  if(globalRegisters2.size()!=3){
      std::cout << "ERROR Global SPI does not have 3 words" << std::endl;
      return -1;
  }

  int idx = hybrid_index * 2 + vmm_index;
  std::string sVmmIndex = "99";
  if(idx < 10) sVmmIndex = "0" + std::to_string(idx);
  else if(idx < 16) sVmmIndex = std::to_string(idx);
  
  bool g_use_config_check = true;
  if(g_use_config_check) {
    //reset I2C address 65 register 0
    if(enableConfigCheck) {
      // TODO: Check with we need that (the enableConfigCheck is always false in the VMM code) 
      //uint32_t value = (1 << hybrid_index);
      //ESS_WriteSc("sc_i2c_reset_config_check", 0x00000000, ok);
      //ESS_WriteSc("sc_i2c_reset_config_check", value, ok);
      //ESS_WriteSc("sc_i2c_reset_config_check", 0x00000000, ok);
    }
  }

  unsigned int firstGlobalRegSPI_2 = 0;
  unsigned int lastGlobalRegSPI_2 = 2;
  unsigned int firstGlobalRegSPI_1 = 67;
  unsigned int lastGlobalRegSPI_1 = 69;

  // global SPI / VMM3: global bank 0
  for(unsigned int i = firstGlobalRegSPI_2; i <= lastGlobalRegSPI_2; i++) {
    std::string param = "vmm_global_bank2_sp" + std::to_string(i - firstGlobalRegSPI_2) + sVmmIndex;
    uint32_t value = std::stoul(globalRegisters2[i - firstGlobalRegSPI_2], nullptr, 2);
    pFEN->userRegWrite(param, value);
  }

  //channel SPI
  for(unsigned int i = 0; i < CHANNELS_PER_VMM; i++) {
    std::string position_str = std::to_string(i);
    if (position_str.size() == 1) position_str = "0" + position_str;
    std::string param = "vmm_ch" + position_str + sVmmIndex;
    uint32_t value = std::stoul(channelRegisters[i], nullptr, 2);
    std::cout << "param: " << param << std::endl;
    pFEN->userRegWrite(param, value);
  }

  // global SPI / VMM3: global bank 1
  for(unsigned int i = firstGlobalRegSPI_1; i <= lastGlobalRegSPI_1; i++) {
    std::string param = "vmm_global_bank1_sp" + std::to_string(i - firstGlobalRegSPI_1) + sVmmIndex;
    uint32_t value = std::stoul(globalRegisters[i - firstGlobalRegSPI_1], nullptr, 2);
    pFEN->userRegWrite(param, value);
  }

  uint32_t value = (1 << idx);
  pFEN->userRegWrite("sc_cfg_vmm", 0x00000000);
  pFEN->userRegWrite("sc_cfg_vmm", value);
  pFEN->userRegWrite("sc_cfg_vmm", 0x00000000);
  
  return result;
}

void FENConfigModule::fillGlobalRegisters(std::vector<std::string>& global, int hybrid_index, int vmm_index)
{
  unsigned short result;

  global.clear();
  int sequence = 0;
  
  // GLOBAL SPI 0
  std::string spi0 = "00000000000000000000000000000000";

  //[0,3] reserved
  sequence += 4;

  // Function to replace a bit in the spi0 string with a value from VMM settings
  auto replaceVMMBit = [&](int index, const std::string& settingName) {
    // Convert value to '1' if it's not zero, otherwise '0'
    hybrids[hybrid_index].getVMM(vmm_index).getRegister(settingName, result);
    spi0[index] = '0' + (result != 0); 
  };

  const std::vector<std::string> settings = {"slvs", "s32", "stcr", "ssart", "srec", "stlc", "sbip", "srat", "sfrst",
                                            "slvsbc", "slvstp", "slvstk", "slvsdt", "slvsart", "slvstki", "slvsena",
                                            "slvs6b", "slh", "slxh", "stgc", "reset1", "reset2"};

  for (const auto& setting : settings) {
    if (setting == "reset1") sequence += 5; //[25,29] not used
    replaceVMMBit(sequence, setting);
    sequence++;
  }

  global.push_back(spi0);

  // GLOBAL SPI 1
  // Define a function to populate spi string based on VMM settings
  auto populateSPI1 = [&](const std::vector<std::pair<std::string, int>>& settings) {
    std::string spi;
    int sequence = 0;
    for (const auto& setting : settings) {
      hybrids[hybrid_index].getVMM(vmm_index).getRegister(setting.first, result);
      std::string binary_string = std::bitset<16>(result).to_string().substr(16 - setting.second);

      if (setting.first == "sdt") {
        // Populate threshold DAC lowest 6 bits
        for (int i = 4; i < 10; ++i) spi += binary_string[i];
      } else spi += binary_string;
      sequence += setting.second;
    }

    // Append '0' to the end of spi to reserve the last bit: [31] reserved
    spi += '0';

    // Push back the populated spi string to the global vector
    global.push_back(spi);
  };

  // Populate SPI 1 settings
  populateSPI1({{"sdt", 10},{"sdp10", 10}, {"sc10b", 2}, {"sc8b", 2},
                {"sc6b", 3}, {"s8b", 1}, {"s6b", 1}, {"s10b", 1},
                {"sdcks", 1}, {"sdcka", 1}, {"sdck6b", 1}, {"sdrv", 1},
                {"stpp", 1}});

  // GLOBAL SPI 2
  // Define a function to populate spi string based on VMM settings
  auto populateSPI2 = [&](const std::vector<std::pair<std::string, int>>& settings) {
    std::string spi;
    int sequence = 0;

    for (const auto& setting : settings) {
      hybrids[hybrid_index].getVMM(vmm_index).getRegister(setting.first, result);
      std::string binary_string = std::bitset<16>(result).to_string().substr(16 - setting.second);
      if (setting.first == "sdt") {
        // Populate threshold DAC highest 4 bits
        for (int i = 0; i < 4; ++i) {
          spi += binary_string[i];
          sequence++;
        }
      } else {
        spi += binary_string;
        sequence += setting.second;
      }
    }
    // Push back the populated spi string to the global vector
    global.push_back(spi);
  };

  // Populate SPI 2 settings
  populateSPI2({{"sp", 1}, {"sdp", 1}, {"sbmx", 1}, {"sbft", 1}, {"sbfp", 1},
                {"sbfm", 1}, {"slg", 1}, {"sm5_sm0", 6}, {"scmx", 1}, {"sfa", 1},
                {"sfam", 1}, {"st", 2}, {"sfm", 1}, {"sg", 3}, {"sng", 1}, {"stot", 1},
                {"sttt", 1}, {"ssh", 1}, {"stc", 2}, {"sdt", 10}});

}

void FENConfigModule::fillChRegisters(std::vector<std::string>& registers, int hybrid_index, int vmm_index)
{
  registers.clear();
  unsigned short result;

  auto populateRegisters = [&](const std::vector<std::pair<std::string, int>>& regs, int channel) {
    std::string _reg = "00000000";
    int sequence = 8;

    for (const auto& reg : regs) {
      hybrids[hybrid_index].getVMM(vmm_index).getRegister(reg.first, channel, result);
      std::string binary_string = std::bitset<16>(result).to_string().substr(16 - reg.second);
      if (reg.first == "sd" || reg.first == "sz10b" ||
          reg.first == "sz08b" || reg.first == "sz06b") {
        //According to ATLAS software, has to be reversed
        std::reverse(binary_string.begin(), binary_string.end());
      }
      _reg += binary_string;
      sequence += reg.second;
    }

    // Append '0' to the end of spi to reserve the last bit: [31] reserved
    _reg += "0";

    // Push back the populated _reg string to the registers vector
    registers.push_back(_reg);
  };

  std::vector<std::pair<std::string, int>> registers_map = {{"sc", 1}, {"sl", 1}, {"st", 1},
                                                        {"sth", 1}, {"sm", 1}, {"smx", 1},
                                                        {"sd", 5}, {"sz10b", 5}, {"sz08b", 4},
                                                        {"sz06b", 3}};

  for(int ch = 0; ch < CHANNELS_PER_VMM; ch++) {
    populateRegisters(registers_map, ch);
  }
}

void FENConfigModule::fillGlobalRegisters2(std::vector<std::string>& global, int hybrid_index, int vmm_index)
{
  unsigned short result;
  int sequence = 0;

  // GLOBAL SPI 0
  std::string spi0 = "00000000000000000000000000000000";
  //[0,30] not used
  sequence+=31;

  // magic number on BCID [31]
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("nskipm_i", result);
  spi0.replace(sequence, 1, std::to_string(result));
  
  global.push_back(spi0);

  // GLOBAL SPI 1
  std::string spi1 = "00000000000000000000000000000000";
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sL0cktest", result);
  // clocks when L0 core disabled [0]
  spi1.replace(sequence, 1, std::to_string(result));
  sequence++;

  //invert DCK [1]
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sL0dckinv", result);
  spi1.replace(sequence, 1, std::to_string(result));
  sequence++;

  //invert BCCLK [2]
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sL0ckinv", result);
  spi1.replace(sequence, 1, std::to_string(result));
  sequence++;

  //L0 core
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sL0ena", result);
  spi1.replace(sequence, 1, std::to_string(result));
  sequence++;

  global.push_back(spi1);

  // GLOBAL SPI 2
  std::string spi2 = "00000000000000000000000000000000";
  global.push_back(spi2);
}

int FENConfigModule::readADC(int hybrid_index, int vmm_index) {
  int adc_result = 0;
  int vmm = hybrid_index * 2 + vmm_index;
  std::string sVmmIndex = "99";
  if(vmm < 10) sVmmIndex = "0" + std::to_string(vmm);
  else if(vmm < 16) sVmmIndex = std::to_string(vmm);

  uint32_t result = readWriteRegs("sc_i2c_vmm_adc", vmm, "vmm_i2c_adc" + sVmmIndex);
  adc_result = (result & 0xFFFF) >> 4; // bit shift

  unsigned short sp_value = 0;
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sp", sp_value);
  unsigned sm5_sm0_value = 0;
  hybrids[hybrid_index].getVMM(vmm_index).getRegister("sm5_sm0", sp_value);

  if((sp_value != 0) && (sm5_sm0_value == 1)) adc_result = 1200 - adc_result;

  return adc_result;
}

uint32_t FENConfigModule::readWriteRegs(std::string command_reg, int index, std::string read_reg) {
  unsigned int data = 0x00000000;
  pFEN->userRegWrite(command_reg, data);
  data = (1 << index);
  pFEN->userRegWrite(command_reg, data);

  //Do not read registers back too soon, otherwise old ADC value is read
  delayMilliseconds(7);

  uint32_t value = pFEN->userRegRead(read_reg);
  std::cout << "value: " << value << std::endl;
  pFEN->userRegWrite(command_reg, 0x0);

  return value;
}