This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DRA821U: Get CPU Die Temperature in QNX 7.1

Part Number: DRA821U
Other Parts Discussed in Thread: DRA821

Hi,

We are currently evaluating the DRA821 processor, as a replacement for an obsolete x86 processor, running QNX 7.1.

Part of the evaluation includes being able to read the CPU Die Temperature from our software, every few seconds.

We've tried to build up the example vtm_pvt_sensor_read, but it doesn't run on the chip, and the structure of the code is far more complex than we were expecting to just read the temperature.

We also tried to read directly from the WKUP_VTM_TMPSENS_CTRL_1 using HW_RD_REG32, but it caused a segfault immediately. 

Is there a simple example of how to read the temperature of the CPU on regular intervals to collect as part of a larger stress test? 

Thanks for any help with this!

Michael. 

  • Hi Michael,

    The registers can be read directly from the A72 as needed, if your vtm_pvt_sensor_read() is failing it likely has to do with how the memory is being mapped.  QNX requires that physical address be mapped to a virtual address.  See - mmap_device_memory() (qnx.com), and reference other examples in PSDK QNX release.

    The existing code in the PDK can be ported to A72 and provides conversion logic of the values that are being read from the registers.

    Regards,

    kb

  • Hi KB,

    I'm not sure where that read is done in the examples? There is a lot of code involved in Board Setup, then some EFUSE setup. How much of this is required to read the CPU temperature? I can't find any reference to mmap_device_memory in the examples for vtm_pvt_sensor_read or other examples. 

    Kind regards,

    Michael.

  • Hi kb,

    I've done some further investigation. I've found I can use mmap_device_memory, as you say. However, I'm having trouble determining which register address I am meant to map for the A72 temperature. There are the CSL_VTM_CFG1_TMPSENS_STAT_DATA_OUT_MASK, _SHIFT, and _MAX registers, which seem to be closest to what is read. Do I just need to map that register, convert the result, as shown in the example, and I should have temperature? 

    Kind regards,

    Michael. 

  • Hi Michael,

    Below is some pseudo code which can be referenced.  Code is intended to show at a high level one way to make use of the existing PDK code.  The code below would require modifications to become functional but has been run successfully on QNX on older SDKs.

    In regards to registers to be read, please reference CSL_vtmGetAdcCode(), in the PDK which shows the ADC registers values being read, and sequence to follow.   In the TRM the Section 5.2.2.1.5 Voltage and Thermal Manager (VTM) can be referenced.

    Regards,

    kb

    Main function, parses command line options, maps memory and loops reading the sensors:

    int32_t main(int argc, char *argv[])
    {
    
        /* Parse options */
        options (argc, argv);
    
        /*/ Map the VTM registers */
        vtm_map_memory();
    
        /* Loop forever */
        while (1)
        {
            (void) csl_vtm_pvt_sensor_read();
    
                    pthread_mutex_unlock( &vtm_mutex );
    
                delay(poll_interval_ms);
    
        }
    }
    

    Example function to parse command line options:

    /*
     *  options
     *
     *  This routine handles the command line options.
     *  For this simple example:
     *      -v      verbose operation
     *      -i      polling interval in milliseconds
     *      -o      Optionally, output to stdout
    */
    
    void options (int argc, char *argv[])
    {
        int     opt;
    
         optv = 0;
    
        while ((opt = getopt (argc, argv, "vi:o")) != -1) {
            switch (opt) {
            case 'v':
                optv++;
                break;
            case 'o':
                output_to_stdout = 1;
                break;
            case 'i':
                poll_interval_ms = atoi(optarg);
                break;
    
            }
        }
    }
    

    Example function showing mapping the memory space for the VTM registers to be read:

    uintptr_t CSL_VTM_TEST_CFG1_BASE = 0;
    uintptr_t CSL_VTM_TEST_CFG2_BASE = 0;
    
    void vtm_map_memory(void)
    {
    
            /* Virtual Address for base address of WKUP_VTM0_MMR_VBUSP_CFG1 */
            CSL_VTM_TEST_CFG1_BASE = (uintptr_t) mmap_device_memory(0,
                            CSL_WKUP_VTM0_MMR_VBUSP_CFG1_SIZE,
                            PROT_READ|PROT_WRITE|PROT_NOCACHE, 0,
                            CSL_WKUP_VTM0_MMR_VBUSP_CFG1_BASE);
    
            if (CSL_VTM_TEST_CFG1_BASE == 0)
            {
                    printf("mmap_device_io failed for VTM CFG1\n");
                    exit(-1);
            }
    
            /* Virtual Address for base address of WKUP_VTM0_MMR_VBUSP_CFG2 */
            CSL_VTM_TEST_CFG2_BASE = (uintptr_t) mmap_device_memory(0,
                            CSL_WKUP_VTM0_MMR_VBUSP_CFG2_SIZE,
                            PROT_READ|PROT_WRITE|PROT_NOCACHE, 0,
                            CSL_WKUP_VTM0_MMR_VBUSP_CFG2_BASE);
            if (CSL_VTM_TEST_CFG2_BASE == 0)
            {
                    printf("mmap_device_io failed for VTM CFG2\n");
                    exit(-1);
            }
    }

    Function showing the PDK code making use of the memory addresses which were mapped:

    void csl_vtm_pvt_sensor_read (void)
    {
        int32_t temp_milli_degrees_read, ts_id;
        CSL_vtm_adc_code       adc_code;
        CSL_vtm_cfg1Regs       *p_vtm_cfg1_regs;
        CSL_vtm_cfg2Regs       *p_vtm_cfg2_regs;
        CSL_vtm_tsStat_read_ctrl tsStat_read_ctrl = CSL_VTM_TS_READ_DATA_OUT_VAL;
        CSL_vtm_tsStat_val       ts_stat_val;
        CSL_vtm_ts_ctrl_cfg     ts_ctrl_cfg;
        int32_t                 retVal = CSL_PASS;
        struct timespec get_sensors;
        char tmpString[32];
            int offset = 0;  // offset into buffer
    
    
        p_vtm_cfg1_regs = (CSL_vtm_cfg1Regs *) CSL_VTM_TEST_CFG1_BASE;
        p_vtm_cfg2_regs = (CSL_vtm_cfg2Regs *) CSL_VTM_TEST_CFG2_BASE;
    
        for (ts_id = 0; ts_id < VTM_NUM_SENSORS; ts_id++)
        {
    
            ts_ctrl_cfg.valid_map = CSL_VTM_TS_CTRL_MAXT_OUTG_ALERT_VALID   |
                                    CSL_VTM_TS_CTRL_RESET_CTRL_VALID        |
                                    CSL_VTM_TS_CTRL_SOC_VALID               |
                                    CSL_VTM_TS_CTRL_MODE_VALID;
    
            (void) CSL_vtmTsGetCtrl (p_vtm_cfg2_regs,
                             ts_id,
                             &ts_ctrl_cfg);
    
            ts_ctrl_cfg.valid_map = CSL_VTM_TS_CTRL_RESET_CTRL_VALID        |
                                    CSL_VTM_TS_CTRL_SOC_VALID               |
                                    CSL_VTM_TS_CTRL_MODE_VALID;
    
            ts_ctrl_cfg.adc_stat   = CSL_VTM_TS_CTRL_SINGLESHOT_ADC_CONV_IN_PROGRESS;
            ts_ctrl_cfg.mode       = CSL_VTM_TS_CTRL_CONTINUOUS_MODE;
            ts_ctrl_cfg.tsReset    = CSL_VTM_TS_CTRL_SENSOR_NORM_OP;
            (void) CSL_vtmTsSetCtrl (p_vtm_cfg2_regs,
                                     ts_id,
                                     &ts_ctrl_cfg,
                                     FALSE);
        }
    
            // Initialize buffer
            memset(buffer, 0x0, 2048);
    
    
            // Get number of (s) since system booted
            if( clock_gettime( CLOCK_REALTIME, &get_sensors) == -1 ) {
                    exit(-1);
            }
    
            // Optionally output to stdout
            if(output_to_stdout)
            {
                    printf("%05ld ",get_sensors.tv_sec);
            }
    
            // Add timestamp to buffer
            memset(tmpString, 0x0, sizeof(tmpString));
            sprintf(tmpString,"Time(s) : %05ld\n", get_sensors.tv_sec);
            strncpy(buffer + offset, tmpString, strlen(tmpString));
            offset += strlen(tmpString);
    
    
            for (ts_id = 0; ts_id < VTM_NUM_SENSORS; ts_id++)
            {
    
                    retVal = CSL_vtmTsGetSensorStat (p_vtm_cfg1_regs, &tsStat_read_ctrl, \
                                    ts_id, &ts_stat_val);
                    if(retVal == CSL_EBADARGS)
                    {
                            printf("CSL_vtmTsGetSensorStat failed\n");
                            exit(-1);
                    }
    
                    adc_code = ts_stat_val.data_out;
    
                    (void) CSL_vtmTsConvADCToTemp (adc_code,
                                    (CSL_vtm_tmpSens_id)     ts_id,
                                    &temp_milli_degrees_read);
                    if(output_to_stdout)
                    {
                            printf ("%d ", temp_milli_degrees_read);
                    }
    
                    // Add sensor reading to buffer
                    memset(tmpString, 0x0, sizeof(tmpString));
                    sprintf(tmpString,"S%d : %05d C\n", ts_id, temp_milli_degrees_read);
                    strncpy(buffer + offset, tmpString, strlen(tmpString));
                    offset += strlen(tmpString);
    
            }
            if(output_to_stdout)
            {
                    printf("\n");
            }
    }
    

  • Hi kb, 

    The memory mapping step there helped a lot, thank you! I can read temperatures successfully now.

    Just a quick clarify, I'm getting S0 to S4, am I right in guessing S0 is package temp, S1 and S2 are core temps, and S3 and S4 are invalid because I only have a dual core processor? 

    Thanks again and kind regards,

    Michael.