#include "RCUControlEngine.h" RCUControlEngine::RCUControlEngine(){ } RCUControlEngine::~RCUControlEngine(){ } uint32_t RCUControlEngine::sendRCU_EXEC(){ // Send the execution command to run the sequence written to rcu instruction memory return RCU_EXEC; } uint32_t RCUControlEngine::sendRCU_STOP_EXEC(){ // Stop the execution return RCU_STOP_EXEC; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_STORE(uint32_t wordCount){ // Store Altro instructions in FeeServer memory. Use RCU_ALTRO_INSTRUCTION_EXECUTE // to actually write to Altros. Command assume payload is in pairs of 32-bit words, // where first word is
and second is . // Parameter: number of 32 bit words // Payload: 32 bit data, count specified by parameter uint32_t retval; retval = RCU_ALTRO_INSTRUCTION_STORE + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_INSTRUCTION(uint32_t wordCount){ // Write to rcu instruction memory // Parameter: number of 32 bit words // Payload: 32 bit data, count specified by parameter uint32_t retval; retval = RCU_WRITE_INSTRUCTION + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_EXEC_INSTRUCTION(uint32_t wordCount){ // Write to rcu instruction memory and send the execution command // Parameter: number of 32 bit words // Payload: 32 bit data, count specified by parameter uint32_t retval; retval = RCU_EXEC_INSTRUCTION + (wordCount&ci16BitOn); return retval; } /* uint32_t RCUControlEngine::sendRCU_WRITE_PATTERN8(uint32_t wordCount){ // Write 8 bit data to rcu pattern memory // Parameter: number of 8 bit words // (parameter + 3)/4 (data block has to be aligned to 4) uint32_t retval; retval = RCU_WRITE_PATTERN8 + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_PATTERN16(uint32_t wordCount){ // Write 16 bit data to rcu pattern memory // Parameter: number of 16 bit words // (parameter + 1)/2 (data block has to be aligned to 4) uint32_t retval; retval = RCU_WRITE_PATTERN16 + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_PATTERN32(uint32_t wordCount){ // Write 32 bit data to rcu pattern memory // Parameter: number of 32 bit words // Payload: 32 bit data, count specified by parameter uint32_t retval; retval = RCU_WRITE_PATTERN32 + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_PATTERN10(uint32_t wordCount){ // Write 10 bit compressed data to rcu pattern memory, // each 32-bit word of the data block contains 3 10-bit words // Parameter: number of 10 bit words // (parameter + 2)/3 (data block has to be aligned to 4) uint32_t retval; retval = RCU_WRITE_PATTERN10 + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_READ_PATTERN(uint32_t wordCount){ // Read from rcu pattern memory // Parameter: number of 32-bit words to read uint32_t retval; retval = RCU_READ_PATTERN + (wordCount&ci16BitOn); return retval; } */ uint32_t RCUControlEngine::sendRCU_READ_INSTRUCTION(uint32_t wordCount){ // Read from rcu instruction memory // Parameter: number of 32-bit words to read uint32_t retval; retval = RCU_READ_INSTRUCTION + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_READ_MEMORY(uint32_t address){ // Read from rcu memory location // Parameter: address in the RCU memory space uint32_t retval; retval = RCU_READ_MEMORY + (address&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_MEMORY(uint32_t address){ // Write to rcu memory location // Parameter: address in the RCU memory space // Payload: one 32 bit word uint32_t retval; retval = RCU_WRITE_MEMORY + (address&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_RESULT(uint32_t wordCount){ // Write to rcu result memory // Parameter: number of 32-bit words to write // Payload: 32 bit data, count specified by parameter uint32_t retval; retval = RCU_WRITE_RESULT + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_READ_RESULT(uint32_t wordCount){ // Read from rcu result memory // Parameter: number of 32-bit words to read uint32_t retval; retval = RCU_READ_RESULT + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_MEMBLOCK(uint32_t wordCount){ // Write to rcu memory - the first word specifies the address, data words are // following // Parameter: number of 32-bit words to write // Payload: one 32 bit address followd by 32 bit data, count specified by parameter uint32_t retval; retval = RCU_WRITE_MEMBLOCK + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_READ_MEMBLOCK(uint32_t wordCount){ // Read from rcu memory - the word in the data block is treated as address in RCU // memory space // Parameter: number of 32-bit words to read // Payload: one 32 bit address uint32_t retval; retval = RCU_READ_MEMBLOCK + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_EXECUTE() { // Write Altro instructions stored by RCU_ALTRO_INSTRUCTION_STORE to Altros. return RCU_ALTRO_INSTRUCTION_EXECUTE; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_CLEAR() { // Clear altro instructions that are stored return RCU_ALTRO_INSTRUCTION_CLEAR; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_WRITE_FILE() { // Write Altro instructions stored to file. return RCU_ALTRO_INSTRUCTION_WRITE_FILE; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_READ_FILE() { // Read Altro instructions from file. return RCU_ALTRO_INSTRUCTION_READ_FILE; } uint32_t RCUControlEngine::sendRCU_ALTRO_INSTRUCTION_FILE_NAME(uint32_t wordCount) { // Set the file name to be used for storing IMEM instructions. uint32_t retval; retval = RCU_ALTRO_INSTRUCTION_FILE_NAME + (wordCount&ci16BitOn); return retval; } /* uint32_t RCUControlEngine::sendRCU_READ_ERRST(){ // Read Error Status register return RCU_READ_ERRST; } */ uint32_t RCUControlEngine::sendRCU_WRITE_TRGCFG(){ // Write to TRCFG register // Payload: one 32bit word (only bit 0 to 18 valid for write) return RCU_WRITE_TRGCFG; } uint32_t RCUControlEngine::sendRCU_READ_TRGCFG(){ // Read TRCFG register return RCU_READ_TRGCFG; } uint32_t RCUControlEngine::sendRCU_WRITE_PMCFG(){ // Write to PMCFG register return RCU_WRITE_PMCFG; } uint32_t RCUControlEngine::sendRCU_READ_PMCFG(){ // Read PMCFG register return RCU_READ_PMCFG; } /* uint32_t RCUControlEngine::sendRCU_CHECK_ERROR(){ // Send ...??? return RCU_CHECK_ERROR; } uint32_t RCUControlEngine::sendRCU_EN_AUTO_CHECK(bool autoCheckState){ // Send ...??? uint32_t retval; retval = RCU_EN_AUTO_CHECK + (autoCheckState&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_SET_ABM(uint32_t busMasterDevice){ // Set the Altro Bus Master // parameter: 0 DDL SIU, 1 DCS board return (RCU_SET_ABM + (busMasterDevice&ci16BitOn)); } uint32_t RCUControlEngine::sendRCU_SET_DDL_ABM(){ // Set the Altro Bus Master to DDL return RCU_SET_ABM; } uint32_t RCUControlEngine::sendRCU_SET_DCS_ABM(){ // Set the Altro Bus Master to DCS return (RCU_SET_ABM+1); } uint32_t RCUControlEngine::sendRCU_CHECK_VERSION(uint32_t version){ // Send ...??? uint32_t retval; retval = RCU_CHECK_VERSION + (version&ci16BitOn); return retval; } */ uint32_t RCUControlEngine::sendRCU_WRITE_FPGA_CONF(){ // Write a configuration to the RCU FPGA return RCU_WRITE_FPGA_CONF; } uint32_t RCUControlEngine::sendRCU_READ_FPGA_CONF(){ // Read the configuration of the RCU FPGA return RCU_READ_FPGA_CONF; } uint32_t RCUControlEngine::sendRCU_WRITE_FLASH(){ // Write a file to the Flash return RCU_WRITE_FLASH; } /* uint32_t RCUControlEngine::sendRCU_READ_FLASH(){ // Write a file to the Flash return RCU_READ_FLASH; } */ uint32_t RCUControlEngine::sendRCU_REG_CFG_STORE(uint32_t wordCount) { // Store register configuration in FeeServer memory. Use REG_CFG_EXECUTE // to actually write to hardware. Command assume payload is in pairs of 32-bit words, // where first word is
and second is . // parameter: number of 32 bit words in the payload (should be even number!) // payload: 32 bit word address/data pairs uint32_t retval; retval = REG_CFG_STORE + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_REG_CFG_EXECUTE() { // Write register configuration stored by REG_CFG_STORE to hardware. // parameter: none // payload: none return REG_CFG_EXECUTE; } uint32_t RCUControlEngine::sendRCU_REG_CFG_EXECUTE_SINGLE() { // Write register configuration stored by REG_CFG_STORE to hardware for single register. // parameter: none // payload: 32 bit word register address to execute return REG_CFG_EXECUTE_SINGLE; } uint32_t RCUControlEngine::sendRCU_REG_CFG_CLEAR() { // Clear register configuration stored by REG_CFG_STORE. // parameter: none // payload: none return REG_CFG_CLEAR; } uint32_t RCUControlEngine::sendRCU_REG_CFG_WRITE_FILE() { // Copy register confiuration stored by REG_CFG_STORE to file. // parameter: none // payload: none return REG_CFG_WRITE_FILE; } uint32_t RCUControlEngine::sendRCU_REG_CFG_READ_FILE() { // Copy register configuration stored in file by REG_CFG_WRITE_FILE back to map. // parameter: none // payload: none return REG_CFG_READ_FILE; } uint32_t RCUControlEngine::sendRCU_REG_CFG_FILE_NAME(uint32_t wordCount) { // Set the file name to be used for storing REG_CFG instructions. uint32_t retval; retval = REG_CFG_FILE_NAME + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_CONFGFEC(){ // Turn off the FECs for 4us. return RCU_CONFGFEC; } uint32_t RCUControlEngine::sendRCU_RESET(uint32_t parameter){ // Reset the Rcu // Parameter: 1 = FEC_RESET, 2 = RCU_RESET, GLB_RESET otherwise uint32_t retval; retval = RCU_RESET + (parameter&ci02BitOn); return retval; } uint32_t RCUControlEngine::sendGLB_RESET(){ // Global Reset return RCU_RESET; } uint32_t RCUControlEngine::sendRCU_RESET(){ // Reset only RCU uint32_t retval; retval = RCU_RESET + 0x2; return retval; } uint32_t RCUControlEngine::sendFEC_RESET(){ // Reset all FECs uint32_t retval; retval = RCU_RESET + 0x1; return retval; } uint32_t RCUControlEngine::sendRCU_L1TRG_SELECT(uint32_t parameter) { // Enable trigger source // Parameter: 1 = L1_TTC, 2 = L1_I2C, L1_CMD otherwise // This command serves the L1_CMD, L1_TTC and L1_I2C operations of the RCU firmware uint32_t retval; retval = RCU_L1TRG_SELECT + (parameter&ci02BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_L1_TTC() { // Send SELECT L1_TTC Trigger source uint32_t retval; retval = RCU_L1TRG_SELECT + 0x1; return retval; } uint32_t RCUControlEngine::sendRCU_L1_I2C() { // Send SELECT L1_I2C Trigger source uint32_t retval; retval = RCU_L1TRG_SELECT + 0x2; return retval; } uint32_t RCUControlEngine::sendRCU_L1_CMD() { // Send SELECT L1_CMD Trigger source return RCU_L1TRG_SELECT; } uint32_t RCUControlEngine::sendRCU_SYNC_SCLK_L1TTC() { // Re-synchronize the SCLK with the next L1TTC trigger. return RCU_SYNC_SCLK_L1TTC; } uint32_t RCUControlEngine::sendRCU_WRITE_AFL() { // write the AFL register of the RCU. // The value is masked with the bit pattern of the valid FECs, i.e. FECs // defined by the BranchLayout of the active RCUControlEngine. To override // the masking use the @ref RCU_WRITE_MEMORY command. // There has to be a payload: 32bit data (only bit 0 to 28 valid). return RCU_WRITE_AFL; } uint32_t RCUControlEngine::sendRCU_READ_AFL() { // Read the AFL register of the RCU. // Result: 32 bit register content return RCU_READ_AFL; } uint32_t RCUControlEngine::sendRCU_WRITE_ACL(uint32_t wordCount) { // Write to rcu ACL memory. // Parameter: number of 32 bit words in the payload // Payload: Number of 32 bit data words return RCU_WRITE_ACL + (wordCount&ci16BitOn); } uint32_t RCUControlEngine::sendRCU_READ_ACL(uint32_t wordCount) { // Read from rcu ACL memory. // Parameter: number of 32 bit words to read // No Payload. return RCU_READ_ACL + (wordCount&ci16BitOn); } uint32_t RCUControlEngine::sendRCU_WRITE_EN_INT_BA() { // Enables the RCU to switch off the FEC branch in case the interrupt signals // Parameter: ignored // Payload: value to be written to register, i.e. 0, 1, 2 or 3 return RCU_WRITE_EN_INT_BA; } /* uint32_t RCUControlEngine::sendRCU_READ_EN_INT_BA() { // READ the RCU EN_INT_BA register // Read the AFL register of the RCU. // Result: Register content return RCU_READ_EN_INT_BA; } */ uint32_t RCUControlEngine::sendFEE_CONFIGURE(uint32_t wordCount){ // A configure command for the FEE. // The command encapsulates and arbitrary sequence of other commands. // The layout of registers, the memory sizes aso might be different between RCU // firmware versions. In order to adapt the CE to a firmware // version, different code books are used to define the memory and register // layout. Currently, there is no version number available in the RCU firmware // and the CE is therefor "hard-wired" to a certain firmware version. // // Parameter: number of words inside the block after the checksum, i.e. the // the total number of (including FEE_VERIFICATION, FEE_CONFIGURE_END) // Payload: // - 32 bit HW address, related to the ALTRO addressing // - 32 bit checksum for this configuration command // - first command of the block // - ... // - last configuration command of the block // - FEE_VERIFICATION command // - FEE_CONFIGURE_END command // // The FEE_VERIFICATION command can be empty but is mandatory. // The FEE_CONFIGURE_END command signals the end of the configuration for // a device/partition. uint32_t retval; retval = FEE_CONFIGURE + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_NOSTATE(uint32_t wordCount){ // Same as the @ref FEE_CONFIGURE command, but will not trigger any change of state. uint32_t retval; retval = FEE_CONFIGURE_NOSTATE + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_32(){ // Same as the FEE_CONFIGURE command, but size of block is stored in first // word of data block rather than in parameter. To be used if more than 16 bit // payload size is needed. // Payload: // - 32 bit size of data block // - 32 bit HW address, related to the ALTRO addressing // - 32 bit checksum for this configuration command // - first command of the block // - ... // - ... uint32_t retval; retval = FEE_CONFIGURE_32; return retval; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_32_NOSTATE(){ // Same as the @ref FEE_CONFIGURE_32 command, but will not trigger any change of state. uint32_t retval; retval = FEE_CONFIGURE_32_NOSTATE; return retval; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_1Word(uint32_t configID, uint32_t device, uint32_t altroHWaddress){ // First word to follow the FEE_CONFIGURE command. Contains the HW address: // 32 bit HW address, related to the ALTRO addressing // bit 11-0 ALTRO address // bit 15-12 Device: 0 - CE, 1 - RCU, 2 - FEC, 3 - Altro, 4 - Channel // bit 31-16 configuration id to distinguish blocks to the same device/partition uint32_t retval; retval = ((configID&ci16BitOn)<<16) + ((device&ci04BitOn)<<12) + (altroHWaddress&ci12BitOn); return retval; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_END(){ // Signal the end of the configuration for a device specified by the HW address. // The end of a configuration for a device/partition will cause it to change its state. // Payload: 32 bit HW address as for the FEE_CONFIGURE command return FEE_CONFIGURE_END; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_END_NOSTATE(){ // Same as the @ref FEE_CONFIGURE_END command, but will not trigger any change of state. return FEE_CONFIGURE_END_NOSTATE; } uint32_t RCUControlEngine::sendFEE_CONFIGURE_END_1Word(uint32_t configID, uint32_t device, uint32_t altroHWaddress){ // Contains the 32 bit HW address for the FEE_CONFIGURE_END command // - 32 bit HW address, related to the ALTRO addressing // bit 11-0 ALTRO address // bit 15-12 Device: 0 - CE, 1 - RCU, 2 - FEC, 3 - Altro, 4 - Channel // bit 31-16 configuration id to distinguish blocks to the same device/partition return sendFEE_CONFIGURE_1Word(configID, device, altroHWaddress); } uint32_t RCUControlEngine::sendFEE_VERIFICATION(){ // No verification sequence. return sendFEE_VERIFICATION(0); } uint32_t RCUControlEngine::sendFEE_VERIFICATION(uint32_t wordCount){ // Specify a verification sequence. The command can only occur inside a FEE_CONFIGURE command // and will be connected to this. // // Parameter: number of payload words excluding the 2 checksum words. If 0, no checksum words // are expected // payload: // - 32 bit checksum for this verification command block // - 32 bit checksum for the result of the verification sequence // - first command of the block // - ... // - last command of the block // // An empty verification command (parameter zero) does not have any payload, not even // the checksums. uint32_t retval; retval = FEE_VERIFICATION + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendFEE_EXTERNAL_CONFIGURATION() { // Signal the need for external configuration data. return sendFEE_EXTERNAL_CONFIGURATION(0); } uint32_t RCUControlEngine::sendFEE_EXTERNAL_CONFIGURATION(uint32_t wordCount) { // Signal the need for external configuration data. // The command can only occur inside a FEE_CONFIGURE command and will abort the interpretation // of the configuration sequence. The device will go into state CONF_DDL and wait for a // DDL_END action. After this action the device is considered to be completely // configured and it will go to state STBY_CONFIGURED. // Parameter: lower 16 bit of the device HW address described in FEE_CONFIGURE return FEE_EXTERNAL_CONFIGURATION + (wordCount&ci16BitOn); } uint32_t RCUControlEngine::sendCE_CMD_TAILER(){ // The tailer to be used for all commands return CE_CMD_TAILER; } // // RCU FW V 2 // uint32_t RCUControlEngine::sendRCU_WRITE_ALTROIF() { uint32_t retval = WRITE_ALTROIF; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TRGCONF() { uint32_t retval = sendRCU_WRITE_TRGCFG(); // the same. temporary?? return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_RDOMOD() { uint32_t retval = WRITE_RDO_MOD; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_ALTROCFG1() { uint32_t retval = WRITE_ALTROCFG1; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_ALTROCFG2() { uint32_t retval = WRITE_ALTROCFG2; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_RCUID() { uint32_t retval = WRITE_RCUID; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_CONTROL() { uint32_t retval = WRITE_TTCCONTROL; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_L1_LATENCY() { uint32_t retval = WRITE_TTC_L1_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_L1_MSG_LATENCY() { uint32_t retval = WRITE_TTC_L1_MSG_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_ROI_CONFIG1() { uint32_t retval = WRITE_TTC_ROICONFIG1; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_ROI_CONFIG2() { uint32_t retval = WRITE_TTC_ROICONFIG2; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_L2_LATENCY() { uint32_t retval = WRITE_TTC_L2_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_TTC_ROI_LATENCY() { uint32_t retval = WRITE_TTC_ROILATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_HLMEM(uint32_t wordCount) { uint32_t retval = WRITE_ALTRO_HLMEM + (wordCount&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_WRITE_PEDMEM(uint32_t channelAddress) { uint32_t retval = RCU_WRITE_ALTRO_PEDMEM + (channelAddress&ci16BitOn); return retval; } uint32_t RCUControlEngine::sendRCU_READ_ALTROIF() { uint32_t retval = READ_ALTROIF; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TRGCONF() { uint32_t retval = sendRCU_READ_TRGCFG(); // ?? return retval; } uint32_t RCUControlEngine::sendRCU_READ_RDOMOD() { uint32_t retval = READ_RDO_MOD; return retval; } uint32_t RCUControlEngine::sendRCU_READ_ALTROCFG1() { uint32_t retval = READ_ALTROCFG1; return retval; } uint32_t RCUControlEngine::sendRCU_READ_ALTROCFG2() { uint32_t retval = READ_ALTROCFG2; return retval; } uint32_t RCUControlEngine::sendRCU_READ_RCUID() { uint32_t retval = READ_RCUID; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_CONTROL() { uint32_t retval = READ_TTCCONTROL; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_ROI_CONFIG1() { uint32_t retval = READ_TTC_ROICONFIG1; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_ROI_CONFIG2() { uint32_t retval = READ_TTC_ROICONFIG2; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_L2_LATENCY() { uint32_t retval = READ_TTC_L2_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_ROI_LATENCY() { uint32_t retval = READ_TTC_ROILATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_L1_LATENCY() { uint32_t retval = READ_TTC_L1_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_READ_TTC_L1_MSG_LATENCY() { uint32_t retval = READ_TTC_L1_MSG_LATENCY; return retval; } uint32_t RCUControlEngine::sendRCU_READ_HLMEM() { uint32_t retval = READ_ALTRO_HLMEM; return retval; }