Skip to content
Snippets Groups Projects
Select Git revision
  • 48773743dec2fd618e30fa63a0f9fa7e037fa70a
  • master default protected
  • sdptr_lift
  • v1.5.0
  • v1.4.0
  • v1.3.0
  • v1.2.0
  • v1.1.2
  • v1.1.1
  • v1.1.0
  • v1.0.0
11 results

fpga.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    fpga.cpp 42.80 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 <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 "../tools/mmap.h"
    #include "../tools/util.h"
    
    #include<fstream>
    
    using namespace std;
    
    extern int debug;
    
    #define M_CACHE 0
    #define M_UCP   1
    
    Periph_fpga::Periph_fpga(string ipaddr,
                             string expected_design_name,
                             uint expected_firmware_version,
                             bool 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_fw_version = "-.-";
        my_current_temp = -1.;
    
        Enabled = enabled;
    
        ucp = new UCP(ipaddr);
    
        if (Enabled) {
            mmap = new CMMap(read_reg_map());
        }
        else {
            mmap = new CMMap();
        }
    
        registerMap = new CPointMap();
    
        registerMap->add_register("fpga/system",                                 "-", 0, "RO", REG_FORMAT_STRING,  2);
        registerMap->add_register("fpga/name",                                   "-", 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/stamps",                                 "-", 0, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/note",                                   "-", 0, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/firmware_version",                       "-", 0, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/hardware_version",                       "-", 0, "RO", REG_FORMAT_INT16,   1);
        registerMap->add_register("fpga/temp",                                   "-", 1, "RO", REG_FORMAT_STRING,  5);
        registerMap->add_register("fpga/status",                                 "-", 1, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/enable_mask",                            "-", 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/rbf",                                    "-", 0, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_init",                             "-", 0, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_erase",                            "-", 0, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_pages",                            "-", 0, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_page",                             "-", 0, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/flash_prot",                             "-", 0, "WO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/epcs_wait_busy",                         "-", 0, "RO", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/epcs_mmdp_data",                         "-", 0, "WO", REG_FORMAT_STRING,  1);
    
        uint32_t scrap_span = mmap->getSpan("mm/0/RAM_SCRAP/data");
        registerMap->add_register("fpga/scrap_R",                                "-", scrap_span, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/scrap_W",                                "-", scrap_span, "RW", REG_FORMAT_UINT32, 1);
    
        uint32_t weights_span = (187392 / 16);  // = 11712
        registerMap->add_register("fpga/weights_R",                              "-", weights_span, "RO", REG_FORMAT_INT16, 1);
        registerMap->add_register("fpga/weights_W",                              "-", weights_span, "RW", REG_FORMAT_INT16, 1);
    
        registerMap->add_register("fpga/processing_enable",                      "-", 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_selector",                   "-", 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_enable",                     "-", 1, "RW", REG_FORMAT_BOOLEAN, 1);
        registerMap->add_register("fpga/sst_offload_hdr_eth_destination_mac",    "-", 1, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/sst_offload_hdr_ip_destination_address", "-", 1, "RW", REG_FORMAT_STRING,  1);
        registerMap->add_register("fpga/sst_offload_hdr_udp_destination_port",   "-", 1, "RW", REG_FORMAT_UINT16,  1);
    
        registerMap->add_register("fpga/sdp_info_station_id",                    "-", 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_observation_id",                "-", 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_nyquist_sampling_zone_index",   "-", 1, "RW", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_antenna_band_index",            "-", 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_f_adc",                         "-", 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_fsub_type",                     "-", 1, "RO", REG_FORMAT_UINT32, 1);
        registerMap->add_register("fpga/sdp_info_block_period",                  "-", 1, "RO", REG_FORMAT_UINT32, 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 ret;
        if (!Enabled) {
            throw runtime_error("disabled " + addr_str);
        }
        uint32_t addr = mmap->getAddr((addr_str));
        mmap->getReadPermission((addr_str));
    
        uint32_t nvalues = mmap->getSpan((addr_str));
        uint32_t mask = mmap->getMask((addr_str));
        uint32_t shift = mmap->getShift((addr_str));
        bool isfifo = mmap->type_isfifo((addr_str));
    
        ret = ucp->readRegister(addr, nvalues, data_ptr, isfifo);
        if (ret && (shift != 0 || mask != 0xffffffff)) {
            for (uint32_t i=0; i<nvalues; i++) {
                data_ptr[i] &= mask;
                data_ptr[i] = data_ptr[i] >> shift;
            }
        }
        return ret;
    }
    
    bool Periph_fpga::Write(const string addr_str, const uint32_t nvalues, uint32_t *data_ptr)
    {
        if (!Enabled) {
            throw runtime_error("disabled " + addr_str);
        }
    
        uint32_t addr = mmap->getValidAddr((addr_str), 1);
        uint32_t span = mmap->getSpan((addr_str));
        mmap->getWritePermission((addr_str));
    
        uint32_t shift = mmap->getShift((addr_str));
        uint32_t mask = mmap->getMask((addr_str));
        bool isfifo = mmap->type_isfifo((addr_str));
    
        if (shift != 0 || mask != 0xffffffff) {
            for (uint32_t i=0; i<span; i++) {
                data_ptr[i] = data_ptr[i] << shift;
                data_ptr[i] &= mask;
            }
        }
        return ucp->writeRegister(addr, span, data_ptr, isfifo);
    }
    
    bool Periph_fpga::read_fpga_status(TermOutput& termout, int format)
    {
        termout.strout << "\"" << my_current_status << "\"";
        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.strout << "\"" << Enabled << "\"";
        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);
        string design_name = read_design_name();
        termout.strout << 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;
        termout.strout << firmware_version << "." << firmware_subversion;
        my_current_fw_version = to_string(firmware_version) + "." + to_string(firmware_subversion);
    
        my_current_hw_version = (data & 0x0000300) >> 8;
    
        if (design_name == my_expected_design_name && firmware_version >= my_expected_firmware_version) {
            my_current_status = "online";
            retval = true;
        }
        else {
            retval = false;
            termout.strerr << "Unexpected design_name / firmware_version" << endl;
    
            cerr << "Warning: Node configuration mismatch!! (read_design_name/version=" << 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",
            //       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;
    
        if (type == "mm") {
            uint32_t *data_ptr = (uint32_t *)data;
            retval = Read(addr, data_ptr);
    
            if (retval) {
                for (uint i=0; i<nvalues; i++) {
                    if (i > 0) {
                        termout.strout << ",";
                    }
                    termout.strout << data_ptr[i];
                }
            }
            termout.datatype = format;
        }
        else { // "fpga/..."
    
            if (addr == "fpga/system") {
                retval = read_system_info(termout);
            }
            else if (addr == "fpga/name") {
                string str = read_design_name();
                termout.strout << str;
                termout.nof_vals = str.size();
                termout.datatype = format;
                strcpy(termout.val, str.c_str());
                retval = true;
            }
            else if (addr == "fpga/stamps") {
                retval = read_stamps(termout, format);
                termout.datatype = format;
            }
            else if (addr == "fpga/note") {
                termout.strout << read_design_note();
                termout.datatype = format;
                retval = true;
            }
            else if (addr == "fpga/firmware_version") {
                retval = read_firmware_version(termout, format);
            }
            else if (addr == "fpga/hardware_version") {
                retval = read_hardware_version(termout, format);
            }
            else if (addr == "fpga/temp") {
                retval = read_fpga_temperature(termout, format, M_CACHE);
            }
            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_RW") {
                retval = read_fpga_scrap_RW(termout, format);
            }
            else if (addr == "fpga/scrap_R") {
                retval = read_fpga_scrap_R(termout, format);
            }
            else if (addr == "fpga/weights_RW") {
                retval = read_fpga_weights_RW(termout, format);
            }
            else if (addr == "fpga/weights_R") {
                retval = read_fpga_weights_R(termout, format);
            }
            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 {
                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;
        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_W") {
                retval = write_fpga_scrap_RW(data_ptr, nvalues);
            }
            else if (addr == "fpga/weights_W") {
                retval = write_fpga_weights_RW(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_ptr, nvalues);
                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_ptr, nvalues);
                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 {
                throw runtime_error("address " + addr + " not found!");
            }
        }
        return retval;
    }
    
    bool Periph_fpga::monitor(uint seconds)
    {
        TermOutput termout;
        if ((seconds % registerMap->getUpdateInterval("fpga/temp")) == 0) {
            read_fpga_temperature(termout, REG_FORMAT_FLOAT, M_UCP);
        }
        if ((seconds % registerMap->getUpdateInterval("fpga/system")) == 0) {
            read_system_info(termout);
        }
    
        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/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/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)
    {
        bool retval = true;
        uint32_t data[20];
        uint hw_version_nr;
        string hw_version;
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/PIO_SYSTEM_INFO/info_hw_version", data);
        hw_version_nr = data[0];
        if (hw_version_nr == 1) {
            hw_version = "UniBoard2b";
        }
        else if (hw_version_nr == 2) {
            hw_version = "UniBoard2c";
        }
        else {
            hw_version = "Unknown";
        }
        termout.nof_vals = hw_version.size();
        termout.datatype = format;
        strcpy(termout.val, hw_version.c_str());
        termout.strout << "hardware_version= " << hw_version << endl;
        return retval;
    }
    
    bool Periph_fpga::read_firmware_version(TermOutput& termout, int format)
    {
        bool retval = true;
        uint32_t data[20];
        termout.datatype = format;
    
        memset((void *)data, 0, sizeof(data));
        retval = Read("mm/0/PIO_SYSTEM_INFO/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/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/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/stamp_commit", data);
        if (retval == false) {
            return retval;
        }
        string revision = to_string(data[0]);
    
        string firmware_version;
        firmware_version = date.substr(0, 2) + "-" + date.substr(2, 2) + "-" + date.substr(4, 2);
        firmware_version += "T";
        firmware_version += time.substr(0, 2) + "." + time.substr(2, 2) + "." + time.substr(4, 2);
        firmware_version += "_";
        firmware_version += revision + "_" + design_name;
    
        termout.nof_vals = firmware_version.size();
        termout.datatype = format;
        strcpy(termout.val, firmware_version.c_str());
        termout.strout << "firmware_version= " << firmware_version << endl;
        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/stamp_date", data);
    
        termout.strout << "date=" << data[0] << " time=" << data[1] << " git=" << data[2] << endl;
        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 == M_UCP) {
            // cout << "read from UCP" << endl;
            uint32_t data[20];
            memset((void *)data, 0, sizeof(data));
            retval = Read("mm/0/REG_FPGA_TEMP_SENS/temp", data);
    
            // 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;
        }
    
        termout.strout << to_string(temp);
        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_R(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t nvalues = mmap->getSpan("mm/0/RAM_SCRAP/data");
        uint32_t *ptr = (uint32_t *)termout.val;
        retval = Read("mm/0/RAM_SCRAP/data", ptr);
    
        for (uint i=0; i<nvalues; i++) {
            if (i > 0) {
                termout.strout << ",";
            }
            termout.strout << ptr[i];
        }
        termout.nof_vals = nvalues;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_fpga_scrap_RW(uint32_t *data, uint nvalues)
    {
        cout << "Periph_fpga::write_fpga_scrap " << nvalues << " values" << endl;
        uint32_t nvalues_chk = mmap->getSpan("mm/0/RAM_SCRAP/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/data", nvalues, data);
        return retval;
    }
    
    bool Periph_fpga::read_fpga_scrap_RW(TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t *ptr = (uint32_t *)termout.val;
        for (uint i=0; i<Scrap_RW_copy.size(); i++) {
            ptr[i] = Scrap_RW_copy[i];
            if (i>0) {
                termout.strout << ",";
            }
            termout.strout << ptr[i];
        }
        termout.nof_vals = Scrap_RW_copy.size();
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_weights_R(TermOutput& termout, int format)
    {
        bool retval = true;
        uint32_t nvalues_scrap = mmap->getSpan("mm/0/RAM_SCRAP/data");
        uint nblocks = 48; // 11712/244=48
    
        uint32_t *data_scrap = new uint32_t[nvalues_scrap];
        short *ptr = (short *)termout.val;
    
        for (uint i=0; i<nblocks; i++) {
            retval = Read("mm/0/RAM_SCRAP/data", data_scrap);
    
            for (uint j=0; j<nvalues_scrap; j+=2) {
                uint32_t ds = data_scrap[j];
                *ptr++ = (short)(ds & 0xffff);
                *ptr++ = (short)(ds >> 16);
            }
        }
    
        termout.nof_vals = registerMap->getSpan("fpga/weights_R");
        termout.datatype = format;
        delete[] data_scrap;
        return retval;
    }
    
    bool Periph_fpga::write_fpga_weights_RW(const uint32_t *data, uint nvalues)
    {
        bool retval = false;
        uint32_t nvalues_scrap = mmap->getSpan("mm/0/RAM_SCRAP/data");
        uint32_t nvalues_weights = registerMap->getSpan("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/data", nvalues_scrap, data_scrap);
        }
    
        delete[] data_scrap;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_weights_RW(TermOutput& termout, int format)
    {
        bool retval = true;
    
        short *ptr = (short *)termout.val;
        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::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/input_select", data);
    
        bool select = (bool)data[0];
    
        termout.strout << select;
        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/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/enable", data);
    
        bool select = (bool)data[0];
    
        termout.strout << select;
        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/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/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();
        termout.strout << mac_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.good()) {
            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/eth_destination_mac", nvalues, mac);
        }
        cout << "parse error in write_sst_offload_hdr_eth_destination_mac (" << ds << ")" << endl;
        return false;
    }
    
    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/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();
        termout.strout << ip_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);
        stringstream ss(ds);
        ss >> setbase(10) >> ip0 >> sep >> ip1 >> sep >> ip2 >> sep >> ip3;
        if (ss.good()) {
            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/ip_destination_address", nvalues, ip);
        }
        cout << "parse error in write_sst_offload_hdr_ip_destination_address (" << ds << ")" << endl;
        return false;
    }
    
    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/udp_destination_port", data);
        int port = (int)data[0];
    
        termout.strout << port;
        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/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/dp_on", data);
        bool dp_on = (bool)data[0];
    
        termout.strout << dp_on;
        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/dp_on", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/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/bsn_init", 2, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/bsn_init)" << endl;
                return false;
            }
            reg[0] = offset;
            if (Write("mm/0/REG_BSN_SOURCE_V2/bsn_time_offset", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/bsn_time_offset)" << endl;
                return false;
            }
            reg[0] = 1;
            if (Write("mm/0/REG_BSN_SOURCE_V2/dp_on_pps", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/dp_on_pps)" << endl;
                return false;
            }
            reg[0] = 1;
            if (Write("mm/0/REG_BSN_SOURCE_V2/dp_on", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/dp_on) 1" << endl;
                return false;
            }
        }
        else {
            reg[0] = 0;
            if (Write("mm/0/REG_BSN_SOURCE_V2/dp_on", 1, reg) == false) {
                cout << "Write error (mm/0/REG_BSN_SOURCE_V2/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/station_id", data);
    
        uint32_t station_id = (uint32_t)data[0];
    
        termout.strout << station_id;
        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/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/observation_id", data);
    
        uint32_t observation_id = (uint32_t)data[0];
    
        termout.strout << observation_id;
        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/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/nyquist_zone_index", data);
    
        uint32_t nyquist_zone_index = (uint32_t)data[0];
    
        termout.strout << nyquist_zone_index;
        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/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/antenna_band_index", data);
    
        uint32_t antenna_band_index = (uint32_t)data[0];
    
        termout.strout << antenna_band_index;
        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/f_adc", data);
    
        uint32_t f_adc = (uint32_t)data[0];
    
        termout.strout << f_adc;
        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/fsub_type", data);
    
        uint32_t fsub_type = (uint32_t)data[0];
    
        termout.strout << fsub_type;
        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/block_period", data);
    
        uint32_t block_period = (uint32_t)data[0];
    
        termout.strout << block_period;
        uint32_t *_ptr = (uint32_t *)termout.val;
        *_ptr = block_period;
        termout.nof_vals = 1;
        termout.datatype = format;
        return retval;
    }
    
    bool Periph_fpga::write_wdi_override(TermOutput& termout)
    {
        uint32_t data = 0xB007FAC7;
        return Write("mm/0/PIO_WDI/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);
    }