大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
参看:UBI – Unsorted Block Images
UBL 前两篇文章中我们有提到过了。
简单点来说,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。
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。
二、修改 UBL 源码
How can I modify Leopardboard DM368 running on 432MHz
Timing DDR DM368 MT47H64M16HR-25E IT:H
DM368 (Performance & Power Consumption)
DDR2/mDDR Memory Controller User’s Guide
Setting up AM35x SDRC registers
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
- For configuration details of PLL Controllers (PLL1 and PLL2) look into TMS320DM36x ARM Subsystem Reference Guide (literature number SPRUFG5).
- 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.
- 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
- Open DM36x/CCS/UBL/UBL.pjt UBL project.
- Make sure you selected BOOT_NAND configuration from active configuration drop down.
- 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源码分析
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函数主要调用了LOCAL_boot函数来进行实质的引导功能,下面是此函数的内容:
而后通过UTIL_setCurrMemPtr函数对全局变量currMemPtr赋值,以后用到。接着通过判断不同的引导方式,采取不同的处理办法,以 NAND启动为例,将调用NANDBOOT_copy函数,此函数另有专篇分析。此函数将NAND中的某些内容(就是UBOOT)搬移到RAM中,而后 UBL结束,控制权正式交给UBOOT。 书接上回,看看UBL对平台的初始化,主要是调用了DEVICE_init函数,函数内容如下:
首先屏蔽和清除中断,然后调用DEVICE_PSCInit函数实现对各模块的电源时钟使能,实质是调用PSC电源时钟管理模块的寄存器实现,函数内容如下:
然后调用DEVICE_pinmuxControl函数决定复用引脚的功能选择,详见数据手册查看引脚功能。 接着调用DEVICE_PLL1Init函数实现了PLL1的配置,预分频,倍频,后分频,分频到各个模块,其设置顺序可以参看用户指南ARM子系统文档,有详细的介绍,PLL2类似不再赘述,函数内容如下:
继续在DEVICE_init函数中,下面是调用DEVICE_DDR2Init函数来配置DDR控制器,这是 UBL中重要的一部分,如果硬件电路需要更换内存芯片的话,需要在UBL中修改这个函数,即按照芯片手册来配置DDR控制寄存器中的相关参数,比如时 序,BANK数,页大小等。这个函数主要是操作SYS模块和DDR模块的相关寄存器来配置内存,函数中调用的DEVICE_LPSCTransition 函数用来实现模块的电源时钟状态的改变,函数内容如下:
而后调用DEVICE_EMIFInit函数来配置EMIF模块,这个模块用来接外存,比如NAND,NOR等。DM365有两个片选空间,如果某一空间配置成NAND,则需要在寄存器中设置,其函数内容如下:
而后调用DEVICE_UART0Init函数来配置串口0,调用DEVICE_TIMER0Init函数来配置TIMER0,调用 DEVICE_I2C0Init函数来配置I2C控制器,都是操作某一模块的控制寄存器实现,具体如何设置可以参考相关模块的手册,这三个函数的内容如 下:
至此,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的时候,有人点到过,这里本人忘记在什么地方改了。
|
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170235.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...