2009年2月4日

miniBfin启动busybox成功

1. 设置CONFIG_INITRAMFS_SOURCE="image根目录",不需要设置roo=....内核参数,比linux2.4方便多了。
2. 写一个启动shell的程序
#include <stdio.h>
#include <unistd.h>

int
main(init argc, char argv[])
{
printf("Hello world from initramfs!\n");
system("/bin/sh");
sleep(99999);
return 0;
}
3.编译
bfin-linux-uclibc-gcc -o init -static -s init.c
4.将init放到image根目录下面
5.创建console设备
mknod -m 600 dev/console c 5 1

请参考:深入理解 Linux 2.6 的 initramfs 机制 (上)

启动后的信息:
U-Boot 1.1.5 (Feb 4 2009 - 14:52:53)

CPU: ADSP BF532 Rev.: 0.5
Board: miniBfin
http://bfincn.blogspot.com
Clock: VCO: 400 MHz, Core: 400 MHz, System: 133 MHz
SDRAM: 32 MB
*** Warning - bad CRC, using default environment

In: serial
Out: serial
Err: serial
No ethernet found.
starting from spi flash
Hit any key to stop autoboot: 0

EEPROM @0x0 read: addr 0x01000000 off 0x30000 count 0x200000

done
## Booting image at 01000000 ...
Image Name: Linux-2.6.28-ADI-2009R1-pre-svn6
Image Type: Blackfin Linux Kernel Image (gzip compressed)
Data Size: 1921753 Bytes = 1.8 MB
Load Address: 00001000
Entry Point: 001b0fa4
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting Kernel at = 1b0fa4
Linux version 2.6.28-ADI-2009R1-pre-svn6028 (root@debian) (gcc version 4.1.2 (ADI svn)) #37 Wed Feb 4 14:32:22 CST 2009
Board Memory: 32MB
Kernel Managed Memory: 32MB
Memory map:
fixedcode = 0x00000400-0x00000490
text = 0x00001000-0x0012f260
rodata = 0x0012f260-0x001883f4
bss = 0x00188400-0x00197124
data = 0x00197124-0x001aa000
stack = 0x001a8000-0x001aa000
init = 0x001aa000-0x002a4000
available = 0x002a4000-0x01eff000
DMA Zone = 0x01f00000-0x02000000
Hardware Trace Active and Enabled
Boot Mode: 6
Blackfin support (C) 2004-2008 Analog Devices, Inc.
Compiled for ADSP-BF532 Rev 0.5
Blackfin Linux support by http://blackfin.uclinux.org/
Processor Speed: 400 MHz core clock and 133 MHz System Clock
Built 1 zonelists in Zone order, mobility grouping off. Total pages: 7873
Kernel command line: console=ttyBF0,115200
Configuring Blackfin Priority Driven Interrupts
PID hash table entries: 128 (order: 7, 512 bytes)
console [ttyBF0] enabled
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory available: 28736k/32768k RAM, (1000k init code, 1208k kernel code, 492k data, 1024k dma, 304k reserved)
Calibrating delay loop... 782.33 BogoMIPS (lpj=1564672)
Security Framework initialized
Mount-cache hash table entries: 512
Blackfin Scratchpad data SRAM: 4 KB
Blackfin L1 Data A SRAM: 16 KB (15 KB free)
Blackfin L1 Data B SRAM: 16 KB (16 KB free)
Blackfin L1 Instruction SRAM: 48 KB (41 KB free)
PDA for CPU0 reserved at 001890e8
net_namespace: 424 bytes
NET: Registered protocol family 16
Blackfin GPIO Controller
Blackfin DMA Controller
ip0x_init(): registering device resources
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
NET: Registered protocol family 1
msgmni has been set to 56
io scheduler noop registered
io scheduler anticipatory registered (default)
io scheduler cfq registered
Serial: Blackfin serial driver
bfin-uart.1: ttyBF0 at MMIO 0xffc00400 (irq = 21) is a BFIN-UART
brd: module loaded
dm9000 Ethernet Driver, V1.31
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: wrong id: 0x4a4a4a4a
dm9000 dm9000.0: not found (-19).
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: wrong id: 0x4a4a4a4a
dm9000 dm9000.1: not found (-19).
Driver 'sd' needs updating - please use bus_type methods
usbmon: debugfs is not available
driver isp1362-hcd, 2005-04-04
isp1362-hcd isp1362-hcd.0: ISP1362 Host Controller
isp1362-hcd isp1362-hcd.0: new USB bus registered, assigned bus number 1
isp1362_hc_reset:
Software reset timeout
Clock not ready after 100ms
isp1362-hcd isp1362-hcd.0: can't setup
isp1362-hcd isp1362-hcd.0: USB bus 1 deregistered
init error, -19
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP cubic registered
NET: Registered protocol family 17
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
Freeing unused kernel memory: 1000k freed
dma_alloc_init: dma_page @ 0x01d5a000 - 256 pages at 0x01f00000
Hello world from initramfs!


BusyBox v1.13.2 (2009-02-04 13:18:58 CST) built-in shell (msh)
Enter 'help' for a list of built-in commands.

# free
total used free shared buffers
Mem: 29736 5364 24372 0 0
Swap: 0 0 0
Total: 29736 5364 24372
#

miniBfin启动Linux操作系统成功

在调试中碰到一些问题,主要是:
1.对image的loading address和entry address理解上有错误,导致uboot解压vmImage失败,以后再分析。
2.由于miniBfin没有flash部分,在setup_arch会导致kernel panic,但是这时由于console driver还没有加载,无法输出printk消息,弄得我只好写一小段UART的打印函数进行调试,还好最终找到原因。

下面是系统启动的信息。还有很多工作没有完成,慢慢来。

U-Boot 1.1.5 (Feb 2 2009 - 22:25:59)

CPU: ADSP BF532 Rev.: 0.5
Board: miniBfin
http://bfincn.blogspot.com
Clock: VCO: 400 MHz, Core: 400 MHz, System: 133 MHz
SDRAM: 32 MB
*** Warning - bad CRC, using default environment

In: serial
Out: serial
Err: serial
No ethernet found.
starting from spi flash
Hit any key to stop autoboot: 0

EEPROM @0x0 read: addr 0x01000000 off 0x30000 count 0x110000

done
## Booting image at 01000000 ...
Image Name: Linux-2.6.28-ADI-2009R1-pre-svn6
Image Type: Blackfin Linux Kernel Image (gzip compressed)
Data Size: 1006736 Bytes = 983.1 kB
Load Address: 00001000
Entry Point: 001b0fa4
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
Starting Kernel at = 1b0fa4
Linux version 2.6.28-ADI-2009R1-pre-svn6028 (root@debian) (gcc version 4.1.2 (ADI svn)) #31 Wed Feb 4 10:35:45 CST 2009
Board Memory: 32MB
Kernel Managed Memory: 32MB
Memory map:
fixedcode = 0x00000400-0x00000490
text = 0x00001000-0x0012f260
rodata = 0x0012f260-0x001883f4
bss = 0x00188400-0x00197124
data = 0x00197124-0x001aa000
stack = 0x001a8000-0x001aa000
init = 0x001aa000-0x001c3000
available = 0x001c3000-0x01eff000
DMA Zone = 0x01f00000-0x02000000
Hardware Trace Active and Enabled
Boot Mode: 6
Blackfin support (C) 2004-2008 Analog Devices, Inc.
Compiled for ADSP-BF532 Rev 0.5
Blackfin Linux support by http://blackfin.uclinux.org/
Processor Speed: 400 MHz core clock and 133 MHz System Clock
Built 1 zonelists in Zone order, mobility grouping off. Total pages: 7873
Kernel command line: console=ttyBF0,115200
Configuring Blackfin Priority Driven Interrupts
PID hash table entries: 128 (order: 7, 512 bytes)
console [ttyBF0] enabled
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory available: 29636k/32768k RAM, (100k init code, 1208k kernel code, 492k data, 1024k dma, 304k reserved)
Calibrating delay loop... 782.33 BogoMIPS (lpj=1564672)
Security Framework initialized
Mount-cache hash table entries: 512
Blackfin Scratchpad data SRAM: 4 KB
Blackfin L1 Data A SRAM: 16 KB (15 KB free)
Blackfin L1 Data B SRAM: 16 KB (16 KB free)
Blackfin L1 Instruction SRAM: 48 KB (41 KB free)
PDA for CPU0 reserved at 001890e8
net_namespace: 424 bytes
NET: Registered protocol family 16
Blackfin GPIO Controller
Blackfin DMA Controller
ip0x_init(): registering device resources
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
NET: Registered protocol family 1
msgmni has been set to 57
io scheduler noop registered
io scheduler anticipatory registered (default)
io scheduler cfq registered
Serial: Blackfin serial driver
bfin-uart.1: ttyBF0 at MMIO 0xffc00400 (irq = 21) is a BFIN-UART
brd: module loaded
dm9000 Ethernet Driver, V1.31
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: read wrong id 0x4a4a4a4a
dm9000 dm9000.0: wrong id: 0x4a4a4a4a
dm9000 dm9000.0: not found (-19).
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: read wrong id 0x4a4a4a4a
dm9000 dm9000.1: wrong id: 0x4a4a4a4a
dm9000 dm9000.1: not found (-19).
Driver 'sd' needs updating - please use bus_type methods
usbmon: debugfs is not available
driver isp1362-hcd, 2005-04-04
isp1362-hcd isp1362-hcd.0: ISP1362 Host Controller
isp1362-hcd isp1362-hcd.0: new USB bus registered, assigned bus number 1
isp1362_hc_reset:
Software reset timeout
Clock not ready after 100ms
isp1362-hcd isp1362-hcd.0: can't setup
isp1362-hcd isp1362-hcd.0: USB bus 1 deregistered
init error, -19
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
ip_tables: (C) 2000-2006 Netfilter Core Team
TCP cubic registered
NET: Registered protocol family 17
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
VFS: Cannot open root device "<NULL>" or unknown-block(0,0)
Please append a correct "root=" boot option; here are the available partitions:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

2009年1月23日

精通initramfs构建step by step

精通initramfs构建step by step


(一)hello world
一、initramfs是什么
在2.6版本的linux内核中,都包含一个压缩过的cpio格式 的打包文件。当内核启动时,会从这个打包文件中导出文件到内核的rootfs文件系统,然后内核检查rootfs中是否包含有init文件,如果有则执行 它,作为PID为1的第一个进程。这个init进程负责启动系统后续的工作,包括定位、挂载"真正的"根文件系统设备(如果有的话)。如果内核没有在 rootfs中找到init文件,则内核会按以前版本的方式定位、挂载根分区,然后执行/sbin/init程序完成系统的后续初始化工作。
这 个压缩过的cpio格式的打包文件就是initramfs。编译2.6版本的linux内核时,编译系统总会创建initramfs,然后把它与编译好的 内核连接在一起。内核源代码树中的usr目录就是专门用于构建内核中的initramfs的,其中的initramfs_data.cpio.gz文件就 是initramfs。缺省情况下,initramfs是空的,X86架构下的文件大小是134个字节。

二、构建第一个initramfs:hello world
从C语言开始,学习计算机编程语言的第一个程序几乎都是hello world,因此我们也构建一个最简单的hello world式的initramfs,以说明initramfs的基本构建方法。
initramfs的灵魂是init文件(或者叫程序,因为它会被内核第一个执行),我们先写一个简单的init程序,它会在内核的console中打印出经典的hello world信息。
hello.c:
#include <stdio.h>
#include <unistd.h>
int main(int argc,char argv[])
{
printf("hello world, from initramfs.\n");
sleep(9999999);
return 0;
}

其中的sleep()函数语句是为了避免执行时内核很快打出panic的信息,并非功能上的需要。

接着把hello.c编译成静态连接程序:
gcc -o hello_static -static -s hello.c
命令行中的-s参数表示编译后的程序不包含调试定位信息,目的是减少编译出来的程序文件的大小。
再创建一个initramfs的构建源文件目录image,把hello_static程序拷入这个目录,并改名为init。
在image目录下,创建一个dev/console的设备文件,否init程序无法在内核console中输出信息:
mknod -m 600 dev/console c 5 1
注意,执行这个命令需要有root权限。

好了,现在可以设置内核配置参数,进行initramfs的构建了:
在general setup配置目录下的initramfs sources配置项下输入image的路径名,比如我的路径就是/home/wyk/initramfs-test/image。因为我们的init程 序是ELF格式的,所以内核需要支持ELF的可执行文件,否则启动这个init程序会失败。在内核的 Executable file formats配置目录下,选择 kernel support for ELF binaries,则可使内核支持ELF格式的可执行文件。其他内核配置参数根据实际需要设置即可,不过,为了减少内核编译时间,可参考这篇文章http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-293122.html设置一个最简单的内核配置。
内核配置参数设置完成后,按常规的内核编译方法进行编译,initramfs就自动连接到编译好的内核映像文件中了。

三、试验环境搭建
试验initramfs需要经常重启系统,所以使用CPU模拟器是不错的选择。我们可以选用qemu,它支持直接启动linux内核,无需在模拟器中安装OS。从方便使用的角度考虑,我们采用qemu launcher设置qemu的各项参数,它的安装可参考http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-612280.html
在qemu launcher的linux配置标签中,打勾直接启动linux,然后在下面的文本框中填上刚才编译好的内核映像文件的路径名。因为qemu的运行还需要设置硬盘映像文件,所以还需要在左边的配置标签中新建一个硬盘映像文件,但实际上我们并不使用硬盘。
配置好qemu的参数后,点击launcher按钮,内核就开始在qemu中运行了。内核输出一堆内核运行信息后,最后打出了
hello world, from initramfs.
哈哈,我们构建的initramfs已经能够正常工作了!
(二)initramfs的前世今生
四、什么是rootfs和ramfs
所有的2.6版本linux内核都有一个特殊的文件系统 rootfs,是内核启动的初始始根文件系统,initramfs的文件会复制到rootfs。如果把initramfs比作种子,那么rootfs就是 它生长的土壤。大部分linux系统正常运行后都会安装另外的文件系统,然后忽略rootfs。
rootfs是ramfs文件系统的一个特殊实例。ramfs是一种非常简单的文件系统,是基于内存的文件系统。ramfs文件系统没有容量大小的限制,它可以根据需要动态增加容量。
ramfs 直接利用了内核的磁盘高速缓存机制。所有的文件的读写数据都会在内存中做高速缓存(cache),当系统再次使用文件数据时,可以直接从内存中读写,以提 供系统的I/O性能。高速缓存中的写入数据会在适当的时候回写到对应的文件系统设备(如磁盘等)中,这时它的状态就标识为clean,这样系统在必要时可 以释放掉这些内存。ramfs没有对应文件系统设备,所以它的数据永远都不会回写回去,也就不会标识为clean,因此系统也永远不会释放ramfs所占 用的内存。
因为ramfs直接使用了内核已有的磁盘高速缓存机制,所以它的实现代码非常小。也由于这个原因,ramfs特性不能通过内核配置参数删除,它是内核的天然特性。

五、ramfs不是ramdisk
ramdisk 是在一块内存区域中创建的块设备,用于存放文件系统。ramdisk的容量是固定的,不能象ramfs一样动态增长。ramdisk需要内核的文件系统驱 动程序(如ext2)来操作其上的数据,而ramfs则是内核的天然特性,无需额外的驱动程序。ramdisk也象其他文件系统设备一样,需要在块设备和 内存中的磁盘高速缓存之间复制数据,而这种数据复制实际不必要的。

六、从ramfs派生的文件系统tmpfs
ramfs 的一个缺点是它可能不停的动态增长直到耗尽系统的全部内存,所以只有root或授权用户允许使用ramfs。为了解决这个问题,从ramfs派生出了 tmpfs文件系统,增加了容量大小的限制,而且允许把数据写入交换分区。由于增加了这两个特性,所以tmpfs允许普通用户使用。
关于tmpfs文件系统更多的信息,可以看内核源码中的 Documentation/filesystems/tmpfs.txt 文档。

综上所述,initramfs是一种ramfs文件系统,在内核启动完成后把它复制到rootfs中,作为内核初始的根文件系统,它的任务是挂载系统真正的根文件系统。这就是initramfs的前世今生。
(三):busybox
七、什么是busybox
busybox号称是嵌入式Linux中的瑞士军刀――小巧、功能齐 全。它把许多常用的Linux命令都集成到一个单一的可执行程序中,只用这一个可执行程序(即busybox)加上Linux内核就可以构建一个基本的 Linux系统。busybox程序非常小巧,包含全部命令可执行文件大小也只有750多K。busybox是完全模块化的,可以很容易地在编译时增加、 删除其中包含的命令。
由于busybox的这些特点,它广泛应用于LiveCD、应急修复盘、安装盘等系统中。我们也是以它为基础,构建initramfs。

八、busybox的配置、编译和安装
(1)去http://busybox.net 去下载最新的源码,解压展开。
(2)用
make menuconfig
命令启动配置界面配置,配置busybox的特性、选择要包含在busybox的命令(busybox称为applet);
也可以用
make defconfig
命令做缺省配置,包含全部的applet。
另外两个配置命令是
make allyesconfig――最大配置
make allnoconfig――最小配置
它们和make defconfig命令都可以用来作为自定义配置的初始配置,然后再用make menuconfing命令做定制化配置。
为了简单,我们用make defconfig做缺省配置。
(3)用
make
命令编译busybox软件。
(4)用
make CONFIG_PREFIX=<安装目录> install
命令安装。如果在命令行中省略CONFIG_PREFIX变量的赋值,则会安装缺省值 ./_install 目录下。CONFIG_PREFIX可以在make menuconfig的配置界面中修改。
我们用make CONFIG_PREFIX=~/initramfs-test/image 命令把busybox安装到initramfs的构建目录中。
(5)缺省配置下,busybox动态链接到glibc,所以要把它用到的动态库复制到initramfs的构建目录中。用ldd命令查看busybox用到了哪些动态库文件及相应的文件路径,然后把它们复制到相应的目录下即可。
我们编译的busybox需要向image/lib目录下复制
ld-linux.so.2
libc.so.6
libcrypt.so.1
libm.so.6
动态库文件。

九、在image下创建必要的目录和设备文件
(1)在imgae目录下创建
proc , sys , etc ,mnt
四个目录
(2)hello world 已经创建了console 设备文件,我们再用
mknod -m 600 dev/null c 1 3
命令创建另一个基本的设备文件。

十、试验一下
busybox的构建和准备工作做完了,我们试验一下吧:
在image目录下以root用户权限――
(1)用
mount -vt proc proc =proc
mount -vt sysfs sysfs =sys

命令安装内核虚拟文件系统
(2)用
mount -v -o bind /dev dev
命令绑定/dev的设备文件到image/dev
(3)用
chroot . /bin/sh
命令进入busybox的环境。出现shell的命令提示符,可以试着输入几个命令,看看执行结果。例如,输入 fdisk -l 命令看看是否能显示硬盘的分区。
(四):mini linux
十一、自动生成/dev下的设备文件
上节用chroot方法试验busybox时,为了简单,是用"绑定"的方式把主机的/dev中的设备文件映射到image目录下的dev目录。在initramfs上,这种方法显然不能使用。
生成系统的设备文件,现在通常都是用udev动态生成,而initramfs为了做到通用,动态生成的要求是必须的。在busybox中有一个mdev命令,就是用来动态生成设备文件,填充到/dev目录的。
在系统启动时,用
mdev -s
命令可以根据内核的sysfs文件系统在/dev目录中自动生成相应的设备文件。命令执行前,需要先挂载内核的proc和sysfs虚拟文件系统。

十二、初始身手
解决了自动生成设备文件的问题后,我们可以试着做一个最简单的可运行的linux系统了:
(1)在image目录下写一个最简单的init脚本。
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
/bin/sh
(2)为init脚本设置可执行权限,否则内核不会去执行它。
chmod +x init
(3)有些busybox配置中,mdev命令需要读取/etc/mdev.conf文件,为了避免出错信息,我们创建一个空文件。
touch etc/mdev.conf
(4)在内核源码目录下,执行
make
命令,重新编译内核,生成新的initramfs。

好了,在QEMU模拟环境下启动这个新的内核,系统初始化后,会进入SHELL环境。在这个SHELL环境下,试验一些常用命令,看看是否可以正常运行。

十三、can't access tty
上一步创建的简单linux系统在进入SHELL环境时,会打出下面这一句出错信息:
/bin/sh: can't access tty; job controll off
虽然不影响使用,但终究不够完美。
产 生这个错误的原因是我们的SHELL是直接运行在内核的console上的,而console是不能提供控制终端(terminal)功能的,所以必须把 SHELL运行在tty设备上,才能消除这个错误。解决问题的办法是使用正规init机制,在执行SHELL前打开tty设备。
另外,这个简单系统的reboot、halt等命令是不起作用的,也必须通过init方式解决。

十四、busybox的缺省init模式
busybox支持init功能,当系统没有/etc/inittab文件时,它有一套缺省的模式,按下面配置执行:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

如果busybox检测到/dev/console不是串口控制台,init还要执行下面的动作:
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh

我们试试这种模式是否可以解决我们的问题。
(1)写/etc/init.d/rcS脚本
这个脚本实际是要执行系统的初始化操作。我们把前面的init脚本改造一下,将最后的/bin/sh命令删除,然后移到 etc/init.d目录下,改名为rcS。
(2)initramfs不需要linuxrc,而且如果没有init文件,内核就不认为它是一个有效的initramfs,因而不安装它,导致内核panic。于是,我们在image目录下,把busybox安装的linuxrc改名为init
mv linuxrc init
(3)重新编译内核,生成新的initramfs
(4)用QEMU试验一下新编译的内核。系统启动后,会打出一句话"please press Enter to active this console"――感觉还不错。但是按下回车键后,系统依然会打出错误信息"-/bin/sh:
can't access tty; job controll off "。用tty命令看看当前的终端设备文件名:
# tty
/dev/console
它还是console,不是tty设备,所以问题没有解决。不过,reboot和halt命令倒是可以正常工作了。

经过验证,busybox的缺省init模式无法满足我们的要求,我们还是要写inittab,定制自己的init初始化流程。

十五、busybox的inittab文件格式说明
要写自己的inittab,需要理解busybox的inittab文件格式。
busybox的inittab文件与通常的inittab不同,它没有runlevel的概念,语句功能上也有限制。inittab语句的标准格式是
<id>:<runlevels>:<action>:<process>
各字段的含义如下
<id>:
id字段与通常的inittab中的含义不同,它代表的是这个语句中process执行所在的tty设备,内容就是/dev目录中tty设备的文件名。由于是运行process的tty设备的文件名,所以也不能象通常的inittab那样要求每条语句id的值唯一。
<runlevels>:
busybox不支持runlevel,所以此字段完全被忽略。
<action>:
为下列这些值之一:
sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown
其 含义与通常的inittab的定义相同。特别提一下askfirst,它的含义与respawn相同,只是在运行process前,会打出一句话 "please press Enter to active this console",然后等用户在终端上敲入回车键后才运行process。
<process>
指定要运行的process的命令行。

十六、写mini linux的inittab
理解了busybox的inittab格式,我们就可以写mini linux的inittab:
::sysinit:/etc/init.d/rcS
tty1::askfirst:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
tty5::askfirst:/bin/sh
tty6::askfirst:/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r

把这个文件放到image的etc目录下。为了执行reboot命令时避免提示找不到/etc/fstab文件,我们再在etc目录下创建一个空文件
touch fstab

做好了这些,就可以重新编译内核,生成新的initramfs了。在QEMU试验环境下验证新生成的mini linux,系统运行正常,而且象通常的linux系统一样,用ALT+F1~F6键可以在6个终端间切换。
(五)initrd
十七、配置内核支持initrd
到目前为止,我们的initramfs都由内核编译系统生成的,并链接到内核中。其实我们也可以用cpio命令生成单独的initramfs,与内核编译脱钩,在内核运行时以initrd的形式加载到内核,以增加灵活性。
首 先配置内核使用单独的initrd:在 Device Driver / Block device / 配置目录下,选择 RAM filesystem and RAMdisk ( initramfs/initrd ) support 配置项;再到 General Setup 配置目录项下,将 initramfs source file(s) 配置项原有的内容清空。然后把内核源码树的usr目录下已由内核编译生成的initramfs文件initramfs_data.cpio.gz拷贝到 ~/initramfs-test 目录下,我们先直接用这个文件试验一下 initrd 方式的initramfs的效果。最后,执行make命令重新编译内核后,在QEMU试验环境中,把initrd配置框(linux配置框的下面)的内容 写为 ~/initramfs-test/initramfs_data.cpio.gz,指定initrd的文件路径。
好了,试验一下新的initrd方式的initramfs吧,效果跟先前的完全一样。

十八、用cpio命令生成initramfs
cpio 命令有三种操作模式:copy-out、copy-in、copy-pass,生成initramfs用的是它的copy-out模式,即把文件打包的操 作模式。cpio的copy-out操作模式使用 -o 命令行选项指定。缺省情况下,cpio从标准输入读取输入数据,向标准输出写入输出数据。使用 -I 选项可以指定文件名代替标准输入,使用 -O 选项可以指定文件名代替标准输出,而 -F 选项指定的文件名则根据cpio操作模式的不同可代替标准输入或标准输出。
把~/initramfs-test/image目录下的文件打包成initramfs,执行下面的命令:
find . | cpio -o -H newc | gzip > ../image.cpio.gz
命令执行完毕后,在~/initramfs-test目录下就会生成文件名为imgae.cpio.gz的initramfs。
上 面cpio命令的 -H 选项指定打包文件的具体格式,要生成initramfs,只能用newc 格式,如果使用其他格式,内核会打出这样的出错信息:Unpacking initramfs...<0> kernel panic - not syncing: no cpio magic

在QEMU试验环境下试验一下新的initrd方式的initramfs,效果跟先前的完全一样。

十九、cpio命令的其他用法
如 果我们要解开一个cpio格式的打包文件,则要使用cpio命令的copy-in操作模式。cpio的copy-out操作模式使用 -i 命令行选项指定。例如,我们想把前一步从内核源码树 usr目录下拷贝的initramfs_data.cpio.gz 展开到~/initramfs-test/initramfs_data目录下,则使用下列命令:
mkdir ~/initramfs-test/initramfs_data
cd ~/initramfs-test/initramfs_data
cpio -i -F ../initramfs_data.cpio.gz --no-absolute-filename
命令执行完毕后,initramfs_data目录下出现多个目录和文件,用diff命令比较initramfs_data与image目录,两者的完全一样。
上面cpio命令的 --no-absolute-filename 选项的作用是展开文件时,去掉文件路径最前面的"/",把绝对路径名变为相对路径名。内核编译时生成的initramfs使用了绝对路径名,所以这个选项 必须使用,否则initramfs内文件展开到"/"目录去了,如果你是root用户或有"/"目录的写权限,那么展开的文件就有可能覆盖同名的文件(在 文件修改时间新于原有文件),那就糟糕了!
展开文件前,你可能会想先看看打包文件里都有哪些文件,这时就要用 -t 选项了。例如,我们想看看内核编译时生成的initramfs_data.cpio.gz中都有哪些文件,我们就可以用下面的命令:
zcat initramfs_data.cpio.gz | cpio -t
在标准输出中打出文件名列表。
使用 -v 选项可以在cpio命令执行时输出详细信息:在打包或展开文件时,输出已处理的文件名;与 -t 选项连用时,则显示文件的详细信息,类似 ls -l 的输出内容。-V 选项则用打点的方式,显示cpio命令的执行进度信息,一个点代表处理一个文件。

(六)switch_root
二十、switch_root 命令
除了基于initramfs的系统(如第四节的mini linux),通常initramfs都是为安装最终的根文件系统做准备工作,它的最后一步需要安装最终的根文件系统,然后切换到新根文件系统上去。以往 的基于ramdisk 的initrd 使用pivot_root命令切换到新的根文件系统,然后卸载ramdisk。但是initramfs是rootfs,而rootfs既不能 pivot_root,也不能umount。为了从initramfs中切换到新根文件系统,需要作如下处理:
(1)删除rootfs的全部内容,释放空间
find -xdev / -exec rm '{}' ';'
(2)安装新的根文件系统,并切换
cd /newmount; mount --move . /; chroot .
(3)把stdin/stdout/stderr 附加到新的/dev/console,然后执行新文件系统的init程序

上述步骤比较麻烦,而且要解决一个重要的问题:第一步删除rootfs的所有内容也删除了所有的命令,那么后续如何再使用这些命令完成其他步骤?busybox的解决方案是,提供了switch_root命令,完成全部的处理过程,使用起来非常方便。
switch_root命令的格式是:
switch_root [-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT]
其中NEW_ROOT是实际的根文件系统的挂载目录,执行switch_root命令前需要挂载到系统中;NEW_INIT是实际根文件系统的init程序的路径,一般是/sbin/init;-c /dev/console是可选参数,用于重定向实际的根文件系统的设备文件,一般情况我们不会使用;而ARGUMENTS_TO_INIT则是传递给实际的根文件系统的init程序的参数,也是可选的。

需要特别注意的是:switch_root命令必须由PID=1的进程调用,也就是必须由initramfs的init程序直接调用,不能由init派生的其他进程调用,否则会出错,提示:
switch_root: not rootfs
也是同样的原因,init脚本调用switch_root命令必须用exec命令调用,否则也会出错,提示:
switch_root: not rootfs

二十一、实践:用initramfs安装CLFS根文件系统
现在实践一下switch_root命令,用它切换一个CLFS的根文件系统硬盘分区。我的CLFS安装在/dev/sda8硬盘分区,我们就以此为例说明。
我们还是在以前的image目录中构建
(1)改写init脚本
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
mount /dev/sda8 /mnt (注意:为了简单,我们直接把CLFS分区写死在init脚本中了)
exec switch_root /mnt /sbin/init
(2)生成新的initrd
按上一节"精通initramfs构建step by step (五):initrd"描述的cpio命令生成新的initrd。
(3)把新的initrd拷贝到CLFS分区的/boot目录下,改名为clfs-initrd
(4)在GRUB的menu.lst配置文件中增加一个启动项
#test for initramfs of CLFS
title test for initramfs of CLFS (on /dev/sda8)
root (hd0,7)
kernel /boot/clfskernel-2.6.17.13 (注意:并没有向内核传递root参数信息)
initrd /boot/clfs-initrd


全部做完后,重启机器,选择 test for initramfs of CLFS 启动项,机器顺利进入了CLFS系统,我们构建的initramfs用switch_root命令完成了CLFS实际根文件系统的安装和切换。
(七)modules
二十二、内核模块支持
到目前为止,我们在构建initramfs时还没有涉及内核模块的支持,所用到的硬件驱动程序都是直接编译到内核中。现在我们就看看如何使initramfs支持内核模块。
首 先,内核配置要支持模块,并支持内核模块的自动加载功能:在内核配置菜单中的激活下面的配置项,编译进内核 Load module support / Enable loadable module support / Automatic kernel loading ;
然后把需要的硬件驱动程序配置模块形式,比如把我的机器上的硬盘控制器的驱动编译成模块,则选择
Device Driver
|---->SCSI device support
|---->SCSI disk support
|----->verbose SCSI error reporting (不是必须的,但可方便问题定位)
|----->SCSI low-level drivers
|---->Serial ATA (SATA) support
|---->intel PIIX/ICH SATA support
把它们配置成模块。
最后,编译内核,并把编译好的内核模块安装到image的目录下:
make
make INSTALL_MOD_PATH=~/initramfs-test/image modules_install
命 令执行完毕后,在image/lib/modules/2.6.17.13/kernel/drivers/scsi目录下安装了4个内核模文 件:scsi_mod.ko、sd_mod.ko、ata_piix.ko、libata.ko,它们就是所需的硬盘控制器的驱动程序。

好了,都准备好了,可以用cpio命令生成inintramfs了。不过,为了方便后面的试验,我们再把init脚本改成
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
exec /bin/sh
使系统启动后进入shell环境,并且用exec调用的方式,使shell的pid为1,能够执行switch_root命令。

二十三、试验:用initramfs中的内核模块安装硬盘文件系统
用 新生成的initramfs启动系统,内核并没有自动加载硬盘控制器的驱动程序,所以 /dev目录下也没有sda等硬盘设备文件。好吧,我们自己加载内核模块文件。不幸的是,busybox的modprobe命令执行不正常,不能加载内核 模块。怀疑是busybox的modprobe命令配置或编译有问题,后续再花时间定位吧,先用insmod命令依次加载。查看/lib/modules /2.6.17.13/modules.dep,弄清楚了4个模块的依赖关系,执行下面的命令加载:
insmod scsi_mod
insmod libata
insmod ata_piix
insmod sd_mod
然后再用
mdev -s
命令生成硬盘的设备文件。
好了,可以安装CLFS的硬盘分区,并把根文件系统切换到CLFS的硬盘分区:
mount /dev/sda8 /mnt
exec switch_root /mnt /sbin/init
系统正常启动到了CLFS,我们可以做到用initramfs中的硬盘控制器的驱动模块安装硬盘分区了。

二十四、mdev的hotplug模式
上面的试验中,我们在加载完驱动模块后调用了mdev -s 命令来生成硬盘的设备文件。其实,可以使用mdev的hotplug模式在加载内核时自动生成对应的设备文件:
在执行insmod命令前,用
echo /sbin/mdev > /proc/sys/kernel/hotplug
命令设置系统的hotplug程序为mdev。
后续使用insmod命令加载模块时,系统自动调用mdev生成相应的设备文件。
注意:内核必须配置支持hotplug功能,而前面提到的CLFS最简内核配置方案是没有配置hotplug支持的。

(八)coldplug
二十五、udev的coldplug模式
内核在启动时已经检测到了系统的硬件设备,并把硬件设备 信息通过sysfs内核虚拟文件系统导出。udev扫描sysfs文件系统,根据硬件设备信息生成热插拔(hotplug)事件,udev再读取这些事 件,生成对应的硬件设备文件。由于没有实际的硬件插拔动作,所以这一过程被称为coldplug。我们的initramfs就是利用这一机制,加载硬件设 备的驱动程序模块。

udev完成coldplug操作,需要下面三个程序:
udevd――作为deamon,记录hotplug事件,然后排队后再发送给udev,避免事件冲突(race conditions)。
udevtrigger――扫描sysfs文件系统,生成相应的硬件设备hotplug事件。
udevsettle――查看udev事件队列,等队列内事件全部处理完毕才退出。

在initramfs的init脚本中可以执行下面的语句实现coldplug功能:
mkdir -p /dev/.udev/db
udevd --daemon
mkdir -p /dev/.udev/queue
udevtrigger
udevsettle
许多文档提到的在udevd --daemon 命令前要执行
echo > /proc/sys/kernel/hotplug
命令,经验证,在我们的initramfs环境下的coldplug功能中并不需要。
二十六、试验:用udev自动加载设备驱动模块
了解了udev的coldplug的机理,我们就试验一下用udev自动加载设备驱动模块,并生成硬件设备文件。
(1)从 /sbin 目录下拷贝udevd、udevtrigger、udevsettle程序到image目录下的sbin目录下,并用ldd命令找到它们所需要的动态库文件,拷贝到image目录下的lib目录下。

(2)修改init脚本,增加coldplug功能:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
#using udev autoload hard disk driver module
mkdir -p /dev/.udev/db
udevd --daemon
mkdir -p /dev/.udev/queue
udevtrigger
udevsettle
mount /dev/sda8 /mnt
killall udevd
exec switch_root /mnt /sbin/init

注意:在切换到真正根文件系统前,要把udevd进程杀掉,否则会和真正根文件系统中的udev脚本的执行相冲突。这就是上面killall udevd 语句的作用。

(3)编写udev规则文件
规 则文件是udev的灵魂,没有规则文件,udev无法自动加载硬件设备的驱动模块。为了简单,我们直接使用CLFS中的40- modprobe.rules,把它拷贝到image目录下的etc/udev/rules.d目录。有关udev的规则文件编写,已超出了本文的范围, 后续我有可能专文描述。
########################################################################
#
# Description : 40-modprobe.rules
#
# Authors : Based on Open Suse Udev Rules
#
kay.sievers@suse.de
#
# Adapted to : Jim Gifford
# LFS : Alexander E. Patrakov
#
# Version : 00.01
#
# Notes :
#
########################################################################

# hotplug
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"


# scsi
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="0|7|14", RUN+="/sbin/modprobe sd_mod"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="1", SYSFS{device/vendor}=="On[sS]tream", RUN+="/sbin/modprobe osst"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="1", RUN+="/sbin/modprobe st"
SUBSYSTEM=="scsi_device", ACTION=="add", SYSFS{device/type}=="[45]", RUN+="/sbin/modprobe sr_mod"
SUBSYSTEM=="scsi_device", ACTION=="add", RUN+="/sbin/modprobe sg"


# floppy
KERNEL=="nvram", ACTION=="add", RUN+="load_floppy_module.sh"

注意:上面的
ENV{MODALIAS}=="?*", RUN+="/sbin/modprobe $env{MODALIAS}"
语 句是实现自动加载硬件设备驱动模块功能的关键,它根据sysfs文件系统中记录的模块aliases数据,用modprobe命令加载对应的内核模块。有 关模块aliases的进一步说明,可参考CLFS手册(CLFS-1.0.0-x86)中的11.5.2.4. Module Loading一节的描述。

(4)拷贝modprobe命令
前一节提到过,busybox的modprobe 命令不能正常使用,所以我们需要拷贝 /sbin 目录下的modprobe命令到image目录下的sbin目录,供udev加载内核模块使用。再用ldd命令检查一下 /sbin/modprobe 命令所需的动态库文件,如果有则拷贝到image/lib目录下。(我的检查结果是,除了libc6外,不需要其他动态库,所以不需要拷贝)

好了,重新生成initramfs,启动CLFS系统,initramfs能够自动加载硬盘设备的驱动模块,系统顺利地从initramfs切换到了真正的CLFS的根文件系统。
(九)内核编译时构建initramfs补遗
二十七、直接把cpio打包文件编译进内核
如果我们有一个已经做好的cpio格式的initramfs,可以在内核编译时直接编译进内核。回忆一下第一节的内容,我们在内核配置参数中的initramfs sources配置项下输入构建initramfs的目录路径。其实我们也可以直接输出现成的initramfs的文件名,这样在内核编译时,就可以把它编译进内核了。
使用这种方法,有两点需要注意:
(1)cpio文件不能压缩。一般作为initrd的cpio文件都经过了压缩,所以编译前需要先把压缩过的文件解压。
(2)cpio文件的后缀名必须是 .cpio。内核编译通过 .cpio的后缀名来识别此文件是cpio打包文件,而其他文件后缀名则会被认为是initramfs构建的描述文件(关于描述文件,下面后详细说明)。

二十八、用描述文件构建initramfs
用内核编译工具构建initramfs的第三种方法是使用描述文件。在内核配置参数中的initramfs sources配置项下可以输入initramfs构建描述文件的文件名,内核编译工具根据描述文件完成initramfs的构建。
描述文件的语法格式的说明如下:
# a comment
file <name> <location> <mode> <uid> <gid>
dir <name> <mode> <uid> <gid>
nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>
slink <name> <target> <mode> <uid> <gid>
pipe <name> <mode> <uid> <gid>
sock <name> <mode> <uid> <gid>


<name> name of the file/dir/nod/etc in the archive
<location> location of the file in the current filesystem
<target> link target
<mode> mode/permissions of the file
<uid> user id (0=root)
<gid> group id (0=root)
<dev_type> device type (b=block, c=character)
<maj> major number of nod
<min> minor number of nod


例子:
我们用描述文件的方式,构建第一节中的hello world的initramfs。
hello-init.desp:
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
file /init /home/wyk/initramfs-test/hello_static 0755 0 0

在内核配置项initramfs sources中指定描述文件hello-init.desp,编译内核时就会生成hello world的initramfs,运行效果与第一节用指定构建目录的方法构建的initramfs的完全相同。

注意:在 内核帮助文件中,提到initramfs sources配置项可以指定多个目录或描述文件,内核会汇集这些目录或文件生成一个initramfs。但从我的试验来看,initramfs sources只接受单一的目录名或文件名,输出多个目录名或文件名(之间用空格分隔),内核编译时就会出错。也许是我的方法有误,还望读者指正。
(十)uclibc
二十九、toolchain
在initramfs中使用uclibc库,关键是构建uclibc的工具链toolchain。构建uclibc 的 toolchain 有两种主要方式:(1)用buildroot工具( http://buildroot.uclibc.org/)自动构建,这也是uclibc的官方标准做法。(2)用CLFS Embedded手册的方法手工创建。目前CLFS Embedded还在开发中,可在http://cross-lfs.org/view/clfs-embedded/x86/中查阅。

我们简单地说明用buildroot工具构建uclbic的toolchain的步骤:
(1)获取buildroot。
推荐用svn命令从它的版本库中下载:
svn co svn://uclibc.org/trunk/buildroot
要求使用svn命令,需要先安装subversion软件包。下载过程中,可能会出现连接异常中断的情况,这时重新执行上述命令,继续进行下载,有可能要重复多次。
(2)配置buildroot
因为我们只是创建toolchain,所以需要做相应的配置。在buildroot的顶层目录下,执行
make menuconfig
命令,在缺省配置的基础上做如下配置
Target Architecture: i386
Target Architecture Variant: i686
Package Selection for the target: 取消BusyBox的选项(缺省是选中的)
Target filesystem options: 取消 ext2 root filesystem(缺省是选中的)
Toolchain --> Toolchain type: Buildroot toolchain
(3)编译
执行
make
命令,buildroot工具会自动下载所需要的源文件并自动编译,等一两个小时后,toolchain就编译好了。编译好的toolchain位于
buildroot/build_i686/staging_dir/usr/bin/
目录下。工具命令的前缀是 i686-linux- 。

三十、编译Busybox静态连接uclibc库
一般而言,使用uclibc库是为了把它静态连接到busybox中。具体步骤是:
(1)把uclibc toolchain的目录添加到PATH中。
在~/.bash_profile文件中添加:
#set PATH so it includes uclibc toolchain if it exist
if [ -d ~/buildroot/build_i686/staging_dir/usr/bin ] ; then
PATH="${PATH}":~/buildroot/build_i686/staging_dir/usr/bin
fi

(2)配置busybox静态连接库。
在busybox的配置界面中,选择:
Build Options --> Build BusyBox as a static binary (no shared libs)
(3)编译
执行
make CROSS_COMPILE=i686-linux-
命令"交叉编译"busybox。
最后编译生成的是静态连接的可执行文件,不需要在initramfs中拷贝库文件。

三十一、用buildroot自动构建initramfs
buildroot 工具实际是一个功能强大的根文件系统构建工具,它以uclibc和busybox作为系统构建的基础,toolchain只是它构建系统的中间产品。 initramfs是一种特殊的根文件系统,当然也可以用buildroot工具自动构建,下面是构建方法的简要描述:
(1)配置
在buildroot的配置界面下做如下的配置:
Package Selection for the target: 选择
Busybox
Run Busybox's own full installation

Use minimal target skeleton
Target filesystem options --> cpio the root filesystem --> comprassion method: gzip
(2)编译
执行
make
命令,进行编译。
(3)输出
构建好的cpio文件是
buildroot/binaries/rootfs.i686.cpio.gz
同一目录下还包含一个未压缩的文件:rootfs.i686.cpio
构建目录则是
buildroot/project_build_i686/uclibc/root
可以在这个目录下对原始的initramfs进行修改调整,然后自己用cpio命令打包生成新的initramfs。
(4)调整
直接用buildroot生成的root.i686.cpio.gz作为initramfs,运行时会出现
can't open /dev/tty1: No such file or directory
can't open /dev/tty2: No such file or directory
can't open /dev/tty3: No such file or directory
错误信息的循环输出,系统不能正常运行。
错误的原因是没有在initramfs的/dev目录下生成相应的设备文件。需要做如下的调整:
1)在构建目录(buildroot/project_build_i686/uclibc/root)下的etc/init.d目录中新增一个初始化脚本文件S10mountfs
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s

2)更改busybox的setuid属性,否则无法执行mount命令。在构建目录(buildroot/project_build_i686/uclibc/root)下执行
chmod -s bin/busybox
命令。
这两项调整工作做完后,在构建目录(buildroot/project_build_i686/uclibc/root)下执行
find . | cpio -o -H newc |gzip > ../initramfs.cpio.gz
命令,重新生成initramfs的cpio打包文件。
(5)运行效果
运行新的initramfs,系统出现登录提示。输入用户名 root,密码为空,即可进入一个mini的linux系统。

buildroot是功能强大、配置灵活的自动化构建工具,它的详细使用和配置方法超出了本文的范围,后续可能会专文描述,此处就从略了。

2009年1月22日

深入理解 Linux 2.6 的 initramfs 機制 (上)

转自深入理解 Linux 2.6 的 initramfs �制 (上)

Howto Make a RAMDisk image with Linux Kernel 2.6

2.6内核中的initrd.img采用cpio压缩,不再是2.4内核使用的ext2格式,无法使用mount -o loop 挂载。需要使用gunzip解压缩,然后再使用cpio解包

cp /boot/initrd-***.img initrd.img.gz
gunzip initrd.img.gz
mkdir initrd
mv initrd.img initrd
cd initrd
cpio -ivmd < initrd.img

通过以上命令就将initrd.img解压了,现在就可以对其进行编辑,完成后使用以下命令重新压制

find . | cpio -ov > ../initrd.new.img
gzip ../initrd.new.img

2009年1月18日

此曲只应天上有!

悼念峰哥和阿莹。

网友的悼念诗词

  君居五羊城,我自湛江边,

  自幼系粤剧,有幸见梨园。

  才艺双色绝,只恨相见晚,

  君拂琴瑟瑟,我歌凄委婉。

  不见花城秀,但听君心蝉,

  今生与君知,陋室蓬壁宽。

  今世无何求,但愿伴身边,

  不能身相许,但求心相见。

  可怜妾命苦,生我君已残,

  只恨尘缘短,更怨妾生晚。

  不能尘缘续,唯有赴黄泉,

  生命有时尽,黄泉路无限。

  网友:dayoo546591


  叹

  离去

  随君行

  无恨淡语

  遥想梨园内

  放喉豪吟九曲

  忘年师徒皆有情

  又岂在宫商角征羽

  ――释然过客

  

  红尘缘分浅,黄泉情路长。

  从今月下,应有双燕共翱翔。

  挥手梨园喧嚣,长伴天涯万里,剪烛诉衷肠。

  寂寞人间路,烟树锁苍茫。

  笛箫残、钟鼓断、追暗香!

  老梅飞雪,翩然双影向潇湘。

  多少花前月下,情寄生前身后,真爱动粤乡。

  此事不应泪,笑语对斜阳。

  转天山

  ――京城狗少

miniBfin板引导u-boot成功

经过几天的调试,今天终于可以成功boot起来了。

主要问题:
1。使用DLC5下载线烧code很不稳定,经常在写的过程中hangup
或写完后校验出错,后改用WIGGLER下载线就很稳定了。原因不明。
2。测试中发现两个排阻中有1~2电阻居然在焊接中烧坏了。

下面启动的一些信息。

U-Boot 1.1.5 (Jan 18 2009 - 14:37:34)

CPU: ADSP BF532 Rev.: 0.5
Board: miniBfin
http://bfincn.blogspot.com
Clock: VCO: 400 MHz, Core: 400 MHz, System: 133 MHz
SDRAM: 32 MB
*** Warning - bad CRC, using default environment

In: serial
Out: serial
Err: serial
No NAND device found!!!
0 MiB
No ethernet found.
starting from spi flash
Hit any key to stop autoboot: 3  2  1  0

EEPROM @0x0 read: addr 0x01000000 off 0x30000 count 0x110000

done
## Booting image at 01000000 ...
Bad Magic Number
bfin>?

? - alias for 'help'
autoscr - run script from memory
base - print or set address offset
bdinfo - print Board Info structure
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootm - boot application image from memory
bootp - boot image via network using BootP/TFTP protocol
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
echo - echo args to console
eeprom - EEPROM sub-system
go - start application at address 'addr'
help - print online help
iminfo - print header information for application image
imls - list all images found in flash
itest - return true/false on integer compare
loop - infinite loop on address range
md - memory display
mm - memory modify (auto-incrementing)
mtest - simple RAM test
mw - memory write (fill)
nand - NAND sub-system
nboot - boot from NAND device
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
rarpboot- boot image via network using RARP/TFTP protocol
reset - Perform RESET of the CPU
run - run commands in an environment variable
saveenv - save environment variables to persistent storage
setenv - set environment variables
sleep - delay execution for some time
tftpboot- boot image via network using TFTP protocol
version - print monitor version
bfin>

2009年1月17日

使用JTAG调试miniBfin板

1. 启动gdbproxy,要使用配置了libbfemu的版本
     # gdbproxy bfin
    
2. 启动gdb
     # bfin-uclinux-gdb
     (gdb)target remote : 2000
    
3. 使用gdb命令查看寄存器值
     1)PLL Divide register
     (gdb) p /x *0xffc00004
           => 0x0003 SSEL=3 CSEL=0
     2)PLL Control register
     (gdb) p /x *0xffc00000
           => 0x5000 DF=0 MSEL=40
     计算频率:
     if(!MSEL)
         VCO = CLKIN/(DF+1) * MSEL
     else
         VCO = CLKIN/(DF+1)
    
     CCLK = VCO/(2^CSEL) (core clock)
     SCLK = VCO/SSEL     (system clock)
     所以:
     输入晶阵10MHz,
     VCO = 10M/1 * 40 = 400MHz
     CCLK = 400M/(2^0) = 400MHz
     SCLK = 400M/3 = 133MHz
     3)SDRAM Memory Global Control Register
     (gdb) p /x *0xffc00a10
           => 0x801111cd
           CDDBG 0 - continue driving SDRAM
                     controls during bus grant
           TCSR  0 - 45 degrees C
           FBBRW 0 - Disabled
           EBUFE 0 - External buffering timing disabled
           SRFS  0 - Disable self-refresh
           PSSE  0 - No effect
           PSM   0 - Precharge, 8 CBR refresh cycles,
                     mode register set
           PUPSD 0 - No extra delay added before first
                     Precharge command
           TWR   10 - 2 cycles
           TRCD  010 - 2 cycles
           TRP   010 - 2 cycles
           TRAS  0111 - 7 cycles
           PASR  00 - All 4 banks refreshed
           CL    11 - 3 cycles
           SCTE  1 - Enabled
     4)SDRAM Memory Bank Control register
     (gdb) p /x *0xffc00a14
           => 0x13
           EBE   1 - Enabled
           EBSZ  01 - 32Mbyte
           EBCAW 01 - 9bits
     5)SDRAM Control Status Register
     (gdb) p /x *0xffc00a1c
           => 0x0001
           BGSTAT 0 - Bus not granted
           SDEASE - W1C 0 - No error detected
           SDRS   0 - SDRAM already powered up
           SDPUA  0 - SDC not in power up sequence
           SDSRA  0 - SDRAMs not in self-refresh mode
           SDCL   1 - SDC is idle
     6)SDRAM Refresh Rate Control Register
     (gdb) p /x *0xffc00a18
           => 0x0408

4. 查看Memory内容
   例如查看0x01ef0000开始的128字节的内容
   (gdb) x /128xh 0x01ef0000
  
   设置memory值
   (gdb) set *0x01ef0000 = 0x1168
   (gdb) p /x *0x01ef0000

2009年1月14日

BF532芯片内调试UART程序补充

编译步骤:
0. 替换jump.S文件
1. 修改u-boot/tools/bin2ldr/runme.sh,减小code size
# strongly recommend to run this script to convert u-boot.bin to LDR format file
#!/bin/sh
if [ -e ../../u-boot.bin ];then
make clean
make all
echo "" > app.bin
./bin2ldr
bfin-uclinux-objcopy -I binary -O ihex app.ldr app.hex
make distclean
else
echo -e "ERR:Please build the u-boot package first"
fi

2. run ./runme.sh
3. brun app.ldr to sflash
4. app.ldr的内容(无法上传,郁闷!)

2009年1月12日

SDT下载电缆原理图

手上有一块ARM44b0x的JTAG子板,之前用ADS1.20调试过程序,但一直不清楚它的接口是如何定义的。今天在网上找到原理图,转载于此以备忘。

S3C2410简易JTAG详解 常见的JTAG cable结构都比较简单,一端是DB25,接到电脑的并口上,中间经过74HC244和一些电阻实现电平转换(5V3.3V?)(也有比较猥琐的只用了 几个100 Ohm电阻的方案),另一端的JTAG header接到目标板的JTAG interface。
并口DB25原先主要是用来连接打印机,其引脚定义如下:
25 Pin D-Sub SPP SIGNAL DIRECTION IN/OUT
1 /Strobe In/Out
2 Data 0 Out
3 Data 1 Out
4 Data 2 Out
5 Data 3 Out
6 Data 4 Out
7 Data 5 Out
8 Data 6 Out
9 Data 7 Out
10 /ACK In
11 Busy In
12 Paper Out In
13 Select In
14 /Line Feed In/Out
15 /Error In
16 /INIT In/Out
17 /Select In In/Out
18-25 Ground GND
可见,数据线Pin2~Pin9可以用来发送数据,Pin10~Pin13等可以用来接收数据。
JTAG的数据传输形式是串行,主要使用了以下引脚:
TDI (Test Data In)
TDO (Test Data Out)
TCK (Test ClocK)
TMS (Test Mode Select)
TRST (Test ReSeT) optional.
因此,DB25-JTAG实际上只利用了DB25的少数几根线。但由于DB25的8条数据线都可以作为output,市面上就出现了各种使用不同Pin Assignment的JTAG线。

例如Wiggler的Pin assignment:
TMS : Pin3 (D1)
TCK : Pin4 (D2)
TDI : Pin5 (D3)
TDO : Pin11 (Busy)
SRST : Pin2 (D0)
TRST : Pin6 (D4)
(参见Wiggler的电路图,但最后的nSRST和nTRST可能不一定用到,并且在H-JTAG中的定义也不一样,TRST是Pin2,没有SRST)
H-JTAG里还有另外一种接法--STD,定义如下:
TMS : Pin3 (D1)
TCK : Pin2 (D0)
TDI : Pin8 (D6)
TDO : Pin13 (Select)
SRST : N/A
TRST : Pin4 (D2)
而S3C2410的烧写程序SJF2410用的是:
TCK : Pin2 (D0)
TDI : Pin3 (D1)
TMS : Pin4 (D2)
TDO : Pin11 (Busy)
这个可以在源代码中的jtag.h文件里看到。
除了DB25那边的Pin Assignment有多种方案,JTAG connector也有20pin, 14pin, 12pin, 10pin等几种标准。这里一块ARM9开发板配的DB25-JTAG转换板就十分好玩,上面也是只有一块244,但是有Wiggler, STD, S3C2410三个20pin接口。由于20pin connector的Pin Assignment是固定的,三个接口对应的是DB25那边的三种接法,因此开发板可以根据pc上运行的程序不同选择不同的接口来接。
20Pin Connector的定义如下(参考此图):
Vcc ← 1 2 → NC
nTRST ← 3 4 → GND
TDI ←5 6 → GND
TMS ←7 8 → GND
TCK ←9 10 → GND
GND ←11 12 → GND
TDO ←13 14 → GND
NRESET ←15 16 → GND
NC ←17 18 → GND
NC ←19 20 → GND
14Pin:
nTRST ← 1 2 → GND
TDI ← 3 4 → GND
TDO ← 5 6 → GND
TMS ←7 8 → GND
TCK ←9 10 → GND
nSRST ←11 12 → n/a
DINT ←13 14 → Vcc
(DINT pin is used to raise Debug Interrupt. Many chips has no this pin.)
12Pin:
nTRST ← 1 2 → GND
TDI ← 3 4 → GND
TDO ← 5 6 → GND
TMS ← 7 8 → GND
TCK ← 9 10 → GND
nSRST ←11 12 → GND
10Pin:
TCK ← 1 2 → Vcc
TDI ← 3 4 → Vcc
TDO ← 5 6 → GND
TMS ← 7 8 → GND
nTRST← 9 10 → GND

2009年1月11日

miniBfin板JTAG简单检测

今天电路板焊好后用JTAG简单检测一下能否检测到CPU和SFlash,一切正常。

miniBfin板焊接中发现的问题

这两天焊接miniBfin板发现一些问题。
1。 电源部分元器件布局太密,比较拥挤。
2。下图中的C20由于使用封装是B,两个管脚间距很小,很容易造成短路,特别是在TopLayer短路时由于看不到很难发现。
3。贴片二极管封装上有横杠的一边是负极。
4。贴片发光二极管在器件反面有符号“-|”,“|”侧是正极。

2009年1月7日

miniBfin板靓照

今天拿到miniBfin板子,拍个照先。

2009年1月1日

Happy New Year!

2008年12月31日

减小DC/DC变换器中的接地反弹 — 一些接地要点

对接地分析的好文,转载于此,慢慢研读。减小DC/DC变换器中的接地反弹 — 一些接地要点

下载全文(PDF, 1,638 KB)

减小DC/DC变换器中的接地反弹 — 一些接地要点

By Jeff Barrow [jeff.barrow@analog.com]

电路接地1在电路原理图中看起来很简单;但是,电路的实际性能是由其印制电路板(PCB)布局决定的。而且,接地节点的分析很困难,特别是对于DC/DC变换器,例如降压型升压型变换器,这些电路的接地节点会聚快速变化的大电流。当接地节点移动时,系统性能会遭受影响并且该系统会辐射电磁干扰(EMI)。但是如果很好地理解“接地“引起的接地噪声的物理本质可提供一种减小接地噪声问题的直观认识。

接地反弹(Ground bounce)简称地弹会产生幅度为几伏的瞬态电压;最常见的是由磁通量变化引起的。传输电流的导线环路实际上构成了一个磁场,其磁场强度与电流成正比。磁通量与穿过环路面积和磁场强度乘 积成正比。

磁通量 ∝ 磁场强度 × 环路面积

更精确的表示是:

ΦB = BA cosφ

其中磁通量ΦB等于磁场强度B乘以穿过环路平面A和磁场方向与环路平面单位矢量夹角φ的余弦。

图1示出了磁通量与电流之间的关系。一个电压源驱动电流克服电阻沿导线环路流动。电流与环绕导线的磁通量相关联。为了将不同的物理量联系起来,可以考虑用你的右手握住导线(应用右手定则)。如果你的拇指指向电流的方向,那么你的其它手指将沿磁场磁力线方向环绕导线。因为那些磁力线穿过环路,所以形成了磁通量,在本例中磁通量方向为穿入页面。

Analog Devices :右手定则

图1. 右手定则

改变磁场强度或环路面积都会引起磁通量变化。当磁通量变化时,在导线中产生与磁通量变化率dΦB/d t成正比的电压。应该注意的是,当环路面积固定,电流变化;或者电流恒定,环路面积变化;或者两种情况同时变化——都会改变磁通量。

例如,假设图2中的开关突然断开。当电流停止流动时,磁通量消失,这会沿导线各处产生一个瞬态大电压。如果导线的一部分是一个接地返回引脚,那么以地电平为参考端的电压会产生一个尖峰,从而在任何使用该引脚为接地参考端的电路中都会产生错误信号。

Analog Devices :开启开关的作用

图2. 开启开关的作用

通常,PCB印制线电阻上的电压降不是接地反弹的主要来源。1盎司(oz)铜的电阻为500 微欧/方数(µΩ/⌈),因此1 A电流变化只能产生500 µV/⌈的反弹电压——问题只存在于采用细长印制线或菊花链式接地或精密电子电路。

寄生电容器的充电和放电为瞬态大电流返回到地提供了一条路径。由于电流变化引起的磁通量变化也引起接地反弹。

在DC/DC开关电源中减少接地反弹的最好方法就是控制磁通量变化——使电流环路面积和环路面积变化最小。

在某些情况下,例如图3所示,电流保持恒定,而开关切换引起环路面积变化,因此产生磁通量的变化。在开关状态1中,一个理想的电压源通过理想导线与一个理想电流源相连。电流在一个包含接地回路的环路中流动。

在开关状态2中,当开关改变位置时,同样的电流在不同的路径中流动。电流源为直流(DC),且并没有变化,但环路面子发生了变化。环路面积的变化意味着磁通量的变化,所以产生了电压。因为接地回路为变化环路的一部分,所以它会产生反弹电压。

Analog Devices

图 3

降压型变换器的接地反弹

为了讨论方便,将图3中的简单电路变换成与其类似的电路——图4中的降压型变换器。

Analog Devices

图4. 对于高频开关,大电容CVIN和大电感LBUCK可以看作一个电压源和电流源

在高频时,一个大电容器——例如降压型变换器输入电容器,CVIN——可以看作一个DC电压源。类似地,一个大电感器——例如降压型变换器输出电感器,LBUCK——也可以看作一个DC电流源。所做的这些近似有助于直观理解。

图5示出当开关在两个位置之间交替切换时磁通量如何变化。

Analog Devices: 开关对环路面积的影响

图5. 开关对环路面积的影响

大电感器LBUCK使输出电流大约保持恒定。类似地,大电容器CVIN保持电压大约等于VIN。由于输入引线电感两端的电压不变,所以输入电流也大约保持恒定。

尽管输入电流和输出电流基本不变,但当开关从位置1切换到位置2时,总环路面积会迅速变为原来的一半。环路面积的变化意味着磁通量的快速变化,从而沿着接地回路引起接地反弹。

实际上,降压型变换器由一对半导体开关构成,如图6所示。

虽然每个图中的复杂程度增加,但是通过磁通量变化引起接地反弹的分析方法仍然很简单和直观。

Analog Devices :采半导体开关对分析接地反弹的基本原理不变

图6. 采半导体开关对分析接地反弹的基本原理不变

事实上,磁通量的变化会沿着接地回路各处都产生电压,这就带来了一个有趣的问题:哪里是真正的地?因为接地反弹意味着,相对于称作的某个理想点(那一点需要定义),在接地返回印制线上产生一个反弹电压。

在电源稳压器电路中,真实的地应该连接在负载的低压端。毕竟,DC/DC变换器的目的是为负载提供稳定的电压和电流。电流回路上的其它所有点都不是真正的地,只是接地回路的一部份。

由于在负载的低压端接地并且环路面积的变化是接地反弹的原因,图7显示了如何精心地放置CVIN通过减小环路面积变化的比率降低接地反弹。

Analog Devices

图7. 精心放置CVIN可以大大减小接地反弹

电容器CVIN旁路PCB顶层的高端开关直接到达底层低端开关两端,因此减小了环路面积的变化,将其与接地回路隔离。当开关从一种状态切换到另一种状态时,从VIN的底部到负载的底部,无环路面积变化或开关电流变化。因此接地回路没有发生反弹。

Analog Devices :当开关从一种状态切换到另一种状态时不合理的布线会导致电流环路面积大幅变化

图8. 当开关从一种状态切换到另一种状态时不合理的布线会导致电流环路面积大幅变化

实际上,PCB布线本身决定了电路的性能。图8为图6中降压型变换器电路原理图的PCB布线图。当开关处于状态1所示的 位置,高端开关闭合,DC电流沿着外圈红色环路流动。当开关处于状态2所示的位置,低端开关闭合,DC电流沿着蓝色环路流动。注意由于环路面积变化引起磁 通量变化。因此产生电压和接地反弹。

为了清晰起见,在单层PCB上实现布线,但即使使用第二层整块接地平面也无法解决接地反弹问题。在展示改进布线图之前,图9给出了一个简单例子说明地平面无法解决问题。

Analog Devices :整体地平面并不总是一个好方法

图9. 整体地平面并不总是一个好方法

这里,我们采用双层PCB以便在与顶层电源线垂直处附加一个旁路电容。在左边的例子中,地平面是整体的并且未切割。顶层印制线电流通过电容器流过,穿过过孔,到达地平面。

因为交流(AC)电总是沿着最小阻抗路径流动,接地返回电流绕着其路径拐角返回电源。所以当电流的幅度或频率发生变化时,电流的磁场及其环路面积发生变化,从而改变磁通量。电流沿最小阻抗路径流动的规律意味着,即使采用整体地平面也会发生接地反弹——与其导通性无关。

在右边的例子中,一个经过合理规划切割的地平面会限制返回电流以使环路面积最小,从而大大减小接地反弹。在切割返回线路内产生的任何剩余接地反弹电压与通用地平面隔离。

图10中的PCB布线利用图9中示出的原理减小了接地反弹。采用双层PCB板以便将输入电容器和两个开关安排在地平面的孤岛上。

这种布线不必最好,但它工作很好,而且能够说明关键问题。应该注意红色电流(状态1)和蓝色电流(状态2)包围的环路面 积很大,但两个环路面积之差很小。环路面积变换很小意味着磁通量的变化小——即接地反弹小。(然而,一般情况下,也要保证环路面积小——图10只是为了说 明AC电流路径匹配的重要性。)

另外,在磁场和环路面积发生变化的接地回路孤岛内,沿着任何接地回路引起的接地反弹都受接地切割限制。

此外,可能第一眼看上去,输入电容器CVIN好像没有位于图7中所示的顶层高端开关和低层低端开关之间,但进一步观察才会发现是这样。尽管物理邻近可以很好,但真正起作用的是通过最小化环路面积实现的电子接近。

合理的降压型变换器布线可以使状态1和状态2之间的环路面积变化很小

图10. 合理的降压型变换器布线可以使状态1和状态2之间的环路面积变化很小

升压型变换器中的接地反弹

升压型变换器实际上是降压型变换器的反射,因此——如图11所示——它是输出电容器,输出电容器必须放在顶层高端开关和底层低端开关底端之间以使环路面积变化最小。

Analog Devices

图11. 按照降压型变换器将CVIN放在关键位置相同的方法,升压型变换器将CVOUT放在关键位置。
a)不合理设计,b)合理设计。

小结

接地反弹电压主要是由于磁通量变化引起的。在DC/DC开关电源中,磁通量变化是由于在不同的电流环路面积之间高速切换 DC电流引起的。但是精心放置降压型变换器的输入电容器和升压型变换器的输出电容器并且合理切割接地平面可以隔离接地反弹。然而,重要的是当切割地平面时 必须谨慎以避免增加电路中其它返回电流的环路面积。

另外,一个合理的布线应该将真正的地放在连接负载的底层,不会引起环路面积的变化或电流的变化。任何其它与导通相关的点都可以称为“地”,但它只是沿着返回路径的一点而已。

其它用于接地分析的有用概念

如果你牢记下述基本概念,你就会清楚地知道什么情况会产生接地反弹,什么情况不会产生接地反弹。图12示出了相互垂直的两个导体不会遭受磁场的互相影响。

相互垂直的两个导体不会遭受磁场的互相影响

图12. 相互垂直的两个导体不会遭受磁场的互相影响

沿着相同方向传输相等电流的两条平行导线周围产生的磁力线在两条导线之间处会相互抵消,所以两条导线总储存能量要比单独一条导线储存的能量少。因此,PCB宽印制线的电感要比窄印制线小。

电流沿着相同方向流动的两条平行导线

图13. 电流沿着相同方向流动的两条平行导线

沿着相反方向传输相等电流的两条平行导线周围产生的磁力线在两条导线的外部相互抵消,而在两条导线之间处增强。如果内部环路面积缩小,那么总磁通量,所以电感也随之变小。该现象可以解释为何AC地平面的返回电流总是沿顶层印制线导线下方流动。

电流沿着相反方向流动的两条平行导线

图14. 电流沿着相反方向流动的两条平行导线

图15示出了为何拐角增加电感。一条直导线只能看到它自己的磁场,但是在拐角处,还能看到垂直导线的磁场。因此,拐角储存了更多的磁场能量,其电感要大于直导线。

为何拐角增加电感

图15. 为何拐角增加电感

图16示出了在传输流的导线下面切割接地平面,由于转移回路电流可增加环路面积,从而增大环路尺寸并且助长接地反弹。

返回电流沿着最小阻抗路径流动

图16. 返回电流沿着最小阻抗路径流动

元件方向的作用,如图17所示。

元件方向的影响

图17. 元件方向的影响

总结

接地反弹一直是一个潜在的问题。对于监视器或电视,它意味着图像有噪声;对于音频设备则意味着噪声本底。在数字系统系统中,接地反弹可能会导致计算错误——甚至是系统崩溃。

对于预测接地反弹幅度来说,仔细估计寄生元素和细致的仿真是有效的方法。但对指导电路设计的直觉,理解背后的物理原理是很必要的。

首先,设计PCB时,应该将负载的低压端设置为真正的地。

然后,用电流源和电压源容替大电感器和电容器以简化电路动态特性。观察每种开关组合下的电流环路。应该使环路重叠;如果无法做到重叠,应该精心地在地平面上切割出一个小岛以确保只有DC流入和流出孤岛开口。

在大多数情况下,经过这些努力可以获得可以接受的接地性能。如果还是不能,应该首先考虑地平面的电阻,然后考虑所有开关和进入返回路径的寄生电容器两端流过的位移电流。

无论什么电路,基本接地原理都是相同的——应该使磁通量的变化最小或者对它隔离。

尾注

1Analog Dialogue杂志已经发表了许多关于“接地”问题的文章。这些文章可以在我们的综合在线存档文件中找到。Paul Brokaw和本文作 者合作撰写的一篇文章,见23-3 (1989) pp. 7-9。其中包括与接地和噪 声相关的早期参考文献索引。相关资料见11-2 (1977) pp. 10-15; 25-2(1991) pp. 24-25; 26-2 (1992) p. 27 (包含一系列很好的参考文献); 30-2 (1996) p. 11 (包括EMC/EMI/ESD方面的参考文献);39-3 (2005) pp. 3-8。研讨会教材《High Speed System Applications(高速系统应 用)》,Walt Kester和Hank Zumbahlen编著,ADI公司出版 (2006),书 中有大量章节介绍PCB布线和设计工具(pp. 4.1-4.90)。其它相关文章 还有“Reducing ground bounce in dc/dc-converter applications(减小 DC/DC变换器应用中的接地反弹)”,Jeff Barrow,EDN, 7/6/2006。

2008年12月28日

PCB板加工参数

今天去赛格电子市场,了解了一下4层PCB板加工的情况:
  • 最小线宽:4mil/0.1mm
  • 最小安全间距:4mil/0.1mm (线宽不能小于安全间距)
  • 最小过孔:8mil/0.2mm
  • 加工要求过孔外径不能小于0.15mm,如果外径减小则需要板厚加大

2008年12月22日

miniBfin最小系统板硬件设计完成啦!

BF532最小系统:
  • BF532
  • 32M SDRAM
  • 8M SFlash
  • UART
  • JTAG
  • GPIO Interface
  • Power
先睹为快!

2008年11月26日

BF532芯片内调试UART程序

今天写了一段UART串口消息输出调试代码,用于加载到芯片L1 RAM中运行测试UART的连接情况,成功输出信息:Hello Bfincn
代码比较简单,大家看看就明白了。

#include "bin2ldr.h"

#define hi(x) (x >> 16)
#define lo(x) (x & 0xffff)

/* UART Controller (0xFFC00400 - 0xFFC004FF) */
/* Transmit Holding register */
#define UART_THR 0xFFC00400
/* Receive Buffer register */
#define UART_RBR 0xFFC00400
/* Divisor Latch (Low-Byte) */
#define UART_DLL 0xFFC00400
/* Interrupt Enable Register */
#define UART_IER 0xFFC00404
/* Divisor Latch (High-Byte) */
#define UART_DLH 0xFFC00404
/* Interrupt Identification Register */
#define UART_IIR 0xFFC00408
/* Line Control Register */
#define UART_LCR 0xFFC0040C
/* Modem Control Register */
#define UART_MCR 0xFFC00410
/* Line Status Register */
#define UART_LSR 0xFFC00414
/* SCR Scratch Register */
#define UART_SCR 0xFFC0041C
/* Global Control Register */
#define UART_GCTL 0xFFC00424


/* Initialise UART */
p0.h = hi(UART_LCR);
p0.l = lo(UART_LCR);

/* 8-bit word; 1 stop bit; No parity */
r0 = 0x83(Z);
w[p0] = r0.l; /* To enable DLL writes */
ssync;

p0.h = hi(UART_DLL);
p0.l = lo(UART_DLL);

/* SCLK = 133MHz, BaudRate=115200
BaudRate = SCLK /(16 * Divisor) */
r0 = 0x48(Z);
w[p0] = r0.l;
ssync;

p0.h = hi(UART_DLH);
p0.l = lo(UART_DLH);
r0 = 0x00(Z);
w[p0] = r0.l;
ssync;

p0.h = hi(UART_GCTL);
p0.l = lo(UART_GCTL);
r0 = 0x1(Z);
w[p0] = r0.l; /* To enable UART clock */
ssync;

/* Begin to process rx/tx data */
p0.h = hi(UART_LCR);
p0.l = lo(UART_LCR);

/* 8-bit word; 1 stop bit; No parity */
r0 = 0x03(Z);
/* To enable access THR/PBR */
w[p0] = r0.l;
ssync;

/* Write 'Hello Bfincn' to Console */
p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x48(Z); /* H */
w[p0] = r0.l;
ssync;

ww1:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww1;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x65(Z); /* e */
w[p0] = r0.l;
ssync;

ww2:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww2;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x6c(Z); /* l */
w[p0] = r0.l;
ssync;

ww3:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww3;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x6c(Z); /* l */
w[p0] = r0.l;
ssync;

ww4:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww4;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x6f(Z); /* l */
w[p0] = r0.l;
ssync;

ww5:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww5;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x20(Z); /* space */
w[p0] = r0.l;
ssync;

ww6:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww6;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x42(Z); /* B */
w[p0] = r0.l;
ssync;

ww7:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww7;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x66(Z); /* f */
w[p0] = r0.l;
ssync;

ww8:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww8;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x69(Z); /* i */
w[p0] = r0.l;
ssync;

ww9:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww9;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x6e(Z); /* n */
w[p0] = r0.l;
ssync;

ww10:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww10;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x63(Z); /* c */
w[p0] = r0.l;
ssync;

ww11:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump ww11;

p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
r0 = 0x6e(Z); /* n */
w[p0] = r0.l;
ssync;

ww12:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,0);
if !cc jump ww12;

/* read from buffer */
p0.h = hi(UART_RBR);
p0.l = lo(UART_RBR);
r1 = w[p0](z);

/* send buffer is empty or not */
wait:
p0.h = hi(UART_LSR);
p0.l = lo(UART_LSR);
r0 = w[p0](z);
cc = bittst(r0,5);
if !cc jump wait;

/* write back to console */
p0.h = hi(UART_THR);
p0.l = lo(UART_THR);
w[p0] = r1.l;
ssync;

/* if user enter the char 'q' then exit */
r1 = 0x72(z);
cc = r0 == r1; /* q */
if !cc jump ww12;

p0.l = (APP_ENTRY & 0xFFFF);
p0.h = (APP_ENTRY >> 16);
jump (p0);