U-Boot 在44B0X 开发板上的移植以及代码分析

发布者:BlissfulCharm最新更新时间:2016-08-14 来源: eefocus关键字:U-Boot  44B0X  移植  代码分析 手机看文章 扫描二维码
随时随地手机看文章
1. u-boot 介绍
       u-boot 是一个open source 的bootloader,目前版本是1.1.2。u-boot 是在ppcboot 以及armboot 的基础上发展而来,相当的成熟和稳定,已经在许多嵌入式系统开发过程中被采用。由于其开发源代码,其支持的开发板众多。
       为什么我们需要u-boot?显然可以将uClinux 直接烧入flash,从而不需要额外的引导装载程序(bootloader)。但是从软件升级的角度以及程序修补的来说,软件的自动更新非常重要。事实上,引导装载程序(bootloader)的用途不仅如此,但仅从软件的自动更新的需要就说明我们的开发是必要的。同时,u-boot 移植的过程也是一个对嵌入式系统包括软硬件以及操作系统加深理解的一个过程。

2. u-boot 移植的框架
移植u-boot 到新的开发板上仅需要修改和硬件相关的部分。在代码结构上:
1) 在board 目录下创建gold44b 目录,创建gold44b.c 以及flash.c,memsetup.S,u-boot.lds等。不需要从零开始,可选择一个相似的目录,直接复制过来,修改文件名以及内容。我在移植u-boot 过程中,选择的是Dave/B2目录。由于u-boot 已经包含基于s3c24b0 的开发板目录,作为参考,也可以复制相应的目录。
2) 在cpu 目录下创建s3c44b0x 目录,主要包含start.S,interrupts.c 以及cpu.c,serial.c几个文件。同样不需要从零开始建立文件,直接从arm720t 复制,然后修改相应内容。
3) 在include/configs 目录下添加gold44b.h,在这里放上全局的宏定义等。
4) 找到u-boot 根目录下Makefile 修改加入
gold44b_config : unconfig
@./mkconfig $(@:_config=) arm s3c44b0 gold44b
5) 运行make ev44bii_config,如果没有错误就可以开始硬件相关代码移植的工作

3. u-boot 的体系结构
1) 总体结构
u-boot 是一个层次式结构。做移植工作的软件人员应当提供串口驱动(UART Driver),以太网驱动(Ethernet Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。目前,通过USB 口下载程序显得不是十分必要,而且开发板上也没有USB接口,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command 通过串口提供人机界面。我们可以使用一些命令做一些常用的工作,比如内存查看命令md。
Kermit 应用主要用来支持使用串口通过超级终端下载应用程序。TFTP 则是通过网络方式来下载应用程序,例如uClinux 操作系统。
2) 内存分布
gold44b 的flash 大小2M(8bits),现在将0-40000 共256k 作为u-boot 的存储空间。由于u-boot 中有一些环境变量,例如ip 地址,引导文件名等,可在命令行通过setenv 配置好,通过saveenv 保存在40000-50000(共64k)这段空间里。如果存在保存好的环境变量,u-boot 引导将直接使用这些环境变量。正如从代码分析中可以看到,我们会把flash 引导代码搬移到DRAM 中运行。u-boot 的代码在DRAM中的位置在u-boot-1.1.2/board/gold44b/config.mk配置如下:TEXT_BASE = 0x0C700000。这样,引导代码u-boot将从0x0000 0000 处搬移到0x0C700000 处。特别注意的由于gold44b uClinux 中断向量程序地址在0x0c000 0000 处,所以不能将程序下载到0x0c000 0000 出,通常下载到0x0c008 0000 处。

4. start.S 代码结构
1) 定义入口
一个可执行的Image 必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0 地址。例如start.S 中的
.globl _start
_start:
值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连接器脚本文件(lds)。
开发板上的u-boot.lds如下:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;

. = ALIGN(4);
.text :
{
cpu/s3c44b0/start.o (.text)
*(.text)
}

. = ALIGN(4);
.rodata : { *(.rodata) }

. = ALIGN(4);
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }

__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

armboot_end_data = .;

. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}

2) 设置异常向量(Exception Vector)
异常中断矢量表(Exception Vector Table)是u-boot与uClinux内核发生联系关键的地方之一。即使uClinux内核已经得到处理器的控制权运行,一旦发生中断,处理器还是会自动跳转到从0x0地址开始的第一级异常中断矢量表中的某个表项(依据于中断类型)处读取指令运行。
异常中断矢量表必须是从0 地址开始,连续的存放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误 (Dabort),保留,以及IRQ,FIQ 等。注意这里的值必须与uClinux 的vector_base 一致。这就是说如果uClinux 中vector_base(include/armnommu/proc-armv/system.h)定义为0x0c00 0000,则HandleUndef 应该在
0x0c00 0004。
.globl _start
_start: b reset
/*Modfied by zl 2005-2-21 */
/* add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000 
*/
ldr pc, =0x0c000004
ldr pc, =0x0c000008
ldr pc, =0x0c00000c
ldr pc, =0x0c000010
ldr pc, =0x0c000014
ldr pc, =0x0c000018
ldr pc, =0x0c00001c

.balignl 16,0xdeadbeef
这里,地址0x0处的一级异常中断矢量表只简单地包含向二级异常中断矢量表的跳转指令就可以。这样,就能够正确地将发生的事件交给uClinux的中断处理程序来处理。这样设计是因为在本u-boot移植里不使用中断,8019和Timer都是轮询中断的,如果u-boot要使用中断(如要用到USB设备),就需要建立二级异常中断矢量表了。代码如下(没有调试通过):
.globl _start
_start: b reset
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000 

.balignl 16,0xdeadbeef 
....
在Reset是复制中断矢量表
/*
now copy to sram the interrupt vector
*/
adr r0, real_vectors
add r2, r0, #1024
ldr r1, =0x0c000000
add r1, r1, #0x08
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble vector_copy_loop 

....
建立三级中断跳转
/*************************************************/
/* interrupt vectors */
/*************************************************/
/*
real_vectors:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq
b fiq 
*/

/*************************************************/
undefined_instruction:
mov r6, #3
b reset

software_interrupt:
mov r6, #4
b reset

prefetch_abort:
mov r6, #5
b reset

data_abort:
mov r6, #6
b reset

not_used:
/* we *should* never reach this */
mov r6, #7
b reset

irq:
mov r6, #8
b reset

fiq:
mov r6, #9
b reset

3) 初始化CPU 相关的pll,clock,中断控制寄存器
依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock loop),以及时钟。
这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/

#define INTCON (0x01c00000+0x200000)
#define INTMSK (0x01c00000+0x20000c)
#define LOCKTIME (0x01c00000+0x18000c)
#define PLLCON (0x01c00000+0x180000)
#define CLKCON (0x01c00000+0x180004)
#define WTCON (0x01c00000+0x130000)
cpu_init_crit:
/* disable watch dog */
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]

/*
* mask all IRQs by clearing all bits in the INTMRs
*/
ldr r1,=INTMSK
ldr r0, =0x03fffeff
str r0, [r1]

ldr r1, =INTCON
ldr r0, =0x05
str r0, [r1]

/* Set Clock Control Register */
ldr r1, =LOCKTIME
ldrb r0, =800
strb r0, [r1]

ldr r1, =PLLCON

#if CONFIG_S3C44B0_CLOCK_SPEED==64
ldr r0, =0x38021 /* smdk4110: Xtal=8MHz Fclk=64MHz */
#elif CONFIG_S3C44B0_CLOCK_SPEED==66
ldr r0, =0x34031 /* 66MHz (Quartz=11MHz) */
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
ldr r0, =0x610c1 /*B2: Xtal=20mhz Fclk=75MHz */
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif

str r0, [r1]

ldr r1,=CLKCON
ldr r0, =0x7ff8
str r0, [r1]

mov pc, lr

4) 初始化SDRAM控制器
内存控制器(主要是SDRAM控制器),主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度,8 个内存bank,bank 大小,sclk,以及两个bank mode。
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a memsetup.S in your board directory.
*/
bl memsetup
#endif
初始化内存控制器的代码存放在u-boot-1.1.2/board/gold44b/memsetup.S中
与ADS或者SDT下的boot代码(memcfg.s和44binit.s)一致,只是汇编格式有点不一样。
5) 将rom 中的程序复制到RAM 中
首先利用PC 取得bootloader 在flash 的起始地址,再通过标号之差计算出这个程序代
码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。取得正
确信息后,通过寄存器(r3 到r10)做为复制的中间媒介,将代码复制到RAM 中。

relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup

ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */

copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
6) 初始化堆栈
进入各种模式设置相应模式的堆栈.
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */

7) 转到RAM 中执行
使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。
ldr pc, _start_armboot

5. 系统初始化部分
1) 串口部分(u-boot-1.1.2/cpu/s3c44b0/serial.c)
串口的设置主要包括初始化串口部分,值得注意的串口的Baudrate 与时钟MCLK 有很大关系,是通过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 )计算得出。这可以在手册中查到。由于u-boot支持可变的波特率,所以采用宏定义设置默认波特率(64Mhz,115200bps)和其他波特率。代码如下:
void serial_setbrg (void)
{
DECLARE_GLOBAL_DATA_PTR;

u32 divisor = 0;

/* get correct divisor */
switch(gd->baudrate) {

case 1200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 3124;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 3905;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默认
divisor = 3332;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;

case 9600:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 390;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 487;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默认
divisor = 416;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;

case 19200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 194;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 243;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默认
divisor = 207;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;

case 38400:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 97;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 121;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默认
divisor = 103;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;

case 57600:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 64;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 80;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默认
divisor = 68;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;

case 115200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 32;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64
divisor = 34;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75 /默认
divisor = 40;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
}

serial_flush_output();
serial_flush_input();
UFCON0 = 0x0;
ULCON0 = 0x03;
UCON0 = 0x05;
UBRDIV0 = divisor;

UFCON1 = 0x0;
ULCON1 = 0x03;
UCON1 = 0x05;
UBRDIV1 = divisor;

for(divisor=0; divisor<100; divisor++) {
/* NOP */
}
}
其他的函数包括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完成。
例如,接收函数:
static int serial_flush_input(void)
{
volatile u32 tmp;

/* keep on reading as long as the receiver is not empty */
while(UTRSTAT0&0x01) {
tmp = REGB(URXH0);
}

return 0;
}
2) 时钟部分(u-boot-1.1.2/cpu/s3c44b0/interrupt.c)
实现了延时函数udelay。
这里的get_timer 由于没有使用中断,是使用全局变量来累加的。
void udelay (unsigned long usec)
{
ulong tmo;

tmo = usec / 1000;
tmo *= CFG_HZ;
tmo /= 8;

tmo += get_timer (0);

while (get_timer_masked () < tmo)
/*NOP*/;
}

3) flash 部分(u-boot-1.1.2/board/gold44b.c)
flash 作为内存的一部分,读肯定没有问题,关键是flash 的写部分。
Flash 的写必须先擦除,然后再写。
flash_init 完成初始化部分,这里的主要目的是检验flash 的型号是否正确。

unsigned long flash_init (void)
{
#ifdef __DEBUG_START_FROM_SRAM__
return CFG_DUMMY_FLASH_SIZE;
#else
unsigned long size_b0;
int i;

/* Init: no FLASHes known */
for (i=0; i flash_info[i].flash_id = FLASH_UNKNOWN;
}

/* Static FLASH Bank configuration here - FIXME XXX */

size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]);

if (flash_info[0].flash_id == FLASH_UNKNOWN) {
printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n",
size_b0, size_b0<<20);
}

/* Setup offsets */
flash_get_offsets (0, &flash_info[0]);

/* Monitor protection ON by default */
(void)flash_protect(FLAG_PROTECT_SET,
-CFG_MONITOR_LEN,
0xffffffff,
&flash_info[0]);

flash_info[0].size = size_b0;

return (size_b0);
#endif
}


flash_erase 擦除flash,BlankCheck 则检查该部分内容是否擦除成功。

int flash_erase (flash_info_t *info, int s_first, int s_last)
{
volatile CFG_FLASH_WORD_SIZE *addr = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *addr2;
int flag, prot, sect, l_sect;
ulong start, now, last;
int i;

if ((s_first < 0) || (s_first > s_last)) {
if (info->flash_id == FLASH_UNKNOWN) {
printf ("- missing\n");
} else {
printf ("- no sectors to erase\n");
}
return 1;
}

if (info->flash_id == FLASH_UNKNOWN) {
printf ("Can't erase unknown flash type - aborted\n");
return 1;
}

prot = 0;
for (sect=s_first; sect<=s_last; ++sect) {
if (info->protect[sect]) {
prot++;
}
}

if (prot) {
printf ("- Warning: %d protected sectors will not be erased!\n",
prot);
} else {
printf ("\n");
}

l_sect = -1;

/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();

/* Start erase on unprotected sectors */
for (sect = s_first; sect<=s_last; sect++) {
if (info->protect[sect] == 0) { /* not protected */
addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[sect]);
if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00500050; /* block erase */
for (i=0; i<50; i++)
udelay(1000); /* wait 1 ms */
} else {
if (sect == s_first) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
}
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00300030; /* sector erase */
}
l_sect = sect;
}
}

/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();

/* wait at least 80us - let's wait 1 ms */
udelay (1000);

/*
* We wait for the last triggered sector
*/
if (l_sect < 0)
goto DONE;

start = get_timer (0);
last = start;
addr = (CFG_FLASH_WORD_SIZE *)(info->start[l_sect]);
while ((addr[0] & (CFG_FLASH_WORD_SIZE)0x00800080) != (CFG_FLASH_WORD_SIZE)0x00800080) {
if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) {
printf ("Timeout\n");
return 1;
}
/* show that we're waiting */
if ((now - last) > 50000000) { /* every second */
putc ('.');
last = now;
}
}

DONE:
/* reset to read mode */
addr = (CFG_FLASH_WORD_SIZE *)info->start[0];
addr[0] = (CFG_FLASH_WORD_SIZE)0x00F000F0; /* reset bank */

printf (" done\n");
return 0;
}


wirte_word 则想flash 里面写入unsigned long 类型的data,因为flash 一次只能写入16bits,所以这里分两次写入。
/*-----------------------------------------------------------------------
* Write a word to Flash, returns:
* 0 - OK
* 1 - write timeout
* 2 - Flash not erased
*/
static int write_word (flash_info_t *info, ulong dest, ulong data)
{
volatile CFG_FLASH_WORD_SIZE *addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *dest2 = (CFG_FLASH_WORD_SIZE *)dest;
volatile CFG_FLASH_WORD_SIZE *data2 = (CFG_FLASH_WORD_SIZE *)&data;
ulong start;
int flag;
int i;

/* Check if Flash is (sufficiently) erased */
if ((*((volatile ulong *)dest) & data) != data) {
return (2);
}
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();

for (i=0; i<4/sizeof(CFG_FLASH_WORD_SIZE); i++)
{
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr2[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00A000A0;

dest2[i] = data2[i];

/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();

/* data polling for D7 */
start = get_timer (0);
while ((dest2[i] & (CFG_FLASH_WORD_SIZE)0x00800080) !=
(data2[i] & (CFG_FLASH_WORD_SIZE)0x00800080)) {
if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
return (1);
}
}
}

return (0);
}
关键字:U-Boot  44B0X  移植  代码分析 引用地址:U-Boot 在44B0X 开发板上的移植以及代码分析

上一篇:S3C2440外部中断之按键中断
下一篇:uboot移植到S3C44B0X开发板的经历

推荐阅读最新更新时间:2024-03-16 15:04

基于ARM7TDMI的uClinux移植
随着信息技术技术的飞速发展, ARM技术方案架构作为一种具备低功耗、高性能、以及小体积等特性的32位嵌入式微处理器,得到了众多的知识产权授权用户,其中包括世界顶级的半导体和系统公司。目前已被广泛的用于各类电子产品,汽车、消费娱乐、影像、工业控制、海量存储、网络、安保和无线等领域。被业界人士认为,基于ARM的技术方案是最具市场前景和市场优势的解决方案。 现场总线CAN是为解决现代汽车中众多的电控模块之间的数据交换而开发的一种串行通信协议。由于其具有多主站依据优先权进行总线访问,采用非破坏性总线仲裁,可完成对通信数据的错误检验和优先级判别,数据长度最多为8个字节,传输时间短,受干扰的概率低,抗干扰能力较强,通信速率最高可达1Mbi
[单片机]
米尔入门级i.MX6UL开发板的神经网络框架ncnn移植与测试
米尔入门级i.MX6UL开发板的神经网络框架ncnn移植与测试 嵌入式Linux入门级板卡的神经网络框架ncnn移植与测试-米尔i.MX6UL开发板 米尔 MYD-Y6ULX-V2 开发板,基于 NXP i.MX6UL/i.MX6ULL处理器,该开发板被米尔称之为经典王牌产品。本次测试目标是在此开发板上进行神经网络框架ncnn的移植与测试开发,测试ncnn在此开发板上的性能与应用测试。 01.什么是ncnn ncnn 是腾讯优图推出的在手机端极致优化的高性能神经网络前向计算框架。也能够在移动设备上的高性能神经网络前向计算框架。ncnn 从设计之初深刻考虑移动端的部署和使用。无第三方依赖,跨平台,其中手机
[嵌入式]
米尔入门级i.MX6UL开发板的神经网络框架ncnn<font color='red'>移植</font>与测试
TensorFlow™优化开源SYCL™库 可更轻松地移植现有代码
Imagination Technologies宣布:得益于全新优化的开源SYCL神经网络库,使用TensorFlow的开发人员将可以直接面向PowerVR图形处理器(GPU)进行开发。其首个版本将在2019年提供商用。 TensorFlow的SYCL版本支持大量的人工智能(AI)操作(如图1),并且用户也易于去按需定制,这意味着开发人员通过使用最新的神经网络或他们自己研究的AI技术,就可以在PowerVR上运行那些即刻可用的高性能网络。由于TensorFlow SYCL的支持既是开源的又是基于开放标准的,因此对于那些想要在低功耗设备上对最新的AI技术进行加速的开发人员而言,它是一种理想的解决方案。
[物联网]
TensorFlow™优化开源SYCL™库 可更轻松地<font color='red'>移植</font>现有<font color='red'>代码</font>
3年后4K电视渗透率将达71%,超高清产业风口已
在短短的三四年间,我国4K电视获得迅速普及,市场渗透率已经逼近60%。来自中国电子视像行业协会的数据显示,到今年年底,我国4K电视渗透率将达到58%,预计3年后将达到71%。 超高清视频产业将带来芯片、显示面板、视频制作设备、存储设备、网络传输设备、终端整机等产业链各环节的升级换代,从而形成一个拉动国民经济的重要增长点。工信部电子信息司副司长乔跃山指出,未来3~5年将是我国超高清视频产业发展的战略机遇期。 4K电视日益普及,8K电视也将崛起 受上游液晶面板的驱动,下游电视厂商正大力推动4K电视的日益普及以及大尺寸8K电视的销售。 友达光电产品业务负责人Liao Wei-lun指出,2018年全球4K电视渗透率将从去
[手机便携]
TinyOS在MSP430F2618上移植(三)之LCD
本节记录串口TFT LCD在TinyOS上的移植。1.8寸LCD显示屏采用ST7735控制器,使用SPI接口实现控制、显示数据的传输。 总体架构:分为3层结构,最底层位于相应的platforms中chips目录下,实现在特定平台上控制引脚连接,SPI接口连接,以及SPI配置。中间层实现了LCD基本操作,LCD初始化,提供给上层模块连接的接口,以及资源管理。最上层提供应用层模块连接的接口。 在本架构中,中间层和最高层提供相同的接口LCD16,该接口提供的实现方法有english_string,clear,single_color等,以及写完成事件writeDone。不同的是在最高层提供的接口LCD16中,并没有真正实现接口的
[单片机]
分析TCP/IP协议栈代码之IP & ICMP(STM32平台)
1. IP介绍 IP是TCP/IP协议族中最为核心的协议。大家,如TCP、UDP、ICMP及IGMP数据,都是在IP数据报格式基础上再封装一层再来传输的(见图1 - 4)。 不可靠(unreliable)的意思是它不能保证 IP数据报能成功地到达目的地。 IP仅提供最好的传输服务。如果发生某种错误时,如某个路由器暂时用完了缓冲区, IP有一个简单的错误处理算法:丢弃该数据报,然后发送 ICMP消息报给信源端。任何要求的可靠性必须由上层来提供(如TCP) 。 无连接(connectionless)这个术语的意思是I P并不维护任何关于后续数据报的状态信息。每个数据报的处理是相互独立的。这也说明, IP数据报可以不按发送顺序接收。
[单片机]
<font color='red'>分析</font>TCP/IP协议栈<font color='red'>代码</font>之IP & ICMP(STM32平台)
魅族被员工有理有据,这2000万大手笔啊
近日, 魅族 开除了在微博公开吐槽魅族副总裁 杨柘 的魅族文创部总监,并做了通报。     对此,涉事人张佳又发微博称未有辱骂性字眼,没有散步谣言,对公司邮件中的评价不接受,要采取法律手段维护。   在表示不服的同时,张佳还提到某高管滥用权力,指定供应商,图文视频虚高,导致钱浪费。张佳还表示会公布两个立项表。其中一个立项表显示,魅族15整体营销费高达4000多万。   今天,在微博公开吐槽魅族副总裁杨柘的魅族文创部总监,张佳已经被魅族开除,并做了通报。     魅族表示张佳在魅族新品15发布在即之时,通过微博等社会媒体发泄个人不满情绪,对同事使用辱骂性预言、散布针对公司的谣言,引发众多媒体、魅友对公司经营和管理状况作出负面
[嵌入式]
S3C2440 Linux驱动移植——NAND驱动
1. 修改分区表 打开文件arch/arm/plat-s3c24xx/common-smdk.c,修改mtd_partition结构体数组。 修改后如下: view plain copy static struct mtd_partition smdk_default_nand_part = { = { .name = Uboot , .size = 0x00040000, .offset = 0x00000000, }, = { .name = K
[单片机]
S3C2440 Linux驱动<font color='red'>移植</font>——NAND驱动
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved