posts - 116,  comments - 123,  trackbacks - 0
 
在Makefile中可以使用函数来处理变量,从而让我们的命令或是规则更为的灵活和具 
有智能。make所支持的函数也不算很多,不过已经足够我们的操作了。函数调用后,函 
数的返回值可以当做变量来使用。

 

一、函数的调用语法
函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:
$(<function> <arguments> ) 
或是
${<function> <arguments>}


这里,<function>就是函数名,make支持的函数不多。<arguments>是函数的参数,参数 
间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。函数调用以“$”开头,以圆 
括号或花括号把函数名和参数括起。感觉很像一个变量,是不是?函数中的参数可以使 
用变量,为了风格的统一,函数和变量的括号最好一样,如使用“$(substa,b,$(x))”这 
样的形式,而不是“$(substa,b,${x})”的形式。因为统一会更清楚,也会减少一些不必 
要的麻烦。
还是来看一个示例:
comma:= , 
empty:= 
space:= $(empty) $(empty) 
foo:= a b c 
bar:= $(subst $(space),$(comma),$(foo))
在这个示例中,$(comma)的值是一个逗号。$(space)使用了$(empty)定义了一个空格, 
$(foo)的值是“a b c”,$(bar)的定义用,调用了函数“subst”,这是一个替换函数,这 
个函数有三个参数,第一个参数是被替换字串,第二个参数是替换字串,第三个参数 
是替换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号,所以$(bar)的值 
是“a,b,c”。

 

二、字符串处理函数


$(subst <from>,<to>,<text> )


名称:字符串替换函数——subst。 
功能:把字串<text>中的<from>字符串替换成<to>。 
返回:函数返回被替换过后的字符串。

示例:
$(subst ee,EE,feet on the street),
把“feetonthestreet”中的“ee”替换成“EE”,返回结果是“fEEtonthestrEEt”。

$(patsubst <pattern>,<replacement>,<text> ) 

名称:模式字符串替换函数——patsubst。 
功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否 
符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括 
通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么, 
<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“\” 
来转义,以“\%”来表示真实含义的“%”字符) 
返回:函数返回被替换过后的字符串。


示例:
$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.cbar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.obar.o”
备注:
这和我们前面“变量章节”说过的相关知识有点相似。如:


“$(var:<pattern>=<replacement> )” 
相当于 
“$(patsubst <pattern>,<replacement>,$(var))”,


而“$(var:<suffix>=<replacement>)” 
则相当于 
“$(patsubst %<suffix>,%<replacement>,$(var))”。


例如有:objects=foo.obar.obaz.o, 
那么,“$(objects:.o=.c)”和“$(patsubst%.o,%.c,$(objects))”是一样的。


$(strip <string> )


名称:去空格函数——strip。 
功能:去掉<string>字串中开头和结尾的空字符。 
返回:返回被去掉空格的字符串值。 
示例:
$(strip a b c )
把字串“abc”去到开头和结尾的空格,结果是“abc”。
$(findstring <find>,<in> )


名称:查找字符串函数——findstring。 

功能:在字串<in>中查找<find>字串。 
返回:如果找到,那么返回<find>,否则返回空字符串。 
示例:
$(findstring a,a b c) 
$(findstring a,b c)
第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
$(filter <pattern...>,<text> )
名称:过滤函数——filter。 
功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可 
以有多个模式。 
返回:返回符合模式<pattern>的字串。 
示例:


sources := foo.c bar.c baz.s ugh.h 
foo: $(sources) 
cc $(filter %.c %.s,$(sources)) -o foo


$(filter %.c %.s,$(sources))返回的值是“foo.c bar.c baz.s”。


$(filter-out <pattern...>,<text> )


名称:反过滤函数——filter-out。 
功能:以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可 
以有多个模式。 
返回:返回不符合模式<pattern>的字串。 
示例:


objects=main1.o foo.o main2.o bar.o 
mains=main1.o main2.o


$(filter-out $(mains),$(objects)) 返回值是“foo.o bar.o”。


$(sort <list> )


名称:排序函数——sort。 
功能:给字符串<list>中的单词排序(升序)。 
返回:返回排序后的字符串。 
示例:$(sortfoobarlose)返回“barfoolose”。 
备注:sort函数会去掉<list>中相同的单词。 

$(word <n>,<text> )


名称:取单词函数——word。 
功能:取字符串<text>中第<n>个单词。(从一开始) 
返回:返回字符串<text>中第<n>个单词。如果<n>比<text>中的单词数要大,那么返回 
空字符串。 
示例:$(word2,foobarbaz)返回值是“bar”。


$(wordlist <s>,<e>,<text> )


名称:取单词串函数——wordlist。 
功能:从字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。 
返回:返回字符串<text>中从<s>到<e>的单词字串。如果<s>比<text>中的单词数要大, 
那么返回空字符串。如果<e>大于<text>的单词数,那么返回从<s>开始,到<text>结束 
的单词串。 
示例:$(wordlist2,3,foobarbaz)返回值是“barbaz”。


$(words <text> )


名称:单词个数统计函数——words。 
功能:统计<text>中字符串中的单词个数。 
返回:返回<text>中的单词数。 
示例:$(words,foobarbaz)返回值是“3”。 
备注:如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words 
<text> ),<text> )。


$(firstword <text> )


名称:首单词函数——firstword。 
功能:取字符串<text>中的第一个单词。 
返回:返回字符串<text>的第一个单词。 
示例:$(firstwordfoobar)返回值是“foo”。 
备注:这个函数可以用word函数来实现:$(word1,<text>)。


以上,是所有的字符串操作函数,如果搭配混合使用,可以完成比较复杂的功能。这里, 
举一个现实中应用的例子。我们知道,make使用“VPATH”变量来指定“依赖文件”的 
搜索路径。于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数 
CFLAGS,如:


override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))


如果我们的“$(VPATH)”值是“src:../headers”,那么“$(patsubst %,-I%,$(subst :, , 
$(VPATH)))”将返回“-Isrc -I../headers”,这正是 cc 或 gcc 搜索头文件路径的参数。 

三、文件名操作函数

下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或 
是一系列的文件名来对待。

$(dir <names...> )

名称:取目录函数——dir。 
功能:从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”) 
之前的部分。如果没有反斜杠,那么返回“./”。 
返回:返回文件名序列<names>的目录部分。 
示例:$(dirsrc/foo.chacks)返回值是“src/./”。

$(notdir <names...> )

名称:取文件函数——notdir。 
功能:从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/ 
”)之后的部分。 
返回:返回文件名序列<names>的非目录部分。 
示例:$(notdirsrc/foo.chacks)返回值是“foo.chacks”。


$(suffix <names...> )


名称:取后缀函数——suffix。 
功能:从文件名序列<names>中取出各个文件名的后缀。 
返回:返回文件名序列<names>的后缀序列,如果文件没有后缀,则返回空字串。 
示例:$(suffixsrc/foo.csrc-1.0/bar.chacks)返回值是“.c.c”。


$(basename <names...> )


名称:取前缀函数——basename。 
功能:从文件名序列<names>中取出各个文件名的前缀部分。 
返回:返回文件名序列<names>的前缀序列,如果文件没有前缀,则返回空字串。 
示例:$(basenamesrc/foo.csrc-1.0/bar.chacks)返回值是“src/foosrc-1.0/barhacks”。


$(addsuffix <suffix>,<names...> )


名称:加后缀函数——addsuffix。 
功能:把后缀<suffix>加到<names>中的每个单词后面。 
返回:返回加过后缀的文件名序列。 
示例:$(addsuffix.c,foobar)返回值是“foo.cbar.c”。 

$(addprefix <prefix>,<names...> )


名称:加前缀函数——addprefix。 
功能:把前缀<prefix>加到<names>中的每个单词后面。 
返回:返回加过前缀的文件名序列。 
示例:$(addprefixsrc/,foobar)返回值是“src/foosrc/bar”。


$(join <list1>,<list2> )


名称:连接函数——join。 
功能:把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比 
<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比 
<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。 
返回:返回连接过后的字符串。 
示例:$(joinaaabbb,111222333)返回值是“aaa111bbb222333”。

 
四、foreach函数

foreach 函数和别的函数非常的不一样。因为这个函数是用来做循环用的,Makefile 中的 
foreach 函数几乎是仿照于 Unix 标准 Shell(/bin/sh)中的 for 语句,或是 C-Shell 
(/bin/csh)中的foreach语句而构建的。它的语法是:

 


$(foreach <var>,<list>,<text> )

 


这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中, 
然后再执行<text>所包含的表达式。每一次<text>会返回一个字符串,循环过程中, 
<text>的所返回的每个字符串会以空格分隔,最后当整个循环结束时,<text>所返回的 
每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

 


所以,<var>最好是一个变量名,<list>可以是一个表达式,而<text>中一般会使用 
<var>这个参数来依次枚举<list>中的单词。举个例子: 

names := a b c d


files := $(foreach n,$(names),$(n).o)

 


上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根 
据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, 
$(files)的值是“a.o b.o c.o d.o”。

 


注意,foreach中的<var>参数是一个临时的局部变量,foreach函数执行完后,参数 
<var>的变量将不在作用,其作用域只在 foreach 函数当中。

 


五、if函数

 

if 函数很像 GNU 的 make 所支持的条件语句——ifeq(参见前面所述的章节),if 函数 
的语法是:

 


$(if <condition>,<then-part> )

 


或是

 


$(if <condition>,<then-part>,<else-part> )

 


可见,if函数可以包含“else”部分,或是不含。即if函数的参数可以是两个,也可以是 
三个。<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就 

相当于返回真,于是,<then-part>会被计算,否则<else-part>会被计算。

 
而if函数的返回值是,如果<condition>为真(非空字符串),那个<then-part>会是整 
个函数的返回值,如果<condition>为假(空字符串),那么<else-part>会是整个函数的 
返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。 
所以,<then-part>和<else-part>只会有一个被计算。


六、call函数

call 函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式, 
这个表达式中,你可以定义许多参数,然后你可以用call函数来向这个表达式传递参 
数。其语法是:


$(call <expression>,<parm1>,<parm2>,<parm3>...)


当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参 
数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call函数的返 
回值。例如:


reverse = $(1) $(2)


foo = $(call reverse,a,b)


那么,foo的值就是“ab”。当然,参数的次序是可以自定义的,不一定是顺序的,如: 

reverse = $(2) $(1)


foo = $(call reverse,a,b)


此时的foo的值就是“ba”。 
七、origin函数 
origin 函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里 
来的?其语法是: 
$(origin <variable> )


注意,<variable>是变量的名字,不应该是引用。所以你最好不要在<variable>中使用“ 
$”字符。Origin 函数会以其返回值来告诉你这个变量的“出生情况”,下面,是 origin 
函数的返回值:


“undefined” 
如果<variable>从来没有定义过,origin函数返回这个值“undefined”。 
“default”


如果<variable>是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。 

“environment” 
如果<variable>是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。


“file”


如果<variable>这个变量被定义在Makefile中。 
“command line”


如果<variable>这个变量是被命令行定义的。

 
“override”


如果<variable>是被override指示符重新定义的。

 
“automatic” 
如果<variable>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。 
这些信息对于我们编写Makefile是非常有用的,例如,假设我们有一个Makefile其包 
了一个定义文件Make.def,在Make.def中定义了一个变量“bletch”,而我们的环境中 
也有一个环境变量“bletch”,此时,我们想判断一下,如果变量来源于环境,那么我 
们就把之重定义了,如果来源于Make.def或是命令行等非环境的,那么我们就不重新 
定义它。于是,在我们的Makefile中,我们可以这样写: 
ifdef bletch 

ifeq "$(origin bletch)" "environment" 
bletch = barf, gag, etc. 
endif 
endif 
当然,你也许会说,使用override关键字不就可以重新定义环境中的变量了吗?为什 
么需要使用这样的步骤?是的,我们用override是可以达到这样的效果,可是override 
过于粗暴,它同时会把从命令行定义的变量也覆盖了,而我们只想重新定义环境传来 
的,而不想重新定义命令行传来的。

 


八、shell函数

 

shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统 Shell 的命令。它和 
反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数 
返回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed等等命令来生成 
一个变量,如:

 


contents := $(shell cat foo)

 


files := $(shell echo *.c)

 


注意,这个函数会新生成一个Shell程序来执行命令,所以你要注意其运行性能,如果 
你的Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统 
性能是有害的。特别是Makefile的隐晦的规则可能会让你的shell函数执行的次数比你 
想像的多得多。

posted @ 2014-09-01 15:09 yuhen 阅读(588) | 评论 (0)编辑 收藏

1. os.system

import os
import tempfile

filename1
= tempfile.mktemp (".txt")
open (filename1,
"w").close ()
filename2
= filename1 + ".copy"
print filename1, "=>", filename2

#拷文件
os.system ("copy %s %s" % (filename1, filename2))

if os.path.isfile (filename2): print "Success"

dirname1
= tempfile.mktemp (".dir")
os.mkdir (dirname1)
dirname2
= dirname1 + ".copy"
print dirname1, "=>", dirname2

#拷目录
os.system ("xcopy /s %s %s" % (dirname1, dirname2))

if os.path.isdir (dirname2): print "Success"

2. shutil.copy和shutil.copytree

import os
import shutil
import tempfile

filename1
= tempfile.mktemp (".txt")
open (filename1,
"w").close ()
filename2
= filename1 + ".copy"
print filename1, "=>", filename2

#拷文件
shutil.copy (filename1, filename2)

if os.path.isfile (filename2): print "Success"

dirname1
= tempfile.mktemp (".dir")
os.mkdir (dirname1)
dirname2
= dirname1 + ".copy"
print dirname1, "=>", dirname2

#拷目录
shutil.copytree (dirname1, dirname2)

if os.path.isdir (dirname2): print "Success"

3.  win32file.CopyFile

import os
import win32file
import tempfile

filename1
= tempfile.mktemp (".txt")
open (filename1,
"w").close ()
filename2
= filename1 + ".copy"
print filename1, "=>", filename2

#拷文件
#
文件已存在时,1为不覆盖,0为覆盖
win32file.CopyFile (filename1, filename2, 1)
win32file.CopyFile (filename1, filename2, 0)
win32file.CopyFile (filename1, filename2,
1)

if os.path.isfile (filename2): print "Success"

dirname1
= tempfile.mktemp (".dir")
os.mkdir (dirname1)
dirname2
= dirname1 + ".copy"
print dirname1, "=>", dirname2

#拷目录
win32file.CopyFile (dirname1, dirname2, 1)

if os.path.isdir (dirname2): print "Success"

4. SHFileOperation 

import os
from win32com.shell import shell, shellcon
import tempfile

filename1
= tempfile.mktemp (".txt")
open (filename1,
"w").close ()
filename2
= filename1 + ".copy"
print filename1, "=>", filename2

#拷文件
#
文件已存在时,shellcon.FOF_RENAMEONCOLLISION会指示重命名文件
shell.SHFileOperation (
(0, shellcon.FO_COPY, filename1, filename2, 0, None, None)
)
shell.SHFileOperation (
(0, shellcon.FO_COPY, filename1, filename2, shellcon.FOF_RENAMEONCOLLISION, None, None)
)
shell.SHFileOperation (
(0, shellcon.FO_COPY, filename1, filename2, 0, None, None)
)

if os.path.isfile (filename2): print "Success"

dirname1
= tempfile.mktemp (".dir")
os.mkdir (dirname1)
dirname2
= dirname1 + ".copy"
print dirname1, "=>", dirname2

#拷目录
shell.SHFileOperation (
(0, shellcon.FO_COPY, dirname1, dirname2, 0, None, None)
)

if os.path.isdir (dirname2): print "Success"
不知道有没有其它的了,os.rename不算,那个是移动文件。另外我在测试它们的性能如何。
http://timgolden.me.uk/python/win32_how_do_i/copy-a-file.html
这里和楼主列出的都一样,没有更多的了
或者使用Chilkat http://www.chilkatsoft.com/refdoc/pythonCkFileAccessRef.html

测试结果出来了:
测试环境:系统——Win7 RTM,CPU——P4 3.0,MEM——1.5G DDR400,U盘——Kingston 4G

用4种不同的方法从硬盘拷贝MSDN 2008 SP1(2.37G)到U盘:

os System      的方法耗时903.218秒
shutil        的方法耗时1850.634秒
win32file      的方法耗时861.438秒
SHFileOperation的方法耗时794.023秒

另外SHFileOperation是显示对话框的,可以这样用
shell.SHFileOperation (
(0, shellcon.FO_COPY, filename1, filename2,
shellcon.FOF_RENAMEONCOLLISION
| \
shellcon.FOF_NOCONFIRMATION
|\
shellcon.FOF_NOERRORUI
| \
shellcon.FOF_SILENT, None, None)
)
posted @ 2014-08-14 15:14 yuhen 阅读(581) | 评论 (0)编辑 收藏
     摘要: 做视频采集与处理,自然少不了要学会分析YUV数据。因为从采集的角度来说,一般的视频采集芯片输出的码流一般都是YUV数据流的形式,而从视频处理(例如H.264、MPEG视频编解码)的角度来说,也是在原始YUV码流进行编码和解析,所以,了解如何分析YUV数据流对于做视频领域的人而言,至关重要。本文就是根据我的学习和了解,简单地介绍如何分析YUV数据流。    YUV,分为...  阅读全文
posted @ 2014-06-25 15:30 yuhen 阅读(529) | 评论 (0)编辑 收藏
对于.lds文件,它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。虽然现在我还没怎么用它,但感觉还是挺重要的,有必要了解一下。
先看一下GNU官方网站上对.lds文件形式的完整描述:

SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}

 

secname和contents是必须的,其他的都是可选的。下面挑几个常用的看看:
1、secname:段名
2、contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)
3、start:本段连接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。
4、AT(ldadr):定义本段存储(加载)的地址。
看一个简单的例子:(摘自《2410完全开发》)

 

/* nand.lds */
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}

 

    以上,head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址相同(没有AT指定);main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nand flash。
这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定。
编写好的.lds文件,在用arm-linux-ld连接命令时带-Tfilename来调用执行,如
arm-linux-ld –Tnand.lds x.o y.o –o xy.o。也用-Ttext参数直接指定连接地址,如
arm-linux-ld –Ttext 0x30000000 x.o y.o –o xy.o。
 既然程序有了两种地址,就涉及到一些跳转指令的区别,这里正好写下来,以后万一忘记了也可查看,以前不少东西没记下来现在忘得差不多了。。。
ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。
我自己经过归纳如下:
(1)       b step1 :b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该指令本身的bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代码的位置,只看指令本身。
(2)       ldr pc, =step1 :该指令是从内存中的某个位置(step1)读出数据并赋给PC,同样依赖当前PC的值,但是偏移量是那个位置(step1)的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程序跳转。
(3)       此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实现当前程序是在RAM中还是flash中。仍然用我当时的注释:

 

relocate: /* 把U-Boot重新定位到RAM */
    adr r0, _start /* r0是代码的当前位置 */
/* adr伪指令,汇编器自动通过当前PC的值算出 如果执行到_start时PC的值,放到r0中:
当此段在flash中执行时r0 = _start = 0;当此段在RAM中执行时_start = _TEXT_BASE(在board/smdk2410/config.mk中指定的值为0x33F80000,即u-boot在把代码拷贝到RAM中去执行的代码段的开始) */
    ldr r1, _TEXT_BASE /* 测试判断是从Flash启动,还是RAM */
/* 此句执行的结果r1始终是0x33FF80000,因为此值是又编译器指定的(ads中设置,或-D设置编译器参数) */
    cmp r0, r1 /* 比较r0和r1,调试的时候不要执行重定位 */

 

    下面,结合u-boot.lds看看一个正式的连接脚本文件。这个文件的基本功能还能看明白,虽然上面分析了好多,但其中那些GNU风格的符号还是着实让我感到迷惑,好菜啊,怪不得连被3家公司鄙视,自己鄙视自己。。。
OUTPUT_FORMAT("elf32&shy;littlearm", "elf32&shy;littlearm", "elf32&shy;littlearm")
  ;指定输出可执行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
  ;指定输出可执行文件的平台为ARM
ENTRY(_start)
  ;指定输出可执行文件的起始代码段为_start.
SECTIONS
{
        . = 0x00000000 ; 从0x0位置开始
        . = ALIGN(4) ; 代码以4字节对齐
        .text : ;指定代码段
        {
          cpu/arm920t/start.o (.text) ; 代码的第一个代码部分
          *(.text) ;其它代码部分
        }
        . = ALIGN(4)
        .rodata : { *(.rodata) } ;指定只读数据段
        . = ALIGN(4);
        .data : { *(.data) } ;指定读/写数据段
        . = ALIGN(4);
        .got : { *(.got) } ;指定got段, got段式是uboot自定义的一个段, 非标准段
        __u_boot_cmd_start = . ;把__u_boot_cmd_start赋值为当前位置, 即起始位置
        .u_boot_cmd : { *(.u_boot_cmd) } ;指定u_boot_cmd段, uboot把所有的uboot命令放在该段.
        __u_boot_cmd_end = .;把__u_boot_cmd_end赋值为当前位置,即结束位置
        . = ALIGN(4);
        __bss_start = .; 把__bss_start赋值为当前位置,即bss段的开始位置
        .bss : { *(.bss) }; 指定bss段
        _end = .; 把_end赋值为当前位置,即bss段的结束位置
}


 

88888888**********************************

r与adr的区别

转自:http://coon.blogbus.com/logs/2738861.html

        ldr     r0, _start

        adr     r0, _start

        ldr     r0, =_start

        nop

        mov     pc, lr

_start:

        nop

        

编译的时候设置 RO 为 0x0c008000

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

0c008000 <_start-0x14>:

c008000:       e59f000c        ldr     r0, [pc, #12]   ; c008014 <_start>

c008004:       e28f0008        add     r0, pc, #8      ; 0x8

c008008:       e59f0008        ldr     r0, [pc, #8]    ; c008018 <_start+0x4>

c00800c:       e1a00000        nop                     (mov r0,r0)

c008010:       e1a0f00e        mov     pc, lr

0c008014 <_start>:

c008014:       e1a00000        nop                     (mov r0,r0)

c008018:       0c008014        stceq   0, cr8, [r0], -#80

分析:

ldr     r0, _start

从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000

adr     r0, _start

取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的时相对的位置。例如这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。

ldr     r0, =_start

这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start 的真正数据,在这里就是 0x0c008014)。

因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014


posted @ 2012-11-19 16:30 yuhen 阅读(2496) | 评论 (1)编辑 收藏
#include <config.h>
#include <version.h>

#if defined(CONFIG_S3C2410)
#include <s3c2410.h>
#elif defined(CONFIG_S3C2440)//include\configs\smdk2440.h中定义。
#include <s3c2440.h>
#endif

#include <status_led.h>

/*************************************************************************
  Jump vector table as in table 3.1 in [1]
 *************************************************************************/
//.global声明一个符号可被其它文件引用,相当于声明了一个
//全局变量,.globl与.global相同。
//该部分为处理器的异常处理向量表。地址范围为
//0x0000 0000 ~ 0x0000 0020,刚好8条指令。

//声明全局变量 _start
.globl _start     /*系统复位位置,整个程序入口*/
_start:	b       start_code    /*各个异常向量对应的相对跳转代码,0x00*/
     /*start_code用b,就是因为start_code在MMU建立前后都有可能发生*/
     /*其他的异常只有在MMU建立之后才会发生*/
	ldr	pc, _undefined_instruction  //未定义指令异常,0x04
	ldr	pc, _software_interrupt    //软中断异常,0x08
	ldr	pc, _prefetch_abort  //内存操作异常,0x0c
	ldr	pc, _data_abort     //数据异常,0x10
	ldr	pc, _not_used      //未适用,0x14
	ldr	pc, _irq          //慢速中断异常,0x18 
	ldr	pc, _fiq         //快速中断异常,0x1c

//.word伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并
//用伪操作中的expr初始化。.long与.int作用与之相同。
/*.word 表达式 ==> 就是在当前位置放一个word型的值,这个值就是"表达式"
;rWTCON: .word 0x15300000 就是在当前地址,即_rWTCON处放一个值0x15300000*/ 

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq


/*.balign[wl] abs-expr, abs-expr, abs-expr
;增加位置计数器(在当前子段)使它指向规定的存储边界。
;第一个表达式参数(结果必须是纯粹的数字)是必需参数:边界基准,单位
为字节。
;例如, '.balign 8'向后移动位置计数器直至计数器的值等于8的倍数。
;如果位置计数器已经是8的倍数,则无需移动。
;第2个表达式参数(结果必须是纯粹的数字)给出填充字节的值,用这个值
填充位置计数器越过的地方。
;第2个参数(和逗点)可以省略。如果省略它,填充字节的值通常是0。
;但在某些系统上,如果本段标识为包含代码,而填充值被省略,则使用
no-op指令填充空白区。
;第3个参数的结果也必须是纯粹的数字,这个参数是可选的。如果存在第
3个参数,
;它代表本对齐命令允许跳过字节数的最大值。如果完成这个对齐需要跳
过的字节数比规定的最大值还多,
;则根本无法完成对齐。您可以在边界基准参数后简单地使用两个逗号,
以省略填充值参数(第二参数);
;如果您在想在适当的时候,对齐操作自动使用no-op指令填充

balignw和.balignl是.balign命令的变化形式。.balignw使用2个字节来填充空白区。
.balignl使用4字节来填充。例如,.balignw 4,0x368d将地址对齐到4的倍数,如果它跳
过2个字节,GAS将使用0x368d填充这2个字节(字节的确切存放位置视处理器
的存储方式而定)。
如果它跳过1或3个字节,则填充值不明确。


//它的含义是以16字节边界对齐,为了对齐而越过的地址以字为单位填冲
//值0xdeadbeef。我猜0xdeadbeef可能NOP指令。
	.balignl 16,0xdeadbeef  //对齐内存为16的倍数
/*************************************************************************
 *
 * Startup Code (called from the ARM reset exception vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************/

// TEXT_BASE在开发板相关的目录中的config.mk文件中定义, 它定义了
// 代码在运行时所在的地址, 那么_TEXT_BASE中保存了这个地址
/*
 *保存变量的数据区,保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者
 其它的使用。
 *还有一些变量的长度是通过连接脚本里得到,实际上由编译器算出来的
 */
//TEXT_BASE定义在\board\smdk2410\config.mk中。

/*TEXT_BASE是代码执行的起始地址.编译产生的二进制文件必需下载到该地
址,因为所有的函数,全局变量等等定位都是以这个地址为参照的.
如果uboot中是TEXT_BASE就是设的0x33F80000, 那么必需download到这个地址的ram中才
能正常运行.
*/  
_TEXT_BASE:  //_TEXT_BASE=TEXT_BASE.
			.word	TEXT_BASE   /*uboot映像在SDRAM中的重定位地址*/

// 标号_start在前面有定义
	
.globl _armboot_start   // /*在_armboot_start标号处,保存了_start的值*/
_armboot_start:     //_armboot_start=_start。
				.word _start    /*_start是程序入口,链接完毕它的值应该是TEXT_BASE*/ 

/*
 * These are defined in the board-specific linker script.
 */
//__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;
//实际上,_armboot_start并没有实际意义,它只是在"ldr r2, _armboot_start"中用來寻
//址_start的值而已,_bss_start也是一样的道理,真正有意义的应该是_start和 
//__bss_start本身。 

.globl _bss_start  /*__bss_start是uboot 的bss段起始地址,*/
_bss_start:         /*uboot映像的大小就是__bss_start - _start*/
		.word __bss_start

.globl _bss_end
_bss_end:
		.word _end

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
				.word	0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
				.word 0x0badc0de
#endif


/* the actual start code*/
start_code:/*复位启动子程序*/
	/*设置cpu运行在SVC32模式。共有7种模式*/	
	mrs	r0,cpsr  /*复制当前程序状态寄存器cpsr到r0*/
	bic	r0,r0,#0x1f  //这里使用位清除指令,把中断全部清除,只置位模式控制位
	                          //7种异常,共占0x00 - 0x16空间
	/*ORR{条件}{S}  <dest>, <op 1>, <op 2>*/
       /*OR 将在两个操作数上进行逻辑或,把结果放置到目的寄存器中*/
	orr	r0,r0,#0xd3 /*选择新模式,(现在设为超级保护模式)*/
	
	msr	cpsr,r0  /*设置cpsr为超级保护模式*/
   /*通过设置ARM的CPSR寄存器,让CPU运行在操作系统模式,为后面进行其它操作作好准备*/

//如果定义了CONFIG_AT91RM9200DK,CONFIG_AT91RM9200EK,CONFIG_AT91RM9200DF中的任意一个
//,就会执行其中的语句.这里没有用。
#if	defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)
	/* relocate exception table*/
	ldr		r0, =_start
	ldr		r1, =0x0
	mov		r2, #16
copyex:
	subs	r2, r2,   #1
	ldr		r3, [r0], #4
	str		r3, [r1], #4
	bne		copyex
#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)

#if defined(CONFIG_S3C2400)
#define pWTCON	0x15300000
#define INTMSK	0x14400008	/* Interupt-Controller base addresses */
#define CLKDIVN	0x14800014	/* clock divisor register */
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#define pWTCON	0x53000000  /*"看门狗定时器控制寄存器"的地址0x53000000*/  
#define INTMSK	0x4A000008 /*"中断屏蔽寄存器"的地址:0x4A000008 */
#define INTSUBMSK	0x4A00001C /*针对INTMAK具体化的一个中断请求屏蔽寄存器,其地
                                                             址0x4A00001C */  
#define LOCKTIME	0x4c000000  //锁时计数寄存器    
#define MPLLCON 	0x4c000004  //MPLL寄存器
#define UPLLCON 	0x4c000008  //UPLL寄存器
#define CLKDIVN	0x4C000014	/*CPU时钟分频控制寄存器,地址0x4C000014*/  
#endif

#if defined(CONFIG_S3C2410)
#define INTSUBMSK_val	0x7ff
#define MPLLCON_val	((0x90 << 12) + (0x7 << 4) + 0x0)	/* 202 MHz */
#define UPLLCON_val	((0x78 << 12) + (0x2 << 4) + 0x3)
#define CLKDIVN_val	3 /* FCLK:HCLK:PCLK = 1:2:4 */
#elif defined(CONFIG_S3C2440)
#define INTSUBMSK_val	0xffff  //以便屏蔽INTSUBMSK的bit[15:0]对应的中断请求
#if (CONFIG_SYS_CLK_FREQ == 16934400)//晶振=16.9344M在include\configs\smdk2440.h中定义。
/*
Mpll = (2 * m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV

Upll = (m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV

MDIV =PLLCON[19:12];  PDIV=PLLCON[9:4];  SDIV=PLLCON[1:0];
*/
//# define MPLLCON_val	((0x61 << 12) + (0x1 << 4) + 0x2)	/* 296.35 MHz */
//# define UPLLCON_val	((0x3c << 12) + (0x4 << 4) + 0x2)	/*  47.98 MHz */
                                //MDIV=184        PDIV=2     SDIV=2                                 
#define MPLLCON_val	((184 << 12) + (2 << 4) + 2)	/*406M*/
                                //MDIV=60        PDIV=4     SDIV=2  
#define UPLLCON_val 	((60 << 12) + (4 << 4) + 2)   /*  47M */
#elif (CONFIG_SYS_CLK_FREQ == 12000000)
#define MPLLCON_val	((0x44 << 12) + (0x1 << 4) + 0x1)	/* 304.00 MHz */
#define UPLLCON_val	((0x38 << 12) + (0x2 << 4) + 0x2)	/*  48.00 MHz */
#endif
#define CLKDIVN_val	7 /* FCLK:HCLK:PCLK = 1:3:6   CLKDIVN=7  */
                       //CPU  : 高速设备:  低速设备
#define CAMDIVN	0x4C000018
#endif

        //禁用看门狗
	ldr       r0, =pWTCON
	mov     r1, #0x0
	str       r1, [r0]

	/* mask all IRQs by setting all bits in the INTMR - default*/
	/*在SVC模式下,屏蔽所有中断发生*/
	mov	  r1, #0xffffffff
	ldr	  r0, =INTMSK
	str	  r1, [r0]
	
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
	ldr	r1, =INTSUBMSK_val  /*子中断同样屏蔽INTSUBMSK_val=0xffff*/
	ldr	r0, =INTSUBMSK
	str	r1, [r0]

/*To reduce PLL lock time, adjust the LOCKTIME register. */
        ldr     r0,=LOCKTIME
        ldr     r1,=0xffffff
        str     r1,[r0]	
#endif

    /* FCLK:HCLK:PCLK = 1:3:6*//* default FCLK is 406M MHz ! */
	ldr	r0, =CLKDIVN
	mov	r1, #CLKDIVN_val
	str	r1, [r0]

#if defined(CONFIG_S3C2440)
      /* Make sure we get FCLK:HCLK:PCLK = 1:3:6 */
        ldr        r0, =CAMDIVN
        mov     r1, #0
        str       r1, [r0]

        /* Clock asynchronous mode */
        mrc     p15, 0, r1, c1, c0, 0
        orr      r1,   r1, #0xc0000000
        mcr     p15, 0, r1, c1, c0, 0

	ldr	r0,=UPLLCON
	ldr	r1,=UPLLCON_val
	str	r1,[r0]
	
	nop	
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	
	ldr	r0,=MPLLCON
	ldr	r1,=MPLLCON_val
	str	r1,[r0]
//
#define GPJCON      0x560000D0
#define GPJDAT       0x560000D4
#define GPJUP		0x560000D8
/*
        LDR   R0,   = GPJCON
        LDR   R1,   = 0x15555
        STR   R1,   [R0]
        LDR   R0,   = GPJUP
        LDR   R1,   = 0x1f
        STR   R1,   [R0]
        LDR   R0,   = GPJDAT  
//    LDR   R1,   = 0xffff
        LDR   R1,   = 0x00
        STR   R1,   [R0]
*/
#endif

#endif	/* CONFIG_S3C2400 || CONFIG_S3C2410 */

	/*
	 * we do sys-critical inits only at reboot,
	 * not when booting from ram!
	 */
	//bl      LED_FLASH
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
/*这些初始化代码在系统重启的时候执行,运行时热复位从RAM中启动不执行*/
	bl	cpu_init_crit
#endif
#if 1
        LDR   R0,   = GPJCON
        LDR   R1,   = 0x15555
        STR   R1,   [R0]
        LDR   R0,   = GPJUP
        LDR   R1,   = 0x1f
        STR   R1,   [R0]
        LDR   R0,   = GPJDAT
        LDR   R1,   = 0x00
        STR   R1,   [R0]
#endif
	
#if defined(CONFIG_AT91RM9200) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
#ifndef CONFIG_S3C2410_NAND_BOOT
//NOR_BOOT :
relocate:	/* 把U-BOOT重新定位到RAM*/
         //r0=0;
	adr	r0, _start		/* r0是代码的当前位置*/
	//r1=TEXT_BASE = 0x33F80000
	ldr	r1, _TEXT_BASE	/*测试判断是从FLASH启动,还是RAM  */
	cmp     r0, r1     /*比较R0、R1,调试的时候不需要重定位。 */
	beq     stack_setup  /*如果R0等于R1,跳到重定位代码。*/

	//如果不是从RAM运行的话,则将代码拷贝到_TEXT_BASE标识的RAM中。
	/*准备重新定义代码。*/
	ldr	r2, _armboot_start//_armboot_start=_start
	ldr	r3, _bss_start  //
	sub	r2, r3, r2		/* r2得到armboot的大小*/
	add	r2, r0, r2		/* r2得到要复制代码的末尾地址*/
//kaobei guo cheng
copy_loop:/*重新定位代码*/
	ldmia	r0!, {r3-r10}		/*从源地址[r0]复制,r0指向_start(=0)*/
	stmia	r1!, {r3-r10}		/*复制到目的地址[r1],r1指向_TEXT_BASE(=0x33F80000)*/
	cmp	r0, r2			/* 复制数据块直到源数据末尾地址[r2]*/
	ble	copy_loop
#else /* NAND_BOOT */
//relocate:
copy_myself:
	/* mov	r10, lr */
#if defined(CONFIG_S3C2410)
	@ reset NAND
	mov	r1, #S3C2410_NAND_BASE
	ldr	r2, =0xf842		@ initial value enable tacls=3,rph0=6,rph1=0
	str	r2, [r1, #oNFCONF]
	ldr	r2, [r1, #oNFCONF]
	bic	r2, r2, #0x800		@ enable chip
	str	r2, [r1, #oNFCONF]
	mov	r2, #0xff		@ RESET command
	strb	r2, [r1, #oNFCMD]
	mov	r3, #0			@ wait
1:	add	r3, r3, #0x1
	cmp	r3, #0xa
	blt	1b
2:	ldr	r2, [r1, #oNFSTAT]	@ wait ready
	tst	r2, #0x1
	beq	2b
	ldr	r2, [r1, #oNFCONF]
	orr	r2, r2, #0x800		@ disable chip
	str	r2, [r1, #oNFCONF]
#elif defined(CONFIG_S3C2440)
        /*从NAND闪存中把U-BOOT拷贝到RAM*/
	mov	r1, #S3C2440_NAND_BASE //S3C2440_NAND_BASE=0x4E000000
	ldr	r2, =0xfff0		@ initial value tacls=3,rph0=7,rph1=7
	ldr	r3, [r1, #oNFCONF] //oNFCONF=0x00
	orr	r3, r3, r2
	str	r3, [r1, #oNFCONF]//oNFCONF=0x00

	ldr	r3, [r1, #oNFCONT] //oNFCONT=0x04
	orr	r3, r3, #1		@ enable nand controller
	str	r3, [r1, #oNFCONT]//oNFCONT=0x04
#endif  //if defined(CONFIG_S3C2410)

#if 0
	@ get ready to call C functions (for nand_read())
	ldr	sp, DW_STACK_START	@ setup stack pointer
	mov	fp, #0			@ no previous frame, so fp=0
#else
	ldr	r0, _TEXT_BASE		/* upper 128 KiB: relocated uboot   */
	
        /* CFG_MALLOC_LEN=(CFG_ENV_SIZE + 2048*1024) =0x210000 ;   CFG_ENV_SIZE	= 0x10000
             CFG_GBL_DATA_SIZE=128	*/
	sub	r0, r0, #CFG_MALLOC_LEN	/* malloc area                      */
	sub	r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
#ifdef CONFIG_USE_IRQ  /*include/configs/smdk2440.h*/
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)  //8K+4K
#endif
	sub	sp, r0, #12		/* leave 3 words for abort-stack    */
#endif   //#if 0

	@ copy u-boot to RAM
	ldr	r0, _TEXT_BASE //置第1个参数: UBOOT在RAM中的起始地址
	mov     r1, #0x0   //设置第2个参数:NAND闪存的起始地址
	//CFG_UBOOT_SIZE=0x40000=256k
	mov	r2, #CFG_UBOOT_SIZE  // 设置第3个参数: U-BOOT的长度(256KB)
	bl	nand_read_ll   //调用nand_read_whole(),把NAND闪存中的数据读入到RAM中

	tst	r0, #0x0 // 如果函数的返回值为0,表示执行成功
	beq	ok_nand_read  //执行内存比较,把RAM中的前4K内容与NAND闪存中的前4K内容进行比较, 如果完全相同, 则表示搬移成功
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
	ldr	r0, STR_FAIL
	ldr	r1, SerBase
	bl	PrintWord
1:	b	1b		@ infinite loop
#endif

ok_nand_read:
#ifdef CONFIG_DEBUG_LL
	ldr	r0, STR_OK
	ldr	r1, SerBase
	bl	PrintWord
#endif

	@ verify
	mov	r0, #0
	@ldr	r1, =0x33f00000
	ldr	r1, _TEXT_BASE
	mov	r2, #0x400	@ 4 bytes * 1024 = 4K-bytes
go_next:
	ldr	r3, [r0], #4
	ldr	r4, [r1], #4
	teq	r3, r4
	bne	notmatch
	subs	r2, r2, #4
	beq	done_nand_read
	bne	go_next
notmatch:
#ifdef CONFIG_DEBUG_LL
	sub	r0, r0, #4
	ldr	r1, SerBase
	bl	PrintHexWord
	ldr	r0, STR_FAIL
	ldr	r1, SerBase
	bl	PrintWord
#endif

#if 1
        LDR   R0,   = GPJDAT
        LDR   R1,   = 0x4
        STR   R1,   [R0]
#endif
1:	b	1b
done_nand_read:
#if 1
        LDR   R0,   = GPJDAT
        LDR   R1,   = 0x2
        STR   R1,   [R0]
#endif

#endif /* NAND_BOOT */
#endif	/* CONFIG_SKIP_RELOCATE_UBOOT */
#endif
	/* 初始化堆栈*/
stack_setup:
	ldr	r0, _TEXT_BASE		/*上面是128kib重定位的u-boot*/
	/*在smdk244.h中定义 #define CFG_MALLOC_LEN		(CFG_ENV_SIZE + 2048*1024)
	                    #define CFG_ENV_SIZE	    0x10000 
			   #define CFG_GBL_DATA_SIZE	128	*/
	sub	r0, r0, #CFG_MALLOC_LEN	/*向下是内存分配空间*/
	sub	r0, r0, #CFG_GBL_DATA_SIZE /*然后是bdinfo结构体地址空间*/
#ifdef CONFIG_USE_IRQ  //在 smdk2440.h中定义。
    /*在smdk244.h中定义#define CONFIG_STACKSIZE_IRQ	(8*1024)	
                                                 #define CONFIG_STACKSIZE_FIQ	(4*1024)	 */
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
	sub	sp, r0, #12		/*为 abort-stack 预留3个字,得到最终sp指针初始值*/

clear_bss:
	ldr	r0, _bss_start		/*找到bss 段起始地址。*/
	ldr	r1, _bss_end		/* bss 段末尾地址。*/
	mov r2, #0x00000000		/* 清零。*/

clbss_l:str	r2, [r0]		/*bss 段地址空间清零循环。。。*/
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l
#if 1
        LDR   R0,   = GPJDAT
        LDR   R1,   = 0x1
        STR   R1,   [R0]
#endif
     /*跳转到start_armboot函数入口,_start_armboot字保存函数入口指针*/
	ldr	pc, _start_armboot

//_start_armboot=start_armboot
//pc=start_armboot;
//去执行void start_armboot (void),在lib_arm/boarb.c中。
_start_armboot:	.word start_armboot


/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */

//功能:设置CP15寄存器 这里完成的功能:失效Icache和Dcache,禁能MMU和cache 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
//关键的初始化子程序。
cpu_init_crit:
	 /*  flush v4 I/D caches  | 失效指令cache和数据cache       */
	mov	r0, #0
	//使I/D cache失效:将寄存器r0的数据传送到协处理器p15的c7中。C7寄存器
	//位对应cp15中的cache控制寄存器
	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
	//使TLB操作寄存器失效:将r0数据送到cp15的c8、c7中。C8对应TLB操作
	//寄存器
	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */

	/*
	 * disable MMU stuff and caches
	 */
	 /*   disable MMU stuff and caches |   禁能MMU和cache       */ 
	mrc	p15, 0, r0, c1, c0, 0 //先把c1和c0寄存器的各位置0(r0 = 0)
	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
	bic	r0, r0, #0x00000087	@ clear bits 7, 2:0 (B--- -CAM)
	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * before relocating, we have to setup RAM timing
	 * because memory timing is board-dependend, you will
	 * find a lowlevel_init.S in your board directory.
	 */
	mov	ip, lr
#if	defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)

#else
	bl	lowlevel_init  //位于board/smdk2440/lowlevel_init.S:用于完成芯片存储器的初始化,
	                             //执行完成后返回
#endif
	mov	lr, ip
	mov	pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */

@
@ IRQ stack frame.
@
#define S_FRAME_SIZE	72

#define S_OLD_R0	68
#define S_PSR		64
#define S_PC		60
#define S_LR		56
#define S_SP		52

#define S_IP		48
#define S_FP		44
#define S_R10		40
#define S_R9		36
#define S_R8		32
#define S_R7		28
#define S_R6		24
#define S_R5		20
#define S_R4		16
#define S_R3		12
#define S_R2		8
#define S_R1		4
#define S_R0		0

#define MODE_SVC 0x13
#define I_BIT	 0x80

/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */

	.macro	bad_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}			@ Calling r0-r12
	ldr	r2, _armboot_start
	sub	r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
	sub	r2, r2, #(CFG_GBL_DATA_SIZE+8)  @ set base 2 words into abort stack
	ldmia	r2, {r2 - r3}			@ get pc, cpsr
	add	r0, sp, #S_FRAME_SIZE		@ restore sp_SVC

	add	r5, sp, #S_SP
	mov	r1, lr
	stmia	r5, {r0 - r3}			@ save sp_SVC, lr_SVC, pc, cpsr
	mov	r0, sp
	.endm

	.macro	irq_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}			@ Calling r0-r12
	add     r8, sp, #S_PC
	stmdb   r8, {sp, lr}^                   @ Calling SP, LR
	str     lr, [r8, #0]                    @ Save calling PC
	mrs     r6, spsr
	str     r6, [r8, #4]                    @ Save CPSR
	str     r0, [r8, #8]                    @ Save OLD_R0
	mov	r0, sp
	.endm

	.macro	irq_restore_user_regs
	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
	mov	r0, r0
	ldr	lr, [sp, #S_PC]			@ Get PC
	add	sp, sp, #S_FRAME_SIZE
	subs	pc, lr, #4			@ return & move spsr_svc into cpsr
	.endm

	.macro get_bad_stack
	ldr	r13, _armboot_start		@ setup our mode stack
	sub	r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
	sub	r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack

	str	lr, [r13]			@ save caller lr / spsr
	mrs	lr, spsr
	str     lr, [r13, #4]

	mov	r13, #MODE_SVC			@ prepare SVC-Mode
	@ msr	spsr_c, r13
	msr	spsr, r13
	mov	lr, pc
	movs	pc, lr
	.endm

	.macro get_irq_stack			@ setup IRQ stack
	ldr	sp, IRQ_STACK_START
	.endm

	.macro get_fiq_stack			@ setup FIQ stack
	ldr	sp, FIQ_STACK_START
	.endm

/*
 * exception handlers
 */
	.align  5
undefined_instruction:
	get_bad_stack
	bad_save_user_regs
	bl 	do_undefined_instruction

	.align	5
software_interrupt:
	get_bad_stack
	bad_save_user_regs
	bl 	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl 	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl 	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl 	do_not_used

#ifdef CONFIG_USE_IRQ

	.align	5
irq:
	get_irq_stack
	irq_save_user_regs
	bl 	do_irq
	irq_restore_user_regs

	.align	5
fiq:
	get_fiq_stack
	/* someone ought to write a more effiction fiq_save_user_regs */
	irq_save_user_regs
	bl 	do_fiq
	irq_restore_user_regs

#else

	.align	5
irq:
	get_bad_stack
	bad_save_user_regs
	bl 	do_irq

	.align	5
fiq:
	get_bad_stack
	bad_save_user_regs
	bl 	do_fiq

#endif
posted @ 2012-10-30 16:49 yuhen 阅读(520) | 评论 (0)编辑 收藏

最近遇到一个设置系统sleep状态的case,最后从客户那里得到了设置方法~就是用Powercfg 命令

powercfg -energy

顺便找了一些powercfg的其他命令行选项在下面:

powercfg [-l] [-q ] [-x] [-changename] [-duplicatescheme] [-d] [-deletesetting] [-setactive] [-getactivescheme] [-setacvalueindex] [-setdcvalueindex] [-h] [-a] [-devicequery] [-deviceenablewake] [-devicedisablewake] [-import] [-export] [-lastwake] [-?][-aliases] [-setsecuritydescriptor] [-getsecuritydescriptor]


选项            描述           

-list

-l

列出当前用户环境中的所有电源方案。

例如:

powercfg -list

-query [Scheme_GUID] [Sub_GUID]

-q [Scheme_GUID] [Sub_GUID]

显示指定的电源方案的内容。

用法:

powercfg -query [Scheme_GUID] [Sub_GUID]

SCHEME_GUID

(可选)指定要显示的电源方案的 GUID。可以使用 powercfg -l 命令获取。

SUB_GUID

(可选)指定要显示的子组的 GUID。要求提供 SCHEME_GUID

如果未提供 SCHEME_GUIDSUB_GUID,则显示当前用户的活动电源方案的设置。

如果未指定 SUB_GUID,则显示指定电源方案中的所有设置。

-changesettingvalue

-xsetting value

修改当前电源方案中的设置值。

用法:

powercfg-xsetting value

设置

指定以下设置之一:

-monitor-timeout-ac分钟

-monitor-timeout-dc分钟

-disk-timeout-ac分钟

-disk-timeout-dc分钟

-standby-timeout-acminutes

-standby-timeout-dc分钟

-hibernate-timeout-ac分钟

-hibernate-timeout-dc分钟

指定值,以分钟为单位。

例如:

powercfg-change-monitor-timeout-ac5

这将监视器使用交流电源时的空闲超时值设置为五分钟。

-changenameGUID name [scheme_description]

修改电源方案的名称,也可以修改方案描述。

用法:

powercfg-changenameGUID namescheme_description

GUID

指定电源方案的 GUID

名称

指定电源方案的名称。

scheme_description

描述电源方案。

如果忽略描述,将仅更改名称。

-duplicateschemeGUID [DestinationGUID]

复制指定的电源方案。将显示产生的 GUID(表示新方案)。

用法:

powercfg-duplicateschemeGUID [DestinationGUID]

GUID

指定方案 GUID。通过使用 powercfg-l 命令获取。

DestinationGUID

指定将在其中复制方案的 GUID。

如果省略 DestinationGUID,则将为重复方案创建新 GUID。

-deleteGUID

-dGUID

删除带有指定 GUID 的电源方案。

用法:

Powercfg-deleteGUID

GUID

指定方案的 GUID。使用 -list 选项获取。

-deletesettingSub_GUID Setting_GUID

删除电源设置。

用法:

powercfg-deletesettingSub_GUID Setting_GUID

Sub_GUID

指定子组 GUID。

Setting_GUID

指定电源设置 GUID。

-setactiveScheme_GUID

-s Scheme_GUID

使计算机上指定的电源方案成为活动的方案。

用法:

Powercfg-setactiveScheme_GUID

Scheme_GUID

指定方案 GUID。

-getactivescheme

检索当前活动的电源方案。

用法:

Powercfg-getactivescheme

-setacvalueindexScheme_GUID Sub_GUID Setting_GUID SettingIndex

设置在计算机使用交流电源供电时与指定电源设置相关联的值。

用法:

powercfg-setacvalueindexScheme_GUIDSub_GUIDSetting_GUIDSettingIndex

Scheme_GUID

指定电源方案 GUID。使用 -l 选项获取。

Sub_GUID

指定电源设置 GUID 的子组。使用 -q 选项获取。

Setting_GUID

指定单个电源设置 GUID。通过使用 -q 选项获取。

SettingIndex

指定此电源设置将会设置为可能值列表中的哪个。

例如:

powercfg-setacvalueindexScheme_GUID Sub_GUID Setting_GUID 5

这会将电源设置的交流电源值设置为此电源设置可能值列表中的第五项。

-setdcvalueindexScheme_GUID Sub_GUID Setting_GUID SettingIndex

设置在计算机使用直流电源供电时与指定电源设置相关联的值。

用法:

powercfg-setdcvalueindexScheme_GUID Sub_GUID Setting_GUID SettingIndex

Scheme_GUID

指定电源方案 GUID。通过使用 -l 选项获取。

Sub_GUID

指定电源设置 GUID 的子组。通过使用 -q 选项获取。

Setting_GUID

指定单个电源设置 GUID。通过使用 -q 选项获取。

SettingIndex

指定此电源设置将设置为可能值的列表中的哪一个。

例如:

powercfg-setdcvalueindexScheme_GUID Sub_GUID Setting_GUID 5

这会将电源设置的直流电源值设置为此电源设置可能值列表中的第五项。

-hibernate [on|off]

-h [on|off]

启用或禁用休眠功能。所有计算机上均不支持休眠超时。

例如:powercfg-h on

-availablesleepstates

-a

报告计算机上可用的睡眠状态。尝试报告睡眠状态不可用的原因。

-devicequeryquery_flags

返回符合指定条件的设备。

用法:

powercfg-devicequeryquery_flags

query_flags

指定以下条件之一:

wake_from_S1_supported - 返回支持将计算机从轻度睡眠状态中唤醒的所有设备。

例如:

powercfg -devicequery wake_armed

-deviceenablewakedevicename

使设备可以将计算机从睡眠状态中唤醒。

用法:

powercfg-deviceenablewakedevicename

devicename

指定通过使用 powercfg-devicequerywake_programmable 命令检索的设备。

例如:

powercfg-deviceenablewake"Microsoft USB IntelliMouse Explorer"

-devicedisablewakedevicename

使设备不能将计算机从睡眠状态中唤醒。

用法:

powercfg-devicedisablewakedevicename

devicename

指定通过使用 powercfg-devicequerywake_armed 命令检索的设备。

-import filename [GUID]

从指定的文件导入所有电源设置。

用法:

powercfg-importfilename [GUID]

filename

指定通过使用 powercfg-export 选项生成的文件的完全限定路径。

GUID

(可选)表示加载到电源方案的设置。如果未提供,则 Powercfg 将生成并使用新的 GUID

例如:

powercfg-importc:\scheme.pow

-export filename GUID

将指定 GUID 表示的电源方案导出到指定文件。

用法:

powercfg -export filename GUID

filename

指定目标文件的完全限定路径。

GUID

指定电源方案 GUID。使用 -/l 选项获取。

例如:

powercfg -export c:\scheme.pow 381b4222-f694-41f0-9685-ff5bb260df2e

-lastwake

报告有关将计算机从最后一个睡眠转换中唤醒的事件的信息。

-help

-?

显示有关 Powercfg 命令行选项的信息。

-aliases

显示所有别名及其相应的 GUID。用户可能在命令提示符处使用这些别名来代替任意 GUID

-setsecruitydescriptor [GUID|Action] SDDL

设置与指定的电源设置、电源方案或操作相关联的安全描述符。

用法:

powercfg -setsecuritydescriptor [GUID|Action] SDDL

GUID

指定电源方案或电源设置 GUID。

Action

指定以下字符串之一:ActionSetActiveActionCreateActionDefault

SDDL

指定 SDD 格式的有效的安全描述符字符串。调用 powercfg -getsecuritydescriptor 来查看示例 SDDL STRING。

-getsecuritydescriptor [GUID|Action]

获取与指定的电源设置、电源方案或操作相关联的安全描述符。

用法:

powercfg -getsecuritydescriptor [GUID|Action]

GUID

指定电源方案或电源设置 GUID。

Action

指定以下一个字符串:ActionSetActiveActionCreateActionDefault

posted @ 2012-06-05 15:27 yuhen 阅读(1247) | 评论 (2)编辑 收藏

ACPI BIOS implementations that directly access certain system hardware resources from AML code cannot be synchronized with operating system access to the same resources, which can cause the operating system to become unstable or to stop responding. This article describes issues related to BIOS AML firmware accessing system board resources, and the changes implemented to address these issues in the Windows XP, Windows Server 2003, and future versions of the operating system.

 

Introduction

During the development of the Windows XP operating system, Microsoft discovered many ACPI BIOS implementations that directly access and attempt to manipulate system hardware resources from BIOS ASL code. At run time, system board resources must not be simultaneously accessed or modified by both the BIOS and the operating system, because these accesses cannot be synchronized with operating system access to the same system resources. As a result, BIOS read or write access to these resources can cause adverse effects, ranging from general instability to causing the system to stop responding.

Historically, error conditions of this nature might have been construed by the operating system as an unrecoverable error, resulting in a stop error (blue screen) and system shutdown. In an effort to improve the end user experience and increase system reliability, Windows XP has been re-designed as described in this article.

Changes in Windows XP

While developing and testing Windows XP a list of system board resources was identified that, when accessed from BIOS AML code, proved to be the most problematic. This article refers to the list of addresses associated with these resources as the "blocked ports list." Table 1 lists affected system resources and their associated I/O addresses.

The ACPI AML interpreter in the Windows XP kernel monitors all attempts by BIOS AML code to read from or write to the specific addresses on the blocked ports list. When a read or write access is detected at any of these addresses, the following actions will occur:

An error will be added to the system event log stating that the ACPI AML interpreter has detected an illegal read or write, and that this read or write has been blocked.

For BIOS AML code that indicates compatibility with the Windows XP ACPI implementation, the operating system will block all read and write accesses to these addresses. This compatibility is determined by the AML code calling the _OSI method, as described later in this article.

For BIOS AML code that is compatible with versions of Windows released before Windows XP, the operating system will allow the read or write access to succeed in most cases, but will still add an error to the system event log.

Accesses to the Programmable Interrupt Controller (PIC) and cascaded PIC, and to PIC Edge/Level Control Registers, are always blocked because BIOS access to these ports is potentially catastrophic and might prevent the system from running. These I/O addresses are noted in Table 1.

 

Windows XP BIOS Compatibility Determined Using _OSI

The specific actions taken by the operating system when read or write accesses to these resources are detected depends on the version of ACPI interface that the BIOS indicates it supports. This is determined through the BIOS use of the _OSI method as described in this article.

For complete details about the _OSI method, see How to Identify Windows Versions in ACPI Using _OSI.

The _OSI method is being introduced with the Windows XP operating system. BIOS ASL code can test for the level of features supported in the current Windows operating system by passing a string to the _OSI method. The operating system returns TRUE for any string that represents a feature set that it can support. Windows XP, for example, returns TRUE for the string "Windows 2001".

By passing the string "Windows 2001" into the _OSI method, the BIOS indicates to the operating system that the BIOS is aware of and compatible with the ACPI implementation and feature set of Windows XP. Windows XP will then reject all I/O reads or writes from BIOS ASL code to addresses on the blocked ports list and generate an error in the system event log.

Windows XP and Legacy BIOS ASL Implementations

For those BIOS implementations that are compatible with versions of Windows released before Windows XP, the operating system will allow the read or write access to succeed in most cases. Accesses to the PIC and cascaded PIC, and to the PIC Edge/Level Control Registers, are always blocked. In all cases an error will be written to the system event log.

To help ensure system stability on systems with legacy BIOS ASL implementations, Windows XP attempts to synchronize accesses to resources whenever possible. For example:

When a read or write access is detected to the CMOS/RTC index and data pair registers at I/O addresses 0x70 and 0x71, the operating system does the following:

Disables interrupts

Preserves the contents of the index (address) register

Allows the ASL code access to proceed

Restores the value of the index register

This process helps prevent a race condition in which the operating system writes the desired address to the index register and a BIOS method changes the address in the index register before the operating system can access the intended data register, causing the operating system to use the wrong index value.

Note: This approach cannot guarantee that the CMOS address register (0x70) will not be corrupted on multi-processor systems.

ASL code that attempts to access the PCI Configuration Space registers at 0xCF8 0xD00 is re-routed by the kernel AML interpreter to call HalGetBusDataByOffset, which allows access to system resources to be properly synchronized.

 

Accessing BIOS Non-volatile Memory (CMOS NVRAM)

With these changes in Windows XP, BIOS developers may be looking for alternative methods to access CMOS NVRAM. The following examples present some possible solutions.

Reading from CMOS NVRAM

This example ASL code defines an operation region in the system memory address space. During POST, BIOS code copies the data from CMOS to this memory area. ASL code can then use fields to read the CMOS data.

Each CMnn field in the example corresponds to 8 bits of CMOS. The BIOS engineer is responsible for defining these fields appropriately. Each field of this memory can be any combination of bits.

During system boot, BIOS POST code should update this memory area with the value written in the CMOS before handing off to the operating system. Once the operating system has switched to ACPI mode, any attempt to read CMOS from ASL code should be done by reading from these memory locations and not from CMOS NVRAM I/O ports.

// Declare a memory operation region for 255 bytes of CMOS. 
// First 0 to 127 bytes reflects CMOS access from IO ports 0x70 
// and 0x71 and the subsequent bytes reflect CMOS access from IO 
// ports 0x72 to 0x73.  BIOS code should properly set up the base 
// address of the beginning of this memory range at offset during 
// BIOS POST.

OperationRegion (CMRM, SystemMemory, offset, 255)  //Operation Region 

Field (CMRM, AnyAcc, NoLock, Preserve) {   //Field
    //  Memory corresponding to addresses 0x70, 0x71
    CM00,    8,
    CM01,    8,
    CM02,    8,
    .
    .
    CM7f,     8,     

    // Memory corresponding to addresses 0x72, 0x73
    CM80,    8,
    .
    .
    CMFF
    }   // End of CMOS field

The following is an example of ASL code which reads from CMOS offset 52 bits 4 to 7 and returns the value read.

// This example returns bits 4 to 7 of CMOS offset 52

Method (RDCM) {
    ShiftRight (CM52, 4, Local0)
    Return (Local0) 
    }

Writing to CMOS NVRAM

BIOS code should write to CMOS NVRAM by generating a system management interrupt (SMI). AML code can generate a SMI by writing a specific value to the SMI command port. AML code can pass the CMOS offset and value to be written through the NVRAM memory operation region. The BIOS SMI handles the writes to CMOS, and also updates the memory area pointed by the CMRM operation region to reflect the correct CMOS contents.

Affected System Resources and Addresses

Table 1 lists the system resources and associated I/O addresses that should not be directly accessed by BIOS AML code (the "blocked ports" list).

Table 1 Blocked I/O Port Addresses and System Board Resources

Address Function Comments

0x000 0x00F

DMA Controller 1

 

0x020 0x021

Programmable Interrupt Controller

Access is never allowed*

0x040 0x043

System Timer 1

 

0x048 0x04B

Timer 2 Failsafe

 

0x070 0x071

System CMOS, RTC

 

0x074 0x076

Extended CMOS

 

0x081 0x083

DMA1 Page Registers

 

0x087

DMA1 CH0 Low Page

 

0x089

DMA2 CH2 Low Page

 

0x08A 0x08B

DMA2 CH3 Low Page,

 

0x08F

DMA2 Low Page Refresh

 

0x090 0x091

Arbitration Control Port Card Select Feedback

 

0x093 0x094

Reserved System Board Setup

 

0x096 0x097

POS Channel Select

 

0x0A0 0x0A1

Cascaded Programmable Interrupt Controller

Access is never allowed*

0x0C0 0x0DF

ISA DMA

 

0x4D0 0x4D1

PIC Edge/Level Control Registers

Access is never allowed*

0xCF8 0xD00

PCI Configuration Space Access

 

*Read or write accesses to these ports are always blocked, regardless of the BIOS use of the _OSI method.

Call to Action

BIOS developers should design their BIOS code according to the guidelines in this article:

BIOS code that is compatible with Windows XP should access system resources as described in this article; they should not directly read from or write to any of the I/O port addresses listed in Table 1.

BIOS code that is compatible with earlier versions of Windows released before Windows XP should not directly access the PIC and cascaded PIC, or PIC Edge/Level Control Registers.

BIOS code should report compatibility with Windows XP as described in How to Identify Windows Versions in ACPI Using _OSI.

posted @ 2009-12-31 14:54 yuhen 阅读(809) | 评论 (1)编辑 收藏

        如果EFI支持64-bit的话,那么就可以启动Windows 7, 还不是因为MS不好好支持32-bit,这样就不需要CSM module了
        那么如果EFI支持64-bit的话,SMM driver也就变成64-bit了,这个时候SMM就必须从Real mode 转成64-bit protect mode,其实主要的问题就是CPU的mode是IA-32e mode,这是由CPU的一个寄存器来决定的,具体的就是MSR(C0000080h)的bit8,所以在SMI handler中就需要load 64-bit的GDT,然后切成保护模式之前,设置CPU的这个寄存器,使之变成64-bit,其他的和32-bit的一模一样,就可以去执行64-bit的C程序了
        需要强调的是,64-bit汇编code中,eax就用rax了,等等。
        贴一张图吧,说明一下CPU的不同模式:

posted @ 2009-11-09 18:26 yuhen 阅读(1147) | 评论 (1)编辑 收藏
 

       ACPI TableBIOS提供给OSPM的硬件配置数据,包括系统硬件的电源管理和配置管理,ACPI Table有很多表,根据存储的位置,可以分为:

1) RSDP位于F段,用于OSPM搜索ACPI TableRSDP可以定位其他所有ACPI Table

2) FACS位于ACPI NVS内存,用于系统进行S3保存的恢复指针,内存为NV Store

3) 剩下所有ACPI Table都位于ACPI Reclaim内存,进入OS后,内存可以释放

           ACPI Table根据版本又分为1.0B2.03.0,4.02.0以后,支持了64-bit的地址空间,因此几个重要的Table会不大一样,比如:RSDPRSDTFADTFACS。简单的列举一下不同版本的ACPI Table

1) ACPI 1.0BRSDP1RSDTFADT1FACS1DSDTMADTSSDTHPETMCFG

2) ACPI 3.0 RSDP3RSDTXSDTFADT3FACS3DSDTMADTHPETMCFGSSDT

以系统支持ACPI3.0为例子,说明系统中ACPI table之间的关系如图:

 

 

 

 其中绿色代表在内存F段,蓝色是ACPI Reclaim内存,红色是NV store内存


 

posted @ 2009-10-21 16:04 yuhen 阅读(10046) | 评论 (2)编辑 收藏

    当我们在coding的时候,使用两个变量相加的情况,或者用 +,或者用 | ,都是没有问题,比如:
    0x1000 + 0x55 = 0x1055
    0x1000 | 0x55  = 0x1055
    介于以前这种固有的思维,因此没有把其中不同仔细考虑,直到这几天的bug,才恍然大悟,还是因为基础知识的不扎实和习惯性的思维导致这个bug,不过也给我了一个机会,彻底的搞清楚这两个运算符在变量相加的时候的区别。
    因为code是这么定义的:
#define PCI_ADDR(bus,device,func,reg)  (UINT64)(bus<<24 + device<<16 + func<<8 + reg ) & 0x00000000FFFFFFFF
#define DRAM_PCI    PCI_ADDR(0,0,3,0)
    UINT64 Addr;
    假如我要访问寄存器0x55
    (1) Addr = DRAM_PCI + 0x55;
    (2) Addr = DRAM_PCI  | 0x55;
     那么请问(1)和(2)相等吗?
     答案是: (1) Addr = 0;
                       (2) Addr = RealValue;

     那么为什么在(1)中,Addr = 0呢,问题还是出在PCI_ADDR这个宏定义上:
     因为宏定义最后做了一个与运算,所以在之后的运算中就需要和 + 或者 | 进行优先级的比较,由于+  》 &  》 | ,因此当使用 | 的时候,就不会出现问题,能够和我们想要的运算一致;当使用 + 的时候,由于 + 比 & 优先级高,因此就率先计算了 0x00000000FFFFFFFF + 0x55,导致得到了新值 0x0000000100000054,然后和之前的数值进行 &, 不就得到了 0 嘛
     所以得到的经验教训:
     1)在复杂的定义面前不能想当然的去使用自己认为没有问题的运算符,因为检查这个问题可不是这么容易找到根源的。
     2)在进行宏定义的时候最好给整个宏加上括号,那么就可以避免很多优先级的问题,因为调用这个宏的人可能多种多样。
     3)尽可能的使用最简单的运算符进行coding,可以避免一些问题,当然如果反复调用的情况下,还是需要定义宏,当然定义要慎重,参考2)
     4)基础很重要啊,要能快速的找到问题的根源还是需要熟悉各种运算符的优先级,这次又好好的复习了一下。

顺便附上C语言中常用运算符的优先级:
1 () [] -> .   ::  ! ~ ++ --
2 - (unary) * (dereference)  & (address of) sizeof
3 ->* .*
4 * (multiply) / %
5 + -
6 << >>
7 < <= > >=
8 == !=
9 & (bitwise AND)
10 ^
11 |
12 &&
13 ||
14 ? :
15 = += -= etc.
16 ,

posted @ 2009-05-22 13:18 yuhen 阅读(1393) | 评论 (4)编辑 收藏
仅列出标题
共12页: 1 2 3 4 5 6 7 8 9 Last 
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

Believe in who you are,
you are a shinning star!

常用链接

留言簿(16)

随笔分类(122)

随笔档案(116)

文章分类(2)

文章档案(2)

相册

BLOG

Study

Testing

最新随笔

搜索

  •  

积分与排名

  • 积分 - 119986
  • 排名 - 54

最新随笔

最新评论

阅读排行榜

评论排行榜