Skip to content
Snippets Groups Projects
Select Git revision
  • c931eba73fff722b7b624165d7921e3a2ec7d5d1
  • master default protected
  • L2SDP-1131
  • L2SDP-LIFT
  • L2SDP-1137
  • HPR-158
6 results

sim_transceiver_serializer.vhd

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    fpga.cpp 52.09 KiB
    /* *************************************************************************
    * Copyright 2020
    * ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
    * P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    * *********************************************************************** */
    
    /* *************************************************************************
    * Author:
    * . Leon Hiemstra
    * . Pieter Donker
    * Purpose:
    * . opc-ua to ucp translator
    * Description:
    * . class for fpga registers (peripherals) that need some more actions
    * *********************************************************************** */
    
    #ifndef _REENTRANT
    #error ACK! You need to compile with _REENTRANT defined since this uses threads
    #endif
    
    #include <string>
    #include <cstdint>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    
    #include <stdexcept>
    #include <iostream>
    #include <iomanip>
    #include <exception>
    #include <unistd.h>
    
    #include <sys/time.h>
    #include <arpa/inet.h> // htons, inet_pton
    
    #include "fpga.h"
    #include "../constants.h"
    #include "../tools/mmap.h"
    #include "../tools/util.h"
    
    #include<fstream>
    
    using namespace std;
    
    extern int debug;
    
    #define R_MEM 0  // read mem value
    #define R_UCP 1  // read value from hardware
    
    Periph_fpga::Periph_fpga(string ipaddr, string expected_design_name, uint expected_firmware_version, bool enabled):
        Enabled(enabled),
        my_expected_design_name(expected_design_name),
        my_expected_firmware_version(expected_firmware_version),
        my_current_design_name("-"),
        my_current_status("offline"),
        my_current_hw_version(0),
        my_current_fw_version("-.-"),
        my_current_hardware_version("-"),
        my_current_firmware_version("-"),
        my_current_temp(-1.)
    {
        ucp = new UCP(ipaddr);
    
        if (Enabled) {
            mmap = new CMMap(read_reg_map());
        }
        else {
            mmap = new CMMap();
        }
    
        registerMap = new CPointMap();
        //                                                                                                         update interval
        //                                                                                                         |
        registerMap->add_register("fpga/system",                                 "-", 0, 1, "RO", REG_FORMAT_STRING,  2);
        registerMap->add_register("fpga/name",                                   "-", 1, 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/stamps",                                 "-", 0, 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/note",                                   "-", 0, 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/firmware_version",                       "-", 0, 1, "RO", REG_FORMAT_STRING,  10);
        registerMap->add_register("fpga/hardware_version",                       "-", 0, 1, "RO", REG_FORMAT_STRING,  10);
        registerMap->add_register("fpga/temp",                                   "-", 1, 1, "RO", REG_FORMAT_STRING,  5);
        registerMap->add_register("fpga/status",                                 "-", 1, 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/enable_mask",                            "-", 1, 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/rbf",                                    "-", 0, 1, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_init",                             "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_erase",                            "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_pages",                            "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_page",                             "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_prot",                             "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/epcs_wait_busy",                         "-", 0, 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/epcs_mmdp_data",                         "-", 0, 1, "WO", REG_FORMAT_STRING,  1);
    
        uint32_t scrap_span = mmap->getSpan("mm/0/RAM_SCRAP/0/data");
        registerMap->add_register("fpga/scrap",                                "-", 1, scrap_span, "RW", REG_FORMAT_UINT32, 20);
    
        uint32_t weights_span = (187392 / 16);  // = 11712
        registerMap->add_register("fpga/weights",                              "-", 1, weights_span, "RW", REG_FORMAT_INT16, 33);
    
        registerMap->add_register("fpga/processing_enable",                      "-", 1, 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_selector",                   "-", 1, 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_enable",                     "-", 1, 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_hdr_eth_destination_mac",    "-", 1, 1, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/sst_offload_hdr_ip_destination_address", "-", 1, 1, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/sst_offload_hdr_udp_destination_port",   "-", 1, 1, "RW", REG_FORMAT_UINT16,  1);
    
        registerMap->add_register("fpga/sdp_info_station_id",                    "-", 1, 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_observation_id",                "-", 1, 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_nyquist_sampling_zone_index",   "-", 1, 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_antenna_band_index",            "-", 1, 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_f_adc",                         "-", 1, 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_fsub_type",                     "-", 1, 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_block_period",                  "-", 1, 1, "RO", REG_FORMAT_UINT32, 1);
    
        registerMap->add_register("fpga/wg_enable",                              "-", 1, 12, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/wg_amplitude",                           "-", 1, 12, "RW", REG_FORMAT_FLOAT, 1);
        registerMap->add_register("fpga/wg_phase",                               "-", 1, 12, "RW", REG_FORMAT_FLOAT, 1);
        registerMap->add_register("fpga/wg_frequency",                           "-", 1, 12, "RW", REG_FORMAT_FLOAT, 1);
    
        registerMap->print_screen();
    
        // Test FPGA by reading system info:
        try {
            TermOutput termout;
            read_system_info(termout);
        } catch (exception& e) {
            cerr << "Test Periph_fpga::Periph_fpga:read_system_info(): " << e.what() << endl;
        }
    
    //    rbf_wf.open ("/tmp/rbf.dat", ofstream::out | ofstream::binary );
    //    if (!rbf_wf) {
    //      cerr << "Cannot open file!" << endl;
    //      exit(-1);
    //   }
       Flash_fact_sector_start = 0;
       Flash_fact_sector_end   = 159;
       Flash_user_sector_start = 160;
       Flash_user_sector_end   = 319;
    
       Flash_pages_per_sector  = 1024;
       Flash_page_size_bytes   = 256;
    
       Flash_page_start = Flash_user_sector_start * Flash_pages_per_sector;
       Flash_select = 1; // default is user
    
    #define FLASH_EPCS_REG_ADDR         0
    #define FLASH_EPCS_REG_RDEN         1
    #define FLASH_EPCS_REG_READ         2
    #define FLASH_EPCS_REG_WRITE        3
    #define FLASH_EPCS_REG_SECTOR_ERASE 4
    #define FLASH_EPCS_REG_BUSY         5
    #define FLASH_EPCS_REG_UNPROTECT    6
    
        cout << "Periph_fpga::scrapram_size=" << scrap_span << endl;
        Scrap_RW_copy.resize(scrap_span);
    
        cout << "Periph_fpga::weights_size=" << weights_span << endl;
        Weights_RW_copy.resize(weights_span);
    }
    
    Periph_fpga::~Periph_fpga()
    {
    //    rbf_wf.close();
        if (ucp != NULL) delete ucp;
        if (mmap != NULL) delete mmap;
        if (registerMap != NULL) delete registerMap;
    }
    
    bool Periph_fpga::Read(const string addr_str, uint32_t *data_ptr, bool use_mask_shift=true)
    {
        bool ret;
        if (!Enabled) {
            return false;
            // throw runtime_error("disabled: " + addr_str);
        }
        if (!mmap->find_register(addr_str)) {
            return false;
        }
    
        uint32_t addr = mmap->getAddr((addr_str));
    
        mmap->getReadPermission((addr_str));
    
        uint32_t nvalues = mmap->getSpan((addr_str));
        bool isfifo = mmap->type_isfifo((addr_str));
    
        ret = ucp->readRegister(addr, nvalues, data_ptr, isfifo);
        if (use_mask_shift) {
            for (uint32_t i=0; i<nvalues; i++) {
                data_ptr[i] = mask_shift(addr_str, data_ptr[i]);
            }
        }
        return ret;
    }
    
    bool Periph_fpga::Write(const string addr_str, const uint32_t nvalues, uint32_t *data_ptr, bool use_shift_mask=true)
    {
        if (!Enabled) {
            return false;
            // throw runtime_error("disabled " + addr_str);
        }
        if (!mmap->find_register(addr_str)) {
            return false;
        }
        uint32_t addr = mmap->getValidAddr((addr_str), 1);
        uint32_t span = mmap->getSpan((addr_str));
        cout << "addr=" << addr_str << " span=" << to_string(span) << endl;
        mmap->getWritePermission((addr_str));
    
        bool isfifo = mmap->type_isfifo((addr_str));
        if (use_shift_mask) {
            for (uint32_t i=0; i<span; i++) {
                data_ptr[i] = shift_mask(addr_str, data_ptr[i]);
            }
        }
        return ucp->writeRegister(addr, span, data_ptr, isfifo);
    }
    
    // for reading
    uint32_t Periph_fpga::mask_shift(const string addr_str, uint32_t data)
    {
        uint32_t shift = mmap->getShift((addr_str));
        uint32_t mask = mmap->getMask((addr_str));
        uint32_t _data = data;
    
        if (shift != 0 || mask != 0xffffffff) {
            _data &= mask;
            _data = _data >> shift;
        }
        return _data;
    }
    
    // for writing
    uint32_t Periph_fpga::shift_mask(const string addr_str, uint32_t data)
    {
        uint32_t shift = mmap->getShift((addr_str));
        uint32_t mask = mmap->getMask((addr_str));
        uint32_t _data = data;
    
        if (shift != 0 || mask != 0xffffffff) {
            _data = _data << shift;
            _data &= mask;
        }
        return _data;
    }
    
    
    bool Periph_fpga::read_fpga_status(TermOutput& termout, int format)
    {
        if (my_current_status == "online") {
            termout.val[0] = 1;
        }
        else {
            termout.val[0] = 0;
        }
        termout.nof_vals = 1;
        termout.datatype = format;
        return true;
    }
    
    bool Periph_fpga::write_fpga_enable_mask(const char *data)
    {
        Enabled = (bool)*data;
        return true;
    }
    
    bool Periph_fpga::read_fpga_enable_mask(TermOutput& termout, int format)
    {
        termout.val[0] = Enabled;
        termout.nof_vals = 1;
        termout.datatype = format;
        return true;
    }
    
    /*
    """Peripheral system_info
       Register map:
        31             24 23             16 15              8 7               0  wi
       |-----------------|-----------------|-----------------|-----------------|
       |                                                          use_phy[7:0] |  1
       |-----------------------------------------------------------------------|
       |                           system_info[31:0]                           |  0
       |-----------------------------------------------------------------------|
    
        system_info[23:20] = firmware version high[3:0]
        system_info[19:16] = firmware version low[3:0]
        system_info[10]    = cs_sim (= g_sim, 0 on HW, 1 in VHDL simulation)
        system_info[9:8]   = hardware version [1:0] (= 0 for UniBoard 1A and 1B)
        system_info[7:0]   = node id[7;0]
    */
    bool Periph_fpga::read_system_info(TermOutput& termout)
    {
        // cout << "read system info" << endl;
        uint32_t data;
        bool retval = Read("mm/0/PIO_SYSTEM_INFO/info", &data);
        if (retval == false) { return false; }
    
        // string design_name = read_design_name();
        my_current_design_name = read_design_name();
    
        // FIXME: get rid of magic constants in masks, should be in CCFG:
        uint firmware_version    = (data & 0x00F00000) >> 20;
        uint firmware_subversion = (data & 0x000F0000) >> 16;
        my_current_fw_version = to_string(firmware_version) + "." + to_string(firmware_subversion);
    
        my_current_hw_version = (data & 0x0000300) >> 8;
    
        if (my_current_design_name == my_expected_design_name && firmware_version >= my_expected_firmware_version) {
            my_current_status = "online";
            retval = true;
        }
        else {
            retval = false;
            cerr << "Warning: Node configuration mismatch!! (read_design_name/version=" << my_current_design_name
                      << "/" << firmware_version << "), expected=" << my_expected_design_name
                      << "/" << my_expected_firmware_version << ")" << endl;
            //syslog(LOG_WARNING,"Node configuration mismatch!! (read_design_name/version=%s/%d, expected=%s/%d)\n",
            //       my_current_design_name.c_str(), firmware_version,
            //       my_expected_design_name.c_str(), my_expected_firmware_version);
    
            my_current_status = "offline";
            registerMap->setAllPermission_NA();
        }
        return retval;
    }
    
    bool Periph_fpga::read(TermOutput& termout, const string addr,
                           const string type, char *data, const uint nvalues,
                           const int format)
    {
        bool retval = false;
    
        termout.datatype = format;
        termout.nof_vals = 0;
        if (!Enabled) { return false; }
    
        if (type == "mm") {
            uint32_t *data_ptr = (uint32_t *)data;
            retval = Read(addr, data_ptr);
            termout.datatype = format;
        }
        else { // "fpga/..."
    
            if (addr == "fpga/system") {
                retval = read_system_info(termout);
            }
            else if (addr == "fpga/name") {
                termout.nof_vals = my_current_design_name.size();
                termout.datatype = format;
                strcpy(termout.val, my_current_design_name.c_str());
                retval = true;
            }
            else if (addr == "fpga/stamps") {
                retval = read_stamps(termout, format);
                termout.datatype = format;
            }
            else if (addr == "fpga/note") {
                termout.datatype = format;
                retval = true;
            }
            else if (addr == "fpga/firmware_version") {
                retval = read_firmware_version(termout, format, R_MEM);  // later on R_MEM
            }
            else if (addr == "fpga/hardware_version") {
                retval = read_hardware_version(termout, format, R_MEM);  // later on R_MEM
            }
            else if (addr == "fpga/temp") {
                retval = read_fpga_temperature(termout, format, R_MEM);  // later on R_MEM
            }
            else if (addr == "fpga/epcs_wait_busy") {
                retval = wait_while_epcs_busy(1000);
            }
            else if (addr == "fpga/status") {
                retval = read_fpga_status(termout, format);
            }
            else if (addr == "fpga/enable_mask") {
                retval = read_fpga_enable_mask(termout, format);
            }
            else if (addr == "fpga/scrap") {
                retval = read_fpga_scrap(termout, format, R_MEM);
            }
            else if (addr == "fpga/weights") {
                // retval = read_fpga_weights(termout, format);
                retval = read_fpga_weights(termout, format, R_MEM);
            }
            else if (addr == "fpga/sst_offload_selector") {
                retval = read_sst_offload_selector(termout, format);
            }
            else if (addr == "fpga/sst_offload_enable") {
                retval = read_sst_offload_enable(termout, format);
            }
            else if (addr == "fpga/sst_offload_hdr_eth_destination_mac") {
                retval = read_sst_offload_hdr_eth_destination_mac(termout, format);
            }
            else if (addr == "fpga/sst_offload_hdr_ip_destination_address") {
                retval = read_sst_offload_hdr_ip_destination_address(termout, format);
            }
            else if (addr == "fpga/sst_offload_hdr_udp_destination_port") {
                retval = read_sst_offload_hdr_udp_destination_port(termout, format);
            }
            else if (addr == "fpga/processing_enable") {
                retval = read_processing_enable(termout, format);
            }
            else if (addr == "fpga/sdp_info_station_id") {
                retval = read_sdp_info_station_id(termout, format);
            }
            else if (addr == "fpga/sdp_info_observation_id") {
                retval = read_sdp_info_observation_id(termout, format);
            }
            else if (addr == "fpga/sdp_info_nyquist_sampling_zone_index") {
                retval = read_sdp_info_nyquist_sampling_zone_index(termout, format);
            }
            else if (addr == "fpga/sdp_info_antenna_band_index") {
                retval = read_sdp_info_antenna_band_index(termout, format);
            }
            else if (addr == "fpga/sdp_info_f_adc") {
                retval = read_sdp_info_f_adc(termout, format);
            }
            else if (addr == "fpga/sdp_info_fsub_type") {
                retval = read_sdp_info_fsub_type(termout, format);
            }
            else if (addr == "fpga/sdp_info_block_period") {
                retval = read_sdp_info_block_period(termout, format);
            }
            else if (addr == "fpga/wg_enable") {
                retval = read_wg_enable(termout, format);
            }
            else if (addr == "fpga/wg_amplitude") {
                retval = read_wg_amplitude(termout, format);
            }
            else if (addr == "fpga/wg_phase") {
                retval = read_wg_phase(termout, format);
            }
            else if (addr == "fpga/wg_frequency") {
                retval = read_wg_frequency(termout, format);
            }
    
            else {
                throw runtime_error("address " + addr + " not found!");
            }
        }
        //cout << "Periph_fpga::read, addr=" << addr << ", datatype=" << format << "/" << termout.datatype << endl;
        return retval;
    }
    
    bool Periph_fpga::write(TermOutput& termout, const string addr, const string type,
                            char *data, const uint nvalues, const int format)
    {
        bool retval = false;
    
        if (!Enabled) { return false; }
    
        uint32_t *data_ptr = (uint32_t *)data;
    
        if (type == "mm") {
            retval = Write(addr, nvalues, data_ptr);
        }
        else { // "fpga/..."
        //    if (addr == "fpga/rbf") {
        //        char *data_ptr=(char *)data;
        //        cout << "writing: " << len*sizeof(uint32_t) << " bytes" << endl;
        //        rbf_wf.write(data_ptr, len*sizeof(uint32_t));
        //        retval = true;
        //    } else
            if (addr == "fpga/flash_init") {
                Flash_select = data_ptr[0];
                if (Flash_select == 0) { // factory
                    Flash_page_start = Flash_fact_sector_start * Flash_pages_per_sector;
                }
                else { // user
                    Flash_page_start = Flash_user_sector_start * Flash_pages_per_sector;
                }
            }
            else if (addr == "fpga/flash_erase") {
                flash_erase();
            }
            else if (addr == "fpga/flash_pages") {
                retval = flash_pages(data_ptr, nvalues);
            }
            else if (addr == "fpga/flash_page") {
                retval = flash_page(data_ptr, nvalues);
            }
            else if (addr == "fpga/flash_prot") {
                retval = flash_prot(data_ptr);
            }
            else if (addr == "fpga/epcs_mmdp_data") {
                // write to FIFO
                retval = Write("mm/0/REG_MMDP_DATA/data", nvalues, data_ptr);
            }
            else if (addr == "fpga/enable_mask") {
                retval = write_fpga_enable_mask(data);
            }
            else if (addr == "fpga/scrap") {
                retval = write_fpga_scrap(data_ptr, nvalues);
            }
            else if (addr == "fpga/weights") {
                retval = write_fpga_weights(data_ptr, nvalues);
            }
            else if (addr == "fpga/sst_offload_selector") {
                retval = write_sst_offload_selector(data_ptr, nvalues);
            }
            else if (addr == "fpga/sst_offload_enable") {
                retval = write_sst_offload_enable(data_ptr, nvalues);
            }
            else if (addr == "fpga/sst_offload_hdr_eth_destination_mac") {
                retval = write_sst_offload_hdr_eth_destination_mac(data, nvalues);
            }
            else if (addr == "fpga/sst_offload_hdr_ip_destination_address") {
                retval = write_sst_offload_hdr_ip_destination_address(data, nvalues);
            }
            else if (addr == "fpga/sst_offload_hdr_udp_destination_port") {
                retval = write_sst_offload_hdr_udp_destination_port(data_ptr, nvalues);
            }
            else if (addr == "fpga/processing_enable") {
                retval = write_processing_enable(data_ptr, nvalues);
            }
            else if (addr == "fpga/sdp_info_station_id") {
                retval = write_sdp_info_station_id(data_ptr, nvalues);
            }
            else if (addr == "fpga/sdp_info_observation_id") {
                retval = write_sdp_info_observation_id(data_ptr, nvalues);
            }
            else if (addr == "fpga/sdp_info_nyquist_sampling_zone_index") {
                retval = write_sdp_info_nyquist_sampling_zone_index(data_ptr, nvalues);
            }
            else if (addr == "fpga/wg_enable") {
                retval = write_wg_enable(data_ptr, nvalues);
            }
            else if (addr == "fpga/wg_amplitude") {
                retval = write_wg_amplitude(data_ptr, nvalues);
            }
            else if (addr == "fpga/wg_phase") {
                retval = write_wg_phase(data_ptr, nvalues);
            }
            else if (addr == "fpga/wg_frequency") {
                retval = write_wg_frequency(data_ptr, nvalues);
            }
            else {
                throw runtime_error("address " + addr + " not found!");
            }
        }
        return retval;
    }
    
    bool Periph_fpga::monitor(TermOutput& termout, uint seconds)
    {
        if (!Enabled) { return false; }
    
        if ((seconds % registerMap->getUpdateInterval("fpga/system")) == 0) {
            read_system_info(termout);
            termout.clear();
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/temp")) == 0) {
            read_fpga_temperature(termout, REG_FORMAT_FLOAT, R_UCP);
            termout.clear();
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/hardware_version")) == 0) {
            read_hardware_version(termout, REG_FORMAT_STRING, R_UCP);
            termout.clear();
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/firmware_version")) == 0) {
            read_firmware_version(termout, REG_FORMAT_STRING, R_UCP);
            termout.clear();
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/scrap")) == 0) {
            read_fpga_scrap(termout, REG_FORMAT_UINT32, R_UCP);
            termout.clear();
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/weights")) == 0) {
            read_fpga_weights(termout, REG_FORMAT_INT16, R_UCP);
            termout.clear();
        }
        return true;
    }
    
    
    bool Periph_fpga::flash_prot(uint32_t *data)
    {
        bool retval = false;
        uint32_t passphrase_protect = 0;
        uint32_t passphrase_unprotect = 0xBEDA221E;
    
        if (*data == 0) { // unprotect
            retval = Write("mm/0/REG_EPCS/unprotect", 1, &passphrase_unprotect);
        }
        else { // protect
            retval = Write("mm/0/REG_EPCS/unprotect", 1, &passphrase_protect);
        }
        return retval;
    }
    
    bool Periph_fpga::flash_page(uint32_t *data, const uint len)
    {
        bool retval = false;
    
        wait_while_epcs_busy(1);
        // write address
        uint32_t addr = Flash_page_start * Flash_page_size_bytes;
        retval = Write("mm/0/REG_EPCS/addr", 1, &addr);
    
        // write to FIFO
        retval = Write("mm/0/REG_MMDP_DATA/data", len, data);
    
        // write_write
        uint32_t d = 1;
        retval = Write("mm/0/REG_EPCS/write", 1, &d);
    
        return retval;
    }
    
    bool Periph_fpga::flash_pages(uint32_t *data, const uint len)
    {
        bool retval = false;
        uint page_size_words = Flash_page_size_bytes / sizeof(uint32_t);
        uint nof_pages = ceil_div(len, page_size_words);
    
        cout << "Periph_fpga::flash_pages nof_pages=" << nof_pages << endl;
    
        for (uint p=0; p<nof_pages; p++) {
            retval = flash_page(&data[(p*page_size_words)], page_size_words);
            Flash_page_start++;
        }
        return retval;
    }
    
    bool Periph_fpga::wait_while_epcs_busy(uint sleeptime)
    {
        uint32_t data;
        bool retval;
        //cout << "wait_while_epcs_busy:";
        for (int i=0; i<100; i++) {
            retval = Read("mm/0/REG_EPCS/busy", &data);
            if (!retval) break;
            if (data == 0) break;
            usleep(sleeptime);
            //cout << "." << flush;
        }
        //cout << endl;
        if (data != 0) {
            retval = false; // still busy
        }
        return retval;
    }
    
    bool Periph_fpga::flash_erase_sector(uint32_t sector)
    {
        // We need to write any address in the target sector's address range to select that sector for erase.
        // We'll use the base (lowest) address of the sectors for this: sector 0 starts at 0x0, sector 1 starts
        // at 0x40000 etc.
    
        bool retval = false;
        uint32_t s,d;
        vector<uint32_t> s_list = {0,0x10000,0x20000,0x30000};
    
        cout << "erase flash sector: " << sector << endl;
        for (uint i=0; i<s_list.size(); i++) {
            s = sector * 0x40000 + s_list[i];
            // write address
            retval = Write("mm/0/REG_EPCS/addr", 1, &s);
            // sector erase
            d = 1;
            retval = Write("mm/0/REG_EPCS/sector_erase", 1, &d);
            wait_while_epcs_busy(50000);
        }
        return retval;
    }
    
    bool Periph_fpga::flash_erase()
    {
        bool retval = false;
        uint32_t start, end;
        cout << "erase flash for bank: " << Flash_select << endl;
        if (Flash_select == 0) { // factory
            start = Flash_fact_sector_start;
            end = Flash_fact_sector_end + 1;
        }
        else { // user
            start = Flash_user_sector_start;
            end = Flash_user_sector_end + 1;
        }
        for (uint32_t s=start; s<end; s++) {
            retval = flash_erase_sector(s);
        }
        return retval;
    }
    
    string Periph_fpga::read_design_name()
    {
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        if (Read("mm/0/PIO_SYSTEM_INFO/0/design_name", data)) {
            char *str_ptr = (char *)data;
            string name = string(str_ptr);
            //if (data != NULL) delete[] data;
            return name;
        }
        return "? (error)";
    }
    
    string Periph_fpga::read_design_note()
    {
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        if (Read("mm/0/PIO_SYSTEM_INFO/0/design_note", data)) {
            char *str_ptr = (char *)data;
            string note = string(str_ptr);
            //if (data != NULL) delete[] data;
            return note;
        }
        return "? (error)";
    }
    
    bool Periph_fpga::read_hardware_version(TermOutput& termout, int format, int mode)
    {
        bool retval = true;
        string version(my_current_hardware_version);
        if (mode == R_UCP) {
            uint32_t data[20];
            uint hw_version_nr;
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/PIO_SYSTEM_INFO/0/info_hw_version", data);
            if (retval == false) { return false; }
            hw_version_nr = data[0];
            if (hw_version_nr == 1) {
                version = "UniBoard2b";
            }
            else if (hw_version_nr == 2) {
                version = "UniBoard2c";
            }
            else {
                version = "Unknown";
            }
        }
        termout.nof_vals = version.size();
        termout.datatype = format;
        strcpy(termout.val, version.c_str());
        my_current_hardware_version = version;
        return retval;
    }
    
    bool Periph_fpga::read_firmware_version(TermOutput& termout, int format, int mode)
    {
        bool retval = true;
        string version(my_current_firmware_version);
        if (mode == R_UCP) {
            uint32_t data[20];
    
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/PIO_SYSTEM_INFO/0/design_name", data);
            if (retval == false) { return retval; }
            char *str_ptr = (char *)data;
            string design_name = string(str_ptr);
    
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/PIO_SYSTEM_INFO/0/stamp_date", data);
            if (retval == false) { return retval; }
            string date = to_string(data[0]);
    
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/PIO_SYSTEM_INFO/0/stamp_time", data);
            if (retval == false) { return retval; }
            string time = to_string(data[0]);
    
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/PIO_SYSTEM_INFO/0/stamp_commit", data);
            if (retval == false) { return retval; }
            string revision = to_string(data[0]);
    
            version = date.substr(0, 2) + "-" + date.substr(2, 2) + "-" + date.substr(4, 2)
                    + "T"
                    + time.substr(0, 2) + "." + time.substr(2, 2) + "." + time.substr(4, 2)
                    + "_"
                    + revision + "_" + design_name;
    
        }
        termout.nof_vals = version.size();
        termout.datatype = format;
        strcpy(termout.val, version.c_str());
        my_current_firmware_version = version;
        return retval;
    }
    
    bool Periph_fpga::read_stamps(TermOutput& termout, int format)
    {
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        bool retval = Read("mm/0/PIO_SYSTEM_INFO/0/stamp_date", data);
    
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_temperature(TermOutput& termout, int format, int mode)
    {
        bool retval = true;
        float temp = my_current_temp;
        if (mode == R_UCP) {
            // cout << "read from UCP" << endl;
            uint32_t data[20];
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/REG_FPGA_TEMP_SENS/0/temp", data);
            if (retval == false) { return false; }
            // ADC to engineering
            // see the constants: https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_alttemp_sense.pdf
            // page 10
            temp = ((693. * (float)data[0]) / 1024.) - 265;
        }
        float *temp_ptr = (float *)termout.val;
        *temp_ptr = temp;
        termout.nof_vals = 1;
        termout.datatype = format;
        my_current_temp = temp;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_scrap(TermOutput& termout, int format, int mode)
    {
        bool retval = true;
        uint32_t *ptr = (uint32_t *)termout.val;
        if (mode == R_UCP) {
            uint32_t nvalues = mmap->getSpan("mm/0/RAM_SCRAP/0/data");
            retval = Read("mm/0/RAM_SCRAP/0/data", ptr);
            termout.nof_vals = nvalues;
        }
        else {
            for (uint i=0; i<Scrap_RW_copy.size(); i++) {
                ptr[i] = Scrap_RW_copy[i];
                termout.nof_vals = Scrap_RW_copy.size();
            }
        }
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_fpga_scrap(uint32_t *data, uint nvalues)
    {
        cout << "Periph_fpga::write_fpga_scrap " << nvalues << " values" << endl;
        uint32_t nvalues_chk = mmap->getSpan("mm/0/RAM_SCRAP/0/data");
        if (nvalues > nvalues_chk) {
            nvalues = nvalues_chk;
        }
    
        for (uint i=0; i<nvalues; i++) {
            Scrap_RW_copy[i] = data[i];
            cout << data[i] << " ";
        }
        cout << endl;
        bool retval = Write("mm/0/RAM_SCRAP/0/data", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_fpga_weights(TermOutput& termout, int format, int mode)
    {
        bool retval = true;
        int16_t *ptr = (int16_t *)termout.val;
        if (mode == R_UCP) {
            uint32_t nvalues_scrap = mmap->getSpan("mm/0/RAM_SCRAP/0/data");
            uint nblocks = 48; // 11712/244=48
    
            uint32_t *data_scrap = new uint32_t[nvalues_scrap];
            for (uint i=0; i<nblocks; i++) {
                retval = Read("mm/0/RAM_SCRAP/0/data", data_scrap);
    
                for (uint j=0; j<nvalues_scrap; j+=2) {
                    uint32_t ds = data_scrap[j];
                    *ptr++ = (int16_t)(ds & 0xffff);
                    *ptr++ = (int16_t)(ds >> 16);
                }
            }
            termout.nof_vals = registerMap->getDataSize("fpga/weights_R");
            delete[] data_scrap;
        }
        else {
            for (uint i=0; i<Weights_RW_copy.size(); i++) {
                ptr[i] = Weights_RW_copy[i];
            }
            termout.nof_vals = Weights_RW_copy.size();
        }
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_fpga_weights(const uint32_t *data, uint nvalues)
    {
        bool retval = false;
        uint32_t nvalues_scrap = mmap->getSpan("mm/0/RAM_SCRAP/0/data");
        uint32_t nvalues_weights = registerMap->getDataSize("fpga/weights_R");
        uint nblocks = 48; // 11712/244=48
        if (nvalues > nvalues_weights) {
            nvalues = nvalues_weights;
        }
    
        uint32_t *data_scrap = new uint32_t[nvalues_scrap];
    
        for (uint i=0; i<nvalues; i++) {
            Weights_RW_copy[i] = (short)data[i];
        }
    
        const uint32_t *ptr = data;
        uint si=0;
        for (uint i=0; i<nblocks; i++) {
            for (uint j=0; j<nvalues_scrap; j++) {
                uint32_t ds;
    
                if (si >= nvalues) {
                    break;
                }
                ds = *ptr++; si++;
                if (si >= nvalues) {
                    break;
                }
                ds |= ((*ptr++) << 16);
                si++;
                data_scrap[j] = ds;
            }
            retval = Write("mm/0/RAM_SCRAP/0/data", nvalues_scrap, data_scrap);
        }
    
        delete[] data_scrap;
        return retval;
    }
    
    bool Periph_fpga::read_sst_offload_selector(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_DP_SELECTOR/0/input_select", data);
    
        bool select = (bool)data[0];
    
        bool *_ptr = (bool *)termout.val;
        *_ptr = select;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sst_offload_selector(uint32_t *data, uint32_t nvalues)
    {
        bool retval = Write("mm/0/REG_DP_SELECTOR/0/input_select", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_sst_offload_enable(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_STAT_ENABLE_SST/0/enable", data);
    
        bool select = (bool)data[0];
    
        bool *_ptr = (bool *)termout.val;
        *_ptr = select;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sst_offload_enable(uint32_t *data, uint32_t nvalues)
    {
        cout << "sst_offload_enable data=" << data[0] << " nvlues=" << nvalues << endl;
        return Write("mm/0/REG_STAT_ENABLE_SST/0/enable", nvalues, data);
    }
    
    
    bool Periph_fpga::read_sst_offload_hdr_eth_destination_mac(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_STAT_HDR_DAT_SST/0/eth_destination_mac", data);
    
        uint64_t mac = (uint64_t)data[1] << 32 | data[0];
    
        stringstream mac_ss;
        for (int i=5; i>=0; i--) {
            mac_ss << setfill('0') << setw(2) << right << hex << ((mac >> (i * 8)) & 0xff);
            if (i > 0) {
                mac_ss << ":";
            }
        }
    
        string mac_str = mac_ss.str();
        strcpy(termout.val, mac_str.c_str());
        termout.nof_vals = mac_str.size();
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sst_offload_hdr_eth_destination_mac(const char *data, uint32_t nvalues)
    {
        if (nvalues == 0) return true;
    
        // cout << "data=" << string(data) << endl;
        char sep;
        uint32_t m0, m1, m2, m3, m4, m5;
        uint32_t mac[2] {0, 0};
        string ds(data);
        stringstream ss(ds);
        ss >> setbase(16) >> m0 >> sep >> m1 >> sep >> m2 >> sep >> m3 >> sep >> m4 >> sep >> m5;
        if (ss.fail() || ss.bad()) {
            cout << "parse error in write_sst_offload_hdr_eth_destination_mac (" << ds << ")" << endl;
            return false;
        }
        mac[1] = (m0 << 8) + (m1 << 0);
        mac[0] = (m2 << 24) + (m3 << 16) + (m4 << 8) + m5;
        // cout << "mac=" << mac[1] << ", " << mac[0] << endl;
        return Write("mm/0/REG_STAT_HDR_DAT_SST/0/eth_destination_mac", nvalues, mac);
    }
    
    bool Periph_fpga::read_sst_offload_hdr_ip_destination_address(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_STAT_HDR_DAT_SST/0/ip_destination_address", data);
    
        stringstream ip_ss;
        for (int i=3; i>=0; i--) {
            ip_ss << dec << ((data[0] >> (i * 8)) & 0xff);
            if (i > 0) {
                ip_ss << ".";
            }
        }
    
        string ip_str = ip_ss.str();
        strcpy(termout.val, ip_str.c_str());
        termout.nof_vals = ip_str.size();
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sst_offload_hdr_ip_destination_address(const char *data, uint32_t nvalues)
    {
        if (nvalues == 0) return true;
    
        char sep;
        uint ip0, ip1, ip2, ip3;
        uint32_t ip[1] = {0};
        string ds(data);
        cout << "write_ip=[" << ds << "]" << endl;
        stringstream ss(ds);
        ss >> setbase(10) >> ip0;
        ss >> sep;
        ss >> setbase(10) >> ip1;
        ss >> sep;
        ss >> setbase(10) >> ip2;
        ss >> sep;
        ss >> setbase(10) >> ip3;
        cout << "ip0=" << to_string(ip0) << " ip1=" << to_string(ip1) << " ip2=" << to_string(ip2) << " ip3=" << to_string(ip3) << endl;
        if (ss.fail() || ss.bad()) {
            cout << "parse error in write_sst_offload_hdr_ip_destination_address (" << ds << ")" << endl;
            return false;
        }
        ip[0] += (ip0 & 0xff) << 24;
        ip[0] += (ip1 & 0xff) << 16;
        ip[0] += (ip2 & 0xff) << 8;
        ip[0] += ip3 & 0xff;
        cout << "ip=" << hex << ip[0] << endl;
        return Write("mm/0/REG_STAT_HDR_DAT_SST/0/ip_destination_address", nvalues, ip);
    }
    
    bool Periph_fpga::read_sst_offload_hdr_udp_destination_port(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_STAT_HDR_DAT_SST/0/udp_destination_port", data);
        int port = (int)data[0];
    
        int *_ptr = (int *)termout.val;
        *_ptr = port;
    
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sst_offload_hdr_udp_destination_port(uint32_t *data, uint32_t nvalues)
    {
        return Write("mm/0/REG_STAT_HDR_DAT_SST/0/udp_destination_port", nvalues, data);
    }
    
    
    bool Periph_fpga::read_processing_enable(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_BSN_SOURCE_V2/0/dp_on", data);
        bool dp_on = (bool)data[0];
    
        bool *_ptr = (bool *)termout.val;
        *_ptr = dp_on;
    
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_processing_enable(uint32_t *data, uint32_t nvalues)
    {
        // write dp_on 0
        // write bsn_init [calc val for next sec]
        // write bsn_time_offset [calc val if odd]
        // write dp_on_pps 1
        // write dp_on 1
    
        cout << "processing_enable data=" << data[0] << " nvalues=" << nvalues << endl;
        uint32_t *reg = new uint32_t[2];
        reg[0] = 0;
        reg[1] = 0;
        bool processing_enable = (bool)data[0];
        if (processing_enable) {
            cout << "turn on bsn" << endl;
            // reg[0] = 0;
            // if (Write("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 1, reg) == false) {
            //     cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/dp_on) 0" << endl;
            //     return false;
            // }
    
            struct timeval now;
            uint64_t bsn;
            uint32_t offset = 0;
            gettimeofday(&now, NULL);
            while (now.tv_usec > 800000) {
                gettimeofday(&now, NULL);
            }
            bsn = (uint64_t)((now.tv_sec + 1) / 5.12e-6);
    
            if (now.tv_sec % 2 == 1) {
                offset = 512;
            }
            reg[0] = (uint32_t)(bsn & 0xffffffff);
            reg[1] = (uint32_t)((bsn >> 32) & 0xffffffff);
            if (Write("mm/0/REG_BSN_SOURCE_V2/0/bsn_init", 2, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/bsn_init)" << endl;
                return false;
            }
            reg[0] = offset;
            if (Write("mm/0/REG_BSN_SOURCE_V2/0/bsn_time_offset", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/bsn_time_offset)" << endl;
                return false;
            }
            reg[0] = shift_mask("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 0) | shift_mask("mm/0/REG_BSN_SOURCE_V2/0/dp_on_pps", 1);
            if (Write("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 1, reg, false) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/dp_on_pps)" << endl;
                return false;
            }
            reg[0] = shift_mask("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 1) | shift_mask("mm/0/REG_BSN_SOURCE_V2/0/dp_on_pps", 1);
            if (Write("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 1, reg, false) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/dp_on) 1" << endl;
                return false;
            }
        }
        else {
            reg[0] = 0;
            if (Write("mm/0/REG_BSN_SOURCE_V2/0/dp_on", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/0/dp_on) 0" << endl;
                return false;
            }
        }
        return true;
    }
    
    bool Periph_fpga::read_sdp_info_station_id(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/station_id", data);
    
        uint32_t station_id = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = station_id;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sdp_info_station_id(uint32_t *data, uint32_t nvalues) {
        bool retval = Write("mm/0/REG_SDP_INFO/0/station_id", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_observation_id(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/observation_id", data);
    
        uint32_t observation_id = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = observation_id;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sdp_info_observation_id(uint32_t *data, uint32_t nvalues) {
        bool retval = Write("mm/0/REG_SDP_INFO/0/observation_id", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_nyquist_sampling_zone_index(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/nyquist_zone_index", data);
    
        uint32_t nyquist_zone_index = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = nyquist_zone_index;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_sdp_info_nyquist_sampling_zone_index(uint32_t *data, uint32_t nvalues) {
        bool retval = Write("mm/0/REG_SDP_INFO/0/nyquist_zone_index", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_antenna_band_index(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/antenna_band_index", data);
    
        uint32_t antenna_band_index = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = antenna_band_index;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_f_adc(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/f_adc", data);
    
        uint32_t f_adc = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = f_adc;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_fsub_type(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/fsub_type", data);
    
        uint32_t fsub_type = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = fsub_type;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::read_sdp_info_block_period(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/REG_SDP_INFO/0/block_period", data);
    
        uint32_t block_period = (uint32_t)data[0];
    
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = block_period;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    // Waveform generator functions "_wg_"
    /*
    When FPGA_wg_enable_RW is set False, then disable the WG via mode = c_mode_off = 0.
    When FPGA_wg_enable_RW is set True, then enable the WG via mode = c_mode_calc = 1. Once enabled, the
    WG starts or restarts when it gets a trigger from the BSN scheduler. The trigger has to occur at the
    same BSN for all WG that are enabled, to ensure that they start synchronously. Any WG that are not
    enabled will ignore the trigger. The exact BSN at which the WG start is don't care. The trigger is
    scheduled via scheduled_bsn in REG_BSN_SCHEDULER. The current BSN can be read from the REG_BSN_SCHEDULER
    on one of the FPGAs. Assume the communication to write the scheduled_bsn in all FPGAs will take
    less than 1 ms, then a margin of 10 - 100 ms is sufficient. The BSN period corresponds to 5.12 μs, so
    a c_bsn_latency = 20000 (≈ 100 ms) is sufficient for scheduled_bsn = current_bsn + c_bsn_latency.
    The MP reports False when mode = c_mode_off = 0, else True.
    Note:
      The nof_samples field and mode field share an address in REG_DIAG_WG. The nof_samples = 2**W_wg_buf = 1024.
    */
    bool Periph_fpga::read_wg_enable(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/mode";
            retval = Read(regname, &data[i]);
        }
    
        bool enabled;
        bool *_ptr = (bool *)termout.val ;
        for (uint i=0; i< C_S_pn; i++) {
            enabled = (bool)data[i];
            *_ptr = enabled;
            _ptr++;
        }
    
        termout.nof_vals = C_S_pn;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_wg_enable(uint32_t *data, uint32_t nvalues) {
        bool retval = true;
        uint32_t *reg = new uint32_t[4];
        uint64_t scheduled_bsn;
        bool wg_enable;
    
        string regname1;
        string regname2;
    
        // turn off waveform
        for (uint i=0; i< C_S_pn; i++) {
            wg_enable = (bool)data[i];
            if (wg_enable == false) {
                regname1 = "mm/0/REG_WG/" + to_string(i) + "/mode";
                regname2 = "mm/0/REG_WG/" + to_string(i) + "/nof_samples";
                reg[0] = shift_mask(regname1, C_WG_MODE_OFF) | shift_mask(regname2, 1024);  // TODO: make constant
                retval = Write(regname1, 1, reg, false);
            }
        }
        // turn on waveform
        for (uint i=0; i< C_S_pn; i++) {
            wg_enable = (bool)data[i];
            if (wg_enable == true) {
                regname1 = "mm/0/REG_WG/" + to_string(i) + "/mode";
                regname2 = "mm/0/REG_WG/" + to_string(i) + "/nof_samples";
                reg[0] = shift_mask(regname1, C_WG_MODE_CALC) | shift_mask(regname2, 1024);  // TODO: make constant
                retval = Write(regname1, 1, reg, false);
            }
        }
        // get bsn and add latency
        wg_enable = (bool)data[0];
        if (wg_enable == true) {
            regname1 = "mm/0/REG_BSN_SCHEDULER/0/scheduled_bsn";
            retval = Read(regname1, reg);
            scheduled_bsn = (((uint64_t)reg[1] << 32) + reg[0]);
            cout << "bsn=" << to_string(scheduled_bsn) << endl;
            scheduled_bsn += C_BSN_LATENCY;
            cout << "new bsn=" << to_string(scheduled_bsn) << endl;
            reg[0] = (uint32_t)(scheduled_bsn & 0xffffffff);
            reg[1] = (uint32_t)((scheduled_bsn >> 32) & 0xffffffff);
        }
        // write sheduled bsn
        for (uint i=0; i< C_S_pn; i++) {
            wg_enable = (bool)data[i];
            if (wg_enable == true) {
                regname1 = "mm/0/REG_BSN_SCHEDULER/" + to_string(i) + "/scheduled_bsn";
                retval = Write(regname1, 1, reg);
            }
        }
        delete[] reg;
        return retval;
    }
    
    /*
    ampl = FPGA_wg_amplitude_RW * c_ampl_unit
      where c_ampl_unit = 2^16.
      MP converts ampl into FPGA_wg_amplitude_R.
    */
    bool Periph_fpga::read_wg_amplitude(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/ampl";
            retval = Read(regname, &data[i]);
        }
    
        float ampl;
        float *_ptr = (float *)termout.val ;
        for (uint i=0; i< C_S_pn; i++) {
            ampl = (float)data[i] / C_WG_AMPL_UNIT;
            *_ptr = ampl;
            _ptr++;
        }
    
        termout.nof_vals = C_S_pn;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_wg_amplitude(uint32_t *data, uint32_t nvalues) {
        bool retval = true;
        uint32_t *reg = new uint32_t[2];
    
        float ampl;
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/ampl";
            ampl = (float)data[i] * C_WG_AMPL_UNIT;
            reg[0] = (uint32_t)ampl;
            retval = Write(regname, 1, reg);
        }
        delete[] reg;
        return retval;
    }
    
    
    /*
    phase = FPGA_wg_phase_RW * c_phase_unit
      where c_phase_unit = 2^16 / 360.
      MP converts phase into FPGA_wg_phase_R.
    */
    bool Periph_fpga::read_wg_phase(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/phase";
            retval = Read(regname, &data[i]);
        }
    
        float phase;
        float *_ptr = (float *)termout.val ;
        for (uint i=0; i< C_S_pn; i++) {
            phase = ((float)data[i]) / C_WG_PHASE_UNIT;
            *_ptr = phase;
            _ptr++;
        }
    
        termout.nof_vals = C_S_pn;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_wg_phase (uint32_t *data, uint32_t nvalues) {
        bool retval = true;
        uint32_t *reg = new uint32_t[2];
    
        float phase;
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/phase";
            phase = ((float)data[i]) * C_WG_PHASE_UNIT;
            reg[0] = (uint32_t)phase;
            retval = Write(regname, 1, reg);
        }
        delete[] reg;
        return retval;
    }
    
    /*
    freq = FPGA_wg_frequency_RW / f_adc
    freq = freq % 1  # to wrap freq in [0, 1> interval
    freq = freq * c_freq_unit
      where f_adc = 200e6 and c_freq_unit = 2^31.
      MP converts freq into FPGA_wg_frequency_R.
    */
    bool Periph_fpga::read_wg_frequency(TermOutput& termout, int format) {
        bool retval = true;
        uint32_t data[20];
        memset((void *)data, 0, sizeof(data));
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/freq";
            retval = Read(regname, &data[i]);
        }
    
        float freq;
        float *_ptr = (float *)termout.val ;
        for (uint i=0; i< C_S_pn; i++) {
            freq = ((float)data[i] / C_WG_FREQ_UNIT) * C_F_adc;
            *_ptr = freq;
            _ptr++;
        }
    
        termout.nof_vals = C_S_pn;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_wg_frequency(uint32_t *data, uint32_t nvalues) {
        bool retval = true;
        uint32_t *reg = new uint32_t[2];
        
        float freq, intpart;
        string regname;
        for (uint i=0; i< C_S_pn; i++) {
            regname = "mm/0/REG_WG/" + to_string(i) + "/freq";
            freq = (float)data[i] / C_F_adc;
            freq = modf(freq, &intpart);
            freq = freq * C_WG_FREQ_UNIT;
            reg[0] = (uint32_t)freq;
            retval = Write(regname, 1, reg);
        }
        delete[] reg;
        return retval;
    }
    
    
    bool Periph_fpga::write_wdi_override(TermOutput& termout)
    {
        uint32_t data = 0xB007FAC7;
        return Write("mm/0/PIO_WDI/0/wdi_override", 1, &data);
    }
    
    CMMap Periph_fpga::read_reg_map()
    {
        CMMap reg;
        uint32_t nvalues = REG_ADDR_ROM_SYSTEM_SPAN;
        uint32_t addr = REG_ADDR_ROM_SYSTEM;
        uint32_t *data = new uint32_t[nvalues * sizeof(uint32_t)];
        if (data == NULL) {
            cerr << "malloc error" << endl;
        }
        memset((void *)data, 0, nvalues * sizeof(uint32_t));
    
        if (!ucp->readRegister(addr, nvalues, data)) {
            if (data != NULL) {
                delete[] data;
            }
            cerr << "ucp->readRegister failed" << endl;
            return reg;
        }
    
        for (uint i=0; i<nvalues; i++) {
            /*
            cout << "data[" << dec << i << "]=0x" << hex << setw(8) << setfill('0') << data[i] << " ";
            for (int j=0;j<4;j++) {
              char c = (char)(data[i]>>(8*j)) ;
              if (c=='\0') printf("\\0"); else cout << c;
            }
            cout << endl;
            */
            data[i] = ntohl(data[i]);
        }
        char *str_ptr = (char *)data;
    
        string reg_map_str(str_ptr);
        cout << "Periph_fpga::read_reg_map:\n" << reg_map_str << endl;
        if (data != NULL) {
            delete[] data;
        }
    
        istringstream iss_regmap(reg_map_str);
        return mmap_to_regmap(iss_regmap);
    }