dm348_再论不平等

dm348_再论不平等UBL前两篇文章中我们有提到过了。参看:DM368开发–文件烧写参看:DM368开发–Bootloader开发简单点来说,UBL主要功能是实现将U-boot代码拷贝到DDR2内存中,建立运行环境并引导U-boot。但是现在我有个问题,我想让DM368开发板运行在432Mhz,目前它的状态是AR,297Mhz、DDR为243Mhz。具体操作为,修改/ps

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

参看:UBI – Unsorted Block Images

参看:DM36x UBL PLL配置

UBL 前两篇文章中我们有提到过了。

参看:DM368开发 — 文件烧写

参看:DM368开发 — Bootloader 开发

简单点来说,UBL 主要功能是实现将 U-boot 代码拷贝到 DDR2 内存中,建立运行环境并引导U-boot。


但是现在我有个问题,我想让 DM368 开发板运行在 432 Mhz,目前它的状态是 AR, 297 Mhz 、DDR 为243Mhz。

具体操作为,修改 /psp/flash-utils/DM36x/Common/Src/device.c  相关的设置。

一、修改内核电源电压

当然,DM368主频大小除了与UBL有关外,还与ARM内核电源电压有关。 当为1.2v时,主频不能达到432Mhz的,只有为1.35v的时候才能达到。所以需要将 ARM 内核电源电压设为1.35v。

dm348_再论不平等

我用的电源管理芯片是TVP65023RSB,这个芯片该如何配置,使其生成1.35v呢?
我使用 I2C 工具 ,可以看到 0x48
The TPS65023 has a 7-bit address:?1001000, other addresses are available upon contact with the factory
则 tps64023的设备地址应为 0x48 ,说明有这个 I2C 设备的。


root@dm368-evm:~# i2cdetect -y 1 
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- 08 -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- UU -- -- 
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 
70: -- -- -- UU -- -- -- --

通过i2ctool修改寄存器: 主要有关的寄存器为05和06如下图:

i2cset -f -y 1 0x48 0x05 0x80
i2cget -f -y 1 0x48 0x05

i2cset -f -y 1 0x48 0x06 0x16
i2cget -f -y 1 0x48 0x06

OK!!!   有电压1.35v。

dm348_再论不平等
dm348_再论不平等
到这步又有问题了,通过I2Ctool可以获得1.35v,但是掉电后会恢复默认的1.2v。
上面的使用i2c的方法不适用,只能使用加电阻分压器的方法。具体参看datasheet。或者硬件设计的时候换一种电源管理芯片。  最后的解决也正是用了后者。

二、修改 UBL 源码

修改电压以后,就可以烧写 ubl_DM36x_ARM432_DDR340_OSC24_NAND.bin,这个可以在网上找到的。
dm348_再论不平等
问题是如果自行,修改 /psp/flash-utils/DM36x/Common/Src/device.c  相关的设置,该如果操作。

About this Guide

This is reference guide to port UBL for DM368 using DM365 UBL code base. This guide helps to configure PLLs to run ARM@432 and DDR@340 with 24MHz crystal oscillator. Modify PLL1, PLL2 and DDR setup function in DM36x/Common/src/device.c as explained in below sections.

PLL1 Setup

Modify DEVICE_PLL1Init(PLL1_Mult) function for 680MHz by replacing configurations of PLL1 multipliers, pre and post divisors as below

    //Program the Multiper and Pre-Divider for PLL1
 PLL1->PLLM   =   0x55;   // VCO will 24*2M/N+1
 PLL1->PREDIV =   0x8000|0x5;
 
     // Post divider setting for PLL1
 
         PLL1->PLLDIV1 = 0x801B;
         PLL1->PLLDIV2 = 0x8001;
         PLL1->PLLDIV3 = 0x8001;   // POST DIV 680/2 = 340Mhz  -> MJCP and HDVICP bus interface clock
         PLL1->PLLDIV4 = 0x8003;   // POST DIV 680/4 = 170Mhz  -> EDMA/Peripheral CFG0(1/2 MJCP/HDVICP bus interface clk)
         PLL1->PLLDIV5 = 0x8001;   // POST DIV 680/2 = 340Mhz  -> VPSS
         PLL1->PLLDIV6 = 0x8008;   // POST DIV 680/9 = 75.6 Mhz -> VENC
 
 
         PLL1->PLLDIV7 = 0x8000;   // POST DIV 680/1 = 680Mhz -> DDRx2(with internal divider of 2, clock boils down to 340 Mhz)
         PLL1->PLLDIV8 = 0x8006;   // POST DIV 680/7= 97Mhz-> MMC0/SD0
         PLL1->PLLDIV9 = 0x801B;   // POST DIV 680/28 = 24.3Mhz-> CLKOUT

PLL2 Setup

Modify DEVICE_PLL2Init()function for 432MHz by replacing configurations PLL2 multipliers, pre and post divisor as below

  //Program the Multiper and Pre-Divider for PLL2
     PLL2->PLLM   =   0x9;   // VCO will 24*2M/N+1 = 432Mhz
     PLL2->PREDIV =   0x8000|0x0;
 
  // Post divider setting for PLL2
     PLL2->PLLDIV1 = 0x8011;
     PLL2->PLLDIV2 = 0x8000;   // POST DIV 432/1=432 Mhz  -> ARM926/(HDVICP block) clk
     PLL2->PLLDIV3 = 0x8001;
     PLL2->PLLDIV4 = 0x8014;   // POST DIV 432/21= 20.5714 Mhz->VOICE Codec clk
     PLL2->PLLDIV5 = 0x800F;   // POST DIV 432/16=27 Mhz  -> VENC(For SD modes, requires )

NOTE

  1. For configuration details of PLL Controllers (PLL1 and PLL2) look into TMS320DM36x ARM Subsystem Reference Guide (literature number SPRUFG5).
  2. Make sure PLLC1_SYSCLK6 is selected for HD modes and is done by configuring VPSS_CLK_CTRL register(i.e. VPSS_CLK_CTRL = 0x18) in drivers/media/video/davinci/davinci_platform.c. VENC requires clock of 74.25Mhz for HD modes to work properly. HD output is tested on TV with above derived clock of 68Mhz.
  3. Make sure PLLC2_SYSCLK5 is selected for SD modes and is done by configuring VPSS_CLK_CTRL register(i.e. VPSS_CLK_CTRL = 0x38) in drivers/media/video/davinci/davinci_platform.c. VENC requires clock of 27Mhz for SD modes to work properly.

DDR2 Setup

Modify DEVICE_DDR2Init() for 340MHZ by replacing SDR bank and timing configurations as below

   DDR->DDRPHYCR = 0x000000C6;
   DDR->SDBCR = 0x00D34A32;        //Program SDRAM Bank Config Register
   DDR->SDBCR = 0x0053CA32;
 
   DDR->SDTIMR  =0x576D7D12;       //Program SDRAM Timing Control Register1
   DDR->SDTIMR2 =0x422EC742;       //Program SDRAM Timing Control Register2
 
   DDR->PBBPR = 0x000000FE;
 
   DDR->SDBCR = 0x08534832;        //Program SDRAM Bank Config Register
 
   DDR->SDRCR = 0xA5C;             //Program SDRAM Refresh Control Register

NOTE
The timing configuration done above are specific to DDR part present on the evm’s. Please refer DDR device data sheet for information.

Build steps

  1. Open DM36x/CCS/UBL/UBL.pjt UBL project.
  2. Make sure you selected BOOT_NAND configuration from active configuration drop down.
  3. Build the project and new binary at DM36x\CCS\UBL\UBL_DM36x_NAND.bin does the PLL configurations to run ARM@432 and DDR@340.

我的修改部分:

	//Program the Multiper and Pre-Divider for PLL1
	//cjk add down
	// PLL1->PLLM   =   0x51;   // VCO will 24*2M/N+1 = 486Mhz
	//PLL1->PREDIV =   0x8000|0x7; 
	 
	 PLL1->PLLM   =   0x55;   // VCO will 24*2M/N+1
   PLL1->PREDIV =   0x8000|0x5;
	 //cjk add up
	 
	 
	 //cjk add down
#if 0
	    PLL1->PLLDIV2 = 0x8001;   
	    PLL1->PLLDIV3 = 0x8001;   // POST DIV 486/2  -> MJCP/HDVICP
	    PLL1->PLLDIV4 = 0x8003;   // POST DIV 486/4  -> EDMA/EDMA CFG
	    PLL1->PLLDIV5 = 0x8001;   // POST DIV 486/2 -> VPSS
	    PLL1->PLLDIV6 = 0x8011;   // 27Mhz POST DIV 486/18  -> VENC
      PLL1->PLLDIV7 = 0x8000;   // POST DIV 486/2 -> DDR
	    PLL1->PLLDIV8 = 0x8003;   // POST DIV 486/4 -> MMC0/SD0
	    PLL1->PLLDIV9 = 0x8001;   // POST DIV 486/2 -> CLKOUT
#endif
			PLL1->PLLDIV1 = 0x801B;
      PLL1->PLLDIV2 = 0x8001;
      PLL1->PLLDIV3 = 0x8001;   // POST DIV 680/2 = 340Mhz  -> MJCP and HDVICP bus interface clock
      PLL1->PLLDIV4 = 0x8003;   // POST DIV 680/4 = 170Mhz  -> EDMA/Peripheral CFG0(1/2 MJCP/HDVICP bus interface clk)
      PLL1->PLLDIV5 = 0x8001;   // POST DIV 680/2 = 340Mhz  -> VPSS
      PLL1->PLLDIV6 = 0x8008;   // POST DIV 680/9 = 75.6 Mhz -> VENC
 
 
      PLL1->PLLDIV7 = 0x8000;   // POST DIV 680/1 = 680Mhz -> DDRx2(with internal divider of 2, clock boils down to 340 Mhz)
      PLL1->PLLDIV8 = 0x8006;   // POST DIV 680/7= 97Mhz-> MMC0/SD0
      PLL1->PLLDIV9 = 0x801B;   // POST DIV 680/28 = 24.3Mhz-> CLKOUT
//cjk add up

//cjk add down
	 //PLL2->PLLM   =   0x63;   // VCO will 24*2M/N+1 = 594Mhz
	 //PLL2->PREDIV =   0x8000|0x7; 
	  PLL2->PLLM   =   0x9;   // VCO will 24*2M/N+1 = 432Mhz
    PLL2->PREDIV =   0x8000|0x0;
	 //cjk add up
	 
	 	//cjk add down
		#if 0
     PLL2->PLLDIV2 = 0x8001;   // 594/2 =297 Mhz -> ARM 
	 PLL2->PLLDIV4 = 0x801C;   // POST DIV 594/29 = 20.48 -> VOICE
	 PLL2->PLLDIV5 = 0x8007;   // POST DIV 594/8 = 74.25 ->VIDEO HD
	 #endif
	  PLL2->PLLDIV1 = 0x8011;
     PLL2->PLLDIV2 = 0x8000;   // POST DIV 432/1=432 Mhz  -> ARM926/(HDVICP block) clk
     PLL2->PLLDIV3 = 0x8001;
     PLL2->PLLDIV4 = 0x8014;   // POST DIV 432/21= 20.5714 Mhz->VOICE Codec clk
     PLL2->PLLDIV5 = 0x800F;   // POST DIV 432/16=27 Mhz  -> VENC(For SD modes, requires )
		//cjk add up      
		
		
		
  //cjk add down
  #if 0  
 	DDR->DDRPHYCR = 0x000000C5; 

	DDR->SDBCR = 0x08D34832;		//Program SDRAM Bank Config Register
	DDR->SDBCR = 0x0853C832;

    DDR->SDTIMR =0x3C934B51;		//Program SDRAM Timing Control Register1
	DDR->SDTIMR2 =0x4221C72;		//Program SDRAM Timing Control Register2

	DDR->PBBPR = 0x000000FE;
		
	DDR->SDBCR = 0x08534832;		//Program SDRAM Bank Config Register

	DDR->SDRCR = 0x00000768;		//Program SDRAM Refresh Control Register
  #endif 
   DDR->DDRPHYCR = 0x000000C6;
   DDR->SDBCR = 0x00D34A32;        //Program SDRAM Bank Config Register
   DDR->SDBCR = 0x0053CA32;
 
   DDR->SDTIMR  =0x576D7D12;       //Program SDRAM Timing Control Register1
   DDR->SDTIMR2 =0x422EC742;       //Program SDRAM Timing Control Register2
 
   DDR->PBBPR = 0x000000FE;
 
   DDR->SDBCR = 0x08534832;        //Program SDRAM Bank Config Register
 
   DDR->SDRCR = 0xA5C;             //Program SDRAM Refresh Control Register
   //cjk add up

三、UBL 编译

四、UBL源码分析

参看: UBL descriptor
参看:DM36X 的UBL移植

DM365是的启动方式有两种,通过BOOTSEL[2:0]引脚决定。当其为001时,直接从AEMIF上启 动,比如NOR和OneNAND。除此之外皆是从RBL启动,顺序为RBL-UBL-UBOOT-KERNEL,比如NAND,串口,SD卡等。RBL会 搜寻block1到block24去找UBL,关于RBL启动的详细细节可以参考用户指南关于ARM子系统的那篇文档,很详尽,下面只分析UBL的源码。

 
      UBL源码在PSP包里的board_utilities\flash_utils目录下,主要是COMMON目录和各子平台的目录如DM36x等,内中除了UBL的源码外还有CCS下JTAG的擦除烧写源码,串口烧写源码等。下面只分析UBL的启动代码。
 
      入门代码是汇编文件start.S,主要是切换操作模式,建立堆栈等,然后跳转到main函数,进入到Common\ubl\src目录下的C文件ubl.c中。main函数如下:
 

// Main entry point

void main(void)
{

    
    // Call to real boot function code

    LOCAL_boot();
    
    // Jump to entry point

    DEBUG_printString("\r\nJumping to entry point at ");
    DEBUG_printHexInt(gEntryPoint);
    DEBUG_printString(".\r\n");
    APPEntry = (void(*)(void)) gEntryPoint;
    (*APPEntry)();     
}

      main函数主要调用了LOCAL_boot函数来进行实质的引导功能,下面是此函数的内容:

static Uint32 LOCAL_boot(void)
{

    DEVICE_BootMode bootMode;
    
    // Read boot mode 

    bootMode = DEVICE_bootMode();
    
    if (bootMode== DEVICE_BOOTMODE_UART)
    {

        // Wait until the RBL is done using the UART.

        while((UART0->LSR& 0x40)== 0 );
    }
    
    // Platform Initialization

    if ( DEVICE_init()!= E_PASS)
    {

        DEBUG_printString(devString);
        DEBUG_printString(" initialization failed!\r\n");
        asm(" MOV PC, #0");
    }
    else
    {

        DEBUG_printString(devString);
        DEBUG_printString(" initialization passed!\r\n");
    }
    
    // Set RAM pointer to beginning of RAM space

    UTIL_setCurrMemPtr(0);
    
    // Send some information to host

    DEBUG_printString("TI UBL Version: ");
    DEBUG_printString(UBL_VERSION_STRING);
    DEBUG_printString("\r\nBooting Catalog Boot Loader\r\nBootMode = ");
    
    // Select Boot Mode

#if defined(UBL_NAND)
    {

        //Report Bootmode to host

        DEBUG_printString("NAND\r\n");
        
        // Copy binary image application from NAND to RAM

        if (NANDBOOT_copy()!= E_PASS)
        {

            DEBUG_printString("NAND Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#elif defined(UBL_NOR)
    {

        //Report Bootmode to host

        DEBUG_printString("NOR \r\n");
        
        // Copy binary application image from NOR to RAM

        if (NORBOOT_copy()!= E_PASS)
        {

            DEBUG_printString("NOR Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#elif defined(UBL_SD_MMC)
    {

        //Report Bootmode to host

        DEBUG_printString("SD/MMC \r\n");
        
        // Copy binary of application image from SD/MMC card to RAM

        if (SDMMCBOOT_copy()!= E_PASS)
        {

            DEBUG_printString("SD/MMC Boot failed.\r\n");
            LOCAL_bootAbort();
        }
    }
#else
    {

        //Report Bootmode to host

        DEBUG_printString("UART\r\n");
        UARTBOOT_copy();
    }
    
    
#endif
    
    DEBUG_printString(" DONE");
    
    UTIL_waitLoop(10000);
    
    DEVICE_TIMER0Stop();
    
    return E_PASS;
}


      先通过调用DEVICE_bootMode函数来判断启动方式(通过读取SYS寄存器实现),而后调用了DEVICE_init函数来进行平台的最底层初始化,包括电源域,时钟,DDR,EMIF,UART,I2C,TIMER等,另有专篇分析。

      而后通过UTIL_setCurrMemPtr函数对全局变量currMemPtr赋值,以后用到。接着通过判断不同的引导方式,采取不同的处理办法,以 NAND启动为例,将调用NANDBOOT_copy函数,此函数另有专篇分析。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后 UBL结束,控制权正式交给UBOOT。


书接上回,看看UBL对平台的初始化,主要是调用了DEVICE_init函数,函数内容如下:

Uint32 DEVICE_init()
{

    Uint32 status = E_PASS;
    
    // Mask all interrupts
    AINTC->INTCTL= 0x4;
    AINTC->EABASE= 0x0;
    AINTC->EINT0= 0x0;
    AINTC->EINT1= 0x0;        
    
    // Clear all interrupts
    AINTC->FIQ0= 0xFFFFFFFF;
    AINTC->FIQ1= 0xFFFFFFFF;
    AINTC->IRQ0= 0xFFFFFFFF;
    AINTC->IRQ1= 0xFFFFFFFF;
    
#ifndef SKIP_LOW_LEVEL_INIT
    
    POR_RESET();
    
    // System PSC setup - enable all
    DEVICE_PSCInit();
    
    DEVICE_pinmuxControl(0,0xFFFFFFFF,0x00FD0000);// All Video Inputs

    DEVICE_pinmuxControl(1,0xFFFFFFFF,0x00145555);// All Video Outputs

    DEVICE_pinmuxControl(2,0xFFFFFFFF,0x000000DA);// EMIFA

    DEVICE_pinmuxControl(3,0xFFFFFFFF,0x00180000);// SPI0, SPI1, UART1, I2C, SD0, SD1, McBSP0, CLKOUTs

    DEVICE_pinmuxControl(4,0xFFFFFFFF,0x55555555);// MMC/SD0 instead of MS, SPI0

    
    GPIO->DIR02&= 0xfeffffff;
    GPIO->CLRDATA02= 0x01000000;
    
    // System PLL setup
    if (status== E_PASS) status|= DEVICE_PLL1Init(PLL1_Mult);
    
    // DDR PLL setup
    if (status== E_PASS) status|= DEVICE_PLL2Init();
    
    // DDR2 module setup
    if (status== E_PASS) status|= DEVICE_DDR2Init();
#endif
    
    // AEMIF Setup
    if (status== E_PASS) status|= DEVICE_EMIFInit();
    
    // UART0 Setup
    if (status== E_PASS) status|= DEVICE_UART0Init();
    
    // TIMER0 Setup
    if (status== E_PASS) status|= DEVICE_TIMER0Init();
    
    // I2C0 Setup
    if (status== E_PASS) status|= DEVICE_I2C0Init();
    
    return status;
}

      首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现,函数内容如下:

void DEVICE_PSCInit()
{
    
    unsigned char i=0;
    unsigned char lpsc_start;
    unsigned char lpsc_end,lpscgroup,lpscmin,lpscmax;
    unsigned int PdNum= 0;
    
    lpscmin =0;
    lpscmax =2;
    
    for(lpscgroup=lpscmin; lpscgroup<=lpscmax; lpscgroup++){

        if(lpscgroup==0)
        {

            lpsc_start = 0;// Enabling LPSC 3 to 28 SCR first

            lpsc_end = 28;
        }
        else if(lpscgroup== 1){
/* Skip locked LPSCs [29-37] */
            lpsc_start = 38;
            lpsc_end = 47;
        } else{

            lpsc_start = 50;
            lpsc_end = 51;
        }
        
        //NEXT=0x3, Enable LPSC's
        for(i=lpsc_start; i<=lpsc_end; i++){

            PSC->MDCTL[i]|= 0x3;
        }
        
        //Program goctl to start transition sequence for LPSCs
        PSC->PTCMD=(1<<PdNum);
        
        //Wait for GOSTAT = NO TRANSITION from PSC for Pdomain 0
        while(!(((PSC->PTSTAT>> PdNum)& 0x00000001)== 0));
        
        //Wait for MODSTAT = ENABLE from LPSC's
        for(i=lpsc_start; i<=lpsc_end; i++){

            while(!((PSC->MDSTAT[i]& 0x0000001F)== 0x3));             
        }    
    }     
    
}

      然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择,详见数据手册查看引脚功能。

      接着调用DEVICE_PLL1Init函数实现了PLL1的配置,预分频,倍频,后分频,分频到各个模块,其设置顺序可以参看用户指南ARM子系统文档,有详细的介绍,PLL2类似不再赘述,函数内容如下:

Uint32 DEVICE_PLL1Init(Uint32 PLLMult)
{

    unsigned int CLKSRC=0x0;             
    unsigned int j;
    
    /*Power up the PLL*/
    PLL1->PLLCTL&= 0xFFFFFFFD;        
    
    PLL1->PLLCTL&= 0xFFFFFEFF;             
    PLL1->PLLCTL|= CLKSRC<<8;
    
    /*Set PLLENSRC '0', PLL Enable(PLLEN) selection is controlled through MMR*/
    PLL1->PLLCTL&= 0xFFFFFFDF;    
    
    /*Set PLLEN=0 => PLL BYPASS MODE*/
    PLL1->PLLCTL&= 0xFFFFFFFE;
    
    UTIL_waitLoop(150);                 
    
    // PLLRST=1(reset assert)
    PLL1->PLLCTL|= 0x00000008; 
    
    UTIL_waitLoop(300);
    
    /*Bring PLL out of Reset*/ 
    PLL1->PLLCTL&= 0xFFFFFFF7;
    
    //Program the Multiper and Pre-Divider for PLL1
    PLL1->PLLM= 0x51;// VCO will 24*2M/N+1 = 486Mhz

    PLL1->PREDIV= 0x8000|0x7;
    
    PLL1->SECCTL= 0x00470000;// Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 1 
    PLL1->SECCTL= 0x00460000;// Assert TENABLE = 1, TENABLEDIV = 1, TINITZ = 0 
    PLL1->SECCTL= 0x00400000;// Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 0 
    PLL1->SECCTL= 0x00410000;// Assert TENABLE = 0, TENABLEDIV = 0, TINITZ = 1    
  
    //Program the PostDiv for PLL1
    PLL1->POSTDIV= 0x8000;
    
    // Post divider setting for PLL1     
    PLL1->PLLDIV2= 0x8001;
    PLL1->PLLDIV3= 0x8001;// POST DIV 486/2 -> MJCP/HDVICP
    PLL1->PLLDIV4= 0x8003;// POST DIV 486/4 -> EDMA/EDMA CFG
    PLL1->PLLDIV5= 0x8001;// POST DIV 486/2 -> VPSS
    PLL1->PLLDIV6= 0x8011;// 27Mhz POST DIV 486/18 -> VENC
    PLL1->PLLDIV7= 0x8000;// POST DIV 486/2 -> DDR
    PLL1->PLLDIV8= 0x8003;// POST DIV 486/4 -> MMC0/SD0
    PLL1->PLLDIV9= 0x8001;// POST DIV 486/2 -> CLKOUT

    UTIL_waitLoop(300);
    
    /*Set the GOSET bit */ 
    PLL1->PLLCMD= 0x00000001;// Go

    UTIL_waitLoop(300);
        
    /*Wait for PLL to LOCK */
    while(!(((SYSTEM->PLL0_CONFIG)& 0x07000000)== 0x07000000));    
    
    /*Enable the PLL Bit of PLLCTL*/
    PLL1->PLLCTL|= 0x00000001;// PLLEN=0
  
    return E_PASS;
}

继续在DEVICE_init函数中,下面是调用DEVICE_DDR2Init函数来配置DDR控制器,这是 UBL中重要的一部分,如果硬件电路需要更换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄存器中的相关参数,比如时 序,BANK数,页大小等。这个函数主要是操作SYS模块和DDR模块的相关寄存器来配置内存,函数中调用的DEVICE_LPSCTransition 函数用来实现模块的电源时钟状态的改变,函数内容如下:

Uint32 DEVICE_DDR2Init()
{

    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    SYSTEM->VTPIOCR=(SYSTEM->VTPIOCR)& 0xFFFF9F3F;
    
    // Set bit CLRZ (bit 13)
    SYSTEM->VTPIOCR=(SYSTEM->VTPIOCR)| 0x00002000;
    
    // Check VTP READY Status
    while( !(SYSTEM->VTPIOCR& 0x8000));
    
    // Set bit VTP_IOPWRDWN bit 14 for DDR input buffers)
    //SYSTEM->VTPIOCR = SYSTEM->VTPIOCR | 0x00004000; 

    // Set bit LOCK(bit7) and PWRSAVE (bit8)
    SYSTEM->VTPIOCR=SYSTEM->VTPIOCR| 0x00000080;
    
    // Powerdown VTP as it is locked (bit 6)
    // Set bit VTP_IOPWRDWN bit 14 for DDR input buffers)
    SYSTEM->VTPIOCR=SYSTEM->VTPIOCR| 0x00004040;
    
    // Wait for calibration to complete 
    UTIL_waitLoop( 150 );
    
    // Set the DDR2 to synreset, then enable it again
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET);
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    DDR->DDRPHYCR= 0x000000C5;   
    DDR->SDBCR= 0x08D34832;        //Program SDRAM Bank Config Register
    DDR->SDBCR= 0x0853C832;    
    DDR->SDTIMR=0x3C934B51;        //Program SDRAM Timing Control Register1
    DDR->SDTIMR2=0x4221C72;        //Program SDRAM Timing Control Register2
    DDR->PBBPR= 0x000000FE;   
    DDR->SDBCR= 0x08534832;        //Program SDRAM Bank Config Register   
    DDR->SDRCR= 0x00000768;        //Program SDRAM Refresh Control Register
 
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_SYNCRESET);
    DEVICE_LPSCTransition(LPSC_DDR2,0,PSC_ENABLE);
    
    return E_PASS;
}

void DEVICE_LPSCTransition(Uint8 module, Uint8 domain, Uint8 state)
{

    // Wait for any outstanding transition to complete
    while ((PSC->PTSTAT)&(0x00000001<< domain));
    
    // If we are already in that state, just return
    if (((PSC->MDSTAT[module])& 0x1F) == state)return;
    
    // Perform transition
    PSC->MDCTL[module]=((PSC->MDCTL[module])&(0xFFFFFFE0))|(state);
    PSC->PTCMD|=(0x00000001 << domain);
    
    // Wait for transition to complete
    while ((PSC->PTSTAT)&(0x00000001<< domain));
    
    // Wait and verify the state
    while (((PSC->MDSTAT[module])& 0x1F) != state);    
}

      而后调用DEVICE_EMIFInit函数来配置EMIF模块,这个模块用来接外存,比如NAND,NOR等。DM365有两个片选空间,如果某一空间配置成NAND,则需要在寄存器中设置,其函数内容如下:

Uint32 DEVICE_EMIFInit()
{
    
    AEMIF->AWCCR= 0xff;   
    AEMIF->A1CR= 0x40400204;  
    AEMIF->NANDFCR|= 1;  
    AEMIF->A2CR= 0x00a00505;
    
    return E_PASS;   
}

      而后调用DEVICE_UART0Init函数来配置串口0,调用DEVICE_TIMER0Init函数来配置TIMER0,调用 DEVICE_I2C0Init函数来配置I2C控制器,都是操作某一模块的控制寄存器实现,具体如何设置可以参考相关模块的手册,这三个函数的内容如 下:

Uint32 DEVICE_UART0Init()
{
    
    UART0->PWREMU_MGNT= 0;// Reset UART TX & RX components

    UTIL_waitLoop( 100 );
    
    UART0->MDR= 0x0;
    UART0->DLL= 0xd;// Set baud rate    
    UART0->DLH= 0;
     
    UART0->FCR= 0x0007;// Clear UART TX & RX FIFOs
    UART0->FCR= 0x0000;// Non-FIFO mode
    UART0->IER= 0x0007;// Enable interrupts

    UART0->LCR= 0x0003;// 8-bit words
    // 1 STOP bit generated,
    // No Parity, No Stick paritiy,
    // No Break control
 
    UART0->MCR= 0x0000;// RTS & CTS disabled,
    // Loopback mode disabled,
    // Autoflow disabled
 
    UART0->PWREMU_MGNT= 0xE001;// Enable TX & RX componenets
 
    return E_PASS;
}

Uint32 DEVICE_I2C0Init()
{

    I2C0->ICMDR= 0;// Reset I2C
    I2C0->ICPSC= 26;// Config prescaler for 27MHz
    I2C0->ICCLKL= 20;// Config clk LOW for 20kHz
    I2C0->ICCLKH= 20;// Config clk HIGH for 20kHz
    I2C0->ICMDR|= I2C_ICMDR_IRS;// Release I2C from reset
    
    return E_PASS;
}

Uint32 DEVICE_TIMER0Init()
{

    // Put timer into reset
    TIMER0->EMUMGT_CLKSPD= 0x00000003;
    TIMER0->TCR= 0x00000000;
    
    // Enable TINT0, TINT1 interrupt
    TIMER0->INTCTL_STAT= 0x00000001;
    
    // Set to 64-bit GP Timer mode, enable TIMER12 & TIMER34
    TIMER0->TGCR= 0x00000003;
    
    // Reset timers to zero 
    TIMER0->TIM12= 0x00000000;
    TIMER0->TIM34= 0x00000000;
    
    // Set timer period (5 second timeout = (24000000 * 5) cycles = 0x07270E00) 
    TIMER0->PRD34= 0x00000000;
    TIMER0->PRD12= 0x07270E00;
    
    return E_PASS;
}

      至此,DEVICE_init函数结束,程序返回至LOCAL_boot函数中,接着就调用NANDBOOT_copy函数了。


下面继续分析,由于后面的代码和函数量相对的多且复杂,所以不再贴上代码,只说流程,只要把程序源码打开对着看很容易就明白了。

      在DEVICE_init()这个大的平台初始化函数结束以后,下面要做的工作就是将NAND的中UBOOT复制到DDR中并且跳转到那里,然后UBL的历史使命结束,UBOOT的执行开始,那么就是调用这个函数,NANDBOOT_copy()。
      NANDBOOT_copy()函数主要分三大块,第一大块是调用NAND_open()函数对NAND控制器和NAND芯片进行初始化,它传递的参数是CE的地址(一般是CE0)和总线位宽(一般是8位)。好,我们跳到NAND_open()函数中。
      NAND_open()函数在common\driver\src\nand.c中,与平台无关。首先定义一个NAND_InfoHandle结构 hNandInfo,这个结构包含了所有与NAND有关的参数,比如厂商ID,设备ID,基址,位宽,块数,页数每块,页大小及与ECC,BB有关的一些 函数。然后对这个hNandInfo进行赋值,这里最重要的是把 DEVICE_NAND_PAGE_layout,DEVICE_NAND_ECC_info,DEVICE_NAND_BB_info,DEVICE_NAND_CHIP_infoTable 这四大全局结构赋给了hNandInfo的相关字段。这四大全局结构在每个平台目录下都有定义(比如dm36x\common\src \device_nand.c),编译不同平台的UBL,会将不同平台的四大全局结构传递给nand.c中使用,这样就做到了NAND的驱动分离中平台无 关部分和平台有关部分,这种思想也贯穿在整个LINUX的架构中,先不提这个。然后,操作AEMIF寄存器使能NAND的哪一个CE中,这个由传递的基址 决定,一般是CE0,并使能这段片选之上有ECC功能。
 
      接着调用NAND_reset()函数对NAND芯片进行复位,这个函数本身很简单,就是先发复位命令0xFF,再等待操作完成,懂得NAND操作过程的 会很容易这个操作。但是这里多说一点,一般我们遇到的芯片操作NAND,其CPU内部会有一个NAND控制器,对NAND的操作会通过这个寄存器来操作, 发命令发地址等都是操作这些寄存器来完成。但是DM365不是这样,它没有实质上的NAND控制器,它还是将NAND当做是一个类RAM一样的操作,通过 总线上的时序来完成。CLE和ALE连在地址总线A2和A1上,这样的话发命令实质上就是在总线地址0x00000010u上写值(注意DM365的地址 线问题,有个32位问题),发地址实质就是在总线地址0x00000008u上写值,读写数据实质就是在总线地址0x00000000u上读写值。这些地 址的操作隐含着CLE和ALE的高低电平,再加上配好的时序参数就把NAND的操作弄成了类RAM的操作。这种操作以前没有碰到过,TI的工程师很牛。
 
      接着调用LOCAL_flashGetDetails()函数,这个函数很重要,它会得到NAND的很多详细信息。首先它会判断NAND是否是ONFI, 这个没有细究过,跳过不管。然后它会向NAND发读ID命令,从而得到NAND的厂商ID和设备ID值。有了这两个值就好办了,通过一个循环把它们通四全 大局结构中的DEVICE_NAND_CHIP_infoTable中定义的各种NAND的资料进行对比,通过ID来定位是哪一种NAND,然后就得出了 页数,块数,页大小等重要信息。另外,还要区分NAND是大型还是小型(一般看页大小,大于2K为大),因为它们的地址周期不同。这个函数后面还有一些东 西,没有深究。调用完成后再复位一下NAND,整个NAND_open()函数就结束了。
 
      NAND_open()函数完成后,会调用NAND_readPage()函数从第25块开始一直搜索到第50块,只读第0页。因为UBOOT放在第25 块开始的地方,这是手册规定的,而且第0页放一些启动信息等等。通过MAGIC_NUMBER来判断是否有效,这个知识点详见手册。一旦 MAGIC_NUMBER正确,就会将后面的几个字段,比如UBOOT的进入点,页数,UBOOT在哪一个块,哪一个页,UBOOT的装载点等信息就会得 到。而后循环调用NAND_readPage()函数将整个UBOOT从NAND拷贝到DDR里,最后将刚才得到的UBOOT的进入点地址复制到 gEntryPoint变量中。然后主函数跳转到这里,从此UBL结束,UBOOT正式启动。
 
      这里的分析没有涉及到坏块处理,ECC校验的部分,一方面现在太忙没时间细看。另一方面UBL不是重点,要想深究这个东西,还是到Linux内核里的 NAND驱动中深究,这里能用就行了。还有就是一开始担心的是我们项目用到的NAND是512B每页的老式NAND,担心UBL不支持,需要修改UBL源 码。后来读了UBL源码以后,特别是平台相关的device_nand.c文件,才发现TI的工程师已经把该考虑的都考虑的,都能用,老外就是牛X。不像 国内的代码,根本没有普适性和跨平台性。UBL整个源码,架构较清晰,注释也多,代码整体很完整和美观,很有参考和学习价值。读了UBL源码后,其实很容 易就能读懂NANDEraser,NANDWriter等的源码了,说白了就是平台初始化加NAND读写,呵呵。

五、DM36X 的UBL移植

关于UBL的移植,本人打算单独写一篇文章的,但是UBL和UBOOT太紧密了,是TI davinci芯片的特点,所以放到一起讲。我们重点放在NAND BOOT的移植,这个UBL的版本是V1.50,在dvsdk_dm368_4_02_00_06\psp\flash-utils\DM36x和flash-utils\Common目录下,Common目录里有非常多东西,包括UBL的驱动源码、工具、脚本等等。我们主要关注arch,drivers,src,ubl。UBL的main()函数在dvsdk_dm368_4_02_00_06\psp\flash-utils\Common\ubl\src\ubl.c里,这几个文件夹打开看看就明白什么意思了,这里不罗嗦。
DM36x下有CCS、Common,GNU三个文件夹:
CCS文件夹:
这里边的程序需要在TI CCS下编译,通过仿真器和JTAG在DM36X的板子上调试和烧写NAND FLASH或NOR FLASH,有烧写Writer的应用程序,用CCS打开工程文件,会连接到dvsdk_dm368_4_02_00_06\psp\flash-utils\Common\ drivers里。
Common文件夹:Common里有核心的文件device.c和device_nand.c。device.c是最重要的文件,这里初始化很多系统的东西,见DEVICE_init():
1、屏蔽所有中断;
2、清除中断标志;
3、DEVICE_PSCInit(),Power and Sleep Controller;
4、主芯片管脚复用的设置DEVICE_pinmuxControl(),DM36X的管脚复用很多,很复杂,一共5个PINMUX寄存器需配置;
5、DEVICE_PLL1Init(),PPL1配置,见SPRUSG5A.pdf的35页开始的介绍,使用不同的频率的DM36X,这些值都不同,不过TI已经提供参数参考,我们的DM365核心板是:ARM297_DDR243_OSC24,DM368核心板是:ARM432_DDR340_OSC24;
6、DEVICE_PLL2Init(),PPL2的配置,使用同上,不同频率的值不同;
7、DEVICE_DDR2Init()的配置,市场上不同的DDR2内存芯片需要不同的参数配置,就在这个函数内。
8、DEVICE_EMIFInit(),这个针对NAND FLASH接口或NOR FLASH接口的访问时序配置;
9、DEVICE_UART0Init()的配置,这个就是我们调试DM36X串口的设置,我们使用UART0来调试LINUX,这里配置不好,后面的开发不用调试了。
10、DEVICE_TIMER0Init()定时器TIMER0的设置;
11、DEVICE_I2C0Init()的设置;
GNU的文件夹:
这个就是在LINUX环境下编译UBL的环境,修改dvsdk_dm368_4_02_00_06\psp\flash-utils\DM36x\GNU\ubl下的makefile,把
#$(MAKE) -C build TYPE=nor注释掉,只保留$(MAKE) -C build TYPE=nand,然后是make clean和 make生产ubl_DM36x_nand.bin的文件;
如果出现:device.c:(.text+0x2ec): undefined reference to `__aeabi_uidiv’和device.c:(.text+0x2e8): undefined reference to `__aeabi_uidivmod’的BUG,就是DDR_Get_Val()函数里边的除法和求余的语句和你的编译器不配置造成的。
result = ((parm * freq) / 10000) – 1;和if(((parm * freq) % 10000)),我们对参数的求值直接:
    tRFC = xxx; //DDR_Get_Val(DDR_T_RFC, DDR_FREQ);
    tRP = xxx; //DDR_Get_Val(DDR_T_RP, DDR_FREQ);
    tRCD = xxx; //DDR_Get_Val(DDR_T_RCD, DDR_FREQ);
    tWR = xxx; //DDR_Get_Val(DDR_T_WR, DDR_FREQ);
    tRAS = xxx; //DDR_Get_Val(DDR_T_RAS, DDR_FREQ);
    tRC = xxx; //DDR_Get_Val(DDR_T_RC, DDR_FREQ);
    tRRD = xxx; //DDR_Get_Val(DDR_T_RRD, DDR_FREQ);
    tWTR = xxx; //DDR_Get_Val(DDR_T_WTR, DDR_FREQ);
不用DDR_Get_Val()函数求值,这样就可以编译通过了。后来发觉TI的

里也有人这人解决,呵呵,没想到本人的解决办法和一个网友一样的。本人记得调试三星2440 UBOOT的时候,有人点到过,这里本人忘记在什么地方改了。

dm348_再论不平等

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170235.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 在 Ubuntu系统下安装 OpenCV 全过程

    在 Ubuntu系统下安装 OpenCV 全过程经过不断的试错、安装、删除重装……写下这篇博客,希望其他人安装时能够轻松一点,少几次还原重装……版本:ubuntu18.10OpenCV-4.1.0首先,OpenCV有在ubuntu上安装的官方文档:https://docs.opencv.org/4.1.0/d7/d9f/tutorial_linux_install.html大家可以参照官方文档进行操作…

  • idea配置tomcat以及环境变量「建议收藏」

    idea配置tomcat以及环境变量「建议收藏」这里写目录标题tomcat环境变量idea配置tomcattomcat环境变量第一步下载tomcat网址:https://tomcat.apache.org/download-90.cgi下载完成后解压进入解压后的文件夹复制地址第二步下载tomcat环境变量配置右击此电脑->属性->高级系统设置->环境变量1.CATALINA_HOME2.编辑Path,点击“新建”,添加变量值:%CATALINA_HOME%\lib%CATALINA_HOME%\b

  • lmdb数据库_Interbase数据库

    lmdb数据库_Interbase数据库LMDB全称为LightningMemory-MappedDatabase,就是非常快的内存映射型数据库,LMDB使用内存映射文件,可以提供更好的输入/输出性能,对于用于神经网络的大型数据集(比如ImageNet),可以将其存储在LMDB中…

  • createthread函数详解_createremotethread

    createthread函数详解_createremotethreadCreateRemoteThread和WriteProcessMemory技术示例程序:WinSpy另一种注入代码到其他进程地址空间的方法是使用WriteProcessMemoryAPI。这次你不用编写一个独立的DLL而是直接复制你的代码到远程进程(WriteProcessMemory)并用CreateRemoteThread执行之。让我们看一下CreateRemoteThread

  • js判断字符串_js中判断字符串包含另一个字符串

    js判断字符串_js中判断字符串包含另一个字符串1.string.search()返回值:匹配成功的第一个字符的下标,未匹配则返回-1用法:string.search(searchValue)举个例子:letsearchVal=’yyds’letsearchResult=searchVal.search(‘y’)console.log(searchResult)//03.string.includes()返回值:Boolean用法:string.includes(searchValue,start)

  • fflush和fsync的联系和区别「建议收藏」

    fflush和fsync的联系和区别「建议收藏」1.提供者fflush是libc.a中提供的方法,fsync是系统提供的系统调用。2.原形fflush接受一个参数FILE*.fflush(FILE*);fsync接受的时一个Int型的文件描述符。fsync(intfd);3.功能fflush:是把C库中的缓冲调用write函数写到磁盘[其实是写到内核的缓冲区]。fsync:是把内核缓冲刷到磁盘上。c库

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号