Skip to content
Snippets Groups Projects
Select Git revision
  • master
1 result

cmsis_armcc.h

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    fpga.cpp 19.35 KiB
    /*
     * Copyright 2020 Stichting Nederlandse Wetenschappelijk Onderzoek Instituten,
     * ASTRON Netherlands Institute for Radio Astronomy
     * Licensed under the Apache License, Version 2.0 (the "License");
     *
     * you may not use this file except in compliance with the License.
     *
     * 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.
     *
     * See ../../LICENSE.txt for more info.
     */
    
    #ifndef _REENTRANT
    #error ACK! You need to compile with _REENTRANT defined since this uses threads
    #endif
    
    #include <unistd.h>
    #include <iostream>
    #include <sstream>
    #include <fstream>
    #include <string.h>
    #include <iomanip>
    #include <stdio.h>
    #include <stdint.h>
    //#include <syslog.h>
    #include <math.h>
    #include <sys/time.h>
    #include <arpa/inet.h>
    
    #include "fpga.h"
    #include "../tools/mmap.h"
    #include "../tools/util.h"
    
    #include<iostream>
    #include<fstream>
    
    using namespace std;
    
    extern int debug;
    
    Periph_fpga::Periph_fpga(UCP *ucp, 
                                 const string expected_design_name,
                                 const uint expected_firmware_version, const bool enabled)
    {
        my_expected_design_name      = expected_design_name;
        my_expected_firmware_version = expected_firmware_version;
        my_current_status = "offline";
        my_current_temp = -1.;
        
        Enabled = enabled;
        if(Enabled) {
            registerMap = new RegisterMap(read_reg_map(ucp));
        } else {
            registerMap = new RegisterMap();
        }
    
        // Add composite registers:
        registerMap->add_register("fpga/system",  0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/name",    0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/stamps",  0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/note",    0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/temp",    0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/status",  0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/mask",    0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/rbf",     0, 0, 0xffffffff, 0, "RW", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/flash_init", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/flash_erase", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/flash_pages", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/flash_page", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/flash_prot", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/epcs_wait_busy", 0, 0, 0xffffffff, 0, "RO", "COMP", 0, REG_FORMAT_STRING);
        registerMap->add_register("fpga/epcs_mmdp_data", 0, 0, 0xffffffff, 0, "WO", "COMP", 0, REG_FORMAT_STRING);
    
        // Test FPGA by reading system info:
        try {
            TermOutput termout;
            read_system_info(ucp, termout);
        } catch(std::exception& e) {
            cerr << "Test Periph_fpga::Periph_fpga:read_system_info(): " << e.what() << endl;
        }
    
    //    rbf_wf.open ("/tmp/rbf.dat", std::ofstream::out | std::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
    }
    
    Periph_fpga::~Periph_fpga() 
    {
    //    rbf_wf.close();
        if(registerMap != NULL) delete registerMap;
    }
    
    bool Periph_fpga::Read(UCP *ucp, const std::string addr_str, const uint32_t offset, const uint32_t nvalues, 
                           uint32_t *data_ptr)
    {
        bool ret;
        if(!Enabled) {
            throw runtime_error("disabled "+addr_str);
        }
        uint32_t addr = registerMap->getValidAddr(addr_str,offset,nvalues);
        registerMap->getReadPermission(addr_str);
    
        uint32_t mask = registerMap->getMask((addr_str));
        uint32_t shift = registerMap->getShift((addr_str));
        bool isfifo = registerMap->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(UCP *ucp, const std::string addr_str, const uint32_t offset, const uint32_t nvalues, 
                            uint32_t *data_ptr)
    {
        if(!Enabled) {
            throw runtime_error("disabled "+addr_str);
        }
        uint32_t addr = registerMap->getValidAddr((addr_str),offset,nvalues);
        registerMap->getWritePermission((addr_str));
    
        uint32_t shift = registerMap->getShift((addr_str));
        uint32_t mask = registerMap->getMask((addr_str));
        bool isfifo = registerMap->type_isfifo((addr_str));
    
        if(shift != 0 || mask != 0xffffffff) {
            for(uint32_t i=0; i<nvalues; i++) {
                data_ptr[i] = data_ptr[i] << shift;
                data_ptr[i] &= mask;
            }
        }
        return ucp->writeRegister(addr,nvalues,data_ptr,isfifo);
    }
    
    bool Periph_fpga::read_fpga_status(TermOutput& termout, int format)
    {
        termout.strout << "\"" << my_current_status << "\"";
        if(my_current_status == "online") {
            termout.valint[0] = 1;
            termout.valfloat[0] = 1.;
        } else {
            termout.valint[0] = 0;
            termout.valfloat[0] = 0.;
        }
        termout.nof_floats = 1;
        termout.nof_ints = 1;
        termout.datatype = format;
        return true;
    }
    
    bool Periph_fpga::read_fpga_mask(TermOutput& termout, int format)
    {
        termout.strout << "\"" << Enabled << "\"";
        termout.valint[0] = (int)Enabled;
        termout.valfloat[0] = (float)Enabled;
        termout.nof_floats = 1;
        termout.nof_ints = 1;
        termout.datatype = format;
        return true;
    }
    
    bool Periph_fpga::read_fpga_temp(TermOutput& termout)
    {
        termout.strout << "\"" << my_current_temp << "\"";
        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(UCP *ucp, TermOutput& termout)
    {
        uint32_t data;
        bool retval = Read(ucp, "mm/0/unb2b/system/info",0,1,&data);
        std::string design_name = read_design_name(ucp);
        termout.strout << "design_name=" << design_name << ", ";
    
        char str[1000];
        // FIXME: get rid of magic constants in masks, should be in CCFG:
        uint firmware_version    = (data & 0x00F00000) >> 20;
        uint firmware_subversion = (data & 0x000F0000) >> 16;
        sprintf(str,"fw_version=%d.%d",firmware_version,firmware_subversion); termout.strout << str;
    
        if(design_name == my_expected_design_name && firmware_version >= my_expected_firmware_version) {
            my_current_status = "online";
            retval = true;
        } else {
            retval = false;
            termout.strout << "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(UCP *ucp, TermOutput& termout, const std::string addr,
                           const std::string type, const uint offs, uint *data, const uint nvalues,
                           const int format) // FIXME: may delete 'format' arg
    {
        bool retval = false;
    
        if(type == "mm") {
            retval = Read(ucp, addr, offs,nvalues,data);
    
            if(retval) {
                for(uint i=0; i < nvalues; i++) {
                    if(i>0) termout.strout << ",";
                    termout.strout << data[i];
                }
            }
        } else { // "fpga/..."
            if(addr == "fpga/system") {
                retval = read_system_info(ucp, termout);
            } else if(addr == "fpga/name"){ 
                string str = read_design_name(ucp);
                termout.strout << "read_design_name:" << str;
                termout.datatype = format;
                strcpy(termout.valstr[0], str.c_str());
                retval = true;
            } else if(addr == "fpga/stamps") {
                retval = read_stamps(ucp, termout);
            } else if(addr == "fpga/note") {
                termout.strout << "read_design_note:" << read_design_note(ucp);
                retval = true;
            } else if(addr == "fpga/temp") {
                retval = read_fpga_temperature(ucp, termout, format);
            } else if(addr == "fpga/epcs_wait_busy") {
                retval = wait_while_epcs_busy(ucp,1000); 
            } else if(addr == "fpga/status") {
                retval = read_fpga_status(termout, format); 
            } else if(addr == "fpga/mask") {
                retval = read_fpga_mask(termout, format);
            } else if(addr == "fpga/scrap") {
                retval = read_fpga_scrap(ucp, termout); 
            } else {
                throw runtime_error("address "+addr+" not found!");
            }
        }
        return retval;
    }
    
    bool Periph_fpga::write(UCP *ucp, const std::string addr, const std::string type,
                            const uint offs, const uint *data, const uint len, const int format)
    {
        bool retval = false;
    
        if(type == "mm") {
            retval = Write(ucp, addr, offs, len, (uint32_t *)data);
        } 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 = (uint32_t)data[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(ucp);
            } else if(addr == "fpga/flash_pages") {
                retval = flash_pages(ucp, data, len);
            } else if(addr == "fpga/flash_page") {
                retval = flash_page(ucp, data, len);
            } else if(addr == "fpga/flash_prot") {
                retval = flash_prot(ucp, data);
            } else if(addr == "fpga/epcs_mmdp_data") {
                // write to FIFO
                retval = Write(ucp, "mm/0/REG_MMDP_DATA", 0, len, (uint32_t *)data);
            } else {
                throw runtime_error("address "+addr+" not found!");
            }
        }
        return retval;
    }
    
    bool Periph_fpga::flash_prot(UCP *ucp, const uint *data)
    {
        bool retval = false;
        uint32_t passphrase_protect = 0;
        uint32_t passphrase_unprotect = 0xBEDA221E;
    
        if(*data == 0) { // unprotect
            retval = Write(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_UNPROTECT, 1, &passphrase_unprotect);
        } else { // protect
            retval = Write(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_UNPROTECT, 1, &passphrase_protect);
        }
        return retval;
    }
    
    bool Periph_fpga::flash_page(UCP *ucp, const uint *data, const uint len)
    {
        bool retval = false;
    
        wait_while_epcs_busy(ucp,1);
        // write address
        uint32_t addr = Flash_page_start * Flash_page_size_bytes;
        retval = Write(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_ADDR, 1, &addr);
    
        // write to FIFO
        retval = Write(ucp, "mm/0/REG_MMDP_DATA", 0, len, (uint32_t *)data);
    
        // write_write
        uint32_t d = 1;
        retval = Write(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_WRITE, 1, &d);
    
        return retval;
    }
    
    bool Periph_fpga::flash_pages(UCP *ucp, const uint *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(ucp, &data[(p*page_size_words)], page_size_words);
            Flash_page_start++;
        }
        return retval;
    }
    
    bool Periph_fpga::wait_while_epcs_busy(UCP *ucp, uint sleeptime)
    {
        uint32_t data;
        bool retval;
        //cout << "wait_while_epcs_busy:";
        for(int i=0; i<100; i++) {
            retval = Read(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_BUSY, 1, &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(UCP *ucp, 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;
        std::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(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_ADDR, 1, &s);
            // sector erase
            d = 1;
            retval = Write(ucp, "mm/0/REG_EPCS", FLASH_EPCS_REG_SECTOR_ERASE, 1, &d);
            wait_while_epcs_busy(ucp,50000);
        }
        return retval;
    }
    
    bool Periph_fpga::flash_erase(UCP *ucp)
    {
        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(ucp, s);
        }
        return retval;
    }
    
    string Periph_fpga::read_design_name(UCP *ucp)
    {
        uint32_t nof_design_name_regs = 4;
        uint32_t nvalues = nof_design_name_regs;
        //uint32_t *data = new uint32_t[nvalues * sizeof(uint32_t)+1];
        uint32_t data[20];
        memset((void *)data,0,20);
        bool retval = Read(ucp, "mm/0/unb2b/system/info", 2, nvalues, data);
        char *str_ptr = (char *)data;
        string name = string(str_ptr);
        //if(data != NULL) delete[] data;
        return (retval ? name : "? (error)");
    }
    
    string Periph_fpga::read_design_note(UCP *ucp)
    {
        uint32_t nof_design_name_regs = 4;
        uint32_t nvalues = nof_design_name_regs;
        //uint32_t *data = new uint32_t[nvalues * sizeof(uint32_t)+1];
        uint32_t data[20];
        memset((void *)data,0,20);
        bool retval = Read(ucp, "mm/0/unb2b/system/info", 20, nvalues,data);
        char *str_ptr = (char *)data;
        string note = string(str_ptr);
        //if(data != NULL) delete[] data;
        return (retval ? note : "? (error)");
    }
    
    bool Periph_fpga::read_stamps(UCP *ucp, TermOutput& termout)
    {
        uint32_t nof_regs = 3;
        uint32_t nvalues = nof_regs;
        uint32_t data[20];
        memset((void *)data,0,20);
        //uint32_t *data = new uint32_t[nvalues * sizeof(uint32_t)+1];
        bool retval = Read(ucp, "mm/0/unb2b/system/info", 15, nvalues, data);
    
        termout.strout << "    Stamp: date=" << data[0] << endl;
        termout.strout << "    Stamp: time=" << data[1] << endl;
        termout.strout << "    Stamp: svn=" << data[2] << endl;
        //if(data != NULL) delete[] data;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_temperature(UCP *ucp, TermOutput& termout, int format)
    {
        bool retval = true;
    
        uint32_t nvalues = 1;
        uint32_t data[20];
        memset((void *)data,0,20);
        //uint32_t *data = new uint32_t[nvalues * sizeof(uint32_t)];
        retval = Read(ucp, "mm/0/unb2b/fpga/temp", 0, nvalues, 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
        float temp = ((693. * (float)data[0]) / 1024.) - 265;
        char str[1000];
        sprintf(str,"  FPGA temperature = %.02f [degC]\n", temp); termout.strout << str;
        termout.valfloat[0] = temp;
        termout.nof_floats = 1;
        termout.nof_ints = 0;
        termout.datatype = format;
        my_current_temp = temp;
        //if(data != NULL) delete[] data;
        return retval;
    }
    
    bool Periph_fpga::read_fpga_scrap(UCP *ucp, TermOutput& termout)
    {
        bool retval = true;
    
        uint32_t nvalues = 1;
        uint32_t data[20];
        memset((void *)data,0,20);
        retval = Read(ucp, "mm/0/unb2b/scrap_ram/data", 0, nvalues, data);
    
        char str[1000];
        sprintf(str,"  Scrap RAM[0] = %d\n", data[0]); termout.strout << str;
        termout.valint[0] = data[0];
        termout.nof_floats = 0;
        termout.nof_ints = 1;
    
        return retval;
    }
    
    bool Periph_fpga::write_wdi_override(UCP *ucp, TermOutput& termout)
    {
        uint32_t data = 0xB007FAC7;
        return Write(ucp, "mm/0/wdi/wdi/reset_word", 0, 1, &data);
    }
    
    RegisterMap Periph_fpga::read_reg_map(UCP *ucp)
    {
        RegisterMap 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 << std::setw(8) << std::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);
    }