EVMC6678L時鐘主頻配置

本文從兩個方面來講解如何配置6678的CPU:


關於6678芯片時鐘初始化可以利用F:\ProgramFiles\ti\ccsv8\ccs_base\emulation\boards\evmc6678l\gel 文件夾下面的GEL文件來進行初始化,在Target Configuration.ccxml下Advanced中右邊Initialization script中加載6678的gel文件即可。

  1. 打開GEL文件,第42行註釋文件詳細解釋了CPU輸出頻率的計算,以及倍頻器與分頻器的設置:
​// The System PLL governs the device (CorePac) operating speed.  
//
// Each board designer defines the CLKIN frequency.  On the 
// TMDXEVM6678L,LE,LXE EVMs, the CLKIN frequency defined to 100MHz.  The 
// values for PLL1_M(39) and PLL1_D(1) defined below are pre-set 
// to provide a 1000MHz operating frequency on the EVMs.
//
// Other board designs using different CLKIN frequencies and/or 
// applications that requiring other operating frequecies, the PLL can 
// be configured by adjusting PLL1_M and PLL1_D per the 
// following formula:
//
//     Target Frequency (MHz) = 
//         input_clock (MHz) * [(PLL1_M + 1)]/ (2 * (PLL1_D + 1) )
//
// Table 2-13, "C66x DSP System PLL Configuration" in the device data sheet, 
// http://focus.ti.com/lit/ds/symlink/tms320c6678.pdf provides the suggested
// values for PLL1_M and PLL1_D for various input clocks and desired
// operating frequencies.
// 
// Please note that there might be multiple PLL1_M and PLL1_D
// values for the same Input clock and Desired Device Speed as long as the 
// multipliers and dividers are in the acceptable range. 
//
// More details on the PLL including the limitations on acceptable ranges
// for multipliers and dividers are in the PLL's user guide at
// http://www.ti.com/lit/sprugv2
// 
// The table provides some sample values of PLL1_M and PLL1_D:
//
// Please select PLL1_M values such that 0 < PLL1_M <= 64
// +--------------------+---------------+--------+--------+
// | (CLK)Desired       | (CLKIN) Input |        |        |
// | Device Speed (MHz) | Clock (MHz)   | PLL1_M | PLL1_D |
// +--------------------+---------------+--------+--------+
// | 1000               | 100           | 19     | 0      |
// | 1000               | 100 (EVM)     | 39     | 1      |
// | 1250               | 100           | 24     | 0      |
// | 1000               | 50            | 39     | 1      |
// | 1000               | 156.25        | 63     | 4      |
// +--------------------+---------------+--------+--------+
//
// +--------------------+---------------+--------+--------+
// | PA PLL VCO         | (CLKIN) Input |        |        |
// | Rate (MHz)         | Clock (MHz)   | PLL1_M | PLL1_D |
// +--------------------+---------------+--------+--------+
// | 1050               | 100.00 (EVM)  | 20     | 0      |
// | 1044               | 122.88        | 31     | 1      |
// | 1050               | 122.88        | 204    | 11     |
// | 1050               | 156.25        | 335    | 24     |
// +--------------------+---------------+--------+--------+
//
// +--------------------+---------------+--------+--------+
// | DDR3 PLL VCO       | (CLKIN) Input |        |        |
// | Rate (MHz)         | Clock (MHz)   | PLL1_M | PLL1_D |
// +--------------------+---------------+--------+--------+
// | 1333               | 66.667 (EVM)  | 19     | 0      |
// | 1066               | 66.667        | 31     | 1      |
// | 800                | 66.667        | 11     | 0      |
// +--------------------+---------------+--------+--------+​

從註釋文件中可知,CPU的頻率 = 輸入時鐘頻率 * [(PLL1_M + 1)] / (2 * (PLL1_D + 1) ),例如當輸入時鐘頻率(Input Clock)爲100Mhz時,若設置PLL1_M爲24,PLL1_D爲0,則得到的輸出時鐘頻率爲1250MHZ。定位文件到第857行,

這裏的Init_PLL函數爲配置CPU時鐘頻率的入口函數,入口參數爲倍頻值,分頻值,默認GEL文件中在第101行定義的爲39,1:

#define PLL1_M 39  
#define PLL1_D 1


#define DDR3_BASE_ADDRESS            0x80000000

打開Init_PLL(int pll_mult, int pll_div )函數:

hotmenu Init_PLL(int pll_mult, int pll_div )
{
    int i, TEMP;
    /* Default dividers */
    unsigned int div2=3, div5=5, div8=64;
    int dsp_freq;
    int dsp_freM,dsp_freD;

    GEL_TextOut ( "Main PLL (PLL1) Setup ... \n");
    /*Unlock Boot Config*/
    KICK0 = 0x83E70B13;
    KICK1 = 0x95A4F1E0;

    // Only core0 can set PLL
    if (DNUM == 0)
    {

        /* 1. Wait for Stabilization time (min 100 us)                                *
         * The below loop is good enough for the Gel file to get minimum of        *
         * 100 micro seconds, this should be appropriately modified for port       *
         * to a C function                                                         *
         * Minimum delay in GEL can be 1 milli seconds, so program to 1ms=1000us,  *
         * more than required, but should be Okay                                  */
        Delay_milli_seconds(1);

        /* 2. Check the status of BYPASS bit in SECCTL register,                   *
         *    execute following steps if                                           *
         *    BYPASS == 1 (if bypass enabled), if BYPASS==0 then Jump to Step 3    */
        TEMP = PLL1_SECCTL &  0x00800000; /* Check the Bit 23 value */

        if (TEMP != 0) /* PLL BYPASS is enabled, we assume if not in Bypass ENSAT = 1 */
        {
            GEL_TextOut ( "PLL in Bypass ... \n");

            /* 2a. Usage Note 9: For optimal PLL operation, the ENSAT bit in the PLL control *
             * registers for the Main PLL, DDR3 PLL, and PA PLL should be set to 1.          *
             * The PLL initialization sequence in the boot ROM sets this bit to 0 and        *
             * could lead to non-optimal PLL operation. Software can set the bit to the      *
             * optimal value of 1 after boot                                                 *
             * Ref: http://www.ti.com/lit/er/sprz334b/sprz334b.pdf                           *
             * |31...7   |6     |5 4       |3...0      |                                     *
             * |Reserved |ENSAT |Reserved  |BWADJ[11:8]|                                     */

            MAINPLLCTL1 = MAINPLLCTL1 | 0x00000040;

            /* 2b. Clear PLLEN bit */
            PLL1_PLLCTL &= ~(1 << 0);
            
            /* 2c. Clear PLLENSRC bit */
            PLL1_PLLCTL &= ~(1 << 5);

            /* 2d. Wait for 4 RefClks   *
             * Assuming slowest Ref clock of 25MHz, min: 160 ns delay */
            Delay_milli_seconds(1);

            /* 2e. Bypass needed to perform PWRDN cycle for C6670 and C6678                  *
             * Needed on all devices when in NOBOOT, I2C or SPI boot modes                   *
             * Ref: Figure 4-2 of http://www.ti.com/lit/ug/sprugv2a/sprugv2a.pdf             *
             * PLL Secondary Control Register (SECCTL)  Layout                               *
             * |31...24  |23     |22...19       |18...0   |                                  *
             * |Reserved |BYPASS |OUTPUT DIVIDE |Reserved |                                  */
            PLL1_SECCTL |= 0x00800000; /* Set the Bit 23 */

            /* 2f. Advisory 8: Multiple PLLs May Not Lock After Power-on Reset Issue         *
             * In order to ensure proper PLL startup, the PLL power_down pin needs to be     *
             * toggled. This is accomplished by toggling the PLLPWRDN bit in the PLLCTL      *
             * register. This needs to be done before the main PLL initialization            *
             * sequence                                                                      *
             * Ref: Figure 4-1 of http://www.ti.com/lit/ug/sprugv2a/sprugv2a.pdf             *
             * PLL Control Register (PLLCTL)  Layout                                         *
             * |31...4   |3      |2        |1        |0        |                             *
             * |Reserved |PLLRST |Reserved |PLLPWRDN |Reserved |                             */

            PLL1_PLLCTL   |= 0x00000002; /*Power Down the PLL */

            /* 2g. Stay in a loop such that the bit is set for 5 �s (minimum) and           *
             * then clear the bit.                                                          */

            Delay_milli_seconds(1); /* This is more than required delay */  

            /* 2h. Power up the PLL */  
            PLL1_PLLCTL   &= ~(0x00000002); 

        }
        else
        {
            /* 3. Enable BYPASS in the PLL controller */
            GEL_TextOut ( "PLL not in Bypass, Enable BYPASS in the PLL Controller... \n");
            
            /* 3a. Clear PLLEN bit (bypass enabled in PLL controlelr mux) */
            PLL1_PLLCTL &= ~(1 << 0);
            
            /* 3b. Clear PLLENSRC bit (enable PLLEN to control PLL controller mux) */
            PLL1_PLLCTL &= ~(1 << 5);
            
            /* 3c. Wait for 4 RefClks (to make sure the PLL controller *
             * mux switches properly to the bypass)                    *
             * Assuming slowest Ref clock of 25MHz, min: 160 ns delay  */
            Delay_milli_seconds(1);
        }

        /* Wait for PLL Reset assertion Time (min: 50 us)                          *
         * Minimum delay in GEL can be 1 milli seconds, so program to 1ms=1000us,  *
         * more than required, but should be Okay                                  */
        //Delay_milli_seconds(1);

        /* 4, 5, 6 and 7 are done here:                                              *
         * Program the necessary multipliers/dividers and BW adjustments             */
        prog_pll1_values(pll_mult, pll_div, 1);

        /* 8. Set PLL dividers if needed */
        
        /* part of 8, go stat bit needs to be zero here                             *
         * Read the GOSTAT bit in PLLSTAT to make sure the bit returns to 0 to      *  
         * indicate that the GO operation has completed                             */
        /* wait for the GOSTAT, but don't trap if lock is never read */
        for (i = 0; i < 1000; i++)
        {
            if ( (PLL1_STAT & 0x00000001) == 0 ) {
                break;
            }
        }
        if ( i == 1000 ) {
            GEL_TextOut ( "Error while waiting for GOSTAT bit returning to 0 ... \n");
            return(-1);
        }

        /* part of 8, Set PLL dividers if needed */
        PLL1_DIV2 = (0x8000) | (div2 - 1);
        PLL1_DIV5 = (0x8000) | (div5 - 1);
        PLL1_DIV8 = (0x8000) | (div8 - 1);

        /* part of 8, Program ALNCTLn */
        /* Set bit 1, 4 and 7 */
        PLL1_ALNCTL |= ( (1 << 1) | (1 << 4) | (1 << 7));

        /* part of 8, Set GOSET bit in PLLCMD to initiate the GO operation to change the divide *
         * values and align the SYSCLKs as programmed                                           */
        PLL1_CMD     |= 0x00000001;

        /* part of 8, Read the GOSTAT bit in PLLSTAT to make sure the bit returns to 0 to      *  
         * indicate that the GO operation has completed                             */

        /* wait for the GOSTAT, but don't trap if lock is never read */
        for (i = 0; i < 1000; i++)
        {

            if ( (PLL1_STAT & 0x00000001) == 0 ) {
                break;
            }

        }

        if ( i == 1000 ) {
            GEL_TextOut ( "Error while waiting for GOSTAT bit returning to 0 ... \n");
            return(-1);
        }

        /* 9. Place PLL in Reset, In PLLCTL, write PLLRST = 1 (PLL is reset)         */
        PLL1_PLLCTL |= 0x00000008;
        
        /* 10. Wait for the PLL Reset duration time (min: 7us    )                */
        Delay_milli_seconds(1);

        /* 11. In PLLCTL, write PLLRST = 0 to bring PLL out of reset */
        PLL1_PLLCTL &= ~(0x00000008);

        /* 
         * 12. PLL Lock Delay needs to be 500 RefClk periods * (PLLD + 1)
         * i.e., Wait for at least 500 * CLKIN cycles * (PLLD + 1) (PLL lock time)
         * Using 2000 25ns RefClk periods per DM
         * Wait for PLL to lock min 50 micro seconds
         * 
         * */
        Delay_milli_seconds(1);

        /* 13. In SECCTL, write BYPASS = 0 to enable PLL mux to switch to PLL mode */
        PLL1_SECCTL &= ~(0x00800000); /* Release Bypass */

        /* 14. Set the PLLEN */
        PLL1_PLLCTL |= (1 << 0);
        
        /* 15. The PLL and PLL Controller are now initialized in PLL mode - Complete */

        // Compute the real dsp freq (*100)
        dsp_freq = (((REF_CLOCK_KHZ/10) * ((pll_mult+1)/2))/(pll_div+1));

        // Displayed frequency setup
        // dsp freq in MHz
        dsp_freM = dsp_freq / 100;

        // dsp freq first decimal if freq expressed in MHz
        dsp_freD = ((dsp_freq - dsp_freM * 100) + 5) / 10;

        // Add roundup unit to MHz displayed and reajust decimal value if necessary...
        if (dsp_freD > 9)
        {
            dsp_freD = dsp_freD - 10;
            dsp_freM = dsp_freM + 1;
        }

        // Print freq info...
        GEL_TextOut( "PLL1 Setup for DSP @ %d.%d MHz.\n",,,,, dsp_freM, dsp_freD );
        GEL_TextOut( "           SYSCLK2 = %f MHz, SYSCLK5 = %f MHz.\n",,,,, ((float)(dsp_freq/100)/div2), ((float)(dsp_freq/100)/div5));
        GEL_TextOut( "           SYSCLK8 = %f MHz.\n",,,,, ((float)(dsp_freq/100)/div8));
        GEL_TextOut( "PLL1 Setup... Done.\n" );
        return (0);
    }
    else
    {
        GEL_TextOut("DSP core #%d cannot set PLL1.\n",,2,,,DNUM);
        return(-1);
    }
}

在GEL文件中第1229-1233行代碼知道F(MHZ) = dsp_freq / 100; dsp_freq = (((REF_CLOCK_KHZ/10) * ((pll_mult+1)/2))/(pll_div+1));其中REF_CLOCK_KHZ定義在第108行中爲100000;根據默認傳入的參數39,1,通過計算可得頻率爲1000MHZ。接下來打開prog_pll1_values(pll_mult, pll_div, 1)函數,該函數主要是對PLLM寄存器和MAINPLLCTL0寄存器進行配置。

/* Set the PLL Multiplier, Divider, BWADJ                                    *
     * The PLLM[5:0] bits of the multiplier are controlled by the PLLM Register  *
     * inside the PLL Controller and the PLLM[12:6] bits are controlled by the   *
     * chip-level MAINPLLCTL0 Register.                                          *
     * PLL Control Register (PLLM)  Layout                                       *
     * |31...6   |5...0        |                                                 *
     * |Reserved |PLLM         |                                                 *
     *                                                                           *
     * Main PLL Control Register (MAINPLLCTL0)                                   *
     * |31...24   |23...19   |18...12    | 11...6   |5...0 |                     *
     * |BWADJ[7:0]| Reserved |PLLM[12:6] | Reserved | PLLD |                     */

    /* Set pll multipler (13 bit field) */
    PLL1_PLLM     = (pll_multiplier & 0x0000003F); /* bits[5:0]  */
    TEMP          = (pll_multiplier & 0x1FC0) >> 6;/* bits[12:6] */
    MAINPLLCTL0  &=~(0x0007F000);                /*Clear PLLM field */
    MAINPLLCTL0  |=((TEMP << 12) & 0x0007F000);

   /* Set the BWADJ     (12 bit field)                                          *
     * BWADJ[11:8] and BWADJ[7:0] are located in MAINPLLCTL0 and MAINPLLCTL1     *
     * registers. BWADJ[11:0] should be programmed to a value equal to half of   *
     * PLLM[12:0] value (round down if PLLM has an odd value)                    *
     * Example: If PLLM = 15, then BWADJ = 7                                     */
    TEMP = ((pll_multiplier + 1) >> 1) - 1; /* Divide the pllm by 2 */
    MAINPLLCTL0 &=~(0xFF000000);  /* Clear the BWADJ Field */
    MAINPLLCTL0 |=  ((TEMP << 24) & 0xFF000000);
    MAINPLLCTL1 &=~(0x0000000F);   /* Clear the BWADJ field */
    MAINPLLCTL1 |= ((TEMP >> 8) & 0x0000000F);

    /* Set the pll divider (6 bit field)                                         *
     * PLLD[5:0] is located in MAINPLLCTL0                                       */
    MAINPLLCTL0   &= ~(0x0000003F);    /* Clear the Field */
    MAINPLLCTL0   |= (pll_divider & 0x0000003F);

    /* Set the OUTPUT DIVIDE (4 bit field) in SECCTL */
    PLL1_SECCTL    &= ~(0x00780000);     /* Clear the field       */
    PLL1_SECCTL   |= ((odiv << 19) & 0x00780000) ;

需要注意的是,倍頻器中的PLLM[5:0]是在PLL控制器中的PLL寄存器所控制(PLLM),該寄存器的詳細說明文檔在KeyStone Architecture Phase-Locked Loop (PLL)手冊中:

代碼:PLL1_PLLM  = (pll_multiplier & 0x0000003F) 表示將倍頻值與3F相與,結果存入PLL1_PLLM地址中。

而倍頻器中的PLLM[12:6]爲是在主PLL寄存器所控制(MAINPLLCTL0),MAINPLLCTL0的說明文檔在SPRS691E中列出:

TEMP = (pll_multiplier & 0x1FC0) >> 6;/* bits[12:6] */
MAINPLLCTL0  &=~(0x0007F000);  /*Clear PLLM field */
MAINPLLCTL0  |=((TEMP << 12) & 0x0007F000);

將倍頻值與0x1FC0相與右移6位放置低6位存儲於TEMP中,MAINPLLCTL0  寄存器中值進行取反相與清零處理,將TEMP值進行左移12位移動到PLLM[12:6]處,並於7F000相與,確保該寄存器中其他值位0,因此導致PLLD[5:0]與BWADJ[7:0]全爲零。從這裏看出應該先配置PLLM,再配置BWADJ,最後配置PLLD;

關於BWADJ配置:

TEMP = ((pll_multiplier + 1) >> 1) - 1; /* Divide the pllm by 2 */
MAINPLLCTL0 &=~(0xFF000000);  /* Clear the BWADJ Field */
MAINPLLCTL0 |=  ((TEMP << 24) & 0xFF000000);
MAINPLLCTL1 &=~(0x0000000F);   /* Clear the BWADJ field */
MAINPLLCTL1 |= ((TEMP >> 8) & 0x0000000F);

BWADJ域由BWADJ[11:8]和BWADJ[7:0]構成,分別位於MAINPLLCTL1和MAINPLLCTL0寄存器中,其中,BWADJ[11:0]寄存器組合值必須設置爲PLLM[12:0]值的一半,向下取整,得到TEMP值。BWADJ[7:0]位於MAINPLLCTL0寄存器中的第24-31位,因此需要將TEMP值左移24位並於0xFF000000相與,清空TEMP中其他位,再和MAINPLLCTL0寄存器相或,避免之前設置的PLLM[12:6]被改變。對於MAINPLLCTL1寄存器中的BWADJ[11:8]域,通過手冊可知,BWADJ位於MAINPLLCTL1中的第0到第3位。因此需要將TEMP值中BWADJ[11:8]右移8位到低位處再於0xF相與清空TEMP其他位並於原始MAINPLLCTL1寄存器中值相或。

分頻器 PLLD[5:0]位於MAINPLLCTL0寄存器中,因此先清空MAINPLLCTL0中PLLD區域,再將分頻值pll_divider與0x3F相與並於原始MAINPLLCTL0寄存器相或更新PLLD[5:0]中值。


  2. 第二種配置時鐘的方法是通過platform_init()函數進行配置;該函數文件位於F:\ProgramFiles\ti\pdk_c667x_2_0_9\packages\ti\platform\evmc6678l\platform_lib\src文件夾下面。

            memset(&pllc_hwSetup, 0, sizeof(PllcHwSetup));

            /* Setup PLLC hardware parameters */
            pllc_hwSetup.divEnable  = (CSL_BitMask32) (PLLC_DIVEN_PLLDIV2 |
                                                       PLLC_DIVEN_PLLDIV5 |
                                                       PLLC_DIVEN_PLLDIV8) ;

            /* Setup PLLC hardware parameters */        
            pllc_hwSetup.pllM       = 
                    (((p_config->pllm) ? p_config->pllm : PLATFORM_PLL1_PLLM_val) - 1);
            pllc_hwSetup.preDiv   = PLATFORM_PLL_PREDIV_val - 1;
            pllc_hwSetup.pllDiv2  = PLATFORM_PLLDIV2_val - 1;
            pllc_hwSetup.pllDiv5  = PLATFORM_PLLDIV5_val - 1;
            pllc_hwSetup.pllDiv8  = PLATFORM_PLLDIV8_val - 1;
            pllc_hwSetup.postDiv  = PLATFORM_PLL_POSTDIV_val -1;

            /* set Pll */
            status = CorePllcHwSetup (&pllc_hwSetup);

 定位到函數內部第384行可知,這裏的倍頻係數設置值爲pllM = PLATFORM_PLL1_PLLM_val - 1,分頻值爲preDiv = PLATFORM_PLL_PREDIV_val - 1。打開這兩個變量的定義在F:\ProgramFiles\ti\pdk_c667x_2_0_9\packages\ti\platform\evmc6678l\platform_lib\include下的platform_internal.h文件中。

/* Clock rate */
#define PLATFORM_BASE_CLK_RATE_MHZ (100)

/* PREDIV */
#define PLATFORM_PLL_PREDIV_val (1)

/* POSTDIV */
#define PLATFORM_PLL_POSTDIV_val (2)

/* Default PLL PLLM value (100/1*(20/2)) = 1.0GHz) */
#define  PLATFORM_PLL1_PLLM_val (25)//25 = 1.2

如代碼所示,基準頻率設置的是100,PLATFORM_PLL_PREDIV_val爲1,PLATFORM_PLL1_PLLM_val爲25(默認爲20);因爲這兩個變量均減去1,因此實際傳入到prog_pll1_values函數中的值爲0,24;之後的寄存器配置和第一部分介紹相同。 配置完成後在platform.c文件中的第458行代碼platform_get_frequency()函數中計算實際頻率值,打開該函數:

static inline uint32_t platform_get_frequency(void)
{
    CSL_Status      status;
    PllcHwSetup     hwSetupRead;
    uint32_t        dsp_freq;
    
    status = CorePllcGetHwSetup (&hwSetupRead);

    if (status != CSL_SOK) {
        IFPRINT(platform_write("platform_get_info: Hardware setup parameters reading... Failed.\n"));
        IFPRINT(platform_write("\tReason: Error setting in hardware validation."\
                " [status = 0x%x].\n", status));
        platform_errno = PLATFORM_ERRNO_GENERIC;
        return (uint32_t)-1;
    } else {
        /* Compute the real dsp freq (*100) */
        dsp_freq = (hwSetupRead.pllM + 1)>> 1;
        dsp_freq = (dsp_freq * PLATFORM_BASE_CLK_RATE_MHZ)/(hwSetupRead.preDiv + 1);        
    }
    printf("dsp_freq: %dHZ\n",dsp_freq);

    return (dsp_freq);
    
}

 從else分支語句中看出頻率值 = [100 * (pllM + 1) / 2] / ( preDiv + 1 ) = 100 * ( 24 + 1 ) / 2 / 1 = 1250Hz

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章