Skip to content
Snippets Groups Projects
Commit 7014c32c authored by Douglas Araujo's avatar Douglas Araujo
Browse files

Merge branch 'read_fen_regs' into 'main'

ICSHWI-14275: Create VMM FEN Epics module

See merge request !1
parents 03c48c9e f46c1c33
No related branches found
No related tags found
1 merge request!1ICSHWI-14275: Create VMM FEN Epics module
{
"deployment" : "Lab testbed setup at B02",
"version" : "1.0.0",
"notes" : "Testbed setup",
"topology": {
"0": {
"frontend" : "vmm_multiblade",
"nodes" : 1
},
"2": {
"frontend" : "ac701_testbed",
"nodes" : 1
}
},
"output_queues" : {
"default" : {
"enabled" : false,
"eth_src_mac" : "0e:05:05:00:00:04",
"eth_dst_mac" : "50:6b:4b:c0:b2:50",
"ip_src_addr" : "192.168.10.10",
"ip_dst_addr" : "192.168.10.100",
"udp_src_port" : 8997,
"udp_dst_port" : 9000,
"readout_type" : "0x48",
"pkt_gen_enable" : false,
"pkt_gen_idles" : "0x00000f",
"pkt_gen_length" : "0x000000ff"
},
"0": {
"enabled" : true,
"udp_dst_port" : 9000
},
"1": {
"enabled" : false,
"udp_dst_port" : 9001
},
"2": {
"enabled" : false,
"udp_dst_port" : 9002
},
"3": {
"enabled" : false,
"udp_dst_port" : 9003
}
}
}
\ No newline at end of file
require vmmTbl
epicsEnvSet("IPADDR", "192.168.50.2")
epicsEnvSet("IPPORT", "65535")
epicsEnvSet("P", "LAB:")
epicsEnvSet("R", "NDet-RMM-001:")
drvAsynIPPortConfigure("L0", "$(IPADDR):$(IPPORT) UDP", 0, 0, 1)
#asynSetTraceMask( L0, -1, 0x9)
#asynSetTraceIOMask( L0, -1, 0x4)
#asynSetTraceInfoMask ( L0, -1, 0x9)
RMMConfig("RMM", "L0", "/home/iocuser/douglas/fe-vmm-tbl/cmds/config_file/lab_testbed.json" 0)
VMMTblConfig("RMM", 0, 0)
dbLoadRecords("${asyn_DIR}/db/asynRecord.db","P=asyn:,R=Test,PORT=L0,ADDR=0,IMAX=0,OMAX=0")
dbLoadRecords($(rmm_DB)/rmm.db, "P=$(P), R=$(R), PORT=RMM, ADDR=0, TIMEOUT=1")
dbLoadRecords($(rmm_DB)/rmm_sensors.db, "P=$(P), R=$(R), PORT=RMM, TIMEOUT=1")
dbLoadRecords($(rmm_DB)/topology.db, "P=$(P), R=$(R), PORT=RMM, TIMEOUT=1")
dbLoadRecords("$(rmm_DB)/rmm_packets.template", "P=$(P), R=$(R), PORT=RMM, RING=0, NODE=0, ADDR=0, TIMEOUT=1")
dbLoadRecords("$(vmmTbl_DB)/vmm_tbl.db", "P=$(P), R=NDet-VMM-001:, PORT=RMM, RING=0, NODE=0, ADDR=0, TIMEOUT=1")
iocInit()
dbpf $(P)$(R)Temperature-R.SCAN 2
#dbpf $(P)$(R)ProcessPktCount-S.SCAN 2
dbpf $(P)$(R)VoltVCCINT-R.SCAN 2
dbpf $(P)$(R)VoltVCC1V8-R.SCAN 2
dbpf $(P)$(R)VoltVADJ1V8-R.SCAN 2
dbpf $(P)$(R)VoltVCC1V2-R.SCAN 2
dbpf $(P)$(R)VoltMGTAVCC-R.SCAN 2
dbpf $(P)$(R)VoltMGTAVTT-R.SCAN 2
dbpf $(P)$(R)VoltVCCINTIOBRAM-R.SCAN 2
#
\ No newline at end of file
record(mbbo, "$(P)$(R)AnalogMon-VMM0-S") {
field(DESC, "Select analog monitor (sm)")
field(ZRST, "Pulser DAC [mV]")
field(ZRVL, "0")
field(ONST, "Threshold DAC [mV]")
field(ONVL, "1")
field(TWST, "Bandgap ref [mV]")
field(TWVL, "2")
field(THST, "Temperature [degC]")
field(THVL, "3")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))SEL_ANALOG_MONITOR_VMM0")
}
record(mbbo, "$(P)$(R)AnalogMon-VMM1-S") {
field(DESC, "Select analog monitor (sm)")
field(ZRST, "Pulser DAC [mV]")
field(ZRVL, "0")
field(ONST, "Threshold DAC [mV]")
field(ONVL, "1")
field(TWST, "Bandgap ref [mV]")
field(TWVL, "2")
field(THST, "Temperature [degC]")
field(THVL, "3")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))SEL_ANALOG_MONITOR_VMM1")
}
record(bo, "$(P)$(R)ReadADC-VMMs-S") {
field(DESC, "Input signal")
field(VAL, "0")
field(DTYP, "asynInt32")
field(HIGH, "0.1")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))READ_ADC_VMMS")
}
record(ai, "$(P)$(R)MonValue-VMM0-RB") {
field(DESC, "Monitor Value Read")
field(DTYP, "asynFloat64")
field(SCAN, "I/O Intr")
field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ADC_VALUE_VMM0")
}
record(ai, "$(P)$(R)MonValue-VMM1-RB") {
field(DESC, "Monitor Value Read")
field(DTYP, "asynFloat64")
field(SCAN, "I/O Intr")
field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))ADC_VALUE_VMM1")
}
record(bo, "$(P)$(R)Acquire-S") {
field(DESC, "Start/stop FEN acquisition")
field(VAL, "0")
field(ZNAM, "Done")
field(ONAM, "Acquire")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))VMM_FEN_ACQUIRE")
}
#include "hybrid.h"
Hybrid::Hybrid() {
loadDefault();
}
Hybrid::~Hybrid() {}
void Hybrid::loadDefault(){
hybrid_registers = {{"TP_skew", 0}, {"TP_width", 0}, {"TP_pol", 0}};
hybrid_info = {{"firmware_version", ""}, {"geo_id", ""}, {"hybrid_id", ""},{"link_status", "0"}, {"description", ""}};
}
VMM3a& Hybrid::getVMM(int index) {
if (index < 0 || index >= VMMS_PER_HYBRID)
throw std::out_of_range("Invalid VMM index");
return vmms[index];
}
vmmStatus Hybrid::getRegister(std::string feature, unsigned short& result) {
vmmStatus status = vmmSuccess;
if(hybrid_registers.find(feature) != hybrid_registers.end())
result = hybrid_registers[feature];
else
status = vmmParamNotFound;
return status;
}
#pragma once
#include <string>
#include <map>
#include <iostream>
#include "vmm_params.h"
#include "vmm3a.h"
class Hybrid {
public:
Hybrid();
~Hybrid();
void loadDefault();
VMM3a& getVMM(int index);
vmmStatus getRegister(std::string feature, unsigned short& result);
private:
VMM3a vmms[VMMS_PER_HYBRID];
std::map<std::string, unsigned short> hybrid_registers;
std::map<std::string, std::string> hybrid_info;
};
#include "vmm3a.h"
// Define a map to store the valid range for each feature
std::map<std::string, std::pair<int, int>> featureRanges = {
{"sm5_sm0", {0, 67}},
{"sfam", {0, 1}},
{"st", {0, 3}},
{"sg", {0, 7}},
{"stc", {0, 3}},
{"sdt", {0, 1023}},
{"sdp10", {0, 1023}},
{"sc10b", {0, 3}},
{"sc8b", {0, 3}},
{"truncate_i", {0, 63}},
{"nskip_i", {0, 127}},
{"window_i", {0, 7}},
{"rollover_i", {0, 4095}},
{"L0offset_i", {0, 4095}},
{"offset_i", {0, 4095}}
};
std::map<std::string, std::pair<int, int>> featureChannelRanges = {
{"sd", {0, 31}},
{"sz10b", {0, 31}},
{"sz08b", {0, 15}},
{"sz06b", {0, 7}},
{"sc", {0, 1}},
{"sl", {0, 1}},
{"st", {0, 1}},
{"sth", {0, 1}},
{"sc8b", {0, 3}},
{"sm", {0, 1}},
{"smx", {0, 1}}
};
VMM3a::VMM3a(): vmm3aSettings(std::make_unique<VMM3aSettings>()) {
LoadDefault();
}
void VMM3a::LoadDefault() {
//VMM3a channel registers
for(int ch=0; ch<CHANNELS_PER_VMM; ch++){
vmm3aSettings->channels[ch] = {{"sc", 0}, {"sl", 0}, {"st", 0}, {"sth", 0}, {"sm", 0}, {"sd", 0}, {"smx", 0}, {"sz10b", 0}, {"sz08b", 0}, {"sz06b", 0} };
}
//Fill default values to map for Global Register
std::vector<std::string> reg_names = {
"sp", "sdp", "sbmx", "sbft", "sbfp",
"sbfm", "slg", "sm5_sm0", "scmx", "sfa",
"sfam", "st", "sfm", "sg", "sng",
"stot", "sttt", "ssh", "stc",
"sdt", "sdp10", "sc10b", "sc8b", "sc6b",
"s8b", "s6b", "s10b", "sdcks", "sdcka",
"sdck6b", "sdrv", "stpp",
"slvs", "s32", "stcr", "ssart", "srec",
"stlc", "sbip", "srat", "sfrst", "slvsbc",
"slvstp", "slvstk", "slvsdt", "slvsart", "slvstki",
"slvsena", "slvs6b", "sL0enaV", "slh", "slxh",
"stgc", "reset1", "reset2", "nskipm_i", "sL0cktest",
"sL0dckinv", "sL0ckinv", "sL0ena", "truncate_i",
"nskip_i", "window_i", "rollover_i", "L0offset_i", "offset_i"
};
for(const std::string& reg: reg_names) {
vmm3aSettings->globalRegisters.insert(std::pair<std::string, unsigned short>(reg, 0));
}
setRegister("sg", 2);//corrsponds to 3 mV/fC
setRegister("sm5_sm0", 67); //Temperature_sensor
setRegister("sdp10", 300);
setRegister("sdt",300);
setRegister("s10b",1);
setRegister("s8b",1);
setRegister("stc",0);
setRegister("sc8b",3);
setRegister("sc10b",3);
setRegister("srat",0);
setRegister("sbip",true);
setRegister("sbfm",true);
setRegister("sdcks",false);
}
const VMM3a::VMM3aSettings& VMM3a::getVMM3aSettings() const {
return *vmm3aSettings;
}
vmmStatus VMM3a::setRegister(std::string feature, int val) {
vmmStatus status = vmmSuccess;
status = updateRegisterMap(feature, val);
if (status == vmmParamNotFound)
std::cout << "ERROR register " << feature << " does not exist." << std::endl;
else if (status == vmmBadValue)
std::cout << "Value " << val << " is not valid to register: " << feature << std::endl;
return status;
}
vmmStatus VMM3a::setRegister(std::string feature, int val, int ch) {
vmmStatus status = vmmSuccess;
status = updateRegisterMap(feature, val, ch);
if (status == vmmParamNotFound)
std::cout << "ERROR register " << feature << " does not exist." << std::endl;
else if (status == vmmBadValue)
std::cout << "Value " << val << " is not valid to register: " << feature << std::endl;
else if (status == vmmChannelOutRange)
std::cout << "Channel " << ch << " out of the range (0 - 63)" << std::endl;
return status;
}
vmmStatus VMM3a::getRegister(std::string feature, unsigned short& result) const {
vmmStatus status = vmmSuccess;
if(vmm3aSettings->globalRegisters.find(feature)!=vmm3aSettings->globalRegisters.end())
result = vmm3aSettings->globalRegisters.at(feature);
else
status = vmmParamNotFound;
return status;
}
vmmStatus VMM3a::getRegister(std::string feature, int ch, unsigned short& result) const {
vmmStatus status = vmmSuccess;
if (ch >= 0 && ch < 64) {
auto it = vmm3aSettings->channels[ch].find(feature);
if (it != vmm3aSettings->channels[ch].end())
result = it->second;
else
status = vmmParamNotFound;
}
else status = vmmChannelOutRange;
return status;
}
vmmStatus VMM3a::updateRegisterMap(std::string feature, int value) {
vmmStatus status = vmmSuccess;
auto it = vmm3aSettings->globalRegisters.find(feature);
if (it != vmm3aSettings->globalRegisters.end()) {
auto range = featureRanges.find(feature);
if (range != featureRanges.end()) {
int minVal = range->second.first;
int maxVal = range->second.second;
if (feature == "sm5_sm0") {
if(value >= 0 && value <= 63) {
vmm3aSettings->globalRegisters.at("scmx") = 1;
vmm3aSettings->globalRegisters.at("sm5_sm0") = value;
} else if (value >= 64 && value <= 67) {
//Attention: Starts at 1: 1=Pulser_DAC, 2=Threshold_DAC, 3=Bandgap_reference, 4=Temperature_sensor
vmm3aSettings->globalRegisters.at("scmx") = 0;
vmm3aSettings->globalRegisters.at("sm5_sm0") = value - 63;
} else status = vmmBadValue;
}
else {
if (value >= minVal && value <= maxVal)
vmm3aSettings->globalRegisters.at(feature) = value;
else status = vmmBadValue;
}
}
// If not in the featureRanges, the param expects boolean value.
else {
if(value >= 0 && value <= 1)
vmm3aSettings->globalRegisters.at(feature) = value;
else status = vmmBadValue;
}
}
else { status = vmmParamNotFound; }
return status;
}
vmmStatus VMM3a::updateRegisterMap(std::string feature, int value, int ch) {
vmmStatus status = vmmSuccess;
if(ch >= 0 && ch < 64) {
auto range = featureChannelRanges.find(feature);
if (range != featureChannelRanges.end()) {
int minVal = range->second.first;
int maxVal = range->second.second;
if (value >= minVal && value <= maxVal)
vmm3aSettings->channels[ch][feature] = value;
else status = vmmBadValue;
}
else status = vmmParamNotFound;
}
else status = vmmChannelOutRange;
return status;
}
vmmStatus VMM3a::getInfo(const std::string& feature, std::string& result) {
vmmStatus status = vmmSuccess;
if (vmmInfo.find(feature) != vmmInfo.end())
result = vmmInfo[feature];
else
status = vmmParamNotFound;
return status;
}
vmmStatus VMM3a::setInfo(const std::string& feature, const std::string& value) {
vmmStatus status = vmmSuccess;
if (vmmInfo.find(feature) != vmmInfo.end())
vmmInfo[feature] = value;
else
status = vmmParamNotFound;
return status;
}
VMM3a::~VMM3a(){}
#pragma once
#include <string>
#include <map>
#include <vector>
#include <memory>
#include <iostream>
#include "vmm_params.h"
class VMM3a {
public:
VMM3a();
~VMM3a();
void LoadDefault();
struct VMM3aSettings{
std::map<std::string, unsigned short> channels[CHANNELS_PER_VMM];
std::map<std::string, unsigned short> globalRegisters;
};
const VMM3aSettings& getVMM3aSettings() const;
vmmStatus setRegister(std::string feature, int val);
vmmStatus setRegister(std::string feature, int val, int ch);
vmmStatus getRegister(std::string feature, unsigned short& result) const;
vmmStatus getRegister(std::string feature, int ch, unsigned short& result) const;
vmmStatus getInfo(const std::string& feature, std::string& result);
vmmStatus setInfo(const std::string& feature, const std::string& value);
private:
vmmStatus updateRegisterMap(std::string feature, int value);
vmmStatus updateRegisterMap(std::string feature, int value, int ch);
std::unique_ptr<VMM3aSettings> vmm3aSettings;
std::map<std::string, std::string> vmmInfo = {{"all_sc", "0"}, {"all_sl", "0"}, {"all_st", "0"}, {"all_sth", "0"},{"all_sm", "0"}, {"all_sd", "0"},{"all_smx", "0"},
{"all_sz10b", "0"},{"all_sz08b", "0"}, {"all_sz06b", "0"},{"adc_value", ""}, {"description",""}};
};
\ No newline at end of file
#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->user_reg_write("app_acq_enable", enabled);
pFEN->user_reg_write("sc_acq_on_off", 0x00000000);
pFEN->user_reg_write("sc_acq_on_off", 0x00000001);
pFEN->user_reg_write("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->user_reg_write("app_debug_data_format", 0);
pFEN->user_reg_write("app_latency_reset", 27);
pFEN->user_reg_write("app_latency_data_max", 1024);
pFEN->user_reg_write("app_latency_data_jitter", 0);
pFEN->user_reg_write("app_tp_offset_first", 100);
pFEN->user_reg_write("app_tp_offset", 1000);
pFEN->user_reg_write("app_tp_latency", 46);
pFEN->user_reg_write("app_tp_number", 1);
pFEN->user_reg_write("app_chmask", getChMap());
pFEN->user_reg_write("sc_cfg_app", 0x00000000);
pFEN->user_reg_write("sc_cfg_app", 0x00000001);
pFEN->user_reg_write("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->user_reg_write(reg + std::to_string(hybrid_index), result);
}
uint32_t value = (1 << hybrid_index);
pFEN->user_reg_write("sc_cfg_hyb", 0x0);
pFEN->user_reg_write("sc_cfg_hyb", value);
pFEN->user_reg_write("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->user_reg_write(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->user_reg_write(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->user_reg_write(param, value);
}
uint32_t value = (1 << idx);
pFEN->user_reg_write("sc_cfg_vmm", 0x00000000);
pFEN->user_reg_write("sc_cfg_vmm", value);
pFEN->user_reg_write("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->user_reg_write(command_reg, data);
data = (1 << index);
pFEN->user_reg_write(command_reg, data);
//Do not read registers back too soon, otherwise old ADC value is read
delayMilliseconds(7);
uint32_t value = pFEN->user_reg_read(read_reg);
std::cout << "value: " << value << std::endl;
pFEN->user_reg_write(command_reg, 0x0);
return value;
}
#pragma once
#include <vector>
#include <string>
#include <map>
#include <vector>
#include <memory>
#include <iostream>
#include "hybrid.h"
class FrontEndBase;
class FENConfigModule {
public:
FENConfigModule(FrontEndBase& frontend);
~FENConfigModule();
void configFEN();
void configHybrid(int hybrid_index);
bool configVMM(int hybrid_index, int vmm_index, bool enableConfigCheck=false);
void fillGlobalRegisters(std::vector<std::string>& global, int hybrid_index, int vmm_index);
void fillGlobalRegisters2(std::vector<std::string>& global, int hybrid_index, int vmm_index);
void fillChRegisters(std::vector<std::string>& registers, int hybrid_index, int vmm_index);
int readADC(int hybrid_index, int vmm_index);
Hybrid& getHybrid(int index);
bool enableHybrid(int hybrid, bool onOff);
bool isHybridEnabled(int hybrid);
void acquire(bool acquire);
private:
FrontEndBase *pFEN;
Hybrid hybrids[HYBRIDS_PER_FEN];
bool enabled_hybrids[HYBRIDS_PER_FEN] = {};
uint32_t readWriteRegs(std::string command_reg, int index, std::string read_reg);
uint16_t getChMap();
void checkLinkStatus(std::string& message, std::vector<std::string>& linkStatus);
void sendAll(bool useConfigCheck);
void enableAcquisition(bool enabled);
};
#pragma once
#define CHANNELS_PER_VMM 64
#define VMMS_PER_HYBRID 2
#define HYBRIDS_PER_FEN 5
typedef enum {
vmmSuccess,vmmTimeout,vmmParamNotFound,vmmBadValue,vmmChannelOutRange
}vmmStatus;
......@@ -3,6 +3,7 @@
/** Constructor for the VMMTbl class */
VMMTbl::VMMTbl(RMM& rmm, int ring, int node)
: FrontEndBase(rmm.get_rmm_reg_access(), ring, node),
pVMMConfig(*this),
pRMM(&rmm)
{
addr_list = ring * NUMBER_NODES_PER_RING + node;
......@@ -10,6 +11,7 @@ VMMTbl::VMMTbl(RMM& rmm, int ring, int node)
create_epics_params();
set_initial_epics_params();
pRMM->get_rmm_rings().set_topology(ring, node, "vmm-tbl");
pVMMConfig.enableHybrid(0, true);
}
void VMMTbl::config_FE() {
......@@ -21,6 +23,13 @@ void VMMTbl::set_initial_epics_params() {
}
asynStatus VMMTbl::create_epics_params() {
pRMM->createParam(addr_list, "SEL_ANALOG_MONITOR_VMM0", asynParamInt32, &vmmSelectMonitorVMM0_);
pRMM->createParam(addr_list, "SEL_ANALOG_MONITOR_VMM1", asynParamInt32, &vmmSelectMonitorVMM1_);
pRMM->createParam(addr_list, "READ_ADC_VMMS", asynParamInt32, &vmmProcessReadMonitorVMMs_);
pRMM->createParam(addr_list, "ADC_VALUE_VMM0", asynParamFloat64, &vmmMonitorValueVMM0_);
pRMM->createParam(addr_list, "ADC_VALUE_VMM1", asynParamFloat64, &vmmMonitorValueVMM1_);
pRMM->createParam(addr_list, "VMM_FEN_ACQUIRE", asynParamInt32, &vmmAcquire_);
return asynSuccess;
}
......@@ -30,6 +39,36 @@ asynStatus VMMTbl::writeInt32(asynUser *pasynUser, epicsInt32 value) {
int addr = 0;
pRMM->getAddress(pasynUser, &addr);
if ((value) && (function == vmmProcessReadMonitorVMMs_)) {
int adc_sensor = 0;
for (int vmm = 0; vmm <= 1; vmm++) {
pRMM->getIntegerParam(addr, vmmSelectMonitorVMM0_ + vmm, &adc_sensor);
std::cout << "adc_sensor" << adc_sensor << std::endl;
pVMMConfig.getHybrid(0).getVMM(vmm).setRegister("sm5_sm0", adc_sensor + 64);
pVMMConfig.configVMM(0, vmm);
int adc_read = pVMMConfig.readADC(0, vmm);
std::cout << "adc_read" << adc_read << std::endl;
if (adc_sensor == 3) {
double temperature = (725 - adc_read) / 1.85;
pRMM->setDoubleParam(addr, vmmMonitorValueVMM0_ + vmm, temperature);
}
else {
pRMM->setDoubleParam(addr, vmmMonitorValueVMM0_ + vmm, adc_read);
}
}
}
else if (function == vmmAcquire_) {
// enable test_pulse (st) of Hybrid 0, VMM 0, channel 0 to test purpose
// TODO: ADD PVs to enable/disable that
pVMMConfig.getHybrid(0).getVMM(0).setRegister("st", 1, 0);
if (value) pVMMConfig.acquire(true);
else pVMMConfig.acquire(false);
}
return status;
}
......
......@@ -3,6 +3,7 @@
#include "rmm.h"
#include "FrontEndBase.h"
#include "vmm_tbl_regs_map.h"
#include "vmm_config.h"
/** Class definition for the VMMTbl class */
class VMMTbl : public FrontEndBase {
......@@ -13,9 +14,17 @@ public:
virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
void set_initial_epics_params();
void config_FE();
protected:
int vmmSelectMonitorVMM0_;
int vmmSelectMonitorVMM1_;
int vmmProcessReadMonitorVMMs_;
int vmmMonitorValueVMM0_;
int vmmMonitorValueVMM1_;
int vmmAcquire_;
private:
FENConfigModule pVMMConfig;
RMM *pRMM;
int addr_list = 0;
};
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment